[
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\nimpls/vbs/*.vbs text eol=crlf"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "Pull request requirements:\n\n- [ ] Commits are well written and well organized.\n- [ ] Commits for a specific implementation should be prefixed with\n  the implementation name.\n- [ ] Github Actions CI passes all checks (including self-host)\n\nAdditional requirements if you are adding a new implementation (see [FAQ](../docs/FAQ.md#add_implementation) for details):\n\n- [ ] Follow incremental structure (no common eval code)\n- [ ] Add `impls/<IMPL>/Dockerfile`\n- [ ] Add `impls/<IMPL>/Makefile`\n- [ ] Update `IMPLS.yml`\n- [ ] Update `Makefile.impls`\n- [ ] Update `README.md`\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Build and Test\n\npermissions:\n  contents: read\n  packages: write\n\non:\n  push: {}\n  pull_request: {}\n  workflow_dispatch:\n    inputs:\n      impls:\n        description: 'Space separated list of impls to test (or all)'\n        required: true\n        default: 'all'\n      self-hosted:\n        description: 'Include self-hosted tests'\n        required: true\n        default: 'yes'\n        options: ['yes', 'no']\n\njobs:\n  get-matrix:\n    runs-on: ubuntu-24.04\n    outputs:\n      do-linux: ${{ steps.get-matrix-step.outputs.do_linux }}\n      matrix-linux: ${{ steps.get-matrix-step.outputs.linux }}\n      do-macos: ${{ steps.get-matrix-step.outputs.do_macos }}\n      matrix-macos: ${{ steps.get-matrix-step.outputs.macos }}\n      do-windows: ${{ steps.get-matrix-step.outputs.do_windows }}\n      matrix-windows: ${{ steps.get-matrix-step.outputs.windows }}\n    steps:\n      - uses: actions/checkout@v4\n      - id: files\n        if: ${{ github.event_name != 'workflow_dispatch' }}\n        uses: kanaka/get-changed-files@v4\n        with:\n          default-base: master\n      - id: get-matrix-step\n        run: |\n          export OVERRIDE_IMPLS=\"${{ github.event.inputs.impls }}\" # \"\n          echo \"OVERRIDE_IMPLS: ${OVERRIDE_IMPLS}\"\n          ./get-ci-matrix.py ${{ steps.files.outputs.all }} > \"${GITHUB_OUTPUT}\"\n\n  linux:\n    needs: get-matrix\n    if: ${{ needs.get-matrix.outputs.do-linux == 'true' }}\n    runs-on: ubuntu-24.04\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.get-matrix.outputs.matrix-linux) }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0  # Need full history for voom like versions\n      - name: Log in to GitHub Container Registry\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Docker Build/Push\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh docker-build-push ${IMPL}\n      - name: Build\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh build ${IMPL}\n      - name: Step Tests\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh test ${IMPL}\n      - name: Regression Tests\n        run: |\n          export ${{ matrix.IMPL }}\n          STEP=stepA REGRESS=1 HARD=1 OPTIONAL=0 ./ci.sh test ${IMPL}\n      - name: Performance Tests\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh perf ${IMPL}\n      - name: Self-hosted Tests\n        if: ${{ github.event.inputs.self-hosted != 'no' }}\n        run: |\n          export ${{ matrix.IMPL }}\n          if [ -n \"${NO_SELF_HOST:-}\" ]; then\n            echo \"Skipping self-host for ${IMPL} due to NO_SELF_HOST variable\"\n          else\n            DO_SELF_HOST=1 ./ci.sh test ${IMPL}\n            # Check that self-hosted mode really ran\n            [ \"`grep -a \"mal-user>\" test-mal-*${IMPL}.debug | wc -l`\" -gt 800 ]\n          fi\n      - name: Print debug log\n        if: failure()\n        run: cat *.debug\n      - name: Archive logs and debug output\n        uses: actions/upload-artifact@v4\n        with:\n          name: logs.${{ matrix.IMPL }}\n          path: |\n            *.log\n            *.debug\n\n  macos:\n    needs: get-matrix\n    if: ${{ needs.get-matrix.outputs.do-macos == 'true' }}\n    runs-on: macos-12\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.get-matrix.outputs.matrix-macos) }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Build\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh build ${IMPL}\n      - name: Step Tests\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh test ${IMPL}\n      - name: Regression Tests\n        run: |\n          export ${{ matrix.IMPL }}\n          STEP=stepA REGRESS=1 HARD=1 OPTIONAL=0 ./ci.sh test ${IMPL}\n      - name: Performance Tests\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh perf ${IMPL}\n      - name: Self-hosted Tests\n        if: ${{ github.event.inputs.self-hosted != 'no' }}\n        run: |\n          export ${{ matrix.IMPL }}\n          if [ -n \"${NO_SELF_HOST:-}\" ]; then\n            echo \"Skipping self-host for ${IMPL} due to NO_SELF_HOST variable\"\n          else\n            DO_SELF_HOST=1 ./ci.sh test ${IMPL}\n            # Check that self-hosted mode really ran\n            [ \"`grep -a \"mal-user>\" test-mal-*${IMPL}.debug | wc -l`\" -gt 800 ]\n          fi\n      - name: Print debug log\n        if: failure()\n        run: cat *.debug\n      - name: Archive logs and debug output\n        uses: actions/upload-artifact@v4\n        with:\n          name: logs.${{ matrix.IMPL }}\n          path: |\n            *.log\n            *.debug\n\n  windows:\n    needs: get-matrix\n    if: ${{ needs.get-matrix.outputs.do-windows == 'true' }}\n    runs-on: windows-2022\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.get-matrix.outputs.matrix-windows) }}\n    steps:\n      - uses: Vampire/setup-wsl@v3\n        with:\n            distribution: Ubuntu-24.04\n      - name: Install requirements for WSL\n        shell: wsl-bash {0}\n        run: |\n          sudo apt update -y\n          sudo apt install make -y\n          sudo apt install python3 -y\n          sudo ln -s /usr/bin/python3 /usr/bin/python\n      - uses: actions/checkout@v4\n      - name: Build\n        shell: wsl-bash {0}\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh build ${IMPL}\n      - name: Step Tests\n        shell: wsl-bash {0}\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh test ${IMPL}\n      - name: Regression Tests\n        shell: wsl-bash {0}\n        run: |\n          export ${{ matrix.IMPL }}\n          STEP=stepA REGRESS=1 HARD=1 OPTIONAL=0 ./ci.sh test ${IMPL}\n      - name: Performance Tests\n        shell: wsl-bash {0}\n        run: |\n          export ${{ matrix.IMPL }}\n          ./ci.sh perf ${IMPL}\n      - name: Self-hosted Tests\n        if: ${{ github.event.inputs.self-hosted != 'no' }}\n        shell: wsl-bash {0}\n        run: |\n          export ${{ matrix.IMPL }}\n          if [ -n \"${NO_SELF_HOST:-}\" ]; then\n            echo \"Skipping self-host for ${IMPL} due to NO_SELF_HOST variable\"\n          else\n            DO_SELF_HOST=1 ./ci.sh test ${IMPL}\n            # Check that self-hosted mode really ran\n            [ \"`grep -a \"mal-user>\" test-mal-*${IMPL}.debug | wc -l`\" -gt 800 ]\n          fi\n      - name: Print debug log\n        if: failure()\n        run: cat *.debug\n      - name: Archive logs and debug output\n        uses: actions/upload-artifact@v4\n        with:\n          name: logs.${{ matrix.IMPL }}\n          path: |\n            *.log\n            *.debug\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.bash_history\n.cache\n.cargo\n.config\n.mal-history\n.mal_history\n.crystal\n.lein\n.local\n.m2\n.ivy2\n.sbt\n.npm\n.node-gyp\n*/experiments\nnode_modules\n*/notes\nGPATH\nGTAGS\nGRTAGS\nlogs\nold\ntmp/\n.xslt_mal_history\nzig-cache/\n"
  },
  {
    "path": ".gitmodules",
    "content": ""
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\n\n# matrix layout based on:\n# https://github.com/libressl-portable/portable/blob/9e090286b55def5ca2c0cc375c65023a70d8796e/.travis.yml\n\nmatrix:\n  include:\n    - {env: IMPL=objc NO_DOCKER=1, os: osx, osx_image: xcode7}\n    - {env: IMPL=swift NO_DOCKER=1, os: osx, osx_image: xcode7.3}\n    - {env: IMPL=swift3 NO_DOCKER=1, os: osx, osx_image: xcode8}\n    - {env: IMPL=swift4 NO_DOCKER=1, os: osx, osx_image: xcode10}\n    - {env: IMPL=swift5 NO_DOCKER=1, os: osx, osx_image: xcode11}\n\nscript:\n  # Build, test, perf\n  - ./ci.sh build ${IMPL}\n  - ./ci.sh test ${IMPL}\n  - STEP=stepA REGRESS=1 HARD=1 OPTIONAL=0 ./ci.sh test ${IMPL}\n  - ./ci.sh perf ${IMPL}\n"
  },
  {
    "path": "IMPLS.yml",
    "content": "IMPL:\n  - {IMPL: ada}\n  - {IMPL: ada.2}\n  - {IMPL: awk}\n  - {IMPL: bash, NO_SELF_HOST: 1}  # step8 timeout\n  - {IMPL: basic, basic_MODE: cbm, NO_SELF_HOST: 1}  # step4 OOM\n  - {IMPL: basic, basic_MODE: qbasic, NO_SELF_HOST: 1}  # step4 OOM\n  - {IMPL: bbc-basic}\n  - {IMPL: c}\n  - {IMPL: c.2}\n  - {IMPL: cpp}\n  - {IMPL: coffee}\n  - {IMPL: cs}\n  - {IMPL: chuck, NO_SELF_HOST_PERF: 1}  # perf OOM\n  - {IMPL: clojure, clojure_MODE: clj}\n  - {IMPL: clojure, clojure_MODE: cljs}\n  - {IMPL: common-lisp}\n  - {IMPL: crystal}\n  - {IMPL: d, d_MODE: gdc}\n  - {IMPL: d, d_MODE: ldc2}\n  - {IMPL: d, d_MODE: dmd}\n  - {IMPL: dart}\n  - {IMPL: elisp}\n  - {IMPL: elixir}\n  - {IMPL: elm}\n  - {IMPL: erlang, NO_SELF_HOST: 1}  # step4 silent exit on \"(DO 3)\"\n  - {IMPL: es6}\n  - {IMPL: factor}\n  - {IMPL: fantom}\n  - {IMPL: fennel}\n  - {IMPL: forth}\n  - {IMPL: fsharp}\n  - {IMPL: go}\n  - {IMPL: groovy}\n  - {IMPL: gnu-smalltalk}\n  - {IMPL: guile}\n  - {IMPL: hare}\n  - {IMPL: haskell}\n  - {IMPL: haxe, haxe_MODE: neko}\n  - {IMPL: haxe, haxe_MODE: python}\n  - {IMPL: haxe, haxe_MODE: cpp, SLOW: 1}\n  - {IMPL: haxe, haxe_MODE: js}\n  - {IMPL: hy}\n  - {IMPL: io, NO_SELF_HOST: 1, NO_SELF_HOST_PERF: 1}  # invalid pointer, perf OOM\n  - {IMPL: janet}\n  - {IMPL: java}\n  - {IMPL: java-truffle}\n  - {IMPL: jq, NO_SELF_HOST: 1}  # start-up failure and other issues\n  - {IMPL: js}\n  - {IMPL: julia}\n  - {IMPL: kotlin}\n  - {IMPL: latex3, NO_PERF: 1, NO_SELF_HOST: 1, SLOW: 1}\n  - {IMPL: livescript}\n  - {IMPL: logo}\n  - {IMPL: lua}\n  - {IMPL: make, NO_SELF_HOST: 1}  # step4 timeout\n  - {IMPL: mal, MAL_IMPL: js,      BUILD_IMPL: js,  NO_SELF_HOST: 1}\n  - {IMPL: mal, MAL_IMPL: js-mal,  BUILD_IMPL: js,  NO_SELF_HOST: 1, NO_PERF: 1, SLOW: 1}\n  - {IMPL: mal, MAL_IMPL: nim,     BUILD_IMPL: nim, NO_SELF_HOST: 1}\n  - {IMPL: mal, MAL_IMPL: nim-mal, BUILD_IMPL: nim, NO_SELF_HOST: 1, NO_PERF: 1, SLOW: 1}\n  - {IMPL: matlab, NO_SELF_HOST_PERF: 1}  # Octave, perf timeout\n  - {IMPL: miniMAL, NO_SELF_HOST_PERF: 1, SLOW: 1}  # perf timeout\n  - {IMPL: nasm, NO_SELF_HOST: 1}  # needs memory bump, then fails in step7/quasiquote\n  - {IMPL: nim}\n  - {IMPL: objpascal}\n  - {IMPL: objc}\n  - {IMPL: ocaml}\n  - {IMPL: perl}\n  - {IMPL: perl6}\n  - {IMPL: php}\n  - {IMPL: picolisp}\n  - {IMPL: pike}\n  - {IMPL: plpgsql, NO_SELF_HOST: 1, SLOW: 1}  # step3 timeout\n# - {IMPL: plsql}\n  - {IMPL: prolog}\n  - {IMPL: ps}\n  - {IMPL: powershell, NO_SELF_HOST: 1}  # works, but too slow be default enabled\n  - {IMPL: purs}\n  - {IMPL: python2}\n  - {IMPL: python3}\n  - {IMPL: r}\n  - {IMPL: racket}\n  - {IMPL: rexx}\n  - {IMPL: rpython, SLOW: 1}\n  - {IMPL: ruby}\n  - {IMPL: ruby.2}\n  - {IMPL: rust}\n  - {IMPL: scala}\n  - {IMPL: scheme, scheme_MODE: chibi}\n  - {IMPL: scheme, scheme_MODE: kawa}\n  - {IMPL: scheme, scheme_MODE: gauche}\n  - {IMPL: scheme, scheme_MODE: chicken}\n  - {IMPL: scheme, scheme_MODE: sagittarius}\n  - {IMPL: scheme, scheme_MODE: cyclone}\n# - {IMPL: scheme, scheme_MODE: foment}\n  - {IMPL: skew}\n  - {IMPL: sml, sml_MODE: polyml}\n  - {IMPL: sml, sml_MODE: mlton}\n  - {IMPL: sml, sml_MODE: mosml}\n  - {IMPL: tcl}\n  - {IMPL: ts}\n  - {IMPL: vala}\n  - {IMPL: vb}\n  - {IMPL: vhdl, NO_SELF_HOST_PERF: 1}  # perf timeout\n  - {IMPL: vimscript}\n  # no self-host perf for wasm due to mac stack overflow\n  - {IMPL: wasm, wasm_MODE: wasmtime,  NO_SELF_HOST_PERF: 1, NO_PERF: 1}\n  - {IMPL: wasm, wasm_MODE: wasmer,    NO_SELF_HOST_PERF: 1, NO_PERF: 1}\n  #- {IMPL: wasm, wasm_MODE: wax,       NO_SELF_HOST_PERF: 1}  # Hangs on GH Actions\n  - {IMPL: wasm, wasm_MODE: node,      NO_SELF_HOST_PERF: 1, NO_PERF: 1}\n  #- {IMPL: wasm, wasm_MODE: warpy,     NO_SELF_HOST_PERF: 1}  # Hangs on GH Actions\n  #- {IMPL: wasm, wasm_MODE: wace_libc, NO_SELF_HOST_PERF: 1}  # Hangs on GH Actions\n  - {IMPL: wren}\n  - {IMPL: xslt, NO_SELF_HOST: 1}  # step1 fail: \"Too many nested template ...\"\n  - {IMPL: yorick}\n  - {IMPL: zig}\n\n  # See .travis.yml (for older osx / xcode tests)\n  - {IMPL: swift3}\n# - {IMPL: swift3, NO_DOCKER: 1, OS: xcode8}\n  - {IMPL: swift4}\n# - {IMPL: swift4, NO_DOCKER: 1, OS: xcode10}\n  - {IMPL: swift6}\n# - {IMPL: swift6, NO_DOCKER: 1, OS: macos} # works but too expensive in GH Actions\n\n  - {IMPL: vbs, NO_SELF_HOST: 1, NO_DOCKER: 1, OS: windows} # self-host too slow/expensive in GH Actions\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2015 Joel Martin <github@martintribe.org>\n\nMal (make-a-lisp) is licensed under the MPL 2.0 (Mozilla Public\nLicense 2.0). The text of the MPL 2.0 license is included below and\ncan be found at https://www.mozilla.org/MPL/2.0/\n\nMany of the implementations run or compile using a line editing\nlibrary. In some cases, the implementations provide an option in the\ncode to switch between the GNU GPL licensed GNU readline library and\nthe BSD licensed editline (libedit) library.\n\n\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n\n\n"
  },
  {
    "path": "Makefile",
    "content": "# Usage/help\nall help:\n\t@echo\n\t@echo 'USAGE:'\n\t@echo\n\t@echo 'Rules/Targets:'\n\t@echo\n\t@echo 'make \"IMPL\"                       # build all steps of IMPL'\n\t@echo 'make \"build^IMPL\"                 # build all steps of IMPL'\n\t@echo 'make \"IMPL^STEP\"                  # build STEP of IMPL'\n\t@echo 'make \"build^IMPL^STEP\"            # build STEP of IMPL'\n\t@echo\n\t@echo 'make \"test\"                       # test all implementations'\n\t@echo 'make \"test^IMPL\"                  # test all steps of IMPL'\n\t@echo 'make \"test^STEP\"                  # test STEP for all implementations'\n\t@echo 'make \"test^IMPL^STEP\"             # test STEP of IMPL'\n\t@echo\n\t@echo 'make \"perf\"                       # run microbenchmarks for all implementations'\n\t@echo 'make \"perf^IMPL\"                  # run microbenchmarks for IMPL'\n\t@echo\n\t@echo 'make \"repl^IMPL\"                  # run stepA of IMPL'\n\t@echo 'make \"repl^IMPL^STEP\"             # test STEP of IMPL'\n\t@echo\n\t@echo 'make \"clean\"                      # run 'make clean' for all implementations'\n\t@echo 'make \"clean^IMPL\"                 # run 'make clean' for IMPL'\n\t@echo\n\t@echo 'make \"stats\"                      # run 'make stats' for all implementations'\n\t@echo 'make \"stats-lisp\"                 # run 'make stats-lisp' for all implementations'\n\t@echo 'make \"stats^IMPL\"                 # run 'make stats' for IMPL'\n\t@echo 'make \"stats-lisp^IMPL\"            # run 'make stats-lisp' for IMPL'\n\t@echo\n\t@echo 'Options/Settings:'\n\t@echo\n\t@echo 'make MAL_IMPL=IMPL \"test^mal...\"  # use IMPL for self-host tests'\n\t@echo 'make REGRESS=1 \"test...\"          # test with previous step tests too'\n\t@echo 'make DOCKERIZE=1 ...              # to dockerize above rules/targets'\n\t@echo 'make TEST_OPTS=\"--opt ...\"        # options to pass to runtest.py'\n\t@echo\n\t@echo 'Other:'\n\t@echo\n\t@echo 'make \"docker-build^IMPL\"          # build docker image for IMPL'\n\t@echo\n\t@echo 'make \"docker-shell^IMPL\"          # start bash shell in docker image for IMPL'\n\t@echo\n\n# Implementation specific settings are here:\ninclude Makefile.impls\n\n#\n# General command line settings\n#\n\nMAL_IMPL = js\n\n# Path to loccount for counting LOC stats\nLOCCOUNT = loccount\n\n# Extra options to pass to runtest.py\nTEST_OPTS =\n\n# Test with previous test files not just the test files for the\n# current step. Step 0 and 1 tests are special and not included in\n# later steps.\nREGRESS =\n\nHARD=\nDEFERRABLE=1\nOPTIONAL=1\n\n# Run target/rule within docker image for the implementation\nDOCKERIZE =\n\n\n#\n# General settings and utility functions\n#\n\nEXTENSION = .mal\n\nstep0 = step0_repl\nstep1 = step1_read_print\nstep2 = step2_eval\nstep3 = step3_env\nstep4 = step4_if_fn_do\nstep5 = step5_tco\nstep6 = step6_file\nstep7 = step7_quote\nstep8 = step8_macros\nstep9 = step9_try\nstepA = stepA_mal\n\nargv_STEP = step6_file\n\n\nregress_step0 = step0\nregress_step1 = step1\nregress_step2 = step2\nregress_step3 = $(regress_step2) step3\nregress_step4 = $(regress_step3) step4\nregress_step5 = $(regress_step4) step5\nregress_step6 = $(regress_step5) step6\nregress_step7 = $(regress_step6) step7\nregress_step8 = $(regress_step7) step8\nregress_step9 = $(regress_step8) step9\nregress_stepA = $(regress_step9) stepA\n\n# Needed some argument munging\nCOMMA = ,\nnoop =\nSPACE = $(noop) $(noop)\nexport FACTOR_ROOTS := .\n\nopt_HARD            = $(if $(strip $(HARD)),$(if $(filter t true T True TRUE 1 y yes Yes YES,$(HARD)),--hard,),)\nopt_DEFERRABLE      = $(if $(strip $(DEFERRABLE)),$(if $(filter t true T True TRUE 1 y yes Yes YES,$(DEFERRABLE)),--deferrable,--no-deferrable),--no-deferrable)\nopt_OPTIONAL        = $(if $(strip $(OPTIONAL)),$(if $(filter t true T True TRUE 1 y yes Yes YES,$(OPTIONAL)),--optional,--no-optional),--no-optional)\n\n# Return list of test files for a given step. If REGRESS is set then\n# test files will include step 2 tests through tests for the step\n# being tested.\nSTEP_TEST_FILES = $(strip $(wildcard \\\n\t\t    $(foreach s,$(if $(strip $(REGRESS)),\\\n\t\t\t$(filter-out $(if $(filter $(1),$(step5_EXCLUDES)),step5,),\\\n\t\t\t  $(regress_$(2)))\\\n\t\t\t,$(2)),\\\n\t\t      impls/$(1)/tests/$($(s))$(EXTENSION) impls/tests/$($(s))$(EXTENSION))))\n\n# DOCKERIZE utility functions\nlc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))\nimpl_to_image = ghcr.io/kanaka/mal-test-$(call lc,$(1)):$(shell ./voom-like-version.sh impls/$(1)/Dockerfile)\n\nactual_impl = $(if $(filter mal,$(1)),$(patsubst %-mal,%,$(MAL_IMPL)),$(1))\n\n# Takes impl\n# Returns nothing if DOCKERIZE is not set, otherwise returns the\n# docker prefix necessary to run make within the docker environment\n# for this impl\nget_build_command = $(strip $(foreach mode,$(1)_MODE, \\\n    $(if $(strip $(DOCKERIZE)),\\\n      docker run \\\n      -it --rm -u $(shell id -u) \\\n      -v $(dir $(abspath $(lastword $(MAKEFILE_LIST)))):/mal \\\n      -w /mal/impls/$(1) \\\n      $(if $(strip $($(mode))),-e $(mode)=$($(mode)),) \\\n      $(if $(filter factor,$(1)),-e FACTOR_ROOTS=$(FACTOR_ROOTS),) \\\n      $(call impl_to_image,$(1)) \\\n      make $(if $(strip $($(mode))),$(mode)=$($(mode)),) \\\n      ,\\\n      $(MAKE) $(if $(strip $($(mode))),$(mode)=$($(mode)),) -C impls/$(impl))))\n\n# Takes impl and step args. Optional env vars and dockerize args\n# Returns a command prefix (docker command and environment variables)\n# necessary to launch the given impl and step\nget_run_prefix = $(strip $(foreach mode,$(call actual_impl,$(1))_MODE, \\\n    $(if $(strip $(DOCKERIZE) $(4)),\\\n      docker run -e STEP=$($2) -e MAL_IMPL=$(MAL_IMPL) \\\n      -it --rm -u $(shell id -u) \\\n      -v $(dir $(abspath $(lastword $(MAKEFILE_LIST)))):/mal \\\n      -w /mal/impls/$(call actual_impl,$(1)) \\\n      $(if $(strip $($(mode))),-e $(mode)=$($(mode)),) \\\n      $(if $(filter factor,$(1)),-e FACTOR_ROOTS=$(FACTOR_ROOTS),) \\\n      $(foreach env,$(3),-e $(env)) \\\n      $(call impl_to_image,$(call actual_impl,$(1))) \\\n      ,\\\n      env STEP=$($2) MAL_IMPL=$(MAL_IMPL) \\\n      $(if $(strip $($(mode))),$(mode)=$($(mode)),) \\\n      $(if $(filter factor,$(1)),FACTOR_ROOTS=$(FACTOR_ROOTS),) \\\n      $(3))))\n\n# Takes impl and step\n# Returns the runtest command prefix (with runtest options) for testing the given step\nget_runtest_cmd = $(call get_run_prefix,$(1),$(2),$(if $(filter cs fsharp mal tcl vb,$(1)),RAW=1,)) \\\n\t\t    ../../runtest.py $(opt_HARD) $(opt_DEFERRABLE) $(opt_OPTIONAL) $(call $(1)_TEST_OPTS) $(TEST_OPTS)\n\n# Takes impl and step\n# Returns the runtest command prefix (with runtest options) for testing the given step\nget_argvtest_cmd = $(call get_run_prefix,$(1),$(2)) ../tests/run_argv_test.sh\n\n# Derived lists\nSTEPS = $(sort $(filter-out %_EXCLUDES,$(filter step%,$(.VARIABLES))))\nDO_IMPLS = $(filter-out $(SKIP_IMPLS),$(IMPLS))\nIMPL_TESTS = $(foreach impl,$(DO_IMPLS),test^$(impl))\nSTEP_TESTS = $(foreach step,$(STEPS),test^$(step))\nALL_TESTS = $(filter-out $(foreach e,$(step5_EXCLUDES),test^$(e)^step5),\\\n              $(strip $(sort \\\n                $(foreach impl,$(DO_IMPLS),\\\n                  $(foreach step,$(STEPS),test^$(impl)^$(step))))))\nALL_BUILDS = $(strip $(sort \\\n               $(foreach impl,$(DO_IMPLS),\\\n                 $(foreach step,$(STEPS),build^$(impl)^$(step)))))\n\nDOCKER_BUILD = $(foreach impl,$(DO_IMPLS),docker-build^$(impl))\n\nDOCKER_SHELL = $(foreach impl,$(DO_IMPLS),docker-shell^$(impl))\n\nIMPL_PERF = $(foreach impl,$(filter-out $(perf_EXCLUDES),$(DO_IMPLS)),perf^$(impl))\n\nIMPL_STATS = $(foreach impl,$(DO_IMPLS),stats^$(impl))\n\nIMPL_REPL = $(foreach impl,$(DO_IMPLS),repl^$(impl))\nALL_REPL = $(strip $(sort \\\n             $(foreach impl,$(DO_IMPLS),\\\n               $(foreach step,$(STEPS),repl^$(impl)^$(step)))))\n\n\n#\n# Build rules\n#\n\n# Enable secondary expansion for all rules\n.SECONDEXPANSION:\n\n# Build a program in an implementation directory\n# Make sure we always try and build first because the dependencies are\n# encoded in the implementation Makefile not here\n.PHONY: $(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),$(call $(i)_STEP_TO_PROG,$(s))))\n$(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),$(call $(i)_STEP_TO_PROG,$(s)))):\n\t$(foreach impl,$(word 2,$(subst /, ,$(@))),\\\n\t  $(if $(DOCKERIZE), \\\n\t    $(call get_build_command,$(impl)) $(patsubst impls/$(impl)/%,%,$(@)), \\\n\t    $(call get_build_command,$(impl)) $(subst impls/$(impl)/,,$(@))))\n\n# Allow IMPL, build^IMPL, IMPL^STEP, and build^IMPL^STEP\n$(DO_IMPLS): $$(foreach s,$$(STEPS),$$(call $$(@)_STEP_TO_PROG,$$(s)))\n\n$(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),build^$(i))): $$(foreach s,$$(STEPS),$$(call $$(word 2,$$(subst ^, ,$$(@)))_STEP_TO_PROG,$$(s)))\n\n$(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),$(i)^$(s))): $$(call $$(word 1,$$(subst ^, ,$$(@)))_STEP_TO_PROG,$$(word 2,$$(subst ^, ,$$(@))))\n\n$(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),build^$(i)^$(s))): $$(call $$(word 2,$$(subst ^, ,$$(@)))_STEP_TO_PROG,$$(word 3,$$(subst ^, ,$$(@))))\n\n\n\n#\n# Test rules\n#\n\n$(ALL_TESTS): $$(call $$(word 2,$$(subst ^, ,$$(@)))_STEP_TO_PROG,$$(word 3,$$(subst ^, ,$$(@))))\n\t@$(foreach impl,$(word 2,$(subst ^, ,$(@))),\\\n\t  $(foreach step,$(word 3,$(subst ^, ,$(@))),\\\n\t    echo \"(call STEP_TEST_FILES,$(impl),$(step)): $(call STEP_TEST_FILES,$(impl),$(step))\" && \\\n\t    cd impls/$(call actual_impl,$(impl)) && \\\n\t    $(foreach test,$(patsubst impls/%,%,$(call STEP_TEST_FILES,$(impl),$(step))),\\\n\t      echo '----------------------------------------------' && \\\n\t      echo 'Testing $@; step file: $+, test file: $(test)' && \\\n\t      echo 'Running: $(call get_runtest_cmd,$(impl),$(step)) ../$(test) -- ../$(impl)/run' && \\\n\t      $(call get_runtest_cmd,$(impl),$(step)) ../$(test) -- ../$(impl)/run && \\\n\t      $(if $(filter tests/$(argv_STEP)$(EXTENSION),$(test)),\\\n\t        echo '----------------------------------------------' && \\\n\t        echo 'Testing ARGV of $@; step file: $+' && \\\n\t        echo 'Running: $(call get_argvtest_cmd,$(impl),$(step)) ../$(impl)/run ' && \\\n\t        $(call get_argvtest_cmd,$(impl),$(step)) ../$(impl)/run  && ,\\\n\t\ttrue && ))\\\n\t    true))\n\n# Allow test, tests, test^STEP, test^IMPL, and test^IMPL^STEP\ntest: $(ALL_TESTS)\ntests: $(ALL_TESTS)\n\n$(IMPL_TESTS): $$(filter $$@^%,$$(ALL_TESTS))\n\n$(STEP_TESTS): $$(foreach step,$$(subst test^,,$$@),$$(filter %^$$(step),$$(ALL_TESTS)))\n\n\n#\n# Docker build rules\n#\n\ndocker-build: $(DOCKER_BUILD)\n\n$(DOCKER_BUILD):\n\t@echo \"----------------------------------------------\"; \\\n\t$(foreach impl,$(word 2,$(subst ^, ,$(@))),\\\n\t  echo \"Running: docker build -t $(call impl_to_image,$(impl)) .:\"; \\\n\t  cd impls/$(impl) && docker build -t $(call impl_to_image,$(impl)) .)\n\n#\n# Docker shell rules\n#\n\n$(DOCKER_SHELL):\n\t@echo \"----------------------------------------------\"; \\\n\t$(foreach impl,$(word 2,$(subst ^, ,$(@))),\\\n\t  echo \"Running: $(call get_run_prefix,$(impl),stepA,,dockerize) bash\"; \\\n\t  $(call get_run_prefix,$(impl),stepA,,dockerize) bash)\n\n\n#\n# Performance test rules\n#\n\nperf: $(IMPL_PERF)\n\n$(IMPL_PERF):\n\t@echo \"----------------------------------------------\"; \\\n\t$(foreach impl,$(word 2,$(subst ^, ,$(@))),\\\n\t  cd impls/$(call actual_impl,$(impl)); \\\n\t  echo \"Performance test for $(impl):\"; \\\n\t  echo 'Running: $(call get_run_prefix,$(impl),stepA) ../$(impl)/run ../tests/perf1.mal'; \\\n\t  $(call get_run_prefix,$(impl),stepA) ../$(impl)/run ../tests/perf1.mal; \\\n\t  echo 'Running: $(call get_run_prefix,$(impl),stepA) ../$(impl)/run ../tests/perf2.mal'; \\\n\t  $(call get_run_prefix,$(impl),stepA) ../$(impl)/run ../tests/perf2.mal; \\\n\t  echo 'Running: $(call get_run_prefix,$(impl),stepA) ../$(impl)/run ../tests/perf3.mal'; \\\n\t  $(call get_run_prefix,$(impl),stepA) ../$(impl)/run ../tests/perf3.mal)\n\n\n#\n# REPL invocation rules\n#\n\n$(ALL_REPL): $$(call $$(word 2,$$(subst ^, ,$$(@)))_STEP_TO_PROG,$$(word 3,$$(subst ^, ,$$(@))))\n\t@$(foreach impl,$(word 2,$(subst ^, ,$(@))),\\\n\t  $(foreach step,$(word 3,$(subst ^, ,$(@))),\\\n\t    cd impls/$(call actual_impl,$(impl)); \\\n\t    echo 'REPL implementation $(impl), step file: $+'; \\\n\t    echo 'Running: $(call get_run_prefix,$(impl),$(step)) ../$(impl)/run $(RUN_ARGS)'; \\\n\t    $(call get_run_prefix,$(impl),$(step)) ../$(impl)/run $(RUN_ARGS);))\n\n# Allow repl^IMPL^STEP and repl^IMPL (which starts REPL of stepA)\n$(IMPL_REPL): $$@^stepA\n\n#\n# Stats test rules\n#\n\n# For a concise summary:\n#   make stats | egrep -A1 \"^Stats for|^all\" | egrep -v \"^all|^--\"\nstats: $(IMPL_STATS)\n\n$(IMPL_STATS):\n\t@$(foreach impl,$(word 2,$(subst ^, ,$(@))),\\\n\t  echo \"Stats for $(impl):\"; \\\n\t  $(LOCCOUNT) -x \"[sS]tep[0-9]_.*|[.]md$$|tests|examples|Makefile|package.json|tsconfig.json|Cargo.toml|project.clj|node_modules|getline.cs|terminal.cs|elm-stuff|objpascal/regexpr|rdyncall|swift/templates\" impls/$(impl))\n\n#\n# Utility functions\n#\nprint-%:\n\t@echo \"$($(*))\"\n\n#\n# Recursive rules (call make FOO in each subdirectory)\n#\n\ndefine recur_template\n.PHONY: $(1)\n$(1): $(2)\n$(2):\n\t@echo \"----------------------------------------------\"; \\\n\t$$(foreach impl,$$(word 2,$$(subst ^, ,$$(@))),\\\n\t  echo \"Running: $$(call get_build_command,$$(impl)) --no-print-directory $(1)\"; \\\n\t  $$(call get_build_command,$$(impl)) --no-print-directory $(1))\nendef\n\nrecur_impls_ = $(filter-out $(foreach impl,$($(1)_EXCLUDES),$(1)^$(impl)),$(foreach impl,$(IMPLS),$(1)^$(impl)))\n\n# recursive clean\n$(eval $(call recur_template,clean,$(call recur_impls_,clean)))\n\n# recursive dist\n$(eval $(call recur_template,dist,$(call recur_impls_,dist)))\n"
  },
  {
    "path": "Makefile.impls",
    "content": "# HOWTO add a new implementation (named \"foo\"):\n# - Add \"foo\" to the IMPLS variable (alphabetical order)\n# - Add a new \"foo_STEP_TO_PROG\" variable.\n# - Add an \"impls/foo/run\" script.\n# - Add an \"impls/foo/Makefile\"\n# - Add an \"impls/foo/Dockerfile\"\n# - Implement each step in \"impls/foo/\".\n\n#\n# Implementation specific command line settings\n#\n\n# cbm or qbasic\nbasic_MODE = cbm\n# clj or cljs (Clojure vs ClojureScript/lumo)\nclojure_MODE = clj\n# gdc, ldc2, or dmd\nd_MODE = gdc\n# python, js, cpp, or neko\nhaxe_MODE = neko\n# octave or matlab\nmatlab_MODE = octave\n# scheme (chibi, kawa, gauche, chicken, sagittarius, cyclone, foment)\nscheme_MODE = chibi\n# sml (polyml, mlton, mosml)\nsml_MODE = polyml\n# wasmtime wasmer wax node warpy wace_libc direct js wace_fooboot\nwasm_MODE = wasmtime\n\n\n#\n# Implementation specific settings\n#\n\nIMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lisp cpp crystal cs d dart \\\n\telisp elixir elm erlang es6 factor fantom fennel forth fsharp go groovy gnu-smalltalk \\\n\tguile hare haskell haxe hy io janet java java-truffle js jq julia kotlin latex3 livescript logo lua make mal \\\n\tmatlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \\\n\tplsql powershell prolog ps purs python2 python3 r racket rexx rpython ruby ruby.2 rust scala scheme skew sml \\\n\tswift swift3 swift4 swift6 tcl ts vala vb vbs vhdl vimscript wasm wren yorick xslt zig\n\nstep5_EXCLUDES += bash        # never completes at 10,000\nstep5_EXCLUDES += basic       # too slow, and limited to ints of 2^16\nstep5_EXCLUDES += latex3      # no iteration, limited native stack\nstep5_EXCLUDES += make        # no TCO capability (iteration or recursion)\nstep5_EXCLUDES += mal         # host impl dependent\nstep5_EXCLUDES += matlab      # never completes at 10,000\nstep5_EXCLUDES += plpgsql     # too slow for 10,000\nstep5_EXCLUDES += plsql       # too slow for 10,000\nstep5_EXCLUDES += powershell  # too slow for 10,000\nstep5_EXCLUDES += prolog      # no iteration (but interpreter does TCO implicitly)\nstep5_EXCLUDES += sml         # not implemented :(\nstep5_EXCLUDES += $(if $(filter cpp,$(haxe_MODE)),haxe,) # cpp finishes 10,000, segfaults at 100,000\nstep5_EXCLUDES += xslt\t\t  # iteration cannot be expressed\nstep5_EXCLUDES += vbs         # too slow for 10,000\n\ndist_EXCLUDES += mal\n# TODO: still need to implement dist\ndist_EXCLUDES += guile io julia matlab swift\n\n\n# Extra options to pass to runtest.py\nbbc-basic_TEST_OPTS = --test-timeout 60\nguile_TEST_OPTS = --test-timeout 120\nio_TEST_OPTS = --test-timeout 120\njava-truffle_TEST_OPTS = --start-timeout 30\nlogo_TEST_OPTS = --start-timeout 60 --test-timeout 120\nmal_TEST_OPTS = --start-timeout 60 --test-timeout 120\nminiMAL_TEST_OPTS = --start-timeout 60 --test-timeout 120\nperl6_TEST_OPTS = --test-timeout=60\nplpgsql_TEST_OPTS = --start-timeout 60 --test-timeout 180\nplsql_TEST_OPTS = --start-timeout 120 --test-timeout 120\nvimscript_TEST_OPTS = --test-timeout 30\nifeq ($(MAL_IMPL),vimscript)\nmal_TEST_OPTS = --start-timeout 60 --test-timeout 180\nelse ifeq ($(MAL_IMPL),powershell)\nmal_TEST_OPTS = --start-timeout 60 --test-timeout 180\nelse ifeq ($(MAL_IMPL),vbs)\nmal_TEST_OPTS = --start-timeout 60 --test-timeout 180 --no-pty\nendif\nxslt_TEST_OPTS = --test-timeout 120\nvbs_TEST_OPTS = --no-pty\n\n\n#\n# Implementation specific utility functions\n#\n\nbasic_STEP_TO_PROG_cbm          = impls/basic/$($(1)).bas\nbasic_STEP_TO_PROG_qbasic       = impls/basic/$($(1))\n\nclojure_STEP_TO_PROG_clj        = impls/clojure/target/$($(1)).jar\nclojure_STEP_TO_PROG_cljs       = impls/clojure/src/mal/$($(1)).cljc\n\nhaxe_STEP_TO_PROG_neko          = impls/haxe/$($(1)).n\nhaxe_STEP_TO_PROG_python        = impls/haxe/$($(1)).py\nhaxe_STEP_TO_PROG_cpp           = impls/haxe/cpp/$($(1))\nhaxe_STEP_TO_PROG_js            = impls/haxe/$($(1)).js\n\nscheme_STEP_TO_PROG_chibi       = impls/scheme/$($(1)).scm\nscheme_STEP_TO_PROG_kawa        = impls/scheme/out/$($(1)).class\nscheme_STEP_TO_PROG_gauche      = impls/scheme/$($(1)).scm\nscheme_STEP_TO_PROG_chicken     = impls/scheme/$($(1))\nscheme_STEP_TO_PROG_sagittarius = impls/scheme/$($(1)).scm\nscheme_STEP_TO_PROG_cyclone     = impls/scheme/$($(1))\nscheme_STEP_TO_PROG_foment      = impls/scheme/$($(1)).scm\n\n# Map of step (e.g. \"step8\") to executable file for that step\nada_STEP_TO_PROG =           impls/ada/$($(1))\nada.2_STEP_TO_PROG =         impls/ada.2/$($(1))\nawk_STEP_TO_PROG =           impls/awk/$($(1)).awk\nbash_STEP_TO_PROG =          impls/bash/$($(1)).sh\nbasic_STEP_TO_PROG =         $(basic_STEP_TO_PROG_$(basic_MODE))\nbbc-basic_STEP_TO_PROG =     impls/bbc-basic/$($(1)).bas\nc_STEP_TO_PROG =             impls/c/$($(1))\nc.2_STEP_TO_PROG =           impls/c.2/$($(1))\nchuck_STEP_TO_PROG =         impls/chuck/$($(1)).ck\nclojure_STEP_TO_PROG =       $(clojure_STEP_TO_PROG_$(clojure_MODE))\ncoffee_STEP_TO_PROG =        impls/coffee/$($(1)).coffee\ncommon-lisp_STEP_TO_PROG =   impls/common-lisp/$($(1))\ncpp_STEP_TO_PROG =           impls/cpp/$($(1))\ncrystal_STEP_TO_PROG =       impls/crystal/$($(1))\ncs_STEP_TO_PROG =            impls/cs/$($(1)).exe\nd_STEP_TO_PROG =             impls/d/$($(1))\ndart_STEP_TO_PROG =          impls/dart/$($(1)).dart\nelisp_STEP_TO_PROG =         impls/elisp/$($(1)).el\nelixir_STEP_TO_PROG =        impls/elixir/lib/mix/tasks/$($(1)).ex\nelm_STEP_TO_PROG =           impls/elm/$($(1)).js\nerlang_STEP_TO_PROG =        impls/erlang/$($(1))\nes6_STEP_TO_PROG =           impls/es6/$($(1)).mjs\nfactor_STEP_TO_PROG =        impls/factor/$($(1))/$($(1)).factor\nfantom_STEP_TO_PROG =        impls/fantom/lib/fan/$($(1)).pod\nfennel_STEP_TO_PROG =        impls/fennel/$($(1)).fnl\nforth_STEP_TO_PROG =         impls/forth/$($(1)).fs\nfsharp_STEP_TO_PROG =        impls/fsharp/$($(1)).exe\ngo_STEP_TO_PROG =            impls/go/$($(1))\ngroovy_STEP_TO_PROG =        impls/groovy/$($(1)).groovy\ngnu-smalltalk_STEP_TO_PROG = impls/gnu-smalltalk/$($(1)).st\nguile_STEP_TO_PROG =         impls/guile/$($(1)).scm\nhare_STEP_TO_PROG =          impls/hare/$($(1))\nhaskell_STEP_TO_PROG =       impls/haskell/$($(1))\nhaxe_STEP_TO_PROG =          $(haxe_STEP_TO_PROG_$(haxe_MODE))\nhy_STEP_TO_PROG =            impls/hy/$($(1)).hy\nio_STEP_TO_PROG =            impls/io/$($(1)).io\njanet_STEP_TO_PROG =         impls/janet/$($(1)).janet\njava_STEP_TO_PROG =          impls/java/target/classes/mal/$($(1)).class\njava-truffle_STEP_TO_PROG =  impls/java-truffle/build/classes/java/main/truffle/mal/$($(1)).class\njs_STEP_TO_PROG =            impls/js/$($(1)).js\njq_STEP_PROG =               impls/jq/$($(1)).jq\njulia_STEP_TO_PROG =         impls/julia/$($(1)).jl\nkotlin_STEP_TO_PROG =        impls/kotlin/$($(1)).jar\nlatex3_STEP_TO_PROG =        impls/latex3/$($(1)).tex\nlivescript_STEP_TO_PROG =    impls/livescript/$($(1)).js\nlogo_STEP_TO_PROG =          impls/logo/$($(1)).lg\nlua_STEP_TO_PROG =           impls/lua/$($(1)).lua\nmake_STEP_TO_PROG =          impls/make/$($(1)).mk\nmal_STEP_TO_PROG =           impls/mal/$($(1)).mal\nmatlab_STEP_TO_PROG =        impls/matlab/$($(1)).m\nminiMAL_STEP_TO_PROG =       impls/miniMAL/$($(1)).json\nnasm_STEP_TO_PROG =          impls/nasm/$($(1))\nnim_STEP_TO_PROG =           impls/nim/$($(1))\nobjc_STEP_TO_PROG =          impls/objc/$($(1))\nobjpascal_STEP_TO_PROG =     impls/objpascal/$($(1))\nocaml_STEP_TO_PROG =         impls/ocaml/$($(1))\nperl_STEP_TO_PROG =          impls/perl/$($(1)).pl\nperl6_STEP_TO_PROG =         impls/perl6/$($(1)).pl\nphp_STEP_TO_PROG =           impls/php/$($(1)).php\npicolisp_STEP_TO_PROG =      impls/picolisp/$($(1)).l\npike_STEP_TO_PROG =          impls/pike/$($(1)).pike\nplpgsql_STEP_TO_PROG =       impls/plpgsql/$($(1)).sql\nplsql_STEP_TO_PROG =         impls/plsql/$($(1)).sql\npowershell_STEP_TO_PROG =    impls/powershell/$($(1)).ps1\nprolog_STEP_TO_PROG =        impls/prolog/$($(1)).pl\nps_STEP_TO_PROG =            impls/ps/$($(1)).ps\npurs_STEP_TO_PROG =          impls/purs/$($(1)).js\npython2_STEP_TO_PROG =       impls/python2/$($(1)).py\npython3_STEP_TO_PROG =       impls/python3/$($(1)).py\nr_STEP_TO_PROG =             impls/r/$($(1)).r\nracket_STEP_TO_PROG =        impls/racket/$($(1)).rkt\nrexx_STEP_TO_PROG =          impls/rexx/$($(1)).rexxpp\nrpython_STEP_TO_PROG =       impls/rpython/$($(1))\nruby_STEP_TO_PROG =          impls/ruby/$($(1)).rb\nruby.2_STEP_TO_PROG =        impls/ruby.2/$($(1)).rb\nrust_STEP_TO_PROG =          impls/rust/target/release/$($(1))\nscala_STEP_TO_PROG =         impls/scala/target/scala-2.11/classes/$($(1)).class\nscheme_STEP_TO_PROG =        $(scheme_STEP_TO_PROG_$(scheme_MODE))\nskew_STEP_TO_PROG =          impls/skew/$($(1)).js\nsml_STEP_TO_PROG =           impls/sml/$($(1))\nswift_STEP_TO_PROG =         impls/swift/$($(1))\nswift3_STEP_TO_PROG =        impls/swift3/$($(1))\nswift4_STEP_TO_PROG =        impls/swift4/$($(1))\nswift6_STEP_TO_PROG =        impls/swift6/$($(1))\ntcl_STEP_TO_PROG =           impls/tcl/$($(1)).tcl\nts_STEP_TO_PROG =            impls/ts/$($(1)).js\nvala_STEP_TO_PROG =          impls/vala/$($(1))\nvb_STEP_TO_PROG =            impls/vb/$($(1)).exe\nvbs_STEP_TO_PROG =           impls/vbs/$($(1)).vbs\nvhdl_STEP_TO_PROG =          impls/vhdl/$($(1))\nvimscript_STEP_TO_PROG =     impls/vimscript/$($(1)).vim\nwasm_STEP_TO_PROG =          impls/wasm/$($(1)).wasm\nwren_STEP_TO_PROG =          impls/wren/$($(1)).wren\nyorick_STEP_TO_PROG =        impls/yorick/$($(1)).i\nxslt_STEP_TO_PROG =          impls/xslt/$($(1))\nzig_STEP_TO_PROG =           impls/zig/$($(1))\n"
  },
  {
    "path": "README.md",
    "content": "# mal - Make a Lisp\n\n[![Build and Test](https://github.com/kanaka/mal/actions/workflows/main.yml/badge.svg)](https://github.com/kanaka/mal/actions/workflows/main.yml)\n\n## Description\n\n**1. Mal is a Clojure inspired Lisp interpreter**\n\n**2. Mal is a learning tool**\n\nEach implementation of mal is separated into\n11 incremental, self-contained (and testable) steps that demonstrate\ncore concepts of Lisp. The last step is capable of self-hosting\n(running the mal implementation of mal). See the [make-a-lisp process\nguide](process/guide.md).\n\nThe make-a-lisp steps are:\n\n* [step0_repl](process/guide.md#step-0-the-repl)\n* [step1_read_print](process/guide.md#step-1-read-and-print)\n* [step2_eval](process/guide.md#step-2-eval)\n* [step3_env](process/guide.md#step-3-environments)\n* [step4_if_fn_do](process/guide.md#step-4-if-fn-do)\n* [step5_tco](process/guide.md#step-5-tail-call-optimization)\n* [step6_file](process/guide.md#step-6-files-mutation-and-evil)\n* [step7_quote](process/guide.md#step-7-quoting)\n* [step8_macros](process/guide.md#step-8-macros)\n* [step9_try](process/guide.md#step-9-try)\n* [stepA_mal](process/guide.md#step-a-metadata-self-hosting-and-interop)\n\nEach make-a-lisp step has an associated architectural diagram. That elements\nthat are new for that step are highlighted in red.\nHere is the final architecture once [step A](process/guide.md#stepA)\nis complete:\n\n![stepA_mal architecture](process/steps.png)\n\nIf you are interested in creating a mal implementation (or just\ninterested in using mal for something) you are welcome to to join our\n[Discord](https://discord.gg/CKgnNbJBpF). In addition to the [make-a-lisp\nprocess guide](process/guide.md) there is also a [mal/make-a-lisp\nFAQ](docs/FAQ.md) where I attempt to answer some common questions.\n\n\n**3. Mal is implemented in 89 languages (95 different implementations and 118 runtime modes)**\n\n| Language | Creator |\n| -------- | ------- |\n| [Ada](#ada) | [Chris Moore](https://github.com/zmower) |\n| [Ada #2](#ada2) | [Nicolas Boulenguez](https://github.com/asarhaddon) |\n| [GNU Awk](#gnu-awk) | [Mitsuru Kariya](https://github.com/kariya-mitsuru) |\n| [Bash 4](#bash-4) | [Joel Martin](https://github.com/kanaka)  |\n| [BASIC](#basic-c64-and-qbasic) (C64 &amp; QBasic) | [Joel Martin](https://github.com/kanaka) |\n| [BBC BASIC V](#bbc-basic-v) | [Ben Harris](https://github.com/bjh21) |\n| [C](#c) | [Joel Martin](https://github.com/kanaka)  |\n| [C #2](#c2) | [Duncan Watts](https://github.com/fungiblecog)  |\n| [C++](#c-1) | [Stephen Thirlwall](https://github.com/sdt) |\n| [C#](#c-2) | [Joel Martin](https://github.com/kanaka)  |\n| [ChucK](#chuck) | [Vasilij Schneidermann](https://github.com/wasamasa) |\n| [Clojure](#clojure) (Clojure &amp; ClojureScript) | [Joel Martin](https://github.com/kanaka) |\n| [CoffeeScript](#coffeescript) | [Joel Martin](https://github.com/kanaka)  |\n| [Common Lisp](#common-lisp) | [Iqbal Ansari](https://github.com/iqbalansari) |\n| [Crystal](#crystal) | [Linda_pp](https://github.com/rhysd) |\n| [D](#d) | [Dov Murik](https://github.com/dubek) |\n| [Dart](#dart) | [Harry Terkelsen](https://github.com/hterkelsen) |\n| [Elixir](#elixir) | [Martin Ek](https://github.com/ekmartin) |\n| [Elm](#elm) | [Jos van Bakel](https://github.com/c0deaddict) |\n| [Emacs Lisp](#emacs-lisp) | [Vasilij Schneidermann](https://github.com/wasamasa) |\n| [Erlang](#erlang) | [Nathan Fiedler](https://github.com/nlfiedler) |\n| [ES6](#es6-ecmascript-2015) (ECMAScript 2015) | [Joel Martin](https://github.com/kanaka) |\n| [F#](#f) | [Peter Stephens](https://github.com/pstephens) |\n| [Factor](#factor) | [Jordan Lewis](https://github.com/jordanlewis) |\n| [Fantom](#fantom) | [Dov Murik](https://github.com/dubek) |\n| [Fennel](#fennel) | [sogaiu](https://github.com/sogaiu) |\n| [Forth](#forth) | [Chris Houser](https://github.com/chouser) |\n| [GNU Guile](#gnu-guile-21) | [Mu Lei](https://github.com/NalaGinrut) |\n| [GNU Smalltalk](#gnu-smalltalk) | [Vasilij Schneidermann](https://github.com/wasamasa) |\n| [Go](#go) | [Joel Martin](https://github.com/kanaka)  |\n| [Groovy](#groovy) | [Joel Martin](https://github.com/kanaka)  |\n| [Hare](#hare) | [Lou Woell](http://github.com/einsiedlerspiel) |\n| [Haskell](#haskell) | [Joel Martin](https://github.com/kanaka)  |\n| [Haxe](#haxe-neko-python-c-and-javascript) (Neko, Python, C++, &amp; JS) | [Joel Martin](https://github.com/kanaka) |\n| [Hy](#hy) | [Joel Martin](https://github.com/kanaka)  |\n| [Io](#io) | [Dov Murik](https://github.com/dubek) |\n| [Janet](#janet) | [sogaiu](https://github.com/sogaiu) |\n| [Java](#java-17) | [Joel Martin](https://github.com/kanaka)  |\n| [Java Truffle](#java-using-truffle-for-graalvm) (Truffle/GraalVM) | [Matt McGill](https://github.com/mmcgill) |\n| [JavaScript](#javascriptnode) ([Demo](http://kanaka.github.io/mal)) | [Joel Martin](https://github.com/kanaka) |\n| [jq](#jq) | [Ali MohammadPur](https://github.com/alimpfard) |\n| [Julia](#julia) | [Joel Martin](https://github.com/kanaka)  |\n| [Kotlin](#kotlin) | [Javier Fernandez-Ivern](https://github.com/ivern) |\n| [LaTeX3](#latex3) | [Nicolas Boulenguez](https://github.com/asarhaddon) |\n| [LiveScript](#livescript) | [Jos van Bakel](https://github.com/c0deaddict) |\n| [Logo](#logo) | [Dov Murik](https://github.com/dubek) |\n| [Lua](#lua) | [Joel Martin](https://github.com/kanaka)  |\n| [GNU Make](#gnu-make-381) | [Joel Martin](https://github.com/kanaka)  |\n| [mal itself](#mal) | [Joel Martin](https://github.com/kanaka)  |\n| [MATLAB](#matlab-gnu-octave-and-matlab) (GNU Octave &amp; MATLAB) | [Joel Martin](https://github.com/kanaka) |\n| [miniMAL](#minimal) ([Repo](https://github.com/kanaka/miniMAL), [Demo](https://kanaka.github.io/miniMAL/)) | [Joel Martin](https://github.com/kanaka) |\n| [NASM](#nasm) | [Ben Dudson](https://github.com/bendudson) |\n| [Nim](#nim-104) | [Dennis Felsing](https://github.com/def-) |\n| [Object Pascal](#object-pascal) | [Joel Martin](https://github.com/kanaka)  |\n| [Objective C](#objective-c) | [Joel Martin](https://github.com/kanaka)  |\n| [OCaml](#ocaml-4010) | [Chris Houser](https://github.com/chouser) |\n| [Perl](#perl-5) | [Joel Martin](https://github.com/kanaka)  |\n| [Perl 6](#perl-6) | [Hinrik Örn Sigurðsson](https://github.com/hinrik) |\n| [PHP](#php-53) | [Joel Martin](https://github.com/kanaka)  |\n| [Picolisp](#picolisp) | [Vasilij Schneidermann](https://github.com/wasamasa) |\n| [Pike](#pike) | [Dov Murik](https://github.com/dubek) |\n| [PL/pgSQL](#plpgsql-postgresql-sql-procedural-language) (PostgreSQL) | [Joel Martin](https://github.com/kanaka) |\n| [PL/SQL](#plsql-oracle-sql-procedural-language) (Oracle) | [Joel Martin](https://github.com/kanaka) |\n| [PostScript](#postscript-level-23) | [Joel Martin](https://github.com/kanaka)  |\n| [PowerShell](#powershell) | [Joel Martin](https://github.com/kanaka)  |\n| [Prolog](#prolog-logical-language) | [Nicolas Boulenguez](https://github.com/asarhaddon) |\n| [PureScript](#purescript) | [mrsekut](https://github.com/mrsekut) |\n| [Python2](#python2) | [Joel Martin](https://github.com/kanaka) |\n| [Python3](#python3) | [Gavin Lewis](https://github.com/epylar) |\n| [RPython](#rpython) | [Joel Martin](https://github.com/kanaka)  |\n| [R](#r) | [Joel Martin](https://github.com/kanaka)  |\n| [Racket](#racket-53) | [Joel Martin](https://github.com/kanaka)  |\n| [Rexx](#rexx) | [Dov Murik](https://github.com/dubek) |\n| [Ruby](#ruby-19) | [Joel Martin](https://github.com/kanaka)  |\n| [Ruby #2](#ruby) | [Ryan Cook](https://github.com/cookrn)  |\n| [Rust](#rust-138) | [Joel Martin](https://github.com/kanaka)  |\n| [Scala](#scala) | [Joel Martin](https://github.com/kanaka)  |\n| [Scheme (R7RS)](#scheme-r7rs) | [Vasilij Schneidermann](https://github.com/wasamasa) |\n| [Skew](#skew) | [Dov Murik](https://github.com/dubek) |\n| [Standard ML](#sml) | [Fabian Bergström](https://github.com/fabjan) |\n| [Swift 3](#swift-3) | [Joel Martin](https://github.com/kanaka)  |\n| [Swift 4](#swift-4) | [陆遥](https://github.com/LispLY)  |\n| [Swift 6](#swift-6) | [Oleg Montak](https://github.com/MontakOleg)  |\n| [Tcl](#tcl-86) | [Dov Murik](https://github.com/dubek) |\n| [TypeScript](#typescript) | [Masahiro Wakame](https://github.com/vvakame) |\n| [Vala](#vala) | [Simon Tatham](https://github.com/sgtatham) |\n| [VHDL](#vhdl) | [Dov Murik](https://github.com/dubek) |\n| [Vimscript](#vimscript) | [Dov Murik](https://github.com/dubek) |\n| [Visual Basic.NET](#visual-basicnet) | [Joel Martin](https://github.com/kanaka)  |\n| [Visual Basic Script](#visual-basic-script) | [刘百超](https://github.com/OldLiu001)  |\n| [WebAssembly](#webassembly-wasm) (wasm) | [Joel Martin](https://github.com/kanaka) |\n| [Wren](#wren) | [Dov Murik](https://github.com/dubek) |\n| [XSLT](#xslt) | [Ali MohammadPur](https://github.com/alimpfard) |\n| [Yorick](#yorick) | [Dov Murik](https://github.com/dubek) |\n| [Zig](#zig) | [Josh Tobin](https://github.com/rjtobin) |\n\n\n## Presentations\n\nMal was presented publicly for the first time in a lightning talk at\nClojure West 2014 (unfortunately there is no video). See\nexamples/clojurewest2014.mal for the presentation that was given at the\nconference (yes, the presentation is a mal program).\n\nAt Midwest.io 2015, Joel Martin gave a presentation on Mal titled\n\"Achievement Unlocked: A Better Path to Language Learning\".\n[Video](https://www.youtube.com/watch?v=lgyOAiRtZGw),\n[Slides](http://kanaka.github.io/midwest.io.mal/).\n\nMore recently Joel gave a presentation on \"Make Your Own Lisp Interpreter\nin 10 Incremental Steps\" at LambdaConf 2016:\n[Part 1](https://www.youtube.com/watch?v=jVhupfthTEk),\n[Part 2](https://www.youtube.com/watch?v=X5OQBMGpaTU),\n[Part 3](https://www.youtube.com/watch?v=6mARZzGgX4U),\n[Part 4](https://www.youtube.com/watch?v=dCO1SYR5kDU),\n[Slides](http://kanaka.github.io/lambdaconf/).\n\n## Building/running implementations\n\nThe simplest way to run any given implementation is to use docker.\nEvery implementation has a docker image pre-built with language\ndependencies installed. You can launch the REPL using a convenient\ntarget in the top level Makefile (where IMPL is the implementation\ndirectory name and stepX is the step to run):\n\n```\nmake DOCKERIZE=1 \"repl^IMPL^stepX\"\n    # OR stepA is the default step:\nmake DOCKERIZE=1 \"repl^IMPL\"\n```\n\n## External / Alternate Implementations\n\nThe following implementations are maintained as separate projects:\n\n### HolyC\n\n* [by Alexander Bagnalla](https://github.com/bagnalla/holyc_mal)\n\n### Rust\n\n* [by Tim Morgan](https://github.com/seven1m/mal-rust)\n* [by vi](https://github.com/vi/mal-rust-vi) - using [Pest](https://pest.rs/) grammar, not using typical Mal infrastructure (cargo-ized steps and built-in converted tests).\n\n### Swift 2\n\n* [by Keith Rollin](https://github.com/kanaka/mal/tree/fbfe678/impls/swift) - This implementation used to be in the repo. However, Swift 2 is no longer easily buildable/testable.\n\n### Q\n\n* [by Ali Mohammad Pur](https://github.com/alimpfard/mal/tree/q/impls/q) - The Q implementation works fine but it requires a proprietary manual download that can't be Dockerized (or integrated into the mal CI pipeline) so for now it remains a separate project.\n\n\n## Other mal Projects\n\n * [malc](https://github.com/dubek/malc) - Mal (Make A Lisp) compiler. Compiles a Mal program to LLVM assembly language, then binary.\n * [malcc](https://github.com/seven1m/malcc) - malcc is an incremental compiler implementation for the Mal language. It uses the Tiny C Compiler as the compiler backend and has full support for the Mal language, including macros, tail-call elimination, and even run-time eval. [\"I Built a Lisp Compiler\"](https://mpov.timmorgan.org/i-built-a-lisp-compiler/) post about the process.\n * [frock](https://github.com/chr15m/frock) - Clojure-flavoured PHP. Uses mal/php to run programs.\n * [flk](https://github.com/chr15m/flk) - A LISP that runs wherever Bash is\n * [glisp](https://github.com/baku89/glisp) - Self-bootstrapping graphic design tool on Lisp. [Live Demo](https://baku89.com/glisp/)\n * [mal2py-compiler](https://github.com/jcguu95/mal2py-compiler) - MAL-to-Python. A fork of the python3 implementation that compiles mal to python with a 16x performance improvement on the perf3 synthetic benchmark.\n\n\n## Implementation Details\n\n### Ada\n\nThe Ada implementation was developed with GNAT 4.9 on debian. It also\ncompiles unchanged on windows if you have windows versions of git,\nGNAT and (optionally) make.  There are no external dependencies\n(readline not implemented).\n\n```\ncd impls/ada\nmake\n./stepX_YYY\n```\n\n### Ada.2\n\nThe second Ada implementation was developed with GNAT 8 and links with\nthe GNU readline library.\n\n```\ncd impls/ada\nmake\n./stepX_YYY\n```\n\n### GNU awk\n\nThe GNU awk implementation of mal has been tested with GNU awk 4.1.1.\n\n```\ncd impls/gawk\ngawk -O -f stepX_YYY.awk\n```\n\n### Bash 4\n\n```\ncd impls/bash\nbash stepX_YYY.sh\n```\n\n### BASIC (C64 and QBasic)\n\nThe BASIC implementation uses a preprocessor that can generate BASIC\ncode that is compatible with both C64 BASIC (CBM v2) or QBasic. The\nC64 mode has been tested with\n[cbmbasic](https://github.com/kanaka/cbmbasic) (the patched version is\ncurrently required to fix issues with line input) and the QBasic mode\nhas been tested with [FreeBASIC](freebasic.net).\n\nGenerate C64 code and run it using cbmbasic:\n\n```\ncd impls/basic\nmake MODE=cbm stepX_YYY.bas\nSTEP=stepX_YYY basic_MODE=cbm ./run\n```\n\nGenerate QBasic code, compile using FreeBASIC, and execute it:\n\n```\ncd impls/basic\nmake MODE=qbasic stepX_YYY.bas\nmake MODE=qbasic stepX_YYY\n./stepX_YYY\n```\n\nThanks to [Steven Syrek](https://github.com/sjsyrek) for the original\ninspiration for this implementation.\n\n### BBC BASIC V\n\nThe BBC BASIC V implementation can run in the Brandy interpreter:\n\n```\ncd impls/bbc-basic\nbrandy -quit stepX_YYY.bbc\n```\n\nOr in ARM BBC BASIC V under RISC OS 3 or later:\n\n```\n*Dir bbc-basic.riscos\n*Run setup\n*Run stepX_YYY\n```\n\n### C\n\nThe C implementation of mal requires the following libraries (lib and\nheader packages): glib, libffi6, libgc, and either the libedit or GNU readline\nlibrary.\n\n```\ncd impls/c\nmake\n./stepX_YYY\n```\n\n### C.2\n\nThe second C implementation of mal requires the following libraries (lib and\nheader packages): libedit, libgc, libdl, and libffi.\n\n```\ncd impls/c.2\nmake\n./stepX_YYY\n```\n\n\n### C++\n\nThe C++ implementation of mal requires g++-4.9 or clang++-3.5 and\na readline compatible library to build. See the `cpp/README.md` for\nmore details:\n\n```\ncd impls/cpp\nmake\n    # OR\nmake CXX=clang++-3.5\n./stepX_YYY\n```\n\n\n### C# ###\n\nThe C# implementation of mal has been tested on Linux using the Mono\nC# compiler (mcs) and the Mono runtime (version 2.10.8.1). Both are\nrequired to build and run the C# implementation.\n\n```\ncd impls/cs\nmake\nmono ./stepX_YYY.exe\n```\n\n### ChucK\n\nThe ChucK implementation has been tested with ChucK 1.3.5.2.\n\n```\ncd impls/chuck\n./run\n```\n\n### Clojure\n\nFor the most part the Clojure implementation requires Clojure 1.5,\nhowever, to pass all tests, Clojure 1.8.0-RC4 is required.\n\n```\ncd impls/clojure\nlein with-profile +stepX trampoline run\n```\n\n### CoffeeScript\n\n```\nsudo npm install -g coffee-script\ncd impls/coffee\ncoffee ./stepX_YYY\n```\n\n### Common Lisp\n\nThe implementation has been tested with SBCL, CCL, CMUCL, GNU CLISP, ECL and\nAllegro CL on Ubuntu 16.04 and Ubuntu 12.04, see\nthe [README](impls/common-lisp/README.org) for more details. Provided you have the\ndependencies mentioned installed, do the following to run the implementation\n\n```\ncd impls/common-lisp\nmake\n./run\n```\n\n### Crystal\n\nThe Crystal implementation of mal has been tested with Crystal 0.26.1.\n\n```\ncd impls/crystal\ncrystal run ./stepX_YYY.cr\n    # OR\nmake   # needed to run tests\n./stepX_YYY\n```\n\n### D\n\nThe D implementation of mal was tested with GDC 4.8.  It requires the GNU\nreadline library.\n\n```\ncd impls/d\nmake\n./stepX_YYY\n```\n\n### Dart\n\nThe Dart implementation has been tested with Dart 1.20.\n\n```\ncd impls/dart\ndart ./stepX_YYY\n```\n\n### Emacs Lisp\n\nThe Emacs Lisp implementation of mal has been tested with Emacs 24.3\nand 24.5.  While there is very basic readline editing (`<backspace>`\nand `C-d` work, `C-c` cancels the process), it is recommended to use\n`rlwrap`.\n\n```\ncd impls/elisp\nemacs -Q --batch --load stepX_YYY.el\n# with full readline support\nrlwrap emacs -Q --batch --load stepX_YYY.el\n```\n\n### Elixir\n\nThe Elixir implementation of mal has been tested with Elixir 1.0.5.\n\n```\ncd impls/elixir\nmix stepX_YYY\n# Or with readline/line editing functionality:\niex -S mix stepX_YYY\n```\n\n### Elm\n\nThe Elm implementation of mal has been tested with Elm 0.18.0\n\n```\ncd impls/elm\nmake stepX_YYY.js\nSTEP=stepX_YYY ./run\n```\n\n### Erlang\n\nThe Erlang implementation of mal requires [Erlang/OTP R17](http://www.erlang.org/download.html)\nand [rebar](https://github.com/rebar/rebar) to build.\n\n```\ncd impls/erlang\nmake\n    # OR\nMAL_STEP=stepX_YYY rebar compile escriptize # build individual step\n./stepX_YYY\n```\n\n### ES6 (ECMAScript 2015)\n\nThe ES6 / ECMAScript 2015 implementation uses the\n[babel](https://babeljs.io) compiler to generate ES5 compatible\nJavaScript. The generated code has been tested with Node 0.12.4.\n\n```\ncd impls/es6\nmake\nnode build/stepX_YYY.js\n```\n\n\n### F# ###\n\nThe F# implementation of mal has been tested on Linux using the Mono\nF# compiler (fsharpc) and the Mono runtime (version 3.12.1). The mono C#\ncompiler (mcs) is also necessary to compile the readline dependency. All are\nrequired to build and run the F# implementation.\n\n```\ncd impls/fsharp\nmake\nmono ./stepX_YYY.exe\n```\n\n### Factor\n\nThe Factor implementation of mal has been tested with Factor 0.97\n([factorcode.org](http://factorcode.org)).\n\n```\ncd impls/factor\nFACTOR_ROOTS=. factor -run=stepX_YYY\n```\n\n### Fantom\n\nThe Fantom implementation of mal has been tested with Fantom 1.0.70.\n\n```\ncd impls/fantom\nmake lib/fan/stepX_YYY.pod\nSTEP=stepX_YYY ./run\n```\n\n### Fennel\n\nThe Fennel implementation of mal has been tested with Fennel version\n0.9.1 on Lua 5.4.\n\n```\ncd impls/fennel\nfennel ./stepX_YYY.fnl\n```\n\n### Forth\n\n```\ncd impls/forth\ngforth stepX_YYY.fs\n```\n\n### GNU Guile 2.1+\n\n```\ncd impls/guile\nguile -L ./ stepX_YYY.scm\n```\n\n### GNU Smalltalk\n\nThe Smalltalk implementation of mal has been tested with GNU Smalltalk 3.2.91.\n\n```\ncd impls/gnu-smalltalk\n./run\n```\n\n### Go\n\nThe Go implementation of mal requires that go is installed on on the\npath. The implementation has been tested with Go 1.3.1.\n\n```\ncd impls/go\nmake\n./stepX_YYY\n```\n\n\n### Groovy\n\nThe Groovy implementation of mal requires Groovy to run and has been\ntested with Groovy 1.8.6.\n\n```\ncd impls/groovy\nmake\ngroovy ./stepX_YYY.groovy\n```\n\n### Hare\n\nThe hare implementation was tested against Hare 0.25.2.\n\n```\ncd impls/hare\nmake\n./stepX_YYY\n```\n\n### Haskell\n\nThe Haskell implementation requires the ghc compiler version 7.10.1 or\nlater and also the Haskell parsec and readline (or editline) packages.\n\n```\ncd impls/haskell\nmake\n./stepX_YYY\n```\n\n### Haxe (Neko, Python, C++ and JavaScript)\n\nThe Haxe implementation of mal requires Haxe version 3.2 to compile.\nFour different Haxe targets are supported: Neko, Python, C++, and\nJavaScript.\n\n```\ncd impls/haxe\n# Neko\nmake all-neko\nneko ./stepX_YYY.n\n# Python\nmake all-python\npython3 ./stepX_YYY.py\n# C++\nmake all-cpp\n./cpp/stepX_YYY\n# JavaScript\nmake all-js\nnode ./stepX_YYY.js\n```\n\n### Hy\n\nThe Hy implementation of mal has been tested with Hy 0.13.0.\n\n```\ncd impls/hy\n./stepX_YYY.hy\n```\n\n### Io\n\nThe Io implementation of mal has been tested with Io version 20110905.\n\n```\ncd impls/io\nio ./stepX_YYY.io\n```\n\n### Janet\n\nThe Janet implementation of mal has been tested with Janet version 1.12.2.\n\n```\ncd impls/janet\njanet ./stepX_YYY.janet\n```\n\n### Java 1.7\n\nThe Java implementation of mal requires maven2 to build.\n\n```\ncd impls/java\nmvn compile\nmvn -quiet exec:java -Dexec.mainClass=mal.stepX_YYY\n    # OR\nmvn -quiet exec:java -Dexec.mainClass=mal.stepX_YYY -Dexec.args=\"CMDLINE_ARGS\"\n```\n\n### Java, using Truffle for GraalVM\n\nThis Java implementation will run on OpenJDK, but can run\nas much as 30x faster on GraalVM thanks to the Truffle framework.\nIt's been tested with OpenJDK 11, GraalVM CE 20.1.0, and\nGraalVM CE 21.1.0.\n\n```\ncd impls/java-truffle\n./gradlew build\nSTEP=stepX_YYY ./run\n```\n\n### JavaScript/Node\n\n```\ncd impls/js\nnpm install\nnode stepX_YYY.js\n```\n\n### Julia\n\nThe Julia implementation of mal requires Julia 0.4.\n\n```\ncd impls/julia\njulia stepX_YYY.jl\n```\n\n### jq\n\nTested against version 1.6, with a lot of cheating in the IO department\n\n```\ncd impls/jq\nSTEP=stepA_YYY ./run\n    # with Debug\nDEBUG=true STEP=stepA_YYY ./run\n```\n\n### Kotlin\n\nThe Kotlin implementation of mal has been tested with Kotlin 1.0.\n\n```\ncd impls/kotlin\nmake\njava -jar stepX_YYY.jar\n```\n\n### LaTeX3\n\nThe LaTeX3 implementation of mal has been tested with pdfTeX\n3.141592653-2.6-1.40.24.\n\nSelf hosting is too slow for any sensible timeout, and crashes in\nstep4, apparently because of hard-coded limitations.\n\nAnybody working on this should uncomment the two lines of (slow)\ndebugging options in the step file, and export DEBUG=1 (for more\noutput than tests accept).\n\n### LiveScript\n\nThe LiveScript implementation of mal has been tested with LiveScript 1.5.\n\n```\ncd impls/livescript\nmake\nnode_modules/.bin/lsc stepX_YYY.ls\n```\n\n### Logo\n\nThe Logo implementation of mal has been tested with UCBLogo 6.0.\n\n```\ncd impls/logo\nlogo stepX_YYY.lg\n```\n\n### Lua\n\nThe Lua implementation of mal has been tested with Lua 5.3.5 The\nimplementation requires luarocks to be installed.\n\n```\ncd impls/lua\nmake  # to build and link linenoise.so and rex_pcre.so\n./stepX_YYY.lua\n```\n\n### Mal\n\nRunning the mal implementation of mal involves running stepA of one of\nthe other implementations and passing the mal step to run as a command\nline argument.\n\n```\ncd impls/IMPL\nIMPL_STEPA_CMD ../mal/stepX_YYY.mal\n\n```\n\n### GNU Make 3.81\n\n```\ncd impls/make\nmake -f stepX_YYY.mk\n```\n\n### NASM\n\nThe NASM implementation of mal is written for x86-64 Linux, and has been tested\nwith Linux 3.16.0-4-amd64 and NASM version 2.11.05.\n\n```\ncd impls/nasm\nmake\n./stepX_YYY\n```\n\n### Nim 1.0.4\n\nThe Nim implementation of mal has been tested with Nim 1.0.4.\n\n```\ncd impls/nim\nmake\n  # OR\nnimble build\n./stepX_YYY\n```\n\n### Object Pascal\n\nThe Object Pascal implementation of mal has been built and tested on\nLinux using the Free Pascal compiler version 2.6.2 and 2.6.4.\n\n```\ncd impls/objpascal\nmake\n./stepX_YYY\n```\n\n### Objective C\n\nThe Objective C implementation of mal has been built and tested on\nLinux using clang/LLVM 3.6. It has also been built and tested on OS\nX using Xcode 7.\n\n```\ncd impls/objc\nmake\n./stepX_YYY\n```\n\n### OCaml 4.01.0\n\n```\ncd impls/ocaml\nmake\n./stepX_YYY\n```\n\n### MATLAB (GNU Octave and MATLAB)\n\nThe MatLab implementation has been tested with GNU Octave 4.2.1.\nIt has also been tested with MATLAB version R2014a on Linux. Note that\nMATLAB is a commercial product.\n\n```\ncd impls/matlab\n./stepX_YYY\noctave -q --no-gui --no-history --eval \"stepX_YYY();quit;\"\nmatlab -nodisplay -nosplash -nodesktop -nojvm -r \"stepX_YYY();quit;\"\n    # OR with command line arguments\noctave -q --no-gui --no-history --eval \"stepX_YYY('arg1','arg2');quit;\"\nmatlab -nodisplay -nosplash -nodesktop -nojvm -r \"stepX_YYY('arg1','arg2');quit;\"\n```\n\n### miniMAL\n\n[miniMAL](https://github.com/kanaka/miniMAL) is small Lisp interpreter\nimplemented in less than 1024 bytes of JavaScript. To run the miniMAL\nimplementation of mal you need to download/install the miniMAL\ninterpreter (which requires Node.js).\n```\ncd impls/miniMAL\n# Download miniMAL and dependencies\nnpm install\nexport PATH=`pwd`/node_modules/minimal-lisp/:$PATH\n# Now run mal implementation in miniMAL\nminiMAL ./stepX_YYY\n```\n\n### Perl 5\n\nThe Perl 5 implementation should work with perl 5.19.3 and later.\n\nFor readline line editing support, install Term::ReadLine::Perl or\nTerm::ReadLine::Gnu from CPAN.\n\n```\ncd impls/perl\nperl stepX_YYY.pl\n```\n\n### Perl 6\n\nThe Perl 6 implementation was tested on Rakudo Perl 6 2016.04.\n\n```\ncd impls/perl6\nperl6 stepX_YYY.pl\n```\n\n### PHP 5.3\n\nThe PHP implementation of mal requires the php command line interface\nto run.\n\n```\ncd impls/php\nphp stepX_YYY.php\n```\n\n### Picolisp\n\nThe Picolisp implementation requires libreadline and Picolisp 3.1.11\nor later.\n\n```\ncd impls/picolisp\n./run\n```\n\n### Pike\n\nThe Pike implementation was tested on Pike 8.0.\n\n```\ncd impls/pike\npike stepX_YYY.pike\n```\n\n### PL/pgSQL (PostgreSQL SQL Procedural Language)\n\nThe PL/pgSQL implementation of mal requires a running PostgreSQL server\n(the \"kanaka/mal-test-plpgsql\" docker image automatically starts\na PostgreSQL server). The implementation connects to the PostgreSQL server\nand create a database named \"mal\" to store tables and stored\nprocedures. The wrapper script uses the psql command to connect to the\nserver and defaults to the user \"postgres\" but this can be overridden\nwith the PSQL_USER environment variable. A password can be specified\nusing the PGPASSWORD environment variable. The implementation has been\ntested with PostgreSQL 9.4.\n\n```\ncd impls/plpgsql\n./wrap.sh stepX_YYY.sql\n    # OR\nPSQL_USER=myuser PGPASSWORD=mypass ./wrap.sh stepX_YYY.sql\n```\n\n### PL/SQL (Oracle SQL Procedural Language)\n\nThe PL/SQL implementation of mal requires a running Oracle DB\nserver (the \"kanaka/mal-test-plsql\" docker image automatically\nstarts an Oracle Express server). The implementation connects to the\nOracle server to create types, tables and stored procedures. The\ndefault SQL\\*Plus logon value (username/password@connect_identifier) is\n\"system/oracle\" but this can be overridden with the ORACLE_LOGON\nenvironment variable. The implementation has been tested with Oracle\nExpress Edition 11g Release 2. Note that any SQL\\*Plus connection\nwarnings (user password expiration, etc) will interfere with the\nability of the wrapper script to communicate with the DB.\n\n```\ncd impls/plsql\n./wrap.sh stepX_YYY.sql\n    # OR\nORACLE_LOGON=myuser/mypass@ORCL ./wrap.sh stepX_YYY.sql\n```\n\n### PostScript Level 2/3\n\nThe PostScript implementation of mal requires Ghostscript to run. It\nhas been tested with Ghostscript 9.10.\n\n```\ncd impls/ps\ngs -q -dNODISPLAY -I./ stepX_YYY.ps\n```\n\n### PowerShell\n\nThe PowerShell implementation of mal requires the PowerShell script\nlanguage. It has been tested with PowerShell 6.0.0 Alpha 9 on Linux.\n\n```\ncd impls/powershell\npowershell ./stepX_YYY.ps1\n```\n\n### Prolog\n\nThe Prolog implementation uses some constructs specific to SWI-Prolog,\nincludes readline support and has been tested on Debian GNU/Linux with\nversion 8.2.1.\n\n```\ncd impls/prolog\nswipl stepX_YYY\n```\n\n### PureScript\nThe PureScript implementation requires the spago compiler version 0.20.2.\n\n```\ncd impls/purs\nmake\nnode ./stepX_YYY.js\n```\n\n### Python2\n\nThis implementation only uses python2 features, but avoids\nincompatibilities with python3.\n\n### Python3\n\nThis implementation is checked for style and types\n(flake8, pylint, mypy).  It reports all errors with details.\nIt demonstrates iterators, decorators, functional tools, chain maps,\ndataclasses, introspection, match statements, assignement expressions.\n\n### RPython\n\nYou must have [rpython](https://rpython.readthedocs.org/) on your path\n(included with [pypy](https://bitbucket.org/pypy/pypy/)).\n\n```\ncd impls/rpython\nmake        # this takes a very long time\n./stepX_YYY\n```\n\n### R\n\nThe R implementation of mal requires R (r-base-core) to run.\n\n```\ncd impls/r\nmake libs  # to download and build rdyncall\nRscript stepX_YYY.r\n```\n\n### Racket (5.3)\n\nThe Racket implementation of mal requires the Racket\ncompiler/interpreter to run.\n\n```\ncd impls/racket\n./stepX_YYY.rkt\n```\n\n### Rexx\n\nThe Rexx implementation of mal has been tested with Regina Rexx 3.6.\n\n```\ncd impls/rexx\nmake\nrexx -a ./stepX_YYY.rexxpp\n```\n\n### Ruby (1.9+)\n\n```\ncd impls/ruby\nruby stepX_YYY.rb\n```\n\n### Ruby #2\n\nA second Ruby implementation with the following goals:\n\n- No global variables\n- No modification (monkey-patching) of core Ruby classes\n- Modularized into the `Mal` module namespace\n\n```\ncd impls/ruby.2\nruby stepX_YYY.rb\n```\n\n### Rust (1.38+)\n\nThe rust implementation of mal requires the rust compiler and build\ntool (cargo) to build.\n\n```\ncd impls/rust\ncargo run --release --bin stepX_YYY\n```\n\n### Scala ###\n\nInstall scala and sbt (http://www.scala-sbt.org/0.13/tutorial/Installing-sbt-on-Linux.html):\n\n```\ncd impls/scala\nsbt 'run-main stepX_YYY'\n    # OR\nsbt compile\nscala -classpath target/scala*/classes stepX_YYY\n```\n\n### Scheme (R7RS) ###\n\nThe Scheme implementation of MAL has been tested with Chibi-Scheme\n0.10, Kawa 3.1.1, Gauche 0.9.6, CHICKEN 5.1.0, Sagittarius 0.9.7,\nCyclone 0.32.0 (Git version) and Foment 0.4 (Git version).  You should\nbe able to get it running on other conforming R7RS implementations\nafter figuring out how libraries are loaded and adjusting the\n`Makefile` and `run` script accordingly.\n\n```\ncd impls/scheme\n# chibi\nscheme_MODE=chibi ./run\n# kawa\nmake kawa\nscheme_MODE=kawa ./run\n# gauche\nscheme_MODE=gauche ./run\n# chicken\nmake chicken\nscheme_MODE=chicken ./run\n# sagittarius\nscheme_MODE=sagittarius ./run\n# cyclone\nmake cyclone\nscheme_MODE=cyclone ./run\n# foment\nscheme_MODE=foment ./run\n```\n\n### Skew ###\n\nThe Skew implementation of mal has been tested with Skew 0.7.42.\n\n```\ncd impls/skew\nmake\nnode stepX_YYY.js\n```\n\n\n### Standard ML (Poly/ML, MLton, Moscow ML)\n\nThe Standard ML implementation of mal requires an\n[SML97](https://github.com/SMLFamily/The-Definition-of-Standard-ML-Revised)\nimplementation. The Makefile supports Poly/ML, MLton, Moscow ML, and has\nbeen tested with Poly/ML 5.8.1, MLton 20210117, and Moscow ML version 2.10.\n\n```\ncd impls/sml\n# Poly/ML\nmake sml_MODE=polyml\n./stepX_YYY\n# MLton\nmake sml_MODE=mlton\n./stepX_YYY\n# Moscow ML\nmake sml_MODE=mosml\n./stepX_YYY\n```\n\n\n### Swift 3\n\nThe Swift 3 implementation of mal requires the Swift 3.0 compiler. It\nhas been tested with Swift 3 Preview 3.\n\n```\ncd impls/swift3\nmake\n./stepX_YYY\n```\n\n### Swift 4\n\nThe Swift 4 implementation of mal requires the Swift 4.0 compiler. It\nhas been tested with Swift 4.2.3 release.\n\n```\ncd impls/swift4\nmake\n./stepX_YYY\n```\n\n### Swift 5\n\nThe Swift 5 implementation of mal requires the Swift 5.0 compiler. It\nhas been tested with Swift 5.1.1 release.\n\n```\ncd impls/swift6\nswift run stepX_YYY\n```\n\n### Tcl 8.6\n\nThe Tcl implementation of mal requires Tcl 8.6 to run.  For readline line\nediting support, install tclreadline.\n\n```\ncd impls/tcl\ntclsh ./stepX_YYY.tcl\n```\n\n### TypeScript\n\nThe TypeScript implementation of mal requires the TypeScript 2.2 compiler.\nIt has been tested with Node.js v6.\n\n```\ncd impls/ts\nmake\nnode ./stepX_YYY.js\n```\n\n### Vala\n\nThe Vala implementation of mal has been tested with the Vala 0.40.8\ncompiler. You will need to install `valac` and `libreadline-dev` or\nequivalent.\n\n```\ncd impls/vala\nmake\n./stepX_YYY\n```\n\n### VHDL\n\nThe VHDL implementation of mal has been tested with GHDL 0.29.\n\n```\ncd impls/vhdl\nmake\n./run_vhdl.sh ./stepX_YYY\n```\n\n### Vimscript\n\nThe Vimscript implementation of mal requires Vim 8.0 to run.\n\n```\ncd impls/vimscript\n./run_vimscript.sh ./stepX_YYY.vim\n```\n\n### Visual Basic.NET ###\n\nThe VB.NET implementation of mal has been tested on Linux using the Mono\nVB compiler (vbnc) and the Mono runtime (version 2.10.8.1). Both are\nrequired to build and run the VB.NET implementation.\n\n```\ncd impls/vb\nmake\nmono ./stepX_YYY.exe\n```\n\n### Visual Basic Script ###\n\nThe VBScript implementation of mal has been tested on Windows 10 1909.\n`install.vbs` can help you install the requirements (.NET 2.0 3.0 3.5).\nIf you havn't install `.NET 2.0 3.0 3.5`, it will popup a window for installation.\nIf you already installed that, it will do nothing.\n\n```\ncd impls\\vbs\ninstall.vbs\ncscript -nologo stepX_YYY.vbs\n```\n\n### WebAssembly (wasm) ###\n\nThe WebAssembly implementation is written in\n[Wam](https://github.com/kanaka/wam) (WebAssembly Macro language) and\nruns under several different non-web embeddings (runtimes):\n[node](https://nodejs.org),\n[wasmtime](https://github.com/CraneStation/wasmtime),\n[wasmer](https://wasmer.io),\n[wax](https://github.com/kanaka/wac),\n[wace](https://github.com/kanaka/wac),\n[warpy](https://github.com/kanaka/warpy).\n\n```\ncd impls/wasm\n# node\nmake wasm_MODE=node\n./run.js ./stepX_YYY.wasm\n# wasmtime\nmake wasm_MODE=wasmtime\nwasmtime --dir=./ --dir=../ --dir=/ ./stepX_YYY.wasm\n# wasmer\nmake wasm_MODE=wasmer\nwasmer run --dir=./ --dir=../ --dir=/ ./stepX_YYY.wasm\n# wax\nmake wasm_MODE=wax\nwax ./stepX_YYY.wasm\n# wace\nmake wasm_MODE=wace_libc\nwace ./stepX_YYY.wasm\n# warpy\nmake wasm_MODE=warpy\nwarpy --argv --memory-pages 256 ./stepX_YYY.wasm\n```\n\n### XSLT\n\nThe XSLT implementation of mal is written with XSLT 3 and tested on Saxon 9.9.1.6 Home Edition.\n\n```\ncd impls/xslt\nSTEP=stepX_YY ./run\n```\n\n### Wren\n\nThe Wren implementation of mal was tested on Wren 0.2.0.\n\n```\ncd impls/wren\nwren ./stepX_YYY.wren\n```\n\n### Yorick\n\nThe Yorick implementation of mal was tested on Yorick 2.2.04.\n\n```\ncd impls/yorick\nyorick -batch ./stepX_YYY.i\n```\n\n### Zig\n\nThe Zig implementation of mal was tested on Zig 0.5.\n\n```\ncd impls/zig\nzig build stepX_YYY\n```\n\n\n\n## Running tests\n\nThe top level Makefile has a number of useful targets to assist with\nimplementation development and testing. The `help` target provides\na list of the targets and options:\n\n```\nmake help\n```\n\n### Functional tests\n\nThe are almost 800 generic functional tests (for all implementations)\nin the `tests/` directory. Each step has a corresponding test file\ncontaining tests specific to that step. The `runtest.py` test harness\nlaunches a Mal step implementation and then feeds the tests one at\na time to the implementation and compares the output/return value to\nthe expected output/return value.\n\n* To run all the tests across all implementations (be prepared to wait):\n\n```\nmake test\n```\n\n* To run all tests against a single implementation:\n\n```\nmake \"test^IMPL\"\n\n# e.g.\nmake \"test^clojure\"\nmake \"test^js\"\n```\n\n* To run tests for a single step against all implementations:\n\n```\nmake \"test^stepX\"\n\n# e.g.\nmake \"test^step2\"\nmake \"test^step7\"\n```\n\n* To run tests for a specific step against a single implementation:\n\n```\nmake \"test^IMPL^stepX\"\n\n# e.g\nmake \"test^ruby^step3\"\nmake \"test^ps^step4\"\n```\n\n### Self-hosted functional tests\n\n* To run the functional tests in self-hosted mode, you specify `mal`\n  as the test implementation and use the `MAL_IMPL` make variable\n  to change the underlying host language (default is JavaScript):\n```\nmake MAL_IMPL=IMPL \"test^mal^step2\"\n\n# e.g.\nmake \"test^mal^step2\"   # js is default\nmake MAL_IMPL=ruby \"test^mal^step2\"\nmake MAL_IMPL=python3 \"test^mal^step2\"\n```\n\n### Starting the REPL\n\n* To start the REPL of an implementation in a specific step:\n\n```\nmake \"repl^IMPL^stepX\"\n\n# e.g\nmake \"repl^ruby^step3\"\nmake \"repl^ps^step4\"\n```\n\n* If you omit the step, then `stepA` is used:\n\n```\nmake \"repl^IMPL\"\n\n# e.g\nmake \"repl^ruby\"\nmake \"repl^ps\"\n```\n\n* To start the REPL of the self-hosted implementation, specify `mal` as the\n  REPL implementation and use the `MAL_IMPL` make variable to change the\n  underlying host language (default is JavaScript):\n```\nmake MAL_IMPL=IMPL \"repl^mal^stepX\"\n\n# e.g.\nmake \"repl^mal^step2\"   # js is default\nmake MAL_IMPL=ruby \"repl^mal^step2\"\nmake MAL_IMPL=python3 \"repl^mal\"\n```\n\n### Performance tests\n\nWarning: These performance tests are neither statistically valid nor\ncomprehensive; runtime performance is a not a primary goal of mal. If\nyou draw any serious conclusions from these performance tests, then\nplease contact me about some amazing oceanfront property in Kansas\nthat I'm willing to sell you for cheap.\n\n* To run performance tests against a single implementation:\n```\nmake \"perf^IMPL\"\n\n# e.g.\nmake \"perf^js\"\n```\n\n* To run performance tests against all implementations:\n```\nmake \"perf\"\n```\n\n### Generating language statistics\n\n* To report line and byte statistics for a single implementation:\n```\nmake \"stats^IMPL\"\n\n# e.g.\nmake \"stats^js\"\n```\n\n## Dockerized testing\n\nEvery implementation directory contains a Dockerfile to create\na docker image containing all the dependencies for that\nimplementation. In addition, the top-level Makefile contains support\nfor running the tests target (and perf, stats, repl, etc) within\na docker container for that implementation by passing *\"DOCKERIZE=1\"*\non the make command line. For example:\n\n```\nmake DOCKERIZE=1 \"test^js^step3\"\n```\n\nExisting implementations already have docker images built and pushed\nto the docker registry. However, if\nyou wish to build or rebuild a docker image locally, the toplevel\nMakefile provides a rule for building docker images:\n\n```\nmake \"docker-build^IMPL\"\n```\n\n\n**Notes**:\n* Docker images are named *\"ghcr.io/kanaka/mal-test-IMPL\"*\n* JVM-based language implementations (Groovy, Java, Clojure, Scala):\n  you will probably need to run this command once manually\n  first `make DOCKERIZE=1 \"repl^IMPL\"` before you can run tests because\n  runtime dependencies need to be downloaded to avoid the tests timing\n  out. These dependencies are downloaded to dot-files in the /mal\n  directory so they will persist between runs.\n\n\n## License\n\nMal (make-a-lisp) is licensed under the MPL 2.0 (Mozilla Public\nLicense 2.0). See LICENSE.txt for more details.\n"
  },
  {
    "path": "ci.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\nACTION=${1}\nIMPL=${2}\n\ndie() { local ret=$1; shift; echo >&2 \"${*}\"; exit $ret; }\n\n# Environment variable configuration\nBUILD_IMPL=${BUILD_IMPL:-${IMPL}}\n\nif [ \"${DO_SELF_HOST}\" ]; then\n    MAL_IMPL=${IMPL}\n    IMPL=mal\nfi\n\nif [ \"${DO_HARD}\" ]; then\n    TEST_OPTS=\"${TEST_OPTS} --hard\"\nfi\n\nraw_mode_var=${MAL_IMPL:-${IMPL}}_MODE\nmode_var=${raw_mode_var/-/__}\nmode_var=${mode_var/./__}\nmode_val=${!mode_var}\n\nlog_prefix=\"${ACTION}${REGRESS:+-regress}-${IMPL}${mode_val:+-${mode_val}}${MAL_IMPL:+-${MAL_IMPL}}\"\nTEST_OPTS=\"${TEST_OPTS} -vv --debug-file ../../${log_prefix}.debug\"\nTEST_OPTS=\"${TEST_OPTS} --continue-after-fail\"\n\nstep_summary() {\n    echo \"${*}\"\n    if [ \"${GITHUB_STEP_SUMMARY}\" ]; then\n        echo \"${*}\" >> \"${GITHUB_STEP_SUMMARY}\"\n    fi\n}\n\nimg_base=\"${MAL_IMPL:-${IMPL}}\"\nimg_impl=\"${img_base%%-mal}\"\nimg_name=\"mal-test-$(echo \"${img_impl}\" | tr '[:upper:]' '[:lower:]')\"\nimg_ver=$(./voom-like-version.sh impls/${img_impl}/Dockerfile)\nIMAGE=\"ghcr.io/kanaka/${img_name}:${img_ver}\"\n\n# If NO_DOCKER is blank then run make in a docker image\nMAKE=\"make ${mode_val:+${mode_var}=${mode_val}}\"\nif [ -z \"${NO_DOCKER}\" ]; then\n    # We could just use make DOCKERIZE=1 instead but that does add\n    # non-trivial startup overhead for each step.\n    MAKE=\"docker run -i -u $(id -u) -v `pwd`:/mal ${IMAGE} ${MAKE}\"\nfi\n\n# Log everything below this point:\nexec &> >(tee ./${log_prefix}.log)\n\nif [ \"${NO_PERF}\" -a \"${ACTION}\" = \"perf\" ]; then\n    die 0 \"Skipping perf test\"\nfi\nif [ \"${NO_SELF_HOST}\" -a \"${DO_SELF_HOST}\" ]; then\n    die 0 \"Skipping ${ACTION} of ${MAL_IMPL} self-host\"\nfi\nif [ \"${NO_SELF_HOST_PERF}\" -a \"${DO_SELF_HOST}\" -a \"${ACTION}\" = \"perf\" ]; then\n    die 0 \"Skipping only perf test for ${MAL_IMPL} self-host\"\nfi\n\necho \"ACTION: ${ACTION}\"\necho \"IMPL: ${IMPL}\"\necho \"BUILD_IMPL: ${BUILD_IMPL}\"\necho \"MAL_IMPL: ${MAL_IMPL}\"\necho \"TEST_OPTS: ${TEST_OPTS}\"\necho \"IMAGE: ${IMAGE}\"\necho \"MAKE: ${MAKE}\"\n\ncase \"${ACTION}\" in\ndocker-build-push)\n    if ! docker pull ${IMAGE}; then\n        step_summary \"${BUILD_IMPL} - building ${IMAGE}\"\n        make \"docker-build^${BUILD_IMPL}\"\n        step_summary \"${BUILD_IMPL} - built ${IMAGE}\"\n        if [ \"${GITHUB_REPOSITORY}\" = \"kanaka/mal\" ] && [ \"${GITHUB_REF}\" = \"refs/heads/master\" ]; then\n            docker push ${IMAGE}\n            step_summary \"${BUILD_IMPL} - pushed ${IMAGE}\"\n        fi\n    fi\n    ;;\nbuild)\n    # rpython often fails on step9 in compute_vars_longevity\n    # so build step9, then continue with the full build\n    if [ \"${BUILD_IMPL}\" = \"rpython\" ]; then\n        ${MAKE} -C \"impls/${BUILD_IMPL}\" step9_try || true\n    fi\n    ${MAKE} -C \"impls/${BUILD_IMPL}\"\n    ;;\ntest|perf)\n    [ \"${ACTION}\" = \"perf\" ] && STEP=\n    if ! ${MAKE} TEST_OPTS=\"${TEST_OPTS}\" \\\n            ${MAL_IMPL:+MAL_IMPL=${MAL_IMPL}} \\\n            ${REGRESS:+REGRESS=${REGRESS}} \\\n            ${HARD:+HARD=${HARD}} \\\n            ${DEFERRABLE:+DEFERRABLE=${DEFERRABLE}} \\\n            ${OPTIONAL:+OPTIONAL=${OPTIONAL}} \\\n            ${ACTION}^${IMPL}${STEP:+^${STEP}}; then\n        # show debug-file path on error\n        echo \"Full debug log is at: ${log_prefix}.debug\"\n        false\n    fi\n    ;;\nesac\n"
  },
  {
    "path": "docs/FAQ.md",
    "content": "# Mal/Make-a-Lisp FAQ\n\n<a name=\"why_mal\"></a>\n\n### Why did you create mal/make-a-lisp?\n### OR Why the name \"mal\"?\n### OR Why?\n### OR Wat?\n\nIn November of 2013, Alan Dipert gave a [lightning talk at\nClojure/conj](https://www.youtube.com/watch?v=bmHTFo2Rf2w#t=28m55s)\nabout [gherkin](https://github.com/alandipert/gherkin), a Lisp\nimplemented in bash. His presentation led me to ask myself the question\nof whether a Lisp could be created using the GNU Make macro language.\nAs you have probably guessed, the answer to that question is yes.\n\nInterestingly, the current pedagogical/educational purpose of mal\nhappened due to a semantic naming accident (naming is such a fraught\ntask in computer science). If I am remembering correctly, the name\n\"mal\" original meant \"MAke Lisp\". I do not remember precisely why\nI continued to create more implementations, apart from the fact that\nit was a fun challenge, but after the make implementation, many of the\nothers were relatively easy. At some point during that process,\nI realized that the multiple implementations and incremental steps\n(which was originally just for my own clarity) was a useful learning\ntool and so the \"mal\" name became a double entendre for \"Make, A Lisp\"\nand \"make-a-lisp\" (and eventually just the latter given that the make\nimplementation is now just a small part of the whole).\n\n\n<a name=\"code_split\"></a>\n\n### Why is some code split into steps and some code not?\n\nThe split between code that goes in steps and code that goes into other files\nis not completely arbitrary (a bit arbitrary, but not completely). My rule of\nthumb is something like this: if the code is specific and necessary for\nimplementing a Lisp then it belongs in the step files. If the purpose of the\ncode is for implementing new dynamic data-types/objects and the functions or\nmethods that operate on those types, then it goes in separate files.\n\nIf the target language has types and functions that resemble mal types, then\nthose files tend to be very small or non-existent. Examples:\n\n* the mal implementation has no types, reader, printer files and\n  has a trivial core file (just to hoist underlying functions)\n* the Clojure implementation has no types file and a fairly trivial\n  core file\n* ruby types and the functions that operate on them are very \"Lispy\"\n  so the Ruby types file and core file are very small.\n\nThe env file is somewhat more arbitrary, however, it is\na self-contained module that is implemented early and changes very\nlittle after that, so I decided to separate it. Also, for languages\nthat have hierarchical maps/dictionaries (e.g. Javascript\nobjects/prototype chain), you do not necessarily need an env file.\n\nAnother way of summarizing this answer is that the step files\nrepresent the core of what makes something a Lisp, the rest of the\nmodules are just language specific details (they may be the harder\nthan the Lisp part, but that is due to the nature of the target\nlanguage not because of Lisp functionality per se).\n\n\n<a name=\"steps\"></a>\n\n### Why are the mal/make-a-lisp steps structured the way they are?\n\n### OR Why is X functionality in step Y instead of step Z?\n\nThere is no single consistent rule that I have used to determine which\nfunctionality goes in which step and the arrangement has changed\nnumerous times since the beginning of the project. There are several\ndifferent goals that I try and balance in determining which\nfunctionality goes into which step:\n\n* **Optimize Lisp learning**: I want developers who are unfamiliar with\n  Lisp to be able to use the project and guide to learn about Lisp\n  without becoming overwhelmed. In many Lisp introductions, concepts\n  like quoting and homoiconicity (i.e. a user exposed eval function)\n  are introduced early. But these are fairly foreign to most other\n  languages so they are introduced in later steps in mal. I also try\n  to not to concentrate too many Lisp concepts in a single step. So\n  many steps contain one or two Lisp concepts plus some core function\n  additions that support those concepts.\n\n* **Optimize implementation language learning (equal-ish step\n  sizing)**: I try to structure the steps so that the target\n  implementation can be learned incrementally. This goal is the one\n  that has caused me to refactor the steps the most. Different\n  languages have different areas that they optimize and make simple\n  for the developer. For example, in Java (prior to 8) and PostScript\n  creating the equivalent of anonymous functions and function closures\n  is painful. In other languages, function closures are trivial, but\n  IO and error handling are tedious when you are first learning the\n  language (I am looking at you Haskell). So this goal is really about\n  trying to balance step size across multiple languages.\n\n* **Practical results early and continuous feedback**: it is\n  a scientific fact that many small rewards are more motivating than\n  a single large reward (citation intentionally omitted, get a small\n  reward by googling it yourself). Each step in mal adds new\n  functionality that can actually be exercised by the implementer and,\n  just as importantly, easily tested.\n\nAlso, the step structure of mal/make-a-lisp is not perfect. It never\nwill be perfect, but there are some areas that could be improved. The\nmost glaring problem is that step1 is on the heavy/large size because\nin most languages you have to implement a good portion of the\nreader/printer before you can begin using/testing the step. The\ncompromise I have settled on for now is to put extra detail in the\nprocess guide for step1 and to be clear that many of the types are\ndeferrable until later. But I am always open to suggestions.\n\n\n<a name=\"add_implementation\"></a>\n\n### Will you add my new implementation?\n\nAbsolutely! I want mal to have a idiomatic implementation in every\nprogramming language.\n\nHere is a quick checklist of what you need to do to merge a new\nimplementation:\n- Follow the incremental layout (no extracted eval code)\n- Dockerfile that defines requirements for building and running you\n  implementation and has this LABEL and\n  ```\n  LABEL org.opencontainers.image.source=https://github.com/kanaka/mal\n  ```\n- Makefile: if it is a compiled/built implementation then add rules\n  for building each step and a clean rule.\n- Add your implementation to IMPLS.yml\n  - if takes a long time to build add `SLOW: 1`\n- Add implemenation to `Makefile.impls`\n    - Add to `IMPLS` variable (alphabetical order)\n    - Add a `*_STEP_TO_PROG` line for resolving artifacts to build and\n      run (if not compiled, just point to the step file itself)\n- Update the top-level README.md:\n    - Increment the implementation and runtime counts\n    - Add to the table of implementations\n    - Add a build/run notes sub-section to the `Implementation\n      Details` section\n- Create a pull request (this will trigger CI and allow review)\n- Make sure that CI passes for your implementation including\n  self-hosting (some esoteric languages can have an exception to this)\n\nHere are more detailed guidelines for getting your implementation\naccepted into the main repository:\n\n* Your implementation should follow the existing mal steps and\n  structure: Lisp-centric code (eval, eval_ast, quasiquote,\n  macroexpand) in the step files, other code in reader, printer, env,\n  and core files. See [code layout rationale](#code_split) above.\n  I encourage you to create implementations that take mal in new\n  directions for your own learning and experimentation, but for it to\n  be included in the main repository I ask that it follows the steps\n  and structure.\n\n* Your implementation should stick as much as possible to the accepted\n  idioms and conventions in that language. Try to create an\n  implementation that will not make an expert in that language say\n  \"Woah, that's a strange way of doing things\". And on that topic,\n  I make no guarantees that the existing implementations are\n  particularly idiomatic in their target languages (improvements are\n  welcome). However, if it is clear to me that your implementation is\n  not idiomatic in a given language then I will probably ask you to\n  improve it first.\n\n* Your implementation needs to be complete enough to self-host. This\n  means that all the mandatory tests should pass in both direct and\n  self-hosted modes:\n  ```bash\n  make \"test^[IMPL_NAME]\"\n  make MAL_IMPL=[IMPL_NAME] \"test^mal\"\n  ```\n  You do not need to pass the final optional tests for stepA that are\n  marked as optional and not needed for self-hosting (except for the\n  `time-ms` function which is needed to run the micro-benchmark tests).\n\n* Create a `Dockerfile` in your directory that installs all the\n  packages necessary to build and run your implementation. In order to\n  integrate fully with the Github Actions CI workflow, the\n  `Dockerfile` needs to include the following boilerplate (with your\n  name, email, and implementation filled in):\n  ```\n  MAINTAINER Your Name <your@email.com>\n  LABEL org.opencontainers.image.source=https://github.com/kanaka/mal\n  LABEL org.opencontainers.image.description=\"mal test container: Your_Implementation\"\n  ```\n\n  In addition, the docker image should provide python3 (with a python\n  symlink to it) to enable running tests using the image. Here is the\n  typical `Dockerfile` template you should use if your\n  implementation does not require a special base distro:\n\n  ```\n  FROM ubuntu:24.04\n  MAINTAINER Your Name <your@email.com>\n  LABEL org.opencontainers.image.source=https://github.com/kanaka/mal\n  LABEL org.opencontainers.image.description=\"mal test container: Your_Implementation\"\n  ##########################################################\n  # General requirements for testing or common across many\n  # implementations\n  ##########################################################\n\n  RUN apt-get -y update\n\n  # Required for running tests\n  RUN apt-get -y install make python3\n  RUN ln -sf /usr/bin/python3 /usr/bin/python\n\n  # Some typical implementation and test requirements\n  RUN apt-get -y install curl libreadline-dev libedit-dev\n\n  RUN mkdir -p /mal\n  WORKDIR /mal\n\n  ##########################################################\n  # Specific implementation requirements\n  ##########################################################\n\n  ... Your packages ...\n  ```\n\n* Build and tag your docker image. The image tag will have the\n  form `ghcr.io/kanaka/mal-test-[IMPL_NAME]:[VOOM_VERSION]`.\n  ```\n  make \"docker-build^[IMPL_NAME]\"\n\n* The top-level Makefile has support for building/testing using\n  the docker image with the `DOCKERIZE` flag:\n  ```bash\n  make DOCKERIZE=1 \"test^[IMPL_NAME]\"\n  make DOCKERIZE=1 MAL_IMPL=[IMPL_NAME] \"test^mal\"\n  ```\n\n* Make sure the CI build and test scripts pass locally:\n  ```bash\n  ./ci.sh build [IMPL_NAME]\n  ./ci.sh test [IMPL_NAME]\n  ```\n\n* Push your code to a branch and make sure that the automated Github\n  Actions CI passes for your implementation.\n\n* If you are creating a new implementation for an existing\n  implementation (or somebody beats you to the punch while you are\n  working on it), there is still a chance I will merge your\n  implementation. If you can make a compelling argument that your\n  implementation is more idiomatic or significantly better in some way\n  than the existing implementation then I may replace the existing\n  one. However, if your approach is different or unique from the\n  existing implementation, there is still a good chance I will merge\n  your implementation side-by-side with the existing one. At the very\n  least, even if I decide not to merge your implementation, I am\n  certainly willing to link to you implementation once it is\n  completed.\n\n* You do not need to implement line editing (i.e. readline)\n  functionality for your implementation, however, it is a nice\n  convenience for users of your implementation and I personally find\n  it saves a lot of time when I am creating a new implementation to\n  have line edit support early in the process.\n\n### Why do some mal forms end in \"\\*\" or \"!\" (swap!, def!, let\\*, etc)?\n\nThe forms that end in a bang mutate something:\n* **def!** mutates the current environment\n* **swap!** and **reset!** mutate an atom to refer to a new value\n\nThe forms that end in a star are similar to similar Clojure forms but\nare more limited in functionality:\n* **fn\\*** does not do parameter destructuring and only supports\n  a single body form.\n* **let\\*** does not do parameter destructuring\n* **try\\*** and **catch\\*** do not support type matching of\n  exceptions\n\n"
  },
  {
    "path": "docs/Hints.md",
    "content": "# Mal/Make-a-Lisp Implementation Hints\n\n<a name=\"milliseconds\"></a>\n\n### How do I get milliseconds since epoch for the \"time-ms\" function?\n### Does the \"time-ms\" function have to return millisecond since epoch?\n\nMost languages usually have some way to do this natively even though\nit might be buried deeply in the language. If you are having trouble\nfinding how to do this in your target language, consider asking the\nquestion on stackoverflow (if it has not been asked already) or asking\non a discussion channel for your language because there is a good\nchance somebody there knows how and will answer quickly (if there is\na native way at all).\n\nAs a last resort you can always shell out and call the date command\nlike this:\n\n```\ndate +%s%3N\n```\n\nThere are currently two implementations where this method was\nnecessary (probably): bash and make. Unfortunately this method is\nlimited to Linux/UNIX.\n\nAlso, \"time-ms\" technically just needs to return accurate milliseconds\nsince some arbitrary point in time (even program start) in order to be\nused correctly for timing/benchmarking. For consistency it is best if\nit returns epoch milliseconds, but this is not strictly required if\nyou language limitations make it difficult (e.g. size limit of\nintegers).\n\n\n<a name=\"function_references\"></a>\n\n### How do I implement core/native functions if my language does not have any sort of function references (function pointers, closures, lambdas, etc)?\n### How do I implement mal functions in step4 if I do not have function references?\n\nThere are very few language that do not have any sort of function\nreferences so I suggest asking about the specific problem you are\nhaving on stackoverflow or a discussion channel for your language. In\nthe rare case where you have a language without some sort of function\nreference abstraction, then you may have to implement a single\nfunction with a large switch statement (or equivalent) that calls out\nto the appropriate native core function (\"+\", \"list\", \"throw\", etc).\nIn other words, you create a function that implements \"function\nreferences\" rather than using a feature of your language.  You will\nstill need to store the symbol names for those function in the base\nREPL environment but you will have some sort of tagging or marker that\nwill indicate to the `EVAL` function that it should call your \"big\nswitch\" function.\n\nIn addition, if your language has no sort of closure/anonymous\nfunction capability (note that with sufficient object oriented\nfeatures you can implement closure like functionality), then in step4\nyou will need to borrow the way that functions are implemented from\nstep5. In other words, functions become a normal data type that stores\nthe function body (AST), the parameter list and the environment at the\ntime the function is defined. When the function is invoked, `EVAL`\nwill then evaluate these stored items rather than invoking a function\nclosure. It is less convenient to have to do this at step4, but the\nbright side is that step5 will be simpler because you just have to\nimplement the TCO loop because you have already refactored how\nfunctions are stored in step4.\n\n<a name=\"IO\"></a>\n\n### How do I implement terminal input and output in a language which does not have standard I/O capabilities?\n\nIf your target language has some way to get data in and out while it\nis running (even if it is not standard terminal or file I/O) then you\nwill need to create some sort of wrapper script (see\n`vimscript/run_vimscript.sh`) or call out to a shell script (see\n`make/readline.mk` and `make/util.mk`) or implement some other\n\"appropriate\" hack to to get the data in and out. As long\nas your implementation can be used with the test runner and the hack\nis just for working around I/O limitations in your target language,\nit is considered legitimate for upstream inclusion.\n\n### How do I read the command-line arguments if my language runtime doesn't support access to them?\n\nMost languages give access to the command-line arguments that were passed to\nthe program, either as an argument to the `main` function (like `argc` and\n`argv` in C) or as a global variable (like `sys.argv` in Python).  If your\ntarget language doesn't have such mechanisms, consider adding a wrapper script\nthat will read the command-line arguments that were passed to the script and\npass them to the program in a way that the program can read.  This might be\nthrough an environment variable (if the target language allows reading from\nenvironment variables) or through a temporary file.\n\n\n<a name=\"no_reader_object\">\n\n### How can I implement the reader without using a mutable object?\n\nYou do not need a mutable object, but you do need someway of keeping\ntrack of the current position in the token list. One way to implement\nthis is to pass both the token list and the current position to the\nreader functions (read_form, read_list, read_atom, etc) and return\nboth the parsed AST and the new token list position. If your language\ndoes not allow multiple values to be returned from functions then you\nmay need to define a data structure to return both the new position\nand the parsed AST together. In other words, the pseudo-code would\nlook something like this:\n\n```\nast, position = read_list(tokens, position)\n```\n\n---\n\nAnswers for the following questions are TBD.\n\n### How do I implement slurp in a language without the ability to read raw file data?\n\n<a name=\"exceptions\">\n\n### How do I support raising/throwing arbitrary objects in a language that does not support that?\n### What do I do if my implementation language only supports string exceptions?\n\n\n\n"
  },
  {
    "path": "docs/TODO",
    "content": "General:\n    * update language graph code and data\n        - pull from GHA instead of Travis\n\n    * Add self-hosted CI mode/variable\n\n    * Go through PRs. Close or update.\n\n    * Add quick checklist for merging upstream to FAQ:\n\n    * Add PR template/checklist.\n\n    * Update diagrams to reflect the merged eval-ast/macroexpand\n      process.\n\n    * Check that implementations are actually running self-hosted.\n      Check for \"mal-user>\" prompt or something.\n\n    * update language graph code and data\n        * pull from GHA instead of Travis\n\n    * update get-changed-files\n        * use GITHUB_OUTPUT instead of set-output\n        * update version of node\n\n    - Fix self-hosted implementations #662\n\n    - Fix wasm modes wax and wace_libc\n\n    - Fix wasm perf3 hang/OOM\n\nAll/multiple Implementations:\n    - Add step3 and step4 tests. Fix powershell, jq, and xslt with\n      binding/closures. https://github.com/kanaka/mal/issues/645\n\n---------------------------------------------\n\nOther ideas for All:\n    - redefine (defmacro!) as (def! foo (macro*))\n    - Fix/implement interop in more implementations\n\n    - propagate/print errors when self-hosted\n    - metadata on symbols (as per Clojure)\n    - metadata as a map only. ^ merges metadata in the reader itself.\n      Line numbers in metadata from reader.\n    - protocols!\n        - https://github.com/pixie-lang/pixie\n        - http://www.toccata.io/2015/01/Mapping/\n    - namespaces\n        - environments first class: *ENV*, *outer* defined by env-new\n        - namespaces is *namespaces* map in environment which maps namespace\n          names to other environments.\n        - def! become an alias for (env-set! *ENV* 'sym value)\n        - Namespace lookup: go up the environment hierarchy until\n          a *namespaces* map is found with the namespace name being\n          looked up. Then the symbol would be looked up starting in\n          the namespace environment. Need protocols first probably.\n\n    - multi-line REPL read\n    - explicit recur in loops (for error checking)\n    - gensym reader inside quasiquote\n    - standalone executables\n\n\n---------------------------------------------\n\nBash:\n    - explore using ${!prefix*} syntax (more like make impl)\n    - GC\n    - maybe make it work more like basic/wasm\n\nC:\n    - come up with better way to do 20 vararg code\n\nC#:\n    - accumulates line breaks with mal/clojurewest2014.mal\n    - interop: http://www.ckode.dk/programming/eval-in-c-yes-its-possible/\n\nCoffeeScript:\n    - make target to compile to JS\n\nGo:\n    - consider variable arguments in places where it makes sense\n        https://gobyexample.com/variadic-functions\n\nHaskell:\n    - TCO using seq/bang patterns:\n      http://stackoverflow.com/questions/9149183/tail-optimization-guarantee-loop-encoding-in-haskell\n    - immediately exits mal/clojurewest2014.mal (\"\\/\" exception)\n\nJava:\n    - build step, don't use mvn in run script\n    - Use gradle instead of mvn\n        http://blog.paralleluniverse.co/2014/05/01/modern-java/\n\nJavascript:\n    - interop: adopt techniques from miniMAL\n\nMake:\n    - allow '_' in make variable names\n    - hash-map with space in key string\n    - errors should propagate up from within load-file\n    - GC: explore using \"undefine\" directive in Make 3.82\n\nMal:\n    - line numbers in errors\n    - step5_tco\n\nminiMAL:\n    - figure out why {} literals are \"static\"/persistent\n\nObjPascal:\n    - verify that GC/reference counting works\n    - fix comment by itself error at REPL\n\nplpgsql:\n    - maybe combine wrap.sh and run\n\nPerl:\n    - fix metadata on native functions\n    - fix extra line breaks at REPL\n\nPostscript:\n    - add negative numbers\n    - fix blank line after comments\n    - fix command line arg processing (doesn't run file specified)\n\nPowershell:\n    - convert function with \"abc_def\" to \"abc-def\"\n    - remove extraneous return statements at end of functions\n    - remove unnecessary semi-colons\n    - use ArrayList instead of Array for performance\n    - new test to test Keys/keys as hash-map key\n    - test *? predicates with nil\n\nR:\n    - tracebacks in errors\n    - fix running from different directory\n\nRacket\n    - metadata on collections\n\nRust:\n    - fix 'make all' invocation of cargo build\n\nScala\n    - readline\n    - fix exception when finished running something on command line\n\nVHDL:\n    - combine run_vhdl.sh and run\n\nvimscript:\n    - combine run_vimscript.sh and run\n"
  },
  {
    "path": "docs/cheatsheet.html",
    "content": "<html>\n<head>\n  <style>\n    h2 {\n        text-align: center;\n    }\n    table {\n        font-size: 11px;\n        border-width: 0px;\n        border-collapse: collapse;\n    }\n    table tr {\n        border: none;\n    }\n    table td,th {\n        border-left: solid 1px #008;\n    }\n    table td:first-child, th:first-child {\n        border-left: none;\n    }\n\n    table td {\n        vertical-align: top;\n        padding: 2px;\n    }\n    pre {\n        background: #FCFCF9;\n        padding: 3px;\n        border-width: 1px;\n        border-style: solid;\n        border-color: lightgrey;\n    }\n\n    .file {\n        font-weight: 700;\n        color: #338833;\n    }\n    .var {\n        font-weight: 700;\n        color: #BB8855;\n    }\n    .function {\n        font-weight: 700;\n        color: #887744;\n    }\n    .object {\n        font-weight: 700;\n        color: #DD7744;\n    }\n    .malsym {\n        font-weight: 600;\n        color: #883333;\n    }\n    .string {\n        font-weight: 400;\n        color: #994444;\n    }\n  </style>\n</head>\n<body>\n  <h2>Make-A-Lisp Cheatsheet</h2>\n  <table align=left>\n  <tr>\n    <th>Step 1</th> <th>Step 6</th>\n  <tr>\n    <td width=50%>\n<!--\n<pre><code><span class=file>step0_repl.EXT</span>:\n  <span class=function>READ</span></span>(<span class=var>ast</span>):       passthrough <span class=var>ast</span>\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):  passthrough <span class=var>ast</span>\n  <span class=function>PRINT</span>(<span class=var>ast</span>):      passthrough <span class=var>ast</span>\n  <span class=function>main</span>(<span class=var>args</span>):      loop: <span class=function>writeline</span> <span class=function>PRINT</span>(<span class=function>EVAL</span>(<span class=function>READ</span>(<span class=function>readline</span>()), <span class=string>\"\"</span>))\n\n\n\n\n</code></pre>\n-->\n<pre><code><span class=file>reader.EXT</span>:\n  <span class=object>Reader</span>(<span class=var>tokens</span>) object: <span class=var>position</span>, <span class=function>next</span>(), <span class=function>peek</span>()\n  <span class=function>tokenize</span>:  <span class=string>/[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\\s\\[\\]{}('\"`,;)]*)/</span>\n  <span class=function>read_atom</span>: int, float, string (escaped), keyword, nil, true, false, symbol\n  <span class=function>read_list</span>: repeatedly <span class=function>read_form</span> until end token (<span class=object>EOF</span> is error)\n  <span class=function>read_form</span>: expand reader macros, <span class=function>read_list</span> (vector/maps too), or <span class=function>read_atom</span>\n  <span class=function>read_str</span>:  <span class=function>tokenize</span>, error if no tokens, call <span class=function>read_form</span>(<span class=object>Reader</span>(<span class=var>tokens</span>))\n<span class=file>printer.EXT</span>:\n  <span class=function>pr_str</span>(<span class=var>ast</span>, <span class=var>print_readably</span>):\n    - map <span class=function>pr_str</span> across collections\n    - unescape strings if <span class=var>print_readably</span>\n<span class=file>step1_read_print.EXT</span>:\n  <span class=function>main</span>(<span class=var>args</span>): loop: <span class=function>writeline</span> <span class=function>PRINT</span>(<span class=function>EVAL</span>(<span class=function>READ</span>(<span class=function>readline</span>()), <span class=string>\"\"</span>))\n</code></pre>\n    </td>\n    <td width=50%>\n<pre><code><span class=file>core.EXT</span>:\n  <span class=function>read-string</span>: call <span class=function>reader.read_str</span>\n  <span class=function>slurp</span>: return file content as a string\n  <span class=function>atom</span>, <span class=function>atom?</span>, <span class=function>deref</span>, <span class=function>reset!</span>, <span class=function>swap!</span>: atom functions\n<span class=file>step6_file.EXT</span>:\n  <span class=function>main</span>(<span class=var>args</span>):\n    - add <span class=malsym>eval</span> and <span class=malsym>*ARGV*</span> to <span class=var>repl_env</span>\n    - define <span class=malsym>load-file</span> using <span class=function>rep</span>\n    - if <span class=var>args</span>, set <span class=malsym>*ARGV*</span> to rest(<span class=var>args</span>) and call <span class=malsym>load-file</span> with <span class=var>arg</span>s[0]\n\n\n\n\n</code></pre>\n    </td>\n  <tr>\n    <th>&nbsp;</th> <th>&nbsp;</th>\n  <tr>\n    <th>Step 2</th> <th>Step 7</th>\n  <tr>\n    <td width=50%>\n<pre><code><span class=file>step2_eval.EXT</span>:\n  <span class=function>eval_ast</span>(<span class=var>ast</span>, <span class=var>env</span>): lookup symbols in <span class=var>env</span>, map <span class=function>EVAL</span> across collections\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - if not list?(<span class=var>ast</span>), return <span class=function>eval_ast</span>(<span class=var>ast</span>, <span class=var>env</span>)\n    - otherwise apply (<span class=var>ast</span> is a list):\n      <span class=var>el</span> = <span class=function>eval_ast</span>(<span class=var>ast</span>, <span class=var>env</span>)\n      return <span class=var>el</span>[0](rest(<span class=var>el</span>))\n  <span class=function>main</span>(<span class=var>args</span>): loop: <span class=function>writeline</span> <span class=function>PRINT</span>(<span class=function>EVAL</span>(<span class=function>READ</span>(<span class=function>readline</span>()), {<span class=malsym>+</span>: <span class=function>add</span>, ...}))\n\n\n\n</code></pre>\n    </td>\n    <td width=50%>\n<pre><code><span class=file>core.EXT</span>:\n  <span class=function>cons</span>, <span class=function>concat</span>: sequence functions\n<span class=file>step7_quote.EXT</span>:\n  <span class=function>quasiquote</span>(ast):\n    - <span class=var>ast</span> is empty or not a list   -> (<span class=malsym>quote</span> <span class=var>ast</span>)\n    - (<span class=malsym>unquote</span> FOO)                -> FOO\n    - ((<span class=malsym>splice-unquote</span> FOO) BAR..) -> (<span class=malsym>concat</span> FOO <span class=function>quasiquote</span>(BAR...))</span>\n    - (FOO BAR...)                 -> (<span class=malsym>cons</span> FOO <span class=function>quasiquote</span>(BAR...))</span>\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - <span class=malsym>quote</span>      -> return <span class=var>ast</span>[1]\n    - <span class=malsym>quasiquote</span> -> set <span class=var>ast</span> to <span class=function>quasiquote</span>(<span class=var>ast</span>[1]), loop\n</code></pre>\n    </td>\n  <tr>\n    <th>&nbsp;</th> <th>&nbsp;</th>\n  <tr>\n    <th>Step 3</th> <th>Step 8</th>\n  <tr>\n    <td width=50%>\n<pre><code><span class=file>env.EXT</span>:\n  <span class=object>Env</span>(<span class=var>outer</span>) object: <span class=var>data</span>, <span class=function>set</span>(<span class=var>k</span>, <span class=var>v</span>), <span class=function>find</span>(<span class=var>k</span>), <span class=function>get</span>(<span class=var>k</span>)\n<span class=file>step3_env.EXT</span>:\n  <span class=function>eval_ast</span>(<span class=var>ast</span>, <span class=var>env</span>): switch to <span class=var>env</span>.<span class=function>get</span> for symbol lookup\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - <span class=malsym>def!</span>  -> return <span class=var>env</span>.<span class=function>set</span>(<span class=var>ast</span>[1], <span class=function>EVAL</span>(<span class=var>ast</span>[2], <span class=var>env</span>))\n    - <span class=malsym>let*</span>  -> create new env <span class=var>let_env</span>\n               for each ODD/EVEN pair in <span class=var>ast[1]</span>:\n                 <span class=var>let_env</span>.<span class=function>set</span>(ODD, <span class=function>EVAL</span>(EVEN, <span class=var>let_env</span>))\n               return <span class=function>EVAL</span>(<span class=var>ast</span>[2], <span class=var>let_env</span>)\n  <span class=function>main</span>(<span class=var>args</span>): populate <span class=var>repl_env</span> with numeric functions using <span class=var>repl_env</span>.<span class=function>set</span>\n</code></pre>\n    </td>\n    <td width=50%>\n<pre><code><span class=file>core.EXT</span>:\n  <span class=function>nth</span>, <span class=function>first</span>, <span class=function>rest</span>: sequence functions\n<span class=file>step8_macros.EXT</span>:\n  <span class=function>macroexpand</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - while <span class=var>env</span>.<span class=function>get</span>(<span class=var>ast</span>[0]) is a macro: <span class=var>ast</span> = <span class=var>env</span>.<span class=function>get</span>(<span class=var>ast</span>[0])(rest(<span class=var>ast</span>))\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - before apply section, add <span class=var>ast</span> = <span class=function>macroexpand</span>(<span class=var>ast</span>, <span class=var>env</span>)\n    - <span class=malsym>defmacro!</span>   -> same as <span class=malsym>def!</span>, but set mal function macro flag\n    - <span class=malsym>macroexpand</span> -> return <span class=function>macroexpand</span>(<span class=var>ast</span>[1], <span class=var>env</span>)\n\n\n</code></pre>\n    </td>\n  <tr>\n    <th>&nbsp;</th> <th>&nbsp;</th>\n  <tr>\n    <th>Step 4</th> <th>Step 9</th>\n  <tr>\n    <td width=50%>\n<pre><code><span class=file>env.EXT</span>:\n  <span class=object>Env</span>(<span class=var>outer</span>, <span class=var>binds</span>, <span class=var>exprs</span>) object: map <span class=var>binds</span> to <span class=var>exprs</span>, handle <span class=string>\"&amp;\"</span> as variadic\n<span class=file>core.EXT</span>:\n  <span class=function>=</span>: recursive compare of collections\n  <span class=function>pr-str</span>, <span class=function>str</span>: return <span class=function>pr_str</span>(<span class=function>arg</span>, true) join <span class=string>\" \"</span>, <span class=function>pr_str</span>(<span class=function>arg</span>, false) join <span class=string>\"\"</span>\n  <span class=function>prn</span>, <span class=function>println</span>: print <span class=function>pr_str</span>(<span class=function>arg</span>, true) join <span class=string>\"\"</span>, <span class=function>pr_str</span>(<span class=function>arg</span>, false) join <span class=string>\"\"</span>\n  <span class=function><</span>, <span class=function><=</span>, <span class=function>></span>, <span class=function>>=</span>, <span class=function>+</span>, <span class=function>-</span>, <span class=function>*</span>, <span class=function>/</span>: numeric comparison and numeric operations\n  <span class=function>list</span>, <span class=function>list?</span>, <span class=function>empty?</span>, <span class=function>count</span>: sequence functions\n<span class=file>step4_do_if_fn.EXT</span>:\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - <span class=malsym>do</span>  -> return last element of <span class=function>eval_ast</span>(<span class=var>ast</span>, <span class=var>env</span>)\n    - <span class=malsym>if</span>  -> if <span class=function>EVAL</span>(<span class=var>ast[1]</span>, <span class=var>env</span>): return <span class=function>EVAL</span>(<span class=var>ast[2]</span>, <span class=var>env</span>)\n             else                : return <span class=function>EVAL</span>(<span class=var>ast[3]</span>, <span class=var>env</span>)\n    - <span class=malsym>fn*</span> -> return closure:\n               (<span class=var>args</span>) -> <span class=function>EVAL</span>(<span class=var>ast</span>[2], new <span class=object>Env</span>(<span class=var>env</span>, <span class=var>ast</span>[1], <span class=var>args</span>))\n  <span class=function>main</span>(<span class=var>args</span>): populate <span class=var>repl_env</span> with core functions, define not using <span class=function>rep</span>()\n</code></pre>\n    </td>\n    <td width=50%>\n<pre><code><span class=file>core.EXT</span>:\n  <span class=function>throw</span>: raise mal value as exception (maybe wrap in native exception)\n  <span class=function>vector</span>, <span class=function>vector?</span>: sequence functions\n  <span class=function>hash-map</span>, <span class=function>get</span>, <span class=function>contains?</span>, <span class=function>keys</span>, <span class=function>vals</span>: hash-map functions\n  <span class=function>assoc</span>, <span class=function>dissoc</span>: immutable hash-map transform functions\n  <span class=function>apply</span>(<span class=var>f</span>, <span class=var>args...</span>, <span class=var>last</span>): return <span class=function>f</span>(<span class=function>concat</span>(<span class=var>args</span>, <span class=var>last</span>))\n  <span class=function>map</span>(<span class=var>f</span>, <span class=var>args</span>): return list of mapping <span class=function>f</span> on each <span class=var>args</span>\n<span class=file>step9_try.EXT</span>:\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - <span class=malsym>try*</span> -> try <span class=function>EVAL</span>(<span class=var>ast</span>[1], <span class=var>env</span>)\n                catch exception <span class=var>exc</span> (unwrap if necessary):\n                  new <span class=var>err_env</span> with <span class=var>ast</span>[2][1] symbol bound to <span class=var>exc</span>\n                  <span class=function>EVAL</span>(<span class=var>ast</span>[2][2], <span class=var>err_env</span>)\n\n\n\n</code></pre>\n    </td>\n  <tr>\n    <th>&nbsp;</th> <th>&nbsp;</th>\n  <tr>\n    <th>Step 5</th> <th>Step A</th>\n  <tr>\n    <td width=50%>\n<pre><code><span class=file>step5_tco.EXT</span>:\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - top level loop in <span class=function>EVAL</span>\n    - <span class=malsym>let*</span>  -> set <span class=var>env</span> to <span class=var>let_env</span>, set <span class=var>ast</span> to <span class=var>ast</span>[2], loop\n    - <span class=malsym>do</span>    -> <span class=function>eval_ast</span> of middle elements, sets <span class=var>ast</span> to last element, loop\n    - <span class=malsym>if</span>    -> set <span class=var>ast</span> to <span class=var>ast</span>[2] or <span class=var>ast</span>[3] (or <span class=malsym>nil</span>) depending condition, loop\n    - <span class=malsym>fn*</span>   -> return new mal function type <span class=var>f</span> with:\n                <span class=var>f</span>.<span class=var>ast</span>=<span class=var>ast</span>[2], <span class=var>f</span>.<span class=var>params</span>=<span class=var>ast</span>[1], <span class=var>f</span>.<span class=var>env</span>=<span class=var>env</span>\n    - apply -> <span class=var>el</span> = <span class=function>eval_ast</span>(<span class=var>ast</span>, <span class=var>env</span>)\n               <span class=var>f</span> = <span class=var>el</span>[0]\n               if <span class=var>f</span> is a mal function: <span class=var>ast</span> = <span class=var>f</span>.<span class=var>ast</span> and <span class=var>env</span> = <span class=var>f</span>.<span class=var>env</span>, loop\n               else                  : return <span class=var>el</span>[0](rest(<span class=var>el</span>))\n\n</code></pre>\n    </td>\n    <td width=50%>\n<pre><code><span class=file>core.EXT</span>:\n  <span class=function>string?</span>: true if string\n  <span class=function>readline</span>: prompt and read a line of input (synchronous)\n  <span class=function>time-ms</span>: return milliseconds since epoch (1970-1-1)\n  <span class=function>conj</span>, <span class=function>seq</span>: type specific sequence functions\n  <span class=function>meta</span>, <span class=function>with-meta</span>: metadata functions\n<span class=file>step9_try.EXT</span>:\n  <span class=function>EVAL</span>(<span class=var>ast</span>, <span class=var>env</span>):\n    - set <span class=malsym>*host-language*</span> in <span class=var>repl_env</span> to host language name\n  <span class=function>main</span>(<span class=var>args</span>): <span class=function>rep</span>(<span class=string>\"(println (str \\\"Mal [\\\" <span class=malsym>*host-language*</span> \\\"]\\\"))\"</span>)\n</code></pre>\n    </td>\n  <tr>\n    <th>&nbsp;</th> <th>&nbsp;</th>\n  </table>\n</body>\n</html>\n"
  },
  {
    "path": "docs/exercises.md",
    "content": "# Exercises to learn MAL\n\nThe process introduces LISP by describing the internals of selected\nlow-level constructs. As a complementary and more traditional\napproach, you may want to solve the following exercises in the MAL\nlanguage itself, using any of the existing implementations.\n\nYou are encouraged to use the shortcuts defined in the step files\n(`not`...) and `the `lib/` subdirectory (`reduce`...) whenever you\nfind that they increase the readability.\n\nThe difficulty is progressive in each section, but they focus on\nrelated topics and it is recommended to start them in parallel.\n\nSome solutions are given in the `examples` directory. Feel free to\nsubmit new solutions, or new exercises.\n\n## Replace parts of the process with native constructs\n\nOnce you have a working implementation, you may want to implement\nparts of the process inside the MAL language itself. This has no other\npurpose than learning the MAL language. Once it exists, a built-in\nimplementation will always be more efficient than a native\nimplementation. Also, the functions described in MAL process are\nselected for educative purposes, so portability accross\nimplementations does not matter much.\n\nYou may easily check your answers by passing them directly to the\ninterpreter. They will hide the built-in functions carrying the same\nnames, and the usual tests will check them.\n```\nmake REGRESS=1 TEST_OPTS='--hard --pre-eval=\\(load-file\\ \\\"../answer.mal\\\"\\)' test^IMPL^stepA\n```\n\n- Implement `nil?`, `true?`, `false?`, `empty?` and `sequential` with\n  another built-in function.\n\n- Implement `>`, `<=` and `>=` with `<`.\n\n- Implement `list`, `vec`, `prn`, `hash-map` and `swap!` as non-recursive\n  functions.\n\n- Implement `count`, `nth`, `map`, `concat` and `conj` with the empty\n  constructor `()`, `empty?`, `cons`, `first` and `rest`.\n\n  You may use `or` to make the definition of `nth` a bit less ugly,\n  but avoid `cond` because its definition refers to `nth`.\n\n  Let `count` and `nth` benefit from tail call optimization.\n\n  Try to replace explicit recursions with calls to `reduce` and `foldr`.\n\n  Once you have tested your solution, you should comment at least\n  `nth`.  Many implementations, for example `foldr` in `core.mal`,\n  rely on an efficient `nth` built-in function.\n\n- Implement the `do` special as a non-recursive function. The special\n  form will hide your implementation, so in order to test it, you will\n  need to give it another name and adapt the test accordingly.\n\n- Implement quoting with macros.\n  The same remark applies.\n\n- Implement most of `let*` as a macro that uses `fn*` and recursion.\n  The same remark applies.\n  A macro is necessary because a function would attempt to evaluate\n  the first argument.\n\n  Once your answer passes most tests and you understand which part is\n  tricky, you should search for black magic recipes on the web. Few of\n  us mortals are known to have invented a full solution on their own.\n\n- Implement `apply`.\n\n- Implement maps using lists.\n  - Recall how maps must be evaluated.\n  - In the tests, you may want to replace `{...}` with `(hash-map ...)`.\n  - An easy solution relies on lists alterning keys and values, so\n    that the `hash-map` is only a list in reverse order so that the\n    last definition takes precedence during searches.\n  - As a more performant solution will use lists to construct trees,\n    and ideally keep them balanced. You will find examples in most\n    teaching material about functional languages.\n  - Recall that `dissoc` is an optional feature. One you can implement\n    dissoc is by assoc'ing a replacement value that is a magic delete\n    keyword (e.g.: `__..DELETED..__`) which allows you to shadow\n    values in the lower levels of the structure. The hash map\n    functions have to detect that and do the right thing. e.g. `(keys\n    ...)` might have to keep track of deleted values as it is scanning\n    the tree and not add those keys when it finds them further down\n    the tree.\n\n- Implement macros within MAL.\n\n## More folds\n\n- Compute the sum of a sequence of numbers.\n- Compute the product of a sequence of numbers.\n\n- Compute the logical conjunction (\"and\") and disjunction (\"or\") of a\n  sequence of MAL values interpreted as boolean values.  For example,\n  `(conjunction [true 1 0 \"\" \"a\" nil true {}])`\n  should evaluate to `false` or `nil` because of the `nil` element.\n\n  Why are folds not the best solution here, in terms of average\n  performances?\n\n- Does \"-2-3-4\" translate to `(reduce - 0 [2 3 4])`?\n\n- Suggest better solutions for\n  `(reduce str \"\" xs)` and\n  `(reduce concat [] xs)`.\n\n- What does `(reduce (fn* [acc _] acc) xs)` nil answer?\n\n- The answer is `(fn* [xs] (reduce (fn* [_ x] x) nil xs))`.\n  What was the question?\n\n- What is the intent of\n `(reduce (fn* [acc x] (if (< acc x) x acc)) 0 xs)`?\n\n  Why is it the wrong answer?\n\n- Though `(sum (map count xs))` or `(count (apply concat xs))` can be\n  considered more readable, implement the same effect with a single loop.\n- Compute the maximal length in a list of lists.\n\n- How would you name\n  `(fn* [& fs] (foldr (fn* [f acc] (fn* [x] (f (acc x)))) identity fs))`?\n"
  },
  {
    "path": "docs/graph/README.md",
    "content": "# Mal Implementation Stats Graph\n\n\n## Updating the data\n\n* Install prerequisites:\n\nFor ubuntu:\n```\nsudo apt-get install gh\nsudo apt-get golang\n```\n\nFor macos:\n```\nbrew install gh\nbrew install go\n```\n\n* Create logs dir and enter graph dir:\n```\nmkdir -p docs/graph/logs\ncd docs/graph/logs\n```\n\n* Install npm deps\n```\nnpm install\n```\n\n* Clone and build loccount:\n```\ngit clone https://gitlab.com/esr/loccount\nmake -C loccount\n```\n\n* Auth with github:\n```\ngh auth login\n```\n\n* Download artifacts from a recent full and successful workflow run:\n\n```\n# list workflow runs\n$ gh run list --repo kanaka/mal\n\n# Download recent full successful run:\n$ gh run download 10598199016 --repo kanaka/mal\n```\n\n* Run the [StackOverflow tags\n  query](https://data.stackexchange.com/stackoverflow/query/edit/1013465)\n  and then download the CSV link:\n\n```\ncurl https://data.stackexchange.com/stackoverflow/csv/2267200 -o so-tags.csv\n```\n\n* Remove/clean all generated files:\n\n```\n( cd ../.. && git ls-files --others impls/ | xargs rm )\n```\n\n* Download GitHub and StackOverflow data and generate the final\n  combined data set:\n\n```\nPATH=$PATH:$(pwd)/loccount\ntime VERBOSE=1 node ./collect_data.js logs/ all_data.json\n```\n"
  },
  {
    "path": "docs/graph/all_data.json",
    "content": "{\n  \"ada\": {\n    \"dir\": \"ada\",\n    \"name\": \"Ada\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 10,\n    \"perf2\": 44,\n    \"perf3\": 974,\n    \"pull_count\": 261,\n    \"pull_rank\": 66,\n    \"push_count\": 19718,\n    \"push_rank\": 60,\n    \"star_count\": 554,\n    \"star_rank\": 68,\n    \"sloc\": 3547,\n    \"files\": 19,\n    \"author_name\": \"Chris Moore\",\n    \"author_url\": \"https://github.com/zmower\",\n    \"so_count\": 2416,\n    \"so_rank\": 57,\n    \"lloc\": 2199\n  },\n  \"ada.2\": {\n    \"dir\": \"ada.2\",\n    \"name\": \"Ada #2\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 1,\n    \"perf3\": 84457,\n    \"pull_count\": 261,\n    \"pull_rank\": 67,\n    \"push_count\": 19718,\n    \"push_rank\": 61,\n    \"star_count\": 554,\n    \"star_rank\": 69,\n    \"sloc\": 2277,\n    \"files\": 30,\n    \"author_name\": \"Nicolas Boulenguez\",\n    \"author_url\": \"https://github.com/asarhaddon\",\n    \"so_count\": 2416,\n    \"so_rank\": 58,\n    \"lloc\": 1437\n  },\n  \"awk\": {\n    \"dir\": \"awk\",\n    \"name\": \"GNU Awk\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 7,\n    \"perf2\": 24,\n    \"perf3\": 1356,\n    \"pull_count\": 2,\n    \"pull_rank\": 71,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": 13346,\n    \"star_rank\": 54,\n    \"sloc\": 2203,\n    \"files\": 7,\n    \"author_name\": \"Miutsuru Kariya\",\n    \"author_url\": \"https://github.com/kariya-mitsuru\",\n    \"so_count\": 33144,\n    \"so_rank\": 30,\n    \"lloc\": 0\n  },\n  \"bash\": {\n    \"dir\": \"bash\",\n    \"name\": \"Bash 4\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 787,\n    \"perf2\": 3465,\n    \"perf3\": 11,\n    \"pull_count\": 921358,\n    \"pull_rank\": 16,\n    \"push_count\": 3851409,\n    \"push_rank\": 16,\n    \"star_count\": 2264769,\n    \"star_rank\": 18,\n    \"sloc\": 1110,\n    \"files\": 7,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 156259,\n    \"so_rank\": 17,\n    \"lloc\": 0\n  },\n  \"basic\": {\n    \"dir\": \"basic\",\n    \"name\": \"BASIC\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Static\",\n    \"modes\": [\n      \"cbm\",\n      \"qbasic\"\n    ],\n    \"perf1\": 6,\n    \"perf2\": 19,\n    \"perf3\": 1675,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1960,\n    \"files\": 13,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 858,\n    \"so_rank\": 65,\n    \"lloc\": 1697\n  },\n  \"bbc-basic\": {\n    \"dir\": \"bbc-basic\",\n    \"name\": \"BBC BASIC V\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 60,\n    \"perf2\": 290,\n    \"perf3\": 149,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1355,\n    \"files\": 7,\n    \"author_name\": \"Ben Harris\",\n    \"author_url\": \"https://github.com/bjh21\",\n    \"so_count\": 9,\n    \"so_rank\": 85,\n    \"lloc\": 1353\n  },\n  \"c\": {\n    \"dir\": \"c\",\n    \"name\": \"C\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 36416,\n    \"pull_count\": 1411773,\n    \"pull_rank\": 13,\n    \"push_count\": 5888004,\n    \"push_rank\": 11,\n    \"star_count\": 4085282,\n    \"star_rank\": 10,\n    \"sloc\": 1990,\n    \"files\": 15,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 405875,\n    \"so_rank\": 10,\n    \"lloc\": 1069\n  },\n  \"c.2\": {\n    \"dir\": \"c.2\",\n    \"name\": \"C #2\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 3,\n    \"perf3\": 15820,\n    \"pull_count\": 1411773,\n    \"pull_rank\": 14,\n    \"push_count\": 5888004,\n    \"push_rank\": 12,\n    \"star_count\": 4085282,\n    \"star_rank\": 11,\n    \"sloc\": 3326,\n    \"files\": 16,\n    \"author_name\": \"Duncan Watts\",\n    \"author_url\": \"https://github.com/fungiblecog\",\n    \"so_count\": 405875,\n    \"so_rank\": 11,\n    \"lloc\": 1677\n  },\n  \"cpp\": {\n    \"dir\": \"cpp\",\n    \"name\": \"C++\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 33490,\n    \"pull_count\": 2960837,\n    \"pull_rank\": 7,\n    \"push_count\": 10016021,\n    \"push_rank\": 8,\n    \"star_count\": 5571338,\n    \"star_rank\": 8,\n    \"sloc\": 2021,\n    \"files\": 19,\n    \"author_name\": \"Stephen Thirlwall\",\n    \"author_url\": \"https://github.com/sdt\",\n    \"so_count\": 887548,\n    \"so_rank\": 7,\n    \"lloc\": 945\n  },\n  \"cs\": {\n    \"dir\": \"cs\",\n    \"name\": \"C#\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 4,\n    \"perf2\": 5,\n    \"perf3\": 24285,\n    \"pull_count\": 1264911,\n    \"pull_rank\": 15,\n    \"push_count\": 4838573,\n    \"push_rank\": 14,\n    \"star_count\": 2687097,\n    \"star_rank\": 17,\n    \"sloc\": 1185,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 1652013,\n    \"so_rank\": 5,\n    \"lloc\": 582\n  },\n  \"chuck\": {\n    \"dir\": \"chuck\",\n    \"name\": \"ChucK\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 24,\n    \"perf2\": 70,\n    \"perf3\": 142,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 2509,\n    \"files\": 87,\n    \"author_name\": \"Vasilij Schneidermann\",\n    \"author_url\": \"https://github.com/wasamasa\",\n    \"so_count\": 22,\n    \"so_rank\": 83,\n    \"lloc\": 963\n  },\n  \"clojure\": {\n    \"dir\": \"clojure\",\n    \"name\": \"Clojure\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [\n      \"clj\",\n      \"cljs\"\n    ],\n    \"perf1\": 10,\n    \"perf2\": 34,\n    \"perf3\": 7675,\n    \"pull_count\": 118302,\n    \"pull_rank\": 27,\n    \"push_count\": 556345,\n    \"push_rank\": 26,\n    \"star_count\": 296560,\n    \"star_rank\": 25,\n    \"sloc\": 408,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 17703,\n    \"so_rank\": 37,\n    \"lloc\": 0\n  },\n  \"coffee\": {\n    \"dir\": \"coffee\",\n    \"name\": \"CoffeeScript\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 2,\n    \"perf2\": 6,\n    \"perf3\": 33096,\n    \"pull_count\": 135423,\n    \"pull_rank\": 26,\n    \"push_count\": 596268,\n    \"push_rank\": 25,\n    \"star_count\": 587936,\n    \"star_rank\": 21,\n    \"sloc\": 447,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 9742,\n    \"so_rank\": 42,\n    \"lloc\": 0\n  },\n  \"common-lisp\": {\n    \"dir\": \"common-lisp\",\n    \"name\": \"Common Lisp\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 2,\n    \"perf3\": 35135,\n    \"pull_count\": 9583,\n    \"pull_rank\": 49,\n    \"push_count\": 85811,\n    \"push_rank\": 44,\n    \"star_count\": 52545,\n    \"star_rank\": 39,\n    \"sloc\": 1000,\n    \"files\": 11,\n    \"author_name\": \"Iqbal Ansari\",\n    \"author_url\": \"https://github.com/iqbalansari\",\n    \"so_count\": 6341,\n    \"so_rank\": 49,\n    \"lloc\": 0\n  },\n  \"crystal\": {\n    \"dir\": \"crystal\",\n    \"name\": \"Crystal\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 1,\n    \"perf3\": 64175,\n    \"pull_count\": 11247,\n    \"pull_rank\": 48,\n    \"push_count\": 35005,\n    \"push_rank\": 52,\n    \"star_count\": 29422,\n    \"star_rank\": 47,\n    \"sloc\": 944,\n    \"files\": 8,\n    \"author_name\": \"Linda_pp\",\n    \"author_url\": \"https://github.com/rhysd\",\n    \"so_count\": 662,\n    \"so_rank\": 67,\n    \"lloc\": 0\n  },\n  \"d\": {\n    \"dir\": \"d\",\n    \"name\": \"D\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 0,\n    \"perf3\": 41431,\n    \"pull_count\": 8541,\n    \"pull_rank\": 52,\n    \"push_count\": 71800,\n    \"push_rank\": 49,\n    \"star_count\": 23317,\n    \"star_rank\": 48,\n    \"sloc\": 1281,\n    \"files\": 8,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 2644,\n    \"so_rank\": 56,\n    \"lloc\": 549\n  },\n  \"dart\": {\n    \"dir\": \"dart\",\n    \"name\": \"Dart\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 5,\n    \"perf2\": 10,\n    \"perf3\": 17398,\n    \"pull_count\": 182518,\n    \"pull_rank\": 22,\n    \"push_count\": 245271,\n    \"push_rank\": 34,\n    \"star_count\": 280006,\n    \"star_rank\": 26,\n    \"sloc\": 935,\n    \"files\": 8,\n    \"author_name\": \"Harry Terkelsen\",\n    \"author_url\": \"https://github.com/hterkelsen\",\n    \"so_count\": 94667,\n    \"so_rank\": 23,\n    \"lloc\": 467\n  },\n  \"elixir\": {\n    \"dir\": \"elixir\",\n    \"name\": \"Elixir\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 17,\n    \"perf2\": 43,\n    \"perf3\": 839,\n    \"pull_count\": 118076,\n    \"pull_rank\": 28,\n    \"push_count\": 266735,\n    \"push_rank\": 33,\n    \"star_count\": 216415,\n    \"star_rank\": 27,\n    \"sloc\": 669,\n    \"files\": 10,\n    \"author_name\": \"Martin Ek\",\n    \"author_url\": \"https://github.com/ekmartin\",\n    \"so_count\": 9601,\n    \"so_rank\": 44,\n    \"lloc\": 0\n  },\n  \"elm\": {\n    \"dir\": \"elm\",\n    \"name\": \"Elm\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 19,\n    \"perf2\": 55,\n    \"perf3\": 1971,\n    \"pull_count\": 12978,\n    \"pull_rank\": 46,\n    \"push_count\": 59552,\n    \"push_rank\": 51,\n    \"star_count\": 38261,\n    \"star_rank\": 43,\n    \"sloc\": 2404,\n    \"files\": 13,\n    \"author_name\": \"Jos van Bakel\",\n    \"author_url\": \"https://github.com/c0deaddict\",\n    \"so_count\": 1895,\n    \"so_rank\": 61,\n    \"lloc\": 0\n  },\n  \"elisp\": {\n    \"dir\": \"elisp\",\n    \"name\": \"Emacs Lisp\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 14,\n    \"perf3\": 5600,\n    \"pull_count\": 69374,\n    \"pull_rank\": 34,\n    \"push_count\": 366344,\n    \"push_rank\": 29,\n    \"star_count\": 207304,\n    \"star_rank\": 30,\n    \"sloc\": 725,\n    \"files\": 7,\n    \"author_name\": \"Vasilij Schneidermann\",\n    \"author_url\": \"https://github.com/wasamasa\",\n    \"so_count\": 3771,\n    \"so_rank\": 54,\n    \"lloc\": 0\n  },\n  \"erlang\": {\n    \"dir\": \"erlang\",\n    \"name\": \"Erlang\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 32,\n    \"perf2\": 62,\n    \"perf3\": 344,\n    \"pull_count\": 79420,\n    \"pull_rank\": 31,\n    \"push_count\": 285722,\n    \"push_rank\": 31,\n    \"star_count\": 149139,\n    \"star_rank\": 31,\n    \"sloc\": 1130,\n    \"files\": 8,\n    \"author_name\": \"Nathan Fiedler\",\n    \"author_url\": \"https://github.com/nlfiedler\",\n    \"so_count\": 9674,\n    \"so_rank\": 43,\n    \"lloc\": 0\n  },\n  \"es6\": {\n    \"dir\": \"es6\",\n    \"name\": \"ES6\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 3,\n    \"perf3\": 30500,\n    \"pull_count\": 6754454,\n    \"pull_rank\": 2,\n    \"push_count\": 24043941,\n    \"push_rank\": 2,\n    \"star_count\": 25547072,\n    \"star_rank\": 2,\n    \"sloc\": 474,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 519108,\n    \"so_rank\": 8,\n    \"lloc\": 0\n  },\n  \"fsharp\": {\n    \"dir\": \"fsharp\",\n    \"name\": \"F#\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 6,\n    \"perf2\": 6,\n    \"perf3\": 35952,\n    \"pull_count\": 32510,\n    \"pull_rank\": 37,\n    \"push_count\": 145642,\n    \"push_rank\": 38,\n    \"star_count\": 44240,\n    \"star_rank\": 42,\n    \"sloc\": 1074,\n    \"files\": 11,\n    \"author_name\": \"Peter Stephens\",\n    \"author_url\": \"https://github.com/pstephens\",\n    \"so_count\": 18116,\n    \"so_rank\": 36,\n    \"lloc\": 2\n  },\n  \"fennel\": {\n    \"dir\": \"fennel\",\n    \"name\": \"Fennel\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 2301,\n    \"perf2\": 7241,\n    \"perf3\": 4,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1,\n    \"files\": 1,\n    \"author_name\": \"sogaiu\",\n    \"author_url\": \"https://github.com/sogaiu\",\n    \"so_count\": 3,\n    \"so_rank\": 88,\n    \"lloc\": 0\n  },\n  \"factor\": {\n    \"dir\": \"factor\",\n    \"name\": \"Factor\",\n    \"syntax\": \"Stack\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 2,\n    \"perf3\": 40360,\n    \"pull_count\": 421,\n    \"pull_rank\": 63,\n    \"push_count\": 10507,\n    \"push_rank\": 66,\n    \"star_count\": 100,\n    \"star_rank\": 71,\n    \"sloc\": 394,\n    \"files\": 8,\n    \"author_name\": \"Jordan Lewis\",\n    \"author_url\": \"https://github.com/jordanlewis\",\n    \"so_count\": 65,\n    \"so_rank\": 78,\n    \"lloc\": 0\n  },\n  \"fantom\": {\n    \"dir\": \"fantom\",\n    \"name\": \"Fantom\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 7,\n    \"perf2\": 16,\n    \"perf3\": 109845,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 733,\n    \"files\": 9,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 63,\n    \"so_rank\": 79,\n    \"lloc\": 0\n  },\n  \"forth\": {\n    \"dir\": \"forth\",\n    \"name\": \"Forth\",\n    \"syntax\": \"Stack\",\n    \"type_check\": \"OTHER\",\n    \"modes\": [],\n    \"perf1\": 37,\n    \"perf2\": 147,\n    \"perf3\": 291,\n    \"pull_count\": 32,\n    \"pull_rank\": 69,\n    \"push_count\": 1926,\n    \"push_rank\": 68,\n    \"star_count\": 632,\n    \"star_rank\": 67,\n    \"sloc\": 1415,\n    \"files\": 8,\n    \"author_name\": \"Chris Houser\",\n    \"author_url\": \"https://github.com/chouser\",\n    \"so_count\": 299,\n    \"so_rank\": 72,\n    \"lloc\": 0\n  },\n  \"guile\": {\n    \"dir\": \"guile\",\n    \"name\": \"GNU Guile\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 2,\n    \"perf3\": 15138,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 735,\n    \"files\": 9,\n    \"author_name\": \"Mu Lei\",\n    \"author_url\": \"https://github.com/NalaGinrut\",\n    \"so_count\": 262,\n    \"so_rank\": 73,\n    \"lloc\": 0\n  },\n  \"gnu-smalltalk\": {\n    \"dir\": \"gnu-smalltalk\",\n    \"name\": \"GNU Smalltalk\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 7,\n    \"perf2\": 21,\n    \"perf3\": 1709,\n    \"pull_count\": 13447,\n    \"pull_rank\": 44,\n    \"push_count\": 69848,\n    \"push_rank\": 50,\n    \"star_count\": 4823,\n    \"star_rank\": 61,\n    \"sloc\": 1005,\n    \"files\": 10,\n    \"author_name\": \"Vasilij Schneidermann\",\n    \"author_url\": \"https://github.com/wasamasa\",\n    \"so_count\": 115,\n    \"so_rank\": 75,\n    \"lloc\": 0\n  },\n  \"go\": {\n    \"dir\": \"go\",\n    \"name\": \"Go\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 0,\n    \"perf3\": 72067,\n    \"pull_count\": 2795669,\n    \"pull_rank\": 8,\n    \"push_count\": 5100633,\n    \"push_rank\": 13,\n    \"star_count\": 7730404,\n    \"star_rank\": 7,\n    \"sloc\": 1412,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 73691,\n    \"so_rank\": 24,\n    \"lloc\": 673\n  },\n  \"groovy\": {\n    \"dir\": \"groovy\",\n    \"name\": \"Groovy\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 64,\n    \"perf2\": 127,\n    \"perf3\": 1685,\n    \"pull_count\": 141340,\n    \"pull_rank\": 24,\n    \"push_count\": 473823,\n    \"push_rank\": 28,\n    \"star_count\": 148765,\n    \"star_rank\": 32,\n    \"sloc\": 672,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 30295,\n    \"so_rank\": 31,\n    \"lloc\": 0\n  },\n  \"haskell\": {\n    \"dir\": \"haskell\",\n    \"name\": \"Haskell\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 6,\n    \"perf3\": 6558,\n    \"pull_count\": 114458,\n    \"pull_rank\": 29,\n    \"push_count\": 732765,\n    \"push_rank\": 23,\n    \"star_count\": 334357,\n    \"star_rank\": 24,\n    \"sloc\": 712,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 51449,\n    \"so_rank\": 27,\n    \"lloc\": 0\n  },\n  \"haxe\": {\n    \"dir\": \"haxe\",\n    \"name\": \"Haxe\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [\n      \"neko\",\n      \"python\",\n      \"cpp\",\n      \"js\"\n    ],\n    \"perf1\": 2,\n    \"perf2\": 4,\n    \"perf3\": 62403,\n    \"pull_count\": 13654,\n    \"pull_rank\": 43,\n    \"push_count\": 74768,\n    \"push_rank\": 48,\n    \"star_count\": 33782,\n    \"star_rank\": 44,\n    \"sloc\": 1089,\n    \"files\": 11,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 1635,\n    \"so_rank\": 62,\n    \"lloc\": 454\n  },\n  \"hy\": {\n    \"dir\": \"hy\",\n    \"name\": \"Hy\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 10,\n    \"perf2\": 37,\n    \"perf3\": 1030,\n    \"pull_count\": 8,\n    \"pull_rank\": 70,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": 308,\n    \"star_rank\": 70,\n    \"sloc\": 388,\n    \"files\": 7,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 101,\n    \"so_rank\": 76,\n    \"lloc\": 0\n  },\n  \"io\": {\n    \"dir\": \"io\",\n    \"name\": \"Io\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 113,\n    \"perf2\": 423,\n    \"perf3\": 78,\n    \"pull_count\": 72,\n    \"pull_rank\": 68,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 538,\n    \"files\": 7,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 17620,\n    \"so_rank\": 38,\n    \"lloc\": 0\n  },\n  \"java\": {\n    \"dir\": \"java\",\n    \"name\": \"Java\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 2,\n    \"perf2\": 10,\n    \"perf3\": 182169,\n    \"pull_count\": 4219162,\n    \"pull_rank\": 5,\n    \"push_count\": 15173396,\n    \"push_rank\": 5,\n    \"star_count\": 9695699,\n    \"star_rank\": 5,\n    \"sloc\": 1511,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 1919299,\n    \"so_rank\": 4,\n    \"lloc\": 696\n  },\n  \"java-truffle\": {\n    \"dir\": \"java-truffle\",\n    \"name\": \"Java Truffle\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 5,\n    \"perf2\": 13,\n    \"perf3\": 163894,\n    \"pull_count\": 4219162,\n    \"pull_rank\": 6,\n    \"push_count\": 15173396,\n    \"push_rank\": 6,\n    \"star_count\": 9695699,\n    \"star_rank\": 6,\n    \"sloc\": 5827,\n    \"files\": 12,\n    \"author_name\": \"Matt McGill\",\n    \"author_url\": \"https://github.com/mmcgill\",\n    \"so_count\": 897,\n    \"so_rank\": 64,\n    \"lloc\": 2811\n  },\n  \"js\": {\n    \"dir\": \"js\",\n    \"name\": \"JavaScript\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 4,\n    \"perf3\": 43462,\n    \"pull_count\": 6754454,\n    \"pull_rank\": 1,\n    \"push_count\": 24043941,\n    \"push_rank\": 1,\n    \"star_count\": 25547072,\n    \"star_rank\": 1,\n    \"sloc\": 856,\n    \"files\": 10,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 4346255,\n    \"so_rank\": 1,\n    \"lloc\": 0\n  },\n  \"jq\": {\n    \"dir\": \"jq\",\n    \"name\": \"jq\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": null,\n    \"perf2\": null,\n    \"perf3\": 0,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 88,\n    \"files\": 2,\n    \"author_name\": \"Ali MohammadPur\",\n    \"author_url\": \"https://github.com/alimpfard\",\n    \"so_count\": 6778,\n    \"so_rank\": 48,\n    \"lloc\": 0\n  },\n  \"janet\": {\n    \"dir\": \"janet\",\n    \"name\": \"Janet\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 7,\n    \"perf2\": 26,\n    \"perf3\": 1846,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1,\n    \"files\": 1,\n    \"author_name\": \"sogaiu\",\n    \"author_url\": \"https://github.com/sogaiu\",\n    \"so_count\": 3,\n    \"so_rank\": 89,\n    \"lloc\": 0\n  },\n  \"julia\": {\n    \"dir\": \"julia\",\n    \"name\": \"Julia\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 115,\n    \"perf2\": 13,\n    \"perf3\": 8375,\n    \"pull_count\": 41276,\n    \"pull_rank\": 36,\n    \"push_count\": 174375,\n    \"push_rank\": 36,\n    \"star_count\": 54307,\n    \"star_rank\": 38,\n    \"sloc\": 560,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 12754,\n    \"so_rank\": 40,\n    \"lloc\": 0\n  },\n  \"kotlin\": {\n    \"dir\": \"kotlin\",\n    \"name\": \"Kotlin\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 8,\n    \"perf2\": 19,\n    \"perf3\": 114806,\n    \"pull_count\": 368780,\n    \"pull_rank\": 20,\n    \"push_count\": 853297,\n    \"push_rank\": 21,\n    \"star_count\": 577249,\n    \"star_rank\": 22,\n    \"sloc\": 741,\n    \"files\": 8,\n    \"author_name\": \"Javier Fernandez-Ivern\",\n    \"author_url\": \"https://github.com/ivern\",\n    \"so_count\": 96732,\n    \"so_rank\": 21,\n    \"lloc\": 0\n  },\n  \"latex3\": {\n    \"dir\": \"latex3\",\n    \"name\": \"LaTeX3\",\n    \"syntax\": \"Other\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1131,\n    \"files\": 7,\n    \"author_name\": \"Nicolas Boulenguez\",\n    \"author_url\": \"https://github.com/asarhaddon\",\n    \"so_count\": 11509,\n    \"so_rank\": 41,\n    \"lloc\": 0\n  },\n  \"livescript\": {\n    \"dir\": \"livescript\",\n    \"name\": \"LiveScript\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 4,\n    \"perf2\": 10,\n    \"perf3\": 16804,\n    \"pull_count\": 327,\n    \"pull_rank\": 64,\n    \"push_count\": 8343,\n    \"push_rank\": 67,\n    \"star_count\": 9631,\n    \"star_rank\": 56,\n    \"sloc\": 783,\n    \"files\": 8,\n    \"author_name\": \"Jos van Bakel\",\n    \"author_url\": \"https://github.com/c0deaddict\",\n    \"so_count\": 66,\n    \"so_rank\": 77,\n    \"lloc\": 0\n  },\n  \"logo\": {\n    \"dir\": \"logo\",\n    \"name\": \"Logo\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 179,\n    \"perf2\": 777,\n    \"perf3\": 48,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 805,\n    \"files\": 8,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 47,\n    \"so_rank\": 81,\n    \"lloc\": 0\n  },\n  \"lua\": {\n    \"dir\": \"lua\",\n    \"name\": \"Lua\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 3931,\n    \"perf2\": 16211,\n    \"perf3\": 2,\n    \"pull_count\": 149093,\n    \"pull_rank\": 23,\n    \"push_count\": 765952,\n    \"push_rank\": 22,\n    \"star_count\": 386542,\n    \"star_rank\": 23,\n    \"sloc\": 925,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 22832,\n    \"so_rank\": 34,\n    \"lloc\": 0\n  },\n  \"make\": {\n    \"dir\": \"make\",\n    \"name\": \"GNU Make\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"OTHER\",\n    \"modes\": [],\n    \"perf1\": 270,\n    \"perf2\": 1329,\n    \"perf3\": 18,\n    \"pull_count\": 97160,\n    \"pull_rank\": 30,\n    \"push_count\": 286234,\n    \"push_rank\": 30,\n    \"star_count\": 101953,\n    \"star_rank\": 34,\n    \"sloc\": 798,\n    \"files\": 12,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 25601,\n    \"so_rank\": 33,\n    \"lloc\": 0\n  },\n  \"mal\": {\n    \"dir\": \"mal\",\n    \"name\": \"mal itself\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 72,\n    \"perf2\": 321,\n    \"perf3\": 156,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 206,\n    \"files\": 4,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 0,\n    \"so_rank\": 91,\n    \"lloc\": 0\n  },\n  \"matlab\": {\n    \"dir\": \"matlab\",\n    \"name\": \"MATLAB\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 598,\n    \"perf2\": 2036,\n    \"perf3\": 17,\n    \"pull_count\": 6111,\n    \"pull_rank\": 54,\n    \"push_count\": 130167,\n    \"push_rank\": 39,\n    \"star_count\": 17596,\n    \"star_rank\": 51,\n    \"sloc\": 1103,\n    \"files\": 17,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 94997,\n    \"so_rank\": 22,\n    \"lloc\": 0\n  },\n  \"miniMAL\": {\n    \"dir\": \"miniMAL\",\n    \"name\": \"miniMAL\",\n    \"syntax\": \"JSON\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 144,\n    \"perf2\": 524,\n    \"perf3\": 72,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 727,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 0,\n    \"so_rank\": 92,\n    \"lloc\": 0\n  },\n  \"nasm\": {\n    \"dir\": \"nasm\",\n    \"name\": \"NASM\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"OTHER\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 3,\n    \"pull_count\": 14978,\n    \"pull_rank\": 41,\n    \"push_count\": 126728,\n    \"push_rank\": 40,\n    \"star_count\": 54612,\n    \"star_rank\": 37,\n    \"sloc\": 6166,\n    \"files\": 9,\n    \"author_name\": \"Ben Dudson\",\n    \"author_url\": \"https://github.com/bendudson\",\n    \"so_count\": 5255,\n    \"so_rank\": 52,\n    \"lloc\": 0\n  },\n  \"nim\": {\n    \"dir\": \"nim\",\n    \"name\": \"Nim\",\n    \"syntax\": \"Python\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 56321,\n    \"pull_count\": 5093,\n    \"pull_rank\": 55,\n    \"push_count\": 17440,\n    \"push_rank\": 62,\n    \"star_count\": 16110,\n    \"star_rank\": 52,\n    \"sloc\": 625,\n    \"files\": 7,\n    \"author_name\": \"Dennis Felsing\",\n    \"author_url\": \"https://github.com/def-\",\n    \"so_count\": 687,\n    \"so_rank\": 66,\n    \"lloc\": 0\n  },\n  \"objpascal\": {\n    \"dir\": \"objpascal\",\n    \"name\": \"Object Pascal\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 3,\n    \"perf2\": 12,\n    \"perf3\": 3596,\n    \"pull_count\": 9438,\n    \"pull_rank\": 50,\n    \"push_count\": 95092,\n    \"push_rank\": 42,\n    \"star_count\": 48783,\n    \"star_rank\": 40,\n    \"sloc\": 1553,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 67138,\n    \"so_rank\": 26,\n    \"lloc\": 967\n  },\n  \"objc\": {\n    \"dir\": \"objc\",\n    \"name\": \"Objective C\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 5,\n    \"perf2\": 19,\n    \"perf3\": 1958,\n    \"pull_count\": 290475,\n    \"pull_rank\": 21,\n    \"push_count\": 1326999,\n    \"push_rank\": 19,\n    \"star_count\": 3444492,\n    \"star_rank\": 14,\n    \"sloc\": 1121,\n    \"files\": 16,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 292176,\n    \"so_rank\": 13,\n    \"lloc\": 511\n  },\n  \"ocaml\": {\n    \"dir\": \"ocaml\",\n    \"name\": \"OCaml\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 39621,\n    \"pull_count\": 71286,\n    \"pull_rank\": 33,\n    \"push_count\": 242743,\n    \"push_rank\": 35,\n    \"star_count\": 131717,\n    \"star_rank\": 33,\n    \"sloc\": 541,\n    \"files\": 7,\n    \"author_name\": \"Chris Houser\",\n    \"author_url\": \"https://github.com/chouser\",\n    \"so_count\": 7644,\n    \"so_rank\": 47,\n    \"lloc\": 0\n  },\n  \"perl\": {\n    \"dir\": \"perl\",\n    \"name\": \"Perl\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 3,\n    \"perf2\": 12,\n    \"perf3\": 3315,\n    \"pull_count\": 138992,\n    \"pull_rank\": 25,\n    \"push_count\": 720857,\n    \"push_rank\": 24,\n    \"star_count\": 210224,\n    \"star_rank\": 29,\n    \"sloc\": 836,\n    \"files\": 9,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 68197,\n    \"so_rank\": 25,\n    \"lloc\": 418\n  },\n  \"perl6\": {\n    \"dir\": \"perl6\",\n    \"name\": \"Perl 6\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 55,\n    \"perf2\": 147,\n    \"perf3\": 311,\n    \"pull_count\": 4302,\n    \"pull_rank\": 56,\n    \"push_count\": 24807,\n    \"push_rank\": 57,\n    \"star_count\": 1314,\n    \"star_rank\": 65,\n    \"sloc\": 460,\n    \"files\": 7,\n    \"author_name\": \"Hinrik Örn Sigurðsson\",\n    \"author_url\": \"https://github.com/hinrik\",\n    \"so_count\": 2054,\n    \"so_rank\": 60,\n    \"lloc\": 155\n  },\n  \"php\": {\n    \"dir\": \"php\",\n    \"name\": \"PHP\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 2,\n    \"perf3\": 12551,\n    \"pull_count\": 2791184,\n    \"pull_rank\": 9,\n    \"push_count\": 10165121,\n    \"push_rank\": 7,\n    \"star_count\": 4379644,\n    \"star_rank\": 9,\n    \"sloc\": 951,\n    \"files\": 10,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 1467322,\n    \"so_rank\": 6,\n    \"lloc\": 524\n  },\n  \"picolisp\": {\n    \"dir\": \"picolisp\",\n    \"name\": \"Picolisp\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 3,\n    \"perf3\": 10702,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 561,\n    \"files\": 9,\n    \"author_name\": \"Vasilij Schneidermann\",\n    \"author_url\": \"https://github.com/wasamasa\",\n    \"so_count\": 8,\n    \"so_rank\": 86,\n    \"lloc\": 0\n  },\n  \"pike\": {\n    \"dir\": \"pike\",\n    \"name\": \"Pike\",\n    \"syntax\": \"C\",\n    \"type_check\": \"OTHER\",\n    \"modes\": [],\n    \"perf1\": 2,\n    \"perf2\": 6,\n    \"perf3\": 7568,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1,\n    \"files\": 1,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 14,\n    \"so_rank\": 84,\n    \"lloc\": 0\n  },\n  \"plpgsql\": {\n    \"dir\": \"plpgsql\",\n    \"name\": \"PL/pgSQL\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 324,\n    \"perf2\": 1673,\n    \"perf3\": 28,\n    \"pull_count\": 16616,\n    \"pull_rank\": 40,\n    \"push_count\": 111156,\n    \"push_rank\": 41,\n    \"star_count\": 29471,\n    \"star_rank\": 46,\n    \"sloc\": 1883,\n    \"files\": 11,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 4386,\n    \"so_rank\": 53,\n    \"lloc\": 0\n  },\n  \"plsql\": {\n    \"dir\": \"plsql\",\n    \"name\": \"PL/SQL\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": null,\n    \"perf2\": null,\n    \"perf3\": 0,\n    \"pull_count\": 7172,\n    \"pull_rank\": 53,\n    \"push_count\": 31314,\n    \"push_rank\": 55,\n    \"star_count\": 6361,\n    \"star_rank\": 59,\n    \"sloc\": 2223,\n    \"files\": 11,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 29113,\n    \"so_rank\": 32,\n    \"lloc\": 0\n  },\n  \"powershell\": {\n    \"dir\": \"powershell\",\n    \"name\": \"PowerShell\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 624,\n    \"perf2\": 2076,\n    \"perf3\": 17,\n    \"pull_count\": 75193,\n    \"pull_rank\": 32,\n    \"push_count\": 284307,\n    \"push_rank\": 32,\n    \"star_count\": 210859,\n    \"star_rank\": 28,\n    \"sloc\": 812,\n    \"files\": 7,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 118255,\n    \"so_rank\": 19,\n    \"lloc\": 0\n  },\n  \"prolog\": {\n    \"dir\": \"prolog\",\n    \"name\": \"Prolog\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 15,\n    \"perf2\": 68,\n    \"perf3\": 648,\n    \"pull_count\": 638,\n    \"pull_rank\": 62,\n    \"push_count\": 34091,\n    \"push_rank\": 53,\n    \"star_count\": 4179,\n    \"star_rank\": 62,\n    \"sloc\": 591,\n    \"files\": 8,\n    \"author_name\": \"Nicolas Boulenguez\",\n    \"author_url\": \"https://github.com/asarhaddon\",\n    \"so_count\": 13462,\n    \"so_rank\": 39,\n    \"lloc\": 237\n  },\n  \"ps\": {\n    \"dir\": \"ps\",\n    \"name\": \"PostScript\",\n    \"syntax\": \"Stack\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 11,\n    \"perf2\": 54,\n    \"perf3\": 963,\n    \"pull_count\": 1816,\n    \"pull_rank\": 61,\n    \"push_count\": 13598,\n    \"push_rank\": 64,\n    \"star_count\": 1272,\n    \"star_rank\": 66,\n    \"sloc\": 1245,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 535,\n    \"so_rank\": 69,\n    \"lloc\": 0\n  },\n  \"purs\": {\n    \"dir\": \"purs\",\n    \"name\": \"PureScript\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 41,\n    \"perf2\": 110,\n    \"perf3\": 1758,\n    \"pull_count\": 12980,\n    \"pull_rank\": 45,\n    \"push_count\": 23297,\n    \"push_rank\": 59,\n    \"star_count\": 15237,\n    \"star_rank\": 53,\n    \"sloc\": 13,\n    \"files\": 2,\n    \"author_name\": \"mrsekut\",\n    \"author_url\": \"https://github.com/mrsekut\",\n    \"so_count\": 601,\n    \"so_rank\": 68,\n    \"lloc\": 0\n  },\n  \"python2\": {\n    \"dir\": \"python2\",\n    \"name\": \"Python2\",\n    \"syntax\": \"Python\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 3,\n    \"perf2\": 11,\n    \"perf3\": 4088,\n    \"pull_count\": 6523874,\n    \"pull_rank\": 3,\n    \"push_count\": 19048234,\n    \"push_rank\": 3,\n    \"star_count\": 12495438,\n    \"star_rank\": 3,\n    \"sloc\": 552,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 2301019,\n    \"so_rank\": 3,\n    \"lloc\": 0\n  },\n  \"python3\": {\n    \"dir\": \"python3\",\n    \"name\": \"Python3\",\n    \"syntax\": \"Python\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 4,\n    \"perf2\": 12,\n    \"perf3\": 2834,\n    \"pull_count\": 6523874,\n    \"pull_rank\": 4,\n    \"push_count\": 19048234,\n    \"push_rank\": 4,\n    \"star_count\": 12495438,\n    \"star_rank\": 4,\n    \"sloc\": 867,\n    \"files\": 7,\n    \"author_name\": \"Gavin Lewis\",\n    \"author_url\": \"https://github.com/epylar\",\n    \"so_count\": 2549011,\n    \"so_rank\": 2,\n    \"lloc\": 0\n  },\n  \"rpython\": {\n    \"dir\": \"rpython\",\n    \"name\": \"RPython\",\n    \"syntax\": \"Python\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 219999,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1004,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 62,\n    \"so_rank\": 80,\n    \"lloc\": 0\n  },\n  \"r\": {\n    \"dir\": \"r\",\n    \"name\": \"R\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 37,\n    \"perf2\": 114,\n    \"perf3\": 376,\n    \"pull_count\": 53300,\n    \"pull_rank\": 35,\n    \"push_count\": 522906,\n    \"push_rank\": 27,\n    \"star_count\": 95252,\n    \"star_rank\": 35,\n    \"sloc\": 736,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 508699,\n    \"so_rank\": 9,\n    \"lloc\": 0\n  },\n  \"racket\": {\n    \"dir\": \"racket\",\n    \"name\": \"Racket\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 4,\n    \"perf3\": 9695,\n    \"pull_count\": 2247,\n    \"pull_rank\": 60,\n    \"push_count\": 27140,\n    \"push_rank\": 56,\n    \"star_count\": 8941,\n    \"star_rank\": 58,\n    \"sloc\": 495,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 5880,\n    \"so_rank\": 50,\n    \"lloc\": 0\n  },\n  \"rexx\": {\n    \"dir\": \"rexx\",\n    \"name\": \"Rexx\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 81,\n    \"perf2\": 340,\n    \"perf3\": 121,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1237,\n    \"files\": 8,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 174,\n    \"so_rank\": 74,\n    \"lloc\": 0\n  },\n  \"ruby\": {\n    \"dir\": \"ruby\",\n    \"name\": \"Ruby\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 6,\n    \"perf3\": 7021,\n    \"pull_count\": 2750926,\n    \"pull_rank\": 10,\n    \"push_count\": 6646427,\n    \"push_rank\": 9,\n    \"star_count\": 3577810,\n    \"star_rank\": 12,\n    \"sloc\": 442,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 229218,\n    \"so_rank\": 15,\n    \"lloc\": 0\n  },\n  \"ruby.2\": {\n    \"dir\": \"ruby.2\",\n    \"name\": \"Ruby #2\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 7,\n    \"perf2\": 28,\n    \"perf3\": 1498,\n    \"pull_count\": 2750926,\n    \"pull_rank\": 11,\n    \"push_count\": 6646427,\n    \"push_rank\": 10,\n    \"star_count\": 3577810,\n    \"star_rank\": 13,\n    \"sloc\": 1249,\n    \"files\": 8,\n    \"author_name\": \"Ryan Cook\",\n    \"author_url\": \"https://github.com/cookrn\",\n    \"so_count\": 229218,\n    \"so_rank\": 16,\n    \"lloc\": 0\n  },\n  \"rust\": {\n    \"dir\": \"rust\",\n    \"name\": \"Rust\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 66511,\n    \"pull_count\": 427223,\n    \"pull_rank\": 19,\n    \"push_count\": 988782,\n    \"push_rank\": 20,\n    \"star_count\": 1016188,\n    \"star_rank\": 19,\n    \"sloc\": 1118,\n    \"files\": 7,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 42120,\n    \"so_rank\": 28,\n    \"lloc\": 212\n  },\n  \"scala\": {\n    \"dir\": \"scala\",\n    \"name\": \"Scala\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 11,\n    \"perf2\": 28,\n    \"perf3\": 83454,\n    \"pull_count\": 648787,\n    \"pull_rank\": 17,\n    \"push_count\": 1623883,\n    \"push_rank\": 17,\n    \"star_count\": 593810,\n    \"star_rank\": 20,\n    \"sloc\": 829,\n    \"files\": 7,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 112669,\n    \"so_rank\": 20,\n    \"lloc\": 0\n  },\n  \"scheme\": {\n    \"dir\": \"scheme\",\n    \"name\": \"Scheme (R7RS)\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [\n      \"chibi\",\n      \"kawa\",\n      \"gauche\",\n      \"chicken\",\n      \"sagittarius\",\n      \"cyclone\",\n      \"foment\"\n    ],\n    \"perf1\": 4,\n    \"perf2\": 16,\n    \"perf3\": 2647,\n    \"pull_count\": 3192,\n    \"pull_rank\": 58,\n    \"push_count\": 89139,\n    \"push_rank\": 43,\n    \"star_count\": 32255,\n    \"star_rank\": 45,\n    \"sloc\": 895,\n    \"files\": 8,\n    \"author_name\": \"Vasilij Schneidermann\",\n    \"author_url\": \"https://github.com/wasamasa\",\n    \"so_count\": 8168,\n    \"so_rank\": 45,\n    \"lloc\": 0\n  },\n  \"skew\": {\n    \"dir\": \"skew\",\n    \"name\": \"Skew\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 2,\n    \"perf2\": 6,\n    \"perf3\": 68779,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 704,\n    \"files\": 8,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 386,\n    \"so_rank\": 70,\n    \"lloc\": 0\n  },\n  \"sml\": {\n    \"dir\": \"sml\",\n    \"name\": \"Standard ML\",\n    \"syntax\": \"ML\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 0,\n    \"perf2\": 1,\n    \"perf3\": 42241,\n    \"pull_count\": 3494,\n    \"pull_rank\": 57,\n    \"push_count\": 15782,\n    \"push_rank\": 63,\n    \"star_count\": 3521,\n    \"star_rank\": 63,\n    \"sloc\": 553,\n    \"files\": 10,\n    \"author_name\": \"Fabian Bergström\",\n    \"author_url\": \"https://github.com/fabjan\",\n    \"so_count\": 2099,\n    \"so_rank\": 59,\n    \"lloc\": 0\n  },\n  \"swift5\": {\n    \"dir\": \"swift5\",\n    \"name\": \"Swift 5\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 5,\n    \"perf2\": 22,\n    \"perf3\": 1884,\n    \"pull_count\": 441064,\n    \"pull_rank\": 18,\n    \"push_count\": 1361391,\n    \"push_rank\": 18,\n    \"star_count\": 2778564,\n    \"star_rank\": 16,\n    \"sloc\": 1232,\n    \"files\": 11,\n    \"author_name\": \"Oleg Montak\",\n    \"author_url\": \"https://github.com/MontakOleg\",\n    \"so_count\": 343733,\n    \"so_rank\": 12,\n    \"lloc\": 0\n  },\n  \"tcl\": {\n    \"dir\": \"tcl\",\n    \"name\": \"Tcl\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 10,\n    \"perf2\": 32,\n    \"perf3\": 1057,\n    \"pull_count\": 2760,\n    \"pull_rank\": 59,\n    \"push_count\": 33537,\n    \"push_rank\": 54,\n    \"star_count\": 6233,\n    \"star_rank\": 60,\n    \"sloc\": 1083,\n    \"files\": 8,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 8074,\n    \"so_rank\": 46,\n    \"lloc\": 0\n  },\n  \"ts\": {\n    \"dir\": \"ts\",\n    \"name\": \"TypeScript\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 3,\n    \"perf3\": 61159,\n    \"pull_count\": 2152989,\n    \"pull_rank\": 12,\n    \"push_count\": 4497441,\n    \"push_rank\": 15,\n    \"star_count\": 3141436,\n    \"star_rank\": 15,\n    \"sloc\": 1244,\n    \"files\": 8,\n    \"author_name\": \"Masahiro Wakame\",\n    \"author_url\": \"https://github.com/vvakame\",\n    \"so_count\": 239371,\n    \"so_rank\": 14,\n    \"lloc\": 0\n  },\n  \"vala\": {\n    \"dir\": \"vala\",\n    \"name\": \"Vala\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 3,\n    \"perf2\": 12,\n    \"perf3\": 4062,\n    \"pull_count\": 12061,\n    \"pull_rank\": 47,\n    \"push_count\": 80233,\n    \"push_rank\": 47,\n    \"star_count\": 45712,\n    \"star_rank\": 41,\n    \"sloc\": 2248,\n    \"files\": 8,\n    \"author_name\": \"Simon Tatham\",\n    \"author_url\": \"https://github.com/sgtatham\",\n    \"so_count\": 1006,\n    \"so_rank\": 63,\n    \"lloc\": 1114\n  },\n  \"vhdl\": {\n    \"dir\": \"vhdl\",\n    \"name\": \"VHDL\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 4,\n    \"perf2\": 16,\n    \"perf3\": 2593,\n    \"pull_count\": 284,\n    \"pull_rank\": 65,\n    \"push_count\": 23377,\n    \"push_rank\": 58,\n    \"star_count\": 9567,\n    \"star_rank\": 57,\n    \"sloc\": 1925,\n    \"files\": 9,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 5811,\n    \"so_rank\": 51,\n    \"lloc\": 0\n  },\n  \"vimscript\": {\n    \"dir\": \"vimscript\",\n    \"name\": \"Vimscript\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 101,\n    \"perf2\": 436,\n    \"perf3\": 98,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": 965,\n    \"push_rank\": 69,\n    \"star_count\": 1547,\n    \"star_rank\": 64,\n    \"sloc\": 969,\n    \"files\": 10,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 41,\n    \"so_rank\": 82,\n    \"lloc\": 12\n  },\n  \"vb\": {\n    \"dir\": \"vb\",\n    \"name\": \"Visual Basic.NET\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 2,\n    \"perf2\": 3,\n    \"perf3\": 33311,\n    \"pull_count\": 22562,\n    \"pull_rank\": 38,\n    \"push_count\": 80494,\n    \"push_rank\": 46,\n    \"star_count\": 19848,\n    \"star_rank\": 50,\n    \"sloc\": 1451,\n    \"files\": 8,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 140396,\n    \"so_rank\": 18,\n    \"lloc\": 0\n  },\n  \"vbs\": {\n    \"dir\": \"vbs\",\n    \"name\": \"Visual Basic Script\",\n    \"syntax\": \"Algol\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 2716,\n    \"perf2\": 13072,\n    \"perf3\": 3,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 2109,\n    \"files\": 8,\n    \"author_name\": \"刘百超\",\n    \"author_url\": \"https://github.com/OldLiu001\",\n    \"so_count\": 18658,\n    \"so_rank\": 35,\n    \"lloc\": 0\n  },\n  \"wasm\": {\n    \"dir\": \"wasm\",\n    \"name\": \"WebAssembly\",\n    \"syntax\": \"Lisp\",\n    \"type_check\": \"Static\",\n    \"modes\": [\n      \"wace_libc\",\n      \"node\",\n      \"warpy\"\n    ],\n    \"pull_count\": 9341,\n    \"pull_rank\": 51,\n    \"push_count\": 11939,\n    \"push_rank\": 65,\n    \"star_count\": 10628,\n    \"star_rank\": 55,\n    \"sloc\": 3024,\n    \"files\": 16,\n    \"author_name\": \"Joel Martin\",\n    \"author_url\": \"https://github.com/kanaka\",\n    \"so_count\": 3002,\n    \"so_rank\": 55,\n    \"lloc\": 0\n  },\n  \"wren\": {\n    \"dir\": \"wren\",\n    \"name\": \"Wren\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 2,\n    \"perf2\": 5,\n    \"perf3\": 7236,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1,\n    \"files\": 1,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 4,\n    \"so_rank\": 87,\n    \"lloc\": 0\n  },\n  \"xslt\": {\n    \"dir\": \"xslt\",\n    \"name\": \"XSLT\",\n    \"syntax\": \"OTHER\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": null,\n    \"perf2\": null,\n    \"perf3\": 0,\n    \"pull_count\": 14834,\n    \"pull_rank\": 42,\n    \"push_count\": 83261,\n    \"push_rank\": 45,\n    \"star_count\": 22225,\n    \"star_rank\": 49,\n    \"sloc\": 132,\n    \"files\": 1,\n    \"author_name\": \"Ali MohammadPur\",\n    \"author_url\": \"https://github.com/alimpfard\",\n    \"so_count\": 38679,\n    \"so_rank\": 29,\n    \"lloc\": 0\n  },\n  \"yorick\": {\n    \"dir\": \"yorick\",\n    \"name\": \"Yorick\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Dynamic\",\n    \"modes\": [],\n    \"perf1\": 53,\n    \"perf2\": 248,\n    \"perf3\": 184,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1013,\n    \"files\": 8,\n    \"author_name\": \"Dov Murik\",\n    \"author_url\": \"https://github.com/dubek\",\n    \"so_count\": 1,\n    \"so_rank\": 90,\n    \"lloc\": 108\n  },\n  \"zig\": {\n    \"dir\": \"zig\",\n    \"name\": \"Zig\",\n    \"syntax\": \"C\",\n    \"type_check\": \"Static\",\n    \"modes\": [],\n    \"perf1\": 1,\n    \"perf2\": 2,\n    \"perf3\": 9556,\n    \"pull_count\": null,\n    \"pull_rank\": null,\n    \"push_count\": null,\n    \"push_rank\": null,\n    \"star_count\": null,\n    \"star_rank\": null,\n    \"sloc\": 1,\n    \"files\": 1,\n    \"author_name\": \"Josh Tobin\",\n    \"author_url\": \"https://github.com/rjtobin\",\n    \"so_count\": 378,\n    \"so_rank\": 71,\n    \"lloc\": 0\n  }\n}"
  },
  {
    "path": "docs/graph/base_data.yaml",
    "content": "headers:\n  - [dir           , name             , syntax , type_check , modes]\n\nlanguages:\n  - [ada           , Ada              , Algol  , Static     , []]\n  - [ada.2         , \"Ada #2\"         , Algol  , Static     , []]\n  - [awk           , GNU Awk          , C      , Dynamic    , []]\n  - [bash          , Bash 4           , OTHER  , Dynamic    , []]\n  - [basic         , BASIC            , OTHER  , Static     , [cbm, qbasic]]\n  - [bbc-basic     , BBC BASIC V      , OTHER  , Static     , []]\n  - [c             , C                , C      , Static     , []]\n  - [c.2           , \"C #2\"           , C      , Static     , []]\n  - [cpp           , C++              , C      , Static     , []]\n  - [cs            , C#               , C      , Static     , []]\n  - [chuck         , ChucK            , C      , Static     , []]\n  - [clojure       , Clojure          , Lisp   , Dynamic    , [clj, cljs]]\n  - [coffee        , CoffeeScript     , OTHER  , Dynamic    , []]\n  - [common-lisp   , Common Lisp      , Lisp   , Dynamic    , []]\n  - [crystal       , Crystal          , OTHER  , Static     , []]\n  - [d             , D                , C      , Static     , []]\n  - [dart          , Dart             , C      , Static     , []]\n  - [elixir        , Elixir           , OTHER  , Dynamic    , []]\n  - [elm           , Elm              , ML     , Static     , []]\n  - [elisp         , Emacs Lisp       , Lisp   , Dynamic    , []]\n  - [erlang        , Erlang           , OTHER  , Dynamic    , []]\n  - [es6           , ES6              , C      , Dynamic    , []]\n  - [fsharp        , F#               , ML     , Static     , []]\n  - [fennel        , \"Fennel\"         , Lisp   , Dynamic    , []]\n  - [factor        , Factor           , Stack  , Dynamic    , []]\n  - [fantom        , Fantom           , C      , Static     , []]\n  - [forth         , Forth            , Stack  , OTHER      , []]\n  - [guile         , GNU Guile        , Lisp   , Dynamic    , []]\n  - [gnu-smalltalk , GNU Smalltalk    , OTHER  , Dynamic    , []]\n  - [go            , Go               , C      , Static     , []]\n  - [groovy        , Groovy           , C      , Dynamic    , []]\n  - [haskell       , Haskell          , ML     , Static     , []]\n  - [haxe          , Haxe             , C      , Static     , [neko,python,cpp,js]]\n  - [hy            , Hy               , Lisp   , Dynamic    , []]\n  - [io            , Io               , OTHER  , Dynamic    , []]\n  - [java          , Java             , C      , Static     , []]\n  - [java-truffle  , \"Java Truffle\"   , C      , Static     , []]\n  - [js            , JavaScript       , C      , Dynamic    , []]\n  - [jq            , jq               , OTHER  , Dynamic    , []]\n  - [janet         , \"Janet\"          , Lisp   , Dynamic    , []]\n  - [julia         , Julia            , Algol  , Dynamic    , []]\n  - [kotlin        , Kotlin           , C      , Static     , []]\n  - [latex3        , LaTeX3           , Other  , Dynamic    , []]\n  - [livescript    , LiveScript       , ML     , Dynamic    , []]\n  - [logo          , Logo             , OTHER  , Dynamic    , []]\n  - [lua           , Lua              , Algol  , Dynamic    , []]\n  - [make          , GNU Make         , OTHER  , OTHER      , []]\n  - [mal           , mal itself       , Lisp   , Dynamic    , []]\n  - [matlab        , MATLAB           , Algol  , Dynamic    , []]\n  - [miniMAL       , miniMAL          , JSON   , Dynamic    , []]\n  - [nasm          , NASM             , OTHER  , OTHER      , []]\n  - [nim           , Nim              , Python , Static     , []]\n  - [objpascal     , Object Pascal    , Algol  , Static     , []]\n  - [objc          , Objective C      , C      , Static     , []]\n  - [ocaml         , OCaml            , ML     , Static     , []]\n  - [perl          , Perl             , C      , Dynamic    , []]\n  - [perl6         , Perl 6           , C      , Dynamic    , []]\n  - [php           , PHP              , C      , Dynamic    , []]\n  - [picolisp      , Picolisp         , Lisp   , Dynamic    , []]\n  - [pike          , Pike             , C      , OTHER      , []]\n  - [plpgsql       , PL/pgSQL         , Algol  , Static     , []]\n  - [plsql         , PL/SQL           , Algol  , Static     , []]\n  - [powershell    , PowerShell       , OTHER  , Dynamic    , []]\n  - [prolog        , Prolog           , OTHER  , Dynamic    , []]\n  - [ps            , PostScript       , Stack  , Dynamic    , []]\n  - [purs          , PureScript       , ML     , Static     , []]\n  - [python2       , Python2          , Python , Dynamic    , []]\n  - [python3       , Python3          , Python , Dynamic    , []]\n  - [rpython       , RPython          , Python , Static     , []]\n  - [r             , R                , C      , Dynamic    , []]\n  - [racket        , Racket           , Lisp   , Dynamic    , []]\n  - [rexx          , Rexx             , OTHER  , Dynamic    , []]\n  - [ruby          , Ruby             , OTHER  , Dynamic    , []]\n  - [ruby.2        , \"Ruby #2\"        , OTHER  , Dynamic    , []]\n  - [rust          , Rust             , C      , Static     , []]\n  - [scala         , Scala            , C      , Static     , []]\n  - [scheme        , Scheme (R7RS)    , Lisp   , Dynamic    , [chibi,kawa,gauche,chicken,sagittarius,cyclone,foment]]\n  - [skew          , Skew             , OTHER  , Static     , []]\n  - [sml           , \"Standard ML\"    , ML     , Static     , []]\n  - [swift6        , \"Swift 6\"        , C      , Static     , []]\n  - [tcl           , Tcl              , OTHER  , Dynamic    , []]\n  - [ts            , TypeScript       , C      , Static     , []]\n  - [vala          , Vala             , C      , Static     , []]\n  - [vhdl          , VHDL             , Algol  , Static     , []]\n  - [vimscript     , Vimscript        , Algol  , Dynamic    , []]\n  - [vb            , Visual Basic.NET , Algol  , Static     , []]\n  - [vbs           , Visual Basic Script , Algol  , Dynamic    , []]\n  - [wasm          , WebAssembly      , Lisp   , Static     , [wace_libc,node,warpy]]\n  - [wren          , Wren             , C      , Dynamic    , []]\n  - [xslt          , XSLT             , OTHER  , Dynamic    , []]\n  - [yorick        , Yorick           , C      , Dynamic    , []]\n  - [zig           , Zig              , C      , Static     , []]\n"
  },
  {
    "path": "docs/graph/collect_data.js",
    "content": "#!/usr/bin/env python\n\nconst { promisify } = require('util')\nconst readFile = promisify(require('fs').readFile)\nconst writeFile = promisify(require('fs').writeFile)\nconst readdir = promisify(require('fs').readdir)\nconst path = require('path')\nconst yaml = require('js-yaml')\nconst csv = require('csvtojson')\nconst request = require('request-promise-native')\nconst exec = promisify(require('child_process').exec)\n\nconst VERBOSE = process.env['VERBOSE'] || false\nconst BASE_PATH = process.env['BASE_PATH'] || 'base_data.yaml'\nconst README_PATH = process.env['README_PATH'] || '../../README.md'\nconst MAL_PATH = process.env['MAL_PATH'] || '../../'\n// Refresh this file using this Query page:\n// https://data.stackexchange.com/stackoverflow/query/edit/1013465\nconst SO_TAGS_PATH = process.env['SO_TAGS_PATH'] || 'so-tags.csv'\n\n// GitHut 2.0 Pull Requests\nconst GITHUT_PULL_URL = process.env['GITHUT_PULL_URL'] || 'https://raw.githubusercontent.com/madnight/githut/master/src/data/gh-pull-request.json'\n// GitHut 2.0 Pushes\nconst GITHUT_PUSH_URL = process.env['GITHUT_PUSH_URL'] || 'https://raw.githubusercontent.com/madnight/githut/master/src/data/gh-push-event.json'\n// GitHut 2.0 Stars\nconst GITHUT_STAR_URL = process.env['GITHUT_STAR_URL'] || 'https://raw.githubusercontent.com/madnight/githut/master/src/data/gh-star-event.json'\n\nconst ignoreLanguages = {\"Swift 2\":1, \"Swift 3\":1, \"Swift 4\":1}\n\nconst githutToNames = {\n    'Awk':          ['GNU Awk'],\n    'Ada':          ['Ada', 'Ada #2'],\n    'C':            ['C', 'C #2'],\n    'Shell':        ['Bash 4'],\n    'Java':         ['Java', 'Java Truffle'],\n    'JavaScript':   ['JavaScript', 'ES6'],\n    'Makefile':     ['GNU Make'],\n    'Matlab':       ['MATLAB'],\n    'Assembly':     ['NASM'],\n    'Pascal':       ['Object Pascal'],\n    'Objective-C':  ['Objective C'],\n    'PLpgSQL':      ['PL/pgSQL'],\n    'PLSQL':        ['PL/SQL'],\n    'Python':       ['Python2', 'Python3'],\n    'Ruby':         ['Ruby', 'Ruby #2'],\n    'Scheme':       ['Scheme (R7RS)'],\n    'Smalltalk':    ['GNU Smalltalk'],\n    'Swift':        ['Swift 5'],\n    'Vim script':   ['Vimscript'],\n    'Visual Basic': ['Visual Basic.NET'],\n}\nconst dirToSOTags = {\n    'ada.2':     ['ada'],\n    'bbc-basic': ['bbc-micro'],\n    'cpp':       ['c++', 'c++98', 'c++11', 'c++14', 'c++17'],\n    'coffee':    ['coffeescript'],\n    'crystal':   ['crystal-lang'],\n    'cs':        ['c#', 'c#-2.0', 'c#-3.0', 'c#-4.0'],\n    'c.2':       ['c'],\n    'es6':       ['ecmascript-6', 'es6-promise', 'es6-modules', 'es6-class', 'reactjs'],\n    'fsharp':    ['f#', 'f#-interactive', 'f#-data', 'f#-3.0'],\n    'factor':    ['factor-lang'],\n    'java-truffle': ['graalvm'],\n    'js':        ['javascript', 'node.js', 'jquery', 'angular'],\n    'latex3':    ['latex'],\n    'logo':      ['logo-lang'],\n    'make':      ['makefile'],\n    'nim':       ['nim-lang'],\n    'objpascal': ['delphi', 'freepascal', 'delphi-7', 'delphi-2007', 'delphi-2009', 'delphi-2010', 'delphi-xe', 'delphi-xe2', 'delphi-xe3', 'delphi-xe4', 'delphi-xe5', 'delphi-xe7'],\n    'objc':      ['objective-c'],\n    'perl6':     ['raku'],\n    'purs':      ['purescript'],\n    'python2':   ['python', 'python-2.7'],\n    'python3':   ['python', 'python-3.x'],\n    'ruby.2':    ['ruby'],\n    'swift5':    ['swift', 'swift4', 'swift5'],\n    'ts':        ['typescript', 'typescript-generics', 'typescript2.0'],\n    'vimscript': ['viml'],\n    'vb':        ['vb.net'],\n    'vbs':       ['vbscript'],\n    'wasm':      ['webassembly'],\n}\n\nconst soMapOverrides = {\n  'mal':       0,  // StackOverflow mal is something else\n  'miniMAL':   0,\n  'bbc-micro': 9,  // outside 50,000 query limit\n  'fennel':    3,  // outside 50,000 query limit\n  'janet':     3,  // outside 50,000 query limit\n  'picolisp':  8,  // outside 50,000 query limit\n  'wren':      4,  // outside 50,000 query limit\n  'yorick':    1,  // outside 50,000 query limit\n}\n\nfunction vlog(...args) {\n    if (VERBOSE) {\n        console.log(...args)\n    }\n}\n\nfunction die(code, ...args) {\n    console.error(...args)\n    process.exit(code)\n}\n\nasync function main() {\n    const logsPath = path.resolve(process.argv[2])\n    const outPath = path.resolve(process.argv[3])\n\n    vlog(`Loading base data yaml from '${BASE_PATH}`)\n    const baseYaml = yaml.safeLoad(await readFile(BASE_PATH, 'utf8'))\n    vlog(`Loading README text from '${README_PATH}`)\n    const readmeLines = (await readFile(README_PATH, 'utf8')).split(/\\n/)\n    vlog(`Downloading GitHut Pulls HTML from '${GITHUT_PULL_URL}`)\n    const githutPullText = (await request(GITHUT_PULL_URL))\n    vlog(`Downloading GitHut Pushes HTML from '${GITHUT_PUSH_URL}`)\n    const githutPushText = (await request(GITHUT_PUSH_URL))\n    vlog(`Downloading GitHut Stars HTML from '${GITHUT_STAR_URL}`)\n    const githutStarText = (await request(GITHUT_STAR_URL))\n    vlog(`Loading StackOverflow Tags CSV from '${SO_TAGS_PATH}`)\n    const soTagList = await csv().fromFile(SO_TAGS_PATH)\n    vlog(`Loading log data from '${logsPath}'`)\n    const logDirs = (await readdir(logsPath)).sort()\n    let logData = []\n    for (const d of logDirs) {\n        let dir = /IMPL=([^ ]*)/.exec(d)[1]\n        if (!dir) { console.log(\"ignoring log dir:\", d); continue }\n        let logPath = `${logsPath}/${d}`\n        const logFiles = (await readdir(logPath))\n            .filter(f => /^perf-.*\\.log/.exec(f))\n        const path = `${logPath}/${logFiles[0]}`\n        logData.push([await readFile(path, 'utf8'), path, dir])\n    }\n\n    let dirs = []\n    let names = []\n    let dataList = []\n    let dataByDir  = {}\n    let dataByName = {}\n\n    vlog(`Processing base data`)\n    for (let d of baseYaml['languages']) {\n        let data = {'dir':         d[0],\n                    'name':        d[1],\n                    'syntax':      d[2],\n                    'type_check':  d[3],\n                    'modes':       d[4],\n                    'perf1':       null,\n                    'perf2':       null,\n                    'perf3':       0,\n                    'pull_count':  null,\n                    'pull_rank':   null,\n                    'push_count':  null,\n                    'push_rank':   null,\n                    'star_count':  null,\n                    'star_rank':   null,\n                    'sloc':        0,\n                    'files':       0}\n        dirs.push(d[0])\n        names.push(d[1])\n        dataList.push(data)\n        dataByDir[d[0]]  = data\n        dataByName[d[1]] = data\n    }\n\n\n    vlog(`Processing README implementations table`)\n    const readme_re = /^\\| \\[([^\\[]*)\\].* \\| \\[([^|]*)\\]\\(([^|]*)\\)  *\\| *$/\n    for (let row of readmeLines.filter(l => /^\\| [\\[]/.exec(l))) {\n        t = readme_re.exec(row)\n        if (t) {\n            if (t[1] in ignoreLanguages) {\n              vlog(`  ${t[1]}: ignoring (in ignoreLanguages list)`)\n            } else if (t[1] in dataByName) {\n                let data = dataByName[t[1]]\n                data.author_name = t[2]\n                data.author_url  = t[3]\n            } else {\n                die(1, `README language '${t[1]}' not found in base data`)\n            }\n        } else {\n            die(1, `No match for README table row: ${row}`)\n        }\n    }\n\n\n    vlog(`Processing StackOverflow tag data`)\n    const soMap = {\n        ...soTagList\n             .reduce((m,d) => (m[d.TagName] = parseInt(d.Rate), m), {}),\n        ...soMapOverrides\n    }\n    for (let dir of dirs) {\n        if (!('so_count' in dataByDir[dir])) {\n            dataByDir[dir]['so_count'] = 0\n        }\n        let tags = dirToSOTags[dir]\n        if (!tags) {\n            if (dir in soMap) {\n                tags = [dir]\n            } else {\n                vlog(`  ${dir} not found as StackOverflow tag`)\n                tags = []\n            }\n        }\n        for (let tag of tags) {\n            if (tag in soMap) {\n                dataByDir[dir]['so_count'] += soMap[tag]\n                //vlog(`  ${dir} count: ${count}`)\n            } else {\n                die(1, `${tag} not found in soMap`)\n            }\n        }\n    }\n    let curRank = 1\n    let soSort = Object.values(dataByDir).sort((a,b) => b.so_count - a.so_count)\n    for (let data of soSort) {\n        data.so_rank = curRank\n        vlog(`  ${data.dir} so_count: ${data.so_count}, rank: ${curRank}`)\n        curRank += 1\n    }\n    const maxSORank = curRank\n\n\n    vlog(`Processing log file data`)\n    const perf_run_re = /Running:.*\\.\\.\\/tests\\/(perf[0-9])\\.mal/\n    const perf_num_re = /Elapsed time: ([0-9.]+) msecs|iters over 10 seconds: ([0-9]+)/\n    for (let [log, file, dir] of logData) {\n        const data = dataByDir[dir]\n//        if (data.perf1 !== null) {\n//            vlog(`  ${dir} already has perf data, ignoring ${file}`)\n//            continue\n//        }\n        const perfs = {}\n        const logLines = log.split(/\\n/)\n        for (let i = 0; i < logLines.length; i++) {\n            const match_run = perf_run_re.exec(logLines[i])\n            if (match_run) {\n                // Find the result line\n                let match_num = null\n                do {\n                    i += 1\n                    match_num = perf_num_re.exec(logLines[i])\n                    if (match_num) {\n                        num = parseFloat(match_num[1] || match_num[2], 10)\n                        perfs[match_run[1]] = num\n                    }\n                } while ((!match_num) && i < logLines.length)\n            }\n        }\n        if ((!data.perf3) || (perfs.perf3 > data.perf3)) {\n            data.perf1 = perfs.perf1\n            data.perf2 = perfs.perf2\n            data.perf3 = perfs.perf3\n            vlog(`  ${dir}: ${perfs.perf1}, ${perfs.perf2}, ${perfs.perf3}`)\n        } else {\n            vlog(`  ${dir}: ${perfs.perf1}, ${perfs.perf2}, ${perfs.perf3} (perf3 is worse, ignoring log ${file})`)\n        }\n    }\n\n\n    function githutProcess(textData, kind) {\n        const gMap = JSON.parse(textData)\n            .reduce((m, d) => (m[d.name] = parseInt(d.count) + (m[d.name] || 0), m), {})\n        const gdata = Object.entries(gMap)\n            .sort(([k1,v1],[k2,v2]) => v2 - v1)\n        let curRank = 1\n        for (let [gname, gcount] of gdata) {\n            const names = githutToNames[gname] || [gname]\n            for (let name of names) {\n                if (name in dataByName) {\n                    dataByName[name][kind + '_count'] = gcount\n                    dataByName[name][kind + '_rank'] = curRank\n                    vlog(`  ${dataByName[name].dir} count: ${gcount}, rank: ${curRank}`)\n                    curRank += 1\n                } else if (gname in githutToNames) {\n                    vlog(`  ignoring known GitHut language ${name} (${gname})`)\n                } else {\n                    //vlog(`  ignoring GitHut language ${name}`)\n                }\n            }\n        }\n        for (let name in dataByName) {\n          if (!dataByName[name][kind + '_count']) {\n            vlog(`  ${dataByName[name].dir} no GitHut data`)\n          }\n        }\n        return curRank;\n    }\n    vlog(`Processing GitHut Pull Request data`)\n    githutProcess(githutPullText, 'pull')\n    vlog(`Processing GitHut Push data`)\n    githutProcess(githutPushText, 'push')\n    vlog(`Processing GitHut Stars data`)\n    githutProcess(githutStarText, 'star')\n\n\n    vlog(`Gathering LOC stats`)\n    const stat_re = /SLOC=([0-9]+).*LLOC=([0-9]+).*in ([0-9]+) files/\n    process.chdir(MAL_PATH)\n    for (let data of dataList) {\n        const { stdout, stderr } = await exec(`make \"stats^${data.dir}\"`)\n        const match = stat_re.exec(stdout.split(/\\n/)[1])\n        data.sloc = parseInt(match[1], 10)\n        data.lloc = parseInt(match[2], 10)\n        data.files = parseInt(match[3], 10)\n        vlog(`  ${data.dir}: sloc: ${data.sloc}, lloc: ${data.lloc}, files: ${data.files}`)\n    }\n\n\n    vlog(`Writing full lanaguage data to ${outPath}`)\n    await writeFile(outPath, JSON.stringify(dataByDir, null, 2))\n\n    process.exit(0)\n}\n\nmain()\n"
  },
  {
    "path": "docs/graph/graph_languages.js",
    "content": "const malColors = [\n    \"#1f77b4\",\"#bf7f0e\",\"#4cb00c\",\"#b62728\",\"#9467bd\",\"#bc664b\",\"#b377c2\",\"#0fbf6f\",\"#bcbd22\",\"#17beef\",\n    \"#1f6784\",\"#8f7f0e\",\"#4c800c\",\"#862728\",\"#54678d\",\"#8c564b\",\"#8377c2\",\"#0f8f6f\",\"#8c8d22\",\"#178eef\",\n    \"#1f97d4\",\"#ff7f0e\",\"#4cf00c\",\"#f62728\",\"#c467fd\",\"#fc764b\",\"#f377c2\",\"#0fff6f\",\"#fcfd22\",\"#17feef\",\n]\n\nconst axisMap = {\n    'pull_rank':  'GH PRs',\n    'push_rank':  'GH Pushes',\n    'star_rank':  'GH Stars',\n    'so_rank':    'SO Tags',\n    'perf1':      'Perf 1',\n    'perf2':      'Perf 2',\n    'perf3':      'Perf 3',\n    'sloc':       'SLOC size',\n    'files':      'File count',\n}\nconst colorMap = {\n    'syntax': 'Syntax Style',\n    'type_check': 'Type Discipline',\n    'author_name': 'Author',\n}\nconst axisKeySet = new Set(Object.keys(axisMap))\nconst colorKeySet = new Set(['type_check', 'syntax', 'author_name'])\n\nconst perfSet = new Set(['perf1', 'perf2', 'perf3'])\nconst invertSet = new Set(['pull_rank', 'push_rank', 'star_rank', 'so_rank', 'perf1', 'perf2'])\nconst perfLogSet = new Set(['perf1', 'perf2', 'sloc', 'files'])\n\nlet cfg = {\n    ckey: 'syntax',\n    xkey: 'so_rank',\n    ykey: 'perf3',\n    skey: 'sloc',\n\n    xlog: false,\n    ylog: true,\n}\n\nlet allData\nlet graphData = []\nlet chart\n\n//\n// Util functions\n//\n\nfunction malExtent(data, key) {\n    let extent = d3.extent(Object.values(data), d => d[key])\n    // pad the bottom rank so it's not on the opposite axis line\n    if (key.endsWith('_rank')) {\n        extent[0] = 0.99 // Setting this to 1 breaks log scale render\n        extent[extent.length-1] += 1\n    }\n    // Replace 0's with 0.01 to prevent divide by zero errors\n    if (extent[0] === 0) { extent[0] = 0.0001 }\n    if (extent[extent.length-1] === 0) { extent[extent.length-1] = 0.0001 }\n    // For rankings, perf1, and perf2 reverse the Axis range\n    if (invertSet.has(key)) {\n        extent.reverse()\n    }\n    return extent\n}\n\nfunction malScale(log) {\n    return log ? d3.scale.log() : d3.scale.linear()\n}\n\nfunction malTickValues(key, log) {\n    if (log && perfSet.has(key)) {\n        return [1, 10, 100, 1000, 10000, 100000]\n    } else {\n        return null\n    }\n}\n\nfunction malCircleSize(key, min, max, val) {\n    let size = (val || 0.01) - (min - 0.01)\n    if (invertSet.has(key)) {\n        size = (max + 0.01) - size\n    }\n//    if (perfLogSet.has(key)) {\n//        size = Math.log(size)\n//    }\n//    console.log(key, max, val, size)\n    return size\n}\n\n\n//\n// UI / Axis Data / query parameters\n//\n\n// Parser query string and update cfg map with valid config options\n(function parseQuery(q) {\n    const pairs = (q[0] === '?' ? q.substr(1) : q).split('&')\n    for (const [p1, p2] of pairs.map(p => p.split('='))) {\n        let k = decodeURIComponent(p1).toLowerCase()\n        let v = p2 ? decodeURIComponent(p2) : true\n        if (v in {\"true\":1,\"1\":1,\"yes\":1}) { v = true }\n        if (v in {\"false\":1,\"0\":1,\"no\":1}) { v = false }\n        if (k in cfg && (axisKeySet.has(v) || colorKeySet.has(v))) {\n            cfg[k] = v\n        }\n        if ((new Set(['xlog', 'ylog'])).has(k) && typeof v === 'boolean') {\n            cfg[k] = v\n        }\n    }\n})(location.search)\n\n// Generate the control buttons and set the checked elements based on\n// the cfg\nfunction ctlChange(evt) {\n    if (new Set(['xlog', 'ylog']).has(evt.target.name)) {\n        cfg[evt.target.name] = evt.target.checked\n    } else {\n        cfg[evt.target.name] = evt.target.value\n    }\n    const query = Object.entries(cfg).map(([k,v]) => k + \"=\" + v).join('&')\n    history.pushState(null, '', '?' + query)\n    updateGraphData()\n}\nfor (let key of ['ckey', 'xkey', 'ykey', 'skey']) {\n    const parent = document.getElementById(key + '-controls')\n    const ctlMap = ({\n        'ckey': colorMap,\n        'xkey': Object.assign({}, axisMap, {'xlog': 'Log Scale'}),\n        'ykey': Object.assign({}, axisMap, {'ylog': 'Log Scale'}),\n        'skey': axisMap,\n    })[key]\n    for (let [val, name] of Object.entries(ctlMap)) {\n        const log = (new Set(['xlog', 'ylog']).has(val)) ? val : false\n        const ctl = document.createElement('input')\n        ctl.class = 'selects'\n        ctl.type = log ? 'checkbox' : 'radio'\n        ctl.name = log ? log : key\n        ctl.value = log ? true : val\n        if ((log && cfg[val] === true) || cfg[key] === val) {\n            ctl.checked = true\n        }\n        ctl.addEventListener('change', ctlChange)\n        parent.appendChild(ctl)\n        parent.appendChild(document.createTextNode(name))\n    }\n}\n\n//\n// Graph rendering / updating\n//\n\nfunction updateGraphData() {\n    let xMax = 0\n    let yMax = 0\n    let sMin = null\n    let sMax = null\n    const colorSet = new Set(Object.values(allData).map(d => d[cfg.ckey]))\n    const colorList = Array.from(colorSet.values())\n    // empty the graphData without recreating it\n    while (graphData.length > 0) { graphData.pop() }\n    graphData.push(...colorList.map(t => ({key: t, values: []})))\n    for (let dir of Object.keys(allData)) {\n        const impl = allData[dir]\n        if (impl[cfg.xkey] > xMax) { xMax = impl[cfg.xkey] }\n        if (impl[cfg.ykey] > yMax) { yMax = impl[cfg.ykey] }\n        if (sMin === null) { sMin = impl[cfg.skey] }\n        if (impl[cfg.skey] < sMin) { sMin = impl[cfg.skey] }\n        if (impl[cfg.skey] > sMax) { sMax = impl[cfg.skey] }\n    }\n    for (let dir of Object.keys(allData)) {\n        const impl = allData[dir]\n        // Invert size for inverted data\n        graphData[colorList.indexOf(impl[cfg.ckey])].values.push({\n            x: impl[cfg.xkey] || 0,\n            y: impl[cfg.ykey] || 0,\n            size: malCircleSize(cfg.skey, sMin, sMax, impl[cfg.skey]),\n            shape: 'circle',\n            label: impl.name,\n            impl: impl,\n        })\n    }\n\n    // Update the axes domain, scale and tick values\n    chart.xDomain(malExtent(allData, cfg.xkey))\n    chart.yDomain(malExtent(allData, cfg.ykey))\n    chart.xScale(malScale(cfg.xlog))\n    chart.yScale(malScale(cfg.ylog))\n    chart.xAxis.tickValues(malTickValues(cfg.xkey, cfg.xlog))\n    chart.yAxis.tickValues(malTickValues(cfg.ykey, cfg.ylog))\n    chart.xAxis.axisLabel(axisMap[cfg.xkey])\n    chart.yAxis.axisLabel(axisMap[cfg.ykey])\n\n    // Update the graph\n    d3.select('#mal svg')\n        .data([graphData])\n        .transition().duration(350).ease('linear')\n        .call(chart)\n\n    chart.update()\n\n    nv.utils.windowResize(chart.update)\n}\n\nnv.addGraph(function() {\n    chart = nv.models.scatterChart()\n        .showDistX(true)\n        .showDistY(true)\n        .showLabels(true)\n        .duration(300)\n        .color(malColors)\n    chart.dispatch.on('renderEnd', function() {\n        //console.log('render complete')\n    })\n    chart.dispatch.on('stateChange', function(e) {\n        nv.log('New State:', JSON.stringify(e))\n    })\n    chart.tooltip.contentGenerator(function(obj) {\n        const i = obj.point.impl\n        return '<h3>' + i.name + '</h3>' +\n            '<ul class=\"impl-data\">' +\n            '<li><b>Syntax Style</b>: ' + i.syntax +\n            '<li><b>Type Discipline</b>: ' + i.type_check +\n            '<li><b>GitHub</b>:' +\n            '  <ul>' +\n            '  <li><b>PR Count</b>: ' + (i.pull_count || 'unknown') +\n            '  <li><b>PR Rank</b>: ' + i.pull_rank +\n            '  <li><b>Push Count</b>: ' + (i.push_count || 'unknown') +\n            '  <li><b>Push Rank</b>: ' + i.push_rank +\n            '  <li><b>Star Count</b>: ' + (i.star_count || 'unknown') +\n            '  <li><b>Star Rank</b>: ' + i.star_rank +\n            '  </ul>' +\n            '<li><b>StackOverflow</b>:' +\n            '  <ul>' +\n            '  <li><b>Tag Count</b>: ' + (i.so_count || 'unknown') +\n            '  <li><b>Tag Rank</b>: ' + i.so_rank +\n            '  </ul>' +\n            '<li><br>' +\n            '<li><b>Perf 1</b>: ' + i.perf1 + ' ms<br>' +\n            '<li><b>Perf 2</b>: ' + i.perf2 + ' ms<br>' +\n            '<li><b>Perf 3</b>: ' + i.perf3 + ' iters / 10 sec<br>' +\n            '<li><b>SLOC</b>: ' + i.sloc + ' lines<br>' +\n            '<li><b>Author</b>: ' + i.author_name + '<br>' +\n            '&nbsp; &nbsp; ' + i.author_url.replace(/https?:\\/\\//, '') +\n            '</ul>'\n    })\n\n    // Load and mangle the data\n    d3.json(\"all_data.json\", function (error, data) {\n        allData = data\n\n        console.log(`Filling in missing data attributes`)\n        const dataList = Object.values(allData)\n        // leave a gap between ranked impls and those with no rank\n        const rankGap = 10\n        const maxPullRank = Math.max(...dataList.map(d => d.pull_rank))\n        const maxPushRank = Math.max(...dataList.map(d => d.push_rank))\n        const maxStarRank = Math.max(...dataList.map(d => d.star_rank))\n        const maxSORank = Math.max(...dataList.map(d => d.so_rank))\n        const maxPerf1 = dataList.reduce((a, d) => d.perf1 > a ? d.perf1 : a, 0)\n        const maxPerf2 = dataList.reduce((a, d) => d.perf2 > a ? d.perf1 : a, 0)\n        for (let d of dataList) {\n            if (d.pull_rank === null) {\n                d.pull_rank = maxPullRank + rankGap\n                console.log(`  set pull_rank to ${d.pull_rank} for ${d.dir}`)\n            }\n            if (d.push_rank === null) {\n                d.push_rank = maxPushRank + rankGap\n                console.log(`  set push_rank to ${d.push_rank} for ${d.dir}`)\n            }\n            if (d.star_rank === null) {\n                d.star_rank = maxStarRank + rankGap\n                console.log(`  set star_rank to ${d.star_rank} for ${d.dir}`)\n            }\n            if (d.so_count === 0) {\n                d.so_rank = maxSORank + rankGap\n                console.log(`  set so_rank to ${d.so_rank} for ${d.dir}`)\n            }\n            if (d.perf1 === null) {\n                d.perf1 = maxPerf1\n                console.log(`  set perf1 to ${maxPerf1} for ${d.dir}`)\n            }\n            if (d.perf2 === null) {\n                d.perf2 = maxPerf2\n                console.log(`  set perf2 to ${maxPerf2} for ${d.dir}`)\n            }\n        }\n\n        console.log(`Adjusting perf numbers to avoid 0`)\n        for (let d of dataList) {\n            if (d.perf1 === 0) { d.perf1 = 0.9 }\n            if (d.perf2 === 0) { d.perf2 = 0.9 }\n            if (d.perf3 === 0) { d.perf3 = 0.01 }\n        }\n\n        // NOTE: TODO: major hack to workaround bug with switching\n        // to/from logarithmic mode. Seems to require at least one\n        // value to be less than 1 for it to work\n        allData.rpython.perf2 = 0.9\n\n        updateGraphData()\n    })\n\n    return chart\n})\n\n"
  },
  {
    "path": "docs/graph/index.html",
    "content": "<html>\n<head>\n  <meta charset=\"utf-8\">\n  <link href=\"https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.min.css\" rel=\"stylesheet\" type=\"text/css\">\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js\" charset=\"utf-8\"></script>\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.min.js\"></script>\n\n  <style>\n    text {\n      font: 12px sans-serif;\n    }\n    svg {\n      display: block;\n    }\n    #notes {\n      border: 2px solid #202020;\n      padding: 4px;\n      margin: 2px 20px 2px 10px;\n      width: 70%;\n      position: absolute;\n      background: #ffffff;\n    }\n    .controls {\n    }\n    #toggle {\n      padding: 2px;\n      margin-right: 10px;\n    }\n    #toggle button {\n      width: 7em;\n      color: #d04444;\n    }\n    #show {\n      display: none;\n    }\n    .impl-data, .impl-data ul {\n      text-align: left !important;\n      list-style: none;\n      padding: 0;\n      margin: 5;\n    }\n    .impl-data ul {\n      margin: 0 0 0 15;\n    }\n    #title {\n      text-align: center;\n      padding: 5px;\n    }\n    #mal {\n        height: 80%;\n        width: 100%;\n    }\n    html, body, #mal, svg {\n      margin: 0px;\n      padding: 0px;\n    }\n  </style>\n</head>\n<body class='with-3d-shadow with-transitions'>\n\n<div>\n  <h2 id=\"title\">Mal Implementation Stats</h2>\n</div>\n\n<div class=\"controls\">\n  <table border=0>\n    <tr>\n      <td rowspan=4 valign=top>\n        <div id=\"toggle\">\n          <button id=\"hide\" onclick=\"hide_notes()\">Hide Notes</button>\n          <button id=\"show\" onclick=\"show_notes()\">Show Notes</button>\n          <div id=\"notes\">\n            <h3>Important Caveats:</h3>\n            <p>The data on this graph is <b>very</b> specific to <a\n               href=\"https://github.com/kanaka/mal\">Mal</a>.\n            Do not use this data to directly compare programming\n            languages.</p>\n            <ul>\n              <li><b>Bad takeaway</b>: <i>\"Language X is faster than\n                      language Y\"</i></li>\n              <li><b>Good takeway</b>: <i>\"The mal impl in\n                      language X is faster than the one\n                      in language Y for the 'perf 3' microbenchmark\"</i></li>\n            </ul>\n            <p>Here are some reasons (non-exhaustive) why this data\n            should be taken with a grain of salt:</p>\n            <ul>\n              <li>The focus of the make-a-lisp process is on learning\n                (either Lisp or the target language). The resulting\n                implementations have a common structure that is\n                intended for <b>understandability and consistency</b>\n                between implementations. They are not structured or\n                intended to have optimal runtime performance or code\n                concision.</li>\n              <li>Mal implementations are created by <b>different\n                authors</b> and the authors have varying levels of\n                experience with the target language and they often\n                created a mal implementation with the goal of learning\n                the target language.</li>\n              <li>While the overall structure of each mal\n                implementation is similar, the implementation details\n                are <b>up to the author</b>.</li>\n              <li>There are hundreds of tests that each implementation\n                must pass before being accepted into the tree.\n                However, the mal language has <b>no formal\n                specification</b> so authors make choices\n                about whether and how to handle edge cases that are\n                not covered by the tests. For example, mal\n                implementations have different levels of runtime error\n                checking.</li>\n              <li>The performance <b>benchmarks are very narrow</b> in\n                focus and these numbers should not be extrapolated\n                casually.  For example, the 'perf 3' microbenchmark\n                repeats a macro and data structure manipulation test\n                repeatedly for 10 seconds and counts the number of\n                iterations through the test.  Languages with runtime\n                optimization (e.g. JIT) tend to do particularly well\n                at this benchmark (RPython, JVM-based, etc).</li>\n            </ul>\n            <h3>Other Notes:</h3>\n            <ul>\n              <li>Syntax style and type discipline are best effort\n                and based on Wikipedia information and personal\n                experience. There are also other aspects to type\n                discipline (strong, gradual, duck, etc) that are not\n                currently included.</li>\n              <li>The GitHub information was gathered by the <a\n                href=\"https://madnight.github.io/githut/\">GitHut\n                2.0</a> project and then translated into a ordinal\n                ranking of implementations relative to each other.\n              <li>The StackOverflow information was generated\n                by a <a\n                href=\"https://data.stackexchange.com/stackoverflow/query/edit/1013465\">tag\n                count query</a> and then translated into a ordinal\n                ranking of implementations relative to each other.\n              <li>Not all languages have GitHub or StackOverflow data\n                so a gap of 10 ticks is introduced between the\n                ranked languages and the languages with no data.</li>\n            </ul>\n          </div>\n        </div>\n      </td>\n      <th align=right>Color data:</th>\n      <td id='ckey-controls'></td>\n    </tr>\n    <tr>\n      <th align=right>X-Axis data:</th>\n      <td id='xkey-controls'></td>\n    </tr>\n    <tr>\n      <th align=right>Y-Axis data:</th>\n      <td id='ykey-controls'></td>\n    </tr>\n    <tr>\n      <th align=right>Circle size:</th>\n      <td id='skey-controls'></td>\n    </tr>\n  </table>\n</div>\n\n<div id=\"mal\" class=\"chartWrap\">\n  <svg></svg>\n</div>\n\n<script>\n  function hide_notes() {\n    document.getElementById('hide').style.display = \"none\"\n    document.getElementById('show').style.display = \"inline\"\n    document.getElementById('notes').style.visibility = \"hidden\"\n  }\n  function show_notes() {\n    document.getElementById('hide').style.display = \"inline\"\n    document.getElementById('show').style.display = \"none\"\n    document.getElementById('notes').style.visibility = \"\"\n  }\n</script>\n\n<script src=\"graph_languages.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "docs/graph/package.json",
    "content": "{\n    \"name\": \"mal_graph\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Graph Mal Languages\",\n    \"dependencies\": {\n        \"js-yaml\": \"3.13.1\",\n        \"csvtojson\": \"2.0.8\",\n        \"request\": \"2.88.0\",\n        \"request-promise-native\": \"1.0.7\"\n    }\n}\n"
  },
  {
    "path": "docs/graph/so-tags.csv",
    "content": "Rate,TagName\n\"2532623\",\"javascript\"\n\"2205901\",\"python\"\n\"1919299\",\"java\"\n\"1620836\",\"c#\"\n\"1467322\",\"php\"\n\"1419124\",\"android\"\n\"1188879\",\"html\"\n\"1034208\",\"jquery\"\n\"810400\",\"c++\"\n\"806359\",\"css\"\n\"688094\",\"ios\"\n\"673281\",\"sql\"\n\"661830\",\"mysql\"\n\"508699\",\"r\"\n\"479715\",\"reactjs\"\n\"473356\",\"node.js\"\n\"417506\",\"arrays\"\n\"405875\",\"c\"\n\"374706\",\"asp.net\"\n\"361025\",\"json\"\n\"343110\",\"python-3.x\"\n\"339673\",\".net\"\n\"338275\",\"ruby-on-rails\"\n\"335747\",\"sql-server\"\n\"334941\",\"swift\"\n\"312746\",\"django\"\n\"306068\",\"angular\"\n\"292176\",\"objective-c\"\n\"288497\",\"excel\"\n\"288305\",\"pandas\"\n\"262606\",\"angularjs\"\n\"260613\",\"regex\"\n\"233158\",\"typescript\"\n\"229218\",\"ruby\"\n\"227898\",\"linux\"\n\"221724\",\"ajax\"\n\"220561\",\"iphone\"\n\"215414\",\"vba\"\n\"214973\",\"xml\"\n\"212693\",\"laravel\"\n\"211908\",\"spring\"\n\"201292\",\"asp.net-mvc\"\n\"195197\",\"database\"\n\"192135\",\"wordpress\"\n\"184749\",\"string\"\n\"178977\",\"flutter\"\n\"177876\",\"postgresql\"\n\"176089\",\"mongodb\"\n\"169898\",\"wpf\"\n\"168390\",\"windows\"\n\"159813\",\"amazon-web-services\"\n\"159792\",\"xcode\"\n\"156259\",\"bash\"\n\"153110\",\"git\"\n\"152311\",\"oracle\"\n\"149763\",\"spring-boot\"\n\"147126\",\"dataframe\"\n\"143815\",\"firebase\"\n\"142273\",\"azure\"\n\"141881\",\"list\"\n\"140679\",\"multithreading\"\n\"140396\",\"vb.net\"\n\"139125\",\"docker\"\n\"138101\",\"react-native\"\n\"124939\",\"eclipse\"\n\"121178\",\"algorithm\"\n\"118255\",\"powershell\"\n\"118073\",\"macos\"\n\"115734\",\"visual-studio\"\n\"114584\",\"numpy\"\n\"114135\",\"image\"\n\"113687\",\"forms\"\n\"112669\",\"scala\"\n\"111504\",\"function\"\n\"108146\",\"vue.js\"\n\"103062\",\"twitter-bootstrap\"\n\"102573\",\"performance\"\n\"100414\",\"selenium\"\n\"99255\",\"winforms\"\n\"96732\",\"kotlin\"\n\"96589\",\"loops\"\n\"95744\",\"express\"\n\"95222\",\"hibernate\"\n\"95118\",\"python-2.7\"\n\"95093\",\"sqlite\"\n\"94997\",\"matlab\"\n\"94667\",\"dart\"\n\"94558\",\"api\"\n\"92871\",\"shell\"\n\"92623\",\"rest\"\n\"92369\",\"apache\"\n\"91885\",\"entity-framework\"\n\"90681\",\"android-studio\"\n\"90480\",\"csv\"\n\"88966\",\"maven\"\n\"86630\",\"linq\"\n\"86273\",\"qt\"\n\"86235\",\"dictionary\"\n\"85732\",\"unit-testing\"\n\"85537\",\"facebook\"\n\"83634\",\"asp.net-core\"\n\"82905\",\"tensorflow\"\n\"82661\",\"apache-spark\"\n\"81612\",\"file\"\n\"81367\",\"swing\"\n\"79894\",\"class\"\n\"77183\",\"sorting\"\n\"77028\",\"unity-game-engine\"\n\"76975\",\"date\"\n\"76077\",\"authentication\"\n\"74257\",\"symfony\"\n\"73691\",\"go\"\n\"73460\",\"opencv\"\n\"73384\",\"t-sql\"\n\"73085\",\".htaccess\"\n\"72761\",\"google-chrome\"\n\"72579\",\"matplotlib\"\n\"72036\",\"for-loop\"\n\"71268\",\"datetime\"\n\"69528\",\"codeigniter\"\n\"68358\",\"http\"\n\"68197\",\"perl\"\n\"67699\",\"validation\"\n\"66618\",\"sockets\"\n\"66067\",\"google-maps\"\n\"65127\",\"object\"\n\"64404\",\"uitableview\"\n\"62697\",\"xaml\"\n\"62318\",\"oop\"\n\"62222\",\"if-statement\"\n\"61584\",\"cordova\"\n\"61010\",\"ubuntu\"\n\"60154\",\"visual-studio-code\"\n\"59886\",\"web-services\"\n\"59490\",\"email\"\n\"59129\",\"android-layout\"\n\"58654\",\"spring-mvc\"\n\"58605\",\"elasticsearch\"\n\"58402\",\"github\"\n\"58157\",\"kubernetes\"\n\"57861\",\"selenium-webdriver\"\n\"57856\",\"ms-access\"\n\"57637\",\"parsing\"\n\"57566\",\"user-interface\"\n\"56993\",\"ggplot2\"\n\"56850\",\"pointers\"\n\"56741\",\"c++11\"\n\"56583\",\"security\"\n\"56146\",\"machine-learning\"\n\"55938\",\"ruby-on-rails-3\"\n\"55743\",\"flask\"\n\"55663\",\"google-sheets\"\n\"55590\",\"nginx\"\n\"55376\",\"templates\"\n\"55020\",\"google-apps-script\"\n\"53957\",\"variables\"\n\"53780\",\"exception\"\n\"53745\",\"sql-server-2008\"\n\"52689\",\"listview\"\n\"52660\",\"debugging\"\n\"52596\",\"gradle\"\n\"52563\",\"tkinter\"\n\"52193\",\"jpa\"\n\"52128\",\"delphi\"\n\"51935\",\"jsp\"\n\"51895\",\"asynchronous\"\n\"51705\",\"pdf\"\n\"51449\",\"haskell\"\n\"51406\",\"web-scraping\"\n\"51354\",\"ssl\"\n\"50962\",\"amazon-s3\"\n\"50858\",\"jenkins\"\n\"50848\",\"xamarin\"\n\"50769\",\"wcf\"\n\"50607\",\"testing\"\n\"50401\",\"batch-file\"\n\"50383\",\"google-cloud-platform\"\n\"50243\",\"npm\"\n\"50003\",\"generics\"\n\"48643\",\"ionic-framework\"\n\"47653\",\"unix\"\n\"47274\",\"recursion\"\n\"47116\",\"google-app-engine\"\n\"46877\",\"mongoose\"\n\"46559\",\"visual-studio-2010\"\n\"45686\",\"android-fragments\"\n\"45564\",\".net-core\"\n\"44881\",\"animation\"\n\"44709\",\"assembly\"\n\"44412\",\"hadoop\"\n\"44400\",\"session\"\n\"44388\",\"math\"\n\"44218\",\"web\"\n\"44195\",\"svg\"\n\"43951\",\"curl\"\n\"43899\",\"intellij-idea\"\n\"43848\",\"django-models\"\n\"43721\",\"laravel-5\"\n\"43703\",\"join\"\n\"43554\",\"heroku\"\n\"43522\",\"url\"\n\"43319\",\"http-redirect\"\n\"43224\",\"winapi\"\n\"43141\",\"tomcat\"\n\"42960\",\"next.js\"\n\"42849\",\"google-cloud-firestore\"\n\"42781\",\"webpack\"\n\"42738\",\"inheritance\"\n\"42500\",\"keras\"\n\"42368\",\"image-processing\"\n\"42345\",\"asp.net-mvc-4\"\n\"42120\",\"rust\"\n\"41867\",\"gcc\"\n\"41752\",\"dom\"\n\"41726\",\"logging\"\n\"41261\",\"matrix\"\n\"41120\",\"actionscript-3\"\n\"40884\",\"pyspark\"\n\"40821\",\"post\"\n\"40748\",\"button\"\n\"40153\",\"firebase-realtime-database\"\n\"39887\",\"swiftui\"\n\"39852\",\"optimization\"\n\"39720\",\"jquery-ui\"\n\"39623\",\"cocoa\"\n\"39505\",\"xpath\"\n\"39495\",\"iis\"\n\"39352\",\"d3.js\"\n\"38979\",\"internet-explorer\"\n\"38955\",\"firefox\"\n\"38742\",\"javafx\"\n\"38679\",\"xslt\"\n\"38446\",\"caching\"\n\"38331\",\"asp.net-mvc-3\"\n\"38276\",\"select\"\n\"38253\",\"networking\"\n\"38109\",\"opengl\"\n\"38082\",\"asp.net-web-api\"\n\"38082\",\"events\"\n\"37903\",\"plot\"\n\"37508\",\"magento\"\n\"37311\",\"search\"\n\"37299\",\"dplyr\"\n\"37251\",\"encryption\"\n\"37147\",\"stored-procedures\"\n\"37088\",\"amazon-ec2\"\n\"36756\",\"ruby-on-rails-4\"\n\"36412\",\"memory\"\n\"36155\",\"canvas\"\n\"36093\",\"audio\"\n\"35780\",\"jsf\"\n\"35636\",\"multidimensional-array\"\n\"35548\",\"random\"\n\"35433\",\"redux\"\n\"35346\",\"vector\"\n\"35290\",\"cookies\"\n\"35170\",\"input\"\n\"35129\",\"facebook-graph-api\"\n\"34893\",\"flash\"\n\"34715\",\"xamarin.forms\"\n\"34621\",\"ipad\"\n\"34594\",\"arraylist\"\n\"34507\",\"cocoa-touch\"\n\"34469\",\"indexing\"\n\"34109\",\"video\"\n\"34062\",\"data-structures\"\n\"33597\",\"model-view-controller\"\n\"33489\",\"serialization\"\n\"33425\",\"jdbc\"\n\"33342\",\"apache-kafka\"\n\"33314\",\"razor\"\n\"33298\",\"routes\"\n\"33188\",\"servlets\"\n\"33148\",\"mod-rewrite\"\n\"33144\",\"awk\"\n\"33127\",\"woocommerce\"\n\"32801\",\"iframe\"\n\"32798\",\"beautifulsoup\"\n\"32406\",\"filter\"\n\"32313\",\"docker-compose\"\n\"32245\",\"azure-devops\"\n\"32167\",\"design-patterns\"\n\"32117\",\"excel-formula\"\n\"32115\",\"aws-lambda\"\n\"31946\",\"text\"\n\"31942\",\"django-rest-framework\"\n\"31737\",\"visual-c++\"\n\"31670\",\"cakephp\"\n\"31153\",\"mobile\"\n\"30937\",\"android-intent\"\n\"30841\",\"react-hooks\"\n\"30662\",\"struct\"\n\"30474\",\"methods\"\n\"30299\",\"mvvm\"\n\"30295\",\"groovy\"\n\"30228\",\"ssh\"\n\"30116\",\"lambda\"\n\"29981\",\"ecmascript-6\"\n\"29973\",\"checkbox\"\n\"29899\",\"time\"\n\"29874\",\"grails\"\n\"29828\",\"google-chrome-extension\"\n\"29703\",\"installation\"\n\"29535\",\"sharepoint\"\n\"29355\",\"jakarta-ee\"\n\"29327\",\"android-recyclerview\"\n\"29215\",\"core-data\"\n\"29158\",\"shiny\"\n\"29113\",\"plsql\"\n\"29097\",\"spring-security\"\n\"29086\",\"meteor\"\n\"29045\",\"android-activity\"\n\"28983\",\"cmake\"\n\"28898\",\"types\"\n\"28882\",\"sed\"\n\"28851\",\"bootstrap-4\"\n\"28751\",\"graph\"\n\"28715\",\"activerecord\"\n\"28655\",\"websocket\"\n\"28378\",\"replace\"\n\"28317\",\"scikit-learn\"\n\"28233\",\"file-upload\"\n\"28157\",\"vim\"\n\"28085\",\"group-by\"\n\"28082\",\"junit\"\n\"27930\",\"boost\"\n\"27855\",\"deep-learning\"\n\"27746\",\"import\"\n\"27625\",\"sass\"\n\"27603\",\"memory-management\"\n\"27481\",\"error-handling\"\n\"27403\",\"async-await\"\n\"27362\",\"dynamic\"\n\"27319\",\"eloquent\"\n\"27290\",\"soap\"\n\"27145\",\"silverlight\"\n\"26975\",\"charts\"\n\"26939\",\"layout\"\n\"26922\",\"apache-spark-sql\"\n\"26907\",\"dependency-injection\"\n\"26812\",\"browser\"\n\"26796\",\"gridview\"\n\"26730\",\"svn\"\n\"26706\",\"deployment\"\n\"26489\",\"while-loop\"\n\"26409\",\"vuejs2\"\n\"26212\",\"google-bigquery\"\n\"26147\",\"highcharts\"\n\"26097\",\"dll\"\n\"26087\",\"ffmpeg\"\n\"26085\",\"view\"\n\"25796\",\"foreach\"\n\"25795\",\"c#-4.0\"\n\"25732\",\"cmd\"\n\"25696\",\"plugins\"\n\"25626\",\"reporting-services\"\n\"25624\",\"redis\"\n\"25601\",\"makefile\"\n\"25243\",\"server\"\n\"25240\",\"merge\"\n\"25189\",\"https\"\n\"25187\",\"unicode\"\n\"25175\",\"jupyter-notebook\"\n\"25126\",\"google-maps-api-3\"\n\"25124\",\"reflection\"\n\"25120\",\"twitter\"\n\"24884\",\"extjs\"\n\"24715\",\"axios\"\n\"24643\",\"mysqli\"\n\"24572\",\"oauth-2.0\"\n\"24500\",\"terminal\"\n\"24484\",\"split\"\n\"24389\",\"django-views\"\n\"24369\",\"encoding\"\n\"24319\",\"pytorch\"\n\"24260\",\"pip\"\n\"24147\",\"netbeans\"\n\"24111\",\"collections\"\n\"24110\",\"database-design\"\n\"24056\",\"hash\"\n\"23985\",\"ember.js\"\n\"23913\",\"data-binding\"\n\"23902\",\"automation\"\n\"23897\",\"pdo\"\n\"23828\",\"tcp\"\n\"23821\",\"apache-flex\"\n\"23768\",\"build\"\n\"23694\",\"sqlalchemy\"\n\"23492\",\"command-line\"\n\"23360\",\"printing\"\n\"23255\",\"spring-data-jpa\"\n\"23236\",\"react-redux\"\n\"23233\",\"java-8\"\n\"23164\",\"service\"\n\"23115\",\"jestjs\"\n\"23107\",\"concurrency\"\n\"23088\",\"html-table\"\n\"22993\",\"neo4j\"\n\"22894\",\"entity-framework-core\"\n\"22893\",\"visual-studio-2012\"\n\"22876\",\"ansible\"\n\"22876\",\"parameters\"\n\"22832\",\"lua\"\n\"22792\",\"module\"\n\"22777\",\"material-ui\"\n\"22716\",\"promise\"\n\"22676\",\"enums\"\n\"22596\",\"webview\"\n\"22543\",\"outlook\"\n\"22539\",\"web-applications\"\n\"22521\",\"flexbox\"\n\"22516\",\"jquery-mobile\"\n\"22487\",\"uwp\"\n\"22399\",\"firebase-authentication\"\n\"22371\",\"utf-8\"\n\"22277\",\"datatable\"\n\"22118\",\"python-requests\"\n\"22015\",\"drop-down-menu\"\n\"21932\",\"scroll\"\n\"21925\",\"colors\"\n\"21920\",\"hive\"\n\"21854\",\"tfs\"\n\"21829\",\"parallel-processing\"\n\"21682\",\"count\"\n\"21660\",\"scipy\"\n\"21612\",\"syntax\"\n\"21456\",\"twitter-bootstrap-3\"\n\"21453\",\"ms-word\"\n\"21371\",\"google-analytics\"\n\"21315\",\"ssis\"\n\"21196\",\"fonts\"\n\"21176\",\"three.js\"\n\"21140\",\"file-io\"\n\"21138\",\"constructor\"\n\"21125\",\"graphql\"\n\"21096\",\"paypal\"\n\"21051\",\"rxjs\"\n\"20930\",\"discord\"\n\"20919\",\"cassandra\"\n\"20914\",\"socket.io\"\n\"20791\",\"gwt\"\n\"20746\",\"datatables\"\n\"20707\",\"graphics\"\n\"20608\",\"compiler-errors\"\n\"20605\",\"nlp\"\n\"20597\",\"backbone.js\"\n\"20587\",\"solr\"\n\"20566\",\"url-rewriting\"\n\"20554\",\"react-router\"\n\"20492\",\"powerbi\"\n\"20475\",\"memory-leaks\"\n\"20429\",\"datagridview\"\n\"20363\",\"oracle11g\"\n\"20360\",\"drupal\"\n\"20357\",\"zend-framework\"\n\"20343\",\"oauth\"\n\"20266\",\"knockout.js\"\n\"20213\",\"neural-network\"\n\"20200\",\"django-forms\"\n\"20112\",\"interface\"\n\"20037\",\"triggers\"\n\"20002\",\"google-api\"\n\"19997\",\"casting\"\n\"19947\",\"linked-list\"\n\"19904\",\"angular-material\"\n\"19869\",\"terraform\"\n\"19846\",\"jmeter\"\n\"19731\",\"proxy\"\n\"19689\",\"django-templates\"\n\"19689\",\"timer\"\n\"19678\",\"path\"\n\"19621\",\"parse-platform\"\n\"19611\",\"visual-studio-2015\"\n\"19589\",\"windows-phone-7\"\n\"19583\",\"directory\"\n\"19560\",\"cron\"\n\"19555\",\"orm\"\n\"19451\",\"arduino\"\n\"19441\",\"push-notification\"\n\"19367\",\"conditional-statements\"\n\"19343\",\"primefaces\"\n\"19147\",\"functional-programming\"\n\"19084\",\"model\"\n\"18955\",\"jar\"\n\"18858\",\"xamarin.android\"\n\"18763\",\"hyperlink\"\n\"18727\",\"visual-studio-2013\"\n\"18709\",\"uiview\"\n\"18658\",\"vbscript\"\n\"18496\",\"download\"\n\"18428\",\"swift3\"\n\"18418\",\"gitlab\"\n\"18384\",\"google-cloud-functions\"\n\"18381\",\"azure-active-directory\"\n\"18353\",\"sql-server-2005\"\n\"18316\",\"process\"\n\"18265\",\"jwt\"\n\"18263\",\"rspec\"\n\"18208\",\"properties\"\n\"18196\",\"configuration\"\n\"18186\",\"windows-phone-8\"\n\"18182\",\"callback\"\n\"18166\",\"combobox\"\n\"18085\",\"pygame\"\n\"17916\",\"safari\"\n\"17860\",\"scrapy\"\n\"17827\",\"permissions\"\n\"17799\",\"pagination\"\n\"17769\",\"scripting\"\n\"17768\",\"linux-kernel\"\n\"17767\",\"emacs\"\n\"17736\",\"raspberry-pi\"\n\"17703\",\"clojure\"\n\"17652\",\"scope\"\n\"17620\",\"io\"\n\"17543\",\"angularjs-directive\"\n\"17521\",\"nhibernate\"\n\"17511\",\"mongodb-query\"\n\"17477\",\"responsive-design\"\n\"17468\",\"x86\"\n\"17453\",\"request\"\n\"17420\",\"compilation\"\n\"17342\",\"bluetooth\"\n\"17328\",\"dns\"\n\"17317\",\"binding\"\n\"17293\",\"reference\"\n\"17290\",\"playframework\"\n\"17286\",\"discord.js\"\n\"17275\",\"3d\"\n\"17237\",\"architecture\"\n\"17234\",\"doctrine-orm\"\n\"17231\",\"version-control\"\n\"17202\",\"pyqt\"\n\"17141\",\"get\"\n\"17113\",\"package\"\n\"17102\",\"sql-server-2012\"\n\"17039\",\"pycharm\"\n\"17029\",\"rubygems\"\n\"17019\",\"f#\"\n\"16869\",\"autocomplete\"\n\"16840\",\"kendo-ui\"\n\"16835\",\"datepicker\"\n\"16798\",\"tree\"\n\"16785\",\"azure-functions\"\n\"16763\",\"yii\"\n\"16718\",\"openssl\"\n\"16692\",\"controller\"\n\"16687\",\"jackson\"\n\"16655\",\"xamarin.ios\"\n\"16646\",\"expo\"\n\"16584\",\"grep\"\n\"16573\",\"nested\"\n\"16481\",\"static\"\n\"16415\",\"statistics\"\n\"16359\",\"datagrid\"\n\"16350\",\"null\"\n\"16334\",\"transactions\"\n\"16323\",\"active-directory\"\n\"16320\",\"phpmyadmin\"\n\"16310\",\"uiviewcontroller\"\n\"16227\",\"webforms\"\n\"16160\",\"discord.py\"\n\"16140\",\"notifications\"\n\"16135\",\"dockerfile\"\n\"16080\",\"sas\"\n\"16008\",\"youtube\"\n\"15991\",\"nullpointerexception\"\n\"15988\",\"duplicates\"\n\"15937\",\"mocking\"\n\"15869\",\"computer-vision\"\n\"15868\",\"menu\"\n\"15721\",\"bitmap\"\n\"15719\",\"yaml\"\n\"15642\",\"asp.net-mvc-5\"\n\"15629\",\"visual-studio-2008\"\n\"15626\",\"sum\"\n\"15614\",\"jsf-2\"\n\"15562\",\"stream\"\n\"15550\",\"yii2\"\n\"15526\",\"android-listview\"\n\"15502\",\"time-series\"\n\"15484\",\"electron\"\n\"15481\",\"stl\"\n\"15445\",\"css-selectors\"\n\"15399\",\"ant\"\n\"15339\",\"floating-point\"\n\"15297\",\"hashmap\"\n\"15279\",\"character-encoding\"\n\"15245\",\"frontend\"\n\"15235\",\"cryptography\"\n\"15198\",\"jboss\"\n\"15193\",\"msbuild\"\n\"15178\",\"sdk\"\n\"15113\",\"google-drive-api\"\n\"15082\",\"joomla\"\n\"15065\",\"selenium-chromedriver\"\n\"15026\",\"devise\"\n\"14983\",\"anaconda\"\n\"14965\",\"asp.net-core-mvc\"\n\"14950\",\"navigation\"\n\"14886\",\"background\"\n\"14842\",\"binary\"\n\"14818\",\"camera\"\n\"14809\",\"pyqt5\"\n\"14772\",\"linq-to-sql\"\n\"14745\",\"multiprocessing\"\n\"14677\",\"onclick\"\n\"14675\",\"cors\"\n\"14675\",\"ios7\"\n\"14674\",\"blazor\"\n\"14667\",\"iterator\"\n\"14548\",\"cuda\"\n\"14536\",\"plotly\"\n\"14516\",\"mariadb\"\n\"14508\",\"android-asynctask\"\n\"14456\",\"rabbitmq\"\n\"14417\",\"laravel-4\"\n\"14377\",\"tabs\"\n\"14369\",\"uicollectionview\"\n\"14368\",\"insert\"\n\"14250\",\"amazon-dynamodb\"\n\"14195\",\"linker\"\n\"14193\",\"upload\"\n\"14190\",\"coldfusion\"\n\"14181\",\"environment-variables\"\n\"14171\",\"xsd\"\n\"14170\",\"console\"\n\"14152\",\"ftp\"\n\"14108\",\"textview\"\n\"14052\",\"continuous-integration\"\n\"14044\",\"opengl-es\"\n\"14031\",\"microsoft-graph-api\"\n\"13920\",\"xml-parsing\"\n\"13907\",\"localization\"\n\"13899\",\"operating-system\"\n\"13866\",\"mockito\"\n\"13857\",\"formatting\"\n\"13832\",\"json.net\"\n\"13811\",\"kivy\"\n\"13769\",\"macros\"\n\"13747\",\"type-conversion\"\n\"13728\",\"calendar\"\n\"13719\",\"data.table\"\n\"13710\",\"timestamp\"\n\"13639\",\"integer\"\n\"13603\",\"vuejs3\"\n\"13598\",\"segmentation-fault\"\n\"13578\",\"android-ndk\"\n\"13462\",\"prolog\"\n\"13454\",\"drag-and-drop\"\n\"13406\",\"char\"\n\"13398\",\"android-jetpack-compose\"\n\"13386\",\"jasmine\"\n\"13384\",\"crash\"\n\"13288\",\"automated-tests\"\n\"13253\",\"itext\"\n\"13239\",\"header\"\n\"13215\",\"sprite-kit\"\n\"13211\",\"dependencies\"\n\"13181\",\"geometry\"\n\"13174\",\"nosql\"\n\"13173\",\"android-gradle-plugin\"\n\"13164\",\"mfc\"\n\"13160\",\"attributes\"\n\"13126\",\"fortran\"\n\"13087\",\"format\"\n\"13082\",\"nuxt.js\"\n\"13080\",\"firebase-cloud-messaging\"\n\"13024\",\"jquery-plugins\"\n\"12973\",\"leaflet\"\n\"12907\",\"flutter-layout\"\n\"12892\",\"db2\"\n\"12890\",\"jenkins-pipeline\"\n\"12865\",\"event-handling\"\n\"12842\",\"annotations\"\n\"12841\",\"odoo\"\n\"12825\",\"nestjs\"\n\"12762\",\"keyboard\"\n\"12762\",\"postman\"\n\"12754\",\"julia\"\n\"12732\",\"textbox\"\n\"12688\",\"visual-studio-2017\"\n\"12684\",\"gulp\"\n\"12660\",\"libgdx\"\n\"12593\",\"arm\"\n\"12590\",\"crystal-reports\"\n\"12590\",\"xampp\"\n\"12564\",\"synchronization\"\n\"12550\",\"dom-events\"\n\"12515\",\"uiscrollview\"\n\"12491\",\"timezone\"\n\"12474\",\"wso2\"\n\"12474\",\"azure-pipelines\"\n\"12472\",\"sequelize.js\"\n\"12465\",\"aggregation-framework\"\n\"12465\",\"swagger\"\n\"12465\",\"android-emulator\"\n\"12461\",\"namespaces\"\n\"12403\",\"stripe-payments\"\n\"12400\",\"centos\"\n\"12391\",\"azure-web-app-service\"\n\"12382\",\"jvm\"\n\"12382\",\"chart.js\"\n\"12368\",\"geolocation\"\n\"12368\",\"webdriver\"\n\"12353\",\"com\"\n\"12346\",\"subprocess\"\n\"12320\",\"uikit\"\n\"12258\",\"html5-canvas\"\n\"12257\",\"dialog\"\n\"12217\",\"garbage-collection\"\n\"12214\",\"widget\"\n\"12206\",\"numbers\"\n\"12190\",\"windows-10\"\n\"12177\",\"concatenation\"\n\"12166\",\"mapreduce\"\n\"12151\",\"sql-update\"\n\"12139\",\"ionic2\"\n\"12114\",\"set\"\n\"12096\",\"android-edittext\"\n\"12087\",\"tuples\"\n\"12077\",\"rotation\"\n\"12070\",\"spring-data\"\n\"12068\",\"modal-dialog\"\n\"12068\",\"qml\"\n\"12065\",\"smtp\"\n\"12062\",\"google-sheets-formula\"\n\"12039\",\"radio-button\"\n\"12034\",\"doctrine\"\n\"12033\",\"http-headers\"\n\"11995\",\"grid\"\n\"11993\",\"xmlhttprequest\"\n\"11979\",\"sonarqube\"\n\"11975\",\"lucene\"\n\"11921\",\"nuget\"\n\"11869\",\"java-stream\"\n\"11842\",\"listbox\"\n\"11828\",\"internationalization\"\n\"11813\",\"components\"\n\"11808\",\"initialization\"\n\"11806\",\"switch-statement\"\n\"11797\",\"apache-camel\"\n\"11793\",\"google-play\"\n\"11782\",\"snowflake-cloud-data-platform\"\n\"11777\",\"boolean\"\n\"11726\",\"ios5\"\n\"11724\",\"ldap\"\n\"11716\",\"serial-port\"\n\"11660\",\"return\"\n\"11640\",\"eclipse-plugin\"\n\"11635\",\"youtube-api\"\n\"11613\",\"frameworks\"\n\"11595\",\"pivot\"\n\"11594\",\"tags\"\n\"11540\",\"gdb\"\n\"11509\",\"latex\"\n\"11464\",\"asp-classic\"\n\"11447\",\"dataset\"\n\"11435\",\"containers\"\n\"11433\",\"compiler-construction\"\n\"11413\",\"label\"\n\"11409\",\"subquery\"\n\"11408\",\"foreign-keys\"\n\"11404\",\"network-programming\"\n\"11390\",\"uinavigationcontroller\"\n\"11362\",\"delegates\"\n\"11356\",\"copy\"\n\"11355\",\"struts2\"\n\"11319\",\"protractor\"\n\"11298\",\"sql-server-2008-r2\"\n\"11290\",\"google-cloud-storage\"\n\"11284\",\"base64\"\n\"11275\",\"uibutton\"\n\"11254\",\"find\"\n\"11251\",\"migration\"\n\"11248\",\"queue\"\n\"11184\",\"append\"\n\"11182\",\"arguments\"\n\"11177\",\"composer-php\"\n\"11156\",\"jaxb\"\n\"11151\",\"c++17\"\n\"11133\",\"zip\"\n\"11083\",\"stack\"\n\"11070\",\"cucumber\"\n\"11065\",\"autolayout\"\n\"11029\",\"embedded\"\n\"10979\",\"entity-framework-6\"\n\"10969\",\"ide\"\n\"10966\",\"popup\"\n\"10959\",\"windows-7\"\n\"10920\",\"github-actions\"\n\"10915\",\"iteration\"\n\"10845\",\"vb6\"\n\"10837\",\"r-markdown\"\n\"10835\",\"jqgrid\"\n\"10801\",\"ssl-certificate\"\n\"10801\",\"gmail\"\n\"10793\",\"hover\"\n\"10769\",\"android-viewpager\"\n\"10756\",\"airflow\"\n\"10753\",\"command\"\n\"10732\",\"passwords\"\n\"10713\",\"udp\"\n\"10695\",\"g++\"\n\"10693\",\"range\"\n\"10685\",\"vue-component\"\n\"10682\",\"uiwebview\"\n\"10644\",\"ios4\"\n\"10627\",\"twig\"\n\"10612\",\"uiimageview\"\n\"10608\",\"salesforce\"\n\"10607\",\"conv-neural-network\"\n\"10543\",\"clang\"\n\"10534\",\"authorization\"\n\"10523\",\"local-storage\"\n\"10519\",\"twilio\"\n\"10473\",\"bots\"\n\"10468\",\"pytest\"\n\"10454\",\"angular-ui-router\"\n\"10415\",\"jersey\"\n\"10388\",\"wix\"\n\"10369\",\"constants\"\n\"10368\",\"polymorphism\"\n\"10363\",\"ionic3\"\n\"10309\",\"gps\"\n\"10307\",\"user-controls\"\n\"10296\",\"connection\"\n\"10272\",\"debian\"\n\"10271\",\"time-complexity\"\n\"10264\",\"compare\"\n\"10263\",\"windows-8\"\n\"10237\",\"django-admin\"\n\"10232\",\"localhost\"\n\"10200\",\"slider\"\n\"10187\",\"google-oauth\"\n\"10184\",\"tidyverse\"\n\"10163\",\"cocos2d-iphone\"\n\"10130\",\"python-imaging-library\"\n\"10125\",\"tailwind-css\"\n\"10104\",\"admob\"\n\"10098\",\"ado.net\"\n\"10058\",\"certificate\"\n\"10047\",\"phpunit\"\n\"10031\",\"save\"\n\"10016\",\"azure-sql-database\"\n\"9999\",\"mono\"\n\"9977\",\"jframe\"\n\"9971\",\"sbt\"\n\"9959\",\"pipe\"\n\"9934\",\"cypress\"\n\"9918\",\"fetch\"\n\"9879\",\"cypher\"\n\"9877\",\"output\"\n\"9866\",\"fullcalendar\"\n\"9861\",\"mapping\"\n\"9860\",\"imageview\"\n\"9851\",\"runtime-error\"\n\"9842\",\"timeout\"\n\"9832\",\"apache-poi\"\n\"9830\",\"gson\"\n\"9798\",\"include\"\n\"9786\",\"java-native-interface\"\n\"9777\",\"babeljs\"\n\"9742\",\"coffeescript\"\n\"9741\",\"hex\"\n\"9740\",\"drupal-7\"\n\"9728\",\"seaborn\"\n\"9710\",\"signalr\"\n\"9703\",\"jinja2\"\n\"9697\",\"substring\"\n\"9690\",\"web-crawler\"\n\"9675\",\"bluetooth-lowenergy\"\n\"9674\",\"erlang\"\n\"9665\",\"typo3\"\n\"9663\",\"icons\"\n\"9653\",\"observable\"\n\"9647\",\"command-line-interface\"\n\"9647\",\"odbc\"\n\"9646\",\"filesystems\"\n\"9633\",\"location\"\n\"9633\",\"int\"\n\"9623\",\"cocoapods\"\n\"9616\",\"export\"\n\"9607\",\"log4j\"\n\"9601\",\"elixir\"\n\"9581\",\"syntax-error\"\n\"9576\",\"printf\"\n\"9567\",\"window\"\n\"9560\",\"regression\"\n\"9532\",\"dax\"\n\"9529\",\"treeview\"\n\"9506\",\"telerik\"\n\"9505\",\"key\"\n\"9501\",\"storyboard\"\n\"9492\",\"maps\"\n\"9490\",\"realm\"\n\"9479\",\"thread-safety\"\n\"9467\",\"azure-data-factory\"\n\"9460\",\"iis-7\"\n\"9440\",\"logic\"\n\"9428\",\"build.gradle\"\n\"9418\",\"ruby-on-rails-5\"\n\"9412\",\"botframework\"\n\"9411\",\"kernel\"\n\"9375\",\"click\"\n\"9375\",\"in-app-purchase\"\n\"9351\",\"wordpress-theming\"\n\"9345\",\"asp.net-core-webapi\"\n\"9345\",\"amazon-elastic-beanstalk\"\n\"9339\",\"microservices\"\n\"9338\",\"imagemagick\"\n\"9335\",\"jsx\"\n\"9330\",\"resources\"\n\"9317\",\"compression\"\n\"9310\",\"malloc\"\n\"9299\",\"thymeleaf\"\n\"9296\",\"ip\"\n\"9282\",\"ios8\"\n\"9271\",\"ckeditor\"\n\"9271\",\"wsdl\"\n\"9266\",\"vuetify.js\"\n\"9256\",\"position\"\n\"9256\",\"resize\"\n\"9247\",\"uiimage\"\n\"9242\",\"cloud\"\n\"9239\",\"state\"\n\"9215\",\"dojo\"\n\"9183\",\"repository\"\n\"9169\",\"webrtc\"\n\"9152\",\"gpu\"\n\"9131\",\"where-clause\"\n\"9127\",\"celery\"\n\"9106\",\"actionscript\"\n\"9103\",\"office365\"\n\"9076\",\"cross-browser\"\n\"9073\",\"max\"\n\"9070\",\"asp.net-identity\"\n\"9067\",\"angularjs-ng-repeat\"\n\"9060\",\"gruntjs\"\n\"9035\",\"azure-blob-storage\"\n\"9029\",\"windows-services\"\n\"9028\",\"escaping\"\n\"9008\",\"closures\"\n\"9008\",\"jquery-selectors\"\n\"9004\",\"google-visualization\"\n\"8990\",\"shopify\"\n\"8979\",\"pthreads\"\n\"8974\",\"markdown\"\n\"8962\",\"constraints\"\n\"8953\",\"windows-installer\"\n\"8940\",\"angularjs-scope\"\n\"8933\",\"pattern-matching\"\n\"8921\",\"artificial-intelligence\"\n\"8920\",\"google-chrome-devtools\"\n\"8896\",\"locking\"\n\"8892\",\"android-actionbar\"\n\"8873\",\"styles\"\n\"8866\",\"global-variables\"\n\"8863\",\"backend\"\n\"8859\",\"swift2\"\n\"8839\",\"applescript\"\n\"8820\",\"try-catch\"\n\"8815\",\".net-4.0\"\n\"8800\",\"many-to-many\"\n\"8799\",\"match\"\n\"8767\",\"gitlab-ci\"\n\"8765\",\"qt5\"\n\"8762\",\"amazon-redshift\"\n\"8755\",\"alignment\"\n\"8752\",\"http-post\"\n\"8724\",\"windows-runtime\"\n\"8704\",\"pandas-groupby\"\n\"8700\",\"web-config\"\n\"8681\",\"ios6\"\n\"8680\",\"video-streaming\"\n\"8658\",\"zend-framework2\"\n\"8657\",\"logstash\"\n\"8646\",\"material-design\"\n\"8645\",\"singleton\"\n\"8622\",\"task\"\n\"8612\",\"data-science\"\n\"8589\",\"spring-batch\"\n\"8578\",\"react-navigation\"\n\"8568\",\"sh\"\n\"8568\",\"c++14\"\n\"8567\",\"operator-overloading\"\n\"8562\",\"retrofit\"\n\"8536\",\"gtk\"\n\"8528\",\"vagrant\"\n\"8523\",\"ef-code-first\"\n\"8521\",\"uitextfield\"\n\"8520\",\"jtable\"\n\"8510\",\"bitbucket\"\n\"8493\",\"mocha.js\"\n\"8458\",\"internet-explorer-8\"\n\"8455\",\"language-lawyer\"\n\"8450\",\"jasper-reports\"\n\"8450\",\"controls\"\n\"8448\",\"testng\"\n\"8447\",\"sharepoint-2010\"\n\"8430\",\"asp.net-mvc-2\"\n\"8395\",\"broadcastreceiver\"\n\"8394\",\"bar-chart\"\n\"8385\",\"aws-cloudformation\"\n\"8384\",\"aggregate\"\n\"8361\",\"language-agnostic\"\n\"8359\",\"double\"\n\"8347\",\"blackberry\"\n\"8298\",\"hdfs\"\n\"8295\",\"conda\"\n\"8294\",\"left-join\"\n\"8289\",\"android-sqlite\"\n\"8278\",\"tinymce\"\n\"8271\",\"pivot-table\"\n\"8270\",\"polymer\"\n\"8260\",\"virtual-machine\"\n\"8258\",\"mercurial\"\n\"8245\",\"client\"\n\"8238\",\"webserver\"\n\"8219\",\"case\"\n\"8216\",\"glsl\"\n\"8214\",\"akka\"\n\"8208\",\"out-of-memory\"\n\"8208\",\"comparison\"\n\"8206\",\"devops\"\n\"8204\",\"themes\"\n\"8172\",\"databricks\"\n\"8168\",\"scheme\"\n\"8166\",\"overriding\"\n\"8156\",\"deserialization\"\n\"8148\",\"app-store\"\n\"8147\",\"momentjs\"\n\"8143\",\"fragment\"\n\"8136\",\"query-optimization\"\n\"8132\",\"parameter-passing\"\n\"8131\",\"accessibility\"\n\"8091\",\"jupyter\"\n\"8087\",\"shared-libraries\"\n\"8074\",\"apple-push-notifications\"\n\"8074\",\"tcl\"\n\"8064\",\"keycloak\"\n\"8059\",\"mule\"\n\"8026\",\"spring-integration\"\n\"8024\",\"puppeteer\"\n\"8018\",\"html5-video\"\n\"8017\",\"usb\"\n\"8011\",\"media-queries\"\n\"8009\",\"full-text-search\"\n\"7995\",\"bigdata\"\n\"7986\",\"apache2\"\n\"7985\",\"refactoring\"\n\"7976\",\"bit-manipulation\"\n\"7954\",\"apk\"\n\"7951\",\"tableview\"\n\"7933\",\"google-colaboratory\"\n\"7909\",\"dynamics-crm\"\n\"7908\",\"cygwin\"\n\"7904\",\"rstudio\"\n\"7889\",\"seo\"\n\"7889\",\"appium\"\n\"7878\",\"aws-api-gateway\"\n\"7867\",\"httprequest\"\n\"7859\",\"angular6\"\n\"7858\",\"runtime\"\n\"7854\",\"classification\"\n\"7840\",\"apache-flink\"\n\"7839\",\"boto3\"\n\"7834\",\"operators\"\n\"7824\",\"protocol-buffers\"\n\"7820\",\"react-router-dom\"\n\"7815\",\"byte\"\n\"7799\",\"typeerror\"\n\"7797\",\"row\"\n\"7793\",\"character\"\n\"7791\",\"filtering\"\n\"7787\",\"coding-style\"\n\"7783\",\"adb\"\n\"7783\",\"single-sign-on\"\n\"7770\",\"python-asyncio\"\n\"7755\",\"air\"\n\"7753\",\"vuex\"\n\"7748\",\"bootstrap-modal\"\n\"7740\",\"openshift\"\n\"7729\",\"sharedpreferences\"\n\"7728\",\"jax-rs\"\n\"7723\",\"asp.net-web-api2\"\n\"7723\",\"requirejs\"\n\"7722\",\"token\"\n\"7719\",\"blob\"\n\"7713\",\"glassfish\"\n\"7707\",\"visual-studio-2019\"\n\"7703\",\"handlebars.js\"\n\"7696\",\"rss\"\n\"7695\",\"windows-phone-8.1\"\n\"7695\",\"sql-order-by\"\n\"7694\",\"expression\"\n\"7689\",\"azure-cosmosdb\"\n\"7678\",\"css-animations\"\n\"7668\",\"odata\"\n\"7644\",\"ocaml\"\n\"7634\",\"oracle-sqldeveloper\"\n\"7632\",\"decimal\"\n\"7631\",\"jms\"\n\"7620\",\"grouping\"\n\"7609\",\"progress-bar\"\n\"7605\",\"sms\"\n\"7604\",\"schema\"\n\"7600\",\"phantomjs\"\n\"7599\",\"2d\"\n\"7588\",\"jpanel\"\n\"7581\",\"phpstorm\"\n\"7569\",\"retrofit2\"\n\"7567\",\"ssms\"\n\"7565\",\"maui\"\n\"7546\",\"pdf-generation\"\n\"7541\",\"virtualenv\"\n\"7537\",\"report\"\n\"7536\",\"signals\"\n\"7522\",\"nunit\"\n\"7501\",\"kendo-grid\"\n\"7495\",\"android-webview\"\n\"7472\",\"laravel-blade\"\n\"7472\",\"scanf\"\n\"7457\",\"firefox-addon\"\n\"7446\",\"webkit\"\n\"7440\",\"applet\"\n\"7421\",\"pine-script\"\n\"7415\",\"data-visualization\"\n\"7413\",\"streaming\"\n\"7406\",\"amazon-cognito\"\n\"7393\",\"registry\"\n\"7393\",\"angular-cli\"\n\"7373\",\"console-application\"\n\"7365\",\"entity-framework-4\"\n\"7338\",\"aes\"\n\"7333\",\"focus\"\n\"7308\",\"xna\"\n\"7301\",\"laravel-8\"\n\"7301\",\"google-cloud-messaging\"\n\"7298\",\"less\"\n\"7290\",\"nativescript\"\n\"7285\",\"jenkins-plugins\"\n\"7271\",\"firebase-storage\"\n\"7266\",\"devexpress\"\n\"7250\",\"wxpython\"\n\"7245\",\"jetty\"\n\"7237\",\"tooltip\"\n\"7236\",\"database-connection\"\n\"7229\",\"google-calendar-api\"\n\"7229\",\"ipython\"\n\"7228\",\"google-cloud-datastore\"\n\"7228\",\"google-play-services\"\n\"7224\",\"scheduled-tasks\"\n\"7218\",\"x86-64\"\n\"7205\",\"notepad++\"\n\"7202\",\"javascript-objects\"\n\"7190\",\"powerpoint\"\n\"7176\",\"load\"\n\"7161\",\"fastapi\"\n\"7151\",\"content-management-system\"\n\"7140\",\"list-comprehension\"\n\"7140\",\"flask-sqlalchemy\"\n\"7123\",\"nltk\"\n\"7116\",\"nsstring\"\n\"7116\",\"mpi\"\n\"7115\",\"oracle10g\"\n\"7112\",\"websphere\"\n\"7112\",\"buffer\"\n\"7109\",\"amazon-rds\"\n\"7101\",\"size\"\n\"7087\",\"uilabel\"\n\"7083\",\"sapui5\"\n\"7077\",\"android-room\"\n\"7049\",\"integration-testing\"\n\"7049\",\"mysql-workbench\"\n\"7043\",\"scrollview\"\n\"7041\",\"flutter-dependencies\"\n\"7037\",\"oracle-apex\"\n\"7035\",\"uml\"\n\"7031\",\"shader\"\n\"7027\",\"http-status-code-404\"\n\"7021\",\"rendering\"\n\"7018\",\"google-kubernetes-engine\"\n\"7014\",\"extract\"\n\"7007\",\"visualization\"\n\"7007\",\"prometheus\"\n\"7006\",\"lisp\"\n\"7006\",\"homebrew\"\n\"7003\",\"lodash\"\n\"6987\",\"vaadin\"\n\"6983\",\"cursor\"\n\"6976\",\"ascii\"\n\"6975\",\"windows-store-apps\"\n\"6969\",\"playframework-2.0\"\n\"6967\",\"ruby-on-rails-3.2\"\n\"6966\",\"rx-java\"\n\"6966\",\"passport.js\"\n\"6965\",\"eslint\"\n\"6961\",\"overloading\"\n\"6948\",\"rsa\"\n\"6947\",\"hbase\"\n\"6912\",\"version\"\n\"6911\",\"pymongo\"\n\"6910\",\"httpclient\"\n\"6890\",\"robotframework\"\n\"6887\",\"domain-driven-design\"\n\"6870\",\"linq-to-entities\"\n\"6869\",\"subset\"\n\"6863\",\"processing\"\n\"6855\",\"big-o\"\n\"6846\",\"django-queryset\"\n\"6845\",\"mingw\"\n\"6844\",\"coordinates\"\n\"6840\",\"undefined\"\n\"6837\",\"relational-database\"\n\"6833\",\"gnuplot\"\n\"6830\",\"binary-tree\"\n\"6816\",\"blockchain\"\n\"6816\",\"ethereum\"\n\"6816\",\"storage\"\n\"6815\",\"png\"\n\"6807\",\"ibm-cloud\"\n\"6787\",\"jsoup\"\n\"6783\",\"webgl\"\n\"6782\",\"google-compute-engine\"\n\"6781\",\"port\"\n\"6778\",\"jq\"\n\"6776\",\"vectorization\"\n\"6774\",\"windows-phone\"\n\"6772\",\"grpc\"\n\"6766\",\"pyinstaller\"\n\"6762\",\"jquery-validate\"\n\"6761\",\"histogram\"\n\"6733\",\"android-volley\"\n\"6731\",\"text-files\"\n\"6729\",\"vite\"\n\"6727\",\"xhtml\"\n\"6708\",\"android-service\"\n\"6707\",\"node-modules\"\n\"6706\",\"solidity\"\n\"6701\",\"fork\"\n\"6700\",\"gis\"\n\"6700\",\"ejb\"\n\"6680\",\"vsto\"\n\"6680\",\"inner-join\"\n\"6666\",\"wildfly\"\n\"6654\",\"heap-memory\"\n\"6631\",\"automapper\"\n\"6628\",\"openmp\"\n\"6603\",\"azure-storage\"\n\"6598\",\"karma-jasmine\"\n\"6590\",\"awt\"\n\"6582\",\"structure\"\n\"6573\",\"mapbox\"\n\"6573\",\"linear-regression\"\n\"6571\",\"sails.js\"\n\"6566\",\"llvm\"\n\"6550\",\"android-camera\"\n\"6539\",\"angular5\"\n\"6535\",\"client-server\"\n\"6521\",\"dropdown\"\n\"6519\",\"ejs\"\n\"6514\",\"scrollbar\"\n\"6513\",\"uitabbarcontroller\"\n\"6505\",\"chef-infra\"\n\"6490\",\"avfoundation\"\n\"6487\",\"java.util.scanner\"\n\"6487\",\"liferay\"\n\"6482\",\"generator\"\n\"6477\",\"metadata\"\n\"6464\",\"sitecore\"\n\"6464\",\"entity\"\n\"6446\",\"mqtt\"\n\"6439\",\"combinations\"\n\"6422\",\"textarea\"\n\"6399\",\"binary-search-tree\"\n\"6395\",\"kibana\"\n\"6383\",\"overflow\"\n\"6382\",\"pug\"\n\"6378\",\"cross-domain\"\n\"6378\",\"spring-webflux\"\n\"6375\",\"kubernetes-helm\"\n\"6375\",\"powerquery\"\n\"6369\",\"bootstrap-5\"\n\"6350\",\"sublimetext3\"\n\"6348\",\"aws-sdk\"\n\"6344\",\"jekyll\"\n\"6341\",\"common-lisp\"\n\"6338\",\"this\"\n\"6335\",\"css-position\"\n\"6331\",\"slice\"\n\"6313\",\"comments\"\n\"6311\",\"ocr\"\n\"6310\",\"touch\"\n\"6307\",\"css-grid\"\n\"6304\",\"css-transitions\"\n\"6295\",\"lstm\"\n\"6292\",\"formula\"\n\"6291\",\"element\"\n\"6279\",\"verilog\"\n\"6274\",\"task-parallel-library\"\n\"6267\",\"carousel\"\n\"6263\",\"nodes\"\n\"6261\",\"line\"\n\"6255\",\"mouseevent\"\n\"6246\",\"telegram\"\n\"6238\",\"excel-2010\"\n\"6232\",\"cluster-analysis\"\n\"6231\",\"interface-builder\"\n\"6228\",\"osgi\"\n\"6225\",\"docusignapi\"\n\"6224\",\"hyperledger-fabric\"\n\"6218\",\"fetch-api\"\n\"6218\",\"prepared-statement\"\n\"6216\",\"vue-router\"\n\"6216\",\"height\"\n\"6215\",\"uinavigationbar\"\n\"6204\",\"config\"\n\"6194\",\"sparql\"\n\"6183\",\"google-sheets-api\"\n\"6176\",\"uri\"\n\"6172\",\"c++-cli\"\n\"6168\",\"unique\"\n\"6165\",\"ssrs-2008\"\n\"6161\",\"azure-ad-b2c\"\n\"6157\",\"instagram\"\n\"6154\",\"couchdb\"\n\"6151\",\"app-store-connect\"\n\"6150\",\"associations\"\n\"6147\",\"navbar\"\n\"6139\",\"tdd\"\n\"6139\",\"zsh\"\n\"6137\",\"jquery-animate\"\n\"6118\",\"swt\"\n\"6116\",\"xcode6\"\n\"6114\",\"gzip\"\n\"6111\",\"64-bit\"\n\"6105\",\"posix\"\n\"6102\",\"xslt-1.0\"\n\"6097\",\"std\"\n\"6091\",\"android-manifest\"\n\"6086\",\"teamcity\"\n\"6077\",\"alamofire\"\n\"6074\",\"sequence\"\n\"6073\",\"width\"\n\"6072\",\"adobe\"\n\"6071\",\"zooming\"\n\"6070\",\"profiling\"\n\"6070\",\"cross-platform\"\n\"6070\",\"ios-simulator\"\n\"6067\",\"ibm-mobilefirst\"\n\"6061\",\"networkx\"\n\"6052\",\"background-image\"\n\"6048\",\"driver\"\n\"6044\",\"python-import\"\n\"6043\",\"svelte\"\n\"6043\",\"ms-access-2010\"\n\"6038\",\"html-parsing\"\n\"6035\",\"weblogic\"\n\"6030\",\"editor\"\n\"6027\",\"transform\"\n\"6014\",\"apply\"\n\"6014\",\"lazy-loading\"\n\"5993\",\"html-lists\"\n\"5983\",\"ms-office\"\n\"5979\",\"wifi\"\n\"5979\",\"mapkit\"\n\"5968\",\"windows-subsystem-for-linux\"\n\"5964\",\"css-float\"\n\"5962\",\"grafana\"\n\"5959\",\"etl\"\n\"5957\",\"exec\"\n\"5949\",\"drawing\"\n\"5942\",\"capybara\"\n\"5942\",\"mips\"\n\"5939\",\"jira\"\n\"5939\",\"toggle\"\n\"5936\",\".net-3.5\"\n\"5935\",\"xmpp\"\n\"5931\",\"javafx-8\"\n\"5930\",\"field\"\n\"5928\",\"directx\"\n\"5923\",\"border\"\n\"5923\",\".net-6.0\"\n\"5919\",\"multiple-columns\"\n\"5916\",\"gatsby\"\n\"5914\",\"signal-processing\"\n\"5914\",\"response\"\n\"5908\",\"c-preprocessor\"\n\"5900\",\"textures\"\n\"5897\",\"create-react-app\"\n\"5897\",\"chat\"\n\"5894\",\"warnings\"\n\"5892\",\"sympy\"\n\"5880\",\"racket\"\n\"5875\",\"aggregate-functions\"\n\"5873\",\"prestashop\"\n\"5849\",\"preg-replace\"\n\"5843\",\"average\"\n\"5822\",\"uitextview\"\n\"5811\",\"vhdl\"\n\"5810\",\"moq\"\n\"5800\",\"subdomain\"\n\"5799\",\"navigation-drawer\"\n\"5795\",\"backup\"\n\"5790\",\"interop\"\n\"5769\",\"hook\"\n\"5763\",\"tensorflow2.0\"\n\"5757\",\"opencl\"\n\"5751\",\"echo\"\n\"5749\",\"underscore.js\"\n\"5746\",\"java-me\"\n\"5744\",\"reactive-programming\"\n\"5739\",\"hosting\"\n\"5738\",\"wpf-controls\"\n\"5732\",\"activemq-classic\"\n\"5730\",\"android-widget\"\n\"5723\",\"office-js\"\n\"5720\",\"android-alertdialog\"\n\"5712\",\"converters\"\n\"5696\",\"pipeline\"\n\"5695\",\"ssas\"\n\"5694\",\"maven-2\"\n\"5693\",\"dynamic-programming\"\n\"5672\",\"listener\"\n\"5669\",\"swift4\"\n\"5661\",\"virtualbox\"\n\"5656\",\"relationship\"\n\"5651\",\"openid-connect\"\n\"5647\",\"c++20\"\n\"5645\",\"eclipse-rcp\"\n\"5641\",\"user-input\"\n\"5639\",\"font-awesome\"\n\"5638\",\"openpyxl\"\n\"5636\",\"android-animation\"\n\"5634\",\"progressive-web-apps\"\n\"5632\",\"amazon-iam\"\n\"5625\",\"jquery-select2\"\n\"5623\",\"addition\"\n\"5621\",\"python-3.6\"\n\"5618\",\"teradata\"\n\"5616\",\"win-universal-app\"\n\"5616\",\"spark-streaming\"\n\"5614\",\"protocols\"\n\"5604\",\"pyqt4\"\n\"5592\",\"phpmailer\"\n\"5592\",\"cordova-plugins\"\n\"5582\",\"ionic4\"\n\"5574\",\"rounding\"\n\"5573\",\"keyboard-shortcuts\"\n\"5572\",\"httpwebrequest\"\n\"5571\",\"extjs4\"\n\"5563\",\"firebase-security\"\n\"5551\",\"internet-explorer-11\"\n\"5547\",\"stm32\"\n\"5543\",\"google-cloud-dataflow\"\n\"5539\",\"cluster-computing\"\n\"5510\",\"webstorm\"\n\"5510\",\"hashtable\"\n\"5507\",\"performance-testing\"\n\"5506\",\"wamp\"\n\"5504\",\"bundle\"\n\"5504\",\"exe\"\n\"5501\",\"rename\"\n\"5494\",\"codeigniter-3\"\n\"5494\",\"dialogflow-es\"\n\"5492\",\"fluent-nhibernate\"\n\"5487\",\"push\"\n\"5482\",\"ember-data\"\n\"5480\",\"jstl\"\n\"5478\",\"settings\"\n\"5478\",\"tomcat7\"\n\"5478\",\"cxf\"\n\"5472\",\"lxml\"\n\"5471\",\"branch\"\n\"5468\",\"amazon-ecs\"\n\"5467\",\"microsoft-teams\"\n\"5464\",\"workflow\"\n\"5455\",\"xpages\"\n\"5448\",\"fft\"\n\"5447\",\"npm-install\"\n\"5446\",\"prototype\"\n\"5424\",\"identityserver4\"\n\"5418\",\"instance\"\n\"5417\",\"tableau-api\"\n\"5397\",\"single-page-application\"\n\"5395\",\"speech-recognition\"\n\"5394\",\"cpanel\"\n\"5391\",\"xcode4\"\n\"5388\",\"code-coverage\"\n\"5383\",\"refresh\"\n\"5382\",\"linkedin-api\"\n\"5381\",\"xquery\"\n\"5370\",\"knitr\"\n\"5368\",\"settimeout\"\n\"5365\",\"preg-match\"\n\"5361\",\"sinatra\"\n\"5352\",\"e-commerce\"\n\"5346\",\"google-maps-markers\"\n\"5341\",\"cython\"\n\"5337\",\"nsarray\"\n\"5335\",\"union\"\n\"5333\",\"simulation\"\n\"5331\",\"abstract-class\"\n\"5331\",\"titanium\"\n\"5324\",\"render\"\n\"5321\",\"facebook-javascript-sdk\"\n\"5319\",\"pom.xml\"\n\"5319\",\"opencart\"\n\"5313\",\"apache-nifi\"\n\"5311\",\"parent-child\"\n\"5307\",\"nsmutablearray\"\n\"5307\",\"export-to-csv\"\n\"5303\",\"permutation\"\n\"5300\",\"octave\"\n\"5298\",\"servicestack\"\n\"5298\",\"asp.net-ajax\"\n\"5293\",\"qt4\"\n\"5292\",\"locale\"\n\"5288\",\"counter\"\n\"5280\",\"es6-promise\"\n\"5277\",\"submit\"\n\"5275\",\"load-balancing\"\n\"5271\",\"antd\"\n\"5267\",\"interpolation\"\n\"5263\",\"transition\"\n\"5255\",\"nasm\"\n\"5241\",\"fancybox\"\n\"5239\",\"maven-3\"\n\"5236\",\"ruby-on-rails-3.1\"\n\"5218\",\"reverse-proxy\"\n\"5213\",\"jakarta-mail\"\n\"5212\",\"spring-cloud\"\n\"5211\",\"angular7\"\n\"5196\",\"apache-pig\"\n\"5192\",\"memcached\"\n\"5190\",\"jquery-events\"\n\"5187\",\"spyder\"\n\"5186\",\"sftp\"\n\"5181\",\"html-select\"\n\"5180\",\"cgi\"\n\"5179\",\"blazor-server-side\"\n\"5177\",\"openlayers\"\n\"5167\",\"distinct\"\n\"5166\",\"telegram-bot\"\n\"5166\",\"legend\"\n\"5160\",\"html-email\"\n\"5155\",\"silverlight-4.0\"\n\"5152\",\"query-string\"\n\"5151\",\"spinner\"\n\"5150\",\"sql-insert\"\n\"5139\",\"persistence\"\n\"5130\",\"textfield\"\n\"5130\",\"google-signin\"\n\"5129\",\"linux-device-driver\"\n\"5128\",\"zurb-foundation\"\n\"5127\",\"google-tag-manager\"\n\"5124\",\"newline\"\n\"5124\",\"responsive\"\n\"5120\",\"segue\"\n\"5115\",\"cell\"\n\"5110\",\"url-routing\"\n\"5102\",\"product\"\n\"5093\",\"eclipselink\"\n\"5074\",\"amazon-cloudfront\"\n\"5073\",\"magento2\"\n\"5072\",\"href\"\n\"5072\",\"hql\"\n\"5069\",\"h2\"\n\"5058\",\"command-line-arguments\"\n\"5057\",\"ag-grid\"\n\"5055\",\"special-characters\"\n\"5051\",\"aws-amplify\"\n\"5049\",\"linq-to-xml\"\n\"5042\",\"adapter\"\n\"5038\",\"aem\"\n\"5035\",\"anchor\"\n\"5032\",\"google-places-api\"\n\"5019\",\"netty\"\n\"5017\",\"gstreamer\"\n\"5013\",\"github-pages\"\n\"5001\",\"pickle\"\n\"5000\",\"decorator\"\n\"4998\",\"microsoft-edge\"\n\"4992\",\"mkmapview\"\n\"4992\",\"sdl\"\n\"4972\",\"user-defined-functions\"\n\"4971\",\"jpeg\"\n\"4964\",\"android-notifications\"\n\"4962\",\"android-linearlayout\"\n\"4960\",\"qt-creator\"\n\"4958\",\"server-side-rendering\"\n\"4957\",\"codenameone\"\n\"4956\",\"viewmodel\"\n\"4955\",\"action\"\n\"4952\",\"embed\"\n\"4948\",\"translation\"\n\"4948\",\"nested-loops\"\n\"4947\",\"eval\"\n\"4945\",\"resharper\"\n\"4943\",\"ubuntu-16.04\"\n\"4931\",\"jbutton\"\n\"4931\",\"geospatial\"\n\"4926\",\"alert\"\n\"4917\",\"analytics\"\n\"4911\",\"tortoisesvn\"\n\"4907\",\"python-multiprocessing\"\n\"4902\",\"jhipster\"\n\"4897\",\"rails-activerecord\"\n\"4892\",\"data-analysis\"\n\"4890\",\"integration\"\n\"4881\",\"session-cookies\"\n\"4873\",\"inno-setup\"\n\"4872\",\"netsuite\"\n\"4860\",\"typescript-typings\"\n\"4859\",\"webhooks\"\n\"4858\",\"classpath\"\n\"4858\",\"sharepoint-2013\"\n\"4855\",\"overlay\"\n\"4849\",\"apache-beam\"\n\"4849\",\"javafx-2\"\n\"4842\",\"ubuntu-14.04\"\n\"4836\",\"angular-reactive-forms\"\n\"4834\",\"nsdate\"\n\"4831\",\"project\"\n\"4828\",\"typeorm\"\n\"4820\",\"hiveql\"\n\"4813\",\"exchange-server\"\n\"4809\",\"metaprogramming\"\n\"4809\",\"stdout\"\n\"4808\",\"xslt-2.0\"\n\"4807\",\"angular2-routing\"\n\"4800\",\"monitoring\"\n\"4796\",\"android-mediaplayer\"\n\"4789\",\"azure-databricks\"\n\"4783\",\"datasource\"\n\"4783\",\"core-graphics\"\n\"4782\",\"upgrade\"\n\"4781\",\"screenshot\"\n\"4780\",\"screen\"\n\"4777\",\"native\"\n\"4775\",\"mutex\"\n\"4764\",\"multipartform-data\"\n\"4763\",\"service-worker\"\n\"4763\",\"sprite\"\n\"4758\",\"setinterval\"\n\"4751\",\"hide\"\n\"4748\",\"kotlin-coroutines\"\n\"4745\",\"bokeh\"\n\"4742\",\"webbrowser-control\"\n\"4741\",\"boost-asio\"\n\"4738\",\"sql-server-2014\"\n\"4733\",\"mongoid\"\n\"4733\",\"typescript-generics\"\n\"4732\",\"return-value\"\n\"4729\",\"sencha-touch\"\n\"4724\",\"csrf\"\n\"4723\",\"global\"\n\"4722\",\"augmented-reality\"\n\"4722\",\"threadpool\"\n\"4719\",\"cpu\"\n\"4719\",\"angular8\"\n\"4711\",\"stata\"\n\"4709\",\"autohotkey\"\n\"4703\",\"primeng\"\n\"4699\",\"postgis\"\n\"4696\",\"openapi\"\n\"4696\",\"system\"\n\"4688\",\"documentation\"\n\"4682\",\"categories\"\n\"4682\",\"quarkus\"\n\"4681\",\"xml-serialization\"\n\"4677\",\"middleware\"\n\"4670\",\"gunicorn\"\n\"4668\",\"fabricjs\"\n\"4663\",\"diff\"\n\"4662\",\"mobile-safari\"\n\"4661\",\"vscode-extensions\"\n\"4660\",\"object-detection\"\n\"4658\",\"wildcard\"\n\"4654\",\"web-deployment\"\n\"4651\",\"angular-ui-bootstrap\"\n\"4644\",\"crud\"\n\"4642\",\"calculator\"\n\"4637\",\"owin\"\n\"4635\",\"karate\"\n\"4623\",\"spreadsheet\"\n\"4621\",\"soapui\"\n\"4621\",\"cross-compiling\"\n\"4620\",\"connection-string\"\n\"4620\",\"karma-runner\"\n\"4620\",\"ipc\"\n\"4619\",\"polygon\"\n\"4618\",\"plotly-dash\"\n\"4618\",\"embedded-linux\"\n\"4616\",\"real-time\"\n\"4611\",\"dask\"\n\"4610\",\"centos7\"\n\"4609\",\"customization\"\n\"4606\",\"linear-algebra\"\n\"4602\",\"styled-components\"\n\"4600\",\"alarmmanager\"\n\"4599\",\"precision\"\n\"4596\",\"outlook-addin\"\n\"4592\",\"advanced-custom-fields\"\n\"4591\",\"ngrx\"\n\"4586\",\"razor-pages\"\n\"4583\",\"apollo\"\n\"4579\",\"gcloud\"\n\"4578\",\"static-libraries\"\n\"4574\",\"laravel-5.1\"\n\"4573\",\"admin\"\n\"4573\",\"contextmenu\"\n\"4573\",\"gnu-make\"\n\"4563\",\"primary-key\"\n\"4559\",\"mp3\"\n\"4555\",\"swagger-ui\"\n\"4554\",\"pass-by-reference\"\n\"4551\",\"scale\"\n\"4547\",\"imap\"\n\"4545\",\"clone\"\n\"4545\",\"intellisense\"\n\"4545\",\"vlookup\"\n\"4545\",\"message\"\n\"4542\",\"spring-kafka\"\n\"4537\",\"youtube-data-api\"\n\"4529\",\"svm\"\n\"4508\",\"xss\"\n\"4501\",\"umbraco\"\n\"4501\",\"wolfram-mathematica\"\n\"4500\",\"graph-theory\"\n\"4494\",\"okhttp\"\n\"4494\",\"computer-science\"\n\"4493\",\"nfc\"\n\"4492\",\"smarty\"\n\"4491\",\"psycopg2\"\n\"4487\",\"alias\"\n\"4482\",\"aws-cli\"\n\"4479\",\"uicollectionviewcell\"\n\"4477\",\"log4j2\"\n\"4477\",\"powerbi-desktop\"\n\"4473\",\"cakephp-3.0\"\n\"4472\",\"tesseract\"\n\"4468\",\"addeventlistener\"\n\"4468\",\"heatmap\"\n\"4467\",\"share\"\n\"4459\",\"oledb\"\n\"4457\",\"innodb\"\n\"4456\",\"ibm-mq\"\n\"4452\",\"prism\"\n\"4452\",\"emulation\"\n\"4445\",\"collision-detection\"\n\"4443\",\"bioinformatics\"\n\"4442\",\"powershell-2.0\"\n\"4441\",\"function-pointers\"\n\"4439\",\"proguard\"\n\"4436\",\"dynamics-crm-2011\"\n\"4425\",\"variable-assignment\"\n\"4424\",\"openstreetmap\"\n\"4421\",\"recaptcha\"\n\"4419\",\"shapes\"\n\"4410\",\"visual-studio-2022\"\n\"4406\",\"limit\"\n\"4401\",\"rows\"\n\"4401\",\"inputstream\"\n\"4395\",\"whitespace\"\n\"4389\",\"inversion-of-control\"\n\"4388\",\"wmi\"\n\"4386\",\"plpgsql\"\n\"4386\",\"autodesk-forge\"\n\"4384\",\"gradient\"\n\"4383\",\"internet-explorer-9\"\n\"4381\",\"geojson\"\n\"4379\",\"database-migration\"\n\"4379\",\"mouse\"\n\"4377\",\"android-arrayadapter\"\n\"4373\",\"rake\"\n\"4371\",\"ms-access-2007\"\n\"4370\",\"decode\"\n\"4366\",\"artifactory\"\n\"4364\",\"chromium\"\n\"4360\",\"chatbot\"\n\"4353\",\"nan\"\n\"4352\",\"kafka-consumer-api\"\n\"4348\",\"tidyr\"\n\"4344\",\"gmail-api\"\n\"4339\",\"google-analytics-api\"\n\"4335\",\"autofac\"\n\"4333\",\"codeblocks\"\n\"4322\",\"rdf\"\n\"4318\",\"sparse-matrix\"\n\"4315\",\"string-formatting\"\n\"4302\",\"enzyme\"\n\"4299\",\"amazon-sqs\"\n\"4293\",\"azure-logic-apps\"\n\"4287\",\"greatest-n-per-group\"\n\"4286\",\"antlr\"\n\"4283\",\"paperclip\"\n\"4273\",\"payment-gateway\"\n\"4271\",\"common-table-expression\"\n\"4258\",\"amazon-cloudwatch\"\n\"4257\",\"grails-orm\"\n\"4252\",\"android-imageview\"\n\"4247\",\"terraform-provider-aws\"\n\"4242\",\"stack-overflow\"\n\"4238\",\"window-functions\"\n\"4236\",\"delay\"\n\"4235\",\"wxwidgets\"\n\"4232\",\"laravel-5.3\"\n\"4221\",\"aws-glue\"\n\"4218\",\"call\"\n\"4217\",\"custom-controls\"\n\"4211\",\"cpu-architecture\"\n\"4207\",\"xcode8\"\n\"4205\",\"qr-code\"\n\"4203\",\"iis-7.5\"\n\"4200\",\"command-prompt\"\n\"4199\",\"ctypes\"\n\"4197\",\"valgrind\"\n\"4194\",\"phonegap-plugins\"\n\"4186\",\"netlogo\"\n\"4185\",\"access-token\"\n\"4180\",\"onclicklistener\"\n\"4179\",\"asp.net-core-2.0\"\n\"4177\",\"filenames\"\n\"4174\",\"bundler\"\n\"4166\",\"orientation\"\n\"4163\",\"flutter-web\"\n\"4162\",\"accordion\"\n\"4162\",\"subclass\"\n\"4161\",\"core-animation\"\n\"4158\",\"junit4\"\n\"4157\",\"yarnpkg\"\n\"4156\",\"classloader\"\n\"4156\",\"laravel-5.4\"\n\"4153\",\"ios9\"\n\"4149\",\"asp.net-mvc-routing\"\n\"4145\",\"clipboard\"\n\"4139\",\"rx-java2\"\n\"4123\",\"python-3.7\"\n\"4121\",\"scenekit\"\n\"4121\",\"webclient\"\n\"4120\",\"google-plus\"\n\"4117\",\"uiimagepickercontroller\"\n\"4114\",\"wso2-esb\"\n\"4111\",\"rvm\"\n\"4111\",\"cakephp-2.0\"\n\"4108\",\"puppet\"\n\"4103\",\"nsdictionary\"\n\"4103\",\"android-permissions\"\n\"4101\",\"parquet\"\n\"4098\",\"pixel\"\n\"4096\",\"drools\"\n\"4094\",\"internet-explorer-7\"\n\"4091\",\"raspberry-pi3\"\n\"4088\",\"exchangewebservices\"\n\"4087\",\"z-index\"\n\"4085\",\"programming-languages\"\n\"4083\",\"travis-ci\"\n\"4081\",\"python-multithreading\"\n\"4080\",\"panel\"\n\"4075\",\"md5\"\n\"4073\",\"liquid\"\n\"4073\",\"probability\"\n\"4073\",\"rdd\"\n\"4071\",\"jax-ws\"\n\"4065\",\"react-bootstrap\"\n\"4064\",\"phoenix-framework\"\n\"4062\",\"couchbase\"\n\"4058\",\"django-orm\"\n\"4057\",\"azure-application-insights\"\n\"4056\",\"selection\"\n\"4053\",\"jsonp\"\n\"4051\",\"game-development\"\n\"4047\",\"naming-conventions\"\n\"4044\",\"lapply\"\n\"4043\",\"sublimetext2\"\n\"4038\",\"cdi\"\n\"4033\",\"linker-errors\"\n\"4032\",\"apache-kafka-streams\"\n\"4030\",\"antlr4\"\n\"4026\",\"variadic-templates\"\n\"4026\",\"batch-processing\"\n\"4025\",\"symfony4\"\n\"4022\",\"spotify\"\n\"4013\",\"contacts\"\n\"4012\",\"igraph\"\n\"4011\",\"padding\"\n\"4011\",\"log4net\"\n\"4004\",\"javabeans\"\n\"4003\",\"digital-ocean\"\n\"4002\",\"bind\"\n\"4002\",\"sharepoint-online\"\n\"4000\",\"message-queue\"\n\"3997\",\"oracle12c\"\n\"3994\",\"android-view\"\n\"3988\",\"distance\"\n\"3988\",\"facebook-like\"\n\"3987\",\"winrt-xaml\"\n\"3985\",\"xcode5\"\n\"3984\",\"symfony1\"\n\"3980\",\"mean\"\n\"3974\",\"mdx\"\n\"3967\",\"blazor-webassembly\"\n\"3961\",\"urllib\"\n\"3957\",\"clr\"\n\"3955\",\"ads\"\n\"3955\",\"kubernetes-ingress\"\n\"3953\",\"reverse-engineering\"\n\"3952\",\"media-player\"\n\"3948\",\"facebook-opengraph\"\n\"3946\",\"hadoop-yarn\"\n\"3940\",\"xcode7\"\n\"3935\",\"hyperledger\"\n\"3935\",\"game-physics\"\n\"3934\",\"simplexml\"\n\"3933\",\"entity-framework-5\"\n\"3929\",\"data-manipulation\"\n\"3920\",\"unity-container\"\n\"3919\",\"stdin\"\n\"3914\",\"code-generation\"\n\"3912\",\"opengl-es-2.0\"\n\"3905\",\"replication\"\n\"3905\",\"flex4\"\n\"3905\",\"numpy-ndarray\"\n\"3904\",\"firemonkey\"\n\"3902\",\"wkwebview\"\n\"3901\",\"reshape\"\n\"3895\",\"selector\"\n\"3895\",\"redhat\"\n\"3892\",\"atomic\"\n\"3887\",\"pyodbc\"\n\"3886\",\"manifest\"\n\"3886\",\"minecraft\"\n\"3886\",\"iot\"\n\"3884\",\"marklogic\"\n\"3882\",\"ip-address\"\n\"3881\",\"pentaho\"\n\"3878\",\"mean-stack\"\n\"3877\",\"cocos2d-x\"\n\"3876\",\"yocto\"\n\"3876\",\"future\"\n\"3875\",\"microsoft-metro\"\n\"3874\",\"capistrano\"\n\"3871\",\"apache-kafka-connect\"\n\"3868\",\"ui-automation\"\n\"3866\",\"complexity-theory\"\n\"3865\",\"each\"\n\"3865\",\"google-cloud-sql\"\n\"3864\",\"httpresponse\"\n\"3862\",\"amazon-eks\"\n\"3858\",\"wget\"\n\"3856\",\"liquibase\"\n\"3856\",\"android-relativelayout\"\n\"3853\",\"prisma\"\n\"3853\",\"spacy\"\n\"3852\",\"loading\"\n\"3852\",\"ninject\"\n\"3850\",\"router\"\n\"3849\",\"session-variables\"\n\"3846\",\"sql-server-2016\"\n\"3842\",\"sencha-touch-2\"\n\"3834\",\"openxml\"\n\"3834\",\"activex\"\n\"3833\",\"matlab-figure\"\n\"3830\",\"firebird\"\n\"3830\",\"aws-cdk\"\n\"3829\",\"game-engine\"\n\"3828\",\"automatic-ref-counting\"\n\"3817\",\"reverse\"\n\"3817\",\"sql-injection\"\n\"3816\",\"option-type\"\n\"3815\",\"quartz-scheduler\"\n\"3809\",\"android-canvas\"\n\"3807\",\"raster\"\n\"3805\",\"screen-scraping\"\n\"3804\",\"web-component\"\n\"3803\",\"inline\"\n\"3801\",\"github-api\"\n\"3797\",\"python-sphinx\"\n\"3794\",\"pinvoke\"\n\"3792\",\"system-calls\"\n\"3790\",\"azureservicebus\"\n\"3784\",\"updates\"\n\"3784\",\".net-4.5\"\n\"3781\",\"django-urls\"\n\"3779\",\"nsuserdefaults\"\n\"3779\",\"logistic-regression\"\n\"3772\",\"text-to-speech\"\n\"3771\",\"elisp\"\n\"3769\",\"marshalling\"\n\"3767\",\"grand-central-dispatch\"\n\"3767\",\"react-testing-library\"\n\"3767\",\"microcontroller\"\n\"3763\",\"gallery\"\n\"3760\",\"tornado\"\n\"3756\",\"avro\"\n\"3755\",\"local\"\n\"3753\",\"wicket\"\n\"3753\",\"margin\"\n\"3747\",\"afnetworking\"\n\"3746\",\"war\"\n\"3746\",\"one-to-many\"\n\"3745\",\"psql\"\n\"3743\",\"angular-routing\"\n\"3741\",\"executable\"\n\"3741\",\"nvidia\"\n\"3738\",\"elastic-stack\"\n\"3736\",\"nuget-package\"\n\"3732\",\"junit5\"\n\"3732\",\"splash-screen\"\n\"3727\",\"handler\"\n\"3724\",\"frame\"\n\"3723\",\"background-color\"\n\"3719\",\"reporting\"\n\"3719\",\"c#-3.0\"\n\"3717\",\"mvvmcross\"\n\"3715\",\"immutability\"\n\"3715\",\"apache-zookeeper\"\n\"3715\",\"pdfbox\"\n\"3713\",\"maven-plugin\"\n\"3712\",\"logback\"\n\"3709\",\"openid\"\n\"3707\",\"random-forest\"\n\"3706\",\"correlation\"\n\"3706\",\"richfaces\"\n\"3706\",\"uisearchbar\"\n\"3703\",\"aop\"\n\"3703\",\"kubectl\"\n\"3694\",\"nokogiri\"\n\"3694\",\"lookup\"\n\"3692\",\"scatter-plot\"\n\"3692\",\"draggable\"\n\"3690\",\"windows-8.1\"\n\"3689\",\"excel-2007\"\n\"3680\",\"facebook-php-sdk\"\n\"3679\",\"paypal-sandbox\"\n\"3673\",\"sql-server-ce\"\n\"3655\",\"python-typing\"\n\"3654\",\"redux-toolkit\"\n\"3653\",\"wrapper\"\n\"3648\",\"export-to-excel\"\n\"3648\",\"office-interop\"\n\"3644\",\"syntax-highlighting\"\n\"3642\",\"drupal-6\"\n\"3639\",\"uigesturerecognizer\"\n\"3636\",\"dynamic-memory-allocation\"\n\"3634\",\"phpexcel\"\n\"3632\",\"ssrs-2012\"\n\"3632\",\"cs50\"\n\"3631\",\"saml\"\n\"3631\",\"mediawiki\"\n\"3630\",\"sfml\"\n\"3629\",\"google-docs\"\n\"3628\",\"azure-aks\"\n\"3623\",\"bit\"\n\"3621\",\"argparse\"\n\"3620\",\"fxml\"\n\"3620\",\"avplayer\"\n\"3619\",\"toolbar\"\n\"3618\",\"gremlin\"\n\"3618\",\"activeadmin\"\n\"3617\",\"pyside\"\n\"3614\",\"passenger\"\n\"3610\",\"atom-editor\"\n\"3610\",\"extension-methods\"\n\"3608\",\"amazon-athena\"\n\"3604\",\"html5-audio\"\n\"3603\",\"series\"\n\"3602\",\"xlsx\"\n\"3602\",\"ubuntu-18.04\"\n\"3602\",\"struts\"\n\"3600\",\"infinite-loop\"\n\"3597\",\"assets\"\n\"3596\",\"biztalk\"\n\"3596\",\"physics\"\n\"3596\",\"benchmarking\"\n\"3595\",\"python-unittest\"\n\"3591\",\"intel\"\n\"3590\",\"appcelerator\"\n\"3590\",\"reduce\"\n\"3588\",\"header-files\"\n\"3584\",\"serverless\"\n\"3584\",\"digital-signature\"\n\"3581\",\"kerberos\"\n\"3580\",\"traits\"\n\"3579\",\"vercel\"\n\"3577\",\"jboss7.x\"\n\"3575\",\"store\"\n\"3572\",\"mern\"\n\"3569\",\"materialize\"\n\"3569\",\"data-cleaning\"\n\"3564\",\"slideshow\"\n\"3564\",\"asset-pipeline\"\n\"3564\",\"curve-fitting\"\n\"3563\",\"android-support-library\"\n\"3560\",\"increment\"\n\"3559\",\"richtextbox\"\n\"3558\",\"acumatica\"\n\"3558\",\"ienumerable\"\n\"3557\",\"rgb\"\n\"3556\",\"xdebug\"\n\"3556\",\"guava\"\n\"3553\",\"draw\"\n\"3549\",\"transparency\"\n\"3544\",\"setuptools\"\n\"3542\",\"alfresco\"\n\"3540\",\"dropbox\"\n\"3538\",\"dotnetnuke\"\n\"3538\",\"haml\"\n\"3538\",\"eigen\"\n\"3536\",\"default\"\n\"3535\",\"android-spinner\"\n\"3533\",\"symbols\"\n\"3529\",\"open-source\"\n\"3523\",\"deadlock\"\n\"3521\",\"purrr\"\n\"3517\",\"jpql\"\n\"3517\",\"monads\"\n\"3517\",\"copy-paste\"\n\"3513\",\"c++builder\"\n\"3513\",\"office-addins\"\n\"3509\",\"lotus-notes\"\n\"3509\",\"use-effect\"\n\"3507\",\"jsonschema\"\n\"3504\",\"fullscreen\"\n\"3503\",\"git-bash\"\n\"3501\",\"importerror\"\n\"3500\",\"backgroundworker\"\n\"3498\",\"k-means\"\n\"3497\",\"actionlistener\"\n\"3493\",\"google-maps-android-api-2\"\n\"3492\",\"footer\"\n\"3492\",\"transformation\"\n\"3491\",\"system-verilog\"\n\"3489\",\"huggingface-transformers\"\n\"3486\",\"scheduler\"\n\"3485\",\"next.js13\"\n\"3484\",\"onchange\"\n\"3481\",\"package.json\"\n\"3474\",\"wait\"\n\"3474\",\"windows-xp\"\n\"3473\",\"mathematical-optimization\"\n\"3473\",\"aggregation\"\n\"3472\",\"ros\"\n\"3472\",\"libraries\"\n\"3471\",\"sqlplus\"\n\"3469\",\"twisted\"\n\"3466\",\"deep-linking\"\n\"3465\",\"html-agility-pack\"\n\"3460\",\"payment\"\n\"3457\",\"mime-types\"\n\"3457\",\"plist\"\n\"3452\",\"sublimetext\"\n\"3450\",\"move\"\n\"3448\",\"load-testing\"\n\"3448\",\"declaration\"\n\"3446\",\"boxplot\"\n\"3446\",\"system.reactive\"\n\"3446\",\"font-face\"\n\"3439\",\"trigonometry\"\n\"3435\",\"abap\"\n\"3431\",\"powershell-3.0\"\n\"3427\",\"block\"\n\"3427\",\"jquery-ui-sortable\"\n\"3422\",\"center\"\n\"3421\",\"mybatis\"\n\"3415\",\"spatial\"\n\"3414\",\"git-merge\"\n\"3409\",\"publish\"\n\"3409\",\"interrupt\"\n\"3409\",\"aurelia\"\n\"3409\",\"bazel\"\n\"3407\",\"wso2-api-manager\"\n\"3406\",\"release\"\n\"3406\",\"amazon-emr\"\n\"3405\",\"x11\"\n\"3405\",\"bdd\"\n\"3404\",\"apollo-client\"\n\"3400\",\"sendgrid\"\n\"3393\",\"semaphore\"\n\"3392\",\"external\"\n\"3392\",\"commit\"\n\"3391\",\"clickonce\"\n\"3391\",\"android-adapter\"\n\"3389\",\"actions-on-google\"\n\"3388\",\"python-3.5\"\n\"3385\",\"facebook-login\"\n\"3382\",\"standards\"\n\"3382\",\"mongoose-schema\"\n\"3378\",\"desktop-application\"\n\"3376\",\"media\"\n\"3375\",\"shared-ptr\"\n\"3372\",\"datetimepicker\"\n\"3371\",\"react-props\"\n\"3369\",\".net-assembly\"\n\"3368\",\"primes\"\n\"3364\",\"magento-1.7\"\n\"3353\",\"android-espresso\"\n\"3353\",\"aspectj\"\n\"3350\",\"transpose\"\n\"3350\",\"recurrent-neural-network\"\n\"3349\",\"uipickerview\"\n\"3348\",\"root\"\n\"3348\",\"hook-woocommerce\"\n\"3347\",\"project-reactor\"\n\"3345\",\"basic-authentication\"\n\"3345\",\"eclipse-cdt\"\n\"3345\",\"sybase\"\n\"3338\",\"slf4j\"\n\"3335\",\"here-api\"\n\"3333\",\"cloudflare\"\n\"3332\",\"tls1.2\"\n\"3330\",\"zeromq\"\n\"3329\",\"perforce\"\n\"3325\",\"arkit\"\n\"3324\",\"regex-lookarounds\"\n\"3321\",\"flink-streaming\"\n\"3318\",\"vpn\"\n\"3318\",\"playwright\"\n\"3317\",\"mstest\"\n\"3313\",\"spring-data-mongodb\"\n\"3309\",\"uuid\"\n\"3306\",\"turtle-graphics\"\n\"3306\",\"use-state\"\n\"3304\",\"stanford-nlp\"\n\"3302\",\"compiler-optimization\"\n\"3299\",\"tweepy\"\n\"3295\",\"connection-pooling\"\n\"3294\",\"android-custom-view\"\n\"3293\",\"cloud-foundry\"\n\"3293\",\"grammar\"\n\"3287\",\"esp32\"\n\"3287\",\"tfsbuild\"\n\"3286\",\"xunit\"\n\"3286\",\"entity-framework-migrations\"\n\"3283\",\"bufferedreader\"\n\"3282\",\"git-branch\"\n\"3279\",\"box2d\"\n\"3276\",\"actionbarsherlock\"\n\"3275\",\"vertical-alignment\"\n\"3275\",\"webpack-dev-server\"\n\"3274\",\"gtk3\"\n\"3270\",\"jlabel\"\n\"3269\",\"updatepanel\"\n\"3268\",\"array-formulas\"\n\"3263\",\"indentation\"\n\"3263\",\"blender\"\n\"3262\",\"android-constraintlayout\"\n\"3259\",\"slack\"\n\"3259\",\"multer\"\n\"3257\",\"alexa-skills-kit\"\n\"3255\",\"custom-post-type\"\n\"3254\",\"hibernate-mapping\"\n\"3254\",\"httpurlconnection\"\n\"3249\",\"asp.net-membership\"\n\"3247\",\"mod-wsgi\"\n\"3247\",\"auth0\"\n\"3246\",\"quicksort\"\n\"3244\",\"windows-mobile\"\n\"3243\",\"lwjgl\"\n\"3243\",\"multilingual\"\n\"3238\",\"glob\"\n\"3237\",\"watchkit\"\n\"3237\",\"google-forms\"\n\"3234\",\"key-value\"\n\"3229\",\"nested-lists\"\n\"3227\",\"cart\"\n\"3227\",\"presto\"\n\"3226\",\"guice\"\n\"3225\",\"associative-array\"\n\"3223\",\"libcurl\"\n\"3223\",\"geocoding\"\n\"3221\",\"jpa-2.0\"\n\"3221\",\"vmware\"\n\"3220\",\"sql-like\"\n\"3214\",\"sleep\"\n\"3214\",\"repository-pattern\"\n\"3213\",\"mysql-python\"\n\"3208\",\"whatsapp\"\n\"3208\",\"android-contentprovider\"\n\"3206\",\"gif\"\n\"3206\",\"destructor\"\n\"3205\",\"microsoft-dynamics\"\n\"3204\",\"x509certificate\"\n\"3204\",\"partitioning\"\n\"3200\",\"normalization\"\n\"3200\",\"encode\"\n\"3199\",\"background-process\"\n\"3199\",\"dagger-2\"\n\"3198\",\"forms-authentication\"\n\"3197\",\"require\"\n\"3195\",\"docker-swarm\"\n\"3195\",\"visibility\"\n\"3195\",\"doxygen\"\n\"3191\",\"edit\"\n\"3190\",\"nest\"\n\"3190\",\"git-submodules\"\n\"3189\",\"mamp\"\n\"3186\",\"factory\"\n\"3185\",\"repeat\"\n\"3185\",\"shared-memory\"\n\"3178\",\"gitlab-ci-runner\"\n\"3177\",\"datetime-format\"\n\"3177\",\"android-source\"\n\"3166\",\"distribution\"\n\"3162\",\"bouncycastle\"\n\"3162\",\"virtual\"\n\"3159\",\"bower\"\n\"3151\",\"versioning\"\n\"3150\",\"p5.js\"\n\"3150\",\"spring-security-oauth2\"\n\"3146\",\"react-native-ios\"\n\"3145\",\"nexus\"\n\"3142\",\"coronasdk\"\n\"3142\",\"azure-service-fabric\"\n\"3142\",\"thumbnails\"\n\"3136\",\"visual-studio-2005\"\n\"3135\",\"na\"\n\"3134\",\"python-itertools\"\n\"3134\",\"crop\"\n\"3134\",\"web-hosting\"\n\"3132\",\"wear-os\"\n\"3131\",\"codeigniter-2\"\n\"3130\",\"angularfire2\"\n\"3129\",\"userform\"\n\"3128\",\"multi-tenant\"\n\"3125\",\"expandablelistview\"\n\"3123\",\"swift6\"\n\"3122\",\"sql-delete\"\n\"3121\",\"serverless-framework\"\n\"3117\",\"core-location\"\n\"3116\",\"typedef\"\n\"3116\",\"ember-cli\"\n\"3115\",\"file-permissions\"\n\"3114\",\"google-cloud-pubsub\"\n\"3113\",\"barcode\"\n\"3113\",\"ignite\"\n\"3112\",\"app-config\"\n\"3111\",\"docx\"\n\"3111\",\"android-toolbar\"\n\"3111\",\"virtualhost\"\n\"3109\",\"freemarker\"\n\"3104\",\"mp4\"\n\"3103\",\"data-modeling\"\n\"3103\",\"swig\"\n\"3103\",\"crashlytics\"\n\"3103\",\"moodle\"\n\"3102\",\"delphi-7\"\n\"3101\",\"form-submit\"\n\"3101\",\"android-jetpack\"\n\"3100\",\"saml-2.0\"\n\"3100\",\"collision\"\n\"3098\",\"nsurlconnection\"\n\"3097\",\"smartcontracts\"\n\"3097\",\"dashboard\"\n\"3096\",\"bitwise-operators\"\n\"3095\",\"compact-framework\"\n\"3094\",\"remote-access\"\n\"3094\",\"axis\"\n\"3093\",\"uwp-xaml\"\n\"3091\",\"crm\"\n\"3081\",\"dotnet-httpclient\"\n\"3080\",\"cdn\"\n\"3079\",\"rcpp\"\n\"3076\",\"offset\"\n\"3074\",\"windows-authentication\"\n\"3072\",\"e2e-testing\"\n\"3070\",\"data-mining\"\n\"3070\",\"roles\"\n\"3065\",\"video-processing\"\n\"3063\",\"flash-builder\"\n\"3061\",\"pie-chart\"\n\"3061\",\"amazon-sagemaker\"\n\"3060\",\".net-5\"\n\"3056\",\"jndi\"\n\"3053\",\"v8\"\n\"3052\",\"weka\"\n\"3052\",\"wso2-identity-server\"\n\"3051\",\"min\"\n\"3049\",\"contains\"\n\"3048\",\"data-annotations\"\n\"3045\",\"delimiter\"\n\"3045\",\"equals\"\n\"3044\",\"stdvector\"\n\"3041\",\"wireshark\"\n\"3039\",\"facebook-fql\"\n\"3038\",\"react-context\"\n\"3036\",\"carrierwave\"\n\"3036\",\"sdl-2\"\n\"3033\",\"azure-resource-manager\"\n\"3031\",\"dependency-management\"\n\"3031\",\"nio\"\n\"3028\",\"android-xml\"\n\"3027\",\"rdlc\"\n\"3027\",\"tokenize\"\n\"3025\",\"binary-search\"\n\"3021\",\"abstract-syntax-tree\"\n\"3020\",\"wav\"\n\"3015\",\"amazon-sns\"\n\"3014\",\"xib\"\n\"3014\",\"x86-16\"\n\"3013\",\"gitignore\"\n\"3012\",\"influxdb\"\n\"3012\",\"magento-1.9\"\n\"3011\",\"program-entry-point\"\n\"3009\",\"sharepoint-2007\"\n\"3009\",\"compatibility\"\n\"3008\",\"identity\"\n\"3006\",\"filepath\"\n\"3006\",\"innerhtml\"\n\"3005\",\"record\"\n\"3004\",\"instagram-api\"\n\"3004\",\"obfuscation\"\n\"3003\",\"freeze\"\n\"3002\",\"layout-manager\"\n\"3002\",\"webassembly\"\n\"3000\",\"publish-subscribe\"\n\"2999\",\"document\"\n\"2998\",\"hardware\"\n\"2998\",\"conditional-formatting\"\n\"2997\",\"android-drawable\"\n\"2994\",\"matrix-multiplication\"\n\"2993\",\"anylogic\"\n\"2993\",\"azure-powershell\"\n\"2991\",\"firewall\"\n\"2990\",\"raspbian\"\n\"2988\",\"jenkins-groovy\"\n\"2988\",\"new-operator\"\n\"2987\",\"graphviz\"\n\"2987\",\"java-7\"\n\"2986\",\"image-uploading\"\n\"2985\",\"sendmail\"\n\"2985\",\"javadoc\"\n\"2983\",\"entity-relationship\"\n\"2981\",\"drawable\"\n\"2981\",\"currency\"\n\"2978\",\"breakpoints\"\n\"2978\",\"google-cloud-run\"\n\"2975\",\"jquery-ui-datepicker\"\n\"2970\",\"angular-material2\"\n\"2970\",\"raphael\"\n\"2968\",\"onedrive\"\n\"2965\",\"device\"\n\"2964\",\"highlight\"\n\"2963\",\"graph-databases\"\n\"2963\",\"jtextfield\"\n\"2962\",\"nspredicate\"\n\"2961\",\"coq\"\n\"2961\",\"gd\"\n\"2957\",\"azure-keyvault\"\n\"2956\",\"display\"\n\"2956\",\"urllib2\"\n\"2953\",\"uwsgi\"\n\"2952\",\"database-schema\"\n\"2951\",\"browserify\"\n\"2950\",\"aframe\"\n\"2949\",\"master-pages\"\n\"2949\",\"orchardcms\"\n\"2947\",\"filereader\"\n\"2947\",\"attachment\"\n\"2947\",\"java-11\"\n\"2946\",\"file-handling\"\n\"2945\",\"latitude-longitude\"\n\"2943\",\"windbg\"\n\"2943\",\"intervals\"\n\"2941\",\"rvest\"\n\"2940\",\"slack-api\"\n\"2939\",\"binaryfiles\"\n\"2936\",\"jodatime\"\n\"2935\",\"nsdateformatter\"\n\"2932\",\"hazelcast\"\n\"2931\",\"elementtree\"\n\"2930\",\"tensor\"\n\"2930\",\"gdi+\"\n\"2929\",\"uistoryboard\"\n\"2929\",\"dapper\"\n\"2928\",\"fs\"\n\"2928\",\"chai\"\n\"2922\",\"parent\"\n\"2919\",\"android-livedata\"\n\"2916\",\"cpu-usage\"\n\"2914\",\"blogger\"\n\"2912\",\"fopen\"\n\"2908\",\"browser-cache\"\n\"2904\",\"iis-6\"\n\"2903\",\"formik\"\n\"2902\",\"azure-synapse\"\n\"2902\",\"search-engine\"\n\"2900\",\"sip\"\n\"2895\",\"roslyn\"\n\"2893\",\"entity-framework-4.1\"\n\"2892\",\"content-security-policy\"\n\"2891\",\"simulink\"\n\"2889\",\"private\"\n\"2889\",\"observablecollection\"\n\"2889\",\"angular2-template\"\n\"2886\",\"clion\"\n\"2886\",\"jobs\"\n\"2885\",\"metrics\"\n\"2884\",\"sveltekit\"\n\"2884\",\"nlog\"\n\"2883\",\"number-formatting\"\n\"2882\",\"python-2.x\"\n\"2879\",\"azure-ad-msal\"\n\"2879\",\"plone\"\n\"2879\",\"ld\"\n\"2878\",\"frequency\"\n\"2877\",\"missing-data\"\n\"2877\",\"do-while\"\n\"2877\",\"startup\"\n\"2876\",\"z3\"\n\"2872\",\"distributed-computing\"\n\"2868\",\"hierarchy\"\n\"2867\",\"mask\"\n\"2867\",\"indexoutofboundsexception\"\n\"2866\",\"gnu\"\n\"2865\",\"statsmodels\"\n\"2864\",\"stylesheet\"\n\"2863\",\"calayer\"\n\"2863\",\"environment\"\n\"2862\",\"resttemplate\"\n\"2861\",\"nginx-reverse-proxy\"\n\"2861\",\"fstream\"\n\"2860\",\"strapi\"\n\"2860\",\"googletest\"\n\"2859\",\"es6-modules\"\n\"2857\",\"blogs\"\n\"2856\",\"viewport\"\n\"2855\",\"webcam\"\n\"2852\",\"mvvm-light\"\n\"2851\",\"xgboost\"\n\"2851\",\"ping\"\n\"2850\",\"loopbackjs\"\n\"2849\",\"sudo\"\n\"2847\",\"patch\"\n\"2844\",\"jruby\"\n\"2842\",\"greasemonkey\"\n\"2842\",\"smart-pointers\"\n\"2842\",\"simple-form\"\n\"2838\",\"swipe\"\n\"2835\",\"captcha\"\n\"2835\",\"font-size\"\n\"2834\",\"criteria\"\n\"2834\",\"asterisk\"\n\"2831\",\"sinon\"\n\"2829\",\"slim\"\n\"2826\",\"caffe\"\n\"2826\",\"asmx\"\n\"2826\",\"prediction\"\n\"2824\",\"ibm-midrange\"\n\"2823\",\"data-warehouse\"\n\"2823\",\"trace\"\n\"2823\",\"arcgis\"\n\"2822\",\"static-methods\"\n\"2821\",\"rdbms\"\n\"2820\",\"ios10\"\n\"2820\",\"multi-index\"\n\"2818\",\"drupal-8\"\n\"2817\",\"fpga\"\n\"2815\",\"shinydashboard\"\n\"2813\",\"angularfire\"\n\"2812\",\"spring-cloud-stream\"\n\"2810\",\"breeze\"\n\"2810\",\"autowired\"\n\"2809\",\"paint\"\n\"2809\",\"apple-m1\"\n\"2808\",\"word-wrap\"\n\"2806\",\"avaudioplayer\"\n\"2804\",\"scheduling\"\n\"2803\",\"type-inference\"\n\"2802\",\"talend\"\n\"2802\",\"computational-geometry\"\n\"2800\",\"android-5.0-lollipop\"\n\"2799\",\"ubuntu-12.04\"\n\"2796\",\"contour\"\n\"2794\",\"openai-api\"\n\"2792\",\"jython\"\n\"2791\",\"repeater\"\n\"2790\",\"viewcontroller\"\n\"2789\",\"filestream\"\n\"2789\",\"nullreferenceexception\"\n\"2789\",\"comparator\"\n\"2788\",\"acl\"\n\"2787\",\"keystore\"\n\"2787\",\"mergesort\"\n\"2785\",\"asp.net-core-3.1\"\n\"2784\",\"solaris\"\n\"2783\",\"h.264\"\n\"2781\",\"geopandas\"\n\"2780\",\"c-strings\"\n\"2775\",\"dos\"\n\"2775\",\"expect\"\n\"2772\",\"flex3\"\n\"2771\",\"reload\"\n\"2771\",\"undefined-behavior\"\n\"2771\",\"iphone-sdk-3.0\"\n\"2770\",\"castle-windsor\"\n\"2770\",\"graph-algorithm\"\n\"2768\",\"prototypejs\"\n\"2766\",\"ksh\"\n\"2764\",\"gfortran\"\n\"2764\",\"kivy-language\"\n\"2763\",\"procedure\"\n\"2762\",\"flutter-animation\"\n\"2761\",\"angular-ui\"\n\"2760\",\"twitter-oauth\"\n\"2759\",\"fedora\"\n\"2757\",\"voip\"\n\"2755\",\"android-softkeyboard\"\n\"2754\",\"domdocument\"\n\"2750\",\"jfreechart\"\n\"2747\",\"multiple-inheritance\"\n\"2747\",\"semantic-ui\"\n\"2746\",\"registration\"\n\"2745\",\"google-play-console\"\n\"2743\",\"php-7\"\n\"2742\",\"matching\"\n\"2742\",\"symfony-forms\"\n\"2740\",\"regex-group\"\n\"2739\",\"react-native-flatlist\"\n\"2739\",\"credentials\"\n\"2739\",\"surfaceview\"\n\"2738\",\"jetbrains-ide\"\n\"2738\",\"kendo-asp.net-mvc\"\n\"2738\",\"assert\"\n\"2737\",\"windows-server-2008\"\n\"2737\",\"percentage\"\n\"2736\",\"pca\"\n\"2732\",\"jooq\"\n\"2731\",\"azure-mobile-services\"\n\"2730\",\"paypal-ipn\"\n\"2729\",\"http-live-streaming\"\n\"2728\",\"icloud\"\n\"2728\",\"compiler-warnings\"\n\"2728\",\"amazon-elb\"\n\"2726\",\"tar\"\n\"2724\",\"firebug\"\n\"2722\",\"partial-views\"\n\"2719\",\"cllocationmanager\"\n\"2718\",\"operator-keyword\"\n\"2715\",\"dynamic-sql\"\n\"2712\",\"kinect\"\n\"2712\",\"positioning\"\n\"2711\",\"jscrollpane\"\n\"2711\",\"minikube\"\n\"2708\",\"reinforcement-learning\"\n\"2704\",\"jupyter-lab\"\n\"2701\",\"ado\"\n\"2698\",\"postback\"\n\"2694\",\"pine-script-v5\"\n\"2694\",\"nuxt3.js\"\n\"2693\",\"bing-maps\"\n\"2693\",\"variadic-functions\"\n\"2693\",\"mongodb-.net-driver\"\n\"2691\",\"tomcat8\"\n\"2691\",\"mesh\"\n\"2690\",\"show-hide\"\n\"2688\",\"simd\"\n\"2688\",\"restkit\"\n\"2687\",\"windows-ce\"\n\"2687\",\"continuous-deployment\"\n\"2687\",\"auto-increment\"\n\"2685\",\"shortcut\"\n\"2685\",\"istio\"\n\"2683\",\"amazon-vpc\"\n\"2682\",\"interceptor\"\n\"2681\",\"paramiko\"\n\"2679\",\"power-automate\"\n\"2679\",\"android-appcompat\"\n\"2676\",\"vert.x\"\n\"2675\",\"subdirectory\"\n\"2672\",\"helper\"\n\"2671\",\"masm\"\n\"2670\",\"checkout\"\n\"2670\",\"kml\"\n\"2669\",\"joomla2.5\"\n\"2669\",\"styling\"\n\"2668\",\"orientdb\"\n\"2667\",\"vlc\"\n\"2662\",\"monodevelop\"\n\"2661\",\"remote-debugging\"\n\"2660\",\"singly-linked-list\"\n\"2656\",\"android-virtual-device\"\n\"2656\",\"setstate\"\n\"2654\",\"audio-streaming\"\n\"2654\",\"angular2-forms\"\n\"2653\",\"jspdf\"\n\"2652\",\"simpledateformat\"\n\"2651\",\"hudson\"\n\"2650\",\"daemon\"\n\"2649\",\"derby\"\n\"2648\",\"amazon-route53\"\n\"2644\",\"hdf5\"\n\"2644\",\"d\"\n\"2643\",\"factory-bot\"\n\"2643\",\"mypy\"\n\"2641\",\"phonegap-build\"\n\"2639\",\"server-side\"\n\"2638\",\"watir\"\n\"2638\",\"haproxy\"\n\"2637\",\"erb\"\n\"2637\",\"32bit-64bit\"\n\"2635\",\"instantiation\"\n\"2635\",\"numeric\"\n\"2634\",\"laravel-livewire\"\n\"2633\",\"sensors\"\n\"2633\",\"ironpython\"\n\"2631\",\"priority-queue\"\n\"2630\",\"bison\"\n\"2630\",\"spring-aop\"\n\"2630\",\"email-attachments\"\n\"2629\",\"nsis\"\n\"2628\",\"linear-programming\"\n\"2627\",\"nstimer\"\n\"2626\",\"amcharts\"\n\"2625\",\"jsonb\"\n\"2624\",\"depth-first-search\"\n\"2624\",\"objective-c-blocks\"\n\"2623\",\"pandoc\"\n\"2622\",\"emoji\"\n\"2621\",\"dao\"\n\"2619\",\"keypress\"\n\"2619\",\"logical-operators\"\n\"2617\",\"break\"\n\"2617\",\"exit\"\n\"2616\",\"react-hook-form\"\n\"2616\",\"web-audio-api\"\n\"2614\",\"lubridate\"\n\"2613\",\"picturebox\"\n\"2611\",\"in-app-billing\"\n\"2611\",\"lazy-evaluation\"\n\"2609\",\"fosuserbundle\"\n\"2607\",\"azure-cognitive-services\"\n\"2607\",\"messaging\"\n\"2606\",\"file-get-contents\"\n\"2605\",\"feed\"\n\"2605\",\"cicd\"\n\"2605\",\"rmi\"\n\"2604\",\"database-performance\"\n\"2602\",\"code-signing\"\n\"2601\",\"apex\"\n\"2601\",\"pascal\"\n\"2600\",\"free\"\n\"2597\",\"text-mining\"\n\"2597\",\"webdriverwait\"\n\"2596\",\"sqoop\"\n\"2596\",\"delphi-xe2\"\n\"2595\",\"guzzle\"\n\"2592\",\"lumen\"\n\"2592\",\"el\"\n\"2592\",\"stringr\"\n\"2591\",\"dbcontext\"\n\"2591\",\"attributeerror\"\n\"2588\",\"event-listener\"\n\"2587\",\"add-in\"\n\"2587\",\"accelerometer\"\n\"2587\",\"sql-server-2000\"\n\"2587\",\"redux-thunk\"\n\"2586\",\"query-builder\"\n\"2583\",\"ipv6\"\n\"2582\",\"deprecated\"\n\"2581\",\"python-3.4\"\n\"2581\",\"cross-validation\"\n\"2581\",\"paste\"\n\"2581\",\"maya\"\n\"2579\",\"lotus-domino\"\n\"2578\",\"mapbox-gl-js\"\n\"2577\",\"apache-storm\"\n\"2577\",\"contenteditable\"\n\"2577\",\"axapta\"\n\"2577\",\".net-2.0\"\n\"2577\",\"zxing\"\n\"2576\",\"mootools\"\n\"2573\",\"resultset\"\n\"2572\",\"owl\"\n\"2572\",\"azure-api-management\"\n\"2570\",\"tcpclient\"\n\"2570\",\"qemu\"\n\"2568\",\"constexpr\"\n\"2567\",\"pydev\"\n\"2561\",\"rpc\"\n\"2561\",\"android-videoview\"\n\"2557\",\"str-replace\"\n\"2555\",\"uialertview\"\n\"2554\",\"multiplication\"\n\"2554\",\"nav\"\n\"2551\",\"barcode-scanner\"\n\"2551\",\"tostring\"\n\"2548\",\"menuitem\"\n\"2545\",\"wampserver\"\n\"2545\",\"abstract\"\n\"2545\",\"iostream\"\n\"2542\",\"picasso\"\n\"2540\",\"hsqldb\"\n\"2540\",\"android-resources\"\n\"2539\",\"development-environment\"\n\"2537\",\"channel\"\n\"2536\",\"ravendb\"\n\"2535\",\"image-segmentation\"\n\"2534\",\"swap\"\n\"2531\",\"bulkinsert\"\n\"2531\",\"ios11\"\n\"2530\",\"amqp\"\n\"2530\",\"tk-toolkit\"\n\"2529\",\"css-transforms\"\n\"2522\",\"intersection\"\n\"2522\",\"nodemailer\"\n\"2521\",\"show\"\n\"2520\",\"scaling\"\n\"2519\",\"androidx\"\n\"2519\",\"google-chrome-app\"\n\"2519\",\"restsharp\"\n\"2518\",\"photoshop\"\n\"2517\",\"decision-tree\"\n\"2516\",\"knex.js\"\n\"2514\",\"windows-forms-designer\"\n\"2514\",\"twilio-api\"\n\"2513\",\"opacity\"\n\"2513\",\"core-bluetooth\"\n\"2513\",\"google-ads-api\"\n\"2511\",\"cloudera\"\n\"2510\",\"mysql-connector\"\n\"2509\",\"ghc\"\n\"2509\",\"powerapps\"\n\"2509\",\"resteasy\"\n\"2508\",\"rtsp\"\n\"2508\",\"desktop\"\n\"2506\",\"nginx-ingress\"\n\"2504\",\"datatemplate\"\n\"2502\",\"android-databinding\"\n\"2499\",\"adt\"\n\"2497\",\"wsgi\"\n\"2496\",\"mechanize\"\n\"2495\",\"meta-tags\"\n\"2494\",\"python-decorators\"\n\"2493\",\"core-audio\"\n\"2492\",\"azure-virtual-machine\"\n\"2492\",\"esp8266\"\n\"2489\",\"telnet\"\n\"2489\",\"sandbox\"\n\"2489\",\"google-cloud-endpoints\"\n\"2488\",\"symlink\"\n\"2488\",\"wkhtmltopdf\"\n\"2487\",\"relative-path\"\n\"2487\",\"title\"\n\"2486\",\"lombok\"\n\"2485\",\"nsdata\"\n\"2485\",\"spark-structured-streaming\"\n\"2484\",\"nsurlsession\"\n\"2484\",\"archive\"\n\"2482\",\"gsub\"\n\"2482\",\"video-capture\"\n\"2479\",\"firebase-hosting\"\n\"2479\",\"mouseover\"\n\"2478\",\"redux-saga\"\n\"2474\",\"laravel-5.5\"\n\"2474\",\"javac\"\n\"2473\",\"slick\"\n\"2473\",\"model-binding\"\n\"2472\",\"trim\"\n\"2472\",\"nullable\"\n\"2471\",\"firebase-analytics\"\n\"2471\",\"rspec-rails\"\n\"2469\",\"icalendar\"\n\"2468\",\"finance\"\n\"2467\",\"rsync\"\n\"2464\",\"implicit-conversion\"\n\"2463\",\"popen\"\n\"2463\",\"systemd\"\n\"2463\",\"plyr\"\n\"2463\",\"communication\"\n\"2461\",\"gensim\"\n\"2460\",\"var\"\n\"2457\",\"keyword\"\n\"2457\",\"glfw\"\n\"2454\",\"lightbox\"\n\"2451\",\"minify\"\n\"2450\",\"xml-namespaces\"\n\"2450\",\"spring-jdbc\"\n\"2450\",\"code-injection\"\n\"2450\",\"speech-to-text\"\n\"2449\",\"python-xarray\"\n\"2449\",\"statusbar\"\n\"2446\",\"jmx\"\n\"2445\",\"theano\"\n\"2443\",\"long-integer\"\n\"2442\",\"casperjs\"\n\"2436\",\"ngfor\"\n\"2436\",\"android-lifecycle\"\n\"2436\",\"sphinx\"\n\"2435\",\"jcombobox\"\n\"2435\",\"executable-jar\"\n\"2434\",\"mailchimp\"\n\"2433\",\"terraform-provider-azure\"\n\"2433\",\"getelementbyid\"\n\"2432\",\"datastax\"\n\"2432\",\"liferay-6\"\n\"2431\",\"ibm-watson\"\n\"2431\",\"offline\"\n\"2429\",\"pcre\"\n\"2427\",\"installshield\"\n\"2426\",\"vulkan\"\n\"2425\",\"copy-constructor\"\n\"2425\",\"race-condition\"\n\"2423\",\"web3js\"\n\"2417\",\"tiff\"\n\"2416\",\"apache-axis\"\n\"2416\",\"ada\"\n\"2413\",\"symfony-1.4\"\n\"2413\",\"openlayers-3\"\n\"2411\",\"jena\"\n\"2411\",\"android-button\"\n\"2411\",\"prompt\"\n\"2410\",\"vtk\"\n\"2410\",\"wysiwyg\"\n\"2409\",\"apple-watch\"\n\"2408\",\"serilog\"\n\"2405\",\"ejabberd\"\n\"2404\",\"date-format\"\n\"2403\",\"spock\"\n\"2401\",\"sidekiq\"\n\"2401\",\"zlib\"\n\"2401\",\"user-agent\"\n\"2399\",\"wagtail\"\n\"2398\",\"snmp\"\n\"2396\",\"redux-form\"\n\"2395\",\"windows-10-universal\"\n\"2394\",\"hybrid-mobile-app\"\n\"2392\",\"json-deserialization\"\n\"2392\",\"azure-table-storage\"\n\"2391\",\"velocity\"\n\"2391\",\"elf\"\n\"2389\",\"readfile\"\n\"2388\",\"models\"\n\"2387\",\"flowtype\"\n\"2387\",\"numba\"\n\"2387\",\"reset\"\n\"2386\",\"code-first\"\n\"2385\",\"actionmailer\"\n\"2384\",\"marionette\"\n\"2384\",\"ehcache\"\n\"2383\",\"dropbox-api\"\n\"2380\",\"assemblies\"\n\"2380\",\"resolution\"\n\"2378\",\"indexeddb\"\n\"2377\",\"utc\"\n\"2376\",\"combinatorics\"\n\"2374\",\"fibonacci\"\n\"2371\",\"template-meta-programming\"\n\"2371\",\"virtualization\"\n\"2370\",\"avr\"\n\"2369\",\"wpfdatagrid\"\n\"2368\",\"boto\"\n\"2368\",\"angular-directive\"\n\"2367\",\"rust-cargo\"\n\"2367\",\"rpm\"\n\"2365\",\"sse\"\n\"2365\",\"client-side\"\n\"2365\",\"flask-wtforms\"\n\"2365\",\"content-type\"\n\"2365\",\"polymer-1.0\"\n\"2365\",\"glibc\"\n\"2363\",\"paging\"\n\"2357\",\"coroutine\"\n\"2357\",\"http-status-code-403\"\n\"2356\",\"yeoman\"\n\"2354\",\"stack-trace\"\n\"2352\",\"visual-studio-debugging\"\n\"2351\",\"omniauth\"\n\"2350\",\"cloudkit\"\n\"2350\",\"unix-timestamp\"\n\"2350\",\"uibarbuttonitem\"\n\"2347\",\"netcdf\"\n\"2346\",\"apache-httpclient-4.x\"\n\"2345\",\"reportviewer\"\n\"2345\",\"ghostscript\"\n\"2345\",\"openstack\"\n\"2345\",\"dreamweaver\"\n\"2344\",\"inner-classes\"\n\"2344\",\"ejb-3.0\"\n\"2344\",\"qtquick2\"\n\"2343\",\"next-auth\"\n\"2343\",\"urlencode\"\n\"2343\",\"bcrypt\"\n\"2341\",\"godot\"\n\"2340\",\"cplex\"\n\"2338\",\"ram\"\n\"2334\",\"logcat\"\n\"2333\",\"dllimport\"\n\"2332\",\"streamlit\"\n\"2331\",\"android-glide\"\n\"2330\",\"splunk\"\n\"2329\",\"unreal-engine4\"\n\"2328\",\"android-cardview\"\n\"2328\",\"executorservice\"\n\"2326\",\"roblox\"\n\"2326\",\"iis-express\"\n\"2325\",\"corda\"\n\"2324\",\"arabic\"\n\"2322\",\"firebase-admin\"\n\"2322\",\"subscription\"\n\"2320\",\"uilocalnotification\"\n\"2319\",\"pyserial\"\n\"2318\",\"bytecode\"\n\"2318\",\"react-apollo\"\n\"2318\",\"forecasting\"\n\"2317\",\"capacitor\"\n\"2316\",\"vps\"\n\"2316\",\"dependency-properties\"\n\"2316\",\"winsock\"\n\"2316\",\"shuffle\"\n\"2314\",\"ssrs-2008-r2\"\n\"2314\",\"scalability\"\n\"2314\",\"pm2\"\n\"2314\",\"string-matching\"\n\"2313\",\"ajaxcontroltoolkit\"\n\"2312\",\"clojurescript\"\n\"2312\",\"uitabbar\"\n\"2312\",\"traefik\"\n\"2311\",\"amp-html\"\n\"2310\",\"terminology\"\n\"2305\",\"fpdf\"\n\"2305\",\"specflow\"\n\"2304\",\"nhibernate-mapping\"\n\"2301\",\"calculated-columns\"\n\"2300\",\"silverstripe\"\n\"2297\",\"jersey-2.0\"\n\"2297\",\"flyway\"\n\"2297\",\"lifetime\"\n\"2296\",\"build-process\"\n\"2296\",\"odoo-8\"\n\"2295\",\"android-mapview\"\n\"2294\",\"azure-cli\"\n\"2294\",\"interpreter\"\n\"2294\",\"itunes\"\n\"2293\",\"division\"\n\"2293\",\"python-3.8\"\n\"2293\",\"word2vec\"\n\"2292\",\"nsfetchedresultscontroller\"\n\"2290\",\"swiper.js\"\n\"2290\",\"hashset\"\n\"2288\",\"api-design\"\n\"2287\",\"kernel-module\"\n\"2286\",\"putty\"\n\"2284\",\"tracking\"\n\"2284\",\"glut\"\n\"2283\",\"countdown\"\n\"2281\",\"ddl\"\n\"2279\",\"soap-client\"\n\"2274\",\"pydantic\"\n\"2274\",\"azure-webjobs\"\n\"2273\",\"netlify\"\n\"2272\",\"fiddler\"\n\"2269\",\"neovim\"\n\"2268\",\"web-worker\"\n\"2267\",\"laravel-7\"\n\"2266\",\"gpgpu\"\n\"2266\",\"amazon-ses\"\n\"2266\",\"dropzone.js\"\n\"2264\",\"nsattributedstring\"\n\"2261\",\"ibeacon\"\n\"2260\",\"distributed\"\n\"2259\",\"html-helper\"\n\"2257\",\"sidebar\"\n\"2256\",\"readline\"\n\"2254\",\"photo\"\n\"2254\",\"child-process\"\n\"2254\",\"pull-request\"\n\"2254\",\"keyboard-events\"\n\"2254\",\"informix\"\n\"2254\",\"breadth-first-search\"\n\"2253\",\"uninstallation\"\n\"2253\",\"rest-assured\"\n\"2252\",\"string-comparison\"\n\"2251\",\"configuration-files\"\n\"2250\",\"infinite-scroll\"\n\"2250\",\"shortcode\"\n\"2248\",\"jsf-2.2\"\n\"2248\",\"metal\"\n\"2246\",\"classnotfoundexception\"\n\"2246\",\"css-tables\"\n\"2246\",\"node-red\"\n\"2244\",\"webapi\"\n\"2243\",\"python-polars\"\n\"2243\",\"put\"\n\"2243\",\"line-breaks\"\n\"2241\",\"pygtk\"\n\"2240\",\"arduino-uno\"\n\"2239\",\"unique-ptr\"\n\"2239\",\"rebase\"\n\"2238\",\"nservicebus\"\n\"2237\",\"apache-spark-mllib\"\n\"2235\",\"gdal\"\n\"2234\",\"pseudo-element\"\n\"2233\",\"video.js\"\n\"2233\",\"laravel-routing\"\n\"2231\",\"yii2-advanced-app\"\n\"2231\",\"sap-commerce-cloud\"\n\"2230\",\"tensorflow-lite\"\n\"2230\",\"tf.keras\"\n\"2229\",\"nstableview\"\n\"2228\",\"mef\"\n\"2227\",\"tabcontrol\"\n\"2225\",\"jquery-ui-dialog\"\n\"2225\",\"andengine\"\n\"2223\",\"clearcase\"\n\"2223\",\"spring-data-rest\"\n\"2223\",\"emgucv\"\n\"2221\",\"selenium-ide\"\n\"2219\",\"gesture\"\n\"2219\",\"parallax\"\n\"2218\",\"dry\"\n\"2217\",\"kohana\"\n\"2216\",\"pyramid\"\n\"2215\",\"dsl\"\n\"2214\",\"inline-assembly\"\n\"2214\",\"shopping-cart\"\n\"2210\",\"equality\"\n\"2210\",\"text-editor\"\n\"2210\",\"logout\"\n\"2210\",\"mpmovieplayercontroller\"\n\"2210\",\"static-analysis\"\n\"2207\",\"scapy\"\n\"2206\",\"ontology\"\n\"2205\",\"bloc\"\n\"2205\",\"osx-lion\"\n\"2205\",\"noclassdeffounderror\"\n\"2204\",\"angular2-services\"\n\"2202\",\"tcpdf\"\n\"2200\",\"implementation\"\n\"2200\",\"searchview\"\n\"2199\",\"vscode-debugger\"\n\"2198\",\"grid-layout\"\n\"2197\",\"image-resizing\"\n\"2196\",\"elementor\"\n\"2195\",\"android-theme\"\n\"2194\",\"popover\"\n\"2194\",\"drupal-modules\"\n\"2194\",\"itext7\"\n\"2188\",\"birt\"\n\"2188\",\"omnet++\"\n\"2186\",\"numerical-methods\"\n\"2186\",\"storybook\"\n\"2184\",\"detection\"\n\"2184\",\"gdi\"\n\"2184\",\"ms-access-2013\"\n\"2183\",\"phaser-framework\"\n\"2182\",\"kdb\"\n\"2177\",\"getter-setter\"\n\"2172\",\"android-bluetooth\"\n\"2171\",\"masstransit\"\n\"2169\",\"google-apps\"\n\"2168\",\"android-6.0-marshmallow\"\n\"2167\",\"centering\"\n\"2166\",\"android-testing\"\n\"2165\",\"git-commit\"\n\"2164\",\"send\"\n\"2163\",\"bufferedimage\"\n\"2162\",\"rx-swift\"\n\"2162\",\"nslayoutconstraint\"\n\"2162\",\"torch\"\n\"2160\",\"mixins\"\n\"2159\",\"joomla3.0\"\n\"2159\",\"ioc-container\"\n\"2159\",\"plotly-python\"\n\"2159\",\"endianness\"\n\"2157\",\"drag\"\n\"2155\",\"oracle-adf\"\n\"2154\",\"kql\"\n\"2154\",\"ifstream\"\n\"2153\",\"lamp\"\n\"2151\",\"dto\"\n\"2149\",\"sizeof\"\n\"2148\",\"move-semantics\"\n\"2148\",\"odoo-10\"\n\"2146\",\"quotes\"\n\"2146\",\"testflight\"\n\"2145\",\"cql\"\n\"2144\",\"analysis\"\n\"2143\",\"apollo-server\"\n\"2141\",\"org-mode\"\n\"2138\",\"anonymous-function\"\n\"2137\",\"social-networking\"\n\"2136\",\"lag\"\n\"2135\",\"explode\"\n\"2134\",\"stringbuilder\"\n\"2133\",\"web2py\"\n\"2127\",\"endpoint\"\n\"2123\",\"clickhouse\"\n\"2122\",\"ios13\"\n\"2121\",\"jquery-ui-autocomplete\"\n\"2120\",\"database-administration\"\n\"2120\",\"spss\"\n\"2119\",\"large-data\"\n\"2118\",\"multi-select\"\n\"2118\",\"hidden\"\n\"2117\",\"langchain\"\n\"2117\",\"angular-promise\"\n\"2117\",\"android-tabhost\"\n\"2115\",\"delete-file\"\n\"2114\",\"spring-test\"\n\"2113\",\"android-fragmentactivity\"\n\"2111\",\"indy\"\n\"2111\",\"expression-trees\"\n\"2111\",\"layer\"\n\"2110\",\"uisplitviewcontroller\"\n\"2109\",\"tensorflow-datasets\"\n\"2109\",\"reactive\"\n\"2108\",\"flex-lexer\"\n\"2108\",\"kineticjs\"\n\"2108\",\"onload\"\n\"2106\",\"nginx-config\"\n\"2105\",\"sitemap\"\n\"2102\",\"fatal-error\"\n\"2102\",\"google-api-php-client\"\n\"2102\",\"npgsql\"\n\"2102\",\"toast\"\n\"2102\",\"genetic-algorithm\"\n\"2101\",\"confluent-platform\"\n\"2101\",\"vb.net-2010\"\n\"2099\",\"sml\"\n\"2099\",\"varnish\"\n\"2097\",\"adfs\"\n\"2097\",\"ruby-on-rails-6\"\n\"2096\",\"placeholder\"\n\"2096\",\"opencv3.0\"\n\"2096\",\"r-caret\"\n\"2094\",\"jwplayer\"\n\"2094\",\"convolution\"\n\"2094\",\"weblogic12c\"\n\"2092\",\"servlet-filters\"\n\"2092\",\"soundcloud\"\n\"2090\",\"internet-explorer-6\"\n\"2087\",\"azure-iot-hub\"\n\"2086\",\"fgets\"\n\"2086\",\"vi\"\n\"2084\",\"python-module\"\n\"2084\",\"cakephp-1.3\"\n\"2083\",\"console.log\"\n\"2083\",\"osx-yosemite\"\n\"2083\",\"pywin32\"\n\"2082\",\"angular-services\"\n\"2082\",\"arm64\"\n\"2081\",\"firefox-addon-sdk\"\n\"2081\",\"bit-shift\"\n\"2081\",\"actor\"\n\"2080\",\"hierarchical-data\"\n\"2076\",\"business-intelligence\"\n\"2075\",\"sonata-admin\"\n\"2073\",\"impala\"\n\"2072\",\"python-re\"\n\"2071\",\"kubernetes-pod\"\n\"2071\",\"shadow\"\n\"2071\",\"angular-components\"\n\"2071\",\"web.xml\"\n\"2070\",\"clang++\"\n\"2070\",\"fadein\"\n\"2070\",\"uipageviewcontroller\"\n\"2070\",\"directive\"\n\"2069\",\"caliburn.micro\"\n\"2069\",\"android-contacts\"\n\"2068\",\"clock\"\n\"2068\",\"sql-server-data-tools\"\n\"2067\",\"angular-forms\"\n\"2066\",\"recursive-query\"\n\"2063\",\"runnable\"\n\"2062\",\"flot\"\n\"2059\",\"exc-bad-access\"\n\"2056\",\"encapsulation\"\n\"2055\",\"glm\"\n\"2054\",\"raku\"\n\"2053\",\"projection\"\n\"2053\",\"combine\"\n\"2053\",\"linechart\"\n\"2052\",\"unzip\"\n\"2052\",\"azure-data-explorer\"\n\"2051\",\"fastcgi\"\n\"2049\",\"mime\"\n\"2047\",\"lets-encrypt\"\n\"2047\",\"point\"\n\"2047\",\"httpd.conf\"\n\"2045\",\"string-concatenation\"\n\"2045\",\"iis-8\"\n\"2044\",\"tumblr\"\n\"2044\",\"exoplayer\"\n\"2044\",\"tkinter-canvas\"\n\"2043\",\"modeling\"\n\"2043\",\"android-tablayout\"\n\"2043\",\"audio-recording\"\n\"2043\",\"hadoop2\"\n\"2043\",\"restore\"\n\"2042\",\"directshow\"\n\"2040\",\"phalcon\"\n\"2040\",\"jtextarea\"\n\"2040\",\"plesk\"\n\"2039\",\"supabase\"\n\"2039\",\"shapefile\"\n\"2039\",\"hashcode\"\n\"2039\",\"powermock\"\n\"2036\",\"signature\"\n\"2036\",\"gerrit\"\n\"2035\",\"jstree\"\n\"2034\",\"looker-studio\"\n\"2033\",\"default-value\"\n\"2032\",\"android-wifi\"\n\"2030\",\"custom-wordpress-pages\"\n\"2028\",\"subplot\"\n\"2026\",\"azure-eventhub\"\n\"2026\",\"dt\"\n\"2026\",\"scenebuilder\"\n\"2025\",\"jdbctemplate\"\n\"2025\",\"jna\"\n\"2025\",\"freebsd\"\n\"2023\",\"appdelegate\"\n\"2021\",\"guid\"\n\"2021\",\"spring-transactions\"\n\"2021\",\"querydsl\"\n\"2020\",\"chromecast\"\n\"2020\",\"java-ee-6\"\n\"2016\",\"lifecycle\"\n\"2016\",\"sticky\"\n\"2015\",\"dompdf\"\n\"2014\",\"data-conversion\"\n\"2014\",\"xctest\"\n\"2013\",\"react-select\"\n\"2013\",\"dropwizard\"\n\"2010\",\"traversal\"\n\"2010\",\"iptables\"\n\"2007\",\"azure-pipelines-release-pipeline\"\n\"2004\",\"dispose\"\n\"2003\",\"conditional-operator\"\n\"1999\",\"xts\"\n\"1999\",\"r-sf\"\n\"1999\",\"cqrs\"\n\"1998\",\"sentiment-analysis\"\n\"1998\",\"jit\"\n\"1998\",\"midi\"\n\"1998\",\"difference\"\n\"1997\",\"azure-ad-graph-api\"\n\"1995\",\"lldb\"\n\"1995\",\"biginteger\"\n\"1994\",\"xlsxwriter\"\n\"1993\",\"sha256\"\n\"1993\",\"mmap\"\n\"1992\",\"gsap\"\n\"1991\",\"google-authentication\"\n\"1990\",\"react-router-v4\"\n\"1990\",\"google-workspace\"\n\"1990\",\"zend-form\"\n\"1989\",\"slick.js\"\n\"1989\",\"ssh-keys\"\n\"1989\",\"software-design\"\n\"1989\",\"rally\"\n\"1988\",\"tensorboard\"\n\"1988\",\"modulo\"\n\"1987\",\"indexof\"\n\"1987\",\"space\"\n\"1986\",\"directx-11\"\n\"1984\",\"react-native-navigation\"\n\"1983\",\"remote-server\"\n\"1983\",\"osx-mavericks\"\n\"1982\",\"typeclass\"\n\"1982\",\"redmine\"\n\"1980\",\"esb\"\n\"1980\",\"ode\"\n\"1978\",\"keychain\"\n\"1978\",\"arangodb\"\n\"1975\",\"p2p\"\n\"1975\",\"azure-devops-rest-api\"\n\"1975\",\"text-processing\"\n\"1974\",\"go-gorm\"\n\"1973\",\"semantic-web\"\n\"1971\",\"internet-explorer-10\"\n\"1971\",\"android-context\"\n\"1970\",\"code-behind\"\n\"1968\",\"hana\"\n\"1968\",\"msmq\"\n\"1967\",\"getjson\"\n\"1966\",\"pgadmin\"\n\"1965\",\"powershell-4.0\"\n\"1964\",\"unmarshalling\"\n\"1964\",\"target\"\n\"1963\",\"mvp\"\n\"1963\",\"packaging\"\n\"1962\",\"volatile\"\n\"1962\",\"spring-jms\"\n\"1961\",\"azure-machine-learning-service\"\n\"1959\",\"nested-forms\"\n\"1956\",\"smartcard\"\n\"1955\",\"atlassian-sourcetree\"\n\"1955\",\"actionscript-2\"\n\"1954\",\"mustache\"\n\"1954\",\"soa\"\n\"1953\",\"dataweave\"\n\"1953\",\"opera\"\n\"1953\",\"streamreader\"\n\"1952\",\"xcode4.2\"\n\"1951\",\"create-table\"\n\"1951\",\"touch-event\"\n\"1950\",\"navigationbar\"\n\"1949\",\"vimeo\"\n\"1949\",\"overlap\"\n\"1949\",\"android-dialogfragment\"\n\"1948\",\"shortest-path\"\n\"1948\",\"outlook-web-addins\"\n\"1948\",\"multiline\"\n\"1946\",\"yolo\"\n\"1946\",\"code-snippets\"\n\"1946\",\"regex-negation\"\n\"1945\",\"blur\"\n\"1943\",\"pseudocode\"\n\"1942\",\"git-svn\"\n\"1941\",\"typoscript\"\n\"1940\",\"equation\"\n\"1940\",\"compass-sass\"\n\"1939\",\"preg-match-all\"\n\"1938\",\"kafka-producer-api\"\n\"1938\",\"mpandroidchart\"\n\"1937\",\"autodesk-viewer\"\n\"1937\",\"predicate\"\n\"1933\",\"jacoco\"\n\"1932\",\"yacc\"\n\"1932\",\"covariance\"\n\"1932\",\"c99\"\n\"1932\",\"dijkstra\"\n\"1932\",\"bean-validation\"\n\"1932\",\"query-performance\"\n\"1931\",\"x509\"\n\"1929\",\"embedded-resource\"\n\"1928\",\"lm\"\n\"1928\",\"imagick\"\n\"1927\",\"void\"\n\"1927\",\"init\"\n\"1926\",\"public-key-encryption\"\n\"1925\",\"multipart\"\n\"1925\",\"transparent\"\n\"1925\",\".net-8.0\"\n\"1924\",\"epplus\"\n\"1923\",\"oozie\"\n\"1923\",\"scikit-image\"\n\"1922\",\"laravel-9\"\n\"1922\",\"session-state\"\n\"1921\",\"python-datetime\"\n\"1921\",\"django-serializer\"\n\"1920\",\"azure-data-lake\"\n\"1920\",\"android-preferences\"\n\"1919\",\"core-plot\"\n\"1919\",\"delete-row\"\n\"1918\",\"django-authentication\"\n\"1917\",\"mobile-application\"\n\"1917\",\"angular9\"\n\"1916\",\"android-styles\"\n\"1912\",\"hyperledger-composer\"\n\"1912\",\"uart\"\n\"1910\",\"enumeration\"\n\"1909\",\"contact-form-7\"\n\"1909\",\"laravel-6\"\n\"1909\",\"quickbooks\"\n\"1907\",\"win32com\"\n\"1907\",\"substitution\"\n\"1906\",\"slurm\"\n\"1906\",\"checksum\"\n\"1906\",\"broadcast\"\n\"1905\",\"tvos\"\n\"1905\",\"builder\"\n\"1905\",\"diagram\"\n\"1905\",\"azure-rm-template\"\n\"1903\",\"fixed\"\n\"1903\",\"wcf-data-services\"\n\"1901\",\"mobx\"\n\"1901\",\"android-library\"\n\"1900\",\"form-data\"\n\"1900\",\"monogame\"\n\"1897\",\"android-pendingintent\"\n\"1896\",\"pyautogui\"\n\"1895\",\"getter\"\n\"1895\",\"named-pipes\"\n\"1895\",\"joptionpane\"\n\"1895\",\"ncurses\"\n\"1895\",\"elm\"\n\"1894\",\"thrift\"\n\"1893\",\"angular-ui-grid\"\n\"1892\",\"saxon\"\n\"1892\",\"allocation\"\n\"1891\",\"server-sent-events\"\n\"1890\",\"interactive\"\n\"1889\",\"http2\"\n\"1888\",\"qmake\"\n\"1886\",\"h2o\"\n\"1886\",\"google-translate\"\n\"1885\",\"firebaseui\"\n\"1885\",\"synchronized\"\n\"1884\",\"tablesorter\"\n\"1883\",\"selenium-grid\"\n\"1883\",\"portlet\"\n\"1883\",\"code-analysis\"\n\"1883\",\"powerpivot\"\n\"1883\",\"preprocessor\"\n\"1881\",\"counting\"\n\"1879\",\"serversocket\"\n\"1879\",\"flutter-test\"\n\"1879\",\"prestashop-1.6\"\n\"1878\",\"cin\"\n\"1877\",\"similarity\"\n\"1877\",\"tmux\"\n\"1877\",\"css-shapes\"\n\"1876\",\"xls\"\n\"1876\",\"messagebox\"\n\"1874\",\"circleci\"\n\"1874\",\"testcafe\"\n\"1873\",\"fill\"\n\"1872\",\"adsense\"\n\"1872\",\"heap\"\n\"1870\",\"bluebird\"\n\"1869\",\"production-environment\"\n\"1869\",\"snapshot\"\n\"1869\",\"countif\"\n\"1869\",\"schedule\"\n\"1868\",\"windows-phone-7.1\"\n\"1867\",\"aws-fargate\"\n\"1866\",\"alpine-linux\"\n\"1865\",\"jsonpath\"\n\"1865\",\"database-replication\"\n\"1864\",\"angular-router\"\n\"1864\",\"dynamic-arrays\"\n\"1864\",\"com-interop\"\n\"1863\",\"autoscaling\"\n\"1862\",\"scp\"\n\"1861\",\"collation\"\n\"1860\",\"floating-action-button\"\n\"1860\",\"lint\"\n\"1859\",\"ecmascript-5\"\n\"1857\",\"ansible-2.x\"\n\"1857\",\"tomcat6\"\n\"1856\",\"ffi\"\n\"1854\",\"build-automation\"\n\"1853\",\"adodb\"\n\"1853\",\"game-center\"\n\"1851\",\"eof\"\n\"1848\",\"yum\"\n\"1847\",\"face-recognition\"\n\"1847\",\"bubble-sort\"\n\"1847\",\"parse-server\"\n\"1846\",\"dc.js\"\n\"1846\",\"sfinae\"\n\"1845\",\"rtf\"\n\"1845\",\"provisioning-profile\"\n\"1844\",\"host\"\n\"1844\",\"exif\"\n\"1843\",\"coded-ui-tests\"\n\"1843\",\"facebook-android-sdk\"\n\"1842\",\"sha1\"\n\"1842\",\"doubly-linked-list\"\n\"1842\",\"react-admin\"\n\"1841\",\"file-transfer\"\n\"1840\",\"observer-pattern\"\n\"1840\",\"lucene.net\"\n\"1839\",\"laravel-5.6\"\n\"1839\",\"cout\"\n\"1838\",\"pypi\"\n\"1838\",\"pojo\"\n\"1837\",\"jlist\"\n\"1837\",\"operator-precedence\"\n\"1837\",\"progressdialog\"\n\"1836\",\"jmeter-plugins\"\n\"1835\",\"xsl-fo\"\n\"1835\",\"react-query\"\n\"1835\",\"instruments\"\n\"1835\",\"volume\"\n\"1835\",\"informatica\"\n\"1835\",\"lex\"\n\"1835\",\"cabal\"\n\"1833\",\"laravel-5.8\"\n\"1832\",\"bukkit\"\n\"1832\",\"amazon-kinesis\"\n\"1831\",\"htmlunit\"\n\"1829\",\"aix\"\n\"1829\",\"micronaut\"\n\"1829\",\"postgresql-9.1\"\n\"1829\",\"android-viewmodel\"\n\"1828\",\"bootloader\"\n\"1827\",\"uisegmentedcontrol\"\n\"1826\",\"gpio\"\n\"1826\",\"entitymanager\"\n\"1826\",\"html2canvas\"\n\"1826\",\"java-time\"\n\"1824\",\"src\"\n\"1823\",\"gaussian\"\n\"1823\",\"permalinks\"\n\"1822\",\"slide\"\n\"1821\",\"nvd3.js\"\n\"1820\",\"static-linking\"\n\"1820\",\"using\"\n\"1819\",\"drupal-views\"\n\"1819\",\"openldap\"\n\"1819\",\"angular2-directives\"\n\"1819\",\"webdriver-io\"\n\"1818\",\"favicon\"\n\"1818\",\"mongoengine\"\n\"1817\",\"aws-appsync\"\n\"1816\",\"memory-address\"\n\"1815\",\"facelets\"\n\"1814\",\"apache-commons\"\n\"1813\",\"mathjax\"\n\"1813\",\"getline\"\n\"1813\",\"licensing\"\n\"1812\",\"snakemake\"\n\"1811\",\"profile\"\n\"1809\",\"bayesian\"\n\"1808\",\"lme4\"\n\"1808\",\"windows-7-x64\"\n\"1808\",\"cpu-registers\"\n\"1808\",\"http-status-code-301\"\n\"1808\",\"imagebutton\"\n\"1808\",\"alpha\"\n\"1807\",\"phonegap\"\n\"1807\",\"loader\"\n\"1807\",\"colorbox\"\n\"1807\",\"gnupg\"\n\"1806\",\"syncfusion\"\n\"1805\",\"jquery-ui-draggable\"\n\"1804\",\"kivymd\"\n\"1803\",\"implicit\"\n\"1803\",\"valueerror\"\n\"1803\",\"theory\"\n\"1802\",\"i2c\"\n\"1801\",\"windows-server-2008-r2\"\n\"1799\",\"facet\"\n\"1799\",\"boolean-logic\"\n\"1797\",\"invoke\"\n\"1796\",\"githooks\"\n\"1796\",\"bert-language-model\"\n\"1795\",\"uiviewanimation\"\n\"1794\",\"laravel-artisan\"\n\"1794\",\"extjs4.1\"\n\"1794\",\"protobuf-net\"\n\"1793\",\"java-web-start\"\n\"1792\",\"ecto\"\n\"1791\",\"rhel\"\n\"1791\",\"uibezierpath\"\n\"1791\",\"titanium-mobile\"\n\"1790\",\"facebook-ios-sdk\"\n\"1790\",\"exists\"\n\"1788\",\"spring-data-neo4j\"\n\"1787\",\"android-scrollview\"\n\"1785\",\"palindrome\"\n\"1785\",\"facebook-c#-sdk\"\n\"1783\",\"mulesoft\"\n\"1782\",\"kentico\"\n\"1781\",\"tampermonkey\"\n\"1779\",\"path-finding\"\n\"1779\",\"java-io\"\n\"1778\",\"gettext\"\n\"1778\",\"yui\"\n\"1777\",\"bitcoin\"\n\"1777\",\"django-celery\"\n\"1777\",\"azure-cognitive-search\"\n\"1777\",\"knn\"\n\"1777\",\"arcore\"\n\"1776\",\"categorical-data\"\n\"1776\",\"android-bitmap\"\n\"1775\",\"fwrite\"\n\"1775\",\"wikipedia\"\n\"1775\",\"sax\"\n\"1775\",\"qt-designer\"\n\"1775\",\"training-data\"\n\"1774\",\"jsdoc\"\n\"1774\",\"azure-pipelines-yaml\"\n\"1773\",\"super\"\n\"1772\",\"rx-android\"\n\"1772\",\"simulator\"\n\"1772\",\"setter\"\n\"1770\",\"goroutine\"\n\"1769\",\"bitnami\"\n\"1769\",\"keylistener\"\n\"1769\",\"libreoffice\"\n\"1768\",\"member\"\n\"1767\",\"cobol\"\n\"1766\",\"virtual-reality\"\n\"1766\",\"pylint\"\n\"1766\",\"xcode9\"\n\"1765\",\"execution\"\n\"1764\",\"aptana\"\n\"1763\",\"cefsharp\"\n\"1763\",\"angular-universal\"\n\"1763\",\"setup.py\"\n\"1763\",\"mount\"\n\"1763\",\"autoit\"\n\"1762\",\"composition\"\n\"1761\",\"bottomnavigationview\"\n\"1760\",\"circular-dependency\"\n\"1760\",\"akka-stream\"\n\"1760\",\"provider\"\n\"1759\",\"decoding\"\n\"1759\",\"ubuntu-20.04\"\n\"1758\",\"ternary-operator\"\n\"1757\",\"infragistics\"\n\"1756\",\"netbeans-8\"\n\"1756\",\"okta\"\n\"1755\",\"visual-studio-extensions\"\n\"1754\",\"websphere-liberty\"\n\"1754\",\"spring-boot-actuator\"\n\"1754\",\"loss-function\"\n\"1753\",\"specifications\"\n\"1752\",\"password-protection\"\n\"1751\",\"popupwindow\"\n\"1751\",\"excel-2013\"\n\"1751\",\"state-machine\"\n\"1749\",\"semantics\"\n\"1749\",\"key-bindings\"\n\"1748\",\"pyside2\"\n\"1744\",\"docker-registry\"\n\"1743\",\"profiler\"\n\"1743\",\"mule-studio\"\n\"1742\",\"yield\"\n\"1742\",\"foreign-key-relationship\"\n\"1740\",\"realloc\"\n\"1739\",\"windows-server-2012\"\n\"1737\",\"production\"\n\"1737\",\"signals-slots\"\n\"1737\",\"cx-freeze\"\n\"1736\",\"optaplanner\"\n\"1735\",\"ng-bootstrap\"\n\"1735\",\"sql-server-express\"\n\"1731\",\"kotlin-multiplatform\"\n\"1730\",\"filenotfoundexception\"\n\"1729\",\"react-typescript\"\n\"1728\",\"gatling\"\n\"1728\",\"git-push\"\n\"1727\",\"face-detection\"\n\"1727\",\"mozilla\"\n\"1726\",\"structuremap\"\n\"1724\",\"pid\"\n\"1722\",\"sugarcrm\"\n\"1720\",\"git-rebase\"\n\"1719\",\"preview\"\n\"1719\",\"naming\"\n\"1719\",\"qgis\"\n\"1717\",\"flash-cs5\"\n\"1717\",\"android-architecture-components\"\n\"1717\",\"rcp\"\n\"1716\",\"large-files\"\n\"1715\",\"frame-rate\"\n\"1715\",\"spree\"\n\"1714\",\"python-tesseract\"\n\"1714\",\"cas\"\n\"1714\",\"extends\"\n\"1713\",\"xml-rpc\"\n\"1713\",\"date-formatting\"\n\"1713\",\"uipopovercontroller\"\n\"1713\",\"tfs-2015\"\n\"1711\",\"smalltalk\"\n\"1711\",\"django-allauth\"\n\"1711\",\"http-status-codes\"\n\"1709\",\"classcastexception\"\n\"1709\",\"aiohttp\"\n\"1707\",\"solver\"\n\"1707\",\"google-play-games\"\n\"1707\",\"google-search\"\n\"1703\",\"jface\"\n\"1702\",\"prettier\"\n\"1701\",\"assign\"\n\"1700\",\"bigdecimal\"\n\"1700\",\"user-experience\"\n\"1699\",\"archlinux\"\n\"1696\",\"sample\"\n\"1696\",\"postgresql-9.3\"\n\"1695\",\"unions\"\n\"1695\",\"blocking\"\n\"1694\",\"translate\"\n\"1693\",\"web-parts\"\n\"1693\",\"watch\"\n\"1693\",\"countdowntimer\"\n\"1692\",\"ribbon\"\n\"1692\",\"uialertcontroller\"\n\"1691\",\"jsfiddle\"\n\"1691\",\"outer-join\"\n\"1691\",\"asp.net-core-2.1\"\n\"1690\",\"olap\"\n\"1690\",\"cut\"\n\"1689\",\"owl-carousel\"\n\"1689\",\"docker-machine\"\n\"1689\",\"jqplot\"\n\"1688\",\"rules\"\n\"1686\",\"fade\"\n\"1686\",\"winjs\"\n\"1684\",\"rtmp\"\n\"1683\",\"directory-structure\"\n\"1682\",\"cloudinary\"\n\"1682\",\"pear\"\n\"1681\",\"onesignal\"\n\"1678\",\"mingw-w64\"\n\"1677\",\"robolectric\"\n\"1677\",\"android-camera2\"\n\"1676\",\"text-classification\"\n\"1676\",\"bigcommerce\"\n\"1675\",\"sharding\"\n\"1673\",\"simple-html-dom\"\n\"1672\",\"graphics2d\"\n\"1671\",\"final\"\n\"1671\",\"jquery-isotope\"\n\"1671\",\"swagger-2.0\"\n\"1670\",\"disassembly\"\n\"1670\",\"hlsl\"\n\"1670\",\"iron-router\"\n\"1669\",\"django-class-based-views\"\n\"1668\",\"jsch\"\n\"1667\",\"hibernate-criteria\"\n\"1667\",\"large-language-model\"\n\"1666\",\"contact-form\"\n\"1666\",\"cryptojs\"\n\"1665\",\"cheerio\"\n\"1665\",\"ini\"\n\"1664\",\"template-specialization\"\n\"1664\",\"axis-labels\"\n\"1664\",\"swiftmailer\"\n\"1664\",\"parcelable\"\n\"1663\",\"xsd-validation\"\n\"1663\",\"c#-2.0\"\n\"1663\",\"tablet\"\n\"1662\",\"temp-tables\"\n\"1662\",\"paintcomponent\"\n\"1661\",\"vue-composition-api\"\n\"1661\",\"feature-extraction\"\n\"1661\",\"ranking\"\n\"1661\",\"netflix-eureka\"\n\"1659\",\"synchronous\"\n\"1658\",\"rank\"\n\"1657\",\"ts-jest\"\n\"1657\",\"datastax-enterprise\"\n\"1655\",\"baseadapter\"\n\"1655\",\"pic\"\n\"1654\",\"conflict\"\n\"1653\",\"google-cloud-dataproc\"\n\"1652\",\"backtracking\"\n\"1652\",\"private-key\"\n\"1652\",\"database-trigger\"\n\"1652\",\"spring-amqp\"\n\"1652\",\"uidatepicker\"\n\"1652\",\"autoload\"\n\"1651\",\"template-engine\"\n\"1651\",\"nsview\"\n\"1650\",\"django-channels\"\n\"1649\",\"vcl\"\n\"1649\",\"py2exe\"\n\"1649\",\"bootstrap-vue\"\n\"1649\",\"nsurl\"\n\"1648\",\"right-to-left\"\n\"1647\",\"public\"\n\"1646\",\"date-range\"\n\"1646\",\"xor\"\n\"1646\",\"wcf-binding\"\n\"1646\",\"xcode4.5\"\n\"1646\",\"ethernet\"\n\"1645\",\"back-button\"\n\"1645\",\"android-progressbar\"\n\"1645\",\"amazon-aurora\"\n\"1644\",\"telerik-grid\"\n\"1644\",\"mysql-error-1064\"\n\"1644\",\"android-studio-3.0\"\n\"1644\",\"excel-interop\"\n\"1643\",\"delphi-2010\"\n\"1643\",\"nested-attributes\"\n\"1642\",\"marker\"\n\"1642\",\"readonly\"\n\"1640\",\"scala-collections\"\n\"1639\",\"workflow-foundation-4\"\n\"1639\",\"shiny-server\"\n\"1639\",\"plsqldeveloper\"\n\"1639\",\"multicast\"\n\"1636\",\"functor\"\n\"1636\",\".net-standard\"\n\"1636\",\"signalr-hub\"\n\"1635\",\"samsung-mobile\"\n\"1635\",\"haxe\"\n\"1634\",\"vue-cli\"\n\"1634\",\"orders\"\n\"1634\",\"tastypie\"\n\"1633\",\"http-get\"\n\"1631\",\"cx-oracle\"\n\"1630\",\"dynamic-linking\"\n\"1629\",\"children\"\n\"1629\",\"angular-ngmodel\"\n\"1629\",\"fread\"\n\"1628\",\"algolia\"\n\"1627\",\"setup-project\"\n\"1627\",\"code-formatting\"\n\"1627\",\"rack\"\n\"1626\",\"flux\"\n\"1625\",\"delayed-job\"\n\"1625\",\"cpu-word\"\n\"1625\",\"sampling\"\n\"1624\",\"android-sensors\"\n\"1621\",\"geoserver\"\n\"1620\",\"gradle-plugin\"\n\"1620\",\"supervisord\"\n\"1620\",\"android-gridview\"\n\"1619\",\"watir-webdriver\"\n\"1618\",\"flutter-getx\"\n\"1617\",\"shared-hosting\"\n\"1616\",\"adal\"\n\"1616\",\"aws-step-functions\"\n\"1616\",\"spring-restcontroller\"\n\"1616\",\"google-analytics-4\"\n\"1615\",\"datediff\"\n\"1614\",\"fixtures\"\n\"1614\",\"acrobat\"\n\"1613\",\"claims-based-identity\"\n\"1613\",\"boot\"\n\"1612\",\"java-9\"\n\"1612\",\"scalatest\"\n\"1611\",\"history\"\n\"1610\",\"glassfish-3\"\n\"1609\",\"binary-data\"\n\"1609\",\"dump\"\n\"1609\",\"predict\"\n\"1608\",\"calculation\"\n\"1608\",\"kill\"\n\"1608\",\"wordpress-rest-api\"\n\"1608\",\"higher-order-functions\"\n\"1607\",\"unityscript\"\n\"1606\",\"truncate\"\n\"1606\",\"flask-restful\"\n\"1606\",\"ivy\"\n\"1605\",\"flatten\"\n\"1605\",\"t4\"\n\"1605\",\"uislider\"\n\"1603\",\"codable\"\n\"1602\",\"tabbar\"\n\"1602\",\"linear-gradients\"\n\"1602\",\"solrj\"\n\"1601\",\"octobercms\"\n\"1601\",\"google-api-client\"\n\"1601\",\"screen-orientation\"\n\"1601\",\"nonblocking\"\n\"1600\",\"webrequest\"\n\"1600\",\"unordered-map\"\n\"1599\",\"memcpy\"\n\"1599\",\"yup\"\n\"1598\",\"jolt\"\n\"1598\",\"tic-tac-toe\"\n\"1597\",\"python-telegram-bot\"\n\"1596\",\"vuforia\"\n\"1595\",\"tensorflow.js\"\n\"1595\",\"intentfilter\"\n\"1595\",\"picker\"\n\"1595\",\"activemq-artemis\"\n\"1595\",\"linux-mint\"\n\"1594\",\"devtools\"\n\"1593\",\"uniqueidentifier\"\n\"1593\",\"service-accounts\"\n\"1592\",\"winui-3\"\n\"1591\",\"definition\"\n\"1591\",\"producer-consumer\"\n\"1591\",\"google-api-python-client\"\n\"1590\",\"nsmanagedobject\"\n\"1590\",\"viewstate\"\n\"1590\",\"laravel-passport\"\n\"1589\",\"hpc\"\n\"1589\",\"shadow-dom\"\n\"1587\",\"literals\"\n\"1586\",\"google-docs-api\"\n\"1585\",\"qtp\"\n\"1584\",\"treemap\"\n\"1583\",\"enterprise-library\"\n\"1582\",\"nsnotificationcenter\"\n\"1581\",\"class-diagram\"\n\"1581\",\"cassandra-3.0\"\n\"1581\",\"meteor-blaze\"\n\"1581\",\"naudio\"\n\"1581\",\"pyomo\"\n\"1579\",\"live\"\n\"1579\",\"database-backups\"\n\"1578\",\"access-violation\"\n\"1577\",\"bamboo\"\n\"1577\",\"rollback\"\n\"1577\",\"windows-vista\"\n\"1577\",\"centos6\"\n\"1576\",\"fortran90\"\n\"1576\",\"taxonomy\"\n\"1575\",\"partition\"\n\"1574\",\"permission-denied\"\n\"1573\",\"firefox-addon-webextensions\"\n\"1571\",\"homestead\"\n\"1569\",\"configure\"\n\"1568\",\"aws-codepipeline\"\n\"1568\",\"genymotion\"\n\"1567\",\"browser-history\"\n\"1567\",\"openxml-sdk\"\n\"1567\",\"google-glass\"\n\"1567\",\"spotfire\"\n\"1567\",\"glib\"\n\"1566\",\"iad\"\n\"1565\",\"packet\"\n\"1565\",\"r-leaflet\"\n\"1565\",\"autofill\"\n\"1565\",\"cycle\"\n\"1564\",\"nutch\"\n\"1563\",\"mongodb-atlas\"\n\"1563\",\"google-admin-sdk\"\n\"1561\",\"figure\"\n\"1561\",\"extjs4.2\"\n\"1559\",\"symbolic-math\"\n\"1559\",\"differential-equations\"\n\"1558\",\"mailgun\"\n\"1558\",\"timepicker\"\n\"1557\",\"multiplayer\"\n\"1557\",\"windows-server-2012-r2\"\n\"1557\",\"minitest\"\n\"1557\",\"schema.org\"\n\"1557\",\"autoencoder\"\n\"1555\",\"netbeans-7\"\n\"1555\",\"uistoryboardsegue\"\n\"1554\",\"playback\"\n\"1553\",\"twitter4j\"\n\"1549\",\"quickblox\"\n\"1548\",\"appkit\"\n\"1548\",\"shopware\"\n\"1548\",\"webfonts\"\n\"1547\",\"crash-reports\"\n\"1546\",\"sentry\"\n\"1546\",\"virtual-functions\"\n\"1546\",\"return-type\"\n\"1545\",\"logstash-grok\"\n\"1544\",\"inotifypropertychanged\"\n\"1544\",\"android-coordinatorlayout\"\n\"1543\",\"extend\"\n\"1542\",\"mapstruct\"\n\"1541\",\"wcf-ria-services\"\n\"1541\",\"foursquare\"\n\"1540\",\"autotools\"\n\"1539\",\"horizontal-scrolling\"\n\"1539\",\"fragment-shader\"\n\"1539\",\"android-image\"\n\"1539\",\"custom-fields\"\n\"1538\",\"python-venv\"\n\"1538\",\"api-platform.com\"\n\"1538\",\"poco\"\n\"1538\",\"qthread\"\n\"1537\",\"pusher\"\n\"1536\",\"friend\"\n\"1536\",\"laravel-query-builder\"\n\"1535\",\"impersonation\"\n\"1534\",\"eager-loading\"\n\"1534\",\"stock\"\n\"1533\",\"self\"\n\"1533\",\"criteria-api\"\n\"1532\",\"braintree\"\n\"1532\",\"streamwriter\"\n\"1532\",\"excel-2016\"\n\"1532\",\"flutter-provider\"\n\"1531\",\"jira-rest-api\"\n\"1531\",\"restart\"\n\"1531\",\"preferences\"\n\"1530\",\"live-streaming\"\n\"1530\",\"buffer-overflow\"\n\"1529\",\"progress\"\n\"1529\",\"arrow-functions\"\n\"1528\",\"keras-layer\"\n\"1528\",\"pouchdb\"\n\"1528\",\"macos-sierra\"\n\"1527\",\"privileges\"\n\"1527\",\"spring-data-elasticsearch\"\n\"1527\",\"libxml2\"\n\"1526\",\"pool\"\n\"1526\",\"angular-httpclient\"\n\"1525\",\"file-rename\"\n\"1525\",\"angularjs-service\"\n\"1524\",\"cat\"\n\"1524\",\"sql-server-2017\"\n\"1524\",\"memoization\"\n\"1523\",\"mpdf\"\n\"1523\",\"has-many-through\"\n\"1522\",\"crosstab\"\n\"1522\",\"branching-and-merging\"\n\"1521\",\"borrow-checker\"\n\"1521\",\"pypdf\"\n\"1520\",\"hyper-v\"\n\"1520\",\"visio\"\n\"1520\",\"google-cloud-build\"\n\"1520\",\"median\"\n\"1519\",\"python-packaging\"\n\"1519\",\"alarm\"\n\"1519\",\"fastlane\"\n\"1519\",\"http-proxy\"\n\"1519\",\"cvs\"\n\"1519\",\"tradingview-api\"\n\"1517\",\"ms-access-2016\"\n\"1516\",\"txt\"\n\"1516\",\"apt\"\n\"1515\",\"instance-variables\"\n\"1515\",\"processbuilder\"\n\"1514\",\"jquery-autocomplete\"\n\"1513\",\"apex-code\"\n\"1513\",\"i18next\"\n\"1513\",\"postfix-mta\"\n\"1511\",\"silverlight-3.0\"\n\"1511\",\"mosquitto\"\n\"1510\",\"dbt\"\n\"1510\",\"rxjs5\"\n\"1510\",\"openfire\"\n\"1509\",\"quaternions\"\n\"1508\",\"django-migrations\"\n\"1508\",\"quartz.net\"\n\"1508\",\"linked-server\"\n\"1507\",\"grails-plugin\"\n\"1507\",\"read-eval-print-loop\"\n\"1507\",\"sonarqube-scan\"\n\"1506\",\"wtforms\"\n\"1504\",\"one-to-one\"\n\"1502\",\"hololens\"\n\"1501\",\"python-turtle\"\n\"1501\",\"functional-testing\"\n\"1501\",\".net-7.0\"\n\"1501\",\"extern\"\n\"1501\",\"macos-catalina\"\n\"1501\",\"ieee-754\"\n\"1500\",\"named-entity-recognition\"\n\"1500\",\"delphi-xe\"\n\"1500\",\"stdmap\"\n\"1499\",\"install4j\"\n\"1499\",\"subtraction\"\n\"1498\",\"image-manipulation\"\n\"1498\",\"android-sdcard\"\n\"1498\",\"suitescript\"\n\"1498\",\"web-development-server\"\n\"1497\",\"deep-copy\"\n\"1496\",\"back\"\n\"1495\",\"unmanaged\"\n\"1495\",\"uicollectionviewlayout\"\n\"1495\",\"nightwatch.js\"\n\"1495\",\"dagger\"\n\"1495\",\"bearer-token\"\n\"1494\",\"gsm\"\n\"1494\",\"outputstream\"\n\"1494\",\"project-management\"\n\"1492\",\"feature-selection\"\n\"1492\",\"smoothing\"\n\"1492\",\"akka-http\"\n\"1492\",\"high-availability\"\n\"1491\",\"datacontext\"\n\"1491\",\"mex\"\n\"1491\",\"direct3d\"\n\"1488\",\"pagespeed\"\n\"1488\",\"codec\"\n\"1487\",\"rollup\"\n\"1487\",\"bottle\"\n\"1486\",\"filebeat\"\n\"1486\",\"newrelic\"\n\"1486\",\"radix\"\n\"1484\",\"fluid\"\n\"1483\",\"relation\"\n\"1483\",\"django-cms\"\n\"1483\",\"superclass\"\n\"1482\",\"flags\"\n\"1482\",\"salt-project\"\n\"1482\",\"cumulative-sum\"\n\"1481\",\"graphql-js\"\n\"1481\",\"umbraco7\"\n\"1481\",\"spring-boot-test\"\n\"1480\",\"fadeout\"\n\"1480\",\"typescript2.0\"\n\"1480\",\"qwidget\"\n\"1479\",\"spring-websocket\"\n\"1479\",\"hibernate-search\"\n\"1479\",\"odoo-9\"\n\"1478\",\"fluentvalidation\"\n\"1478\",\"android-4.4-kitkat\"\n\"1477\",\"bitbake\"\n\"1477\",\"factory-pattern\"\n\"1477\",\"assertion\"\n\"1476\",\"smack\"\n\"1476\",\"jsp-tags\"\n\"1476\",\"gradient-descent\"\n\"1476\",\"codeception\"\n\"1475\",\"php-carbon\"\n\"1475\",\"overwrite\"\n\"1475\",\"sql-execution-plan\"\n\"1475\",\"quasar-framework\"\n\"1475\",\"partial\"\n\"1474\",\"python-docx\"\n\"1474\",\"aws-codebuild\"\n\"1474\",\"mobile-website\"\n\"1473\",\"docker-volume\"\n\"1473\",\"fabric\"\n\"1473\",\"monitor\"\n\"1473\",\"iqueryable\"\n\"1472\",\"portforwarding\"\n\"1472\",\"xamarin-studio\"\n\"1471\",\"electron-builder\"\n\"1470\",\"dagger-hilt\"\n\"1469\",\"remote-desktop\"\n\"1468\",\"anova\"\n\"1468\",\"geckodriver\"\n\"1468\",\"stomp\"\n\"1468\",\"embedding\"\n\"1465\",\"apache-zeppelin\"\n\"1465\",\"wai-aria\"\n\"1464\",\"duration\"\n\"1464\",\"jailbreak\"\n\"1463\",\"polling\"\n\"1463\",\"google-maps-api-2\"\n\"1462\",\"file-descriptor\"\n\"1462\",\"language-design\"\n\"1462\",\"text-extraction\"\n\"1461\",\"python-idle\"\n\"1461\",\"osx-mountain-lion\"\n\"1461\",\"directed-acyclic-graphs\"\n\"1460\",\"imacros\"\n\"1459\",\"capture\"\n\"1457\",\"ioexception\"\n\"1457\",\"android-dialog\"\n\"1456\",\"varchar\"\n\"1456\",\"nsfilemanager\"\n\"1455\",\"voice-recognition\"\n\"1455\",\"openedge\"\n\"1455\",\"hmac\"\n\"1454\",\"vega-lite\"\n\"1454\",\"uistackview\"\n\"1454\",\"web-frontend\"\n\"1453\",\"verification\"\n\"1453\",\"portability\"\n\"1453\",\"globalization\"\n\"1452\",\"database-normalization\"\n\"1452\",\"epoch\"\n\"1452\",\"io-redirection\"\n\"1452\",\"sqldatatypes\"\n\"1452\",\"gaps-and-islands\"\n\"1451\",\"jbpm\"\n\"1451\",\"uac\"\n\"1451\",\"autocompletetextview\"\n\"1450\",\"react-component\"\n\"1450\",\"coldfusion-9\"\n\"1450\",\"has-many\"\n\"1449\",\"xmlserializer\"\n\"1449\",\"chess\"\n\"1449\",\"monorepo\"\n\"1449\",\"sd-card\"\n\"1448\",\"recommendation-engine\"\n\"1448\",\"at-command\"\n\"1448\",\"modelica\"\n\"1448\",\"autocad\"\n\"1447\",\"riverpod\"\n\"1447\",\"landscape\"\n\"1447\",\"animated-gif\"\n\"1447\",\"restful-authentication\"\n\"1445\",\"extbase\"\n\"1445\",\"substr\"\n\"1444\",\"x++\"\n\"1444\",\"spell-checking\"\n\"1444\",\"completable-future\"\n\"1444\",\"status\"\n\"1443\",\"robots.txt\"\n\"1442\",\"dbus\"\n\"1442\",\"keydown\"\n\"1442\",\"cache-control\"\n\"1442\",\"egit\"\n\"1441\",\"google-sheets-query\"\n\"1440\",\"rtp\"\n\"1440\",\"dtd\"\n\"1439\",\"vertica\"\n\"1437\",\"boost-spirit\"\n\"1437\",\"webpack-2\"\n\"1436\",\"prestashop-1.7\"\n\"1436\",\"idisposable\"\n\"1435\",\"rethinkdb\"\n\"1434\",\"nginx-location\"\n\"1434\",\"7zip\"\n\"1432\",\"bitbucket-pipelines\"\n\"1432\",\"ontouchlistener\"\n\"1432\",\"gnome\"\n\"1431\",\"spi\"\n\"1430\",\"stderr\"\n\"1428\",\"react-leaflet\"\n\"1427\",\"jdeveloper\"\n\"1427\",\"wiki\"\n\"1427\",\"xilinx\"\n\"1427\",\"cortex-m\"\n\"1427\",\"lua-table\"\n\"1426\",\"angle\"\n\"1426\",\"xcodebuild\"\n\"1426\",\"complex-numbers\"\n\"1424\",\"labview\"\n\"1424\",\"android-tv\"\n\"1422\",\"vaadin7\"\n\"1421\",\"skspritenode\"\n\"1421\",\"workflow-foundation\"\n\"1420\",\"imagemagick-convert\"\n\"1420\",\"pcap\"\n\"1420\",\"bson\"\n\"1420\",\"ref\"\n\"1419\",\"timing\"\n\"1417\",\"filesize\"\n\"1417\",\"strtok\"\n\"1416\",\"shared\"\n\"1415\",\"porting\"\n\"1415\",\"uicolor\"\n\"1414\",\"git-clone\"\n\"1414\",\"portable-class-library\"\n\"1414\",\"silverlight-5.0\"\n\"1414\",\"java.util.concurrent\"\n\"1414\",\"mixed-models\"\n\"1414\",\"tizen\"\n\"1413\",\"dynamics-crm-2013\"\n\"1412\",\"visualforce\"\n\"1412\",\"webpack-4\"\n\"1412\",\"submenu\"\n\"1411\",\"hikaricp\"\n\"1410\",\"ssrs-tablix\"\n\"1410\",\"appdomain\"\n\"1410\",\"pyaudio\"\n\"1409\",\"kettle\"\n\"1409\",\"huawei-mobile-services\"\n\"1406\",\"echarts\"\n\"1406\",\"jnlp\"\n\"1406\",\"point-cloud-library\"\n\"1405\",\"camera-calibration\"\n\"1404\",\"zabbix\"\n\"1403\",\"32-bit\"\n\"1402\",\"raspberry-pi4\"\n\"1402\",\"screen-resolution\"\n\"1401\",\"google-cast\"\n\"1401\",\"spring-tool-suite\"\n\"1400\",\"cancan\"\n\"1399\",\"swashbuckle\"\n\"1399\",\"systemjs\"\n\"1399\",\"aspnetboilerplate\"\n\"1399\",\"embedded-jetty\"\n\"1398\",\"delta-lake\"\n\"1398\",\"audio-player\"\n\"1396\",\"point-clouds\"\n\"1395\",\"openssh\"\n\"1391\",\"selenium-rc\"\n\"1391\",\"unpivot\"\n\"1391\",\"appium-android\"\n\"1391\",\"java-6\"\n\"1391\",\"access-control\"\n\"1390\",\"void-pointers\"\n\"1390\",\"cpython\"\n\"1390\",\"auto-update\"\n\"1389\",\"uppercase\"\n\"1389\",\"react-table\"\n\"1389\",\"osx-snow-leopard\"\n\"1388\",\"nfs\"\n\"1388\",\"mainframe\"\n\"1387\",\"dbi\"\n\"1387\",\"laravel-5.7\"\n\"1387\",\"cascade\"\n\"1387\",\"text-parsing\"\n\"1384\",\"react-native-firebase\"\n\"1384\",\"rectangles\"\n\"1383\",\"unique-constraint\"\n\"1383\",\"blackberry-10\"\n\"1383\",\"tkinter-entry\"\n\"1383\",\"context-free-grammar\"\n\"1382\",\"cruisecontrol.net\"\n\"1382\",\"riscv\"\n\"1382\",\"timedelta\"\n\"1380\",\"xtext\"\n\"1380\",\"tabular\"\n\"1380\",\"webdav\"\n\"1379\",\"nsmanagedobjectcontext\"\n\"1379\",\"meta\"\n\"1378\",\"pymysql\"\n\"1378\",\"maven-surefire-plugin\"\n\"1376\",\"tablelayout\"\n\"1376\",\"policy\"\n\"1375\",\"factorial\"\n\"1375\",\"nodemon\"\n\"1375\",\"image-gallery\"\n\"1374\",\"bookmarklet\"\n\"1374\",\"has-and-belongs-to-many\"\n\"1374\",\"trading\"\n\"1373\",\"csproj\"\n\"1373\",\"spring-rabbit\"\n\"1373\",\"paypal-rest-sdk\"\n\"1373\",\"google-maps-sdk-ios\"\n\"1372\",\"ssis-2012\"\n\"1371\",\"cherrypy\"\n\"1371\",\"biopython\"\n\"1371\",\"fingerprint\"\n\"1371\",\"android-8.0-oreo\"\n\"1371\",\"azure-language-understanding\"\n\"1371\",\"cucumber-jvm\"\n\"1371\",\"q\"\n\"1371\",\"altair\"\n\"1370\",\"floating-accuracy\"\n\"1370\",\"elasticsearch-5\"\n\"1370\",\"textinput\"\n\"1369\",\"django-filter\"\n\"1369\",\"netezza\"\n\"1369\",\"openmpi\"\n\"1369\",\"between\"\n\"1367\",\"destructuring\"\n\"1367\",\"asp.net-4.0\"\n\"1367\",\"tail-recursion\"\n\"1367\",\"handle\"\n\"1367\",\"laravel-mix\"\n\"1367\",\"area\"\n\"1367\",\"glm-math\"\n\"1366\",\"tibble\"\n\"1365\",\"xlrd\"\n\"1365\",\"jquery-ui-tabs\"\n\"1364\",\"html-entities\"\n\"1364\",\"hapi.js\"\n\"1364\",\"ipa\"\n\"1363\",\"flex4.5\"\n\"1363\",\"identifier\"\n\"1362\",\"seekbar\"\n\"1361\",\"material-components-android\"\n\"1361\",\"python-poetry\"\n\"1361\",\"apache-superset\"\n\"1360\",\"silex\"\n\"1360\",\"seam\"\n\"1359\",\"freepascal\"\n\"1359\",\"hangfire\"\n\"1359\",\"perl-module\"\n\"1359\",\"beagleboneblack\"\n\"1358\",\"intrinsics\"\n\"1358\",\"evaluation\"\n\"1358\",\"linq-to-objects\"\n\"1357\",\"apache-karaf\"\n\"1357\",\"m\"\n\"1357\",\"nonetype\"\n\"1356\",\"assignment-operator\"\n\"1356\",\"android-location\"\n\"1356\",\"zoo\"\n\"1354\",\"job-scheduling\"\n\"1354\",\"mnist\"\n\"1353\",\"markup\"\n\"1353\",\"absolute\"\n\"1353\",\"prefix\"\n\"1352\",\"nswindow\"\n\"1351\",\"enterprise-architect\"\n\"1351\",\"android-workmanager\"\n\"1351\",\"tabulator\"\n\"1351\",\"solana\"\n\"1350\",\"excel-addins\"\n\"1350\",\"strip\"\n\"1349\",\"intellij-plugin\"\n\"1349\",\"master-detail\"\n\"1349\",\"durandal\"\n\"1349\",\"restful-url\"\n\"1348\",\"yahoo-finance\"\n\"1348\",\"image-recognition\"\n\"1348\",\"swift-playground\"\n\"1347\",\"client-certificates\"\n\"1347\",\"robotics\"\n\"1347\",\"projects-and-solutions\"\n\"1346\",\"pipenv\"\n\"1346\",\"gradle-kotlin-dsl\"\n\"1345\",\"group-concat\"\n\"1344\",\"facebook-apps\"\n\"1343\",\"worksheet-function\"\n\"1342\",\"api-gateway\"\n\"1342\",\"execute\"\n\"1342\",\"google-fusion-tables\"\n\"1341\",\"atl\"\n\"1341\",\"dst\"\n\"1340\",\"firebase-dynamic-links\"\n\"1340\",\"appstore-approval\"\n\"1340\",\"azure-virtual-network\"\n\"1340\",\"numerical-integration\"\n\"1340\",\"collectionview\"\n\"1340\",\"folium\"\n\"1339\",\"delete-operator\"\n\"1339\",\"android-architecture-navigation\"\n\"1339\",\"curve\"\n\"1338\",\"dbpedia\"\n\"1338\",\"boost-python\"\n\"1338\",\"h5py\"\n\"1338\",\"lazarus\"\n\"1338\",\"hugo\"\n\"1336\",\"yesod\"\n\"1336\",\"agent\"\n\"1336\",\"cube\"\n\"1335\",\"fluentd\"\n\"1335\",\"ibm-cloud-infrastructure\"\n\"1335\",\"shopify-app\"\n\"1335\",\"commonjs\"\n\"1334\",\"sha\"\n\"1334\",\"userscripts\"\n\"1333\",\"xul\"\n\"1333\",\"avx\"\n\"1333\",\"r-package\"\n\"1333\",\"ormlite\"\n\"1330\",\"matlab-guide\"\n\"1328\",\"php-curl\"\n\"1328\",\"pythonanywhere\"\n\"1328\",\"odp.net\"\n\"1327\",\"sklearn-pandas\"\n\"1327\",\"cassandra-2.0\"\n\"1326\",\"progress-4gl\"\n\"1326\",\"telethon\"\n\"1326\",\"sas-macro\"\n\"1326\",\"ls\"\n\"1325\",\"checkstyle\"\n\"1325\",\"properties-file\"\n\"1324\",\"tsconfig\"\n\"1324\",\"keytool\"\n\"1324\",\"rfid\"\n\"1324\",\"wordpress-gutenberg\"\n\"1324\",\"iterable\"\n\"1324\",\"cgal\"\n\"1323\",\"chmod\"\n\"1323\",\"gsp\"\n\"1322\",\"dynamodb-queries\"\n\"1322\",\"sumifs\"\n\"1321\",\"local-variables\"\n\"1321\",\"data-wrangling\"\n\"1321\",\"xargs\"\n\"1320\",\"ireport\"\n\"1320\",\"webmethod\"\n\"1320\",\"powermockito\"\n\"1318\",\"apache-fop\"\n\"1318\",\"python-2.6\"\n\"1318\",\"microphone\"\n\"1317\",\"identityserver3\"\n\"1316\",\"skype\"\n\"1316\",\"asihttprequest\"\n\"1316\",\"arduino-ide\"\n\"1315\",\"smtplib\"\n\"1315\",\"m2eclipse\"\n\"1315\",\"bezier\"\n\"1314\",\"spring-annotations\"\n\"1314\",\"tinymce-4\"\n\"1312\",\"pdf.js\"\n\"1312\",\"codeigniter-4\"\n\"1312\",\"restructuredtext\"\n\"1312\",\"powerbuilder\"\n\"1312\",\"tf-idf\"\n\"1311\",\"amd\"\n\"1311\",\"scons\"\n\"1310\",\"filemaker\"\n\"1310\",\"polymorphic-associations\"\n\"1309\",\"insert-update\"\n\"1309\",\"spring-rest\"\n\"1308\",\"coredump\"\n\"1308\",\"strtotime\"\n\"1308\",\"auto\"\n\"1307\",\"xcode11\"\n\"1307\",\"react-functional-component\"\n\"1306\",\"reportlab\"\n\"1306\",\"dispatcher\"\n\"1306\",\"data-migration\"\n\"1306\",\"watchos\"\n\"1306\",\"jquery-chosen\"\n\"1306\",\"postgresql-9.4\"\n\"1306\",\"storekit\"\n\"1305\",\"django-haystack\"\n\"1305\",\"spring-cloud-dataflow\"\n\"1305\",\"nsmutabledictionary\"\n\"1305\",\"mule4\"\n\"1304\",\"distributed-system\"\n\"1304\",\"jquery-file-upload\"\n\"1304\",\"shopware6\"\n\"1303\",\"llvm-ir\"\n\"1303\",\"survival-analysis\"\n\"1303\",\"office365api\"\n\"1303\",\"summary\"\n\"1302\",\"swiftui-list\"\n\"1302\",\"google-search-console\"\n\"1301\",\"api-key\"\n\"1301\",\"fiware\"\n\"1301\",\"dql\"\n\"1300\",\"slidetoggle\"\n\"1300\",\"apache-tika\"\n\"1300\",\"disk\"\n\"1300\",\"sap-ase\"\n\"1300\",\"account\"\n\"1299\",\"spring-webflow\"\n\"1299\",\"httpserver\"\n\"1299\",\"ole\"\n\"1298\",\"microsoft-graph-sdks\"\n\"1298\",\"open-telemetry\"\n\"1298\",\"angular-dart\"\n\"1296\",\"multi-touch\"\n\"1296\",\"spring-cloud-gateway\"\n\"1296\",\"launch\"\n\"1295\",\"modalviewcontroller\"\n\"1295\",\"tiles\"\n\"1294\",\"google-cloud-composer\"\n\"1293\",\"hid\"\n\"1292\",\"azure-cosmosdb-sqlapi\"\n\"1292\",\"algorithmic-trading\"\n\"1291\",\"slickgrid\"\n\"1291\",\"leiningen\"\n\"1291\",\"google-api-dotnet-client\"\n\"1291\",\"colorbar\"\n\"1290\",\"case-sensitive\"\n\"1290\",\"grails-2.0\"\n\"1289\",\"data-access-layer\"\n\"1288\",\"or-tools\"\n\"1287\",\"app-engine-ndb\"\n\"1287\",\"nameerror\"\n\"1287\",\"worker\"\n\"1287\",\"strongloop\"\n\"1287\",\"emscripten\"\n\"1286\",\"php-5.3\"\n\"1286\",\"wcf-security\"\n\"1286\",\"facebook-messenger\"\n\"1286\",\"montecarlo\"\n\"1285\",\"session-timeout\"\n\"1285\",\"angularjs-routing\"\n\"1285\",\"ktor\"\n\"1285\",\"jackson-databind\"\n\"1285\",\"activiti\"\n\"1284\",\"swift-package-manager\"\n\"1284\",\"informatica-powercenter\"\n\"1284\",\"memory-alignment\"\n\"1284\",\"generic-programming\"\n\"1283\",\"haskell-stack\"\n\"1282\",\"salesforce-lightning\"\n\"1282\",\"normal-distribution\"\n\"1281\",\"undefined-reference\"\n\"1281\",\"uitoolbar\"\n\"1281\",\"ngrx-store\"\n\"1281\",\"windows-server\"\n\"1281\",\"cytoscape.js\"\n\"1280\",\"smtpclient\"\n\"1280\",\"self-join\"\n\"1280\",\"type-traits\"\n\"1278\",\"typeahead.js\"\n\"1278\",\"event-log\"\n\"1276\",\"wif\"\n\"1276\",\"designer\"\n\"1276\",\"visual-studio-cordova\"\n\"1275\",\"qtableview\"\n\"1274\",\"unicorn\"\n\"1273\",\"repaint\"\n\"1273\",\"aws-iot\"\n\"1273\",\"upsert\"\n\"1273\",\"shiro\"\n\"1273\",\"nopcommerce\"\n\"1273\",\"es6-class\"\n\"1272\",\"slug\"\n\"1272\",\"rubymine\"\n\"1272\",\"mkannotation\"\n\"1271\",\"memorystream\"\n\"1270\",\"java-17\"\n\"1270\",\"cairo\"\n\"1269\",\"django-staticfiles\"\n\"1269\",\"unsigned\"\n\"1269\",\"uisearchcontroller\"\n\"1269\",\"toad\"\n\"1268\",\"spring-roo\"\n\"1268\",\"android-sdk-tools\"\n\"1267\",\"createjs\"\n\"1267\",\"bottom-sheet\"\n\"1267\",\"rails-activestorage\"\n\"1267\",\"loopback\"\n\"1266\",\"react-state\"\n\"1266\",\"android-menu\"\n\"1266\",\"angular2-nativescript\"\n\"1264\",\"backpropagation\"\n\"1264\",\"uiactivityviewcontroller\"\n\"1264\",\"authorize.net\"\n\"1263\",\"nsxmlparser\"\n\"1263\",\"subview\"\n\"1263\",\"powershell-remoting\"\n\"1263\",\"subroutine\"\n\"1262\",\"spring-ws\"\n\"1261\",\"loadrunner\"\n\"1261\",\"survey\"\n\"1261\",\"mouselistener\"\n\"1261\",\"restlet\"\n\"1260\",\"expressionengine\"\n\"1260\",\"comparable\"\n\"1259\",\"smartgwt\"\n\"1259\",\"keyevent\"\n\"1259\",\"gradlew\"\n\"1259\",\"express-session\"\n\"1259\",\"pysimplegui\"\n\"1259\",\"gxt\"\n\"1258\",\"tensorflow-serving\"\n\"1258\",\"shutdown\"\n\"1258\",\"bundling-and-minification\"\n\"1258\",\"lattice\"\n\"1257\",\"vsix\"\n\"1257\",\"quill\"\n\"1257\",\"logstash-configuration\"\n\"1255\",\"avcapturesession\"\n\"1255\",\"aws-serverless\"\n\"1255\",\"reddit\"\n\"1255\",\"string-literals\"\n\"1254\",\"attr\"\n\"1253\",\"chef-recipe\"\n\"1253\",\"visual-foxpro\"\n\"1253\",\"solrcloud\"\n\"1252\",\"afnetworking-2\"\n\"1252\",\"r-raster\"\n\"1252\",\"objective-c++\"\n\"1252\",\"hashicorp-vault\"\n\"1252\",\"modbus\"\n\"1252\",\"argv\"\n\"1251\",\"vim-plugin\"\n\"1250\",\"xmldocument\"\n\"1249\",\"event-sourcing\"\n\"1248\",\"airflow-scheduler\"\n\"1248\",\"httphandler\"\n\"1248\",\"email-validation\"\n\"1247\",\"matplotlib-basemap\"\n\"1247\",\"android-4.0-ice-cream-sandwich\"\n\"1246\",\"core-image\"\n\"1246\",\"codemirror\"\n\"1245\",\"w3c\"\n\"1245\",\"unobtrusive-validation\"\n\"1245\",\"git-pull\"\n\"1244\",\"ear\"\n\"1244\",\".net-4.8\"\n\"1242\",\"dynamics-365\"\n\"1242\",\"angularjs-controller\"\n\"1242\",\"latency\"\n\"1242\",\"struts-1\"\n\"1242\",\"multicore\"\n\"1241\",\"sign\"\n\"1241\",\"dimensions\"\n\"1241\",\"pgadmin-4\"\n\"1240\",\"jquery-masonry\"\n\"1239\",\"django-testing\"\n\"1239\",\"android-broadcast\"\n\"1239\",\"expression-blend\"\n\"1239\",\"togglebutton\"\n\"1238\",\"chromium-embedded\"\n\"1238\",\"angular-ng-if\"\n\"1238\",\"libsvm\"\n\"1238\",\"lotusscript\"\n\"1237\",\"puma\"\n\"1237\",\"sapply\"\n\"1237\",\"koa\"\n\"1237\",\"dpi\"\n\"1237\",\"mesos\"\n\"1236\",\"publishing\"\n\"1236\",\"android-collapsingtoolbarlayout\"\n\"1236\",\"arima\"\n\"1235\",\"clean-architecture\"\n\"1235\",\"appcelerator-titanium\"\n\"1235\",\"worksheet\"\n\"1234\",\"phone-number\"\n\"1234\",\"hibernate-validator\"\n\"1234\",\"angularjs-filter\"\n\"1233\",\"filesystemwatcher\"\n\"1233\",\"row-number\"\n\"1233\",\"android-tabs\"\n\"1233\",\"raspberry-pi2\"\n\"1233\",\"rider\"\n\"1233\",\"ntlm\"\n\"1232\",\"inertiajs\"\n\"1232\",\"c++-concepts\"\n\"1232\",\"iis-10\"\n\"1231\",\"initializer-list\"\n\"1231\",\"double-quotes\"\n\"1230\",\"instrumentation\"\n\"1229\",\"sendkeys\"\n\"1229\",\"swi-prolog\"\n\"1229\",\"url-parameters\"\n\"1228\",\"cloudant\"\n\"1228\",\"one-hot-encoding\"\n\"1228\",\"layout-inflater\"\n\"1227\",\"rxjs6\"\n\"1227\",\"type-hinting\"\n\"1227\",\"network-protocols\"\n\"1227\",\"headless\"\n\"1226\",\"addclass\"\n\"1226\",\"hortonworks-data-platform\"\n\"1226\",\"rdp\"\n\"1225\",\"sharing\"\n\"1225\",\"raycasting\"\n\"1224\",\"gridbaglayout\"\n\"1224\",\"friendly-url\"\n\"1224\",\"rollupjs\"\n\"1224\",\"camunda\"\n\"1224\",\"orbeon\"\n\"1224\",\"rhino-mocks\"\n\"1224\",\"gateway\"\n\"1224\",\"peewee\"\n\"1224\",\"idioms\"\n\"1223\",\"optional-parameters\"\n\"1223\",\"waterline\"\n\"1223\",\"can-bus\"\n\"1222\",\"node-webkit\"\n\"1221\",\"uitapgesturerecognizer\"\n\"1221\",\"docker-container\"\n\"1221\",\"java-ee-7\"\n\"1221\",\"qtablewidget\"\n\"1220\",\"nagios\"\n\"1220\",\"android-tablelayout\"\n\"1220\",\"webmatrix\"\n\"1220\",\"solid-principles\"\n\"1219\",\"jtree\"\n\"1219\",\"dynamics-ax-2012\"\n\"1218\",\"pyarrow\"\n\"1218\",\"setcookie\"\n\"1218\",\"android-navigation\"\n\"1218\",\"utf-16\"\n\"1218\",\"webdeploy\"\n\"1217\",\"fullpage.js\"\n\"1217\",\"outliers\"\n\"1217\",\"lines\"\n\"1216\",\"confluence\"\n\"1216\",\"windows-server-2003\"\n\"1216\",\"osmdroid\"\n\"1216\",\"aspect-ratio\"\n\"1215\",\"mapbox-gl\"\n\"1215\",\"asp.net-core-identity\"\n\"1215\",\"spline\"\n\"1215\",\"achartengine\"\n\"1213\",\"rpy2\"\n\"1213\",\"variant\"\n\"1213\",\"postgresql-9.5\"\n\"1213\",\"virtual-memory\"\n\"1213\",\"protege\"\n\"1213\",\"quarto\"\n\"1212\",\"netflix-zuul\"\n\"1212\",\"mkdir\"\n\"1212\",\"healthkit\"\n\"1212\",\"quartz-graphics\"\n\"1211\",\"phone-call\"\n\"1211\",\"rust-tokio\"\n\"1210\",\"adobe-indesign\"\n\"1210\",\"dynamics-crm-online\"\n\"1210\",\"protected\"\n\"1210\",\"state-management\"\n\"1210\",\"sudoku\"\n\"1209\",\"azure-ad-b2c-custom-policy\"\n\"1209\",\"ramda.js\"\n\"1209\",\"ace-editor\"\n\"1209\",\"stdstring\"\n\"1208\",\"paho\"\n\"1208\",\"password-encryption\"\n\"1208\",\"cpan\"\n\"1207\",\"mutable\"\n\"1207\",\"metaclass\"\n\"1206\",\"immutable.js\"\n\"1205\",\"key-value-observing\"\n\"1205\",\"dereference\"\n\"1205\",\"derived-class\"\n\"1205\",\"custom-taxonomy\"\n\"1204\",\"itemscontrol\"\n\"1201\",\"xna-4.0\"\n\"1201\",\"watermark\"\n\"1200\",\"websphere-8\"\n\"1200\",\"osx-elcapitan\"\n\"1200\",\"a-star\"\n\"1200\",\"android-mediacodec\"\n\"1198\",\"gherkin\"\n\"1198\",\"credit-card\"\n\"1198\",\"terraform-provider-gcp\"\n\"1198\",\"scrolltop\"\n\"1197\",\"r-plotly\"\n\"1197\",\"explorer\"\n\"1197\",\"static-members\"\n\"1195\",\"openapi-generator\"\n\"1195\",\"hierarchical-clustering\"\n\"1195\",\"tomcat9\"\n\"1195\",\"transfer\"\n\"1195\",\"subsonic\"\n\"1194\",\"crossfilter\"\n\"1194\",\"information-retrieval\"\n\"1193\",\"built-in\"\n\"1192\",\"workspace\"\n\"1192\",\"lda\"\n\"1192\",\"quantmod\"\n\"1191\",\"ksoap2\"\n\"1191\",\"stringstream\"\n\"1190\",\"flickr\"\n\"1190\",\"csh\"\n\"1190\",\"ionic-native\"\n\"1190\",\"controltemplate\"\n\"1190\",\"pass-by-value\"\n\"1189\",\"fancybox-2\"\n\"1189\",\"ttk\"\n\"1189\",\"shutil\"\n\"1189\",\"shinyapps\"\n\"1188\",\"timeline\"\n\"1188\",\"transformer-model\"\n\"1188\",\"mschart\"\n\"1187\",\"retina-display\"\n\"1187\",\"objectmapper\"\n\"1186\",\"steam\"\n\"1185\",\"jscript\"\n\"1185\",\"keyerror\"\n\"1185\",\"ag-grid-angular\"\n\"1185\",\"cognos\"\n\"1185\",\"scrollviewer\"\n\"1184\",\"user-permissions\"\n\"1183\",\"reshape2\"\n\"1182\",\"fluent\"\n\"1182\",\"system.text.json\"\n\"1182\",\"rbenv\"\n\"1181\",\"erd\"\n\"1180\",\"google-chrome-headless\"\n\"1180\",\"android-alarms\"\n\"1179\",\"populate\"\n\"1179\",\"custom-attributes\"\n\"1179\",\"having\"\n\"1178\",\"plc\"\n\"1178\",\"restful-architecture\"\n\"1178\",\"zk\"\n\"1177\",\"masking\"\n\"1177\",\"jfilechooser\"\n\"1176\",\"anypoint-studio\"\n\"1176\",\"kaggle\"\n\"1176\",\"timespan\"\n\"1176\",\"android-gallery\"\n\"1176\",\"geo\"\n\"1175\",\"hotkeys\"\n\"1175\",\"syslog\"\n\"1174\",\"public-key\"\n\"1173\",\"func\"\n\"1173\",\"facebook-sdk-4.0\"\n\"1173\",\"spring-cloud-config\"\n\"1172\",\"page-refresh\"\n\"1171\",\"lan\"\n\"1171\",\"nearest-neighbor\"\n\"1171\",\"mahout\"\n\"1171\",\"google-closure-compiler\"\n\"1171\",\"sqlcmd\"\n\"1171\",\"android-listfragment\"\n\"1169\",\"shapely\"\n\"1169\",\"azure-log-analytics\"\n\"1169\",\"confidence-interval\"\n\"1168\",\"react-dom\"\n\"1168\",\"automator\"\n\"1168\",\"zapier\"\n\"1167\",\"privacy\"\n\"1167\",\"hostname\"\n\"1167\",\"fold\"\n\"1166\",\"wizard\"\n\"1166\",\"xcode-ui-testing\"\n\"1166\",\"httpcontext\"\n\"1164\",\"osdev\"\n\"1163\",\"consul\"\n\"1163\",\"emr\"\n\"1163\",\"powerbi-embedded\"\n\"1162\",\"dbeaver\"\n\"1162\",\"dart-pub\"\n\"1161\",\"joomla1.5\"\n\"1161\",\"navigator\"\n\"1161\",\"bcp\"\n\"1160\",\"tridion\"\n\"1160\",\"confluent-schema-registry\"\n\"1160\",\"breadcrumbs\"\n\"1160\",\"asp.net-core-6.0\"\n\"1160\",\"cultureinfo\"\n\"1159\",\"case-insensitive\"\n\"1159\",\"propel\"\n\"1159\",\"cucumber-java\"\n\"1158\",\"opencart2.x\"\n\"1157\",\"ansible-inventory\"\n\"1157\",\"rbac\"\n\"1156\",\"onsen-ui\"\n\"1156\",\"nvm\"\n\"1155\",\"datadog\"\n\"1155\",\"sanitization\"\n\"1154\",\"audit\"\n\"1154\",\"c3p0\"\n\"1154\",\"dart-null-safety\"\n\"1153\",\"google-api-java-client\"\n\"1153\",\"conventions\"\n\"1153\",\"qgraphicsview\"\n\"1152\",\"weak-references\"\n\"1152\",\"rspec2\"\n\"1152\",\"same-origin-policy\"\n\"1152\",\"opensearch\"\n\"1151\",\"gevent\"\n\"1151\",\"objectify\"\n\"1151\",\"google-query-language\"\n\"1150\",\"swift-protocols\"\n\"1150\",\"callstack\"\n\"1150\",\"oracleforms\"\n\"1150\",\"crc\"\n\"1149\",\"file-extension\"\n\"1149\",\"dataflow\"\n\"1149\",\"infowindow\"\n\"1149\",\"browser-automation\"\n\"1149\",\"dicom\"\n\"1149\",\"nib\"\n\"1149\",\"googlemock\"\n\"1148\",\"vector-graphics\"\n\"1147\",\"debezium\"\n\"1147\",\"jdo\"\n\"1147\",\"remoting\"\n\"1147\",\"owasp\"\n\"1147\",\"jquery-deferred\"\n\"1147\",\".net-framework-version\"\n\"1146\",\"extjs5\"\n\"1146\",\"framebuffer\"\n\"1144\",\"fileinputstream\"\n\"1144\",\"jta\"\n\"1144\",\"sharepoint-designer\"\n\"1144\",\"system.reflection\"\n\"1144\",\"url-scheme\"\n\"1143\",\"stackexchange.redis\"\n\"1143\",\"freertos\"\n\"1143\",\"pybind11\"\n\"1143\",\"uinavigationitem\"\n\"1142\",\"system.drawing\"\n\"1141\",\"symfony5\"\n\"1141\",\"autoconf\"\n\"1140\",\"distutils\"\n\"1140\",\"aws-application-load-balancer\"\n\"1140\",\"measure\"\n\"1139\",\"python-3.3\"\n\"1139\",\"filewriter\"\n\"1139\",\"sqldatareader\"\n\"1139\",\"ios14\"\n\"1139\",\"mediastore\"\n\"1138\",\"cjk\"\n\"1138\",\"nat\"\n\"1137\",\"fifo\"\n\"1137\",\"azure-web-roles\"\n\"1137\",\"android-maps-v2\"\n\"1136\",\"flume\"\n\"1136\",\"http-status-code-401\"\n\"1136\",\"isabelle\"\n\"1135\",\"with-statement\"\n\"1134\",\"datamapper\"\n\"1134\",\"tortoisehg\"\n\"1133\",\"dask-distributed\"\n\"1133\",\"postgresql-9.2\"\n\"1133\",\"lighttpd\"\n\"1132\",\"flush\"\n\"1132\",\"python-3.9\"\n\"1132\",\"abstraction\"\n\"1132\",\"alter-table\"\n\"1131\",\"radgrid\"\n\"1130\",\"voice\"\n\"1130\",\"micropython\"\n\"1130\",\"object-detection-api\"\n\"1130\",\"elk\"\n\"1129\",\"python-c-api\"\n\"1129\",\"gurobi\"\n\"1129\",\"spawn\"\n\"1128\",\"alfresco-share\"\n\"1128\",\"google-app-maker\"\n\"1128\",\"textmate\"\n\"1127\",\"webservice-client\"\n\"1127\",\"fileoutputstream\"\n\"1127\",\"libc\"\n\"1127\",\"rate-limiting\"\n\"1127\",\"2d-games\"\n\"1127\",\"railstutorial.org\"\n\"1126\",\"ebay-api\"\n\"1126\",\"ckeditor5\"\n\"1126\",\"source-maps\"\n\"1125\",\"flexslider\"\n\"1124\",\"xlwings\"\n\"1124\",\"bootstrap-datepicker\"\n\"1124\",\"appsettings\"\n\"1124\",\"c3.js\"\n\"1124\",\"gdscript\"\n\"1124\",\"event-loop\"\n\"1124\",\"spacing\"\n\"1123\",\"class-design\"\n\"1123\",\"recordset\"\n\"1123\",\"bpmn\"\n\"1123\",\"asp.net-3.5\"\n\"1122\",\"add-on\"\n\"1122\",\"react-three-fiber\"\n\"1122\",\"dynamically-generated\"\n\"1122\",\"buildozer\"\n\"1122\",\"scipy-optimize\"\n\"1122\",\"string-interpolation\"\n\"1121\",\"bookmarks\"\n\"1121\",\"parse-cloud-code\"\n\"1120\",\"intentservice\"\n\"1120\",\"cloud9-ide\"\n\"1120\",\"mailto\"\n\"1120\",\"kvm\"\n\"1120\",\"rich-text-editor\"\n\"1120\",\"reactiveui\"\n\"1120\",\"illegalstateexception\"\n\"1120\",\"threadpoolexecutor\"\n\"1119\",\"mxml\"\n\"1119\",\"regexp-replace\"\n\"1118\",\"git-diff\"\n\"1118\",\"cdata\"\n\"1118\",\"hp-uft\"\n\"1118\",\"word-embedding\"\n\"1118\",\"sql-server-2019\"\n\"1118\",\"behat\"\n\"1117\",\"diacritics\"\n\"1116\",\"checkboxlist\"\n\"1116\",\"xmlreader\"\n\"1115\",\"getusermedia\"\n\"1115\",\"window.open\"\n\"1115\",\"nsfetchrequest\"\n\"1115\",\"android-cursor\"\n\"1115\",\"elasticsearch-aggregation\"\n\"1115\",\"stub\"\n\"1114\",\"refresh-token\"\n\"1114\",\"dotnetopenauth\"\n\"1114\",\"custom-data-attribute\"\n\"1113\",\"flask-socketio\"\n\"1113\",\"simple-injector\"\n\"1113\",\"handsontable\"\n\"1113\",\"tortoisegit\"\n\"1112\",\"trie\"\n\"1112\",\"playframework-2.2\"\n\"1112\",\"rvalue-reference\"\n\"1112\",\"digits\"\n\"1112\",\"lift\"\n\"1111\",\"backbone-views\"\n\"1111\",\"cakephp-2.3\"\n\"1111\",\"jmeter-5.0\"\n\"1111\",\"bmp\"\n\"1111\",\"qunit\"\n\"1111\",\"perf\"\n\"1111\",\"throttling\"\n\"1110\",\"rolling-computation\"\n\"1109\",\"integer-overflow\"\n\"1109\",\"ebean\"\n\"1108\",\"maze\"\n\"1107\",\"gridfs\"\n\"1107\",\"git-checkout\"\n\"1107\",\"database-partitioning\"\n\"1107\",\"bounding-box\"\n\"1107\",\"migrate\"\n\"1106\",\"date-arithmetic\"\n\"1106\",\"pubnub\"\n\"1106\",\"nancy\"\n\"1106\",\"sys\"\n\"1106\",\"comet\"\n\"1106\",\"tor\"\n\"1104\",\"confusion-matrix\"\n\"1104\",\"uploadify\"\n\"1104\",\"aws-code-deploy\"\n\"1104\",\"device-driver\"\n\"1103\",\"clob\"\n\"1103\",\"dlib\"\n\"1103\",\"infinispan\"\n\"1103\",\"knapsack-problem\"\n\"1103\",\"stdio\"\n\"1102\",\"sitecore6\"\n\"1102\",\"expand\"\n\"1101\",\"maven-assembly-plugin\"\n\"1101\",\"irc\"\n\"1101\",\"long-polling\"\n\"1101\",\"mediarecorder\"\n\"1100\",\"sqlconnection\"\n\"1100\",\"ejb-3.1\"\n\"1100\",\"alsa\"\n\"1099\",\"automake\"\n\"1099\",\"alpine.js\"\n\"1098\",\"listviewitem\"\n\"1098\",\"macports\"\n\"1096\",\"git-flow\"\n\"1096\",\"jasperserver\"\n\"1096\",\"javax.imageio\"\n\"1096\",\"mousewheel\"\n\"1096\",\"testcontainers\"\n\"1096\",\"msdeploy\"\n\"1095\",\"llvm-clang\"\n\"1095\",\"calling-convention\"\n\"1095\",\"infinite\"\n\"1095\",\"dot\"\n\"1094\",\"posixct\"\n\"1094\",\"pdfkit\"\n\"1094\",\"custom-component\"\n\"1093\",\"symfony-2.1\"\n\"1093\",\"nsurlrequest\"\n\"1092\",\"jogl\"\n\"1092\",\"bluej\"\n\"1092\",\"openjpa\"\n\"1091\",\"ag-grid-react\"\n\"1091\",\"msdn\"\n\"1090\",\"flask-login\"\n\"1090\",\"html.dropdownlistfor\"\n\"1090\",\"qlikview\"\n\"1089\",\"angularjs-ng-click\"\n\"1089\",\"box-api\"\n\"1089\",\"exit-code\"\n\"1088\",\"discrete-mathematics\"\n\"1088\",\"wikipedia-api\"\n\"1087\",\"mobile-development\"\n\"1087\",\"vaadin-flow\"\n\"1086\",\"ghci\"\n\"1086\",\"lexer\"\n\"1086\",\"provisioning\"\n\"1086\",\"zend-db\"\n\"1085\",\"typing\"\n\"1085\",\"android-logcat\"\n\"1084\",\"edge-detection\"\n\"1083\",\"react-native-maps\"\n\"1083\",\"cil\"\n\"1083\",\"doctype\"\n\"1083\",\"tm\"\n\"1082\",\"deferred\"\n\"1082\",\"textblock\"\n\"1082\",\"geom-bar\"\n\"1082\",\"autodesk\"\n\"1081\",\"docker-network\"\n\"1080\",\"listadapter\"\n\"1080\",\"huffman-code\"\n\"1079\",\"decompiling\"\n\"1079\",\"rtk-query\"\n\"1079\",\"java-2d\"\n\"1079\",\"forward-declaration\"\n\"1079\",\"resourcedictionary\"\n\"1077\",\"raytracing\"\n\"1077\",\"npm-scripts\"\n\"1077\",\"ipv4\"\n\"1077\",\"shell-exec\"\n\"1076\",\"salt-cryptography\"\n\"1076\",\"portable-executable\"\n\"1076\",\"openfiledialog\"\n\"1076\",\"ionic5\"\n\"1076\",\"libvlc\"\n\"1076\",\"google-contacts-api\"\n\"1075\",\"truffle\"\n\"1075\",\"enterprise\"\n\"1074\",\"docker-image\"\n\"1074\",\"currying\"\n\"1073\",\"apexcharts\"\n\"1073\",\"swiftui-navigationlink\"\n\"1073\",\"ibatis\"\n\"1073\",\"postscript\"\n\"1073\",\"u-boot\"\n\"1073\",\"msys2\"\n\"1072\",\"insertion-sort\"\n\"1072\",\"joomla-extensions\"\n\"1072\",\"surface\"\n\"1072\",\"wsh\"\n\"1072\",\"visual-studio-lightswitch\"\n\"1072\",\"timezone-offset\"\n\"1071\",\"rounded-corners\"\n\"1071\",\"servicenow\"\n\"1070\",\"ngrok\"\n\"1070\",\"ng-grid\"\n\"1070\",\"sitecore8\"\n\"1070\",\"revit-api\"\n\"1070\",\"moving-average\"\n\"1069\",\"pjsip\"\n\"1069\",\"goto\"\n\"1069\",\"crystal-reports-2008\"\n\"1069\",\"postcss\"\n\"1069\",\"non-ascii-characters\"\n\"1068\",\"backwards-compatibility\"\n\"1068\",\"unit-of-work\"\n\"1068\",\"spring-data-redis\"\n\"1068\",\"java-threads\"\n\"1068\",\"behavior\"\n\"1067\",\"servicebus\"\n\"1067\",\"resque\"\n\"1067\",\"performancecounter\"\n\"1066\",\"xlib\"\n\"1066\",\"winscp\"\n\"1066\",\"minimum\"\n\"1066\",\"azure-servicebus-queues\"\n\"1065\",\"python-unicode\"\n\"1065\",\"scaffolding\"\n\"1065\",\"microsoft-graph-teams\"\n\"1065\",\"iso\"\n\"1065\",\"asp.net-identity-2\"\n\"1064\",\"saas\"\n\"1064\",\"azure-cloud-services\"\n\"1064\",\"boolean-expression\"\n\"1064\",\"polyline\"\n\"1064\",\"uipangesturerecognizer\"\n\"1064\",\"gnu-assembler\"\n\"1064\",\"pretty-print\"\n\"1063\",\"cpu-cache\"\n\"1063\",\"android-framelayout\"\n\"1063\",\"shift\"\n\"1062\",\"oracle19c\"\n\"1062\",\"openai-gym\"\n\"1062\",\"business-objects\"\n\"1062\",\"azure-pipelines-build-task\"\n\"1062\",\"zero\"\n\"1061\",\"runtimeexception\"\n\"1061\",\"apt-get\"\n\"1061\",\"allure\"\n\"1060\",\"temporary-files\"\n\"1060\",\"tslint\"\n\"1060\",\"locationmanager\"\n\"1060\",\"updating\"\n\"1060\",\"roc\"\n\"1060\",\"access-denied\"\n\"1059\",\"signing\"\n\"1059\",\"httpwebresponse\"\n\"1059\",\"string-length\"\n\"1058\",\"coldfusion-10\"\n\"1058\",\"glew\"\n\"1057\",\"internal-server-error\"\n\"1057\",\"armadillo\"\n\"1057\",\"transactionscope\"\n\"1057\",\"avaudiosession\"\n\"1056\",\"inappbrowser\"\n\"1056\",\"facebook-page\"\n\"1056\",\"android-viewholder\"\n\"1055\",\"ssh-tunnel\"\n\"1055\",\"effects\"\n\"1055\",\"att\"\n\"1055\",\"mac-address\"\n\"1055\",\"access-modifiers\"\n\"1054\",\"asp.net-web-api-routing\"\n\"1054\",\"custom-action\"\n\"1054\",\"heroku-postgres\"\n\"1053\",\"sweetalert\"\n\"1053\",\"mdm\"\n\"1053\",\"suitescript2.0\"\n\"1052\",\"many-to-one\"\n\"1052\",\"firebase-tools\"\n\"1052\",\"radio-group\"\n\"1052\",\"gac\"\n\"1051\",\"yahoo\"\n\"1051\",\"xcode4.3\"\n\"1050\",\"scalaz\"\n\"1049\",\"selenium-firefoxdriver\"\n\"1049\",\"cisco\"\n\"1049\",\"mongodb-java\"\n\"1049\",\"drawrect\"\n\"1048\",\"laravel-10\"\n\"1048\",\"pixi.js\"\n\"1048\",\"visual-studio-mac\"\n\"1048\",\"hl7-fhir\"\n\"1048\",\"android-file\"\n\"1047\",\"chrome-extension-manifest-v3\"\n\"1047\",\"voiceover\"\n\"1047\",\"nsoperationqueue\"\n\"1047\",\"buttonclick\"\n\"1047\",\"httr\"\n\"1047\",\"gmp\"\n\"1046\",\"recurrence\"\n\"1046\",\"ios-autolayout\"\n\"1045\",\"greedy\"\n\"1045\",\"modx\"\n\"1045\",\"tail\"\n\"1045\",\"eigen3\"\n\"1044\",\"tcpdump\"\n\"1044\",\"preload\"\n\"1044\",\"parentheses\"\n\"1043\",\"monaco-editor\"\n\"1043\",\"spring-webclient\"\n\"1042\",\"cell-array\"\n\"1042\",\"agora.io\"\n\"1042\",\"watin\"\n\"1042\",\"brackets\"\n\"1042\",\"bookdown\"\n\"1042\",\"c++-chrono\"\n\"1042\",\"uikeyboard\"\n\"1042\",\"imageicon\"\n\"1041\",\"git-lfs\"\n\"1041\",\"nant\"\n\"1041\",\"android-contentresolver\"\n\"1041\",\"ms-project\"\n\"1040\",\"unsafe\"\n\"1040\",\"docker-desktop\"\n\"1040\",\"deno\"\n\"1040\",\"global-asax\"\n\"1039\",\"lapack\"\n\"1039\",\"shapeless\"\n\"1039\",\"logarithm\"\n\"1038\",\"php-extension\"\n\"1038\",\"samba\"\n\"1037\",\"stack-memory\"\n\"1036\",\"class-library\"\n\"1036\",\"youtube-dl\"\n\"1036\",\"wildfly-8\"\n\"1036\",\"rails-migrations\"\n\"1036\",\"lowercase\"\n\"1036\",\"menubar\"\n\"1035\",\"proc\"\n\"1035\",\"jtextpane\"\n\"1035\",\"nstextfield\"\n\"1035\",\"spam\"\n\"1034\",\"naivebayes\"\n\"1034\",\"linqpad\"\n\"1033\",\"detect\"\n\"1033\",\"nose\"\n\"1033\",\"promql\"\n\"1033\",\"parallel.foreach\"\n\"1032\",\"localdb\"\n\"1032\",\"adobe-illustrator\"\n\"1032\",\"geodjango\"\n\"1032\",\"binance\"\n\"1031\",\"xunit.net\"\n\"1031\",\"agile\"\n\"1031\",\"intuit-partner-platform\"\n\"1030\",\"vega\"\n\"1030\",\"introspection\"\n\"1029\",\"android-proguard\"\n\"1028\",\"dalvik\"\n\"1027\",\"aws-sam\"\n\"1026\",\"multiple-instances\"\n\"1026\",\"semantic-markup\"\n\"1026\",\"mongoose-populate\"\n\"1026\",\"searchbar\"\n\"1026\",\"dart-polymer\"\n\"1025\",\"least-squares\"\n\"1024\",\"ggmap\"\n\"1024\",\"opencsv\"\n\"1023\",\"jdialog\"\n\"1023\",\"gravity-forms-plugin\"\n\"1023\",\"wpftoolkit\"\n\"1023\",\"bucket\"\n\"1022\",\"pointer-to-member\"\n\"1022\",\"curses\"\n\"1022\",\"gnu-screen\"\n\"1022\",\"zebra-printers\"\n\"1021\",\"multitasking\"\n\"1021\",\"keyword-argument\"\n\"1021\",\"paypal-adaptive-payments\"\n\"1021\",\"esri\"\n\"1020\",\"fish\"\n\"1020\",\"konvajs\"\n\"1020\",\"huggingface\"\n\"1020\",\"custom-element\"\n\"1019\",\"package-managers\"\n\"1019\",\"myisam\"\n\"1019\",\"django-south\"\n\"1019\",\"taskbar\"\n\"1019\",\"resx\"\n\"1019\",\"ipywidgets\"\n\"1018\",\"sqlsrv\"\n\"1018\",\"swifty-json\"\n\"1018\",\"xml-deserialization\"\n\"1018\",\"sql-loader\"\n\"1017\",\"selecteditem\"\n\"1017\",\"testcase\"\n\"1016\",\"xstream\"\n\"1016\",\"sqlclr\"\n\"1016\",\"isset\"\n\"1016\",\"pem\"\n\"1015\",\"extjs3\"\n\"1015\",\"spaces\"\n\"1014\",\"anonymous-types\"\n\"1014\",\"cloudera-cdh\"\n\"1014\",\"sql-view\"\n\"1014\",\"datastax-java-driver\"\n\"1013\",\"flutter-bloc\"\n\"1013\",\"inject\"\n\"1013\",\"ofstream\"\n\"1012\",\"bandwidth\"\n\"1012\",\"xml-validation\"\n\"1012\",\"kotlin-flow\"\n\"1012\",\"vhosts\"\n\"1011\",\"cosine-similarity\"\n\"1011\",\"chaining\"\n\"1010\",\"pyqtgraph\"\n\"1010\",\"google-cloud-ml\"\n\"1010\",\"amazon-neptune\"\n\"1009\",\"javacard\"\n\"1008\",\"truetype\"\n\"1008\",\"knockout-2.0\"\n\"1008\",\"pyparsing\"\n\"1007\",\"onmouseover\"\n\"1007\",\"polynomials\"\n\"1007\",\"deque\"\n\"1007\",\"aar\"\n\"1007\",\"async.js\"\n\"1006\",\"vala\"\n\"1006\",\"cakephp-2.1\"\n\"1006\",\"javascript-framework\"\n\"1006\",\"frames\"\n\"1006\",\"tinkerpop\"\n\"1006\",\"scene\"\n\"1006\",\"pywinauto\"\n\"1006\",\"spark-cassandra-connector\"\n\"1005\",\"backslash\"\n\"1005\",\"jboss-arquillian\"\n\"1005\",\"findbugs\"\n\"1005\",\"windows-server-2016\"\n\"1005\",\"easeljs\"\n\"1005\",\"infopath\"\n\"1004\",\"jquery-ui-accordion\"\n\"1004\",\"eloquent-relationship\"\n\"1004\",\"arduino-esp8266\"\n\"1003\",\"spfx\"\n\"1003\",\"subscribe\"\n\"1002\",\"jslint\"\n\"1002\",\"easymock\"\n\"1002\",\"pascalscript\"\n\"1001\",\"joi\"\n\"1001\",\"android-download-manager\"\n\"1000\",\"telerik-mvc\"\n\"1000\",\"interaction\"\n\"1000\",\"azure-bot-service\"\n\"1000\",\"shiny-reactivity\"\n\"999\",\"managed-bean\"\n\"998\",\"bluez\"\n\"998\",\"uiactionsheet\"\n\"998\",\"oncreate\"\n\"997\",\"swagger-codegen\"\n\"997\",\"android-calendar\"\n\"997\",\"url-encoding\"\n\"996\",\"playframework-2.1\"\n\"996\",\"macos-big-sur\"\n\"996\",\"asyncstorage\"\n\"996\",\"signed\"\n\"996\",\"node-gyp\"\n\"994\",\"vagrantfile\"\n\"994\",\"opc-ua\"\n\"993\",\"framer-motion\"\n\"993\",\"onactivityresult\"\n\"992\",\"python-dataclasses\"\n\"992\",\"pdflatex\"\n\"992\",\"winsock2\"\n\"992\",\"typo3-9.x\"\n\"991\",\"spring-el\"\n\"991\",\"dynamic-data\"\n\"990\",\"smooth-scrolling\"\n\"990\",\"robotium\"\n\"990\",\"hta\"\n\"990\",\"destroy\"\n\"990\",\"miniconda\"\n\"989\",\"n-tier-architecture\"\n\"988\",\"datastore\"\n\"988\",\"javacv\"\n\"988\",\"mode\"\n\"988\",\"uitabbaritem\"\n\"988\",\"google-geocoder\"\n\"988\",\"composite-primary-key\"\n\"987\",\"db2-400\"\n\"987\",\"pseudo-class\"\n\"987\",\"body-parser\"\n\"987\",\"minio\"\n\"987\",\"topic-modeling\"\n\"986\",\"function-definition\"\n\"986\",\"rule-engine\"\n\"986\",\"cryptocurrency\"\n\"986\",\"android-appwidget\"\n\"986\",\"reusability\"\n\"986\",\"brute-force\"\n\"986\",\"uiactivityindicatorview\"\n\"986\",\"gdata\"\n\"986\",\"resampling\"\n\"986\",\"concurrent.futures\"\n\"985\",\"playframework-2.3\"\n\"985\",\"tty\"\n\"984\",\"docker-for-windows\"\n\"984\",\"enumerate\"\n\"984\",\"axes\"\n\"984\",\"rhino\"\n\"984\",\"android-keystore\"\n\"984\",\"amazon-ecr\"\n\"983\",\"native-base\"\n\"983\",\"opentk\"\n\"983\",\"google-custom-search\"\n\"982\",\"appendchild\"\n\"982\",\"ruby-on-rails-7\"\n\"982\",\".net-core-3.1\"\n\"982\",\"mkannotationview\"\n\"982\",\"membership-provider\"\n\"982\",\"custom-adapter\"\n\"981\",\"file-format\"\n\"981\",\"nsoperation\"\n\"981\",\"texture-mapping\"\n\"980\",\"vscode-remote\"\n\"980\",\"sage\"\n\"980\",\"onitemclicklistener\"\n\"979\",\"primitive\"\n\"979\",\"antialiasing\"\n\"979\",\"grayscale\"\n\"979\",\"jshint\"\n\"979\",\"fine-uploader\"\n\"979\",\"pyside6\"\n\"979\",\"cocoa-bindings\"\n\"979\",\"string-parsing\"\n\"978\",\"levenshtein-distance\"\n\"978\",\"foundation\"\n\"978\",\"tapestry\"\n\"978\",\"android-camera-intent\"\n\"978\",\"gawk\"\n\"978\",\"illegalargumentexception\"\n\"977\",\"preventdefault\"\n\"977\",\"rselenium\"\n\"977\",\"gspread\"\n\"977\",\"audiokit\"\n\"977\",\"regex-greedy\"\n\"977\",\"nodemcu\"\n\"977\",\"eventemitter\"\n\"977\",\"cgcontext\"\n\"977\",\"membership\"\n\"977\",\"composite\"\n\"976\",\"pulp\"\n\"976\",\"ntfs\"\n\"976\",\"python-wheel\"\n\"975\",\"arcgis-js-api\"\n\"974\",\"jboss6.x\"\n\"974\",\"csom\"\n\"974\",\"woocommerce-rest-api\"\n\"974\",\"xcode10\"\n\"974\",\"monkeypatching\"\n\"972\",\"datalist\"\n\"972\",\"dispatch\"\n\"972\",\"turbolinks\"\n\"972\",\"android-10.0\"\n\"972\",\"mousemove\"\n\"971\",\"json-ld\"\n\"971\",\"simplecursoradapter\"\n\"971\",\"gpuimage\"\n\"971\",\"pull\"\n\"971\",\"wso2-enterprise-integrator\"\n\"971\",\"uiinterfaceorientation\"\n\"971\",\"webapp2\"\n\"970\",\"apache-spark-dataset\"\n\"970\",\"azure-automation\"\n\"970\",\"posts\"\n\"970\",\"osgi-bundle\"\n\"970\",\"sqldatasource\"\n\"970\",\"rest-client\"\n\"969\",\"bootstrap-table\"\n\"969\",\"outlook-restapi\"\n\"969\",\"sqlbulkcopy\"\n\"969\",\"rails-routing\"\n\"969\",\"nonlinear-optimization\"\n\"968\",\"google-fit\"\n\"968\",\"google-earth\"\n\"968\",\"compile-time\"\n\"967\",\"tsc\"\n\"967\",\"stackdriver\"\n\"967\",\"extjs6\"\n\"967\",\"elasticsearch-plugin\"\n\"967\",\"angular-http\"\n\"966\",\"instanceof\"\n\"966\",\"cartopy\"\n\"966\",\"separator\"\n\"966\",\"apache-tomee\"\n\"966\",\"scala-cats\"\n\"966\",\"quasar\"\n\"966\",\"user-roles\"\n\"965\",\"templating\"\n\"965\",\"fuzzy-search\"\n\"965\",\"thrust\"\n\"964\",\"realitykit\"\n\"964\",\"random-seed\"\n\"964\",\"system-administration\"\n\"964\",\"rails-admin\"\n\"964\",\"uiswitch\"\n\"964\",\"strong-parameters\"\n\"964\",\"beanshell\"\n\"963\",\"ngx-bootstrap\"\n\"963\",\"gtkmm\"\n\"963\",\"video-encoding\"\n\"963\",\"text-alignment\"\n\"962\",\"mysql-5.7\"\n\"962\",\"delphi-2009\"\n\"962\",\"elmah\"\n\"961\",\"serial-communication\"\n\"961\",\"cakephp-3.x\"\n\"961\",\"wiremock\"\n\"961\",\"color-scheme\"\n\"960\",\"vapor\"\n\"960\",\"macvim\"\n\"960\",\"spray\"\n\"959\",\"git-log\"\n\"959\",\"imputation\"\n\"959\",\"x509certificate2\"\n\"959\",\"titan\"\n\"958\",\"mat\"\n\"958\",\"ngrx-effects\"\n\"957\",\"adaptive-cards\"\n\"957\",\"openwrt\"\n\"957\",\"strcmp\"\n\"957\",\"git-remote\"\n\"956\",\"processor\"\n\"956\",\"bootstrapping\"\n\"956\",\"overlapping\"\n\"956\",\"speech\"\n\"955\",\"cashapelayer\"\n\"955\",\"apc\"\n\"955\",\"jgit\"\n\"955\",\"ws-security\"\n\"955\",\"visible\"\n\"954\",\"masonry\"\n\"954\",\"mysql-8.0\"\n\"954\",\"noise\"\n\"954\",\"propertygrid\"\n\"954\",\"google-cloud-vertex-ai\"\n\"953\",\"taglib\"\n\"953\",\"oh-my-zsh\"\n\"953\",\"strcpy\"\n\"952\",\"terminate\"\n\"952\",\"sendmessage\"\n\"952\",\"winston\"\n\"952\",\"coordinate-systems\"\n\"952\",\"hue\"\n\"951\",\"mandrill\"\n\"951\",\"dockerhub\"\n\"951\",\"android-youtube-api\"\n\"951\",\"outlook-2010\"\n\"950\",\"magento-1.8\"\n\"950\",\"pyopengl\"\n\"950\",\"asp.net-mvc-partialview\"\n\"950\",\"screen-readers\"\n\"950\",\"euclidean-distance\"\n\"950\",\"hdl\"\n\"949\",\"bulk\"\n\"949\",\"tile\"\n\"948\",\"hidden-field\"\n\"947\",\"odoo-11\"\n\"946\",\"nativescript-angular\"\n\"946\",\"aws-secrets-manager\"\n\"945\",\"pageload\"\n\"945\",\"grpc-java\"\n\"945\",\"epub\"\n\"945\",\"mdi\"\n\"944\",\"coalesce\"\n\"944\",\"coreml\"\n\"944\",\"pop3\"\n\"944\",\"extendscript\"\n\"944\",\"bulma\"\n\"943\",\"db2-luw\"\n\"943\",\"serde\"\n\"943\",\"dev-c++\"\n\"943\",\"space-complexity\"\n\"942\",\"banner\"\n\"942\",\"appium-ios\"\n\"942\",\"azure-bicep\"\n\"942\",\"movieclip\"\n\"941\",\"mysql2\"\n\"941\",\"browser-sync\"\n\"941\",\"asp.net-core-2.2\"\n\"941\",\"drive\"\n\"941\",\"look-and-feel\"\n\"941\",\"getchar\"\n\"940\",\"cron-task\"\n\"940\",\"node-sass\"\n\"939\",\"background-service\"\n\"939\",\"apache-spark-ml\"\n\"939\",\"django-settings\"\n\"939\",\"xcode-storyboard\"\n\"939\",\"azure-managed-identity\"\n\"939\",\"odoo-12\"\n\"938\",\"file-writing\"\n\"938\",\"azure-hdinsight\"\n\"937\",\"apache2.4\"\n\"937\",\"laravel-backpack\"\n\"937\",\"servlet-3.0\"\n\"937\",\"avl-tree\"\n\"937\",\"box\"\n\"937\",\"summernote\"\n\"936\",\"graphite\"\n\"936\",\"horizontalscrollview\"\n\"936\",\"scatter\"\n\"935\",\"saxparser\"\n\"935\",\"form-for\"\n\"935\",\"wordnet\"\n\"935\",\"minimax\"\n\"935\",\"jaas\"\n\"935\",\"sqflite\"\n\"935\",\"force-layout\"\n\"934\",\"sequential\"\n\"934\",\"abi\"\n\"934\",\"azure-stream-analytics\"\n\"933\",\"snowflake-schema\"\n\"933\",\"robocopy\"\n\"932\",\"wildfly-10\"\n\"932\",\"javasound\"\n\"931\",\"gimp\"\n\"931\",\"pharo\"\n\"931\",\"tcplistener\"\n\"930\",\"ng-options\"\n\"930\",\"docx4j\"\n\"929\",\"cname\"\n\"929\",\"bios\"\n\"928\",\"fasta\"\n\"928\",\"rancher\"\n\"927\",\"blas\"\n\"927\",\"facet-wrap\"\n\"927\",\"ksqldb\"\n\"927\",\"google-assistant-sdk\"\n\"927\",\"pyqt6\"\n\"926\",\"windows-task-scheduler\"\n\"926\",\"iso8601\"\n\"926\",\"amazon-elasticache\"\n\"925\",\"python-logging\"\n\"925\",\"pyglet\"\n\"925\",\"bytebuffer\"\n\"924\",\"data-transfer\"\n\"924\",\"addressbook\"\n\"924\",\"typeahead\"\n\"924\",\"observers\"\n\"924\",\"sqlcommand\"\n\"924\",\"google-cloud-vision\"\n\"923\",\"sql-function\"\n\"923\",\"openvpn\"\n\"923\",\"generative-adversarial-network\"\n\"923\",\"restangular\"\n\"923\",\"array-broadcasting\"\n\"922\",\"symfony-3.4\"\n\"922\",\"micro-optimization\"\n\"922\",\"macos-mojave\"\n\"922\",\"openoffice.org\"\n\"922\",\"android-networking\"\n\"922\",\"powershell-5.0\"\n\"922\",\"mcrypt\"\n\"921\",\"regular-language\"\n\"921\",\"class-method\"\n\"921\",\"wikidata\"\n\"921\",\"go-gin\"\n\"921\",\"vertex-shader\"\n\"920\",\"release-management\"\n\"920\",\"swingworker\"\n\"920\",\"rbind\"\n\"920\",\"fortify\"\n\"920\",\"tibco\"\n\"919\",\"smb\"\n\"919\",\"fiware-orion\"\n\"919\",\"kendo-ui-angular2\"\n\"919\",\"timertask\"\n\"919\",\"tagging\"\n\"919\",\"bxslider\"\n\"919\",\"onbeforeunload\"\n\"919\",\"gltf\"\n\"918\",\"include-path\"\n\"918\",\"neon\"\n\"918\",\"isolatedstorage\"\n\"917\",\"editing\"\n\"917\",\"libreoffice-calc\"\n\"917\",\"titanium-alloy\"\n\"917\",\"mule-component\"\n\"916\",\"adjacency-matrix\"\n\"916\",\"w3c-validation\"\n\"916\",\"supertest\"\n\"916\",\"knockout-mapping-plugin\"\n\"916\",\"csvhelper\"\n\"915\",\"antlr3\"\n\"915\",\"session-storage\"\n\"915\",\"developer-tools\"\n\"915\",\"postsharp\"\n\"915\",\"weblogic-10.x\"\n\"914\",\"try-except\"\n\"914\",\"reportingservices-2005\"\n\"914\",\"alembic\"\n\"914\",\"boot2docker\"\n\"914\",\"dvcs\"\n\"914\",\"asp.net-2.0\"\n\"914\",\"sonar-runner\"\n\"913\",\"jboss5.x\"\n\"913\",\"mfmailcomposeviewcontroller\"\n\"912\",\"apache-felix\"\n\"912\",\"data-processing\"\n\"912\",\"facebook-access-token\"\n\"911\",\"paypal-subscriptions\"\n\"911\",\"c11\"\n\"910\",\"renderer\"\n\"910\",\"chunks\"\n\"910\",\"cgo\"\n\"910\",\"lockscreen\"\n\"909\",\"onnx\"\n\"909\",\"delphi-xe7\"\n\"909\",\"subnet\"\n\"908\",\"multi-module\"\n\"908\",\"phpdoc\"\n\"908\",\"gekko\"\n\"907\",\"azure-application-gateway\"\n\"907\",\"asp.net-core-signalr\"\n\"907\",\"glade\"\n\"906\",\"feature-detection\"\n\"906\",\"data-extraction\"\n\"906\",\"vbo\"\n\"906\",\"android-uiautomator\"\n\"906\",\"abp-framework\"\n\"906\",\"pyenv\"\n\"906\",\"signalr.client\"\n\"906\",\"autosuggest\"\n\"906\",\"glassfish-4\"\n\"906\",\"batch-rename\"\n\"905\",\"reportbuilder3.0\"\n\"905\",\"fftw\"\n\"905\",\"yii-extensions\"\n\"905\",\"netcat\"\n\"905\",\"locust\"\n\"905\",\"imagej\"\n\"904\",\"dygraphs\"\n\"904\",\"required\"\n\"903\",\"caret\"\n\"903\",\"pycrypto\"\n\"903\",\"spring-integration-dsl\"\n\"903\",\"polyfills\"\n\"902\",\"bitmapimage\"\n\"902\",\"datacontractserializer\"\n\"902\",\"pdfsharp\"\n\"902\",\"android-textinputlayout\"\n\"901\",\"django-users\"\n\"901\",\"iboutlet\"\n\"901\",\"nsobject\"\n\"901\",\"laravel-nova\"\n\"901\",\"qt-quick\"\n\"901\",\"liferay-7\"\n\"900\",\"lwuit\"\n\"900\",\"colormap\"\n\"900\",\"elasticsearch-dsl\"\n\"900\",\"qgraphicsscene\"\n\"899\",\"nrwl-nx\"\n\"899\",\"keep-alive\"\n\"899\",\"quickbooks-online\"\n\"899\",\"nuget-package-restore\"\n\"898\",\"controllers\"\n\"897\",\"materialized-views\"\n\"897\",\"graalvm\"\n\"897\",\"serializable\"\n\"897\",\"postmessage\"\n\"897\",\"modernizr\"\n\"896\",\"guard\"\n\"896\",\"launcher\"\n\"896\",\"throw\"\n\"896\",\"powershell-cmdlet\"\n\"895\",\"carriage-return\"\n\"895\",\"e4\"\n\"895\",\"pdb\"\n\"895\",\"moss\"\n\"894\",\"intel-fortran\"\n\"894\",\"ibaction\"\n\"894\",\"in-memory-database\"\n\"894\",\"devextreme\"\n\"894\",\"npx\"\n\"893\",\"bing\"\n\"893\",\"symbian\"\n\"893\",\"payload\"\n\"893\",\"janusgraph\"\n\"893\",\"exponential\"\n\"892\",\"datanucleus\"\n\"892\",\"windows-11\"\n\"892\",\"django-signals\"\n\"892\",\"formatter\"\n\"892\",\"android-build\"\n\"892\",\"amazon-ami\"\n\"891\",\"application-pool\"\n\"891\",\"sbcl\"\n\"890\",\"ssas-tabular\"\n\"890\",\"managed\"\n\"890\",\"hibernate-envers\"\n\"890\",\"sdwebimage\"\n\"890\",\"usability\"\n\"889\",\"ormlite-servicestack\"\n\"889\",\"args\"\n\"889\",\"webpack-5\"\n\"888\",\"jedis\"\n\"888\",\"vitest\"\n\"888\",\"android-jetpack-navigation\"\n\"887\",\"cartesian-product\"\n\"887\",\"web.py\"\n\"886\",\"spring-cloud-netflix\"\n\"886\",\"typo3-7.6.x\"\n\"886\",\"color-picker\"\n\"886\",\"laravel-validation\"\n\"886\",\"solr4\"\n\"885\",\"php-5.6\"\n\"885\",\"xpages-ssjs\"\n\"885\",\"enter\"\n\"885\",\"dice\"\n\"884\",\"standard-deviation\"\n\"884\",\"hyperparameters\"\n\"884\",\"libusb\"\n\"884\",\"android-3.0-honeycomb\"\n\"884\",\"angular-http-interceptors\"\n\"883\",\"bit-fields\"\n\"883\",\"malware\"\n\"883\",\"datatrigger\"\n\"883\",\"sqlexception\"\n\"883\",\"cucumberjs\"\n\"883\",\"tex\"\n\"882\",\"indices\"\n\"882\",\"pg\"\n\"881\",\"debug-symbols\"\n\"881\",\"dma\"\n\"881\",\"algebra\"\n\"881\",\"ms-media-foundation\"\n\"880\",\"n-gram\"\n\"880\",\"xelement\"\n\"880\",\"case-when\"\n\"880\",\"delphi-xe5\"\n\"880\",\"code-reuse\"\n\"880\",\"facebook-ads-api\"\n\"880\",\"dictionary-comprehension\"\n\"880\",\"dotenv\"\n\"880\",\"lotus\"\n\"880\",\"three20\"\n\"879\",\"fluid-layout\"\n\"879\",\"runtime.exec\"\n\"879\",\"names\"\n\"879\",\"data-fitting\"\n\"879\",\"reverse-geocoding\"\n\"878\",\"effect\"\n\"878\",\"ngroute\"\n\"878\",\"win32gui\"\n\"878\",\"boost-thread\"\n\"878\",\"tasm\"\n\"878\",\"double-click\"\n\"878\",\"asp.net-core-1.0\"\n\"877\",\"pinia\"\n\"877\",\"sikuli\"\n\"877\",\"android-cursoradapter\"\n\"877\",\"android-orientation\"\n\"877\",\"moxy\"\n\"877\",\"meanjs\"\n\"877\",\"summarize\"\n\"876\",\"gsutil\"\n\"876\",\"android-7.0-nougat\"\n\"876\",\"android-camerax\"\n\"876\",\"mediaelement\"\n\"875\",\"phpspreadsheet\"\n\"875\",\"dex\"\n\"874\",\"feathersjs\"\n\"874\",\"ng-class\"\n\"874\",\"smlnj\"\n\"874\",\"openerp-7\"\n\"874\",\"pmd\"\n\"874\",\"nstextview\"\n\"874\",\"std-function\"\n\"873\",\"badge\"\n\"873\",\"microdata\"\n\"873\",\"entity-attribute-value\"\n\"873\",\"cfml\"\n\"873\",\"mql4\"\n\"872\",\"grid-search\"\n\"872\",\"clang-format\"\n\"872\",\"pinterest\"\n\"872\",\"magicalrecord\"\n\"872\",\"branch.io\"\n\"872\",\"gamekit\"\n\"872\",\"battery\"\n\"872\",\"thinking-sphinx\"\n\"872\",\"utf\"\n\"872\",\"beacon\"\n\"872\",\"amazon-cloudwatchlogs\"\n\"871\",\"sling\"\n\"871\",\"semantic-ui-react\"\n\"871\",\"packer\"\n\"871\",\"nix\"\n\"871\",\"cesiumjs\"\n\"870\",\"rpmbuild\"\n\"870\",\"twitter-fabric\"\n\"870\",\"blueimp\"\n\"870\",\"windows-store\"\n\"870\",\"scilab\"\n\"870\",\"iterm2\"\n\"870\",\"maven-release-plugin\"\n\"869\",\"contentful\"\n\"869\",\"markerclusterer\"\n\"869\",\"selected\"\n\"869\",\"red5\"\n\"869\",\"lottie\"\n\"869\",\"accessor\"\n\"868\",\"undo\"\n\"868\",\"failover\"\n\"868\",\"html-framework-7\"\n\"868\",\"forum\"\n\"868\",\"hadoop-streaming\"\n\"868\",\"morphia\"\n\"865\",\"xmppframework\"\n\"865\",\"universal-image-loader\"\n\"864\",\"flicker\"\n\"864\",\"jbossfuse\"\n\"864\",\"smt\"\n\"864\",\"mahapps.metro\"\n\"863\",\"imagemap\"\n\"863\",\"passport-local\"\n\"863\",\"memory-mapped-files\"\n\"863\",\"css-sprites\"\n\"863\",\"z3py\"\n\"862\",\"derivative\"\n\"862\",\"applicationcontext\"\n\"862\",\"cran\"\n\"862\",\"model-associations\"\n\"862\",\"dummy-variable\"\n\"862\",\"link-to\"\n\"861\",\"flash-cs6\"\n\"861\",\"keyframe\"\n\"861\",\"google-speech-api\"\n\"860\",\"rmagick\"\n\"860\",\"azure-notificationhub\"\n\"860\",\"reactive-cocoa\"\n\"859\",\"jenkins-job-dsl\"\n\"859\",\"eclipse-jdt\"\n\"859\",\"will-paginate\"\n\"859\",\"aws-security-group\"\n\"859\",\"pentaho-spoon\"\n\"859\",\"multilabel-classification\"\n\"858\",\"basic\"\n\"858\",\"platform\"\n\"858\",\"uber-api\"\n\"858\",\"points\"\n\"858\",\"highlighting\"\n\"858\",\"mobx-react\"\n\"858\",\"actioncable\"\n\"858\",\"panel-data\"\n\"857\",\"web-sql\"\n\"857\",\"minimize\"\n\"857\",\"android-broadcastreceiver\"\n\"857\",\"aspose\"\n\"857\",\"tablerow\"\n\"857\",\"google-fabric\"\n\"856\",\"phpseclib\"\n\"856\",\"documentation-generation\"\n\"856\",\"go-templates\"\n\"856\",\"maxima\"\n\"855\",\"gantt-chart\"\n\"854\",\"pageobjects\"\n\"854\",\"gcc-warning\"\n\"854\",\"openweathermap\"\n\"853\",\"envoyproxy\"\n\"853\",\"drawerlayout\"\n\"853\",\"podman\"\n\"853\",\"ellipse\"\n\"853\",\"google-drive-android-api\"\n\"853\",\"reachability\"\n\"852\",\"findall\"\n\"852\",\"windows-10-mobile\"\n\"852\",\"database-cursor\"\n\"852\",\"kestrel-http-server\"\n\"852\",\"uglifyjs\"\n\"852\",\"formbuilder\"\n\"852\",\"foxpro\"\n\"852\",\"android-handler\"\n\"852\",\"prototypal-inheritance\"\n\"852\",\"pentaho-data-integration\"\n\"852\",\"sunspot\"\n\"852\",\"urlfetch\"\n\"851\",\"trino\"\n\"851\",\"doc\"\n\"851\",\"sap-fiori\"\n\"851\",\"android-2.2-froyo\"\n\"850\",\"scala.js\"\n\"850\",\"atmega\"\n\"850\",\"android-maps\"\n\"849\",\"ggplotly\"\n\"849\",\"image-rotation\"\n\"849\",\"keycode\"\n\"849\",\"absolute-path\"\n\"849\",\"mlab\"\n\"849\",\"asp.net-core-3.0\"\n\"849\",\"melt\"\n\"849\",\"ios-charts\"\n\"849\",\"metamask\"\n\"848\",\"wifi-direct\"\n\"848\",\"blobstore\"\n\"848\",\"control-flow\"\n\"847\",\"lexical-analysis\"\n\"847\",\"sigabrt\"\n\"847\",\"stylus\"\n\"846\",\"agda\"\n\"846\",\"delphi-2007\"\n\"846\",\"pecl\"\n\"845\",\"kohana-3\"\n\"845\",\"dom-manipulation\"\n\"845\",\"alter\"\n\"845\",\"qt6\"\n\"844\",\"modulenotfounderror\"\n\"844\",\"stat\"\n\"843\",\"pki\"\n\"843\",\"mod-proxy\"\n\"843\",\"buildroot\"\n\"842\",\"palantir-foundry\"\n\"842\",\"jsf-1.2\"\n\"842\",\"modulus\"\n\"842\",\"azure-worker-roles\"\n\"842\",\"stopwatch\"\n\"842\",\"pyyaml\"\n\"842\",\"use-case\"\n\"841\",\"youtube-javascript-api\"\n\"841\",\"connection-timeout\"\n\"841\",\"proof\"\n\"841\",\"angular2-observables\"\n\"840\",\"intellij-14\"\n\"840\",\"file-type\"\n\"840\",\"connector\"\n\"840\",\"watchos-2\"\n\"840\",\"entities\"\n\"840\",\"numberformatexception\"\n\"840\",\"ethers.js\"\n\"840\",\"subclassing\"\n\"839\",\"firedac\"\n\"839\",\"flat-file\"\n\"839\",\"hosts\"\n\"839\",\"keil\"\n\"838\",\"google-api-nodejs-client\"\n\"838\",\"hashtag\"\n\"838\",\"pep8\"\n\"837\",\"vue-test-utils\"\n\"837\",\"intel-xdk\"\n\"837\",\"bioconductor\"\n\"837\",\"jsdom\"\n\"837\",\"android-external-storage\"\n\"837\",\"ios-universal-links\"\n\"836\",\"integral\"\n\"836\",\"multiple-monitors\"\n\"835\",\"default-constructor\"\n\"835\",\"triangulation\"\n\"835\",\"tree-traversal\"\n\"835\",\"apigee\"\n\"835\",\"rating\"\n\"835\",\"autofilter\"\n\"834\",\"rundeck\"\n\"834\",\"gulp-watch\"\n\"834\",\"vimeo-api\"\n\"834\",\"ubuntu-server\"\n\"834\",\"pygobject\"\n\"834\",\"type-mismatch\"\n\"834\",\"drake\"\n\"833\",\"enthought\"\n\"833\",\"android-4.2-jelly-bean\"\n\"833\",\"cgaffinetransform\"\n\"832\",\"ef-fluent-api\"\n\"832\",\"squarespace\"\n\"832\",\"rtti\"\n\"832\",\"newsletter\"\n\"832\",\"ria\"\n\"831\",\"pi\"\n\"831\",\"aerospike\"\n\"831\",\"kable\"\n\"831\",\"stored-functions\"\n\"831\",\"array-merge\"\n\"830\",\"flexdashboard\"\n\"830\",\"intel-mkl\"\n\"830\",\"django-crispy-forms\"\n\"830\",\"cp\"\n\"830\",\"postgresql-9.6\"\n\"829\",\"map-function\"\n\"829\",\"failed-installation\"\n\"829\",\"mailkit\"\n\"829\",\"httpmodule\"\n\"829\",\"google-developers-console\"\n\"828\",\"relationships\"\n\"828\",\"pylons\"\n\"827\",\"squid\"\n\"827\",\"imshow\"\n\"827\",\"twitch\"\n\"826\",\"multiple-tables\"\n\"826\",\"datatables-1.10\"\n\"826\",\"cumsum\"\n\"826\",\"spam-prevention\"\n\"826\",\"qtreeview\"\n\"825\",\"gravity\"\n\"825\",\"printwriter\"\n\"825\",\"ef-core-3.1\"\n\"825\",\"ui-testing\"\n\"825\",\"jtabbedpane\"\n\"825\",\"capistrano3\"\n\"825\",\"workbox\"\n\"825\",\"android-appbarlayout\"\n\"825\",\"sift\"\n\"825\",\"titlebar\"\n\"824\",\"jira-plugin\"\n\"824\",\"interstitial\"\n\"824\",\"google-places\"\n\"824\",\"tfvc\"\n\"823\",\"jersey-client\"\n\"823\",\"pexpect\"\n\"823\",\"web-api-testing\"\n\"823\",\"thread-sleep\"\n\"823\",\"amazon-mws\"\n\"822\",\"teechart\"\n\"822\",\"listitem\"\n\"822\",\"webview2\"\n\"822\",\"pgp\"\n\"822\",\"facebook-oauth\"\n\"822\",\"interrupt-handling\"\n\"822\",\"code-completion\"\n\"822\",\"geb\"\n\"822\",\"uisearchdisplaycontroller\"\n\"822\",\"culture\"\n\"822\",\"olap-cube\"\n\"822\",\"http-status-code-400\"\n\"822\",\"image-compression\"\n\"821\",\"jmockit\"\n\"821\",\"angular10\"\n\"821\",\"reactjs-flux\"\n\"820\",\"rlang\"\n\"820\",\"facebook-authentication\"\n\"820\",\"drupal-theming\"\n\"820\",\"webgrid\"\n\"819\",\"primitive-types\"\n\"819\",\"dbf\"\n\"819\",\"indy10\"\n\"819\",\"sweetalert2\"\n\"819\",\"setup-deployment\"\n\"818\",\"tycho\"\n\"818\",\"reducers\"\n\"818\",\"lighting\"\n\"817\",\"ckan\"\n\"817\",\"screen-capture\"\n\"817\",\"opentok\"\n\"817\",\"requestanimationframe\"\n\"816\",\"telephony\"\n\"816\",\"xslt-3.0\"\n\"816\",\"legacy\"\n\"816\",\"fractions\"\n\"815\",\"skphysicsbody\"\n\"815\",\"aws-event-bridge\"\n\"815\",\"suppress-warnings\"\n\"815\",\"shopify-api\"\n\"815\",\"azure-monitoring\"\n\"815\",\"lookup-tables\"\n\"815\",\"eventhandler\"\n\"815\",\"concurrenthashmap\"\n\"814\",\"proc-sql\"\n\"814\",\"ckeditor4.x\"\n\"814\",\"bittorrent\"\n\"814\",\"cells\"\n\"814\",\"corruption\"\n\"814\",\"sprockets\"\n\"813\",\"youtube-iframe-api\"\n\"813\",\"checked\"\n\"813\",\"led\"\n\"813\",\"c++03\"\n\"812\",\"smo\"\n\"812\",\"p-value\"\n\"812\",\"abaddressbook\"\n\"812\",\"organization\"\n\"812\",\"type-erasure\"\n\"812\",\"yql\"\n\"812\",\"billing\"\n\"812\",\"paragraph\"\n\"811\",\"websphere-7\"\n\"811\",\"bitbucket-server\"\n\"811\",\"rodbc\"\n\"810\",\"erp\"\n\"810\",\"libstdc++\"\n\"810\",\"opensuse\"\n\"810\",\"mudblazor\"\n\"810\",\"urlsession\"\n\"809\",\"epoll\"\n\"809\",\"gtk#\"\n\"809\",\"azure-media-services\"\n\"809\",\"qliksense\"\n\"809\",\"octopus-deploy\"\n\"808\",\"cllocation\"\n\"808\",\"dylib\"\n\"808\",\"error-logging\"\n\"808\",\"cqlsh\"\n\"808\",\"java-bytecode-asm\"\n\"808\",\"head\"\n\"807\",\"facebook-messenger-bot\"\n\"807\",\"gyroscope\"\n\"807\",\"google-earth-engine\"\n\"806\",\"greenplum\"\n\"806\",\"yii2-basic-app\"\n\"806\",\"static-variables\"\n\"805\",\"self-signed\"\n\"805\",\"hough-transform\"\n\"805\",\"mingw32\"\n\"805\",\"minecraft-forge\"\n\"805\",\"mlflow\"\n\"805\",\"c++-cx\"\n\"805\",\"touchscreen\"\n\"805\",\"webpacker\"\n\"804\",\"floating\"\n\"804\",\"windows-10-iot-core\"\n\"804\",\"puzzle\"\n\"804\",\"libpcap\"\n\"804\",\"ms-access-2003\"\n\"803\",\"weather\"\n\"803\",\"constraint-programming\"\n\"803\",\"firebase-cli\"\n\"803\",\"fosrestbundle\"\n\"802\",\"markers\"\n\"802\",\"fastify\"\n\"802\",\"pypy\"\n\"802\",\"caesar-cipher\"\n\"802\",\"glyphicons\"\n\"801\",\"polymer-2.x\"\n\"801\",\"sqliteopenhelper\"\n\"801\",\"memory-barriers\"\n\"801\",\"paperjs\"\n\"800\",\"psexec\"\n\"800\",\"bloomberg\"\n\"800\",\".net-standard-2.0\"\n\"800\",\"sparkr\"\n\"799\",\"datacontract\"\n\"799\",\"springfox\"\n\"799\",\"vis.js\"\n\"799\",\"exploit\"\n\"799\",\"openmodelica\"\n\"799\",\"lighthouse\"\n\"798\",\"opengl-3\"\n\"798\",\"emu8086\"\n\"797\",\"listactivity\"\n\"797\",\"python-3.10\"\n\"797\",\"fusioncharts\"\n\"797\",\"azure-appservice\"\n\"797\",\"erlang-otp\"\n\"797\",\"sanity\"\n\"797\",\"tabview\"\n\"797\",\"generic-list\"\n\"797\",\"persistent-volumes\"\n\"797\",\"url-rewrite-module\"\n\"797\",\"qtstylesheets\"\n\"795\",\"function-call\"\n\"795\",\"recharts\"\n\"795\",\"velo\"\n\"795\",\"asymptotic-complexity\"\n\"795\",\"vivado\"\n\"794\",\"window-resize\"\n\"794\",\"sankey-diagram\"\n\"794\",\"scala-macros\"\n\"794\",\"tbb\"\n\"794\",\"azureportal\"\n\"794\",\"static-files\"\n\"794\",\"user-defined-types\"\n\"793\",\"union-all\"\n\"793\",\"spring-bean\"\n\"793\",\"buddypress\"\n\"793\",\"macos-high-sierra\"\n\"793\",\"android-datepicker\"\n\"793\",\"byte-buddy\"\n\"793\",\"v-for\"\n\"793\",\"certbot\"\n\"793\",\"qstring\"\n\"792\",\"veins\"\n\"792\",\"iso-8859-1\"\n\"792\",\"idris\"\n\"791\",\"apdu\"\n\"791\",\"icefaces\"\n\"791\",\"pcm\"\n\"791\",\"machine-code\"\n\"791\",\"typeof\"\n\"791\",\"settext\"\n\"791\",\"is-empty\"\n\"791\",\"notepad\"\n\"791\",\"moviepy\"\n\"791\",\"flv\"\n\"791\",\"yuv\"\n\"791\",\"hebrew\"\n\"790\",\"grepl\"\n\"790\",\"multisite\"\n\"790\",\"rxjs-observables\"\n\"790\",\"watson-conversation\"\n\"790\",\"facebook-comments\"\n\"790\",\"ambari\"\n\"790\",\"rds\"\n\"790\",\"strptime\"\n\"790\",\"foreground-service\"\n\"789\",\"master-slave\"\n\"789\",\"ownership\"\n\"789\",\"android-11\"\n\"789\",\"android-audiomanager\"\n\"789\",\"geofencing\"\n\"788\",\"whm\"\n\"788\",\"backbone-events\"\n\"788\",\"telephonymanager\"\n\"788\",\"sony\"\n\"787\",\"application-settings\"\n\"787\",\"rails-engines\"\n\"787\",\"mediaelement.js\"\n\"786\",\"self-hosting\"\n\"786\",\"roslyn-code-analysis\"\n\"786\",\"simplify\"\n\"786\",\"quantitative-finance\"\n\"785\",\"edmx\"\n\"785\",\"json-schema-validator\"\n\"785\",\"icu\"\n\"785\",\"uipath\"\n\"785\",\"c#-5.0\"\n\"785\",\"eventtrigger\"\n\"785\",\"http-status-code-500\"\n\"784\",\"snap.svg\"\n\"784\",\"crtp\"\n\"784\",\"stress-testing\"\n\"784\",\"sparklyr\"\n\"784\",\"particles\"\n\"783\",\"jcr\"\n\"783\",\"sequel\"\n\"783\",\"inkscape\"\n\"783\",\"visitor-pattern\"\n\"783\",\"dimension\"\n\"783\",\"rails-i18n\"\n\"783\",\"urllib3\"\n\"783\",\"google-iam\"\n\"782\",\"file-conversion\"\n\"782\",\"replaceall\"\n\"782\",\"overload-resolution\"\n\"782\",\"nsnumber\"\n\"782\",\"ransack\"\n\"782\",\"asn.1\"\n\"781\",\"marquee\"\n\"781\",\"katalon-studio\"\n\"781\",\"navigationview\"\n\"781\",\"typo3-6.2.x\"\n\"781\",\"std-pair\"\n\"780\",\"listboxitem\"\n\"780\",\"datareader\"\n\"780\",\"finder\"\n\"780\",\"pkcs#11\"\n\"780\",\"azure-durable-functions\"\n\"780\",\"equation-solving\"\n\"780\",\"merge-conflict-resolution\"\n\"780\",\"google-home\"\n\"780\",\"zend-framework3\"\n\"779\",\"yahoo-api\"\n\"779\",\"kong\"\n\"779\",\"uibinder\"\n\"778\",\"airflow-2.x\"\n\"778\",\"html-input\"\n\"778\",\"typechecking\"\n\"778\",\"aspxgridview\"\n\"778\",\"xcode12\"\n\"778\",\"composite-component\"\n\"778\",\"quantile\"\n\"777\",\"jcomponent\"\n\"777\",\"reactstrap\"\n\"777\",\"spring-hateoas\"\n\"777\",\"event-dispatch-thread\"\n\"777\",\"parseint\"\n\"777\",\"zoho\"\n\"777\",\"bin\"\n\"776\",\"vertex\"\n\"776\",\"argument-passing\"\n\"776\",\"multimap\"\n\"776\",\"webp\"\n\"776\",\"heredoc\"\n\"775\",\"confirm\"\n\"775\",\"keyup\"\n\"775\",\"modelsim\"\n\"775\",\"obiee\"\n\"775\",\"typo3-8.x\"\n\"775\",\"dhcp\"\n\"775\",\"resnet\"\n\"775\",\"node-redis\"\n\"775\",\"sublist\"\n\"775\",\"comparison-operators\"\n\"774\",\"removing-whitespace\"\n\"774\",\"choropleth\"\n\"774\",\"unreal-engine5\"\n\"774\",\"recovery\"\n\"774\",\"irb\"\n\"774\",\"mouse-cursor\"\n\"774\",\"persistent\"\n\"774\",\"stenciljs\"\n\"774\",\"pragma\"\n\"774\",\"sublime-text-plugin\"\n\"773\",\"ebpf\"\n\"773\",\"directoryservices\"\n\"773\",\"riak\"\n\"773\",\"mockmvc\"\n\"772\",\"sitefinity\"\n\"772\",\"circular-reference\"\n\"772\",\"bounds\"\n\"772\",\"region\"\n\"772\",\"drawer\"\n\"771\",\"pythonpath\"\n\"771\",\"windows-shell\"\n\"771\",\"twilio-twiml\"\n\"771\",\"gtsummary\"\n\"771\",\"ostream\"\n\"771\",\"b-tree\"\n\"771\",\"oledbconnection\"\n\"771\",\"prometheus-alertmanager\"\n\"770\",\"symfony-2.3\"\n\"770\",\"variance\"\n\"770\",\"dllexport\"\n\"770\",\"spring-oauth2\"\n\"770\",\"galaxy\"\n\"770\",\"android-mvvm\"\n\"770\",\"google-geocoding-api\"\n\"770\",\"gnu-parallel\"\n\"769\",\"qgraphicsitem\"\n\"769\",\"ipfs\"\n\"769\",\"soql\"\n\"769\",\"u-sql\"\n\"768\",\"appcelerator-mobile\"\n\"768\",\"socks\"\n\"768\",\"video-player\"\n\"768\",\"task-queue\"\n\"768\",\"dialogflow-es-fulfillment\"\n\"768\",\"messenger\"\n\"767\",\"spring-3\"\n\"766\",\"wdk\"\n\"766\",\"x-editable\"\n\"766\",\"jplayer\"\n\"766\",\"docstring\"\n\"766\",\"monad-transformers\"\n\"766\",\"ray\"\n\"765\",\"applepay\"\n\"765\",\"spring-session\"\n\"765\",\"blend\"\n\"765\",\"wix3.5\"\n\"765\",\"radiobuttonlist\"\n\"765\",\"hardhat\"\n\"765\",\"user-accounts\"\n\"764\",\"python-pptx\"\n\"764\",\"google-apps-marketplace\"\n\"764\",\"terra\"\n\"764\",\"transactional\"\n\"764\",\"multiclass-classification\"\n\"763\",\"crypto++\"\n\"763\",\"delegation\"\n\"763\",\"librosa\"\n\"763\",\"coordinate-transformation\"\n\"763\",\"rails-api\"\n\"763\",\"geocode\"\n\"762\",\"flashdevelop\"\n\"762\",\"adonis.js\"\n\"762\",\"unhandled-exception\"\n\"762\",\"oracle-apex-5.1\"\n\"762\",\"jvm-hotspot\"\n\"762\",\"core-text\"\n\"762\",\"exoplayer2.x\"\n\"762\",\"eigenvalue\"\n\"762\",\"partials\"\n\"761\",\"php-ziparchive\"\n\"761\",\"callable\"\n\"761\",\"tableau-desktop\"\n\"761\",\"cocos2d-x-3.0\"\n\"761\",\"java-module\"\n\"760\",\"icc\"\n\"760\",\"info.plist\"\n\"760\",\"custom-cell\"\n\"760\",\"ellipsis\"\n\"760\",\"arcpy\"\n\"759\",\"bitset\"\n\"759\",\"scrum\"\n\"759\",\"headless-browser\"\n\"758\",\"audiounit\"\n\"758\",\"notify\"\n\"758\",\"openshift-origin\"\n\"758\",\"resourcebundle\"\n\"758\",\"scrollto\"\n\"758\",\"belongs-to\"\n\"757\",\"wowza\"\n\"757\",\"svelte-3\"\n\"757\",\"playlist\"\n\"757\",\"non-linear-regression\"\n\"757\",\"splice\"\n\"757\",\"encryption-symmetric\"\n\"756\",\"clips\"\n\"756\",\"tsx\"\n\"756\",\"datarow\"\n\"756\",\"google-vision\"\n\"756\",\"portal\"\n\"756\",\"spring-saml\"\n\"756\",\"bufferedwriter\"\n\"755\",\"declare\"\n\"755\",\"traveling-salesman\"\n\"755\",\"angular-resource\"\n\"755\",\"wallpaper\"\n\"755\",\"svd\"\n\"755\",\"msbuild-task\"\n\"754\",\"bing-api\"\n\"754\",\"jquery-templates\"\n\"754\",\"ios-ui-automation\"\n\"754\",\"amazon-quicksight\"\n\"754\",\"sticky-footer\"\n\"753\",\"implode\"\n\"753\",\"diskspace\"\n\"753\",\"wcf-client\"\n\"753\",\"twitter-bootstrap-4\"\n\"753\",\"aws-sdk-js\"\n\"753\",\"m3u8\"\n\"753\",\"astropy\"\n\"753\",\"direct2d\"\n\"753\",\"flutter-navigation\"\n\"753\",\"composite-key\"\n\"752\",\"ubuntu-22.04\"\n\"752\",\"reflection.emit\"\n\"752\",\"xcopy\"\n\"752\",\"android-design-library\"\n\"751\",\"sap-gui\"\n\"751\",\"gulp-sass\"\n\"751\",\"spring-cloud-feign\"\n\"751\",\"typography\"\n\"751\",\"parsley.js\"\n\"751\",\"altbeacon\"\n\"750\",\"decltype\"\n\"750\",\"recording\"\n\"750\",\"quickfix\"\n\"750\",\"timescaledb\"\n\"750\",\"percentile\"\n\"750\",\"allocator\"\n\"749\",\"margins\"\n\"749\",\"meteor-accounts\"\n\"749\",\"android-transitions\"\n\"749\",\"google-cardboard\"\n\"749\",\"office-automation\"\n\"749\",\"ip-camera\"\n\"748\",\"microsoft-sync-framework\"\n\"748\",\"hammer.js\"\n\"748\",\"subreport\"\n\"748\",\"google-sites\"\n\"747\",\"instruction-set\"\n\"747\",\"cd\"\n\"747\",\"firebase-notifications\"\n\"747\",\"bootstrap-datetimepicker\"\n\"747\",\"episerver\"\n\"747\",\"facebook-marketing-api\"\n\"747\",\"hamburger-menu\"\n\"747\",\"angular-filters\"\n\"747\",\"chakra-ui\"\n\"747\",\"splunk-query\"\n\"747\",\"emmet\"\n\"747\",\"conan\"\n\"746\",\"fxcop\"\n\"746\",\"python-internals\"\n\"746\",\"ruby-on-rails-2\"\n\"746\",\"let\"\n\"745\",\"file-copying\"\n\"745\",\"relayjs\"\n\"745\",\"cancellation\"\n\"745\",\"isolation-level\"\n\"745\",\"dhtml\"\n\"745\",\"uipagecontrol\"\n\"745\",\"flutter-listview\"\n\"745\",\"gnuradio\"\n\"745\",\"amazon-kms\"\n\"744\",\"ansible-facts\"\n\"744\",\"web-testing\"\n\"744\",\"annotation-processing\"\n\"744\",\"kableextra\"\n\"744\",\"erase\"\n\"744\",\"css-modules\"\n\"744\",\"conditional-compilation\"\n\"743\",\"visualsvn-server\"\n\"743\",\"qcombobox\"\n\"742\",\"celery-task\"\n\"742\",\"smartphone\"\n\"742\",\"bitmask\"\n\"742\",\"playframework-1.x\"\n\"742\",\"rsyslog\"\n\"742\",\"depth\"\n\"742\",\"siblings\"\n\"742\",\"condition-variable\"\n\"741\",\"gsl\"\n\"741\",\"invoice\"\n\"741\",\"c#-6.0\"\n\"741\",\"traceback\"\n\"740\",\"import-from-excel\"\n\"740\",\"keystonejs\"\n\"740\",\"modem\"\n\"740\",\"azure-data-lake-gen2\"\n\"740\",\"rasa\"\n\"740\",\"jaxb2\"\n\"740\",\"bigint\"\n\"740\",\"transfer-learning\"\n\"739\",\"flip\"\n\"739\",\"angular-test\"\n\"739\",\"nativescript-vue\"\n\"739\",\"android-ksoap2\"\n\"739\",\"queryover\"\n\"738\",\"sencha-architect\"\n\"738\",\"calculated-field\"\n\"738\",\"m2e\"\n\"738\",\"tinkerpop3\"\n\"738\",\"hasura\"\n\"738\",\"screen-size\"\n\"738\",\"zope\"\n\"737\",\"xjc\"\n\"737\",\"disqus\"\n\"737\",\"kde-plasma\"\n\"737\",\"apscheduler\"\n\"737\",\"rcurl\"\n\"737\",\"active-model-serializers\"\n\"737\",\"amazon-lex\"\n\"736\",\"ef-database-first\"\n\"736\",\"pinchzoom\"\n\"736\",\"sitecore7\"\n\"736\",\"android-gps\"\n\"736\",\"uifont\"\n\"736\",\"strftime\"\n\"735\",\"pointer-arithmetic\"\n\"735\",\"android-checkbox\"\n\"735\",\"continue\"\n\"735\",\"ctags\"\n\"734\",\"clip\"\n\"734\",\"ftp-client\"\n\"734\",\"avx2\"\n\"734\",\"oracle-cloud-infrastructure\"\n\"734\",\"operation\"\n\"734\",\"dio\"\n\"734\",\"google-cloud-stackdriver\"\n\"733\",\"citrix\"\n\"733\",\"gitlab-api\"\n\"733\",\"ngx-translate\"\n\"733\",\"assertions\"\n\"732\",\"rvalue\"\n\"732\",\"image-upload\"\n\"732\",\"py2neo\"\n\"732\",\"word-cloud\"\n\"732\",\"execution-time\"\n\"732\",\"compareto\"\n\"732\",\"collectors\"\n\"732\",\"http-error\"\n\"732\",\"qualtrics\"\n\"731\",\"react-navigation-v5\"\n\"731\",\"renderscript\"\n\"731\",\"adobe-analytics\"\n\"731\",\"rpa\"\n\"731\",\"microchip\"\n\"731\",\"formulas\"\n\"731\",\"poco-libraries\"\n\"731\",\"android-multidex\"\n\"731\",\"alphabetical\"\n\"731\",\"bids\"\n\"730\",\"template-argument-deduction\"\n\"730\",\"connectivity\"\n\"730\",\"dji-sdk\"\n\"730\",\"readability\"\n\"730\",\"perspective\"\n\"729\",\"os.walk\"\n\"729\",\"japplet\"\n\"729\",\"npapi\"\n\"729\",\"business-process-management\"\n\"729\",\"redis-cluster\"\n\"729\",\"strategy-pattern\"\n\"729\",\"pytz\"\n\"729\",\"restriction\"\n\"729\",\"restkit-0.20\"\n\"729\",\"ios-app-extension\"\n\"729\",\"arel\"\n\"729\",\"autodesk-model-derivative\"\n\"728\",\"yammer\"\n\"728\",\"ecdsa\"\n\"728\",\"content-script\"\n\"728\",\"hystrix\"\n\"728\",\"easy-install\"\n\"728\",\"internal\"\n\"728\",\"nsthread\"\n\"728\",\"google-gdk\"\n\"728\",\"autoplay\"\n\"728\",\"gluon\"\n\"727\",\"typescript-eslint\"\n\"727\",\"android-gridlayout\"\n\"726\",\"background-task\"\n\"726\",\"php-7.2\"\n\"726\",\"slick2d\"\n\"726\",\"fitnesse\"\n\"726\",\"magnific-popup\"\n\"726\",\"poisson\"\n\"726\",\"getopt\"\n\"726\",\"sonarlint\"\n\"725\",\"dataview\"\n\"725\",\"seq\"\n\"725\",\"datagrip\"\n\"725\",\"modularity\"\n\"725\",\"neo4j-apoc\"\n\"725\",\"google-cloud-iam\"\n\"725\",\"custom-keyboard\"\n\"725\",\"low-level\"\n\"724\",\"babel-loader\"\n\"724\",\"angular-translate\"\n\"724\",\"crash-dumps\"\n\"724\",\"nsregularexpression\"\n\"724\",\"mifare\"\n\"724\",\"browserstack\"\n\"724\",\"stringify\"\n\"724\",\"mongo-shell\"\n\"723\",\"skip\"\n\"723\",\"packet-sniffers\"\n\"723\",\"twilio-php\"\n\"723\",\"wmic\"\n\"723\",\"nohup\"\n\"722\",\"joblib\"\n\"722\",\"rpgle\"\n\"722\",\"gam\"\n\"721\",\"material-table\"\n\"721\",\"unix-socket\"\n\"721\",\"pushviewcontroller\"\n\"721\",\"dependent-type\"\n\"721\",\"authlogic\"\n\"721\",\"sccm\"\n\"721\",\"tcsh\"\n\"721\",\"google-cloud-spanner\"\n\"721\",\"avaudiorecorder\"\n\"720\",\"clip-path\"\n\"720\",\"divide-and-conquer\"\n\"720\",\"core-motion\"\n\"720\",\"visual-sourcesafe\"\n\"720\",\"viewdidload\"\n\"720\",\"c++-winrt\"\n\"720\",\"hamcrest\"\n\"720\",\"strpos\"\n\"720\",\"compass\"\n\"719\",\"antivirus\"\n\"719\",\"remix\"\n\"719\",\"case-class\"\n\"719\",\"nashorn\"\n\"719\",\"onsubmit\"\n\"719\",\"svg-animate\"\n\"719\",\"nvcc\"\n\"719\",\"sql-grant\"\n\"719\",\"lazy-initialization\"\n\"719\",\"trac\"\n\"718\",\"yeoman-generator\"\n\"718\",\"portfolio\"\n\"718\",\"corpus\"\n\"718\",\"core-foundation\"\n\"718\",\"code-duplication\"\n\"718\",\"nokia\"\n\"718\",\"draftjs\"\n\"718\",\"cgrect\"\n\"718\",\"heroku-cli\"\n\"718\",\"alphanumeric\"\n\"717\",\"xero-api\"\n\"717\",\"document-ready\"\n\"717\",\"caption\"\n\"717\",\"thermal-printer\"\n\"716\",\"flink-sql\"\n\"716\",\"jssor\"\n\"716\",\"flexbuilder\"\n\"716\",\"sonata\"\n\"715\",\"debouncing\"\n\"715\",\"oracle-apex-5\"\n\"715\",\"rhel7\"\n\"715\",\"gemfile\"\n\"715\",\"android-launcher\"\n\"715\",\"threshold\"\n\"714\",\"onresume\"\n\"714\",\"virtuemart\"\n\"713\",\"reactor\"\n\"713\",\"ef-core-2.0\"\n\"713\",\"firefox-developer-tools\"\n\"713\",\"android-viewpager2\"\n\"713\",\"spring-social\"\n\"713\",\"viewbag\"\n\"713\",\"gameobject\"\n\"713\",\"nmap\"\n\"712\",\"ruby-on-rails-plugins\"\n\"712\",\"database-restore\"\n\"712\",\"kotlin-android-extensions\"\n\"712\",\"anaconda3\"\n\"712\",\"corba\"\n\"712\",\"offline-caching\"\n\"711\",\"faker\"\n\"711\",\"nsjsonserialization\"\n\"711\",\"sql++\"\n\"711\",\"angular-cdk\"\n\"711\",\"user-profile\"\n\"711\",\"sections\"\n\"710\",\"reportbuilder\"\n\"710\",\"android-vectordrawable\"\n\"710\",\"tcpserver\"\n\"709\",\"github-for-windows\"\n\"709\",\"csrf-protection\"\n\"709\",\"istream\"\n\"709\",\"activity-lifecycle\"\n\"709\",\"ios-provisioning\"\n\"709\",\"subclipse\"\n\"708\",\"anonymous-class\"\n\"708\",\"json-api\"\n\"708\",\"findstr\"\n\"708\",\"administrator\"\n\"708\",\"google-apis-explorer\"\n\"708\",\"npm-start\"\n\"708\",\"powerpc\"\n\"707\",\"mailmerge\"\n\"707\",\"ioctl\"\n\"707\",\"isnull\"\n\"707\",\"logfile\"\n\"707\",\"elliptic-curve\"\n\"706\",\"insert-into\"\n\"706\",\"trigger.io\"\n\"706\",\"sylius\"\n\"706\",\"mapi\"\n\"706\",\"dnx\"\n\"706\",\"jvm-arguments\"\n\"706\",\"type-safety\"\n\"706\",\"direction\"\n\"706\",\"activemodel\"\n\"706\",\"automata\"\n\"706\",\"linkedhashmap\"\n\"705\",\"awtrobot\"\n\"705\",\"boost-graph\"\n\"705\",\"dynamics-crm-2016\"\n\"705\",\"nine-patch\"\n\"705\",\"strsplit\"\n\"705\",\"prawn\"\n\"704\",\"clickable\"\n\"704\",\"crystal-reports-2010\"\n\"704\",\"blazor-client-side\"\n\"704\",\"strlen\"\n\"703\",\"kill-process\"\n\"703\",\"browser-extension\"\n\"702\",\"replit\"\n\"702\",\"private-members\"\n\"702\",\"vcf-vcard\"\n\"702\",\"pow\"\n\"702\",\"spring-cache\"\n\"702\",\"aws-ssm\"\n\"702\",\"android-mediarecorder\"\n\"702\",\"amazon-lightsail\"\n\"702\",\"parceljs\"\n\"701\",\"bindingsource\"\n\"701\",\"xpath-2.0\"\n\"701\",\"ruby-on-rails-4.2\"\n\"701\",\"django-registration\"\n\"701\",\"react-i18next\"\n\"701\",\"shellcode\"\n\"701\",\"google-street-view\"\n\"701\",\"google-project-tango\"\n\"700\",\"date-conversion\"\n\"700\",\"pact\"\n\"700\",\"vcpkg\"\n\"700\",\"scientific-notation\"\n\"700\",\"ca\"\n\"699\",\"apache-phoenix\"\n\"699\",\"azure-container-instances\"\n\"699\",\"correlated-subquery\"\n\"699\",\"ojdbc\"\n\"699\",\"node-mysql\"\n\"698\",\"vue-cli-3\"\n\"698\",\"graphicsmagick\"\n\"698\",\"getstream-io\"\n\"698\",\"google-app-engine-python\"\n\"698\",\"android-nestedscrollview\"\n\"698\",\"lightgbm\"\n\"698\",\"preloader\"\n\"697\",\"imaplib\"\n\"697\",\"rebol\"\n\"697\",\"fragmentmanager\"\n\"697\",\"right-click\"\n\"697\",\"android-c2dm\"\n\"696\",\"photos\"\n\"696\",\"vnc\"\n\"696\",\"dayofweek\"\n\"696\",\"binning\"\n\"696\",\"initializer\"\n\"696\",\"formtastic\"\n\"696\",\"pymc3\"\n\"696\",\"azure-servicebus-topics\"\n\"696\",\"assetic\"\n\"696\",\"gmaps4rails\"\n\"695\",\"live-wallpaper\"\n\"695\",\"chisel\"\n\"695\",\"dymola\"\n\"695\",\"payment-processing\"\n\"695\",\"forward\"\n\"695\",\"ubuntu-10.04\"\n\"695\",\"geoip\"\n\"694\",\"apache-arrow\"\n\"694\",\"decodable\"\n\"694\",\"lit-element\"\n\"694\",\"graphing\"\n\"694\",\"micrometer\"\n\"694\",\"dlopen\"\n\"694\",\"reactivex\"\n\"694\",\"beta\"\n\"694\",\"thin\"\n\"693\",\"any\"\n\"693\",\"photoshop-script\"\n\"693\",\"boost-spirit-qi\"\n\"693\",\"bootstrapper\"\n\"693\",\"visual-studio-app-center\"\n\"693\",\"directed-graph\"\n\"693\",\"android-fileprovider\"\n\"693\",\"export-to-pdf\"\n\"692\",\"pack\"\n\"692\",\"magento-1.4\"\n\"692\",\"modelform\"\n\"692\",\"nls\"\n\"692\",\"angular11\"\n\"691\",\"kernel-density\"\n\"691\",\"tweets\"\n\"691\",\"rake-task\"\n\"691\",\"drawimage\"\n\"691\",\"nosuchmethoderror\"\n\"691\",\"git-stash\"\n\"690\",\"anonymous\"\n\"690\",\"mxnet\"\n\"690\",\"grails-domain-class\"\n\"690\",\"angular-pipe\"\n\"689\",\"divide\"\n\"689\",\"firmware\"\n\"689\",\"pika\"\n\"689\",\"flask-admin\"\n\"689\",\"phpword\"\n\"689\",\"twos-complement\"\n\"689\",\"word-count\"\n\"689\",\"atom-feed\"\n\"689\",\"scientific-computing\"\n\"689\",\"heuristics\"\n\"689\",\"tidymodels\"\n\"688\",\"base-class\"\n\"688\",\"cloudbees\"\n\"688\",\"appfabric\"\n\"688\",\"verify\"\n\"688\",\"c++98\"\n\"688\",\"spartacus-storefront\"\n\"687\",\"standard-library\"\n\"687\",\"varnish-vcl\"\n\"687\",\"angular-template\"\n\"687\",\"variadic\"\n\"687\",\"words\"\n\"687\",\"workbench\"\n\"687\",\"internals\"\n\"687\",\"sqldf\"\n\"687\",\"nim-lang\"\n\"687\",\"flutter-futurebuilder\"\n\"686\",\"swiperefreshlayout\"\n\"686\",\"r-sp\"\n\"686\",\"mojolicious\"\n\"686\",\"azure-iot-edge\"\n\"686\",\"foreground\"\n\"686\",\"arithmetic-expressions\"\n\"685\",\"selectize.js\"\n\"685\",\"safari-extension\"\n\"685\",\"configparser\"\n\"685\",\"myfaces\"\n\"685\",\"simplemodal\"\n\"685\",\"pull-to-refresh\"\n\"685\",\"nsimage\"\n\"685\",\"pynput\"\n\"685\",\"ios12\"\n\"684\",\"react-proptypes\"\n\"684\",\"jquery-ui-slider\"\n\"684\",\"ntp\"\n\"684\",\"redux-observable\"\n\"683\",\"slim-lang\"\n\"683\",\"jenkins-cli\"\n\"683\",\"json-rpc\"\n\"683\",\"outputcache\"\n\"683\",\"dpdk\"\n\"683\",\"autoresize\"\n\"682\",\"fuse\"\n\"682\",\"ftplib\"\n\"682\",\"facet-grid\"\n\"682\",\"rtos\"\n\"682\",\"inventory\"\n\"682\",\"astrojs\"\n\"682\",\"proxypass\"\n\"682\",\"angular-animations\"\n\"682\",\"email-client\"\n\"681\",\"ansi-escape\"\n\"681\",\"flurry\"\n\"681\",\"rubymotion\"\n\"681\",\"asp.net-core-5.0\"\n\"681\",\"torchvision\"\n\"680\",\"cmusphinx\"\n\"680\",\"xcuitest\"\n\"679\",\"whitelist\"\n\"679\",\"freeswitch\"\n\"679\",\"software-distribution\"\n\"679\",\"packet-capture\"\n\"679\",\"wireless\"\n\"679\",\"handshake\"\n\"679\",\"lock-free\"\n\"679\",\"qprocess\"\n\"679\",\"excel-2003\"\n\"678\",\"aforge\"\n\"678\",\"biometrics\"\n\"678\",\"end-to-end\"\n\"678\",\"suds\"\n\"678\",\"preferenceactivity\"\n\"677\",\"vpc\"\n\"677\",\"selinux\"\n\"677\",\"dynamics-crm-4\"\n\"677\",\"popupmenu\"\n\"677\",\"system.diagnostics\"\n\"677\",\"opennlp\"\n\"677\",\"device-tree\"\n\"677\",\"pg-dump\"\n\"676\",\"removechild\"\n\"676\",\"quicktime\"\n\"676\",\"requirements.txt\"\n\"676\",\"sharpdx\"\n\"675\",\"mutation-observers\"\n\"675\",\"stacked-chart\"\n\"675\",\"semantic-versioning\"\n\"675\",\"sms-gateway\"\n\"675\",\"akka.net\"\n\"675\",\"fmdb\"\n\"674\",\"web-push\"\n\"674\",\"reloaddata\"\n\"674\",\"chocolatey\"\n\"674\",\"request-headers\"\n\"674\",\"qpainter\"\n\"674\",\"sumo\"\n\"673\",\"django-database\"\n\"673\",\"opc\"\n\"673\",\"ml\"\n\"673\",\"ivalueconverter\"\n\"673\",\"tfs-workitem\"\n\"673\",\"solution\"\n\"673\",\"gitolite\"\n\"673\",\"powerbi-datasource\"\n\"672\",\"maple\"\n\"672\",\"nslog\"\n\"672\",\"oculus\"\n\"672\",\"stop-words\"\n\"672\",\"tr\"\n\"672\",\"font-family\"\n\"671\",\"urlconnection\"\n\"671\",\"sicp\"\n\"671\",\"love2d\"\n\"670\",\"f-string\"\n\"670\",\"recv\"\n\"670\",\"variable-declaration\"\n\"670\",\"records\"\n\"670\",\"build-error\"\n\"670\",\"lvalue\"\n\"670\",\"plotly.js\"\n\"669\",\"photon\"\n\"669\",\"vb6-migration\"\n\"669\",\"jax\"\n\"669\",\"jackrabbit\"\n\"669\",\"thread-synchronization\"\n\"669\",\"quartz-2d\"\n\"668\",\"skaction\"\n\"668\",\"localdate\"\n\"668\",\"mapper\"\n\"668\",\"window.location\"\n\"668\",\"app-inventor\"\n\"668\",\"silverlight-toolkit\"\n\"667\",\"consumer\"\n\"667\",\"micro-frontend\"\n\"667\",\"delphi-xe3\"\n\"667\",\"raspberry-pi-pico\"\n\"667\",\"milliseconds\"\n\"667\",\"coinbase-api\"\n\"667\",\"vaadin8\"\n\"666\",\"clipping\"\n\"666\",\"keycloak-services\"\n\"666\",\"abort\"\n\"666\",\"mailer\"\n\"666\",\"scanning\"\n\"666\",\"texture2d\"\n\"666\",\"laravel-sanctum\"\n\"665\",\"clearinterval\"\n\"665\",\"angular-library\"\n\"665\",\"ajaxform\"\n\"665\",\"pathlib\"\n\"665\",\"getelementsbyclassname\"\n\"665\",\"google-cloud-logging\"\n\"665\",\"ninja\"\n\"665\",\"pre-commit-hook\"\n\"665\",\"auto-generate\"\n\"664\",\"decimalformat\"\n\"664\",\"uitouch\"\n\"664\",\"facebook-social-plugins\"\n\"664\",\"iar\"\n\"664\",\"jquery-ui-droppable\"\n\"664\",\"swift4.2\"\n\"664\",\"kalman-filter\"\n\"664\",\"wsdl2java\"\n\"664\",\"netcdf4\"\n\"664\",\"cordova-3\"\n\"664\",\"ondraw\"\n\"663\",\"square\"\n\"663\",\"sockjs\"\n\"663\",\"html-to-pdf\"\n\"663\",\"visualvm\"\n\"663\",\"iis-8.5\"\n\"663\",\"google-mlkit\"\n\"663\",\"complex-event-processing\"\n\"662\",\"vsphere\"\n\"662\",\"wifimanager\"\n\"662\",\"nft\"\n\"662\",\"directx-9\"\n\"662\",\"crystal-lang\"\n\"662\",\"nsoutlineview\"\n\"662\",\"virtualenvwrapper\"\n\"662\",\"android-actionbar-compat\"\n\"662\",\"zpl\"\n\"662\",\"scss-mixins\"\n\"662\",\"thingsboard\"\n\"661\",\"adjacency-list\"\n\"661\",\"indicator\"\n\"661\",\"jpa-2.1\"\n\"661\",\"curly-braces\"\n\"661\",\"multi-factor-authentication\"\n\"660\",\"react-navigation-stack\"\n\"660\",\"flash-cs4\"\n\"660\",\"r-s4\"\n\"660\",\"jupyterhub\"\n\"660\",\"holoviews\"\n\"660\",\"member-functions\"\n\"660\",\"custom-error-pages\"\n\"660\",\"qsort\"\n\"660\",\"hateoas\"\n\"659\",\"freetds\"\n\"659\",\"vee-validate\"\n\"659\",\"net-snmp\"\n\"659\",\"qlabel\"\n\"659\",\"httpsession\"\n\"659\",\"customtkinter\"\n\"658\",\"back-stack\"\n\"658\",\"serenity-bdd\"\n\"658\",\"cql3\"\n\"658\",\"nsubstitute\"\n\"658\",\"onblur\"\n\"658\",\"ldap-query\"\n\"658\",\"mediapipe\"\n\"657\",\"multiple-conditions\"\n\"657\",\"tensorflow-estimator\"\n\"657\",\"method-chaining\"\n\"657\",\"android-seekbar\"\n\"657\",\"html2pdf\"\n\"657\",\"richtext\"\n\"657\",\"java.util.logging\"\n\"657\",\"mq\"\n\"656\",\"rscript\"\n\"656\",\"roxygen2\"\n\"656\",\"azure-eventgrid\"\n\"656\",\"rasa-nlu\"\n\"656\",\"system-tray\"\n\"656\",\"pytorch-lightning\"\n\"656\",\"angular12\"\n\"656\",\"iife\"\n\"656\",\"webautomation\"\n\"655\",\"relay\"\n\"655\",\"skscene\"\n\"655\",\"image-scaling\"\n\"655\",\"file-read\"\n\"655\",\"unicode-string\"\n\"655\",\"ida\"\n\"655\",\"react-state-management\"\n\"655\",\"android-security\"\n\"655\",\"des\"\n\"655\",\"freebase\"\n\"655\",\"css-variables\"\n\"655\",\"arduino-c++\"\n\"655\",\"stdatomic\"\n\"654\",\"ternary\"\n\"654\",\"filezilla\"\n\"654\",\"spring-cloud-sleuth\"\n\"654\",\"payara\"\n\"654\",\"lerna\"\n\"654\",\"messagebroker\"\n\"654\",\"eigenvector\"\n\"654\",\"hybrid\"\n\"653\",\"cobertura\"\n\"653\",\"git-config\"\n\"653\",\"savefiledialog\"\n\"653\",\"shinyjs\"\n\"653\",\"reveal.js\"\n\"653\",\"gmail-imap\"\n\"652\",\"defaultdict\"\n\"652\",\"appbar\"\n\"652\",\"index-error\"\n\"652\",\"django-csrf\"\n\"652\",\"astronomy\"\n\"652\",\"angular-datatables\"\n\"651\",\"telegraf\"\n\"651\",\"werkzeug\"\n\"651\",\"sabre\"\n\"651\",\"windowbuilder\"\n\"651\",\"nearprotocol\"\n\"651\",\"google-cloud-bigtable\"\n\"650\",\"apache-drill\"\n\"650\",\"apache-servicemix\"\n\"650\",\"firebird2.5\"\n\"650\",\"svg-filters\"\n\"650\",\"android-inflate\"\n\"650\",\"stylecop\"\n\"649\",\"pdb-files\"\n\"649\",\"extjs-mvc\"\n\"649\",\"ratchet\"\n\"649\",\"uicontainerview\"\n\"649\",\"busybox\"\n\"649\",\"vertical-scrolling\"\n\"648\",\"load-data-infile\"\n\"648\",\"uiwindow\"\n\"648\",\"oracle-call-interface\"\n\"648\",\"spring-ldap\"\n\"648\",\"bootstrap-typeahead\"\n\"648\",\"code-contracts\"\n\"648\",\"drm\"\n\"648\",\"amazon-ebs\"\n\"648\",\"cvxpy\"\n\"647\",\"livecode\"\n\"647\",\"chi-squared\"\n\"647\",\"dismiss\"\n\"647\",\"caml\"\n\"647\",\"mips32\"\n\"647\",\"android-app-bundle\"\n\"647\",\"c89\"\n\"647\",\"launchd\"\n\"647\",\"light\"\n\"647\",\"secret-key\"\n\"647\",\"custom-view\"\n\"646\",\"integer-division\"\n\"646\",\"xerces\"\n\"646\",\"ng-animate\"\n\"646\",\"mgo\"\n\"646\",\"intersect\"\n\"646\",\"google-analytics-firebase\"\n\"646\",\"resolve\"\n\"646\",\"google-search-api\"\n\"645\",\"lambda-calculus\"\n\"645\",\"distinct-values\"\n\"645\",\"urbanairship.com\"\n\"645\",\"inference\"\n\"645\",\"hard-drive\"\n\"645\",\"quanteda\"\n\"644\",\"viewflipper\"\n\"644\",\"mininet\"\n\"644\",\"linkage\"\n\"644\",\"webm\"\n\"644\",\"stax\"\n\"643\",\"administration\"\n\"643\",\"named-ranges\"\n\"643\",\"android-toast\"\n\"643\",\"inotify\"\n\"643\",\"zos\"\n\"643\",\"msp430\"\n\"643\",\"zod\"\n\"643\",\"qtwebkit\"\n\"642\",\"stage\"\n\"642\",\"php-mongodb\"\n\"642\",\"powerapps-canvas\"\n\"642\",\"libgit2\"\n\"642\",\"code-cleanup\"\n\"642\",\"system.data.sqlite\"\n\"642\",\"ml.net\"\n\"641\",\"feign\"\n\"641\",\"eclipse-juno\"\n\"641\",\"service-discovery\"\n\"641\",\"crystal-reports-xi\"\n\"641\",\"iconv\"\n\"641\",\"analyzer\"\n\"641\",\"ext.net\"\n\"641\",\"quote\"\n\"641\",\"uvicorn\"\n\"640\",\"bad-request\"\n\"640\",\"material-design-lite\"\n\"640\",\"avr-gcc\"\n\"640\",\"callkit\"\n\"640\",\"review\"\n\"640\",\"nusoap\"\n\"640\",\"percona\"\n\"640\",\"mediawiki-api\"\n\"639\",\"temperature\"\n\"639\",\"babel-jest\"\n\"639\",\"check-constraints\"\n\"639\",\"firebase-mlkit\"\n\"639\",\"grafana-loki\"\n\"639\",\"os.system\"\n\"639\",\"system-design\"\n\"639\",\"iphone-x\"\n\"639\",\"elasticsearch-query\"\n\"638\",\"discord.net\"\n\"638\",\"database-project\"\n\"638\",\"unixodbc\"\n\"638\",\"openam\"\n\"638\",\"google-hangouts\"\n\"638\",\"subscript\"\n\"637\",\"xlwt\"\n\"637\",\"adc\"\n\"637\",\"pine-script-v4\"\n\"637\",\"sessionid\"\n\"637\",\"sap-erp\"\n\"637\",\"drivers\"\n\"637\",\"items\"\n\"637\",\"computation-theory\"\n\"636\",\"closedxml\"\n\"636\",\"spring-java-config\"\n\"636\",\"polynomial-math\"\n\"636\",\"forwarding\"\n\"636\",\"google-api-js-client\"\n\"636\",\"contextmanager\"\n\"636\",\"toolchain\"\n\"636\",\"stream-builder\"\n\"636\",\"mouseclick-event\"\n\"635\",\"group\"\n\"635\",\"vtable\"\n\"635\",\"processing-efficiency\"\n\"635\",\"django-tables2\"\n\"635\",\"rect\"\n\"635\",\"postgresql-10\"\n\"635\",\"godot4\"\n\"635\",\"osmnx\"\n\"635\",\"qpushbutton\"\n\"635\",\"tpl-dataflow\"\n\"635\",\"seed\"\n\"634\",\"segment\"\n\"634\",\"pushstate\"\n\"634\",\"wso2-data-services-server\"\n\"634\",\"html-encode\"\n\"634\",\"android-9.0-pie\"\n\"634\",\"arduino-esp32\"\n\"633\",\"datagram\"\n\"633\",\"read-write\"\n\"633\",\"abaqus\"\n\"633\",\"notation\"\n\"633\",\"modalpopupextender\"\n\"633\",\"etag\"\n\"633\",\"huawei-developers\"\n\"633\",\"image-classification\"\n\"632\",\"datastage\"\n\"632\",\"database-deadlocks\"\n\"632\",\"factors\"\n\"632\",\"ns2\"\n\"632\",\"business-logic\"\n\"631\",\"stackpanel\"\n\"631\",\"treeset\"\n\"631\",\"bitmapfactory\"\n\"631\",\"recode\"\n\"631\",\"objective-c-runtime\"\n\"631\",\".net-core-3.0\"\n\"631\",\"quota\"\n\"631\",\"mplab\"\n\"631\",\"pre-signed-url\"\n\"631\",\"amazon-kinesis-firehose\"\n\"631\",\"autofixture\"\n\"630\",\"daterangepicker\"\n\"630\",\"druid\"\n\"630\",\"zshrc\"\n\"630\",\"http-status-code-302\"\n\"630\",\"irvine32\"\n\"629\",\"treenode\"\n\"629\",\"php-8\"\n\"629\",\"selectonemenu\"\n\"629\",\"xmlstarlet\"\n\"629\",\"xpages-extlib\"\n\"629\",\"inet\"\n\"629\",\"tween\"\n\"629\",\"pymssql\"\n\"629\",\"short\"\n\"629\",\"npoi\"\n\"629\",\"istanbul\"\n\"629\",\"laravel-socialite\"\n\"628\",\"ansi-c\"\n\"628\",\"fluent-nhibernate-mapping\"\n\"628\",\"instagram-graph-api\"\n\"628\",\"cryptoapi\"\n\"628\",\"delphi-10-seattle\"\n\"628\",\"dynamic-cast\"\n\"628\",\"detox\"\n\"628\",\"didselectrowatindexpath\"\n\"628\",\"encoder\"\n\"628\",\"concurrentmodification\"\n\"628\",\"textwatcher\"\n\"627\",\"flextable\"\n\"627\",\"println\"\n\"627\",\"localnotification\"\n\"627\",\"vue-i18n\"\n\"627\",\"multiple-databases\"\n\"627\",\"eclipse-pdt\"\n\"627\",\"fix-protocol\"\n\"627\",\"azure-app-service-plans\"\n\"627\",\"neo4jclient\"\n\"627\",\"magic-methods\"\n\"627\",\"google-classroom\"\n\"627\",\"spring.net\"\n\"627\",\"pyzmq\"\n\"627\",\"evernote\"\n\"627\",\"stringtokenizer\"\n\"627\",\"darkmode\"\n\"626\",\"probability-density\"\n\"626\",\"weighted\"\n\"626\",\"code-organization\"\n\"626\",\"msxml\"\n\"625\",\"ansible-template\"\n\"625\",\"xorg\"\n\"625\",\"units-of-measurement\"\n\"625\",\"python.net\"\n\"625\",\"wine\"\n\"625\",\"sqlperformance\"\n\"625\",\"eventmachine\"\n\"624\",\"repo\"\n\"624\",\"install.packages\"\n\"624\",\"mv\"\n\"624\",\"file-not-found\"\n\"624\",\"selection-sort\"\n\"624\",\"2sxc\"\n\"624\",\"r.java-file\"\n\"624\",\"ognl\"\n\"624\",\"weblogic11g\"\n\"624\",\"linq-to-nhibernate\"\n\"623\",\"graphene-python\"\n\"623\",\"py2app\"\n\"623\",\"http-authentication\"\n\"623\",\"restify\"\n\"623\",\"action-filter\"\n\"623\",\"iphone-5\"\n\"623\",\"maximo\"\n\"622\",\"socketexception\"\n\"622\",\"cdc\"\n\"622\",\"swfobject\"\n\"622\",\"objectinputstream\"\n\"621\",\"graphql-java\"\n\"621\",\"classname\"\n\"621\",\"react-native-router-flux\"\n\"621\",\"unpack\"\n\"621\",\"atmel\"\n\"621\",\"plupload\"\n\"621\",\"element-ui\"\n\"621\",\"emotion\"\n\"621\",\"behaviorsubject\"\n\"621\",\"statistics-bootstrap\"\n\"620\",\"universal\"\n\"620\",\"realm-mobile-platform\"\n\"620\",\"java-home\"\n\"620\",\"httparty\"\n\"620\",\"odoo-13\"\n\"619\",\"decompiler\"\n\"619\",\"datetimeoffset\"\n\"619\",\"createprocess\"\n\"619\",\"spring-data-cassandra\"\n\"619\",\"forever\"\n\"619\",\"raw-input\"\n\"619\",\"ambiguous\"\n\"619\",\"libpng\"\n\"619\",\"pytables\"\n\"619\",\"hardware-acceleration\"\n\"619\",\"burn\"\n\"618\",\"square-connect\"\n\"618\",\"deb\"\n\"618\",\"language-features\"\n\"618\",\"react-tsx\"\n\"618\",\"unsupervised-learning\"\n\"618\",\"ttl\"\n\"618\",\"jquery-hover\"\n\"618\",\"gwt-rpc\"\n\"618\",\"completionhandler\"\n\"618\",\"traffic\"\n\"617\",\"removeclass\"\n\"617\",\"rjava\"\n\"617\",\"jsonparser\"\n\"617\",\"single-table-inheritance\"\n\"617\",\"javassist\"\n\"617\",\"spring-4\"\n\"617\",\"mssql-jdbc\"\n\"617\",\"bbcode\"\n\"616\",\"alassetslibrary\"\n\"616\",\"windows-console\"\n\"616\",\"wcf-rest\"\n\"616\",\"forge\"\n\"616\",\"objectdatasource\"\n\"616\",\"libav\"\n\"616\",\"referrer\"\n\"616\",\"polar-coordinates\"\n\"616\",\"pfx\"\n\"616\",\"stdthread\"\n\"616\",\"praw\"\n\"616\",\"web-frameworks\"\n\"615\",\"ngtable\"\n\"615\",\"density-plot\"\n\"615\",\"nscalendar\"\n\"615\",\"html5-history\"\n\"615\",\"freeglut\"\n\"615\",\"azure-webapps\"\n\"615\",\"ashx\"\n\"614\",\"dbunit\"\n\"614\",\"ddms\"\n\"614\",\"ballerina\"\n\"614\",\"ftpwebrequest\"\n\"614\",\"vegan\"\n\"614\",\"android-facebook\"\n\"614\",\"android-music-player\"\n\"613\",\"jcheckbox\"\n\"613\",\"inspect\"\n\"613\",\"scala-3\"\n\"613\",\"omnifaces\"\n\"613\",\"glmnet\"\n\"613\",\"arraybuffer\"\n\"612\",\"jointjs\"\n\"612\",\"kinect-sdk\"\n\"612\",\"hornetq\"\n\"612\",\"spring-camel\"\n\"612\",\"modx-revolution\"\n\"612\",\"minimum-spanning-tree\"\n\"612\",\"shopify-template\"\n\"612\",\"amcharts4\"\n\"612\",\"asp.net-4.5\"\n\"612\",\"explain\"\n\"612\",\"qlineedit\"\n\"612\",\"email-notifications\"\n\"612\",\"argocd\"\n\"612\",\"topojson\"\n\"611\",\"gorilla\"\n\"611\",\"tunnel\"\n\"611\",\"odoo-14\"\n\"611\",\"parsec\"\n\"610\",\"slidedown\"\n\"610\",\"entropy\"\n\"610\",\"nscoding\"\n\"610\",\"raii\"\n\"610\",\"moose\"\n\"610\",\"encryption-asymmetric\"\n\"609\",\"oracle9i\"\n\"609\",\"kibana-4\"\n\"609\",\"unity-webgl\"\n\"609\",\"equinox\"\n\"609\",\"swiftdata\"\n\"609\",\"aac\"\n\"609\",\"alloy\"\n\"608\",\"sliding-window\"\n\"608\",\"file-exists\"\n\"608\",\"swiftui-navigationview\"\n\"608\",\"self-reference\"\n\"608\",\"pycurl\"\n\"608\",\"sequence-diagram\"\n\"608\",\"unsigned-integer\"\n\"608\",\"wix3\"\n\"608\",\"quoting\"\n\"608\",\"conways-game-of-life\"\n\"608\",\"sdn\"\n\"607\",\"gridsearchcv\"\n\"607\",\"symfony-2.8\"\n\"607\",\"xforms\"\n\"607\",\"facebook-unity-sdk\"\n\"607\",\"axon\"\n\"607\",\"google-cloud-speech\"\n\"607\",\"node-postgres\"\n\"606\",\"week-number\"\n\"606\",\"underline\"\n\"606\",\"enumerable\"\n\"606\",\"for-in-loop\"\n\"606\",\"except\"\n\"606\",\"actionlink\"\n\"606\",\"angular4-forms\"\n\"606\",\"loss\"\n\"606\",\"armv7\"\n\"606\",\"mule-esb\"\n\"606\",\"mayavi\"\n\"605\",\"flowplayer\"\n\"605\",\"reinterpret-cast\"\n\"605\",\"datamodel\"\n\"605\",\"reboot\"\n\"605\",\"negative-number\"\n\"605\",\"android-syncadapter\"\n\"605\",\"leaderboard\"\n\"605\",\"google-pay\"\n\"604\",\"deflate\"\n\"604\",\"mat-table\"\n\"604\",\"masm32\"\n\"604\",\"matlab-cvst\"\n\"604\",\"displaytag\"\n\"604\",\"landscape-portrait\"\n\"604\",\"ui-thread\"\n\"604\",\"android-things\"\n\"604\",\"detailsview\"\n\"604\",\"azure-container-registry\"\n\"604\",\"rfc\"\n\"604\",\"sqlcipher\"\n\"604\",\"preprocessor-directive\"\n\"604\",\"subtitle\"\n\"603\",\"printers\"\n\"603\",\"unc\"\n\"603\",\"symfony-sonata\"\n\"603\",\"biztalk-2010\"\n\"603\",\"azure-deployment\"\n\"603\",\"go-modules\"\n\"603\",\"linq-expressions\"\n\"602\",\"marshmallow\"\n\"602\",\"formarray\"\n\"601\",\"yfinance\"\n\"601\",\"blazeds\"\n\"601\",\"distributed-transactions\"\n\"601\",\"purescript\"\n\"601\",\"intersection-observer\"\n\"601\",\"fragmentpageradapter\"\n\"601\",\"out\"\n\"601\",\"spark-submit\"\n\"600\",\"docker-build\"\n\"600\",\"password-hash\"\n\"600\",\"downcast\"\n\"600\",\"paraview\"\n\"600\",\"tqdm\"\n\"600\",\"solrnet\"\n\"600\",\"gluon-mobile\"\n\"599\",\"webvr\"\n\"599\",\"replicaset\"\n\"599\",\"cascading-deletes\"\n\"599\",\"simplemembership\"\n\"599\",\"azure-artifacts\"\n\"599\",\"codedom\"\n\"599\",\"networkstream\"\n\"599\",\"android-assets\"\n\"599\",\"cabasicanimation\"\n\"598\",\"widgetkit\"\n\"598\",\"yolov5\"\n\"598\",\"jetty-9\"\n\"598\",\"finite-automata\"\n\"598\",\"icmp\"\n\"598\",\"autoscroll\"\n\"598\",\"arp\"\n\"597\",\"laravel-echo\"\n\"597\",\"configurationmanager\"\n\"597\",\"r-mice\"\n\"597\",\"unset\"\n\"597\",\"bpel\"\n\"597\",\"boxing\"\n\"597\",\"one-time-password\"\n\"597\",\"magento-1.5\"\n\"597\",\"diagonal\"\n\"597\",\"soundpool\"\n\"596\",\"slidingmenu\"\n\"596\",\"data-uri\"\n\"596\",\"red-black-tree\"\n\"596\",\"facebook-sharer\"\n\"596\",\"value-type\"\n\"596\",\"dendrogram\"\n\"596\",\"pause\"\n\"596\",\"homescreen\"\n\"596\",\"exponent\"\n\"596\",\"charles-proxy\"\n\"595\",\"greendao\"\n\"595\",\"tridion-2011\"\n\"595\",\"wicked-pdf\"\n\"595\",\"functional-interface\"\n\"595\",\"calabash\"\n\"595\",\"lxc\"\n\"595\",\"word-addins\"\n\"595\",\"stargazer\"\n\"594\",\"file-sharing\"\n\"594\",\"aidl\"\n\"594\",\"magnolia\"\n\"594\",\"mpeg-dash\"\n\"593\",\"bare-metal\"\n\"593\",\"firebase-remote-config\"\n\"593\",\"dynamics-ax-2009\"\n\"593\",\"bookshelf.js\"\n\"593\",\"taskmanager\"\n\"593\",\"geom-text\"\n\"593\",\"thread-local\"\n\"593\",\"toolkit\"\n\"592\",\"edges\"\n\"592\",\"onenote\"\n\"592\",\"crc32\"\n\"592\",\"sqlanywhere\"\n\"592\",\"dhtmlx\"\n\"592\",\"scoping\"\n\"592\",\"qlistwidget\"\n\"592\",\"amazon-efs\"\n\"591\",\"chatgpt-api\"\n\"591\",\"database-indexes\"\n\"591\",\"django-rest-auth\"\n\"591\",\"bootstrap-select\"\n\"591\",\"gsoap\"\n\"591\",\"midp\"\n\"591\",\"codesign\"\n\"591\",\"openoffice-calc\"\n\"591\",\"qweb\"\n\"591\",\"onbackpressed\"\n\"591\",\"text-align\"\n\"591\",\"terrain\"\n\"591\",\"acceptance-testing\"\n\"590\",\"flowchart\"\n\"590\",\"openal\"\n\"590\",\"application-server\"\n\"590\",\"scala-2.10\"\n\"590\",\"netbeans-platform\"\n\"590\",\"mirror\"\n\"590\",\"jagged-arrays\"\n\"590\",\"xamarin.mac\"\n\"590\",\"morris.js\"\n\"589\",\"ggpubr\"\n\"589\",\"firebase-console\"\n\"589\",\"mail-server\"\n\"589\",\"rstudio-server\"\n\"589\",\"goland\"\n\"589\",\"typo3-extensions\"\n\"589\",\"maatwebsite-excel\"\n\"589\",\"notifydatasetchanged\"\n\"589\",\"scrapy-splash\"\n\"589\",\"droppable\"\n\"589\",\"azure-storage-account\"\n\"589\",\"preact\"\n\"588\",\"weld\"\n\"588\",\"photo-gallery\"\n\"588\",\"vscode-tasks\"\n\"588\",\"deprecation-warning\"\n\"588\",\"3dsmax\"\n\"588\",\"response.redirect\"\n\"588\",\"android-productflavors\"\n\"588\",\"gesture-recognition\"\n\"588\",\"strict-aliasing\"\n\"588\",\"thunderbird\"\n\"588\",\"webpack-module-federation\"\n\"587\",\"antiforgerytoken\"\n\"587\",\"insertion\"\n\"587\",\"laravel-excel\"\n\"587\",\"calculus\"\n\"587\",\"surf\"\n\"587\",\"asyncsocket\"\n\"587\",\"expandablelistadapter\"\n\"587\",\"substrate\"\n\"586\",\"sweave\"\n\"586\",\"byte-order-mark\"\n\"586\",\"android-database\"\n\"586\",\"motion\"\n\"586\",\"mousehover\"\n\"585\",\"mapbox-android\"\n\"585\",\"ngcordova\"\n\"585\",\"service-broker\"\n\"585\",\"coreos\"\n\"585\",\"pos-tagger\"\n\"585\",\"core-data-migration\"\n\"585\",\"visual-studio-express\"\n\"585\",\"wininet\"\n\"585\",\"formset\"\n\"585\",\"continuous-delivery\"\n\"585\",\"zap\"\n\"584\",\"gridgain\"\n\"584\",\"instructions\"\n\"584\",\"apple-silicon\"\n\"584\",\"ordereddictionary\"\n\"584\",\"dfa\"\n\"584\",\"gcov\"\n\"584\",\"geotools\"\n\"584\",\"web-controls\"\n\"583\",\"mutation\"\n\"583\",\"skype-for-business\"\n\"583\",\"firefox-os\"\n\"583\",\"manifest.json\"\n\"583\",\"unrecognized-selector\"\n\"583\",\"virtual-environment\"\n\"583\",\"mailchimp-api-v3.0\"\n\"583\",\"hierarchical\"\n\"583\",\"azure-qna-maker\"\n\"583\",\"nosuchelementexception\"\n\"583\",\"angular-bootstrap\"\n\"583\",\"qtextedit\"\n\"583\",\"tidy\"\n\"583\",\"scrollable\"\n\"583\",\"use-context\"\n\"583\",\"compute-shader\"\n\"582\",\"blackberry-simulator\"\n\"582\",\"imgur\"\n\"582\",\"rpm-spec\"\n\"582\",\"sharepoint-workflow\"\n\"582\",\"aws-codecommit\"\n\"582\",\"intranet\"\n\"582\",\"rgdal\"\n\"582\",\"mms\"\n\"582\",\"coldfusion-8\"\n\"582\",\"concrete5\"\n\"582\",\"computed-properties\"\n\"581\",\"udpclient\"\n\"581\",\"activesupport\"\n\"581\",\"log4net-configuration\"\n\"581\",\"laravel-middleware\"\n\"580\",\"relational-algebra\"\n\"580\",\"swrevealviewcontroller\"\n\"580\",\"address-sanitizer\"\n\"580\",\"method-reference\"\n\"580\",\"bonjour\"\n\"580\",\"getelementsbytagname\"\n\"580\",\"android-json\"\n\"580\",\"custom-function\"\n\"580\",\"flutter-widget\"\n\"579\",\"ef-core-2.2\"\n\"579\",\"trello\"\n\"579\",\"typeconverter\"\n\"579\",\"delphi-10.1-berlin\"\n\"579\",\"envdte\"\n\"579\",\"synology\"\n\"579\",\"gatt\"\n\"579\",\"exchange-server-2010\"\n\"578\",\"angular-module\"\n\"578\",\"applicative\"\n\"578\",\"jquery-cycle\"\n\"578\",\"postfix-notation\"\n\"578\",\"terraform0.12+\"\n\"577\",\"faceted-search\"\n\"577\",\"samsung-smart-tv\"\n\"577\",\"video-recording\"\n\"577\",\"c#-8.0\"\n\"577\",\"mouseenter\"\n\"577\",\"street-address\"\n\"577\",\"web-inspector\"\n\"576\",\"postgresql-performance\"\n\"576\",\"retain\"\n\"576\",\"stm32f4discovery\"\n\"575\",\"uitextfielddelegate\"\n\"575\",\"cardview\"\n\"575\",\"rgl\"\n\"575\",\"asp.net-mvc-areas\"\n\"575\",\"ios7.1\"\n\"575\",\"persist\"\n\"574\",\"pygame-surface\"\n\"574\",\"object-oriented-analysis\"\n\"574\",\"geofire\"\n\"574\",\"react-chartjs\"\n\"573\",\"fbml\"\n\"573\",\"imageresizer\"\n\"573\",\"windows-explorer\"\n\"573\",\"windows-server-2019\"\n\"573\",\"nested-class\"\n\"573\",\"dynamic-linq\"\n\"573\",\"invalidation\"\n\"573\",\"razorengine\"\n\"572\",\"markov-chains\"\n\"572\",\"affinetransform\"\n\"572\",\"django-generic-views\"\n\"572\",\"read-the-docs\"\n\"572\",\"blueprism\"\n\"572\",\"nskeyedarchiver\"\n\"572\",\"invoke-command\"\n\"572\",\"wms\"\n\"572\",\"azure-function-app\"\n\"572\",\"azure-sdk-.net\"\n\"572\",\"event-bubbling\"\n\"572\",\"cucumber-junit\"\n\"572\",\"web3py\"\n\"571\",\"jet\"\n\"571\",\"livereload\"\n\"571\",\"semantic-segmentation\"\n\"571\",\"ibeacon-android\"\n\"571\",\"kaminari\"\n\"571\",\"blending\"\n\"571\",\"errno\"\n\"571\",\"eventkit\"\n\"571\",\"stm32f4\"\n\"571\",\"tfs-sdk\"\n\"571\",\"gnome-terminal\"\n\"570\",\"xslt-grouping\"\n\"570\",\"material-components\"\n\"570\",\"flowdocument\"\n\"570\",\"pdfmake\"\n\"570\",\"post-increment\"\n\"570\",\"lync\"\n\"570\",\"objectoutputstream\"\n\"570\",\"linegraph\"\n\"570\",\"cyrillic\"\n\"569\",\"gganimate\"\n\"569\",\"vxworks\"\n\"569\",\"read.table\"\n\"569\",\"postgresql-12\"\n\"569\",\"nsbundle\"\n\"569\",\"object-literal\"\n\"569\",\"r-highcharter\"\n\"569\",\"string.format\"\n\"569\",\"android-package-managers\"\n\"569\",\"forecast\"\n\"569\",\"weather-api\"\n\"569\",\"alphablending\"\n\"568\",\"graph-visualization\"\n\"568\",\"websphere-portal\"\n\"568\",\"fullcalendar-5\"\n\"568\",\"direct-line-botframework\"\n\"568\",\"lcd\"\n\"568\",\"cgimage\"\n\"568\",\"parse-error\"\n\"568\",\"beamer\"\n\"567\",\"eclipse-adt\"\n\"567\",\"union-types\"\n\"567\",\"jsplumb\"\n\"567\",\"aptana3\"\n\"567\",\"formview\"\n\"567\",\"http-referer\"\n\"567\",\"response-headers\"\n\"567\",\"usb-drive\"\n\"567\",\"sts-springsourcetoolsuite\"\n\"567\",\"ascx\"\n\"567\",\"quartus\"\n\"567\",\"spark-java\"\n\"566\",\"react-native-reanimated\"\n\"566\",\"watchdog\"\n\"566\",\"android-windowmanager\"\n\"566\",\"nsdatecomponents\"\n\"566\",\"normalize\"\n\"565\",\"yaxis\"\n\"565\",\"adfs2.0\"\n\"565\",\"waveform\"\n\"565\",\"calloc\"\n\"565\",\"mac-catalyst\"\n\"565\",\"spring-xd\"\n\"565\",\"contiki\"\n\"565\",\"shibboleth\"\n\"565\",\"idl\"\n\"564\",\"closest\"\n\"564\",\"marklogic-8\"\n\"564\",\"claims\"\n\"564\",\"jcarousel\"\n\"564\",\"doc2vec\"\n\"564\",\"apple-tv\"\n\"564\",\"delphi-10.2-tokyo\"\n\"564\",\"uefi\"\n\"564\",\"outlook-2007\"\n\"564\",\"xamarin.uwp\"\n\"564\",\"ios15\"\n\"564\",\"user32\"\n\"564\",\"presentmodalviewcontroller\"\n\"564\",\"customvalidator\"\n\"563\",\"gridextra\"\n\"563\",\"full-text-indexing\"\n\"563\",\"cardlayout\"\n\"563\",\"key-value-store\"\n\"563\",\"opencart-3\"\n\"563\",\"htmx\"\n\"563\",\"gecko\"\n\"563\",\"nixos\"\n\"562\",\"oxyplot\"\n\"562\",\"days\"\n\"562\",\"freeradius\"\n\"562\",\"error-code\"\n\"562\",\"nsnumberformatter\"\n\"562\",\"bnf\"\n\"562\",\"revert\"\n\"562\",\"asmack\"\n\"562\",\"sonatype\"\n\"561\",\"xterm\"\n\"561\",\"bi-publisher\"\n\"561\",\"universal-analytics\"\n\"561\",\"sinch\"\n\"561\",\"opayo\"\n\"561\",\"postgresql-11\"\n\"561\",\"android-geofence\"\n\"561\",\"android-jobscheduler\"\n\"560\",\"matomo\"\n\"560\",\"lnk2019\"\n\"560\",\"template-literals\"\n\"560\",\"phpbb\"\n\"560\",\"loadlibrary\"\n\"560\",\"python-os\"\n\"560\",\"libvirt\"\n\"560\",\"coldfusion-11\"\n\"560\",\"codepen\"\n\"560\",\"accessibilityservice\"\n\"560\",\"avaudioengine\"\n\"559\",\"decoder\"\n\"559\",\"procedural-generation\"\n\"559\",\"mapply\"\n\"559\",\"python-requests-html\"\n\"559\",\"reader\"\n\"559\",\"information-schema\"\n\"559\",\"referenceerror\"\n\"559\",\"google-dfp\"\n\"559\",\"chain\"\n\"559\",\"proto\"\n\"559\",\"bazaar\"\n\"559\",\"particle-system\"\n\"558\",\"apache-beam-io\"\n\"558\",\"python-social-auth\"\n\"558\",\"findviewbyid\"\n\"558\",\"ngxs\"\n\"558\",\"intern\"\n\"558\",\"createelement\"\n\"558\",\"googlebot\"\n\"558\",\"expander\"\n\"558\",\"google-pagespeed\"\n\"558\",\"msysgit\"\n\"557\",\"ddos\"\n\"557\",\"delphi-10.3-rio\"\n\"557\",\"dosbox\"\n\"557\",\"audio-processing\"\n\"557\",\"cad\"\n\"557\",\"itemtemplate\"\n\"557\",\"http-status-code-405\"\n\"557\",\"git-tag\"\n\"557\",\"sox\"\n\"557\",\"dafny\"\n\"556\",\"temp\"\n\"556\",\"apache-httpcomponents\"\n\"556\",\"dynamics-crm-2015\"\n\"556\",\"spring-boot-maven-plugin\"\n\"556\",\"android-instant-apps\"\n\"556\",\"qimage\"\n\"556\",\"mediatr\"\n\"556\",\"subsonic3\"\n\"556\",\"preg-split\"\n\"555\",\"next.js14\"\n\"555\",\"blackberry-webworks\"\n\"555\",\"jqgrid-asp.net\"\n\"555\",\"free-jqgrid\"\n\"555\",\"convex-hull\"\n\"555\",\"perfect-forwarding\"\n\"554\",\"rule\"\n\"554\",\"avplayerviewcontroller\"\n\"554\",\"mixpanel\"\n\"554\",\"numpy-slicing\"\n\"554\",\"mockk\"\n\"553\",\"dbscan\"\n\"553\",\"localstack\"\n\"553\",\"vtiger\"\n\"553\",\"mysql-connector-python\"\n\"553\",\"nas\"\n\"553\",\"middleman\"\n\"553\",\"winrm\"\n\"552\",\"functional-dependencies\"\n\"552\",\"image-size\"\n\"552\",\"dml\"\n\"552\",\"aws-sdk-nodejs\"\n\"552\",\"uiaccessibility\"\n\"552\",\"fortran77\"\n\"552\",\"dpkg\"\n\"552\",\"openmdao\"\n\"552\",\"http-delete\"\n\"552\",\"tox\"\n\"552\",\"parcel\"\n\"551\",\"remix.run\"\n\"551\",\"angular-material-table\"\n\"551\",\"crypt\"\n\"551\",\"nomethoderror\"\n\"551\",\"prerender\"\n\"551\",\"bigtable\"\n\"550\",\"fillna\"\n\"550\",\"nsscrollview\"\n\"550\",\"domparser\"\n\"550\",\"javaagents\"\n\"550\",\"moshi\"\n\"550\",\"heapsort\"\n\"550\",\"git-subtree\"\n\"550\",\"maxlength\"\n\"549\",\"jbehave\"\n\"549\",\"jboss-eap-6\"\n\"549\",\"divide-by-zero\"\n\"549\",\"fsm\"\n\"549\",\"flatmap\"\n\"549\",\"aws-java-sdk\"\n\"549\",\"namedtuple\"\n\"549\",\"hittest\"\n\"549\",\"change-password\"\n\"549\",\"chainlink\"\n\"548\",\"xvfb\"\n\"548\",\"floating-point-precision\"\n\"548\",\"contactscontract\"\n\"548\",\"fullcalendar-4\"\n\"548\",\"kie\"\n\"548\",\"tap\"\n\"548\",\"xbee\"\n\"548\",\"contravariance\"\n\"548\",\"exceljs\"\n\"548\",\"spf\"\n\"548\",\"get-childitem\"\n\"548\",\"specs2\"\n\"548\",\"use-ref\"\n\"547\",\"jcrop\"\n\"547\",\"yarn-workspaces\"\n\"547\",\"git-fork\"\n\"547\",\"adhoc\"\n\"547\",\"ftps\"\n\"547\",\"sharepoint-list\"\n\"547\",\"karaf\"\n\"547\",\"rgba\"\n\"547\",\"git-merge-conflict\"\n\"546\",\"ghost-blog\"\n\"546\",\"jspm\"\n\"546\",\"upstart\"\n\"546\",\"delphi-xe4\"\n\"546\",\"bugzilla\"\n\"546\",\"macos-carbon\"\n\"546\",\"mockery\"\n\"546\",\"ios-frameworks\"\n\"546\",\"angularjs-e2e\"\n\"546\",\"resume\"\n\"546\",\"google-font-api\"\n\"546\",\"http-status-code-503\"\n\"546\",\"maven-shade-plugin\"\n\"546\",\"spannablestring\"\n\"545\",\"addsubview\"\n\"545\",\"adobe-brackets\"\n\"545\",\"gql\"\n\"545\",\"android-video-player\"\n\"545\",\"wmi-query\"\n\"545\",\"continuations\"\n\"545\",\"talkback\"\n\"545\",\"google-people-api\"\n\"544\",\"voronoi\"\n\"544\",\"undertow\"\n\"544\",\"recursive-datastructures\"\n\"544\",\"gs-conditional-formatting\"\n\"544\",\"type-providers\"\n\"544\",\"fractals\"\n\"544\",\"exist-db\"\n\"544\",\"nivo-slider\"\n\"544\",\"glsurfaceview\"\n\"543\",\"tee\"\n\"543\",\"python-click\"\n\"543\",\"nexus3\"\n\"543\",\"unresolved-external\"\n\"543\",\"ruby-on-rails-5.2\"\n\"543\",\"qtgui\"\n\"542\",\"reactor-netty\"\n\"542\",\"graphdb\"\n\"542\",\"mutate\"\n\"542\",\"discriminated-union\"\n\"542\",\"smartsheet-api\"\n\"542\",\"rm\"\n\"542\",\"infix-notation\"\n\"542\",\"podio\"\n\"542\",\"angular-google-maps\"\n\"542\",\"std-ranges\"\n\"542\",\"powercli\"\n\"541\",\"github-flavored-markdown\"\n\"541\",\"staging\"\n\"541\",\"jql\"\n\"541\",\"mixed-integer-programming\"\n\"541\",\"netstat\"\n\"541\",\"uiprogressview\"\n\"540\",\"apache-commons-httpclient\"\n\"540\",\"file-management\"\n\"540\",\"id3\"\n\"540\",\"watson\"\n\"540\",\"android-12\"\n\"540\",\"qwebview\"\n\"540\",\"azure-sql-server\"\n\"540\",\"user-management\"\n\"539\",\"teradata-sql-assistant\"\n\"539\",\"discord-jda\"\n\"539\",\"configmap\"\n\"539\",\"aws-opsworks\"\n\"539\",\"readr\"\n\"539\",\"ros2\"\n\"539\",\"auc\"\n\"539\",\"esper\"\n\"539\",\"uvm\"\n\"539\",\"avaloniaui\"\n\"539\",\"multi-level\"\n\"538\",\"temporary\"\n\"538\",\"playframework-2.4\"\n\"538\",\"graalvm-native-image\"\n\"538\",\"cross-join\"\n\"538\",\"upnp\"\n\"538\",\"jmespath\"\n\"538\",\"svelte-component\"\n\"538\",\"ports\"\n\"538\",\"android-sharedpreferences\"\n\"538\",\"seek\"\n\"537\",\"federated-identity\"\n\"537\",\"jsonserializer\"\n\"537\",\"sequences\"\n\"537\",\"amd-processor\"\n\"537\",\"udf\"\n\"537\",\"atmosphere\"\n\"537\",\"scalar\"\n\"537\",\"butterknife\"\n\"537\",\"alm\"\n\"537\",\"stemming\"\n\"537\",\"msal.js\"\n\"536\",\"appharbor\"\n\"536\",\"jsondecoder\"\n\"536\",\"sencha-cmd\"\n\"536\",\"keystroke\"\n\"536\",\"type-parameter\"\n\"536\",\"android-install-apk\"\n\"536\",\"azure-policy\"\n\"536\",\"httpconnection\"\n\"536\",\"android-paging\"\n\"535\",\"ps\"\n\"535\",\"wakelock\"\n\"535\",\"aws-sam-cli\"\n\"535\",\"azure-authentication\"\n\"535\",\"nssortdescriptor\"\n\"535\",\"nsbutton\"\n\"535\",\"supervised-learning\"\n\"535\",\"outlook-redemption\"\n\"535\",\"3g\"\n\"535\",\"minimization\"\n\"535\",\"razorpay\"\n\"535\",\"strcat\"\n\"534\",\"sslhandshakeexception\"\n\"534\",\"python-behave\"\n\"534\",\"approximation\"\n\"534\",\"jquery-waypoints\"\n\"534\",\"fpm\"\n\"534\",\"hidden-markov-models\"\n\"534\",\"uiswipegesturerecognizer\"\n\"534\",\"pytube\"\n\"534\",\"responsive-images\"\n\"534\",\"amazon-product-api\"\n\"533\",\"dblink\"\n\"533\",\"fixed-point\"\n\"533\",\"spring-mybatis\"\n\"533\",\"nsarraycontroller\"\n\"533\",\"saucelabs\"\n\"533\",\"java-websocket\"\n\"533\",\"non-static\"\n\"533\",\"itk\"\n\"533\",\"taxonomy-terms\"\n\"533\",\"autorotate\"\n\"533\",\"array.prototype.map\"\n\"533\",\"qtreewidget\"\n\"533\",\"steam-web-api\"\n\"532\",\"remember-me\"\n\"532\",\"freetype\"\n\"532\",\"selectlist\"\n\"532\",\"aiogram\"\n\"532\",\"csr\"\n\"532\",\"critical-section\"\n\"532\",\"doctrine-odm\"\n\"532\",\"sapb1\"\n\"532\",\"dropdownbox\"\n\"532\",\"gdata-api\"\n\"532\",\"android-instrumentation\"\n\"532\",\"columnsorting\"\n\"532\",\"powershell-core\"\n\"532\",\"parameterized\"\n\"532\",\"prefetch\"\n\"531\",\"fileapi\"\n\"531\",\"openembedded\"\n\"531\",\"google-chrome-os\"\n\"531\",\".htpasswd\"\n\"531\",\"hhvm\"\n\"531\",\"hal\"\n\"530\",\"gremlin-server\"\n\"530\",\"pg-promise\"\n\"530\",\"jeditorpane\"\n\"530\",\"swipe-gesture\"\n\"530\",\"manytomanyfield\"\n\"530\",\"angularjs-ng-model\"\n\"530\",\"google-closure\"\n\"530\",\"anagram\"\n\"530\",\".net-maui\"\n\"530\",\"virtual-directory\"\n\"530\",\"gutenberg-blocks\"\n\"530\",\"geopy\"\n\"529\",\"matlab-deployment\"\n\"529\",\"ng2-charts\"\n\"529\",\"apostrophe-cms\"\n\"529\",\"watson-assistant\"\n\"529\",\"docusaurus\"\n\"529\",\"portaudio\"\n\"529\",\"opendaylight\"\n\"529\",\"boolean-operations\"\n\"529\",\"java-platform-module-system\"\n\"529\",\"dozer\"\n\"529\",\"sqlclient\"\n\"529\",\"static-site\"\n\"529\",\"line-endings\"\n\"528\",\"data-synchronization\"\n\"528\",\"pagespeed-insights\"\n\"528\",\"softmax\"\n\"528\",\"cakephp-4.x\"\n\"528\",\"couchbase-lite\"\n\"528\",\"boilerplate\"\n\"528\",\"onkeydown\"\n\"528\",\"rad\"\n\"528\",\"iphone-privateapi\"\n\"528\",\"monolog\"\n\"528\",\"specialization\"\n\"528\",\"http-caching\"\n\"528\",\"steganography\"\n\"527\",\"clpfd\"\n\"527\",\"consistency\"\n\"527\",\"ng-show\"\n\"527\",\"react-virtualized\"\n\"527\",\"css-loader\"\n\"527\",\"blogdown\"\n\"527\",\"openframeworks\"\n\"527\",\"gt\"\n\"527\",\"apriori\"\n\"527\",\"homography\"\n\"527\",\"angular13\"\n\"527\",\"presentviewcontroller\"\n\"527\",\"querying\"\n\"526\",\"relational\"\n\"526\",\"unreal-blueprint\"\n\"526\",\"canopy\"\n\"526\",\"jprofiler\"\n\"526\",\"sbt-assembly\"\n\"526\",\"uiapplicationdelegate\"\n\"526\",\"android-pay\"\n\"526\",\"requirements\"\n\"526\",\"mouseleave\"\n\"526\",\"email-verification\"\n\"526\",\"enable-if\"\n\"526\",\"avassetwriter\"\n\"526\",\"shellexecute\"\n\"525\",\"fieldset\"\n\"525\",\"in-app-subscription\"\n\"525\",\"simple-framework\"\n\"525\",\"virtuoso\"\n\"525\",\"android-browser\"\n\"525\",\"asio\"\n\"525\",\"timber\"\n\"524\",\"webrick\"\n\"524\",\"cifilter\"\n\"524\",\"avi\"\n\"524\",\"spring-cloud-stream-binder-kafka\"\n\"524\",\"save-as\"\n\"524\",\"stateless\"\n\"524\",\"git-reset\"\n\"523\",\"backbone.js-collections\"\n\"523\",\"catalyst\"\n\"523\",\"chef-solo\"\n\"523\",\"unlink\"\n\"523\",\"revit\"\n\"523\",\"plm\"\n\"523\",\"collada\"\n\"523\",\"qooxdoo\"\n\"523\",\"odoo-15\"\n\"523\",\"userdefaults\"\n\"523\",\"powershell-ise\"\n\"523\",\"emacs24\"\n\"523\",\"asana\"\n\"522\",\"ime\"\n\"522\",\"data-preprocessing\"\n\"522\",\"named-query\"\n\"522\",\"pycaffe\"\n\"522\",\"pattern-recognition\"\n\"522\",\"libavcodec\"\n\"522\",\"pymc\"\n\"522\",\"dsc\"\n\"522\",\"express-checkout\"\n\"522\",\"nmake\"\n\"522\",\"seeding\"\n\"522\",\"quartz\"\n\"521\",\"php-5.5\"\n\"521\",\"size-classes\"\n\"521\",\"unique-key\"\n\"521\",\"marathon\"\n\"521\",\"ngresource\"\n\"521\",\"chm\"\n\"521\",\"read.csv\"\n\"521\",\"icecast\"\n\"521\",\"fallback\"\n\"521\",\"bsd\"\n\"521\",\"koin\"\n\"521\",\"knife\"\n\"521\",\"qmainwindow\"\n\"521\",\"acts-as-taggable-on\"\n\"520\",\"fuelphp\"\n\"520\",\"snackbar\"\n\"520\",\"celerybeat\"\n\"520\",\"pdftk\"\n\"520\",\"redux-persist\"\n\"520\",\"scorm\"\n\"520\",\"event-tracking\"\n\"520\",\"google-directions-api\"\n\"520\",\"collaboration\"\n\"519\",\"fckeditor\"\n\"519\",\"prime-factoring\"\n\"519\",\"file-access\"\n\"519\",\"animate.css\"\n\"519\",\"kiosk-mode\"\n\"519\",\"scene2d\"\n\"519\",\"mobilefirst-adapters\"\n\"519\",\"google-drive-realtime-api\"\n\"519\",\"persistence.xml\"\n\"519\",\"scriptmanager\"\n\"519\",\"static-cast\"\n\"519\",\"heroku-toolbelt\"\n\"518\",\"php-7.1\"\n\"518\",\"react-scripts\"\n\"518\",\"cmis\"\n\"518\",\"psychopy\"\n\"518\",\"iokit\"\n\"518\",\"errorbar\"\n\"518\",\"neo4j-ogm\"\n\"518\",\"lwc\"\n\"518\",\"dspace\"\n\"518\",\"nunjucks\"\n\"518\",\"google-cloud-sdk\"\n\"518\",\"cytoscape\"\n\"517\",\"process.start\"\n\"517\",\"stack-navigator\"\n\"517\",\"flex-spark\"\n\"517\",\"datagridviewcolumn\"\n\"517\",\"worklight-adapters\"\n\"517\",\"device-orientation\"\n\"517\",\"memgraphdb\"\n\"517\",\"user-data\"\n\"517\",\"stereo-3d\"\n\"516\",\"product-variations\"\n\"516\",\"unauthorized\"\n\"516\",\"ad-hoc-distribution\"\n\"516\",\"kendo-treeview\"\n\"516\",\"keycloak-rest-api\"\n\"516\",\"canvasjs\"\n\"516\",\"jquery-select2-4\"\n\"516\",\"bpf\"\n\"516\",\"html5-appcache\"\n\"516\",\"spring-data-neo4j-4\"\n\"516\",\"artifacts\"\n\"515\",\"client-side-validation\"\n\"515\",\"after-effects\"\n\"515\",\"fuzzywuzzy\"\n\"515\",\"smsmanager\"\n\"515\",\"python-sockets\"\n\"515\",\"suse\"\n\"515\",\"browser-detection\"\n\"515\",\"tabindex\"\n\"515\",\"cookbook\"\n\"515\",\"diagnostics\"\n\"515\",\"hibernate-annotations\"\n\"515\",\"geotiff\"\n\"515\",\"stretch\"\n\"515\",\"gestures\"\n\"515\",\"ogg\"\n\"515\",\"huggingface-tokenizers\"\n\"514\",\"instances\"\n\"514\",\"ts-node\"\n\"514\",\"invalidoperationexception\"\n\"514\",\"post-build-event\"\n\"514\",\"knockout-validation\"\n\"514\",\"period\"\n\"514\",\"nlme\"\n\"514\",\"reactivemongo\"\n\"514\",\"meson-build\"\n\"514\",\"accountmanager\"\n\"514\",\"actix-web\"\n\"513\",\"php-7.4\"\n\"513\",\"datasnap\"\n\"513\",\"disable\"\n\"513\",\"windows-firewall\"\n\"513\",\"eas\"\n\"513\",\"domain-name\"\n\"513\",\"open-liberty\"\n\"513\",\"electron-packager\"\n\"513\",\"geography\"\n\"512\",\"whmcs\"\n\"512\",\"jsonlite\"\n\"512\",\"sequelize-cli\"\n\"512\",\"systemctl\"\n\"512\",\"microsoft-graph-mail\"\n\"512\",\"f2py\"\n\"512\",\"tmap\"\n\"512\",\"pnpm\"\n\"512\",\"hl7\"\n\"512\",\"memset\"\n\"512\",\"laravel-migrations\"\n\"512\",\"ipod\"\n\"512\",\"cgpoint\"\n\"512\",\"pre-trained-model\"\n\"511\",\"clustered-index\"\n\"511\",\"jeditable\"\n\"511\",\"blackberry-eclipse-plugin\"\n\"511\",\"owl-api\"\n\"511\",\"unobtrusive-javascript\"\n\"511\",\"fastreport\"\n\"511\",\"kiosk\"\n\"511\",\"bluetooth-gatt\"\n\"511\",\"mirth\"\n\"511\",\"pfquery\"\n\"511\",\"qnx\"\n\"511\",\"git-revert\"\n\"511\",\"measurement\"\n\"511\",\"suitecrm\"\n\"510\",\"react-router-redux\"\n\"510\",\"pkcs#7\"\n\"510\",\"symfony6\"\n\"510\",\"dkim\"\n\"510\",\"roku\"\n\"510\",\"rocksdb\"\n\"510\",\"nsindexpath\"\n\"510\",\"springdoc\"\n\"510\",\"rapidminer\"\n\"510\",\"odeint\"\n\"510\",\"eslintrc\"\n\"510\",\"spectrogram\"\n\"510\",\"autostart\"\n\"509\",\"python-class\"\n\"509\",\"microbenchmark\"\n\"509\",\"mongomapper\"\n\"509\",\"wordpress-shortcode\"\n\"509\",\"viewer\"\n\"509\",\"nunit-3.0\"\n\"509\",\"android-imagebutton\"\n\"509\",\"playwright-python\"\n\"509\",\"c#-7.0\"\n\"509\",\"autodesk-bim360\"\n\"508\",\"fbx\"\n\"508\",\"edi\"\n\"508\",\"fullcalendar-scheduler\"\n\"508\",\"readlines\"\n\"508\",\"databricks-sql\"\n\"508\",\"virus\"\n\"508\",\"pylance\"\n\"508\",\"tableadapter\"\n\"508\",\"tire\"\n\"508\",\"qpixmap\"\n\"508\",\"angular2-components\"\n\"508\",\"requiredfieldvalidator\"\n\"508\",\"gitpython\"\n\"507\",\"pjax\"\n\"507\",\"date-fns\"\n\"507\",\"udev\"\n\"507\",\"issue-tracking\"\n\"507\",\"textcolor\"\n\"507\",\"user-registration\"\n\"507\",\"msys\"\n\"506\",\"mars-simulator\"\n\"506\",\"group-policy\"\n\"506\",\"matrix-inverse\"\n\"506\",\"where-in\"\n\"506\",\"algebraic-data-types\"\n\"506\",\"annotate\"\n\"506\",\"android-viewbinding\"\n\"506\",\"savon\"\n\"506\",\"viewgroup\"\n\"506\",\"business-rules\"\n\"506\",\"timestamp-with-timezone\"\n\"506\",\"tableofcontents\"\n\"506\",\"qt-signals\"\n\"505\",\"whenever\"\n\"505\",\"playframework-2.5\"\n\"505\",\"working-directory\"\n\"505\",\"dropzone\"\n\"505\",\"angular14\"\n\"504\",\"jks\"\n\"504\",\"meteor-autoform\"\n\"504\",\"dynamic-allocation\"\n\"504\",\"coefficients\"\n\"504\",\"regedit\"\n\"504\",\"android-ffmpeg\"\n\"504\",\"gemfire\"\n\"504\",\"screen-rotation\"\n\"504\",\"react-datepicker\"\n\"504\",\"google-identity\"\n\"503\",\"cntk\"\n\"503\",\"date-parsing\"\n\"503\",\"saga\"\n\"503\",\"csrf-token\"\n\"503\",\"infrastructure-as-code\"\n\"503\",\"delphi-xe6\"\n\"503\",\"pde\"\n\"503\",\"navigationcontroller\"\n\"503\",\"osb\"\n\"503\",\"openiddict\"\n\"502\",\"ant-design-pro\"\n\"502\",\"dateadd\"\n\"502\",\"ado.net-entity-data-model\"\n\"502\",\"uploading\"\n\"502\",\"cakephp-model\"\n\"502\",\"wave\"\n\"502\",\"angularjs-material\"\n\"502\",\"descriptor\"\n\"502\",\"os.path\"\n\"502\",\"buffering\"\n\"502\",\"libgit2sharp\"\n\"502\",\"tablecellrenderer\"\n\"502\",\"raw-sockets\"\n\"502\",\"android-cursorloader\"\n\"502\",\"gesturedetector\"\n\"502\",\"office365-apps\"\n\"502\",\"mpi4py\"\n\"502\",\"stdclass\"\n\"501\",\"manpage\"\n\"501\",\"admin-on-rest\"\n\"501\",\"cck\"\n\"501\",\"unsatisfiedlinkerror\"\n\"501\",\"ajax4jsf\"\n\"501\",\"fasterxml\"\n\"501\",\"gs-vlookup\"\n\"501\",\"mod-security\"\n\"501\",\"synonym\"\n\"501\",\"shap\"\n\"501\",\"dotnet-cli\"\n\"501\",\"color-space\"\n\"501\",\"flutter-plugin\"\n\"501\",\"static-code-analysis\"\n\"501\",\"email-templates\"\n\"500\",\"multipeer-connectivity\"\n\"500\",\"ef-core-2.1\"\n\"500\",\"eclipse-emf\"\n\"500\",\"phabricator\"\n\"500\",\"x-frame-options\"\n\"500\",\"apostrophe\"\n\"500\",\"sendgrid-api-v3\"\n\"500\",\"wsimport\"\n\"500\",\"rdfs\"\n\"500\",\"toggleclass\"\n\"500\",\"polly\"\n\"500\",\"peoplesoft\"\n\"500\",\"prepend\"\n\"499\",\"television\"\n\"499\",\"multiplatform\"\n\"499\",\"json-server\"\n\"499\",\"ruamel.yaml\"\n\"499\",\"onfocus\"\n\"499\",\"cox-regression\"\n\"499\",\"coupon\"\n\"499\",\"viewpagerindicator\"\n\"499\",\"dokku\"\n\"499\",\"strace\"\n\"499\",\"zurb-foundation-6\"\n\"499\",\"httplistener\"\n\"499\",\"asciidoc\"\n\"499\",\"searchkick\"\n\"499\",\"bidirectional\"\n\"498\",\"react-native-web\"\n\"498\",\"fltk\"\n\"498\",\"dealloc\"\n\"498\",\"friendly-id\"\n\"498\",\"dataprovider\"\n\"498\",\"mgcv\"\n\"498\",\"invisible\"\n\"498\",\"r-factor\"\n\"498\",\"reticulate\"\n\"498\",\"sprite-sheet\"\n\"498\",\"asp.net-mvc-viewmodel\"\n\"498\",\"mcmc\"\n\"498\",\"fog\"\n\"498\",\"autosize\"\n\"497\",\"nextcloud\"\n\"497\",\"pulumi\"\n\"497\",\"psutil\"\n\"497\",\"ndef\"\n\"497\",\"writefile\"\n\"497\",\"cocoon-gem\"\n\"497\",\"levels\"\n\"497\",\"sql-server-agent\"\n\"497\",\"tablelayoutpanel\"\n\"497\",\"stateful\"\n\"497\",\"compiler-flags\"\n\"496\",\"tshark\"\n\"496\",\"ng-file-upload\"\n\"496\",\"pkg-config\"\n\"496\",\"jss\"\n\"496\",\"django-sessions\"\n\"496\",\"pci\"\n\"496\",\"delaunay\"\n\"496\",\"domxpath\"\n\"496\",\"android-annotations\"\n\"496\",\"formgroups\"\n\"496\",\"type-systems\"\n\"496\",\"np\"\n\"496\",\"actionview\"\n\"496\",\"spotipy\"\n\"496\",\"angular17\"\n\"496\",\"bho\"\n\"496\",\"artifact\"\n\"496\",\"tikz\"\n\"495\",\"skiasharp\"\n\"495\",\"kurento\"\n\"495\",\"facebook-prophet\"\n\"495\",\"aws-dms\"\n\"495\",\"t-test\"\n\"495\",\"modelstate\"\n\"495\",\"libtool\"\n\"495\",\"refinerycms\"\n\"495\",\"mjpeg\"\n\"494\",\"basex\"\n\"494\",\"fgetcsv\"\n\"494\",\"page-break\"\n\"494\",\"rcharts\"\n\"494\",\"rdl\"\n\"494\",\"android-billing\"\n\"494\",\"uirefreshcontrol\"\n\"494\",\"google-text-to-speech\"\n\"493\",\"xmlwriter\"\n\"493\",\"akka-cluster\"\n\"493\",\"ruby-1.9.3\"\n\"493\",\"android-snackbar\"\n\"493\",\"code-signing-certificate\"\n\"493\",\"bubble-chart\"\n\"493\",\"java-3d\"\n\"493\",\"assimp\"\n\"493\",\"health-monitoring\"\n\"493\",\"having-clause\"\n\"492\",\"intel-fpga\"\n\"492\",\"function-declaration\"\n\"492\",\"wchar-t\"\n\"492\",\"joystick\"\n\"492\",\"angular-meteor\"\n\"492\",\"gstreamer-1.0\"\n\"492\",\"pdfminer\"\n\"492\",\"delphi-xe8\"\n\"492\",\"azure-webjobssdk\"\n\"492\",\"reduction\"\n\"492\",\"nvarchar\"\n\"492\",\"tizen-web-app\"\n\"492\",\"strict\"\n\"492\",\"google-earth-plugin\"\n\"492\",\"custom-font\"\n\"492\",\"berkeley-db\"\n\"492\",\"theorem-proving\"\n\"491\",\"separation-of-concerns\"\n\"491\",\"jprogressbar\"\n\"491\",\"coverage.py\"\n\"491\",\"two-factor-authentication\"\n\"491\",\"rate\"\n\"491\",\"lucee\"\n\"491\",\"normals\"\n\"491\",\"drop\"\n\"491\",\"history.js\"\n\"491\",\"angular2-changedetection\"\n\"490\",\"unity3d-2dtools\"\n\"490\",\"chrome-web-store\"\n\"490\",\"plaintext\"\n\"490\",\"blackberry-jde\"\n\"490\",\"pager\"\n\"490\",\"agent-based-modeling\"\n\"490\",\"mongodb-compass\"\n\"490\",\"swagger-editor\"\n\"490\",\"buildpack\"\n\"490\",\"javacc\"\n\"490\",\"number-theory\"\n\"490\",\"scipy-optimize-minimize\"\n\"490\",\"resizable\"\n\"490\",\"heap-dump\"\n\"489\",\"relational-division\"\n\"489\",\"pkcs#12\"\n\"489\",\"magrittr\"\n\"489\",\"objdump\"\n\"489\",\"poltergeist\"\n\"489\",\"stringbuffer\"\n\"489\",\"stroke\"\n\"488\",\"dcg\"\n\"488\",\"cascadingdropdown\"\n\"488\",\"symfony-3.3\"\n\"488\",\"icommand\"\n\"488\",\"pwm\"\n\"488\",\"eps\"\n\"488\",\"8051\"\n\"488\",\"form-control\"\n\"488\",\"objectid\"\n\"488\",\"npm-package\"\n\"488\",\"elasticsearch-painless\"\n\"488\",\"shebang\"\n\"487\",\"candlestick-chart\"\n\"487\",\"fault\"\n\"487\",\"document.write\"\n\"487\",\"desktop-bridge\"\n\"487\",\"android-studio-2.2\"\n\"487\",\"twincat\"\n\"487\",\"pysnmp\"\n\"487\",\"point-of-sale\"\n\"487\",\"numerical\"\n\"487\",\"cstring\"\n\"487\",\"estimation\"\n\"487\",\"dacpac\"\n\"487\",\"msvcrt\"\n\"486\",\"grok\"\n\"486\",\"ggvis\"\n\"486\",\"slick-3.0\"\n\"486\",\"mamp-pro\"\n\"486\",\"survival\"\n\"486\",\"libc++\"\n\"486\",\"winui\"\n\"486\",\"game-loop\"\n\"486\",\"dreamhost\"\n\"486\",\"uiapplication\"\n\"486\",\"jags\"\n\"486\",\"project-structure\"\n\"486\",\"flutter-packages\"\n\"486\",\"usbserial\"\n\"485\",\"ecmascript-2016\"\n\"485\",\"grizzly\"\n\"485\",\"paas\"\n\"485\",\"xmlnode\"\n\"485\",\"spring-retry\"\n\"485\",\"hot-reload\"\n\"485\",\"azure-devops-extensions\"\n\"485\",\"brightness\"\n\"485\",\"asp.net-authorization\"\n\"485\",\"node-mongodb-native\"\n\"485\",\"perceptron\"\n\"485\",\"hubspot\"\n\"485\",\"tidyeval\"\n\"485\",\"mechanicalturk\"\n\"484\",\"primeng-datatable\"\n\"484\",\"truststore\"\n\"484\",\"addthis\"\n\"484\",\"data-augmentation\"\n\"484\",\"microprocessors\"\n\"484\",\"ring\"\n\"484\",\"android-2.3-gingerbread\"\n\"484\",\"rails-postgresql\"\n\"484\",\"gearman\"\n\"484\",\"higher-order-components\"\n\"484\",\"persistent-storage\"\n\"483\",\"slideup\"\n\"483\",\"jdom\"\n\"483\",\"processing.js\"\n\"483\",\"jenkins-declarative-pipeline\"\n\"483\",\"weighted-average\"\n\"483\",\"xssf\"\n\"483\",\"jcl\"\n\"483\",\"laravel-dusk\"\n\"483\",\"android-sdk-manager\"\n\"483\",\"ocmock\"\n\"483\",\"digital-certificate\"\n\"483\",\"loopback4\"\n\"483\",\"quantization\"\n\"482\",\"vue-props\"\n\"482\",\"jmsserializerbundle\"\n\"482\",\"onkeyup\"\n\"482\",\"android-sdk-2.3\"\n\"482\",\"gtk2\"\n\"482\",\"jquery-dialog\"\n\"482\",\"infinity\"\n\"482\",\"doparallel\"\n\"482\",\"side-effects\"\n\"482\",\"legacy-code\"\n\"482\",\"iterable-unpacking\"\n\"482\",\"ceph\"\n\"482\",\"qtwebengine\"\n\"481\",\"babylonjs\"\n\"481\",\"laravel-cashier\"\n\"481\",\"fasttext\"\n\"481\",\"dna-sequence\"\n\"481\",\"opcode\"\n\"481\",\"wordpress-plugin-creation\"\n\"481\",\"mismatch\"\n\"481\",\"itemrenderer\"\n\"481\",\"eve\"\n\"481\",\"euler-angles\"\n\"481\",\"etcd\"\n\"481\",\"evaluate\"\n\"481\",\"flutter-sliver\"\n\"481\",\"autobahn\"\n\"480\",\"dll-injection\"\n\"480\",\"dock\"\n\"480\",\"dynamic-library\"\n\"480\",\"turing-machines\"\n\"480\",\"twebbrowser\"\n\"480\",\"pyopenssl\"\n\"480\",\"format-specifiers\"\n\"480\",\"jaws-screen-reader\"\n\"480\",\"memory-efficient\"\n\"480\",\"passbook\"\n\"480\",\"google-polyline\"\n\"480\",\"heap-corruption\"\n\"479\",\"flood-fill\"\n\"479\",\"nsdocument\"\n\"479\",\"mondrian\"\n\"479\",\"inverse\"\n\"479\",\"boundary\"\n\"479\",\"suspend\"\n\"479\",\"ripple\"\n\"479\",\"express-handlebars\"\n\"479\",\"executor\"\n\"479\",\"spark-graphx\"\n\"479\",\"dart-html\"\n\"479\",\"static-assert\"\n\"478\",\"reindex\"\n\"478\",\"ftdi\"\n\"478\",\"sharp\"\n\"478\",\"famo.us\"\n\"478\",\"denormalization\"\n\"478\",\"mojarra\"\n\"478\",\"pong\"\n\"478\",\"sieve-of-eratosthenes\"\n\"478\",\"outline\"\n\"478\",\"asp.net-mvc-5.2\"\n\"478\",\"rails-activejob\"\n\"478\",\"textselection\"\n\"478\",\"genexus\"\n\"478\",\"qdialog\"\n\"478\",\"power-management\"\n\"477\",\"eclipse-kepler\"\n\"477\",\"data-storage\"\n\"477\",\"pam\"\n\"477\",\"aws-documentdb\"\n\"477\",\"inputbox\"\n\"477\",\"rascal\"\n\"477\",\"vertices\"\n\"477\",\"google-slides\"\n\"476\",\"sqlxml\"\n\"476\",\"app-engine-flexible\"\n\"476\",\"python-mock\"\n\"476\",\"unnest\"\n\"476\",\"nsnotifications\"\n\"476\",\"magento-1.6\"\n\"476\",\"express-validator\"\n\"476\",\"abstract-data-type\"\n\"476\",\"sqlite-net\"\n\"476\",\"autodoc\"\n\"476\",\"il\"\n\"475\",\"filechooser\"\n\"475\",\"class-variables\"\n\"475\",\"multiprocess\"\n\"475\",\"chromebook\"\n\"475\",\"meteorite\"\n\"475\",\"google-tv\"\n\"475\",\"nested-routes\"\n\"475\",\"building\"\n\"475\",\"orc\"\n\"475\",\"revision\"\n\"475\",\"mitmproxy\"\n\"475\",\"ui-grid\"\n\"475\",\"screensaver\"\n\"475\",\"gem5\"\n\"475\",\"tokbox\"\n\"475\",\"memory-model\"\n\"475\",\"monkeyrunner\"\n\"475\",\"flutter-go-router\"\n\"474\",\"yolov8\"\n\"474\",\"marklogic-9\"\n\"474\",\"page-lifecycle\"\n\"474\",\"jsrender\"\n\"474\",\"carthage\"\n\"474\",\"ruby-on-rails-4.1\"\n\"474\",\"ruby-2.0\"\n\"474\",\"jquery-tools\"\n\"474\",\"aapt\"\n\"474\",\"short-circuiting\"\n\"474\",\"stun\"\n\"474\",\"precompile\"\n\"473\",\"opl\"\n\"473\",\"ptrace\"\n\"473\",\"pci-e\"\n\"473\",\"java-ee-8\"\n\"473\",\"fork-join\"\n\"473\",\"mac-app-store\"\n\"473\",\"nettcpbinding\"\n\"473\",\"largenumber\"\n\"473\",\"last-modified\"\n\"473\",\"rerender\"\n\"473\",\"start-activity\"\n\"472\",\"weekday\"\n\"472\",\"eclipse-pde\"\n\"472\",\"matcher\"\n\"472\",\"multimedia\"\n\"472\",\"blackjack\"\n\"472\",\"createfile\"\n\"472\",\"k3s\"\n\"472\",\"samesite\"\n\"472\",\"typo3-10.x\"\n\"472\",\"presentation\"\n\"472\",\"alpha-transparency\"\n\"472\",\"zend-studio\"\n\"471\",\"file-manager\"\n\"471\",\"catia\"\n\"471\",\"checkedlistbox\"\n\"471\",\"rubocop\"\n\"471\",\"hstore\"\n\"471\",\"dyld\"\n\"471\",\"hsv\"\n\"471\",\"f#-interactive\"\n\"471\",\"dialogfragment\"\n\"471\",\"reference-counting\"\n\"471\",\"react-google-maps\"\n\"471\",\"zendesk\"\n\"470\",\"mathml\"\n\"470\",\"matlab-compiler\"\n\"470\",\"slash\"\n\"470\",\"fixed-width\"\n\"470\",\"smpp\"\n\"470\",\"manim\"\n\"470\",\"imessage\"\n\"470\",\"htmlspecialchars\"\n\"470\",\"sha512\"\n\"470\",\"asp.net-apicontroller\"\n\"470\",\"poker\"\n\"470\",\"polygons\"\n\"470\",\"react-final-form\"\n\"470\",\"submission\"\n\"470\",\"statements\"\n\"470\",\"linker-scripts\"\n\"469\",\"intercept\"\n\"469\",\"imaging\"\n\"469\",\"capybara-webkit\"\n\"469\",\"ucanaccess\"\n\"469\",\"vgg-net\"\n\"469\",\"ipod-touch\"\n\"469\",\"amazon-data-pipeline\"\n\"469\",\"mrtk\"\n\"468\",\"eclipse-luna\"\n\"468\",\"git-filter-branch\"\n\"468\",\"ssrs-2016\"\n\"468\",\"jsessionid\"\n\"468\",\"imagepicker\"\n\"468\",\"confirmation\"\n\"468\",\"kdtree\"\n\"468\",\"internet-connection\"\n\"468\",\"onelogin\"\n\"468\",\"macos-monterey\"\n\"468\",\"caanimation\"\n\"468\",\"azure-static-web-app\"\n\"468\",\"strophe\"\n\"468\",\"google-cloud-datalab\"\n\"468\",\"pytorch-dataloader\"\n\"468\",\"use-reducer\"\n\"467\",\"anychart\"\n\"467\",\"grails-controller\"\n\"467\",\"angularjs-ng-include\"\n\"467\",\"input-field\"\n\"467\",\"code-splitting\"\n\"467\",\"jackson2\"\n\"467\",\"gauge\"\n\"467\",\"mobilefirst-server\"\n\"467\",\"game-maker\"\n\"467\",\"cfc\"\n\"467\",\"spatstat\"\n\"467\",\"autofocus\"\n\"466\",\"webpack-style-loader\"\n\"466\",\"localtime\"\n\"466\",\"data-retrieval\"\n\"466\",\"postman-collection-runner\"\n\"466\",\"openresty\"\n\"466\",\"laravel-queue\"\n\"466\",\"queryselector\"\n\"466\",\"su\"\n\"465\",\"laravel-filament\"\n\"465\",\"pandas-datareader\"\n\"465\",\"jsr223\"\n\"465\",\"openerp-8\"\n\"464\",\"skmaps\"\n\"464\",\"map-projections\"\n\"464\",\"cherry-pick\"\n\"464\",\"django-middleware\"\n\"464\",\"rss-reader\"\n\"464\",\"facebook-wall\"\n\"464\",\"onpause\"\n\"464\",\"blowfish\"\n\"464\",\"codepages\"\n\"464\",\"dotnetzip\"\n\"463\",\"ddd-repositories\"\n\"463\",\"clisp\"\n\"463\",\"apache-spark-2.0\"\n\"463\",\"hour\"\n\"463\",\"frama-c\"\n\"463\",\"redundancy\"\n\"463\",\"geckofx\"\n\"463\",\"dimensional-modeling\"\n\"463\",\"nodatime\"\n\"463\",\"react-chartjs-2\"\n\"463\",\"haskell-lens\"\n\"463\",\"google-slides-api\"\n\"462\",\"fedex\"\n\"462\",\"xquery-sql\"\n\"462\",\"plantuml\"\n\"462\",\"wallet\"\n\"462\",\"mezzanine\"\n\"462\",\"depth-buffer\"\n\"462\",\"intervention\"\n\"462\",\"writer\"\n\"462\",\"spring-scheduled\"\n\"462\",\"motionevent\"\n\"462\",\"react-highcharts\"\n\"462\",\"layoutparams\"\n\"462\",\"precompiled-headers\"\n\"462\",\"foreman\"\n\"461\",\"cbind\"\n\"461\",\"managed-c++\"\n\"461\",\"html-webpack-plugin\"\n\"461\",\"portrait\"\n\"461\",\"pocketsphinx\"\n\"461\",\"excel-charts\"\n\"460\",\"feature-engineering\"\n\"460\",\"integer-arithmetic\"\n\"460\",\"ngx-datatable\"\n\"460\",\"nextflow\"\n\"460\",\"uniq\"\n\"460\",\"urldecode\"\n\"460\",\"django-import-export\"\n\"460\",\"pure-virtual\"\n\"460\",\"intl\"\n\"460\",\"bootbox\"\n\"460\",\"erc20\"\n\"460\",\"rdflib\"\n\"460\",\"cockroachdb\"\n\"460\",\"asynccallback\"\n\"460\",\"download-manager\"\n\"460\",\"hoisting\"\n\"460\",\"sqldependency\"\n\"460\",\"android-intentservice\"\n\"460\",\"nswag\"\n\"460\",\"commandbutton\"\n\"460\",\"testcomplete\"\n\"460\",\"actionresult\"\n\"460\",\"linear-interpolation\"\n\"460\",\"iif\"\n\"460\",\"folding\"\n\"459\",\"graphic\"\n\"459\",\"filehelpers\"\n\"459\",\"wildcard-subdomain\"\n\"459\",\"calc\"\n\"459\",\"razor-2\"\n\"459\",\"mink\"\n\"459\",\"letter\"\n\"459\",\"nstask\"\n\"459\",\"logrotate\"\n\"459\",\"android-keypad\"\n\"459\",\"ios16\"\n\"459\",\"etw\"\n\"459\",\"laravel-jetstream\"\n\"459\",\"google-smart-home\"\n\"458\",\"pry\"\n\"458\",\"supabase-database\"\n\"458\",\"attached-properties\"\n\"458\",\"tabitem\"\n\"458\",\"today-extension\"\n\"458\",\"scheduledexecutorservice\"\n\"458\",\"openni\"\n\"458\",\"lasso-regression\"\n\"458\",\"proximity\"\n\"458\",\"electron-forge\"\n\"457\",\"ffprobe\"\n\"457\",\"clang-tidy\"\n\"457\",\"django-tests\"\n\"457\",\"aws-batch\"\n\"457\",\"dynamics-crm-365\"\n\"457\",\"blockingqueue\"\n\"457\",\"playwright-test\"\n\"457\",\"okta-api\"\n\"457\",\"lightbox2\"\n\"457\",\"userid\"\n\"456\",\"content-disposition\"\n\"456\",\"froala\"\n\"456\",\"cancancan\"\n\"456\",\"windows-sharepoint-services\"\n\"456\",\"recipe\"\n\"456\",\"native-code\"\n\"456\",\"aggregateroot\"\n\"456\",\"boost-serialization\"\n\"456\",\"kafka-python\"\n\"456\",\"net-http\"\n\"456\",\"extentreports\"\n\"456\",\"google-cloud-data-fusion\"\n\"456\",\"q-learning\"\n\"456\",\"google-container-registry\"\n\"456\",\"nightmare\"\n\"455\",\"phasset\"\n\"455\",\"phpbb3\"\n\"455\",\"catalog\"\n\"455\",\"castle-activerecord\"\n\"455\",\"doctest\"\n\"455\",\"service-reference\"\n\"455\",\"scaffold\"\n\"455\",\"visualsvn\"\n\"455\",\"directions\"\n\"455\",\"nodelist\"\n\"455\",\"monotouch.dialog\"\n\"455\",\"node-fetch\"\n\"455\",\"darknet\"\n\"455\",\"zipkin\"\n\"454\",\"date-difference\"\n\"454\",\"pan\"\n\"454\",\"json4s\"\n\"454\",\"unordered-set\"\n\"454\",\"android-developer-api\"\n\"454\",\"oledbcommand\"\n\"454\",\"android-layout-weight\"\n\"454\",\"google-fonts\"\n\"454\",\"google-openid\"\n\"453\",\"in-memory\"\n\"453\",\"jxl\"\n\"452\",\"jconsole\"\n\"452\",\"mvcsitemapprovider\"\n\"452\",\"selectors-api\"\n\"452\",\"airplay\"\n\"452\",\"creation\"\n\"452\",\"optionmenu\"\n\"452\",\"country\"\n\"452\",\"entry-point\"\n\"452\",\"aql\"\n\"452\",\"atg\"\n\"452\",\"fragmenttransaction\"\n\"452\",\"esp-idf\"\n\"452\",\"angular-formly\"\n\"452\",\"angular-flex-layout\"\n\"452\",\"cgpath\"\n\"452\",\"touchesbegan\"\n\"452\",\"array-map\"\n\"451\",\"jboss-eap-7\"\n\"451\",\"mysql-5.6\"\n\"451\",\"punctuation\"\n\"451\",\"viewchild\"\n\"451\",\"visual-studio-addins\"\n\"451\",\"netflix\"\n\"451\",\"azure-openai\"\n\"451\",\"radius\"\n\"451\",\"elastic-map-reduce\"\n\"451\",\"speech-synthesis\"\n\"451\",\"stm32cubeide\"\n\"451\",\"stdarray\"\n\"451\",\"pre-commit\"\n\"451\",\"gmt\"\n\"450\",\"ddev\"\n\"450\",\"window-managers\"\n\"450\",\"single-responsibility-principle\"\n\"450\",\"recurring-billing\"\n\"450\",\"pdi\"\n\"450\",\"azure-cdn\"\n\"450\",\"azure-cosmosdb-mongoapi\"\n\"450\",\"ws-federation\"\n\"450\",\"corrupt\"\n\"450\",\"ionic-view\"\n\"450\",\"wix3.7\"\n\"450\",\"android-graphview\"\n\"450\",\"azure-functions-runtime\"\n\"450\",\"tls1.3\"\n\"450\",\"locks\"\n\"450\",\"peerjs\"\n\"450\",\"centos8\"\n\"450\",\"mediator\"\n\"450\",\"mbed\"\n\"450\",\"linode\"\n\"450\",\"google-mirror-api\"\n\"449\",\"frp\"\n\"449\",\"filepicker\"\n\"449\",\"simulate\"\n\"449\",\"vbulletin\"\n\"449\",\"uistatusbar\"\n\"449\",\"uiappearance\"\n\"449\",\"v4l2\"\n\"448\",\"maven-archetype\"\n\"448\",\"gravityforms\"\n\"448\",\"ssis-2008\"\n\"448\",\"sharekit\"\n\"448\",\"react-spring\"\n\"448\",\"fat-free-framework\"\n\"448\",\"vibration\"\n\"448\",\"rasa-core\"\n\"448\",\"strong-typing\"\n\"447\",\"finalizer\"\n\"447\",\"rpart\"\n\"447\",\"intune\"\n\"447\",\"application.properties\"\n\"447\",\"easyadmin\"\n\"447\",\"radgridview\"\n\"447\",\"sceneform\"\n\"447\",\"sql-scripts\"\n\"447\",\"android-motionlayout\"\n\"447\",\"parallels\"\n\"447\",\"font-awesome-5\"\n\"446\",\"anomaly-detection\"\n\"446\",\"locationlistener\"\n\"446\",\"shared-element-transition\"\n\"446\",\"alerts\"\n\"446\",\"rollover\"\n\"446\",\"crashlytics-android\"\n\"446\",\"monetdb\"\n\"446\",\"visual-c++-6\"\n\"446\",\"nullable-reference-types\"\n\"446\",\"openxlsx\"\n\"446\",\"angularjs-factory\"\n\"446\",\"getopts\"\n\"446\",\"using-statement\"\n\"446\",\"google-nativeclient\"\n\"445\",\"adminlte\"\n\"445\",\"vs-extensibility\"\n\"445\",\"kubernetes-secrets\"\n\"445\",\"formidable\"\n\"445\",\".net-4.6\"\n\"445\",\"visual-web-developer\"\n\"445\",\"libjpeg\"\n\"445\",\"azure-queues\"\n\"445\",\"xcode13\"\n\"445\",\"asp.net-webpages\"\n\"445\",\"esbuild\"\n\"445\",\"event-bus\"\n\"445\",\"preference\"\n\"445\",\"health-check\"\n\"444\",\"anti-patterns\"\n\"444\",\"nameservers\"\n\"444\",\"jradiobutton\"\n\"444\",\"ota\"\n\"444\",\"office-scripts\"\n\"444\",\"node.js-connect\"\n\"444\",\"testng-dataprovider\"\n\"444\",\"partial-classes\"\n\"444\",\"compojure\"\n\"443\",\"react-pdf\"\n\"443\",\"python-dateutil\"\n\"443\",\"kubeflow\"\n\"443\",\"aws-elasticsearch\"\n\"443\",\"sessionfactory\"\n\"443\",\"jqtouch\"\n\"443\",\"luajit\"\n\"443\",\"rasterio\"\n\"443\",\"rails-console\"\n\"443\",\"mesosphere\"\n\"443\",\"google-cloud-monitoring\"\n\"443\",\"automl\"\n\"443\",\"haxm\"\n\"443\",\"alt\"\n\"443\",\"powerbi-custom-visuals\"\n\"443\",\"mshtml\"\n\"442\",\"programmatically-created\"\n\"442\",\"roi\"\n\"442\",\"aws-msk\"\n\"442\",\"windows-scripting\"\n\"442\",\"pbkdf2\"\n\"442\",\"dust.js\"\n\"442\",\"hotchocolate\"\n\"442\",\"sasl\"\n\"442\",\"minizinc\"\n\"442\",\"bridge\"\n\"442\",\"syntactic-sugar\"\n\"442\",\"timeoutexception\"\n\"442\",\"garbage\"\n\"442\",\"uicontrol\"\n\"442\",\"railo\"\n\"442\",\"com+\"\n\"442\",\"message-driven-bean\"\n\"442\",\"linkbutton\"\n\"442\",\"sub-array\"\n\"441\",\"interactive-brokers\"\n\"441\",\"slidingdrawer\"\n\"441\",\"fips\"\n\"441\",\"voxel\"\n\"441\",\"metatrader4\"\n\"441\",\"pty\"\n\"441\",\"error-reporting\"\n\"441\",\"bug-tracking\"\n\"441\",\"ratingbar\"\n\"441\",\"lemmatization\"\n\"441\",\"xcode4.6\"\n\"441\",\"tabpanel\"\n\"441\",\"sqldataadapter\"\n\"441\",\"officer\"\n\"441\",\"vaadin-grid\"\n\"441\",\"google-play-developer-api\"\n\"440\",\"react-navigation-bottom-tab\"\n\"440\",\"termux\"\n\"440\",\"fluent-interface\"\n\"440\",\"decimal-point\"\n\"440\",\"try-catch-finally\"\n\"440\",\"slot\"\n\"440\",\"function-templates\"\n\"440\",\"pundit\"\n\"440\",\"android-screen-support\"\n\"440\",\"google-account\"\n\"440\",\"legend-properties\"\n\"440\",\"aspect\"\n\"440\",\"google-finance\"\n\"440\",\"monit\"\n\"440\",\"chart.js2\"\n\"440\",\"zipcode\"\n\"440\",\"avatar\"\n\"440\",\"zend-db-table\"\n\"439\",\"mxgraph\"\n\"439\",\"clearcase-ucm\"\n\"439\",\"kubernetes-service\"\n\"439\",\"calibration\"\n\"439\",\"hp-quality-center\"\n\"439\",\"diffie-hellman\"\n\"439\",\"requestfactory\"\n\"439\",\"zedgraph\"\n\"439\",\"solidworks\"\n\"439\",\"stdtuple\"\n\"438\",\"keyvaluepair\"\n\"438\",\"kendo-chart\"\n\"438\",\"optimistic-locking\"\n\"438\",\"grpc-python\"\n\"438\",\"htmlwidgets\"\n\"438\",\"appstore-sandbox\"\n\"438\",\"sapi\"\n\"438\",\"rigid-bodies\"\n\"438\",\"synthesis\"\n\"438\",\"ampersand\"\n\"438\",\"go-ethereum\"\n\"438\",\"setfocus\"\n\"438\",\"dir\"\n\"438\",\"androiddesignsupport\"\n\"438\",\"digest\"\n\"438\",\"opengl-4\"\n\"438\",\"continuous\"\n\"438\",\"esxi\"\n\"438\",\"web-analytics\"\n\"438\",\"haversine\"\n\"438\",\"lidar\"\n\"437\",\"greatest-common-divisor\"\n\"437\",\"dispatch-async\"\n\"437\",\"modelmapper\"\n\"437\",\"ns-3\"\n\"437\",\"atoi\"\n\"437\",\"v-model\"\n\"437\",\"retry-logic\"\n\"437\",\"jaeger\"\n\"437\",\"sql-agent-job\"\n\"437\",\"preflight\"\n\"437\",\"tflearn\"\n\"437\",\"vaadin14\"\n\"436\",\"recyclerview-layout\"\n\"436\",\"hyperledger-chaincode\"\n\"436\",\"dask-dataframe\"\n\"436\",\"ringtone\"\n\"436\",\"misra\"\n\"436\",\"mirroring\"\n\"436\",\"ab-testing\"\n\"436\",\"dft\"\n\"436\",\"android-jetpack-compose-material3\"\n\"436\",\"perlin-noise\"\n\"436\",\"glkit\"\n\"436\",\"spacebars\"\n\"435\",\"fluent-ui\"\n\"435\",\"yii-components\"\n\"435\",\"find-occurrences\"\n\"435\",\"gprs\"\n\"435\",\"sass-loader\"\n\"435\",\"grunt-contrib-watch\"\n\"435\",\"hashlib\"\n\"435\",\"expansion\"\n\"435\",\"activexobject\"\n\"435\",\"bayesian-networks\"\n\"435\",\"bem\"\n\"435\",\"tpu\"\n\"435\",\"autodesk-designautomation\"\n\"434\",\"decrement\"\n\"434\",\"dataloader\"\n\"434\",\"jtds\"\n\"434\",\"app-router\"\n\"434\",\"posix-select\"\n\"434\",\"scala-gatling\"\n\"434\",\"gojs\"\n\"434\",\"kryo\"\n\"434\",\"associative\"\n\"434\",\"j\"\n\"434\",\"c++23\"\n\"434\",\"cursor-position\"\n\"434\",\"google-places-autocomplete\"\n\"433\",\"jce\"\n\"433\",\"llama\"\n\"433\",\"datomic\"\n\"433\",\"ibm-doors\"\n\"433\",\"oracle11gr2\"\n\"433\",\"jmh\"\n\"433\",\"azure-batch\"\n\"433\",\"tx-news\"\n\"433\",\"nested-if\"\n\"433\",\"ractivejs\"\n\"433\",\"mpeg\"\n\"433\",\"emf\"\n\"433\",\"precision-recall\"\n\"432\",\"skin\"\n\"432\",\"distributed-caching\"\n\"432\",\"fusedlocationproviderapi\"\n\"432\",\"dataadapter\"\n\"432\",\"angular-validation\"\n\"432\",\"pbs\"\n\"432\",\"ensemble-learning\"\n\"432\",\"rijndael\"\n\"432\",\"build-tools\"\n\"432\",\"setattribute\"\n\"432\",\"angular2-aot\"\n\"432\",\"custom-renderer\"\n\"432\",\"android-navigationview\"\n\"432\",\"beagleboard\"\n\"432\",\"gnat\"\n\"432\",\"autologin\"\n\"432\",\"embedded-database\"\n\"431\",\"multinomial\"\n\"431\",\"class-validator\"\n\"431\",\"transpiler\"\n\"431\",\"physics-engine\"\n\"431\",\"xml-signature\"\n\"431\",\"bitbucket-api\"\n\"431\",\"fast-ai\"\n\"431\",\"navision\"\n\"431\",\"quickcheck\"\n\"431\",\"office-ui-fabric\"\n\"430\",\"category-theory\"\n\"430\",\"rust-diesel\"\n\"430\",\"datetime-parsing\"\n\"430\",\"myeclipse\"\n\"430\",\"savechanges\"\n\"430\",\"pydub\"\n\"430\",\"objective-c-category\"\n\"430\",\"spy\"\n\"430\",\"azure-repos\"\n\"430\",\"motorola\"\n\"429\",\"fuseki\"\n\"429\",\"kudu\"\n\"429\",\"ruby-1.9\"\n\"429\",\"variable-length-array\"\n\"429\",\"derived\"\n\"429\",\"spring-data-r2dbc\"\n\"429\",\"border-layout\"\n\"429\",\"jvectormap\"\n\"429\",\"jung\"\n\"429\",\"typesafe-activator\"\n\"429\",\"minidom\"\n\"429\",\"rangeslider\"\n\"429\",\"geany\"\n\"429\",\"google-directory-api\"\n\"429\",\"spatial-query\"\n\"429\",\"metalkit\"\n\"429\",\"event-driven\"\n\"429\",\"text-analysis\"\n\"429\",\"parser-combinators\"\n\"429\",\"subset-sum\"\n\"428\",\"case-statement\"\n\"428\",\"xen\"\n\"428\",\"aes-gcm\"\n\"428\",\"ibm-integration-bus\"\n\"428\",\"django-permissions\"\n\"428\",\"microsoft-entra-id\"\n\"428\",\"mysql-json\"\n\"428\",\"delphi-11-alexandria\"\n\"428\",\"nscollectionview\"\n\"428\",\"virtual-inheritance\"\n\"428\",\"actioncontroller\"\n\"428\",\"movie\"\n\"428\",\"android-pageradapter\"\n\"428\",\"mdf\"\n\"427\",\"yodlee\"\n\"427\",\"floor\"\n\"427\",\"vs-web-site-project\"\n\"427\",\"vmware-workstation\"\n\"427\",\"disabled-input\"\n\"427\",\"function-composition\"\n\"427\",\"azure-api-apps\"\n\"427\",\"jquery-tabs\"\n\"427\",\"android-input-method\"\n\"427\",\"android-debug\"\n\"427\",\"rabbitmq-exchange\"\n\"427\",\"gdbserver\"\n\"427\",\"petapoco\"\n\"427\",\"gets\"\n\"427\",\"throughput\"\n\"427\",\"webgl2\"\n\"427\",\"asciidoctor\"\n\"426\",\"xmlpullparser\"\n\"426\",\"biztalk-2013\"\n\"426\",\"rtc\"\n\"426\",\"metro-bundler\"\n\"426\",\"katana\"\n\"426\",\"newtons-method\"\n\"426\",\"facebook-insights\"\n\"426\",\"view-helpers\"\n\"426\",\"angularjs-http\"\n\"426\",\"linphone\"\n\"425\",\"deeplearning4j\"\n\"425\",\"plane\"\n\"425\",\"vscode-snippets\"\n\"425\",\"django-apps\"\n\"425\",\"p2\"\n\"425\",\"binutils\"\n\"425\",\"push-back\"\n\"425\",\"canonical-link\"\n\"425\",\"hmvc\"\n\"425\",\"low-latency\"\n\"425\",\"move-constructor\"\n\"425\",\"tfrecord\"\n\"425\",\"concourse\"\n\"425\",\"train-test-split\"\n\"424\",\"backtrace\"\n\"424\",\"fbo\"\n\"424\",\"llvm-c++-api\"\n\"424\",\"manjaro\"\n\"424\",\"sentinel\"\n\"424\",\"mako\"\n\"424\",\"service-locator\"\n\"424\",\"wpml\"\n\"424\",\"internet-radio\"\n\"424\",\"audiotrack\"\n\"424\",\"newman\"\n\"424\",\"netfilter\"\n\"424\",\"winhttp\"\n\"424\",\"brightscript\"\n\"424\",\"openquery\"\n\"424\",\"msgpack\"\n\"424\",\"theming\"\n\"423\",\"cloudflare-workers\"\n\"423\",\"chunked-encoding\"\n\"423\",\"django-2.0\"\n\"423\",\"jinternalframe\"\n\"423\",\"google-vr\"\n\"423\",\"aws-lambda-layers\"\n\"423\",\"grpc-go\"\n\"423\",\"vmware-clarity\"\n\"423\",\"16-bit\"\n\"423\",\"build-system\"\n\"423\",\"plink\"\n\"423\",\"contract\"\n\"423\",\"python-zipfile\"\n\"423\",\"perspectivecamera\"\n\"422\",\"xpath-1.0\"\n\"422\",\"fseek\"\n\"422\",\"ccsprite\"\n\"422\",\"camelcasing\"\n\"422\",\"windows-media-player\"\n\"422\",\"microsoft-fakes\"\n\"422\",\"pdf-viewer\"\n\"422\",\"mod-jk\"\n\"422\",\"wpf-4.0\"\n\"422\",\"octal\"\n\"422\",\"frameset\"\n\"422\",\"javascriptserializer\"\n\"422\",\"motion-detection\"\n\"422\",\"cups\"\n\"422\",\"mousedown\"\n\"422\",\"react-dnd\"\n\"422\",\"startswith\"\n\"421\",\"gitkraken\"\n\"421\",\"matplotlib-animation\"\n\"421\",\"directshow.net\"\n\"421\",\"picasa\"\n\"421\",\"xps\"\n\"421\",\"rmysql\"\n\"421\",\"grails-3.0\"\n\"421\",\"kendo-mobile\"\n\"421\",\"spring-micrometer\"\n\"421\",\"nrwl\"\n\"421\",\"ampl\"\n\"421\",\"donut-chart\"\n\"421\",\"scip\"\n\"421\",\"responsiveness\"\n\"421\",\"login-script\"\n\"421\",\"quadratic\"\n\"421\",\"maven-dependency-plugin\"\n\"421\",\"stubbing\"\n\"420\",\"ansible-awx\"\n\"420\",\"vue-reactivity\"\n\"420\",\"undefined-symbol\"\n\"420\",\"circular-buffer\"\n\"420\",\"flash-media-server\"\n\"420\",\"nested-function\"\n\"420\",\"spring-mongodb\"\n\"420\",\"turbo-c++\"\n\"420\",\"turn\"\n\"420\",\"pyrocms\"\n\"420\",\"forgot-password\"\n\"420\",\"mobile-browser\"\n\"420\",\"uima\"\n\"420\",\"spinnaker\"\n\"420\",\"gephi\"\n\"420\",\"google-nearby\"\n\"419\",\"react-native-webview\"\n\"419\",\"stan\"\n\"419\",\"vue-chartjs\"\n\"419\",\"vscode-devcontainer\"\n\"419\",\"phylogeny\"\n\"419\",\"flake8\"\n\"419\",\"swingx\"\n\"419\",\"shared-objects\"\n\"419\",\"siri\"\n\"419\",\"design-time\"\n\"419\",\"vlcj\"\n\"419\",\"minesweeper\"\n\"419\",\"azure-vm-scale-set\"\n\"419\",\"dimensionality-reduction\"\n\"419\",\"meta-boxes\"\n\"419\",\"mel\"\n\"419\",\"bibtex\"\n\"418\",\"git-extensions\"\n\"418\",\"backbone-routing\"\n\"418\",\"whois\"\n\"418\",\"groupbox\"\n\"418\",\"rrdtool\"\n\"418\",\"spring-cloud-contract\"\n\"418\",\"open3d\"\n\"418\",\"android-service-binding\"\n\"418\",\"entitlements\"\n\"418\",\"buildpath\"\n\"418\",\"col\"\n\"418\",\"coding-efficiency\"\n\"418\",\"magento-soap-api\"\n\"418\",\"itunes-store\"\n\"418\",\"evolutionary-algorithm\"\n\"418\",\"acs\"\n\"418\",\"papaparse\"\n\"418\",\"qsqlquery\"\n\"417\",\"xtend\"\n\"417\",\"gregorian-calendar\"\n\"417\",\"slim-3\"\n\"417\",\"apache-iotdb\"\n\"417\",\"fbconnect\"\n\"417\",\"capitalization\"\n\"417\",\"superscript\"\n\"417\",\"onvif\"\n\"417\",\"windows-update\"\n\"417\",\"systems-programming\"\n\"417\",\"leveldb\"\n\"417\",\"netsh\"\n\"417\",\"httpcookie\"\n\"417\",\"message-passing\"\n\"417\",\"autorelease\"\n\"417\",\"scrollmagic\"\n\"417\",\"qt3d\"\n\"417\",\"mql5\"\n\"417\",\"power-bi-report-server\"\n\"417\",\"stimulusjs\"\n\"416\",\"transient\"\n\"416\",\"php-openssl\"\n\"416\",\"livecharts\"\n\"416\",\"language-server-protocol\"\n\"416\",\"python-importlib\"\n\"416\",\"card\"\n\"416\",\"varbinary\"\n\"416\",\"jquery-easyui\"\n\"416\",\"tizen-wearable-sdk\"\n\"416\",\"androidhttpclient\"\n\"416\",\"react-fullstack\"\n\"416\",\"getdate\"\n\"416\",\"text-formatting\"\n\"415\",\"ssrs-grouping\"\n\"415\",\"sqrt\"\n\"415\",\"git-fetch\"\n\"415\",\"telerik-reporting\"\n\"415\",\"vuetifyjs3\"\n\"415\",\"wcag\"\n\"415\",\"blueprint-osgi\"\n\"415\",\"svcutil.exe\"\n\"415\",\"output-buffering\"\n\"415\",\"model-fitting\"\n\"415\",\"xalan\"\n\"415\",\"mnesia\"\n\"415\",\"excel-tables\"\n\"415\",\"elevated-privileges\"\n\"415\",\"heidisql\"\n\"414\",\"django-socialauth\"\n\"414\",\"serverside-javascript\"\n\"414\",\"robo3t\"\n\"414\",\"keylogger\"\n\"414\",\"enterprise-guide\"\n\"414\",\"gwt2\"\n\"414\",\"node-streams\"\n\"414\",\"scnnode\"\n\"414\",\"drone.io\"\n\"414\",\"longlistselector\"\n\"414\",\"get-request\"\n\"414\",\"spread-syntax\"\n\"414\",\"google-drive-shared-drive\"\n\"414\",\"sortedlist\"\n\"413\",\"apache-commons-net\"\n\"413\",\"filehandle\"\n\"413\",\"yelp\"\n\"413\",\"fbsdk\"\n\"413\",\"fetchxml\"\n\"413\",\"squeak\"\n\"413\",\"fuzzy-logic\"\n\"413\",\"adblock\"\n\"413\",\"microk8s\"\n\"413\",\"pycuda\"\n\"413\",\"interprocess\"\n\"413\",\"azure-container-service\"\n\"413\",\"brush\"\n\"413\",\"wix3.6\"\n\"413\",\"asp.net-core-middleware\"\n\"413\",\"nsviewcontroller\"\n\"413\",\"uianimation\"\n\"413\",\"xacml\"\n\"413\",\"mpich\"\n\"413\",\"google-cloud-shell\"\n\"413\",\"storage-access-framework\"\n\"413\",\"qobject\"\n\"413\",\"preg-replace-callback\"\n\"413\",\"multi-gpu\"\n\"413\",\"qtip2\"\n\"412\",\"definitelytyped\"\n\"412\",\"translate-animation\"\n\"412\",\"bitmapdata\"\n\"412\",\"angular-service-worker\"\n\"412\",\"django-oscar\"\n\"412\",\"awesome-wm\"\n\"412\",\"htmlpurifier\"\n\"412\",\"worklight-server\"\n\"412\",\"shoutcast\"\n\"412\",\"xcode-instruments\"\n\"412\",\"table-valued-parameters\"\n\"412\",\"not-exists\"\n\"412\",\"isapi\"\n\"412\",\"touch-id\"\n\"411\",\"reminders\"\n\"411\",\"prisma-graphql\"\n\"411\",\"implements\"\n\"411\",\"firebreath\"\n\"411\",\"first-responder\"\n\"411\",\"python-3.11\"\n\"411\",\"hsm\"\n\"411\",\"superagent\"\n\"411\",\"moment-timezone\"\n\"411\",\"woocommerce-subscriptions\"\n\"411\",\"virtual-keyboard\"\n\"411\",\"openlayers-6\"\n\"411\",\"jacoco-maven-plugin\"\n\"411\",\"spread\"\n\"411\",\"computation\"\n\"411\",\"usort\"\n\"411\",\"utf8mb4\"\n\"411\",\"line-plot\"\n\"410\",\"file-association\"\n\"410\",\"voyager\"\n\"410\",\"google-webfonts\"\n\"410\",\"openacc\"\n\"410\",\"nslocalizedstring\"\n\"410\",\"sanitize\"\n\"410\",\"gedit\"\n\"410\",\"openrefine\"\n\"410\",\"haar-classifier\"\n\"410\",\"strongly-typed-dataset\"\n\"410\",\"hazelcast-imap\"\n\"410\",\"flying-saucer\"\n\"410\",\"msgbox\"\n\"410\",\"state-monad\"\n\"410\",\"url-shortener\"\n\"409\",\"tensorflow-federated\"\n\"409\",\"xpcom\"\n\"409\",\"laravel-elixir\"\n\"409\",\"uiviewanimationtransition\"\n\"409\",\"circe\"\n\"409\",\"android-search\"\n\"409\",\"build.xml\"\n\"409\",\"siddhi\"\n\"409\",\"knockout-3.0\"\n\"409\",\"hapi\"\n\"409\",\"cagradientlayer\"\n\"409\",\"qnetworkaccessmanager\"\n\"409\",\"email-headers\"\n\"409\",\"auto-indent\"\n\"409\",\"scriptlet\"\n\"409\",\"theos\"\n\"409\",\"zeep\"\n\"408\",\"eclipse-wtp\"\n\"408\",\"flash-message\"\n\"408\",\"kustomize\"\n\"408\",\"jmeter-4.0\"\n\"408\",\"awesomium\"\n\"408\",\"cppcheck\"\n\"408\",\"box2d-iphone\"\n\"408\",\"pytest-django\"\n\"408\",\"amazon-dynamodb-streams\"\n\"408\",\"iisnode\"\n\"408\",\"avalonia\"\n\"407\",\"maui-blazor\"\n\"407\",\"snakeyaml\"\n\"407\",\"filepicker.io\"\n\"407\",\"explicit\"\n\"407\",\"azure-runbook\"\n\"407\",\"protoc\"\n\"407\",\"node-oracledb\"\n\"407\",\"mongotemplate\"\n\"407\",\"ember-router\"\n\"407\",\"flutter-hive\"\n\"406\",\"dbplyr\"\n\"406\",\"ecmascript-next\"\n\"406\",\"divider\"\n\"406\",\"disconnect\"\n\"406\",\"rowcount\"\n\"406\",\"gpo\"\n\"406\",\"singlestore\"\n\"406\",\"dataimporthandler\"\n\"406\",\"azure-diagnostics\"\n\"406\",\"ambiguity\"\n\"406\",\"libuv\"\n\"406\",\"system.out\"\n\"406\",\"lettuce\"\n\"406\",\"tauri\"\n\"406\",\"tablemodel\"\n\"406\",\"dialogflow-cx\"\n\"406\",\"penetration-testing\"\n\"406\",\"quantlib\"\n\"406\",\"sunspot-rails\"\n\"406\",\"msdtc\"\n\"405\",\"jboss-weld\"\n\"405\",\"slime\"\n\"405\",\"gridlayoutmanager\"\n\"405\",\"fluent-assertions\"\n\"405\",\"frequency-analysis\"\n\"405\",\"inline-styles\"\n\"405\",\"audit-logging\"\n\"405\",\"python-webbrowser\"\n\"405\",\"require-once\"\n\"405\",\"quantum-computing\"\n\"404\",\"listobject\"\n\"404\",\"ckfinder\"\n\"404\",\"slicers\"\n\"404\",\"square-root\"\n\"404\",\"sirikit\"\n\"404\",\"cancellation-token\"\n\"404\",\"uno-platform\"\n\"404\",\"apple-sign-in\"\n\"404\",\"android-statusbar\"\n\"404\",\"attention-model\"\n\"404\",\"codeigniter-url\"\n\"404\",\"scopes\"\n\"404\",\"timeit\"\n\"404\",\"google-crawlers\"\n\"404\",\"forceclose\"\n\"404\",\"solace\"\n\"403\",\"xserver\"\n\"403\",\"github-desktop\"\n\"403\",\"discount\"\n\"403\",\"language-translation\"\n\"403\",\"placement-new\"\n\"403\",\"runge-kutta\"\n\"403\",\"wavelet\"\n\"403\",\"postal-code\"\n\"403\",\"type-constraints\"\n\"403\",\"type-alias\"\n\"403\",\"svg.js\"\n\"403\",\"java-10\"\n\"403\",\"shallow-copy\"\n\"403\",\"gadt\"\n\"403\",\"galera\"\n\"403\",\"podfile\"\n\"403\",\"assertj\"\n\"403\",\"nvidia-jetson\"\n\"403\",\"hwnd\"\n\"403\",\"quartz-core\"\n\"402\",\"cloud-hosting\"\n\"402\",\"vue-resource\"\n\"402\",\"multiple-select\"\n\"402\",\"gil\"\n\"402\",\"circuit-breaker\"\n\"402\",\"chrome-custom-tabs\"\n\"402\",\"opticalflow\"\n\"402\",\"aws-auto-scaling\"\n\"402\",\"inline-formset\"\n\"402\",\"java-security\"\n\"402\",\"coercion\"\n\"402\",\"do-loops\"\n\"402\",\"external-tables\"\n\"402\",\"showdialog\"\n\"402\",\"asp.net-routing\"\n\"402\",\"uielement\"\n\"402\",\"beanstalkd\"\n\"402\",\"pandas-styles\"\n\"402\",\"qtip\"\n\"402\",\"flutter-state\"\n\"401\",\"jelastic\"\n\"401\",\"imdb\"\n\"401\",\"owncloud\"\n\"401\",\"microsoft-graph-calendar\"\n\"401\",\"object-lifetime\"\n\"401\",\"redisson\"\n\"401\",\"cglib\"\n\"401\",\"subtotal\"\n\"401\",\"maven-jetty-plugin\"\n\"401\",\"avasset\"\n\"400\",\"php-5.4\"\n\"400\",\"backreference\"\n\"400\",\"react-navigation-drawer\"\n\"400\",\"websecurity\"\n\"400\",\"srand\"\n\"400\",\"php-ini\"\n\"400\",\"fluent-bit\"\n\"400\",\"bintray\"\n\"400\",\"checkin\"\n\"400\",\"rebus\"\n\"400\",\"row-level-security\"\n\"400\",\"keymapping\"\n\"400\",\"html-datalist\"\n\"400\",\"typeface\"\n\"400\",\"py-langchain\"\n\"400\",\"coq-tactic\"\n\"400\",\"sql-server-2012-express\"\n\"400\",\"web-essentials\"\n\"400\",\"conditional-rendering\"\n\"399\",\"xtragrid\"\n\"399\",\"dcom\"\n\"399\",\"cassandra-2.1\"\n\"399\",\"django-1.8\"\n\"399\",\"advantage-database-server\"\n\"399\",\"selectedvalue\"\n\"399\",\"configuration-management\"\n\"399\",\"sitecore-mvc\"\n\"399\",\"delphi-5\"\n\"399\",\"saprfc\"\n\"399\",\"oracle-xe\"\n\"399\",\"extjs6-classic\"\n\"399\",\"rails-geocoder\"\n\"399\",\"google-distancematrix-api\"\n\"399\",\"excel-dna\"\n\"399\",\"react-intl\"\n\"399\",\"amadeus\"\n\"399\",\"subform\"\n\"399\",\"autoboxing\"\n\"398\",\"xtable\"\n\"398\",\"sails-mongo\"\n\"398\",\"xmltype\"\n\"398\",\"opus\"\n\"398\",\"service-provider\"\n\"398\",\"in-place\"\n\"398\",\"twilio-programmable-chat\"\n\"398\",\"swashbuckle.aspnetcore\"\n\"398\",\"miglayout\"\n\"398\",\"timeserieschart\"\n\"398\",\"tag-helpers\"\n\"398\",\"tkinter-button\"\n\"398\",\"azure-sdk\"\n\"398\",\"leanback\"\n\"398\",\"google-cloud-automl\"\n\"398\",\"nodejs-server\"\n\"398\",\"ienumerator\"\n\"398\",\"autosave\"\n\"397\",\"smartcard-reader\"\n\"397\",\"srcset\"\n\"397\",\"pixelsense\"\n\"397\",\"ngmodel\"\n\"397\",\"cidr\"\n\"397\",\"chinese-locale\"\n\"397\",\"service-layer\"\n\"397\",\"venn-diagram\"\n\"397\",\"jfrog-cli\"\n\"397\",\"ruby-on-rails-5.1\"\n\"397\",\"mongodb-indexes\"\n\"397\",\"navigation-properties\"\n\"397\",\"direct3d11\"\n\"397\",\"spring-validator\"\n\"397\",\"tkinter-layout\"\n\"397\",\"uiscrollviewdelegate\"\n\"397\",\"stoppropagation\"\n\"397\",\"google-rich-snippets\"\n\"397\",\"arr\"\n\"397\",\"multibinding\"\n\"397\",\"hexdump\"\n\"396\",\"standards-compliance\"\n\"396\",\"lit\"\n\"396\",\"social-media\"\n\"396\",\"manual\"\n\"396\",\"undo-redo\"\n\"396\",\"ankhsvn\"\n\"396\",\"azure-devops-server-2019\"\n\"396\",\"applinks\"\n\"396\",\"bootstrap-multiselect\"\n\"396\",\"intersystems-cache\"\n\"396\",\"azure-analysis-services\"\n\"396\",\"migradoc\"\n\"396\",\"audiorecord\"\n\"396\",\"fragmentstatepageradapter\"\n\"396\",\"plotly-express\"\n\"396\",\"exiftool\"\n\"396\",\"r2dbc\"\n\"396\",\"npm-publish\"\n\"396\",\"spectrum\"\n\"396\",\"prometheus-operator\"\n\"396\",\"commonsware-cwac\"\n\"396\",\"restfb\"\n\"396\",\"mule-el\"\n\"396\",\"prestashop-1.5\"\n\"396\",\"emit\"\n\"396\",\"sdp\"\n\"396\",\"bgi\"\n\"396\",\"tiled\"\n\"396\",\"yq\"\n\"395\",\"eclipse-indigo\"\n\"395\",\"skinning\"\n\"395\",\"instant-messaging\"\n\"395\",\"recaptcha-v3\"\n\"395\",\"vendor\"\n\"395\",\"kernel-extension\"\n\"395\",\"wand\"\n\"395\",\"data-class\"\n\"395\",\"swift-extensions\"\n\"395\",\"springdoc-openapi-ui\"\n\"395\",\"ribbonx\"\n\"395\",\"objectbox\"\n\"395\",\"microsoft-test-manager\"\n\"395\",\"re2\"\n\"395\",\"tfidfvectorizer\"\n\"395\",\"struts2-jquery\"\n\"395\",\"autograd\"\n\"394\",\"django-1.7\"\n\"394\",\"lang\"\n\"394\",\"functools\"\n\"394\",\"capl\"\n\"394\",\"aws-xray\"\n\"394\",\"atk4\"\n\"394\",\"broker\"\n\"394\",\"express-graphql\"\n\"394\",\"mplot3d\"\n\"394\",\"mouseout\"\n\"394\",\"mono.cecil\"\n\"394\",\"subject\"\n\"394\",\"batik\"\n\"394\",\"zbar\"\n\"393\",\"lmfit\"\n\"393\",\"clean-urls\"\n\"393\",\"flume-ng\"\n\"393\",\"sliding\"\n\"393\",\"indexoutofrangeexception\"\n\"393\",\"filepond\"\n\"393\",\"alexa-voice-service\"\n\"393\",\"epson\"\n\"393\",\"pyproject.toml\"\n\"393\",\"dot-product\"\n\"393\",\"angular15\"\n\"393\",\"reselect\"\n\"393\",\"cudnn\"\n\"393\",\"batch-normalization\"\n\"392\",\"jdbi\"\n\"392\",\"griddb\"\n\"392\",\"flatbuffers\"\n\"392\",\"jszip\"\n\"392\",\"ruby-grape\"\n\"392\",\"pycord\"\n\"392\",\"apple-developer\"\n\"392\",\"box-shadow\"\n\"392\",\"powerapps-formula\"\n\"392\",\"postgresql-13\"\n\"392\",\"formal-languages\"\n\"392\",\"javers\"\n\"392\",\"android-anr-dialog\"\n\"392\",\"py-telegram-bot-api\"\n\"392\",\"egl\"\n\"392\",\"ios8.1\"\n\"392\",\"android-loadermanager\"\n\"392\",\"string-aggregation\"\n\"392\",\"webpack-encore\"\n\"391\",\"apache-commons-dbcp\"\n\"391\",\"renderpartial\"\n\"391\",\"laravel-3\"\n\"391\",\"firebase-app-check\"\n\"391\",\"xml-documentation\"\n\"391\",\"pandas-loc\"\n\"391\",\"method-call\"\n\"391\",\"spring-data-couchbase\"\n\"391\",\"surveymonkey\"\n\"391\",\"nested-sets\"\n\"391\",\"formal-verification\"\n\"391\",\"cocos2d-android\"\n\"391\",\"orchestration\"\n\"391\",\"fabric8\"\n\"391\",\"generic-collections\"\n\"391\",\"launch4j\"\n\"390\",\"pagedlist\"\n\"390\",\"role\"\n\"390\",\"infrastructure\"\n\"390\",\"devart\"\n\"390\",\"vim-syntax-highlighting\"\n\"390\",\"java-memory-model\"\n\"390\",\"network-analysis\"\n\"390\",\"nosetests\"\n\"390\",\"lookbehind\"\n\"390\",\"angular2-http\"\n\"390\",\"parfor\"\n\"390\",\"emv\"\n\"389\",\"mvc-mini-profiler\"\n\"389\",\"jmf\"\n\"389\",\"named\"\n\"389\",\"django-rest-framework-simplejwt\"\n\"389\",\"jquery-callback\"\n\"389\",\"koala\"\n\"389\",\"xcode15\"\n\"389\",\"azure-rest-api\"\n\"389\",\"express-jwt\"\n\"389\",\"subversive\"\n\"389\",\"tornadofx\"\n\"389\",\"maximize\"\n\"388\",\"sshd\"\n\"388\",\"ecmascript-2017\"\n\"388\",\"image-loading\"\n\"388\",\"fullcalendar-3\"\n\"388\",\"circular-list\"\n\"388\",\"platformio\"\n\"388\",\"receiver\"\n\"388\",\"key-value-coding\"\n\"388\",\"windows-mobile-6.5\"\n\"388\",\"error-log\"\n\"388\",\"jquery-ui-resizable\"\n\"388\",\"violin-plot\"\n\"388\",\"facebook-canvas\"\n\"388\",\"rapidjson\"\n\"388\",\"azure-front-door\"\n\"388\",\"azure-storage-queues\"\n\"388\",\"redraw\"\n\"388\",\"texas-instruments\"\n\"388\",\"ember-simple-auth\"\n\"387\",\"ssh.net\"\n\"387\",\"graphene-django\"\n\"387\",\"git-cherry-pick\"\n\"387\",\"socketserver\"\n\"387\",\"laravel-authentication\"\n\"387\",\"soft-delete\"\n\"387\",\"dm-script\"\n\"387\",\"doctrine-1.2\"\n\"387\",\"android-studio-2.3\"\n\"387\",\"jquery-jtable\"\n\"387\",\"facade\"\n\"387\",\"amazon-simpledb\"\n\"387\",\"azure-maps\"\n\"387\",\"cert-manager\"\n\"387\",\"meshlab\"\n\"387\",\"perl-data-structures\"\n\"387\",\"autoloader\"\n\"387\",\"authorize-attribute\"\n\"387\",\"gmail-addons\"\n\"387\",\"has-one\"\n\"387\",\"array-push\"\n\"386\",\"background-position\"\n\"386\",\"vstest\"\n\"386\",\"skew\"\n\"386\",\"aeson\"\n\"386\",\"rxtx\"\n\"386\",\"roleprovider\"\n\"386\",\"boost-geometry\"\n\"386\",\"twitter-streaming-api\"\n\"386\",\"wstring\"\n\"386\",\"eol\"\n\"386\",\"visualstatemanager\"\n\"386\",\"azure-iot-sdk\"\n\"386\",\"quarkus-panache\"\n\"386\",\"ascii-art\"\n\"385\",\"cloning\"\n\"385\",\"jsqmessagesviewcontroller\"\n\"385\",\"bipartite\"\n\"385\",\"fsockopen\"\n\"385\",\"facebook-sdk-3.0\"\n\"385\",\"nsset\"\n\"385\",\"popper.js\"\n\"385\",\"object-storage\"\n\"385\",\"code-translation\"\n\"385\",\"kotlin-native\"\n\"385\",\"digest-authentication\"\n\"385\",\"spring-web\"\n\"385\",\"nonlinear-functions\"\n\"385\",\"cupy\"\n\"385\",\"getattr\"\n\"385\",\"sti\"\n\"385\",\"autocad-plugin\"\n\"384\",\"xulrunner\"\n\"384\",\"dbix-class\"\n\"384\",\"directx-12\"\n\"384\",\"incomplete-type\"\n\"384\",\"singularity-container\"\n\"384\",\"avx512\"\n\"384\",\"ajv\"\n\"384\",\"boost-program-options\"\n\"384\",\"easing\"\n\"384\",\"dynamic-loading\"\n\"384\",\"enumerator\"\n\"384\",\"facebook-audience-network\"\n\"384\",\"pyobjc\"\n\"384\",\"ui-select\"\n\"384\",\"opensaml\"\n\"384\",\"contourf\"\n\"384\",\"activereports\"\n\"384\",\"quadtree\"\n\"384\",\"pre\"\n\"384\",\"qtabwidget\"\n\"384\",\"allegro\"\n\"383\",\"matplotlib-3d\"\n\"383\",\"phing\"\n\"383\",\"llama-index\"\n\"383\",\"jslider\"\n\"383\",\"mapr\"\n\"383\",\"named-parameters\"\n\"383\",\"data-driven-tests\"\n\"383\",\"k6\"\n\"383\",\"html-rendering\"\n\"383\",\"html-escape-characters\"\n\"383\",\"onkeypress\"\n\"383\",\"nerdtree\"\n\"383\",\"tablecell\"\n\"383\",\"cognos-10\"\n\"383\",\"typegraphql\"\n\"383\",\"xcode6.1\"\n\"383\",\"dsn\"\n\"383\",\"bytecode-manipulation\"\n\"383\",\"string-conversion\"\n\"383\",\"ocx\"\n\"383\",\"offlineapps\"\n\"383\",\"login-control\"\n\"383\",\"qsub\"\n\"382\",\"bixby\"\n\"382\",\"piecewise\"\n\"382\",\"catel\"\n\"382\",\"overpass-api\"\n\"382\",\"chunking\"\n\"382\",\"adafruit\"\n\"382\",\"calendarview\"\n\"382\",\"angular-ngselect\"\n\"382\",\"twitter-bootstrap-2\"\n\"382\",\"shoulda\"\n\"382\",\"redmine-plugins\"\n\"382\",\"podspec\"\n\"382\",\"test-coverage\"\n\"382\",\"leakcanary\"\n\"382\",\"usage-statistics\"\n\"382\",\"url-pattern\"\n\"382\",\"qtcore\"\n\"381\",\"template-haskell\"\n\"381\",\"youtrack\"\n\"381\",\"file-locking\"\n\"381\",\"castle\"\n\"381\",\"gpx\"\n\"381\",\"rspec3\"\n\"381\",\"javadb\"\n\"381\",\"code-documentation\"\n\"381\",\"exponentiation\"\n\"381\",\"openscenegraph\"\n\"381\",\"custom-lists\"\n\"381\",\"medical\"\n\"380\",\"stacked\"\n\"380\",\"ef-core-3.0\"\n\"380\",\"flash-memory\"\n\"380\",\"import-from-csv\"\n\"380\",\"python-2.5\"\n\"380\",\"rotational-matrices\"\n\"380\",\"azure-app-service-envrmnt\"\n\"380\",\"network-drive\"\n\"380\",\"radar-chart\"\n\"380\",\"scoring\"\n\"380\",\"copying\"\n\"380\",\"iscroll\"\n\"380\",\"cgridview\"\n\"380\",\"angular-changedetection\"\n\"380\",\"collectionviewsource\"\n\"380\",\"scrollpane\"\n\"379\",\"yajra-datatable\"\n\"379\",\"try-with-resources\"\n\"379\",\"blackberry-cascades\"\n\"379\",\"chroot\"\n\"379\",\"db4o\"\n\"379\",\"simplesamlphp\"\n\"379\",\"kestrel\"\n\"379\",\"blueprint\"\n\"379\",\"julia-jump\"\n\"379\",\"dynamics-nav\"\n\"379\",\"sapper\"\n\"379\",\"oci8\"\n\"379\",\"shipping-method\"\n\"379\",\"testthat\"\n\"379\",\"webos\"\n\"378\",\"mvc-editor-templates\"\n\"378\",\"jcenter\"\n\"378\",\"graph-traversal\"\n\"378\",\"react-native-paper\"\n\"378\",\"apl\"\n\"378\",\"swr\"\n\"378\",\"sniffing\"\n\"378\",\"image-stitching\"\n\"378\",\"dataverse\"\n\"378\",\"aws-amplify-cli\"\n\"378\",\"angular-strap\"\n\"378\",\"sigint\"\n\"378\",\"qwt\"\n\"378\",\"rest-assured-jsonpath\"\n\"378\",\"liferay-theme\"\n\"378\",\"autosys\"\n\"378\",\"zig\"\n\"377\",\"figma\"\n\"377\",\"cloudera-manager\"\n\"377\",\"friend-function\"\n\"377\",\"jxbrowser\"\n\"377\",\"ndk-build\"\n\"377\",\"deobfuscation\"\n\"377\",\"visual-composer\"\n\"377\",\"caddy\"\n\"377\",\"azure-rm\"\n\"377\",\"gams-math\"\n\"377\",\"cgroups\"\n\"377\",\"stripes\"\n\"377\",\"lc3\"\n\"377\",\"spl\"\n\"377\",\"argument-dependent-lookup\"\n\"376\",\"chown\"\n\"376\",\"content-length\"\n\"376\",\"wayland\"\n\"376\",\"windows-community-toolkit\"\n\"376\",\"nsdocumentdirectory\"\n\"376\",\"equivalent\"\n\"376\",\"easyphp\"\n\"376\",\"boost-interprocess\"\n\"376\",\"android-background\"\n\"376\",\"sightly\"\n\"376\",\"openurl\"\n\"376\",\"nswindowcontroller\"\n\"376\",\"x264\"\n\"376\",\"metabase\"\n\"376\",\"ms-release-management\"\n\"375\",\"jenkins-workflow\"\n\"375\",\"php-gd\"\n\"375\",\"anorm\"\n\"375\",\"back4app\"\n\"375\",\"findcontrol\"\n\"375\",\"plaid\"\n\"375\",\"frida\"\n\"375\",\"kubernetes-statefulset\"\n\"375\",\"simultaneous\"\n\"375\",\"nested-resources\"\n\"375\",\"mongoimport\"\n\"375\",\"asp.net-core-tag-helpers\"\n\"375\",\"isometric\"\n\"375\",\"currency-formatting\"\n\"375\",\"summarization\"\n\"375\",\"topology\"\n\"375\",\"starling-framework\"\n\"375\",\"stdbind\"\n\"374\",\"fluent-ffmpeg\"\n\"374\",\"tensorrt\"\n\"374\",\"smarty3\"\n\"374\",\"padrino\"\n\"374\",\"android-timepicker\"\n\"374\",\"facebook-iframe\"\n\"374\",\"null-pointer\"\n\"374\",\"dexie\"\n\"374\",\"openvino\"\n\"374\",\"mle\"\n\"374\",\"http-method\"\n\"374\",\"scylla\"\n\"374\",\"stl-algorithm\"\n\"374\",\"complextype\"\n\"374\",\"article\"\n\"373\",\"decoupling\"\n\"373\",\"hyperledger-fabric-ca\"\n\"373\",\"mysql-real-escape-string\"\n\"373\",\"darwin\"\n\"373\",\"doctrine-query\"\n\"373\",\"cross-reference\"\n\"373\",\"delta\"\n\"373\",\"google-closure-library\"\n\"373\",\"f#-data\"\n\"373\",\"pysftp\"\n\"373\",\"windows-terminal\"\n\"373\",\"objectlistview\"\n\"373\",\"itemssource\"\n\"373\",\"dimple.js\"\n\"373\",\"digit\"\n\"373\",\"mpmovieplayer\"\n\"373\",\"mpmusicplayercontroller\"\n\"373\",\"resolver\"\n\"373\",\"image-capture\"\n\"373\",\"web-deployment-project\"\n\"373\",\"mediastream\"\n\"373\",\"maven-failsafe-plugin\"\n\"372\",\"template-matching\"\n\"372\",\"directus\"\n\"372\",\"laravel-collection\"\n\"372\",\"snappy\"\n\"372\",\"unusernotificationcenter\"\n\"372\",\"pulseaudio\"\n\"372\",\"windows-container\"\n\"372\",\"jls\"\n\"372\",\"openflow\"\n\"372\",\"writetofile\"\n\"372\",\"audiotoolbox\"\n\"372\",\"azure-redis-cache\"\n\"372\",\"drilldown\"\n\"372\",\"nonce\"\n\"372\",\"getattribute\"\n\"372\",\"qfiledialog\"\n\"372\",\"webauthn\"\n\"372\",\"linked-data\"\n\"372\",\"yugabytedb\"\n\"371\",\"ef-core-6.0\"\n\"371\",\"interbase\"\n\"371\",\"catboost\"\n\"371\",\"apache-pulsar\"\n\"371\",\"sharepoint-api\"\n\"371\",\"aws-glue-data-catalog\"\n\"371\",\"psd\"\n\"371\",\"sifr\"\n\"371\",\"attributerouting\"\n\"371\",\".net-1.1\"\n\"371\",\"dropshadow\"\n\"371\",\"android-deep-link\"\n\"371\",\"prometheus-node-exporter\"\n\"371\",\"activation\"\n\"371\",\"array-filter\"\n\"371\",\"idp\"\n\"370\",\"flink-cep\"\n\"370\",\"default-parameters\"\n\"370\",\"sentence\"\n\"370\",\"python-unittest.mock\"\n\"370\",\"indexer\"\n\"370\",\"packets\"\n\"370\",\"flashlight\"\n\"370\",\"django-i18n\"\n\"370\",\"crt\"\n\"370\",\"jgroups\"\n\"370\",\"onnxruntime\"\n\"370\",\"nativequery\"\n\"370\",\"delphi-10.4-sydney\"\n\"370\",\"branch-prediction\"\n\"370\",\"execvp\"\n\"370\",\"ply\"\n\"370\",\"css-specificity\"\n\"370\",\"line-numbers\"\n\"370\",\"star-schema\"\n\"370\",\"for-comprehension\"\n\"370\",\"solana-web3js\"\n\"369\",\"declarative\"\n\"369\",\"ssi\"\n\"369\",\"defaulttablemodel\"\n\"369\",\"sql-server-profiler\"\n\"369\",\"palette\"\n\"369\",\"binaryformatter\"\n\"369\",\"mandelbrot\"\n\"369\",\"rounding-error\"\n\"369\",\"windows-10-desktop\"\n\"369\",\"data-import\"\n\"369\",\"just-audio\"\n\"369\",\".so\"\n\"369\",\"hint\"\n\"369\",\"plinq\"\n\"369\",\"azure-files\"\n\"369\",\"android-optionsmenu\"\n\"369\",\"ews-managed-api\"\n\"369\",\"qtquickcontrols2\"\n\"368\",\"gist\"\n\"368\",\"whoosh\"\n\"368\",\"render-to-texture\"\n\"368\",\"pimcore\"\n\"368\",\"swiftui-tabview\"\n\"368\",\"fuseesb\"\n\"368\",\"django-nonrel\"\n\"368\",\"pdftotext\"\n\"368\",\"android-wake-lock\"\n\"368\",\"nsmutableurlrequest\"\n\"368\",\"libevent\"\n\"368\",\"cocos2d-js\"\n\"368\",\"3-tier\"\n\"368\",\"3d-reconstruction\"\n\"368\",\"tab-completion\"\n\"368\",\"magento-rest-api\"\n\"368\",\"rails-models\"\n\"368\",\"regexp-substr\"\n\"368\",\"x-axis\"\n\"368\",\"generate\"\n\"368\",\"testunit\"\n\"368\",\"mtom\"\n\"367\",\"default-arguments\"\n\"367\",\"filedialog\"\n\"367\",\"jspdf-autotable\"\n\"367\",\"p4v\"\n\"367\",\"paginator\"\n\"367\",\"sencha-touch-2.1\"\n\"367\",\"avkit\"\n\"367\",\"cross-apply\"\n\"367\",\"django-rest-viewsets\"\n\"367\",\"cross-entropy\"\n\"367\",\"mib\"\n\"367\",\"signtool\"\n\"367\",\"nsmenu\"\n\"367\",\"navicat\"\n\"367\",\"jrubyonrails\"\n\"367\",\"lua-patterns\"\n\"367\",\"lwp\"\n\"367\",\"setvalue\"\n\"367\",\"udid\"\n\"367\",\"mach-o\"\n\"367\",\"netbeans-6.9\"\n\"367\",\"raml\"\n\"367\",\"javascript-debugger\"\n\"367\",\"uid\"\n\"367\",\"android-r8\"\n\"367\",\"iphone-developer-program\"\n\"367\",\"hevc\"\n\"367\",\"identity-column\"\n\"366\",\"kubernetes-pvc\"\n\"366\",\"cross-correlation\"\n\"366\",\"sequelize-typescript\"\n\"366\",\"facebook-pixel\"\n\"366\",\"django-validation\"\n\"366\",\"modifier\"\n\"366\",\"www-mechanize\"\n\"366\",\"two-way-binding\"\n\"366\",\"sap-basis\"\n\"366\",\"mongodump\"\n\"366\",\"google-checkout\"\n\"366\",\"brunch\"\n\"366\",\"netbeans-plugins\"\n\"366\",\"asp.net-mvc-scaffolding\"\n\"366\",\"dotnetnuke-module\"\n\"366\",\"openrowset\"\n\"366\",\"spnego\"\n\"366\",\"react-bootstrap-table\"\n\"366\",\"transcoding\"\n\"366\",\"image-conversion\"\n\"366\",\"utility\"\n\"366\",\"prettyphoto\"\n\"365\",\"jsni\"\n\"365\",\"cascading\"\n\"365\",\"single-quotes\"\n\"365\",\"methodology\"\n\"365\",\"windows-phone-8-emulator\"\n\"365\",\"documentum\"\n\"365\",\"django-template-filters\"\n\"365\",\"cancellationtokensource\"\n\"365\",\"savestate\"\n\"365\",\"dynamic-import\"\n\"365\",\"hotmail\"\n\"365\",\"viewbox\"\n\"365\",\"strstr\"\n\"365\",\"glpk\"\n\"365\",\"urlopen\"\n\"365\",\"scriptaculous\"\n\"364\",\"fileinfo\"\n\"364\",\"ansi-sql\"\n\"364\",\"xmllint\"\n\"364\",\"kendo-scheduler\"\n\"364\",\"readxl\"\n\"364\",\"azure-boards\"\n\"364\",\"hp-ux\"\n\"364\",\"nstimeinterval\"\n\"364\",\"existential-type\"\n\"364\",\"hibernate-5.x\"\n\"364\",\"textjoin\"\n\"364\",\"changeset\"\n\"364\",\"accounting\"\n\"364\",\"youtube-livestreaming-api\"\n\"364\",\"script-task\"\n\"364\",\"arrow-keys\"\n\"364\",\"ticker\"\n\"363\",\"vspackage\"\n\"363\",\"primevue\"\n\"363\",\"primereact\"\n\"363\",\"mapped-types\"\n\"363\",\"simpy\"\n\"363\",\"sbt-native-packager\"\n\"363\",\"jvisualvm\"\n\"363\",\"microsoft-translator\"\n\"363\",\"randomaccessfile\"\n\"363\",\"notifyicon\"\n\"363\",\"tanstackreact-query\"\n\"363\",\"mobile-phones\"\n\"363\",\"azure-load-balancer\"\n\"363\",\"android-memory\"\n\"363\",\"request-promise\"\n\"363\",\"conceptual\"\n\"363\",\"structured-data\"\n\"363\",\"arcmap\"\n\"362\",\"truncation\"\n\"362\",\"fakeiteasy\"\n\"362\",\"readme\"\n\"362\",\"rebuild\"\n\"362\",\"desire2learn\"\n\"362\",\"sim-card\"\n\"362\",\"nsight\"\n\"362\",\"samsung-galaxy\"\n\"362\",\"postgresql-8.4\"\n\"362\",\"expandoobject\"\n\"362\",\"numberpicker\"\n\"362\",\"c#-to-f#\"\n\"362\",\"collaborative-filtering\"\n\"362\",\"chartkick\"\n\"362\",\"zustand\"\n\"362\",\"hector\"\n\"362\",\"url-mapping\"\n\"361\",\"jmenu\"\n\"361\",\"reason\"\n\"361\",\"saving-data\"\n\"361\",\"visual-c++-2010\"\n\"361\",\"cognos-bi\"\n\"361\",\"android-fonts\"\n\"361\",\"laravel-sail\"\n\"361\",\"memorycache\"\n\"361\",\"react-animated\"\n\"361\",\"google-translation-api\"\n\"361\",\"cythonize\"\n\"361\",\"bignum\"\n\"361\",\"measurement-protocol\"\n\"360\",\"gitlab-ce\"\n\"360\",\"rust-polars\"\n\"360\",\"catalina\"\n\"360\",\"firebase-test-lab\"\n\"360\",\"faye\"\n\"360\",\"boost-filesystem\"\n\"360\",\"android-storage\"\n\"360\",\"deterministic\"\n\"360\",\"pdftron\"\n\"360\",\"3des\"\n\"360\",\"3dtouch\"\n\"360\",\"orange\"\n\"360\",\"javafxports\"\n\"360\",\"winpcap\"\n\"360\",\"spring-test-mvc\"\n\"360\",\"xcode7.3\"\n\"360\",\"reactive-streams\"\n\"360\",\"event-viewer\"\n\"360\",\"late-binding\"\n\"360\",\"change-tracking\"\n\"360\",\"qlistview\"\n\"360\",\"compiler-bug\"\n\"360\",\"google-plus-one\"\n\"360\",\"pre-commit.com\"\n\"359\",\"aot\"\n\"359\",\"backspace\"\n\"359\",\"feedparser\"\n\"359\",\"ulimit\"\n\"359\",\"app.yaml\"\n\"359\",\"react-three-drei\"\n\"359\",\"oracle-data-integrator\"\n\"359\",\"inflate\"\n\"359\",\"spring-data-jdbc\"\n\"359\",\"nsmutableattributedstring\"\n\"359\",\"jasypt\"\n\"359\",\"buildout\"\n\"359\",\"type-deduction\"\n\"359\",\"bridging-header\"\n\"359\",\"type-families\"\n\"359\",\"ubercart\"\n\"359\",\"isomorphic-javascript\"\n\"359\",\"mkoverlay\"\n\"359\",\"nullptr\"\n\"359\",\"compare-and-swap\"\n\"359\",\"toolbox\"\n\"359\",\"android-management-api\"\n\"358\",\"skrollr\"\n\"358\",\"treeviewitem\"\n\"358\",\"unbind\"\n\"358\",\"fresco\"\n\"358\",\"distortion\"\n\"358\",\"flatlist\"\n\"358\",\"kendo-dropdown\"\n\"358\",\"scala-2.8\"\n\"358\",\"silverstripe-4\"\n\"358\",\"dense-rank\"\n\"358\",\"domain-model\"\n\"358\",\"libpq\"\n\"358\",\"objectanimator\"\n\"358\",\"radial-gradients\"\n\"358\",\"bulk-load\"\n\"358\",\"stringio\"\n\"358\",\"test-kitchen\"\n\"358\",\"strip-tags\"\n\"358\",\"android-popupwindow\"\n\"357\",\"gitlab-omnibus\"\n\"357\",\"ssh-agent\"\n\"357\",\"rxjs-pipeable-operators\"\n\"357\",\"jspinner\"\n\"357\",\"factory-boy\"\n\"357\",\"ibm-rad\"\n\"357\",\"nao-robot\"\n\"357\",\"spring-integration-sftp\"\n\"357\",\"system.io.file\"\n\"357\",\"machine-translation\"\n\"357\",\"dojox.grid.datagrid\"\n\"357\",\"time-format\"\n\"357\",\"referential-integrity\"\n\"357\",\"openpgp\"\n\"357\",\"google-datalayer\"\n\"357\",\"stripe-connect\"\n\"357\",\"android-notification-bar\"\n\"357\",\"ios-extensions\"\n\"357\",\"second-level-cache\"\n\"356\",\"ebnf\"\n\"356\",\"grocery-crud\"\n\"356\",\"blazemeter\"\n\"356\",\"wc\"\n\"356\",\"appveyor\"\n\"356\",\"inode\"\n\"356\",\"rar\"\n\"356\",\"registrykey\"\n\"356\",\"collect\"\n\"355\",\"list-initialization\"\n\"355\",\"vsx\"\n\"355\",\"fuzzy-comparison\"\n\"355\",\"fasm\"\n\"355\",\"alfresco-webscripts\"\n\"355\",\"init.d\"\n\"355\",\"visionos\"\n\"355\",\"fragment-backstack\"\n\"355\",\"restrict\"\n\"355\",\"http-request\"\n\"355\",\"egg\"\n\"355\",\"strongname\"\n\"355\",\"statefulwidget\"\n\"355\",\"amazon-connect\"\n\"355\",\"hdf\"\n\"355\",\"ussd\"\n\"354\",\"decomposition\"\n\"354\",\"termination\"\n\"354\",\"fuzzy\"\n\"354\",\"marketplace\"\n\"354\",\"caldav\"\n\"354\",\"modular\"\n\"354\",\"spring-profiles\"\n\"354\",\"boost-propertytree\"\n\"354\",\"r-grid\"\n\"354\",\"pymupdf\"\n\"354\",\"video-editing\"\n\"354\",\"shorthand\"\n\"354\",\"aspose.words\"\n\"354\",\"dgrid\"\n\"354\",\"tapply\"\n\"354\",\"scrapyd\"\n\"354\",\"prototype-programming\"\n\"354\",\"duckdb\"\n\"354\",\"activeresource\"\n\"354\",\"angular-fullstack\"\n\"354\",\"duplicate-data\"\n\"354\",\"traefik-ingress\"\n\"353\",\"xv6\"\n\"353\",\"gridpane\"\n\"353\",\"dd\"\n\"353\",\"dcos\"\n\"353\",\"react-native-video\"\n\"353\",\"cdo-climate\"\n\"353\",\"rs485\"\n\"353\",\"nsrunloop\"\n\"353\",\"ioerror\"\n\"353\",\"rhandsontable\"\n\"353\",\"fputcsv\"\n\"353\",\"scandir\"\n\"353\",\"prop\"\n\"353\",\"http-response-codes\"\n\"353\",\"androidplot\"\n\"353\",\"ilmerge\"\n\"353\",\"usergroups\"\n\"353\",\"arules\"\n\"353\",\"avassetexportsession\"\n\"353\",\"mbeans\"\n\"352\",\"livecycle\"\n\"352\",\"flowlayout\"\n\"352\",\"base-url\"\n\"352\",\"selectedindex\"\n\"352\",\"silent\"\n\"352\",\"nebular\"\n\"352\",\"boxlayout\"\n\"352\",\"dxf\"\n\"352\",\"dynamicquery\"\n\"352\",\"iocp\"\n\"352\",\"mixed-content\"\n\"352\",\"coldfusion-2016\"\n\"352\",\"fortran95\"\n\"352\",\"dropdownlistfor\"\n\"352\",\"android-paging-3\"\n\"352\",\"ignore\"\n\"351\",\"xsd.exe\"\n\"351\",\"cloud-init\"\n\"351\",\"web-standards\"\n\"351\",\"maui-community-toolkit\"\n\"351\",\"in-app\"\n\"351\",\"piping\"\n\"351\",\"django-mptt\"\n\"351\",\"docbook\"\n\"351\",\"gpu-shared-memory\"\n\"351\",\"spring-boot-admin\"\n\"351\",\"boost-bind\"\n\"351\",\"oscommerce\"\n\"351\",\"mermaid\"\n\"351\",\"acfpro\"\n\"351\",\"google-coral\"\n\"351\",\"qabstractitemmodel\"\n\"351\",\"octopress\"\n\"351\",\"active-directory-group\"\n\"351\",\"topshelf\"\n\"351\",\"zoneddatetime\"\n\"351\",\"web-chat\"\n\"350\",\"multiset\"\n\"350\",\"skia\"\n\"350\",\"php-telegram-bot\"\n\"350\",\"progress-db\"\n\"350\",\"mailmessage\"\n\"350\",\"n-queens\"\n\"350\",\"mod-perl\"\n\"350\",\"android-studio-2.0\"\n\"350\",\"wndproc\"\n\"350\",\"broom\"\n\"350\",\"android-homebutton\"\n\"350\",\"elevation\"\n\"350\",\"angularjs-components\"\n\"350\",\"shelve\"\n\"350\",\"as.date\"\n\"350\",\"subscriptions\"\n\"349\",\"economics\"\n\"349\",\"vs-unit-testing-framework\"\n\"349\",\"mupdf\"\n\"349\",\"ef-core-5.0\"\n\"349\",\"imbalanced-data\"\n\"349\",\"flame\"\n\"349\",\"cdf\"\n\"349\",\"checkmarx\"\n\"349\",\"pagerank\"\n\"349\",\"unirest\"\n\"349\",\"shared-directory\"\n\"349\",\"reagent\"\n\"349\",\"rssi\"\n\"349\",\"avcapturedevice\"\n\"349\",\"bower-install\"\n\"349\",\"vigenere\"\n\"349\",\"atmelstudio\"\n\"349\",\"kops\"\n\"349\",\"convention\"\n\"349\",\"angular-controller\"\n\"349\",\"dtype\"\n\"349\",\"request-mapping\"\n\"349\",\"ogre\"\n\"349\",\"acumatica-kb\"\n\"349\",\"pre-increment\"\n\"349\",\"automated-refactoring\"\n\"349\",\"avalondock\"\n\"348\",\"tensorflow-probability\"\n\"348\",\"ng-view\"\n\"348\",\"function-calls\"\n\"348\",\"filesystemobject\"\n\"348\",\"packing\"\n\"348\",\"aws-device-farm\"\n\"348\",\"java-5\"\n\"348\",\"3d-model\"\n\"348\",\"winscp-net\"\n\"348\",\"drush\"\n\"348\",\"ivr\"\n\"348\",\"activation-function\"\n\"348\",\"android-radiobutton\"\n\"348\",\"either\"\n\"348\",\"resilience4j\"\n\"348\",\"google-code\"\n\"348\",\"zoom-sdk\"\n\"348\",\"qtimer\"\n\"348\",\"arrows\"\n\"347\",\"multi-user\"\n\"347\",\"multipleselection\"\n\"347\",\"listagg\"\n\"347\",\"photoswipe\"\n\"347\",\"salesforce-communities\"\n\"347\",\"jarsigner\"\n\"347\",\"minimagick\"\n\"347\",\"nvda\"\n\"347\",\"testng-eclipse\"\n\"347\",\"text-rendering\"\n\"347\",\"leaflet.markercluster\"\n\"347\",\"reserved-words\"\n\"347\",\"genetic-programming\"\n\"347\",\"iphone-6\"\n\"347\",\"qpid\"\n\"347\",\"google-maps-static-api\"\n\"347\",\"spacy-3\"\n\"347\",\"stimulsoft\"\n\"346\",\"size-t\"\n\"346\",\"vpython\"\n\"346\",\"xmonad\"\n\"346\",\"dateformatter\"\n\"346\",\"hypothesis-test\"\n\"346\",\"jitsi\"\n\"346\",\"cpack\"\n\"346\",\"boost-log\"\n\"346\",\"rhomobile\"\n\"346\",\"wlst\"\n\"346\",\"synchronize\"\n\"346\",\"random-access\"\n\"346\",\"siebel\"\n\"346\",\"difflib\"\n\"346\",\"radix-sort\"\n\"346\",\"toastr\"\n\"346\",\"noexcept\"\n\"346\",\"xbap\"\n\"346\",\"dijit.form\"\n\"346\",\"quirks-mode\"\n\"346\",\"color-palette\"\n\"346\",\"react-native-camera\"\n\"346\",\"nimbus\"\n\"346\",\"leap-motion\"\n\"346\",\"scrollspy\"\n\"345\",\"aem-6\"\n\"345\",\"ultrawingrid\"\n\"345\",\"ng-template\"\n\"345\",\"icarousel\"\n\"345\",\"cakephp-2.4\"\n\"345\",\"dynamics-business-central\"\n\"345\",\"networkd3\"\n\"345\",\"gwtp\"\n\"345\",\"ios17\"\n\"345\",\"excel-365\"\n\"345\",\"command-line-tool\"\n\"345\",\"elasticsearch-2.0\"\n\"345\",\"paper-elements\"\n\"345\",\"gml\"\n\"345\",\"concurrentdictionary\"\n\"344\",\"vuelidate\"\n\"344\",\"data-recovery\"\n\"344\",\"data-partitioning\"\n\"344\",\"nextjs-image\"\n\"344\",\"silent-installer\"\n\"344\",\"android-auto\"\n\"344\",\"double-pointer\"\n\"344\",\"downgrade\"\n\"344\",\"bulletphysics\"\n\"344\",\"cabal-install\"\n\"344\",\"reference-type\"\n\"344\",\"css-multicolumn-layout\"\n\"344\",\"sumproduct\"\n\"344\",\"sos\"\n\"344\",\"mediawiki-extensions\"\n\"343\",\"multivalue\"\n\"343\",\"git-add\"\n\"343\",\"const-correctness\"\n\"343\",\"owl-carousel-2\"\n\"343\",\"bindinglist\"\n\"343\",\"cp-sat\"\n\"343\",\"workflow-activity\"\n\"343\",\"scala-ide\"\n\"343\",\"azcopy\"\n\"343\",\"post-processing\"\n\"343\",\"microsoft-identity-platform\"\n\"343\",\"obd-ii\"\n\"343\",\"magmi\"\n\"343\",\".net-4.7.2\"\n\"343\",\".net-core-2.0\"\n\"343\",\"netlink\"\n\"343\",\"luigi\"\n\"343\",\"xbox\"\n\"343\",\"scoped-storage\"\n\"343\",\"qwebengineview\"\n\"343\",\"dts\"\n\"343\",\"gethashcode\"\n\"343\",\"memory-profiling\"\n\"343\",\"aruco\"\n\"343\",\"imagefield\"\n\"343\",\"amazon-cloudsearch\"\n\"343\",\"sunburst-diagram\"\n\"342\",\"vue-apollo\"\n\"342\",\"listeners\"\n\"342\",\"unique-index\"\n\"342\",\"financial\"\n\"342\",\"smartgit\"\n\"342\",\"rust-macros\"\n\"342\",\"iccube\"\n\"342\",\"ibm-datapower\"\n\"342\",\"document-root\"\n\"342\",\"mongo-java-driver\"\n\"342\",\"jquery-validation-engine\"\n\"342\",\"demo\"\n\"342\",\"nsrange\"\n\"342\",\"entity-framework-4.3\"\n\"342\",\"bluehost\"\n\"342\",\"pyopencl\"\n\"342\",\"android-api-levels\"\n\"342\",\"lwip\"\n\"342\",\"javafx-11\"\n\"342\",\"dovecot\"\n\"342\",\"expert-system\"\n\"342\",\"proxy-classes\"\n\"342\",\"customdialog\"\n\"342\",\"elixir-mix\"\n\"342\",\"webpack-hmr\"\n\"342\",\"flutter-ios\"\n\"342\",\"maven-jaxb2-plugin\"\n\"341\",\"inspector\"\n\"341\",\"castle-dynamicproxy\"\n\"341\",\"connection-refused\"\n\"341\",\"vnet\"\n\"341\",\"uncaught-exception\"\n\"341\",\"appender\"\n\"341\",\"jmenuitem\"\n\"341\",\"datainputstream\"\n\"341\",\"word-frequency\"\n\"341\",\"abcpdf\"\n\"341\",\"form-helpers\"\n\"341\",\"type-level-computation\"\n\"341\",\"screen-recording\"\n\"341\",\"drawtext\"\n\"341\",\"drupal-webform\"\n\"341\",\"textblob\"\n\"341\",\"arraycollection\"\n\"340\",\"dita\"\n\"340\",\"contentcontrol\"\n\"340\",\"ovh\"\n\"340\",\"flex4.6\"\n\"340\",\"fits\"\n\"340\",\"crosswalk\"\n\"340\",\"application-loader\"\n\"340\",\"tablecelleditor\"\n\"340\",\"gobject\"\n\"340\",\"libsodium\"\n\"340\",\"quickfixj\"\n\"340\",\"hadoop-partitioning\"\n\"340\",\"spray-json\"\n\"340\",\"duplex\"\n\"340\",\"cublas\"\n\"340\",\"spacemacs\"\n\"339\",\"eeprom\"\n\"339\",\"cleartool\"\n\"339\",\"manifest.mf\"\n\"339\",\"volumes\"\n\"339\",\"facebook-share\"\n\"339\",\"canoe\"\n\"339\",\"named-scope\"\n\"339\",\"sitecore7.2\"\n\"339\",\"kernighan-and-ritchie\"\n\"339\",\"bootstrap-popover\"\n\"339\",\"gtag.js\"\n\"339\",\"opencv3.1\"\n\"339\",\"fpdi\"\n\"339\",\"pyright\"\n\"339\",\"sql-server-2014-express\"\n\"339\",\"np-complete\"\n\"339\",\"redis-sentinel\"\n\"339\",\"mmenu\"\n\"339\",\"sql-merge\"\n\"339\",\"opengl-compat\"\n\"339\",\"spring-thymeleaf\"\n\"339\",\"iterm\"\n\"339\",\"identity-management\"\n\"339\",\"gnuradio-companion\"\n\"338\",\"eclipse-neon\"\n\"338\",\"swiftui-navigationstack\"\n\"338\",\"file-watcher\"\n\"338\",\"sni\"\n\"338\",\"vault\"\n\"338\",\"recycle\"\n\"338\",\"angularjs-ng-options\"\n\"338\",\"cs-cart\"\n\"338\",\"mongodb-replica-set\"\n\"338\",\"onlongclicklistener\"\n\"338\",\"ws\"\n\"338\",\"paytm\"\n\"338\",\"kotlin-extension\"\n\"338\",\"f5\"\n\"338\",\"code-readability\"\n\"338\",\"author\"\n\"338\",\"titanium-modules\"\n\"338\",\"escpos\"\n\"338\",\"activity-finish\"\n\"338\",\"heremaps\"\n\"338\",\"seconds\"\n\"337\",\"jbuilder\"\n\"337\",\"imagesource\"\n\"337\",\"affinity\"\n\"337\",\"file-processing\"\n\"337\",\"dayjs\"\n\"337\",\"vba7\"\n\"337\",\"publisher\"\n\"337\",\"html5boilerplate\"\n\"337\",\"countvectorizer\"\n\"337\",\".obj\"\n\"337\",\"facebook-likebox\"\n\"337\",\"android-fusedlocation\"\n\"337\",\"openstack-nova\"\n\"337\",\"sql-server-2008-express\"\n\"337\",\"event-delegation\"\n\"337\",\"moodle-api\"\n\"337\",\"android-lint\"\n\"337\",\"thinktecture-ident-server\"\n\"337\",\"google-secret-manager\"\n\"337\",\"solaris-10\"\n\"337\",\"git-rewrite-history\"\n\"337\",\"arrayobject\"\n\"336\",\"flutter-desktop\"\n\"336\",\"dataformat\"\n\"336\",\"django-unittest\"\n\"336\",\"server-push\"\n\"336\",\"hsts\"\n\"336\",\"susy-compass\"\n\"336\",\"postman-pre-request-script\"\n\"336\",\"javascriptcore\"\n\"336\",\"code-access-security\"\n\"336\",\"ondestroy\"\n\"336\",\"permgen\"\n\"336\",\"accumulate\"\n\"336\",\"linter\"\n\"336\",\"gmsmapview\"\n\"335\",\"multi-project\"\n\"335\",\"renaming\"\n\"335\",\"switching\"\n\"335\",\"value-objects\"\n\"335\",\"root-framework\"\n\"335\",\"country-codes\"\n\"335\",\"simpleadapter\"\n\"335\",\"information-extraction\"\n\"335\",\"typename\"\n\"335\",\"guzzle6\"\n\"335\",\"pepper\"\n\"335\",\"zend-form-element\"\n\"335\",\"glyph\"\n\"335\",\"z-order\"\n\"334\",\"sknode\"\n\"334\",\"teams-toolkit\"\n\"334\",\"python-3.2\"\n\"334\",\"veracode\"\n\"334\",\"cpu-cores\"\n\"334\",\"2-way-object-databinding\"\n\"334\",\"facebook-chatbot\"\n\"334\",\"vertex-buffer\"\n\"334\",\"uidocumentinteraction\"\n\"334\",\"monthcalendar\"\n\"334\",\"sungridengine\"\n\"334\",\"tflite\"\n\"334\",\"flutter-local-notification\"\n\"334\",\"securityexception\"\n\"333\",\"mwaa\"\n\"333\",\"json-serialization\"\n\"333\",\"python-extensions\"\n\"333\",\"namecheap\"\n\"333\",\"r-lavaan\"\n\"333\",\"nsmenuitem\"\n\"333\",\"crc16\"\n\"333\",\"azure-container-apps\"\n\"333\",\"bootstrap-selectpicker\"\n\"333\",\"hottowel\"\n\"333\",\"android-fullscreen\"\n\"333\",\"scipy.stats\"\n\"333\",\"asp.net-blazor\"\n\"333\",\"reset-password\"\n\"333\",\"node-request\"\n\"333\",\"compass-geolocation\"\n\"333\",\"amazon-glacier\"\n\"333\",\"maxmind\"\n\"332\",\"gridpanel\"\n\"332\",\"flowlayoutpanel\"\n\"332\",\"next-router\"\n\"332\",\"appearance\"\n\"332\",\"data-integrity\"\n\"332\",\"robovm\"\n\"332\",\"docplex\"\n\"332\",\"scala-java-interop\"\n\"332\",\"modular-arithmetic\"\n\"332\",\"gbm\"\n\"332\",\"mlr\"\n\"332\",\"acid\"\n\"332\",\"multibyte\"\n\"331\",\"mux\"\n\"331\",\"datetime-conversion\"\n\"331\",\"date-comparison\"\n\"331\",\"connected-components\"\n\"331\",\"var-dump\"\n\"331\",\"django-email\"\n\"331\",\"nats.io\"\n\"331\",\"justify\"\n\"331\",\"android-audiorecord\"\n\"331\",\"quicklook\"\n\"331\",\"tabwidget\"\n\"331\",\"rackspace-cloud\"\n\"331\",\"httpsurlconnection\"\n\"331\",\"scribe\"\n\"331\",\"media-source\"\n\"330\",\"remotewebdriver\"\n\"330\",\"telemetry\"\n\"330\",\"wifip2p\"\n\"330\",\"feedback\"\n\"330\",\"voting\"\n\"330\",\"lagom\"\n\"330\",\"import-csv\"\n\"330\",\"rust-rocket\"\n\"330\",\"django-file-upload\"\n\"330\",\"faster-rcnn\"\n\"330\",\"auditing\"\n\"330\",\"osx-leopard\"\n\"330\",\"microsoft-ui-automation\"\n\"330\",\"tablespace\"\n\"330\",\"coordinate\"\n\"330\",\"azure-sdk-python\"\n\"330\",\"devexpress-windows-ui\"\n\"330\",\"bzip2\"\n\"330\",\"test-suite\"\n\"330\",\"splinter\"\n\"330\",\"force.com\"\n\"330\",\"emmeans\"\n\"330\",\"idl-programming-language\"\n\"330\",\"starlette\"\n\"329\",\"mutablelivedata\"\n\"329\",\"remoteview\"\n\"329\",\"localizable.strings\"\n\"329\",\"react-native-fbsdk\"\n\"329\",\"flask-migrate\"\n\"329\",\"firebase-crash-reporting\"\n\"329\",\"django-widget\"\n\"329\",\"datagridcomboboxcolumn\"\n\"329\",\"one-definition-rule\"\n\"329\",\"onenote-api\"\n\"329\",\"network-traffic\"\n\"329\",\"object-reference\"\n\"329\",\"nosql-aggregation\"\n\"329\",\"elastic-load-balancer\"\n\"329\",\"odoo-16\"\n\"329\",\"md5sum\"\n\"328\",\"matter.js\"\n\"328\",\"probability-distribution\"\n\"328\",\"datetimeindex\"\n\"328\",\"snort\"\n\"328\",\"data-access\"\n\"328\",\"recover\"\n\"328\",\"roboguice\"\n\"328\",\"oracle18c\"\n\"328\",\"wavefront\"\n\"328\",\"angularjs-ng-route\"\n\"328\",\"rollingfileappender\"\n\"328\",\"onscroll\"\n\"328\",\"twitterapi-python\"\n\"328\",\"java.util.date\"\n\"328\",\"minimal-apis\"\n\"328\",\"context-switch\"\n\"328\",\"openshift-client-tools\"\n\"328\",\"xa\"\n\"328\",\"playing-cards\"\n\"328\",\"spinlock\"\n\"328\",\"nic\"\n\"328\",\"monomac\"\n\"328\",\"geode\"\n\"328\",\"mrjob\"\n\"328\",\"statsd\"\n\"328\",\"array-algorithms\"\n\"328\",\"static-initialization\"\n\"327\",\"tensorflow-hub\"\n\"327\",\"deezer\"\n\"327\",\"php-8.1\"\n\"327\",\"sql-tuning\"\n\"327\",\"apk-expansion-files\"\n\"327\",\"smart-table\"\n\"327\",\"chilkat\"\n\"327\",\"kusto-explorer\"\n\"327\",\"flask-restplus\"\n\"327\",\"vonage\"\n\"327\",\"model-validation\"\n\"327\",\"jquery-cookie\"\n\"327\",\"range-v3\"\n\"327\",\"3d-modelling\"\n\"327\",\"associated-types\"\n\"327\",\"gdk\"\n\"327\",\"google-cloud-scheduler\"\n\"327\",\"spoofing\"\n\"327\",\"limits\"\n\"327\",\"tidesdk\"\n\"327\",\"parser-generator\"\n\"326\",\"trend\"\n\"326\",\"disabled-control\"\n\"326\",\"contactless-smartcard\"\n\"326\",\"data-integration\"\n\"326\",\"capitalize\"\n\"326\",\"opscenter\"\n\"326\",\"dwr\"\n\"326\",\"inflate-exception\"\n\"326\",\"pymongo-3.x\"\n\"326\",\"terragrunt\"\n\"326\",\"angular16\"\n\"326\",\"httplib\"\n\"326\",\"amazon-linux\"\n\"326\",\"topological-sort\"\n\"326\",\"submit-button\"\n\"326\",\"partial-specialization\"\n\"325\",\"yield-return\"\n\"325\",\"phpcodesniffer\"\n\"325\",\"backbone-model\"\n\"325\",\"cifs\"\n\"325\",\"unicode-escapes\"\n\"325\",\"sender\"\n\"325\",\"gprof\"\n\"325\",\"supportmapfragment\"\n\"325\",\"wit.ai\"\n\"325\",\"jasmine-node\"\n\"325\",\"tabula\"\n\"325\",\"react-beautiful-dnd\"\n\"325\",\"office-2007\"\n\"325\",\"persian\"\n\"325\",\"qtwidgets\"\n\"325\",\"argo-workflows\"\n\"325\",\"google-picker\"\n\"325\",\"static-ip-address\"\n\"325\",\"arbitrary-precision\"\n\"324\",\"cloud9\"\n\"324\",\"telecommunication\"\n\"324\",\"datepickerdialog\"\n\"324\",\"symfony-3.2\"\n\"324\",\"function-parameter\"\n\"324\",\"vuex-modules\"\n\"324\",\"unity-editor\"\n\"324\",\"asynchttpclient\"\n\"324\",\"typescript1.8\"\n\"324\",\"dom4j\"\n\"324\",\"convex-optimization\"\n\"324\",\"lead\"\n\"324\",\"lotus-formula\"\n\"324\",\"hcl\"\n\"324\",\"qt4.8\"\n\"324\",\"mui-datatable\"\n\"323\",\"bash-completion\"\n\"323\",\"anonymous-methods\"\n\"323\",\"mass-assignment\"\n\"323\",\"pandas-resample\"\n\"323\",\"django-1.11\"\n\"323\",\"bitcode\"\n\"323\",\"mallet\"\n\"323\",\"pimpl-idiom\"\n\"323\",\"unsigned-char\"\n\"323\",\"database-relations\"\n\"323\",\"coverity\"\n\"323\",\"opencart-module\"\n\"323\",\"spring-repositories\"\n\"323\",\"html5-filesystem\"\n\"323\",\"nsstream\"\n\"323\",\"viewdata\"\n\"323\",\"gzipstream\"\n\"323\",\"openlayers-5\"\n\"323\",\"modbus-tcp\"\n\"323\",\"android-fingerprint-api\"\n\"323\",\"custom-errors\"\n\"323\",\"react-big-calendar\"\n\"323\",\"text-recognition\"\n\"323\",\"duck-typing\"\n\"323\",\"script-tag\"\n\"323\",\"bazel-rules\"\n\"322\",\"dbnull\"\n\"322\",\"termios\"\n\"322\",\"webviewclient\"\n\"322\",\"integer-programming\"\n\"322\",\"phar\"\n\"322\",\"flask-security\"\n\"322\",\"uiwebviewdelegate\"\n\"322\",\"round-robin\"\n\"322\",\"win32-process\"\n\"322\",\"react-transition-group\"\n\"322\",\"aws-php-sdk\"\n\"322\",\"pci-compliance\"\n\"322\",\"karma-mocha\"\n\"322\",\"revolution-slider\"\n\"322\",\"ordinal\"\n\"322\",\"typescript-decorator\"\n\"322\",\"bus\"\n\"322\",\"azure-rbac\"\n\"322\",\"command-pattern\"\n\"322\",\"react-lifecycle\"\n\"322\",\"changelog\"\n\"322\",\"conditional-types\"\n\"322\",\"allegro5\"\n\"322\",\"linguistics\"\n\"322\",\"autosar\"\n\"322\",\"dart-async\"\n\"322\",\"autoprefixer\"\n\"322\",\"amazon-rekognition\"\n\"322\",\"fmt\"\n\"321\",\"yii2-model\"\n\"321\",\"jsoncpp\"\n\"321\",\"python-appium\"\n\"321\",\"router-outlet\"\n\"321\",\"kindle-fire\"\n\"321\",\"django-storage\"\n\"321\",\"del\"\n\"321\",\"coreclr\"\n\"321\",\"ext4\"\n\"321\",\"mimekit\"\n\"321\",\"drupal-taxonomy\"\n\"321\",\"plumber\"\n\"321\",\"xamarin.uitest\"\n\"321\",\"sqlparameter\"\n\"321\",\"asp.net-core-7.0\"\n\"321\",\"last.fm\"\n\"321\",\"lru\"\n\"321\",\"pester\"\n\"321\",\"pgbouncer\"\n\"321\",\"angular4-router\"\n\"321\",\"qscrollarea\"\n\"321\",\"user-interaction\"\n\"321\",\"webix\"\n\"321\",\"top-n\"\n\"320\",\"editorconfig\"\n\"320\",\"math.h\"\n\"320\",\"cl\"\n\"320\",\"temporary-objects\"\n\"320\",\"yaml-cpp\"\n\"320\",\"flatpickr\"\n\"320\",\"page-object-gem\"\n\"320\",\"filesaver.js\"\n\"320\",\"data-management\"\n\"320\",\"g1gc\"\n\"320\",\"microprofile\"\n\"320\",\"akamai\"\n\"320\",\"android-sharing\"\n\"320\",\"apple-maps\"\n\"320\",\"android-textinputedittext\"\n\"320\",\"pymodbus\"\n\"320\",\"rackspace\"\n\"320\",\"xcode14\"\n\"320\",\"text-size\"\n\"320\",\"react-konva\"\n\"320\",\"mbprogresshud\"\n\"320\",\"hce\"\n\"320\",\"ifs\"\n\"320\",\"thread-local-storage\"\n\"319\",\"teamcity-9.0\"\n\"319\",\"graylog\"\n\"319\",\"php4\"\n\"319\",\"bitwise-and\"\n\"319\",\"name-mangling\"\n\"319\",\"velocity.js\"\n\"319\",\"wso2-iot\"\n\"319\",\"silverlight-2.0\"\n\"319\",\"pearson-correlation\"\n\"319\",\"tabactivity\"\n\"319\",\"orthographic\"\n\"319\",\"accelerate-framework\"\n\"319\",\"tmp\"\n\"319\",\"play-json\"\n\"319\",\"vfs\"\n\"319\",\"scraper\"\n\"319\",\"gentoo\"\n\"319\",\"test-bench\"\n\"319\",\"gen-server\"\n\"319\",\"genesis\"\n\"319\",\"peer\"\n\"319\",\"laravel-seeding\"\n\"319\",\"msbuild-4.0\"\n\"319\",\"hdmi\"\n\"319\",\"quandl\"\n\"319\",\"alphabet\"\n\"319\",\"zend-route\"\n\"319\",\"web-mediarecorder\"\n\"319\",\"panoramas\"\n\"318\",\"rxdart\"\n\"318\",\"aws-cloudwatch-log-insights\"\n\"318\",\"windows-messages\"\n\"318\",\"database-table\"\n\"318\",\"keyboard-hook\"\n\"318\",\"routerlink\"\n\"318\",\"boost-variant\"\n\"318\",\"sap-cloud-sdk\"\n\"318\",\"sfsafariviewcontroller\"\n\"318\",\"object-files\"\n\"318\",\"shoes\"\n\"318\",\"digital\"\n\"318\",\"android-event\"\n\"318\",\"spidermonkey\"\n\"318\",\"towers-of-hanoi\"\n\"318\",\"amazon-opensearch\"\n\"318\",\"mdbootstrap\"\n\"318\",\"msix\"\n\"318\",\"glimpse\"\n\"317\",\"jenkins-agent\"\n\"317\",\"telnetlib\"\n\"317\",\"yahoo-pipes\"\n\"317\",\"kubernetes-cronjob\"\n\"317\",\"kernel32\"\n\"317\",\"swarm\"\n\"317\",\"jquery-load\"\n\"317\",\"twython\"\n\"317\",\"borrowing\"\n\"317\",\"inorder\"\n\"317\",\"blogspot\"\n\"317\",\"libtorch\"\n\"317\",\"sql-limit\"\n\"317\",\"qlist\"\n\"317\",\"projects\"\n\"317\",\"on-duplicate-key\"\n\"317\",\"esapi\"\n\"317\",\"zepto\"\n\"317\",\"imagelist\"\n\"317\",\"google-sheets-macros\"\n\"317\",\"google-schemas\"\n\"316\",\"eclipse-mars\"\n\"316\",\"fluentui-react\"\n\"316\",\"backticks\"\n\"316\",\"chipmunk\"\n\"316\",\"advanced-installer\"\n\"316\",\"waf\"\n\"316\",\"jgrasp\"\n\"316\",\"recursive-backtracking\"\n\"316\",\"rte\"\n\"316\",\"bourbon\"\n\"316\",\"nestedscrollview\"\n\"316\",\"spring-batch-admin\"\n\"316\",\"buildbot\"\n\"316\",\"outlook-web-app\"\n\"316\",\"drawstring\"\n\"316\",\"jackson-dataformat-xml\"\n\"316\",\"xcb\"\n\"316\",\"hudson-plugins\"\n\"316\",\"elasticsearch-7\"\n\"316\",\"cufon\"\n\"316\",\"sonarqube5.1\"\n\"316\",\"sortedset\"\n\"315\",\"replacewith\"\n\"315\",\"pg-restore\"\n\"315\",\"telegram-api\"\n\"315\",\"slots\"\n\"315\",\"php-7.3\"\n\"315\",\"runas\"\n\"315\",\"xml-attribute\"\n\"315\",\"design-principles\"\n\"315\",\"ar.js\"\n\"315\",\"nserror\"\n\"315\",\"pyrogram\"\n\"315\",\"broadleaf-commerce\"\n\"315\",\"orchardcms-1.6\"\n\"315\",\"openstack-swift\"\n\"315\",\"uigraphicscontext\"\n\"315\",\"nsurlsessiondownloadtask\"\n\"315\",\"nouislider\"\n\"315\",\"xceed\"\n\"315\",\"dup2\"\n\"315\",\"google-cloud-dns\"\n\"315\",\"eventlet\"\n\"315\",\"ofbiz\"\n\"314\",\"tryparse\"\n\"314\",\"consensus\"\n\"314\",\"apache-mina\"\n\"314\",\"runtime-permissions\"\n\"314\",\"fxmlloader\"\n\"314\",\"real-time-data\"\n\"314\",\"cakephp-1.2\"\n\"314\",\"unwind-segue\"\n\"314\",\"bolt-cms\"\n\"314\",\"dynamic-forms\"\n\"314\",\"oozie-coordinator\"\n\"314\",\"object-recognition\"\n\"314\",\"modelattribute\"\n\"314\",\"operands\"\n\"314\",\"dart-isolates\"\n\"314\",\"mdichild\"\n\"314\",\"autopostback\"\n\"313\",\"phonegap-pushplugin\"\n\"313\",\"circuit\"\n\"313\",\"socialengine\"\n\"313\",\"mapquest\"\n\"313\",\"apache-tiles\"\n\"313\",\"python-embedding\"\n\"313\",\"jira-rest-java-api\"\n\"313\",\"recurring\"\n\"313\",\"gpt-3\"\n\"313\",\"workitem\"\n\"313\",\"apple-vision\"\n\"313\",\"jrebel\"\n\"313\",\"salesforce-service-cloud\"\n\"313\",\"libtiff\"\n\"313\",\"overhead\"\n\"313\",\"wireshark-dissector\"\n\"313\",\"convergence\"\n\"313\",\"notin\"\n\"313\",\"notion-api\"\n\"313\",\"galleria\"\n\"313\",\"tabpage\"\n\"313\",\"android-radiogroup\"\n\"313\",\"search-suggestion\"\n\"312\",\"eddystone\"\n\"312\",\"pingfederate\"\n\"312\",\"appcompatactivity\"\n\"312\",\"datefield\"\n\"312\",\"ups\"\n\"312\",\"mongo-java\"\n\"312\",\"nsnotification\"\n\"312\",\"audit-trail\"\n\"312\",\"woocommerce-theming\"\n\"312\",\"azure-service-principal\"\n\"312\",\"scim\"\n\"312\",\"long-press\"\n\"312\",\"cometd\"\n\"312\",\"zurb-foundation-5\"\n\"312\",\"octobercms-plugins\"\n\"312\",\"autodesk-data-management\"\n\"312\",\"powershell-5.1\"\n\"311\",\"xticks\"\n\"311\",\"wechat\"\n\"311\",\"checkpoint\"\n\"311\",\"laravel-11\"\n\"311\",\"image-preprocessing\"\n\"311\",\"xml-comments\"\n\"311\",\"pagemethods\"\n\"311\",\"oraclereports\"\n\"311\",\"psr-4\"\n\"311\",\"metatrader5\"\n\"311\",\"virtualtreeview\"\n\"311\",\"doorkeeper\"\n\"311\",\"numa\"\n\"311\",\"cordova-2.0.0\"\n\"311\",\"ios5.1\"\n\"311\",\"httponly\"\n\"311\",\"spool\"\n\"311\",\"qiskit\"\n\"311\",\"webcrypto-api\"\n\"311\",\"zombie-process\"\n\"311\",\"alpha-beta-pruning\"\n\"311\",\"subsequence\"\n\"311\",\"google-photos\"\n\"311\",\"google-search-appliance\"\n\"310\",\"ansible-tower\"\n\"310\",\"liskov-substitution-principle\"\n\"310\",\"mvccontrib\"\n\"310\",\"symmetricds\"\n\"310\",\"aws-cloud9\"\n\"310\",\"name-lookup\"\n\"310\",\"angular-router-guards\"\n\"310\",\"nest-api\"\n\"310\",\"infrared\"\n\"310\",\"dynamic-controls\"\n\"310\",\"open-uri\"\n\"310\",\"jai\"\n\"310\",\"centos5\"\n\"310\",\"cellular-network\"\n\"310\",\"http-compression\"\n\"310\",\"qbxml\"\n\"310\",\"multibranch-pipeline\"\n\"309\",\"pixel-shader\"\n\"309\",\"undetected-chromedriver\"\n\"309\",\"xiaomi\"\n\"309\",\"mfcc\"\n\"309\",\"fault-tolerance\"\n\"309\",\"azure-devops-self-hosted-agent\"\n\"309\",\"dynamic-columns\"\n\"309\",\"ganache\"\n\"309\",\"dotnetnuke-7\"\n\"309\",\"mktime\"\n\"309\",\"bulkupdate\"\n\"309\",\"estimote\"\n\"309\",\"es6-proxy\"\n\"309\",\"activemerchant\"\n\"309\",\"ctest\"\n\"309\",\"ios-4.2\"\n\"309\",\"sharpsvn\"\n\"309\",\"stylelint\"\n\"308\",\"replicate\"\n\"308\",\"jdbc-odbc\"\n\"308\",\"removeall\"\n\"308\",\"data-transform\"\n\"308\",\"ceil\"\n\"308\",\"nfa\"\n\"308\",\"servo\"\n\"308\",\"fancytree\"\n\"308\",\"pybrain\"\n\"308\",\"jmeter-3.2\"\n\"308\",\"rownum\"\n\"308\",\"pose-estimation\"\n\"308\",\"appointment\"\n\"308\",\"dill\"\n\"308\",\"scalatra\"\n\"308\",\"actionevent\"\n\"308\",\"qfile\"\n\"308\",\"stocks\"\n\"308\",\"android-lvl\"\n\"308\",\"msbi\"\n\"308\",\"maxscript\"\n\"308\",\"amazon-redshift-spectrum\"\n\"308\",\"stdset\"\n\"307\",\"reliability\"\n\"307\",\"mutability\"\n\"307\",\"chrome-native-messaging\"\n\"307\",\"adobe-reader\"\n\"307\",\"seq2seq\"\n\"307\",\"windows-kernel\"\n\"307\",\"grape-api\"\n\"307\",\"mongojs\"\n\"307\",\"svnkit\"\n\"307\",\"typed-arrays\"\n\"307\",\"externalinterface\"\n\"307\",\"vk\"\n\"307\",\"orientation-changes\"\n\"307\",\"pmml\"\n\"307\",\"centroid\"\n\"307\",\"react-map-gl\"\n\"307\",\"launch-screen\"\n\"307\",\"omnisharp\"\n\"307\",\"utm\"\n\"307\",\"compile-time-constant\"\n\"306\",\"standard-error\"\n\"306\",\"gridcontrol\"\n\"306\",\"python-jira\"\n\"306\",\"goutte\"\n\"306\",\"crosswalk-runtime\"\n\"306\",\"angular-material-6\"\n\"306\",\"detach\"\n\"306\",\"twilio-click-to-call\"\n\"306\",\"opcache\"\n\"306\",\"worklight-studio\"\n\"306\",\"nsmutablestring\"\n\"306\",\"tlb\"\n\"306\",\"tclientdataset\"\n\"306\",\"gamepad\"\n\"306\",\"android-navigation-graph\"\n\"306\",\"genetics\"\n\"306\",\"pelican\"\n\"306\",\"ipados\"\n\"306\",\"always-encrypted\"\n\"305\",\"primefaces-extensions\"\n\"305\",\"defined\"\n\"305\",\"selectedindexchanged\"\n\"305\",\"playframework-2.6\"\n\"305\",\"maintainability\"\n\"305\",\"data-protection\"\n\"305\",\"function-prototypes\"\n\"305\",\"akka-persistence\"\n\"305\",\"cruisecontrol\"\n\"305\",\"session-management\"\n\"305\",\"jgrapht\"\n\"305\",\"variadic-macros\"\n\"305\",\"bootstrap-sass\"\n\"305\",\"eonasdan-datetimepicker\"\n\"305\",\"cornerradius\"\n\"305\",\"sandcastle\"\n\"305\",\"pyflink\"\n\"305\",\"nw.js\"\n\"305\",\"hierarchicaldatatemplate\"\n\"305\",\"notnull\"\n\"305\",\"numericupdown\"\n\"305\",\"azure-java-sdk\"\n\"305\",\"openocd\"\n\"305\",\"large-data-volumes\"\n\"305\",\"shell-extensions\"\n\"305\",\"tpm\"\n\"305\",\"arc4random\"\n\"305\",\"power-platform\"\n\"305\",\"maven-compiler-plugin\"\n\"305\",\"subgraph\"\n\"305\",\"linefeed\"\n\"304\",\"cmyk\"\n\"304\",\"react-native-image-picker\"\n\"304\",\"debian-based\"\n\"304\",\"addressing-mode\"\n\"304\",\"django-autocomplete-light\"\n\"304\",\"incompatibility\"\n\"304\",\"blurry\"\n\"304\",\"infopath2010\"\n\"304\",\"detailview\"\n\"304\",\"mkdocs\"\n\"304\",\"android-bundle\"\n\"304\",\"nomachine-nx\"\n\"304\",\"retain-cycle\"\n\"304\",\"perfmon\"\n\"304\",\"eventbrite\"\n\"304\",\"questdb\"\n\"304\",\"autorun\"\n\"304\",\"email-spam\"\n\"304\",\"startactivityforresult\"\n\"303\",\"lithium\"\n\"303\",\"mute\"\n\"303\",\"litespeed\"\n\"303\",\"intellij-13\"\n\"303\",\"listen\"\n\"303\",\"runspace\"\n\"303\",\"selenide\"\n\"303\",\"vowpalwabbit\"\n\"303\",\"chronometer\"\n\"303\",\"appdata\"\n\"303\",\"css-filters\"\n\"303\",\"readdir\"\n\"303\",\"dask-delayed\"\n\"303\",\"waitpid\"\n\"303\",\"wpf-style\"\n\"303\",\"cowplot\"\n\"303\",\"forex\"\n\"303\",\"tablecolumn\"\n\"303\",\"aspose-cells\"\n\"303\",\"higher-kinded-types\"\n\"303\",\"memory-segmentation\"\n\"303\",\"mopub\"\n\"303\",\"pentaho-cde\"\n\"303\",\"paper-trail-gem\"\n\"303\",\"emoticons\"\n\"303\",\"webmin\"\n\"303\",\"flutter-image\"\n\"302\",\"sjplot\"\n\"302\",\"matrix-indexing\"\n\"302\",\"imgui\"\n\"302\",\"xml2\"\n\"302\",\"meteor-helper\"\n\"302\",\"joomla1.7\"\n\"302\",\"csc\"\n\"302\",\"jms-topic\"\n\"302\",\"nse\"\n\"302\",\"bounce\"\n\"302\",\"codelite\"\n\"302\",\"objectcontext\"\n\"302\",\"kubeadm\"\n\"302\",\"knime\"\n\"302\",\"ocelot\"\n\"302\",\"noise-reduction\"\n\"302\",\"tailwind-ui\"\n\"302\",\"opensql\"\n\"302\",\"getch\"\n\"302\",\"leaflet.draw\"\n\"302\",\"stringi\"\n\"302\",\"cumulocity\"\n\"302\",\"nodejs-stream\"\n\"302\",\"param\"\n\"302\",\"spannable\"\n\"302\",\"stdlist\"\n\"302\",\"stepper\"\n\"302\",\"qtcpsocket\"\n\"301\",\"integrity\"\n\"301\",\"clang-static-analyzer\"\n\"301\",\"apache2.2\"\n\"301\",\"mutual-authentication\"\n\"301\",\"stack-frame\"\n\"301\",\"cimg\"\n\"301\",\"django-3.0\"\n\"301\",\"advanceddatagrid\"\n\"301\",\"docker-toolbox\"\n\"301\",\"twitter-typeahead\"\n\"301\",\"pos\"\n\"301\",\"poppler\"\n\"301\",\"nbconvert\"\n\"301\",\"typelib\"\n\"301\",\"tabbarcontroller\"\n\"301\",\"hibernate-4.x\"\n\"301\",\"asp.net-mvc-5.1\"\n\"301\",\"android-icons\"\n\"301\",\"expandable\"\n\"301\",\"itfoxtec-identity-saml2\"\n\"301\",\"textkit\"\n\"301\",\"angular-chart\"\n\"301\",\"megamenu\"\n\"301\",\"iphone-4\"\n\"301\",\"subshell\"\n\"300\",\"flock\"\n\"300\",\"ciimage\"\n\"300\",\"chemistry\"\n\"300\",\"nano\"\n\"300\",\"vehicle-routing\"\n\"300\",\"cakephp-2.x\"\n\"300\",\"django-taggit\"\n\"300\",\"html-editor\"\n\"300\",\"neat\"\n\"300\",\"influxdb-2\"\n\"300\",\"konva\"\n\"300\",\"network-interface\"\n\"300\",\"ubuntu-11.04\"\n\"300\",\"mlr3\"\n\"300\",\"controlsfx\"\n\"300\",\"cg\"\n\"300\",\"parameterized-query\"\n\"300\",\"concept\"\n\"300\",\"glass-mapper\"\n\"300\",\"amazon-cloudtrail\"\n\"300\",\"mediacontroller\"\n\"300\",\"tidytext\"\n\"299\",\"fgetc\"\n\"299\",\"uniswap\"\n\"299\",\"apim\"\n\"299\",\"django-filters\"\n\"299\",\"turfjs\"\n\"299\",\"gtk4\"\n\"299\",\"forth\"\n\"299\",\"extending\"\n\"299\",\"libssh2\"\n\"299\",\"system.net\"\n\"299\",\"node-sqlite3\"\n\"299\",\"burp\"\n\"299\",\"cab\"\n\"299\",\"execcommand\"\n\"299\",\"eureka-forms\"\n\"299\",\"mov\"\n\"299\",\"yui3\"\n\"299\",\"mdc\"\n\"299\",\"solid-js\"\n\"299\",\"lightningchart\"\n\"298\",\"flutter-bottomnavigation\"\n\"298\",\"jsse\"\n\"298\",\"content-assist\"\n\"298\",\"apktool\"\n\"298\",\"css3pie\"\n\"298\",\"sesame\"\n\"298\",\"route-provider\"\n\"298\",\"simplejson\"\n\"298\",\"calllog\"\n\"298\",\"simplepie\"\n\"298\",\"serial-number\"\n\"298\",\"passport-facebook\"\n\"298\",\"kapt\"\n\"298\",\"settings.bundle\"\n\"298\",\"ksoap\"\n\"298\",\"c++-coroutine\"\n\"298\",\"asp.net-core-1.1\"\n\"298\",\"cakebuild\"\n\"298\",\"azure-form-recognizer\"\n\"298\",\"qtspim\"\n\"298\",\"touchableopacity\"\n\"297\",\"template10\"\n\"297\",\"filefield\"\n\"297\",\"sna\"\n\"297\",\"kubeflow-pipelines\"\n\"297\",\"jsdoc3\"\n\"297\",\"cc\"\n\"297\",\"django-pagination\"\n\"297\",\"intptr\"\n\"297\",\"amf\"\n\"297\",\"winforms-interop\"\n\"297\",\"to-date\"\n\"297\",\"google-image-search\"\n\"297\",\"zigbee\"\n\"296\",\"printdocument\"\n\"296\",\"squash\"\n\"296\",\"live-tile\"\n\"296\",\"stacked-bar-chart\"\n\"296\",\"xilinx-ise\"\n\"296\",\"anime.js\"\n\"296\",\"googlevis\"\n\"296\",\"realsense\"\n\"296\",\"kendo-datasource\"\n\"296\",\"onion-architecture\"\n\"296\",\"dwarf\"\n\"296\",\"sat\"\n\"296\",\"960.gs\"\n\"296\",\"rhel6\"\n\"296\",\"sql-date-functions\"\n\"296\",\"spring-webflow-2\"\n\"296\",\"copy-elision\"\n\"296\",\"hmacsha1\"\n\"296\",\"testing-library\"\n\"296\",\"angular-elements\"\n\"296\",\"textureview\"\n\"296\",\"project-template\"\n\"296\",\"logparser\"\n\"296\",\"odoo-view\"\n\"296\",\"ninject.web.mvc\"\n\"296\",\"glsles\"\n\"296\",\"sunspot-solr\"\n\"296\",\"sharpdevelop\"\n\"296\",\"pane\"\n\"295\",\"procfile\"\n\"295\",\"team-explorer\"\n\"295\",\"xtrareport\"\n\"295\",\"django-annotate\"\n\"295\",\"dbase\"\n\"295\",\"adview\"\n\"295\",\"rocket.chat\"\n\"295\",\"factorization\"\n\"295\",\"hypervisor\"\n\"295\",\"asyncfileupload\"\n\"295\",\"bufferedinputstream\"\n\"295\",\"microstrategy\"\n\"295\",\"vm-implementation\"\n\"295\",\"hockeyapp\"\n\"295\",\"polymer-3.x\"\n\"295\",\"pointfree\"\n\"295\",\"omnipay\"\n\"295\",\"using-directives\"\n\"295\",\"webpack-file-loader\"\n\"294\",\"apache-age\"\n\"294\",\"stable-baselines\"\n\"294\",\"relaycommand\"\n\"294\",\"dbmigrate\"\n\"294\",\"smtp-auth\"\n\"294\",\"rpg\"\n\"294\",\"operations\"\n\"294\",\"boost-regex\"\n\"294\",\"sap-iq\"\n\"294\",\"tunneling\"\n\"294\",\"onerror\"\n\"294\",\"spring-logback\"\n\"294\",\"wrappanel\"\n\"294\",\"signal-handling\"\n\"294\",\"abstracttablemodel\"\n\"294\",\"videochat\"\n\"294\",\"richedit\"\n\"294\",\"magento-2.3\"\n\"294\",\"gatsby-image\"\n\"294\",\"c#-to-vb.net\"\n\"294\",\"tinymce-5\"\n\"294\",\"sql-optimization\"\n\"294\",\"husky\"\n\"294\",\"react-css-modules\"\n\"294\",\"long-running-processes\"\n\"294\",\"sony-smartwatch\"\n\"294\",\"soundmanager2\"\n\"294\",\"transactional-replication\"\n\"294\",\"cyanogenmod\"\n\"293\",\"floating-point-conversion\"\n\"293\",\"graph-tool\"\n\"293\",\"tempdata\"\n\"293\",\"representation\"\n\"293\",\"jcifs\"\n\"293\",\"smime\"\n\"293\",\"simplehttpserver\"\n\"293\",\"ndepend\"\n\"293\",\"boost-spirit-x3\"\n\"293\",\"opam\"\n\"293\",\"erlang-shell\"\n\"293\",\"kaa\"\n\"293\",\"godaddy-api\"\n\"293\",\"visual-studio-2003\"\n\"293\",\"outlook-2013\"\n\"293\",\"rabl\"\n\"293\",\"contextmenustrip\"\n\"293\",\"ta-lib\"\n\"293\",\"nuget-server\"\n\"293\",\"peg\"\n\"293\",\"es6-module-loader\"\n\"293\",\"react-dropzone\"\n\"293\",\"daphne\"\n\"292\",\"smali\"\n\"292\",\"yeoman-generator-angular\"\n\"292\",\"multipage\"\n\"292\",\"triplestore\"\n\"292\",\"xmlbeans\"\n\"292\",\"laragon\"\n\"292\",\"rstan\"\n\"292\",\"fancybox-3\"\n\"292\",\"blit\"\n\"292\",\"spring-data-gemfire\"\n\"292\",\"bloom-filter\"\n\"292\",\"invisible-recaptcha\"\n\"292\",\"vimeo-player\"\n\"292\",\"r-dbi\"\n\"292\",\"fragment-tab-host\"\n\"292\",\"audiocontext\"\n\"292\",\"extensibility\"\n\"292\",\"contrast\"\n\"292\",\"gate\"\n\"292\",\"iso8583\"\n\"292\",\"android-ibeacon\"\n\"292\",\"azure-resource-group\"\n\"292\",\"node.js-addon\"\n\"292\",\"accord.net\"\n\"292\",\"charat\"\n\"292\",\"ios-camera\"\n\"292\",\"storefront\"\n\"292\",\"looker\"\n\"292\",\"ppm\"\n\"292\",\"subdocument\"\n\"291\",\"background-subtraction\"\n\"291\",\"livy\"\n\"291\",\"psych\"\n\"291\",\"key-pair\"\n\"291\",\"android-wear-data-api\"\n\"291\",\"openai-whisper\"\n\"291\",\"cortana\"\n\"291\",\"spring-authorization-server\"\n\"291\",\"android-settings\"\n\"291\",\"boost-beast\"\n\"291\",\"brightcove\"\n\"291\",\"mit-scratch\"\n\"291\",\"system.net.mail\"\n\"291\",\"viewwillappear\"\n\"291\",\"observableobject\"\n\"291\",\"playn\"\n\"291\",\"bullet\"\n\"291\",\"mlogit\"\n\"291\",\"xcframework\"\n\"291\",\"hamming-distance\"\n\"291\",\"logitech\"\n\"291\",\"collatz\"\n\"291\",\"pervasive\"\n\"291\",\"mql\"\n\"291\",\"eslint-config-airbnb\"\n\"291\",\"cucumber-serenity\"\n\"291\",\"cyclomatic-complexity\"\n\"291\",\"ie8-compatibility-mode\"\n\"291\",\"mdns\"\n\"291\",\"panic\"\n\"291\",\"paradigms\"\n\"290\",\"nextjs-dynamic-routing\"\n\"290\",\"jsonresponse\"\n\"290\",\"django-cors-headers\"\n\"290\",\"blackberry-playbook\"\n\"290\",\"xmltable\"\n\"290\",\"page-load-time\"\n\"290\",\"pycryptodome\"\n\"290\",\"documents\"\n\"290\",\"variable-length\"\n\"290\",\"superfish\"\n\"290\",\"swift-concurrency\"\n\"290\",\"swc\"\n\"290\",\"visual-studio-sdk\"\n\"290\",\"excel-indirect\"\n\"290\",\"copy-item\"\n\"290\",\"android-compose-textfield\"\n\"290\",\"azure-functions-core-tools\"\n\"290\",\"getresource\"\n\"290\",\"elasticsearch-java-api\"\n\"290\",\"huggingface-datasets\"\n\"290\",\"collider\"\n\"290\",\"flutter-pageview\"\n\"290\",\"static-classes\"\n\"290\",\"zend-view\"\n\"290\",\"arff\"\n\"289\",\"squirrel-sql\"\n\"289\",\"laradock\"\n\"289\",\"lampp\"\n\"289\",\"kubelet\"\n\"289\",\"adfs3.0\"\n\"289\",\"fiware-cygnus\"\n\"289\",\"aero\"\n\"289\",\"dnsmasq\"\n\"289\",\"grails3\"\n\"289\",\"docker-swarm-mode\"\n\"289\",\"winappdriver\"\n\"289\",\"urlclassloader\"\n\"289\",\"bold\"\n\"289\",\"delimited\"\n\"289\",\"inkcanvas\"\n\"289\",\"android-version\"\n\"289\",\"abac\"\n\"289\",\"notice\"\n\"289\",\"pebble-watch\"\n\"289\",\"persistent-volume-claims\"\n\"289\",\"angular2-testing\"\n\"289\",\"change-data-capture\"\n\"289\",\"laravel-lighthouse\"\n\"289\",\"genfromtxt\"\n\"289\",\"eiffel\"\n\"289\",\"google-cloud-tasks\"\n\"289\",\"stockquotes\"\n\"289\",\"msg\"\n\"289\",\"google-my-business-api\"\n\"288\",\"yoast\"\n\"288\",\"fizzbuzz\"\n\"288\",\"nextjs14\"\n\"288\",\"xfce\"\n\"288\",\"animated\"\n\"288\",\"html.actionlink\"\n\"288\",\"jquery-effects\"\n\"288\",\"kartik-v\"\n\"288\",\"password-recovery\"\n\"288\",\"mod-python\"\n\"288\",\"onmouseout\"\n\"288\",\"openejb\"\n\"288\",\"macos-ventura\"\n\"288\",\"libavformat\"\n\"288\",\"mobile-devices\"\n\"288\",\"logitech-gaming-software\"\n\"288\",\"lte\"\n\"288\",\"hybridauth\"\n\"288\",\"charindex\"\n\"288\",\"toolstrip\"\n\"288\",\"sparc\"\n\"288\",\"yui-compressor\"\n\"287\",\"ec2-ami\"\n\"287\",\"skshapenode\"\n\"287\",\"listpreference\"\n\"287\",\"websphere-6.1\"\n\"287\",\"sketchup\"\n\"287\",\"teradatasql\"\n\"287\",\"terminal-services\"\n\"287\",\"adal.js\"\n\"287\",\"affiliate\"\n\"287\",\"marketo\"\n\"287\",\"nhibernate-criteria\"\n\"287\",\"xml.etree\"\n\"287\",\"self-signed-certificate\"\n\"287\",\"containerd\"\n\"287\",\"django-login\"\n\"287\",\"avmutablecomposition\"\n\"287\",\"airtable\"\n\"287\",\"optparse\"\n\"287\",\"karma-coverage\"\n\"287\",\"boost-mpl\"\n\"287\",\"paw-app\"\n\"287\",\"luxon\"\n\"287\",\"pydicom\"\n\"287\",\"c++-modules\"\n\"287\",\"protobuf-java\"\n\"287\",\"column-width\"\n\"287\",\"log4net-appender\"\n\"287\",\"textchanged\"\n\"287\",\"storing-data\"\n\"287\",\"ld-preload\"\n\"287\",\"ios-bluetooth\"\n\"287\",\"leading-zero\"\n\"287\",\"binance-api-client\"\n\"287\",\"ikvm\"\n\"287\",\"bibliography\"\n\"287\",\"thickbox\"\n\"287\",\"bc\"\n\"287\",\"custom-server-controls\"\n\"286\",\"trayicon\"\n\"286\",\"flutter-android\"\n\"286\",\"file-storage\"\n\"286\",\"bitarray\"\n\"286\",\"pairwise\"\n\"286\",\"apm\"\n\"286\",\"bin-packing\"\n\"286\",\"mysql-error-1054\"\n\"286\",\"keyboardinterrupt\"\n\"286\",\"oracle-spatial\"\n\"286\",\"sirishortcuts\"\n\"286\",\"rasterizing\"\n\"286\",\".class-file\"\n\"286\",\"hibernate-entitymanager\"\n\"286\",\"eventual-consistency\"\n\"286\",\"beego\"\n\"285\",\"repr\"\n\"285\",\"cloud-document-ai\"\n\"285\",\"slimdx\"\n\"285\",\"react-slick\"\n\"285\",\"tedious\"\n\"285\",\"full-outer-join\"\n\"285\",\"addchild\"\n\"285\",\"real-mode\"\n\"285\",\"real-time-updates\"\n\"285\",\"docker-in-docker\"\n\"285\",\"writeablebitmap\"\n\"285\",\"tweak\"\n\"285\",\"gssapi\"\n\"285\",\"minmax\"\n\"285\",\"pygraphviz\"\n\"285\",\"typescript-types\"\n\"285\",\"pypyodbc\"\n\"285\",\"broadcasting\"\n\"285\",\"scjp\"\n\"285\",\"nvidia-jetson-nano\"\n\"285\",\"azure-sql-managed-instance\"\n\"285\",\"hk2\"\n\"285\",\"memory-layout\"\n\"285\",\"http-request-parameters\"\n\"285\",\"uv-mapping\"\n\"285\",\"iequalitycomparer\"\n\"284\",\"square-bracket\"\n\"284\",\"dct\"\n\"284\",\"private-methods\"\n\"284\",\"treegrid\"\n\"284\",\"wicket-6\"\n\"284\",\"react-native-gesture-handler\"\n\"284\",\"imei\"\n\"284\",\"lame\"\n\"284\",\"jlink\"\n\"284\",\"julian-date\"\n\"284\",\"spring-restdocs\"\n\"284\",\"gtfs\"\n\"284\",\"openblas\"\n\"284\",\"jquery-ui-tooltip\"\n\"284\",\"spring-security-ldap\"\n\"284\",\"delayed-execution\"\n\"284\",\"android-adapterview\"\n\"284\",\"android-5.1.1-lollipop\"\n\"284\",\"kotlinx.serialization\"\n\"284\",\"spring-statemachine\"\n\"284\",\"r.js\"\n\"284\",\"noscript\"\n\"284\",\"drools-guvnor\"\n\"284\",\"ios-universal-app\"\n\"284\",\"http-status-code-504\"\n\"284\",\"ipsec\"\n\"284\",\"launching-application\"\n\"284\",\"elasticsearch-6\"\n\"284\",\"nock\"\n\"284\",\"array-difference\"\n\"283\",\"fclose\"\n\"283\",\"stackexchange-api\"\n\"283\",\"sendfile\"\n\"283\",\"flet\"\n\"283\",\"soft-keyboard\"\n\"283\",\"voice-recording\"\n\"283\",\"symfony-security\"\n\"283\",\"django-custom-user\"\n\"283\",\"aiml\"\n\"283\",\"windowsiot\"\n\"283\",\"rom\"\n\"283\",\"aws-lambda-edge\"\n\"283\",\"postman-testcase\"\n\"283\",\"azure-cosmosdb-gremlinapi\"\n\"283\",\"path-variables\"\n\"283\",\"exim\"\n\"283\",\"istio-gateway\"\n\"283\",\"dexterity\"\n\"283\",\"diamond-problem\"\n\"283\",\"point-in-polygon\"\n\"283\",\"qlpreviewcontroller\"\n\"283\",\"lcov\"\n\"283\",\"tetris\"\n\"283\",\"ekevent\"\n\"283\",\"spatial-index\"\n\"283\",\"prerequisites\"\n\"283\",\"parent-pom\"\n\"283\",\"idempotent\"\n\"282\",\"react-native-svg\"\n\"282\",\"jformattedtextfield\"\n\"282\",\"github-copilot\"\n\"282\",\"socat\"\n\"282\",\"adobe-edge\"\n\"282\",\"packagist\"\n\"282\",\"unary-operator\"\n\"282\",\"receipt\"\n\"282\",\"durandal-2.0\"\n\"282\",\"set-intersection\"\n\"282\",\"scenarios\"\n\"282\",\"chartjs-2.6.0\"\n\"282\",\"mpeg2-ts\"\n\"282\",\"linear-search\"\n\"282\",\"zen-cart\"\n\"282\",\"preloading\"\n\"281\",\"wia\"\n\"281\",\"flutterflow\"\n\"281\",\"stable-diffusion\"\n\"281\",\"phpwebsocket\"\n\"281\",\"kindle\"\n\"281\",\"rotativa\"\n\"281\",\"aws-sdk-go\"\n\"281\",\"sharepoint-clientobject\"\n\"281\",\"cardinality\"\n\"281\",\"rowfilter\"\n\"281\",\"internal-storage\"\n\"281\",\"gui-testing\"\n\"281\",\"rfcomm\"\n\"281\",\"for-xml-path\"\n\"281\",\"opentracing\"\n\"281\",\"xaf\"\n\"281\",\"scala-reflect\"\n\"281\",\"uint8t\"\n\"281\",\"android-nested-fragment\"\n\"281\",\"longitudinal\"\n\"281\",\"ios8-share-extension\"\n\"281\",\"laravel-valet\"\n\"281\",\"ess\"\n\"281\",\"beeline\"\n\"281\",\"cydia\"\n\"281\",\"begininvoke\"\n\"280\",\"skaffold\"\n\"280\",\"cnosdb\"\n\"280\",\"banner-ads\"\n\"280\",\"weibull\"\n\"280\",\"laravelcollective\"\n\"280\",\"finally\"\n\"280\",\"play-billing-library\"\n\"280\",\"uniform\"\n\"280\",\"avplayeritem\"\n\"280\",\"agents-jade\"\n\"280\",\"wildfly-9\"\n\"280\",\"google-client\"\n\"280\",\".net-remoting\"\n\"280\",\"r-forestplot\"\n\"280\",\"typeid\"\n\"280\",\"pyscript\"\n\"280\",\"android-bottomnav\"\n\"280\",\"devstack\"\n\"280\",\"hibernate-onetomany\"\n\"280\",\"redis-cli\"\n\"280\",\"mkv\"\n\"280\",\"activator\"\n\"280\",\"node.js-fs\"\n\"280\",\"multifile-uploader\"\n\"280\",\"haskell-platform\"\n\"280\",\"fmod\"\n\"280\",\"folderbrowserdialog\"\n\"280\",\"hexo\"\n\"279\",\"class-hierarchy\"\n\"279\",\"sse2\"\n\"279\",\"trendline\"\n\"279\",\"disaster-recovery\"\n\"279\",\"confluence-rest-api\"\n\"279\",\"smartface.io\"\n\"279\",\"ruta\"\n\"279\",\"ibooks\"\n\"279\",\"redbean\"\n\"279\",\"air-native-extension\"\n\"279\",\"hotspot\"\n\"279\",\"jquery-tokeninput\"\n\"279\",\"azure-data-studio\"\n\"279\",\"junit-jupiter\"\n\"279\",\"postgresql-14\"\n\"279\",\"lexical-scope\"\n\"279\",\"objection.js\"\n\"279\",\"systemc\"\n\"279\",\"shadcnui\"\n\"279\",\"redefinition\"\n\"279\",\"hindi\"\n\"279\",\"mlops\"\n\"279\",\"sql-job\"\n\"279\",\"android-mvp\"\n\"279\",\"memory-mapping\"\n\"279\",\"mean.io\"\n\"279\",\"ilist\"\n\"279\",\"tibco-ems\"\n\"278\",\"livechat\"\n\"278\",\"gridster\"\n\"278\",\"temporal\"\n\"278\",\"react-native-elements\"\n\"278\",\"xuggler\"\n\"278\",\"snmp4j\"\n\"278\",\"nextcord\"\n\"278\",\"chromadb\"\n\"278\",\"i386\"\n\"278\",\"icollection\"\n\"278\",\"serve\"\n\"278\",\"twain\"\n\"278\",\"opencmis\"\n\"278\",\"spring-cloud-function\"\n\"278\",\"surefire\"\n\"278\",\"bloodhound\"\n\"278\",\".net-4.6.1\"\n\"278\",\"ribbon-control\"\n\"278\",\"android-date\"\n\"278\",\"highmaps\"\n\"278\",\"android-ble\"\n\"278\",\"tizen-native-app\"\n\"278\",\"hubot\"\n\"278\",\"ctrl\"\n\"278\",\"partition-by\"\n\"278\",\"mui-x-data-grid\"\n\"278\",\"webmail\"\n\"277\",\"gitattributes\"\n\"277\",\"graylog2\"\n\"277\",\"federation\"\n\"277\",\"sql-timestamp\"\n\"277\",\"s3cmd\"\n\"277\",\"k-fold\"\n\"277\",\"inequality\"\n\"277\",\"angularjs-resource\"\n\"277\",\"indoor-positioning-system\"\n\"277\",\"datagridtemplatecolumn\"\n\"277\",\"angular-observable\"\n\"277\",\"ibdesignable\"\n\"277\",\"dynamic-language-runtime\"\n\"277\",\"invoke-webrequest\"\n\"277\",\"code-conversion\"\n\"277\",\"rdkit\"\n\"277\",\"shake\"\n\"277\",\"xcode3.2\"\n\"277\",\"acra\"\n\"277\",\"google-geolocation\"\n\"277\",\"http-put\"\n\"277\",\"access-rights\"\n\"277\",\"lazy-sequences\"\n\"277\",\"zynq\"\n\"277\",\"gmap.net\"\n\"276\",\"mutt\"\n\"276\",\"react-rails\"\n\"276\",\"matlab-coder\"\n\"276\",\"graphql-subscriptions\"\n\"276\",\"django-formwizard\"\n\"276\",\"idataerrorinfo\"\n\"276\",\"data-ingestion\"\n\"276\",\"pushwoosh\"\n\"276\",\"realurl\"\n\"276\",\"jlayeredpane\"\n\"276\",\"coverflow\"\n\"276\",\"pdf-reader\"\n\"276\",\"code-metrics\"\n\"276\",\"org.json\"\n\"276\",\"vml\"\n\"276\",\"uimenucontroller\"\n\"276\",\"android-gesture\"\n\"276\",\"azure-signalr\"\n\"276\",\"taskkill\"\n\"276\",\"stream-processing\"\n\"276\",\"iphone-6-plus\"\n\"276\",\"sheetjs\"\n\"276\",\"autotest\"\n\"276\",\"web.config-transform\"\n\"276\",\"cypress-cucumber-preprocessor\"\n\"275\",\"deedle\"\n\"275\",\"relevance\"\n\"275\",\"react-ref\"\n\"275\",\"ujs\"\n\"275\",\"xml-drawable\"\n\"275\",\"divi\"\n\"275\",\"undirected-graph\"\n\"275\",\"segment-tree\"\n\"275\",\"python-curses\"\n\"275\",\"importrange\"\n\"275\",\"wasm-bindgen\"\n\"275\",\"dmg\"\n\"275\",\"canny-operator\"\n\"275\",\"faraday\"\n\"275\",\"wcag2.0\"\n\"275\",\"roman-numerals\"\n\"275\",\"ontouch\"\n\"275\",\"turtle-rdf\"\n\"275\",\"nattable\"\n\"275\",\"openfl\"\n\"275\",\"domino-designer-eclipse\"\n\"275\",\"outofrangeexception\"\n\"275\",\"pytest-mock\"\n\"275\",\"kotlin-stateflow\"\n\"275\",\"dht\"\n\"275\",\"pytorch-geometric\"\n\"275\",\"mongorestore\"\n\"275\",\"lpsolve\"\n\"275\",\"lazycolumn\"\n\"275\",\"eleventy\"\n\"275\",\"node-inspector\"\n\"275\",\"flutter-video-player\"\n\"275\",\"autolisp\"\n\"274\",\"cmake-gui\"\n\"274\",\"blacklist\"\n\"274\",\"django-1.5\"\n\"274\",\"data-stream\"\n\"274\",\"jpackage\"\n\"274\",\"nested-object\"\n\"274\",\"scalacheck\"\n\"274\",\"delphi-6\"\n\"274\",\"innertext\"\n\"274\",\"coremltools\"\n\"274\",\"hotwire-rails\"\n\"274\",\"onitemclick\"\n\"274\",\"r-corrplot\"\n\"274\",\"codesandbox\"\n\"274\",\"syslog-ng\"\n\"274\",\"netlify-cms\"\n\"274\",\"railscasts\"\n\"274\",\"azure-search-.net-sdk\"\n\"274\",\"g-wan\"\n\"274\",\"location-services\"\n\"274\",\"toml\"\n\"274\",\"ektron\"\n\"274\",\"idhttp\"\n\"274\",\"google-plus-signin\"\n\"274\",\"amazon-dynamodb-index\"\n\"274\",\"heat\"\n\"274\",\"search-box\"\n\"274\",\"sharpziplib\"\n\"274\",\"conemu\"\n\"274\",\"usart\"\n\"273\",\"yii1.x\"\n\"273\",\"jclouds\"\n\"273\",\"decentralized-applications\"\n\"273\",\"sqlyog\"\n\"273\",\"page-title\"\n\"273\",\"ng-zorro-antd\"\n\"273\",\"docking\"\n\"273\",\"djongo\"\n\"273\",\"turbo-c\"\n\"273\",\"android-13\"\n\"273\",\"mahout-recommender\"\n\"273\",\"knockout-mvc\"\n\"273\",\"lenses\"\n\"273\",\"kotlin-exposed\"\n\"273\",\"visual-c#-express-2010\"\n\"273\",\"copy-protection\"\n\"273\",\"busboy\"\n\"273\",\"tld\"\n\"273\",\"highcharts-ng\"\n\"273\",\"resource-files\"\n\"273\",\"across\"\n\"273\",\"quantstrat\"\n\"273\",\"static-typing\"\n\"273\",\"scripting-language\"\n\"272\",\"terminal-emulator\"\n\"272\",\"yocto-recipe\"\n\"272\",\"cloud-storage\"\n\"272\",\"vue-loader\"\n\"272\",\"pkce\"\n\"272\",\"imread\"\n\"272\",\"xlsm\"\n\"272\",\"adsi\"\n\"272\",\"redactor\"\n\"272\",\"axlsx\"\n\"272\",\"opencover\"\n\"272\",\"pdflib\"\n\"272\",\"octobercms-backend\"\n\"272\",\"pyephem\"\n\"272\",\".post\"\n\"272\",\"m2crypto\"\n\"272\",\"nvidia-docker\"\n\"272\",\"nominatim\"\n\"272\",\"spritebuilder\"\n\"272\",\"nunit-console\"\n\"272\",\"ollydbg\"\n\"272\",\"google-cloud-load-balancer\"\n\"272\",\"textformfield\"\n\"272\",\"laravel-permission\"\n\"272\",\"offline-mode\"\n\"272\",\"mtls\"\n\"272\",\"sonos\"\n\"272\",\"traceroute\"\n\"272\",\"emma\"\n\"271\",\"class-template\"\n\"271\",\"weasyprint\"\n\"271\",\"dcast\"\n\"271\",\"clojure-java-interop\"\n\"271\",\"django-1.9\"\n\"271\",\"undefined-index\"\n\"271\",\"jsonresult\"\n\"271\",\"r-tree\"\n\"271\",\"jquery-forms-plugin\"\n\"271\",\"nsautoreleasepool\"\n\"271\",\"opencpu\"\n\"271\",\"applescript-objc\"\n\"271\",\"observablelist\"\n\"271\",\"kotlin-js\"\n\"271\",\"browser-plugin\"\n\"271\",\"null-coalescing-operator\"\n\"271\",\"accumulator\"\n\"271\",\"qbytearray\"\n\"271\",\"hyper\"\n\"271\",\"subscriber\"\n\"271\",\"toplevel\"\n\"271\",\"imagedownload\"\n\"270\",\"mathematical-expressions\"\n\"270\",\"ansi\"\n\"270\",\"cmath\"\n\"270\",\"ng-style\"\n\"270\",\"fileserver\"\n\"270\",\"dynamic-tables\"\n\"270\",\"nested-for-loop\"\n\"270\",\"code-inspection\"\n\"270\",\"rijndaelmanaged\"\n\"270\",\"wix3.8\"\n\"270\",\"uipopover\"\n\"270\",\"xcode5.1\"\n\"270\",\"screensharing\"\n\"270\",\"ios-darkmode\"\n\"270\",\"specman\"\n\"270\",\"getfiles\"\n\"269\",\"barrier\"\n\"269\",\"ffmpeg-php\"\n\"269\",\"template-toolkit\"\n\"269\",\"processstartinfo\"\n\"269\",\"fine-tuning\"\n\"269\",\"kubernetes-deployment\"\n\"269\",\"rx.net\"\n\"269\",\"rxandroidble\"\n\"269\",\"fuzzing\"\n\"269\",\"xmldom\"\n\"269\",\"calico\"\n\"269\",\"document-body\"\n\"269\",\"grafana-variable\"\n\"269\",\"inline-editing\"\n\"269\",\"pdf-conversion\"\n\"269\",\"modern-ui\"\n\"269\",\"core-telephony\"\n\"269\",\"boost-test\"\n\"269\",\"r-exams\"\n\"269\",\"express-router\"\n\"269\",\"abc\"\n\"269\",\"jaspersoft-studio\"\n\"269\",\"exchange-server-2007\"\n\"269\",\"azure-storage-files\"\n\"269\",\"quotation-marks\"\n\"269\",\"sqlgeography\"\n\"269\",\"nlohmann-json\"\n\"269\",\"proj\"\n\"269\",\"resourcemanager\"\n\"269\",\"react-helmet\"\n\"269\",\"imagefilter\"\n\"268\",\"back-testing\"\n\"268\",\"php-internals\"\n\"268\",\"bindparam\"\n\"268\",\"json-query\"\n\"268\",\"sinon-chai\"\n\"268\",\"data-distribution-service\"\n\"268\",\"fail2ban\"\n\"268\",\"dynamic-variables\"\n\"268\",\"aws-sdk-net\"\n\"268\",\"jqxgrid\"\n\"268\",\"degrees\"\n\"268\",\"javafx-webengine\"\n\"268\",\"pydrive\"\n\"268\",\"isolation\"\n\"268\",\"bytesio\"\n\"268\",\"po\"\n\"268\",\"angular-i18n\"\n\"268\",\"node-http-proxy\"\n\"268\",\"mpmediaitem\"\n\"268\",\"accumulo\"\n\"268\",\"comma-operator\"\n\"268\",\"google-talk\"\n\"268\",\"time.h\"\n\"268\",\"medical-imaging\"\n\"267\",\"template-templates\"\n\"267\",\"multiple-choice\"\n\"267\",\"apache-iceberg\"\n\"267\",\"app-id\"\n\"267\",\"symfony-2.7\"\n\"267\",\"ng2-bootstrap\"\n\"267\",\"cscope\"\n\"267\",\"windows-iot-core-10\"\n\"267\",\"wcf-web-api\"\n\"267\",\"facebook-timeline\"\n\"267\",\"spring-cloud-task\"\n\"267\",\"portainer\"\n\"267\",\"hsl\"\n\"267\",\"bootstrap-carousel\"\n\"267\",\"google-apps-script-addon\"\n\"267\",\"wix-react-native-navigation\"\n\"267\",\"uidocument\"\n\"267\",\"ironruby\"\n\"267\",\"geom\"\n\"267\",\"heartbeat\"\n\"267\",\"array-splice\"\n\"267\",\"start-process\"\n\"266\",\"flurl\"\n\"266\",\"maskedtextbox\"\n\"266\",\"photosframework\"\n\"266\",\"webtest\"\n\"266\",\"weak-ptr\"\n\"266\",\"apache-calcite\"\n\"266\",\"underscore.js-templating\"\n\"266\",\"jscrollbar\"\n\"266\",\"laravel-forge\"\n\"266\",\"casbah\"\n\"266\",\"avspeechsynthesizer\"\n\"266\",\"icomparable\"\n\"266\",\"induction\"\n\"266\",\"oracle-coherence\"\n\"266\",\"dynamics-365-operations\"\n\"266\",\"wshttpbinding\"\n\"266\",\"invalid-argument\"\n\"266\",\"svn-checkout\"\n\"266\",\"coin-change\"\n\"266\",\"shadowbox\"\n\"266\",\"viewengine\"\n\"266\",\"pygments\"\n\"266\",\"amazon-selling-partner-api\"\n\"266\",\"m2m\"\n\"266\",\"osascript\"\n\"266\",\"drupal-9\"\n\"266\",\"nsurlsessiondatatask\"\n\"266\",\"nsurlcache\"\n\"266\",\"c#-9.0\"\n\"266\",\"mlp\"\n\"266\",\"mmu\"\n\"266\",\"versions\"\n\"266\",\"trailing-slash\"\n\"266\",\"web-application-firewall\"\n\"266\",\"webflow\"\n\"266\",\"empty-list\"\n\"266\",\"artoolkit\"\n\"266\",\"pascals-triangle\"\n\"265\",\"siteminder\"\n\"265\",\"citations\"\n\"265\",\"locate\"\n\"265\",\"overleaf\"\n\"265\",\"in-clause\"\n\"265\",\"jmenubar\"\n\"265\",\"keras-2\"\n\"265\",\"spring-messaging\"\n\"265\",\"android-studio-2.1\"\n\"265\",\"pcsc\"\n\"265\",\"pcf\"\n\"265\",\"wss-3.0\"\n\"265\",\"azure-devops-pipelines\"\n\"265\",\"mit-scheme\"\n\"265\",\"codesys\"\n\"265\",\"fpu\"\n\"265\",\"azure-vm\"\n\"265\",\"dtrace\"\n\"265\",\"react-modal\"\n\"265\",\"stringtemplate\"\n\"265\",\"alteryx\"\n\"265\",\"torrent\"\n\"264\",\"babel-polyfill\"\n\"264\",\"react-navigation-v6\"\n\"264\",\"prism-4\"\n\"264\",\"skypedeveloper\"\n\"264\",\"fields-for\"\n\"264\",\"bad-alloc\"\n\"264\",\"tripledes\"\n\"264\",\"mutual-exclusion\"\n\"264\",\"int64\"\n\"264\",\"sshfs\"\n\"264\",\"selectionchanged\"\n\"264\",\"unboxing\"\n\"264\",\"dbal\"\n\"264\",\"chutzpah\"\n\"264\",\"jsonstore\"\n\"264\",\"angularjs-ng-change\"\n\"264\",\"ibm-connections\"\n\"264\",\"jira-agile\"\n\"264\",\"azure-app-configuration\"\n\"264\",\"opendir\"\n\"264\",\"salesforce-marketing-cloud\"\n\"264\",\"opa\"\n\"264\",\"azerothcore\"\n\"264\",\"atomikos\"\n\"264\",\"libreoffice-basic\"\n\"264\",\"dom-traversal\"\n\"264\",\"amazon-waf\"\n\"264\",\"fortran-iso-c-binding\"\n\"264\",\"kotlinx.coroutines\"\n\"264\",\"numpy-einsum\"\n\"264\",\"hash-function\"\n\"264\",\"raft\"\n\"264\",\"opengl-es-3.0\"\n\"264\",\"android-push-notification\"\n\"264\",\"memory-limit\"\n\"264\",\"elasticsearch-net\"\n\"264\",\"angular-cli-v6\"\n\"264\",\"mongrel\"\n\"264\",\"google-data-api\"\n\"264\",\"dancer\"\n\"264\",\"gnome-shell\"\n\"263\",\"graphical-logo\"\n\"263\",\"apache-config\"\n\"263\",\"youtube-analytics-api\"\n\"263\",\"catkin\"\n\"263\",\"afnetworking-3\"\n\"263\",\"datepart\"\n\"263\",\"appcelerator-alloy\"\n\"263\",\"mapbox-marker\"\n\"263\",\"circleci-2.0\"\n\"263\",\"data-persistence\"\n\"263\",\"django-q\"\n\"263\",\"avconv\"\n\"263\",\"server-error\"\n\"263\",\"fastq\"\n\"263\",\"aws-aurora-serverless\"\n\"263\",\"uno\"\n\"263\",\"delphi-2006\"\n\"263\",\"mongo-cxx-driver\"\n\"263\",\"jvm-crash\"\n\"263\",\"inputmismatchexception\"\n\"263\",\"dynamics-ax-2012-r2\"\n\"263\",\"typesafe\"\n\"263\",\"wordpress-jetpack\"\n\"263\",\"schematron\"\n\"263\",\"uipasteboard\"\n\"263\",\"uiresponder\"\n\"263\",\"ocunit\"\n\"263\",\"spotlight\"\n\"263\",\"before-filter\"\n\"263\",\"webhdfs\"\n\"263\",\"transaction-isolation\"\n\"263\",\"linkify\"\n\"262\",\"telegram-webhook\"\n\"262\",\"multiple-domains\"\n\"262\",\"yadcf\"\n\"262\",\"edit-distance\"\n\"262\",\"bjam\"\n\"262\",\"runner\"\n\"262\",\"credential-providers\"\n\"262\",\"jmonkeyengine\"\n\"262\",\"dxgi\"\n\"262\",\"guile\"\n\"262\",\"rc\"\n\"262\",\"amazon-swf\"\n\"262\",\"mithril.js\"\n\"262\",\"midlet\"\n\"262\",\"modelbinders\"\n\"262\",\"x87\"\n\"262\",\"numerical-analysis\"\n\"262\",\"nuspec\"\n\"262\",\"cache-manifest\"\n\"262\",\"hiera\"\n\"262\",\"pyusb\"\n\"262\",\"proxmox\"\n\"262\",\"angular2-formbuilder\"\n\"262\",\"spreadsheetgear\"\n\"262\",\"ldd\"\n\"262\",\"authorize\"\n\"262\",\"webdriver-manager\"\n\"262\",\"batterylevel\"\n\"262\",\"helix-3d-toolkit\"\n\"261\",\"react-server-components\"\n\"261\",\"lmdb\"\n\"261\",\"yarn-v2\"\n\"261\",\"dead-letter\"\n\"261\",\"debian-buster\"\n\"261\",\"filenet-p8\"\n\"261\",\"react-quill\"\n\"261\",\"photokit\"\n\"261\",\"cng\"\n\"261\",\"connect-by\"\n\"261\",\"nexus-7\"\n\"261\",\"file-structure\"\n\"261\",\"page-tables\"\n\"261\",\"flash-cs3\"\n\"261\",\"data-generation\"\n\"261\",\"datagridviewcombobox\"\n\"261\",\"angularjs-orderby\"\n\"261\",\"recoiljs\"\n\"261\",\"angular-unit-test\"\n\"261\",\"natural-sort\"\n\"261\",\"botocore\"\n\"261\",\"magnetometer\"\n\"261\",\"face-api\"\n\"261\",\"system-properties\"\n\"261\",\"hapi-fhir\"\n\"261\",\"plsql-package\"\n\"261\",\"taylor-series\"\n\"261\",\"qlayout\"\n\"261\",\"http-proxy-middleware\"\n\"261\",\"lcdui\"\n\"261\",\"google-fit-sdk\"\n\"261\",\"ipopt\"\n\"261\",\"ursina\"\n\"261\",\"here-maps-rest\"\n\"261\",\"web-forms-for-marketers\"\n\"261\",\"armv8\"\n\"260\",\"mutiny\"\n\"260\",\"procedures\"\n\"260\",\"jetpack-compose-navigation\"\n\"260\",\"maven-central\"\n\"260\",\"citrus-framework\"\n\"260\",\"contentobserver\"\n\"260\",\"django-2.2\"\n\"260\",\"socket-timeout-exception\"\n\"260\",\"dbexpress\"\n\"260\",\"undeclared-identifier\"\n\"260\",\"chatterbot\"\n\"260\",\"variable-names\"\n\"260\",\"vagrant-windows\"\n\"260\",\"pushsharp\"\n\"260\",\"optgroup\"\n\"260\",\"turborepo\"\n\"260\",\"arabic-support\"\n\"260\",\"nsexception\"\n\"260\",\"e4x\"\n\"260\",\"android-async-http\"\n\"260\",\"view-scope\"\n\"260\",\"rating-system\"\n\"260\",\"tnsnames\"\n\"260\",\"elastica\"\n\"260\",\"google-cloud-networking\"\n\"260\",\"restler\"\n\"260\",\"iphone-standalone-web-app\"\n\"260\",\"google-cloud-console\"\n\"260\",\"cgi-bin\"\n\"260\",\"spark-streaming-kafka\"\n\"260\",\"parallel.for\"\n\"259\",\"remap\"\n\"259\",\"relocation\"\n\"259\",\"ssms-2012\"\n\"259\",\"basichttpbinding\"\n\"259\",\"data-science-experience\"\n\"259\",\"distributed-tracing\"\n\"259\",\"laravel-api\"\n\"259\",\"xfbml\"\n\"259\",\"xp-cmdshell\"\n\"259\",\"owin-middleware\"\n\"259\",\"pycairo\"\n\"259\",\"microsoft-edge-extension\"\n\"259\",\"vcenter\"\n\"259\",\"farsi\"\n\"259\",\"blink\"\n\"259\",\"model-view\"\n\"259\",\"android-tabactivity\"\n\"259\",\"cppunit\"\n\"259\",\"html-heading\"\n\"259\",\"nwjs\"\n\"259\",\"wolframalpha\"\n\"259\",\"shim\"\n\"259\",\"mime-message\"\n\"259\",\"orb\"\n\"259\",\"buefy\"\n\"259\",\"viewdidappear\"\n\"259\",\"azure-traffic-manager\"\n\"259\",\"ack\"\n\"259\",\"merge-replication\"\n\"259\",\"license-key\"\n\"259\",\"thumb\"\n\"259\",\"liferay-aui\"\n\"259\",\"statistical-test\"\n\"259\",\"headset\"\n\"258\",\"listiterator\"\n\"258\",\"math.net\"\n\"258\",\"dataoutputstream\"\n\"258\",\"run-length-encoding\"\n\"258\",\"language-model\"\n\"258\",\"imageurl\"\n\"258\",\"angular-upgrade\"\n\"258\",\"simple-schema\"\n\"258\",\"jitpack\"\n\"258\",\"pthread-join\"\n\"258\",\"jmock\"\n\"258\",\"jpopupmenu\"\n\"258\",\"dynamic-binding\"\n\"258\",\"ooad\"\n\"258\",\"dynamic-content\"\n\"258\",\"boost-multi-index\"\n\"258\",\"wsadmin\"\n\"258\",\"sap-cloud-platform\"\n\"258\",\"winrar\"\n\"258\",\"rights\"\n\"258\",\"android-7.1-nougat\"\n\"258\",\"osrm\"\n\"258\",\"mixed\"\n\"258\",\"nucleo\"\n\"258\",\"proxy-server\"\n\"258\",\"react-native-cli\"\n\"258\",\"nmea\"\n\"258\",\"panels\"\n\"258\",\"auto-populate\"\n\"258\",\"conduit\"\n\"258\",\"partial-application\"\n\"258\",\"embedded-v8\"\n\"257\",\"react-native-reanimated-v2\"\n\"257\",\"transliteration\"\n\"257\",\"local-database\"\n\"257\",\"cncontact\"\n\"257\",\"templatetags\"\n\"257\",\"multivariate-testing\"\n\"257\",\"webusercontrol\"\n\"257\",\"ng-packagr\"\n\"257\",\"pitch\"\n\"257\",\"imx6\"\n\"257\",\"constructor-injection\"\n\"257\",\"fileshare\"\n\"257\",\"unity3d-editor\"\n\"257\",\"socketchannel\"\n\"257\",\"factory-method\"\n\"257\",\"favorites\"\n\"257\",\"anko\"\n\"257\",\"rollapply\"\n\"257\",\"swfupload\"\n\"257\",\"kali-linux\"\n\"257\",\"nslocale\"\n\"257\",\"on-screen-keyboard\"\n\"257\",\"nsinteger\"\n\"257\",\"setting\"\n\"257\",\"sigmoid\"\n\"257\",\"sphinx4\"\n\"257\",\"projectile\"\n\"257\",\"zio\"\n\"257\",\"zingchart\"\n\"257\",\"qtquickcontrols\"\n\"257\",\"autoresizingmask\"\n\"257\",\"mui-x\"\n\"257\",\"prettyfaces\"\n\"257\",\"flutterwebviewplugin\"\n\"257\",\"suffix\"\n\"257\",\"premake\"\n\"256\",\"ant-contrib\"\n\"256\",\"data-sharing\"\n\"256\",\"mapkitannotation\"\n\"256\",\"sendinput\"\n\"256\",\"cbc-mode\"\n\"256\",\"google-vpc\"\n\"256\",\"cannot-find-symbol\"\n\"256\",\"angular-leaflet-directive\"\n\"256\",\"dwm\"\n\"256\",\"jquery-svg\"\n\"256\",\"kanban\"\n\"256\",\"random-effects\"\n\"256\",\"halide\"\n\"256\",\"leap-year\"\n\"256\",\"laravel-mail\"\n\"256\",\"solid-state-drive\"\n\"256\",\"arcgis-server\"\n\"256\",\"sonarcloud\"\n\"256\",\"ie-developer-tools\"\n\"256\",\"user-defined\"\n\"255\",\"integrate\"\n\"255\",\"truncated\"\n\"255\",\"reply\"\n\"255\",\"apache-curator\"\n\"255\",\"backbone-relational\"\n\"255\",\"swiftui-animation\"\n\"255\",\"bisection\"\n\"255\",\"selenium-iedriver\"\n\"255\",\"python-c-extension\"\n\"255\",\"appendto\"\n\"255\",\"unsafe-pointers\"\n\"255\",\"aws-pinpoint\"\n\"255\",\"microsoft365\"\n\"255\",\"django-managers\"\n\"255\",\"watchconnectivity\"\n\"255\",\"nslookup\"\n\"255\",\"moles\"\n\"255\",\"otrs\"\n\"255\",\"abstract-factory\"\n\"255\",\"google-ad-manager\"\n\"255\",\"javascript-automation\"\n\"255\",\"setuid\"\n\"255\",\"setlocale\"\n\"255\",\"redhawksdr\"\n\"255\",\"sqlite.swift\"\n\"255\",\"qmenu\"\n\"255\",\"stormpath\"\n\"255\",\"pyttsx3\"\n\"255\",\"event-driven-design\"\n\"255\",\"strncpy\"\n\"255\",\"hwioauthbundle\"\n\"255\",\"activity-diagram\"\n\"255\",\"string-search\"\n\"255\",\"terraform-template-file\"\n\"255\",\"getaddrinfo\"\n\"255\",\"webots\"\n\"255\",\"zfs\"\n\"255\",\"predis\"\n\"255\",\"pass-data\"\n\"254\",\"tel\"\n\"254\",\"deck.gl\"\n\"254\",\"procedural-programming\"\n\"254\",\"dde\"\n\"254\",\"xelatex\"\n\"254\",\"pairing\"\n\"254\",\"console.writeline\"\n\"254\",\"variable-expansion\"\n\"254\",\"rotatetransform\"\n\"254\",\"puppet-enterprise\"\n\"254\",\"docpad\"\n\"254\",\"negation\"\n\"254\",\"entity-framework-6.1\"\n\"254\",\"openfeign\"\n\"254\",\"facebook-graph-api-v2.0\"\n\"254\",\"ringcentral\"\n\"254\",\"random-walk\"\n\"254\",\"bundle-identifier\"\n\"254\",\"nstimezone\"\n\"254\",\"no-www\"\n\"254\",\"plone-4.x\"\n\"254\",\"vga\"\n\"254\",\"plunker\"\n\"254\",\"stochastic\"\n\"254\",\"chapel\"\n\"254\",\"menustrip\"\n\"254\",\"ctime\"\n\"254\",\"google-maps-flutter\"\n\"254\",\"tracker\"\n\"254\",\"completion\"\n\"254\",\"ideavim\"\n\"254\",\"torque\"\n\"254\",\"flyout\"\n\"253\",\"tree-shaking\"\n\"253\",\"eclipse-gef\"\n\"253\",\"dbgrid\"\n\"253\",\"react-native-textinput\"\n\"253\",\"socket.io-1.0\"\n\"253\",\"xmp\"\n\"253\",\"snapchat\"\n\"253\",\"microsoft-band\"\n\"253\",\"svn-externals\"\n\"253\",\"bot-framework-composer\"\n\"253\",\"sammy.js\"\n\"253\",\"code-editor\"\n\"253\",\"ubuntu-11.10\"\n\"253\",\"android-binder\"\n\"253\",\"f#-3.0\"\n\"253\",\"opentsdb\"\n\"253\",\"tabnavigator\"\n\"253\",\"vespa\"\n\"253\",\"poedit\"\n\"253\",\"companion-object\"\n\"253\",\"geometry-surface\"\n\"253\",\"office-2010\"\n\"253\",\"react-forms\"\n\"253\",\"automapping\"\n\"253\",\"gke-networking\"\n\"252\",\"groovyshell\"\n\"252\",\"backendless\"\n\"252\",\"clause\"\n\"252\",\"fitbit\"\n\"252\",\"apiblueprint\"\n\"252\",\"uiviewrepresentable\"\n\"252\",\"nanohttpd\"\n\"252\",\"dart-webui\"\n\"252\",\"window-handles\"\n\"252\",\"nativescript-plugin\"\n\"252\",\"nco\"\n\"252\",\"mongoexport\"\n\"252\",\"mixed-mode\"\n\"252\",\"magento-2.0\"\n\"252\",\"type-punning\"\n\"252\",\"r-faq\"\n\"252\",\"opentype\"\n\"252\",\"isapi-rewrite\"\n\"252\",\"open-policy-agent\"\n\"252\",\"sql-null\"\n\"252\",\"sql-server-ce-4\"\n\"252\",\"c1-cms\"\n\"252\",\"strawberry-perl\"\n\"252\",\"custom-events\"\n\"252\",\"staticresource\"\n\"252\",\"glusterfs\"\n\"252\",\"multilinestring\"\n\"252\",\"eml\"\n\"251\",\"markov\"\n\"251\",\"php-include\"\n\"251\",\"barcode-printing\"\n\"251\",\"laminas-api-tools\"\n\"251\",\"mapbox-ios\"\n\"251\",\"ng2-smart-table\"\n\"251\",\"adaboost\"\n\"251\",\"mantis\"\n\"251\",\"simplification\"\n\"251\",\"robospice\"\n\"251\",\"data-entry\"\n\"251\",\"jqgrid-formatter\"\n\"251\",\"wso2-cep\"\n\"251\",\"detours\"\n\"251\",\"wso2-micro-integrator\"\n\"251\",\"tuckey-urlrewrite-filter\"\n\"251\",\"amazon-textract\"\n\"251\",\"shortcut-file\"\n\"251\",\"java-metro-framework\"\n\"251\",\"vichuploaderbundle\"\n\"251\",\"amd-gpu\"\n\"251\",\"scatter3d\"\n\"251\",\"tcpsocket\"\n\"251\",\"scintilla\"\n\"251\",\"splitter\"\n\"251\",\"test-data\"\n\"251\",\"propagation\"\n\"251\",\"geom-point\"\n\"251\",\"laravel-scout\"\n\"251\",\"google-deployment-manager\"\n\"251\",\"throws\"\n\"251\",\"urlrequest\"\n\"251\",\"archiva\"\n\"250\",\"cloud-sql-proxy\"\n\"250\",\"py4j\"\n\"250\",\"google-trends\"\n\"250\",\"faunadb\"\n\"250\",\"windows-mixed-reality\"\n\"250\",\"criteriaquery\"\n\"250\",\"capacity\"\n\"250\",\"cakephp-2.2\"\n\"250\",\"aws-certificate-manager\"\n\"250\",\"open-closed-principle\"\n\"250\",\"dynamics-crm-webapi\"\n\"250\",\"bootstrap-grid\"\n\"250\",\"gulp-uglify\"\n\"250\",\"blockui\"\n\"250\",\"libclang\"\n\"250\",\"forkjoinpool\"\n\"250\",\"viewport-units\"\n\"250\",\"virtual-destructor\"\n\"250\",\"assetbundle\"\n\"250\",\"spork\"\n\"250\",\"beep\"\n\"250\",\"sparklines\"\n\"250\",\"zone\"\n\"250\",\"sugarorm\"\n\"249\",\"flask-mail\"\n\"249\",\"keyboard-layout\"\n\"249\",\"jitsi-meet\"\n\"249\",\"wal\"\n\"249\",\"winbugs\"\n\"249\",\"single-instance\"\n\"249\",\"calabash-android\"\n\"249\",\"warden\"\n\"249\",\"dataform\"\n\"249\",\"jmeter-maven-plugin\"\n\"249\",\"red\"\n\"249\",\"hotswap\"\n\"249\",\"spring-orm\"\n\"249\",\"visualizer\"\n\"249\",\"osx-server\"\n\"249\",\"letters\"\n\"249\",\"object-oriented-database\"\n\"249\",\"regsvr32\"\n\"249\",\"podcast\"\n\"249\",\"openjdk-11\"\n\"249\",\"devise-confirmable\"\n\"249\",\"hashicorp\"\n\"249\",\"radcombobox\"\n\"249\",\"pointer-events\"\n\"249\",\"rad-studio\"\n\"249\",\"hive-metastore\"\n\"249\",\"actionfilterattribute\"\n\"249\",\"python-watchdog\"\n\"249\",\"locked\"\n\"249\",\"dailymotion-api\"\n\"249\",\"amazon-elasticsearch\"\n\"249\",\"scroller\"\n\"248\",\"baseline\"\n\"248\",\"cleardb\"\n\"248\",\"webpage-screenshot\"\n\"248\",\"editorfor\"\n\"248\",\"django-cache\"\n\"248\",\"blast\"\n\"248\",\"sequelpro\"\n\"248\",\"purely-functional\"\n\"248\",\"cross-site\"\n\"248\",\"svelte-store\"\n\"248\",\"sigterm\"\n\"248\",\"html4\"\n\"248\",\"mod-pagespeed\"\n\"248\",\"turbo\"\n\"248\",\"spring-mongo\"\n\"248\",\"nsimageview\"\n\"248\",\"cowboy\"\n\"248\",\"bootstrap-accordion\"\n\"248\",\"lzma\"\n\"248\",\"outlook-2016\"\n\"248\",\"setwindowshookex\"\n\"248\",\"kohana-orm\"\n\"248\",\"xcode9-beta\"\n\"248\",\"sproutcore\"\n\"248\",\"tomahawk\"\n\"248\",\"generate-series\"\n\"248\",\"mpeg-4\"\n\"248\",\"cfquery\"\n\"248\",\"tomcat5.5\"\n\"248\",\"odac\"\n\"248\",\"mbunit\"\n\"248\",\"conditional-comments\"\n\"248\",\"beyondcompare\"\n\"247\",\"sql-to-linq-conversion\"\n\"247\",\"flex-mobile\"\n\"247\",\"white-framework\"\n\"247\",\"anonymous-inner-class\"\n\"247\",\"greenrobot-eventbus\"\n\"247\",\"gridlines\"\n\"247\",\"pinax\"\n\"247\",\"bitrate\"\n\"247\",\"datastep\"\n\"247\",\"ngx-charts\"\n\"247\",\"cdo.message\"\n\"247\",\"avplayerlayer\"\n\"247\",\"ibm-blockchain\"\n\"247\",\"docker-multi-stage-build\"\n\"247\",\"ajp\"\n\"247\",\"spring-mvc-test\"\n\"247\",\"shrink\"\n\"247\",\"visual-studio-setup-proje\"\n\"247\",\"lego-mindstorms\"\n\"247\",\"differentiation\"\n\"247\",\"gwt-platform\"\n\"247\",\"olingo\"\n\"247\",\"custom-data-type\"\n\"247\",\"google-cloud-kms\"\n\"247\",\"hunspell\"\n\"247\",\"party\"\n\"247\",\"static-content\"\n\"247\",\"compiler-options\"\n\"247\",\"ignite-ui\"\n\"246\",\"gitlab-ci.yml\"\n\"246\",\"apache-commons-fileupload\"\n\"246\",\"background-fetch\"\n\"246\",\"telebot\"\n\"246\",\"dead-code\"\n\"246\",\"marmalade\"\n\"246\",\"getview\"\n\"246\",\"finite-element-analysis\"\n\"246\",\"confluent-kafka-python\"\n\"246\",\"python-cryptography\"\n\"246\",\"django-2.1\"\n\"246\",\"rrule\"\n\"246\",\"mysql-5.5\"\n\"246\",\"camunda-modeler\"\n\"246\",\"react-suspense\"\n\"246\",\"oracleclient\"\n\"246\",\"kdevelop\"\n\"246\",\"app-transport-security\"\n\"246\",\"polymer-starter-kit\"\n\"246\",\"inventory-management\"\n\"246\",\"ion-auth\"\n\"246\",\"mojo\"\n\"246\",\"dynatree\"\n\"246\",\"format-string\"\n\"246\",\"google-artifact-registry\"\n\"246\",\"foselasticabundle\"\n\"246\",\"codeigniter-datamapper\"\n\"246\",\"sigma.js\"\n\"246\",\"pfobject\"\n\"246\",\"asgi\"\n\"246\",\"touchesmoved\"\n\"246\",\"linq-to-twitter\"\n\"246\",\"amazon-linux-2\"\n\"246\",\"flutter-http\"\n\"246\",\"usb-debugging\"\n\"245\",\"cisco-ios\"\n\"245\",\"vs-web-application-project\"\n\"245\",\"phpoffice\"\n\"245\",\"transport\"\n\"245\",\"vuepress\"\n\"245\",\"uitest\"\n\"245\",\"flac\"\n\"245\",\"chronicle\"\n\"245\",\"cartesian\"\n\"245\",\"binaryreader\"\n\"245\",\"puppeteer-sharp\"\n\"245\",\"bounded-wildcard\"\n\"245\",\"hotlinking\"\n\"245\",\"openedx\"\n\"245\",\"openwhisk\"\n\"245\",\"uipinchgesturerecognizer\"\n\"245\",\"model-checking\"\n\"245\",\"executenonquery\"\n\"245\",\"non-deterministic\"\n\"245\",\"contingency\"\n\"245\",\"cursors\"\n\"245\",\"q-lang\"\n\"245\",\"omniauth-facebook\"\n\"245\",\"metaplex\"\n\"245\",\"argc\"\n\"245\",\"emacs23\"\n\"245\",\"spark-ar-studio\"\n\"245\",\"sun\"\n\"245\",\"ihttphandler\"\n\"245\",\"maven-profiles\"\n\"244\",\"php-5.2\"\n\"244\",\"mutators\"\n\"244\",\"clistctrl\"\n\"244\",\"github-enterprise\"\n\"244\",\"xmlslurper\"\n\"244\",\"file-put-contents\"\n\"244\",\"wasapi\"\n\"244\",\"unity3d-unet\"\n\"244\",\"google-workspace-add-ons\"\n\"244\",\"upcasting\"\n\"244\",\"guidewire\"\n\"244\",\"dynamic-typing\"\n\"244\",\"appx\"\n\"244\",\"jqwidget\"\n\"244\",\"fragment-identifier\"\n\"244\",\"shadowing\"\n\"244\",\"netstream\"\n\"244\",\"codeigniter-routing\"\n\"244\",\"t4mvc\"\n\"244\",\"tabbedpage\"\n\"244\",\"java-ee-5\"\n\"244\",\"isodate\"\n\"244\",\"sp-send-dbmail\"\n\"244\",\"openzeppelin\"\n\"244\",\"exchangelib\"\n\"244\",\"openvswitch\"\n\"244\",\"azure-vpn\"\n\"244\",\"google-groups\"\n\"244\",\"sorl-thumbnail\"\n\"243\",\"mathnet-numerics\"\n\"243\",\"intel-pin\"\n\"243\",\"printing-web-page\"\n\"243\",\"sitemesh\"\n\"243\",\"flexigrid\"\n\"243\",\"flow-router\"\n\"243\",\"multiplexing\"\n\"243\",\"lit-html\"\n\"243\",\"integer-promotion\"\n\"243\",\"socrata\"\n\"243\",\"json-web-token\"\n\"243\",\"python-ldap\"\n\"243\",\"snapkit\"\n\"243\",\"ng-bind-html\"\n\"243\",\"childviewcontroller\"\n\"243\",\"cross-origin-read-blocking\"\n\"243\",\"reasoning\"\n\"243\",\"vacuum\"\n\"243\",\"postconstruct\"\n\"243\",\"openbsd\"\n\"243\",\"sbatch\"\n\"243\",\"grunt-contrib-uglify\"\n\"243\",\"mongo-go\"\n\"243\",\"pbx\"\n\"243\",\"interlocked\"\n\"243\",\"nested-table\"\n\"243\",\"appium-desktop\"\n\"243\",\"google-ai-platform\"\n\"243\",\"google-authenticator\"\n\"243\",\"dojox.grid\"\n\"243\",\"osc\"\n\"243\",\"external-accessory\"\n\"243\",\"brightway\"\n\"243\",\"libpqxx\"\n\"243\",\"jasmine-jquery\"\n\"243\",\"6502\"\n\"243\",\"analytic-functions\"\n\"243\",\"pointer-to-pointer\"\n\"243\",\"nlb\"\n\"243\",\"nlp-question-answering\"\n\"243\",\"nitrousio\"\n\"243\",\"access-log\"\n\"243\",\"access-specifier\"\n\"243\",\"angular2-highcharts\"\n\"243\",\"preferencefragment\"\n\"243\",\"ilogger\"\n\"243\",\"autoformatting\"\n\"243\",\"automationanywhere\"\n\"243\",\"encog\"\n\"242\",\"jaydata\"\n\"242\",\"antlrworks\"\n\"242\",\"pgpool\"\n\"242\",\"pacman\"\n\"242\",\"select-string\"\n\"242\",\"sniffer\"\n\"242\",\"aws-glue-spark\"\n\"242\",\"ajax.beginform\"\n\"242\",\"ruby-datamapper\"\n\"242\",\"android-support-design\"\n\"242\",\"nsdecimalnumber\"\n\"242\",\"satellite\"\n\"242\",\"atlas\"\n\"242\",\"code-push\"\n\"242\",\"builder-pattern\"\n\"242\",\"typescript-compiler-api\"\n\"242\",\"retina\"\n\"242\",\"raise\"\n\"242\",\"sysfs\"\n\"242\",\"68000\"\n\"242\",\"libx264\"\n\"242\",\"drupal-forms\"\n\"242\",\"mixing\"\n\"242\",\"hashchange\"\n\"242\",\"text-manipulation\"\n\"242\",\"spatialite\"\n\"242\",\"glassfish-4.1\"\n\"242\",\"sonarqube-ops\"\n\"242\",\"predicatebuilder\"\n\"242\",\"heading\"\n\"242\",\"qtmultimedia\"\n\"241\",\"stackblitz\"\n\"241\",\"procfs\"\n\"241\",\"vscode-code-runner\"\n\"241\",\"apn\"\n\"241\",\"unchecked\"\n\"241\",\"dashing\"\n\"241\",\"recycle-bin\"\n\"241\",\"dms\"\n\"241\",\"kibana-7\"\n\"241\",\"vao\"\n\"241\",\"cracking\"\n\"241\",\"spring-boot-starter\"\n\"241\",\"jquery-ui-selectable\"\n\"241\",\"pooling\"\n\"241\",\"howler.js\"\n\"241\",\"application-restart\"\n\"241\",\"twitter-bootstrap-tooltip\"\n\"241\",\"juce\"\n\"241\",\"lexicographic\"\n\"241\",\"codeceptjs\"\n\"241\",\"virtual-hosts\"\n\"241\",\"netflix-ribbon\"\n\"241\",\"reflector\"\n\"241\",\"notificationcenter\"\n\"241\",\"dev-to-production\"\n\"241\",\"expo-router\"\n\"241\",\"custom-exceptions\"\n\"241\",\"geos\"\n\"241\",\"ejb-2.x\"\n\"241\",\"sonata-user-bundle\"\n\"241\",\"zappa\"\n\"241\",\"quantifiers\"\n\"240\",\"eclipse-oxygen\"\n\"240\",\"filamentphp\"\n\"240\",\"print-preview\"\n\"240\",\"transitive-dependency\"\n\"240\",\"mapfragment\"\n\"240\",\"malware-detection\"\n\"240\",\"safearealayoutguide\"\n\"240\",\"blank-line\"\n\"240\",\"database-permissions\"\n\"240\",\"unlock\"\n\"240\",\"aws-policies\"\n\"240\",\"false-positive\"\n\"240\",\"single-spa\"\n\"240\",\"grpc-web\"\n\"240\",\"hash-collision\"\n\"240\",\"gazebo-simu\"\n\"240\",\"dryioc\"\n\"240\",\"asp.net-core-viewcomponent\"\n\"240\",\"restcomm\"\n\"240\",\"command-substitution\"\n\"240\",\"oembed\"\n\"240\",\"httpservice\"\n\"240\",\"zsh-completion\"\n\"240\",\"project-organization\"\n\"240\",\"hexagonal-tiles\"\n\"240\",\"zeroclipboard\"\n\"240\",\"qtsql\"\n\"240\",\"hdp\"\n\"240\",\"ti-basic\"\n\"239\",\"fcntl\"\n\"239\",\"phase\"\n\"239\",\"producer\"\n\"239\",\"dispatch-queue\"\n\"239\",\"software-quality\"\n\"239\",\"bins\"\n\"239\",\"pinch\"\n\"239\",\"chunked\"\n\"239\",\"ruby-on-rails-6.1\"\n\"239\",\"rowwise\"\n\"239\",\"pycaret\"\n\"239\",\"intro.js\"\n\"239\",\"coremidi\"\n\"239\",\"application-design\"\n\"239\",\"android-selector\"\n\"239\",\"jwplayer6\"\n\"239\",\"wtl\"\n\"239\",\"delve\"\n\"239\",\"orchardcms-1.8\"\n\"239\",\"google-books\"\n\"239\",\"raylib\"\n\"239\",\"double-precision\"\n\"239\",\"tinymce-plugins\"\n\"239\",\"non-nullable\"\n\"239\",\"monoids\"\n\"239\",\"promisekit\"\n\"239\",\"logic-programming\"\n\"239\",\"passkit\"\n\"239\",\"alpha-vantage\"\n\"238\",\"react-native-scrollview\"\n\"238\",\"react-native-stylesheet\"\n\"238\",\"material-design-in-xaml\"\n\"238\",\"ecmascript-harmony\"\n\"238\",\"construct\"\n\"238\",\"xhtml2pdf\"\n\"238\",\"ngoninit\"\n\"238\",\"data-munging\"\n\"238\",\"sentence-transformers\"\n\"238\",\"rx-cocoa\"\n\"238\",\"rx-kotlin\"\n\"238\",\"django-media\"\n\"238\",\"rebar\"\n\"238\",\"cakephp-2.5\"\n\"238\",\"craftcms\"\n\"238\",\"payumoney\"\n\"238\",\"bluestacks\"\n\"238\",\"m4a\"\n\"238\",\"device-admin\"\n\"238\",\"exp\"\n\"238\",\"xcode6.3\"\n\"238\",\"eventaggregator\"\n\"238\",\"event-propagation\"\n\"238\",\"reactable\"\n\"238\",\"propertyinfo\"\n\"238\",\"loopj\"\n\"238\",\"text-based\"\n\"238\",\"log-likelihood\"\n\"237\",\"flutter-appbar\"\n\"237\",\"repeatingalarm\"\n\"237\",\"inbox\"\n\"237\",\"selenium2library\"\n\"237\",\"method-signature\"\n\"237\",\"crossrider\"\n\"237\",\"facebox\"\n\"237\",\"ndis\"\n\"237\",\"jquery-click-event\"\n\"237\",\"superglobals\"\n\"237\",\"delta-live-tables\"\n\"237\",\"victory-charts\"\n\"237\",\"libphonenumber\"\n\"237\",\"video-thumbnails\"\n\"237\",\"jar-signing\"\n\"237\",\"xcasset\"\n\"237\",\"isinstance\"\n\"237\",\"performselector\"\n\"237\",\"geth\"\n\"237\",\"argumentexception\"\n\"237\",\"sublimerepl\"\n\"236\",\"wicket-1.5\"\n\"236\",\"slack-commands\"\n\"236\",\"gremlinpython\"\n\"236\",\"mailx\"\n\"236\",\"incompatibletypeerror\"\n\"236\",\"pandasql\"\n\"236\",\"fileutils\"\n\"236\",\"cropperjs\"\n\"236\",\"animate-cc\"\n\"236\",\"wikitude\"\n\"236\",\"icomparer\"\n\"236\",\"routed-events\"\n\"236\",\"blockingcollection\"\n\"236\",\"ionic-react\"\n\"236\",\"invocationtargetexception\"\n\"236\",\"hpa\"\n\"236\",\"borland-c++\"\n\"236\",\"navmesh\"\n\"236\",\"onsaveinstancestate\"\n\"236\",\"form-fields\"\n\"236\",\"lftp\"\n\"236\",\"rhodes\"\n\"236\",\"broken-pipe\"\n\"236\",\"right-join\"\n\"236\",\"regasm\"\n\"236\",\"non-clustered-index\"\n\"236\",\"xbrl\"\n\"236\",\"null-layout-manager\"\n\"236\",\"gatsby-plugin\"\n\"236\",\"android-chips\"\n\"236\",\"react-devtools\"\n\"236\",\"ios-app-group\"\n\"236\",\"projection-matrix\"\n\"236\",\"node-crypto\"\n\"236\",\"sp-executesql\"\n\"236\",\"sudoers\"\n\"236\",\"quadratic-programming\"\n\"236\",\"footable\"\n\"236\",\"energy\"\n\"236\",\"qstandarditemmodel\"\n\"236\",\"google-identity-toolkit\"\n\"236\",\"thread-priority\"\n\"236\",\"pango\"\n\"236\",\"alloy-ui\"\n\"235\",\"xwpf\"\n\"235\",\"apache-commons-logging\"\n\"235\",\"react-redux-form\"\n\"235\",\"apacheds\"\n\"235\",\"json-normalize\"\n\"235\",\"childwindow\"\n\"235\",\"json-simple\"\n\"235\",\"const-cast\"\n\"235\",\"imessage-extension\"\n\"235\",\"cbcentralmanager\"\n\"235\",\"aws-cloudformation-custom-resource\"\n\"235\",\"cakeyframeanimation\"\n\"235\",\"rserve\"\n\"235\",\"vector-database\"\n\"235\",\"jpos\"\n\"235\",\"input-mask\"\n\"235\",\"horizontal-scaling\"\n\"235\",\"mining\"\n\"235\",\"winrt-async\"\n\"235\",\"visual-studio-templates\"\n\"235\",\"netflix-feign\"\n\"235\",\"network-share\"\n\"235\",\".net-micro-framework\"\n\"235\",\"koa2\"\n\"235\",\"android-actionbaractivity\"\n\"235\",\"azure-pipelines-tasks\"\n\"235\",\"cookiecutter-django\"\n\"235\",\"chartist.js\"\n\"235\",\"pep\"\n\"235\",\"genome\"\n\"235\",\"cfnetwork\"\n\"235\",\"parity\"\n\"235\",\"usecallback\"\n\"234\",\"ghost\"\n\"234\",\"jekyll-extensions\"\n\"234\",\"deeplink\"\n\"234\",\"coap\"\n\"234\",\"sizing\"\n\"234\",\"my.cnf\"\n\"234\",\"gravatar\"\n\"234\",\"github-package-registry\"\n\"234\",\"xliff\"\n\"234\",\"flask-mongoengine\"\n\"234\",\"sentence-similarity\"\n\"234\",\"distribute\"\n\"234\",\"python-socketio\"\n\"234\",\"pushkit\"\n\"234\",\"server-side-includes\"\n\"234\",\"ready-api\"\n\"234\",\"cronexpression\"\n\"234\",\"animator\"\n\"234\",\"ruby-1.9.2\"\n\"234\",\"kibana-5\"\n\"234\",\"document-database\"\n\"234\",\"core-ui\"\n\"234\",\"dynamics-ax-2012-r3\"\n\"234\",\"jquery-steps\"\n\"234\",\"asyncpg\"\n\"234\",\"formattable\"\n\"234\",\"ui-design\"\n\"234\",\"exec-maven-plugin\"\n\"234\",\"notificationmanager\"\n\"234\",\"redis-py\"\n\"234\",\"mplayer\"\n\"234\",\"lsof\"\n\"234\",\"chaquopy\"\n\"234\",\"textedit\"\n\"234\",\"http-streaming\"\n\"234\",\"reqwest\"\n\"233\",\"multipartentity\"\n\"233\",\"phonegap-cli\"\n\"233\",\"yo\"\n\"233\",\"ef-model-first\"\n\"233\",\"sqltransaction\"\n\"233\",\"plasticscm\"\n\"233\",\"vnc-server\"\n\"233\",\"pic18\"\n\"233\",\"catransform3d\"\n\"233\",\"kendo-dataviz\"\n\"233\",\"inetaddress\"\n\"233\",\"keyword-search\"\n\"233\",\"do.call\"\n\"233\",\"post-redirect-get\"\n\"233\",\"tweetsharp\"\n\"233\",\"couchbase-sync-gateway\"\n\"233\",\"grpc-node\"\n\"233\",\"module-pattern\"\n\"233\",\"jquery-mobile-listview\"\n\"233\",\"android-app-signing\"\n\"233\",\"word-2010\"\n\"233\",\"libssh\"\n\"233\",\"domaincontroller\"\n\"233\",\"typeguards\"\n\"233\",\"virtual-address-space\"\n\"233\",\"lexikjwtauthbundle\"\n\"233\",\"significance\"\n\"233\",\"mkfifo\"\n\"233\",\"nodetool\"\n\"233\",\"downsampling\"\n\"233\",\"azure-identity\"\n\"233\",\"qdockwidget\"\n\"233\",\"achievements\"\n\"233\",\"speaker\"\n\"233\",\"haskell-snap-framework\"\n\"233\",\"custom-validators\"\n\"233\",\"mechanize-python\"\n\"233\",\"starttls\"\n\"233\",\"sonarqube-web\"\n\"233\",\"globalplatform\"\n\"233\",\"gnome-shell-extensions\"\n\"233\",\"spark-avro\"\n\"232\",\"sta\"\n\"232\",\"webservices-client\"\n\"232\",\"reproducible-research\"\n\"232\",\"sslstream\"\n\"232\",\"web-storage\"\n\"232\",\"teamcity-8.0\"\n\"232\",\"vst\"\n\"232\",\"prisma2\"\n\"232\",\"phone-state-listener\"\n\"232\",\"slowdown\"\n\"232\",\"daxstudio\"\n\"232\",\"jsreport\"\n\"232\",\"hyperthreading\"\n\"232\",\"fax\"\n\"232\",\"jms-serializer\"\n\"232\",\"alexa-slot\"\n\"232\",\"wp-api\"\n\"232\",\"android-wear-2.0\"\n\"232\",\"detectron\"\n\"232\",\"spring-data-solr\"\n\"232\",\"revision-history\"\n\"232\",\"android-8.1-oreo\"\n\"232\",\"build-server\"\n\"232\",\"xctestcase\"\n\"232\",\"reentrantlock\"\n\"232\",\"scala-option\"\n\"232\",\"bun\"\n\"232\",\"azure-sentinel\"\n\"232\",\"bytearrayoutputstream\"\n\"232\",\"qmessagebox\"\n\"232\",\"restlet-2.0\"\n\"232\",\"react-data-grid\"\n\"232\",\"collectd\"\n\"232\",\"nm\"\n\"232\",\"android-screen\"\n\"232\",\"string-view\"\n\"232\",\"restrictions\"\n\"232\",\"geometry-shader\"\n\"232\",\"preorder\"\n\"232\",\"google-location-services\"\n\"232\",\"powershell-7.0\"\n\"232\",\"shieldui\"\n\"232\",\"tr1\"\n\"231\",\"insomnia\"\n\"231\",\"banking\"\n\"231\",\"prng\"\n\"231\",\"fluent-migrator\"\n\"231\",\"sl4a\"\n\"231\",\"ng-modules\"\n\"231\",\"pageviews\"\n\"231\",\"windows-rt\"\n\"231\",\"docker-run\"\n\"231\",\"windowing\"\n\"231\",\"document-classification\"\n\"231\",\"cprofile\"\n\"231\",\"deneb\"\n\"231\",\"kannel\"\n\"231\",\"ionic6\"\n\"231\",\"monaca\"\n\"231\",\"woothemes\"\n\"231\",\"libvlcsharp\"\n\"231\",\"revenuecat\"\n\"231\",\"codemagic\"\n\"231\",\"minix\"\n\"231\",\"libtorrent\"\n\"231\",\"mipmaps\"\n\"231\",\"codeship\"\n\"231\",\"vis.js-network\"\n\"231\",\"digital-logic\"\n\"231\",\"tarfile\"\n\"231\",\"core.async\"\n\"231\",\"asp.net-identity-3\"\n\"231\",\"generated-code\"\n\"231\",\"activeandroid\"\n\"231\",\"latin1\"\n\"231\",\"ios8-today-widget\"\n\"231\",\"touches\"\n\"231\",\"glu\"\n\"231\",\"sortablejs\"\n\"231\",\"concurrent-programming\"\n\"231\",\"section508\"\n\"231\",\"ilnumerics\"\n\"230\",\"cmder\"\n\"230\",\"clearfix\"\n\"230\",\"laravel-authorization\"\n\"230\",\"rust-crates\"\n\"230\",\"python-cffi\"\n\"230\",\"jsgrid\"\n\"230\",\"ico\"\n\"230\",\"rolify\"\n\"230\",\"windows-desktop-gadgets\"\n\"230\",\"alasql\"\n\"230\",\"wss4j\"\n\"230\",\"cost-management\"\n\"230\",\"ax\"\n\"230\",\"determinants\"\n\"230\",\"pebble-sdk\"\n\"230\",\"magento2.2\"\n\"230\",\"syntastic\"\n\"230\",\"google-apps-script-api\"\n\"230\",\"orleans\"\n\"230\",\"google-cast-sdk\"\n\"230\",\"typesafe-config\"\n\"230\",\"azure-management-api\"\n\"230\",\"uicollectionviewcompositionallayout\"\n\"230\",\"openjfx\"\n\"230\",\"directadmin\"\n\"230\",\"directory-listing\"\n\"230\",\"scikits\"\n\"230\",\"xbox360\"\n\"230\",\"maven-publish\"\n\"230\",\"thor\"\n\"230\",\"ifc\"\n\"230\",\"usb-otg\"\n\"230\",\"archetypes\"\n\"230\",\"cvxopt\"\n\"230\",\"linux-containers\"\n\"230\",\"headless-cms\"\n\"230\",\"mse\"\n\"229\",\"pheatmap\"\n\"229\",\"react-native-push-notification\"\n\"229\",\"tensorflow2.x\"\n\"229\",\"getuikit\"\n\"229\",\"featuretools\"\n\"229\",\"unification\"\n\"229\",\"categorization\"\n\"229\",\"pagerslidingtabstrip\"\n\"229\",\"xml-libxml\"\n\"229\",\"servicestack.redis\"\n\"229\",\"rsocket\"\n\"229\",\"ps1\"\n\"229\",\"appinsights\"\n\"229\",\"swift4.1\"\n\"229\",\"jwk\"\n\"229\",\"coredns\"\n\"229\",\"application-lifecycle\"\n\"229\",\"absolute-value\"\n\"229\",\"google-assistant\"\n\"229\",\"network-printers\"\n\"229\",\"lwp-useragent\"\n\"229\",\"hibernate-ogm\"\n\"229\",\"die\"\n\"229\",\"uipath-studio\"\n\"229\",\"assembly-resolution\"\n\"229\",\"directoryinfo\"\n\"229\",\"comexception\"\n\"229\",\"activesync\"\n\"229\",\"ios6.1\"\n\"229\",\"text-cursor\"\n\"229\",\"google-material-icons\"\n\"229\",\"sctp\"\n\"229\",\"amazon-fire-tv\"\n\"229\",\"binance-smart-chain\"\n\"229\",\"maven-javadoc-plugin\"\n\"228\",\"clgeocoder\"\n\"228\",\"web-performance\"\n\"228\",\"report-viewer2010\"\n\"228\",\"soapheader\"\n\"228\",\"apiconnect\"\n\"228\",\"cellular-automata\"\n\"228\",\"xlconnect\"\n\"228\",\"binary-heap\"\n\"228\",\"ccavenue\"\n\"228\",\"appcode\"\n\"228\",\"finalize\"\n\"228\",\"discriminator\"\n\"228\",\"avcapture\"\n\"228\",\"optimistic-concurrency\"\n\"228\",\"mysql5\"\n\"228\",\"jpgraph\"\n\"228\",\"vbe\"\n\"228\",\"angular-routerlink\"\n\"228\",\"dynamic-proxy\"\n\"228\",\"bootstrap-tags-input\"\n\"228\",\"errorlevel\"\n\"228\",\"wso2-das\"\n\"228\",\"foundry-code-repositories\"\n\"228\",\"ko.observablearray\"\n\"228\",\"magento-1.9.1\"\n\"228\",\"nxp-microcontroller\"\n\"228\",\"f#-fake\"\n\"228\",\"android-4.3-jelly-bean\"\n\"228\",\"devenv\"\n\"228\",\"geopoints\"\n\"228\",\"office-2013\"\n\"228\",\"logstash-file\"\n\"227\",\"dbset\"\n\"227\",\"mx-record\"\n\"227\",\"laravel-controller\"\n\"227\",\"jsonata\"\n\"227\",\"aesthetics\"\n\"227\",\"fusion\"\n\"227\",\"django-1.4\"\n\"227\",\"cassini\"\n\"227\",\"ng-build\"\n\"227\",\"keystone\"\n\"227\",\"django-rest-framework-jwt\"\n\"227\",\"rsqlite\"\n\"227\",\"windows-mobile-6\"\n\"227\",\"nativescript-telerik-ui\"\n\"227\",\"wso2-integration-studio\"\n\"227\",\"brownie\"\n\"227\",\"android-backup-service\"\n\"227\",\"python-2.4\"\n\"227\",\"rakefile\"\n\"227\",\"cordova-plugin-fcm\"\n\"227\",\"asp.net5\"\n\"227\",\"redgate\"\n\"227\",\"openoffice-writer\"\n\"227\",\"game-theory\"\n\"227\",\"android-internet\"\n\"227\",\"lsf\"\n\"227\",\"response-time\"\n\"227\",\"commerce\"\n\"227\",\"lightswitch-2013\"\n\"227\",\"suffix-tree\"\n\"227\",\"securestring\"\n\"227\",\"maven-dependency\"\n\"227\",\"image-caching\"\n\"227\",\"max-flow\"\n\"227\",\"endpoints\"\n\"227\",\"git-squash\"\n\"227\",\"qt5.5\"\n\"226\",\"background-thread\"\n\"226\",\"replaykit\"\n\"226\",\"ssrs-expression\"\n\"226\",\"dbconnection\"\n\"226\",\"smil\"\n\"226\",\"displayobject\"\n\"226\",\"marklogic-10\"\n\"226\",\"constructor-overloading\"\n\"226\",\"constant-expression\"\n\"226\",\"xerces-c\"\n\"226\",\"metasploit\"\n\"226\",\"realm-list\"\n\"226\",\"uploader\"\n\"226\",\"fbjs\"\n\"226\",\"junction-table\"\n\"226\",\"pdfa\"\n\"226\",\"aws-sts\"\n\"226\",\"kafkajs\"\n\"226\",\"kodi\"\n\"226\",\"sid\"\n\"226\",\"riot.js\"\n\"226\",\"timedelay\"\n\"226\",\"x11-forwarding\"\n\"226\",\"rabbitmqctl\"\n\"226\",\"uistepper\"\n\"226\",\"r5rs\"\n\"226\",\"isr\"\n\"226\",\"asp.net-roles\"\n\"226\",\"azure-security\"\n\"226\",\"pex\"\n\"226\",\"certificate-authority\"\n\"226\",\"testlink\"\n\"226\",\"sourcetree\"\n\"226\",\"flutter-inappwebview\"\n\"226\",\"maven-war-plugin\"\n\"226\",\"zend-server\"\n\"226\",\"google-photos-api\"\n\"225\",\"flickity\"\n\"225\",\"treetable\"\n\"225\",\"whatsapi\"\n\"225\",\"ansible-role\"\n\"225\",\"ggraph\"\n\"225\",\"flat\"\n\"225\",\"uitextviewdelegate\"\n\"225\",\"python-gstreamer\"\n\"225\",\"laravel-6.2\"\n\"225\",\"unexpected-token\"\n\"225\",\"chrome-devtools-protocol\"\n\"225\",\"dashdb\"\n\"225\",\"rpath\"\n\"225\",\"django-wsgi\"\n\"225\",\"micronaut-data\"\n\"225\",\"canjs\"\n\"225\",\"django-debug-toolbar\"\n\"225\",\"nestjs-config\"\n\"225\",\"onhover\"\n\"225\",\"spring-boot-3\"\n\"225\",\"oserror\"\n\"225\",\"lexical\"\n\"225\",\"orbit\"\n\"225\",\"formatexception\"\n\"225\",\"pygithub\"\n\"225\",\"kmz\"\n\"225\",\"nuget-spec\"\n\"225\",\"null-check\"\n\"225\",\"scrapy-pipeline\"\n\"225\",\"refit\"\n\"225\",\"tint\"\n\"225\",\"xaringan\"\n\"225\",\"assemble\"\n\"225\",\"itextpdf\"\n\"225\",\"tiptap\"\n\"225\",\"httpbackend\"\n\"225\",\"zpl-ii\"\n\"225\",\"nicedit\"\n\"225\",\"spl-autoload-register\"\n\"225\",\"powerset\"\n\"225\",\"scriptengine\"\n\"225\",\"tiktok\"\n\"225\",\"traminer\"\n\"224\",\"basic4android\"\n\"224\",\"git-gui\"\n\"224\",\"clerk\"\n\"224\",\"edx\"\n\"224\",\"vue-directives\"\n\"224\",\"ggrepel\"\n\"224\",\"dateinterval\"\n\"224\",\"apache-synapse\"\n\"224\",\"fipy\"\n\"224\",\"frequency-distribution\"\n\"224\",\"metpy\"\n\"224\",\"dockpanel\"\n\"224\",\"win64\"\n\"224\",\"jib\"\n\"224\",\"django-deployment\"\n\"224\",\"gradio\"\n\"224\",\"nsevent\"\n\"224\",\"svc\"\n\"224\",\"rangy\"\n\"224\",\"komodo\"\n\"224\",\"shutdown-hook\"\n\"224\",\"sql-agent\"\n\"224\",\"gamma-distribution\"\n\"224\",\"handlers\"\n\"224\",\"pointcut\"\n\"224\",\"uirepeat\"\n\"224\",\"control-characters\"\n\"224\",\"nszombie\"\n\"224\",\"mesa\"\n\"224\",\"odm\"\n\"224\",\"urlloader\"\n\"224\",\"parallel-foreach\"\n\"224\",\"asdf\"\n\"224\",\"mdiparent\"\n\"224\",\"web-farm\"\n\"224\",\"composable\"\n\"223\",\"apache-commons-beanutils\"\n\"223\",\"clangd\"\n\"223\",\"firebase-extensions\"\n\"223\",\"adbannerview\"\n\"223\",\"capturing-group\"\n\"223\",\"purge\"\n\"223\",\"watchservice\"\n\"223\",\"unity-ui\"\n\"223\",\"database-metadata\"\n\"223\",\"boost-fusion\"\n\"223\",\"onload-event\"\n\"223\",\"kamailio\"\n\"223\",\"couchapp\"\n\"223\",\"inverted-index\"\n\"223\",\"google-analytics-sdk\"\n\"223\",\"java-21\"\n\"223\",\"seurat\"\n\"223\",\"luarocks\"\n\"223\",\".net-core-2.2\"\n\"223\",\"fp-ts\"\n\"223\",\"mixture-model\"\n\"223\",\"normalizr\"\n\"223\",\"regression-testing\"\n\"223\",\"dot-emacs\"\n\"223\",\"xbmc\"\n\"223\",\"asp.net-core-routing\"\n\"223\",\"taskscheduler\"\n\"223\",\"excel-web-addins\"\n\"223\",\"gaussianblur\"\n\"223\",\"laravel-request\"\n\"223\",\"textscan\"\n\"223\",\"propertychanged\"\n\"223\",\"string-to-datetime\"\n\"223\",\"android-kernel\"\n\"223\",\"metamodel\"\n\"223\",\"spim\"\n\"223\",\"zend-validate\"\n\"223\",\"mt4\"\n\"223\",\"gnucobol\"\n\"223\",\"trace32\"\n\"223\",\"alpakka\"\n\"222\",\"transmission\"\n\"222\",\"graphlab\"\n\"222\",\"contentpresenter\"\n\"222\",\"package-lock.json\"\n\"222\",\"uivisualeffectview\"\n\"222\",\"ccxt\"\n\"222\",\"cartesian-coordinates\"\n\"222\",\"djoser\"\n\"222\",\"joomla-k2\"\n\"222\",\"ruby-2.1\"\n\"222\",\"css-in-js\"\n\"222\",\"sharepoint-2016\"\n\"222\",\"aws-userpools\"\n\"222\",\"invariants\"\n\"222\",\"knights-tour\"\n\"222\",\"new-window\"\n\"222\",\"8-bit\"\n\"222\",\"tdengine\"\n\"222\",\"nsundomanager\"\n\"222\",\"sql-in\"\n\"222\",\"istringstream\"\n\"222\",\"sqlmodel\"\n\"222\",\"sql-server-2008r2-express\"\n\"222\",\"asp.net-core-mvc-2.0\"\n\"222\",\"mouseup\"\n\"222\",\"terracotta\"\n\"222\",\"office365-restapi\"\n\"222\",\"laravel-spark\"\n\"222\",\"moqui\"\n\"222\",\"lateral-join\"\n\"222\",\"protected-mode\"\n\"222\",\"memory-corruption\"\n\"222\",\"thread-dump\"\n\"222\",\"parsefloat\"\n\"222\",\"subtype\"\n\"222\",\"fmi\"\n\"222\",\"enhanced-ecommerce\"\n\"222\",\"qualifiers\"\n\"222\",\"bellman-ford\"\n\"222\",\"email-ext\"\n\"221\",\"cobalt\"\n\"221\",\"slam\"\n\"221\",\"dbvisualizer\"\n\"221\",\"graphiql\"\n\"221\",\"uitableviewsectionheader\"\n\"221\",\"firebird-3.0\"\n\"221\",\"chatroom\"\n\"221\",\"sharepoint-rest-api\"\n\"221\",\"rms\"\n\"221\",\"aws-databricks\"\n\"221\",\"simpleitk\"\n\"221\",\"optional-arguments\"\n\"221\",\"informatica-cloud\"\n\"221\",\"ioredis\"\n\"221\",\"der\"\n\"221\",\"env\"\n\"221\",\"google-api-ruby-client\"\n\"221\",\"object-slicing\"\n\"221\",\"orientdb2.2\"\n\"221\",\"to-char\"\n\"221\",\"table-relationships\"\n\"221\",\"dropout\"\n\"221\",\"exceptionhandler\"\n\"221\",\"moc\"\n\"221\",\"mpns\"\n\"221\",\"metafor\"\n\"221\",\"react-class-based-component\"\n\"221\",\"currentlocation\"\n\"221\",\"lossless-compression\"\n\"221\",\"string-substitution\"\n\"221\",\"stormcrawler\"\n\"221\",\"pfuser\"\n\"221\",\"ip-geolocation\"\n\"221\",\"ios8.3\"\n\"221\",\"ifft\"\n\"221\",\"automatic-properties\"\n\"221\",\"static-site-generation\"\n\"220\",\"react-player\"\n\"220\",\"apachebench\"\n\"220\",\"file-manipulation\"\n\"220\",\"react-native-fs\"\n\"220\",\"webspeech-api\"\n\"220\",\"cbperipheral\"\n\"220\",\"chrome-web-driver\"\n\"220\",\"rsacryptoserviceprovider\"\n\"220\",\"call-graph\"\n\"220\",\"css-calc\"\n\"220\",\"native-ads\"\n\"220\",\"android-viewgroup\"\n\"220\",\"erpnext\"\n\"220\",\"spring-resttemplate\"\n\"220\",\"raspberry-pi-zero\"\n\"220\",\"rhel8\"\n\"220\",\"contextual-action-bar\"\n\"220\",\"xamdatagrid\"\n\"220\",\"polylang\"\n\"220\",\"azure-logic-app-standard\"\n\"220\",\"non-english\"\n\"220\",\"open-telemetry-collector\"\n\"220\",\"tableviewer\"\n\"220\",\"sqlite3-ruby\"\n\"220\",\"dtmf\"\n\"220\",\"elfinder\"\n\"220\",\"qpython\"\n\"220\",\"angular-formbuilder\"\n\"220\",\"react-boilerplate\"\n\"220\",\"angular4-httpclient\"\n\"220\",\"git-post-receive\"\n\"220\",\"zombie.js\"\n\"220\",\"alv\"\n\"220\",\"toplink\"\n\"219\",\"ecma\"\n\"219\",\"deferred-execution\"\n\"219\",\"localserver\"\n\"219\",\"youtube-channels\"\n\"219\",\"mat-file\"\n\"219\",\"intel-edison\"\n\"219\",\"vstack\"\n\"219\",\"github-for-mac\"\n\"219\",\"immer.js\"\n\"219\",\"selectall\"\n\"219\",\"keypad\"\n\"219\",\"data-files\"\n\"219\",\"android-vision\"\n\"219\",\"jquery-cycle2\"\n\"219\",\"jvmti\"\n\"219\",\"gtktreeview\"\n\"219\",\"mikro-orm\"\n\"219\",\"oauth2client\"\n\"219\",\"orocommerce\"\n\"219\",\"kruskals-algorithm\"\n\"219\",\"viber\"\n\"219\",\"java-annotations\"\n\"219\",\"cadence-workflow\"\n\"219\",\"copy-on-write\"\n\"219\",\"hidden-files\"\n\"219\",\"dropdownbutton\"\n\"219\",\"referer\"\n\"219\",\"istio-sidecar\"\n\"219\",\"diagrammer\"\n\"219\",\"spdy\"\n\"219\",\"morse-code\"\n\"219\",\"layer-list\"\n\"219\",\"trailing\"\n\"219\",\"yslow\"\n\"219\",\"qtreewidgetitem\"\n\"219\",\"uti\"\n\"219\",\"seam2\"\n\"218\",\"graphhopper\"\n\"218\",\"classnotfound\"\n\"218\",\"adventureworks\"\n\"218\",\"ftp-server\"\n\"218\",\"selenium-extent-report\"\n\"218\",\"mapserver\"\n\"218\",\"ag-grid-ng2\"\n\"218\",\"valueconverter\"\n\"218\",\"database-security\"\n\"218\",\"docker-for-mac\"\n\"218\",\"mylyn\"\n\"218\",\"grafana-alerts\"\n\"218\",\"appwrite\"\n\"218\",\"grunt-usemin\"\n\"218\",\"inputstreamreader\"\n\"218\",\"nsstatusitem\"\n\"218\",\"hoverintent\"\n\"218\",\"macruby\"\n\"218\",\"libspotify\"\n\"218\",\"rcpparmadillo\"\n\"218\",\"pyo3\"\n\"218\",\"m4\"\n\"218\",\"outbound\"\n\"218\",\"android-bottomsheetdialog\"\n\"218\",\"cookie-httponly\"\n\"218\",\"android-hardware\"\n\"218\",\"execve\"\n\"218\",\"asp.net-dynamic-data\"\n\"218\",\"tarantool\"\n\"218\",\"control-m\"\n\"218\",\"periodic-task\"\n\"218\",\"getimagedata\"\n\"218\",\"tess4j\"\n\"218\",\"google-gemini\"\n\"218\",\"lower-bound\"\n\"218\",\"qmediaplayer\"\n\"218\",\"foreach-loop-container\"\n\"218\",\"berkshelf\"\n\"218\",\"secure-coding\"\n\"218\",\"spark-view-engine\"\n\"218\",\"scrollbars\"\n\"218\",\"ifconfig\"\n\"217\",\"installshield-le\"\n\"217\",\"matplotlib-widget\"\n\"217\",\"jboss-tools\"\n\"217\",\"clover\"\n\"217\",\"sqlx\"\n\"217\",\"selectinput\"\n\"217\",\"fisheye\"\n\"217\",\"bitcoind\"\n\"217\",\"s3fs\"\n\"217\",\"kubernetes-networkpolicy\"\n\"217\",\"mapnik\"\n\"217\",\"xml-sitemap\"\n\"217\",\"datacolumn\"\n\"217\",\"fat32\"\n\"217\",\"service-fabric-stateful\"\n\"217\",\"opshub\"\n\"217\",\"singlepage\"\n\"217\",\"csla\"\n\"217\",\"oracle-jet\"\n\"217\",\"capacitor-plugin\"\n\"217\",\"django-errors\"\n\"217\",\"error-correction\"\n\"217\",\"android-webservice\"\n\"217\",\"twitter-api-v2\"\n\"217\",\"azure-caching\"\n\"217\",\"interrupted-exception\"\n\"217\",\"influxql\"\n\"217\",\"scalafx\"\n\"217\",\"design-by-contract\"\n\"217\",\"jquery-tooltip\"\n\"217\",\"ucma\"\n\"217\",\"android.mk\"\n\"217\",\"coda\"\n\"217\",\"lg\"\n\"217\",\"javascript-intellisense\"\n\"217\",\"isenabled\"\n\"217\",\"devise-invitable\"\n\"217\",\"plots.jl\"\n\"217\",\"android-holo-everywhere\"\n\"217\",\"reentrancy\"\n\"217\",\"http-verbs\"\n\"217\",\"httplib2\"\n\"217\",\"elastic-ip\"\n\"217\",\"activejdbc\"\n\"217\",\"lora\"\n\"217\",\"sdl-ttf\"\n\"217\",\"fluttermap\"\n\"217\",\"securitymanager\"\n\"217\",\"gitversion\"\n\"217\",\"best-in-place\"\n\"217\",\"zbar-sdk\"\n\"216\",\"llvm-gcc\"\n\"216\",\"yolov4\"\n\"216\",\"stackview\"\n\"216\",\"ts-loader\"\n\"216\",\"yii-url-manager\"\n\"216\",\"jaxp\"\n\"216\",\"ng-hide\"\n\"216\",\"flannel\"\n\"216\",\"adorner\"\n\"216\",\"swisscomdev\"\n\"216\",\"man-in-the-middle\"\n\"216\",\"biztalk-2016\"\n\"216\",\"dispatchertimer\"\n\"216\",\"rowsum\"\n\"216\",\"pusher-js\"\n\"216\",\"real-time-clock\"\n\"216\",\"gpt-2\"\n\"216\",\"recursive-descent\"\n\"216\",\"waterfall\"\n\"216\",\"updatemodel\"\n\"216\",\"servicestack-text\"\n\"216\",\"watson-discovery\"\n\"216\",\"cpprest-sdk\"\n\"216\",\"inter-process-communicat\"\n\"216\",\"patchwork\"\n\"216\",\"raw\"\n\"216\",\"wix3.11\"\n\"216\",\"miktex\"\n\"216\",\"android-augmented-reality\"\n\"216\",\"android-automotive\"\n\"216\",\"build-definition\"\n\"216\",\"libusb-1.0\"\n\"216\",\"ocsp\"\n\"216\",\"node-serialport\"\n\"216\",\"timetable\"\n\"216\",\"asteriskami\"\n\"216\",\"azureml-python-sdk\"\n\"216\",\"tinyxml\"\n\"216\",\"hmisc\"\n\"216\",\"contextpath\"\n\"216\",\"channel-api\"\n\"216\",\"strimzi\"\n\"216\",\"exact-match\"\n\"216\",\"proxies\"\n\"216\",\"powershell-module\"\n\"216\",\"mscorlib\"\n\"216\",\"state-pattern\"\n\"216\",\"hawtio\"\n\"216\",\"haxeflixel\"\n\"216\",\"parametric-polymorphism\"\n\"215\",\"background-size\"\n\"215\",\"sspi\"\n\"215\",\"maven-antrun-plugin\"\n\"215\",\"installscript\"\n\"215\",\"symbolicatecrash\"\n\"215\",\"sendto\"\n\"215\",\"kubernetes-health-check\"\n\"215\",\"cics\"\n\"215\",\"sensormanager\"\n\"215\",\"owner\"\n\"215\",\"rpostgresql\"\n\"215\",\"vcl-styles\"\n\"215\",\"jira-xray\"\n\"215\",\"denial-of-service\"\n\"215\",\"inputaccessoryview\"\n\"215\",\"nspopover\"\n\"215\",\"http-1.1\"\n\"215\",\"spring-security-rest\"\n\"215\",\"cover\"\n\"215\",\"rapids\"\n\"215\",\"typo3-11.x\"\n\"215\",\"virtual-dom\"\n\"215\",\"object-initializers\"\n\"215\",\"hls.js\"\n\"215\",\"gwidgets\"\n\"215\",\"izpack\"\n\"215\",\"mobile-chrome\"\n\"215\",\"tintcolor\"\n\"215\",\"homekit\"\n\"215\",\"angular-ivy\"\n\"215\",\"logfiles\"\n\"215\",\"odt\"\n\"215\",\"alibaba-cloud\"\n\"215\",\"mspec\"\n\"214\",\"bapi\"\n\"214\",\"dbms-scheduler\"\n\"214\",\"vue-router4\"\n\"214\",\"ssid\"\n\"214\",\"jspx\"\n\"214\",\"datastax-enterprise-graph\"\n\"214\",\"unboundid-ldap-sdk\"\n\"214\",\"xml-twig\"\n\"214\",\"data-loss\"\n\"214\",\"rowid\"\n\"214\",\"unsubscribe\"\n\"214\",\"joomla-template\"\n\"214\",\"vagrant-provision\"\n\"214\",\"realm-migration\"\n\"214\",\"fb-hydra\"\n\"214\",\"receipt-validation\"\n\"214\",\"wadl\"\n\"214\",\"alasset\"\n\"214\",\"windowsformshost\"\n\"214\",\"react-table-v7\"\n\"214\",\"jts\"\n\"214\",\"net.tcp\"\n\"214\",\"gulp-concat\"\n\"214\",\"svn-hooks\"\n\"214\",\"macos-sonoma\"\n\"214\",\"android-biometric-prompt\"\n\"214\",\"dqn\"\n\"214\",\"openshift-3\"\n\"214\",\"register-transfer-level\"\n\"214\",\"eventsource\"\n\"214\",\"last-insert-id\"\n\"214\",\"google-gadget\"\n\"214\",\"angular-dynamic-components\"\n\"214\",\"ogr\"\n\"214\",\"flutter-textformfield\"\n\"214\",\"qtnetwork\"\n\"214\",\"particles.js\"\n\"214\",\"ava\"\n\"213\",\"probability-theory\"\n\"213\",\"phpstan\"\n\"213\",\"intel-oneapi\"\n\"213\",\"trusted-web-activity\"\n\"213\",\"sleep-mode\"\n\"213\",\"live555\"\n\"213\",\"affix\"\n\"213\",\"datawindow\"\n\"213\",\"bitrise\"\n\"213\",\"binomial-coefficients\"\n\"213\",\"cats-effect\"\n\"213\",\"s#arp-architecture\"\n\"213\",\"laravel-4.2\"\n\"213\",\"facebook-sdk-4.x\"\n\"213\",\"crossdomain.xml\"\n\"213\",\"ptvs\"\n\"213\",\"servercontrols\"\n\"213\",\"single-threaded\"\n\"213\",\"blueprint-css\"\n\"213\",\"eofexception\"\n\"213\",\"dynamic-routing\"\n\"213\",\"html-content-extraction\"\n\"213\",\"htmlunit-driver\"\n\"213\",\"frappe\"\n\"213\",\"rdoc\"\n\"213\",\"luhn\"\n\"213\",\"null-terminated\"\n\"213\",\"tag-cloud\"\n\"213\",\"spyne\"\n\"213\",\"sqlpackage\"\n\"213\",\"resource-leak\"\n\"213\",\"qgridlayout\"\n\"213\",\"oledbdataadapter\"\n\"213\",\"google-cloud-python\"\n\"213\",\"msw\"\n\"213\",\"stm\"\n\"213\",\"ifttt\"\n\"213\",\"media-library\"\n\"213\",\"web-bluetooth\"\n\"213\",\"tftp\"\n\"213\",\"git-tfs\"\n\"212\",\"truthtable\"\n\"212\",\"react-native-gifted-chat\"\n\"212\",\"yandex\"\n\"212\",\"apollo-federation\"\n\"212\",\"nfc-p2p\"\n\"212\",\"phrase\"\n\"212\",\"rjs\"\n\"212\",\"jmstemplate\"\n\"212\",\"css-frameworks\"\n\"212\",\"share-extension\"\n\"212\",\"rnotebook\"\n\"212\",\"win32ole\"\n\"212\",\"groupwise-maximum\"\n\"212\",\"negative-lookbehind\"\n\"212\",\"spring-form\"\n\"212\",\"apple-watch-complication\"\n\"212\",\"orocrm\"\n\"212\",\"visual-studio-2008-sp1\"\n\"212\",\".env\"\n\"212\",\"microsoft-graph-files\"\n\"212\",\"rich-snippets\"\n\"212\",\"directoryentry\"\n\"212\",\"numpy-ufunc\"\n\"212\",\"gcp-ai-platform-notebook\"\n\"212\",\"mod-alias\"\n\"212\",\"asset-catalog\"\n\"212\",\"devicetoken\"\n\"212\",\"aspectj-maven-plugin\"\n\"212\",\"dotcover\"\n\"212\",\"angular2-ngmodel\"\n\"212\",\"csv-import\"\n\"212\",\"activity-indicator\"\n\"212\",\"requestdispatcher\"\n\"212\",\"states\"\n\"212\",\"multer-s3\"\n\"212\",\"webcam-capture\"\n\"212\",\"sourcegenerators\"\n\"212\",\"quarkus-rest-client\"\n\"212\",\"baud-rate\"\n\"212\",\"embedded-tomcat-8\"\n\"211\",\"trial\"\n\"211\",\"grav\"\n\"211\",\"webusb\"\n\"211\",\"client-go\"\n\"211\",\"file-browser\"\n\"211\",\"ll-grammar\"\n\"211\",\"philips-hue\"\n\"211\",\"gradle-dependencies\"\n\"211\",\"hyperledger-sawtooth\"\n\"211\",\"django-postgresql\"\n\"211\",\"servant\"\n\"211\",\"singlechildscrollview\"\n\"211\",\"dl4j\"\n\"211\",\"couchdb-futon\"\n\"211\",\"kafka-topic\"\n\"211\",\"spring-cloud-config-server\"\n\"211\",\"android-textwatcher\"\n\"211\",\"mongoid3\"\n\"211\",\"jquery-jscrollpane\"\n\"211\",\"influxdb-python\"\n\"211\",\"jquery-gmap3\"\n\"211\",\"developer-console\"\n\"211\",\"orchardcms-1.7\"\n\"211\",\"atlassian-plugin-sdk\"\n\"211\",\"net-ssh\"\n\"211\",\"drawrectangle\"\n\"211\",\"redistributable\"\n\"211\",\"dig\"\n\"211\",\"nstoolbar\"\n\"211\",\"tns\"\n\"211\",\"asprepeater\"\n\"211\",\"node.js-typeorm\"\n\"211\",\"android-maven-plugin\"\n\"211\",\"generated\"\n\"211\",\"collapsable\"\n\"211\",\"ogre3d\"\n\"211\",\"par\"\n\"211\",\"automatic-differentiation\"\n\"211\",\"sts\"\n\"211\",\"componentone\"\n\"211\",\"qsqltablemodel\"\n\"211\",\"iis-5\"\n\"211\",\"gnu-coreutils\"\n\"211\",\"qtablewidgetitem\"\n\"210\",\"jco\"\n\"210\",\"deconvolution\"\n\"210\",\"class-transformer\"\n\"210\",\"remote-control\"\n\"210\",\"multiple-file-upload\"\n\"210\",\"selendroid\"\n\"210\",\"jsviews\"\n\"210\",\"agi\"\n\"210\",\"mybb\"\n\"210\",\"mysqlimport\"\n\"210\",\"kedro\"\n\"210\",\"onmousedown\"\n\"210\",\"html-parser\"\n\"210\",\"sanic\"\n\"210\",\"dynatrace\"\n\"210\",\"pci-dss\"\n\"210\",\"twilio-studio\"\n\"210\",\"craco\"\n\"210\",\"blobstorage\"\n\"210\",\"code-folding\"\n\"210\",\"knative\"\n\"210\",\"highdpi\"\n\"210\",\"dotfiles\"\n\"210\",\"uicollectionviewflowlayout\"\n\"210\",\"asp.net-core-8\"\n\"210\",\"jakarta-migration\"\n\"210\",\"elementwise-operations\"\n\"210\",\"google-console-developer\"\n\"210\",\"cgfloat\"\n\"210\",\"maximo-anywhere\"\n\"210\",\"autoregressive-models\"\n\"210\",\"utilities\"\n\"210\",\"ticket-system\"\n\"210\",\"panning\"\n\"210\",\"liferay-ide\"\n\"209\",\"printer-control-language\"\n\"209\",\"clientid\"\n\"209\",\"jca\"\n\"209\",\"graphml\"\n\"209\",\"stacked-area-chart\"\n\"209\",\"contain\"\n\"209\",\"datamember\"\n\"209\",\"xdp-bpf\"\n\"209\",\"optuna\"\n\"209\",\"vcf-variant-call-format\"\n\"209\",\"angular-mock\"\n\"209\",\"vast\"\n\"209\",\"angularjs-ng-transclude\"\n\"209\",\"airprint\"\n\"209\",\"nskeyedunarchiver\"\n\"209\",\"erlang-supervisor\"\n\"209\",\"saved-searches\"\n\"209\",\"type-theory\"\n\"209\",\"anchor-solana\"\n\"209\",\"amfphp\"\n\"209\",\"google-apps-script-editor\"\n\"209\",\"code-structure\"\n\"209\",\"abbreviation\"\n\"209\",\"janus\"\n\"209\",\"form-authentication\"\n\"209\",\"jacob\"\n\"209\",\"business-logic-layer\"\n\"209\",\"loose-coupling\"\n\"209\",\"ninject-2\"\n\"209\",\"moya\"\n\"209\",\"rescue\"\n\"209\",\"loess\"\n\"209\",\"spiral\"\n\"209\",\"lcs\"\n\"209\",\"webfaction\"\n\"209\",\"dapr\"\n\"209\",\"identification\"\n\"209\",\"archiving\"\n\"209\",\"thunderbird-addon\"\n\"209\",\"maven-site-plugin\"\n\"209\",\"beta-testing\"\n\"209\",\"autocorrect\"\n\"209\",\"stencil-buffer\"\n\"209\",\"autocommit\"\n\"208\",\"floyd-warshall\"\n\"208\",\"prims-algorithm\"\n\"208\",\"jfoenix\"\n\"208\",\"ggally\"\n\"208\",\"multiple-users\"\n\"208\",\"phpredis\"\n\"208\",\"jdbcrealm\"\n\"208\",\"listpicker\"\n\"208\",\"ebcdic\"\n\"208\",\"django-aggregation\"\n\"208\",\"futuretask\"\n\"208\",\"meteor-react\"\n\"208\",\"sip-server\"\n\"208\",\"namenode\"\n\"208\",\"rebol3\"\n\"208\",\"windows-identity\"\n\"208\",\"html-sanitizing\"\n\"208\",\"opencart2.3\"\n\"208\",\"android-vibration\"\n\"208\",\"sam\"\n\"208\",\"mongodb-update\"\n\"208\",\"azure-alerts\"\n\"208\",\"visual-studio-macros\"\n\"208\",\"build-script\"\n\"208\",\"javascript-injection\"\n\"208\",\"mobile-webkit\"\n\"208\",\"playsound\"\n\"208\",\"android-elevation\"\n\"208\",\"drupal-commerce\"\n\"208\",\"bytestring\"\n\"208\",\"google-cloud-iot\"\n\"208\",\"chars\"\n\"208\",\"spotify-app\"\n\"208\",\"meld\"\n\"208\",\"qabstracttablemodel\"\n\"208\",\"commandlink\"\n\"208\",\"commoncrypto\"\n\"208\",\"node-mssql\"\n\"208\",\"protobuf-c\"\n\"208\",\"trackbar\"\n\"208\",\"start-job\"\n\"208\",\"struts-tags\"\n\"208\",\"limesurvey\"\n\"208\",\"flutter-streambuilder\"\n\"208\",\"zimbra\"\n\"208\",\"bigcartel\"\n\"208\",\"transclusion\"\n\"208\",\"flutter-windows\"\n\"208\",\"use-case-diagram\"\n\"207\",\"jexcelapi\"\n\"207\",\"tsqlt\"\n\"207\",\"debian-jessie\"\n\"207\",\"grib\"\n\"207\",\"gitk\"\n\"207\",\"js-amd\"\n\"207\",\"django-1.6\"\n\"207\",\"api-manager\"\n\"207\",\"selenium4\"\n\"207\",\"kube-dns\"\n\"207\",\"verifyerror\"\n\"207\",\"metricbeat\"\n\"207\",\"calabash-ios\"\n\"207\",\"aws-iot-core\"\n\"207\",\"angular-ui-typeahead\"\n\"207\",\"facebook-webhooks\"\n\"207\",\"spring-boot-gradle-plugin\"\n\"207\",\"nsopenpanel\"\n\"207\",\"twilio-functions\"\n\"207\",\"world-of-warcraft\"\n\"207\",\"couchbase-view\"\n\"207\",\"sustainsys-saml2\"\n\"207\",\"video-conferencing\"\n\"207\",\"extended-ascii\"\n\"207\",\"facebook-app-requests\"\n\"207\",\"rdata\"\n\"207\",\"ranorex\"\n\"207\",\"setattr\"\n\"207\",\"visual-glitch\"\n\"207\",\"wordpress-hook\"\n\"207\",\"assemblyinfo\"\n\"207\",\"open-json\"\n\"207\",\"regexp-like\"\n\"207\",\"dotfuscator\"\n\"207\",\"redirecttoaction\"\n\"207\",\"buttongroup\"\n\"207\",\"tag-it\"\n\"207\",\"irq\"\n\"207\",\"ekeventstore\"\n\"207\",\"geometryreader\"\n\"207\",\"columnstore\"\n\"207\",\"ace\"\n\"207\",\"okd\"\n\"207\",\"excel-2019\"\n\"207\",\"comobject\"\n\"207\",\"image-comparison\"\n\"207\",\"securesocial\"\n\"207\",\"sourceforge\"\n\"207\",\"searchable\"\n\"206\",\"vtigercrm\"\n\"206\",\"ssms-2014\"\n\"206\",\"widechar\"\n\"206\",\"ssas-2008\"\n\"206\",\"apache-commons-math\"\n\"206\",\"matlab-app-designer\"\n\"206\",\"slave\"\n\"206\",\"apify\"\n\"206\",\"imblearn\"\n\"206\",\"labeling\"\n\"206\",\"choicefield\"\n\"206\",\"symbol-table\"\n\"206\",\"chdir\"\n\"206\",\"watchos-3\"\n\"206\",\"card.io\"\n\"206\",\"crontrigger\"\n\"206\",\"reconnect\"\n\"206\",\"unordered\"\n\"206\",\"sign-in-with-apple\"\n\"206\",\"jxls\"\n\"206\",\"scala-2.11\"\n\"206\",\"twig-extension\"\n\"206\",\"post-commit\"\n\"206\",\"susy\"\n\"206\",\"brainfuck\"\n\"206\",\"android-x86\"\n\"206\",\"kmdf\"\n\"206\",\"set-difference\"\n\"206\",\"go-fiber\"\n\"206\",\"java-http-client\"\n\"206\",\"buildconfiguration\"\n\"206\",\"todataurl\"\n\"206\",\"uialertaction\"\n\"206\",\"xcode9.2\"\n\"206\",\"asp.net-boilerplate\"\n\"206\",\"tkinter-text\"\n\"206\",\"mobx-state-tree\"\n\"206\",\"project-planning\"\n\"206\",\"accurev\"\n\"206\",\"ninject-extensions\"\n\"206\",\"generic-type-argument\"\n\"206\",\"moto\"\n\"206\",\"mpfr\"\n\"206\",\"webmethods\"\n\"206\",\"alloc\"\n\"206\",\"parameter-pack\"\n\"206\",\"utf8-decode\"\n\"206\",\"linkedin-jsapi\"\n\"205\",\"graphql-codegen\"\n\"205\",\"pg-search\"\n\"205\",\"flower\"\n\"205\",\"git-bare\"\n\"205\",\"vtl\"\n\"205\",\"rel\"\n\"205\",\"configurable-product\"\n\"205\",\"data-masking\"\n\"205\",\"firebase-performance\"\n\"205\",\"manage.py\"\n\"205\",\"jtableheader\"\n\"205\",\"python-hypothesis\"\n\"205\",\"python-ggplot\"\n\"205\",\"camel-ftp\"\n\"205\",\"database-optimization\"\n\"205\",\"jpeg2000\"\n\"205\",\"pdf-form\"\n\"205\",\"opendj\"\n\"205\",\"ncbi\"\n\"205\",\"bootstrap-tabs\"\n\"205\",\"tui\"\n\"205\",\"application-cache\"\n\"205\",\"wt\"\n\"205\",\"sap-gateway\"\n\"205\",\"mailjet\"\n\"205\",\"kramdown\"\n\"205\",\"audioqueue\"\n\"205\",\"revel\"\n\"205\",\"levelplot\"\n\"205\",\"lyx\"\n\"205\",\"outsystems\"\n\"205\",\"pyhook\"\n\"205\",\"order-of-execution\"\n\"205\",\"mobilenet\"\n\"205\",\"gunzip\"\n\"205\",\"cordova-ios\"\n\"205\",\"uiblureffect\"\n\"205\",\"lasagne\"\n\"205\",\"accounts\"\n\"205\",\"lua-api\"\n\"205\",\"httpbuilder\"\n\"205\",\"google-cloud-dataprep\"\n\"205\",\"trait-objects\"\n\"205\",\"autocorrelation\"\n\"205\",\"allennlp\"\n\"205\",\"prepare\"\n\"205\",\"git-status\"\n\"204\",\"removeeventlistener\"\n\"204\",\"liveserver\"\n\"204\",\"squish\"\n\"204\",\"mvel\"\n\"204\",\"listdir\"\n\"204\",\"live-templates\"\n\"204\",\"mxmlc\"\n\"204\",\"xrm\"\n\"204\",\"unicode-normalization\"\n\"204\",\"jsonconvert\"\n\"204\",\"binary-operators\"\n\"204\",\"app.xaml\"\n\"204\",\"address-bar\"\n\"204\",\"rjdbc\"\n\"204\",\"key-events\"\n\"204\",\"ruby-1.8.7\"\n\"204\",\"pubmed\"\n\"204\",\"dynamicform\"\n\"204\",\"androidviewclient\"\n\"204\",\"sap-dotnet-connector\"\n\"204\",\"networkonmainthread\"\n\"204\",\"dollar-sign\"\n\"204\",\"dokuwiki\"\n\"204\",\"3nf\"\n\"204\",\"build-dependencies\"\n\"204\",\"tabcontainer\"\n\"204\",\"gcdasyncsocket\"\n\"204\",\"dragula\"\n\"204\",\"text-search\"\n\"204\",\"node.js-stream\"\n\"204\",\"linear-equation\"\n\"204\",\"linked-tables\"\n\"204\",\"sublimelinter\"\n\"203\",\"ef-core-7.0\"\n\"203\",\"wijmo\"\n\"203\",\"staggered-gridview\"\n\"203\",\"jersey-1.0\"\n\"203\",\"multiuserchat\"\n\"203\",\"django-comments\"\n\"203\",\"datapump\"\n\"203\",\"disparity-mapping\"\n\"203\",\"fsevents\"\n\"203\",\"celltable\"\n\"203\",\"freetype2\"\n\"203\",\"avrdude\"\n\"203\",\"jqgrid-php\"\n\"203\",\"io-monad\"\n\"203\",\"supercsv\"\n\"203\",\"android-togglebutton\"\n\"203\",\"android-switch\"\n\"203\",\"jython-2.7\"\n\"203\",\"onmousemove\"\n\"203\",\"ranking-functions\"\n\"203\",\"orgchart\"\n\"203\",\"android-actionmode\"\n\"203\",\"ezpublish\"\n\"203\",\"facebook-chat\"\n\"203\",\"sql-query-store\"\n\"203\",\"opentbs\"\n\"203\",\"uikit-dynamics\"\n\"203\",\"vhd\"\n\"203\",\"hardware-interface\"\n\"203\",\"expo-av\"\n\"203\",\"itemizedoverlay\"\n\"203\",\"nomad\"\n\"203\",\"android-inapp-purchase\"\n\"203\",\"google-cloud-endpoints-v2\"\n\"203\",\"oidc-client-js\"\n\"203\",\"commandbinding\"\n\"203\",\"merging-data\"\n\"203\",\"qtcharts\"\n\"203\",\"bevy\"\n\"202\",\"background-music\"\n\"202\",\"fibers\"\n\"202\",\"umbraco6\"\n\"202\",\"jshell\"\n\"202\",\"cds\"\n\"202\",\"angular-nvd3\"\n\"202\",\"wakeup\"\n\"202\",\"servicenow-rest-api\"\n\"202\",\"readprocessmemory\"\n\"202\",\"rpn\"\n\"202\",\"cpu-speed\"\n\"202\",\"ioncube\"\n\"202\",\"azure-ad-b2b\"\n\"202\",\"payflowpro\"\n\"202\",\"spring-framework-beans\"\n\"202\",\"information-theory\"\n\"202\",\"pydot\"\n\"202\",\"raw-data\"\n\"202\",\"side-menu\"\n\"202\",\"code-composer\"\n\"202\",\"game-maker-studio-2\"\n\"202\",\"custom-object\"\n\"202\",\"google-cloud-print\"\n\"202\",\"cfhttp\"\n\"202\",\"activity-recognition\"\n\"202\",\"elgg\"\n\"202\",\"laravel-pagination\"\n\"202\",\"qsortfilterproxymodel\"\n\"202\",\"maven-tomcat-plugin\"\n\"202\",\"google-speech-to-text-api\"\n\"202\",\"zend-auth\"\n\"202\",\"parallel-testing\"\n\"202\",\"heightmap\"\n\"202\",\"forcats\"\n\"202\",\"gitosis\"\n\"202\",\"panorama-control\"\n\"201\",\"ddp\"\n\"201\",\"multiple-definition-error\"\n\"201\",\"main-activity\"\n\"201\",\"bit.ly\"\n\"201\",\"ng-map\"\n\"201\",\"flash-cc\"\n\"201\",\"disjoint-sets\"\n\"201\",\"pandas.excelwriter\"\n\"201\",\"adwhirl\"\n\"201\",\"cross-domain-policy\"\n\"201\",\"falconframework\"\n\"201\",\"pst\"\n\"201\",\"mfc-feature-pack\"\n\"201\",\"mysql-cluster\"\n\"201\",\"rna-seq\"\n\"201\",\"rrd\"\n\"201\",\"spring-cloud-aws\"\n\"201\",\"wp-admin\"\n\"201\",\"lync-2013\"\n\"201\",\"shadow-mapping\"\n\"201\",\"vimdiff\"\n\"201\",\"siemens\"\n\"201\",\"dolphindb\"\n\"201\",\"polkadot\"\n\"201\",\"hibernate-tools\"\n\"201\",\"azure-servicebus-subscriptions\"\n\"201\",\"azure-postgresql\"\n\"201\",\"policies\"\n\"201\",\"bytea\"\n\"201\",\"executioncontext\"\n\"201\",\"httpx\"\n\"201\",\"strikethrough\"\n\"201\",\"request.querystring\"\n\"201\",\"special-folders\"\n\"201\",\"custom-url\"\n\"201\",\"ijulia-notebook\"\n\"201\",\"email-address\"\n\"201\",\"compiler-theory\"\n\"201\",\"zend-navigation\"\n\"201\",\"prefect\"\n\"201\",\"powerdesigner\"\n\"201\",\"msal-angular\"\n\"200\",\"github-cli\"\n\"200\",\"ansible-vault\"\n\"200\",\"profiles\"\n\"200\",\"localdatetime\"\n\"200\",\"youcompleteme\"\n\"200\",\"api-versioning\"\n\"200\",\"symfony-routing\"\n\"200\",\"python-camelot\"\n\"200\",\"language-interoperability\"\n\"200\",\"jtag\"\n\"200\",\"kubernetes-apiserver\"\n\"200\",\"mysql-slow-query-log\"\n\"200\",\"wavesurfer.js\"\n\"200\",\"wchar\"\n\"200\",\"pyc\"\n\"200\",\"canonicalization\"\n\"200\",\"operations-research\"\n\"200\",\"growl\"\n\"200\",\"nestjs-typeorm\"\n\"200\",\"twitter-bootstrap-rails\"\n\"200\",\"guacamole\"\n\"200\",\"inline-code\"\n\"200\",\"facebook-group\"\n\"200\",\"visual-c++-2008\"\n\"200\",\"browsermob-proxy\"\n\"200\",\"rakudo\"\n\"200\",\"mage\"\n\"200\",\"fparsec\"\n\"200\",\"hgignore\"\n\"200\",\"ods\"\n\"200\",\"lean\"\n\"200\",\"text-segmentation\"\n\"200\",\"duende-identity-server\"\n\"200\",\"color-mapping\"\n\"200\",\"geohashing\"\n\"200\",\"reactcsstransitiongroup\"\n\"200\",\"dart2js\"\n\"200\",\"auto-py-to-exe\"\n\"200\",\"computer-forensics\"\n\"200\",\"transaction-log\"\n\"200\",\"glreadpixels\"\n\"200\",\"querystringparameter\"\n\"200\",\"preferencescreen\"\n\"199\",\"jess\"\n\"199\",\"phpspec\"\n\"199\",\"maskedinput\"\n\"199\",\"material3\"\n\"199\",\"gettype\"\n\"199\",\"yard\"\n\"199\",\"base-conversion\"\n\"199\",\"fflush\"\n\"199\",\"phpcs\"\n\"199\",\"multiple-axes\"\n\"199\",\"cloudwatch-alarms\"\n\"199\",\"squeryl\"\n\"199\",\"xll\"\n\"199\",\"python-imageio\"\n\"199\",\"meteor-collection2\"\n\"199\",\"dmd\"\n\"199\",\"docker-api\"\n\"199\",\"katex\"\n\"199\",\"coupling\"\n\"199\",\"invite\"\n\"199\",\"twisted.web\"\n\"199\",\"wql\"\n\"199\",\"inversifyjs\"\n\"199\",\"scala-breeze\"\n\"199\",\"asynctaskloader\"\n\"199\",\"javafx-3d\"\n\"199\",\"forwarding-reference\"\n\"199\",\"amplifyjs\"\n\"199\",\"javaparser\"\n\"199\",\"shinywidgets\"\n\"199\",\"netmiko\"\n\"199\",\"iverilog\"\n\"199\",\"garrys-mod\"\n\"199\",\"sqlmembershipprovider\"\n\"199\",\"aslr\"\n\"199\",\"uint64\"\n\"199\",\"scorm2004\"\n\"199\",\"dsym\"\n\"199\",\"context.xml\"\n\"199\",\"getpixel\"\n\"199\",\"android-savedstate\"\n\"199\",\"getimagesize\"\n\"199\",\"lauterbach\"\n\"199\",\"qaf\"\n\"199\",\"custom-error-handling\"\n\"199\",\"git-workflow\"\n\"199\",\"tiles2\"\n\"199\",\"identify\"\n\"199\",\"identity-experience-framework\"\n\"198\",\"bacon.js\"\n\"198\",\"maui-windows\"\n\"198\",\"loaddata\"\n\"198\",\"treepanel\"\n\"198\",\"flowbite\"\n\"198\",\"whatsapp-cloud-api\"\n\"198\",\"jflex\"\n\"198\",\"phphotolibrary\"\n\"198\",\"flutter-change-notifier\"\n\"198\",\"import.io\"\n\"198\",\"json-path-expression\"\n\"198\",\"biztalk-mapper\"\n\"198\",\"switchmap\"\n\"198\",\"umd\"\n\"198\",\"servletcontextlistener\"\n\"198\",\"angular-moment\"\n\"198\",\"sequence-points\"\n\"198\",\"factor-analysis\"\n\"198\",\"mfmessagecomposeviewcontroller\"\n\"198\",\"hyphenation\"\n\"198\",\"recorder\"\n\"198\",\"onunload\"\n\"198\",\"sap-web-ide\"\n\"198\",\"sbt-plugin\"\n\"198\",\"nestedrecyclerview\"\n\"198\",\"spring-social-facebook\"\n\"198\",\"google-assist-api\"\n\"198\",\"minifilter\"\n\"198\",\"ubuntu-13.10\"\n\"198\",\"google-chat\"\n\"198\",\"go-cd\"\n\"198\",\"pyperclip\"\n\"198\",\"nuxt-auth\"\n\"198\",\"android-contextmenu\"\n\"198\",\"angular-cdk-drag-drop\"\n\"198\",\"custom-routes\"\n\"198\",\"ios8-extension\"\n\"198\",\"android-loader\"\n\"198\",\"curl-multi\"\n\"198\",\"mouse-position\"\n\"198\",\"compose-recomposition\"\n\"198\",\"gnustep\"\n\"198\",\"hbm2ddl\"\n\"198\",\"bcd\"\n\"198\",\"solution-explorer\"\n\"198\",\"gmm\"\n\"198\",\"mediawiki-templates\"\n\"198\",\"linq2db\"\n\"198\",\"stdoptional\"\n\"198\",\"urlparse\"\n\"198\",\"usrp\"\n\"197\",\"sklabelnode\"\n\"197\",\"ssms-17\"\n\"197\",\"multiview\"\n\"197\",\"mathematica-8\"\n\"197\",\"trust\"\n\"197\",\"declarative-services\"\n\"197\",\"product-quantity\"\n\"197\",\"file-search\"\n\"197\",\"dired\"\n\"197\",\"symfony-3.1\"\n\"197\",\"cartalyst-sentry\"\n\"197\",\"pubspec\"\n\"197\",\"jnienv\"\n\"197\",\"varchar2\"\n\"197\",\"hot-module-replacement\"\n\"197\",\"twilio-video\"\n\"197\",\"bnd\"\n\"197\",\"postdata\"\n\"197\",\"jasmine2.0\"\n\"197\",\"codeplex\"\n\"197\",\"rational-team-concert\"\n\"197\",\"cognos-11\"\n\"197\",\"java-stored-procedures\"\n\"197\",\"ufw\"\n\"197\",\"rethinkdb-javascript\"\n\"197\",\"360-degrees\"\n\"197\",\"iscroll4\"\n\"197\",\"dingo-api\"\n\"197\",\"devexpress-mvc\"\n\"197\",\"chainer\"\n\"197\",\"du\"\n\"197\",\"node-cron\"\n\"197\",\"centos6.5\"\n\"197\",\"getselection\"\n\"197\",\"angularjs-digest\"\n\"197\",\"bigbluebutton\"\n\"197\",\"mdxjs\"\n\"197\",\"ilog\"\n\"197\",\"dart-editor\"\n\"197\",\"auto-ptr\"\n\"196\",\"git-for-windows\"\n\"196\",\"jdatechooser\"\n\"196\",\"slcomposeviewcontroller\"\n\"196\",\"gridsplitter\"\n\"196\",\"getvalue\"\n\"196\",\"installation-package\"\n\"196\",\"mutation-testing\"\n\"196\",\"remove-if\"\n\"196\",\"smart-tv\"\n\"196\",\"json-patch\"\n\"196\",\"image-scanner\"\n\"196\",\"rust-sqlx\"\n\"196\",\"django-compressor\"\n\"196\",\"kinvey\"\n\"196\",\"win2d\"\n\"196\",\"create-react-native-app\"\n\"196\",\"kendo-ui-grid\"\n\"196\",\"onpaint\"\n\"196\",\"jquery-post\"\n\"196\",\"android-touch-event\"\n\"196\",\"easygui\"\n\"196\",\"blockly\"\n\"196\",\"extrapolation\"\n\"196\",\"lvm\"\n\"196\",\"networkextension\"\n\"196\",\"astyanax\"\n\"196\",\"objectdisposedexception\"\n\"196\",\"shopify-api-node\"\n\"196\",\"rdma\"\n\"196\",\"java-14\"\n\"196\",\"retention\"\n\"196\",\"drupal-fapi\"\n\"196\",\"plotnine\"\n\"196\",\"gallio\"\n\"196\",\"timefold\"\n\"196\",\"ui-select2\"\n\"196\",\"testrail\"\n\"196\",\"elapsedtime\"\n\"196\",\"geonames\"\n\"196\",\"compose-multiplatform\"\n\"196\",\"ash\"\n\"196\",\"artisan-migrate\"\n\"196\",\"dart-io\"\n\"196\",\"alternate\"\n\"196\",\"alwayson\"\n\"195\",\"marklogic-dhf\"\n\"195\",\"base-class-library\"\n\"195\",\"ssrs-2014\"\n\"195\",\"clos\"\n\"195\",\"cloudera-quickstart-vm\"\n\"195\",\"slick-2.0\"\n\"195\",\"vuedraggable\"\n\"195\",\"stage3d\"\n\"195\",\"flutter-cupertino\"\n\"195\",\"page-fault\"\n\"195\",\"jsplitpane\"\n\"195\",\"mantine\"\n\"195\",\"dockerpy\"\n\"195\",\"watchman\"\n\"195\",\"watson-studio\"\n\"195\",\"avqueueplayer\"\n\"195\",\"caliburn\"\n\"195\",\"crf\"\n\"195\",\"pruning\"\n\"195\",\"android-typeface\"\n\"195\",\"native-web-component\"\n\"195\",\"braces\"\n\"195\",\"border-radius\"\n\"195\",\"swiftlint\"\n\"195\",\"wp-cli\"\n\"195\",\"inf\"\n\"195\",\"nedb\"\n\"195\",\"enyo\"\n\"195\",\"code-complexity\"\n\"195\",\"obsolete\"\n\"195\",\"mix-blend-mode\"\n\"195\",\"orika\"\n\"195\",\"gwt-gin\"\n\"195\",\"regional-settings\"\n\"195\",\"isql\"\n\"195\",\"hardlink\"\n\"195\",\"elasticsearch-py\"\n\"195\",\"android-mediaprojection\"\n\"195\",\"ipdb\"\n\"195\",\"qos\"\n\"195\",\"ms-access-forms\"\n\"195\",\"zeroconf\"\n\"195\",\"batching\"\n\"195\",\"gjs\"\n\"195\",\"gnome-3\"\n\"195\",\"mbstring\"\n\"194\",\"cmsis\"\n\"194\",\"cmakelists-options\"\n\"194\",\"backoffice\"\n\"194\",\"intellij-15\"\n\"194\",\"yaws\"\n\"194\",\"ssh-keygen\"\n\"194\",\"ng-switch\"\n\"194\",\"sobel\"\n\"194\",\"image-thresholding\"\n\"194\",\"xlookup\"\n\"194\",\"appauth\"\n\"194\",\"find-replace\"\n\"194\",\"imagenet\"\n\"194\",\"callable-statement\"\n\"194\",\"databricks-connect\"\n\"194\",\"datagridviewcomboboxcell\"\n\"194\",\"recompose\"\n\"194\",\"icon-fonts\"\n\"194\",\"capture-group\"\n\"194\",\"input-type-file\"\n\"194\",\"apple-music\"\n\"194\",\"pathos\"\n\"194\",\"nscoder\"\n\"194\",\"invalid-characters\"\n\"194\",\"android-scroll\"\n\"194\",\"create-directory\"\n\"194\",\"setcontentview\"\n\"194\",\"pyshark\"\n\"194\",\"magic-numbers\"\n\"194\",\"android-doze\"\n\"194\",\"xc8\"\n\"194\",\"longest-substring\"\n\"194\",\"spigot\"\n\"194\",\"z80\"\n\"194\",\"binaries\"\n\"194\",\"arcobjects\"\n\"194\",\"arrayfire\"\n\"194\",\"bcc\"\n\"194\",\"zend-mail\"\n\"194\",\"part-of-speech\"\n\"194\",\"zfcuser\"\n\"193\",\"temporal-tables\"\n\"193\",\"eclipse-rap\"\n\"193\",\"regularized\"\n\"193\",\"jenkins-blueocean\"\n\"193\",\"apache-commons-io\"\n\"193\",\"yamldotnet\"\n\"193\",\"filechannel\"\n\"193\",\"apache-hudi\"\n\"193\",\"liquid-layout\"\n\"193\",\"sshpass\"\n\"193\",\"cider\"\n\"193\",\"kvc\"\n\"193\",\"vmware-player\"\n\"193\",\"server.xml\"\n\"193\",\"validationrules\"\n\"193\",\"mysql-connect\"\n\"193\",\"recompile\"\n\"193\",\"docutils\"\n\"193\",\"roundcube\"\n\"193\",\"aws-organizations\"\n\"193\",\"validationerror\"\n\"193\",\"method-resolution-order\"\n\"193\",\"puts\"\n\"193\",\"hortonworks-sandbox\"\n\"193\",\"equalizer\"\n\"193\",\"jvm-bytecode\"\n\"193\",\"spring-ioc\"\n\"193\",\"jquery-inputmask\"\n\"193\",\"supabase-js\"\n\"193\",\"librdkafka\"\n\"193\",\"sigkill\"\n\"193\",\"pyscripter\"\n\"193\",\"system.in\"\n\"193\",\"ancestry\"\n\"193\",\"knowledge-graph\"\n\"193\",\"expression-evaluation\"\n\"193\",\"todo\"\n\"193\",\"sql-server-2005-express\"\n\"193\",\"xamarin.essentials\"\n\"193\",\"northwind\"\n\"193\",\"opensl\"\n\"193\",\"hydration\"\n\"193\",\"ios-animations\"\n\"193\",\"ipython-parallel\"\n\"193\",\"textnode\"\n\"193\",\"sonata-media-bundle\"\n\"193\",\"qtconcurrent\"\n\"193\",\"zodb\"\n\"193\",\"zend-search-lucene\"\n\"193\",\"mechanize-ruby\"\n\"193\",\"themoviedb-api\"\n\"193\",\"compose-desktop\"\n\"192\",\"yarnpkg-v2\"\n\"192\",\"jenssegers-mongodb\"\n\"192\",\"livecycle-designer\"\n\"192\",\"appdynamics\"\n\"192\",\"apache-tez\"\n\"192\",\"waitress\"\n\"192\",\"dlsym\"\n\"192\",\"docker-entrypoint\"\n\"192\",\"kendo-combobox\"\n\"192\",\"jfr\"\n\"192\",\"watson-iot\"\n\"192\",\"jitter\"\n\"192\",\"ruby-hash\"\n\"192\",\"fat\"\n\"192\",\"appwidgetprovider\"\n\"192\",\"apr\"\n\"192\",\"satellite-image\"\n\"192\",\"pass-through\"\n\"192\",\"android-shape\"\n\"192\",\"derived-table\"\n\"192\",\"boost-hana\"\n\"192\",\"superuser\"\n\"192\",\"equations\"\n\"192\",\"envelope\"\n\"192\",\"dojox.charting\"\n\"192\",\"midl\"\n\"192\",\"wmp\"\n\"192\",\"uint\"\n\"192\",\"itemtouchhelper\"\n\"192\",\"xcode10.1\"\n\"192\",\"npm-run\"\n\"192\",\"redirectstandardoutput\"\n\"192\",\"azure-python-sdk\"\n\"192\",\"ios-keyboard-extension\"\n\"192\",\"resource-cleanup\"\n\"192\",\"react-hot-loader\"\n\"192\",\"laravel-websockets\"\n\"192\",\"pentaho-report-designer\"\n\"192\",\"multi-device-hybrid-apps\"\n\"192\",\"alu\"\n\"192\",\"sonarscanner\"\n\"192\",\"biml\"\n\"192\",\"url-parsing\"\n\"192\",\"webjars\"\n\"192\",\"google-mobile-ads\"\n\"192\",\"scripting-bridge\"\n\"191\",\"yosys\"\n\"191\",\"unet-neural-network\"\n\"191\",\"django-1.10\"\n\"191\",\"casablanca\"\n\"191\",\"rtweet\"\n\"191\",\"wildfly-swarm\"\n\"191\",\"simulated-annealing\"\n\"191\",\"was\"\n\"191\",\"wakanda\"\n\"191\",\"iomanip\"\n\"191\",\"visa\"\n\"191\",\"javascriptmvc\"\n\"191\",\"rdfa\"\n\"191\",\"refluxjs\"\n\"191\",\"isnumeric\"\n\"191\",\"xap\"\n\"191\",\"associativity\"\n\"191\",\"lpc\"\n\"191\",\"google-cloud-tpu\"\n\"191\",\"angular-file-upload\"\n\"191\",\"qmap\"\n\"191\",\"mtp\"\n\"191\",\"quart\"\n\"191\",\"google-tasks-api\"\n\"191\",\"muenchian-grouping\"\n\"191\",\"hexagonal-architecture\"\n\"191\",\"totp\"\n\"191\",\"zenity\"\n\"190\",\"vtd-xml\"\n\"190\",\"procedural\"\n\"190\",\"trinidad\"\n\"190\",\"get-wmiobject\"\n\"190\",\"vocabulary\"\n\"190\",\"kubernetes-dashboard\"\n\"190\",\"aws-parameter-store\"\n\"190\",\"windows-defender\"\n\"190\",\"docker-stack\"\n\"190\",\"pushdown-automaton\"\n\"190\",\"iaas\"\n\"190\",\"ingress-controller\"\n\"190\",\"twitch-api\"\n\"190\",\"libs\"\n\"190\",\"coil\"\n\"190\",\"asp.net-authentication\"\n\"190\",\"uicollectionreusableview\"\n\"190\",\"nuitka\"\n\"190\",\"non-greedy\"\n\"190\",\"home-directory\"\n\"190\",\"asp.net-core-localization\"\n\"190\",\"qbfc\"\n\"190\",\"activitygroup\"\n\"190\",\"glow\"\n\"190\",\"amazon-appstore\"\n\"190\",\"bidi\"\n\"190\",\"maven-resources-plugin\"\n\"190\",\"asana-api\"\n\"190\",\"steamworks-api\"\n\"190\",\"script#\"\n\"190\",\"tfx\"\n\"189\",\"trix\"\n\"189\",\"jcache\"\n\"189\",\"gitea\"\n\"189\",\"class-members\"\n\"189\",\"matrix-factorization\"\n\"189\",\"symfony-2.2\"\n\"189\",\"xml-layout\"\n\"189\",\"include-guards\"\n\"189\",\"xpc\"\n\"189\",\"circle-pack\"\n\"189\",\"cargo\"\n\"189\",\"sharepoint-apps\"\n\"189\",\"w3wp\"\n\"189\",\"grafana-templating\"\n\"189\",\"avd-manager\"\n\"189\",\"airflow-webserver\"\n\"189\",\"rufus-scheduler\"\n\"189\",\"sink\"\n\"189\",\"core-video\"\n\"189\",\"dynamic-class-loaders\"\n\"189\",\"entity-framework-ctp5\"\n\"189\",\"save-image\"\n\"189\",\"worker-thread\"\n\"189\",\"openfoam\"\n\"189\",\"aws-sdk-java-2.0\"\n\"189\",\"twilio-programmable-voice\"\n\"189\",\"pyppeteer\"\n\"189\",\"winlogon\"\n\"189\",\"lzw\"\n\"189\",\"fragmentation\"\n\"189\",\"observedobject\"\n\"189\",\"scaletransform\"\n\"189\",\"business-catalyst\"\n\"189\",\"dexguard\"\n\"189\",\"tapi\"\n\"189\",\"drjava\"\n\"189\",\"drizzle\"\n\"189\",\"android-configchanges\"\n\"189\",\"gameplay-kit\"\n\"189\",\"xcode-server\"\n\"189\",\"r6\"\n\"189\",\"http-status-code-429\"\n\"189\",\"android-runonuithread\"\n\"189\",\"getopt-long\"\n\"189\",\"ipad-2\"\n\"189\",\"test-runner\"\n\"189\",\"zephyr-rtos\"\n\"189\",\"avalonedit\"\n\"189\",\"alphabetical-sort\"\n\"188\",\"react-redux-firebase\"\n\"188\",\"primeng-turbotable\"\n\"188\",\"filelock\"\n\"188\",\"intel-vtune\"\n\"188\",\"livesearch\"\n\"188\",\"in-app-update\"\n\"188\",\"xeon-phi\"\n\"188\",\"bixbystudio\"\n\"188\",\"mapactivity\"\n\"188\",\"kendo-datepicker\"\n\"188\",\"django-fixtures\"\n\"188\",\"aws-elb\"\n\"188\",\"data-dictionary\"\n\"188\",\"datacontractjsonserializer\"\n\"188\",\"django-manage.py\"\n\"188\",\"payu\"\n\"188\",\"derived-types\"\n\"188\",\"pathname\"\n\"188\",\"invocation\"\n\"188\",\"oozie-workflow\"\n\"188\",\"outlook-calendar\"\n\"188\",\"bspline\"\n\"188\",\"sieve-algorithm\"\n\"188\",\"java-server\"\n\"188\",\"build-agent\"\n\"188\",\"milo\"\n\"188\",\"ob-start\"\n\"188\",\"amcharts5\"\n\"188\",\"mini-css-extract-plugin\"\n\"188\",\"magento2.4\"\n\"188\",\"face\"\n\"188\",\"rhel5\"\n\"188\",\"referrals\"\n\"188\",\"quotations\"\n\"188\",\"execute-immediate\"\n\"188\",\"openstack-neutron\"\n\"188\",\"scd\"\n\"188\",\"changelistener\"\n\"188\",\"huge-pages\"\n\"188\",\"pyttsx\"\n\"188\",\"httppostedfilebase\"\n\"188\",\"getscript\"\n\"188\",\"elasticsearch-mapping\"\n\"188\",\"stateless-session-bean\"\n\"188\",\"embedded-fonts\"\n\"188\",\"emplace\"\n\"187\",\"tron\"\n\"187\",\"matlab-engine\"\n\"187\",\"apache-cordova\"\n\"187\",\"litedb\"\n\"187\",\"caroufredsel\"\n\"187\",\"carplay\"\n\"187\",\"union-find\"\n\"187\",\"chicken-scheme\"\n\"187\",\"adk\"\n\"187\",\"adventure\"\n\"187\",\"kingfisher\"\n\"187\",\"sitecore8.1\"\n\"187\",\"warbler\"\n\"187\",\"unity-networking\"\n\"187\",\"puphpet\"\n\"187\",\"angular-standalone-components\"\n\"187\",\"angular-providers\"\n\"187\",\"vugen\"\n\"187\",\"angularjs-module\"\n\"187\",\"jibx\"\n\"187\",\"easynetq\"\n\"187\",\"enum-class\"\n\"187\",\"on-the-fly\"\n\"187\",\"epplus-4\"\n\"187\",\"authenticode\"\n\"187\",\"formclosing\"\n\"187\",\"minidump\"\n\"187\",\"rft\"\n\"187\",\"vips\"\n\"187\",\"abstract-methods\"\n\"187\",\"drf-yasg\"\n\"187\",\"mixer\"\n\"187\",\"gcp-load-balancer\"\n\"187\",\"acpi\"\n\"187\",\"terraform-modules\"\n\"187\",\"mercurial-hook\"\n\"187\",\"google-developer-tools\"\n\"187\",\"looper\"\n\"187\",\"logstash-jdbc\"\n\"187\",\"android-paging-library\"\n\"187\",\"angular2-pipe\"\n\"187\",\"sealed\"\n\"187\",\"pre-build-event\"\n\"187\",\"tooltipster\"\n\"187\",\"som\"\n\"187\",\"hazelcast-jet\"\n\"187\",\"state-space\"\n\"186\",\"intercom\"\n\"186\",\"gridsome\"\n\"186\",\"graphframes\"\n\"186\",\"file-attributes\"\n\"186\",\"int32\"\n\"186\",\"gio\"\n\"186\",\"jenkins-api\"\n\"186\",\"template-classes\"\n\"186\",\"panda3d\"\n\"186\",\"cheat-engine\"\n\"186\",\"vote\"\n\"186\",\"ng-controller\"\n\"186\",\"apache-modules\"\n\"186\",\"swipeview\"\n\"186\",\"appfog\"\n\"186\",\"binary-compatibility\"\n\"186\",\"cedet\"\n\"186\",\"unwrap\"\n\"186\",\"metatable\"\n\"186\",\"vendor-prefix\"\n\"186\",\"ibm-mobile-services\"\n\"186\",\"dependabot\"\n\"186\",\"boosting\"\n\"186\",\"infiniband\"\n\"186\",\"mongodb-csharp-2.0\"\n\"186\",\"detachedcriteria\"\n\"186\",\"mailitem\"\n\"186\",\"uberjar\"\n\"186\",\"formio\"\n\"186\",\"min-heap\"\n\"186\",\"wkt\"\n\"186\",\"otto\"\n\"186\",\"network-security\"\n\"186\",\"dokan\"\n\"186\",\"android-14\"\n\"186\",\"np-hard\"\n\"186\",\"nuke\"\n\"186\",\"tasklist\"\n\"186\",\"spritebatch\"\n\"186\",\"mobaxterm\"\n\"186\",\"handwriting-recognition\"\n\"186\",\"polybase\"\n\"186\",\"dsa\"\n\"186\",\"iphone-web-app\"\n\"186\",\"progressive-enhancement\"\n\"186\",\"promtail\"\n\"186\",\"http-status-code-304\"\n\"186\",\"qfilesystemmodel\"\n\"186\",\"evil-mode\"\n\"186\",\"qglwidget\"\n\"186\",\"parseexception\"\n\"186\",\"submatrix\"\n\"185\",\"slowcheetah\"\n\"185\",\"try-finally\"\n\"185\",\"cleartimeout\"\n\"185\",\"apache-jena\"\n\"185\",\"apache-kafka-mirrormaker\"\n\"185\",\"xhtml-1.0-strict\"\n\"185\",\"ownerdrawn\"\n\"185\",\"seh\"\n\"185\",\"distcp\"\n\"185\",\"distributed-database\"\n\"185\",\"xml-configuration\"\n\"185\",\"content-negotiation\"\n\"185\",\"first-order-logic\"\n\"185\",\"narrowing\"\n\"185\",\"simple-form-for\"\n\"185\",\"pushpin\"\n\"185\",\"optional-chaining\"\n\"185\",\"unreachable-code\"\n\"185\",\"public-method\"\n\"185\",\"dynamic-reports\"\n\"185\",\"delphi-prism\"\n\"185\",\"tws\"\n\"185\",\"port-scanning\"\n\"185\",\"netweaver\"\n\"185\",\"system-verilog-assertions\"\n\"185\",\"ubuntu-10.10\"\n\"185\",\"kombu\"\n\"185\",\"signed-apk\"\n\"185\",\"mailcore2\"\n\"185\",\"vertx-verticle\"\n\"185\",\"didreceivememorywarning\"\n\"185\",\"uimanageddocument\"\n\"185\",\"openshift-enterprise\"\n\"185\",\"azure-synapse-analytics\"\n\"185\",\"c#-10.0\"\n\"185\",\"execv\"\n\"185\",\"android-diffutils\"\n\"185\",\"gcc4\"\n\"185\",\"xcode4.4\"\n\"185\",\"aspose.pdf\"\n\"185\",\"mention\"\n\"185\",\"node-async\"\n\"185\",\"ehcache-3\"\n\"185\",\"google-cloud-ml-engine\"\n\"185\",\"custom-authentication\"\n\"185\",\"google-reader\"\n\"185\",\"parse-tree\"\n\"185\",\"maven-deploy-plugin\"\n\"185\",\"dart-js-interop\"\n\"185\",\"scsi\"\n\"185\",\"qsettings\"\n\"185\",\"qtkit\"\n\"185\",\"usernotifications\"\n\"185\",\"bbc-microbit\"\n\"184\",\"staleelementreferenceexception\"\n\"184\",\"master-theorem\"\n\"184\",\"gitbook\"\n\"184\",\"tstringgrid\"\n\"184\",\"datastax-startup\"\n\"184\",\"db2-zos\"\n\"184\",\"unique-id\"\n\"184\",\"nexus-5\"\n\"184\",\"malformed\"\n\"184\",\"xmlunit\"\n\"184\",\"iasyncenumerable\"\n\"184\",\"validationattribute\"\n\"184\",\"journal\"\n\"184\",\"serverless-architecture\"\n\"184\",\"data-driven\"\n\"184\",\"documentviewer\"\n\"184\",\"keypoint\"\n\"184\",\"windows-1252\"\n\"184\",\"create-view\"\n\"184\",\"pdfview\"\n\"184\",\"android-studio-3.1\"\n\"184\",\"twisted.internet\"\n\"184\",\"inline-functions\"\n\"184\",\"typecasting-operator\"\n\"184\",\"hstack\"\n\"184\",\"oauth-1.0a\"\n\"184\",\"set-theory\"\n\"184\",\"rapidxml\"\n\"184\",\"domcrawler\"\n\"184\",\"pyrebase\"\n\"184\",\"kriging\"\n\"184\",\"openscad\"\n\"184\",\"spservices\"\n\"184\",\"mockserver\"\n\"184\",\"qvariant\"\n\"184\",\"scichart\"\n\"184\",\"android-mediasession\"\n\"184\",\"qregexp\"\n\"184\",\"combinators\"\n\"184\",\"pyvmomi\"\n\"184\",\"monk\"\n\"184\",\"changestream\"\n\"184\",\"iperf\"\n\"184\",\"fody\"\n\"184\",\"alljoyn\"\n\"184\",\"web-inf\"\n\"184\",\"tramp\"\n\"184\",\"std-filesystem\"\n\"184\",\"bde\"\n\"184\",\"user-stories\"\n\"184\",\"sunos\"\n\"184\",\"iformfile\"\n\"183\",\"release-mode\"\n\"183\",\"jekyll-theme\"\n\"183\",\"webvtt\"\n\"183\",\"webrat\"\n\"183\",\"react-native-testing-library\"\n\"183\",\"instance-methods\"\n\"183\",\"templatefield\"\n\"183\",\"clojure.spec\"\n\"183\",\"python-descriptors\"\n\"183\",\"ng-bind\"\n\"183\",\"smote\"\n\"183\",\"packagemaker\"\n\"183\",\"page-numbering\"\n\"183\",\"ngrx-entity\"\n\"183\",\"biztalk-2013r2\"\n\"183\",\"swrl\"\n\"183\",\"biztalk-2009\"\n\"183\",\"joomla-component\"\n\"183\",\"dat.gui\"\n\"183\",\"oracle-fusion-middleware\"\n\"183\",\"kendo-multiselect\"\n\"183\",\"oracle-ebs\"\n\"183\",\"kind\"\n\"183\",\"tukey\"\n\"183\",\"gtkmm3\"\n\"183\",\"formcollection\"\n\"183\",\"google-chrome-storage\"\n\"183\",\"java-compiler-api\"\n\"183\",\"rickshaw\"\n\"183\",\"word-boundary\"\n\"183\",\"attask\"\n\"183\",\"pymel\"\n\"183\",\"android-dark-theme\"\n\"183\",\"spymemcached\"\n\"183\",\"bytestream\"\n\"183\",\"spoon\"\n\"183\",\"etherscan\"\n\"183\",\"project.json\"\n\"183\",\"active-form\"\n\"183\",\"census\"\n\"183\",\"predictionio\"\n\"182\",\"prismic.io\"\n\"182\",\"class-attributes\"\n\"182\",\"relative-import\"\n\"182\",\"rep\"\n\"182\",\"srt\"\n\"182\",\"phonon\"\n\"182\",\"swiftui-scrollview\"\n\"182\",\"pinning\"\n\"182\",\"v-select\"\n\"182\",\"datapager\"\n\"182\",\"python-sounddevice\"\n\"182\",\"findelement\"\n\"182\",\"django-timezone\"\n\"182\",\"row-height\"\n\"182\",\"server-configuration\"\n\"182\",\"docusigncompositetmplts\"\n\"182\",\"angular-schematics\"\n\"182\",\"gradle-eclipse\"\n\"182\",\"aio\"\n\"182\",\"android-sliding\"\n\"182\",\"pdf-parsing\"\n\"182\",\"sbjson\"\n\"182\",\"sim900\"\n\"182\",\"postfix-operator\"\n\"182\",\"libreoffice-base\"\n\"182\",\"kong-plugin\"\n\"182\",\"video-conversion\"\n\"182\",\"pyproj\"\n\"182\",\"bucklescript\"\n\"182\",\"setjmp\"\n\"182\",\"orbital-mechanics\"\n\"182\",\"rippledrawable\"\n\"182\",\"hole-punching\"\n\"182\",\"x3d\"\n\"182\",\"uimanager\"\n\"182\",\"dtls\"\n\"182\",\"homebrew-cask\"\n\"182\",\"difftime\"\n\"182\",\"chartboost\"\n\"182\",\"access-point\"\n\"182\",\"eventstoredb\"\n\"182\",\"pessimistic-locking\"\n\"182\",\"lost-focus\"\n\"182\",\"spine.js\"\n\"182\",\"oid\"\n\"182\",\"oculusquest\"\n\"182\",\"geocoder\"\n\"182\",\"transcription\"\n\"182\",\"thunk\"\n\"182\",\"sulu\"\n\"181\",\"smallrye\"\n\"181\",\"client-side-scripting\"\n\"181\",\"mathematical-morphology\"\n\"181\",\"jbox2d\"\n\"181\",\"page-factory\"\n\"181\",\"pic32\"\n\"181\",\"jspinclude\"\n\"181\",\"python-2to3\"\n\"181\",\"chord-diagram\"\n\"181\",\"datetimeformatter\"\n\"181\",\"jgraphx\"\n\"181\",\"mysql-num-rows\"\n\"181\",\"psobject\"\n\"181\",\"ruby-c-extension\"\n\"181\",\"mysql-error-1045\"\n\"181\",\"aggregates\"\n\"181\",\"windows-security\"\n\"181\",\"turkish\"\n\"181\",\"apportable\"\n\"181\",\"nsapplication\"\n\"181\",\"android-sdk-2.1\"\n\"181\",\"power-automate-desktop\"\n\"181\",\"winhttprequest\"\n\"181\",\"exuberant-ctags\"\n\"181\",\"pymunk\"\n\"181\",\"ocl\"\n\"181\",\"library-project\"\n\"181\",\"wireframe\"\n\"181\",\"magick++\"\n\"181\",\"rhapsody\"\n\"181\",\"redux-reducers\"\n\"181\",\"openseadragon\"\n\"181\",\"jackson-modules\"\n\"181\",\"scenegraph\"\n\"181\",\"android-internal-storage\"\n\"181\",\"context-free-language\"\n\"181\",\"scalding\"\n\"181\",\"byref\"\n\"181\",\"asp.net-mvc-validation\"\n\"181\",\"assembly-binding-redirect\"\n\"181\",\"r.net\"\n\"181\",\"log4cxx\"\n\"181\",\"mounted-volumes\"\n\"181\",\"most-vexing-parse\"\n\"181\",\"lr-grammar\"\n\"181\",\"qbasic\"\n\"181\",\"ninja-forms\"\n\"181\",\"lowpass-filter\"\n\"181\",\"seaside\"\n\"181\",\"automation-testing\"\n\"181\",\"glx\"\n\"181\",\"subviews\"\n\"181\",\"linq-to-dataset\"\n\"181\",\"array-unique\"\n\"181\",\"webpack-3\"\n\"181\",\"mediainfo\"\n\"181\",\"linux-capabilities\"\n\"181\",\"zendesk-api\"\n\"181\",\"subtyping\"\n\"181\",\"stata-macros\"\n\"180\",\"effective-java\"\n\"180\",\"ssas-2012\"\n\"180\",\"jelly\"\n\"180\",\"phonegap-desktop-app\"\n\"180\",\"slack-block-kit\"\n\"180\",\"technical-indicator\"\n\"180\",\"firebase-app-distribution\"\n\"180\",\"selenium-edgedriver\"\n\"180\",\"data-representation\"\n\"180\",\"unix-ar\"\n\"180\",\"watershed\"\n\"180\",\"fact\"\n\"180\",\"varnish-4\"\n\"180\",\"icloud-drive\"\n\"180\",\"anki\"\n\"180\",\"meteor-up\"\n\"180\",\"postdelayed\"\n\"180\",\"blpapi\"\n\"180\",\"bounded-contexts\"\n\"180\",\"worklight-security\"\n\"180\",\"extjs7\"\n\"180\",\"significant-digits\"\n\"180\",\"minecraft-fabric\"\n\"180\",\"system.net.httpwebrequest\"\n\"180\",\"acceleration\"\n\"180\",\"home-assistant\"\n\"180\",\"pocketpc\"\n\"180\",\"tms\"\n\"180\",\"mkpolyline\"\n\"180\",\"uidocumentpickerviewcontroller\"\n\"180\",\"galaxy-tab\"\n\"180\",\"gyp\"\n\"180\",\"scrapinghub\"\n\"180\",\"ios9.3\"\n\"180\",\"duplication\"\n\"180\",\"tomcat10\"\n\"180\",\"streamsets\"\n\"180\",\"dagster\"\n\"180\",\"maven-jar-plugin\"\n\"180\",\"zelle-graphics\"\n\"180\",\"hclust\"\n\"180\",\"paradox\"\n\"180\",\"thonny\"\n\"179\",\"multi-window\"\n\"179\",\"jes\"\n\"179\",\"xsd-1.1\"\n\"179\",\"tekton\"\n\"179\",\"jdb\"\n\"179\",\"grouplayout\"\n\"179\",\"tray\"\n\"179\",\"tstringlist\"\n\"179\",\"bitbucket-cloud\"\n\"179\",\"cons\"\n\"179\",\"incognito-mode\"\n\"179\",\"fivem\"\n\"179\",\"snapcraft\"\n\"179\",\"voting-system\"\n\"179\",\"dirent.h\"\n\"179\",\"django-admin-filters\"\n\"179\",\"marginal-effects\"\n\"179\",\"vcr\"\n\"179\",\"simplecv\"\n\"179\",\"service-fabric-stateless\"\n\"179\",\"angular-local-storage\"\n\"179\",\"dynamic-pivot\"\n\"179\",\"apprequests\"\n\"179\",\"dynamic-dispatch\"\n\"179\",\"passport-google-oauth\"\n\"179\",\"apple-musickit\"\n\"179\",\"jquery-slider\"\n\"179\",\"revealing-module-pattern\"\n\"179\",\"return-value-optimization\"\n\"179\",\".net-core-2.1\"\n\"179\",\"sys-refcursor\"\n\"179\",\"bstr\"\n\"179\",\"luasocket\"\n\"179\",\"xbox-one\"\n\"179\",\"azure-speech\"\n\"179\",\"tabulate\"\n\"179\",\"sqldatetime\"\n\"179\",\"asp.net-mvc-controller\"\n\"179\",\"azure-iot-central\"\n\"179\",\"double-click-advertising\"\n\"179\",\"non-relational-database\"\n\"179\",\"uint32\"\n\"179\",\"bundles\"\n\"179\",\"nul\"\n\"179\",\"retaincount\"\n\"179\",\"geom-col\"\n\"179\",\"text-decorations\"\n\"179\",\"evm\"\n\"179\",\"tethering\"\n\"179\",\"dup\"\n\"179\",\"spelling\"\n\"179\",\"mongorepository\"\n\"179\",\"generic-method\"\n\"179\",\"solr-query-syntax\"\n\"179\",\"d3-force-directed\"\n\"179\",\"arch\"\n\"178\",\"defaultlistmodel\"\n\"178\",\"loadimage\"\n\"178\",\"backcolor\"\n\"178\",\"fido\"\n\"178\",\"basecamp\"\n\"178\",\"reactphp\"\n\"178\",\"ghostdriver\"\n\"178\",\"bad-gateway\"\n\"178\",\"jsonschema2pojo\"\n\"178\",\"binary-serialization\"\n\"178\",\"smooks\"\n\"178\",\"appcmd\"\n\"178\",\"lalr\"\n\"178\",\"chronicle-queue\"\n\"178\",\"selectlistitem\"\n\"178\",\"doctrine-extensions\"\n\"178\",\"ibm-was\"\n\"178\",\"windows-embedded-compact\"\n\"178\",\"ibm-cloud-functions\"\n\"178\",\"kendo-ui-mvc\"\n\"178\",\"unused-variables\"\n\"178\",\"vcalendar\"\n\"178\",\"wagtail-streamfield\"\n\"178\",\"pscustomobject\"\n\"178\",\"bootstrapvalidator\"\n\"178\",\"nspopupbutton\"\n\"178\",\"azure-cloud-shell\"\n\"178\",\"spring-config\"\n\"178\",\"svnadmin\"\n\"178\",\"spring-portlet-mvc\"\n\"178\",\"app-startup\"\n\"178\",\"openapi-generator-maven-plugin\"\n\"178\",\"side-by-side\"\n\"178\",\"left-recursion\"\n\"178\",\"visnetwork\"\n\"178\",\"objective-c-2.0\"\n\"178\",\"wkwebviewconfiguration\"\n\"178\",\"object-pooling\"\n\"178\",\"auth-guard\"\n\"178\",\"buildx\"\n\"178\",\"domainservices\"\n\"178\",\"typo3-6.1.x\"\n\"178\",\"scrapy-shell\"\n\"178\",\"playwright-typescript\"\n\"178\",\"doublebuffered\"\n\"178\",\"mmc\"\n\"178\",\"c++builder-6\"\n\"178\",\"angular-aot\"\n\"178\",\"react-grid-layout\"\n\"178\",\"android-ion\"\n\"178\",\"mplfinance\"\n\"178\",\"mpmath\"\n\"178\",\"laravel-testing\"\n\"178\",\"source-code-protection\"\n\"178\",\"webi\"\n\"178\",\"argmax\"\n\"178\",\"vaadin23\"\n\"178\",\"glmmtmb\"\n\"178\",\"image-editing\"\n\"178\",\"scss-lint\"\n\"178\",\"mbedtls\"\n\"178\",\"hdr\"\n\"178\",\"fontforge\"\n\"178\",\"tiling\"\n\"178\",\"dac\"\n\"178\",\"pgm\"\n\"177\",\"jboss-4.2.x\"\n\"177\",\"vuefire\"\n\"177\",\"dedicated-server\"\n\"177\",\"clrs\"\n\"177\",\"printdialog\"\n\"177\",\"volatility\"\n\"177\",\"mariadb-10.3\"\n\"177\",\"apartment-gem\"\n\"177\",\"sharepointdocumentlibrary\"\n\"177\",\"icheck\"\n\"177\",\"mfi\"\n\"177\",\"ibm-sbt\"\n\"177\",\"alamofireimage\"\n\"177\",\"html.beginform\"\n\"177\",\"ponyorm\"\n\"177\",\"application.cfc\"\n\"177\",\"jqxhr\"\n\"177\",\"jaxb2-maven-plugin\"\n\"177\",\"synchronizationcontext\"\n\"177\",\"vmware-fusion\"\n\"177\",\"bresenham\"\n\"177\",\"review-board\"\n\"177\",\"lz4\"\n\"177\",\"openx\"\n\"177\",\"android-mediascanner\"\n\"177\",\"http-status-code-406\"\n\"177\",\"char-pointer\"\n\"177\",\"oledbdatareader\"\n\"177\",\"mongoskin\"\n\"177\",\"luabind\"\n\"177\",\"pyvisa\"\n\"177\",\"headless-ui\"\n\"177\",\"media-type\"\n\"177\",\"struts1\"\n\"177\",\"becomefirstresponder\"\n\"177\",\"web-config-transform\"\n\"176\",\"relaxng\"\n\"176\",\"lnk2001\"\n\"176\",\"debian-stretch\"\n\"176\",\"privacy-policy\"\n\"176\",\"interception\"\n\"176\",\"connexion\"\n\"176\",\"firefox4\"\n\"176\",\"unique-values\"\n\"176\",\"discourse\"\n\"176\",\"select-query\"\n\"176\",\"unistd.h\"\n\"176\",\"freepbx\"\n\"176\",\"semantic-release\"\n\"176\",\"putchar\"\n\"176\",\"pvlib\"\n\"176\",\"vxml\"\n\"176\",\"aggregate-initialization\"\n\"176\",\"kendo-mvvm\"\n\"176\",\"graphael\"\n\"176\",\"grails-3.1\"\n\"176\",\"wikidata-query-service\"\n\"176\",\"css-content\"\n\"176\",\"psychtoolbox\"\n\"176\",\"wallet-connect\"\n\"176\",\"fading\"\n\"176\",\"upi\"\n\"176\",\"rootscope\"\n\"176\",\"mysql-event\"\n\"176\",\"bootsfaces\"\n\"176\",\"coturn\"\n\"176\",\"nestjs-swagger\"\n\"176\",\"crawler4j\"\n\"176\",\"scalac\"\n\"176\",\"write.table\"\n\"176\",\"core-web-vitals\"\n\"176\",\"tvml\"\n\"176\",\"javax\"\n\"176\",\"microsoft-r\"\n\"176\",\"sgx\"\n\"176\",\"javascript-engine\"\n\"176\",\"shinymodules\"\n\"176\",\"microsoft-teams-js\"\n\"176\",\"4gl\"\n\"176\",\"rego\"\n\"176\",\"uicomponents\"\n\"176\",\"npm-link\"\n\"176\",\"plot-annotations\"\n\"176\",\"har\"\n\"176\",\"elastic-apm\"\n\"176\",\"custom-model-binder\"\n\"176\",\"strtol\"\n\"176\",\"test-explorer\"\n\"176\",\"cudf\"\n\"176\",\"stringification\"\n\"176\",\"android-runtime\"\n\"176\",\"sorteddictionary\"\n\"176\",\"textwrangler\"\n\"176\",\"hcatalog\"\n\"176\",\"shift-reduce-conflict\"\n\"176\",\"endeca\"\n\"176\",\"mbtiles\"\n\"176\",\"subgrid\"\n\"175\",\"wic\"\n\"175\",\"vue-script-setup\"\n\"175\",\"react-native-sectionlist\"\n\"175\",\"jedi\"\n\"175\",\"intel-ipp\"\n\"175\",\"location-href\"\n\"175\",\"photolibrary\"\n\"175\",\"printstream\"\n\"175\",\"pitest\"\n\"175\",\"symfony3\"\n\"175\",\"django-ckeditor\"\n\"175\",\"disruptor-pattern\"\n\"175\",\"variable-variables\"\n\"175\",\"databound\"\n\"175\",\"face-id\"\n\"175\",\"neo4j.rb\"\n\"175\",\"dyalog\"\n\"175\",\"dynamic-jasper\"\n\"175\",\"grub\"\n\"175\",\"grunt-contrib-concat\"\n\"175\",\"population\"\n\"175\",\"atexit\"\n\"175\",\"360-virtual-reality\"\n\"175\",\"freeimage\"\n\"175\",\"building-github-actions\"\n\"175\",\"ktor-client\"\n\"175\",\".when\"\n\"175\",\"lerp\"\n\"175\",\"h.265\"\n\"175\",\"iwebbrowser2\"\n\"175\",\"nuxt-i18n\"\n\"175\",\"tail-call-optimization\"\n\"175\",\"hacklang\"\n\"175\",\"azure-monitor\"\n\"175\",\"reactjs.net\"\n\"175\",\"react-google-charts\"\n\"175\",\"qcheckbox\"\n\"175\",\"nifti\"\n\"175\",\"members\"\n\"175\",\"mpmediapickercontroller\"\n\"175\",\"subtree\"\n\"175\",\"encodeuricomponent\"\n\"175\",\"tfs-power-tools\"\n\"175\",\"toupper\"\n\"175\",\"lilypond\"\n\"175\",\"ms-query\"\n\"175\",\"auto-renewing\"\n\"175\",\"amazon-polly\"\n\"175\",\"stereoscopy\"\n\"175\",\"batch-updates\"\n\"174\",\"featherlight.js\"\n\"174\",\"yahoo-mail\"\n\"174\",\"yasm\"\n\"174\",\"pretty-urls\"\n\"174\",\"weebly\"\n\"174\",\"clap\"\n\"174\",\"react-native-listview\"\n\"174\",\"procmail\"\n\"174\",\"pandas-apply\"\n\"174\",\"pid-controller\"\n\"174\",\"symmetric\"\n\"174\",\"umbraco8\"\n\"174\",\"imagettftext\"\n\"174\",\"cartodb\"\n\"174\",\"symfony-cmf\"\n\"174\",\"snowflake-connector\"\n\"174\",\"image-preloader\"\n\"174\",\"ibm-api-management\"\n\"174\",\"metronic\"\n\"174\",\"pugixml\"\n\"174\",\"ptx\"\n\"174\",\"microsoft-distributed-file-system\"\n\"174\",\"jpype\"\n\"174\",\"sharethis\"\n\"174\",\"android-securityexception\"\n\"174\",\"jwe\"\n\"174\",\"equals-operator\"\n\"174\",\"azure-deployment-slots\"\n\"174\",\"surfaceholder\"\n\"174\",\"wpallimport\"\n\"174\",\"surveyjs\"\n\"174\",\"cocoa-design-patterns\"\n\"174\",\"network-monitoring\"\n\"174\",\"netbeans6.8\"\n\"174\",\"brave\"\n\"174\",\"word-2007\"\n\"174\",\"pyqgis\"\n\"174\",\"system.web\"\n\"174\",\"dopostback\"\n\"174\",\"rexx\"\n\"174\",\"sysctl\"\n\"174\",\"time-t\"\n\"174\",\"dih\"\n\"174\",\"draw.io\"\n\"174\",\"qvector\"\n\"174\",\"handles\"\n\"174\",\"plpython\"\n\"174\",\"asp.net-optimization\"\n\"174\",\"operand\"\n\"174\",\"redcap\"\n\"174\",\"ejabberd-module\"\n\"174\",\"node-cluster\"\n\"174\",\"launchmode\"\n\"174\",\"fogbugz\"\n\"174\",\"solr6\"\n\"174\",\"mtu\"\n\"174\",\"array-intersect\"\n\"174\",\"tilde\"\n\"174\",\"ieee\"\n\"174\",\"partial-page-refresh\"\n\"173\",\"eclipse-che\"\n\"173\",\"web-reference\"\n\"173\",\"liquibase-hibernate\"\n\"173\",\"flow-control\"\n\"173\",\"bitwise-xor\"\n\"173\",\"filereference\"\n\"173\",\"picture-in-picture\"\n\"173\",\"safaridriver\"\n\"173\",\"laravel-facade\"\n\"173\",\"swig-template\"\n\"173\",\"self-tracking-entities\"\n\"173\",\"mhtml\"\n\"173\",\"recurly\"\n\"173\",\"uptime\"\n\"173\",\"nrf52\"\n\"173\",\"application-shutdown\"\n\"173\",\"jwilder-nginx-proxy\"\n\"173\",\"posthoc\"\n\"173\",\"dynamic-html\"\n\"173\",\"botkit\"\n\"173\",\"couchbase-java-api\"\n\"173\",\"pygal\"\n\"173\",\"ocean\"\n\"173\",\"audioqueueservices\"\n\"173\",\"extent\"\n\"173\",\"dotnetnuke-5\"\n\"173\",\"taurus\"\n\"173\",\"targeting\"\n\"173\",\"tankauth\"\n\"173\",\"reflow\"\n\"173\",\"wysihtml5\"\n\"173\",\"expo-go\"\n\"173\",\"spotbugs\"\n\"173\",\"cubit\"\n\"173\",\"nio2\"\n\"173\",\"topmost\"\n\"173\",\"multilevel-analysis\"\n\"173\",\"emeditor\"\n\"173\",\"mediamuxer\"\n\"173\",\"if-constexpr\"\n\"173\",\"daisyui\"\n\"172\",\"render.com\"\n\"172\",\"ssms-16\"\n\"172\",\"mastercard\"\n\"172\",\"jax-rpc\"\n\"172\",\"backup-strategies\"\n\"172\",\"sqoop2\"\n\"172\",\"bank\"\n\"172\",\"data-pipeline\"\n\"172\",\"fyne\"\n\"172\",\"social-framework\"\n\"172\",\"django-context\"\n\"172\",\"adafruit-circuitpython\"\n\"172\",\"appcelerator-studio\"\n\"172\",\"xmla\"\n\"172\",\"fitnesse-slim\"\n\"172\",\"next-i18next\"\n\"172\",\"oxygenxml\"\n\"172\",\"adminhtml\"\n\"172\",\"windowlistener\"\n\"172\",\"dllregistration\"\n\"172\",\"databricks-unity-catalog\"\n\"172\",\"cs193p\"\n\"172\",\"universe\"\n\"172\",\"database-first\"\n\"172\",\"camera-roll\"\n\"172\",\"routed-commands\"\n\"172\",\"blue-green-deployment\"\n\"172\",\"errordocument\"\n\"172\",\"dynamicobject\"\n\"172\",\"onscrolllistener\"\n\"172\",\"pydoc\"\n\"172\",\"typhoon\"\n\"172\",\"typescript1.5\"\n\"172\",\"sys.path\"\n\"172\",\"asynchronous-javascript\"\n\"172\",\"auctex\"\n\"172\",\"plotmath\"\n\"172\",\"table-variable\"\n\"172\",\"james\"\n\"172\",\"tabletools\"\n\"172\",\"tanstack\"\n\"172\",\"devforce\"\n\"172\",\"asp.net-controls\"\n\"172\",\"testng.xml\"\n\"172\",\"ldap3\"\n\"172\",\"ollama\"\n\"172\",\"performanceanalytics\"\n\"172\",\"qcustomplot\"\n\"172\",\"textmatebundles\"\n\"172\",\"mp4parser\"\n\"172\",\"tomcat8.5\"\n\"172\",\"amazon-cloudwatch-metrics\"\n\"172\",\"lines-of-code\"\n\"172\",\"dangling-pointer\"\n\"172\",\"particle-swarm\"\n\"172\",\"parameter-expansion\"\n\"172\",\"zipoutputstream\"\n\"172\",\"suitetalk\"\n\"172\",\"utl-file\"\n\"172\",\"helm3\"\n\"172\",\"tosca\"\n\"171\",\"phpactiverecord\"\n\"171\",\"livewires\"\n\"171\",\"anythingslider\"\n\"171\",\"sliding-tile-puzzle\"\n\"171\",\"debuggervisualizer\"\n\"171\",\"federated-learning\"\n\"171\",\"bash-trap\"\n\"171\",\"flash-builder4.5\"\n\"171\",\"dispatchevent\"\n\"171\",\"saaj\"\n\"171\",\"jsxgraph\"\n\"171\",\"python-rq\"\n\"171\",\"xfa\"\n\"171\",\"uitableviewrowaction\"\n\"171\",\"binarywriter\"\n\"171\",\"imagesharp\"\n\"171\",\"fuzzyjoin\"\n\"171\",\"google-voice\"\n\"171\",\"serviceloader\"\n\"171\",\"rtools\"\n\"171\",\"ora-00904\"\n\"171\",\"kie-workbench\"\n\"171\",\"windows-app-sdk\"\n\"171\",\"oracle-pro-c\"\n\"171\",\"away3d\"\n\"171\",\"angularjs-ng-show\"\n\"171\",\"pcntl\"\n\"171\",\"modelsummary\"\n\"171\",\"fotorama\"\n\"171\",\"newsstand-kit\"\n\"171\",\"absolutelayout\"\n\"171\",\"4g\"\n\"171\",\"systemtime\"\n\"171\",\"plivo\"\n\"171\",\"dotcloud\"\n\"171\",\"chained\"\n\"171\",\"electronic-signature\"\n\"171\",\"httpclientfactory\"\n\"171\",\"ldif\"\n\"171\",\"react-forwardref\"\n\"171\",\"angular-event-emitter\"\n\"171\",\"using-declaration\"\n\"171\",\"maya-api\"\n\"171\",\"userprincipal\"\n\"171\",\"qt5.4\"\n\"171\",\"struts-config\"\n\"171\",\"script-component\"\n\"170\",\"webxr\"\n\"170\",\"xwiki\"\n\"170\",\"reinstall\"\n\"170\",\"wgs84\"\n\"170\",\"listings\"\n\"170\",\"incremental-build\"\n\"170\",\"finagle\"\n\"170\",\"sahi\"\n\"170\",\"fileparsing\"\n\"170\",\"page-curl\"\n\"170\",\"package-name\"\n\"170\",\"impdp\"\n\"170\",\"semantic-mediawiki\"\n\"170\",\"ng-messages\"\n\"170\",\"chr\"\n\"170\",\"safari-web-inspector\"\n\"170\",\"selenium-grid2\"\n\"170\",\"distance-matrix\"\n\"170\",\"implicits\"\n\"170\",\"datebox\"\n\"170\",\"vaex\"\n\"170\",\"mysqli-multi-query\"\n\"170\",\"document-library\"\n\"170\",\"mgtwitterengine\"\n\"170\",\"workday-api\"\n\"170\",\"suppress\"\n\"170\",\"karma-webpack\"\n\"170\",\"wso2-bam\"\n\"170\",\"wow64\"\n\"170\",\"type-declaration\"\n\"170\",\"pyro\"\n\"170\",\"magit\"\n\"170\",\"authlib\"\n\"170\",\"wordpress-featured-image\"\n\"170\",\"formik-material-ui\"\n\"170\",\"google-chrome-arc\"\n\"170\",\"vitamio\"\n\"170\",\"braintree-sandbox\"\n\"170\",\".net-core-rc2\"\n\"170\",\"devexpress-wpf\"\n\"170\",\"hibernate-spatial\"\n\"170\",\"hivemq\"\n\"170\",\"uidevice\"\n\"170\",\"c++-faq\"\n\"170\",\"regions\"\n\"170\",\"handlebarshelper\"\n\"170\",\"radians\"\n\"170\",\"pervasive-sql\"\n\"170\",\"cubic-spline\"\n\"170\",\"member-variables\"\n\"170\",\"httpresponsemessage\"\n\"170\",\"mount-point\"\n\"170\",\"ipython-magic\"\n\"170\",\"string.h\"\n\"170\",\"batch-insert\"\n\"170\",\"linearlayoutmanager\"\n\"170\",\"transactionmanager\"\n\"170\",\"encoder-decoder\"\n\"170\",\"bfg-repo-cleaner\"\n\"169\",\"answer-set-programming\"\n\"169\",\"eclipse-emf-ecore\"\n\"169\",\"primeng-dropdowns\"\n\"169\",\"ssrs-2017\"\n\"169\",\"default-method\"\n\"169\",\"repair\"\n\"169\",\"flutter-build\"\n\"169\",\"backpressure\"\n\"169\",\"pipelining\"\n\"169\",\"directsound\"\n\"169\",\"mariadb-10.4\"\n\"169\",\"dbd\"\n\"169\",\"ng-pattern\"\n\"169\",\"api-ai\"\n\"169\",\"flask-session\"\n\"169\",\"js-scrollintoview\"\n\"169\",\"cascade-classifier\"\n\"169\",\"v-data-table\"\n\"169\",\"family-tree\"\n\"169\",\"keras-tuner\"\n\"169\",\"angular-ui-select\"\n\"169\",\"grafana-api\"\n\"169\",\"equivalence\"\n\"169\",\"nss\"\n\"169\",\"boost-build\"\n\"169\",\"horizontallist\"\n\"169\",\"enum-flags\"\n\"169\",\"hotdeploy\"\n\"169\",\"spring-properties\"\n\"169\",\"libtooling\"\n\"169\",\"system.net.webexception\"\n\"169\",\"magicmock\"\n\"169\",\"sideloading\"\n\"169\",\"wiredtiger\"\n\"169\",\"bsxfun\"\n\"169\",\"kiwi-tcms\"\n\"169\",\"brotli\"\n\"169\",\"ransac\"\n\"169\",\"visual-recognition\"\n\"169\",\"code-signing-entitlements\"\n\"169\",\"luau\"\n\"169\",\"node-telegram-bot-api\"\n\"169\",\"hippocms\"\n\"169\",\"android-googleapiclient\"\n\"169\",\"mpmediaquery\"\n\"169\",\"long-double\"\n\"169\",\"csvreader\"\n\"169\",\"resignfirstresponder\"\n\"169\",\"odata-v4\"\n\"169\",\"loop-unrolling\"\n\"169\",\"nnet\"\n\"169\",\"preferredsize\"\n\"169\",\"iequatable\"\n\"169\",\"amazon-elastic-transcoder\"\n\"169\",\"dart-mirrors\"\n\"169\",\"stm32f7\"\n\"169\",\"liipimaginebundle\"\n\"169\",\"lightgallery\"\n\"169\",\"qt4.7\"\n\"168\",\"fetchall\"\n\"168\",\"bag\"\n\"168\",\"stack-pointer\"\n\"168\",\"multiple-value\"\n\"168\",\"ecdf\"\n\"168\",\"city\"\n\"168\",\"function-object\"\n\"168\",\"jsonexception\"\n\"168\",\"function-handle\"\n\"168\",\"sensu\"\n\"168\",\"distributed-cache\"\n\"168\",\"bitblt\"\n\"168\",\"ice\"\n\"168\",\"grails-2.3\"\n\"168\",\"role-base-authorization\"\n\"168\",\"html-pdf\"\n\"168\",\"dwg\"\n\"168\",\"hresult\"\n\"168\",\"in-house-distribution\"\n\"168\",\"codekit\"\n\"168\",\"for-xml\"\n\"168\",\"libsndfile\"\n\"168\",\"cocos3d\"\n\"168\",\"vlan\"\n\"168\",\"network-connection\"\n\"168\",\"signaturepad\"\n\"168\",\"android-connectivitymanager\"\n\"168\",\"dialect\"\n\"168\",\"highslide\"\n\"168\",\"rad-controls\"\n\"168\",\"direct3d9\"\n\"168\",\"mersenne-twister\"\n\"168\",\"octokit\"\n\"168\",\"custom-domain\"\n\"168\",\"geosphere\"\n\"168\",\"resin\"\n\"168\",\"resthighlevelclient\"\n\"168\",\"channelfactory\"\n\"168\",\"thrift-protocol\"\n\"168\",\"webmock\"\n\"168\",\"lightning\"\n\"168\",\"user-defined-literals\"\n\"168\",\"preconditions\"\n\"168\",\"sonarqube-4.5\"\n\"168\",\"mddialog\"\n\"167\",\"markup-extensions\"\n\"167\",\"remote-validation\"\n\"167\",\"load-time-weaving\"\n\"167\",\"flowable\"\n\"167\",\"vue-material\"\n\"167\",\"php-java-bridge\"\n\"167\",\"flink-batch\"\n\"167\",\"ssm\"\n\"167\",\"principal\"\n\"167\",\"ssml\"\n\"167\",\"gigya\"\n\"167\",\"fusedlocationproviderclient\"\n\"167\",\"kubernetes-operator\"\n\"167\",\"contao\"\n\"167\",\"unhandled\"\n\"167\",\"volttron\"\n\"167\",\"rust-axum\"\n\"167\",\"const-iterator\"\n\"167\",\"confluent-kafka-dotnet\"\n\"167\",\"pandas-profiling\"\n\"167\",\"jsonmodel\"\n\"167\",\"fscalendar\"\n\"167\",\"aws-mobilehub\"\n\"167\",\"ruby-2.3\"\n\"167\",\"vector-tiles\"\n\"167\",\"alchemyapi\"\n\"167\",\"crystal-reports-8.5\"\n\"167\",\"aws-powershell\"\n\"167\",\"docusignconnect\"\n\"167\",\"mfmailcomposer\"\n\"167\",\"axwindowsmediaplayer\"\n\"167\",\"portable-applications\"\n\"167\",\"module-info\"\n\"167\",\"htmltidy\"\n\"167\",\"countdownlatch\"\n\"167\",\"derived-column\"\n\"167\",\"gulp-sourcemaps\"\n\"167\",\"external-process\"\n\"167\",\"object-model\"\n\"167\",\"newlib\"\n\"167\",\"oc4j\"\n\"167\",\"pygsheets\"\n\"167\",\"netmq\"\n\"167\",\"google-analytics-filters\"\n\"167\",\"showcaseview\"\n\"167\",\"gooddata\"\n\"167\",\"sysinternals\"\n\"167\",\"drupal-themes\"\n\"167\",\"gamma\"\n\"167\",\"double-buffering\"\n\"167\",\"bulkloader\"\n\"167\",\"isnullorempty\"\n\"167\",\"tofixed\"\n\"167\",\"x-cart\"\n\"167\",\"laravel-relations\"\n\"167\",\"angular-e2e\"\n\"167\",\"splat\"\n\"167\",\"nintex-workflow\"\n\"167\",\"ctf\"\n\"167\",\"conda-build\"\n\"167\",\"msal-react\"\n\"167\",\"image-formats\"\n\"167\",\"sony-xperia\"\n\"167\",\"bbpress\"\n\"167\",\"ms-wopi\"\n\"167\",\"argb\"\n\"167\",\"ember-cli-mirage\"\n\"167\",\"mdc-components\"\n\"167\",\"aliases\"\n\"167\",\"automata-theory\"\n\"167\",\"ietf-netmod-yang\"\n\"167\",\"std-variant\"\n\"167\",\"stm32f1\"\n\"166\",\"deap\"\n\"166\",\"skeleton-css-boilerplate\"\n\"166\",\"multiscreen\"\n\"166\",\"ff\"\n\"166\",\"jsfl\"\n\"166\",\"vqmod\"\n\"166\",\"apiary.io\"\n\"166\",\"indexpath\"\n\"166\",\"mapped-drive\"\n\"166\",\"cassandra-cli\"\n\"166\",\"adyen\"\n\"166\",\"session-scope\"\n\"166\",\"dmarc\"\n\"166\",\"windows-api-code-pack\"\n\"166\",\"cancel-button\"\n\"166\",\"jhipster-registry\"\n\"166\",\"indirection\"\n\"166\",\"postgresql-15\"\n\"166\",\"http4s\"\n\"166\",\"opencascade\"\n\"166\",\"boost-preprocessor\"\n\"166\",\"deploying\"\n\"166\",\"jquery-on\"\n\"166\",\"silverlight-oob\"\n\"166\",\"jquery-pagination\"\n\"166\",\"2checkout\"\n\"166\",\".app\"\n\"166\",\"ancestor\"\n\"166\",\"wmv\"\n\"166\",\"typescript3.0\"\n\"166\",\"extractor\"\n\"166\",\"fqdn\"\n\"166\",\"async-ctp\"\n\"166\",\"mobiscroll\"\n\"166\",\"jain-sip\"\n\"166\",\"asp.net-mvc-views\"\n\"166\",\"continuation-passing\"\n\"166\",\"quicklisp\"\n\"166\",\"highlight.js\"\n\"166\",\"uint8array\"\n\"166\",\"ekeventkit\"\n\"166\",\"action-button\"\n\"166\",\"stringdist\"\n\"166\",\"lcm\"\n\"166\",\"ios9.1\"\n\"166\",\"cubes\"\n\"166\",\"dummy-data\"\n\"166\",\"columnheader\"\n\"166\",\"lazyvgrid\"\n\"166\",\"throwable\"\n\"166\",\"stdasync\"\n\"166\",\"likert\"\n\"166\",\"solr5\"\n\"166\",\"hbm\"\n\"166\",\"traitsui\"\n\"166\",\"sts-securitytokenservice\"\n\"166\",\"google-optimize\"\n\"166\",\"armv6\"\n\"166\",\"usps\"\n\"165\",\"flutter-alertdialog\"\n\"165\",\"jcreator\"\n\"165\",\"trunk\"\n\"165\",\"gii\"\n\"165\",\"react-native-vector-icons\"\n\"165\",\"sizzle\"\n\"165\",\"phppgadmin\"\n\"165\",\"llblgenpro\"\n\"165\",\"phishing\"\n\"165\",\"sycl\"\n\"165\",\"directx-10\"\n\"165\",\"catch2\"\n\"165\",\"apache-spark-standalone\"\n\"165\",\"symfony-2.4\"\n\"165\",\"api-doc\"\n\"165\",\"simpletest\"\n\"165\",\"wikidata-api\"\n\"165\",\"update-attributes\"\n\"165\",\"gplots\"\n\"165\",\"puredata\"\n\"165\",\"two-way\"\n\"165\",\"jquery-blockui\"\n\"165\",\"cpuid\"\n\"165\",\"mongodb-stitch\"\n\"165\",\"pyjnius\"\n\"165\",\"mach\"\n\"165\",\"wix4\"\n\"165\",\"minimongo\"\n\"165\",\"attr-accessor\"\n\"165\",\".net-4.6.2\"\n\"165\",\"nsurlconnectiondelegate\"\n\"165\",\"mkpinannotationview\"\n\"165\",\"game-center-leaderboard\"\n\"165\",\"openvms\"\n\"165\",\"tinybutstrong\"\n\"165\",\"android-looper\"\n\"165\",\"google-cloud-cdn\"\n\"165\",\"chaincode\"\n\"165\",\"string-interning\"\n\"165\",\"pyvista\"\n\"165\",\"startupscript\"\n\"165\",\"autocompleteextender\"\n\"165\",\"argumentnullexception\"\n\"165\",\"email-bounces\"\n\"165\",\"structural-equation-model\"\n\"164\",\"widevine\"\n\"164\",\"react-native-tabnavigator\"\n\"164\",\"groovy-console\"\n\"164\",\"temporal-workflow\"\n\"164\",\"jdom-2\"\n\"164\",\"mat-select\"\n\"164\",\"clipboarddata\"\n\"164\",\"instant\"\n\"164\",\"yahoo-weather-api\"\n\"164\",\"php-password-hash\"\n\"164\",\"cloneable\"\n\"164\",\"banno-digital-toolkit\"\n\"164\",\"fdt\"\n\"164\",\"uniform-distribution\"\n\"164\",\"sendgrid-templates\"\n\"164\",\"xdotool\"\n\"164\",\"configurable\"\n\"164\",\"fuelux\"\n\"164\",\"pjsua2\"\n\"164\",\"saiku\"\n\"164\",\"consolidation\"\n\"164\",\"django-jsonfield\"\n\"164\",\"dashcode\"\n\"164\",\"crx\"\n\"164\",\"fastercsv\"\n\"164\",\"jmap\"\n\"164\",\"c-api\"\n\"164\",\"interpretation\"\n\"164\",\"azure-app-registration\"\n\"164\",\"spring-cloud-gcp\"\n\"164\",\"azure-cli2\"\n\"164\",\"wso2-governance-registry\"\n\"164\",\"word-web-addins\"\n\"164\",\"invoke-sqlcmd\"\n\"164\",\"intershop\"\n\"164\",\"android-wallpaper\"\n\"164\",\".d.ts\"\n\"164\",\"video-codecs\"\n\"164\",\"broccolijs\"\n\"164\",\"system.data\"\n\"164\",\"non-standard-evaluation\"\n\"164\",\"reformatting\"\n\"164\",\"aspnet-compiler\"\n\"164\",\"asp.net-core-hosted-services\"\n\"164\",\"tchar\"\n\"164\",\"npm-build\"\n\"164\",\"sql-drop\"\n\"164\",\"devextreme-angular\"\n\"164\",\"gdlib\"\n\"164\",\"mod-deflate\"\n\"164\",\"xcode-bots\"\n\"164\",\"pmap\"\n\"164\",\"toctree\"\n\"164\",\"property-binding\"\n\"164\",\"react-custom-hooks\"\n\"164\",\"elki\"\n\"164\",\"project-reference\"\n\"164\",\"static-block\"\n\"164\",\"sparqlwrapper\"\n\"164\",\"spark-jobserver\"\n\"164\",\"subproject\"\n\"164\",\"tibco-business-works\"\n\"164\",\"zipinputstream\"\n\"164\",\"qsqldatabase\"\n\"163\",\"flipview\"\n\"163\",\"background-foreground\"\n\"163\",\"clingo\"\n\"163\",\"ecslidingviewcontroller\"\n\"163\",\"teensy\"\n\"163\",\"temenos-quantum\"\n\"163\",\"yii2-user\"\n\"163\",\"cloudhub\"\n\"163\",\"file-comparison\"\n\"163\",\"filelist\"\n\"163\",\"catiledlayer\"\n\"163\",\"python-interactive\"\n\"163\",\"symfony-2.6\"\n\"163\",\"datejs\"\n\"163\",\"freezed\"\n\"163\",\"file-pointer\"\n\"163\",\"flann\"\n\"163\",\"conflicting-libraries\"\n\"163\",\"laminas\"\n\"163\",\"select-case\"\n\"163\",\"datalog\"\n\"163\",\"cannon.js\"\n\"163\",\"shareactionprovider\"\n\"163\",\"createobject\"\n\"163\",\"joomla1.6\"\n\"163\",\"vbox\"\n\"163\",\"service-principal\"\n\"163\",\"angular-material-7\"\n\"163\",\"grapesjs\"\n\"163\",\"capifony\"\n\"163\",\"kendo-window\"\n\"163\",\"unsigned-long-long-int\"\n\"163\",\"svnignore\"\n\"163\",\"path-dependent-type\"\n\"163\",\"dynamic-url\"\n\"163\",\"powerapps-modeldriven\"\n\"163\",\"boost-iostreams\"\n\"163\",\"neighbours\"\n\"163\",\"aquamacs\"\n\"163\",\"pomelo-entityframeworkcore-mysql\"\n\"163\",\"dynamic-feature-module\"\n\"163\",\"winreg\"\n\"163\",\"browser-feature-detection\"\n\"163\",\"ocamlbuild\"\n\"163\",\"libcrypto\"\n\"163\",\"fpc\"\n\"163\",\"woocommerce-bookings\"\n\"163\",\"cadvisor\"\n\"163\",\"drupal-blocks\"\n\"163\",\"drop-duplicates\"\n\"163\",\"redux-framework\"\n\"163\",\"poi-hssf\"\n\"163\",\"drools-fusion\"\n\"163\",\"ets\"\n\"163\",\"qapplication\"\n\"163\",\"httpexception\"\n\"163\",\"actiontext\"\n\"163\",\"react-18\"\n\"163\",\"reach-router\"\n\"163\",\"geomesa\"\n\"163\",\"sdl-image\"\n\"163\",\"webhttpbinding\"\n\"163\",\"bbedit\"\n\"163\",\"torchtext\"\n\"163\",\"cxfrs\"\n\"163\",\"mediadevices\"\n\"163\",\"compiled\"\n\"163\",\"time-and-attendance\"\n\"163\",\"usdz\"\n\"163\",\"parameterization\"\n\"162\",\"gridfs-stream\"\n\"162\",\"flutter-design\"\n\"162\",\"stack-smash\"\n\"162\",\"react-native-image\"\n\"162\",\"defineproperty\"\n\"162\",\"apache-echarts\"\n\"162\",\"music21\"\n\"162\",\"closed-captions\"\n\"162\",\"great-expectations\"\n\"162\",\"slate.js\"\n\"162\",\"phpquery\"\n\"162\",\"unauthorizedaccessexcepti\"\n\"162\",\"paintevent\"\n\"162\",\"free-monad\"\n\"162\",\"fs2\"\n\"162\",\"servicehost\"\n\"162\",\"verbose\"\n\"162\",\"wavelet-transform\"\n\"162\",\"window.opener\"\n\"162\",\"w2ui\"\n\"162\",\"react-test-renderer\"\n\"162\",\"cortex-a\"\n\"162\",\"bonecp\"\n\"162\",\"sap-business-technology-platform\"\n\"162\",\"jquery-draggable\"\n\"162\",\"dynamic-rdlc-generation\"\n\"162\",\"error-messaging\"\n\"162\",\"jquery-traversing\"\n\"162\",\"hpricot\"\n\"162\",\"inversion\"\n\"162\",\"rampart\"\n\"162\",\"rfe\"\n\"162\",\"tabbed\"\n\"162\",\"reverse-dns\"\n\"162\",\"r-haven\"\n\"162\",\"libwebsockets\"\n\"162\",\"asp.net-mvc-3-areas\"\n\"162\",\"wxmaxima\"\n\"162\",\"gcsfuse\"\n\"162\",\"dotnetbrowser\"\n\"162\",\"controlvalueaccessor\"\n\"162\",\"uiscenedelegate\"\n\"162\",\"targets\"\n\"162\",\"export-csv\"\n\"162\",\"android-jetpack-datastore\"\n\"162\",\"testbed\"\n\"162\",\"collate\"\n\"162\",\"genetic\"\n\"162\",\"hud\"\n\"162\",\"resize-image\"\n\"162\",\"ether\"\n\"162\",\"react-native-code-push\"\n\"162\",\"office-store\"\n\"162\",\"exact-online\"\n\"162\",\"ifnull\"\n\"162\",\"webjob\"\n\"162\",\"footnotes\"\n\"161\",\"local-files\"\n\"161\",\"flatpak\"\n\"161\",\"sybase-asa\"\n\"161\",\"catransition\"\n\"161\",\"pagedown\"\n\"161\",\"castor\"\n\"161\",\"ng-upgrade\"\n\"161\",\"make.com\"\n\"161\",\"connection-reset\"\n\"161\",\"run-configuration\"\n\"161\",\"ng-dialog\"\n\"161\",\"dita-ot\"\n\"161\",\"oracle-golden-gate\"\n\"161\",\"angular-module-federation\"\n\"161\",\"oracle-aq\"\n\"161\",\"data-lake\"\n\"161\",\"wagtail-admin\"\n\"161\",\"jmc\"\n\"161\",\"fb.ui\"\n\"161\",\"gradle-task\"\n\"161\",\"pybluez\"\n\"161\",\"docker-buildkit\"\n\"161\",\"blogengine.net\"\n\"161\",\"dynpro\"\n\"161\",\"nspasteboard\"\n\"161\",\"boost-function\"\n\"161\",\"erl\"\n\"161\",\"dendextend\"\n\"161\",\"payment-method\"\n\"161\",\"jupyter-irkernel\"\n\"161\",\"do-notation\"\n\"161\",\"word-automation\"\n\"161\",\"syntaxhighlighter\"\n\"161\",\"lxml.html\"\n\"161\",\"exslt\"\n\"161\",\"opensocial\"\n\"161\",\"c++-amp\"\n\"161\",\"hive-serde\"\n\"161\",\"azure-yaml-pipelines\"\n\"161\",\"tinytex\"\n\"161\",\"xcrun\"\n\"161\",\"hashable\"\n\"161\",\"peek\"\n\"161\",\"cfwheels\"\n\"161\",\"hvplot\"\n\"161\",\"elastic4s\"\n\"161\",\"irrlicht\"\n\"161\",\"project-server\"\n\"161\",\"eventargs\"\n\"161\",\"elasticsearch-jest\"\n\"161\",\"autonumber\"\n\"161\",\"scriptblock\"\n\"160\",\"trpc\"\n\"160\",\"apache-commons-vfs\"\n\"160\",\"apache-httpclient-5.x\"\n\"160\",\"local-network\"\n\"160\",\"phoenix-live-view\"\n\"160\",\"musl\"\n\"160\",\"github-graphql\"\n\"160\",\"ln\"\n\"160\",\"literate-programming\"\n\"160\",\"confluent-cloud\"\n\"160\",\"socialshare\"\n\"160\",\"findby\"\n\"160\",\"jsonobjectrequest\"\n\"160\",\"fingerprinting\"\n\"160\",\"soda\"\n\"160\",\"aviary\"\n\"160\",\"ready\"\n\"160\",\"ibm-cloud-private\"\n\"160\",\"simplewebrtc\"\n\"160\",\"epsilon\"\n\"160\",\"mod-perl2\"\n\"160\",\"type-assertion\"\n\"160\",\"boost-phoenix\"\n\"160\",\"bootcompleted\"\n\"160\",\"sample-rate\"\n\"160\",\"pdf-scraping\"\n\"160\",\"neovim-plugin\"\n\"160\",\"attiny\"\n\"160\",\"build-pipeline\"\n\"160\",\"typed-racket\"\n\"160\",\"libssl\"\n\"160\",\"object-destructuring\"\n\"160\",\"audacity\"\n\"160\",\"caffeine-cache\"\n\"160\",\"mlcp\"\n\"160\",\"tally\"\n\"160\",\"mkmapviewdelegate\"\n\"160\",\"http-status-code-502\"\n\"160\",\"iplimage\"\n\"160\",\"personalization\"\n\"160\",\"stride\"\n\"160\",\"custom-functions-excel\"\n\"160\",\"hessian\"\n\"160\",\"multi-agent\"\n\"160\",\"ember-qunit\"\n\"160\",\"conditional-breakpoint\"\n\"160\",\"pgi\"\n\"160\",\"hbox\"\n\"160\",\"arcgis-runtime\"\n\"160\",\"multicastsocket\"\n\"160\",\"touchstart\"\n\"160\",\"sony-camera-api\"\n\"159\",\"proc-open\"\n\"159\",\"stability\"\n\"159\",\"flurry-analytics\"\n\"159\",\"gridstack\"\n\"159\",\"x-www-form-urlencoded\"\n\"159\",\"grid-system\"\n\"159\",\"triangular\"\n\"159\",\"xuggle\"\n\"159\",\"feof\"\n\"159\",\"xquery-3.0\"\n\"159\",\"python-attrs\"\n\"159\",\"python-babel\"\n\"159\",\"select-into\"\n\"159\",\"ngen\"\n\"159\",\"frustum\"\n\"159\",\"find-in-set\"\n\"159\",\"python-chess\"\n\"159\",\"ryu\"\n\"159\",\"recordrtc\"\n\"159\",\"angular-signals\"\n\"159\",\"database-tuning\"\n\"159\",\"robust\"\n\"159\",\"sequencefile\"\n\"159\",\"css-counter\"\n\"159\",\"microsoft-fabric\"\n\"159\",\"wcf-endpoint\"\n\"159\",\"ibmhttpserver\"\n\"159\",\"data-collection\"\n\"159\",\"role-based-access-control\"\n\"159\",\"wpf-animation\"\n\"159\",\"dvc\"\n\"159\",\"anglesharp\"\n\"159\",\"passport-jwt\"\n\"159\",\"entitydatasource\"\n\"159\",\"infobox\"\n\"159\",\"apple-id\"\n\"159\",\"nssplitview\"\n\"159\",\"mod-mono\"\n\"159\",\"spring-native\"\n\"159\",\"e2e\"\n\"159\",\"java.nio.file\"\n\"159\",\"mailing\"\n\"159\",\"rdf4j\"\n\"159\",\"mailboxer\"\n\"159\",\"javascript-namespaces\"\n\"159\",\"extended-sql\"\n\"159\",\"kotlin-null-safety\"\n\"159\",\"expss\"\n\"159\",\"reflections\"\n\"159\",\"hashbang\"\n\"159\",\"tailwind-3\"\n\"159\",\"tmx\"\n\"159\",\"generic-handler\"\n\"159\",\"nhibernate-mapping-by-code\"\n\"159\",\"node-canvas\"\n\"159\",\"responsetext\"\n\"159\",\"chain-of-responsibility\"\n\"159\",\"esp8266wifi\"\n\"159\",\"promela\"\n\"159\",\"flvplayback\"\n\"159\",\"autocompletebox\"\n\"159\",\"zend-translate\"\n\"159\",\"quarkus-reactive\"\n\"159\",\"google-plugin-eclipse\"\n\"159\",\"soil\"\n\"159\",\"amazon-mq\"\n\"159\",\"soundex\"\n\"159\",\"touchpad\"\n\"159\",\"custom-type\"\n\"158\",\"phpfox\"\n\"158\",\"ecore\"\n\"158\",\"vsync\"\n\"158\",\"fiddlercore\"\n\"158\",\"cmp\"\n\"158\",\"intellij-lombok-plugin\"\n\"158\",\"smartclient\"\n\"158\",\"sles\"\n\"158\",\"filenet-content-engine\"\n\"158\",\"munit\"\n\"158\",\"primefaces-datatable\"\n\"158\",\"grouped-bar-chart\"\n\"158\",\"adobe-animate\"\n\"158\",\"python-3.12\"\n\"158\",\"jsf-2.3\"\n\"158\",\"soapserver\"\n\"158\",\"swiftui-charts\"\n\"158\",\"appfabric-cache\"\n\"158\",\"self-modifying\"\n\"158\",\"datetime2\"\n\"158\",\"adam\"\n\"158\",\"aws-media-convert\"\n\"158\",\"pulse\"\n\"158\",\"angular-material-5\"\n\"158\",\"server-side-scripting\"\n\"158\",\"microformats\"\n\"158\",\"w3-total-cache\"\n\"158\",\"capslock\"\n\"158\",\"django-piston\"\n\"158\",\"r-maptools\"\n\"158\",\"gpsd\"\n\"158\",\"data-kinds\"\n\"158\",\"infusionsoft\"\n\"158\",\"one-liner\"\n\"158\",\"boxapiv2\"\n\"158\",\"interpreted-language\"\n\"158\",\"mod-security2\"\n\"158\",\"entity-component-system\"\n\"158\",\"setenv\"\n\"158\",\"for-of-loop\"\n\"158\",\"bsod\"\n\"158\",\"browser-support\"\n\"158\",\"branching-strategy\"\n\"158\",\"vlfeat\"\n\"158\",\"orchard-modules\"\n\"158\",\"or-operator\"\n\"158\",\"refs\"\n\"158\",\"tdbgrid\"\n\"158\",\"ntdll\"\n\"158\",\"x12\"\n\"158\",\"device-policy-manager\"\n\"158\",\"mergetool\"\n\"158\",\"angularjs-1.6\"\n\"158\",\"lto\"\n\"158\",\"loop-invariant\"\n\"158\",\"rescale\"\n\"158\",\"mpld3\"\n\"158\",\"qqmlcomponent\"\n\"158\",\"before-save\"\n\"157\",\"greenlets\"\n\"157\",\"mvcjqgrid\"\n\"157\",\"skip-lists\"\n\"157\",\"yii2-validation\"\n\"157\",\"packery\"\n\"157\",\"kylin\"\n\"157\",\"mainwindow\"\n\"157\",\"j-security-check\"\n\"157\",\"firebase-job-dispatcher\"\n\"157\",\"snmp-trap\"\n\"157\",\"market-basket-analysis\"\n\"157\",\"jsonlines\"\n\"157\",\"angular-lifecycle-hooks\"\n\"157\",\"vba6\"\n\"157\",\"keycloak-connect\"\n\"157\",\"facebook-permissions\"\n\"157\",\"oracle-apex-19.1\"\n\"157\",\"kendo-template\"\n\"157\",\"django-messages\"\n\"157\",\"aws-iot-greengrass\"\n\"157\",\"jquery-bootgrid\"\n\"157\",\"gtk2hs\"\n\"157\",\"htmlcollection\"\n\"157\",\"posting\"\n\"157\",\"dynamic-languages\"\n\"157\",\"fable-f#\"\n\"157\",\"visual-paradigm\"\n\"157\",\"amazon-timestream\"\n\"157\",\"caddyfile\"\n\"157\",\"hana-sql-script\"\n\"157\",\"reference-class\"\n\"157\",\"uimodalpresentationstyle\"\n\"157\",\"gaussian-process\"\n\"157\",\"device-owner\"\n\"157\",\"tlistview\"\n\"157\",\"npm-request\"\n\"157\",\"onconfigurationchanged\"\n\"157\",\"http-status-code-415\"\n\"157\",\"spin\"\n\"157\",\"prolog-toplevel\"\n\"157\",\"chalice\"\n\"157\",\"heroku-api\"\n\"157\",\"always-on-top\"\n\"157\",\"sorbet\"\n\"157\",\"prettytable\"\n\"157\",\"flutter-method-channel\"\n\"157\",\"userinfo\"\n\"156\",\"backlog\"\n\"156\",\"tsung\"\n\"156\",\"grob\"\n\"156\",\"mat-tab\"\n\"156\",\"giraph\"\n\"156\",\"multi-step\"\n\"156\",\"renv\"\n\"156\",\"xtk\"\n\"156\",\"skemitternode\"\n\"156\",\"content-encoding\"\n\"156\",\"jsr352\"\n\"156\",\"vsftpd\"\n\"156\",\"package-management\"\n\"156\",\"flask-appbuilder\"\n\"156\",\"xively\"\n\"156\",\"r-s3\"\n\"156\",\"joomla-module\"\n\"156\",\"vavr\"\n\"156\",\"credential-manager\"\n\"156\",\"django-grappelli\"\n\"156\",\"in-operator\"\n\"156\",\"enoent\"\n\"156\",\"azure-blob-trigger\"\n\"156\",\"eoferror\"\n\"156\",\"ingres\"\n\"156\",\"pdf-extraction\"\n\"156\",\"postgrest\"\n\"156\",\"postgres-fdw\"\n\"156\",\"kohana-3.3\"\n\"156\",\"mina\"\n\"156\",\"formal-methods\"\n\"156\",\"sysdate\"\n\"156\",\"minute\"\n\"156\",\"versionone\"\n\"156\",\"azure-private-link\"\n\"156\",\"hierarchyid\"\n\"156\",\"mockwebserver\"\n\"156\",\"aspell\"\n\"156\",\"xamlparseexception\"\n\"156\",\"nsworkspace\"\n\"156\",\"asp.net-profiles\"\n\"156\",\"uiscreen\"\n\"156\",\"numberformatter\"\n\"156\",\"movesense\"\n\"156\",\"column-alias\"\n\"156\",\"react-draft-wysiwyg\"\n\"156\",\"ewsjavaapi\"\n\"156\",\"activeperl\"\n\"156\",\"http-patch\"\n\"156\",\"stunnel\"\n\"156\",\"array-key\"\n\"156\",\"biblatex\"\n\"156\",\"step\"\n\"156\",\"cvpixelbuffer\"\n\"156\",\"structural-search\"\n\"155\",\"wicket-1.6\"\n\"155\",\"vue-multiselect\"\n\"155\",\"filenet\"\n\"155\",\"skybox\"\n\"155\",\"github-actions-self-hosted-runners\"\n\"155\",\"websharper\"\n\"155\",\"process-management\"\n\"155\",\"fee\"\n\"155\",\"tei\"\n\"155\",\"voltdb\"\n\"155\",\"mamba\"\n\"155\",\"app-code\"\n\"155\",\"bitvector\"\n\"155\",\"pac\"\n\"155\",\"pac4j\"\n\"155\",\"french\"\n\"155\",\"checkedtextview\"\n\"155\",\"fiona\"\n\"155\",\"socialauth\"\n\"155\",\"rjags\"\n\"155\",\"laravel-breeze\"\n\"155\",\"serde-json\"\n\"155\",\"csharpcodeprovider\"\n\"155\",\"oracle-maf\"\n\"155\",\"wandb\"\n\"155\",\"inlining\"\n\"155\",\"correctness\"\n\"155\",\"nsmutabledata\"\n\"155\",\"html5-fullscreen\"\n\"155\",\"payum\"\n\"155\",\"pearson\"\n\"155\",\"ampps\"\n\"155\",\"acceleo\"\n\"155\",\"kotlin-multiplatform-mobile\"\n\"155\",\"codebase\"\n\"155\",\"newtype\"\n\"155\",\"woff\"\n\"155\",\"videocall\"\n\"155\",\"node-soap\"\n\"155\",\"drop-table\"\n\"155\",\"modalpopup\"\n\"155\",\"b2b\"\n\"155\",\"gdprconsentform\"\n\"155\",\"hardcode\"\n\"155\",\"zxing.net\"\n\"155\",\"elixir-iex\"\n\"155\",\"getboundingclientrect\"\n\"155\",\"odoo-17\"\n\"155\",\"google-cloud-billing\"\n\"155\",\"memory-access\"\n\"155\",\"script-fu\"\n\"155\",\"fold-expression\"\n\"155\",\"custom-tags\"\n\"155\",\"tf.data.dataset\"\n\"155\",\"max-pooling\"\n\"155\",\"hex-editors\"\n\"155\",\"foreign-data-wrapper\"\n\"155\",\"auto-vectorization\"\n\"155\",\"flux.jl\"\n\"155\",\"web3-java\"\n\"155\",\"embedded-tomcat-7\"\n\"155\",\"gmail-contextual-gadgets\"\n\"154\",\"edsdk\"\n\"154\",\"echarts4r\"\n\"154\",\"webrtc-android\"\n\"154\",\"phaserjs\"\n\"154\",\"mat-dialog\"\n\"154\",\"live-sdk\"\n\"154\",\"x-sendfile\"\n\"154\",\"chrome-options\"\n\"154\",\"volt\"\n\"154\",\"xlink\"\n\"154\",\"bitwise-or\"\n\"154\",\"addressing\"\n\"154\",\"planning\"\n\"154\",\"reality-composer\"\n\"154\",\"oracle-apps\"\n\"154\",\"recursive-cte\"\n\"154\",\"nscache\"\n\"154\",\"nsinvocation\"\n\"154\",\"onpress\"\n\"154\",\"htc-vive\"\n\"154\",\"ane\"\n\"154\",\"mojibake\"\n\"154\",\"wpbakery\"\n\"154\",\"nested-generics\"\n\"154\",\"android-searchmanager\"\n\"154\",\"twine\"\n\"154\",\"error-checking\"\n\"154\",\"synthesizer\"\n\"154\",\"atan2\"\n\"154\",\"signal-strength\"\n\"154\",\"fable-r\"\n\"154\",\"org-babel\"\n\"154\",\"systrace\"\n\"154\",\".doc\"\n\"154\",\"go-cobra\"\n\"154\",\"nx-monorepo\"\n\"154\",\"word-contentcontrol\"\n\"154\",\"360-panorama\"\n\"154\",\"branding\"\n\"154\",\"j2mepolish\"\n\"154\",\"digraphs\"\n\"154\",\"tiny-tds\"\n\"154\",\"dotless\"\n\"154\",\"uisearchbardelegate\"\n\"154\",\"dhtmlx-scheduler\"\n\"154\",\"high-load\"\n\"154\",\"cakedc\"\n\"154\",\"redisearch\"\n\"154\",\"openid-provider\"\n\"154\",\"itemlistener\"\n\"154\",\"dpapi\"\n\"154\",\"qaction\"\n\"154\",\"latin\"\n\"154\",\"logstash-forwarder\"\n\"154\",\"qprogressbar\"\n\"154\",\"react-dates\"\n\"154\",\"cgrectmake\"\n\"154\",\"source-control-explorer\"\n\"154\",\"helidon\"\n\"154\",\"suffix-array\"\n\"154\",\"solarium\"\n\"154\",\"structured-bindings\"\n\"154\",\"query-cache\"\n\"154\",\"liferay-6.2\"\n\"154\",\"cypress-component-test-runner\"\n\"154\",\"steroids\"\n\"154\",\"precompiled\"\n\"154\",\"quad\"\n\"153\",\"stagefright\"\n\"153\",\"load-time\"\n\"153\",\"term-document-matrix\"\n\"153\",\"clamp\"\n\"153\",\"antixsslibrary\"\n\"153\",\"cloudify\"\n\"153\",\"six\"\n\"153\",\"remoteobject\"\n\"153\",\"feather\"\n\"153\",\"xstate\"\n\"153\",\"teiid\"\n\"153\",\"masked-array\"\n\"153\",\"clutter\"\n\"153\",\"socketcan\"\n\"153\",\"piranha-cms\"\n\"153\",\"fusionauth\"\n\"153\",\"unreal-development-kit\"\n\"153\",\"optimizely\"\n\"153\",\"farseer\"\n\"153\",\"angularjs-rootscope\"\n\"153\",\"methodinfo\"\n\"153\",\"joomla3.1\"\n\"153\",\"oracle-ords\"\n\"153\",\"rmdir\"\n\"153\",\"joomla3.2\"\n\"153\",\"dword\"\n\"153\",\"posixlt\"\n\"153\",\"junit3\"\n\"153\",\"twilio-flex\"\n\"153\",\"spring-cloud-consul\"\n\"153\",\"spring-batch-tasklet\"\n\"153\",\"cosmicmind\"\n\"153\",\"cosmos\"\n\"153\",\"build-settings\"\n\"153\",\"newrelic-platform\"\n\"153\",\"aapt2\"\n\"153\",\"revitpythonshell\"\n\"153\",\"milvus\"\n\"153\",\"ord\"\n\"153\",\"ramdisk\"\n\"153\",\"32feet\"\n\"153\",\"ucwa\"\n\"153\",\"buildfire\"\n\"153\",\"nyc\"\n\"153\",\"go-echo\"\n\"153\",\"typegoose\"\n\"153\",\"wix3.10\"\n\"153\",\"pnp-js\"\n\"153\",\"non-type\"\n\"153\",\"exporter\"\n\"153\",\"isomorphism\"\n\"153\",\"azure-storage-emulator\"\n\"153\",\"italic\"\n\"153\",\"tcxgrid\"\n\"153\",\"nofollow\"\n\"153\",\"quit\"\n\"153\",\"iscsi\"\n\"153\",\"android-module\"\n\"153\",\"getmethod\"\n\"153\",\"octree\"\n\"153\",\"activecollab\"\n\"153\",\"speex\"\n\"153\",\"streambuf\"\n\"153\",\"custom-binding\"\n\"153\",\"google-groups-api\"\n\"153\",\"zone.js\"\n\"153\",\"quic\"\n\"153\",\"bcnf\"\n\"153\",\"hedera-hashgraph\"\n\"153\",\"qslider\"\n\"153\",\"max-heap\"\n\"153\",\"gnu-toolchain\"\n\"152\",\"filehandler\"\n\"152\",\"bada\"\n\"152\",\"ansi-colors\"\n\"152\",\"flappy-bird-clone\"\n\"152\",\"symfony-2.5\"\n\"152\",\"impresspages\"\n\"152\",\"apns-php\"\n\"152\",\"indexed\"\n\"152\",\"xposed\"\n\"152\",\"imu\"\n\"152\",\"directwrite\"\n\"152\",\"django-tinymce\"\n\"152\",\"valueinjecter\"\n\"152\",\"animatewithduration\"\n\"152\",\"aws-codeartifact\"\n\"152\",\"iccube-reporting\"\n\"152\",\"ruby-debug\"\n\"152\",\"enterprise-library-5\"\n\"152\",\"turbogears2\"\n\"152\",\"tvirtualstringtree\"\n\"152\",\"scada\"\n\"152\",\"blocked\"\n\"152\",\"invoke-restmethod\"\n\"152\",\"spring-async\"\n\"152\",\"out-of-browser\"\n\"152\",\"reuseidentifier\"\n\"152\",\"osclass\"\n\"152\",\"nettopologysuite\"\n\"152\",\"extjs6-modern\"\n\"152\",\"copy-assignment\"\n\"152\",\"gear-vr\"\n\"152\",\"ganglia\"\n\"152\",\"jack\"\n\"152\",\"contiguous\"\n\"152\",\"executescalar\"\n\"152\",\"hl7-v2\"\n\"152\",\"radwindow\"\n\"152\",\"cufft\"\n\"152\",\"persistent-connection\"\n\"152\",\"community-toolkit-mvvm\"\n\"152\",\"split-apply-combine\"\n\"152\",\"layoutsubviews\"\n\"152\",\"emberfire\"\n\"152\",\"linkedhashset\"\n\"152\",\"weaviate\"\n\"152\",\"pander\"\n\"152\",\"lightweight-charts\"\n\"152\",\"flutter-integration-test\"\n\"152\",\"structured-array\"\n\"152\",\"ytdl\"\n\"152\",\"gnutls\"\n\"151\",\"cmmotionmanager\"\n\"151\",\"mason\"\n\"151\",\"treetableview\"\n\"151\",\"fluid-dynamics\"\n\"151\",\"translucency\"\n\"151\",\"fscheck\"\n\"151\",\"disk-partitioning\"\n\"151\",\"checklistbox\"\n\"151\",\"jsr\"\n\"151\",\"python-mode\"\n\"151\",\"swizzling\"\n\"151\",\"jstack\"\n\"151\",\"python-twitter\"\n\"151\",\"checkmark\"\n\"151\",\"flask-cors\"\n\"151\",\"fiware-wirecloud\"\n\"151\",\"oversampling\"\n\"151\",\"ng-tags-input\"\n\"151\",\"underflow\"\n\"151\",\"aws-billing\"\n\"151\",\"react-usememo\"\n\"151\",\"database-concurrency\"\n\"151\",\"mysql-error-1005\"\n\"151\",\"animationdrawable\"\n\"151\",\"sessionstorage\"\n\"151\",\"w3.css\"\n\"151\",\"canactivate\"\n\"151\",\"akeneo\"\n\"151\",\"rtcpeerconnection\"\n\"151\",\"pwd\"\n\"151\",\"dlna\"\n\"151\",\"dash-shell\"\n\"151\",\"internal-tables\"\n\"151\",\"tun\"\n\"151\",\"workflowservice\"\n\"151\",\"nsbezierpath\"\n\"151\",\"bndtools\"\n\"151\",\"bochs\"\n\"151\",\"android-titlebar\"\n\"151\",\"patindex\"\n\"151\",\".net-native\"\n\"151\",\"kryonet\"\n\"151\",\"cocoaasyncsocket\"\n\"151\",\"settings.settings\"\n\"151\",\"codefluent\"\n\"151\",\"word-break\"\n\"151\",\"leptonica\"\n\"151\",\"kohana-3.2\"\n\"151\",\"jasig\"\n\"151\",\"rawstring\"\n\"151\",\"less-mixins\"\n\"151\",\"orthogonal\"\n\"151\",\"xcode10.2\"\n\"151\",\"xamarin.forms.shell\"\n\"151\",\"tolower\"\n\"151\",\"nsurlsessionuploadtask\"\n\"151\",\"isin\"\n\"151\",\"android-framework\"\n\"151\",\"x3dom\"\n\"151\",\"memoryview\"\n\"151\",\"node-ffi\"\n\"151\",\"texturepacker\"\n\"151\",\"office365connectors\"\n\"151\",\"nodeclipse\"\n\"151\",\"spagobi\"\n\"151\",\"qt-installer\"\n\"151\",\"webdrivermanager-java\"\n\"151\",\"stdcall\"\n\"151\",\"automotive\"\n\"150\",\"backgrid\"\n\"150\",\"multiple-projects\"\n\"150\",\"websphere-commerce\"\n\"150\",\"phantom-dsl\"\n\"150\",\"listselectionlistener\"\n\"150\",\"mutagen\"\n\"150\",\"git-amend\"\n\"150\",\"eclipse-3.6\"\n\"150\",\"tridion-content-delivery\"\n\"150\",\"getstring\"\n\"150\",\"picamera\"\n\"150\",\"dataproc\"\n\"150\",\"data-transfer-objects\"\n\"150\",\"map-directions\"\n\"150\",\"p12\"\n\"150\",\"jtextcomponent\"\n\"150\",\"cellrenderer\"\n\"150\",\"celeryd\"\n\"150\",\"fzf\"\n\"150\",\"universal-binary\"\n\"150\",\"icriteria\"\n\"150\",\"fastparquet\"\n\"150\",\"gracenote\"\n\"150\",\"ibm-bpm\"\n\"150\",\"rman\"\n\"150\",\"kbuild\"\n\"150\",\"jquery-ajaxq\"\n\"150\",\"swagger-php\"\n\"150\",\"azure-communication-services\"\n\"150\",\"wso2-streaming-integrator\"\n\"150\",\"supersized\"\n\"150\",\"android-thread\"\n\"150\",\"coroutinescope\"\n\"150\",\"postgresql-copy\"\n\"150\",\"nsinputstream\"\n\"150\",\"pcl\"\n\"150\",\"broken-links\"\n\"150\",\"mintty\"\n\"150\",\"rapidapi\"\n\"150\",\"lxd\"\n\"150\",\"lync-2010\"\n\"150\",\"java.util.calendar\"\n\"150\",\"ostringstream\"\n\"150\",\"qwebkit\"\n\"150\",\"ntlm-authentication\"\n\"150\",\"xcode8.2\"\n\"150\",\"dotnetrdf\"\n\"150\",\"play-reactivemongo\"\n\"150\",\"garmin\"\n\"150\",\"activesupport-concern\"\n\"150\",\"mpnowplayinginfocenter\"\n\"150\",\"stochastic-process\"\n\"150\",\"account-kit\"\n\"150\",\"pencilkit\"\n\"150\",\"office-2016\"\n\"150\",\"excel-2011\"\n\"150\",\"generator-expression\"\n\"150\",\"cgimageref\"\n\"150\",\"launchimage\"\n\"150\",\"cts\"\n\"150\",\"ode45\"\n\"150\",\"healpy\"\n\"150\",\"zend-gdata\"\n\"150\",\"maxdate\"\n\"150\",\"google-swiffy\"\n\"150\",\"flutter-showmodalbottomsheet\"\n\"150\",\"google-maps-react\"\n\"150\",\"flux-influxdb\"\n\"149\",\"griffon\"\n\"149\",\"dcmtk\"\n\"149\",\"dcm4che\"\n\"149\",\"instantclient\"\n\"149\",\"flip-flop\"\n\"149\",\"listfield\"\n\"149\",\"jenkins-shared-libraries\"\n\"149\",\"insertafter\"\n\"149\",\"safearray\"\n\"149\",\"ngx-leaflet\"\n\"149\",\"aero-glass\"\n\"149\",\"smp\"\n\"149\",\"smart-device\"\n\"149\",\"findwindow\"\n\"149\",\"picking\"\n\"149\",\"kundera\"\n\"149\",\"rootview\"\n\"149\",\"angular-pwa\"\n\"149\",\"cappuccino\"\n\"149\",\"dnn9\"\n\"149\",\"kinect-v2\"\n\"149\",\"alchemy\"\n\"149\",\"bootstrap-cards\"\n\"149\",\"dynamo-local\"\n\"149\",\"apple-mail\"\n\"149\",\"open-basedir\"\n\"149\",\"pch\"\n\"149\",\"kaltura\"\n\"149\",\"popstate\"\n\"149\",\"jvm-languages\"\n\"149\",\"facebook-events\"\n\"149\",\"eye-tracking\"\n\"149\",\"should.js\"\n\"149\",\"fragment-lifecycle\"\n\"149\",\"visual-web-developer-2010\"\n\"149\",\"occi\"\n\"149\",\"opentext\"\n\"149\",\"hlist\"\n\"149\",\"mjml\"\n\"149\",\"hijri\"\n\"149\",\"home-automation\"\n\"149\",\"azure-purview\"\n\"149\",\"column-chart\"\n\"149\",\"memo\"\n\"149\",\"lorawan\"\n\"149\",\"elasticsearch-analyzers\"\n\"149\",\"spring4d\"\n\"149\",\"react-icons\"\n\"149\",\"event-binding\"\n\"149\",\"userlocation\"\n\"149\",\"amazon-sagemaker-studio\"\n\"149\",\"conda-forge\"\n\"149\",\"qtserialport\"\n\"149\",\"flutter-riverpod\"\n\"149\",\"git-worktree\"\n\"148\",\"ecma262\"\n\"148\",\"localforage\"\n\"148\",\"defensive-programming\"\n\"148\",\"template-strings\"\n\"148\",\"prism-6\"\n\"148\",\"remote-branch\"\n\"148\",\"mat-autocomplete\"\n\"148\",\"remote-connection\"\n\"148\",\"apache-commons-config\"\n\"148\",\"advanced-search\"\n\"148\",\"vpc-endpoint\"\n\"148\",\"import-maps\"\n\"148\",\"swiftui-environment\"\n\"148\",\"xnu\"\n\"148\",\"umask\"\n\"148\",\"bind-variables\"\n\"148\",\"jjwt\"\n\"148\",\"microsoft-chart-controls\"\n\"148\",\"agora-web-sdk-ng\"\n\"148\",\"sequencing\"\n\"148\",\"i3\"\n\"148\",\"django-select-related\"\n\"148\",\"faces-config\"\n\"148\",\"jqlite\"\n\"148\",\"database-cleaner\"\n\"148\",\"validationsummary\"\n\"148\",\"keyguard\"\n\"148\",\"intermediate-language\"\n\"148\",\"initialization-list\"\n\"148\",\"springmockito\"\n\"148\",\"susy-sass\"\n\"148\",\"azure-configuration\"\n\"148\",\"nested-form-for\"\n\"148\",\"opencms\"\n\"148\",\"wsse\"\n\"148\",\"mongohq\"\n\"148\",\"jquery-widgets\"\n\"148\",\"info\"\n\"148\",\"sample-data\"\n\"148\",\"microsoft-web-deploy\"\n\"148\",\"wiringpi\"\n\"148\",\"cognos-8\"\n\"148\",\"cocoalibspotify-2.0\"\n\"148\",\"networkcredentials\"\n\"148\",\"sqldelight\"\n\"148\",\"pluralize\"\n\"148\",\"drools-planner\"\n\"148\",\"sqlite-net-extensions\"\n\"148\",\"sql-parser\"\n\"148\",\"android-navigation-bar\"\n\"148\",\"mongoose-web-server\"\n\"148\",\"google-cloud-memorystore\"\n\"148\",\"protein-database\"\n\"148\",\"noaa\"\n\"148\",\"cfloop\"\n\"148\",\"laravel-storage\"\n\"148\",\"resource-management\"\n\"148\",\"mouse-listeners\"\n\"148\",\"http-token-authentication\"\n\"148\",\"ios-permissions\"\n\"148\",\"qitemdelegate\"\n\"148\",\"laravel-notification\"\n\"148\",\"color-profile\"\n\"148\",\"memmove\"\n\"148\",\"qstyleditemdelegate\"\n\"148\",\"user-tracking\"\n\"148\",\"qspinbox\"\n\"148\",\"linqkit\"\n\"148\",\"linux-namespaces\"\n\"148\",\"zipline\"\n\"148\",\"pandas-to-sql\"\n\"148\",\"zend-framework-mvc\"\n\"148\",\"google-now\"\n\"148\",\"qualcomm\"\n\"148\",\"availability\"\n\"148\",\"idictionary\"\n\"147\",\"badimageformatexception\"\n\"147\",\"eclipse-scout\"\n\"147\",\"transitive-closure\"\n\"147\",\"geturl\"\n\"147\",\"eel\"\n\"147\",\"dbms-output\"\n\"147\",\"standby\"\n\"147\",\"ecdh\"\n\"147\",\"filemtime\"\n\"147\",\"skype4com\"\n\"147\",\"firebasesimplelogin\"\n\"147\",\"catch-block\"\n\"147\",\"smooch\"\n\"147\",\"adminer\"\n\"147\",\"rust-proc-macros\"\n\"147\",\"flask-jwt-extended\"\n\"147\",\"checked-exceptions\"\n\"147\",\"rx-java3\"\n\"147\",\"image-registration\"\n\"147\",\"picklist\"\n\"147\",\"indexed-view\"\n\"147\",\"documentlistener\"\n\"147\",\"cross-product\"\n\"147\",\"microsoft-custom-vision\"\n\"147\",\"docopt\"\n\"147\",\"twitterizer\"\n\"147\",\"ionicons\"\n\"147\",\"twirl\"\n\"147\",\"hosts-file\"\n\"147\",\"bluecove\"\n\"147\",\"popviewcontroller\"\n\"147\",\"satellizer\"\n\"147\",\"nsscanner\"\n\"147\",\"mongodb-aggregation\"\n\"147\",\"letter-spacing\"\n\"147\",\"system-variable\"\n\"147\",\"2d-vector\"\n\"147\",\".net-cf-3.5\"\n\"147\",\"m3u\"\n\"147\",\"magic-square\"\n\"147\",\"openrasta\"\n\"147\",\"android-compatibility\"\n\"147\",\"exploratory-data-analysis\"\n\"147\",\"toarray\"\n\"147\",\"quorum\"\n\"147\",\"scalamock\"\n\"147\",\"hierarchical-query\"\n\"147\",\"dotconnect\"\n\"147\",\"coordinator-layout\"\n\"147\",\"table-per-type\"\n\"147\",\"bulletedlist\"\n\"147\",\"numerical-computing\"\n\"147\",\"uilongpressgesturerecogni\"\n\"147\",\"redis-cache\"\n\"147\",\"cookiecutter\"\n\"147\",\"restrict-qualifier\"\n\"147\",\"storagefile\"\n\"147\",\"petsc\"\n\"147\",\"custom-painting\"\n\"147\",\"argument-unpacking\"\n\"147\",\"msbuildcommunitytasks\"\n\"147\",\"glog\"\n\"147\",\"a-records\"\n\"147\",\"autoresetevent\"\n\"147\",\"flutter-routes\"\n\"147\",\"ppp\"\n\"147\",\"bigquery-udf\"\n\"147\",\"illegal-characters\"\n\"147\",\"uwp-maps\"\n\"147\",\"sealed-class\"\n\"147\",\"il2cpp\"\n\"146\",\"multisampling\"\n\"146\",\"webresource.axd\"\n\"146\",\"lob\"\n\"146\",\"appicon\"\n\"146\",\"firebase-in-app-messaging\"\n\"146\",\"marching-cubes\"\n\"146\",\"undefined-function\"\n\"146\",\"xmlrpclib\"\n\"146\",\"maproute\"\n\"146\",\"bitrix\"\n\"146\",\"phyloseq\"\n\"146\",\"gpflow\"\n\"146\",\"r-recipes\"\n\"146\",\"database-versioning\"\n\"146\",\"rtsp-client\"\n\"146\",\"icefaces-3\"\n\"146\",\"kernel-mode\"\n\"146\",\"ical4j\"\n\"146\",\"nanotime\"\n\"146\",\"angular-oauth2-oidc\"\n\"146\",\"valuetuple\"\n\"146\",\"keyof\"\n\"146\",\"rootviewcontroller\"\n\"146\",\"equatable\"\n\"146\",\"worldwind\"\n\"146\",\"sap-bw\"\n\"146\",\"passphrase\"\n\"146\",\"sim800\"\n\"146\",\"spring-dsl\"\n\"146\",\"bootswatch\"\n\"146\",\"mirc\"\n\"146\",\"network-flow\"\n\"146\",\"out-parameters\"\n\"146\",\"code128\"\n\"146\",\"extjs-grid\"\n\"146\",\"android-app-indexing\"\n\"146\",\"right-align\"\n\"146\",\".a\"\n\"146\",\"kofax\"\n\"146\",\"objcopy\"\n\"146\",\"revmob\"\n\"146\",\"3d-secure\"\n\"146\",\"videogular\"\n\"146\",\"object-persistence\"\n\"146\",\"gcp-ai-platform-training\"\n\"146\",\"hive-partitions\"\n\"146\",\"conversion-operator\"\n\"146\",\"quoted-identifier\"\n\"146\",\"railway\"\n\"146\",\"bus-error\"\n\"146\",\"quickfixn\"\n\"146\",\"quickblox-android\"\n\"146\",\"explicit-instantiation\"\n\"146\",\"iso-prolog\"\n\"146\",\"dotnethighcharts\"\n\"146\",\"radeditor\"\n\"146\",\"target-platform\"\n\"146\",\"core.autocrlf\"\n\"146\",\"cordova-plugin-file\"\n\"146\",\"memory-dump\"\n\"146\",\"tess-two\"\n\"146\",\"perlbrew\"\n\"146\",\"charsequence\"\n\"146\",\"str-to-date\"\n\"146\",\"textpad\"\n\"146\",\"morelikethis\"\n\"146\",\"prestashop-modules\"\n\"146\",\"qtranslate\"\n\"146\",\"embeddable\"\n\"146\",\"msal\"\n\"146\",\"comtypes\"\n\"146\",\"helios\"\n\"146\",\"query-planner\"\n\"146\",\"condor\"\n\"146\",\"linq-to-excel\"\n\"146\",\"cyclic-reference\"\n\"145\",\"clarity\"\n\"145\",\"pgrouting\"\n\"145\",\"sktexture\"\n\"145\",\"citus\"\n\"145\",\"matlab-struct\"\n\"145\",\"relaymodern\"\n\"145\",\"background-audio\"\n\"145\",\"tsvector\"\n\"145\",\"bacpac\"\n\"145\",\"edismax\"\n\"145\",\"telerik-open-access\"\n\"145\",\"language-detection\"\n\"145\",\"mariasql\"\n\"145\",\"picocli\"\n\"145\",\"front-controller\"\n\"145\",\"json-extract\"\n\"145\",\"ngx-admin\"\n\"145\",\"datagridviewcheckboxcell\"\n\"145\",\"servlet-listeners\"\n\"145\",\"createuser\"\n\"145\",\"failovercluster\"\n\"145\",\"angularjs-validation\"\n\"145\",\"google-url-shortener\"\n\"145\",\"hyperledger-fabric-sdk-js\"\n\"145\",\"public-html\"\n\"145\",\"cakephp-3.2\"\n\"145\",\"molecule\"\n\"145\",\"jquery-ui-selectmenu\"\n\"145\",\"hosted\"\n\"145\",\"pdcurses\"\n\"145\",\"jqxwidgets\"\n\"145\",\"tween.js\"\n\"145\",\"nssm\"\n\"145\",\"nreco\"\n\"145\",\"android-splashscreen\"\n\"145\",\"block-cipher\"\n\"145\",\"block-device\"\n\"145\",\"dynamic-data-display\"\n\"145\",\"scaladoc\"\n\"145\",\"4d\"\n\"145\",\"javafx-css\"\n\"145\",\"libreoffice-writer\"\n\"145\",\"3ds\"\n\"145\",\"bslib\"\n\"145\",\"pyspark-pandas\"\n\"145\",\"wiql\"\n\"145\",\"virtualmin\"\n\"145\",\"framerjs\"\n\"145\",\"raw-types\"\n\"145\",\"machinekey\"\n\"145\",\"draw2d\"\n\"145\",\"convex\"\n\"145\",\"control-flow-graph\"\n\"145\",\"screen-brightness\"\n\"145\",\"nios\"\n\"145\",\"cffi\"\n\"145\",\"elasticsearch-dsl-py\"\n\"145\",\"property-based-testing\"\n\"145\",\"human-readable\"\n\"145\",\"proto3\"\n\"145\",\"angularjs-compile\"\n\"145\",\"layout-gravity\"\n\"145\",\"text-styling\"\n\"145\",\"longtext\"\n\"145\",\"log-rotation\"\n\"145\",\"computercraft\"\n\"145\",\"daml\"\n\"145\",\"struts-validation\"\n\"145\",\"sorcery\"\n\"145\",\"spamassassin\"\n\"145\",\"theano-cuda\"\n\"145\",\"web-notifications\"\n\"145\",\"flutter-text\"\n\"144\",\"fig\"\n\"144\",\"localbroadcastmanager\"\n\"144\",\"wdm\"\n\"144\",\"ed25519\"\n\"144\",\"multiple-versions\"\n\"144\",\"web-traffic\"\n\"144\",\"balanced-payments\"\n\"144\",\"clipboard.js\"\n\"144\",\"react-native-native-module\"\n\"144\",\"triangle\"\n\"144\",\"match-against\"\n\"144\",\"cloudfoundry-uaa\"\n\"144\",\"mattermost\"\n\"144\",\"listview-adapter\"\n\"144\",\"deferred-rendering\"\n\"144\",\"tensorflowjs-converter\"\n\"144\",\"weighted-graph\"\n\"144\",\"reportserver\"\n\"144\",\"imperative-programming\"\n\"144\",\"contentplaceholder\"\n\"144\",\"app-bundle\"\n\"144\",\"kubernetes-jobs\"\n\"144\",\"xml2js\"\n\"144\",\"uncrustify\"\n\"144\",\"vscodevim\"\n\"144\",\"service-worker-events\"\n\"144\",\"fact-table\"\n\"144\",\"vcs-checkout\"\n\"144\",\"django-imagekit\"\n\"144\",\"doctrine-migrations\"\n\"144\",\"dnf\"\n\"144\",\"simple-salesforce\"\n\"144\",\"mgwt\"\n\"144\",\"unknown-host\"\n\"144\",\"unity3d-gui\"\n\"144\",\"jquery-knob\"\n\"144\",\"jri\"\n\"144\",\"hp-alm\"\n\"144\",\"sikuli-script\"\n\"144\",\"ionic-v1\"\n\"144\",\"type-bounds\"\n\"144\",\"fpic\"\n\"144\",\"rc4-cipher\"\n\"144\",\"bugsnag\"\n\"144\",\"fortran2003\"\n\"144\",\"overlapped-io\"\n\"144\",\"codespaces\"\n\"144\",\"showmodaldialog\"\n\"144\",\"tablename\"\n\"144\",\"tde\"\n\"144\",\"gun\"\n\"144\",\"tabula-py\"\n\"144\",\"busyindicator\"\n\"144\",\"tippyjs\"\n\"144\",\"azure-marketplace\"\n\"144\",\"diazo\"\n\"144\",\"azure-functions-isolated\"\n\"144\",\"mobilefirst-cli\"\n\"144\",\"nvl\"\n\"144\",\"hash-of-hashes\"\n\"144\",\"opensea\"\n\"144\",\"angular1.6\"\n\"144\",\"splunk-dashboard\"\n\"144\",\"etsy\"\n\"144\",\"spawning\"\n\"144\",\"angularjs-injector\"\n\"144\",\"colorama\"\n\"144\",\"mpvolumeview\"\n\"144\",\"requestjs\"\n\"144\",\"event-stream\"\n\"144\",\"exceldatareader\"\n\"144\",\"ppl\"\n\"144\",\"medium-trust\"\n\"144\",\"zend-acl\"\n\"144\",\"msvc12\"\n\"144\",\"google-oauth-java-client\"\n\"144\",\"linux-from-scratch\"\n\"144\",\"headphones\"\n\"144\",\"parsley\"\n\"144\",\"helmet.js\"\n\"144\",\"mdx-query\"\n\"144\",\"qt5.6\"\n\"144\",\"stdmove\"\n\"144\",\"prettify\"\n\"144\",\"mtm\"\n\"143\",\"civicrm\"\n\"143\",\"processmaker\"\n\"143\",\"loaded\"\n\"143\",\"interface-implementation\"\n\"143\",\"webview-flutter\"\n\"143\",\"translate3d\"\n\"143\",\"listboxitems\"\n\"143\",\"treeviewer\"\n\"143\",\"ssh2\"\n\"143\",\"clarifai\"\n\"143\",\"jaybird\"\n\"143\",\"gforth\"\n\"143\",\"jaydebeapi\"\n\"143\",\"xtermjs\"\n\"143\",\"django-contenttypes\"\n\"143\",\"admob-rewardedvideoad\"\n\"143\",\"pkgbuild\"\n\"143\",\"datatemplateselector\"\n\"143\",\"packet-loss\"\n\"143\",\"dirname\"\n\"143\",\"jose\"\n\"143\",\"readelf\"\n\"143\",\"docusign-sdk\"\n\"143\",\"angular-storybook\"\n\"143\",\"varbinarymax\"\n\"143\",\"kibana-6\"\n\"143\",\"workbox-webpack-plugin\"\n\"143\",\"gulp-browser-sync\"\n\"143\",\"spring-integration-http\"\n\"143\",\"azure-ai\"\n\"143\",\"blueprintjs\"\n\"143\",\"wow.js\"\n\"143\",\"axiom\"\n\"143\",\"bootstrap-daterangepicker\"\n\"143\",\"gtable\"\n\"143\",\"jquery-ui-multiselect\"\n\"143\",\"mogrify\"\n\"143\",\"shopware6-app\"\n\"143\",\"wiremock-standalone\"\n\"143\",\"authenticator\"\n\"143\",\"mailing-list\"\n\"143\",\"missingmethodexception\"\n\"143\",\"android-beam\"\n\"143\",\"formhelper\"\n\"143\",\"attachedbehaviors\"\n\"143\",\"sf-symbols\"\n\"143\",\"rfc5545\"\n\"143\",\"pysqlite\"\n\"143\",\"facebook-friends\"\n\"143\",\"amazon-transcribe\"\n\"143\",\"setsockopt\"\n\"143\",\"double-checked-locking\"\n\"143\",\"hibernate-6.x\"\n\"143\",\"opensso\"\n\"143\",\"norm\"\n\"143\",\"drupal-hooks\"\n\"143\",\"expected-condition\"\n\"143\",\"nstextattachment\"\n\"143\",\"tinker\"\n\"143\",\"collection-select\"\n\"143\",\"android-safe-args\"\n\"143\",\"eucalyptus\"\n\"143\",\"propensity-score-matching\"\n\"143\",\"prototyping\"\n\"143\",\"http-status-code-413\"\n\"143\",\"laravel-horizon\"\n\"143\",\"rdtsc\"\n\"143\",\"tomee-7\"\n\"143\",\"mediatemple\"\n\"143\",\"webpack-loader\"\n\"143\",\"end-of-line\"\n\"143\",\"mclapply\"\n\"143\",\"static-constructor\"\n\"143\",\"automatic-updates\"\n\"143\",\"traffic-simulation\"\n\"143\",\"transfer-encoding\"\n\"143\",\"presenter\"\n\"142\",\"standardized\"\n\"142\",\"xmi\"\n\"142\",\"adobecreativesdk\"\n\"142\",\"causality\"\n\"142\",\"cdap\"\n\"142\",\"index-match\"\n\"142\",\"pkix\"\n\"142\",\"xinput\"\n\"142\",\"python-daemon\"\n\"142\",\"language-implementation\"\n\"142\",\"choregraphe\"\n\"142\",\"angular-resolver\"\n\"142\",\"recurring-events\"\n\"142\",\"rooted-device\"\n\"142\",\"updateprogress\"\n\"142\",\"akka-typed\"\n\"142\",\"django-guardian\"\n\"142\",\"url.action\"\n\"142\",\"faiss\"\n\"142\",\"record-linkage\"\n\"142\",\"android-websettings\"\n\"142\",\"azure-data-sync\"\n\"142\",\"hprof\"\n\"142\",\"onfling\"\n\"142\",\"neoscms\"\n\"142\",\"modularization\"\n\"142\",\"bolt\"\n\"142\",\"pc-lint\"\n\"142\",\"htmlelements\"\n\"142\",\"scalable\"\n\"142\",\"jwplayer7\"\n\"142\",\"typeinfo\"\n\"142\",\"orientdb-2.1\"\n\"142\",\"miui\"\n\"142\",\"android-applicationinfo\"\n\"142\",\"oauth2-playground\"\n\"142\",\"extra\"\n\"142\",\"setbackground\"\n\"142\",\"java.time.instant\"\n\"142\",\"nette\"\n\"142\",\"signalr-backplane\"\n\"142\",\"libjingle\"\n\"142\",\"amplitude\"\n\"142\",\"coin-flipping\"\n\"142\",\"system-testing\"\n\"142\",\"uipicker\"\n\"142\",\"nsurlprotocol\"\n\"142\",\"xcode7.2\"\n\"142\",\"android-inputtype\"\n\"142\",\"nokia-s40\"\n\"142\",\"take\"\n\"142\",\"schema-design\"\n\"142\",\"re-frame\"\n\"142\",\"dragonfly-gem\"\n\"142\",\"ondemand\"\n\"142\",\"monkey\"\n\"142\",\"nlopt\"\n\"142\",\"google-eclipse-plugin\"\n\"142\",\"mptt\"\n\"142\",\"activescaffold\"\n\"142\",\"cereal\"\n\"142\",\"http-options-method\"\n\"142\",\"powermail\"\n\"142\",\"vaadin10\"\n\"142\",\"cypress-intercept\"\n\"142\",\"gitops\"\n\"142\",\"tfs-2010\"\n\"142\",\"glad\"\n\"142\",\"preserve\"\n\"142\",\"hci\"\n\"142\",\"google-reporting-api\"\n\"142\",\"papyrus\"\n\"141\",\"tempdb\"\n\"141\",\"react-native-flexbox\"\n\"141\",\"eclipse-marketplace\"\n\"141\",\"repast-simphony\"\n\"141\",\"sku\"\n\"141\",\"file-move\"\n\"141\",\"jcalendar\"\n\"141\",\"gettime\"\n\"141\",\"yargs\"\n\"141\",\"backand\"\n\"141\",\"multipartfile\"\n\"141\",\"biztalk-orchestrations\"\n\"141\",\"add-filter\"\n\"141\",\"import-module\"\n\"141\",\"swtbot\"\n\"141\",\"app-actions\"\n\"141\",\"rvo\"\n\"141\",\"rvm-capistrano\"\n\"141\",\"make-shared\"\n\"141\",\"camera2\"\n\"141\",\"kerning\"\n\"141\",\"fastboot\"\n\"141\",\"oracle-manageddataaccess\"\n\"141\",\"docker-secrets\"\n\"141\",\"verbosity\"\n\"141\",\"windows-8.1-universal\"\n\"141\",\"ruby-1.8\"\n\"141\",\"value-of\"\n\"141\",\"akka-testkit\"\n\"141\",\"deriving\"\n\"141\",\"dynamics-gp\"\n\"141\",\"spring-kafka-test\"\n\"141\",\"mongodb-atlas-search\"\n\"141\",\"shopware6-api\"\n\"141\",\"rethinkdb-python\"\n\"141\",\"google-cdn\"\n\"141\",\"winmerge\"\n\"141\",\"kobold2d\"\n\"141\",\"less-unix\"\n\"141\",\"facebook-analytics\"\n\"141\",\"nsurlsessionconfiguration\"\n\"141\",\"sqlfiddle\"\n\"141\",\"cordova-cli\"\n\"141\",\"ivar\"\n\"141\",\"nom\"\n\"141\",\"uitableviewautomaticdimension\"\n\"141\",\"response.write\"\n\"141\",\"laravel-jobs\"\n\"141\",\"node-pdfkit\"\n\"141\",\"eksctl\"\n\"141\",\"currentculture\"\n\"141\",\"certificate-revocation\"\n\"141\",\"geokit\"\n\"141\",\"react-day-picker\"\n\"141\",\"dual-sim\"\n\"141\",\"timage\"\n\"141\",\"archunit\"\n\"141\",\"hdpi\"\n\"141\",\"qtcpserver\"\n\"141\",\"spark-notebook\"\n\"141\",\"struts2-interceptors\"\n\"141\",\"conference\"\n\"141\",\"powerpoint-2010\"\n\"140\",\"livebindings\"\n\"140\",\"photogrammetry\"\n\"140\",\"eclipse-memory-analyzer\"\n\"140\",\"github-codespaces\"\n\"140\",\"floating-point-exceptions\"\n\"140\",\"gitlab-pages\"\n\"140\",\"fixed-header-tables\"\n\"140\",\"bitlocker\"\n\"140\",\"adjustment\"\n\"140\",\"flashvars\"\n\"140\",\"firewalld\"\n\"140\",\"redash\"\n\"140\",\"jison\"\n\"140\",\"google-web-designer\"\n\"140\",\"jquery-easing\"\n\"140\",\"eager-execution\"\n\"140\",\"azure-devops-server\"\n\"140\",\"borderless\"\n\"140\",\"nsfilehandle\"\n\"140\",\"jquery-data\"\n\"140\",\"rayon\"\n\"140\",\"coderush\"\n\"140\",\"obs\"\n\"140\",\"fossil\"\n\"140\",\"google-chrome-console\"\n\"140\",\"ubuntu-15.04\"\n\"140\",\"formvalidation-plugin\"\n\"140\",\"video-compression\"\n\"140\",\"coldbox\"\n\"140\",\"scim2\"\n\"140\",\"control-structure\"\n\"140\",\"directinput\"\n\"140\",\"plotrix\"\n\"140\",\"asplinkbutton\"\n\"140\",\"driving-directions\"\n\"140\",\"culling\"\n\"140\",\"petrel\"\n\"140\",\"oledbexception\"\n\"140\",\"merge-module\"\n\"140\",\"law-of-demeter\"\n\"140\",\"octopus\"\n\"140\",\"python-vlc\"\n\"140\",\"node-mysql2\"\n\"140\",\"prettier-eslint\"\n\"140\",\"structural-typing\"\n\"140\",\"scrypt\"\n\"140\",\"zerobrane\"\n\"140\",\"papervision3d\"\n\"140\",\"linqdatasource\"\n\"140\",\"qtestlib\"\n\"140\",\"pprof\"\n\"140\",\"arrow-kt\"\n\"139\",\"deduplication\"\n\"139\",\"ecb\"\n\"139\",\"webshop\"\n\"139\",\"inspect-element\"\n\"139\",\"private-subnet\"\n\"139\",\"maven-cargo\"\n\"139\",\"feature-branch\"\n\"139\",\"multiple-forms\"\n\"139\",\"pgx\"\n\"139\",\"templatebinding\"\n\"139\",\"grequests\"\n\"139\",\"laravel-fortify\"\n\"139\",\"discretization\"\n\"139\",\"catextlayer\"\n\"139\",\"vp8\"\n\"139\",\"adoptopenjdk\"\n\"139\",\"final-form\"\n\"139\",\"data-quality\"\n\"139\",\"pacman-package-manager\"\n\"139\",\"bitstring\"\n\"139\",\"python-sip\"\n\"139\",\"soapfault\"\n\"139\",\"bitconverter\"\n\"139\",\"method-invocation\"\n\"139\",\"gradienttape\"\n\"139\",\"vb.net-to-c#\"\n\"139\",\"ibm-rational\"\n\"139\",\"fatjar\"\n\"139\",\"jquery-datatables-editor\"\n\"139\",\"appserver\"\n\"139\",\"passwordbox\"\n\"139\",\"nautilus\"\n\"139\",\"kdiff3\"\n\"139\",\"pcap.net\"\n\"139\",\"cratedb\"\n\"139\",\"openbravo\"\n\"139\",\"postorder\"\n\"139\",\"into-outfile\"\n\"139\",\"designated-initializer\"\n\"139\",\"pastebin\"\n\"139\",\"nxt\"\n\"139\",\"buffer-geometry\"\n\"139\",\"raiserror\"\n\"139\",\"shrine\"\n\"139\",\"orca\"\n\"139\",\"code-golf\"\n\"139\",\"vlsi\"\n\"139\",\"amortized-analysis\"\n\"139\",\"sgml\"\n\"139\",\"amr\"\n\"139\",\"modal-window\"\n\"139\",\"tizen-emulator\"\n\"139\",\"tizen-studio\"\n\"139\",\"hiredis\"\n\"139\",\"j2objc\"\n\"139\",\"spy++\"\n\"139\",\"tdb\"\n\"139\",\"janrain\"\n\"139\",\"xbuild\"\n\"139\",\"hadoop-plugins\"\n\"139\",\"redcarpet\"\n\"139\",\"assembly-references\"\n\"139\",\"harmonyos\"\n\"139\",\"activity-stack\"\n\"139\",\"gethostbyname\"\n\"139\",\"om\"\n\"139\",\"textmate2\"\n\"139\",\"odroid\"\n\"139\",\"tessellation\"\n\"139\",\"android-remoteview\"\n\"139\",\"elasticsearch-x-pack\"\n\"139\",\"zul\"\n\"139\",\"elmah.mvc\"\n\"139\",\"zend-pdf\"\n\"139\",\"this-pointer\"\n\"139\",\"maximize-window\"\n\"139\",\"custom-widgets\"\n\"139\",\"zend-session\"\n\"139\",\"linearmodels\"\n\"139\",\"haskell-pipes\"\n\"139\",\"qtextbrowser\"\n\"139\",\"pprint\"\n\"138\",\"jdl\"\n\"138\",\"skview\"\n\"138\",\"clickjacking\"\n\"138\",\"mautic\"\n\"138\",\"fedora-25\"\n\"138\",\"triples\"\n\"138\",\"flutter-container\"\n\"138\",\"smartsheet-api-2.0\"\n\"138\",\"disk-io\"\n\"138\",\"kube-proxy\"\n\"138\",\"segments\"\n\"138\",\"bitmapsource\"\n\"138\",\"contentoffset\"\n\"138\",\"crossbar\"\n\"138\",\"rome\"\n\"138\",\"kif\"\n\"138\",\"jlayer\"\n\"138\",\"rowdatabound\"\n\"138\",\"angularjs-ng-form\"\n\"138\",\"ag-grid-vue\"\n\"138\",\"gpt-4\"\n\"138\",\"server-administration\"\n\"138\",\"intrusion-detection\"\n\"138\",\"tt-news\"\n\"138\",\"wwdc\"\n\"138\",\"pdfrenderer\"\n\"138\",\"dvd\"\n\"138\",\"enunciate\"\n\"138\",\"kaniko\"\n\"138\",\"brace-expansion\"\n\"138\",\"scala-compiler\"\n\"138\",\"module.exports\"\n\"138\",\"aqueduct\"\n\"138\",\"blazorise\"\n\"138\",\"paypal-buttons\"\n\"138\",\"apple-appclips\"\n\"138\",\"breakout\"\n\"138\",\"s-expression\"\n\"138\",\"systemtap\"\n\"138\",\".net-4.5.2\"\n\"138\",\"xamarin.auth\"\n\"138\",\"hogan.js\"\n\"138\",\"android-filterable\"\n\"138\",\"mnemonics\"\n\"138\",\"cadisplaylink\"\n\"138\",\"azure-http-trigger\"\n\"138\",\"executemany\"\n\"138\",\"spss-modeler\"\n\"138\",\"generic-constraints\"\n\"138\",\"react-native-calendars\"\n\"138\",\"screeps\"\n\"138\",\"google-iap\"\n\"138\",\"tfs-process-template\"\n\"138\",\"touchmove\"\n\"138\",\"spark-csv\"\n\"138\",\"linq-group\"\n\"138\",\"powershell-1.0\"\n\"138\",\"pox\"\n\"138\",\"search-form\"\n\"138\",\"url-launcher\"\n\"138\",\"lighttable\"\n\"137\",\"flexible-array-member\"\n\"137\",\"principalcontext\"\n\"137\",\"cmake-modules\"\n\"137\",\"discovery\"\n\"137\",\"adsense-api\"\n\"137\",\"image-masking\"\n\"137\",\"vscode-python\"\n\"137\",\"vnc-viewer\"\n\"137\",\"sendbird\"\n\"137\",\"xmlworker\"\n\"137\",\"adobe-xd\"\n\"137\",\"gorouter\"\n\"137\",\"aws-acm\"\n\"137\",\"singular\"\n\"137\",\"rjson\"\n\"137\",\"upperbound\"\n\"137\",\"micrometer-tracing\"\n\"137\",\"rubiks-cube\"\n\"137\",\"bonita\"\n\"137\",\"cortana-intelligence\"\n\"137\",\"pchart\"\n\"137\",\"swfloader\"\n\"137\",\"wse\"\n\"137\",\"samsung-galaxy-gear\"\n\"137\",\"dynamicresource\"\n\"137\",\"html-templates\"\n\"137\",\"ncover\"\n\"137\",\"tumblr-themes\"\n\"137\",\"formwizard\"\n\"137\",\"rets\"\n\"137\",\"networkmanager\"\n\"137\",\"javacompiler\"\n\"137\",\"otool\"\n\"137\",\"sicstus-prolog\"\n\"137\",\"java-client\"\n\"137\",\"sha2\"\n\"137\",\"uiactivity\"\n\"137\",\"scom\"\n\"137\",\"assembla\"\n\"137\",\"android-bottomappbar\"\n\"137\",\"mobile-ad-mediation\"\n\"137\",\"screen-lock\"\n\"137\",\"drf-spectacular\"\n\"137\",\"xamarin.forms.listview\"\n\"137\",\"openkinect\"\n\"137\",\"comm\"\n\"137\",\"accessoryview\"\n\"137\",\"terser\"\n\"137\",\"android-native-library\"\n\"137\",\"event-receiver\"\n\"137\",\"log4cplus\"\n\"137\",\"elastalert\"\n\"137\",\"pellet\"\n\"137\",\"promotions\"\n\"137\",\"required-field\"\n\"137\",\"bean-io\"\n\"137\",\"flysystem\"\n\"137\",\"startmenu\"\n\"137\",\"urp\"\n\"137\",\"statechart\"\n\"137\",\"globalize\"\n\"137\",\"webchromeclient\"\n\"136\",\"relative-url\"\n\"136\",\"flops\"\n\"136\",\"matlab-table\"\n\"136\",\"deepzoom\"\n\"136\",\"remote-notifications\"\n\"136\",\"imagemapster\"\n\"136\",\"apiary\"\n\"136\",\"cell-formatting\"\n\"136\",\"vscode-keybinding\"\n\"136\",\"daydream\"\n\"136\",\"immediate-window\"\n\"136\",\"python-django-storages\"\n\"136\",\"python-db-api\"\n\"136\",\"mysqlbinlog\"\n\"136\",\"robotframework-ide\"\n\"136\",\"sharepoint-jsom\"\n\"136\",\"cakephp-2.6\"\n\"136\",\"gosu\"\n\"136\",\"angular-ssr\"\n\"136\",\"rubyzip\"\n\"136\",\"canopen\"\n\"136\",\"cakephp-3.4\"\n\"136\",\"dependency-inversion\"\n\"136\",\"nrpe\"\n\"136\",\"android-strictmode\"\n\"136\",\"post-commit-hook\"\n\"136\",\"wpa\"\n\"136\",\"opendata\"\n\"136\",\"paxos\"\n\"136\",\"k2\"\n\"136\",\"counting-sort\"\n\"136\",\"epub3\"\n\"136\",\"superview\"\n\"136\",\"svn2git\"\n\"136\",\"gstat\"\n\"136\",\"samsung-mobile-sdk\"\n\"136\",\"spring-graphql\"\n\"136\",\"set-returning-functions\"\n\"136\",\"facebook-instant-articles\"\n\"136\",\"outlook-2003\"\n\"136\",\"koa-router\"\n\"136\",\"observablehq\"\n\"136\",\"osgeo\"\n\"136\",\"tasker\"\n\"136\",\"azure-log-analytics-workspace\"\n\"136\",\"rack-pow\"\n\"136\",\"excel-match\"\n\"136\",\"devops-services\"\n\"136\",\"asp.net-charts\"\n\"136\",\"hibernate3\"\n\"136\",\"radial\"\n\"136\",\"gcc4.7\"\n\"136\",\"android-build-flavors\"\n\"136\",\"spdlog\"\n\"136\",\"ios-pdfkit\"\n\"136\",\"qplaintextedit\"\n\"136\",\"actionmethod\"\n\"136\",\"getelementsbyname\"\n\"136\",\"combinelatest\"\n\"136\",\"etherpad\"\n\"136\",\"react-loadable\"\n\"136\",\"getresponse\"\n\"136\",\"sdkman\"\n\"136\",\"quartz-composer\"\n\"136\",\"hdfstore\"\n\"136\",\"structured-text\"\n\"136\",\"lightswitch-2012\"\n\"136\",\"msxml6\"\n\"135\",\"apache-cayenne\"\n\"135\",\"mat-datepicker\"\n\"135\",\"report-designer\"\n\"135\",\"jdesktoppane\"\n\"135\",\"cloudbuild.yaml\"\n\"135\",\"intel-syntax\"\n\"135\",\"yew\"\n\"135\",\"graphql-ruby\"\n\"135\",\"multi-table\"\n\"135\",\"fdf\"\n\"135\",\"yaml-front-matter\"\n\"135\",\"webresponse\"\n\"135\",\"symfony-messenger\"\n\"135\",\"js-xlsx\"\n\"135\",\"maintenance-mode\"\n\"135\",\"chefspec\"\n\"135\",\"data-url\"\n\"135\",\"content-management\"\n\"135\",\"nextgen-gallery\"\n\"135\",\"uncertainty\"\n\"135\",\"incoming-call\"\n\"135\",\"fts3\"\n\"135\",\"selenoid\"\n\"135\",\"s4sdk\"\n\"135\",\"data-layers\"\n\"135\",\"s60\"\n\"135\",\"jgraph\"\n\"135\",\"facebook-sdk-3.1\"\n\"135\",\"react-window\"\n\"135\",\"fastmm\"\n\"135\",\"cross-language\"\n\"135\",\"hyperopt\"\n\"135\",\"grails-2.2\"\n\"135\",\"nand2tetris\"\n\"135\",\"django-viewflow\"\n\"135\",\"roxygen\"\n\"135\",\"sharepoint-search\"\n\"135\",\"fast-enumeration\"\n\"135\",\"angular-schema-form\"\n\"135\",\"opos\"\n\"135\",\"inmobi\"\n\"135\",\"android-studio-4.0\"\n\"135\",\"mongoosastic\"\n\"135\",\"pax-exam\"\n\"135\",\"passwd\"\n\"135\",\"coreldraw\"\n\"135\",\"dyno\"\n\"135\",\"sanitizer\"\n\"135\",\"wix-extension\"\n\"135\",\"raven\"\n\"135\",\"java-12\"\n\"135\",\"libtiff.net\"\n\"135\",\"vispy\"\n\"135\",\"buildr\"\n\"135\",\"audio-service\"\n\"135\",\"windows-xp-sp3\"\n\"135\",\"itemsource\"\n\"135\",\"gwt-celltable\"\n\"135\",\"expo-notifications\"\n\"135\",\"xcode9.3\"\n\"135\",\"scorm1.2\"\n\"135\",\"uideviceorientation\"\n\"135\",\"screen-density\"\n\"135\",\"uiedgeinsets\"\n\"135\",\"sqlite3-python\"\n\"135\",\"opennetcf\"\n\"135\",\"property-wrapper\"\n\"135\",\"cube.js\"\n\"135\",\"iphone-softkeyboard\"\n\"135\",\"activity-manager\"\n\"135\",\"long-long\"\n\"135\",\"lastinsertid\"\n\"135\",\"laravel-vapor\"\n\"135\",\"google-cloud-dlp\"\n\"135\",\"commit-message\"\n\"135\",\"spp\"\n\"135\",\"webgpu\"\n\"135\",\"timeago\"\n\"135\",\"mbr\"\n\"135\",\"ends-with\"\n\"135\",\"iframe-resizer\"\n\"135\",\"state-restoration\"\n\"135\",\"spark-shell\"\n\"135\",\"ember-model\"\n\"134\",\"stack-unwinding\"\n\"134\",\"vue-class-components\"\n\"134\",\"terminator\"\n\"134\",\"deeplab\"\n\"134\",\"treelist\"\n\"134\",\"graph-coloring\"\n\"134\",\"yepnope\"\n\"134\",\"imultivalueconverter\"\n\"134\",\"symfony-validator\"\n\"134\",\"fishpig\"\n\"134\",\"first-class-functions\"\n\"134\",\"self-organizing-maps\"\n\"134\",\"const-reference\"\n\"134\",\"manualresetevent\"\n\"134\",\"snmpd\"\n\"134\",\"metric\"\n\"134\",\"angularjs-select2\"\n\"134\",\"native-activity\"\n\"134\",\"jpcap\"\n\"134\",\"sequence-alignment\"\n\"134\",\"valuechangelistener\"\n\"134\",\"icollectionview\"\n\"134\",\"angularjs-ng-init\"\n\"134\",\"sitecore-dms\"\n\"134\",\"rocket-chip\"\n\"134\",\"mod-expires\"\n\"134\",\"tweetinvi\"\n\"134\",\"environmentobject\"\n\"134\",\"crate\"\n\"134\",\"ingress-nginx\"\n\"134\",\"postmark\"\n\"134\",\"lync-client-sdk\"\n\"134\",\"type-narrowing\"\n\"134\",\"rajawali\"\n\"134\",\"browser-addons\"\n\"134\",\"go-http\"\n\"134\",\"short-url\"\n\"134\",\"accelerated-mobile-page\"\n\"134\",\"siamese-network\"\n\"134\",\"overfitting-underfitting\"\n\"134\",\"rgraph\"\n\"134\",\"xades4j\"\n\"134\",\"isbn\"\n\"134\",\"gammu\"\n\"134\",\"table-per-hierarchy\"\n\"134\",\"halcon\"\n\"134\",\"spring-vault\"\n\"134\",\"performancepoint\"\n\"134\",\"sparse-checkout\"\n\"134\",\"member-initialization\"\n\"134\",\"element-plus\"\n\"134\",\"motordriver\"\n\"134\",\"elastic-cloud\"\n\"134\",\"angular-guards\"\n\"134\",\"nidaqmx\"\n\"134\",\"strava\"\n\"134\",\"cxf-client\"\n\"134\",\"amazon-advertising-api\"\n\"134\",\"ember-testing\"\n\"134\",\"preemption\"\n\"134\",\"urwid\"\n\"134\",\"liferay-velocity\"\n\"134\",\"git-p4\"\n\"134\",\"compound-literals\"\n\"134\",\"computation-expression\"\n\"134\",\"parallel-port\"\n\"133\",\"webproxy\"\n\"133\",\"phpexcelreader\"\n\"133\",\"bandwidth-throttling\"\n\"133\",\"django-1.3\"\n\"133\",\"unetstack\"\n\"133\",\"js-cookie\"\n\"133\",\"castle-monorail\"\n\"133\",\"uniform-initialization\"\n\"133\",\"semantic-analysis\"\n\"133\",\"platform-independent\"\n\"133\",\"python-jsonschema\"\n\"133\",\"self-referencing-table\"\n\"133\",\"wikimedia\"\n\"133\",\"airflow-taskflow\"\n\"133\",\"django-syncdb\"\n\"133\",\"angular-slickgrid\"\n\"133\",\"valence\"\n\"133\",\"robustness\"\n\"133\",\"go-testing\"\n\"133\",\"w3c-geolocation\"\n\"133\",\"post-build\"\n\"133\",\"countries\"\n\"133\",\"htdocs\"\n\"133\",\"keen-io\"\n\"133\",\"keda\"\n\"133\",\"information-hiding\"\n\"133\",\"hotfix\"\n\"133\",\"navigateurl\"\n\"133\",\"oclazyload\"\n\"133\",\"winpe\"\n\"133\",\"domain-events\"\n\"133\",\"foxx\"\n\"133\",\"audio-fingerprinting\"\n\"133\",\"tableheader\"\n\"133\",\"rewardedvideoad\"\n\"133\",\"machine-learning-model\"\n\"133\",\"android-app-links\"\n\"133\",\"victoriametrics\"\n\"133\",\"executequery\"\n\"133\",\"time-limiting\"\n\"133\",\"tin-can-api\"\n\"133\",\"asm.js\"\n\"133\",\"game-maker-language\"\n\"133\",\"openstack-horizon\"\n\"133\",\"taglib-sharp\"\n\"133\",\"drf-queryset\"\n\"133\",\"hangouts-api\"\n\"133\",\"highest\"\n\"133\",\"hmmlearn\"\n\"133\",\"tailwind-in-js\"\n\"133\",\"asp.net-web-api-odata\"\n\"133\",\"logback-classic\"\n\"133\",\"oim\"\n\"133\",\"message-digest\"\n\"133\",\"geronimo\"\n\"133\",\"stringtemplate-4\"\n\"133\",\"character-arrays\"\n\"133\",\"angularjs-forms\"\n\"133\",\"logical-or\"\n\"133\",\"custom-membershipprovider\"\n\"133\",\"sublimetext4\"\n\"133\",\"batterymanager\"\n\"133\",\"gitweb\"\n\"133\",\"dalli\"\n\"133\",\"computed-observable\"\n\"133\",\"google-news\"\n\"132\",\"insets\"\n\"132\",\"ghdl\"\n\"132\",\"insert-select\"\n\"132\",\"wfp\"\n\"132\",\"vue-tables-2\"\n\"132\",\"tsibble\"\n\"132\",\"file-recovery\"\n\"132\",\"xenforo\"\n\"132\",\"impex\"\n\"132\",\"symfony-console\"\n\"132\",\"adomd.net\"\n\"132\",\"ng2-dragula\"\n\"132\",\"jtoolbar\"\n\"132\",\"biztalk2006r2\"\n\"132\",\"kubernetes-custom-resources\"\n\"132\",\"manual-testing\"\n\"132\",\"sybase-ase15\"\n\"132\",\"apache-kudu\"\n\"132\",\"nextui\"\n\"132\",\"mapster\"\n\"132\",\"vaticle-typedb\"\n\"132\",\"docfx\"\n\"132\",\"django-modeladmin\"\n\"132\",\"createuserwizard\"\n\"132\",\"database-locking\"\n\"132\",\"aws-java-sdk-2.x\"\n\"132\",\"canvg\"\n\"132\",\"worldpay\"\n\"132\",\"jquery-terminal\"\n\"132\",\"boo\"\n\"132\",\"enterprise-integration\"\n\"132\",\"port80\"\n\"132\",\"bokehjs\"\n\"132\",\"descendant\"\n\"132\",\"botan\"\n\"132\",\"appsflyer\"\n\"132\",\"type-coercion\"\n\"132\",\"visual-studio-2017-build-tools\"\n\"132\",\"attoparsec\"\n\"132\",\"fql.multiquery\"\n\"132\",\"komodoedit\"\n\"132\",\"kotest\"\n\"132\",\"winusb\"\n\"132\",\"aabb\"\n\"132\",\"codahale-metrics\"\n\"132\",\"output-parameter\"\n\"132\",\"minishift\"\n\"132\",\"outlet\"\n\"132\",\"opera-mini\"\n\"132\",\"opensips\"\n\"132\",\"xacml3\"\n\"132\",\"nstreecontroller\"\n\"132\",\"column-count\"\n\"132\",\"cerberus\"\n\"132\",\"stripe.net\"\n\"132\",\"nms\"\n\"132\",\"google-cloud-api-gateway\"\n\"132\",\"string-pool\"\n\"132\",\"getparameter\"\n\"132\",\"angular2-modules\"\n\"132\",\"splitview\"\n\"132\",\"motorola-emdk\"\n\"132\",\"flutter-notification\"\n\"132\",\"sonarqube-5.0\"\n\"132\",\"component-scan\"\n\"132\",\"parse-android-sdk\"\n\"132\",\"webpack-plugin\"\n\"132\",\"sumologic\"\n\"132\",\"email-parsing\"\n\"132\",\"bcc-bpf\"\n\"131\",\"cmake-language\"\n\"131\",\"eclemma\"\n\"131\",\"maven-bundle-plugin\"\n\"131\",\"greenfoot\"\n\"131\",\"x-ua-compatible\"\n\"131\",\"ebextensions\"\n\"131\",\"process-substitution\"\n\"131\",\"trpc.io\"\n\"131\",\"file-encodings\"\n\"131\",\"xs\"\n\"131\",\"react-native-hermes\"\n\"131\",\"ffimageloading\"\n\"131\",\"pickadate\"\n\"131\",\"snappydata\"\n\"131\",\"imdbpy\"\n\"131\",\"symfony2-easyadmin\"\n\"131\",\"jsctypes\"\n\"131\",\"sensor-fusion\"\n\"131\",\"selectable\"\n\"131\",\"fixture\"\n\"131\",\"fadeto\"\n\"131\",\"akka-actor\"\n\"131\",\"jqmodal\"\n\"131\",\"kendo-react-ui\"\n\"131\",\"server.mappath\"\n\"131\",\"database-mirroring\"\n\"131\",\"gqlquery\"\n\"131\",\"microsoft-account\"\n\"131\",\"wan\"\n\"131\",\"roblox-studio\"\n\"131\",\"share-button\"\n\"131\",\"microfocus\"\n\"131\",\"twilio-conversations\"\n\"131\",\"intersystems\"\n\"131\",\"couchdb-nano\"\n\"131\",\"eager\"\n\"131\",\"intraweb\"\n\"131\",\"modx-evolution\"\n\"131\",\"inotifycollectionchanged\"\n\"131\",\"opencypher\"\n\"131\",\"booleanquery\"\n\"131\",\"atof\"\n\"131\",\"retrieval-augmented-generation\"\n\"131\",\"typekit\"\n\"131\",\"anchor-scroll\"\n\"131\",\"android-biometric\"\n\"131\",\"microsoft-identity-web\"\n\"131\",\"shinobi\"\n\"131\",\"aasm\"\n\"131\",\"winmain\"\n\"131\",\"vision-api\"\n\"131\",\"abpeoplepickerview\"\n\"131\",\"shacl\"\n\"131\",\"sql-server-2022\"\n\"131\",\"android-color\"\n\"131\",\"sprint\"\n\"131\",\"uint16\"\n\"131\",\"diagramming\"\n\"131\",\"npm-audit\"\n\"131\",\"acrobat-sdk\"\n\"131\",\"proxy-pattern\"\n\"131\",\"angular2-meteor\"\n\"131\",\"excel-automation\"\n\"131\",\"nightwatch\"\n\"131\",\"ioslides\"\n\"131\",\"oncheckedchanged\"\n\"131\",\"lti\"\n\"131\",\"splash-js-render\"\n\"131\",\"search-path\"\n\"131\",\"soundplayer\"\n\"131\",\"suite\"\n\"131\",\"linker-flags\"\n\"131\",\"compiler-directives\"\n\"131\",\"yt-dlp\"\n\"131\",\"zonejs\"\n\"131\",\"scrolltrigger\"\n\"131\",\"glide-golang\"\n\"131\",\"user-presence\"\n\"131\",\"tortoise-orm\"\n\"131\",\"ember-components\"\n\"131\",\"lift-json\"\n\"131\",\"sonarqube-msbuild-runner\"\n\"130\",\"weak\"\n\"130\",\"renderaction\"\n\"130\",\"ghidra\"\n\"130\",\"whitenoise\"\n\"130\",\"react-native-fetch-blob\"\n\"130\",\"ansible-galaxy\"\n\"130\",\"anypoint-platform\"\n\"130\",\"ggforce\"\n\"130\",\"seleniumbase\"\n\"130\",\"consistent-hashing\"\n\"130\",\"python-collections\"\n\"130\",\"fileobserver\"\n\"130\",\"datarepeater\"\n\"130\",\"phusion\"\n\"130\",\"binary-image\"\n\"130\",\"aws-sdk-cpp\"\n\"130\",\"jnetpcap\"\n\"130\",\"datagridcolumn\"\n\"130\",\"django-related-manager\"\n\"130\",\"ibinspectable\"\n\"130\",\"cropper\"\n\"130\",\"servicemesh\"\n\"130\",\"spring-cloud-vault-config\"\n\"130\",\"intl-tel-input\"\n\"130\",\"nscolor\"\n\"130\",\"porter-stemmer\"\n\"130\",\"pdfplumber\"\n\"130\",\"supercollider\"\n\"130\",\"corespotlight\"\n\"130\",\"neo4j-spatial\"\n\"130\",\"depth-testing\"\n\"130\",\"twitter-digits\"\n\"130\",\"s-function\"\n\"130\",\"magick.net\"\n\"130\",\"brms\"\n\"130\",\"libsass\"\n\"130\",\"dojox.mobile\"\n\"130\",\"object-object-mapping\"\n\"130\",\"virtocommerce\"\n\"130\",\"javascript-import\"\n\"130\",\"r-colnames\"\n\"130\",\"excel-online\"\n\"130\",\"tchromium\"\n\"130\",\"generalization\"\n\"130\",\"gwt-super-dev-mode\"\n\"130\",\"haddock\"\n\"130\",\"xaudio2\"\n\"130\",\"jackcess\"\n\"130\",\"collectionfs\"\n\"130\",\"getenv\"\n\"130\",\"elasticsearch-opendistro\"\n\"130\",\"accesscontrolexception\"\n\"130\",\"android-profiler\"\n\"130\",\"texture-atlas\"\n\"130\",\"tfs-migration\"\n\"130\",\"mayavi.mlab\"\n\"130\",\"stm32f0\"\n\"130\",\"staruml\"\n\"130\",\"spaceship-operator\"\n\"130\",\"md-autocomplete\"\n\"130\",\"automaton\"\n\"130\",\"heap-size\"\n\"130\",\"soot\"\n\"129\",\"templating-engine\"\n\"129\",\"teamcity-7.0\"\n\"129\",\"clearcase-remote-client\"\n\"129\",\"terminfo\"\n\"129\",\"declarative-authorization\"\n\"129\",\"matlab-uitable\"\n\"129\",\"basehttpserver\"\n\"129\",\"relativesource\"\n\"129\",\"cllocationcoordinate2d\"\n\"129\",\"cl.exe\"\n\"129\",\"apache-commons-cli\"\n\"129\",\"r-xlsx\"\n\"129\",\"jsbin\"\n\"129\",\"blame\"\n\"129\",\"blazegraph\"\n\"129\",\"datamatrix\"\n\"129\",\"image-optimization\"\n\"129\",\"selenium-java\"\n\"129\",\"ngx-quill\"\n\"129\",\"pixelformat\"\n\"129\",\"datastax-astra\"\n\"129\",\"sensenet\"\n\"129\",\"kubernetes-go-client\"\n\"129\",\"runloop\"\n\"129\",\"pad\"\n\"129\",\"mysql-backup\"\n\"129\",\"angularjs-watch\"\n\"129\",\"oracle-autonomous-db\"\n\"129\",\"fb-graph\"\n\"129\",\"recvfrom\"\n\"129\",\"internal-load-balancer\"\n\"129\",\"appxmanifest\"\n\"129\",\"pdfium\"\n\"129\",\"counter-cache\"\n\"129\",\"boringssl\"\n\"129\",\"postcss-loader\"\n\"129\",\"grunt-contrib-copy\"\n\"129\",\"designmode\"\n\"129\",\"springboard\"\n\"129\",\"nsrangeexception\"\n\"129\",\"gtts\"\n\"129\",\"formatdatetime\"\n\"129\",\"legacy-database\"\n\"129\",\"a2dp\"\n\"129\",\"uft14\"\n\"129\",\"overlays\"\n\"129\",\"pytest-cov\"\n\"129\",\"virtualscroll\"\n\"129\",\"typo3-8.7.x\"\n\"129\",\"pythagorean\"\n\"129\",\"devise-token-auth\"\n\"129\",\"android-bottomnavigationview\"\n\"129\",\"c#-ziparchive\"\n\"129\",\"hibernate-session\"\n\"129\",\"histogram2d\"\n\"129\",\"experimental-design\"\n\"129\",\"c3\"\n\"129\",\"noindex\"\n\"129\",\"istream-iterator\"\n\"129\",\"q#\"\n\"129\",\"node-opcua\"\n\"129\",\"perfect\"\n\"129\",\"prototype-chain\"\n\"129\",\"logical-replication\"\n\"129\",\"proj4js\"\n\"129\",\"loginview\"\n\"129\",\"mercurial-subrepos\"\n\"129\",\"commandparameter\"\n\"129\",\"hyperion\"\n\"129\",\"node-addon-api\"\n\"129\",\"log-analysis\"\n\"129\",\"autocloseable\"\n\"129\",\"auth-token\"\n\"129\",\"quick.db\"\n\"129\",\"altitude\"\n\"129\",\"threadgroup\"\n\"129\",\"zip4j\"\n\"129\",\"amazon-kinesis-analytics\"\n\"129\",\"static-pages\"\n\"129\",\"ms-jet-ace\"\n\"129\",\"trackpad\"\n\"129\",\"dart-ffi\"\n\"129\",\"shell32\"\n\"128\",\"installshield-2012\"\n\"128\",\"sslsocketfactory\"\n\"128\",\"smallrye-reactive-messaging\"\n\"128\",\"proftpd\"\n\"128\",\"edgar\"\n\"128\",\"squeel\"\n\"128\",\"in-subquery\"\n\"128\",\"stackedbarseries\"\n\"128\",\"intellitrace\"\n\"128\",\"grasshopper\"\n\"128\",\"editbox\"\n\"128\",\"choco\"\n\"128\",\"pythoninterpreter\"\n\"128\",\"pivot-chart\"\n\"128\",\"snow\"\n\"128\",\"ngui\"\n\"128\",\"g++4.8\"\n\"128\",\"file-ownership\"\n\"128\",\"fulltext-index\"\n\"128\",\"date-math\"\n\"128\",\"unidata\"\n\"128\",\"ftrace\"\n\"128\",\"ngonchanges\"\n\"128\",\"pacemaker\"\n\"128\",\"swiftui-foreach\"\n\"128\",\"filetable\"\n\"128\",\"jfrog-xray\"\n\"128\",\"mysql-error-1062\"\n\"128\",\"realpath\"\n\"128\",\"wcs\"\n\"128\",\"albumart\"\n\"128\",\"keyboard-navigation\"\n\"128\",\"readonly-attribute\"\n\"128\",\"calendarextender\"\n\"128\",\"aws-config\"\n\"128\",\"datagridcell\"\n\"128\",\"core-nfc\"\n\"128\",\"simple.data\"\n\"128\",\"createinstance\"\n\"128\",\"injectable\"\n\"128\",\"interruption\"\n\"128\",\"appsdk2\"\n\"128\",\"cp1252\"\n\"128\",\"asyncore\"\n\"128\",\"system-clock\"\n\"128\",\"shading\"\n\"128\",\"kqueue\"\n\"128\",\".lib\"\n\"128\",\"browsermob\"\n\"128\",\"audit.net\"\n\"128\",\"domcontentloaded\"\n\"128\",\"mikroc\"\n\"128\",\"ubuntu-12.10\"\n\"128\",\"nsstringencoding\"\n\"128\",\"tab-ordering\"\n\"128\",\"redis-server\"\n\"128\",\"caffeine\"\n\"128\",\"expo-camera\"\n\"128\",\"nomenclature\"\n\"128\",\"model-driven\"\n\"128\",\"play-slick\"\n\"128\",\"ej2-syncfusion\"\n\"128\",\"proof-of-correctness\"\n\"128\",\"acr122\"\n\"128\",\"iprincipal\"\n\"128\",\"gerrit-trigger\"\n\"128\",\"angular-decorator\"\n\"128\",\"perfect-numbers\"\n\"128\",\"iphone-sdk-3.2\"\n\"128\",\"mpu6050\"\n\"128\",\"android-jetpack-compose-lazy-column\"\n\"128\",\"colormatrix\"\n\"128\",\"automapper-3\"\n\"128\",\"static-functions\"\n\"128\",\"custom-tag\"\n\"128\",\"usermanager\"\n\"128\",\"starteam\"\n\"128\",\"topdown\"\n\"128\",\"webapi2\"\n\"128\",\"maven-scm\"\n\"128\",\"pressed\"\n\"128\",\"parallel-coordinates\"\n\"127\",\"multiple-languages\"\n\"127\",\"reorderlist\"\n\"127\",\"fernet\"\n\"127\",\"installutil\"\n\"127\",\"debug-mode\"\n\"127\",\"clipboardmanager\"\n\"127\",\"feature-file\"\n\"127\",\"ssh2-sftp\"\n\"127\",\"jedit\"\n\"127\",\"pixmap\"\n\"127\",\"child-nodes\"\n\"127\",\"language-specifications\"\n\"127\",\"cartography\"\n\"127\",\"vscode-remote-ssh\"\n\"127\",\"kik\"\n\"127\",\"readystate\"\n\"127\",\"simplex\"\n\"127\",\"pure-function\"\n\"127\",\"read-data\"\n\"127\",\"wake-on-lan\"\n\"127\",\"serverxmlhttp\"\n\"127\",\"unity3d-mirror\"\n\"127\",\"nd4j\"\n\"127\",\"satchmo\"\n\"127\",\"epic\"\n\"127\",\"mongo-c-driver\"\n\"127\",\"spring-reactive\"\n\"127\",\"saxparseexception\"\n\"127\",\"dynamic-compilation\"\n\"127\",\"bootstrap-tour\"\n\"127\",\"navigationservice\"\n\"127\",\"twitter-card\"\n\"127\",\"deltaspike\"\n\"127\",\"bonobo\"\n\"127\",\"pathgeometry\"\n\"127\",\"spring-aspects\"\n\"127\",\"boost-date-time\"\n\"127\",\"fputs\"\n\"127\",\"coldfusion-2018\"\n\"127\",\"rive\"\n\"127\",\"pylucene\"\n\"127\",\"kotlin-reflect\"\n\"127\",\"rdcomclient\"\n\"127\",\"kolmogorov-smirnov\"\n\"127\",\"knppaginator\"\n\"127\",\"java-record\"\n\"127\",\"oracle-text\"\n\"127\",\"tamil\"\n\"127\",\"holtwinters\"\n\"127\",\"dronekit-python\"\n\"127\",\"azure-tablequery\"\n\"127\",\"ui-sref\"\n\"127\",\"executereader\"\n\"127\",\"react-jsonschema-forms\"\n\"127\",\"textile\"\n\"127\",\"omnithreadlibrary\"\n\"127\",\"angularjs-1.5\"\n\"127\",\"pega\"\n\"127\",\"android-layoutparams\"\n\"127\",\"offline-browsing\"\n\"127\",\"meta-inf\"\n\"127\",\"accessory\"\n\"127\",\"webcenter\"\n\"127\",\"power-law\"\n\"127\",\"sparkling-water\"\n\"127\",\"sticky-session\"\n\"127\",\"therubyracer\"\n\"127\",\"heightforrowatindexpath\"\n\"127\",\"sharppcap\"\n\"127\",\"query-expressions\"\n\"127\",\"linphone-sdk\"\n\"127\",\"bgp\"\n\"127\",\"altova\"\n\"126\",\"white-labelling\"\n\"126\",\"lite-server\"\n\"126\",\"react-native-modal\"\n\"126\",\"lnk\"\n\"126\",\"rendertargetbitmap\"\n\"126\",\"websub\"\n\"126\",\"selectsinglenode\"\n\"126\",\"django-3.2\"\n\"126\",\"rust-analyzer\"\n\"126\",\"django-3.1\"\n\"126\",\"pinecone\"\n\"126\",\"datashader\"\n\"126\",\"bind9\"\n\"126\",\"appgallery\"\n\"126\",\"kendo-upload\"\n\"126\",\"pumping-lemma\"\n\"126\",\"data-consistency\"\n\"126\",\"cakephp-bake\"\n\"126\",\"graphaware\"\n\"126\",\"jolokia\"\n\"126\",\"failure-slice\"\n\"126\",\"mysqlnd\"\n\"126\",\"aws-documentdb-mongoapi\"\n\"126\",\"fatfs\"\n\"126\",\"callgrind\"\n\"126\",\"wazuh\"\n\"126\",\"silhouette\"\n\"126\",\"delayedvariableexpansion\"\n\"126\",\"aws-vpc\"\n\"126\",\"devcontainer\"\n\"126\",\"htc-android\"\n\"126\",\"pddl\"\n\"126\",\"nsalert\"\n\"126\",\"inotifywait\"\n\"126\",\"onitemselectedlistener\"\n\"126\",\"signed-applet\"\n\"126\",\"virtual-serial-port\"\n\"126\",\"google-ads-script\"\n\"126\",\"cocoalumberjack\"\n\"126\",\"syntaxnet\"\n\"126\",\"shake-build-system\"\n\"126\",\"krl\"\n\"126\",\"java-16\"\n\"126\",\"video-tracking\"\n\"126\",\"riot-games-api\"\n\"126\",\"osx-gatekeeper\"\n\"126\",\"outlook.com\"\n\"126\",\"pyodide\"\n\"126\",\"synthesize\"\n\"126\",\"rfc822\"\n\"126\",\"qwik\"\n\"126\",\"spry\"\n\"126\",\"expectation-maximization\"\n\"126\",\"mobilefirst-studio\"\n\"126\",\"assistant\"\n\"126\",\"azure-nsg\"\n\"126\",\"drupal-rules\"\n\"126\",\"tcc\"\n\"126\",\"curves\"\n\"126\",\"react-bootstrap-typeahead\"\n\"126\",\"geofirestore\"\n\"126\",\"custom-headers\"\n\"126\",\"qchart\"\n\"126\",\"qnx-neutrino\"\n\"126\",\"changelist\"\n\"126\",\"strdup\"\n\"126\",\"splitcontainer\"\n\"126\",\"collectstatic\"\n\"126\",\"curb\"\n\"126\",\"pegjs\"\n\"126\",\"lastindexof\"\n\"126\",\"tilemill\"\n\"126\",\"mdriven\"\n\"126\",\"line-intersection\"\n\"126\",\"yubico\"\n\"126\",\"flutter-theme\"\n\"126\",\"secondary-indexes\"\n\"126\",\"automated-deploy\"\n\"126\",\"hawq\"\n\"126\",\"autorest\"\n\"126\",\"powerbi-paginated-reports\"\n\"125\",\"livequery\"\n\"125\",\"ssjs\"\n\"125\",\"lis\"\n\"125\",\"ffserver\"\n\"125\",\"gimpfu\"\n\"125\",\"reportportal\"\n\"125\",\"fso\"\n\"125\",\"xmltextreader\"\n\"125\",\"pisa\"\n\"125\",\"xdebug-3\"\n\"125\",\"jsonencoder\"\n\"125\",\"volusion\"\n\"125\",\"snowflake-task\"\n\"125\",\"page-layout\"\n\"125\",\"jsonnet\"\n\"125\",\"runtime-compilation\"\n\"125\",\"faultexception\"\n\"125\",\"captiveportal\"\n\"125\",\"windows-error-reporting\"\n\"125\",\"wamp-protocol\"\n\"125\",\"mezzio\"\n\"125\",\"capability\"\n\"125\",\"windows-embedded\"\n\"125\",\"djcelery\"\n\"125\",\"readinessprobe\"\n\"125\",\"htop\"\n\"125\",\"jquery-mobile-collapsible\"\n\"125\",\"boolean-algebra\"\n\"125\",\"natural-join\"\n\"125\",\"covariance-matrix\"\n\"125\",\"spring-integration-aws\"\n\"125\",\"samtools\"\n\"125\",\"epl\"\n\"125\",\"nawk\"\n\"125\",\"wpa-supplicant\"\n\"125\",\"nelmioapidocbundle\"\n\"125\",\"postgraphile\"\n\"125\",\"otel\"\n\"125\",\"object-fit\"\n\"125\",\"microsoft-graph-toolkit\"\n\"125\",\"viper-architecture\"\n\"125\",\"pyenchant\"\n\"125\",\"javap\"\n\"125\",\"kiwi\"\n\"125\",\"ambiguous-grammar\"\n\"125\",\"sqlmap\"\n\"125\",\"copyright-display\"\n\"125\",\"xcode-6.2\"\n\"125\",\"dotliquid\"\n\"125\",\"hiddenfield\"\n\"125\",\"tabstop\"\n\"125\",\"harbor\"\n\"125\",\"drools-flow\"\n\"125\",\"dijit.layout\"\n\"125\",\"device-manager\"\n\"125\",\"honeypot\"\n\"125\",\"nstablecellview\"\n\"125\",\"stringwithformat\"\n\"125\",\"test-plan\"\n\"125\",\"accessibility-api\"\n\"125\",\"petalinux\"\n\"125\",\"evdev\"\n\"125\",\"android-media3\"\n\"125\",\"ligature\"\n\"125\",\"google-scholar\"\n\"125\",\"zend-decorators\"\n\"125\",\"scroll-snap\"\n\"125\",\"google-one-tap\"\n\"125\",\"usb-flash-drive\"\n\"125\",\"statelesswidget\"\n\"125\",\"array-initialization\"\n\"124\",\"jetpack-compose-accompanist\"\n\"124\",\"prismjs\"\n\"124\",\"git-alias\"\n\"124\",\"sliverappbar\"\n\"124\",\"ycsb\"\n\"124\",\"multitargeting\"\n\"124\",\"vue-select\"\n\"124\",\"swiftui-form\"\n\"124\",\"apache-ranger\"\n\"124\",\"rust-wasm\"\n\"124\",\"content-pages\"\n\"124\",\"owlready\"\n\"124\",\"sense\"\n\"124\",\"symbolicate\"\n\"124\",\"serialversionuid\"\n\"124\",\"document-management\"\n\"124\",\"recreate\"\n\"124\",\"wap\"\n\"124\",\"mysql-insert-id\"\n\"124\",\"django-paypal\"\n\"124\",\"captivenetwork\"\n\"124\",\"workfront-api\"\n\"124\",\"jquery-selectbox\"\n\"124\",\"cpanm\"\n\"124\",\"android-task\"\n\"124\",\"grpc-c#\"\n\"124\",\"spring-jmx\"\n\"124\",\"couchdb-2.0\"\n\"124\",\"wordprocessingml\"\n\"124\",\"dynamic-queries\"\n\"124\",\"horizontal-pod-autoscaling\"\n\"124\",\"entity-framework-core-2.1\"\n\"124\",\"mod-fcgid\"\n\"124\",\"rexml\"\n\"124\",\"facebook-instant-games\"\n\"124\",\"ucs2\"\n\"124\",\"sfguard\"\n\"124\",\"java-melody\"\n\"124\",\"machine.config\"\n\"124\",\"javalite\"\n\"124\",\"konvajs-reactjs\"\n\"124\",\"setneedsdisplay\"\n\"124\",\"codeigniter-hmvc\"\n\"124\",\"fosoauthserverbundle\"\n\"124\",\"vivado-hls\"\n\"124\",\"azure-monitor-workbooks\"\n\"124\",\"polish\"\n\"124\",\"cordova-android\"\n\"124\",\"driverkit\"\n\"124\",\"gdb-python\"\n\"124\",\"redux-devtools\"\n\"124\",\"android-flavors\"\n\"124\",\"gwt-mvp\"\n\"124\",\"hackage\"\n\"124\",\"nurbs\"\n\"124\",\"h3\"\n\"124\",\"tinyos\"\n\"124\",\"researchkit\"\n\"124\",\"elasticsearch-jdbc-river\"\n\"124\",\"projector\"\n\"124\",\"acrofields\"\n\"124\",\"cfmail\"\n\"124\",\"perforce-client-spec\"\n\"124\",\"pywikibot\"\n\"124\",\"nl2br\"\n\"124\",\"character-set\"\n\"124\",\"prometheus-blackbox-exporter\"\n\"124\",\"angular-dom-sanitizer\"\n\"124\",\"protege4\"\n\"124\",\"angular2-cli\"\n\"124\",\"email-confirmation\"\n\"124\",\"qtextdocument\"\n\"124\",\"security-roles\"\n\"124\",\"pari\"\n\"124\",\"autodesk-inventor\"\n\"124\",\"parameterized-unit-test\"\n\"124\",\"solr-boost\"\n\"124\",\"zkteco\"\n\"124\",\"google-meet\"\n\"124\",\"parallel-extensions\"\n\"123\",\"multiple-records\"\n\"123\",\"jenkins-2\"\n\"123\",\"php-8.2\"\n\"123\",\"git-blame\"\n\"123\",\"slimerjs\"\n\"123\",\"replay\"\n\"123\",\"cjson\"\n\"123\",\"react-native-ui-kitten\"\n\"123\",\"telescope\"\n\"123\",\"graphql-mutation\"\n\"123\",\"cni\"\n\"123\",\"jsonconverter\"\n\"123\",\"jtogglebutton\"\n\"123\",\"apparmor\"\n\"123\",\"makecert\"\n\"123\",\"rworldmap\"\n\"123\",\"ngx-formly\"\n\"123\",\"apache-wink\"\n\"123\",\"configurationsection\"\n\"123\",\"snap\"\n\"123\",\"language-comparisons\"\n\"123\",\"wcsession\"\n\"123\",\"roracle\"\n\"123\",\"singularitygs\"\n\"123\",\"method-missing\"\n\"123\",\"canvas-lms\"\n\"123\",\"docker-daemon\"\n\"123\",\"variable-initialization\"\n\"123\",\"metastore\"\n\"123\",\"unsupported-class-version\"\n\"123\",\"rospy\"\n\"123\",\"gpib\"\n\"123\",\"rtd\"\n\"123\",\"pdu\"\n\"123\",\"paypal-webhooks\"\n\"123\",\"coveralls\"\n\"123\",\"initialization-vector\"\n\"123\",\"describe\"\n\"123\",\"createml\"\n\"123\",\"azure-billing-api\"\n\"123\",\"writable\"\n\"123\",\"nslayoutmanager\"\n\"123\",\"swagger-3.0\"\n\"123\",\"video-toolbox\"\n\"123\",\"netlify-function\"\n\"123\",\"javacpp\"\n\"123\",\"migrating\"\n\"123\",\"google-beacon-platform\"\n\"123\",\"kube-apiserver\"\n\"123\",\"object-type\"\n\"123\",\"shunting-yard\"\n\"123\",\"atomicinteger\"\n\"123\",\"reflect\"\n\"123\",\"spyon\"\n\"123\",\"tms-web-core\"\n\"123\",\"azure-mysql-database\"\n\"123\",\"drift\"\n\"123\",\"conversion-specifier\"\n\"123\",\"permanent\"\n\"123\",\"qregularexpression\"\n\"123\",\"commandbar\"\n\"123\",\"angular-dependency-injection\"\n\"123\",\"mercator\"\n\"123\",\"tone.js\"\n\"123\",\"spatial-interpolation\"\n\"123\",\"morgan\"\n\"123\",\"merge-request\"\n\"123\",\"exacttarget\"\n\"123\",\"qabstractlistmodel\"\n\"123\",\"zeos\"\n\"123\",\"google-tasks\"\n\"123\",\"beam\"\n\"123\",\"fminsearch\"\n\"122\",\"mat-form-field\"\n\"122\",\"clonenode\"\n\"122\",\"dbref\"\n\"122\",\"ggts\"\n\"122\",\"list-manipulation\"\n\"122\",\"instrumented-test\"\n\"122\",\"jersey-test-framework\"\n\"122\",\"inspec\"\n\"122\",\"xmltodict\"\n\"122\",\"jsonslurper\"\n\"122\",\"xmltextwriter\"\n\"122\",\"data-race\"\n\"122\",\"maplibre-gl\"\n\"122\",\"firebird2.1\"\n\"122\",\"pins\"\n\"122\",\"bitflags\"\n\"122\",\"label-encoding\"\n\"122\",\"xdomainrequest\"\n\"122\",\"kinematics\"\n\"122\",\"robobrowser\"\n\"122\",\"ajax-request\"\n\"122\",\"database-management\"\n\"122\",\"variable-types\"\n\"122\",\"rollbar\"\n\"122\",\"gpg-signature\"\n\"122\",\"jmdns\"\n\"122\",\"google-workflows\"\n\"122\",\"waffle\"\n\"122\",\"vundle\"\n\"122\",\"kendo-tabstrip\"\n\"122\",\"vdsp\"\n\"122\",\"jpa-criteria\"\n\"122\",\"kerberos-delegation\"\n\"122\",\"angular-material-stepper\"\n\"122\",\"routeparams\"\n\"122\",\"nsprogressindicator\"\n\"122\",\"mongodb-kafka-connector\"\n\"122\",\"junit-runner\"\n\"122\",\"paypal-nvp\"\n\"122\",\"nspersistentstore\"\n\"122\",\"junction\"\n\"122\",\"spring-boot-2\"\n\"122\",\"bootstrap-switch\"\n\"122\",\"u2\"\n\"122\",\"tyrus\"\n\"122\",\"system32\"\n\"122\",\"visualize\"\n\"122\",\"ext3\"\n\"122\",\"god\"\n\"122\",\"extract-text-plugin\"\n\"122\",\"rgeo\"\n\"122\",\"magento2.1\"\n\"122\",\"exchange-server-2013\"\n\"122\",\"notarize\"\n\"122\",\"dropbox-php\"\n\"122\",\"node-worker-threads\"\n\"122\",\"opengl-es-1.1\"\n\"122\",\"controlpanel\"\n\"122\",\"haptic-feedback\"\n\"122\",\"azure-triggers\"\n\"122\",\"redo\"\n\"122\",\"android-darkmode\"\n\"122\",\"gzipinputstream\"\n\"122\",\"nuxeo\"\n\"122\",\"radtreeview\"\n\"122\",\"numpad\"\n\"122\",\"azure-iot-hub-device-management\"\n\"122\",\"scope-identity\"\n\"122\",\"charset\"\n\"122\",\"spartan\"\n\"122\",\"cuba-platform\"\n\"122\",\"mesh-network\"\n\"122\",\"reactive-swift\"\n\"122\",\"google-feed-api\"\n\"122\",\"bearing\"\n\"122\",\"arcade\"\n\"122\",\"flutter-sharedpreference\"\n\"122\",\"ems\"\n\"122\",\"idle-timer\"\n\"122\",\"query-tuning\"\n\"121\",\"template-function\"\n\"121\",\"anyobject\"\n\"121\",\"cloudflare-pages\"\n\"121\",\"weekend\"\n\"121\",\"interact.js\"\n\"121\",\"listcellrenderer\"\n\"121\",\"primeng-calendar\"\n\"121\",\"reporters\"\n\"121\",\"cncontactstore\"\n\"121\",\"instantsearch.js\"\n\"121\",\"jetty-8\"\n\"121\",\"js-test-driver\"\n\"121\",\"runbook\"\n\"121\",\"kue\"\n\"121\",\"inbound\"\n\"121\",\"selectnodes\"\n\"121\",\"adaptive-design\"\n\"121\",\"xmlsec\"\n\"121\",\"chronicle-map\"\n\"121\",\"xojo\"\n\"121\",\"python-oracledb\"\n\"121\",\"swingutilities\"\n\"121\",\"fulfillment\"\n\"121\",\"pycassa\"\n\"121\",\"idatareader\"\n\"121\",\"unreal\"\n\"121\",\"unityads\"\n\"121\",\"data-export\"\n\"121\",\"akka-remote-actor\"\n\"121\",\"ag\"\n\"121\",\"hypermedia\"\n\"121\",\"react-toastify\"\n\"121\",\"description-logic\"\n\"121\",\"wp-list-categories\"\n\"121\",\"polymerfire\"\n\"121\",\"turbo-pascal\"\n\"121\",\"onselect\"\n\"121\",\"ndjson\"\n\"121\",\"jump-list\"\n\"121\",\"entity-framework-core-3.1\"\n\"121\",\"spring-shell\"\n\"121\",\"san\"\n\"121\",\"ios10.3\"\n\"121\",\"powerapps-collection\"\n\"121\",\"maf\"\n\"121\",\"build-runner\"\n\"121\",\"knpmenubundle\"\n\"121\",\"vignette\"\n\"121\",\"2captcha\"\n\"121\",\"typeloadexception\"\n\"121\",\"accelerator\"\n\"121\",\"riscv32\"\n\"121\",\"goodness-of-fit\"\n\"121\",\"networkimageview\"\n\"121\",\"origen-sdk\"\n\"121\",\"gcovr\"\n\"121\",\"jackrabbit-oak\"\n\"121\",\"ref-cursor\"\n\"121\",\"express-gateway\"\n\"121\",\"tivoli\"\n\"121\",\"nowjs-sockets\"\n\"121\",\"gcc4.8\"\n\"121\",\"nstableviewcell\"\n\"121\",\"no-op\"\n\"121\",\"expectations\"\n\"121\",\"uievent\"\n\"121\",\"notsupportedexception\"\n\"121\",\"azure-resource-graph\"\n\"121\",\"peoplepicker\"\n\"121\",\"access-keys\"\n\"121\",\"omnicomplete\"\n\"121\",\"android-recents\"\n\"121\",\"react-native-animatable\"\n\"121\",\"reactive-extensions-js\"\n\"121\",\"react-native-ble-plx\"\n\"121\",\"react-flow\"\n\"121\",\"testdriven.net\"\n\"121\",\"lsa\"\n\"121\",\"qevent\"\n\"121\",\"layered-navigation\"\n\"121\",\"activestate\"\n\"121\",\"sphinxql\"\n\"121\",\"messageui\"\n\"121\",\"ihttpmodule\"\n\"121\",\"scriptable-object\"\n\"121\",\"cxf-codegen-plugin\"\n\"121\",\"partialfunction\"\n\"121\",\"cyclic-dependency\"\n\"121\",\"cydia-substrate\"\n\"121\",\"idispatch\"\n\"121\",\"hessian-matrix\"\n\"121\",\"usertype\"\n\"121\",\"subject-observer\"\n\"121\",\"stm32cubemx\"\n\"121\",\"ie11-developer-tools\"\n\"121\",\"parallax.js\"\n\"121\",\"spark-koalas\"\n\"121\",\"avahi\"\n\"120\",\"material-components-web\"\n\"120\",\"flutter-doctor\"\n\"120\",\"liquibase-sql\"\n\"120\",\"yourkit\"\n\"120\",\"yandex-maps\"\n\"120\",\"trident\"\n\"120\",\"ansys\"\n\"120\",\"web-platform-installer\"\n\"120\",\"template-method-pattern\"\n\"120\",\"xsp\"\n\"120\",\"ffmpeg-python\"\n\"120\",\"jssc\"\n\"120\",\"dataservice\"\n\"120\",\"django-admin-tools\"\n\"120\",\"childbrowser\"\n\"120\",\"jt400\"\n\"120\",\"discord-buttons\"\n\"120\",\"bioperl\"\n\"120\",\"contentsize\"\n\"120\",\"xpathnavigator\"\n\"120\",\"psr-0\"\n\"120\",\"roboflow\"\n\"120\",\"airwatch\"\n\"120\",\"univocity\"\n\"120\",\"session-hijacking\"\n\"120\",\"dataflowtask\"\n\"120\",\"ionic-cli\"\n\"120\",\"money-format\"\n\"120\",\"azure-dns\"\n\"120\",\"sap-data-dictionary\"\n\"120\",\"inputview\"\n\"120\",\"nscombobox\"\n\"120\",\"frameworkelement\"\n\"120\",\"object-properties\"\n\"120\",\"abstract-base-class\"\n\"120\",\"java-canvas\"\n\"120\",\"net-use\"\n\"120\",\"netrw\"\n\"120\",\"winrt-xaml-toolkit\"\n\"120\",\"bug-reporting\"\n\"120\",\"lzo\"\n\"120\",\"kraken.js\"\n\"120\",\"outlook-api\"\n\"120\",\"wmd\"\n\"120\",\"google-app-engine-php\"\n\"120\",\"setw\"\n\"120\",\"modelandview\"\n\"120\",\"devkit\"\n\"120\",\"schema-compare\"\n\"120\",\"hardcoded\"\n\"120\",\"notserializableexception\"\n\"120\",\"numeric-limits\"\n\"120\",\"tkcalendar\"\n\"120\",\"xcode7.1\"\n\"120\",\"horizon\"\n\"120\",\"certificate-store\"\n\"120\",\"android-jetpack-compose-text\"\n\"120\",\"c-standard-library\"\n\"120\",\"proximitysensor\"\n\"120\",\"http-content-length\"\n\"120\",\"qprinter\"\n\"120\",\"combn\"\n\"120\",\"cybersource\"\n\"120\",\"bcel\"\n\"120\",\"multi-database\"\n\"120\",\"yui-datatable\"\n\"120\",\"weave\"\n\"120\",\"pari-gp\"\n\"120\",\"arcanist\"\n\"120\",\"stdint\"\n\"120\",\"tournament\"\n\"120\",\"uvc\"\n\"120\",\"solana-cli\"\n\"120\",\"stl-format\"\n\"120\",\"sparkpost\"\n\"120\",\"praat\"\n\"120\",\"mediaextractor\"\n\"119\",\"jest-puppeteer\"\n\"119\",\"renovate\"\n\"119\",\"clojure-core.logic\"\n\"119\",\"printscreen\"\n\"119\",\"edittextpreference\"\n\"119\",\"wearables\"\n\"119\",\"sites\"\n\"119\",\"sshj\"\n\"119\",\"tegra\"\n\"119\",\"webpack-splitchunks\"\n\"119\",\"edifact\"\n\"119\",\"master-data-services\"\n\"119\",\"ssdp\"\n\"119\",\"mariadb-10.5\"\n\"119\",\"blackberry-storm\"\n\"119\",\"xmlnodelist\"\n\"119\",\"select-menu\"\n\"119\",\"filter-var\"\n\"119\",\"graceful-degradation\"\n\"119\",\"rml\"\n\"119\",\"canon-sdk\"\n\"119\",\"varray\"\n\"119\",\"ruby-2.2\"\n\"119\",\"vapor-fluent\"\n\"119\",\"grails-4\"\n\"119\",\"ibm-odm\"\n\"119\",\"gui-builder\"\n\"119\",\"invokelater\"\n\"119\",\"boost-any\"\n\"119\",\"e\"\n\"119\",\"density-independent-pixel\"\n\"119\",\"intermittent\"\n\"119\",\"bootstrap-slider\"\n\"119\",\"nebula-graph\"\n\"119\",\"gulp-less\"\n\"119\",\"inverse-kinematics\"\n\"119\",\"abbyy\"\n\"119\",\"code-standards\"\n\"119\",\"kpi\"\n\"119\",\"pymol\"\n\"119\",\"oauth-provider\"\n\"119\",\"visual-c++-2012\"\n\"119\",\"codepoint\"\n\"119\",\"observability\"\n\"119\",\"sfspeechrecognizer\"\n\"119\",\"output-formatting\"\n\"119\",\"object-tag\"\n\"119\",\"rexster\"\n\"119\",\"buildship\"\n\"119\",\"typescript1.4\"\n\"119\",\"java1.4\"\n\"119\",\"scramble\"\n\"119\",\"radzen\"\n\"119\",\"tadoquery\"\n\"119\",\"gemspecs\"\n\"119\",\"redisjson\"\n\"119\",\"assume-role\"\n\"119\",\"byebug\"\n\"119\",\"chameleon\"\n\"119\",\"spongycastle\"\n\"119\",\"dtw\"\n\"119\",\"react-360\"\n\"119\",\"qcompleter\"\n\"119\",\"react-data-table-component\"\n\"119\",\"off-screen\"\n\"119\",\"angular-activatedroute\"\n\"119\",\"angular-cookies\"\n\"119\",\"angular2-router\"\n\"119\",\"liferay-service-builder\"\n\"119\",\"sdl-mixer\"\n\"119\",\"stateful-session-bean\"\n\"119\",\"image-generation\"\n\"119\",\"better-sqlite3\"\n\"119\",\"sortedmap\"\n\"119\",\"heterogeneous\"\n\"119\",\"mstsc\"\n\"119\",\"google-index\"\n\"118\",\"backtrader\"\n\"118\",\"stack-size\"\n\"118\",\"flow-typed\"\n\"118\",\"flutter-freezed\"\n\"118\",\"lld\"\n\"118\",\"lambda-authorizer\"\n\"118\",\"firebase-admob\"\n\"118\",\"cdialog\"\n\"118\",\"firebase-invites\"\n\"118\",\"makemigrations\"\n\"118\",\"date-pipe\"\n\"118\",\"next-intl\"\n\"118\",\"swift-package\"\n\"118\",\"fileset\"\n\"118\",\"select2-rails\"\n\"118\",\"window-soft-input-mode\"\n\"118\",\"jint\"\n\"118\",\"job-queue\"\n\"118\",\"aws-lex\"\n\"118\",\"r-rownames\"\n\"118\",\"walmart-api\"\n\"118\",\"unrar\"\n\"118\",\"django-sites\"\n\"118\",\"facter\"\n\"118\",\"session-replication\"\n\"118\",\"modulation\"\n\"118\",\"mod-ssl\"\n\"118\",\"grpc-c++\"\n\"118\",\"samsung\"\n\"118\",\"satisfiability\"\n\"118\",\"erc721\"\n\"118\",\"core-js\"\n\"118\",\"applovin\"\n\"118\",\"aws-sqs-fifo\"\n\"118\",\"spring-security-kerberos\"\n\"118\",\"crash-log\"\n\"118\",\"porter-duff\"\n\"118\",\"nssavepanel\"\n\"118\",\"spring-autoconfiguration\"\n\"118\",\"shader-graph\"\n\"118\",\"branch-and-bound\"\n\"118\",\"knative-serving\"\n\"118\",\"domexception\"\n\"118\",\"pytest-asyncio\"\n\"118\",\"kotlin-interop\"\n\"118\",\"lexicaljs\"\n\"118\",\"sysml\"\n\"118\",\"objective-c-swift-bridge\"\n\"118\",\"ex-unit\"\n\"118\",\"browser-tab\"\n\"118\",\"shortest\"\n\"118\",\"visualworks\"\n\"118\",\"visited\"\n\"118\",\"rda\"\n\"118\",\"non-recursive\"\n\"118\",\"cookieless\"\n\"118\",\"cacti\"\n\"118\",\"controller-advice\"\n\"118\",\"xcode6.4\"\n\"118\",\"haar-wavelet\"\n\"118\",\"nowrap\"\n\"118\",\"polyglot\"\n\"118\",\"gcc4.9\"\n\"118\",\"deviceiocontrol\"\n\"118\",\"hiveddl\"\n\"118\",\"execl\"\n\"118\",\"gembox-spreadsheet\"\n\"118\",\"g-code\"\n\"118\",\"converse.js\"\n\"118\",\"openmaptiles\"\n\"118\",\"account-management\"\n\"118\",\"monospace\"\n\"118\",\"motif\"\n\"118\",\"mosaic\"\n\"118\",\"messagedialog\"\n\"118\",\"log4js-node\"\n\"118\",\"ldapjs\"\n\"118\",\"elementhost\"\n\"118\",\"qnetworkreply\"\n\"118\",\"onclientclick\"\n\"118\",\"geotagging\"\n\"118\",\"elasticsearch-rails\"\n\"118\",\"office-js-helpers\"\n\"118\",\"ctype\"\n\"118\",\"stripe.js\"\n\"118\",\"iqkeyboardmanager\"\n\"118\",\"tf-cli\"\n\"118\",\"themeroller\"\n\"118\",\"batchsize\"\n\"118\",\"structuremap3\"\n\"118\",\"amazon-keyspaces\"\n\"118\",\"illuminate-container\"\n\"118\",\"forall\"\n\"118\",\"userspace\"\n\"118\",\"automapper-6\"\n\"118\",\"compound-index\"\n\"118\",\"gm\"\n\"118\",\"artifactory-query-lang\"\n\"118\",\"custom-scrolling\"\n\"118\",\"git-rm\"\n\"118\",\"cyclic\"\n\"118\",\"amazon-pay\"\n\"117\",\"site-prism\"\n\"117\",\"git-filter-repo\"\n\"117\",\"st\"\n\"117\",\"graphcool\"\n\"117\",\"slimscroll\"\n\"117\",\"lnk2005\"\n\"117\",\"xsd2code\"\n\"117\",\"remedy\"\n\"117\",\"slidify\"\n\"117\",\"ssis-2017\"\n\"117\",\"github-app\"\n\"117\",\"reportviewer2008\"\n\"117\",\"youtube-analytics\"\n\"117\",\"ccnet-config\"\n\"117\",\"containable\"\n\"117\",\"jsapi\"\n\"117\",\"jsonreader\"\n\"117\",\"sendinblue\"\n\"117\",\"main-method\"\n\"117\",\"cedar\"\n\"117\",\"operator-sdk\"\n\"117\",\"oracle8i\"\n\"117\",\"readxml\"\n\"117\",\"unsafemutablepointer\"\n\"117\",\"airdrop\"\n\"117\",\"windows-server-2022\"\n\"117\",\"rebar3\"\n\"117\",\"server-side-validation\"\n\"117\",\"push-api\"\n\"117\",\"angular-ngrx-data\"\n\"117\",\"mongodb-shell\"\n\"117\",\"onstart\"\n\"117\",\"superpowered\"\n\"117\",\"dynamicmethod\"\n\"117\",\"nsexpression\"\n\"117\",\"nebula\"\n\"117\",\"ordereddict\"\n\"117\",\"virtualpathprovider\"\n\"117\",\"system.timers.timer\"\n\"117\",\"sidenav\"\n\"117\",\"wkhtmltoimage\"\n\"117\",\"ezdxf\"\n\"117\",\"golang-migrate\"\n\"117\",\"browserify-shim\"\n\"117\",\"mips64\"\n\"117\",\"sfdc\"\n\"117\",\"lynx\"\n\"117\",\".net-maui.shell\"\n\"117\",\"typemock\"\n\"117\",\"typo3-flow\"\n\"117\",\"rewriting\"\n\"117\",\"gogs\"\n\"117\",\"amazon-workspaces\"\n\"117\",\"fabricjs2\"\n\"117\",\"asp.net-mvc-ajax\"\n\"117\",\"reformat\"\n\"117\",\"openlaszlo\"\n\"117\",\"hadoop3\"\n\"117\",\"android-gradle-3.0\"\n\"117\",\"tlf\"\n\"117\",\"openpose\"\n\"117\",\"scite\"\n\"117\",\"openshift-cartridge\"\n\"117\",\"expires-header\"\n\"117\",\"differentialequations.jl\"\n\"117\",\"laravel-localization\"\n\"117\",\"chai-as-promised\"\n\"117\",\"iptc\"\n\"117\",\"textinputlayout\"\n\"117\",\"proxyquire\"\n\"117\",\"logstash-logback-encoder\"\n\"117\",\"qnap\"\n\"117\",\"texreg\"\n\"117\",\"strict-mode\"\n\"117\",\"angular-cdk-virtual-scroll\"\n\"117\",\"custom-properties\"\n\"117\",\"property-list\"\n\"117\",\"goal-tracking\"\n\"117\",\"mean-square-error\"\n\"117\",\"cyclejs\"\n\"117\",\"zebra-puzzle\"\n\"117\",\"tilt\"\n\"116\",\"gitblit\"\n\"116\",\"apache-commons-csv\"\n\"116\",\"clicklistener\"\n\"116\",\"fiji\"\n\"116\",\"cmake-custom-command\"\n\"116\",\"cloudcontrol\"\n\"116\",\"probe\"\n\"116\",\"editorjs\"\n\"116\",\"wdf\"\n\"116\",\"livenessprobe\"\n\"116\",\"phpthumb\"\n\"116\",\"console.readline\"\n\"116\",\"xlc\"\n\"116\",\"ngrx-store-4.0\"\n\"116\",\"select-object\"\n\"116\",\"flask-restless\"\n\"116\",\"image-quality\"\n\"116\",\"ccl\"\n\"116\",\"adfs4.0\"\n\"116\",\"kubernetes-security\"\n\"116\",\"xgbclassifier\"\n\"116\",\"kubernetes-networking\"\n\"116\",\"ruby-mocha\"\n\"116\",\"grails-2.4\"\n\"116\",\"dnspython\"\n\"116\",\"urlconf\"\n\"116\",\"facial-identification\"\n\"116\",\"namevaluecollection\"\n\"116\",\"vcredist\"\n\"116\",\"windows-dev-center\"\n\"116\",\"ibm-cloud-storage\"\n\"116\",\"sharepoint-2019\"\n\"116\",\"share-open-graph\"\n\"116\",\"wildfly-11\"\n\"116\",\"callout\"\n\"116\",\"data-acquisition\"\n\"116\",\"waithandle\"\n\"116\",\"watch-face-api\"\n\"116\",\"kafka-rest\"\n\"116\",\"border-image\"\n\"116\",\"onsen-ui2\"\n\"116\",\"create-function\"\n\"116\",\"crafter-cms\"\n\"116\",\"worker-process\"\n\"116\",\"htmlbars\"\n\"116\",\"gulp-protractor\"\n\"116\",\"angstrom-linux\"\n\"116\",\"natvis\"\n\"116\",\"hssf\"\n\"116\",\"wp-nav-walker\"\n\"116\",\"winmm\"\n\"116\",\"libev\"\n\"116\",\"codelens\"\n\"116\",\"kmm\"\n\"116\",\"reversing\"\n\"116\",\"libmysql\"\n\"116\",\"kong-ingress\"\n\"116\",\"libgosu\"\n\"116\",\"model.matrix\"\n\"116\",\"opennms\"\n\"116\",\"opera-extension\"\n\"116\",\"hololens-emulator\"\n\"116\",\"playwright-java\"\n\"116\",\"timer-jobs\"\n\"116\",\"reference-wrapper\"\n\"116\",\"tablerowsorter\"\n\"116\",\"caffe2\"\n\"116\",\"nvenc\"\n\"116\",\"regfreecom\"\n\"116\",\"characteristics\"\n\"116\",\"essbase\"\n\"116\",\"text-indent\"\n\"116\",\"spir-v\"\n\"116\",\"chart.js3\"\n\"116\",\"log-shipping\"\n\"116\",\"iggrid\"\n\"116\",\"seam3\"\n\"116\",\"hasownproperty\"\n\"116\",\"stb-image\"\n\"116\",\"ember-old-router\"\n\"116\",\"idn\"\n\"116\",\"toolstripmenu\"\n\"116\",\"texturing\"\n\"116\",\"zend-db-select\"\n\"116\",\"subsampling\"\n\"116\",\"auto-value\"\n\"116\",\"force-download\"\n\"116\",\"tf-slim\"\n\"116\",\"avaudioplayernode\"\n\"116\",\"quartile\"\n\"115\",\"primality-test\"\n\"115\",\"jenkins-docker\"\n\"115\",\"github-api-v3\"\n\"115\",\"trojan\"\n\"115\",\"treesitter\"\n\"115\",\"instruction-encoding\"\n\"115\",\"reactor-kafka\"\n\"115\",\"react-on-rails\"\n\"115\",\"graphql-tools\"\n\"115\",\"sstream\"\n\"115\",\"phonetics\"\n\"115\",\"co\"\n\"115\",\"carryflag\"\n\"115\",\"appfuse\"\n\"115\",\"voicexml\"\n\"115\",\"laravel-events\"\n\"115\",\"cinder\"\n\"115\",\"cclayer\"\n\"115\",\"saleor\"\n\"115\",\"smartfoxserver\"\n\"115\",\"catalan\"\n\"115\",\"angular-ui-router-extras\"\n\"115\",\"pscp\"\n\"115\",\"id3v2\"\n\"115\",\"vdproj\"\n\"115\",\"ajax-upload\"\n\"115\",\"cs4\"\n\"115\",\"windows-mobile-5.0\"\n\"115\",\"aws-cdk-typescript\"\n\"115\",\"createprocessasuser\"\n\"115\",\"nscell\"\n\"115\",\"errai\"\n\"115\",\"payara-micro\"\n\"115\",\"turing-complete\"\n\"115\",\"initializing\"\n\"115\",\"native-module\"\n\"115\",\"android-usb\"\n\"115\",\"azimuth\"\n\"115\",\"borderpane\"\n\"115\",\"infix-operator\"\n\"115\",\"modular-design\"\n\"115\",\"deployd\"\n\"115\",\"ar.drone\"\n\"115\",\"nssearchfield\"\n\"115\",\"appintents\"\n\"115\",\"entrust\"\n\"115\",\"8085\"\n\"115\",\"ampscript\"\n\"115\",\"pyjwt\"\n\"115\",\"viewexpiredexception\"\n\"115\",\"setupapi\"\n\"115\",\"neventstore\"\n\"115\",\"oracle-ucm\"\n\"115\",\"4d-database\"\n\"115\",\"asyncdisplaykit\"\n\"115\",\"domready\"\n\"115\",\"ixmlserializable\"\n\"115\",\"hitcounter\"\n\"115\",\"android-gui\"\n\"115\",\"cooja\"\n\"115\",\"quicksand\"\n\"115\",\"hiphop\"\n\"115\",\"protobuf.js\"\n\"115\",\"layered\"\n\"115\",\"launchpad\"\n\"115\",\"odf\"\n\"115\",\"pen\"\n\"115\",\"location-provider\"\n\"115\",\"mesibo\"\n\"115\",\"off-canvas-menu\"\n\"115\",\"react-native-debugger\"\n\"115\",\"qbs\"\n\"115\",\"google-fit-api\"\n\"115\",\"node-fibers\"\n\"115\",\"maxent\"\n\"115\",\"qtscript\"\n\"115\",\"mbaas\"\n\"115\",\"gnu-smalltalk\"\n\"115\",\"url-masking\"\n\"115\",\"line-by-line\"\n\"115\",\"elsa-workflows\"\n\"115\",\"beaker\"\n\"115\",\"fontmetrics\"\n\"115\",\"std-bitset\"\n\"115\",\"zclip\"\n\"115\",\"google-smartlockpasswords\"\n\"115\",\"sup\"\n\"115\",\"zbuffer\"\n\"114\",\"muse\"\n\"114\",\"websocket-sharp\"\n\"114\",\"widestring\"\n\"114\",\"process-pool\"\n\"114\",\"decibel\"\n\"114\",\"multiple-views\"\n\"114\",\"graphstream\"\n\"114\",\"backing-beans\"\n\"114\",\"multiple-processes\"\n\"114\",\"sketchapp\"\n\"114\",\"conio\"\n\"114\",\"xinclude\"\n\"114\",\"mapsforge\"\n\"114\",\"runatserver\"\n\"114\",\"connect-mongo\"\n\"114\",\"socket.io-redis\"\n\"114\",\"filenotfounderror\"\n\"114\",\"software-defined-radio\"\n\"114\",\"laravel-filesystem\"\n\"114\",\"smooth-streaming\"\n\"114\",\"construct-2\"\n\"114\",\"python-playsound\"\n\"114\",\"physx\"\n\"114\",\"firefox3.6\"\n\"114\",\"gpars\"\n\"114\",\"factories\"\n\"114\",\"jpasswordfield\"\n\"114\",\"docker-engine\"\n\"114\",\"captions\"\n\"114\",\"data-connections\"\n\"114\",\"agens-graph\"\n\"114\",\"episerver-7\"\n\"114\",\"dynamics-al\"\n\"114\",\"demandware\"\n\"114\",\"azure-devops-migration-tools\"\n\"114\",\"gulp-4\"\n\"114\",\"system-shutdown\"\n\"114\",\"uiaccelerometer\"\n\"114\",\"shinybs\"\n\"114\",\"typeform\"\n\"114\",\"visual-format-language\"\n\"114\",\"azure-notebooks\"\n\"114\",\"hm-10\"\n\"114\",\"taco\"\n\"114\",\"dimensional\"\n\"114\",\"azure-spatial-anchors\"\n\"114\",\"android-instant-run\"\n\"114\",\"vetur\"\n\"114\",\"hamiltonian-cycle\"\n\"114\",\"asp.net-1.1\"\n\"114\",\"opensearch-dashboards\"\n\"114\",\"low-memory\"\n\"114\",\"google-cloud-identity\"\n\"114\",\"angularjs-animation\"\n\"114\",\"specification-pattern\"\n\"114\",\"acm-java-libraries\"\n\"114\",\"offloading\"\n\"114\",\"generic-lambda\"\n\"114\",\"reactive-banana\"\n\"114\",\"pyvis\"\n\"114\",\"collabnet\"\n\"114\",\"pardot\"\n\"114\",\"libxslt\"\n\"114\",\"sdf\"\n\"114\",\"flutter-objectbox\"\n\"114\",\"zapier-cli\"\n\"114\",\"complement\"\n\"114\",\"struts2-json-plugin\"\n\"114\",\"autocmd\"\n\"114\",\"zend-controller\"\n\"114\",\"qt5.3\"\n\"114\",\"shellsort\"\n\"114\",\"zero-padding\"\n\"113\",\"eclipse-virgo\"\n\"113\",\"jenkins-email-ext\"\n\"113\",\"inspection\"\n\"113\",\"feedburner\"\n\"113\",\"squirrel.windows\"\n\"113\",\"react-router-component\"\n\"113\",\"tempus-dominus-datetimepicker\"\n\"113\",\"clasp\"\n\"113\",\"primeng-table\"\n\"113\",\"xslcompiledtransform\"\n\"113\",\"tree-structure\"\n\"113\",\"nexus-4\"\n\"113\",\"flask-restx\"\n\"113\",\"jsr310\"\n\"113\",\"freshdesk\"\n\"113\",\"xpsdocument\"\n\"113\",\"construction\"\n\"113\",\"jsonapi-resources\"\n\"113\",\"futex\"\n\"113\",\"serverless-offline\"\n\"113\",\"oracle-apex-18.2\"\n\"113\",\"meta-search\"\n\"113\",\"django-simple-history\"\n\"113\",\"icinga\"\n\"113\",\"avaya\"\n\"113\",\"avurlasset\"\n\"113\",\"django-flatpages\"\n\"113\",\"facescontext\"\n\"113\",\"rllib\"\n\"113\",\"kingswaysoft\"\n\"113\",\"document-based\"\n\"113\",\"angularjs-select\"\n\"113\",\"alfresco-enterprise\"\n\"113\",\"unnotificationrequest\"\n\"113\",\"khan-academy\"\n\"113\",\"keytab\"\n\"113\",\"angular-openlayers\"\n\"113\",\"aws-sdk-ios\"\n\"113\",\"jquery-1.9\"\n\"113\",\"svn-merge\"\n\"113\",\"wp-graphql\"\n\"113\",\"corner-detection\"\n\"113\",\"survminer\"\n\"113\",\"ttkwidgets\"\n\"113\",\"bottom-navigation-bar\"\n\"113\",\"delphi-12-athens\"\n\"113\",\"html5lib\"\n\"113\",\"native-methods\"\n\"113\",\"appletviewer\"\n\"113\",\"error-detection\"\n\"113\",\"rightbarbuttonitem\"\n\"113\",\"rational-number\"\n\"113\",\"virtual-pc\"\n\"113\",\"system.componentmodel\"\n\"113\",\"janus-gateway\"\n\"113\",\"buck\"\n\"113\",\"code.org\"\n\"113\",\"rasa-x\"\n\"113\",\"extras\"\n\"113\",\"ravendb-studio\"\n\"113\",\"object-graph\"\n\"113\",\"virtual-attribute\"\n\"113\",\"wmode\"\n\"113\",\"organizer\"\n\"113\",\"pydantic-v2\"\n\"113\",\"vertical-text\"\n\"113\",\"schannel\"\n\"113\",\"expression-templates\"\n\"113\",\"bunyan\"\n\"113\",\"xcdatamodel\"\n\"113\",\"gwt-ext\"\n\"113\",\"happy\"\n\"113\",\"timed\"\n\"113\",\"android-intent-chooser\"\n\"113\",\"acr\"\n\"113\",\"splidejs\"\n\"113\",\"chai-http\"\n\"113\",\"ceylon\"\n\"113\",\"oncreateoptionsmenu\"\n\"113\",\"qlistwidgetitem\"\n\"113\",\"google-cloud-armor\"\n\"113\",\"memcmp\"\n\"113\",\"mps\"\n\"113\",\"respond-to\"\n\"113\",\"customtaskpane\"\n\"113\",\"amazonsellercentral\"\n\"113\",\"zend-paginator\"\n\"113\",\"embedded-sql\"\n\"113\",\"automapper-2\"\n\"113\",\"mechanicalsoup\"\n\"113\",\"qtoolbar\"\n\"113\",\"maven-enforcer-plugin\"\n\"113\",\"web-developer-toolbar\"\n\"112\",\"listctrl\"\n\"112\",\"srgb\"\n\"112\",\"ef-core-8.0\"\n\"112\",\"xrandr\"\n\"112\",\"dbx\"\n\"112\",\"clearance\"\n\"112\",\"flipclock\"\n\"112\",\"ec2-api-tools\"\n\"112\",\"skype-bots\"\n\"112\",\"ebay-sdk\"\n\"112\",\"site-packages\"\n\"112\",\"class-constructors\"\n\"112\",\"mvcmailer\"\n\"112\",\"vue-cli-4\"\n\"112\",\"mura\"\n\"112\",\"intel-galileo\"\n\"112\",\"xz\"\n\"112\",\"file-uri\"\n\"112\",\"image-morphology\"\n\"112\",\"freetext\"\n\"112\",\"django-admin-actions\"\n\"112\",\"fitdistrplus\"\n\"112\",\"self-contained\"\n\"112\",\"uiview-hierarchy\"\n\"112\",\"flash-cs5.5\"\n\"112\",\"pact-jvm\"\n\"112\",\"pushbullet\"\n\"112\",\"agents\"\n\"112\",\"react-testing\"\n\"112\",\"mysql-5.1\"\n\"112\",\"jmodelica\"\n\"112\",\"rot13\"\n\"112\",\"datagridtextcolumn\"\n\"112\",\"method-swizzling\"\n\"112\",\"rowlocking\"\n\"112\",\"django-model-field\"\n\"112\",\"avro-tools\"\n\"112\",\"micronaut-client\"\n\"112\",\"django-static\"\n\"112\",\"pspdfkit\"\n\"112\",\"blazor-jsinterop\"\n\"112\",\"jquery-lazyload\"\n\"112\",\"mongodb-realm\"\n\"112\",\"opencv-stitching\"\n\"112\",\"arangojs\"\n\"112\",\"mod-headers\"\n\"112\",\"surrogate-key\"\n\"112\",\"surrogate-pairs\"\n\"112\",\"post-meta\"\n\"112\",\"easyocr\"\n\"112\",\"typecast-operator\"\n\"112\",\"wurfl\"\n\"112\",\"mongo-collection\"\n\"112\",\"winrt-component\"\n\"112\",\"java.library.path\"\n\"112\",\"rhadoop\"\n\"112\",\"ocaml-dune\"\n\"112\",\"formflow\"\n\"112\",\".net-3.0\"\n\"112\",\"gob\"\n\"112\",\"sql-returning\"\n\"112\",\"reed-solomon\"\n\"112\",\"droplet\"\n\"112\",\"dgraph\"\n\"112\",\"directorysearcher\"\n\"112\",\"happens-before\"\n\"112\",\"business-objects-sdk\"\n\"112\",\"dialyzer\"\n\"112\",\"excel-web-query\"\n\"112\",\"dotnetnuke-9\"\n\"112\",\"mockups\"\n\"112\",\"c++builder-2010\"\n\"112\",\"spring-tools-4\"\n\"112\",\"storage-class-specifier\"\n\"112\",\"angularjs-controlleras\"\n\"112\",\"cfspreadsheet\"\n\"112\",\"geomap\"\n\"112\",\"test-environments\"\n\"112\",\"moovweb\"\n\"112\",\"nodejitsu\"\n\"112\",\"react-animations\"\n\"112\",\"android-parser\"\n\"112\",\"mbox\"\n\"112\",\"suave\"\n\"112\",\"web-animations\"\n\"112\",\"solana-program-library\"\n\"112\",\"multi-layer\"\n\"112\",\"google-prediction\"\n\"112\",\"thorntail\"\n\"112\",\"glcm\"\n\"112\",\"focusout\"\n\"111\",\"balloon\"\n\"111\",\"federated\"\n\"111\",\"ggsave\"\n\"111\",\"deferred-loading\"\n\"111\",\"sitemap.xml\"\n\"111\",\"apache-cloudstack\"\n\"111\",\"remoteapp\"\n\"111\",\"bacnet\"\n\"111\",\"db-browser-sqlite\"\n\"111\",\"apache-spark-1.6\"\n\"111\",\"landsat\"\n\"111\",\"black-box-testing\"\n\"111\",\"apache-stringutils\"\n\"111\",\"python-newspaper\"\n\"111\",\"checkpointing\"\n\"111\",\"apache-poi-4\"\n\"111\",\"connection-pool\"\n\"111\",\"chron\"\n\"111\",\"ccache\"\n\"111\",\"grails-3.3\"\n\"111\",\"oracle21c\"\n\"111\",\"roguelike\"\n\"111\",\"datagridviewrow\"\n\"111\",\"django-pipeline\"\n\"111\",\"fast-forward\"\n\"111\",\"watson-dialog\"\n\"111\",\"valarray\"\n\"111\",\"jodit\"\n\"111\",\"share-intent\"\n\"111\",\"jongo\"\n\"111\",\"hyperledger-indy\"\n\"111\",\"database-link\"\n\"111\",\"keynote\"\n\"111\",\"iasyncresult\"\n\"111\",\"rootfs\"\n\"111\",\"wcf-configuration\"\n\"111\",\"nest-device-access\"\n\"111\",\"pcfdev\"\n\"111\",\"infopath-2007\"\n\"111\",\"paster\"\n\"111\",\"turbine\"\n\"111\",\"turbogears\"\n\"111\",\"appium-java\"\n\"111\",\"nsnetservice\"\n\"111\",\"path-parameter\"\n\"111\",\"boost-signals2\"\n\"111\",\"enterprise-distribution\"\n\"111\",\"dwt\"\n\"111\",\"epsg\"\n\"111\",\"pdfjs-dist\"\n\"111\",\"sidecar\"\n\"111\",\"uglifyjs2\"\n\"111\",\"syntax-checking\"\n\"111\",\"microtime\"\n\"111\",\".net-4.7\"\n\"111\",\"wixsharp\"\n\"111\",\"libmemcached\"\n\"111\",\"osi\"\n\"111\",\"uclibc\"\n\"111\",\"gdt\"\n\"111\",\"mmx\"\n\"111\",\"scalapb\"\n\"111\",\"digital-ocean-spaces\"\n\"111\",\"happstack\"\n\"111\",\"expr\"\n\"111\",\"expression-blend-4\"\n\"111\",\"efxclipse\"\n\"111\",\"restheart\"\n\"111\",\"testify\"\n\"111\",\"spectron\"\n\"111\",\"memory-consumption\"\n\"111\",\"hxt\"\n\"111\",\"react-error-boundary\"\n\"111\",\"qdap\"\n\"111\",\"laravel-scheduler\"\n\"111\",\"log4perl\"\n\"111\",\"mcu\"\n\"111\",\"three-tier\"\n\"111\",\"bass\"\n\"111\",\"lime\"\n\"111\",\"dajaxice\"\n\"111\",\"array-column\"\n\"111\",\"web-component-tester\"\n\"111\",\"yui-pure-css\"\n\"111\",\"thai\"\n\"111\",\"subgit\"\n\"111\",\"heic\"\n\"111\",\"googlesigninaccount\"\n\"111\",\"urlhelper\"\n\"110\",\"defaults\"\n\"110\",\"phalcon-routing\"\n\"110\",\"dbghelp\"\n\"110\",\"vue-native\"\n\"110\",\"stackmob\"\n\"110\",\"github-issues\"\n\"110\",\"getserversideprops\"\n\"110\",\"instafeedjs\"\n\"110\",\"munin\"\n\"110\",\"flutter-cubit\"\n\"110\",\"web-publishing\"\n\"110\",\"sslv3\"\n\"110\",\"backstage\"\n\"110\",\"remarkjs\"\n\"110\",\"multiplicity\"\n\"110\",\"xxd\"\n\"110\",\"team-explorer-everywhere\"\n\"110\",\"remote-execution\"\n\"110\",\"flipper\"\n\"110\",\"seleniumwire\"\n\"110\",\"freshmvvm\"\n\"110\",\"xquartz\"\n\"110\",\"xml-simple\"\n\"110\",\"laravel-form\"\n\"110\",\"frontpage\"\n\"110\",\"voila\"\n\"110\",\"data-mapping\"\n\"110\",\"xlsb\"\n\"110\",\"ng-admin\"\n\"110\",\"kubeconfig\"\n\"110\",\"run-script\"\n\"110\",\"micro-architecture\"\n\"110\",\"jobservice\"\n\"110\",\"documentfilter\"\n\"110\",\"aws-ebs\"\n\"110\",\"aws-ecr\"\n\"110\",\"fal\"\n\"110\",\"ibm-jazz\"\n\"110\",\"recaptcha-enterprise\"\n\"110\",\"database-sequence\"\n\"110\",\"rolling-average\"\n\"110\",\"boost-multi-array\"\n\"110\",\"ncdf4\"\n\"110\",\"salesforce-chatter\"\n\"110\",\"nested-json\"\n\"110\",\"ionic7\"\n\"110\",\"spring-cloud-kubernetes\"\n\"110\",\"katalon-recorder\"\n\"110\",\"cortex-a8\"\n\"110\",\"n-dimensional\"\n\"110\",\"nspersistentcloudkitcontainer\"\n\"110\",\"worklight-runtime\"\n\"110\",\"azure-backup-vault\"\n\"110\",\"android-tabbed-activity\"\n\"110\",\"neo4jphp\"\n\"110\",\"cpu-time\"\n\"110\",\"konsole\"\n\"110\",\"visual-c++-2005\"\n\"110\",\"extended-events\"\n\"110\",\"type-definition\"\n\"110\",\"golem\"\n\"110\",\"netbeans-11\"\n\"110\",\"java-15\"\n\"110\",\"knuth-morris-pratt\"\n\"110\",\"javapos\"\n\"110\",\"google-breakpad\"\n\"110\",\"pocketsphinx-android\"\n\"110\",\"differential-evolution\"\n\"110\",\"to-json\"\n\"110\",\"didset\"\n\"110\",\"draft-js-plugins\"\n\"110\",\"bullmq\"\n\"110\",\"cac\"\n\"110\",\"difftool\"\n\"110\",\"asp-net-core-spa-services\"\n\"110\",\"plural\"\n\"110\",\"openoffice-basic\"\n\"110\",\"vertex-array\"\n\"110\",\"dottrace\"\n\"110\",\"sql-data-warehouse\"\n\"110\",\"dronekit\"\n\"110\",\"executors\"\n\"110\",\"android-immersive\"\n\"110\",\"schemacrawler\"\n\"110\",\"messagekit\"\n\"110\",\"responsive-slides\"\n\"110\",\"testfx\"\n\"110\",\"splunk-formula\"\n\"110\",\"ceres-solver\"\n\"110\",\"ios-background-mode\"\n\"110\",\"odp.net-managed\"\n\"110\",\"odata4j\"\n\"110\",\"cold-start\"\n\"110\",\"text2vec\"\n\"110\",\"everyauth\"\n\"110\",\"google-pixel\"\n\"110\",\"urql\"\n\"110\",\"webkitgtk\"\n\"110\",\"lint-staged\"\n\"110\",\"imagecreatefrompng\"\n\"110\",\"shift-jis\"\n\"110\",\"mds\"\n\"110\",\"font-lock\"\n\"110\",\"mta\"\n\"110\",\"thread-sanitizer\"\n\"110\",\"arity\"\n\"110\",\"endpoints-proto-datastore\"\n\"110\",\"prettier-vscode\"\n\"110\",\"powerpoint-2013\"\n\"110\",\"engineyard\"\n\"110\",\"git-patch\"\n\"109\",\"multiversx\"\n\"109\",\"efk\"\n\"109\",\"react-portal\"\n\"109\",\"ckrecord\"\n\"109\",\"flutter-custompainter\"\n\"109\",\"tekton-pipelines\"\n\"109\",\"telepot\"\n\"109\",\"vstest.console.exe\"\n\"109\",\"livewire-3\"\n\"109\",\"teardown\"\n\"109\",\"ggridges\"\n\"109\",\"pkcs#8\"\n\"109\",\"json-serializable\"\n\"109\",\"dbcc\"\n\"109\",\"pkcs11interop\"\n\"109\",\"appgallery-connect\"\n\"109\",\"chrome-debugging\"\n\"109\",\"snowsql\"\n\"109\",\"pandas-melt\"\n\"109\",\"flashbuilder4\"\n\"109\",\"fullcalendar-6\"\n\"109\",\"self-invoking-function\"\n\"109\",\"dithering\"\n\"109\",\"oz\"\n\"109\",\"salat\"\n\"109\",\"meteor-publications\"\n\"109\",\"fairplay\"\n\"109\",\"icinga2\"\n\"109\",\"cakephp-3.1\"\n\"109\",\"recursion-schemes\"\n\"109\",\"mysql-variables\"\n\"109\",\"methodhandle\"\n\"109\",\"hyperterminal\"\n\"109\",\"html.textboxfor\"\n\"109\",\"popcornjs\"\n\"109\",\"android-xmlpullparser\"\n\"109\",\"spring-context\"\n\"109\",\"gtmetrix\"\n\"109\",\"openfeint\"\n\"109\",\"scala-2.13\"\n\"109\",\"info-plist\"\n\"109\",\"android-vpn-service\"\n\"109\",\"opencv-solvepnp\"\n\"109\",\"twitterkit\"\n\"109\",\"rinside\"\n\"109\",\"formsauthentication\"\n\"109\",\"wing-ide\"\n\"109\",\"brython\"\n\"109\",\"eyeshot\"\n\"109\",\"py-datatable\"\n\"109\",\"pyrevit\"\n\"109\",\"rasterize\"\n\"109\",\"async-pipe\"\n\"109\",\"forgerock\"\n\"109\",\"type-signature\"\n\"109\",\"vertx-eventbus\"\n\"109\",\"xcode9.4\"\n\"109\",\"openpop\"\n\"109\",\"xcode11.3\"\n\"109\",\"xcode11.4\"\n\"109\",\"time-wait\"\n\"109\",\"cadence\"\n\"109\",\"openrewrite\"\n\"109\",\"drools-kie-server\"\n\"109\",\"bz2\"\n\"109\",\"dstream\"\n\"109\",\"mixitup\"\n\"109\",\"hibernate-native-query\"\n\"109\",\"qvtkwidget\"\n\"109\",\"asp.net-web-api-helppages\"\n\"109\",\"stripslashes\"\n\"109\",\"cfdocument\"\n\"109\",\"mercurial-extension\"\n\"109\",\"lark-parser\"\n\"109\",\"laravel-medialibrary\"\n\"109\",\"meta-key\"\n\"109\",\"prolog-dif\"\n\"109\",\"challenge-response\"\n\"109\",\"lsmeans\"\n\"109\",\"mp4box\"\n\"109\",\"getcwd\"\n\"109\",\"esi\"\n\"109\",\"nodegit\"\n\"109\",\"longest-path\"\n\"109\",\"angular-calendar\"\n\"109\",\"react-markdown\"\n\"109\",\"currency-exchange-rates\"\n\"109\",\"specs\"\n\"109\",\"generic-foreign-key\"\n\"109\",\"global-scope\"\n\"109\",\"yubikey\"\n\"109\",\"qt5.7\"\n\"109\",\"secure-gateway\"\n\"109\",\"powermanager\"\n\"109\",\"array-multisort\"\n\"109\",\"quantile-regression\"\n\"109\",\"maven-module\"\n\"109\",\"bcrypt-ruby\"\n\"109\",\"cvx\"\n\"109\",\"artillery\"\n\"109\",\"touchxml\"\n\"109\",\"tidygraph\"\n\"108\",\"cmsamplebuffer\"\n\"108\",\"multiple-dispatch\"\n\"108\",\"wdio\"\n\"108\",\"flutter-form-builder\"\n\"108\",\"treemodel\"\n\"108\",\"git-bisect\"\n\"108\",\"econnrefused\"\n\"108\",\"apacheignite\"\n\"108\",\"wicked-gem\"\n\"108\",\"clojure-contrib\"\n\"108\",\"musicbrainz\"\n\"108\",\"trimesh\"\n\"108\",\"fido-u2f\"\n\"108\",\"eda\"\n\"108\",\"yasnippet\"\n\"108\",\"closest-points\"\n\"108\",\"frege\"\n\"108\",\"carrot2\"\n\"108\",\"bisect\"\n\"108\",\"flask-mysql\"\n\"108\",\"uncaughtexceptionhandler\"\n\"108\",\"oxygene\"\n\"108\",\"firefox-marionette\"\n\"108\",\"fl-chart\"\n\"108\",\"congestion-control\"\n\"108\",\"pythoncom\"\n\"108\",\"adaptive-layout\"\n\"108\",\"kentor-authservices\"\n\"108\",\"vue-transitions\"\n\"108\",\"windows-search\"\n\"108\",\"aggregator\"\n\"108\",\"johnsnowlabs-spark-nlp\"\n\"108\",\"crossover\"\n\"108\",\"candy-machine\"\n\"108\",\"options-menu\"\n\"108\",\"gradle-experimental\"\n\"108\",\"uppy\"\n\"108\",\"mysqldatareader\"\n\"108\",\"hyperledger-explorer\"\n\"108\",\"bpmn.io\"\n\"108\",\"easyhook\"\n\"108\",\"ndimage\"\n\"108\",\"html5-template\"\n\"108\",\"kademlia\"\n\"108\",\"errorprovider\"\n\"108\",\"julia-plots\"\n\"108\",\"gulp-inject\"\n\"108\",\"viewstub\"\n\"108\",\"microsoft-planner\"\n\"108\",\"java-service-wrapper\"\n\"108\",\"rich-internet-application\"\n\"108\",\"go-interface\"\n\"108\",\"sigaction\"\n\"108\",\"wma\"\n\"108\",\"viewparams\"\n\"108\",\"microsoft-information-protection\"\n\"108\",\"vis.js-timeline\"\n\"108\",\"ext2\"\n\"108\",\"pytest-html\"\n\"108\",\"downtime\"\n\"108\",\"gd2\"\n\"108\",\"notion\"\n\"108\",\"isis\"\n\"108\",\"mlmodel\"\n\"108\",\"assembly-loading\"\n\"108\",\"npm-live-server\"\n\"108\",\"numerical-stability\"\n\"108\",\"hierarchical-bayesian\"\n\"108\",\"device-detection\"\n\"108\",\"tango\"\n\"108\",\"mlt\"\n\"108\",\"http-status-code-422\"\n\"108\",\"acronym\"\n\"108\",\"mozilla-deepspeech\"\n\"108\",\"ltree\"\n\"108\",\"laravel-resource\"\n\"108\",\"angular7-router\"\n\"108\",\"elasticsearch-hadoop\"\n\"108\",\"messageformat\"\n\"108\",\"android-lru-cache\"\n\"108\",\"propertychangelistener\"\n\"108\",\"getprocaddress\"\n\"108\",\"sphero-api\"\n\"108\",\"pffile\"\n\"108\",\"hc-05\"\n\"108\",\"amazon-kcl\"\n\"108\",\"arduino-yun\"\n\"108\",\"encodable\"\n\"108\",\"autovacuum\"\n\"108\",\"bessel-functions\"\n\"108\",\"predicates\"\n\"108\",\"successor-arithmetics\"\n\"108\",\"zoho-deluge\"\n\"108\",\"studio3t\"\n\"108\",\"linq.js\"\n\"108\",\"argo\"\n\"107\",\"telerik-appbuilder\"\n\"107\",\"program-counter\"\n\"107\",\"jdi\"\n\"107\",\"mutual-recursion\"\n\"107\",\"feature-descriptor\"\n\"107\",\"sql-workbench-j\"\n\"107\",\"matlab-gui\"\n\"107\",\"ggbiplot\"\n\"107\",\"ape\"\n\"107\",\"vs-community-edition\"\n\"107\",\"pageable\"\n\"107\",\"rx-kotlin2\"\n\"107\",\"software-packaging\"\n\"107\",\"xml-dsig\"\n\"107\",\"advanced-filter\"\n\"107\",\"vmware-tools\"\n\"107\",\"next-images\"\n\"107\",\"safe-mode\"\n\"107\",\"python-black\"\n\"107\",\"page-size\"\n\"107\",\"sakai\"\n\"107\",\"uninitialized-constant\"\n\"107\",\"readonly-collection\"\n\"107\",\"windows-live\"\n\"107\",\"keyboard-input\"\n\"107\",\"meta-query\"\n\"107\",\"validating\"\n\"107\",\"camanjs\"\n\"107\",\"surrealdb\"\n\"107\",\"delete-directory\"\n\"107\",\"enqueue\"\n\"107\",\"boundfield\"\n\"107\",\"pdfptable\"\n\"107\",\"juniper\"\n\"107\",\"sarama\"\n\"107\",\"kaldi\"\n\"107\",\"ion-slides\"\n\"107\",\"jasny-bootstrap\"\n\"107\",\"codecov\"\n\"107\",\"r-future\"\n\"107\",\"orange-pi\"\n\"107\",\"magickwand\"\n\"107\",\"visualstates\"\n\"107\",\"tabbing\"\n\"107\",\"ambiguous-call\"\n\"107\",\"forums\"\n\"107\",\"shadowjar\"\n\"107\",\"android-authenticator\"\n\"107\",\"go-html-template\"\n\"107\",\"visual-tree\"\n\"107\",\"shaka\"\n\"107\",\"external-project\"\n\"107\",\"rfc3339\"\n\"107\",\"javapoet\"\n\"107\",\"bucket-sort\"\n\"107\",\"system.management\"\n\"107\",\"libjpeg-turbo\"\n\"107\",\"virus-scanning\"\n\"107\",\"openvz\"\n\"107\",\"sqlproj\"\n\"107\",\"cookiecontainer\"\n\"107\",\"wxtextctrl\"\n\"107\",\"xcode-command-line-tools\"\n\"107\",\"uicollectionviewdelegate\"\n\"107\",\"novacode-docx\"\n\"107\",\"azure-hybrid-connections\"\n\"107\",\"openproject\"\n\"107\",\"dotty\"\n\"107\",\"tinyxml2\"\n\"107\",\"r2dbc-postgresql\"\n\"107\",\"xamlreader\"\n\"107\",\"drawing2d\"\n\"107\",\"android-implicit-intent\"\n\"107\",\"jamstack\"\n\"107\",\"c++-templates\"\n\"107\",\"react-google-maps-api\"\n\"107\",\"office-app\"\n\"107\",\"lokijs\"\n\"107\",\"cfstring\"\n\"107\",\"pgcrypto\"\n\"107\",\"on-demand-resources\"\n\"107\",\"cf-bosh\"\n\"107\",\"qlik-expression\"\n\"107\",\"lockfile\"\n\"107\",\"spinnaker-halyard\"\n\"107\",\"multer-gridfs-storage\"\n\"107\",\"parallelism-amdahl\"\n\"107\",\"transfer-function\"\n\"107\",\"daemons\"\n\"107\",\"ford-fulkerson\"\n\"107\",\"v4l2loopback\"\n\"107\",\"glossary\"\n\"107\",\"seccomp\"\n\"107\",\"linklabel\"\n\"107\",\"amazon-echo\"\n\"107\",\"embedded-video\"\n\"107\",\"usergrid\"\n\"107\",\"linkageerror\"\n\"107\",\"google-play-integrity-api\"\n\"107\",\"gnu-prolog\"\n\"106\",\"web-user-controls\"\n\"106\",\"github-webhook\"\n\"106\",\"coc.nvim\"\n\"106\",\"trustzone\"\n\"106\",\"installshield-2010\"\n\"106\",\"cloudfiles\"\n\"106\",\"slidingpanelayout\"\n\"106\",\"filefilter\"\n\"106\",\"class-table-inheritance\"\n\"106\",\"x-ray\"\n\"106\",\"jde\"\n\"106\",\"jcarousellite\"\n\"106\",\"mumin\"\n\"106\",\"xdocreport\"\n\"106\",\"mappedsuperclass\"\n\"106\",\"fsync\"\n\"106\",\"python-fu\"\n\"106\",\"console-output\"\n\"106\",\"unison\"\n\"106\",\"apollostack\"\n\"106\",\"smarty2\"\n\"106\",\"connect-flash\"\n\"106\",\"freopen\"\n\"106\",\"python-object\"\n\"106\",\"sef\"\n\"106\",\"doevents\"\n\"106\",\"jimp\"\n\"106\",\"rn-fetch-blob\"\n\"106\",\"unobtrusive-ajax\"\n\"106\",\"singleton-type\"\n\"106\",\"ib-api\"\n\"106\",\"server.transfer\"\n\"106\",\"realbasic\"\n\"106\",\"docblocks\"\n\"106\",\"server-action\"\n\"106\",\"warehouse\"\n\"106\",\"azure-digital-twins\"\n\"106\",\"nsresponder\"\n\"106\",\"cornerstone\"\n\"106\",\"android-sms\"\n\"106\",\"twitter-search\"\n\"106\",\"patsy\"\n\"106\",\"bootstrap-material-design\"\n\"106\",\"openaiembeddings\"\n\"106\",\"nestjs-passport\"\n\"106\",\"grunt-contrib-connect\"\n\"106\",\"infovis\"\n\"106\",\"application-error\"\n\"106\",\"apple-wallet\"\n\"106\",\"gst-launch\"\n\"106\",\"spring-boot-devtools\"\n\"106\",\"spring-security-saml2\"\n\"106\",\"android-account\"\n\"106\",\"codeigniter-form-helper\"\n\"106\",\"typescript1.6\"\n\"106\",\"shinyproxy\"\n\"106\",\"systray\"\n\"106\",\"doobie\"\n\"106\",\"wordpress-admin\"\n\"106\",\"javax.validation\"\n\"106\",\"kruskal-wallis\"\n\"106\",\"shrinkwrap\"\n\"106\",\"pyhive\"\n\"106\",\"android-build-type\"\n\"106\",\"tlv\"\n\"106\",\"hipaa\"\n\"106\",\"bull\"\n\"106\",\"springsource\"\n\"106\",\"explicit-conversion\"\n\"106\",\"handwriting\"\n\"106\",\"quoted-printable\"\n\"106\",\"express-4\"\n\"106\",\"hivecontext\"\n\"106\",\"expdp\"\n\"106\",\"rabin-karp\"\n\"106\",\"nvim-lspconfig\"\n\"106\",\"redmine-api\"\n\"106\",\"xcode-project\"\n\"106\",\"gameboy\"\n\"106\",\"xauth\"\n\"106\",\"scipy-spatial\"\n\"106\",\"nicescroll\"\n\"106\",\"large-file-upload\"\n\"106\",\"qdatastream\"\n\"106\",\"stretching\"\n\"106\",\"angular-highcharts\"\n\"106\",\"qheaderview\"\n\"106\",\"android-jetpack-compose-list\"\n\"106\",\"eip\"\n\"106\",\"speed-test\"\n\"106\",\"memory-fences\"\n\"106\",\"getgauge\"\n\"106\",\"locomotive-scroll\"\n\"106\",\"compound-assignment\"\n\"106\",\"utilization\"\n\"106\",\"sparkle\"\n\"106\",\"automapper-5\"\n\"106\",\"spark-kafka-integration\"\n\"106\",\"sdcc\"\n\"106\",\"transcode\"\n\"106\",\"automatic-license-plate-recognition\"\n\"106\",\"dartium\"\n\"106\",\"tracelistener\"\n\"106\",\"imagekit\"\n\"106\",\"text-widget\"\n\"106\",\"linear-discriminant\"\n\"105\",\"vt100\"\n\"105\",\"tsd\"\n\"105\",\"matlab-class\"\n\"105\",\"git-difftool\"\n\"105\",\"feed-forward\"\n\"105\",\"cls-compliant\"\n\"105\",\"graph-drawing\"\n\"105\",\"graphicimage\"\n\"105\",\"apache-chemistry\"\n\"105\",\"graphql-dotnet\"\n\"105\",\"python-install\"\n\"105\",\"aether\"\n\"105\",\"ultraedit\"\n\"105\",\"fixest\"\n\"105\",\"cinema-4d\"\n\"105\",\"xodus\"\n\"105\",\"fileopendialog\"\n\"105\",\"mail-sender\"\n\"105\",\"packagereference\"\n\"105\",\"fts4\"\n\"105\",\"snowpack\"\n\"105\",\"cdr\"\n\"105\",\"soci\"\n\"105\",\"switchcompat\"\n\"105\",\"aweber\"\n\"105\",\"windows64\"\n\"105\",\"callouts\"\n\"105\",\"vuex4\"\n\"105\",\"i18n-gem\"\n\"105\",\"aws-iam-policy\"\n\"105\",\"serverspec\"\n\"105\",\"sitecore9\"\n\"105\",\"windev\"\n\"105\",\"faust\"\n\"105\",\"icloud-api\"\n\"105\",\"django-redis\"\n\"105\",\"boost-process\"\n\"105\",\"pdfstamper\"\n\"105\",\"boost-range\"\n\"105\",\"enterprisedb\"\n\"105\",\"http3\"\n\"105\",\"deleted-functions\"\n\"105\",\"sas-studio\"\n\"105\",\"android-wrap-content\"\n\"105\",\"craftyjs\"\n\"105\",\"createcriteria\"\n\"105\",\"sap-pi\"\n\"105\",\"mongoose-im\"\n\"105\",\"hpple\"\n\"105\",\"objectdb\"\n\"105\",\"rich\"\n\"105\",\"new-project\"\n\"105\",\"object-construction\"\n\"105\",\"atg-dynamo\"\n\"105\",\"pytesser\"\n\"105\",\"facebooker\"\n\"105\",\"sigar\"\n\"105\",\"bsp\"\n\"105\",\"dongle\"\n\"105\",\"ats\"\n\"105\",\"rclone\"\n\"105\",\"xapian\"\n\"105\",\"tkinter-label\"\n\"105\",\"scotty\"\n\"105\",\"sqlfilestream\"\n\"105\",\"hgrc\"\n\"105\",\"number-systems\"\n\"105\",\"bytearrayinputstream\"\n\"105\",\"ml-agent\"\n\"105\",\"podscms\"\n\"105\",\"cookie-authentication\"\n\"105\",\"numpy-memmap\"\n\"105\",\"coqide\"\n\"105\",\"stompjs\"\n\"105\",\"espeak\"\n\"105\",\"ldapconnection\"\n\"105\",\"event-dispatching\"\n\"105\",\"office-ui-fabric-react\"\n\"105\",\"iris-dataset\"\n\"105\",\"metafile\"\n\"105\",\"cgpdfdocument\"\n\"105\",\"exc-bad-instruction\"\n\"105\",\"ole-automation\"\n\"105\",\"resharper-8.0\"\n\"105\",\"textmeshpro\"\n\"105\",\"mosek\"\n\"105\",\"terraform-cloud\"\n\"105\",\"react-android\"\n\"105\",\"stty\"\n\"105\",\"dam\"\n\"105\",\"url-validation\"\n\"105\",\"auto-renewable\"\n\"105\",\"tidb\"\n\"105\",\"architectural-patterns\"\n\"105\",\"bcmath\"\n\"105\",\"fody-costura\"\n\"104\",\"gridviewcolumn\"\n\"104\",\"matrix-decomposition\"\n\"104\",\"background-attachment\"\n\"104\",\"repa\"\n\"104\",\"eflags\"\n\"104\",\"git-credential-manager\"\n\"104\",\"ssl-client-authentication\"\n\"104\",\"phinx\"\n\"104\",\"php-amqplib\"\n\"104\",\"phong\"\n\"104\",\"relu\"\n\"104\",\"procrun\"\n\"104\",\"datetime-comparison\"\n\"104\",\"symfony-process\"\n\"104\",\"volume-rendering\"\n\"104\",\"rweka\"\n\"104\",\"cbor\"\n\"104\",\"safetynet\"\n\"104\",\"xgettext\"\n\"104\",\"sw-precache\"\n\"104\",\"nginfinitescroll\"\n\"104\",\"pivotviewer\"\n\"104\",\"flask-peewee\"\n\"104\",\"pikaday\"\n\"104\",\"adlds\"\n\"104\",\"casl\"\n\"104\",\"ng2-file-upload\"\n\"104\",\"microblaze\"\n\"104\",\"roo\"\n\"104\",\"jira-zephyr\"\n\"104\",\"airbrake\"\n\"104\",\"ora-01722\"\n\"104\",\"angular-kendo\"\n\"104\",\"metaspace\"\n\"104\",\"akka-supervision\"\n\"104\",\"wcftestclient\"\n\"104\",\"psycopg3\"\n\"104\",\"jqassistant\"\n\"104\",\"capnproto\"\n\"104\",\"sinatra-activerecord\"\n\"104\",\"psake\"\n\"104\",\"servicecontroller\"\n\"104\",\"simperium\"\n\"104\",\"dyndns\"\n\"104\",\"justmock\"\n\"104\",\"sas-token\"\n\"104\",\"mongoid4\"\n\"104\",\"android-wear-notification\"\n\"104\",\"post-install\"\n\"104\",\"htmltext\"\n\"104\",\"aws-sdk-ruby\"\n\"104\",\"guice-3\"\n\"104\",\"passport-azure-ad\"\n\"104\",\"horizontal-line\"\n\"104\",\"wordpress-thesis-theme\"\n\"104\",\"wordsearch\"\n\"104\",\"bpl\"\n\"104\",\"onreadystatechange\"\n\"104\",\"bq\"\n\"104\",\"coin-or-cbc\"\n\"104\",\"orphan\"\n\"104\",\"wns\"\n\"104\",\"java-13\"\n\"104\",\"pysvn\"\n\"104\",\"return-code\"\n\"104\",\"cocos2d-x-2.x\"\n\"104\",\"net-sftp\"\n\"104\",\"krakend\"\n\"104\",\"donations\"\n\"104\",\"attr-accessible\"\n\"104\",\"ou\"\n\"104\",\"wix3.9\"\n\"104\",\"facebook-credits\"\n\"104\",\"sql-convert\"\n\"104\",\"home-button\"\n\"104\",\"tcserver\"\n\"104\",\"azure-servicebusrelay\"\n\"104\",\"sql-mode\"\n\"104\",\"hkhealthstore\"\n\"104\",\"model-comparison\"\n\"104\",\"aspnetdb\"\n\"104\",\"uicontrolevents\"\n\"104\",\"gwt-maven-plugin\"\n\"104\",\"hiccup\"\n\"104\",\"nsusernotification\"\n\"104\",\"open-with\"\n\"104\",\"redhat-containers\"\n\"104\",\"requesthandler\"\n\"104\",\"hungarian-algorithm\"\n\"104\",\"angularfire5\"\n\"104\",\"tomtom\"\n\"104\",\"chaos\"\n\"104\",\"geoxml3\"\n\"104\",\"android-number-picker\"\n\"104\",\"flwor\"\n\"104\",\"ms-yarp\"\n\"104\",\"prefab\"\n\"104\",\"stem\"\n\"104\",\"sharpssh\"\n\"104\",\"git-tower\"\n\"104\",\"benchmarkdotnet\"\n\"104\",\"powerview\"\n\"104\",\"amazon-qldb\"\n\"104\",\"sonarlint-eclipse\"\n\"104\",\"spanning-tree\"\n\"104\",\"max-msp-jitter\"\n\"104\",\"webmatrix-2\"\n\"103\",\"editmode\"\n\"103\",\"base64url\"\n\"103\",\"pgzero\"\n\"103\",\"efficientnet\"\n\"103\",\"php-di\"\n\"103\",\"ghostscript.net\"\n\"103\",\"widget-test-flutter\"\n\"103\",\"greasemonkey-4\"\n\"103\",\"lms\"\n\"103\",\"mutablelist\"\n\"103\",\"webresource\"\n\"103\",\"ggtern\"\n\"103\",\"php-deployer\"\n\"103\",\"yandex-api\"\n\"103\",\"flutter-dropdownbutton\"\n\"103\",\"photoimage\"\n\"103\",\"ecs-taskdefinition\"\n\"103\",\"flask-marshmallow\"\n\"103\",\"soc\"\n\"103\",\"ng-init\"\n\"103\",\"plack\"\n\"103\",\"file-properties\"\n\"103\",\"fitness\"\n\"103\",\"display-templates\"\n\"103\",\"immediate-operand\"\n\"103\",\"datetime64\"\n\"103\",\"admin-rights\"\n\"103\",\"cdecl\"\n\"103\",\"circlize\"\n\"103\",\"googleway\"\n\"103\",\"database-agnostic\"\n\"103\",\"django-modeltranslation\"\n\"103\",\"routines\"\n\"103\",\"serviceconnection\"\n\"103\",\"dmn\"\n\"103\",\"sharepointframework\"\n\"103\",\"key-generator\"\n\"103\",\"meter\"\n\"103\",\"ibm-cloud-tools\"\n\"103\",\"gopath\"\n\"103\",\"roots-sage\"\n\"103\",\"hyperjaxb\"\n\"103\",\"dm\"\n\"103\",\"azure-devops-server-2020\"\n\"103\",\"spring-boot-configuration\"\n\"103\",\"enumset\"\n\"103\",\"position-independent-code\"\n\"103\",\"jquery-countdown\"\n\"103\",\"mongolite\"\n\"103\",\"nscopying\"\n\"103\",\"nats-streaming-server\"\n\"103\",\"jquerydatetimepicker\"\n\"103\",\"jquery-dynatree\"\n\"103\",\"deinit\"\n\"103\",\"extension-function\"\n\"103\",\"wolfram-language\"\n\"103\",\"google-chrome-frame\"\n\"103\",\"luaj\"\n\"103\",\"java.lang.class\"\n\"103\",\"osmf\"\n\"103\",\"object-relational-model\"\n\"103\",\"microsoft-reporting\"\n\"103\",\"synthetic\"\n\"103\",\"word-2013\"\n\"103\",\"external-links\"\n\"103\",\"uber-cadence\"\n\"103\",\"google-anthos\"\n\"103\",\"retrolambda\"\n\"103\",\"luminus\"\n\"103\",\"tds\"\n\"103\",\"tipsy\"\n\"103\",\"azure-pipelines-release-task\"\n\"103\",\"uiautomatorviewer\"\n\"103\",\"busy-waiting\"\n\"103\",\"expressionbuilder\"\n\"103\",\"tclsh\"\n\"103\",\"export-to-word\"\n\"103\",\"tapku\"\n\"103\",\"diameter-protocol\"\n\"103\",\"higher-rank-types\"\n\"103\",\"request-uri\"\n\"103\",\"colorfilter\"\n\"103\",\"android-jetpack-compose-canvas\"\n\"103\",\"csvtojson\"\n\"103\",\"esri-maps\"\n\"103\",\"httpie\"\n\"103\",\"angular18\"\n\"103\",\"angular-hybrid\"\n\"103\",\"logoff\"\n\"103\",\"huggingface-trainer\"\n\"103\",\"promise.all\"\n\"103\",\"pyuic\"\n\"103\",\"google-code-prettify\"\n\"103\",\"combiners\"\n\"103\",\"ejabberd-api\"\n\"103\",\"laravel-session\"\n\"103\",\"ids\"\n\"103\",\"identity-insert\"\n\"103\",\"array-agg\"\n\"103\",\"security-context\"\n\"103\",\"zend-cache\"\n\"103\",\"embeddedwebserver\"\n\"103\",\"asdf-vm\"\n\"103\",\"idfa\"\n\"103\",\"amazon-personalize\"\n\"103\",\"zero-copy\"\n\"103\",\"web-extension\"\n\"103\",\"biginsights\"\n\"103\",\"lightopenid\"\n\"103\",\"flutter-platform-channel\"\n\"103\",\"gloss\"\n\"103\",\"qtconsole\"\n\"103\",\"gkturnbasedmatch\"\n\"103\",\"lifetime-scoping\"\n\"103\",\"git-repo\"\n\"103\",\"submitchanges\"\n\"103\",\"foldleft\"\n\"103\",\"google-shopping-api\"\n\"102\",\"multi-master-replication\"\n\"102\",\"jcr-sql2\"\n\"102\",\"ggtree\"\n\"102\",\"eclim\"\n\"102\",\"xval\"\n\"102\",\"phoenix-channels\"\n\"102\",\"trigram\"\n\"102\",\"sslcontext\"\n\"102\",\"multiple-resultsets\"\n\"102\",\"grinder\"\n\"102\",\"installed-applications\"\n\"102\",\"clrstoredprocedure\"\n\"102\",\"truthiness\"\n\"102\",\"class-extensions\"\n\"102\",\"filesort\"\n\"102\",\"sails-postgresql\"\n\"102\",\"adx\"\n\"102\",\"rivets.js\"\n\"102\",\"sendasynchronousrequest\"\n\"102\",\"p4python\"\n\"102\",\"ui-virtualization\"\n\"102\",\"aeron\"\n\"102\",\"python-datamodel\"\n\"102\",\"s4hana\"\n\"102\",\"consteval\"\n\"102\",\"xpo\"\n\"102\",\"circleci-workflows\"\n\"102\",\"datamaps\"\n\"102\",\"cinnamon\"\n\"102\",\"data-security\"\n\"102\",\"crosstalk\"\n\"102\",\"go-reflect\"\n\"102\",\"rebol2\"\n\"102\",\"johnny-five\"\n\"102\",\"rotateanimation\"\n\"102\",\"microsoft-bits\"\n\"102\",\"kendo-listview\"\n\"102\",\"rowcommand\"\n\"102\",\"simple-peer\"\n\"102\",\"password-less\"\n\"102\",\"sar\"\n\"102\",\"android-twitter\"\n\"102\",\"epicorerp\"\n\"102\",\"openexr\"\n\"102\",\"dymo\"\n\"102\",\"nscharacterset\"\n\"102\",\"boost-optional\"\n\"102\",\"approximate\"\n\"102\",\"inherited\"\n\"102\",\"scala-2.9\"\n\"102\",\"neomodel\"\n\"102\",\"silktest\"\n\"102\",\"jquery-calculation\"\n\"102\",\"netbsd\"\n\"102\",\"codewarrior\"\n\"102\",\"retrypolicy\"\n\"102\",\"video-embedding\"\n\"102\",\"raty\"\n\"102\",\"visual-studio-6\"\n\"102\",\"r-bigmemory\"\n\"102\",\"system-information\"\n\"102\",\"code-sharing\"\n\"102\",\"abstract-type\"\n\"102\",\"razorgenerator\"\n\"102\",\"scalaz7\"\n\"102\",\"plug\"\n\"102\",\"sqlalchemy-migrate\"\n\"102\",\"talend-mdm\"\n\"102\",\"sqlcipher-android\"\n\"102\",\"tinyint\"\n\"102\",\"gcdwebserver\"\n\"102\",\"gdbus\"\n\"102\",\"redefine\"\n\"102\",\"openpdf\"\n\"102\",\"android-dialer\"\n\"102\",\"table-locking\"\n\"102\",\"common-controls\"\n\"102\",\"reactivesearch\"\n\"102\",\"stream-socket-client\"\n\"102\",\"testcontainers-junit5\"\n\"102\",\"node-amqp\"\n\"102\",\"cubic\"\n\"102\",\"string-function\"\n\"102\",\"mpv\"\n\"102\",\"customizing\"\n\"102\",\"texteditingcontroller\"\n\"102\",\"activedirectorymembership\"\n\"102\",\"maven-ear-plugin\"\n\"102\",\"webkit-transform\"\n\"102\",\"alternating\"\n\"102\",\"quickbase\"\n\"102\",\"linkerd\"\n\"102\",\"shdocvw\"\n\"102\",\"fly\"\n\"102\",\"traceur\"\n\"102\",\"git-stage\"\n\"102\",\"qt-jambi\"\n\"102\",\"ms-project-server-2013\"\n\"102\",\"cypress-custom-commands\"\n\"102\",\"autolink\"\n\"102\",\"pass-by-pointer\"\n\"102\",\"glassfish-embedded\"\n\"102\",\"powerline\"\n\"102\",\"alpacajs\"\n\"101\",\"massive\"\n\"101\",\"staggeredgridlayout\"\n\"101\",\"markov-models\"\n\"101\",\"grommet\"\n\"101\",\"annotation-processor\"\n\"101\",\"ghcjs\"\n\"101\",\"defaultmodelbinder\"\n\"101\",\"cloudpebble\"\n\"101\",\"gettimeofday\"\n\"101\",\"file-listing\"\n\"101\",\"distinct-on\"\n\"101\",\"select-options\"\n\"101\",\"appframework\"\n\"101\",\"vorbis\"\n\"101\",\"rx-binding\"\n\"101\",\"pythonista\"\n\"101\",\"swoole\"\n\"101\",\"nexus-s\"\n\"101\",\"marie\"\n\"101\",\"django-extensions\"\n\"101\",\"cryptographic-hash-function\"\n\"101\",\"kie-server\"\n\"101\",\"agenda\"\n\"101\",\"kendo-editor\"\n\"101\",\"tuleap\"\n\"101\",\"dependencyobject\"\n\"101\",\"surfaceflinger\"\n\"101\",\"wpd\"\n\"101\",\"typed\"\n\"101\",\"delegatecommand\"\n\"101\",\"inout\"\n\"101\",\"jython-2.5\"\n\"101\",\"android-shapedrawable\"\n\"101\",\"google-amp\"\n\"101\",\"java-package\"\n\"101\",\"ubuntu-15.10\"\n\"101\",\"form-api\"\n\"101\",\"google-app-invites\"\n\"101\",\"coff\"\n\"101\",\"ab-initio\"\n\"101\",\"ucp\"\n\"101\",\"type-variables\"\n\"101\",\"extended-properties\"\n\"101\",\"double-free\"\n\"101\",\"contenttype\"\n\"101\",\"titleview\"\n\"101\",\"execjs\"\n\"101\",\"xcconfig\"\n\"101\",\"android-graphics\"\n\"101\",\"convex-polygon\"\n\"101\",\"aspnet-regiis.exe\"\n\"101\",\"dstu2-fhir\"\n\"101\",\"dtexec\"\n\"101\",\"gae-eclipse-plugin\"\n\"101\",\"null-character\"\n\"101\",\"tombstone\"\n\"101\",\"dotcms\"\n\"101\",\"azure-scheduler\"\n\"101\",\"jama\"\n\"101\",\"character-class\"\n\"101\",\"tombstoning\"\n\"101\",\"google-cloud-source-repos\"\n\"101\",\"membase\"\n\"101\",\"meilisearch\"\n\"101\",\"android-renderscript\"\n\"101\",\"hy\"\n\"101\",\"hydra\"\n\"101\",\"huawei-push-notification\"\n\"101\",\"qscintilla\"\n\"101\",\"compareobject\"\n\"101\",\"strongly-typed-view\"\n\"101\",\"getcomputedstyle\"\n\"101\",\"node-commander\"\n\"101\",\"omxplayer\"\n\"101\",\"angular-compiler\"\n\"101\",\"celluloid\"\n\"101\",\"activity-transition\"\n\"101\",\"zstd\"\n\"101\",\"mpxj\"\n\"101\",\"amazon-dynamodb-dax\"\n\"101\",\"glasspane\"\n\"101\",\"tornado-motor\"\n\"101\",\"quantreg\"\n\"101\",\"customscrollview\"\n\"101\",\"subrepos\"\n\"101\",\"com-port\"\n\"101\",\"multidatatrigger\"\n\"101\",\"gn\"\n\"100\",\"sketchflow\"\n\"100\",\"intel-pmu\"\n\"100\",\"edge-list\"\n\"100\",\"renpy\"\n\"100\",\"apache-atlas\"\n\"100\",\"local-datastore\"\n\"100\",\"class-visibility\"\n\"100\",\"mvn-repo\"\n\"100\",\"x-xsrf-token\"\n\"100\",\"skyfield\"\n\"100\",\"editcontrol\"\n\"100\",\"webui\"\n\"100\",\"flare\"\n\"100\",\"immutables-library\"\n\"100\",\"jsobject\"\n\"100\",\"adobe-captivate\"\n\"100\",\"volume-shadow-service\"\n\"100\",\"cedit\"\n\"100\",\"sage-erp\"\n\"100\",\"chord\"\n\"100\",\"xdt-transform\"\n\"100\",\"language-theory\"\n\"100\",\"container-view\"\n\"100\",\"flask-bootstrap\"\n\"100\",\"blackberry-android\"\n\"100\",\"maintenance-plan\"\n\"100\",\"nameof\"\n\"100\",\"documentfragment\"\n\"100\",\"ora-00933\"\n\"100\",\"jmx-exporter\"\n\"100\",\"kendo-autocomplete\"\n\"100\",\"django-logging\"\n\"100\",\"gopro\"\n\"100\",\"ajax-polling\"\n\"100\",\"dmz\"\n\"100\",\"recarray\"\n\"100\",\"iboutletcollection\"\n\"100\",\"carddav\"\n\"100\",\"simplecart\"\n\"100\",\"dep\"\n\"100\",\"bpopup\"\n\"100\",\"cpputest\"\n\"100\",\"postgres-9.6\"\n\"100\",\"jrules\"\n\"100\",\"jrockit\"\n\"100\",\"corona-storyboard\"\n\"100\",\"onlyoffice\"\n\"100\",\"postgresql-9.0\"\n\"100\",\"guice-servlet\"\n\"100\",\"border-box\"\n\"100\",\"ws-addressing\"\n\"100\",\"nsoutputstream\"\n\"100\",\"inherited-resources\"\n\"100\",\"wsp\"\n\"100\",\"http.client\"\n\"100\",\"apple-developer-account\"\n\"100\",\"viper-go\"\n\"100\",\"system.xml\"\n\"100\",\"3gp\"\n\"100\",\"missing-features\"\n\"100\",\"mahalanobis\"\n\"100\",\"netapp\"\n\"100\",\"otree\"\n\"100\",\"shadows\"\n\"100\",\"typesetting\"\n\"100\",\"kogito\"\n\"100\",\"formvalidation.io\"\n\"100\",\"returnurl\"\n\"100\",\"netflow\"\n\"100\",\"time-tracking\"\n\"100\",\"target-framework\"\n\"100\",\"scopus\"\n\"100\",\"sqlite.net\"\n\"100\",\"deviation\"\n\"100\",\"tcmalloc\"\n\"100\",\"gacutil\"\n\"100\",\"gadfly\"\n\"100\",\"assembly-signing\"\n\"100\",\"mkpointannotation\"\n\"100\",\"xceed-datagrid\"\n\"100\",\"tablet-pc\"\n\"100\",\"uisplitview\"\n\"100\",\"galleryview\"\n\"100\",\"non-printing-characters\"\n\"100\",\"mercure\"\n\"100\",\"specrun\"\n\"100\",\"android-resolution\"\n\"100\",\"mpd\"\n\"100\",\"duplicate-symbol\"\n\"100\",\"memory-leak-detector\"\n\"100\",\"elasticsearch-curator\"\n\"100\",\"quest\"\n\"100\",\"use-strict\"\n\"100\",\"start-stop-daemon\"\n\"100\",\"gluonfx\"\n\"100\",\"webflux\"\n\"100\",\"stardog\"\n\"100\",\"concurrent-collections\"\n\"100\",\"forced-unwrapping\"\n\"100\",\"parametric-equations\"\n\"100\",\"webpack-dev-middleware\"\n\"100\",\"webpack-html-loader\"\n\"100\",\"powerpoint-addins\"\n\"100\",\"zarr\"\n\"100\",\"questasim\"\n\"99\",\"background-mode\"\n\"99\",\"ggproto\"\n\"99\",\"react-relay\"\n\"99\",\"git-detached-head\"\n\"99\",\"maven-bom\"\n\"99\",\"photography\"\n\"99\",\"php-ews\"\n\"99\",\"yarn-lock.json\"\n\"99\",\"cmtime\"\n\"99\",\"mybatis-generator\"\n\"99\",\"mathjs\"\n\"99\",\"client-side-templating\"\n\"99\",\"listgrid\"\n\"99\",\"ecm\"\n\"99\",\"eclipse-gmf\"\n\"99\",\"grid-computing\"\n\"99\",\"multi-tier\"\n\"99\",\"reloading\"\n\"99\",\"paintcode\"\n\"99\",\"affdex-sdk\"\n\"99\",\"ng-content\"\n\"99\",\"xfs\"\n\"99\",\"mapdb\"\n\"99\",\"advertisement-server\"\n\"99\",\"sentinel2\"\n\"99\",\"django-auth-ldap\"\n\"99\",\"rstatix\"\n\"99\",\"crittercism\"\n\"99\",\"fastlane-match\"\n\"99\",\"vagrant-plugin\"\n\"99\",\"false-sharing\"\n\"99\",\"aws-codestar\"\n\"99\",\"unitywebrequest\"\n\"99\",\"rmongodb\"\n\"99\",\"aif\"\n\"99\",\"oracle-sql-data-modeler\"\n\"99\",\"microsoft-dynamics-nav\"\n\"99\",\"nscollectionviewitem\"\n\"99\",\"kcachegrind\"\n\"99\",\"cpp-core-guidelines\"\n\"99\",\"android-textureview\"\n\"99\",\"nested-repeater\"\n\"99\",\"postgresql-json\"\n\"99\",\"sigpipe\"\n\"99\",\"pax\"\n\"99\",\"pdftools\"\n\"99\",\"application-state\"\n\"99\",\"satellite-assembly\"\n\"99\",\"bluesnap\"\n\"99\",\"app-update\"\n\"99\",\"entitygraph\"\n\"99\",\"entity-sql\"\n\"99\",\"android-binding-adapter\"\n\"99\",\"pysolr\"\n\"99\",\"winzip\"\n\"99\",\"extending-classes\"\n\"99\",\"word-interop\"\n\"99\",\"obsidian\"\n\"99\",\"output-redirect\"\n\"99\",\"orchardcms-1.9\"\n\"99\",\"atmega32\"\n\"99\",\"ublas\"\n\"99\",\"systemd-journald\"\n\"99\",\"object-composition\"\n\"99\",\"return-by-reference\"\n\"99\",\"devil\"\n\"99\",\"nvme\"\n\"99\",\"nvelocity\"\n\"99\",\"c4\"\n\"99\",\"nodeunit\"\n\"99\",\"caemitterlayer\"\n\"99\",\"novell\"\n\"99\",\"excel-udf\"\n\"99\",\"mod-auth-openidc\"\n\"99\",\"tmlanguage\"\n\"99\",\"tmpfs\"\n\"99\",\"quine\"\n\"99\",\"modeless\"\n\"99\",\"assemblyscript\"\n\"99\",\"exim4\"\n\"99\",\"handheld\"\n\"99\",\"r2jags\"\n\"99\",\"scalaquery\"\n\"99\",\"gdcm\"\n\"99\",\"expandablerecyclerview\"\n\"99\",\"leanft\"\n\"99\",\"css-mask\"\n\"99\",\"ios6-maps\"\n\"99\",\"hunchentoot\"\n\"99\",\"angularjs-bootstrap\"\n\"99\",\"monkeytalk\"\n\"99\",\"nixpkgs\"\n\"99\",\"ios9.2\"\n\"99\",\"eshell\"\n\"99\",\"qdebug\"\n\"99\",\"strchr\"\n\"99\",\"menu-items\"\n\"99\",\"protovis\"\n\"99\",\"iptv\"\n\"99\",\"qeventloop\"\n\"99\",\"suitecommerce\"\n\"99\",\"bidirectional-relation\"\n\"99\",\"imagej-macro\"\n\"99\",\"ms-solver-foundation\"\n\"99\",\"google-translator-toolkit\"\n\"99\",\"hello.js\"\n\"99\",\"zipper\"\n\"99\",\"qstackedwidget\"\n\"99\",\"autodiff\"\n\"99\",\"parametrized-testing\"\n\"99\",\"sector\"\n\"99\",\"sectionheader\"\n\"99\",\"iedriverserver\"\n\"99\",\"asiformdatarequest\"\n\"99\",\"elytron\"\n\"99\",\"tika-server\"\n\"99\",\"spacy-transformers\"\n\"99\",\"query-by-example\"\n\"99\",\"qtabbar\"\n\"99\",\"best-fit-curve\"\n\"99\",\"ildasm\"\n\"99\",\"zeus\"\n\"99\",\"topicmodels\"\n\"99\",\"ieaddon\"\n\"98\",\"jboss-mdb\"\n\"98\",\"cnc\"\n\"98\",\"ssis-2016\"\n\"98\",\"temporary-directory\"\n\"98\",\"class-constants\"\n\"98\",\"jcanvas\"\n\"98\",\"phobos\"\n\"98\",\"jest-fetch-mock\"\n\"98\",\"sliders\"\n\"98\",\"fiber\"\n\"98\",\"jettison\"\n\"98\",\"self-type\"\n\"98\",\"xenapp\"\n\"98\",\"distributed-testing\"\n\"98\",\"inappsettingskit\"\n\"98\",\"python-trio\"\n\"98\",\"xmlspy\"\n\"98\",\"language-concepts\"\n\"98\",\"jtidy\"\n\"98\",\"rust-warp\"\n\"98\",\"rust-actix\"\n\"98\",\"dmv\"\n\"98\",\"watchpoint\"\n\"98\",\"call-by-value\"\n\"98\",\"database-connectivity\"\n\"98\",\"server-response\"\n\"98\",\"jobintentservice\"\n\"98\",\"uproot\"\n\"98\",\"aws-nlb\"\n\"98\",\"airplane-mode\"\n\"98\",\"urdu\"\n\"98\",\"ajax.net\"\n\"98\",\"django-request\"\n\"98\",\"windows-live-id\"\n\"98\",\"rsvp.js\"\n\"98\",\"readwritelock\"\n\"98\",\"sbrk\"\n\"98\",\"svmlight\"\n\"98\",\"awstats\"\n\"98\",\"svnsync\"\n\"98\",\"gtk-rs\"\n\"98\",\"application-bar\"\n\"98\",\"erase-remove-idiom\"\n\"98\",\"denodo\"\n\"98\",\"workmanagers\"\n\"98\",\"gui-designer\"\n\"98\",\"dynamoose\"\n\"98\",\"http-accept-language\"\n\"98\",\"jwindow\"\n\"98\",\"android-studio-3.2\"\n\"98\",\"sas-iml\"\n\"98\",\"dwscript\"\n\"98\",\"nested-sortable\"\n\"98\",\"rawsql\"\n\"98\",\"accelerate\"\n\"98\",\"at-job\"\n\"98\",\"javax.sound.sampled\"\n\"98\",\"shapedrawable\"\n\"98\",\"domain-object\"\n\"98\",\"android-application-class\"\n\"98\",\"typedoc\"\n\"98\",\"shopify-storefront-api\"\n\"98\",\"formpanel\"\n\"98\",\"gomobile\"\n\"98\",\"network-security-groups\"\n\"98\",\"google-api-console\"\n\"98\",\"diffabledatasource\"\n\"98\",\"android-drawer\"\n\"98\",\"playwright-dotnet\"\n\"98\",\"nstoolbaritem\"\n\"98\",\"taipy\"\n\"98\",\"non-type-template-parameter\"\n\"98\",\"non-member-functions\"\n\"98\",\"dot-matrix\"\n\"98\",\"aspx-user-control\"\n\"98\",\"null-safety\"\n\"98\",\"radlistview\"\n\"98\",\"azure-web-app-for-containers\"\n\"98\",\"vesta\"\n\"98\",\"polaris\"\n\"98\",\"title-case\"\n\"98\",\"plot3d\"\n\"98\",\"redux-actions\"\n\"98\",\"double-dispatch\"\n\"98\",\"holoviz\"\n\"98\",\"janitor\"\n\"98\",\"css-reset\"\n\"98\",\"proportions\"\n\"98\",\"login-page\"\n\"98\",\"string-decoding\"\n\"98\",\"certutil\"\n\"98\",\"spring5\"\n\"98\",\"no-cache\"\n\"98\",\"cffile\"\n\"98\",\"activitynotfoundexception\"\n\"98\",\"react-lazy-load\"\n\"98\",\"projective-geometry\"\n\"98\",\"elasticsearch-indices\"\n\"98\",\"google-natural-language\"\n\"98\",\"amazon-dynamodb-local\"\n\"98\",\"argoproj\"\n\"98\",\"third-party-cookies\"\n\"98\",\"ignore-case\"\n\"98\",\"authorize.net-cim\"\n\"98\",\"flutter-gridview\"\n\"98\",\"ember-app-kit\"\n\"98\",\"glulookat\"\n\"98\",\"dapper-extensions\"\n\"97\",\"reql\"\n\"97\",\"edit-in-place\"\n\"97\",\"clregion\"\n\"97\",\"printk\"\n\"97\",\"web-scripting\"\n\"97\",\"teamviewer\"\n\"97\",\"sld\"\n\"97\",\"debugdiag\"\n\"97\",\"edmx-designer\"\n\"97\",\"anycpu\"\n\"97\",\"sizetofit\"\n\"97\",\"ydn-db\"\n\"97\",\"clsid\"\n\"97\",\"mvcc\"\n\"97\",\"econnreset\"\n\"97\",\"clique\"\n\"97\",\"slim-4\"\n\"97\",\"install-referrer\"\n\"97\",\"gridview-sorting\"\n\"97\",\"php-cs-fixer\"\n\"97\",\"graphicspath\"\n\"97\",\"locality-sensitive-hash\"\n\"97\",\"getstaticprops\"\n\"97\",\"trilateration\"\n\"97\",\"php-socket\"\n\"97\",\"flow-project\"\n\"97\",\"jemalloc\"\n\"97\",\"fire-and-forget\"\n\"97\",\"jsonnode\"\n\"97\",\"unit-conversion\"\n\"97\",\"advanced-queuing\"\n\"97\",\"snyk\"\n\"97\",\"laravel-formrequest\"\n\"97\",\"package-control\"\n\"97\",\"church-encoding\"\n\"97\",\"bindy\"\n\"97\",\"x-forwarded-for\"\n\"97\",\"rx-py\"\n\"97\",\"datamart\"\n\"97\",\"page-caching\"\n\"97\",\"data-objects\"\n\"97\",\"rundll32\"\n\"97\",\"js-beautify\"\n\"97\",\"xmldatasource\"\n\"97\",\"fann\"\n\"97\",\"call-recording\"\n\"97\",\"oraclelinux\"\n\"97\",\"sitecore8.2\"\n\"97\",\"keychainitemwrapper\"\n\"97\",\"nanoc\"\n\"97\",\"google-vr-sdk\"\n\"97\",\"jodconverter\"\n\"97\",\"servermanager\"\n\"97\",\"unmount\"\n\"97\",\"oracle-bi\"\n\"97\",\"named-entity-extraction\"\n\"97\",\"candidate-key\"\n\"97\",\"vaticle-typeql\"\n\"97\",\"grpc-dotnet\"\n\"97\",\"apprtc\"\n\"97\",\"kapacitor\"\n\"97\",\"pdfclown\"\n\"97\",\"applicationpoolidentity\"\n\"97\",\"svgpanzoom\"\n\"97\",\"crashlytics-beta\"\n\"97\",\"http.sys\"\n\"97\",\"wso2-business-process\"\n\"97\",\"openair\"\n\"97\",\"audiobuffer\"\n\"97\",\"pyexcel\"\n\"97\",\"pydroid\"\n\"97\",\"visual-editor\"\n\"97\",\"wireguard\"\n\"97\",\"format-conversion\"\n\"97\",\"klocwork\"\n\"97\",\"formsets\"\n\"97\",\"network-scan\"\n\"97\",\"wmf\"\n\"97\",\"uclinux\"\n\"97\",\"buildconfig\"\n\"97\",\"pyquery\"\n\"97\",\"fabric.io\"\n\"97\",\"gobject-introspection\"\n\"97\",\"codeigniter-restserver\"\n\"97\",\"gocql\"\n\"97\",\"virsh\"\n\"97\",\"plex\"\n\"97\",\"non-interactive\"\n\"97\",\"azure-timeseries-insights\"\n\"97\",\"toggleswitch\"\n\"97\",\"mne-python\"\n\"97\",\"notification-channel\"\n\"97\",\"npoco\"\n\"97\",\"cookiestore\"\n\"97\",\"targetsdkversion\"\n\"97\",\"uidocumentpickervc\"\n\"97\",\"tinydb\"\n\"97\",\"mochawesome\"\n\"97\",\"titanium-android\"\n\"97\",\"xattr\"\n\"97\",\"control-theory\"\n\"97\",\"vertx-httpclient\"\n\"97\",\"iosdeployment\"\n\"97\",\"angular2-jwt\"\n\"97\",\"long-click\"\n\"97\",\"peripherals\"\n\"97\",\"nice\"\n\"97\",\"ogr2ogr\"\n\"97\",\"loggly\"\n\"97\",\"text-justify\"\n\"97\",\"qnetworkrequest\"\n\"97\",\"perltk\"\n\"97\",\"memcheck\"\n\"97\",\"color-detection\"\n\"97\",\"pgagent\"\n\"97\",\"message-hub\"\n\"97\",\"certificate-pinning\"\n\"97\",\"android-query\"\n\"97\",\"moralis\"\n\"97\",\"tesseract.js\"\n\"97\",\"mercurial-queue\"\n\"97\",\"beeware\"\n\"97\",\"threetenbp\"\n\"97\",\"static-polymorphism\"\n\"97\",\"custom-training\"\n\"97\",\"thucydides\"\n\"97\",\"thinktecture-ident-model\"\n\"97\",\"flutter-image-picker\"\n\"97\",\"mediametadataretriever\"\n\"97\",\"passkey\"\n\"97\",\"amazon-bedrock\"\n\"97\",\"flutter-moor\"\n\"97\",\"google-guava-cache\"\n\"97\",\"autopep8\"\n\"97\",\"google-maps-embed\"\n\"97\",\"cve\"\n\"97\",\"zend-debugger\"\n\"97\",\"arm-none-eabi-gcc\"\n\"97\",\"mri\"\n\"97\",\"tiles-3\"\n\"96\",\"stackexchange\"\n\"96\",\"xsockets.net\"\n\"96\",\"locals\"\n\"96\",\"apache-hive\"\n\"96\",\"murmurhash\"\n\"96\",\"matcaffe\"\n\"96\",\"ssdt-bi\"\n\"96\",\"baidu\"\n\"96\",\"git-history\"\n\"96\",\"phpoffice-phpspreadsheet\"\n\"96\",\"flutter-audioplayers\"\n\"96\",\"listcollectionview\"\n\"96\",\"request.form\"\n\"96\",\"bindable\"\n\"96\",\"djangoappengine\"\n\"96\",\"addressof\"\n\"96\",\"smslib\"\n\"96\",\"playframework-evolutions\"\n\"96\",\"pkg-resources\"\n\"96\",\"function-expression\"\n\"96\",\"snipmate\"\n\"96\",\"sentestingkit\"\n\"96\",\"ng-storage\"\n\"96\",\"self-extracting\"\n\"96\",\"filetime\"\n\"96\",\"ultisnips\"\n\"96\",\"selenium-server\"\n\"96\",\"jscript.net\"\n\"96\",\"cd-rom\"\n\"96\",\"jscolor\"\n\"96\",\"jsonpickle\"\n\"96\",\"segger-jlink\"\n\"96\",\"window-object\"\n\"96\",\"dash.js\"\n\"96\",\"kendonumerictextbox\"\n\"96\",\"simplecov\"\n\"96\",\"oracle-nosql\"\n\"96\",\"aide-ide\"\n\"96\",\"shared-project\"\n\"96\",\"angularjs-templates\"\n\"96\",\"dj-rest-auth\"\n\"96\",\"cross-database\"\n\"96\",\"airflow-api\"\n\"96\",\"keystrokes\"\n\"96\",\"hyperloglog\"\n\"96\",\"angular-renderer2\"\n\"96\",\"aleagpu\"\n\"96\",\"winapp\"\n\"96\",\"realm-java\"\n\"96\",\"database-mail\"\n\"96\",\"rpyc\"\n\"96\",\"joomla3.3\"\n\"96\",\"pdf417\"\n\"96\",\"spring-boot-jpa\"\n\"96\",\"bpy\"\n\"96\",\"corretto\"\n\"96\",\"bll\"\n\"96\",\"jqvmap\"\n\"96\",\"earlgrey\"\n\"96\",\"ws-trust\"\n\"96\",\"jquery-ui-button\"\n\"96\",\"jquery-address\"\n\"96\",\"fouc\"\n\"96\",\"shlex\"\n\"96\",\"code-templates\"\n\"96\",\"atmega16\"\n\"96\",\"visitor-statistic\"\n\"96\",\"codeigniter-query-builder\"\n\"96\",\"pyfpdf\"\n\"96\",\"mail-form\"\n\"96\",\"kprobe\"\n\"96\",\"winscard\"\n\"96\",\"buffered\"\n\"96\",\"javaw\"\n\"96\",\"asset-management\"\n\"96\",\"scalikejdbc\"\n\"96\",\"hidapi\"\n\"96\",\"android-enterprise\"\n\"96\",\"dining-philosopher\"\n\"96\",\"azurite\"\n\"96\",\"gwtquery\"\n\"96\",\"uipresentationcontroller\"\n\"96\",\"expat-parser\"\n\"96\",\"gcloud-node\"\n\"96\",\"azure-file-share\"\n\"96\",\"hamming-code\"\n\"96\",\"iseries-navigator\"\n\"96\",\"dialogresult\"\n\"96\",\"device-emulation\"\n\"96\",\"ipconfig\"\n\"96\",\"request-timed-out\"\n\"96\",\"propertynotfoundexception\"\n\"96\",\"esx\"\n\"96\",\"active-record-query\"\n\"96\",\"peer-dependencies\"\n\"96\",\"custom-titlebar\"\n\"96\",\"iif-function\"\n\"96\",\"usagestatsmanager\"\n\"96\",\"scroll-position\"\n\"96\",\"struts-action\"\n\"96\",\"beyondcompare4\"\n\"96\",\"hbitmap\"\n\"96\",\"prefix-operator\"\n\"96\",\"parse-url\"\n\"96\",\"tph\"\n\"96\",\"avassetreader\"\n\"96\",\"pgf\"\n\"96\",\"light-inject\"\n\"96\",\"eloqua\"\n\"96\",\"idoc\"\n\"96\",\"utop\"\n\"96\",\"concurrent-queue\"\n\"96\",\"emacs-faces\"\n\"95\",\"phpcassa\"\n\"95\",\"ebcli\"\n\"95\",\"mashup\"\n\"95\",\"dbm\"\n\"95\",\"smartbanner\"\n\"95\",\"apache-commons-compress\"\n\"95\",\"little-man-computer\"\n\"95\",\"clickatell\"\n\"95\",\"multirow\"\n\"95\",\"react-native-fcm\"\n\"95\",\"eclipse-classpath\"\n\"95\",\"cls\"\n\"95\",\"sizer\"\n\"95\",\"php-pgsql\"\n\"95\",\"xpi\"\n\"95\",\"unassigned-variable\"\n\"95\",\"consumption\"\n\"95\",\"firebase-ab-testing\"\n\"95\",\"piano\"\n\"95\",\"blackboard\"\n\"95\",\"configurationproperties\"\n\"95\",\"sensitive-data\"\n\"95\",\"dirichlet\"\n\"95\",\"firebird-embedded\"\n\"95\",\"rustup\"\n\"95\",\"xms\"\n\"95\",\"vptr\"\n\"95\",\"imageswitcher\"\n\"95\",\"addtarget\"\n\"95\",\"datapicker\"\n\"95\",\"valuestack\"\n\"95\",\"method-hiding\"\n\"95\",\"alertify\"\n\"95\",\"dns-sd\"\n\"95\",\"session-bean\"\n\"95\",\"fba\"\n\"95\",\"docker-cloud\"\n\"95\",\"waze\"\n\"95\",\"jfxtras\"\n\"95\",\"psr-7\"\n\"95\",\"dynamic-text\"\n\"95\",\"nessus\"\n\"95\",\"kate\"\n\"95\",\"android-signing\"\n\"95\",\"invokerequired\"\n\"95\",\"informatica-powerexchange\"\n\"95\",\"povray\"\n\"95\",\"jupyter-console\"\n\"95\",\"passthru\"\n\"95\",\"dos2unix\"\n\"95\",\"nxlog\"\n\"95\",\".profile\"\n\"95\",\"system.io.directory\"\n\"95\",\"overflow-menu\"\n\"95\",\"extjs6.2\"\n\"95\",\"view-source\"\n\"95\",\"side-scroller\"\n\"95\",\"and-operator\"\n\"95\",\"pylintrc\"\n\"95\",\"luainterface\"\n\"95\",\"typhoeus\"\n\"95\",\"google-auth-library\"\n\"95\",\"retrieve-and-rank\"\n\"95\",\"facebook-ads\"\n\"95\",\"version-numbering\"\n\"95\",\"openthread\"\n\"95\",\"xamarin-binding\"\n\"95\",\"redis-streams\"\n\"95\",\"redisgraph\"\n\"95\",\"plotly.graph-objects\"\n\"95\",\"openlitespeed\"\n\"95\",\"registerhotkey\"\n\"95\",\"openhab\"\n\"95\",\"happybase\"\n\"95\",\"exponential-distribution\"\n\"95\",\"android-customtabs\"\n\"95\",\"xcode-cloud\"\n\"95\",\"redoc\"\n\"95\",\"timer-trigger\"\n\"95\",\"itoa\"\n\"95\",\"numexpr\"\n\"95\",\"nstabview\"\n\"95\",\"poloniex\"\n\"95\",\"mencoder\"\n\"95\",\"mendix\"\n\"95\",\"odoo-website\"\n\"95\",\"getopenfilename\"\n\"95\",\"lockbits\"\n\"95\",\"propertysheet\"\n\"95\",\"hybris-data-hub\"\n\"95\",\"memory-pool\"\n\"95\",\"genexus-sd\"\n\"95\",\"angular2-ngcontent\"\n\"95\",\"mpmediaplayercontroller\"\n\"95\",\"officedev\"\n\"95\",\"genie\"\n\"95\",\"monix\"\n\"95\",\"duktape\"\n\"95\",\"cgcolor\"\n\"95\",\"pedometer\"\n\"95\",\"angular-cli-v7\"\n\"95\",\"euro\"\n\"95\",\"movabletype\"\n\"95\",\"liferay-dxp\"\n\"95\",\"embedded-documents\"\n\"95\",\"torchscript\"\n\"95\",\"flutter-ios-build\"\n\"95\",\"paragraphs\"\n\"95\",\"emacsclient\"\n\"95\",\"composite-controls\"\n\"95\",\"line-segment\"\n\"95\",\"asic\"\n\"95\",\"subitem\"\n\"95\",\"qsplitter\"\n\"95\",\"dart-sass\"\n\"95\",\"scroll-paging\"\n\"95\",\"gitorious\"\n\"94\",\"dcgan\"\n\"94\",\"multimethod\"\n\"94\",\"clearquest\"\n\"94\",\"php-parse-error\"\n\"94\",\"file-location\"\n\"94\",\"xrange\"\n\"94\",\"y-combinator\"\n\"94\",\"sketch-3\"\n\"94\",\"mustache.php\"\n\"94\",\"repository-design\"\n\"94\",\"ef-code-first-mapping\"\n\"94\",\"rendertransform\"\n\"94\",\"sslexception\"\n\"94\",\"selectedtext\"\n\"94\",\"xlpagertabstrip\"\n\"94\",\"bitstream\"\n\"94\",\"uiviewpropertyanimator\"\n\"94\",\"discrete\"\n\"94\",\"blank-nodes\"\n\"94\",\"vnext\"\n\"94\",\"addhandler\"\n\"94\",\"checkboxpreference\"\n\"94\",\"cellid\"\n\"94\",\"picketlink\"\n\"94\",\"connected-react-router\"\n\"94\",\"documentfile\"\n\"94\",\"css-gradients\"\n\"94\",\"django-oauth\"\n\"94\",\"calendly\"\n\"94\",\"warp\"\n\"94\",\"rocketmq\"\n\"94\",\"jgoodies\"\n\"94\",\"single-spa-angular\"\n\"94\",\"oracle-commerce\"\n\"94\",\"nanopb\"\n\"94\",\"validates-uniqueness-of\"\n\"94\",\"dnd-kit\"\n\"94\",\"sitecore7.5\"\n\"94\",\"keycloak-gatekeeper\"\n\"94\",\"sequence-to-sequence\"\n\"94\",\"ibm-data-studio\"\n\"94\",\"unmanaged-memory\"\n\"94\",\"joomla-sef-urls\"\n\"94\",\"angular-route-guards\"\n\"94\",\"databricks-community-edition\"\n\"94\",\"tthread\"\n\"94\",\"dynamic-image-generation\"\n\"94\",\"htl\"\n\"94\",\"near\"\n\"94\",\"opencl-c\"\n\"94\",\"wordpress-login\"\n\"94\",\"couchdb-mango\"\n\"94\",\"android-viewbinder\"\n\"94\",\"nsstatusbar\"\n\"94\",\"password-generator\"\n\"94\",\"applepayjs\"\n\"94\",\"environment-modules\"\n\"94\",\"twitter-rest-api\"\n\"94\",\"derbyjs\"\n\"94\",\"html-imports\"\n\"94\",\"turnjs\"\n\"94\",\"wwwroot\"\n\"94\",\"entitylisteners\"\n\"94\",\"nrf51\"\n\"94\",\"blobs\"\n\"94\",\"htmlcleaner\"\n\"94\",\"table-functions\"\n\"94\",\"mailcore\"\n\"94\",\"visualtreehelper\"\n\"94\",\"minim\"\n\"94\",\"ocamllex\"\n\"94\",\"razor-components\"\n\"94\",\"synth\"\n\"94\",\"obout\"\n\"94\",\"virtual-threads\"\n\"94\",\"signalr-2\"\n\"94\",\"sevenzipsharp\"\n\"94\",\"12factor\"\n\"94\",\"ambassador\"\n\"94\",\"built-in-types\"\n\"94\",\"libffi\"\n\"94\",\"buckets\"\n\"94\",\"netoffice\"\n\"94\",\"range-query\"\n\"94\",\"uhd\"\n\"94\",\"lwuit-form\"\n\"94\",\"nstextfieldcell\"\n\"94\",\"gdkpixbuf\"\n\"94\",\"c64\"\n\"94\",\"mmo\"\n\"94\",\"wxformbuilder\"\n\"94\",\"radio-transmission\"\n\"94\",\"dfm\"\n\"94\",\"npm-workspaces\"\n\"94\",\"xamarin-test-cloud\"\n\"94\",\"metaio\"\n\"94\",\"laravel-models\"\n\"94\",\"perforce-integrate\"\n\"94\",\"iron.io\"\n\"94\",\"textrange\"\n\"94\",\"react-app-rewired\"\n\"94\",\"react-16\"\n\"94\",\"mpc\"\n\"94\",\"ejb-3.2\"\n\"94\",\"splunk-calculation\"\n\"94\",\"reserved\"\n\"94\",\"lttng\"\n\"94\",\"css-paged-media\"\n\"94\",\"get-aduser\"\n\"94\",\"linaro\"\n\"94\",\"qtopengl\"\n\"94\",\"statnet\"\n\"94\",\"amazon-rds-proxy\"\n\"94\",\"gitpod\"\n\"94\",\"statefulset\"\n\"94\",\"aloha-editor\"\n\"94\",\"prebid.js\"\n\"94\",\"usleep\"\n\"94\",\"tez\"\n\"94\",\"ifilter\"\n\"94\",\"sparse-array\"\n\"94\",\"quadprog\"\n\"94\",\"ppi\"\n\"94\",\"compose-db\"\n\"94\",\"heremaps-android-sdk\"\n\"93\",\"editortemplates\"\n\"93\",\"decidable\"\n\"93\",\"sln-file\"\n\"93\",\"jboss-cli\"\n\"93\",\"apache-httpasyncclient\"\n\"93\",\"git-husky\"\n\"93\",\"maven-checkstyle-plugin\"\n\"93\",\"great-circle\"\n\"93\",\"temporal-database\"\n\"93\",\"telegraf-inputs-plugin\"\n\"93\",\"ddd-service\"\n\"93\",\"phpass\"\n\"93\",\"ddd-debugger\"\n\"93\",\"material-react-table\"\n\"93\",\"ggiraph\"\n\"93\",\"mainclass\"\n\"93\",\"ngsanitize\"\n\"93\",\"ng-submit\"\n\"93\",\"datasheet\"\n\"93\",\"bitmap-fonts\"\n\"93\",\"catch-unit-test\"\n\"93\",\"full-trust\"\n\"93\",\"vrml\"\n\"93\",\"imagesloaded\"\n\"93\",\"pafy\"\n\"93\",\"findandmodify\"\n\"93\",\"symfony-4.4\"\n\"93\",\"containment\"\n\"93\",\"kubernetes-python-client\"\n\"93\",\"immutable-collections\"\n\"93\",\"datefilter\"\n\"93\",\"choroplethr\"\n\"93\",\"icacls\"\n\"93\",\"angularjs-ng-disabled\"\n\"93\",\"dojo.gridx\"\n\"93\",\"optionparser\"\n\"93\",\"valet\"\n\"93\",\"data-dumper\"\n\"93\",\"gqlgen\"\n\"93\",\"go-swagger\"\n\"93\",\"databricks-autoloader\"\n\"93\",\"meteor-velocity\"\n\"93\",\"service-fabric-actor\"\n\"93\",\"sikuli-ide\"\n\"93\",\"aws-toolkit\"\n\"93\",\"eai\"\n\"93\",\"azure-elasticpool\"\n\"93\",\"azure-compute-emulator\"\n\"93\",\"jvcl\"\n\"93\",\"ionos\"\n\"93\",\"wunderground\"\n\"93\",\"design-decisions\"\n\"93\",\"android-scrollbar\"\n\"93\",\"sapjco3\"\n\"93\",\"passport-google-oauth2\"\n\"93\",\"karatsuba\"\n\"93\",\"durandal-navigation\"\n\"93\",\"dependency-parsing\"\n\"93\",\"razor-class-library\"\n\"93\",\"vim-fugitive\"\n\"93\",\"audio-analysis\"\n\"93\",\"koala-gem\"\n\"93\",\"ati\"\n\"93\",\"android-2.1-eclair\"\n\"93\",\"sgen\"\n\"93\",\"pymqi\"\n\"93\",\"eyed3\"\n\"93\",\"raw-ethernet\"\n\"93\",\"formatted\"\n\"93\",\"plesk-onyx\"\n\"93\",\"gzipoutputstream\"\n\"93\",\"gwt-2.2-celltable\"\n\"93\",\"gae-search\"\n\"93\",\"xcode9.1\"\n\"93\",\"nstextstorage\"\n\"93\",\"sqlitestudio\"\n\"93\",\"c9.io\"\n\"93\",\"redirect-loop\"\n\"93\",\"exception-safety\"\n\"93\",\"sqlmetal\"\n\"93\",\"dfsort\"\n\"93\",\"scnscene\"\n\"93\",\"uihostingcontroller\"\n\"93\",\"dotnet-sdk\"\n\"93\",\"tmemo\"\n\"93\",\"hints\"\n\"93\",\"cumulative-frequency\"\n\"93\",\"custom-protocol\"\n\"93\",\"google-finance-api\"\n\"93\",\"qmouseevent\"\n\"93\",\"launchctl\"\n\"93\",\"ipb\"\n\"93\",\"mouse-hook\"\n\"93\",\"android-min-sdk\"\n\"93\",\"splistitem\"\n\"93\",\"metallb\"\n\"93\",\"action-mapping\"\n\"93\",\"laravel-vue\"\n\"93\",\"es6-map\"\n\"93\",\"prose-mirror\"\n\"93\",\"event-simulation\"\n\"93\",\"nightly-build\"\n\"93\",\"android-night-mode\"\n\"93\",\"ikimagebrowserview\"\n\"93\",\"amazon-s3-select\"\n\"93\",\"mcc\"\n\"93\",\"computer-science-theory\"\n\"93\",\"webkitspeechrecognition\"\n\"93\",\"sound-synthesis\"\n\"93\",\"tidyselect\"\n\"93\",\"quarkus-native\"\n\"93\",\"ember-addon\"\n\"93\",\"tidyquant\"\n\"93\",\"git-reflog\"\n\"93\",\"zipalign\"\n\"93\",\"spark-thriftserver\"\n\"93\",\"heapq\"\n\"93\",\"amazon-cognito-triggers\"\n\"93\",\"quickaction\"\n\"93\",\"powershell-workflow\"\n\"93\",\"google-publisher-tag\"\n\"93\",\"amazon-kinesis-video-streams\"\n\"93\",\"mdb2\"\n\"93\",\"cytoscape-web\"\n\"93\",\"google-latitude\"\n\"93\",\"google-play-billing\"\n\"93\",\"google-play-core\"\n\"92\",\"mvccontrib-grid\"\n\"92\",\"file-mapping\"\n\"92\",\"php-7.0\"\n\"92\",\"graph-neural-network\"\n\"92\",\"intelephense\"\n\"92\",\"background-agents\"\n\"92\",\"instr\"\n\"92\",\"yii-booster\"\n\"92\",\"wiimote\"\n\"92\",\"edit-and-continue\"\n\"92\",\"xml-binding\"\n\"92\",\"django-celery-beat\"\n\"92\",\"bitboard\"\n\"92\",\"bing-ads-api\"\n\"92\",\"dismax\"\n\"92\",\"dbflow\"\n\"92\",\"malformedurlexception\"\n\"92\",\"pivotal-web-services\"\n\"92\",\"smtpappender\"\n\"92\",\"sails.io.js\"\n\"92\",\"chez-scheme\"\n\"92\",\"paket\"\n\"92\",\"adempiere\"\n\"92\",\"unicode-literals\"\n\"92\",\"rowdeleting\"\n\"92\",\"rubber\"\n\"92\",\"ora-00907\"\n\"92\",\"angularjs-ng-if\"\n\"92\",\"calibre\"\n\"92\",\"aws-regions\"\n\"92\",\"django-mysql\"\n\"92\",\"unsupportedoperation\"\n\"92\",\"servicestack-bsd\"\n\"92\",\"angular-lazyloading\"\n\"92\",\"server-explorer\"\n\"92\",\"csl\"\n\"92\",\"aws-amplify-sdk-js\"\n\"92\",\"serilog-sinks-file\"\n\"92\",\"aws-alb\"\n\"92\",\"ibm-appid\"\n\"92\",\"satis\"\n\"92\",\"opalrb\"\n\"92\",\"ionic-storage\"\n\"92\",\"dynamic-list\"\n\"92\",\"opendocument\"\n\"92\",\"bnlearn\"\n\"92\",\"android-studio-3.5\"\n\"92\",\"surround\"\n\"92\",\"cpu-cycles\"\n\"92\",\"appimage\"\n\"92\",\"box2dweb\"\n\"92\",\"bodymovin\"\n\"92\",\"ones-complement\"\n\"92\",\"postmortem-debugging\"\n\"92\",\"objectinstantiation\"\n\"92\",\"browser-testing\"\n\"92\",\"pytest-bdd\"\n\"92\",\"ammap\"\n\"92\",\"lumen-5.2\"\n\"92\",\"pytest-xdist\"\n\"92\",\"kogrid\"\n\"92\",\"cocoscreator\"\n\"92\",\"nppexec\"\n\"92\",\"ml-gradle\"\n\"92\",\"dpc++\"\n\"92\",\"tinymce-6\"\n\"92\",\"haproxy-ingress\"\n\"92\",\"cairngorm\"\n\"92\",\"xcode-template\"\n\"92\",\"xcode-organizer\"\n\"92\",\"direct3d12\"\n\"92\",\"uibackgroundcolor\"\n\"92\",\"tlbimp\"\n\"92\",\"isar\"\n\"92\",\"controlled-component\"\n\"92\",\"h2db\"\n\"92\",\"polarion\"\n\"92\",\"nunit-2.5\"\n\"92\",\"downloadstring\"\n\"92\",\"uipickerviewcontroller\"\n\"92\",\"hydrogen\"\n\"92\",\"cgsize\"\n\"92\",\"custom-action-filter\"\n\"92\",\"getc\"\n\"92\",\"streaminsight\"\n\"92\",\"google-cloud-nl\"\n\"92\",\"log4php\"\n\"92\",\"odk\"\n\"92\",\"ctad\"\n\"92\",\"locomotivecms\"\n\"92\",\"geolocator\"\n\"92\",\"comaddin\"\n\"92\",\"offsetof\"\n\"92\",\"protocol-handler\"\n\"92\",\"character-properties\"\n\"92\",\"ie-compatibility-mode\"\n\"92\",\"zillow\"\n\"92\",\"glove\"\n\"92\",\"utf-32\"\n\"92\",\"trailing-return-type\"\n\"92\",\"header-only\"\n\"92\",\"imagebrush\"\n\"92\",\"archer\"\n\"92\",\"fontconfig\"\n\"92\",\"struts2-jquery-plugin\"\n\"92\",\"zend-autoloader\"\n\"92\",\"armcc\"\n\"92\",\"ytplayerview\"\n\"92\",\"threejs-editor\"\n\"92\",\"arm7\"\n\"92\",\"mcustomscrollbar\"\n\"92\",\"custom-theme\"\n\"92\",\"fo-dicom\"\n\"92\",\"static-memory-allocation\"\n\"91\",\"figures\"\n\"91\",\"cloudsim\"\n\"91\",\"groq\"\n\"91\",\"ggpmisc\"\n\"91\",\"fest\"\n\"91\",\"transitive-closure-table\"\n\"91\",\"instapy\"\n\"91\",\"yolov7\"\n\"91\",\"listjs\"\n\"91\",\"github-mantle\"\n\"91\",\"graphql-spqr\"\n\"91\",\"jeromq\"\n\"91\",\"carla\"\n\"91\",\"carrier\"\n\"91\",\"celltemplate\"\n\"91\",\"cbir\"\n\"91\",\"date-manipulation\"\n\"91\",\"configserver\"\n\"91\",\"jsprit\"\n\"91\",\"l2cap\"\n\"91\",\"symantec\"\n\"91\",\"contentpane\"\n\"91\",\"adcolony\"\n\"91\",\"fixpoint-combinators\"\n\"91\",\"fitbounds\"\n\"91\",\"symfony-flex\"\n\"91\",\"froogaloop\"\n\"91\",\"xom\"\n\"91\",\"django-uploads\"\n\"91\",\"pyd\"\n\"91\",\"jfugue\"\n\"91\",\"dataframes.jl\"\n\"91\",\"windows-networking\"\n\"91\",\"crossword\"\n\"91\",\"server-name\"\n\"91\",\"ruby-3\"\n\"91\",\"ibm-content-navigator\"\n\"91\",\"datagridviewtextboxcell\"\n\"91\",\"key-management\"\n\"91\",\"kentico-12\"\n\"91\",\"micronaut-rest\"\n\"91\",\"scala-dispatch\"\n\"91\",\"jquery-3\"\n\"91\",\"html-head\"\n\"91\",\"supercomputers\"\n\"91\",\"boost-multiprecision\"\n\"91\",\"jquery-form-validator\"\n\"91\",\"nspanel\"\n\"91\",\"htk\"\n\"91\",\"jquery-scrollify\"\n\"91\",\"password-storage\"\n\"91\",\"rappid\"\n\"91\",\"viewport3d\"\n\"91\",\"pygooglechart\"\n\"91\",\"knuth\"\n\"91\",\"visual-studio-community\"\n\"91\",\"nvprof\"\n\"91\",\"video-subtitles\"\n\"91\",\"virtualizingstackpanel\"\n\"91\",\"wordle-game\"\n\"91\",\"netbeans-8.1\"\n\"91\",\"set-union\"\n\"91\",\"py-shiny\"\n\"91\",\"fraud-prevention\"\n\"91\",\"wmplib\"\n\"91\",\"google-advertising-id\"\n\"91\",\"iso-15693\"\n\"91\",\"hocon\"\n\"91\",\"c++builder-xe2\"\n\"91\",\"cookie-session\"\n\"91\",\"uiimagejpegrepresentation\"\n\"91\",\"sql-server-2016-express\"\n\"91\",\"mks\"\n\"91\",\"gutter\"\n\"91\",\"scala-quasiquotes\"\n\"91\",\"conversation-scope\"\n\"91\",\"mobility\"\n\"91\",\"hgsubversion\"\n\"91\",\"requestcontext\"\n\"91\",\"progress-indicator\"\n\"91\",\"prolog-cut\"\n\"91\",\"test-reporting\"\n\"91\",\"geom-tile\"\n\"91\",\"ohlc\"\n\"91\",\"large-object-heap\"\n\"91\",\"qicon\"\n\"91\",\"http-tunneling\"\n\"91\",\"android-maps-utils\"\n\"91\",\"common-data-service\"\n\"91\",\"protocol-extension\"\n\"91\",\"splitpane\"\n\"91\",\"ios-sharesheet\"\n\"91\",\"learning-rate\"\n\"91\",\"custom-pages\"\n\"91\",\"ejbca\"\n\"91\",\"activemq-cpp\"\n\"91\",\"color-management\"\n\"91\",\"alluxio\"\n\"91\",\"autokey\"\n\"91\",\"soundeffect\"\n\"91\",\"zalenium\"\n\"91\",\"mcafee\"\n\"91\",\"google-provisioning-api\"\n\"91\",\"medoo\"\n\"91\",\"seekg\"\n\"91\",\"multiautocompletetextview\"\n\"91\",\"ihtmldocument2\"\n\"91\",\"flutter-localizations\"\n\"91\",\"compile-time-weaving\"\n\"91\",\"autofac-configuration\"\n\"91\",\"arraydeque\"\n\"91\",\"google-python-api\"\n\"91\",\"helix\"\n\"91\",\"webex\"\n\"91\",\"glassfish-5\"\n\"91\",\"bilinear-interpolation\"\n\"91\",\"ihostedservice\"\n\"91\",\"aruba\"\n\"91\",\"web-content\"\n\"90\",\"photoeditorsdk\"\n\"90\",\"filegroup\"\n\"90\",\"file-monitoring\"\n\"90\",\"gray-code\"\n\"90\",\"sql-types\"\n\"90\",\"integrated-pipeline-mode\"\n\"90\",\"jekyll-bootstrap\"\n\"90\",\"skeletal-animation\"\n\"90\",\"jexl\"\n\"90\",\"loadview\"\n\"90\",\"barbajs\"\n\"90\",\"teraterm\"\n\"90\",\"xtensor\"\n\"90\",\"phpickerviewcontroller\"\n\"90\",\"print-spooler-api\"\n\"90\",\"multiprocessing-manager\"\n\"90\",\"firefox-3\"\n\"90\",\"soft-references\"\n\"90\",\"swingbuilder\"\n\"90\",\"python-bindings\"\n\"90\",\"firefox-profile\"\n\"90\",\"sendmailr\"\n\"90\",\"pint\"\n\"90\",\"rx-scala\"\n\"90\",\"sabredav\"\n\"90\",\"angularjs-model\"\n\"90\",\"reason-react\"\n\"90\",\"rowversion\"\n\"90\",\"campaign-monitor\"\n\"90\",\"oracle-apex-19.2\"\n\"90\",\"wacom\"\n\"90\",\"watchface\"\n\"90\",\"dataexplorer\"\n\"90\",\"windows-phone-7.1.1\"\n\"90\",\"id3-tag\"\n\"90\",\"croppie\"\n\"90\",\"watchify\"\n\"90\",\"aws-ecs\"\n\"90\",\"rtmfp\"\n\"90\",\"workload-scheduler\"\n\"90\",\"boggle\"\n\"90\",\"bltoolkit\"\n\"90\",\"twitter-gem\"\n\"90\",\"wpmu\"\n\"90\",\"pos-for-.net\"\n\"90\",\"boost-mutex\"\n\"90\",\"jung2\"\n\"90\",\"posix-ere\"\n\"90\",\"bolts-framework\"\n\"90\",\"ini-set\"\n\"90\",\"world-map\"\n\"90\",\"easy68k\"\n\"90\",\"gruntfile\"\n\"90\",\"design-guidelines\"\n\"90\",\"ncrunch\"\n\"90\",\"nsdatepicker\"\n\"90\",\"invoices\"\n\"90\",\"hostapd\"\n\"90\",\"natural-logarithm\"\n\"90\",\"minimized\"\n\"90\",\"octave-gui\"\n\"90\",\"ocamlyacc\"\n\"90\",\"libharu\"\n\"90\",\"viola-jones\"\n\"90\",\"levenberg-marquardt\"\n\"90\",\"freeipa\"\n\"90\",\"video-card\"\n\"90\",\"shadow-copy\"\n\"90\",\"shadow-cljs\"\n\"90\",\"netnamedpipebinding\"\n\"90\",\"javax.crypto\"\n\"90\",\"kodein\"\n\"90\",\"external-dependencies\"\n\"90\",\"winsxs\"\n\"90\",\"cookieconsent\"\n\"90\",\"mod-cluster\"\n\"90\",\"registerstartupscript\"\n\"90\",\"nusmv\"\n\"90\",\"azure-vm-role\"\n\"90\",\"nvidia-digits\"\n\"90\",\"nolock\"\n\"90\",\"bunny\"\n\"90\",\"directdraw\"\n\"90\",\"hanami\"\n\"90\",\"redux-middleware\"\n\"90\",\"cacerts\"\n\"90\",\"hit\"\n\"90\",\"mmdrawercontroller\"\n\"90\",\"qdatetime\"\n\"90\",\"metadata-extractor\"\n\"90\",\"exasol\"\n\"90\",\"stringreader\"\n\"90\",\"irr\"\n\"90\",\"evopdf\"\n\"90\",\"memory-bandwidth\"\n\"90\",\"cuda-gdb\"\n\"90\",\"login-required\"\n\"90\",\"late-static-binding\"\n\"90\",\"curlpp\"\n\"90\",\"google-experiments\"\n\"90\",\"lbph-algorithm\"\n\"90\",\"dumpbin\"\n\"90\",\"text-coloring\"\n\"90\",\"angular2-router3\"\n\"90\",\"http-status-code-410\"\n\"90\",\"colorize\"\n\"90\",\"actualwidth\"\n\"90\",\"coldfusionbuilder\"\n\"90\",\"cubism.js\"\n\"90\",\"string-hashing\"\n\"90\",\"android-print-framework\"\n\"90\",\"cgcontextref\"\n\"90\",\"megaparsec\"\n\"90\",\"arrayofarrays\"\n\"90\",\"sharpgl\"\n\"90\",\"steamvr\"\n\"90\",\"mavlink\"\n\"90\",\"stencil-component\"\n\"90\",\"qtextstream\"\n\"90\",\"iexpress\"\n\"90\",\"sdi\"\n\"90\",\"solidus\"\n\"90\",\"parsexml\"\n\"90\",\"aries\"\n\"90\",\"gnat-gps\"\n\"90\",\"google-map-react\"\n\"90\",\"arduino-due\"\n\"90\",\"user-feedback\"\n\"90\",\"argonaut\"\n\"90\",\"static-class\"\n\"90\",\"threadabortexception\"\n\"89\",\"renjin\"\n\"89\",\"base32\"\n\"89\",\"load-csv\"\n\"89\",\"template-tal\"\n\"89\",\"matillion\"\n\"89\",\"yii-events\"\n\"89\",\"figwheel\"\n\"89\",\"clientscript\"\n\"89\",\"file.readalllines\"\n\"89\",\"matchmedia\"\n\"89\",\"transport-stream\"\n\"89\",\"flexmojos\"\n\"89\",\"multivariate-time-series\"\n\"89\",\"mumps\"\n\"89\",\"flink-statefun\"\n\"89\",\"flutter2.0\"\n\"89\",\"productivity-power-tools\"\n\"89\",\"paketo\"\n\"89\",\"image-rendering\"\n\"89\",\"ng2-translate\"\n\"89\",\"symbolic-integration\"\n\"89\",\"uitextinput\"\n\"89\",\"imagemin\"\n\"89\",\"jsr286\"\n\"89\",\"cscore\"\n\"89\",\"ibm-infosphere\"\n\"89\",\"operationalerror\"\n\"89\",\"agrep\"\n\"89\",\"aginity\"\n\"89\",\"joincolumn\"\n\"89\",\"pug-loader\"\n\"89\",\"createwindow\"\n\"89\",\"mysql-function\"\n\"89\",\"serving\"\n\"89\",\"akavache\"\n\"89\",\"crud-repository\"\n\"89\",\"payfort\"\n\"89\",\"spring-remoting\"\n\"89\",\"twinx\"\n\"89\",\"pathogen\"\n\"89\",\"apply-templates\"\n\"89\",\"sas-ods\"\n\"89\",\"application-start\"\n\"89\",\"sap-cloud-foundry\"\n\"89\",\"polynomial-approximations\"\n\"89\",\"android-threading\"\n\"89\",\"error-suppression\"\n\"89\",\"enlive\"\n\"89\",\"entityreference\"\n\"89\",\"bsearch\"\n\"89\",\"kubebuilder\"\n\"89\",\"system-error\"\n\"89\",\"audiosession\"\n\"89\",\"wistia\"\n\"89\",\"netscaler\"\n\"89\",\"libvpx\"\n\"89\",\"btrfs\"\n\"89\",\"netbeans-12\"\n\"89\",\"rhino3d\"\n\"89\",\"set-operations\"\n\"89\",\"macbookpro-touch-bar\"\n\"89\",\"raw-pointer\"\n\"89\",\"system.configuration\"\n\"89\",\"code-hinting\"\n\"89\",\"objectdataprovider\"\n\"89\",\"javalin\"\n\"89\",\"explicit-interface\"\n\"89\",\"expected-exception\"\n\"89\",\"podofo\"\n\"89\",\"spring-test-dbunit\"\n\"89\",\"azure-node-sdk\"\n\"89\",\"drupal-nodes\"\n\"89\",\"polkadot-js\"\n\"89\",\"scd2\"\n\"89\",\"dotnetnuke-6\"\n\"89\",\"sqlkata\"\n\"89\",\"scikit-learn-pipeline\"\n\"89\",\"polyhedra\"\n\"89\",\"half-precision-float\"\n\"89\",\"tls1.0\"\n\"89\",\"hla\"\n\"89\",\"c++builder-10.2-tokyo\"\n\"89\",\"npm-update\"\n\"89\",\"nsuseractivity\"\n\"89\",\"bunit\"\n\"89\",\"dotvvm\"\n\"89\",\"command-objects\"\n\"89\",\"chaco\"\n\"89\",\"logistics\"\n\"89\",\"ltpa\"\n\"89\",\"perfect-scrollbar\"\n\"89\",\"resharper-6.0\"\n\"89\",\"google-generativeai\"\n\"89\",\"get-headers\"\n\"89\",\"split-screen\"\n\"89\",\"google-sso\"\n\"89\",\"linkedin-j\"\n\"89\",\"zend-framework-modules\"\n\"89\",\"alloca\"\n\"89\",\"web2py-modules\"\n\"89\",\"folder-permissions\"\n\"89\",\"pgloader\"\n\"89\",\"quartz.net-2.0\"\n\"89\",\"iframe-app\"\n\"89\",\"stateflow\"\n\"89\",\"flyweight-pattern\"\n\"89\",\"stubs\"\n\"89\",\"subversion-edge\"\n\"89\",\"static-initializer\"\n\"89\",\"paperclip-validation\"\n\"89\",\"transcrypt\"\n\"89\",\"arduino-ultra-sonic\"\n\"89\",\"flutter-textinputfield\"\n\"89\",\"msn\"\n\"88\",\"stacklayout\"\n\"88\",\"tensorflow-model-garden\"\n\"88\",\"lmer\"\n\"88\",\"react-native-windows\"\n\"88\",\"jcs\"\n\"88\",\"git-index\"\n\"88\",\"ggtext\"\n\"88\",\"anti-cheat\"\n\"88\",\"gflags\"\n\"88\",\"phpdotenv\"\n\"88\",\"default-scope\"\n\"88\",\"flutterdriver\"\n\"88\",\"clientcredential\"\n\"88\",\"jboss-logging\"\n\"88\",\"debian-packaging\"\n\"88\",\"xmgrace\"\n\"88\",\"rust-chrono\"\n\"88\",\"cassandra-driver\"\n\"88\",\"xlutils\"\n\"88\",\"dism\"\n\"88\",\"function-qualifier\"\n\"88\",\"diskimage\"\n\"88\",\"rust-futures\"\n\"88\",\"distributed-training\"\n\"88\",\"checker-framework\"\n\"88\",\"contention\"\n\"88\",\"chromakey\"\n\"88\",\"voicemail\"\n\"88\",\"nhibernate.search\"\n\"88\",\"manipulators\"\n\"88\",\"inclusion\"\n\"88\",\"swiftui-navigationsplitview\"\n\"88\",\"vora\"\n\"88\",\"overscroll\"\n\"88\",\"swiftui-picker\"\n\"88\",\"kitura\"\n\"88\",\"django-tagging\"\n\"88\",\"uppaal\"\n\"88\",\"callcc\"\n\"88\",\"react-starter-kit\"\n\"88\",\"rt\"\n\"88\",\"rtcdatachannel\"\n\"88\",\"angularjs-nvd3-directives\"\n\"88\",\"mysql-error-1111\"\n\"88\",\"server-variables\"\n\"88\",\"angular-seed\"\n\"88\",\"react-toolbox\"\n\"88\",\"pwntools\"\n\"88\",\"rncryptor\"\n\"88\",\"opencensus\"\n\"88\",\"twilio-taskrouter\"\n\"88\",\"modelmetadata\"\n\"88\",\"dynamictype\"\n\"88\",\"onrestoreinstancestate\"\n\"88\",\"jquery-backstretch\"\n\"88\",\"passport-saml\"\n\"88\",\"workload-identity\"\n\"88\",\"bottombar\"\n\"88\",\"eregi\"\n\"88\",\"inotifydataerrorinfo\"\n\"88\",\"htmleditorkit\"\n\"88\",\"nrvo\"\n\"88\",\"sarimax\"\n\"88\",\"jquery-ui-map\"\n\"88\",\"htmltextwriter\"\n\"88\",\"wordml\"\n\"88\",\"pyspark-schema\"\n\"88\",\"codeql\"\n\"88\",\"sfx\"\n\"88\",\"pyelasticsearch\"\n\"88\",\"128-bit\"\n\"88\",\"pyfits\"\n\"88\",\"kotlin-dokka\"\n\"88\",\"netmsmqbinding\"\n\"88\",\"cognos-tm1\"\n\"88\",\"type-promotion\"\n\"88\",\"osticket\"\n\"88\",\"nevpnmanager\"\n\"88\",\"aurelia-templating\"\n\"88\",\"wkinterfacetable\"\n\"88\",\"drake-r-package\"\n\"88\",\"refer\"\n\"88\",\"gupshup\"\n\"88\",\"tla+\"\n\"88\",\"ithit-webdav-server\"\n\"88\",\"openvr\"\n\"88\",\"azure-gov\"\n\"88\",\"c#-7.3\"\n\"88\",\"tkmessagebox\"\n\"88\",\"game-ai\"\n\"88\",\"execfile\"\n\"88\",\"ispconfig\"\n\"88\",\"gelf\"\n\"88\",\"rails-3-upgrade\"\n\"88\",\"galois-field\"\n\"88\",\"asp.net-session\"\n\"88\",\"tkinter-menu\"\n\"88\",\"radix-ui\"\n\"88\",\"react-apollo-hooks\"\n\"88\",\"css-purge\"\n\"88\",\"terraform-cdk\"\n\"88\",\"progressive-download\"\n\"88\",\"dunit\"\n\"88\",\"android-phone-call\"\n\"88\",\"mprotect\"\n\"88\",\"periodicity\"\n\"88\",\"actionviewhelper\"\n\"88\",\"strncmp\"\n\"88\",\"nitrogen\"\n\"88\",\"elastix\"\n\"88\",\"reactjs-testutils\"\n\"88\",\"auto-import\"\n\"88\",\"autodiscovery\"\n\"88\",\"lineageos\"\n\"88\",\"glance-appwidget\"\n\"88\",\"gksession\"\n\"88\",\"lifting\"\n\"88\",\"bim\"\n\"88\",\"beta-distribution\"\n\"88\",\"ms-access-2000\"\n\"88\",\"zeitwerk\"\n\"88\",\"webdynpro\"\n\"88\",\"torquebox\"\n\"88\",\"tframe\"\n\"88\",\"webdatarocks\"\n\"88\",\"stylish\"\n\"88\",\"if-modified-since\"\n\"88\",\"struts2-jquery-grid\"\n\"88\",\"steambot\"\n\"87\",\"react-native-vision-camera\"\n\"87\",\"groovy-grape\"\n\"87\",\"mashape\"\n\"87\",\"instantiation-error\"\n\"87\",\"slackware\"\n\"87\",\"trusted-timestamp\"\n\"87\",\"jenkins-scriptler\"\n\"87\",\"tensorflow-slim\"\n\"87\",\"gevent-socketio\"\n\"87\",\"apache-commons-exec\"\n\"87\",\"webrole\"\n\"87\",\"wfastcgi\"\n\"87\",\"jetpack-compose-animation\"\n\"87\",\"phplist\"\n\"87\",\"rendertarget\"\n\"87\",\"primavera\"\n\"87\",\"wercker\"\n\"87\",\"flexjson\"\n\"87\",\"music-notation\"\n\"87\",\"edge.js\"\n\"87\",\"multiprecision\"\n\"87\",\"rename-item-cmdlet\"\n\"87\",\"jdk1.7\"\n\"87\",\"bindvalue\"\n\"87\",\"xmlrpcclient\"\n\"87\",\"api-authorization\"\n\"87\",\"datestamp\"\n\"87\",\"nghttp2\"\n\"87\",\"cardslib\"\n\"87\",\"dbatools\"\n\"87\",\"filesplitting\"\n\"87\",\"django-4.0\"\n\"87\",\"python-s3fs\"\n\"87\",\"python-pdfkit\"\n\"87\",\"js-ipfs\"\n\"87\",\"pii\"\n\"87\",\"safari-app-extension\"\n\"87\",\"python-can\"\n\"87\",\"pivotitem\"\n\"87\",\"jsonkit\"\n\"87\",\"catplot\"\n\"87\",\"smbus\"\n\"87\",\"directory-traversal\"\n\"87\",\"finalbuilder\"\n\"87\",\"uitraitcollection\"\n\"87\",\"fla\"\n\"87\",\"symbol-server\"\n\"87\",\"lando\"\n\"87\",\"pkcs#1\"\n\"87\",\"jline\"\n\"87\",\"jose4j\"\n\"87\",\"verdaccio\"\n\"87\",\"wavesplatform\"\n\"87\",\"aws-chime-sdk\"\n\"87\",\"unnotificationserviceextension\"\n\"87\",\"mysql++\"\n\"87\",\"oql\"\n\"87\",\"jqbootstrapvalidation\"\n\"87\",\"r-mapview\"\n\"87\",\"publish-profiles\"\n\"87\",\"google-wave\"\n\"87\",\"hypertable\"\n\"87\",\"avcam\"\n\"87\",\"fastscroll\"\n\"87\",\"callbyname\"\n\"87\",\"boomi\"\n\"87\",\"core-media\"\n\"87\",\"wro4j\"\n\"87\",\"nrepl\"\n\"87\",\"tweetstream\"\n\"87\",\"dvb\"\n\"87\",\"open62541\"\n\"87\",\"eos\"\n\"87\",\"sikuli-x\"\n\"87\",\"opaque-pointers\"\n\"87\",\"dynatable\"\n\"87\",\"attribution\"\n\"87\",\"knox-amazon-s3-client\"\n\"87\",\"java-ws\"\n\"87\",\"amp-email\"\n\"87\",\"augeas\"\n\"87\",\"viewswitcher\"\n\"87\",\"r-car\"\n\"87\",\"google-benchmark\"\n\"87\",\"level-of-detail\"\n\"87\",\"jasmine-marbles\"\n\"87\",\"vision\"\n\"87\",\"object-tracking\"\n\"87\",\"mini-batch\"\n\"87\",\"ui5-tooling\"\n\"87\",\"javah\"\n\"87\",\"netbeans6.7\"\n\"87\",\"netbeans6.5\"\n\"87\",\"lib-nfc\"\n\"87\",\"libv8\"\n\"87\",\"ratpack\"\n\"87\",\"mks-integrity\"\n\"87\",\"sqljdbc\"\n\"87\",\"dpkt\"\n\"87\",\"azuremlsdk\"\n\"87\",\"scoverage\"\n\"87\",\"diagrams\"\n\"87\",\"d-pad\"\n\"87\",\"openmax\"\n\"87\",\"mkdirs\"\n\"87\",\"openstack-heat\"\n\"87\",\"isolate-scope\"\n\"87\",\"drupal-templates\"\n\"87\",\"modelchoicefield\"\n\"87\",\"xaml-designer\"\n\"87\",\"openxava\"\n\"87\",\"express-generator\"\n\"87\",\"null-conditional-operator\"\n\"87\",\"bulk-collect\"\n\"87\",\"octokit-js\"\n\"87\",\"react-hooks-testing-library\"\n\"87\",\"memory-fragmentation\"\n\"87\",\"resque-scheduler\"\n\"87\",\"getorgchart\"\n\"87\",\"cglayer\"\n\"87\",\"angular-builder\"\n\"87\",\"perfect-square\"\n\"87\",\"android-picture-in-picture\"\n\"87\",\"tesla\"\n\"87\",\"hugo-shortcode\"\n\"87\",\"ctrlp\"\n\"87\",\"pentaho-design-studio\"\n\"87\",\"terraform-provider-databricks\"\n\"87\",\"getlasterror\"\n\"87\",\"papi\"\n\"87\",\"dagre-d3\"\n\"87\",\"weblogic9.x\"\n\"87\",\"conditional-aggregation\"\n\"87\",\"flutter-workmanager\"\n\"87\",\"tool-uml\"\n\"87\",\"ihp\"\n\"87\",\"papaja\"\n\"87\",\"security-constraint\"\n\"87\",\"spanned\"\n\"86\",\"eclipse-clp\"\n\"86\",\"proc-r-package\"\n\"86\",\"primary-key-design\"\n\"86\",\"xxe\"\n\"86\",\"vue-events\"\n\"86\",\"localauthentication\"\n\"86\",\"react-native-tab-view\"\n\"86\",\"ef4-code-only\"\n\"86\",\"vsvim\"\n\"86\",\"ssim\"\n\"86\",\"multipleoutputs\"\n\"86\",\"json-view\"\n\"86\",\"jssip\"\n\"86\",\"apollo-angular\"\n\"86\",\"function-module\"\n\"86\",\"binary-decision-diagram\"\n\"86\",\"selenium3\"\n\"86\",\"unhandled-promise-rejection\"\n\"86\",\"umdf\"\n\"86\",\"json-c\"\n\"86\",\"pidgin\"\n\"86\",\"apache-ode\"\n\"86\",\"ng-container\"\n\"86\",\"bitcoinj\"\n\"86\",\"wcm\"\n\"86\",\"camera-overlay\"\n\"86\",\"grails-2.5\"\n\"86\",\"jfrog-container-registry\"\n\"86\",\"server-side-swift\"\n\"86\",\"sitecore-media-library\"\n\"86\",\"falcon\"\n\"86\",\"factoextra\"\n\"86\",\"ora-00942\"\n\"86\",\"awss3transfermanager\"\n\"86\",\"alertview\"\n\"86\",\"django-pyodbc\"\n\"86\",\"servicecontract\"\n\"86\",\"purchase-order\"\n\"86\",\"database-testing\"\n\"86\",\"waitforsingleobject\"\n\"86\",\"simplex-noise\"\n\"86\",\"android-studio-import\"\n\"86\",\"cortana-skills-kit\"\n\"86\",\"spring-dm\"\n\"86\",\"html5shiv\"\n\"86\",\"sanctum\"\n\"86\",\"signpost\"\n\"86\",\"nav-pills\"\n\"86\",\"katalon\"\n\"86\",\"gtksourceview\"\n\"86\",\"onlinebanking\"\n\"86\",\"android-sinch-api\"\n\"86\",\"bottom-up\"\n\"86\",\"azul-zulu\"\n\"86\",\"dynamic-scope\"\n\"86\",\"az\"\n\"86\",\"cpp-netlib\"\n\"86\",\"inquirer\"\n\"86\",\"neo4j-driver\"\n\"86\",\"oscilloscope\"\n\"86\",\"codesmith\"\n\"86\",\"ubuntu-9.10\"\n\"86\",\"out-of-process\"\n\"86\",\"ui.bootstrap\"\n\"86\",\"wiredep\"\n\"86\",\"shtml\"\n\"86\",\"forfiles\"\n\"86\",\"woodstox\"\n\"86\",\"obex\"\n\"86\",\"visual-sourcesafe-2005\"\n\"86\",\"objectarx\"\n\"86\",\"synchronized-block\"\n\"86\",\"visual-studio-designer\"\n\"86\",\"foundry-workshop\"\n\"86\",\"android-attributes\"\n\"86\",\"return-type-deduction\"\n\"86\",\"visual-studio-project\"\n\"86\",\"burndowncharts\"\n\"86\",\"mklink\"\n\"86\",\"hibernate-cascade\"\n\"86\",\"gae-module\"\n\"86\",\"hmail-server\"\n\"86\",\"high-order-component\"\n\"86\",\"drc\"\n\"86\",\"screens\"\n\"86\",\"quickselect\"\n\"86\",\"hamlet\"\n\"86\",\"iwork\"\n\"86\",\"metacharacters\"\n\"86\",\"google-gears\"\n\"86\",\"custom-formatting\"\n\"86\",\"ip-restrictions\"\n\"86\",\"electron-vue\"\n\"86\",\"lseek\"\n\"86\",\"esmodules\"\n\"86\",\"logical-and\"\n\"86\",\"strong-references\"\n\"86\",\"metal-performance-shaders\"\n\"86\",\"omap\"\n\"86\",\"stream-analytics\"\n\"86\",\"cuda-streams\"\n\"86\",\"okio\"\n\"86\",\"meego\"\n\"86\",\"common.logging\"\n\"86\",\"examine\"\n\"86\",\"elm327\"\n\"86\",\"pq\"\n\"86\",\"pptp\"\n\"86\",\"usb-mass-storage\"\n\"86\",\"argument-error\"\n\"86\",\"helpermethods\"\n\"86\",\"custom-transition\"\n\"86\",\"transducer\"\n\"86\",\"web-midi\"\n\"86\",\"preemptive\"\n\"86\",\"secure-random\"\n\"86\",\"user-inactivity\"\n\"86\",\"top-command\"\n\"86\",\"sonarqube5.6\"\n\"86\",\"tie\"\n\"86\",\"youtubeplayer\"\n\"86\",\"prebuild\"\n\"86\",\"fma\"\n\"86\",\"linq-query-syntax\"\n\"86\",\"linux-disk-free\"\n\"86\",\"maven-install-plugin\"\n\"86\",\"searchlogic\"\n\"86\",\"multi-dimensional-scaling\"\n\"85\",\"treetop\"\n\"85\",\"liquidsoap\"\n\"85\",\"xregexp\"\n\"85\",\"trending\"\n\"85\",\"mat-stepper\"\n\"85\",\"react-native-navigation-v2\"\n\"85\",\"multi-query\"\n\"85\",\"anyevent\"\n\"85\",\"template-instantiation\"\n\"85\",\"location-client\"\n\"85\",\"apache-commons-dbutils\"\n\"85\",\"graphite-carbon\"\n\"85\",\"clear-cache\"\n\"85\",\"apache-commons-lang3\"\n\"85\",\"cleartype\"\n\"85\",\"jenkins-job-builder\"\n\"85\",\"tensorflow-xla\"\n\"85\",\"apache-spark-xml\"\n\"85\",\"nhibernate-3\"\n\"85\",\"page-editor\"\n\"85\",\"sencha-charts\"\n\"85\",\"findersync\"\n\"85\",\"smart-wizard\"\n\"85\",\"flask-babel\"\n\"85\",\"app-distribution\"\n\"85\",\"data-link-layer\"\n\"85\",\"manova\"\n\"85\",\"addrange\"\n\"85\",\"checkboxfor\"\n\"85\",\"ape-phylo\"\n\"85\",\"mapview\"\n\"85\",\"realm-object-server\"\n\"85\",\"credits\"\n\"85\",\"oracle-service-bus\"\n\"85\",\"unrealscript\"\n\"85\",\"django-mongodb-engine\"\n\"85\",\"oracle-soa\"\n\"85\",\"vectormath\"\n\"85\",\"ibpy\"\n\"85\",\"rotator\"\n\"85\",\"kernlab\"\n\"85\",\"cross-fade\"\n\"85\",\"boyer-moore\"\n\"85\",\"denormalized\"\n\"85\",\"nservicebus4\"\n\"85\",\"dev-appserver\"\n\"85\",\"craigslist\"\n\"85\",\"working-copy\"\n\"85\",\"pathinfo\"\n\"85\",\"writeablebitmapex\"\n\"85\",\"html5-animation\"\n\"85\",\"nsslider\"\n\"85\",\"html5-draggable\"\n\"85\",\"ridgeline-plot\"\n\"85\",\"knockout-sortable\"\n\"85\",\"reverse-iterator\"\n\"85\",\"analytical\"\n\"85\",\"javax.sound.midi\"\n\"85\",\"rational-numbers\"\n\"85\",\"visualbrush\"\n\"85\",\"pystan\"\n\"85\",\"lumia-imaging-sdk\"\n\"85\",\"viterbi\"\n\"85\",\"minimalmodbus\"\n\"85\",\"netbeans-8.2\"\n\"85\",\"nvp\"\n\"85\",\"browser-refresh\"\n\"85\",\"shallow-clone\"\n\"85\",\"visual-assist\"\n\"85\",\"nstouchbar\"\n\"85\",\"uipath-orchestrator\"\n\"85\",\"uipath-activity\"\n\"85\",\"tinder\"\n\"85\",\"noir\"\n\"85\",\"isolatedstoragefile\"\n\"85\",\"timsort\"\n\"85\",\"cache-invalidation\"\n\"85\",\"iterparse\"\n\"85\",\"xcache\"\n\"85\",\"openwebrtc\"\n\"85\",\"executionpolicy\"\n\"85\",\"non-exhaustive-patterns\"\n\"85\",\"dotnetbar\"\n\"85\",\"tbxml\"\n\"85\",\"nonlinear-equation\"\n\"85\",\"null-object-pattern\"\n\"85\",\"hibernate.cfg.xml\"\n\"85\",\"mkstorekit\"\n\"85\",\"reflect-metadata\"\n\"85\",\"c#-7.2\"\n\"85\",\"pex-and-moles\"\n\"85\",\"angular-component-router\"\n\"85\",\"ctl\"\n\"85\",\"qdir\"\n\"85\",\"qjson\"\n\"85\",\"no-data\"\n\"85\",\"commercetools\"\n\"85\",\"node-schedule\"\n\"85\",\"spn\"\n\"85\",\"mousemotionlistener\"\n\"85\",\"qpython3\"\n\"85\",\"leaflet-routing-machine\"\n\"85\",\"textangular\"\n\"85\",\"launch-daemon\"\n\"85\",\"qmenubar\"\n\"85\",\"glidejs\"\n\"85\",\"bimap\"\n\"85\",\"sonarqube5.3\"\n\"85\",\"libzip\"\n\"85\",\"web-optimization\"\n\"85\",\"identitymodel\"\n\"85\",\"sourceforge-appscript\"\n\"85\",\"enctype\"\n\"85\",\"amazon-policy\"\n\"85\",\"googlesheets4\"\n\"85\",\"composite-index\"\n\"85\",\"big-ip\"\n\"85\",\"webpack-cli\"\n\"85\",\"emacs-helm\"\n\"85\",\"computed-field\"\n\"84\",\"instamojo\"\n\"84\",\"cmsmadesimple\"\n\"84\",\"weighting\"\n\"84\",\"y86\"\n\"84\",\"skproduct\"\n\"84\",\"github-organizations\"\n\"84\",\"jboss-developer-studio\"\n\"84\",\"graph-layout\"\n\"84\",\"installshield-2011\"\n\"84\",\"materialdrawer\"\n\"84\",\"apache-commons-email\"\n\"84\",\"apache-cocoon\"\n\"84\",\"grass\"\n\"84\",\"flink-table-api\"\n\"84\",\"field-names\"\n\"84\",\"teamspeak\"\n\"84\",\"jdoql\"\n\"84\",\"anti-join\"\n\"84\",\"gretty\"\n\"84\",\"vue-sfc\"\n\"84\",\"cloudmade\"\n\"84\",\"ggalluvial\"\n\"84\",\"gridworld\"\n\"84\",\"afhttprequestoperation\"\n\"84\",\"xml-formatting\"\n\"84\",\"platypus\"\n\"84\",\"cartalyst-sentinel\"\n\"84\",\"impress.js\"\n\"84\",\"voximplant\"\n\"84\",\"django-contrib\"\n\"84\",\"friend-class\"\n\"84\",\"adobe-premiere\"\n\"84\",\"lambdaj\"\n\"84\",\"check-mk\"\n\"84\",\"xidel\"\n\"84\",\"frozenset\"\n\"84\",\"xperf\"\n\"84\",\"fireworks\"\n\"84\",\"uitypeeditor\"\n\"84\",\"firewatir\"\n\"84\",\"symmetric-key\"\n\"84\",\"python-pika\"\n\"84\",\"page-jump\"\n\"84\",\"chicagoboss\"\n\"84\",\"xming\"\n\"84\",\"g1ant\"\n\"84\",\"conky\"\n\"84\",\"catalina.out\"\n\"84\",\"wincrypt\"\n\"84\",\"databags\"\n\"84\",\"grails-validation\"\n\"84\",\"watchos-6\"\n\"84\",\"oracle-enterprise-manager\"\n\"84\",\"database-abstraction\"\n\"84\",\"caliper\"\n\"84\",\"react-swiper\"\n\"84\",\"read-csv\"\n\"84\",\"joomla3.4\"\n\"84\",\"wcf-hosting\"\n\"84\",\"keras-rl\"\n\"84\",\"uribuilder\"\n\"84\",\"simscape\"\n\"84\",\"mysql-error-1093\"\n\"84\",\"sequence-generators\"\n\"84\",\"jooq-codegen-maven\"\n\"84\",\"pts\"\n\"84\",\"django-filebrowser\"\n\"84\",\"vdi\"\n\"84\",\"canary-deployment\"\n\"84\",\"public-activity\"\n\"84\",\"routeconfig\"\n\"84\",\"nano-server\"\n\"84\",\"verifone\"\n\"84\",\"punycode\"\n\"84\",\"doclet\"\n\"84\",\"negate\"\n\"84\",\"dwolla\"\n\"84\",\"easyadmin3\"\n\"84\",\"wso2-message-broker\"\n\"84\",\"power-automate-custom-connector\"\n\"84\",\"swc-compiler\"\n\"84\",\"k8s-serviceaccount\"\n\"84\",\"erasure\"\n\"84\",\"savefig\"\n\"84\",\"ttkbootstrap\"\n\"84\",\"money-rails\"\n\"84\",\"spring-security-acl\"\n\"84\",\"android-wireless\"\n\"84\",\"twitter-follow\"\n\"84\",\"aws-sso\"\n\"84\",\"simba\"\n\"84\",\"extern-c\"\n\"84\",\"nyromodal\"\n\"84\",\"externals\"\n\"84\",\"pygit2\"\n\"84\",\"netflix-dgs\"\n\"84\",\"godoc\"\n\"84\",\"code-assist\"\n\"84\",\"shoutem\"\n\"84\",\"google-caja\"\n\"84\",\"facebook-invite\"\n\"84\",\"viewmodellocator\"\n\"84\",\"revolution-r\"\n\"84\",\"ubuntu-17.10\"\n\"84\",\"video-intelligence-api\"\n\"84\",\"pyflakes\"\n\"84\",\"freecad\"\n\"84\",\"timecodes\"\n\"84\",\"nstablecolumn\"\n\"84\",\"sql-server-2012-datatools\"\n\"84\",\"taskfactory\"\n\"84\",\"convertapi\"\n\"84\",\"registerforactivityresult\"\n\"84\",\"qwebpage\"\n\"84\",\"nstokenfield\"\n\"84\",\"hindley-milner\"\n\"84\",\"plyr.js\"\n\"84\",\"xamarin-community-toolkit\"\n\"84\",\"devexpress-gridcontrol\"\n\"84\",\"time-frequency\"\n\"84\",\"export-to-text\"\n\"84\",\"android-filter\"\n\"84\",\"gemfile.lock\"\n\"84\",\"playready\"\n\"84\",\"access-levels\"\n\"84\",\"elastic-beats\"\n\"84\",\"ctor-initializer\"\n\"84\",\"protobuf-go\"\n\"84\",\"resample\"\n\"84\",\"monte-carlo-tree-search\"\n\"84\",\"messagecontract\"\n\"84\",\"ios-enterprise\"\n\"84\",\"generic-function\"\n\"84\",\"requirejs-optimizer\"\n\"84\",\"splint\"\n\"84\",\"rescript\"\n\"84\",\"react-instantsearch\"\n\"84\",\"lockless\"\n\"84\",\"lossless\"\n\"84\",\"mpeg-2\"\n\"84\",\"isalpha\"\n\"84\",\"elementref\"\n\"84\",\"centralized\"\n\"84\",\"actiondispatch\"\n\"84\",\"color-depth\"\n\"84\",\"morphological-analysis\"\n\"84\",\"acme\"\n\"84\",\"terraform-provider-kubernetes\"\n\"84\",\"berkeley-db-je\"\n\"84\",\"qubole\"\n\"84\",\"web3-react\"\n\"84\",\"gnu-findutils\"\n\"84\",\"soundcard\"\n\"84\",\"endlessscroll\"\n\"84\",\"battery-saver\"\n\"84\",\"preloadjs\"\n\"84\",\"spark3\"\n\"84\",\"tiddlywiki\"\n\"84\",\"gmlib\"\n\"84\",\"git-tf\"\n\"84\",\"sudzc\"\n\"84\",\"embedded-language\"\n\"84\",\"foreach-object\"\n\"83\",\"wgpu-rs\"\n\"83\",\"edk2\"\n\"83\",\"instantsearch\"\n\"83\",\"jdedwards\"\n\"83\",\"productbuild\"\n\"83\",\"ckquery\"\n\"83\",\"festival\"\n\"83\",\"ssmtp\"\n\"83\",\"clockify\"\n\"83\",\"eclipse-jee\"\n\"83\",\"definitions\"\n\"83\",\"llblgen\"\n\"83\",\"integrated-security\"\n\"83\",\"biztalk-deployment\"\n\"83\",\"mali\"\n\"83\",\"adldap\"\n\"83\",\"social-authentication\"\n\"83\",\"lapacke\"\n\"83\",\"languagetool\"\n\"83\",\"import-contacts\"\n\"83\",\"firefox-developer-edition\"\n\"83\",\"sendasync\"\n\"83\",\"finite-field\"\n\"83\",\"constraint-satisfaction\"\n\"83\",\"symphony-cms\"\n\"83\",\"datapoint\"\n\"83\",\"chessboard.js\"\n\"83\",\"ngx-bootstrap-modal\"\n\"83\",\"wcfserviceclient\"\n\"83\",\"rnw\"\n\"83\",\"window-management\"\n\"83\",\"iana\"\n\"83\",\"iconbutton\"\n\"83\",\"awesome-notifications\"\n\"83\",\"camel-sql\"\n\"83\",\"alfa\"\n\"83\",\"rpm-maven-plugin\"\n\"83\",\"jmagick\"\n\"83\",\"microsoft-exchange\"\n\"83\",\"rjsonio\"\n\"83\",\"metro-ui-css\"\n\"83\",\"mysql.data\"\n\"83\",\"kirby\"\n\"83\",\"android-studio-3.3\"\n\"83\",\"equality-operator\"\n\"83\",\"android-studio-arctic-fox\"\n\"83\",\"appleevents\"\n\"83\",\"sap-business-one-di-api\"\n\"83\",\"swapfile\"\n\"83\",\"swank\"\n\"83\",\"suricata\"\n\"83\",\"openapi-generator-cli\"\n\"83\",\"azure-ai-search\"\n\"83\",\"pdfviewer\"\n\"83\",\"wtelegramclient\"\n\"83\",\"twincat-ads\"\n\"83\",\"onresize\"\n\"83\",\"twill\"\n\"83\",\"coverlet\"\n\"83\",\"pony\"\n\"83\",\"popularity\"\n\"83\",\"nsnull\"\n\"83\",\"3d-rendering\"\n\"83\",\"amq\"\n\"83\",\"knowledge-management\"\n\"83\",\"absinthe\"\n\"83\",\"shaderlab\"\n\"83\",\"r-glue\"\n\"83\",\"oak\"\n\"83\",\"libiconv\"\n\"83\",\"libx265\"\n\"83\",\"facebook-java-api\"\n\"83\",\"go-get\"\n\"83\",\"dom-node\"\n\"83\",\"browser-link\"\n\"83\",\"cohesion\"\n\"83\",\"netmask\"\n\"83\",\"libreadline\"\n\"83\",\"atlassian-crowd\"\n\"83\",\"rattle\"\n\"83\",\"browserfield\"\n\"83\",\"azure-webjobs-triggered\"\n\"83\",\"c++builder-xe\"\n\"83\",\"aspbutton\"\n\"83\",\"itunes-sdk\"\n\"83\",\"quickreports\"\n\"83\",\"digital-design\"\n\"83\",\"pls\"\n\"83\",\"nsuinteger\"\n\"83\",\"byobu\"\n\"83\",\"xbase\"\n\"83\",\"iup\"\n\"83\",\"opensolaris\"\n\"83\",\"hana-xs\"\n\"83\",\"drupal-fields\"\n\"83\",\"uipath-robot\"\n\"83\",\"sqlcl\"\n\"83\",\"taskcompletionsource\"\n\"83\",\"mobicents\"\n\"83\",\"draper\"\n\"83\",\"assemblybinding\"\n\"83\",\"ntfs-mft\"\n\"83\",\"tlist\"\n\"83\",\"deviceid\"\n\"83\",\"scalaz-stream\"\n\"83\",\"convertview\"\n\"83\",\"wxhaskell\"\n\"83\",\"mkreversegeocoder\"\n\"83\",\"dia\"\n\"83\",\"spherical-coordinate\"\n\"83\",\"proxy-authentication\"\n\"83\",\"n-layer\"\n\"83\",\"percent-encoding\"\n\"83\",\"nivo-react\"\n\"83\",\"color-codes\"\n\"83\",\"peoplecode\"\n\"83\",\"memory-optimization\"\n\"83\",\"mendeley\"\n\"83\",\"stringescapeutils\"\n\"83\",\"cfchart\"\n\"83\",\"spotify-scio\"\n\"83\",\"google-container-os\"\n\"83\",\"cursive\"\n\"83\",\"qoq\"\n\"83\",\"office-2003\"\n\"83\",\"nibabel\"\n\"83\",\"stm8\"\n\"83\",\"getproperty\"\n\"83\",\"tidycensus\"\n\"83\",\"msaa\"\n\"83\",\"bcs\"\n\"83\",\"source-sets\"\n\"83\",\"globalize3\"\n\"83\",\"vaadin24\"\n\"83\",\"ms-access-reports\"\n\"83\",\"dart-http\"\n\"83\",\"bigrquery\"\n\"83\",\"glmm\"\n\"83\",\"stylus-pen\"\n\"83\",\"solana-transaction-instruction\"\n\"83\",\"tools.jar\"\n\"83\",\"argon2-ffi\"\n\"83\",\"usernametoken\"\n\"83\",\"webexception\"\n\"83\",\"autofac-module\"\n\"83\",\"hbs\"\n\"83\",\"usersession\"\n\"82\",\"fetching-strategy\"\n\"82\",\"telegraf.js\"\n\"82\",\"wicket-7\"\n\"82\",\"clockkit\"\n\"82\",\"websphere-mq-fte\"\n\"82\",\"smallbasic\"\n\"82\",\"client-library\"\n\"82\",\"mupad\"\n\"82\",\"xubuntu\"\n\"82\",\"eclipse-hono\"\n\"82\",\"xml-to-json\"\n\"82\",\"blaze\"\n\"82\",\"kudan\"\n\"82\",\"ngb-datepicker\"\n\"82\",\"pixate\"\n\"82\",\"smf\"\n\"82\",\"flashing\"\n\"82\",\"firefox-addon-restartless\"\n\"82\",\"apic\"\n\"82\",\"symfony-mailer\"\n\"82\",\"social-media-like\"\n\"82\",\"datanode\"\n\"82\",\"p3p\"\n\"82\",\"pitch-shifting\"\n\"82\",\"addslashes\"\n\"82\",\"biztalk-2020\"\n\"82\",\"n2cms\"\n\"82\",\"django-reversion\"\n\"82\",\"ora-06550\"\n\"82\",\"awakefromnib\"\n\"82\",\"server-to-server\"\n\"82\",\"puremvc\"\n\"82\",\"react-styleguidist\"\n\"82\",\"django-nose\"\n\"82\",\"gpiozero\"\n\"82\",\"csharp-source-generator\"\n\"82\",\"cryptoswift\"\n\"82\",\"nativeapplication\"\n\"82\",\"puppetlabs-apache\"\n\"82\",\"uploadcare\"\n\"82\",\"modeshape\"\n\"82\",\"mongoose-plugins\"\n\"82\",\"kcfinder\"\n\"82\",\"brain.js\"\n\"82\",\"ensembles\"\n\"82\",\"eaglview\"\n\"82\",\"early-stopping\"\n\"82\",\"interopservices\"\n\"82\",\"htmltools\"\n\"82\",\"posh-git\"\n\"82\",\"input-devices\"\n\"82\",\"poster\"\n\"82\",\"pdfnet\"\n\"82\",\"iobluetooth\"\n\"82\",\"kadanes-algorithm\"\n\"82\",\"poptoviewcontroller\"\n\"82\",\"booksleeve\"\n\"82\",\"nsopenglview\"\n\"82\",\"http-accept-header\"\n\"82\",\"code-rally\"\n\"82\",\"knockout-components\"\n\"82\",\"microsoft-speech-api\"\n\"82\",\"java-batch\"\n\"82\",\"atlassian-fisheye\"\n\"82\",\"mime-mail\"\n\"82\",\"kotlintest\"\n\"82\",\"abpersonviewcontroller\"\n\"82\",\"oboe\"\n\"82\",\"organic-groups\"\n\"82\",\"jasmin\"\n\"82\",\"virtual-device-manager\"\n\"82\",\"shockwave\"\n\"82\",\"audioformat\"\n\"82\",\"typesafe-stack\"\n\"82\",\"mkcoordinateregion\"\n\"82\",\"notification-listener\"\n\"82\",\"xcode7-beta5\"\n\"82\",\"hipchat\"\n\"82\",\"redeclare\"\n\"82\",\"quosure\"\n\"82\",\"ntl\"\n\"82\",\"playstation3\"\n\"82\",\"versions-maven-plugin\"\n\"82\",\"hikvision\"\n\"82\",\"geddy\"\n\"82\",\"gated-recurrent-unit\"\n\"82\",\"gamepad-api\"\n\"82\",\"hashref\"\n\"82\",\"logfile-analysis\"\n\"82\",\"android-junit\"\n\"82\",\"mosaic-plot\"\n\"82\",\"nhibernate-projections\"\n\"82\",\"angular2-material\"\n\"82\",\"spreadsheetml\"\n\"82\",\"cup\"\n\"82\",\"morphing\"\n\"82\",\"menhir\"\n\"82\",\"lazylist\"\n\"82\",\"curly-brackets\"\n\"82\",\"ios7-statusbar\"\n\"82\",\"member-pointers\"\n\"82\",\"lov\"\n\"82\",\"storage-duration\"\n\"82\",\"custom-rom\"\n\"82\",\"stocktwits\"\n\"82\",\"log-level\"\n\"82\",\"react-leaflet-v3\"\n\"82\",\"pyvirtualdisplay\"\n\"82\",\"utf-16le\"\n\"82\",\"parentviewcontroller\"\n\"82\",\"bicubic\"\n\"82\",\"zcat\"\n\"82\",\"iirf\"\n\"82\",\"heartbleed-bug\"\n\"82\",\"ember-rails\"\n\"82\",\"heroku-ci\"\n\"82\",\"mqttnet\"\n\"82\",\"computability\"\n\"82\",\"webpack-hot-middleware\"\n\"82\",\"struts2-convention-plugin\"\n\"82\",\"illegalaccessexception\"\n\"82\",\"sun-codemodel\"\n\"82\",\"dalekjs\"\n\"82\",\"mule-connector\"\n\"82\",\"autogen\"\n\"82\",\"shelljs\"\n\"82\",\"flutter-hooks\"\n\"82\",\"zend-layout\"\n\"82\",\"spark-jdbc\"\n\"82\",\"mui-x-date-picker\"\n\"82\",\"beyondcompare3\"\n\"82\",\"subsonic2.2\"\n\"82\",\"zend-form2\"\n\"82\",\"pressure\"\n\"82\",\"authy\"\n\"82\",\"hdiv\"\n\"81\",\"deepl\"\n\"81\",\"flutter-canvas\"\n\"81\",\"ssreflect\"\n\"81\",\"deepface\"\n\"81\",\"deepstream.io\"\n\"81\",\"cncontactviewcontroller\"\n\"81\",\"multiple-insert\"\n\"81\",\"gitlab-pipelines\"\n\"81\",\"bash4\"\n\"81\",\"flexunit\"\n\"81\",\"phpmd\"\n\"81\",\"xsd-1.0\"\n\"81\",\"jazz\"\n\"81\",\"insmod\"\n\"81\",\"process-monitoring\"\n\"81\",\"feathers-sequelize\"\n\"81\",\"eclipse-3.4\"\n\"81\",\"clickhouse-client\"\n\"81\",\"listbox-control\"\n\"81\",\"apache-commons-lang\"\n\"81\",\"mup\"\n\"81\",\"jbossws\"\n\"81\",\"symbolic-computation\"\n\"81\",\"maptiler\"\n\"81\",\"selectoneradio\"\n\"81\",\"containskey\"\n\"81\",\"selectionmodel\"\n\"81\",\"platform-builder\"\n\"81\",\"syncdb\"\n\"81\",\"addressbookui\"\n\"81\",\"management-studio-express\"\n\"81\",\"cefpython\"\n\"81\",\"lambdify\"\n\"81\",\"packed\"\n\"81\",\"apache-samza\"\n\"81\",\"pandas-bokeh\"\n\"81\",\"fsi\"\n\"81\",\"ibm-jdk\"\n\"81\",\"putimagedata\"\n\"81\",\"sharedarraybuffer\"\n\"81\",\"django-facebook\"\n\"81\",\"vcxproj\"\n\"81\",\"route-me\"\n\"81\",\"wagmi\"\n\"81\",\"vader\"\n\"81\",\"rsa-archer-grc\"\n\"81\",\"windowinsets\"\n\"81\",\"meteor-collections\"\n\"81\",\"kif-framework\"\n\"81\",\"myob\"\n\"81\",\"pdf-annotations\"\n\"81\",\"jquery-bbq\"\n\"81\",\"suphp\"\n\"81\",\"eabi\"\n\"81\",\"android-soong\"\n\"81\",\"azure-devops-wiki\"\n\"81\",\"online-game\"\n\"81\",\"moleculer\"\n\"81\",\"pdf-to-html\"\n\"81\",\"invantive-sql\"\n\"81\",\"opencv-contrib\"\n\"81\",\"jzmq\"\n\"81\",\"opends\"\n\"81\",\"nsfilewrapper\"\n\"81\",\"openfaas\"\n\"81\",\"model-viewer\"\n\"81\",\"android-studio-4.1\"\n\"81\",\"apple-touch-icon\"\n\"81\",\"entity-framework-core-2.2\"\n\"81\",\"sample-size\"\n\"81\",\"typo3-12.x\"\n\"81\",\"osmosis\"\n\"81\",\"system.web.optimization\"\n\"81\",\"pyinotify\"\n\"81\",\"mindate\"\n\"81\",\"riemann\"\n\"81\",\"authenticity-token\"\n\"81\",\"ubuntu-unity\"\n\"81\",\"richeditbox\"\n\"81\",\".net-reflector\"\n\"81\",\"minhash\"\n\"81\",\"uddi\"\n\"81\",\"codenvy\"\n\"81\",\"sframe\"\n\"81\",\"syndication\"\n\"81\",\"magicsuggest\"\n\"81\",\"nx-workspace\"\n\"81\",\"cocoonjs\"\n\"81\",\"sha-3\"\n\"81\",\"homogenous-transformation\"\n\"81\",\"iterative-deepening\"\n\"81\",\"cactivedataprovider\"\n\"81\",\"ragel\"\n\"81\",\"pmdarima\"\n\"81\",\"nsunknownkeyexception\"\n\"81\",\"xbl\"\n\"81\",\"gwt-designer\"\n\"81\",\"expando\"\n\"81\",\"geckoview\"\n\"81\",\"sceneview\"\n\"81\",\"tdlib\"\n\"81\",\"azure-waf\"\n\"81\",\"hardware-security-module\"\n\"81\",\"gamlss\"\n\"81\",\"bulbs\"\n\"81\",\"nuxt-content\"\n\"81\",\"mkbundle\"\n\"81\",\"ploneformgen\"\n\"81\",\"modeladmin\"\n\"81\",\"angular2-animation\"\n\"81\",\"stm32ldiscovery\"\n\"81\",\"current-time\"\n\"81\",\"propertydescriptor\"\n\"81\",\"elapsed\"\n\"81\",\"comctl32\"\n\"81\",\"google-document-viewer\"\n\"81\",\"pythonw\"\n\"81\",\"personal-access-token\"\n\"81\",\"oggvorbis\"\n\"81\",\"odb\"\n\"81\",\"office-fabric\"\n\"81\",\"textmatching\"\n\"81\",\"persistence-unit\"\n\"81\",\"commandargument\"\n\"81\",\"iphone-3gs\"\n\"81\",\"omr\"\n\"81\",\"leadtools-sdk\"\n\"81\",\"parameterized-types\"\n\"81\",\"zest\"\n\"81\",\"partiql\"\n\"81\",\"hdbscan\"\n\"81\",\"bayeux\"\n\"81\",\"google-ima\"\n\"81\",\"flutter-isar\"\n\"81\",\"image-augmentation\"\n\"81\",\"qtembedded\"\n\"81\",\"mud\"\n\"81\",\"ilspy\"\n\"81\",\"partial-ordering\"\n\"81\",\"web-administration\"\n\"81\",\"font-awesome-4\"\n\"81\",\"gmpy\"\n\"81\",\"pre-compilation\"\n\"81\",\"qttest\"\n\"81\",\"preset\"\n\"81\",\"iiop\"\n\"80\",\"jenkins-x\"\n\"80\",\"print-css\"\n\"80\",\"jena-rules\"\n\"80\",\"babel-node\"\n\"80\",\"dbml\"\n\"80\",\"vue-devtools\"\n\"80\",\"php-gettext\"\n\"80\",\"live-preview\"\n\"80\",\"basehttprequesthandler\"\n\"80\",\"backbarbuttonitem\"\n\"80\",\"weinre\"\n\"80\",\"truecrypt\"\n\"80\",\"removable-storage\"\n\"80\",\"tree-balancing\"\n\"80\",\"backport\"\n\"80\",\"localreport\"\n\"80\",\"xsltforms\"\n\"80\",\"tree-conflict\"\n\"80\",\"xrdp\"\n\"80\",\"selectmanycheckbox\"\n\"80\",\"jsonbuilder\"\n\"80\",\"function-signature\"\n\"80\",\"pivottable.js\"\n\"80\",\"filetree\"\n\"80\",\"frisby.js\"\n\"80\",\"undocumented-behavior\"\n\"80\",\"json-value\"\n\"80\",\"python-stackless\"\n\"80\",\"django-commands\"\n\"80\",\"socketrocket\"\n\"80\",\"adaptive-threshold\"\n\"80\",\"xmldataprovider\"\n\"80\",\"mysqladmin\"\n\"80\",\"jparepository\"\n\"80\",\"dask-ml\"\n\"80\",\"fastly\"\n\"80\",\"psgi\"\n\"80\",\"react-vis\"\n\"80\",\"dash-bootstrap-components\"\n\"80\",\"databricks-workflows\"\n\"80\",\"callbackurl\"\n\"80\",\"crystal-reports-server\"\n\"80\",\"rouge\"\n\"80\",\"serializearray\"\n\"80\",\"uritemplate\"\n\"80\",\"optional-values\"\n\"80\",\"wagtail-snippet\"\n\"80\",\"win32exception\"\n\"80\",\"cryptostream\"\n\"80\",\"hreflang\"\n\"80\",\"coursera-api\"\n\"80\",\"grunt-contrib-requirejs\"\n\"80\",\"boost-signals\"\n\"80\",\"ionic-popup\"\n\"80\",\"invalidargumentexception\"\n\"80\",\"boost-mpi\"\n\"80\",\"twitter-login\"\n\"80\",\"onmouseup\"\n\"80\",\"spring-boot-security\"\n\"80\",\"azure-devtest-labs\"\n\"80\",\"inner-query\"\n\"80\",\"pdf2image\"\n\"80\",\"wsgen\"\n\"80\",\"jqueryform\"\n\"80\",\"wps\"\n\"80\",\"app-launcher\"\n\"80\",\"supabase-flutter\"\n\"80\",\"jxtreetable\"\n\"80\",\"jxtable\"\n\"80\",\"cost-based-optimizer\"\n\"80\",\"aquery\"\n\"80\",\"initrd\"\n\"80\",\"pci-bus\"\n\"80\",\"blazor-hybrid\"\n\"80\",\"wrapall\"\n\"80\",\"nestjs-jwt\"\n\"80\",\"sat-solvers\"\n\"80\",\"postman-newman\"\n\"80\",\"modin\"\n\"80\",\"ocpjp\"\n\"80\",\"system.printing\"\n\"80\",\"outerhtml\"\n\"80\",\"virtual-server\"\n\"80\",\"atmosphere.js\"\n\"80\",\"visual-studio-emulator\"\n\"80\",\"rewritemap\"\n\"80\",\"rfc2445\"\n\"80\",\"libp2p\"\n\"80\",\"osquery\"\n\"80\",\"typedescriptor\"\n\"80\",\"visual-studio-monaco\"\n\"80\",\"formatted-text\"\n\"80\",\"libmosquitto\"\n\"80\",\"kotlin-lateinit\"\n\"80\",\"bsc\"\n\"80\",\"rastervis\"\n\"80\",\"news-ticker\"\n\"80\",\"viewpage\"\n\"80\",\"javax.xml\"\n\"80\",\"system.drawing.imaging\"\n\"80\",\"javax.comm\"\n\"80\",\"code-climate\"\n\"80\",\"nutiteq\"\n\"80\",\"ti-nspire\"\n\"80\",\"referential-transparency\"\n\"80\",\"exitstatus\"\n\"80\",\"sqlresultsetmapping\"\n\"80\",\"hakyll\"\n\"80\",\"dotnet-publish\"\n\"80\",\"android-device-monitor\"\n\"80\",\"jacoco-plugin\"\n\"80\",\"directcompute\"\n\"80\",\"dropdownchoice\"\n\"80\",\"r2winbugs\"\n\"80\",\"bundle-install\"\n\"80\",\"itunes-app\"\n\"80\",\"asp.net-mvc-2-validation\"\n\"80\",\"hive-query\"\n\"80\",\"node-xmpp\"\n\"80\",\"spliterator\"\n\"80\",\"tex-live\"\n\"80\",\"cunit\"\n\"80\",\"colorbrewer\"\n\"80\",\"mozart\"\n\"80\",\"officedown\"\n\"80\",\"string-constant\"\n\"80\",\"acts-as-votable\"\n\"80\",\"dumpsys\"\n\"80\",\"gen-tcp\"\n\"80\",\"google-cloud-data-transfer\"\n\"80\",\"progressive\"\n\"80\",\"stofdoctrineextensions\"\n\"80\",\"ios8.4\"\n\"80\",\"angular-devkit\"\n\"80\",\"mplcursors\"\n\"80\",\"node-amqplib\"\n\"80\",\"cubic-bezier\"\n\"80\",\"qbwc\"\n\"80\",\"tfs-code-review\"\n\"80\",\"ietf-netconf\"\n\"80\",\"arscnview\"\n\"80\",\"cyberduck\"\n\"80\",\"focuslistener\"\n\"80\",\"git-show\"\n\"80\",\"trafficshaping\"\n\"80\",\"gnuwin32\"\n\"80\",\"compoundjs\"\n\"80\",\"id-generation\"\n\"80\",\"linestyle\"\n\"80\",\"ember-cli-addons\"\n\"80\",\"step-into\"\n\"80\",\"mediarecorder-api\"\n\"80\",\"z3c.form\"\n\"80\",\"endl\"\n\"80\",\"quercus\"\n\"80\",\"pre-authentication\"\n\"80\",\"sharp-snmp\"\n\"80\",\"shellexecuteex\"\n\"80\",\"maxscale\"\n\"80\",\"compass-lucene\"\n\"80\",\"mc\"\n\"80\",\"soundjs\"\n\"79\",\"math.sqrt\"\n\"79\",\"groupingby\"\n\"79\",\"grouped-table\"\n\"79\",\"installanywhere\"\n\"79\",\"program-files\"\n\"79\",\"flir\"\n\"79\",\"my.settings\"\n\"79\",\"vue-storefront\"\n\"79\",\"private-constructor\"\n\"79\",\"deflatestream\"\n\"79\",\"sitemapprovider\"\n\"79\",\"ssrs-2019\"\n\"79\",\"multistore\"\n\"79\",\"integrated\"\n\"79\",\"get-winevent\"\n\"79\",\"phash\"\n\"79\",\"map-files\"\n\"79\",\"chokidar\"\n\"79\",\"imp\"\n\"79\",\"child-theming\"\n\"79\",\"rustdoc\"\n\"79\",\"flask-oauthlib\"\n\"79\",\"symmetry\"\n\"79\",\"uniformgrid\"\n\"79\",\"flask-pymongo\"\n\"79\",\"contentful-management\"\n\"79\",\"flask-testing\"\n\"79\",\"contentful-api\"\n\"79\",\"vp9\"\n\"79\",\"cilk-plus\"\n\"79\",\"flamegraph\"\n\"79\",\"picturefill\"\n\"79\",\"socket.io-client\"\n\"79\",\"service-fabric-on-premises\"\n\"79\",\"pugjs\"\n\"79\",\"psi\"\n\"79\",\"windows-phone-7-emulator\"\n\"79\",\"agile-project-management\"\n\"79\",\"pyalgotrade\"\n\"79\",\"reader-monad\"\n\"79\",\"crosstool-ng\"\n\"79\",\"pycparser\"\n\"79\",\"cardano\"\n\"79\",\"robotlegs\"\n\"79\",\"databricks-cli\"\n\"79\",\"django-filer\"\n\"79\",\"readerwriterlockslim\"\n\"79\",\"rtcp\"\n\"79\",\"avvideocomposition\"\n\"79\",\"mysql-error-1241\"\n\"79\",\"updatesourcetrigger\"\n\"79\",\"industrial\"\n\"79\",\"jpl\"\n\"79\",\"dnu\"\n\"79\",\"window-messages\"\n\"79\",\"gpath\"\n\"79\",\"agora\"\n\"79\",\"wikitext\"\n\"79\",\"jpm\"\n\"79\",\"simplexmlrpcserver\"\n\"79\",\"capacity-planning\"\n\"79\",\"aws-credentials\"\n\"79\",\"momentum\"\n\"79\",\"navigationitem\"\n\"79\",\"inputbinding\"\n\"79\",\"pbo\"\n\"79\",\"postal\"\n\"79\",\"kendo-angular-ui\"\n\"79\",\"interval-tree\"\n\"79\",\"bottomsheetdialogfragment\"\n\"79\",\"jquery-scrollable\"\n\"79\",\"swift3.2\"\n\"79\",\"nsformatter\"\n\"79\",\"dynamic-pages\"\n\"79\",\"invokedynamic\"\n\"79\",\"postgresql-extensions\"\n\"79\",\"syndication-feed\"\n\"79\",\"libconfig\"\n\"79\",\"domo\"\n\"79\",\"objective-sharpie\"\n\"79\",\"word-list\"\n\"79\",\"java-opts\"\n\"79\",\"amazon-systems-manager\"\n\"79\",\"libbpf\"\n\"79\",\"system-sounds\"\n\"79\",\"luke\"\n\"79\",\"mailman\"\n\"79\",\"extjs-stores\"\n\"79\",\"anchorpoint\"\n\"79\",\"external-sorting\"\n\"79\",\"system.io.fileinfo\"\n\"79\",\"winsockets\"\n\"79\",\"sfdoctrineguard\"\n\"79\",\"virtual-earth\"\n\"79\",\"overflowexception\"\n\"79\",\"android-espresso-recorder\"\n\"79\",\"aspdotnetstorefront\"\n\"79\",\"tanstack-table\"\n\"79\",\"gcc5\"\n\"79\",\"xamarin.ios-binding\"\n\"79\",\"azure-information-protection\"\n\"79\",\"pnpm-workspace\"\n\"79\",\"handbrake\"\n\"79\",\"noise-generator\"\n\"79\",\"numba-pro\"\n\"79\",\"timelapse\"\n\"79\",\"exponential-backoff\"\n\"79\",\"mklocalsearch\"\n\"79\",\"drillthrough\"\n\"79\",\"noflo\"\n\"79\",\"conversion-tracking\"\n\"79\",\"doubleanimation\"\n\"79\",\"android-handlerthread\"\n\"79\",\"openglcontext\"\n\"79\",\"ischecked\"\n\"79\",\"asp.net-bundling\"\n\"79\",\"railsapps\"\n\"79\",\"dropbox-sdk\"\n\"79\",\"convert-tz\"\n\"79\",\"tcltk\"\n\"79\",\"ojalgo\"\n\"79\",\"custom-activity\"\n\"79\",\"moodle-mobile\"\n\"79\",\"qkeyevent\"\n\"79\",\"text-to-column\"\n\"79\",\"string-agg\"\n\"79\",\"qgroupbox\"\n\"79\",\"ctc\"\n\"79\",\"lostfocus\"\n\"79\",\"http-status\"\n\"79\",\"eunit\"\n\"79\",\"request-validation\"\n\"79\",\"activepivot\"\n\"79\",\"acquia\"\n\"79\",\"testautomationfx\"\n\"79\",\"promotion-code\"\n\"79\",\"angular-custom-validators\"\n\"79\",\"lookbackapi\"\n\"79\",\"spotfire-analyst\"\n\"79\",\"maven-cobertura-plugin\"\n\"79\",\"static-html\"\n\"79\",\"multikey\"\n\"79\",\"vaadin4spring\"\n\"79\",\"automoq\"\n\"79\",\"gnus\"\n\"79\",\"daemonset\"\n\"79\",\"imagebackground\"\n\"79\",\"embedded-kafka\"\n\"79\",\"prefix-sum\"\n\"79\",\"google-maps-mobile\"\n\"79\",\"focusable\"\n\"79\",\"gmsplacepicker\"\n\"79\",\"glkview\"\n\"79\",\"spark-ui\"\n\"79\",\"fody-propertychanged\"\n\"79\",\"gmaps.js\"\n\"78\",\"multiple-input\"\n\"78\",\"fileappender\"\n\"78\",\"sjcl\"\n\"78\",\"mvvm-toolkit\"\n\"78\",\"react-scroll\"\n\"78\",\"vuejs-slots\"\n\"78\",\"jayway\"\n\"78\",\"effective-c++\"\n\"78\",\"declared-property\"\n\"78\",\"jcuda\"\n\"78\",\"skype4py\"\n\"78\",\"anonymize\"\n\"78\",\"declarative-programming\"\n\"78\",\"chrome-extension-manifest-v2\"\n\"78\",\"connectexception\"\n\"78\",\"next-link\"\n\"78\",\"python-gitlab\"\n\"78\",\"apache-storm-topology\"\n\"78\",\"chrome-gcm\"\n\"78\",\"ngx-pagination\"\n\"78\",\"smoothstate.js\"\n\"78\",\"selectpdf\"\n\"78\",\"syncfusion-chart\"\n\"78\",\"unbound\"\n\"78\",\"pipenv-install\"\n\"78\",\"adaptor\"\n\"78\",\"uiviewcontrollerrepresentable\"\n\"78\",\"unidac\"\n\"78\",\"window.onunload\"\n\"78\",\"name-collision\"\n\"78\",\"akka.net-cluster\"\n\"78\",\"puppeteer-cluster\"\n\"78\",\"windowserror\"\n\"78\",\"nanomsg\"\n\"78\",\"cs3\"\n\"78\",\"django-url-reverse\"\n\"78\",\"roboto\"\n\"78\",\"rsk\"\n\"78\",\"servlet-container\"\n\"78\",\"csslint\"\n\"78\",\"django-shell\"\n\"78\",\"keyset\"\n\"78\",\"oprofile\"\n\"78\",\"icefaces-1.8\"\n\"78\",\"csg\"\n\"78\",\"roaming\"\n\"78\",\"nsd\"\n\"78\",\"onmouseclick\"\n\"78\",\"boilerpipe\"\n\"78\",\"dynamic-usercontrols\"\n\"78\",\"jquery-mobile-popup\"\n\"78\",\"desktop-app-converter\"\n\"78\",\"savepoints\"\n\"78\",\"dynamic-class-creation\"\n\"78\",\"jquery-1.7\"\n\"78\",\"dynamic-function\"\n\"78\",\"hpcc-ecl\"\n\"78\",\"appjs\"\n\"78\",\"blockquote\"\n\"78\",\"gulp-imagemin\"\n\"78\",\"jupyter-contrib-nbextensions\"\n\"78\",\"tttattributedlabel\"\n\"78\",\"samsung-knox\"\n\"78\",\"pdfrw\"\n\"78\",\"mollie\"\n\"78\",\"wsod\"\n\"78\",\"azman\"\n\"78\",\"aws-vpn\"\n\"78\",\"couchdb-python\"\n\"78\",\"wrl\"\n\"78\",\"turbo-rails\"\n\"78\",\"go-chi\"\n\"78\",\"codex\"\n\"78\",\"vine\"\n\"78\",\"brakeman\"\n\"78\",\"objective-j\"\n\"78\",\"legacy-sql\"\n\"78\",\"foundry-slate\"\n\"78\",\"orangehrm\"\n\"78\",\"winsound\"\n\"78\",\"midp-2.0\"\n\"78\",\"wm-paint\"\n\"78\",\"lf\"\n\"78\",\"browser-security\"\n\"78\",\"acaccount\"\n\"78\",\"ubuntu-17.04\"\n\"78\",\"diamond-operator\"\n\"78\",\"tokyo-cabinet\"\n\"78\",\"c#-11.0\"\n\"78\",\"timeline.js\"\n\"78\",\"numpy-random\"\n\"78\",\"gwt-editors\"\n\"78\",\"mlxtend\"\n\"78\",\"byte-shifting\"\n\"78\",\"xcode-workspace\"\n\"78\",\"mmapi\"\n\"78\",\"gwtbootstrap3\"\n\"78\",\"ply-file-format\"\n\"78\",\"hammerspoon\"\n\"78\",\"tink\"\n\"78\",\"reentrantreadwritelock\"\n\"78\",\"timthumb\"\n\"78\",\"noncopyable\"\n\"78\",\"uiinclude\"\n\"78\",\"sqlcachedependency\"\n\"78\",\"b2\"\n\"78\",\"android-ndk-r5\"\n\"78\",\"property-placeholder\"\n\"78\",\"custom-build-step\"\n\"78\",\"communication-protocol\"\n\"78\",\"google-cloud-error-reporting\"\n\"78\",\"mpremotecommandcenter\"\n\"78\",\"esqueleto\"\n\"78\",\"actions-builder\"\n\"78\",\"qquickitem\"\n\"78\",\"geographic-distance\"\n\"78\",\"android-log\"\n\"78\",\"cgeventtap\"\n\"78\",\"perforce-stream\"\n\"78\",\"ex\"\n\"78\",\"http-conduit\"\n\"78\",\"lpr\"\n\"78\",\"texttrimming\"\n\"78\",\"zenject\"\n\"78\",\"google-sites-2016\"\n\"78\",\"mqtt-vernemq\"\n\"78\",\"autodesk-construction-cloud\"\n\"78\",\"mscapi\"\n\"78\",\"subforms\"\n\"78\",\"subpixel\"\n\"78\",\"qtandroidextras\"\n\"78\",\"d3plus\"\n\"78\",\"spannablestringbuilder\"\n\"78\",\"subresource-integrity\"\n\"78\",\"md-select\"\n\"78\",\"zend-router\"\n\"78\",\"allegrograph\"\n\"78\",\"tooling\"\n\"78\",\"prawnto\"\n\"78\",\"solr8\"\n\"78\",\"flycheck\"\n\"78\",\"presentation-layer\"\n\"78\",\"msbuildextensionpack\"\n\"77\",\"mate\"\n\"77\",\"wicketstuff\"\n\"77\",\"instaloader\"\n\"77\",\"skins\"\n\"77\",\"weakhashmap\"\n\"77\",\"tensorflow-transform\"\n\"77\",\"react-native-iap\"\n\"77\",\"yajl\"\n\"77\",\"jberet\"\n\"77\",\"mutated\"\n\"77\",\"eclipse-3.5\"\n\"77\",\"maui-android\"\n\"77\",\"webshim\"\n\"77\",\"fibonacci-heap\"\n\"77\",\"pagecontrol\"\n\"77\",\"xmlgregoriancalendar\"\n\"77\",\"check-constraint\"\n\"77\",\"jspsych\"\n\"77\",\"celementtree\"\n\"77\",\"jscs\"\n\"77\",\"next-redux-wrapper\"\n\"77\",\"python-mss\"\n\"77\",\"sendmail.exe\"\n\"77\",\"selenium-webdriver-python\"\n\"77\",\"filter-driver\"\n\"77\",\"packaged-task\"\n\"77\",\"packrat\"\n\"77\",\"pic24\"\n\"77\",\"address-space\"\n\"77\",\"appcenter\"\n\"77\",\"data-vault\"\n\"77\",\"constructor-chaining\"\n\"77\",\"wasm-pack\"\n\"77\",\"angular-template-form\"\n\"77\",\"kendo-treelist\"\n\"77\",\"mysql-error-1242\"\n\"77\",\"kiln\"\n\"77\",\"rolling-sum\"\n\"77\",\"r-portfolioanalytics\"\n\"77\",\"data-comparison\"\n\"77\",\"rp2040\"\n\"77\",\"servlet-mapping\"\n\"77\",\"graniteds\"\n\"77\",\"microsoft-edge-chromium\"\n\"77\",\"type-constructor\"\n\"77\",\"scalajs-react\"\n\"77\",\"core-elements\"\n\"77\",\"jqtree\"\n\"77\",\"mojolicious-lite\"\n\"77\",\"io-completion-ports\"\n\"77\",\"desiredcapabilities\"\n\"77\",\"html-tag-details\"\n\"77\",\"spring-integration-amqp\"\n\"77\",\"jquery-droppable\"\n\"77\",\"nsbuttoncell\"\n\"77\",\"junit-rule\"\n\"77\",\"k\"\n\"77\",\"pcregrep\"\n\"77\",\"jupyter-kernel\"\n\"77\",\"blogger-dynamic-views\"\n\"77\",\"onmeasure\"\n\"77\",\"aqgridview\"\n\"77\",\"port-number\"\n\"77\",\"reverse-ajax\"\n\"77\",\"attachment-fu\"\n\"77\",\"bufferedoutputstream\"\n\"77\",\"magnification\"\n\"77\",\"rational-rsa\"\n\"77\",\"rcs\"\n\"77\",\"netbios\"\n\"77\",\"analytics.js\"\n\"77\",\"seven-segment-display\"\n\"77\",\"vim-airline\"\n\"77\",\"jaro-winkler\"\n\"77\",\"build-time\"\n\"77\",\"ocamlfind\"\n\"77\",\"lemon\"\n\"77\",\"taglist\"\n\"77\",\"redigo\"\n\"77\",\"expression-encoder\"\n\"77\",\"dpi-aware\"\n\"77\",\"gaps-in-data\"\n\"77\",\"butterworth\"\n\"77\",\"notimplementedexception\"\n\"77\",\"plone-5.x\"\n\"77\",\"asp.net-webcontrol\"\n\"77\",\"dsharp+\"\n\"77\",\"halting-problem\"\n\"77\",\"hidpi\"\n\"77\",\"caf\"\n\"77\",\"dspic\"\n\"77\",\"azurekinect\"\n\"77\",\"ragged\"\n\"77\",\"task-runner-explorer\"\n\"77\",\"highland.js\"\n\"77\",\"aspects\"\n\"77\",\"opengrok\"\n\"77\",\"scala-macro-paradise\"\n\"77\",\"cookiejar\"\n\"77\",\"npm-registry\"\n\"77\",\"coloranimation\"\n\"77\",\"elasticsearch-bulk-api\"\n\"77\",\"nitro\"\n\"77\",\"commando\"\n\"77\",\"react-native-chart-kit\"\n\"77\",\"eventqueue\"\n\"77\",\"one2many\"\n\"77\",\"stringwriter\"\n\"77\",\"tomcat-valve\"\n\"77\",\"genshi\"\n\"77\",\"qmodelindex\"\n\"77\",\"google-drive-picker\"\n\"77\",\"custom-code\"\n\"77\",\"geometric-arc\"\n\"77\",\"prometheus-pushgateway\"\n\"77\",\"mouse-picking\"\n\"77\",\"httpcontent\"\n\"77\",\"testng-annotation-test\"\n\"77\",\"ipad-mini\"\n\"77\",\"angular-factory\"\n\"77\",\"com-automation\"\n\"77\",\"acoustics\"\n\"77\",\"array-indexing\"\n\"77\",\"suid\"\n\"77\",\"urn\"\n\"77\",\"qt-mobility\"\n\"77\",\"threadx\"\n\"77\",\"idiorm\"\n\"77\",\"hg-git\"\n\"77\",\"webpack.config.js\"\n\"77\",\"iio\"\n\"77\",\"dagre\"\n\"77\",\"complex-data-types\"\n\"77\",\"statelistdrawable\"\n\"77\",\"ignition\"\n\"77\",\"darkflow\"\n\"77\",\"lineargradientbrush\"\n\"77\",\"areas\"\n\"77\",\"fmp4\"\n\"76\",\"flume-twitter\"\n\"76\",\"react-native-track-player\"\n\"76\",\"multiple-repositories\"\n\"76\",\"gitlab-runner\"\n\"76\",\"traversable\"\n\"76\",\"ballerina-swan-lake\"\n\"76\",\"gridjs\"\n\"76\",\"jcodec\"\n\"76\",\"graphics32\"\n\"76\",\"fenwick-tree\"\n\"76\",\"whiteboard\"\n\"76\",\"princexml\"\n\"76\",\"grecaptcha\"\n\"76\",\"private-pub\"\n\"76\",\"marshalbyrefobject\"\n\"76\",\"tendermint\"\n\"76\",\"interactive-shell\"\n\"76\",\"release-builds\"\n\"76\",\"skflow\"\n\"76\",\"transloadit\"\n\"76\",\"tryton\"\n\"76\",\"prime31\"\n\"76\",\"swiftui-view\"\n\"76\",\"adp\"\n\"76\",\"voltrb\"\n\"76\",\"python-elixir\"\n\"76\",\"db-first\"\n\"76\",\"vmware-server\"\n\"76\",\"chromedp\"\n\"76\",\"softhsm\"\n\"76\",\"connection-leaks\"\n\"76\",\"circos\"\n\"76\",\"json-lib\"\n\"76\",\"advapi32\"\n\"76\",\"app-certification-kit\"\n\"76\",\"xla\"\n\"76\",\"picocontainer\"\n\"76\",\"uitabview\"\n\"76\",\"soaplite\"\n\"76\",\"soappy\"\n\"76\",\"sencha-touch-2.3\"\n\"76\",\"data-oriented-design\"\n\"76\",\"fully-qualified-naming\"\n\"76\",\"vanity-url\"\n\"76\",\"singleton-methods\"\n\"76\",\"servicemanager\"\n\"76\",\"sitecore-ecm\"\n\"76\",\"ibrokers\"\n\"76\",\"robolectric-gradle-plugin\"\n\"76\",\"awkward-array\"\n\"76\",\"waffle-chart\"\n\"76\",\"go-redis\"\n\"76\",\"gpgme\"\n\"76\",\"graceful-shutdown\"\n\"76\",\"goquery\"\n\"76\",\"document-oriented-db\"\n\"76\",\"pub.dev\"\n\"76\",\"microsoft-file-explorer\"\n\"76\",\"dnvm\"\n\"76\",\"go-to-definition\"\n\"76\",\"aws-http-api\"\n\"76\",\"createquery\"\n\"76\",\"django-inheritance\"\n\"76\",\"simpledom\"\n\"76\",\"mylocationoverlay\"\n\"76\",\"r-ranger\"\n\"76\",\"carbon-design-system\"\n\"76\",\"hspec\"\n\"76\",\"wufoo\"\n\"76\",\"appmobi\"\n\"76\",\"grunt-contrib-cssmin\"\n\"76\",\"scala-2.12\"\n\"76\",\"nsfont\"\n\"76\",\"powerapps-selected-items\"\n\"76\",\"axios-mock-adapter\"\n\"76\",\"dynamic-properties\"\n\"76\",\"cox\"\n\"76\",\"eaaccessory\"\n\"76\",\"boost-lambda\"\n\"76\",\"jquery-append\"\n\"76\",\"android-update-app\"\n\"76\",\"infopath-forms-services\"\n\"76\",\"dx\"\n\"76\",\"wlanapi\"\n\"76\",\"extracttextwebpackplugin\"\n\"76\",\"pyobject\"\n\"76\",\"ringtonemanager\"\n\"76\",\"coerce\"\n\"76\",\"javascript-databinding\"\n\"76\",\"bufferstrategy\"\n\"76\",\"javapackager\"\n\"76\",\"javascript-api-for-office\"\n\"76\",\"broken-image\"\n\"76\",\"newid\"\n\"76\",\"typescript1.7\"\n\"76\",\"gomock\"\n\"76\",\"vimperator\"\n\"76\",\"raiseevent\"\n\"76\",\"libalsa\"\n\"76\",\"code-migration\"\n\"76\",\"pytest-fixtures\"\n\"76\",\"domc\"\n\"76\",\"aura-framework\"\n\"76\",\"400-bad-request\"\n\"76\",\"polymer-cli\"\n\"76\",\"nodevalue\"\n\"76\",\"timesten\"\n\"76\",\"cordova-plugin-proguard\"\n\"76\",\"uikeyboardtype\"\n\"76\",\"c1flexgrid\"\n\"76\",\"target-sdk\"\n\"76\",\"gamecontroller\"\n\"76\",\"node-set\"\n\"76\",\"radare2\"\n\"76\",\"gcc7\"\n\"76\",\"drawingcontext\"\n\"76\",\"assetslibrary\"\n\"76\",\"azure-git-deployment\"\n\"76\",\"expired-sessions\"\n\"76\",\"memento\"\n\"76\",\"laravel-snappy\"\n\"76\",\"iqr\"\n\"76\",\"androidpdfviewer\"\n\"76\",\"nmf\"\n\"76\",\"electron-updater\"\n\"76\",\"node-debugger\"\n\"76\",\"iostat\"\n\"76\",\"com4j\"\n\"76\",\"ipcrenderer\"\n\"76\",\"react-google-login\"\n\"76\",\"actionpack\"\n\"76\",\"node-csv-parse\"\n\"76\",\"proxysql\"\n\"76\",\"texmaker\"\n\"76\",\"ironpdf\"\n\"76\",\"elasticsearch-watcher\"\n\"76\",\"c-str\"\n\"76\",\"project-settings\"\n\"76\",\"react-dom-server\"\n\"76\",\"oltp\"\n\"76\",\"longtable\"\n\"76\",\"scriptresource.axd\"\n\"76\",\"identity-aware-proxy\"\n\"76\",\"stdafx.h\"\n\"76\",\"global-temp-tables\"\n\"76\",\"image-file\"\n\"76\",\"zend-server-ce\"\n\"76\",\"flutter-future\"\n\"76\",\"web-application-design\"\n\"76\",\"msr\"\n\"76\",\"google-style-guide\"\n\"76\",\"iis-logs\"\n\"76\",\"igrouping\"\n\"76\",\"arq\"\n\"76\",\"tga\"\n\"76\",\"arpack\"\n\"76\",\"concurrent-processing\"\n\"76\",\"powerbi-api\"\n\"76\",\"touchimageview\"\n\"76\",\"compound-key\"\n\"76\",\"quantlib-swig\"\n\"76\",\"thinktecture\"\n\"76\",\"parsekit\"\n\"76\",\"seal\"\n\"76\",\"prefuse\"\n\"76\",\"thesaurus\"\n\"76\",\"helmfile\"\n\"76\",\"subsystem\"\n\"76\",\"ilasm\"\n\"76\",\"autodesk-navisworks\"\n\"76\",\"thin-client\"\n\"75\",\"cllocationdistance\"\n\"75\",\"clearscript\"\n\"75\",\"grel\"\n\"75\",\"graphical-layout-editor\"\n\"75\",\"ggfortify\"\n\"75\",\"featuretoggle\"\n\"75\",\"react-native-sound\"\n\"75\",\"ansible-module\"\n\"75\",\"antbuilder\"\n\"75\",\"sling-models\"\n\"75\",\"sktextureatlas\"\n\"75\",\"ssh-config\"\n\"75\",\"echosign\"\n\"75\",\"github3.py\"\n\"75\",\"interceptorstack\"\n\"75\",\"easyrtc\"\n\"75\",\"xquery-update\"\n\"75\",\"weak-linking\"\n\"75\",\"apache-commons-collection\"\n\"75\",\"groff\"\n\"75\",\"sql-server-migration-assi\"\n\"75\",\"indesign-server\"\n\"75\",\"fileresult\"\n\"75\",\"front-camera\"\n\"75\",\"jstat\"\n\"75\",\"catransaction\"\n\"75\",\"package-development\"\n\"75\",\"self-updating\"\n\"75\",\"fileprovider\"\n\"75\",\"cdma\"\n\"75\",\"fixnum\"\n\"75\",\"fileversioninfo\"\n\"75\",\"rubinius\"\n\"75\",\"doctrine-mongodb\"\n\"75\",\"airconsole\"\n\"75\",\"kendo-datetimepicker\"\n\"75\",\"aws-data-pipeline\"\n\"75\",\"dnssec\"\n\"75\",\"rosetta\"\n\"75\",\"windows-phone-store\"\n\"75\",\"dockpanel-suite\"\n\"75\",\"windows-phone-8-sdk\"\n\"75\",\"grpc-gateway\"\n\"75\",\"simpleaudioengine\"\n\"75\",\"neopixel\"\n\"75\",\"defragmentation\"\n\"75\",\"entity-framework-plus\"\n\"75\",\"jquery-filter\"\n\"75\",\"gtkbuilder\"\n\"75\",\"popup-blocker\"\n\"75\",\"jquery-mask\"\n\"75\",\"jquery-mobile-button\"\n\"75\",\"boost-iterators\"\n\"75\",\"interleave\"\n\"75\",\"openebs\"\n\"75\",\"bonfire\"\n\"75\",\"internalsvisibleto\"\n\"75\",\"spring-filter\"\n\"75\",\"azure-database-mysql\"\n\"75\",\"native-sql\"\n\"75\",\"html-safe\"\n\"75\",\"workgroup\"\n\"75\",\"nerddinner\"\n\"75\",\"bounded-types\"\n\"75\",\"visual-studio-test-runner\"\n\"75\",\"visual-styles\"\n\"75\",\"lego-mindstorms-ev3\"\n\"75\",\"range-based-loop\"\n\"75\",\"mistral-7b\"\n\"75\",\"jawbone\"\n\"75\",\"cocossharp\"\n\"75\",\"domain-mapping\"\n\"75\",\"pyffmpeg\"\n\"75\",\"code-size\"\n\"75\",\"fragment-caching\"\n\"75\",\"orientjs\"\n\"75\",\"magnitude\"\n\"75\",\".net-standard-2.1\"\n\"75\",\"video-watermarking\"\n\"75\",\"coinmarketcap\"\n\"75\",\"objective-function\"\n\"75\",\"mirth-connect\"\n\"75\",\"setbounds\"\n\"75\",\"netconnection\"\n\"75\",\"library-path\"\n\"75\",\"openpgp.js\"\n\"75\",\"azure-sas\"\n\"75\",\"explicit-specialization\"\n\"75\",\"nosuchfileexception\"\n\"75\",\"cordova-admob\"\n\"75\",\"openhtmltopdf\"\n\"75\",\"pls-00103\"\n\"75\",\"italics\"\n\"75\",\"uiprintinteractioncntrler\"\n\"75\",\"gcj\"\n\"75\",\"businessworks\"\n\"75\",\"vertex-attributes\"\n\"75\",\"gce-instance-group\"\n\"75\",\"gamma-function\"\n\"75\",\"quickgraph\"\n\"75\",\"uicollectionviewdiffabledatasource\"\n\"75\",\"tao\"\n\"75\",\"dictation\"\n\"75\",\"xbind\"\n\"75\",\"android-flexboxlayout\"\n\"75\",\"uint32-t\"\n\"75\",\"dotpeek\"\n\"75\",\"hill-climbing\"\n\"75\",\"azure-linux\"\n\"75\",\"octokit.net\"\n\"75\",\"exasolution\"\n\"75\",\"http-chunked\"\n\"75\",\"cflags\"\n\"75\",\"android-room-relation\"\n\"75\",\"android-powermanager\"\n\"75\",\"resume-download\"\n\"75\",\"elassandra\"\n\"75\",\"message-handlers\"\n\"75\",\"qmail\"\n\"75\",\"ipad-3\"\n\"75\",\"react-google-recaptcha\"\n\"75\",\"r-dygraphs\"\n\"75\",\"logql\"\n\"75\",\"spooler\"\n\"75\",\"leadfoot\"\n\"75\",\"google-closure-templates\"\n\"75\",\"qhull\"\n\"75\",\"resharper-7.1\"\n\"75\",\"qsharedpointer\"\n\"75\",\"complexheatmap\"\n\"75\",\"foregroundnotification\"\n\"75\",\"cvc4\"\n\"75\",\"toolstripdropdown\"\n\"75\",\"tform\"\n\"75\",\"shedlock\"\n\"75\",\"computer-name\"\n\"75\",\"startapp\"\n\"75\",\"automatonymous\"\n\"75\",\"webkitaudiocontext\"\n\"75\",\"heif\"\n\"75\",\"autovivification\"\n\"75\",\"theforeman\"\n\"75\",\"solr-schema\"\n\"75\",\"aria2\"\n\"75\",\"trackball\"\n\"75\",\"arduino-nano\"\n\"75\",\"dart-analyzer\"\n\"75\",\"pandoc-citeproc\"\n\"75\",\"spark-hive\"\n\"75\",\"qtlocation\"\n\"75\",\"web-analytics-tools\"\n\"74\",\"apache-commons-digester\"\n\"74\",\"jdk1.4\"\n\"74\",\"github-packages\"\n\"74\",\"yahoo-oauth\"\n\"74\",\"tensorflow1.15\"\n\"74\",\"tsconfig-paths\"\n\"74\",\"reporting-services-2012\"\n\"74\",\"sslengine\"\n\"74\",\"fieldtype\"\n\"74\",\"lirc\"\n\"74\",\"listtile\"\n\"74\",\"sqr\"\n\"74\",\"file-import\"\n\"74\",\"babel-plugin\"\n\"74\",\"teamcity-7.1\"\n\"74\",\"skphysicsjoint\"\n\"74\",\"badpaddingexception\"\n\"74\",\"giphy-api\"\n\"74\",\"templavoila\"\n\"74\",\"eeglab\"\n\"74\",\"jedi-vim\"\n\"74\",\"giphy\"\n\"74\",\"react-native-onesignal\"\n\"74\",\"rust-tracing\"\n\"74\",\"nfsclient\"\n\"74\",\"file-organization\"\n\"74\",\"adjustpan\"\n\"74\",\"data-paging\"\n\"74\",\"python-iris\"\n\"74\",\"ownership-semantics\"\n\"74\",\"bit-depth\"\n\"74\",\"apache-nifi-registry\"\n\"74\",\"jsmpp\"\n\"74\",\"fw1\"\n\"74\",\"advanced-rest-client\"\n\"74\",\"pairwise.wilcox.test\"\n\"74\",\"filterrific\"\n\"74\",\"safe-browsing\"\n\"74\",\"dayofmonth\"\n\"74\",\"flask-uploads\"\n\"74\",\"select-for-update\"\n\"74\",\"cheetah\"\n\"74\",\"selection-api\"\n\"74\",\"pinojs\"\n\"74\",\"firephp\"\n\"74\",\"container-registry\"\n\"74\",\"pim\"\n\"74\",\"kura\"\n\"74\",\"josephus\"\n\"74\",\"docker-maven-plugin\"\n\"74\",\"rparallel\"\n\"74\",\"aiortc\"\n\"74\",\"dng\"\n\"74\",\"crm-ribbon-workbench\"\n\"74\",\"kepler.gl\"\n\"74\",\"robocode\"\n\"74\",\"aws-nat-gateway\"\n\"74\",\"facebook-test-users\"\n\"74\",\"r-stars\"\n\"74\",\"rle\"\n\"74\",\"value-categories\"\n\"74\",\"gperftools\"\n\"74\",\"avcomposition\"\n\"74\",\"vector-search\"\n\"74\",\"azure-disk\"\n\"74\",\"jquery-ui-widget-factory\"\n\"74\",\"mod-fastcgi\"\n\"74\",\"gsuite-addons\"\n\"74\",\"svprogresshud\"\n\"74\",\"gulp-karma\"\n\"74\",\"onupdate\"\n\"74\",\"bounding\"\n\"74\",\"nsentitydescription\"\n\"74\",\"spring-batch-job-monitoring\"\n\"74\",\"pebble-js\"\n\"74\",\"cp1251\"\n\"74\",\"dependency-walker\"\n\"74\",\"initial-context\"\n\"74\",\"ensime\"\n\"74\",\"depends\"\n\"74\",\"bosun\"\n\"74\",\"app-offline.htm\"\n\"74\",\"desolve\"\n\"74\",\"poodle-attack\"\n\"74\",\"nsmetadataquery\"\n\"74\",\"destruction\"\n\"74\",\"nsmutableset\"\n\"74\",\"dynamic-ip\"\n\"74\",\"sign-extension\"\n\"74\",\"magnetic-cards\"\n\"74\",\"shadow-root\"\n\"74\",\"virtualfilesystem\"\n\"74\",\"buffer-overrun\"\n\"74\",\".net-client-profile\"\n\"74\",\"miniprofiler\"\n\"74\",\"kotlinpoet\"\n\"74\",\"libm\"\n\"74\",\"mit-kerberos\"\n\"74\",\"external-script\"\n\"74\",\"network-shares\"\n\"74\",\"libgcrypt\"\n\"74\",\"objloader\"\n\"74\",\"code-separation\"\n\"74\",\"breeze-sharp\"\n\"74\",\"audio-capture\"\n\"74\",\"java-wireless-toolkit\"\n\"74\",\"rethrow\"\n\"74\",\"google-apps-for-education\"\n\"74\",\"tacit-programming\"\n\"74\",\"sqlite-net-pcl\"\n\"74\",\"refcell\"\n\"74\",\"openvas\"\n\"74\",\"aspmenu\"\n\"74\",\"azure-synapse-pipeline\"\n\"74\",\"scaletype\"\n\"74\",\"pocketbase\"\n\"74\",\"aspnet-contrib\"\n\"74\",\"azure-function-app-proxy\"\n\"74\",\"jail\"\n\"74\",\"tinyioc\"\n\"74\",\"itemdatabound\"\n\"74\",\"nsxml\"\n\"74\",\"screencast\"\n\"74\",\"dsl-tools\"\n\"74\",\"drupal-exposed-filter\"\n\"74\",\"sql-server-ce-3.5\"\n\"74\",\"guptateamdeveloper\"\n\"74\",\"taps\"\n\"74\",\"tampering\"\n\"74\",\"openmesh\"\n\"74\",\"speechsynthesizer\"\n\"74\",\"lockbox-3\"\n\"74\",\"mouse-coordinates\"\n\"74\",\"google-email-settings-api\"\n\"74\",\"android-jetifier\"\n\"74\",\"custom-configuration\"\n\"74\",\"persian-calendar\"\n\"74\",\"qframe\"\n\"74\",\"ltac\"\n\"74\",\"strassen\"\n\"74\",\"memory-footprint\"\n\"74\",\"android-listadapter\"\n\"74\",\"meetup\"\n\"74\",\"proteus\"\n\"74\",\"cfbundleidentifier\"\n\"74\",\"perl-hash\"\n\"74\",\"android-market-filtering\"\n\"74\",\"custom-dimensions\"\n\"74\",\"get-display-media\"\n\"74\",\"excel-import\"\n\"74\",\"oidc-client\"\n\"74\",\"common-crawl\"\n\"74\",\"cgcontextdrawimage\"\n\"74\",\"acts-as-list\"\n\"74\",\"accepts-nested-attributes\"\n\"74\",\"userfrosting\"\n\"74\",\"flymake\"\n\"74\",\"user-friendly\"\n\"74\",\"qudpsocket\"\n\"74\",\"total.js\"\n\"74\",\"qtextcursor\"\n\"74\",\"tput\"\n\"74\",\"gmsh\"\n\"74\",\"cwnd\"\n\"74\",\"mediasoup\"\n\"74\",\"torchaudio\"\n\"74\",\"line-count\"\n\"74\",\"subtlecrypto\"\n\"74\",\"statet\"\n\"74\",\"google-tag-manager-server-side\"\n\"74\",\"foldable\"\n\"74\",\"parceler\"\n\"74\",\"image-enhancement\"\n\"74\",\"custom-sort\"\n\"74\",\"sdlc\"\n\"74\",\"composite-types\"\n\"74\",\"zinnia\"\n\"74\",\"stellar.js\"\n\"74\",\"webdatagrid\"\n\"74\",\"sunstudio\"\n\"74\",\"premature-optimization\"\n\"74\",\"arcgis-runtime-net\"\n\"74\",\"im4java\"\n\"73\",\"reorganize\"\n\"73\",\"clang-ast-matchers\"\n\"73\",\"xtify\"\n\"73\",\"tridion2009\"\n\"73\",\"php-parser\"\n\"73\",\"yield-keyword\"\n\"73\",\"react-native-mapbox-gl\"\n\"73\",\"php-imap\"\n\"73\",\"sktilemapnode\"\n\"73\",\"masm64\"\n\"73\",\"remoteio\"\n\"73\",\"cksubscription\"\n\"73\",\"multiprocessor\"\n\"73\",\"dds-format\"\n\"73\",\"transparentproxy\"\n\"73\",\"flutter-dialog\"\n\"73\",\"class-names\"\n\"73\",\"material-components-ios\"\n\"73\",\"laravel-datatables\"\n\"73\",\"pixel-perfect\"\n\"73\",\"apcu\"\n\"73\",\"ftell\"\n\"73\",\"pact-broker\"\n\"73\",\"paddle-paddle\"\n\"73\",\"fixeddocument\"\n\"73\",\"fixed-data-table\"\n\"73\",\"snowball\"\n\"73\",\"imagemagick.net\"\n\"73\",\"langchain-js\"\n\"73\",\"ngzone\"\n\"73\",\"plagiarism-detection\"\n\"73\",\"impromptu\"\n\"73\",\"fill-parent\"\n\"73\",\"xmlstreamreader\"\n\"73\",\"nftables\"\n\"73\",\"umbraco-contour\"\n\"73\",\"ultralytics\"\n\"73\",\"planar-graph\"\n\"73\",\"package-explorer\"\n\"73\",\"cardreader\"\n\"73\",\"contentmode\"\n\"73\",\"fromcharcode\"\n\"73\",\"furrr\"\n\"73\",\"pickerview\"\n\"73\",\"agiletoolkit\"\n\"73\",\"datafeed\"\n\"73\",\"rmiregistry\"\n\"73\",\"unity-interception\"\n\"73\",\"rootless\"\n\"73\",\"kendo-tooltip\"\n\"73\",\"aws-data-wrangler\"\n\"73\",\"mybatis-mapper\"\n\"73\",\"warm-up\"\n\"73\",\"django-endless-pagination\"\n\"73\",\"public-folders\"\n\"73\",\"facebook-requests\"\n\"73\",\"kitematic\"\n\"73\",\"aws-lake-formation\"\n\"73\",\"ahoy\"\n\"73\",\"ruby-2.4\"\n\"73\",\"sequencematcher\"\n\"73\",\"databinder\"\n\"73\",\"jomsocial\"\n\"73\",\"cake-pattern\"\n\"73\",\"sharp-architecture\"\n\"73\",\"pose\"\n\"73\",\"bootstrap5-modal\"\n\"73\",\"hssfworkbook\"\n\"73\",\"mogenerator\"\n\"73\",\"aptitude\"\n\"73\",\"dynamic-feature\"\n\"73\",\"jquery-1.4\"\n\"73\",\"turi-create\"\n\"73\",\"workload\"\n\"73\",\"karnaugh-map\"\n\"73\",\"salesforce-commerce-cloud\"\n\"73\",\"inform7\"\n\"73\",\"sbteclipse\"\n\"73\",\"openalpr\"\n\"73\",\"modifiers\"\n\"73\",\"password-policy\"\n\"73\",\"twitter-feed\"\n\"73\",\"dependency-resolver\"\n\"73\",\"openears\"\n\"73\",\"frank\"\n\"73\",\"lwrp\"\n\"73\",\"wonderware\"\n\"73\",\"sysv\"\n\"73\",\"lejos-nxj\"\n\"73\",\"ordinals\"\n\"73\",\"signedxml\"\n\"73\",\"object-property\"\n\"73\",\"foxit\"\n\"73\",\"magnet-uri\"\n\"73\",\"system.threading.channels\"\n\"73\",\"google-awareness\"\n\"73\",\"facebook-graph-api-v2.2\"\n\"73\",\"system.io.compression\"\n\"73\",\"ubuntu-19.04\"\n\"73\",\"google-api-gateway\"\n\"73\",\"system-center\"\n\"73\",\"ambient\"\n\"73\",\"javapns\"\n\"73\",\"revisions\"\n\"73\",\"pokeapi\"\n\"73\",\"high-contrast\"\n\"73\",\"non-latin\"\n\"73\",\"gcloud-cli\"\n\"73\",\"azure-video-indexer\"\n\"73\",\"dexclassloader\"\n\"73\",\"mockjax\"\n\"73\",\"gemini\"\n\"73\",\"android-doze-and-standby\"\n\"73\",\"td-agent\"\n\"73\",\"openh264\"\n\"73\",\"referrer-policy\"\n\"73\",\"nuclide-editor\"\n\"73\",\"scancodes\"\n\"73\",\"n-triples\"\n\"73\",\"mootools-events\"\n\"73\",\"react-native-component\"\n\"73\",\"google-forms-api\"\n\"73\",\"python-zip\"\n\"73\",\"custom-field-type\"\n\"73\",\"react-calendar\"\n\"73\",\"column-family\"\n\"73\",\"textlabel\"\n\"73\",\"esoteric-languages\"\n\"73\",\"request-cancelling\"\n\"73\",\"qr-decomposition\"\n\"73\",\"google-cloud-filestore\"\n\"73\",\"stopiteration\"\n\"73\",\"peft\"\n\"73\",\"pyviz\"\n\"73\",\"low-level-io\"\n\"73\",\"duplicity\"\n\"73\",\"merkle-tree\"\n\"73\",\"best-fit\"\n\"73\",\"hbmxml\"\n\"73\",\"concrete5-5.7\"\n\"73\",\"betfair\"\n\"73\",\"powerpoint-2007\"\n\"73\",\"concrete\"\n\"73\",\"powershell-7.2\"\n\"73\",\"maven-metadata\"\n\"73\",\"haskell-wai\"\n\"73\",\"multichoiceitems\"\n\"73\",\"textwriter\"\n\"73\",\"maven-eclipse-plugin\"\n\"73\",\"power-saving\"\n\"73\",\"ppapi\"\n\"73\",\"dangerouslysetinnerhtml\"\n\"73\",\"concordion\"\n\"73\",\"tigase\"\n\"73\",\"heapster\"\n\"73\",\"trampolines\"\n\"73\",\"sorm\"\n\"73\",\"iics\"\n\"73\",\"cvs2svn\"\n\"73\",\"ember-controllers\"\n\"73\",\"stetho\"\n\"73\",\"md5-file\"\n\"73\",\"query-analyzer\"\n\"73\",\"google-knowledge-graph\"\n\"73\",\"embedded-ruby\"\n\"73\",\"predictive\"\n\"73\",\"styledtext\"\n\"73\",\"amazon-app-runner\"\n\"73\",\"bgr\"\n\"73\",\"queuing\"\n\"73\",\"flutter-onpressed\"\n\"73\",\"webdev.webserver\"\n\"72\",\"skphysicscontact\"\n\"72\",\"jetson-xavier\"\n\"72\",\"babel-preset-env\"\n\"72\",\"jdbc-pool\"\n\"72\",\"fbsdkloginkit\"\n\"72\",\"srv\"\n\"72\",\"termcap\"\n\"72\",\"weakmap\"\n\"72\",\"stamp\"\n\"72\",\"jdwp\"\n\"72\",\"fennec\"\n\"72\",\"well-formed\"\n\"72\",\"filecontentresult\"\n\"72\",\"matplotlib-venn\"\n\"72\",\"class-instance-variables\"\n\"72\",\"mass\"\n\"72\",\"websphere-9\"\n\"72\",\"phonejs\"\n\"72\",\"fluid-styled-content\"\n\"72\",\"printjs\"\n\"72\",\"clp\"\n\"72\",\"truezip\"\n\"72\",\"flooding\"\n\"72\",\"self-host-webapi\"\n\"72\",\"bit.dev\"\n\"72\",\"admin-generator\"\n\"72\",\"umbraco-blog\"\n\"72\",\"symfony-components\"\n\"72\",\"bisonc++\"\n\"72\",\"freetts\"\n\"72\",\"firemonkey-fm3\"\n\"72\",\"aec\"\n\"72\",\"cilk\"\n\"72\",\"xmlmapper\"\n\"72\",\"impressions\"\n\"72\",\"laravel-envoy\"\n\"72\",\"bitcoin-testnet\"\n\"72\",\"xdist\"\n\"72\",\"pingdom\"\n\"72\",\"addtextchangedlistener\"\n\"72\",\"fiware-cosmos\"\n\"72\",\"page-index-changed\"\n\"72\",\"variable-substitution\"\n\"72\",\"createthread\"\n\"72\",\"simpletransformers\"\n\"72\",\"against\"\n\"72\",\"falcor\"\n\"72\",\"cap-theorem\"\n\"72\",\"narrator\"\n\"72\",\"wcf-callbacks\"\n\"72\",\"readerwriterlock\"\n\"72\",\"serenity-js\"\n\"72\",\"inline-images\"\n\"72\",\"blotter\"\n\"72\",\"guitar\"\n\"72\",\"era5\"\n\"72\",\"sap-data-services\"\n\"72\",\"app-secret\"\n\"72\",\"navigationlink\"\n\"72\",\"postgresql-16\"\n\"72\",\"junit5-extension-model\"\n\"72\",\"app-themes\"\n\"72\",\"sap\"\n\"72\",\"positional-parameter\"\n\"72\",\"mongock\"\n\"72\",\"worker-service\"\n\"72\",\"infinite-recursion\"\n\"72\",\"deform\"\n\"72\",\"svnserve\"\n\"72\",\"simics\"\n\"72\",\"ionide\"\n\"72\",\"coderunner\"\n\"72\",\"sigchld\"\n\"72\",\"colander\"\n\"72\",\"system.exit\"\n\"72\",\"kotlin-dsl\"\n\"72\",\"ksort\"\n\"72\",\"form-post\"\n\"72\",\"with-clause\"\n\"72\",\"mailboxprocessor\"\n\"72\",\"pyes\"\n\"72\",\"windowstate\"\n\"72\",\"magic-quotes-gpc\"\n\"72\",\"google-blockly\"\n\"72\",\"audit-tables\"\n\"72\",\"facebook-fbml\"\n\"72\",\"register-allocation\"\n\"72\",\"openxr\"\n\"72\",\"explicit-constructor\"\n\"72\",\"redux-firestore\"\n\"72\",\"togglz\"\n\"72\",\"bullseye\"\n\"72\",\"scope-resolution\"\n\"72\",\"null-coalescing\"\n\"72\",\"tabulizer\"\n\"72\",\"dfu\"\n\"72\",\"drupal-panels\"\n\"72\",\"hibernate-cache\"\n\"72\",\"device-mapper\"\n\"72\",\"control-center\"\n\"72\",\"scalate\"\n\"72\",\"sqlobject\"\n\"72\",\"tagname\"\n\"72\",\"doxygen-wizard\"\n\"72\",\"logentries\"\n\"72\",\"custom-cursor\"\n\"72\",\"evp-cipher\"\n\"72\",\"pfquerytableviewcontrolle\"\n\"72\",\"charactercount\"\n\"72\",\"pythonxy\"\n\"72\",\"character-replacement\"\n\"72\",\"react-d3\"\n\"72\",\"merb\"\n\"72\",\"angular-compiler-cli\"\n\"72\",\"esprima\"\n\"72\",\"getdirectories\"\n\"72\",\"cubemx\"\n\"72\",\"messagepack\"\n\"72\",\"center-align\"\n\"72\",\"ios8.2\"\n\"72\",\"httrack\"\n\"72\",\"react-ga\"\n\"72\",\"geoviews\"\n\"72\",\"http-host\"\n\"72\",\"olap4j\"\n\"72\",\"mergefield\"\n\"72\",\"commonschunkplugin\"\n\"72\",\"react-fiber\"\n\"72\",\"message-bus\"\n\"72\",\"splunk-sdk\"\n\"72\",\"qradiobutton\"\n\"72\",\"propertyeditor\"\n\"72\",\"stripping\"\n\"72\",\"ilgenerator\"\n\"72\",\"sony-smarteyeglass\"\n\"72\",\"user-preferences\"\n\"72\",\"computer-algebra-systems\"\n\"72\",\"zend-amf\"\n\"72\",\"mci\"\n\"72\",\"bazel-cpp\"\n\"72\",\"structure-from-motion\"\n\"72\",\"utorrent\"\n\"72\",\"fontello\"\n\"72\",\"compatibility-mode\"\n\"72\",\"solace-mq\"\n\"72\",\"urlsearchparams\"\n\"72\",\"parallel-arrays\"\n\"72\",\"weborb\"\n\"72\",\"qt5.2\"\n\"72\",\"parents\"\n\"72\",\"global-hotkey\"\n\"72\",\"emq\"\n\"72\",\"google-managed-vm\"\n\"72\",\"alpn\"\n\"72\",\"stdany\"\n\"71\",\"floppy\"\n\"71\",\"xtratreelist\"\n\"71\",\"vue-meta\"\n\"71\",\"cmsamplebufferref\"\n\"71\",\"cloudways\"\n\"71\",\"flopy\"\n\"71\",\"eclipse-photon\"\n\"71\",\"grid.mvc\"\n\"71\",\"graphql-tag\"\n\"71\",\"photo-upload\"\n\"71\",\"multiple-matches\"\n\"71\",\"slicknav\"\n\"71\",\"react-routing\"\n\"71\",\"youtube.net-api\"\n\"71\",\"web-setup-project\"\n\"71\",\"edititemtemplate\"\n\"71\",\"installation-path\"\n\"71\",\"ssh2-exec\"\n\"71\",\"standardization\"\n\"71\",\"pgvector\"\n\"71\",\"multiple-return-values\"\n\"71\",\"background-repeat\"\n\"71\",\"nextsibling\"\n\"71\",\"pact-lang\"\n\"71\",\"adaptive-bitrate\"\n\"71\",\"xpdf\"\n\"71\",\"cbperipheralmanager\"\n\"71\",\"python-dedupe\"\n\"71\",\"firebug-lite\"\n\"71\",\"distinguishedname\"\n\"71\",\"mainloop\"\n\"71\",\"next-generation-plugin\"\n\"71\",\"implicit-typing\"\n\"71\",\"connectycube\"\n\"71\",\"undefined-variable\"\n\"71\",\"cheminformatics\"\n\"71\",\"pipfile\"\n\"71\",\"firmata\"\n\"71\",\"python-nonlocal\"\n\"71\",\"varcharmax\"\n\"71\",\"alertifyjs\"\n\"71\",\"jni4net\"\n\"71\",\"grafika\"\n\"71\",\"angular-ui-tree\"\n\"71\",\"angular-templatecache\"\n\"71\",\"alarms\"\n\"71\",\"unspecified-behavior\"\n\"71\",\"rmq\"\n\"71\",\"cap\"\n\"71\",\"rpivottable\"\n\"71\",\"jocl\"\n\"71\",\"callable-object\"\n\"71\",\"cakephp-2.7\"\n\"71\",\"kissfft\"\n\"71\",\"facetime\"\n\"71\",\"operationcontract\"\n\"71\",\"pyarmor\"\n\"71\",\"micro-orm\"\n\"71\",\"cradle\"\n\"71\",\"kdoc\"\n\"71\",\"android-shortcut\"\n\"71\",\"cqlengine\"\n\"71\",\"nspersistentdocument\"\n\"71\",\"dynamics-crm-portals\"\n\"71\",\"inquirerjs\"\n\"71\",\"android-studio-bumblebee\"\n\"71\",\"nsorderedset\"\n\"71\",\"delete-record\"\n\"71\",\"popen3\"\n\"71\",\"nesc\"\n\"71\",\"apptrackingtransparency\"\n\"71\",\"applicationhost\"\n\"71\",\"covariant\"\n\"71\",\"opendds\"\n\"71\",\"auth0-lock\"\n\"71\",\"risc\"\n\"71\",\"build-numbers\"\n\"71\",\"minikanren\"\n\"71\",\"ubsan\"\n\"71\",\"signed-integer\"\n\"71\",\"2phase-commit\"\n\"71\",\"pyenv-virtualenv\"\n\"71\",\"2dsphere\"\n\"71\",\"netduino\"\n\"71\",\"rfc2616\"\n\"71\",\"vimeo-android\"\n\"71\",\"winium\"\n\"71\",\"facebook-business-sdk\"\n\"71\",\"google-books-api\"\n\"71\",\"eye-detection\"\n\"71\",\"occlusion\"\n\"71\",\"object-create\"\n\"71\",\"schemaless\"\n\"71\",\"honeywell\"\n\"71\",\"nugetgallery\"\n\"71\",\"ds-5\"\n\"71\",\"redux-devtools-extension\"\n\"71\",\"hilo\"\n\"71\",\"scala-template\"\n\"71\",\"openimaj\"\n\"71\",\"hammingweight\"\n\"71\",\"devicemotion\"\n\"71\",\"gemset\"\n\"71\",\"mkpolygon\"\n\"71\",\"mobile-country-code\"\n\"71\",\"c17\"\n\"71\",\"playing\"\n\"71\",\"targetinvocationexception\"\n\"71\",\"timeslots\"\n\"71\",\"uisearchbardisplaycontrol\"\n\"71\",\"j48\"\n\"71\",\"message-loop\"\n\"71\",\"compact-framework2.0\"\n\"71\",\"spray-client\"\n\"71\",\"cgaffinetransformscale\"\n\"71\",\"angular-fontawesome\"\n\"71\",\"storage-engines\"\n\"71\",\"stripe-payment-intent\"\n\"71\",\"ektorp\"\n\"71\",\"dundas\"\n\"71\",\"mpmediaitemcollection\"\n\"71\",\"cudafy.net\"\n\"71\",\"eventlog-source\"\n\"71\",\"elasticsearch-high-level-restclient\"\n\"71\",\"qqmlapplicationengine\"\n\"71\",\"currentuiculture\"\n\"71\",\"perfview\"\n\"71\",\"prometheus-java\"\n\"71\",\"color-blending\"\n\"71\",\"google-cloud-vpn\"\n\"71\",\"permute\"\n\"71\",\"angular-grid\"\n\"71\",\"long-format-data\"\n\"71\",\"qb64\"\n\"71\",\"lua-5.1\"\n\"71\",\"bep20\"\n\"71\",\"state-diagram\"\n\"71\",\"google-hadoop\"\n\"71\",\"availability-zone\"\n\"71\",\"mdt\"\n\"71\",\"hcl-notes\"\n\"71\",\"webharvest\"\n\"71\",\"parsing-error\"\n\"71\",\"papermill\"\n\"71\",\"automount\"\n\"71\",\"tiktok-api\"\n\"71\",\"query-hints\"\n\"71\",\"glance\"\n\"71\",\"alternative-functor\"\n\"71\",\"quasiquotes\"\n\"71\",\"vaadin-charts\"\n\"71\",\"parrot-os\"\n\"71\",\"bernoulli-probability\"\n\"71\",\"glteximage2d\"\n\"70\",\"jdeps\"\n\"70\",\"trichedit\"\n\"70\",\"program-flow\"\n\"70\",\"processors\"\n\"70\",\"privilege\"\n\"70\",\"cnf\"\n\"70\",\"proc-report\"\n\"70\",\"phpldapadmin\"\n\"70\",\"printstacktrace\"\n\"70\",\"multi-upload\"\n\"70\",\"debug-information\"\n\"70\",\"cldc\"\n\"70\",\"gridex\"\n\"70\",\"ef-power-tools\"\n\"70\",\"mastodon\"\n\"70\",\"lipo\"\n\"70\",\"jform\"\n\"70\",\"react-native-snap-carousel\"\n\"70\",\"feedzirra\"\n\"70\",\"muxer\"\n\"70\",\"anytree\"\n\"70\",\"martini\"\n\"70\",\"cloudamqp\"\n\"70\",\"yellowbrick\"\n\"70\",\"bitronix\"\n\"70\",\"over-the-air\"\n\"70\",\"swinject\"\n\"70\",\"mailtrap\"\n\"70\",\"apache-velocity\"\n\"70\",\"connector-j\"\n\"70\",\"pkg-file\"\n\"70\",\"physijs\"\n\"70\",\"xml-nil\"\n\"70\",\"firebase-polymer\"\n\"70\",\"pageant\"\n\"70\",\"bing-search\"\n\"70\",\"rule-of-three\"\n\"70\",\"lab-color-space\"\n\"70\",\"uitoolbaritem\"\n\"70\",\"vsixmanifest\"\n\"70\",\"pitch-tracking\"\n\"70\",\"adonisjs-ace\"\n\"70\",\"cinemachine\"\n\"70\",\"python-keyring\"\n\"70\",\"adox\"\n\"70\",\"mappoint\"\n\"70\",\"data-lineage\"\n\"70\",\"planetscale\"\n\"70\",\"rxdatasources\"\n\"70\",\"content-editor\"\n\"70\",\"microsoft.codeanalysis\"\n\"70\",\"value-initialization\"\n\"70\",\"watcom\"\n\"70\",\"joined-subclass\"\n\"70\",\"mysql-select-db\"\n\"70\",\"canexecute\"\n\"70\",\"rugged\"\n\"70\",\"variable-templates\"\n\"70\",\"kentico-mvc\"\n\"70\",\"gpg-agent\"\n\"70\",\"rowtype\"\n\"70\",\"josso\"\n\"70\",\"fastled\"\n\"70\",\"document-conversion\"\n\"70\",\"dllnotfoundexception\"\n\"70\",\"aws-backup\"\n\"70\",\"mysql-error-1452\"\n\"70\",\"avcapturemoviefileoutput\"\n\"70\",\"swarmplot\"\n\"70\",\"environments\"\n\"70\",\"blazor-editform\"\n\"70\",\"enquire.js\"\n\"70\",\"ion-range-slider\"\n\"70\",\"android-timer\"\n\"70\",\"sap-xi\"\n\"70\",\"azure-connect\"\n\"70\",\"dynamic-resizing\"\n\"70\",\"azure-dashboard\"\n\"70\",\"ncache\"\n\"70\",\"corrupt-data\"\n\"70\",\"nat-traversal\"\n\"70\",\"wso2-as\"\n\"70\",\"twa\"\n\"70\",\"group-summaries\"\n\"70\",\"awt-eventqueue\"\n\"70\",\"infobubble\"\n\"70\",\"bluetooth-peripheral\"\n\"70\",\"pdfhtml\"\n\"70\",\"forward-list\"\n\"70\",\"typescript-declarations\"\n\"70\",\"netbeans-7.1\"\n\"70\",\"vlc-android\"\n\"70\",\"ktable\"\n\"70\",\"winget\"\n\"70\",\"shift-register\"\n\"70\",\"pyrfc\"\n\"70\",\"audio-converter\"\n\"70\",\"wofstream\"\n\"70\",\"coinbase-php\"\n\"70\",\"richtextblock\"\n\"70\",\"attunity\"\n\"70\",\"system.data.oracleclient\"\n\"70\",\"atlassian-crucible\"\n\"70\",\"codeeffects\"\n\"70\",\"korma\"\n\"70\",\"ocmockito\"\n\"70\",\"viewhelper\"\n\"70\",\"jaxb2-basics\"\n\"70\",\"victory-native\"\n\"70\",\"reward\"\n\"70\",\"vertex-array-object\"\n\"70\",\"radiant\"\n\"70\",\"qutip\"\n\"70\",\"dropbox-sdk-js\"\n\"70\",\"azure-sdk-for-java\"\n\"70\",\"open-session-in-view\"\n\"70\",\"scatterview\"\n\"70\",\"pluck\"\n\"70\",\"dev-null\"\n\"70\",\"sqlcommandbuilder\"\n\"70\",\"uitabcontroller\"\n\"70\",\"tkinter.checkbutton\"\n\"70\",\"drupal-10\"\n\"70\",\"uibackgroundtask\"\n\"70\",\"modalpopups\"\n\"70\",\"directoryindex\"\n\"70\",\"xbox-live\"\n\"70\",\"non-convex\"\n\"70\",\"azure-management\"\n\"70\",\"no-duplicates\"\n\"70\",\"c++builder-xe5\"\n\"70\",\"angular2-form-validation\"\n\"70\",\"angular-content-projection\"\n\"70\",\"react-lifecycle-hooks\"\n\"70\",\"petri-net\"\n\"70\",\"splay-tree\"\n\"70\",\"prolog-setof\"\n\"70\",\"angular-dragdrop\"\n\"70\",\"electron.net\"\n\"70\",\"chaplinjs\"\n\"70\",\"mergeddictionaries\"\n\"70\",\"mesi\"\n\"70\",\"chained-payments\"\n\"70\",\"event-triggers\"\n\"70\",\"ethercat\"\n\"70\",\"stroke-dasharray\"\n\"70\",\"storyblok\"\n\"70\",\"personality-insights\"\n\"70\",\"leaf\"\n\"70\",\"merchant-account\"\n\"70\",\"zypper\"\n\"70\",\"httpentity\"\n\"70\",\"generated-columns\"\n\"70\",\"weblate\"\n\"70\",\"toolstripbutton\"\n\"70\",\"sonarlint-intellij\"\n\"70\",\"google-shopping\"\n\"70\",\"thread-abort\"\n\"70\",\"thanos\"\n\"70\",\"webcam.js\"\n\"70\",\"flutter-sliverappbar\"\n\"70\",\"static-resource\"\n\"70\",\"sumoselect.js\"\n\"70\",\"image-editor\"\n\"70\",\"tibco-rv\"\n\"70\",\"statusstrip\"\n\"70\",\"elmo\"\n\"70\",\"heartrate\"\n\"70\",\"archlinux-arm\"\n\"70\",\"mu\"\n\"70\",\"amazon-comprehend\"\n\"70\",\"autodeploy\"\n\"70\",\"user-activity\"\n\"70\",\"mujoco\"\n\"70\",\"median-of-medians\"\n\"70\",\"parallel-execution\"\n\"70\",\"parasoft\"\n\"70\",\"cyclicbarrier\"\n\"70\",\"parse-javascript-sdk\"\n\"70\",\"powershell-7.3\"\n\"70\",\"zend-soap\"\n\"70\",\"shelveset\"\n\"70\",\"std-span\"\n\"70\",\"solar\"\n\"69\",\"template-aliases\"\n\"69\",\"cloudkit-web-services\"\n\"69\",\"github-actions-runners\"\n\"69\",\"mat-sidenav\"\n\"69\",\"remoteapi\"\n\"69\",\"team-project\"\n\"69\",\"phpdocumentor2\"\n\"69\",\"mathprog\"\n\"69\",\"tempo\"\n\"69\",\"dbo\"\n\"69\",\"ssa\"\n\"69\",\"debug-backtrace\"\n\"69\",\"int128\"\n\"69\",\"stable-sort\"\n\"69\",\"multipass\"\n\"69\",\"related-content\"\n\"69\",\"jcolorchooser\"\n\"69\",\"jax-ws-customization\"\n\"69\",\"transloco\"\n\"69\",\"yank\"\n\"69\",\"trx\"\n\"69\",\"sssd\"\n\"69\",\"xlsread\"\n\"69\",\"disambiguation\"\n\"69\",\"xmlunit-2\"\n\"69\",\"xdgutils\"\n\"69\",\"snipcart\"\n\"69\",\"swiftui-text\"\n\"69\",\"const-char\"\n\"69\",\"app-data\"\n\"69\",\"flask-jwt\"\n\"69\",\"incanter\"\n\"69\",\"swift-structs\"\n\"69\",\"rust-ndarray\"\n\"69\",\"flask-cache\"\n\"69\",\"apng\"\n\"69\",\"consul-template\"\n\"69\",\"catch-all\"\n\"69\",\"self-attention\"\n\"69\",\"pagerduty\"\n\"69\",\"containstable\"\n\"69\",\"python-exec\"\n\"69\",\"camel-http\"\n\"69\",\"myspace\"\n\"69\",\"windows-nt\"\n\"69\",\"roo-gem\"\n\"69\",\"icsharpcode\"\n\"69\",\"jitterbit\"\n\"69\",\"angular-validator\"\n\"69\",\"rollout\"\n\"69\",\"job-control\"\n\"69\",\"redaction\"\n\"69\",\"journaling\"\n\"69\",\"roberta-language-model\"\n\"69\",\"agsxmpp\"\n\"69\",\"realex-payments-api\"\n\"69\",\"fastlane-gym\"\n\"69\",\"serilog-aspnetcore\"\n\"69\",\"csquery\"\n\"69\",\"index-signature\"\n\"69\",\"svg-sprite\"\n\"69\",\"interfacing\"\n\"69\",\"enum-map\"\n\"69\",\"suppression\"\n\"69\",\"dynamic-links\"\n\"69\",\"neoload\"\n\"69\",\"apprtcdemo\"\n\"69\",\"nservicebus5\"\n\"69\",\"jquery-clone\"\n\"69\",\"module-path\"\n\"69\",\"mod-php\"\n\"69\",\"applozic\"\n\"69\",\"applicationdomain\"\n\"69\",\"gstring\"\n\"69\",\"svn-repository\"\n\"69\",\"core-api\"\n\"69\",\"inline-svg\"\n\"69\",\"swift-dictionary\"\n\"69\",\"mongodb-oplog\"\n\"69\",\"houghlinesp\"\n\"69\",\"nshttpcookie\"\n\"69\",\"libgcc\"\n\"69\",\"gollum-wiki\"\n\"69\",\"typo3-4.5\"\n\"69\",\"typst\"\n\"69\",\"ribboncontrolslibrary\"\n\"69\",\"coefplot\"\n\"69\",\"amphp\"\n\"69\",\"reverse-shell\"\n\"69\",\"lexicon\"\n\"69\",\"lexical-cast\"\n\"69\",\"bro\"\n\"69\",\"formats\"\n\"69\",\"fabrication-gem\"\n\"69\",\"kohana-auth\"\n\"69\",\"go-colly\"\n\"69\",\"kraft\"\n\"69\",\"btle\"\n\"69\",\"coingecko\"\n\"69\",\"pysdl2\"\n\"69\",\"visual-c++-2015\"\n\"69\",\"hashids\"\n\"69\",\"nsubiquitouskeyvaluestore\"\n\"69\",\"doxywizard\"\n\"69\",\"azure-webjobs-continuous\"\n\"69\",\"tkinter-scale\"\n\"69\",\"mochiweb\"\n\"69\",\"taint\"\n\"69\",\"hangfire-sql\"\n\"69\",\"mknetworkkit\"\n\"69\",\"scala-swing\"\n\"69\",\"dsolve\"\n\"69\",\"mobx-react-lite\"\n\"69\",\"tinyurl\"\n\"69\",\"spynner\"\n\"69\",\"dremio\"\n\"69\",\"uiimagepngrepresentation\"\n\"69\",\"continuous-fourier\"\n\"69\",\"bulksms\"\n\"69\",\"downloadfileasync\"\n\"69\",\"dotnet-test\"\n\"69\",\"mlpack\"\n\"69\",\"hashbytes\"\n\"69\",\"bulk-email\"\n\"69\",\"harfbuzz\"\n\"69\",\"espressif-idf\"\n\"69\",\"active-relation\"\n\"69\",\"move-assignment-operator\"\n\"69\",\"zscaler\"\n\"69\",\"responsive-filemanager\"\n\"69\",\"lua-5.2\"\n\"69\",\"resharper-9.0\"\n\"69\",\"ios-navigationview\"\n\"69\",\"spreadsheet-protection\"\n\"69\",\"react-cookie\"\n\"69\",\"launching\"\n\"69\",\"excanvas\"\n\"69\",\"accessible\"\n\"69\",\"terraform-provider\"\n\"69\",\"compact-database\"\n\"69\",\"http-status-code-200\"\n\"69\",\"zstack\"\n\"69\",\"spiffs\"\n\"69\",\"motherboard\"\n\"69\",\"chartjs-plugin-zoom\"\n\"69\",\"mercurialeclipse\"\n\"69\",\"tfs-web-access\"\n\"69\",\"ms-project-server-2010\"\n\"69\",\"linked-service\"\n\"69\",\"amazon-redshift-serverless\"\n\"69\",\"query-parser\"\n\"69\",\"qtvirtualkeyboard\"\n\"69\",\"usb4java\"\n\"69\",\"amazon-cloudwatch-events\"\n\"69\",\"amazon-chime\"\n\"69\",\"transactional-email\"\n\"69\",\"iec61131-3\"\n\"69\",\"usermode\"\n\"69\",\"haxelib\"\n\"69\",\"folly\"\n\"68\",\"federated-queries\"\n\"68\",\"apache2-module\"\n\"68\",\"clarion\"\n\"68\",\"relational-operators\"\n\"68\",\"click-through\"\n\"68\",\"flow-js\"\n\"68\",\"defects\"\n\"68\",\"websocket++\"\n\"68\",\"xsbt-web-plugin\"\n\"68\",\"trustmanager\"\n\"68\",\"fedora-23\"\n\"68\",\"webseal\"\n\"68\",\"ggpairs\"\n\"68\",\"slrequest\"\n\"68\",\"match-phrase\"\n\"68\",\"whiptail\"\n\"68\",\"tripadvisor\"\n\"68\",\"function-binding\"\n\"68\",\"major-upgrade\"\n\"68\",\"json2html\"\n\"68\",\"vscodium\"\n\"68\",\"page-break-inside\"\n\"68\",\"xmlhttprequest-level2\"\n\"68\",\"mariadb-10.1\"\n\"68\",\"data-virtualization\"\n\"68\",\"runc\"\n\"68\",\"data-members\"\n\"68\",\"black-box\"\n\"68\",\"laravel-factory\"\n\"68\",\"circuit-sdk\"\n\"68\",\"after-save\"\n\"68\",\"imapclient\"\n\"68\",\"binary-reproducibility\"\n\"68\",\"swiftui-button\"\n\"68\",\"imutils\"\n\"68\",\"admin-ajax\"\n\"68\",\"fs-extra\"\n\"68\",\"kylo\"\n\"68\",\"fixed-length-record\"\n\"68\",\"segment-io\"\n\"68\",\"confirmation-email\"\n\"68\",\"pandas-explode\"\n\"68\",\"date-sorting\"\n\"68\",\"jpa-2.2\"\n\"68\",\"jodd\"\n\"68\",\"url-design\"\n\"68\",\"rsh\"\n\"68\",\"pulsar\"\n\"68\",\"database-server\"\n\"68\",\"winavr\"\n\"68\",\"aws-appsync-resolver\"\n\"68\",\"oracle-fusion-apps\"\n\"68\",\"kgdb\"\n\"68\",\"react-sortable-hoc\"\n\"68\",\"alexa-internet\"\n\"68\",\"watchos-4\"\n\"68\",\"data-handling\"\n\"68\",\"inequalities\"\n\"68\",\"mysql-proxy\"\n\"68\",\"twig-filter\"\n\"68\",\"supplier\"\n\"68\",\"blazor-component\"\n\"68\",\"boost-gil\"\n\"68\",\"tvjs\"\n\"68\",\"inference-engine\"\n\"68\",\"naturallyspeaking\"\n\"68\",\"jquery-migrate\"\n\"68\",\"wp-editor\"\n\"68\",\"hortonworks-dataflow\"\n\"68\",\"sap-r3\"\n\"68\",\"mongo-express\"\n\"68\",\"detekt\"\n\"68\",\"easing-functions\"\n\"68\",\"typed.js\"\n\"68\",\"bootable\"\n\"68\",\"anemic-domain-model\"\n\"68\",\"write-host\"\n\"68\",\"positional-argument\"\n\"68\",\"boost-msm\"\n\"68\",\"nssegmentedcontrol\"\n\"68\",\"hostheaders\"\n\"68\",\"guizero\"\n\"68\",\"jquery-1.5\"\n\"68\",\"dynamic-css\"\n\"68\",\"pdf-lib.js\"\n\"68\",\"go-build\"\n\"68\",\"broadcom\"\n\"68\",\"kotlin-reified-type-parameters\"\n\"68\",\"bringtofront\"\n\"68\",\"android-architecture-lifecycle\"\n\"68\",\"nx.dev\"\n\"68\",\"showdown\"\n\"68\",\"google-appsheet\"\n\"68\",\"type-kinds\"\n\"68\",\"tzinfo\"\n\"68\",\"outlook-object-model\"\n\"68\",\"rbm\"\n\"68\",\"rapache\"\n\"68\",\"amibroker\"\n\"68\",\"3d-engine\"\n\"68\",\"shippo\"\n\"68\",\"known-types\"\n\"68\",\"pygame-clock\"\n\"68\",\"woff2\"\n\"68\",\"wkinterfacecontroller\"\n\"68\",\"wmd-editor\"\n\"68\",\"liblinear\"\n\"68\",\"typesense\"\n\"68\",\"java-security-manager\"\n\"68\",\"sigbus\"\n\"68\",\"macos-system-extension\"\n\"68\",\"digital-ocean-apps\"\n\"68\",\"execute-script\"\n\"68\",\"execute-sql-task\"\n\"68\",\"xcode6-beta6\"\n\"68\",\"tinyscrollbar\"\n\"68\",\"itemcommand\"\n\"68\",\"vibed\"\n\"68\",\"android-for-work\"\n\"68\",\"c++builder-xe7\"\n\"68\",\"xcode8-beta6\"\n\"68\",\"copy-and-swap\"\n\"68\",\"ui-calendar\"\n\"68\",\"dfc\"\n\"68\",\"sqlgeometry\"\n\"68\",\"mlm\"\n\"68\",\"android-chrome\"\n\"68\",\"sqlitemanager\"\n\"68\",\"mockito-kotlin\"\n\"68\",\"c23\"\n\"68\",\"drawbitmap\"\n\"68\",\"isort\"\n\"68\",\"copyonwritearraylist\"\n\"68\",\"timestamping\"\n\"68\",\"strictness\"\n\"68\",\"elgamal\"\n\"68\",\"prophet\"\n\"68\",\"geoalchemy2\"\n\"68\",\"change-management\"\n\"68\",\"string-algorithm\"\n\"68\",\"huggingface-hub\"\n\"68\",\"qdbus\"\n\"68\",\"custom-arrayadapter\"\n\"68\",\"qgraphicstextitem\"\n\"68\",\"splist\"\n\"68\",\"http-kit\"\n\"68\",\"genomicranges\"\n\"68\",\"android-paint\"\n\"68\",\"actor-model\"\n\"68\",\"strawberry-graphql\"\n\"68\",\"comparevalidator\"\n\"68\",\"pendulum\"\n\"68\",\"monochrome\"\n\"68\",\"mosby\"\n\"68\",\"react-memo\"\n\"68\",\"etw-eventsource\"\n\"68\",\"spectral-density\"\n\"68\",\"request-response\"\n\"68\",\"actioncontext\"\n\"68\",\"eventfilter\"\n\"68\",\"angular2-mdl\"\n\"68\",\"protractor-net\"\n\"68\",\"scriptrunner-for-jira\"\n\"68\",\"space-efficiency\"\n\"68\",\"solr-cell\"\n\"68\",\"automocking\"\n\"68\",\"glazedlists\"\n\"68\",\"webarchive\"\n\"68\",\"user-event\"\n\"68\",\"security-testing\"\n\"68\",\"mtproto\"\n\"68\",\"thephpleague\"\n\"68\",\"bias-neuron\"\n\"68\",\"tfjs-node\"\n\"68\",\"liclipse\"\n\"68\",\"imageai\"\n\"68\",\"ijson\"\n\"68\",\"sonarlint-vs\"\n\"68\",\"webdriverjs\"\n\"68\",\"zend-inputfilter\"\n\"68\",\"googletrans\"\n\"68\",\"helpfile\"\n\"68\",\"ember.js-2\"\n\"68\",\"ierrorhandler\"\n\"68\",\"parental-control\"\n\"68\",\"msxsl\"\n\"68\",\"the-little-schemer\"\n\"68\",\"multiboot\"\n\"68\",\"email-forwarding\"\n\"68\",\"utility-method\"\n\"68\",\"secp256k1\"\n\"68\",\"queuetrigger\"\n\"68\",\"concatmap\"\n\"68\",\"emc\"\n\"68\",\"compiled-query\"\n\"67\",\"trusted\"\n\"67\",\"xsi\"\n\"67\",\"fedora20\"\n\"67\",\"fedora-21\"\n\"67\",\"ginkgo\"\n\"67\",\"gini\"\n\"67\",\"github-secret\"\n\"67\",\"git-gc\"\n\"67\",\"webtorrent\"\n\"67\",\"renewal\"\n\"67\",\"gibbon\"\n\"67\",\"github-linguist\"\n\"67\",\"processing-instruction\"\n\"67\",\"clockwork\"\n\"67\",\"profanity\"\n\"67\",\"flutter-animatedlist\"\n\"67\",\"ghost.py\"\n\"67\",\"deepsecurity\"\n\"67\",\"reportmanager\"\n\"67\",\"markupbuilder\"\n\"67\",\"ecj\"\n\"67\",\"multiple-select-query\"\n\"67\",\"integrator\"\n\"67\",\"mutation-events\"\n\"67\",\"yang\"\n\"67\",\"clamav\"\n\"67\",\"reification\"\n\"67\",\"list-comparison\"\n\"67\",\"filemerge\"\n\"67\",\"clipper\"\n\"67\",\"year2038\"\n\"67\",\"sms-verification\"\n\"67\",\"x-macros\"\n\"67\",\"ulong\"\n\"67\",\"apksigner\"\n\"67\",\"aescryptoserviceprovider\"\n\"67\",\"biomart\"\n\"67\",\"finch\"\n\"67\",\"swift-optionals\"\n\"67\",\"mapping-model\"\n\"67\",\"bjyauthorize\"\n\"67\",\"smart-on-fhir\"\n\"67\",\"file-security\"\n\"67\",\"cassandra-4.0\"\n\"67\",\"language-ext\"\n\"67\",\"constraint-layout-chains\"\n\"67\",\"ng-idle\"\n\"67\",\"divio\"\n\"67\",\"chat-gpt-4\"\n\"67\",\"database-cluster\"\n\"67\",\"unmodifiable\"\n\"67\",\"rubular\"\n\"67\",\"angular-redux\"\n\"67\",\"cakephp-3.7\"\n\"67\",\"rowset\"\n\"67\",\"unmanagedresources\"\n\"67\",\"pubspec.yaml\"\n\"67\",\"google-weather-api\"\n\"67\",\"indic\"\n\"67\",\"facebook-workplace\"\n\"67\",\"docker-java\"\n\"67\",\"psr-2\"\n\"67\",\"oracle-adf-mobile\"\n\"67\",\"polyml\"\n\"67\",\"boost-units\"\n\"67\",\"epel\"\n\"67\",\"tweenjs\"\n\"67\",\"justgage\"\n\"67\",\"working-set\"\n\"67\",\"dynamic-analysis\"\n\"67\",\"htmlcontrols\"\n\"67\",\"spring-android\"\n\"67\",\"sweet.js\"\n\"67\",\"nested-documents\"\n\"67\",\"nspredicateeditor\"\n\"67\",\"keccak\"\n\"67\",\"azure-acs\"\n\"67\",\"juno-ide\"\n\"67\",\"pbr\"\n\"67\",\"jquery-ui-timepicker\"\n\"67\",\"azure-cosmosdb-changefeed\"\n\"67\",\"worklight-appcenter\"\n\"67\",\"sangria\"\n\"67\",\"wise\"\n\"67\",\"viewbuilder\"\n\"67\",\"pys60\"\n\"67\",\"librsvg\"\n\"67\",\"code-maintainability\"\n\"67\",\"oauth2-proxy\"\n\"67\",\"siesta-swift\"\n\"67\",\"coda-slider\"\n\"67\",\"abtest\"\n\"67\",\"buildnumber-maven-plugin\"\n\"67\",\"pysal\"\n\"67\",\"android-6.0.1-marshmallow\"\n\"67\",\"netsuite-rest-api\"\n\"67\",\"klee\"\n\"67\",\"retool\"\n\"67\",\"analysisservices\"\n\"67\",\"ucontext\"\n\"67\",\"ras\"\n\"67\",\"lzx\"\n\"67\",\"external-js\"\n\"67\",\"left-to-right\"\n\"67\",\"bs4dash\"\n\"67\",\"vmc\"\n\"67\",\"milestone\"\n\"67\",\"visitors\"\n\"67\",\"object-comparison\"\n\"67\",\"ranged-loops\"\n\"67\",\"pyral\"\n\"67\",\"radiobuttonfor\"\n\"67\",\"nt\"\n\"67\",\"jad\"\n\"67\",\"ntlmv2\"\n\"67\",\"ivyde\"\n\"67\",\"mlrun\"\n\"67\",\"tkinter.optionmenu\"\n\"67\",\"scraperwiki\"\n\"67\",\"button-to\"\n\"67\",\"sqlc\"\n\"67\",\"hilbert-curve\"\n\"67\",\"byval\"\n\"67\",\"scalameta\"\n\"67\",\"openj9\"\n\"67\",\"android-identifiers\"\n\"67\",\"non-linear\"\n\"67\",\"dotted-line\"\n\"67\",\"cache-expiration\"\n\"67\",\"devtoolset\"\n\"67\",\"spree-auth-devise\"\n\"67\",\"accumarray\"\n\"67\",\"laravel-views\"\n\"67\",\"custom-draw\"\n\"67\",\"custom-directive\"\n\"67\",\"google-cloud-trace\"\n\"67\",\"irony\"\n\"67\",\"isam\"\n\"67\",\"stripos\"\n\"67\",\"stringindexoutofbounds\"\n\"67\",\"long-filenames\"\n\"67\",\"lateral\"\n\"67\",\"qhash\"\n\"67\",\"sparse-file\"\n\"67\",\"textreader\"\n\"67\",\"cgbitmapcontextcreate\"\n\"67\",\"oculusgo\"\n\"67\",\"luabridge\"\n\"67\",\"custompaging\"\n\"67\",\"hubspot-crm\"\n\"67\",\"straight-line-detection\"\n\"67\",\"linden-scripting-language\"\n\"67\",\"google-maps-engine\"\n\"67\",\"zomato-api\"\n\"67\",\"thingworx\"\n\"67\",\"google-http-client\"\n\"67\",\"emcee\"\n\"67\",\"concave\"\n\"67\",\"qstandarditem\"\n\"67\",\"thickness\"\n\"67\",\"tilde-expansion\"\n\"67\",\"array-key-exists\"\n\"67\",\"suhosin\"\n\"67\",\"line-height\"\n\"67\",\"berkeley-sockets\"\n\"67\",\"avaudiofile\"\n\"67\",\"stencils\"\n\"67\",\"suggestbox\"\n\"67\",\"cvzone\"\n\"67\",\"compositing\"\n\"66\",\"flixel\"\n\"66\",\"fileloadexception\"\n\"66\",\"materialized\"\n\"66\",\"mathematica-frontend\"\n\"66\",\"feathers-authentication\"\n\"66\",\"cloudcustodian\"\n\"66\",\"mutating-table\"\n\"66\",\"anonymous-users\"\n\"66\",\"intel-mic\"\n\"66\",\"backblaze\"\n\"66\",\"private-messaging\"\n\"66\",\"stagewebview\"\n\"66\",\"multi-page-application\"\n\"66\",\"welcome-file\"\n\"66\",\"graphicscontext\"\n\"66\",\"balloon-tip\"\n\"66\",\"eclipse-ditto\"\n\"66\",\"rendered-attribute\"\n\"66\",\"stack-corruption\"\n\"66\",\"phantom-types\"\n\"66\",\"matconvnet\"\n\"66\",\"jboss-esb\"\n\"66\",\"cmocka\"\n\"66\",\"cleditor\"\n\"66\",\"lmertest\"\n\"66\",\"livescript\"\n\"66\",\"dd-wrt\"\n\"66\",\"clients\"\n\"66\",\"matrix-transform\"\n\"66\",\"musicxml\"\n\"66\",\"matblazor\"\n\"66\",\"skstorereviewcontroller\"\n\"66\",\"declspec\"\n\"66\",\"tsne\"\n\"66\",\"primefaces-mobile\"\n\"66\",\"lmax\"\n\"66\",\"sencha-touch-2.2\"\n\"66\",\"vosk\"\n\"66\",\"js-of-ocaml\"\n\"66\",\"laravel-dompdf\"\n\"66\",\"laravel-admin\"\n\"66\",\"firebase-app-indexing\"\n\"66\",\"data-scrubbing\"\n\"66\",\"managed-property\"\n\"66\",\"cherokee\"\n\"66\",\"cefsharp.offscreen\"\n\"66\",\"segmentedcontrol\"\n\"66\",\"xmltask\"\n\"66\",\"dbaccess\"\n\"66\",\"rosetta-stone\"\n\"66\",\"sitecore7.1\"\n\"66\",\"doi\"\n\"66\",\"django-profiles\"\n\"66\",\"mfp\"\n\"66\",\"docusignapextoolkit\"\n\"66\",\"jgrowl\"\n\"66\",\"rows-affected\"\n\"66\",\"mysqljs\"\n\"66\",\"jhipster-gateway\"\n\"66\",\"sitecollection\"\n\"66\",\"alexa-smart-home-skill\"\n\"66\",\"rlike\"\n\"66\",\"rspec-mocks\"\n\"66\",\"method-parameters\"\n\"66\",\"nativecall\"\n\"66\",\"canalyzer\"\n\"66\",\"vueuse\"\n\"66\",\"icenium\"\n\"66\",\"root-certificate\"\n\"66\",\"camlp4\"\n\"66\",\"wiktionary\"\n\"66\",\"readdirectorychangesw\"\n\"66\",\"servletexception\"\n\"66\",\"agm\"\n\"66\",\"gs1-128\"\n\"66\",\"twitpic\"\n\"66\",\"svg-edit\"\n\"66\",\"ion-select\"\n\"66\",\"openfaces\"\n\"66\",\"early-binding\"\n\"66\",\"dependent-name\"\n\"66\",\"onem2m\"\n\"66\",\"wpf-extended-toolkit\"\n\"66\",\"mom\"\n\"66\",\"appsource\"\n\"66\",\"nsdatadetector\"\n\"66\",\"grunt-contrib-sass\"\n\"66\",\"spring-junit\"\n\"66\",\"swagger-maven-plugin\"\n\"66\",\"table-alias\"\n\"66\",\"ray-tune\"\n\"66\",\"android-architecture\"\n\"66\",\"outer-apply\"\n\"66\",\"kraken.com\"\n\"66\",\"brave-browser\"\n\"66\",\"google-appengine-node\"\n\"66\",\"kotlin-sharedflow\"\n\"66\",\"rancher-desktop\"\n\"66\",\"macvlan\"\n\"66\",\"system.net.sockets\"\n\"66\",\"machine-instruction\"\n\"66\",\"system-alert-window\"\n\"66\",\"magenta\"\n\"66\",\"build-variant\"\n\"66\",\"libgphoto2\"\n\"66\",\"wml\"\n\"66\",\"microsoft-speech-platform\"\n\"66\",\"javascript-marked\"\n\"66\",\"dom-repeat\"\n\"66\",\"pyfftw\"\n\"66\",\"library-design\"\n\"66\",\"bubble.io\"\n\"66\",\"24-bit\"\n\"66\",\"ammonite\"\n\"66\",\"tmuxinator\"\n\"66\",\"jalali-calendar\"\n\"66\",\"targets-r-package\"\n\"66\",\"scanpy\"\n\"66\",\"cookiemanager\"\n\"66\",\"target-action\"\n\"66\",\"sqlitejdbc\"\n\"66\",\"asp.net-core-scaffolding\"\n\"66\",\"time-measurement\"\n\"66\",\"plumatic-schema\"\n\"66\",\"dropnet\"\n\"66\",\"devserver\"\n\"66\",\"spring-social-twitter\"\n\"66\",\"pointer-aliasing\"\n\"66\",\"dialog-preference\"\n\"66\",\"noty\"\n\"66\",\"ispostback\"\n\"66\",\"wxgrid\"\n\"66\",\"tix\"\n\"66\",\"openmeetings\"\n\"66\",\"hal-json\"\n\"66\",\"nsurlcredential\"\n\"66\",\"redisclient\"\n\"66\",\"excellibrary\"\n\"66\",\"google-datastream\"\n\"66\",\"qmdiarea\"\n\"66\",\"protobuf-python\"\n\"66\",\"prolog-findall\"\n\"66\",\"splines\"\n\"66\",\"propel2\"\n\"66\",\"loglog\"\n\"66\",\"google-dl-platform\"\n\"66\",\"ton\"\n\"66\",\"lavalamp\"\n\"66\",\"cfqueryparam\"\n\"66\",\"mellanox\"\n\"66\",\"google-cloud-code\"\n\"66\",\"http-status-code-204\"\n\"66\",\"hugs\"\n\"66\",\"iota\"\n\"66\",\"layout-animation\"\n\"66\",\"memory-optimized-tables\"\n\"66\",\"nlua\"\n\"66\",\"lstm-stateful\"\n\"66\",\"launch-agent\"\n\"66\",\"commutativity\"\n\"66\",\"angularjs-bindings\"\n\"66\",\"cssom\"\n\"66\",\"locomotivejs\"\n\"66\",\"colima\"\n\"66\",\"prepros\"\n\"66\",\"sublime-anaconda\"\n\"66\",\"google-maps-urls\"\n\"66\",\"automated-deployment\"\n\"66\",\"elmish\"\n\"66\",\"lifelines\"\n\"66\",\"solidworksapi\"\n\"66\",\"panresponder\"\n\"66\",\"urlvariables\"\n\"66\",\"webengine\"\n\"66\",\"line-profiler\"\n\"66\",\"google-pagespeed-insights-api\"\n\"66\",\"zoneinfo\"\n\"66\",\"web-garden\"\n\"66\",\"parallel-python\"\n\"66\",\"ie8-browser-mode\"\n\"66\",\"mdbreact\"\n\"66\",\"linearization\"\n\"66\",\"urlrewriter.net\"\n\"65\",\"report-builder2.0\"\n\"65\",\"jaxws-maven-plugin\"\n\"65\",\"ed\"\n\"65\",\"materialbutton\"\n\"65\",\"jenkins-build-flow\"\n\"65\",\"cluetip\"\n\"65\",\"skpaymenttransaction\"\n\"65\",\"react-native-webrtc\"\n\"65\",\"ecmascript-2020\"\n\"65\",\"local-class\"\n\"65\",\"react-native-swiper\"\n\"65\",\"react-native-permissions\"\n\"65\",\"defaultbutton\"\n\"65\",\"git-interactive-rebase\"\n\"65\",\"troposphere\"\n\"65\",\"getstream-chat\"\n\"65\",\"cloudwatch\"\n\"65\",\"instagram-story\"\n\"65\",\"vue-mixin\"\n\"65\",\"background-transfer\"\n\"65\",\"aos.js\"\n\"65\",\"principles\"\n\"65\",\"matlab-load\"\n\"65\",\"classloading\"\n\"65\",\"multiple-entries\"\n\"65\",\"pg-trgm\"\n\"65\",\"react-native-drawer\"\n\"65\",\"teamsite\"\n\"65\",\"wii\"\n\"65\",\"mapinfo\"\n\"65\",\"directxtk\"\n\"65\",\"switch-expression\"\n\"65\",\"apache-toree\"\n\"65\",\"snapping\"\n\"65\",\"xposed-framework\"\n\"65\",\"markitup\"\n\"65\",\"ngx-extended-pdf-viewer\"\n\"65\",\"apache-royale\"\n\"65\",\"firebird-.net-provider\"\n\"65\",\"db2-connect\"\n\"65\",\"adehabitathr\"\n\"65\",\"incoming-mail\"\n\"65\",\"jsvc\"\n\"65\",\"kube-prometheus-stack\"\n\"65\",\"l2tp\"\n\"65\",\"django-ajax-selects\"\n\"65\",\"jsperf\"\n\"65\",\"distcc\"\n\"65\",\"jsdt\"\n\"65\",\"funq\"\n\"65\",\"pivotaltracker\"\n\"65\",\"afp\"\n\"65\",\"python-regex\"\n\"65\",\"bit-packing\"\n\"65\",\"pades\"\n\"65\",\"disnake\"\n\"65\",\"function-fitting\"\n\"65\",\"pixel-density\"\n\"65\",\"px4\"\n\"65\",\"go-sqlmock\"\n\"65\",\"readable\"\n\"65\",\"iban\"\n\"65\",\"jpda\"\n\"65\",\"hyperledger-fabric-sdk-go\"\n\"65\",\"value-class\"\n\"65\",\"icepdf\"\n\"65\",\"recursive-type\"\n\"65\",\"docker-exec\"\n\"65\",\"cross-origin-resource-policy\"\n\"65\",\"database-view\"\n\"65\",\"django-notification\"\n\"65\",\"rss2\"\n\"65\",\"microsoft-forms\"\n\"65\",\"cross-cutting-concerns\"\n\"65\",\"roundtrip\"\n\"65\",\"mysql-x-devapi\"\n\"65\",\"server-side-attacks\"\n\"65\",\"datadirectory\"\n\"65\",\"n-api\"\n\"65\",\"metis\"\n\"65\",\"candlesticks\"\n\"65\",\"factor-lang\"\n\"65\",\"docxtemplater\"\n\"65\",\"jqmobi\"\n\"65\",\"input-parameters\"\n\"65\",\"spring-mvc-initbinders\"\n\"65\",\"dynamic-ui\"\n\"65\",\"opencv-mat\"\n\"65\",\"design-view\"\n\"65\",\"swift-framework\"\n\"65\",\"password-manager\"\n\"65\",\"iolanguage\"\n\"65\",\"guard-clause\"\n\"65\",\"openconnect\"\n\"65\",\"application-verifier\"\n\"65\",\"android-studio-4.2\"\n\"65\",\"wscript.shell\"\n\"65\",\"patroni\"\n\"65\",\"html-object\"\n\"65\",\"android-studio-3.6\"\n\"65\",\"insecure-connection\"\n\"65\",\"apple-app-site-associate\"\n\"65\",\"dynamic-script-loading\"\n\"65\",\"onejar\"\n\"65\",\"payflowlink\"\n\"65\",\"risk-analysis\"\n\"65\",\"visual-prolog\"\n\"65\",\"ripgrep\"\n\"65\",\".emf\"\n\"65\",\"codenarc\"\n\"65\",\"rave-reports\"\n\"65\",\"microsoft-graph-intune\"\n\"65\",\"for-json\"\n\"65\",\"libarchive\"\n\"65\",\"fortran2008\"\n\"65\",\"browser-console\"\n\"65\",\"viewmodifier\"\n\"65\",\"viridis\"\n\"65\",\"codehighlighter\"\n\"65\",\"wolfssl\"\n\"65\",\".aspxauth\"\n\"65\",\"ammo.js\"\n\"65\",\"gccgo\"\n\"65\",\"rails-3.1\"\n\"65\",\"asp.net-development-serv\"\n\"65\",\"handlebars.java\"\n\"65\",\"schema-migration\"\n\"65\",\"non-alphanumeric\"\n\"65\",\"draggesture\"\n\"65\",\"reflex\"\n\"65\",\"dijit.tree\"\n\"65\",\"cookielib\"\n\"65\",\"regex-replace\"\n\"65\",\"rack-middleware\"\n\"65\",\"numpy-indexing\"\n\"65\",\"hive-udf\"\n\"65\",\"android-gravity\"\n\"65\",\"nme\"\n\"65\",\"pg8000\"\n\"65\",\"ipojo\"\n\"65\",\"mpi-io\"\n\"65\",\"lpt\"\n\"65\",\"esri-leaflet\"\n\"65\",\"geosparql\"\n\"65\",\"memory-warning\"\n\"65\",\"pywavelets\"\n\"65\",\"android-jack-and-jill\"\n\"65\",\"react-native-community-netinfo\"\n\"65\",\"perl-pod\"\n\"65\",\"cfgrid\"\n\"65\",\"eulers-number\"\n\"65\",\"reactive-kafka\"\n\"65\",\"qmetaobject\"\n\"65\",\"comonad\"\n\"65\",\"leaflet-geoman\"\n\"65\",\"iphoto\"\n\"65\",\"message-listener\"\n\"65\",\"coldfusion-7\"\n\"65\",\"omniorb\"\n\"65\",\"stringgrid\"\n\"65\",\"custom-backend\"\n\"65\",\"billboard.js\"\n\"65\",\"statamic\"\n\"65\",\"global-state\"\n\"65\",\"google-nearby-connections\"\n\"65\",\"gnn\"\n\"65\",\"heaps-algorithm\"\n\"65\",\"amazon-cognito-facebook\"\n\"65\",\"gl-triangle-strip\"\n\"65\",\"cvi\"\n\"65\",\"foolproof-validation\"\n\"65\",\"authzforce\"\n\"65\",\"automapper-4\"\n\"65\",\"styleddocument\"\n\"65\",\"google-pie-chart\"\n\"65\",\"ido\"\n\"65\",\"foreign-collection\"\n\"65\",\"qt5.9\"\n\"65\",\"ie-automation\"\n\"65\",\"quantization-aware-training\"\n\"65\",\"flutter-in-app-purchase\"\n\"65\",\"paredit\"\n\"65\",\"sparkcore\"\n\"65\",\"mqtt.js\"\n\"65\",\"git-shell\"\n\"65\",\"std-future\"\n\"65\",\"tr24731\"\n\"65\",\"medium-editor\"\n\"65\",\"linker-warning\"\n\"65\",\"zend-tool\"\n\"65\",\"stm32h7\"\n\"64\",\"phpexcel-1.8.0\"\n\"64\",\"github-release\"\n\"64\",\"cldr\"\n\"64\",\"yara\"\n\"64\",\"react-native-sqlite-storage\"\n\"64\",\"trivy\"\n\"64\",\"remotecommand\"\n\"64\",\"back-button-control\"\n\"64\",\"matroska\"\n\"64\",\"photologue\"\n\"64\",\"ffbase\"\n\"64\",\"profile-picture\"\n\"64\",\"great-firewall-of-china\"\n\"64\",\"marko\"\n\"64\",\"intel-mpi\"\n\"64\",\"vuetify-datatable\"\n\"64\",\"c-libraries\"\n\"64\",\"liquid-template\"\n\"64\",\"yii-widgets\"\n\"64\",\"snowfall\"\n\"64\",\"kubernetes-rbac\"\n\"64\",\"addin-express\"\n\"64\",\"ccaction\"\n\"64\",\"implicit-declaration\"\n\"64\",\"adodb-php\"\n\"64\",\"django-cron\"\n\"64\",\"chronoforms\"\n\"64\",\"ovirt\"\n\"64\",\"pixel-bender\"\n\"64\",\"adxstudio-portals\"\n\"64\",\"xdr\"\n\"64\",\"function-approximation\"\n\"64\",\"bionic\"\n\"64\",\"full-table-scan\"\n\"64\",\"flashback\"\n\"64\",\"apollo-cache-inmemory\"\n\"64\",\"managed-directx\"\n\"64\",\"first-chance-exception\"\n\"64\",\"smoke-testing\"\n\"64\",\"xhtml-transitional\"\n\"64\",\"pipedrive-api\"\n\"64\",\"palm\"\n\"64\",\"alex\"\n\"64\",\"dojo-build\"\n\"64\",\"ptc-windchill\"\n\"64\",\"alfred\"\n\"64\",\"datafield\"\n\"64\",\"dataflow-diagram\"\n\"64\",\"nanoframework\"\n\"64\",\"routetable\"\n\"64\",\"documentum6.5\"\n\"64\",\"rstanarm\"\n\"64\",\"docbook-5\"\n\"64\",\"jil\"\n\"64\",\"venmo\"\n\"64\",\"google-website-optimizer\"\n\"64\",\"rope\"\n\"64\",\"nativewind\"\n\"64\",\"negamax\"\n\"64\",\"opc-da\"\n\"64\",\"enterprise-library-6\"\n\"64\",\"nesper\"\n\"64\",\"nservicebus-sagas\"\n\"64\",\"bootstrap-tokenfield\"\n\"64\",\"spring-data-mongodb-reactive\"\n\"64\",\"android-virtual-keyboard\"\n\"64\",\"spring-data-envers\"\n\"64\",\"dynamics-ax7\"\n\"64\",\"modi\"\n\"64\",\"axacropdf\"\n\"64\",\"tufte\"\n\"64\",\"axis2c\"\n\"64\",\"hp-nonstop\"\n\"64\",\"inherited-widget\"\n\"64\",\"delicious-api\"\n\"64\",\"boofcv\"\n\"64\",\"entity-framework-core-migrations\"\n\"64\",\"onos\"\n\"64\",\"pdl\"\n\"64\",\"cosign-api\"\n\"64\",\"woocommerce-memberships\"\n\"64\",\"object-serialization\"\n\"64\",\"orchardcms-1.10\"\n\"64\",\"winnovative\"\n\"64\",\"accent-insensitive\"\n\"64\",\"osisoft\"\n\"64\",\"r-googlesheets\"\n\"64\",\"sigfpe\"\n\"64\",\"vline\"\n\"64\",\"godeps\"\n\"64\",\"libimobiledevice\"\n\"64\",\"goo.gl\"\n\"64\",\"asyncapi\"\n\"64\",\"wm-copydata\"\n\"64\",\"rangevalidator\"\n\"64\",\"lvalue-to-rvalue\"\n\"64\",\"misspelling\"\n\"64\",\"typescript-class\"\n\"64\",\"ripple-effect\"\n\"64\",\"visual-c++-2013\"\n\"64\",\"nethereum\"\n\"64\",\"facebook-feed\"\n\"64\",\"virtual-desktop\"\n\"64\",\"numpydoc\"\n\"64\",\"refile\"\n\"64\",\"dhall\"\n\"64\",\"dotnet-aspire\"\n\"64\",\"bybit\"\n\"64\",\"sqlbase\"\n\"64\",\"scalapack\"\n\"64\",\"rabbitmq-shovel\"\n\"64\",\"coords\"\n\"64\",\"xcodeproj\"\n\"64\",\"point-of-interest\"\n\"64\",\"nsvalue\"\n\"64\",\"dreamfactory\"\n\"64\",\"redmi-device\"\n\"64\",\"uiinputviewcontroller\"\n\"64\",\"handoff\"\n\"64\",\"gaps-in-visuals\"\n\"64\",\"sqlite-json1\"\n\"64\",\"pnotify\"\n\"64\",\"numeric-keypad\"\n\"64\",\"exchange-server-2016\"\n\"64\",\"taffydb\"\n\"64\",\"tao-framework\"\n\"64\",\"openoffice-base\"\n\"64\",\"ironsource\"\n\"64\",\"motorola-droid\"\n\"64\",\"geoip2\"\n\"64\",\"spreadsheetlight\"\n\"64\",\"nls-lang\"\n\"64\",\"iri\"\n\"64\",\"spfx-extension\"\n\"64\",\"angular-daterangepicker\"\n\"64\",\"google-domain-api\"\n\"64\",\"nlu\"\n\"64\",\"android-jetpack-compose-layout\"\n\"64\",\"geom-hline\"\n\"64\",\"exadata\"\n\"64\",\"performance-monitor\"\n\"64\",\"customer-account-data-api\"\n\"64\",\"respond.js\"\n\"64\",\"resty-gwt\"\n\"64\",\"electron-react-boilerplate\"\n\"64\",\"node-orm2\"\n\"64\",\"ldflags\"\n\"64\",\"mule-cluster\"\n\"64\",\"arithmeticexception\"\n\"64\",\"partial-postback\"\n\"64\",\"scriptella\"\n\"64\",\"pantheon\"\n\"64\",\"web-architecture\"\n\"64\",\"stateserver\"\n\"64\",\"dandelion\"\n\"64\",\"mrunit\"\n\"64\",\"fluxcd\"\n\"64\",\"zend-date\"\n\"64\",\"autogeneratecolumn\"\n\"64\",\"autogrow\"\n\"64\",\"amazon-appflow\"\n\"64\",\"qstyle\"\n\"64\",\"git-untracked\"\n\"64\",\"secure-crt\"\n\"64\",\"starscream\"\n\"64\",\"scrollcontroller\"\n\"64\",\"glitch-framework\"\n\"64\",\"alivepdf\"\n\"64\",\"articulate-storyline\"\n\"64\",\"uwamp\"\n\"63\",\"web-share\"\n\"63\",\"github-codereviews\"\n\"63\",\"background-application\"\n\"63\",\"reportng\"\n\"63\",\"xvalue\"\n\"63\",\"tealium\"\n\"63\",\"ef-model-builder\"\n\"63\",\"ssziparchive\"\n\"63\",\"template-inheritance\"\n\"63\",\"ssg\"\n\"63\",\"reportgenerator\"\n\"63\",\"mxe\"\n\"63\",\"graphical-programming\"\n\"63\",\"localtunnel\"\n\"63\",\"backbone-forms\"\n\"63\",\"ssao\"\n\"63\",\"private-inheritance\"\n\"63\",\"smartadmin\"\n\"63\",\"greenhills\"\n\"63\",\"fhir-server-for-azure\"\n\"63\",\"process-explorer\"\n\"63\",\"webpagetest\"\n\"63\",\"react-native-linking\"\n\"63\",\"apache-beam-kafkaio\"\n\"63\",\"llvmlite\"\n\"63\",\"liveedit\"\n\"63\",\"xml-dml\"\n\"63\",\"packageinstaller\"\n\"63\",\"p6spy\"\n\"63\",\"xoom\"\n\"63\",\"unit-type\"\n\"63\",\"umbraco-ucommerce\"\n\"63\",\"imap-open\"\n\"63\",\"uncaught-reference-error\"\n\"63\",\"mantissa\"\n\"63\",\"xlabs\"\n\"63\",\"fstab\"\n\"63\",\"python-textfsm\"\n\"63\",\"distribution-list\"\n\"63\",\"xinetd\"\n\"63\",\"adminjs\"\n\"63\",\"datamodule\"\n\"63\",\"jscience\"\n\"63\",\"fitsharp\"\n\"63\",\"flask-script\"\n\"63\",\"choetl\"\n\"63\",\"cim\"\n\"63\",\"smartsvn\"\n\"63\",\"pixelate\"\n\"63\",\"data-tier-applications\"\n\"63\",\"cielab\"\n\"63\",\"microsoft-ajax\"\n\"63\",\"angularjs-ng-class\"\n\"63\",\"fantom\"\n\"63\",\"wcf-routing\"\n\"63\",\"go-zap\"\n\"63\",\"rubaxa-sortable\"\n\"63\",\"rubber-band\"\n\"63\",\"shared-data\"\n\"63\",\"rtems\"\n\"63\",\"kitti\"\n\"63\",\"sitecore-workflow\"\n\"63\",\"named-graphs\"\n\"63\",\"mysql-error-1146\"\n\"63\",\"vwdexpress\"\n\"63\",\"angular-material-datetimepicker\"\n\"63\",\"granularity\"\n\"63\",\"graaljs\"\n\"63\",\"server-rendering\"\n\"63\",\"datahandler\"\n\"63\",\"wamp64\"\n\"63\",\"myget\"\n\"63\",\"aggregateexception\"\n\"63\",\"invitation\"\n\"63\",\"html-help\"\n\"63\",\"type-2-dimension\"\n\"63\",\"httpapplication\"\n\"63\",\"sast\"\n\"63\",\"nested-map\"\n\"63\",\"jump-table\"\n\"63\",\"boost-coroutine\"\n\"63\",\"android-unity-plugin\"\n\"63\",\"apps-for-office\"\n\"63\",\"pdfpages\"\n\"63\",\"azure-devops-hosted-agent\"\n\"63\",\"svndump\"\n\"63\",\"deployment-target\"\n\"63\",\"nats-jetstream\"\n\"63\",\"passport-twitter\"\n\"63\",\"delphi-ide\"\n\"63\",\"springlayout\"\n\"63\",\"houdini\"\n\"63\",\"blockchain.info-api\"\n\"63\",\"saxon-js\"\n\"63\",\"android-speech-api\"\n\"63\",\"appstorage\"\n\"63\",\"cppyy\"\n\"63\",\"nservicebus3\"\n\"63\",\"ws-client\"\n\"63\",\"visual-artifacts\"\n\"63\",\"fortrabbit\"\n\"63\",\"browsershot\"\n\"63\",\"ratchet-2\"\n\"63\",\"system.security\"\n\"63\",\"richtextfx\"\n\"63\",\"8thwall-xr\"\n\"63\",\"libmemcache\"\n\"63\",\"ableton-live\"\n\"63\",\"objectscript\"\n\"63\",\"2048\"\n\"63\",\"object-to-string\"\n\"63\",\"tablefilter\"\n\"63\",\"vimage\"\n\"63\",\"cocos2d-html5\"\n\"63\",\"osqa\"\n\"63\",\"vms\"\n\"63\",\"google-bucket\"\n\"63\",\"rd\"\n\"63\",\"authority\"\n\"63\",\"rgui\"\n\"63\",\"go-map\"\n\"63\",\"typo3-7.x\"\n\"63\",\"tablegateway\"\n\"63\",\"rcw\"\n\"63\",\"outlook-for-mac\"\n\"63\",\"ocra\"\n\"63\",\"coco\"\n\"63\",\"attr-encrypted\"\n\"63\",\".net-4.7.1\"\n\"63\",\"rauth\"\n\"63\",\"shred\"\n\"63\",\"retro-computing\"\n\"63\",\"luminance\"\n\"63\",\"retrywhen\"\n\"63\",\"virtual-network\"\n\"63\",\"wordcloud2\"\n\"63\",\"oas\"\n\"63\",\".npmrc\"\n\"63\",\"vidyo\"\n\"63\",\"redeclaration\"\n\"63\",\"contracts\"\n\"63\",\"uiprogressbar\"\n\"63\",\"directfb\"\n\"63\",\"contiki-ng\"\n\"63\",\"j#\"\n\"63\",\"timeofday\"\n\"63\",\"nullif\"\n\"63\",\"dotcmis\"\n\"63\",\"dtf\"\n\"63\",\"item-decoration\"\n\"63\",\"mo\"\n\"63\",\"harmon.ie\"\n\"63\",\"qurl\"\n\"63\",\"gatling-plugin\"\n\"63\",\"uisearchresultscontroller\"\n\"63\",\"gdataxml\"\n\"63\",\"mobilefirst-appcenter\"\n\"63\",\"coordinator-pattern\"\n\"63\",\"harp\"\n\"63\",\"azure-site-recovery\"\n\"63\",\"general-network-error\"\n\"63\",\"table-structure\"\n\"63\",\"openmq\"\n\"63\",\"asp.net-webhooks\"\n\"63\",\"oneclick\"\n\"63\",\"mpiexec\"\n\"63\",\"pfrelation\"\n\"63\",\"terraform-aws-modules\"\n\"63\",\"android-pagetransformer\"\n\"63\",\"ofx\"\n\"63\",\"get-mapping\"\n\"63\",\"collapsiblepanelextender\"\n\"63\",\"perl-critic\"\n\"63\",\"leadbolt\"\n\"63\",\"generic-type-parameters\"\n\"63\",\"qpropertyanimation\"\n\"63\",\"qproperty\"\n\"63\",\"http-unit\"\n\"63\",\"tesselation\"\n\"63\",\"test-class\"\n\"63\",\"elixir-poison\"\n\"63\",\"strongswan\"\n\"63\",\"member-access\"\n\"63\",\"logos\"\n\"63\",\"react-hoc\"\n\"63\",\"tomcat-jdbc\"\n\"63\",\"google-shared-contacts\"\n\"63\",\"ember-octane\"\n\"63\",\"lifo\"\n\"63\",\"quectel\"\n\"63\",\"asammdf\"\n\"63\",\"mecab\"\n\"63\",\"auto-close\"\n\"63\",\"structlog\"\n\"63\",\"pascalcasing\"\n\"63\",\"iis-manager\"\n\"63\",\"embedded-browser\"\n\"63\",\"illegal-instruction\"\n\"63\",\"qualified-name\"\n\"63\",\"zabbix-api\"\n\"63\",\"qtplugin\"\n\"63\",\"elpy\"\n\"63\",\"haskell-mode\"\n\"63\",\"mediabrowserservicecompat\"\n\"63\",\"querypath\"\n\"63\",\"seesaw\"\n\"63\",\"parameterized-tests\"\n\"63\",\"complex-networks\"\n\"63\",\"powerdns\"\n\"63\",\"beancreationexception\"\n\"63\",\"focusmanager\"\n\"63\",\"zendesk-app\"\n\"63\",\"flutterwave\"\n\"63\",\"alibaba-cloud-ecs\"\n\"63\",\"arbor.js\"\n\"62\",\"apache-commons-daemon\"\n\"62\",\"debugview\"\n\"62\",\"photon-pun\"\n\"62\",\"apache-commons-codec\"\n\"62\",\"install-name-tool\"\n\"62\",\"sql-server-json\"\n\"62\",\"loadnibnamed\"\n\"62\",\"llama-cpp-python\"\n\"62\",\"tritium\"\n\"62\",\"instantiationexception\"\n\"62\",\"load-path\"\n\"62\",\"photosphere\"\n\"62\",\"easyslider\"\n\"62\",\"clique-problem\"\n\"62\",\"master-data-management\"\n\"62\",\"massif\"\n\"62\",\"gh-unit\"\n\"62\",\"mwphotobrowser\"\n\"62\",\"graphql-schema\"\n\"62\",\"clientbundle\"\n\"62\",\"when-js\"\n\"62\",\"skyscanner\"\n\"62\",\"xunit2\"\n\"62\",\"private-network\"\n\"62\",\"mathquill\"\n\"62\",\"default-interface-member\"\n\"62\",\"deface\"\n\"62\",\"vows\"\n\"62\",\"chomsky-normal-form\"\n\"62\",\"running-count\"\n\"62\",\"s3-kafka-connector\"\n\"62\",\"nhibernate-envers\"\n\"62\",\"dbfit\"\n\"62\",\"appgyver\"\n\"62\",\"pkgdown\"\n\"62\",\"umbraco5\"\n\"62\",\"labwindows\"\n\"62\",\"ims\"\n\"62\",\"pintos\"\n\"62\",\"adgroup\"\n\"62\",\"unchecked-exception\"\n\"62\",\"addobserver\"\n\"62\",\"xhprof\"\n\"62\",\"uncss\"\n\"62\",\"fiware-wilma\"\n\"62\",\"makie.jl\"\n\"62\",\"pancakeswap\"\n\"62\",\"binlog\"\n\"62\",\"firefox-quantum\"\n\"62\",\"windows-2000\"\n\"62\",\"hyperledger-caliper\"\n\"62\",\"dll-reference\"\n\"62\",\"windows-rs\"\n\"62\",\"camel-cxf\"\n\"62\",\"albumentations\"\n\"62\",\"update-site\"\n\"62\",\"ropensci\"\n\"62\",\"routerlinkactive\"\n\"62\",\"rprofile\"\n\"62\",\"database-diagram\"\n\"62\",\"pvs-studio\"\n\"62\",\"meta-predicate\"\n\"62\",\"simpsons-rule\"\n\"62\",\"reasoner\"\n\"62\",\"pure-css\"\n\"62\",\"kendo-validator\"\n\"62\",\"vaapi\"\n\"62\",\"rpython\"\n\"62\",\"keypaths\"\n\"62\",\"nagiosxi\"\n\"62\",\"unity-web-player\"\n\"62\",\"universal-reference\"\n\"62\",\"kepler\"\n\"62\",\"name-clash\"\n\"62\",\"jquery-ui-touch-punch\"\n\"62\",\"keepass\"\n\"62\",\"box-sizing\"\n\"62\",\"aws-sdk-js-v3\"\n\"62\",\"wsdualhttpbinding\"\n\"62\",\"counterclockwise\"\n\"62\",\"jxta\"\n\"62\",\"juggernaut\"\n\"62\",\"hpcc\"\n\"62\",\"www-authenticate\"\n\"62\",\"dynamic-type-feature\"\n\"62\",\"jqtransform\"\n\"62\",\"on-premises-instances\"\n\"62\",\"initializecomponent\"\n\"62\",\"pdp\"\n\"62\",\"jquery-mobile-navbar\"\n\"62\",\"poplib\"\n\"62\",\"spring-initializr\"\n\"62\",\"lucidworks\"\n\"62\",\"setrlimit\"\n\"62\",\"lucid\"\n\"62\",\"audio-worklet\"\n\"62\",\"windows-users\"\n\"62\",\"form-load\"\n\"62\",\"go-context\"\n\"62\",\"buildah\"\n\"62\",\"visual-studio-app-center-distribute\"\n\"62\",\"audiovideoplayback\"\n\"62\",\"libnet\"\n\"62\",\"android-autofill-manager\"\n\"62\",\"kotlin-js-interop\"\n\"62\",\"google-api-javascript-client\"\n\"62\",\"pyngrok\"\n\"62\",\"ubuntu-9.04\"\n\"62\",\"lgpl\"\n\"62\",\"java-19\"\n\"62\",\"viewdidunload\"\n\"62\",\"mailcatcher\"\n\"62\",\"system-requirements\"\n\"62\",\"visionkit\"\n\"62\",\"facebook-messages\"\n\"62\",\"build-events\"\n\"62\",\"android-4.1-jelly-bean\"\n\"62\",\"virtualalloc\"\n\"62\",\"vlang\"\n\"62\",\"driver-signing\"\n\"62\",\"jacorb\"\n\"62\",\"exe4j\"\n\"62\",\"poller\"\n\"62\",\"versioninfo\"\n\"62\",\"dredd\"\n\"62\",\"contextroot\"\n\"62\",\"highcharts-gantt\"\n\"62\",\"bump-mapping\"\n\"62\",\"playstation\"\n\"62\",\"hardware-id\"\n\"62\",\"scala-xml\"\n\"62\",\"azure-media-player\"\n\"62\",\"redistogo\"\n\"62\",\"pointerlock\"\n\"62\",\"modal-view\"\n\"62\",\"playorm\"\n\"62\",\"exchange-online\"\n\"62\",\"excel-lambda\"\n\"62\",\"gvnix\"\n\"62\",\"assisted-inject\"\n\"62\",\"directory-permissions\"\n\"62\",\"digital-persona-sdk\"\n\"62\",\"dotmemory\"\n\"62\",\"quicksight-embedding\"\n\"62\",\"azure-stack\"\n\"62\",\"mkmapitem\"\n\"62\",\"opennebula\"\n\"62\",\"redcloth\"\n\"62\",\"android-custom-attributes\"\n\"62\",\"gant\"\n\"62\",\"gdax-api\"\n\"62\",\"pocket\"\n\"62\",\"terratest\"\n\"62\",\"generic-variance\"\n\"62\",\"iperf3\"\n\"62\",\"textctrl\"\n\"62\",\"ironworker\"\n\"62\",\"eula\"\n\"62\",\"perfect-hash\"\n\"62\",\"lastpass\"\n\"62\",\"okta-signin-widget\"\n\"62\",\"property-injection\"\n\"62\",\"google-cloud-pubsub-emulator\"\n\"62\",\"spookyjs\"\n\"62\",\"custom-attribute\"\n\"62\",\"node-html-pdf\"\n\"62\",\"lpcwstr\"\n\"62\",\"strtod\"\n\"62\",\"csvwriter\"\n\"62\",\"project-panama\"\n\"62\",\"eventtocommand\"\n\"62\",\"omegaconf\"\n\"62\",\"toolsapi\"\n\"62\",\"use-form\"\n\"62\",\"predix\"\n\"62\",\"security-policy\"\n\"62\",\"msbuild-target\"\n\"62\",\"authorized-keys\"\n\"62\",\"scxml\"\n\"62\",\"alter-column\"\n\"62\",\"soundfile\"\n\"62\",\"papertrail-app\"\n\"62\",\"zend-config\"\n\"62\",\"autobean\"\n\"62\",\"google-maps-styling\"\n\"62\",\"usmap\"\n\"62\",\"zend-http-client\"\n\"62\",\"auto-responder\"\n\"62\",\"imagedata\"\n\"62\",\"auto-route\"\n\"62\",\"tortoisecvs\"\n\"62\",\"image-clipping\"\n\"62\",\"user-variables\"\n\"62\",\"authsub\"\n\"62\",\"scriptlab\"\n\"62\",\"structural-pattern-matching\"\n\"62\",\"trailblazer\"\n\"62\",\"beast\"\n\"62\",\"mser\"\n\"62\",\"tilelist\"\n\"62\",\"thunderclient\"\n\"62\",\"userdetailsservice\"\n\"61\",\"phpdocx\"\n\"61\",\"stanza\"\n\"61\",\"vue-formulate\"\n\"61\",\"prism-5\"\n\"61\",\"sql-server-native-client\"\n\"61\",\"maui-ios\"\n\"61\",\"intellij-idea-2016\"\n\"61\",\"phpdesktop\"\n\"61\",\"matisse\"\n\"61\",\"installscript-msi\"\n\"61\",\"edb\"\n\"61\",\"background-sync\"\n\"61\",\"fluentftp\"\n\"61\",\"apache-directory\"\n\"61\",\"github-ci\"\n\"61\",\"react-pdfrenderer\"\n\"61\",\"r-environment\"\n\"61\",\"git-annex\"\n\"61\",\"marklogic-7\"\n\"61\",\"fieldofview\"\n\"61\",\"php-pest\"\n\"61\",\"integromat-apps\"\n\"61\",\"wgsl\"\n\"61\",\"slack-dialog\"\n\"61\",\"yourls\"\n\"61\",\"babel-cli\"\n\"61\",\"github-oauth\"\n\"61\",\"skphysicsworld\"\n\"61\",\"insertadjacenthtml\"\n\"61\",\"xemacs\"\n\"61\",\"frombodyattribute\"\n\"61\",\"swiftui-ontapgesture\"\n\"61\",\"swiftui-previews\"\n\"61\",\"dirty-data\"\n\"61\",\"paceautomationframework\"\n\"61\",\"xmpppy\"\n\"61\",\"swirl\"\n\"61\",\"swift-nio\"\n\"61\",\"rythm\"\n\"61\",\"adal4j\"\n\"61\",\"nexus-prisma\"\n\"61\",\"chomp\"\n\"61\",\"dataspell\"\n\"61\",\"rsl\"\n\"61\",\"akka-remoting\"\n\"61\",\"dmp\"\n\"61\",\"session-fixation\"\n\"61\",\"recent-file-list\"\n\"61\",\"rtl-sdr\"\n\"61\",\"oracle-rest-data-services\"\n\"61\",\"docx-mailmerge\"\n\"61\",\"go-pg\"\n\"61\",\"jnativehook\"\n\"61\",\"cryptarithmetic-puzzle\"\n\"61\",\"name-decoration\"\n\"61\",\"unreachable-statement\"\n\"61\",\"upstream-branch\"\n\"61\",\"oracleapplications\"\n\"61\",\"aws-s3-client\"\n\"61\",\"variants\"\n\"61\",\"fast-csv\"\n\"61\",\"angular-ng-class\"\n\"61\",\"urbancode\"\n\"61\",\"serverless-application-model\"\n\"61\",\"rtcmulticonnection\"\n\"61\",\"dlookup\"\n\"61\",\"windows-clustering\"\n\"61\",\"meteor-packages\"\n\"61\",\"postgresql-8.3\"\n\"61\",\"aws-vpc-peering\"\n\"61\",\"hotjar\"\n\"61\",\"supertype\"\n\"61\",\"core-file\"\n\"61\",\"popsql\"\n\"61\",\"dynamics-crm-365-v9\"\n\"61\",\"neo4j-java-api\"\n\"61\",\"nscursor\"\n\"61\",\"dvorak\"\n\"61\",\"erlang-nif\"\n\"61\",\"html-generation\"\n\"61\",\"pdf-writer\"\n\"61\",\"neo4j-browser\"\n\"61\",\"pausing-execution\"\n\"61\",\"azure-cosmosdb-tables\"\n\"61\",\"aptos\"\n\"61\",\"applicationcontroller\"\n\"61\",\"post-conditions\"\n\"61\",\"passport-local-mongoose\"\n\"61\",\"botman\"\n\"61\",\"nested-views\"\n\"61\",\"desctools\"\n\"61\",\"jwrapper\"\n\"61\",\"gulp-typescript\"\n\"61\",\"deselect\"\n\"61\",\"sap-successfactors\"\n\"61\",\"epoxy\"\n\"61\",\"modifier-key\"\n\"61\",\"silent-notification\"\n\"61\",\"java-calendar\"\n\"61\",\"video-upload\"\n\"61\",\"amp-list\"\n\"61\",\"codemirror-modes\"\n\"61\",\"fortran-common-block\"\n\"61\",\"wmi-service\"\n\"61\",\"codacy\"\n\"61\",\"attach-to-process\"\n\"61\",\"system-tables\"\n\"61\",\"google-app-engine-go\"\n\"61\",\"kotlinc\"\n\"61\",\"systemcolors\"\n\"61\",\"uci\"\n\"61\",\"midje\"\n\"61\",\"breakpoint-sass\"\n\"61\",\"word-2003\"\n\"61\",\"udeploy\"\n\"61\",\"rfc2822\"\n\"61\",\"formmail\"\n\"61\",\"knockout-templating\"\n\"61\",\"magento-layout-xml\"\n\"61\",\"abseil\"\n\"61\",\"codesourcery\"\n\"61\",\"knp-snappy\"\n\"61\",\"codeanywhere\"\n\"61\",\"maemo\"\n\"61\",\"bulk-operations\"\n\"61\",\"dotnet-tool\"\n\"61\",\"historian\"\n\"61\",\"convention-over-configur\"\n\"61\",\"gchart\"\n\"61\",\"drupal-comments\"\n\"61\",\"context-switching\"\n\"61\",\"gcloud-python\"\n\"61\",\"drbd\"\n\"61\",\"radchart\"\n\"61\",\"model-driven-development\"\n\"61\",\"android-image-capture\"\n\"61\",\"gated-checkin\"\n\"61\",\"mod-dav-svn\"\n\"61\",\"quick-search\"\n\"61\",\"modelbinder\"\n\"61\",\"r6rs\"\n\"61\",\"redux-mock-store\"\n\"61\",\"non-unicode\"\n\"61\",\"xamarin.forms.maps\"\n\"61\",\"npm-shrinkwrap\"\n\"61\",\"export-to-xml\"\n\"61\",\"sql-server-group-concat\"\n\"61\",\"tcpreplay\"\n\"61\",\"nsvaluetransformer\"\n\"61\",\"xctestexpectation\"\n\"61\",\"nsurlerrordomain\"\n\"61\",\"gwt-bootstrap\"\n\"61\",\"genson\"\n\"61\",\"node-config\"\n\"61\",\"officewriter\"\n\"61\",\"spreadjs\"\n\"61\",\"lowest-common-ancestor\"\n\"61\",\"respect-validation\"\n\"61\",\"ipaf\"\n\"61\",\"office-communicator\"\n\"61\",\"resiliency\"\n\"61\",\"zurb-joyride\"\n\"61\",\"hungarian-notation\"\n\"61\",\"hung\"\n\"61\",\"custom-event\"\n\"61\",\"locked-files\"\n\"61\",\"comfortable-mexican-sofa\"\n\"61\",\"android-layout-editor\"\n\"61\",\"project-loom\"\n\"61\",\"httpoison\"\n\"61\",\"njsonschema\"\n\"61\",\"colon-equals\"\n\"61\",\"angular-cli-v8\"\n\"61\",\"android-photoview\"\n\"61\",\"moped\"\n\"61\",\"geom-vline\"\n\"61\",\"elementary-os\"\n\"61\",\"react-image\"\n\"61\",\"cfform\"\n\"61\",\"resource-scheduling\"\n\"61\",\"tiki-wiki\"\n\"61\",\"hcaptcha\"\n\"61\",\"security-framework\"\n\"61\",\"msflexgrid\"\n\"61\",\"sttwitterapi\"\n\"61\",\"cyber-panel\"\n\"61\",\"lineseries\"\n\"61\",\"dacl\"\n\"61\",\"emblem.js\"\n\"61\",\"asciidoctor-pdf\"\n\"61\",\"font-style\"\n\"61\",\"stdev\"\n\"61\",\"auto-keras\"\n\"61\",\"allocatable-array\"\n\"61\",\"autoit-c#-wrapper\"\n\"61\",\"google-play-internal-testing\"\n\"61\",\"concourse-pipeline\"\n\"61\",\"flutter-positioned\"\n\"61\",\"tracesource\"\n\"61\",\"haystack\"\n\"61\",\"search-engine-bots\"\n\"61\",\"web-console\"\n\"60\",\"websolr\"\n\"60\",\"yearmonth\"\n\"60\",\"echonest\"\n\"60\",\"sitefinity-4\"\n\"60\",\"antenna-house\"\n\"60\",\"f-bounded-polymorphism\"\n\"60\",\"ant-colony\"\n\"60\",\"multitexturing\"\n\"60\",\"file-in-use\"\n\"60\",\"squishit\"\n\"60\",\"dbms-job\"\n\"60\",\"web-search\"\n\"60\",\"feature-flags\"\n\"60\",\"smalldatetime\"\n\"60\",\"slugify\"\n\"60\",\"react-snap\"\n\"60\",\"render-to-string\"\n\"60\",\"defer-keyword\"\n\"60\",\"skeleton-code\"\n\"60\",\"trellis\"\n\"60\",\"jdk6\"\n\"60\",\"widgetliveactivity\"\n\"60\",\"clicking\"\n\"60\",\"proget\"\n\"60\",\"flutter-aws-amplify\"\n\"60\",\"jet-sql\"\n\"60\",\"apache-apisix\"\n\"60\",\"tronweb\"\n\"60\",\"marklogic-optic-api\"\n\"60\",\"skobbler-maps\"\n\"60\",\"symfony-http-foundation\"\n\"60\",\"disclosure\"\n\"60\",\"imeoptions\"\n\"60\",\"datarelation\"\n\"60\",\"xproc\"\n\"60\",\"jsqlparser\"\n\"60\",\"marker-interfaces\"\n\"60\",\"conftest\"\n\"60\",\"jsr330\"\n\"60\",\"software-update\"\n\"60\",\"mappedbytebuffer\"\n\"60\",\"jscodeshift\"\n\"60\",\"s3distcp\"\n\"60\",\"circuit-diagram\"\n\"60\",\"image-replacement\"\n\"60\",\"piracy\"\n\"60\",\"flaui\"\n\"60\",\"jsforce\"\n\"60\",\"bindableproperty\"\n\"60\",\"ng-app\"\n\"60\",\"imageprocessor\"\n\"60\",\"xpack\"\n\"60\",\"firemonkey-fm2\"\n\"60\",\"datanitro\"\n\"60\",\"database-scan\"\n\"60\",\"gps-time\"\n\"60\",\"read-replication\"\n\"60\",\"servicepointmanager\"\n\"60\",\"jinput\"\n\"60\",\"django-rq\"\n\"60\",\"alfresco-maven\"\n\"60\",\"windows-phone-toolkit\"\n\"60\",\"until-loop\"\n\"60\",\"wcffacility\"\n\"60\",\"facebook-stream-story\"\n\"60\",\"react-to-print\"\n\"60\",\"psalm-php\"\n\"60\",\"alglib\"\n\"60\",\"algorand\"\n\"60\",\"keymaps\"\n\"60\",\"joomla-article\"\n\"60\",\"jooq-codegen\"\n\"60\",\"wasi\"\n\"60\",\"factoring\"\n\"60\",\"angular-loopback\"\n\"60\",\"react-tabs\"\n\"60\",\"oppo\"\n\"60\",\"warc\"\n\"60\",\"angular-nativescript\"\n\"60\",\"avisynth\"\n\"60\",\"cal\"\n\"60\",\"spring-cloud-bus\"\n\"60\",\"ionic-vue\"\n\"60\",\"azkaban\"\n\"60\",\"jrun\"\n\"60\",\"onitemlongclicklistener\"\n\"60\",\"spring-data-commons\"\n\"60\",\"boost-spirit-lex\"\n\"60\",\"scala-generics\"\n\"60\",\"html-components\"\n\"60\",\"axshockwaveflash\"\n\"60\",\"appstats\"\n\"60\",\"sap-cap\"\n\"60\",\"dx-data-grid\"\n\"60\",\"initramfs\"\n\"60\",\"path-separator\"\n\"60\",\"mongodb-nodejs-driver\"\n\"60\",\"interface-segregation-principle\"\n\"60\",\"azure-appfabric\"\n\"60\",\"popviewcontrolleranimated\"\n\"60\",\"nscontrol\"\n\"60\",\"apple-pdfkit\"\n\"60\",\"azure-elastic-scale\"\n\"60\",\"mido\"\n\"60\",\"set-comprehension\"\n\"60\",\"shogun\"\n\"60\",\"rfc5766turnserver\"\n\"60\",\"pyroot\"\n\"60\",\"pylatex\"\n\"60\",\"jasper-plugin\"\n\"60\",\"microsoft-graph-onenote\"\n\"60\",\"signaling\"\n\"60\",\"vista64\"\n\"60\",\"miracast\"\n\"60\",\"network-service\"\n\"60\",\"riak-search\"\n\"60\",\"typeddict\"\n\"60\",\"libressl\"\n\"60\",\"ably-realtime\"\n\"60\",\"gofmt\"\n\"60\",\"google-app-indexing\"\n\"60\",\"t3\"\n\"60\",\"ntpd\"\n\"60\",\"non-modal\"\n\"60\",\"associated-object\"\n\"60\",\"iunknown\"\n\"60\",\"dial-up\"\n\"60\",\"uialertviewdelegate\"\n\"60\",\"isomorphic-fetch-api\"\n\"60\",\"hksamplequery\"\n\"60\",\"genealogy\"\n\"60\",\"gwt-2.5\"\n\"60\",\"sqlps\"\n\"60\",\"version-control-migration\"\n\"60\",\"gboard\"\n\"60\",\"hashcat\"\n\"60\",\"plugin-architecture\"\n\"60\",\"scale-color-manual\"\n\"60\",\"uidynamicanimator\"\n\"60\",\"itemcontainerstyle\"\n\"60\",\"digg\"\n\"60\",\"hook-form-alter\"\n\"60\",\"hook-menu\"\n\"60\",\"xbim\"\n\"60\",\"azure-static-website-hosting\"\n\"60\",\"spdep\"\n\"60\",\"movilizer\"\n\"60\",\"ehcache-bigmemory\"\n\"60\",\"custom-painter\"\n\"60\",\"angular2-di\"\n\"60\",\"test-framework\"\n\"60\",\"android-network-security-config\"\n\"60\",\"acts-as-taggable\"\n\"60\",\"percona-xtradb-cluster\"\n\"60\",\"lawnchair\"\n\"60\",\"evaluator\"\n\"60\",\"change-notification\"\n\"60\",\"zurb-ink\"\n\"60\",\"android-ondestroy\"\n\"60\",\"pedestal\"\n\"60\",\"pentaho-ctools\"\n\"60\",\"spire.doc\"\n\"60\",\"requestscope\"\n\"60\",\"actionbardrawertoggle\"\n\"60\",\"tessnet2\"\n\"60\",\"ninject-interception\"\n\"60\",\"commodore\"\n\"60\",\"character-limit\"\n\"60\",\"nineoldandroids\"\n\"60\",\"motion-planning\"\n\"60\",\"generative-art\"\n\"60\",\"ios-multithreading\"\n\"60\",\"tom-select\"\n\"60\",\"cgpdf\"\n\"60\",\"certenroll\"\n\"60\",\"layerdrawable\"\n\"60\",\"merit-gem\"\n\"60\",\"l-systems\"\n\"60\",\"webobjects\"\n\"60\",\"zohobooks\"\n\"60\",\"head.js\"\n\"60\",\"almalinux\"\n\"60\",\"sum-of-digits\"\n\"60\",\"sttwitter\"\n\"60\",\"sectionedrecyclerviewadapter\"\n\"60\",\"yowsup\"\n\"60\",\"lightroom\"\n\"60\",\"lightspeed\"\n\"60\",\"dart-2\"\n\"60\",\"web-container\"\n\"60\",\"web-ide\"\n\"60\",\"component-diagram\"\n\"60\",\"gml-geographic-markup-lan\"\n\"60\",\"shellcheck\"\n\"60\",\"flutter-html\"\n\"60\",\"fluxible\"\n\"60\",\"link-to-remote\"\n\"60\",\"helicontech\"\n\"60\",\"touchablehighlight\"\n\"60\",\"fnmatch\"\n\"60\",\"composite-id\"\n\"60\",\"partitioner\"\n\"60\",\"dajax\"\n\"60\",\"fme\"\n\"60\",\"webfont-loader\"\n\"60\",\"ifndef\"\n\"59\",\"mvs\"\n\"59\",\"easy-thumbnails\"\n\"59\",\"green-threads\"\n\"59\",\"greenrobot-eventbus-3.0\"\n\"59\",\"edn\"\n\"59\",\"trustpilot\"\n\"59\",\"yii-cmodel\"\n\"59\",\"jest-dom\"\n\"59\",\"editorformodel\"\n\"59\",\"greendao-generator\"\n\"59\",\"triplet\"\n\"59\",\"jetbrains-compose\"\n\"59\",\"dcevm\"\n\"59\",\"yocto-layer\"\n\"59\",\"slam-algorithm\"\n\"59\",\"webrtc-ios\"\n\"59\",\"effort\"\n\"59\",\"react-native-render-html\"\n\"59\",\"interactive-mode\"\n\"59\",\"live-video\"\n\"59\",\"wgl\"\n\"59\",\"procdump\"\n\"59\",\"clientaccesspolicy.xml\"\n\"59\",\"listcontrol\"\n\"59\",\"clio-api\"\n\"59\",\"anonymous-objects\"\n\"59\",\"playfab\"\n\"59\",\"ceedling\"\n\"59\",\"xhtmlrenderer\"\n\"59\",\"childviews\"\n\"59\",\"imx8\"\n\"59\",\"ccmenuitem\"\n\"59\",\"pi4j\"\n\"59\",\"apiclient\"\n\"59\",\"unbounded-wildcard\"\n\"59\",\"cblas\"\n\"59\",\"apollo-android\"\n\"59\",\"flair\"\n\"59\",\"managed-code\"\n\"59\",\"paddleocr\"\n\"59\",\"pixastic\"\n\"59\",\"xml-builder\"\n\"59\",\"vsam\"\n\"59\",\"datarowview\"\n\"59\",\"xpinc\"\n\"59\",\"fusionpbx\"\n\"59\",\"disposable\"\n\"59\",\"console2\"\n\"59\",\"major-mode\"\n\"59\",\"django-bootstrap3\"\n\"59\",\"django-caching\"\n\"59\",\"s7-1200\"\n\"59\",\"rune\"\n\"59\",\"filestreamresult\"\n\"59\",\"smartsheet-c#-sdk-v2\"\n\"59\",\"displaylist\"\n\"59\",\"alamofire-request\"\n\"59\",\"shark-sql\"\n\"59\",\"role-based\"\n\"59\",\"data-hiding\"\n\"59\",\"data-exchange\"\n\"59\",\"crouton\"\n\"59\",\"psycopg\"\n\"59\",\"icarus\"\n\"59\",\"rpt\"\n\"59\",\"verbatim-string\"\n\"59\",\"ruby-2.7\"\n\"59\",\"aws-cost-explorer\"\n\"59\",\"n2\"\n\"59\",\"aws-roles\"\n\"59\",\"rshiny\"\n\"59\",\"angularjs-provider\"\n\"59\",\"aws-marketplace\"\n\"59\",\"nalgebra\"\n\"59\",\"pybullet\"\n\"59\",\"django-mssql\"\n\"59\",\"angular-ui-modal\"\n\"59\",\"windows-firewall-api\"\n\"59\",\"single-logout\"\n\"59\",\"single-precision\"\n\"59\",\"demorgans-law\"\n\"59\",\"posix-api\"\n\"59\",\"jwt-go\"\n\"59\",\"swiftcharts\"\n\"59\",\"grub2\"\n\"59\",\"spring-cloud-dataflow-ui\"\n\"59\",\"www-mechanize-firefox\"\n\"59\",\"bloom\"\n\"59\",\"onnewintent\"\n\"59\",\"borrow\"\n\"59\",\"ereg\"\n\"59\",\"word-template\"\n\"59\",\"deployment-descriptor\"\n\"59\",\"writablebitmap\"\n\"59\",\"turbolinks-5\"\n\"59\",\"boot-clj\"\n\"59\",\"sbt-web\"\n\"59\",\"kairosdb\"\n\"59\",\"jquery-csv\"\n\"59\",\"go-git\"\n\"59\",\"rightnow-crm\"\n\"59\",\"kompose\"\n\"59\",\"luci\"\n\"59\",\"minima\"\n\"59\",\"typer\"\n\"59\",\"system-preferences\"\n\"59\",\"objectquery\"\n\"59\",\"lua-userdata\"\n\"59\",\"buildspec\"\n\"59\",\"cocosbuilder\"\n\"59\",\"astyle\"\n\"59\",\"androidappsonchromeos\"\n\"59\",\"java-mission-control\"\n\"59\",\"signed-url\"\n\"59\",\"rayshader\"\n\"59\",\"facebook4j\"\n\"59\",\"oval\"\n\"59\",\"o365-flow\"\n\"59\",\"analog-digital-converter\"\n\"59\",\"system-verilog-dpi\"\n\"59\",\"buffalo\"\n\"59\",\"knockout-kendo\"\n\"59\",\"magic-quotes\"\n\"59\",\"osgi-fragment\"\n\"59\",\"android-buildconfig\"\n\"59\",\"janino\"\n\"59\",\"nt-native-api\"\n\"59\",\"expression-web\"\n\"59\",\"podman-compose\"\n\"59\",\"dot42\"\n\"59\",\"pointer-to-array\"\n\"59\",\"nonblank\"\n\"59\",\"asp.net-mvc-sitemap\"\n\"59\",\"excelpackage\"\n\"59\",\"mktileoverlay\"\n\"59\",\"pluggable\"\n\"59\",\"no-response\"\n\"59\",\"qwebelement\"\n\"59\",\"exchange-server-2003\"\n\"59\",\"downloading-website-files\"\n\"59\",\"tandem\"\n\"59\",\"gcc8\"\n\"59\",\"poly\"\n\"59\",\"azure-storage-explorer\"\n\"59\",\"vertical-scroll\"\n\"59\",\"tarjans-algorithm\"\n\"59\",\"tarsosdsp\"\n\"59\",\"registerclientscriptblock\"\n\"59\",\"openssl-engine\"\n\"59\",\"qiime\"\n\"59\",\"geopackage\"\n\"59\",\"qiodevice\"\n\"59\",\"offsetdatetime\"\n\"59\",\"collectioneditor\"\n\"59\",\"commonsware\"\n\"59\",\"colordialog\"\n\"59\",\"hypercorn\"\n\"59\",\"acts-as-audited\"\n\"59\",\"http-status-code-307\"\n\"59\",\"human-interface\"\n\"59\",\"custom-notification\"\n\"59\",\"custom-build\"\n\"59\",\"petitparser\"\n\"59\",\"elasticsearch-marvel\"\n\"59\",\"com.sun.net.httpserver\"\n\"59\",\"qpdf\"\n\"59\",\"metalsmith\"\n\"59\",\"mpj-express\"\n\"59\",\"lpcstr\"\n\"59\",\"ipmi\"\n\"59\",\"textstyle\"\n\"59\",\"angular-errorhandler\"\n\"59\",\"specifier\"\n\"59\",\"angular-amd\"\n\"59\",\"splitstackshape\"\n\"59\",\"split-button\"\n\"59\",\"iphone-sdk-3.1\"\n\"59\",\"parent-node\"\n\"59\",\"enclave\"\n\"59\",\"flutter-redux\"\n\"59\",\"subscript-operator\"\n\"59\",\"imagedatagenerator\"\n\"59\",\"tfs-2012\"\n\"59\",\"qtxml\"\n\"59\",\"amazon-machine-learning\"\n\"59\",\"mdbtools\"\n\"59\",\"stddraw\"\n\"59\",\"maven-wagon-plugin\"\n\"59\",\"pandera\"\n\"59\",\"stdhash\"\n\"59\",\"starvation\"\n\"59\",\"google-reseller-api\"\n\"59\",\"usb-camera\"\n\"59\",\"predefined-variables\"\n\"59\",\"linuxbrew\"\n\"59\",\"passive-mode\"\n\"59\",\"google-sheets-custom-function\"\n\"59\",\"webintents\"\n\"58\",\"class-fields\"\n\"58\",\"massmail\"\n\"58\",\"yield-from\"\n\"58\",\"cloaking\"\n\"58\",\"slimbox\"\n\"58\",\"clang-complete\"\n\"58\",\"eco\"\n\"58\",\"base-address\"\n\"58\",\"multiple-constructors\"\n\"58\",\"relational-model\"\n\"58\",\"skipper\"\n\"58\",\"jcop\"\n\"58\",\"trygetvalue\"\n\"58\",\"git-clean\"\n\"58\",\"trumbowyg\"\n\"58\",\"marklogic-corb\"\n\"58\",\"eclipse-mat\"\n\"58\",\"web-vitals\"\n\"58\",\"eer-model\"\n\"58\",\"local-system-account\"\n\"58\",\"flexform\"\n\"58\",\"fenics\"\n\"58\",\"bam\"\n\"58\",\"weibo\"\n\"58\",\"remobjects\"\n\"58\",\"mathtype\"\n\"58\",\"mat-input\"\n\"58\",\"snaplogic\"\n\"58\",\"flax\"\n\"58\",\"jsdata\"\n\"58\",\"pako\"\n\"58\",\"blackberry-qnx\"\n\"58\",\"python-huey\"\n\"58\",\"page-init\"\n\"58\",\"cellpadding\"\n\"58\",\"fuse.js\"\n\"58\",\"binomial-cdf\"\n\"58\",\"jsonstream\"\n\"58\",\"pipelined-function\"\n\"58\",\"makestyles\"\n\"58\",\"data-layer\"\n\"58\",\"datawedge\"\n\"58\",\"conjunctive-normal-form\"\n\"58\",\"finalization\"\n\"58\",\"select-n-plus-1\"\n\"58\",\"consul-kv\"\n\"58\",\"swiftui-state\"\n\"58\",\"categorical\"\n\"58\",\"laplacian\"\n\"58\",\"apereo\"\n\"58\",\"imperative\"\n\"58\",\"awss3transferutility\"\n\"58\",\"cross-origin-embedder-policy\"\n\"58\",\"rsa-sha256\"\n\"58\",\"shared-worker\"\n\"58\",\"kinect.toolbox\"\n\"58\",\"avrcp\"\n\"58\",\"window-chrome\"\n\"58\",\"shapesheet\"\n\"58\",\"rotten-tomatoes\"\n\"58\",\"django-select2\"\n\"58\",\"oracle.manageddataaccess\"\n\"58\",\"ora-00932\"\n\"58\",\"dask-kubernetes\"\n\"58\",\"django-custom-tags\"\n\"58\",\"datagridcolumnheader\"\n\"58\",\"uri-scheme\"\n\"58\",\"pyav\"\n\"58\",\"agile-processes\"\n\"58\",\"data-dump\"\n\"58\",\"p-table\"\n\"58\",\"csplit\"\n\"58\",\"angular-toastr\"\n\"58\",\"databricks-repos\"\n\"58\",\"spring-actuator\"\n\"58\",\"sas-jmp\"\n\"58\",\"aws-sdk-java\"\n\"58\",\"twitter-finagle\"\n\"58\",\"onflow-cadence\"\n\"58\",\"nsq\"\n\"58\",\"nemerle\"\n\"58\",\"nsprintoperation\"\n\"58\",\"opacitymask\"\n\"58\",\"deis\"\n\"58\",\"ionic-appflow\"\n\"58\",\"wpf-grid\"\n\"58\",\"spring-cloud-stream-binder\"\n\"58\",\"grunt-contrib-less\"\n\"58\",\"oocss\"\n\"58\",\"entity-framework-core-3.0\"\n\"58\",\"hsb\"\n\"58\",\"gtkentry\"\n\"58\",\"app-shell\"\n\"58\",\"htmlgenericcontrol\"\n\"58\",\"pdfdocument\"\n\"58\",\"spring-loaded\"\n\"58\",\"neato\"\n\"58\",\"bleu\"\n\"58\",\"google-admin-settings-api\"\n\"58\",\"amplitude-analytics\"\n\"58\",\"orchardcore\"\n\"58\",\"java-money\"\n\"58\",\"ocaml-core\"\n\"58\",\"minecraft-commands\"\n\"58\",\"sgd\"\n\"58\",\"amp-story\"\n\"58\",\"lektor\"\n\"58\",\"netconf\"\n\"58\",\"audience\"\n\"58\",\"netbeans-7.2\"\n\"58\",\"amazon-workmail\"\n\"58\",\"atdd\"\n\"58\",\"51degrees\"\n\"58\",\"formatted-input\"\n\"58\",\"overlapping-matches\"\n\"58\",\"javax.script\"\n\"58\",\".nettiers\"\n\"58\",\"faas\"\n\"58\",\"shopify-liquid\"\n\"58\",\"newrow\"\n\"58\",\"typehead\"\n\"58\",\"kotlin-android\"\n\"58\",\"b-tree-index\"\n\"58\",\"bssid\"\n\"58\",\"openjdk-17\"\n\"58\",\"dspack\"\n\"58\",\"control-panel\"\n\"58\",\"ntext\"\n\"58\",\"xcode7-beta4\"\n\"58\",\"nsusernotificationcenter\"\n\"58\",\"quora\"\n\"58\",\"taskdialog\"\n\"58\",\"timepickerdialog\"\n\"58\",\"sql-import-wizard\"\n\"58\",\"gap-system\"\n\"58\",\"gdc\"\n\"58\",\"plcrashreporter\"\n\"58\",\"dotnet-isolated\"\n\"58\",\"xajax\"\n\"58\",\"regional\"\n\"58\",\"azure-role-environment\"\n\"58\",\"redeploy\"\n\"58\",\"quick-nimble\"\n\"58\",\"drupal-content-types\"\n\"58\",\"xc16\"\n\"58\",\"aspnet-api-versioning\"\n\"58\",\"npp\"\n\"58\",\"draw2d-js\"\n\"58\",\"policy-based-design\"\n\"58\",\"businesscentral\"\n\"58\",\"pollingduplexhttpbinding\"\n\"58\",\"textfieldparser\"\n\"58\",\"strsep\"\n\"58\",\"meta-analysis\"\n\"58\",\"prompt-toolkit\"\n\"58\",\"google-cloud-translate\"\n\"58\",\"angular-input\"\n\"58\",\"sphinx-napoleon\"\n\"58\",\"cudd\"\n\"58\",\"pyx\"\n\"58\",\"espn\"\n\"58\",\"actionmode\"\n\"58\",\"pywebview\"\n\"58\",\"cen-xfs\"\n\"58\",\"android-jodatime\"\n\"58\",\"iphone-sdk-4.1\"\n\"58\",\"android-overlay\"\n\"58\",\"angular-httpclient-interceptors\"\n\"58\",\"omniauth-google-oauth2\"\n\"58\",\"montage\"\n\"58\",\"moveit\"\n\"58\",\"google-cloud-compute-engine\"\n\"58\",\"iphonecoredatarecipes\"\n\"58\",\"elasticsearch-nested\"\n\"58\",\"hunit\"\n\"58\",\"prototype-pattern\"\n\"58\",\"luac\"\n\"58\",\"concurrent-mark-sweep\"\n\"58\",\"powerbuilder-build-deploy\"\n\"58\",\"tigris\"\n\"58\",\"git-webhooks\"\n\"58\",\"uuencode\"\n\"58\",\"hfp\"\n\"58\",\"array-reduce\"\n\"58\",\"ms-office-script\"\n\"58\",\"bcftools\"\n\"58\",\"haskell-warp\"\n\"58\",\"qt5.8\"\n\"58\",\"asar\"\n\"58\",\"subreports\"\n\"58\",\"d3dx\"\n\"58\",\"stdinitializerlist\"\n\"58\",\"multidimensional-cube\"\n\"58\",\"git-plumbing\"\n\"58\",\"linspace\"\n\"58\",\"array-sum\"\n\"58\",\"mediatypeformatter\"\n\"58\",\"zf3\"\n\"58\",\"haskell-persistent\"\n\"58\",\"hdbc\"\n\"58\",\"theia\"\n\"58\",\"measures\"\n\"58\",\"argouml\"\n\"58\",\"cvat\"\n\"58\",\"qtoolbutton\"\n\"58\",\"gmaven-plugin\"\n\"58\",\"preempt-rt\"\n\"58\",\"partition-problem\"\n\"58\",\"embedly\"\n\"58\",\"ase\"\n\"58\",\"static-binding\"\n\"58\",\"cwd\"\n\"58\",\"flutter-stripe\"\n\"58\",\"cypress-conditional-testing\"\n\"58\",\"google-my-business\"\n\"58\",\"qtruby\"\n\"58\",\"ms-app-analytics\"\n\"57\",\"sizewithfont\"\n\"57\",\"jboss-modules\"\n\"57\",\"multiple-arguments\"\n\"57\",\"aol\"\n\"57\",\"truclient\"\n\"57\",\"math.js\"\n\"57\",\"localized\"\n\"57\",\"deep-residual-networks\"\n\"57\",\"instabug\"\n\"57\",\"wide-format-data\"\n\"57\",\"ghc-mod\"\n\"57\",\"marshmallow-sqlalchemy\"\n\"57\",\"localconnection\"\n\"57\",\"maskededitextender\"\n\"57\",\"interactive-grid\"\n\"57\",\"clearcanvas\"\n\"57\",\"yahoo-messenger\"\n\"57\",\"remote-registry\"\n\"57\",\"eddystone-url\"\n\"57\",\"phaselistener\"\n\"57\",\"remote-backup\"\n\"57\",\"clr4.0\"\n\"57\",\"client-side-attacks\"\n\"57\",\"llamacpp\"\n\"57\",\"ss7\"\n\"57\",\"graphenedb\"\n\"57\",\"edgejs\"\n\"57\",\"trialware\"\n\"57\",\"sitefinity-10\"\n\"57\",\"json2csv\"\n\"57\",\"p4api.net\"\n\"57\",\"fusebox\"\n\"57\",\"kucoin\"\n\"57\",\"imagespan\"\n\"57\",\"birthday-paradox\"\n\"57\",\"dbforge\"\n\"57\",\"firewire\"\n\"57\",\"disjoint-union\"\n\"57\",\"configureawait\"\n\"57\",\"pipx\"\n\"57\",\"fsunit\"\n\"57\",\"pipes-filters\"\n\"57\",\"ngx-mask\"\n\"57\",\"api-hook\"\n\"57\",\"rviz\"\n\"57\",\"ng2-pdfjs-viewer\"\n\"57\",\"cda\"\n\"57\",\"directxmath\"\n\"57\",\"sailpoint\"\n\"57\",\"swiftui-sheet\"\n\"57\",\"nexus-one\"\n\"57\",\"symfony-panther\"\n\"57\",\"dirpagination\"\n\"57\",\"jqmath\"\n\"57\",\"camera-flash\"\n\"57\",\"vungle-ads\"\n\"57\",\"facebook-pop\"\n\"57\",\"indy-9\"\n\"57\",\"facebook-rest-api\"\n\"57\",\"pycall\"\n\"57\",\"docker-pull\"\n\"57\",\"mysql-cli\"\n\"57\",\"rosalind\"\n\"57\",\"windows-hosting\"\n\"57\",\"angularjs-ngmock\"\n\"57\",\"name-hiding\"\n\"57\",\"unnamed-namespace\"\n\"57\",\"database-engine\"\n\"57\",\"pyathena\"\n\"57\",\"kentico-kontent\"\n\"57\",\"ajax-update\"\n\"57\",\"unselect\"\n\"57\",\"documents4j\"\n\"57\",\"psftp\"\n\"57\",\"verisign\"\n\"57\",\"jmapviewer\"\n\"57\",\"r-parsnip\"\n\"57\",\"v-autocomplete\"\n\"57\",\"rq\"\n\"57\",\"methodnotfound\"\n\"57\",\"pcspim\"\n\"57\",\"oomph\"\n\"57\",\"salesforce-flow\"\n\"57\",\"bloburls\"\n\"57\",\"nsindexset\"\n\"57\",\"appodeal\"\n\"57\",\"natural-key\"\n\"57\",\"post-processor\"\n\"57\",\"kdeplot\"\n\"57\",\"azure-availability-set\"\n\"57\",\"twint\"\n\"57\",\"pdf-manipulation\"\n\"57\",\"ionic-tabs\"\n\"57\",\"jquery-globalize\"\n\"57\",\"spring-jersey\"\n\"57\",\"jquery-ui-spinner\"\n\"57\",\"spring-io\"\n\"57\",\"superobject\"\n\"57\",\"negotiate\"\n\"57\",\"word-processor\"\n\"57\",\"cors-anywhere\"\n\"57\",\"interop-domino\"\n\"57\",\"easypost\"\n\"57\",\"erlang-ports\"\n\"57\",\"azure-ad-powershell-v2\"\n\"57\",\"doskey\"\n\"57\",\"rights-management\"\n\"57\",\"setparent\"\n\"57\",\"acaccountstore\"\n\"57\",\"minitab\"\n\"57\",\"system.data.datatable\"\n\"57\",\"ray-picking\"\n\"57\",\"cocos2d-x-3.x\"\n\"57\",\"ri\"\n\"57\",\"showwindow\"\n\"57\",\"bugsense\"\n\"57\",\"libcloud\"\n\"57\",\"oct2py\"\n\"57\",\"libpd\"\n\"57\",\"mindmapping\"\n\"57\",\"ata\"\n\"57\",\"typescript4.0\"\n\"57\",\"itemgroup\"\n\"57\",\"xcode5.0.1\"\n\"57\",\"region-monitoring\"\n\"57\",\"business-process\"\n\"57\",\"drive-letter\"\n\"57\",\"nswagstudio\"\n\"57\",\"noip\"\n\"57\",\"high-level\"\n\"57\",\"cachedrowset\"\n\"57\",\"timeval\"\n\"57\",\"hana-studio\"\n\"57\",\"plone-3.x\"\n\"57\",\"nokiax\"\n\"57\",\"mocha-phantomjs\"\n\"57\",\"nsxpcconnection\"\n\"57\",\"scene-manager\"\n\"57\",\"tcp-keepalive\"\n\"57\",\"timestampdiff\"\n\"57\",\"h2o.ai\"\n\"57\",\"racing\"\n\"57\",\"ml5.js\"\n\"57\",\"red-gate-ants\"\n\"57\",\"xcode12.5\"\n\"57\",\"azure-ml-pipelines\"\n\"57\",\"uiimageorientation\"\n\"57\",\"hgweb\"\n\"57\",\"member-hiding\"\n\"57\",\"nhibernate-validator\"\n\"57\",\"logical-purity\"\n\"57\",\"cusolver\"\n\"57\",\"office-pia\"\n\"57\",\"string-operations\"\n\"57\",\"mercurial-convert\"\n\"57\",\"customcolumn\"\n\"57\",\"angular2-hostbinding\"\n\"57\",\"mosync\"\n\"57\",\"evosuite\"\n\"57\",\"spectre\"\n\"57\",\"activitylog\"\n\"57\",\"mongo-scala-driver\"\n\"57\",\"peer-connection\"\n\"57\",\"ogc\"\n\"57\",\"mousepress\"\n\"57\",\"huawei-map-kit\"\n\"57\",\"motion-blur\"\n\"57\",\"concave-hull\"\n\"57\",\"maven-gae-plugin\"\n\"57\",\"gm-xmlhttprequest\"\n\"57\",\"foaf\"\n\"57\",\"power-virtual-agents\"\n\"57\",\"computational-finance\"\n\"57\",\"avaudiopcmbuffer\"\n\"57\",\"prefix-tree\"\n\"57\",\"cyber-ark\"\n\"57\",\"ardent\"\n\"57\",\"partial-matches\"\n\"57\",\"git-rev-list\"\n\"57\",\"sourcegear-vault\"\n\"57\",\"state-saving\"\n\"57\",\"web.sitemap\"\n\"57\",\"traffic-measurement\"\n\"57\",\"weblogic8.x\"\n\"57\",\"line-drawing\"\n\"57\",\"autopilot\"\n\"57\",\"haskell-diagrams\"\n\"57\",\"max-size\"\n\"57\",\"image-extraction\"\n\"57\",\"substance\"\n\"57\",\"mbcs\"\n\"57\",\"bdc\"\n\"57\",\"static-import\"\n\"57\",\"static-data\"\n\"57\",\"qtest\"\n\"57\",\"zend-guard\"\n\"57\",\"pg-hba.conf\"\n\"56\",\"backlight\"\n\"56\",\"declare-styleable\"\n\"56\",\"livevalidation\"\n\"56\",\"processwire\"\n\"56\",\"webservicetemplate\"\n\"56\",\"ansistring\"\n\"56\",\"mass-emails\"\n\"56\",\"decodeuricomponent\"\n\"56\",\"materialdatepicker\"\n\"56\",\"ecl\"\n\"56\",\"dedicated\"\n\"56\",\"tern\"\n\"56\",\"phlivephoto\"\n\"56\",\"bandpass-filter\"\n\"56\",\"maven-ant-tasks\"\n\"56\",\"dbutils\"\n\"56\",\"treelistview\"\n\"56\",\"phantom-reference\"\n\"56\",\"php-builtin-server\"\n\"56\",\"yelp-fusion-api\"\n\"56\",\"yii-modules\"\n\"56\",\"matplotlib-gridspec\"\n\"56\",\"vue-testing-library\"\n\"56\",\"default-copy-constructor\"\n\"56\",\"dblinq\"\n\"56\",\"installshield-2009\"\n\"56\",\"sse4\"\n\"56\",\"adobe-scriptui\"\n\"56\",\"xmlschema\"\n\"56\",\"rwlock\"\n\"56\",\"blackberry-world\"\n\"56\",\"container-image\"\n\"56\",\"mappingexception\"\n\"56\",\"addressables\"\n\"56\",\"s3-bucket\"\n\"56\",\"software-serial\"\n\"56\",\"pixel-manipulation\"\n\"56\",\"catamorphism\"\n\"56\",\"language-construct\"\n\"56\",\"phrets\"\n\"56\",\"appian\"\n\"56\",\"apache-spark-encoders\"\n\"56\",\"labjs\"\n\"56\",\"churn\"\n\"56\",\"python-magic\"\n\"56\",\"soaphandler\"\n\"56\",\"python-memcached\"\n\"56\",\"ccombobox\"\n\"56\",\"divi-theme\"\n\"56\",\"flash-video\"\n\"56\",\"self-destruction\"\n\"56\",\"snoop\"\n\"56\",\"flasgger\"\n\"56\",\"in-class-initialization\"\n\"56\",\"platform-specific\"\n\"56\",\"json-arrayagg\"\n\"56\",\"undef\"\n\"56\",\"riverpod-generator\"\n\"56\",\"fswatch\"\n\"56\",\"datastax-python-driver\"\n\"56\",\"binary-matrix\"\n\"56\",\"django-treebeard\"\n\"56\",\"windowed\"\n\"56\",\"r-marginaleffects\"\n\"56\",\"wack\"\n\"56\",\"react-ssr\"\n\"56\",\"ora-00936\"\n\"56\",\"wildcard-mapping\"\n\"56\",\"camera-api\"\n\"56\",\"watson-nlu\"\n\"56\",\"fan-page\"\n\"56\",\"watchos-5\"\n\"56\",\"walkthrough\"\n\"56\",\"angularjs-ng-resource\"\n\"56\",\"cross-origin-opener-policy\"\n\"56\",\"oracle-fdw\"\n\"56\",\"kendo-panelbar\"\n\"56\",\"realm-js\"\n\"56\",\"data-cube\"\n\"56\",\"roulette-wheel-selection\"\n\"56\",\"angular-ui-bootstrap-tab\"\n\"56\",\"rsuite\"\n\"56\",\"govendor\"\n\"56\",\"publish-actions\"\n\"56\",\"rootkit\"\n\"56\",\"metatag\"\n\"56\",\"gulp-livereload\"\n\"56\",\"wsdl.exe\"\n\"56\",\"ws-discovery\"\n\"56\",\"modulus.io\"\n\"56\",\"pdf-rendering\"\n\"56\",\"tus\"\n\"56\",\"txtextcontrol\"\n\"56\",\"neko\"\n\"56\",\"ncalc\"\n\"56\",\"boost-polygon\"\n\"56\",\"nsbitmapimagerep\"\n\"56\",\"junos-automation\"\n\"56\",\"boids\"\n\"56\",\"nsmanagedobjectmodel\"\n\"56\",\"mongokit\"\n\"56\",\"bluepill\"\n\"56\",\"html-post\"\n\"56\",\"payment-request-api\"\n\"56\",\"input-filtering\"\n\"56\",\"sap-selection-screens\"\n\"56\",\"ontime\"\n\"56\",\"covering-index\"\n\"56\",\"bluetooth-device-discovery\"\n\"56\",\"io.js\"\n\"56\",\"spring-reactor\"\n\"56\",\"magnify\"\n\"56\",\"external-application\"\n\"56\",\"shopware5\"\n\"56\",\"osm.pbf\"\n\"56\",\"maildir\"\n\"56\",\"pyorient\"\n\"56\",\"osql\"\n\"56\",\"3g-network\"\n\"56\",\"rcppparallel\"\n\"56\",\"extreact\"\n\"56\",\"object-initialization\"\n\"56\",\"amazon-simple-email-service\"\n\"56\",\"facebook-graph-api-v2.4\"\n\"56\",\"forward-compatibility\"\n\"56\",\"ravendb4\"\n\"56\",\"rainbowtable\"\n\"56\",\"fortigate\"\n\"56\",\"sieve-language\"\n\"56\",\"fortify-source\"\n\"56\",\"pyfmi\"\n\"56\",\"hibernate-jpa\"\n\"56\",\"drupal-services\"\n\"56\",\"screen-off\"\n\"56\",\"handlebars.net\"\n\"56\",\"tinymce-3\"\n\"56\",\"itcl\"\n\"56\",\"handleerror\"\n\"56\",\"dialplan\"\n\"56\",\"ispf\"\n\"56\",\"uimenuitem\"\n\"56\",\"uinput\"\n\"56\",\"android-input-filter\"\n\"56\",\"openslide\"\n\"56\",\"gwt-openlayers\"\n\"56\",\"rails-generate\"\n\"56\",\"scala-spark\"\n\"56\",\"sqlreportingservice\"\n\"56\",\"nsvisualeffectview\"\n\"56\",\"device-width\"\n\"56\",\"jackson-dataformat-csv\"\n\"56\",\"vesa\"\n\"56\",\"vfw\"\n\"56\",\"dotnetcorecli\"\n\"56\",\"tdataset\"\n\"56\",\"c++builder-10.3-rio\"\n\"56\",\"refcounting\"\n\"56\",\"bunifu\"\n\"56\",\"historical-db\"\n\"56\",\"c2664\"\n\"56\",\"non-repetitive\"\n\"56\",\"mpmoviewcontroller\"\n\"56\",\"activepython\"\n\"56\",\"pyxb\"\n\"56\",\"geogebra\"\n\"56\",\"es5-shim\"\n\"56\",\"logical-foundations\"\n\"56\",\"spot-instances\"\n\"56\",\"esent\"\n\"56\",\"iron\"\n\"56\",\"stockfish\"\n\"56\",\"ctree\"\n\"56\",\"cer\"\n\"56\",\"login-attempts\"\n\"56\",\"moo\"\n\"56\",\"learndash\"\n\"56\",\"iris-recognition\"\n\"56\",\"mergemap\"\n\"56\",\"nintendo\"\n\"56\",\"zpt\"\n\"56\",\"google-form-quiz\"\n\"56\",\"csvkit\"\n\"56\",\"cstdio\"\n\"56\",\"irs\"\n\"56\",\"acts-as-commentable\"\n\"56\",\"ackermann\"\n\"56\",\"css-parsing\"\n\"56\",\"geom-raster\"\n\"56\",\"angular-auth-oidc-client\"\n\"56\",\"log4r\"\n\"56\",\"zurb-reveal\"\n\"56\",\"elasticsearch-percolate\"\n\"56\",\"mule-flow\"\n\"56\",\"embedded-javascript\"\n\"56\",\"flutter-upgrade\"\n\"56\",\"msbuild-propertygroup\"\n\"56\",\"static-array\"\n\"56\",\"presto-jdbc\"\n\"56\",\"st-link\"\n\"56\",\"zebra-scanners\"\n\"56\",\"zend-controller-router\"\n\"56\",\"concrete5-8.x\"\n\"56\",\"altorouter\"\n\"56\",\"concat-ws\"\n\"56\",\"zend-framework-routing\"\n\"56\",\"mediastreamsource\"\n\"56\",\"stsadm\"\n\"56\",\"scriptcs\"\n\"56\",\"urlretrieve\"\n\"56\",\"authorize.net-arb\"\n\"56\",\"queueing\"\n\"56\",\"structuremap4\"\n\"56\",\"behaviorspace\"\n\"56\",\"qt-linguist\"\n\"56\",\"thingsboard-gateway\"\n\"56\",\"arrayaccess\"\n\"56\",\"parallel-builds\"\n\"56\",\"dart-shelf\"\n\"56\",\"styled-jsx\"\n\"56\",\"cyclic-graph\"\n\"56\",\"global-namespace\"\n\"56\",\"user-identification\"\n\"56\",\"sharpmap\"\n\"56\",\"global-assembly-cache\"\n\"55\",\"vue-dynamic-components\"\n\"55\",\"math-functions\"\n\"55\",\"anytime\"\n\"55\",\"clr-profiling-api\"\n\"55\",\"flutter-engine\"\n\"55\",\"feathers-hook\"\n\"55\",\"bamboo-specs\"\n\"55\",\"gidsignin\"\n\"55\",\"grdb\"\n\"55\",\"multiple-results\"\n\"55\",\"remote-repository\"\n\"55\",\"sql-server-openxml\"\n\"55\",\"template-variables\"\n\"55\",\"inspectdb\"\n\"55\",\"flipboard\"\n\"55\",\"livelink\"\n\"55\",\"gitlab-ee\"\n\"55\",\"apache-camel-3\"\n\"55\",\"ckeditor.net\"\n\"55\",\"git-init\"\n\"55\",\"git-ftp\"\n\"55\",\"clojurescript-javascript-interop\"\n\"55\",\"material-dialog\"\n\"55\",\"ant-design-vue\"\n\"55\",\"clearml\"\n\"55\",\"tspan\"\n\"55\",\"fileopenpicker\"\n\"55\",\"data-segment\"\n\"55\",\"soa-suite\"\n\"55\",\"imgkit\"\n\"55\",\"incredibuild\"\n\"55\",\"nextval\"\n\"55\",\"apache-sentry\"\n\"55\",\"socketasynceventargs\"\n\"55\",\"adonetappender\"\n\"55\",\"fleet\"\n\"55\",\"mappings\"\n\"55\",\"filter-input\"\n\"55\",\"xml-entities\"\n\"55\",\"safearea\"\n\"55\",\"flask-ask\"\n\"55\",\"dispatchgroup\"\n\"55\",\"appery.io\"\n\"55\",\"packet-injection\"\n\"55\",\"fromjson\"\n\"55\",\"discogs-api\"\n\"55\",\"xpathquery\"\n\"55\",\"smartystreets\"\n\"55\",\"unity3d-terrain\"\n\"55\",\"angular-social-login\"\n\"55\",\"method-names\"\n\"55\",\"oracle-apex-20.2\"\n\"55\",\"grandstack\"\n\"55\",\"sim-toolkit\"\n\"55\",\"fasthttp\"\n\"55\",\"upshot\"\n\"55\",\"avcodec\"\n\"55\",\"rosetta-2\"\n\"55\",\"unix-head\"\n\"55\",\"pure-js\"\n\"55\",\"rubymine-7\"\n\"55\",\"database-deployment\"\n\"55\",\"aide\"\n\"55\",\"angularjs-ng-href\"\n\"55\",\"windows-screensaver\"\n\"55\",\"databound-controls\"\n\"55\",\"microsoft-extensions-logging\"\n\"55\",\"gorm-mongodb\"\n\"55\",\"mysql-spatial\"\n\"55\",\"jniwrapper\"\n\"55\",\"unslider\"\n\"55\",\"informatica-data-integration-hub\"\n\"55\",\"jquery-ui-theme\"\n\"55\",\"apple-numbers\"\n\"55\",\"pasteboard\"\n\"55\",\"gulp-babel\"\n\"55\",\"word-sense-disambiguation\"\n\"55\",\"springjunit4classrunner\"\n\"55\",\"tx-gridelements\"\n\"55\",\"postbackurl\"\n\"55\",\"neo4django\"\n\"55\",\"popup-balloons\"\n\"55\",\"grunt-contrib-compass\"\n\"55\",\"sata\"\n\"55\",\"svn-update\"\n\"55\",\"silentpush\"\n\"55\",\"typo3-tca\"\n\"55\",\"system.drawing.color\"\n\"55\",\"luks\"\n\"55\",\"virtual-path\"\n\"55\",\"audiostreamer\"\n\"55\",\"amstock\"\n\"55\",\"ubuntu-13.04\"\n\"55\",\"libtorrent-rasterbar\"\n\"55\",\"m2doc\"\n\"55\",\"google-api-go-client\"\n\"55\",\"ril\"\n\"55\",\"outbox-pattern\"\n\"55\",\"retrofit2.6\"\n\"55\",\"atomicreference\"\n\"55\",\"asynchronous-messaging-protocol\"\n\"55\",\"pykafka\"\n\"55\",\"luajava\"\n\"55\",\"rcu\"\n\"55\",\"context-bound\"\n\"55\",\"tabpy\"\n\"55\",\"digital-filter\"\n\"55\",\"context-sensitive-grammar\"\n\"55\",\"opensc\"\n\"55\",\"gcal\"\n\"55\",\"npm-vulnerabilities\"\n\"55\",\"playlists\"\n\"55\",\"android-cookiemanager\"\n\"55\",\"open-telemetry-java\"\n\"55\",\"dstore\"\n\"55\",\"guvnor\"\n\"55\",\"tawk.to\"\n\"55\",\"non-volatile\"\n\"55\",\"high-traffic\"\n\"55\",\"qvboxlayout\"\n\"55\",\"gecode\"\n\"55\",\"itmstransporter\"\n\"55\",\"nsurlsessiontask\"\n\"55\",\"copy-initialization\"\n\"55\",\"timeunit\"\n\"55\",\"genbank\"\n\"55\",\"hibernate-reactive\"\n\"55\",\"convenience-methods\"\n\"55\",\"leader-election\"\n\"55\",\"iproute\"\n\"55\",\"hubspot-api\"\n\"55\",\"react-motion\"\n\"55\",\"elasticsearch-api\"\n\"55\",\"elastic-cache\"\n\"55\",\"ipyleaflet\"\n\"55\",\"pywin\"\n\"55\",\"nimbus-jose-jwt\"\n\"55\",\"geom-segment\"\n\"55\",\"node-imagemagick\"\n\"55\",\"reactfire\"\n\"55\",\"human-computer-interface\"\n\"55\",\"niftynet\"\n\"55\",\"egui\"\n\"55\",\"command-timeout\"\n\"55\",\"zsh-zle\"\n\"55\",\"everyplay\"\n\"55\",\"pytransitions\"\n\"55\",\"angular2-databinding\"\n\"55\",\"android-make\"\n\"55\",\"genesys\"\n\"55\",\"http-head\"\n\"55\",\"suexec\"\n\"55\",\"flysystem-google-drive\"\n\"55\",\"glympse\"\n\"55\",\"msxml2\"\n\"55\",\"gluon-desktop\"\n\"55\",\"pass-by-const-reference\"\n\"55\",\"medium.com-publishing-api\"\n\"55\",\"particle-filter\"\n\"55\",\"transactional-memory\"\n\"55\",\"mda\"\n\"55\",\"cycle2\"\n\"55\",\"globalcompositeoperation\"\n\"55\",\"styleframe\"\n\"55\",\"qt3\"\n\"55\",\"threadstatic\"\n\"55\",\"cylindrical\"\n\"55\",\"hermit\"\n\"55\",\"powershell-v6.0\"\n\"55\",\"image-enlarge\"\n\"55\",\"powerpoint-2016\"\n\"55\",\"elpa\"\n\"55\",\"threepenny-gui\"\n\"55\",\"bastion-host\"\n\"55\",\"steeltoe\"\n\"55\",\"pants\"\n\"55\",\"structured-logging\"\n\"55\",\"qtdbus\"\n\"54\",\"vsta\"\n\"54\",\"vsql\"\n\"54\",\"vue-options-api\"\n\"54\",\"cloud-object-storage\"\n\"54\",\"staggeredgridlayoutmanager\"\n\"54\",\"dbmail\"\n\"54\",\"clustering-key\"\n\"54\",\"php-shorttags\"\n\"54\",\"yii2-extension\"\n\"54\",\"localytics\"\n\"54\",\"yahoo-boss-api\"\n\"54\",\"deadlines\"\n\"54\",\"llvm-3.0\"\n\"54\",\"list.js\"\n\"54\",\"sqltools\"\n\"54\",\"flexible-search\"\n\"54\",\"web-statistics\"\n\"54\",\"flipkart-api\"\n\"54\",\"cloudcaptain\"\n\"54\",\"clr-hosting\"\n\"54\",\"cisco-axl\"\n\"54\",\"backups\"\n\"54\",\"antisamy\"\n\"54\",\"fieldinfo\"\n\"54\",\"trusted-computing\"\n\"54\",\"treecellrenderer\"\n\"54\",\"edt\"\n\"54\",\"basename\"\n\"54\",\"multiple-browsers\"\n\"54\",\"telegraf-plugins\"\n\"54\",\"ffill\"\n\"54\",\"req\"\n\"54\",\"intel-parallel-studio\"\n\"54\",\"translation-unit\"\n\"54\",\"feincms\"\n\"54\",\"run-app\"\n\"54\",\"snapshot-isolation\"\n\"54\",\"runsettings\"\n\"54\",\"cassette\"\n\"54\",\"marc\"\n\"54\",\"mapstatetoprops\"\n\"54\",\"jsonlint\"\n\"54\",\"xdebug-profiler\"\n\"54\",\"saf\"\n\"54\",\"snakecasing\"\n\"54\",\"packager\"\n\"54\",\"soap4r\"\n\"54\",\"cellspacing\"\n\"54\",\"cilium\"\n\"54\",\"xfdf\"\n\"54\",\"uname\"\n\"54\",\"unitils\"\n\"54\",\"ng-apexcharts\"\n\"54\",\"config-transformation\"\n\"54\",\"python-coverage\"\n\"54\",\"python-jedi\"\n\"54\",\"datecreated\"\n\"54\",\"constantcontact\"\n\"54\",\"freshjs\"\n\"54\",\"python-dotenv\"\n\"54\",\"distributed-filesystem\"\n\"54\",\"simple-openni\"\n\"54\",\"ibm-cloud-plugin\"\n\"54\",\"jqgrid-inlinenav\"\n\"54\",\"jfrog-pipelines\"\n\"54\",\"joomla4\"\n\"54\",\"aws-glue-connection\"\n\"54\",\"jide\"\n\"54\",\"aiff\"\n\"54\",\"ora2pg\"\n\"54\",\"redactor.js\"\n\"54\",\"servicestack-razor\"\n\"54\",\"ibatis.net\"\n\"54\",\"verilator\"\n\"54\",\"rke\"\n\"54\",\"agal\"\n\"54\",\"servicebehavior\"\n\"54\",\"django-sitemaps\"\n\"54\",\"django-widget-tweaks\"\n\"54\",\"angular-route-segment\"\n\"54\",\"avif\"\n\"54\",\"windowless\"\n\"54\",\"keycloak-nodejs-connect\"\n\"54\",\"unoconv\"\n\"54\",\"wikimedia-commons\"\n\"54\",\"croogo\"\n\"54\",\"dnx50\"\n\"54\",\"mysite\"\n\"54\",\"nameko\"\n\"54\",\"aws-rds-data-service\"\n\"54\",\"nacl-cryptography\"\n\"54\",\"mysql-5.0\"\n\"54\",\"validationrule\"\n\"54\",\"vcloud-director-rest-api\"\n\"54\",\"payout\"\n\"54\",\"twistd\"\n\"54\",\"apple-cryptokit\"\n\"54\",\"gulp-rename\"\n\"54\",\"guice-persist\"\n\"54\",\"svnant\"\n\"54\",\"cosmos-sdk\"\n\"54\",\"mongodb-mms\"\n\"54\",\"wse3.0\"\n\"54\",\"open-generics\"\n\"54\",\"android-unit-testing\"\n\"54\",\"bogus\"\n\"54\",\"jtwitter\"\n\"54\",\"android-screen-pinning\"\n\"54\",\"nservicebus-distributor\"\n\"54\",\"portia\"\n\"54\",\"jxcore\"\n\"54\",\"swaggerhub\"\n\"54\",\"aws-service-catalog\"\n\"54\",\"neography\"\n\"54\",\"sca\"\n\"54\",\"swagger-codegen-maven-plugin\"\n\"54\",\"botpress\"\n\"54\",\"neo4j-desktop\"\n\"54\",\"botium-box\"\n\"54\",\"core-services\"\n\"54\",\"oniguruma\"\n\"54\",\"momentics\"\n\"54\",\"boost-tuples\"\n\"54\",\"javax.ws.rs\"\n\"54\",\"pydio\"\n\"54\",\"shopify-hydrogen\"\n\"54\",\"return-by-value\"\n\"54\",\"sifr3\"\n\"54\",\"codio\"\n\"54\",\"raspbian-buster\"\n\"54\",\"virtualmode\"\n\"54\",\".mov\"\n\"54\",\"form-designer\"\n\"54\",\"word-2016\"\n\"54\",\"golden-layout\"\n\"54\",\"pypandoc\"\n\"54\",\"minimatch\"\n\"54\",\"virtual-column\"\n\"54\",\"kmalloc\"\n\"54\",\"java-memory-leaks\"\n\"54\",\"formsauthenticationticket\"\n\"54\",\"rfc3986\"\n\"54\",\"formatjs\"\n\"54\",\"objective-c-literals\"\n\"54\",\"raiseerror\"\n\"54\",\"facebook-live-api\"\n\"54\",\"code39\"\n\"54\",\"krypton-toolkit\"\n\"54\",\"cocoahttpserver\"\n\"54\",\"f#-scripting\"\n\"54\",\"macdeployqt\"\n\"54\",\"typelist\"\n\"54\",\"macos-darkmode\"\n\"54\",\"vision-transformer\"\n\"54\",\"android-applicationrecord\"\n\"54\",\"vitess\"\n\"54\",\"reform\"\n\"54\",\"poe\"\n\"54\",\"sqlfluff\"\n\"54\",\"c++builder-xe8\"\n\"54\",\"hook-wordpress\"\n\"54\",\"isapi-redirect\"\n\"54\",\"control-p5\"\n\"54\",\"buttonbar\"\n\"54\",\"driverless-ai\"\n\"54\",\"mixture\"\n\"54\",\"plv8\"\n\"54\",\"drupal-5\"\n\"54\",\"gb2312\"\n\"54\",\"azure-public-ip\"\n\"54\",\"dublin-core\"\n\"54\",\"spl-autoloader\"\n\"54\",\"angular-in-memory-web-api\"\n\"54\",\"getpass\"\n\"54\",\"commonmark\"\n\"54\",\"nntp\"\n\"54\",\"google-cloud-ai\"\n\"54\",\"eigenclass\"\n\"54\",\"qpainterpath\"\n\"54\",\"geocomplete\"\n\"54\",\"storybook-addon\"\n\"54\",\"laravel-response\"\n\"54\",\"toolbaritems\"\n\"54\",\"http-status-code-407\"\n\"54\",\"ninject.web\"\n\"54\",\"chargify\"\n\"54\",\"lsb\"\n\"54\",\"ejabberd-hooks\"\n\"54\",\"textflow\"\n\"54\",\"laravel-upgrade\"\n\"54\",\"command-query-separation\"\n\"54\",\"pyuno\"\n\"54\",\"message-pump\"\n\"54\",\"active-window\"\n\"54\",\"sprig-template-functions\"\n\"54\",\"moses\"\n\"54\",\"meta-method\"\n\"54\",\"cucm\"\n\"54\",\"off-by-one\"\n\"54\",\"strongly-connected-graph\"\n\"54\",\"google-data-catalog\"\n\"54\",\"qdrant\"\n\"54\",\"autobahnws\"\n\"54\",\"tig\"\n\"54\",\"msmq-wcf\"\n\"54\",\"uwebsockets\"\n\"54\",\"altivec\"\n\"54\",\"heatmaply\"\n\"54\",\"spark-ada\"\n\"54\",\"maven-repository\"\n\"54\",\"enchant\"\n\"54\",\"arquillian-drone\"\n\"54\",\"alt-tab\"\n\"54\",\"qt-slot\"\n\"54\",\"array-walk\"\n\"54\",\"qstatemachine\"\n\"54\",\"web3modal\"\n\"54\",\"shell32.dll\"\n\"54\",\"flutter-webrtc\"\n\"54\",\"solrconfig\"\n\"54\",\"preferslargetitles\"\n\"54\",\"iglistkit\"\n\"54\",\"suitescript1.0\"\n\"54\",\"igmp\"\n\"54\",\"global-payments-api\"\n\"54\",\"dart-unittest\"\n\"54\",\"linecache\"\n\"54\",\"glblendfunc\"\n\"54\",\"herestring\"\n\"54\",\"topbraid-composer\"\n\"54\",\"completion-block\"\n\"54\",\"source-insight\"\n\"54\",\"web-compiler\"\n\"54\",\"compiler-specific\"\n\"54\",\"flutter-intl\"\n\"54\",\"pareto-chart\"\n\"54\",\"igoogle\"\n\"53\",\"transit\"\n\"53\",\"pg-upgrade\"\n\"53\",\"vue2-google-maps\"\n\"53\",\"dbup\"\n\"53\",\"clipperlib\"\n\"53\",\"phalcon-orm\"\n\"53\",\"sql-server-job\"\n\"53\",\"instant-run\"\n\"53\",\"clicktag\"\n\"53\",\"babelify\"\n\"53\",\"fiddle\"\n\"53\",\"skos\"\n\"53\",\"mask-rcnn\"\n\"53\",\"jenkins-github-plugin\"\n\"53\",\"figaro-ruby\"\n\"53\",\"groupchat\"\n\"53\",\"webpage-rendering\"\n\"53\",\"php-imagine\"\n\"53\",\"gitlens\"\n\"53\",\"clientip\"\n\"53\",\"live-connect-sdk\"\n\"53\",\"matchtemplate\"\n\"53\",\"apache-kafka-security\"\n\"53\",\"gitlab-8\"\n\"53\",\"webtrends\"\n\"53\",\"github-projects\"\n\"53\",\"ants\"\n\"53\",\"fetchcontent\"\n\"53\",\"php-mssql\"\n\"53\",\"probing\"\n\"53\",\"ggcorrplot\"\n\"53\",\"phoneme\"\n\"53\",\"probot\"\n\"53\",\"ngrx-router-store\"\n\"53\",\"underscores-wp\"\n\"53\",\"swiftydropbox\"\n\"53\",\"flexboxgrid\"\n\"53\",\"daypilot\"\n\"53\",\"bit-masks\"\n\"53\",\"rxvt\"\n\"53\",\"firstdata\"\n\"53\",\"adaptive-ui\"\n\"53\",\"file-system-access-api\"\n\"53\",\"select-function\"\n\"53\",\"chrome-for-android\"\n\"53\",\"bizspark\"\n\"53\",\"jsr356\"\n\"53\",\"image-viewer\"\n\"53\",\"firebird1.5\"\n\"53\",\"swift-mt\"\n\"53\",\"imputets\"\n\"53\",\"palm-pre\"\n\"53\",\"fsfs\"\n\"53\",\"distributed-lock\"\n\"53\",\"xmladapter\"\n\"53\",\"biological-neural-network\"\n\"53\",\"palantir-foundry-api\"\n\"53\",\"connman\"\n\"53\",\"find-by-sql\"\n\"53\",\"uml-designer\"\n\"53\",\"apache-nms\"\n\"53\",\"checkin-policy\"\n\"53\",\"indexing-service\"\n\"53\",\"social-tables\"\n\"53\",\"kube-state-metrics\"\n\"53\",\"flash-scope\"\n\"53\",\"kubespray\"\n\"53\",\"findfirst\"\n\"53\",\"imagenamed\"\n\"53\",\"map-force\"\n\"53\",\"xenomai\"\n\"53\",\"sal\"\n\"53\",\"constexpr-function\"\n\"53\",\"varying\"\n\"53\",\"mysql-error-2002\"\n\"53\",\"recorder.js\"\n\"53\",\"opkg\"\n\"53\",\"crowdsourcing\"\n\"53\",\"grails-services\"\n\"53\",\"camera-view\"\n\"53\",\"agora-implementation\"\n\"53\",\"psoc\"\n\"53\",\"doctrine-phpcr\"\n\"53\",\"dataitem\"\n\"53\",\"microkernel\"\n\"53\",\"ruby-enterprise-edition\"\n\"53\",\"createobjecturl\"\n\"53\",\"pycodestyle\"\n\"53\",\"google-wallet\"\n\"53\",\"google-web-component\"\n\"53\",\"goose\"\n\"53\",\"fail-fast\"\n\"53\",\"oracle-dump\"\n\"53\",\"rswag\"\n\"53\",\"caldroid\"\n\"53\",\"valuemember\"\n\"53\",\"microsoft-graph-edu\"\n\"53\",\"meteor-slingshot\"\n\"53\",\"ajdt\"\n\"53\",\"vega-lite-api\"\n\"53\",\"vega-embed\"\n\"53\",\"jhtmlarea\"\n\"53\",\"django-react\"\n\"53\",\"episerver-6\"\n\"53\",\"html-injections\"\n\"53\",\"twitter-anywhere\"\n\"53\",\"createcontext\"\n\"53\",\"modelio\"\n\"53\",\"easyautocomplete\"\n\"53\",\"pathauto\"\n\"53\",\"password-confirmation\"\n\"53\",\"epub.js\"\n\"53\",\"navigation-timing-api\"\n\"53\",\"ontap\"\n\"53\",\"android-studio-3.4\"\n\"53\",\"spring-cloud-loadbalancer\"\n\"53\",\"openaccess\"\n\"53\",\"cqlinq\"\n\"53\",\"paymill\"\n\"53\",\"application-data\"\n\"53\",\"swfaddress\"\n\"53\",\"entity-model\"\n\"53\",\"environ\"\n\"53\",\"kbhit\"\n\"53\",\"juju\"\n\"53\",\"entityset\"\n\"53\",\"postgresql-triggers\"\n\"53\",\"kafka-partition\"\n\"53\",\"android-textattributes\"\n\"53\",\"view-hierarchy\"\n\"53\",\"atata\"\n\"53\",\"browser-action\"\n\"53\",\"pygrib\"\n\"53\",\"windows-xp-embedded\"\n\"53\",\"oracle-wallet\"\n\"53\",\"mikrotik\"\n\"53\",\"lexicographic-ordering\"\n\"53\",\"audioeffect\"\n\"53\",\"netbeans7.0\"\n\"53\",\"lexical-closures\"\n\"53\",\"codeskulptor\"\n\"53\",\"oracle-xml-db\"\n\"53\",\"extensible\"\n\"53\",\"object-identity\"\n\"53\",\"foundry-code-workbooks\"\n\"53\",\"windows-template-studio\"\n\"53\",\"magic-draw\"\n\"53\",\"less-loader\"\n\"53\",\"neutralinojs\"\n\"53\",\"rikulo\"\n\"53\",\"external-data-source\"\n\"53\",\"mahotas\"\n\"53\",\"oauth.io\"\n\"53\",\"facebook-batch-request\"\n\"53\",\"aubio\"\n\"53\",\"system-paths\"\n\"53\",\"tabular-form\"\n\"53\",\"asort\"\n\"53\",\"jaggery-js\"\n\"53\",\"modeline\"\n\"53\",\"xaml-binding\"\n\"53\",\"drizzle-orm\"\n\"53\",\"hig\"\n\"53\",\"hibernate-search-6\"\n\"53\",\"gcc6\"\n\"53\",\"not-operator\"\n\"53\",\"gcc-plugins\"\n\"53\",\"aspose-slides\"\n\"53\",\"drawellipse\"\n\"53\",\"gabor-filter\"\n\"53\",\"excel-pivot\"\n\"53\",\"playwright-sharp\"\n\"53\",\"x-accel-redirect\"\n\"53\",\"tbitmap\"\n\"53\",\"toit\"\n\"53\",\"iserializable\"\n\"53\",\"double-hashing\"\n\"53\",\"xcode7-beta3\"\n\"53\",\"numerics\"\n\"53\",\"xctool\"\n\"53\",\"tabris\"\n\"53\",\"offsetwidth\"\n\"53\",\"httpruntime.cache\"\n\"53\",\"angular2-providers\"\n\"53\",\"http-protocols\"\n\"53\",\"nibble\"\n\"53\",\"excel-4.0\"\n\"53\",\"strophe.js\"\n\"53\",\"nicegui\"\n\"53\",\"command-line-parser\"\n\"53\",\"elephantbird\"\n\"53\",\"login-with-amazon\"\n\"53\",\"loguru\"\n\"53\",\"spectral\"\n\"53\",\"resource-id\"\n\"53\",\"launch-services\"\n\"53\",\"ios-homekit\"\n\"53\",\"cupertinopicker\"\n\"53\",\"mountebank\"\n\"53\",\"protorpc\"\n\"53\",\"oltu\"\n\"53\",\"node-neo4j\"\n\"53\",\"mosca\"\n\"53\",\"morphic\"\n\"53\",\"sphinx-apidoc\"\n\"53\",\"geom-area\"\n\"53\",\"launchdarkly\"\n\"53\",\"reactjs-native\"\n\"53\",\"log4cpp\"\n\"53\",\"merge-statement\"\n\"53\",\"las\"\n\"53\",\"concurrently\"\n\"53\",\"amazon-fsx\"\n\"53\",\"thread-exceptions\"\n\"53\",\"zen\"\n\"53\",\"mean-shift\"\n\"53\",\"goaccess\"\n\"53\",\"line-spacing\"\n\"53\",\"quarkus-oidc\"\n\"53\",\"shibboleth-sp\"\n\"53\",\"emacs-ecb\"\n\"53\",\"suckerfish\"\n\"53\",\"webcal\"\n\"53\",\"scribd\"\n\"53\",\"v4l\"\n\"53\",\"query-notifications\"\n\"53\",\"auth-request\"\n\"53\",\"embedded-tomcat\"\n\"53\",\"sugarbean\"\n\"53\",\"webgl-extensions\"\n\"53\",\"gnome-builder\"\n\"53\",\"yticks\"\n\"53\",\"ms-forms\"\n\"53\",\"qt5.15\"\n\"53\",\"hazard\"\n\"53\",\"zigzag\"\n\"53\",\"powerbi-rest-api\"\n\"53\",\"qt4.6\"\n\"53\",\"zope.interface\"\n\"53\",\"engine.io\"\n\"53\",\"webkit.net\"\n\"53\",\"asdoc\"\n\"53\",\"mediasession\"\n\"53\",\"asf\"\n\"53\",\"ber\"\n\"53\",\"heads-up-notifications\"\n\"52\",\"jenkins-kubernetes\"\n\"52\",\"sql-server-performance\"\n\"52\",\"linux-toolchain\"\n\"52\",\"xrp\"\n\"52\",\"group-membership\"\n\"52\",\"filecompare\"\n\"52\",\"jconnect\"\n\"52\",\"lispworks\"\n\"52\",\"multiway-tree\"\n\"52\",\"primeflex\"\n\"52\",\"cling\"\n\"52\",\"mathematical-lattices\"\n\"52\",\"tensorflow2\"\n\"52\",\"flutter-debug\"\n\"52\",\"multi-table-inheritance\"\n\"52\",\"sitefinity-5\"\n\"52\",\"apache-commons-pool\"\n\"52\",\"eclipse-sirius\"\n\"52\",\"floor-division\"\n\"52\",\"liveconnect\"\n\"52\",\"transport-security\"\n\"52\",\"materialcardview\"\n\"52\",\"fetchrequest\"\n\"52\",\"git-archive\"\n\"52\",\"ssp\"\n\"52\",\"apache-chainsaw\"\n\"52\",\"mat-card\"\n\"52\",\"safeareaview\"\n\"52\",\"chrome-remote-debugging\"\n\"52\",\"addon-domain\"\n\"52\",\"swiftui-layout\"\n\"52\",\"jsonb-api\"\n\"52\",\"unbuffered\"\n\"52\",\"safari-web-extension\"\n\"52\",\"appcelerator-hyperloop\"\n\"52\",\"unchecked-cast\"\n\"52\",\"python-for-android\"\n\"52\",\"mapbox-gl-draw\"\n\"52\",\"smartscreen\"\n\"52\",\"contactpicker\"\n\"52\",\"piracy-prevention\"\n\"52\",\"snapshot-testing\"\n\"52\",\"console.readkey\"\n\"52\",\"python-moderngl\"\n\"52\",\"rxdb\"\n\"52\",\"confluent-control-center\"\n\"52\",\"chewy-gem\"\n\"52\",\"symfony-2.0\"\n\"52\",\"dismo\"\n\"52\",\"xl-deploy\"\n\"52\",\"kurtosis\"\n\"52\",\"smtpd\"\n\"52\",\"chefdk\"\n\"52\",\"runcommand\"\n\"52\",\"jtapplecalendar\"\n\"52\",\"runjags\"\n\"52\",\"sms-retriever-api\"\n\"52\",\"diskarbitration\"\n\"52\",\"socket.io-java-client\"\n\"52\",\"binding.scala\"\n\"52\",\"connectionexception\"\n\"52\",\"mysql-innodb-cluster\"\n\"52\",\"wildfly-26\"\n\"52\",\"datajs\"\n\"52\",\"gradle-shadow-plugin\"\n\"52\",\"gradle-release-plugin\"\n\"52\",\"aws-media-live\"\n\"52\",\"simplekml\"\n\"52\",\"mysqltuner\"\n\"52\",\"wildcard-expansion\"\n\"52\",\"jgitflow-maven-plugin\"\n\"52\",\"waveout\"\n\"52\",\"jive\"\n\"52\",\"django-management-command\"\n\"52\",\"seq-logging\"\n\"52\",\"pyamf\"\n\"52\",\"csip-simple\"\n\"52\",\"windows-administration\"\n\"52\",\"angular-spectator\"\n\"52\",\"angular-state-managmement\"\n\"52\",\"angular-ui-datepicker\"\n\"52\",\"wagon\"\n\"52\",\"django-dev-server\"\n\"52\",\"angularjs-view\"\n\"52\",\"angularjs-track-by\"\n\"52\",\"rsvp-promise\"\n\"52\",\"windows-2003-webserver\"\n\"52\",\"routedevent\"\n\"52\",\"windowsdomainaccount\"\n\"52\",\"windows-driver\"\n\"52\",\"kendo-menu\"\n\"52\",\"dmx512\"\n\"52\",\"grape-entity\"\n\"52\",\"windows-forms-core\"\n\"52\",\"rst2pdf\"\n\"52\",\"datadog-dashboard\"\n\"52\",\"caller-id\"\n\"52\",\"pattern-synonyms\"\n\"52\",\"nested-set-model\"\n\"52\",\"application-icon\"\n\"52\",\"jquery-mobile-ajax\"\n\"52\",\"ini4j\"\n\"52\",\"boehm-gc\"\n\"52\",\"jquery-layout\"\n\"52\",\"boost-locale\"\n\"52\",\"android-traceview\"\n\"52\",\"modx-resources\"\n\"52\",\"inria-spoon\"\n\"52\",\"salt-cloud\"\n\"52\",\"gulp-useref\"\n\"52\",\"nssecurecoding\"\n\"52\",\"dynamic-assemblies\"\n\"52\",\"two.js\"\n\"52\",\"eaccelerator\"\n\"52\",\"dynamic-parallelism\"\n\"52\",\"dynamic-values\"\n\"52\",\"wsastartup\"\n\"52\",\"cpplint\"\n\"52\",\"inplace-editing\"\n\"52\",\"bootcamp\"\n\"52\",\"postgres-10\"\n\"52\",\"inlines\"\n\"52\",\"intrinsic-content-size\"\n\"52\",\"inputverifier\"\n\"52\",\"bonsai-elasticsearch\"\n\"52\",\"mailhog\"\n\"52\",\"typescript-definitions\"\n\"52\",\"branchless\"\n\"52\",\"javahelp\"\n\"52\",\"libmagic\"\n\"52\",\"tabbed-view\"\n\"52\",\"system.type\"\n\"52\",\"pygame2\"\n\"52\",\"ampersand.js\"\n\"52\",\"abortcontroller\"\n\"52\",\"typescript2.2\"\n\"52\",\"rdf-xml\"\n\"52\",\"code-navigation\"\n\"52\",\".netrc\"\n\"52\",\"build-triggers\"\n\"52\",\"audio-source\"\n\"52\",\"ramaze\"\n\"52\",\"lessphp\"\n\"52\",\"rcaller\"\n\"52\",\"leksah\"\n\"52\",\"gold-linker\"\n\"52\",\"m2e-wtp\"\n\"52\",\"syncsort\"\n\"52\",\"pykalman\"\n\"52\",\"word-field\"\n\"52\",\"dompurify\"\n\"52\",\"netbeans-7.3\"\n\"52\",\"reversi\"\n\"52\",\"knox-gateway\"\n\"52\",\"azure-secrets\"\n\"52\",\"pod-install\"\n\"52\",\"buttonfield\"\n\"52\",\"double-submit-problem\"\n\"52\",\"table-statistics\"\n\"52\",\"continuation\"\n\"52\",\"scikit-optimize\"\n\"52\",\"sqlhelper\"\n\"52\",\"ispell\"\n\"52\",\"expressionvisitor\"\n\"52\",\"table-per-class\"\n\"52\",\"reduce-reduce-conflict\"\n\"52\",\"expression-encoder-sdk\"\n\"52\",\"ganymede\"\n\"52\",\"jade4j\"\n\"52\",\"railway.js\"\n\"52\",\"gaufrette\"\n\"52\",\"red-zone\"\n\"52\",\"gwt-compiler\"\n\"52\",\"gemalto\"\n\"52\",\"tcomport\"\n\"52\",\"copybook\"\n\"52\",\"ref-qualifier\"\n\"52\",\"xcos\"\n\"52\",\"drupal-navigation\"\n\"52\",\"dex2jar\"\n\"52\",\"azure-function-async\"\n\"52\",\"uimenu\"\n\"52\",\"drives\"\n\"52\",\"tapkey\"\n\"52\",\"tinkerpop-blueprint\"\n\"52\",\"sql-server-2017-express\"\n\"52\",\"radrails\"\n\"52\",\"dev-mode\"\n\"52\",\"getproperties\"\n\"52\",\"property-observer\"\n\"52\",\"current-page\"\n\"52\",\"geom-ribbon\"\n\"52\",\"chalk\"\n\"52\",\"oh-my-posh\"\n\"52\",\"movable\"\n\"52\",\"cumulative-layout-shift\"\n\"52\",\"acts-as-tree\"\n\"52\",\"chaiscript\"\n\"52\",\"event-wait-handle\"\n\"52\",\"prtg\"\n\"52\",\"elasticsearch.net\"\n\"52\",\"ejbql\"\n\"52\",\"spotfire-webplayer\"\n\"52\",\"event-capturing\"\n\"52\",\"cfiledialog\"\n\"52\",\"geometry-instancing\"\n\"52\",\"react-datetime\"\n\"52\",\"perlscript\"\n\"52\",\"dumpdata\"\n\"52\",\"iphone-vibrate\"\n\"52\",\"monitors\"\n\"52\",\"qcar-sdk\"\n\"52\",\"reactive-cocoa-3\"\n\"52\",\"csx\"\n\"52\",\"lazyvstack\"\n\"52\",\"monolithic\"\n\"52\",\"msp\"\n\"52\",\"behind\"\n\"52\",\"amazon-deequ\"\n\"52\",\"avassetimagegenerator\"\n\"52\",\"passlib\"\n\"52\",\"asadmin\"\n\"52\",\"hft\"\n\"52\",\"lidr\"\n\"52\",\"multimarkdown\"\n\"52\",\"stxxl\"\n\"52\",\"comvisible\"\n\"52\",\"zendx\"\n\"52\",\"pgfplots\"\n\"52\",\"pangocairo\"\n\"52\",\"healthconnect\"\n\"52\",\"czml\"\n\"52\",\"amazon-kinesis-kpl\"\n\"52\",\"seadragon\"\n\"52\",\"em-websocket\"\n\"52\",\"glassfish-2.x\"\n\"52\",\"qt5.1\"\n\"52\",\"multiarch\"\n\"52\",\"image-cropper\"\n\"52\",\"maximization\"\n\"52\",\"linear-probing\"\n\"52\",\"arduinojson\"\n\"52\",\"multicollinearity\"\n\"52\",\"starcluster\"\n\"51\",\"groovy-eclipse\"\n\"51\",\"flexunit4\"\n\"51\",\"dbproviderfactories\"\n\"51\",\"white-box-testing\"\n\"51\",\"technical-debt\"\n\"51\",\"yajsw\"\n\"51\",\"graphql-playground\"\n\"51\",\"vssdk\"\n\"51\",\"xui\"\n\"51\",\"relativedelta\"\n\"51\",\"reorderable-list\"\n\"51\",\"yii-cactiverecord\"\n\"51\",\"matchmaking\"\n\"51\",\"masspay\"\n\"51\",\"ginput\"\n\"51\",\"phpstorm-2017.1\"\n\"51\",\"apache-apex\"\n\"51\",\"gimbal\"\n\"51\",\"apache-crunch\"\n\"51\",\"mathcad\"\n\"51\",\"base36\"\n\"51\",\"wep\"\n\"51\",\"filab\"\n\"51\",\"yii-chtml\"\n\"51\",\"integer-partition\"\n\"51\",\"dateparser\"\n\"51\",\"fsspec\"\n\"51\",\"configobj\"\n\"51\",\"pipelinedb\"\n\"51\",\"xml-declaration\"\n\"51\",\"pandaboard\"\n\"51\",\"firebase-queue\"\n\"51\",\"filterfunction\"\n\"51\",\"disk-access\"\n\"51\",\"page-setup\"\n\"51\",\"fts5\"\n\"51\",\"ceilometer\"\n\"51\",\"binascii\"\n\"51\",\"snowplow\"\n\"51\",\"catalystbyzoho\"\n\"51\",\"swupdate\"\n\"51\",\"content-for\"\n\"51\",\"fromfile\"\n\"51\",\"rust-async-std\"\n\"51\",\"switchpreference\"\n\"51\",\"constraint-validation-api\"\n\"51\",\"cassandra-jdbc\"\n\"51\",\"chromeless\"\n\"51\",\"kuzzle\"\n\"51\",\"ng-mocks\"\n\"51\",\"rust-obsolete\"\n\"51\",\"ngx-toastr\"\n\"51\",\"distributed-objects\"\n\"51\",\"cassandra-0.7\"\n\"51\",\"pureconfig\"\n\"51\",\"i2s\"\n\"51\",\"up-navigation\"\n\"51\",\"docxtpl\"\n\"51\",\"datajoint\"\n\"51\",\"aho-corasick\"\n\"51\",\"gopls\"\n\"51\",\"verlet-integration\"\n\"51\",\"upsource\"\n\"51\",\"aws-devops\"\n\"51\",\"django-formtools\"\n\"51\",\"upx\"\n\"51\",\"serenity-platform\"\n\"51\",\"got\"\n\"51\",\"angulartics\"\n\"51\",\"angular-testing-library\"\n\"51\",\"windows-server-2000\"\n\"51\",\"ruby-2.5\"\n\"51\",\"facetwp\"\n\"51\",\"mystic\"\n\"51\",\"call-hierarchy\"\n\"51\",\"kframework\"\n\"51\",\"rose-plot\"\n\"51\",\"django-polymorphic\"\n\"51\",\"simpledialog\"\n\"51\",\"metrics-server\"\n\"51\",\"rspec-puppet\"\n\"51\",\"aws-amplify-sdk-android\"\n\"51\",\"namespace-package\"\n\"51\",\"vcproj\"\n\"51\",\"wspbuilder\"\n\"51\",\"cray\"\n\"51\",\"nativescript-cli\"\n\"51\",\"navigationsplitview\"\n\"51\",\"infobip\"\n\"51\",\"android-window\"\n\"51\",\"gtrendsr\"\n\"51\",\"axon-framework\"\n\"51\",\"monger\"\n\"51\",\"wp-nav-menu-item\"\n\"51\",\"azure-emulator\"\n\"51\",\"wowza-transcoder\"\n\"51\",\"corewcf\"\n\"51\",\"txmldocument\"\n\"51\",\"boost-spirit-karma\"\n\"51\",\"popuppanel\"\n\"51\",\"onepage-checkout\"\n\"51\",\"gtkmm4\"\n\"51\",\"gulp-rev\"\n\"51\",\"invision-power-board\"\n\"51\",\"html-help-workshop\"\n\"51\",\"android-simple-facebook\"\n\"51\",\"swift-keypath\"\n\"51\",\"application-security\"\n\"51\",\"postgres-xl\"\n\"51\",\"dynamicparameters\"\n\"51\",\"pasting\"\n\"51\",\"libdispatch\"\n\"51\",\"ocmod\"\n\"51\",\"synedit\"\n\"51\",\"wordpress.com\"\n\"51\",\"pysam\"\n\"51\",\"netbeans-7.4\"\n\"51\",\"synopsys-vcs\"\n\"51\",\"mini-forge\"\n\"51\",\"winforms-to-web\"\n\"51\",\"google-2fa\"\n\"51\",\"rainmeter\"\n\"51\",\"fragment-transitions\"\n\"51\",\"fpgrowth\"\n\"51\",\"sgplot\"\n\"51\",\"tableau-prep\"\n\"51\",\"google-alerts\"\n\"51\",\"visual-leak-detector\"\n\"51\",\"siege\"\n\"51\",\"sidr\"\n\"51\",\"pymodbustcp\"\n\"51\",\"asynccontroller\"\n\"51\",\"vmd\"\n\"51\",\"xcode7-beta6\"\n\"51\",\"android-firmware\"\n\"51\",\"halo\"\n\"51\",\"scalastyle\"\n\"51\",\"hibernation\"\n\"51\",\"taleo\"\n\"51\",\"cacheapi\"\n\"51\",\"hoare-logic\"\n\"51\",\"uidynamicbehavior\"\n\"51\",\"sql-match-all\"\n\"51\",\"android-components\"\n\"51\",\"azure-subscription\"\n\"51\",\"gwt-activities\"\n\"51\",\"table-partitioning\"\n\"51\",\"controller-action\"\n\"51\",\"mobicents-sip-servlets\"\n\"51\",\"gbk\"\n\"51\",\"dotween\"\n\"51\",\"hilla\"\n\"51\",\"spring-social-linkedin\"\n\"51\",\"tdm-mingw\"\n\"51\",\"direct-labels\"\n\"51\",\"android-dateutils\"\n\"51\",\"exif-js\"\n\"51\",\"devkitpro\"\n\"51\",\"tivoli-work-scheduler\"\n\"51\",\"xattribute\"\n\"51\",\"azure-sql\"\n\"51\",\"android-bottom-nav-view\"\n\"51\",\"rackspace-cloudfiles\"\n\"51\",\"tagfile\"\n\"51\",\"gcmlistenerservice\"\n\"51\",\"timemachine\"\n\"51\",\"android-d8\"\n\"51\",\"dib\"\n\"51\",\"dtd-parsing\"\n\"51\",\"polarssl\"\n\"51\",\"scala-metals\"\n\"51\",\"assemblyversions\"\n\"51\",\"isomorphic\"\n\"51\",\"tolist\"\n\"51\",\"tokudb\"\n\"51\",\"gal\"\n\"51\",\"penetration-tools\"\n\"51\",\"moto-360\"\n\"51\",\"respond-with\"\n\"51\",\"cfrunloop\"\n\"51\",\"movefile\"\n\"51\",\"get-eventlog\"\n\"51\",\"getfeatureinfo\"\n\"51\",\"spinning\"\n\"51\",\"eggplant\"\n\"51\",\"ojs\"\n\"51\",\"eggdrop\"\n\"51\",\"custom-receiver\"\n\"51\",\"ironmq\"\n\"51\",\"qgraphicspixmapitem\"\n\"51\",\"node-pg-pool\"\n\"51\",\"google-cloud-debugger\"\n\"51\",\"latent-semantic-indexing\"\n\"51\",\"command-window\"\n\"51\",\"angularjs-interpolate\"\n\"51\",\"node-https\"\n\"51\",\"android-photos\"\n\"51\",\"ipynb\"\n\"51\",\"node-promisify\"\n\"51\",\"mesh-collider\"\n\"51\",\"node.js-nan\"\n\"51\",\"ltrace\"\n\"51\",\"pyvenv\"\n\"51\",\"event-based-programming\"\n\"51\",\"event-arc\"\n\"51\",\"testrigor\"\n\"51\",\"spatial-data-frame\"\n\"51\",\"nmock\"\n\"51\",\"nltk-trainer\"\n\"51\",\"multidplyr\"\n\"51\",\"suitesparse\"\n\"51\",\"qstylesheet\"\n\"51\",\"array-combine\"\n\"51\",\"idispatchmessageinspector\"\n\"51\",\"zend-test\"\n\"51\",\"daab\"\n\"51\",\"prelaunch\"\n\"51\",\"parallel-data-warehouse\"\n\"51\",\"user-defined-data-types\"\n\"51\",\"idle-processing\"\n\"51\",\"thinkscript\"\n\"51\",\"stereotype\"\n\"51\",\"lingo\"\n\"51\",\"parse-dashboard\"\n\"51\",\"gkmatchmaker\"\n\"51\",\"sound-recognition\"\n\"51\",\"subsonic-active-record\"\n\"51\",\"limma\"\n\"51\",\"bigchaindb\"\n\"51\",\"gnu-efi\"\n\"51\",\"comsol\"\n\"51\",\"auto-build\"\n\"51\",\"artwork\"\n\"51\",\"scrolledwindow\"\n\"51\",\"hexagon-dsp\"\n\"51\",\"authz\"\n\"51\",\"alpine-package-keeper\"\n\"51\",\"compojure-api\"\n\"50\",\"cleverhans\"\n\"50\",\"matrix-synapse\"\n\"50\",\"probabilistic-programming\"\n\"50\",\"class-properties\"\n\"50\",\"groupstyle\"\n\"50\",\"flowgear\"\n\"50\",\"multiple-makefiles\"\n\"50\",\"markov-decision-process\"\n\"50\",\"xtradb\"\n\"50\",\"feedly\"\n\"50\",\"websvn\"\n\"50\",\"listmodel\"\n\"50\",\"php-stream-wrappers\"\n\"50\",\"ssis-2019\"\n\"50\",\"vue.draggable\"\n\"50\",\"primeng-treetable\"\n\"50\",\"phalanger\"\n\"50\",\"jericho-html-parser\"\n\"50\",\"intellitest\"\n\"50\",\"default.png\"\n\"50\",\"flutter-deep-link\"\n\"50\",\"packetbeat\"\n\"50\",\"lammps\"\n\"50\",\"content-tag\"\n\"50\",\"container-queries\"\n\"50\",\"unified-service-desk\"\n\"50\",\"biplot\"\n\"50\",\"package-private\"\n\"50\",\"impyla\"\n\"50\",\"fixed-point-iteration\"\n\"50\",\"language-switching\"\n\"50\",\"r-zelig\"\n\"50\",\"pia\"\n\"50\",\"ccd\"\n\"50\",\"apache-sshd\"\n\"50\",\"adm-zip\"\n\"50\",\"seneca\"\n\"50\",\"chrome-declarativenetrequest\"\n\"50\",\"distillery\"\n\"50\",\"jsencrypt\"\n\"50\",\"xml-generation\"\n\"50\",\"ng\"\n\"50\",\"rust-clippy\"\n\"50\",\"jtopen\"\n\"50\",\"flashplayer-10\"\n\"50\",\"runlevel\"\n\"50\",\"dataviewwebpart\"\n\"50\",\"diskcache\"\n\"50\",\"blanket.js\"\n\"50\",\"mariadb-10.6\"\n\"50\",\"grafana-dashboard\"\n\"50\",\"unshelve\"\n\"50\",\"server-migration\"\n\"50\",\"sharepoint-userprofile\"\n\"50\",\"pvrtc\"\n\"50\",\"waitgroup\"\n\"50\",\"windows-mobile-6.1\"\n\"50\",\"crystal-reports-2013\"\n\"50\",\"ora-00979\"\n\"50\",\"upwork-api\"\n\"50\",\"document.evaluate\"\n\"50\",\"keyrelease\"\n\"50\",\"gora\"\n\"50\",\"shared-secret\"\n\"50\",\"docker-command\"\n\"50\",\"wcf-extensions\"\n\"50\",\"service-object\"\n\"50\",\"named-routing\"\n\"50\",\"windows-share\"\n\"50\",\"sequential-number\"\n\"50\",\"read-uncommitted\"\n\"50\",\"read-text\"\n\"50\",\"mysql-error-1055\"\n\"50\",\"ora-00001\"\n\"50\",\"meteor-methods\"\n\"50\",\"optics-algorithm\"\n\"50\",\"routevalues\"\n\"50\",\"angularjs-ng-pattern\"\n\"50\",\"createwritestream\"\n\"50\",\"microsoft365-defender\"\n\"50\",\"avcaptureoutput\"\n\"50\",\"simultaneous-calls\"\n\"50\",\"option-strict\"\n\"50\",\"windows-process\"\n\"50\",\"upsetr\"\n\"50\",\"boost-ublas\"\n\"50\",\"pawn\"\n\"50\",\"openbugs\"\n\"50\",\"ereg-replace\"\n\"50\",\"openbmc\"\n\"50\",\"htcsense\"\n\"50\",\"inno-setup-v6\"\n\"50\",\"dynamic-delivery\"\n\"50\",\"appinstaller\"\n\"50\",\"word-table\"\n\"50\",\"open-flash-chart\"\n\"50\",\"turbo-frames\"\n\"50\",\"guided-access\"\n\"50\",\"simplecaptcha\"\n\"50\",\"mongodb-ruby\"\n\"50\",\"houghlines\"\n\"50\",\"boost-dynamic-bitset\"\n\"50\",\"cqwp\"\n\"50\",\"densenet\"\n\"50\",\"k3d\"\n\"50\",\"nativewindow\"\n\"50\",\"html5mode\"\n\"50\",\"html-escape\"\n\"50\",\"typedarray\"\n\"50\",\"kcat\"\n\"50\",\"salesforce-ios-sdk\"\n\"50\",\"surge.sh\"\n\"50\",\"online-compilation\"\n\"50\",\"typebuilder\"\n\"50\",\"dependency-graph\"\n\"50\",\"desktop-duplication\"\n\"50\",\"twelvemonkeys\"\n\"50\",\"azure-cost-calculation\"\n\"50\",\"nestjs-mongoose\"\n\"50\",\"jaspic\"\n\"50\",\"setdefault\"\n\"50\",\"libtcod\"\n\"50\",\"lwuit-resource-editor\"\n\"50\",\"extjs2\"\n\"50\",\"objectfactory\"\n\"50\",\"fable\"\n\"50\",\"google-api-objc-client\"\n\"50\",\"richdatatable\"\n\"50\",\"cocoa-sheet\"\n\"50\",\"rcpp11\"\n\"50\",\"freedesktop.org\"\n\"50\",\"ubuntu-14.10\"\n\"50\",\"btdf\"\n\"50\",\"sysv-ipc\"\n\"50\",\"a2lix-translation\"\n\"50\",\"pynacl\"\n\"50\",\"formit\"\n\"50\",\"system.numerics\"\n\"50\",\"riff\"\n\"50\",\"facebook-invite-friends\"\n\"50\",\"broadcast-channel\"\n\"50\",\"68hc12\"\n\"50\",\"viewdidlayoutsubviews\"\n\"50\",\"extract-value\"\n\"50\",\"objc-bridging-header\"\n\"50\",\"forward-reference\"\n\"50\",\"javahl\"\n\"50\",\"rake-test\"\n\"50\",\"set-based\"\n\"50\",\"amortization\"\n\"50\",\"nyquist\"\n\"50\",\"plexus\"\n\"50\",\"redhat-brms\"\n\"50\",\"copytree\"\n\"50\",\"c#-interactive\"\n\"50\",\"exposure\"\n\"50\",\"tcombobox\"\n\"50\",\"scipy.ndimage\"\n\"50\",\"tdm-gcc\"\n\"50\",\"nokia-imaging-sdk\"\n\"50\",\"conventional-commits\"\n\"50\",\"contentview\"\n\"50\",\"wxperl\"\n\"50\",\"dot-operator\"\n\"50\",\"schemaexport\"\n\"50\",\"xapi\"\n\"50\",\"itemplate\"\n\"50\",\"caemittercell\"\n\"50\",\"itemspaneltemplate\"\n\"50\",\"itemwriter\"\n\"50\",\"cachemanager\"\n\"50\",\"driveinfo\"\n\"50\",\"radscheduler\"\n\"50\",\"plugin.xml\"\n\"50\",\"diagnostic-tools\"\n\"50\",\"modality\"\n\"50\",\"dinktopdf\"\n\"50\",\"diophantine\"\n\"50\",\"xclip\"\n\"50\",\"continuum\"\n\"50\",\"uipopoverpresentationcontroller\"\n\"50\",\"nvidia-deepstream\"\n\"50\",\"npm-init\"\n\"50\",\"angular-gridster2\"\n\"50\",\"log4javascript\"\n\"50\",\"angular-akita\"\n\"50\",\"layout-inspector\"\n\"50\",\"collection-initializer\"\n\"50\",\"httppostedfile\"\n\"50\",\"spire\"\n\"50\",\"httpruntime\"\n\"50\",\"movies\"\n\"50\",\"string-utils\"\n\"50\",\"peoplesoft-app-engine\"\n\"50\",\"google-elevation-api\"\n\"50\",\"tether\"\n\"50\",\"rest-security\"\n\"50\",\"mpu\"\n\"50\",\"result-of\"\n\"50\",\"getmessage\"\n\"50\",\"memory-size\"\n\"50\",\"etrade-api\"\n\"50\",\"etcd3\"\n\"50\",\"react-native-config\"\n\"50\",\"october-form-controller\"\n\"50\",\"node-pre-gyp\"\n\"50\",\"activecampaign\"\n\"50\",\"react-apexcharts\"\n\"50\",\"project-online\"\n\"50\",\"mop\"\n\"50\",\"centura\"\n\"50\",\"react-final-form-arrays\"\n\"50\",\"getid3\"\n\"50\",\"cfstream\"\n\"50\",\"mui-x-charts\"\n\"50\",\"cypher-3.1\"\n\"50\",\"sdmx\"\n\"50\",\"summarytools\"\n\"50\",\"auto-versioning\"\n\"50\",\"powershell-v5.1\"\n\"50\",\"foreignobject\"\n\"50\",\"paramarray\"\n\"50\",\"timber-android\"\n\"50\",\"tfs-2017\"\n\"50\",\"powercfg\"\n\"50\",\"statusline\"\n\"50\",\"bazel-java\"\n\"50\",\"stlport\"\n\"50\",\"pglogical\"\n\"50\",\"parallel-collections\"\n\"50\",\"haskell-turtle\"\n\"50\",\"ignite-ui-angular\"\n\"50\",\"threaded-comments\"\n\"50\",\"scrolledcomposite\"\n\"50\",\"gmaps4rails2\"\n\"50\",\"tpagecontrol\"\n\"50\",\"subfigure\"\n\"50\",\"basm\"\n\"50\",\"sublime-build\"\n\"50\",\"ar-foundation\"\n\"50\",\"zebra-striping\"\n\"50\",\"compositecollection\"\n\"50\",\"batman.js\"\n\"50\",\"concourse-git-resource\"\n\"50\",\"mediacapture\"\n\"50\",\"star-schema-datawarehouse\"\n\"50\",\"structlayout\"\n\"50\",\"qsslsocket\"\n\"50\",\"scribble\"\n\"50\",\"linux-distro\"\n\"50\",\"pptxgenjs\"\n\"49\",\"react-native-popup-menu\"\n\"49\",\"grep-indesign\"\n\"49\",\"phppresentation\"\n\"49\",\"repmgr\"\n\"49\",\"slf4j-api\"\n\"49\",\"loadable-component\"\n\"49\",\"dday\"\n\"49\",\"multiple-sites\"\n\"49\",\"report-viewer2012\"\n\"49\",\"phpstorm-2017.2\"\n\"49\",\"jetbrains-toolbox\"\n\"49\",\"cloc\"\n\"49\",\"multiserver\"\n\"49\",\"gfs\"\n\"49\",\"pgo\"\n\"49\",\"treecontrol\"\n\"49\",\"feature-store\"\n\"49\",\"ghostdoc\"\n\"49\",\"whisper\"\n\"49\",\"vtt\"\n\"49\",\"termcolor\"\n\"49\",\"clickable-image\"\n\"49\",\"websocket4net\"\n\"49\",\"jdbi3\"\n\"49\",\"jdbc-postgres\"\n\"49\",\"programmers-notepad\"\n\"49\",\"clevertap\"\n\"49\",\"coalescing\"\n\"49\",\"clang-cl\"\n\"49\",\"groovyscriptengine\"\n\"49\",\"telerik-scheduler\"\n\"49\",\"flask-dance\"\n\"49\",\"jstilemap\"\n\"49\",\"address-operator\"\n\"49\",\"bindingnavigator\"\n\"49\",\"socketscan\"\n\"49\",\"make-install\"\n\"49\",\"votive\"\n\"49\",\"functional-java\"\n\"49\",\"run-time-polymorphism\"\n\"49\",\"blat\"\n\"49\",\"self-executing-function\"\n\"49\",\"semantic-zoom\"\n\"49\",\"semigroup\"\n\"49\",\"image-reader\"\n\"49\",\"chronograf\"\n\"49\",\"selenium-remotedriver\"\n\"49\",\"ultrawebgrid\"\n\"49\",\"fsck\"\n\"49\",\"blackberry-editfield\"\n\"49\",\"pick\"\n\"49\",\"disconnection\"\n\"49\",\"discord.io\"\n\"49\",\"imanage\"\n\"49\",\"p4merge\"\n\"49\",\"safari-content-blocker\"\n\"49\",\"selectcommand\"\n\"49\",\"language-extension\"\n\"49\",\"jsp-fragments\"\n\"49\",\"unexpectendoffile\"\n\"49\",\"impactjs\"\n\"49\",\"xmldiff\"\n\"49\",\"unfold\"\n\"49\",\"rxjs-marbles\"\n\"49\",\"dataset-designer\"\n\"49\",\"implicit-flow\"\n\"49\",\"apigee-baas\"\n\"49\",\"recursive-regex\"\n\"49\",\"pushy\"\n\"49\",\"hyperref\"\n\"49\",\"watermelondb\"\n\"49\",\"pthreads-win32\"\n\"49\",\"name-value\"\n\"49\",\"pycocotools\"\n\"49\",\"createtextnode\"\n\"49\",\"data-formats\"\n\"49\",\"meteor-tracker\"\n\"49\",\"angular-tree-component\"\n\"49\",\"django-oauth-toolkit\"\n\"49\",\"airflow-xcom\"\n\"49\",\"fastjson\"\n\"49\",\"dashboard-designer\"\n\"49\",\"kite\"\n\"49\",\"django-mailer\"\n\"49\",\"gpt4all\"\n\"49\",\"wildfly-18\"\n\"49\",\"sharepoint-object-model\"\n\"49\",\"server-farm\"\n\"49\",\"pxssh\"\n\"49\",\"read-sql\"\n\"49\",\"rtx\"\n\"49\",\"gopacket\"\n\"49\",\"icanhaz.js\"\n\"49\",\"gossip\"\n\"49\",\"infinispan-9\"\n\"49\",\"juice-ui\"\n\"49\",\"deployment-project\"\n\"49\",\"pdoc\"\n\"49\",\"bootstrap-icons\"\n\"49\",\"htdp\"\n\"49\",\"pdist\"\n\"49\",\"html-tableextract\"\n\"49\",\"dynamics-365-sales\"\n\"49\",\"samd21\"\n\"49\",\"invariantculture\"\n\"49\",\"invariance\"\n\"49\",\"intrusive-containers\"\n\"49\",\"mongodb-c\"\n\"49\",\"bluetooth-socket\"\n\"49\",\"sbt-idea\"\n\"49\",\"errorprone\"\n\"49\",\"opaque-types\"\n\"49\",\"initwithcoder\"\n\"49\",\"dynamics-crm-uci\"\n\"49\",\"html.listboxfor\"\n\"49\",\"easy-peasy\"\n\"49\",\"dynamic-finders\"\n\"49\",\"sap-smp\"\n\"49\",\"azure-devops-services\"\n\"49\",\"nsmatrix\"\n\"49\",\"jzy3d\"\n\"49\",\"superpixels\"\n\"49\",\"gulp-ruby-sass\"\n\"49\",\"postgres-14\"\n\"49\",\"wuapi\"\n\"49\",\"onrender\"\n\"49\",\"ooyala\"\n\"49\",\"pax-web\"\n\"49\",\"svelte-transition\"\n\"49\",\"svndumpfilter\"\n\"49\",\"jquery-multidatespicker\"\n\"49\",\"nshttpurlresponse\"\n\"49\",\"wso2-stratos\"\n\"49\",\"jquery-ajax\"\n\"49\",\"patternlab.io\"\n\"49\",\"libxml-ruby\"\n\"49\",\"buildkite\"\n\"49\",\"codi\"\n\"49\",\"8thwall-web\"\n\"49\",\"audioinputstream\"\n\"49\",\"system.speech.recognition\"\n\"49\",\"objectname\"\n\"49\",\"miragejs\"\n\"49\",\"cocotb\"\n\"49\",\"pyjamas\"\n\"49\",\"external-tools\"\n\"49\",\"external-url\"\n\"49\",\"rgooglemaps\"\n\"49\",\"libgee\"\n\"49\",\"codecvt\"\n\"49\",\"format-patch\"\n\"49\",\"lemon-graph-library\"\n\"49\",\"oclint\"\n\"49\",\"ubifs\"\n\"49\",\"netzke\"\n\"49\",\"vlc-qt\"\n\"49\",\"amp-analytics\"\n\"49\",\"brokeredmessage\"\n\"49\",\"formfield\"\n\"49\",\"pymoo\"\n\"49\",\"freeform\"\n\"49\",\"uipickerviewdatasource\"\n\"49\",\"quickdialog\"\n\"49\",\"refinery\"\n\"49\",\"nstextcontainer\"\n\"49\",\"nosuchmethod\"\n\"49\",\"mjs\"\n\"49\",\"android-ide\"\n\"49\",\"game-maker-studio-1.4\"\n\"49\",\"drupal-feeds\"\n\"49\",\"gwt-places\"\n\"49\",\"raddatepicker\"\n\"49\",\"gwt-history\"\n\"49\",\"xcode4.6.3\"\n\"49\",\"itanium\"\n\"49\",\"schemagen\"\n\"49\",\"scala-maven-plugin\"\n\"49\",\"uiculture\"\n\"49\",\"tlistbox\"\n\"49\",\"doze\"\n\"49\",\"g77\"\n\"49\",\"timeuuid\"\n\"49\",\"opentripplanner\"\n\"49\",\"opentest\"\n\"49\",\"tdatetime\"\n\"49\",\"todomvc\"\n\"49\",\"uiimageasset\"\n\"49\",\"dhl\"\n\"49\",\"exiv2\"\n\"49\",\"dotnet-restore\"\n\"49\",\"azure-managed-disk\"\n\"49\",\"j1939\"\n\"49\",\"testability\"\n\"49\",\"chararray\"\n\"49\",\"android-rom\"\n\"49\",\"memory-safety\"\n\"49\",\"test-results\"\n\"49\",\"android-jetpack-compose-testing\"\n\"49\",\"chap-links-library\"\n\"49\",\"storing-information\"\n\"49\",\"moodle-theme\"\n\"49\",\"omnibox\"\n\"49\",\"android-percentrelativelayout\"\n\"49\",\"getrusage\"\n\"49\",\"omdbapi\"\n\"49\",\"android-pdf-api\"\n\"49\",\"acumos\"\n\"49\",\"angular-chosen\"\n\"49\",\"lcds\"\n\"49\",\"angular-date-format\"\n\"49\",\"getasync\"\n\"49\",\"activeweb\"\n\"49\",\"elasticsearch-template\"\n\"49\",\"account-linking\"\n\"49\",\"cuckoo\"\n\"49\",\"dulwich\"\n\"49\",\"ldf\"\n\"49\",\"google-dataflow\"\n\"49\",\"mplab-c18\"\n\"49\",\"ipfw\"\n\"49\",\"acts-as-paranoid\"\n\"49\",\"color-theory\"\n\"49\",\"activity-monitor\"\n\"49\",\"qcc\"\n\"49\",\"qchartview\"\n\"49\",\"sphero\"\n\"49\",\"aceoledb\"\n\"49\",\"towerjs\"\n\"49\",\"webcenter-sites\"\n\"49\",\"pandas-ta\"\n\"49\",\"sortables\"\n\"49\",\"google-indexing-api\"\n\"49\",\"stdmutex\"\n\"49\",\"googleio\"\n\"49\",\"mb-convert-encoding\"\n\"49\",\"topography\"\n\"49\",\"darcs\"\n\"49\",\"maxreceivedmessagesize\"\n\"49\",\"bb-messenger\"\n\"49\",\"screwturn\"\n\"49\",\"toolstripitem\"\n\"49\",\"tfs-reports\"\n\"49\",\"flutter-material\"\n\"49\",\"pppd\"\n\"49\",\"powergui\"\n\"49\",\"ms-mpi\"\n\"49\",\"powerpacks\"\n\"49\",\"shazam\"\n\"49\",\"cvs2git\"\n\"49\",\"msi-patch\"\n\"49\",\"stuff\"\n\"49\",\"soundtouch\"\n\"49\",\"zeebe\"\n\"49\",\"stylegan\"\n\"49\",\"md-chip\"\n\"49\",\"linq-method-syntax\"\n\"49\",\"heremaps-ios-sdk\"\n\"49\",\"search-tree\"\n\"49\",\"liferay-7.1\"\n\"49\",\"zorba\"\n\"49\",\"user-generated-content\"\n\"49\",\"partial-index\"\n\"48\",\"jekyll-paginator\"\n\"48\",\"git-apply\"\n\"48\",\"relative-addressing\"\n\"48\",\"srid\"\n\"48\",\"reprojection-error\"\n\"48\",\"llvm-codegen\"\n\"48\",\"multiple-accounts\"\n\"48\",\"insert-id\"\n\"48\",\"pheanstalk\"\n\"48\",\"ggpattern\"\n\"48\",\"fluidsynth\"\n\"48\",\"griddle\"\n\"48\",\"cloudboost\"\n\"48\",\"react-native-modules\"\n\"48\",\"privacy-manifest\"\n\"48\",\"react-native-svg-charts\"\n\"48\",\"mat-pagination\"\n\"48\",\"figma-api\"\n\"48\",\"greendroid\"\n\"48\",\"procmon\"\n\"48\",\"eclipse-rcptt\"\n\"48\",\"react-popper\"\n\"48\",\"dcount\"\n\"48\",\"printqueue\"\n\"48\",\"echo-cancellation\"\n\"48\",\"db-schema\"\n\"48\",\"vue2leaflet\"\n\"48\",\"remote-host\"\n\"48\",\"client-side-rendering\"\n\"48\",\"flexpaper\"\n\"48\",\"jboss-seam\"\n\"48\",\"weex\"\n\"48\",\"freetexttable\"\n\"48\",\"swiftsoup\"\n\"48\",\"contenttemplate\"\n\"48\",\"rust-tonic\"\n\"48\",\"fileprovider-extension\"\n\"48\",\"xlform\"\n\"48\",\"cclabelttf\"\n\"48\",\"filtered-index\"\n\"48\",\"symlink-traversal\"\n\"48\",\"rulers\"\n\"48\",\"softkeys\"\n\"48\",\"xilinx-edk\"\n\"48\",\"sxs\"\n\"48\",\"xpath-3.0\"\n\"48\",\"binary-semaphore\"\n\"48\",\"configsection\"\n\"48\",\"import-libraries\"\n\"48\",\"fubumvc\"\n\"48\",\"jst\"\n\"48\",\"find-package\"\n\"48\",\"labeled-statements\"\n\"48\",\"jsignature\"\n\"48\",\"administrate\"\n\"48\",\"add-type\"\n\"48\",\"adobe-cirrus\"\n\"48\",\"kubernetespodoperator\"\n\"48\",\"oxid\"\n\"48\",\"playframework-json\"\n\"48\",\"apex-trigger\"\n\"48\",\"languageservice\"\n\"48\",\"cidetector\"\n\"48\",\"g++-4.7\"\n\"48\",\"docker-cli\"\n\"48\",\"wallaby.js\"\n\"48\",\"pybuilder\"\n\"48\",\"optimus\"\n\"48\",\"wincc\"\n\"48\",\"jovo-framework\"\n\"48\",\"validation-application-bl\"\n\"48\",\"aikau\"\n\"48\",\"ora-00911\"\n\"48\",\"django-rest-knox\"\n\"48\",\"database-integrity\"\n\"48\",\"kimono\"\n\"48\",\"aws-reserved-instances\"\n\"48\",\"database-theory\"\n\"48\",\"updatecommand\"\n\"48\",\"jointplot\"\n\"48\",\"django-pyodbc-azure\"\n\"48\",\"up-button\"\n\"48\",\"vb5\"\n\"48\",\"windows-hello\"\n\"48\",\"alacritty\"\n\"48\",\"rosbag\"\n\"48\",\"data-lakehouse\"\n\"48\",\"react-thunk\"\n\"48\",\"cakephp-3.3\"\n\"48\",\"name-conflict\"\n\"48\",\"aws-pipeline\"\n\"48\",\"react-table-v6\"\n\"48\",\"fast-math\"\n\"48\",\"sharepointfoundation2010\"\n\"48\",\"micronaut-aws\"\n\"48\",\"rocksdb-java\"\n\"48\",\"onpaste\"\n\"48\",\"boost-logging\"\n\"48\",\"pdfparser\"\n\"48\",\"nsstackview\"\n\"48\",\"cppcms\"\n\"48\",\"appv\"\n\"48\",\"sim800l\"\n\"48\",\"azure-cosmosdb-emulator\"\n\"48\",\"tsyringe\"\n\"48\",\"blynk\"\n\"48\",\"oom\"\n\"48\",\"grunt-browserify\"\n\"48\",\"monday.com\"\n\"48\",\"nsparagraphstyle\"\n\"48\",\"tyk\"\n\"48\",\"mod-rails\"\n\"48\",\"openfst\"\n\"48\",\"approval-tests\"\n\"48\",\"modx-templates\"\n\"48\",\"jquery-ias\"\n\"48\",\"jquery-ui-menu\"\n\"48\",\"application-layer\"\n\"48\",\"nsdatecomponentsformatter\"\n\"48\",\"sas-dis\"\n\"48\",\"jxpath\"\n\"48\",\"hosted-app\"\n\"48\",\"spring-cloud-security\"\n\"48\",\"azure-entra-id\"\n\"48\",\"cosu\"\n\"48\",\"bootstrap-form-helper\"\n\"48\",\"externalizable\"\n\"48\",\"external-display\"\n\"48\",\"libunwind\"\n\"48\",\"oslc\"\n\"48\",\"oslog\"\n\"48\",\"lumen-5.4\"\n\"48\",\"knpmenu\"\n\"48\",\"klarna\"\n\"48\",\"google-api-webmasters\"\n\"48\",\"videodisplay\"\n\"48\",\"rcov\"\n\"48\",\"libnotify\"\n\"48\",\"kotlin-gradle-plugin\"\n\"48\",\"facebook-checkins\"\n\"48\",\"table-calendar\"\n\"48\",\"lxml.objectify\"\n\"48\",\"objective-c-protocol\"\n\"48\",\"rhino-mocks-3.5\"\n\"48\",\"libxml-js\"\n\"48\",\"sysprep\"\n\"48\",\"view-components\"\n\"48\",\"pygresql\"\n\"48\",\"pymodbus3\"\n\"48\",\"system-configuration\"\n\"48\",\"kotlinx\"\n\"48\",\"viewstack\"\n\"48\",\"pyldavis\"\n\"48\",\"osx-yosemite-beta\"\n\"48\",\"newspaper3k\"\n\"48\",\"go-micro\"\n\"48\",\"netplan\"\n\"48\",\"uft-api\"\n\"48\",\"outer-classes\"\n\"48\",\"ocaml-lwt\"\n\"48\",\"systemevent\"\n\"48\",\"pyscard\"\n\"48\",\"f#-giraffe\"\n\"48\",\"qwebenginepage\"\n\"48\",\"gae-quotas\"\n\"48\",\"redux-saga-test-plan\"\n\"48\",\"rack-cors\"\n\"48\",\"node-windows\"\n\"48\",\"open-nfc\"\n\"48\",\"uinavigationbarappearance\"\n\"48\",\"dtplyr\"\n\"48\",\"redux-promise\"\n\"48\",\"hmacsha256\"\n\"48\",\"tivoli-identity-manager\"\n\"48\",\"openprocess\"\n\"48\",\"numeric-input\"\n\"48\",\"mobile-ffmpeg\"\n\"48\",\"rackup\"\n\"48\",\"nook\"\n\"48\",\"dotnetnuke-settings\"\n\"48\",\"non-equi-join\"\n\"48\",\"time-management\"\n\"48\",\"ismouseover\"\n\"48\",\"tbb-flow-graph\"\n\"48\",\"ml.net-model-builder\"\n\"48\",\"tipfy\"\n\"48\",\"qwizard\"\n\"48\",\"expo-image-picker\"\n\"48\",\"npmjs\"\n\"48\",\"tinylog\"\n\"48\",\"rails-sprockets\"\n\"48\",\"haskell-criterion\"\n\"48\",\"drupal-contextual-filters\"\n\"48\",\"drf-nested-routers\"\n\"48\",\"numeral.js\"\n\"48\",\"isparta\"\n\"48\",\"double-submit-prevention\"\n\"48\",\"rails-ujs\"\n\"48\",\"cub\"\n\"48\",\"comboboxmodel\"\n\"48\",\"nlg\"\n\"48\",\"huxtable\"\n\"48\",\"genericdao\"\n\"48\",\"reroute\"\n\"48\",\"lua-5.3\"\n\"48\",\"common-dialog\"\n\"48\",\"locf\"\n\"48\",\"lcg\"\n\"48\",\"splfileobject\"\n\"48\",\"lsh\"\n\"48\",\"mercadopago\"\n\"48\",\"google-container-builder\"\n\"48\",\"node.js-got\"\n\"48\",\"geodesic-sphere\"\n\"48\",\"eventreceiver\"\n\"48\",\"http-parameters\"\n\"48\",\"ios11.2\"\n\"48\",\"cfs\"\n\"48\",\"httpforbiddenhandler\"\n\"48\",\"nodebb\"\n\"48\",\"reactive-cocoa-4\"\n\"48\",\"lattice-diamond\"\n\"48\",\"ios10-today-widget\"\n\"48\",\"react-infinite-scroll-component\"\n\"48\",\"durability\"\n\"48\",\"resize-crop\"\n\"48\",\"io-uring\"\n\"48\",\"logstash-filter\"\n\"48\",\"node-oidc-provider\"\n\"48\",\"react-codemirror\"\n\"48\",\"android-parsequeryadapter\"\n\"48\",\"metaphone\"\n\"48\",\"login-system\"\n\"48\",\"google-cloud-automl-nl\"\n\"48\",\"logic-error\"\n\"48\",\"excel-2021\"\n\"48\",\"erwin\"\n\"48\",\"chained-assignment\"\n\"48\",\"perf4j\"\n\"48\",\"image-load\"\n\"48\",\"spark-checkpoint\"\n\"48\",\"maven-versions-plugin\"\n\"48\",\"webpack-bundle-analyzer\"\n\"48\",\"maxifs\"\n\"48\",\"zerorpc\"\n\"48\",\"msmtp\"\n\"48\",\"google-reviews\"\n\"48\",\"heap-profiling\"\n\"48\",\"autopy\"\n\"48\",\"alternate-data-stream\"\n\"48\",\"ushort\"\n\"48\",\"flutter-module\"\n\"48\",\"beforeupdate\"\n\"48\",\"theorem\"\n\"48\",\"zohocatalyst\"\n\"48\",\"webinvoke\"\n\"48\",\"scrumboard\"\n\"48\",\"qtranslate-x\"\n\"48\",\"cycle-detection\"\n\"48\",\"suiteql\"\n\"48\",\"asciiencoding\"\n\"48\",\"zoomcharts\"\n\"48\",\"flutter-slider\"\n\"48\",\"liferay-hook\"\n\"48\",\"qtwayland\"\n\"48\",\"starmap\"\n\"48\",\"flutter-web-browser\"\n\"47\",\"standard-layout\"\n\"47\",\"travis-ci-cli\"\n\"47\",\"github-pages-deploy-action\"\n\"47\",\"trove4j\"\n\"47\",\"flutter-file\"\n\"47\",\"proc-object\"\n\"47\",\"anonymity\"\n\"47\",\"remote-actors\"\n\"47\",\"graph-explorer\"\n\"47\",\"wechat-miniprogram\"\n\"47\",\"relative-date\"\n\"47\",\"fbsdksharekit\"\n\"47\",\"claudiajs\"\n\"47\",\"slowmotion\"\n\"47\",\"webviewchromium\"\n\"47\",\"tensorflow-data-validation\"\n\"47\",\"graphedit\"\n\"47\",\"tritonserver\"\n\"47\",\"eclipse-formatter\"\n\"47\",\"xyz\"\n\"47\",\"mutablestateof\"\n\"47\",\"basic-msi\"\n\"47\",\"skeletal-mesh\"\n\"47\",\"jboss-4.0.x\"\n\"47\",\"grouping-sets\"\n\"47\",\"mvcgrid\"\n\"47\",\"react-native-dropdown-picker\"\n\"47\",\"vue-render-function\"\n\"47\",\"jcmd\"\n\"47\",\"skvideonode\"\n\"47\",\"clplacemark\"\n\"47\",\"jcabi\"\n\"47\",\"bacula\"\n\"47\",\"xml-editor\"\n\"47\",\"selectbooleancheckbox\"\n\"47\",\"apns-sharp\"\n\"47\",\"physicsjs\"\n\"47\",\"runit\"\n\"47\",\"xna-3.0\"\n\"47\",\"ngx-chips\"\n\"47\",\"rust-bindgen\"\n\"47\",\"rust-ink\"\n\"47\",\"afincrementalstore\"\n\"47\",\"pimcore-v5\"\n\"47\",\"cassandra-python-driver\"\n\"47\",\"apfs\"\n\"47\",\"dismissviewcontroller\"\n\"47\",\"fsyacc\"\n\"47\",\"dismissible\"\n\"47\",\"incron\"\n\"47\",\"ujson\"\n\"47\",\"distriqt\"\n\"47\",\"phrases\"\n\"47\",\"managedobjectcontext\"\n\"47\",\"flask-assets\"\n\"47\",\"semantic-search\"\n\"47\",\"software-collections\"\n\"47\",\"mapsui\"\n\"47\",\"chromium-os\"\n\"47\",\"fuchsia\"\n\"47\",\"safety-critical\"\n\"47\",\"pagertabstrip\"\n\"47\",\"python-bytearray\"\n\"47\",\"method-group\"\n\"47\",\"django-tenants\"\n\"47\",\"angular-localize\"\n\"47\",\"n\"\n\"47\",\"documentclient\"\n\"47\",\"waypoint\"\n\"47\",\"pyasn1\"\n\"47\",\"capicom\"\n\"47\",\"oracle-cdc\"\n\"47\",\"django-parler\"\n\"47\",\"windows-machine-learning\"\n\"47\",\"na.rm\"\n\"47\",\"wavemaker\"\n\"47\",\"django-datatable\"\n\"47\",\"jnotify\"\n\"47\",\"grandchild\"\n\"47\",\"ice-cube\"\n\"47\",\"airbyte\"\n\"47\",\"server-core\"\n\"47\",\"walrus-operator\"\n\"47\",\"docker-push\"\n\"47\",\"public-key-pinning\"\n\"47\",\"dllmain\"\n\"47\",\"cakephp-routing\"\n\"47\",\"aws-app-config\"\n\"47\",\"grails3.2.0\"\n\"47\",\"jobrunr\"\n\"47\",\"cricheditctrl\"\n\"47\",\"wikimedia-dumps\"\n\"47\",\"robotframework-sshlibrary\"\n\"47\",\"aws-private-link\"\n\"47\",\"grails-searchable\"\n\"47\",\"grunt-contrib-jshint\"\n\"47\",\"grunt-contrib-jasmine\"\n\"47\",\"neo4j-python-driver\"\n\"47\",\"twilio-node\"\n\"47\",\"simgrid\"\n\"47\",\"bonferroni\"\n\"47\",\"sametime\"\n\"47\",\"oneplusone\"\n\"47\",\"bootstrap-dialog\"\n\"47\",\"saxon-c\"\n\"47\",\"epg\"\n\"47\",\"enyim\"\n\"47\",\"spring-oxm\"\n\"47\",\"opcodes\"\n\"47\",\"nsscroller\"\n\"47\",\"iommu\"\n\"47\",\"kaitai-struct\"\n\"47\",\"navigatetourl\"\n\"47\",\"natsort\"\n\"47\",\"online-store\"\n\"47\",\"karel\"\n\"47\",\"btrace\"\n\"47\",\"oai\"\n\"47\",\"gocardless\"\n\"47\",\"overlayitem\"\n\"47\",\"new-style-class\"\n\"47\",\"ob-get-contents\"\n\"47\",\"razorsql\"\n\"47\",\"rapidclipse\"\n\"47\",\"object.observe\"\n\"47\",\"abrecord\"\n\"47\",\"set-cover\"\n\"47\",\"pypiserver\"\n\"47\",\"setforegroundwindow\"\n\"47\",\"acc\"\n\"47\",\"google-bigquery-storage-api\"\n\"47\",\"libusbdotnet\"\n\"47\",\"ezaudio\"\n\"47\",\"osm2pgsql\"\n\"47\",\"ride\"\n\"47\",\"javascript-function-declaration\"\n\"47\",\"golangci-lint\"\n\"47\",\"virtual-printer\"\n\"47\",\"libgpiod\"\n\"47\",\"codesynthesis\"\n\"47\",\"amd-rocm\"\n\"47\",\"brk\"\n\"47\",\"reverse-lookup\"\n\"47\",\"learnr\"\n\"47\",\"rawimage\"\n\"47\",\"code-map\"\n\"47\",\"2-legged\"\n\"47\",\"uc-browser\"\n\"47\",\"accent-sensitive\"\n\"47\",\"game-boy-advance\"\n\"47\",\"tobjectlist\"\n\"47\",\"registrar\"\n\"47\",\"associate\"\n\"47\",\"rails-4-2-1\"\n\"47\",\"tatsu\"\n\"47\",\"halt\"\n\"47\",\"expo-web\"\n\"47\",\"iterator-traits\"\n\"47\",\"timefield\"\n\"47\",\"ml-studio\"\n\"47\",\"uic\"\n\"47\",\"opera-mobile\"\n\"47\",\"xcode14.3\"\n\"47\",\"spring-validation\"\n\"47\",\"takesscreenshot\"\n\"47\",\"controlfile\"\n\"47\",\"drop-database\"\n\"47\",\"exceptionmapper\"\n\"47\",\"xcode10.3\"\n\"47\",\"table-rename\"\n\"47\",\"exception-specification\"\n\"47\",\"dsbulk\"\n\"47\",\"continuewith\"\n\"47\",\"haneke\"\n\"47\",\"isolate\"\n\"47\",\"uint8list\"\n\"47\",\"wxnotebook\"\n\"47\",\"openstack-cinder\"\n\"47\",\"contextily\"\n\"47\",\"spritefont\"\n\"47\",\"scoped-model\"\n\"47\",\"tapjoy\"\n\"47\",\"scanline\"\n\"47\",\"mobile-data\"\n\"47\",\"asp.net-core-configuration\"\n\"47\",\"androidimageslider\"\n\"47\",\"isession\"\n\"47\",\"communicationexception\"\n\"47\",\"onappear\"\n\"47\",\"google-cloud-repository\"\n\"47\",\"common-test\"\n\"47\",\"prolog-assert\"\n\"47\",\"requestfiltering\"\n\"47\",\"column-types\"\n\"47\",\"stream-graph\"\n\"47\",\"laravel-package\"\n\"47\",\"tomee-8\"\n\"47\",\"morelinq\"\n\"47\",\"spek\"\n\"47\",\"testcasesource\"\n\"47\",\"testem\"\n\"47\",\"membershipreboot\"\n\"47\",\"humanize\"\n\"47\",\"cfile\"\n\"47\",\"irepository\"\n\"47\",\"logo-lang\"\n\"47\",\"android-mms\"\n\"47\",\"resource-file\"\n\"47\",\"ctakes\"\n\"47\",\"launch-configuration\"\n\"47\",\"text-analytics-api\"\n\"47\",\"leap-second\"\n\"47\",\"stripchart\"\n\"47\",\"elasticsearch-rest-client\"\n\"47\",\"ninjaframework\"\n\"47\",\"protocol-oriented\"\n\"47\",\"evict\"\n\"47\",\"http-equiv\"\n\"47\",\"elasticsearch-model\"\n\"47\",\"permissionerror\"\n\"47\",\"resharper-5.0\"\n\"47\",\"prosody-im\"\n\"47\",\"moxios\"\n\"47\",\"qjsonobject\"\n\"47\",\"emr-serverless\"\n\"47\",\"zend-filter\"\n\"47\",\"dapper-contrib\"\n\"47\",\"pandas-udf\"\n\"47\",\"utm-tracking\"\n\"47\",\"emojione\"\n\"47\",\"panelgrid\"\n\"47\",\"panel-pyviz\"\n\"47\",\"urlrewriter\"\n\"47\",\"gnuplot-iostream\"\n\"47\",\"bigsql\"\n\"47\",\"soomla\"\n\"47\",\"tiddlywiki5\"\n\"47\",\"heist\"\n\"47\",\"tpanel\"\n\"47\",\"quazip\"\n\"47\",\"bijection\"\n\"47\",\"hellosign-api\"\n\"47\",\"sublimetext-snippet\"\n\"47\",\"bilstm\"\n\"47\",\"thirdweb\"\n\"47\",\"prefixes\"\n\"47\",\"scrollviewreader\"\n\"47\",\"seaborn-objects\"\n\"47\",\"torii\"\n\"47\",\"flutter-release\"\n\"47\",\"subparsers\"\n\"47\",\"emacs-prelude\"\n\"47\",\"asa\"\n\"47\",\"useselector\"\n\"47\",\"style-transfer\"\n\"47\",\"yui2\"\n\"47\",\"tfs-2013\"\n\"47\",\"compiler-explorer\"\n\"47\",\"sdl-1.2\"\n\"47\",\"haskell-prelude\"\n\"47\",\"conductor\"\n\"47\",\"static-order-fiasco\"\n\"47\",\"end-of-life\"\n\"46\",\"intellij-scala\"\n\"46\",\"xslkey\"\n\"46\",\"lisp-macros\"\n\"46\",\"listbuffer\"\n\"46\",\"sql-server-mars\"\n\"46\",\"flex-mx\"\n\"46\",\"git-ls-files\"\n\"46\",\"gf\"\n\"46\",\"yii-relations\"\n\"46\",\"weld-se\"\n\"46\",\"react-router-relay\"\n\"46\",\"vue-ssr\"\n\"46\",\"flutter-apk\"\n\"46\",\"teaspoon\"\n\"46\",\"flowdocumentreader\"\n\"46\",\"stackless\"\n\"46\",\"debhelper\"\n\"46\",\"sitemappath\"\n\"46\",\"yahoo-maps\"\n\"46\",\"processing-ide\"\n\"46\",\"jboss-cache\"\n\"46\",\"fluorinefx\"\n\"46\",\"default-browser\"\n\"46\",\"materialized-path-pattern\"\n\"46\",\"barcode4j\"\n\"46\",\"private-repository\"\n\"46\",\"cloudevents\"\n\"46\",\"sslpinning\"\n\"46\",\"translators\"\n\"46\",\"temporal-difference\"\n\"46\",\"fluent-ribbon\"\n\"46\",\"chrome-ios\"\n\"46\",\"adornerlayer\"\n\"46\",\"appdomainsetup\"\n\"46\",\"apache-minifi\"\n\"46\",\"display-manager\"\n\"46\",\"python-applymap\"\n\"46\",\"cassandra-stress\"\n\"46\",\"jslink\"\n\"46\",\"apache-metamodel\"\n\"46\",\"distilbert\"\n\"46\",\"rust-no-std\"\n\"46\",\"mapbox-studio\"\n\"46\",\"ngrx-data\"\n\"46\",\"json-framework\"\n\"46\",\"unattended-processing\"\n\"46\",\"addr2line\"\n\"46\",\"cassandra-2.2\"\n\"46\",\"laravel-broadcast\"\n\"46\",\"cc-mode\"\n\"46\",\"content-delivery-network\"\n\"46\",\"nextjs-rewrites\"\n\"46\",\"undelete\"\n\"46\",\"swxmlhash\"\n\"46\",\"socketcluster\"\n\"46\",\"freetextbox\"\n\"46\",\"console-input\"\n\"46\",\"xpath-3.1\"\n\"46\",\"fiscal\"\n\"46\",\"manifest-merging\"\n\"46\",\"flatiron.js\"\n\"46\",\"unittest++\"\n\"46\",\"pair-programming\"\n\"46\",\"implicitwait\"\n\"46\",\"xirr\"\n\"46\",\"pimple\"\n\"46\",\"capped-collections\"\n\"46\",\"css-cascade\"\n\"46\",\"aws-ec2-instance-connect\"\n\"46\",\"urhosharp\"\n\"46\",\"wasabi\"\n\"46\",\"waitformultipleobjects\"\n\"46\",\"angular-ui-sortable\"\n\"46\",\"aircrack-ng\"\n\"46\",\"cakephp-3.6\"\n\"46\",\"aws-direct-connect\"\n\"46\",\"icu4j\"\n\"46\",\"ruby-test\"\n\"46\",\"sharefile\"\n\"46\",\"veeam\"\n\"46\",\"kiali\"\n\"46\",\"rsocket-java\"\n\"46\",\"vector-multiplication\"\n\"46\",\"unpkg\"\n\"46\",\"aws-lambda-containers\"\n\"46\",\"server-communication\"\n\"46\",\"real-time-multiplayer\"\n\"46\",\"google-voice-actions\"\n\"46\",\"rolap\"\n\"46\",\"server-monitoring\"\n\"46\",\"rocky-os\"\n\"46\",\"keycloak-spi\"\n\"46\",\"aws-control-tower\"\n\"46\",\"sharetribe\"\n\"46\",\"vcftools\"\n\"46\",\"avspeechutterance\"\n\"46\",\"aws-access-policy\"\n\"46\",\"servlet-3.1\"\n\"46\",\"publishing-site\"\n\"46\",\"optapy\"\n\"46\",\"mysql-error-1052\"\n\"46\",\"pulumi-python\"\n\"46\",\"facebook-ui\"\n\"46\",\"icloneable\"\n\"46\",\"joomla3.5\"\n\"46\",\"sine-wave\"\n\"46\",\"nantcontrib\"\n\"46\",\"nested-select\"\n\"46\",\"jviewport\"\n\"46\",\"dynamic-method\"\n\"46\",\"tycho-surefire-plugin\"\n\"46\",\"boost-statechart\"\n\"46\",\"botconnector\"\n\"46\",\"polymorphic-relationship\"\n\"46\",\"keepalived\"\n\"46\",\"twiki\"\n\"46\",\"app-thinning\"\n\"46\",\"mongoose-models\"\n\"46\",\"jwysiwyg\"\n\"46\",\"ioref\"\n\"46\",\"eot\"\n\"46\",\"applocker\"\n\"46\",\"boost-foreach\"\n\"46\",\"monetdblite\"\n\"46\",\"mongoalchemy\"\n\"46\",\"julia-dataframe\"\n\"46\",\"karate-call-single\"\n\"46\",\"popupmenubutton\"\n\"46\",\"invokemember\"\n\"46\",\"ioio\"\n\"46\",\"nspopupbuttoncell\"\n\"46\",\"jubula\"\n\"46\",\"writexl\"\n\"46\",\"delimited-continuations\"\n\"46\",\"jtreetable\"\n\"46\",\"htmleditorextender\"\n\"46\",\"entity-bean\"\n\"46\",\"inno-download-plugin\"\n\"46\",\"pebble\"\n\"46\",\"nrql\"\n\"46\",\"bootstrap-wysiwyg\"\n\"46\",\"nelmio-alice\"\n\"46\",\"app-route\"\n\"46\",\"crdt\"\n\"46\",\"wp-mail\"\n\"46\",\"http.server\"\n\"46\",\"pcre2\"\n\"46\",\"kaios\"\n\"46\",\"spring-data-hadoop\"\n\"46\",\"moinmoin\"\n\"46\",\"ospf\"\n\"46\",\"netty4\"\n\"46\",\"microsoft-query\"\n\"46\",\"missing-symbols\"\n\"46\",\"microsoft-store\"\n\"46\",\"ubuntu-21.04\"\n\"46\",\"typewriter\"\n\"46\",\"lyft-api\"\n\"46\",\"ucos\"\n\"46\",\"fragment-oncreateview\"\n\"46\",\"google-client-login\"\n\"46\",\"netbeans-9\"\n\"46\",\"oci-java-sdk\"\n\"46\",\"orbitcontrols\"\n\"46\",\".net-security\"\n\"46\",\"fourcc\"\n\"46\",\"audioclip\"\n\"46\",\"netlify-cli\"\n\"46\",\"godot-shader-language\"\n\"46\",\"synclock\"\n\"46\",\"shodan\"\n\"46\",\"about-box\"\n\"46\",\"razor-declarative-helpers\"\n\"46\",\"amp-bind\"\n\"46\",\"extrinsic-parameters\"\n\"46\",\"ancova\"\n\"46\",\"withings\"\n\"46\",\"brushes\"\n\"46\",\"extended-precision\"\n\"46\",\"winghci\"\n\"46\",\"winginx\"\n\"46\",\"javafx-9\"\n\"46\",\"randomized-algorithm\"\n\"46\",\"facebook-app-settings\"\n\"46\",\"virtual-ip-address\"\n\"46\",\"difference-lists\"\n\"46\",\"gcc-extensions\"\n\"46\",\"xaml-islands\"\n\"46\",\"nvapi\"\n\"46\",\"dot.js\"\n\"46\",\"gcc9\"\n\"46\",\"hashicorp-packer\"\n\"46\",\"xamarin-forms-4\"\n\"46\",\"uibarbuttonitemstyle\"\n\"46\",\"galen\"\n\"46\",\"gatein\"\n\"46\",\"quil\"\n\"46\",\"exchange-management-shell\"\n\"46\",\"continuous-testing\"\n\"46\",\"xampp-vm\"\n\"46\",\"drawertoggle\"\n\"46\",\"asqueryable\"\n\"46\",\"nuxtjs2\"\n\"46\",\"rack-test\"\n\"46\",\"sqlbrite\"\n\"46\",\"jam\"\n\"46\",\"uimodaltransitionstyle\"\n\"46\",\"control-language\"\n\"46\",\"redhat-bpm\"\n\"46\",\"scalar-subquery\"\n\"46\",\"hammock\"\n\"46\",\"aspen\"\n\"46\",\"vertex-ai-pipeline\"\n\"46\",\"radix-tree\"\n\"46\",\"dotspatial\"\n\"46\",\"notification-area\"\n\"46\",\"uidocumentinteractioncontroller\"\n\"46\",\"xcode7-beta2\"\n\"46\",\"nose2\"\n\"46\",\"sql.js\"\n\"46\",\"hibernate-filters\"\n\"46\",\"dr-memory\"\n\"46\",\"qxmlstreamreader\"\n\"46\",\"gambling\"\n\"46\",\"rails-upgrade\"\n\"46\",\"redirect-uri\"\n\"46\",\"spectra\"\n\"46\",\"react-flatlist\"\n\"46\",\"spotlight-dbpedia\"\n\"46\",\"react-font-awesome\"\n\"46\",\"actualheight\"\n\"46\",\"google-cloud-identity-aware-proxy\"\n\"46\",\"duplex-channel\"\n\"46\",\"resize-observer\"\n\"46\",\"speculative-execution\"\n\"46\",\"low-level-code\"\n\"46\",\"http-range\"\n\"46\",\"lpeg\"\n\"46\",\"cufflinks\"\n\"46\",\"react-native-bridge\"\n\"46\",\"qodbc\"\n\"46\",\"android-ktx\"\n\"46\",\"qopenglwidget\"\n\"46\",\"android-reboot\"\n\"46\",\"actionform\"\n\"46\",\"large-title\"\n\"46\",\"cuda-driver\"\n\"46\",\"cuda-context\"\n\"46\",\"melpa\"\n\"46\",\"act\"\n\"46\",\"requirejs-text\"\n\"46\",\"memberof\"\n\"46\",\"hwpf\"\n\"46\",\"cssresource\"\n\"46\",\"http-status-code-303\"\n\"46\",\"geospark\"\n\"46\",\"cfpdf\"\n\"46\",\"mdpi\"\n\"46\",\"automationelement\"\n\"46\",\"amazon-cognito-identity-js\"\n\"46\",\"tpot\"\n\"46\",\"passive-view\"\n\"46\",\"zend-rest\"\n\"46\",\"preg-grep\"\n\"46\",\"scsf\"\n\"46\",\"mcisendstring\"\n\"46\",\"autoreconf\"\n\"46\",\"parameter-sniffing\"\n\"46\",\"pre-allocation\"\n\"46\",\"automap\"\n\"46\",\"beam-sql\"\n\"46\",\"sosex\"\n\"46\",\"threadcontext\"\n\"46\",\"tooltwist\"\n\"46\",\"web-feature-service\"\n\"46\",\"fomantic-ui\"\n\"46\",\"iformatprovider\"\n\"46\",\"linear-optimization\"\n\"46\",\"lidgren\"\n\"46\",\"google-roads-api\"\n\"46\",\"ihttpclientfactory\"\n\"46\",\"google-inbox\"\n\"46\",\"toplink-essentials\"\n\"46\",\"gluu\"\n\"46\",\"hbase-shell\"\n\"46\",\"starlark\"\n\"46\",\"stateless-session\"\n\"46\",\"glscene\"\n\"46\",\"maven-reactor\"\n\"46\",\"flyspell\"\n\"45\",\"git-describe\"\n\"45\",\"graphdiff\"\n\"45\",\"jcodemodel\"\n\"45\",\"tree-search\"\n\"45\",\"yii-routing\"\n\"45\",\"ggh4x\"\n\"45\",\"cmdb\"\n\"45\",\"squashfs\"\n\"45\",\"wheelnav.js\"\n\"45\",\"ant-media-server-sdk\"\n\"45\",\"stackage\"\n\"45\",\"click-tracking\"\n\"45\",\"eazfuscator\"\n\"45\",\"feature-scaling\"\n\"45\",\"insert-query\"\n\"45\",\"babel-core\"\n\"45\",\"jet-engine\"\n\"45\",\"sktime\"\n\"45\",\"jets3t\"\n\"45\",\"llc\"\n\"45\",\"relaunch\"\n\"45\",\"progmem\"\n\"45\",\"multimodal\"\n\"45\",\"vte\"\n\"45\",\"page-replacement\"\n\"45\",\"binding-context\"\n\"45\",\"findoneandupdate\"\n\"45\",\"xmobar\"\n\"45\",\"runtime-environment\"\n\"45\",\"cdb\"\n\"45\",\"kyotocabinet\"\n\"45\",\"smart-quotes\"\n\"45\",\"unittest2\"\n\"45\",\"python-binance\"\n\"45\",\"xop\"\n\"45\",\"connectiq\"\n\"45\",\"play-framework-2.7\"\n\"45\",\"fullcalendar-2\"\n\"45\",\"conll\"\n\"45\",\"ultimate-member\"\n\"45\",\"displayname-attribute\"\n\"45\",\"python-cmd\"\n\"45\",\"firefox-android\"\n\"45\",\"constantfolding\"\n\"45\",\"kube-scheduler\"\n\"45\",\"jtapi\"\n\"45\",\"oxm\"\n\"45\",\"flattr\"\n\"45\",\"apache-spark-1.5\"\n\"45\",\"date-histogram\"\n\"45\",\"xml-encoding\"\n\"45\",\"pitch-detection\"\n\"45\",\"xmlelement\"\n\"45\",\"directory-tree\"\n\"45\",\"django-sphinx\"\n\"45\",\"dnsimple\"\n\"45\",\"django-custom-manager\"\n\"45\",\"watson-knowledge-studio\"\n\"45\",\"createremotethread\"\n\"45\",\"docker-logs\"\n\"45\",\"cryptanalysis\"\n\"45\",\"csound\"\n\"45\",\"server-load\"\n\"45\",\"crosswalk-project\"\n\"45\",\"oraclecommand\"\n\"45\",\"w3wp.exe\"\n\"45\",\"vector-auto-regression\"\n\"45\",\"crf++\"\n\"45\",\"sharepoint-addin\"\n\"45\",\"unobtrusive\"\n\"45\",\"kfp\"\n\"45\",\"vctrs\"\n\"45\",\"aws-msk-connect\"\n\"45\",\"datadetectortypes\"\n\"45\",\"go-packages\"\n\"45\",\"fall-through\"\n\"45\",\"pugxmultiuserbundle\"\n\"45\",\"gramex\"\n\"45\",\"pssnapin\"\n\"45\",\"jotform\"\n\"45\",\"pwabuilder\"\n\"45\",\"mi\"\n\"45\",\"wikibase\"\n\"45\",\"datagridviewcellstyle\"\n\"45\",\"anjuta\"\n\"45\",\"agda-mode\"\n\"45\",\"myo\"\n\"45\",\"dataguard\"\n\"45\",\"rsvg\"\n\"45\",\"facebook-sdk-3.14.x\"\n\"45\",\"facebook-python-business-sdk\"\n\"45\",\"react-tooltip\"\n\"45\",\"failed-to-connect\"\n\"45\",\"rebalancing\"\n\"45\",\"input-buffer\"\n\"45\",\"application-blocks\"\n\"45\",\"kamon\"\n\"45\",\"kaboom\"\n\"45\",\"one-click-web-publishing\"\n\"45\",\"kde4\"\n\"45\",\"mongo-go-driver\"\n\"45\",\"grunt-contrib-imagemin\"\n\"45\",\"tweedie\"\n\"45\",\"nested-properties\"\n\"45\",\"bpython\"\n\"45\",\"azure-custom-domain\"\n\"45\",\"dependency-resolution\"\n\"45\",\"mongodb.driver\"\n\"45\",\"springdoc-openui\"\n\"45\",\"enterprise-portal\"\n\"45\",\"easy-digital-downloads\"\n\"45\",\"couchrest\"\n\"45\",\"jquery-1.8\"\n\"45\",\"azure-dsvm\"\n\"45\",\"opendap\"\n\"45\",\"dynamic-class\"\n\"45\",\"mongodb-lookup\"\n\"45\",\"intermec\"\n\"45\",\"intermediate-code\"\n\"45\",\"grunt-wiredep\"\n\"45\",\"dynamic-view\"\n\"45\",\"jquery-attributes\"\n\"45\",\"gs1-ai-syntax\"\n\"45\",\"nslayoutanchor\"\n\"45\",\"jquery-resizable\"\n\"45\",\"ingest\"\n\"45\",\"typemock-isolator\"\n\"45\",\"type-members\"\n\"45\",\"java-print\"\n\"45\",\"google-aiy\"\n\"45\",\"uia\"\n\"45\",\"ratchet-bootstrap\"\n\"45\",\"occlusion-culling\"\n\"45\",\"code-server\"\n\"45\",\"lunrjs\"\n\"45\",\"shinydashboardplus\"\n\"45\",\"systemexit\"\n\"45\",\"winexe\"\n\"45\",\"midi-instrument\"\n\"45\",\"magic-function\"\n\"45\",\"netdata\"\n\"45\",\"system-identification\"\n\"45\",\"freebasic\"\n\"45\",\"dosgi\"\n\"45\",\"extjs6.5\"\n\"45\",\"goclipse\"\n\"45\",\"oasis\"\n\"45\",\"gobblin\"\n\"45\",\"google-blogger-api\"\n\"45\",\"formborderstyle\"\n\"45\",\"missing-template\"\n\"45\",\"lync-server-2010\"\n\"45\",\"asus-xtion\"\n\"45\",\"shorthand-if\"\n\"45\",\"iterated-function\"\n\"45\",\"openhardwaremonitor\"\n\"45\",\"drupal-ctools\"\n\"45\",\"hono\"\n\"45\",\"bungeecord\"\n\"45\",\"devpi\"\n\"45\",\"railsinstaller\"\n\"45\",\"hash-code-uniqueness\"\n\"45\",\"xamarin-live-player\"\n\"45\",\"halogen\"\n\"45\",\"spweb\"\n\"45\",\"expo-auth-session\"\n\"45\",\"dgl\"\n\"45\",\"radosgw\"\n\"45\",\"gedcom\"\n\"45\",\"gwtupload\"\n\"45\",\"mkmapsnapshotter\"\n\"45\",\"gwttestcase\"\n\"45\",\"railtie\"\n\"45\",\"highpass-filter\"\n\"45\",\"android-designer\"\n\"45\",\"sqitch\"\n\"45\",\"built-value\"\n\"45\",\"gembox-document\"\n\"45\",\"xai\"\n\"45\",\"collocation\"\n\"45\",\"progressmonitor\"\n\"45\",\"resource-loading\"\n\"45\",\"iredmail\"\n\"45\",\"android-jetpack-compose-tv\"\n\"45\",\"ipmitool\"\n\"45\",\"angular-cli-v9\"\n\"45\",\"activerecord-import\"\n\"45\",\"colt\"\n\"45\",\"tomography-reconstruction\"\n\"45\",\"resolveurl\"\n\"45\",\"excel-dates\"\n\"45\",\"angular-broadcast\"\n\"45\",\"geolite2\"\n\"45\",\"terraform-provider-vsphere\"\n\"45\",\"moonlight\"\n\"45\",\"lazy-high-charts\"\n\"45\",\"google-cloud-ops-agent\"\n\"45\",\"resilience4j-retry\"\n\"45\",\"geostatistics\"\n\"45\",\"storable\"\n\"45\",\"angular2-moment\"\n\"45\",\"node-archiver\"\n\"45\",\"storekit2\"\n\"45\",\"angular2-injection\"\n\"45\",\"request.servervariables\"\n\"45\",\"stripe-payments-js\"\n\"45\",\"generative\"\n\"45\",\"character-entities\"\n\"45\",\"spoof\"\n\"45\",\"qcoreapplication\"\n\"45\",\"property-files\"\n\"45\",\"laravel-octane\"\n\"45\",\"cfsocket\"\n\"45\",\"spine\"\n\"45\",\"protobuf-csharp-port\"\n\"45\",\"zend-log\"\n\"45\",\"pgi-accelerator\"\n\"45\",\"sparrow-framework\"\n\"45\",\"ember.js-view\"\n\"45\",\"mediatorlivedata\"\n\"45\",\"aliexpress\"\n\"45\",\"ms-access-data-macro\"\n\"45\",\"google-keep\"\n\"45\",\"pre-rendering\"\n\"45\",\"google-maps-autocomplete\"\n\"45\",\"mclust\"\n\"45\",\"light-4j\"\n\"45\",\"sonarqube5.2\"\n\"45\",\"footprint\"\n\"45\",\"archilogic\"\n\"45\",\"soundfont\"\n\"45\",\"spark-operator\"\n\"45\",\"enaml\"\n\"45\",\"mediawiki-installation\"\n\"45\",\"amazon-marketplace\"\n\"45\",\"quaggajs\"\n\"45\",\"compiled-language\"\n\"45\",\"powershell-dsc\"\n\"45\",\"total-commander\"\n\"45\",\"enet\"\n\"45\",\"hfs+\"\n\"45\",\"timagelist\"\n\"45\",\"almond\"\n\"45\",\"webdrivermanager-python\"\n\"45\",\"partial-trust\"\n\"45\",\"sorttable.js\"\n\"45\",\"conceptual-model\"\n\"45\",\"usebean\"\n\"45\",\"array-unset\"\n\"45\",\"question2answer\"\n\"45\",\"pass-by-name\"\n\"45\",\"customtool\"\n\"45\",\"google-product-search\"\n\"45\",\"identitymanager\"\n\"45\",\"spark-excel\"\n\"45\",\"webget\"\n\"44\",\"vue-property-decorator\"\n\"44\",\"editablegrid\"\n\"44\",\"gridviewrow\"\n\"44\",\"tedgebrowser\"\n\"44\",\"graylog3\"\n\"44\",\"greenhopper\"\n\"44\",\"clickstream\"\n\"44\",\"truestudio\"\n\"44\",\"multiplechoicefield\"\n\"44\",\"material-you\"\n\"44\",\"stackalloc\"\n\"44\",\"ddl-trigger\"\n\"44\",\"claims-authentication\"\n\"44\",\"git-bundle\"\n\"44\",\"wdio-v5\"\n\"44\",\"yguard\"\n\"44\",\"clearcase-automation\"\n\"44\",\"react-native-text\"\n\"44\",\"react-native-share\"\n\"44\",\"econnect\"\n\"44\",\"program-slicing\"\n\"44\",\"deadbolt-2\"\n\"44\",\"deadbolt\"\n\"44\",\"react-native-pdf\"\n\"44\",\"cobol85\"\n\"44\",\"flutter-devtools\"\n\"44\",\"yesod-forms\"\n\"44\",\"ecmascript-intl\"\n\"44\",\"programdata\"\n\"44\",\"imap-tools\"\n\"44\",\"catboostregressor\"\n\"44\",\"g++4.9\"\n\"44\",\"symfony-http-client\"\n\"44\",\"switch-user\"\n\"44\",\"labelimg\"\n\"44\",\"soapexception\"\n\"44\",\"contacts-framework\"\n\"44\",\"contact-list\"\n\"44\",\"vrtk\"\n\"44\",\"xmlschemaset\"\n\"44\",\"nginx-upstreams\"\n\"44\",\"adplus\"\n\"44\",\"snowflake-stage\"\n\"44\",\"jstree-dnd\"\n\"44\",\"nhapi\"\n\"44\",\"fromhtml\"\n\"44\",\"aerospike-ce\"\n\"44\",\"cellinfo\"\n\"44\",\"afhttpclient\"\n\"44\",\"umts\"\n\"44\",\"smbj\"\n\"44\",\"filenet-process-engine\"\n\"44\",\"xmpp4r\"\n\"44\",\"vscode-liveshare\"\n\"44\",\"datemodified\"\n\"44\",\"bit-representation\"\n\"44\",\"package-info\"\n\"44\",\"firefox-headless\"\n\"44\",\"distroless\"\n\"44\",\"consensys-truffle\"\n\"44\",\"carousel-slider\"\n\"44\",\"flask-httpauth\"\n\"44\",\"finatra\"\n\"44\",\"python-standalone\"\n\"44\",\"flask-caching\"\n\"44\",\"bing-translator-api\"\n\"44\",\"bindservice\"\n\"44\",\"fairseq\"\n\"44\",\"gpu-warp\"\n\"44\",\"upickle\"\n\"44\",\"root-access\"\n\"44\",\"jpct\"\n\"44\",\"recognizer-intent\"\n\"44\",\"rodeo\"\n\"44\",\"sipml\"\n\"44\",\"ibis\"\n\"44\",\"i18next-http-backend\"\n\"44\",\"optional-binding\"\n\"44\",\"simplex-algorithm\"\n\"44\",\"data-filtering\"\n\"44\",\"fastjsonapi\"\n\"44\",\"calendarkit\"\n\"44\",\"keyeventargs\"\n\"44\",\"microsoft-commerce-server\"\n\"44\",\"robotjs\"\n\"44\",\"serverless-plugins\"\n\"44\",\"crystal-reports-2011\"\n\"44\",\"unity-components\"\n\"44\",\"favorite\"\n\"44\",\"shareplum\"\n\"44\",\"n3\"\n\"44\",\"windowsformsintegration\"\n\"44\",\"akita\"\n\"44\",\"angularjs-ng-checked\"\n\"44\",\"canjs-model\"\n\"44\",\"wcf-4\"\n\"44\",\"icloud-documents\"\n\"44\",\"grads\"\n\"44\",\"aws-sct\"\n\"44\",\"ibm-domino\"\n\"44\",\"rowsorter\"\n\"44\",\"recursive-mutex\"\n\"44\",\"rtmps\"\n\"44\",\"jml\"\n\"44\",\"shared-variable\"\n\"44\",\"aws-networking\"\n\"44\",\"avro4s\"\n\"44\",\"cri-o\"\n\"44\",\"ruby-2.6\"\n\"44\",\"joinfaces\"\n\"44\",\"fastavro\"\n\"44\",\"window-position\"\n\"44\",\"rostering\"\n\"44\",\"session-store\"\n\"44\",\"covariant-return-types\"\n\"44\",\"detailtextlabel\"\n\"44\",\"ws-reliablemessaging\"\n\"44\",\"azure-database-postgresql\"\n\"44\",\"postman-native-app\"\n\"44\",\"apple-app-site-association\"\n\"44\",\"native-file-system-api-js\"\n\"44\",\"spring-integration-mqtt\"\n\"44\",\"nehotspothelper\"\n\"44\",\"neo4j-graphql-js\"\n\"44\",\"approximate-nn-searching\"\n\"44\",\"kazoo\"\n\"44\",\"mojolang\"\n\"44\",\"wrds\"\n\"44\",\"ararat-synapse\"\n\"44\",\"justpy\"\n\"44\",\"simctl\"\n\"44\",\"nested-datalist\"\n\"44\",\"scala-implicits\"\n\"44\",\"html-frames\"\n\"44\",\"inproc\"\n\"44\",\"swifter\"\n\"44\",\"tvos9.1\"\n\"44\",\"wordpress-json-api\"\n\"44\",\"mongodb-cluster\"\n\"44\",\"sap-cpi\"\n\"44\",\"pattern-layout\"\n\"44\",\"simile\"\n\"44\",\"wpforms\"\n\"44\",\"aws-session-manager\"\n\"44\",\"guardian\"\n\"44\",\"boost-proto\"\n\"44\",\"pdfobject\"\n\"44\",\"android-video-record\"\n\"44\",\"boxsizer\"\n\"44\",\"intersystems-iris\"\n\"44\",\"path-2d\"\n\"44\",\"epp\"\n\"44\",\"nested-transactions\"\n\"44\",\"win-phone-silverlight-8.1\"\n\"44\",\"minibuffer\"\n\"44\",\"orbited\"\n\"44\",\"ezplatform\"\n\"44\",\"ktlint\"\n\"44\",\"javascript-security\"\n\"44\",\"klaxon\"\n\"44\",\"system-services\"\n\"44\",\"t4scaffolding\"\n\"44\",\"netty-socketio\"\n\"44\",\"kotlin-java-interop\"\n\"44\",\"netbox\"\n\"44\",\"android-a11y\"\n\"44\",\"ezsql\"\n\"44\",\"millennial-media\"\n\"44\",\"visual-studio-shell\"\n\"44\",\"orientdb3.0\"\n\"44\",\"known-folders\"\n\"44\",\"aswebauthenticationsession\"\n\"44\",\"obfuscar\"\n\"44\",\"ocs\"\n\"44\",\"bscscan\"\n\"44\",\"ocsigen\"\n\"44\",\"java-interop\"\n\"44\",\"m4v\"\n\"44\",\"lustre\"\n\"44\",\"async-iterator\"\n\"44\",\"nvvp\"\n\"44\",\"sysdba\"\n\"44\",\"rete\"\n\"44\",\"dom-to-image\"\n\"44\",\"wordpad\"\n\"44\",\"libstreaming\"\n\"44\",\"libopencm3\"\n\"44\",\"java-binding\"\n\"44\",\"syswow64\"\n\"44\",\"system.windows.media\"\n\"44\",\"lz77\"\n\"44\",\"attachevent\"\n\"44\",\"viber-bot\"\n\"44\",\"knative-eventing\"\n\"44\",\"netdatacontractserializer\"\n\"44\",\"viber-api\"\n\"44\",\"tls1.1\"\n\"44\",\"sqlcompare\"\n\"44\",\"jammit\"\n\"44\",\"tailwind-elements\"\n\"44\",\"sqlcode\"\n\"44\",\"cachestorage\"\n\"44\",\"uidocumentbrowservc\"\n\"44\",\"geemap\"\n\"44\",\"highrise\"\n\"44\",\"isqlquery\"\n\"44\",\"gvm\"\n\"44\",\"harvest\"\n\"44\",\"openxml-powertools\"\n\"44\",\"android-debugging\"\n\"44\",\"hive-configuration\"\n\"44\",\"mkdocs-material\"\n\"44\",\"ithit-ajax-file-browser\"\n\"44\",\"azure-web-pubsub\"\n\"44\",\"qz-tray\"\n\"44\",\"xamarin-linker\"\n\"44\",\"caa\"\n\"44\",\"plottable\"\n\"44\",\"drawingvisual\"\n\"44\",\"asterisk-ari\"\n\"44\",\"xamarin-android-player\"\n\"44\",\"android-custom-keyboard\"\n\"44\",\"hibernate-types\"\n\"44\",\"sql-graph\"\n\"44\",\"drupal-search\"\n\"44\",\"conv1d\"\n\"44\",\"sqlbuilder\"\n\"44\",\"xcode-build-settings\"\n\"44\",\"gdelt\"\n\"44\",\"react-link\"\n\"44\",\"color-conversion\"\n\"44\",\"color-coding\"\n\"44\",\"csv-parser\"\n\"44\",\"laravel-ui\"\n\"44\",\"color-blindness\"\n\"44\",\"node.js-tape\"\n\"44\",\"qresource\"\n\"44\",\"gentelella\"\n\"44\",\"spatial-data\"\n\"44\",\"project-properties\"\n\"44\",\"qfilesystemwatcher\"\n\"44\",\"ip-fragmentation\"\n\"44\",\"exactly-once\"\n\"44\",\"chakra\"\n\"44\",\"e-signature\"\n\"44\",\"actionsheet\"\n\"44\",\"zune\"\n\"44\",\"angular2-guards\"\n\"44\",\"event-stream-processing\"\n\"44\",\"testing-strategies\"\n\"44\",\"specular\"\n\"44\",\"dub\"\n\"44\",\"google-cloud-ai-platform-pipelines\"\n\"44\",\"perl-xs\"\n\"44\",\"nltk-book\"\n\"44\",\"leader\"\n\"44\",\"octoprint\"\n\"44\",\"react-component-unmount\"\n\"44\",\"qmutex\"\n\"44\",\"perplexity\"\n\"44\",\"lsusb\"\n\"44\",\"text-comparison\"\n\"44\",\"stress\"\n\"44\",\"prcomp\"\n\"44\",\"torchserve\"\n\"44\",\"subst\"\n\"44\",\"prebid\"\n\"44\",\"armclang\"\n\"44\",\"thejit\"\n\"44\",\"topendialog\"\n\"44\",\"ember-i18n\"\n\"44\",\"bea\"\n\"44\",\"big5\"\n\"44\",\"google-refine\"\n\"44\",\"state-machine-workflow\"\n\"44\",\"themeprovider\"\n\"44\",\"webgl-globe\"\n\"44\",\"zend-lucene\"\n\"44\",\"gnu-arm\"\n\"44\",\"transform-feedback\"\n\"44\",\"emcc\"\n\"44\",\"parquet-mr\"\n\"44\",\"amazon-alb\"\n\"44\",\"gnumeric\"\n\"44\",\"bats-core\"\n\"44\",\"computed-style\"\n\"44\",\"glrotate\"\n\"44\",\"arcgis-android-api\"\n\"44\",\"tia-portal\"\n\"44\",\"touchjson\"\n\"44\",\"solaris-studio\"\n\"44\",\"power-off\"\n\"44\",\"tightly-coupled-code\"\n\"44\",\"dali\"\n\"44\",\"ikimageview\"\n\"44\",\"multibyte-functions\"\n\"44\",\"multibyte-characters\"\n\"44\",\"pantheios\"\n\"44\",\"sourcelink\"\n\"44\",\"powershell-az-module\"\n\"44\",\"powershell-jobs\"\n\"44\",\"spark-framework\"\n\"43\",\"floyd-cycle-finding\"\n\"43\",\"llama3\"\n\"43\",\"sskeychain\"\n\"43\",\"installshield-2015\"\n\"43\",\"multiple-gpu\"\n\"43\",\"php-phantomjs\"\n\"43\",\"prism-7\"\n\"43\",\"sshkit\"\n\"43\",\"jcasc\"\n\"43\",\"tsoa\"\n\"43\",\"gitahead\"\n\"43\",\"git-filter\"\n\"43\",\"bank-conflict\"\n\"43\",\"tello-drone\"\n\"43\",\"triton\"\n\"43\",\"antimalware\"\n\"43\",\"local-functions\"\n\"43\",\"programming-pearls\"\n\"43\",\"prezto\"\n\"43\",\"intellij-inspections\"\n\"43\",\"cmb2\"\n\"43\",\"clam\"\n\"43\",\"fieldcodes\"\n\"43\",\"antlr4cs\"\n\"43\",\"background-blend-mode\"\n\"43\",\"cmenu\"\n\"43\",\"decal\"\n\"43\",\"location-based-service\"\n\"43\",\"grouped-collection-select\"\n\"43\",\"sql-variant\"\n\"43\",\"symfit\"\n\"43\",\"casing\"\n\"43\",\"phpwkhtmltopdf\"\n\"43\",\"rxalamofire\"\n\"43\",\"check-digit\"\n\"43\",\"dist-zilla\"\n\"43\",\"datastax-php-driver\"\n\"43\",\"rjb\"\n\"43\",\"mariadb-connect-engine\"\n\"43\",\"page-transition\"\n\"43\",\"apartments\"\n\"43\",\"xonsh\"\n\"43\",\"firebase-machine-learning\"\n\"43\",\"python-responses\"\n\"43\",\"ftgl\"\n\"43\",\"flask-sockets\"\n\"43\",\"pact-java\"\n\"43\",\"pythonmagick\"\n\"43\",\"pip-tools\"\n\"43\",\"xpce\"\n\"43\",\"fslex\"\n\"43\",\"next-pwa\"\n\"43\",\"umount\"\n\"43\",\"snmptrapd\"\n\"43\",\"django-countries\"\n\"43\",\"aerogear\"\n\"43\",\"cefglue\"\n\"43\",\"datalength\"\n\"43\",\"chruby\"\n\"43\",\"x-dwm\"\n\"43\",\"semplot\"\n\"43\",\"ccr\"\n\"43\",\"syn\"\n\"43\",\"image-stabilization\"\n\"43\",\"chrome-app-developer-tool\"\n\"43\",\"pkcs#5\"\n\"43\",\"freezable\"\n\"43\",\"python-contextvars\"\n\"43\",\"s2i\"\n\"43\",\"adoconnection\"\n\"43\",\"ngx-echarts\"\n\"43\",\"immutablelist\"\n\"43\",\"chips\"\n\"43\",\"readlink\"\n\"43\",\"keynotfoundexception\"\n\"43\",\"mysql.sock\"\n\"43\",\"rnoaa\"\n\"43\",\"microsoft.build\"\n\"43\",\"optix\"\n\"43\",\"crlf-vulnerability\"\n\"43\",\"angularjs-ng-touch\"\n\"43\",\"django-rest-swagger\"\n\"43\",\"django-generic-relations\"\n\"43\",\"rubyxl\"\n\"43\",\"recess\"\n\"43\",\"ib-insync\"\n\"43\",\"servicestack-auth\"\n\"43\",\"cryptographicexception\"\n\"43\",\"rmstore\"\n\"43\",\"jotai\"\n\"43\",\"createwindowex\"\n\"43\",\"awr\"\n\"43\",\"capstone\"\n\"43\",\"keyboard-maestro\"\n\"43\",\"grapecity\"\n\"43\",\"ptr-vector\"\n\"43\",\"fastmember\"\n\"43\",\"rkt\"\n\"43\",\"akka.net-persistence\"\n\"43\",\"role-manager\"\n\"43\",\"callblocking\"\n\"43\",\"wbem\"\n\"43\",\"psreadline\"\n\"43\",\"mysql-error-2003\"\n\"43\",\"angular-ngfor\"\n\"43\",\"mysql-error-1442\"\n\"43\",\"ora-06512\"\n\"43\",\"bluegiga\"\n\"43\",\"oneupuploaderbundle\"\n\"43\",\"android-wear-complication\"\n\"43\",\"bnfc\"\n\"43\",\"errbot\"\n\"43\",\"password-checker\"\n\"43\",\"hostmonster\"\n\"43\",\"infineon\"\n\"43\",\"nsdiffabledatasourcesnapshot\"\n\"43\",\"jquery-globalization\"\n\"43\",\"jquery-2.0\"\n\"43\",\"inline-scripting\"\n\"43\",\"onepage-scroll\"\n\"43\",\"payload-cms\"\n\"43\",\"azure-data-catalog\"\n\"43\",\"svgwrite\"\n\"43\",\"html5-notifications\"\n\"43\",\"sas-metadata\"\n\"43\",\"sap-business-application-studio\"\n\"43\",\"enumerated-types\"\n\"43\",\"aws-storage-gateway\"\n\"43\",\"springrunner\"\n\"43\",\"spring-data-graph\"\n\"43\",\"kafka-transactions-api\"\n\"43\",\"demographics\"\n\"43\",\"apptainer\"\n\"43\",\"bond\"\n\"43\",\"apple-login\"\n\"43\",\"envi\"\n\"43\",\"asymmetric\"\n\"43\",\"typeorm-datamapper\"\n\"43\",\"rich-notifications\"\n\"43\",\"riak-cs\"\n\"43\",\"revenue\"\n\"43\",\"visual-c++-2017\"\n\"43\",\"vim-powerline\"\n\"43\",\"netldap\"\n\"43\",\"golint\"\n\"43\",\"mailchimp-api-v3\"\n\"43\",\"fosfacebookbundle\"\n\"43\",\"netflix-conductor\"\n\"43\",\"javascript-globalize\"\n\"43\",\"browscap\"\n\"43\",\"setup.exe\"\n\"43\",\"rexcel\"\n\"43\",\"btrieve\"\n\"43\",\"richtextediting\"\n\"43\",\"asynchronous-postback\"\n\"43\",\"abstract-algebra\"\n\"43\",\"rblpapi\"\n\"43\",\"overlapping-instances\"\n\"43\",\"video-memory\"\n\"43\",\"java-nio\"\n\"43\",\"pystray\"\n\"43\",\"typeinitializeexception\"\n\"43\",\"external-dns\"\n\"43\",\"ossec\"\n\"43\",\"formencode\"\n\"43\",\"visual-inheritance\"\n\"43\",\"lfsr\"\n\"43\",\"extreme-programming\"\n\"43\",\"learn-ruby-the-hard-way\"\n\"43\",\"colcon\"\n\"43\",\"signatures\"\n\"43\",\"regexkitlite\"\n\"43\",\"aspnetcore-environment\"\n\"43\",\"gauss\"\n\"43\",\"azure-releases\"\n\"43\",\"aspnetzero\"\n\"43\",\"refinements\"\n\"43\",\"numbered-list\"\n\"43\",\"radium\"\n\"43\",\"galileo\"\n\"43\",\"scala-pickling\"\n\"43\",\"mobile-angular-ui\"\n\"43\",\"x509trustmanager\"\n\"43\",\"table-lock\"\n\"43\",\"npcap\"\n\"43\",\"xamarin.forms.entry\"\n\"43\",\"diaspora\"\n\"43\",\"pn532\"\n\"43\",\"continuous-forms\"\n\"43\",\"devicecheck\"\n\"43\",\"itunesartwork\"\n\"43\",\"xamarin.shell\"\n\"43\",\"handshaking\"\n\"43\",\"rails7\"\n\"43\",\"xamarin.winphone\"\n\"43\",\"isolation-forest\"\n\"43\",\"astral-plane\"\n\"43\",\"android-data-usage\"\n\"43\",\"hashgraph\"\n\"43\",\"wxglade\"\n\"43\",\"dragonboard\"\n\"43\",\"vert.x-webclient\"\n\"43\",\"hierarchical-trees\"\n\"43\",\"content-values\"\n\"43\",\"rag\"\n\"43\",\"sql-server-administration\"\n\"43\",\"assemblyresolve\"\n\"43\",\"openshift-online\"\n\"43\",\"nuclio\"\n\"43\",\"diffmerge\"\n\"43\",\"gw-basic\"\n\"43\",\"c++builder-10.1-berlin\"\n\"43\",\"policyfiles\"\n\"43\",\"quickform\"\n\"43\",\"scmmanager\"\n\"43\",\"nolearn\"\n\"43\",\"nix-flake\"\n\"43\",\"pep8-assembly\"\n\"43\",\"storagefolder\"\n\"43\",\"android-jetpack-compose-ui\"\n\"43\",\"move-lang\"\n\"43\",\"hummus.js\"\n\"43\",\"react-multi-carousel\"\n\"43\",\"generic.xaml\"\n\"43\",\"huawei-account\"\n\"43\",\"android-lazyloading\"\n\"43\",\"angular-injector\"\n\"43\",\"mercury\"\n\"43\",\"charms-bar\"\n\"43\",\"responder-chain\"\n\"43\",\"collaborative\"\n\"43\",\"movieplayer\"\n\"43\",\"pfloginviewcontroller\"\n\"43\",\"geotargetting\"\n\"43\",\"chainlink-keepers\"\n\"43\",\"geshi\"\n\"43\",\"angularbuild\"\n\"43\",\"perl-tidy\"\n\"43\",\"eviews\"\n\"43\",\"qquickwidget\"\n\"43\",\"eventvalidation\"\n\"43\",\"request-queueing\"\n\"43\",\"texttemplate\"\n\"43\",\"mootools1.2\"\n\"43\",\"laravel-mix-vue3\"\n\"43\",\"curry-howard\"\n\"43\",\"react-ace\"\n\"43\",\"qabstractitemview\"\n\"43\",\"active-objects\"\n\"43\",\"spectral-python\"\n\"43\",\"elasticsearch-8\"\n\"43\",\"spray-dsl\"\n\"43\",\"pep517\"\n\"43\",\"git-review\"\n\"43\",\"web-mining\"\n\"43\",\"third-normal-form\"\n\"43\",\"mru\"\n\"43\",\"google-search-platform\"\n\"43\",\"alpaca\"\n\"43\",\"tpc\"\n\"43\",\"cvxr\"\n\"43\",\"panoramio\"\n\"43\",\"libyuv\"\n\"43\",\"structured-concurrency\"\n\"43\",\"qt-vs-addin\"\n\"43\",\"biicode\"\n\"43\",\"partcover\"\n\"43\",\"emm\"\n\"43\",\"mt\"\n\"43\",\"conan-2\"\n\"43\",\"arviz\"\n\"43\",\"git-mv\"\n\"43\",\"multi-configuration\"\n\"43\",\"amazon-product-advertising-api\"\n\"43\",\"transformation-matrix\"\n\"43\",\"gl-matrix\"\n\"43\",\"google-php-sdk\"\n\"43\",\"scrollrect\"\n\"43\",\"tfs-alerts\"\n\"43\",\"secure-element\"\n\"43\",\"premailer\"\n\"43\",\"daq-mx\"\n\"43\",\"google-plus-domains\"\n\"43\",\"precompiler\"\n\"43\",\"parsefacebookutils\"\n\"43\",\"structr\"\n\"43\",\"web-client\"\n\"43\",\"heron\"\n\"43\",\"google-streetview-publish\"\n\"42\",\"slurp\"\n\"42\",\"match-recognize\"\n\"42\",\"banana-pi\"\n\"42\",\"xwork\"\n\"42\",\"cloudconvert\"\n\"42\",\"ecryptfs\"\n\"42\",\"masonry-ios-osx\"\n\"42\",\"intellij-idea-2018\"\n\"42\",\"wdio-v6\"\n\"42\",\"define-syntax\"\n\"42\",\"defaultifempty\"\n\"42\",\"ssis-connection-manager\"\n\"42\",\"troff\"\n\"42\",\"graphql-php\"\n\"42\",\"ec2-userdata\"\n\"42\",\"defaultazurecredential\"\n\"42\",\"process-control\"\n\"42\",\"jboss-portal\"\n\"42\",\"loadcontrol\"\n\"42\",\"gideros\"\n\"42\",\"xtype\"\n\"42\",\"clipboard-interaction\"\n\"42\",\"file-find\"\n\"42\",\"skulpt\"\n\"42\",\"jest-preset-angular\"\n\"42\",\"ghost4j\"\n\"42\",\"multiple-variable-return\"\n\"42\",\"ghcr\"\n\"42\",\"baseline-profile\"\n\"42\",\"debug-diagnostic-tool\"\n\"42\",\"intel-8080\"\n\"42\",\"matlab-spm\"\n\"42\",\"mvccontrib-testhelper\"\n\"42\",\"phonertc\"\n\"42\",\"mvp4g\"\n\"42\",\"skcropnode\"\n\"42\",\"flutter-background\"\n\"42\",\"telerik-test-studio\"\n\"42\",\"app-globalresources\"\n\"42\",\"chrome-profile\"\n\"42\",\"appassembler\"\n\"42\",\"chopper\"\n\"42\",\"adodbapi\"\n\"42\",\"data-profiling\"\n\"42\",\"jtemplates\"\n\"42\",\"addremoveprograms\"\n\"42\",\"jscharts\"\n\"42\",\"filtered\"\n\"42\",\"consuming\"\n\"42\",\"confluent-kafka-go\"\n\"42\",\"platform-agnostic\"\n\"42\",\"cart-analysis\"\n\"42\",\"fink\"\n\"42\",\"aparapi\"\n\"42\",\"flashlite\"\n\"42\",\"volar\"\n\"42\",\"bitmapdrawable\"\n\"42\",\"rxcpp\"\n\"42\",\"bitcount\"\n\"42\",\"freestanding\"\n\"42\",\"makemaker\"\n\"42\",\"swiftystorekit\"\n\"42\",\"xmlcatalog\"\n\"42\",\"sa\"\n\"42\",\"xmllist\"\n\"42\",\"page.js\"\n\"42\",\"xml-literals\"\n\"42\",\"pagefile\"\n\"42\",\"image-slider\"\n\"42\",\"ftpwebresponse\"\n\"42\",\"vscode-jsconfig\"\n\"42\",\"django-auth-models\"\n\"42\",\"xlm\"\n\"42\",\"fvm\"\n\"42\",\"xdoclet\"\n\"42\",\"fxcopcmd\"\n\"42\",\"pairplot\"\n\"42\",\"uniobjects\"\n\"42\",\"manifold\"\n\"42\",\"pandas-merge\"\n\"42\",\"seetest\"\n\"42\",\"cargo-maven2-plugin\"\n\"42\",\"fsharpchart\"\n\"42\",\"rsi\"\n\"42\",\"ruff\"\n\"42\",\"fastlane-deliver\"\n\"42\",\"aws-glue-workflow\"\n\"42\",\"ora-01017\"\n\"42\",\"aws-datasync\"\n\"42\",\"unity-dots\"\n\"42\",\"data-gateway\"\n\"42\",\"waitforexit\"\n\"42\",\"real-number\"\n\"42\",\"sitecore-analytics\"\n\"42\",\"validationgroup\"\n\"42\",\"docker-ucp\"\n\"42\",\"servicestack-autoquery\"\n\"42\",\"windows-98\"\n\"42\",\"iaik-jce\"\n\"42\",\"windows-7-embedded\"\n\"42\",\"native-aot\"\n\"42\",\"keycloak-angular\"\n\"42\",\"kinect-interaction\"\n\"42\",\"gpuimagestillcamera\"\n\"42\",\"datagridviewimagecolumn\"\n\"42\",\"dockerignore\"\n\"42\",\"angular-ui-tabset\"\n\"42\",\"databasedotcom-gem\"\n\"42\",\"shared-access-signatures\"\n\"42\",\"roweditor\"\n\"42\",\"icd\"\n\"42\",\"rollovers\"\n\"42\",\"gst-launch-1.0\"\n\"42\",\"borb\"\n\"42\",\"nsblockoperation\"\n\"42\",\"wrk\"\n\"42\",\"wsit\"\n\"42\",\"boost-random\"\n\"42\",\"delphi-10.4.2\"\n\"42\",\"guard-statement\"\n\"42\",\"guideline-support-library\"\n\"42\",\"nslock\"\n\"42\",\"jquery-get\"\n\"42\",\"neo4j-bolt\"\n\"42\",\"hotwire\"\n\"42\",\"jquery-trigger\"\n\"42\",\"error-list\"\n\"42\",\"twrequest\"\n\"42\",\"nestjs-fastify\"\n\"42\",\"htonl\"\n\"42\",\"turbo-prolog\"\n\"42\",\"aps\"\n\"42\",\"openfin\"\n\"42\",\"mod-proxy-balancer\"\n\"42\",\"cppwinrt\"\n\"42\",\"sap-hr\"\n\"42\",\"appledoc\"\n\"42\",\"sbml\"\n\"42\",\"html-renderer\"\n\"42\",\"powerapps-portal\"\n\"42\",\"postgresql-8.2\"\n\"42\",\"workflow-foundation-4.5\"\n\"42\",\"paypal-pdt\"\n\"42\",\"u8darts\"\n\"42\",\"typescript2.1\"\n\"42\",\"vimeo-ios\"\n\"42\",\"luasql\"\n\"42\",\"rex\"\n\"42\",\"mill\"\n\"42\",\"javafx-tableview\"\n\"42\",\"raw-loader\"\n\"42\",\"code-security\"\n\"42\",\"vivus\"\n\"42\",\"extrafont\"\n\"42\",\"outline-view\"\n\"42\",\"outlining\"\n\"42\",\".net-runtime\"\n\"42\",\"object-code\"\n\"42\",\"rank-n-types\"\n\"42\",\"audiosegment\"\n\"42\",\"kotlin-symbol-processing\"\n\"42\",\"formstack\"\n\"42\",\"javascript-decorators\"\n\"42\",\"pyfcm\"\n\"42\",\"mio\"\n\"42\",\"o365security-compliance\"\n\"42\",\"coinduction\"\n\"42\",\"new-item\"\n\"42\",\"form-layout\"\n\"42\",\"facebook-debugger\"\n\"42\",\"virtual-drive\"\n\"42\",\"bubblewrap\"\n\"42\",\"kotlin-companion\"\n\"42\",\"devsecops\"\n\"42\",\"mobileiron\"\n\"42\",\"radajaxmanager\"\n\"42\",\"redislabs\"\n\"42\",\"rails-generators\"\n\"42\",\"horde\"\n\"42\",\"novnc\"\n\"42\",\"pljson\"\n\"42\",\"tagless-final\"\n\"42\",\"nuodb\"\n\"42\",\"drupal-ajax\"\n\"42\",\"hasattr\"\n\"42\",\"scoped-ptr\"\n\"42\",\"taction\"\n\"42\",\"hardening\"\n\"42\",\"azure-sphere\"\n\"42\",\"racket-student-languages\"\n\"42\",\"expandable-table\"\n\"42\",\"time-estimation\"\n\"42\",\"bundletransformer\"\n\"42\",\"sql-except\"\n\"42\",\"associated-domains\"\n\"42\",\"cordova-media-plugin\"\n\"42\",\"racsignal\"\n\"42\",\"tobase64string\"\n\"42\",\"hl7-v3\"\n\"42\",\"itunes-search-api\"\n\"42\",\"nszombieenabled\"\n\"42\",\"mobclix\"\n\"42\",\"task-management\"\n\"42\",\"highgui\"\n\"42\",\"opengl-2.0\"\n\"42\",\"openwebbeans\"\n\"42\",\"azure-private-dns\"\n\"42\",\"qwerty\"\n\"42\",\"notary\"\n\"42\",\"notesview\"\n\"42\",\"azure-hub\"\n\"42\",\"android-exifinterface\"\n\"42\",\"cfthread\"\n\"42\",\"activitykit\"\n\"42\",\"hudson-api\"\n\"42\",\"geonetwork\"\n\"42\",\"column-oriented\"\n\"42\",\"iotivity\"\n\"42\",\"european-data-format\"\n\"42\",\"gethostbyaddr\"\n\"42\",\"generic-interface\"\n\"42\",\"spotless\"\n\"42\",\"qjsengine\"\n\"42\",\"hxcpp\"\n\"42\",\"offscreen-canvas\"\n\"42\",\"node-imap\"\n\"42\",\"leaflet-draw\"\n\"42\",\"low-level-api\"\n\"42\",\"okuma\"\n\"42\",\"metadatatype\"\n\"42\",\"streaming-flv-video\"\n\"42\",\"stringcollection\"\n\"42\",\"ehcache-2\"\n\"42\",\"perldoc\"\n\"42\",\"ei\"\n\"42\",\"protocolexception\"\n\"42\",\"teststack\"\n\"42\",\"hygiene\"\n\"42\",\"chartjs-plugin-annotation\"\n\"42\",\"spmd\"\n\"42\",\"messagingcenter\"\n\"42\",\"protostuff\"\n\"42\",\"latex-suite\"\n\"42\",\"terr\"\n\"42\",\"logits\"\n\"42\",\"hyperhtml\"\n\"42\",\"react-native-ble-manager\"\n\"42\",\"cfgrib\"\n\"42\",\"monotorrent\"\n\"42\",\"react-aad-msal\"\n\"42\",\"spgridview\"\n\"42\",\"cometchat\"\n\"42\",\"elasticsearch-geo-shape\"\n\"42\",\"mongovue\"\n\"42\",\"transformable\"\n\"42\",\"google-perftools\"\n\"42\",\"alternation\"\n\"42\",\"helium\"\n\"42\",\"msf4j\"\n\"42\",\"scriptcontrol\"\n\"42\",\"alt-key\"\n\"42\",\"argo-events\"\n\"42\",\"msn-messenger\"\n\"42\",\"limejs\"\n\"42\",\"compilation-time\"\n\"42\",\"arrayindexoutofboundsexception\"\n\"42\",\"hermite\"\n\"42\",\"global.asa\"\n\"42\",\"heritrix\"\n\"42\",\"webiopi\"\n\"42\",\"usermetadata\"\n\"42\",\"soundcloud-stratus\"\n\"42\",\"d3.geo\"\n\"42\",\"thegraph\"\n\"42\",\"top-down\"\n\"42\",\"enforcement\"\n\"42\",\"webbot\"\n\"42\",\"igx-grid\"\n\"42\",\"flutter-row\"\n\"42\",\"starter-kits\"\n\"42\",\"git-rerere\"\n\"42\",\"dart-packages\"\n\"42\",\"has-many-polymorphs\"\n\"42\",\"idempiere\"\n\"42\",\"web-farm-framework\"\n\"42\",\"webactivator\"\n\"42\",\"cxml\"\n\"42\",\"quadratic-curve\"\n\"42\",\"lifecycle-hook\"\n\"42\",\"southeast-asian-languages\"\n\"42\",\"avalara\"\n\"41\",\"mathgl\"\n\"41\",\"mutating-function\"\n\"41\",\"multistage-pipeline\"\n\"41\",\"jedi-code-library\"\n\"41\",\"clientwebsocket\"\n\"41\",\"skeffectnode\"\n\"41\",\"mutablemap\"\n\"41\",\"sql-session-state\"\n\"41\",\"jetbrains-fleet\"\n\"41\",\"jcommander\"\n\"41\",\"telerik-ajax\"\n\"41\",\"fetch-mock\"\n\"41\",\"size-type\"\n\"41\",\"sre\"\n\"41\",\"website-hosting\"\n\"41\",\"skbio\"\n\"41\",\"groovyclassloader\"\n\"41\",\"sketchware\"\n\"41\",\"fedora-commons\"\n\"41\",\"vue-good-table\"\n\"41\",\"tscrollbox\"\n\"41\",\"fluent-design\"\n\"41\",\"dc\"\n\"41\",\"class-eval\"\n\"41\",\"bartintcolor\"\n\"41\",\"cmfctoolbar\"\n\"41\",\"bartender\"\n\"41\",\"treetagger\"\n\"41\",\"white-box\"\n\"41\",\"dddd\"\n\"41\",\"edirectory\"\n\"41\",\"easyxdm\"\n\"41\",\"fluentlenium\"\n\"41\",\"clickablespan\"\n\"41\",\"ansible-ad-hoc\"\n\"41\",\"yii2-rbac\"\n\"41\",\"fluentvalidation-2.0\"\n\"41\",\"barman\"\n\"41\",\"bare-metal-server\"\n\"41\",\"safe-publication\"\n\"41\",\"managed-vm\"\n\"41\",\"ccnode\"\n\"41\",\"binary-log\"\n\"41\",\"cbuttoncolumn\"\n\"41\",\"dirtyread\"\n\"41\",\"bing-speech\"\n\"41\",\"pact-net\"\n\"41\",\"unicast\"\n\"41\",\"ng-html2js\"\n\"41\",\"ng-flow\"\n\"41\",\"bitcore\"\n\"41\",\"apikit\"\n\"41\",\"s\"\n\"41\",\"ada2012\"\n\"41\",\"xfire\"\n\"41\",\"snowflake-pipe\"\n\"41\",\"disable-link\"\n\"41\",\"api-security\"\n\"41\",\"python-language-server\"\n\"41\",\"mainbundle\"\n\"41\",\"firepad\"\n\"41\",\"ngx-monaco-editor\"\n\"41\",\"rvmrc\"\n\"41\",\"ci-merchant\"\n\"41\",\"cartogram\"\n\"41\",\"appendtext\"\n\"41\",\"vscode-api\"\n\"41\",\"json-flattener\"\n\"41\",\"divshot\"\n\"41\",\"adobe-connect\"\n\"41\",\"imapx\"\n\"41\",\"language-binding\"\n\"41\",\"chomsky-hierarchy\"\n\"41\",\"markdown-it\"\n\"41\",\"socks5\"\n\"41\",\"socketstream\"\n\"41\",\"dockerode\"\n\"41\",\"wiki-markup\"\n\"41\",\"public-key-exchange\"\n\"41\",\"sitecore-sxa\"\n\"41\",\"r-neo4j\"\n\"41\",\"operationcontext\"\n\"41\",\"vary\"\n\"41\",\"gradle-daemon\"\n\"41\",\"shared-resource\"\n\"41\",\"oracle-analytics\"\n\"41\",\"vdm++\"\n\"41\",\"dnsjava\"\n\"41\",\"django-viewsets\"\n\"41\",\"unity-test-framework\"\n\"41\",\"service-model\"\n\"41\",\"doh\"\n\"41\",\"nachos\"\n\"41\",\"jform-designer\"\n\"41\",\"session-affinity\"\n\"41\",\"n-ary-tree\"\n\"41\",\"gradle-multi-project-build\"\n\"41\",\"document-class\"\n\"41\",\"winamp\"\n\"41\",\"pyarango\"\n\"41\",\"rsa-key-fingerprint\"\n\"41\",\"wasmtime\"\n\"41\",\"oracle-cloud-infrastructure-classic\"\n\"41\",\"valuetask\"\n\"41\",\"afx\"\n\"41\",\"pure-layout\"\n\"41\",\"windows-phone-emulator\"\n\"41\",\"hyperstack\"\n\"41\",\"hyperscript\"\n\"41\",\"rpres\"\n\"41\",\"readkey\"\n\"41\",\"w3m\"\n\"41\",\"oracle-rac\"\n\"41\",\"sips\"\n\"41\",\"verbatim\"\n\"41\",\"wpf-4.5\"\n\"41\",\"jupyterbook\"\n\"41\",\"design-time-data\"\n\"41\",\"html-tbody\"\n\"41\",\"opendatabase\"\n\"41\",\"androidx-test\"\n\"41\",\"svn-server\"\n\"41\",\"tuxedo\"\n\"41\",\"blitz++\"\n\"41\",\"monadplus\"\n\"41\",\"postcss-import\"\n\"41\",\"braille\"\n\"41\",\"module-map\"\n\"41\",\"wpf-mediakit\"\n\"41\",\"nsrunningapplication\"\n\"41\",\"cp-optimizer\"\n\"41\",\"sbv\"\n\"41\",\"entitycollection\"\n\"41\",\"infinitest\"\n\"41\",\"nsdialogs\"\n\"41\",\"tumblr-html\"\n\"41\",\"jquery-ui-progressbar\"\n\"41\",\"nestjs-graphql\"\n\"41\",\"mongo-jackson-mapper\"\n\"41\",\"nsnetservicebrowser\"\n\"41\",\"posprinter\"\n\"41\",\"pdal\"\n\"41\",\"jquery-nestable\"\n\"41\",\"devcon\"\n\"41\",\"blender-2.61\"\n\"41\",\"corosync\"\n\"41\",\"sap-crm\"\n\"41\",\"jquery-confirm\"\n\"41\",\"spring-resource-server\"\n\"41\",\"boltdb\"\n\"41\",\"aurelia-fetch-client\"\n\"41\",\"pymeshlab\"\n\"41\",\"middy\"\n\"41\",\"rapi\"\n\"41\",\"visual-studio-package\"\n\"41\",\"kmongo\"\n\"41\",\"java-collections-api\"\n\"41\",\"5g\"\n\"41\",\"domaindatasource\"\n\"41\",\"libmysqlclient\"\n\"41\",\"rightfax\"\n\"41\",\"libxl\"\n\"41\",\"google-alloydb\"\n\"41\",\"winmd\"\n\"41\",\"libtool-xcode\"\n\"41\",\"viewanimator\"\n\"41\",\"formkit\"\n\"41\",\"systemjs-builder\"\n\"41\",\"virtual-disk\"\n\"41\",\"androidasync-koush\"\n\"41\",\"rapid-prototyping\"\n\"41\",\"magento-1.13\"\n\"41\",\"window-style\"\n\"41\",\"visual-c++-2010-express\"\n\"41\",\"augmented-assignment\"\n\"41\",\"river-crossing-puzzle\"\n\"41\",\"macrodef\"\n\"41\",\"rdrand\"\n\"41\",\"wind-river-workbench\"\n\"41\",\"systemdynamics\"\n\"41\",\"pytest-qt\"\n\"41\",\"setwd\"\n\"41\",\"viml\"\n\"41\",\"american-fuzzy-lop\"\n\"41\",\"opennmt\"\n\"41\",\"dropbear\"\n\"41\",\"vertx3\"\n\"41\",\"drawingarea\"\n\"41\",\"schemaspy\"\n\"41\",\"highlightjs\"\n\"41\",\"aspmenu-control\"\n\"41\",\"redland\"\n\"41\",\"asp.net-mvc-filters\"\n\"41\",\"hibernateexception\"\n\"41\",\"ntvs\"\n\"41\",\"double-byte\"\n\"41\",\"gdbm\"\n\"41\",\"gwt-2.4\"\n\"41\",\"polymer-elements\"\n\"41\",\"versionupgrade\"\n\"41\",\"policy-gradient-descent\"\n\"41\",\"notification-bar\"\n\"41\",\"h.323\"\n\"41\",\"expo-sqlite\"\n\"41\",\"uigraphicsimagerenderer\"\n\"41\",\"expresso\"\n\"41\",\"quill.io\"\n\"41\",\"uikeycommand\"\n\"41\",\"j2v8\"\n\"41\",\"c++builder-10.4-sydney\"\n\"41\",\"executable-format\"\n\"41\",\"sqldb\"\n\"41\",\"iso-3166\"\n\"41\",\"uiapplicationshortcutitem\"\n\"41\",\"tcomb-form-native\"\n\"41\",\"timelion\"\n\"41\",\"mod-auth\"\n\"41\",\"xamarin.forms.labs\"\n\"41\",\"ivalidatableobject\"\n\"41\",\"tinkergraph\"\n\"41\",\"tasklet\"\n\"41\",\"iranges\"\n\"41\",\"omniauth-linkedin\"\n\"41\",\"messagebird\"\n\"41\",\"cfimage\"\n\"41\",\"character-trimming\"\n\"41\",\"chatbase\"\n\"41\",\"hybrid-cloud\"\n\"41\",\"mouseleftbuttondown\"\n\"41\",\"testfairy\"\n\"41\",\"geo-replication\"\n\"41\",\"google-cloud-resource-manager\"\n\"41\",\"http-digest\"\n\"41\",\"lts\"\n\"41\",\"acsl\"\n\"41\",\"response-entity\"\n\"41\",\"office-online-server\"\n\"41\",\"qextserialport\"\n\"41\",\"curity\"\n\"41\",\"active-pattern\"\n\"41\",\"medusajs\"\n\"41\",\"ios-3.x\"\n\"41\",\"spill-range\"\n\"41\",\"layered-windows\"\n\"41\",\"respondstoselector\"\n\"41\",\"geoext\"\n\"41\",\"pyv8\"\n\"41\",\"spike\"\n\"41\",\"onactionexecuting\"\n\"41\",\"nodename\"\n\"41\",\"zero-pad\"\n\"41\",\"searchqueryset\"\n\"41\",\"google-profiles-api\"\n\"41\",\"gmail-pop\"\n\"41\",\"autodesk-realitycapture\"\n\"41\",\"searchdisplaycontroller\"\n\"41\",\"msp432\"\n\"41\",\"alt-attribute\"\n\"41\",\"webpack-config\"\n\"41\",\"sunone\"\n\"41\",\"zentest\"\n\"41\",\"gmsa\"\n\"41\",\"global-object\"\n\"41\",\"arrange-act-assert\"\n\"41\",\"qtgstreamer\"\n\"41\",\"com-server\"\n\"41\",\"light-sensor\"\n\"41\",\"lightstreamer\"\n\"41\",\"tradeoff\"\n\"41\",\"thredds\"\n\"41\",\"mt19937\"\n\"41\",\"parity-io\"\n\"41\",\"shellshock-bash-bug\"\n\"41\",\"libyaml\"\n\"41\",\"libz\"\n\"41\",\"cwac-camera\"\n\"41\",\"component-space\"\n\"41\",\"max-path\"\n\"41\",\"sonarqube-api\"\n\"41\",\"beam-search\"\n\"41\",\"d2xx\"\n\"41\",\"autoscalemode\"\n\"41\",\"completion-stage\"\n\"41\",\"ms-access-97\"\n\"41\",\"mdanalysis\"\n\"41\",\"sonar-maven-plugin\"\n\"41\",\"z-axis\"\n\"40\",\"phppowerpoint\"\n\"40\",\"ggdendro\"\n\"40\",\"squirrel\"\n\"40\",\"babel-register\"\n\"40\",\"vue-instant-search\"\n\"40\",\"fbs\"\n\"40\",\"phonepe\"\n\"40\",\"matic\"\n\"40\",\"rentrez\"\n\"40\",\"primus\"\n\"40\",\"backgrounding\"\n\"40\",\"loading-image\"\n\"40\",\"ghc-api\"\n\"40\",\"react-native-device-info\"\n\"40\",\"instancetype\"\n\"40\",\"wiki.js\"\n\"40\",\"prism.js\"\n\"40\",\"truedepth-camera\"\n\"40\",\"teamcity-9.1\"\n\"40\",\"fluid-framework\"\n\"40\",\"git-am\"\n\"40\",\"jenkins-git-plugin\"\n\"40\",\"render-blocking\"\n\"40\",\"dearpygui\"\n\"40\",\"feedjira\"\n\"40\",\"live-update\"\n\"40\",\"flutter-assetimage\"\n\"40\",\"bare\"\n\"40\",\"antivirus-integration\"\n\"40\",\"tearing\"\n\"40\",\"skorch\"\n\"40\",\"federated-table\"\n\"40\",\"gilead\"\n\"40\",\"intel-tsx\"\n\"40\",\"slony\"\n\"40\",\"youtrack-api\"\n\"40\",\"listproperty\"\n\"40\",\"annyang\"\n\"40\",\"webtop\"\n\"40\",\"syncfusion-calendar\"\n\"40\",\"distube\"\n\"40\",\"index-buffer\"\n\"40\",\"apache-knox\"\n\"40\",\"plasmoid\"\n\"40\",\"nexusdb\"\n\"40\",\"dividebyzeroexception\"\n\"40\",\"python-aiofiles\"\n\"40\",\"fst\"\n\"40\",\"python-assignment-expression\"\n\"40\",\"placeautocompletefragment\"\n\"40\",\"chirp\"\n\"40\",\"finalcut\"\n\"40\",\"ftp4j\"\n\"40\",\"data-quality-services\"\n\"40\",\"connector-net\"\n\"40\",\"apache-traffic-server\"\n\"40\",\"fuzzy-c-means\"\n\"40\",\"adpcm\"\n\"40\",\"page-flipping\"\n\"40\",\"jsonify\"\n\"40\",\"pixel-ratio\"\n\"40\",\"smartthings\"\n\"40\",\"ovf\"\n\"40\",\"displayfor\"\n\"40\",\"circular-queue\"\n\"40\",\"circularreveal\"\n\"40\",\"packed-decimal\"\n\"40\",\"pipewire\"\n\"40\",\"package-manager-console\"\n\"40\",\"swscale\"\n\"40\",\"ccparticlesystem\"\n\"40\",\"runonce\"\n\"40\",\"catmull-rom-curve\"\n\"40\",\"pickerinput\"\n\"40\",\"freerdp\"\n\"40\",\"cartridge\"\n\"40\",\"sodium\"\n\"40\",\"keycloak-js\"\n\"40\",\"rnaturalearth\"\n\"40\",\"microsoft.mshtml\"\n\"40\",\"vanilla-forums\"\n\"40\",\"value-restriction\"\n\"40\",\"air-android\"\n\"40\",\"windows-composition-api\"\n\"40\",\"factominer\"\n\"40\",\"windows-controls\"\n\"40\",\"meteoric\"\n\"40\",\"cro\"\n\"40\",\"famous-angular\"\n\"40\",\"microsoft-ajax-minifier\"\n\"40\",\"metronome\"\n\"40\",\"pvcs\"\n\"40\",\"reader-macro\"\n\"40\",\"recyclerlistview\"\n\"40\",\"sitecore-xdb\"\n\"40\",\"animatedvectordrawable\"\n\"40\",\"grails-2.1\"\n\"40\",\"css-hyphens\"\n\"40\",\"shared-primary-key\"\n\"40\",\"name-attribute\"\n\"40\",\"vbc\"\n\"40\",\"candidate\"\n\"40\",\"unity3d-shaders\"\n\"40\",\"carbide\"\n\"40\",\"roomle\"\n\"40\",\"aws-route53\"\n\"40\",\"nagle\"\n\"40\",\"rowdetailstemplate\"\n\"40\",\"verifiable-c\"\n\"40\",\"django-swagger\"\n\"40\",\"dxva\"\n\"40\",\"tuya\"\n\"40\",\"nssplitviewcontroller\"\n\"40\",\"spring-boot-testcontainers\"\n\"40\",\"opendialog\"\n\"40\",\"azure-defender\"\n\"40\",\"appwarp\"\n\"40\",\"bootclasspath\"\n\"40\",\"inflector\"\n\"40\",\"ergonomics\"\n\"40\",\"equivalence-classes\"\n\"40\",\"nssound\"\n\"40\",\"azure-billing\"\n\"40\",\"postgis-installation\"\n\"40\",\"infobright\"\n\"40\",\"pcapplusplus\"\n\"40\",\"apple-configurator\"\n\"40\",\"jquery-after\"\n\"40\",\"correspondence\"\n\"40\",\"ndk-gdb\"\n\"40\",\"nestjs-gateways\"\n\"40\",\"jquery-fileupload-rails\"\n\"40\",\"axwebbrowser\"\n\"40\",\"azure-acr\"\n\"40\",\"swd\"\n\"40\",\"patricia-trie\"\n\"40\",\"gsettings\"\n\"40\",\"nsapptransportsecurity\"\n\"40\",\"inverse-transform\"\n\"40\",\"mongodb4.0\"\n\"40\",\"dynamodb-mapper\"\n\"40\",\"derivingvia\"\n\"40\",\"jquery-chaining\"\n\"40\",\"gru\"\n\"40\",\"mongodb-scala\"\n\"40\",\"suppressmessage\"\n\"40\",\"border-spacing\"\n\"40\",\"entity-framework-mapping\"\n\"40\",\"envoyer.io\"\n\"40\",\"openbabel\"\n\"40\",\"interior-mutability\"\n\"40\",\"invokescript\"\n\"40\",\"bootstrap-ui\"\n\"40\",\"retinanet\"\n\"40\",\"google-app-engine-golang\"\n\"40\",\"godot3\"\n\"40\",\"pyscipopt\"\n\"40\",\"rasa-sdk\"\n\"40\",\"pypsa\"\n\"40\",\"wiris\"\n\"40\",\"viewmodel-savedstate\"\n\"40\",\"facebook-authorization\"\n\"40\",\"magma\"\n\"40\",\"3d-printing\"\n\"40\",\"klipfolio\"\n\"40\",\"m2e-pro\"\n\"40\",\"vistadb\"\n\"40\",\"octet-stream\"\n\"40\",\"video4linux\"\n\"40\",\"microsoft-graph-plannertasks\"\n\"40\",\"rhdf5\"\n\"40\",\"return-path\"\n\"40\",\"rhodecode\"\n\"40\",\"bref\"\n\"40\",\"system.data.sqlclient\"\n\"40\",\"u2netdk\"\n\"40\",\"2-digit-year\"\n\"40\",\"shopify-activemerchant\"\n\"40\",\"wkinterfacelabel\"\n\"40\",\"sidekick\"\n\"40\",\"os161\"\n\"40\",\"visual-studio-mac-2022\"\n\"40\",\"word-boundaries\"\n\"40\",\"viewwilltransitiontosize\"\n\"40\",\"mindmap\"\n\"40\",\"forwarderrorcorrection\"\n\"40\",\"facebooker2\"\n\"40\",\"typelite\"\n\"40\",\"mixed-code\"\n\"40\",\"reductio\"\n\"40\",\"redhat-openjdk\"\n\"40\",\"dhis-2\"\n\"40\",\"excel-r1c1-notation\"\n\"40\",\"xacml2\"\n\"40\",\"redhat-sso\"\n\"40\",\"redux-promise-middleware\"\n\"40\",\"azure-mapping-data-flow\"\n\"40\",\"xamarin.forms-styles\"\n\"40\",\"openmap\"\n\"40\",\"nvml\"\n\"40\",\"openlink-virtuoso\"\n\"40\",\"bull-queue\"\n\"40\",\"istio-kiali\"\n\"40\",\"xamarin-zebble\"\n\"40\",\"bump\"\n\"40\",\"istio-operator\"\n\"40\",\"nstableheaderview\"\n\"40\",\"contextclassloader\"\n\"40\",\"wxruby\"\n\"40\",\"nstabviewcontroller\"\n\"40\",\"quickbasic\"\n\"40\",\"android-include\"\n\"40\",\"hard-coding\"\n\"40\",\"vfork\"\n\"40\",\"non-admin\"\n\"40\",\"assembly.load\"\n\"40\",\"pngquant\"\n\"40\",\"sqlncli\"\n\"40\",\"cacls\"\n\"40\",\"plugman\"\n\"40\",\"azure-iot-dps\"\n\"40\",\"sql-pl\"\n\"40\",\"qvideowidget\"\n\"40\",\"android-emulator-plugin\"\n\"40\",\"directory-browsing\"\n\"40\",\"nsxmldocument\"\n\"40\",\"qxmlquery\"\n\"40\",\"number-sequence\"\n\"40\",\"dta\"\n\"40\",\"gwas\"\n\"40\",\"expo-file-system\"\n\"40\",\"scatterpie\"\n\"40\",\"todoist\"\n\"40\",\"scala-repl\"\n\"40\",\"radmenu\"\n\"40\",\"dsquery\"\n\"40\",\"android-cast-api\"\n\"40\",\"asp.net-caching\"\n\"40\",\"ui-codemirror\"\n\"40\",\"azure-spring-cloud\"\n\"40\",\"gwt-jsinterop\"\n\"40\",\"asp.net-core-mvc-2.1\"\n\"40\",\"refinement-type\"\n\"40\",\"hub\"\n\"40\",\"dune\"\n\"40\",\"meep\"\n\"40\",\"android-multiple-users\"\n\"40\",\"css-print\"\n\"40\",\"properties.settings\"\n\"40\",\"node-rest-client\"\n\"40\",\"esri-arc-engine\"\n\"40\",\"combres\"\n\"40\",\"eulerr\"\n\"40\",\"pg-cron\"\n\"40\",\"ctp\"\n\"40\",\"pytorch-forecasting\"\n\"40\",\"spire.xls\"\n\"40\",\"octopack\"\n\"40\",\"http-post-vars\"\n\"40\",\"exceed\"\n\"40\",\"ohai-gem\"\n\"40\",\"qdate\"\n\"40\",\"excel-external-data\"\n\"40\",\"pyxll\"\n\"40\",\"logrus\"\n\"40\",\"node.js-domains\"\n\"40\",\"ctx\"\n\"40\",\"android-lazyadapter\"\n\"40\",\"stm32-hal\"\n\"40\",\"google-diff-match-patch\"\n\"40\",\"latexmk\"\n\"40\",\"angular2-universal\"\n\"40\",\"cfexecute\"\n\"40\",\"logback-groovy\"\n\"40\",\"hero\"\n\"40\",\"idml\"\n\"40\",\"webcodecs\"\n\"40\",\"behavior-tree\"\n\"40\",\"google-source-repositories\"\n\"40\",\"heroku-redis\"\n\"40\",\"flutter-layoutbuilder\"\n\"40\",\"muc\"\n\"40\",\"source-control-bindings\"\n\"40\",\"params-keyword\"\n\"40\",\"google-indoor-maps\"\n\"40\",\"bcdedit\"\n\"40\",\"ms-publisher\"\n\"40\",\"spark-window-function\"\n\"40\",\"asana-connect\"\n\"40\",\"presentation-model\"\n\"40\",\"as3crypto\"\n\"40\",\"sdist\"\n\"40\",\"glimmer.js\"\n\"40\",\"staticlayout\"\n\"40\",\"powerbuilder-pfc\"\n\"40\",\"secrets\"\n\"40\",\"git-refspec\"\n\"40\",\"imagejpeg\"\n\"40\",\"tfilestream\"\n\"40\",\"shfb\"\n\"40\",\"vaadin6\"\n\"40\",\"compound-drawables\"\n\"40\",\"stdformat\"\n\"40\",\"pangram\"\n\"40\",\"touch-up-inside\"\n\"40\",\"ie-mobile\"\n\"40\",\"panes\"\n\"40\",\"tf-agent\"\n\"40\",\"cypher-shell\"\n\"40\",\"thick-client\"\n\"40\",\"beatsmusic\"\n\"40\",\"fluxor\"\n\"40\",\"zend-application\"\n\"40\",\"scriptable\"\n\"40\",\"struts-html\"\n\"40\",\"webconfigurationmanager\"\n\"40\",\"predefined-macro\"\n\"40\",\"gmagick\"\n\"40\",\"static-allocation\"\n\"40\",\"area-chart\"\n\"40\",\"webformsmvp\"\n\"40\",\"zend-form-sub-form\"\n\"40\",\"automatic-storage\"\n\"40\",\"lidar-data\"\n\"40\",\"asianfonts\"\n\"40\",\"user.config\"\n\"40\",\"mscoco\"\n\"40\",\"ms-clarity\"\n\"39\",\"integration-patterns\"\n\"39\",\"slack-bolt\"\n\"39\",\"privilege-elevation\"\n\"39\",\"anonymous-delegates\"\n\"39\",\"flex-grow\"\n\"39\",\"anonymous-struct\"\n\"39\",\"yad\"\n\"39\",\"cmis-workbench\"\n\"39\",\"teradata-covalent\"\n\"39\",\"bamboo-artifacts\"\n\"39\",\"jbcrypt\"\n\"39\",\"debezium-engine\"\n\"39\",\"react-native-deep-linking\"\n\"39\",\"clistview\"\n\"39\",\"ecmascript-3\"\n\"39\",\"flutter-facebook-login\"\n\"39\",\"wds\"\n\"39\",\"multitrigger\"\n\"39\",\"rendering-engine\"\n\"39\",\"gremlinnet\"\n\"39\",\"getstaticpaths\"\n\"39\",\"tensordot\"\n\"39\",\"edmonds-karp\"\n\"39\",\"mat-option\"\n\"39\",\"apache-camel-k\"\n\"39\",\"vuetify-tabs\"\n\"39\",\"webstore\"\n\"39\",\"instanceid\"\n\"39\",\"ssh2-sftp-client\"\n\"39\",\"mvxbind\"\n\"39\",\"cljsbuild\"\n\"39\",\"edid\"\n\"39\",\"apache-camel-aws\"\n\"39\",\"anydac\"\n\"39\",\"ckasset\"\n\"39\",\"deferred-shading\"\n\"39\",\"backstop.js\"\n\"39\",\"mat-expansion-panel\"\n\"39\",\"vue-konva\"\n\"39\",\"babel-plugin-react-css-modules\"\n\"39\",\"webtestclient\"\n\"39\",\"phpflickr\"\n\"39\",\"backing-field\"\n\"39\",\"phpdesigner\"\n\"39\",\"react-router-native\"\n\"39\",\"fbsdksharedialog\"\n\"39\",\"ciscoconfparse\"\n\"39\",\"yarnpkg-v3\"\n\"39\",\"web-serial-api\"\n\"39\",\"dcl\"\n\"39\",\"whatsapp-flows\"\n\"39\",\"insert-statement\"\n\"39\",\"symbolic-execution\"\n\"39\",\"mammoth\"\n\"39\",\"binarystream\"\n\"39\",\"nhibernate-configuration\"\n\"39\",\"pairwise-distance\"\n\"39\",\"unique-lock\"\n\"39\",\"adler32\"\n\"39\",\"jsonplaceholder\"\n\"39\",\"swiftui-texteditor\"\n\"39\",\"apache-spark-2.3\"\n\"39\",\"xjb\"\n\"39\",\"snap7\"\n\"39\",\"mapmyindia-api\"\n\"39\",\"unificationengine\"\n\"39\",\"function-attributes\"\n\"39\",\"ngx-cookie-service\"\n\"39\",\"pik\"\n\"39\",\"jsonix\"\n\"39\",\"jsoniq\"\n\"39\",\"sw-toolbox\"\n\"39\",\"soffice\"\n\"39\",\"python-holidays\"\n\"39\",\"xml-import\"\n\"39\",\"platform-tools\"\n\"39\",\"sem\"\n\"39\",\"xmodem\"\n\"39\",\"apollo-ios\"\n\"39\",\"content-pipeline\"\n\"39\",\"play-authenticate\"\n\"39\",\"adhoc-queries\"\n\"39\",\"laravel-eloquent-resource\"\n\"39\",\"jstree-search\"\n\"39\",\"fsharp.data.typeproviders\"\n\"39\",\"ngx-graph\"\n\"39\",\"runnable-jar\"\n\"39\",\"datavisualization.toolkit\"\n\"39\",\"jstl-functions\"\n\"39\",\"discord-interactions\"\n\"39\",\"blaze-persistence\"\n\"39\",\"chef-zero\"\n\"39\",\"operational-transform\"\n\"39\",\"fastadapter\"\n\"39\",\"cross-application\"\n\"39\",\"serena\"\n\"39\",\"ice40\"\n\"39\",\"microsoft.extensions.logging\"\n\"39\",\"value-provider\"\n\"39\",\"method-dispatch\"\n\"39\",\"awaitility\"\n\"39\",\"simplejdbccall\"\n\"39\",\"winbugs14\"\n\"39\",\"ibtool\"\n\"39\",\"airpush\"\n\"39\",\"djinni\"\n\"39\",\"mysql-error-1292\"\n\"39\",\"docsify\"\n\"39\",\"sip-servlet\"\n\"39\",\"microsoft-copilot\"\n\"39\",\"cs-script\"\n\"39\",\"vcpu\"\n\"39\",\"aws-app-mesh\"\n\"39\",\"jnaerator\"\n\"39\",\"dartz\"\n\"39\",\"gorp\"\n\"39\",\"docker-ce\"\n\"39\",\"fauxton\"\n\"39\",\"google-voice-search\"\n\"39\",\"wcf-authentication\"\n\"39\",\"windows-ribbon-framework\"\n\"39\",\"psse\"\n\"39\",\"react-static\"\n\"39\",\"datagridviewbuttoncolumn\"\n\"39\",\"ibexpert\"\n\"39\",\"r-taskscheduler\"\n\"39\",\"vdso\"\n\"39\",\"scalajs-bundler\"\n\"39\",\"boost-bimap\"\n\"39\",\"scalafmt\"\n\"39\",\"jquery-focusout\"\n\"39\",\"jquery-1.3\"\n\"39\",\"swagger-node-express\"\n\"39\",\"demangler\"\n\"39\",\"errorformat\"\n\"39\",\"errorplacement\"\n\"39\",\"wptoolkit\"\n\"39\",\"interface-orientation\"\n\"39\",\"swift-macro\"\n\"39\",\"desktop.ini\"\n\"39\",\"intersystems-ensemble\"\n\"39\",\"horizontal-pager\"\n\"39\",\"angr\"\n\"39\",\"blocked-threads\"\n\"39\",\"dvd-burning\"\n\"39\",\"open-gauss\"\n\"39\",\"android-viewtreeobserver\"\n\"39\",\"blender-2.67\"\n\"39\",\"ios10.2\"\n\"39\",\"nsbox\"\n\"39\",\"kafka-cluster\"\n\"39\",\"wsgiref\"\n\"39\",\"tvos10\"\n\"39\",\"androidsvg\"\n\"39\",\"writexml\"\n\"39\",\"salesforce-einstein\"\n\"39\",\"svg-path\"\n\"39\",\"sap-smart-forms\"\n\"39\",\"apple-news\"\n\"39\",\"kdc\"\n\"39\",\"dynamic-island\"\n\"39\",\"nsstoryboard\"\n\"39\",\"ion-koush\"\n\"39\",\"jquery-slide-effects\"\n\"39\",\"apsw\"\n\"39\",\"supersonic\"\n\"39\",\"pyexiv2\"\n\"39\",\"tzdata\"\n\"39\",\"raymarching\"\n\"39\",\"java-access-bridge\"\n\"39\",\"68hc11\"\n\"39\",\"pynotify\"\n\"39\",\"output-window\"\n\"39\",\"system2\"\n\"39\",\"360-panorama-viewer\"\n\"39\",\"razzle\"\n\"39\",\"foundationdb\"\n\"39\",\"android-api-31\"\n\"39\",\"udpipe\"\n\"39\",\"java-runtime-compiler\"\n\"39\",\"type-projection\"\n\"39\",\"ko-custom-binding\"\n\"39\",\"gomoku\"\n\"39\",\"goliath\"\n\"39\",\"uglifier\"\n\"39\",\"vite-reactjs\"\n\"39\",\"typemaps\"\n\"39\",\"rawcontacts\"\n\"39\",\"pysimplesoap\"\n\"39\",\"ucd\"\n\"39\",\"lib-jitsi-meet\"\n\"39\",\"google-ajax-api\"\n\"39\",\"ext-direct\"\n\"39\",\"pygame-tick\"\n\"39\",\"shimmer\"\n\"39\",\"jasmine-reporters\"\n\"39\",\"oclif\"\n\"39\",\"orientdb-etl\"\n\"39\",\"visual-studio-setup\"\n\"39\",\"build-helper-maven-plugin\"\n\"39\",\"code-search-engine\"\n\"39\",\"form-with\"\n\"39\",\"pymacs\"\n\"39\",\"facebook-conversions-api\"\n\"39\",\"pyspider\"\n\"39\",\"formsflow.ai\"\n\"39\",\"randoop\"\n\"39\",\"retina.js\"\n\"39\",\"plperl\"\n\"39\",\"sqlline\"\n\"39\",\"jakarta-validation\"\n\"39\",\"regex-alternation\"\n\"39\",\"tapir\"\n\"39\",\"nopcommerce-3.90\"\n\"39\",\"talos\"\n\"39\",\"mobilefirst-analytics\"\n\"39\",\"x86-emulation\"\n\"39\",\"sql-server-2012-localdb\"\n\"39\",\"exploded\"\n\"39\",\"mocha-webpack\"\n\"39\",\"jake\"\n\"39\",\"azure-migrate\"\n\"39\",\"scrap-your-boilerplate\"\n\"39\",\"hippomocks\"\n\"39\",\"c++-loki\"\n\"39\",\"hol\"\n\"39\",\"uipickerviewdelegate\"\n\"39\",\"tagged-templates\"\n\"39\",\"home-screen-widget\"\n\"39\",\"cordova-plugin-camera\"\n\"39\",\"tokumx\"\n\"39\",\"drive-mapping\"\n\"39\",\"pointer-conversion\"\n\"39\",\"regex-look-ahead\"\n\"39\",\"asp.net-validators\"\n\"39\",\"c#-12.0\"\n\"39\",\"c++builder-5\"\n\"39\",\"digicert\"\n\"39\",\"android-crop\"\n\"39\",\"ask-cli\"\n\"39\",\"register-globals\"\n\"39\",\"tkx\"\n\"39\",\"executable-path\"\n\"39\",\"express.io\"\n\"39\",\"rails-cells\"\n\"39\",\"gambit\"\n\"39\",\"mkuserlocation\"\n\"39\",\"handhelddevice\"\n\"39\",\"xcode3to4\"\n\"39\",\"no-framework\"\n\"39\",\"j9\"\n\"39\",\"android-gpuimageview\"\n\"39\",\"ipfs-http-client\"\n\"39\",\"elementname\"\n\"39\",\"iron-ajax\"\n\"39\",\"acceptance\"\n\"39\",\"accessorytype\"\n\"39\",\"pechkin\"\n\"39\",\"google-genomics\"\n\"39\",\"ipcmain\"\n\"39\",\"offsetheight\"\n\"39\",\"textacy\"\n\"39\",\"restframeworkmongoengine\"\n\"39\",\"geor\"\n\"39\",\"angular6-json-schema-form\"\n\"39\",\"laravel-paginate\"\n\"39\",\"responsibility\"\n\"39\",\"perlmagick\"\n\"39\",\"android-multiselectlistpreference\"\n\"39\",\"lossy-compression\"\n\"39\",\"cgcolorspace\"\n\"39\",\"property-wrapper-published\"\n\"39\",\"python-zappa\"\n\"39\",\"android-ndk-r7\"\n\"39\",\"mox\"\n\"39\",\"android-jetpack-compose-preview\"\n\"39\",\"textout\"\n\"39\",\"textrenderer\"\n\"39\",\"dunitx\"\n\"39\",\"getlatest\"\n\"39\",\"hyperbolic-function\"\n\"39\",\"geofence\"\n\"39\",\"com-object\"\n\"39\",\"mergeinfo\"\n\"39\",\"petl\"\n\"39\",\"strict-weak-ordering\"\n\"39\",\"spquery\"\n\"39\",\"sports-league-scheduling-problem\"\n\"39\",\"sprache\"\n\"39\",\"stretched\"\n\"39\",\"mortar\"\n\"39\",\"pandora\"\n\"39\",\"mqueue\"\n\"39\",\"iics-di\"\n\"39\",\"multilingual-app-toolkit\"\n\"39\",\"passcode\"\n\"39\",\"qt5.12\"\n\"39\",\"secret-manager\"\n\"39\",\"stay-logged-in\"\n\"39\",\"bazel-aspect\"\n\"39\",\"concurrentskiplistmap\"\n\"39\",\"totality\"\n\"39\",\"webassets\"\n\"39\",\"script-debugging\"\n\"39\",\"gldrawarrays\"\n\"39\",\"componentart\"\n\"39\",\"heyzap\"\n\"39\",\"git-notes\"\n\"39\",\"alipay\"\n\"39\",\"zebble\"\n\"39\",\"flutter-ui\"\n\"39\",\"ihttpasynchandler\"\n\"39\",\"arena-simulation\"\n\"39\",\"font-embedding\"\n\"39\",\"static-factory\"\n\"39\",\"identifiable\"\n\"39\",\"urlmon\"\n\"39\",\"cxxtest\"\n\"39\",\"compiz\"\n\"39\",\"tfsintegrationplatform\"\n\"39\",\"uses-feature\"\n\"39\",\"webcrypto\"\n\"39\",\"starknet\"\n\"39\",\"mcsession\"\n\"39\",\"here-olp\"\n\"39\",\"seedstack\"\n\"39\",\"flutter-pub\"\n\"39\",\"thread-state\"\n\"38\",\"ecmascript-2018\"\n\"38\",\"material-icons\"\n\"38\",\"fedext\"\n\"38\",\"transitivity\"\n\"38\",\"greenmail\"\n\"38\",\"yetanotherforum\"\n\"38\",\"flutter-card\"\n\"38\",\"remote-process\"\n\"38\",\"flutter-column\"\n\"38\",\"class-structure\"\n\"38\",\"react-native-draggable-flatlist\"\n\"38\",\"printthis\"\n\"38\",\"jetpack-compose-modalbottomsheet\"\n\"38\",\"wice-grid\"\n\"38\",\"jet-ef-provider\"\n\"38\",\"litho\"\n\"38\",\"default-constraint\"\n\"38\",\"file-moving\"\n\"38\",\"ssis-2014\"\n\"38\",\"litjson\"\n\"38\",\"graphserviceclient\"\n\"38\",\"priority-web-sdk\"\n\"38\",\"tso\"\n\"38\",\"cloud-security\"\n\"38\",\"react-share\"\n\"38\",\"instance-eval\"\n\"38\",\"materialpageroute\"\n\"38\",\"private-network-access\"\n\"38\",\"xrmtoolbox\"\n\"38\",\"weak-typing\"\n\"38\",\"jaxbelement\"\n\"38\",\"photoviewer\"\n\"38\",\"phptal\"\n\"38\",\"webpack-production\"\n\"38\",\"close-application\"\n\"38\",\"reportparameter\"\n\"38\",\"cmock\"\n\"38\",\"pgmpy\"\n\"38\",\"tell\"\n\"38\",\"github-api-v4\"\n\"38\",\"renderui\"\n\"38\",\"yaf\"\n\"38\",\"srs\"\n\"38\",\"feasibility\"\n\"38\",\"vsm\"\n\"38\",\"cmdline-args\"\n\"38\",\"bash-function\"\n\"38\",\"gfx\"\n\"38\",\"function-literal\"\n\"38\",\"fisher-yates-shuffle\"\n\"38\",\"apimonitor\"\n\"38\",\"birt-emitter\"\n\"38\",\"caspol\"\n\"38\",\"xml-database\"\n\"38\",\"xmpphp\"\n\"38\",\"functional-api\"\n\"38\",\"symfony-dependency-injection\"\n\"38\",\"swt-awt\"\n\"38\",\"pa11y\"\n\"38\",\"add-action\"\n\"38\",\"voip-android\"\n\"38\",\"aem-touch-ui\"\n\"38\",\"owned-types\"\n\"38\",\"swiftui-zstack\"\n\"38\",\"connection-close\"\n\"38\",\"addattribute\"\n\"38\",\"von-neumann\"\n\"38\",\"fuelcms\"\n\"38\",\"python-keyboard\"\n\"38\",\"appcelerator-arrow\"\n\"38\",\"django-bootstrap4\"\n\"38\",\"safari6\"\n\"38\",\"directquery\"\n\"38\",\"c-header\"\n\"38\",\"imasdk\"\n\"38\",\"snowpark\"\n\"38\",\"frozen-columns\"\n\"38\",\"ngmocke2e\"\n\"38\",\"managementeventwatcher\"\n\"38\",\"umijs\"\n\"38\",\"datatableadapters\"\n\"38\",\"datascroller\"\n\"38\",\"palm-os\"\n\"38\",\"nexus-10\"\n\"38\",\"vsinstaller\"\n\"38\",\"fstat\"\n\"38\",\"snk\"\n\"38\",\"const-pointer\"\n\"38\",\"apache-shindig\"\n\"38\",\"pigpio\"\n\"38\",\"filestack\"\n\"38\",\"langchain-agents\"\n\"38\",\"go-sqlite3\"\n\"38\",\"aws-copilot\"\n\"38\",\"record-count\"\n\"38\",\"rsconnect\"\n\"38\",\"albacore\"\n\"38\",\"py-appscript\"\n\"38\",\"candeactivate\"\n\"38\",\"gquery\"\n\"38\",\"pureftpd\"\n\"38\",\"cross-build\"\n\"38\",\"dml-lang\"\n\"38\",\"unnotificationtrigger\"\n\"38\",\"cross-thread\"\n\"38\",\"r-modis\"\n\"38\",\"docker-ingress\"\n\"38\",\"reality-composer-pro\"\n\"38\",\"siri-remote\"\n\"38\",\"routedata\"\n\"38\",\"angular-material-8\"\n\"38\",\"robohelp\"\n\"38\",\"round-slider\"\n\"38\",\"vuexfire\"\n\"38\",\"rook-storage\"\n\"38\",\"ruboto\"\n\"38\",\"velocity-template-language\"\n\"38\",\"opsgenie\"\n\"38\",\"servicepacks\"\n\"38\",\"django-ninja\"\n\"38\",\"windows-server-2008-x64\"\n\"38\",\"fbdialogs\"\n\"38\",\"docker-layer\"\n\"38\",\"ahah\"\n\"38\",\"ais\"\n\"38\",\"django-multilingual\"\n\"38\",\"optparse-applicative\"\n\"38\",\"grails-spring-security\"\n\"38\",\"icedtea\"\n\"38\",\"erlide\"\n\"38\",\"katharsis\"\n\"38\",\"mongodb-geospatial\"\n\"38\",\"spring-kotlin\"\n\"38\",\"botnet\"\n\"38\",\"spring-rsocket\"\n\"38\",\"nsight-compute\"\n\"38\",\"nsapplescript\"\n\"38\",\"gtid\"\n\"38\",\"android-studio-3.1.3\"\n\"38\",\"hosted-blazor-webassembly\"\n\"38\",\"cpanel-xmlapi\"\n\"38\",\"scadalts\"\n\"38\",\"horovod\"\n\"38\",\"entity-framework-extensions\"\n\"38\",\"appirater\"\n\"38\",\"mod-plsql\"\n\"38\",\"interlacing\"\n\"38\",\"appindicator\"\n\"38\",\"kal\"\n\"38\",\"pdf2htmlex\"\n\"38\",\"tuespechkin\"\n\"38\",\"html5-apps\"\n\"38\",\"bluemix-app-scan\"\n\"38\",\"delegatinghandler\"\n\"38\",\"tuareg\"\n\"38\",\"worklight-console\"\n\"38\",\"salesforce-development\"\n\"38\",\"moengage\"\n\"38\",\"epublib\"\n\"38\",\"initialization-order\"\n\"38\",\"jquery-mobile-flipswitch\"\n\"38\",\"onpremises-gateway\"\n\"38\",\"anemone\"\n\"38\",\"bqplot\"\n\"38\",\"sasm\"\n\"38\",\"kotlin-script\"\n\"38\",\"visualgdb\"\n\"38\",\"virtual-table\"\n\"38\",\"jasmine-maven-plugin\"\n\"38\",\"audioflinger\"\n\"38\",\"outlook-filter\"\n\"38\",\"async-hooks\"\n\"38\",\"set-analysis\"\n\"38\",\"windrose\"\n\"38\",\"visual-programming\"\n\"38\",\"facebook-business-manager\"\n\"38\",\"system.transactions\"\n\"38\",\"raygun\"\n\"38\",\"3d.io\"\n\"38\",\"setx\"\n\"38\",\"pyro4\"\n\"38\",\"setimmediate\"\n\"38\",\"kleisli\"\n\"38\",\"system.io.packaging\"\n\"38\",\"signedness\"\n\"38\",\"brew-framework\"\n\"38\",\"bridging\"\n\"38\",\"atomic-design\"\n\"38\",\"formula-editor\"\n\"38\",\"3270\"\n\"38\",\"express-stormpath\"\n\"38\",\"object-relationships\"\n\"38\",\"uds\"\n\"38\",\"buildaction\"\n\"38\",\"objectbrowser\"\n\"38\",\"liberator\"\n\"38\",\"vim-registers\"\n\"38\",\"letrec\"\n\"38\",\"ubuntu-24.04\"\n\"38\",\".git-folder\"\n\"38\",\"pykd\"\n\"38\",\"system-codedom-compiler\"\n\"38\",\"obspy\"\n\"38\",\"vimgrep\"\n\"38\",\"shopp\"\n\"38\",\"abperson\"\n\"38\",\"java2wsdl\"\n\"38\",\"r-daisy\"\n\"38\",\"mail-gem\"\n\"38\",\"c2hs\"\n\"38\",\"express-fileupload\"\n\"38\",\"bulk-delete\"\n\"38\",\"titanium-sdk\"\n\"38\",\"hoogle\"\n\"38\",\"pljava\"\n\"38\",\"tag-soup\"\n\"38\",\"business-connector\"\n\"38\",\"scratchpad\"\n\"38\",\"xades\"\n\"38\",\"moby\"\n\"38\",\"gaia\"\n\"38\",\"xcode6gm\"\n\"38\",\"reducing\"\n\"38\",\"timespec\"\n\"38\",\"exception-notification\"\n\"38\",\"playscape\"\n\"38\",\"contract-first\"\n\"38\",\"dotnet-build\"\n\"38\",\"qwraps2\"\n\"38\",\"notify-send\"\n\"38\",\"openrdf\"\n\"38\",\"wymeditor\"\n\"38\",\"direct3d10\"\n\"38\",\"tagged-corpus\"\n\"38\",\"taocp\"\n\"38\",\"tagbuilder\"\n\"38\",\"redistimeseries\"\n\"38\",\"nsxmlelement\"\n\"38\",\"uicontextualaction\"\n\"38\",\"uicollisionbehavior\"\n\"38\",\"sql-server-2019-express\"\n\"38\",\"homotopy-type-theory\"\n\"38\",\"tinkercad\"\n\"38\",\"rabbitmq-c\"\n\"38\",\"download-speed\"\n\"38\",\"gatttool\"\n\"38\",\"android-device-manager\"\n\"38\",\"gem-bundler\"\n\"38\",\"mobile-center\"\n\"38\",\"xcode-scheme\"\n\"38\",\"c++-actor-framework\"\n\"38\",\"opensplice\"\n\"38\",\"tdictionary\"\n\"38\",\"openstack-glance\"\n\"38\",\"redmon\"\n\"38\",\"game-automation\"\n\"38\",\"excel.application\"\n\"38\",\"cusp-library\"\n\"38\",\"escape-analysis\"\n\"38\",\"text-rotation\"\n\"38\",\"tomcat-manager\"\n\"38\",\"perforce-branch-spec\"\n\"38\",\"loggerfactory\"\n\"38\",\"colormatrixfilter\"\n\"38\",\"prophecy\"\n\"38\",\"qbo3\"\n\"38\",\"node-java\"\n\"38\",\"restbed\"\n\"38\",\"laterjs\"\n\"38\",\"mpir\"\n\"38\",\"georss\"\n\"38\",\"android-job\"\n\"38\",\"android-jetpack-compose-scaffold\"\n\"38\",\"office365-rest-client\"\n\"38\",\"nintendo-ds\"\n\"38\",\"node-forge\"\n\"38\",\"acl2\"\n\"38\",\"string.xml\"\n\"38\",\"responsys\"\n\"38\",\"proof-general\"\n\"38\",\"android-maps-extensions\"\n\"38\",\"tone-analyzer\"\n\"38\",\"angular-cache\"\n\"38\",\"google-datatable\"\n\"38\",\"resolution-independence\"\n\"38\",\"ejml\"\n\"38\",\"cgdb\"\n\"38\",\"ejb-jar.xml\"\n\"38\",\"search-guard\"\n\"38\",\"searchkit\"\n\"38\",\"textx\"\n\"38\",\"webkit-animation\"\n\"38\",\"ariadne-graphql\"\n\"38\",\"urxvt\"\n\"38\",\"zend-optimizer\"\n\"38\",\"security-warning\"\n\"38\",\"autocad-scripts\"\n\"38\",\"usb-descriptor\"\n\"38\",\"zeit-pkg\"\n\"38\",\"tibco-designer\"\n\"38\",\"to-timestamp\"\n\"38\",\"zeek\"\n\"38\",\"user-defined-aggregate\"\n\"38\",\"idref\"\n\"38\",\"powervr-sgx\"\n\"38\",\"spark-launcher\"\n\"38\",\"presentationml\"\n\"38\",\"imageflow\"\n\"38\",\"subapplication\"\n\"38\",\"subcommand\"\n\"38\",\"emacs-semantic\"\n\"38\",\"parameter-sets\"\n\"38\",\"linq-to-json\"\n\"38\",\"pass-by-rvalue-reference\"\n\"38\",\"scrutinizer\"\n\"38\",\"sea-orm\"\n\"38\",\"qsizepolicy\"\n\"38\",\"focusin\"\n\"38\",\"google-ranking\"\n\"38\",\"mediabrowserservice\"\n\"38\",\"quarkus-qute\"\n\"38\",\"maven-jib\"\n\"38\",\"custom-url-protocol\"\n\"38\",\"d3dimage\"\n\"38\",\"pardiso\"\n\"38\",\"flutter-run\"\n\"38\",\"alignas\"\n\"38\",\"scrollreveal.js\"\n\"38\",\"maven-nar-plugin\"\n\"37\",\"phpdebugbar\"\n\"37\",\"pg-stat-statements\"\n\"37\",\"multiple-assignment\"\n\"37\",\"ddmathparser\"\n\"37\",\"live-cd\"\n\"37\",\"proactive\"\n\"37\",\"flexibility\"\n\"37\",\"react-navigation-top-tabs\"\n\"37\",\"tsify\"\n\"37\",\"flightphp\"\n\"37\",\"multiple-login\"\n\"37\",\"babel-eslint\"\n\"37\",\"processlist\"\n\"37\",\"multivalue-database\"\n\"37\",\"ts-morph\"\n\"37\",\"flup\"\n\"37\",\"debconf\"\n\"37\",\"ggimage\"\n\"37\",\"srp-protocol\"\n\"37\",\"react-native-sentry\"\n\"37\",\"cloo\"\n\"37\",\"whatsapp-stickers\"\n\"37\",\"skcameranode\"\n\"37\",\"squirrelmail\"\n\"37\",\"cmake-presets\"\n\"37\",\"sqslistener\"\n\"37\",\"intellicode\"\n\"37\",\"baml\"\n\"37\",\"dbms-metadata\"\n\"37\",\"liquid-haskell\"\n\"37\",\"git-fsck\"\n\"37\",\"skadnetwork\"\n\"37\",\"git-daemon\"\n\"37\",\"flutter-ffmpeg\"\n\"37\",\"tensorboardx\"\n\"37\",\"flutter-downloader\"\n\"37\",\"material-design-icons\"\n\"37\",\"remotipart\"\n\"37\",\"wicket-tester\"\n\"37\",\"cockpit\"\n\"37\",\"sizetocontent\"\n\"37\",\"phpgrid\"\n\"37\",\"flex-charting\"\n\"37\",\"clojureclr\"\n\"37\",\"slidy\"\n\"37\",\"transmitfile\"\n\"37\",\"slidesjs\"\n\"37\",\"incremental-load\"\n\"37\",\"ngcloak\"\n\"37\",\"after-create\"\n\"37\",\"kubernetes-hpa\"\n\"37\",\"mail-queue\"\n\"37\",\"fscrawler\"\n\"37\",\"biztalk-pipelines\"\n\"37\",\"smf-forum\"\n\"37\",\"switchyard\"\n\"37\",\"unapply\"\n\"37\",\"inboxsdk\"\n\"37\",\"xgbregressor\"\n\"37\",\"xmlsocket\"\n\"37\",\"fsolve\"\n\"37\",\"pythonbrew\"\n\"37\",\"voldemort\"\n\"37\",\"child-fragment\"\n\"37\",\"swiftpm\"\n\"37\",\"semi-join\"\n\"37\",\"confluence-macros\"\n\"37\",\"snap-in\"\n\"37\",\"jsr296\"\n\"37\",\"laravel-components\"\n\"37\",\"ccspritebatchnode\"\n\"37\",\"fiware-keyrock\"\n\"37\",\"unified-memory\"\n\"37\",\"nhibernate-cascade\"\n\"37\",\"chimp.js\"\n\"37\",\"dbcommand\"\n\"37\",\"python-pdfreader\"\n\"37\",\"binomial-heap\"\n\"37\",\"django-4.1\"\n\"37\",\"finite-difference\"\n\"37\",\"findwindowex\"\n\"37\",\"imgscalr\"\n\"37\",\"sender-id\"\n\"37\",\"paint.net\"\n\"37\",\"frequency-table\"\n\"37\",\"binary-bomb\"\n\"37\",\"xdoc\"\n\"37\",\"csi\"\n\"37\",\"kepserverex\"\n\"37\",\"aws-sdk-go-v2\"\n\"37\",\"iced-coffeescript\"\n\"37\",\"keyhook\"\n\"37\",\"roxy-fileman\"\n\"37\",\"dnode\"\n\"37\",\"rpxnow\"\n\"37\",\"upsetplot\"\n\"37\",\"uploadifive\"\n\"37\",\"canopy-web-testing\"\n\"37\",\"pudb\"\n\"37\",\"kie-wb\"\n\"37\",\"aviarc\"\n\"37\",\"jfxpanel\"\n\"37\",\"data-corruption\"\n\"37\",\"sequence-analysis\"\n\"37\",\"servletconfig\"\n\"37\",\"serialscroll\"\n\"37\",\"icicles\"\n\"37\",\"kiba-etl\"\n\"37\",\"fanotify\"\n\"37\",\"ruby-block\"\n\"37\",\"dmesg\"\n\"37\",\"databricks-rest-api\"\n\"37\",\"optimizer-hints\"\n\"37\",\"ibm-cloud-code-engine\"\n\"37\",\"dash-leaflet\"\n\"37\",\"hyperledger-fabric-sdk-java\"\n\"37\",\"rmongo\"\n\"37\",\"camel-test\"\n\"37\",\"aws-directory-services\"\n\"37\",\"joyent\"\n\"37\",\"fastclick.js\"\n\"37\",\"sitebricks\"\n\"37\",\"camel-jms\"\n\"37\",\"django-smart-selects\"\n\"37\",\"criteriabuilder\"\n\"37\",\"aws-iot-analytics\"\n\"37\",\"session-state-provider\"\n\"37\",\"roundhouse\"\n\"37\",\"inetd\"\n\"37\",\"grand-theft-auto\"\n\"37\",\"django-leaflet\"\n\"37\",\"jfrog-mission-control\"\n\"37\",\"angular-migration\"\n\"37\",\"rspec-expectations\"\n\"37\",\"aws-cloudmap\"\n\"37\",\"angular-material-theming\"\n\"37\",\"angular-xeditable\"\n\"37\",\"bootstrap-wysihtml5\"\n\"37\",\"blessed\"\n\"37\",\"in-memory-tables\"\n\"37\",\"jquery-query-builder\"\n\"37\",\"svgkit\"\n\"37\",\"navigation-compose\"\n\"37\",\"post-update\"\n\"37\",\"gtktextview\"\n\"37\",\"sane\"\n\"37\",\"nsconnection\"\n\"37\",\"support-v4\"\n\"37\",\"wsman\"\n\"37\",\"corflags\"\n\"37\",\"hotplugging\"\n\"37\",\"correlated\"\n\"37\",\"jquery-dropkick\"\n\"37\",\"gud\"\n\"37\",\"dynamic-code\"\n\"37\",\"type-annotation\"\n\"37\",\"inline-variable\"\n\"37\",\"twemproxy\"\n\"37\",\"samplegrabber\"\n\"37\",\"ean-13\"\n\"37\",\"wpf-positioning\"\n\"37\",\"nsfilecoordinator\"\n\"37\",\"pathtoolongexception\"\n\"37\",\"enterframeevent\"\n\"37\",\"gtkd\"\n\"37\",\"aws-transfer-family\"\n\"37\",\"azure-analytics\"\n\"37\",\"correspondence-analysis\"\n\"37\",\"episerver-6-r2\"\n\"37\",\"erlangweb\"\n\"37\",\"bluetooth-printing\"\n\"37\",\"bootstrap-treeview\"\n\"37\",\"cpu-load\"\n\"37\",\"nclob\"\n\"37\",\"bmi\"\n\"37\",\"position-dodge\"\n\"37\",\"swift-data-relationship\"\n\"37\",\"boost-smart-ptr\"\n\"37\",\"coursier\"\n\"37\",\"boost-tokenizer\"\n\"37\",\"portable-areas\"\n\"37\",\"html-formhandler\"\n\"37\",\"dynamicpdf\"\n\"37\",\"grpcio\"\n\"37\",\"simbl\"\n\"37\",\"cra\"\n\"37\",\"html.hiddenfor\"\n\"37\",\"input-language\"\n\"37\",\"internal-link\"\n\"37\",\"infinite-carousel\"\n\"37\",\"jquery.repeater\"\n\"37\",\"oneway\"\n\"37\",\"jansson\"\n\"37\",\"typescript2.4\"\n\"37\",\"vincent\"\n\"37\",\"outerheight\"\n\"37\",\"least-common-ancestor\"\n\"37\",\"viewrendering\"\n\"37\",\"astroquery\"\n\"37\",\"magicalrecord-2.2\"\n\"37\",\"facebook-graph-api-v2.3\"\n\"37\",\"3d-texture\"\n\"37\",\"libfreenect2\"\n\"37\",\"libfuzzer\"\n\"37\",\"exrm\"\n\"37\",\"aurelia-dialog\"\n\"37\",\"visual-testing\"\n\"37\",\"browsable\"\n\"37\",\"magic-string\"\n\"37\",\"goinstant\"\n\"37\",\"libman\"\n\"37\",\"systemmodeler\"\n\"37\",\"shopt\"\n\"37\",\"output-caching\"\n\"37\",\"jaunt-api\"\n\"37\",\"typed-factory-facility\"\n\"37\",\"shotgun\"\n\"37\",\"ancs\"\n\"37\",\"outputdebugstring\"\n\"37\",\"7-bit\"\n\"37\",\"mip-sdk\"\n\"37\",\"letters-and-numbers\"\n\"37\",\"pyfacebook\"\n\"37\",\"uat\"\n\"37\",\"wolkenkit\"\n\"37\",\"nvm-windows\"\n\"37\",\"ory\"\n\"37\",\"pysyft\"\n\"37\",\"jawr\"\n\"37\",\"rdfstore\"\n\"37\",\"asp.net-customcontrol\"\n\"37\",\"azure-managed-database\"\n\"37\",\"sqlalchemy-utils\"\n\"37\",\"plug-and-play\"\n\"37\",\"jacl\"\n\"37\",\"notorm\"\n\"37\",\"gumstix\"\n\"37\",\"gumbo\"\n\"37\",\"nodiscard\"\n\"37\",\"vertex-ai-search\"\n\"37\",\"open-graph-beta\"\n\"37\",\"noreturn\"\n\"37\",\"spsite\"\n\"37\",\"tokio-postgres\"\n\"37\",\"normalizing\"\n\"37\",\"geneos\"\n\"37\",\"scala-streams\"\n\"37\",\"nstrackingarea\"\n\"37\",\"j2ssh\"\n\"37\",\"gaussian-mixture-model\"\n\"37\",\"hmatrix\"\n\"37\",\"c++builder-10-seattle\"\n\"37\",\"tlabel\"\n\"37\",\"isight\"\n\"37\",\"dimension-reduction\"\n\"37\",\"uikit-state-preservation\"\n\"37\",\"bundle-layout\"\n\"37\",\"openstruct\"\n\"37\",\"dotnetnuke-8\"\n\"37\",\"ragged-tensors\"\n\"37\",\"histplot\"\n\"37\",\"pointcuts\"\n\"37\",\"mod-autoindex\"\n\"37\",\"asp.net-4.6\"\n\"37\",\"poeaa\"\n\"37\",\"registering\"\n\"37\",\"directcast\"\n\"37\",\"uiprintpagerenderer\"\n\"37\",\"exception-logging\"\n\"37\",\"azure-private-dns-zone\"\n\"37\",\"mkplacemark\"\n\"37\",\"qquickview\"\n\"37\",\"getderivedstatefromprops\"\n\"37\",\"perfino\"\n\"37\",\"cffunction\"\n\"37\",\"cfdata\"\n\"37\",\"spectrumjs\"\n\"37\",\"mousetrap\"\n\"37\",\"mousekeyhook\"\n\"37\",\"charm-crypto\"\n\"37\",\"perfecto\"\n\"37\",\"text-generation\"\n\"37\",\"memory-visibility\"\n\"37\",\"char16-t\"\n\"37\",\"angular2-google-maps\"\n\"37\",\"react-leaflet-v4\"\n\"37\",\"zumero\"\n\"37\",\"httpful\"\n\"37\",\"lstlisting\"\n\"37\",\"odo\"\n\"37\",\"google-cloud-transcoder\"\n\"37\",\"access-rules\"\n\"37\",\"resuming-training\"\n\"37\",\"ios3.0\"\n\"37\",\"excel4node\"\n\"37\",\"eruby\"\n\"37\",\"cephfs\"\n\"37\",\"text-chunking\"\n\"37\",\"acts-as-tenant\"\n\"37\",\"google-cloud-dataproc-serverless\"\n\"37\",\"persistent-data\"\n\"37\",\"memcachedb\"\n\"37\",\"qpalette\"\n\"37\",\"log4jdbc\"\n\"37\",\"stream-cipher\"\n\"37\",\"messageid\"\n\"37\",\"stochastic-gradient\"\n\"37\",\"custom-operator\"\n\"37\",\"storage-file-share\"\n\"37\",\"google-codelab\"\n\"37\",\"elasticsearch-spark\"\n\"37\",\"texstudio\"\n\"37\",\"com-interface\"\n\"37\",\"maven-toolchains-plugin\"\n\"37\",\"usb-hostcontroller\"\n\"37\",\"iis-express-10\"\n\"37\",\"begincollectionitem\"\n\"37\",\"tracepoint\"\n\"37\",\"avassetwriterinput\"\n\"37\",\"tikzdevice\"\n\"37\",\"lightmode\"\n\"37\",\"shell-namespace-extension\"\n\"37\",\"queueuserworkitem\"\n\"37\",\"bfo\"\n\"37\",\"mu-law\"\n\"37\",\"availability-group\"\n\"37\",\"qstatusbar\"\n\"37\",\"qstringlist\"\n\"37\",\"youtube-player-flutter\"\n\"37\",\"pressable\"\n\"37\",\"webdriver-io-v4\"\n\"37\",\"scriptbundle\"\n\"37\",\"mediafire\"\n\"37\",\"emqx\"\n\"37\",\"query-designer\"\n\"37\",\"help-system\"\n\"37\",\"idris2\"\n\"37\",\"armbian\"\n\"37\",\"automapper-10\"\n\"37\",\"pg-jdbc\"\n\"37\",\"maven-exec-plugin\"\n\"37\",\"end-user\"\n\"37\",\"weatherkit\"\n\"37\",\"text-width\"\n\"37\",\"spack\"\n\"37\",\"solr-search\"\n\"37\",\"hfile\"\n\"37\",\"amazon-ivs\"\n\"37\",\"amazon-forecast\"\n\"37\",\"webbroker\"\n\"37\",\"zerofill\"\n\"37\",\"gitx\"\n\"37\",\"secretsmanager\"\n\"37\",\"usefetch\"\n\"37\",\"powershell-sdk\"\n\"37\",\"webob\"\n\"37\",\"cyk\"\n\"37\",\"arrayiterator\"\n\"37\",\"gmailr\"\n\"37\",\"threecsg\"\n\"37\",\"limiting\"\n\"37\",\"subject-alternative-name\"\n\"37\",\"flutter-linux\"\n\"37\",\"image-effects\"\n\"37\",\"glibmm\"\n\"36\",\"tensor2tensor\"\n\"36\",\"figsize\"\n\"36\",\"base-path\"\n\"36\",\"react-server\"\n\"36\",\"cisco-jtapi\"\n\"36\",\"editview\"\n\"36\",\"templatetag\"\n\"36\",\"tree-nodes\"\n\"36\",\"telosys\"\n\"36\",\"ansi-sql-92\"\n\"36\",\"anychart-8.2\"\n\"36\",\"phped\"\n\"36\",\"react-native-picker-select\"\n\"36\",\"git-history-graph\"\n\"36\",\"jetty-12\"\n\"36\",\"insert-image\"\n\"36\",\"instasharp\"\n\"36\",\"instruction-reordering\"\n\"36\",\"insertion-order\"\n\"36\",\"ss\"\n\"36\",\"insertonsubmit\"\n\"36\",\"ssms-18\"\n\"36\",\"edge-tpu\"\n\"36\",\"mvcroutehandler\"\n\"36\",\"primeng-dialog\"\n\"36\",\"yagmail\"\n\"36\",\"reporting-services-2016\"\n\"36\",\"mat-icon\"\n\"36\",\"match-types\"\n\"36\",\"slapd\"\n\"36\",\"telerik-charting\"\n\"36\",\"ansible-collections\"\n\"36\",\"backbone-stickit\"\n\"36\",\"renderdoc\"\n\"36\",\"rendercontrol\"\n\"36\",\"mathlink\"\n\"36\",\"fluid-images\"\n\"36\",\"cleaned-data\"\n\"36\",\"xslf\"\n\"36\",\"release-notes\"\n\"36\",\"flutter-datatable\"\n\"36\",\"gravitee\"\n\"36\",\"flutter-circularprogressindicator\"\n\"36\",\"groovydoc\"\n\"36\",\"vtk.js\"\n\"36\",\"little-proxy\"\n\"36\",\"eclipse-rse\"\n\"36\",\"phpstorm-2018.1\"\n\"36\",\"imagemagick-identify\"\n\"36\",\"jsr75\"\n\"36\",\"ladder-logic\"\n\"36\",\"jsr305\"\n\"36\",\"imagemagick-montage\"\n\"36\",\"smart-mobile-studio\"\n\"36\",\"imageshack\"\n\"36\",\"fuzzer\"\n\"36\",\"softirq\"\n\"36\",\"swup\"\n\"36\",\"rusqlite\"\n\"36\",\"dbg\"\n\"36\",\"kubernetes-namespace\"\n\"36\",\"future-proof\"\n\"36\",\"datastax-node-driver\"\n\"36\",\"python-docker\"\n\"36\",\"cinterop\"\n\"36\",\"ngdoc\"\n\"36\",\"displayformat\"\n\"36\",\"pin-code\"\n\"36\",\"circom\"\n\"36\",\"semantic-logging\"\n\"36\",\"sendbeacon\"\n\"36\",\"biztalk-bam\"\n\"36\",\"xero\"\n\"36\",\"umfpack\"\n\"36\",\"xmlencoder\"\n\"36\",\"function-templates-overloading\"\n\"36\",\"unify\"\n\"36\",\"confirm-dialog\"\n\"36\",\"first-class\"\n\"36\",\"ng-filter\"\n\"36\",\"apache-spark-2.2\"\n\"36\",\"ftputil\"\n\"36\",\"ngx-daterangepicker-material\"\n\"36\",\"paginateddatatable\"\n\"36\",\"makeappx\"\n\"36\",\"constraint-kinds\"\n\"36\",\"bitbucket-aws-code-deploy\"\n\"36\",\"unfuddle\"\n\"36\",\"pihole\"\n\"36\",\"confuserex\"\n\"36\",\"findinfiles\"\n\"36\",\"pagerfanta\"\n\"36\",\"facebook-php-webdriver\"\n\"36\",\"grako\"\n\"36\",\"ibm-cloud-kubernetes\"\n\"36\",\"dlquery\"\n\"36\",\"grapevine\"\n\"36\",\"psr-1\"\n\"36\",\"simplemde\"\n\"36\",\"mysql-error-1067\"\n\"36\",\"rubberduck\"\n\"36\",\"optimistic\"\n\"36\",\"validate-request\"\n\"36\",\"metricsql\"\n\"36\",\"ibm-app-connect\"\n\"36\",\"fastlane-snapshot\"\n\"36\",\"docker-repository\"\n\"36\",\"cakephp-2.8\"\n\"36\",\"optional-variables\"\n\"36\",\"docker-tag\"\n\"36\",\"myst\"\n\"36\",\"rowlex\"\n\"36\",\"django-rest-framework-gis\"\n\"36\",\"fancyimpute\"\n\"36\",\"aggregate-filter\"\n\"36\",\"roberta\"\n\"36\",\"method-declaration\"\n\"36\",\"data-caching\"\n\"36\",\"mysql-odbc-connector\"\n\"36\",\"update-all\"\n\"36\",\"cryengine\"\n\"36\",\"react-table-v8\"\n\"36\",\"gpu-atomics\"\n\"36\",\"gpu-constant-memory\"\n\"36\",\"single-dispatch\"\n\"36\",\"single-file\"\n\"36\",\"series-40\"\n\"36\",\"pry-rails\"\n\"36\",\"cross-server\"\n\"36\",\"windows-1251\"\n\"36\",\"upload-max-filesize\"\n\"36\",\"react-strictmode\"\n\"36\",\"sinch-verification\"\n\"36\",\"pubchem\"\n\"36\",\"django-rosetta\"\n\"36\",\"keyref\"\n\"36\",\"aws-code-deploy-appspec\"\n\"36\",\"wildfly-12\"\n\"36\",\"rowheader\"\n\"36\",\"realm-database\"\n\"36\",\"animationcontroller\"\n\"36\",\"unload\"\n\"36\",\"nestjs-testing\"\n\"36\",\"twilio-python\"\n\"36\",\"wsdl2code\"\n\"36\",\"spring-cloud-zookeeper\"\n\"36\",\"tuner\"\n\"36\",\"input-filter\"\n\"36\",\"nba-api\"\n\"36\",\"workflow-engine\"\n\"36\",\"android-tiramisu\"\n\"36\",\"dependency-tree\"\n\"36\",\"desktop-wallpaper\"\n\"36\",\"android-spannable\"\n\"36\",\"gulp-replace\"\n\"36\",\"boost-xpressive\"\n\"36\",\"er-diagram\"\n\"36\",\"eraser\"\n\"36\",\"nsarchiving\"\n\"36\",\"spring-cloud-gcp-bigquery\"\n\"36\",\"cornerstonejs\"\n\"36\",\"azure-blockchain-workbench\"\n\"36\",\"pdf2swf\"\n\"36\",\"hotswapagent\"\n\"36\",\"ttml\"\n\"36\",\"application-name\"\n\"36\",\"defunct\"\n\"36\",\"postgres.app\"\n\"36\",\"worker-loader\"\n\"36\",\"internal-compiler-error\"\n\"36\",\"android-side-navigation\"\n\"36\",\"bootstrap-studio\"\n\"36\",\"neo4j-embedded\"\n\"36\",\"post-quantum-cryptography\"\n\"36\",\"app-lab\"\n\"36\",\"errortemplate\"\n\"36\",\"sap-ariba\"\n\"36\",\"botdetect\"\n\"36\",\"android-slider\"\n\"36\",\"coreference-resolution\"\n\"36\",\"coveo\"\n\"36\",\"azure-bastion\"\n\"36\",\"android-shell\"\n\"36\",\"app-service-environment\"\n\"36\",\"oat++\"\n\"36\",\"windsor-3.0\"\n\"36\",\"libserial\"\n\"36\",\"fabric-beta\"\n\"36\",\"android-activitymanager\"\n\"36\",\"pyinvoke\"\n\"36\",\"bucardo\"\n\"36\",\"madexcept\"\n\"36\",\"vitepress\"\n\"36\",\"ordered-map\"\n\"36\",\"system.addin\"\n\"36\",\"lucene-highlighter\"\n\"36\",\"facebook-app-center\"\n\"36\",\"browser-bugs\"\n\"36\",\"code-visualization\"\n\"36\",\"shutter\"\n\"36\",\"net-reactor\"\n\"36\",\"krpano\"\n\"36\",\"audio-video-sync\"\n\"36\",\"rapidshare\"\n\"36\",\"browserid\"\n\"36\",\"setuptools-scm\"\n\"36\",\"kr-c\"\n\"36\",\"siena\"\n\"36\",\"2.5d\"\n\"36\",\"asyncdata\"\n\"36\",\"object-diagram\"\n\"36\",\"atg-droplet\"\n\"36\",\"minimum-size\"\n\"36\",\"go-iris\"\n\"36\",\"amba\"\n\"36\",\"konga\"\n\"36\",\"vmalloc\"\n\"36\",\"vmdk\"\n\"36\",\"microstream\"\n\"36\",\"rfc3161\"\n\"36\",\"typescript-module-resolution\"\n\"36\",\"android-1.5-cupcake\"\n\"36\",\"extract-error-message\"\n\"36\",\"shader-storage-buffer\"\n\"36\",\"viewpropertyanimator\"\n\"36\",\"osx-tiger\"\n\"36\",\"pynamodb\"\n\"36\",\"knopflerfish\"\n\"36\",\"otl\"\n\"36\",\"pymatgen\"\n\"36\",\"braintree-rails\"\n\"36\",\"asynchronous-wcf-call\"\n\"36\",\"amp-img\"\n\"36\",\"pyo\"\n\"36\",\"networkit\"\n\"36\",\"code-collaborator\"\n\"36\",\"kleene-star\"\n\"36\",\"pyjade\"\n\"36\",\"mobileprovision\"\n\"36\",\"npm-version\"\n\"36\",\"nvidia-smi\"\n\"36\",\"android-capture\"\n\"36\",\"scoreloop\"\n\"36\",\"itemeditor\"\n\"36\",\"plr\"\n\"36\",\"android-compose-layout\"\n\"36\",\"iso-8859-2\"\n\"36\",\"table-plus\"\n\"36\",\"nuxt-strapi\"\n\"36\",\"scalardb\"\n\"36\",\"rackattack\"\n\"36\",\"android-drm\"\n\"36\",\"scope-chain\"\n\"36\",\"dot-source\"\n\"36\",\"scoop-installer\"\n\"36\",\"drawingbrush\"\n\"36\",\"gwt-tablayoutpanel\"\n\"36\",\"tabletop.js\"\n\"36\",\"controltemplates\"\n\"36\",\"openoffice-impress\"\n\"36\",\"hacker-news-api\"\n\"36\",\"r2dbc-mysql\"\n\"36\",\"xcarchive\"\n\"36\",\"timing-attack\"\n\"36\",\"nushell\"\n\"36\",\"scalatags\"\n\"36\",\"excel-reader\"\n\"36\",\"buster.js\"\n\"36\",\"gen-class\"\n\"36\",\"itextg\"\n\"36\",\"azure-vm-templates\"\n\"36\",\"mknod\"\n\"36\",\"iview\"\n\"36\",\"azure-spring-boot\"\n\"36\",\"time-precision\"\n\"36\",\"sqlj\"\n\"36\",\"nopcommerce-4.0\"\n\"36\",\"gcc11\"\n\"36\",\"c10k\"\n\"36\",\"iwconfig\"\n\"36\",\"gcloud-java\"\n\"36\",\"gdrive\"\n\"36\",\"geektool\"\n\"36\",\"qfont\"\n\"36\",\"google-container-optimized-os\"\n\"36\",\"chardet\"\n\"36\",\"activity-streams\"\n\"36\",\"spin.js\"\n\"36\",\"httperf\"\n\"36\",\"android-jetpack-compose-animation\"\n\"36\",\"react-image-crop\"\n\"36\",\"httpbrowsercapabilities\"\n\"36\",\"react-async\"\n\"36\",\"httplistenerrequest\"\n\"36\",\"cfengine\"\n\"36\",\"zurb-foundation-apps\"\n\"36\",\"android-mipmap\"\n\"36\",\"moq-3\"\n\"36\",\"terraform-provider-openstack\"\n\"36\",\"cssnext\"\n\"36\",\"spray-routing\"\n\"36\",\"motoko\"\n\"36\",\"react-native-contacts\"\n\"36\",\"android-managed-profile\"\n\"36\",\"tetrahedra\"\n\"36\",\"stripe-tax\"\n\"36\",\"activetcl\"\n\"36\",\"meeting-request\"\n\"36\",\"pyudev\"\n\"36\",\"lua-c++-connection\"\n\"36\",\"qlocalsocket\"\n\"36\",\"laravel-translatable\"\n\"36\",\"ios-library\"\n\"36\",\"liferay-7.2\"\n\"36\",\"parsoid\"\n\"36\",\"cxf-xjc-plugin\"\n\"36\",\"automatic-semicolon-insertion\"\n\"36\",\"qsqlquerymodel\"\n\"36\",\"aria-live\"\n\"36\",\"qt-necessitas\"\n\"36\",\"msvcr90.dll\"\n\"36\",\"iidentity\"\n\"36\",\"stat-density2d\"\n\"36\",\"tgrid\"\n\"36\",\"zebra\"\n\"36\",\"amazon-mobile-hub\"\n\"36\",\"partial-functions\"\n\"36\",\"usercake\"\n\"36\",\"premultiplied-alpha\"\n\"36\",\"ytplayer\"\n\"36\",\"gnu-sed\"\n\"36\",\"amazon-kendra\"\n\"36\",\"parse-framework\"\n\"36\",\"zgc\"\n\"36\",\"git-sparse-checkout\"\n\"36\",\"msmessage\"\n\"36\",\"sec\"\n\"36\",\"space-partitioning\"\n\"36\",\"zk-grid\"\n\"36\",\"illegalmonitorstateexcep\"\n\"36\",\"tortoisemerge\"\n\"36\",\"config.json\"\n\"36\",\"stdlaunder\"\n\"36\",\"z-order-curve\"\n\"36\",\"tightvnc\"\n\"36\",\"mss\"\n\"36\",\"webmachine\"\n\"36\",\"for-else\"\n\"36\",\"sonarqube-5.4\"\n\"36\",\"shields.io\"\n\"36\",\"hermes-jms\"\n\"36\",\"alice-fixtures\"\n\"36\",\"zend-locale\"\n\"36\",\"asciimath\"\n\"36\",\"allegro-cl\"\n\"36\",\"enable-shared-from-this\"\n\"36\",\"parallel.foreachasync\"\n\"36\",\"mruby\"\n\"36\",\"strtr\"\n\"36\",\"idx\"\n\"36\",\"stm32f3\"\n\"36\",\"alphabetized\"\n\"36\",\"hdrimages\"\n\"35\",\"clrmd\"\n\"35\",\"mvcgrid.net\"\n\"35\",\"dcc32\"\n\"35\",\"grit\"\n\"35\",\"babun\"\n\"35\",\"dbn\"\n\"35\",\"phpcas\"\n\"35\",\"badi\"\n\"35\",\"yocto-wic\"\n\"35\",\"ckeditor5-react\"\n\"35\",\"ckeditor5-plugin\"\n\"35\",\"yfiles\"\n\"35\",\"ebook-reader\"\n\"35\",\"clsql\"\n\"35\",\"liquibase-maven-plugin\"\n\"35\",\"render-to-response\"\n\"35\",\"where-object\"\n\"35\",\"trilinos\"\n\"35\",\"xsb\"\n\"35\",\"git-lfs-migrate\"\n\"35\",\"getstate\"\n\"35\",\"phpjs\"\n\"35\",\"gitlab-autodevops\"\n\"35\",\"remove-method\"\n\"35\",\"flutter-drawer\"\n\"35\",\"gitlab-7\"\n\"35\",\"cleartext\"\n\"35\",\"php-pthread\"\n\"35\",\"listlabel\"\n\"35\",\"standardjs\"\n\"35\",\"xtensa\"\n\"35\",\"graph-notebook\"\n\"35\",\"little-o\"\n\"35\",\"phpgraphlib\"\n\"35\",\"process-monitor\"\n\"35\",\"debezium-connect\"\n\"35\",\"reprex\"\n\"35\",\"reinforced-typings\"\n\"35\",\"lmplot\"\n\"35\",\"ggnetwork\"\n\"35\",\"git-fast-import\"\n\"35\",\"react-native-screens\"\n\"35\",\"temboo\"\n\"35\",\"ssis-2005\"\n\"35\",\"ffdshow\"\n\"35\",\"loadjava\"\n\"35\",\"baidu-map\"\n\"35\",\"sslerrorhandler\"\n\"35\",\"antdv\"\n\"35\",\"dat-protocol\"\n\"35\",\"maphilight\"\n\"35\",\"chipset\"\n\"35\",\"vsdoc\"\n\"35\",\"pallet\"\n\"35\",\"pkill\"\n\"35\",\"day-cq\"\n\"35\",\"filesystem-access\"\n\"35\",\"filestreams\"\n\"35\",\"runkit\"\n\"35\",\"next-connect\"\n\"35\",\"nextpeer\"\n\"35\",\"mapdeck\"\n\"35\",\"jstorage\"\n\"35\",\"apache-sedona\"\n\"35\",\"kubernetes-container\"\n\"35\",\"apache-pivot\"\n\"35\",\"snomed-ct\"\n\"35\",\"uivewcontroller\"\n\"35\",\"r-usethis\"\n\"35\",\"mariadb-connector-c\"\n\"35\",\"incremental-search\"\n\"35\",\"carmen\"\n\"35\",\"vscode-problem-matcher\"\n\"35\",\"jsonassert\"\n\"35\",\"lanterna\"\n\"35\",\"displot\"\n\"35\",\"adaptive-icon\"\n\"35\",\"ftplugin\"\n\"35\",\"markermanager\"\n\"35\",\"blackfire\"\n\"35\",\"symbol-not-found\"\n\"35\",\"apollo-gateway\"\n\"35\",\"ozeki\"\n\"35\",\"firepath\"\n\"35\",\"smartsheet-api-1.1\"\n\"35\",\"djangocms-text-ckeditor\"\n\"35\",\"switchery\"\n\"35\",\"ngx-editor\"\n\"35\",\"jsonforms\"\n\"35\",\"alchemy-cms\"\n\"35\",\"dlq\"\n\"35\",\"url-action\"\n\"35\",\"prvalue\"\n\"35\",\"valign\"\n\"35\",\"vendors\"\n\"35\",\"pyad\"\n\"35\",\"reconciliation\"\n\"35\",\"pulumi-azure\"\n\"35\",\"simple-xml-converter\"\n\"35\",\"ora-00923\"\n\"35\",\"rogue-wave\"\n\"35\",\"r-table\"\n\"35\",\"rocket\"\n\"35\",\"journey\"\n\"35\",\"jqchart\"\n\"35\",\"mysql4\"\n\"35\",\"vapid\"\n\"35\",\"microsoft-expression-web\"\n\"35\",\"mymaps\"\n\"35\",\"mysqladministrator\"\n\"35\",\"faker.js\"\n\"35\",\"ag-grid-validation\"\n\"35\",\"rmaps\"\n\"35\",\"pv\"\n\"35\",\"root-node\"\n\"35\",\"validationmessage\"\n\"35\",\"nanopi\"\n\"35\",\"database-dump\"\n\"35\",\"grahams-scan\"\n\"35\",\"camunda-plugin\"\n\"35\",\"oracle-apex21.2\"\n\"35\",\"dnspy\"\n\"35\",\"camping\"\n\"35\",\"n8n\"\n\"35\",\"microsoft.ink\"\n\"35\",\"ibm-information-server\"\n\"35\",\"ajaxsubmit\"\n\"35\",\"cakephp-2.9\"\n\"35\",\"namespace-organisation\"\n\"35\",\"datacontracts\"\n\"35\",\"roaming-profile\"\n\"35\",\"unmanagedexports\"\n\"35\",\"sitecore-speak-ui\"\n\"35\",\"wcf-discovery\"\n\"35\",\"vcbuild\"\n\"35\",\"django-two-factor-auth\"\n\"35\",\"django-hvad\"\n\"35\",\"rector\"\n\"35\",\"optimal\"\n\"35\",\"createoleobject\"\n\"35\",\"crystal-reports-2016\"\n\"35\",\"kentico-13\"\n\"35\",\"android-sdk-1.6\"\n\"35\",\"block-comments\"\n\"35\",\"jquery-mobile-panel\"\n\"35\",\"html-tree\"\n\"35\",\"horizontalpodautoscaler\"\n\"35\",\"aws-signature\"\n\"35\",\"easy-auth\"\n\"35\",\"postgres-12\"\n\"35\",\"jquery.panzoom\"\n\"35\",\"pdp-11\"\n\"35\",\"jquery-ui-widget\"\n\"35\",\"twincat-ads-.net\"\n\"35\",\"epydoc\"\n\"35\",\"jumi\"\n\"35\",\"samsung-galaxy-watch-4\"\n\"35\",\"nsinvocationoperation\"\n\"35\",\"sarsa\"\n\"35\",\"samsung-touchwiz\"\n\"35\",\"ncl\"\n\"35\",\"work-stealing\"\n\"35\",\"gulp-connect\"\n\"35\",\"corresponding-records\"\n\"35\",\"inline-view\"\n\"35\",\"infinidb\"\n\"35\",\"azure-devops-deploymentgroups\"\n\"35\",\"mod-vhost-alias\"\n\"35\",\"sandbox-solution\"\n\"35\",\"spring-expression-language\"\n\"35\",\"negative-integer\"\n\"35\",\"gs1-datamatrix\"\n\"35\",\"jquery-textext\"\n\"35\",\"kadena\"\n\"35\",\"application.cfm\"\n\"35\",\"inets\"\n\"35\",\"path-traversal\"\n\"35\",\"sasl-scram\"\n\"35\",\"epollet\"\n\"35\",\"appsflyer-android-sdk\"\n\"35\",\"patreon\"\n\"35\",\"winobjc\"\n\"35\",\"reuters\"\n\"35\",\"system.web.ui.webcontrols\"\n\"35\",\"system-views\"\n\"35\",\"form-parameter\"\n\"35\",\"libsox\"\n\"35\",\"pyez\"\n\"35\",\"objectcache\"\n\"35\",\"tableau-cloud\"\n\"35\",\"typed-memory-views\"\n\"35\",\"atlaskit\"\n\"35\",\"kivy-recycleview\"\n\"35\",\"viewchildren\"\n\"35\",\"microsoft-skype-bot\"\n\"35\",\"klein-mvc\"\n\"35\",\"abnf\"\n\"35\",\"domain-masking\"\n\"35\",\"object-destruction\"\n\"35\",\"google-business-profile-api\"\n\"35\",\"abbr\"\n\"35\",\"fabric8-maven-plugin\"\n\"35\",\"rfc2898\"\n\"35\",\"rancher-rke\"\n\"35\",\"libsmbclient\"\n\"35\",\"netcoreapp3.1\"\n\"35\",\"word-diff\"\n\"35\",\"abide\"\n\"35\",\"pyshp\"\n\"35\",\"ucfirst\"\n\"35\",\"ocaml-batteries\"\n\"35\",\"pykml\"\n\"35\",\"android-afilechooser\"\n\"35\",\"fr3dldapbundle\"\n\"35\",\"sfnetwork\"\n\"35\",\"vim-macros\"\n\"35\",\"expresso-store\"\n\"35\",\"foundry-contour\"\n\"35\",\"minicom\"\n\"35\",\"mineflayer\"\n\"35\",\"visudo\"\n\"35\",\"viper\"\n\"35\",\"tablefooterview\"\n\"35\",\"viper-mode\"\n\"35\",\"rgee\"\n\"35\",\"taskwarrior\"\n\"35\",\"itemspanel\"\n\"35\",\"uibuttonbaritem\"\n\"35\",\"associated-value\"\n\"35\",\"asp.net-mvc-futures\"\n\"35\",\"noweb\"\n\"35\",\"gatsby-plugin-mdx\"\n\"35\",\"geant4\"\n\"35\",\"exclusion-constraint\"\n\"35\",\"gcdasyncudpsocket\"\n\"35\",\"nv12-nv21\"\n\"35\",\"android-custom-drawable\"\n\"35\",\"azure-managed-app\"\n\"35\",\"azure-sdk-js\"\n\"35\",\"itemcontainergenerator\"\n\"35\",\"wxstyledtextctrl\"\n\"35\",\"hidden-characters\"\n\"35\",\"quotas\"\n\"35\",\"drop-shadow\"\n\"35\",\"drb\"\n\"35\",\"uifontdescriptor\"\n\"35\",\"c51\"\n\"35\",\"gdata-python-client\"\n\"35\",\"dtcoretext\"\n\"35\",\"galaxy-nexus\"\n\"35\",\"mobius\"\n\"35\",\"pl-i\"\n\"35\",\"azure-load-testing\"\n\"35\",\"dia-sdk\"\n\"35\",\"taskstackbuilder\"\n\"35\",\"pngcrush\"\n\"35\",\"sql-authentication\"\n\"35\",\"cordova-chrome-app\"\n\"35\",\"nunittestadapter\"\n\"35\",\"drupal-entities\"\n\"35\",\"downshift\"\n\"35\",\"redquerybuilder\"\n\"35\",\"bulk-import\"\n\"35\",\"android-guava\"\n\"35\",\"taskdef\"\n\"35\",\"scodec\"\n\"35\",\"uinib\"\n\"35\",\"tabu-search\"\n\"35\",\"gwt-highcharts\"\n\"35\",\"diffusers\"\n\"35\",\"gcm-network-manager\"\n\"35\",\"non-breaking-characters\"\n\"35\",\"hamachi\"\n\"35\",\"c++builder-11-alexandria\"\n\"35\",\"mpmedialibrary\"\n\"35\",\"qradar\"\n\"35\",\"stock-data\"\n\"35\",\"angularjs-google-maps\"\n\"35\",\"geom-sf\"\n\"35\",\"terraform-provider-ibm\"\n\"35\",\"google-content-api\"\n\"35\",\"laravel-reverb\"\n\"35\",\"resumablejs\"\n\"35\",\"on-clause\"\n\"35\",\"persistent-object-store\"\n\"35\",\"testimonials\"\n\"35\",\"z-wave\"\n\"35\",\"strictnullchecks\"\n\"35\",\"zstandard\"\n\"35\",\"tonejs\"\n\"35\",\"hydra-core\"\n\"35\",\"peekmessage\"\n\"35\",\"split-screen-multitasking\"\n\"35\",\"everscale\"\n\"35\",\"mpesa\"\n\"35\",\"lookupfield\"\n\"35\",\"st-monad\"\n\"35\",\"mercury-editor\"\n\"35\",\"cumulative-line-chart\"\n\"35\",\"dunn.test\"\n\"35\",\"resharper-plugins\"\n\"35\",\"event-id\"\n\"35\",\"laravel-mongodb\"\n\"35\",\"testcontext\"\n\"35\",\"cgbitmapcontext\"\n\"35\",\"changenotifier\"\n\"35\",\"csv-write-stream\"\n\"35\",\"ete3\"\n\"35\",\"acceptbutton\"\n\"35\",\"excel-2008\"\n\"35\",\"layout-anchor\"\n\"35\",\"ternary-tree\"\n\"35\",\"node.js-client\"\n\"35\",\"accesscontrolservice\"\n\"35\",\"accessibility-insights\"\n\"35\",\"generic-relations\"\n\"35\",\"strong-named-key\"\n\"35\",\"laravel-service-container\"\n\"35\",\"ternary-search-tree\"\n\"35\",\"custom-application\"\n\"35\",\"pester-5\"\n\"35\",\"nmcli\"\n\"35\",\"beans-binding\"\n\"35\",\"shfileoperation\"\n\"35\",\"parking\"\n\"35\",\"panzoom\"\n\"35\",\"compress-archive\"\n\"35\",\"emailrelay\"\n\"35\",\"multi-agent-reinforcement-learning\"\n\"35\",\"web-fragment\"\n\"35\",\"uservoice\"\n\"35\",\"parseui\"\n\"35\",\"flutter-graphql\"\n\"35\",\"basler\"\n\"35\",\"parslet\"\n\"35\",\"solarwinds-orion\"\n\"35\",\"maxrequestlength\"\n\"35\",\"fn\"\n\"35\",\"quagga\"\n\"35\",\"lintr\"\n\"35\",\"subactivity\"\n\"35\",\"amazon-managed-blockchain\"\n\"35\",\"google-groups-settings\"\n\"35\",\"thoughtworks-go\"\n\"35\",\"conditional-split\"\n\"35\",\"cxml-commercexml\"\n\"35\",\"powerschool\"\n\"35\",\"arm9\"\n\"35\",\"suneditor\"\n\"35\",\"webgrease\"\n\"35\",\"zio-test\"\n\"35\",\"armeria\"\n\"35\",\"mediaplayback\"\n\"35\",\"git-server\"\n\"35\",\"msgrcv\"\n\"35\",\"cvblobslib\"\n\"35\",\"qtdeclarative\"\n\"35\",\"mscomm32\"\n\"35\",\"beginreceive\"\n\"35\",\"zenoss\"\n\"35\",\"security-by-obscurity\"\n\"35\",\"scrollableresults\"\n\"35\",\"theano.scan\"\n\"35\",\"struts2-namespace\"\n\"35\",\"ide-customization\"\n\"35\",\"pppoe\"\n\"35\",\"url-fragment\"\n\"34\",\"clipspy\"\n\"34\",\"website-deployment\"\n\"34\",\"base62\"\n\"34\",\"repositorylookupedit\"\n\"34\",\"deduction-guide\"\n\"34\",\"ghc-generics\"\n\"34\",\"triangle-count\"\n\"34\",\"tensorflow-agents\"\n\"34\",\"whenever-capistrano\"\n\"34\",\"default-document\"\n\"34\",\"default-implementation\"\n\"34\",\"transitionend\"\n\"34\",\"backquote\"\n\"34\",\"stable-marriage\"\n\"34\",\"installaware\"\n\"34\",\"default-programs\"\n\"34\",\"trigraphs\"\n\"34\",\"basicnamevaluepair\"\n\"34\",\"dbms-crypto\"\n\"34\",\"marvin-framework\"\n\"34\",\"tss\"\n\"34\",\"flinkml\"\n\"34\",\"ansi-term\"\n\"34\",\"yarn-v3\"\n\"34\",\"clj-http\"\n\"34\",\"flowdocumentscrollviewer\"\n\"34\",\"wcsf\"\n\"34\",\"local-shared-object\"\n\"34\",\"skype4java\"\n\"34\",\"dc.leaflet.js\"\n\"34\",\"flow-scope\"\n\"34\",\"flex-mojos\"\n\"34\",\"eclipse-databinding\"\n\"34\",\"multipart-mixed-replace\"\n\"34\",\"fluent-security\"\n\"34\",\"slickedit\"\n\"34\",\"anr\"\n\"34\",\"clock-synchronization\"\n\"34\",\"fedora-27\"\n\"34\",\"remote-forms\"\n\"34\",\"fedena\"\n\"34\",\"fieldlist\"\n\"34\",\"liquid-xml\"\n\"34\",\"smartassembly\"\n\"34\",\"background-agent\"\n\"34\",\"jenetics\"\n\"34\",\"basil.js\"\n\"34\",\"vraptor\"\n\"34\",\"semantic-ui-css\"\n\"34\",\"const-generics\"\n\"34\",\"kubernetes-nodeport\"\n\"34\",\"adfs2.1\"\n\"34\",\"xmlexception\"\n\"34\",\"adjacency-list-model\"\n\"34\",\"snowpipe\"\n\"34\",\"selectivizr\"\n\"34\",\"select-into-outfile\"\n\"34\",\"vob\"\n\"34\",\"future-warning\"\n\"34\",\"seldon\"\n\"34\",\"firefox3.5\"\n\"34\",\"lamar\"\n\"34\",\"smooth-streaming-player\"\n\"34\",\"fuelphp-orm\"\n\"34\",\"lambda-architecture\"\n\"34\",\"sentimentr\"\n\"34\",\"xmlroot\"\n\"34\",\"fuzzyfinder\"\n\"34\",\"case-expression\"\n\"34\",\"xml-schema-collection\"\n\"34\",\"soapcore\"\n\"34\",\"content-based-retrieval\"\n\"34\",\"fxruby\"\n\"34\",\"syncfusion-blazor\"\n\"34\",\"chatkit\"\n\"34\",\"firemonkey-style\"\n\"34\",\"runtime-type\"\n\"34\",\"sailfish-os\"\n\"34\",\"runumap\"\n\"34\",\"datatable.select\"\n\"34\",\"python-3.1\"\n\"34\",\"manipulate\"\n\"34\",\"rust-piston\"\n\"34\",\"indexed-properties\"\n\"34\",\"discord4j\"\n\"34\",\"independent-set\"\n\"34\",\"imessagefilter\"\n\"34\",\"rxjs7\"\n\"34\",\"binary-deserialization\"\n\"34\",\"swiftui-asyncimage\"\n\"34\",\"indefinite\"\n\"34\",\"apiman\"\n\"34\",\"unaccent\"\n\"34\",\"django-custom-field\"\n\"34\",\"data-linking\"\n\"34\",\"incapsula\"\n\"34\",\"chilkat-email\"\n\"34\",\"vercel-ai\"\n\"34\",\"name-binding\"\n\"34\",\"ruby-prof\"\n\"34\",\"window-load\"\n\"34\",\"microc\"\n\"34\",\"shark\"\n\"34\",\"angularjs-ng-value\"\n\"34\",\"crosstable\"\n\"34\",\"microsoft.data.sqlite\"\n\"34\",\"facebook-send-api\"\n\"34\",\"dataiku\"\n\"34\",\"alexa-presentation-language\"\n\"34\",\"sitecore-rocks\"\n\"34\",\"methodexpression\"\n\"34\",\"read-committed-snapshot\"\n\"34\",\"ora-06502\"\n\"34\",\"r-library\"\n\"34\",\"djl\"\n\"34\",\"opml\"\n\"34\",\"jnr\"\n\"34\",\"aws-elemental\"\n\"34\",\"wincvs\"\n\"34\",\"airbnb-js-styleguide\"\n\"34\",\"read-host\"\n\"34\",\"facenet\"\n\"34\",\"session-0-isolation\"\n\"34\",\"rtm\"\n\"34\",\"django-subquery\"\n\"34\",\"falcor-router\"\n\"34\",\"ora-00918\"\n\"34\",\"fay\"\n\"34\",\"unity3d-ui\"\n\"34\",\"variable-selection\"\n\"34\",\"icustomtypedescriptor\"\n\"34\",\"go-playground\"\n\"34\",\"angular-template-variable\"\n\"34\",\"gradle.properties\"\n\"34\",\"meta-raspberrypi\"\n\"34\",\"psqlodbc\"\n\"34\",\"interlocked-increment\"\n\"34\",\"axe\"\n\"34\",\"springfox-boot-starter\"\n\"34\",\"kaspersky\"\n\"34\",\"spring-projections\"\n\"34\",\"input-split\"\n\"34\",\"ttnavigator\"\n\"34\",\"kemal\"\n\"34\",\"influx-line-protocol\"\n\"34\",\"post-parameter\"\n\"34\",\"entrypointnotfoundexcept\"\n\"34\",\"appsync-apollo-client\"\n\"34\",\"inner-exception\"\n\"34\",\"braced-init-list\"\n\"34\",\"informat\"\n\"34\",\"simple.odata.client\"\n\"34\",\"pax-runner\"\n\"34\",\"grunt-cli\"\n\"34\",\"boost-unit-test-framework\"\n\"34\",\"dynamic-splash-screen\"\n\"34\",\"jugglingdb\"\n\"34\",\"nsdocumentcontroller\"\n\"34\",\"pclose\"\n\"34\",\"android-viewflipper\"\n\"34\",\"postmates\"\n\"34\",\"box2dlights\"\n\"34\",\"aws-sdk-mock\"\n\"34\",\"env-file\"\n\"34\",\"jquery-ui-layout\"\n\"34\",\"spring-data-redis-reactive\"\n\"34\",\"android-studio-plugin\"\n\"34\",\"naudio-framework\"\n\"34\",\"workfusion\"\n\"34\",\"navigation-architecture\"\n\"34\",\"spring-data-neo4j-5\"\n\"34\",\"spring-mobile\"\n\"34\",\"supertab\"\n\"34\",\"nspipe\"\n\"34\",\"ionic-zip\"\n\"34\",\"pbxproj\"\n\"34\",\"svn-reintegrate\"\n\"34\",\"spring-json\"\n\"34\",\"deveco-studio\"\n\"34\",\"http-accept-encoding\"\n\"34\",\"boost-accumulators\"\n\"34\",\"devel-cover\"\n\"34\",\"android-style-tabhost\"\n\"34\",\"coverity-prevent\"\n\"34\",\"pbcopy\"\n\"34\",\"magellan\"\n\"34\",\"acceleratorkey\"\n\"34\",\"sgi\"\n\"34\",\"vim-fzf\"\n\"34\",\"revisionable\"\n\"34\",\"wixlib\"\n\"34\",\"wordpress-3.5\"\n\"34\",\"lenskit\"\n\"34\",\"r-commander\"\n\"34\",\"jaws-wordnet\"\n\"34\",\"vin\"\n\"34\",\"sfdx\"\n\"34\",\"octetstring\"\n\"34\",\"bug-tracker\"\n\"34\",\"coderay\"\n\"34\",\"pypng\"\n\"34\",\"system.drawing.graphics\"\n\"34\",\"rfacebook\"\n\"34\",\"bsddb\"\n\"34\",\"android-2.0-eclair\"\n\"34\",\"typehandler\"\n\"34\",\"richedit-control\"\n\"34\",\"extender\"\n\"34\",\"knife-solo\"\n\"34\",\"javascript-scope\"\n\"34\",\"javax.swing.timer\"\n\"34\",\"type-synonyms\"\n\"34\",\"amp-stories\"\n\"34\",\"libgomp\"\n\"34\",\"2-3-4-tree\"\n\"34\",\"network-driver\"\n\"34\",\"shingles\"\n\"34\",\"ravendb-http\"\n\"34\",\"windows-themes\"\n\"34\",\"forward-engineer\"\n\"34\",\"libigl\"\n\"34\",\"brewmp\"\n\"34\",\"visual-effects\"\n\"34\",\"openvidu\"\n\"34\",\"polymer-2.0\"\n\"34\",\"excelquery\"\n\"34\",\"diawi\"\n\"34\",\"c++pmr\"\n\"34\",\"gdbinit\"\n\"34\",\"non-renewing-subscription\"\n\"34\",\"xcode-extension\"\n\"34\",\"hamming-numbers\"\n\"34\",\"regioninfo\"\n\"34\",\"non-termination\"\n\"34\",\"rails-assets\"\n\"34\",\"tagged-pdf\"\n\"34\",\"npm-ci\"\n\"34\",\"referrer-spam\"\n\"34\",\"high-voltage\"\n\"34\",\"pointycastle\"\n\"34\",\"referenceequals\"\n\"34\",\"null-propagation-operator\"\n\"34\",\"drupal-field-collection\"\n\"34\",\"nupic\"\n\"34\",\"coovachilli\"\n\"34\",\"pluto.jl\"\n\"34\",\"itertools-groupby\"\n\"34\",\"gcp-iam\"\n\"34\",\"explicit-destructor-call\"\n\"34\",\"asp.net-mvc-apiexplorer\"\n\"34\",\"isaserver\"\n\"34\",\"target-audience\"\n\"34\",\"sql-server-2016-localdb\"\n\"34\",\"openid4java\"\n\"34\",\"nuxt-link\"\n\"34\",\"timestep\"\n\"34\",\"executionengineexception\"\n\"34\",\"tagbar\"\n\"34\",\"dryscrape\"\n\"34\",\"gwr\"\n\"34\",\"asset-sync\"\n\"34\",\"version-compatibility\"\n\"34\",\"asp.net-placeholder\"\n\"34\",\"asp.net-spa\"\n\"34\",\"h2o4gpu\"\n\"34\",\"openlayers-7\"\n\"34\",\"copywithzone\"\n\"34\",\"monitoring-query-language\"\n\"34\",\"elasticsearch-6.8\"\n\"34\",\"large-scale\"\n\"34\",\"rest-parameters\"\n\"34\",\"one-class-classification\"\n\"34\",\"geometric-mean\"\n\"34\",\"ipxe\"\n\"34\",\"spelevaluationexception\"\n\"34\",\"chained-select\"\n\"34\",\"monomorphism-restriction\"\n\"34\",\"angular-di\"\n\"34\",\"lockscreenwidget\"\n\"34\",\"etcpasswd\"\n\"34\",\"responsivevoice\"\n\"34\",\"lptstr\"\n\"34\",\"httpresponsecache\"\n\"34\",\"lastaccesstime\"\n\"34\",\"custom-button\"\n\"34\",\"hwndhost\"\n\"34\",\"ning\"\n\"34\",\"custom-pipeline-component\"\n\"34\",\"test-project\"\n\"34\",\"spotify-docker-client\"\n\"34\",\"active-hdl\"\n\"34\",\"color-wheel\"\n\"34\",\"ios-lifecycle\"\n\"34\",\"hybridauthprovider\"\n\"34\",\"httpinvoker\"\n\"34\",\"android-palette\"\n\"34\",\"protobufjs\"\n\"34\",\"mozrepl\"\n\"34\",\"requirements-management\"\n\"34\",\"resume-upload\"\n\"34\",\"certifi\"\n\"34\",\"http-toolkit\"\n\"34\",\"duende\"\n\"34\",\"http-content-range\"\n\"34\",\"activeqt\"\n\"34\",\"requirejs-define\"\n\"34\",\"angular2-upgrade\"\n\"34\",\"huawei-iap\"\n\"34\",\"angular2viewencapsulation\"\n\"34\",\"nix-shell\"\n\"34\",\"qfuture\"\n\"34\",\"parametrize\"\n\"34\",\"urlread\"\n\"34\",\"custom-sections\"\n\"34\",\"sharpcompress\"\n\"34\",\"d2rq\"\n\"34\",\"urlmappings.groovy\"\n\"34\",\"struct.pack\"\n\"34\",\"stargate\"\n\"34\",\"elmish-wpf\"\n\"34\",\"global-filter\"\n\"34\",\"avalanche\"\n\"34\",\"flutter-pubspec\"\n\"34\",\"quay.io\"\n\"34\",\"media-url\"\n\"34\",\"msscriptcontrol\"\n\"34\",\"shelving\"\n\"34\",\"struct-member-alignment\"\n\"34\",\"security-scoped-bookmarks\"\n\"34\",\"stellar\"\n\"34\",\"lightweight-processes\"\n\"34\",\"scrollwheel\"\n\"34\",\"email-threading\"\n\"34\",\"haslayout\"\n\"34\",\"autoflush\"\n\"34\",\"sonarjs\"\n\"34\",\"bass.dll\"\n\"34\",\"uxtheme\"\n\"34\",\"hasura-docker\"\n\"34\",\"zkemkeeper\"\n\"34\",\"zipstream\"\n\"34\",\"mson\"\n\"34\",\"touchablewithoutfeedback\"\n\"34\",\"linkmovementmethod\"\n\"34\",\"yuidoc\"\n\"34\",\"mtkview\"\n\"34\",\"msvcr100.dll\"\n\"34\",\"quadratic-probing\"\n\"34\",\"ember-table\"\n\"34\",\"autonomy\"\n\"34\",\"google-play-protect\"\n\"34\",\"bed\"\n\"34\",\"amazon-in-app-purchase\"\n\"34\",\"queryinterface\"\n\"34\",\"automatic-failover\"\n\"34\",\"webimage\"\n\"34\",\"prepareforreuse\"\n\"34\",\"stateless-state-machine\"\n\"34\",\"ardalis-cleanarchitecture\"\n\"34\",\"user-acceptance-testing\"\n\"34\",\"bigfloat\"\n\"34\",\"user-object\"\n\"33\",\"jcc\"\n\"33\",\"bankers-rounding\"\n\"33\",\"banana\"\n\"33\",\"marten\"\n\"33\",\"cloudflare-r2\"\n\"33\",\"cloudrail\"\n\"33\",\"wct\"\n\"33\",\"jazzy\"\n\"33\",\"cloudflare-apps\"\n\"33\",\"phpmqtt\"\n\"33\",\"php-cpp\"\n\"33\",\"react-number-format\"\n\"33\",\"baqend\"\n\"33\",\"defaultnetworkcredentials\"\n\"33\",\"gina\"\n\"33\",\"treap\"\n\"33\",\"stacking-context\"\n\"33\",\"transit-gateway\"\n\"33\",\"ggspatial\"\n\"33\",\"stamen-maps\"\n\"33\",\"graphlookup\"\n\"33\",\"dedicated-hosting\"\n\"33\",\"webscarab\"\n\"33\",\"fdb\"\n\"33\",\"xquery-3.1\"\n\"33\",\"graphql-go\"\n\"33\",\"wherehas\"\n\"33\",\"edaplayground\"\n\"33\",\"gettickcount\"\n\"33\",\"smartcardio\"\n\"33\",\"clean-css\"\n\"33\",\"xsitype\"\n\"33\",\"why3\"\n\"33\",\"decentralized-identity\"\n\"33\",\"fedora16\"\n\"33\",\"anonymous-access\"\n\"33\",\"eclipse-orion\"\n\"33\",\"relplot\"\n\"33\",\"interface-design\"\n\"33\",\"fluentwait\"\n\"33\",\"multiple-apk\"\n\"33\",\"v-slot\"\n\"33\",\"jffs2\"\n\"33\",\"privatefontcollection\"\n\"33\",\"matlab-hg2\"\n\"33\",\"flotr2\"\n\"33\",\"base-tag\"\n\"33\",\"ecies\"\n\"33\",\"multipoint\"\n\"33\",\"vuejs-transition\"\n\"33\",\"yap\"\n\"33\",\"vuejs3-composition-api\"\n\"33\",\"printf-debugging\"\n\"33\",\"vue-filter\"\n\"33\",\"principalpermission\"\n\"33\",\"jest-mock-axios\"\n\"33\",\"field-description\"\n\"33\",\"pg-query\"\n\"33\",\"cmmi\"\n\"33\",\"phimagemanager\"\n\"33\",\"groovyws\"\n\"33\",\"ebay-design-templates\"\n\"33\",\"backbone-layout-manager\"\n\"33\",\"fiftyone\"\n\"33\",\"snap.js\"\n\"33\",\"xml-publisher\"\n\"33\",\"ng-bootstrap-modal\"\n\"33\",\"apollo-link\"\n\"33\",\"incremental\"\n\"33\",\"nextuntil\"\n\"33\",\"files-app\"\n\"33\",\"config-spec\"\n\"33\",\"apklib\"\n\"33\",\"configuration-profile\"\n\"33\",\"symfony-plugins\"\n\"33\",\"labelfield\"\n\"33\",\"cikernel\"\n\"33\",\"apisauce\"\n\"33\",\"pino\"\n\"33\",\"xmlconvert\"\n\"33\",\"language-packs\"\n\"33\",\"adobe-flash-cs3\"\n\"33\",\"imapi\"\n\"33\",\"packer-builder\"\n\"33\",\"apama\"\n\"33\",\"manatee.trello\"\n\"33\",\"jsf-1.1\"\n\"33\",\"laravel-cache\"\n\"33\",\"apache-stanbol\"\n\"33\",\"direnv\"\n\"33\",\"running-other-programs\"\n\"33\",\"pinata\"\n\"33\",\"admin-bro\"\n\"33\",\"sage50\"\n\"33\",\"cd-burning\"\n\"33\",\"nginx-module\"\n\"33\",\"sencha-touch-theming\"\n\"33\",\"adrotator\"\n\"33\",\"function-interposition\"\n\"33\",\"safari-push-notifications\"\n\"33\",\"jsr168\"\n\"33\",\"distro\"\n\"33\",\"discrete-optimization\"\n\"33\",\"plai\"\n\"33\",\"sendy\"\n\"33\",\"platform-sdk\"\n\"33\",\"ng-multiselect-dropdown\"\n\"33\",\"marble\"\n\"33\",\"free-variable\"\n\"33\",\"disco\"\n\"33\",\"soaphttpclientprotocol\"\n\"33\",\"ngrx-selectors\"\n\"33\",\"fslab\"\n\"33\",\"advising-functions\"\n\"33\",\"adhoc-polymorphism\"\n\"33\",\"cakephp-3.8\"\n\"33\",\"django-webpack-loader\"\n\"33\",\"ajaxmin\"\n\"33\",\"unreal-umg\"\n\"33\",\"rollup-plugin-postcss\"\n\"33\",\"jpopup\"\n\"33\",\"wcf-data-services-client\"\n\"33\",\"rowdetails\"\n\"33\",\"rollover-effect\"\n\"33\",\"angular-new-router\"\n\"33\",\"djgpp\"\n\"33\",\"djvu\"\n\"33\",\"mysql-connector-c\"\n\"33\",\"dnlib\"\n\"33\",\"fastsearch\"\n\"33\",\"root.plist\"\n\"33\",\"rpx\"\n\"33\",\"wcf-behaviour\"\n\"33\",\"sipp\"\n\"33\",\"ruby-jmeter\"\n\"33\",\"mysql-loadfile\"\n\"33\",\"ora-12154\"\n\"33\",\"rr\"\n\"33\",\"akka-grpc\"\n\"33\",\"windows-mobile-gps\"\n\"33\",\"vector-icons\"\n\"33\",\"aws-api-gateway-v2\"\n\"33\",\"unix-text-processing\"\n\"33\",\"aws-rest-api\"\n\"33\",\"red5pro\"\n\"33\",\"ps4\"\n\"33\",\"agentset\"\n\"33\",\"avm2\"\n\"33\",\"aws-chatbot\"\n\"33\",\"ibp-vscode-extension\"\n\"33\",\"docc\"\n\"33\",\"recordreader\"\n\"33\",\"doccano\"\n\"33\",\"datadude\"\n\"33\",\"wash-out\"\n\"33\",\"ibm-sterling\"\n\"33\",\"unparseable\"\n\"33\",\"jhat\"\n\"33\",\"canonical-quickly\"\n\"33\",\"document-view\"\n\"33\",\"angular-material-15\"\n\"33\",\"microsoft.identity.web\"\n\"33\",\"oracle-apex-20.1\"\n\"33\",\"pybliometrics\"\n\"33\",\"ropemacs\"\n\"33\",\"windowsbuilder\"\n\"33\",\"optim\"\n\"33\",\"urdf\"\n\"33\",\"microsoft-appstudio\"\n\"33\",\"jhbuild\"\n\"33\",\"servicepoint\"\n\"33\",\"covariogram\"\n\"33\",\"nsbrowser\"\n\"33\",\"epipe\"\n\"33\",\"pose-detection\"\n\"33\",\"ncr\"\n\"33\",\"epicor\"\n\"33\",\"superscrollorama\"\n\"33\",\"android-scripting\"\n\"33\",\"entryset\"\n\"33\",\"boilerplatejs\"\n\"33\",\"android-vitals\"\n\"33\",\"enterprise-web-library\"\n\"33\",\"svn-propset\"\n\"33\",\"ionic2-select\"\n\"33\",\"kaazing\"\n\"33\",\"aranchor\"\n\"33\",\"sap-query\"\n\"33\",\"nrules\"\n\"33\",\"sanctuary\"\n\"33\",\"gulp-clean-css\"\n\"33\",\"openbd\"\n\"33\",\"ttx-fonttools\"\n\"33\",\"swift5.7\"\n\"33\",\"twincat-hmi\"\n\"33\",\"inner-product\"\n\"33\",\"html-treebuilder\"\n\"33\",\"nsprogress\"\n\"33\",\"wso2-cloud\"\n\"33\",\"devanagari\"\n\"33\",\"boost-pool\"\n\"33\",\"nativescript-firebase\"\n\"33\",\"pchar\"\n\"33\",\"boost-property-map\"\n\"33\",\"gsmcomm\"\n\"33\",\"kdf\"\n\"33\",\"sbt-release\"\n\"33\",\"gs-installable-triggers\"\n\"33\",\"jquery-dirtyforms\"\n\"33\",\"blazored\"\n\"33\",\"mongojack\"\n\"33\",\"sbt-android-plugin\"\n\"33\",\"dynamics-nav-2013\"\n\"33\",\"postico\"\n\"33\",\"aztec-barcode\"\n\"33\",\"bootstrap-file-upload\"\n\"33\",\"dynamic-attributes\"\n\"33\",\"gtm-oauth2\"\n\"33\",\"azure-autoscaling-block\"\n\"33\",\"dynamics-nav-2016\"\n\"33\",\"bootstrap-file-input\"\n\"33\",\"wordpress-theme-astra\"\n\"33\",\"html-title\"\n\"33\",\"spring-security-6\"\n\"33\",\"turnkeylinux.org\"\n\"33\",\"azure-adf\"\n\"33\",\"buckminster\"\n\"33\",\"brat\"\n\"33\",\"osgearth\"\n\"33\",\"raster-graphics\"\n\"33\",\"donut-caching\"\n\"33\",\"org-table\"\n\"33\",\"orange-api\"\n\"33\",\"lwm2m\"\n\"33\",\"ubuntu-touch\"\n\"33\",\"over-clause\"\n\"33\",\"extensible-storage-engine\"\n\"33\",\"google-calendar-recurring-events\"\n\"33\",\"windowsversion\"\n\"33\",\"abstractuser\"\n\"33\",\"learn-ruby-on-rails\"\n\"33\",\"google-buzz\"\n\"33\",\"abrecordref\"\n\"33\",\"os.execl\"\n\"33\",\"tabbed-interface\"\n\"33\",\"wkb\"\n\"33\",\"system-analysis\"\n\"33\",\"androguard\"\n\"33\",\"system-font\"\n\"33\",\"magic-mirror\"\n\"33\",\"fragmentstateadapter\"\n\"33\",\"objective-c-nullability\"\n\"33\",\"cocos2d-python\"\n\"33\",\"machinist\"\n\"33\",\"sidekiq-monitor\"\n\"33\",\"1password\"\n\"33\",\"system.commandline\"\n\"33\",\"system.array\"\n\"33\",\".net-attributes\"\n\"33\",\"rdbms-agnostic\"\n\"33\",\"atompub\"\n\"33\",\"3scale\"\n\"33\",\"shape-recognition\"\n\"33\",\"visifire\"\n\"33\",\"videoquality\"\n\"33\",\"pyreverse\"\n\"33\",\"obr\"\n\"33\",\"foundry-data-connection\"\n\"33\",\"foundry-python-transform\"\n\"33\",\"foxit-reader\"\n\"33\",\"mixamo\"\n\"33\",\"codeigniter-form-validation\"\n\"33\",\"rb-appscript\"\n\"33\",\"continued-fractions\"\n\"33\",\"tizen-tv\"\n\"33\",\"openvdb\"\n\"33\",\"azure-oms\"\n\"33\",\"polish-notation\"\n\"33\",\"mleap\"\n\"33\",\"hl7-cda\"\n\"33\",\"scrabble\"\n\"33\",\"c++builder-xe4\"\n\"33\",\"cordovawebview\"\n\"33\",\"sqldbtype\"\n\"33\",\"business-layer\"\n\"33\",\"xamarin.windows\"\n\"33\",\"tmb\"\n\"33\",\"bull.js\"\n\"33\",\"cordova-nativestorage\"\n\"33\",\"dramatiq\"\n\"33\",\"control-array\"\n\"33\",\"non-printable\"\n\"33\",\"c#-record-type\"\n\"33\",\"mobfox\"\n\"33\",\"gcc3\"\n\"33\",\"cachegrind\"\n\"33\",\"tbl\"\n\"33\",\"convertto-json\"\n\"33\",\"excel-template\"\n\"33\",\"gumby-framework\"\n\"33\",\"exoplayer-media-item\"\n\"33\",\"nullish-coalescing\"\n\"33\",\"scalaxb\"\n\"33\",\"device-management\"\n\"33\",\"coolstorage\"\n\"33\",\"sql2o\"\n\"33\",\"ragdoll\"\n\"33\",\"asp.net-core-testhost\"\n\"33\",\"cachefactory\"\n\"33\",\"uialertviewcontroller\"\n\"33\",\"c4.5\"\n\"33\",\"hocr\"\n\"33\",\"numactl\"\n\"33\",\"scaldi\"\n\"33\",\"uiswipeactionsconfiguration\"\n\"33\",\"openidm\"\n\"33\",\"vfio\"\n\"33\",\"holder.js\"\n\"33\",\"tinymce-rails\"\n\"33\",\"commitizen\"\n\"33\",\"cstdint\"\n\"33\",\"string-table\"\n\"33\",\"colgroup\"\n\"33\",\"oms\"\n\"33\",\"cstringio\"\n\"33\",\"collaborative-editing\"\n\"33\",\"teststand\"\n\"33\",\"perspectives\"\n\"33\",\"move-uploaded-file\"\n\"33\",\"generic-derivation\"\n\"33\",\"nl-classifier\"\n\"33\",\"ogg-theora\"\n\"33\",\"lr1\"\n\"33\",\"nimble\"\n\"33\",\"elasticsearch-shield\"\n\"33\",\"pest\"\n\"33\",\"hug\"\n\"33\",\"protect-from-forgery\"\n\"33\",\"color-thief\"\n\"33\",\"react-bootstrap4-modal\"\n\"33\",\"qi\"\n\"33\",\"laravel-route\"\n\"33\",\"qnames\"\n\"33\",\"commonj\"\n\"33\",\"mesos-chronos\"\n\"33\",\"angular.json\"\n\"33\",\"offline-storage\"\n\"33\",\"cfoutput\"\n\"33\",\"mootools-more\"\n\"33\",\"reactive-mongo-java\"\n\"33\",\"ios13.3\"\n\"33\",\"angular-config\"\n\"33\",\"loopback-address\"\n\"33\",\"gentics-mesh\"\n\"33\",\"comp-3\"\n\"33\",\"cubical-type-theory\"\n\"33\",\"qcamera\"\n\"33\",\"oft\"\n\"33\",\"chargebee\"\n\"33\",\"common-service-locator\"\n\"33\",\"hugo-theme\"\n\"33\",\"angular-chartist.js\"\n\"33\",\"resolutions\"\n\"33\",\"ipyvuetify\"\n\"33\",\"alias-method\"\n\"33\",\"bfd\"\n\"33\",\"statistical-sampling\"\n\"33\",\"tfs-2018\"\n\"33\",\"mdbg\"\n\"33\",\"tigergraph\"\n\"33\",\"msbuild-15\"\n\"33\",\"tibco-topic\"\n\"33\",\"themedata\"\n\"33\",\"secure-transport\"\n\"33\",\"seam-conversation\"\n\"33\",\"startup-error\"\n\"33\",\"webmatrix-3\"\n\"33\",\"amazon-gateway\"\n\"33\",\"google-gsuite\"\n\"33\",\"stddeque\"\n\"33\",\"zabbix-custom-reports\"\n\"33\",\"automerge\"\n\"33\",\"hatch\"\n\"33\",\"powershellget\"\n\"33\",\"zero-initialization\"\n\"33\",\"gmsplace\"\n\"33\",\"confd\"\n\"33\",\"tf2onnx\"\n\"33\",\"mdac\"\n\"33\",\"gmaven\"\n\"33\",\"scroll-snap-points\"\n\"33\",\"archiverjs\"\n\"33\",\"concreteclass\"\n\"33\",\"amazon-dynamodb-data-modeling\"\n\"33\",\"ppx\"\n\"33\",\"cvsnt\"\n\"33\",\"mrtg\"\n\"33\",\"autoplot\"\n\"33\",\"webcontent\"\n\"33\",\"glom\"\n\"33\",\"asenumerable\"\n\"33\",\"flutter-qrcode\"\n\"33\",\"beast-websockets\"\n\"33\",\"heremap-navigation\"\n\"33\",\"d3heatmap\"\n\"33\",\"touchdb\"\n\"33\",\"concurrentmodificationexception\"\n\"33\",\"componentlistener\"\n\"33\",\"google-merchant-center\"\n\"33\",\"compodoc\"\n\"33\",\"dartdoc\"\n\"33\",\"partial-mocks\"\n\"33\",\"web3dart\"\n\"33\",\"amazon-kinesis-agent\"\n\"33\",\"arelle\"\n\"33\",\"ashot\"\n\"33\",\"multicastdelegate\"\n\"33\",\"autossh\"\n\"33\",\"weblog\"\n\"33\",\"traceability\"\n\"33\",\"flutter-table\"\n\"32\",\"bandit-python\"\n\"32\",\"backtrack-linux\"\n\"32\",\"transitiondrawable\"\n\"32\",\"editplus\"\n\"32\",\"editpad\"\n\"32\",\"mvchtmlstring\"\n\"32\",\"staleobjectstate\"\n\"32\",\"mvapich2\"\n\"32\",\"php-code-coverage\"\n\"32\",\"edeliver\"\n\"32\",\"cloudberry\"\n\"32\",\"getwritabledatabase\"\n\"32\",\"phpize\"\n\"32\",\"multiviews\"\n\"32\",\"principal-components\"\n\"32\",\"xsp4\"\n\"32\",\"remix-ide\"\n\"32\",\"flite\"\n\"32\",\"flutter-add-to-app\"\n\"32\",\"badparcelableexception\"\n\"32\",\"live-sass-compiler\"\n\"32\",\"multipolygons\"\n\"32\",\"ts-node-dev\"\n\"32\",\"tsdx\"\n\"32\",\"yahoo-kafka-manager\"\n\"32\",\"load-factor\"\n\"32\",\"flow-framework\"\n\"32\",\"react-pdf-viewer\"\n\"32\",\"whoops\"\n\"32\",\"file-link\"\n\"32\",\"click-counting\"\n\"32\",\"sitefinity-8\"\n\"32\",\"ckreference\"\n\"32\",\"debug-build\"\n\"32\",\"graphql-flutter\"\n\"32\",\"grgit\"\n\"32\",\"siteorigin\"\n\"32\",\"tempdir\"\n\"32\",\"telecom-manager\"\n\"32\",\"apache-beam-internals\"\n\"32\",\"ansible-filter\"\n\"32\",\"wddx\"\n\"32\",\"dci\"\n\"32\",\"client-templates\"\n\"32\",\"declval\"\n\"32\",\"graphql-python\"\n\"32\",\"dbproj\"\n\"32\",\"manageiq\"\n\"32\",\"firefox-5\"\n\"32\",\"pico-8\"\n\"32\",\"p4java\"\n\"32\",\"firebird-psql\"\n\"32\",\"runpy\"\n\"32\",\"casbin\"\n\"32\",\"rum\"\n\"32\",\"flamerobin\"\n\"32\",\"soap-extension\"\n\"32\",\"padre\"\n\"32\",\"fingerprintjs2\"\n\"32\",\"vorpal.js\"\n\"32\",\"aes-ni\"\n\"32\",\"catransform3drotate\"\n\"32\",\"find-all-references\"\n\"32\",\"ngrx-reducers\"\n\"32\",\"uniroot\"\n\"32\",\"uninstallstring\"\n\"32\",\"separating-axis-theorem\"\n\"32\",\"sentinelsat\"\n\"32\",\"xerces2-j\"\n\"32\",\"ads-api\"\n\"32\",\"xdt\"\n\"32\",\"cdata-drivers\"\n\"32\",\"ngboilerplate\"\n\"32\",\"semantic-kernel\"\n\"32\",\"dbdatareader\"\n\"32\",\"semaphore-ci\"\n\"32\",\"semgrep\"\n\"32\",\"swift-pythonkit\"\n\"32\",\"child-actions\"\n\"32\",\"fuel\"\n\"32\",\"full-text-catalog\"\n\"32\",\"importdata\"\n\"32\",\"pixbuf\"\n\"32\",\"jsr107\"\n\"32\",\"checkvalidity\"\n\"32\",\"constraintset\"\n\"32\",\"laravel-backpack-5\"\n\"32\",\"chronic\"\n\"32\",\"imageresizer-diskcache\"\n\"32\",\"jtemplate\"\n\"32\",\"container-managed\"\n\"32\",\"xpand\"\n\"32\",\"json-web-signature\"\n\"32\",\"symfony-eventdispatcher\"\n\"32\",\"pubxml\"\n\"32\",\"optimistic-ui\"\n\"32\",\"variable-fonts\"\n\"32\",\"caprover\"\n\"32\",\"walmart-electrode\"\n\"32\",\"vds\"\n\"32\",\"recursionerror\"\n\"32\",\"fast-endpoints\"\n\"32\",\"dockeroperator\"\n\"32\",\"ora-01427\"\n\"32\",\"grails-config\"\n\"32\",\"serilog-exceptions\"\n\"32\",\"django-userena\"\n\"32\",\"microsoft.ml\"\n\"32\",\"data-access-object\"\n\"32\",\"wiki-engine\"\n\"32\",\"servicetestcase\"\n\"32\",\"session-state-server\"\n\"32\",\"dkpro-core\"\n\"32\",\"angular-scenario\"\n\"32\",\"dkms\"\n\"32\",\"windeployqt\"\n\"32\",\"pthread-barriers\"\n\"32\",\"vb4android\"\n\"32\",\"routelink\"\n\"32\",\"pushbots\"\n\"32\",\"pushd\"\n\"32\",\"psapi\"\n\"32\",\"upn\"\n\"32\",\"myhdl\"\n\"32\",\"iaccessible\"\n\"32\",\"kendo-gantt\"\n\"32\",\"datagridviewcomboboxcolumn\"\n\"32\",\"microsoft-graph-booking\"\n\"32\",\"windows-server-container\"\n\"32\",\"valums-file-uploader\"\n\"32\",\"docplexcloud\"\n\"32\",\"ora-00600\"\n\"32\",\"serverless-framework-step-functions\"\n\"32\",\"gpytorch\"\n\"32\",\"mysqlupgrade\"\n\"32\",\"sequelize-auto\"\n\"32\",\"pvclust\"\n\"32\",\"inertial-navigation\"\n\"32\",\"pseudo-streaming\"\n\"32\",\"document-store\"\n\"32\",\"carchive\"\n\"32\",\"aws-appstream\"\n\"32\",\"annotatedtimeline\"\n\"32\",\"updown\"\n\"32\",\"updatexml\"\n\"32\",\"csharpscript\"\n\"32\",\"keyguardlock\"\n\"32\",\"documentum-dql\"\n\"32\",\"uriencoding\"\n\"32\",\"pspell\"\n\"32\",\"windows-phone-7.8\"\n\"32\",\"mysql-group-replication\"\n\"32\",\"kentico-api\"\n\"32\",\"inputconnection\"\n\"32\",\"crashloopbackoff\"\n\"32\",\"opencv3.2\"\n\"32\",\"postgrex\"\n\"32\",\"opcode-cache\"\n\"32\",\"wrangler\"\n\"32\",\"errbit\"\n\"32\",\"infobip-api\"\n\"32\",\"tun-tap\"\n\"32\",\"tuple-relational-calculus\"\n\"32\",\"jruby-java-interop\"\n\"32\",\"delete-method\"\n\"32\",\"bluedragon\"\n\"32\",\"apple-vpp\"\n\"32\",\"surveillance\"\n\"32\",\"pdf-reactor\"\n\"32\",\"boost-icl\"\n\"32\",\"module-export\"\n\"32\",\"nsepy\"\n\"32\",\"salesforce-developer\"\n\"32\",\"nsenumerator\"\n\"32\",\"twisted.client\"\n\"32\",\"bloc-test\"\n\"32\",\"navgraph\"\n\"32\",\"cpio\"\n\"32\",\"twisted.conch\"\n\"32\",\"nsapplication-delegate\"\n\"32\",\"android-text-color\"\n\"32\",\"boost-format\"\n\"32\",\"nestjs-microservice\"\n\"32\",\"invantive-control\"\n\"32\",\"onnx-coreml\"\n\"32\",\"word-spacing\"\n\"32\",\"apple-m7\"\n\"32\",\"jquery-transit\"\n\"32\",\"groupwise\"\n\"32\",\"openform\"\n\"32\",\"dwoo\"\n\"32\",\"android-scrollable-tabs\"\n\"32\",\"nsattributedstringkey\"\n\"32\",\"ueye\"\n\"32\",\"out-gridview\"\n\"32\",\"viewwithtag\"\n\"32\",\"orchestra\"\n\"32\",\"rgeo-shapefile\"\n\"32\",\"table-footer\"\n\"32\",\"abcpdf9\"\n\"32\",\"vision-pro\"\n\"32\",\"foscommentbundle\"\n\"32\",\"migratordotnet\"\n\"32\",\"rcc\"\n\"32\",\"extended-choice-parameter\"\n\"32\",\"legacy-app\"\n\"32\",\"koin-scope\"\n\"32\",\"windows-virtual-desktop\"\n\"32\",\"syndicationfeed\"\n\"32\",\"pyqtdeploy\"\n\"32\",\"richtextarea\"\n\"32\",\"extaudiofile\"\n\"32\",\"lumen-5.3\"\n\"32\",\"java-pair-rdd\"\n\"32\",\"range-checking\"\n\"32\",\"android-appshortcut\"\n\"32\",\"browser-state\"\n\"32\",\"typetoken\"\n\"32\",\"pygears\"\n\"32\",\"system.memory\"\n\"32\",\"shinyalert\"\n\"32\",\"1wire\"\n\"32\",\"fabric8-kubernetes-client\"\n\"32\",\"lunarvim\"\n\"32\",\"shinytree\"\n\"32\",\"3dcamera\"\n\"32\",\"networkstatsmanager\"\n\"32\",\"output-clause\"\n\"32\",\"oam\"\n\"32\",\"typolink\"\n\"32\",\"kontakt.io\"\n\"32\",\"newsequentialid\"\n\"32\",\"javafx-webview\"\n\"32\",\"viewmodelproviders\"\n\"32\",\"visual-odometry\"\n\"32\",\"async-profiler\"\n\"32\",\"frameset-iframe\"\n\"32\",\"codemirror-6\"\n\"32\",\"wordpress-database\"\n\"32\",\"google-business\"\n\"32\",\"rewind\"\n\"32\",\"razorpdf\"\n\"32\",\"objectaid\"\n\"32\",\"android-assetmanager\"\n\"32\",\"facebook-actionscript-api\"\n\"32\",\"gae-python27\"\n\"32\",\"c++builder-xe6\"\n\"32\",\"android-connectionservice\"\n\"32\",\"hashalgorithm\"\n\"32\",\"dreamspark\"\n\"32\",\"numpy-stl\"\n\"32\",\"nuke-build\"\n\"32\",\"experience-editor\"\n\"32\",\"scikit-multilearn\"\n\"32\",\"asp.net-minimal-apis\"\n\"32\",\"refactoring-databases\"\n\"32\",\"nullability\"\n\"32\",\"uikitformac\"\n\"32\",\"nosuchfieldexception\"\n\"32\",\"drupal-path-aliases\"\n\"32\",\"azure-iot-suite\"\n\"32\",\"devicepixelratio\"\n\"32\",\"opengl-1.x\"\n\"32\",\"openstack-keystone\"\n\"32\",\"scnmaterial\"\n\"32\",\"wx.textctrl\"\n\"32\",\"exclude-constraint\"\n\"32\",\"vgam\"\n\"32\",\"iso9660\"\n\"32\",\"sql-maven-plugin\"\n\"32\",\"executiontimeout\"\n\"32\",\"version-detection\"\n\"32\",\"gedit-plugin\"\n\"32\",\"scala-native\"\n\"32\",\"uitableviewdiffabledatasource\"\n\"32\",\"gcc-pedantic\"\n\"32\",\"xcode4.1\"\n\"32\",\"android-flavordimension\"\n\"32\",\"notistack\"\n\"32\",\"digit-separator\"\n\"32\",\"qvalidator\"\n\"32\",\"drawtobitmap\"\n\"32\",\"dgml\"\n\"32\",\"digital-assets-links\"\n\"32\",\"uib\"\n\"32\",\"drawinrect\"\n\"32\",\"redhat-datavirt\"\n\"32\",\"screen-grab\"\n\"32\",\"uicollectionviewdelegateflowlayout\"\n\"32\",\"numpy-dtype\"\n\"32\",\"iscrollview\"\n\"32\",\"dialing\"\n\"32\",\"digital-compass\"\n\"32\",\"redpanda\"\n\"32\",\"mockk-verify\"\n\"32\",\"duckduckgo\"\n\"32\",\"activeview\"\n\"32\",\"spreadsheet-excel-writer\"\n\"32\",\"communicator\"\n\"32\",\"community-server\"\n\"32\",\"react-dropdown-tree-select\"\n\"32\",\"google-cloud-http-load-balancer\"\n\"32\",\"proxy-protocol\"\n\"32\",\"monticello\"\n\"32\",\"react-leaflet-draw\"\n\"32\",\"speakerphone\"\n\"32\",\"ioutils\"\n\"32\",\"curve-25519\"\n\"32\",\"activation-context-api\"\n\"32\",\"escript\"\n\"32\",\"e-ink\"\n\"32\",\"combining-marks\"\n\"32\",\"cuml\"\n\"32\",\"google-compute-api\"\n\"32\",\"combinatory-logic\"\n\"32\",\"android-monkey\"\n\"32\",\"peverify\"\n\"32\",\"angular-arrays\"\n\"32\",\"log-viewer\"\n\"32\",\"test-first\"\n\"32\",\"android-jetpack-compose-button\"\n\"32\",\"ltk\"\n\"32\",\"elixir-jason\"\n\"32\",\"resource-adapter\"\n\"32\",\"odometer\"\n\"32\",\"zposition\"\n\"32\",\"commandfield\"\n\"32\",\"hypercube\"\n\"32\",\"oidc-provider\"\n\"32\",\"geopositioning\"\n\"32\",\"geoplot\"\n\"32\",\"angular-controlvalueaccessor\"\n\"32\",\"angular2-inputs\"\n\"32\",\"angular2-custom-pipes\"\n\"32\",\"resource-monitor\"\n\"32\",\"texinfo\"\n\"32\",\"angular14upgrade\"\n\"32\",\"terraform-state\"\n\"32\",\"angulardraganddroplists\"\n\"32\",\"prolog-metainterpreter\"\n\"32\",\"memory-mapped-io\"\n\"32\",\"perl-packager\"\n\"32\",\"log-files\"\n\"32\",\"monocle-scala\"\n\"32\",\"logbook\"\n\"32\",\"android-room-prepackageddatabase\"\n\"32\",\"hwid\"\n\"32\",\"storybook-addon-specifications\"\n\"32\",\"commerce.js\"\n\"32\",\"spider-chart\"\n\"32\",\"android-jsinterface\"\n\"32\",\"odbc-sql-server-driver\"\n\"32\",\"geonear\"\n\"32\",\"moniker\"\n\"32\",\"requires\"\n\"32\",\"qdomdocument\"\n\"32\",\"stringbyevaluatingjavascr\"\n\"32\",\"getcaretpos\"\n\"32\",\"lazy-static\"\n\"32\",\"sunrpc\"\n\"32\",\"bazel-python\"\n\"32\",\"webaddress\"\n\"32\",\"iiif\"\n\"32\",\"git-subrepo\"\n\"32\",\"has-one-through\"\n\"32\",\"hbm2java\"\n\"32\",\"line-api\"\n\"32\",\"berkeley-db-xml\"\n\"32\",\"gldrawpixels\"\n\"32\",\"heap-fragmentation\"\n\"32\",\"quality-gate\"\n\"32\",\"qscrollbar\"\n\"32\",\"sorting-network\"\n\"32\",\"flycapture\"\n\"32\",\"flutter-scrollbar\"\n\"32\",\"flutter-secure-storage\"\n\"32\",\"webforms-routing\"\n\"32\",\"licenses.licx\"\n\"32\",\"gloox\"\n\"32\",\"multihomed\"\n\"32\",\"webkit2\"\n\"32\",\"font-rendering\"\n\"32\",\"glr\"\n\"32\",\"pareto-optimality\"\n\"32\",\"parrot\"\n\"32\",\"compile-time-type-checking\"\n\"32\",\"parse4cn1\"\n\"32\",\"mtl-file\"\n\"32\",\"tquery\"\n\"32\",\"mtlbuffer\"\n\"32\",\"amazon-fire-os\"\n\"32\",\"argon\"\n\"32\",\"transferable\"\n\"32\",\"tps\"\n\"32\",\"tileserver-gl\"\n\"32\",\"subgurim-maps\"\n\"32\",\"max-age\"\n\"32\",\"automapper-9\"\n\"32\",\"measureoverride\"\n\"32\",\"scringo\"\n\"32\",\"zend-loader\"\n\"32\",\"uvloop\"\n\"32\",\"securitycenter\"\n\"32\",\"linux-security-module\"\n\"32\",\"scrutor\"\n\"32\",\"vaadin21\"\n\"32\",\"darksky\"\n\"32\",\"z-notation\"\n\"32\",\"zephir\"\n\"32\",\"zgrep\"\n\"32\",\"searchfiltercollection\"\n\"32\",\"if-none-match\"\n\"32\",\"maven-invoker-plugin\"\n\"32\",\"touchesended\"\n\"32\",\"std-byte\"\n\"32\",\"powerbuilder.net\"\n\"32\",\"seafile-server\"\n\"31\",\"defensive-copy\"\n\"31\",\"eclipse-tptp\"\n\"31\",\"graphcms\"\n\"31\",\"deepequals\"\n\"31\",\"basedon\"\n\"31\",\"cloudsight\"\n\"31\",\"matlabpool\"\n\"31\",\"floatbuffer\"\n\"31\",\"jboss-web\"\n\"31\",\"flutter-clippath\"\n\"31\",\"phpbrew\"\n\"31\",\"instancecontextmode\"\n\"31\",\"griddler\"\n\"31\",\"grapheme\"\n\"31\",\"eccodes\"\n\"31\",\"ts-mockito\"\n\"31\",\"graphene2\"\n\"31\",\"yii-validation\"\n\"31\",\"echo-server\"\n\"31\",\"php-amqp\"\n\"31\",\"multiparameter\"\n\"31\",\"clockpicker\"\n\"31\",\"flickable\"\n\"31\",\"telerik-combobox\"\n\"31\",\"repeating-linear-gradient\"\n\"31\",\"groupme\"\n\"31\",\"flutter-exception\"\n\"31\",\"local-security-policy\"\n\"31\",\"graphql.net\"\n\"31\",\"trustedconnection\"\n\"31\",\"sst\"\n\"31\",\"relaxed-atomics\"\n\"31\",\"gige-sdk\"\n\"31\",\"phonegap-facebook-plugin\"\n\"31\",\"getsockopt\"\n\"31\",\"process-group\"\n\"31\",\"fen\"\n\"31\",\"edgedb\"\n\"31\",\"eclipse-hawkbit\"\n\"31\",\"react-native-keychain\"\n\"31\",\"backbone-boilerplate\"\n\"31\",\"jenv\"\n\"31\",\"fluentautomation\"\n\"31\",\"product-key\"\n\"31\",\"liteide\"\n\"31\",\"filenet-cpe\"\n\"31\",\"phantom-wallet\"\n\"31\",\"cloudera-cdp\"\n\"31\",\"profile-provider\"\n\"31\",\"telescope.nvim\"\n\"31\",\"react-native-notifications\"\n\"31\",\"react-native-nfc-manager\"\n\"31\",\"livekit\"\n\"31\",\"trinidad-gem\"\n\"31\",\"sketching\"\n\"31\",\"terminal-server\"\n\"31\",\"fuser\"\n\"31\",\"unchecked-conversion\"\n\"31\",\"pikepdf\"\n\"31\",\"xdv\"\n\"31\",\"smartgwt-pro\"\n\"31\",\"undeploy\"\n\"31\",\"next-api\"\n\"31\",\"chip-8\"\n\"31\",\"ditto\"\n\"31\",\"in-app-purchase-receipt\"\n\"31\",\"celllist\"\n\"31\",\"ccm\"\n\"31\",\"flatfilereader\"\n\"31\",\"unirx\"\n\"31\",\"sentencepiece\"\n\"31\",\"filtered-lookup\"\n\"31\",\"cbutton\"\n\"31\",\"x-facebook-platform\"\n\"31\",\"contactus\"\n\"31\",\"adobe-javascript\"\n\"31\",\"fxg\"\n\"31\",\"xpress-optimizer\"\n\"31\",\"senti-wordnet\"\n\"31\",\"python-server-pages\"\n\"31\",\"findname\"\n\"31\",\"fstar\"\n\"31\",\"margin-left\"\n\"31\",\"socketmobile\"\n\"31\",\"finereader\"\n\"31\",\"adobe-director\"\n\"31\",\"binomial-theorem\"\n\"31\",\"appcfg\"\n\"31\",\"frontend-maven-plugin\"\n\"31\",\"social-network-friendship\"\n\"31\",\"blackduck\"\n\"31\",\"apache-spark-1.4\"\n\"31\",\"xpathdocument\"\n\"31\",\"jsfunit\"\n\"31\",\"bi-temporal\"\n\"31\",\"firebase-assistant\"\n\"31\",\"bixolon-printer\"\n\"31\",\"jtcalendar\"\n\"31\",\"soaplib\"\n\"31\",\"xirsys\"\n\"31\",\"pivot-without-aggregate\"\n\"31\",\"checklist-model\"\n\"31\",\"vshost.exe\"\n\"31\",\"soap1.2\"\n\"31\",\"jscoverage\"\n\"31\",\"falco\"\n\"31\",\"sitecore-azure\"\n\"31\",\"sharepointadmin\"\n\"31\",\"wcf-proxy\"\n\"31\",\"sisense\"\n\"31\",\"react-widgets\"\n\"31\",\"fastcgi-mono-server\"\n\"31\",\"microsoft-data-sqlclient\"\n\"31\",\"grammar-kit\"\n\"31\",\"gpy\"\n\"31\",\"mysql-error-2013\"\n\"31\",\"microsoft-cpp-unit-test\"\n\"31\",\"gprbuild\"\n\"31\",\"simplerepository\"\n\"31\",\"react-virtuoso\"\n\"31\",\"method-modifier\"\n\"31\",\"ora-04091\"\n\"31\",\"fastapiusers\"\n\"31\",\"fastparse\"\n\"31\",\"kissxml\"\n\"31\",\"share-plus\"\n\"31\",\"meteor-useraccounts\"\n\"31\",\"gradientdrawable\"\n\"31\",\"sharepoint-feature\"\n\"31\",\"failed-to-load-viewstate\"\n\"31\",\"aws-iam-identity-center\"\n\"31\",\"agm-map\"\n\"31\",\"css-houdini\"\n\"31\",\"mydac\"\n\"31\",\"css-import\"\n\"31\",\"serverpod\"\n\"31\",\"facebook-payments\"\n\"31\",\"rlwrap\"\n\"31\",\"push.js\"\n\"31\",\"w3-css\"\n\"31\",\"rubymotion-promotion\"\n\"31\",\"django-runserver\"\n\"31\",\"calendar-control\"\n\"31\",\"dltk\"\n\"31\",\"datakey\"\n\"31\",\"mysql-dependent-subquery\"\n\"31\",\"updateview\"\n\"31\",\"validation-controls\"\n\"31\",\"aio-mysql\"\n\"31\",\"mysql-error-1025\"\n\"31\",\"vuzix\"\n\"31\",\"rkobjectmapping\"\n\"31\",\"iconvertible\"\n\"31\",\"pyaudioanalysis\"\n\"31\",\"ruby-ffi\"\n\"31\",\"camera-matrix\"\n\"31\",\"windows-live-writer\"\n\"31\",\"service-node-port-range\"\n\"31\",\"angular-router-params\"\n\"31\",\"watch-window\"\n\"31\",\"ajax-forms\"\n\"31\",\"awsdeploy\"\n\"31\",\"cakephp3\"\n\"31\",\"canonical-form\"\n\"31\",\"average-precision\"\n\"31\",\"creative-cloud\"\n\"31\",\"aws-permissions\"\n\"31\",\"ibm-jvm\"\n\"31\",\"ajaxpro\"\n\"31\",\"near-real-time\"\n\"31\",\"saripaar\"\n\"31\",\"jqwik\"\n\"31\",\"ttphotoviewcontroller\"\n\"31\",\"aws-security-hub\"\n\"31\",\"jquery-delegate\"\n\"31\",\"jwi\"\n\"31\",\"apple-pencil\"\n\"31\",\"cosm\"\n\"31\",\"svg-android\"\n\"31\",\"jqte\"\n\"31\",\"juniper-network-connect\"\n\"31\",\"sardine\"\n\"31\",\"grunt-connect-proxy\"\n\"31\",\"spring-ide\"\n\"31\",\"nearley\"\n\"31\",\"spring-integration-ip\"\n\"31\",\"junitparams\"\n\"31\",\"tv4\"\n\"31\",\"gruff\"\n\"31\",\"android-sound\"\n\"31\",\"spring-security-cas\"\n\"31\",\"jquery-ui-plugins\"\n\"31\",\"information-gain\"\n\"31\",\"cpd\"\n\"31\",\"jquery-1.3.2\"\n\"31\",\"android-things-console\"\n\"31\",\"application-resource\"\n\"31\",\"onscrollchanged\"\n\"31\",\"inputscope\"\n\"31\",\"boost-ptr-container\"\n\"31\",\"boost-unordered\"\n\"31\",\"cornice\"\n\"31\",\"gulp-notify\"\n\"31\",\"twilio-javascript\"\n\"31\",\"nshttpcookiestorage\"\n\"31\",\"kantu\"\n\"31\",\"application-close\"\n\"31\",\"spring-cloud-skipper\"\n\"31\",\"nerdctl\"\n\"31\",\"twofish\"\n\"31\",\"password-autofill\"\n\"31\",\"inputformatter\"\n\"31\",\"input-history\"\n\"31\",\"writealltext\"\n\"31\",\"jruby-rack\"\n\"31\",\"posh-ssh\"\n\"31\",\"mod-proxy-html\"\n\"31\",\"aqtime\"\n\"31\",\"path-manipulation\"\n\"31\",\"netwire\"\n\"31\",\"oboe.js\"\n\"31\",\"analytics-engine\"\n\"31\",\"pynsist\"\n\"31\",\"brooklyn\"\n\"31\",\"video-ads\"\n\"31\",\"least-privilege\"\n\"31\",\"min3d\"\n\"31\",\"oslo\"\n\"31\",\"vix\"\n\"31\",\"microsoft-oauth\"\n\"31\",\"vifm\"\n\"31\",\"3cx\"\n\"31\",\"microsoft-runtime-library\"\n\"31\",\"osmium\"\n\"31\",\".ico\"\n\"31\",\"magic-constants\"\n\"31\",\"google-analytics-data-api\"\n\"31\",\"visibilitychange\"\n\"31\",\"aura.js\"\n\"31\",\"wit-ai\"\n\"31\",\"rfc5322\"\n\"31\",\"middleman-4\"\n\"31\",\"viewresult\"\n\"31\",\"short-circuit-evaluation\"\n\"31\",\"system-monitoring\"\n\"31\",\"nzsql\"\n\"31\",\"shopify-javascript-buy-sdk\"\n\"31\",\"rdiscount\"\n\"31\",\"rbokeh\"\n\"31\",\"bufferedstream\"\n\"31\",\"set-include-path\"\n\"31\",\"mirage\"\n\"31\",\"kotlinx.coroutines.flow\"\n\"31\",\"netscape\"\n\"31\",\"libjson\"\n\"31\",\"f#-async\"\n\"31\",\"oracle-xml-publisher\"\n\"31\",\"visual-web-gui\"\n\"31\",\"pyhdf\"\n\"31\",\"sysobjects\"\n\"31\",\"ordered-test\"\n\"31\",\"objectpool\"\n\"31\",\"rhythmbox\"\n\"31\",\".net-core-publishsinglefile\"\n\"31\",\"rhtml\"\n\"31\",\"windsor-facilities\"\n\"31\",\"express-rate-limit\"\n\"31\",\"javascript-audio-api\"\n\"31\",\"jar-with-dependencies\"\n\"31\",\"azure-oauth\"\n\"31\",\"radhtmlchart\"\n\"31\",\"jak\"\n\"31\",\"holoviz-panel\"\n\"31\",\"aspwizard\"\n\"31\",\"azure-sql-edge\"\n\"31\",\"togaf\"\n\"31\",\"jahia\"\n\"31\",\"sccm-2007\"\n\"31\",\"horizontal-accordion\"\n\"31\",\"r7rs\"\n\"31\",\"sql-calc-found-rows\"\n\"31\",\"qwikjs\"\n\"31\",\"uicontrolstate\"\n\"31\",\"num-lock\"\n\"31\",\"mkmaprect\"\n\"31\",\"quicklaunch\"\n\"31\",\"normalize-css\"\n\"31\",\"xamarin.mobile\"\n\"31\",\"non-maximum-suppression\"\n\"31\",\"digital-analog-converter\"\n\"31\",\"tclientsocket\"\n\"31\",\"device-name\"\n\"31\",\"devise-jwt\"\n\"31\",\"isinrole\"\n\"31\",\"devise-recoverable\"\n\"31\",\"sql-revoke\"\n\"31\",\"hierarchyviewer\"\n\"31\",\"openrouteservice\"\n\"31\",\"titled-border\"\n\"31\",\"uiscene\"\n\"31\",\"tinygo\"\n\"31\",\"openiso8583.net\"\n\"31\",\"scratch-file\"\n\"31\",\"redisinsights\"\n\"31\",\"baasbox\"\n\"31\",\"tail-call\"\n\"31\",\"hibernate-generic-dao\"\n\"31\",\"controller-factory\"\n\"31\",\"open-mobile-api\"\n\"31\",\"android-os-handler\"\n\"31\",\"colortransform\"\n\"31\",\"protocol-buffers-3\"\n\"31\",\"qpolygon\"\n\"31\",\"resgen\"\n\"31\",\"ejabberd-auth\"\n\"31\",\"zsi\"\n\"31\",\"cfdirectory\"\n\"31\",\"lrs\"\n\"31\",\"zsh-alias\"\n\"31\",\"layoutmargins\"\n\"31\",\"cewp\"\n\"31\",\"residemenu\"\n\"31\",\"android-obfuscation\"\n\"31\",\"laspy\"\n\"31\",\"http-status-code-411\"\n\"31\",\"collator\"\n\"31\",\"escrow\"\n\"31\",\"com0com\"\n\"31\",\"streamline\"\n\"31\",\"resharper-6.1\"\n\"31\",\"odftoolkit\"\n\"31\",\"nifty-gui\"\n\"31\",\"textsum\"\n\"31\",\"memory-editing\"\n\"31\",\"qcolor\"\n\"31\",\"es5-compatiblity\"\n\"31\",\"react-monaco-editor\"\n\"31\",\"ios-targets\"\n\"31\",\"mplab-x\"\n\"31\",\"geneva-framework\"\n\"31\",\"elcimagepickercontroller\"\n\"31\",\"reachability-swift\"\n\"31\",\"october-partial\"\n\"31\",\"custom-paging\"\n\"31\",\"android-multi-module\"\n\"31\",\"persona\"\n\"31\",\"pgbackrest\"\n\"31\",\"ipp-protocol\"\n\"31\",\"cgpdfcontext\"\n\"31\",\"memberpress\"\n\"31\",\"android-open-accessory\"\n\"31\",\"pytmx\"\n\"31\",\"mpf\"\n\"31\",\"strictfp\"\n\"31\",\"ios6.0\"\n\"31\",\"ios-universal-framework\"\n\"31\",\"reactivekit\"\n\"31\",\"sp-msforeachdb\"\n\"31\",\"zxing-js\"\n\"31\",\"ios-web-app\"\n\"31\",\"cgit\"\n\"31\",\"google-data\"\n\"31\",\"react-intersection-observer\"\n\"31\",\"peek-pop\"\n\"31\",\"e-token\"\n\"31\",\"columnspan\"\n\"31\",\"commission-junction\"\n\"31\",\"qtwebview\"\n\"31\",\"powerpoint-web-addins\"\n\"31\",\"webforms-view-engine\"\n\"31\",\"sony-lifelog-api\"\n\"31\",\"user-mode-linux\"\n\"31\",\"textutils\"\n\"31\",\"zalgo\"\n\"31\",\"energyplus\"\n\"31\",\"end-to-end-encryption\"\n\"31\",\"hawkular\"\n\"31\",\"query-variables\"\n\"31\",\"cwrsync\"\n\"31\",\"sdo\"\n\"31\",\"sort-object\"\n\"31\",\"healthvault\"\n\"31\",\"powerbuilder-conversion\"\n\"31\",\"usb-hid\"\n\"31\",\"cview\"\n\"31\",\"gksudo\"\n\"31\",\"bigcommerce-stencil-cli\"\n\"31\",\"passenger-nginx\"\n\"31\",\"has-scope\"\n\"31\",\"fontawesome-4.4.0\"\n\"31\",\"header-row\"\n\"31\",\"sttp\"\n\"31\",\"google-suggest\"\n\"31\",\"emptydatatext\"\n\"31\",\"mediarouter\"\n\"31\",\"haskell-language-server\"\n\"31\",\"structopt\"\n\"31\",\"ms-project-server-2016\"\n\"31\",\"composite-literals\"\n\"31\",\"here-routing\"\n\"31\",\"basis\"\n\"31\",\"webclient.uploaddata\"\n\"31\",\"heroku-pipelines\"\n\"31\",\"iepngfix\"\n\"31\",\"google-java-format\"\n\"31\",\"torsocks\"\n\"31\",\"heterogeneous-services\"\n\"31\",\"flutter-scaffold\"\n\"31\",\"vaadin22\"\n\"31\",\"elysiajs\"\n\"31\",\"mayanedms\"\n\"31\",\"linguaplone\"\n\"31\",\"linguijs\"\n\"31\",\"thunderbird-webextensions\"\n\"31\",\"multihead-attention\"\n\"31\",\"linktext\"\n\"30\",\"bart\"\n\"30\",\"floating-point-comparison\"\n\"30\",\"deep-diff\"\n\"30\",\"ebay-lms\"\n\"30\",\"react-simple-maps\"\n\"30\",\"skia4delphi\"\n\"30\",\"teradata-aster\"\n\"30\",\"backbone-local-storage\"\n\"30\",\"antlr2\"\n\"30\",\"x-robots-tag\"\n\"30\",\"phassetcollection\"\n\"30\",\"vue-quill-editor\"\n\"30\",\"loadmodule\"\n\"30\",\"webspeed\"\n\"30\",\"load-order\"\n\"30\",\"multiton\"\n\"30\",\"antora\"\n\"30\",\"jemmy\"\n\"30\",\"yii2-widget\"\n\"30\",\"skaudionode\"\n\"30\",\"tensorflow-quantum\"\n\"30\",\"instance-method\"\n\"30\",\"git-hash\"\n\"30\",\"multiscaleimage\"\n\"30\",\"vuejs-datepicker\"\n\"30\",\"tri-state-logic\"\n\"30\",\"intel-atom\"\n\"30\",\"graphene-sqlalchemy\"\n\"30\",\"fdopen\"\n\"30\",\"phonegap-plugin-push\"\n\"30\",\"graphical-interaction\"\n\"30\",\"antv\"\n\"30\",\"relative-locators\"\n\"30\",\"ferret\"\n\"30\",\"flowpanel\"\n\"30\",\"vsprops\"\n\"30\",\"tsfresh\"\n\"30\",\"flowfile\"\n\"30\",\"eclipse-microprofile-config\"\n\"30\",\"efl\"\n\"30\",\"efi\"\n\"30\",\"grasp\"\n\"30\",\"tsconfig.json\"\n\"30\",\"flotr\"\n\"30\",\"stackify\"\n\"30\",\"slamdata\"\n\"30\",\"privoxy\"\n\"30\",\"reify\"\n\"30\",\"fetched-property\"\n\"30\",\"interactjs\"\n\"30\",\"sql-server-linux\"\n\"30\",\"liquidfun\"\n\"30\",\"file-generation\"\n\"30\",\"dcommit\"\n\"30\",\"tropo\"\n\"30\",\"clistbox\"\n\"30\",\"gigaspaces\"\n\"30\",\"slimv\"\n\"30\",\"weak-events\"\n\"30\",\"php-ml\"\n\"30\",\"ssl-handshake\"\n\"30\",\"xpathnodeiterator\"\n\"30\",\"python-beautifultable\"\n\"30\",\"kube-controller-manager\"\n\"30\",\"piccolo\"\n\"30\",\"senchatouch-2.4\"\n\"30\",\"soft-hyphen\"\n\"30\",\"c-ares\"\n\"30\",\"ccw\"\n\"30\",\"app-engine-patch\"\n\"30\",\"safehandle\"\n\"30\",\"sendgrid-api-v2\"\n\"30\",\"vroom\"\n\"30\",\"snoopy\"\n\"30\",\"python-onvif\"\n\"30\",\"swiftui-toolbar\"\n\"30\",\"vote-up-buttons\"\n\"30\",\"datazen-server\"\n\"30\",\"fsharp.data.sqlclient\"\n\"30\",\"causal-inference\"\n\"30\",\"circular-permutations\"\n\"30\",\"biojava\"\n\"30\",\"jsmooth\"\n\"30\",\"finite-state-automaton\"\n\"30\",\"pagepiling.js\"\n\"30\",\"mapael\"\n\"30\",\"imaskjs\"\n\"30\",\"manifest.cache\"\n\"30\",\"flash-player-11\"\n\"30\",\"unity2.0\"\n\"30\",\"pagy\"\n\"30\",\"land-of-lisp\"\n\"30\",\"manchester-syntax\"\n\"30\",\"filterattribute\"\n\"30\",\"lanczos\"\n\"30\",\"lager\"\n\"30\",\"jsonfield\"\n\"30\",\"inbound-security-rule\"\n\"30\",\"blacklight\"\n\"30\",\"firefox-driver\"\n\"30\",\"data-serialization\"\n\"30\",\"nextion\"\n\"30\",\"configsource\"\n\"30\",\"biztalk-rule-engine\"\n\"30\",\"kubernetes-rook\"\n\"30\",\"fileopener2\"\n\"30\",\"apache-spark-3.0\"\n\"30\",\"blackberry-push\"\n\"30\",\"chrome-dev-editor\"\n\"30\",\"uivibrancyeffect\"\n\"30\",\"kubernetes-cluster\"\n\"30\",\"keystonejs6\"\n\"30\",\"angular-ngmodelchange\"\n\"30\",\"fastapi-middleware\"\n\"30\",\"docbook-xsl\"\n\"30\",\"red-black-tree-insertion\"\n\"30\",\"avmetadataitem\"\n\"30\",\"keywindow\"\n\"30\",\"microblink\"\n\"30\",\"alassetlibrary\"\n\"30\",\"animate-on-scroll\"\n\"30\",\"vendor-branch\"\n\"30\",\"server-tags\"\n\"30\",\"ibm-ifs\"\n\"30\",\"sitecore-exm\"\n\"30\",\"read-committed\"\n\"30\",\"servicecollection\"\n\"30\",\"rndis\"\n\"30\",\"opnet\"\n\"30\",\"nana\"\n\"30\",\"alexa-sdk-nodejs\"\n\"30\",\"cantera\"\n\"30\",\"namedpipeserverstream\"\n\"30\",\"aws-app-runner\"\n\"30\",\"variadic-tuple-types\"\n\"30\",\"windows-credential-provider\"\n\"30\",\"django-push-notifications\"\n\"30\",\"vuex-orm\"\n\"30\",\"vuforia-cloud-recognition\"\n\"30\",\"cameracapturetask\"\n\"30\",\"recordtype\"\n\"30\",\"vultr\"\n\"30\",\"windows-media-encoder\"\n\"30\",\"joomla-content-editor\"\n\"30\",\"react-with-styles\"\n\"30\",\"fast-esp\"\n\"30\",\"row-major-order\"\n\"30\",\"wcf-wshttpbinding\"\n\"30\",\"aiopg\"\n\"30\",\"ora-12560\"\n\"30\",\"californium\"\n\"30\",\"faces-flow\"\n\"30\",\"aim\"\n\"30\",\"mysql-error-1044\"\n\"30\",\"rsyntaxtextarea\"\n\"30\",\"document-provider\"\n\"30\",\"updatable-views\"\n\"30\",\"google-truth\"\n\"30\",\"fastroute\"\n\"30\",\"sipjs\"\n\"30\",\"calendarcontract\"\n\"30\",\"django-suit\"\n\"30\",\"airpods\"\n\"30\",\"hyperledger-iroha\"\n\"30\",\"akka-kafka\"\n\"30\",\"faultcontract\"\n\"30\",\"read-unread\"\n\"30\",\"real-datatype\"\n\"30\",\"fatwire\"\n\"30\",\"karabiner\"\n\"30\",\"gulp-task\"\n\"30\",\"k8s-rolebinding\"\n\"30\",\"twitter-bootstrap-4-beta\"\n\"30\",\"apple-documentation\"\n\"30\",\"pcap-ng\"\n\"30\",\"bluenrg\"\n\"30\",\"spring-security-test\"\n\"30\",\"grunt-ts\"\n\"30\",\"spring-social-google\"\n\"30\",\"countplot\"\n\"30\",\"corewlan\"\n\"30\",\"invalid-pointer\"\n\"30\",\"spring-batch-stream\"\n\"30\",\"kartograph\"\n\"30\",\"twitter-api-v1\"\n\"30\",\"spring-cloud-connectors\"\n\"30\",\"desktop-sharing\"\n\"30\",\"enrich-my-library\"\n\"30\",\"android-sql\"\n\"30\",\"inputmethodmanager\"\n\"30\",\"boxen\"\n\"30\",\"episerver-8\"\n\"30\",\"azure-app-api\"\n\"30\",\"azure-china\"\n\"30\",\"twilio-conference\"\n\"30\",\"android-textview-autosize\"\n\"30\",\"dynamic-sizing\"\n\"30\",\"spring-gem\"\n\"30\",\"sass-maps\"\n\"30\",\"nsanimationcontext\"\n\"30\",\"boinc\"\n\"30\",\"delphi-units\"\n\"30\",\"pomegranate\"\n\"30\",\"mongoid5\"\n\"30\",\"eaglcontext\"\n\"30\",\"applepay-web\"\n\"30\",\"application-client\"\n\"30\",\"neoeloquent\"\n\"30\",\"io-buffering\"\n\"30\",\"appsflyer-ios-sdk\"\n\"30\",\"couchdb-lucene\"\n\"30\",\"swift-composable-architecture\"\n\"30\",\"blender-2.50\"\n\"30\",\"sap-conversational-ai\"\n\"30\",\"android-studio-electric-eel\"\n\"30\",\"juddi\"\n\"30\",\"onexception\"\n\"30\",\"http-1.0\"\n\"30\",\"entityspaces\"\n\"30\",\"postman-mocks\"\n\"30\",\"nsfontmanager\"\n\"30\",\"positive-lookahead\"\n\"30\",\"twitter-bootstrap-wizard\"\n\"30\",\"power-analysis\"\n\"30\",\"workflow-manager-1.x\"\n\"30\",\"neos-server\"\n\"30\",\"nbuilder\"\n\"30\",\"organizational-chart\"\n\"30\",\"objectstatemanager\"\n\"30\",\"systemmenu\"\n\"30\",\"netbeans-10\"\n\"30\",\"browser-api\"\n\"30\",\"system-integration\"\n\"30\",\"libqxt\"\n\"30\",\"javaapns\"\n\"30\",\"bubble-popup\"\n\"30\",\"domino-appdev-pack\"\n\"30\",\"godbolt\"\n\"30\",\"java-bridge-method\"\n\"30\",\"javabuilders\"\n\"30\",\"form-processing\"\n\"30\",\"sigils\"\n\"30\",\"breezingforms\"\n\"30\",\"virtual-file\"\n\"30\",\"coldfusion-2021\"\n\"30\",\"orphaned-objects\"\n\"30\",\"formsflow\"\n\"30\",\"syncml\"\n\"30\",\"coldfire\"\n\"30\",\"pyfirmata\"\n\"30\",\"midi-interface\"\n\"30\",\"pyftpdlib\"\n\"30\",\"256color\"\n\"30\",\"browserstack-app-automate\"\n\"30\",\"minimum-cut\"\n\"30\",\"ui5-webcomponents\"\n\"30\",\"browser-scrollbars\"\n\"30\",\"siesta\"\n\"30\",\"gold-parser\"\n\"30\",\".net-core-1.1\"\n\"30\",\"minifiedjs\"\n\"30\",\"rhq\"\n\"30\",\"sfml.net\"\n\"30\",\"vivagraphjs\"\n\"30\",\"oshi\"\n\"30\",\"777\"\n\"30\",\"rhino-etl\"\n\"30\",\"ott\"\n\"30\",\"lumia\"\n\"30\",\"kotlin-delegate\"\n\"30\",\"virtualstore\"\n\"30\",\"pypika\"\n\"30\",\"vivaldi\"\n\"30\",\"knppaginatorbundle\"\n\"30\",\"abap-adt\"\n\"30\",\"setf\"\n\"30\",\"sfauthenticationsession\"\n\"30\",\"rich-client-platform\"\n\"30\",\"pytest-aiohttp\"\n\"30\",\"cocos2d-iphone-3\"\n\"30\",\"hiqpdf\"\n\"30\",\"xamarin.forms.carouselview\"\n\"30\",\"asp-net-mvc-1\"\n\"30\",\"scrapy-playwright\"\n\"30\",\"dot-plot\"\n\"30\",\"scrapy-selenium\"\n\"30\",\"registration-free-com\"\n\"30\",\"control-c\"\n\"30\",\"hlint\"\n\"30\",\"gcc10\"\n\"30\",\"astoria\"\n\"30\",\"tnsping\"\n\"30\",\"nox\"\n\"30\",\"mkcircle\"\n\"30\",\"tippecanoe\"\n\"30\",\"cakeemail\"\n\"30\",\"radtextbox\"\n\"30\",\"c5.0\"\n\"30\",\"azure-fluent-api\"\n\"30\",\"gatsby-remark-image\"\n\"30\",\"p-np\"\n\"30\",\"expect.pm\"\n\"30\",\"openid-selector\"\n\"30\",\"android-compose-button\"\n\"30\",\"itemselector\"\n\"30\",\"pluggable-database\"\n\"30\",\"tar.gz\"\n\"30\",\"png-24\"\n\"30\",\"toisostring\"\n\"30\",\"opengl-es-3.1\"\n\"30\",\"android-cards\"\n\"30\",\"norton\"\n\"30\",\"redpitaya\"\n\"30\",\"byteman\"\n\"30\",\"no-match\"\n\"30\",\"burrows-wheeler-transform\"\n\"30\",\"doxygen-addtogroup\"\n\"30\",\"assertraises\"\n\"30\",\"gcutil\"\n\"30\",\"drupal-field-api\"\n\"30\",\"scntext\"\n\"30\",\"xcelsius\"\n\"30\",\"openstacksdk\"\n\"30\",\"android-fragmentscenario\"\n\"30\",\"openscript\"\n\"30\",\"mkstemp\"\n\"30\",\"point-in-time\"\n\"30\",\"node-libcurl\"\n\"30\",\"spout\"\n\"30\",\"layouttransform\"\n\"30\",\"mef2\"\n\"30\",\"iosurface\"\n\"30\",\"ios-vision\"\n\"30\",\"nlm\"\n\"30\",\"metaobject\"\n\"30\",\"moryx\"\n\"30\",\"project-hosting\"\n\"30\",\"nodemanager\"\n\"30\",\"activex-exe\"\n\"30\",\"http-status-code-409\"\n\"30\",\"pecs\"\n\"30\",\"custom-eventlog\"\n\"30\",\"launch-template\"\n\"30\",\"dual-table\"\n\"30\",\"mono-embedding\"\n\"30\",\"provisioned-iops\"\n\"30\",\"elasticsearch-ruby\"\n\"30\",\"responsive-emails\"\n\"30\",\"esri-javascript-api\"\n\"30\",\"angular-jest\"\n\"30\",\"locationmatch\"\n\"30\",\"mpic++\"\n\"30\",\"etags\"\n\"30\",\"logging-application-block\"\n\"30\",\"angular-dragula\"\n\"30\",\"mousearea\"\n\"30\",\"elasticity\"\n\"30\",\"commandbox\"\n\"30\",\"qandroidjniobject\"\n\"30\",\"hyperic\"\n\"30\",\"terraform0.11\"\n\"30\",\"actionbarsherlock-map\"\n\"30\",\"resource-editor\"\n\"30\",\"command-history\"\n\"30\",\"resonance-audio\"\n\"30\",\"strconv\"\n\"30\",\"chardev\"\n\"30\",\"pywt\"\n\"30\",\"genericsetup\"\n\"30\",\"longest-prefix\"\n\"30\",\"node-firebird\"\n\"30\",\"irssi\"\n\"30\",\"getresponsestream\"\n\"30\",\"mozilla-sops\"\n\"30\",\"nhunspell\"\n\"30\",\"httpinterceptor\"\n\"30\",\"angular2-compiler\"\n\"30\",\"omniauth-twitter\"\n\"30\",\"requests-oauthlib\"\n\"30\",\"android-livedata-transformations\"\n\"30\",\"reactfx\"\n\"30\",\"streamwriter.write\"\n\"30\",\"zk-snark\"\n\"30\",\"quarkus-extension\"\n\"30\",\"secure-context\"\n\"30\",\"vaadin-spring-boot\"\n\"30\",\"git-track\"\n\"30\",\"soy-templates\"\n\"30\",\"structured-exception\"\n\"30\",\"art-runtime\"\n\"30\",\"uwp-navigation\"\n\"30\",\"msisdn\"\n\"30\",\"ietf-restconf\"\n\"30\",\"zones\"\n\"30\",\"securitydomain\"\n\"30\",\"glium\"\n\"30\",\"hdcp\"\n\"30\",\"zend-servicemanager\"\n\"30\",\"webargs\"\n\"30\",\"topaz-signatures\"\n\"30\",\"web-animations-api\"\n\"30\",\"practical-common-lisp\"\n\"30\",\"best-buy-api\"\n\"30\",\"qsystemtrayicon\"\n\"30\",\"pragma-pack\"\n\"30\",\"stryker\"\n\"30\",\"ident\"\n\"30\",\"sundials\"\n\"30\",\"starman\"\n\"30\",\"solr7\"\n\"30\",\"concurrency-runtime\"\n\"30\",\"sunpkcs11\"\n\"30\",\"liipfunctionaltestbundle\"\n\"30\",\"pandorabots\"\n\"30\",\"beginthread\"\n\"30\",\"scriban\"\n\"30\",\"passive-event-listeners\"\n\"30\",\"mule-module-jpa\"\n\"30\",\"arcgis-online\"\n\"30\",\"amazon-cloudhsm\"\n\"30\",\"soundchannel\"\n\"30\",\"sui\"\n\"30\",\"ido-mode\"\n\"30\",\"stickyrecycleview\"\n\"30\",\"msde\"\n\"30\",\"liferay-7.3\"\n\"30\",\"zegocloud\"\n\"30\",\"flutter-razorpay\"\n\"30\",\"qt-resource\"\n\"30\",\"zef\"\n\"30\",\"sucker-punch\"\n\"30\",\"ie7.js\"\n\"30\",\"tilt-sensor\"\n\"30\",\"completion-service\"\n\"30\",\"theme-ui\"\n\"30\",\"amazon-javascript-sdk\"\n\"30\",\"msbuild-wpp\"\n\"30\",\"fm-radio\"\n\"30\",\"amazon-msk\"\n\"30\",\"autosummary\"\n\"29\",\"wifstream\"\n\"29\",\"clair\"\n\"29\",\"tsdoc\"\n\"29\",\"debian-bookworm\"\n\"29\",\"fibplus\"\n\"29\",\"flutter-3.0\"\n\"29\",\"edge-function\"\n\"29\",\"skfuzzy\"\n\"29\",\"whatwg-streams-api\"\n\"29\",\"localsystem\"\n\"29\",\"defadvice\"\n\"29\",\"liveview\"\n\"29\",\"teaser\"\n\"29\",\"progid\"\n\"29\",\"felogin\"\n\"29\",\"git-grep\"\n\"29\",\"bankid\"\n\"29\",\"remotes\"\n\"29\",\"maui-shell\"\n\"29\",\"phaser\"\n\"29\",\"flexicious\"\n\"29\",\"livelock\"\n\"29\",\"mutable-reference\"\n\"29\",\"trustwallet\"\n\"29\",\"jbase\"\n\"29\",\"ecpg\"\n\"29\",\"phfetchoptions\"\n\"29\",\"intake\"\n\"29\",\"github-actions-reusable-workflows\"\n\"29\",\"tera\"\n\"29\",\"processmodel\"\n\"29\",\"jchartfx\"\n\"29\",\"xstream-js\"\n\"29\",\"clpb\"\n\"29\",\"gitbucket\"\n\"29\",\"deep-dream\"\n\"29\",\"teachable-machine\"\n\"29\",\"base58\"\n\"29\",\"floating-labels\"\n\"29\",\"client-hints\"\n\"29\",\"x-tag\"\n\"29\",\"deepar\"\n\"29\",\"wicket-8\"\n\"29\",\"mvvm-foundation\"\n\"29\",\"wide-column-store\"\n\"29\",\"xsom\"\n\"29\",\"ffplay\"\n\"29\",\"annox\"\n\"29\",\"claude\"\n\"29\",\"ssms-addin\"\n\"29\",\"loadui\"\n\"29\",\"jboss-messaging\"\n\"29\",\"photo-picker\"\n\"29\",\"babelfish\"\n\"29\",\"jboss-forge\"\n\"29\",\"vssettings\"\n\"29\",\"ssrf\"\n\"29\",\"clipped\"\n\"29\",\"react-responsive-carousel\"\n\"29\",\"smacss\"\n\"29\",\"phpeclipse\"\n\"29\",\"groupon\"\n\"29\",\"giter8\"\n\"29\",\"ssrs-subscription\"\n\"29\",\"apache-ace\"\n\"29\",\"blaze-html\"\n\"29\",\"swish\"\n\"29\",\"pace\"\n\"29\",\"rush\"\n\"29\",\"apache-spark-sql-repartition\"\n\"29\",\"bi-tool\"\n\"29\",\"frequency-domain\"\n\"29\",\"blackberry-maps\"\n\"29\",\"bitmex\"\n\"29\",\"vsdbcmd\"\n\"29\",\"bitly\"\n\"29\",\"bitcoinlib\"\n\"29\",\"discoverability\"\n\"29\",\"voxels\"\n\"29\",\"manifoldcf\"\n\"29\",\"xml.modify\"\n\"29\",\"front-end-optimization\"\n\"29\",\"rxjs-subscriptions\"\n\"29\",\"sockaddr-in\"\n\"29\",\"vobject\"\n\"29\",\"apigee127\"\n\"29\",\"mariadb-10.2\"\n\"29\",\"binary-indexed-tree\"\n\"29\",\"laratrust\"\n\"29\",\"s5\"\n\"29\",\"implementation-defined-behavior\"\n\"29\",\"implication\"\n\"29\",\"sablecc\"\n\"29\",\"xetex\"\n\"29\",\"function-constructor\"\n\"29\",\"python-netifaces\"\n\"29\",\"safenet\"\n\"29\",\"unichar\"\n\"29\",\"xpointer\"\n\"29\",\"functions-framework\"\n\"29\",\"select-insert\"\n\"29\",\"include-once\"\n\"29\",\"python-decouple\"\n\"29\",\"xdp-pdf\"\n\"29\",\"uivideoeditorcontroller\"\n\"29\",\"smartedit\"\n\"29\",\"smartercsv\"\n\"29\",\"category-abstractions\"\n\"29\",\"ngrx-component-store\"\n\"29\",\"jsonpath-ng\"\n\"29\",\"chinese-remainder-theorem\"\n\"29\",\"castcompanionlibrary\"\n\"29\",\"pkpass\"\n\"29\",\"datetimeformatinfo\"\n\"29\",\"circleimage\"\n\"29\",\"fire-sharp\"\n\"29\",\"piwiktracker\"\n\"29\",\"cegui\"\n\"29\",\"fiware-sth-comet\"\n\"29\",\"fixer.io\"\n\"29\",\"flartoolkit\"\n\"29\",\"jstatd\"\n\"29\",\"ng2-datepicker\"\n\"29\",\"flascc\"\n\"29\",\"flatfilesource\"\n\"29\",\"fileupdate\"\n\"29\",\"smtps\"\n\"29\",\"chatfuel\"\n\"29\",\"datechooser\"\n\"29\",\"p-lang\"\n\"29\",\"aioredis\"\n\"29\",\"will-change\"\n\"29\",\"gradle-wrapper\"\n\"29\",\"ora-12170\"\n\"29\",\"windows-live-messenger\"\n\"29\",\"jpedal\"\n\"29\",\"r-mosaic\"\n\"29\",\"mfrc522\"\n\"29\",\"pssh\"\n\"29\",\"robotc\"\n\"29\",\"django-model-utils\"\n\"29\",\"shared-state\"\n\"29\",\"route-parameters\"\n\"29\",\"databricks-dbx\"\n\"29\",\"ruby-3.1\"\n\"29\",\"angular-json\"\n\"29\",\"metatype\"\n\"29\",\"rs256\"\n\"29\",\"crouton-os\"\n\"29\",\"ibm-ilog-opl\"\n\"29\",\"critical-css\"\n\"29\",\"avsc\"\n\"29\",\"jgss\"\n\"29\",\"serenity\"\n\"29\",\"jpegoptim\"\n\"29\",\"sinaweibo\"\n\"29\",\"graph-api-explorer\"\n\"29\",\"jmxtrans\"\n\"29\",\"robotics-studio\"\n\"29\",\"servercontrol\"\n\"29\",\"unsafe-perform-io\"\n\"29\",\"rtaudio\"\n\"29\",\"waitone\"\n\"29\",\"windows-server-2003-r2\"\n\"29\",\"sinopia\"\n\"29\",\"sequel-gem\"\n\"29\",\"microsoft-dynamics-webapi\"\n\"29\",\"pthread-exit\"\n\"29\",\"farpoint-spread\"\n\"29\",\"documentum-dfs\"\n\"29\",\"purify\"\n\"29\",\"jpamodelgen\"\n\"29\",\"vanity\"\n\"29\",\"cal-heatmap\"\n\"29\",\"rebus-azureservicebus\"\n\"29\",\"method-interception\"\n\"29\",\"wagtail-apiv2\"\n\"29\",\"winbgi\"\n\"29\",\"ora-00900\"\n\"29\",\"ruby-paranoia\"\n\"29\",\"angular-tour-of-heroes\"\n\"29\",\"sharepoint-branding\"\n\"29\",\"ora-00905\"\n\"29\",\"upstream\"\n\"29\",\"mygeneration\"\n\"29\",\"dmi\"\n\"29\",\"sharepoint-deployment\"\n\"29\",\"do-catch\"\n\"29\",\"error-recovery\"\n\"29\",\"ttlauncherview\"\n\"29\",\"azure-application-settings\"\n\"29\",\"b-plus-tree\"\n\"29\",\"apptentive\"\n\"29\",\"azure-application-insights-profiler\"\n\"29\",\"android-vertical-seekbar\"\n\"29\",\"pootle\"\n\"29\",\"box-view-api\"\n\"29\",\"boost-uuid\"\n\"29\",\"input-type-range\"\n\"29\",\"gulp-nunjucks-render\"\n\"29\",\"surface-pro\"\n\"29\",\"e1071\"\n\"29\",\"nearprotocol-validator\"\n\"29\",\"openehr\"\n\"29\",\"apple-live-photos\"\n\"29\",\"nest-nested-object\"\n\"29\",\"modelr\"\n\"29\",\"mongodb-hadoop\"\n\"29\",\"dynamic-data-list\"\n\"29\",\"nsdragginginfo\"\n\"29\",\"paystack\"\n\"29\",\"bottomappbar\"\n\"29\",\"mongoid6\"\n\"29\",\"boost-compute\"\n\"29\",\"pcap4j\"\n\"29\",\"applitools\"\n\"29\",\"cosmosdbtrigger\"\n\"29\",\"dynamic-linq-core\"\n\"29\",\"wp-enqueue-scripts\"\n\"29\",\"simple.odata\"\n\"29\",\"aws-sdk-rust\"\n\"29\",\"module-augmentation\"\n\"29\",\"wso2-appm\"\n\"29\",\"jquery-tags-input\"\n\"29\",\"spring-environment\"\n\"29\",\"android-work-profile\"\n\"29\",\"mongodb-charts\"\n\"29\",\"sveltekit-adapter-node\"\n\"29\",\"grpcurl\"\n\"29\",\"onkeylistener\"\n\"29\",\"ttr\"\n\"29\",\"gui-toolkit\"\n\"29\",\"mod-evasive\"\n\"29\",\"nelmiocorsbundle\"\n\"29\",\"kdevelop4\"\n\"29\",\"nscolorpanel\"\n\"29\",\"jrails\"\n\"29\",\"epos\"\n\"29\",\"aramex\"\n\"29\",\"jqzoom\"\n\"29\",\"deployr\"\n\"29\",\"gogo-shell\"\n\"29\",\"syncservices\"\n\"29\",\"ubuntu-20.10\"\n\"29\",\"wnck\"\n\"29\",\"object-expected\"\n\"29\",\"ubuntu-8.04\"\n\"29\",\"lfe\"\n\"29\",\"pygad\"\n\"29\",\"lib.web.mvc\"\n\"29\",\"shadertoy\"\n\"29\",\"javaspaces\"\n\"29\",\"facebook-customer-chat\"\n\"29\",\"2d-3d-conversion\"\n\"29\",\"net-ftp\"\n\"29\",\"pykinect\"\n\"29\",\"visiblox\"\n\"29\",\"macfuse\"\n\"29\",\"othello\"\n\"29\",\"rhel9\"\n\"29\",\".net-core-configuration\"\n\"29\",\"googleads-mobile-unity\"\n\"29\",\"sfcalendar\"\n\"29\",\"atomicboolean\"\n\"29\",\"javaplot\"\n\"29\",\"ugc\"\n\"29\",\"macromedia\"\n\"29\",\"amember\"\n\"29\",\"exprtk\"\n\"29\",\"t4-toolbox\"\n\"29\",\"macro-recorder\"\n\"29\",\"or-condition\"\n\"29\",\"virtex\"\n\"29\",\"pyicu\"\n\"29\",\"ming\"\n\"29\",\"asyncresttemplate\"\n\"29\",\"rdo\"\n\"29\",\"mitk\"\n\"29\",\"netapi32\"\n\"29\",\"winrun4j\"\n\"29\",\"br-automation-studio\"\n\"29\",\"microsoft-graph-security\"\n\"29\",\"reward-system\"\n\"29\",\"rawrepresentable\"\n\"29\",\"rinsim\"\n\"29\",\"formal-semantics\"\n\"29\",\"libraw\"\n\"29\",\"osmbonuspack\"\n\"29\",\"newsapi\"\n\"29\",\".net-standard-1.5\"\n\"29\",\"r-inla\"\n\"29\",\"visual-studio-app-center-test\"\n\"29\",\"shopify-app-bridge\"\n\"29\",\"visual-studio-installer\"\n\"29\",\"signer\"\n\"29\",\"ova\"\n\"29\",\"vmmap\"\n\"29\",\"winverifytrust\"\n\"29\",\"python-2.3\"\n\"29\",\"setenvif\"\n\"29\",\"netflix-metaflow\"\n\"29\",\"forever-monitor\"\n\"29\",\"auth0-connection\"\n\"29\",\"bsondocument\"\n\"29\",\"amslidemenu\"\n\"29\",\"shoelace\"\n\"29\",\"woocommerce-checkout-fields\"\n\"29\",\"pypubsub\"\n\"29\",\"setitimer\"\n\"29\",\"knockout-3.2\"\n\"29\",\"issuu\"\n\"29\",\"openmediavault\"\n\"29\",\"development-process\"\n\"29\",\"sqlkorma\"\n\"29\",\"hobo\"\n\"29\",\"xcode11.1\"\n\"29\",\"tadotable\"\n\"29\",\"pnp-core-sdk\"\n\"29\",\"quicktype\"\n\"29\",\"nopcommerce-3.80\"\n\"29\",\"notificationservices\"\n\"29\",\"gaelyk\"\n\"29\",\"scatter-matrix\"\n\"29\",\"tactionmanager\"\n\"29\",\"reflog\"\n\"29\",\"c++builder-xe3\"\n\"29\",\"policy-injection\"\n\"29\",\"gcloud-compute\"\n\"29\",\"tac\"\n\"29\",\"nor\"\n\"29\",\"mobile-broadband-api\"\n\"29\",\"gwt-dev-mode\"\n\"29\",\"aspxcombobox\"\n\"29\",\"sqloledb\"\n\"29\",\"taskaffinity\"\n\"29\",\"dotdotdot\"\n\"29\",\"nonclient-area\"\n\"29\",\"azure-java-tools\"\n\"29\",\"c++builder-2009\"\n\"29\",\"nutch2\"\n\"29\",\"cache-dependency\"\n\"29\",\"moai\"\n\"29\",\"npyscreen\"\n\"29\",\"azure-pack\"\n\"29\",\"jackhenry-jxchange\"\n\"29\",\"npruntime\"\n\"29\",\"npm-cli\"\n\"29\",\"sqlcedatareader\"\n\"29\",\"gcc4.4\"\n\"29\",\"redwoodjs\"\n\"29\",\"regsub\"\n\"29\",\"xcode8-beta2\"\n\"29\",\"direct-composition\"\n\"29\",\"hipi\"\n\"29\",\"galsim\"\n\"29\",\"exchange-transport-agents\"\n\"29\",\"context-param\"\n\"29\",\"openxls\"\n\"29\",\"hip\"\n\"29\",\"rails-administrate\"\n\"29\",\"scopt\"\n\"29\",\"sqlapi++\"\n\"29\",\"excon\"\n\"29\",\"gundb\"\n\"29\",\"nsviewrepresentable\"\n\"29\",\"scope-resolution-operator\"\n\"29\",\"sqlalchemy-access\"\n\"29\",\"openmrs\"\n\"29\",\"openvg\"\n\"29\",\"springsource-dm-server\"\n\"29\",\"pointer-address\"\n\"29\",\"openmmlab\"\n\"29\",\"development-mode\"\n\"29\",\"j8583\"\n\"29\",\"activityresultcontracts\"\n\"29\",\"get-method\"\n\"29\",\"color-key\"\n\"29\",\"column-sum\"\n\"29\",\"ldap-client\"\n\"29\",\"commaide\"\n\"29\",\"laravel-helper\"\n\"29\",\"latent-semantic-analysis\"\n\"29\",\"lcid\"\n\"29\",\"genexus-gam\"\n\"29\",\"genstrings\"\n\"29\",\"event-processor-host\"\n\"29\",\"node-notifier\"\n\"29\",\"event-hooking\"\n\"29\",\"octobercms-widgets\"\n\"29\",\"mem-fun\"\n\"29\",\"eli5\"\n\"29\",\"qmetatype\"\n\"29\",\"stratio\"\n\"29\",\"euclidean-algorithm\"\n\"29\",\"loginstatus\"\n\"29\",\"qlcdnumber\"\n\"29\",\"perl-io\"\n\"29\",\"etetoolkit\"\n\"29\",\"ios11.3\"\n\"29\",\"perl5.8\"\n\"29\",\"ios13.2\"\n\"29\",\"lockdown\"\n\"29\",\"qgraphicswidget\"\n\"29\",\"qfileinfo\"\n\"29\",\"message-channel\"\n\"29\",\"stm32l152\"\n\"29\",\"stm32h743\"\n\"29\",\"spring2.x\"\n\"29\",\"httr2\"\n\"29\",\"resend.com\"\n\"29\",\"message-forwarding\"\n\"29\",\"elasticsearch-php\"\n\"29\",\"qcalendarwidget\"\n\"29\",\"elasticsearch.js\"\n\"29\",\"qaxobject\"\n\"29\",\"restier\"\n\"29\",\"nlog-configuration\"\n\"29\",\"requires-clause\"\n\"29\",\"elastic-appsearch\"\n\"29\",\"sp-msforeachtable\"\n\"29\",\"pywhois\"\n\"29\",\"tetgen\"\n\"29\",\"spf13vim\"\n\"29\",\"propositional-calculus\"\n\"29\",\"activerecord-relation\"\n\"29\",\"protobuf-net.grpc\"\n\"29\",\"angular-foundation\"\n\"29\",\"ip2location\"\n\"29\",\"motorengine\"\n\"29\",\"text-compression\"\n\"29\",\"ctabctrl\"\n\"29\",\"hygieia\"\n\"29\",\"actian\"\n\"29\",\"motodev-studio\"\n\"29\",\"cfeclipse\"\n\"29\",\"duplicity-backup\"\n\"29\",\"spinbox\"\n\"29\",\"toolbars\"\n\"29\",\"angular-component-life-cycle\"\n\"29\",\"spiceworks\"\n\"29\",\"isapi-extension\"\n\"29\",\"cts-search\"\n\"29\",\"angular-auxiliary-routes\"\n\"29\",\"ironpython-studio\"\n\"29\",\"tonic\"\n\"29\",\"trackback\"\n\"29\",\"pg-dumpall\"\n\"29\",\"linkedmdb\"\n\"29\",\"tracking-pixel\"\n\"29\",\"compgen\"\n\"29\",\"batch-request\"\n\"29\",\"parboiled\"\n\"29\",\"betareg\"\n\"29\",\"weatherdata\"\n\"29\",\"beatbox\"\n\"29\",\"parsehub\"\n\"29\",\"zero-extension\"\n\"29\",\"parse-ios-sdk\"\n\"29\",\"userstyles\"\n\"29\",\"gnu-global\"\n\"29\",\"sharpshell\"\n\"29\",\"focus-engine\"\n\"29\",\"batarang\"\n\"29\",\"endeca-workbench\"\n\"29\",\"stitch\"\n\"29\",\"partial-methods\"\n\"29\",\"web-manifest\"\n\"29\",\"git-mirror\"\n\"29\",\"transactiontoolargeexception\"\n\"29\",\"idbconnection\"\n\"29\",\"beeswarm\"\n\"29\",\"stimulus-reflex\"\n\"29\",\"iisreset\"\n\"29\",\"flutter-renderflex-error\"\n\"29\",\"uv4l\"\n\"29\",\"starburst\"\n\"29\",\"start-page\"\n\"29\",\"iis-modules\"\n\"29\",\"fogbugz-api\"\n\"29\",\"emotion-js\"\n\"29\",\"zend-file\"\n\"29\",\"usd\"\n\"29\",\"paper-dialog\"\n\"29\",\"beginthreadex\"\n\"29\",\"embarrassingly-parallel\"\n\"29\",\"bcryptjs\"\n\"29\",\"qt-events\"\n\"29\",\"tiles-game\"\n\"29\",\"multi-catch\"\n\"29\",\"cyclone\"\n\"29\",\"qt-connection\"\n\"29\",\"google-openidconnect\"\n\"29\",\"array-reverse\"\n\"29\",\"dart-test\"\n\"29\",\"google-music\"\n\"29\",\"ilm\"\n\"29\",\"cwp\"\n\"29\",\"cygpath\"\n\"29\",\"mraid\"\n\"29\",\"google-toolbox-for-mac\"\n\"29\",\"cutycapt\"\n\"29\",\"space-leak\"\n\"29\",\"thumbor\"\n\"29\",\"maven-lifecycle\"\n\"29\",\"msxml3\"\n\"29\",\"google-healthcare-api\"\n\"29\",\"spacevim\"\n\"29\",\"quantopian\"\n\"29\",\"three-way-merge\"\n\"29\",\"msdropdown\"\n\"29\",\"powershell-studio\"\n\"28\",\"flexlm\"\n\"28\",\"ansible-lint\"\n\"28\",\"location-based\"\n\"28\",\"multi-targeting\"\n\"28\",\"reliable-message-delivery\"\n\"28\",\"github-codeowners\"\n\"28\",\"primesense\"\n\"28\",\"clappr\"\n\"28\",\"telepresence\"\n\"28\",\"prism-2\"\n\"28\",\"primefaces-gmap\"\n\"28\",\"slack-bot\"\n\"28\",\"skydns\"\n\"28\",\"y2k\"\n\"28\",\"reparsepoint\"\n\"28\",\"multi-model-forms\"\n\"28\",\"jep\"\n\"28\",\"federated-storage-engine\"\n\"28\",\"intel-composer\"\n\"28\",\"sqsh\"\n\"28\",\"wijmo5\"\n\"28\",\"debugpy\"\n\"28\",\"ff4j\"\n\"28\",\"eclipse-gemini\"\n\"28\",\"jbehave-plugin\"\n\"28\",\"defaultmutabletreenode\"\n\"28\",\"ghostscriptsharp\"\n\"28\",\"photobucket\"\n\"28\",\"cleave\"\n\"28\",\"stack-machine\"\n\"28\",\"deepspeed\"\n\"28\",\"ballerina-http\"\n\"28\",\"gimple\"\n\"28\",\"matrixcursor\"\n\"28\",\"sse3\"\n\"28\",\"vue-jest\"\n\"28\",\"transient-failure\"\n\"28\",\"figma-plugin\"\n\"28\",\"websockify\"\n\"28\",\"cmac\"\n\"28\",\"apache-aurora\"\n\"28\",\"yocto-kirkstone\"\n\"28\",\"ghcup\"\n\"28\",\"stack-allocation\"\n\"28\",\"weld2\"\n\"28\",\"php-pdftk\"\n\"28\",\"yii2-module\"\n\"28\",\"git-http-backend\"\n\"28\",\"mat-list\"\n\"28\",\"installshield-2013\"\n\"28\",\"react-native-picker\"\n\"28\",\"decoration\"\n\"28\",\"phpcrawl\"\n\"28\",\"web-sys\"\n\"28\",\"jenkins-jira-trigger\"\n\"28\",\"cncontactpicker\"\n\"28\",\"stack-dump\"\n\"28\",\"python-bybit\"\n\"28\",\"python-bob\"\n\"28\",\"pkcs#10\"\n\"28\",\"catransformlayer\"\n\"28\",\"vmware-sdk\"\n\"28\",\"piexif\"\n\"28\",\"jsonpointer\"\n\"28\",\"xeround\"\n\"28\",\"marathontesting\"\n\"28\",\"dbfunctions\"\n\"28\",\"smt-lib\"\n\"28\",\"pandas-rolling\"\n\"28\",\"label-studio\"\n\"28\",\"lambda-metafactory\"\n\"28\",\"implicit-parameters\"\n\"28\",\"symfony-voter\"\n\"28\",\"softaculous\"\n\"28\",\"nginx-unit\"\n\"28\",\"safe-navigation-operator\"\n\"28\",\"imodelbinder\"\n\"28\",\"language-history\"\n\"28\",\"iml\"\n\"28\",\"cdktf\"\n\"28\",\"pandas-settingwithcopy-warning\"\n\"28\",\"findancestor\"\n\"28\",\"pine-editor\"\n\"28\",\"pagerjs\"\n\"28\",\"pagerequestmanager\"\n\"28\",\"firebird-4.0\"\n\"28\",\"bits-service\"\n\"28\",\"selecteditemchanged\"\n\"28\",\"selenium-docker\"\n\"28\",\"nextjs-api-router\"\n\"28\",\"biomod2\"\n\"28\",\"apollo-boost\"\n\"28\",\"bindata\"\n\"28\",\"adobe-embed-api\"\n\"28\",\"cartocss\"\n\"28\",\"disk.frame\"\n\"28\",\"vscode-git\"\n\"28\",\"pythonqt\"\n\"28\",\"fuzz-testing\"\n\"28\",\"rust-result\"\n\"28\",\"pkgcloud\"\n\"28\",\"picturegallery\"\n\"28\",\"sxssf\"\n\"28\",\"vram\"\n\"28\",\"python-markdown\"\n\"28\",\"function-points\"\n\"28\",\"case-conversion\"\n\"28\",\"jsr354\"\n\"28\",\"frm\"\n\"28\",\"catalyst-optimizer\"\n\"28\",\"rust-decl-macros\"\n\"28\",\"jsr82\"\n\"28\",\"plaidml\"\n\"28\",\"markerspiderfier\"\n\"28\",\"chgrp\"\n\"28\",\"vpath\"\n\"28\",\"docklayoutpanel\"\n\"28\",\"universal-link\"\n\"28\",\"window.closed\"\n\"28\",\"angularjs-ng-switch\"\n\"28\",\"angularjs-ng-template\"\n\"28\",\"grails-3.0.9\"\n\"28\",\"doctrine-dbal\"\n\"28\",\"wallpapermanager\"\n\"28\",\"docker-watchtower\"\n\"28\",\"named-instance\"\n\"28\",\"crfsuite\"\n\"28\",\"credit-card-track-data\"\n\"28\",\"rspec-sidekiq\"\n\"28\",\"angular-seo\"\n\"28\",\"nape\"\n\"28\",\"django-upgrade\"\n\"28\",\"ora-01400\"\n\"28\",\"create-ref\"\n\"28\",\"ora-12514\"\n\"28\",\"createparams\"\n\"28\",\"iap-hosted-content\"\n\"28\",\"jpa-annotations\"\n\"28\",\"ora-00937\"\n\"28\",\"callermembername\"\n\"28\",\"canvaskit\"\n\"28\",\"avcapturephotooutput\"\n\"28\",\"pybossa\"\n\"28\",\"iceweasel\"\n\"28\",\"jigoshop\"\n\"28\",\"recordstore\"\n\"28\",\"carbon-copy\"\n\"28\",\"crow\"\n\"28\",\"crostini\"\n\"28\",\"documentum-dfc\"\n\"28\",\"wildfly-16\"\n\"28\",\"vapi\"\n\"28\",\"datagridrowheader\"\n\"28\",\"rebex\"\n\"28\",\"canjs-view\"\n\"28\",\"myrrix\"\n\"28\",\"pscl\"\n\"28\",\"rtvs\"\n\"28\",\"facebook-recommendations\"\n\"28\",\"unroll\"\n\"28\",\"google-vault-api\"\n\"28\",\"pspice\"\n\"28\",\"room\"\n\"28\",\"jmail\"\n\"28\",\"sesam\"\n\"28\",\"unusernotification\"\n\"28\",\"serial-processing\"\n\"28\",\"document-layout-analysis\"\n\"28\",\"polymorphic-functions\"\n\"28\",\"opencore\"\n\"28\",\"tweener\"\n\"28\",\"sign-in-with-google\"\n\"28\",\"easyrdf\"\n\"28\",\"kafka-node\"\n\"28\",\"bottlenose\"\n\"28\",\"bootstrap-lightbox\"\n\"28\",\"information-visualization\"\n\"28\",\"pbiviz\"\n\"28\",\"boost-beast-websocket\"\n\"28\",\"openafs\"\n\"28\",\"tx-mask\"\n\"28\",\"countable\"\n\"28\",\"ionic-cloud\"\n\"28\",\"molehill\"\n\"28\",\"mongomock\"\n\"28\",\"nrefactory\"\n\"28\",\"boolean-indexing\"\n\"28\",\"grunt-contrib-qunit\"\n\"28\",\"sutime\"\n\"28\",\"writer-monad\"\n\"28\",\"wpengine\"\n\"28\",\"mongodate\"\n\"28\",\"in-memory-data-grid\"\n\"28\",\"svn-client\"\n\"28\",\"spring-ai\"\n\"28\",\"needle.js\"\n\"28\",\"twitter-r\"\n\"28\",\"nsassert\"\n\"28\",\"tying-the-knot\"\n\"28\",\"wtx\"\n\"28\",\"wvd\"\n\"28\",\"twgl.js\"\n\"28\",\"angelscript\"\n\"28\",\"worklight-mtww\"\n\"28\",\"jquery-1.6\"\n\"28\",\"sweeper\"\n\"28\",\"application-variables\"\n\"28\",\"e-notices\"\n\"28\",\"navcontroller\"\n\"28\",\"nearlyfreespeech\"\n\"28\",\"html.textbox\"\n\"28\",\"app-store-connect-api\"\n\"28\",\"boxsdk\"\n\"28\",\"twitter-widget\"\n\"28\",\"libpurple\"\n\"28\",\"visual-c++-2008-express\"\n\"28\",\"konacha\"\n\"28\",\"android-api-30\"\n\"28\",\"shrinkresources\"\n\"28\",\"type-equivalence\"\n\"28\",\"oracle-type\"\n\"28\",\"liblas\"\n\"28\",\"macrobenchmark\"\n\"28\",\"dojox.gfx\"\n\"28\",\"libsigc++\"\n\"28\",\"visualhg\"\n\"28\",\"ezapi\"\n\"28\",\"oauth-ruby\"\n\"28\",\"outputformat\"\n\"28\",\"goertzel-algorithm\"\n\"28\",\"minicart\"\n\"28\",\"system.io.pipelines\"\n\"28\",\"macbookpro\"\n\"28\",\"outgoing-mail\"\n\"28\",\"riverplot\"\n\"28\",\"jarbundler\"\n\"28\",\"rfduino\"\n\"28\",\"amp-mustache\"\n\"28\",\"coderush-xpress\"\n\"28\",\"java-gstreamer\"\n\"28\",\"leftalign\"\n\"28\",\"shady-dom\"\n\"28\",\".net-1.0\"\n\"28\",\"signalr-client\"\n\"28\",\"jaudiotagger\"\n\"28\",\"amp-form\"\n\"28\",\"attributedstring\"\n\"28\",\"viewflow\"\n\"28\",\"orsserialport\"\n\"28\",\"frameworkelementfactory\"\n\"28\",\"system-setting\"\n\"28\",\"typescript-conditional-types\"\n\"28\",\"raknet\"\n\"28\",\"rcppeigen\"\n\"28\",\"extraction-operator\"\n\"28\",\"klout\"\n\"28\",\"pyramid-arima\"\n\"28\",\"codevisionavr\"\n\"28\",\"jarjar\"\n\"28\",\"express-vhost\"\n\"28\",\"revit-2015\"\n\"28\",\"amiga\"\n\"28\",\"jamvm\"\n\"28\",\"b2g\"\n\"28\",\"iweb\"\n\"28\",\"schema.yml\"\n\"28\",\"drupal-fivestar\"\n\"28\",\"token-exchange\"\n\"28\",\"nsurlcomponents\"\n\"28\",\"tkinter-scrolledtext\"\n\"28\",\"scan-build\"\n\"28\",\"exchange-basicauth\"\n\"28\",\"dscl\"\n\"28\",\"nptl\"\n\"28\",\"ixmldomdocument\"\n\"28\",\"uipath-api\"\n\"28\",\"expired-cookies\"\n\"28\",\"explicit-implementation\"\n\"28\",\"experience-manager\"\n\"28\",\"bzip\"\n\"28\",\"isnan\"\n\"28\",\"nui\"\n\"28\",\"scoped\"\n\"28\",\"expo-build\"\n\"28\",\"cairo-lang\"\n\"28\",\"np.argsort\"\n\"28\",\"referenceproperty\"\n\"28\",\"scoped-lock\"\n\"28\",\"azure-rtos\"\n\"28\",\"itween\"\n\"28\",\"sceditor\"\n\"28\",\"plc4x\"\n\"28\",\"gwt-material-design\"\n\"28\",\"coq-extraction\"\n\"28\",\"taffy\"\n\"28\",\"gendarme\"\n\"28\",\"polymer-1.x\"\n\"28\",\"opera-dragonfly\"\n\"28\",\"gwt-hosted-mode\"\n\"28\",\"controlcollection\"\n\"28\",\"gwt-2.7\"\n\"28\",\"openvx\"\n\"28\",\"tab-size\"\n\"28\",\"point-sprites\"\n\"28\",\"xcglogger\"\n\"28\",\"dotnetinstaller\"\n\"28\",\"podsecuritypolicy\"\n\"28\",\"opengis\"\n\"28\",\"asp.net-core-cli\"\n\"28\",\"mls\"\n\"28\",\"r2rml\"\n\"28\",\"android-credential-manager\"\n\"28\",\"hackintosh\"\n\"28\",\"radupload\"\n\"28\",\"qwizardpage\"\n\"28\",\"raid\"\n\"28\",\"openjpa-maven-plugin\"\n\"28\",\"hamiltonian-path\"\n\"28\",\"drawingcache\"\n\"28\",\"rails-flash\"\n\"28\",\"pm2-logrotate\"\n\"28\",\"diff3\"\n\"28\",\"cfreadstream\"\n\"28\",\"resharper-sdk\"\n\"28\",\"evaluatejavascript\"\n\"28\",\"qpointer\"\n\"28\",\"https-proxy-agent\"\n\"28\",\"pffacebookutils\"\n\"28\",\"morph-x\"\n\"28\",\"omake\"\n\"28\",\"http-status-code-412\"\n\"28\",\"mosel\"\n\"28\",\"laravel-http-client\"\n\"28\",\"spid\"\n\"28\",\"laravel-livewire-wiremodel\"\n\"28\",\"chai-enzyme\"\n\"28\",\"mousemotionevent\"\n\"28\",\"protocol-relative\"\n\"28\",\"metalanguage\"\n\"28\",\"hubspot-cms\"\n\"28\",\"android-notification.mediastyle\"\n\"28\",\"project-setup\"\n\"28\",\"angularjs-fileupload\"\n\"28\",\"angular-cli-ghpages\"\n\"28\",\"action-menu\"\n\"28\",\"dukescript\"\n\"28\",\"duplicate-content\"\n\"28\",\"textspan\"\n\"28\",\"text-services-framework\"\n\"28\",\"ipcs\"\n\"28\",\"nimbus-ios\"\n\"28\",\"tomcat7-maven-plugin\"\n\"28\",\"custom-compare\"\n\"28\",\"eintr\"\n\"28\",\"columnmappings\"\n\"28\",\"io-socket\"\n\"28\",\"test-and-set\"\n\"28\",\"google-cloud-deploy\"\n\"28\",\"terraform-remote-state\"\n\"28\",\"leak-sanitizer\"\n\"28\",\"performance-measuring\"\n\"28\",\"zuora\"\n\"28\",\"google-cloud-tools\"\n\"28\",\"google-fitness-api\"\n\"28\",\"logdna\"\n\"28\",\"react-bootstrap-nav\"\n\"28\",\"ellipsize\"\n\"28\",\"node-horseman\"\n\"28\",\"persistent-memory\"\n\"28\",\"lookml\"\n\"28\",\"lowcode\"\n\"28\",\"query-timeout\"\n\"28\",\"arrangeoverride\"\n\"28\",\"gmcs\"\n\"28\",\"struts2-spring-plugin\"\n\"28\",\"d3fc\"\n\"28\",\"tidyverts\"\n\"28\",\"google-local-search\"\n\"28\",\"sublimecodeintel\"\n\"28\",\"searchable-plugin\"\n\"28\",\"avatars\"\n\"28\",\"bignumber.js\"\n\"28\",\"emboss\"\n\"28\",\"qtwebchannel\"\n\"28\",\"endorsed\"\n\"28\",\"google-surveys\"\n\"28\",\"linq-to-xsd\"\n\"28\",\"eme\"\n\"28\",\"vaadin12\"\n\"28\",\"throbber\"\n\"28\",\"limit-choices-to\"\n\"28\",\"qtremoteobjects\"\n\"28\",\"maven-extension\"\n\"28\",\"seckeyref\"\n\"28\",\"ihaskell\"\n\"28\",\"billingclient\"\n\"28\",\"flutter-responsive-layout\"\n\"28\",\"sonicmq\"\n\"28\",\"benchmark.js\"\n\"28\",\"amazon-mobile-analytics\"\n\"28\",\"zipapp\"\n\"28\",\"webcola\"\n\"28\",\"totalview\"\n\"28\",\"soti\"\n\"28\",\"web-clips\"\n\"28\",\"threebox\"\n\"28\",\"webhttp\"\n\"28\",\"liferay-7.4\"\n\"28\",\"scripting.dictionary\"\n\"28\",\"solandra\"\n\"28\",\"tracemalloc\"\n\"28\",\"url-helper\"\n\"28\",\"zeplin\"\n\"28\",\"heapalloc\"\n\"28\",\"auto-sklearn\"\n\"28\",\"bgfx\"\n\"28\",\"tilestache\"\n\"28\",\"trailing-whitespace\"\n\"28\",\"alt.js\"\n\"28\",\"suitecloud\"\n\"28\",\"imagelibrary\"\n\"28\",\"header-bidding\"\n\"28\",\"webkit-perspective\"\n\"28\",\"quarkus-hibernate-reactive\"\n\"28\",\"tfs-security\"\n\"28\",\"mdtool\"\n\"28\",\"parchment\"\n\"28\",\"sharpbox\"\n\"28\",\"git-send-email\"\n\"28\",\"userappdatapath\"\n\"28\",\"illuminate\"\n\"28\",\"flutter-hotreload\"\n\"28\",\"conditional-variable\"\n\"27\",\"skus\"\n\"27\",\"yardstick\"\n\"27\",\"insight\"\n\"27\",\"jbossmq\"\n\"27\",\"debuggerdisplay\"\n\"27\",\"sitemap-generator-gem\"\n\"27\",\"reactotron\"\n\"27\",\"git-cvs\"\n\"27\",\"dda\"\n\"27\",\"smallcaps\"\n\"27\",\"editline\"\n\"27\",\"gist-index\"\n\"27\",\"vue-slot\"\n\"27\",\"fetchmail\"\n\"27\",\"cliptobounds\"\n\"27\",\"graphql-spqr-spring-boot-starter\"\n\"27\",\"jfif\"\n\"27\",\"yasson\"\n\"27\",\"jbig2\"\n\"27\",\"yandex-tank\"\n\"27\",\"masonite\"\n\"27\",\"slab\"\n\"27\",\"clojure-core.typed\"\n\"27\",\"ggsurvfit\"\n\"27\",\"clist\"\n\"27\",\"php-closures\"\n\"27\",\"loadbalancer\"\n\"27\",\"reparenting\"\n\"27\",\"cloveretl\"\n\"27\",\"groovydsl\"\n\"27\",\"webservicehost\"\n\"27\",\"whirlpool\"\n\"27\",\"edamam-api\"\n\"27\",\"pgraphics\"\n\"27\",\"terminal-color\"\n\"27\",\"dbt-cloud\"\n\"27\",\"list-unsubscribe\"\n\"27\",\"eclipse-fp\"\n\"27\",\"litecoin\"\n\"27\",\"intents-extension\"\n\"27\",\"bankers-algorithm\"\n\"27\",\"remoteexception\"\n\"27\",\"remote-file-inclusion\"\n\"27\",\"greendao3\"\n\"27\",\"git-history-rewrite\"\n\"27\",\"php-resque\"\n\"27\",\"tell-dont-ask\"\n\"27\",\"ebtables\"\n\"27\",\"list-definition\"\n\"27\",\"grouped-list\"\n\"27\",\"phplint\"\n\"27\",\"greenlock\"\n\"27\",\"truevault\"\n\"27\",\"xsl-choose\"\n\"27\",\"mat-slider\"\n\"27\",\"webwork\"\n\"27\",\"xsl-stylesheet\"\n\"27\",\"repeatbutton\"\n\"27\",\"yagni\"\n\"27\",\"printer-properties\"\n\"27\",\"bar3d\"\n\"27\",\"jseparator\"\n\"27\",\"laravel-dusk2\"\n\"27\",\"appengine-maven-plugin\"\n\"27\",\"constant-time\"\n\"27\",\"ui-toolkit\"\n\"27\",\"afjsonrequestoperation\"\n\"27\",\"vs-code-settings\"\n\"27\",\"content-experiments\"\n\"27\",\"connected-services\"\n\"27\",\"packages.config\"\n\"27\",\"umbraco4\"\n\"27\",\"finite-group-theory\"\n\"27\",\"birt-deapi\"\n\"27\",\"plan-9\"\n\"27\",\"sygic\"\n\"27\",\"directory-walk\"\n\"27\",\"pageloadstrategy\"\n\"27\",\"unbounce\"\n\"27\",\"nextjs-storybook\"\n\"27\",\"carekit\"\n\"27\",\"aplpy\"\n\"27\",\"jsunit\"\n\"27\",\"flash-list\"\n\"27\",\"adobe-media-server\"\n\"27\",\"directory-server\"\n\"27\",\"content-query-web-part\"\n\"27\",\"swing-highlighter\"\n\"27\",\"runscope\"\n\"27\",\"pythoncard\"\n\"27\",\"python-bigquery\"\n\"27\",\"json-table\"\n\"27\",\"s3-lifecycle-policy\"\n\"27\",\"smart-device-framework\"\n\"27\",\"cbitmap\"\n\"27\",\"django-constraints\"\n\"27\",\"datalife-engine\"\n\"27\",\"adsutil.vbs\"\n\"27\",\"datatip\"\n\"27\",\"datalistitem\"\n\"27\",\"impressionist\"\n\"27\",\"maintainscrollpositionon\"\n\"27\",\"social-graph\"\n\"27\",\"app42\"\n\"27\",\"pixel-shading\"\n\"27\",\"langgraph\"\n\"27\",\"xml-1.1\"\n\"27\",\"cctray\"\n\"27\",\"social-auth-app-django\"\n\"27\",\"contentdialog\"\n\"27\",\"sage-line-50\"\n\"27\",\"mapstraction\"\n\"27\",\"sage-crm\"\n\"27\",\"ngx-spinner\"\n\"27\",\"data-interchange\"\n\"27\",\"kendo-sortable\"\n\"27\",\"serverless-webpack-plugin\"\n\"27\",\"rt.jar\"\n\"27\",\"document-set\"\n\"27\",\"server-application\"\n\"27\",\"cardio\"\n\"27\",\"pushstreamcontent\"\n\"27\",\"alamofire5\"\n\"27\",\"calligraphy\"\n\"27\",\"mysql-error-1068\"\n\"27\",\"update-by-query\"\n\"27\",\"animatorset\"\n\"27\",\"djnativeswing\"\n\"27\",\"update-attribute\"\n\"27\",\"go-toolchain\"\n\"27\",\"jinja2-cli\"\n\"27\",\"aws-appsync-ios\"\n\"27\",\"windows-phone-voip\"\n\"27\",\"vectorstore\"\n\"27\",\"windows-phone-silverlight\"\n\"27\",\"pubdate\"\n\"27\",\"angular-language-service\"\n\"27\",\"fat-binaries\"\n\"27\",\"rtsp-server\"\n\"27\",\"simpletype\"\n\"27\",\"dojo-dnd\"\n\"27\",\"grails-filters\"\n\"27\",\"rowmapper\"\n\"27\",\"ibm-data-replication\"\n\"27\",\"facial-landmark-alignment\"\n\"27\",\"django-excel\"\n\"27\",\"unminify\"\n\"27\",\"cross-process\"\n\"27\",\"water-jug-problem\"\n\"27\",\"ibm-graph\"\n\"27\",\"data.tree\"\n\"27\",\"rngcryptoserviceprovider\"\n\"27\",\"watir-classic\"\n\"27\",\"universal-hashing\"\n\"27\",\"credssp\"\n\"27\",\"cancelanimationframe\"\n\"27\",\"win32con\"\n\"27\",\"wildfly-15\"\n\"27\",\"ibm-streams\"\n\"27\",\"session-set-save-handler\"\n\"27\",\"django-graphql-jwt\"\n\"27\",\"named-captures\"\n\"27\",\"kind-projector\"\n\"27\",\"database-caching\"\n\"27\",\"http4k\"\n\"27\",\"word-size\"\n\"27\",\"growlnotify\"\n\"27\",\"app-transfer\"\n\"27\",\"postgres-crosstab\"\n\"27\",\"android-wheel\"\n\"27\",\"azure-cosmosdb-cassandra-api\"\n\"27\",\"twitter-hbc\"\n\"27\",\"mongoose-middleware\"\n\"27\",\"iodbc\"\n\"27\",\"delphi-4\"\n\"27\",\"swiftbond\"\n\"27\",\"delphi-2005\"\n\"27\",\"wsat\"\n\"27\",\"tungsten-replicator\"\n\"27\",\"htmldoc\"\n\"27\",\"apriltags\"\n\"27\",\"swift-class\"\n\"27\",\"ion-menu\"\n\"27\",\"postasync\"\n\"27\",\"postgresql-bdr\"\n\"27\",\"svnlook\"\n\"27\",\"nsrect\"\n\"27\",\"appsmith\"\n\"27\",\"bluetooth-sco\"\n\"27\",\"on-location-changed\"\n\"27\",\"nspersistentcontainer\"\n\"27\",\"deftype\"\n\"27\",\"wunderlist\"\n\"27\",\"ionic-plugins\"\n\"27\",\"swift-custom-framework\"\n\"27\",\"intero\"\n\"27\",\"poset\"\n\"27\",\"turbopower\"\n\"27\",\"wtsapi32\"\n\"27\",\"boost-intrusive\"\n\"27\",\"spring-cloud-config-client\"\n\"27\",\"tx-indexed-search\"\n\"27\",\"supplementary\"\n\"27\",\"jupyter-extensions\"\n\"27\",\"spring-js\"\n\"27\",\"navigationwindow\"\n\"27\",\"inext\"\n\"27\",\"designview\"\n\"27\",\"mojo-useragent\"\n\"27\",\"wsgiserver\"\n\"27\",\"httpapi\"\n\"27\",\"modulefile\"\n\"27\",\"earley-parser\"\n\"27\",\"natty\"\n\"27\",\"pattern-guards\"\n\"27\",\"dependent-destroy\"\n\"27\",\"deluge\"\n\"27\",\"samsung-gear\"\n\"27\",\"spring-boot-starter-oauth2-client\"\n\"27\",\"application.xml\"\n\"27\",\"modx-chunks\"\n\"27\",\"boonex-dolphin\"\n\"27\",\"keda-scaledjob\"\n\"27\",\"deploymentitem\"\n\"27\",\"blog-engine\"\n\"27\",\"pound\"\n\"27\",\"kaleido\"\n\"27\",\"jquery-upload-file-plugin\"\n\"27\",\"applicationwindow\"\n\"27\",\"bounds-checker\"\n\"27\",\"saturation-arithmetic\"\n\"27\",\"earthdistance\"\n\"27\",\"nscountedset\"\n\"27\",\"jquery-rails\"\n\"27\",\"jwebunit\"\n\"27\",\"negative-zero\"\n\"27\",\"infoblox\"\n\"27\",\"gtkwave\"\n\"27\",\"ringojs\"\n\"27\",\"sysbench\"\n\"27\",\"virtual-tour\"\n\"27\",\"winit\"\n\"27\",\"javafx-1\"\n\"27\",\"video-reactjs\"\n\"27\",\"random-data\"\n\"27\",\"setalarmclock\"\n\"27\",\"lvgl\"\n\"27\",\"atlassian-connect\"\n\"27\",\"network-load-balancer\"\n\"27\",\"visual-studio-code-server\"\n\"27\",\"asymptote\"\n\"27\",\"codepro\"\n\"27\",\"god-object\"\n\"27\",\"rars-simulator\"\n\"27\",\".net-sdk\"\n\"27\",\"extension-modules\"\n\"27\",\"oauth2resttemplate\"\n\"27\",\"netflix-archaius\"\n\"27\",\"revoscaler\"\n\"27\",\"wiremock-record\"\n\"27\",\"luvit\"\n\"27\",\"javolution\"\n\"27\",\"async-components\"\n\"27\",\"pyq\"\n\"27\",\"libnodave\"\n\"27\",\"rcrawler\"\n\"27\",\"java-18\"\n\"27\",\"minko\"\n\"27\",\"build-environment\"\n\"27\",\"dolphin-browser\"\n\"27\",\"dolby\"\n\"27\",\"browser-close\"\n\"27\",\"system-restore\"\n\"27\",\"aasa\"\n\"27\",\"libmodbus\"\n\"27\",\"audiojs\"\n\"27\",\"fourier-descriptors\"\n\"27\",\"atlcom\"\n\"27\",\"pylearn\"\n\"27\",\"java-native-library\"\n\"27\",\"extconf.rb\"\n\"27\",\"accept.js\"\n\"27\",\"code-statistics\"\n\"27\",\"outlook-form\"\n\"27\",\"build-pipeline-plugin\"\n\"27\",\"sfdatagrid\"\n\"27\",\"build-process-template\"\n\"27\",\"google-admin-audit-api\"\n\"27\",\"magento-rules\"\n\"27\",\"wkinterfaceimage\"\n\"27\",\"javascript-inheritance\"\n\"27\",\"asp.net-core-logging\"\n\"27\",\"jackson-dataformat-avro\"\n\"27\",\"toeplitz\"\n\"27\",\"redux-router\"\n\"27\",\"radlistbox\"\n\"27\",\"gdm\"\n\"27\",\"tablesort\"\n\"27\",\"redux-offline\"\n\"27\",\"gears\"\n\"27\",\"nth-element\"\n\"27\",\"regexbuddy\"\n\"27\",\"exponentjs\"\n\"27\",\"gcc12\"\n\"27\",\"cache-locality\"\n\"27\",\"sqldmo\"\n\"27\",\"uikeyinput\"\n\"27\",\"dpkg-buildpackage\"\n\"27\",\"assertthat\"\n\"27\",\"tls-psk\"\n\"27\",\"copy-data\"\n\"27\",\"hapi.js-lab\"\n\"27\",\"azure-management-groups\"\n\"27\",\"drupal-permissions\"\n\"27\",\"iusertype\"\n\"27\",\"geiser\"\n\"27\",\"exploratory\"\n\"27\",\"npm-pack\"\n\"27\",\"xcode6-beta5\"\n\"27\",\"xcode6.3.1\"\n\"27\",\"time-select\"\n\"27\",\"c++-experimental\"\n\"27\",\"gamesparks\"\n\"27\",\"nonsequential\"\n\"27\",\"uioutput\"\n\"27\",\"vertex-cover\"\n\"27\",\"android-fragment-manager\"\n\"27\",\"drools-kie-workbench\"\n\"27\",\"opengl-extensions\"\n\"27\",\"mixed-programming\"\n\"27\",\"itemtemplateselector\"\n\"27\",\"mobile-robot-toolkit\"\n\"27\",\"scaleanimation\"\n\"27\",\"tailscale\"\n\"27\",\"cordic\"\n\"27\",\"nunit-2.5.9\"\n\"27\",\"bypass\"\n\"27\",\"dictionary-attack\"\n\"27\",\"dse\"\n\"27\",\"titanium-web-proxy\"\n\"27\",\"scene7\"\n\"27\",\"sql-navigator\"\n\"27\",\"nomachine\"\n\"27\",\"redis-stack-server\"\n\"27\",\"vestal-versions\"\n\"27\",\"askbot\"\n\"27\",\"highline\"\n\"27\",\"modal-popup\"\n\"27\",\"mockup-tool\"\n\"27\",\"scep\"\n\"27\",\"ompr\"\n\"27\",\"angularjs-limitto\"\n\"27\",\"more-itertools\"\n\"27\",\"lsyncd\"\n\"27\",\"eventsetter\"\n\"27\",\"cgimagesource\"\n\"27\",\"iphone-keypad\"\n\"27\",\"angular17-ssr\"\n\"27\",\"olsmultiplelinearregression\"\n\"27\",\"event-flow\"\n\"27\",\"react.rb\"\n\"27\",\"coledatetime\"\n\"27\",\"cupertinotabbar\"\n\"27\",\"qrect\"\n\"27\",\"ip-blocking\"\n\"27\",\"iowait\"\n\"27\",\"react-daterange-picker\"\n\"27\",\"react-flexbox-grid\"\n\"27\",\"nist\"\n\"27\",\"changetype\"\n\"27\",\"property-graph\"\n\"27\",\"pythran\"\n\"27\",\"q-municate\"\n\"27\",\"customproperty\"\n\"27\",\"testdroid\"\n\"27\",\"pywinusb\"\n\"27\",\"office-web-components\"\n\"27\",\"memoise\"\n\"27\",\"project-explorer\"\n\"27\",\"qa-c\"\n\"27\",\"odatacontroller\"\n\"27\",\"test.check\"\n\"27\",\"react-fragment\"\n\"27\",\"long-lines\"\n\"27\",\"logtalk\"\n\"27\",\"google-cloud-pubsublite\"\n\"27\",\"nntool\"\n\"27\",\"google-cloud-talent-solution\"\n\"27\",\"large-file-support\"\n\"27\",\"off-heap\"\n\"27\",\"nocount\"\n\"27\",\"androidpublisher\"\n\"27\",\"react-key-index\"\n\"27\",\"color-channel\"\n\"27\",\"meraki-api\"\n\"27\",\"mercadopagosdk\"\n\"27\",\"android-koin\"\n\"27\",\"performbatchupdates\"\n\"27\",\"pelops\"\n\"27\",\"petgraph\"\n\"27\",\"activity-result-api\"\n\"27\",\"humanizer\"\n\"27\",\"stream-operators\"\n\"27\",\"requests-mock\"\n\"27\",\"elasticlayout\"\n\"27\",\"resolv\"\n\"27\",\"percy\"\n\"27\",\"requires-expression\"\n\"27\",\"responsecache\"\n\"27\",\"resource-timing-api\"\n\"27\",\"spray-test\"\n\"27\",\"electron-react\"\n\"27\",\"stellargraph\"\n\"27\",\"embedded-flashplayer\"\n\"27\",\"bifunctor\"\n\"27\",\"powershell-6.0\"\n\"27\",\"powershell-7\"\n\"27\",\"user-warning\"\n\"27\",\"component-services\"\n\"27\",\"billiards\"\n\"27\",\"parsedown\"\n\"27\",\"stealjs\"\n\"27\",\"queue.js\"\n\"27\",\"quake\"\n\"27\",\"paserver\"\n\"27\",\"zipf\"\n\"27\",\"subobject\"\n\"27\",\"cygwin-64\"\n\"27\",\"parsimonious\"\n\"27\",\"arcamera\"\n\"27\",\"prado\"\n\"27\",\"avassetdownloadtask\"\n\"27\",\"qubit\"\n\"27\",\"seaweedfs\"\n\"27\",\"qsqlrelationaltablemodel\"\n\"27\",\"compilationunit\"\n\"27\",\"sublimemerge\"\n\"27\",\"sectionindexer\"\n\"27\",\"compc\"\n\"27\",\"auto-compile\"\n\"27\",\"sonarqube5.1.2\"\n\"27\",\"ember-bootstrap\"\n\"27\",\"maxby\"\n\"27\",\"urlrewriting.net\"\n\"27\",\"google-logging\"\n\"27\",\"queryselectall\"\n\"27\",\"struts2-tiles-plugin\"\n\"27\",\"tikv\"\n\"27\",\"stl-decomposition\"\n\"27\",\"emitmapper\"\n\"27\",\"alphabetic\"\n\"27\",\"custom-selectors\"\n\"27\",\"linq-to-sharepoint\"\n\"27\",\"sdl-net\"\n\"27\",\"thephpleague-fractal\"\n\"27\",\"tfhpple\"\n\"27\",\"mdp\"\n\"27\",\"beginread\"\n\"27\",\"gmail-promo-tab\"\n\"27\",\"lingpipe\"\n\"27\",\"sourcekit\"\n\"27\",\"bazel-rules-nodejs\"\n\"27\",\"multi-friend-selector\"\n\"27\",\"webhid\"\n\"27\",\"structured-programming\"\n\"27\",\"preforking\"\n\"27\",\"space-filling-curve\"\n\"27\",\"top-level-await\"\n\"27\",\"maven-clean-plugin\"\n\"27\",\"scully\"\n\"27\",\"gnome-keyring-daemon\"\n\"27\",\"spark-redis\"\n\"27\",\"encrypting-file-system\"\n\"27\",\"embind\"\n\"27\",\"qtranslator\"\n\"27\",\"qtime\"\n\"27\",\"scrypto\"\n\"27\",\"autoformat\"\n\"27\",\"identity-map\"\n\"27\",\"endpointbehavior\"\n\"27\",\"toolkitscriptmanager\"\n\"27\",\"custom-ui\"\n\"26\",\"debugbreak\"\n\"26\",\"flowise\"\n\"26\",\"gremlinjs\"\n\"26\",\"remote-management\"\n\"26\",\"interactivepopgesture\"\n\"26\",\"deeppavlov\"\n\"26\",\"dcu\"\n\"26\",\"ansible-handlers\"\n\"26\",\"wideimage\"\n\"26\",\"cloud-automation-manager\"\n\"26\",\"insight.database\"\n\"26\",\"litmus\"\n\"26\",\"react-native-google-mobile-ads\"\n\"26\",\"phpfog\"\n\"26\",\"massivejs\"\n\"26\",\"phpdbg\"\n\"26\",\"mathematical-typesetting\"\n\"26\",\"defaultview\"\n\"26\",\"dcraw\"\n\"26\",\"fluent-docker\"\n\"26\",\"travelport-api\"\n\"26\",\"matchedgeometryeffect\"\n\"26\",\"xwalkview\"\n\"26\",\"class-factory\"\n\"26\",\"ansible-api\"\n\"26\",\"php-mysqlidb\"\n\"26\",\"jbullet\"\n\"26\",\"jd-gui\"\n\"26\",\"princomp\"\n\"26\",\"skopt\"\n\"26\",\"edge-side-includes\"\n\"26\",\"local-security-authority\"\n\"26\",\"ssdt-2019\"\n\"26\",\"gghighlight\"\n\"26\",\"maturin\"\n\"26\",\"prime-ui\"\n\"26\",\"webproject\"\n\"26\",\"fhs\"\n\"26\",\"graphql-federation\"\n\"26\",\"ssconvert\"\n\"26\",\"fgarch\"\n\"26\",\"photosui\"\n\"26\",\"ffmpeg-wasm\"\n\"26\",\"base-sdk\"\n\"26\",\"ecos\"\n\"26\",\"photo-tagging\"\n\"26\",\"flutter-datetime-picker\"\n\"26\",\"react-select-search\"\n\"26\",\"yii2-urlmanager\"\n\"26\",\"xspec\"\n\"26\",\"anti-piracy\"\n\"26\",\"tstream\"\n\"26\",\"rendertron\"\n\"26\",\"intellij-datagrip\"\n\"26\",\"ggmosaic\"\n\"26\",\"cm-synergy\"\n\"26\",\"dbtype\"\n\"26\",\"feathers-service\"\n\"26\",\"grooveshark\"\n\"26\",\"backdrop\"\n\"26\",\"xscale\"\n\"26\",\"gff\"\n\"26\",\"flutter-channel\"\n\"26\",\"vstest.console\"\n\"26\",\"groovy-sql\"\n\"26\",\"flutter-change-notifier-provider\"\n\"26\",\"staledataexception\"\n\"26\",\"barryvdh\"\n\"26\",\"instagram-reels\"\n\"26\",\"vue2-dropzone\"\n\"26\",\"sitetemplate\"\n\"26\",\"python-dragonfly\"\n\"26\",\"laravel-backup\"\n\"26\",\"fillfactor\"\n\"26\",\"freezegun\"\n\"26\",\"datatable-buttons\"\n\"26\",\"swiftui-windowgroup\"\n\"26\",\"container-data-type\"\n\"26\",\"checkov\"\n\"26\",\"fuslogvw\"\n\"26\",\"datetime-generation\"\n\"26\",\"kubernetes-metrics\"\n\"26\",\"xforms-betterform\"\n\"26\",\"chirpy\"\n\"26\",\"dispatchsemaphore\"\n\"26\",\"swiftui-sharelink\"\n\"26\",\"jsonreststore\"\n\"26\",\"laravel-exceptions\"\n\"26\",\"jsdelivr\"\n\"26\",\"in-call\"\n\"26\",\"bindinghandlers\"\n\"26\",\"kxml2\"\n\"26\",\"xname\"\n\"26\",\"displayattribute\"\n\"26\",\"owasp-dependency-check\"\n\"26\",\"blackberry-dynamics\"\n\"26\",\"symfonyux\"\n\"26\",\"bizdays\"\n\"26\",\"content-expiration\"\n\"26\",\"flapdoodle-embed-mongo\"\n\"26\",\"fivetran\"\n\"26\",\"childcontrol\"\n\"26\",\"pythonnet\"\n\"26\",\"pythonplotter\"\n\"26\",\"pitr\"\n\"26\",\"xml-conduit\"\n\"26\",\"appendfile\"\n\"26\",\"unicode-range\"\n\"26\",\"maplist\"\n\"26\",\"adbwireless\"\n\"26\",\"phpundercontrol\"\n\"26\",\"segment-analytics\"\n\"26\",\"ng-required\"\n\"26\",\"uitextposition\"\n\"26\",\"ng2-nvd3\"\n\"26\",\"dbcp\"\n\"26\",\"vodapay-miniprogram\"\n\"26\",\"appcontainer\"\n\"26\",\"safecracker\"\n\"26\",\"s390x\"\n\"26\",\"s2\"\n\"26\",\"afhttpsessionmanager\"\n\"26\",\"cef4delphi\"\n\"26\",\"filesavepicker\"\n\"26\",\"soft-input-panel\"\n\"26\",\"filepattern\"\n\"26\",\"markdownsharp\"\n\"26\",\"ryujit\"\n\"26\",\"sails-skipper\"\n\"26\",\"software-product-lines\"\n\"26\",\"sails-orientdb\"\n\"26\",\"rusoto\"\n\"26\",\"runat\"\n\"26\",\"cascalog\"\n\"26\",\"fbflipper\"\n\"26\",\"css-expressions\"\n\"26\",\"pushover\"\n\"26\",\"gpu.js\"\n\"26\",\"r-server\"\n\"26\",\"ups-api\"\n\"26\",\"vcd\"\n\"26\",\"vax\"\n\"26\",\"push-diffusion\"\n\"26\",\"fast-app-switching\"\n\"26\",\"roauth\"\n\"26\",\"rowstate\"\n\"26\",\"pull-queue\"\n\"26\",\"roomdb\"\n\"26\",\"mysql-error-1066\"\n\"26\",\"aiosmtpd\"\n\"26\",\"simple-realtime-server\"\n\"26\",\"dmx-ssas\"\n\"26\",\"sharepoint-timer-job\"\n\"26\",\"wcat\"\n\"26\",\"goreleaser\"\n\"26\",\"meteor-galaxy\"\n\"26\",\"router-os\"\n\"26\",\"hyperledger-besu\"\n\"26\",\"vbide\"\n\"26\",\"publickeytoken\"\n\"26\",\"fastutil\"\n\"26\",\"sessionend\"\n\"26\",\"route-model-binding\"\n\"26\",\"ptxas\"\n\"26\",\"unwind\"\n\"26\",\"database-installation\"\n\"26\",\"go-structtag\"\n\"26\",\"micronaut-kafka\"\n\"26\",\"django-wkhtmltopdf\"\n\"26\",\"django-voting\"\n\"26\",\"avplayerview\"\n\"26\",\"unmarked-package\"\n\"26\",\"ichat\"\n\"26\",\"famous-engine\"\n\"26\",\"reactxp\"\n\"26\",\"iconic\"\n\"26\",\"mglmapview\"\n\"26\",\"servicem8\"\n\"26\",\"metroframework\"\n\"26\",\"algorithm-animation\"\n\"26\",\"ibm-case-manager\"\n\"26\",\"vvv-wordpress\"\n\"26\",\"service-factory\"\n\"26\",\"icq\"\n\"26\",\"site-column\"\n\"26\",\"crtdbg.h\"\n\"26\",\"windows-sandbox\"\n\"26\",\"databricks-asset-bundle\"\n\"26\",\"unity3d-mecanim\"\n\"26\",\"record-locking\"\n\"26\",\"icu4c\"\n\"26\",\"database-tuning-advisor\"\n\"26\",\"doit\"\n\"26\",\"mysql-udf\"\n\"26\",\"recommenderlab\"\n\"26\",\"data-aware\"\n\"26\",\"servlet-2.5\"\n\"26\",\"wab\"\n\"26\",\"mybatis-sql\"\n\"26\",\"rebus-rabbitmq\"\n\"26\",\"idataobject\"\n\"26\",\"animatedcontainer\"\n\"26\",\"aws-cloudshell\"\n\"26\",\"fantasyland\"\n\"26\",\"rocks\"\n\"26\",\"windows-10-iot-enterprise\"\n\"26\",\"pdfmerger\"\n\"26\",\"svn-administraton\"\n\"26\",\"axd\"\n\"26\",\"cptbarplot\"\n\"26\",\"ioremap\"\n\"26\",\"cqengine\"\n\"26\",\"nestjs-i18n\"\n\"26\",\"pdblp\"\n\"26\",\"cpropertysheet\"\n\"26\",\"simplebutton\"\n\"26\",\"svgo\"\n\"26\",\"ent\"\n\"26\",\"posterous\"\n\"26\",\"springdoc-ui\"\n\"26\",\"invalid-url\"\n\"26\",\"modified-date\"\n\"26\",\"nested-includes\"\n\"26\",\"defold\"\n\"26\",\"mongo-dart\"\n\"26\",\"android-studio-3.1.4\"\n\"26\",\"svg-transforms\"\n\"26\",\"grpc-js\"\n\"26\",\"entity-linking\"\n\"26\",\"cperl-mode\"\n\"26\",\"workflow-definition-language\"\n\"26\",\"oneplusthree\"\n\"26\",\"arangodb-php\"\n\"26\",\"jquery-webcam-plugin\"\n\"26\",\"blend-mode\"\n\"26\",\"jquery-week-calendar\"\n\"26\",\"entity-system\"\n\"26\",\"bleno\"\n\"26\",\"kaizala\"\n\"26\",\"gulp-autoprefixer\"\n\"26\",\"grunt-babel\"\n\"26\",\"ongr\"\n\"26\",\"horizontalfieldmanager\"\n\"26\",\"horizontallistview\"\n\"26\",\"neo4j-aura\"\n\"26\",\"svncommit\"\n\"26\",\"android-trafficstats\"\n\"26\",\"polyvariadic\"\n\"26\",\"spring-insight\"\n\"26\",\"openai-assistants-api\"\n\"26\",\"apple-m2\"\n\"26\",\"nsproxy\"\n\"26\",\"count-unique\"\n\"26\",\"appium-inspector\"\n\"26\",\"error-messages-for\"\n\"26\",\"tvm\"\n\"26\",\"nsolid\"\n\"26\",\"nsimagerep\"\n\"26\",\"superfeedr\"\n\"26\",\"jquery-jscroll\"\n\"26\",\"jtwig\"\n\"26\",\"bootstrap-timepicker\"\n\"26\",\"inheriting-constructors\"\n\"26\",\"posthog\"\n\"26\",\"ponylang\"\n\"26\",\"patch-package\"\n\"26\",\"crawlera\"\n\"26\",\"create-guten-block\"\n\"26\",\"bosh-deployer\"\n\"26\",\"azure-devops-server-2022\"\n\"26\",\"delegating-constructor\"\n\"26\",\"html-tag-summary\"\n\"26\",\"patternsyntaxexception\"\n\"26\",\"rhino-servicebus\"\n\"26\",\"oauth2orize\"\n\"26\",\"viro-react\"\n\"26\",\"libselinux\"\n\"26\",\"rhino-security\"\n\"26\",\"overlayfs\"\n\"26\",\"os-detection\"\n\"26\",\"ordbms\"\n\"26\",\"sfdc-metadata-api\"\n\"26\",\"ucs\"\n\"26\",\"short-filenames\"\n\"26\",\"exrin\"\n\"26\",\"visio2013\"\n\"26\",\"rfb-protocol\"\n\"26\",\"oraoledb\"\n\"26\",\"sysdig\"\n\"26\",\"jasonette\"\n\"26\",\"virtualbox-guest-additions\"\n\"26\",\"new-psdrive\"\n\"26\",\"object-layout\"\n\"26\",\"visual-c++-runtime\"\n\"26\",\"setup-wizard\"\n\"26\",\"sidekiq-cron\"\n\"26\",\"rational-rose\"\n\"26\",\"system.messaging\"\n\"26\",\"ocpp\"\n\"26\",\"rapidsql\"\n\"26\",\"buildmaster\"\n\"26\",\"javax.json\"\n\"26\",\"ravendb5\"\n\"26\",\"typescript-utility\"\n\"26\",\"right-justified\"\n\"26\",\"broadband\"\n\"26\",\"r-formula\"\n\"26\",\"new-object\"\n\"26\",\"sysinfo\"\n\"26\",\"magic-mouse\"\n\"26\",\"magicknet\"\n\"26\",\"google-chrome-flags\"\n\"26\",\"minidumpwritedump\"\n\"26\",\"kosaraju-algorithm\"\n\"26\",\"lwuit-list\"\n\"26\",\"authenticity\"\n\"26\",\"for-xml-explicit\"\n\"26\",\"pymodm\"\n\"26\",\"aadhaar\"\n\"26\",\"amplify-auth-cognito\"\n\"26\",\"fortran-coarrays\"\n\"26\",\"atlassian-python-api\"\n\"26\",\"midori\"\n\"26\",\"amp-ad\"\n\"26\",\"missing-cookies\"\n\"26\",\"macwire\"\n\"26\",\"asus\"\n\"26\",\"cohen-kappa\"\n\"26\",\"3-way-merge\"\n\"26\",\"framemaker\"\n\"26\",\"atlantis\"\n\"26\",\"cocoa-scripting\"\n\"26\",\"coinpayments-api\"\n\"26\",\"magpie\"\n\"26\",\"targetnullvalue\"\n\"26\",\"screenrc\"\n\"26\",\"hangfire-autofac\"\n\"26\",\"xcode9beta6\"\n\"26\",\"convertfrom-json\"\n\"26\",\"scitools\"\n\"26\",\"ui-patterns\"\n\"26\",\"controlparameter\"\n\"26\",\"android-gradle-2.0\"\n\"26\",\"tavern\"\n\"26\",\"timex\"\n\"26\",\"non-virtual-interface\"\n\"26\",\"scncamera\"\n\"26\",\"tabletop-simulator\"\n\"26\",\"tcanvas\"\n\"26\",\"mock-location\"\n\"26\",\"excel-solver\"\n\"26\",\"xcode-previews\"\n\"26\",\"expandometaclass\"\n\"26\",\"tcp-port\"\n\"26\",\"timecop\"\n\"26\",\"asp.net-core-authenticationhandler\"\n\"26\",\"hibernate3-maven-plugin\"\n\"26\",\"polymer-designer-tool\"\n\"26\",\"node-vm2\"\n\"26\",\"taiga\"\n\"26\",\"haskeline\"\n\"26\",\"polkit\"\n\"26\",\"quickdraw\"\n\"26\",\"gem-fury\"\n\"26\",\"uibuilder\"\n\"26\",\"openturns\"\n\"26\",\"redux-orm\"\n\"26\",\"busy-cursor\"\n\"26\",\"cache2k\"\n\"26\",\"gce-persistent-disk\"\n\"26\",\"sqlboiler\"\n\"26\",\"rabbitmqadmin\"\n\"26\",\"r2d3\"\n\"26\",\"hadoop-2.7.2\"\n\"26\",\"ca2000\"\n\"26\",\"iso-8859-15\"\n\"26\",\"redirect-uri-mismatch\"\n\"26\",\"opentaps\"\n\"26\",\"gcmtaskservice\"\n\"26\",\"sql-server-2014-localdb\"\n\"26\",\"assemblyversionattribute\"\n\"26\",\"device-discovery\"\n\"26\",\"corda-flow\"\n\"26\",\"gang-of-four\"\n\"26\",\"copy-local\"\n\"26\",\"expression-blend-3\"\n\"26\",\"itsdangerous\"\n\"26\",\"hammer\"\n\"26\",\"diplib\"\n\"26\",\"rack-mini-profiler\"\n\"26\",\"copy-webpack-plugin\"\n\"26\",\"iteritems\"\n\"26\",\"itil\"\n\"26\",\"drawingml\"\n\"26\",\"python-xmlschema\"\n\"26\",\"s-plus\"\n\"26\",\"terraform-provider-github\"\n\"26\",\"google-cloud-shell-editor\"\n\"26\",\"hugsql\"\n\"26\",\"android-jacoco\"\n\"26\",\"google-dataplex\"\n\"26\",\"react-native-community\"\n\"26\",\"google-gauges\"\n\"26\",\"lockout\"\n\"26\",\"android-jetpack-compose-gesture\"\n\"26\",\"esky\"\n\"26\",\"eslint-plugin-react-hooks\"\n\"26\",\"logmein\"\n\"26\",\"strong-soap\"\n\"26\",\"collective-intelligence\"\n\"26\",\"esplorer\"\n\"26\",\"persistence-manager\"\n\"26\",\"nodejs-express-server\"\n\"26\",\"node-media-server\"\n\"26\",\"resolve-url-loader\"\n\"26\",\"merlin\"\n\"26\",\"georgian\"\n\"26\",\"resharper-10.0\"\n\"26\",\"react-aria\"\n\"26\",\"httpcontext.cache\"\n\"26\",\"streamlink\"\n\"26\",\"melonjs\"\n\"26\",\"httpplatformhandler\"\n\"26\",\"geotrellis\"\n\"26\",\"memory-pressure\"\n\"26\",\"od\"\n\"26\",\"spfield\"\n\"26\",\"angularjs-config\"\n\"26\",\"elasticsearch-mongo-river\"\n\"26\",\"mpi-rma\"\n\"26\",\"geoalchemy\"\n\"26\",\"getmodulefilename\"\n\"26\",\"change-detector-ref\"\n\"26\",\"accordionpane\"\n\"26\",\"getdibits\"\n\"26\",\"splitchunksplugin\"\n\"26\",\"monocross\"\n\"26\",\"columndefinition\"\n\"26\",\"mormot\"\n\"26\",\"mongrel-cluster\"\n\"26\",\"activity-state\"\n\"26\",\"laravel-modules\"\n\"26\",\"mousecapture\"\n\"26\",\"cfbundledisplayname\"\n\"26\",\"custom-datetimepicker\"\n\"26\",\"commercial-application\"\n\"26\",\"tomtom-android-sdk\"\n\"26\",\"durable-subscription\"\n\"26\",\"eglfs\"\n\"26\",\"spc\"\n\"26\",\"activityunittestcase\"\n\"26\",\"mosh\"\n\"26\",\"zend-controller-plugin\"\n\"26\",\"spark-redshift\"\n\"26\",\"hfs\"\n\"26\",\"berksfile\"\n\"26\",\"webidl\"\n\"26\",\"tfs-2005\"\n\"26\",\"toolstripstatuslabel\"\n\"26\",\"hbbtv\"\n\"26\",\"hbase-client\"\n\"26\",\"statmodels\"\n\"26\",\"alice\"\n\"26\",\"subcomponent\"\n\"26\",\"likely-unlikely\"\n\"26\",\"flutter-get\"\n\"26\",\"sonar-plugin\"\n\"26\",\"flutter-patrol\"\n\"26\",\"time4j\"\n\"26\",\"dart-server\"\n\"26\",\"mtrace\"\n\"26\",\"msmqintegrationbinding\"\n\"26\",\"qtbluetooth\"\n\"26\",\"iks\"\n\"26\",\"maven-scm-plugin\"\n\"26\",\"danfojs\"\n\"26\",\"styled-system\"\n\"26\",\"pannellum\"\n\"26\",\"web-ar\"\n\"26\",\"helpndoc\"\n\"26\",\"help-viewer\"\n\"26\",\"amazon-device-messaging\"\n\"26\",\"elvis-operator\"\n\"26\",\"cypress-file-upload\"\n\"26\",\"amazon-certificate-manager\"\n\"26\",\"confidentiality\"\n\"26\",\"scriptish\"\n\"26\",\"webaii\"\n\"26\",\"multi-instance-deployment\"\n\"26\",\"qsys\"\n\"26\",\"email-delivery\"\n\"26\",\"forestadmin\"\n\"26\",\"hdmi-cec\"\n\"26\",\"amazon-guardduty\"\n\"26\",\"mbcalendarkit\"\n\"26\",\"force-touch\"\n\"26\",\"webdriver-w3c-spec\"\n\"26\",\"ifc-open-shell\"\n\"26\",\"beaker-notebook\"\n\"26\",\"dart-ui\"\n\"26\",\"globalize2\"\n\"26\",\"all-delete-orphan\"\n\"26\",\"global-functions\"\n\"26\",\"ihtmldocument\"\n\"26\",\"email-processing\"\n\"26\",\"better-errors-gem\"\n\"26\",\"batch-choice\"\n\"26\",\"secondary-live-tile\"\n\"26\",\"link-time-optimization\"\n\"26\",\"zope3\"\n\"26\",\"v8js\"\n\"26\",\"amazon-gamelift\"\n\"25\",\"skimr\"\n\"25\",\"sliding-doors\"\n\"25\",\"fieldcollapsing\"\n\"25\",\"sliver-grid\"\n\"25\",\"jerkson\"\n\"25\",\"apache-camel-cdi\"\n\"25\",\"primeng-tree\"\n\"25\",\"clickhouse-go\"\n\"25\",\"trusted-sites\"\n\"25\",\"class-reference\"\n\"25\",\"edmgen\"\n\"25\",\"decouple\"\n\"25\",\"phonebook\"\n\"25\",\"preview-feature\"\n\"25\",\"xrc\"\n\"25\",\"remote-administration\"\n\"25\",\"squeezebox\"\n\"25\",\"balsamiq\"\n\"25\",\"primary-constructor\"\n\"25\",\"citrus-pay\"\n\"25\",\"private-class\"\n\"25\",\"jetty-10\"\n\"25\",\"stagexl\"\n\"25\",\"dcpu-16\"\n\"25\",\"editcap\"\n\"25\",\"wfs\"\n\"25\",\"yapf\"\n\"25\",\"client-side-data\"\n\"25\",\"greybox\"\n\"25\",\"temporaries\"\n\"25\",\"llvm-cov\"\n\"25\",\"loading-animation\"\n\"25\",\"vuesax\"\n\"25\",\"tensorflow-gradient\"\n\"25\",\"program-structure\"\n\"25\",\"git-crypt\"\n\"25\",\"flutter-custompaint\"\n\"25\",\"react-native-voice\"\n\"25\",\"web-widget\"\n\"25\",\"graph-sharp\"\n\"25\",\"widescreen\"\n\"25\",\"clojure.test\"\n\"25\",\"jetbrains-gateway\"\n\"25\",\"multi-release-jar\"\n\"25\",\"printform\"\n\"25\",\"ansible-runner\"\n\"25\",\"interchange\"\n\"25\",\"bitnami-kafka\"\n\"25\",\"python-pulsar\"\n\"25\",\"cassandra-lucene-index\"\n\"25\",\"bitstuffing\"\n\"25\",\"bitwise-not\"\n\"25\",\"owin-security\"\n\"25\",\"fit-framework\"\n\"25\",\"jsartoolkit\"\n\"25\",\"blastula\"\n\"25\",\"imageloader\"\n\"25\",\"mappers\"\n\"25\",\"syncano\"\n\"25\",\"jsduck\"\n\"25\",\"semisupervised-learning\"\n\"25\",\"symfony-cache\"\n\"25\",\"mark-and-sweep\"\n\"25\",\"chiseltest\"\n\"25\",\"cdonts\"\n\"25\",\"chromatic\"\n\"25\",\"adobe-sign\"\n\"25\",\"add-references-dialog\"\n\"25\",\"symmetric-difference\"\n\"25\",\"python-cloudant\"\n\"25\",\"voice-control\"\n\"25\",\"saddle\"\n\"25\",\"smartos\"\n\"25\",\"l20n\"\n\"25\",\"jsoneditor\"\n\"25\",\"chrome-ux-report\"\n\"25\",\"smartmatch\"\n\"25\",\"incr-tcl\"\n\"25\",\"chrome-webrequest\"\n\"25\",\"sentinel1\"\n\"25\",\"incremental-compiler\"\n\"25\",\"disp\"\n\"25\",\"rup\"\n\"25\",\"case-folding\"\n\"25\",\"play2-mini\"\n\"25\",\"flashcatalyst\"\n\"25\",\"paddle\"\n\"25\",\"pipedream\"\n\"25\",\"carto-mobile\"\n\"25\",\"adjustable\"\n\"25\",\"pagersnaphelper\"\n\"25\",\"find-sec-bugs\"\n\"25\",\"xendesktop\"\n\"25\",\"selenium-fitnesse-bridge\"\n\"25\",\"adts\"\n\"25\",\"snpe\"\n\"25\",\"mapkit-js\"\n\"25\",\"content-repository\"\n\"25\",\"dbgeng\"\n\"25\",\"configurationelement\"\n\"25\",\"recursiveiterator\"\n\"25\",\"gost3410\"\n\"25\",\"watson-assistant-solutions\"\n\"25\",\"servicenow-client-scripts\"\n\"25\",\"root-finding\"\n\"25\",\"session-cache\"\n\"25\",\"vulcanize\"\n\"25\",\"roottools\"\n\"25\",\"sessioncontext\"\n\"25\",\"docview\"\n\"25\",\"rose-db-object\"\n\"25\",\"database-driven\"\n\"25\",\"document-storage\"\n\"25\",\"ropes\"\n\"25\",\"vue-validator\"\n\"25\",\"jini\"\n\"25\",\"angular-webpack\"\n\"25\",\"serilog-filter\"\n\"25\",\"angulartics2\"\n\"25\",\"jms-queue\"\n\"25\",\"react-swipeable-views\"\n\"25\",\"data-harvest\"\n\"25\",\"service-worker-config\"\n\"25\",\"grails-3.3.x\"\n\"25\",\"roomplan\"\n\"25\",\"oracle-cursor\"\n\"25\",\"csi-driver\"\n\"25\",\"canjs-routing\"\n\"25\",\"cross-page-posting\"\n\"25\",\"val\"\n\"25\",\"hyperloop\"\n\"25\",\"akka-monitoring\"\n\"25\",\"wildfly-17\"\n\"25\",\"facepy\"\n\"25\",\"ibm-db2\"\n\"25\",\"icallbackeventhandler\"\n\"25\",\"pt-online-schema-change\"\n\"25\",\"wikitude-sdk\"\n\"25\",\"publishsubject\"\n\"25\",\"validate.js\"\n\"25\",\"docker-copy\"\n\"25\",\"dn\"\n\"25\",\"windows-application-packaging\"\n\"25\",\"shareware\"\n\"25\",\"pydal\"\n\"25\",\"validateset\"\n\"25\",\"ruby-debug-ide\"\n\"25\",\"kentico-11\"\n\"25\",\"vec\"\n\"25\",\"fastlane-pilot\"\n\"25\",\"window-decoration\"\n\"25\",\"crystal-reports-viewer\"\n\"25\",\"sharepoint-discussion-board\"\n\"25\",\"facebook-tabs\"\n\"25\",\"pryr\"\n\"25\",\"variable-width\"\n\"25\",\"windows-1255\"\n\"25\",\"validation-layers\"\n\"25\",\"fat16\"\n\"25\",\"cakephp-debug-kit\"\n\"25\",\"icingaweb2\"\n\"25\",\"ajax-push\"\n\"25\",\"ajaxify\"\n\"25\",\"single-user\"\n\"25\",\"unnotificationattachment\"\n\"25\",\"pybigquery\"\n\"25\",\"aws-java-sdk-dynamodb\"\n\"25\",\"meteor-cordova\"\n\"25\",\"microsoft-documentation\"\n\"25\",\"windows-media-center\"\n\"25\",\"android-tools-namespace\"\n\"25\",\"pdfsharpcore\"\n\"25\",\"karaf-maven-plugin\"\n\"25\",\"moltin\"\n\"25\",\"paypal-soap\"\n\"25\",\"infosphere-spl\"\n\"25\",\"bluecloth\"\n\"25\",\"appscale\"\n\"25\",\"nested-exceptions\"\n\"25\",\"mongodb-3.6\"\n\"25\",\"mod-proxy-ajp\"\n\"25\",\"azure-agent\"\n\"25\",\"jrepl\"\n\"25\",\"sap-analytics-cloud\"\n\"25\",\"aqua-data-studio\"\n\"25\",\"opencalais\"\n\"25\",\"guid-partition-table\"\n\"25\",\"injection-tokens\"\n\"25\",\"swagger-jsdocs\"\n\"25\",\"julia-studio\"\n\"25\",\"hpx\"\n\"25\",\"one-simulator\"\n\"25\",\"enumerate-devices\"\n\"25\",\"arangodb-foxx\"\n\"25\",\"appjar\"\n\"25\",\"horizontal-alignment\"\n\"25\",\"opencl.net\"\n\"25\",\"depot\"\n\"25\",\"onfocuschangelistener\"\n\"25\",\"gsheets\"\n\"25\",\"axios-retry\"\n\"25\",\"init-parameters\"\n\"25\",\"enterprise-architecture\"\n\"25\",\"invalid-object-name\"\n\"25\",\"design-documents\"\n\"25\",\"postman-collection\"\n\"25\",\"sbt-0.13\"\n\"25\",\"corestore\"\n\"25\",\"openerp-6\"\n\"25\",\"nscfstring\"\n\"25\",\"android-view-invalidate\"\n\"25\",\"demosaicing\"\n\"25\",\"mog\"\n\"25\",\"easyb\"\n\"25\",\"spring-boot-data-geode\"\n\"25\",\"passport.socketio\"\n\"25\",\"tvos13\"\n\"25\",\"kdbg\"\n\"25\",\"cpu-hazard\"\n\"25\",\"epmd\"\n\"25\",\"bluetoothadapter\"\n\"25\",\"jquery-select2-3\"\n\"25\",\"equal-range\"\n\"25\",\"ttabsheet\"\n\"25\",\"postgresql-simple\"\n\"25\",\"azure-blockchain-service\"\n\"25\",\"boxable\"\n\"25\",\"worklight-geolocation\"\n\"25\",\"bounding-volume\"\n\"25\",\"setstring\"\n\"25\",\"google-auth-library-nodejs\"\n\"25\",\"signalr-service\"\n\"25\",\"freegeoip\"\n\"25\",\"video-on-demand\"\n\"25\",\"codesense\"\n\"25\",\"settings-bundle\"\n\"25\",\"objectset\"\n\"25\",\"netlify-form\"\n\"25\",\"2d-context-api\"\n\"25\",\"network-state\"\n\"25\",\"winelib\"\n\"25\",\"astronomer\"\n\"25\",\"video-gallery\"\n\"25\",\"domain-data-modelling\"\n\"25\",\"btahl7\"\n\"25\",\"forest-plots\"\n\"25\",\"amber-smalltalk\"\n\"25\",\"google-anthos-service-mesh\"\n\"25\",\"sieve-of-atkin\"\n\"25\",\"windows-socket-api\"\n\"25\",\"java-20\"\n\"25\",\"sfauthorizationpluginview\"\n\"25\",\"extjs-chart\"\n\"25\",\"brother-print-sdk\"\n\"25\",\"mission-control\"\n\"25\",\".net-spark\"\n\"25\",\"libexif\"\n\"25\",\"originlab\"\n\"25\",\"revolute-joints\"\n\"25\",\"mime4j\"\n\"25\",\"table-index\"\n\"25\",\"madcap\"\n\"25\",\"ubl\"\n\"25\",\"lumenworks\"\n\"25\",\"knitr-spin\"\n\"25\",\"osmar\"\n\"25\",\"lwt\"\n\"25\",\"outlook-2019\"\n\"25\",\"google-chrome-webview\"\n\"25\",\"view-templates\"\n\"25\",\"google-bi-engine\"\n\"25\",\"android-apt\"\n\"25\",\"nxc\"\n\"25\",\"android-1.6-donut\"\n\"25\",\"ktrain\"\n\"25\",\"abstract-action\"\n\"25\",\"cocoapods-1.0.1\"\n\"25\",\"pydatalog\"\n\"25\",\"otlp-grpc\"\n\"25\",\"gon\"\n\"25\",\"go-back-n\"\n\"25\",\"good-dynamics\"\n\"25\",\"view-model-pattern\"\n\"25\",\"object-notation\"\n\"25\",\"framebusting\"\n\"25\",\"wkinterfacegroup\"\n\"25\",\"libtomcrypt\"\n\"25\",\"wknavigationdelegate\"\n\"25\",\"visual-studio-dbpro\"\n\"25\",\"pyevolve\"\n\"25\",\"pydeck\"\n\"25\",\"build-target\"\n\"25\",\"facebook-as3-api\"\n\"25\",\"neuron-simulator\"\n\"25\",\"visual-build-professional\"\n\"25\",\"hidden-features\"\n\"25\",\"android-compose-card\"\n\"25\",\"scatterplot3d\"\n\"25\",\"redis-search\"\n\"25\",\"model-based-testing\"\n\"25\",\"gwt-2.8\"\n\"25\",\"pokemon-go\"\n\"25\",\"context-sensitive-help\"\n\"25\",\"drmaa\"\n\"25\",\"hibernate-5\"\n\"25\",\"scottplot\"\n\"25\",\"podfile-lock\"\n\"25\",\"sql-server-collation\"\n\"25\",\"cordova-5.0.0\"\n\"25\",\"dfd\"\n\"25\",\"novaclient\"\n\"25\",\"dfdl\"\n\"25\",\"dfinity\"\n\"25\",\"opengts\"\n\"25\",\"tca\"\n\"25\",\"directional-light\"\n\"25\",\"openinventor\"\n\"25\",\"high-resolution-clock\"\n\"25\",\"plovr\"\n\"25\",\"mockstatic\"\n\"25\",\"xamarin.droid\"\n\"25\",\"openjms\"\n\"25\",\"qwindow\"\n\"25\",\"tarantool-cartridge\"\n\"25\",\"nothrow\"\n\"25\",\"time-trial\"\n\"25\",\"gambas\"\n\"25\",\"exi\"\n\"25\",\"hoodie\"\n\"25\",\"isspace\"\n\"25\",\"dtsearch\"\n\"25\",\"toad-data-point\"\n\"25\",\"hamlc\"\n\"25\",\"jamon\"\n\"25\",\"xcode13.2\"\n\"25\",\"refine.js\"\n\"25\",\"nth-root\"\n\"25\",\"refspec\"\n\"25\",\"reference-binding\"\n\"25\",\"node-supervisor\"\n\"25\",\"hit-highlighting\"\n\"25\",\"mkdirection\"\n\"25\",\"aster\"\n\"25\",\"nsurlrequestcachepolicy\"\n\"25\",\"scikit-survival\"\n\"25\",\"handle-leak\"\n\"25\",\"xcode7.1beta\"\n\"25\",\"schema.rb\"\n\"25\",\"dpll\"\n\"25\",\"regexp-grammars\"\n\"25\",\"tizen-web-simulator\"\n\"25\",\"tadodataset\"\n\"25\",\"jakarta-ee-security-api\"\n\"25\",\"vfr-reader\"\n\"25\",\"regex-recursion\"\n\"25\",\"redocly\"\n\"25\",\"devspace\"\n\"25\",\"npm-cache\"\n\"25\",\"contiki-process\"\n\"25\",\"express-cassandra\"\n\"25\",\"httprouter\"\n\"25\",\"cssnano\"\n\"25\",\"launchdagent\"\n\"25\",\"qqmlengine\"\n\"25\",\"tone-generator\"\n\"25\",\"nhprof\"\n\"25\",\"node-rsa\"\n\"25\",\"lua-busted\"\n\"25\",\"layouttransition\"\n\"25\",\"angular2-decorators\"\n\"25\",\"strerror\"\n\"25\",\"elm-architecture\"\n\"25\",\"stringcomparer\"\n\"25\",\"react-color\"\n\"25\",\"laravel-vite\"\n\"25\",\"text-normalization\"\n\"25\",\"cula\"\n\"25\",\"elevatezoom\"\n\"25\",\"stripplot\"\n\"25\",\"text-decoding\"\n\"25\",\"currentitem\"\n\"25\",\"reswift\"\n\"25\",\"strrchr\"\n\"25\",\"z-score\"\n\"25\",\"log4net-filter\"\n\"25\",\"nite\"\n\"25\",\"ekcalendar\"\n\"25\",\"ios-stickers\"\n\"25\",\"ztree\"\n\"25\",\"ios13.4\"\n\"25\",\"node-apn\"\n\"25\",\"ios18\"\n\"25\",\"google-goggles\"\n\"25\",\"node-api\"\n\"25\",\"elasticsearch-scripting\"\n\"25\",\"google-cloud-healthcare\"\n\"25\",\"getcontent\"\n\"25\",\"google-cloud-internal-load-balancer\"\n\"25\",\"nnapi\"\n\"25\",\"nmssh\"\n\"25\",\"ohm\"\n\"25\",\"certificate-transparency\"\n\"25\",\"moonapns\"\n\"25\",\"mptcp\"\n\"25\",\"mozjpeg\"\n\"25\",\"memory-reallocation\"\n\"25\",\"httpwatch\"\n\"25\",\"pytorch3d\"\n\"25\",\"memory-table\"\n\"25\",\"proxy-object\"\n\"25\",\"stp\"\n\"25\",\"odp\"\n\"25\",\"http-upload\"\n\"25\",\"lds\"\n\"25\",\"mongosh\"\n\"25\",\"odoo-mobile\"\n\"25\",\"erubis\"\n\"25\",\"http-status-code-100\"\n\"25\",\"pyzo\"\n\"25\",\"centos-web-panel\"\n\"25\",\"esb-toolkit-2.0\"\n\"25\",\"link-local\"\n\"25\",\"google-qpx-express-api\"\n\"25\",\"autonumeric.js\"\n\"25\",\"hcard\"\n\"25\",\"utf-7\"\n\"25\",\"toolstripcontrolhost\"\n\"25\",\"qualys\"\n\"25\",\"tracking.js\"\n\"25\",\"headertext\"\n\"25\",\"compiler-version\"\n\"25\",\"trains\"\n\"25\",\"encrypted-shared-preference\"\n\"25\",\"user-administration\"\n\"25\",\"lightfm\"\n\"25\",\"auto-registration\"\n\"25\",\"msvc14\"\n\"25\",\"heterogeneous-array\"\n\"25\",\"parenscript\"\n\"25\",\"font-smoothing\"\n\"25\",\"headerdoc\"\n\"25\",\"folium-plugins\"\n\"25\",\"bean-managed-transactions\"\n\"25\",\"msdasql\"\n\"25\",\"hawkbit\"\n\"25\",\"archos\"\n\"25\",\"amazon-linux-2023\"\n\"25\",\"quantitative\"\n\"25\",\"linear-types\"\n\"25\",\"quartz.net-3.0\"\n\"25\",\"arrow-python\"\n\"25\",\"maven-mojo\"\n\"25\",\"zip.js\"\n\"25\",\"google-language-api\"\n\"25\",\"amazon-memory-db\"\n\"25\",\"bigtop\"\n\"25\",\"zend-feed\"\n\"25\",\"artisan-serve\"\n\"25\",\"multi-mapping\"\n\"25\",\"zmodem\"\n\"25\",\"stargate-oss\"\n\"25\",\"webfocus\"\n\"25\",\"arrayref\"\n\"25\",\"maven-docker-plugin\"\n\"25\",\"mcollective\"\n\"25\",\"amazon-fps\"\n\"25\",\"computation-graph\"\n\"25\",\"ti-dsp\"\n\"25\",\"ember-1\"\n\"25\",\"zend-dom-query\"\n\"25\",\"amazon-s3-access-points\"\n\"25\",\"linqtocsv\"\n\"25\",\"maven-source-plugin\"\n\"25\",\"linq-extensions\"\n\"25\",\"embedded-cassandra\"\n\"25\",\"ijkplayer\"\n\"25\",\"shift-reduce\"\n\"25\",\"sharpkml\"\n\"25\",\"arquicklook\"\n\"25\",\"seekbar-thumb\"\n\"25\",\"gmgridview\"\n\"25\",\"flutter-list-tile\"\n\"25\",\"scrooge\"\n\"25\",\"qsignalmapper\"\n\"25\",\"google-nexus\"\n\"25\",\"max-allowed-packet\"\n\"25\",\"webistrano\"\n\"25\",\"gnu-common-lisp\"\n\"25\",\"parameter-splatting\"\n\"24\",\"clickbank\"\n\"24\",\"github-container-registry\"\n\"24\",\"treemaps\"\n\"24\",\"clbeaconregion\"\n\"24\",\"xspf\"\n\"24\",\"fcl\"\n\"24\",\"debezium-server\"\n\"24\",\"tensorflow-c++\"\n\"24\",\"remote-development\"\n\"24\",\"efcore.bulkextensions\"\n\"24\",\"transmogrifier\"\n\"24\",\"telegraf-output-plugins\"\n\"24\",\"gratia\"\n\"24\",\"trax\"\n\"24\",\"telprompt\"\n\"24\",\"webradio\"\n\"24\",\"standard-evaluation\"\n\"24\",\"websocket-rails\"\n\"24\",\"transifex\"\n\"24\",\"edward\"\n\"24\",\"transmission-daemon\"\n\"24\",\"balana\"\n\"24\",\"apache-hop\"\n\"24\",\"babel-plugin-react-intl\"\n\"24\",\"clucene\"\n\"24\",\"jdbi3-core\"\n\"24\",\"marytts\"\n\"24\",\"gretl\"\n\"24\",\"file-inclusion\"\n\"24\",\"mvcrecaptcha\"\n\"24\",\"backout\"\n\"24\",\"clash\"\n\"24\",\"private-functions\"\n\"24\",\"skel\"\n\"24\",\"math.round\"\n\"24\",\"yandex-metrika\"\n\"24\",\"jboss-amq\"\n\"24\",\"fiddler-dev\"\n\"24\",\"mat-error\"\n\"24\",\"phonenumberutils\"\n\"24\",\"marpa\"\n\"24\",\"balancing-groups\"\n\"24\",\"vueify\"\n\"24\",\"jdownloader\"\n\"24\",\"class-cluster\"\n\"24\",\"pg-partman\"\n\"24\",\"fluentcassandra\"\n\"24\",\"float32\"\n\"24\",\"eclipse-collections\"\n\"24\",\"yacas\"\n\"24\",\"slickr\"\n\"24\",\"v-tooltip\"\n\"24\",\"multiple-regression\"\n\"24\",\"reluctant-quantifiers\"\n\"24\",\"multiple-schema\"\n\"24\",\"vue-data-tables\"\n\"24\",\"prezi\"\n\"24\",\"feedback-loop\"\n\"24\",\"clrprofiler\"\n\"24\",\"weakeventmanager\"\n\"24\",\"matchit\"\n\"24\",\"jceks\"\n\"24\",\"dbobject\"\n\"24\",\"yarv\"\n\"24\",\"rembg\"\n\"24\",\"vsts-sync-migrator\"\n\"24\",\"adobe-form\"\n\"24\",\"python4delphi\"\n\"24\",\"indexed-image\"\n\"24\",\"lacontext\"\n\"24\",\"jsonb-array-elements\"\n\"24\",\"ng-sortable\"\n\"24\",\"checkinstall\"\n\"24\",\"swi-prolog-for-sharing\"\n\"24\",\"flash-ide\"\n\"24\",\"jsontemplate\"\n\"24\",\"json-api-response-converter\"\n\"24\",\"json-annotation\"\n\"24\",\"cci\"\n\"24\",\"ccscene\"\n\"24\",\"sejda\"\n\"24\",\"safe-stack\"\n\"24\",\"contenttemplateselector\"\n\"24\",\"python-config\"\n\"24\",\"xml-column\"\n\"24\",\"discounts\"\n\"24\",\"unary-function\"\n\"24\",\"immutant\"\n\"24\",\"datefinder\"\n\"24\",\"xmlbeans-maven-plugin\"\n\"24\",\"immutability-helper\"\n\"24\",\"sencha-command\"\n\"24\",\"imgix\"\n\"24\",\"sn.exe\"\n\"24\",\"runtime-configuration\"\n\"24\",\"carp\"\n\"24\",\"page-fragments\"\n\"24\",\"laravel-auditing\"\n\"24\",\"selenium-hub\"\n\"24\",\"datascript\"\n\"24\",\"firebase-app-hosting\"\n\"24\",\"vsct\"\n\"24\",\"smile\"\n\"24\",\"apollo-link-state\"\n\"24\",\"fsutil\"\n\"24\",\"xp-mode\"\n\"24\",\"ng-annotate\"\n\"24\",\"nhaml\"\n\"24\",\"p4a\"\n\"24\",\"swift-string\"\n\"24\",\"jsr179\"\n\"24\",\"symbol-tables\"\n\"24\",\"mappedby\"\n\"24\",\"oxwall\"\n\"24\",\"conform\"\n\"24\",\"fsc\"\n\"24\",\"contentflow\"\n\"24\",\"maml\"\n\"24\",\"pivot-grid\"\n\"24\",\"jsbundling-rails\"\n\"24\",\"overrun\"\n\"24\",\"apache-plc4x\"\n\"24\",\"jscompress\"\n\"24\",\"rxjs-dom\"\n\"24\",\"jspresso\"\n\"24\",\"dbaas\"\n\"24\",\"jstreer\"\n\"24\",\"fritzbox\"\n\"24\",\"symfony7\"\n\"24\",\"ng-component-outlet\"\n\"24\",\"page-directives\"\n\"24\",\"casadi\"\n\"24\",\"grafana-tempo\"\n\"24\",\"unsafe-eval\"\n\"24\",\"mysql-shell\"\n\"24\",\"angular-timer\"\n\"24\",\"shared-drive\"\n\"24\",\"roxy\"\n\"24\",\"vaultsharp\"\n\"24\",\"shared-file\"\n\"24\",\"ptp\"\n\"24\",\"pyclips\"\n\"24\",\"serial-monitor\"\n\"24\",\"mysql-error-1267\"\n\"24\",\"rs\"\n\"24\",\"icefaces-2\"\n\"24\",\"rsolr\"\n\"24\",\"veracrypt\"\n\"24\",\"verity\"\n\"24\",\"uri-fragment\"\n\"24\",\"sharedservices\"\n\"24\",\"wcf-interoperability\"\n\"24\",\"i18n-node\"\n\"24\",\"wagtail-search\"\n\"24\",\"n900\"\n\"24\",\"icicle-diagram\"\n\"24\",\"gradle-android-test-plugi\"\n\"24\",\"hypersql\"\n\"24\",\"windows-performance-analyzer\"\n\"24\",\"sharepoint-2003\"\n\"24\",\"jline3\"\n\"24\",\"aglio\"\n\"24\",\"read.fwf\"\n\"24\",\"governance-registry\"\n\"24\",\"meta-where\"\n\"24\",\"animejs\"\n\"24\",\"kendo-contextmenu\"\n\"24\",\"crnk\"\n\"24\",\"rebasing\"\n\"24\",\"way2sms\"\n\"24\",\"aws-lambda-go\"\n\"24\",\"simulink-library\"\n\"24\",\"single-spa-react\"\n\"24\",\"fast-refresh\"\n\"24\",\"mybatis-plus\"\n\"24\",\"uptime-monitoring\"\n\"24\",\"routeattribute\"\n\"24\",\"puma-dev\"\n\"24\",\"upvar\"\n\"24\",\"database-diagramming\"\n\"24\",\"rtai\"\n\"24\",\"id3lib\"\n\"24\",\"naming-containers\"\n\"24\",\"windows-principal\"\n\"24\",\"windmill\"\n\"24\",\"awesomeprint\"\n\"24\",\"candy\"\n\"24\",\"ruby-cocoa\"\n\"24\",\"kissmetrics\"\n\"24\",\"fastagi\"\n\"24\",\"microcoding\"\n\"24\",\"r-rook-package\"\n\"24\",\"wildwebdeveloper\"\n\"24\",\"joomfish\"\n\"24\",\"vwo\"\n\"24\",\"awql\"\n\"24\",\"r-rio\"\n\"24\",\"serilog-sinks-elasticsearch\"\n\"24\",\"mysql-error-1136\"\n\"24\",\"simple-spring-memcached\"\n\"24\",\"mfslidemenu\"\n\"24\",\"oracle-agile-plm\"\n\"24\",\"swiftdate\"\n\"24\",\"sap-bsp\"\n\"24\",\"jquery-datatables-rails\"\n\"24\",\"turn-by-turn\"\n\"24\",\"sas-visual-analytics\"\n\"24\",\"one-trust\"\n\"24\",\"grovepi+\"\n\"24\",\"jquery-xml\"\n\"24\",\"iojs\"\n\"24\",\"sas-stored-process\"\n\"24\",\"ioports\"\n\"24\",\"azure-elastic-sharding\"\n\"24\",\"boolean-search\"\n\"24\",\"opencart-events\"\n\"24\",\"nscompoundpredicate\"\n\"24\",\"apple-search-ads\"\n\"24\",\"deskband\"\n\"24\",\"superpower\"\n\"24\",\"grpc-kotlin\"\n\"24\",\"twitter-util\"\n\"24\",\"dynamic-keyword\"\n\"24\",\"applicationpage\"\n\"24\",\"deployjava\"\n\"24\",\"nspathcontrol\"\n\"24\",\"nsfontpanel\"\n\"24\",\"neo4jrestclient\"\n\"24\",\"openal-soft\"\n\"24\",\"pbuilder\"\n\"24\",\"popmotion\"\n\"24\",\"spring-cloud-azure\"\n\"24\",\"epiphany\"\n\"24\",\"katacoda\"\n\"24\",\"bootrun\"\n\"24\",\"html-reports-jenkins\"\n\"24\",\"html-to-text\"\n\"24\",\"julia-gpu\"\n\"24\",\"jwebbrowser\"\n\"24\",\"html.renderpartial\"\n\"24\",\"createentityadapter\"\n\"24\",\"wrds-compusat\"\n\"24\",\"html5builder\"\n\"24\",\"dynamics-nav-2009\"\n\"24\",\"enyim.caching\"\n\"24\",\"infinite-sequence\"\n\"24\",\"eric-ide\"\n\"24\",\"android-swipe\"\n\"24\",\"eris\"\n\"24\",\"gui-design\"\n\"24\",\"inview\"\n\"24\",\"gui-test-framework\"\n\"24\",\"grunt-shell\"\n\"24\",\"html-manipulation\"\n\"24\",\"guix\"\n\"24\",\"infinispan-8\"\n\"24\",\"word-style\"\n\"24\",\"peak-detection\"\n\"24\",\"kango-framework\"\n\"24\",\"brackets-shell\"\n\"24\",\"modelmetadataprovider\"\n\"24\",\"passwordvault\"\n\"24\",\"positional-operator\"\n\"24\",\"pdftables\"\n\"24\",\"pdftex\"\n\"24\",\"worklight-analytics\"\n\"24\",\"invokeandwait\"\n\"24\",\"amdatu\"\n\"24\",\"coin-or\"\n\"24\",\"ubuntu-16.10\"\n\"24\",\"windows-xp-sp2\"\n\"24\",\"rim-4.5\"\n\"24\",\"libasound\"\n\"24\",\"outliner\"\n\"24\",\"ocaml-toplevel\"\n\"24\",\"rhino-commons\"\n\"24\",\"abline\"\n\"24\",\"cocoapods-1.1.1\"\n\"24\",\"gomega\"\n\"24\",\"organizational-unit\"\n\"24\",\"pydictionary\"\n\"24\",\"forge2d\"\n\"24\",\"showtext\"\n\"24\",\"ripemd\"\n\"24\",\"visual-studio-publish\"\n\"24\",\"learnpress\"\n\"24\",\"pysqlcipher\"\n\"24\",\"sidewaffle\"\n\"24\",\"pydantic-settings\"\n\"24\",\"amplify-flutter\"\n\"24\",\"tablayout\"\n\"24\",\"oryx\"\n\"24\",\"macro-rules\"\n\"24\",\"oauth2-server\"\n\"24\",\"razor-3\"\n\"24\",\"fabletools\"\n\"24\",\"vlc.dotnet\"\n\"24\",\"pyfakefs\"\n\"24\",\"pytest-dependency\"\n\"24\",\"oaf\"\n\"24\",\"google-ads-data-hub\"\n\"24\",\"jasidepanels\"\n\"24\",\"mindbody\"\n\"24\",\"lemoon\"\n\"24\",\"leshan\"\n\"24\",\"lwuit-button\"\n\"24\",\"new-webserviceproxy\"\n\"24\",\"setthreadaffinitymask\"\n\"24\",\"code-search\"\n\"24\",\"fabric-digits\"\n\"24\",\"systemmanagement\"\n\"24\",\".net-gadgeteer\"\n\"24\",\"system-on-chip\"\n\"24\",\"pynetdicom\"\n\"24\",\"miller\"\n\"24\",\"videocore\"\n\"24\",\"extglob\"\n\"24\",\"foswiki\"\n\"24\",\"revoke-token\"\n\"24\",\"pylibmc\"\n\"24\",\"atag\"\n\"24\",\"network-framework\"\n\"24\",\"vinyl\"\n\"24\",\"revmobads\"\n\"24\",\"vim-quickfix\"\n\"24\",\"atom-beautify\"\n\"24\",\"winsql\"\n\"24\",\"build-rules\"\n\"24\",\"vimpulse\"\n\"24\",\"mailbox\"\n\"24\",\"rajawalivr\"\n\"24\",\"occasionallyconnected\"\n\"24\",\"viemu\"\n\"24\",\"systemfit\"\n\"24\",\"google-chrome-theme\"\n\"24\",\"librsync\"\n\"24\",\"winhelp\"\n\"24\",\"oci-python-sdk\"\n\"24\",\"viewcontext\"\n\"24\",\"syslistview32\"\n\"24\",\"winsock-lsp\"\n\"24\",\"raspberry-pi5\"\n\"24\",\".net-core-logging\"\n\"24\",\"c#-7.1\"\n\"24\",\"task-switching\"\n\"24\",\"di-containers\"\n\"24\",\"time-travel\"\n\"24\",\"openkm\"\n\"24\",\"taskset\"\n\"24\",\"coocox\"\n\"24\",\"cordova-win10\"\n\"24\",\"galleriffic\"\n\"24\",\"cookiebot\"\n\"24\",\"cactus\"\n\"24\",\"aspchart\"\n\"24\",\"excelize\"\n\"24\",\"mklocalsearchrequest\"\n\"24\",\"executeprocesstask\"\n\"24\",\"openimageio\"\n\"24\",\"bulk-mail\"\n\"24\",\"reek\"\n\"24\",\"hibernate-query\"\n\"24\",\"mockoon\"\n\"24\",\"hash-location-strategy\"\n\"24\",\"tcollection\"\n\"24\",\"asterisk-java\"\n\"24\",\"numeric-conversion\"\n\"24\",\"copula\"\n\"24\",\"asp.net-4.8\"\n\"24\",\"geckoboard\"\n\"24\",\"asternet\"\n\"24\",\"dpm\"\n\"24\",\"dr.racket\"\n\"24\",\"gwt-widgets\"\n\"24\",\"guppy\"\n\"24\",\"copyleaks-api\"\n\"24\",\"mkcert\"\n\"24\",\"bwu-datagrid\"\n\"24\",\"plinqo\"\n\"24\",\"continuity\"\n\"24\",\"bwplot\"\n\"24\",\"ui-guidelines\"\n\"24\",\"raco\"\n\"24\",\"isinteger\"\n\"24\",\"xamgrid\"\n\"24\",\"tipkit\"\n\"24\",\"taleo-connect-client\"\n\"24\",\"tokyo-tyrant\"\n\"24\",\"android-controls\"\n\"24\",\"pocketmine\"\n\"24\",\"modbus-tk\"\n\"24\",\"sqlite2\"\n\"24\",\"x-callback-url\"\n\"24\",\"quickgrid\"\n\"24\",\"non-uniform-distribution\"\n\"24\",\"spring-starter\"\n\"24\",\"tkplot\"\n\"24\",\"node-static\"\n\"24\",\"isosurface\"\n\"24\",\"nordic-semi\"\n\"24\",\"dtreeviz\"\n\"24\",\"azure-service-hooks\"\n\"24\",\"android-droidtext\"\n\"24\",\"spyware\"\n\"24\",\"x64dbg\"\n\"24\",\"uilocalizedcollation\"\n\"24\",\"expo-updates\"\n\"24\",\"sproutcore-views\"\n\"24\",\"targettype\"\n\"24\",\"acquisition\"\n\"24\",\"logz.io\"\n\"24\",\"ewmh\"\n\"24\",\"curry\"\n\"24\",\"acts-as-ferret\"\n\"24\",\"custom-contextmenu\"\n\"24\",\"logicblox\"\n\"24\",\"qchar\"\n\"24\",\"gen-fsm\"\n\"24\",\"nipype\"\n\"24\",\"ios11.4\"\n\"24\",\"getpicture\"\n\"24\",\"android-percent-library\"\n\"24\",\"nis\"\n\"24\",\"hubl\"\n\"24\",\"percentile-cont\"\n\"24\",\"es2022\"\n\"24\",\"resulttransformer\"\n\"24\",\"react-native-background-fetch\"\n\"24\",\"cfadmin\"\n\"24\",\"hugo-content-organization\"\n\"24\",\"google-email-migration\"\n\"24\",\"google-email-audit-api\"\n\"24\",\"genomics\"\n\"24\",\"test-more\"\n\"24\",\"testkit\"\n\"24\",\"hyde\"\n\"24\",\"terraform-provider-cloudflare\"\n\"24\",\"android-overscoll\"\n\"24\",\"prost\"\n\"24\",\"stream-compaction\"\n\"24\",\"resharper-4.5\"\n\"24\",\"latin-square\"\n\"24\",\"evernote-app-notebook\"\n\"24\",\"activitydesigner\"\n\"24\",\"lazydatamodel\"\n\"24\",\"iphone-64bit\"\n\"24\",\"speed-dial\"\n\"24\",\"membershipuser\"\n\"24\",\"androidjunitrunner\"\n\"24\",\"special-variables\"\n\"24\",\"css-to-pdf\"\n\"24\",\"odoo.sh\"\n\"24\",\"generative-programming\"\n\"24\",\"httpclienthandler\"\n\"24\",\"qgroundcontrol\"\n\"24\",\"cfsearch\"\n\"24\",\"meego-harmattan\"\n\"24\",\"get-it\"\n\"24\",\"duckling\"\n\"24\",\"iplots\"\n\"24\",\"angular-dart-routing\"\n\"24\",\"angular-data\"\n\"24\",\"zend-app-bootstrap\"\n\"24\",\"mturk\"\n\"24\",\"gnu-sort\"\n\"24\",\"auto-populating\"\n\"24\",\"gmsgroundoverlay\"\n\"24\",\"msxml4\"\n\"24\",\"beat-detection\"\n\"24\",\"automapper-8\"\n\"24\",\"forem\"\n\"24\",\"preon\"\n\"24\",\"cwac-endless\"\n\"24\",\"structure-packing\"\n\"24\",\"arrowdb\"\n\"24\",\"emitter\"\n\"24\",\"enide\"\n\"24\",\"haste\"\n\"24\",\"ms-access-web-app\"\n\"24\",\"embedded-server\"\n\"24\",\"scriptdom\"\n\"24\",\"cut-and-paste\"\n\"24\",\"quadruple-precision\"\n\"24\",\"haskell-spock\"\n\"24\",\"scriptprocessor\"\n\"24\",\"parsel\"\n\"24\",\"script-src\"\n\"24\",\"qtquick3d\"\n\"24\",\"lichess\"\n\"24\",\"google-maps-advanced-marker-element\"\n\"24\",\"iinterceptor\"\n\"24\",\"flutter-timer\"\n\"24\",\"sophoslabs-intelix\"\n\"24\",\"empty-class\"\n\"24\",\"pgm-protocol\"\n\"24\",\"thunderbird-lightning\"\n\"24\",\"line-continuation\"\n\"24\",\"powermockrunner\"\n\"24\",\"stayontop\"\n\"24\",\"image-charts\"\n\"24\",\"dalex\"\n\"24\",\"iis-metabase\"\n\"24\",\"maven-embedder\"\n\"24\",\"theta360\"\n\"24\",\"arcball\"\n\"24\",\"webgl-earth\"\n\"24\",\"imagegrab\"\n\"24\",\"thawte\"\n\"24\",\"vaadin-elements\"\n\"24\",\"bcrypt.net\"\n\"24\",\"dart-native-extension\"\n\"24\",\"toothpick-di\"\n\"24\",\"bgtaskscheduler\"\n\"24\",\"component-based\"\n\"24\",\"toolz\"\n\"24\",\"mediaprojection\"\n\"24\",\"utplsql\"\n\"24\",\"heavy-computation\"\n\"24\",\"std-source-location\"\n\"24\",\"fmpp\"\n\"24\",\"lime-haxe\"\n\"24\",\"user-guide\"\n\"23\",\"ssis-data-types\"\n\"23\",\"anthill\"\n\"23\",\"mwe\"\n\"23\",\"gridx\"\n\"23\",\"bacnet4j\"\n\"23\",\"xsl-variable\"\n\"23\",\"websphere-traditional\"\n\"23\",\"trello-powerup\"\n\"23\",\"feast\"\n\"23\",\"fftpack\"\n\"23\",\"tensorflow-model-analysis\"\n\"23\",\"photoshop-cs5\"\n\"23\",\"groupprincipal\"\n\"23\",\"declaration-files\"\n\"23\",\"git-diff-tree\"\n\"23\",\"term-query\"\n\"23\",\"clarity-lang\"\n\"23\",\"dbus-python\"\n\"23\",\"babeltrace\"\n\"23\",\"bash-on-windows\"\n\"23\",\"primeicons\"\n\"23\",\"yiic\"\n\"23\",\"jeet-grid\"\n\"23\",\"fbwebdialogs\"\n\"23\",\"github-archive\"\n\"23\",\"groupdocs\"\n\"23\",\"phirehose\"\n\"23\",\"phantomcss\"\n\"23\",\"graphics3d\"\n\"23\",\"vue-cli-5\"\n\"23\",\"figcaption\"\n\"23\",\"tensorrt-python\"\n\"23\",\"yii2-active-records\"\n\"23\",\"yii2-api\"\n\"23\",\"tensorly\"\n\"23\",\"fedora-26\"\n\"23\",\"list-separator\"\n\"23\",\"apache-camel-mail\"\n\"23\",\"eclipse-iot\"\n\"23\",\"fluentscheduler\"\n\"23\",\"truss\"\n\"23\",\"defaulted-functions\"\n\"23\",\"react-native-splash-screen\"\n\"23\",\"apache-arrow-flight\"\n\"23\",\"remote-login\"\n\"23\",\"react-native-turbomodule\"\n\"23\",\"relaxng-compact\"\n\"23\",\"phpfastcache\"\n\"23\",\"wdio-v4\"\n\"23\",\"flowpane\"\n\"23\",\"ansi-common-lisp\"\n\"23\",\"cloudkit-environments\"\n\"23\",\"mashery\"\n\"23\",\"staf\"\n\"23\",\"sitefinity-feather\"\n\"23\",\"multiparty\"\n\"23\",\"intellij-idea2017\"\n\"23\",\"defaulttreemodel\"\n\"23\",\"eclipse-templates\"\n\"23\",\"git-cherry\"\n\"23\",\"mxbean\"\n\"23\",\"webpartpage\"\n\"23\",\"apache-dolphinscheduler\"\n\"23\",\"tsserver\"\n\"23\",\"web-to-winforms\"\n\"23\",\"anzograph\"\n\"23\",\"clj-time\"\n\"23\",\"yandex-mapkit\"\n\"23\",\"tsr\"\n\"23\",\"barracuda\"\n\"23\",\"jflap\"\n\"23\",\"aegis\"\n\"23\",\"xmlstreamwriter\"\n\"23\",\"nhibernate-caches\"\n\"23\",\"physical-design\"\n\"23\",\"xmlsec1\"\n\"23\",\"xmlseclibs\"\n\"23\",\"adtf\"\n\"23\",\"python-crfsuite\"\n\"23\",\"vod\"\n\"23\",\"xnamespace\"\n\"23\",\"ngtemplateoutlet\"\n\"23\",\"snmpsharpnet\"\n\"23\",\"kubernetesexecutor\"\n\"23\",\"cbc-mac\"\n\"23\",\"xor-linkedlist\"\n\"23\",\"pkcs\"\n\"23\",\"pythonocc\"\n\"23\",\"python-fractions\"\n\"23\",\"russian-doll-caching\"\n\"23\",\"jsr250\"\n\"23\",\"sneakers\"\n\"23\",\"rxtest\"\n\"23\",\"ad-hoc-network\"\n\"23\",\"django-crontab\"\n\"23\",\"symbolic-references\"\n\"23\",\"distributed-algorithm\"\n\"23\",\"rustfmt\"\n\"23\",\"fillable\"\n\"23\",\"adobe-pdfservices\"\n\"23\",\"flatfiledestination\"\n\"23\",\"flask-principal\"\n\"23\",\"ungetc\"\n\"23\",\"apk-signing\"\n\"23\",\"apigen\"\n\"23\",\"flex4.7\"\n\"23\",\"api-documentation\"\n\"23\",\"bingbot\"\n\"23\",\"adobe-scout\"\n\"23\",\"pagedjs\"\n\"23\",\"padr\"\n\"23\",\"padleft\"\n\"23\",\"x-http-method-override\"\n\"23\",\"ump\"\n\"23\",\"dbd-pg\"\n\"23\",\"apex-data-loader\"\n\"23\",\"umbraco-mvc\"\n\"23\",\"manhattan\"\n\"23\",\"flanneld\"\n\"23\",\"fixed-size-types\"\n\"23\",\"datetime-select\"\n\"23\",\"packagecompiler.jl\"\n\"23\",\"adobe-dtm\"\n\"23\",\"apache-karaf-feature\"\n\"23\",\"sembast\"\n\"23\",\"datetime-local\"\n\"23\",\"jsl\"\n\"23\",\"dbdeploy\"\n\"23\",\"laplacianofgaussian\"\n\"23\",\"immediate-mode\"\n\"23\",\"circleci-orb\"\n\"23\",\"json5\"\n\"23\",\"lablgtk\"\n\"23\",\"c-cda\"\n\"23\",\"jsonapi-serialize\"\n\"23\",\"pig-udf\"\n\"23\",\"makecode\"\n\"23\",\"safe-browsing-api\"\n\"23\",\"mailslot\"\n\"23\",\"mainscreen\"\n\"23\",\"send-port\"\n\"23\",\"mainactor\"\n\"23\",\"pstack\"\n\"23\",\"animatetransform\"\n\"23\",\"rqt\"\n\"23\",\"kenlm\"\n\"23\",\"rpostgres\"\n\"23\",\"vaadin-valo-theme\"\n\"23\",\"watson-text-to-speech\"\n\"23\",\"wbxml\"\n\"23\",\"carbon-fields\"\n\"23\",\"rolling-updates\"\n\"23\",\"aleph\"\n\"23\",\"mysqlcommand\"\n\"23\",\"django-project-architect\"\n\"23\",\"vapix\"\n\"23\",\"unstructured-data\"\n\"23\",\"documentpaginator\"\n\"23\",\"keyedcollection\"\n\"23\",\"i18n-js\"\n\"23\",\"documentsdirectory\"\n\"23\",\"service-application\"\n\"23\",\"django-database-functions\"\n\"23\",\"django-drf-renderer\"\n\"23\",\"ruffle\"\n\"23\",\"windows-media-services\"\n\"23\",\"upc\"\n\"23\",\"data-governance\"\n\"23\",\"hypotenuse\"\n\"23\",\"icomoon\"\n\"23\",\"icns\"\n\"23\",\"vectorwise\"\n\"23\",\"rtts\"\n\"23\",\"akka-camel\"\n\"23\",\"windows-key\"\n\"23\",\"grails-constraints\"\n\"23\",\"rtlcss\"\n\"23\",\"shared-cache\"\n\"23\",\"database-scripts\"\n\"23\",\"simple-mvvm\"\n\"23\",\"dockerfile-maven-plugin\"\n\"23\",\"ical-dotnet\"\n\"23\",\"optaweb-employee-rostering\"\n\"23\",\"unity-ecs\"\n\"23\",\"docker-ee\"\n\"23\",\"django-hosts\"\n\"23\",\"django-geoposition\"\n\"23\",\"row-key\"\n\"23\",\"native-android\"\n\"23\",\"cruisecontrol.rb\"\n\"23\",\"angularjs-timeout\"\n\"23\",\"creole\"\n\"23\",\"rnmapbox-maps\"\n\"23\",\"sipdroid\"\n\"23\",\"canutils\"\n\"23\",\"metering\"\n\"23\",\"jimfs\"\n\"23\",\"ora-24247\"\n\"23\",\"routing-slip\"\n\"23\",\"oracle-streams\"\n\"23\",\"criteo\"\n\"23\",\"data-containers\"\n\"23\",\"keyset-pagination\"\n\"23\",\"metaweblog\"\n\"23\",\"dllplugin\"\n\"23\",\"angular-ng\"\n\"23\",\"j-interop\"\n\"23\",\"vcg\"\n\"23\",\"aws-identitypools\"\n\"23\",\"cronet\"\n\"23\",\"nconf\"\n\"23\",\"azure-file-copy\"\n\"23\",\"internal-representation\"\n\"23\",\"tx\"\n\"23\",\"delphi.net\"\n\"23\",\"save-dialog\"\n\"23\",\"bootstrap-notify\"\n\"23\",\"bootstrap-duallistbox\"\n\"23\",\"spring-cloud-circuitbreaker\"\n\"23\",\"internal-app-sharing\"\n\"23\",\"jquery-mobile-pageshow\"\n\"23\",\"peano-numbers\"\n\"23\",\"bonobo-etl\"\n\"23\",\"delay-load\"\n\"23\",\"nested-stack\"\n\"23\",\"gstreamer-0.10\"\n\"23\",\"wrangle\"\n\"23\",\"apxs2\"\n\"23\",\"samsung-browser\"\n\"23\",\"wsapi\"\n\"23\",\"postgres-plpython\"\n\"23\",\"nsshadow\"\n\"23\",\"initwithcontentsofurl\"\n\"23\",\"kadira\"\n\"23\",\"gtd\"\n\"23\",\"ws4py\"\n\"23\",\"bluetoothlescanner\"\n\"23\",\"kappa\"\n\"23\",\"nested-groups\"\n\"23\",\"nested-types\"\n\"23\",\"kanji\"\n\"23\",\"twitter-flight\"\n\"23\",\"similarity-search\"\n\"23\",\"nbitcoin\"\n\"23\",\"k8s-cluster-role\"\n\"23\",\"bluetooth-mesh\"\n\"23\",\"spring-cloud-stream-binder-kinesis\"\n\"23\",\"twitter-button\"\n\"23\",\"workling\"\n\"23\",\"nscolorwell\"\n\"23\",\"information-architecture\"\n\"23\",\"mongodb-java-3.3.0\"\n\"23\",\"patternfly\"\n\"23\",\"bpp\"\n\"23\",\"enumerators\"\n\"23\",\"cpuset\"\n\"23\",\"wordpress-media\"\n\"23\",\"app-preview\"\n\"23\",\"spring-boot-chaos-monkey\"\n\"23\",\"application-structure\"\n\"23\",\"android-slices\"\n\"23\",\"openbox\"\n\"23\",\"hpack\"\n\"23\",\"open-esb\"\n\"23\",\"entity-framework-extended\"\n\"23\",\"gulp-usemin\"\n\"23\",\"nes\"\n\"23\",\"ionic-keyboard\"\n\"23\",\"infoview\"\n\"23\",\"jquery-ui-contextmenu\"\n\"23\",\"spring-pulsar\"\n\"23\",\"applicationsettingsbase\"\n\"23\",\"deployit\"\n\"23\",\"modern.ie\"\n\"23\",\"boomerang\"\n\"23\",\"swift6\"\n\"23\",\"spring-lemon\"\n\"23\",\"super-columns\"\n\"23\",\"swampy\"\n\"23\",\"ndc\"\n\"23\",\"ndbunit\"\n\"23\",\"nsmanagedobjectid\"\n\"23\",\"inherited-constructors\"\n\"23\",\"easyapache-4\"\n\"23\",\"navigator-ios\"\n\"23\",\"pytest-markers\"\n\"23\",\"802.11p\"\n\"23\",\"kohana-db\"\n\"23\",\"microstation\"\n\"23\",\"visreg\"\n\"23\",\"brisk\"\n\"23\",\"javaquery\"\n\"23\",\"java-heap\"\n\"23\",\"wintersmith\"\n\"23\",\"ubuntu-23.10\"\n\"23\",\"buildsrc\"\n\"23\",\"javaoptions\"\n\"23\",\"u3d\"\n\"23\",\"code-complete\"\n\"23\",\"build-management\"\n\"23\",\"network-efficiency\"\n\"23\",\"neuraxle\"\n\"23\",\"audio-device\"\n\"23\",\"range-tree\"\n\"23\",\"rakudo-star\"\n\"23\",\"aaa-security-protocol\"\n\"23\",\"vmrun\"\n\"23\",\"vizard\"\n\"23\",\"jaseci\"\n\"23\",\"foundry-functions\"\n\"23\",\"typespec\"\n\"23\",\"vk-sdk\"\n\"23\",\"typescript-never\"\n\"23\",\"richfaces-modal\"\n\"23\",\"virtus\"\n\"23\",\"nxhtml\"\n\"23\",\"objectlistview-python\"\n\"23\",\"nwb\"\n\"23\",\"windres\"\n\"23\",\"out-of-band\"\n\"23\",\".net-standard-1.4\"\n\"23\",\"liberty-maven-plugin\"\n\"23\",\"origami\"\n\"23\",\"knockback.js\"\n\"23\",\"objcmongodb\"\n\"23\",\"domoticz\"\n\"23\",\"side-channel-attacks\"\n\"23\",\"facebook-graph-api-v2.8\"\n\"23\",\"returnn\"\n\"23\",\"shadermaterial\"\n\"23\",\"audio-route\"\n\"23\",\"shippable-ci\"\n\"23\",\"syntax-rules\"\n\"23\",\"minmax-heap\"\n\"23\",\"google-chat-api\"\n\"23\",\"lucidchart\"\n\"23\",\"visual-studio-liveshare\"\n\"23\",\"sidetiq\"\n\"23\",\"pygst\"\n\"23\",\"typeorm-activerecord\"\n\"23\",\"magnification-api\"\n\"23\",\"mailaddress\"\n\"23\",\"vistime\"\n\"23\",\"bsp-tree\"\n\"23\",\"bsdmake\"\n\"23\",\"network.framework\"\n\"23\",\"gomail\"\n\"23\",\"ubersvn\"\n\"23\",\"auth0-delegated-admin\"\n\"23\",\"syslog4j\"\n\"23\",\"visual-studio-power-tools\"\n\"23\",\"tablednd\"\n\"23\",\"contextswitchdeadlock\"\n\"23\",\"asprintf\"\n\"23\",\"mockito-scala\"\n\"23\",\"mobileserviceclient\"\n\"23\",\"taglib-ruby\"\n\"23\",\"c5\"\n\"23\",\"xcode8-beta4\"\n\"23\",\"mmc3\"\n\"23\",\"android-bootstrap\"\n\"23\",\"toloka\"\n\"23\",\"ui-scroll\"\n\"23\",\"continuous-aggregates\"\n\"23\",\"asmjit\"\n\"23\",\"iui\"\n\"23\",\"quickcontactbadge\"\n\"23\",\"explicit-object-parameter\"\n\"23\",\"npmignore\"\n\"23\",\"driving-distance\"\n\"23\",\"openshift-4\"\n\"23\",\"bundletool\"\n\"23\",\"xcplayground\"\n\"23\",\"tachyons-css\"\n\"23\",\"refined\"\n\"23\",\"aspect-fit\"\n\"23\",\"azure-promptflow\"\n\"23\",\"rails.vim\"\n\"23\",\"x-content-type-options\"\n\"23\",\"xcode13.3\"\n\"23\",\"drupal-form-submission\"\n\"23\",\"expo-linking\"\n\"23\",\"radius-protocol\"\n\"23\",\"node-sqlserver\"\n\"23\",\"quilt\"\n\"23\",\"azure-webhooks\"\n\"23\",\"asp.net-mvc-helpers\"\n\"23\",\"gconf\"\n\"23\",\"asp.net-mvc-layout\"\n\"23\",\"scaleform\"\n\"23\",\"dictvectorizer\"\n\"23\",\"exfat\"\n\"23\",\"scratchbox\"\n\"23\",\"xact-abort\"\n\"23\",\"nuxt.config.js\"\n\"23\",\"target-link-libraries\"\n\"23\",\"notification-icons\"\n\"23\",\"scrapy-middleware\"\n\"23\",\"tanuki\"\n\"23\",\"mobilefirst-qa\"\n\"23\",\"wxerlang\"\n\"23\",\"sql-server-data-project\"\n\"23\",\"executefetchrequest\"\n\"23\",\"sql-server-config-manager\"\n\"23\",\"tcheckbox\"\n\"23\",\"android-cts\"\n\"23\",\"tcomb\"\n\"23\",\"azure-relay\"\n\"23\",\"vertxoptions\"\n\"23\",\"pligg\"\n\"23\",\"operadriver\"\n\"23\",\"iso-7816-4\"\n\"23\",\"model-glue\"\n\"23\",\"gatk\"\n\"23\",\"nonfactors-mvc-grid\"\n\"23\",\"expansion-tile\"\n\"23\",\"tailwind-variants\"\n\"23\",\"scala-placeholder-syntax\"\n\"23\",\"rabbitmq-management\"\n\"23\",\"dtn\"\n\"23\",\"propertybag\"\n\"23\",\"layar\"\n\"23\",\"active-users\"\n\"23\",\"angular-gettext\"\n\"23\",\"njs\"\n\"23\",\"ipu\"\n\"23\",\"geom-histogram\"\n\"23\",\"pelles-c\"\n\"23\",\"angular-eslint\"\n\"23\",\"csvtoarray\"\n\"23\",\"activation-codes\"\n\"23\",\"cfbundledocumenttypes\"\n\"23\",\"angular-carousel\"\n\"23\",\"omnipascal\"\n\"23\",\"duffs-device\"\n\"23\",\"geojsonio\"\n\"23\",\"proximityapi\"\n\"23\",\"monkey-testing\"\n\"23\",\"mosaic-decisions\"\n\"23\",\"cuda-arrays\"\n\"23\",\"acid-state\"\n\"23\",\"oma\"\n\"23\",\"eid\"\n\"23\",\"mosml\"\n\"23\",\"spintax\"\n\"23\",\"oledbparameter\"\n\"23\",\"tomcat-dbcp\"\n\"23\",\"egress\"\n\"23\",\"acrcloud\"\n\"23\",\"hygraph\"\n\"23\",\"node-pty\"\n\"23\",\"eventhub\"\n\"23\",\"react-children\"\n\"23\",\"etcdctl\"\n\"23\",\"http-status-code-408\"\n\"23\",\"lowdb\"\n\"23\",\"http-status-code-308\"\n\"23\",\"mesa-abm\"\n\"23\",\"pestphp\"\n\"23\",\"luafilesystem\"\n\"23\",\"react-gtm-module\"\n\"23\",\"stomp.py\"\n\"23\",\"responders\"\n\"23\",\"ewam\"\n\"23\",\"resemblejs\"\n\"23\",\"mendel-os\"\n\"23\",\"qrunnable\"\n\"23\",\"meekro\"\n\"23\",\"excel-2000\"\n\"23\",\"megabyte\"\n\"23\",\"huawei-location-kit\"\n\"23\",\"meltano\"\n\"23\",\"qlibrary\"\n\"23\",\"communityengine\"\n\"23\",\"genius-api\"\n\"23\",\"project-layout\"\n\"23\",\"zugferd\"\n\"23\",\"generic-associated-types\"\n\"23\",\"qpid-proton\"\n\"23\",\"ios-app-signing\"\n\"23\",\"periodic-processing\"\n\"23\",\"pfimageview\"\n\"23\",\"spock-reports\"\n\"23\",\"google-cloud-registry\"\n\"23\",\"genstage\"\n\"23\",\"oftype\"\n\"23\",\"google-cloud-launcher\"\n\"23\",\"promoted-builds\"\n\"23\",\"msodbcsql17\"\n\"23\",\"cxjs\"\n\"23\",\"touchgfx\"\n\"23\",\"auv3\"\n\"23\",\"static-compilation\"\n\"23\",\"msagl\"\n\"23\",\"paraccel\"\n\"23\",\"email-formats\"\n\"23\",\"glmulti\"\n\"23\",\"email-spec\"\n\"23\",\"web-access\"\n\"23\",\"qtwebsockets\"\n\"23\",\"idm\"\n\"23\",\"pp-perl-par-packager\"\n\"23\",\"thruway\"\n\"23\",\"measurement-studio\"\n\"23\",\"bcbsn\"\n\"23\",\"through2\"\n\"23\",\"google-spark-operator\"\n\"23\",\"glui\"\n\"23\",\"powerform\"\n\"23\",\"mdd\"\n\"23\",\"structured-references\"\n\"23\",\"pandas-timeindex\"\n\"23\",\"artificial-life\"\n\"23\",\"pandastable\"\n\"23\",\"mediastreamtrack\"\n\"23\",\"zendesk-sdk\"\n\"23\",\"heroku-connect\"\n\"23\",\"beforeinstallprompt\"\n\"23\",\"bayesian-deep-learning\"\n\"23\",\"autofield\"\n\"23\",\"powerbi-datagateway\"\n\"23\",\"scribe-workbench\"\n\"23\",\"gmsautocomplete\"\n\"23\",\"conditional-expressions\"\n\"23\",\"ashmem\"\n\"23\",\"weboperationcontext\"\n\"23\",\"sunburnt\"\n\"23\",\"space-tree\"\n\"23\",\"msdeployserviceagent\"\n\"23\",\"hex-file\"\n\"23\",\"custom-scope\"\n\"23\",\"multicol\"\n\"23\",\"qtpy\"\n\"23\",\"solar2d\"\n\"23\",\"arcadedb\"\n\"23\",\"imagehash\"\n\"23\",\"fnv\"\n\"23\",\"zero-width-space\"\n\"23\",\"compiler-generated\"\n\"23\",\"stdapply\"\n\"23\",\"utl-http\"\n\"23\",\"parameterized-constructor\"\n\"23\",\"mule-sdk\"\n\"23\",\"qsharedmemory\"\n\"23\",\"embedded-osgi\"\n\"23\",\"parcelfiledescriptor\"\n\"23\",\"parentid\"\n\"23\",\"usenet\"\n\"23\",\"qsplashscreen\"\n\"23\",\"authorize.net-webhooks\"\n\"23\",\"max-execution-timeout\"\n\"23\",\"bigcouch\"\n\"23\",\"qt5.10\"\n\"23\",\"compatibility-level\"\n\"23\",\"font-scaling\"\n\"23\",\"armasm\"\n\"23\",\"parted\"\n\"23\",\"archimate\"\n\"22\",\"stalled\"\n\"22\",\"jcryption\"\n\"22\",\"group-object\"\n\"22\",\"react-particles-js\"\n\"22\",\"inserthtml\"\n\"22\",\"stacks-blockchain\"\n\"22\",\"yoothemes\"\n\"22\",\"matter-iot-standard\"\n\"22\",\"clojars\"\n\"22\",\"react-native-image-crop-picker\"\n\"22\",\"dbisam\"\n\"22\",\"travel-time\"\n\"22\",\"webusercontrols\"\n\"22\",\"mutual-information\"\n\"22\",\"maskformatter\"\n\"22\",\"marp\"\n\"22\",\"stanford-nlp-server\"\n\"22\",\"php-gtk\"\n\"22\",\"material-design-3\"\n\"22\",\"telligent\"\n\"22\",\"filebuf\"\n\"22\",\"jbatch\"\n\"22\",\"cloudkit-sharing\"\n\"22\",\"apache-bahir\"\n\"22\",\"trello.net\"\n\"22\",\"ggtitle\"\n\"22\",\"stack-based\"\n\"22\",\"instana\"\n\"22\",\"template-metal\"\n\"22\",\"cloud66\"\n\"22\",\"install-requires\"\n\"22\",\"websync\"\n\"22\",\"fdmemtable\"\n\"22\",\"program-transformation\"\n\"22\",\"flow-diagram\"\n\"22\",\"gritter\"\n\"22\",\"lli\"\n\"22\",\"xsjs\"\n\"22\",\"multistage\"\n\"22\",\"ecommerce-sales\"\n\"22\",\"replication-factor\"\n\"22\",\"ebxml\"\n\"22\",\"github-search\"\n\"22\",\"teleport\"\n\"22\",\"classtag\"\n\"22\",\"sly\"\n\"22\",\"flutter-camera\"\n\"22\",\"release-apk\"\n\"22\",\"yellow-screen-of-death\"\n\"22\",\"ecslidingviewcontroller-2\"\n\"22\",\"live-unit-tests\"\n\"22\",\"fluent-entity-framework\"\n\"22\",\"linuxthreads\"\n\"22\",\"livecoding\"\n\"22\",\"multiple-join-rows\"\n\"22\",\"multivariate-partition\"\n\"22\",\"jetbrains-hub\"\n\"22\",\"eclipse-dtp\"\n\"22\",\"yapdatabase\"\n\"22\",\"backbone.validation.js\"\n\"22\",\"relx\"\n\"22\",\"trivially-copyable\"\n\"22\",\"eclipse-ecf\"\n\"22\",\"proficy\"\n\"22\",\"print-style\"\n\"22\",\"reposurgeon\"\n\"22\",\"ansible-container\"\n\"22\",\"sktransition\"\n\"22\",\"antd-mobile\"\n\"22\",\"pack-expansion\"\n\"22\",\"impredicativetypes\"\n\"22\",\"bioservices\"\n\"22\",\"aframe-networked\"\n\"22\",\"dispatch-table\"\n\"22\",\"kubernetes-gateway-api\"\n\"22\",\"ngit\"\n\"22\",\"piracy-protection\"\n\"22\",\"appgroups\"\n\"22\",\"umzug\"\n\"22\",\"flashair\"\n\"22\",\"xhtml-1.1\"\n\"22\",\"manticore-search\"\n\"22\",\"chrome-for-testing\"\n\"22\",\"chrome-remote-desktop\"\n\"22\",\"swiftui-transition\"\n\"22\",\"manual-retain-release\"\n\"22\",\"page-inspector\"\n\"22\",\"binding-expressions\"\n\"22\",\"swiftyuserdefaults\"\n\"22\",\"jsondecodeerror\"\n\"22\",\"swig-typemap\"\n\"22\",\"pagertitlestrip\"\n\"22\",\"symfony-guard\"\n\"22\",\"swing-app-framework\"\n\"22\",\"chuck\"\n\"22\",\"check-framework\"\n\"22\",\"unisharp-file-manager\"\n\"22\",\"freemind\"\n\"22\",\"runtime-packages\"\n\"22\",\"flatbutton\"\n\"22\",\"riverpod-annotation\"\n\"22\",\"swiz\"\n\"22\",\"incremental-linking\"\n\"22\",\"file-type-associations\"\n\"22\",\"xmlhttprequest-states\"\n\"22\",\"impact-analysis\"\n\"22\",\"img2pdf\"\n\"22\",\"ng-deep\"\n\"22\",\"symstore\"\n\"22\",\"django-anymail\"\n\"22\",\"python-gearman\"\n\"22\",\"smart-tags\"\n\"22\",\"symfony-cli\"\n\"22\",\"circusd\"\n\"22\",\"image-resolution\"\n\"22\",\"software-protection\"\n\"22\",\"apache-pinot\"\n\"22\",\"jsbarcode\"\n\"22\",\"ngx-image-cropper\"\n\"22\",\"uitextchecker\"\n\"22\",\"social-likes\"\n\"22\",\"xmltocsv\"\n\"22\",\"jsajaxfileuploader\"\n\"22\",\"js2-mode\"\n\"22\",\"fittextjs\"\n\"22\",\"g2o\"\n\"22\",\"connect-timeout\"\n\"22\",\"manage-nuget-packages\"\n\"22\",\"map-matching\"\n\"22\",\"vogels\"\n\"22\",\"ngx-socket-io\"\n\"22\",\"fixed-length-file\"\n\"22\",\"bitmovin-player\"\n\"22\",\"rust-itertools\"\n\"22\",\"selenium-jupiter\"\n\"22\",\"vs-color-theme-editor\"\n\"22\",\"cinch\"\n\"22\",\"data-uri-scheme\"\n\"22\",\"ngx-treeview\"\n\"22\",\"v-navigation-drawer\"\n\"22\",\"js-routes\"\n\"22\",\"socketpair\"\n\"22\",\"cayley\"\n\"22\",\"jpa-1.0\"\n\"22\",\"facepile\"\n\"22\",\"ruby-build\"\n\"22\",\"sessiontracking\"\n\"22\",\"cson\"\n\"22\",\"pvm\"\n\"22\",\"roda\"\n\"22\",\"ora-01858\"\n\"22\",\"joomla-community-builder\"\n\"22\",\"cron4j\"\n\"22\",\"iasyncoperation\"\n\"22\",\"vyper\"\n\"22\",\"cross-env\"\n\"22\",\"keras-cv\"\n\"22\",\"oracle12.1\"\n\"22\",\"optionsettype\"\n\"22\",\"database-create\"\n\"22\",\"vanilla-extract\"\n\"22\",\"rowexpansion\"\n\"22\",\"ajaxuploader\"\n\"22\",\"r-qgraph\"\n\"22\",\"jpa-buddy\"\n\"22\",\"narray\"\n\"22\",\"data-jpa-test\"\n\"22\",\"kendo-ui-vue\"\n\"22\",\"reaper\"\n\"22\",\"ruby-install\"\n\"22\",\"vanishing-point\"\n\"22\",\"django-rest-framework-filters\"\n\"22\",\"keyring\"\n\"22\",\"service-not-available\"\n\"22\",\"pscx\"\n\"22\",\"django-shop\"\n\"22\",\"watchapp\"\n\"22\",\"microsoft-graph-excel\"\n\"22\",\"django-prefetch-related\"\n\"22\",\"psr-12\"\n\"22\",\"jinterface\"\n\"22\",\"rule-of-five\"\n\"22\",\"microsoft-contracts\"\n\"22\",\"windowsondevices\"\n\"22\",\"microsoft-cdn\"\n\"22\",\"sequence-sql\"\n\"22\",\"algol\"\n\"22\",\"icepush\"\n\"22\",\"dlt\"\n\"22\",\"vector-space\"\n\"22\",\"unstated\"\n\"22\",\"cameraoverlayview\"\n\"22\",\"aws-iam-authenticator\"\n\"22\",\"jol\"\n\"22\",\"ahead-of-time-compile\"\n\"22\",\"micronaut-test\"\n\"22\",\"wildfly-13\"\n\"22\",\"gradle-tooling-api\"\n\"22\",\"keyboardfocusmanager\"\n\"22\",\"shapley\"\n\"22\",\"site-definition\"\n\"22\",\"jonas\"\n\"22\",\"micr\"\n\"22\",\"mysql-error-1142\"\n\"22\",\"database-programming\"\n\"22\",\"infernojs\"\n\"22\",\"nqp\"\n\"22\",\"enterprise-miner\"\n\"22\",\"gulp-compass\"\n\"22\",\"htmldecode\"\n\"22\",\"cp1250\"\n\"22\",\"nsanimation\"\n\"22\",\"ttthumbsviewcontroller\"\n\"22\",\"onestepcheckout\"\n\"22\",\"deployment-diagram\"\n\"22\",\"k8ssandra\"\n\"22\",\"sigprocmask\"\n\"22\",\"hotpatching\"\n\"22\",\"tup\"\n\"22\",\"entityobject\"\n\"22\",\"writeonly\"\n\"22\",\"swift-array\"\n\"22\",\"destructure\"\n\"22\",\"hot-rod\"\n\"22\",\"simhash\"\n\"22\",\"svn-trunk\"\n\"22\",\"crawlee\"\n\"22\",\"monadfix\"\n\"22\",\"simplebar\"\n\"22\",\"twaindotnet\"\n\"22\",\"swift-keyboard\"\n\"22\",\"swashbuckle.examples\"\n\"22\",\"boost-system\"\n\"22\",\"saspy\"\n\"22\",\"blazorinputfile\"\n\"22\",\"corecursion\"\n\"22\",\"boost.test\"\n\"22\",\"julia-pkg\"\n\"22\",\"sat4j\"\n\"22\",\"epiceditor\"\n\"22\",\"equational-reasoning\"\n\"22\",\"nsis-mui\"\n\"22\",\"jquery-mobile-select\"\n\"22\",\"epicor10\"\n\"22\",\"jquery-mobile-themeroller\"\n\"22\",\"popper\"\n\"22\",\"delivery-pipeline\"\n\"22\",\"moloquent\"\n\"22\",\"html-to-image\"\n\"22\",\"open-atrium\"\n\"22\",\"grunt-plugins\"\n\"22\",\"pbm\"\n\"22\",\"open-banking\"\n\"22\",\"ionic-devapp\"\n\"22\",\"wsrp\"\n\"22\",\"jquery-1.10\"\n\"22\",\"samsung-gear-s2\"\n\"22\",\"postgres15\"\n\"22\",\"svelte-5\"\n\"22\",\"nested-controls\"\n\"22\",\"in-memory-cache\"\n\"22\",\"wso2-es\"\n\"22\",\"postgis-raster\"\n\"22\",\"spring-batch-integration\"\n\"22\",\"bluetooth-profile\"\n\"22\",\"appliance\"\n\"22\",\"dynamic-invoke\"\n\"22\",\"application-end\"\n\"22\",\"opal\"\n\"22\",\"html-react-parser\"\n\"22\",\"internet-options\"\n\"22\",\"pdk\"\n\"22\",\"apply-visitor\"\n\"22\",\"block-storage\"\n\"22\",\"invoice-ninja\"\n\"22\",\"htmlextensions\"\n\"22\",\"karma-chrome-launcher\"\n\"22\",\"iorderedenumerable\"\n\"22\",\"p-dropdown\"\n\"22\",\"modified-preorder-tree-t\"\n\"22\",\"internet-computer\"\n\"22\",\"ion-slide-box\"\n\"22\",\"opencv3.3\"\n\"22\",\"opendiff\"\n\"22\",\"kahadb\"\n\"22\",\"spring-auto-restdocs\"\n\"22\",\"ion-toggle\"\n\"22\",\"net.pipe\"\n\"22\",\"dynamic-rebinding\"\n\"22\",\"rbindlist\"\n\"22\",\"outlook.application\"\n\"22\",\"ucrop\"\n\"22\",\"async-workflow\"\n\"22\",\"externalizing\"\n\"22\",\"abstractmethoderror\"\n\"22\",\"37-signals\"\n\"22\",\"codefixprovider\"\n\"22\",\"udp-data-transfer\"\n\"22\",\"lwuit-textfield\"\n\"22\",\"microsoft-partner-center\"\n\"22\",\"rise\"\n\"22\",\"minimum-requirements\"\n\"22\",\"pyrus\"\n\"22\",\"output-directory\"\n\"22\",\"observation\"\n\"22\",\"rewire\"\n\"22\",\"facebook-java-sdk\"\n\"22\",\"libsoup\"\n\"22\",\"range-types\"\n\"22\",\"leave-one-out\"\n\"22\",\"view-transitions-api\"\n\"22\",\"pydruid\"\n\"22\",\"library-interposition\"\n\"22\",\"pyspin\"\n\"22\",\"wkhtmltopdf-binary\"\n\"22\",\"goldbach-conjecture\"\n\"22\",\"orbitdb\"\n\"22\",\"ubiquity\"\n\"22\",\"ameritrade\"\n\"22\",\"amdp\"\n\"22\",\"cognex\"\n\"22\",\"system-profiler\"\n\"22\",\"rdebug\"\n\"22\",\"rapidsvn\"\n\"22\",\"amd-gcn\"\n\"22\",\"jasmin-sms\"\n\"22\",\"java-deployment-toolkit\"\n\"22\",\"wookmark\"\n\"22\",\"octicons\"\n\"22\",\"setwindowpos\"\n\"22\",\"setwindowlong\"\n\"22\",\"atomic-swap\"\n\"22\",\"knockout-binding-handlers\"\n\"22\",\"netbeans-13\"\n\"22\",\"bridj\"\n\"22\",\"javascriptexecutor\"\n\"22\",\"tabledit\"\n\"22\",\"bteq\"\n\"22\",\"system.web.routing\"\n\"22\",\"oauth2resourceserver\"\n\"22\",\"type-mapping\"\n\"22\",\"neupy\"\n\"22\",\"browser-width\"\n\"22\",\"audiobufferlist\"\n\"22\",\"systimestamp\"\n\"22\",\"type-resolution\"\n\"22\",\"rapier\"\n\"22\",\"leptos\"\n\"22\",\"leon\"\n\"22\",\"freeboard\"\n\"22\",\"reverse-debugging\"\n\"22\",\"osmand\"\n\"22\",\"mailman-gem\"\n\"22\",\"mimemultipart\"\n\"22\",\"extranet\"\n\"22\",\"asx\"\n\"22\",\"vmargs\"\n\"22\",\"wmctrl\"\n\"22\",\"freak\"\n\"22\",\"quokka.js\"\n\"22\",\"qvector3d\"\n\"22\",\"convexity-defects\"\n\"22\",\"c++26\"\n\"22\",\"rails-authorization\"\n\"22\",\"nuxt-bridge\"\n\"22\",\"byte-code-enhancement\"\n\"22\",\"dtruss\"\n\"22\",\"dreamweaver-templates\"\n\"22\",\"xcode6.1.1\"\n\"22\",\"cactiverecord\"\n\"22\",\"dropify\"\n\"22\",\"device-instance-id\"\n\"22\",\"cordova-4\"\n\"22\",\"dilation\"\n\"22\",\"gamequery\"\n\"22\",\"hama\"\n\"22\",\"rabbitvcs\"\n\"22\",\"toast-ui-editor\"\n\"22\",\"cordova-sqlite-storage\"\n\"22\",\"toast-ui-image-editor\"\n\"22\",\"cordova-plugin-firebasex\"\n\"22\",\"mload\"\n\"22\",\"cordova-plugin-advanced-http\"\n\"22\",\"dsx\"\n\"22\",\"iso-image\"\n\"22\",\"nuance\"\n\"22\",\"jack-compiler\"\n\"22\",\"drupal-contact-form\"\n\"22\",\"sql-data-tools\"\n\"22\",\"drupal-database\"\n\"22\",\"cabal-new\"\n\"22\",\"redux-form-validators\"\n\"22\",\"numeric-ranges\"\n\"22\",\"radiance-flamingo\"\n\"22\",\"iwyu\"\n\"22\",\"mlton\"\n\"22\",\"openvpn-connect\"\n\"22\",\"devexpress-blazor\"\n\"22\",\"scalatest-maven-plugin\"\n\"22\",\"histogram-equalization\"\n\"22\",\"sql-manager\"\n\"22\",\"redux-api-middleware\"\n\"22\",\"android-enterprise-features\"\n\"22\",\"xcuielement\"\n\"22\",\"azure-static-web-app-routing\"\n\"22\",\"aspnet-development-server\"\n\"22\",\"drupal-views-relationship\"\n\"22\",\"scopeguard\"\n\"22\",\"conversational-ai\"\n\"22\",\"registerclass\"\n\"22\",\"contextual-binding\"\n\"22\",\"tdom\"\n\"22\",\"openshift-php-cartidges\"\n\"22\",\"bullet-chart\"\n\"22\",\"tinkerpop-frames\"\n\"22\",\"rails-spring\"\n\"22\",\"tinyalsa\"\n\"22\",\"azure-sql-reporting\"\n\"22\",\"pnrp\"\n\"22\",\"harvard-architecture\"\n\"22\",\"expo-permissions\"\n\"22\",\"plistbuddy\"\n\"22\",\"tabris-js\"\n\"22\",\"xcode-debugger\"\n\"22\",\"table-per-subclass\"\n\"22\",\"timed-events\"\n\"22\",\"dronekit-android\"\n\"22\",\"mockrestserviceserver\"\n\"22\",\"controlleras\"\n\"22\",\"qinputdialog\"\n\"22\",\"http-request2\"\n\"22\",\"angular-http-auth\"\n\"22\",\"http-pipelining\"\n\"22\",\"irfanview\"\n\"22\",\"moonmail\"\n\"22\",\"generate-scripts\"\n\"22\",\"odfpy\"\n\"22\",\"mono-service\"\n\"22\",\"angularjs-1.7\"\n\"22\",\"irp\"\n\"22\",\"angular2-seed\"\n\"22\",\"laravel-schema-builder\"\n\"22\",\"node-native-addon\"\n\"22\",\"genero\"\n\"22\",\"qftp\"\n\"22\",\"compare-contrast\"\n\"22\",\"ondoubleclick\"\n\"22\",\"qfontmetrics\"\n\"22\",\"qdoublespinbox\"\n\"22\",\"oeis\"\n\"22\",\"qdial\"\n\"22\",\"reactify\"\n\"22\",\"qcodo\"\n\"22\",\"electron-rebuild\"\n\"22\",\"ninject-conventions\"\n\"22\",\"reactive-cocoa-5\"\n\"22\",\"qbuttongroup\"\n\"22\",\"genicam\"\n\"22\",\"commitlint\"\n\"22\",\"ol3-google-maps\"\n\"22\",\"testserver\"\n\"22\",\"okular\"\n\"22\",\"elasticsearch-sql\"\n\"22\",\"terraform-enterprise\"\n\"22\",\"geoapi\"\n\"22\",\"og-meta\"\n\"22\",\"ios-statusbar\"\n\"22\",\"android-print-manager\"\n\"22\",\"android-kenburnsview\"\n\"22\",\"pettingzoo\"\n\"22\",\"storyshots\"\n\"22\",\"evenly\"\n\"22\",\"google-cloud-intellij\"\n\"22\",\"storm-orm\"\n\"22\",\"strict-transport-security\"\n\"22\",\"curl-commandline\"\n\"22\",\"persistence-ignorance\"\n\"22\",\"spree-paypal-express\"\n\"22\",\"cequel\"\n\"22\",\"zotonic\"\n\"22\",\"perl5.10\"\n\"22\",\"iphelper\"\n\"22\",\"cert\"\n\"22\",\"memgraph\"\n\"22\",\"spin-rdf\"\n\"22\",\"cucumber-spring\"\n\"22\",\"actuator\"\n\"22\",\"iphone-sdk-3.1.3\"\n\"22\",\"google-filament\"\n\"22\",\"lpwstr\"\n\"22\",\"speech-recognition-api\"\n\"22\",\"lpstr\"\n\"22\",\"chartfx\"\n\"22\",\"iphone-xs-max\"\n\"22\",\"spawn-fcgi\"\n\"22\",\"cgi-application\"\n\"22\",\"chart.jsv3\"\n\"22\",\"provider-model\"\n\"22\",\"iprogress\"\n\"22\",\"prolog-directive-dynamic\"\n\"22\",\"euler-path\"\n\"22\",\"eto\"\n\"22\",\"iraf\"\n\"22\",\"action-hook\"\n\"22\",\"protobuf-3\"\n\"22\",\"activation-record\"\n\"22\",\"font-awesome-6\"\n\"22\",\"concrete-inheritance\"\n\"22\",\"parsey-mcparseface\"\n\"22\",\"prefix-notation\"\n\"22\",\"thinkphp\"\n\"22\",\"utest\"\n\"22\",\"sdiff\"\n\"22\",\"line-through\"\n\"22\",\"maven-dependency-check-plugin\"\n\"22\",\"ember-engines\"\n\"22\",\"cypress-testing-library\"\n\"22\",\"google-routes-api\"\n\"22\",\"usocket\"\n\"22\",\"iloggerfactory\"\n\"22\",\"als\"\n\"22\",\"scribunto\"\n\"22\",\"trackpy\"\n\"22\",\"solidcolorbrush\"\n\"22\",\"conditional-execution\"\n\"22\",\"autogpt\"\n\"22\",\"lightgraphs\"\n\"22\",\"tfs-query\"\n\"22\",\"cypress-configuration\"\n\"22\",\"lifx\"\n\"22\",\"hdl-coder\"\n\"22\",\"spark-ec2\"\n\"22\",\"spark-connect\"\n\"22\",\"tfignore\"\n\"22\",\"arduino-mkr1000\"\n\"22\",\"auto-lock\"\n\"22\",\"ardalis-specification\"\n\"22\",\"custom-scheme-url\"\n\"22\",\"style-dictionary\"\n\"22\",\"encrypted-core-data-sql\"\n\"22\",\"webpack-externals\"\n\"22\",\"cypress-each\"\n\"22\",\"suo\"\n\"22\",\"parse4j\"\n\"22\",\"linktable\"\n\"22\",\"partial-response\"\n\"22\",\"mcimagemanager\"\n\"22\",\"zii-widgets\"\n\"22\",\"sonar-gerrit\"\n\"22\",\"utl-mail\"\n\"22\",\"somee\"\n\"22\",\"igor\"\n\"22\",\"email-signature\"\n\"22\",\"zikula\"\n\"22\",\"prerenderview\"\n\"22\",\"maven-replacer-plugin\"\n\"22\",\"qt-design-studio\"\n\"22\",\"google-patent-search\"\n\"22\",\"flvplayer\"\n\"22\",\"partiallinktext\"\n\"22\",\"image-graphviz\"\n\"22\",\"limited-user\"\n\"22\",\"limit-per-group\"\n\"22\",\"beanstalk-svn\"\n\"22\",\"automic\"\n\"22\",\"arithmetic-overflow\"\n\"22\",\"flutter-swiper\"\n\"22\",\"traccar\"\n\"22\",\"tortoisehg-2.0\"\n\"22\",\"zipwith\"\n\"22\",\"arb\"\n\"22\",\"binance-chain\"\n\"22\",\"gitstack\"\n\"22\",\"mtj\"\n\"22\",\"glumpy\"\n\"22\",\"google-maps-android-api-3\"\n\"22\",\"identicon\"\n\"22\",\"pandastream\"\n\"22\",\"cyrus\"\n\"22\",\"preact-signal\"\n\"22\",\"maven-gpg-plugin\"\n\"22\",\"mediastreamsegmenter\"\n\"21\",\"multiple-indirection\"\n\"21\",\"jbox\"\n\"21\",\"staging-table\"\n\"21\",\"react-slate\"\n\"21\",\"cloud-platform\"\n\"21\",\"client-dependency\"\n\"21\",\"skyve\"\n\"21\",\"maruku\"\n\"21\",\"multiple-occurrence\"\n\"21\",\"clientscriptmanager\"\n\"21\",\"teamcenter\"\n\"21\",\"teamcity-10\"\n\"21\",\"multipath\"\n\"21\",\"teamcity-5.1\"\n\"21\",\"clientcache\"\n\"21\",\"react-native-fbsdk-next\"\n\"21\",\"class-helpers\"\n\"21\",\"client-applications\"\n\"21\",\"xtrf\"\n\"21\",\"flutteramplify\"\n\"21\",\"deepnote\"\n\"21\",\"sli\"\n\"21\",\"slicehost\"\n\"21\",\"vsomeip\"\n\"21\",\"fedcm\"\n\"21\",\"cloudflare-argo\"\n\"21\",\"php-ide\"\n\"21\",\"click-framework\"\n\"21\",\"ddrmenu\"\n\"21\",\"react-native-state\"\n\"21\",\"weceem\"\n\"21\",\"intel-python\"\n\"21\",\"ef-bulkinsert\"\n\"21\",\"product-management\"\n\"21\",\"bandit\"\n\"21\",\"cloth-simulation\"\n\"21\",\"edgeengine\"\n\"21\",\"tensorflow-ssd\"\n\"21\",\"skip-take\"\n\"21\",\"instapaper\"\n\"21\",\"report-studio\"\n\"21\",\"primepush\"\n\"21\",\"fcmp\"\n\"21\",\"groupname\"\n\"21\",\"graph-modelling-language\"\n\"21\",\"graphqlclient\"\n\"21\",\"yii-rest\"\n\"21\",\"fieldeditor\"\n\"21\",\"jdepend\"\n\"21\",\"yii-xupload\"\n\"21\",\"sklightnode\"\n\"21\",\"deck.js\"\n\"21\",\"photoshop-cs4\"\n\"21\",\"square-flow\"\n\"21\",\"react-paginate\"\n\"21\",\"webshot\"\n\"21\",\"photutils\"\n\"21\",\"jenkins-pipeline-unit\"\n\"21\",\"yii2-grid\"\n\"21\",\"installshield-2014\"\n\"21\",\"gringo\"\n\"21\",\"whois-ruby\"\n\"21\",\"principalsearcher\"\n\"21\",\"matlab-java\"\n\"21\",\"github-token\"\n\"21\",\"ycbcr\"\n\"21\",\"localityofreference\"\n\"21\",\"repoze.who\"\n\"21\",\"skeleton-ui\"\n\"21\",\"github-actions-artifacts\"\n\"21\",\"musixmatch\"\n\"21\",\"git-branch-sculpting\"\n\"21\",\"affiliates\"\n\"21\",\"data-studio-custom-visuals\"\n\"21\",\"dirty-checking\"\n\"21\",\"xpdo\"\n\"21\",\"app-hub\"\n\"21\",\"pace.js\"\n\"21\",\"bit-cast\"\n\"21\",\"bit64\"\n\"21\",\"bindonce\"\n\"21\",\"pahocpp\"\n\"21\",\"palm-api\"\n\"21\",\"pandarallel\"\n\"21\",\"unified-diff\"\n\"21\",\"unescapestring\"\n\"21\",\"underscore-java\"\n\"21\",\"selectmanymenu\"\n\"21\",\"umdh\"\n\"21\",\"umbrella\"\n\"21\",\"sencha-cmd5\"\n\"21\",\"sendgrid-ruby\"\n\"21\",\"mapzen\"\n\"21\",\"s3-rewrite-rules\"\n\"21\",\"void-t\"\n\"21\",\"rxjs-lettable-operators\"\n\"21\",\"social-gaming\"\n\"21\",\"vpc-peering\"\n\"21\",\"sobipro\"\n\"21\",\"vsewss\"\n\"21\",\"rust-compiler-plugin\"\n\"21\",\"catalystserverless\"\n\"21\",\"ngx-build-plus\"\n\"21\",\"ng-modal\"\n\"21\",\"ccscrolllayer\"\n\"21\",\"cddvd\"\n\"21\",\"cdi-unit\"\n\"21\",\"nexus-6p\"\n\"21\",\"flask-cli\"\n\"21\",\"flash-v3-components\"\n\"21\",\"find-util\"\n\"21\",\"flash-8\"\n\"21\",\"smjobbless\"\n\"21\",\"jsnetworkx\"\n\"21\",\"jsnlog\"\n\"21\",\"chronicle-bytes\"\n\"21\",\"smart-http\"\n\"21\",\"pkzip\"\n\"21\",\"playcanvas\"\n\"21\",\"chef-template\"\n\"21\",\"chatscript\"\n\"21\",\"python-jose\"\n\"21\",\"swizzle\"\n\"21\",\"diskpart\"\n\"21\",\"distributed-tensorflow\"\n\"21\",\"ovi\"\n\"21\",\"distributed-apps\"\n\"21\",\"python-arango\"\n\"21\",\"index-sequence\"\n\"21\",\"blackberry-widgets\"\n\"21\",\"xlsx-populate\"\n\"21\",\"swiftui-datepicker\"\n\"21\",\"blackfin\"\n\"21\",\"laravel-guard\"\n\"21\",\"kysely\"\n\"21\",\"xml-encryption\"\n\"21\",\"xmemcached\"\n\"21\",\"ims-db\"\n\"21\",\"image-text\"\n\"21\",\"imebra\"\n\"21\",\"imovie\"\n\"21\",\"fsxaml\"\n\"21\",\"python-tenacity\"\n\"21\",\"python-siphon\"\n\"21\",\"python-schedule\"\n\"21\",\"lab-management\"\n\"21\",\"rpl\"\n\"21\",\"service-control-manager\"\n\"21\",\"watson-openscale\"\n\"21\",\"angularjs-ui-utils\"\n\"21\",\"ice-protocol\"\n\"21\",\"ibm-wdt\"\n\"21\",\"ibm-iam\"\n\"21\",\"cryptico\"\n\"21\",\"rockmongo\"\n\"21\",\"record-rules\"\n\"21\",\"windows-arm64\"\n\"21\",\"credible-interval\"\n\"21\",\"serviceinsight\"\n\"21\",\"document-versioning\"\n\"21\",\"windows-media-server\"\n\"21\",\"watchos-simulator\"\n\"21\",\"pushpad\"\n\"21\",\"dashlet\"\n\"21\",\"avs\"\n\"21\",\"service-name\"\n\"21\",\"pycallgraph\"\n\"21\",\"jobeet\"\n\"21\",\"sequential-workflow\"\n\"21\",\"readyroll\"\n\"21\",\"mysql-error-2006\"\n\"21\",\"gpa\"\n\"21\",\"gperf\"\n\"21\",\"keyczar\"\n\"21\",\"keydb\"\n\"21\",\"angular-workspace-configuration\"\n\"21\",\"mysql-logic\"\n\"21\",\"unsafe-inline\"\n\"21\",\"jparsec\"\n\"21\",\"wcf-sessions\"\n\"21\",\"mysql-routines\"\n\"21\",\"unordered-multiset\"\n\"21\",\"unordered-multimap\"\n\"21\",\"dockable\"\n\"21\",\"keyword-extraction\"\n\"21\",\"simplecursortreeadapter\"\n\"21\",\"docker-cmd\"\n\"21\",\"microsoft365r\"\n\"21\",\"rsocket-js\"\n\"21\",\"nacos\"\n\"21\",\"oracleexception\"\n\"21\",\"angular-transfer-state\"\n\"21\",\"jfuzzylogic\"\n\"21\",\"aws-datastore\"\n\"21\",\"nailgun\"\n\"21\",\"universal-storyboard\"\n\"21\",\"react-syntax-highlighter\"\n\"21\",\"microblogging\"\n\"21\",\"operationqueue\"\n\"21\",\"aws-global-accelerator\"\n\"21\",\"name-matching\"\n\"21\",\"jprobe\"\n\"21\",\"fastexport\"\n\"21\",\"optimathsat\"\n\"21\",\"fastfile\"\n\"21\",\"docsplit\"\n\"21\",\"oracle11gr1\"\n\"21\",\"routing-controllers\"\n\"21\",\"nanoid\"\n\"21\",\"jpegtran\"\n\"21\",\"ajaxhelper\"\n\"21\",\"pstree\"\n\"21\",\"dmo\"\n\"21\",\"calloutview\"\n\"21\",\"jpacontainer\"\n\"21\",\"realproxy\"\n\"21\",\"realstudio\"\n\"21\",\"psyco\"\n\"21\",\"pth\"\n\"21\",\"callr\"\n\"21\",\"django-sekizai\"\n\"21\",\"call-user-func-array\"\n\"21\",\"war-filedeployment\"\n\"21\",\"django-scheduler\"\n\"21\",\"kendo-observable\"\n\"21\",\"upcase\"\n\"21\",\"validationexception\"\n\"21\",\"mysqldumpslow\"\n\"21\",\"justboil.me\"\n\"21\",\"pcloud\"\n\"21\",\"neomutt\"\n\"21\",\"supabase-realtime\"\n\"21\",\"saturn-framework\"\n\"21\",\"pcdata\"\n\"21\",\"bootstrap-themes\"\n\"21\",\"android-studio-3.6.1\"\n\"21\",\"salesforce-mobile-sdk\"\n\"21\",\"app-shortcut\"\n\"21\",\"border-container\"\n\"21\",\"applehealth\"\n\"21\",\"ionic-webview\"\n\"21\",\"moltenvk\"\n\"21\",\"oov\"\n\"21\",\"blockcypher\"\n\"21\",\"appservice\"\n\"21\",\"hp-performance-center\"\n\"21\",\"openargs\"\n\"21\",\"sve\"\n\"21\",\"swar\"\n\"21\",\"eoserror\"\n\"21\",\"dynamic-mapping\"\n\"21\",\"crc64\"\n\"21\",\"envsubst\"\n\"21\",\"deno-deploy\"\n\"21\",\"ttcn\"\n\"21\",\"houndify\"\n\"21\",\"password-prompt\"\n\"21\",\"couchdbkit\"\n\"21\",\"svg-animationelements\"\n\"21\",\"gulp-mocha\"\n\"21\",\"ion-grid\"\n\"21\",\"path-provider\"\n\"21\",\"powerapps-component-framework\"\n\"21\",\"workato\"\n\"21\",\"jreddit\"\n\"21\",\"negroni\"\n\"21\",\"entitymanagerfactory\"\n\"21\",\"apply-async\"\n\"21\",\"svelte-native\"\n\"21\",\"supersocket.net\"\n\"21\",\"path-combine\"\n\"21\",\"navigationbaritems\"\n\"21\",\"wwsapi\"\n\"21\",\"boost-json\"\n\"21\",\"nrf-connect\"\n\"21\",\"mongodb-authentication\"\n\"21\",\"online-machine-learning\"\n\"21\",\"wsdl2objc\"\n\"21\",\"dynamic-chart-series\"\n\"21\",\"dynamic-expression\"\n\"21\",\"android-webview-javascript\"\n\"21\",\"interwoven\"\n\"21\",\"onready\"\n\"21\",\"applescript-numbers\"\n\"21\",\"bless\"\n\"21\",\"sap-enterprise-portal\"\n\"21\",\"erlang-driver\"\n\"21\",\"supabase-py\"\n\"21\",\"gstreamer-sharp\"\n\"21\",\"k8s-cronjobber\"\n\"21\",\"inky\"\n\"21\",\"errata\"\n\"21\",\"kallithea\"\n\"21\",\"ws4j\"\n\"21\",\"type-ahead\"\n\"21\",\"module-build\"\n\"21\",\"jqpivot\"\n\"21\",\"formalchemy\"\n\"21\",\"rdio\"\n\"21\",\"nwebsec\"\n\"21\",\"auth0-js\"\n\"21\",\"outlook-graph-api\"\n\"21\",\"system.linq.dynamic\"\n\"21\",\"microsoft-todo\"\n\"21\",\"sic\"\n\"21\",\"lua-scripting-library\"\n\"21\",\"knx\"\n\"21\",\"ludwig\"\n\"21\",\"javascript-interop\"\n\"21\",\"formsy-react\"\n\"21\",\"visual-c++-2019\"\n\"21\",\"lumx\"\n\"21\",\"kotlin-sealed\"\n\"21\",\"java-audio\"\n\"21\",\"typescript-2.5\"\n\"21\",\"fpml\"\n\"21\",\"raml-1.0\"\n\"21\",\"kloxo\"\n\"21\",\"ocrmypdf\"\n\"21\",\"klaviyo\"\n\"21\",\"kotlin-inline-class\"\n\"21\",\"atsam3x\"\n\"21\",\"viewusercontrol\"\n\"21\",\"overheating\"\n\"21\",\"jaxb2-annotate-plugin\"\n\"21\",\"2-3-tree\"\n\"21\",\"code-view\"\n\"21\",\"typeface.js\"\n\"21\",\"3d-mapping\"\n\"21\",\"koloda\"\n\"21\",\"obs-studio\"\n\"21\",\"video-library\"\n\"21\",\"netgraph\"\n\"21\",\"kotlin-coroutine-channel\"\n\"21\",\"type-extension\"\n\"21\",\"system.err\"\n\"21\",\"py-redis\"\n\"21\",\"setsid\"\n\"21\",\"2-satisfiability\"\n\"21\",\"javax.activation\"\n\"21\",\"korge\"\n\"21\",\"systemml\"\n\"21\",\"buf\"\n\"21\",\"browsefragment\"\n\"21\",\"o365rwsclient\"\n\"21\",\"raspbian-stretch\"\n\"21\",\"btreemap\"\n\"21\",\"outlook-2007-addin\"\n\"21\",\"mixed-authentication\"\n\"21\",\"extjs7-classic\"\n\"21\",\"libxlsxwriter\"\n\"21\",\"madlib\"\n\"21\",\"oriento\"\n\"21\",\"magick-r-package\"\n\"21\",\"dolibarr\"\n\"21\",\"amber-framework\"\n\"21\",\"neutrino\"\n\"21\",\"object-address\"\n\"21\",\"net-tcp\"\n\"21\",\"t9\"\n\"21\",\"bryntum-scheduler\"\n\"21\",\"microsoft-graph-groups\"\n\"21\",\"winstone\"\n\"21\",\"orchid\"\n\"21\",\"lenny\"\n\"21\",\"wmdc\"\n\"21\",\"goimports\"\n\"21\",\"braze\"\n\"21\",\"libdl\"\n\"21\",\"objectbox-android\"\n\"21\",\"vllm\"\n\"21\",\"winmail.dat\"\n\"21\",\"wordpress-capabilities\"\n\"21\",\"winrs\"\n\"21\",\"atomic-long\"\n\"21\",\"wnet\"\n\"21\",\"miranda\"\n\"21\",\"macos-app-extension\"\n\"21\",\"buildbox\"\n\"21\",\"magento-go\"\n\"21\",\"scgi\"\n\"21\",\"nolio\"\n\"21\",\"dotnet-core-pack\"\n\"21\",\"gemstone\"\n\"21\",\"expert-advisor\"\n\"21\",\"xamlwriter\"\n\"21\",\"hidden-fields\"\n\"21\",\"rack-rewrite\"\n\"21\",\"novocaine\"\n\"21\",\"expanded\"\n\"21\",\"sql-server-authentication\"\n\"21\",\"hoptoad\"\n\"21\",\"android-hardware-keyboard\"\n\"21\",\"dry-rb\"\n\"21\",\"tinyml\"\n\"21\",\"non-well-formed\"\n\"21\",\"redirecttoroute\"\n\"21\",\"numericstepper\"\n\"21\",\"directory-security\"\n\"21\",\"redirection-wordpress-plugin\"\n\"21\",\"dhclient\"\n\"21\",\"diagrams.net\"\n\"21\",\"wysiwym\"\n\"21\",\"moa\"\n\"21\",\"openonload\"\n\"21\",\"istio-prometheus\"\n\"21\",\"ispconfig-3\"\n\"21\",\"nunit-2.6\"\n\"21\",\"x25519\"\n\"21\",\"mobile-analytics\"\n\"21\",\"plugin.media.crossmedia\"\n\"21\",\"reddit-access-token\"\n\"21\",\"time-to-first-byte\"\n\"21\",\"plumbum\"\n\"21\",\"scratchcard\"\n\"21\",\"screen-positioning\"\n\"21\",\"to-be-continuous\"\n\"21\",\"d-star\"\n\"21\",\"toco\"\n\"21\",\"reflex-dom\"\n\"21\",\"gcore\"\n\"21\",\"hadoop-lzo\"\n\"21\",\"controlling\"\n\"21\",\"nstablerowview\"\n\"21\",\"nszombies\"\n\"21\",\"doxia\"\n\"21\",\"dpdk-pmd\"\n\"21\",\"mkmf\"\n\"21\",\"asp.net-mvc-templates\"\n\"21\",\"scngeometry\"\n\"21\",\"opentofu\"\n\"21\",\"drupal-files\"\n\"21\",\"harmony\"\n\"21\",\"dribbble-api\"\n\"21\",\"opensmpp\"\n\"21\",\"gdal-python-bindings\"\n\"21\",\"export-to-html\"\n\"21\",\"sproutcore-2\"\n\"21\",\"hapi-swagger\"\n\"21\",\"nsuuid\"\n\"21\",\"scntechnique\"\n\"21\",\"expo-dev-client\"\n\"21\",\"gedmo-loggable\"\n\"21\",\"controlbox\"\n\"21\",\"token-name-resolution\"\n\"21\",\"gelly\"\n\"21\",\"vfs-stream\"\n\"21\",\"ivars\"\n\"21\",\"xcode8-beta3\"\n\"21\",\"node-centrality\"\n\"21\",\"moonscript\"\n\"21\",\"android-messaging\"\n\"21\",\"location-updates\"\n\"21\",\"monkeyc\"\n\"21\",\"escalation\"\n\"21\",\"layout-parser\"\n\"21\",\"omeka\"\n\"21\",\"pytumblr\"\n\"21\",\"split-function\"\n\"21\",\"google-cloud-spanner-emulator\"\n\"21\",\"hwclock\"\n\"21\",\"ldapauth\"\n\"21\",\"spec2\"\n\"21\",\"nodereference\"\n\"21\",\"provider-hosted\"\n\"21\",\"huawei-ads\"\n\"21\",\"spring-2.5\"\n\"21\",\"google-cloud-metrics\"\n\"21\",\"okteto\"\n\"21\",\"movefileex\"\n\"21\",\"resharper-5.1\"\n\"21\",\"movidius\"\n\"21\",\"streamingresponsebody\"\n\"21\",\"qlocale\"\n\"21\",\"angular-google-chart\"\n\"21\",\"duosecurity\"\n\"21\",\"comdlg32\"\n\"21\",\"angular-directive-link\"\n\"21\",\"acf-gutenberg\"\n\"21\",\"resumable\"\n\"21\",\"ejb-timer\"\n\"21\",\"perception\"\n\"21\",\"ctp4\"\n\"21\",\"accpac\"\n\"21\",\"ironscheme\"\n\"21\",\"qmovie\"\n\"21\",\"perfetto\"\n\"21\",\"getcustomattributes\"\n\"21\",\"irrklang\"\n\"21\",\"angular2-toaster\"\n\"21\",\"performance.now\"\n\"21\",\"ctr-mode\"\n\"21\",\"restartmanager\"\n\"21\",\"qopenglfunctions\"\n\"21\",\"actuate\"\n\"21\",\"elasticsearch-performance\"\n\"21\",\"iphone-sdk-4.3\"\n\"21\",\"nickel\"\n\"21\",\"memmap\"\n\"21\",\"cuda-graphs\"\n\"21\",\"qglviewer\"\n\"21\",\"string-iteration\"\n\"21\",\"currency-pipe\"\n\"21\",\"text2image\"\n\"21\",\"odoo-accounting\"\n\"21\",\"http-message-converter\"\n\"21\",\"coldspring\"\n\"21\",\"column-major-order\"\n\"21\",\"esptool\"\n\"21\",\"column-defaults\"\n\"21\",\"ios-standalone-mode\"\n\"21\",\"linq.compiledquery\"\n\"21\",\"msgfmt\"\n\"21\",\"zfc-rbac\"\n\"21\",\"google-input-tools\"\n\"21\",\"powergrep\"\n\"21\",\"lifecycleexception\"\n\"21\",\"powerpack\"\n\"21\",\"bayessearchcv\"\n\"21\",\"powerbi-filters\"\n\"21\",\"google-nearby-messages\"\n\"21\",\"encoding-json-go\"\n\"21\",\"bedtools\"\n\"21\",\"zfit\"\n\"21\",\"msmq-transaction\"\n\"21\",\"lightweight-stream-api\"\n\"21\",\"parallel.invoke\"\n\"21\",\"empirical-distribution\"\n\"21\",\"alignof\"\n\"21\",\"particle-photon\"\n\"21\",\"parallelstream\"\n\"21\",\"parse-recdescent\"\n\"21\",\"linux-kernel-headers\"\n\"21\",\"partialviews\"\n\"21\",\"thomson-reuters-eikon\"\n\"21\",\"web-ext\"\n\"21\",\"compiler-services\"\n\"21\",\"user-defined-fields\"\n\"21\",\"hbasestorage\"\n\"21\",\"bfcache\"\n\"21\",\"concrete-syntax-tree\"\n\"21\",\"compiler-development\"\n\"21\",\"embedded-object\"\n\"21\",\"hdfql\"\n\"21\",\"giza++\"\n\"21\",\"gkpeerpickercontroller\"\n\"21\",\"emacs-speedbar\"\n\"21\",\"forecastr\"\n\"21\",\"arm-mpu\"\n\"21\",\"stimulus-rails\"\n\"21\",\"thinky\"\n\"21\",\"webauthenticator\"\n\"21\",\"security-trimming\"\n\"21\",\"struts2-rest-plugin\"\n\"21\",\"concurrent-ruby\"\n\"21\",\"heroicons\"\n\"21\",\"haskell-hedgehog\"\n\"21\",\"flutter-mockito\"\n\"21\",\"secondary-sort\"\n\"21\",\"componentkit\"\n\"21\",\"componentmodel\"\n\"21\",\"maximum-profit-problem\"\n\"21\",\"headerfooter\"\n\"21\",\"spark-shuffle\"\n\"21\",\"usn\"\n\"21\",\"uss\"\n\"21\",\"mcs\"\n\"21\",\"ijvm\"\n\"21\",\"touchswipe\"\n\"21\",\"zencoder\"\n\"21\",\"artemiscloud\"\n\"21\",\"flyte\"\n\"21\",\"fongo\"\n\"21\",\"sonarqube6.3\"\n\"21\",\"sharp-repository\"\n\"21\",\"computed-values\"\n\"21\",\"z3-fixedpoint\"\n\"21\",\"compute-capability\"\n\"21\",\"weblogic14c\"\n\"21\",\"identity-provider\"\n\"21\",\"sonicwall\"\n\"21\",\"sublayout\"\n\"21\",\"behance-api\"\n\"21\",\"fms3\"\n\"21\",\"multcompview\"\n\"21\",\"shiboken2\"\n\"21\",\"identityserver2\"\n\"21\",\"maven-webstart-plugin\"\n\"21\",\"zcml\"\n\"21\",\"gmavenplus\"\n\"20\",\"trusted-vs-untrusted\"\n\"20\",\"terminal.app\"\n\"20\",\"cloud-connect\"\n\"20\",\"xrmservicetoolkit\"\n\"20\",\"decltype-auto\"\n\"20\",\"backdraftjs\"\n\"20\",\"apache-arrow-datafusion\"\n\"20\",\"ckqueryoperation\"\n\"20\",\"dead-reckoning\"\n\"20\",\"close-button\"\n\"20\",\"stanza.io\"\n\"20\",\"ggboxplot\"\n\"20\",\"clean-url\"\n\"20\",\"clang-query\"\n\"20\",\"balena\"\n\"20\",\"bale-messenger\"\n\"20\",\"tensorflowsharp\"\n\"20\",\"ckmodifyrecordsoperation\"\n\"20\",\"anti-bot\"\n\"20\",\"ckfinder3\"\n\"20\",\"ggeffects\"\n\"20\",\"classformaterror\"\n\"20\",\"floris\"\n\"20\",\"wepay\"\n\"20\",\"ckerror\"\n\"20\",\"cluster-mode\"\n\"20\",\"debug-print\"\n\"20\",\"bareword\"\n\"20\",\"floating-ui\"\n\"20\",\"flutter-easy-localization\"\n\"20\",\"ecdhe\"\n\"20\",\"vue-styleguidist\"\n\"20\",\"clozure-cl\"\n\"20\",\"flink-checkpoint\"\n\"20\",\"decentraland\"\n\"20\",\"vue-suspense\"\n\"20\",\"class-decorator\"\n\"20\",\"tembeddedwb\"\n\"20\",\"installshield-2016\"\n\"20\",\"react-native-unimodules\"\n\"20\",\"file-diffs\"\n\"20\",\"materials\"\n\"20\",\"squib\"\n\"20\",\"teltonika\"\n\"20\",\"deferred-result\"\n\"20\",\"my.resources\"\n\"20\",\"pharo-5\"\n\"20\",\"figlet\"\n\"20\",\"reitit\"\n\"20\",\"jbehave-maven-plugin\"\n\"20\",\"website-payment-pro\"\n\"20\",\"phpstorm-2016.1\"\n\"20\",\"jcycle\"\n\"20\",\"liveid\"\n\"20\",\"graphiti\"\n\"20\",\"replit-database\"\n\"20\",\"instaparse\"\n\"20\",\"squad\"\n\"20\",\"repeatable-read\"\n\"20\",\"slick-pg\"\n\"20\",\"react-native-fast-image\"\n\"20\",\"math-mode\"\n\"20\",\"jfeinstein\"\n\"20\",\"few-shot-learning\"\n\"20\",\"jetty-httpclient\"\n\"20\",\"php-safe-mode\"\n\"20\",\"ssbo\"\n\"20\",\"intellij-12\"\n\"20\",\"jboss-wildfly-11\"\n\"20\",\"jett\"\n\"20\",\"filebrowse\"\n\"20\",\"jetspeed2\"\n\"20\",\"smallcheck\"\n\"20\",\"inserter\"\n\"20\",\"intellij-17\"\n\"20\",\"default-namespace\"\n\"20\",\"graphql-yoga\"\n\"20\",\"renesas-rx\"\n\"20\",\"rendr\"\n\"20\",\"jest-mock-extended\"\n\"20\",\"file-globs\"\n\"20\",\"file-connection\"\n\"20\",\"react-native-safe-area-view\"\n\"20\",\"ffmpegkit\"\n\"20\",\"multi-model-database\"\n\"20\",\"react-quilljs\"\n\"20\",\"filehash\"\n\"20\",\"loadvars\"\n\"20\",\"jayrock\"\n\"20\",\"process-elevation\"\n\"20\",\"photran\"\n\"20\",\"react-router-bootstrap\"\n\"20\",\"react-qr-code\"\n\"20\",\"grimport\"\n\"20\",\"cbmc\"\n\"20\",\"function-reference\"\n\"20\",\"ccmenu\"\n\"20\",\"dataparallel\"\n\"20\",\"function-call-operator\"\n\"20\",\"data-presentation\"\n\"20\",\"fullcontact\"\n\"20\",\"datarowcollection\"\n\"20\",\"file-system-storage\"\n\"20\",\"flask-oidc\"\n\"20\",\"symbian3\"\n\"20\",\"firebase-util\"\n\"20\",\"fiware-knowage\"\n\"20\",\"adobe-dps\"\n\"20\",\"jsbn\"\n\"20\",\"js-joda\"\n\"20\",\"swiftui-navigationpath\"\n\"20\",\"swiftui-swipeactions\"\n\"20\",\"fresnel\"\n\"20\",\"chrome-canary\"\n\"20\",\"json-spirit\"\n\"20\",\"choices.js\"\n\"20\",\"content-model\"\n\"20\",\"sx\"\n\"20\",\"chef-windows\"\n\"20\",\"chef-vault\"\n\"20\",\"chatops\"\n\"20\",\"adf-task-flow\"\n\"20\",\"rivescript\"\n\"20\",\"funscript\"\n\"20\",\"cashfree\"\n\"20\",\"runhaskell\"\n\"20\",\"running-object-table\"\n\"20\",\"fusefabric\"\n\"20\",\"python-gnupgp\"\n\"20\",\"rust-criterion\"\n\"20\",\"python-ast\"\n\"20\",\"rust-pin\"\n\"20\",\"voluptuous\"\n\"20\",\"indexed-views\"\n\"20\",\"appboy\"\n\"20\",\"sacct\"\n\"20\",\"safetynet-api\"\n\"20\",\"lance\"\n\"20\",\"sage-one\"\n\"20\",\"self-supervised-learning\"\n\"20\",\"uiviewcontentmode\"\n\"20\",\"unbuffered-queries\"\n\"20\",\"select-for-xml\"\n\"20\",\"seldon-core\"\n\"20\",\"blackhole\"\n\"20\",\"unified-automation-sdk\"\n\"20\",\"unikernel\"\n\"20\",\"unionfs\"\n\"20\",\"pagoda-box\"\n\"20\",\"pagingtoolbar\"\n\"20\",\"binary-string\"\n\"20\",\"api-eveonline\"\n\"20\",\"overtone\"\n\"20\",\"bing-news-search-api\"\n\"20\",\"blackberry-os6\"\n\"20\",\"bitmap-index\"\n\"20\",\"apartment-state\"\n\"20\",\"biwavelet\"\n\"20\",\"apache-vysper\"\n\"20\",\"p7b\"\n\"20\",\"apache-syncope\"\n\"20\",\"p5.play\"\n\"20\",\"smtpjs\"\n\"20\",\"dispy\"\n\"20\",\"xing\"\n\"20\",\"playbin2\"\n\"20\",\"xeus-cling\"\n\"20\",\"smartsheet-java-sdk-v2\"\n\"20\",\"picard\"\n\"20\",\"pingback\"\n\"20\",\"xlw\"\n\"20\",\"smarty-plugins\"\n\"20\",\"dbgeography\"\n\"20\",\"xna-math-library\"\n\"20\",\"divmod\"\n\"20\",\"platform.sh\"\n\"20\",\"rubycas\"\n\"20\",\"csplitterwnd\"\n\"20\",\"simple-machines-forum\"\n\"20\",\"micronaut-security\"\n\"20\",\"django-jenkins\"\n\"20\",\"csqldataprovider\"\n\"20\",\"angular-material-paginator\"\n\"20\",\"universal-ctags\"\n\"20\",\"micrium\"\n\"20\",\"django-translated-fields\"\n\"20\",\"r-labelled\"\n\"20\",\"grails-5\"\n\"20\",\"icap\"\n\"20\",\"mget\"\n\"20\",\"punjab\"\n\"20\",\"kendospreadsheet\"\n\"20\",\"aws-route-table\"\n\"20\",\"camtasia\"\n\"20\",\"dockerrun.aws.json\"\n\"20\",\"fastify-multipart\"\n\"20\",\"avr32\"\n\"20\",\"row-removal\"\n\"20\",\"wayfinder\"\n\"20\",\"naniar\"\n\"20\",\"window.parent\"\n\"20\",\"ora-00054\"\n\"20\",\"crewai\"\n\"20\",\"inet-aton\"\n\"20\",\"rowdefinition\"\n\"20\",\"akismet\"\n\"20\",\"route-constraint\"\n\"20\",\"ag-charts\"\n\"20\",\"vcal\"\n\"20\",\"ora-01422\"\n\"20\",\"napalm\"\n\"20\",\"django-multiwidget\"\n\"20\",\"django-webtest\"\n\"20\",\"sinatra-assetpack\"\n\"20\",\"aws-load-balancer-controller\"\n\"20\",\"rml-rdf\"\n\"20\",\"watson-knowledge-catalog\"\n\"20\",\"gpc\"\n\"20\",\"crystal-reports-formulas\"\n\"20\",\"unspecified\"\n\"20\",\"joda-money\"\n\"20\",\"dataflex\"\n\"20\",\"sharepoint-webservice\"\n\"20\",\"vector-clock\"\n\"20\",\"pybinding\"\n\"20\",\"factual\"\n\"20\",\"icx\"\n\"20\",\"documentgroup\"\n\"20\",\"pybricks-micropython\"\n\"20\",\"data-controls\"\n\"20\",\"wcf-serialization\"\n\"20\",\"crypto-obfuscator\"\n\"20\",\"realm-cocoa\"\n\"20\",\"keypreview\"\n\"20\",\"pweave\"\n\"20\",\"wcf-streaming\"\n\"20\",\"urho3d\"\n\"20\",\"readeventlog\"\n\"20\",\"unqualified-name\"\n\"20\",\"dns-over-https\"\n\"20\",\"facesservlet\"\n\"20\",\"keter\"\n\"20\",\"mytoolkit\"\n\"20\",\"docvariable\"\n\"20\",\"angular-ui-tinymce\"\n\"20\",\"agm-core\"\n\"20\",\"gradle-custom-plugin\"\n\"20\",\"dataformwebpart\"\n\"20\",\"kimball\"\n\"20\",\"angular-transitions\"\n\"20\",\"wails\"\n\"20\",\"aws-mediapackage\"\n\"20\",\"aksequencer\"\n\"20\",\"wikimapia\"\n\"20\",\"pvpython\"\n\"20\",\"kermit\"\n\"20\",\"docx2pdf\"\n\"20\",\"venn\"\n\"20\",\"twitter-bootstrap-form\"\n\"20\",\"write-error\"\n\"20\",\"openbr\"\n\"20\",\"sim7600\"\n\"20\",\"envjs\"\n\"20\",\"passthrough-elements\"\n\"20\",\"derelict3\"\n\"20\",\"bower-register\"\n\"20\",\"tvbox\"\n\"20\",\"nsenter\"\n\"20\",\"hourglass\"\n\"20\",\"gulp-plugin\"\n\"20\",\"azure-android-sdk\"\n\"20\",\"ttkthemes\"\n\"20\",\"http4s-circe\"\n\"20\",\"module-federationnextjs-mfutils\"\n\"20\",\"gtkada\"\n\"20\",\"one-time-binding\"\n\"20\",\"nest-simulator\"\n\"20\",\"neptune\"\n\"20\",\"turn.js\"\n\"20\",\"nscondition\"\n\"20\",\"axelar\"\n\"20\",\"ion-checkbox\"\n\"20\",\"gulp-minify-css\"\n\"20\",\"gulp-minify\"\n\"20\",\"invocationhandler\"\n\"20\",\"twitter-share\"\n\"20\",\"svn-export\"\n\"20\",\"spring-batch-excel\"\n\"20\",\"ttstyledtextlabel\"\n\"20\",\"pdo-odbc\"\n\"20\",\"cps\"\n\"20\",\"nsb-servicecontrol\"\n\"20\",\"enscript\"\n\"20\",\"initwithcontentsoffile\"\n\"20\",\"worker-pool\"\n\"20\",\"silicon\"\n\"20\",\"gulp-if\"\n\"20\",\"swift-compiler\"\n\"20\",\"kafka-join\"\n\"20\",\"ndb\"\n\"20\",\"grunt-assemble\"\n\"20\",\"online-algorithm\"\n\"20\",\"dynamic-web-twain\"\n\"20\",\"internal-class\"\n\"20\",\"wsd\"\n\"20\",\"mongodb-biconnector\"\n\"20\",\"bonsaijs\"\n\"20\",\"android-studio-dolphin\"\n\"20\",\"spring-integration-file\"\n\"20\",\"twinfield\"\n\"20\",\"android-studio-chipmunk\"\n\"20\",\"sass-rails\"\n\"20\",\"poppler-utils\"\n\"20\",\"dynamic-frameworks\"\n\"20\",\"nsobjectcontroller\"\n\"20\",\"interval-arithmetic\"\n\"20\",\"apple-musickit-js\"\n\"20\",\"superblock\"\n\"20\",\"azure-auto-ml\"\n\"20\",\"erdpy\"\n\"20\",\"ion-list\"\n\"20\",\"epoxy-modelview\"\n\"20\",\"apple-authentication\"\n\"20\",\"azure-feature-manager\"\n\"20\",\"jxmaps\"\n\"20\",\"azure-ad-verifiable-credentials\"\n\"20\",\"wreq\"\n\"20\",\"wpf-core\"\n\"20\",\"pomm\"\n\"20\",\"salesforce-cli\"\n\"20\",\"jxmapviewer\"\n\"20\",\"postgres-operator\"\n\"20\",\"twenty-ten-theme\"\n\"20\",\"samsung-health\"\n\"20\",\"borland-c\"\n\"20\",\"wp7test\"\n\"20\",\"jumphost\"\n\"20\",\"kannada\"\n\"20\",\"spring-mono\"\n\"20\",\"ephemeral-storage\"\n\"20\",\"jukito\"\n\"20\",\"desfire\"\n\"20\",\"inline-method\"\n\"20\",\"nchar\"\n\"20\",\"jquery-hotkeys\"\n\"20\",\"codemod\"\n\"20\",\"codelldb\"\n\"20\",\"sysex\"\n\"20\",\"minor-mode\"\n\"20\",\"wordnik\"\n\"20\",\"pyforms\"\n\"20\",\"android-3.1-honeycomb\"\n\"20\",\"object-design\"\n\"20\",\"ubi\"\n\"20\",\"vici\"\n\"20\",\"shinyauthr\"\n\"20\",\".net-generic-math\"\n\"20\",\"pymongo-2.x\"\n\"20\",\"abap-st\"\n\"20\",\"dom4\"\n\"20\",\".net-traceprocessing\"\n\"20\",\"richeditabletext\"\n\"20\",\"wordpress-action\"\n\"20\",\"rich-domain-model\"\n\"20\",\"r-distill\"\n\"20\",\"shouldly\"\n\"20\",\"brew-doctor\"\n\"20\",\"reversegeocodelocation\"\n\"20\",\"typeglob\"\n\"20\",\"ubuntu-8.10\"\n\"20\",\"bridge.net\"\n\"20\",\"networkinfo\"\n\"20\",\"magento-1.3\"\n\"20\",\"amazon-sumerian\"\n\"20\",\"os-agnostic\"\n\"20\",\"lupa\"\n\"20\",\"coexistence\"\n\"20\",\"microsoft-graph-sites\"\n\"20\",\"go-flag\"\n\"20\",\"kohana-2\"\n\"20\",\"pygmt\"\n\"20\",\"kognitio-wx2\"\n\"20\",\"levelhelper\"\n\"20\",\"koding\"\n\"20\",\"viewwilldisappear\"\n\"20\",\"pyrserve\"\n\"20\",\"rap\"\n\"20\",\"java-failsafe\"\n\"20\",\"ui4j\"\n\"20\",\"atari-2600\"\n\"20\",\"librarian-puppet\"\n\"20\",\"cocor\"\n\"20\",\"rippled\"\n\"20\",\"gonum\"\n\"20\",\"academic-graph\"\n\"20\",\"aurigma\"\n\"20\",\"codecave\"\n\"20\",\"facebook-graph-api-v2.1\"\n\"20\",\"auraphp\"\n\"20\",\"pydbg\"\n\"20\",\"librato\"\n\"20\",\"magic-leap\"\n\"20\",\"libnice\"\n\"20\",\"tabbed-browsing\"\n\"20\",\"typeset\"\n\"20\",\"pydotplus\"\n\"20\",\"augraph\"\n\"20\",\"goinstall\"\n\"20\",\"accelerate-haskell\"\n\"20\",\"android-activity-alias\"\n\"20\",\"mac-frameworks\"\n\"20\",\"wisper\"\n\"20\",\"android-actionbar-tabs\"\n\"20\",\"pyro.ai\"\n\"20\",\"syphon\"\n\"20\",\"osxfuse\"\n\"20\",\"virtual-channel\"\n\"20\",\"uap\"\n\"20\",\"6510\"\n\"20\",\"extension-builder3\"\n\"20\",\"java-console\"\n\"20\",\"spying\"\n\"20\",\"notification-action\"\n\"20\",\"gadbannerview\"\n\"20\",\"nuxmv\"\n\"20\",\"xcode6-beta7\"\n\"20\",\"mktemp\"\n\"20\",\"sqlacodegen\"\n\"20\",\"caf-receiver-sdk\"\n\"20\",\"noraui\"\n\"20\",\"png-8\"\n\"20\",\"pnp.powershell\"\n\"20\",\"pmwiki\"\n\"20\",\"hkobserverquery\"\n\"20\",\"highs\"\n\"20\",\"cordova-facebook\"\n\"20\",\"wxhtmlwindow\"\n\"20\",\"directmemory\"\n\"20\",\"hoplon\"\n\"20\",\"android-device-owner\"\n\"20\",\"expressiveannotations\"\n\"20\",\"xcode11.2.1\"\n\"20\",\"coproc\"\n\"20\",\"non-scrolling\"\n\"20\",\"sqlbuddy\"\n\"20\",\"azure-resource-lock\"\n\"20\",\"homoiconicity\"\n\"20\",\"c14n\"\n\"20\",\"pointofservice\"\n\"20\",\"context-configuration\"\n\"20\",\"node-test-runner\"\n\"20\",\"nowdoc\"\n\"20\",\"sql-rank\"\n\"20\",\"gvisor\"\n\"20\",\"hmr\"\n\"20\",\"ixmldomelement\"\n\"20\",\"gwt-elemental\"\n\"20\",\"controlled-folder-access\"\n\"20\",\"isml\"\n\"20\",\"polygon.io\"\n\"20\",\"npm-config\"\n\"20\",\"dremel\"\n\"20\",\"ui-spy\"\n\"20\",\"tinytest\"\n\"20\",\"dsymutil\"\n\"20\",\"tinyproxy\"\n\"20\",\"uisplitviewdelegate\"\n\"20\",\"tanstack-router\"\n\"20\",\"mod-auth-kerb\"\n\"20\",\"dfply\"\n\"20\",\"dgrams\"\n\"20\",\"bupar\"\n\"20\",\"bunnycdn\"\n\"20\",\"dotnet-interactive\"\n\"20\",\"toll-free-bridging\"\n\"20\",\"openwrap\"\n\"20\",\"radtreelist\"\n\"20\",\"douglas-peucker\"\n\"20\",\"modeless-dialog\"\n\"20\",\"tobject\"\n\"20\",\"dotnetcharting\"\n\"20\",\"opensymphony\"\n\"20\",\"openxml-table\"\n\"20\",\"associative-table\"\n\"20\",\"uiactivitytypeairdrop\"\n\"20\",\"uiprintformatter\"\n\"20\",\"dpinst\"\n\"20\",\"drupal-routes\"\n\"20\",\"tag-handler\"\n\"20\",\"aspnet-merge\"\n\"20\",\"scalatra-sbt\"\n\"20\",\"redis-stack\"\n\"20\",\"qx\"\n\"20\",\"drupal-render\"\n\"20\",\"scalr\"\n\"20\",\"uidocumentbrowserviewcontroller\"\n\"20\",\"asp.net-cache\"\n\"20\",\"ref-struct\"\n\"20\",\"gd-graph\"\n\"20\",\"lc3-trap\"\n\"20\",\"meta-learning\"\n\"20\",\"mercurial-commit\"\n\"20\",\"genie.jl\"\n\"20\",\"e-sim\"\n\"20\",\"odometry\"\n\"20\",\"proguard-maven-plugin\"\n\"20\",\"qgraphicsrectitem\"\n\"20\",\"eslint-plugin-vue\"\n\"20\",\"mercurial-server\"\n\"20\",\"qkeysequence\"\n\"20\",\"qcubed\"\n\"20\",\"esri-loader\"\n\"20\",\"lazyvim\"\n\"20\",\"prom-client\"\n\"20\",\"odatalib\"\n\"20\",\"ohhttpstubs\"\n\"20\",\"protogen\"\n\"20\",\"chakracore\"\n\"20\",\"proximo\"\n\"20\",\"geograpy\"\n\"20\",\"moquette\"\n\"20\",\"omnicppcomplete\"\n\"20\",\"geokettle\"\n\"20\",\"omnixml\"\n\"20\",\"monobjc\"\n\"20\",\"cflocation\"\n\"20\",\"cfinvoke\"\n\"20\",\"ondrawitem\"\n\"20\",\"hypergraph\"\n\"20\",\"request-pipeline\"\n\"20\",\"huffman-tree\"\n\"20\",\"huawei-ml-kit\"\n\"20\",\"http-status-code-505\"\n\"20\",\"strawberryshake\"\n\"20\",\"petastorm\"\n\"20\",\"http-permissions-policy\"\n\"20\",\"http-mock\"\n\"20\",\"httpi\"\n\"20\",\"resharper-c++\"\n\"20\",\"stretchblt\"\n\"20\",\"persisted-column\"\n\"20\",\"strong-reference-cycle\"\n\"20\",\"perlnetssh\"\n\"20\",\"electronic-direct-mail\"\n\"20\",\"zts\"\n\"20\",\"responsestream\"\n\"20\",\"periodic\"\n\"20\",\"elasticsearch-client\"\n\"20\",\"accountpicker\"\n\"20\",\"elastic-container-registry\"\n\"20\",\"acl9\"\n\"20\",\"acorn\"\n\"20\",\"action-mailbox\"\n\"20\",\"pencilblue\"\n\"20\",\"nls-sort\"\n\"20\",\"lasso-lang\"\n\"20\",\"custom-overlay\"\n\"20\",\"custom-linq-providers\"\n\"20\",\"customizer\"\n\"20\",\"iot-devkit\"\n\"20\",\"react-chat-engine\"\n\"20\",\"terraform-provider-helm\"\n\"20\",\"ipcopen3\"\n\"20\",\"node-kafka\"\n\"20\",\"ls-remote\"\n\"20\",\"large-text\"\n\"20\",\"long-path\"\n\"20\",\"angular2-dart\"\n\"20\",\"nhlambdaextensions\"\n\"20\",\"nhibernate-search\"\n\"20\",\"google-doodle\"\n\"20\",\"cuba\"\n\"20\",\"irule\"\n\"20\",\"evaluation-strategy\"\n\"20\",\"evil-dicom\"\n\"20\",\"google-flexible\"\n\"20\",\"google-floodlight\"\n\"20\",\"ironspeed\"\n\"20\",\"ip-protocol\"\n\"20\",\"launch-condition\"\n\"20\",\"ews-javascript-api\"\n\"20\",\"logical-tree\"\n\"20\",\"colmap\"\n\"20\",\"nodeenv\"\n\"20\",\"qmk-firmware\"\n\"20\",\"qmk\"\n\"20\",\"lazyhgrid\"\n\"20\",\"angularjs-3rd-party\"\n\"20\",\"userchrome.css\"\n\"20\",\"flutter-stacked\"\n\"20\",\"ignoreroute\"\n\"20\",\"webchartcontrol\"\n\"20\",\"three-valued-logic\"\n\"20\",\"concourse-task\"\n\"20\",\"panelbar\"\n\"20\",\"compositionroot\"\n\"20\",\"cypress-origin\"\n\"20\",\"heliconzoo\"\n\"20\",\"zio-http\"\n\"20\",\"mawk\"\n\"20\",\"hdiutil\"\n\"20\",\"user-location\"\n\"20\",\"subliminal\"\n\"20\",\"shell-icons\"\n\"20\",\"alt-ergo\"\n\"20\",\"tibbletime\"\n\"20\",\"compositeitemwriter\"\n\"20\",\"sortedcontainers\"\n\"20\",\"image-annotations\"\n\"20\",\"igbinary\"\n\"20\",\"folktale\"\n\"20\",\"solr9\"\n\"20\",\"zerotier\"\n\"20\",\"statelist\"\n\"20\",\"utf8json\"\n\"20\",\"autodesk-webhooks\"\n\"20\",\"mtgox\"\n\"20\",\"zend-form-fieldset\"\n\"20\",\"mdxstudio\"\n\"20\",\"amazon-ground-truth\"\n\"20\",\"spark-bigquery-connector\"\n\"20\",\"zapproxy\"\n\"20\",\"qtquick-designer\"\n\"20\",\"msscci\"\n\"20\",\"structured-clone\"\n\"20\",\"glossaries\"\n\"20\",\"heroku-nodejs\"\n\"20\",\"qtsvg\"\n\"20\",\"search-keywords\"\n\"20\",\"scullyio\"\n\"20\",\"toplevel-statement\"\n\"20\",\"battlenet-api\"\n\"20\",\"qtooltip\"\n\"20\",\"yt\"\n\"20\",\"spark-2014\"\n\"20\",\"ember-query-params\"\n\"20\",\"ysod\"\n\"20\",\"scroll-snap-type\"\n\"20\",\"array-pointer\"\n\"20\",\"struts2-junit-plugin\"\n\"20\",\"zend-rest-route\"\n\"20\",\"tor-browser-bundle\"\n\"20\",\"licode\"\n\"20\",\"mt940\"\n\"20\",\"scroll-lock\"\n\"20\",\"google-realtime-api\"\n\"20\",\"hasura-cli\"\n\"20\",\"webfiltering\"\n\"20\",\"idbcommand\"\n\"20\",\"lifecycleowner\"\n\"20\",\"dark-theme\"\n\"20\",\"spark2.4.4\"\n\"20\",\"ppc64le\"\n\"20\",\"ilrepack\"\n\"20\",\"precompiling\"\n\"20\",\"stencil-compiler\"\n\"20\",\"meck\"\n\"19\",\"claimcenter\"\n\"19\",\"felix-dependency-manager\"\n\"19\",\"sitescope\"\n\"19\",\"cloudposse\"\n\"19\",\"graphiti-js\"\n\"19\",\"mutual-friendship\"\n\"19\",\"apache-commons-scxml\"\n\"19\",\"vue-dropzone\"\n\"19\",\"trinitycore\"\n\"19\",\"php-attributes\"\n\"19\",\"clover-payment\"\n\"19\",\"yii-migrations\"\n\"19\",\"github-dependabot\"\n\"19\",\"clean-language\"\n\"19\",\"mvcrazortopdf\"\n\"19\",\"backload\"\n\"19\",\"sll\"\n\"19\",\"trunk-rs\"\n\"19\",\"repopulation\"\n\"19\",\"intel-media-sdk\"\n\"19\",\"xsl-grouping\"\n\"19\",\"decimal-precision\"\n\"19\",\"decimal.js\"\n\"19\",\"programmatic-config\"\n\"19\",\"filecopy\"\n\"19\",\"repaintmanager\"\n\"19\",\"fdroid\"\n\"19\",\"anonymous-recursion\"\n\"19\",\"eclipse-project-file\"\n\"19\",\"gitleaks\"\n\"19\",\"flutter-audio-query\"\n\"19\",\"anypoint-rtf\"\n\"19\",\"cj\"\n\"19\",\"eclipse-temurin\"\n\"19\",\"xtemplate\"\n\"19\",\"liquibase-cli\"\n\"19\",\"linux-x32-abi\"\n\"19\",\"tensorflow-addons\"\n\"19\",\"flickrj\"\n\"19\",\"clickup-api\"\n\"19\",\"ffmpeg.js\"\n\"19\",\"stack-level\"\n\"19\",\"tenancyforlaravel\"\n\"19\",\"location-aware\"\n\"19\",\"ant-design-blazor\"\n\"19\",\"flex-datagrid\"\n\"19\",\"private-cloud\"\n\"19\",\"jfeed\"\n\"19\",\"yampa\"\n\"19\",\"dbtable\"\n\"19\",\"slashdb\"\n\"19\",\"php-jwt\"\n\"19\",\"tensorflowjs\"\n\"19\",\"coccinelle\"\n\"19\",\"multipart-upload\"\n\"19\",\"weston\"\n\"19\",\"cobol.net\"\n\"19\",\"instagrapi\"\n\"19\",\"gitbox\"\n\"19\",\"template-mixins\"\n\"19\",\"barbecue\"\n\"19\",\"cloudfront-functions\"\n\"19\",\"groovyfx\"\n\"19\",\"apache-abdera\"\n\"19\",\"slick-codegen\"\n\"19\",\"instancestate\"\n\"19\",\"ssha\"\n\"19\",\"eclipse-api\"\n\"19\",\"yahoo-widgets\"\n\"19\",\"ggdist\"\n\"19\",\"ggalt\"\n\"19\",\"easytracker\"\n\"19\",\"ssdt-2017\"\n\"19\",\"siteedit\"\n\"19\",\"cloudmailin\"\n\"19\",\"easysnmp\"\n\"19\",\"ssas-2016\"\n\"19\",\"react-native-view-shot\"\n\"19\",\"aopalliance\"\n\"19\",\"edge-devtools\"\n\"19\",\"citrix-access-gateway\"\n\"19\",\"laravel-filters\"\n\"19\",\"adornerdecorator\"\n\"19\",\"ngx-modal\"\n\"19\",\"laravel-gate\"\n\"19\",\"switchers\"\n\"19\",\"flask-flatpages\"\n\"19\",\"managed-cuda\"\n\"19\",\"discrete-space\"\n\"19\",\"biztalk-2006\"\n\"19\",\"ozone\"\n\"19\",\"p4.net\"\n\"19\",\"rx-netty\"\n\"19\",\"maquette\"\n\"19\",\"flatpak-builder\"\n\"19\",\"xml3d\"\n\"19\",\"filevisitor\"\n\"19\",\"send-on-behalf-of\"\n\"19\",\"disable-caching\"\n\"19\",\"symengine\"\n\"19\",\"picture-element\"\n\"19\",\"jsmpeg\"\n\"19\",\"file-templates\"\n\"19\",\"sendgrid-rails\"\n\"19\",\"mangopay\"\n\"19\",\"softbody\"\n\"19\",\"chronicle-wire\"\n\"19\",\"adversarial-attack\"\n\"19\",\"makehuman\"\n\"19\",\"bitcoinjs-lib\"\n\"19\",\"aedes\"\n\"19\",\"filestructure\"\n\"19\",\"padding-oracle-attack\"\n\"19\",\"flex-builder-3\"\n\"19\",\"ngrep\"\n\"19\",\"constraintlayout-guideline\"\n\"19\",\"uitextrange\"\n\"19\",\"constraint-handling-rules\"\n\"19\",\"constraintexception\"\n\"19\",\"ng-lightning\"\n\"19\",\"aero-snap\"\n\"19\",\"binding.pry\"\n\"19\",\"nexus6\"\n\"19\",\"bincode\"\n\"19\",\"binary-diff\"\n\"19\",\"self-healing\"\n\"19\",\"nginx-rtmp\"\n\"19\",\"selenium-side-runner\"\n\"19\",\"uniscribe\"\n\"19\",\"uniquery\"\n\"19\",\"apache-whirr\"\n\"19\",\"chop\"\n\"19\",\"select-xml\"\n\"19\",\"nginx-log\"\n\"19\",\"jspeex\"\n\"19\",\"ng2-pdf-viewer\"\n\"19\",\"celerity\"\n\"19\",\"apoc\"\n\"19\",\"uniface\"\n\"19\",\"apiato\"\n\"19\",\"selectmanylistbox\"\n\"19\",\"pandasgui\"\n\"19\",\"unattend-file\"\n\"19\",\"cds.copernicus\"\n\"19\",\"unfoldingmap\"\n\"19\",\"configurationproperty\"\n\"19\",\"ngcomponentrouter\"\n\"19\",\"adapt\"\n\"19\",\"apipie\"\n\"19\",\"python-manylinux\"\n\"19\",\"js-data-angular\"\n\"19\",\"xml-spreadsheet\"\n\"19\",\"run-sequence\"\n\"19\",\"ruport\"\n\"19\",\"django-1.2\"\n\"19\",\"picoblaze\"\n\"19\",\"careplicatorlayer\"\n\"19\",\"smart-tv-alliance\"\n\"19\",\"firefox-nightly\"\n\"19\",\"smd\"\n\"19\",\"python-reflex\"\n\"19\",\"firefox-sidebar\"\n\"19\",\"physfs\"\n\"19\",\"administrative\"\n\"19\",\"xmlpoke\"\n\"19\",\"kubernetes-jenkins-plugin\"\n\"19\",\"xmllite\"\n\"19\",\"vs-android\"\n\"19\",\"kuka-krl\"\n\"19\",\"kurento-media-server\"\n\"19\",\"kwicks\"\n\"19\",\"kxml\"\n\"19\",\"displaymode\"\n\"19\",\"impromptu-interface\"\n\"19\",\"display-cutouts\"\n\"19\",\"datetimerangefield\"\n\"19\",\"v-play\"\n\"19\",\"import-table\"\n\"19\",\"vpd\"\n\"19\",\"rxfire\"\n\"19\",\"adobe-extension\"\n\"19\",\"rxbluetooth\"\n\"19\",\"jsm\"\n\"19\",\"aws-deeplens\"\n\"19\",\"callisto\"\n\"19\",\"cam-pdf\"\n\"19\",\"jpath\"\n\"19\",\"angular-theming\"\n\"19\",\"agda-stdlib\"\n\"19\",\"vegas-viz\"\n\"19\",\"iasyncdisposable\"\n\"19\",\"angular-mdl\"\n\"19\",\"facebook-node-sdk\"\n\"19\",\"datagridviewlinkcolumn\"\n\"19\",\"r-ppp\"\n\"19\",\"value-iteration\"\n\"19\",\"myro\"\n\"19\",\"data-importer\"\n\"19\",\"validator.js\"\n\"19\",\"uploadstring\"\n\"19\",\"pure-react-carousel\"\n\"19\",\"mysql-command-line-client\"\n\"19\",\"ora-01031\"\n\"19\",\"ruby-gnome2\"\n\"19\",\"methodaccessexception\"\n\"19\",\"ruby-daemons\"\n\"19\",\"servemux\"\n\"19\",\"serpapi\"\n\"19\",\"jms2\"\n\"19\",\"rro\"\n\"19\",\"public-members\"\n\"19\",\"vaadin-touchkit\"\n\"19\",\"rs.exe\"\n\"19\",\"gota\"\n\"19\",\"keycloak-admin-client\"\n\"19\",\"keyfilter\"\n\"19\",\"grails-resources-plugin\"\n\"19\",\"mysql-pconnect\"\n\"19\",\"unreliable-connection\"\n\"19\",\"psd2\"\n\"19\",\"namedparameterjdbctemplate\"\n\"19\",\"opnsense\"\n\"19\",\"oracle-service-cloud\"\n\"19\",\"unocss\"\n\"19\",\"fallocate\"\n\"19\",\"kicad\"\n\"19\",\"fastcgi++\"\n\"19\",\"fastcall\"\n\"19\",\"sharejs\"\n\"19\",\"oracle-integration-cloud\"\n\"19\",\"sitecore-social-connected\"\n\"19\",\"kindle-kdk\"\n\"19\",\"microsoft.extensions.configuration\"\n\"19\",\"react-use-gesture\"\n\"19\",\"far\"\n\"19\",\"django-mssql-backend\"\n\"19\",\"avr-studio5\"\n\"19\",\"angularjs-ng-style\"\n\"19\",\"database-comparison\"\n\"19\",\"session-per-request\"\n\"19\",\"avr-studio4\"\n\"19\",\"vb-like-operator\"\n\"19\",\"crossbeam\"\n\"19\",\"vbcodeprovider\"\n\"19\",\"cross-context\"\n\"19\",\"aws-resource-group\"\n\"19\",\"wincache\"\n\"19\",\"dogecoin-api\"\n\"19\",\"canjs-control\"\n\"19\",\"waterlock\"\n\"19\",\"row-value-expression\"\n\"19\",\"aws-scp\"\n\"19\",\"watij\"\n\"19\",\"robocup\"\n\"19\",\"ora-01861\"\n\"19\",\"django-fsm\"\n\"19\",\"nanoscroller\"\n\"19\",\"pycups\"\n\"19\",\"data-capture\"\n\"19\",\"avdepthdata\"\n\"19\",\"dataconnect\"\n\"19\",\"servicemonitor\"\n\"19\",\"variable-product\"\n\"19\",\"service-management\"\n\"19\",\"single-shot-detector\"\n\"19\",\"serviceknowntype\"\n\"19\",\"service-installer\"\n\"19\",\"pyalsaaudio\"\n\"19\",\"rpgmakermv\"\n\"19\",\"icr\"\n\"19\",\"capturestream\"\n\"19\",\"carbon-emacs\"\n\"19\",\"data-formatters\"\n\"19\",\"routedcommand\"\n\"19\",\"nanogallery\"\n\"19\",\"cardinality-estimation\"\n\"19\",\"nspreferencepane\"\n\"19\",\"bottomtabs\"\n\"19\",\"simd-library\"\n\"19\",\"inline-namespaces\"\n\"19\",\"blazor-hosted\"\n\"19\",\"postgresql-8.0\"\n\"19\",\"spring-integration-ftp\"\n\"19\",\"payment-services\"\n\"19\",\"pear-mail\"\n\"19\",\"cpanel-uapi\"\n\"19\",\"nspec\"\n\"19\",\"demoscene\"\n\"19\",\"onutterancecompleted\"\n\"19\",\"bootstrap-image-gallery\"\n\"19\",\"sass-lint\"\n\"19\",\"createdibsection\"\n\"19\",\"blender-2.76\"\n\"19\",\"module-packaging\"\n\"19\",\"neodynamic\"\n\"19\",\"jurassic\"\n\"19\",\"gulp-jscs\"\n\"19\",\"blitz.js\"\n\"19\",\"degrafa\"\n\"19\",\"saleslogix\"\n\"19\",\"enomem\"\n\"19\",\"blinkid\"\n\"19\",\"braintree-data\"\n\"19\",\"nsgradient\"\n\"19\",\"nservicebus6\"\n\"19\",\"blend-2012\"\n\"19\",\"boost-context\"\n\"19\",\"delphi-mocks\"\n\"19\",\"nested-checkboxes\"\n\"19\",\"input-method-kit\"\n\"19\",\"paypal-plus\"\n\"19\",\"inittab\"\n\"19\",\"bodypix\"\n\"19\",\"pattern-finding\"\n\"19\",\"hsc2hs\"\n\"19\",\"spring-boot-starter-security\"\n\"19\",\"openapi.net\"\n\"19\",\"application-scope\"\n\"19\",\"application-role\"\n\"19\",\"spring-modules\"\n\"19\",\"bpftrace\"\n\"19\",\"boxy\"\n\"19\",\"android-shortcutmanager\"\n\"19\",\"twarc2\"\n\"19\",\"jrecord\"\n\"19\",\"surface-controller\"\n\"19\",\"deriveddata\"\n\"19\",\"grunt-contrib-htmlmin\"\n\"19\",\"turbopack\"\n\"19\",\"mongodb-schema\"\n\"19\",\"android-sharesheet\"\n\"19\",\"entity-groups\"\n\"19\",\"intldateformatter\"\n\"19\",\"swf-decompiler\"\n\"19\",\"android-vts\"\n\"19\",\"earth-movers-distance\"\n\"19\",\"svg-font\"\n\"19\",\"dv360\"\n\"19\",\"azure-blueprints\"\n\"19\",\"wpr\"\n\"19\",\"invalidprogramexception\"\n\"19\",\"erlang-escript\"\n\"19\",\"svn-merge-reintegrate\"\n\"19\",\"ioptionsmonitor\"\n\"19\",\"suptitle\"\n\"19\",\"tynamo\"\n\"19\",\"wso2-msf4j\"\n\"19\",\"dust-helpers\"\n\"19\",\"turbojpeg\"\n\"19\",\"swank-clojure\"\n\"19\",\"two-legged\"\n\"19\",\"jqueryi-ui-buttonset\"\n\"19\",\"couch-cms\"\n\"19\",\"grpc-dart\"\n\"19\",\"axure\"\n\"19\",\"wsc\"\n\"19\",\"amazon-silk\"\n\"19\",\"newforms\"\n\"19\",\"type-stability\"\n\"19\",\"sgmlreader\"\n\"19\",\"wingdings\"\n\"19\",\"magiczoomplus\"\n\"19\",\"newrelic-synthetics\"\n\"19\",\"librarian\"\n\"19\",\"windsor-nhfacility\"\n\"19\",\"javascript-oscillator\"\n\"19\",\"google-api-explorer\"\n\"19\",\"window-tester\"\n\"19\",\"framework-design\"\n\"19\",\"google-bigquery-java\"\n\"19\",\"koa-passport\"\n\"19\",\"async-onprogressupdate\"\n\"19\",\"rfc1123\"\n\"19\",\"amethyst\"\n\"19\",\"javaloader\"\n\"19\",\"netlist\"\n\"19\",\"raspberry-pi-os\"\n\"19\",\"foundry-phonograph\"\n\"19\",\"viennacl\"\n\"19\",\"wordpress-ecommerce\"\n\"19\",\"system-f\"\n\"19\",\"rdiff-backup\"\n\"19\",\"form.io\"\n\"19\",\"javafx-17\"\n\"19\",\"vinyl-ftp\"\n\"19\",\"foundry-actions\"\n\"19\",\"formatmessage\"\n\"19\",\"freefem++\"\n\"19\",\"rautomation\"\n\"19\",\"typesafe-actions\"\n\"19\",\"pyiron\"\n\"19\",\"winrt-httpclient\"\n\"19\",\"netiq\"\n\"19\",\"octane\"\n\"19\",\"mirah\"\n\"19\",\"virtio\"\n\"19\",\"fosjsroutingbundle\"\n\"19\",\".net-services\"\n\"19\",\"extjs6.5.1\"\n\"19\",\"syndication-item\"\n\"19\",\"winpdb\"\n\"19\",\"shakespeare-text\"\n\"19\",\"formsy-material-ui\"\n\"19\",\"oracle-warehouse-builder\"\n\"19\",\"video-effects\"\n\"19\",\"newlisp\"\n\"19\",\"buffer-objects\"\n\"19\",\"java-assist\"\n\"19\",\"orbbec\"\n\"19\",\"typescript2.8\"\n\"19\",\"lua-lanes\"\n\"19\",\"java-custom-serialization\"\n\"19\",\"dom-selection\"\n\"19\",\"vlckit\"\n\"19\",\"o3d\"\n\"19\",\"codefresh\"\n\"19\",\"vlad-deployer\"\n\"19\",\"lwuit-textarea\"\n\"19\",\"microsoft-partnercenter-java\"\n\"19\",\"virtualquery\"\n\"19\",\"visionos-simulator\"\n\"19\",\"system.drawing.common\"\n\"19\",\"abaddressbooksource\"\n\"19\",\"pyrocms-lex\"\n\"19\",\"rightjs\"\n\"19\",\"razorlight\"\n\"19\",\"richpush\"\n\"19\",\"vmt\"\n\"19\",\"code-profiling\"\n\"19\",\"ouya\"\n\"19\",\"analytics-for-apache-hdp\"\n\"19\",\"kommunicate\"\n\"19\",\"domain-aliasing\"\n\"19\",\"nvrtc\"\n\"19\",\"nxopen\"\n\"19\",\"mindsdb\"\n\"19\",\"lesscss-resources\"\n\"19\",\"code-splitting-async\"\n\"19\",\"audiowaveform\"\n\"19\",\"outerwidth\"\n\"19\",\"nexe\"\n\"19\",\"google-analytics-campaign-builder\"\n\"19\",\"rc2-cipher\"\n\"19\",\"kronecker-product\"\n\"19\",\"java-text-blocks\"\n\"19\",\"amz-sagemaker-distributed-training\"\n\"19\",\"4store\"\n\"19\",\"attachmate-extra\"\n\"19\",\"oauth-refresh-token\"\n\"19\",\"netpbm\"\n\"19\",\"f2c\"\n\"19\",\"cocoascript\"\n\"19\",\"nxbre\"\n\"19\",\"domainkeys\"\n\"19\",\"orthanc-server\"\n\"19\",\"gcallowverylargeobjects\"\n\"19\",\"polipo\"\n\"19\",\"android-cling\"\n\"19\",\"h5p\"\n\"19\",\"tagify\"\n\"19\",\"mobx-persist\"\n\"19\",\"explicit-intent\"\n\"19\",\"busy-loop\"\n\"19\",\"tiptip\"\n\"19\",\"tizen-sdk\"\n\"19\",\"ntvdm\"\n\"19\",\"dotras\"\n\"19\",\"openshift-pipelines\"\n\"19\",\"gymnasium\"\n\"19\",\"android-compose-image\"\n\"19\",\"tabmenu\"\n\"19\",\"tlc\"\n\"19\",\"assignment-problem\"\n\"19\",\"nopcommerce-4.1\"\n\"19\",\"scrapysharp\"\n\"19\",\"hallo-js\"\n\"19\",\"taskservice\"\n\"19\",\"nosql-injection\"\n\"19\",\"plt-redex\"\n\"19\",\"plt\"\n\"19\",\"gcp-databricks\"\n\"19\",\"tau-prolog\"\n\"19\",\"reference-collapsing\"\n\"19\",\"android-gnss\"\n\"19\",\"android-go\"\n\"19\",\"uipopoverbackgroundview\"\n\"19\",\"openide\"\n\"19\",\"tcm\"\n\"19\",\"contextify\"\n\"19\",\"vertx4\"\n\"19\",\"exclusionpath\"\n\"19\",\"regl\"\n\"19\",\"mockito-inline\"\n\"19\",\"aspnetcoretemplatepack\"\n\"19\",\"asmselect\"\n\"19\",\"gegl\"\n\"19\",\"hardware-port\"\n\"19\",\"reflexil\"\n\"19\",\"diarization\"\n\"19\",\"drawnow\"\n\"19\",\"c++builder-2007\"\n\"19\",\"sqlmail\"\n\"19\",\"itunesconnect-analytics\"\n\"19\",\"mobilefirst-console\"\n\"19\",\"rack-cache\"\n\"19\",\"direct-initialization\"\n\"19\",\"sqlroleprovider\"\n\"19\",\"sqlite-journal-mode\"\n\"19\",\"uibackgroundmode\"\n\"19\",\"openwebstart\"\n\"19\",\"c++filt\"\n\"19\",\"garb-gem\"\n\"19\",\"scala-script\"\n\"19\",\"gargle\"\n\"19\",\"quire-api\"\n\"19\",\"j2html\"\n\"19\",\"itemprocessor\"\n\"19\",\"c1001\"\n\"19\",\"dexmaker\"\n\"19\",\"numerical-recipes\"\n\"19\",\"sql-azure-federations\"\n\"19\",\"sql-azure-alerts\"\n\"19\",\"diffstat\"\n\"19\",\"isomorphic-style-loader\"\n\"19\",\"hiveserver2\"\n\"19\",\"isoneway\"\n\"19\",\"hive-table\"\n\"19\",\"tnef\"\n\"19\",\"uilaunchimagefile\"\n\"19\",\"ui-leaflet\"\n\"19\",\"mmix\"\n\"19\",\"x10\"\n\"19\",\"spurious-wakeup\"\n\"19\",\"railsinstaller-windows\"\n\"19\",\"devpartner\"\n\"19\",\"buzz\"\n\"19\",\"diego\"\n\"19\",\"xact\"\n\"19\",\"jandex\"\n\"19\",\"dsoframer\"\n\"19\",\"xcodegen\"\n\"19\",\"cfinput\"\n\"19\",\"cfindex\"\n\"19\",\"react-material-ui-form-validator\"\n\"19\",\"ios-9-beta3\"\n\"19\",\"terraform-import\"\n\"19\",\"cfpreferences\"\n\"19\",\"splitpanel\"\n\"19\",\"spml\"\n\"19\",\"terraform-provider-docker\"\n\"19\",\"speedment\"\n\"19\",\"google-cloud-recommendation\"\n\"19\",\"spoonacular\"\n\"19\",\"cfselect\"\n\"19\",\"central\"\n\"19\",\"omf\"\n\"19\",\"terraform-provider-oci\"\n\"19\",\"pgdb\"\n\"19\",\"specter\"\n\"19\",\"storage-access-api\"\n\"19\",\"lars\"\n\"19\",\"resharper-2016\"\n\"19\",\"storyboard-reference\"\n\"19\",\"google-cloud-colab-enterprise\"\n\"19\",\"laravel-herd\"\n\"19\",\"test-double\"\n\"19\",\"testdrivendesign\"\n\"19\",\"http-auth\"\n\"19\",\"custom-collection\"\n\"19\",\"string-building\"\n\"19\",\"gesturelistener\"\n\"19\",\"resourcestring\"\n\"19\",\"perl-prove\"\n\"19\",\"textformat\"\n\"19\",\"cupertino-widgets\"\n\"19\",\"textpattern\"\n\"19\",\"response.transmitfile\"\n\"19\",\"text-to-html\"\n\"19\",\"periscope\"\n\"19\",\"elasticsearch-snapshot\"\n\"19\",\"textual\"\n\"19\",\"resque-retry\"\n\"19\",\"angular2-styleguide\"\n\"19\",\"rest-firebase\"\n\"19\",\"zyte\"\n\"19\",\"leaky-abstraction\"\n\"19\",\"angular-bootstrap-calendar\"\n\"19\",\"command-execution\"\n\"19\",\"acrylic-material\"\n\"19\",\"getfileversion\"\n\"19\",\"learning-locker\"\n\"19\",\"action-caching\"\n\"19\",\"actionhero\"\n\"19\",\"angularjs-events\"\n\"19\",\"angularjs-decorator\"\n\"19\",\"pending-transition\"\n\"19\",\"actionsheetpicker\"\n\"19\",\"pytype\"\n\"19\",\"react-date-range\"\n\"19\",\"lotus-wcm\"\n\"19\",\"project-navigator\"\n\"19\",\"pythreejs\"\n\"19\",\"react-canvas\"\n\"19\",\"collectionbase\"\n\"19\",\"lorenz-system\"\n\"19\",\"office-dialog-api\"\n\"19\",\"lsm-tree\"\n\"19\",\"promiscuous-mode\"\n\"19\",\"event-channel\"\n\"19\",\"character-codes\"\n\"19\",\"lorem-ipsum\"\n\"19\",\"propertychangesupport\"\n\"19\",\"qcursor\"\n\"19\",\"propertypath\"\n\"19\",\"moviecliploader\"\n\"19\",\"loopingselector\"\n\"19\",\"qsa\"\n\"19\",\"qformlayout\"\n\"19\",\"odoo-enterprise\"\n\"19\",\"react-google-places-autocomplete\"\n\"19\",\"merge-file\"\n\"19\",\"chainable\"\n\"19\",\"evaluation-function\"\n\"19\",\"react-graph-vis\"\n\"19\",\"longhorn\"\n\"19\",\"spectator\"\n\"19\",\"cfstoredproc\"\n\"19\",\"memory-sanitizer\"\n\"19\",\"qprogressdialog\"\n\"19\",\"cgrectintersectsrect\"\n\"19\",\"qore\"\n\"19\",\"vaadin-fusion\"\n\"19\",\"imagecreatefromjpg\"\n\"19\",\"webnfc\"\n\"19\",\"conferencing\"\n\"19\",\"zope.component\"\n\"19\",\"mediaprojectionmanager\"\n\"19\",\"flux-machine-learning\"\n\"19\",\"google-ios-vision\"\n\"19\",\"webkit-transition\"\n\"19\",\"emacs-ediff\"\n\"19\",\"heroku-review-app\"\n\"19\",\"linea-pro\"\n\"19\",\"conditional-attribute\"\n\"19\",\"avatar-generation\"\n\"19\",\"subquery-factoring\"\n\"19\",\"sortdirection\"\n\"19\",\"zipfoundation\"\n\"19\",\"ijetty\"\n\"19\",\"startup-folder\"\n\"19\",\"maven-indexer\"\n\"19\",\"image.createimage\"\n\"19\",\"web-folders\"\n\"19\",\"dart-stream\"\n\"19\",\"here-ios\"\n\"19\",\"linemanjs\"\n\"19\",\"flutter-reorderable-listview\"\n\"19\",\"quantumgrid\"\n\"19\",\"starling-server\"\n\"19\",\"strtoull\"\n\"19\",\"md-card\"\n\"19\",\"yui-menu\"\n\"19\",\"cvlib\"\n\"19\",\"bell-curve\"\n\"19\",\"search-engine-api\"\n\"19\",\"automatic-mixed-precision\"\n\"19\",\"gloo\"\n\"19\",\"haskell-ffi\"\n\"19\",\"automatic-variable\"\n\"19\",\"autocreate\"\n\"19\",\"benthos\"\n\"19\",\"glip\"\n\"19\",\"sedna\"\n\"19\",\"seektotime\"\n\"19\",\"hasura-jwt\"\n\"19\",\"argument-matcher\"\n\"19\",\"git-secret\"\n\"19\",\"mqx\"\n\"19\",\"stumbleupon\"\n\"19\",\"compile-static\"\n\"19\",\"mediacenter\"\n\"19\",\"ietf\"\n\"19\",\"trailing-newline\"\n\"19\",\"parquet.net\"\n\"19\",\"bgiframe\"\n\"19\",\"autoquery-servicestack\"\n\"19\",\"maxstringcontentlength\"\n\"19\",\"google-maps-ios-utils\"\n\"19\",\"solid\"\n\"19\",\"traefik-authentication\"\n\"19\",\"sonarqube-plugin\"\n\"19\",\"bicep\"\n\"19\",\"google-loader\"\n\"19\",\"archive-tar\"\n\"19\",\"solrclient\"\n\"19\",\"uwb\"\n\"19\",\"arbre\"\n\"19\",\"quantconnect\"\n\"19\",\"powerpoint-interop\"\n\"19\",\"shdocvw.internetexplorer\"\n\"19\",\"zfdoctrine\"\n\"18\",\"locallib\"\n\"18\",\"markov-random-fields\"\n\"18\",\"class-attribute\"\n\"18\",\"ecb-pattern\"\n\"18\",\"intercom.js\"\n\"18\",\"dce\"\n\"18\",\"matmul\"\n\"18\",\"backfire\"\n\"18\",\"antlrv3ide\"\n\"18\",\"loadmask\"\n\"18\",\"graphql-ws\"\n\"18\",\"grobid\"\n\"18\",\"prisma-binding\"\n\"18\",\"load-link-store-conditional\"\n\"18\",\"yellow-pages\"\n\"18\",\"flex-charts\"\n\"18\",\"groovy++\"\n\"18\",\"ebay-net-sdk\"\n\"18\",\"pgtap\"\n\"18\",\"sly-scroller\"\n\"18\",\"feature-tracking\"\n\"18\",\"clusterpoint\"\n\"18\",\"slt\"\n\"18\",\"react-pagination\"\n\"18\",\"remote-mysql\"\n\"18\",\"process-mining\"\n\"18\",\"jellyfin\"\n\"18\",\"wiegand\"\n\"18\",\"flourishlib\"\n\"18\",\"fluid-mac-app-engine\"\n\"18\",\"teamcity-5.0\"\n\"18\",\"vuejs-transition-group\"\n\"18\",\"jdeveloper-11g\"\n\"18\",\"ansi-nulls\"\n\"18\",\"primary-interop-assembly\"\n\"18\",\"jdoodle\"\n\"18\",\"ddt\"\n\"18\",\"react-native-sensors\"\n\"18\",\"stackunderflow\"\n\"18\",\"weak-entity\"\n\"18\",\"apache-commons-dateutils\"\n\"18\",\"jbake\"\n\"18\",\"tree-rotation\"\n\"18\",\"insightly\"\n\"18\",\"ggstatsplot\"\n\"18\",\"phpstorm-2016.3\"\n\"18\",\"instagramapi-mgp25\"\n\"18\",\"apache-arrow-cpp\"\n\"18\",\"standardanalyzer\"\n\"18\",\"tensorflow-extended\"\n\"18\",\"php-toolkit\"\n\"18\",\"treeline\"\n\"18\",\"wheelcollider\"\n\"18\",\"transplant\"\n\"18\",\"gfsh\"\n\"18\",\"tree-grammar\"\n\"18\",\"telerik-window\"\n\"18\",\"fbrequest-form\"\n\"18\",\"cloudant-sdp\"\n\"18\",\"reportviewerformvc\"\n\"18\",\"citrus-engine\"\n\"18\",\"mxunit\"\n\"18\",\"whitesource\"\n\"18\",\"gino\"\n\"18\",\"travis-ci-api\"\n\"18\",\"triple-equals\"\n\"18\",\"reportico\"\n\"18\",\"reportdocument\"\n\"18\",\"php-opencloud\"\n\"18\",\"github-services\"\n\"18\",\"gifsicle\"\n\"18\",\"sizehint\"\n\"18\",\"backup-sqldatabase\"\n\"18\",\"clbeacon\"\n\"18\",\"size-reduction\"\n\"18\",\"jconfirm\"\n\"18\",\"telerik-editor\"\n\"18\",\"ecmascript-temporal\"\n\"18\",\"repast-hpc\"\n\"18\",\"bangla-font\"\n\"18\",\"clcircularregion\"\n\"18\",\"wid\"\n\"18\",\"pageasynctask\"\n\"18\",\"pagecontext\"\n\"18\",\"bin-folder\"\n\"18\",\"freewalljs\"\n\"18\",\"firefox2\"\n\"18\",\"frontpage-extensions\"\n\"18\",\"manuals\"\n\"18\",\"cast-iron\"\n\"18\",\"syncfusion-blazor-sfgrid\"\n\"18\",\"constinit\"\n\"18\",\"aerospike-loader\"\n\"18\",\"bindingflags\"\n\"18\",\"firefox6\"\n\"18\",\"python-anyio\"\n\"18\",\"langchain4j\"\n\"18\",\"xdcr\"\n\"18\",\"xml-rpc.net\"\n\"18\",\"nexus-js\"\n\"18\",\"pageshow\"\n\"18\",\"uiviewanimation-curve\"\n\"18\",\"uiwindowscene\"\n\"18\",\"app-ads.txt\"\n\"18\",\"chrome-aws-lambda\"\n\"18\",\"python-redmine\"\n\"18\",\"nftw\"\n\"18\",\"ng2-completer\"\n\"18\",\"pixabay\"\n\"18\",\"apache-torque\"\n\"18\",\"ccscrollview\"\n\"18\",\"fxsl\"\n\"18\",\"ultratree\"\n\"18\",\"freshbooks-api\"\n\"18\",\"fxcop-customrules\"\n\"18\",\"fullscreenchange\"\n\"18\",\"python-phonenumber\"\n\"18\",\"nginx-cache\"\n\"18\",\"connect-direct\"\n\"18\",\"celery-canvas\"\n\"18\",\"python-pbr\"\n\"18\",\"jsr311\"\n\"18\",\"iminuit\"\n\"18\",\"unidecoder\"\n\"18\",\"conic-gradients\"\n\"18\",\"mapfish\"\n\"18\",\"makemessages\"\n\"18\",\"smartxls\"\n\"18\",\"impacket\"\n\"18\",\"confluent-rest-proxy\"\n\"18\",\"cdsw\"\n\"18\",\"unfiltered\"\n\"18\",\"first-level-cache\"\n\"18\",\"first-normal-form\"\n\"18\",\"discountasp\"\n\"18\",\"biztalk-schemas\"\n\"18\",\"catalystcloudscale\"\n\"18\",\"bizagi\"\n\"18\",\"disconnected-session\"\n\"18\",\"flatlaf\"\n\"18\",\"jsonfx\"\n\"18\",\"syck\"\n\"18\",\"imx7\"\n\"18\",\"bittorrent-sync\"\n\"18\",\"smips\"\n\"18\",\"bitsets\"\n\"18\",\"symbolicc++\"\n\"18\",\"fselector\"\n\"18\",\"sming\"\n\"18\",\"kusto-java-sdk\"\n\"18\",\"chronological\"\n\"18\",\"app-engine-modules\"\n\"18\",\"flask-graphql\"\n\"18\",\"caucho\"\n\"18\",\"bitmapencoder\"\n\"18\",\"causalml\"\n\"18\",\"pjl\"\n\"18\",\"lamemp3\"\n\"18\",\"chef-attributes\"\n\"18\",\"filesystemexception\"\n\"18\",\"firecracker\"\n\"18\",\"python-sql\"\n\"18\",\"vscode-calva\"\n\"18\",\"dbca\"\n\"18\",\"jsonpath-plus\"\n\"18\",\"php-webdriver\"\n\"18\",\"ximea\"\n\"18\",\"index.html\"\n\"18\",\"symfony-serializer\"\n\"18\",\"docker-selenium\"\n\"18\",\"faulted\"\n\"18\",\"rstudio-connect\"\n\"18\",\"inet-ntop\"\n\"18\",\"cakephp-3.5\"\n\"18\",\"jfreechart-fx\"\n\"18\",\"rspec-api-documentation\"\n\"18\",\"jfreereport\"\n\"18\",\"shared-module\"\n\"18\",\"jgap\"\n\"18\",\"docker-cp\"\n\"18\",\"sharedsizegroup\"\n\"18\",\"falsy\"\n\"18\",\"sharelatex\"\n\"18\",\"verificationexception\"\n\"18\",\"docassemble\"\n\"18\",\"rsample\"\n\"18\",\"jmesa\"\n\"18\",\"venia\"\n\"18\",\"rubygame\"\n\"18\",\"rqda\"\n\"18\",\"dmalloc\"\n\"18\",\"camel-rest\"\n\"18\",\"dmake\"\n\"18\",\"capture-list\"\n\"18\",\"variable-binding\"\n\"18\",\"camera-intrinsics\"\n\"18\",\"join-hints\"\n\"18\",\"camus\"\n\"18\",\"agora-cloud-recording\"\n\"18\",\"wildfly-cluster\"\n\"18\",\"crossdomain-request.js\"\n\"18\",\"cross-device\"\n\"18\",\"wildfly-maven-plugin\"\n\"18\",\"hyperledger-fabric-orderer\"\n\"18\",\"nanobind\"\n\"18\",\"micronaut-openapi\"\n\"18\",\"reconcile\"\n\"18\",\"akka-fsm\"\n\"18\",\"updatebatchsize\"\n\"18\",\"django-jet\"\n\"18\",\"mybinder\"\n\"18\",\"gopherjs\"\n\"18\",\"google-widget\"\n\"18\",\"ora-12899\"\n\"18\",\"unstage\"\n\"18\",\"real-time-java\"\n\"18\",\"session-keys\"\n\"18\",\"dask-jobqueue\"\n\"18\",\"django-openid-auth\"\n\"18\",\"servlet-dispatching\"\n\"18\",\"airsim\"\n\"18\",\"ora-02291\"\n\"18\",\"named-constructor\"\n\"18\",\"readonly-variable\"\n\"18\",\"kephas\"\n\"18\",\"serverless-framework-offline\"\n\"18\",\"real-ip\"\n\"18\",\"rocket-u2\"\n\"18\",\"metismenu\"\n\"18\",\"unowned-references\"\n\"18\",\"key-rotation\"\n\"18\",\"purifycss\"\n\"18\",\"database-inspector\"\n\"18\",\"metawidget\"\n\"18\",\"servicedcomponent\"\n\"18\",\"mysql-error-1364\"\n\"18\",\"unobserved-exception\"\n\"18\",\"ibm-jsf\"\n\"18\",\"ora-01843\"\n\"18\",\"django-socketio\"\n\"18\",\"django-hstore\"\n\"18\",\"database-tools\"\n\"18\",\"microsoft.extensions.hosting\"\n\"18\",\"icmpv6\"\n\"18\",\"upsolver\"\n\"18\",\"akka.net-streams\"\n\"18\",\"wallaby\"\n\"18\",\"django-localflavor\"\n\"18\",\"graalpython\"\n\"18\",\"rotor\"\n\"18\",\"agroal\"\n\"18\",\"crossmint\"\n\"18\",\"op-tee\"\n\"18\",\"mysql-parameter\"\n\"18\",\"angular-scully\"\n\"18\",\"gotw\"\n\"18\",\"keycloak-admin-cli\"\n\"18\",\"angular-masonry\"\n\"18\",\"oracle-apex-23\"\n\"18\",\"narayana\"\n\"18\",\"kiicloud\"\n\"18\",\"wakatime\"\n\"18\",\"angular-package-format\"\n\"18\",\"rmariadb\"\n\"18\",\"upsizing\"\n\"18\",\"routify\"\n\"18\",\"icann\"\n\"18\",\"oracle-application-server\"\n\"18\",\"django-stubs\"\n\"18\",\"craueformflow\"\n\"18\",\"modula-2\"\n\"18\",\"apple-expose\"\n\"18\",\"spring-boot-starter-parent\"\n\"18\",\"hoverfly\"\n\"18\",\"innobackupex\"\n\"18\",\"pbjvision\"\n\"18\",\"app-signing\"\n\"18\",\"suppressfinalize\"\n\"18\",\"html-dataset\"\n\"18\",\"popperjs\"\n\"18\",\"openfx\"\n\"18\",\"peakutils\"\n\"18\",\"nsdocktile\"\n\"18\",\"paysafe\"\n\"18\",\"postgresql-initdb\"\n\"18\",\"easyphp-devserver\"\n\"18\",\"sap-cloud-connector\"\n\"18\",\"dynamics-crm-3\"\n\"18\",\"surveyor-gem\"\n\"18\",\"swagger-tools\"\n\"18\",\"wsdl4j\"\n\"18\",\"nest-asyncio\"\n\"18\",\"dynamic-data-masking\"\n\"18\",\"blazor-bootstrap\"\n\"18\",\"pcx\"\n\"18\",\"survivejs\"\n\"18\",\"apple-business-manager\"\n\"18\",\"juel\"\n\"18\",\"pdftoppm\"\n\"18\",\"jupyterdash\"\n\"18\",\"booking.com-api\"\n\"18\",\"wse2.0\"\n\"18\",\"nestjs-exception-filters\"\n\"18\",\"ncks\"\n\"18\",\"ontouchstart\"\n\"18\",\"polymorphic-variants\"\n\"18\",\"samsung-gear-fit\"\n\"18\",\"android-tap-and-pay\"\n\"18\",\"nsitemprovider\"\n\"18\",\"info-hash\"\n\"18\",\"intersection-types\"\n\"18\",\"sbt-buildinfo\"\n\"18\",\"nslinguistictagger\"\n\"18\",\"guest-executable\"\n\"18\",\"delta-index\"\n\"18\",\"apple-model-io\"\n\"18\",\"mongodb-csfle\"\n\"18\",\"nerdcommenter\"\n\"18\",\"superslim\"\n\"18\",\"modx-getresources\"\n\"18\",\"n-quads\"\n\"18\",\"azerty-keyboard\"\n\"18\",\"worklight-mbs\"\n\"18\",\"inputsimulator\"\n\"18\",\"boot-animation\"\n\"18\",\"potrace\"\n\"18\",\"azapi\"\n\"18\",\"gtkscrolledwindow\"\n\"18\",\"dynamic365\"\n\"18\",\"payfast\"\n\"18\",\"portable-python\"\n\"18\",\"dynamics-365-ce-onpremises\"\n\"18\",\"inexact-arithmetic\"\n\"18\",\"jvx\"\n\"18\",\"dutch-national-flag-problem\"\n\"18\",\"entityresolver\"\n\"18\",\"nsprintinfo\"\n\"18\",\"bleach\"\n\"18\",\"application-singleton\"\n\"18\",\"coypu\"\n\"18\",\"mongobee\"\n\"18\",\"paypal-permissions\"\n\"18\",\"gulp-filter\"\n\"18\",\"mongoose-os\"\n\"18\",\"workflow-rehosting\"\n\"18\",\"grunt-concurrent\"\n\"18\",\"hound\"\n\"18\",\"infura\"\n\"18\",\"model-mommy\"\n\"18\",\"gulp-eslint\"\n\"18\",\"mimosa\"\n\"18\",\"nwpathmonitor\"\n\"18\",\"libhdfs\"\n\"18\",\"microsoft-odata\"\n\"18\",\"virtualdub\"\n\"18\",\"facebook-game-groups\"\n\"18\",\"virtuozzo\"\n\"18\",\"rcloud\"\n\"18\",\"system.net.websockets\"\n\"18\",\"java-resources\"\n\"18\",\"rational-unified-process\"\n\"18\",\"rangeseekbar\"\n\"18\",\"wix5\"\n\"18\",\"codecharge\"\n\"18\",\"netflix-nebula-plugins\"\n\"18\",\"freelancer.com-api\"\n\"18\",\"ueberauth\"\n\"18\",\"otapi\"\n\"18\",\"visx\"\n\"18\",\"krypton\"\n\"18\",\"kotlin-generics\"\n\"18\",\"android-api-34\"\n\"18\",\"aurelia-auth\"\n\"18\",\"r-forge\"\n\"18\",\"overhead-minimization\"\n\"18\",\"ochamcrest\"\n\"18\",\"cockroachcloud\"\n\"18\",\"pyeda\"\n\"18\",\"minimist\"\n\"18\",\"external-secrets-operator\"\n\"18\",\"ounit\"\n\"18\",\"java-ffm\"\n\"18\",\"rightscale\"\n\"18\",\"pysphere\"\n\"18\",\"rateyo\"\n\"18\",\"free-command\"\n\"18\",\"authorization-server\"\n\"18\",\"go-libp2p\"\n\"18\",\"pyml\"\n\"18\",\"razorpay-andoid-sdk\"\n\"18\",\"r-base-graphics\"\n\"18\",\"cocotron\"\n\"18\",\"viewaction\"\n\"18\",\"pysmb\"\n\"18\",\"libvips\"\n\"18\",\"wkurlschemehandler\"\n\"18\",\"kotlinx-html\"\n\"18\",\"outgoing-call\"\n\"18\",\"synthetica\"\n\"18\",\"libusb-win32\"\n\"18\",\"facebook-custom-audience\"\n\"18\",\"pymesh\"\n\"18\",\"winsnmp\"\n\"18\",\"f#-fake-4\"\n\"18\",\"sfhfkeychainutils\"\n\"18\",\"pygmo\"\n\"18\",\"table-driven\"\n\"18\",\"cognitive-complexity\"\n\"18\",\"objectspace\"\n\"18\",\"ortc\"\n\"18\",\"siemens-nx\"\n\"18\",\"java-sealed-type\"\n\"18\",\"lets-plot\"\n\"18\",\"pyqtchart\"\n\"18\",\"coin3d\"\n\"18\",\"objectsize\"\n\"18\",\"microsoft-live-connect\"\n\"18\",\"python-2.2\"\n\"18\",\"java-test-fixtures\"\n\"18\",\"jasmine-ajax\"\n\"18\",\"octest\"\n\"18\",\"wordpress-block-theme\"\n\"18\",\"macrabbit-espresso\"\n\"18\",\"rawrabbit\"\n\"18\",\"orderbook\"\n\"18\",\"object-class\"\n\"18\",\"system.web.http\"\n\"18\",\"rc-slider\"\n\"18\",\"abas\"\n\"18\",\"anchor-cms\"\n\"18\",\"andar\"\n\"18\",\"tablehtml\"\n\"18\",\"shopify-cli\"\n\"18\",\"codelyzer\"\n\"18\",\"pygame-menu\"\n\"18\",\"setediting\"\n\"18\",\"shinymanager\"\n\"18\",\"wordfence\"\n\"18\",\"viber-bot-python\"\n\"18\",\"pyi\"\n\"18\",\"raptor\"\n\"18\",\"for-range\"\n\"18\",\"oaep\"\n\"18\",\"buddyboss\"\n\"18\",\"vldb\"\n\"18\",\"rapidfuzz\"\n\"18\",\"at-sign\"\n\"18\",\"bucket4j\"\n\"18\",\"luaxml\"\n\"18\",\"vexflow\"\n\"18\",\"ispc\"\n\"18\",\"npm-login\"\n\"18\",\"qwebengine\"\n\"18\",\"mjsip\"\n\"18\",\"mixim\"\n\"18\",\"uicontextmenuinteraction\"\n\"18\",\"excel-writer-xlsx\"\n\"18\",\"r3-gui\"\n\"18\",\"sql-server-7\"\n\"18\",\"opengl-es-lighting\"\n\"18\",\"x10-language\"\n\"18\",\"nps\"\n\"18\",\"plotgooglemaps\"\n\"18\",\"controllercontext\"\n\"18\",\"qvt\"\n\"18\",\"is-same\"\n\"18\",\"notifyjs\"\n\"18\",\"android-gradle-7.0\"\n\"18\",\"android-gradle-3.1.0\"\n\"18\",\"executionexception\"\n\"18\",\"hinstance\"\n\"18\",\"dronedeploy\"\n\"18\",\"uigravitybehavior\"\n\"18\",\"sql-max\"\n\"18\",\"sceptre\"\n\"18\",\"itunes-api\"\n\"18\",\"plutus\"\n\"18\",\"droidscript\"\n\"18\",\"android-downloadable-fonts\"\n\"18\",\"android-displaymanager\"\n\"18\",\"sqlite-browser\"\n\"18\",\"raddocking\"\n\"18\",\"dry-run\"\n\"18\",\"mod-cache\"\n\"18\",\"quip\"\n\"18\",\"android-concatadapter\"\n\"18\",\"tal\"\n\"18\",\"model-driven-app\"\n\"18\",\"jackson-module-scala\"\n\"18\",\"nonclient\"\n\"18\",\"uikit-transitions\"\n\"18\",\"expectj\"\n\"18\",\"openseamap\"\n\"18\",\"point-to-point\"\n\"18\",\"jain-slee\"\n\"18\",\"openstack4j\"\n\"18\",\"poll-syscall\"\n\"18\",\"openser\"\n\"18\",\"dot-notation\"\n\"18\",\"sciruby\"\n\"18\",\"hmvc-codeigniter\"\n\"18\",\"tkintermapview\"\n\"18\",\"expo-calendar\"\n\"18\",\"tkinter-photoimage\"\n\"18\",\"mkusertrackingmode\"\n\"18\",\"nsxmlparsererrordomain\"\n\"18\",\"coolite\"\n\"18\",\"gclient\"\n\"18\",\"ntruencrypt\"\n\"18\",\"numeric-textbox\"\n\"18\",\"azuremapscontrol\"\n\"18\",\"gcc4.6\"\n\"18\",\"mobile-emulator\"\n\"18\",\"nuget-update\"\n\"18\",\"azure-management-portal\"\n\"18\",\"ca65\"\n\"18\",\"difference-equations\"\n\"18\",\"mmppf\"\n\"18\",\"asp-net-config-builders\"\n\"18\",\"dput\"\n\"18\",\"didfailwitherror\"\n\"18\",\"mockgoose\"\n\"18\",\"mobile-security\"\n\"18\",\"numbered\"\n\"18\",\"lparam\"\n\"18\",\"customising\"\n\"18\",\"stream-management\"\n\"18\",\"streamparse\"\n\"18\",\"euiccmanager\"\n\"18\",\"httpbuilder-ng\"\n\"18\",\"char-traits\"\n\"18\",\"latte\"\n\"18\",\"es6-generator\"\n\"18\",\"actionmailer.net\"\n\"18\",\"ode-library\"\n\"18\",\"string-externalization\"\n\"18\",\"text-database\"\n\"18\",\"lazy-io\"\n\"18\",\"resource-governor\"\n\"18\",\"mender\"\n\"18\",\"ls2j\"\n\"18\",\"csstidy\"\n\"18\",\"resource-utilization\"\n\"18\",\"metacircular\"\n\"18\",\"nilearn\"\n\"18\",\"duplicate-detection\"\n\"18\",\"eslint-plugin-import\"\n\"18\",\"actionbuilder\"\n\"18\",\"actionable-notification\"\n\"18\",\"qcolordialog\"\n\"18\",\"elasticui\"\n\"18\",\"generatestaticparams\"\n\"18\",\"mercurial-keyring\"\n\"18\",\"android-room-migration\"\n\"18\",\"message-bundle\"\n\"18\",\"messageboard\"\n\"18\",\"qscopedpointer\"\n\"18\",\"getlocation\"\n\"18\",\"irix\"\n\"18\",\"android-priority-jobqueue\"\n\"18\",\"einsum\"\n\"18\",\"elasticsearch-2.4\"\n\"18\",\"iron-list\"\n\"18\",\"zxspectrum\"\n\"18\",\"merge-strategy\"\n\"18\",\"ctfont\"\n\"18\",\"iphone-xr\"\n\"18\",\"esri-oss\"\n\"18\",\"perch\"\n\"18\",\"launcher-icon\"\n\"18\",\"elasticjs\"\n\"18\",\"iplanet\"\n\"18\",\"motionbuilder\"\n\"18\",\"oledragdrop\"\n\"18\",\"communication-diagram\"\n\"18\",\"proxyfactory\"\n\"18\",\"react-native-button\"\n\"18\",\"monomorphism\"\n\"18\",\"mongrel2\"\n\"18\",\"cflogin\"\n\"18\",\"react-native-collapsible\"\n\"18\",\"node.io\"\n\"18\",\"cfftp\"\n\"18\",\"cfdictionary\"\n\"18\",\"splitbrain\"\n\"18\",\"laravel-livewire-wireclick\"\n\"18\",\"okvs\"\n\"18\",\"loop-counter\"\n\"18\",\"monstache\"\n\"18\",\"node-jose\"\n\"18\",\"chamilo-lms\"\n\"18\",\"spotify-desktop\"\n\"18\",\"centrify\"\n\"18\",\"google-cloud-profiler\"\n\"18\",\"ohif\"\n\"18\",\"sp-rename\"\n\"18\",\"google-cloud-network-load-balancer\"\n\"18\",\"locbaml\"\n\"18\",\"moonsharp\"\n\"18\",\"nodebox\"\n\"18\",\"stoplight\"\n\"18\",\"http-trace\"\n\"18\",\"excel-2002\"\n\"18\",\"terser-webpack-plugin\"\n\"18\",\"prometheus-net\"\n\"18\",\"reaction-commerce\"\n\"18\",\"pf-ring\"\n\"18\",\"spectral-clustering\"\n\"18\",\"pf4j\"\n\"18\",\"projekktor\"\n\"18\",\"project-types\"\n\"18\",\"httpretty\"\n\"18\",\"petite-vue\"\n\"18\",\"ios-privacy-settings\"\n\"18\",\"cfwebsocket\"\n\"18\",\"resharper-7.0\"\n\"18\",\"reactive-feign-client\"\n\"18\",\"hex-pm\"\n\"18\",\"google-postmaster\"\n\"18\",\"gnip\"\n\"18\",\"gmongo\"\n\"18\",\"qt-mfc-migration\"\n\"18\",\"mde\"\n\"18\",\"zopim\"\n\"18\",\"amazon-ion\"\n\"18\",\"mssticker\"\n\"18\",\"glue-crawler\"\n\"18\",\"alias-method-chain\"\n\"18\",\"compressed-files\"\n\"18\",\"ember-cli-rails\"\n\"18\",\"maven-plugin-development\"\n\"18\",\"link-checking\"\n\"18\",\"maven-pdf-plugin\"\n\"18\",\"heap-analytics\"\n\"18\",\"google-license-manager\"\n\"18\",\"foldable-devices\"\n\"18\",\"emacs-jedi\"\n\"18\",\"flutter-native\"\n\"18\",\"here-traffic\"\n\"18\",\"maven-package\"\n\"18\",\"maxdb\"\n\"18\",\"hasp\"\n\"18\",\"ember.js-3\"\n\"18\",\"git-sign\"\n\"18\",\"concatenative-language\"\n\"18\",\"mbrola\"\n\"18\",\"concept-insights\"\n\"18\",\"mediabrowser\"\n\"18\",\"bevelled\"\n\"18\",\"media-buttons\"\n\"18\",\"maven-gatling-plugin\"\n\"18\",\"maven-glassfish-plugin\"\n\"18\",\"email-analytics\"\n\"18\",\"mui\"\n\"18\",\"lighthouse-ci\"\n\"18\",\"alternateview\"\n\"18\",\"allen-sdk\"\n\"18\",\"concourse-resource-types\"\n\"18\",\"complex-upset\"\n\"18\",\"multi-camera-api\"\n\"18\",\"concourse-fly\"\n\"18\",\"articulate\"\n\"18\",\"vaadin20\"\n\"18\",\"seam-carving\"\n\"18\",\"tidekit\"\n\"18\",\"sourcegraph\"\n\"18\",\"yui-grids\"\n\"18\",\"webextension-polyfill\"\n\"18\",\"parallel-ssh\"\n\"18\",\"d3pie.js\"\n\"18\",\"solus\"\n\"18\",\"autoproxy\"\n\"18\",\"zio-streams\"\n\"18\",\"autoeventwireup\"\n\"18\",\"search-dialog\"\n\"18\",\"theme-daynight\"\n\"18\",\"cypress-component-testing\"\n\"18\",\"status-register\"\n\"18\",\"startprocessinfo\"\n\"18\",\"shelly\"\n\"18\",\"prefixfree\"\n\"18\",\"dangerous-request\"\n\"18\",\"staticfilehandler\"\n\"18\",\"static-generator\"\n\"18\",\"araxis\"\n\"18\",\"thruk\"\n\"18\",\"tfs-to-tfs-migration-tool\"\n\"18\",\"user-customization\"\n\"18\",\"sdcalertview\"\n\"18\",\"source-server\"\n\"18\",\"particle.io\"\n\"18\",\"shell-trap\"\n\"18\",\"preroll\"\n\"18\",\"endlessadapter\"\n\"18\",\"tidytable\"\n\"18\",\"struts2-s2hibernate\"\n\"18\",\"tilesets\"\n\"18\",\"preconnect\"\n\"18\",\"args4j\"\n\"18\",\"usability-testing\"\n\"18\",\"scrollable-table\"\n\"18\",\"passenger-apache\"\n\"18\",\"medusa\"\n\"18\",\"ms-reports\"\n\"18\",\"securid\"\n\"18\",\"link-to-function\"\n\"18\",\"tpkeyboardavoiding\"\n\"18\",\"stencyl\"\n\"18\",\"autocommand\"\n\"17\",\"annoy\"\n\"17\",\"relative-time-span\"\n\"17\",\"representable\"\n\"17\",\"tensorlayer\"\n\"17\",\"weakly-typed\"\n\"17\",\"ansicon\"\n\"17\",\"jenkins-generic-webhook-trigger\"\n\"17\",\"graphengine\"\n\"17\",\"cjuidialog\"\n\"17\",\"webpart-connection\"\n\"17\",\"jenkins-mstest\"\n\"17\",\"flutter-charts\"\n\"17\",\"stamplay\"\n\"17\",\"clog\"\n\"17\",\"vue3-sfc-loader\"\n\"17\",\"backbone.paginator\"\n\"17\",\"clustal\"\n\"17\",\"primefaces-push\"\n\"17\",\"slather\"\n\"17\",\"telerik-blazor\"\n\"17\",\"xstate-react\"\n\"17\",\"groovlet\"\n\"17\",\"clojure-repl\"\n\"17\",\"ggthemes\"\n\"17\",\"react-native-native-ui-component\"\n\"17\",\"webpack-serve\"\n\"17\",\"skywalking\"\n\"17\",\"primefaces-dataexporter\"\n\"17\",\"default-template-argument\"\n\"17\",\"youku\"\n\"17\",\"defaultstyleddocument\"\n\"17\",\"multiple-cursor\"\n\"17\",\"git-ls-remote\"\n\"17\",\"xtabs\"\n\"17\",\"react-native-pager-view\"\n\"17\",\"treasure-data\"\n\"17\",\"stacktrace.js\"\n\"17\",\"vue-server-renderer\"\n\"17\",\"ckeditor4\"\n\"17\",\"termux-linux\"\n\"17\",\"jaxl\"\n\"17\",\"cl-who\"\n\"17\",\"whmcs-invoice-template\"\n\"17\",\"skrill\"\n\"17\",\"priority-inversion\"\n\"17\",\"squarify\"\n\"17\",\"php-mode\"\n\"17\",\"deepstream\"\n\"17\",\"effector\"\n\"17\",\"list-processing\"\n\"17\",\"photo-management\"\n\"17\",\"jdesktop\"\n\"17\",\"apache-dubbo\"\n\"17\",\"jdk-desugaring\"\n\"17\",\"intel-advisor\"\n\"17\",\"backgroundrb\"\n\"17\",\"wijmo-grid\"\n\"17\",\"interaction-design\"\n\"17\",\"teavm\"\n\"17\",\"grip\"\n\"17\",\"interaction-to-next-paint\"\n\"17\",\"jest-enzyme\"\n\"17\",\"fcbkcomplete\"\n\"17\",\"felgo\"\n\"17\",\"getsystemmetrics\"\n\"17\",\"weak-symbol\"\n\"17\",\"livefyre\"\n\"17\",\"baduk\"\n\"17\",\"anonymous-pipes\"\n\"17\",\"vuetify-loader\"\n\"17\",\"yarn-berry\"\n\"17\",\"wikia\"\n\"17\",\"stackedit\"\n\"17\",\"jemmyfx\"\n\"17\",\"github-advanced-security\"\n\"17\",\"ddlutils\"\n\"17\",\"eddsa\"\n\"17\",\"graphileon\"\n\"17\",\"lisp-2\"\n\"17\",\"flash-10\"\n\"17\",\"function-try-block\"\n\"17\",\"biztalk-services\"\n\"17\",\"function-coverage\"\n\"17\",\"divx\"\n\"17\",\"contentproperty\"\n\"17\",\"imposition\"\n\"17\",\"constraintviolationexception\"\n\"17\",\"api-auth\"\n\"17\",\"pinned-site\"\n\"17\",\"blackberry-torch\"\n\"17\",\"xnet\"\n\"17\",\"fusionreactor\"\n\"17\",\"jsprettier\"\n\"17\",\"imodeljs\"\n\"17\",\"fiware-cep\"\n\"17\",\"overloaded-strings\"\n\"17\",\"contentlayer\"\n\"17\",\"pandapower\"\n\"17\",\"imsl\"\n\"17\",\"incremental-static-regeneration\"\n\"17\",\"celleditingtemplate\"\n\"17\",\"django-activity-stream\"\n\"17\",\"jsonidentityinfo\"\n\"17\",\"financialinstrument\"\n\"17\",\"p8\"\n\"17\",\"unimrcp\"\n\"17\",\"smartview\"\n\"17\",\"snakebite\"\n\"17\",\"imghdr\"\n\"17\",\"pack-uri\"\n\"17\",\"pin-ptr\"\n\"17\",\"kuromoji\"\n\"17\",\"connect-redis\"\n\"17\",\"bindgen\"\n\"17\",\"flannbasedmatcher\"\n\"17\",\"bind2nd\"\n\"17\",\"xip.io\"\n\"17\",\"ftok\"\n\"17\",\"bitmapframe\"\n\"17\",\"pagetabviewstyle\"\n\"17\",\"directus-flows\"\n\"17\",\"bitmapfield\"\n\"17\",\"laravel-environment\"\n\"17\",\"chromebug\"\n\"17\",\"binary-xml\"\n\"17\",\"bitmapeffect\"\n\"17\",\"social-stream\"\n\"17\",\"cdash\"\n\"17\",\"ng-maxlength\"\n\"17\",\"runjettyrun\"\n\"17\",\"rust-language-server\"\n\"17\",\"cheshire\"\n\"17\",\"dawg\"\n\"17\",\"mappath\"\n\"17\",\"ngx-select-dropdown\"\n\"17\",\"datatextfield\"\n\"17\",\"ngreact\"\n\"17\",\"child-objects\"\n\"17\",\"jsweet\"\n\"17\",\"davinci\"\n\"17\",\"jsr299\"\n\"17\",\"chef-client\"\n\"17\",\"adobe-pdf-library\"\n\"17\",\"cbo\"\n\"17\",\"adobe-launch\"\n\"17\",\"ultracombo\"\n\"17\",\"markdowndeep\"\n\"17\",\"ngx-http-rewrite-module\"\n\"17\",\"adium\"\n\"17\",\"socketfactory\"\n\"17\",\"unboundid\"\n\"17\",\"vpi\"\n\"17\",\"carrot\"\n\"17\",\"js-scrollto\"\n\"17\",\"plato\"\n\"17\",\"mapshaper\"\n\"17\",\"chibios\"\n\"17\",\"dateonly\"\n\"17\",\"sentencecase\"\n\"17\",\"ngtools\"\n\"17\",\"adversarial-machines\"\n\"17\",\"vegeta\"\n\"17\",\"angular-load-children\"\n\"17\",\"singer-io\"\n\"17\",\"serverless-stack\"\n\"17\",\"cakephp-helper\"\n\"17\",\"fallbackvalue\"\n\"17\",\"cryptoki\"\n\"17\",\"database-cloning\"\n\"17\",\"camel-spring-dsl\"\n\"17\",\"keyserver\"\n\"17\",\"oracle-cloud-functions\"\n\"17\",\"django-listview\"\n\"17\",\"methodbase\"\n\"17\",\"iclientmessageinspector\"\n\"17\",\"realmrecyclerviewadapter\"\n\"17\",\"oracle-enterprise-linux\"\n\"17\",\"dladdr\"\n\"17\",\"fakexrmeasy\"\n\"17\",\"fakeroot\"\n\"17\",\"windows-95\"\n\"17\",\"microsoft-adal-angular6\"\n\"17\",\"joomla3.8\"\n\"17\",\"data.stackexchange.com\"\n\"17\",\"mysql-error-150\"\n\"17\",\"ibm-pcomm\"\n\"17\",\"windows-build-tools\"\n\"17\",\"metrolog\"\n\"17\",\"real-time-systems\"\n\"17\",\"roots-toolkit\"\n\"17\",\"hypnotoad\"\n\"17\",\"fast-excel\"\n\"17\",\"r-optimization\"\n\"17\",\"uniwebview\"\n\"17\",\"jopendocument\"\n\"17\",\"daru\"\n\"17\",\"creation-pattern\"\n\"17\",\"vdm-sl\"\n\"17\",\"ptrdiff-t\"\n\"17\",\"recent-screens\"\n\"17\",\"windows-embedded-standard\"\n\"17\",\"joy-ui\"\n\"17\",\"ibm-wcm\"\n\"17\",\"rudp\"\n\"17\",\"jpeg-xr\"\n\"17\",\"docker-proxy\"\n\"17\",\"simpletip\"\n\"17\",\"pstcollectionview\"\n\"17\",\"oracle12.2\"\n\"17\",\"unity-remote\"\n\"17\",\"microsoft.office.interop.excel\"\n\"17\",\"kern-invalid-address\"\n\"17\",\"keyboard-python\"\n\"17\",\"crossplane\"\n\"17\",\"jkube\"\n\"17\",\"wait-free\"\n\"17\",\"waitforimages\"\n\"17\",\"keyboard-wedge\"\n\"17\",\"go-xorm\"\n\"17\",\"django-subdomains\"\n\"17\",\"wapiti\"\n\"17\",\"pycorenlp\"\n\"17\",\"gource\"\n\"17\",\"aws-landing-zone\"\n\"17\",\"crossroadsjs\"\n\"17\",\"optaweb-vehicle-routing\"\n\"17\",\"indextank\"\n\"17\",\"wac\"\n\"17\",\"agvtool\"\n\"17\",\"cross-window-scripting\"\n\"17\",\"nastran\"\n\"17\",\"django-supervisor\"\n\"17\",\"warren-abstract-machine\"\n\"17\",\"dojo-1.9\"\n\"17\",\"django-dynamic-scraper\"\n\"17\",\"vash\"\n\"17\",\"go-server\"\n\"17\",\"capedwarf\"\n\"17\",\"akka-dispatcher\"\n\"17\",\"aws-glue3.0\"\n\"17\",\"roassal\"\n\"17\",\"akka-io\"\n\"17\",\"dnsbl\"\n\"17\",\"vuex-module-decorators\"\n\"17\",\"siren\"\n\"17\",\"grafana-agent\"\n\"17\",\"v-btn\"\n\"17\",\"unused-functions\"\n\"17\",\"kendo-draggable\"\n\"17\",\"rsnapshot\"\n\"17\",\"jolie\"\n\"17\",\"joiner\"\n\"17\",\"crunchy-postgresql-operator\"\n\"17\",\"aiokafka\"\n\"17\",\"named-scopes\"\n\"17\",\"fast-ui\"\n\"17\",\"unsafe-unretained\"\n\"17\",\"ruby2d\"\n\"17\",\"ora-01008\"\n\"17\",\"window.crypto\"\n\"17\",\"grails-maven\"\n\"17\",\"mysql-error-1248\"\n\"17\",\"readerquotas\"\n\"17\",\"serviceinstall\"\n\"17\",\"awesome-nested-set\"\n\"17\",\"faults\"\n\"17\",\"docco\"\n\"17\",\"django-floppyforms\"\n\"17\",\"unqlite\"\n\"17\",\"aws-elastictranscoder\"\n\"17\",\"shared-addin\"\n\"17\",\"jquery-selectric\"\n\"17\",\"neo4jrb\"\n\"17\",\"boost-mp11\"\n\"17\",\"swampdragon\"\n\"17\",\"post-format\"\n\"17\",\"postgraphql\"\n\"17\",\"html5-clipboard-api\"\n\"17\",\"bleve\"\n\"17\",\"easyar\"\n\"17\",\"kana\"\n\"17\",\"jwt.io\"\n\"17\",\"design-tokens\"\n\"17\",\"svnx\"\n\"17\",\"k2-blackpearl\"\n\"17\",\"postcss-cli\"\n\"17\",\"apt-key\"\n\"17\",\"nested-fields\"\n\"17\",\"erasure-code\"\n\"17\",\"svnbridge\"\n\"17\",\"internet-explorer-5\"\n\"17\",\"gtk-textbuffer\"\n\"17\",\"jquery-form-wizard\"\n\"17\",\"online-storage\"\n\"17\",\"jquery-mobile-dialog\"\n\"17\",\"tx-dce\"\n\"17\",\"e10s\"\n\"17\",\"sapscript\"\n\"17\",\"twilio-flow\"\n\"17\",\"desktop-background\"\n\"17\",\"bootjack\"\n\"17\",\"oneplustwo\"\n\"17\",\"azure-availability-zones\"\n\"17\",\"mod-speling\"\n\"17\",\"two-step-verification\"\n\"17\",\"swagger-net\"\n\"17\",\"android-virtualdisplay\"\n\"17\",\"azure-batch-account\"\n\"17\",\"onix\"\n\"17\",\"sigv4\"\n\"17\",\"bootsnap\"\n\"17\",\"error-console\"\n\"17\",\"dets\"\n\"17\",\"apple-photos\"\n\"17\",\"bootstrap-confirmation\"\n\"17\",\"silent-post\"\n\"17\",\"turbodbc\"\n\"17\",\"grunt-contrib-coffee\"\n\"17\",\"nrf52840\"\n\"17\",\"applicationwillterminate\"\n\"17\",\"android-scrolling\"\n\"17\",\"ioloop\"\n\"17\",\"opencomputers\"\n\"17\",\"opendolphin\"\n\"17\",\"entity-framework-designer\"\n\"17\",\"jruby-openssl\"\n\"17\",\"dependency-analysis\"\n\"17\",\"inlineuicontainer\"\n\"17\",\"grpc-swift\"\n\"17\",\"workbox-window\"\n\"17\",\"swift-data-modelcontext\"\n\"17\",\"payola\"\n\"17\",\"turi\"\n\"17\",\"passport-github2\"\n\"17\",\"azure-container-app-jobs\"\n\"17\",\"android-sourcesets\"\n\"17\",\"denotational-semantics\"\n\"17\",\"ncqrs\"\n\"17\",\"grunt-ember-templates\"\n\"17\",\"pay-per-click\"\n\"17\",\"aws-site-to-site\"\n\"17\",\"dvi\"\n\"17\",\"dynamiclayout\"\n\"17\",\"tvalue\"\n\"17\",\"nativexml\"\n\"17\",\"kebab-case\"\n\"17\",\"wso2-micro-gateway\"\n\"17\",\"svg-morphing\"\n\"17\",\"easypie\"\n\"17\",\"crc8\"\n\"17\",\"super-linter\"\n\"17\",\"derived-instances\"\n\"17\",\"nsfastenumeration\"\n\"17\",\"boundaries\"\n\"17\",\"bottom-type\"\n\"17\",\"sales-tax\"\n\"17\",\"gulp-uncss\"\n\"17\",\"interruptions\"\n\"17\",\"dynamic-execution\"\n\"17\",\"pclzip\"\n\"17\",\"springloops\"\n\"17\",\"sc.exe\"\n\"17\",\"blue-screen-of-death\"\n\"17\",\"hp-exstream\"\n\"17\",\"moditect\"\n\"17\",\"samsung-internet\"\n\"17\",\"inotify-tools\"\n\"17\",\"ws-i\"\n\"17\",\"scada-ignition\"\n\"17\",\"easy-engine\"\n\"17\",\"blktrace\"\n\"17\",\"application-framework\"\n\"17\",\"polymodel\"\n\"17\",\"virtual-webcam\"\n\"17\",\"amo\"\n\"17\",\"facebook-monetization-manager\"\n\"17\",\"visualstategroup\"\n\"17\",\"javacameraview\"\n\"17\",\"codeigniter-helpers\"\n\"17\",\"frame-grab\"\n\"17\",\"raspistill\"\n\"17\",\".net-4.0-beta-2\"\n\"17\",\"magic-command\"\n\"17\",\"asynctoken\"\n\"17\",\"reversion\"\n\"17\",\"virtualenv-commands\"\n\"17\",\"codeigniter-pagination\"\n\"17\",\"rational-developer-for-i\"\n\"17\",\"typedjs\"\n\"17\",\"google-chrome-android\"\n\"17\",\"luasec\"\n\"17\",\"windows-wpp\"\n\"17\",\"rethinkdb-ruby\"\n\"17\",\"mifos\"\n\"17\",\"rbenv-gemset\"\n\"17\",\"jasmine-spec-reporter\"\n\"17\",\"macaulay2\"\n\"17\",\".net-core-authorization\"\n\"17\",\"external-contenttype\"\n\"17\",\"type-switch\"\n\"17\",\"right-mouse-button\"\n\"17\",\"amsmath\"\n\"17\",\"wordbreaker\"\n\"17\",\"mac-roman\"\n\"17\",\"attrs.xml\"\n\"17\",\"accelerated-c++\"\n\"17\",\"coefficient-of-determination\"\n\"17\",\"lynxos\"\n\"17\",\"pypm\"\n\"17\",\"magento-1.12\"\n\"17\",\"buddy.com\"\n\"17\",\"pyrax\"\n\"17\",\"signalfx\"\n\"17\",\"3d-convolution\"\n\"17\",\"shinythemes\"\n\"17\",\"jansi\"\n\"17\",\"android-asynclistdiffer\"\n\"17\",\"woodstock\"\n\"17\",\"cocostudio\"\n\"17\",\"asyncssh\"\n\"17\",\"facebook-litho\"\n\"17\",\"ripcord\"\n\"17\",\"build-chain\"\n\"17\",\".refresh\"\n\"17\",\"vite-plugin-pwa\"\n\"17\",\"visual-assist-x\"\n\"17\",\"pyre-check\"\n\"17\",\"atoti\"\n\"17\",\"form-generator\"\n\"17\",\"raygun.io\"\n\"17\",\"ocpsoft-rewrite\"\n\"17\",\"shopizer\"\n\"17\",\"dominate\"\n\"17\",\"pygame-mixer\"\n\"17\",\"doophp\"\n\"17\",\"uglifycss\"\n\"17\",\"pygui\"\n\"17\",\"klepto\"\n\"17\",\"kss\"\n\"17\",\"java-synthetic-methods\"\n\"17\",\"known-hosts\"\n\"17\",\"system.json\"\n\"17\",\"viewdeck\"\n\"17\",\"syscache2\"\n\"17\",\"koa.js\"\n\"17\",\"ublock-origin\"\n\"17\",\"minoccurs\"\n\"17\",\"dompi\"\n\"17\",\"do-not-disturb\"\n\"17\",\"donejs\"\n\"17\",\"ocg\"\n\"17\",\"go-github\"\n\"17\",\"uc4\"\n\"17\",\"netadvantage\"\n\"17\",\"lemur\"\n\"17\",\"domain-forwarding\"\n\"17\",\"go-imagick\"\n\"17\",\"librt\"\n\"17\",\"ory-hydra\"\n\"17\",\"nx-angular\"\n\"17\",\"outlook-2011\"\n\"17\",\"nsviewanimation\"\n\"17\",\"xaml-resources\"\n\"17\",\"gcp-config-connector\"\n\"17\",\"dioxus\"\n\"17\",\"cookie-path\"\n\"17\",\"openlayers-8\"\n\"17\",\"openxlsx2\"\n\"17\",\"openmv\"\n\"17\",\"azure-service-fabric-mesh\"\n\"17\",\"screencapturekit\"\n\"17\",\"openstack-python-api\"\n\"17\",\"tiny-core-linux\"\n\"17\",\"nopcommerce-4.2\"\n\"17\",\"numpyro\"\n\"17\",\"dialogbasedapp\"\n\"17\",\"ituneslibrary\"\n\"17\",\"cooliris\"\n\"17\",\"pnunit\"\n\"17\",\"droid-fu\"\n\"17\",\"tangible-t4-editor\"\n\"17\",\"uispec4j\"\n\"17\",\"tiny-slider\"\n\"17\",\"drei\"\n\"17\",\"pocl\"\n\"17\",\"xcode6.3.2\"\n\"17\",\"scout-sass\"\n\"17\",\"pocodynamo\"\n\"17\",\"azure-lab-services\"\n\"17\",\"openwisp\"\n\"17\",\"expect.js\"\n\"17\",\"openstack-api\"\n\"17\",\"mo-cap\"\n\"17\",\"sql-generation\"\n\"17\",\"schemabinding\"\n\"17\",\"mobx-react-form\"\n\"17\",\"hkdf\"\n\"17\",\"nominal-data\"\n\"17\",\"open-search-server\"\n\"17\",\"jacc\"\n\"17\",\"opentools\"\n\"17\",\"tmxtiledmap\"\n\"17\",\"coqui\"\n\"17\",\"uiloader\"\n\"17\",\"sqlbrowser\"\n\"17\",\"spryker\"\n\"17\",\"devextreme-react\"\n\"17\",\"togetherjs\"\n\"17\",\"radpanelbar\"\n\"17\",\"gasp\"\n\"17\",\"ca2202\"\n\"17\",\"gateways\"\n\"17\",\"tabular-editor\"\n\"17\",\"jail-shell\"\n\"17\",\"redstone.dart\"\n\"17\",\"jackson-dataformat-yaml\"\n\"17\",\"hidl\"\n\"17\",\"tabslideout\"\n\"17\",\"higher-order-types\"\n\"17\",\"tktable\"\n\"17\",\"xcode-archive\"\n\"17\",\"expression-encoder-4\"\n\"17\",\"tablib\"\n\"17\",\"number-to-currency\"\n\"17\",\"tlbexp\"\n\"17\",\"copymemory\"\n\"17\",\"nodeselector\"\n\"17\",\"azure-task-groups\"\n\"17\",\"openswoole\"\n\"17\",\"jank\"\n\"17\",\"reg-expressionvalidator\"\n\"17\",\"tcollectionitem\"\n\"17\",\"reference-manual\"\n\"17\",\"haraka\"\n\"17\",\"exception-safe\"\n\"17\",\"dtr\"\n\"17\",\"timeouterror\"\n\"17\",\"vertex-buffer-objects\"\n\"17\",\"itemspresenter\"\n\"17\",\"time-difference\"\n\"17\",\"as-operator\"\n\"17\",\"aspstate\"\n\"17\",\"drupal-alter\"\n\"17\",\"ask-sdk\"\n\"17\",\"pleasewait\"\n\"17\",\"gae-userservice\"\n\"17\",\"tcpportsharing\"\n\"17\",\"verysleepy\"\n\"17\",\"cadquery\"\n\"17\",\"geany-plugin\"\n\"17\",\"gameanalytics\"\n\"17\",\"dialogviewcontroller\"\n\"17\",\"cakefile\"\n\"17\",\"asp.net-core-security\"\n\"17\",\"referential\"\n\"17\",\"q-value\"\n\"17\",\"asp.net-mail\"\n\"17\",\"sqlprofileprovider\"\n\"17\",\"cadence-virtuoso\"\n\"17\",\"itemcollection\"\n\"17\",\"omniverse\"\n\"17\",\"o-d-matrix\"\n\"17\",\"ldc\"\n\"17\",\"cgpathref\"\n\"17\",\"node-glob\"\n\"17\",\"pep3118\"\n\"17\",\"laravel-telescope\"\n\"17\",\"node-deasync\"\n\"17\",\"node-dev\"\n\"17\",\"lazyeval\"\n\"17\",\"leantween\"\n\"17\",\"resty\"\n\"17\",\"eurekalog\"\n\"17\",\"requests-per-second\"\n\"17\",\"ios-contacts\"\n\"17\",\"certificate-signing-request\"\n\"17\",\"espruino\"\n\"17\",\"event-calendar\"\n\"17\",\"iphone7plus\"\n\"17\",\"reservoir-sampling\"\n\"17\",\"response.addheader\"\n\"17\",\"reset-button\"\n\"17\",\"evented-io\"\n\"17\",\"nim-game\"\n\"17\",\"ogdf\"\n\"17\",\"pftableviewcell\"\n\"17\",\"es6-shim\"\n\"17\",\"pfsubclassing\"\n\"17\",\"ninject-3\"\n\"17\",\"ios-messages-extension\"\n\"17\",\"lavaplayer\"\n\"17\",\"chart-director\"\n\"17\",\"curvycorners\"\n\"17\",\"spreedly\"\n\"17\",\"qf-test\"\n\"17\",\"elevatr\"\n\"17\",\"melt-framework\"\n\"17\",\"mosso\"\n\"17\",\"spip\"\n\"17\",\"curvesmoothing\"\n\"17\",\"react-loading-skeleton\"\n\"17\",\"coldfusion-administrator\"\n\"17\",\"stratum\"\n\"17\",\"hyperas\"\n\"17\",\"actorsystem\"\n\"17\",\"moor\"\n\"17\",\"prototype-oriented\"\n\"17\",\"qabstractbutton\"\n\"17\",\"color-tracking\"\n\"17\",\"curtains.js\"\n\"17\",\"terraform-provider-datadog\"\n\"17\",\"acme.sh\"\n\"17\",\"current-principal\"\n\"17\",\"comautomationfactory\"\n\"17\",\"currencymanager\"\n\"17\",\"qdrantclient\"\n\"17\",\"morphline\"\n\"17\",\"mootools-fx\"\n\"17\",\"curlftpfs\"\n\"17\",\"httpfilecollection\"\n\"17\",\"android-restrictions\"\n\"17\",\"android-licenses\"\n\"17\",\"morea-framework\"\n\"17\",\"cua-mode\"\n\"17\",\"dukpt\"\n\"17\",\"google-friend-connect\"\n\"17\",\"elasticsearch-java-api-client\"\n\"17\",\"zxing.net.mobile\"\n\"17\",\"property-testing\"\n\"17\",\"react-button\"\n\"17\",\"qqmllistproperty\"\n\"17\",\"mercurial-revsets\"\n\"17\",\"getclientrect\"\n\"17\",\"common-code\"\n\"17\",\"getconstructor\"\n\"17\",\"logistf\"\n\"17\",\"test-fixture\"\n\"17\",\"lockup\"\n\"17\",\"ternary-search\"\n\"17\",\"log4postsharp\"\n\"17\",\"testinfra\"\n\"17\",\"httptestingcontroller\"\n\"17\",\"metamug\"\n\"17\",\"seandroid\"\n\"17\",\"zeta-components\"\n\"17\",\"parboiled2\"\n\"17\",\"stubby4j\"\n\"17\",\"automapper-11\"\n\"17\",\"cypress-task\"\n\"17\",\"identity-operator\"\n\"17\",\"msloadtest\"\n\"17\",\"std-system-error\"\n\"17\",\"media-keys\"\n\"17\",\"msbuild-buildengine\"\n\"17\",\"lightadmin\"\n\"17\",\"maven-frontend-plugin\"\n\"17\",\"webapplicationstresstool\"\n\"17\",\"gnucash\"\n\"17\",\"google-sheets-vlookup\"\n\"17\",\"git-sync\"\n\"17\",\"sdv\"\n\"17\",\"msbuild-projectreference\"\n\"17\",\"webcontext\"\n\"17\",\"bernoulli-numbers\"\n\"17\",\"automapper-7\"\n\"17\",\"tile-engine\"\n\"17\",\"argo-rollouts\"\n\"17\",\"steam-condenser\"\n\"17\",\"gnulib\"\n\"17\",\"autoexec\"\n\"17\",\"url-protocol\"\n\"17\",\"amazon-clouddrive\"\n\"17\",\"ieditableobject\"\n\"17\",\"hatchstyle\"\n\"17\",\"hd44780\"\n\"17\",\"touchescancelled\"\n\"17\",\"styleable\"\n\"17\",\"havok\"\n\"17\",\"gmisc\"\n\"17\",\"sharppdf\"\n\"17\",\"mdm-zinc\"\n\"17\",\"paseto\"\n\"17\",\"structural-equality\"\n\"17\",\"automatic-speech-recognition\"\n\"17\",\"user-config\"\n\"17\",\"automatic-migration\"\n\"17\",\"solr4j\"\n\"17\",\"stdcopy\"\n\"17\",\"link-prefetch\"\n\"17\",\"bazaar-plugins\"\n\"17\",\"quarkus-kafka\"\n\"17\",\"zoneminder\"\n\"17\",\"confidential\"\n\"17\",\"bazel-gazelle\"\n\"17\",\"toolstripcombobox\"\n\"17\",\"bento\"\n\"17\",\"uu-parsinglib\"\n\"17\",\"linpack\"\n\"17\",\"bazel-query\"\n\"17\",\"sum-type\"\n\"17\",\"array-flip\"\n\"17\",\"bengali\"\n\"17\",\"zola\"\n\"17\",\"sparkfun\"\n\"17\",\"ember.select\"\n\"17\",\"linewidth\"\n\"17\",\"dart-build\"\n\"17\",\"fmj\"\n\"17\",\"compss\"\n\"17\",\"qstk\"\n\"17\",\"font-awesome-3.2\"\n\"17\",\"arc2\"\n\"17\",\"for-await\"\n\"17\",\"bencoding\"\n\"17\",\"zkp\"\n\"17\",\"qstringlistmodel\"\n\"17\",\"globus-toolkit\"\n\"17\",\"user-messaging-platform\"\n\"17\",\"pressflow\"\n\"17\",\"danger\"\n\"17\",\"the-amazing-audio-engine\"\n\"17\",\"webinar\"\n\"17\",\"spark-skinning\"\n\"17\",\"tiers\"\n\"17\",\"autodesk-data-visualization\"\n\"17\",\"damerau-levenshtein\"\n\"17\",\"papaya\"\n\"17\",\"emsdk\"\n\"17\",\"dailybuilds\"\n\"17\",\"theme-development\"\n\"17\",\"query-engine\"\n\"17\",\"starcounter\"\n\"17\",\"ilookup\"\n\"17\",\"sugar.js\"\n\"17\",\"userland\"\n\"17\",\"glowscript\"\n\"17\",\"d3tree\"\n\"17\",\"ember-cp-validations\"\n\"17\",\"d3-graphviz\"\n\"17\",\"tika-python\"\n\"16\",\"anthropic\"\n\"16\",\"groove\"\n\"16\",\"pg-notify\"\n\"16\",\"babel-plugin-module-resolver\"\n\"16\",\"wear-os-tiles\"\n\"16\",\"clojure-testing\"\n\"16\",\"jbchartview\"\n\"16\",\"react-native-tvos\"\n\"16\",\"skfieldnode\"\n\"16\",\"fieldmanager\"\n\"16\",\"groovysh\"\n\"16\",\"phassetslibrary\"\n\"16\",\"cloud-pak-for-data\"\n\"16\",\"mutablecapabilities\"\n\"16\",\"cloudpickle\"\n\"16\",\"file-forks\"\n\"16\",\"vue-cookies\"\n\"16\",\"maven-1\"\n\"16\",\"maturity\"\n\"16\",\"matterport\"\n\"16\",\"matio\"\n\"16\",\"groupie\"\n\"16\",\"react-phone-number-input\"\n\"16\",\"apache-druid\"\n\"16\",\"clpq\"\n\"16\",\"yjs\"\n\"16\",\"photon-fusion\"\n\"16\",\"clpr\"\n\"16\",\"sized-box\"\n\"16\",\"back-projection\"\n\"16\",\"mxchip\"\n\"16\",\"vue-strap\"\n\"16\",\"vue-socket.io\"\n\"16\",\"yolonas\"\n\"16\",\"clusterize\"\n\"16\",\"clsx\"\n\"16\",\"github-classroom\"\n\"16\",\"slider-revolution\"\n\"16\",\"flutter-floor\"\n\"16\",\"integerupdown\"\n\"16\",\"github-check-run\"\n\"16\",\"report-viewer2016\"\n\"16\",\"trifecta\"\n\"16\",\"trifacta\"\n\"16\",\"tridion-core-services\"\n\"16\",\"littler\"\n\"16\",\"srilm\"\n\"16\",\"xshell\"\n\"16\",\"fenv\"\n\"16\",\"feel-language\"\n\"16\",\"remap-istanbul\"\n\"16\",\"gremlin-java\"\n\"16\",\"square-wire\"\n\"16\",\"fdr\"\n\"16\",\"multipart-alternative\"\n\"16\",\"fedora-29\"\n\"16\",\"ggbreak\"\n\"16\",\"flowgorithm\"\n\"16\",\"remodal\"\n\"16\",\"decision-model-notation\"\n\"16\",\"multipartconfig\"\n\"16\",\"wijgrid\"\n\"16\",\"annotorious\"\n\"16\",\"installshield-2008\"\n\"16\",\"relate\"\n\"16\",\"graphql-java-tools\"\n\"16\",\"list-template\"\n\"16\",\"trng\"\n\"16\",\"ecmascript-2021\"\n\"16\",\"wexitstatus\"\n\"16\",\"decap-cms\"\n\"16\",\"wevtutil\"\n\"16\",\"remote-client\"\n\"16\",\"remote-connections\"\n\"16\",\"skylark\"\n\"16\",\"remote-input\"\n\"16\",\"load-generator\"\n\"16\",\"floating-action-menu\"\n\"16\",\"renderman\"\n\"16\",\"tsup\"\n\"16\",\"anormcypher\"\n\"16\",\"default-package\"\n\"16\",\"graphql-spring-boot\"\n\"16\",\"default-initialization\"\n\"16\",\"smalot-datetimepicker\"\n\"16\",\"treecell\"\n\"16\",\"weed-fs\"\n\"16\",\"fhir-net-api\"\n\"16\",\"yattag\"\n\"16\",\"jsmin\"\n\"16\",\"fileparse\"\n\"16\",\"flang\"\n\"16\",\"appcelerator-cli\"\n\"16\",\"piral\"\n\"16\",\"pageloadtimeout\"\n\"16\",\"apache-spark-1.3\"\n\"16\",\"consoleappender\"\n\"16\",\"ladon\"\n\"16\",\"finger-tree\"\n\"16\",\"xqilla\"\n\"16\",\"playhaven\"\n\"16\",\"xnat\"\n\"16\",\"nexus-player\"\n\"16\",\"nhibernate-4\"\n\"16\",\"afbedsheet\"\n\"16\",\"main-bower-files\"\n\"16\",\"immediate-attribute\"\n\"16\",\"flashmedialiveencoder\"\n\"16\",\"image-translation\"\n\"16\",\"flask-apispec\"\n\"16\",\"pinterest-api\"\n\"16\",\"cassini-dev\"\n\"16\",\"pagedcollectionview\"\n\"16\",\"bing-webmaster-tools\"\n\"16\",\"laravel-encryption\"\n\"16\",\"ngondestroy\"\n\"16\",\"mark.js\"\n\"16\",\"cimage\"\n\"16\",\"castle-windsor-3\"\n\"16\",\"voice-detection\"\n\"16\",\"swipe.js\"\n\"16\",\"software-estimation\"\n\"16\",\"overridependingtransition\"\n\"16\",\"casyncsocket\"\n\"16\",\"ngx-international-phone-number\"\n\"16\",\"flesch-kincaid\"\n\"16\",\"pigeon\"\n\"16\",\"contactgroups\"\n\"16\",\"aegir\"\n\"16\",\"socketio4net\"\n\"16\",\"flask-user\"\n\"16\",\"pixman\"\n\"16\",\"sencha-test\"\n\"16\",\"swrlapi\"\n\"16\",\"adsense-anchor-ads\"\n\"16\",\"addmodelerror\"\n\"16\",\"ngx-clipboard\"\n\"16\",\"safariwatir\"\n\"16\",\"smartmeter\"\n\"16\",\"ngsw-config\"\n\"16\",\"firewall-access\"\n\"16\",\"adaptive-dialogs\"\n\"16\",\"picat\"\n\"16\",\"mapguide\"\n\"16\",\"python-fire\"\n\"16\",\"conkeror\"\n\"16\",\"chiliproject\"\n\"16\",\"selectonelistbox\"\n\"16\",\"cdatabase\"\n\"16\",\"ng2-tag-input\"\n\"16\",\"direct-runner\"\n\"16\",\"directorystream\"\n\"16\",\"chinese-postman\"\n\"16\",\"jtest\"\n\"16\",\"python-arrow\"\n\"16\",\"ng-img-crop\"\n\"16\",\"python-iptables\"\n\"16\",\"undici\"\n\"16\",\"cctexturecache\"\n\"16\",\"disposing\"\n\"16\",\"fundamental-matrix\"\n\"16\",\"api-linkpreview\"\n\"16\",\"umbraco-forms\"\n\"16\",\"jsr363\"\n\"16\",\"unirest-java\"\n\"16\",\"ng2-google-chart\"\n\"16\",\"plasmo\"\n\"16\",\"django-cookies\"\n\"16\",\"seleniummanager\"\n\"16\",\"xmlignore\"\n\"16\",\"paintbox\"\n\"16\",\"selectboxit\"\n\"16\",\"xmonad-contrib\"\n\"16\",\"adobe-bridge\"\n\"16\",\"functx\"\n\"16\",\"circular-slider\"\n\"16\",\"jsonx\"\n\"16\",\"xmltransient\"\n\"16\",\"kyma\"\n\"16\",\"unite.vim\"\n\"16\",\"fixup\"\n\"16\",\"ada95\"\n\"16\",\"datetimeparseexception\"\n\"16\",\"swift-regexbuilder\"\n\"16\",\"python-module-unicodedata\"\n\"16\",\"aiven\"\n\"16\",\"microsoft-graph-cloudcommunications\"\n\"16\",\"valdr\"\n\"16\",\"canoo\"\n\"16\",\"window-size\"\n\"16\",\"pycxx\"\n\"16\",\"urchin\"\n\"16\",\"ora-01830\"\n\"16\",\"canvas3d\"\n\"16\",\"kendo-splitter\"\n\"16\",\"dockable-windows\"\n\"16\",\"kendo-timepicker\"\n\"16\",\"ops4j\"\n\"16\",\"prunsrv\"\n\"16\",\"ps2exe-gui\"\n\"16\",\"pusherswift\"\n\"16\",\"alexa-account-linking\"\n\"16\",\"readstata13\"\n\"16\",\"validating-event\"\n\"16\",\"read-fwf\"\n\"16\",\"nakama\"\n\"16\",\"value-of-css-property\"\n\"16\",\"ruby-style-guide\"\n\"16\",\"puppetlabs-mysql\"\n\"16\",\"ora-01000\"\n\"16\",\"updatedate\"\n\"16\",\"ora-00955\"\n\"16\",\"akka-zeromq\"\n\"16\",\"reasoned-schemer\"\n\"16\",\"kitty\"\n\"16\",\"facebook-widgets\"\n\"16\",\"simplyscroll\"\n\"16\",\"ora-00913\"\n\"16\",\"kint\"\n\"16\",\"r-promises\"\n\"16\",\"sipml5\"\n\"16\",\"ora-12541\"\n\"16\",\"pubmed-api\"\n\"16\",\"wall-time\"\n\"16\",\"ahp\"\n\"16\",\"r-mapedit\"\n\"16\",\"keycloak-authorization-services\"\n\"16\",\"keycloak-javascript\"\n\"16\",\"goproxy\"\n\"16\",\"wasp\"\n\"16\",\"docx-to-pdf-conversion\"\n\"16\",\"gradle-properties\"\n\"16\",\"aws-mediaservices\"\n\"16\",\"robotframework-browser\"\n\"16\",\"grafana-mimir\"\n\"16\",\"django-packages\"\n\"16\",\"aggregators\"\n\"16\",\"servlet-4\"\n\"16\",\"mysql-error-1046\"\n\"16\",\"service-virtualization\"\n\"16\",\"sharepoint-listtemplate\"\n\"16\",\"document-types\"\n\"16\",\"meta-title\"\n\"16\",\"servicemodelex\"\n\"16\",\"django-multiselectfield\"\n\"16\",\"fast-xml-parser\"\n\"16\",\"meteor-easy-search\"\n\"16\",\"watson-virtual-agent\"\n\"16\",\"ibm-installation-manager\"\n\"16\",\"database-reconciliation\"\n\"16\",\"jooby\"\n\"16\",\"crictl\"\n\"16\",\"keystore-access\"\n\"16\",\"keyword-expansion\"\n\"16\",\"ibm-rad-7.5\"\n\"16\",\"document-based-database\"\n\"16\",\"ropc\"\n\"16\",\"joox\"\n\"16\",\"keyword-spotting\"\n\"16\",\"camel-blueprint\"\n\"16\",\"camel-jdbc\"\n\"16\",\"jpanelmenu\"\n\"16\",\"windows-mui\"\n\"16\",\"mysqlrouter\"\n\"16\",\"faraday-oauth\"\n\"16\",\"vector-class-library\"\n\"16\",\"mysql-error\"\n\"16\",\"docksal\"\n\"16\",\"mysql-regexp\"\n\"16\",\"jpf\"\n\"16\",\"sequelize-hooks\"\n\"16\",\"docsy\"\n\"16\",\"mysqlpp\"\n\"16\",\"awscognitotoken\"\n\"16\",\"microsoft-expression\"\n\"16\",\"rosters\"\n\"16\",\"htsql\"\n\"16\",\"in-memory-oltp\"\n\"16\",\"bluetooth-keyboard\"\n\"16\",\"payeezy\"\n\"16\",\"nb-iot\"\n\"16\",\"interspire-shopping-cart\"\n\"16\",\"pdf2json\"\n\"16\",\"spring-content-community-project\"\n\"16\",\"ionic2-providers\"\n\"16\",\"springdoc-openapi-maven-plugin\"\n\"16\",\"awtutilities\"\n\"16\",\"nest-winston\"\n\"16\",\"simevents\"\n\"16\",\"mod-lua\"\n\"16\",\"defn\"\n\"16\",\"power-apps-custom-connector\"\n\"16\",\"oneget\"\n\"16\",\"blog-post\"\n\"16\",\"android-tabstrip\"\n\"16\",\"wwan\"\n\"16\",\"initializr\"\n\"16\",\"postgresapp\"\n\"16\",\"onrowclick\"\n\"16\",\"opencyc\"\n\"16\",\"azure-ad-domain-services\"\n\"16\",\"android-wear-3.0\"\n\"16\",\"htmlwriter\"\n\"16\",\"jqpagination\"\n\"16\",\"near-cache\"\n\"16\",\"silverlight-embedded\"\n\"16\",\"bluedata\"\n\"16\",\"internetsetoption\"\n\"16\",\"android-studio-flamingo\"\n\"16\",\"gsubfn\"\n\"16\",\"blockhound\"\n\"16\",\"jquery-datatables-checkboxes\"\n\"16\",\"karma-typescript\"\n\"16\",\"blkid\"\n\"16\",\"initwithframe\"\n\"16\",\"dev-appserver-2\"\n\"16\",\"paytabs\"\n\"16\",\"bluno\"\n\"16\",\"grunt-exec\"\n\"16\",\"svg-react-loader\"\n\"16\",\"jquery-context\"\n\"16\",\"two-phase-commit\"\n\"16\",\"postgresql-8.1\"\n\"16\",\"mod-pywebsocket\"\n\"16\",\"ncommon\"\n\"16\",\"salesforce-rest-api\"\n\"16\",\"htmltable-control\"\n\"16\",\"kairos-api\"\n\"16\",\"application-monitoring\"\n\"16\",\"salesforce-sfdx\"\n\"16\",\"dvajs\"\n\"16\",\"monero\"\n\"16\",\"scalaj-http\"\n\"16\",\"hostvars\"\n\"16\",\"workdir\"\n\"16\",\"sbt-docker\"\n\"16\",\"corrupted-state-exception\"\n\"16\",\"azure-devops-artifacts\"\n\"16\",\"twentytwenty\"\n\"16\",\"satellite-navigation\"\n\"16\",\"infyom\"\n\"16\",\"couchbase-ottoman\"\n\"16\",\"cppdepend\"\n\"16\",\"nsincrementalstore\"\n\"16\",\"mongo-connector\"\n\"16\",\"mongolian-vertical-script\"\n\"16\",\"tuist\"\n\"16\",\"couchbase-nodejs-sdk\"\n\"16\",\"corepack\"\n\"16\",\"hostheader\"\n\"16\",\"gulp-changed\"\n\"16\",\"bounds-check-elimination\"\n\"16\",\"desktop-recording\"\n\"16\",\"native-maven-plugin\"\n\"16\",\"twilio-autopilot\"\n\"16\",\"epd-python\"\n\"16\",\"worksite-sdk\"\n\"16\",\"entity-framework-4.3.1\"\n\"16\",\"boost-sml\"\n\"16\",\"spring-internationalization\"\n\"16\",\"jquery-lint\"\n\"16\",\"entra\"\n\"16\",\"azure-dev-spaces\"\n\"16\",\"nsmutableorderedset\"\n\"16\",\"neo4j-php-ogm\"\n\"16\",\"neo4j-plugin\"\n\"16\",\"easylogging++\"\n\"16\",\"tuprolog\"\n\"16\",\"syscache\"\n\"16\",\"cocoapods-1.2\"\n\"16\",\"mailgun-api\"\n\"16\",\"shoryuken\"\n\"16\",\"codepad\"\n\"16\",\"javapolicy\"\n\"16\",\"pytest-selenium\"\n\"16\",\"shouldautorotate\"\n\"16\",\"significant-terms\"\n\"16\",\"vip\"\n\"16\",\"browserify-rails\"\n\"16\",\"kronos-workforce-central\"\n\"16\",\"object-equality\"\n\"16\",\"macos-mail-app\"\n\"16\",\"reverting\"\n\"16\",\"code-regions\"\n\"16\",\"mageia\"\n\"16\",\"out-of-source\"\n\"16\",\"misuse\"\n\"16\",\"knockout-es5-plugin\"\n\"16\",\"cocos2d-swift\"\n\"16\",\"mini-xml\"\n\"16\",\"mindsphere\"\n\"16\",\"f#+\"\n\"16\",\"typescript3.8\"\n\"16\",\"occurs-check\"\n\"16\",\"ramda-fantasy\"\n\"16\",\"kotlinx.coroutines.channels\"\n\"16\",\"kotlin-when\"\n\"16\",\"object-state\"\n\"16\",\"amos\"\n\"16\",\"mission-planner\"\n\"16\",\"riak-ts\"\n\"16\",\"vis-timeline\"\n\"16\",\"pyfilesystem\"\n\"16\",\"rancheros\"\n\"16\",\"oberon\"\n\"16\",\"javax-validation\"\n\"16\",\"shape-rendering\"\n\"16\",\"overlap2d\"\n\"16\",\"facebook-ios-sdk-4.0\"\n\"16\",\"kotlin-function-type\"\n\"16\",\"typescript2.3\"\n\"16\",\"lxc-docker\"\n\"16\",\"f#-4.0\"\n\"16\",\"raw-file\"\n\"16\",\"abcl\"\n\"16\",\"rethinkdbdash\"\n\"16\",\"reviver-function\"\n\"16\",\"dolby-audio-api\"\n\"16\",\"ordered-set\"\n\"16\",\"authorization-header\"\n\"16\",\"asyncimageview\"\n\"16\",\"leda\"\n\"16\",\"oai-pmh\"\n\"16\",\"sfc\"\n\"16\",\"atlassian-forge\"\n\"16\",\"libfaac\"\n\"16\",\"android-api-33\"\n\"16\",\"viewdraghelper\"\n\"16\",\"netcobol\"\n\"16\",\"google-barchart\"\n\"16\",\"android-appstandby\"\n\"16\",\"wordpress-4.5.2\"\n\"16\",\"ubuntu-19.10\"\n\"16\",\"revokeobjecturl\"\n\"16\",\"vim-ale\"\n\"16\",\"vimclojure\"\n\"16\",\"rgtk2\"\n\"16\",\"amazon-translate\"\n\"16\",\"synopsis-detect\"\n\"16\",\"rich-media\"\n\"16\",\"java-vertx-web\"\n\"16\",\"pygmaps\"\n\"16\",\"objective-git\"\n\"16\",\"google-ajax-libraries\"\n\"16\",\"rhinoceros\"\n\"16\",\"netlib\"\n\"16\",\"pygbag\"\n\"16\",\"rawurl\"\n\"16\",\"dom-if\"\n\"16\",\"neura\"\n\"16\",\"libstrophe\"\n\"16\",\"libspatialindex\"\n\"16\",\"javafx-bindings\"\n\"16\",\"nokia-wrt\"\n\"16\",\"exchange-server-2019\"\n\"16\",\"taglet\"\n\"16\",\"registry-virtualization\"\n\"16\",\"wxstring\"\n\"16\",\"isuserinteractionenabled\"\n\"16\",\"askql\"\n\"16\",\"contextio\"\n\"16\",\"jabberd2\"\n\"16\",\"radphp\"\n\"16\",\"high-level-architecture\"\n\"16\",\"polykinds\"\n\"16\",\"xcore\"\n\"16\",\"histogram-of-oriented-gradients\"\n\"16\",\"rad-server\"\n\"16\",\"azure-workflow-automation\"\n\"16\",\"nuget-package-manager-console\"\n\"16\",\"holoeverywhere\"\n\"16\",\"node-uuid\"\n\"16\",\"scnvector3\"\n\"16\",\"cordite\"\n\"16\",\"redis-rails\"\n\"16\",\"npm-private-modules\"\n\"16\",\"hg-log\"\n\"16\",\"vgo\"\n\"16\",\"dotnet-new\"\n\"16\",\"azure-web-app-firewall\"\n\"16\",\"hashtree\"\n\"16\",\"hashrocket\"\n\"16\",\"control-charts\"\n\"16\",\"expression-body\"\n\"16\",\"quickjs\"\n\"16\",\"nvidia-isaac\"\n\"16\",\"spritely\"\n\"16\",\"convector\"\n\"16\",\"quicksilver\"\n\"16\",\"gatsby-cloud\"\n\"16\",\"spriteview\"\n\"16\",\"asp.net-core-css-isolation\"\n\"16\",\"bulk-create\"\n\"16\",\"refinitiv-eikon-api\"\n\"16\",\"drupal-distributions\"\n\"16\",\"control-library\"\n\"16\",\"dev-tunnels\"\n\"16\",\"ref-parameters\"\n\"16\",\"openstack-juno\"\n\"16\",\"mkmapcamera\"\n\"16\",\"asp.net-mvc-migration\"\n\"16\",\"plsql-psp\"\n\"16\",\"gcp-notebook\"\n\"16\",\"notificationlistenerservice\"\n\"16\",\"iso-639\"\n\"16\",\"xamlx\"\n\"16\",\"hookrouter\"\n\"16\",\"gcp-compute-instance\"\n\"16\",\"plunit\"\n\"16\",\"drupal-zen\"\n\"16\",\"hipe\"\n\"16\",\"itrs\"\n\"16\",\"drwatson\"\n\"16\",\"redex\"\n\"16\",\"gamesalad\"\n\"16\",\"nsxmlparserdelegate\"\n\"16\",\"tolua++\"\n\"16\",\"iso8583-1993\"\n\"16\",\"sched-deadline\"\n\"16\",\"openinfowindowhtml\"\n\"16\",\"azure-service-plan\"\n\"16\",\"ntfs-3g\"\n\"16\",\"rabbitmq-stream\"\n\"16\",\"plotly.net\"\n\"16\",\"digital-handwritting\"\n\"16\",\"droidparts\"\n\"16\",\"scrapy-item\"\n\"16\",\"digital-downloads\"\n\"16\",\"modbus-rtu-over-tcp\"\n\"16\",\"gameclosure\"\n\"16\",\"redux-selector\"\n\"16\",\"dry-validation\"\n\"16\",\"plnkr.co\"\n\"16\",\"nuget-package-explorer\"\n\"16\",\"business-application\"\n\"16\",\"android-compound-view\"\n\"16\",\"xcf\"\n\"16\",\"digestive-functors\"\n\"16\",\"nuget-cli\"\n\"16\",\"isolated-scope\"\n\"16\",\"double-brace-initialize\"\n\"16\",\"scorm-cloud-api\"\n\"16\",\"openrtsp\"\n\"16\",\"tokenize2\"\n\"16\",\"context-info\"\n\"16\",\"wxmathplot\"\n\"16\",\"open-webkit-sharp\"\n\"16\",\"cube-dimension\"\n\"16\",\"montgomery-multiplication\"\n\"16\",\"react-laravel\"\n\"16\",\"responsive-nav\"\n\"16\",\"lazycache\"\n\"16\",\"esp-now\"\n\"16\",\"mercury-mta\"\n\"16\",\"onactionexecuted\"\n\"16\",\"layout-xml\"\n\"16\",\"collaboration-diagram\"\n\"16\",\"spinwait\"\n\"16\",\"ctabitem\"\n\"16\",\"elasticsearch-bulk\"\n\"16\",\"message-authentication-code\"\n\"16\",\"offline.js\"\n\"16\",\"launch-time\"\n\"16\",\"elastic-mq\"\n\"16\",\"metacello\"\n\"16\",\"comint-mode\"\n\"16\",\"cfnetworking\"\n\"16\",\"iriscouch\"\n\"16\",\"restlet-2.3.1\"\n\"16\",\"angular-dynamic-forms\"\n\"16\",\"iron-form\"\n\"16\",\"commerceserver2007\"\n\"16\",\"python-winshell\"\n\"16\",\"elaboration\"\n\"16\",\"react-native-datetimepicker\"\n\"16\",\"spectacle\"\n\"16\",\"angular-abstract-control\"\n\"16\",\"perlapp\"\n\"16\",\"node-repl\"\n\"16\",\"log4shell\"\n\"16\",\"httpsys\"\n\"16\",\"requiressl\"\n\"16\",\"periodformatter\"\n\"16\",\"lock-guard\"\n\"16\",\"zuul-ci\"\n\"16\",\"node-odbc\"\n\"16\",\"eway\"\n\"16\",\"lolcode\"\n\"16\",\"accessibility-inspector\"\n\"16\",\"string-metric\"\n\"16\",\"loopback3\"\n\"16\",\"everlive\"\n\"16\",\"string-catalog\"\n\"16\",\"stretchdibits\"\n\"16\",\"loose-typing\"\n\"16\",\"loupe\"\n\"16\",\"httpconfiguration\"\n\"16\",\"acitree\"\n\"16\",\"ios9-today-widget\"\n\"16\",\"streaminghttpresponse\"\n\"16\",\"google-cloud-language\"\n\"16\",\"ls-colors\"\n\"16\",\"google-cloud-instances\"\n\"16\",\"odfdom\"\n\"16\",\"google-cloud-cpp\"\n\"16\",\"event-b\"\n\"16\",\"ios-searchapi\"\n\"16\",\"elinks\"\n\"16\",\"mellon\"\n\"16\",\"evc4\"\n\"16\",\"evaporate.js\"\n\"16\",\"element-binding\"\n\"16\",\"memory-overcommitment\"\n\"16\",\"monocle\"\n\"16\",\"acts-as-follower\"\n\"16\",\"cups4j\"\n\"16\",\"qdoc\"\n\"16\",\"cumulative-distribution-function\"\n\"16\",\"android-navhostfragment\"\n\"16\",\"android-ndk-r4\"\n\"16\",\"android-pagedlistview\"\n\"16\",\"cuda.net\"\n\"16\",\"certreq\"\n\"16\",\"qcommandlineparser\"\n\"16\",\"einops\"\n\"16\",\"chart.js-datalabels\"\n\"16\",\"proofs\"\n\"16\",\"chartpanel\"\n\"16\",\"character-reference\"\n\"16\",\"spa-template\"\n\"16\",\"duckduckgo-api\"\n\"16\",\"nmock2\"\n\"16\",\"prose\"\n\"16\",\"moxiemanager\"\n\"16\",\"text-mask\"\n\"16\",\"laravel-sitemap\"\n\"16\",\"project-server-2007\"\n\"16\",\"protected-resource\"\n\"16\",\"tomcat-juli\"\n\"16\",\"nixops\"\n\"16\",\"geometrydrawing\"\n\"16\",\"get-filehash\"\n\"16\",\"getpwuid\"\n\"16\",\"spec#\"\n\"16\",\"protector\"\n\"16\",\"cgihttpserver\"\n\"16\",\"iroutehandler\"\n\"16\",\"prolog-defaulty\"\n\"16\",\"moss2007enterprisesearch\"\n\"16\",\"cgpdfscanner\"\n\"16\",\"getschematable\"\n\"16\",\"step-through\"\n\"16\",\"ember-validations\"\n\"16\",\"mcedit\"\n\"16\",\"seamless-immutable\"\n\"16\",\"tpersistent\"\n\"16\",\"searchable-dropdown\"\n\"16\",\"glcanvas\"\n\"16\",\"hedera\"\n\"16\",\"sonarqube7\"\n\"16\",\"amazon-sagemaker-debugger\"\n\"16\",\"static-dispatch\"\n\"16\",\"stingray\"\n\"16\",\"tidyterra\"\n\"16\",\"fluture\"\n\"16\",\"as-if\"\n\"16\",\"benfords-law\"\n\"16\",\"mrv2\"\n\"16\",\"imagedecoder\"\n\"16\",\"presenceinsights\"\n\"16\",\"scrolltoindex\"\n\"16\",\"stig\"\n\"16\",\"ascii85\"\n\"16\",\"maven-wrapper\"\n\"16\",\"ember-paper\"\n\"16\",\"bert-toolkit\"\n\"16\",\"heatwave\"\n\"16\",\"mca\"\n\"16\",\"bemsimplelinegraph\"\n\"16\",\"google-sheets-charts\"\n\"16\",\"sonarqube-4.0\"\n\"16\",\"sdef\"\n\"16\",\"power-state\"\n\"16\",\"lifetimes-python\"\n\"16\",\"torchmetrics\"\n\"16\",\"thoughtbot\"\n\"16\",\"sourceanalyser\"\n\"16\",\"embedded-coder\"\n\"16\",\"lightning-network\"\n\"16\",\"pgmagick\"\n\"16\",\"medial-axis\"\n\"16\",\"subtext\"\n\"16\",\"quantified-constraints\"\n\"16\",\"user-instance\"\n\"16\",\"flutter-path\"\n\"16\",\"d3-cloud\"\n\"16\",\"autotouch\"\n\"16\",\"ziggy\"\n\"16\",\"userjs\"\n\"16\",\"iiviewdeckcontroller\"\n\"16\",\"bcolz\"\n\"16\",\"mediaplayerelement\"\n\"16\",\"msstickerview\"\n\"16\",\"google-jax\"\n\"16\",\"google-instant\"\n\"16\",\"forcing\"\n\"16\",\"dangling-else\"\n\"16\",\"google-health\"\n\"16\",\"dapper-fluentmap\"\n\"16\",\"imagehandler\"\n\"16\",\"alire\"\n\"16\",\"znodes\"\n\"16\",\"weblogic-maven-plugin\"\n\"16\",\"batch-fetching\"\n\"16\",\"multi-database-connections\"\n\"16\",\"user-secret\"\n\"16\",\"uthash\"\n\"16\",\"fontspec\"\n\"16\",\"solid-start\"\n\"16\",\"stdbool\"\n\"16\",\"linkurious\"\n\"16\",\"quarkus-hibernate-orm\"\n\"16\",\"tools-for-apache-cordova\"\n\"16\",\"sharpen-tool\"\n\"16\",\"subsonic-simplerepository\"\n\"16\",\"bfloat16\"\n\"16\",\"beef\"\n\"16\",\"user-extensions.js\"\n\"16\",\"customstringconvertible\"\n\"16\",\"argument-matching\"\n\"16\",\"compiler-compiler\"\n\"16\",\"embla-carousel\"\n\"16\",\"arduino-uno-wifi\"\n\"16\",\"argp\"\n\"16\",\"webby\"\n\"16\",\"transactional-database\"\n\"16\",\"zend-session-namespace\"\n\"16\",\"help-authoring\"\n\"16\",\"sharpffmpeg\"\n\"16\",\"partial-sort\"\n\"16\",\"emf-compare\"\n\"16\",\"sortcomparefunction\"\n\"16\",\"google-persistent-disk\"\n\"16\",\"partitioned-view\"\n\"16\",\"state-dict\"\n\"16\",\"cvsimport\"\n\"16\",\"helpshift\"\n\"15\",\"gitlab-ci-trigger\"\n\"15\",\"graphql-mesh\"\n\"15\",\"graphql-relay\"\n\"15\",\"badboy\"\n\"15\",\"react-oidc\"\n\"15\",\"term-vectors\"\n\"15\",\"vue-slider-component\"\n\"15\",\"xt\"\n\"15\",\"xtea\"\n\"15\",\"render-html\"\n\"15\",\"backup-agent\"\n\"15\",\"reporting-services-map\"\n\"15\",\"mvxlistview\"\n\"15\",\"flutter-beamer\"\n\"15\",\"cluster-manager\"\n\"15\",\"terminate-handler\"\n\"15\",\"mxnet-gluon\"\n\"15\",\"tensorflow-android\"\n\"15\",\"jaxer\"\n\"15\",\"mat-chip\"\n\"15\",\"efpocoadapter\"\n\"15\",\"eframe\"\n\"15\",\"ghc-pkg\"\n\"15\",\"tenacity\"\n\"15\",\"phplot\"\n\"15\",\"ghcjs-dom\"\n\"15\",\"mat-drawer\"\n\"15\",\"weld-junit5\"\n\"15\",\"git2-rs\"\n\"15\",\"inspect.exe\"\n\"15\",\"treestore\"\n\"15\",\"stagewebviewbridge\"\n\"15\",\"git-assume-unchanged\"\n\"15\",\"git-authentication\"\n\"15\",\"inspinia\"\n\"15\",\"react-native-instagram-login\"\n\"15\",\"git-completion\"\n\"15\",\"ggnewscale\"\n\"15\",\"ballerina-composer\"\n\"15\",\"ssis-data-flow\"\n\"15\",\"material-swift\"\n\"15\",\"websphere-ce\"\n\"15\",\"cloud-haskell\"\n\"15\",\"git-dangling\"\n\"15\",\"instantmessenger\"\n\"15\",\"transform-stream\"\n\"15\",\"cloudkit-js\"\n\"15\",\"sitemapnode\"\n\"15\",\"website-metrics\"\n\"15\",\"trezor\"\n\"15\",\"stanford-parser\"\n\"15\",\"file-icons\"\n\"15\",\"srv-record\"\n\"15\",\"gexperts\"\n\"15\",\"integer-hashing\"\n\"15\",\"terminal-ide\"\n\"15\",\"sqwrl\"\n\"15\",\"fcgid\"\n\"15\",\"fciv\"\n\"15\",\"cmultifileupload\"\n\"15\",\"relevanssi\"\n\"15\",\"loaderlock\"\n\"15\",\"dbms-xmlgen\"\n\"15\",\"ansible-pull\"\n\"15\",\"background-clip\"\n\"15\",\"react-simple-keyboard\"\n\"15\",\"c-mode\"\n\"15\",\"tsavedialog\"\n\"15\",\"teamcity-6\"\n\"15\",\"gridding\"\n\"15\",\"yammer-api\"\n\"15\",\"ddpg\"\n\"15\",\"react-pose\"\n\"15\",\"ckshare\"\n\"15\",\"livedocx\"\n\"15\",\"teamstudio-unplugged\"\n\"15\",\"v-stepper\"\n\"15\",\"react-sidebar\"\n\"15\",\"ckrecordzone\"\n\"15\",\"yahoo-search\"\n\"15\",\"phonegap-admob\"\n\"15\",\"preverify\"\n\"15\",\"jersey-3.0\"\n\"15\",\"yii-cformmodel\"\n\"15\",\"eclipse-2020-06\"\n\"15\",\"dbimport\"\n\"15\",\"lita\"\n\"15\",\"cmaltimeter\"\n\"15\",\"eclipse-fragment\"\n\"15\",\"jdic\"\n\"15\",\"fedora-28\"\n\"15\",\"localsocket\"\n\"15\",\"slimselect\"\n\"15\",\"listof\"\n\"15\",\"vssconverter\"\n\"15\",\"baseweb\"\n\"15\",\"llrp\"\n\"15\",\"debian-7.6.0\"\n\"15\",\"cm\"\n\"15\",\"matplotlib.mlab\"\n\"15\",\"llvm-4.0\"\n\"15\",\"mvvmfx\"\n\"15\",\"class-dbi\"\n\"15\",\"ansys-apdl\"\n\"15\",\"mvw\"\n\"15\",\"xtraeditors\"\n\"15\",\"discord-rpc\"\n\"15\",\"funcunit\"\n\"15\",\"ngx-gallery\"\n\"15\",\"firrtl\"\n\"15\",\"picaxe\"\n\"15\",\"filevault\"\n\"15\",\"sygic-mobile-sdk\"\n\"15\",\"runtime-identifier\"\n\"15\",\"safe-bool-idiom\"\n\"15\",\"vndocumentcameraviewcontroller\"\n\"15\",\"fishtown-analytics\"\n\"15\",\"django-2.x\"\n\"15\",\"python-lru-cache\"\n\"15\",\"owl-date-time\"\n\"15\",\"runtimemodification\"\n\"15\",\"flask-smorest\"\n\"15\",\"jsvalidation\"\n\"15\",\"jsonlogic\"\n\"15\",\"python-fabric-2\"\n\"15\",\"python-pex\"\n\"15\",\"picamera2\"\n\"15\",\"cifacefeature\"\n\"15\",\"black-code-formatter\"\n\"15\",\"swipecardview\"\n\"15\",\"python-decimal\"\n\"15\",\"cifar100\"\n\"15\",\"xmlupdate\"\n\"15\",\"cardspace\"\n\"15\",\"adornment\"\n\"15\",\"cardscrollview\"\n\"15\",\"fitvids\"\n\"15\",\"volta\"\n\"15\",\"django-auditlog\"\n\"15\",\"python-bleak\"\n\"15\",\"image-science\"\n\"15\",\"kubernetes-dns\"\n\"15\",\"laravel-airlock\"\n\"15\",\"django-cache-machine\"\n\"15\",\"language-studio\"\n\"15\",\"smartsheet-c#-sdk-v1\"\n\"15\",\"cassandra-stress-tool\"\n\"15\",\"snowboy\"\n\"15\",\"datastax-csharp-driver\"\n\"15\",\"rule-of-zero\"\n\"15\",\"mail-mime\"\n\"15\",\"jsgauge\"\n\"15\",\"flambo\"\n\"15\",\"imports-loader\"\n\"15\",\"xml-error\"\n\"15\",\"rust-rustlings\"\n\"15\",\"mailsystem.net\"\n\"15\",\"picqer-exact-php-client\"\n\"15\",\"firebase-genkit\"\n\"15\",\"fizzler\"\n\"15\",\"flag-secure\"\n\"15\",\"map-basic\"\n\"15\",\"jsr172\"\n\"15\",\"configuration-as-code\"\n\"15\",\"dirtyrectangle\"\n\"15\",\"packetdotnet\"\n\"15\",\"selectcheckboxmenu\"\n\"15\",\"semicolon-inference\"\n\"15\",\"undecidable-instances\"\n\"15\",\"pact-python\"\n\"15\",\"apache-marmotta\"\n\"15\",\"nexus-5x\"\n\"15\",\"page-loading-message\"\n\"15\",\"xinu\"\n\"15\",\"next-translate\"\n\"15\",\"player-stage\"\n\"15\",\"cedar-bdd\"\n\"15\",\"fileresponse\"\n\"15\",\"sendable\"\n\"15\",\"symfony-config-component\"\n\"15\",\"pamie\"\n\"15\",\"page-flow\"\n\"15\",\"apertium\"\n\"15\",\"unifiednativeadview\"\n\"15\",\"synced-folder\"\n\"15\",\"pidcrypt\"\n\"15\",\"django-environ\"\n\"15\",\"unreal-gameplay-ability-system\"\n\"15\",\"windows-sbs\"\n\"15\",\"awesome-typescript-loader\"\n\"15\",\"r-shinylive\"\n\"15\",\"unsemantic-css\"\n\"15\",\"varint\"\n\"15\",\"carbon-components\"\n\"15\",\"car-analogy\"\n\"15\",\"grafana-plugin\"\n\"15\",\"mysema\"\n\"15\",\"keybase\"\n\"15\",\"angular-pagination\"\n\"15\",\"vue-tsc\"\n\"15\",\"canjs-component\"\n\"15\",\"react-to-pdf\"\n\"15\",\"react-templates\"\n\"15\",\"oraclejdk\"\n\"15\",\"py-amqplib\"\n\"15\",\"aiobotocore\"\n\"15\",\"robobinding\"\n\"15\",\"jgiven\"\n\"15\",\"gradle-node-plugin\"\n\"15\",\"sis\"\n\"15\",\"gradle-managed-device\"\n\"15\",\"ruby-kafka\"\n\"15\",\"gradle-kts\"\n\"15\",\"windows-live-mail\"\n\"15\",\"update-post-meta\"\n\"15\",\"jqote\"\n\"15\",\"ruby-characters\"\n\"15\",\"oracle-home\"\n\"15\",\"crowdflower\"\n\"15\",\"wildfly-21\"\n\"15\",\"gradle-2\"\n\"15\",\"jiffy\"\n\"15\",\"optical-drive\"\n\"15\",\"iactivescript\"\n\"15\",\"uprobe\"\n\"15\",\"mysql-error-1075\"\n\"15\",\"mysql-error-1071\"\n\"15\",\"govmomi\"\n\"15\",\"wagtail-pageurl\"\n\"15\",\"gpudirect\"\n\"15\",\"dmcs\"\n\"15\",\"gpu-cooperative-groups\"\n\"15\",\"alfresco-ldap\"\n\"15\",\"pycountry-convert\"\n\"15\",\"jitcode-jitcdde-jitcsde\"\n\"15\",\"sitecore-habitat\"\n\"15\",\"jjs\"\n\"15\",\"canonical-name\"\n\"15\",\"wcf-rest-starter-kit\"\n\"15\",\"mfe\"\n\"15\",\"reassign\"\n\"15\",\"face-landmark\"\n\"15\",\"ibook-author\"\n\"15\",\"rpscreenrecorder\"\n\"15\",\"oracle8\"\n\"15\",\"ruby-upgrade\"\n\"15\",\"simulte\"\n\"15\",\"dashclock\"\n\"15\",\"publisher-policy\"\n\"15\",\"rubyinstaller\"\n\"15\",\"puppet-bolt\"\n\"15\",\"roofline\"\n\"15\",\"wbr\"\n\"15\",\"keras-vggface\"\n\"15\",\"fandjango\"\n\"15\",\"fastinfoset\"\n\"15\",\"metric-fu\"\n\"15\",\"kinetica\"\n\"15\",\"publify\"\n\"15\",\"joml\"\n\"15\",\"mic-1\"\n\"15\",\"rodio\"\n\"15\",\"public-fields\"\n\"15\",\"hyperledger-cello\"\n\"15\",\"nag-fortran\"\n\"15\",\"ruby-1.9.1\"\n\"15\",\"oracle-bpm-suite\"\n\"15\",\"creators-update\"\n\"15\",\"i18next-browser-languagedetector\"\n\"15\",\"ptv-developer\"\n\"15\",\"service-level-agreement\"\n\"15\",\"simpl-schema\"\n\"15\",\"recent-documents\"\n\"15\",\"cspack\"\n\"15\",\"service-operations\"\n\"15\",\"wcf-faults\"\n\"15\",\"rubocop-rspec\"\n\"15\",\"ics-openvpn\"\n\"15\",\"service-pack\"\n\"15\",\"kingsoft\"\n\"15\",\"aws-jwt-authorizer\"\n\"15\",\"push-queue\"\n\"15\",\"jpicker\"\n\"15\",\"farpoint\"\n\"15\",\"datadesign\"\n\"15\",\"ibm-eventstreams\"\n\"15\",\"turtle-mock\"\n\"15\",\"nsdatecomponent\"\n\"15\",\"app-search\"\n\"15\",\"ionic-native-http\"\n\"15\",\"kefir.js\"\n\"15\",\"nested-tibble\"\n\"15\",\"sus\"\n\"15\",\"tymon-jwt\"\n\"15\",\"corporate-policy\"\n\"15\",\"tutum\"\n\"15\",\"azure-eventhub-client\"\n\"15\",\"depth-camera\"\n\"15\",\"svgz\"\n\"15\",\"ionic-serve\"\n\"15\",\"ionic-material\"\n\"15\",\"nativescript-codesharing\"\n\"15\",\"jquery-rotate\"\n\"15\",\"htc-hero\"\n\"15\",\"dynamics-sl\"\n\"15\",\"invalidselectorexception\"\n\"15\",\"mojo-sdk\"\n\"15\",\"dynamic-dll-import\"\n\"15\",\"openacs\"\n\"15\",\"dynamic-expresso\"\n\"15\",\"android-tablet-layout\"\n\"15\",\"tvar\"\n\"15\",\"twemoji\"\n\"15\",\"ephesoft\"\n\"15\",\"design-data\"\n\"15\",\"aws-sftp\"\n\"15\",\"dynamic-schema\"\n\"15\",\"kcov\"\n\"15\",\"samsung-galaxy-watch\"\n\"15\",\"demand-paging\"\n\"15\",\"gulp-shell\"\n\"15\",\"grover\"\n\"15\",\"inheritdoc\"\n\"15\",\"nsight-systems\"\n\"15\",\"appixia\"\n\"15\",\"typedactor\"\n\"15\",\"swift-for-tensorflow\"\n\"15\",\"pdc\"\n\"15\",\"wowslider\"\n\"15\",\"juicy-pixels\"\n\"15\",\"enzyme-to-snapshot\"\n\"15\",\"postgresql-parallel-query\"\n\"15\",\"opalvoip\"\n\"15\",\"infomaker\"\n\"15\",\"opaleye\"\n\"15\",\"nest-dynamic-modules\"\n\"15\",\"scala-2.7\"\n\"15\",\"mongodb-security\"\n\"15\",\"ncml\"\n\"15\",\"easy-modbus\"\n\"15\",\"kata-containers\"\n\"15\",\"bottleneck\"\n\"15\",\"covr\"\n\"15\",\"grunt-build-control\"\n\"15\",\"mod-userdir\"\n\"15\",\"mod-verto\"\n\"15\",\"nested-gridview\"\n\"15\",\"deftjs\"\n\"15\",\"nerves-project\"\n\"15\",\"jxtaskpane\"\n\"15\",\"modulino\"\n\"15\",\"blazor-webapp\"\n\"15\",\"model-inheritance\"\n\"15\",\"swa-cli\"\n\"15\",\"k0s\"\n\"15\",\"path.js\"\n\"15\",\"boost-type-erasure\"\n\"15\",\"opencv-features2d\"\n\"15\",\"sap-solution-manager\"\n\"15\",\"mongoose-auto-increment\"\n\"15\",\"desktop-integration\"\n\"15\",\"navigateuri\"\n\"15\",\"jquery-mobile-fieldset\"\n\"15\",\"entity-framework-6.4\"\n\"15\",\"deploy-keys\"\n\"15\",\"cpm\"\n\"15\",\"grunt-contrib-compress\"\n\"15\",\"module-alias\"\n\"15\",\"mongokitten\"\n\"15\",\"wumpus-world\"\n\"15\",\"host-object\"\n\"15\",\"dynogels\"\n\"15\",\"html-compression\"\n\"15\",\"apple-help\"\n\"15\",\"azure-ddos\"\n\"15\",\"bootstrap-4.1.x\"\n\"15\",\"popupcontrolextender\"\n\"15\",\"positive-lookbehind\"\n\"15\",\"openbadge\"\n\"15\",\"east-text-detector\"\n\"15\",\"kaizala-action\"\n\"15\",\"jquery-reel\"\n\"15\",\"bpmn-js\"\n\"15\",\"block-programming\"\n\"15\",\"blu-ray\"\n\"15\",\"countly-analytics\"\n\"15\",\"jupyter-widget\"\n\"15\",\"inline-table-function\"\n\"15\",\"injective-function\"\n\"15\",\"desktopcapturer\"\n\"15\",\"bootstrap-native\"\n\"15\",\"coreos-ignition\"\n\"15\",\"spring-integration-jdbc\"\n\"15\",\"deoptimization\"\n\"15\",\"ara\"\n\"15\",\"entitykey\"\n\"15\",\"neoclipse\"\n\"15\",\"gulp-load-plugins\"\n\"15\",\"justin.tv\"\n\"15\",\"milter\"\n\"15\",\"f#-charting\"\n\"15\",\"winlibs\"\n\"15\",\"kotlin-experimental\"\n\"15\",\"jasperstarter\"\n\"15\",\"bronto\"\n\"15\",\"typeinitializer\"\n\"15\",\"libdc1394\"\n\"15\",\"ordercloud\"\n\"15\",\"r-devtools\"\n\"15\",\"shopee\"\n\"15\",\"kognitio\"\n\"15\",\"netmodules\"\n\"15\",\"windward\"\n\"15\",\"shakapacker\"\n\"15\",\"rational-test-workbench\"\n\"15\",\"javax.swing.text\"\n\"15\",\"signed-overflow\"\n\"15\",\"buddy-class\"\n\"15\",\"rapier-3d\"\n\"15\",\"amoeba-gem\"\n\"15\",\"setpropertyactionlistener\"\n\"15\",\"amazon-workdocs\"\n\"15\",\"milton\"\n\"15\",\"retained-in-memory\"\n\"15\",\"windows-subsystem-for-android\"\n\"15\",\"lwuit-dialog\"\n\"15\",\"rawbytestring\"\n\"15\",\"java-22\"\n\"15\",\"bufferunderflowexception\"\n\"15\",\"libcst\"\n\"15\",\"signal-protocol\"\n\"15\",\"win-prolog\"\n\"15\",\"ami.js\"\n\"15\",\"audeering-opensmile\"\n\"15\",\"pygraph\"\n\"15\",\"rdf-star\"\n\"15\",\"r-doredis\"\n\"15\",\"netbeans-15\"\n\"15\",\"code-intelligence\"\n\"15\",\"typerex\"\n\"15\",\"typedi\"\n\"15\",\"format-currency\"\n\"15\",\"ocamldoc\"\n\"15\",\"kooboo\"\n\"15\",\"audioworkletprocessor\"\n\"15\",\"code-testing\"\n\"15\",\"migx\"\n\"15\",\"rfc6749\"\n\"15\",\"libudev\"\n\"15\",\"java-process-runtime\"\n\"15\",\"javaexec-gradle-plugin\"\n\"15\",\"fabric-answers\"\n\"15\",\"freefair-aspectj\"\n\"15\",\"analytics-for-hadoop\"\n\"15\",\"r-box\"\n\"15\",\"free-diameter\"\n\"15\",\"aurelia-store\"\n\"15\",\"microsoft-machine-learning-server\"\n\"15\",\"wkhttpcookiestore\"\n\"15\",\"typescript-namespace\"\n\"15\",\"golfscript\"\n\"15\",\"external-assemblies\"\n\"15\",\"nxjs\"\n\"15\",\"pykde\"\n\"15\",\"winui-xaml\"\n\"15\",\"foundry-scenarios\"\n\"15\",\"pysmt\"\n\"15\",\"pysocks\"\n\"15\",\"express-winston\"\n\"15\",\"facebook-conceal\"\n\"15\",\"dolphin-smalltalk\"\n\"15\",\"express-ws\"\n\"15\",\"android-asset-delivery\"\n\"15\",\"objectivezip\"\n\"15\",\"authlogic-oauth\"\n\"15\",\"android-appwidget-list\"\n\"15\",\"nevron\"\n\"15\",\"kuali\"\n\"15\",\"synset\"\n\"15\",\"otroslogviewer\"\n\"15\",\"javahg\"\n\"15\",\"least-astonishment\"\n\"15\",\"uiaccessoryview\"\n\"15\",\"java-micro-editon-sdk3.0\"\n\"15\",\"obscured-view\"\n\"15\",\"mincemeat\"\n\"15\",\"rfc1035\"\n\"15\",\"dollar-quoting\"\n\"15\",\"viewpoint\"\n\"15\",\"mahjong\"\n\"15\",\"objectpath\"\n\"15\",\"cocos2d-iphone-2.x\"\n\"15\",\"klist\"\n\"15\",\".net-fiddle\"\n\"15\",\"microstack\"\n\"15\",\"windows-shell-extension-menu\"\n\"15\",\"wm-concat\"\n\"15\",\"google-apps-activity\"\n\"15\",\"rio\"\n\"15\",\"udb\"\n\"15\",\"riot-os\"\n\"15\",\"pyexcelerate\"\n\"15\",\"less-rails\"\n\"15\",\"oscache\"\n\"15\",\"do-not-track\"\n\"15\",\"retrytemplate\"\n\"15\",\"lync-server-2013\"\n\"15\",\"buildmanager\"\n\"15\",\"asynctest\"\n\"15\",\"absl-py\"\n\"15\",\"uiaction\"\n\"15\",\"dialog-framework\"\n\"15\",\"dotnet-format\"\n\"15\",\"sqlmigrations\"\n\"15\",\"drupal-cache\"\n\"15\",\"high-volume\"\n\"15\",\"gunit\"\n\"15\",\"number-recognition\"\n\"15\",\"xcode6.0.1\"\n\"15\",\"scenejs\"\n\"15\",\"gwmodel\"\n\"15\",\"dreamservice\"\n\"15\",\"azure-iot-hub-device-update\"\n\"15\",\"scikit-plot\"\n\"15\",\"isolation-frameworks\"\n\"15\",\"digitization\"\n\"15\",\"scalene\"\n\"15\",\"reddot\"\n\"15\",\"hirb\"\n\"15\",\"dexopt\"\n\"15\",\"dibs\"\n\"15\",\"nvdec\"\n\"15\",\"sqf\"\n\"15\",\"express-ntlm\"\n\"15\",\"iview-ui\"\n\"15\",\"scalameter\"\n\"15\",\"gamut\"\n\"15\",\"mobilefirst-mtw\"\n\"15\",\"diffbot\"\n\"15\",\"uifeedbackgenerator\"\n\"15\",\"uidatepickermodetime\"\n\"15\",\"istorage\"\n\"15\",\"is-uploaded-file\"\n\"15\",\"dto-mapping\"\n\"15\",\"uielementcollection\"\n\"15\",\"cordova-2.7.0\"\n\"15\",\"azure-metrics-advisor\"\n\"15\",\"dragtarget\"\n\"15\",\"android-compose-appbar\"\n\"15\",\"android-compose-dropdownmenu\"\n\"15\",\"drakma\"\n\"15\",\"non-public-selectors\"\n\"15\",\"tinysort\"\n\"15\",\"tap-harness\"\n\"15\",\"pnp-framework\"\n\"15\",\"hadoopy\"\n\"15\",\"azure-mobile-engagement\"\n\"15\",\"openoffice-api\"\n\"15\",\"pmw\"\n\"15\",\"gcp-alerts\"\n\"15\",\"assembly-name\"\n\"15\",\"pluto-grid\"\n\"15\",\"mlabwrap\"\n\"15\",\"tasmota\"\n\"15\",\"reference-cycle\"\n\"15\",\"hamming-window\"\n\"15\",\"version-control-keywords\"\n\"15\",\"android-gradle-2.2\"\n\"15\",\"mockserver-netty\"\n\"15\",\"gcp-secret-manager\"\n\"15\",\"hanami-model\"\n\"15\",\"caeagllayer\"\n\"15\",\"reference-library\"\n\"15\",\"execute-as\"\n\"15\",\"contenttypes\"\n\"15\",\"reference-source\"\n\"15\",\"general-protection-fault\"\n\"15\",\"tcptrace-pocketsoap\"\n\"15\",\"tdatasetprovider\"\n\"15\",\"generalized-method-of-moments\"\n\"15\",\"cag\"\n\"15\",\"azure-regions\"\n\"15\",\"azure-performancecounters\"\n\"15\",\"asl\"\n\"15\",\"dropdownlistview\"\n\"15\",\"gc-roots\"\n\"15\",\"regexserde\"\n\"15\",\"playstation-portable\"\n\"15\",\"cake-bake\"\n\"15\",\"hardware-design\"\n\"15\",\"geckosdk\"\n\"15\",\"control-template\"\n\"15\",\"uilayoutguide\"\n\"15\",\"drupal-domain-access\"\n\"15\",\"openssl-net\"\n\"15\",\"expo-splash-screen\"\n\"15\",\"sptbxlib\"\n\"15\",\"spsitedataquery\"\n\"15\",\"tmail\"\n\"15\",\"uimafit\"\n\"15\",\"gwtmockito\"\n\"15\",\"tlsharp\"\n\"15\",\"buybutton.js\"\n\"15\",\"tablesaw\"\n\"15\",\"gwt-visualization\"\n\"15\",\"nodester\"\n\"15\",\"dsofile\"\n\"15\",\"expo-module\"\n\"15\",\"uimodalpresentationformsh\"\n\"15\",\"expo-eas\"\n\"15\",\"device-compatibility\"\n\"15\",\"dropwizard-guice\"\n\"15\",\"mlvision\"\n\"15\",\"red-system\"\n\"15\",\"tizen-sdb\"\n\"15\",\"azure-static-website-routing\"\n\"15\",\"droptarget\"\n\"15\",\"cabal-dev\"\n\"15\",\"android-blur-effect\"\n\"15\",\"policywrap\"\n\"15\",\"titanium-widgets\"\n\"15\",\"quickchart\"\n\"15\",\"quickbuild\"\n\"15\",\"opensearch-security-plugin\"\n\"15\",\"peoplesoft-query\"\n\"15\",\"ctfe\"\n\"15\",\"angular2-build\"\n\"15\",\"acralyzer\"\n\"15\",\"prototype-chosen\"\n\"15\",\"project-open\"\n\"15\",\"react-leaflet-search\"\n\"15\",\"actframework\"\n\"15\",\"resulttype\"\n\"15\",\"angular2-custom-component\"\n\"15\",\"esphome\"\n\"15\",\"char32-t\"\n\"15\",\"hyperapp\"\n\"15\",\"iphone-xs\"\n\"15\",\"penn-treebank\"\n\"15\",\"espocrm\"\n\"15\",\"project-structuring\"\n\"15\",\"message-map\"\n\"15\",\"google-cloud-test-lab\"\n\"15\",\"lazyframe\"\n\"15\",\"project-valhalla\"\n\"15\",\"elision\"\n\"15\",\"office-web-app\"\n\"15\",\"meio-upload\"\n\"15\",\"lazy-propagation\"\n\"15\",\"ios-shortcut\"\n\"15\",\"qclipboard\"\n\"15\",\"text-shadow\"\n\"15\",\"gen-event\"\n\"15\",\"iphone-sdk-4.0.1\"\n\"15\",\"pegkit\"\n\"15\",\"text-embedding-ada-002\"\n\"15\",\"nodejs-polars\"\n\"15\",\"pgbadger\"\n\"15\",\"splitactionbar\"\n\"15\",\"member-enumeration\"\n\"15\",\"lcc-win32\"\n\"15\",\"cuda-events\"\n\"15\",\"iphone-accessory\"\n\"15\",\"pg-ctl\"\n\"15\",\"activerecord-jdbc\"\n\"15\",\"node-ipc\"\n\"15\",\"memcachier\"\n\"15\",\"qdateedit\"\n\"15\",\"prolog-coroutining\"\n\"15\",\"iphone11\"\n\"15\",\"android-multiple-apk\"\n\"15\",\"qemu-device\"\n\"15\",\"geolitecity\"\n\"15\",\"cuelang\"\n\"15\",\"centreon-api\"\n\"15\",\"ipfs-cli\"\n\"15\",\"ipad-playgrounds\"\n\"15\",\"monologue\"\n\"15\",\"react-infinite-scroll\"\n\"15\",\"proxytunnel\"\n\"15\",\"speaker-diarization\"\n\"15\",\"esc-key\"\n\"15\",\"iron-data-table\"\n\"15\",\"node.js-napi\"\n\"15\",\"speechkit\"\n\"15\",\"react-native-charts-wrapper\"\n\"15\",\"get-cli\"\n\"15\",\"zpanel\"\n\"15\",\"ios14.5\"\n\"15\",\"metacpan\"\n\"15\",\"commandinjection\"\n\"15\",\"angularjs-authentication\"\n\"15\",\"logminer\"\n\"15\",\"oledbdestination\"\n\"15\",\"logonserver\"\n\"15\",\"commitanimations\"\n\"15\",\"cgcontextdrawpdfpage\"\n\"15\",\"http-negotiate\"\n\"15\",\"mouseleftbuttonup\"\n\"15\",\"special-form\"\n\"15\",\"chakram\"\n\"15\",\"perlapi\"\n\"15\",\"zynq-ultrascale+\"\n\"15\",\"iruby\"\n\"15\",\"nlog.config\"\n\"15\",\"logan-square\"\n\"15\",\"hummingbird\"\n\"15\",\"access-data-project\"\n\"15\",\"evolus-pencil\"\n\"15\",\"ipvs\"\n\"15\",\"common-expression-language\"\n\"15\",\"spidev\"\n\"15\",\"node-memwatch\"\n\"15\",\"restforce\"\n\"15\",\"cfzip\"\n\"15\",\"moose-technology\"\n\"15\",\"cfwindow\"\n\"15\",\"perwebrequest\"\n\"15\",\"oleview\"\n\"15\",\"nipyapi\"\n\"15\",\"httpfs\"\n\"15\",\"largest-contentful-paint\"\n\"15\",\"csvde\"\n\"15\",\"column-sizing\"\n\"15\",\"cgi.pm\"\n\"15\",\"property-editor\"\n\"15\",\"log-ascii-standard\"\n\"15\",\"lotus-designer\"\n\"15\",\"zset\"\n\"15\",\"google-dataform\"\n\"15\",\"font-feature-settings\"\n\"15\",\"fogbugz-on-demand\"\n\"15\",\"std-expected\"\n\"15\",\"git-rev-parse\"\n\"15\",\"arc-lisp\"\n\"15\",\"trace-listener\"\n\"15\",\"usefaketimers\"\n\"15\",\"security-identifier\"\n\"15\",\"argocd-notification\"\n\"15\",\"stellent\"\n\"15\",\"force-directed-graph\"\n\"15\",\"user-account-control\"\n\"15\",\"mts\"\n\"15\",\"headroom.js\"\n\"15\",\"statsforecast\"\n\"15\",\"fog-aws\"\n\"15\",\"steinberg-asio\"\n\"15\",\"solc\"\n\"15\",\"urlsessionwebsockettask\"\n\"15\",\"tiingo\"\n\"15\",\"sterling-db\"\n\"15\",\"arithabort\"\n\"15\",\"authprovider\"\n\"15\",\"traefik-routers\"\n\"15\",\"haskell-streaming\"\n\"15\",\"user-information-list\"\n\"15\",\"ietf-bcp-47\"\n\"15\",\"urlscan\"\n\"15\",\"glassfish-4.1.1\"\n\"15\",\"autobahnjs\"\n\"15\",\"forerunnerdb\"\n\"15\",\"iesi-collections\"\n\"15\",\"qshortcut\"\n\"15\",\"composite-application\"\n\"15\",\"foreigner\"\n\"15\",\"gns3\"\n\"15\",\"flutter-web3\"\n\"15\",\"web-application-security\"\n\"15\",\"identifying-relationship\"\n\"15\",\"screen-time\"\n\"15\",\"embperl\"\n\"15\",\"cuvid\"\n\"15\",\"google-playground\"\n\"15\",\"zend-form-select\"\n\"15\",\"preact-cli\"\n\"15\",\"pass-by-reference-value\"\n\"15\",\"web-app-manifest\"\n\"15\",\"partytown\"\n\"15\",\"amazon-acl\"\n\"15\",\"beanstream\"\n\"15\",\"emacsw32\"\n\"15\",\"license-maven-plugin\"\n\"15\",\"id-card\"\n\"15\",\"cyberneko\"\n\"15\",\"msinfo32\"\n\"15\",\"cycript\"\n\"15\",\"zenodo\"\n\"15\",\"parseexcel\"\n\"15\",\"webdeploy-3.5\"\n\"15\",\"powershell-provider\"\n\"15\",\"powershell-hosting\"\n\"15\",\"zerolog\"\n\"15\",\"powershell-7.4\"\n\"15\",\"google-managed-prometheus\"\n\"15\",\"parameter-object\"\n\"15\",\"elm-signal\"\n\"15\",\"spark-webui\"\n\"15\",\"mediaquery\"\n\"15\",\"pantone\"\n\"15\",\"tgridpanel\"\n\"15\",\"topcoat\"\n\"15\",\"mspgcc\"\n\"15\",\"eloqua-bulk-api\"\n\"15\",\"dapper-simplecrud\"\n\"15\",\"webkitrequestfullscreen\"\n\"15\",\"mediaview\"\n\"15\",\"linfu\"\n\"15\",\"linkchecker\"\n\"15\",\"linksys\"\n\"15\",\"artemis\"\n\"15\",\"msas\"\n\"15\",\"amazon-linux-extras\"\n\"15\",\"google-street-view-static-api\"\n\"15\",\"mtktextureloader\"\n\"15\",\"artifact-deployer\"\n\"15\",\"z39.50\"\n\"15\",\"amazon-inspector\"\n\"15\",\"gluonts\"\n\"15\",\"tidb-pd\"\n\"15\",\"zaber\"\n\"15\",\"tidal-scheduler\"\n\"15\",\"sublime-syntax\"\n\"15\",\"globalevent\"\n\"15\",\"google-sdm-api\"\n\"15\",\"as86\"\n\"15\",\"sdata\"\n\"15\",\"subnormal-numbers\"\n\"15\",\"prefast\"\n\"15\",\"gmplot\"\n\"15\",\"helidon-webclient\"\n\"15\",\"sony-audio-control-api\"\n\"15\",\"ember-power-select\"\n\"15\",\"amazon-ec2-spot-market\"\n\"15\",\"ember-select\"\n\"15\",\"array-comparison\"\n\"15\",\"custom-search-provider\"\n\"15\",\"custom-stories\"\n\"15\",\"thunar\"\n\"15\",\"stateful-actor-service\"\n\"14\",\"rehosting\"\n\"14\",\"rehypejs\"\n\"14\",\"flot.tooltip\"\n\"14\",\"pgu\"\n\"14\",\"vue3-carousel\"\n\"14\",\"cloudflare-turnstile\"\n\"14\",\"phpsh\"\n\"14\",\"wdqs\"\n\"14\",\"loaderinfo\"\n\"14\",\"skygear\"\n\"14\",\"fiddler-everywhere\"\n\"14\",\"webviewdidfinishload\"\n\"14\",\"sklearn2pmml\"\n\"14\",\"class-designer\"\n\"14\",\"transparent-control\"\n\"14\",\"php-printer\"\n\"14\",\"weak-head-normal-form\"\n\"14\",\"jest-expo\"\n\"14\",\"fhs-twitter-engine\"\n\"14\",\"babashka\"\n\"14\",\"babel-babylon\"\n\"14\",\"ginac\"\n\"14\",\"vstesthost\"\n\"14\",\"skopeo\"\n\"14\",\"wedge\"\n\"14\",\"ghprb\"\n\"14\",\"local-node-modules\"\n\"14\",\"fgetpos\"\n\"14\",\"jbi\"\n\"14\",\"file-loader\"\n\"14\",\"vue3-openlayers\"\n\"14\",\"vue-authenticate\"\n\"14\",\"tslib\"\n\"14\",\"trepan\"\n\"14\",\"phantomjs-node\"\n\"14\",\"reqif\"\n\"14\",\"jboss-rules\"\n\"14\",\"flowground\"\n\"14\",\"ferry\"\n\"14\",\"sleekxmpp\"\n\"14\",\"website-admin-tool\"\n\"14\",\"github-actions-services\"\n\"14\",\"github-actions-workflows\"\n\"14\",\"jemos-podam\"\n\"14\",\"reliable-dictionary\"\n\"14\",\"reliable-multicast\"\n\"14\",\"truthy\"\n\"14\",\"vue-tel-input\"\n\"14\",\"civicrm-extension\"\n\"14\",\"php-8.3\"\n\"14\",\"phonegap-developer-app\"\n\"14\",\"cmfcribbonpanel\"\n\"14\",\"cknotification\"\n\"14\",\"jchart2d\"\n\"14\",\"vuejs-routing\"\n\"14\",\"remote-assistance\"\n\"14\",\"wiener-filter\"\n\"14\",\"vuelayers\"\n\"14\",\"class-relationship\"\n\"14\",\"photospicker\"\n\"14\",\"flutter-button\"\n\"14\",\"smart-commits\"\n\"14\",\"slsvcutil\"\n\"14\",\"removable\"\n\"14\",\"smaato\"\n\"14\",\"feature-clustering\"\n\"14\",\"intercom-ios\"\n\"14\",\"deadline-timer\"\n\"14\",\"process-migration\"\n\"14\",\"privileged-functions\"\n\"14\",\"xsuperobject\"\n\"14\",\"edge-runtime\"\n\"14\",\"procobol\"\n\"14\",\"grid.js\"\n\"14\",\"yolk\"\n\"14\",\"react-slingshot\"\n\"14\",\"dd4t\"\n\"14\",\"apache-falcon\"\n\"14\",\"telerik-radlistbox\"\n\"14\",\"multiscroll.js\"\n\"14\",\"edifabric\"\n\"14\",\"mate-desktop\"\n\"14\",\"annotatorjs\"\n\"14\",\"profunctor\"\n\"14\",\"intellij-platform-psi\"\n\"14\",\"multi-value-dictionary\"\n\"14\",\"react-native-map-clustering\"\n\"14\",\"telerik-datepicker\"\n\"14\",\"preview-pane\"\n\"14\",\"clojureql\"\n\"14\",\"gridlength\"\n\"14\",\"deconstructor\"\n\"14\",\"mvc.jquery.datatables\"\n\"14\",\"dbms-redefinition\"\n\"14\",\"antlrworks2\"\n\"14\",\"matlabcontrol\"\n\"14\",\"installr\"\n\"14\",\"inspections\"\n\"14\",\"greasekit\"\n\"14\",\"telegraph\"\n\"14\",\"deep-fake\"\n\"14\",\"apache-james\"\n\"14\",\"easytrieve\"\n\"14\",\"anvil\"\n\"14\",\"ssdl\"\n\"14\",\"jsog\"\n\"14\",\"cdetailview\"\n\"14\",\"pageheap\"\n\"14\",\"carter\"\n\"14\",\"snapshot-view\"\n\"14\",\"smooth-numbers\"\n\"14\",\"swiftui-navigation\"\n\"14\",\"rx-javafx\"\n\"14\",\"cassandra-node-driver\"\n\"14\",\"directory-upload\"\n\"14\",\"platform-independence\"\n\"14\",\"jssh\"\n\"14\",\"platform-detection\"\n\"14\",\"smoke\"\n\"14\",\"makegood\"\n\"14\",\"frontity\"\n\"14\",\"dispatchworkitem\"\n\"14\",\"xgoogle\"\n\"14\",\"flare3d\"\n\"14\",\"ngx-swiper-wrapper\"\n\"14\",\"swiftui-alert\"\n\"14\",\"sn\"\n\"14\",\"python-envoy\"\n\"14\",\"mapdispatchtoprops\"\n\"14\",\"flamelink-cms\"\n\"14\",\"chatsdk\"\n\"14\",\"jsyn\"\n\"14\",\"imageset\"\n\"14\",\"appcelerator-acs\"\n\"14\",\"js-scrollby\"\n\"14\",\"jsjac\"\n\"14\",\"mapmyfitness\"\n\"14\",\"labelled-generic\"\n\"14\",\"labelfunction\"\n\"14\",\"label-for\"\n\"14\",\"unity3d-cloud-build\"\n\"14\",\"dbext\"\n\"14\",\"self-documenting-code\"\n\"14\",\"playgrounds\"\n\"14\",\"conntrack\"\n\"14\",\"jtooltip\"\n\"14\",\"inappsettings\"\n\"14\",\"fixed-format\"\n\"14\",\"snowballanalyzer\"\n\"14\",\"mailsettings\"\n\"14\",\"paillier\"\n\"14\",\"vrvideoview\"\n\"14\",\"ultidev\"\n\"14\",\"unison-lang\"\n\"14\",\"apache-twill\"\n\"14\",\"apollo-link-rest\"\n\"14\",\"pakyow\"\n\"14\",\"palantir-foundry-security\"\n\"14\",\"datastax-search\"\n\"14\",\"phundament\"\n\"14\",\"rust-embedded\"\n\"14\",\"rust-diesel-mysql\"\n\"14\",\"soap-serialization\"\n\"14\",\"firefox-57+\"\n\"14\",\"fuelphp-routing\"\n\"14\",\"firelens\"\n\"14\",\"kubeflow-kserve\"\n\"14\",\"firefox-addon-bootstrap\"\n\"14\",\"firefox-addon-overlay\"\n\"14\",\"apdex\"\n\"14\",\"rust-2018\"\n\"14\",\"pandadoc\"\n\"14\",\"pike\"\n\"14\",\"chunkypng\"\n\"14\",\"contactsui\"\n\"14\",\"mapbox-static-maps\"\n\"14\",\"p4eclipse\"\n\"14\",\"python-o365\"\n\"14\",\"chatgpt-plugin\"\n\"14\",\"owin.security\"\n\"14\",\"jsonelement\"\n\"14\",\"xlrelease\"\n\"14\",\"blackberry-os5\"\n\"14\",\"pingouin\"\n\"14\",\"addcallback\"\n\"14\",\"symfony-translator\"\n\"14\",\"distutils2\"\n\"14\",\"switchcontrol\"\n\"14\",\"jstockchart\"\n\"14\",\"flex2\"\n\"14\",\"xlet\"\n\"14\",\"add-custom-target\"\n\"14\",\"file-sorting\"\n\"14\",\"xlsx-js\"\n\"14\",\"symja\"\n\"14\",\"flatten-pdf\"\n\"14\",\"ngredux\"\n\"14\",\"smart-listing\"\n\"14\",\"file-saver\"\n\"14\",\"function-query\"\n\"14\",\"sofia-sip\"\n\"14\",\"ccc\"\n\"14\",\"ng-dropdown-multiselect\"\n\"14\",\"jsc3d\"\n\"14\",\"void-safety\"\n\"14\",\"flatui\"\n\"14\",\"bitrock\"\n\"14\",\"smtp-server\"\n\"14\",\"swiftype\"\n\"14\",\"bitpay\"\n\"14\",\"playback-rate\"\n\"14\",\"ngtoast\"\n\"14\",\"vaadin-testbench\"\n\"14\",\"aif360\"\n\"14\",\"windows-embedded-8\"\n\"14\",\"django-ratings\"\n\"14\",\"rtl-language\"\n\"14\",\"nant-task\"\n\"14\",\"ag-charts-react\"\n\"14\",\"mylyn-wikitext\"\n\"14\",\"kinto\"\n\"14\",\"mysql-error-1451\"\n\"14\",\"dlm\"\n\"14\",\"key-storage\"\n\"14\",\"aws-mediastore\"\n\"14\",\"aws-ios\"\n\"14\",\"rtw\"\n\"14\",\"nameservice\"\n\"14\",\"mysql-error-1170\"\n\"14\",\"kigg\"\n\"14\",\"mysql-error-1049\"\n\"14\",\"name-length\"\n\"14\",\"rtmpd\"\n\"14\",\"windows-phone-sl-8.1\"\n\"14\",\"call-flow\"\n\"14\",\"win32serviceutil\"\n\"14\",\"nam\"\n\"14\",\"aion\"\n\"14\",\"django-lfs\"\n\"14\",\"django-rules\"\n\"14\",\"r-text\"\n\"14\",\"unityvs\"\n\"14\",\"vendoring\"\n\"14\",\"avcapturephotosettings\"\n\"14\",\"csproj-user\"\n\"14\",\"red5-recorder\"\n\"14\",\"vb-power-pack\"\n\"14\",\"angularjs-q\"\n\"14\",\"vue-treeselect\"\n\"14\",\"routedevents\"\n\"14\",\"ice4j\"\n\"14\",\"simpleioc\"\n\"14\",\"fakeweb\"\n\"14\",\"csdl\"\n\"14\",\"cscfg\"\n\"14\",\"google-trusted-stores\"\n\"14\",\"ibm-datacap\"\n\"14\",\"cs0246\"\n\"14\",\"angular-rc5\"\n\"14\",\"angular-router-events\"\n\"14\",\"crystal-reports-export\"\n\"14\",\"roundedcorners-dropshadow\"\n\"14\",\"oracle-rdb\"\n\"14\",\"vector2\"\n\"14\",\"vc10\"\n\"14\",\"angular-touch\"\n\"14\",\"session-reuse\"\n\"14\",\"session-replay\"\n\"14\",\"grails-2.0.4\"\n\"14\",\"animsition\"\n\"14\",\"session-less\"\n\"14\",\"fastclick\"\n\"14\",\"serilog-expressions\"\n\"14\",\"vc90\"\n\"14\",\"serverfbml\"\n\"14\",\"document-directory\"\n\"14\",\"hystrix-dashboard\"\n\"14\",\"fastify-swagger\"\n\"14\",\"metricsgraphicsjs\"\n\"14\",\"database-fragmentation\"\n\"14\",\"method-combination\"\n\"14\",\"server-side-sync\"\n\"14\",\"meteor-collection-hooks\"\n\"14\",\"serviceextension\"\n\"14\",\"datadirect\"\n\"14\",\"metapost\"\n\"14\",\"grammy\"\n\"14\",\"napa\"\n\"14\",\"dlt-daemon\"\n\"14\",\"sitecore-commerce-server\"\n\"14\",\"pychef\"\n\"14\",\"pychecker\"\n\"14\",\"gpuarray\"\n\"14\",\"iauthorizationfilter\"\n\"14\",\"pycel\"\n\"14\",\"updatepanel-progressbar\"\n\"14\",\"wampsharp\"\n\"14\",\"validform\"\n\"14\",\"react-universal\"\n\"14\",\"captured-variable\"\n\"14\",\"indy-node\"\n\"14\",\"react-vis-network\"\n\"14\",\"jmathplot\"\n\"14\",\"read-access\"\n\"14\",\"ora-01036\"\n\"14\",\"verizon\"\n\"14\",\"ora-01403\"\n\"14\",\"gouraud\"\n\"14\",\"card-flip\"\n\"14\",\"ora-01461\"\n\"14\",\"single-source\"\n\"14\",\"indexwriter\"\n\"14\",\"gradcam\"\n\"14\",\"facebook-public-feed-api\"\n\"14\",\"css-layer\"\n\"14\",\"gradientstop\"\n\"14\",\"docker-aws\"\n\"14\",\"alamofire-upload\"\n\"14\",\"wasabi-hot-cloud-storage\"\n\"14\",\"single-abstract-method\"\n\"14\",\"realm-studio\"\n\"14\",\"ruby-thread\"\n\"14\",\"crowdin\"\n\"14\",\"rnetlogo\"\n\"14\",\"ruby-watir\"\n\"14\",\"alcatel-ot\"\n\"14\",\"vensim\"\n\"14\",\"gradle-play-publisher\"\n\"14\",\"unity-xr\"\n\"14\",\"gulp-jasmine\"\n\"14\",\"nsdmanager\"\n\"14\",\"npz-file\"\n\"14\",\"nem\"\n\"14\",\"crashpad\"\n\"14\",\"wso2-choreo\"\n\"14\",\"svggraph\"\n\"14\",\"wso2-ml\"\n\"14\",\"neovis\"\n\"14\",\"ndesk.options\"\n\"14\",\"denormal-numbers\"\n\"14\",\"iodocs\"\n\"14\",\"grunt-connect\"\n\"14\",\"path-to-regexp\"\n\"14\",\"android-seek\"\n\"14\",\"scalafix\"\n\"14\",\"bpg\"\n\"14\",\"count-min-sketch\"\n\"14\",\"invantive-query-tool\"\n\"14\",\"createchildcontrols\"\n\"14\",\"worhp\"\n\"14\",\"jquery-uniform\"\n\"14\",\"countdownevent\"\n\"14\",\"hotfolder\"\n\"14\",\"dependabot-script\"\n\"14\",\"braintree-javascript\"\n\"14\",\"intrinsicattributes\"\n\"14\",\"grunt-init\"\n\"14\",\"entity-data-model\"\n\"14\",\"nscala-time\"\n\"14\",\"neoxygen\"\n\"14\",\"sas-gtl\"\n\"14\",\"sas-ds2\"\n\"14\",\"portsip\"\n\"14\",\"andromda\"\n\"14\",\"boost-stacktrace\"\n\"14\",\"erc1155\"\n\"14\",\"neo4j-shell\"\n\"14\",\"android-xml-attribute\"\n\"14\",\"delayed-paperclip\"\n\"14\",\"silvershop\"\n\"14\",\"portable-database\"\n\"14\",\"jquery-filer\"\n\"14\",\"twine-game-engine\"\n\"14\",\"kademi\"\n\"14\",\"nspr\"\n\"14\",\"nspointerarray\"\n\"14\",\"one-time-pad\"\n\"14\",\"azure-arc\"\n\"14\",\"surroundscm\"\n\"14\",\"bootstrap-affix\"\n\"14\",\"silk\"\n\"14\",\"jquery-mobile-table\"\n\"14\",\"onfocusout\"\n\"14\",\"html.checkbox\"\n\"14\",\"arago\"\n\"14\",\"interix\"\n\"14\",\"gss\"\n\"14\",\"neocomplete\"\n\"14\",\"sigqueue\"\n\"14\",\"applescript-studio\"\n\"14\",\"azure-application-proxy\"\n\"14\",\"swagger-play2\"\n\"14\",\"mojoportal\"\n\"14\",\"on-lisp\"\n\"14\",\"htcondor\"\n\"14\",\"devel-nytprof\"\n\"14\",\"bootstrap-toast\"\n\"14\",\"ept\"\n\"14\",\"hslf\"\n\"14\",\"in-place-editor\"\n\"14\",\"nsimagecell\"\n\"14\",\"android-studio-giraffe\"\n\"14\",\"bot-emulator\"\n\"14\",\"infection\"\n\"14\",\"epf\"\n\"14\",\"bluetooth-5\"\n\"14\",\"svg-rect\"\n\"14\",\"svg-salamander\"\n\"14\",\"onset-detection\"\n\"14\",\"bosch-iot-suite\"\n\"14\",\"jquery-triggerhandler\"\n\"14\",\"epilogue\"\n\"14\",\"oodb\"\n\"14\",\"hql-delete\"\n\"14\",\"design-surface\"\n\"14\",\"episerver-find\"\n\"14\",\"typebox\"\n\"14\",\"hping\"\n\"14\",\"postgres-11\"\n\"14\",\"twig.js\"\n\"14\",\"spring-cloud-deployer-kubernetes\"\n\"14\",\"pdfpage\"\n\"14\",\"gulp-webpack\"\n\"14\",\"pcov\"\n\"14\",\"svg-pattern\"\n\"14\",\"brio\"\n\"14\",\"jasmine-matchers\"\n\"14\",\"kotlin-maven-plugin\"\n\"14\",\"ktpass\"\n\"14\",\"vmime\"\n\"14\",\"observable-plot\"\n\"14\",\"kotlin-logging\"\n\"14\",\"brother-bpac\"\n\"14\",\"abmultivalue\"\n\"14\",\"objectbox-java\"\n\"14\",\"coffeekup\"\n\"14\",\"pyelftools\"\n\"14\",\"codebird\"\n\"14\",\"f#-3.1\"\n\"14\",\"kotlin-contracts\"\n\"14\",\"visa-api\"\n\"14\",\"pygmentize\"\n\"14\",\"javonet\"\n\"14\",\"vliw\"\n\"14\",\"accent-color\"\n\"14\",\"pyexcelerator\"\n\"14\",\"overlay-view\"\n\"14\",\"mac-dashboard-widget\"\n\"14\",\"vitis-ai\"\n\"14\",\"facebook-account-kit\"\n\"14\",\"3dr\"\n\"14\",\"aad-pod-identity\"\n\"14\",\"asyncappender\"\n\"14\",\"dokka\"\n\"14\",\"codexl\"\n\"14\",\"misfire-instruction\"\n\"14\",\"setdlldirectory\"\n\"14\",\"r-collapse\"\n\"14\",\"kostache\"\n\"14\",\"rcall\"\n\"14\",\"microsoft-search-server\"\n\"14\",\"ucma2.0\"\n\"14\",\"andengine-gles-2\"\n\"14\",\"brownfield\"\n\"14\",\"microsoft-live-meeting\"\n\"14\",\"bruno\"\n\"14\",\"ksonnet\"\n\"14\",\"attachment-field\"\n\"14\",\"midas-server\"\n\"14\",\"async-graphql\"\n\"14\",\"browser-based\"\n\"14\",\"netmon\"\n\"14\",\"objgraph\"\n\"14\",\"kovan\"\n\"14\",\"luxonis\"\n\"14\",\"kongregate\"\n\"14\",\"maestro\"\n\"14\",\"visualstudio.testtools\"\n\"14\",\"system.web.mail\"\n\"14\",\"nettle\"\n\"14\",\"bricscad\"\n\"14\",\"rich-communications-services\"\n\"14\",\"domain-calculus\"\n\"14\",\"ringout\"\n\"14\",\"facebook-graph-api-v2.5\"\n\"14\",\"authkit\"\n\"14\",\"codemaid\"\n\"14\",\"facebook-graph-api-v2.6\"\n\"14\",\"fpdf2\"\n\"14\",\"pygubu\"\n\"14\",\"pyhf\"\n\"14\",\"rates\"\n\"14\",\"lua-resty-openidc\"\n\"14\",\"pyper\"\n\"14\",\"minibufexplorer\"\n\"14\",\"magma-ca\"\n\"14\",\"random-testing\"\n\"14\",\"pypdf4\"\n\"14\",\"shaped-window\"\n\"14\",\"shoulda-matchers\"\n\"14\",\"r-graphviz\"\n\"14\",\"rfc2396\"\n\"14\",\"showuserlocation\"\n\"14\",\"asyncpostbackerror\"\n\"14\",\"google-base\"\n\"14\",\"buildforge\"\n\"14\",\"winprt\"\n\"14\",\"java.security\"\n\"14\",\"wordperfect\"\n\"14\",\"java-scripting-engine\"\n\"14\",\"miniport\"\n\"14\",\"libfuse\"\n\"14\",\"sgp4\"\n\"14\",\"vimium\"\n\"14\",\"rfc4180\"\n\"14\",\"netcoreapp2.1\"\n\"14\",\"videosdk.live\"\n\"14\",\"raspbian-wheezy\"\n\"14\",\"javascript-date\"\n\"14\",\"shinycssloaders\"\n\"14\",\"wonderpush\"\n\"14\",\"jautodoc\"\n\"14\",\"asyncpraw\"\n\"14\",\"orcid\"\n\"14\",\"mailinator\"\n\"14\",\"network-storage\"\n\"14\",\"goconvey\"\n\"14\",\".net-core-sdk\"\n\"14\",\"form-editing\"\n\"14\",\"shopifyscripts\"\n\"14\",\"frameless\"\n\"14\",\"lumen-routing\"\n\"14\",\"minio-client\"\n\"14\",\"typescript-5\"\n\"14\",\"withcontext\"\n\"14\",\"foxids\"\n\"14\",\"pyke\"\n\"14\",\"kloudless\"\n\"14\",\"sysdatetime\"\n\"14\",\"viewmodelfactory\"\n\"14\",\"pyprocessing\"\n\"14\",\"systemd-nspawn\"\n\"14\",\"winpty\"\n\"14\",\"magiczoom\"\n\"14\",\"woocommerce-email\"\n\"14\",\"system-generator\"\n\"14\",\"typeshed\"\n\"14\",\"vim-go\"\n\"14\",\"coldfusion-6\"\n\"14\",\"google-business-profile\"\n\"14\",\"object-inspector\"\n\"14\",\"radicale\"\n\"14\",\"asp.net-core-staticfile\"\n\"14\",\"hardware-infrastructure\"\n\"14\",\"mkcoordinatespan\"\n\"14\",\"x2go\"\n\"14\",\"uidocumentmenuvc\"\n\"14\",\"azure-in-role-cache\"\n\"14\",\"spuser\"\n\"14\",\"nonatomic\"\n\"14\",\"dictview\"\n\"14\",\"dicttoxml\"\n\"14\",\"asn1crypto\"\n\"14\",\"tailrecursion-modulo-cons\"\n\"14\",\"mknetworkengine\"\n\"14\",\"noncharacter\"\n\"14\",\"diffsharp\"\n\"14\",\"mobileme\"\n\"14\",\"hirefire\"\n\"14\",\"sp-who2\"\n\"14\",\"gulp-zip\"\n\"14\",\"dex2oat\"\n\"14\",\"hangfire-console\"\n\"14\",\"azure-text-translation\"\n\"14\",\"point-in-time-recovery\"\n\"14\",\"wxlua\"\n\"14\",\"azure-packaging\"\n\"14\",\"nsurldownload\"\n\"14\",\"qxmpp\"\n\"14\",\"wx.html2\"\n\"14\",\"ntt\"\n\"14\",\"azure-tableclient\"\n\"14\",\"mode-analytics\"\n\"14\",\"tcpmon\"\n\"14\",\"r3f\"\n\"14\",\"tcomponent\"\n\"14\",\"plop\"\n\"14\",\"handbrakecli\"\n\"14\",\"redips.drag\"\n\"14\",\"redhat-datagrid\"\n\"14\",\"hopper\"\n\"14\",\"uiprintinfo\"\n\"14\",\"polar-plot\"\n\"14\",\"mobilink\"\n\"14\",\"quotaguard\"\n\"14\",\"hootsuite\"\n\"14\",\"sqlake\"\n\"14\",\"spring-surf\"\n\"14\",\"uiactionsheetdelegate\"\n\"14\",\"direct-buffer\"\n\"14\",\"sqljocky\"\n\"14\",\"diode\"\n\"14\",\"azure-service-runtime\"\n\"14\",\"task-tracking\"\n\"14\",\"assembly.reflectiononly\"\n\"14\",\"timetk\"\n\"14\",\"tkinter.text\"\n\"14\",\"jackson-annotations\"\n\"14\",\"modal-logic\"\n\"14\",\"moarvm\"\n\"14\",\"uipi\"\n\"14\",\"quicken\"\n\"14\",\"polyglot-notebooks\"\n\"14\",\"hiawatha\"\n\"14\",\"android-httptransport\"\n\"14\",\"executestorequery\"\n\"14\",\"scnlight\"\n\"14\",\"drupal-node-hook\"\n\"14\",\"exide\"\n\"14\",\"scosta\"\n\"14\",\"drupal-roles\"\n\"14\",\"highperformance\"\n\"14\",\"gcc5.2\"\n\"14\",\"drupal-behaviors\"\n\"14\",\"xcode9-beta5\"\n\"14\",\"xamarin-component\"\n\"14\",\"scalala\"\n\"14\",\"dropshadoweffect\"\n\"14\",\"drop-cap\"\n\"14\",\"sciter\"\n\"14\",\"gbdk\"\n\"14\",\"exceptionfilterattribute\"\n\"14\",\"xaml-composition\"\n\"14\",\"scrapyd-deploy\"\n\"14\",\"drag-event\"\n\"14\",\"buzz.js\"\n\"14\",\"xcode-tools\"\n\"14\",\"expansion-files\"\n\"14\",\"g729\"\n\"14\",\"android-compose-dialog\"\n\"14\",\"dropdownchecklist\"\n\"14\",\"bwwalkthrough\"\n\"14\",\"ispeech\"\n\"14\",\"bytedeco-javacv\"\n\"14\",\"convox\"\n\"14\",\"dsx-desktop\"\n\"14\",\"android-bubbles\"\n\"14\",\"on-disk\"\n\"14\",\"node-gcm\"\n\"14\",\"react-image-lightbox\"\n\"14\",\"android-jetpack-compose-modifier\"\n\"14\",\"electric-fence\"\n\"14\",\"activex-documents\"\n\"14\",\"node-github\"\n\"14\",\"io-ts-library\"\n\"14\",\"mp3agic\"\n\"14\",\"qrubberband\"\n\"14\",\"nmi\"\n\"14\",\"nmatrix\"\n\"14\",\"etimedout\"\n\"14\",\"activitypub\"\n\"14\",\"elementary-functions\"\n\"14\",\"memurai\"\n\"14\",\"re2c\"\n\"14\",\"httpuv\"\n\"14\",\"geographic-lib\"\n\"14\",\"ipaas\"\n\"14\",\"eglibc\"\n\"14\",\"log-forging\"\n\"14\",\"textexpander\"\n\"14\",\"react-alt\"\n\"14\",\"ios10.3.2\"\n\"14\",\"log4c\"\n\"14\",\"octobercms-user-plugin\"\n\"14\",\"strongly-typed-enum\"\n\"14\",\"prometheus-python-client\"\n\"14\",\"resharper-2017\"\n\"14\",\"laravel-jwt\"\n\"14\",\"performance-estimation\"\n\"14\",\"getpasswd\"\n\"14\",\"strobe-media-playback\"\n\"14\",\"hyperfilesql\"\n\"14\",\"pfsense\"\n\"14\",\"react-effects\"\n\"14\",\"odk-xform\"\n\"14\",\"google-cloud-asset-inventory\"\n\"14\",\"mozilla-prism\"\n\"14\",\"logrocket\"\n\"14\",\"strava-api-v3\"\n\"14\",\"movewindow\"\n\"14\",\"nircmd\"\n\"14\",\"stripe-customer-portal\"\n\"14\",\"requirehttps\"\n\"14\",\"google-cloud-auth\"\n\"14\",\"strcat-s\"\n\"14\",\"evo\"\n\"14\",\"prooph\"\n\"14\",\"googledns\"\n\"14\",\"tesseract-5.x\"\n\"14\",\"acr1252\"\n\"14\",\"textboxlist\"\n\"14\",\"google-cloud-node\"\n\"14\",\"requestify\"\n\"14\",\"google-cloud-proxy\"\n\"14\",\"streamex\"\n\"14\",\"streaming-analytics\"\n\"14\",\"requestly\"\n\"14\",\"google-cloud-vm\"\n\"14\",\"chainercv\"\n\"14\",\"getschema\"\n\"14\",\"combinedchart\"\n\"14\",\"qbuffer\"\n\"14\",\"elasticsearch-river\"\n\"14\",\"spleeter\"\n\"14\",\"omnikey\"\n\"14\",\"react-masonry\"\n\"14\",\"elastic-network-interface\"\n\"14\",\"textrank\"\n\"14\",\"cgns\"\n\"14\",\"ohlcv\"\n\"14\",\"cucumber-cpp\"\n\"14\",\"ipycytoscape\"\n\"14\",\"textswitcher\"\n\"14\",\"android-rendering\"\n\"14\",\"iphone-8\"\n\"14\",\"angular2-modal\"\n\"14\",\"omnicontacts-gem\"\n\"14\",\"chartnew.js\"\n\"14\",\"terraform-loop\"\n\"14\",\"irate\"\n\"14\",\"mercurial-phases\"\n\"14\",\"node-expat\"\n\"14\",\"mercurial-api\"\n\"14\",\"cfimport\"\n\"14\",\"ctransformers\"\n\"14\",\"project-gutenberg\"\n\"14\",\"cfhttpparam\"\n\"14\",\"column-chooser\"\n\"14\",\"cudpp\"\n\"14\",\"spinach\"\n\"14\",\"android-monitor\"\n\"14\",\"layoutpanels\"\n\"14\",\"spatial-regression\"\n\"14\",\"qdesktopservices\"\n\"14\",\"speedglm\"\n\"14\",\"om-next\"\n\"14\",\"spinalhdl\"\n\"14\",\"lateinit\"\n\"14\",\"offsite\"\n\"14\",\"restxq\"\n\"14\",\"elastislide\"\n\"14\",\"colordrawable\"\n\"14\",\"nikola\"\n\"14\",\"texture-packing\"\n\"14\",\"commodity\"\n\"14\",\"android-layout-direction\"\n\"14\",\"android-largeheap\"\n\"14\",\"geocaching\"\n\"14\",\"cgan\"\n\"14\",\"specialized-annotation\"\n\"14\",\"ipp-qbd-sync\"\n\"14\",\"collibra\"\n\"14\",\"layout-engine\"\n\"14\",\"android-jetpack-security\"\n\"14\",\"cfdump\"\n\"14\",\"project-conversion\"\n\"14\",\"tfs-aggregator\"\n\"14\",\"hexchat\"\n\"14\",\"weboptimizer\"\n\"14\",\"multicorn\"\n\"14\",\"fmodf\"\n\"14\",\"qshareddata\"\n\"14\",\"heroku-ssl\"\n\"14\",\"sourcesafe-6.0\"\n\"14\",\"v2ray\"\n\"14\",\"touch-typing\"\n\"14\",\"bigloo\"\n\"14\",\"embedded-control\"\n\"14\",\"sonarqube-5.5\"\n\"14\",\"emacs25\"\n\"14\",\"tf.dataset\"\n\"14\",\"webpack-mix\"\n\"14\",\"conditional-binding\"\n\"14\",\"elm-ui\"\n\"14\",\"sugarcube\"\n\"14\",\"starkit\"\n\"14\",\"bigcommerce-checkout-sdk\"\n\"14\",\"helenus\"\n\"14\",\"here-autocomplete\"\n\"14\",\"sonic\"\n\"14\",\"iis-arr\"\n\"14\",\"threetenabp\"\n\"14\",\"tibco-gi\"\n\"14\",\"bin2hex\"\n\"14\",\"toscawidgets\"\n\"14\",\"startup-probe\"\n\"14\",\"web3php\"\n\"14\",\"static-text\"\n\"14\",\"state.go\"\n\"14\",\"flutter-typeahead\"\n\"14\",\"tilecache\"\n\"14\",\"web3swift\"\n\"14\",\"imagebitmap\"\n\"14\",\"avaudiosessioncategory\"\n\"14\",\"msan\"\n\"14\",\"array-view\"\n\"14\",\"search-multiple-words\"\n\"14\",\"glpi\"\n\"14\",\"searchactivity\"\n\"14\",\"alliedvision\"\n\"14\",\"ember-concurrency\"\n\"14\",\"yui-uploader\"\n\"14\",\"sealedsecret\"\n\"14\",\"bcc-compiler\"\n\"14\",\"quantities\"\n\"14\",\"amazonica\"\n\"14\",\"msbuild-itemgroup\"\n\"14\",\"zipexception\"\n\"14\",\"bcg\"\n\"14\",\"beginanimations\"\n\"14\",\"befunge\"\n\"14\",\"sdl.net\"\n\"14\",\"preference-v7\"\n\"14\",\"powermta\"\n\"14\",\"qubes-os\"\n\"14\",\"qtopcua\"\n\"14\",\"amazon-ecr-public\"\n\"14\",\"parameterinfo\"\n\"14\",\"precompiled-templates\"\n\"14\",\"scribus\"\n\"14\",\"gnu-apl\"\n\"14\",\"amazon-cloudwatch-synthetics\"\n\"14\",\"identity-delegation\"\n\"14\",\"preact-router\"\n\"14\",\"google-play-console-beta\"\n\"14\",\"parameter-list\"\n\"14\",\"zfdatagrid\"\n\"14\",\"measurestring\"\n\"14\",\"particle.js\"\n\"14\",\"beanstalkc\"\n\"14\",\"cy.intercept\"\n\"14\",\"cycle-plugin\"\n\"14\",\"google-native-ads\"\n\"14\",\"ppd\"\n\"14\",\"google-maps-android-api-1\"\n\"14\",\"quadrilaterals\"\n\"14\",\"paravirtualization\"\n\"14\",\"altiris\"\n\"14\",\"zentyal\"\n\"14\",\"google-maps-compose\"\n\"14\",\"autorepeat\"\n\"14\",\"google-maps-timezone\"\n\"14\",\"parquet-dataset\"\n\"14\",\"alternate-access-mappings\"\n\"14\",\"arduino-cli\"\n\"14\",\"ietester\"\n\"14\",\"git-non-bare-repository\"\n\"14\",\"ieframe.dll\"\n\"14\",\"mudselect\"\n\"14\",\"bazel-genrule\"\n\"14\",\"bazel-extra-action\"\n\"14\",\"user-manual\"\n\"14\",\"qt5.11\"\n\"14\",\"transcendental-equation\"\n\"14\",\"linkedblockingqueue\"\n\"14\",\"embeddinator\"\n\"14\",\"zoom-meeting\"\n\"14\",\"ender\"\n\"14\",\"alibaba-cloud-oss\"\n\"14\",\"compileassemblyfromsource\"\n\"14\",\"urlstream\"\n\"14\",\"userpoints\"\n\"14\",\"component-query\"\n\"14\",\"bespin\"\n\"14\",\"stickynote\"\n\"14\",\"prestashop-helper-classes\"\n\"14\",\"urlimageviewhelper\"\n\"14\",\"powerbi-gateway\"\n\"14\",\"haskell-vector\"\n\"14\",\"stipple\"\n\"14\",\"dart-dev-compiler\"\n\"14\",\"tradestation\"\n\"14\",\"haskell-polysemy\"\n\"14\",\"styleswitching\"\n\"14\",\"line-of-business-app\"\n\"14\",\"autocommenting\"\n\"14\",\"seasoned-schemer\"\n\"14\",\"dapper-rainbow\"\n\"14\",\"yt-project\"\n\"14\",\"autodesk-arvr\"\n\"13\",\"vuestic\"\n\"13\",\"slonik\"\n\"13\",\"truedbgrid\"\n\"13\",\"phprunner\"\n\"13\",\"pritunl\"\n\"13\",\"classiejs\"\n\"13\",\"multiple-inclusions\"\n\"13\",\"fedora12\"\n\"13\",\"jexceljs\"\n\"13\",\"privategpt\"\n\"13\",\"privateobject.invoke\"\n\"13\",\"jcaps\"\n\"13\",\"vue-teleport\"\n\"13\",\"ferror\"\n\"13\",\"base85\"\n\"13\",\"flutter-date-range-picker\"\n\"13\",\"react-native-tools\"\n\"13\",\"llvm-py\"\n\"13\",\"vuetify2\"\n\"13\",\"phpt\"\n\"13\",\"react-native-ui-components\"\n\"13\",\"related-posts\"\n\"13\",\"interface-class\"\n\"13\",\"flutter-expanded\"\n\"13\",\"websphere-6\"\n\"13\",\"clpplus\"\n\"13\",\"cockpit-cms\"\n\"13\",\"sitefinity-3x\"\n\"13\",\"backbase\"\n\"13\",\"telerik-radribbonbar\"\n\"13\",\"mathematica-7\"\n\"13\",\"webservice-discovery\"\n\"13\",\"photoshop-sdk\"\n\"13\",\"xxtea\"\n\"13\",\"fencepost\"\n\"13\",\"apache-fineract\"\n\"13\",\"mathcontext\"\n\"13\",\"matchevaluator\"\n\"13\",\"locally-abstract-type\"\n\"13\",\"jest-image-snapshot\"\n\"13\",\"mvcminiprofiler\"\n\"13\",\"backing\"\n\"13\",\"ecdsasignature\"\n\"13\",\"jblas\"\n\"13\",\"multitasking-gestures\"\n\"13\",\"backgroundtaskidentifier\"\n\"13\",\"cmd2\"\n\"13\",\"eclipse-atl\"\n\"13\",\"jdf\"\n\"13\",\"react-native-modalize\"\n\"13\",\"jepp\"\n\"13\",\"pg-repack\"\n\"13\",\"munge\"\n\"13\",\"feathericons\"\n\"13\",\"vt-x\"\n\"13\",\"yii2-authclient\"\n\"13\",\"eclipse-2018-09\"\n\"13\",\"musicg\"\n\"13\",\"yandexcloud\"\n\"13\",\"vue-data\"\n\"13\",\"background-drawable\"\n\"13\",\"echoprint\"\n\"13\",\"phonegap-gmaps-plugin\"\n\"13\",\"gitg\"\n\"13\",\"gitfs\"\n\"13\",\"instascan\"\n\"13\",\"srtm\"\n\"13\",\"srl\"\n\"13\",\"groebner-basis\"\n\"13\",\"clever-cloud\"\n\"13\",\"ssl-security\"\n\"13\",\"clone-element\"\n\"13\",\"ffmpy\"\n\"13\",\"apache-camel-aws-kinesis\"\n\"13\",\"sslv2\"\n\"13\",\"react-redux-i18n\"\n\"13\",\"skinny-war\"\n\"13\",\"ssms-2005\"\n\"13\",\"intel-inspector\"\n\"13\",\"sstoolkit\"\n\"13\",\"renderx\"\n\"13\",\"intellij-2020\"\n\"13\",\"wechat-auth\"\n\"13\",\"gitlab-wiki\"\n\"13\",\"renderbox\"\n\"13\",\"wh-keyboard-ll\"\n\"13\",\"re-natal\"\n\"13\",\"clientcontext\"\n\"13\",\"clay\"\n\"13\",\"clockrates\"\n\"13\",\"listview-filter\"\n\"13\",\"edge-to-edge\"\n\"13\",\"listviewgroup\"\n\"13\",\"procps\"\n\"13\",\"sjs\"\n\"13\",\"rematch\"\n\"13\",\"baresip\"\n\"13\",\"react-os\"\n\"13\",\"fileitem\"\n\"13\",\"reltool\"\n\"13\",\"web-stomp\"\n\"13\",\"grib-api\"\n\"13\",\"remarks\"\n\"13\",\"wdio-jasmine\"\n\"13\",\"edgecast\"\n\"13\",\"claudia.js\"\n\"13\",\"proc-format\"\n\"13\",\"classwizard\"\n\"13\",\"six-python\"\n\"13\",\"interaction-plot\"\n\"13\",\"getwindowlong\"\n\"13\",\"livegraph\"\n\"13\",\"anonymousidentification\"\n\"13\",\"websphere-process-server\"\n\"13\",\"filesysteminfo\"\n\"13\",\"symfony-finder\"\n\"13\",\"filenet-workplace\"\n\"13\",\"bitdefender\"\n\"13\",\"display-suite\"\n\"13\",\"vrone\"\n\"13\",\"apache-storm-flux\"\n\"13\",\"adobe-cc\"\n\"13\",\"underlyingtype\"\n\"13\",\"safefilehandle\"\n\"13\",\"data-tracing\"\n\"13\",\"umlgraph\"\n\"13\",\"ngx-doc-viewer\"\n\"13\",\"date-fns-tz\"\n\"13\",\"nhibernate-collections\"\n\"13\",\"apache-tailer\"\n\"13\",\"cdk8s\"\n\"13\",\"umn-mapserver\"\n\"13\",\"datashape\"\n\"13\",\"adsl\"\n\"13\",\"ngdraggable\"\n\"13\",\"file-traversal\"\n\"13\",\"packages.json\"\n\"13\",\"pibase\"\n\"13\",\"fiware-poi\"\n\"13\",\"safe-tensors\"\n\"13\",\"ultraesb\"\n\"13\",\"python-requests-json\"\n\"13\",\"fiware-perseo\"\n\"13\",\"chatwoot\"\n\"13\",\"xmlinclude\"\n\"13\",\"db2-content-manager\"\n\"13\",\"umbraco9\"\n\"13\",\"snmpwalk\"\n\"13\",\"cirq\"\n\"13\",\"advanced-indexing\"\n\"13\",\"chdatastructures\"\n\"13\",\"kubernetes-java-client\"\n\"13\",\"js-fancyproductdesigner\"\n\"13\",\"checkeditems\"\n\"13\",\"aframe-react\"\n\"13\",\"cellbrowser\"\n\"13\",\"admin-interface\"\n\"13\",\"checkboxtree\"\n\"13\",\"adhearsion\"\n\"13\",\"manager-app\"\n\"13\",\"jsr233\"\n\"13\",\"semantic-comparison\"\n\"13\",\"xhp\"\n\"13\",\"laravel-data\"\n\"13\",\"pixiedust\"\n\"13\",\"constraintlayout-barrier\"\n\"13\",\"makumba\"\n\"13\",\"binmode\"\n\"13\",\"laraadmin\"\n\"13\",\"jsr170\"\n\"13\",\"marimekko-chart\"\n\"13\",\"cassandraunit\"\n\"13\",\"language-recognition\"\n\"13\",\"blackmagic-design\"\n\"13\",\"funkload\"\n\"13\",\"addressable-gem\"\n\"13\",\"platform-of-trust\"\n\"13\",\"pkcanvasview\"\n\"13\",\"uiuserinterfacestyle\"\n\"13\",\"swiftui-menu\"\n\"13\",\"swiftui-map\"\n\"13\",\"constructor-reference\"\n\"13\",\"langsmith\"\n\"13\",\"maniphest\"\n\"13\",\"mambaforge\"\n\"13\",\"apicurio-registry\"\n\"13\",\"pinot\"\n\"13\",\"xqj\"\n\"13\",\"ui-validate\"\n\"13\",\"lamson\"\n\"13\",\"xlslib\"\n\"13\",\"jsondoc\"\n\"13\",\"rvg\"\n\"13\",\"xenu\"\n\"13\",\"lambda-prolog\"\n\"13\",\"nexus-iq\"\n\"13\",\"freemat\"\n\"13\",\"console-redirect\"\n\"13\",\"apiconnect-test-monitor\"\n\"13\",\"jslint4java\"\n\"13\",\"mainmenu\"\n\"13\",\"pict\"\n\"13\",\"bitbar\"\n\"13\",\"xfl\"\n\"13\",\"jscalendar\"\n\"13\",\"datalore\"\n\"13\",\"page-state\"\n\"13\",\"selflanguage\"\n\"13\",\"s3transfermanager\"\n\"13\",\"jscep\"\n\"13\",\"self-intersection\"\n\"13\",\"xfermode\"\n\"13\",\"crossterm\"\n\"13\",\"aws-authorizer\"\n\"13\",\"keyboard-focus\"\n\"13\",\"crudbooster\"\n\"13\",\"varybyparam\"\n\"13\",\"cross-page-postback\"\n\"13\",\"mysql-error-1050\"\n\"13\",\"iab\"\n\"13\",\"awilix\"\n\"13\",\"cryptlib\"\n\"13\",\"aiosqlite\"\n\"13\",\"air2\"\n\"13\",\"avsystemcontroller\"\n\"13\",\"crysis\"\n\"13\",\"django-saml2-auth\"\n\"13\",\"airflow-connections\"\n\"13\",\"hypriot\"\n\"13\",\"create-react-kotlin-app\"\n\"13\",\"icccm\"\n\"13\",\"avoriaz\"\n\"13\",\"icedtea-web\"\n\"13\",\"alexa-interaction-model\"\n\"13\",\"avcapturevideodataoutput\"\n\"13\",\"iconnectionpoint\"\n\"13\",\"mysqldbcompare\"\n\"13\",\"valentina-studio\"\n\"13\",\"vercel-hyper-terminal\"\n\"13\",\"mysql-date\"\n\"13\",\"ruby-mode\"\n\"13\",\"verify-tests\"\n\"13\",\"django-wysiwyg\"\n\"13\",\"al.exe\"\n\"13\",\"ruby-saml\"\n\"13\",\"ruby-native-extensions\"\n\"13\",\"django-wiki\"\n\"13\",\"aws-emr-studio\"\n\"13\",\"django-weasyprint\"\n\"13\",\"rubypython\"\n\"13\",\"django-syndication\"\n\"13\",\"kendo-maskedtextbox\"\n\"13\",\"kendo-ui-window\"\n\"13\",\"aws-graviton\"\n\"13\",\"purgecss\"\n\"13\",\"docker.dotnet\"\n\"13\",\"realm-net\"\n\"13\",\"pup\"\n\"13\",\"ptv-vissim\"\n\"13\",\"pthread-key-create\"\n\"13\",\"unity-test-tools\"\n\"13\",\"recoverymodel\"\n\"13\",\"pstricks\"\n\"13\",\"pstats\"\n\"13\",\"r-paws\"\n\"13\",\"recvmsg\"\n\"13\",\"pseudolocalization\"\n\"13\",\"angularjs-new-router\"\n\"13\",\"simple-oauth2\"\n\"13\",\"simplenlg\"\n\"13\",\"oracle-cloud-shell\"\n\"13\",\"angular-maps\"\n\"13\",\"oracle-export-dump\"\n\"13\",\"simplejdbcinsert\"\n\"13\",\"faktor-ips\"\n\"13\",\"angular-nglist\"\n\"13\",\"routed\"\n\"13\",\"angular-permission\"\n\"13\",\"routeboxer\"\n\"13\",\"oracle-objects\"\n\"13\",\"jplist\"\n\"13\",\"oracle-soda\"\n\"13\",\"angular-structural-directive\"\n\"13\",\"doctrine-collection\"\n\"13\",\"animatewindow\"\n\"13\",\"fast-android-networking\"\n\"13\",\"roslynpad\"\n\"13\",\"joypad\"\n\"13\",\"microclimate\"\n\"13\",\"jotm\"\n\"13\",\"jooq-sbt-plugin\"\n\"13\",\"rootbeer\"\n\"13\",\"server2go\"\n\"13\",\"rom-rb\"\n\"13\",\"data-access-app-block\"\n\"13\",\"servertag\"\n\"13\",\"watson-explorer\"\n\"13\",\"metasyntactic-variable\"\n\"13\",\"joda-convert\"\n\"13\",\"data-execution-prevention\"\n\"13\",\"datagridrow\"\n\"13\",\"watchos-8\"\n\"13\",\"datahub\"\n\"13\",\"jncryptor\"\n\"13\",\"vue-typescript\"\n\"13\",\"dogpile.cache\"\n\"13\",\"gradle-groovy-dsl\"\n\"13\",\"jhipster-blueprint\"\n\"13\",\"goslate\"\n\"13\",\"jikes\"\n\"13\",\"jing\"\n\"13\",\"jinjava\"\n\"13\",\"gpyopt\"\n\"13\",\"gpu-instancing\"\n\"13\",\"wal-e\"\n\"13\",\"jjaql\"\n\"13\",\"windows-client\"\n\"13\",\"kinterbasdb\"\n\"13\",\"windows-core-audio\"\n\"13\",\"window-scroll\"\n\"13\",\"windows-10-sdk\"\n\"13\",\"camera-projection\"\n\"13\",\"django-evolution\"\n\"13\",\"named-function\"\n\"13\",\"caniuse\"\n\"13\",\"windows-logon\"\n\"13\",\"sharelink\"\n\"13\",\"urlcomponents\"\n\"13\",\"kibana-3\"\n\"13\",\"wildfly-27\"\n\"13\",\"wildfly-14\"\n\"13\",\"camel-file\"\n\"13\",\"dmenu\"\n\"13\",\"opserver\"\n\"13\",\"mysql-fabric\"\n\"13\",\"dmoz\"\n\"13\",\"pychart\"\n\"13\",\"sharepoint-alerts\"\n\"13\",\"pybuffer\"\n\"13\",\"pybit\"\n\"13\",\"pybel\"\n\"13\",\"sirius\"\n\"13\",\"callscreeningservice\"\n\"13\",\"react-stripe\"\n\"13\",\"dnp3\"\n\"13\",\"aggdraw\"\n\"13\",\"pyapns\"\n\"13\",\"keyman-developer\"\n\"13\",\"keyman\"\n\"13\",\"ora-00928\"\n\"13\",\"rubyamf\"\n\"13\",\"carbon-components-svelte\"\n\"13\",\"callcontext\"\n\"13\",\"mysql-error-1140\"\n\"13\",\"carbonkit\"\n\"13\",\"ruby-csv\"\n\"13\",\"realm-browser\"\n\"13\",\"mysql-error-1060\"\n\"13\",\"jvm-codecache\"\n\"13\",\"katta\"\n\"13\",\"sass-variables\"\n\"13\",\"sa-mp\"\n\"13\",\"inline-c\"\n\"13\",\"cpn-tools\"\n\"13\",\"createbitmap\"\n\"13\",\"nestedlayout\"\n\"13\",\"scala.rx\"\n\"13\",\"android-studio-3.5.3\"\n\"13\",\"surface-hub\"\n\"13\",\"azure-data-share\"\n\"13\",\"svn-switch\"\n\"13\",\"android-studio-3.5.1\"\n\"13\",\"kawa\"\n\"13\",\"karpenter\"\n\"13\",\"blazor-pwa\"\n\"13\",\"modular-monolith\"\n\"13\",\"monaco-languageserver\"\n\"13\",\"wssf\"\n\"13\",\"satpy\"\n\"13\",\"corenlp-server\"\n\"13\",\"moment-range\"\n\"13\",\"twilio-verify\"\n\"13\",\"juliadb\"\n\"13\",\"android-subscriptionmanager\"\n\"13\",\"injected-class-name\"\n\"13\",\"wpf-listview\"\n\"13\",\"modelstatedictionary\"\n\"13\",\"android-update-sdk\"\n\"13\",\"just\"\n\"13\",\"jupyter-server\"\n\"13\",\"wsdl2php\"\n\"13\",\"twilio-cli\"\n\"13\",\"workqueue\"\n\"13\",\"sbom\"\n\"13\",\"kakoune\"\n\"13\",\"mod-proxy-wstunnel\"\n\"13\",\"sap-analysis-for-office\"\n\"13\",\"kamal\"\n\"13\",\"android-spellcheck\"\n\"13\",\"supportfragmentmanager\"\n\"13\",\"ttimer\"\n\"13\",\"coroutineworker\"\n\"13\",\"apple-watch-glances\"\n\"13\",\"mongify\"\n\"13\",\"android-test-orchestrator\"\n\"13\",\"wordpress-roles\"\n\"13\",\"innerxml\"\n\"13\",\"jquery-mobile-checkbox\"\n\"13\",\"nshost\"\n\"13\",\"jquery-mobile-radio\"\n\"13\",\"input-iterator\"\n\"13\",\"near-sdk-rs\"\n\"13\",\"ncron\"\n\"13\",\"dynamic-picklist-vtiger\"\n\"13\",\"html-target\"\n\"13\",\"bound-variable\"\n\"13\",\"nemlogin\"\n\"13\",\"boxee\"\n\"13\",\"opal-framework\"\n\"13\",\"pcl-crypto\"\n\"13\",\"guacamole-common.js\"\n\"13\",\"boa-constructor\"\n\"13\",\"dynamic-management-views\"\n\"13\",\"developer-payload\"\n\"13\",\"easybind\"\n\"13\",\"gulp-newer\"\n\"13\",\"pcmanfm\"\n\"13\",\"b-prolog\"\n\"13\",\"inno-tools-downloader\"\n\"13\",\"oovoo\"\n\"13\",\"enroute\"\n\"13\",\"aquafold\"\n\"13\",\"interposing\"\n\"13\",\"bluespec\"\n\"13\",\"jquery-color\"\n\"13\",\"ncclient\"\n\"13\",\"descartes\"\n\"13\",\"ion-content\"\n\"13\",\"appsweep\"\n\"13\",\"postgresql.conf\"\n\"13\",\"blur-admin\"\n\"13\",\"pdm\"\n\"13\",\"envstats\"\n\"13\",\"dynamics365-app-mobile\"\n\"13\",\"pdh\"\n\"13\",\"onpreferenceclicklistener\"\n\"13\",\"enumerize\"\n\"13\",\"postgres-9.4\"\n\"13\",\"bluetooth-lowenergy-4.2\"\n\"13\",\"neatupload\"\n\"13\",\"errorcollector\"\n\"13\",\"dynamic-c\"\n\"13\",\"jquery-gmap\"\n\"13\",\"bookblock\"\n\"13\",\"nbehave\"\n\"13\",\"bootstrapvalidator-1000hz\"\n\"13\",\"early-return\"\n\"13\",\"pot\"\n\"13\",\"grunt-ngdocs\"\n\"13\",\"pathelement\"\n\"13\",\"payone\"\n\"13\",\"nslevelindicator\"\n\"13\",\"introsort\"\n\"13\",\"jqplot-highlighter\"\n\"13\",\"jqprint\"\n\"13\",\"grunt-express\"\n\"13\",\"boost-parameter\"\n\"13\",\"jquery-flexbox\"\n\"13\",\"applocalizations\"\n\"13\",\"delegated-properties\"\n\"13\",\"earthpy\"\n\"13\",\"paypal-vault\"\n\"13\",\"delta-rs\"\n\"13\",\"ndebug\"\n\"13\",\"detailslist\"\n\"13\",\"bluefish\"\n\"13\",\"migrate-mongo\"\n\"13\",\"codeigniter-a3m\"\n\"13\",\"miniupnpc\"\n\"13\",\"jave\"\n\"13\",\"nxml\"\n\"13\",\"ranch\"\n\"13\",\"wolfram-cdf\"\n\"13\",\"typo3-form\"\n\"13\",\"rgbcolor\"\n\"13\",\"typed-dataset\"\n\"13\",\"virtual-topic\"\n\"13\",\"braintree-vault\"\n\"13\",\"libneo4j-client\"\n\"13\",\".net-standard-1.6\"\n\"13\",\"pyresttest\"\n\"13\",\"shiva3d\"\n\"13\",\"system.graphics\"\n\"13\",\"f#-compiler-services\"\n\"13\",\"visio-2010\"\n\"13\",\"nethack\"\n\"13\",\"wkinterfacebutton\"\n\"13\",\"magicline\"\n\"13\",\"builder.io\"\n\"13\",\"ubikloadpack\"\n\"13\",\"shinysky\"\n\"13\",\"coclass\"\n\"13\",\"shape-outside\"\n\"13\",\"google-app-engine-launch\"\n\"13\",\"object-initializer\"\n\"13\",\"mission-critical\"\n\"13\",\"typescript-language-server\"\n\"13\",\"shinyjqui\"\n\"13\",\"buildingblocks\"\n\"13\",\"codea\"\n\"13\",\"rbs\"\n\"13\",\"codata\"\n\"13\",\"dom7\"\n\"13\",\"raw-post\"\n\"13\",\"ramdrive\"\n\"13\",\"kmip\"\n\"13\",\"breakiterator\"\n\"13\",\"java-communication-api\"\n\"13\",\"netbeans6.1\"\n\"13\",\"syndesis\"\n\"13\",\"authlogic-oid\"\n\"13\",\"visualdesigner\"\n\"13\",\"fortumo\"\n\"13\",\"videojs-record\"\n\"13\",\"rinohtype\"\n\"13\",\"pytextrank\"\n\"13\",\"anaglyph-3d\"\n\"13\",\"goaop\"\n\"13\",\"setuserinteractionenabled\"\n\"13\",\"jasmine-headless-webkit\"\n\"13\",\"atlasboard\"\n\"13\",\"ledger-nano-s\"\n\"13\",\"vite-plugin-development\"\n\"13\",\"right-aws\"\n\"13\",\"pystache\"\n\"13\",\"object-sharing\"\n\"13\",\"sigma-grid-control\"\n\"13\",\"viewlets\"\n\"13\",\"libcmtd\"\n\"13\",\"pyrcc\"\n\"13\",\"fppopover\"\n\"13\",\"middle-tier\"\n\"13\",\"r-dbconnect\"\n\"13\",\".x\"\n\"13\",\"atomic-values\"\n\"13\",\"buddy-build\"\n\"13\",\"settingslogic\"\n\"13\",\"newmips\"\n\"13\",\"signalrcore\"\n\"13\",\"rhino-esb\"\n\"13\",\"revision-graph\"\n\"13\",\"libreoffice-impress\"\n\"13\",\"android-app-ops\"\n\"13\",\"network-tools\"\n\"13\",\"bsmultiselect\"\n\"13\",\"midi.js\"\n\"13\",\"uiaccessibility-notification\"\n\"13\",\"magento-fpc\"\n\"13\",\".searchable\"\n\"13\",\"word-completion\"\n\"13\",\"libreoffice-draw\"\n\"13\",\"kornia\"\n\"13\",\"codeigniter-flashdata\"\n\"13\",\"winqual\"\n\"13\",\"pyhdfs-client\"\n\"13\",\"magicalrecord-2.1\"\n\"13\",\"retransmit-timeout\"\n\"13\",\"shoretel\"\n\"13\",\"wordpress-6\"\n\"13\",\"android-asset-studio\"\n\"13\",\"uglifyjs-webpack-plugin\"\n\"13\",\"knockout-postbox\"\n\"13\",\"mirrorlink\"\n\"13\",\"setneedsdisplayinrect\"\n\"13\",\"abandoned-memory\"\n\"13\",\"typed-lambda-calculus\"\n\"13\",\"vimba-sdk\"\n\"13\",\"libcurl.net\"\n\"13\",\"libical\"\n\"13\",\"domain-service-class\"\n\"13\",\"2-tier\"\n\"13\",\"a86\"\n\"13\",\"maas\"\n\"13\",\"libresolv\"\n\"13\",\"rewritepath\"\n\"13\",\"richtextctrl\"\n\"13\",\"dokku-alt\"\n\"13\",\"devilbox\"\n\"13\",\"izpanel\"\n\"13\",\"controller-tests\"\n\"13\",\"tmediaplayer\"\n\"13\",\"redo-logs\"\n\"13\",\"mobipocket\"\n\"13\",\"devirtualization\"\n\"13\",\"j2objc-gradle\"\n\"13\",\"redlock.net\"\n\"13\",\"rails-for-zombies\"\n\"13\",\"drf-extensions\"\n\"13\",\"quick-install-package\"\n\"13\",\"astah\"\n\"13\",\"play-templates\"\n\"13\",\"rails-5.1.6\"\n\"13\",\"rails-4-upgrade\"\n\"13\",\"azure-pipeline-python-script-task\"\n\"13\",\"conversions-api\"\n\"13\",\"scichart.js\"\n\"13\",\"npm-debug\"\n\"13\",\"play-ws\"\n\"13\",\"mobx-utils\"\n\"13\",\"assistive\"\n\"13\",\"nsurlcredentialstorage\"\n\"13\",\"radscheduleview\"\n\"13\",\"drupal-form-validation\"\n\"13\",\"asreml\"\n\"13\",\"convertall\"\n\"13\",\"tasty\"\n\"13\",\"taskfile\"\n\"13\",\"openjml\"\n\"13\",\"mockrunner\"\n\"13\",\"polychart\"\n\"13\",\"downloadify\"\n\"13\",\"tchecklistbox\"\n\"13\",\"polyglot-markup\"\n\"13\",\"xctestplan\"\n\"13\",\"tingodb\"\n\"13\",\"poison-queue\"\n\"13\",\"polyglot-persistance\"\n\"13\",\"hidden-variables\"\n\"13\",\"expecto\"\n\"13\",\"opengl-to-opengles\"\n\"13\",\"polylineoptions\"\n\"13\",\"vertexdata\"\n\"13\",\"drupal-schema\"\n\"13\",\"xcode-target\"\n\"13\",\"homomorphic-encryption\"\n\"13\",\"tdatetimepicker\"\n\"13\",\"azureshell\"\n\"13\",\"xcode-plugin\"\n\"13\",\"tdl\"\n\"13\",\"gemma\"\n\"13\",\"nonserializedattribute\"\n\"13\",\"registerhelper\"\n\"13\",\"tinytext\"\n\"13\",\"poc\"\n\"13\",\"as-keyword\"\n\"13\",\"non-web\"\n\"13\",\"nook-tablet\"\n\"13\",\"ex-navigation\"\n\"13\",\"honeysql\"\n\"13\",\"export-to-image\"\n\"13\",\"android-devicetoken\"\n\"13\",\"model-binders\"\n\"13\",\"notarization\"\n\"13\",\"tag-dispatching\"\n\"13\",\"hjson\"\n\"13\",\"gdata-java-client\"\n\"13\",\"tkinter.style\"\n\"13\",\"hopscotch\"\n\"13\",\"hibernate-batch-updates\"\n\"13\",\"scollector\"\n\"13\",\"hail\"\n\"13\",\"r-4.0.0\"\n\"13\",\"cabwiz\"\n\"13\",\"c#-devkit\"\n\"13\",\"racf\"\n\"13\",\"racerjs\"\n\"13\",\"dict-comprehension\"\n\"13\",\"mobile-app-tracker\"\n\"13\",\"haiku\"\n\"13\",\"c#-code-model\"\n\"13\",\"uicolorpickerviewcontroller\"\n\"13\",\"c++-attributes\"\n\"13\",\"uicontextmenuconfiguration\"\n\"13\",\"wx2\"\n\"13\",\"uibubbletableview\"\n\"13\",\"nvblas\"\n\"13\",\"sceneeditor\"\n\"13\",\"bzr-svn\"\n\"13\",\"azure-functions-docker\"\n\"13\",\"sched\"\n\"13\",\"uibaritem\"\n\"13\",\"gwt-syncproxy\"\n\"13\",\"mm7\"\n\"13\",\"cordys-opentext\"\n\"13\",\"hammerdb\"\n\"13\",\"itms-90809\"\n\"13\",\"openwebanalytics\"\n\"13\",\"xamarin.forms.collectionview\"\n\"13\",\"null-string\"\n\"13\",\"xamarin-forms-shell\"\n\"13\",\"c++-tr2\"\n\"13\",\"xaction\"\n\"13\",\"qupath\"\n\"13\",\"cache-digests\"\n\"13\",\"diem\"\n\"13\",\"dhc\"\n\"13\",\"azure-image-builder\"\n\"13\",\"x509certficiate2\"\n\"13\",\"rabbitmq-federation\"\n\"13\",\"issharedsizescope\"\n\"13\",\"sql-server-ce-toolbox\"\n\"13\",\"event-queue\"\n\"13\",\"streamingmarkupbuilder\"\n\"13\",\"layerkit\"\n\"13\",\"ole-object\"\n\"13\",\"texnic-center\"\n\"13\",\"chainlit\"\n\"13\",\"react-d3-graph\"\n\"13\",\"sphider\"\n\"13\",\"propertyconfigurator\"\n\"13\",\"react-mapbox-gl\"\n\"13\",\"getpwnam\"\n\"13\",\"httpfox\"\n\"13\",\"string-math\"\n\"13\",\"combinedresourcehandler\"\n\"13\",\"lookback\"\n\"13\",\"combine-pdf\"\n\"13\",\"resharper-8.1\"\n\"13\",\"ipworks\"\n\"13\",\"pyvimeo\"\n\"13\",\"css-paint-api\"\n\"13\",\"spgwr\"\n\"13\",\"ipython-sql\"\n\"13\",\"restivus\"\n\"13\",\"projectitem\"\n\"13\",\"qq\"\n\"13\",\"mplab-5.45\"\n\"13\",\"ios16.4\"\n\"13\",\"angularjs-infdig\"\n\"13\",\"es-hyperneat\"\n\"13\",\"cfpdfform\"\n\"13\",\"nitrous\"\n\"13\",\"large-address-aware\"\n\"13\",\"angular-gantt\"\n\"13\",\"getcolor\"\n\"13\",\"motor-asyncio\"\n\"13\",\"propfind\"\n\"13\",\"spcontext\"\n\"13\",\"logiql\"\n\"13\",\"escp\"\n\"13\",\"angularfire2-offline\"\n\"13\",\"testim.io\"\n\"13\",\"strstream\"\n\"13\",\"no-database\"\n\"13\",\"ternjs\"\n\"13\",\"lark\"\n\"13\",\"angular-bootstrap-toggle\"\n\"13\",\"android-moxy\"\n\"13\",\"android-motionscene\"\n\"13\",\"android-mqtt-client\"\n\"13\",\"memoryanalyzer\"\n\"13\",\"memoir\"\n\"13\",\"android-navigation-editor\"\n\"13\",\"cue-points\"\n\"13\",\"ondblclick\"\n\"13\",\"reactivemongo-play-json\"\n\"13\",\"euterpea\"\n\"13\",\"splunk-api\"\n\"13\",\"react-google-autocomplete\"\n\"13\",\"android-nsd\"\n\"13\",\"customization-point\"\n\"13\",\"ios-urlsheme\"\n\"13\",\"splobjectstorage\"\n\"13\",\"nike\"\n\"13\",\"android-measure\"\n\"13\",\"tomcat5\"\n\"13\",\"peer-discovery\"\n\"13\",\"memberinfo\"\n\"13\",\"monotone\"\n\"13\",\"terraform-variables\"\n\"13\",\"pgcc\"\n\"13\",\"pgbench\"\n\"13\",\"electron-notarize\"\n\"13\",\"resource-dll\"\n\"13\",\"proxy.pac\"\n\"13\",\"android-preference-v14\"\n\"13\",\"cubrid\"\n\"13\",\"android-project-template\"\n\"13\",\"resource-based-authorization\"\n\"13\",\"mems\"\n\"13\",\"pelias\"\n\"13\",\"textdocumentproxy\"\n\"13\",\"duplicati\"\n\"13\",\"moodle-boost\"\n\"13\",\"egnyte\"\n\"13\",\"pendo\"\n\"13\",\"qaxwidget\"\n\"13\",\"android-resource-qualifiers\"\n\"13\",\"monostate\"\n\"13\",\"android-jetpack-compose-navigation\"\n\"13\",\"elastix-itk\"\n\"13\",\"android-json-rpc\"\n\"13\",\"curand\"\n\"13\",\"react-dragula\"\n\"13\",\"ejabberd-saas\"\n\"13\",\"collapsibletree-r\"\n\"13\",\"ipl\"\n\"13\",\"hydrotsm\"\n\"13\",\"react-library\"\n\"13\",\"prototypal\"\n\"13\",\"odesk\"\n\"13\",\"flutter-radiobutton\"\n\"13\",\"here-tourplanning\"\n\"13\",\"linkshare\"\n\"13\",\"dart-frog\"\n\"13\",\"here-fleet-telematics\"\n\"13\",\"sunmi\"\n\"13\",\"quarkus-grpc\"\n\"13\",\"cypress-session\"\n\"13\",\"suncc\"\n\"13\",\"czmq\"\n\"13\",\"linkedin-gem\"\n\"13\",\"eluna-lua-engine\"\n\"13\",\"lightwindow\"\n\"13\",\"quantify\"\n\"13\",\"d3fo\"\n\"13\",\"parallel-assignment\"\n\"13\",\"elrte\"\n\"13\",\"webgrabber\"\n\"13\",\"dart-pdf\"\n\"13\",\"batch-delete\"\n\"13\",\"line-processing\"\n\"13\",\"darkaonlinel5-swagger\"\n\"13\",\"linux-mint-19\"\n\"13\",\"google-groups-migration\"\n\"13\",\"dapper-plus\"\n\"13\",\"header-injection\"\n\"13\",\"archive-file\"\n\"13\",\"foq\"\n\"13\",\"arcsight\"\n\"13\",\"traefik-middleware\"\n\"13\",\"ardl\"\n\"13\",\"bftask\"\n\"13\",\"hdf5dotnet\"\n\"13\",\"stx\"\n\"13\",\"timbre\"\n\"13\",\"bigdl\"\n\"13\",\"betamax\"\n\"13\",\"authorize.net-aim\"\n\"13\",\"shell-verbs\"\n\"13\",\"berkelium\"\n\"13\",\"gleam\"\n\"13\",\"uttype\"\n\"13\",\"bentoml\"\n\"13\",\"gliffy\"\n\"13\",\"glktextureloader\"\n\"13\",\"array-of-dict\"\n\"13\",\"fmle\"\n\"13\",\"seasonal-adjustment\"\n\"13\",\"qt-faststart\"\n\"13\",\"search-regex\"\n\"13\",\"bigquery-public-datasets\"\n\"13\",\"glpointsize\"\n\"13\",\"flynn\"\n\"13\",\"structured-storage\"\n\"13\",\"sdmmc\"\n\"13\",\"vaadin-push\"\n\"13\",\"autofactory\"\n\"13\",\"beeswax\"\n\"13\",\"fluxlang\"\n\"13\",\"asciimatics\"\n\"13\",\"scriptservice\"\n\"13\",\"qtpositioning\"\n\"13\",\"gnu99\"\n\"13\",\"cve-2021-44228\"\n\"13\",\"webapp-runner\"\n\"13\",\"google-place-picker\"\n\"13\",\"thttpd\"\n\"13\",\"automoc\"\n\"13\",\"parse-live-query\"\n\"13\",\"query-help\"\n\"13\",\"threat-model\"\n\"13\",\"web-control\"\n\"13\",\"substratevm\"\n\"13\",\"cypress-iframe\"\n\"13\",\"autoreload\"\n\"13\",\"max-pool-size\"\n\"13\",\"zenhub\"\n\"13\",\"mui-autocomplete\"\n\"13\",\"ms-access-2002\"\n\"13\",\"compositeusertype\"\n\"13\",\"enigma2\"\n\"13\",\"statusbaritem\"\n\"13\",\"ignore-duplicates\"\n\"13\",\"zinc\"\n\"13\",\"preferencefragmentcompat\"\n\"13\",\"zoomify\"\n\"13\",\"ember-cli-pods\"\n\"13\",\"solidworkspdmapi\"\n\"13\",\"concurrent-vector\"\n\"13\",\"maven-install\"\n\"13\",\"embed-tag\"\n\"13\",\"powercommands\"\n\"13\",\"alpha-shape\"\n\"13\",\"stellaris\"\n\"13\",\"altium-designer\"\n\"13\",\"powerpoint-automation\"\n\"13\",\"measurementformatter\"\n\"13\",\"predicatewithformat\"\n\"13\",\"media-manager\"\n\"13\",\"so-reuseport\"\n\"13\",\"mu4e\"\n\"13\",\"solana-py\"\n\"13\",\"msbuild-14.0\"\n\"13\",\"emacs-projectile\"\n\"13\",\"emailfield\"\n\"13\",\"ijavascript\"\n\"13\",\"igcombo\"\n\"13\",\"compiler-as-a-service\"\n\"13\",\"predestroy\"\n\"13\",\"emoji-tones\"\n\"13\",\"spanish\"\n\"13\",\"prelink\"\n\"13\",\"mcl\"\n\"13\",\"ilias\"\n\"13\",\"identityserver5\"\n\"12\",\"tspl\"\n\"12\",\"smart-app-banner\"\n\"12\",\"renewcommand\"\n\"12\",\"renice\"\n\"12\",\"renode\"\n\"12\",\"yappi\"\n\"12\",\"sqlworkflowpersistencese\"\n\"12\",\"decrease-key\"\n\"12\",\"gforge\"\n\"12\",\"website-monitoring\"\n\"12\",\"antplus\"\n\"12\",\"dbmetal\"\n\"12\",\"babel-6\"\n\"12\",\"flink-state\"\n\"12\",\"graphson\"\n\"12\",\"ecobee-api\"\n\"12\",\"clipbucket\"\n\"12\",\"yarn-v4\"\n\"12\",\"flightpath\"\n\"12\",\"classy-prelude\"\n\"12\",\"jd-eclipse\"\n\"12\",\"slp\"\n\"12\",\"temenos\"\n\"12\",\"squirejs\"\n\"12\",\"weyland\"\n\"12\",\"jcurses\"\n\"12\",\"photosphereviewer\"\n\"12\",\"tensorflow-layers\"\n\"12\",\"ebooklib\"\n\"12\",\"transitional\"\n\"12\",\"livewire-powergrid\"\n\"12\",\"deadobjectexception\"\n\"12\",\"flutter-focus-node\"\n\"12\",\"re-python\"\n\"12\",\"ddx\"\n\"12\",\"trouble-tickets\"\n\"12\",\"websharper.ui.next\"\n\"12\",\"loadoptions\"\n\"12\",\"eclipseme\"\n\"12\",\"edgesdk\"\n\"12\",\"webtask\"\n\"12\",\"procrustes\"\n\"12\",\"template-lite\"\n\"12\",\"ant4eclipse\"\n\"12\",\"trusted-signing\"\n\"12\",\"template-control\"\n\"12\",\"print-job-control\"\n\"12\",\"prodigy\"\n\"12\",\"deferred-deep-linking\"\n\"12\",\"ggradar\"\n\"12\",\"trusted-types\"\n\"12\",\"fc\"\n\"12\",\"intel-pytorch\"\n\"12\",\"eclipse-digital-twin\"\n\"12\",\"wicket-1.4\"\n\"12\",\"localserversocket\"\n\"12\",\"fluentdata\"\n\"12\",\"fcitx\"\n\"12\",\"stagevideo\"\n\"12\",\"dcat\"\n\"12\",\"primeng-menu\"\n\"12\",\"remote-containers\"\n\"12\",\"eclipse-vorto\"\n\"12\",\"tsclust\"\n\"12\",\"intellilock\"\n\"12\",\"remove.bg\"\n\"12\",\"regula\"\n\"12\",\"rehype\"\n\"12\",\"rendermonkey\"\n\"12\",\"graphql-compose\"\n\"12\",\"edwin\"\n\"12\",\"git-ls-tree\"\n\"12\",\"gitlist\"\n\"12\",\"sqlsoup\"\n\"12\",\"jenkins-template-engine\"\n\"12\",\"jenkins-spock\"\n\"12\",\"trixbox\"\n\"12\",\"terminal.gui\"\n\"12\",\"webpack-watch\"\n\"12\",\"yeti\"\n\"12\",\"php-glide\"\n\"12\",\"backbone-paginator\"\n\"12\",\"backbase-portal\"\n\"12\",\"closesocket\"\n\"12\",\"grounddb\"\n\"12\",\"backupexec\"\n\"12\",\"apache-bloodhound\"\n\"12\",\"vue-sweetalert2\"\n\"12\",\"apache-ftpserver\"\n\"12\",\"clouddb\"\n\"12\",\"file-header\"\n\"12\",\"phplib\"\n\"12\",\"clj-kafka\"\n\"12\",\"gridlookupedit\"\n\"12\",\"react-native-deck-swiper\"\n\"12\",\"apache-commons-text\"\n\"12\",\"react-refresh\"\n\"12\",\"sketch.js\"\n\"12\",\"marqo\"\n\"12\",\"skb\"\n\"12\",\"groovyc\"\n\"12\",\"apache-commons-imaging\"\n\"12\",\"react-redux-connect\"\n\"12\",\"easy-rules\"\n\"12\",\"php-ffi\"\n\"12\",\"vue-pwa\"\n\"12\",\"banshee\"\n\"12\",\"barista\"\n\"12\",\"mathdotnet-symbolics\"\n\"12\",\"filedrop.js\"\n\"12\",\"function-template\"\n\"12\",\"selectionchanging\"\n\"12\",\"fundamentals-ts\"\n\"12\",\"snakeviz\"\n\"12\",\"pingaccess\"\n\"12\",\"fitch-proofs\"\n\"12\",\"mapbox-navigation\"\n\"12\",\"python-ipaddress\"\n\"12\",\"funnelweb\"\n\"12\",\"fxgl\"\n\"12\",\"unbuffered-output\"\n\"12\",\"django-angular\"\n\"12\",\"python-dataset\"\n\"12\",\"fxaa\"\n\"12\",\"flawfinder\"\n\"12\",\"package-design\"\n\"12\",\"pabx\"\n\"12\",\"pinnacle-cart\"\n\"12\",\"paapi\"\n\"12\",\"safari9\"\n\"12\",\"safari7\"\n\"12\",\"flatpack\"\n\"12\",\"bit-framework\"\n\"12\",\"saaskit\"\n\"12\",\"biweekly\"\n\"12\",\"s6\"\n\"12\",\"caxlsx\"\n\"12\",\"flask-table\"\n\"12\",\"semmle-ql\"\n\"12\",\"bjqs\"\n\"12\",\"manifoldjs\"\n\"12\",\"packr\"\n\"12\",\"biom\"\n\"12\",\"cbt\"\n\"12\",\"paf\"\n\"12\",\"pinging\"\n\"12\",\"xmake\"\n\"12\",\"laravel-charts\"\n\"12\",\"flashplayer-debug\"\n\"12\",\"pinyin\"\n\"12\",\"socialsharing-plugin\"\n\"12\",\"make-scorer\"\n\"12\",\"img-area-select-jquery\"\n\"12\",\"lapack++\"\n\"12\",\"smoothstep\"\n\"12\",\"selmer\"\n\"12\",\"binding-mode\"\n\"12\",\"many2one\"\n\"12\",\"imperative-languages\"\n\"12\",\"imperva\"\n\"12\",\"snorkel\"\n\"12\",\"lambdatest\"\n\"12\",\"ngx-uploader\"\n\"12\",\"import-hooks\"\n\"12\",\"uiveri5\"\n\"12\",\"laika\"\n\"12\",\"la-clojure\"\n\"12\",\"labelme\"\n\"12\",\"nhibernate-burrow\"\n\"12\",\"ujmp\"\n\"12\",\"unitofworkapplication\"\n\"12\",\"case-tools\"\n\"12\",\"ng2-admin\"\n\"12\",\"fixedpage\"\n\"12\",\"pairing-heap\"\n\"12\",\"kubeless\"\n\"12\",\"vshost32\"\n\"12\",\"python-billiard\"\n\"12\",\"fitted-box\"\n\"12\",\"fyber\"\n\"12\",\"advertised-shortcut\"\n\"12\",\"json-ref\"\n\"12\",\"xps-generation\"\n\"12\",\"smarthost\"\n\"12\",\"consul-health-check\"\n\"12\",\"jsfuck\"\n\"12\",\"symfony-http-kernel\"\n\"12\",\"symfony-bundle\"\n\"12\",\"symfony-assetmapper\"\n\"12\",\"adtf3\"\n\"12\",\"funcall\"\n\"12\",\"appcompatdialogfragment\"\n\"12\",\"aem-core-wcm-components\"\n\"12\",\"chrome-plugins\"\n\"12\",\"swtchart\"\n\"12\",\"apache-pig-grunt\"\n\"12\",\"circuits-framework\"\n\"12\",\"apache-tuscany\"\n\"12\",\"python-slate\"\n\"12\",\"cimbalino\"\n\"12\",\"data-science-studio\"\n\"12\",\"dbclient\"\n\"12\",\"firewall-rules\"\n\"12\",\"connect-modrewrite\"\n\"12\",\"adobe-exprience-manager\"\n\"12\",\"datocms\"\n\"12\",\"apache-xml-graphics\"\n\"12\",\"app2sd\"\n\"12\",\"connection-points\"\n\"12\",\"date-sunrise\"\n\"12\",\"apache-zest\"\n\"12\",\"jtc\"\n\"12\",\"chatjs\"\n\"12\",\"json2xls\"\n\"12\",\"jsctags\"\n\"12\",\"dbextensions\"\n\"12\",\"datastore-admin\"\n\"12\",\"cirrious.fluentlayout\"\n\"12\",\"aphrodite\"\n\"12\",\"jsr380\"\n\"12\",\"apfloat\"\n\"12\",\"pkg\"\n\"12\",\"dbd-mysql\"\n\"12\",\"jmxmp\"\n\"12\",\"fbloginview\"\n\"12\",\"mysql-error-1264\"\n\"12\",\"grails-3.0.10\"\n\"12\",\"jmspaymentpaypalbundle\"\n\"12\",\"grails-plugin-rabbitmq\"\n\"12\",\"mysql-error-1222\"\n\"12\",\"callfire\"\n\"12\",\"gosublime\"\n\"12\",\"grammar-induction\"\n\"12\",\"gotenberg\"\n\"12\",\"data-compaction\"\n\"12\",\"jinitiator\"\n\"12\",\"meteor-jasmine\"\n\"12\",\"fastly-vcl\"\n\"12\",\"ruby-dotenv\"\n\"12\",\"mysql-error-1130\"\n\"12\",\"ibm-oneui\"\n\"12\",\"camunda-spin\"\n\"12\",\"windows-application-driver\"\n\"12\",\"naked-objects\"\n\"12\",\"cakephp-ajaxhelper\"\n\"12\",\"rti-dds\"\n\"12\",\"window.external\"\n\"12\",\"windicss\"\n\"12\",\"vecmath\"\n\"12\",\"angular-sanitizer\"\n\"12\",\"vectorbt\"\n\"12\",\"microsoft-azure-documentdb\"\n\"12\",\"algol68\"\n\"12\",\"nanoboxio\"\n\"12\",\"iccid\"\n\"12\",\"microsoft-entra-external-id\"\n\"12\",\"rstudioapi\"\n\"12\",\"microsoft-graph-contacts\"\n\"12\",\"pycom\"\n\"12\",\"nativedroid\"\n\"12\",\"recursivetask\"\n\"12\",\"icontact\"\n\"12\",\"capture-output\"\n\"12\",\"verp\"\n\"12\",\"facilities\"\n\"12\",\"rsm\"\n\"12\",\"public-suffix-list\"\n\"12\",\"idangero\"\n\"12\",\"receive-location\"\n\"12\",\"punbb\"\n\"12\",\"alation\"\n\"12\",\"real-time-strategy\"\n\"12\",\"alassetsgroup\"\n\"12\",\"realplayer\"\n\"12\",\"facebooksdk.net\"\n\"12\",\"purescript-halogen\"\n\"12\",\"purescript-pux\"\n\"12\",\"rsbarcodes\"\n\"12\",\"pushapps\"\n\"12\",\"service-bus-explorer\"\n\"12\",\"universal-code\"\n\"12\",\"sitecore-lucene\"\n\"12\",\"crystal-reports-10\"\n\"12\",\"update-inner-join\"\n\"12\",\"urlaccess\"\n\"12\",\"rootpy\"\n\"12\",\"docker-registry-mirror\"\n\"12\",\"oracle-access-manager\"\n\"12\",\"aws-ebs-csi-driver\"\n\"12\",\"watson-personality-insights\"\n\"12\",\"simple-phpunit\"\n\"12\",\"simpowersystems\"\n\"12\",\"dkan\"\n\"12\",\"simplepager\"\n\"12\",\"updatecheck\"\n\"12\",\"kismet-wireless\"\n\"12\",\"rosetta-code\"\n\"12\",\"django-inline-models\"\n\"12\",\"django-intermediate-table\"\n\"12\",\"dns-get-record\"\n\"12\",\"oracle-http-server\"\n\"12\",\"untyped-variables\"\n\"12\",\"django-johnny-cache\"\n\"12\",\"hyperledger-fabric2.2\"\n\"12\",\"doctrine-orm-postgres\"\n\"12\",\"ora-06553\"\n\"12\",\"routedeventargs\"\n\"12\",\"do178-b\"\n\"12\",\"unleash\"\n\"12\",\"sharepoint2010-bcs\"\n\"12\",\"watchr\"\n\"12\",\"walrus\"\n\"12\",\"aws-mediatailor\"\n\"12\",\"ora-03113\"\n\"12\",\"keywordquery\"\n\"12\",\"django-rss\"\n\"12\",\"unnamed-class\"\n\"12\",\"ias\"\n\"12\",\"aws-copilot-cli\"\n\"12\",\"rl78\"\n\"12\",\"watchos-10\"\n\"12\",\"wago\"\n\"12\",\"keyboard-avoidance\"\n\"12\",\"roslyn-project-system\"\n\"12\",\"django-pandas\"\n\"12\",\"warp-scheduler\"\n\"12\",\"doctrine-mapping\"\n\"12\",\"django-recaptcha\"\n\"12\",\"worklight-cli\"\n\"12\",\"horn\"\n\"12\",\"nspoint\"\n\"12\",\"turbolinks-ios\"\n\"12\",\"justinmind\"\n\"12\",\"nested-statement\"\n\"12\",\"kameleo\"\n\"12\",\"kakao\"\n\"12\",\"bootp\"\n\"12\",\"sap-business-bydesign\"\n\"12\",\"wso2-asgardeo\"\n\"12\",\"jrecorder\"\n\"12\",\"gulpfile\"\n\"12\",\"gs1-databar\"\n\"12\",\"mojo-dom\"\n\"12\",\"nsaffinetransform\"\n\"12\",\"swfmill\"\n\"12\",\"apple-ii\"\n\"12\",\"azul-zing\"\n\"12\",\"countdownjs.js\"\n\"12\",\"ionic.io\"\n\"12\",\"turing-lang\"\n\"12\",\"online-forms\"\n\"12\",\"wtforms-json\"\n\"12\",\"apple-speech\"\n\"12\",\"hostinger\"\n\"12\",\"ncurses-cdk\"\n\"12\",\"apple-clang\"\n\"12\",\"ion-infinite-scroll\"\n\"12\",\"border-collapse\"\n\"12\",\"blazor-jsruntime\"\n\"12\",\"ttlauncheritem\"\n\"12\",\"initwithstyle\"\n\"12\",\"silk-central\"\n\"12\",\"neo4j-cql\"\n\"12\",\"spring-expression\"\n\"12\",\"saslidemenu\"\n\"12\",\"degenerate-dimension\"\n\"12\",\"grunt-svgstore\"\n\"12\",\"boost-fiber\"\n\"12\",\"grunt-ssh\"\n\"12\",\"silverlight-2-rc0\"\n\"12\",\"silverlightcontrols\"\n\"12\",\"arangodb-java\"\n\"12\",\"swiftgen\"\n\"12\",\"invantive-data-hub\"\n\"12\",\"grunt-eslint\"\n\"12\",\"intersystems-cache-studio\"\n\"12\",\"html5-qrcode\"\n\"12\",\"dynamics-nav-2015\"\n\"12\",\"createitem\"\n\"12\",\"springockito\"\n\"12\",\"blazy\"\n\"12\",\"html5-img\"\n\"12\",\"botframeworkemulator\"\n\"12\",\"sc\"\n\"12\",\"bosh\"\n\"12\",\"blazor-routing\"\n\"12\",\"modelmultiplechoicefield\"\n\"12\",\"write-through\"\n\"12\",\"nbug\"\n\"12\",\"axvline\"\n\"12\",\"httpapplicationstate\"\n\"12\",\"paw\"\n\"12\",\"grunt-angular-gettext\"\n\"12\",\"jquery-infinite-scroll\"\n\"12\",\"dynamically-loaded-xap\"\n\"12\",\"twitter-recess\"\n\"12\",\"dylan\"\n\"12\",\"open-array-parameters\"\n\"12\",\"openfb\"\n\"12\",\"application-size\"\n\"12\",\"appsee\"\n\"12\",\"modelvisual3d\"\n\"12\",\"openflashchart2\"\n\"12\",\"inline-if\"\n\"12\",\"wsacleanup\"\n\"12\",\"postman-flows\"\n\"12\",\"html-to-docx\"\n\"12\",\"navparams\"\n\"12\",\"pbrt\"\n\"12\",\"gtmsessionfetcher\"\n\"12\",\"k-combinator\"\n\"12\",\"easendmail\"\n\"12\",\"interfax\"\n\"12\",\"corewars\"\n\"12\",\"pcapy\"\n\"12\",\"wpf-interop\"\n\"12\",\"eastl\"\n\"12\",\"inlineeditbox\"\n\"12\",\"nestacms\"\n\"12\",\"swallowed-exceptions\"\n\"12\",\"dxcore\"\n\"12\",\"needleman-wunsch\"\n\"12\",\"mongo-driver\"\n\"12\",\"design-consideration\"\n\"12\",\"application-integration\"\n\"12\",\"mongodb-tools\"\n\"12\",\"enumdropdownlistfor\"\n\"12\",\"easyquery\"\n\"12\",\"kanso\"\n\"12\",\"inlay-hints\"\n\"12\",\"dynamic-data-exchange\"\n\"12\",\"axhost\"\n\"12\",\"applicationid\"\n\"12\",\"pd4ml\"\n\"12\",\"depottools\"\n\"12\",\"guidance-automation-tool\"\n\"12\",\"inpainting\"\n\"12\",\"deps-edn\"\n\"12\",\"onserviceconnected\"\n\"12\",\"samsung-galaxy-camera\"\n\"12\",\"karafka\"\n\"12\",\"jquery-ui-slider-pips\"\n\"12\",\"cots\"\n\"12\",\"lzf\"\n\"12\",\"neurolab\"\n\"12\",\"newsgroup\"\n\"12\",\"vlad-vector\"\n\"12\",\"mixed-case\"\n\"12\",\"osvr\"\n\"12\",\"videojs-transcript\"\n\"12\",\"oauth-2.1\"\n\"12\",\"minimumosversion\"\n\"12\",\"rbtools\"\n\"12\",\"wix2\"\n\"12\",\"javaexe\"\n\"12\",\"gomobile-android\"\n\"12\",\"osx-extensions\"\n\"12\",\"viewroot\"\n\"12\",\"mac-classic\"\n\"12\",\"abstract-machine\"\n\"12\",\"java-flow\"\n\"12\",\"javaimports\"\n\"12\",\"pyscreeze\"\n\"12\",\"typoscript2\"\n\"12\",\"machine.fakes\"\n\"12\",\"otcl\"\n\"12\",\"coco2d-x\"\n\"12\",\"facebook-browser\"\n\"12\",\"librabbitmq\"\n\"12\",\"abaqus-odb\"\n\"12\",\"oas3\"\n\"12\",\"raycast\"\n\"12\",\"android-app-quick-setting\"\n\"12\",\"vim-syntastic\"\n\"12\",\"javafx-datepicker\"\n\"12\",\"ext.list\"\n\"12\",\"frapi\"\n\"12\",\"view-debugging\"\n\"12\",\"audio-panning\"\n\"12\",\"nxt-python\"\n\"12\",\"javafx-gradle-plugin\"\n\"12\",\"android-banner\"\n\"12\",\"wizard-control\"\n\"12\",\"rbar\"\n\"12\",\"raspivid\"\n\"12\",\"atan\"\n\"12\",\"goldmine\"\n\"12\",\"audio-comparison\"\n\"12\",\"winwrap\"\n\"12\",\"pygsl\"\n\"12\",\"browserslist\"\n\"12\",\"ora-hash\"\n\"12\",\"amd-app\"\n\"12\",\"gocb\"\n\"12\",\"orafce\"\n\"12\",\"pyrender\"\n\"12\",\"netbeans-14\"\n\"12\",\"codepage-437\"\n\"12\",\"knockout-subscribe\"\n\"12\",\"ezpdf\"\n\"12\",\"ravenhq\"\n\"12\",\"pypi-regex\"\n\"12\",\"codesite\"\n\"12\",\"miniz\"\n\"12\",\"windows-virtual-pc\"\n\"12\",\"knowm-xchart\"\n\"12\",\"kolite\"\n\"12\",\"microsoft-graph-webhooks\"\n\"12\",\"pyportmidi\"\n\"12\",\"lexical-editor\"\n\"12\",\"koken\"\n\"12\",\"codeworld\"\n\"12\",\"network-conduit\"\n\"12\",\"shadow-removal\"\n\"12\",\"dominotogo\"\n\"12\",\"codeguard\"\n\"12\",\"system-databases\"\n\"12\",\"shady\"\n\"12\",\"range-partitions\"\n\"12\",\"objectgears\"\n\"12\",\"atlassprites\"\n\"12\",\"libmongoc\"\n\"12\",\"kotlin-context-receivers\"\n\"12\",\"code-elimination\"\n\"12\",\"libnds\"\n\"12\",\"reverseprojection\"\n\"12\",\"wkrefreshbackgroundtask\"\n\"12\",\"kobotoolbox\"\n\"12\",\"freeling\"\n\"12\",\"kover\"\n\"12\",\"system-rules\"\n\"12\",\"facebook-infer\"\n\"12\",\"klvdata\"\n\"12\",\"typescript-3.6\"\n\"12\",\"mirador\"\n\"12\",\"wmd-markdown\"\n\"12\",\"syncthing\"\n\"12\",\"pyimagej\"\n\"12\",\"sfu\"\n\"12\",\"jasync-sql\"\n\"12\",\"cofoundry\"\n\"12\",\"riak-js\"\n\"12\",\"kohana-3.0\"\n\"12\",\"windows-shortcut\"\n\"12\",\".net-mac\"\n\"12\",\"ratecard-api\"\n\"12\",\"lxr\"\n\"12\",\"amplify-ios\"\n\"12\",\"luaplus\"\n\"12\",\"raml-java-parser\"\n\"12\",\"typeliteral\"\n\"12\",\"shutterstock\"\n\"12\",\"javasymbolsolver\"\n\"12\",\"goblin\"\n\"12\",\"shutdown-script\"\n\"12\",\"spu\"\n\"12\",\"pluggableprotocol\"\n\"12\",\"refreshable\"\n\"12\",\"azure-ml-component\"\n\"12\",\"jalopy\"\n\"12\",\"plugin-pattern\"\n\"12\",\"gdr\"\n\"12\",\"asp.net-mvc-awesome\"\n\"12\",\"tagfield\"\n\"12\",\"asp.net-mvc-custom-filter\"\n\"12\",\"itemrenderers\"\n\"12\",\"asmock\"\n\"12\",\"ds9\"\n\"12\",\"notification-content-extension\"\n\"12\",\"difference-between-rows\"\n\"12\",\"highspeed\"\n\"12\",\"png-transparency\"\n\"12\",\"coproduct\"\n\"12\",\"hlsl2glsl\"\n\"12\",\"expo-publish\"\n\"12\",\"asp.net-mvc-uihint\"\n\"12\",\"refinitiv-eikon\"\n\"12\",\"notifyitemchanged\"\n\"12\",\"mockingoose\"\n\"12\",\"copssh\"\n\"12\",\"r2dbc-mssql\"\n\"12\",\"vetiver\"\n\"12\",\"vestacp\"\n\"12\",\"tamagui\"\n\"12\",\"r2d2\"\n\"12\",\"double-double-arithmetic\"\n\"12\",\"tds-fdw\"\n\"12\",\"tangram\"\n\"12\",\"h2-console\"\n\"12\",\"dragenter\"\n\"12\",\"homomorphism\"\n\"12\",\"podman-networking\"\n\"12\",\"mobify\"\n\"12\",\"open-packaging-convention\"\n\"12\",\"dinamico\"\n\"12\",\"nodevm\"\n\"12\",\"gwt-test-utils\"\n\"12\",\"android-compose-exposeddropdown\"\n\"12\",\"tcpchannel\"\n\"12\",\"coq-plugin\"\n\"12\",\"node-xbee\"\n\"12\",\"tinn-r\"\n\"12\",\"polyline-decorator\"\n\"12\",\"nodist\"\n\"12\",\"pointplot\"\n\"12\",\"istool\"\n\"12\",\"gwt-dispatch\"\n\"12\",\"targetprocess\"\n\"12\",\"mms-gateway\"\n\"12\",\"openid-dex\"\n\"12\",\"openindiana\"\n\"12\",\"gwt-2.2\"\n\"12\",\"gvariant\"\n\"12\",\"nokia-n8\"\n\"12\",\"double-splat\"\n\"12\",\"openjscad\"\n\"12\",\"bwidget\"\n\"12\",\"buzztouch\"\n\"12\",\"rails-event-store\"\n\"12\",\"devise-async\"\n\"12\",\"bwip-js\"\n\"12\",\"ganon\"\n\"12\",\"red-gate-sql-prompt\"\n\"12\",\"scintillanet\"\n\"12\",\"sqlfire\"\n\"12\",\"rails-bullet\"\n\"12\",\"rails-roar\"\n\"12\",\"uiactivitycontroller\"\n\"12\",\"caching-application-block\"\n\"12\",\"reddison\"\n\"12\",\"redux-async-actions\"\n\"12\",\"npn\"\n\"12\",\"opera-presto\"\n\"12\",\"opensoundcontrol\"\n\"12\",\"redux-immutable\"\n\"12\",\"redis-om-spring\"\n\"12\",\"playscalajs\"\n\"12\",\"redis-om\"\n\"12\",\"control-adapter\"\n\"12\",\"npm-outdated\"\n\"12\",\"rackunit\"\n\"12\",\"gcc-4.2\"\n\"12\",\"tlbinf32\"\n\"12\",\"mkoverlaypathrenderer\"\n\"12\",\"scct\"\n\"12\",\"xcode12.3\"\n\"12\",\"expression-sketchflow\"\n\"12\",\"re-encoding\"\n\"12\",\"garnet-os\"\n\"12\",\"bzlmod\"\n\"12\",\"sqlite-cipher\"\n\"12\",\"dotmailer\"\n\"12\",\"assembly-trap\"\n\"12\",\"mobile-controls\"\n\"12\",\"nvidia-titan\"\n\"12\",\"handset\"\n\"12\",\"androidinjector\"\n\"12\",\"mixed-type\"\n\"12\",\"sqlalchemy-continuum\"\n\"12\",\"xcode13.3.1\"\n\"12\",\"convertigo\"\n\"12\",\"xcode12beta6\"\n\"12\",\"uisheetpresentationcontroller\"\n\"12\",\"handlerinterceptor\"\n\"12\",\"menuitem-selection\"\n\"12\",\"prometheus-adapter\"\n\"12\",\"omml\"\n\"12\",\"omemo\"\n\"12\",\"huawei-cloud\"\n\"12\",\"ofed\"\n\"12\",\"perlguts\"\n\"12\",\"spectre.console\"\n\"12\",\"esb-toolkit-2.1\"\n\"12\",\"on-behalf-of\"\n\"12\",\"onbeforeload\"\n\"12\",\"spoken-language\"\n\"12\",\"proxygen\"\n\"12\",\"huggingface-evaluate\"\n\"12\",\"centos7.6\"\n\"12\",\"charm++\"\n\"12\",\"qqmlcontext\"\n\"12\",\"offline-web-app\"\n\"12\",\"spgroup\"\n\"12\",\"pyupdater\"\n\"12\",\"qgadget\"\n\"12\",\"sphere.io\"\n\"12\",\"charmap\"\n\"12\",\"mergecursor\"\n\"12\",\"luadoc\"\n\"12\",\"qnx-ifs\"\n\"12\",\"lua-4.0\"\n\"12\",\"qif\"\n\"12\",\"meshroom\"\n\"12\",\"eslintignore\"\n\"12\",\"esp-idf-sys\"\n\"12\",\"pessimistic\"\n\"12\",\"lpad\"\n\"12\",\"mpkg\"\n\"12\",\"pywhatkit\"\n\"12\",\"pfbc\"\n\"12\",\"spiffe\"\n\"12\",\"projectlocker\"\n\"12\",\"spinnaker-cam\"\n\"12\",\"nhibernate-hql\"\n\"12\",\"irvine16\"\n\"12\",\"resharper-9.1\"\n\"12\",\"elaborated-type-specifier\"\n\"12\",\"restrserve\"\n\"12\",\"iron-elements\"\n\"12\",\"elastalert2\"\n\"12\",\"restore-points\"\n\"12\",\"google-compute-disk\"\n\"12\",\"elastic-enterprise-search\"\n\"12\",\"cometserver\"\n\"12\",\"perceptual-sdk\"\n\"12\",\"restkit-0.24.x\"\n\"12\",\"elasticnet\"\n\"12\",\"ips\"\n\"12\",\"lava\"\n\"12\",\"ace-tao\"\n\"12\",\"ipropertystorage\"\n\"12\",\"elasticsearch-1.6.0\"\n\"12\",\"restclientbuilder\"\n\"12\",\"react-location\"\n\"12\",\"google-cloud-save\"\n\"12\",\"google-cloud-run-jobs\"\n\"12\",\"nodechildren\"\n\"12\",\"color-gradient\"\n\"12\",\"iosched\"\n\"12\",\"colemak\"\n\"12\",\"android-reflection\"\n\"12\",\"google-cloud-interconnect\"\n\"12\",\"elasticsearch-phonetic\"\n\"12\",\"resolvejs\"\n\"12\",\"cube-script\"\n\"12\",\"elisp-macro\"\n\"12\",\"android-pullparser\"\n\"12\",\"node-ftp\"\n\"12\",\"actionscript-1\"\n\"12\",\"node-kafka-streams\"\n\"12\",\"active-attr\"\n\"12\",\"ipados13\"\n\"12\",\"acts-as-state-machine\"\n\"12\",\"ios-sqlite\"\n\"12\",\"respondcms\"\n\"12\",\"ip2long\"\n\"12\",\"customize-cra\"\n\"12\",\"perl-core\"\n\"12\",\"periodictimer\"\n\"12\",\"geom-map\"\n\"12\",\"getforegroundwindow\"\n\"12\",\"niagara-4\"\n\"12\",\"angular2-docheck\"\n\"12\",\"testswarm\"\n\"12\",\"httptransportse\"\n\"12\",\"laravel-unit-test\"\n\"12\",\"node-pg-migrate\"\n\"12\",\"generative-testing\"\n\"12\",\"getseq\"\n\"12\",\"testdoublejs\"\n\"12\",\"react-codemirror2\"\n\"12\",\"genericprincipal\"\n\"12\",\"genymotion-gps\"\n\"12\",\"test-and-target\"\n\"12\",\"testtrack\"\n\"12\",\"gervill\"\n\"12\",\"to-yaml\"\n\"12\",\"folksonomy\"\n\"12\",\"embedded-container\"\n\"12\",\"tiger\"\n\"12\",\"uudecode\"\n\"12\",\"compose-multiplatform-ios\"\n\"12\",\"shgo\"\n\"12\",\"qsignalspy\"\n\"12\",\"fog-google\"\n\"12\",\"autoblogged\"\n\"12\",\"uwf\"\n\"12\",\"ms-office-addin\"\n\"12\",\"static-if\"\n\"12\",\"cylon.js\"\n\"12\",\"zend-xmlrpc\"\n\"12\",\"allow-modals\"\n\"12\",\"global-query-filter\"\n\"12\",\"secure-trading\"\n\"12\",\"compound-operator\"\n\"12\",\"struts2-config-browser\"\n\"12\",\"tibero\"\n\"12\",\"em-websocket-client\"\n\"12\",\"belief-propagation\"\n\"12\",\"totara\"\n\"12\",\"ytt\"\n\"12\",\"all-in-one-event-calendar\"\n\"12\",\"hec-ras\"\n\"12\",\"google-notebook\"\n\"12\",\"yui-editor\"\n\"12\",\"google-site-verification-api\"\n\"12\",\"tortoisegitmerge\"\n\"12\",\"webchannelfactory\"\n\"12\",\"maven-multi-module\"\n\"12\",\"linux-mint-21\"\n\"12\",\"helix-editor\"\n\"12\",\"amazon-imagebuilder\"\n\"12\",\"preinit\"\n\"12\",\"glutcreatewindow\"\n\"12\",\"git-switch\"\n\"12\",\"best-first-search\"\n\"12\",\"haxepunk\"\n\"12\",\"msmqbinding\"\n\"12\",\"trait-bounds\"\n\"12\",\"compilationmode\"\n\"12\",\"steemit\"\n\"12\",\"hazelcast-cloud\"\n\"12\",\"embedio\"\n\"12\",\"gitsharp\"\n\"12\",\"mbsync\"\n\"12\",\"power-series\"\n\"12\",\"ie-plugins\"\n\"12\",\"iextenderprovider\"\n\"12\",\"compcert\"\n\"12\",\"embedded-script\"\n\"12\",\"std-invoke\"\n\"12\",\"usedapp\"\n\"12\",\"gkscore\"\n\"12\",\"user-testing\"\n\"12\",\"stylet\"\n\"12\",\"cypress-psoc\"\n\"12\",\"usda-fooddata-central-api\"\n\"12\",\"embedded-postgres\"\n\"12\",\"use-swr\"\n\"12\",\"architect\"\n\"12\",\"bfile\"\n\"12\",\"d3-geo\"\n\"12\",\"hdf5storage\"\n\"12\",\"parlai\"\n\"12\",\"std-call-once\"\n\"12\",\"sua\"\n\"12\",\"solr8.4.1\"\n\"12\",\"usage-tracking\"\n\"12\",\"d3-org-chart\"\n\"12\",\"argument-validation\"\n\"12\",\"bhm\"\n\"12\",\"bcompiler\"\n\"12\",\"statusnet\"\n\"12\",\"glfrustum\"\n\"12\",\"urlspan\"\n\"12\",\"daap\"\n\"12\",\"cypress-code-coverage\"\n\"12\",\"mcdm\"\n\"12\",\"stryker-net\"\n\"12\",\"font-replacement\"\n\"12\",\"sharpvectors\"\n\"12\",\"url-modification\"\n\"12\",\"dacapo\"\n\"12\",\"arm-linux\"\n\"12\",\"sheetrock\"\n\"12\",\"subdomain-fu\"\n\"12\",\"url-link\"\n\"12\",\"conda-pack\"\n\"12\",\"bayesglm\"\n\"12\",\"starcraftgym\"\n\"12\",\"webextension-storage\"\n\"12\",\"zammad\"\n\"12\",\"linq-to-ldap\"\n\"12\",\"tfs-2008\"\n\"12\",\"webloadui\"\n\"12\",\"zappdev\"\n\"12\",\"source-filter\"\n\"12\",\"passive-sts\"\n\"12\",\"linqbridge\"\n\"12\",\"allatori\"\n\"12\",\"weblistener\"\n\"12\",\"hetzner-cloud\"\n\"12\",\"arworldmap\"\n\"12\",\"starrocks\"\n\"12\",\"webodf\"\n\"12\",\"qtserial\"\n\"12\",\"preg-quote\"\n\"12\",\"mdspan\"\n\"12\",\"gnosis-safe\"\n\"12\",\"seaglass\"\n\"12\",\"allauth\"\n\"12\",\"spark2\"\n\"12\",\"mdw\"\n\"12\",\"help-files\"\n\"12\",\"multibox\"\n\"12\",\"zcash\"\n\"12\",\"google-sheets-filter-view\"\n\"12\",\"gnu-indent\"\n\"12\",\"mtasc\"\n\"12\",\"queued-connection\"\n\"12\",\"precompute\"\n\"12\",\"gmock\"\n\"12\",\"glyph-substitution\"\n\"12\",\"mediawiki-visualeditor\"\n\"12\",\"conceptnet\"\n\"12\",\"pptk\"\n\"12\",\"beautytips\"\n\"12\",\"quarkus-testing\"\n\"12\",\"mdls\"\n\"12\",\"webgrind\"\n\"12\",\"sourcery\"\n\"12\",\"libyang\"\n\"11\",\"primeng-checkbox\"\n\"11\",\"multitouch-keyboard\"\n\"11\",\"bash-it\"\n\"11\",\"cmfcmenubutton\"\n\"11\",\"sitespeedio\"\n\"11\",\"vticker\"\n\"11\",\"graphql-compose-mongoose\"\n\"11\",\"relstorage\"\n\"11\",\"pgsync\"\n\"11\",\"marmalade-edk\"\n\"11\",\"filecoin\"\n\"11\",\"mark-of-the-web\"\n\"11\",\"react-native-macos\"\n\"11\",\"yii-behaviour\"\n\"11\",\"clientside-caching\"\n\"11\",\"procstat\"\n\"11\",\"slickquiz\"\n\"11\",\"jenkins-php\"\n\"11\",\"balanced-groups\"\n\"11\",\"stalestateexception\"\n\"11\",\"intel-xdk-contacts\"\n\"11\",\"previewcallback\"\n\"11\",\"marklogic-11\"\n\"11\",\"clientresource\"\n\"11\",\"webtransport\"\n\"11\",\"productsign\"\n\"11\",\"wickedpicker\"\n\"11\",\"remoteserviceexception\"\n\"11\",\"php-generators\"\n\"11\",\"trtc.io\"\n\"11\",\"jaxb2-simplify-plugin\"\n\"11\",\"clustermap\"\n\"11\",\"mass-package\"\n\"11\",\"multiple-interface-implem\"\n\"11\",\"tronlink\"\n\"11\",\"apache-doris\"\n\"11\",\"lmtp\"\n\"11\",\"masstransit-courier\"\n\"11\",\"lobo-cobra\"\n\"11\",\"master-db\"\n\"11\",\"jaxb-episode\"\n\"11\",\"teamcity-rest-api\"\n\"11\",\"trusted-application\"\n\"11\",\"react-native-table-component\"\n\"11\",\"xsp2\"\n\"11\",\"masquerade\"\n\"11\",\"citymaps\"\n\"11\",\"masterslider\"\n\"11\",\"ansible-automation-platform\"\n\"11\",\"ecmascript-4\"\n\"11\",\"eclipse-europa\"\n\"11\",\"filemap\"\n\"11\",\"anypoint-mq\"\n\"11\",\"fbsnapshottestcase\"\n\"11\",\"jet.com-apis\"\n\"11\",\"ecobertura\"\n\"11\",\"localizedstringkey\"\n\"11\",\"fluent-mongo\"\n\"11\",\"badgerdb\"\n\"11\",\"cmake-js\"\n\"11\",\"react-native-upgrade\"\n\"11\",\"web-safe-fonts\"\n\"11\",\"release-cycle\"\n\"11\",\"trimble-maps\"\n\"11\",\"jazzylistview\"\n\"11\",\"clim\"\n\"11\",\"liveservertestcase\"\n\"11\",\"fluentbootstrap\"\n\"11\",\"process-injection\"\n\"11\",\"reliablesession\"\n\"11\",\"yii-form\"\n\"11\",\"badsqlgrammarexception\"\n\"11\",\"clickhouse-kafka\"\n\"11\",\"github-third-party-apps\"\n\"11\",\"matplotlib-table\"\n\"11\",\"skim\"\n\"11\",\"deducer\"\n\"11\",\"vue-functional-component\"\n\"11\",\"tensor-indexing\"\n\"11\",\"weechat\"\n\"11\",\"decorator-chaining\"\n\"11\",\"telecom\"\n\"11\",\"ef-postgresql\"\n\"11\",\"jcstress\"\n\"11\",\"clouddevelopmentkit\"\n\"11\",\"default-database\"\n\"11\",\"weixinjsbridge\"\n\"11\",\"vue-flickity\"\n\"11\",\"github-flow\"\n\"11\",\"phpfarm\"\n\"11\",\"tensorflow-decision-forests\"\n\"11\",\"teamwork-projects\"\n\"11\",\"flint\"\n\"11\",\"gridle\"\n\"11\",\"mvcextensions\"\n\"11\",\"dbmigrator\"\n\"11\",\"php-fig\"\n\"11\",\"wexpect\"\n\"11\",\"skreferencenode\"\n\"11\",\"anycast\"\n\"11\",\"repost\"\n\"11\",\"giteye\"\n\"11\",\"srvany\"\n\"11\",\"ant-junit\"\n\"11\",\"fedora-coreos\"\n\"11\",\"flippy\"\n\"11\",\"skyfloatinglabeltextfield\"\n\"11\",\"anti-xml\"\n\"11\",\"ternary-representation\"\n\"11\",\"dbsetup\"\n\"11\",\"bac0\"\n\"11\",\"anyhow\"\n\"11\",\"sscli\"\n\"11\",\"decisiontreeclassifier\"\n\"11\",\"vue-query\"\n\"11\",\"gridview.builder\"\n\"11\",\"yardoc\"\n\"11\",\"list.selectedvalue\"\n\"11\",\"slim-2\"\n\"11\",\"fedora-33\"\n\"11\",\"clflush\"\n\"11\",\"cdi-2.0\"\n\"11\",\"api-platform\"\n\"11\",\"pact-node\"\n\"11\",\"pimcore-datahub\"\n\"11\",\"cdo-emf\"\n\"11\",\"const-string\"\n\"11\",\"sel4\"\n\"11\",\"fileslurp\"\n\"11\",\"xhtml-mp\"\n\"11\",\"kuma\"\n\"11\",\"constructorargument\"\n\"11\",\"connect.js\"\n\"11\",\"jsonrpc4j\"\n\"11\",\"bioconda\"\n\"11\",\"kubernetes-vitess\"\n\"11\",\"pagekit\"\n\"11\",\"pagekite\"\n\"11\",\"apache-nifi-toolkit\"\n\"11\",\"xmlindex\"\n\"11\",\"uniform-cost-search\"\n\"11\",\"ftpes\"\n\"11\",\"independentsoft\"\n\"11\",\"consolas\"\n\"11\",\"ng2-semantic-ui\"\n\"11\",\"chocolatechip-ui\"\n\"11\",\"apache-unomi\"\n\"11\",\"swiftui-fileimporter\"\n\"11\",\"flarum\"\n\"11\",\"implicit-class\"\n\"11\",\"pircbot\"\n\"11\",\"fira-code\"\n\"11\",\"xml-crypto\"\n\"11\",\"langohr\"\n\"11\",\"fingerprintjs\"\n\"11\",\"smoothie.js\"\n\"11\",\"immutablearray\"\n\"11\",\"immer\"\n\"11\",\"json2csharp\"\n\"11\",\"imgix-js\"\n\"11\",\"flambe\"\n\"11\",\"pion-net\"\n\"11\",\"swiftui-table\"\n\"11\",\"flashmessenger\"\n\"11\",\"imposm\"\n\"11\",\"flashsocket\"\n\"11\",\"xml-document-transform\"\n\"11\",\"laravel-config\"\n\"11\",\"image-management\"\n\"11\",\"cimport\"\n\"11\",\"final-class\"\n\"11\",\"owasp-dependency-track\"\n\"11\",\"ox\"\n\"11\",\"filterfactory\"\n\"11\",\"content-platform-engine\"\n\"11\",\"imsdroid\"\n\"11\",\"pinned-shortcut\"\n\"11\",\"sybase-rs\"\n\"11\",\"p4vs\"\n\"11\",\"filewalker\"\n\"11\",\"xpc-target\"\n\"11\",\"l10n.js\"\n\"11\",\"sylius-resource\"\n\"11\",\"chropath\"\n\"11\",\"sym\"\n\"11\",\"disclosuregroup\"\n\"11\",\"bit-src\"\n\"11\",\"bits-per-pixel\"\n\"11\",\"kylix\"\n\"11\",\"flee\"\n\"11\",\"bitbucket-webhook\"\n\"11\",\"fleetboard\"\n\"11\",\"contactitem\"\n\"11\",\"contact-center\"\n\"11\",\"chrome-sync\"\n\"11\",\"pactflow\"\n\"11\",\"python-generateds\"\n\"11\",\"catch-exception\"\n\"11\",\"function-address\"\n\"11\",\"daypilot-scheduler\"\n\"11\",\"python-egg-cache\"\n\"11\",\"funambol\"\n\"11\",\"db2-express-c\"\n\"11\",\"s3-object-tagging\"\n\"11\",\"mariadb-connector\"\n\"11\",\"safariservices\"\n\"11\",\"advanced-threat-protection\"\n\"11\",\"python-control\"\n\"11\",\"python-constraint\"\n\"11\",\"sag\"\n\"11\",\"fullstory\"\n\"11\",\"semweb\"\n\"11\",\"appconkit\"\n\"11\",\"sails-redis\"\n\"11\",\"adobe-native-extensions\"\n\"11\",\"datumbox\"\n\"11\",\"python-inspect\"\n\"11\",\"platformview\"\n\"11\",\"vql\"\n\"11\",\"checkjs\"\n\"11\",\"snap-to-grid\"\n\"11\",\"xmlworkerhelper\"\n\"11\",\"pix\"\n\"11\",\"vscode-restclient\"\n\"11\",\"vscode-server\"\n\"11\",\"first-class-modules\"\n\"11\",\"jsr-275\"\n\"11\",\"five9\"\n\"11\",\"xmlstore\"\n\"11\",\"nginx-status\"\n\"11\",\"cartalyst\"\n\"11\",\"uiweb\"\n\"11\",\"xdp-ebpf\"\n\"11\",\"soap-rpc-encoded\"\n\"11\",\"cascade-filtering\"\n\"11\",\"soapformatter\"\n\"11\",\"carrierwave-direct\"\n\"11\",\"selenium-ruby\"\n\"11\",\"umbraco12\"\n\"11\",\"cargo-features\"\n\"11\",\"running-balance\"\n\"11\",\"mapisendmail\"\n\"11\",\"adjustviewbounds\"\n\"11\",\"selenium-builder\"\n\"11\",\"childactivity\"\n\"11\",\"databricks-notebook\"\n\"11\",\"document-preview\"\n\"11\",\"uplevel\"\n\"11\",\"aleph-ilp\"\n\"11\",\"push-promise\"\n\"11\",\"updateexception\"\n\"11\",\"reagent-forms\"\n\"11\",\"rsrc\"\n\"11\",\"gradle-cache\"\n\"11\",\"w3.js\"\n\"11\",\"warp10\"\n\"11\",\"push-relabel\"\n\"11\",\"servermiddleware\"\n\"11\",\"hyperkit\"\n\"11\",\"cssbundling-rails\"\n\"11\",\"hyperldger-fabric-peer\"\n\"11\",\"pyansys\"\n\"11\",\"vera++\"\n\"11\",\"iconutil\"\n\"11\",\"ora-00947\"\n\"11\",\"ora-00984\"\n\"11\",\"ora-01034\"\n\"11\",\"dojo2\"\n\"11\",\"graffiticms\"\n\"11\",\"call-directory-extension\"\n\"11\",\"ahk2\"\n\"11\",\"sharepoint-upgrade\"\n\"11\",\"grails-3.2\"\n\"11\",\"mysql-error-126\"\n\"11\",\"calendar-store\"\n\"11\",\"call-queue\"\n\"11\",\"django-rest-framework-permissions\"\n\"11\",\"sharepoint-documents\"\n\"11\",\"django-role-permissions\"\n\"11\",\"fb2\"\n\"11\",\"robotium-recorder\"\n\"11\",\"calculated-property\"\n\"11\",\"camel-jpa\"\n\"11\",\"watchmaker\"\n\"11\",\"django-localeurl\"\n\"11\",\"rtrt\"\n\"11\",\"mysqlcheck\"\n\"11\",\"grammarly\"\n\"11\",\"na.approx\"\n\"11\",\"django-sitetree\"\n\"11\",\"validates-associated\"\n\"11\",\"ajax.request\"\n\"11\",\"aws-sdk-android\"\n\"11\",\"campfire\"\n\"11\",\"jgitver\"\n\"11\",\"my-model-jami\"\n\"11\",\"dll-dependency\"\n\"11\",\"windb\"\n\"11\",\"validity.js\"\n\"11\",\"ruby-parser\"\n\"11\",\"operator-lifecycle-manager\"\n\"11\",\"vunit\"\n\"11\",\"vuze\"\n\"11\",\"nanodbc\"\n\"11\",\"urlbinding\"\n\"11\",\"roadkill-wiki\"\n\"11\",\"verold\"\n\"11\",\"gradlefx\"\n\"11\",\"pycoral\"\n\"11\",\"oracleinternals\"\n\"11\",\"gpu-managed-memory\"\n\"11\",\"ibm-maximo-worker-insights\"\n\"11\",\"gpu-local-memory\"\n\"11\",\"fairlearn\"\n\"11\",\"mhash\"\n\"11\",\"angularjs-slider\"\n\"11\",\"microbundle\"\n\"11\",\"jira-mobile-connect\"\n\"11\",\"angular-material-9\"\n\"11\",\"angular-material-tab\"\n\"11\",\"failable\"\n\"11\",\"fakevim\"\n\"11\",\"i2b2\"\n\"11\",\"r-lib-cpp11\"\n\"11\",\"redbird\"\n\"11\",\"recursive-templates\"\n\"11\",\"oracle-apex-22\"\n\"11\",\"ibm-swift-sandbox\"\n\"11\",\"fact++\"\n\"11\",\"ibm-was-oc\"\n\"11\",\"jqbargraph\"\n\"11\",\"crystal-reports-2005\"\n\"11\",\"oracle-map-viewer\"\n\"11\",\"ptokax\"\n\"11\",\"fastapi-crudrouter\"\n\"11\",\"gpu-overdraw\"\n\"11\",\"simplicity-studio\"\n\"11\",\"ptpython\"\n\"11\",\"pt-query-digest\"\n\"11\",\"aws-acm-certificate\"\n\"11\",\"hyper-virtualization\"\n\"11\",\"algorithmia\"\n\"11\",\"micromamba\"\n\"11\",\"wce\"\n\"11\",\"aws-databrew\"\n\"11\",\"angular-router-loader\"\n\"11\",\"animated-webp\"\n\"11\",\"dask-gateway\"\n\"11\",\"aws-automation\"\n\"11\",\"rebass\"\n\"11\",\"rmic\"\n\"11\",\"aws-amplify-vue\"\n\"11\",\"fancyupload\"\n\"11\",\"ora-12519\"\n\"11\",\"seqlock\"\n\"11\",\"avmutablevideocomposition\"\n\"11\",\"hypertrack\"\n\"11\",\"fare\"\n\"11\",\"realm-cloud\"\n\"11\",\"csf\"\n\"11\",\"facebooktoolkit\"\n\"11\",\"cross-domain-proxy\"\n\"11\",\"aws-cdk-context\"\n\"11\",\"rollingfilesink\"\n\"11\",\"unless\"\n\"11\",\"sinemacula\"\n\"11\",\"joomla3.9\"\n\"11\",\"fastify-jwt\"\n\"11\",\"farbtastic\"\n\"11\",\"svn-api\"\n\"11\",\"twiny\"\n\"11\",\"mongoose-deleteone\"\n\"11\",\"dynamodb-gsi\"\n\"11\",\"guitexture\"\n\"11\",\"jul-to-slf4j\"\n\"11\",\"jumblr\"\n\"11\",\"core-banking\"\n\"11\",\"wrapt\"\n\"11\",\"sbt-crossproject\"\n\"11\",\"couchdb-2.x\"\n\"11\",\"sbt-concat\"\n\"11\",\"twisted.words\"\n\"11\",\"sbt-aspectj\"\n\"11\",\"interval-intersection\"\n\"11\",\"core.match\"\n\"11\",\"earthly\"\n\"11\",\"applaud\"\n\"11\",\"neo4j-dotnet-driver\"\n\"11\",\"dynamicgridview\"\n\"11\",\"ergm\"\n\"11\",\"jquery-boilerplate\"\n\"11\",\"nsmergepolicy\"\n\"11\",\"nsmutablecopying\"\n\"11\",\"native-executable\"\n\"11\",\"gs1-qr-code\"\n\"11\",\"dynamics-crm-sdk\"\n\"11\",\"e57\"\n\"11\",\"twitter-client\"\n\"11\",\"azure-ase\"\n\"11\",\"population-count\"\n\"11\",\"jqxtreegrid\"\n\"11\",\"couchnode\"\n\"11\",\"hostnetwork\"\n\"11\",\"nsbatchupdaterequest\"\n\"11\",\"gulp-jest\"\n\"11\",\"hot-code-replace\"\n\"11\",\"tttattritubedlabel\"\n\"11\",\"infor-eam\"\n\"11\",\"episerver-forms\"\n\"11\",\"epoch.js\"\n\"11\",\"nsdecimal\"\n\"11\",\"ncftp\"\n\"11\",\"cramp\"\n\"11\",\"ws-ex-layered\"\n\"11\",\"negation-as-failure\"\n\"11\",\"dxgrid\"\n\"11\",\"svn-copy\"\n\"11\",\"crash-recovery\"\n\"11\",\"bowtie2\"\n\"11\",\"wpflocalizationextension\"\n\"11\",\"dependent-method-type\"\n\"11\",\"boost-container\"\n\"11\",\"cosmwasm\"\n\"11\",\"ert\"\n\"11\",\"twilio-sdk\"\n\"11\",\"corert\"\n\"11\",\"equalsverifier\"\n\"11\",\"bounded-quantification\"\n\"11\",\"neo4j.py\"\n\"11\",\"wpm\"\n\"11\",\"wpn-xm\"\n\"11\",\"android-time-square\"\n\"11\",\"gulp-connect-php\"\n\"11\",\"demeteorizer\"\n\"11\",\"jquery-mobile-loader\"\n\"11\",\"swift5.9\"\n\"11\",\"devdefined-oauth\"\n\"11\",\"ws-notification\"\n\"11\",\"sbt-sonatype\"\n\"11\",\"nehalem\"\n\"11\",\"spring-modulith\"\n\"11\",\"sbt-proguard\"\n\"11\",\"boomla\"\n\"11\",\"inline-editor\"\n\"11\",\"blendability\"\n\"11\",\"silverfrost-fortran\"\n\"11\",\"android-sdk-build-tools\"\n\"11\",\"bodybuilder.js\"\n\"11\",\"inputevent\"\n\"11\",\"silverlight-plugin\"\n\"11\",\"apple-profile-manager\"\n\"11\",\"android-sparsearray\"\n\"11\",\"oneplus6t\"\n\"11\",\"ndi\"\n\"11\",\"nest2\"\n\"11\",\"open4\"\n\"11\",\"inverse-match\"\n\"11\",\"applicationmanager\"\n\"11\",\"mod-filter\"\n\"11\",\"arangoimport\"\n\"11\",\"blcr\"\n\"11\",\"ndde\"\n\"11\",\"ndk-stack\"\n\"11\",\"input-button-image\"\n\"11\",\"simian\"\n\"11\",\"ionic-popover\"\n\"11\",\"peachpie\"\n\"11\",\"android-stlport\"\n\"11\",\"simmechanics\"\n\"11\",\"simple-authentication\"\n\"11\",\"grunt-contrib-jade\"\n\"11\",\"bolero\"\n\"11\",\"paypal-android-sdk\"\n\"11\",\"jxmapkit\"\n\"11\",\"html-xml-utils\"\n\"11\",\"axi4\"\n\"11\",\"portletbridge\"\n\"11\",\"post-type\"\n\"11\",\"ionic2-tabs\"\n\"11\",\"ndoc\"\n\"11\",\"kaleidoscope\"\n\"11\",\"axelor\"\n\"11\",\"kargers-algorithm\"\n\"11\",\"bluetooth-hci\"\n\"11\",\"application-warmup\"\n\"11\",\"jwasm\"\n\"11\",\"ontorefine\"\n\"11\",\"password-retrieval\"\n\"11\",\"path-iterator\"\n\"11\",\"ons-api\"\n\"11\",\"password-strength\"\n\"11\",\"delay-sign\"\n\"11\",\"pdfminersix\"\n\"11\",\"paver\"\n\"11\",\"ion-item\"\n\"11\",\"initialization-block\"\n\"11\",\"jvi\"\n\"11\",\"apple-watch-standalone\"\n\"11\",\"intunemam\"\n\"11\",\"netbanx-api\"\n\"11\",\"wordpress-3.9\"\n\"11\",\"rhea\"\n\"11\",\"libcoap\"\n\"11\",\"vlc-unity\"\n\"11\",\"wordpress-4.0\"\n\"11\",\"abstraction-layer\"\n\"11\",\"ordinal-classification\"\n\"11\",\"aba\"\n\"11\",\"mixcloud\"\n\"11\",\"mahara\"\n\"11\",\"extjs-form\"\n\"11\",\"osstatus\"\n\"11\",\"attestations\"\n\"11\",\"abstract-interpretation\"\n\"11\",\"objc-protocol\"\n\"11\",\"exscript\"\n\"11\",\"objc-message-send\"\n\"11\",\"orjson\"\n\"11\",\"oracle-xml-db-repository\"\n\"11\",\"mac-in-cloud\"\n\"11\",\"oanda\"\n\"11\",\"ormar\"\n\"11\",\"extension-objects\"\n\"11\",\"lempel-ziv-76\"\n\"11\",\"mitreid-connect\"\n\"11\",\"system-stored-procedures\"\n\"11\",\"system.web.extensions\"\n\"11\",\"browsercaps\"\n\"11\",\"networkcomms.net\"\n\"11\",\"rational-performance-test\"\n\"11\",\"orphan-removal\"\n\"11\",\"goangular\"\n\"11\",\"mindtouch\"\n\"11\",\"anchor-modeling\"\n\"11\",\"built.io\"\n\"11\",\"object-test-bench\"\n\"11\",\"letsencrypt-nginx-proxy-companion\"\n\"11\",\"anamorphism\"\n\"11\",\"go-generate\"\n\"11\",\"objectiveflickr\"\n\"11\",\"win-shell\"\n\"11\",\"breach-attack\"\n\"11\",\"oauth2-toolkit\"\n\"11\",\"mini-language\"\n\"11\",\"rawpy\"\n\"11\",\"wm-command\"\n\"11\",\"python.el\"\n\"11\",\"abbrevia\"\n\"11\",\"objective-c-framework\"\n\"11\",\"brep\"\n\"11\",\"libtins\"\n\"11\",\"authenticationchallenge\"\n\"11\",\"rim-4.2\"\n\"11\",\"tabbarios\"\n\"11\",\"siema\"\n\"11\",\"ufs\"\n\"11\",\"rim-4.6\"\n\"11\",\"rim-4.7\"\n\"11\",\"pykka\"\n\"11\",\"sfcartesianchart\"\n\"11\",\"facebook-field-expansion\"\n\"11\",\"microsoft-tag\"\n\"11\",\"kratos\"\n\"11\",\"23andme-api\"\n\"11\",\"authentication-flows\"\n\"11\",\"sifting-appender\"\n\"11\",\"freebase-acre\"\n\"11\",\"sigmaplot\"\n\"11\",\"visual-foxpro-9\"\n\"11\",\"java-war\"\n\"11\",\"java-persistence-api\"\n\"11\",\"obsidian-dataview\"\n\"11\",\"visual-c++-2022\"\n\"11\",\"komodo-ide\"\n\"11\",\"vim-tabular\"\n\"11\",\"min.js\"\n\"11\",\"codan\"\n\"11\",\"udisks\"\n\"11\",\"visual-age\"\n\"11\",\"atom-liveserver\"\n\"11\",\"microsoft-webdriver\"\n\"11\",\"retrying\"\n\"11\",\"ripping\"\n\"11\",\"shinyscreenshot\"\n\"11\",\"coderef\"\n\"11\",\"uccapi\"\n\"11\",\"midasr\"\n\"11\",\"pyisapie\"\n\"11\",\".net-core-rc1\"\n\"11\",\"videogular2\"\n\"11\",\"neural-mt\"\n\"11\",\"setorientation\"\n\"11\",\"goofys\"\n\"11\",\"pyface\"\n\"11\",\"rallyapi\"\n\"11\",\"codeigniter-session\"\n\"11\",\".net-internals\"\n\"11\",\".net-interactive\"\n\"11\",\"japid\"\n\"11\",\"jaotc\"\n\"11\",\"korpus\"\n\"11\",\"9-bit-serial\"\n\"11\",\"occam-pi\"\n\"11\",\"google-api-key-restrictions\"\n\"11\",\"neuroph\"\n\"11\",\"lumen-5.5\"\n\"11\",\"10gen-csharp-driver\"\n\"11\",\"a2hs\"\n\"11\",\"goji\"\n\"11\",\"typewatch\"\n\"11\",\"lubm\"\n\"11\",\"1c\"\n\"11\",\"net-library\"\n\"11\",\"rainbows\"\n\"11\",\"forio-contour\"\n\"11\",\"razor-component-library\"\n\"11\",\"7digital\"\n\"11\",\"shopifysharp\"\n\"11\",\"type-only-import-export\"\n\"11\",\"sha1sum\"\n\"11\",\"google-api-cpp-client\"\n\"11\",\"oursql\"\n\"11\",\"lumisoft\"\n\"11\",\"visual-studio-exp-2013\"\n\"11\",\"sidekit\"\n\"11\",\"aac+\"\n\"11\",\"shopify-app-extension\"\n\"11\",\"audio-playback-agent\"\n\"11\",\"synchronizedcollection\"\n\"11\",\"revealing-prototype\"\n\"11\",\"convex.dev\"\n\"11\",\"gvfs\"\n\"11\",\"handlerexceptionresolver\"\n\"11\",\"policy-violation\"\n\"11\",\"cactoos\"\n\"11\",\"vertical-partitioning\"\n\"11\",\"screenshotexception\"\n\"11\",\"gvnix-es\"\n\"11\",\"policy-based-security\"\n\"11\",\"hard-real-time\"\n\"11\",\"gatsby-plugin-intl\"\n\"11\",\"handlersocket\"\n\"11\",\"polardb\"\n\"11\",\"dragonfly-bsd\"\n\"11\",\"redhat-decision-manager\"\n\"11\",\"redirectwithcookies\"\n\"11\",\"android-companion-device\"\n\"11\",\"drupal-commons\"\n\"11\",\"draftail\"\n\"11\",\"mksh\"\n\"11\",\"drawablegamecomponent\"\n\"11\",\"expectit\"\n\"11\",\"redismqserver\"\n\"11\",\"druid-rs\"\n\"11\",\"convoy-pattern\"\n\"11\",\"directml\"\n\"11\",\"cookie-policy\"\n\"11\",\"diagflow\"\n\"11\",\"control-state\"\n\"11\",\"tcustomcontrol\"\n\"11\",\"mobify-js\"\n\"11\",\"registrator\"\n\"11\",\"harvest-scm\"\n\"11\",\"hash-reference\"\n\"11\",\"mobirise\"\n\"11\",\"redux-logger\"\n\"11\",\"gadinterstitial\"\n\"11\",\"exotel-api\"\n\"11\",\"nuxtserverinit\"\n\"11\",\"exmpp\"\n\"11\",\"cache-oblivious\"\n\"11\",\"cache-money\"\n\"11\",\"veusz\"\n\"11\",\"hadoop-native-library\"\n\"11\",\"nvim.cmp\"\n\"11\",\"regionadapter\"\n\"11\",\"normalize-space\"\n\"11\",\"gemcutter\"\n\"11\",\"drupal-gmap\"\n\"11\",\"diem-cms\"\n\"11\",\"pluto\"\n\"11\",\"plexe\"\n\"11\",\"assembly-reference-path\"\n\"11\",\"assemblyfileversion\"\n\"11\",\"bulkhead\"\n\"11\",\"android-expandable-list-view\"\n\"11\",\"hook-widgets\"\n\"11\",\"dpd\"\n\"11\",\"hadoop2.7.3\"\n\"11\",\"cop\"\n\"11\",\"reference-parameters\"\n\"11\",\"bunch\"\n\"11\",\"asp.net-web-api-filters\"\n\"11\",\"mockall\"\n\"11\",\"bump2version\"\n\"11\",\"hopac\"\n\"11\",\"hllapi\"\n\"11\",\"scikit-build\"\n\"11\",\"xc32\"\n\"11\",\"sqlglot\"\n\"11\",\"sqliteexception\"\n\"11\",\"opentelemetry-js\"\n\"11\",\"tmsh\"\n\"11\",\"sproutcore-controllers\"\n\"11\",\"sprof\"\n\"11\",\"izimodal\"\n\"11\",\"xar\"\n\"11\",\"xcode-build-phase\"\n\"11\",\"tinyxpath\"\n\"11\",\"dotnet-dev-certs\"\n\"11\",\"token-pasting-operator\"\n\"11\",\"jambi\"\n\"11\",\"azure-xplat-cli\"\n\"11\",\"azure-synapse-link\"\n\"11\",\"uiactivityitemprovider\"\n\"11\",\"taiko\"\n\"11\",\"xamlbuild\"\n\"11\",\"spring-webtestclient\"\n\"11\",\"highcharts-boost\"\n\"11\",\"uialertsheet\"\n\"11\",\"iterator-range\"\n\"11\",\"sql-search\"\n\"11\",\"iterated-logarithm\"\n\"11\",\"spring-tld\"\n\"11\",\"scrapyjs\"\n\"11\",\"dspace-ecu\"\n\"11\",\"mod-authz-host\"\n\"11\",\"tapandhold\"\n\"11\",\"modal-sheet\"\n\"11\",\"rabbitmq-cluster\"\n\"11\",\"scrapy-request\"\n\"11\",\"hit-count\"\n\"11\",\"xcode11.5\"\n\"11\",\"quickcontact\"\n\"11\",\"tact\"\n\"11\",\"android-ble-library\"\n\"11\",\"hivecli\"\n\"11\",\"uifocusguide\"\n\"11\",\"azure-virtual-network-gateway\"\n\"11\",\"polymaps\"\n\"11\",\"xcode16\"\n\"11\",\"uidocumentpicker\"\n\"11\",\"uimotioneffect\"\n\"11\",\"sqlanydb\"\n\"11\",\"tabnine\"\n\"11\",\"tkpdfviewer\"\n\"11\",\"timing-diagram\"\n\"11\",\"r10k\"\n\"11\",\"xamarin.communitytoolkit\"\n\"11\",\"isomorphic-git\"\n\"11\",\"scnsphere\"\n\"11\",\"hivedb\"\n\"11\",\"tkintertable\"\n\"11\",\"azure-vm-extension\"\n\"11\",\"openshift-nextgen\"\n\"11\",\"wysihat\"\n\"11\",\"qx11embedcontainer\"\n\"11\",\"openmx\"\n\"11\",\"tactionlist\"\n\"11\",\"opennn\"\n\"11\",\"openshift-web-console\"\n\"11\",\"iphone-wax\"\n\"11\",\"esri-leaflet-geocoder\"\n\"11\",\"cfb-mode\"\n\"11\",\"merge-tracking\"\n\"11\",\"promoting\"\n\"11\",\"responsive-navigation\"\n\"11\",\"nltokenizer\"\n\"11\",\"cextension\"\n\"11\",\"text-capture\"\n\"11\",\"spline-data-lineage-tracker\"\n\"11\",\"eta-expansion\"\n\"11\",\"android-orm\"\n\"11\",\"cuda-wmma\"\n\"11\",\"iphone-3g\"\n\"11\",\"texlipse\"\n\"11\",\"ethercard\"\n\"11\",\"android-lottie\"\n\"11\",\"android-locale\"\n\"11\",\"certbot-dns-plugin\"\n\"11\",\"menubarextra\"\n\"11\",\"android-jetpack-compose-row\"\n\"11\",\"response.filter\"\n\"11\",\"android-jetpack-compose-pager\"\n\"11\",\"duckyscript\"\n\"11\",\"menpo\"\n\"11\",\"acts-as-nested-set\"\n\"11\",\"centrifugo\"\n\"11\",\"testflight-public-link\"\n\"11\",\"activity-oncreateview\"\n\"11\",\"centos-6.9\"\n\"11\",\"resourceproviderfactory\"\n\"11\",\"react-hot-toast\"\n\"11\",\"company-mode\"\n\"11\",\"nocilla\"\n\"11\",\"spdx\"\n\"11\",\"project-folder\"\n\"11\",\"terraform-cli\"\n\"11\",\"angular2-localstorage\"\n\"11\",\"ogl\"\n\"11\",\"large-query\"\n\"11\",\"spatie-activitylog\"\n\"11\",\"python-visual\"\n\"11\",\"angular-cdk-overlay\"\n\"11\",\"commondomain\"\n\"11\",\"restricted-profiles\"\n\"11\",\"niceforms\"\n\"11\",\"angular-jit\"\n\"11\",\"esdoc\"\n\"11\",\"comlink\"\n\"11\",\"mootools-sortable\"\n\"11\",\"generic-relationship\"\n\"11\",\"comet-ml\"\n\"11\",\"nifi-api\"\n\"11\",\"genesys-platform-sdk\"\n\"11\",\"react-native-0.46\"\n\"11\",\"react-multiselect-checkboxes\"\n\"11\",\"sparsehash\"\n\"11\",\"node-blade\"\n\"11\",\"sparse-columns\"\n\"11\",\"charm\"\n\"11\",\"eslint-plugin-react\"\n\"11\",\"charisma\"\n\"11\",\"laravel-pint\"\n\"11\",\"columnattribute\"\n\"11\",\"textlocal\"\n\"11\",\"prolog-tabling\"\n\"11\",\"messageformat.js\"\n\"11\",\"terraform-provider-vault\"\n\"11\",\"ipreviewhandler\"\n\"11\",\"colormath\"\n\"11\",\"prometheus-postgres-exporter\"\n\"11\",\"geometry-class-library\"\n\"11\",\"moops\"\n\"11\",\"ctfontref\"\n\"11\",\"ch\"\n\"11\",\"terraform-workspace\"\n\"11\",\"action-open-document-tree\"\n\"11\",\"access-database-engine\"\n\"11\",\"pen-tablet\"\n\"11\",\"required-reason-api\"\n\"11\",\"event-gateway\"\n\"11\",\"lso\"\n\"11\",\"action-scheduler\"\n\"11\",\"ios-mqtt-client-framework\"\n\"11\",\"hypercard\"\n\"11\",\"huobi\"\n\"11\",\"logstash-input-jdbc\"\n\"11\",\"react-charts\"\n\"11\",\"stplanr\"\n\"11\",\"qgrid\"\n\"11\",\"ios-icons\"\n\"11\",\"qgraphicspathitem\"\n\"11\",\"httpserverutility\"\n\"11\",\"oculus-runtime\"\n\"11\",\"lookup-field\"\n\"11\",\"google-cloud-php-client\"\n\"11\",\"react-concurrent\"\n\"11\",\"zwoptex\"\n\"11\",\"perfplot\"\n\"11\",\"stringdictionary\"\n\"11\",\"resig\"\n\"11\",\"qlocalserver\"\n\"11\",\"zumo\"\n\"11\",\"lovefield\"\n\"11\",\"requests-futures\"\n\"11\",\"custom-panel\"\n\"11\",\"active-passive\"\n\"11\",\"logical-reads\"\n\"11\",\"perl-html-template\"\n\"11\",\"qmultimap\"\n\"11\",\"reactablefmtr\"\n\"11\",\"react-developer-tools\"\n\"11\",\"streaming-video\"\n\"11\",\"odata-connected-service\"\n\"11\",\"linfu-dynamicproxy\"\n\"11\",\"glkbaseeffect\"\n\"11\",\"statsvn\"\n\"11\",\"urlsplit\"\n\"11\",\"quaqua\"\n\"11\",\"elm-test\"\n\"11\",\"webkit-appearance\"\n\"11\",\"gles20\"\n\"11\",\"elm-port\"\n\"11\",\"ar-mailer\"\n\"11\",\"go-agent\"\n\"11\",\"componentwillreceiveprops\"\n\"11\",\"pgjdbc-ng\"\n\"11\",\"amazon-sagemaker-clarify\"\n\"11\",\"haskell-src-exts\"\n\"11\",\"tfstate\"\n\"11\",\"qualified\"\n\"11\",\"beaker-testing\"\n\"11\",\"ascensor\"\n\"11\",\"source-separation\"\n\"11\",\"scrubyt\"\n\"11\",\"headereditemscontrol\"\n\"11\",\"component-design\"\n\"11\",\"sourcekitservice\"\n\"11\",\"embree\"\n\"11\",\"conf.d\"\n\"11\",\"qt6.4.1\"\n\"11\",\"compiled-bindings\"\n\"11\",\"webpack-handlebars-loader\"\n\"11\",\"mdxclient\"\n\"11\",\"user-environment\"\n\"11\",\"zend-config-xml\"\n\"11\",\"condition-system\"\n\"11\",\"dartpad\"\n\"11\",\"emokit\"\n\"11\",\"haven\"\n\"11\",\"precompiled-views\"\n\"11\",\"hbasetestingutility\"\n\"11\",\"git-revision\"\n\"11\",\"scriptom\"\n\"11\",\"stdstack\"\n\"11\",\"given\"\n\"11\",\"zerossl\"\n\"11\",\"if-case\"\n\"11\",\"gjslint\"\n\"11\",\"hcl-connections\"\n\"11\",\"archway-network\"\n\"11\",\"scrollbox\"\n\"11\",\"conditional-move\"\n\"11\",\"hcluster\"\n\"11\",\"zoomooz\"\n\"11\",\"uses-clause\"\n\"11\",\"msnodesqlv8\"\n\"11\",\"asgardcms\"\n\"11\",\"webmethods-caf\"\n\"11\",\"precompiled-binaries\"\n\"11\",\"maybeuninit\"\n\"11\",\"usesound\"\n\"11\",\"usb-modeswitch\"\n\"11\",\"ascmd\"\n\"11\",\"amasty\"\n\"11\",\"autogluon\"\n\"11\",\"scribe-server\"\n\"11\",\"lighty\"\n\"11\",\"arcface\"\n\"11\",\"amazon-macie\"\n\"11\",\"secevents\"\n\"11\",\"weavy\"\n\"11\",\"cycle-sort\"\n\"11\",\"preprocessor-meta-program\"\n\"11\",\"linearprogressindicator\"\n\"11\",\"mediarss\"\n\"11\",\"batchnorm\"\n\"11\",\"struts2-bootstrap-plugin\"\n\"11\",\"array-population\"\n\"11\",\"google-settings\"\n\"11\",\"totem\"\n\"11\",\"zipmap\"\n\"11\",\"em-synchrony\"\n\"11\",\"vaadin-designer\"\n\"11\",\"web-api-contrib\"\n\"11\",\"maven-jarsigner-plugin\"\n\"11\",\"array-initialize\"\n\"11\",\"avaudioconverter\"\n\"11\",\"flutter-tex\"\n\"11\",\"imageareaselect\"\n\"11\",\"ticoredatasync\"\n\"11\",\"dancer2\"\n\"11\",\"stario-sdk\"\n\"11\",\"thiserror\"\n\"11\",\"focus-stealing\"\n\"11\",\"global-key\"\n\"11\",\"sortedcollection\"\n\"11\",\"identityserver6\"\n\"11\",\"autocode\"\n\"11\",\"arview\"\n\"11\",\"multidrop-bus\"\n\"11\",\"multi-az\"\n\"11\",\"zio-json\"\n\"11\",\"allusersprofile\"\n\"11\",\"que\"\n\"11\",\"shenandoah\"\n\"11\",\"linqpad7\"\n\"11\",\"autodesk-tandem\"\n\"11\",\"startforegroundservice\"\n\"11\",\"sdhc\"\n\"11\",\"zen-of-python\"\n\"11\",\"endly\"\n\"11\",\"fontastic\"\n\"11\",\"google-oauth-.net-client\"\n\"11\",\"helm-tls\"\n\"11\",\"stitches\"\n\"11\",\"autodesk-vault\"\n\"11\",\"flutter-reactive-forms\"\n\"11\",\"sparktable\"\n\"11\",\"email-body\"\n\"11\",\"starship\"\n\"11\",\"amazon-parallelcluster\"\n\"11\",\"staticmatic\"\n\"11\",\"linux-rt\"\n\"11\",\"bigstatsr\"\n\"11\",\"topsy\"\n\"11\",\"lindo\"\n\"11\",\"folder-access\"\n\"11\",\"yui-datasource\"\n\"11\",\"automotive-grade-linux\"\n\"11\",\"static-language\"\n\"10\",\"square-checkout\"\n\"10\",\"process-accounting\"\n\"10\",\"skpsmtpmessage\"\n\"10\",\"react-native-skia\"\n\"10\",\"replaysubject\"\n\"10\",\"cloudcannon\"\n\"10\",\"fiducial-markers\"\n\"10\",\"triplot\"\n\"10\",\"dead-key\"\n\"10\",\"apache-edgent\"\n\"10\",\"procedural-music\"\n\"10\",\"maskededitvalidator\"\n\"10\",\"westwind\"\n\"10\",\"transfuse\"\n\"10\",\"instant-view\"\n\"10\",\"xssfworkbook\"\n\"10\",\"grapheme-cluster\"\n\"10\",\"github-fine-grained-tokens\"\n\"10\",\"bartmachine\"\n\"10\",\"problem-steps-recorder\"\n\"10\",\"reportprogress\"\n\"10\",\"websitespark\"\n\"10\",\"xsocket\"\n\"10\",\"lm-sensors\"\n\"10\",\"filenet-ce-sql\"\n\"10\",\"clib\"\n\"10\",\"regular-type\"\n\"10\",\"closeablehttpresponse\"\n\"10\",\"intacct\"\n\"10\",\"westwind-globalization\"\n\"10\",\"temporary-asp.net-files\"\n\"10\",\"profvis\"\n\"10\",\"reactql\"\n\"10\",\"ggtimeseries\"\n\"10\",\"deferrable-constraint\"\n\"10\",\"edify\"\n\"10\",\"skylink\"\n\"10\",\"clcircleregion\"\n\"10\",\"react-phone-input-2\"\n\"10\",\"decentralized-identifiers\"\n\"10\",\"ci-server\"\n\"10\",\"skein\"\n\"10\",\"staking\"\n\"10\",\"decimation\"\n\"10\",\"ghost-inspector\"\n\"10\",\"cloudflarestream\"\n\"10\",\"whitespace-language\"\n\"10\",\"intellij-idea-2020\"\n\"10\",\"sql-server-mobile\"\n\"10\",\"sql-server-on-linux\"\n\"10\",\"teneo\"\n\"10\",\"stampit.js\"\n\"10\",\"stackato\"\n\"10\",\"cloud-integration\"\n\"10\",\"react-rainbow-components\"\n\"10\",\"listview-selector\"\n\"10\",\"barebox\"\n\"10\",\"skype-uri.js\"\n\"10\",\"ggez\"\n\"10\",\"trestle-admin\"\n\"10\",\"ballerina-vscode-plugin\"\n\"10\",\"cloudscribe\"\n\"10\",\"cloudera-navigator\"\n\"10\",\"terasort\"\n\"10\",\"sql-trace\"\n\"10\",\"cloudera-director\"\n\"10\",\"intellij-16\"\n\"10\",\"react-notifications\"\n\"10\",\"stablebaseline3\"\n\"10\",\"tricore\"\n\"10\",\"renviron\"\n\"10\",\"websphere-mq-ams\"\n\"10\",\"trap\"\n\"10\",\"tridion-storage-extension\"\n\"10\",\"react-reducer\"\n\"10\",\"processmaker-api\"\n\"10\",\"transport-layer-protocol\"\n\"10\",\"cleartk\"\n\"10\",\"livestamp.js\"\n\"10\",\"team-city-10.0\"\n\"10\",\"flutter-font\"\n\"10\",\"cl-ppcre\"\n\"10\",\"flipside\"\n\"10\",\"easyrepro\"\n\"10\",\"fluent-bit-rewrite-tag\"\n\"10\",\"tsung-recorder\"\n\"10\",\"jdk1.8-73\"\n\"10\",\"multi-table-delete\"\n\"10\",\"ts-check\"\n\"10\",\"greenrobot-objectbox\"\n\"10\",\"materialapp\"\n\"10\",\"matrix-profile\"\n\"10\",\"yarp\"\n\"10\",\"jest-junit\"\n\"10\",\"mux-video\"\n\"10\",\"jedox\"\n\"10\",\"floating-ip\"\n\"10\",\"small-business-server\"\n\"10\",\"trustkit\"\n\"10\",\"fence-plots\"\n\"10\",\"vue-infinite-loading\"\n\"10\",\"graph-query\"\n\"10\",\"cmfcpropertypage\"\n\"10\",\"mathematical-notation\"\n\"10\",\"yfrog\"\n\"10\",\"clx\"\n\"10\",\"tsql-parser\"\n\"10\",\"photostream\"\n\"10\",\"photoview\"\n\"10\",\"cmockery\"\n\"10\",\"yecc\"\n\"10\",\"xvim\"\n\"10\",\"yii2-user-roles\"\n\"10\",\"dbt-bigquery\"\n\"10\",\"materialfx\"\n\"10\",\"tsparticles\"\n\"10\",\"feature-activation\"\n\"10\",\"truncate-log\"\n\"10\",\"slk\"\n\"10\",\"multiple-mice\"\n\"10\",\"eclipse-m2t-jet\"\n\"10\",\"eclipse-mdt\"\n\"10\",\"eclipse-2018-12\"\n\"10\",\"phalcon-devtools\"\n\"10\",\"ddex\"\n\"10\",\"fedora11\"\n\"10\",\"truevfs\"\n\"10\",\"slik\"\n\"10\",\"ckfetchrecordchangesopera\"\n\"10\",\"apache-http-server\"\n\"10\",\"feedbackpanel\"\n\"10\",\"flowbite-svelte\"\n\"10\",\"react-native-localize\"\n\"10\",\"php-ci\"\n\"10\",\"clang-plugin\"\n\"10\",\"tediousjs\"\n\"10\",\"yamlbeans\"\n\"10\",\"fbsdkappinvitecontent\"\n\"10\",\"vue-pdf\"\n\"10\",\"jenkins-mailer-plugin\"\n\"10\",\"eclipse-metadata\"\n\"10\",\"vsperfmon\"\n\"10\",\"jetpack-compose-swipe-to-dismiss\"\n\"10\",\"management-pack\"\n\"10\",\"s60-3rd-edition\"\n\"10\",\"adsapi-php.ini\"\n\"10\",\"implicit-grant\"\n\"10\",\"discordgo\"\n\"10\",\"xnanimation\"\n\"10\",\"managedthreadfactory\"\n\"10\",\"chatgpt-function-call\"\n\"10\",\"implicit-constructor\"\n\"10\",\"s3proxy\"\n\"10\",\"managedinstallerclass\"\n\"10\",\"rusage\"\n\"10\",\"voice-interaction\"\n\"10\",\"smspdu\"\n\"10\",\"filterexpression\"\n\"10\",\"socket.io-stream\"\n\"10\",\"castle-validators\"\n\"10\",\"json-everything\"\n\"10\",\"swixml\"\n\"10\",\"managed-ews\"\n\"10\",\"data-parallel-haskell\""
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<!--\nCopyright (c) 2019 Joel Martin\nCopyright (c) 2012 Fogus, Jen Myers and Relevance Inc.\nAll rights reserved. The use and distribution terms for this software\nare covered by the Eclipse Public License 1.0\n(http://opensource.org/licenses/eclipse-1.0.php) which can be found in\nthe file COPYING the root of this distribution.  By using this\nsoftware in any fashion, you are agreeing to be bound by the terms of\nthis license.  You must not remove this notice, or any other, from\nthis software.\n-->\n\n<html>\n<head>\n  <meta charset=\"UTF-8\" />\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"web/base.css\" />\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"web/layout.css\" />\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"web/skeleton.css\" />\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"web/himera.css\" />\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"web/ansi.css\" />\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"web/console.css\" />\n  <style type=\"text/css\" media=\"screen\">\n  </style>\n  <title>Mal Web REPL</title>\n</head>\n<body>\n  <div class=\"container\">\n    <h1 id=\"title\"><a href=\"https://github.com/kanaka/mal\"/>Mal</a></h1>\n    \n    <h2>Mal Web REPL</h2>\n\n    <!--\n    <div id=\"editor-container\" class=\"sixteen columns\">\n    <div id=\"tiny-note\" class=\"tiny-note\"></div>\n    <textarea class=\"editor\" id=\"editor\">;; Develop your Mal program here.\n;; Ctrl+E/Cmd+E evaluates file in the REPL.</textarea>\n    </div>\n    -->\n    \n    <div id=\"console-container\" class=\"sixteen columns\">\n    <div class=\"console\" id=\"console\"></div>\n    </div>\n    <div class=\"eight columns\">\n    <!--\n    <h3><span style=\"cursor: pointer\" class=\"doc-link\" id=\"toggle-editor\">Show file editor</span></h3>\n    -->\n    <h3><span style=\"cursor: pointer\" class=\"doc-link\">&nbsp;</span></h3>\n    </div>\n    <div class=\"eight columns\">\n    <div class=\"source\">\n      <a href=\"http://github.com/kanaka/mal\">View source on Github <img src=\"web/github-icon.png\" /></a></p>\n    </div><!-- /source -->\n    </div>\n    \n    <div class=\"rule sixteen columns\"></div>\n\n\t  <div class=\"sixteen columns\">\n    <h3>Mal at a glance</h3>\n    </div>\n    \n    <div class=\"cheat-box-container eight columns\">\n      <div class=\"cheat-box\">\n        <h4>Datatypes</h4>\n        <table>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Maps</td>\n            <td>{\"key1\" \"val1\", \"key2\" 123}</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Lists</td>\n            <td>(1 2 3 \"four\")</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Vectors</td>\n            <td>[1 2 3 4 \"a\" \"b\" \"c\" 1 2]</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Scalars</td>\n            <td>a-symbol, \"a string\", :a_keyword, 123, nil, true, false</td>\n          </tr>\n        </table>\n      </div><!-- /cheat-box -->\n      <div class=\"cheat-box\">\n        <h4>Functions</h4>\n        <table>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Calling</td>\n            <td>(<span class=\"ebnf\">&lt;function&gt;</span>\n\t         <span class=\"ebnf\">&lt;args*&gt;</span>)</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Defining named functions</td>\n            <td>(def! <span class=\"ebnf\">&lt;name&gt;</span> \n                  (fn*\n                  [<span class=\"ebnf\">&lt;args*&gt;</span>]\n                  <span class=\"ebnf\">&lt;action&gt;</span>))</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Anonymous function</td>\n            <td>(fn*\n                  [<span class=\"ebnf\">&lt;args*&gt;</span>]\n                  <span class=\"ebnf\">&lt;action&gt;</span>)</td>\n          </tr>\n        </table>\n      </div><!-- /cheat-box -->\n      <div class=\"cheat-box\">\n        <h4>Useful Macros and Special Forms</h4>\n        <table>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Conditionals</td>\n            <td>if cond or</td>\n          </tr>\n          <!--\n          <tr>\n            <td class=\"row-label\">Nesting, chaining, and Interop</td>\n            <td>-> ->> doto .. .</td>\n          </tr>\n          -->\n          <tr>\n            <td class=\"row-label\">Multiple Actions (side-effects)</td>\n            <td>(do \n                  <span class=\"ebnf\">&lt;action*&gt;</span>...)</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Defining things</td>\n            <td>def! defmacro! let*</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Quoting</td>\n            <td>' ` ~ ~@</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Examining macros</td>\n            <td>macroexpand</td>\n          </tr>\n\t</table>\n      </div>\n    </div><!-- /cheat-box-container -->\n\n    <div class=\"cheat-box-container eight columns\">\n      <div class=\"cheat-box\">\n        <h4>Useful Functions</h4>\n        <table>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Math</td>\n            <td>+ - * /</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Comparison/Boolean</td>\n            <td>= < > <= >= not</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Predicates</td>\n            <td>nil? true? false? symbol? keyword? string? list? vector? map? sequential?</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Data processing</td>\n            <td>map apply</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Data create</td>\n            <td>list vector hash-map</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Data inspection</td>\n            <td>first rest get keys vals count get nth contains? empty?</td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Data manipulation</td>\n            <td>conj cons concat assoc dissoc</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Lists and Vectors</td>\n            <td>first rest nth seq</td></td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Hash Maps</td>\n            <td>get keys vals contains?</td></td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Strings</td>\n            <td>str pr-str seq</td></td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Atoms</td>\n            <td>atom atom? deref[@] reset! swap!</td></td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Meta</td>\n            <td>meta with-meta[^]</td></td>\n          </tr>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Output</td>\n            <td>println prn</td></td>\n          </tr>\n        </table>\n      </div><!-- /cheat-box -->\n      <div class=\"cheat-box\">\n        <h4>JavaScript Interop</h4>\n        <table>\n          <tr class=\"row-one\">\n            <td class=\"row-label\">Evaluate JavaScript</td>\n            <td>(js-eval \"JS string to eval\")</td>\n          </tr>\n          <tr>\n            <td class=\"row-label\">Method call/access</td>\n            <td>(. js-fn arg...)</td>\n          </tr>\n\t</table>\n      </div>\n    </div><!-- /cheat-box-container -->\n        \n    <div class=\"rule sixteen columns\"></div>\n    \n    <div class=\"column footer-logo\">\n        <div>Mal &copy; 2019 Joel Martin</div>\n        <div>Himera design &copy; 2012-2013 <a ref=\"http://www.fogus.me\">Fogus</a>, <a href=\"http://jenmyers.net/\">Jen Myers</a> and <a href=\"http://www.thinkrelevance.com\">Relevance Inc.</a></div>\n    </div>\n    \n  </div><!-- / container -->\n  <script type=\"text/javascript\" src=\"//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js\"></script>\n  <script type=\"text/javascript\" src=\"web/jqconsole.min.js\"></script>\n  <script type=\"text/javascript\" src=\"web/mal.js\"></script>\n  <script>\n    $(function() {\n      // Creating the console.\n      window.jqconsole = $('#console').jqconsole(null, 'user> ');\n\n      printer.println = function () {\n        var str = Array.prototype.join.call(arguments, \" \")\n        jqconsole.Write(str + \"\\n\", 'jqconsole-output');\n      }\n\n      rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n\n      jq_load_history(jqconsole);\n\n      // Abort prompt on Ctrl+C.\n      jqconsole.RegisterShortcut('C', function() {\n        jqconsole.AbortPrompt();\n        handler();\n      });\n      // Move to line start Ctrl+A.\n      jqconsole.RegisterShortcut('A', function() {\n        jqconsole.MoveToStart();\n        handler();\n      });\n      // Move to line end Ctrl+E.\n      jqconsole.RegisterShortcut('E', function() {\n        jqconsole.MoveToEnd();\n        handler();\n      });\n      jqconsole.RegisterMatching('{', '}', 'brace');\n      jqconsole.RegisterMatching('(', ')', 'paren');\n      jqconsole.RegisterMatching('[', ']', 'bracket');\n      jqconsole.RegisterMatching('\"', '\"', 'dquote');\n      // Handle a command.\n      var handler = function(line) {\n        if (line) {\n          try {\n            jqconsole.Write(rep(line) + '\\n', 'jqconsole-return');\n          } catch (exc) {\n            if (exc instanceof reader.BlankException) { return; }\n            if (exc.stack) {\n              jqconsole.Write(exc.stack + '\\n', 'jqconsole-error');\n            } else {\n              jqconsole.Write(exc + '\\n', 'jqconsole-error');\n            }\n          }\n          jq_save_history(jqconsole);\n        }\n        jqconsole.Prompt(true, handler);\n        /*\n        jqconsole.Prompt(true, handler, function(command) {\n          // Continue line if can't compile the command.\n          try {\n            Function(command);\n          } catch (e) {\n            if (/[\\[\\{\\(]$/.test(command)) {\n              return 1;\n            } else {\n              return 0;\n            }\n          }\n          return false;\n        });\n      */\n      };\n\n      // Initiate the first prompt.\n      handler();\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "docs/notes.md",
    "content": "## Counting languages, implementations, and runtimes/MODES\n\n```\n# languages\n$ egrep -v \"\\-mal\\>|IMPL: mal,.*nim\" IMPLS.yml | grep -o \"\\<IMPL: [^,}]*\" | awk '{print $2}' | sort | egrep -v \"\\.2$|swift[0-9]\" | uniq | wc\n      89      89     526\n\n# implementations\n$ egrep -v \"\\-mal\\>|IMPL: mal,.*nim\" IMPLS.yml | grep -o \"\\<IMPL: [^,}]*\" | awk '{print $2}' | sort | uniq | wc\n      95      95     564\n\n# runtimes/MODES\n$ egrep -v \"\\-mal\\>|IMPL: mal,.*nim\" IMPLS.yml | grep -o \"\\<IMPL: [^,}]*\" | awk '{print $2}' | sort | wc\n     117     117     681\n```\n"
  },
  {
    "path": "docs/step_notes.txt",
    "content": "Step Notes:\n\n- step0_repl\n    - prompt, input, READ, EVAL, PRINT, output\n    - readline module\n        - display prompt, read line of input\n    - Details:\n        - get your language compiler/interpreter running\n        - create step0_repl.EXT\n            - loop that reads input, calls rep, writes output, exits\n              on EOF/Ctrl-D\n            - rep calls PRINT(EVAL(READ(str)))\n            - READ, EVAL, PRINT just return input parameter\n        - modify toplevel Makefile\n            - add language (directory name) to IMPLS\n            - add <lang>_STEP_TO_PROG entry\n            - add <lang>_RUNSTEP entry\n        - for a compiled language, add <lang>/Makefile\n            - targets: all, step*, stats, stats-lisp, \n\n- use native eval in EVAL if available\n\n- libedit/GNU readline:\n    - use existing lib, wrap shell call or implement\n    - load history file on first call\n    - add non-blank lines to history\n    - append to history file\n\n- step1_read_print\n    - types module:\n        - add boxed types if no language equivalent:\n            - nil, true, false, symbol, integer, string, list\n        - error types if necessary\n    - reader module:\n        - stateful reader object\n            - alternative: mutate token list\n        - tokenize (if regex available)\n            - standard regex pattern: \"/[\\s,]*(~@|[\\[\\]{}()'`~^@]|\\\"(?:\\\\.|[^\\\\\\\"])*\\\"|;.*|[^\\s\\[\\]{}('\\\"`,;)]*)/\"\n        - read_str\n            - read_form(new Reader(tokenize(str)))\n        - read_form\n            - detect errors\n            - call read_list or read_atom\n        - read_list\n            - read_form until ')'\n            - return array (boxed)\n        - read_atom (not atom type)\n            - return scalar boxed type:\n                - nil, true, false, symbol, integer, string\n            - skip unquoting\n    - printer module:\n        - _pr_str:\n            - stringify boxed types to their Mal representations\n            - list/array is recursive\n            - skip quoting\n    - repl loop\n        - catch errors, print them and continue\n        - impls without exception handling will need to have a global\n          variable with checks for it at the beginning of critical\n          code sections\n    - Details:\n        - copy step0_repl.EXT to step1_read_print.EXT\n            - modify Makefile if compiled\n            - call reader.read_str from READ\n            - pass through type returned from read_str through\n              READ/EVAL/PRINT\n        - create reader.EXT\n            - if regex support (much easier) then tokenize with this:\n                /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g\n            - add read_str:\n                - call tokenize\n                - handle blank line (exceptions, return code, global\n                  depending on lang features)\n            - read_str -> read_form -> {read_list, read_atom}\n                - mutable reader thing\n        - create printer.EXT\n            - _pr_str function which basically reverses read_str and\n              returns a string representation\n        - run `make test^EXT^step1`. Much of the basics should pass up\n          to vectors\n        - implement read_hash_map (can refactor read_list)\n        - import read_vector \n            - probably want to define types for List and Vector in\n              types.EXT that extend or wrap native arrays\n        - run `make test^EXT^step1`. All mandatory should pass\n\n- comments\n\n- vectors\n    - Basically: two array types that retain their boxed types, can be\n      challenging depending on the language (e.g. JS, PHP: no clean\n      way to derive new array types).\n    - types module:\n        - add vector boxed type\n            - derived from array if possible\n        - pr_str:\n            - vector is recursive\n        - sequential?\n    - reader module:\n        - read_vector:\n            - re-use read_list but with different constructor, delims\n\n- hash-maps\n    - reader module:\n        - re-use read_list function and apply that using hash-map\n          constructor\n    - types module:\n        - pr_str addition\n        - hash-map, map?, assoc, dissoc, get, contains?, keys,\n          vals (probably assoc! and dissoc! for internal)\n    - eval_map: eval the keys and values of hash_maps\n    - EVAL:\n        - if hash_map, call eval_map on it\n\n- step2_eval\n    - types module:\n        - symbol?, list? (if no simple idiomatic impl type check)\n        - first, rest, nth on list\n    - eval_ast:\n        - if symbol, return value of looking up in env\n        - if list, eval each item, return new list\n        - otherwise, just return unchanged ast\n    - EVAL/apply:\n        - if not a list, call eval_ast\n        - otherwise, apply first item to eval_ast of (rest ast)\n    - repl_env as simple one level hash map (assoc. array)\n        - store function as hash_map value\n    - Details:\n        - copy step1_read_print.EXT to step2_eval.EXT\n        - create repl_env hash_map) with +, -, *, /\n            - store anon func as values if possible\n        - types.EXT\n            - implement symbol? (symbol_Q) and list? (list_Q)\n        - add env param to EVAL and add to rep EVAL call\n        - EVAL\n            - if not list call eval_ast\n            - otherwise eval_ast, and call first arg with rest\n        - eval_ast\n            - if symbol?, lookup in env\n            - if List, EVAL each and return eval's list\n            - otherwise, return original\n        - optional: handle vector and hash-map in eval_ast\n\n- vectors\n    - eval each item, return new vector\n\n- hash-maps\n    - eval each value, return new hash_map\n\n- step3_env\n    - types module:\n        - may need function type if HashMap is strongly typed (e.g. Java)\n    - env type:\n        - find, set, get (no binds/exprs in constructor yet)\n    - EVAL/apply:\n        - def! - mutate current environment\n        - let* - create new environment with bindings\n    - Details:\n        - cp step2_eval.EXT to step3_env.EXT\n        - add env.EXT if lang support file dep cycles, otherwise, add\n          to types.EXT\n            - Env type\n            - find, get, set methods/functions\n        - use Env type instead of map/assoc. array\n        - eval_ast: use method for lookup\n        - EVAL:\n            - switch on first symbol\n                - def!\n                    - set env[a1] to EVAL(a2, env)\n                - let*\n                    - loop through let building up let_env\n                    - EVAL(a2, let_env)\n                - move apply to default\n\n- step4_if_fn_do\n    - types module:\n        - function type if no closures in impl language\n        - _equal_Q function (recursive)\n    - reader module\n        - string unescaping\n    - printer module\n        - print_readably option for pr_str\n        - add function printing to pr_str\n        - string escaping in pr_str\n    - core module (export via core_ns):\n        - export equal_Q from types as =\n        - move arith operations here\n        - add arith comparison functions\n        - pr_str, str, prn, println\n        - list, list?, count, empty?\n    - env module:\n        - add binds/exprs handling to Env constructor with variable arity\n    - EVAL:\n        - do:\n        - if:\n        - fn*:\n            - simple if language supports closures\n            - otherwise needs a way of representing functions that can\n              have associated metadata\n    - define \"not\" using REP/RE\n    - Details:\n        - cp step3_env.EXT to step4_env.EXT\n            - modify Makefile if compiled\n        - env.EXT\n            - add binds and exprs args. Create new environments with\n              exprs bound to binds. If & symbol, bind rest of exprs to\n              next bind symbol\n        - EVAL:\n            - do:\n                - eval_ast [1:], then return last eval'd element\n            - if\n                - EVAL(a1)\n                    - if true EVAL(a2)\n                    - else EVAL(a3), unless no a3 then return nil\n            - fn*\n                - if available use function closures to return a new\n                  native function that calls EVAL(a2, Env(env, a1, fargs))\n                - otherwise, store exp, params and env in a structure\n        - core.EXT\n            - create ns object to hold core namespace\n            - move numeric operators here\n            - add comparison operators\n            - add list, list?, empty?, count\n        - run make test^EXT^step4\n        - implement equal?/equal_Q in types.EXT and refer in core.ns\n        - implement not as rep(\"(def! not (fn* (a) (if a false true)))\")\n        - run make test^EXT^step4: should pass everything except\n          string routines\n        - implement: pr-str, str, prn, println in core.EXT and\n          refer in core.ns\n            - should leverage pr-str from printer.EXT\n            - add reader/printer string quote/unquote\n\n- step5_tco\n    - types module:\n        - mal function type:\n            - stores: eval, exp, env, params\n            - eval is EVAL in native mal case (needed for map function\n              later), otherwise reference to platform function\n            - if metadata support, then store exp, env, params as\n              metadata\n    - printer\n        - add printing of mal function type\n    - EVAL:\n        - while loop around whole thing\n        - cases where we directly return result of EVAL, instead set\n          ast and env to what would be put in the EVAL, then loop.\n            - do, if, \"apply\"\n            - \"apply\"\n                - if mal function type\n                    - set env to new Env based on properties on the function\n                - if native function, same as before\n    - Details:\n        - types.EXT\n            - create Mal function type to store eval, exp, env, params\n        - cp step4_if_fn_do.EXT to step5_tco.EXT\n            - wrap EVAL in infinite while loop\n            - in let*, do, and if:\n                - set ast and env and loop (no return)\n            - in fn* create Mal function type\n        - if compiled, update Makefile\n        - in apply, test if Mal function type:\n            - if so, generate new env from stored env, args and callee\n              params\n            - set ast to stored ast\n\n\n- step6_file\n    - core module:\n        - read-string, slurp functions\n    - define eval and load-file functions\n    - set *ARGV*\n    - if files on command line, use load-file to run first argument\n      using rest as arguments\n    - Details:\n        - cp step5_tco.EXT to step6_file.EXT\n        - if compiled update Makefile\n        - add eval to repl_env\n            - if no (or limited closures) may have to add an \"eval\"\n              case to EVAL and use function which gets root of\n              environment to env.EXT (see rust).\n        - add empty *ARGV* list to repl_env\n        - in core.ns:\n            - wrap printer.read-str as read-string\n            - implement slurp\n        - implement load-file using rep\n        - test:\n            (load-file \"../tests/inc.mal\")\n            (inc3 10)\n        - implement command line execution\n        - test:\n            ./step6_file ../tests/incA.mal\n            =>9\n        - implement comments in reader.EXT (ignore in tokenize)\n\n- step7_quote\n    - add is_pair and quasiquote functions\n        - rewrite ast using cons/concat functions\n        - if vectors, use sequential? instead of list? in is_pair\n    - EVAL:\n        - add 'quote', 'quasiquote' cases\n    - core module:\n        - add cons and concat functions\n    - reader module:\n        - add reader macros to read_form for quote, unquote,\n          splice-unquote and quasiquote\n    - Details:\n        - cp step6_file.EXT to step6_quote.EXT\n        - if compiled update Makefile\n        - implement reader macros (', `, ~, ~@) in reader\n        - retest make test^go^step1\n        - add is_pair and quasiquote\n        - add quote and quasiquote cases to EVAL\n        - implement cons and concat in core.EXT\n        - retest test^go^step7\n\n- step8_macros\n    - types\n        - capability to store ismacro property in function\n    - core module:\n        - add first, rest, nth functions\n    - add is_macro_call and macroexpand\n        - recursively macroexpand lists\n        - if applying a macro function, run it on the ast first before\n          continuing\n    - call macroexpand apply in EVAL before apply\n    - EVAL:\n        - add 'defmacro!' and 'macroexpand'\n        - set ismacro property on function\n    - Details:\n        - cp step7_quote.EXT to step8_macros.EXT\n        - if compiled update Makefile\n        - add isMacro property to Mal Function type\n            - may need to go back and adjust step5-7\n        - implement is_macro_call and macroexpand\n        - call macroexpand on ast before apply in EVAL\n        - add defmacro! and macroexpand to EVAL switch\n        - make test^go^step8 should pass some basic macros\n        - add nth, first, and rest to core.ns\n        - make test^go^step8 should now pass\n\n- step9_try\n    - core module:\n        - throw function\n        - apply, map functions: should not directly call EVAL, which\n          requires the function object to be runnable\n        - readline\n        - nil?, true?, false? \n    - EVAL:\n        - try*/catch*: for normal exceptions, extracts string\n          otherwise extracts full value\n    - set and print *host-language*\n    - define cond and or macros using REP/RE\n    - Details:\n        - cp step8_macros.EXT to stepA_try.EXT\n        - if compiled update Makefile\n        - core.ns implement nil?, true?, false?, symbol?, sequential?,\n          vector, vector?\n        - add mal error type which wraps normal mal type\n            - in core.ns add throw which wraps type in mal error type\n              and throws/raises/sets exception\n        - add try*/catch* support to EVAL\n            - if mal error type, bind to catch* bind symbol\n            - otherwise, bind string of error to catch* bind symbol\n        - implement apply, map in core.ns\n        - make test^go^stepA\n        - implement readline.EXT\n            - provide option (e.g. commented out) to link with GNU\n              readline (GPL) or libedit (BSD)\n        - add hash-map functions: hash-map, map?, assoc, dissoc, get,\n          contains?, keys, vals\n        - add metadata support to List, Vector, HashMap, and Functions\n            - add reader macro\n            - may need to box HashMap and native functions\n        - add atom type, reader macro and functions: with_meta, meta\n        - get `make test^go^stepA` to fully pass\n        - get `./stepA_try ../mal/step1_read_print` to pass\n            - continue for each mal step until ../mal/stepA_try\n            - Now self-hosting!\n\n\n- Extra definitions needed for self-hosting\n    - core module:\n        - symbol?, sequential? (if not already)\n        - vector, vector?\n\n\n- atoms\n    - reader module:\n        - @a reader macro -> (deref a)\n    - core module:\n        - pr_str case\n        - atom type, atom, atom?, deref, reset!, swap!\n\n- metadata\n    - reader module:\n        - ^ reader macro reads ^meta obj -> (with-meta obj meta)\n    - types module:\n        - support meta property on collections: lists, vectors,\n          hash-maps, functions, atoms\n            - clone/copy of collections\n    - core module:\n        - add with-meta, meta functions\n\n- Other misc:\n    - conj function\n\n- stepA_mal\n    - convert returned data to mal data\n        - recursive, similar to pr_str\n    - Details:\n\n"
  },
  {
    "path": "docs/web/ansi.css",
    "content": ".jqconsole-ansi-bold {\n  font-weight: bold!important;\n}\n\n.jqconsole-ansi-lighter {\n  font-weight: lighter!important;\n}\n\n.jqconsole-ansi-italic {\n  font-style: italic!important;\n}\n\n.jqconsole-ansi-underline {\n  text-decoration: underline!important;\n}\n\n@-webkit-keyframes blinker {  \n  from { opacity: 1.0; }\n  to { opacity: 0.0; }\n}\n\n@-moz-keyframes blinker {  \n  from { opacity: 1.0; }\n  to { opacity: 0.0; }\n}\n\n@-ms-keyframes blinker {  \n  from { opacity: 1.0; }\n  to { opacity: 0.0; }\n}\n\n@-o-keyframes blinker {  \n  from { opacity: 1.0; }\n  to { opacity: 0.0; }\n}\n\n.jqconsole-ansi-blink {\n  -webkit-animation-name: blinker;\n  -moz-animation-name: blinker;\n  -ms-animation-name: blinker;\n  -o-animation-name: blinker;\n  -webkit-animation-iteration-count: infinite;\n  -moz-animation-iteration-count: infinite;\n  -ms-animation-iteration-count: infinite; \n  -o-animation-iteration-count: infinite; \n  -webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -o-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -webkit-animation-duration: 1s; \n  -moz-animation-duration: 1s;\n  -o-animation-duration: 1s;\n  -ms-animation-duration: 1s;\n}\n\n.jqconsole-ansi-blink-rapid {\n  -webkit-animation-name: blinker;\n  -moz-animation-name: blinker;\n  -ms-animation-name: blinker;\n  -o-animation-name: blinker;\n  -webkit-animation-iteration-count: infinite;\n  -moz-animation-iteration-count: infinite;\n  -ms-animation-iteration-count: infinite; \n  -o-animation-iteration-count: infinite; \n  -webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -o-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0);\n  -webkit-animation-duration: 0.5s; \n  -moz-animation-duration: 0.5s;\n  -o-animation-duration: 0.5s;\n  -ms-animation-duration: 0.5s; \n}\n\n\n.jqconsole-ansi-hidden {\n  visibility:hidden!important;\n}\n\n.jqconsole-ansi-line-through {\n  text-decoration: line-through;\n}\n\n.jqconsole-ansi-fonts-1 {\n  \n}\n.jqconsole-ansi-fonts-2 {\n  \n}\n.jqconsole-ansi-fonts-3 {\n  \n}\n.jqconsole-ansi-fonts-4 {\n  \n}\n.jqconsole-ansi-fonts-5 {\n  \n}\n.jqconsole-ansi-fonts-6 {\n  \n}\n.jqconsole-ansi-fonts-7 {\n  \n}\n.jqconsole-ansi-fonts-8 {\n  \n}\n.jqconsole-ansi-fonts-9 {\n  \n}\n\n.jqconsole-ansi-fraktur {\n  \n}\n\n.jqconsole-ansi-color-black {\n  color: black!important;\n}\n.jqconsole-ansi-color-red {\n  color: red!important;\n}\n.jqconsole-ansi-color-green {\n  color: green!important;\n}\n.jqconsole-ansi-color-yellow {\n  color: yellow!important;\n}\n.jqconsole-ansi-color-blue {\n  color: blue!important;\n}\n.jqconsole-ansi-color-magenta {\n  color: magenta!important;\n}\n.jqconsole-ansi-color-cyan {\n  color: cyan!important;\n}\n.jqconsole-ansi-color-white {\n  color: white!important;\n}\n\n.jqconsole-ansi-background-color-black {\n  background-color: black!important;\n}\n.jqconsole-ansi-background-color-red {\n  background-color: red!important;\n}\n.jqconsole-ansi-background-color-green {\n  background-color: green!important;\n}\n.jqconsole-ansi-background-color-yellow {\n  background-color: yellow!important;\n}\n.jqconsole-ansi-background-color-blue {\n  background-color: blue!important;\n}\n.jqconsole-ansi-background-color-magenta {\n  background-color: magenta!important;\n}\n.jqconsole-ansi-background-color-cyan {\n  background-color: cyan!important;\n}\n.jqconsole-ansi-background-color-white {\n  background-color: white!important;\n}\n\n.jqconsole-ansi-framed {\n  border: 1px solid!important;\n}\n.jqconsole-ansi-overline {\n  text-decoration: overline!important;\n}\n\n"
  },
  {
    "path": "docs/web/base.css",
    "content": "/* \n* Skeleton V1.0.2\n* Copyright 2011, Dave Gamache\n* www.getskeleton.com\n* Free to use under the MIT license.\n* http://www.opensource.org/licenses/mit-license.php\n* 5/20/2011\n*/\n\n\n/* Table of Content\n==================================================\n\t#Reset & Basics\n\t#Basic Styles\n\t#Site Styles\n\t#Typography\n\t#Links\n\t#Lists\n\t#Images\n\t#Buttons\n\t#Tabs\n\t#Forms\n\t#Misc */\n\n\n/* #Reset & Basics (Inspired by E. Meyers)\n================================================== */\n\thtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t\tborder: 0;\n\t\tfont-size: 100%;\n\t\tfont: inherit;\n\t\tvertical-align: baseline; }\n\tarticle, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {\n\t\tdisplay: block; }\n\tbody {\n\t\tline-height: 1; }\n\tol, ul {\n\t\tlist-style: none; }\n\tblockquote, q {\n\t\tquotes: none; }\n\tblockquote:before, blockquote:after,\n\tq:before, q:after {\n\t\tcontent: '';\n\t\tcontent: none; }\n\ttable {\n\t\tborder-collapse: collapse;\n\t\tborder-spacing: 0; }\n\t\t\n\t\t\n/* #Basic Styles\n================================================== */\n\tbody { \n\t\tbackground: #ebe7d7 url(bg-body.png);\n\t\tfont: 14px/21px \"HelveticaNeue\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\t\tcolor: #444; \n\t\t-webkit-font-smoothing: antialiased; /* Fix for webkit rendering */\n }\n\t\n\n/* #Typography\n================================================== */\n\th1, h2, h3, h4, h5, h6 { \n\t\tfont-weight: normal; }\n\th1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; }\n\th1 {\n\t  font-size: 56px;\n\t  line-height: 50px;\n\t  font-family: \"ExoBlack\";\n\t  text-transform: uppercase;\n\t  color: #8f4732;\n\t  margin-bottom: 10px;\n\t  text-shadow: 1px 1px 1px #a9a17c;\n\t}\n\th2 {\n\t  font-size: 18px;\n\t  line-height: 40px;\n\t  margin-top: -45px;\n\t  font-family: \"ExoBold\";\n\t  color: #327a8e;\n\t  float: right;\n\t}\n\th3 {\n\t  font-size: 24px; \n\t  line-height: 34px;\n\t  margin-top: 20px;\n\t  margin-left: 10px;\n\t  font-family: \"ExoBold\";\n\t  color: #327a8e;\n\t}\n\th4 {\n\t  font-size: 18px;\n\t  line-height: 30px;\n\t  margin-bottom: 4px;\n\t  font-family: \"ExoBold\";\n\t  color: #444;\n\t}\n\th5 { font-size: 17px; line-height: 24px; }\n\th6 { font-size: 14px; line-height: 21px; }\n\t.subheader { color: #777; }\n\n\tp { margin: 0 0 20px 0; }\n\tp img { margin: 0; }\n\tp.lead { font-size: 21px; line-height: 27px; color: #777;  }\n\t\n\tem { font-style: italic; }\n\tstrong { font-weight: bold; color: #333; }\n\tsmall { font-size: 80%; }\n\t\n/*\tBlockquotes  */\n\tblockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; }\n\tblockquote { margin: 0 0 20px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; }\n\tblockquote cite { display: block; font-size: 12px; color: #555; }\n\tblockquote cite:before { content: \"\\2014 \\0020\"; }\n\tblockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; }\n\t\n\thr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 10px 0 30px; height: 0; }\n\n\n/* #Links\n================================================== */\n\ta, a:visited { color: #333; text-decoration: underline; outline: 0; }\n\ta:hover, a:focus { color: #000; }\n\tp a, p a:visited { line-height: inherit; }\n\t\n\n/* #Lists\n================================================== */\n\tul, ol { margin-bottom: 20px; }\n\tul { list-style: none outside; }\n\tol { list-style: decimal; }\n\tol, ul.square, ul.circle, ul.disc { margin-left: 30px; }\n\tul.square { list-style: square outside; }\n\tul.circle { list-style: circle outside; }\n\tul.disc { list-style: disc outside; }\n\tul ul, ul ol,\n\tol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%;  }\n\tul ul li, ul ol li,\n\tol ol li, ol ul li { margin-bottom: 6px; }\n\tli { line-height: 18px; margin-bottom: 12px; }\n\tul.large li { line-height: 21px; }\n\tli p { line-height: 21px; }\n\t\n/* #Images\n================================================== */\n/*\n\tThe purpose of the below declaration is to make sure images don't \n\texceed the width of columns they are put into when resizing window. \n\tUnfortunately, this declaration breaks certain lightbox, slider or other plugins,\n\tso the best solution is to individually call these properties on images that \n\tare children of the grid that you want to resize with grid. \n\n\timg { \n\t\tmax-width: 100%;\n\t\theight: auto; }\n\t\t\n*/\n\t\t\t\n\t\t\t\n/* #Forms\n================================================== */\n\n\tform { \n\t\tmargin-bottom: 20px; }\n\tfieldset { \n\t\tmargin-bottom: 20px; }\n\tinput[type=\"text\"], \n\tinput[type=\"password\"],\n\tinput[type=\"email\"],\n\ttextarea, \n\tselect {\n\t\tborder: 1px solid #ccc;\n\t\tpadding: 6px 4px;\n\t\toutline: none;\n\t\t-moz-border-radius: 2px;\n\t\t-webkit-border-radius: 2px;\n\t\tborder-radius: 2px;\n\t\tfont: 13px \"HelveticaNeue\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\t\tcolor: #777;\n\t\tmargin: 0;\n\t\twidth: 210px;\n\t\tmax-width: 100%;\n\t\tdisplay: block;\n\t\tmargin-bottom: 20px;\n\t\tbackground: #fff; }\n\tselect { \n\t\tpadding: 0; }\n\tinput[type=\"text\"]:focus,\n\tinput[type=\"password\"]:focus,\n\tinput[type=\"email\"]:focus, \n\ttextarea:focus {\n\t\tborder: 1px solid #aaa;\n \t\tcolor: #444;\n \t\t-moz-box-shadow: 0 0 3px rgba(0,0,0,.2);\n\t\t-webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);\n\t\tbox-shadow:  0 0 3px rgba(0,0,0,.2); }\n\ttextarea {\n\t\tmin-height: 60px; }\n\tlabel,\n\tlegend { \n\t\tdisplay: block;\n\t\tfont-weight: bold;\n\t\tfont-size: 13px;  }\n\tselect { \n\t\twidth: 220px; }\n\tinput[type=\"checkbox\"] {\n\t\tdisplay: inline; }\n\tlabel span,\n\tlegend span {\n\t\tfont-weight: normal;\n\t\tfont-size: 13px;\n\t\tcolor: #444; }\n\t\t\n/* #Misc\n================================================== */\n\t.remove-bottom { margin-bottom: 0 !important; }\n\t.half-bottom { margin-bottom: 10px !important; }\n\t.add-bottom { margin-bottom: 20px !important; }\n\n\t\t\n\t\n"
  },
  {
    "path": "docs/web/console.css",
    "content": "/* Outer console element */\n#console {\n}\n/* The inner console element. */\n.jqconsole {\n  background-color: black;;\n}\n.jqconsole-prompt {\n  color: #0d0;\n}\n.jqconsole-old-prompt {\n  color: #0b0;\n  font-weight: normal;\n}\n.jqconsole-input {\n  color: #dd0;\n}\n.jqconsole-old-input {\n  color: #bb0;\n  font-weight: normal;\n}\n.jqconsole-output {\n  font-weight: lighter;\n  font-family:monospace;\n  color: grey;\n}\n.jqconsole-return {\n  font-weight: normal;\n  font-family:monospace;\n  color: white;\n}\n.jqconsole-error {\n  font-weight: normal;\n  font-family:monospace;\n  color: red;\n}\n/* The cursor. */\n.jqconsole-cursor {\n  font-weight: normal;\n  font-family:monospace;\n  background-color: #BDB;\n}\n/* The cursor color when the console looses focus. */\n.jqconsole-blurred .jqconsole-cursor {\n  font-weight: normal;\n  font-family:monospace;\n  background-color: #444;\n}\n.brace {\n  color: #00FFFF;\n}\n.paren {\n  color: #FF00FF;\n}\n.bracket {\n  color: #FFFF00;\n}\n.dquote {\n  color: #FF8888;\n}\n.jqconsole-composition {\n  background-color: red;\n}\n"
  },
  {
    "path": "docs/web/himera.css",
    "content": "/* Additional Classes  --------------------------------------------- */\n\n.source {\n  font-family: \"ExoRegular\";\n  font-size: 18px;\n  height: 31px;\n  margin-top: 20px;\n  text-align: right;\n}\n\n.source img {\n  margin-left: 5px;\n  vertical-align: sub;\n}\n\n.source a {\n  text-decoration: none;\n}\n\n.rule {\n  background: url(bg-rule.png);\n  height: 12px;\n  clear: both;\n  margin-top: 20px;\n  margin-bottom: 20px;\n}\n\n.cheat-box-container {\n  background-color: rgba(213,207,180,0.4);\n  border-radius: 5px;\n  -moz-border-radius: 5px;\n  margin-top: 20px;\n}\n\n.cheat-box {\n  padding: 20px;\n}\n\ntable {\n  font-family: monospace;\n  margin-top: 20px;\n  width: 100%;\n}\n\ntd {\n  padding: 3px;\n}\n\n.row-one {\n  background-color: #f2efe4;\n}\n\n.row-label {\n  color: #666;\n  font-family: \"ExoBold\";\n  text-transform: uppercase;\n}\n\n.footer-logo {\n  font-size: 18px;\n  font-family: \"ExoBlack\";\n  text-transform: uppercase;\n  color: #8f4732;\n}\n\nul.footer-links {\n  float: right;\n}\n\n.footer-links li {\n  font-family: \"ExoRegular\";\n  font-size: 14px;\n  display: inline;\n  list-style-type: none;\n  margin-left: 10px;\n  text-transform: uppercase;\n}\n\n.footer-links li a {\n  color: #327a8e;\n  text-decoration: none;\n}\n\n.footer-links li a:hover {\n  color: #333;\n}\n\n.tiny-note {\n  font-size: small;\n}\n\n/* Editor ---------------------------------------------- */\n\n.CodeMirror {\n  position: relative;\n  height: 320px;\n  background: #fbfbf8;\n  border-radius: 5px;\n  -moz-border-radius: 5px;\n  border: 1px solid #d5ceb4;\n}\n\n/* Console --------------------------------------------- */\n\n#console {\n  position: relative;\n  height: 220px;\n  background: #fbfbf8;\n  border-radius: 5px;\n  -moz-border-radius: 5px;\n  border: 1px solid #d5ceb4;\n}\n\n/* Console --------------------------------------------- */\n/* The console container element */\n#console {\n  position: relative;\n  height: 320px;\n  background-color:#fbfbf8;;\n}\n/* The inner console element. */\n.jqconsole {\n    background: #fbfbf8;\n    border-radius: 5px;\n    -moz-border-radius: 5px;\n    border: 1px solid #d5ceb4;\n    padding: 10px;\n\n    white-space: pre-wrap;\n    word-wrap: break-word;\n}\n/* The cursor. */\n.jqconsole-cursor {\n    font-weight: normal;\n    font-family:monospace;\n    background-color: #000;\n}\n/* The cursor color when the console looses focus. */\n.jqconsole-blurred .jqconsole-cursor {\n    font-weight: normal;\n    font-family:monospace;\n    background-color: #7F7F7F;\n}\n/* The current prompt text color */\n.jqconsole-prompt {\n    font-weight: normal;\n    font-family:monospace;\n    color: #000;\n}\n/* The command history */\n.jqconsole-old-prompt {\n    font-weight: normal;\n    font-family:monospace;\n    color: #000;\n}\n/* The text color when in input mode. */\n.jqconsole-input {\n    font-weight: normal;\n    font-family:monospace;\n    color: #000;\n}\n/* Previously entered input. */\n.jqconsole-old-input {\n    color: #000;\n    font-weight: normal;\n    font-family:monospace;\n}\n/* The text color of the output. */\n.jqconsole-output {\n  font-weight: normal;\n  font-family:monospace;\n  color: #000;\n}\n\n.jqconsole-inner { \n  /*width:580px;*/\n  height:200px; \n  margin: 10px 10px;\n  overflow:auto; \n  text-align:left;\n}\n\n.jqconsole-message-value {\n  color:#333; \n  font-family:monospace;\n  padding:0.1em; \n}\n\n.jqconsole-prompt-box {\n  color:#444; font-family:monospace; \n}\n\n.jqconsole-focus span.jquery-console-cursor {\n  background:#333; color:#eee; font-weight:bold;\n}\n\n.jqconsole-message-error {\n  font-family:sans-serif;\n  font-weight:bold;\n  padding:0.1em; \n  color:#ef0505;\n}\n\n.jqconsole-message-success {\n  color:#187718; font-family:monospace;\n  padding:0.1em; \n}\n\n\n.ebnf {\n  color:#444; font-family:monospace;\n  text-transform: uppercase;\n}\n\n.doc-link {\n  font-size: 0.65em;\n  text-decoration: none;\n}\n\n/* Synonym Styles */\n\n#himera-synonym h1 {\n    margin-left: 10px;\n}\n\n#himera-synonym h1 div {\n    font-size: 16px;\n    color: black;\n}\n\n#himera-synonym h4,\n#himera-synonym h5 {\n    margin-left: 10px;\n}\n\n#himera-synonym .cheat-box-container {\n    margin-top: 0px;\n    background-color: rgb(255, 250, 240);\n    border: 1px solid #ccc;\n    box-sizing: border-box;\n    -webkit-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n    -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n    -o-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n    -ms-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n}\n\n#himera-synonym pre {\n    font-size: 13px;\n    font-family: monospace;\n}\n\n.syn-section {\n    clear: both;\n    float: left;\n    margin-bottom: 20px;\n}\n\n#himera-synonym table {\n    margin: 0;\n    padding: 0;\n}\n\n#himera-synonym .container {\n    margin: 0;\n    overflow-x: hidden !important;\n    overflow-y: hidden !important;\n}\n\nh1 a {\n    text-decoration: none;\n    color: inherit;\n}\n\nh1 a:visited {\n    text-decoration: none;\n    color: inherit;\n}\n"
  },
  {
    "path": "docs/web/layout.css",
    "content": "/* \n* Skeleton V1.0.2\n* Copyright 2011, Dave Gamache\n* www.getskeleton.com\n* Free to use under the MIT license.\n* http://www.opensource.org/licenses/mit-license.php\n* 5/20/2011\n*/\n\n/* Table of Content\n==================================================\n\t#Site Styles\n\t#Page Styles\n\t#Media Queries\n\t#Font-Face */\n\t\n/* #Site Styles\n================================================== */\n\n/* #Page Styles\n================================================== */\n\n/* #Media Queries\n================================================== */\n\t\n\t/* iPad Portrait/Browser */\n\t@media only screen and (min-width: 768px) and (max-width: 991px) {}\n\t\n\t/* Mobile/Browser */\n\t@media only screen and (max-width: 767px) {}\n\t\n\t/* Mobile Landscape/Browser */\n\t@media only screen and (min-width: 480px) and (max-width: 767px) {}\n\t\n\t/* Anything smaller than standard 960 */\n\t@media only screen and (max-width: 959px) {\n\t  h1 {\n\t    font-size: 48px;\n\t  }\n\t  h2 {\n\t    font-size: 16px;\n\t    float: none;\n\t    line-height: 150%;\n\t    margin-top: -10px;\n\t  }\n\t  h3 {\n\t    font-size: 18px;\n\t  }\n\t  h4 {\n\t    font-size: 14px;\n\t  }\n\t  .source p {\n\t    font-size: 16px;\n\t  }\n\t}\n\t\n\t/* iPad Portrait Only */\n\t@media only screen and (min-width: 768px) and (max-width: 991px) and (max-device-width: 1000px) {}\n\t\n\t/* Mobile Only */\n\t@media only screen and (max-width: 767px) and (max-device-width: 1000px) {}\n\t\n\t/* Mobile Landscape Only */\n\t@media only screen and (min-width: 480px) and (max-width: 767px) and (max-device-width: 1000px) {}\n\t\n\n/* Fonts --------------------------------------------- */\n\n@font-face {\n    font-family: 'ExoBold';\n    src: url('fonts/exo-bold-webfont.eot');\n    src: url('fonts/exo-bold-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/exo-bold-webfont.woff') format('woff'),\n         url('fonts/exo-bold-webfont.ttf') format('truetype'),\n         url('fonts/exo-bold-webfont.svg#ExoBold') format('svg');\n    font-weight: normal;\n    font-style: normal;\n}\n\n@font-face {\n    font-family: 'ExoBlack';\n    src: url('fonts/exo-black-webfont.eot');\n    src: url('fonts/exo-black-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/exo-black-webfont.woff') format('woff'),\n         url('fonts/exo-black-webfont.ttf') format('truetype'),\n         url('fonts/exo-black-webfont.svg#ExoBlack') format('svg');\n    font-weight: normal;\n    font-style: normal;\n}\n\n@font-face {\n    font-family: 'ExoRegular';\n    src: url('fonts/exo-regular-webfont.eot');\n    src: url('fonts/exo-regular-webfont.eot?#iefix') format('embedded-opentype'),\n         url('fonts/exo-regular-webfont.woff') format('woff'),\n         url('fonts/exo-regular-webfont.ttf') format('truetype'),\n         url('fonts/exo-regular-webfont.svg#ExoRegular') format('svg');\n    font-weight: normal;\n    font-style: normal;\n}\n"
  },
  {
    "path": "docs/web/mal.js",
    "content": "var max_history_length = 1000;\n\nfunction jq_load_history(jq) {\n    if (localStorage['mal_history']) {\n        var lines = JSON.parse(localStorage['mal_history']);\n        if (lines.length > max_history_length) {\n            lines = lines.slice(lines.length-max_history_length);\n        }\n        jq.SetHistory(lines);\n    }\n}\n\nfunction jq_save_history(jq) {\n    var lines = jq.GetHistory();\n    localStorage['mal_history'] = JSON.stringify(lines);\n}\n\n\nvar readline = {\n    'readline': function(prompt_str) {\n            return prompt(prompt_str);\n        }};\n\n// Node vs browser behavior\nvar types = {};\nif (typeof module === 'undefined') {\n    var exports = types;\n}\n\n// General functions\n\nfunction _obj_type(obj) {\n    if      (_symbol_Q(obj)) {   return 'symbol'; }\n    else if (_list_Q(obj)) {     return 'list'; }\n    else if (_vector_Q(obj)) {   return 'vector'; }\n    else if (_hash_map_Q(obj)) { return 'hash-map'; }\n    else if (_nil_Q(obj)) {      return 'nil'; }\n    else if (_true_Q(obj)) {     return 'true'; }\n    else if (_false_Q(obj)) {    return 'false'; }\n    else if (_atom_Q(obj)) {     return 'atom'; }\n    else {\n        switch (typeof(obj)) {\n        case 'number':   return 'number';\n        case 'function': return 'function';\n        case 'string': return obj[0] == '\\u029e' ? 'keyword' : 'string';\n        default: throw new Error(\"Unknown type '\" + typeof(obj) + \"'\");\n        }\n    }\n}\n\nfunction _sequential_Q(lst) { return _list_Q(lst) || _vector_Q(lst); }\n\n\nfunction _equal_Q (a, b) {\n    var ota = _obj_type(a), otb = _obj_type(b);\n    if (!(ota === otb || (_sequential_Q(a) && _sequential_Q(b)))) {\n        return false;\n    }\n    switch (ota) {\n    case 'symbol': return a.value === b.value;\n    case 'list':\n    case 'vector':\n        if (a.length !== b.length) { return false; }\n        for (var i=0; i<a.length; i++) {\n            if (! _equal_Q(a[i], b[i])) { return false; }\n        }\n        return true;\n    case 'hash-map':\n        if (Object.keys(a).length !== Object.keys(b).length) { return false; }\n        for (var k in a) {\n            if (! _equal_Q(a[k], b[k])) { return false; }\n        }\n        return true;\n    default:\n        return a === b;\n    }\n}\n\n\nfunction _clone (obj) {\n    var new_obj;\n    switch (_obj_type(obj)) {\n    case 'list':\n        new_obj = obj.slice(0);\n        break;\n    case 'vector':\n        new_obj = obj.slice(0);\n        new_obj.__isvector__ = true;\n        break;\n    case 'hash-map':\n        new_obj = {};\n        for (var k in obj) {\n            if (obj.hasOwnProperty(k)) { new_obj[k] = obj[k]; }\n        }\n        break;\n    case 'function':\n        new_obj = obj.clone();\n        break;\n    default:\n        throw new Error(\"clone of non-collection: \" + _obj_type(obj));\n    }\n    Object.defineProperty(new_obj, \"__meta__\", {\n        enumerable: false,\n        writable: true\n    });\n    return new_obj;\n}\n\n\n// Scalars\nfunction _nil_Q(a) { return a === null ? true : false; }\nfunction _true_Q(a) { return a === true ? true : false; }\nfunction _false_Q(a) { return a === false ? true : false; }\nfunction _number_Q(obj) { return typeof obj === 'number'; }\nfunction _string_Q(obj) {\n    return typeof obj === 'string' && obj[0] !== '\\u029e';\n}\n\n\n// Symbols\nfunction Symbol(name) {\n    this.value = name;\n    return this;\n}\nSymbol.prototype.toString = function() { return this.value; }\nfunction _symbol(name) { return new Symbol(name); }\nfunction _symbol_Q(obj) { return obj instanceof Symbol; }\n\n\n// Keywords\nfunction _keyword(obj) {\n    if (typeof obj === 'string' && obj[0] === '\\u029e') {\n        return obj;\n    } else {\n        return \"\\u029e\" + obj;\n    }\n}\nfunction _keyword_Q(obj) {\n    return typeof obj === 'string' && obj[0] === '\\u029e';\n}\n\n\n// Functions\nfunction _function(Eval, Env, ast, env, params) {\n    var fn = function() {\n        return Eval(ast, new Env(env, params, arguments));\n    };\n    fn.__meta__ = null;\n    fn.__ast__ = ast;\n    fn.__gen_env__ = function(args) { return new Env(env, params, args); };\n    fn._ismacro_ = false;\n    return fn;\n}\nfunction _function_Q(obj) { return typeof obj == \"function\"; }\nFunction.prototype.clone = function() {\n    var that = this;\n    var temp = function () { return that.apply(this, arguments); };\n    for( key in this ) {\n        temp[key] = this[key];\n    }\n    return temp;\n};\nfunction _fn_Q(obj) { return _function_Q(obj) && !obj._ismacro_; }\nfunction _macro_Q(obj) { return _function_Q(obj) && !!obj._ismacro_; }\n\n\n// Lists\nfunction _list() { return Array.prototype.slice.call(arguments, 0); }\nfunction _list_Q(obj) { return Array.isArray(obj) && !obj.__isvector__; }\n\n\n// Vectors\nfunction _vector() {\n    var v = Array.prototype.slice.call(arguments, 0);\n    v.__isvector__ = true;\n    return v;\n}\nfunction _vector_Q(obj) { return Array.isArray(obj) && !!obj.__isvector__; }\n\n\n\n// Hash Maps\nfunction _hash_map() {\n    if (arguments.length % 2 === 1) {\n        throw new Error(\"Odd number of hash map arguments\");\n    }\n    var args = [{}].concat(Array.prototype.slice.call(arguments, 0));\n    return _assoc_BANG.apply(null, args);\n}\nfunction _hash_map_Q(hm) {\n    return typeof hm === \"object\" &&\n           !Array.isArray(hm) &&\n           !(hm === null) &&\n           !(hm instanceof Symbol) &&\n           !(hm instanceof Atom);\n}\nfunction _assoc_BANG(hm) {\n    if (arguments.length % 2 !== 1) {\n        throw new Error(\"Odd number of assoc arguments\");\n    }\n    for (var i=1; i<arguments.length; i+=2) {\n        var ktoken = arguments[i],\n            vtoken = arguments[i+1];\n        if (typeof ktoken !== \"string\") {\n            throw new Error(\"expected hash-map key string, got: \" + (typeof ktoken));\n        }\n        hm[ktoken] = vtoken;\n    }\n    return hm;\n}\nfunction _dissoc_BANG(hm) {\n    for (var i=1; i<arguments.length; i++) {\n        var ktoken = arguments[i];\n        delete hm[ktoken];\n    }\n    return hm;\n}\n\n\n// Atoms\nfunction Atom(val) { this.val = val; }\nfunction _atom(val) { return new Atom(val); }\nfunction _atom_Q(atm) { return atm instanceof Atom; }\n\n\n// Exports\nexports._obj_type = types._obj_type = _obj_type;\nexports._sequential_Q = types._sequential_Q = _sequential_Q;\nexports._equal_Q = types._equal_Q = _equal_Q;\nexports._clone = types._clone = _clone;\nexports._nil_Q = types._nil_Q = _nil_Q;\nexports._true_Q = types._true_Q = _true_Q;\nexports._false_Q = types._false_Q = _false_Q;\nexports._number_Q = types._number_Q = _number_Q;\nexports._string_Q = types._string_Q = _string_Q;\nexports._symbol = types._symbol = _symbol;\nexports._symbol_Q = types._symbol_Q = _symbol_Q;\nexports._keyword = types._keyword = _keyword;\nexports._keyword_Q = types._keyword_Q = _keyword_Q;\nexports._function = types._function = _function;\nexports._function_Q = types._function_Q = _function_Q;\nexports._fn_Q = types._fn_Q = _fn_Q;\nexports._macro_Q = types._macro_Q = _macro_Q;\nexports._list = types._list = _list;\nexports._list_Q = types._list_Q = _list_Q;\nexports._vector = types._vector = _vector;\nexports._vector_Q = types._vector_Q = _vector_Q;\nexports._hash_map = types._hash_map = _hash_map;\nexports._hash_map_Q = types._hash_map_Q = _hash_map_Q;\nexports._assoc_BANG = types._assoc_BANG = _assoc_BANG;\nexports._dissoc_BANG = types._dissoc_BANG = _dissoc_BANG;\nexports._atom = types._atom = _atom;\nexports._atom_Q = types._atom_Q = _atom_Q;\n// Node vs browser behavior\nvar reader = {};\nif (typeof module !== 'undefined') {\n} else {\n    var exports = reader;\n}\n\nfunction Reader(tokens) {\n    // copy\n    this.tokens = tokens.map(function (a) { return a; });\n    this.position = 0;\n}\nReader.prototype.next = function() { return this.tokens[this.position++]; }\nReader.prototype.peek = function() { return this.tokens[this.position]; }\n\nfunction tokenize(str) {\n    var re = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g;\n    var results = [];\n    while ((match = re.exec(str)[1]) != '') {\n        if (match[0] === ';') { continue; }\n        results.push(match);\n    }\n    return results;\n}\n\nfunction read_atom (reader) {\n    var token = reader.next();\n    //console.log(\"read_atom:\", token);\n    if (token.match(/^-?[0-9]+$/)) {\n        return parseInt(token,10)        // integer\n    } else if (token.match(/^-?[0-9][0-9.]*$/)) {\n        return parseFloat(token,10);     // float\n    } else if (token[0] === \"\\\"\") {\n        if (token.slice(-1) !== \"\\\"\") {\n            throw new Error(\"expected '\\\"', got EOF\");\n        }\n        return token.slice(1,token.length-1) \n            .replace(/\\\\(.)/g, function (_, c) { return c === \"n\" ? \"\\n\" : c})\n    } else if (token[0] === \":\") {\n        return types._keyword(token.slice(1));\n    } else if (token === \"nil\") {\n        return null;\n    } else if (token === \"true\") {\n        return true;\n    } else if (token === \"false\") {\n        return false;\n    } else {\n        return types._symbol(token); // symbol\n    }\n}\n\n// read list of tokens\nfunction read_list(reader, start, end) {\n    start = start || '(';\n    end = end || ')';\n    var ast = [];\n    var token = reader.next();\n    if (token !== start) {\n        throw new Error(\"expected '\" + start + \"'\");\n    }\n    while ((token = reader.peek()) !== end) {\n        if (!token) {\n            throw new Error(\"expected '\" + end + \"', got EOF\");\n        }\n        ast.push(read_form(reader));\n    }\n    reader.next();\n    return ast;\n}\n\n// read vector of tokens\nfunction read_vector(reader) {\n    var lst = read_list(reader, '[', ']');\n    return types._vector.apply(null, lst);\n}\n\n// read hash-map key/value pairs\nfunction read_hash_map(reader) {\n    var lst = read_list(reader, '{', '}');\n    return types._hash_map.apply(null, lst);\n}\n\nfunction read_form(reader) {\n    var token = reader.peek();\n    switch (token) {\n    // reader macros/transforms\n    case ';': return null; // Ignore comments\n    case '\\'': reader.next();\n               return [types._symbol('quote'), read_form(reader)];\n    case '`': reader.next();\n              return [types._symbol('quasiquote'), read_form(reader)];\n    case '~': reader.next();\n              return [types._symbol('unquote'), read_form(reader)];\n    case '~@': reader.next();\n               return [types._symbol('splice-unquote'), read_form(reader)];\n    case '^': reader.next();\n              var meta = read_form(reader);\n              return [types._symbol('with-meta'), read_form(reader), meta];\n    case '@': reader.next();\n              return [types._symbol('deref'), read_form(reader)];\n\n    // list\n    case ')': throw new Error(\"unexpected ')'\");\n    case '(': return read_list(reader);\n\n    // vector\n    case ']': throw new Error(\"unexpected ']'\");\n    case '[': return read_vector(reader);\n\n    // hash-map\n    case '}': throw new Error(\"unexpected '}'\");\n    case '{': return read_hash_map(reader);\n\n    // atom\n    default:  return read_atom(reader);\n    }\n}\n\nfunction BlankException(msg) {\n}\n\nfunction read_str(str) {\n    var tokens = tokenize(str);\n    if (tokens.length === 0) { throw new BlankException(); }\n    return read_form(new Reader(tokens))\n}\n\nexports.Reader = reader.Reader = Reader;\nexports.BlankException = reader.BlankException = BlankException;\nexports.tokenize = reader.tokenize = tokenize;\nexports.read_form = reader.read_form = read_form;\nexports.read_str = reader.read_str = read_str;\n// Node vs browser behavior\nvar printer = {};\nif (typeof module !== 'undefined') {\n    // map output/print to console.log\n    printer.println = exports.println = function () {\n        console.log.apply(console, arguments);\n    };\n}\n\nfunction _pr_str(obj, print_readably) {\n    if (typeof print_readably === 'undefined') { print_readably = true; }\n    var _r = print_readably;\n    var ot = types._obj_type(obj);\n    switch (ot) {\n    case 'list':\n        var ret = obj.map(function(e) { return _pr_str(e,_r); });\n        return \"(\" + ret.join(' ') + \")\";\n    case 'vector':\n        var ret = obj.map(function(e) { return _pr_str(e,_r); });\n        return \"[\" + ret.join(' ') + \"]\";\n    case 'hash-map':\n        var ret = [];\n        for (var k in obj) {\n            ret.push(_pr_str(k,_r), _pr_str(obj[k],_r));\n        }\n        return \"{\" + ret.join(' ') + \"}\";\n    case 'string':\n        if (obj[0] === '\\u029e') {\n            return ':' + obj.slice(1);\n        } else if (_r) {\n            return '\"' + obj.replace(/\\\\/g, \"\\\\\\\\\")\n                .replace(/\"/g, '\\\\\"')\n                .replace(/\\n/g, \"\\\\n\") + '\"'; // string\n        } else {\n            return obj;\n        }\n    case 'keyword':\n        return ':' + obj.slice(1);\n    case 'nil':\n        return \"nil\";\n    case 'atom':\n        return \"(atom \" + _pr_str(obj.val,_r) + \")\";\n    default:\n        return obj.toString();\n    }\n}\n\nexports._pr_str = printer._pr_str = _pr_str;\n\n// Node vs browser behavior\nvar interop = {};\nif (typeof module === 'undefined') {\n    var exports = interop,\n        GLOBAL = window;\n}\n\nfunction resolve_js(str) {\n    if (str.match(/\\./)) {\n        var re = /^(.*)\\.[^\\.]*$/,\n            match = re.exec(str);\n        return [eval(match[1]), eval(str)];\n    } else {\n        return [GLOBAL, eval(str)];\n    }\n}\n\nfunction js_to_mal(obj) {\n    if (obj === null || obj === undefined) {\n        return null;\n    }\n    var cache = [];\n    var str = JSON.stringify(obj, function(key, value) {\n        if (typeof value === 'object' && value !== null) {\n            if (cache.indexOf(value) !== -1) {\n                // Circular reference found, discard key\n                return;\n            }\n            // Store value in our collection\n            cache.push(value);\n        }\n        return value;\n    });\n    cache = null; // Enable garbage collection\n    return JSON.parse(str);\n}\n\nexports.resolve_js = interop.resolve_js = resolve_js;\nexports.js_to_mal = interop.js_to_mal = js_to_mal;\n// Node vs browser behavior\nvar env = {};\nif (typeof module === 'undefined') {\n    var exports = env;\n}\n\n// Env implementation\nfunction Env(outer, binds, exprs) {\n    this.data = {};\n    this.outer = outer || null;\n\n    if (binds && exprs) {\n        // Returns a new Env with symbols in binds bound to\n        // corresponding values in exprs\n        // TODO: check types of binds and exprs and compare lengths\n        for (var i=0; i<binds.length;i++) {\n            if (binds[i].value === \"&\") {\n                // variable length arguments\n                this.data[binds[i+1].value] = Array.prototype.slice.call(exprs, i);\n                break;\n            } else {\n                this.data[binds[i].value] = exprs[i];\n            }\n        }\n    }\n    return this;\n}\nEnv.prototype.find = function (key) {\n    if (!key.constructor || key.constructor.name !== 'Symbol') {\n        throw new Error(\"env.find key must be a symbol\")\n    }\n    if (key.value in this.data) { return this; }\n    else if (this.outer) {  return this.outer.find(key); }\n    else { return null; }\n};\nEnv.prototype.set = function(key, value) {\n    if (!key.constructor || key.constructor.name !== 'Symbol') {\n        throw new Error(\"env.set key must be a symbol\")\n    }\n    this.data[key.value] = value;\n    return value;\n};\nEnv.prototype.get = function(key) {\n    if (!key.constructor || key.constructor.name !== 'Symbol') {\n        throw new Error(\"env.get key must be a symbol\")\n    }\n    var env = this.find(key);\n    if (!env) { throw new Error(\"'\" + key.value + \"' not found\"); }\n    return env.data[key.value];\n};\n\nexports.Env = env.Env = Env;\n// Node vs browser behavior\nvar core = {};\nif (typeof module === 'undefined') {\n    var exports = core;\n} else {\n}\n\n// Errors/Exceptions\nfunction mal_throw(exc) { throw exc; }\n\n\n// String functions\nfunction pr_str() {\n    return Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, true);\n    }).join(\" \");\n}\n\nfunction str() {\n    return Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, false);\n    }).join(\"\");\n}\n\nfunction prn() {\n    printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, true);\n    }));\n}\n\nfunction println() {\n    printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, false);\n    }));\n}\n\nfunction slurp(f) {\n    if (typeof require !== 'undefined') {\n        return require('fs').readFileSync(f, 'utf-8');\n    } else {\n        var req = new XMLHttpRequest();\n        req.open(\"GET\", f, false);\n        req.send();\n        if (req.status == 200) {\n            return req.responseText;\n        } else {\n            throw new Error(\"Failed to slurp file: \" + f);\n        }\n    }\n}\n\n\n// Number functions\nfunction time_ms() { return new Date().getTime(); }\n\n\n// Hash Map functions\nfunction assoc(src_hm) {\n    var hm = types._clone(src_hm);\n    var args = [hm].concat(Array.prototype.slice.call(arguments, 1));\n    return types._assoc_BANG.apply(null, args);\n}\n\nfunction dissoc(src_hm) {\n    var hm = types._clone(src_hm);\n    var args = [hm].concat(Array.prototype.slice.call(arguments, 1));\n    return types._dissoc_BANG.apply(null, args);\n}\n\nfunction get(hm, key) {\n    if (hm != null && key in hm) {\n        return hm[key];\n    } else {\n        return null;\n    }\n}\n\nfunction contains_Q(hm, key) {\n    if (key in hm) { return true; } else { return false; }\n}\n\nfunction keys(hm) { return Object.keys(hm); }\nfunction vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); }\n\n\n// Sequence functions\nfunction cons(a, b) { return [a].concat(b); }\n\nfunction concat(lst) {\n    lst = lst || [];\n    return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));\n}\n\nfunction nth(lst, idx) {\n    if (idx < lst.length) { return lst[idx]; }\n    else                  { throw new Error(\"nth: index out of range\"); }\n}\n\nfunction first(lst) { return (lst === null) ? null : lst[0]; }\n\nfunction rest(lst) { return (lst == null) ? [] : lst.slice(1); }\n\nfunction empty_Q(lst) { return lst.length === 0; }\n\nfunction count(s) {\n    if (Array.isArray(s)) { return s.length; }\n    else if (s === null)  { return 0; }\n    else                  { return Object.keys(s).length; }\n}\n\nfunction conj(lst) {\n    if (types._list_Q(lst)) {\n        return Array.prototype.slice.call(arguments, 1).reverse().concat(lst);\n    } else {\n        var v = lst.concat(Array.prototype.slice.call(arguments, 1));\n        v.__isvector__ = true;\n        return v;\n    }\n}\n\nfunction seq(obj) {\n    if (types._list_Q(obj)) {\n        return obj.length > 0 ? obj : null;\n    } else if (types._vector_Q(obj)) {\n        return obj.length > 0 ? Array.prototype.slice.call(obj, 0): null;\n    } else if (types._string_Q(obj)) {\n        return obj.length > 0 ? obj.split('') : null;\n    } else if (obj === null) {\n        return null;\n    } else {\n        throw new Error(\"seq: called on non-sequence\");\n    }\n}\n\n\nfunction apply(f) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1]));\n}\n\nfunction map(f, lst) {\n    return lst.map(function(el){ return f(el); });\n}\n\n\n// Metadata functions\nfunction with_meta(obj, m) {\n    var new_obj = types._clone(obj);\n    new_obj.__meta__ = m;\n    return new_obj;\n}\n\nfunction meta(obj) {\n    // TODO: support symbols and atoms\n    if ((!types._sequential_Q(obj)) &&\n        (!(types._hash_map_Q(obj))) &&\n        (!(types._function_Q(obj)))) {\n        throw new Error(\"attempt to get metadata from: \" + types._obj_type(obj));\n    }\n    return obj.__meta__;\n}\n\n\n// Atom functions\nfunction deref(atm) { return atm.val; }\nfunction reset_BANG(atm, val) { return atm.val = val; }\nfunction swap_BANG(atm, f) {\n    var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2));\n    atm.val = f.apply(f, args);\n    return atm.val;\n}\n\nfunction js_eval(str) {\n    return interop.js_to_mal(eval(str.toString()));\n}\n\nfunction js_method_call(object_method_str) {\n    var args = Array.prototype.slice.call(arguments, 1),\n        r = interop.resolve_js(object_method_str),\n        obj = r[0], f = r[1];\n    var res = f.apply(obj, args);\n    return interop.js_to_mal(res);\n}\n\n// types.ns is namespace of type functions\nvar ns = {'type': types._obj_type,\n          '=': types._equal_Q,\n          'throw': mal_throw,\n          'nil?': types._nil_Q,\n          'true?': types._true_Q,\n          'false?': types._false_Q,\n          'number?': types._number_Q,\n          'string?': types._string_Q,\n          'symbol': types._symbol,\n          'symbol?': types._symbol_Q,\n          'keyword': types._keyword,\n          'keyword?': types._keyword_Q,\n          'fn?': types._fn_Q,\n          'macro?': types._macro_Q,\n\n          'pr-str': pr_str,\n          'str': str,\n          'prn': prn,\n          'println': println,\n          'readline': readline.readline,\n          'read-string': reader.read_str,\n          'slurp': slurp,\n          '<'  : function(a,b){return a<b;},\n          '<=' : function(a,b){return a<=b;},\n          '>'  : function(a,b){return a>b;},\n          '>=' : function(a,b){return a>=b;},\n          '+'  : function(a,b){return a+b;},\n          '-'  : function(a,b){return a-b;},\n          '*'  : function(a,b){return a*b;},\n          '/'  : function(a,b){return a/b;},\n          \"time-ms\": time_ms,\n\n          'list': types._list,\n          'list?': types._list_Q,\n          'vector': types._vector,\n          'vector?': types._vector_Q,\n          'hash-map': types._hash_map,\n          'map?': types._hash_map_Q,\n          'assoc': assoc,\n          'dissoc': dissoc,\n          'get': get,\n          'contains?': contains_Q,\n          'keys': keys,\n          'vals': vals,\n\n          'sequential?': types._sequential_Q,\n          'cons': cons,\n          'concat': concat,\n          'nth': nth,\n          'first': first,\n          'rest': rest,\n          'empty?': empty_Q,\n          'count': count,\n          'apply': apply,\n          'map': map,\n\n          'conj': conj,\n          'seq': seq,\n\n          'with-meta': with_meta,\n          'meta': meta,\n          'atom': types._atom,\n          'atom?': types._atom_Q,\n          \"deref\": deref,\n          \"reset!\": reset_BANG,\n          \"swap!\": swap_BANG,\n\n          'js-eval': js_eval,\n          '.': js_method_call\n};\n\nexports.ns = core.ns = ns;\nif (typeof module !== 'undefined') {\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction is_pair(x) {\n    return types._sequential_Q(x) && x.length > 0;\n}\n\nfunction quasiquote(ast) {\n    if (!is_pair(ast)) {\n        return [types._symbol(\"quote\"), ast];\n    } else if (types._symbol_Q(ast[0]) && ast[0].value === 'unquote') {\n        return ast[1];\n    } else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') {\n        return [types._symbol(\"concat\"),\n                ast[0][1],\n                quasiquote(ast.slice(1))];\n    } else {\n        return [types._symbol(\"cons\"),\n                quasiquote(ast[0]),\n                quasiquote(ast.slice(1))];\n    }\n}\n\nfunction is_macro_call(ast, env) {\n    return types._list_Q(ast) &&\n           types._symbol_Q(ast[0]) &&\n           env.find(ast[0]) &&\n           env.get(ast[0])._ismacro_;\n}\n\nfunction macroexpand(ast, env) {\n    while (is_macro_call(ast, env)) {\n        var mac = env.get(ast[0]);\n        ast = mac.apply(mac, ast.slice(1));\n    }\n    return ast;\n}\n\nfunction eval_ast(ast, env) {\n    if (types._symbol_Q(ast)) {\n        return env.get(ast);\n    } else if (types._list_Q(ast)) {\n        return ast.map(function(a) { return EVAL(a, env); });\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[EVAL(k, env)] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n}\n\nfunction _EVAL(ast, env) {\n    while (true) {\n\n    //printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    if (!types._list_Q(ast)) {\n        return eval_ast(ast, env);\n    }\n\n    // apply list\n    ast = macroexpand(ast, env);\n    if (!types._list_Q(ast)) {\n        return eval_ast(ast, env);\n    }\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        return env.set(a1, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            let_env.set(a1[i], EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"quote\":\n        return a1;\n    case \"quasiquote\":\n        ast = quasiquote(a1);\n        break;\n    case 'defmacro!':\n        var func = EVAL(a2, env);\n        func._ismacro_ = true;\n        return env.set(a1, func);\n    case 'macroexpand':\n        return macroexpand(a1, env);\n    case \"try*\":\n        try {\n            return EVAL(a1, env);\n        } catch (exc) {\n            if (a2 && a2[0].value === \"catch*\") {\n                if (exc instanceof Error) { exc = exc.message; }\n                return EVAL(a2[2], new Env(env, [a2[1]], [exc]));\n            } else {\n                throw exc;\n            }\n        }\n    case \"do\":\n        eval_ast(ast.slice(1, -1), env);\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var el = eval_ast(ast, env), f = el[0];\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(el.slice(1));\n        } else {\n            return f.apply(f, el.slice(1));\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); }\nrepl_env.set(types._symbol('eval'), function(ast) {\n    return EVAL(ast, repl_env); });\nrepl_env.set(types._symbol('*ARGV*'), []);\n\n// core.mal: defined using the language itself\nrep(\"(def! *host-language* \\\"javascript\\\")\")\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\")\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\nrep(\"(def! inc (fn* [x] (+ x 1)))\");\nrep(\"(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \\\"G__\\\" (swap! counter inc))))))\");\nrep(\"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))\");\n\nif (typeof process !== 'undefined' && process.argv.length > 2) {\n    repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));\n    rep('(load-file \"' + process.argv[2] + '\")');\n    process.exit(0);\n}\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "docs/web/skeleton.css",
    "content": "/* \n* Skeleton V1.0.2\n* Copyright 2011, Dave Gamache\n* www.getskeleton.com\n* Free to use under the MIT license.\n* http://www.opensource.org/licenses/mit-license.php\n* 5/20/2011\n*/\n\n\n/* Table of Contents\n==================================================\n\t#Base 960 Grid    \n\t#Tablet (Portrait)\n\t#Mobile (Portrait) \n\t#Mobile (Landscape)\n\t#Clearing */\n\t\n\t\n\n/* #Base 960 Grid \n================================================== */\n\n\t.container { position: relative; width: 960px; margin: 30px auto; padding: 0; }\n\t.column, .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; }\n\t.row { margin-bottom: 20px; }\n\t\n\t/* Nested Column Classes */\n\t.column.alpha, .columns.alpha \t\t\t\t{ margin-left: 0; }\n\t.column.omega, .columns.omega \t\t\t\t{ margin-right: 0; }\n\t\n\t/* Base Grid */\n\t.container .one.column \t\t\t\t\t\t\t{ width: 40px;  }\n\t.container .two.columns \t\t\t\t\t\t{ width: 100px; }\n\t.container .three.columns \t\t\t\t\t{ width: 160px; }\n\t.container .four.columns \t\t\t\t\t\t{ width: 220px; }\n\t.container .five.columns \t\t\t\t\t\t{ width: 280px; }\n\t.container .six.columns \t\t\t\t\t\t{ width: 340px; }\n\t.container .seven.columns \t\t\t\t\t{ width: 400px; }\t\n\t.container .eight.columns \t\t\t\t\t{ width: 460px; }\n\t.container .nine.columns \t\t\t\t\t\t{ width: 520px; }\n\t.container .ten.columns \t\t\t\t\t\t{ width: 580px; }\t\n\t.container .eleven.columns \t\t\t\t\t{ width: 640px; }\t\n\t.container .twelve.columns \t\t\t\t\t{ width: 700px; }\n\t.container .thirteen.columns \t\t\t\t{ width: 760px; }\t\n\t.container .fourteen.columns \t\t\t\t{ width: 820px; }\t\n\t.container .fifteen.columns \t\t\t\t{ width: 880px; }\n\t.container .sixteen.columns \t\t\t\t{ width: 940px; }\n\t\n\t.container .one-third.column\t\t\t\t{ width: 300px; }\n\t.container .two-thirds.column\t\t\t\t{ width: 620px; }\n\t\n\t/* Offsets */\t\n\t.container .offset-by-one \t\t\t\t\t{ padding-left: 60px;  }\n\t.container .offset-by-two \t\t\t\t\t{ padding-left: 120px; }\n\t.container .offset-by-three \t\t\t\t{ padding-left: 180px; }\n\t.container .offset-by-four \t\t\t\t\t{ padding-left: 240px; }\n\t.container .offset-by-five \t\t\t\t\t{ padding-left: 300px; }\n\t.container .offset-by-six \t\t\t\t\t{ padding-left: 360px; }\n\t.container .offset-by-seven \t\t\t\t{ padding-left: 420px; }\n\t.container .offset-by-eight \t\t\t\t{ padding-left: 480px; }\n\t.container .offset-by-nine \t\t\t\t\t{ padding-left: 540px; }\n\t.container .offset-by-ten \t\t\t\t\t{ padding-left: 600px; }\n\t.container .offset-by-eleven \t\t\t\t{ padding-left: 660px; }\n\t.container .offset-by-twelve \t\t\t\t{ padding-left: 720px; }\n\t.container .offset-by-thirteen \t\t\t{ padding-left: 780px; }\n\t.container .offset-by-fourteen \t\t\t{ padding-left: 840px; }\n\t.container .offset-by-fifteen \t\t\t{ padding-left: 900px; }\n\t\n\t\n\t\n/* #Tablet (Portrait)\n================================================== */\t\n\n\t/* Note: Design for a width of 768px */\n\n\t@media only screen and (min-width: 768px) and (max-width: 959px) {\n\t\t.container { width: 768px; }\n\t\t.container .column, \n\t\t.container .columns { margin-left: 10px; margin-right: 10px;  }\n\t\t.column.alpha, .columns.alpha \t\t\t\t{ margin-left: 0; margin-right: 10px; }\n\t\t.column.omega, .columns.omega \t\t\t\t{ margin-right: 0; margin-left: 10px; }\n\t\n\t\t.container .one.column \t\t\t\t\t\t\t{ width: 28px;  }\n\t\t.container .two.columns \t\t\t\t\t\t{ width: 76px; }\n\t\t.container .three.columns \t\t\t\t\t{ width: 124px; }\n\t\t.container .four.columns \t\t\t\t\t\t{ width: 172px; }\n\t\t.container .five.columns \t\t\t\t\t\t{ width: 220px; }\n\t\t.container .six.columns \t\t\t\t\t\t{ width: 268px; }\n\t\t.container .seven.columns \t\t\t\t\t{ width: 316px; }\t\n\t\t.container .eight.columns \t\t\t\t\t{ width: 364px; }\n\t\t.container .nine.columns \t\t\t\t\t\t{ width: 412px; }\n\t\t.container .ten.columns \t\t\t\t\t\t{ width: 460px; }\t\n\t\t.container .eleven.columns \t\t\t\t\t{ width: 508px; }\t\n\t\t.container .twelve.columns \t\t\t\t\t{ width: 556px; }\n\t\t.container .thirteen.columns \t\t\t\t{ width: 604px; }\t\n\t\t.container .fourteen.columns \t\t\t\t{ width: 652px; }\t\n\t\t.container .fifteen.columns \t\t\t\t{ width: 700px; }\n\t\t.container .sixteen.columns \t\t\t\t{ width: 748px; }\n\t\t\n\t\t.container .one-third.column\t\t\t\t{ width: 236px; }\n\t\t.container .two-thirds.column\t\t\t\t{ width: 492px; }\t\t\n\t\t\n\t\t/* Offsets */\t\n\t\t.container .offset-by-one \t\t\t\t\t{ padding-left: 48px;  }\n\t\t.container .offset-by-two \t\t\t\t\t{ padding-left: 96px; }\n\t\t.container .offset-by-three \t\t\t\t{ padding-left: 144px; }\n\t\t.container .offset-by-four \t\t\t\t\t{ padding-left: 192px; }\n\t\t.container .offset-by-five \t\t\t\t\t{ padding-left: 288px; }\n\t\t.container .offset-by-six \t\t\t\t\t{ padding-left: 336px; }\n\t\t.container .offset-by-seven \t\t\t\t{ padding-left: 348px; }\n\t\t.container .offset-by-eight \t\t\t\t{ padding-left: 432px; }\n\t\t.container .offset-by-nine \t\t\t\t\t{ padding-left: 480px; }\n\t\t.container .offset-by-ten \t\t\t\t\t{ padding-left: 528px; }\n\t\t.container .offset-by-eleven \t\t\t\t{ padding-left: 576px; }\n\t\t.container .offset-by-twelve \t\t\t\t{ padding-left: 624px; }\n\t\t.container .offset-by-thirteen \t\t\t{ padding-left: 672px; }\n\t\t.container .offset-by-fourteen \t\t\t{ padding-left: 720px; }\n\t\t.container .offset-by-fifteen \t\t\t{ padding-left: 900px; }\n\t}\n\t\n\t\n/*\t#Mobile (Portrait) \n================================================== */\n\t\n\t/* Note: Design for a width of 320px */\n\t\n\t@media only screen and (max-width: 767px) {\n\t\t.container { width: 300px; }\n\t\t.columns, .column { margin: 0; }\n\t\t\n\t\t.container .one.column,\n\t\t.container .two.columns,\n\t\t.container .three.columns,\n\t\t.container .four.columns,\n\t\t.container .five.columns,\n\t\t.container .six.columns,\n\t\t.container .seven.columns,\n\t\t.container .eight.columns,\n\t\t.container .nine.columns,\n\t\t.container .ten.columns,\n\t\t.container .eleven.columns,\n\t\t.container .twelve.columns,\n\t\t.container .thirteen.columns,\n\t\t.container .fourteen.columns,\n\t\t.container .fifteen.columns,\n\t\t.container .sixteen.columns, \n\t\t.container .one-third.column, \n\t\t.container .two-thirds.column  { width: 300px; }\n\t\t\n\t\t/* Offsets */\t\n\t\t.container .offset-by-one,\t\t\t\t\n\t\t.container .offset-by-two, \t\t\t\t\t\n\t\t.container .offset-by-three, \t\t\t\t\n\t\t.container .offset-by-four, \t\t\t\t\t\n\t\t.container .offset-by-five, \t\t\t\t\t\n\t\t.container .offset-by-six, \t\t\t\t\t\n\t\t.container .offset-by-seven, \t\t\t\t\n\t\t.container .offset-by-eight, \t\t\t\t\n\t\t.container .offset-by-nine, \t\t\t\t\t\n\t\t.container .offset-by-ten, \t\t\t\t\t\n\t\t.container .offset-by-eleven, \t\t\t\t\n\t\t.container .offset-by-twelve, \t\t\t\t\n\t\t.container .offset-by-thirteen, \t\t\t\n\t\t.container .offset-by-fourteen, \t\t\t\n\t\t.container .offset-by-fifteen { padding-left: 0; } \t\t\t\n\t\t\t\t\n\t}\t \n\t\n\t\n/* #Mobile (Landscape)\n================================================== */\n\n\t/* Note: Design for a width of 480px */\n\t\n\t@media only screen and (min-width: 480px) and (max-width: 767px) {\n\t\t.container { width: 420px; }\n\t\t.columns, .column { margin: 0; }\n\t\t\n\t\t.container .one.column,\n\t\t.container .two.columns,\n\t\t.container .three.columns,\n\t\t.container .four.columns,\n\t\t.container .five.columns,\n\t\t.container .six.columns,\n\t\t.container .seven.columns,\n\t\t.container .eight.columns,\n\t\t.container .nine.columns,\n\t\t.container .ten.columns,\n\t\t.container .eleven.columns,\n\t\t.container .twelve.columns,\n\t\t.container .thirteen.columns,\n\t\t.container .fourteen.columns,\n\t\t.container .fifteen.columns,\n\t\t.container .sixteen.columns,\n\t\t.container .one-third.column, \n\t\t.container .two-thirds.column { width: 420px; }\n\t}\n\t \n\t\n/* #Clearing\n================================================== */\n\n\t/* Self Clearing Goodness */\n\t.container:after { content: \"\\0020\"; display: block; height: 0; clear: both; visibility: hidden; } \n\t\n\t/* Use clearfix class on parent to clear nested columns, \n\tor wrap each row of columns in a <div class=\"row\"> */\n\t.clearfix:before,\n\t.clearfix:after,\n\t.row:before,\n\t.row:after {\n\t  content: '\\0020';\n\t  display: block;\n\t  overflow: hidden;\n\t  visibility: hidden;\n\t  width: 0;\n\t  height: 0; }\n\t.row:after,\n\t.clearfix:after {\n\t  clear: both; }\n\t.row, \n\t.clearfix {\n\t  zoom: 1; }\n\t  \n\t/* You can also use a <br class=\"clear\" /> to clear columns */\n\t.clear {\n\t  clear: both;\n\t  display: block;\n\t  overflow: hidden;\n\t  visibility: hidden;\n\t  width: 0;\n\t  height: 0;\n\t}\n\t\n\t\n\t"
  },
  {
    "path": "examples/clojurewest2014.mal",
    "content": ";; Mal Presentation\n\n(def! clear\n  (fn* ()\n    (str \"\u001b[2J\u001b[;H\")))\n\n(def! bold\n  (fn* (s)\n    (str \"\u001b[1m\" s \"\u001b[0m\")))\n\n(def! blue\n  (fn* (s)\n    (str \"\u001b[1;34m\" s \"\u001b[0m\")))\n\n(def! title\n  (fn* (s)\n    (bold (blue (str s \"\\n\")))))\n\n(def! title2\n  (fn* (s)\n    (bold (blue s))))\n\n\n(def! conj-slides\n  (list\n   (list\n    (title2 \" __  __    _    _\")\n    (title2 \"|  \\\\/  |  / \\\\  | |\")\n    (title2 \"| |\\\\/| | / _ \\\\ | | \")\n    (title2 \"| |  | |/ ___ \\\\| |___ \")\n    (title2 \"|_|  |_/_/   \\\\_\\\\_____|\"))\n   (list\n    (title \"gherkin\")\n    \"- a lisp1 written in bash4\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\"))\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\"\n    \"- and Python\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\"\n    \"- and Python\"\n    \"- and Clojure\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\"\n    \"- and Python\"\n    \"- and Clojure\"\n    \"- and C and Java and PHP\")\n   (list\n    (title \"things it has\")\n    \"- scalars: integers, strings, symbols, nil, true, false\"\n    \"- immutable collections: lists, vectors, hash-maps\"\n    \"- metadata, atoms\"\n    \"- def!, fn*, let*\"\n    \"  - varargs: (fn* (x y & more) ...)\"\n    \"- tail call optimization\"\n    \"  -  except GNU make implementation (no iteration)\"\n    \"- macros (quote, unquote, quasiquote, splice-quote)\"\n    \"- almost 300 unit tests\"\n    \"- REPL with readline (GNU readline or libedit)\")\n   (list\n    (title \"things it does not have\")\n    \"- performance\"\n    \"- namespaces\"\n    \"- keywords\"\n    \"- GC (in bash, make, C implementations)\"\n    \"- lots of other things\")\n   (list\n    (title \"why?\")\n    \"- because!\")\n   (list\n    (title \"why?\")\n    \"- because!\"\n    \"- gherkin was an inspiration to higher levels of crazy\"\n    \"- evolved into learning tool\"\n    \"- way to learn about Lisp and also the target language\"\n    \"- each implementation broken into small 10 steps\")\n   (list\n    (title \"thanks to:\")\n    \"- Peter Norvig: inspiration: lispy\"\n    \"  - http://norvig.com/lispy.html\"\n    \"- Alan Dipert: gherkin, original gherkin slides\"\n    \"  - https://github.com/alandipert/gherkin\")\n   (list\n    (title \"mal - Make a Lisp\")\n    \"https://github.com/kanaka/mal\")\n   (list\n    (title \"demo\"))))\n\n(def! present\n  (fn* (slides)\n    (if (> (count slides) 0)\n      (do\n        ;;(py!* \"import os; r = os.system('clear')\")\n        ;;(sh* \"clear\")\n        ;;(make* \"$(shell clear)\")\n        (println (clear))\n\n        ;;(prn (first slides))\n        (apply println (map (fn* (line) (str \"\\n        \" line)) (first slides)))\n        (println \"\\n\\n\\n\")\n        (readline \"\")\n        (present (rest slides))))))\n\n(present conj-slides)\n"
  },
  {
    "path": "examples/exercises.mal",
    "content": ";; These are the answers to the questions in ../docs/exercise.md.\n\n;; In order to avoid unexpected circular dependencies among solutions,\n;; this answer file attempts to be self-contained.\n(def! reduce (fn* (f init xs)\n  (if (empty? xs) init (reduce f (f init (first xs)) (rest xs)))))\n(def! foldr (fn* [f init xs]\n  (if (empty? xs) init (f (first xs) (foldr f init (rest xs))))))\n\n;; Reimplementations.\n\n(def! nil?   (fn* [x] (= x nil  )))\n(def! true?  (fn* [x] (= x true )))\n(def! false? (fn* [x] (= x false)))\n(def! empty? (fn* [x] (= x []   )))\n\n(def! sequential?\n  (fn* [x]\n    (if (list? x) true (vector? x))))\n\n(def! >  (fn* [a b]      (< b a) ))\n(def! <= (fn* [a b] (not (< b a))))\n(def! >= (fn* [a b] (not (< a b))))\n\n(def! list (fn* [& xs] xs))\n(def! vec (fn* [xs] (apply vector xs)))\n(def! prn (fn* [& xs] (println (apply pr-str xs))))\n(def! hash-map (fn* [& xs] (apply assoc {} xs)))\n(def! swap! (fn* [a f & xs] (reset! a (apply f (deref a) xs))))\n\n(def! count\n  (fn* [xs]\n    (if (nil? xs)\n      0\n      (reduce (fn* [acc _] (+ 1 acc)) 0 xs))))\n(def! nth\n  (fn* [xs index]\n    (if (if (<= 0 index) (not (empty? xs))) ; logical and\n      (if (= 0 index)\n        (first xs)\n        (nth (rest xs) (- index 1)))\n      (throw \"nth: index out of range\"))))\n(def! map\n  (fn* [f xs]\n    (foldr (fn* [x acc] (cons (f x) acc)) () xs)))\n(def! concat\n  (fn* [& xs]\n    (foldr (fn* [x acc] (foldr cons acc x)) () xs)))\n(def! conj\n  (fn* [xs & ys]\n    (if (vector? xs)\n      (vec (concat xs ys))\n      (reduce (fn* [acc x] (cons x acc)) xs ys))))\n\n(def! do2 (fn* [& xs] (nth xs (- (count xs) 1))))\n(def! do3 (fn* [& xs] (reduce (fn* [_ x] x) nil xs)))\n;; do2 will probably be more efficient when lists are implemented as\n;; arrays with direct indexing, but when they are implemented as\n;; linked lists, do3 may win because it only does one traversal.\n\n(defmacro! quote2 (fn* [ast]\n  (list (fn* [] ast))))\n(def! _quasiquote_iter (fn* [x acc]\n  (if (if (list? x) (= (first x) 'splice-unquote)) ; logical and\n    (list 'concat (first (rest x)) acc)\n    (list 'cons (list 'quasiquote2 x) acc))))\n(defmacro! quasiquote2 (fn* [ast]\n  (if (list? ast)\n    (if (= (first ast) 'unquote)\n      (first (rest ast))\n      (foldr _quasiquote_iter () ast))\n    (if (vector? ast)\n      (list 'vec (foldr _quasiquote_iter () ast))\n      (list 'quote ast)))))\n\n;; Interpret kvs as [k1 v1 k2 v2 ... kn vn] and returns\n;; (f k1 v1 (f k2 v2 (f ... (f kn vn)))).\n(def! _foldr_pairs (fn* [f init kvs]\n  (if (empty? kvs)\n    init\n    (let* [key (first kvs)\n           rst (rest kvs)\n           val (first rst)\n           acc (_foldr_pairs f init (rest rst))]\n      (f key val acc)))))\n(defmacro! let*A (fn* [binds form]\n  (let* [formal (_foldr_pairs (fn* [key val acc] (cons key acc)) () binds)\n         actual (_foldr_pairs (fn* [key val acc] (cons val acc)) () binds)]\n    `((fn* ~formal ~form) ~@actual))))\n;; Fails for (let* [a 1 b (+ 1 a)] b)\n(defmacro! let*B (fn* [binds form]\n  (let* [f (fn* [key val acc]\n                `((fn* [~key] ~acc) ~val))]\n    (_foldr_pairs f form binds))))\n;; Fails for (let* (cst (fn* (n) (if (= n 0) nil (cst (- n 1))))) (cst 1))\n(def! _c_combinator (fn* [x] (x x)))\n(def! _d_combinator (fn* [f] (fn* [x] (f (fn* [v] ((x x) v))))))\n(def! _Y_combinator (fn* [x] (_c_combinator (_d_combinator x))))\n(defmacro! let*C (fn* [binds form]\n  (let* [f (fn* [key val acc]\n                `((fn* [~key] ~acc) (_Y_combinator (fn* [~key] ~val))))]\n    (_foldr_pairs f form binds))))\n;; Fails for mutual recursion.\n;; See http://okmij.org/ftp/Computation/fixed-point-combinators.html\n;; if you are motivated to implement solution D.\n\n(def! apply\n  ;; Replace (f a b [c d]) with ('f 'a 'b 'c 'd) then evaluate the\n  ;; resulting function call (the surrounding environment does not\n  ;; matter when evaluating a function call).\n  ;; Use nil as marker to detect deepest recursive call.\n  (let* [q    (fn* [x] (list 'quote x))\n         iter (fn* [x acc]\n                (if (nil? acc) ; x is the last element (a sequence)\n                  (map q x)\n                  (cons (q x) acc)))]\n    (fn* [& xs] (eval (foldr iter nil xs)))))\n\n;; Folds\n\n(def! sum     (fn* [xs] (reduce + 0 xs)))\n(def! product (fn* [xs] (reduce * 1 xs)))\n\n(def! conjunction\n  (let* [and2 (fn* [acc x] (if acc x false))]\n    (fn* [xs]\n      (reduce and2 true xs))))\n(def! disjunction\n  (let* [or2 (fn* [acc x] (if acc true x))]\n    (fn* [xs]\n      (reduce or2 false xs))))\n;; It would be faster to stop the iteration on first failure\n;; (conjunction) or success (disjunction). Even better, `or` in the\n;; stepA and `and` in `core.mal` stop evaluating their arguments.\n\n;; Yes, -2-3-4 means (((0-2)-3)-4).\n\n;; `(reduce str \"\" xs)` is equivalent to `apply str xs`\n;; and `(reduce concat () xs)` is equivalent to `apply concat xs`.\n;; The built-in iterations are probably faster.\n\n;; `(reduce (fn* [acc _] acc) nil xs)` is equivalent to `nil`.\n\n;; For (reduce (fn* [acc x] x) nil xs))), see do3 above.\n\n;; `(reduce (fn* [acc x] (if (< acc x) x acc)) 0 xs)` computes the\n;; maximum of a list of non-negative integers. It is hard to find an\n;; initial value fitting all purposes.\n\n(def! sum_len\n  (let* [add_len (fn* [acc x] (+ acc (count x)))]\n    (fn* [xs]\n      (reduce add_len 0 xs))))\n(def! max_len\n  (let* [update_max (fn* [acc x] (let* [l (count x)] (if (< acc l) l acc)))]\n    (fn* [xs]\n      (reduce update_max 0 xs))))\n\n;; (fn* [& fs] (foldr (fn* [f acc] (fn* [x] (f (acc x)))) identity fs))\n;; computes the composition of an arbitrary number of functions.\n;; The first anonymous function is the mathematical composition.\n;; For practical purposes, `->` and `->>` in `core.mal` are more\n;; efficient and general.\n"
  },
  {
    "path": "examples/hello.mal",
    "content": "(println \"hello world\\n\\nanother line\")\n(println \"and another line\")\n"
  },
  {
    "path": "examples/presentation.mal",
    "content": ";; Mal Presentation\n\n(def! clear\n  (fn* ()\n    (str \"\u001b[2J\u001b[;H\")))\n\n(def! bold\n  (fn* (s)\n    (str \"\u001b[1m\" s \"\u001b[0m\")))\n\n(def! blue\n  (fn* (s)\n    (str \"\u001b[1;34m\" s \"\u001b[0m\")))\n\n(def! title\n  (fn* (s)\n    (bold (blue (str s \"\\n\")))))\n\n(def! title2\n  (fn* (s)\n    (bold (blue s))))\n\n\n(def! slides\n  (list\n   (list\n    (title2 \" __  __    _    _\")\n    (title2 \"|  \\/  |  / \\  | |\")\n    (title2 \"| |\\/| | / _ \\ | | \")\n    (title2 \"| |  | |/ ___ \\| |___ \")\n    (title2 \"|_|  |_/_/   \\_\\_____|\"))\n   (list\n    (title \"gherkin\")\n    \"- a lisp1 written in bash4\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\"))\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\"\n    \"- and Python\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\"\n    \"- and Python\"\n    \"- and Clojure\")\n   (list\n    (title \"mal - an interpreter for a subset of Clojure\")\n    \"- written in GNU make\"\n    \"- and Bash 4\"\n    \"- and Javascript\"\n    \"- and Python\"\n    \"- and Clojure\"\n    \"- and 17 other languages\")\n   (list\n    (title \"things it has\")\n    \"- scalars: integers, strings, symbols, keywords, nil, true, false\"\n    \"- immutable collections: lists, vectors, hash-maps\"\n    \"- metadata, atoms\"\n    \"- def!, fn*, let*\"\n    \"  - varargs: (fn* (x y & more) ...)\"\n    \"- tail call optimization\"\n    \"  -  except GNU make implementation (no iteration)\"\n    \"- macros (quote, unquote, quasiquote, splice-quote)\"\n    \"- over 500 unit tests\"\n    \"- REPL with line editing (GNU readline/libedit/linenoise)\")\n   (list\n    (title \"things it does not have\")\n    \"- performance\"\n    \"- namespaces\"\n    \"- GC (in bash, make, C implementations)\"\n    \"- protocols :-(\"\n    \"- lots of other things\")\n   (list\n    (title \"why?\")\n    \"- because!\")\n   (list\n    (title \"why?\")\n    \"- because!\"\n    \"- gherkin was an inspiration to higher levels of crazy\"\n    \"- evolved into learning tool\"\n    \"- way to learn about Lisp and also the target language\"\n    \"- each implementation broken into small 11 steps\")\n   (list\n    (title \"thanks to:\")\n    \"- Peter Norvig: inspiration: lispy\"\n    \"  - http://norvig.com/lispy.html\"\n    \"- Alan Dipert: gherkin, original gherkin slides\"\n    \"  - https://github.com/alandipert/gherkin\")\n   (list\n    (title \"mal - Make a Lisp\")\n    \"https://github.com/kanaka/mal\")\n   (list\n    (title \"demo\"))))\n\n(def! present\n  (fn* (slides)\n    (if (> (count slides) 0)\n      (do\n        (println (clear))\n\n        (apply println (map (fn* (line) (str \"\\n        \" line)) (first slides)))\n        (println \"\\n\\n\\n\")\n        (readline \"\")\n        (present (rest slides))))))\n\n(present slides)\n"
  },
  {
    "path": "get-ci-matrix.py",
    "content": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport re\nimport sys\nimport yaml\n\nIMPLS_FILE = \"IMPLS.yml\"\nRE_IGNORE = re.compile(r'(^LICENSE$|^README.md$|^docs/|^process/|^IMPLS.yml$|^Makefile.impls$)')\nRE_IMPL = re.compile(r'^impls/(?!lib|tests)([^/]*)/')\n\nOVERRIDE_IMPLS = os.environ.get('OVERRIDE_IMPLS', '').split()\n\ndef eprint(*args, **kwargs):\n    print(*args, file=sys.stderr, **kwargs)\n\ndef impl_text(impl):\n    s = \"IMPL=%s\" % impl['IMPL']\n    for k, v in impl.items():\n        if k == 'IMPL': continue\n        s += \" %s=%s\" % (k, v)\n    return s\n\nall_changes = sys.argv[1:]\n# code changes that are not just to docs or implementation lists\ncode_changes = set([c for c in all_changes if not RE_IGNORE.search(c)])\n# actual changes to implementations\nimpl_changes = set([c for c in all_changes if RE_IMPL.search(c)])\n# names of changed implementations\nrun_impls = set([RE_IMPL.search(c).groups()[0] for c in impl_changes])\n\ndo_full = (len(code_changes) != len(impl_changes))\n\n# If we have non-implementation code changes then we will add all\n# implementations to the test matrix\nif OVERRIDE_IMPLS:\n    run_impls = OVERRIDE_IMPLS\n    if 'all' in OVERRIDE_IMPLS:\n        do_full = True\n\n\neprint(\"OVERRIDE_IMPLS: %s\" % OVERRIDE_IMPLS)\neprint(\"code_changes: %s (%d)\" % (code_changes, len(code_changes)))\neprint(\"impl_changes: %s (%d)\" % (impl_changes, len(impl_changes)))\neprint(\"run_impls: %s (%d)\" % (run_impls, len(run_impls)))\neprint(\"do_full: %s\" % do_full)\n\n# Load the full implementation description file\nall_impls = yaml.safe_load(open(IMPLS_FILE))\n\n# Accumulate and output linux, macos & windows implementations separately\nlinux_impls = []\nmacos_impls = []\nwindows_impls = []\nfor impl in all_impls['IMPL']:\n    targ = linux_impls\n    if 'OS' in impl and impl['OS'] == 'macos':\n        targ = macos_impls\n    if 'OS' in impl and impl['OS'] == 'windows':\n        targ = windows_impls\n    # Run implementations with actual changes first before running\n    # other impls triggered by non-impl code changes\n    if impl['IMPL'] in run_impls:\n        targ.insert(0, impl_text(impl))\n    elif do_full:\n        targ.append(impl_text(impl))\n\nprint(\"do_linux=%s\" % json.dumps(len(linux_impls)>0))\nprint(\"do_macos=%s\" % json.dumps(len(macos_impls)>0))\nprint(\"do_windows=%s\" % json.dumps(len(windows_impls)>0))\nprint(\"linux={\\\"IMPL\\\":%s}\" % json.dumps(linux_impls))\nprint(\"macos={\\\"IMPL\\\":%s}\" % json.dumps(macos_impls))\nprint(\"windows={\\\"IMPL\\\":%s}\" % json.dumps(windows_impls))\n"
  },
  {
    "path": "impls/.gitignore",
    "content": ".DS_Store\n.bash_history\n.cache\n.cargo\n.config\n.mal-history\n.crystal\n.lein\n.m2\n.ivy2\n.sbt\n.npm\n.node-gyp\n.elm\n*/experiments\n*/node_modules\n*.o\n*.pyc\n*/step0_repl\n*/step1_read_print\n*/step2_eval\n*/step3_env\n*/step4_if_fn_do\n*/step5_tco\n*/step6_file\n*/step7_quote\n*/step8_macros\n*/step9_try\n*/stepA_mal\n*/mal\n*/notes\n\nlogs\nold\n\nada/obj/\nawk/mal.awk\nbash/mal.sh\nclojure/mal.jar\nclojure/target\nclojure/.lein-repl-history\ncoffee/mal.coffee\ncs/*.exe\ncs/*.dll\ncs/*.mdb\nd/*.o\nelixir/_build\nelixir/deps\nelixir/erl_crash.dump\nelixir/*.ez\nerlang/ebin\nerlang/.rebar\nerlang/src/*.beam\nes6/mal.js\nes6/.esm-cache\nfactor/mal.factor\nfantom/lib\nforth/mal.fs\nfsharp/*.exe\nfsharp/*.dll\nfsharp/*.mdb\ngo/step*\ngroovy/*.class\ngroovy/mal.jar\nhaskell/*.hi\nhaskell/*.o\nhaxe/*.n\nhaxe/*.py\nhaxe/cpp/\nhaxe/*.js\njava/mal.jar\njava/target/\njava/dependency-reduced-pom.xml\n.npm/\n.node-gyp/\njs/mal.js\njs/web/mal.js\nkotlin/*.jar\nkotlin/.idea\nkotlin/*.iml\nlua/lib\nlua/linenoise.so\nlua/rex_pcre.so\nlua/mal.lua\nmake/mal.mk\nmal/mal.mal\nmatlab/octave-workspace\nminiMAL/mal.json\nnim/nimcache*\nobjc/*.d\nocaml/*.cmi\nocaml/*.cmo\nocaml/*.swp\nocaml/*.cmx\nocaml/*.o\nocaml/mal_lib.*\nobjpascal/*.o\nobjpascal/*.ppu\nobjpascal/pas-readline\nobjpascal/regexpr/Source/RegExpr.ppu\nperl/mal.pl\nperl6/.precomp/\nphp/mal.php\nphp/mal-web.php\nps/mal.ps\npython/mal.pyz\nr/mal.r\nruby/mal.rb\n.cargo/\nrust/target/\nrust/Cargo.lock\nrust/.cargo\nr/lib\nscala/mal.jar\nscala/target\nscala/project\nskew/*.js\ntcl/mal.tcl\nvb/*.exe\nvb/*.dll\nvimscript/mal.vim\nclisp/*.fas\nclisp/*.lib\nbasic/step0_repl.bas\nbasic/step1_read_print.bas\nbasic/step2_eval.bas\nbasic/step3_env.bas\nbasic/step4_if_fn_do.bas\nbasic/step5_tco.bas\nbasic/step6_file.bas\nbasic/step7_quote.bas\nbasic/step8_macros.bas\nbasic/step9_try.bas\nbasic/stepA_mal.bas\nbasic/*.prg\ncommon-lisp/*.fasl\ncommon-lisp/*.lib\ncommon-lisp/images/*\ncommon-lisp/hist/*\nlivescript/*.js\n!livescript/node_readline.js\nlivescript/node_modules\nelm/node_modules\nelm/elm-stuff\nelm/*.js\n!elm/node_readline.js\n!elm/bootstrap.js\nwasm/*.wat\nwasm/*.wasm\n"
  },
  {
    "path": "impls/ada/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\nLABEL org.opencontainers.image.source=https://github.com/kanaka/mal\nLABEL org.opencontainers.image.description=\"mal test container: ada\"\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# GNU Ada compiler\nRUN apt-get -y install gnat\n"
  },
  {
    "path": "impls/ada/Makefile",
    "content": "PROGS=step0_repl step1_read_print step2_eval step3_env step4_if_fn_do \\\n      step5_tco step6_file step7_quote step8_macros step9_try\n\nall:\t${PROGS} stepA_mal\n\nobj:\n\tmkdir -p $@\n\n# stepA_mal is awkward because GNAT requires the filename to be lowercase\n${PROGS} stepa_mal:\tforce obj\n\tgnatmake -O3 -gnata $@.adb -D obj\n\n# so we make stepa_mal and just move it.\nstepA_mal:\tstepa_mal\n\tmv $< $@\n\nclean:\n\trm -f ${PROGS}\n\trm -rf obj\n\n.PHONY: force\n\nforce:\n"
  },
  {
    "path": "impls/ada/core.adb",
    "content": "with Ada.Calendar;\nwith Ada.Characters.Latin_1;\nwith Ada.Strings.Unbounded;\nwith Ada.Text_IO;\nwith Eval_Callback;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\nwith Types.Hash_Map;\nwith Types.Vector;\n\npackage body Core is\n\n   use Types;\n\n   -- primitive functions on Smart_Pointer,\n   function \"+\" is new Arith_Op (\"+\", \"+\");\n   function \"-\" is new Arith_Op (\"-\", \"-\");\n   function \"*\" is new Arith_Op (\"*\", \"*\");\n   function \"/\" is new Arith_Op (\"/\", \"/\");\n\n   function \"<\" is new Rel_Op (\"<\", \"<\");\n   function \"<=\" is new Rel_Op (\"<=\", \"<=\");\n   function \">\" is new Rel_Op (\">\", \">\");\n   function \">=\" is new Rel_Op (\">=\", \">=\");\n\n\n   function Eval_As_Boolean (MH : Types.Mal_Handle) return Boolean is\n      use Types;\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            Res := False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Throw (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      Types.Mal_Exception_Value := First_Param;\n      raise Mal_Exception;\n      return First_Param;  -- Keep the compiler happy.\n   end Throw;\n\n\n   function Is_True (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = Bool and then\n         Deref_Bool (First_Param).Get_Bool);\n   end Is_True;\n\n\n   function Is_False (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = Bool and then\n         not Deref_Bool (First_Param).Get_Bool);\n   end Is_False;\n\n\n   function Is_Nil (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = Nil);\n   end Is_Nil;\n\n\n   function Meta (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Deref (First_Param).Get_Meta;\n   end Meta;\n\n\n   function With_Meta (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Meta_Param, Res : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n      Meta_Param := Car (Rest_List);\n      Res := Copy (First_Param);\n      Deref (Res).Set_Meta (Meta_Param);\n      return Res;\n   end With_Meta;\n\n\n   function New_Atom (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Atom_Mal_Type (First_Param);\n   end New_Atom;\n\n   function Is_Atom (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type (Deref (First_Param).Sym_Type = Atom);\n   end Is_Atom;\n\n\n   function Deref_Atm (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Deref_Atom (First_Param).Get_Atom;\n   end Deref_Atm;\n\n\n   function Reset (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Atom_Param, New_Val : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Atom_Param := Car (Rest_List);\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n      New_Val := Car (Rest_List);\n      Deref_Atom (Atom_Param).Set_Atom (New_Val);\n      return New_Val;\n   end Reset;\n\n\n   function Swap (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Atom_Param, Atom_Val, New_Val : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n      Rest_List_Class : Types.List_Class_Ptr;\n      Func_Param, Param_List : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Atom_Param := Car (Rest_List);\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n      Func_Param := Car (Rest_List);\n      Param_List := Cdr (Rest_List);\n\n      Rest_List_Class := Deref_List_Class (Param_List);\n      Param_List := Rest_List_Class.Duplicate;\n      Atom_Val := Deref_Atom (Atom_Param).Get_Atom;\n      Param_List := Prepend (Atom_Val, Deref_List (Param_List).all);\n      case Deref (Func_Param).Sym_Type is\n         when Lambda =>\n            New_Val := Deref_Lambda (Func_Param).Apply (Param_List);\n         when Func =>\n            New_Val := Deref_Func (Func_Param).Call_Func (Param_List);\n         when others => raise Runtime_Exception with \"Swap with bad func\";\n      end case;\n      Deref_Atom (Atom_Param).Set_Atom (New_Val);\n      return New_Val;\n   end Swap;\n\n\n   function Is_List (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = List and then\n         Deref_List (First_Param).Get_List_Type = List_List);\n   end Is_List;\n\n\n   function Is_Vector (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = List and then\n         Deref_List (First_Param).Get_List_Type = Vector_List);\n   end Is_Vector;\n\n\n   function Is_Map (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = List and then\n         Deref_List (First_Param).Get_List_Type = Hashed_List);\n   end Is_Map;\n\n\n   function Is_Sequential (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return New_Bool_Mal_Type\n        (Deref (First_Param).Sym_Type = List and then\n         Deref_List (First_Param).Get_List_Type /= Hashed_List);\n   end Is_Sequential;\n\n\n   function Is_Empty (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      List : List_Class_Ptr;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      List := Deref_List_Class (First_Param);\n      return New_Bool_Mal_Type (Is_Null (List.all));\n   end Is_Empty;\n\n\n   function Eval_As_List (MH : Types.Mal_Handle) return List_Mal_Type is\n   begin\n      case Deref (MH).Sym_Type is\n         when List   => return Deref_List (MH).all;\n         when Nil    => return Null_List (List_List);\n         when others => null;\n      end case;\n      raise Runtime_Exception with \"Expecting a List\";\n      return Null_List (List_List);\n   end Eval_As_List;\n\n\n   function Count (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Evaled_List : Mal_Handle;\n      L : List_Mal_Type;\n      Rest_List : Types.List_Mal_Type;\n      N : Natural;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      if Deref (First_Param).Sym_Type = List and then\n         Deref_List (First_Param).Get_List_Type = Vector_List then\n         N := Deref_List_Class (First_Param).Length;\n      else\n         L := Eval_As_List (First_Param);\n         N := L.Length;\n      end if;\n      return New_Int_Mal_Type (N);\n   end Count;\n\n\n   function Cons (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      First_Param, List_Handle : Mal_Handle;\n      List : List_Mal_Type;\n      List_Class : List_Class_Ptr;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      List_Handle := Cdr (Rest_List);\n      List := Deref_List (List_Handle).all;\n      List_Handle := Car (List);\n      List_Class := Deref_List_Class (List_Handle);\n      return Prepend (First_Param, List_Class.all);\n   end Cons;\n\n\n   function Concat (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      return Types.Concat (Rest_List);\n   end Concat;\n\n\n   function First (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      First_List : Types.List_Class_Ptr;\n      First_Param : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      if Deref (First_Param).Sym_Type = Nil then\n         return New_Nil_Mal_Type;\n      end if;\n      First_List := Deref_List_Class (First_Param);\n      if Is_Null (First_List.all) then\n         return New_Nil_Mal_Type;\n      else\n         return Types.Car (First_List.all);\n      end if;\n   end First;\n\n\n   function Rest (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      First_Param, Container : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      if Deref (First_Param).Sym_Type = Nil then\n         return New_List_Mal_Type (List_List);\n      end if;\n      Container := Deref_List_Class (First_Param).Cdr;\n      return Deref_List_Class (Container).Duplicate;\n   end Rest;\n\n\n   function Nth (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      -- Rest_List, First_List : Types.List_Mal_Type;\n      Rest_List : Types.List_Mal_Type;\n      First_List : Types.List_Class_Ptr;\n      First_Param, List_Handle, Num_Handle : Mal_Handle;\n      List : List_Mal_Type;\n      Index : Types.Int_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      First_List := Deref_List_Class (First_Param);\n      List_Handle := Cdr (Rest_List);\n      List := Deref_List (List_Handle).all;\n      Num_Handle := Car (List);\n      Index := Deref_Int (Num_Handle).all;\n      return Types.Nth (First_List.all, Natural (Index.Get_Int_Val));\n   end Nth;\n\n\n   function Apply (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n\n      Results_Handle, First_Param : Mal_Handle;\n      Rest_List : List_Mal_Type;\n      Results_List : List_Ptr;\n\n   begin\n\n      -- The rest of the line.\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n\n      Results_Handle := New_List_Mal_Type (List_List);\n      Results_List := Deref_List (Results_Handle);\n\n      -- The last item is a list or a vector which gets flattened so that\n      -- (apply f (A B) C (D E)) becomes (f (A B) C D E)\n      while not Is_Null (Rest_List) loop\n         declare\n            Part_Handle : Mal_Handle;\n         begin\n            Part_Handle := Car (Rest_List);\n            Rest_List := Deref_List (Cdr (Rest_List)).all;\n\n            -- Is Part_Handle the last item in the list?\n            if Is_Null (Rest_List) then\n               declare\n                  The_List : List_Class_Ptr;\n                  List_Item : Mal_Handle;\n                  Next_List : Mal_Handle;\n               begin\n                  The_List := Deref_List_Class (Part_Handle);\n                  while not Is_Null (The_List.all) loop\n                     List_Item := Car (The_List.all);\n                     Append (Results_List.all, List_Item);\n                     Next_List := Cdr (The_List.all);\n                     The_List := Deref_List_Class (Next_List);\n                  end loop;\n               end;\n            else\n               Append (Results_List.all, Part_Handle);\n            end if;\n         end;\n      end loop;\n\n      -- The apply part...\n      if Deref (First_Param).Sym_Type = Func then\n         return Call_Func (Deref_Func (First_Param).all, Results_Handle);\n      elsif Deref (First_Param).Sym_Type = Lambda then\n         declare\n\n            L : Lambda_Mal_Type;\n            E : Envs.Env_Handle;\n            Param_Names : List_Mal_Type;\n            Res : Mal_Handle;\n\n         begin\n\n            L := Deref_Lambda (First_Param).all;\n            E := Envs.New_Env (L.Get_Env);\n\n            Param_Names := Deref_List (L.Get_Params).all;\n\n            if Envs.Bind (E, Param_Names, Results_List.all) then\n\n               return Eval_Callback.Eval.all (L.Get_Expr, E);\n\n            else\n\n               raise Runtime_Exception with \"Bind failed in Apply\";\n\n            end if;\n\n         end;\n\n      else  -- neither a Lambda or a Func\n         raise Runtime_Exception with \"Deref called on non-Func/Lambda\";\n      end if;\n\n   end Apply;\n\n\n   function Map (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n\n      Rest_List, Results_List : List_Mal_Type;\n      Func_Handle, List_Handle, Results_Handle : Mal_Handle;\n\n   begin\n\n      -- The rest of the line.\n      Rest_List := Deref_List (Rest_Handle).all;\n\n      Func_Handle := Car (Rest_List);\n      List_Handle := Nth (Rest_List, 1);\n\n      Results_Handle := New_List_Mal_Type (List_List);\n      Results_List := Deref_List (Results_Handle).all;\n\n      while not Is_Null (Deref_List_Class (List_Handle).all) loop\n\n         declare\n            Parts_Handle : Mal_Handle;\n         begin\n            Parts_Handle :=\n              Make_New_List\n                ((1 => Func_Handle,\n                  2 => Make_New_List\n                         ((1 => Car (Deref_List_Class (List_Handle).all)))));\n\n            List_Handle := Cdr (Deref_List_Class (List_Handle).all);\n\n            Append\n              (Results_List,\n               Apply (Parts_Handle));\n\n         end;\n\n      end loop;\n\n      return New_List_Mal_Type (Results_List);\n\n   end Map;\n\n\n   function Symbol (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n\n      Sym_Handle : Mal_Handle;\n      Rest_List : List_Mal_Type;\n\n   begin\n\n      -- The rest of the line.\n      Rest_List := Deref_List (Rest_Handle).all;\n\n      Sym_Handle := Car (Rest_List);\n\n      return New_Symbol_Mal_Type (Deref_String (Sym_Handle).Get_String);\n\n   end Symbol;\n\n\n   function Is_Symbol (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n\n      Sym_Handle : Mal_Handle;\n      Rest_List : List_Mal_Type;\n      Res : Boolean;\n\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Sym_Handle := Car (Rest_List);\n      if Deref (Sym_Handle).Sym_Type = Sym then\n         Res := Deref_Sym (Sym_Handle).Get_Sym (1) /= ':';\n      else\n         Res := False;\n      end if;\n      return New_Bool_Mal_Type (Res);\n   end Is_Symbol;\n\n\n   function Is_String (Rest_Handle : Mal_Handle) return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n   begin\n      First_Param := Car (Deref_List (Rest_Handle).all);\n      return New_Bool_Mal_Type (Deref (First_Param).Sym_Type = Str);\n   end Is_String;\n\n\n   function Keyword (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n\n      Sym_Handle : Mal_Handle;\n      Rest_List : List_Mal_Type;\n\n   begin\n\n      -- The rest of the line.\n      Rest_List := Deref_List (Rest_Handle).all;\n\n      Sym_Handle := Car (Rest_List);\n\n      case Deref (Sym_Handle).Sym_Type is\n         when Str =>\n            return New_Symbol_Mal_Type (':' & Deref_String (Sym_Handle).Get_String);\n         when Sym =>\n            if Deref_Sym (Sym_Handle).Get_Sym (1) = ':' then\n               return Sym_Handle;\n            end if;\n         when others =>\n            null;\n      end case;\n\n      raise Runtime_Exception with \"keyword: expects a keyword or string\";\n\n   end Keyword;\n\n\n   function Is_Keyword (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n\n      Sym_Handle : Mal_Handle;\n      Rest_List : List_Mal_Type;\n      Res : Boolean;\n\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Sym_Handle := Car (Rest_List);\n      if Deref (Sym_Handle).Sym_Type = Sym then\n         Res := Deref_Sym (Sym_Handle).Get_Sym (1) = ':';\n      else\n         Res := False;\n      end if;\n      return New_Bool_Mal_Type (Res);\n   end Is_Keyword;\n\n\n   function Is_Number (Rest_Handle : Mal_Handle) return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n   begin\n      First_Param := Car (Deref_List (Rest_Handle).all);\n      return New_Bool_Mal_Type (Deref (First_Param).Sym_Type = Int);\n   end Is_Number;\n\n\n   function Is_Fn (Rest_Handle : Mal_Handle) return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Res : Boolean;\n   begin\n      First_Param := Car (Deref_List (Rest_Handle).all);\n      case Deref (First_Param).Sym_Type is\n         when Func =>\n            Res := True;\n         when Lambda =>\n\t    Res := not Deref_Lambda (First_Param).Get_Is_Macro;\n         when others =>\n\t    Res := False;\n      end case;\n      return New_Bool_Mal_Type (Res);\n   end Is_Fn;\n\n\n   function Is_Macro (Rest_Handle : Mal_Handle) return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n   begin\n      First_Param := Car (Deref_List (Rest_Handle).all);\n      return New_Bool_Mal_Type (Deref (First_Param).Sym_Type = Lambda and then Deref_Lambda (First_Param).Get_Is_Macro);\n   end Is_Macro;\n\n\n   function New_List (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      return New_List_Mal_Type (The_List => Rest_List);\n   end New_List;\n\n\n   function New_Vector (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      Res : Mal_Handle;\n      use Types.Vector;\n   begin\n      Res := New_Vector_Mal_Type;\n      Rest_List := Deref_List (Rest_Handle).all;\n      while not Is_Null (Rest_List) loop\n         Deref_Vector (Res).Append (Car (Rest_List));\n         Rest_List := Deref_List (Cdr (Rest_List)).all;\n      end loop;\n      return Res;\n   end New_Vector;\n\n\n   function Vec (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n   begin\n      First_Param := Car (Deref_List (Rest_Handle).all);\n      if Deref (First_Param).Sym_Type /= List then\n         raise Runtime_Exception with \"Expecting a sequence\";\n      end if;\n      case Deref_List_Class (First_Param).Get_List_Type is\n      when Hashed_List =>\n         raise Runtime_Exception with \"Expecting a sequence\";\n      when Vector_List =>\n         return First_Param;\n      when List_List =>\n         return New_Vector (First_Param);\n      end case;\n   end Vec;\n\n\n   function New_Map (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      Res : Mal_Handle;\n   begin\n      Res := Hash_Map.New_Hash_Map_Mal_Type;\n      Rest_List := Deref_List (Rest_Handle).all;\n      while not Is_Null (Rest_List) loop\n         Hash_Map.Deref_Hash (Res).Append (Car (Rest_List));\n         Rest_List := Deref_List (Cdr (Rest_List)).all;\n      end loop;\n      return Res;\n   end New_Map;\n\n\n   function Assoc (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Mal_Handle;\n      Map : Hash_Map.Hash_Map_Mal_Type;\n   begin\n      Rest_List := Rest_Handle;\n      Map := Hash_Map.Deref_Hash (Car (Deref_List (Rest_List).all)).all;\n      Rest_List := Cdr (Deref_List (Rest_List).all);\n      return Hash_Map.Assoc (Map, Rest_List);\n   end Assoc;\n\n\n   function Dis_Assoc (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Mal_Handle;\n      Map : Hash_Map.Hash_Map_Mal_Type;\n   begin\n      Rest_List := Rest_Handle;\n      Map := Hash_Map.Deref_Hash (Car (Deref_List (Rest_List).all)).all;\n      Rest_List := Cdr (Deref_List (Rest_List).all);\n      return Hash_Map.Dis_Assoc (Map, Rest_List);\n   end Dis_Assoc;\n\n\n   function Get_Key (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      Map : Hash_Map.Hash_Map_Mal_Type;\n      Map_Param, Key : Mal_Handle;\n      The_Sym : Sym_Types;\n   begin\n\n      Rest_List := Deref_List (Rest_Handle).all;\n      Map_Param := Car (Rest_List);\n      The_Sym := Deref (Map_Param).Sym_Type;\n      if The_Sym = Sym or The_Sym = Nil then\n         -- Either its nil or its some other atom\n         -- which makes no sense!\n         return New_Nil_Mal_Type;\n      end if;\n\n      -- Assume a map from here on in.\n      Map := Hash_Map.Deref_Hash (Car (Rest_List)).all;\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n      Key := Car (Rest_List);\n\n      return Map.Get (Key);\n\n   end Get_Key;\n\n\n   function Contains_Key (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      Map : Hash_Map.Hash_Map_Mal_Type;\n      Key : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Map := Hash_Map.Deref_Hash (Car (Rest_List)).all;\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n      Key := Car (Rest_List);\n      return New_Bool_Mal_Type (Hash_Map.Contains (Map, Key));\n   end Contains_Key;\n\n\n   function All_Keys (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      Map : Hash_Map.Hash_Map_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Map := Hash_Map.Deref_Hash (Car (Rest_List)).all;\n      return Hash_Map.All_Keys (Map);\n   end All_Keys;\n\n\n   function All_Values (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      Map : Hash_Map.Hash_Map_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      Map := Hash_Map.Deref_Hash (Car (Rest_List)).all;\n      return Hash_Map.All_Values (Map);\n   end All_Values;\n\n\n   -- Take a list with two parameters and produce a single result\n   -- using the Op access-to-function parameter.\n   function Reduce2\n     (Op : Binary_Func_Access; LH : Mal_Handle)\n   return Mal_Handle is\n      Left, Right : Mal_Handle;\n      L, Rest_List : List_Mal_Type;\n   begin\n      L := Deref_List (LH).all;\n      Left := Car (L);\n      Rest_List := Deref_List (Cdr (L)).all;\n      Right := Car (Rest_List);\n      return Op (Left, Right);\n   end Reduce2;\n\n\n   function Plus (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\"+\"'Access, Rest_Handle);\n   end Plus;\n\n\n   function Minus (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\"-\"'Access, Rest_Handle);\n   end Minus;\n\n\n   function Mult (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\"*\"'Access, Rest_Handle);\n   end Mult;\n\n\n   function Divide (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\"/\"'Access, Rest_Handle);\n   end Divide;\n\n\n   function LT (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\"<\"'Access, Rest_Handle);\n   end LT;\n\n\n   function LTE (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\"<=\"'Access, Rest_Handle);\n   end LTE;\n\n\n   function GT (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\">\"'Access, Rest_Handle);\n   end GT;\n\n\n   function GTE (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (\">=\"'Access, Rest_Handle);\n   end GTE;\n\n\n   function EQ (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Types.\"=\"'Access, Rest_Handle);\n   end EQ;\n\n\n   function Pr_Str (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n      return New_String_Mal_Type (Deref_List (Rest_Handle).Pr_Str);\n   end Pr_Str;\n\n\n   function Prn (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n      Ada.Text_IO.Put_Line (Deref_List (Rest_Handle).Pr_Str);\n      return New_Nil_Mal_Type;\n   end Prn;\n\n\n   function Println (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n      Ada.Text_IO.Put_Line (Deref_List (Rest_Handle).Pr_Str (False));\n      return New_Nil_Mal_Type;\n   end Println;\n\n\n   function Str (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n      return New_String_Mal_Type (Deref_List (Rest_Handle).Cat_Str (False));\n   end Str;\n\n\n   function Read_String (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      First_Param : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Reader.Read_Str (Deref_String (First_Param).Get_String);\n   end Read_String;\n\n\n   function Read_Line (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      First_Param : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      -- Output the prompt.\n      Ada.Text_IO.Put (Deref_String (First_Param).Get_String);\n      -- Get the text.\n      return New_String_Mal_Type (Ada.Text_IO.Get_Line);\n   end Read_Line;\n\n\n   function Slurp (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      First_Param : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      declare\n         Unquoted_Str : String := Deref_String (First_Param).Get_String;\n         use Ada.Text_IO;\n         Fn : Ada.Text_IO.File_Type;\n         File_Str : Ada.Strings.Unbounded.Unbounded_String :=\n           Ada.Strings.Unbounded.Null_Unbounded_String;\n         I : Natural := 0;\n      begin\n         Ada.Text_IO.Open (Fn, In_File, Unquoted_Str);\n         while not End_Of_File (Fn) loop\n            declare\n               Line_Str : constant String := Get_Line (Fn);\n            begin\n               if Line_Str'Length > 0 then\n                  Ada.Strings.Unbounded.Append (File_Str, Line_Str);\n                  Ada.Strings.Unbounded.Append (File_Str, Ada.Characters.Latin_1.LF);\n               end if;\n            end;\n         end loop;\n         Ada.Text_IO.Close (Fn);\n         return New_String_Mal_Type (Ada.Strings.Unbounded.To_String (File_Str));\n      end;\n   end Slurp;\n\n\n   function Conj (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      Rest_List : List_Mal_Type;\n      First_Param, Res : Mal_Handle;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      Rest_List := Deref_List (Cdr (Rest_List)).all;\n\n      -- Is this a List or a Vector?\n      case Deref_List (First_Param).Get_List_Type is\n         when List_List =>\n            Res := Copy (First_Param);\n            while not Is_Null (Rest_List) loop\n               Res := Prepend (To_List => Deref_List (Res).all, Op => Car (Rest_List));\n               Rest_List := Deref_List (Cdr (Rest_List)).all;\n            end loop;\n            return Res;\n         when Vector_List =>\n            Res := Copy (First_Param);\n            while not Is_Null (Rest_List) loop\n               Vector.Append (Vector.Deref_Vector (Res).all, Car (Rest_List));\n               Rest_List := Deref_List (Cdr (Rest_List)).all;\n            end loop;\n            return Res;\n         when Hashed_List => raise Runtime_Exception with \"Conj on Hashed_Map\";\n      end case;\n   end Conj;\n\n\n   function Seq (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param, Res : Mal_Handle;\n   begin\n      First_Param := Car (Deref_List (Rest_Handle).all);\n      case Deref (First_Param).Sym_Type is\n         when Nil => return First_Param;\n         when List =>\n            case Deref_List (First_Param).Get_List_Type is\n               when List_List =>\n                  if Is_Null (Deref_List (First_Param).all) then\n                     return New_Nil_Mal_Type;\n                  else\n                     return First_Param;\n                  end if;\n               when Vector_List =>\n                  if Vector.Is_Null (Vector.Deref_Vector (First_Param).all) then\n                     return New_Nil_Mal_Type;\n                  else\n                     return Vector.Duplicate (Vector.Deref_Vector (First_Param).all);\n                  end if;\n               when others => raise Runtime_Exception;\n            end case;\n         when Str =>\n            declare\n               Param_Str : String := Deref_String (First_Param).Get_String;\n               String1 : String (1 .. 1);\n               L_Ptr : List_Ptr;\n            begin\n               if Param_Str'Length = 0 then\n                  return New_Nil_Mal_Type; -- \"\"\n               else\n                  Res := New_List_Mal_Type (List_List);\n                  L_Ptr := Deref_List (Res);\n                  for I in Param_Str'First .. Param_Str'Last loop\n                     String1 (1) := Param_Str (I);\n                     Append (L_Ptr.all, New_String_Mal_Type (String1));\n                  end loop;\n                  return Res;\n               end if;\n            end;\n         when others => raise Runtime_Exception;\n      end case;\n   end Seq;\n\n\n   Start_Time : Ada.Calendar.Time := Ada.Calendar.Clock;\n\n   function Time_Ms (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      D : Duration;\n      use Ada.Calendar;\n   begin\n      D := Clock - Start_Time;  -- seconds\n      D := D * 1000.0;  -- milli-seconds\n      return New_Int_Mal_Type (Integer (D));  -- ms rounded to the nearest one\n   end Time_Ms;\n\n\n   procedure Init (Repl_Env : Envs.Env_Handle) is\n   begin\n\n      Envs.Set (Repl_Env, \"*host-language*\", Types.New_String_Mal_Type (\"Ada\"));\n\n      Envs.Set (Repl_Env,\n           \"true?\",\n           New_Func_Mal_Type (\"true?\", Is_True'access));\n\n      Envs.Set (Repl_Env,\n           \"false?\",\n           New_Func_Mal_Type (\"false?\", Is_False'access));\n\n      Envs.Set (Repl_Env,\n           \"meta\",\n           New_Func_Mal_Type (\"meta\", Meta'access));\n\n      Envs.Set (Repl_Env,\n           \"with-meta\",\n           New_Func_Mal_Type (\"with-meta\", With_Meta'access));\n\n      Envs.Set (Repl_Env,\n           \"nil?\",\n           New_Func_Mal_Type (\"nil?\", Is_Nil'access));\n\n      Envs.Set (Repl_Env,\n           \"throw\",\n           New_Func_Mal_Type (\"throw\", Throw'access));\n\n      Envs.Set (Repl_Env,\n           \"atom\",\n           New_Func_Mal_Type (\"atom\", New_Atom'access));\n\n      Envs.Set (Repl_Env,\n           \"atom?\",\n           New_Func_Mal_Type (\"atom?\", Is_Atom'access));\n\n      Envs.Set (Repl_Env,\n           \"deref\",\n           New_Func_Mal_Type (\"deref\", Deref_Atm'access));\n\n      Envs.Set (Repl_Env,\n           \"reset!\",\n           New_Func_Mal_Type (\"reset!\", Reset'access));\n\n      Envs.Set (Repl_Env,\n           \"swap!\",\n           New_Func_Mal_Type (\"swap!\", Swap'access));\n\n      Envs.Set (Repl_Env,\n           \"list\",\n           New_Func_Mal_Type (\"list\", New_List'access));\n\n      Envs.Set (Repl_Env,\n           \"list?\",\n           New_Func_Mal_Type (\"list?\", Is_List'access));\n\n      Envs.Set (Repl_Env,\n           \"vec\",\n           New_Func_Mal_Type (\"vec\", Vec'access));\n\n      Envs.Set (Repl_Env,\n           \"vector\",\n           New_Func_Mal_Type (\"vector\", New_Vector'access));\n\n      Envs.Set (Repl_Env,\n           \"vector?\",\n           New_Func_Mal_Type (\"vector?\", Is_Vector'access));\n\n      Envs.Set (Repl_Env,\n           \"hash-map\",\n           New_Func_Mal_Type (\"hash-map\", New_Map'access));\n\n      Envs.Set (Repl_Env,\n           \"assoc\",\n           New_Func_Mal_Type (\"assoc\", Assoc'access));\n\n      Envs.Set (Repl_Env,\n           \"dissoc\",\n           New_Func_Mal_Type (\"dissoc\", Dis_Assoc'access));\n\n      Envs.Set (Repl_Env,\n           \"get\",\n           New_Func_Mal_Type (\"get\", Get_Key'access));\n\n      Envs.Set (Repl_Env,\n           \"keys\",\n           New_Func_Mal_Type (\"keys\", All_Keys'access));\n\n      Envs.Set (Repl_Env,\n           \"vals\",\n           New_Func_Mal_Type (\"vals\", All_Values'access));\n\n      Envs.Set (Repl_Env,\n           \"map?\",\n           New_Func_Mal_Type (\"map?\", Is_Map'access));\n\n      Envs.Set (Repl_Env,\n           \"contains?\",\n           New_Func_Mal_Type (\"contains?\", Contains_Key'access));\n\n      Envs.Set (Repl_Env,\n           \"sequential?\",\n           New_Func_Mal_Type (\"sequential?\", Is_Sequential'access));\n\n      Envs.Set (Repl_Env,\n           \"empty?\",\n           New_Func_Mal_Type (\"empty?\", Is_Empty'access));\n\n      Envs.Set (Repl_Env,\n           \"count\",\n           New_Func_Mal_Type (\"count\", Count'access));\n\n      Envs.Set (Repl_Env,\n           \"cons\",\n           New_Func_Mal_Type (\"cons\", Cons'access));\n\n      Envs.Set (Repl_Env,\n           \"concat\",\n           New_Func_Mal_Type (\"concat\", Concat'access));\n\n      Envs.Set (Repl_Env,\n           \"first\",\n           New_Func_Mal_Type (\"first\", First'access));\n\n      Envs.Set (Repl_Env,\n           \"rest\",\n           New_Func_Mal_Type (\"rest\", Rest'access));\n\n      Envs.Set (Repl_Env,\n           \"nth\",\n           New_Func_Mal_Type (\"nth\", Nth'access));\n\n      Envs.Set (Repl_Env,\n           \"map\",\n           New_Func_Mal_Type (\"map\", Map'access));\n\n      Envs.Set (Repl_Env,\n           \"apply\",\n           New_Func_Mal_Type (\"apply\", Apply'access));\n\n      Envs.Set (Repl_Env,\n           \"symbol\",\n           New_Func_Mal_Type (\"symbol\", Symbol'access));\n\n      Envs.Set (Repl_Env,\n           \"symbol?\",\n           New_Func_Mal_Type (\"symbol?\", Is_Symbol'access));\n\n      Envs.Set (Repl_Env,\n           \"string?\",\n           New_Func_Mal_Type (\"string?\", Is_String'access));\n\n      Envs.Set (Repl_Env,\n           \"keyword\",\n           New_Func_Mal_Type (\"keyword\", Keyword'access));\n\n      Envs.Set (Repl_Env,\n           \"keyword?\",\n           New_Func_Mal_Type (\"keyword?\", Is_Keyword'access));\n\n      Envs.Set (Repl_Env,\n           \"number?\",\n           New_Func_Mal_Type (\"number?\", Is_Number'access));\n\n      Envs.Set (Repl_Env,\n           \"fn?\",\n           New_Func_Mal_Type (\"fn?\", Is_Fn'access));\n\n      Envs.Set (Repl_Env,\n           \"macro?\",\n           New_Func_Mal_Type (\"macro?\", Is_Macro'access));\n\n      Envs.Set (Repl_Env,\n           \"pr-str\",\n           New_Func_Mal_Type (\"pr-str\", Pr_Str'access));\n\n      Envs.Set (Repl_Env,\n           \"str\",\n           New_Func_Mal_Type (\"str\", Str'access));\n\n      Envs.Set (Repl_Env,\n           \"prn\",\n           New_Func_Mal_Type (\"prn\", Prn'access));\n\n      Envs.Set (Repl_Env,\n           \"println\",\n           New_Func_Mal_Type (\"println\", Println'access));\n\n      Envs.Set (Repl_Env,\n           \"read-string\",\n           New_Func_Mal_Type (\"read-string\", Read_String'access));\n\n      Envs.Set (Repl_Env,\n           \"readline\",\n           New_Func_Mal_Type (\"readline\", Read_Line'access));\n\n      Envs.Set (Repl_Env,\n           \"slurp\",\n           New_Func_Mal_Type (\"slurp\", Slurp'access));\n\n      Envs.Set (Repl_Env,\n           \"conj\",\n           New_Func_Mal_Type (\"conj\", Conj'access));\n\n      Envs.Set (Repl_Env,\n           \"seq\",\n           New_Func_Mal_Type (\"seq\", Seq'access));\n\n      Envs.Set (Repl_Env,\n           \"time-ms\",\n           New_Func_Mal_Type (\"time-ms\", Time_Ms'access));\n\n      Envs.Set (Repl_Env,\n           \"+\",\n           New_Func_Mal_Type (\"+\", Plus'access));\n\n      Envs.Set (Repl_Env,\n           \"-\",\n           New_Func_Mal_Type (\"-\", Minus'access));\n\n      Envs.Set (Repl_Env,\n           \"*\",\n           New_Func_Mal_Type (\"*\", Mult'access));\n\n      Envs.Set (Repl_Env,\n           \"/\",\n           New_Func_Mal_Type (\"/\", Divide'access));\n\n      Envs.Set (Repl_Env,\n           \"<\",\n           New_Func_Mal_Type (\"<\", LT'access));\n\n      Envs.Set (Repl_Env,\n           \"<=\",\n           New_Func_Mal_Type (\"<=\", LTE'access));\n\n      Envs.Set (Repl_Env,\n           \">\",\n           New_Func_Mal_Type (\">\", GT'access));\n\n      Envs.Set (Repl_Env,\n           \">=\",\n           New_Func_Mal_Type (\">=\", GTE'access));\n\n      Envs.Set (Repl_Env,\n           \"=\",\n           New_Func_Mal_Type (\"=\", EQ'access));\n\n   end Init;\n\n\nend Core;\n"
  },
  {
    "path": "impls/ada/core.ads",
    "content": "with Envs;\n\npackage Core is\n\n   -- Init puts core functions into a new Env.\n   procedure Init (Repl_Env : Envs.Env_Handle);\n\n   Evaluation_Error : exception;\n\nend Core;\n"
  },
  {
    "path": "impls/ada/envs.adb",
    "content": "with Ada.Text_IO;\nwith Types;\nwith Unchecked_Deallocation;\n\npackage body Envs is\n\n\n   function Is_Null (E : Env_Handle) return Boolean is\n      use Smart_Pointers;\n   begin\n     return E = Null_Env_Handle;\n   end Is_Null;\n\n\n   function New_Env (Outer : Env_Handle := Null_Env_Handle) return Env_Handle is\n      use Smart_Pointers;\n      Level : Natural;\n   begin\n      if Is_Null (Outer) then\n         Level := 0;\n      else\n         Level := Deref (Outer).Level + 1;\n      end if;\n      if Debug then\n         Ada.Text_IO.Put_Line\n           (\"Envs: Creating at level \" & Natural'Image (Level));\n      end if;\n      return Env_Handle (Smart_Pointers.New_Ptr (new Env'\n               (Base_Class with The_Map => String_Mal_Hash.Empty_Map,\n                Outer_Env => Outer,\n                Level => Level)));\n   end New_Env;\n\n\n   procedure Set\n     (E : Env_Handle;\n      Key : String;\n      Elem : Smart_Pointers.Smart_Pointer) is\n   begin\n      if Debug then\n         Ada.Text_IO.Put_Line\n           (\"Envs: Setting \" & Key &\n            \" to \" & Types.Deref (Elem).To_String &\n            \" at level \" & Natural'Image (Deref (E).Level));\n      end if;\n      String_Mal_Hash.Include\n        (Container => Deref (E).The_Map,\n         Key       => Ada.Strings.Unbounded.To_Unbounded_String (Key),\n         New_Item  => Elem);\n   end Set;\n\n\n   function Get (E : Env_Handle; Key: String)\n   return Smart_Pointers.Smart_Pointer is\n\n      use String_Mal_Hash;\n      C : Cursor;\n\n   begin\n\n      if Debug then\n         Ada.Text_IO.Put_Line\n           (\"Envs: Finding \" & Key &\n            \" at level \" & Natural'Image (Deref (E).Level));\n      end if;\n\n      C := Find (Deref (E).The_Map,\n                 Ada.Strings.Unbounded.To_Unbounded_String (Key));\n\n      if C = No_Element then\n\n         if Is_Null (Deref (E).Outer_Env) then\n            raise Not_Found;\n         else\n            return Get (Deref (E).Outer_Env, Key);\n         end if;\n\n      else\n         return Element (C);\n      end if;\n\n   end Get;\n\n\n   procedure Set_Outer\n     (E : Env_Handle; Outer_Env : Env_Handle) is\n   begin\n      -- Attempt to avoid making loops.\n      if Deref (E).Level /= 0 then\n         Deref (E).Outer_Env := Outer_Env;\n      end if;\n   end Set_Outer;\n\n\n   function To_String (E : Env_Handle) return String is\n      use String_Mal_Hash, Ada.Strings.Unbounded;\n      C : Cursor;\n      Res : Unbounded_String;\n   begin\n      C := First (Deref (E).The_Map);\n      while C /= No_Element loop\n         Append (Res, Key (C) &  \" => \" & Types.To_String (Types.Deref (Element (C)).all) & \", \");\n         C := Next (C);\n      end loop;\n      return To_String (Res);\n   end To_String;\n\n\n   -- Sym and Exprs are lists.  Bind Sets Keys in Syms to the corresponding\n   -- expression in Exprs.  Returns true if all the parameters were bound.\n   function Bind (Env : Env_Handle; Syms, Exprs : Types.List_Mal_Type)\n   return Boolean is\n      use Types;\n      S, Expr : List_Mal_Type;\n      First_Sym : Sym_Ptr;\n   begin\n      S := Syms;\n      Expr := Exprs;\n      while not Is_Null (S) loop\n\n         First_Sym := Deref_Sym (Car (S));\n\n         if First_Sym.Get_Sym = \"&\" then\n            S := Deref_List (Cdr (S)).all;\n            First_Sym := Deref_Sym (Car (S));\n            Set (Env, First_Sym.Get_Sym, New_List_Mal_Type (Expr));\n            return True;\n         end if;\n\n         Set (Env, First_Sym.Get_Sym, Car (Expr));\n         S := Deref_List (Cdr (S)).all;\n         exit when Is_Null (Expr);\n         Expr := Deref_List (Cdr (Expr)).all;\n\n      end loop;\n      return Is_Null (S);\n   end Bind;\n\n\n   function Deref (SP : Env_Handle) return Env_Ptr is\n   begin\n      return Env_Ptr (Smart_Pointers.Deref (Smart_Pointers.Smart_Pointer (SP)));\n   end Deref;\n\n\nend Envs;\n"
  },
  {
    "path": "impls/ada/envs.ads",
    "content": "with Ada.Containers.Hashed_Maps;\nwith Ada.Strings.Unbounded.Hash;\nwith Smart_Pointers;\nlimited with Types;\n\npackage Envs is\n\n   type Env_Handle is private;\n\n   Null_Env_Handle : constant Env_Handle;\n\n   function New_Env (Outer : Env_Handle := Null_Env_Handle) return Env_Handle;\n\n   -- Set adds an element to the environment E.\n   procedure Set\n     (E : Env_Handle;\n      Key : String;\n      Elem : Smart_Pointers.Smart_Pointer);\n\n   -- Get finds a key in the E env.  If it can't be found it looks\n   -- in an outer env.  If it runs out of envs, Not Found is raised.\n   function Get (E : Env_Handle; Key : String) return Smart_Pointers.Smart_Pointer;\n\n   Not_Found : exception;\n\n   procedure Set_Outer\n     (E : Env_Handle; Outer_Env : Env_Handle);\n\n   -- Sym and Exprs are lists.  Bind Sets Keys in Syms to the corresponding\n   -- expression in Exprs.  Returns true if all the parameters were bound.\n   function Bind (Env : Env_Handle; Syms, Exprs : Types.List_Mal_Type)\n   return Boolean;\n\n   function To_String (E : Env_Handle) return String;\n\n   Debug : Boolean := False;\n\nprivate\n\n   type Env_Handle is new Smart_Pointers.Smart_Pointer;\n\n   Null_Env_Handle : constant Env_Handle :=\n     Env_Handle (Smart_Pointers.Null_Smart_Pointer);\n\n   function Is_Null (E : Env_Handle) return Boolean;\n\n   package String_Mal_Hash is new Ada.Containers.Hashed_Maps\n     (Key_Type        => Ada.Strings.Unbounded.Unbounded_String,\n      Element_Type    => Smart_Pointers.Smart_Pointer,\n      Hash            => Ada.Strings.Unbounded.Hash,\n      Equivalent_Keys => Ada.Strings.Unbounded.\"=\",\n      \"=\"             => Smart_Pointers.\"=\");\n\n   type Env is new Smart_Pointers.Base_Class with record\n      The_Map : String_Mal_Hash.Map;\n      Outer_Env : Env_Handle;\n      Level: Natural;\n   end record;\n\n   type Env_Ptr is access all Env;\n\n   function Deref (SP : Env_Handle) return Env_Ptr;\n\nend Envs;\n"
  },
  {
    "path": "impls/ada/eval_callback.ads",
    "content": "with Envs;\nwith Types;\n\npackage Eval_Callback is\n\n   type Eval_Func is access\n     function (MH : Types.Mal_Handle; Env : Envs.Env_Handle) return Types.Mal_Handle;\n\n   Eval : Eval_Func;\n\nend Eval_Callback;\n"
  },
  {
    "path": "impls/ada/printer.adb",
    "content": "package body Printer is\n\n   function Pr_Str (M : Types.Mal_Handle) return String is\n   begin\n     if Types.Is_Null (M) then\n        return \"\";\n     else\n        return Types.To_String (Types.Deref (M).all);\n     end if;\n   end Pr_Str;\n\nend Printer;\n"
  },
  {
    "path": "impls/ada/printer.ads",
    "content": "with Types;\n\npackage Printer is\n\n   function Pr_Str (M : Types.Mal_Handle) return String;\n\nend Printer;\n"
  },
  {
    "path": "impls/ada/reader.adb",
    "content": "with Ada.IO_Exceptions;\nwith Ada.Characters.Latin_1;\nwith Ada.Exceptions;\nwith Ada.Strings.Maps.Constants;\nwith Ada.Strings.Unbounded;\nwith Ada.Text_IO;\nwith Smart_Pointers;\nwith Types.Vector;\nwith Types.Hash_Map;\n\npackage body Reader is\n\n   use Types;\n\n   package ACL renames Ada.Characters.Latin_1;\n\n   type Lexemes is (Ignored_Tok,\n                    Start_List_Tok, Start_Vector_Tok, Start_Hash_Tok,\n                    Meta_Tok, Deref_Tok,\n                    Quote_Tok, Quasi_Quote_Tok, Splice_Unq_Tok, Unquote_Tok,\n                    Int_Tok, Float_Tok,\n                    Str_Tok, Sym_Tok);\n\n   type Token (ID : Lexemes := Ignored_Tok) is record\n      case ID is\n         when Int_Tok =>\n            Int_Val : Mal_Integer;\n         when Float_Tok =>\n            Float_Val : Mal_Float;\n         when Str_Tok | Sym_Tok =>\n            Start_Char, Stop_Char : Natural;\n         when others => null;\n      end case;\n   end record;\n\n   Lisp_Whitespace : constant Ada.Strings.Maps.Character_Set :=\n     Ada.Strings.Maps.To_Set\n       (ACL.HT & ACL.LF & ACL.CR & ACL.Space & ACL.Comma);\n\n   -- [^\\s\\[\\]{}('\"`,;)]\n   Terminator_Syms : Ada.Strings.Maps.Character_Set :=\n     Ada.Strings.Maps.\"or\"\n       (Lisp_Whitespace, \n        Ada.Strings.Maps.To_Set (\"[]{}('\"\"`,;)\"));\n\n   -- The unterminated string error\n   String_Error : exception;\n\n\n   function Convert_String (S : String) return String is\n      use Ada.Strings.Unbounded;\n      Res : Unbounded_String;\n      I : Positive;\n      Str_Last : Natural;\n   begin\n      Str_Last := S'Last;\n      I := S'First;\n      while I <= Str_Last loop\n         if S (I) = '\\' then\n            if I+1 > Str_Last then\n               Append (Res, S (I));\n               I := I + 1;\n            elsif S (I+1) = 'n' then\n               Append (Res, Ada.Characters.Latin_1.LF);\n               I := I + 2;\n            elsif S (I+1) = '\"' then\n               Append (Res, S (I+1));\n               I := I + 2;\n            elsif S (I+1) = '\\' then\n               Append (Res, S (I+1));\n               I := I + 2;\n            else\n               Append (Res, S (I));\n               I := I + 1;\n            end if;\n         else\n            Append (Res, S (I));\n            I := I + 1;\n         end if;\n      end loop;\n      return To_String (Res);\n   end Convert_String;\n\n   Str_Len : Natural := 0;\n   Saved_Line : Ada.Strings.Unbounded.Unbounded_String;\n   Char_To_Read : Natural := 1;\n\n   function Get_Token return Token is\n      Res : Token;\n      I, J : Natural;\n      use Ada.Strings.Unbounded;\n   begin\n\n      <<Tail_Call_Opt>>\n\n      -- Skip over whitespace...\n      I := Char_To_Read;\n      while I <= Str_Len and then\n            Ada.Strings.Maps.Is_In (Element (Saved_Line, I), Lisp_Whitespace) loop\n         I := I + 1;\n      end loop;\n\n      -- Filter out lines consisting of only whitespace\n      if I > Str_Len then\n         return (ID => Ignored_Tok);\n      end if;\n\n      J := I;\n\n      case Element (Saved_Line, J) is\n\n         when ''' => Res := (ID => Quote_Tok); Char_To_Read := J+1;\n\n         when '`' => Res := (ID => Quasi_Quote_Tok); Char_To_Read := J+1;\n\n         when '~' => -- Tilde\n\n            if J+1 <= Str_Len and then Element (Saved_Line, J+1) = '@' then\n               Res := (ID => Splice_Unq_Tok);\n               Char_To_Read := J+2;\n            else\n               -- Just a Tilde\n               Res := (ID => Unquote_Tok);\n               Char_To_Read := J+1;\n            end if;\n\n         when '(' => Res := (ID => Start_List_Tok); Char_To_Read := J+1;\n         when '[' => Res := (ID => Start_Vector_Tok); Char_To_Read := J+1;\n         when '{' => Res := (ID => Start_Hash_Tok); Char_To_Read := J+1;\n\n         when '^' => Res := (ID => Meta_Tok); Char_To_Read := J+1;\n         when '@' => Res := (ID => Deref_Tok); Char_To_Read := J+1;\n\n         when ']' | '}' | ')' =>\n            \n            Res := (ID => Sym_Tok, Start_Char => J, Stop_Char => J);\n            Char_To_Read := J+1;\n\n         when '\"' => -- a string\n\n            loop\n               if Str_Len <= J then\n                  raise String_Error;\n               end if;\n               J := J + 1;\n               exit when Element (Saved_Line, J) = '\"';\n               if Element (Saved_Line, J) = '\\' then\n                  J := J + 1;\n               end if;\n            end loop;\n\n            Res := (ID => Str_Tok, Start_Char => I, Stop_Char => J);\n            Char_To_Read := J + 1;\n\n         when ';' => -- a comment\n\n            -- Read to the end of the line or until\n            -- the saved_line string is exhausted.\n            -- NB if we reach the end we don't care\n            -- what the last char was.\n            while J < Str_Len and Element (Saved_Line, J) /= ACL.LF loop\n               J := J + 1;\n            end loop;\n            if J = Str_Len then\n               Res := (ID => Ignored_Tok);\n            else\n               Char_To_Read := J + 1;\n               -- was: Res := Get_Token;\n               goto Tail_Call_Opt;\n            end if;\n\n         when others => -- an atom\n\n            while J <= Str_Len and then\n               not Ada.Strings.Maps.Is_In (Element (Saved_Line, J), Terminator_Syms) loop\n               J := J + 1;\n            end loop;\n\n            -- Either we ran out of string or\n            -- the one at J was the start of a new token\n            Char_To_Read := J;\n            J := J - 1;\n\n            declare\n               Dots : Natural;\n               All_Digits : Boolean;\n            begin\n               -- check if all digits or .\n               Dots := 0;\n               All_Digits := True;\n               for K in I .. J loop\n                  if (K = I and K /= J) and then Element (Saved_Line, K) = '-' then\n                     null;\n                  elsif Element (Saved_Line, K) = '.' then\n                     Dots := Dots + 1; \n                  elsif not (Element (Saved_Line, K) in '0' .. '9') then\n                     All_Digits := False;\n                     exit;\n                  end if;\n               end loop;\n\n               if All_Digits then\n                  if Dots = 0 then\n                     Res :=\n                       (ID => Int_Tok,\n                        Int_Val => Mal_Integer'Value (Slice (Saved_Line, I, J)));\n                  elsif Dots = 1 then\n                     Res :=\n                       (ID => Float_Tok,\n                        Float_Val => Mal_Float'Value (Slice (Saved_Line, I, J)));\n                  else\n                     Res := (ID => Sym_Tok, Start_Char => I, Stop_Char => J);\n                  end if;\n               else\n                  Res := (ID => Sym_Tok, Start_Char => I, Stop_Char => J);\n               end if;\n\n            end;\n\n      end case;\n\n      return Res;\n\n   end Get_Token;\n\n\n   function Read_List (LT : Types.List_Types)\n   return Types.Mal_Handle is\n\n      MTA : Mal_Handle;\n\n   begin\n\n      MTA := Read_Form;\n\n      declare\n         List_SP : Mal_Handle;\n         List_P : List_Class_Ptr;\n         Close : String (1..1) := (1 => Types.Closing (LT));\n      begin\n\n         case LT is\n            when List_List   => List_SP := New_List_Mal_Type (List_Type => LT);\n            when Vector_List => List_SP := Vector.New_Vector_Mal_Type;\n            when Hashed_List => List_SP := Hash_Map.New_Hash_Map_Mal_Type;\n         end case;\n\n         -- Need to append to a variable so...\n         List_P := Deref_List_Class (List_SP);\n\n         loop\n\n            if Is_Null (MTA) then\n               return New_Error_Mal_Type (Str => \"expected '\" & Close & \"', got EOF\");\n            end if;\n\n            exit when Deref (MTA).Sym_Type = Sym and then\n                      Symbol_Mal_Type (Deref (MTA).all).Get_Sym = Close;\n\n            Append (List_P.all, MTA);\n\n            MTA := Read_Form;\n\n         end loop;\n\n         return List_SP;\n\n      end;\n\n   end Read_List;\n\n\n   function Read_Form return Types.Mal_Handle is\n      Tok : Token;\n      MTS : Mal_Handle;\n      use Ada.Strings.Unbounded;\n   begin\n\n      Tok := Get_Token;\n\n      case Tok.ID is\n\n         when Ignored_Tok => return Smart_Pointers.Null_Smart_Pointer;\n\n         when Int_Tok => return New_Int_Mal_Type (Tok.Int_Val);\n\n         when Float_Tok => return New_Float_Mal_Type (Tok.Float_Val);\n\n         when Start_List_Tok => return Read_List (List_List);\n\n         when Start_Vector_Tok => return Read_List (Vector_List);\n\n         when Start_Hash_Tok => return Read_List (Hashed_List);\n\n         when Meta_Tok =>\n            \n            declare\n               Meta, Obj : Mal_Handle;\n            begin\n               Meta := Read_Form;\n               Obj := Read_Form;\n               return Make_New_List\n                        ((1 => New_Symbol_Mal_Type (\"with-meta\"),\n                          2 => Obj,\n                          3 => Meta));\n            end;\n\n         when Deref_Tok =>\n\n            return Make_New_List\n                     ((1 => New_Symbol_Mal_Type (\"deref\"),\n                       2 => Read_Form));\n\n         when Quote_Tok =>\n\n            return Make_New_List\n                     ((1 => New_Symbol_Mal_Type (\"quote\"),\n                       2 => Read_Form));\n\n         when Quasi_Quote_Tok =>\n\n            return Make_New_List\n                     ((1 => New_Symbol_Mal_Type (\"quasiquote\"),\n                       2 => Read_Form));\n\n         when Splice_Unq_Tok =>\n\n            return Make_New_List\n                     ((1 =>  New_Symbol_Mal_Type (\"splice-unquote\"),\n                       2 => Read_Form));\n\n         when Unquote_Tok =>\n\n            return Make_New_List\n                     ((1 => New_Symbol_Mal_Type (\"unquote\"),\n                       2 => Read_Form));\n\n         when Str_Tok =>\n\n            -- +/-1 strips out the double quotes.\n            -- Convert_String converts backquoted charaters to raw format.\n            return New_String_Mal_Type\n                     (Convert_String\n                       (Slice (Saved_Line, Tok.Start_Char + 1, Tok.Stop_Char - 1)));\n\n         when Sym_Tok =>\n\n            -- Mal interpreter is required to know about true, false and nil.\n            declare\n               S : String := Slice (Saved_Line, Tok.Start_Char, Tok.Stop_Char);\n            begin\n               if S = \"true\" then\n                  return New_Bool_Mal_Type (True);\n               elsif S = \"false\" then\n                  return New_Bool_Mal_Type (False);\n               elsif S = \"nil\" then\n                  return New_Nil_Mal_Type;\n               else\n                  return New_Symbol_Mal_Type (S);\n               end if;\n            end;\n\n      end case;\n\n   end Read_Form;\n\n\n   procedure Lex_Init (S : String) is\n   begin\n      Str_Len := S'Length;\n      Saved_Line := Ada.Strings.Unbounded.To_Unbounded_String (S);\n      Char_To_Read := 1;\n   end Lex_Init;\n\n\n   function Read_Str (S : String) return Types.Mal_Handle is\n      I, Str_Len : Natural := S'Length;\n   begin\n\n      Lex_Init (S);\n\n      return Read_Form;\n\n   exception\n      when String_Error =>\n        return New_Error_Mal_Type (Str => \"expected '\"\"', got EOF\");\n   end Read_Str;\n   \n\nend Reader;\n"
  },
  {
    "path": "impls/ada/reader.ads",
    "content": "with Types;\n\npackage Reader is\n\n   -- This is the Parser (returns an AST)\n   function Read_Str (S : String) return Types.Mal_Handle;\n\nprivate\n\n   procedure Lex_Init (S : String);\n\n   function Read_Form return Types.Mal_Handle;\n\nend Reader;\n"
  },
  {
    "path": "impls/ada/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/ada/smart_pointers.adb",
    "content": "with Ada.Unchecked_Deallocation;\n\npackage body Smart_Pointers is\n\n\n   function New_Ptr (Base_Class : Base_Class_Accessor) return Smart_Pointer is\n   begin\n      return Smart_Pointer'\n         (Ada.Finalization.Controlled with Pointer => Base_Class);\n   end New_Ptr;\n\n\n   function Deref (Ptr : Smart_Pointer) return Base_Class_Accessor is\n   begin\n      return Ptr.Pointer;\n   end Deref;\n\n\n   overriding procedure Adjust (Object : in out Smart_Pointer) is\n   begin\n      if Object.Pointer /= null then\n         Object.Pointer.Ref_Count := Object.Pointer.Ref_Count + 1;\n      end if;\n   end Adjust;\n\n\n   procedure Free is\n     new Ada.Unchecked_Deallocation (Base_Class'Class, Base_Class_Accessor);\n\n   overriding procedure Finalize (Object : in out Smart_Pointer) is\n   begin\n      if Object.Pointer /= null then\n         if Object.Pointer.Ref_Count > 0 then\n            Object.Pointer.Ref_Count := Object.Pointer.Ref_Count - 1;\n            if Object.Pointer.Ref_Count = 0 then\n               Free (Object.Pointer);\n            end if;\n         end if;\n      end if;\n   end Finalize;\n\n\n   function Is_Null (Ptr : Smart_Pointer) return Boolean is\n   begin\n      return Ptr = Null_Smart_Pointer;\n   end Is_Null;\n\n\nend Smart_Pointers;\n"
  },
  {
    "path": "impls/ada/smart_pointers.ads",
    "content": "with Ada.Finalization;\n\npackage Smart_Pointers is\n\n   -- Classes we want to track derrive from Base Class.\n   type Base_Class is abstract tagged private;\n\n   type Base_Class_Accessor is access Base_Class'Class;\n\n\n   type Smart_Pointer is private;\n\n   function New_Ptr (Base_Class : Base_Class_Accessor) return Smart_Pointer;\n\n   function Deref (Ptr : Smart_Pointer) return Base_Class_Accessor;\n\n   Null_Smart_Pointer : constant Smart_Pointer;\n\n   function Is_Null (Ptr : Smart_Pointer) return Boolean;\n\nprivate\n\n   type Base_Class is abstract tagged record\n      Ref_Count : Natural := 1;\n   end record;\n\n\n   type Smart_Pointer is new Ada.Finalization.Controlled with record\n      Pointer : Base_Class_Accessor;\n   end record;\n\n   overriding procedure Adjust (Object : in out Smart_Pointer);\n\n   overriding procedure Finalize (Object : in out Smart_Pointer);\n\n   Null_Smart_Pointer : constant Smart_Pointer :=\n      (Ada.Finalization.Controlled with Pointer => null);\n\nend Smart_Pointers;\n"
  },
  {
    "path": "impls/ada/step0_repl.adb",
    "content": "with Ada.Text_IO;\n\nprocedure Step0_Repl is\n\n   function Read (Param : String) return String is\n   begin\n      return Param;\n   end Read;\n\n   function Eval (Param : String) return String is\n   begin\n      return Param;\n   end Eval;\n\n   function Print (Param : String) return String is\n   begin\n      return Param;\n   end Print;\n\n   function Rep (Param : String) return String is\n      Read_Str : String := Read (Param);\n      Eval_Str : String := Eval (Read_Str);\n      Print_Str : String := Print (Eval_Str);\n   begin\n      return Print_Str;\n   end Rep;\n\nbegin\n   loop\n      Ada.Text_IO.Put (\"user> \");\n      exit when Ada.Text_IO.End_Of_File;\n      Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line));\n   end loop;\nend Step0_Repl;\n"
  },
  {
    "path": "impls/ada/step1_read_print.adb",
    "content": "with Ada.Text_IO;\nwith Printer;\nwith Reader;\nwith Types;\n\nprocedure Step1_Read_Print is\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n   function Eval (Param : Types.Mal_Handle) return Types.Mal_Handle is\n   begin\n      return Param;\n   end Eval;\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\nbegin\n   loop\n      Ada.Text_IO.Put (\"user> \");\n      exit when Ada.Text_IO.End_Of_File;\n      Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line));\n   end loop;\nend Step1_Read_Print;\n"
  },
  {
    "path": "impls/ada/step2_eval.adb",
    "content": "with Ada.Containers.Hashed_Maps;\nwith Ada.Strings.Unbounded.Hash;\nwith Ada.Text_IO;\nwith Ada.Exceptions;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step2_Eval is\n\n   use Types;\n\n   -- primitive functions on Smart_Pointer,\n   function \"+\" is new Arith_Op (\"+\", \"+\");\n   function \"-\" is new Arith_Op (\"-\", \"-\");\n   function \"*\" is new Arith_Op (\"*\", \"*\");\n   function \"/\" is new Arith_Op (\"/\", \"/\");\n\n   -- Take a list with two parameters and produce a single result\n   -- using the Op access-to-function parameter.\n   function Reduce2\n     (Op : Binary_Func_Access; LH : Mal_Handle)\n   return Mal_Handle is\n      Left, Right : Mal_Handle;\n      L, Rest_List : List_Mal_Type;\n   begin\n      L := Deref_List (LH).all;\n      Left := Car (L);\n      Rest_List := Deref_List (Cdr (L)).all;\n      Right := Car (Rest_List);\n      return Op (Left, Right);\n   end Reduce2;\n\n\n   function Plus (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step2_Eval.\"+\"'Unrestricted_Access, Rest_Handle);\n   end Plus;\n\n\n   function Minus (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step2_Eval.\"-\"'Unrestricted_Access, Rest_Handle);\n   end Minus;\n\n\n   function Mult (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step2_Eval.\"*\"'Unrestricted_Access, Rest_Handle);\n   end Mult;\n\n\n   function Divide (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step2_Eval.\"/\"'Unrestricted_Access, Rest_Handle);\n   end Divide;\n\n\n   package String_Mal_Hash is new Ada.Containers.Hashed_Maps\n     (Key_Type        => Ada.Strings.Unbounded.Unbounded_String,\n      Element_Type    => Smart_Pointers.Smart_Pointer,\n      Hash            => Ada.Strings.Unbounded.Hash,\n      Equivalent_Keys => Ada.Strings.Unbounded.\"=\",\n      \"=\"             => Smart_Pointers.\"=\");\n\n   Not_Found : exception;\n\n   function Get (M : String_Mal_Hash.Map; K : String) return Mal_Handle is\n      use String_Mal_Hash;\n      C : Cursor;\n   begin\n      C := Find (M, Ada.Strings.Unbounded.To_Unbounded_String (K));\n      if C = No_Element then\n         raise Not_Found;\n      else\n         return Element (C);\n      end if;\n   end Get;\n\n\n   Repl_Env : String_Mal_Hash.Map;\n\n\n   function Eval (Param : Types.Mal_Handle; Env : String_Mal_Hash.Map)\n   return Types.Mal_Handle;\n\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : String_Mal_Hash.Map)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n\n   function Eval (Param : Mal_Handle; Env : String_Mal_Hash.Map)\n\t\t return Mal_Handle is\n      First_Elem : Mal_Handle;\n      Ast : Mal_Handle renames Param; --  Historic\n   begin\n      if Debug then\n         Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n      end if;\n\n      case Deref (Ast).Sym_Type is\n\n         when Sym =>\n\n            declare\n               Sym : Mal_String := Deref_Sym (Ast).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Ast;\n               else\n                  return Get (Env, Sym);\n               end if;\n            exception\n               when Not_Found =>\n                  raise Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         declare\n            Evaled_H, First_Param : Mal_Handle;\n            Evaled_List : List_Mal_Type;\n            Param_List : List_Mal_Type;\n         begin\n            Param_List := Deref_List (Param).all;\n\n            -- Deal with empty list..\n            if Param_List.Length = 0 then\n               return Param;\n            end if;\n\n            Evaled_H := Eval_Ast (Param, Env);\n            Evaled_List := Deref_List (Evaled_H).all;\n            First_Param := Car (Evaled_List);\n            return Call_Func (Deref_Func (First_Param).all, Cdr (Evaled_List));\n         end;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n\n   function Rep (Param : String; Env : String_Mal_Hash.Map) return String is\n     AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n     AST := Read (Param);\n\n     if Types.Is_Null (AST) then\n        return \"\";\n     else\n        Evaluated_AST := Eval (AST, Env);\n        return Print (Evaluated_AST);\n     end if;\n\n   end Rep;\n\nbegin\n\n   String_Mal_Hash.Include\n     (Container => Repl_Env,\n      Key       => Ada.Strings.Unbounded.To_Unbounded_String (\"+\"),\n      New_Item  => New_Func_Mal_Type (\"+\", Plus'Unrestricted_access));\n\n   String_Mal_Hash.Include\n     (Container => Repl_Env,\n      Key       => Ada.Strings.Unbounded.To_Unbounded_String (\"-\"),\n      New_Item  => New_Func_Mal_Type (\"-\", Minus'Unrestricted_access));\n\n   String_Mal_Hash.Include\n     (Container => Repl_Env,\n      Key       => Ada.Strings.Unbounded.To_Unbounded_String (\"*\"),\n      New_Item  => New_Func_Mal_Type (\"*\", Mult'Unrestricted_access));\n\n   String_Mal_Hash.Include\n     (Container => Repl_Env,\n      Key       => Ada.Strings.Unbounded.To_Unbounded_String (\"/\"),\n      New_Item  => New_Func_Mal_Type (\"/\", Divide'Unrestricted_access));\n\n   loop\n      begin\n         Ada.Text_IO.Put (\"user> \");\n         exit when Ada.Text_IO.End_Of_File;\n         Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n      exception\n         when E : others =>\n            Ada.Text_IO.Put_Line\n              (Ada.Text_IO.Standard_Error,\n               Ada.Exceptions.Exception_Information (E));\n      end;\n   end loop;\nend Step2_Eval;\n"
  },
  {
    "path": "impls/ada/step3_env.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step3_Env is\n\n   use Types;\n\n   -- primitive functions on Smart_Pointer,\n   function \"+\" is new Arith_Op (\"+\", \"+\");\n   function \"-\" is new Arith_Op (\"-\", \"-\");\n   function \"*\" is new Arith_Op (\"*\", \"*\");\n   function \"/\" is new Arith_Op (\"/\", \"/\");\n\n   -- Take a list with two parameters and produce a single result\n   -- using the Op access-to-function parameter.\n   function Reduce2\n     (Op : Binary_Func_Access; LH : Mal_Handle)\n   return Mal_Handle is\n      Left, Right : Mal_Handle;\n      L, Rest_List : List_Mal_Type;\n   begin\n      L := Deref_List (LH).all;\n      Left := Car (L);\n      Rest_List := Deref_List (Cdr (L)).all;\n      Right := Car (Rest_List);\n      return Op (Left, Right);\n   end Reduce2;\n\n\n   function Plus (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step3_Env.\"+\"'Unrestricted_Access, Rest_Handle);\n   end Plus;\n\n\n   function Minus (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step3_Env.\"-\"'Unrestricted_Access, Rest_Handle);\n   end Minus;\n\n\n   function Mult (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step3_Env.\"*\"'Unrestricted_Access, Rest_Handle);\n   end Mult;\n\n\n   function Divide (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n   begin\n       return Reduce2 (Step3_Env.\"/\"'Unrestricted_Access, Rest_Handle);\n   end Divide;\n\n\n   function Eval (Param : Types.Mal_Handle; Env : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected symbol as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Let_Processing (Args : List_Mal_Type; Env : Envs.Env_Handle)\n                          return Mal_Handle is\n      Defs, Expr, Res : Mal_Handle;\n      E : Envs.Env_Handle;\n   begin\n      E := Envs.New_Env (Env);\n      Defs := Car (Args);\n      Deref_List_Class (Defs).Add_Defs (E);\n      Expr := Car (Deref_List (Cdr (Args)).all);\n      Res := Eval (Expr, E);\n      return Res;\n   end Let_Processing;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n\n   function Eval (Param : Mal_Handle; Env : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Ast : Mal_Handle renames Param; --  Historic\n   begin\n      declare\n         M : Mal_Handle;\n         B : Boolean;\n      begin\n         M := Envs.Get (Env, \"DEBUG-EVAL\");\n         case Deref (M).Sym_Type is\n            when Bool   => B := Deref_Bool (M).Get_Bool;\n            when Nil    => B := False;\n            when others => B := True;\n         end case;\n         if B then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Ast).Sym_Type is\n\n         when Sym =>\n\n            declare\n               Sym : Mal_String := Deref_Sym (Ast).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Ast;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         declare\n            Evaled_H, First_Param, Rest_List : Mal_Handle;\n            Param_List : List_Mal_Type;\n         begin\n            Param_List := Deref_List (Param).all;\n\n            -- Deal with empty list..\n            if Param_List.Length = 0 then\n               return Param;\n            end if;\n\n            First_Param := Car (Param_List);\n            Rest_List := Cdr (Param_List);\n\n            if Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"def!\" then\n               return Def_Fn (Deref_List (Rest_List).all, Env);\n            elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n               return Let_Processing (Deref_List (Rest_List).all, Env);\n            else\n               -- The APPLY section.\n               Evaled_H := Eval_Ast (Param, Env);\n               Param_List := Deref_List (Evaled_H).all;\n               First_Param := Car (Param_List);\n               return Call_Func (Deref_Func (First_Param).all, Cdr (Param_List));\n            end if;\n\n         end;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   procedure Init (Env : Envs.Env_Handle) is\n   begin\n\n      Envs.Set (Env,\n           \"+\",\n           New_Func_Mal_Type (\"+\", Plus'Unrestricted_Access));\n\n      Envs.Set (Env,\n           \"-\",\n           New_Func_Mal_Type (\"-\", Minus'Unrestricted_Access));\n\n      Envs.Set (Env,\n           \"*\",\n           New_Func_Mal_Type (\"*\", Mult'Unrestricted_Access));\n\n      Envs.Set (Env,\n           \"/\",\n           New_Func_Mal_Type (\"/\", Divide'Unrestricted_Access));\n\n   end Init;\n\n\n   Repl_Env : Envs.Env_Handle;\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Repl_Env := Envs.New_Env;\n\n   Init (Repl_Env);\n\n   loop\n      begin\n         Ada.Text_IO.Put (\"user> \");\n         exit when Ada.Text_IO.End_Of_File;\n         Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n      exception\n         when E : others =>\n            Ada.Text_IO.Put_Line\n              (Ada.Text_IO.Standard_Error,\n               Ada.Exceptions.Exception_Information (E));\n      end;\n   end loop;\nend Step3_Env;\n"
  },
  {
    "path": "impls/ada/step4_if_fn_do.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step4_If_Fn_Do is\n\n   use Types;\n\n   function Eval (Param : Types.Mal_Handle; Env : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected symbol as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Let_Processing (Args : List_Mal_Type; Env : Envs.Env_Handle)\n                          return Mal_Handle is\n      Defs, Expr, Res : Mal_Handle;\n      E : Envs.Env_Handle;\n   begin\n      E := Envs.New_Env (Env);\n      Defs := Car (Args);\n      Deref_List_Class (Defs).Add_Defs (E);\n      Expr := Car (Deref_List (Cdr (Args)).all);\n      Res := Eval (Expr, E);\n      return Res;\n   end Let_Processing;\n\n\n   function Do_Processing (Do_List : List_Mal_Type; Env : Envs.Env_Handle)\n                         return Mal_Handle is\n      D : List_Mal_Type;\n      Res : Mal_Handle := Smart_Pointers.Null_Smart_Pointer;\n   begin\n      if Debug then\n         Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Do_List));\n      end if;\n      D := Do_List;\n      while not Is_Null (D) loop\n         Res := Eval (Car (D), Env);\n         D := Deref_List (Cdr(D)).all;\n      end loop;\n      return Res;\n   end Do_Processing;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n\n   function Eval (Param : Mal_Handle; Env : Envs.Env_Handle) return Mal_Handle is\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n      Ast : Mal_Handle renames Param; --  Historic\n   begin\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Ast).Sym_Type is\n\n         when Sym =>\n\n            declare\n               Sym : Mal_String := Deref_Sym (Ast).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Ast;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n\n            return Def_Fn (Rest_List, Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n\n            return Let_Processing (Rest_List, Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n\n            return Do_Processing (Rest_List, Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n\n            declare\n               Cond, True_Part, False_Part : Mal_Handle;\n               Cond_Bool : Boolean;\n               pragma Assert (Length (Rest_List) = 2 or Length (Rest_List) = 3,\n                              \"If_Processing: not 2 or 3 parameters\");\n               L : List_Mal_Type;\n            begin\n\n               Cond := Eval (Car (Rest_List), Env);\n\n               Cond_Bool := Eval_As_Boolean (Cond);\n\n               if Cond_Bool then\n                  L := Deref_List (Cdr (Rest_List)).all;\n                  return Eval (Car (L), Env);\n               else\n                  if Length (Rest_List) = 3 then\n                     L := Deref_List (Cdr (Rest_List)).all;\n                     L := Deref_List (Cdr (L)).all;\n                     return Eval (Car (L), Env);\n                  else\n                     return New_Nil_Mal_Type;\n                  end if;\n               end if;\n\n            end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         else\n\n            -- The APPLY section.\n            declare\n               Evaled_H : Mal_Handle;\n            begin\n               Evaled_H := Eval_Ast (Param, Env);\n\n               Param_List := Deref_List (Evaled_H).all;\n\n               First_Param := Car (Param_List);\n               Rest_Params := Cdr (Param_List);\n               Rest_List := Deref_List (Rest_Params).all;\n\n               if Deref (First_Param).Sym_Type = Func then\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  return Apply (Deref_Lambda (First_Param).all, Rest_Params);\n               else\n                  raise Mal_Exception;\n               end if;\n\n            end;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- This op uses Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   Cmd_Args : Natural;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Cmd_Args := 0;\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     end if;\n   end loop;\n\n   Repl_Env := Envs.New_Env;\n\n   Core.Init (Repl_Env);\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n\n   loop\n      begin\n         Ada.Text_IO.Put (\"user> \");\n         exit when Ada.Text_IO.End_Of_File;\n         Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n      exception\n         when E : others =>\n            Ada.Text_IO.Put_Line\n              (Ada.Text_IO.Standard_Error,\n               \"Error: \" & Ada.Exceptions.Exception_Information (E));\n            if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  Printer.Pr_Str (Types.Mal_Exception_Value));\n               Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n            end if;\n      end;\n   end loop;\nend Step4_If_Fn_Do;\n"
  },
  {
    "path": "impls/ada/step5_tco.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step5_TCO is\n\n   use Types;\n\n   -- Forward declaration of Eval.\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle) return Mal_Handle;\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected atom as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Param : Mal_Handle;\n      Env : Envs.Env_Handle;\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n      Ast : Mal_Handle renames Param; --  Historic\n   begin\n\n      Param := AParam;\n      Env := AnEnv;\n\n      <<Tail_Call_Opt>>\n\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Ast).Sym_Type is\n\n         when Sym =>\n\n            declare\n               Sym : Mal_String := Deref_Sym (Ast).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Ast;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n \t    return Def_Fn (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n            declare\n               Defs, Expr, Res : Mal_Handle;\n               E : Envs.Env_Handle;\n            begin\n               E := Envs.New_Env (Env);\n               Defs := Car (Rest_List);\n               Deref_List_Class (Defs).Add_Defs (E);\n               Expr := Car (Deref_List (Cdr (Rest_List)).all);\n\t       Param := Expr;\n\t       Env := E;\n\t       goto Tail_Call_Opt;\n               -- was:\n               -- Res := Eval (Expr, E);\n               -- return Res;\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n            declare\n               D : List_Mal_Type;\n               E : Mal_Handle;\n            begin\n\n               if Debug then\n                  Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Rest_List));\n               end if;\n\n               if Is_Null (Rest_List) then\n                  return Rest_Params;\n               end if;\n\n               -- Loop processes Evals all but last entry\n               D := Rest_List;\n               loop\n                  E := Car (D);\n                  D := Deref_List (Cdr (D)).all;\n               exit when Is_Null (D);\n                  E := Eval (E, Env);\n               end loop;\n\n               Param := E;\n               goto Tail_Call_Opt;\n\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n \t    declare\n\t       Args : List_Mal_Type := Rest_List;\n\n\t       Cond, True_Part, False_Part : Mal_Handle;\n\t       Cond_Bool : Boolean;\n\t       pragma Assert (Length (Args) = 2 or Length (Args) = 3,\n\t\t\t      \"If_Processing: not 2 or 3 parameters\");\n\t       L : List_Mal_Type;\n\t    begin\n\n\t       Cond := Eval (Car (Args), Env);\n\n\t       Cond_Bool := Eval_As_Boolean (Cond);\n\n\t       if Cond_Bool then\n\t          L := Deref_List (Cdr (Args)).all;\n\n\t          Param := Car (L);\n\t\t  goto Tail_Call_Opt;\n\t\t  -- was: return Eval (Car (L), Env);\n\t       else\n\t\t  if Length (Args) = 3 then\n\t\t     L := Deref_List (Cdr (Args)).all;\n\t\t     L := Deref_List (Cdr (L)).all;\n\n\t\t     Param := Car (L);\n\t\t     goto Tail_Call_Opt;\n\t\t     -- was: return Eval (Car (L), Env);\n\t          else\n\t\t     return New_Nil_Mal_Type;\n\t          end if;\n\t       end if;\n\t    end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         else\n\n            -- The APPLY section.\n            declare\n               Evaled_H : Mal_Handle;\n            begin\n               Evaled_H := Eval_Ast (Param, Env);\n\n               Param_List := Deref_List (Evaled_H).all;\n\n               First_Param := Car (Param_List);\n               Rest_Params := Cdr (Param_List);\n               Rest_List := Deref_List (Rest_Params).all;\n\n               if Deref (First_Param).Sym_Type = Func then\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  declare\n\n                     L : Lambda_Mal_Type;\n                     E : Envs.Env_Handle;\n                     Param_Names : List_Mal_Type;\n                     Res : Mal_Handle;\n\n                  begin\n\n                     L := Deref_Lambda (First_Param).all;\n                     E := Envs.New_Env (L.Get_Env);\n\n                     Param_Names := Deref_List (L.Get_Params).all;\n\n                     if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then\n\n\t\t\tParam := L.Get_Expr;\n                        Env := E;\n\t\t\tgoto Tail_Call_Opt;\n                        -- was: return Eval (L.Get_Expr, E);\n\n                     else\n\n                        raise Mal_Exception with \"Bind failed in Apply\";\n\n                     end if;\n\n                  end;\n\n               else  -- neither a Lambda or a Func\n                  raise Mal_Exception;\n               end if;\n\n            end;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- These two ops use Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   function Do_Eval (Rest_Handle : Mal_Handle; Env : Envs.Env_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Eval_Callback.Eval.all (First_Param, Repl_Env);\n   end Do_Eval;\n\n   Cmd_Args : Natural;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Cmd_Args := 0;\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     end if;\n   end loop;\n\n   Repl_Env := Envs.New_Env;\n\n   Core.Init (Repl_Env);\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n\n   loop\n      begin\n         Ada.Text_IO.Put (\"user> \");\n         exit when Ada.Text_IO.End_Of_File;\n         Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n      exception\n         when E : others =>\n            Ada.Text_IO.Put_Line\n              (Ada.Text_IO.Standard_Error,\n               \"Error: \" & Ada.Exceptions.Exception_Information (E));\n            if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  Printer.Pr_Str (Types.Mal_Exception_Value));\n               Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n            end if;\n      end;\n   end loop;\nend Step5_TCO;\n"
  },
  {
    "path": "impls/ada/step6_file.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step6_File is\n\n   use Types;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   -- Forward declaration of Eval.\n   function Eval (AParam : Types.Mal_Handle; AnEnv : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n\n   Debug : Boolean := False;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected atom as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Param : Mal_Handle;\n      Env : Envs.Env_Handle;\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n      Ast : Mal_Handle renames Param; --  Historic\n   begin\n\n      Param := AParam;\n      Env := AnEnv;\n\n      <<Tail_Call_Opt>>\n\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n\n      case Deref (Ast).Sym_Type is\n\n         when Sym =>\n\n            declare\n               Sym : Mal_String := Deref_Sym (Ast).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Ast;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n \t    return Def_Fn (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n            declare\n               Defs, Expr, Res : Mal_Handle;\n               E : Envs.Env_Handle;\n            begin\n               E := Envs.New_Env (Env);\n               Defs := Car (Rest_List);\n               Deref_List_Class (Defs).Add_Defs (E);\n               Expr := Car (Deref_List (Cdr (Rest_List)).all);\n\t       Param := Expr;\n\t       Env := E;\n\t       goto Tail_Call_Opt;\n               -- was:\n               -- Res := Eval (Expr, E);\n               -- return Res;\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n            declare\n               D : List_Mal_Type;\n               E : Mal_Handle;\n            begin\n\n               if Debug then\n                  Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Rest_List));\n               end if;\n\n               if Is_Null (Rest_List) then\n                  return Rest_Params;\n               end if;\n\n               -- Loop processes Evals all but last entry\n               D := Rest_List;\n               loop\n                  E := Car (D);\n                  D := Deref_List (Cdr (D)).all;\n               exit when Is_Null (D);\n                  E := Eval (E, Env);\n               end loop;\n\n               Param := E;\n               goto Tail_Call_Opt;\n\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n \t    declare\n\t       Args : List_Mal_Type := Rest_List;\n\n\t       Cond, True_Part, False_Part : Mal_Handle;\n\t       Cond_Bool : Boolean;\n\t       pragma Assert (Length (Args) = 2 or Length (Args) = 3,\n\t\t\t      \"If_Processing: not 2 or 3 parameters\");\n\t       L : List_Mal_Type;\n\t    begin\n\n\t       Cond := Eval (Car (Args), Env);\n\n\t       Cond_Bool := Eval_As_Boolean (Cond);\n\n\t       if Cond_Bool then\n\t          L := Deref_List (Cdr (Args)).all;\n\n\t          Param := Car (L);\n\t\t  goto Tail_Call_Opt;\n\t\t  -- was: return Eval (Car (L), Env);\n\t       else\n\t\t  if Length (Args) = 3 then\n\t\t     L := Deref_List (Cdr (Args)).all;\n\t\t     L := Deref_List (Cdr (L)).all;\n\n\t\t     Param := Car (L);\n\t\t     goto Tail_Call_Opt;\n\t\t     -- was: return Eval (Car (L), Env);\n\t          else\n\t\t     return New_Nil_Mal_Type;\n\t          end if;\n\t       end if;\n\t    end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         else\n\n            -- The APPLY section.\n            declare\n               Evaled_H : Mal_Handle;\n            begin\n               Evaled_H := Eval_Ast (Param, Env);\n\n               Param_List := Deref_List (Evaled_H).all;\n\n               First_Param := Car (Param_List);\n               Rest_Params := Cdr (Param_List);\n               Rest_List := Deref_List (Rest_Params).all;\n\n               if Deref (First_Param).Sym_Type = Func then\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  declare\n\n                     L : Lambda_Mal_Type;\n                     E : Envs.Env_Handle;\n                     Param_Names : List_Mal_Type;\n                     Res : Mal_Handle;\n\n                  begin\n\n                     L := Deref_Lambda (First_Param).all;\n                     E := Envs.New_Env (L.Get_Env);\n\n                     Param_Names := Deref_List (L.Get_Params).all;\n\n                     if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then\n\n\t\t\tParam := L.Get_Expr;\n                        Env := E;\n\t\t\tgoto Tail_Call_Opt;\n                        -- was: return Eval (L.Get_Expr, E);\n\n                     else\n\n                        raise Runtime_Exception with \"Bind failed in Apply\";\n\n                     end if;\n\n                  end;\n\n               else  -- neither a Lambda or a Func\n                  raise Runtime_Exception with \"Deref called on non-Func/Lambda\";\n               end if;\n\n            end;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- These two ops use Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   function Do_Eval (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Eval_Callback.Eval.all (First_Param, Repl_Env);\n   end Do_Eval;\n\n\n   Cmd_Args, File_Param : Natural;\n   Command_Args : Types.Mal_Handle;\n   Command_List : Types.List_Ptr;\n   File_Processed : Boolean := False;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Repl_Env := Envs.New_Env;\n\n   -- Core init also creates the first environment.\n   -- This is needed for the def!'s below.\n   Core.Init (Repl_Env);\n\n   -- Register the eval command.  This needs to be done here rather than Core.Init\n   -- as it requires direct access to Repl_Env.\n   Envs.Set (Repl_Env, \"eval\", New_Func_Mal_Type (\"eval\", Do_Eval'Unrestricted_Access));\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n   RE (\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\");\n\n   -- Command line processing.\n\n   Cmd_Args := 0;\n   Command_Args := Types.New_List_Mal_Type (Types.List_List);\n   Command_List := Types.Deref_List (Command_Args);\n\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     elsif not File_Processed then\n        File_Param := Cmd_Args;\n        File_Processed := True;\n     else\n        Command_List.Append\n          (Types.New_String_Mal_Type (Ada.Command_Line.Argument (Cmd_Args)));\n     end if;\n\n   end loop;\n\n   Envs.Set (Repl_Env, \"*ARGV*\", Command_Args);\n\n   if File_Processed then\n      RE (\"(load-file \"\"\" & Ada.Command_Line.Argument (File_Param) & \"\"\")\");\n   else\n      loop\n         begin\n            Ada.Text_IO.Put (\"user> \");\n            exit when Ada.Text_IO.End_Of_File;\n            Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n         exception\n            when E : others =>\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  \"Error: \" & Ada.Exceptions.Exception_Information (E));\n               if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n                  Ada.Text_IO.Put_Line\n                    (Ada.Text_IO.Standard_Error,\n                     Printer.Pr_Str (Types.Mal_Exception_Value));\n                  Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n               end if;\n         end;\n      end loop;\n   end if;\nend Step6_File;\n"
  },
  {
    "path": "impls/ada/step7_quote.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step7_Quote is\n\n   use Types;\n\n   function Eval (AParam : Types.Mal_Handle; AnEnv : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected atom as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n   function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is\n      A0 : Mal_Handle;\n   begin\n      if Deref (Ast).Sym_Type /= List\n        or else Deref_List_Class (Ast).Get_List_Type /= List_List\n        or else Deref_List (Ast).Is_Null\n      then\n         return False;\n      end if;\n      A0 := Deref_List (Ast).Car;\n      return Deref (A0).Sym_Type = Sym\n        and then Deref_Sym (A0).Get_Sym = Symbol;\n   end Starts_With;\n\n   function Quasi_Quote_Processing (Param : Mal_Handle) return Mal_Handle is\n      Res, Elt, New_Res : Mal_Handle;\n      L : List_Ptr;\n   begin\n\n      if Debug then\n         Ada.Text_IO.Put_Line (\"QuasiQt \" & Deref (Param).To_String);\n      end if;\n\n      if Deref (Param).Sym_Type not in Sym | List then\n         --  No need to quote, Eval would not affect these anyway.\n         return Param;\n      end if;\n\n      if Deref (Param).Sym_Type /= List or else\n        Deref_List_Class (Param).Get_List_Type = Hashed_List then\n\n         -- return a new list containing: a symbol named \"quote\" and ast.\n         Res := New_List_Mal_Type (List_List);\n         L := Deref_List (Res);\n         L.Append (New_Symbol_Mal_Type (\"quote\"));\n         L.Append (Param);\n         return Res;\n\n      end if;\n\n      -- if the first element of ast is a symbol named \"unquote\":\n      if Starts_With (Param, \"unquote\") then\n         -- return the second element of ast.`\n         return Deref_List_Class (Param).Nth (1);\n\n      end if;\n\n      Res := New_List_Mal_Type (List_List);\n\n      for I in reverse 0 .. Deref_List_Class (Param).Length - 1 loop\n         Elt := Deref_List_Class (Param).Nth (I);\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         if Starts_With (Elt, \"splice-unquote\") then\n            L.Append (New_Symbol_Mal_Type (\"concat\"));\n            L.Append (Deref_List (Elt).Nth (1));\n         else\n            L.Append (New_Symbol_Mal_Type (\"cons\"));\n            L.Append (Quasi_Quote_Processing (Elt));\n         end if;\n         L.Append (Res);\n         Res := New_Res;\n      end loop;\n\n      if Deref_List_Class (Param).Get_List_Type = Vector_List then\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         L.Append (New_Symbol_Mal_Type (\"vec\"));\n         L.Append (Res);\n         Res := New_Res;\n      end if;\n\n      return Res;\n\n   end Quasi_Quote_Processing;\n\n\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Param : Mal_Handle;\n      Env : Envs.Env_Handle;\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n   begin\n\n      Param := AParam;\n      Env := AnEnv;\n\n      <<Tail_Call_Opt>>\n\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Param).Sym_Type is\n         when Sym =>\n            declare\n               Sym : Mal_String := Deref_Sym (Param).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Param;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n \t    return Def_Fn (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n            declare\n               Defs, Expr, Res : Mal_Handle;\n               E : Envs.Env_Handle;\n            begin\n               E := Envs.New_Env (Env);\n               Defs := Car (Rest_List);\n               Deref_List_Class (Defs).Add_Defs (E);\n               Expr := Car (Deref_List (Cdr (Rest_List)).all);\n\t       Param := Expr;\n\t       Env := E;\n\t       goto Tail_Call_Opt;\n               -- was:\n               -- Res := Eval (Expr, E);\n               -- return Res;\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n            declare\n               D : List_Mal_Type;\n               E : Mal_Handle;\n            begin\n\n               if Debug then\n                  Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Rest_List));\n               end if;\n\n               if Is_Null (Rest_List) then\n                  return Rest_Params;\n               end if;\n\n               -- Loop processes Evals all but last entry\n               D := Rest_List;\n               loop\n                  E := Car (D);\n                  D := Deref_List (Cdr (D)).all;\n               exit when Is_Null (D);\n                  E := Eval (E, Env);\n               end loop;\n\n               Param := E;\n               goto Tail_Call_Opt;\n\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n \t    declare\n\t       Args : List_Mal_Type := Rest_List;\n\n\t       Cond, True_Part, False_Part : Mal_Handle;\n\t       Cond_Bool : Boolean;\n\t       pragma Assert (Length (Args) = 2 or Length (Args) = 3,\n\t\t\t      \"If_Processing: not 2 or 3 parameters\");\n\t       L : List_Mal_Type;\n\t    begin\n\n\t       Cond := Eval (Car (Args), Env);\n\n\t       Cond_Bool := Eval_As_Boolean (Cond);\n\n\t       if Cond_Bool then\n\t          L := Deref_List (Cdr (Args)).all;\n\n\t          Param := Car (L);\n\t\t  goto Tail_Call_Opt;\n\t\t  -- was: return Eval (Car (L), Env);\n\t       else\n\t\t  if Length (Args) = 3 then\n\t\t     L := Deref_List (Cdr (Args)).all;\n\t\t     L := Deref_List (Cdr (L)).all;\n\n\t\t     Param := Car (L);\n\t\t     goto Tail_Call_Opt;\n\t\t     -- was: return Eval (Car (L), Env);\n\t          else\n\t\t     return New_Nil_Mal_Type;\n\t          end if;\n\t       end if;\n\t    end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quote\" then\n\n            return Car (Rest_List);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quasiquote\" then\n\n            Param := Quasi_Quote_Processing (Car (Rest_List));\n            goto Tail_Call_Opt;\n\n         else\n\n            -- The APPLY section.\n            declare\n               Evaled_H : Mal_Handle;\n            begin\n               Evaled_H := Eval_Ast (Param, Env);\n\n               Param_List := Deref_List (Evaled_H).all;\n\n               First_Param := Car (Param_List);\n               Rest_Params := Cdr (Param_List);\n               Rest_List := Deref_List (Rest_Params).all;\n\n               if Deref (First_Param).Sym_Type = Func then\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  declare\n\n                     L : Lambda_Mal_Type;\n                     E : Envs.Env_Handle;\n                     Param_Names : List_Mal_Type;\n                     Res : Mal_Handle;\n\n                  begin\n\n                     L := Deref_Lambda (First_Param).all;\n                     E := Envs.New_Env (L.Get_Env);\n\n                     Param_Names := Deref_List (L.Get_Params).all;\n\n                     if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then\n\n\t\t\tParam := L.Get_Expr;\n                        Env := E;\n\t\t\tgoto Tail_Call_Opt;\n                        -- was: return Eval (L.Get_Expr, E);\n\n                     else\n\n                        raise Runtime_Exception with \"Bind failed in Apply\";\n\n                     end if;\n\n                  end;\n\n               else  -- neither a Lambda or a Func\n                  raise Runtime_Exception with \"Deref called on non-Func/Lambda\";\n               end if;\n\n            end;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- These two ops use Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   function Do_Eval (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Eval_Callback.Eval.all (First_Param, Repl_Env);\n   end Do_Eval;\n\n\n   Cmd_Args, File_Param : Natural;\n   Command_Args : Types.Mal_Handle;\n   Command_List : Types.List_Ptr;\n   File_Processed : Boolean := False;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Repl_Env := Envs.New_Env;\n\n   -- Core init also creates the first environment.\n   -- This is needed for the def!'s below.\n   Core.Init (Repl_Env);\n\n   -- Register the eval command.  This needs to be done here rather than Core.Init\n   -- as it requires direct access to Repl_Env.\n   Envs.Set (Repl_Env, \"eval\", New_Func_Mal_Type (\"eval\", Do_Eval'Unrestricted_Access));\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n   RE (\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\");\n\n   -- Command line processing.\n\n   Cmd_Args := 0;\n   Command_Args := Types.New_List_Mal_Type (Types.List_List);\n   Command_List := Types.Deref_List (Command_Args);\n\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     elsif not File_Processed then\n        File_Param := Cmd_Args;\n        File_Processed := True;\n     else\n        Command_List.Append\n          (Types.New_String_Mal_Type (Ada.Command_Line.Argument (Cmd_Args)));\n     end if;\n\n   end loop;\n\n   Envs.Set (Repl_Env, \"*ARGV*\", Command_Args);\n\n   if File_Processed then\n      RE (\"(load-file \"\"\" & Ada.Command_Line.Argument (File_Param) & \"\"\")\");\n   else\n      loop\n         begin\n            Ada.Text_IO.Put (\"user> \");\n            exit when Ada.Text_IO.End_Of_File;\n            Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n         exception\n            when E : others =>\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  \"Error: \" & Ada.Exceptions.Exception_Information (E));\n               if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n                  Ada.Text_IO.Put_Line\n                    (Ada.Text_IO.Standard_Error,\n                     Printer.Pr_Str (Types.Mal_Exception_Value));\n                  Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n               end if;\n         end;\n      end loop;\n   end if;\nend Step7_Quote;\n"
  },
  {
    "path": "impls/ada/step8_macros.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step8_Macros is\n\n   use Types;\n\n   function Eval (AParam : Types.Mal_Handle; AnEnv : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected atom as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Def_Macro (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n      Lambda_P : Lambda_Ptr;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Macro: expected atom as name\");\n      Fn_Body := Car (Deref_List (Cdr (Args)).all);\n      Res := Eval (Fn_Body, Env);\n      Lambda_P := Deref_Lambda (Res);\n      Res := New_Lambda_Mal_Type (Params => Lambda_P.all.Get_Params,\n                                  Expr   => Lambda_P.all.Get_Expr,\n                                  Env    => Lambda_P.all.Get_Env);\n      Deref_Lambda (Res).Set_Is_Macro (True);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Macro;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n   function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is\n      A0 : Mal_Handle;\n   begin\n      if Deref (Ast).Sym_Type /= List\n        or else Deref_List_Class (Ast).Get_List_Type /= List_List\n        or else Deref_List (Ast).Is_Null\n      then\n         return False;\n      end if;\n      A0 := Deref_List (Ast).Car;\n      return Deref (A0).Sym_Type = Sym\n        and then Deref_Sym (A0).Get_Sym = Symbol;\n   end Starts_With;\n\n   function Quasi_Quote_Processing (Param : Mal_Handle) return Mal_Handle is\n      Res, Elt, New_Res : Mal_Handle;\n      L : List_Ptr;\n   begin\n\n      if Debug then\n         Ada.Text_IO.Put_Line (\"QuasiQt \" & Deref (Param).To_String);\n      end if;\n\n      if Deref (Param).Sym_Type not in Sym | List then\n         --  No need to quote, Eval would not affect these anyway.\n         return Param;\n      end if;\n\n      if Deref (Param).Sym_Type /= List or else\n        Deref_List_Class (Param).Get_List_Type = Hashed_List then\n\n         -- return a new list containing: a symbol named \"quote\" and ast.\n         Res := New_List_Mal_Type (List_List);\n         L := Deref_List (Res);\n         L.Append (New_Symbol_Mal_Type (\"quote\"));\n         L.Append (Param);\n         return Res;\n\n      end if;\n\n      -- if the first element of ast is a symbol named \"unquote\":\n      if Starts_With (Param, \"unquote\") then\n         -- return the second element of ast.`\n         return Deref_List_Class (Param).Nth (1);\n\n      end if;\n\n      Res := New_List_Mal_Type (List_List);\n\n      for I in reverse 0 .. Deref_List_Class (Param).Length - 1 loop\n         Elt := Deref_List_Class (Param).Nth (I);\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         if Starts_With (Elt, \"splice-unquote\") then\n            L.Append (New_Symbol_Mal_Type (\"concat\"));\n            L.Append (Deref_List (Elt).Nth (1));\n         else\n            L.Append (New_Symbol_Mal_Type (\"cons\"));\n            L.Append (Quasi_Quote_Processing (Elt));\n         end if;\n         L.Append (Res);\n         Res := New_Res;\n      end loop;\n\n      if Deref_List_Class (Param).Get_List_Type = Vector_List then\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         L.Append (New_Symbol_Mal_Type (\"vec\"));\n         L.Append (Res);\n         Res := New_Res;\n      end if;\n\n      return Res;\n\n   end Quasi_Quote_Processing;\n\n\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Param : Mal_Handle;\n      Env : Envs.Env_Handle;\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n   begin\n\n      Param := AParam;\n      Env := AnEnv;\n\n      <<Tail_Call_Opt>>\n\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Param).Sym_Type is\n         when Sym =>\n            declare\n               Sym : Mal_String := Deref_Sym (Param).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Param;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n \t    return Def_Fn (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"defmacro!\" then\n            return Def_Macro (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n            declare\n               Defs, Expr, Res : Mal_Handle;\n               E : Envs.Env_Handle;\n            begin\n               E := Envs.New_Env (Env);\n               Defs := Car (Rest_List);\n               Deref_List_Class (Defs).Add_Defs (E);\n               Expr := Car (Deref_List (Cdr (Rest_List)).all);\n\t       Param := Expr;\n\t       Env := E;\n\t       goto Tail_Call_Opt;\n               -- was:\n               -- Res := Eval (Expr, E);\n               -- return Res;\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n            declare\n               D : List_Mal_Type;\n               E : Mal_Handle;\n            begin\n\n               if Debug then\n                  Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Rest_List));\n               end if;\n\n               if Is_Null (Rest_List) then\n                  return Rest_Params;\n               end if;\n\n               -- Loop processes Evals all but last entry\n               D := Rest_List;\n               loop\n                  E := Car (D);\n                  D := Deref_List (Cdr (D)).all;\n               exit when Is_Null (D);\n                  E := Eval (E, Env);\n               end loop;\n\n               Param := E;\n               goto Tail_Call_Opt;\n\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n \t    declare\n\t       Args : List_Mal_Type := Rest_List;\n\n\t       Cond, True_Part, False_Part : Mal_Handle;\n\t       Cond_Bool : Boolean;\n\t       pragma Assert (Length (Args) = 2 or Length (Args) = 3,\n\t\t\t      \"If_Processing: not 2 or 3 parameters\");\n\t       L : List_Mal_Type;\n\t    begin\n\n\t       Cond := Eval (Car (Args), Env);\n\n\t       Cond_Bool := Eval_As_Boolean (Cond);\n\n\t       if Cond_Bool then\n\t          L := Deref_List (Cdr (Args)).all;\n\n\t          Param := Car (L);\n\t\t  goto Tail_Call_Opt;\n\t\t  -- was: return Eval (Car (L), Env);\n\t       else\n\t\t  if Length (Args) = 3 then\n\t\t     L := Deref_List (Cdr (Args)).all;\n\t\t     L := Deref_List (Cdr (L)).all;\n\n\t\t     Param := Car (L);\n\t\t     goto Tail_Call_Opt;\n\t\t     -- was: return Eval (Car (L), Env);\n\t          else\n\t\t     return New_Nil_Mal_Type;\n\t          end if;\n\t       end if;\n\t    end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quote\" then\n\n            return Car (Rest_List);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quasiquote\" then\n\n            Param := Quasi_Quote_Processing (Car (Rest_List));\n            goto Tail_Call_Opt;\n\n         else\n\n            -- The APPLY section.\n               First_Param  := Eval (First_Param, Env);\n\n               if Deref (First_Param).Sym_Type = Func then\n                  Rest_Params := Eval_Ast (Rest_Params, Env);\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  declare\n\n                     L : Lambda_Mal_Type;\n                     E : Envs.Env_Handle;\n                     Param_Names : List_Mal_Type;\n                     Res : Mal_Handle;\n\n                  begin\n\n                     L := Deref_Lambda (First_Param).all;\n\n                     if L.Get_Is_Macro then\n                        --  Apply to *unevaluated* arguments\n                        Param := L.Apply (Rest_Params);\n                        --  then EVAL the result.\n                        goto Tail_Call_Opt;\n                     end if;\n\n                     Rest_Params := Eval_Ast (Rest_Params, Env);\n\n                     E := Envs.New_Env (L.Get_Env);\n\n                     Param_Names := Deref_List (L.Get_Params).all;\n\n                     if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then\n\n\t\t\tParam := L.Get_Expr;\n                        Env := E;\n\t\t\tgoto Tail_Call_Opt;\n                        -- was: return Eval (L.Get_Expr, E);\n\n                     else\n\n                        raise Runtime_Exception with \"Bind failed in Apply\";\n\n                     end if;\n\n                  end;\n\n               else  -- neither a Lambda or a Func\n                  raise Runtime_Exception with \"Deref called on non-Func/Lambda\";\n               end if;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- These two ops use Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   function Do_Eval (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Eval_Callback.Eval.all (First_Param, Repl_Env);\n   end Do_Eval;\n\n\n   Cmd_Args, File_Param : Natural;\n   Command_Args : Types.Mal_Handle;\n   Command_List : Types.List_Ptr;\n   File_Processed : Boolean := False;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Repl_Env := Envs.New_Env;\n\n   -- Core init also creates the first environment.\n   -- This is needed for the def!'s below.\n   Core.Init (Repl_Env);\n\n   -- Register the eval command.  This needs to be done here rather than Core.Init\n   -- as it requires direct access to Repl_Env.\n   Envs.Set (Repl_Env, \"eval\", New_Func_Mal_Type (\"eval\", Do_Eval'Unrestricted_Access));\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n   RE (\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\");\n   RE (\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons 'cond (rest (rest xs)))))))\");\n\n   -- Command line processing.\n\n   Cmd_Args := 0;\n   Command_Args := Types.New_List_Mal_Type (Types.List_List);\n   Command_List := Types.Deref_List (Command_Args);\n\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     elsif not File_Processed then\n        File_Param := Cmd_Args;\n        File_Processed := True;\n     else\n        Command_List.Append\n          (Types.New_String_Mal_Type (Ada.Command_Line.Argument (Cmd_Args)));\n     end if;\n\n   end loop;\n\n   Envs.Set (Repl_Env, \"*ARGV*\", Command_Args);\n\n   if File_Processed then\n      RE (\"(load-file \"\"\" & Ada.Command_Line.Argument (File_Param) & \"\"\")\");\n   else\n      loop\n         begin\n            Ada.Text_IO.Put (\"user> \");\n            exit when Ada.Text_IO.End_Of_File;\n            Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n         exception\n            when E : others =>\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  \"Error: \" & Ada.Exceptions.Exception_Information (E));\n               if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n                  Ada.Text_IO.Put_Line\n                    (Ada.Text_IO.Standard_Error,\n                     Printer.Pr_Str (Types.Mal_Exception_Value));\n                  Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n               end if;\n         end;\n      end loop;\n   end if;\nend Step8_Macros;\n"
  },
  {
    "path": "impls/ada/step9_try.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure Step9_Try is\n\n   use Types;\n\n   function Eval (AParam : Types.Mal_Handle; AnEnv : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected atom as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Def_Macro (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n      Lambda_P : Lambda_Ptr;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Macro: expected atom as name\");\n      Fn_Body := Car (Deref_List (Cdr (Args)).all);\n      Res := Eval (Fn_Body, Env);\n      Lambda_P := Deref_Lambda (Res);\n      Res := New_Lambda_Mal_Type (Params => Lambda_P.all.Get_Params,\n                                  Expr   => Lambda_P.all.Get_Expr,\n                                  Env    => Lambda_P.all.Get_Env);\n      Deref_Lambda (Res).Set_Is_Macro (True);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Macro;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n   function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is\n      A0 : Mal_Handle;\n   begin\n      if Deref (Ast).Sym_Type /= List\n        or else Deref_List_Class (Ast).Get_List_Type /= List_List\n        or else Deref_List (Ast).Is_Null\n      then\n         return False;\n      end if;\n      A0 := Deref_List (Ast).Car;\n      return Deref (A0).Sym_Type = Sym\n        and then Deref_Sym (A0).Get_Sym = Symbol;\n   end Starts_With;\n\n   function Quasi_Quote_Processing (Param : Mal_Handle) return Mal_Handle is\n      Res, Elt, New_Res : Mal_Handle;\n      L : List_Ptr;\n   begin\n\n      if Debug then\n         Ada.Text_IO.Put_Line (\"QuasiQt \" & Deref (Param).To_String);\n      end if;\n\n      if Deref (Param).Sym_Type not in Sym | List then\n         --  No need to quote, Eval would not affect these anyway.\n         return Param;\n      end if;\n\n      if Deref (Param).Sym_Type /= List or else\n        Deref_List_Class (Param).Get_List_Type = Hashed_List then\n\n         -- return a new list containing: a symbol named \"quote\" and ast.\n         Res := New_List_Mal_Type (List_List);\n         L := Deref_List (Res);\n         L.Append (New_Symbol_Mal_Type (\"quote\"));\n         L.Append (Param);\n         return Res;\n\n      end if;\n\n      -- if the first element of ast is a symbol named \"unquote\":\n      if Starts_With (Param, \"unquote\") then\n         -- return the second element of ast.`\n         return Deref_List_Class (Param).Nth (1);\n\n      end if;\n\n      Res := New_List_Mal_Type (List_List);\n\n      for I in reverse 0 .. Deref_List_Class (Param).Length - 1 loop\n         Elt := Deref_List_Class (Param).Nth (I);\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         if Starts_With (Elt, \"splice-unquote\") then\n            L.Append (New_Symbol_Mal_Type (\"concat\"));\n            L.Append (Deref_List (Elt).Nth (1));\n         else\n            L.Append (New_Symbol_Mal_Type (\"cons\"));\n            L.Append (Quasi_Quote_Processing (Elt));\n         end if;\n         L.Append (Res);\n         Res := New_Res;\n      end loop;\n\n      if Deref_List_Class (Param).Get_List_Type = Vector_List then\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         L.Append (New_Symbol_Mal_Type (\"vec\"));\n         L.Append (Res);\n         Res := New_Res;\n      end if;\n\n      return Res;\n\n   end Quasi_Quote_Processing;\n\n\n   function Catch_Processing\n              (Try_Line : Mal_Handle;\n               ExStr : Mal_Handle;\n               Env : Envs.Env_Handle)\n   return Mal_Handle is\n\n      L, CL, CL2, CL3 : List_Mal_Type;\n      C : Mal_Handle;\n      New_Env : Envs.Env_Handle;\n\n   begin\n\n      L := Deref_List (Try_Line).all;\n      C := Car (L);\n      -- CL is the list with the catch in.\n      CL := Deref_List (C).all;\n\n      CL2 := Deref_List (Cdr (CL)).all;\n      New_Env := Envs.New_Env (Env);\n      Envs.Set (New_Env, Deref_Sym (Car (CL2)).Get_Sym, ExStr);\n\n      CL3 := Deref_List (Cdr (CL2)).all;\n      return Eval (Car (CL3), New_Env);\n   end Catch_Processing;\n\n\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Param : Mal_Handle;\n      Env : Envs.Env_Handle;\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n   begin\n\n      Param := AParam;\n      Env := AnEnv;\n\n      <<Tail_Call_Opt>>\n\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Param).Sym_Type is\n         when Sym =>\n            declare\n               Sym : Mal_String := Deref_Sym (Param).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Param;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n \t    return Def_Fn (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"defmacro!\" then\n            return Def_Macro (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n            declare\n               Defs, Expr, Res : Mal_Handle;\n               E : Envs.Env_Handle;\n            begin\n               E := Envs.New_Env (Env);\n               Defs := Car (Rest_List);\n               Deref_List_Class (Defs).Add_Defs (E);\n               Expr := Car (Deref_List (Cdr (Rest_List)).all);\n\t       Param := Expr;\n\t       Env := E;\n\t       goto Tail_Call_Opt;\n               -- was:\n               -- Res := Eval (Expr, E);\n               -- return Res;\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n            declare\n               D : List_Mal_Type;\n               E : Mal_Handle;\n            begin\n\n               if Debug then\n                  Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Rest_List));\n               end if;\n\n               if Is_Null (Rest_List) then\n                  return Rest_Params;\n               end if;\n\n               -- Loop processes Evals all but last entry\n               D := Rest_List;\n               loop\n                  E := Car (D);\n                  D := Deref_List (Cdr (D)).all;\n               exit when Is_Null (D);\n                  E := Eval (E, Env);\n               end loop;\n\n               Param := E;\n               goto Tail_Call_Opt;\n\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n \t    declare\n\t       Args : List_Mal_Type := Rest_List;\n\n\t       Cond, True_Part, False_Part : Mal_Handle;\n\t       Cond_Bool : Boolean;\n\t       pragma Assert (Length (Args) = 2 or Length (Args) = 3,\n\t\t\t      \"If_Processing: not 2 or 3 parameters\");\n\t       L : List_Mal_Type;\n\t    begin\n\n\t       Cond := Eval (Car (Args), Env);\n\n\t       Cond_Bool := Eval_As_Boolean (Cond);\n\n\t       if Cond_Bool then\n\t          L := Deref_List (Cdr (Args)).all;\n\n\t          Param := Car (L);\n\t\t  goto Tail_Call_Opt;\n\t\t  -- was: return Eval (Car (L), Env);\n\t       else\n\t\t  if Length (Args) = 3 then\n\t\t     L := Deref_List (Cdr (Args)).all;\n\t\t     L := Deref_List (Cdr (L)).all;\n\n\t\t     Param := Car (L);\n\t\t     goto Tail_Call_Opt;\n\t\t     -- was: return Eval (Car (L), Env);\n\t          else\n\t\t     return New_Nil_Mal_Type;\n\t          end if;\n\t       end if;\n\t    end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quote\" then\n\n            return Car (Rest_List);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quasiquote\" then\n\n            Param := Quasi_Quote_Processing (Car (Rest_List));\n            goto Tail_Call_Opt;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"try*\" then\n\n            if Length (Rest_List) = 1 then\n               return Eval (Car (Rest_List), Env);\n            end if;\n            declare\n               Res : Mal_Handle;\n            begin\n               return Eval (Car (Rest_List), Env);\n            exception\n               when Mal_Exception =>\n                  Res := Catch_Processing\n                           (Cdr (Rest_List),\n                            Types.Mal_Exception_Value,\n                            Env);\n                  Types.Mal_Exception_Value :=\n                    Smart_Pointers.Null_Smart_Pointer;\n                  return Res;\n               when E : others =>\n                  return Catch_Processing\n                           (Cdr (Rest_List),\n                            New_String_Mal_Type\n                              (Ada.Exceptions.Exception_Message (E)),\n                            Env);\n            end;\n\n         else\n\n            -- The APPLY section.\n               First_Param  := Eval (First_Param, Env);\n\n               if Deref (First_Param).Sym_Type = Func then\n                  Rest_Params := Eval_Ast (Rest_Params, Env);\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  declare\n\n                     L : Lambda_Mal_Type;\n                     E : Envs.Env_Handle;\n                     Param_Names : List_Mal_Type;\n                     Res : Mal_Handle;\n\n                  begin\n\n                     L := Deref_Lambda (First_Param).all;\n\n                     if L.Get_Is_Macro then\n                        --  Apply to *unevaluated* arguments\n                        Param := L.Apply (Rest_Params);\n                        --  then EVAL the result.\n                        goto Tail_Call_Opt;\n                     end if;\n\n                     Rest_Params := Eval_Ast (Rest_Params, Env);\n\n                     E := Envs.New_Env (L.Get_Env);\n\n                     Param_Names := Deref_List (L.Get_Params).all;\n\n                     if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then\n\n\t\t\tParam := L.Get_Expr;\n                        Env := E;\n\t\t\tgoto Tail_Call_Opt;\n                        -- was: return Eval (L.Get_Expr, E);\n\n                     else\n\n                        raise Runtime_Exception with \"Bind failed in Apply\";\n\n                     end if;\n\n                  end;\n\n               else  -- neither a Lambda or a Func\n                  raise Runtime_Exception with \"Deref called on non-Func/Lambda\";\n               end if;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- These two ops use Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   function Do_Eval (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Eval_Callback.Eval.all (First_Param, Repl_Env);\n   end Do_Eval;\n\n\n   Cmd_Args, File_Param : Natural;\n   Command_Args : Types.Mal_Handle;\n   Command_List : Types.List_Ptr;\n   File_Processed : Boolean := False;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Repl_Env := Envs.New_Env;\n\n   -- Core init also creates the first environment.\n   -- This is needed for the def!'s below.\n   Core.Init (Repl_Env);\n\n   -- Register the eval command.  This needs to be done here rather than Core.Init\n   -- as it requires direct access to Repl_Env.\n   Envs.Set (Repl_Env, \"eval\", New_Func_Mal_Type (\"eval\", Do_Eval'Unrestricted_Access));\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n   RE (\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\");\n   RE (\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons 'cond (rest (rest xs)))))))\");\n\n   -- Command line processing.\n\n   Cmd_Args := 0;\n   Command_Args := Types.New_List_Mal_Type (Types.List_List);\n   Command_List := Types.Deref_List (Command_Args);\n\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     elsif not File_Processed then\n        File_Param := Cmd_Args;\n        File_Processed := True;\n     else\n        Command_List.Append\n          (Types.New_String_Mal_Type (Ada.Command_Line.Argument (Cmd_Args)));\n     end if;\n\n   end loop;\n\n   Envs.Set (Repl_Env, \"*ARGV*\", Command_Args);\n\n   if File_Processed then\n      RE (\"(load-file \"\"\" & Ada.Command_Line.Argument (File_Param) & \"\"\")\");\n   else\n      loop\n         begin\n            Ada.Text_IO.Put (\"user> \");\n            exit when Ada.Text_IO.End_Of_File;\n            Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n         exception\n            when E : others =>\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  \"Error: \" & Ada.Exceptions.Exception_Information (E));\n               if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n                  Ada.Text_IO.Put_Line\n                    (Ada.Text_IO.Standard_Error,\n                     Printer.Pr_Str (Types.Mal_Exception_Value));\n                  Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n               end if;\n         end;\n      end loop;\n   end if;\nend Step9_Try;\n"
  },
  {
    "path": "impls/ada/stepa_mal.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Exceptions;\nwith Ada.Text_IO;\nwith Core;\nwith Envs;\nwith Eval_Callback;\nwith Printer;\nwith Reader;\nwith Smart_Pointers;\nwith Types;\n\nprocedure StepA_Mal is\n\n   use Types;\n\n   function Eval (AParam : Types.Mal_Handle; AnEnv : Envs.Env_Handle)\n   return Types.Mal_Handle;\n\n   Debug : Boolean := False;\n\n\n   function Read (Param : String) return Types.Mal_Handle is\n   begin\n      return Reader.Read_Str (Param);\n   end Read;\n\n\n   function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Fn: expected atom as name\");\n      Fn_Body := Nth (Args, 1);\n      Res := Eval (Fn_Body, Env);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Fn;\n\n\n   function Def_Macro (Args : List_Mal_Type; Env : Envs.Env_Handle)\n\t\t   return Mal_Handle is\n      Name, Fn_Body, Res : Mal_Handle;\n      Lambda_P : Lambda_Ptr;\n   begin\n      Name := Car (Args);\n      pragma Assert (Deref (Name).Sym_Type = Sym,\n                     \"Def_Macro: expected atom as name\");\n      Fn_Body := Car (Deref_List (Cdr (Args)).all);\n      Res := Eval (Fn_Body, Env);\n      Lambda_P := Deref_Lambda (Res);\n      Res := New_Lambda_Mal_Type (Params => Lambda_P.all.Get_Params,\n                                  Expr   => Lambda_P.all.Get_Expr,\n                                  Env    => Lambda_P.all.Get_Env);\n      Deref_Lambda (Res).Set_Is_Macro (True);\n      Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);\n      return Res;\n   end Def_Macro;\n\n\n   function Eval_As_Boolean (MH : Mal_Handle) return Boolean is\n      Res : Boolean;\n   begin\n      case Deref (MH).Sym_Type is\n         when Bool =>\n            Res := Deref_Bool (MH).Get_Bool;\n         when Nil =>\n            return False;\n--         when List =>\n--            declare\n--               L : List_Mal_Type;\n--            begin\n--               L := Deref_List (MH).all;\n--               Res := not Is_Null (L);\n--            end;\n         when others => -- Everything else\n            Res := True;\n      end case;\n      return Res;\n   end Eval_As_Boolean;\n\n\n   function Eval_Ast\n     (Ast : Mal_Handle; Env : Envs.Env_Handle)\n     return Mal_Handle is\n\n      function Call_Eval (A : Mal_Handle) return Mal_Handle is\n      begin\n         return Eval (A, Env);\n      end Call_Eval;\n\n   begin\n      pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector\n      return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);\n   end Eval_Ast;\n\n   function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is\n      A0 : Mal_Handle;\n   begin\n      if Deref (Ast).Sym_Type /= List\n        or else Deref_List_Class (Ast).Get_List_Type /= List_List\n        or else Deref_List (Ast).Is_Null\n      then\n         return False;\n      end if;\n      A0 := Deref_List (Ast).Car;\n      return Deref (A0).Sym_Type = Sym\n        and then Deref_Sym (A0).Get_Sym = Symbol;\n   end Starts_With;\n\n   function Quasi_Quote_Processing (Param : Mal_Handle) return Mal_Handle is\n      Res, Elt, New_Res : Mal_Handle;\n      L : List_Ptr;\n   begin\n\n      if Debug then\n         Ada.Text_IO.Put_Line (\"QuasiQt \" & Deref (Param).To_String);\n      end if;\n\n      if Deref (Param).Sym_Type not in Sym | List then\n         --  No need to quote, Eval would not affect these anyway.\n         return Param;\n      end if;\n\n      if Deref (Param).Sym_Type /= List or else\n        Deref_List_Class (Param).Get_List_Type = Hashed_List then\n\n         -- return a new list containing: a symbol named \"quote\" and ast.\n         Res := New_List_Mal_Type (List_List);\n         L := Deref_List (Res);\n         L.Append (New_Symbol_Mal_Type (\"quote\"));\n         L.Append (Param);\n         return Res;\n\n      end if;\n\n      -- if the first element of ast is a symbol named \"unquote\":\n      if Starts_With (Param, \"unquote\") then\n         -- return the second element of ast.`\n         return Deref_List_Class (Param).Nth (1);\n\n      end if;\n\n      Res := New_List_Mal_Type (List_List);\n\n      for I in reverse 0 .. Deref_List_Class (Param).Length - 1 loop\n         Elt := Deref_List_Class (Param).Nth (I);\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         if Starts_With (Elt, \"splice-unquote\") then\n            L.Append (New_Symbol_Mal_Type (\"concat\"));\n            L.Append (Deref_List (Elt).Nth (1));\n         else\n            L.Append (New_Symbol_Mal_Type (\"cons\"));\n            L.Append (Quasi_Quote_Processing (Elt));\n         end if;\n         L.Append (Res);\n         Res := New_Res;\n      end loop;\n\n      if Deref_List_Class (Param).Get_List_Type = Vector_List then\n         New_Res := New_List_Mal_Type (List_List);\n         L := Deref_List (New_Res);\n         L.Append (New_Symbol_Mal_Type (\"vec\"));\n         L.Append (Res);\n         Res := New_Res;\n      end if;\n\n      return Res;\n\n   end Quasi_Quote_Processing;\n\n\n   function Catch_Processing\n              (Try_Line : Mal_Handle;\n               ExStr : Mal_Handle;\n               Env : Envs.Env_Handle)\n   return Mal_Handle is\n\n      L, CL, CL2, CL3 : List_Mal_Type;\n      C : Mal_Handle;\n      New_Env : Envs.Env_Handle;\n\n   begin\n\n      L := Deref_List (Try_Line).all;\n      C := Car (L);\n      -- CL is the list with the catch in.\n      CL := Deref_List (C).all;\n\n      CL2 := Deref_List (Cdr (CL)).all;\n      New_Env := Envs.New_Env (Env);\n      Envs.Set (New_Env, Deref_Sym (Car (CL2)).Get_Sym, ExStr);\n\n      CL3 := Deref_List (Cdr (CL2)).all;\n      return Eval (Car (CL3), New_Env);\n   end Catch_Processing;\n\n\n   function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)\n\t\t return Mal_Handle is\n      Param : Mal_Handle;\n      Env : Envs.Env_Handle;\n      First_Param, Rest_Params : Mal_Handle;\n      Rest_List, Param_List : List_Mal_Type;\n   begin\n\n      Param := AParam;\n      Env := AnEnv;\n\n      <<Tail_Call_Opt>>\n\n      begin\n         if Eval_As_Boolean (Envs.Get (Env, \"DEBUG-EVAL\")) then\n            Ada.Text_IO.Put_Line (\"EVAL: \" & Deref (Param).To_String);\n         end if;\n      exception\n         when Envs.Not_Found => null;\n      end;\n\n      case Deref (Param).Sym_Type is\n         when Sym =>\n            declare\n               Sym : Mal_String := Deref_Sym (Param).Get_Sym;\n            begin\n               -- if keyword, return it. Otherwise look it up in the environment.\n               if Sym(1) = ':' then\n                  return Param;\n               else\n                  return Envs.Get (Env, Sym);\n               end if;\n            exception\n               when Envs.Not_Found =>\n                  raise Envs.Not_Found with (\"'\" &  Sym & \"' not found\");\n            end;\n         when List =>\n         case Deref_List (Param).Get_List_Type is\n         when Hashed_List | Vector_List =>\n            return Eval_Ast (Param, Env);\n         when List_List =>\n\n         Param_List := Deref_List (Param).all;\n\n         -- Deal with empty list..\n         if Param_List.Length = 0 then\n\t    return Param;\n         end if;\n\n         First_Param := Car (Param_List);\n         Rest_Params := Cdr (Param_List);\n         Rest_List := Deref_List (Rest_Params).all;\n\n         if Deref (First_Param).Sym_Type = Sym and then\n            Deref_Sym (First_Param).Get_Sym = \"def!\" then\n \t    return Def_Fn (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"defmacro!\" then\n            return Def_Macro (Rest_List, Env);\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"let*\" then\n            declare\n               Defs, Expr, Res : Mal_Handle;\n               E : Envs.Env_Handle;\n            begin\n               E := Envs.New_Env (Env);\n               Defs := Car (Rest_List);\n               Deref_List_Class (Defs).Add_Defs (E);\n               Expr := Car (Deref_List (Cdr (Rest_List)).all);\n\t       Param := Expr;\n\t       Env := E;\n\t       goto Tail_Call_Opt;\n               -- was:\n               -- Res := Eval (Expr, E);\n               -- return Res;\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"do\" then\n            declare\n               D : List_Mal_Type;\n               E : Mal_Handle;\n            begin\n\n               if Debug then\n                  Ada.Text_IO.Put_Line (\"Do-ing \" & To_String (Rest_List));\n               end if;\n\n               if Is_Null (Rest_List) then\n                  return Rest_Params;\n               end if;\n\n               -- Loop processes Evals all but last entry\n               D := Rest_List;\n               loop\n                  E := Car (D);\n                  D := Deref_List (Cdr (D)).all;\n               exit when Is_Null (D);\n                  E := Eval (E, Env);\n               end loop;\n\n               Param := E;\n               goto Tail_Call_Opt;\n\n            end;\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"if\" then\n \t    declare\n\t       Args : List_Mal_Type := Rest_List;\n\n\t       Cond, True_Part, False_Part : Mal_Handle;\n\t       Cond_Bool : Boolean;\n\t       pragma Assert (Length (Args) = 2 or Length (Args) = 3,\n\t\t\t      \"If_Processing: not 2 or 3 parameters\");\n\t       L : List_Mal_Type;\n\t    begin\n\n\t       Cond := Eval (Car (Args), Env);\n\n\t       Cond_Bool := Eval_As_Boolean (Cond);\n\n\t       if Cond_Bool then\n\t          L := Deref_List (Cdr (Args)).all;\n\n\t          Param := Car (L);\n\t\t  goto Tail_Call_Opt;\n\t\t  -- was: return Eval (Car (L), Env);\n\t       else\n\t\t  if Length (Args) = 3 then\n\t\t     L := Deref_List (Cdr (Args)).all;\n\t\t     L := Deref_List (Cdr (L)).all;\n\n\t\t     Param := Car (L);\n\t\t     goto Tail_Call_Opt;\n\t\t     -- was: return Eval (Car (L), Env);\n\t          else\n\t\t     return New_Nil_Mal_Type;\n\t          end if;\n\t       end if;\n\t    end;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"fn*\" then\n\n            return New_Lambda_Mal_Type\n                     (Params => Car (Rest_List),\n                      Expr => Nth (Rest_List, 1),\n                      Env => Env);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quote\" then\n\n            return Car (Rest_List);\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"quasiquote\" then\n\n            Param := Quasi_Quote_Processing (Car (Rest_List));\n            goto Tail_Call_Opt;\n\n         elsif Deref (First_Param).Sym_Type = Sym and then\n               Deref_Sym (First_Param).Get_Sym = \"try*\" then\n\n            if Length (Rest_List) = 1 then\n               return Eval (Car (Rest_List), Env);\n            end if;\n            declare\n               Res : Mal_Handle;\n            begin\n               return Eval (Car (Rest_List), Env);\n            exception\n               when Mal_Exception =>\n                  Res := Catch_Processing\n                           (Cdr (Rest_List),\n                            Types.Mal_Exception_Value,\n                            Env);\n                  Types.Mal_Exception_Value :=\n                    Smart_Pointers.Null_Smart_Pointer;\n                  return Res;\n               when E : others =>\n                  return Catch_Processing\n                           (Cdr (Rest_List),\n                            New_String_Mal_Type\n                              (Ada.Exceptions.Exception_Message (E)),\n                            Env);\n            end;\n\n         else\n\n            -- The APPLY section.\n               First_Param  := Eval (First_Param, Env);\n\n               if Deref (First_Param).Sym_Type = Func then\n                  Rest_Params := Eval_Ast (Rest_Params, Env);\n                  return Call_Func (Deref_Func (First_Param).all, Rest_Params);\n               elsif Deref (First_Param).Sym_Type = Lambda then\n                  declare\n\n                     L : Lambda_Mal_Type;\n                     E : Envs.Env_Handle;\n                     Param_Names : List_Mal_Type;\n                     Res : Mal_Handle;\n\n                  begin\n\n                     L := Deref_Lambda (First_Param).all;\n\n                     if L.Get_Is_Macro then\n                        --  Apply to *unevaluated* arguments\n                        Param := L.Apply (Rest_Params);\n                        --  then EVAL the result.\n                        goto Tail_Call_Opt;\n                     end if;\n\n                     Rest_Params := Eval_Ast (Rest_Params, Env);\n\n                     E := Envs.New_Env (L.Get_Env);\n\n                     Param_Names := Deref_List (L.Get_Params).all;\n\n                     if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then\n\n\t\t\tParam := L.Get_Expr;\n                        Env := E;\n\t\t\tgoto Tail_Call_Opt;\n                        -- was: return Eval (L.Get_Expr, E);\n\n                     else\n\n                        raise Runtime_Exception with \"Bind failed in Apply\";\n\n                     end if;\n\n                  end;\n\n               else  -- neither a Lambda or a Func\n                  raise Runtime_Exception with \"Deref called on non-Func/Lambda\";\n               end if;\n\n         end if;\n\n         end case;\n      when others => -- not a list, map, symbol or vector\n         return Param;\n      end case;\n   end Eval;\n\n\n   function Print (Param : Types.Mal_Handle) return String is\n   begin\n      return Printer.Pr_Str (Param);\n   end Print;\n\n   function Rep (Param : String; Env : Envs.Env_Handle) return String is\n      AST, Evaluated_AST : Types.Mal_Handle;\n   begin\n\n      AST := Read (Param);\n\n      if Types.Is_Null (AST) then\n         return \"\";\n      else\n         Evaluated_AST := Eval (AST, Env);\n         return Print (Evaluated_AST);\n      end if;\n\n   end Rep;\n\n\n   Repl_Env : Envs.Env_Handle;\n\n\n   -- These two ops use Repl_Env directly.\n\n\n   procedure RE (Str : Mal_String) is\n      Discarded : Mal_Handle;\n   begin\n      Discarded := Eval (Read (Str), Repl_Env);\n   end RE;\n\n\n   function Do_Eval (Rest_Handle : Mal_Handle)\n   return Types.Mal_Handle is\n      First_Param : Mal_Handle;\n      Rest_List : Types.List_Mal_Type;\n   begin\n      Rest_List := Deref_List (Rest_Handle).all;\n      First_Param := Car (Rest_List);\n      return Eval_Callback.Eval.all (First_Param, Repl_Env);\n   end Do_Eval;\n\n\n   Cmd_Args, File_Param : Natural;\n   Command_Args : Types.Mal_Handle;\n   Command_List : Types.List_Ptr;\n   File_Processed : Boolean := False;\n\nbegin\n\n   -- Save a function pointer back to the Eval function.\n   -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK\n   -- as we know Eval will be in scope for the lifetime of the program.\n   Eval_Callback.Eval := Eval'Unrestricted_Access;\n\n   Repl_Env := Envs.New_Env;\n\n   -- Core init also creates the first environment.\n   -- This is needed for the def!'s below.\n   Core.Init (Repl_Env);\n\n   -- Register the eval command.  This needs to be done here rather than Core.Init\n   -- as it requires direct access to Repl_Env.\n   Envs.Set (Repl_Env, \"eval\", New_Func_Mal_Type (\"eval\", Do_Eval'Unrestricted_Access));\n\n   RE (\"(def! not (fn* (a) (if a false true)))\");\n   RE (\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\");\n   RE (\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons 'cond (rest (rest xs)))))))\");\n\n   -- Command line processing.\n\n   Cmd_Args := 0;\n   Command_Args := Types.New_List_Mal_Type (Types.List_List);\n   Command_List := Types.Deref_List (Command_Args);\n\n   while Ada.Command_Line.Argument_Count > Cmd_Args loop\n\n     Cmd_Args := Cmd_Args + 1;\n     if Ada.Command_Line.Argument (Cmd_Args) = \"-d\" then\n        Debug := True;\n     elsif Ada.Command_Line.Argument (Cmd_Args) = \"-e\" then\n        Envs.Debug := True;\n     elsif not File_Processed then\n        File_Param := Cmd_Args;\n        File_Processed := True;\n     else\n        Command_List.Append\n          (Types.New_String_Mal_Type (Ada.Command_Line.Argument (Cmd_Args)));\n     end if;\n\n   end loop;\n\n   Envs.Set (Repl_Env, \"*ARGV*\", Command_Args);\n\n   if File_Processed then\n      RE (\"(load-file \"\"\" & Ada.Command_Line.Argument (File_Param) & \"\"\")\");\n   else\n      RE(\"(println (str \"\"Mal [\"\" *host-language* \"\"]\"\"))\");\n      loop\n         begin\n            Ada.Text_IO.Put (\"user> \");\n            exit when Ada.Text_IO.End_Of_File;\n            Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));\n         exception\n            when E : others =>\n               Ada.Text_IO.Put_Line\n                 (Ada.Text_IO.Standard_Error,\n                  \"Error: \" & Ada.Exceptions.Exception_Information (E));\n               if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then\n                  Ada.Text_IO.Put_Line\n                    (Ada.Text_IO.Standard_Error,\n                     Printer.Pr_Str (Types.Mal_Exception_Value));\n                  Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;\n               end if;\n         end;\n      end loop;\n   end if;\nend StepA_Mal;\n"
  },
  {
    "path": "impls/ada/types-hash_map.adb",
    "content": "with Ada.Strings.Unbounded.Hash;\nwith Smart_Pointers;\n\npackage body Types.Hash_Map is\n\n   function \"=\" (A, B : Hash_Map_Mal_Type) return Boolean is\n      A_Key, A_Elem, B_Elem : Mal_Handle;\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      if A.Length /= B.Length then\n         return False;\n      end if;\n      C := A.Hash.First;\n      while Has_Element (C) loop\n         A_Key := Key (C);\n         A_Elem := Element (C);\n         B_Elem := Mal_Mal_Hash.Element (B.Hash, A_Key);\n         if A_Elem /= B_Elem then\n            return False;\n         end if;\n         Next (C);\n      end loop;\n      return True;\n   end \"=\";\n\n   function New_Hash_Map_Mal_Type\n   return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Hash_Map_Mal_Type'\n           (Mal_Type with\n              List_Type => Hashed_List,\n              The_List => Smart_Pointers.Null_Smart_Pointer,\n              Last_Elem => Smart_Pointers.Null_Smart_Pointer,\n              Is_Key_Expected => True,\n              Next_Key => Smart_Pointers.Null_Smart_Pointer,\n              Hash => Mal_Mal_Hash.Empty_Map));\n   end New_Hash_Map_Mal_Type;\n\n   overriding function Prepend (Op : Mal_Handle; To_Vector : Hash_Map_Mal_Type)\n   return Mal_Handle is\n   begin\n      raise Not_Appropriate;\n      return Smart_Pointers.Null_Smart_Pointer;\n   end Prepend;\n\n   overriding procedure Append (V : in out Hash_Map_Mal_Type; E : Mal_Handle) is\n   begin\n      if V.Is_Key_Expected then\n         V.Next_Key := E;\n      else\n         Mal_Mal_Hash.Include\n           (Container => V.Hash,\n            Key => V.Next_Key,\n            New_Item => E);\n      end if;\n      V.Is_Key_Expected := not V.Is_Key_Expected;\n   end Append;\n\n   overriding function Length (L : Hash_Map_Mal_Type) return Natural is\n   begin\n      return Natural (L.Hash.Length);\n   end Length;\n\n   overriding function Is_Null (L : Hash_Map_Mal_Type) return Boolean is\n   begin\n      return L.Hash.Is_Empty;\n   end Is_Null;\n\n   overriding function Null_List (L : List_Types) return Hash_Map_Mal_Type is\n   begin\n      return \n        Hash_Map_Mal_Type'\n           (Mal_Type with\n              List_Type => Hashed_List,\n              The_List => Smart_Pointers.Null_Smart_Pointer,\n              Last_Elem => Smart_Pointers.Null_Smart_Pointer,\n              Is_Key_Expected => False,\n              Next_Key => Smart_Pointers.Null_Smart_Pointer,\n              Hash => Mal_Mal_Hash.Empty_Map);\n   end Null_List;\n\n   -- Duplicate copies the list (logically).  This is to allow concatenation,\n   -- The result is always a List_List.\n   overriding function Duplicate (The_List : Hash_Map_Mal_Type) return Mal_Handle is\n   begin\n      raise Not_Appropriate;\n      return Smart_Pointers.Null_Smart_Pointer;\n   end Duplicate;\n\n   overriding function Nth (L :Hash_Map_Mal_Type; N : Natural) return Mal_Handle is\n   begin\n      raise Not_Appropriate;\n      return Smart_Pointers.Null_Smart_Pointer;\n   end Nth;\n\n   overriding procedure Add_Defs (Defs : Hash_Map_Mal_Type; Env : Envs.Env_Handle) is\n   begin\n      raise Not_Appropriate;\n   end Add_Defs;\n\n   -- Get the first item in the list:\n   overriding function Car (L : Hash_Map_Mal_Type) return Mal_Handle is\n   begin\n      raise Not_Appropriate;\n      return Smart_Pointers.Null_Smart_Pointer;\n   end Car;\n\n   -- Get the rest of the list (second item onwards)\n   overriding function Cdr (L : Hash_Map_Mal_Type) return Mal_Handle is\n   begin\n      raise Not_Appropriate;\n      return Smart_Pointers.Null_Smart_Pointer;\n   end Cdr;\n\n\n   overriding function Map\n     (Func_Ptr : Func_Access;\n      L : Hash_Map_Mal_Type)\n   return Mal_Handle is\n      Res : Mal_Handle;\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      Res := New_Hash_Map_Mal_Type;\n      C := L.Hash.First;\n      while Has_Element (C) loop\n         -- Assuming we're not applying the func to the keys too.\n         Deref_Hash (Res).Hash.Include\n           (Key => Key (C),\n            New_Item => Func_Ptr (Element (C)));\n         Next (C);\n      end loop;\n      return Res;\n   end Map;\n\n   function Assoc (H : Hash_Map_Mal_Type; List : Mal_Handle) return Mal_Handle is\n      Res : Mal_Handle;\n      Rest_List : List_Mal_Type;\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      Res := New_Hash_Map_Mal_Type;\n      Rest_List := Deref_List (List).all;\n\n      -- Copy arg into result.\n      Deref_Hash (Res).Hash := H.Hash;\n         \n      while not Is_Null (Rest_List) loop\n         Deref_Hash (Res).Append (Car (Rest_List));\n         Rest_List := Deref_List (Cdr (Rest_List)).all;\n      end loop;\n      return Res;\n   end Assoc;\n\n\n   function Dis_Assoc (H : Hash_Map_Mal_Type; List : Mal_Handle) return Mal_Handle is\n      Res : Mal_Handle;\n      Rest_List : List_Mal_Type;\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      Res := New_Hash_Map_Mal_Type;\n      Rest_List := Deref_List (List).all;\n\n      -- Copy arg into result.\n      Deref_Hash (Res).Hash := H.Hash;\n         \n      while not Is_Null (Rest_List) loop\n         Mal_Mal_Hash.Exclude (Deref_Hash (Res).Hash, Car (Rest_List));\n         Rest_List := Deref_List (Cdr (Rest_List)).all;\n      end loop;\n      return Res;\n   end Dis_Assoc;\n\n\n   function Get (H : Hash_Map_Mal_Type; Key : Mal_Handle) return Mal_Handle is\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      C := Mal_Mal_Hash.Find (H.Hash, Key);\n      if Has_Element (C) then\n         return Element (C);\n      else\n         return New_Nil_Mal_Type;\n      end if;\n   end Get;\n\n\n   function All_Keys (H : Hash_Map_Mal_Type) return Mal_Handle is\n      Res, Map_Key : Mal_Handle;\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      Res := New_List_Mal_Type (List_List);\n      C := H.Hash.First;\n      while Has_Element (C) loop\n         Map_Key := Key (C);\n         Deref_List (Res).Append (Map_Key);\n         Next (C);\n      end loop;\n      return Res;\n   end All_Keys;\n\n\n   function All_Values (H : Hash_Map_Mal_Type) return Mal_Handle is\n      Res, Map_Val : Mal_Handle;\n      use Mal_Mal_Hash;\n      C : Cursor;\n   begin\n      Res := New_List_Mal_Type (List_List);\n      C := H.Hash.First;\n      while Has_Element (C) loop\n         Map_Val := Element (C);\n         Deref_List (Res).Append (Map_Val);\n         Next (C);\n      end loop;\n      return Res;\n   end All_Values;\n\n\n   function Contains (H : Hash_Map_Mal_Type; Key : Mal_Handle) return Boolean is\n   begin\n      return Mal_Mal_Hash.Contains (H.Hash, Key);\n   end Contains;\n\n   function Deref_Hash (SP : Mal_Handle) return Hash_Ptr is\n   begin\n      return Hash_Ptr (Deref (SP));\n   end Deref_Hash;\n\n   function Hash (M : Mal_Handle) return Ada.Containers.Hash_Type is\n   begin\n      return Ada.Strings.Unbounded.Hash\n               (Ada.Strings.Unbounded.To_Unbounded_String\n                 (Deref (M).To_String));\n   end Hash;\n\n   overriding function To_Str \n     (T : Hash_Map_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n     use Ada.Containers;\n   begin\n      if (T.Hash.Length = 0) then\n         return Opening (T.List_Type) &\n                Closing (T.List_Type);\n      else\n         declare\n            Res : Ada.Strings.Unbounded.Unbounded_String;\n            use Mal_Mal_Hash;\n            C : Cursor;\n         begin\n            C := First (T.Hash);\n \n            Res := Ada.Strings.Unbounded.\"&\"\n                     (Opening (T.List_Type),\n                      Ada.Strings.Unbounded.To_Unbounded_String\n                        (To_String (Deref (Key (C)).all, Print_Readably)));\n            Res := Ada.Strings.Unbounded.\"&\" (Res, \" \");\n            Res := Ada.Strings.Unbounded.\"&\"\n                     (Res,\n                      Ada.Strings.Unbounded.To_Unbounded_String\n                        (To_String (Deref (Element (C)).all, Print_Readably)));\n            Next (C);\n            while Has_Element (C) loop\n               Res := Ada.Strings.Unbounded.\"&\" (Res, \" \");\n               Res := Ada.Strings.Unbounded.\"&\"\n                        (Res,\n                         Ada.Strings.Unbounded.To_Unbounded_String\n                           (To_String (Deref (Key (C)).all, Print_Readably)));\n               Res := Ada.Strings.Unbounded.\"&\" (Res, \" \");\n               Res := Ada.Strings.Unbounded.\"&\"\n                 (Res,\n                  Ada.Strings.Unbounded.To_Unbounded_String\n                    (To_String (Deref (Element (C)).all, Print_Readably)));\n               Next (C);\n            end loop;\n            Res := Ada.Strings.Unbounded.\"&\" (Res, Closing (T.List_Type));\n            return Ada.Strings.Unbounded.To_String (Res);\n         end;\n      end if;\n   end To_Str;\n\nend Types.Hash_Map;\n"
  },
  {
    "path": "impls/ada/types-hash_map.ads",
    "content": "with Ada.Containers.Hashed_Maps;\nwith Smart_Pointers;\nwith Envs;\n\npackage Types.Hash_Map is\n\n   type Hash_Map_Mal_Type is new List_Mal_Type with private;\n\n   function New_Hash_Map_Mal_Type\n   return Mal_Handle;\n\n   function \"=\" (A, B : Hash_Map_Mal_Type) return Boolean;\n\n   overriding function Prepend (Op : Mal_Handle; To_Vector : Hash_Map_Mal_Type)\n   return Mal_Handle;\n\n   overriding procedure Append (V : in out Hash_Map_Mal_Type; E : Mal_Handle);\n\n   overriding function Length (L : Hash_Map_Mal_Type) return Natural;\n\n   overriding function Is_Null (L : Hash_Map_Mal_Type) return Boolean;\n\n   overriding function Null_List (L : List_Types) return Hash_Map_Mal_Type;\n\n   -- Duplicate copies the list (logically).  This is to allow concatenation,\n   -- The result is always a List_List.\n   overriding function Duplicate (The_List : Hash_Map_Mal_Type) return Mal_Handle;\n\n   overriding function Nth (L :Hash_Map_Mal_Type; N : Natural) return Mal_Handle;\n\n   overriding procedure Add_Defs (Defs : Hash_Map_Mal_Type; Env : Envs.Env_Handle);\n\n   -- Get the first item in the list:\n   overriding function Car (L : Hash_Map_Mal_Type) return Mal_Handle;\n\n   -- Get the rest of the list (second item onwards)\n   overriding function Cdr (L : Hash_Map_Mal_Type) return Mal_Handle;\n\n   overriding function Map\n     (Func_Ptr : Func_Access;\n      L : Hash_Map_Mal_Type)\n   return Mal_Handle;\n\n   function Assoc (H : Hash_Map_Mal_Type; List : Mal_Handle) return Mal_Handle;\n\n   function Dis_Assoc (H : Hash_Map_Mal_Type; List : Mal_Handle) return Mal_Handle;\n\n   function Get (H : Hash_Map_Mal_Type; Key : Mal_Handle) return Mal_Handle;\n\n   function All_Keys (H : Hash_Map_Mal_Type) return Mal_Handle;\n\n   function All_Values (H : Hash_Map_Mal_Type) return Mal_Handle;\n\n   function Contains (H : Hash_Map_Mal_Type; Key : Mal_Handle) return Boolean;\n\n   type Hash_Ptr is access all Hash_Map_Mal_Type;\n\n   function Deref_Hash (SP : Mal_Handle) return Hash_Ptr;\n\n   Not_Appropriate : exception;\n\nprivate\n\n   function Hash (M : Mal_Handle) return Ada.Containers.Hash_Type;\n\n   package Mal_Mal_Hash is new Ada.Containers.Hashed_Maps\n     (Key_Type        => Mal_Handle,\n      Element_Type    => Mal_Handle,\n      Hash            => Hash,\n      Equivalent_Keys => \"=\",\n      \"=\"             => \"=\");\n\n   type Hash_Map_Mal_Type is new List_Mal_Type with record\n      Is_Key_Expected : Boolean := True;\n      Next_Key : Mal_Handle;\n      Hash : Mal_Mal_Hash.Map;\n   end record;\n\n   overriding function To_Str \n     (T : Hash_Map_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\nend Types.Hash_Map;\n"
  },
  {
    "path": "impls/ada/types-vector.adb",
    "content": "with Ada.Strings.Unbounded;\nwith Ada.Text_IO;\nwith Eval_Callback;\n\npackage body Types.Vector is\n\n\n   function New_Vector_Mal_Type\n   return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Vector_Mal_Type'\n           (Mal_Type with\n              List_Type => Vector_List,\n              The_List => Smart_Pointers.Null_Smart_Pointer,\n              Last_Elem => Smart_Pointers.Null_Smart_Pointer,\n              Vec => Mal_Vectors.Empty_Vector));\n   end New_Vector_Mal_Type;\n\n\n   overriding function Prepend (Op : Mal_Handle; To_Vector : Vector_Mal_Type)\n   return Mal_Handle is\n   begin\n      return Types.Prepend (Op, Deref_List (To_Vector.Duplicate).all);\n   end Prepend;\n\n\n   overriding procedure Append (V : in out Vector_Mal_Type; E : Mal_Handle) is\n   begin\n      Mal_Vectors.Append (V.Vec, E);\n   end Append;\n\n\n   overriding function Is_Null (L : Vector_Mal_Type) return Boolean is\n     use Ada.Containers;\n   begin\n      return L.Vec.Is_Empty;\n   end Is_Null;\n\n\n   overriding function Null_List (L : List_Types) return Vector_Mal_Type is\n   begin\n      return Vector_Mal_Type'\n               (Mal_Type with\n                  List_Type => Vector_List,\n                  The_List => Smart_Pointers.Null_Smart_Pointer,\n                  Last_Elem => Smart_Pointers.Null_Smart_Pointer,\n                  Vec => Mal_Vectors.Empty_Vector);\n   end Null_List;\n\n\n   -- Duplicate copies the list (logically).  This is to allow concatenation,\n   -- The result is always a List_List.\n   overriding function Duplicate (The_List : Vector_Mal_Type) return Mal_Handle is\n      Res : Mal_Handle;\n      use Mal_Vectors;\n      C : Cursor;\n   begin\n      Res := New_List_Mal_Type (List_List);\n      C := First (The_List.Vec);\n      while Has_Element (C) loop\n         Deref_List (Res).Append (Element (C));\n         Next (C);\n      end loop;\n      return Res;\n   end Duplicate;\n      \n\n   function Length (L : Vector_Mal_Type) return Natural is\n   begin\n      return Natural (L.Vec.Length);\n   end Length;\n\n\n   procedure Add_Defs (Defs : Vector_Mal_Type; Env : Envs.Env_Handle) is\n      C, D : Cursor;\n   begin\n      C := Defs.Vec.First;\n      while Has_Element (C) loop\n         D := Next (C);\n         exit when not Has_Element (D);\n         Envs.Set\n           (Env,\n            Deref_Sym (Element (C)).Get_Sym,\n            Eval_Callback.Eval.all (Element (D), Env));\n         C := Next (D);\n      end loop;\n   end Add_Defs;\n\n\n   overriding function Nth (L : Vector_Mal_Type; N : Natural) return Mal_Handle is\n   begin\n      if N >= L.Length then\n         raise Runtime_Exception with \"Nth (vector): Index out of range\";\n      else\n         return Mal_Vectors.Element (L.Vec, Vec_Index (N));\n      end if;\n   end Nth;\n\n\n   -- Get the first item in the list:\n   overriding function Car (L : Vector_Mal_Type) return Mal_Handle is\n   begin\n      return L.Vec.Element (0);\n   end Car;\n\n   -- Get the rest of the list (second item onwards)\n\n   overriding function Cdr (L : Vector_Mal_Type) return Mal_Handle is\n      Res : Mal_Handle;\n      Vec_P : Vector_Ptr;\n      C : Mal_Vectors.Cursor;\n      I : Vec_Index;\n      use Ada.Containers;\n   begin\n      Res := New_Vector_Mal_Type;\n      if L.Vec.Length < 2 then\n         return Res;\n      end if;\n      Vec_P := Deref_Vector (Res);\n      Vec_P.Vec := To_Vector (L.Vec.Length - 1);\n\n      -- Set C to second entry.\n      C := L.Vec.First;\n      Mal_Vectors.Next (C);\n\n      I := 0;\n      while Mal_Vectors.Has_Element (C) loop\n         Mal_Vectors.Replace_Element (Vec_P.Vec, I, Mal_Vectors.Element (C));\n         Mal_Vectors.Next (C);\n         I := I + 1;\n      end loop;\n      return Res;\n   end Cdr;\n\n   overriding function Map\n     (Func_Ptr : Func_Access;\n      L : Vector_Mal_Type)\n   return Mal_Handle is\n      Res : Mal_Handle;\n      use Mal_Vectors;\n      C : Cursor;\n   begin\n      Res := New_Vector_Mal_Type;\n      C := First (L.Vec);\n      while Has_Element (C) loop\n         Deref_Vector (Res).Append (Func_Ptr.all (Element (C)));\n         Next (C);\n      end loop;\n      return Res;\n   end Map;\n\n\n   function Deref_Vector (SP : Mal_Handle) return Vector_Ptr is\n   begin\n      return Vector_Ptr (Deref (SP));\n   end Deref_Vector;\n\n\n   overriding function To_Str \n     (T : Vector_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n     use Ada.Containers;\n   begin\n      if (T.Vec.Length = 0) then\n         return Opening (T.List_Type) &\n                Closing (T.List_Type);\n      else\n         declare\n            Res : Ada.Strings.Unbounded.Unbounded_String;\n            use Mal_Vectors;\n            C : Cursor;\n         begin\n            C := First (T.Vec);\n \n            Res := Ada.Strings.Unbounded.\"&\"\n                     (Opening (T.List_Type),\n                      Ada.Strings.Unbounded.To_Unbounded_String\n                        (To_String (Deref (Element (C)).all, Print_Readably)));\n            Next (C);\n            while Has_Element (C) loop\n               Res := Ada.Strings.Unbounded.\"&\" (Res, \" \");\n               Res := Ada.Strings.Unbounded.\"&\"\n                 (Res,\n                  Ada.Strings.Unbounded.To_Unbounded_String\n                    (To_String (Deref (Element (C)).all, Print_Readably)));\n               Next (C);\n            end loop;\n            Res := Ada.Strings.Unbounded.\"&\" (Res, Closing (T.List_Type));\n            return Ada.Strings.Unbounded.To_String (Res);\n         end;\n      end if;\n   end To_Str;\n\n\nend Types.Vector;\n"
  },
  {
    "path": "impls/ada/types-vector.ads",
    "content": "with Ada.Containers.Vectors;\nwith Ada.Strings.Unbounded;\nwith Smart_Pointers;\nwith Envs;\n\npackage Types.Vector is\n\n   type Vector_Mal_Type is new List_Mal_Type with private;\n\n   function New_Vector_Mal_Type\n   return Mal_Handle;\n\n   overriding function Prepend (Op : Mal_Handle; To_Vector : Vector_Mal_Type)\n   return Mal_Handle;\n\n   overriding procedure Append (V : in out Vector_Mal_Type; E : Mal_Handle);\n\n   overriding function Length (L : Vector_Mal_Type) return Natural;\n\n   overriding function Is_Null (L : Vector_Mal_Type) return Boolean;\n\n   overriding function Null_List (L : List_Types) return Vector_Mal_Type;\n\n   -- Duplicate copies the list (logically).  This is to allow concatenation,\n   -- The result is always a List_List.\n   overriding function Duplicate (The_List : Vector_Mal_Type) return Mal_Handle;\n\n   overriding function Nth (L : Vector_Mal_Type; N : Natural) return Mal_Handle;\n\n   overriding procedure Add_Defs (Defs : Vector_Mal_Type; Env : Envs.Env_Handle);\n\n   -- Get the first item in the list:\n   overriding function Car (L : Vector_Mal_Type) return Mal_Handle;\n\n   -- Get the rest of the list (second item onwards)\n   overriding function Cdr (L : Vector_Mal_Type) return Mal_Handle;\n\n   overriding function Map\n     (Func_Ptr : Func_Access;\n      L : Vector_Mal_Type)\n   return Mal_Handle;\n\n   type Vector_Ptr is access all Vector_Mal_Type;\n\n   function Deref_Vector (SP : Mal_Handle) return Vector_Ptr;\n\nprivate\n\n   subtype Vec_Index is Integer range 0 .. 100;\n   package Mal_Vectors is new\n     Ada.Containers.Vectors\n       (Index_Type => Vec_Index,\n        Element_Type => Mal_Handle,\n        \"=\" => \"=\");\n\n   use Mal_Vectors;\n\n   type Vector_Mal_Type is new List_Mal_Type with record\n      Vec : Mal_Vectors.Vector;\n   end record;\n\n   overriding function To_Str \n     (T : Vector_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\nend Types.Vector;\n"
  },
  {
    "path": "impls/ada/types.adb",
    "content": "with Ada.Characters.Latin_1;\nwith Ada.Strings.Fixed;\nwith Ada.Strings.Maps.Constants;\nwith Ada.Text_IO;\nwith Ada.Unchecked_Deallocation;\nwith Envs;\nwith Eval_Callback;\nwith Smart_Pointers;\nwith Types.Vector;\nwith Types.Hash_Map;\n\npackage body Types is\n\n   package ACL renames Ada.Characters.Latin_1;\n\n   function Nodes_Equal (A, B : Mal_Handle) return Boolean;\n\n\n   function \"=\" (A, B : Mal_Handle) return Mal_Handle is\n   begin\n      return New_Bool_Mal_Type (A = B);\n   end \"=\";\n\n\n   function Compare_List_And_Vector (A : List_Mal_Type; B : List_Mal_Type'Class)\n   return Boolean is\n      First_Node, First_Index : Mal_Handle;\n      I : Natural := 0;\n   begin\n      First_Node := A.The_List;\n      loop\n         if not Is_Null (First_Node) and I < B.Length then\n            First_Index := B.Nth (I);\n            if not \"=\" (Deref_Node (First_Node).Data, First_Index) then\n               return False;\n            end if;\n            First_Node := Deref_Node (First_Node).Next;\n            I := I + 1;\n         else\n            return Is_Null (First_Node) and I = B.Length;\n         end if;\n      end loop;\n   end Compare_List_And_Vector;\n\n\n   function \"=\" (A, B : Mal_Handle) return Boolean is\n      use Types.Vector;\n      use Types.Hash_Map;\n   begin\n\n      if (not Is_Null (A) and not Is_Null (B)) and then\n         Deref (A).Sym_Type = Deref (B).Sym_Type then\n\n         case Deref (A).Sym_Type is\n            when Nil =>\n               return True; -- Both nil.\n            when Int =>\n               return (Deref_Int (A).Get_Int_Val = Deref_Int (B).Get_Int_Val);\n            when Floating =>\n               return (Deref_Float (A).Get_Float_Val = Deref_Float (B).Get_Float_Val);\n            when Bool =>\n               return (Deref_Bool (A).Get_Bool = Deref_Bool (B).Get_Bool);\n            when List =>\n            -- When Types.Vector was added, the choice was:\n            -- 1) use interfaces (because you need a class hierachy for the containers\n            --    and a corresponding hierarchy for the cursors and Ada is single dispatch\n            --    + interfaces.\n            -- 2) map out the combinations here and use nth to access vector items.\n               case Deref_List (A).Get_List_Type is\n                  when List_List =>\n                     case Deref_List (B).Get_List_Type is\n                        when List_List => \n                           return Nodes_Equal (Deref_List (A).The_List, Deref_List (B).The_List);\n                        when Vector_List =>\n                           return Compare_List_And_Vector\n                                    (Deref_List (A).all, Deref_List_Class (B).all);\n                        when Hashed_List => return False; -- Comparing a list and a hash\n                     end case;\n                  when Vector_List =>\n                     case Deref_List (B).Get_List_Type is\n                        when List_List =>\n                           return Compare_List_And_Vector\n                                    (Deref_List (B).all, Deref_List_Class (A).all);\n                        when Vector_List =>\n                           return Vector.\"=\" (Deref_Vector (A).all, Deref_Vector (B).all);\n                        when Hashed_List => return False; -- Comparing a vector and a hash\n                     end case;\n                  when Hashed_List =>\n                     case Deref_List (B).Get_List_Type is\n                        when List_List => return False; -- Comparing a list and a hash\n                        when Vector_List => return False; -- Comparing a vector and a hash\n                        when Hashed_List =>\n                           return Hash_Map.\"=\" (Deref_Hash (A).all, Deref_Hash (B).all);\n                     end case;\n               end case;\n            when Str =>\n               return (Deref_String (A).Get_String = Deref_String (B).Get_String);\n            when Sym =>\n               return (Deref_Sym (A).Get_Sym = Deref_Sym (B).Get_Sym);\n            when Atom =>\n               return (Deref_Atom (A).Get_Atom = Deref_Atom (B).Get_Atom);\n            when Func =>\n               return (Deref_Func (A).Get_Func_Name = Deref_Func (B).Get_Func_Name);\n            when Node =>\n               return (Deref_Int(A).Get_Int_Val = Deref_Int(B).Get_Int_Val);\n            when Lambda =>\n               return (Deref_Int(A).Get_Int_Val = Deref_Int(B).Get_Int_Val);\n            when Error =>\n               return (Deref_Int(A).Get_Int_Val = Deref_Int(B).Get_Int_Val);\n          end case;\n      elsif Is_Null (A) and Is_Null (B) then\n         return True;\n      else  -- either one of the args is null or the sym_types don't match\n         return False;\n      end if;\n   end \"=\";\n\n   function Get_Meta (T : Mal_Type) return Mal_Handle is\n   begin\n       if T.Meta = Smart_Pointers.Null_Smart_Pointer then\n          return New_Nil_Mal_Type;\n       else\n          return T.Meta;\n       end if;\n   end Get_Meta;\n\n   procedure Set_Meta (T : in out Mal_Type'Class; SP : Mal_Handle) is\n   begin\n      T.Meta := SP;\n   end Set_Meta;\n\n   function Copy (M : Mal_Handle) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Mal_Type'Class'(Deref (M).all));\n   end Copy;\n\n   function To_String (T : Mal_Type'Class; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      return To_Str (T, Print_Readably);\n   end To_String;\n\n   -- A helper function that just view converts the smart pointer.\n   function Deref (S : Mal_Handle) return Mal_Ptr is\n   begin\n      return Mal_Ptr (Smart_Pointers.Deref (S));\n   end Deref;\n\n   -- A helper function to detect null smart pointers.\n   function Is_Null (S : Mal_Handle) return Boolean is\n      use Smart_Pointers;\n   begin\n      return Smart_Pointers.\"=\"(S, Null_Smart_Pointer);\n   end Is_Null;\n\n\n   -- To_Str on the abstract type...\n   function To_Str (T : Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      raise Constraint_Error;  -- Tha'll teach 'ee\n      return \"\";  -- Keeps the compiler happy.\n   end To_Str;\n\n\n   function New_Nil_Mal_Type return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Nil_Mal_Type'(Mal_Type with null record));\n   end New_Nil_Mal_Type;\n\n   overriding function Sym_Type (T : Nil_Mal_Type) return Sym_Types is\n   begin\n      return Nil;\n   end Sym_Type;\n\n   overriding function To_Str (T : Nil_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      return \"nil\";\n   end To_Str;\n\n\n   function New_Int_Mal_Type (Int : Mal_Integer) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Int_Mal_Type'(Mal_Type with Int_Val => Int));\n   end New_Int_Mal_Type;\n\n   overriding function Sym_Type (T : Int_Mal_Type) return Sym_Types is\n   begin\n      return Int;\n   end Sym_Type;\n\n   function Get_Int_Val (T : Int_Mal_Type) return Mal_Integer is\n   begin\n      return T.Int_Val;\n   end Get_Int_Val;\n\n   overriding function To_Str\n     (T : Int_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n      Res : Mal_String := Mal_Integer'Image (T.Int_Val);\n   begin\n      return Ada.Strings.Fixed.Trim (Res, Ada.Strings.Left);\n   end To_Str;\n\n   function Deref_Int (SP : Mal_Handle) return Int_Ptr is\n   begin\n      return Int_Ptr (Deref (SP));\n   end Deref_Int;\n\n\n   function New_Float_Mal_Type (Floating : Mal_Float) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Float_Mal_Type'(Mal_Type with Float_Val => Floating));\n   end New_Float_Mal_Type;\n\n   overriding function Sym_Type (T : Float_Mal_Type) return Sym_Types is\n   begin\n      return Floating;\n   end Sym_Type;\n\n   function Get_Float_Val (T : Float_Mal_Type) return Mal_Float is\n   begin\n      return T.Float_Val;\n   end Get_Float_Val;\n\n   overriding function To_Str \n     (T : Float_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n      Res : Mal_String := Mal_Float'Image (T.Float_Val);\n   begin\n      return Ada.Strings.Fixed.Trim (Res, Ada.Strings.Left);\n   end To_Str;\n\n   function Deref_Float (SP : Mal_Handle) return Float_Ptr is\n   begin\n      return Float_Ptr (Deref (SP));\n   end Deref_Float;\n\n\n   function New_Bool_Mal_Type (Bool : Boolean) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Bool_Mal_Type'(Mal_Type with Bool_Val => Bool));\n   end New_Bool_Mal_Type;\n\n   overriding function Sym_Type (T : Bool_Mal_Type) return Sym_Types is\n   begin\n      return Bool;\n   end Sym_Type;\n\n   function Get_Bool (T : Bool_Mal_Type) return Boolean is\n   begin\n      return T.Bool_Val;\n   end Get_Bool;\n\n   overriding function To_Str \n     (T : Bool_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n      Res : Mal_String := Boolean'Image (T.Bool_Val);\n   begin\n     return Ada.Strings.Fixed.Translate\n              (Res, Ada.Strings.Maps.Constants.Lower_Case_Map);\n   end To_Str;\n\n   function Deref_Bool (SP : Mal_Handle) return Bool_Ptr is\n   begin\n      return Bool_Ptr (Deref (SP));\n   end Deref_Bool;\n\n\n   function New_String_Mal_Type (Str : Mal_String) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new String_Mal_Type' (Mal_Type with The_String =>\n           Ada.Strings.Unbounded.To_Unbounded_String (Str)));\n   end New_String_Mal_Type;\n\n   overriding function Sym_Type (T : String_Mal_Type) return Sym_Types is\n   begin\n      return Str;\n   end Sym_Type;\n\n   function Get_String (T : String_Mal_Type) return Mal_String is\n   begin\n      return Ada.Strings.Unbounded.To_String (T.The_String);\n   end Get_String;\n\n   function Deref_String (SP : Mal_Handle) return String_Ptr is\n   begin\n      return String_Ptr (Deref (SP));\n   end Deref_String;\n\n\n   overriding function To_Str \n     (T : String_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n      use Ada.Strings.Unbounded;\n      I : Positive := 1;\n      Str_Len : Natural;\n      Res : Unbounded_String;\n      Ch : Character;\n   begin\n      if Print_Readably then\n         Append (Res, '\"');\n         Str_Len := Length (T.The_String);\n         while I <= Str_Len loop\n            Ch := Element (T.The_String, I);\n            if Ch = '\"' then\n               Append (Res, \"\\\"\"\");\n            elsif Ch = '\\' then\n               Append (Res, \"\\\\\");\n            elsif Ch = Ada.Characters.Latin_1.LF then\n               Append (Res, \"\\n\");\n            else\n               Append (Res, Ch);\n            end if;\n            I := I + 1;\n         end loop;\n         Append (Res, '\"');\n         return To_String (Res);\n      else\n         return To_String (T.The_String);\n      end if;\n   end To_Str;\n\n\n   function New_Symbol_Mal_Type (Str : Mal_String) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Symbol_Mal_Type'(Mal_Type with The_Symbol =>\n           Ada.Strings.Unbounded.To_Unbounded_String (Str)));\n   end New_Symbol_Mal_Type;\n\n   overriding function Sym_Type (T : Symbol_Mal_Type) return Sym_Types is\n   begin\n      return Sym;\n   end Sym_Type;\n\n   function Get_Sym (T : Symbol_Mal_Type) return Mal_String is\n   begin\n      return Ada.Strings.Unbounded.To_String (T.The_Symbol);\n   end Get_Sym;\n\n   function Deref_Sym (S : Mal_Handle) return Sym_Ptr is\n   begin\n      return Sym_Ptr (Deref (S));\n   end Deref_Sym;\n\n   overriding function To_Str \n     (T : Symbol_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      return Ada.Strings.Unbounded.To_String (T.The_Symbol);\n   end To_Str;\n\n\n   function New_Atom_Mal_Type (MH : Mal_Handle) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Atom_Mal_Type'(Mal_Type with The_Atom => MH));\n   end New_Atom_Mal_Type;\n\n   overriding function Sym_Type (T : Atom_Mal_Type) return Sym_Types is\n   begin\n      return Atom;\n   end Sym_Type;\n\n   function Get_Atom (T : Atom_Mal_Type) return Mal_Handle is\n   begin\n      return T.The_Atom;\n   end Get_Atom;\n\n   procedure Set_Atom (T : in out Atom_Mal_Type; New_Val : Mal_Handle) is\n   begin\n      T.The_Atom := New_Val;\n   end Set_Atom;\n\n   function Deref_Atom (S : Mal_Handle) return Atom_Ptr is\n   begin\n      return Atom_Ptr (Deref (S));\n   end Deref_Atom;\n\n   overriding function To_Str \n     (T : Atom_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      return \"(atom \" & To_String (Deref (T.The_Atom).all) & ')';\n   end To_Str;\n\n\n   function New_Func_Mal_Type (Str : Mal_String; F : Builtin_Func)\n   return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Func_Mal_Type'(Mal_Type with\n          Func_Name => Ada.Strings.Unbounded.To_Unbounded_String (Str),\n          Func_P => F));\n   end New_Func_Mal_Type;\n\n   overriding function Sym_Type (T : Func_Mal_Type) return Sym_Types is\n   begin\n      return Func;\n   end Sym_Type;\n\n   function Get_Func_Name (T : Func_Mal_Type) return Mal_String is\n   begin\n      return Ada.Strings.Unbounded.To_String (T.Func_Name);\n   end Get_Func_Name;\n\n   function Call_Func\n     (FMT : Func_Mal_Type; Rest_List : Mal_Handle)\n   return Mal_Handle is\n   begin\n      return FMT.Func_P (Rest_List);\n   end Call_Func;\n\n   function Deref_Func (S : Mal_Handle) return Func_Ptr is\n   begin\n      return Func_Ptr (Deref (S));\n   end Deref_Func;\n\n   overriding function To_Str \n     (T : Func_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      return Ada.Strings.Unbounded.To_String (T.Func_Name);\n   end To_Str;\n\n\n   function New_Error_Mal_Type (Str : Mal_String) return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Error_Mal_Type'(Mal_Type with Error_Msg =>\n           Ada.Strings.Unbounded.To_Unbounded_String (Str)));\n   end New_Error_Mal_Type;\n\n   overriding function Sym_Type (T : Error_Mal_Type) return Sym_Types is\n   begin\n      return Error;\n   end Sym_Type;\n\n   overriding function To_Str \n     (T : Error_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      return Ada.Strings.Unbounded.To_String (T.Error_Msg);\n   end To_Str;\n\n\n   function Nodes_Equal (A, B : Mal_Handle) return Boolean is\n   begin\n      if (not Is_Null (A) and not Is_Null (B)) and then\n         Deref (A).Sym_Type = Deref (B).Sym_Type then\n         if Deref (A).Sym_Type = Node then\n            return\n              Nodes_Equal (Deref_Node (A).Data, Deref_Node (B).Data) and then\n              Nodes_Equal (Deref_Node (A).Next, Deref_Node (B).Next);\n         else\n            return A = B; \n         end if;\n      elsif Is_Null (A) and Is_Null (B) then\n         return True;\n      else  -- either one of the args is null or the sym_types don't match\n         return False;\n      end if;\n   end Nodes_Equal;\n\n\n   function New_Node_Mal_Type\n     (Data : Mal_Handle;\n      Next : Mal_Handle := Smart_Pointers.Null_Smart_Pointer)\n   return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Node_Mal_Type'\n          (Mal_Type with Data => Data, Next => Next));\n   end New_Node_Mal_Type;\n\n\n   overriding function Sym_Type (T : Node_Mal_Type) return Sym_Types is\n   begin\n      return Node;\n   end Sym_Type;\n\n\n   -- Get the first item in the list:\n   function Car (L : List_Mal_Type) return Mal_Handle is\n   begin\n      if Is_Null (L.The_List) then\n         return Smart_Pointers.Null_Smart_Pointer;\n      else\n         return Deref_Node (L.The_List).Data;\n      end if;\n   end Car;\n   \n   \n   -- Get the rest of the list (second item onwards)\n   function Cdr (L : List_Mal_Type) return Mal_Handle is\n      Res : Mal_Handle;\n      LP : List_Ptr;\n   begin\n\n      Res := New_List_Mal_Type (L.List_Type);\n\n      if Is_Null (L.The_List) or else\n         Is_Null (Deref_Node (L.The_List).Next) then\n         return Res;\n      else\n         LP := Deref_List (Res);\n         LP.The_List := Deref_Node (L.The_List).Next;\n         LP.Last_Elem := L.Last_Elem;\n         return Res;\n      end if;\n   end Cdr;\n\n\n   function Length (L : List_Mal_Type) return Natural is\n      Res : Natural;\n      NP : Node_Ptr;\n   begin\n      Res := 0;\n      NP := Deref_Node (L.The_List);\n      while NP /= null loop\n         Res := Res + 1;\n         NP := Deref_Node (NP.Next);\n      end loop;\n      return Res;\n   end Length;\n\n\n   function Is_Null (L : List_Mal_Type) return Boolean is\n      use Smart_Pointers;\n   begin\n      return Smart_Pointers.\"=\"(L.The_List, Null_Smart_Pointer);\n   end Is_Null;\n\n\n   function Null_List (L : List_Types) return List_Mal_Type is\n   begin\n      return (Mal_Type with List_Type => L,\n              The_List => Smart_Pointers.Null_Smart_Pointer,\n              Last_Elem => Smart_Pointers.Null_Smart_Pointer);\n   end Null_List;\n\n\n   function Map\n     (Func_Ptr : Func_Access;\n      L : List_Mal_Type)\n   return Mal_Handle is\n\n      Res, Old_List, First_New_Node, New_List : Mal_Handle;\n      LP : List_Ptr;\n\n   begin\n\n      Res := New_List_Mal_Type (List_Type => L.Get_List_Type);\n\n      Old_List := L.The_List;\n\n      if Is_Null (Old_List) then\n         return Res;\n      end if;\n\n      First_New_Node := New_Node_Mal_Type (Func_Ptr.all (Deref_Node (Old_List).Data));\n\n      New_List := First_New_Node;\n\n      Old_List := Deref_Node (Old_List).Next;\n\n      while not Is_Null (Old_List) loop\n\n         Deref_Node (New_List).Next :=\n           New_Node_Mal_Type (Func_Ptr.all (Deref_Node (Old_List).Data));\n\n         New_List := Deref_Node (New_List).Next;\n\n         Old_List := Deref_Node (Old_List).Next;\n\n      end loop;\n\n      LP := Deref_List (Res);\n      LP.The_List := First_New_Node;\n      LP.Last_Elem := New_List;\n\n      return Res;\n\n   end Map;\n\n\n   function Reduce\n     (Func_Ptr : Binary_Func_Access;\n      L : List_Mal_Type)\n   return Mal_Handle is\n\n      C_Node : Node_Ptr;\n      Res : Mal_Handle;\n      use Smart_Pointers;\n\n   begin\n\n      C_Node := Deref_Node (L.The_List);\n\n      if C_Node = null then\n         return Smart_Pointers.Null_Smart_Pointer;\n      end if;\n\n      Res := C_Node.Data;\n      while not Is_Null (C_Node.Next) loop\n         C_Node := Deref_Node (C_Node.Next);\n         Res := Func_Ptr (Res, C_Node.Data);\n      end loop;\n\n      return Res;\n\n   end Reduce;\n\n\n   overriding function To_Str \n     (T : Node_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      if Is_Null (T.Data) then\n         -- Left is null and by implication so is right.\n         return \"\";\n      elsif Is_Null (T.Next) then\n        -- Left is not null but right is.\n        return To_Str (Deref (T.Data).all, Print_Readably);\n      else\n        -- Left and right are both not null.\n        return To_Str (Deref (T.Data).all, Print_Readably) &\n               \" \" &\n               To_Str (Deref (T.Next).all, Print_Readably);\n      end if;\n   end To_Str;\n\n\n   function Cat_Str (T : Node_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      if Is_Null (T.Data) then\n         -- Left is null and by implication so is right.\n         return \"\";\n      elsif Is_Null (T.Next) then\n        -- Left is not null but right is.\n        return To_Str (Deref (T.Data).all, Print_Readably);\n\n      -- Left and right are both not null.\n      else\n        return To_Str (Deref (T.Data).all, Print_Readably) &\n               Cat_Str (Deref_Node (T.Next).all, Print_Readably);\n      end if;\n   end Cat_Str;\n\n\n   function Deref_Node (SP : Mal_Handle) return Node_Ptr is\n   begin\n      return Node_Ptr (Deref (SP));\n   end Deref_Node;\n\n\n   function \"=\" (A, B : List_Mal_Type) return Boolean is\n   begin\n      return Nodes_Equal (A.The_List, B.The_List);\n   end \"=\";\n\n   function New_List_Mal_Type\n     (The_List : List_Mal_Type)\n   return Mal_Handle is\n   begin\n     return Smart_Pointers.New_Ptr\n        (new List_Mal_Type'(Mal_Type with\n          List_Type => The_List.List_Type,\n          The_List => The_List.The_List,\n          Last_Elem => The_List.Last_Elem));\n   end New_List_Mal_Type;\n\n\n   function New_List_Mal_Type\n     (List_Type : List_Types;\n      The_First_Node : Mal_Handle := Smart_Pointers.Null_Smart_Pointer)\n   return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new List_Mal_Type'\n          (Mal_Type with\n            List_Type => List_Type,\n            The_List => The_First_Node,\n            Last_Elem => The_First_Node));\n   end New_List_Mal_Type;\n\n\n   function Make_New_List (Handle_List : Handle_Lists) return Mal_Handle is\n\n      List_SP : Mal_Handle;\n      List_P : List_Ptr;\n\n   begin\n      List_SP := New_List_Mal_Type (List_Type => List_List);\n      List_P := Deref_List (List_SP);\n      for I in Handle_List'Range loop\n         Append (List_P.all, Handle_List (I));\n      end loop;\n      return List_SP;\n   end Make_New_List;\n\n\n   overriding function Sym_Type (T : List_Mal_Type) return Sym_Types is\n   begin\n      return List;\n   end Sym_Type;\n\n\n   function Get_List_Type (L : List_Mal_Type) return List_Types is\n   begin\n      return L.List_Type;\n   end Get_List_Type;\n\n\n   function Prepend (Op : Mal_Handle; To_List : List_Mal_Type)\n   return Mal_Handle is\n   begin\n      return New_List_Mal_Type\n               (List_List,\n                New_Node_Mal_Type (Op, To_List.The_List));\n   end Prepend;\n\n\n   procedure Append (To_List : in out List_Mal_Type; Op : Mal_Handle) is\n   begin\n      if Is_Null (Op) then\n         return;  -- Say what\n      end if;\n\n      -- If the list is null just insert the new element\n      -- else use the last_elem pointer to insert it and then update it.\n      if Is_Null (To_List.The_List) then\n         To_List.The_List := New_Node_Mal_Type (Op);\n         To_List.Last_Elem := To_List.The_List;\n      else\n         Deref_Node (To_List.Last_Elem).Next := New_Node_Mal_Type (Op);\n         To_List.Last_Elem := Deref_Node (To_List.Last_Elem).Next;\n      end if;\n   end Append;\n\n\n   -- Duplicate copies the list (logically).  This is to allow concatenation,\n   -- The result is always a List_List.\n   function Duplicate (The_List : List_Mal_Type) return Mal_Handle is\n      Res, Old_List, First_New_Node, New_List : Mal_Handle;\n      LP : List_Ptr;\n   begin\n\n      Res := New_List_Mal_Type (List_List);\n\n      Old_List := The_List.The_List;\n\n      if Is_Null (Old_List) then\n         return Res;\n      end if;\n\n      First_New_Node := New_Node_Mal_Type (Deref_Node (Old_List).Data);\n      New_List := First_New_Node;\n      Old_List := Deref_Node (Old_List).Next;\n\n      while not Is_Null (Old_List) loop\n\n         Deref_Node (New_List).Next := New_Node_Mal_Type (Deref_Node (Old_List).Data);\n         New_List := Deref_Node (New_List).Next;\n         Old_List := Deref_Node (Old_List).Next;\n\n      end loop;\n\n      LP := Deref_List (Res);\n      LP.The_List := First_New_Node;\n      LP.Last_Elem := New_List;\n\n      return Res;\n\n   end Duplicate;\n\n\n   function Nth (L : List_Mal_Type; N : Natural) return Mal_Handle is\n\n      C : Natural;\n      Next : Mal_Handle;\n\n   begin\n\n      C := 0;\n\n      Next := L.The_List;\n\n      while not Is_Null (Next) loop\n\n         if C >= N then\n            return Deref_Node (Next).Data;\n         end if;\n\n         C := C + 1;\n\n         Next := Deref_Node (Next).Next;\n\n      end loop;\n\n      raise Runtime_Exception with \"Nth (list): Index out of range\";\n\n   end Nth;\n\n\n   function Concat (Rest_Handle : List_Mal_Type)\n   return Types.Mal_Handle is\n      Rest_List : Types.List_Mal_Type;\n      List : Types.List_Class_Ptr;\n      Res_List_Handle, Dup_List : Mal_Handle;\n      Last_Node_P : Mal_Handle := Smart_Pointers.Null_Smart_Pointer;\n   begin\n      Rest_List := Rest_Handle;\n\n      -- Set the result to the null list.\n      Res_List_Handle := New_List_Mal_Type (List_List);\n\n      while not Is_Null (Rest_List) loop\n\n         -- Find the next list in the list...\n         List := Deref_List_Class (Car (Rest_List));\n\n         -- Duplicate nodes to its contents. \n         Dup_List := Duplicate (List.all);\n\n         -- If we haven't inserted a list yet, then take the duplicated list whole.\n         if Is_Null (Last_Node_P) then\n            Res_List_Handle := Dup_List;\n         else\n            -- Note that the first inserted list may have been the null list\n            -- and so may the newly duplicated one...\n            Deref_Node (Last_Node_P).Next := Deref_List (Dup_List).The_List;\n            if Is_Null (Deref_List (Res_List_Handle).The_List) then\n               Deref_List (Res_list_Handle).The_List :=\n                 Deref_List (Dup_List).The_List;\n            end if;\n            if not Is_Null (Deref_List (Dup_List).Last_Elem) then\n               Deref_List (Res_List_Handle).Last_Elem :=\n                 Deref_List (Dup_List).Last_Elem;\n            end if;\n         end if;\n\n         Last_Node_P := Deref_List (Dup_List).Last_Elem;\n\n         Rest_List := Deref_List (Cdr (Rest_List)).all;\n\n      end loop;\n\n      return Res_List_Handle;\n\n   end Concat;\n\n\n   procedure Add_Defs (Defs : List_Mal_Type; Env : Envs.Env_Handle) is\n      D, L : List_Mal_Type;\n   begin\n      D := Defs;\n      while not Is_Null (D) loop\n         L := Deref_List (Cdr (D)).all;\n         Envs.Set\n           (Env,\n            Deref_Sym (Car (D)).Get_Sym,\n            Eval_Callback.Eval.all (Car (L), Env));\n         D := Deref_List (Cdr(L)).all;\n      end loop;\n   end Add_Defs;\n\n\n   function Deref_List (SP : Mal_Handle) return List_Ptr is\n   begin\n      return List_Ptr (Deref (SP));\n   end Deref_List;\n\n\n   function Deref_List_Class (SP : Mal_Handle) return List_Class_Ptr is\n   begin\n      return List_Class_Ptr (Deref (SP));\n   end Deref_List_Class;\n\n\n   overriding function To_Str \n     (T : List_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      if Is_Null (T.The_List) then\n         return Opening (T.List_Type) &\n                Closing (T.List_Type);\n      else\n         return Opening (T.List_Type) &\n                To_String (Deref (T.The_List).all, Print_Readably) &\n                Closing (T.List_Type);\n      end if;\n   end To_Str;\n\n\n   function Pr_Str (T : List_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      if Is_Null (T.The_List) then\n         return \"\";\n      else\n         return To_String (Deref_Node (T.The_List).all, Print_Readably);\n      end if;\n   end Pr_Str;\n\n\n   function Cat_Str (T : List_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n      if Is_Null (T.The_List) then\n         return \"\";\n      else\n         return Cat_Str (Deref_Node (T.The_List).all, Print_Readably);\n      end if;\n   end Cat_Str;\n\n\n   function Opening (LT : List_Types) return Character is\n      Res : Character;\n   begin\n      case LT is\n         when List_List =>\n            Res := '(';\n         when Vector_List =>\n            Res := '[';\n         when Hashed_List =>\n            Res := '{';\n      end case;\n      return Res;\n   end Opening;\n\n\n   function Closing (LT : List_Types) return Character is\n      Res : Character;\n   begin\n      case LT is\n         when List_List =>\n            Res := ')';\n         when Vector_List =>\n            Res := ']';\n         when Hashed_List =>\n            Res := '}';\n      end case;\n      return Res;\n   end Closing;\n\n\n   function New_Lambda_Mal_Type\n     (Params : Mal_Handle; Expr : Mal_Handle; Env : Envs.Env_Handle)\n   return Mal_Handle is\n   begin\n      return Smart_Pointers.New_Ptr\n        (new Lambda_Mal_Type'\n          (Mal_Type with\n             Params => Params,\n             Expr => Expr,\n             Env => Env,\n             Is_Macro => False));\n   end New_Lambda_Mal_Type;\n\n   overriding function Sym_Type (T : Lambda_Mal_Type) return Sym_Types is\n   begin\n      return Lambda;\n   end Sym_Type;\n\n   function Get_Env (L : Lambda_Mal_Type) return Envs.Env_Handle is\n   begin\n      return L.Env;\n   end Get_Env;\n\n   procedure Set_Env (L : in out Lambda_Mal_Type; Env : Envs.Env_Handle) is\n   begin\n      L.Env := Env;\n   end Set_Env;\n\n   function Get_Params (L : Lambda_Mal_Type) return Mal_Handle is\n   begin\n      if Deref (L.Params).Sym_Type = List and then\n         Deref_List (L.Params).Get_List_Type = Vector_List then\n         -- Its a vector and we need a list...\n         return Deref_List_Class (L.Params).Duplicate;\n      else\n         return L.Params;\n      end if;\n   end Get_Params;\n\n   function Get_Expr (L : Lambda_Mal_Type) return Mal_Handle is\n   begin\n      return L.Expr;\n   end Get_Expr;\n\n   function Get_Is_Macro (L : Lambda_Mal_Type) return Boolean is\n   begin\n      return L.Is_Macro;\n   end Get_Is_Macro;\n\n   procedure Set_Is_Macro (L : in out Lambda_Mal_Type; B : Boolean) is\n   begin\n      L.Is_Macro := B;\n   end Set_Is_Macro;\n\n\n   function Apply\n     (L : Lambda_Mal_Type;\n      Param_List : Mal_Handle)\n   return Mal_Handle is\n\n      E : Envs.Env_Handle;\n      Param_Names : List_Mal_Type;\n      Res : Mal_Handle;\n\n   begin\n\n      E := Envs.New_Env (L.Env);\n\n      Param_Names := Deref_List (L.Get_Params).all;\n\n      if Envs.Bind (E, Param_Names, Deref_List (Param_List).all) then\n\n         Res := Eval_Callback.Eval.all (L.Get_Expr, E); \n\n      else\n\n         raise Runtime_Exception with \"Bind failed in Apply\";\n\n      end if;\n\n      return Res;\n\n   end Apply;\n\n   overriding function To_Str \n     (T : Lambda_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String is\n   begin\n--      return \"(lambda \" & Ada.Strings.Unbounded.To_String (T.Rep) & \")\";\n      return \"#<function>\";\n   end To_Str;\n\n   function Deref_Lambda (SP : Mal_Handle) return Lambda_Ptr is\n   begin\n      return Lambda_Ptr (Deref (SP));\n   end Deref_Lambda;\n\n\n   function Arith_Op (A, B : Mal_Handle) return Mal_Handle is\n      use Types;\n      A_Sym_Type : Sym_Types;\n      B_Sym_Type : Sym_Types;\n   begin\n\n      if Is_Null (A) then\n        if Is_Null (B) then\n           -- both null, gotta be zero.\n           return New_Int_Mal_Type (0);\n        else  -- A is null but B is not.\n           return Arith_Op (New_Int_Mal_Type (0), B);\n        end if;\n      elsif Is_Null (B) then\n        -- A is not null but B is.\n         return Arith_Op (A, New_Int_Mal_Type (0));\n      end if;\n  \n      -- else both A and B and not null.:wq\n      A_Sym_Type := Deref (A).Sym_Type;\n      B_Sym_Type := Deref (B).Sym_Type;\n      if A_Sym_Type = Int and B_Sym_Type = Int then\n         return New_Int_Mal_Type\n           (Int_Op (Deref_Int (A).Get_Int_Val, Deref_Int (B).Get_Int_Val));\n      elsif A_Sym_Type = Int and B_Sym_Type = Floating then\n         return New_Float_Mal_Type\n           (Float_Op (Mal_Float (Deref_Int (A).Get_Int_Val),\n            Deref_Float (B).Get_Float_Val));\n      elsif A_Sym_Type = Floating and B_Sym_Type = Int then\n         return New_Float_Mal_Type\n           (Float_Op (Deref_Float (A).Get_Float_Val,\n            Mal_Float (Deref_Float (B).Get_Float_Val)));\n      elsif A_Sym_Type = Floating and B_Sym_Type = Floating then\n         return New_Float_Mal_Type\n           (Float_Op (Deref_Float (A).Get_Float_Val,\n            Deref_Float (B).Get_Float_Val));\n      else\n         if A_Sym_Type = Error then\n            return A;\n         elsif B_Sym_Type = Error then\n            return B;\n         else\n            return New_Error_Mal_Type (\"Invalid operands\");\n         end if;\n      end if;\n   end Arith_Op;\n\n\n   function Rel_Op (A, B : Mal_Handle) return Mal_Handle is\n      use Types;\n      A_Sym_Type : Sym_Types := Deref (A).Sym_Type;\n      B_Sym_Type : Sym_Types := Deref (B).Sym_Type;\n   begin\n      if A_Sym_Type = Int and B_Sym_Type = Int then\n         return New_Bool_Mal_Type\n           (Int_Rel_Op (Deref_Int (A).Get_Int_Val, Deref_Int (B).Get_Int_Val));\n      elsif A_Sym_Type = Int and B_Sym_Type = Floating then\n         return New_Bool_Mal_Type\n           (Float_Rel_Op (Mal_Float (Deref_Int (A).Get_Int_Val),\n            Deref_Float (B).Get_Float_Val));\n      elsif A_Sym_Type = Floating and B_Sym_Type = Int then\n         return New_Bool_Mal_Type\n           (Float_Rel_Op (Deref_Float (A).Get_Float_Val,\n            Mal_Float (Deref_Float (B).Get_Float_Val)));\n      else\n         return New_Bool_Mal_Type\n           (Float_Rel_Op (Deref_Float (A).Get_Float_Val,\n            Deref_Float (B).Get_Float_Val));\n      end if;\n   end Rel_Op;\n\n\nend Types;\n"
  },
  {
    "path": "impls/ada/types.ads",
    "content": "-- This started out as a simple public variant record.\n-- Then smart pointers were added.  They were part of the Mal_Type and\n-- were required to be public because of the dependencies and\n-- how the variant record was public.  Not very Ada-like.\n-- The third version bites the bullet and delares Mal_Type as tagged.\n-- Smart pointers are an OO version in a separate package.\n-- The Doubly_Linked_Lists have been replaced with a tree-like list instead...\n-- The tree-like list has been replaced with a singly linked list. Sigh.\n\n-- WARNING!  This code contains:\n-- Recursive data structures.\n-- Object-based smart pointers.\n-- Object-oriented code.\n-- And strong-typing!\n\n-- Chris M Moore 25/03/2015\n\nwith Ada.Strings.Unbounded;\nwith Smart_Pointers;\nwith Envs;\n\npackage Types is\n\n   -- Some simple types.  Not supposed to use the standard types directly.\n\n   subtype Mal_Float is Float;\n   subtype Mal_Integer is Integer;\n   subtype Mal_String is String;\n\n   -- Start off with the top-level abstract type.\n\n   subtype Mal_Handle is Smart_Pointers.Smart_Pointer;\n\n   function \"=\" (A, B : Mal_Handle) return Mal_Handle;\n\n   function \"=\" (A, B : Mal_Handle) return Boolean;\n\n   type Sym_Types is (Nil, Bool, Int, Floating, Str, Sym, Atom, Node,\n                      List, Func, Lambda, Error);\n\n   type Mal_Type is abstract new Smart_Pointers.Base_Class with private;\n\n   function Sym_Type (T : Mal_Type) return Sym_Types is abstract;\n\n   function Get_Meta (T : Mal_Type) return Mal_Handle;\n\n   procedure Set_Meta (T : in out Mal_Type'Class; SP : Mal_Handle);\n\n   function Copy (M : Mal_Handle) return Mal_Handle;\n\n   function To_String (T : Mal_Type'Class; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Mal_Ptr is access all Mal_Type'Class;\n\n   -- A helper function that just view converts the smart pointer to\n   -- a Mal_Type'Class pointer.\n   function Deref (S : Mal_Handle) return Mal_Ptr;\n\n   -- A helper function to detect null smart pointers.\n   function Is_Null (S : Mal_Handle) return Boolean;\n\n   -- Derived types.  All boilerplate from here.\n\n   type Nil_Mal_Type is new Mal_Type with private;\n\n   function New_Nil_Mal_Type return Mal_Handle;\n\n   overriding function Sym_Type (T : Nil_Mal_Type) return Sym_Types;\n\n\n   type Int_Mal_Type is new Mal_Type with private;\n\n   function New_Int_Mal_Type (Int : Mal_Integer) return Mal_Handle;\n\n   overriding function Sym_Type (T : Int_Mal_Type) return Sym_Types;\n\n   function Get_Int_Val (T : Int_Mal_Type) return Mal_Integer;\n\n   type Int_Ptr is access all Int_Mal_Type;\n\n   function Deref_Int (SP : Mal_Handle) return Int_Ptr;\n\n\n   type Float_Mal_Type is new Mal_Type with private;\n\n   function New_Float_Mal_Type (Floating : Mal_Float) return Mal_Handle;\n\n   overriding function Sym_Type (T : Float_Mal_Type) return Sym_Types;\n\n   function Get_Float_Val (T : Float_Mal_Type) return Mal_Float;\n\n   type Float_Ptr is access all Float_Mal_Type;\n\n   function Deref_Float (SP : Mal_Handle) return Float_Ptr;\n\n\n   type Bool_Mal_Type is new Mal_Type with private;\n\n   function New_Bool_Mal_Type (Bool : Boolean) return Mal_Handle;\n\n   overriding function Sym_Type (T : Bool_Mal_Type) return Sym_Types;\n\n   function Get_Bool (T : Bool_Mal_Type) return Boolean;\n\n   type Bool_Ptr is access all Bool_Mal_Type;\n\n   function Deref_Bool (SP : Mal_Handle) return Bool_Ptr;\n\n\n   type String_Mal_Type is new Mal_Type with private;\n\n   function New_String_Mal_Type (Str : Mal_String) return Mal_Handle;\n\n   overriding function Sym_Type (T : String_Mal_Type) return Sym_Types;\n\n   function Get_String (T : String_Mal_Type) return Mal_String;\n\n   type String_Ptr is access all String_Mal_Type;\n\n   function Deref_String (SP : Mal_Handle) return String_Ptr;\n\n\n   type Symbol_Mal_Type is new Mal_Type with private;\n\n   function New_Symbol_Mal_Type (Str : Mal_String) return Mal_Handle;\n\n   overriding function Sym_Type (T : Symbol_Mal_Type) return Sym_Types;\n\n   function Get_Sym (T : Symbol_Mal_Type) return Mal_String;\n\n   type Sym_Ptr is access all Symbol_Mal_Type;\n\n   function Deref_Sym (S : Mal_Handle) return Sym_Ptr;\n\n\n\n   type Atom_Mal_Type is new Mal_Type with private;\n\n   function New_Atom_Mal_Type (MH : Mal_Handle) return Mal_Handle;\n\n   overriding function Sym_Type (T : Atom_Mal_Type) return Sym_Types;\n\n   function Get_Atom (T : Atom_Mal_Type) return Mal_Handle;\n\n   procedure Set_Atom (T : in out Atom_Mal_Type; New_Val : Mal_Handle);\n\n   type Atom_Ptr is access all Atom_Mal_Type;\n\n   function Deref_Atom (S : Mal_Handle) return Atom_Ptr;\n\n\n\n   type Error_Mal_Type is new Mal_Type with private;\n\n   function New_Error_Mal_Type (Str : Mal_String) return Mal_Handle;\n\n   overriding function Sym_Type (T : Error_Mal_Type) return Sym_Types;\n\n\n   -- Lists.\n\n   type List_Types is (List_List, Vector_List, Hashed_List);\n   function Opening (LT : List_Types) return Character;\n   function Closing (LT : List_Types) return Character;\n\n   type List_Mal_Type is new Mal_Type with private;\n\n   function \"=\" (A, B : List_Mal_Type) return Boolean;\n\n   function New_List_Mal_Type\n     (List_Type : List_Types;\n      The_First_Node : Mal_Handle := Smart_Pointers.Null_Smart_Pointer)\n   return Mal_Handle;\n\n   function New_List_Mal_Type\n     (The_List : List_Mal_Type)\n   return Mal_Handle;\n\n   type Handle_Lists is array (Positive range <>) of Mal_Handle;\n\n   -- Make a new list of the form: (Handle_List(1), Handle_List(2)...)\n   function Make_New_List (Handle_List : Handle_Lists) return Mal_Handle;\n\n   overriding function Sym_Type (T : List_Mal_Type) return Sym_Types;\n\n   function Get_List_Type (L : List_Mal_Type) return List_Types;\n\n   function Prepend (Op : Mal_Handle; To_List : List_Mal_Type)\n   return Mal_Handle;\n\n   procedure Append (To_List : in out List_Mal_Type; Op : Mal_Handle);\n\n   function Length (L : List_Mal_Type) return Natural;\n\n   function Nth (L : List_Mal_Type; N : Natural) return Mal_Handle;\n\n   procedure Add_Defs (Defs : List_Mal_Type; Env : Envs.Env_Handle);\n\n   -- Get the first item in the list:\n   function Car (L : List_Mal_Type) return Mal_Handle;\n\n   -- Get the rest of the list (second item onwards)\n   function Cdr (L : List_Mal_Type) return Mal_Handle;\n\n   type Func_Access is access\n     function (Elem : Mal_Handle)\n     return Mal_Handle;\n\n   function Map\n     (Func_Ptr : Func_Access;\n      L : List_Mal_Type)\n   return Mal_Handle;\n\n   type Binary_Func_Access is access\n     function (A, B : Mal_Handle)\n     return Mal_Handle;\n\n   function Reduce\n     (Func_Ptr : Binary_Func_Access;\n      L : List_Mal_Type)\n   return Mal_Handle;\n\n   function Is_Null (L : List_Mal_Type) return Boolean;\n\n   function Null_List (L : List_Types) return List_Mal_Type;\n\n   function Pr_Str (T : List_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   function Cat_Str (T : List_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   function Concat (Rest_Handle : List_Mal_Type)\n   return Types.Mal_Handle;  -- a new list\n\n   -- Duplicate copies the list (logically).  This is to allow concatenation,\n   -- The result is always a List_List.\n   function Duplicate (The_List : List_Mal_Type) return Mal_Handle;\n\n   type List_Ptr is access all List_Mal_Type;\n\n   function Deref_List (SP : Mal_Handle) return List_Ptr;\n\n   type List_Class_Ptr is access all List_Mal_Type'Class;\n\n   function Deref_List_Class (SP : Mal_Handle) return List_Class_Ptr;\n\n\n   type Func_Mal_Type is new Mal_Type with private;\n\n   type Builtin_Func is access\n      function (MH : Mal_Handle) return Mal_Handle;\n\n   function New_Func_Mal_Type (Str : Mal_String; F : Builtin_Func)\n   return Mal_Handle;\n\n   overriding function Sym_Type (T : Func_Mal_Type) return Sym_Types;\n\n   function Get_Func_Name (T : Func_Mal_Type) return Mal_String;\n\n   function Call_Func\n     (FMT : Func_Mal_Type; Rest_List : Mal_Handle)\n   return Mal_Handle;\n\n   type Func_Ptr is access all Func_Mal_Type;\n\n   function Deref_Func (S : Mal_Handle) return Func_Ptr;\n\n\n\n   type Lambda_Mal_Type is new Mal_Type with private;\n\n   function New_Lambda_Mal_Type\n     (Params : Mal_Handle; Expr : Mal_Handle; Env : Envs.Env_Handle)\n   return Mal_Handle;\n\n   overriding function Sym_Type (T : Lambda_Mal_Type) return Sym_Types;\n\n   function Get_Env (L : Lambda_Mal_Type) return Envs.Env_Handle;\n\n   procedure Set_Env (L : in out Lambda_Mal_Type; Env : Envs.Env_Handle);\n\n   function Get_Params (L : Lambda_Mal_Type) return Mal_Handle;\n\n   function Get_Expr (L : Lambda_Mal_Type) return Mal_Handle;\n\n   function Get_Is_Macro (L : Lambda_Mal_Type) return Boolean;\n\n   procedure Set_Is_Macro (L : in out Lambda_Mal_Type; B : Boolean);\n\n   function Apply\n     (L : Lambda_Mal_Type;\n      Param_List : Mal_Handle) return Mal_Handle;\n\n   type Lambda_Ptr is access all Lambda_Mal_Type;\n\n   function Deref_Lambda (SP : Mal_Handle) return Lambda_Ptr;\n\n   generic\n      with function Int_Op (A, B : Mal_Integer) return Mal_Integer;\n      with function Float_Op (A, B : Mal_Float) return Mal_Float;\n   function Arith_Op (A, B : Mal_Handle) return Mal_Handle;\n\n   generic\n      with function Int_Rel_Op (A, B : Mal_Integer) return Boolean;\n      with function Float_Rel_Op (A, B : Mal_Float) return Boolean;\n   function Rel_Op (A, B : Mal_Handle) return Mal_Handle;\n\n   Runtime_Exception : exception;\n\n   Mal_Exception : exception;  -- So tempting to call this Mal_Function but...\n\n   Mal_Exception_Value : Mal_Handle;  -- Used by mal's throw command\n\nprivate\n\n   type Mal_Type is abstract new Smart_Pointers.Base_Class with record\n      Meta : Mal_Handle;\n   end record;\n\n   -- Not allowed to be abstract and private.  RM 3.9.3(10)\n   -- So if you call this it'll just raise an exception.\n   function To_Str (T : Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Nil_Mal_Type is new Mal_Type with null record;\n\n   overriding function To_Str (T : Nil_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Int_Mal_Type is new Mal_Type with record\n      Int_Val : Mal_Integer;\n   end record;\n\n   overriding function To_Str (T : Int_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Float_Mal_Type is new Mal_Type with record\n      Float_Val : Mal_Float;\n   end record;\n\n   overriding function To_Str (T : Float_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Bool_Mal_Type is new Mal_Type with record\n      Bool_Val : Boolean;\n   end record;\n\n   overriding function To_Str (T : Bool_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type String_Mal_Type is new Mal_Type with record\n      The_String : Ada.Strings.Unbounded.Unbounded_String;\n   end record;\n\n   overriding function To_Str (T : String_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Symbol_Mal_Type is new Mal_Type with record\n      The_Symbol : Ada.Strings.Unbounded.Unbounded_String;\n   end record;\n\n   overriding function To_Str (T : Symbol_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Atom_Mal_Type is new Mal_Type with record\n      The_Atom : Mal_Handle;\n   end record;\n\n   overriding function To_Str (T : Atom_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Func_Mal_Type is new Mal_Type with record\n      Func_Name : Ada.Strings.Unbounded.Unbounded_String;\n      Func_P : Builtin_Func;\n   end record;\n\n   overriding function To_Str (T : Func_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Error_Mal_Type is new Mal_Type with record\n      Error_Msg : Ada.Strings.Unbounded.Unbounded_String;\n   end record;\n\n   overriding function To_Str (T : Error_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n\n   -- Nodes have to be a differnt type from a List;\n   -- otherwise how do you represent a list within a list?\n   type Node_Mal_Type is new Mal_Type with record\n      Data : Mal_Handle;\n      Next : Mal_Handle;  -- This is always a Node_Mal_Type handle\n   end record;\n\n   function New_Node_Mal_Type\n     (Data : Mal_Handle;\n      Next : Mal_Handle := Smart_Pointers.Null_Smart_Pointer)\n   return Mal_Handle;\n\n   overriding function Sym_Type (T : Node_Mal_Type) return Sym_Types;\n\n   overriding function To_Str \n     (T : Node_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Node_Ptr is access all Node_Mal_Type;\n\n   function Deref_Node (SP : Mal_Handle) return Node_Ptr;\n\n\n   type List_Mal_Type is new Mal_Type with record\n      List_Type : List_Types;\n      The_List : Mal_Handle;\n      Last_Elem : Mal_Handle;\n   end record;\n\n   overriding function To_Str \n     (T : List_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n   type Container_Cursor is tagged record\n      The_Node : Node_Ptr := null;\n   end record;\n\n   type Lambda_Mal_Type is new Mal_Type with record\n      Params, Expr : Mal_Handle;\n      Env : Envs.Env_Handle;\n      Is_Macro : Boolean;\n   end record;\n\n   overriding function To_Str \n     (T : Lambda_Mal_Type; Print_Readably : Boolean := True)\n   return Mal_String;\n\n\nend Types;\n"
  },
  {
    "path": "impls/ada.2/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# GNU Ada compiler\nRUN apt-get -y install gnat libreadline-dev\n"
  },
  {
    "path": "impls/ada.2/Makefile",
    "content": "ifdef DEBUG\n  ADAFLAGS := -Wall -Wextra -gnatw.eH.Y -gnatySdouxy -gnatVa -g -gnataEfoqQ \\\n    -fstack-check -pg\n  LDFLAGS  := -pg\nelse\n  # -O3 is not recommended as the default by the GCC documentation,\n  # and -O2 seems to produce slightly better performances.\n  ADAFLAGS := -O2 -gnatnp\nendif\n\n# Compiler arguments.\nCARGS = $(ADAFLAGS)\n# Linker arguments.\nLARGS = $(LDFLAGS) -lreadline\n\nstep0  := step0_repl\nstep13 := step1_read_print \\\n          step2_eval \\\n          step3_env\nstep49 := step4_if_fn_do \\\n          step5_tco \\\n          step6_file \\\n          step7_quote \\\n          step8_macros \\\n          step9_try\nstepa  := stepA_mal\nsteps  := $(step0) $(step13) $(step49) $(stepa)\n\n.PHONY: all clean\nall: $(steps)\nclean:\n\t$(RM) *~ *.adt *.ali *.o b~*.ad[bs] gmon.out $(steps)\n\n# Tell Make how to detect out-of-date executables, and let gnatmake do\n# the rest when it must be executed.\nsources = $(foreach unit,$1,$(unit).adb $(unit).ads)\nTYPES := $(call sources,\\\n  envs \\\n  err \\\n  garbage_collected \\\n  printer \\\n  reader \\\n  readline \\\n  types \\\n  types-atoms \\\n  types-builtins \\\n  types-fns \\\n  types-maps \\\n  types-sequences \\\n  types-strings \\\n)\nCORE := $(call sources,\\\n  core \\\n)\n\n$(step0) :      %:      %.adb\n$(step13):      %:      %.adb $(TYPES)\n$(step49):      %:      %.adb $(TYPES) $(CORE)\n$(stepa) : stepA%: stepa%.adb $(TYPES) $(CORE)\n$(steps) :\n\tgnatmake $< -o $@ -cargs $(CARGS) -largs $(LARGS)\n\n.PHONY: steps.diff\nsteps.diff:\n\tdiff -u step0_*.adb step1_*.adb || true\n\tdiff -u step1_*.adb step2_*.adb || true\n\tdiff -u step2_*.adb step3_*.adb || true\n\tdiff -u step3_*.adb step4_*.adb || true\n\tdiff -u step4_*.adb step5_*.adb || true\n\tdiff -u step5_*.adb step6_*.adb || true\n\tdiff -u step6_*.adb step7_*.adb || true\n\tdiff -u step7_*.adb step8_*.adb || true\n\tdiff -u step8_*.adb step9_*.adb || true\n\tdiff -u step9_*.adb stepa_*.adb || true\n"
  },
  {
    "path": "impls/ada.2/README",
    "content": "Comparison with the first Ada implementation.\n--\n\nThe first implementation was deliberately compatible with all Ada\ncompilers, while this one illustrates various Ada 2012 features:\nassertions, preconditions, invariants, initial assignment for limited\ntypes, limited imports...\n\nThe variant MAL type is implemented with a discriminant instead of\nobject-style dispatching.  This allows more static and dynamic checks,\nbut also two crucial performance improvements:\n* Nil, boolean, integers and pointers to built-in functions are passed\n  by value without dynamic allocation.\n* Lists are implemented as C-style arrays, and can often be\n  allocated on the stack.\n\nAnother difference is that a minimal form of garbage collecting is\nimplemented, removing objects not referenced from the main\nenvironment. Reference counting does not seem efficient even for symbols,\nand never deallocates cyclic structures. The implementation collects\ngarbage after each Read-Eval-Print cycle. It would be much more\ndifficult to collect garbage inside scripts. If this is ever done, it\nwould be better to reimplement load-file in Ada and run a cycle after\neach root evaluation.\nIt is possible to execute the recursion marking references in parallel\nwith the recursion printing the result, which does not modify anything\nand ignores the reference marking. This works but is less performant\nthan sequential execution even with Linux threads and a single task\ninitialized at startup.\nEach pointer type goes on using its own memory pool, enabling better\nperformance when the designated subtype has a fixed size.\n\nThe eventual performances compete with C-style languages, allthough\nall user input is checked (implicit language-defined checks like array\nbounds and discriminant consistency are only enabled during tests).\n\nDebugging\n--\n\nUncaught exceptions are reported with an execution trace (excluding\nTCO cycles).  This has become possible in step9, but has been\nbackported to former steps as this is really handy for debugging.\n\nSome environment variables increase verbosity.\n# dbgread= ./stepAmal     trace reader recursion\n# dbgeval= ./stepAmal     trace eval recursion (including TCO)\n"
  },
  {
    "path": "impls/ada.2/core.adb",
    "content": "with Ada.Calendar;\nwith Ada.Characters.Latin_1;\nwith Ada.Strings.Unbounded;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Err;\nwith Printer;\nwith Reader;\nwith Types.Atoms;\nwith Types.Builtins;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\npackage body Core is\n\n   package ASU renames Ada.Strings.Unbounded;\n   use all type Types.Kind_Type;\n\n   --  Used by time_ms.\n   Start_Time : constant Ada.Calendar.Time := Ada.Calendar.Clock;\n\n   generic\n      Kind : in Types.Kind_Type;\n   function Generic_Kind_Test (Args : in Types.T_Array) return Types.T;\n   function Generic_Kind_Test (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return (Kind_Boolean, Args (Args'First).Kind = Kind);\n   end Generic_Kind_Test;\n\n   generic\n      with function Ada_Operator (Left, Right : in Integer) return Integer;\n   function Generic_Mal_Operator (Args : in Types.T_Array) return Types.T;\n   function Generic_Mal_Operator (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 2\n                   and then Args (Args'First).Kind = Kind_Number\n                   and then Args (Args'Last).Kind = Kind_Number,\n                 \"expected two numbers\");\n      return (Kind_Number, Ada_Operator (Args (Args'First).Number,\n                                         Args (Args'Last).Number));\n   end Generic_Mal_Operator;\n\n   generic\n      with function Ada_Operator (Left, Right : in Integer) return Boolean;\n   function Generic_Comparison (Args : in Types.T_Array) return Types.T;\n   function Generic_Comparison (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 2\n                   and then Args (Args'First).Kind = Kind_Number\n                   and then Args (Args'Last).Kind = Kind_Number,\n                 \"expected two numbers\");\n      return (Kind_Boolean, Ada_Operator (Args (Args'First).Number,\n                                          Args (Args'Last).Number));\n   end Generic_Comparison;\n\n   function Addition      is new Generic_Mal_Operator (\"+\");\n   function Apply         (Args : in Types.T_Array) return Types.T;\n   function Division      is new Generic_Mal_Operator (\"/\");\n   function Equals        (Args : in Types.T_Array) return Types.T;\n   function Greater_Equal is new Generic_Comparison (\">=\");\n   function Greater_Than  is new Generic_Comparison (\">\");\n   function Is_Atom       is new Generic_Kind_Test (Kind_Atom);\n   function Is_False      (Args : in Types.T_Array) return Types.T;\n   function Is_Function   (Args : in Types.T_Array) return Types.T;\n   function Is_Keyword    is new Generic_Kind_Test (Kind_Keyword);\n   function Is_List       is new Generic_Kind_Test (Kind_List);\n   function Is_Macro      is new Generic_Kind_Test (Kind_Macro);\n   function Is_Map        is new Generic_Kind_Test (Kind_Map);\n   function Is_Nil        is new Generic_Kind_Test (Kind_Nil);\n   function Is_Number     is new Generic_Kind_Test (Kind_Number);\n   function Is_Sequential (Args : in Types.T_Array) return Types.T;\n   function Is_String     is new Generic_Kind_Test (Kind_String);\n   function Is_Symbol     is new Generic_Kind_Test (Kind_Symbol);\n   function Is_True       (Args : in Types.T_Array) return Types.T;\n   function Is_Vector     is new Generic_Kind_Test (Kind_Vector);\n   function Keyword       (Args : in Types.T_Array) return Types.T;\n   function Less_Equal    is new Generic_Comparison (\"<=\");\n   function Less_Than     is new Generic_Comparison (\"<\");\n   function Meta          (Args : in Types.T_Array) return Types.T;\n   function Pr_Str        (Args : in Types.T_Array) return Types.T;\n   function Println       (Args : in Types.T_Array) return Types.T;\n   function Prn           (Args : in Types.T_Array) return Types.T;\n   function Product       is new Generic_Mal_Operator (\"*\");\n   function Read_String   (Args : in Types.T_Array) return Types.T;\n   function Readline      (Args : in Types.T_Array) return Types.T;\n   function Seq           (Args : in Types.T_Array) return Types.T;\n   function Slurp         (Args : in Types.T_Array) return Types.T;\n   function Str           (Args : in Types.T_Array) return Types.T;\n   function Subtraction   is new Generic_Mal_Operator (\"-\");\n   function Symbol        (Args : in Types.T_Array) return Types.T;\n   function Time_Ms       (Args : in Types.T_Array) return Types.T;\n   function With_Meta     (Args : in Types.T_Array) return Types.T;\n\n   ----------------------------------------------------------------------\n\n   function Apply (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (2 <= Args'Length\n                   and then Args (Args'Last).Kind in Types.Kind_Sequence,\n                 \"expected a function, optional arguments then a sequence\");\n      declare\n         use type Types.T_Array;\n         F : Types.T renames Args (Args'First);\n         A : constant Types.T_Array\n           := Args (Args'First + 1 .. Args'Last - 1)\n           & Args (Args'Last).Sequence.all.Data;\n      begin\n         case F.Kind is\n            when Kind_Builtin =>\n               return F.Builtin.all (A);\n            when Kind_Builtin_With_Meta =>\n               return F.Builtin_With_Meta.all.Builtin.all (A);\n            when Kind_Fn | Kind_Macro =>\n               return F.Fn.all.Apply (A);\n            when others =>\n               Err.Raise_With (\"parameter 1 must be a function or macro\");\n         end case;\n      end;\n   end Apply;\n\n   function Equals (Args : in Types.T_Array) return Types.T is\n      use type Types.T;\n   begin\n      Err.Check (Args'Length = 2, \"expected 2 parameters\");\n      return (Kind_Boolean, Args (Args'First) = Args (Args'Last));\n   end Equals;\n\n   function Is_False (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return (Kind_Boolean, Args (Args'First).Kind = Kind_Boolean\n                and then not Args (Args'First).Ada_Boolean);\n   end Is_False;\n\n   function Is_Function (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return (Kind_Boolean, Args (Args'First).Kind in Types.Kind_Function);\n   end Is_Function;\n\n   function Is_Sequential (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return (Kind_Boolean, Args (Args'First).Kind in Types.Kind_Sequence);\n   end Is_Sequential;\n\n   function Is_True (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return (Kind_Boolean, Args (Args'First).Kind = Kind_Boolean\n                and then Args (Args'First).Ada_Boolean);\n   end Is_True;\n\n   function Keyword (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1\n                   and then Args (Args'First).Kind in Types.Kind_Key,\n                 \"expected a keyword or a string\");\n      return (Kind_Keyword, Args (Args'First).Str);\n   end Keyword;\n\n   function Meta (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      declare\n         A1 : Types.T renames Args (Args'First);\n      begin\n         case A1.Kind is\n            when Types.Kind_Sequence =>\n               return A1.Sequence.all.Meta;\n            when Kind_Map =>\n               return A1.Map.all.Meta;\n            when Kind_Fn =>\n               return A1.Fn.all.Meta;\n            when Kind_Builtin_With_Meta =>\n               return A1.Builtin_With_Meta.all.Meta;\n            when Kind_Builtin =>\n               return Types.Nil;\n            when others =>\n               Err.Raise_With (\"expected a function, map or sequence\");\n         end case;\n      end;\n   end Meta;\n\n   procedure NS_Add_To_Repl (Repl : in Envs.Ptr) is\n      procedure P (S : in String;\n                   B : in Types.Builtin_Ptr) with Inline;\n      procedure P (S : in String;\n                   B : in Types.Builtin_Ptr)\n      is\n      begin\n         Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (S)),\n                       (Kind_Builtin, B));\n      end P;\n   begin\n      P (\"+\",           Addition'Access);\n      P (\"apply\",       Apply'Access);\n      P (\"assoc\",       Types.Maps.Assoc'Access);\n      P (\"atom\",        Types.Atoms.Atom'Access);\n      P (\"concat\",      Types.Sequences.Concat'Access);\n      P (\"conj\",        Types.Sequences.Conj'Access);\n      P (\"cons\",        Types.Sequences.Cons'Access);\n      P (\"contains?\",   Types.Maps.Contains'Access);\n      P (\"count\",       Types.Sequences.Count'Access);\n      P (\"deref\",       Types.Atoms.Deref'Access);\n      P (\"dissoc\",      Types.Maps.Dissoc'Access);\n      P (\"/\",           Division'Access);\n      P (\"=\",           Equals'Access);\n      P (\"first\",       Types.Sequences.First'Access);\n      P (\"get\",         Types.Maps.Get'Access);\n      P (\">=\",          Greater_Equal'Access);\n      P (\">\",           Greater_Than'Access);\n      P (\"hash-map\",    Types.Maps.Hash_Map'Access);\n      P (\"atom?\",       Is_Atom'Access);\n      P (\"empty?\",      Types.Sequences.Is_Empty'Access);\n      P (\"false?\",      Is_False'Access);\n      P (\"fn?\",         Is_Function'Access);\n      P (\"keyword?\",    Is_Keyword'Access);\n      P (\"list?\",       Is_List'Access);\n      P (\"macro?\",      Is_Macro'Access);\n      P (\"map?\",        Is_Map'Access);\n      P (\"nil?\",        Is_Nil'Access);\n      P (\"number?\",     Is_Number'Access);\n      P (\"sequential?\", Is_Sequential'Access);\n      P (\"string?\",     Is_String'Access);\n      P (\"symbol?\",     Is_Symbol'Access);\n      P (\"true?\",       Is_True'Access);\n      P (\"vector?\",     Is_Vector'Access);\n      P (\"keys\",        Types.Maps.Keys'Access);\n      P (\"keyword\",     Keyword'Access);\n      P (\"<=\",          Less_Equal'Access);\n      P (\"<\",           Less_Than'Access);\n      P (\"list\",        Types.Sequences.List'Access);\n      P (\"map\",         Types.Sequences.Map'Access);\n      P (\"meta\",        Meta'Access);\n      P (\"nth\",         Types.Sequences.Nth'Access);\n      P (\"pr-str\",      Pr_Str'Access);\n      P (\"println\",     Println'Access);\n      P (\"prn\",         Prn'Access);\n      P (\"*\",           Product'Access);\n      P (\"read-string\", Read_String'Access);\n      P (\"readline\",    Readline'Access);\n      P (\"reset!\",      Types.Atoms.Reset'Access);\n      P (\"rest\",        Types.Sequences.Rest'Access);\n      P (\"seq\",         Seq'Access);\n      P (\"slurp\",       Slurp'Access);\n      P (\"str\",         Str'Access);\n      P (\"-\",           Subtraction'Access);\n      P (\"swap!\",       Types.Atoms.Swap'Access);\n      P (\"symbol\",      Symbol'Access);\n      P (\"throw\",       Err.Throw'Access);\n      P (\"time-ms\",     Time_Ms'Access);\n      P (\"vals\",        Types.Maps.Vals'Access);\n      P (\"vec\",         Types.Sequences.Vec'Access);\n      P (\"vector\",      Types.Sequences.Vector'Access);\n      P (\"with-meta\",   With_Meta'Access);\n   end NS_Add_To_Repl;\n\n   function Pr_Str (Args : in Types.T_Array) return Types.T is\n      R       : ASU.Unbounded_String;\n      Started : Boolean := False;\n   begin\n      for A of Args loop\n         if Started then\n            ASU.Append (R, ' ');\n         else\n            Started := True;\n         end if;\n         Printer.Pr_Str (R, A);\n      end loop;\n      return (Kind_String, Types.Strings.Alloc (ASU.To_String (R)));\n   end Pr_Str;\n\n   function Println (Args : in Types.T_Array) return Types.T is\n      Started : Boolean := False;\n      Buffer  : ASU.Unbounded_String;\n   begin\n      for A of Args loop\n         if Started then\n            ASU.Append (Buffer, ' ');\n         else\n            Started := True;\n         end if;\n         Printer.Pr_Str (Buffer, A, Readably => False);\n      end loop;\n      Ada.Text_IO.Unbounded_IO.Put_Line (Buffer);\n      return Types.Nil;\n   end Println;\n\n   function Prn (Args : in Types.T_Array) return Types.T is\n      --  Calling Pr_Str would create an intermediate copy.\n      Buffer  : ASU.Unbounded_String;\n      Started : Boolean := False;\n   begin\n      for A of Args loop\n         if Started then\n            ASU.Append (Buffer, ' ');\n         else\n            Started := True;\n         end if;\n         Printer.Pr_Str (Buffer, A);\n      end loop;\n      Ada.Text_IO.Unbounded_IO.Put_Line (Buffer);\n      return Types.Nil;\n   end Prn;\n\n   function Readline (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_String,\n                 \"expected a string\");\n      Ada.Text_IO.Put (Args (Args'First).Str.all.To_String);\n      if Ada.Text_IO.End_Of_File then\n         return Types.Nil;\n      else\n         return (Kind_String, Types.Strings.Alloc (Ada.Text_IO.Get_Line));\n      end if;\n   end Readline;\n\n   function Read_String (Args : in Types.T_Array) return Types.T is\n      Result : Types.T;\n      procedure Process (Element : in String);\n      procedure Process (Element : in String) is\n         R : constant Types.T_Array := Reader.Read_Str (Element);\n      begin\n         Err.Check (R'Length = 1, \"parameter must contain 1 expression\");\n         Result := R (R'First);\n      end Process;\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_String,\n                 \"expected a string\");\n      Args (Args'First).Str.all.Query_Element (Process'Access);\n      return Result;\n   end Read_String;\n\n   function Seq (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      case Args (Args'First).Kind is\n         when Kind_Nil =>\n            return Types.Nil;\n         when Kind_String =>\n            declare\n               Result : Types.T;\n               procedure Process (S : in String);\n               procedure Process (S : in String) is\n               begin\n                  if S'Length = 0 then\n                     Result := Types.Nil;\n                  else\n                     Result := (Kind_List,\n                                Types.Sequences.Constructor (S'Length));\n                     for I in S'Range loop\n                        Result.Sequence.all.Data (S'First - 1 + I)\n                          := (Kind_String, Types.Strings.Alloc (S (I .. I)));\n                     end loop;\n                  end if;\n               end Process;\n            begin\n               Args (Args'First).Str.all.Query_Element (Process'Access);\n               return Result;\n            end;\n         when Types.Kind_Sequence =>\n            if Args (Args'First).Sequence.all.Length = 0 then\n               return Types.Nil;\n            else\n               return (Kind_List, Args (Args'First).Sequence);\n            end if;\n         when others =>\n            Err.Raise_With (\"expected nil, a sequence or a string\");\n      end case;\n   end Seq;\n\n   function Slurp (Args : in Types.T_Array) return Types.T is\n      use Ada.Text_IO;\n      File   : File_Type;\n      Buffer : ASU.Unbounded_String;\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_String,\n                 \"expected a string\");\n      Open (File, In_File, Args (Args'First).Str.all.To_String);\n      while not End_Of_File (File) loop\n         ASU.Append (Buffer, Get_Line (File));\n         ASU.Append (Buffer, Ada.Characters.Latin_1.LF);\n      end loop;\n      Close (File);\n      return (Kind_String, Types.Strings.Alloc (ASU.To_String (Buffer)));\n   exception\n      --  Catch I/O errors, but not Err.Error...\n      when E : Status_Error | Name_Error | Use_Error | Mode_Error =>\n         if Is_Open (File) then\n            Close (File);\n         end if;\n         Err.Raise_In_Mal (E);\n   end Slurp;\n\n   function Str (Args : in Types.T_Array) return Types.T is\n      R : ASU.Unbounded_String;\n   begin\n      for Arg of Args loop\n         Printer.Pr_Str (R, Arg, Readably => False);\n      end loop;\n      return (Kind_String, Types.Strings.Alloc (ASU.To_String (R)));\n   end Str;\n\n   function Symbol (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_String,\n                 \"expected a string\");\n      return (Kind_Symbol, Args (Args'First).Str);\n   end Symbol;\n\n   function Time_Ms (Args : in Types.T_Array) return Types.T is\n      use type Ada.Calendar.Time;\n   begin\n      Err.Check (Args'Length = 0, \"expected no parameter\");\n      return (Kind_Number,\n              Integer (1000.0 * (Ada.Calendar.Clock - Start_Time)));\n   end Time_Ms;\n\n   function With_Meta (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 2, \"expected 2 parameters\");\n      declare\n         A1 : Types.T renames Args (Args'First);\n         A2 : Types.T renames Args (Args'Last);\n      begin\n         case A1.Kind is\n            when Kind_Builtin_With_Meta =>\n               return A1.Builtin_With_Meta.all.With_Meta (A2);\n            when Kind_Builtin =>\n               return Types.Builtins.With_Meta (A1.Builtin, A2);\n            when Kind_List =>\n               return R : constant Types.T\n                  := Types.Sequences.List (A1.Sequence.all.Data)\n               do\n                  R.Sequence.all.Meta := A2;\n               end return;\n            when Kind_Vector =>\n               return R : constant Types.T\n                  := Types.Sequences.Vector (A1.Sequence.all.Data)\n               do\n                  R.Sequence.all.Meta := A2;\n               end return;\n            when Kind_Map =>\n               return A1.Map.all.With_Meta (A2);\n            when Kind_Fn =>\n               return (Kind_Fn, Types.Fns.New_Function\n                         (A1.Fn.all.Params, A1.Fn.all.Ast, A1.Fn.all.Env, A2));\n            when others =>\n               Err.Raise_With\n                 (\"parameter 1 must be a function, map or sequence\");\n         end case;\n      end;\n   end With_Meta;\n\nend Core;\n"
  },
  {
    "path": "impls/ada.2/core.ads",
    "content": "with Envs;\n\npackage Core is\n\n   procedure NS_Add_To_Repl (Repl : in Envs.Ptr);\n   --  Add built-in functions.\n\nend Core;\n"
  },
  {
    "path": "impls/ada.2/envs.adb",
    "content": "with Ada.Text_IO.Unbounded_IO;\n\nwith Err;\nwith Printer;\nwith Types.Sequences;\n\npackage body Envs is\n\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n\n   ----------------------------------------------------------------------\n\n   procedure Dump_Stack (Env : in Instance) is\n      use Ada.Text_IO;\n   begin\n      Put (\"environment:\");\n      for P in Env.Data.Iterate loop\n         --  Do not print builtins for repl.\n         if HM.Element (P).Kind /= Kind_Builtin or Env.Outer /= null then\n            Put (\"   \");\n            HM.Key (P).all.Query_Element (Put'Access);\n            Put (':');\n            Unbounded_IO.Put (Printer.Pr_Str (HM.Element (P)));\n            New_Line;\n         end if;\n      end loop;\n      if Env.Outer /= null then\n         Put (\"outer is \");\n         Env.Outer.all.Dump_Stack;\n      end if;\n   end Dump_Stack;\n\n   function Get (Env : in Instance;\n                 Key : in Types.String_Ptr) return Types.T\n   is\n      Position : HM.Cursor := Env.Data.Find (Key);\n      Ref      : Link;\n   begin\n      if not HM.Has_Element (Position) then\n         Ref := Env.Outer;\n         loop\n            if Ref = null then\n               --  Not using Err.Check, which would compute the\n               --  argument even if the assertion holds...\n               Err.Raise_With (\"'\" & Key.To_String & \"' not found\");\n            end if;\n            Position := Ref.all.Data.Find (Key);\n            exit when HM.Has_Element (Position);\n            Ref := Ref.all.Outer;\n         end loop;\n      end if;\n      return HM.Element (Position);\n   end Get;\n\n   function Get_Or_Nil (Env : Instance;\n                        Key : Types.String_Ptr) return Types.T is\n      Position : HM.Cursor := Env.Data.Find (Key);\n      Ref      : Link;\n   begin\n      if not HM.Has_Element (Position) then\n         Ref := Env.Outer;\n         loop\n            if Ref = null then\n               return Types.Nil;\n            end if;\n            Position := Ref.all.Data.Find (Key);\n            exit when HM.Has_Element (Position);\n            Ref := Ref.all.Outer;\n         end loop;\n      end if;\n      return HM.Element (Position);\n   end Get_Or_Nil;\n\n   procedure Keep_References (Object : in out Instance) is\n   begin\n      for Position in Object.Data.Iterate loop\n         HM.Key (Position).all.Keep;\n         Types.Keep (HM.Element (Position));\n      end loop;\n      if Object.Outer /= null then\n         Object.Outer.all.Keep;\n      end if;\n   end Keep_References;\n\n   function New_Env (Outer : in Link := null) return Ptr is\n      Ref : constant Ptr := new Instance;\n   begin\n      Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n      Ref.all.Outer := Outer;\n      return Ref;\n   end New_Env;\n\n   procedure Set_Binds (Env   : in out Instance;\n                        Binds : in     Types.T_Array;\n                        Exprs : in     Types.T_Array)\n   is\n   begin\n      if 2 <= Binds'Length and then Binds (Binds'Last - 1).Str.all = \"&\" then\n         Err.Check (Binds'Length - 2 <= Exprs'Length,\n                    \"not enough actual parameters for vararg function\");\n         for I in 0 .. Binds'Length - 3 loop\n            Env.Data.Include (Key      => Binds (Binds'First + I).Str,\n                              New_Item => Exprs (Exprs'First + I));\n         end loop;\n         Env.Data.Include (Key      => Binds (Binds'Last).Str,\n                           New_Item => Types.Sequences.List\n                       (Exprs (Exprs'First + Binds'Length - 2 .. Exprs'Last)));\n      else\n         Err.Check (Binds'Length = Exprs'Length,\n                    \"wrong parameter count for (not vararg) function\");\n         for I in 0 .. Binds'Length - 1 loop\n            Env.Data.Include (Key      => Binds (Binds'First + I).Str,\n                              New_Item => Exprs (Exprs'First + I));\n         end loop;\n      end if;\n   end Set_Binds;\n\n   procedure Set (Env      : in out Instance;\n                  Key      : in     Types.T;\n                  New_Item : in     Types.T)\n   is\n   begin\n      Err.Check (Key.Kind = Kind_Symbol, \"environment keys must be symbols\");\n      Env.Data.Include (Key.Str, New_Item);\n   end Set;\n\nend Envs;\n"
  },
  {
    "path": "impls/ada.2/envs.ads",
    "content": "private with Ada.Containers.Hashed_Maps;\n\nwith Garbage_Collected;\nwith Types.Strings;\n\npackage Envs is\n\n   --  This package should be named Env, but Ada does not allow formal\n   --  parameters to be named like a package dependency, and it seems\n   --  that readability inside Eval is more important.\n\n   type Instance (<>) is abstract new Garbage_Collected.Instance with private;\n   type Link is access Instance;\n   subtype Ptr is not null Link;\n\n   function New_Env (Outer : in Link := null) return Ptr with Inline;\n\n   procedure Set_Binds (Env   : in out Instance;\n                        Binds : in     Types.T_Array;\n                        Exprs : in     Types.T_Array);\n   --  Equivalent to successive calls to Set, except that if Binds\n   --  ends with \"&\" followed by a symbol, the trailing symbol\n   --  receives all remaining values as a list.\n\n   function Get (Env : in Instance;\n                 Key : in Types.String_Ptr) return Types.T;\n\n   function Get_Or_Nil (Env : Instance;\n                        Key : Types.String_Ptr) return Types.T;\n\n   procedure Set (Env      : in out Instance;\n                  Key      : in     Types.T;\n                  New_Item : in     Types.T) with Inline;\n   --  Raises an exception if Key is not a symbol.\n\n   --  Debug.\n   procedure Dump_Stack (Env : in Instance);\n\nprivate\n\n   package HM is new Ada.Containers.Hashed_Maps\n     (Key_Type        => Types.String_Ptr,\n      Element_Type    => Types.T,\n      Hash            => Types.Strings.Hash,\n      Equivalent_Keys => Types.Strings.Same_Contents,\n      \"=\"             => Types.\"=\");\n\n   --  It may be tempting to subclass Types.Map, but this would not\n   --  simplify the code much. And adding metadata to a structure that\n   --  is allocated very often has a cost.\n\n   type Instance is new Garbage_Collected.Instance with record\n      Outer : Link;\n      Data  : HM.Map;\n   end record;\n   overriding procedure Keep_References (Object : in out Instance) with Inline;\n\nend Envs;\n"
  },
  {
    "path": "impls/ada.2/err.adb",
    "content": "with Ada.Characters.Latin_1;\n\nwith Printer;\nwith Types.Strings;\n\npackage body Err is\n\n   use Ada.Strings.Unbounded;\n\n   ----------------------------------------------------------------------\n\n   procedure Add_Trace_Line (Action : in String;\n                             Ast    : in Types.T)\n   is\n   begin\n      Append (Trace, \"  in \");\n      Append (Trace, Action);\n      Append (Trace, \": \");\n      Printer.Pr_Str (Trace, Ast);\n      Append (Trace, Ada.Characters.Latin_1.LF);\n   end Add_Trace_Line;\n\n   procedure Check (Condition : in Boolean;\n                    Message   : in String)\n   is\n   begin\n      if not Condition then\n         Raise_With (Message);\n      end if;\n   end Check;\n\n   procedure Raise_In_Mal (E : in Ada.Exceptions.Exception_Occurrence) is\n      Message : String renames Ada.Exceptions.Exception_Information (E);\n      procedure Process (S : in String);\n      procedure Process (S : in String) is\n      begin\n         Append (Trace, S);\n      end Process;\n   begin\n      Data := (Types.Kind_String, Types.Strings.Alloc (Message));\n      Set_Unbounded_String (Trace, \"Uncaught exception: \");\n      Data.Str.all.Query_Element (Process'Access);\n      raise Error;\n   end Raise_In_Mal;\n\n   procedure Raise_With (Message : in String) is\n   begin\n      Data := (Types.Kind_String, Types.Strings.Alloc (Message));\n      Set_Unbounded_String (Trace, \"Uncaught exception: \");\n      Append (Trace, Message);\n      Append (Trace, Ada.Characters.Latin_1.LF);\n      raise Error;\n   end Raise_With;\n\n   function Throw (Args : in Types.T_Array) return Types.T is\n   begin\n      Check (Args'Length = 1, \"expected 1 parameter\");\n      Data := Args (Args'First);\n      Set_Unbounded_String (Trace, \"Uncaught exception: \");\n      Printer.Pr_Str (Trace, Data);\n      Append (Trace, Ada.Characters.Latin_1.LF);\n      --  A raise value is equivalent to a raise statement, but\n      --  silents a compiler warning.\n      return raise Error;\n   end Throw;\n\nend Err;\n"
  },
  {
    "path": "impls/ada.2/err.ads",
    "content": "with Ada.Exceptions;\nwith Ada.Strings.Unbounded;\n\nwith Types;\n--  We declare a variable of type Types.T.\npragma Elaborate (Types);\n\npackage Err is\n\n   --  Error handling.\n\n   --  Built-in function.\n   function Throw (Args : in Types.T_Array) return Types.T;\n\n   --  Ada exceptions can only carry an immutable String in each\n   --  occurence, so we require a global variable to store the last\n   --  exception as a Mal object anyway, and may as well use it for\n   --  simple string messages.\n\n   Error : exception;\n   Data  : Types.T;\n   Trace : Ada.Strings.Unbounded.Unbounded_String;\n\n   --  Convenient shortcuts.\n\n   procedure Raise_With (Message : in String) with No_Return;\n   --  Similar to a \"raise with Message\" Ada statement.\n   --  Store the message into Data,\n   --  store the message and \"Uncaught exception: \" into Trace,\n   --  then raise Error.\n\n   procedure Raise_In_Mal (E : in Ada.Exceptions.Exception_Occurrence)\n     with No_Return;\n   --  Raise_With (Ada.Exceptions.Exception_Information (E))\n\n   procedure Add_Trace_Line (Action : in String;\n                             Ast    : in Types.T);\n   --  Appends a line like \"Action: Ast\" to Trace.\n\n   procedure Check (Condition : in Boolean;\n                    Message   : in String) with Inline;\n   --  Raise_With if Condition fails.\n\n   --  It is probably more efficient to construct a boolean and call\n   --  this procedure once, as \"inline\" is only a recommendation.\n\n   --  Beware of the classical performance issue that the Message is\n   --  formatted even if the Condition does not hold.\n\nend Err;\n"
  },
  {
    "path": "impls/ada.2/garbage_collected.adb",
    "content": "with Ada.Unchecked_Deallocation;\n\npackage body Garbage_Collected is\n\n   procedure Free is new Ada.Unchecked_Deallocation (Class, Link);\n\n   Top : Link := null;\n\n   ----------------------------------------------------------------------\n\n   procedure Clean is\n      Current  : Link := Top;\n      Previous : Link;\n   begin\n      while Current /= null and then not Current.all.Kept loop\n         Previous := Current;\n         Current := Current.all.Next;\n         Free (Previous);\n      end loop;\n      Top := Current;\n      while Current /= null loop\n         if Current.all.Kept then\n            Current.all.Kept := False;\n            Previous := Current;\n         else\n            Previous.all.Next := Current.all.Next;\n            Free (Current);\n         end if;\n         Current := Previous.all.Next;\n      end loop;\n   end Clean;\n\n   procedure Keep (Object : in out Class) is\n   begin\n      if not Object.Kept then\n         Object.Kept := True;\n         Object.Keep_References;        --  dispatching\n      end if;\n   end Keep;\n\n   procedure Check_Allocations is\n   begin\n      pragma Assert (Top = null);\n   end Check_Allocations;\n\n   procedure Register (Ref : in Pointer) is\n   begin\n      pragma Assert (Ref.all.Kept = False);\n      pragma Assert (Ref.all.Next = null);\n      Ref.all.Next := Top;\n      Top := Ref;\n   end Register;\n\nend Garbage_Collected;\n"
  },
  {
    "path": "impls/ada.2/garbage_collected.ads",
    "content": "package Garbage_Collected is\n\n   --  A generic would not be convenient for lists. We want the\n   --  extended type to be able to have a discriminant.\n\n   --  However, we keep the dispatching in a single enumeration for\n   --  efficiency and clarity of the source.\n\n   type Instance is abstract tagged limited private;\n   subtype Class is Instance'Class;\n   type Link is access all Class;\n   subtype Pointer is not null Link;\n\n   procedure Keep_References (Object : in out Instance) is null with Inline;\n   --  A dispatching call in Keep allows subclasses to override this\n   --  in order to Keep each of the internal reference they maintain.\n\n   --  The following methods have no reason to be overridden.\n\n   procedure Keep (Object : in out Class) with Inline;\n   --  Mark this object so that it is not deleted by next clean,\n   --  then make a dispatching call to Keep_References.\n   --  Does nothing if it has already been called for this object\n   --  since startup or last Clean.\n\n   procedure Register (Ref : in Pointer) with Inline;\n   --  Each subclass defines its own allocation pool, but every call\n   --  to new must be followed by a call to Register.\n\n   procedure Clean;\n   --  For each object for which Keep has not been called since\n   --  startup or last clean, make a dispatching call to Finalize,\n   --  then deallocate the memory for the object.\n\n   --  Debug.\n   procedure Check_Allocations;\n   --  Does nothing if assertions are disabled.\n\nprivate\n\n   type Instance is abstract tagged limited record\n      Kept : Boolean := False;\n      Next : Link    := null;\n   end record;\n\nend Garbage_Collected;\n"
  },
  {
    "path": "impls/ada.2/printer.adb",
    "content": "with Ada.Characters.Latin_1;\n\nwith Types.Atoms;\nwith Types.Fns;\nwith Types.Maps;\npragma Warnings (Off, \"unit \"\"Types.Sequences\"\" is not referenced\");\nwith Types.Sequences;\npragma Warnings (On, \"unit \"\"Types.Sequences\"\" is not referenced\");\n\npackage body Printer is\n\n   use Ada.Strings.Unbounded;\n   use all type Types.Kind_Type;\n\n   procedure Pr_Str (Buffer   : in out Unbounded_String;\n                     Ast      : in     Types.T;\n                     Readably : in     Boolean          := True)\n   is\n\n      procedure Print_Form (Form_Ast : in Types.T);\n      --  The recursive function traversing Ast for Pr_Str.\n      --  Form_Ast is the current node.\n\n      --  Helpers for Print_Form.\n      procedure Print_Number   (Number : in Integer);\n      procedure Print_List     (List   : in Types.T_Array);\n      procedure Print_Map      (Map    : in Types.Maps.Instance);\n      procedure Print_Readably (S      : in String);\n      procedure Print_String   (S      : in String);\n\n      ----------------------------------------------------------------------\n\n      procedure Print_Form (Form_Ast : in Types.T) is\n      begin\n         case Form_Ast.Kind is\n            when Kind_Nil =>\n               Append (Buffer, \"nil\");\n            when Kind_Boolean =>\n               if Form_Ast.Ada_Boolean then\n                  Append (Buffer, \"true\");\n               else\n                  Append (Buffer, \"false\");\n               end if;\n            when Kind_Symbol =>\n               Form_Ast.Str.all.Query_Element (Print_String'Access);\n            when Kind_Number =>\n               Print_Number (Form_Ast.Number);\n            when Kind_Keyword =>\n               Append (Buffer, ':');\n               Form_Ast.Str.all.Query_Element (Print_String'Access);\n            when Kind_String =>\n               if Readably then\n                  Append (Buffer, '\"');\n                  Form_Ast.Str.all.Query_Element (Print_Readably'Access);\n                  Append (Buffer, '\"');\n               else\n                  Form_Ast.Str.all.Query_Element (Print_String'Access);\n               end if;\n            when Kind_List =>\n               Append (Buffer, '(');\n               Print_List (Form_Ast.Sequence.all.Data);\n               Append (Buffer, ')');\n            when Kind_Vector =>\n               Append (Buffer, '[');\n               Print_List (Form_Ast.Sequence.all.Data);\n               Append (Buffer, ']');\n            when Kind_Map =>\n               Append (Buffer, '{');\n               Print_Map (Form_Ast.Map.all);\n               Append (Buffer, '}');\n            when Kind_Builtin | Kind_Builtin_With_Meta =>\n               Append (Buffer, \"#<built-in>\");\n            when Kind_Fn =>\n               Append (Buffer, \"#<function (\");\n               Print_List (Form_Ast.Fn.all.Params.all.Data);\n               Append (Buffer, \") -> \");\n               Print_Form (Form_Ast.Fn.all.Ast);\n               Append (Buffer, '>');\n            when Kind_Macro =>\n               Append (Buffer, \"#<macro (\");\n               Print_List (Form_Ast.Fn.all.Params.all.Data);\n               Append (Buffer, \") -> \");\n               Print_Form (Form_Ast.Fn.all.Ast);\n               Append (Buffer, '>');\n            when Kind_Atom =>\n               Append (Buffer, \"(atom \");\n               Print_Form (Form_Ast.Atom.all.Deref);\n               Append (Buffer, ')');\n         end case;\n      end Print_Form;\n\n      procedure Print_List (List : in Types.T_Array) is\n      begin\n         if 0 < List'Length then\n            Print_Form (List (List'First));\n            for I in List'First + 1 .. List'Last loop\n               Append (Buffer, ' ');\n               Print_Form (List (I));\n            end loop;\n         end if;\n      end Print_List;\n\n      procedure Print_Map (Map : in Types.Maps.Instance) is\n         use all type Types.Maps.Cursor;\n         Position : Types.Maps.Cursor := Map.First;\n      begin\n         if Has_Element (Position) then\n            loop\n               Print_Form (Key (Position));\n               Append (Buffer, ' ');\n               Print_Form (Element (Position));\n               Next (Position);\n               exit when not Has_Element (Position);\n               Append (Buffer, ' ');\n            end loop;\n         end if;\n      end Print_Map;\n\n      procedure Print_Number (Number : in Integer) is\n         Image : constant String := Integer'Image (Number);\n         First : Positive := Image'First;\n      begin\n         if Image (First) = ' ' then\n            First := First + 1;\n         end if;\n         Append (Buffer, Image (First .. Image'Last));\n      end Print_Number;\n\n      procedure Print_Readably (S : in String) is\n      begin\n         for C of S loop\n               case C is\n                  when '\"' | '\\' =>\n                     Append (Buffer, '\\');\n                     Append (Buffer, C);\n                  when Ada.Characters.Latin_1.LF =>\n                     Append (Buffer, \"\\n\");\n                  when others =>\n                     Append (Buffer, C);\n               end case;\n         end loop;\n      end Print_Readably;\n\n      procedure Print_String (S : in String) is\n      begin\n         Append (Buffer, S);\n      end Print_String;\n\n      ----------------------------------------------------------------------\n\n   begin                                --  Pr_Str\n      Print_Form (Ast);\n   end Pr_Str;\n\n   function Pr_Str (Ast      : in Types.T;\n                    Readably : in Boolean := True) return Unbounded_String\n   is\n   begin\n      return Buffer : Unbounded_String do\n         Pr_Str (Buffer, Ast, Readably);\n      end return;\n   end Pr_Str;\n\nend Printer;\n"
  },
  {
    "path": "impls/ada.2/printer.ads",
    "content": "with Ada.Strings.Unbounded;\n\nwith Types;\n\npackage Printer is\n\n   procedure Pr_Str\n     (Buffer   : in out Ada.Strings.Unbounded.Unbounded_String;\n      Ast      : in     Types.T;\n      Readably : in     Boolean                                := True);\n   --  Append the text to Buffer.\n\n   function Pr_Str (Ast      : in Types.T;\n                    Readably : in Boolean := True)\n                   return Ada.Strings.Unbounded.Unbounded_String;\n   --  Return a freshly created unbounded string.\n   --  Convenient, but inefficient.\n\nend Printer;\n"
  },
  {
    "path": "impls/ada.2/reader.adb",
    "content": "with Ada.Characters.Handling;\nwith Ada.Characters.Latin_1;\nwith Ada.Environment_Variables;\nwith Ada.Strings.Maps.Constants;\nwith Ada.Strings.Unbounded;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Err;\nwith Printer;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\npackage body Reader is\n\n   Debug : constant Boolean := Ada.Environment_Variables.Exists (\"dbgread\");\n\n   use all type Types.Kind_Type;\n   use all type Ada.Strings.Maps.Character_Set;\n\n   Ignored_Set : constant Ada.Strings.Maps.Character_Set\n     := Ada.Strings.Maps.Constants.Control_Set\n     or To_Set (\" ,;\");\n\n   Symbol_Set : constant Ada.Strings.Maps.Character_Set\n     := not (Ignored_Set or To_Set (\"\"\"'()@[]^`{}~\"));\n\n   function Read_Str (Source : in String) return Types.T_Array is\n\n      I : Positive := Source'First;\n      --  Index in Source of the currently read character.\n\n      --  Big arrays on the stack are faster than repeated dynamic\n      --  reallocations. This single buffer is used by all Read_List\n      --  recursive invocations, and by Read_Str.\n      Buffer : Types.T_Array (1 .. Source'Length);\n      B_Last : Natural := Buffer'First - 1;\n      --  Index in Buffer of the currently written MAL expression.\n\n      function Read_Form return Types.T;\n      --  The recursive part of Read_Str.\n\n      --  Helpers for Read_Form:\n\n      procedure Skip_Ignored;\n      --  Check if the current character is ignorable or a comment.\n      --  Increment I until it exceeds Source'Last or designates\n      --  an interesting character.\n\n      procedure Skip_Digits with Inline;\n      --  Increment I at least once, until I exceeds Source'Last or\n      --  designates something else than a decimal digit.\n\n      procedure Skip_Symbol with Inline;\n      --  Check if the current character is allowed in a symbol name.\n      --  Increment I until it exceeds Source'Last or stops\n      --  designating an allowed character.\n\n      --  Read_Atom has been merged into the same case/switch\n      --  statement, for clarity and efficiency.\n\n      function Read_List (Ending : in Character) return Natural;\n      --  Returns the index of the last elements in Buffer.\n      --  The elements have been stored in Buffer (B_Last .. result).\n\n      function Read_Quote (Symbol : in String) return Types.T;\n\n      function Read_String return Types.T;\n\n      function Read_With_Meta return Types.T;\n\n      ----------------------------------------------------------------------\n\n      function Read_List (Ending : in Character) return Natural is\n         Opening : constant Character := Source (I);\n         Old     : constant Natural := B_Last;\n         Result  : Positive;\n      begin\n         I := I + 1;         --  Skip (, [ or {.\n         loop\n            Skip_Ignored;\n            Err.Check (I <= Source'Last, \"unbalanced '\" & Opening & \"'\");\n            exit when Source (I) = Ending;\n            B_Last := B_Last + 1;\n            Buffer (B_Last) := Read_Form;\n         end loop;\n         I := I + 1;         --  Skip ), ] or }.\n         Result := B_Last;\n         B_Last := Old;\n         return Result;\n      end Read_List;\n\n      function Read_Quote (Symbol : in String) return Types.T is\n         R : constant Types.Sequence_Ptr := Types.Sequences.Constructor (2);\n      begin\n         I := I + 1;             --  Skip the initial ' or similar.\n         R.all.Data (1) := (Kind_Symbol, Types.Strings.Alloc (Symbol));\n         Skip_Ignored;\n         Err.Check (I <= Source'Last, \"Incomplete '\" & Symbol & \"'\");\n         R.all.Data (2) := Read_Form;\n         return (Kind_List, R);\n      end Read_Quote;\n\n      function Read_Form return Types.T is\n         --  After I has been increased, current token is be\n         --  Source (F .. I - 1).\n         F : Positive;\n         R : Types.T;                   --  The result of this function.\n      begin\n         case Source (I) is\n            when ')' | ']' | '}' =>\n               Err.Raise_With (\"unbalanced '\" & Source (I) & \"'\");\n            when '\"' =>\n               R := Read_String;\n            when ':' =>\n               I := I + 1;\n               F := I;\n               Skip_Symbol;\n               R := (Kind_Keyword, Types.Strings.Alloc (Source (F .. I - 1)));\n            when '-' =>\n               F := I;\n               Skip_Digits;\n               if F + 1 < I then\n                  R := (Kind_Number, Integer'Value (Source (F .. I - 1)));\n               else\n                  Skip_Symbol;\n                  R := (Kind_Symbol,\n                        Types.Strings.Alloc (Source (F .. I - 1)));\n               end if;\n            when '~' =>\n               if I < Source'Last and then Source (I + 1) = '@' then\n                  I := I + 1;\n                  R := Read_Quote (\"splice-unquote\");\n               else\n                  R := Read_Quote (\"unquote\");\n               end if;\n            when '0' .. '9' =>\n               F := I;\n               Skip_Digits;\n               R := (Kind_Number, Integer'Value (Source (F .. I - 1)));\n            when ''' =>\n               R := Read_Quote (\"quote\");\n            when '`' =>\n               R := Read_Quote (\"quasiquote\");\n            when '@' =>\n               R := Read_Quote (\"deref\");\n            when '^' =>\n               R := Read_With_Meta;\n            when '(' =>\n               R := Types.Sequences.List\n                 (Buffer (B_Last + 1 .. Read_List (')')));\n            when '[' =>\n               R := Types.Sequences.Vector\n                 (Buffer (B_Last + 1 .. Read_List (']')));\n            when '{' =>\n               R := Types.Maps.Hash_Map\n                 (Buffer (B_Last + 1 .. Read_List ('}')));\n            when others =>\n               F := I;\n               Skip_Symbol;\n               if Source (F .. I - 1) = \"false\" then\n                  R := (Kind_Boolean, False);\n               elsif Source (F .. I - 1) = \"nil\" then\n                  R := Types.Nil;\n               elsif Source (F .. I - 1) = \"true\" then\n                  R := (Kind_Boolean, True);\n               else\n                  R := (Kind_Symbol,\n                        Types.Strings.Alloc (Source (F .. I - 1)));\n               end if;\n         end case;\n         if Debug then\n            Ada.Text_IO.Put (\"reader: \");\n            Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (R));\n         end if;\n         return R;\n      end Read_Form;\n\n      function Read_String return Types.T is\n         use Ada.Strings.Unbounded;\n         Result : Unbounded_String;\n      begin\n         loop\n            I := I + 1;\n            Err.Check (I <= Source'Last, \"unbalanced '\"\"'\");\n            case Source (I) is\n               when '\"' =>\n                  exit;\n               when '\\' =>\n                  I := I + 1;\n                  Err.Check (I <= Source'Last, \"unbalanced '\"\"'\");\n                  case Source (I) is\n                     when '\\' | '\"' =>\n                        Append (Result, Source (I));\n                     when 'n' =>\n                        Append (Result, Ada.Characters.Latin_1.LF);\n                     when others =>\n                        Append (Result, Source (I - 1 .. I));\n                  end case;\n               when others =>\n                  Append (Result, Source (I));\n            end case;\n         end loop;\n         I := I + 1;                    --  Skip closing double quote.\n         return (Kind_String, Types.Strings.Alloc (To_String (Result)));\n      end Read_String;\n\n      function Read_With_Meta return Types.T is\n         List : constant Types.Sequence_Ptr := Types.Sequences.Constructor (3);\n      begin\n         I := I + 1;                    --  Skip the initial ^.\n         List.all.Data (1) := (Kind_Symbol, Types.Strings.Alloc (\"with-meta\"));\n         for I in reverse 2 .. 3 loop\n            Skip_Ignored;\n            Err.Check (I <= Source'Last, \"Incomplete 'with-meta'\");\n            List.all.Data (I) := Read_Form;\n         end loop;\n         return (Kind_List, List);\n      end Read_With_Meta;\n\n      procedure Skip_Digits is\n         use Ada.Characters.Handling;\n      begin\n         loop\n            I := I + 1;\n            exit when Source'Last < I;\n            exit when not Is_Digit (Source (I));\n         end loop;\n      end Skip_Digits;\n\n      procedure Skip_Ignored is\n         use Ada.Characters.Handling;\n      begin\n         Ignored : while I <= Source'Last\n                         and then Is_In (Source (I), Ignored_Set)\n         loop\n            if Source (I) = ';' then\n               Comment : loop\n                  I := I + 1;\n                  exit Ignored when Source'Last < I;\n                  exit Comment when Is_Line_Terminator (Source (I));\n               end loop Comment;\n            end if;\n            I := I + 1;\n         end loop Ignored;\n      end Skip_Ignored;\n\n      procedure Skip_Symbol is\n      begin\n         while I <= Source'Last and then Is_In (Source (I), Symbol_Set) loop\n            I := I + 1;\n         end loop;\n      end Skip_Symbol;\n\n      ----------------------------------------------------------------------\n\n   begin                                --  Read_Str\n      loop\n         Skip_Ignored;\n         exit when Source'Last < I;\n         B_Last := B_Last + 1;\n         Buffer (B_Last) := Read_Form;\n      end loop;\n      return Buffer (Buffer'First .. B_Last);\n   end Read_Str;\n\nend Reader;\n"
  },
  {
    "path": "impls/ada.2/reader.ads",
    "content": "with Types;\n\npackage Reader is\n\n   function Read_Str (Source : in String) return Types.T_Array;\n   --  The language does not explicitly define what happens when the\n   --  input string contains more than one expression.\n   --  This implementation returns all of them.\n\nend Reader;\n"
  },
  {
    "path": "impls/ada.2/readline.adb",
    "content": "with Interfaces.C.Strings;\n\npackage body Readline is\n\n   function Input (Prompt : in String) return String is\n\n      use Interfaces.C;\n      use Interfaces.C.Strings;\n\n      function C_Readline (Prompt : in char_array) return chars_ptr\n        with Import, Convention => C, External_Name => \"readline\";\n\n      procedure Add_History (Line : in chars_ptr)\n        with Import, Convention => C, External_Name => \"add_history\";\n\n      procedure Free (Line : in chars_ptr)\n        with Import, Convention => C, External_Name => \"free\";\n\n      C_Line : constant chars_ptr := C_Readline (To_C (Prompt));\n   begin\n      if C_Line = Null_Ptr then\n         raise End_Of_File;\n      end if;\n      return Ada_Line : constant String := Value (C_Line) do\n         if Ada_Line /= \"\" then\n            Add_History (C_Line);\n         end if;\n         Free (C_Line);\n      end return;\n   end Input;\n\nend Readline;\n"
  },
  {
    "path": "impls/ada.2/readline.ads",
    "content": "package Readline is\n\n   function Input (Prompt : in String) return String;\n\n   End_Of_File : exception;\n\nend Readline;\n"
  },
  {
    "path": "impls/ada.2/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/ada.2/step0_repl.adb",
    "content": "with Ada.Text_IO;\n\nwith Readline;\n\nprocedure Step0_Repl is\n\n   function Read return String with Inline;\n\n   function Eval (Ast : in String) return String;\n\n   procedure Print (Ast : in String) with Inline;\n\n   procedure Rep with Inline;\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast : in String) return String\n   is (Ast);\n\n   procedure Print (Ast : in String) is\n   begin\n      Ada.Text_IO.Put_Line (Ast);\n   end Print;\n\n   function Read return String is (Readline.Input (\"user> \"));\n\n   procedure Rep is\n   begin\n      Print (Eval (Read));\n   end Rep;\n\n   ----------------------------------------------------------------------\n\nbegin\n   loop\n      begin\n         Rep;\n      exception\n         when Readline.End_Of_File =>\n            exit;\n      end;\n      --  Other exceptions are really unexpected.\n   end loop;\n   Ada.Text_IO.New_Line;\nend Step0_Repl;\n"
  },
  {
    "path": "impls/ada.2/step1_read_print.adb",
    "content": "with Ada.Text_IO.Unbounded_IO;\n\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types;\n\nprocedure Step1_Read_Print is\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast : in Types.T) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep with Inline;\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast : in Types.T) return Types.T is (Ast);\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\nbegin\n   loop\n      begin\n         Rep;\n      exception\n         when Readline.End_Of_File =>\n            exit;\n         when Err.Error =>\n            Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n      end;\n      --  Other exceptions are really unexpected.\n\n      --  Collect garbage.\n      Err.Data := Types.Nil;\n      --  No data survives at this stage, Repl only contains static\n      --  pointers to built-in functions.\n      Garbage_Collected.Clean;\n   end loop;\n   Ada.Text_IO.New_Line;\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step1_Read_Print;\n"
  },
  {
    "path": "impls/ada.2/step2_eval.adb",
    "content": "with Ada.Containers.Indefinite_Hashed_Maps;\nwith Ada.Strings.Hash;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step2_Eval is\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n\n   package Envs is new Ada.Containers.Indefinite_Hashed_Maps\n     (Key_Type        => String,\n      Element_Type    => Types.Builtin_Ptr,\n      Hash            => Ada.Strings.Hash,\n      Equivalent_Keys => \"=\",\n      \"=\"             => Types.\"=\");\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast : in Types.T;\n                  Env : in Envs.Map) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Map) with Inline;\n\n   generic\n      with function Ada_Operator (Left, Right : in Integer) return Integer;\n   function Generic_Mal_Operator (Args : in Types.T_Array) return Types.T;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Map) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Map) return Types.T;\n   --  Helpers for the Eval function.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast : in Types.T;\n                  Env : in Envs.Map) return Types.T\n   is\n      First          : Types.T;\n   begin\n      --  Ada.Text_IO.Put (\"EVAL: \");\n      --  Print (Ast);\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         declare\n            S : constant String      := Ast.Str.all.To_String;\n            C : constant Envs.Cursor := Env.Find (S);\n         begin\n            --  The predefined error message does not pass tests.\n            Err.Check (Envs.Has_Element (C), \"'\" & S & \"' not found\");\n            return (Kind_Builtin, Envs.Element (C));\n         end;\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Ast is a non-empty list, First is its first element.\n      First := Eval (First, Env);\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its evaluated first element.\n      Err.Check (First.Kind = Kind_Builtin,\n                 \"first element must be a function\");\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         return First.Builtin.all (Args);\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Map) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Map) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   function Generic_Mal_Operator (Args : in Types.T_Array) return Types.T\n   is (Kind_Number, Ada_Operator (Args (Args'First).Number,\n                                  Args (Args'Last).Number));\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Map) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   function Addition    is new Generic_Mal_Operator (\"+\");\n   function Subtraction is new Generic_Mal_Operator (\"-\");\n   function Product     is new Generic_Mal_Operator (\"*\");\n   function Division    is new Generic_Mal_Operator (\"/\");\n\n   Repl : Envs.Map;\nbegin\n   Repl.Insert (\"+\", Addition   'Unrestricted_Access);\n   Repl.Insert (\"-\", Subtraction'Unrestricted_Access);\n   Repl.Insert (\"*\", Product    'Unrestricted_Access);\n   Repl.Insert (\"/\", Division   'Unrestricted_Access);\n   loop\n      begin\n         Rep (Repl);\n      exception\n         when Readline.End_Of_File =>\n            exit;\n         when Err.Error =>\n            Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n      end;\n      --  Other exceptions are really unexpected.\n\n      --  Collect garbage.\n      Err.Data := Types.Nil;\n      --  No data survives at this stage, Repl only contains static\n      --  pointers to built-in functions.\n      Garbage_Collected.Clean;\n   end loop;\n   Ada.Text_IO.New_Line;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step2_Eval;\n"
  },
  {
    "path": "impls/ada.2/step3_env.adb",
    "content": "with Ada.Text_IO.Unbounded_IO;\n\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step3_Env is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast : in Types.T;\n                  Env : in Envs.Ptr) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   generic\n      with function Ada_Operator (Left, Right : in Integer) return Integer;\n   function Generic_Mal_Operator (Args : in Types.T_Array) return Types.T;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast : in Types.T;\n                  Env : in Envs.Ptr) return Types.T\n   is\n      First          : Types.T;\n   begin\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n               New_Env  : constant Envs.Ptr := Envs.New_Env (Outer => Env);\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  New_Env.all.Set (Bindings (Bindings'First + 2 * I),\n                     Eval (Bindings (Bindings'First + 2 * I + 1), New_Env));\n                  --  This call checks key kind.\n               end loop;\n               return Eval (Ast.Sequence.all.Data (3), New_Env);\n            end;\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         else\n            First := Eval (First, Env);\n         end if;\n      when others =>\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      Err.Check (First.Kind = Kind_Builtin,\n                 \"first element must be a function\");\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         return First.Builtin.all (Args);\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   function Generic_Mal_Operator (Args : in Types.T_Array) return Types.T\n   is (Kind_Number, Ada_Operator (Args (Args'First).Number,\n                                  Args (Args'Last).Number));\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   function Addition    is new Generic_Mal_Operator (\"+\");\n   function Subtraction is new Generic_Mal_Operator (\"-\");\n   function Product     is new Generic_Mal_Operator (\"*\");\n   function Division    is new Generic_Mal_Operator (\"/\");\n\n   Repl   : constant Envs.Ptr := Envs.New_Env;\nbegin\n   --  Add Core functions into the top environment.\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"+\")),\n                 (Kind_Builtin, Addition   'Unrestricted_Access));\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"-\")),\n                 (Kind_Builtin, Subtraction'Unrestricted_Access));\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"*\")),\n                 (Kind_Builtin, Product    'Unrestricted_Access));\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"/\")),\n                 (Kind_Builtin, Division   'Unrestricted_Access));\n   --  Execute user commands.\n   loop\n      begin\n         Rep (Repl);\n      exception\n         when Readline.End_Of_File =>\n            exit;\n         when Err.Error =>\n            Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n      end;\n      --  Other exceptions are really unexpected.\n\n      --  Collect garbage.\n      Err.Data := Types.Nil;\n      Repl.all.Keep;\n      Dbgeval.Keep;\n      Garbage_Collected.Clean;\n   end loop;\n   Ada.Text_IO.New_Line;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step3_Env;\n"
  },
  {
    "path": "impls/ada.2/step4_if_fn_do.adb",
    "content": "with Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step4_If_Fn_Do is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast : in Types.T;\n                  Env : in Envs.Ptr) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast : in Types.T;\n                  Env : in Envs.Ptr) return Types.T\n   is\n      First          : Types.T;\n   begin\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               return Eval (Ast.Sequence.all.Data (3), Env);\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               return Eval (Ast.Sequence.all.Data (4), Env);\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n               New_Env  : constant Envs.Ptr := Envs.New_Env (Outer => Env);\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  New_Env.all.Set (Bindings (Bindings'First + 2 * I),\n                     Eval (Bindings (Bindings'First + 2 * I + 1), New_Env));\n                  --  This call checks key kind.\n               end loop;\n               return Eval (Ast.Sequence.all.Data (3), New_Env);\n            end;\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               return Result;\n            end;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         else\n            First := Eval (First, Env);\n         end if;\n      when others =>\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      Err.Check (First.Kind in Types.Kind_Function,\n                 \"first element must be a function\");\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         if First.Kind = Kind_Builtin then\n            return First.Builtin.all (Args);\n         end if;\n         return First.Fn.all.Apply (Args);\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\";\n   Repl   : constant Envs.Ptr := Envs.New_Env;\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Execute user commands.\n   loop\n      begin\n         Rep (Repl);\n      exception\n         when Readline.End_Of_File =>\n            exit;\n         when Err.Error =>\n            Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n      end;\n      --  Other exceptions are really unexpected.\n\n      --  Collect garbage.\n      Err.Data := Types.Nil;\n      Repl.all.Keep;\n      Dbgeval.Keep;\n      Garbage_Collected.Clean;\n   end loop;\n   Ada.Text_IO.New_Line;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step4_If_Fn_Do;\n"
  },
  {
    "path": "impls/ada.2/step5_tco.adb",
    "content": "with Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step5_Tco is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T\n   is\n      --  Use local variables, that can be rewritten when tail call\n      --  optimization goes to <<Restart>>.\n      Ast            : Types.T  := Ast0;\n      Env            : Envs.Ptr := Env0;\n      First          : Types.T;\n   begin\n      <<Restart>>\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               Ast := Ast.Sequence.all.Data (4);\n               goto Restart;\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               Env := Envs.New_Env (Outer => Env);\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  Env.all.Set (Bindings (Bindings'First + 2 * I),\n                         Eval (Bindings (Bindings'First + 2 * I + 1), Env));\n                  --  This call checks key kind.\n               end loop;\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            end;\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length - 1 loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               pragma Unreferenced (Result);\n            end;\n            Ast := Ast.Sequence.all.Data (Ast.Sequence.all.Length);\n            goto Restart;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         else\n            --  Equivalent to First := Eval (First, Env)\n            --  except that we already know enough to spare a recursive call.\n            First := Env.all.Get (First.Str);\n         end if;\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         --  Equivalent to First := Eval (First, Env)\n         --  except that we already know enough to spare a recursive call.\n         null;\n      when Types.Kind_Sequence | Kind_Map =>\n         --  Lists are definitely worth a recursion, and the two other\n         --  cases should be rare (they will report an error later).\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      Err.Check (First.Kind in Types.Kind_Function,\n                 \"first element must be a function\");\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         if First.Kind = Kind_Builtin then\n            return First.Builtin.all (Args);\n         end if;\n         --  Like Types.Fns.Apply, except that we use TCO.\n         Env := Envs.New_Env (Outer => First.Fn.all.Env);\n         Env.all.Set_Binds (Binds => First.Fn.all.Params.all.Data,\n                            Exprs => Args);\n         Ast := First.Fn.all.Ast;\n         goto Restart;\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\";\n   Repl   : constant Envs.Ptr := Envs.New_Env;\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Execute user commands.\n   loop\n      begin\n         Rep (Repl);\n      exception\n         when Readline.End_Of_File =>\n            exit;\n         when Err.Error =>\n            Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n      end;\n      --  Other exceptions are really unexpected.\n\n      --  Collect garbage.\n      Err.Data := Types.Nil;\n      Repl.all.Keep;\n      Dbgeval.Keep;\n      Garbage_Collected.Clean;\n   end loop;\n   Ada.Text_IO.New_Line;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step5_Tco;\n"
  },
  {
    "path": "impls/ada.2/step6_file.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step6_File is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n   package ACL renames Ada.Command_Line;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T;\n   --  The built-in variant needs to see the Repl variable.\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T\n   is\n      --  Use local variables, that can be rewritten when tail call\n      --  optimization goes to <<Restart>>.\n      Ast            : Types.T  := Ast0;\n      Env            : Envs.Ptr := Env0;\n      First          : Types.T;\n   begin\n      <<Restart>>\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               Ast := Ast.Sequence.all.Data (4);\n               goto Restart;\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               Env := Envs.New_Env (Outer => Env);\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  Env.all.Set (Bindings (Bindings'First + 2 * I),\n                         Eval (Bindings (Bindings'First + 2 * I + 1), Env));\n                  --  This call checks key kind.\n               end loop;\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            end;\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length - 1 loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               pragma Unreferenced (Result);\n            end;\n            Ast := Ast.Sequence.all.Data (Ast.Sequence.all.Length);\n            goto Restart;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         else\n            --  Equivalent to First := Eval (First, Env)\n            --  except that we already know enough to spare a recursive call.\n            First := Env.all.Get (First.Str);\n         end if;\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         --  Equivalent to First := Eval (First, Env)\n         --  except that we already know enough to spare a recursive call.\n         null;\n      when Types.Kind_Sequence | Kind_Map =>\n         --  Lists are definitely worth a recursion, and the two other\n         --  cases should be rare (they will report an error later).\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      Err.Check (First.Kind in Types.Kind_Function,\n                 \"first element must be a function\");\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         if First.Kind = Kind_Builtin then\n            return First.Builtin.all (Args);\n         end if;\n         --  Like Types.Fns.Apply, except that we use TCO.\n         Env := Envs.New_Env (Outer => First.Fn.all.Env);\n         Env.all.Set_Binds (Binds => First.Fn.all.Params.all.Data,\n                            Exprs => Args);\n         Ast := First.Fn.all.Ast;\n         goto Restart;\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\"\n     & \"(def! load-file (fn* (f)\"\n     & \"  (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\";\n   Repl : constant Envs.Ptr := Envs.New_Env;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return Eval (Args (Args'First), Repl);\n   end Eval_Builtin;\n   Script : constant Boolean := 0 < ACL.Argument_Count;\n   Argv   : constant Types.Sequence_Ptr\n     := Types.Sequences.Constructor (Integer'Max (0, ACL.Argument_Count - 1));\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"eval\")),\n                 (Kind_Builtin, Eval_Builtin'Unrestricted_Access));\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Define ARGV from command line arguments.\n   for I in 2 .. ACL.Argument_Count loop\n      Argv.all.Data (I - 1) := (Kind_String,\n                                Types.Strings.Alloc (ACL.Argument (I)));\n   end loop;\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"*ARGV*\")),\n                 (Kind_List, Argv));\n   --  Execute user commands.\n   if Script then\n      Exec (\"(load-file \"\"\" & ACL.Argument (1) & \"\"\")\", Repl);\n   else\n      loop\n         begin\n            Rep (Repl);\n         exception\n            when Readline.End_Of_File =>\n               exit;\n            when Err.Error =>\n               Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n         end;\n         --  Other exceptions are really unexpected.\n\n         --  Collect garbage.\n         Err.Data := Types.Nil;\n         Repl.all.Keep;\n         Dbgeval.Keep;\n         Garbage_Collected.Clean;\n      end loop;\n      Ada.Text_IO.New_Line;\n   end if;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step6_File;\n"
  },
  {
    "path": "impls/ada.2/step7_quote.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step7_Quote is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n   package ACL renames Ada.Command_Line;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T;\n   --  The built-in variant needs to see the Repl variable.\n\n   function Quasiquote (Ast : in Types.T) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T\n   is\n      --  Use local variables, that can be rewritten when tail call\n      --  optimization goes to <<Restart>>.\n      Ast            : Types.T  := Ast0;\n      Env            : Envs.Ptr := Env0;\n      First          : Types.T;\n   begin\n      <<Restart>>\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               Ast := Ast.Sequence.all.Data (4);\n               goto Restart;\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               Env := Envs.New_Env (Outer => Env);\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  Env.all.Set (Bindings (Bindings'First + 2 * I),\n                         Eval (Bindings (Bindings'First + 2 * I + 1), Env));\n                  --  This call checks key kind.\n               end loop;\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            end;\n         elsif First.Str.all = \"quote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            return Ast.Sequence.all.Data (2);\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length - 1 loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               pragma Unreferenced (Result);\n            end;\n            Ast := Ast.Sequence.all.Data (Ast.Sequence.all.Length);\n            goto Restart;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         elsif First.Str.all = \"quasiquote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            Ast := Quasiquote (Ast.Sequence.all.Data (2));\n            goto Restart;\n         else\n            --  Equivalent to First := Eval (First, Env)\n            --  except that we already know enough to spare a recursive call.\n            First := Env.all.Get (First.Str);\n         end if;\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         --  Equivalent to First := Eval (First, Env)\n         --  except that we already know enough to spare a recursive call.\n         null;\n      when Types.Kind_Sequence | Kind_Map =>\n         --  Lists are definitely worth a recursion, and the two other\n         --  cases should be rare (they will report an error later).\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      Err.Check (First.Kind in Types.Kind_Function,\n                 \"first element must be a function\");\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         if First.Kind = Kind_Builtin then\n            return First.Builtin.all (Args);\n         end if;\n         --  Like Types.Fns.Apply, except that we use TCO.\n         Env := Envs.New_Env (Outer => First.Fn.all.Env);\n         Env.all.Set_Binds (Binds => First.Fn.all.Params.all.Data,\n                            Exprs => Args);\n         Ast := First.Fn.all.Ast;\n         goto Restart;\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Quasiquote (Ast : in Types.T) return Types.T is\n\n      function Qq_Seq return Types.T;\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean;\n\n      function Qq_Seq return Types.T is\n         Result : Types.T := Types.Sequences.List ((1 .. 0 => Types.Nil));\n      begin\n         for Elt of reverse Ast.Sequence.all.Data loop\n            if Elt.Kind = Kind_List\n              and then Starts_With (Elt.Sequence.all.Data, \"splice-unquote\")\n            then\n               Err.Check (Elt.Sequence.all.Length = 2,\n                          \"splice-unquote expects 1 parameter\");\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"concat\")),\n                   Elt.Sequence.all.Data (2), Result));\n            else\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"cons\")),\n                   Quasiquote (Elt), Result));\n            end if;\n         end loop;\n         return Result;\n      end Qq_Seq;\n\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean is\n         (0 < Sequence'Length\n            and then Sequence (Sequence'First).Kind = Kind_Symbol\n            and then Sequence (Sequence'First).Str.all = Symbol);\n\n   begin\n      case Ast.Kind is\n         when Kind_List =>\n            if Starts_With (Ast.Sequence.all.Data, \"unquote\") then\n               Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n               return Ast.Sequence.all.Data (2);\n            else\n               return Qq_Seq;\n            end if;\n         when Kind_Vector =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"vec\")), Qq_Seq));\n         when Kind_Map | Kind_Symbol =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"quote\")), Ast));\n         when others =>\n            return Ast;\n      end case;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"quasiquote\", Ast);\n         raise;\n   end Quasiquote;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\"\n     & \"(def! load-file (fn* (f)\"\n     & \"  (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\";\n   Repl : constant Envs.Ptr := Envs.New_Env;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return Eval (Args (Args'First), Repl);\n   end Eval_Builtin;\n   Script : constant Boolean := 0 < ACL.Argument_Count;\n   Argv   : constant Types.Sequence_Ptr\n     := Types.Sequences.Constructor (Integer'Max (0, ACL.Argument_Count - 1));\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"eval\")),\n                 (Kind_Builtin, Eval_Builtin'Unrestricted_Access));\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Define ARGV from command line arguments.\n   for I in 2 .. ACL.Argument_Count loop\n      Argv.all.Data (I - 1) := (Kind_String,\n                                Types.Strings.Alloc (ACL.Argument (I)));\n   end loop;\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"*ARGV*\")),\n                 (Kind_List, Argv));\n   --  Execute user commands.\n   if Script then\n      Exec (\"(load-file \"\"\" & ACL.Argument (1) & \"\"\")\", Repl);\n   else\n      loop\n         begin\n            Rep (Repl);\n         exception\n            when Readline.End_Of_File =>\n               exit;\n            when Err.Error =>\n               Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n         end;\n         --  Other exceptions are really unexpected.\n\n         --  Collect garbage.\n         Err.Data := Types.Nil;\n         Repl.all.Keep;\n         Dbgeval.Keep;\n         Garbage_Collected.Clean;\n      end loop;\n      Ada.Text_IO.New_Line;\n   end if;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step7_Quote;\n"
  },
  {
    "path": "impls/ada.2/step8_macros.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step8_Macros is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n   package ACL renames Ada.Command_Line;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T;\n   --  The built-in variant needs to see the Repl variable.\n\n   function Quasiquote (Ast : in Types.T) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T\n   is\n      --  Use local variables, that can be rewritten when tail call\n      --  optimization goes to <<Restart>>.\n      Ast            : Types.T  := Ast0;\n      Env            : Envs.Ptr := Env0;\n      First          : Types.T;\n   begin\n      <<Restart>>\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               Ast := Ast.Sequence.all.Data (4);\n               goto Restart;\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               Env := Envs.New_Env (Outer => Env);\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  Env.all.Set (Bindings (Bindings'First + 2 * I),\n                         Eval (Bindings (Bindings'First + 2 * I + 1), Env));\n                  --  This call checks key kind.\n               end loop;\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            end;\n         elsif First.Str.all = \"quote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            return Ast.Sequence.all.Data (2);\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"defmacro!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Fun : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n               Val : Types.T;\n            begin\n               Err.Check (Fun.Kind = Kind_Fn, \"expected a function\");\n               Val := (Kind_Macro, Types.Fns.New_Function\n                         (Params   => Fun.Fn.all.Params,\n                          Ast      => Fun.Fn.all.Ast,\n                          Env      => Fun.Fn.all.Env));\n               Env.all.Set (Key, Val);  --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length - 1 loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               pragma Unreferenced (Result);\n            end;\n            Ast := Ast.Sequence.all.Data (Ast.Sequence.all.Length);\n            goto Restart;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         elsif First.Str.all = \"quasiquote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            Ast := Quasiquote (Ast.Sequence.all.Data (2));\n            goto Restart;\n         else\n            --  Equivalent to First := Eval (First, Env)\n            --  except that we already know enough to spare a recursive call.\n            First := Env.all.Get (First.Str);\n         end if;\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         --  Equivalent to First := Eval (First, Env)\n         --  except that we already know enough to spare a recursive call.\n         null;\n      when Types.Kind_Sequence | Kind_Map =>\n         --  Lists are definitely worth a recursion, and the two other\n         --  cases should be rare (they will report an error later).\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      case First.Kind is\n      when Kind_Macro =>\n         --  Use the unevaluated arguments.\n         Ast := First.Fn.all.Apply\n           (Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));\n         --  Then evaluate the result with TCO.\n         goto Restart;\n      when Types.Kind_Function =>\n         null;\n      when others =>\n         Err.Raise_With (\"first element must be a function or macro\");\n      end case;\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         if First.Kind = Kind_Builtin then\n            return First.Builtin.all (Args);\n         end if;\n         --  Like Types.Fns.Apply, except that we use TCO.\n         Env := Envs.New_Env (Outer => First.Fn.all.Env);\n         Env.all.Set_Binds (Binds => First.Fn.all.Params.all.Data,\n                            Exprs => Args);\n         Ast := First.Fn.all.Ast;\n         goto Restart;\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Quasiquote (Ast : in Types.T) return Types.T is\n\n      function Qq_Seq return Types.T;\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean;\n\n      function Qq_Seq return Types.T is\n         Result : Types.T := Types.Sequences.List ((1 .. 0 => Types.Nil));\n      begin\n         for Elt of reverse Ast.Sequence.all.Data loop\n            if Elt.Kind = Kind_List\n              and then Starts_With (Elt.Sequence.all.Data, \"splice-unquote\")\n            then\n               Err.Check (Elt.Sequence.all.Length = 2,\n                          \"splice-unquote expects 1 parameter\");\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"concat\")),\n                   Elt.Sequence.all.Data (2), Result));\n            else\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"cons\")),\n                   Quasiquote (Elt), Result));\n            end if;\n         end loop;\n         return Result;\n      end Qq_Seq;\n\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean is\n         (0 < Sequence'Length\n            and then Sequence (Sequence'First).Kind = Kind_Symbol\n            and then Sequence (Sequence'First).Str.all = Symbol);\n\n   begin\n      case Ast.Kind is\n         when Kind_List =>\n            if Starts_With (Ast.Sequence.all.Data, \"unquote\") then\n               Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n               return Ast.Sequence.all.Data (2);\n            else\n               return Qq_Seq;\n            end if;\n         when Kind_Vector =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"vec\")), Qq_Seq));\n         when Kind_Map | Kind_Symbol =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"quote\")), Ast));\n         when others =>\n            return Ast;\n      end case;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"quasiquote\", Ast);\n         raise;\n   end Quasiquote;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\"\n     & \"(def! load-file (fn* (f)\"\n     & \"  (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\"\n     & \"(defmacro! cond (fn* (& xs)\"\n     & \"  (if (> (count xs) 0)\"\n     & \"    (list 'if (first xs)\"\n     & \"      (if (> (count xs) 1) (nth xs 1)\"\n     & \"        (throw \"\"odd number of forms to cond\"\"))\"\n     & \"      (cons 'cond (rest (rest xs)))))))\";\n   Repl : constant Envs.Ptr := Envs.New_Env;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return Eval (Args (Args'First), Repl);\n   end Eval_Builtin;\n   Script : constant Boolean := 0 < ACL.Argument_Count;\n   Argv   : constant Types.Sequence_Ptr\n     := Types.Sequences.Constructor (Integer'Max (0, ACL.Argument_Count - 1));\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"eval\")),\n                 (Kind_Builtin, Eval_Builtin'Unrestricted_Access));\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Define ARGV from command line arguments.\n   for I in 2 .. ACL.Argument_Count loop\n      Argv.all.Data (I - 1) := (Kind_String,\n                                Types.Strings.Alloc (ACL.Argument (I)));\n   end loop;\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"*ARGV*\")),\n                 (Kind_List, Argv));\n   --  Execute user commands.\n   if Script then\n      Exec (\"(load-file \"\"\" & ACL.Argument (1) & \"\"\")\", Repl);\n   else\n      loop\n         begin\n            Rep (Repl);\n         exception\n            when Readline.End_Of_File =>\n               exit;\n            when Err.Error =>\n               Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n         end;\n         --  Other exceptions are really unexpected.\n\n         --  Collect garbage.\n         Err.Data := Types.Nil;\n         Repl.all.Keep;\n         Dbgeval.Keep;\n         Garbage_Collected.Clean;\n      end loop;\n      Ada.Text_IO.New_Line;\n   end if;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step8_Macros;\n"
  },
  {
    "path": "impls/ada.2/step9_try.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure Step9_Try is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n   package ACL renames Ada.Command_Line;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T;\n   --  The built-in variant needs to see the Repl variable.\n\n   function Quasiquote (Ast : in Types.T) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T\n   is\n      --  Use local variables, that can be rewritten when tail call\n      --  optimization goes to <<Restart>>.\n      Ast            : Types.T  := Ast0;\n      Env            : Envs.Ptr := Env0;\n      First          : Types.T;\n   begin\n      <<Restart>>\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               Ast := Ast.Sequence.all.Data (4);\n               goto Restart;\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               Env := Envs.New_Env (Outer => Env);\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  Env.all.Set (Bindings (Bindings'First + 2 * I),\n                         Eval (Bindings (Bindings'First + 2 * I + 1), Env));\n                  --  This call checks key kind.\n               end loop;\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            end;\n         elsif First.Str.all = \"quote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            return Ast.Sequence.all.Data (2);\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"defmacro!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Fun : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n               Val : Types.T;\n            begin\n               Err.Check (Fun.Kind = Kind_Fn, \"expected a function\");\n               Val := (Kind_Macro, Types.Fns.New_Function\n                         (Params   => Fun.Fn.all.Params,\n                          Ast      => Fun.Fn.all.Ast,\n                          Env      => Fun.Fn.all.Env));\n               Env.all.Set (Key, Val);  --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length - 1 loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               pragma Unreferenced (Result);\n            end;\n            Ast := Ast.Sequence.all.Data (Ast.Sequence.all.Length);\n            goto Restart;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         elsif First.Str.all = \"quasiquote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            Ast := Quasiquote (Ast.Sequence.all.Data (2));\n            goto Restart;\n         elsif First.Str.all = \"try*\" then\n            if Ast.Sequence.all.Length = 2 then\n               Ast := Ast.Sequence.all.Data (2);\n               goto Restart;\n            end if;\n            Err.Check (Ast.Sequence.all.Length = 3\n                         and then Ast.Sequence.all.Data (3).Kind = Kind_List,\n                       \"expected 1 parameter, maybe followed by a list\");\n            declare\n               A3 : Types.T_Array\n                 renames Ast.Sequence.all.Data (3).Sequence.all.Data;\n            begin\n               Err.Check (A3'Length = 3\n                            and then A3 (A3'First).Kind = Kind_Symbol\n                            and then A3 (A3'First).Str.all = \"catch*\",\n                          \"3rd parameter if present must be a catch* list\");\n               begin\n                  return Eval (Ast.Sequence.all.Data (2), Env);\n               exception\n                  when Err.Error =>\n                     null;\n               end;\n               Env := Envs.New_Env (Outer => Env);\n               Env.all.Set (A3 (A3'First + 1), Err.Data); --  check key kind\n               Ast := A3 (A3'Last);\n               goto Restart;\n            end;\n         else\n            --  Equivalent to First := Eval (First, Env)\n            --  except that we already know enough to spare a recursive call.\n            First := Env.all.Get (First.Str);\n         end if;\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         --  Equivalent to First := Eval (First, Env)\n         --  except that we already know enough to spare a recursive call.\n         null;\n      when Types.Kind_Sequence | Kind_Map =>\n         --  Lists are definitely worth a recursion, and the two other\n         --  cases should be rare (they will report an error later).\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      case First.Kind is\n      when Kind_Macro =>\n         --  Use the unevaluated arguments.\n         Ast := First.Fn.all.Apply\n           (Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));\n         --  Then evaluate the result with TCO.\n         goto Restart;\n      when Types.Kind_Function =>\n         null;\n      when others =>\n         Err.Raise_With (\"first element must be a function or macro\");\n      end case;\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         if First.Kind = Kind_Builtin then\n            return First.Builtin.all (Args);\n         end if;\n         --  Like Types.Fns.Apply, except that we use TCO.\n         Env := Envs.New_Env (Outer => First.Fn.all.Env);\n         Env.all.Set_Binds (Binds => First.Fn.all.Params.all.Data,\n                            Exprs => Args);\n         Ast := First.Fn.all.Ast;\n         goto Restart;\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Quasiquote (Ast : in Types.T) return Types.T is\n\n      function Qq_Seq return Types.T;\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean;\n\n      function Qq_Seq return Types.T is\n         Result : Types.T := Types.Sequences.List ((1 .. 0 => Types.Nil));\n      begin\n         for Elt of reverse Ast.Sequence.all.Data loop\n            if Elt.Kind = Kind_List\n              and then Starts_With (Elt.Sequence.all.Data, \"splice-unquote\")\n            then\n               Err.Check (Elt.Sequence.all.Length = 2,\n                          \"splice-unquote expects 1 parameter\");\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"concat\")),\n                   Elt.Sequence.all.Data (2), Result));\n            else\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"cons\")),\n                   Quasiquote (Elt), Result));\n            end if;\n         end loop;\n         return Result;\n      end Qq_Seq;\n\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean is\n         (0 < Sequence'Length\n            and then Sequence (Sequence'First).Kind = Kind_Symbol\n            and then Sequence (Sequence'First).Str.all = Symbol);\n\n   begin\n      case Ast.Kind is\n         when Kind_List =>\n            if Starts_With (Ast.Sequence.all.Data, \"unquote\") then\n               Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n               return Ast.Sequence.all.Data (2);\n            else\n               return Qq_Seq;\n            end if;\n         when Kind_Vector =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"vec\")), Qq_Seq));\n         when Kind_Map | Kind_Symbol =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"quote\")), Ast));\n         when others =>\n            return Ast;\n      end case;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"quasiquote\", Ast);\n         raise;\n   end Quasiquote;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\"\n     & \"(def! load-file (fn* (f)\"\n     & \"  (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\"\n     & \"(defmacro! cond (fn* (& xs)\"\n     & \"  (if (> (count xs) 0)\"\n     & \"    (list 'if (first xs)\"\n     & \"      (if (> (count xs) 1) (nth xs 1)\"\n     & \"        (throw \"\"odd number of forms to cond\"\"))\"\n     & \"      (cons 'cond (rest (rest xs)))))))\";\n   Repl : constant Envs.Ptr := Envs.New_Env;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return Eval (Args (Args'First), Repl);\n   end Eval_Builtin;\n   Script : constant Boolean := 0 < ACL.Argument_Count;\n   Argv   : constant Types.Sequence_Ptr\n     := Types.Sequences.Constructor (Integer'Max (0, ACL.Argument_Count - 1));\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"eval\")),\n                 (Kind_Builtin, Eval_Builtin'Unrestricted_Access));\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Define ARGV from command line arguments.\n   for I in 2 .. ACL.Argument_Count loop\n      Argv.all.Data (I - 1) := (Kind_String,\n                                Types.Strings.Alloc (ACL.Argument (I)));\n   end loop;\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"*ARGV*\")),\n                 (Kind_List, Argv));\n   --  Execute user commands.\n   if Script then\n      Exec (\"(load-file \"\"\" & ACL.Argument (1) & \"\"\")\", Repl);\n   else\n      loop\n         begin\n            Rep (Repl);\n         exception\n            when Readline.End_Of_File =>\n               exit;\n            when Err.Error =>\n               Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n         end;\n         --  Other exceptions are really unexpected.\n\n         --  Collect garbage.\n         Err.Data := Types.Nil;\n         Repl.all.Keep;\n         Dbgeval.Keep;\n         Garbage_Collected.Clean;\n      end loop;\n      Ada.Text_IO.New_Line;\n   end if;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend Step9_Try;\n"
  },
  {
    "path": "impls/ada.2/stepa_mal.adb",
    "content": "with Ada.Command_Line;\nwith Ada.Text_IO.Unbounded_IO;\n\nwith Core;\nwith Envs;\nwith Err;\nwith Garbage_Collected;\nwith Printer;\nwith Reader;\nwith Readline;\nwith Types.Builtins;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\nwith Types.Strings;\n\nprocedure StepA_Mal is\n\n   Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc (\"DEBUG-EVAL\");\n\n   use type Types.T;\n   use all type Types.Kind_Type;\n   use type Types.Strings.Instance;\n   package ACL renames Ada.Command_Line;\n\n   function Read return Types.T_Array with Inline;\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T;\n   --  The built-in variant needs to see the Repl variable.\n\n   function Quasiquote (Ast : in Types.T) return Types.T;\n\n   procedure Print (Ast : in Types.T) with Inline;\n\n   procedure Rep (Env : in Envs.Ptr) with Inline;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T;\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T;\n   --  Helpers for the Eval function.\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr) with Inline;\n   --  Read the script, eval its elements, but ignore the result.\n\n   ----------------------------------------------------------------------\n\n   function Eval (Ast0 : in Types.T;\n                  Env0 : in Envs.Ptr) return Types.T\n   is\n      --  Use local variables, that can be rewritten when tail call\n      --  optimization goes to <<Restart>>.\n      Ast            : Types.T  := Ast0;\n      Env            : Envs.Ptr := Env0;\n      First          : Types.T;\n   begin\n      <<Restart>>\n      if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then\n         Ada.Text_IO.Put (\"EVAL: \");\n         Print (Ast);\n         Envs.Dump_Stack (Env.all);\n      end if;\n\n      case Ast.Kind is\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         return Ast;\n      when Kind_Symbol =>\n         return Env.all.Get (Ast.Str);\n      when Kind_Map =>\n         return Eval_Map (Ast.Map.all, Env);\n      when Kind_Vector =>\n         return Eval_Vector (Ast.Sequence.all, Env);\n      when Kind_List =>\n         null;\n      end case;\n\n      --  Ast is a list.\n      if Ast.Sequence.all.Length = 0 then\n         return Ast;\n      end if;\n      First := Ast.Sequence.all.Data (1);\n\n      --  Special forms\n      --  Ast is a non-empty list, First is its first element.\n      case First.Kind is\n      when Kind_Symbol =>\n         if First.Str.all = \"if\" then\n            Err.Check (Ast.Sequence.all.Length in 3 .. 4,\n                       \"expected 2 or 3 parameters\");\n            if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            elsif Ast.Sequence.all.Length = 3 then\n               return Types.Nil;\n            else\n               Ast := Ast.Sequence.all.Data (4);\n               goto Restart;\n            end if;\n         elsif First.Str.all = \"let*\" then\n            Err.Check (Ast.Sequence.all.Length = 3\n               and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,\n                       \"expected a sequence then a value\");\n            declare\n               Bindings : Types.T_Array\n                 renames Ast.Sequence.all.Data (2).Sequence.all.Data;\n            begin\n               Err.Check (Bindings'Length mod 2 = 0, \"expected even binds\");\n               Env := Envs.New_Env (Outer => Env);\n               for I in 0 .. Bindings'Length / 2 - 1 loop\n                  Env.all.Set (Bindings (Bindings'First + 2 * I),\n                         Eval (Bindings (Bindings'First + 2 * I + 1), Env));\n                  --  This call checks key kind.\n               end loop;\n               Ast := Ast.Sequence.all.Data (3);\n               goto Restart;\n            end;\n         elsif First.Str.all = \"quote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            return Ast.Sequence.all.Data (2);\n         elsif First.Str.all = \"def!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Val : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n            begin\n               Env.all.Set (Key, Val); --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"defmacro!\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Key : Types.T renames Ast.Sequence.all.Data (2);\n               Fun : constant Types.T := Eval (Ast.Sequence.all.Data (3), Env);\n               Val : Types.T;\n            begin\n               Err.Check (Fun.Kind = Kind_Fn, \"expected a function\");\n               Val := (Kind_Macro, Types.Fns.New_Function\n                         (Params   => Fun.Fn.all.Params,\n                          Ast      => Fun.Fn.all.Ast,\n                          Env      => Fun.Fn.all.Env));\n               Env.all.Set (Key, Val);  --  Check key kind.\n               return Val;\n            end;\n         elsif First.Str.all = \"do\" then\n            Err.Check (1 < Ast.Sequence.all.Length, \"do expects arguments\");\n            declare\n               Result : Types.T;\n            begin\n               for I in 2 .. Ast.Sequence.all.Length - 1 loop\n                  Result := Eval (Ast.Sequence.all.Data (I), Env);\n               end loop;\n               pragma Unreferenced (Result);\n            end;\n            Ast := Ast.Sequence.all.Data (Ast.Sequence.all.Length);\n            goto Restart;\n         elsif First.Str.all = \"fn*\" then\n            Err.Check (Ast.Sequence.all.Length = 3, \"expected 2 parameters\");\n            declare\n               Params : Types.T renames Ast.Sequence.all.Data (2);\n            begin\n               Err.Check (Params.Kind in Types.Kind_Sequence,\n                          \"first argument of fn* must be a sequence\");\n               return (Kind_Fn, Types.Fns.New_Function\n                 (Params => Params.Sequence,\n                  Ast    => Ast.Sequence.all.Data (3),\n                  Env    => Env));\n            end;\n         elsif First.Str.all = \"quasiquote\" then\n            Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n            Ast := Quasiquote (Ast.Sequence.all.Data (2));\n            goto Restart;\n         elsif First.Str.all = \"try*\" then\n            if Ast.Sequence.all.Length = 2 then\n               Ast := Ast.Sequence.all.Data (2);\n               goto Restart;\n            end if;\n            Err.Check (Ast.Sequence.all.Length = 3\n                         and then Ast.Sequence.all.Data (3).Kind = Kind_List,\n                       \"expected 1 parameter, maybe followed by a list\");\n            declare\n               A3 : Types.T_Array\n                 renames Ast.Sequence.all.Data (3).Sequence.all.Data;\n            begin\n               Err.Check (A3'Length = 3\n                            and then A3 (A3'First).Kind = Kind_Symbol\n                            and then A3 (A3'First).Str.all = \"catch*\",\n                          \"3rd parameter if present must be a catch* list\");\n               begin\n                  return Eval (Ast.Sequence.all.Data (2), Env);\n               exception\n                  when Err.Error =>\n                     null;\n               end;\n               Env := Envs.New_Env (Outer => Env);\n               Env.all.Set (A3 (A3'First + 1), Err.Data); --  check key kind\n               Ast := A3 (A3'Last);\n               goto Restart;\n            end;\n         else\n            --  Equivalent to First := Eval (First, Env)\n            --  except that we already know enough to spare a recursive call.\n            First := Env.all.Get (First.Str);\n         end if;\n      when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key\n        | Kind_Macro | Types.Kind_Function =>\n         --  Equivalent to First := Eval (First, Env)\n         --  except that we already know enough to spare a recursive call.\n         null;\n      when Types.Kind_Sequence | Kind_Map =>\n         --  Lists are definitely worth a recursion, and the two other\n         --  cases should be rare (they will report an error later).\n         First := Eval (First, Env);\n      end case;\n\n      --  Apply phase.\n      --  Ast is a non-empty list,\n      --  First is its non-special evaluated first element.\n      case First.Kind is\n      when Kind_Macro =>\n         --  Use the unevaluated arguments.\n         Ast := First.Fn.all.Apply\n           (Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));\n         --  Then evaluate the result with TCO.\n         goto Restart;\n      when Types.Kind_Function =>\n         null;\n      when others =>\n         Err.Raise_With (\"first element must be a function or macro\");\n      end case;\n      --  We are applying a function. Evaluate its arguments.\n      declare\n         Args : Types.T_Array (2 .. Ast.Sequence.all.Length);\n      begin\n         for I in Args'Range loop\n            Args (I) := Eval (Ast.Sequence.all.Data (I), Env);\n         end loop;\n         case First.Kind is\n         when Kind_Builtin =>\n            return First.Builtin.all (Args);\n         when Kind_Builtin_With_Meta =>\n            return First.Builtin_With_Meta.all.Builtin.all (Args);\n         when others =>\n            null;\n         end case;\n         --  Like Types.Fns.Apply, except that we use TCO.\n         Env := Envs.New_Env (Outer => First.Fn.all.Env);\n         Env.all.Set_Binds (Binds => First.Fn.all.Params.all.Data,\n                            Exprs => Args);\n         Ast := First.Fn.all.Ast;\n         goto Restart;\n      end;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"eval\", Ast);\n         raise;\n   end Eval;\n\n   function Eval_Map (Source : in Types.Maps.Instance;\n                      Env    : in Envs.Ptr) return Types.T\n   is\n      use all type Types.Maps.Cursor;\n      --  Copy the whole map so that keys are not hashed again.\n      Result   : constant Types.T  := Types.Maps.New_Map (Source);\n      Position : Types.Maps.Cursor := Result.Map.all.First;\n   begin\n      while Has_Element (Position) loop\n         Result.Map.all.Replace_Element (Position,\n                                         Eval (Element (Position), Env));\n         Next (Position);\n      end loop;\n      return Result;\n   end Eval_Map;\n\n   function Eval_Vector (Source : in Types.Sequences.Instance;\n                         Env    : in Envs.Ptr) return Types.T\n   is\n      Ref : constant Types.Sequence_Ptr\n        := Types.Sequences.Constructor (Source.Length);\n   begin\n      for I in Source.Data'Range loop\n         Ref.all.Data (I) := Eval (Source.Data (I), Env);\n      end loop;\n      return (Kind_Vector, Ref);\n   end Eval_Vector;\n\n   procedure Exec (Script : in String;\n                   Env    : in Envs.Ptr)\n   is\n      Result : Types.T;\n   begin\n      for Expression of Reader.Read_Str (Script) loop\n         Result := Eval (Expression, Env);\n      end loop;\n      pragma Unreferenced (Result);\n   end Exec;\n\n   procedure Print (Ast : in Types.T) is\n   begin\n      Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));\n   end Print;\n\n   function Quasiquote (Ast : in Types.T) return Types.T is\n\n      function Qq_Seq return Types.T;\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean;\n\n      function Qq_Seq return Types.T is\n         Result : Types.T := Types.Sequences.List ((1 .. 0 => Types.Nil));\n      begin\n         for Elt of reverse Ast.Sequence.all.Data loop\n            if Elt.Kind = Kind_List\n              and then Starts_With (Elt.Sequence.all.Data, \"splice-unquote\")\n            then\n               Err.Check (Elt.Sequence.all.Length = 2,\n                          \"splice-unquote expects 1 parameter\");\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"concat\")),\n                   Elt.Sequence.all.Data (2), Result));\n            else\n               Result := Types.Sequences.List\n                 (((Kind_Symbol, Types.Strings.Alloc (\"cons\")),\n                   Quasiquote (Elt), Result));\n            end if;\n         end loop;\n         return Result;\n      end Qq_Seq;\n\n      function Starts_With (Sequence : Types.T_Array;\n                            Symbol   : String) return Boolean is\n         (0 < Sequence'Length\n            and then Sequence (Sequence'First).Kind = Kind_Symbol\n            and then Sequence (Sequence'First).Str.all = Symbol);\n\n   begin\n      case Ast.Kind is\n         when Kind_List =>\n            if Starts_With (Ast.Sequence.all.Data, \"unquote\") then\n               Err.Check (Ast.Sequence.all.Length = 2, \"expected 1 parameter\");\n               return Ast.Sequence.all.Data (2);\n            else\n               return Qq_Seq;\n            end if;\n         when Kind_Vector =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"vec\")), Qq_Seq));\n         when Kind_Map | Kind_Symbol =>\n            return Types.Sequences.List\n              (((Kind_Symbol, Types.Strings.Alloc (\"quote\")), Ast));\n         when others =>\n            return Ast;\n      end case;\n   exception\n      when Err.Error =>\n         Err.Add_Trace_Line (\"quasiquote\", Ast);\n         raise;\n   end Quasiquote;\n\n   function Read return Types.T_Array\n   is (Reader.Read_Str (Readline.Input (\"user> \")));\n\n   procedure Rep (Env : in Envs.Ptr) is\n   begin\n      for Expression of Read loop\n         Print (Eval (Expression, Env));\n      end loop;\n   end Rep;\n\n   ----------------------------------------------------------------------\n\n   Startup : constant String\n     := \"(def! not (fn* (a) (if a false true)))\"\n     & \"(def! load-file (fn* (f)\"\n     & \"  (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\"\n     & \"(defmacro! cond (fn* (& xs)\"\n     & \"  (if (> (count xs) 0)\"\n     & \"    (list 'if (first xs)\"\n     & \"      (if (> (count xs) 1) (nth xs 1)\"\n     & \"        (throw \"\"odd number of forms to cond\"\"))\"\n     & \"      (cons 'cond (rest (rest xs)))))))\"\n     & \"(def! *host-language* \"\"ada.2\"\")\";\n   Repl : constant Envs.Ptr := Envs.New_Env;\n   function Eval_Builtin (Args : in Types.T_Array) return Types.T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      return Eval (Args (Args'First), Repl);\n   end Eval_Builtin;\n   Script : constant Boolean := 0 < ACL.Argument_Count;\n   Argv   : constant Types.Sequence_Ptr\n     := Types.Sequences.Constructor (Integer'Max (0, ACL.Argument_Count - 1));\nbegin\n   --  Show the Eval function to other packages.\n   Types.Fns.Eval_Cb := Eval'Unrestricted_Access;\n   --  Add Core functions into the top environment.\n   Core.NS_Add_To_Repl (Repl);\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"eval\")),\n                 (Kind_Builtin, Eval_Builtin'Unrestricted_Access));\n   --  Native startup procedure.\n   Exec (Startup, Repl);\n   --  Define ARGV from command line arguments.\n   for I in 2 .. ACL.Argument_Count loop\n      Argv.all.Data (I - 1) := (Kind_String,\n                                Types.Strings.Alloc (ACL.Argument (I)));\n   end loop;\n   Repl.all.Set ((Kind_Symbol, Types.Strings.Alloc (\"*ARGV*\")),\n                 (Kind_List, Argv));\n   --  Execute user commands.\n   if Script then\n      Exec (\"(load-file \"\"\" & ACL.Argument (1) & \"\"\")\", Repl);\n   else\n      Exec (\"(println (str \"\"Mal [\"\" *host-language* \"\"]\"\"))\", Repl);\n      loop\n         begin\n            Rep (Repl);\n         exception\n            when Readline.End_Of_File =>\n               exit;\n            when Err.Error =>\n               Ada.Text_IO.Unbounded_IO.Put (Err.Trace);\n         end;\n         --  Other exceptions are really unexpected.\n\n         --  Collect garbage.\n         Err.Data := Types.Nil;\n         Repl.all.Keep;\n         Dbgeval.Keep;\n         Garbage_Collected.Clean;\n      end loop;\n      Ada.Text_IO.New_Line;\n   end if;\n\n   --  If assertions are enabled, check deallocations.\n   --  Normal runs do not need to deallocate before termination.\n   --  Beware that all pointers are now dangling.\n   pragma Debug (Garbage_Collected.Clean);\n   Garbage_Collected.Check_Allocations;\nend StepA_Mal;\n"
  },
  {
    "path": "impls/ada.2/types-atoms.adb",
    "content": "with Err;\nwith Types.Builtins;\nwith Types.Fns;\n\npackage body Types.Atoms is\n\n   function Atom (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      declare\n         Ref : constant Atom_Ptr := new Instance;\n      begin\n         Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n         Ref.all.Data := Args (Args'First);\n         return (Kind_Atom, Ref);\n      end;\n   end Atom;\n\n   function Deref (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_Atom,\n                 \"expected an atom\");\n      return Args (Args'First).Atom.all.Data;\n   end Deref;\n\n   function Deref (Item : in Instance) return T\n   is (Item.Data);\n\n   procedure Keep_References (Object : in out Instance) is\n   begin\n      Keep (Object.Data);\n   end Keep_References;\n\n   function Reset (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 2 and then Args (Args'First).Kind = Kind_Atom,\n                 \"expected an atom then a value\");\n      Args (Args'First).Atom.all.Data := Args (Args'Last);\n      return Args (Args'Last);\n   end Reset;\n\n   function Swap (Args : in T_Array) return T is\n   begin\n      Err.Check (2 <= Args'Length and then Args (Args'First).Kind = Kind_Atom,\n                 \"expected an atom, a function, then optional arguments\");\n      declare\n         X : T renames Args (Args'First).Atom.all.Data;\n         F : T renames Args (Args'First + 1);\n         A : constant T_Array := X & Args (Args'First + 2 .. Args'Last);\n      begin\n         case F.Kind is\n            when Kind_Builtin =>\n               X := F.Builtin.all (A);\n            when Kind_Builtin_With_Meta =>\n               X := F.Builtin_With_Meta.all.Builtin.all (A);\n            when Kind_Fn =>\n               X := F.Fn.all.Apply (A);\n            when others =>\n               Err.Raise_With (\"parameter 2 must be a function\");\n         end case;\n         return X;\n      end;\n   end Swap;\n\nend Types.Atoms;\n"
  },
  {
    "path": "impls/ada.2/types-atoms.ads",
    "content": "with Garbage_Collected;\n\npackage Types.Atoms is\n\n   type Instance (<>) is abstract new Garbage_Collected.Instance with private;\n\n   --  Built-in functions.\n   function Atom  (Args : in T_Array) return T;\n   function Deref (Args : in T_Array) return T;\n   function Reset (Args : in T_Array) return T;\n   function Swap  (Args : in T_Array) return T;\n\n   --  Helper for print.\n   function Deref (Item : in Instance) return T with Inline;\n\nprivate\n\n   type Instance is new Garbage_Collected.Instance with record\n      Data : T;\n   end record;\n\n   overriding procedure Keep_References (Object : in out Instance) with Inline;\n\nend Types.Atoms;\n"
  },
  {
    "path": "impls/ada.2/types-builtins.adb",
    "content": "package body Types.Builtins is\n\n   function Builtin (Item : in Instance) return Builtin_Ptr\n   is (Item.F_Builtin);\n\n   procedure Keep_References (Object : in out Instance) is\n   begin\n      Keep (Object.F_Meta);\n   end Keep_References;\n\n   function Meta (Item : in Instance) return T\n   is (Item.F_Meta);\n\n   function With_Meta (Builtin  : in Builtin_Ptr;\n                       Metadata : in T) return T\n   is\n      --  Builtin is not null and requires an immediate initialization.\n      Ref : constant Builtin_With_Meta_Ptr\n        := new Instance'(Garbage_Collected.Instance with\n                         F_Builtin => Builtin,\n                         F_Meta    => Metadata);\n   begin\n      Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n      return (Kind_Builtin_With_Meta, Ref);\n   end With_Meta;\n\n   function With_Meta (Builtin  : in Instance;\n                       Metadata : in T) return T\n   is (With_Meta (Builtin.F_Builtin, Metadata));\n\nend Types.Builtins;\n"
  },
  {
    "path": "impls/ada.2/types-builtins.ads",
    "content": "with Garbage_Collected;\n\npackage Types.Builtins is\n\n   --  Types.Mal.Builtin_Ptr is efficient and sufficient for most\n   --  purposes, as native function need no deallocation.  The type\n   --  below is only useful to add metadata to a built-in.\n\n   type Instance (<>) is abstract new Garbage_Collected.Instance with private;\n\n   function With_Meta (Builtin  : in Builtin_Ptr;\n                       Metadata : in T) return T with Inline;\n   function With_Meta (Builtin  : in Instance;\n                       Metadata : in T) return T with Inline;\n\n   function Meta (Item : in Instance) return T with Inline;\n   function Builtin (Item : in Instance) return Builtin_Ptr with Inline;\n\nprivate\n\n   type Instance is new Garbage_Collected.Instance with record\n      F_Builtin : Builtin_Ptr;\n      F_Meta    : T;\n   end record;\n\n   overriding procedure Keep_References (Object : in out Instance) with Inline;\n\nend Types.Builtins;\n"
  },
  {
    "path": "impls/ada.2/types-fns.adb",
    "content": "with Err;\npragma Warnings (Off, \"unit \"\"Types.Sequences\"\" is not referenced\");\nwith Types.Sequences;\npragma Warnings (On, \"unit \"\"Types.Sequences\"\" is not referenced\");\n\npackage body Types.Fns is\n\n   function Apply (Item : in Instance;\n                   Args : in T_Array) return T\n   is\n      Env : constant Envs.Ptr := Envs.New_Env (Outer => Item.F_Env);\n   begin\n      Env.all.Set_Binds (Binds => Item.F_Params.all.Data,\n                         Exprs => Args);\n      return Eval_Cb.all (Ast => Item.F_Ast,\n                          Env => Env);\n   end Apply;\n\n   function Ast (Item : in Instance) return T\n   is (Item.F_Ast);\n\n   function Env (Item : in Instance) return Envs.Ptr\n   is (Item.F_Env);\n\n   procedure Keep_References (Object : in out Instance) is\n   begin\n      Keep (Object.F_Ast);\n      Object.F_Params.all.Keep;\n      Object.F_Env.all.Keep;\n      Keep (Object.F_Meta);\n   end Keep_References;\n\n   function Meta (Item : in Instance) return T\n   is (Item.F_Meta);\n\n   function New_Function (Params   : in Sequence_Ptr;\n                          Ast      : in T;\n                          Env      : in Envs.Ptr;\n                          Metadata : in T            := Nil) return Fn_Ptr\n   is\n      --  Env and Params are not null and require an immediate\n      --  initialization.\n      Ref : constant Fn_Ptr\n        := new Instance'(Garbage_Collected.Instance with\n                         F_Ast    => Ast,\n                         F_Env    => Env,\n                         F_Meta   => Metadata,\n                         F_Params => Params);\n   begin\n      Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n      Err.Check ((for all P of Params.all.Data => P.Kind = Kind_Symbol),\n                 \"formal parameters must be symbols\");\n      return Ref;\n   end New_Function;\n\n   function Params (Item : in Instance) return Sequence_Ptr\n   is (Item.F_Params);\n\nend Types.Fns;\n"
  },
  {
    "path": "impls/ada.2/types-fns.ads",
    "content": "with Envs;\nwith Garbage_Collected;\n\npackage Types.Fns is\n\n   Eval_Cb : access function (Ast : in T;\n                              Env : in Envs.Ptr) return T;\n   --  The main program must register this global callback to the main\n   --  eval function before Apply is called.\n\n   type Instance (<>) is abstract new Garbage_Collected.Instance with private;\n\n   function New_Function (Params   : in Sequence_Ptr;\n                          Ast      : in T;\n                          Env      : in Envs.Ptr;\n                          Metadata : in T            := Nil) return Fn_Ptr\n     with Inline;\n   --  Raise an exception if Params contains something else than symbols.\n\n   function Params (Item : in Instance) return Sequence_Ptr\n     with Inline;\n   function Ast (Item : in Instance) return T with Inline;\n   --  Useful to print.\n\n   function Apply (Item : in Instance;\n                   Args : in T_Array) return T with Inline;\n   --  Duplicated in the step files because of TCO.\n\n   function Env (Item : in Instance) return Envs.Ptr with Inline;\n   --  Required for TCO, instead of Apply.\n\n   function Meta (Item : in Instance) return T with Inline;\n\nprivate\n\n   type Instance is new Garbage_Collected.Instance\n     with record\n        F_Ast    : T;\n        F_Env    : Envs.Ptr;\n        F_Meta   : T;\n        F_Params : Sequence_Ptr;\n   end record;\n   overriding procedure Keep_References (Object : in out Instance) with Inline;\n\nend Types.Fns;\n"
  },
  {
    "path": "impls/ada.2/types-maps.adb",
    "content": "with Err;\nwith Types.Sequences;\nwith Types.Strings;\n\npackage body Types.Maps is\n\n   use type HM.Map;\n\n   function Assoc (Initial : in HM.Map;\n                   Bind    : in T_Array) return T;\n\n   function Constructor return Map_Ptr with Inline;\n\n   ----------------------------------------------------------------------\n\n   function \"=\" (Left, Right : in Instance) return Boolean\n   is (Left.Data = Right.Data);\n\n   function Assoc (Initial : in HM.Map;\n                   Bind    : in T_Array) return T\n   is\n   begin\n      Err.Check (Bind'Length mod 2 = 0, \"expected an even bind count\");\n      declare\n         Len : constant Natural := Bind'Length / 2;\n         Ref : constant Map_Ptr := Constructor;\n      begin\n         Ref.all.Data := Initial;\n         for I in 0 .. Len - 1 loop\n            Ref.all.Data.Include (Bind (Bind'First + 2 * I),\n                                  Bind (Bind'First + 2 * I + 1));\n         end loop;\n         return (Kind_Map, Ref);\n      end;\n   end Assoc;\n\n   function Assoc (Args : in T_Array) return T is\n   begin\n      Err.Check (0 < Args'Length and then Args (Args'First).Kind = Kind_Map,\n                 \"first parameter must be a map\");\n      return Assoc (Args (Args'First).Map.all.Data,\n                    Args (Args'First + 1 .. Args'Last));\n   end Assoc;\n\n   function Constructor return Map_Ptr is\n      Ref : constant Map_Ptr := new Instance;\n   begin\n      Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n      return Ref;\n   end Constructor;\n\n   function Contains (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 2 and then Args (Args'First).Kind = Kind_Map,\n                 \"expected a map then a key\");\n      return (Kind_Boolean,\n              Args (Args'First).Map.all.Data.Contains (Args (Args'Last)));\n   end Contains;\n\n   function Dissoc (Args : in T_Array) return T is\n   begin\n      Err.Check (0 < Args'Length and then Args (Args'First).Kind = Kind_Map,\n                 \"expected a map then keys\");\n      declare\n         Ref : constant Map_Ptr := Constructor;\n      begin\n         Ref.all.Data := Args (Args'First).Map.all.Data;\n         for I in Args'First + 1 .. Args'Last loop\n            Ref.all.Data.Exclude (Args (I));\n            --  This call checks the kind of the key.\n         end loop;\n         return (Kind_Map, Ref);\n      end;\n   end Dissoc;\n\n   function Element (Position  : in Cursor) return T\n   is (HM.Element (HM.Cursor (Position)));\n\n   function First (Container : in Instance) return Cursor\n   is (Cursor (Container.Data.First));\n\n   function Get (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 2, \"expected 2 parameters\");\n      case Args (Args'First).Kind is\n         when Kind_Nil =>\n            Err.Check (Args (Args'Last).Kind in Kind_Key,\n                       \"key must be a keyword or string\");\n            return Nil;\n         when Kind_Map =>\n            declare\n               Position : constant HM.Cursor\n                 := Args (Args'First).Map.all.Data.Find (Args (Args'Last));\n            begin\n               if HM.Has_Element (Position) then\n                  return HM.Element (Position);\n               else\n                  return Nil;\n               end if;\n            end;\n         when others =>\n            Err.Raise_With  (\"parameter 1 must be nil or a map\");\n      end case;\n   end Get;\n\n   function Has_Element (Position : in Cursor) return Boolean\n   is (HM.Has_Element (HM.Cursor (Position)));\n\n   function Hash (Item : in T) return Ada.Containers.Hash_Type is\n   begin\n      Err.Check (Item.Kind in Kind_Key, \"keys must be keywords or strings\");\n      return Strings.Hash (Item.Str);\n   end Hash;\n\n   function Hash_Map (Args : in T_Array) return T\n   is (Assoc (HM.Empty_Map, Args));\n\n   procedure Keep_References (Object : in out Instance) is\n   begin\n      for Position in Object.Data.Iterate loop\n         Keep (HM.Key (Position));\n         Keep (HM.Element (Position));\n      end loop;\n      Keep (Object.F_Meta);\n   end Keep_References;\n\n   function Key (Position  : in Cursor) return T\n   is (HM.Key (HM.Cursor (Position)));\n\n   function Keys (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_Map,\n                 \"expected a map\");\n      declare\n         A1       : HM.Map renames Args (Args'First).Map.all.Data;\n         Ref      : constant Sequence_Ptr\n           := Sequences.Constructor (Natural (A1.Length));\n         I        : Positive := 1;\n      begin\n         for Position in A1.Iterate loop\n            Ref.all.Data (I) := HM.Key (Position);\n            I := I + 1;\n         end loop;\n         return (Kind_List, Ref);\n      end;\n   end Keys;\n\n   function Meta (Container : in Instance) return T\n   is (Container.F_Meta);\n\n   procedure Next (Position : in out Cursor) is\n   begin\n      HM.Next (HM.Cursor (Position));\n   end Next;\n\n   function New_Map (Source : in Instance) return T\n   is\n      Ref : constant Map_Ptr := Constructor;\n   begin\n      Ref.all.Data := Source.Data;\n      return (Kind_Map, Ref);\n   end New_Map;\n\n   procedure Replace_Element (Container : in out Instance;\n                              Position  : in     Cursor;\n                              New_Item  : in     T)\n   is\n   begin\n      Container.Data.Replace_Element (HM.Cursor (Position), New_Item);\n   end Replace_Element;\n\n   function Vals (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1 and then Args (Args'First).Kind = Kind_Map,\n                 \"expected a map\");\n      declare\n         A1 : HM.Map renames Args (Args'First).Map.all.Data;\n         R  : constant Sequence_Ptr\n           := Sequences.Constructor (Natural (A1.Length));\n         I  : Positive              := 1;\n      begin\n         for Element of A1 loop\n            R.all.Data (I) := Element;\n            I := I + 1;\n         end loop;\n         return (Kind_List, R);\n      end;\n   end Vals;\n\n   function With_Meta (Container : in Instance;\n                       Metadata  : in T) return T\n   is\n      Ref : constant Map_Ptr := Constructor;\n   begin\n      Ref.all.Data := Container.Data;\n      Ref.all.F_Meta := Metadata;\n      return (Kind_Map, Ref);\n   end With_Meta;\n\nend Types.Maps;\n"
  },
  {
    "path": "impls/ada.2/types-maps.ads",
    "content": "private with Ada.Containers.Hashed_Maps;\n\nwith Garbage_Collected;\n\npackage Types.Maps is\n\n   --  All function receiving a key check that its kind is keyword or\n   --  string.\n\n   type Instance (<>) is abstract new Garbage_Collected.Instance with private;\n\n   --  Built-in functions.\n   function Assoc    (Args : in T_Array) return T;\n   function Contains (Args : in T_Array) return T;\n   function Dissoc   (Args : in T_Array) return T;\n   function Get      (Args : in T_Array) return T;\n   function Hash_Map (Args : in T_Array) return T;\n   function Keys     (Args : in T_Array) return T;\n   function Vals     (Args : in T_Array) return T;\n\n   function \"=\" (Left, Right : in Instance) return Boolean with Inline;\n\n   --  Used to print each element of a map.\n   type Cursor (<>) is limited private;\n   function Has_Element (Position : in Cursor) return Boolean with Inline;\n   function Key (Position  : in Cursor) return T with Inline;\n   function Element (Position  : in Cursor) return T with Inline;\n   function First (Container : in Instance) return Cursor with Inline;\n   procedure Next (Position : in out Cursor) with Inline;\n\n   --  Used to evaluate each element of a map.\n   function New_Map (Source : in Instance) return T with Inline;\n   procedure Replace_Element (Container : in out Instance;\n                              Position  : in     Cursor;\n                              New_Item  : in     T) with Inline;\n\n   function Meta (Container : in Instance) return T with Inline;\n   function With_Meta (Container : in Instance;\n                       Metadata  : in T) return T with Inline;\n\nprivate\n\n   function Hash (Item : in T) return Ada.Containers.Hash_Type with Inline;\n   --  This function also checks the kind of the key, and raise an\n   --  error in case of problem.\n\n   package HM is new Ada.Containers.Hashed_Maps (Key_Type        => T,\n                                                 Element_Type    => T,\n                                                 Hash            => Hash,\n                                                 Equivalent_Keys => \"=\",\n                                                 \"=\"             => \"=\");\n\n   type Instance is new Garbage_Collected.Instance with record\n      Data   : HM.Map;\n      F_Meta : T;\n   end record;\n\n   overriding procedure Keep_References (Object : in out Instance) with Inline;\n\n   type Cursor is new HM.Cursor;\n\nend Types.Maps;\n"
  },
  {
    "path": "impls/ada.2/types-sequences.adb",
    "content": "with Err;\nwith Types.Fns;\nwith Types.Builtins;\n\npackage body Types.Sequences is\n\n   function \"=\" (Left, Right : in Instance) return Boolean is\n      --  Should become Left.all.Data = Right.all.Data when\n      --  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89178 is fixed.\n   begin\n      return Left.Length = Right.Length\n        and then\n        (for all I in 1 .. Left.Data'Length => Left.Data (I) = Right.Data (I));\n   end \"=\";\n\n   function Concat (Args : in T_Array) return T is\n      Sum   : Natural  := 0;\n      First : Positive := 1;\n      Last  : Natural;\n   begin\n      Err.Check ((for all A of Args => A.Kind in Kind_Sequence),\n                 \"expected sequences\");\n      for Arg of Args loop\n         Sum := Sum + Arg.Sequence.all.Data'Length;\n      end loop;\n      declare\n         Ref : constant Sequence_Ptr := Constructor (Sum);\n      begin\n         for Arg of Args loop\n            Last := First - 1 + Arg.Sequence.all.Data'Last;\n            Ref.all.Data (First .. Last) := Arg.Sequence.all.Data;\n            First := Last + 1;\n         end loop;\n         return (Kind_List, Ref);\n      end;\n   end Concat;\n\n   function Conj (Args : in T_Array) return T is\n   begin\n      Err.Check (0 < Args'Length, \"expected at least 1 parameter\");\n      case Args (Args'First).Kind is\n      when Kind_Sequence =>\n         declare\n            Data : T_Array renames Args (Args'First).Sequence.all.Data;\n            Last : constant Natural := Args'Length - 1 + Data'Length;\n            --  Avoid exceptions until Ref is controlled.\n            Ref  : constant Sequence_Ptr := Constructor (Last);\n         begin\n            if Args (Args'First).Kind = Kind_List then\n               for I in 1 .. Args'Length - 1 loop\n                  Ref.all.Data (I) := Args (Args'Last - I + 1);\n               end loop;\n               Ref.all.Data (Args'Length .. Last) := Data;\n               return (Kind_List, Ref);\n            else\n               Ref.all.Data := Data & Args (Args'First + 1 .. Args'Last);\n               return (Kind_Vector, Ref);\n            end if;\n         end;\n      when others =>\n         Err.Raise_With (\"parameter 1 must be a sequence\");\n      end case;\n   end Conj;\n\n   function Cons (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 2\n                   and then Args (Args'Last).Kind in Kind_Sequence,\n                 \"expected a value then a sequence\");\n      declare\n         Head : T renames Args (Args'First);\n         Tail : T_Array renames Args (Args'Last).Sequence.all.Data;\n         Ref  : constant Sequence_Ptr := Constructor (1 + Tail'Length);\n      begin\n         Ref.all.Data := Head & Tail;\n         return (Kind_List, Ref);\n      end;\n   end Cons;\n\n   function Constructor (Length : in Natural) return Sequence_Ptr is\n      Ref : constant Sequence_Ptr := new Instance (Length);\n   begin\n      Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n      return Ref;\n   end Constructor;\n\n   function Count (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      case Args (Args'First).Kind is\n      when Kind_Nil =>\n         return (Kind_Number, 0);\n      when Kind_Sequence =>\n         return (Kind_Number, Args (Args'First).Sequence.all.Data'Length);\n      when others =>\n         Err.Raise_With (\"parameter must be nil or a sequence\");\n      end case;\n   end Count;\n\n   function First (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      case Args (Args'First).Kind is\n      when Kind_Nil =>\n         return Nil;\n      when Kind_Sequence =>\n         declare\n            Data : T_Array renames Args (Args'First).Sequence.all.Data;\n         begin\n            if Data'Length = 0 then\n               return Nil;\n            else\n               return Data (Data'First);\n            end if;\n         end;\n      when others =>\n         Err.Raise_With (\"parameter must be nil or a sequence\");\n      end case;\n   end First;\n\n   function Is_Empty (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1\n                   and then Args (Args'First).Kind in Kind_Sequence,\n                 \"expected a sequence\");\n      return (Kind_Boolean, Args (Args'First).Sequence.all.Data'Length = 0);\n   end Is_Empty;\n\n   procedure Keep_References (Object : in out Instance) is\n   begin\n      Keep (Object.Meta);\n      for M of Object.Data loop\n         Keep (M);\n      end loop;\n   end Keep_References;\n\n   function List (Args : in T_Array) return T\n   is\n      Ref : constant Sequence_Ptr := Constructor (Args'Length);\n   begin\n      Ref.all.Data := Args;\n      return (Kind_List, Ref);\n   end List;\n\n   function Map (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 2\n                   and then Args (Args'Last).Kind in Kind_Sequence,\n                 \"expected a function then a sequence\");\n      declare\n         F   : T renames Args (Args'First);\n         Src : T_Array renames Args (Args'Last).Sequence.all.Data;\n         Ref : constant Sequence_Ptr := Constructor (Src'Length);\n      begin\n         case F.Kind is\n            when Kind_Builtin =>\n               for I in Src'Range loop\n                  Ref.all.Data (I) := F.Builtin.all (Src (I .. I));\n               end loop;\n            when Kind_Builtin_With_Meta =>\n               for I in Src'Range loop\n                  Ref.all.Data (I)\n                    := F.Builtin_With_Meta.all.Builtin.all (Src (I .. I));\n               end loop;\n            when Kind_Fn =>\n               for I in Src'Range loop\n                  Ref.all.Data (I) := F.Fn.all.Apply (Src (I .. I));\n               end loop;\n            when others =>\n               Err.Raise_With (\"parameter 1 must be a function\");\n         end case;\n         return (Kind_List, Ref);\n      end;\n   end Map;\n\n   function Nth (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 2\n                   and then Args (Args'First).Kind in Kind_Sequence\n                   and then Args (Args'Last).Kind = Kind_Number,\n                 \"expected a sequence then a number\");\n      declare\n         L : T_Array renames Args (Args'First).Sequence.all.Data;\n         I : constant Integer := Args (Args'Last).Number + 1;\n      begin\n         Err.Check (I in L'Range, \"index out of bounds\");\n         return L (I);\n      end;\n   end Nth;\n\n   function Rest (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1, \"expected 1 parameter\");\n      case Args (Args'First).Kind is\n      when Kind_Nil =>\n         return (Kind_List, Constructor (0));\n      when Kind_Sequence =>\n         declare\n            A1  : T_Array renames Args (Args'First).Sequence.all.Data;\n            Ref : constant Sequence_Ptr\n              := Constructor (Integer'Max (0, A1'Length - 1));\n         begin\n            Ref.all.Data := A1 (A1'First + 1 .. A1'Last);\n            return (Kind_List, Ref);\n         end;\n      when others =>\n         Err.Raise_With (\"parameter must be nil or a sequence\");\n      end case;\n   end Rest;\n\n   function Vec (Args : in T_Array) return T is\n   begin\n      Err.Check (Args'Length = 1\n                   and then Args (Args'First).Kind in Kind_Sequence,\n                 \"expects a sequence\");\n      return (Kind_Vector, Args (Args'First).Sequence);\n   end Vec;\n\n   function Vector (Args : in T_Array) return T\n   is\n      Ref : constant Sequence_Ptr := Constructor (Args'Length);\n   begin\n      Ref.all.Data := Args;\n      return (Kind_Vector, Ref);\n   end Vector;\n\nend Types.Sequences;\n"
  },
  {
    "path": "impls/ada.2/types-sequences.ads",
    "content": "with Garbage_Collected;\n\npackage Types.Sequences is\n\n   --  Hiding the implementation would either cause a significative\n   --  performance hit (the compiler performs better optimization with\n   --  explicit arrays) or a convoluted interface (demonstrated for\n   --  strings and maps, where the balance is different).\n\n   type Instance (Length : Natural) is new Garbage_Collected.Instance with\n      record\n         Meta : T;\n         Data : T_Array (1 .. Length);\n      end record;\n\n   --  Built-in functions.\n   function Concat   (Args : in T_Array) return T;\n   function Conj     (Args : in T_Array) return T;\n   function Cons     (Args : in T_Array) return T;\n   function Count    (Args : in T_Array) return T;\n   function First    (Args : in T_Array) return T;\n   function Is_Empty (Args : in T_Array) return T;\n   function List     (Args : in T_Array) return T;\n   function Map      (Args : in T_Array) return T;\n   function Nth      (Args : in T_Array) return T;\n   function Rest     (Args : in T_Array) return T;\n   function Vec      (Args : in T_Array) return T;\n   function Vector   (Args : in T_Array) return T;\n\n   --  New instances must be created via this constructor.\n   function Constructor (Length : in Natural) return Sequence_Ptr with Inline;\n\n   --  Helper for Types.\"=\".\n   function \"=\" (Left, Right : in Instance) return Boolean;\n\nprivate\n\n   overriding procedure Keep_References (Object : in out Instance) with Inline;\n\nend Types.Sequences;\n"
  },
  {
    "path": "impls/ada.2/types-strings.adb",
    "content": "with Ada.Strings.Hash;\n\npackage body Types.Strings is\n\n   function \"=\" (Left  : in Instance;\n                 Right : in String) return Boolean\n   is (Left.Data = Right);\n\n   function Alloc (Data : in String) return String_Ptr is\n      Ref : constant String_Ptr := new Instance (Data'Length);\n   begin\n      Garbage_Collected.Register (Garbage_Collected.Pointer (Ref));\n      Ref.all.Data := Data;\n      return Ref;\n   end Alloc;\n\n   function Hash (Item : in String_Ptr) return Ada.Containers.Hash_Type\n   is (Ada.Strings.Hash (Item.all.Data));\n\n   procedure Query_Element\n     (Container : in Instance;\n      Process   : not null access procedure (Element : in String))\n   is\n   begin\n      Process.all (Container.Data);\n   end Query_Element;\n\n   function Same_Contents (Left, Right : in String_Ptr) return Boolean\n   is (Left = Right or else Left.all.Data = Right.all.Data);\n\n   function To_String (Container : in Instance) return String\n   is (Container.Data);\n\nend Types.Strings;\n"
  },
  {
    "path": "impls/ada.2/types-strings.ads",
    "content": "with Ada.Containers;\n\nwith Garbage_Collected;\n\npackage Types.Strings is\n\n   ------------------------------------\n   --  Keywords, Strings and Symbols --\n   ------------------------------------\n\n   --  Tests seem to show that manual garbage collection is faster\n   --  than reference counting in Ada.Strings.Unbounded, probably\n   --  because we know that the values will never change.\n\n   --  Also, maintaining a global structure in order to avoid similar\n   --  symbol allocations does not seem to improve performances.\n\n   type Instance (<>) is abstract new Garbage_Collected.Instance with private;\n\n   function Alloc (Data : in String) return String_Ptr\n     with Inline;\n\n   function \"=\" (Left  : in Instance;\n                 Right : in String) return Boolean\n     with Inline;\n\n   --  This kind of accessor is more efficient than a function\n   --  returning an array.\n   procedure Query_Element\n     (Container : in Instance;\n      Process   : not null access procedure (Element : in String));\n\n   --  These methods could be implemented with Query_Element,\n   --  but we want to optimize Envs.Get.\n   function Hash (Item : in String_Ptr) return Ada.Containers.Hash_Type\n     with Inline;\n   function Same_Contents (Left, Right : in String_Ptr) return Boolean\n     with Inline;\n\n   --  When readability is more important than copying a string.\n   function To_String (Container : in Instance) return String with Inline;\n\nprivate\n\n   type Instance (Last : Natural) is new Garbage_Collected.Instance with record\n      Data : String (1 .. Last);\n   end record;\n\nend Types.Strings;\n"
  },
  {
    "path": "impls/ada.2/types.adb",
    "content": "pragma Warnings (Off, \"no entities of \"\"Types.*\"\" are referenced\");\nwith Types.Atoms;\nwith Types.Builtins;\nwith Types.Fns;\nwith Types.Maps;\nwith Types.Sequences;\npragma Warnings (On, \"no entities of \"\"Types.*\"\" are referenced\");\nwith Types.Strings;\n\npackage body Types is\n\n   function \"=\" (Left, Right : in T) return Boolean\n   is (case Left.Kind is\n      when Kind_Nil =>\n         Right.Kind = Kind_Nil,\n      when Kind_Boolean =>\n         Right.Kind = Kind_Boolean\n         and then Left.Ada_Boolean = Right.Ada_Boolean,\n      when Kind_Number =>\n         Right.Kind = Kind_Number and then Left.Number = Right.Number,\n      --  Here comes the part that differs from the predefined equality.\n      when Kind_Key | Kind_Symbol =>\n         Right.Kind = Left.Kind\n         and then Strings.Same_Contents (Left.Str, Right.Str),\n      when Kind_Sequence =>\n         Right.Kind in Kind_Sequence\n         and then (Left.Sequence = Right.Sequence\n            or else Sequences.\"=\" (Left.Sequence.all, Right.Sequence.all)),\n      when Kind_Map =>\n         Right.Kind = Kind_Map\n         and then (Left.Map = Right.Map\n            or else Maps.\"=\" (Left.Map.all, Right.Map.all)),\n      --  Also, comparing functions is an interesting problem.\n      when others =>\n         False);\n\n   procedure Keep (Object : in T) is\n      --  No dynamic dispatching happens here.\n   begin\n      case Object.Kind is\n      when Kind_Nil | Kind_Boolean | Kind_Number | Kind_Builtin =>\n         null;\n      when Kind_Key | Kind_Symbol =>\n         Object.Str.all.Keep;\n      when Kind_Atom =>\n         Object.Atom.all.Keep;\n      when Kind_Sequence =>\n         Object.Sequence.all.Keep;\n      when Kind_Map =>\n         Object.Map.all.Keep;\n      when Kind_Builtin_With_Meta =>\n         Object.Builtin_With_Meta.all.Keep;\n      when Kind_Fn | Kind_Macro =>\n         Object.Fn.all.Keep;\n      end case;\n   end Keep;\n\n   function To_Boolean (Form : T) return Boolean is\n     (case Form.Kind is\n      when Kind_Nil     => False,\n      when Kind_Boolean => Form.Ada_Boolean,\n      when others       => True);\n\nend Types;\n"
  },
  {
    "path": "impls/ada.2/types.ads",
    "content": "limited with Types.Atoms;\nlimited with Types.Builtins;\nlimited with Types.Fns;\nlimited with Types.Maps;\nlimited with Types.Sequences;\nlimited with Types.Strings;\n\npackage Types is\n\n   --  A type with a default value for the discriminant is the Ada\n   --  equivalent of a C union. It uses a fixed size, and allows\n   --  efficient arrays. A class hierarchy would make this impossible,\n   --  for little gain.\n   --  Native types may seem to consume too much memory, but\n   --  1/ they require no allocation/deallocation.\n   --  2/ the overhead would actually be higher with an intermediate\n   --     reference (the size of the pointer plus the size of the native\n   --     type, while an union uses the minimum of both and a single\n   --     memory area ).\n\n   --  The idea is inspired from the Haskell and OCaml interpreters,\n   --  which use a bit to distinguish pointers from integers.  Ada\n   --  allows to specify the bit position of each component, but\n   --  generating such architecture-dependent definitions seems a lot\n   --  of work for MAL.\n\n   --  The Ada tradition is to give explicit names to types, but this\n   --  one will be used very often.\n\n   type Kind_Type is\n     (Kind_Nil,\n      Kind_Atom,\n      Kind_Boolean,\n      Kind_Number,\n      Kind_Symbol,\n      Kind_Keyword, Kind_String,\n      Kind_List, Kind_Vector,\n      Kind_Map,\n      Kind_Macro, Kind_Fn, Kind_Builtin_With_Meta, Kind_Builtin);\n\n   subtype Kind_Key      is Kind_Type range Kind_Keyword .. Kind_String;\n   subtype Kind_Sequence is Kind_Type range Kind_List .. Kind_Vector;\n   subtype Kind_Function is Kind_Type range Kind_Fn .. Kind_Builtin;\n\n   type T;\n   type T_Array;\n   type Atom_Ptr is not null access Atoms.Instance;\n   type Builtin_Ptr is not null access function (Args : in T_Array) return T;\n   type Builtin_With_Meta_Ptr is not null access Builtins.Instance;\n   type Fn_Ptr is not null access Fns.Instance;\n   type Map_Ptr is not null access Maps.Instance;\n   type Sequence_Ptr is not null access Sequences.Instance;\n   type String_Ptr is not null access Strings.Instance;\n\n   type T (Kind : Kind_Type := Kind_Nil) is record\n      case Kind is\n      when Kind_Nil =>\n         null;\n      when Kind_Boolean =>\n         Ada_Boolean                 : Boolean;\n      when Kind_Number =>\n         Number                      : Integer;\n      when Kind_Atom =>\n         Atom                        : Atom_Ptr;\n      when Kind_Key | Kind_Symbol =>\n         Str                         : String_Ptr;\n      when Kind_Sequence =>\n         Sequence                    : Sequence_Ptr;\n      when Kind_Map =>\n         Map                         : Map_Ptr;\n      when Kind_Builtin =>\n         Builtin                     : Builtin_Ptr;\n      when Kind_Builtin_With_Meta =>\n         Builtin_With_Meta           : Builtin_With_Meta_Ptr;\n      when Kind_Fn | Kind_Macro =>\n         Fn                          : Fn_Ptr;\n      end case;\n   end record;\n\n   --  Useful for recursive automatic definition of equality for\n   --  composite types like the array type below.\n   function \"=\" (Left, Right : in T) return Boolean with Inline;\n\n   Nil : constant T := (Kind => Kind_Nil);\n\n   function To_Boolean (Form : T) return Boolean with Inline;\n\n   procedure Keep (Object : in T) with Inline;\n\n   type T_Array is array (Positive range <>) of T;\n\nend Types;\n"
  },
  {
    "path": "impls/awk/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# GNU Awk\nRUN apt-get -y install gawk\n"
  },
  {
    "path": "impls/awk/Makefile",
    "content": "SOURCES_BASE = types.awk reader.awk printer.awk\nSOURCES_LISP = env.awk core.awk stepA_mal.awk\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.awk mal\n\nmal.awk: $(SOURCES)\n\techo 'arbitrary_long_name==0 \"exec\" \"/usr/bin/gawk\" \"-O\" \"-f\" \"$$0\" \"$$@\"' > $@\n\tcat $+ | grep -v \"^@include \" >> $@\n\nmal: mal.awk\n\techo '#!/bin/sh' > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.awk mal\n"
  },
  {
    "path": "impls/awk/core.awk",
    "content": "@load \"readfile\"\n@load \"time\"\n\nfunction core_eq_sub(lhs, rhs,    i, len)\n{\n\tif (lhs ~ /^[([]/ && rhs ~ /^[([]/) {\n\t\tlhs = substr(lhs, 2)\n\t\trhs = substr(rhs, 2)\n\t\tlen = types_heap[lhs][\"len\"]\n\t\tif (len != types_heap[rhs][\"len\"]) {\n\t\t\treturn 0\n\t\t}\n\t\tfor (i = 0; i < len; ++i) {\n\t\t\tif (!core_eq_sub(types_heap[lhs][i], types_heap[rhs][i])) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t}\n\t\treturn 1\n\t} else if (lhs ~ /^\\{/ && rhs ~ /^\\{/) {\n\t\tlhs = substr(lhs, 2)\n\t\trhs = substr(rhs, 2)\n\t\tif ( length(types_heap[lhs]) - (\"meta\" in types_heap[lhs]) != \\\n\t\t\tlength(types_heap[rhs]) - (\"meta\" in types_heap[rhs]) )\n\t\t{\n\t\t\treturn 0\n\t\t}\n\t\tfor (i in types_heap[lhs]) {\n\t\t\tif ( i != \"meta\" &&\n\t\t\t\ttypes_heap[lhs][i] ~ /^[\"':+#([{?&$%]/ &&\n\t\t\t\t!core_eq_sub(types_heap[lhs][i], types_heap[rhs][i])) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t}\n\t\treturn 1\n\t} else {\n\t\treturn lhs == rhs\n\t}\n}\n\nfunction core_eq(idx)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '='. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn core_eq_sub(types_heap[idx][1], types_heap[idx][2]) ? \"#true\" : \"#false\"\n}\n\nfunction core_throw(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'throw'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn \"!\" types_addref(types_heap[idx][1])\n}\n\n\n\nfunction core_nilp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'nil?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] == \"#nil\" ? \"#true\" : \"#false\"\n}\n\nfunction core_truep(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'true?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] == \"#true\" ? \"#true\" : \"#false\"\n}\n\nfunction core_falsep(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'false?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] == \"#false\" ? \"#true\" : \"#false\"\n}\n\nfunction core_stringp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'string?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^\"/ ? \"#true\" : \"#false\"\n}\n\nfunction core_symbol(idx,    str)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'symbol'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tstr = types_heap[idx][1]\n\tif (str !~ /^\"/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'symbol'. Expects string, supplied \" types_typename(str) \".\"\n\t}\n\treturn \"'\" substr(str, 2)\n}\n\nfunction core_symbolp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'symbol?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^'/ ? \"#true\" : \"#false\"\n}\n\nfunction core_keyword(idx,    str)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'keyword'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tstr = types_heap[idx][1]\n\tswitch (str) {\n\tcase /^:/:\n\t\treturn str\n\tcase /^\"/:\n\t\treturn \"::\" substr(str, 2)\n\t}\n\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'keyword'. Expects string or keyword, supplied \" types_typename(str) \".\"\n}\n\nfunction core_keywordp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'keyword?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^:/ ? \"#true\" : \"#false\"\n}\n\nfunction core_numberp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'number?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^\\+/ ? \"#true\" : \"#false\"\n}\n\nfunction core_fnp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'fn?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tf = types_heap[idx][1]\n\treturn f ~ /^[$&%]/ && !types_heap[substr(f, 2)][\"is_macro\"] ? \"#true\" : \"#false\"\n}\n\nfunction core_macrop(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'macro?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tf = types_heap[idx][1]\n\treturn f ~ /^\\$/ && types_heap[substr(f, 2)][\"is_macro\"] ? \"#true\" : \"#false\"\n}\n\n\n\nfunction core_pr_str(idx,    i, len, result)\n{\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\tresult = result printer_pr_str(types_heap[idx][i], 1) \" \"\n\t}\n\treturn \"\\\"\" substr(result, 1, length(result) - 1)\n}\n\nfunction core_str(idx,    i, len, result)\n{\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\tresult = result printer_pr_str(types_heap[idx][i], 0)\n\t}\n\treturn \"\\\"\" result\n}\n\nfunction core_prn(idx,    i, len, result)\n{\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\tresult = result printer_pr_str(types_heap[idx][i], 1) \" \"\n\t}\n\tprint substr(result, 1, length(result) - 1)\n\treturn \"#nil\"\n}\n\nfunction core_println(idx,    i, len, result)\n{\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\tresult = result printer_pr_str(types_heap[idx][i], 0) \" \"\n\t}\n\tprint substr(result, 1, length(result) - 1)\n\treturn \"#nil\"\n}\n\nfunction core_read_string(idx,    str)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'read-string'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tstr = types_heap[idx][1]\n\tif (str !~ /^\"/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'read-string'. Expects string, supplied \" types_typename(str) \".\"\n\t}\n\treturn reader_read_str(substr(str, 2))\n}\n\nfunction core_readline(idx,    prompt, var)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'readline'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tprompt = types_heap[idx][1]\n\tif (prompt !~ /^\"/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'readline'. Expects string, supplied \" types_typename(prompt) \".\"\n\t}\n\tprintf(\"%s\", printer_pr_str(prompt, 0))\n\treturn getline var <= 0 ? \"#nil\" : \"\\\"\" var\n}\n\nfunction core_slurp(idx,    filename, str)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'slurp'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tfilename = types_heap[idx][1]\n\tif (filename !~ /^\"/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'slurp'. Expects string, supplied \" types_typename(filename) \".\"\n\t}\n\tstr = readfile(substr(filename, 2))\n\tif (str == \"\" && ERRNO != \"\") {\n\t\treturn \"!\\\"cannot read file '\" filename \"', ERRNO = \" ERRNO\n\t}\n\treturn \"\\\"\" str\n}\n\n\n\nfunction core_lt(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '<'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '<'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '<'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn substr(lhs, 2) + 0 < substr(rhs, 2) + 0 ? \"#true\" : \"#false\"\n}\n\nfunction core_le(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '<='. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '<='. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '<='. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn substr(lhs, 2) + 0 <= substr(rhs, 2) + 0 ? \"#true\" : \"#false\"\n}\n\nfunction core_gt(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '>'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '>'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '>'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn substr(lhs, 2) + 0 > substr(rhs, 2) + 0 ? \"#true\" : \"#false\"\n}\n\nfunction core_ge(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '>='. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '>='. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '>='. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn substr(lhs, 2) + 0 >= substr(rhs, 2) + 0 ? \"#true\" : \"#false\"\n}\n\nfunction core_add(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '+'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '+'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '+'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) + substr(rhs, 2))\n}\n\nfunction core_subtract(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '-'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '-'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '-'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) - substr(rhs, 2))\n}\n\nfunction core_multiply(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '*'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '*'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '*'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) * substr(rhs, 2))\n}\n\nfunction core_divide(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '/'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '/'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '/'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" int(substr(lhs, 2) / substr(rhs, 2))\n}\n\nfunction core_time_ms(idx)\n{\n\tif (types_heap[idx][\"len\"] != 1) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'time-ms'. Expects no arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn \"+\" int(gettimeofday() * 1000)\n}\n\n\n\nfunction core_list(idx,    new_idx, len, i)\n{\n\tnew_idx = types_allocate()\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\ttypes_addref(types_heap[new_idx][i - 1] = types_heap[idx][i])\n\t}\n\ttypes_heap[new_idx][\"len\"] = len - 1\n\treturn \"(\" new_idx\n}\n\nfunction core_listp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'list?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^\\(/ ? \"#true\" : \"#false\"\n}\n\nfunction core_vector(idx,    new_idx, len, i)\n{\n\tnew_idx = types_allocate()\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\ttypes_addref(types_heap[new_idx][i - 1] = types_heap[idx][i])\n\t}\n\ttypes_heap[new_idx][\"len\"] = len - 1\n\treturn \"[\" new_idx\n}\n\nfunction core_vectorp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'vector?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^\\[/ ? \"#true\" : \"#false\"\n}\n\nfunction core_hash_map(idx,    len, new_idx, i, key)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len % 2 != 1) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'hash-map'. Expects even number of arguments, supplied \" (len - 1) \".\"\n\t}\n\tnew_idx = types_allocate()\n\tfor (i = 1; i < len; i += 2) {\n\t\tkey = types_heap[idx][i]\n\t\tif (key !~ /^[\":]/) {\n\t\t\ttypes_release(\"{\" new_idx)\n\t\t\treturn \"!\\\"Incompatible type for key argument of builtin function 'hash-map'. Expects string or keyword, supplied \" types_typename(key) \".\"\n\t\t}\n\t\tif (key in types_heap[new_idx]) {\n\t\t\ttypes_release(types_heap[new_idx][key])\n\t\t}\n\t\ttypes_addref(types_heap[new_idx][key] = types_heap[idx][i + 1])\n\t}\n\treturn \"{\" new_idx\n}\n\nfunction core_mapp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'map?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^\\{/ ? \"#true\" : \"#false\"\n}\n\nfunction core_assoc(idx,    len, map, i, key, add_list, new_idx, map_idx)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len % 2 != 0) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'assoc'. Expects odd number of arguments, supplied \" (len - 1) \".\"\n\t}\n\tmap = types_heap[idx][1]\n\tif (map !~ /^\\{/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'assoc'. Expects hash-map, supplied \" types_typename(map) \".\"\n\t}\n\tfor (i = 2; i < len; i += 2) {\n\t\tkey = types_heap[idx][i]\n\t\tif (key !~ /^[\":]/) {\n\t\t\treturn \"!\\\"Incompatible type for key argument of builtin function 'assoc'. Expects string or keyword, supplied \" types_typename(key) \".\"\n\t\t}\n\t\tadd_list[key] = types_heap[idx][i + 1]\n\t}\n\tnew_idx = types_allocate()\n\tmap_idx = substr(map, 2)\n\tfor (key in types_heap[map_idx]) {\n\t\tif (key ~ /^[\":]|^meta$/ && !(key in add_list)) {\n\t\t\ttypes_addref(types_heap[new_idx][key] = types_heap[map_idx][key])\n\t\t}\n\t}\n\tfor (key in add_list) {\n\t\ttypes_addref(types_heap[new_idx][key] = add_list[key])\n\t}\n\treturn \"{\" new_idx\n}\n\nfunction core_dissoc(idx,    len, map, i, key, del_list, new_idx, map_idx)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len < 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'dissoc'. Expects at least 1 argument, supplied \" (len - 1) \".\"\n\t}\n\tmap = types_heap[idx][1]\n\tif (map !~ /^\\{/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'dissoc'. Expects hash-map, supplied \" types_typename(map) \".\"\n\t}\n\tfor (i = 2; i < len; ++i) {\n\t\tkey = types_heap[idx][i]\n\t\tif (key !~ /^[\":]/) {\n\t\t\treturn \"!\\\"Incompatible type for key argument of builtin function 'dissoc'. Expects string or keyword, supplied \" types_typename(key) \".\"\n\t\t}\n\t\tdel_list[key] = \"1\"\n\t}\n\tnew_idx = types_allocate()\n\tmap_idx = substr(map, 2)\n\tfor (key in types_heap[map_idx]) {\n\t\tif (key ~ /^[\":]|^meta$/ && !(key in del_list)) {\n\t\t\ttypes_addref(types_heap[new_idx][key] = types_heap[map_idx][key])\n\t\t}\n\t}\n\treturn \"{\" new_idx\n}\n\nfunction core_get(idx,    map, key, map_idx)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'get'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tmap = types_heap[idx][1]\n\tif (map !~ /^\\{/ && map != \"#nil\") {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'get'. Expects hash-map or nil, supplied \" types_typename(map) \".\"\n\t}\n\tkey = types_heap[idx][2]\n\tif (key !~ /^[\":]/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function 'get'. Expects string or keyword, supplied \" types_typename(key) \".\"\n\t}\n\tif (map != \"#nil\" && key in types_heap[map_idx = substr(map, 2)]) {\n\t\treturn types_addref(types_heap[map_idx][key])\n\t} else {\n\t\treturn \"#nil\"\n\t}\n}\n\nfunction core_containsp(idx,    map, key)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'contains?'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tmap = types_heap[idx][1]\n\tif (map !~ /^\\{/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'contains?'. Expects hash-map, supplied \" types_typename(map) \".\"\n\t}\n\tkey = types_heap[idx][2]\n\tif (key !~ /^[\":]/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function 'contains?'. Expects string or keyword, supplied \" types_typename(key) \".\"\n\t}\n\treturn key in types_heap[substr(map, 2)] ? \"#true\" : \"#false\"\n}\n\nfunction core_keys(idx,    map, map_idx, new_idx, len, key)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'keys'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tmap = types_heap[idx][1]\n\tif (map !~ /^\\{/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'keys'. Expects hash-map, supplied \" types_typename(map) \".\"\n\t}\n\tmap_idx = substr(map, 2)\n\tnew_idx = types_allocate()\n\tlen = 0\n\tfor (key in types_heap[map_idx]) {\n\t\tif (key ~ /^[\":]/) {\n\t\t\ttypes_heap[new_idx][len++] = key\n\t\t}\n\t}\n\ttypes_heap[new_idx][\"len\"] = len\n\treturn \"(\" new_idx\n}\n\nfunction core_vals(idx,    map, map_idx, new_idx, len, key)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'vals'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tmap = types_heap[idx][1]\n\tif (map !~ /^\\{/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'vals'. Expects hash-map, supplied \" types_typename(map) \".\"\n\t}\n\tmap_idx = substr(map, 2)\n\tnew_idx = types_allocate()\n\tlen = 0\n\tfor (key in types_heap[map_idx]) {\n\t\tif (key ~ /^[\":]/) {\n\t\t\ttypes_addref(types_heap[new_idx][len++] = types_heap[map_idx][key])\n\t\t}\n\t}\n\ttypes_heap[new_idx][\"len\"] = len\n\treturn \"(\" new_idx\n}\n\n\n\nfunction core_sequentialp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'sequential?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^[([]/ ? \"#true\" : \"#false\"\n}\n\nfunction core_cons(idx,    lst, lst_idx, new_idx, len, i)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'cons'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlst = types_heap[idx][2]\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'cons'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t}\n\tlst_idx = substr(lst, 2)\n\tnew_idx = types_allocate()\n\ttypes_addref(types_heap[new_idx][0] = types_heap[idx][1])\n\tlen = types_heap[lst_idx][\"len\"]\n\tfor (i = 0; i < len; ++i) {\n\t\ttypes_addref(types_heap[new_idx][i + 1] = types_heap[lst_idx][i])\n\t}\n\ttypes_heap[new_idx][\"len\"] = len + 1\n\treturn \"(\" new_idx\n}\n\nfunction core_concat(idx,    new_idx, new_len, len, i, lst, lst_idx, lst_len, j)\n{\n\tnew_idx = types_allocate()\n\tnew_len = 0\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 1; i < len; ++i) {\n\t\tlst = types_heap[idx][i]\n\t\tif (lst !~ /^[([]/) {\n\t\t\ttypes_heap[new_idx][\"len\"] = new_len\n\t\t\ttypes_release(\"(\" new_idx)\n\t\t\treturn \"!\\\"Incompatible type for argument ' (i - 1) ' of builtin function 'concat'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t\t}\n\t\tlst_idx = substr(lst, 2)\n\t\tlst_len = types_heap[lst_idx][\"len\"]\n\t\tfor (j = 0; j < lst_len; ++j) {\n\t\t\ttypes_addref(types_heap[new_idx][new_len++] = types_heap[lst_idx][j])\n\t\t}\n\t}\n\ttypes_heap[new_idx][\"len\"] = new_len\n\treturn \"(\" new_idx\n}\n\nfunction core_vec(idx,    new_idx, len)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 2)\n\t\treturn \"!\\\"Invalid argument length for builtin function 'vec'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\tidx = types_heap[idx][1]\n\tif (idx !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'vec'. Expects list or vector, supplied \" types_typename(idx) \".\"\n\t}\n\tidx = substr(idx, 2)\n\tlen = types_heap[idx][\"len\"]\n\tnew_idx = types_allocate()\n\ttypes_heap[new_idx][\"len\"] = len\n\twhile (len--)\n\t\ttypes_addref(types_heap[new_idx][len] = types_heap[idx][len])\n\treturn \"[\" new_idx\n}\n\nfunction core_nth(idx,    lst, num, n, lst_idx)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'nth'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlst = types_heap[idx][1]\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'nth'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t}\n\tnum = types_heap[idx][2]\n\tif (num !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function 'nth'. Expects number, supplied \" types_typename(num) \".\"\n\t}\n\tn = substr(num, 2) + 0\n\tlst_idx = substr(lst, 2)\n\tif (n < 0 || types_heap[lst_idx][\"len\"] <= n) {\n\t\treturn \"!\\\"Index out of range. Sequence length is \" types_heap[lst_idx][\"len\"] \", supplied \" n \".\"\n\t}\n\treturn types_addref(types_heap[lst_idx][n])\n}\n\nfunction core_first(idx,    lst, lst_idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'first'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlst = types_heap[idx][1]\n\tif (lst == \"#nil\") {\n\t        return \"#nil\"\n\t}\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'first'. Expects list, vector or nil, supplied \" types_typename(lst) \".\"\n\t}\n\tlst_idx = substr(lst, 2)\n\treturn types_heap[lst_idx][\"len\"] == 0 ? \"#nil\" : types_addref(types_heap[lst_idx][0])\n}\n\nfunction core_rest(idx,    lst, lst_idx, lst_len, new_idx, i)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'rest'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlst = types_heap[idx][1]\n\tif (lst == \"#nil\") {\n\t        new_idx = types_allocate()\n\t        types_heap[new_idx][\"len\"] = 0\n\t        return \"(\" new_idx\n\t}\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'rest'. Expects list, vector or nil, supplied \" types_typename(lst) \".\"\n\t}\n\tlst_idx = substr(lst, 2)\n\tlst_len = types_heap[lst_idx][\"len\"]\n\tnew_idx = types_allocate()\n\tfor (i = 1; i < lst_len; ++i) {\n\t\ttypes_addref(types_heap[new_idx][i - 1] = types_heap[lst_idx][i])\n\t}\n\ttypes_heap[new_idx][\"len\"] = lst_len - 1\n\treturn \"(\" new_idx\n}\n\nfunction core_emptyp(idx,    lst)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'empty?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlst = types_heap[idx][1]\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'empty?'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t}\n\treturn types_heap[substr(lst, 2)][\"len\"] == 0 ? \"#true\" : \"#false\"\n}\n\nfunction core_count(idx,    lst)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'count'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlst = types_heap[idx][1]\n\tif (lst ~ /^[([]/) {\n\t\treturn \"+\" types_heap[substr(lst, 2)][\"len\"]\n\t}\n\tif (lst == \"#nil\") {\n\t\treturn \"+0\"\n\t}\n\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'count'. Expects list, vector or nil, supplied \" types_typename(lst) \".\"\n}\n\nfunction core_apply(idx,    len, f, lst, new_idx, i, lst_idx, lst_len, f_idx, env, ret)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len < 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'apply'. Expects at least 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tf = types_heap[idx][1]\n\tif (f !~ /^[$&%]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'apply'. Expects function, supplied \" types_typename(f) \".\"\n\t}\n\tlst = types_heap[idx][len - 1]\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument ' (len - 1) ' of builtin function 'apply'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t}\n\n\tnew_idx = types_allocate()\n\ttypes_addref(types_heap[new_idx][0] = f)\n\tfor (i = 2; i < len - 1; ++i) {\n\t\ttypes_addref(types_heap[new_idx][i - 1] = types_heap[idx][i])\n\t}\n\tlst_idx = substr(lst, 2)\n\tlst_len = types_heap[lst_idx][\"len\"]\n\tfor (i = 0; i < lst_len; ++i) {\n\t\ttypes_addref(types_heap[new_idx][len + i - 2] = types_heap[lst_idx][i])\n\t}\n\ttypes_heap[new_idx][\"len\"] = len + lst_len - 2\n\n\tf_idx = substr(f, 2)\n\tswitch (f) {\n\tcase /^\\$/:\n\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], new_idx)\n\t\ttypes_release(\"(\" new_idx)\n\t\tif (env ~ /^!/) {\n\t\t\treturn env\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[f_idx][\"body\"]), env)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^%/:\n\t\tf_idx = types_heap[f_idx][\"func\"]\n\tcase /^&/:\n\t\tret = @f_idx(new_idx)\n\t\ttypes_release(\"(\" new_idx)\n\t\treturn ret\n\t}\n}\n\nfunction core_map(idx,    f, lst, f_idx, lst_idx, lst_len, new_idx, expr_idx, i, env, ret, val)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'map'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tf = types_heap[idx][1]\n\tif (f !~ /^[$&%]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'map'. Expects function, supplied \" types_typename(f) \".\"\n\t}\n\tlst = types_heap[idx][2]\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function 'map'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t}\n\tf_idx = substr(f, 2)\n\tlst_idx = substr(lst, 2)\n\tlst_len = types_heap[lst_idx][\"len\"]\n\tnew_idx = types_allocate()\n\ttypes_heap[new_idx][0] = f\n\ttypes_heap[new_idx][\"len\"] = 2\n\texpr_idx = types_allocate()\n\tfor (i = 0; i < lst_len; ++i) {\n\t\ttypes_heap[new_idx][1] = types_heap[lst_idx][i]\n\t\tswitch (f) {\n\t\tcase /^\\$/:\n\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], new_idx)\n\t\t\tif (env ~ /^!/) {\n\t\t\t\ttypes_heap[expr_idx][\"len\"] = i\n\t\t\t\ttypes_heap[new_idx][\"len\"] = 0\n\t\t\t\ttypes_release(\"(\" expr_idx)\n\t\t\t\ttypes_release(\"(\" new_idx)\n\t\t\t\treturn env\n\t\t\t}\n\t\t\tret = EVAL(types_addref(types_heap[f_idx][\"body\"]), env)\n\t\t\tenv_release(env)\n\t\t\tbreak\n\t\tcase /^%/:\n\t\t\tf_idx = types_heap[f_idx][\"func\"]\n\t\tcase /^&/:\n\t\t\tret = @f_idx(new_idx)\n\t\t\tbreak\n\t\t}\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_heap[expr_idx][\"len\"] = i\n\t\t\ttypes_heap[new_idx][\"len\"] = 0\n\t\t\ttypes_release(\"(\" expr_idx)\n\t\t\ttypes_release(\"(\" new_idx)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_heap[expr_idx][i] = ret\n\t}\n\ttypes_heap[expr_idx][\"len\"] = lst_len\n\ttypes_heap[new_idx][\"len\"] = 0\n\ttypes_release(\"(\" new_idx)\n\treturn \"(\" expr_idx\n}\n\n\n\nfunction core_conj(idx,    len, lst, lst_idx, lst_len, new_idx, i, j)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len < 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'conj'. Expects at least 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tlst = types_heap[idx][1]\n\tif (lst !~ /^[([]/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'conj'. Expects list or vector, supplied \" types_typename(lst) \".\"\n\t}\n\tlst_idx = substr(lst, 2)\n\tlst_len = types_heap[lst_idx][\"len\"]\n\tnew_idx = types_allocate()\n\tj = 0\n\tif (lst ~ /^\\(/) {\n\t\tfor (i = len - 1; i >= 2; --i) {\n\t\t\ttypes_addref(types_heap[new_idx][j++] = types_heap[idx][i])\n\t\t}\n\t\tfor (i = 0; i < lst_len; ++i) {\n\t\t\ttypes_addref(types_heap[new_idx][j++] = types_heap[lst_idx][i])\n\t\t}\n\t} else {\n\t\tfor (i = 0; i < lst_len; ++i) {\n\t\t\ttypes_addref(types_heap[new_idx][j++] = types_heap[lst_idx][i])\n\t\t}\n\t\tfor (i = 2; i < len; ++i) {\n\t\t\ttypes_addref(types_heap[new_idx][j++] = types_heap[idx][i])\n\t\t}\n\t}\n\ttypes_addref(types_heap[new_idx][\"meta\"] = types_heap[lst_idx][\"meta\"])\n\ttypes_heap[new_idx][\"len\"] = j\n\treturn substr(lst, 1, 1) new_idx\n}\n\nfunction core_seq(idx,     obj, obj_idx, new_idx, i, len, chars)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'seq'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tobj = types_heap[idx][1]\n\tif (obj ~ /^[(]/) {\n\t\tif (types_heap[substr(obj, 2)][\"len\"] == 0) {\n\t\t\treturn \"#nil\"\n\t\t}\n\t\treturn types_addref(obj)\n\t} else if (obj ~ /^\\[/) {\n\t\tobj_idx = substr(obj, 2)\n\t\tlen = types_heap[obj_idx][\"len\"]\n\t\tif (len == 0) { return \"#nil\" }\n\t\tnew_idx = types_allocate()\n\t\tfor (i = 0; i < len; ++i) {\n\t\t\ttypes_addref(types_heap[new_idx][i] = types_heap[obj_idx][i])\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn \"(\" new_idx\n\t} else if (obj ~ /^\"/) {\n\t\tobj_idx = substr(obj, 2)\n\t\tlen = length(obj_idx)\n\t\tif (len == 0) { return \"#nil\" }\n\t\tnew_idx = types_allocate()\n\t\tsplit(obj_idx, chars, \"\")\n\t\tfor (i = 0; i <= len; ++i) {\n\t\t\ttypes_heap[new_idx][i] = \"\\\"\" chars[i+1]\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn \"(\" new_idx\n\t} else if (obj == \"#nil\") {\n\t\treturn \"#nil\"\n\t} else {\n\t\treturn \"!\\\"seq: called on non-sequence\"\n\t}\n}\n\n\nfunction core_meta(idx,    obj, obj_idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'meta'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tobj = types_heap[idx][1]\n\tif (obj ~ /^[([{$%]/ && \"meta\" in types_heap[obj_idx = substr(obj, 2)]) {\n\t\treturn types_addref(types_heap[obj_idx][\"meta\"])\n\t}\n\treturn \"#nil\"\n}\n\nfunction core_with_meta(idx,    obj, obj_idx, new_idx, i, len)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'with-meta'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tobj = types_heap[idx][1]\n\tobj_idx = substr(obj, 2)\n\tnew_idx = types_allocate()\n\ttypes_addref(types_heap[new_idx][\"meta\"] = types_heap[idx][2])\n\tswitch (obj) {\n\tcase /^[([]/:\n\t\tlen = types_heap[obj_idx][\"len\"]\n\t\tfor (i = 0; i < len; ++i) {\n\t\t\ttypes_addref(types_heap[new_idx][i] = types_heap[obj_idx][i])\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(obj, 1, 1) new_idx\n\tcase /^\\{/:\n\t\tfor (i in types_heap[obj_idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\ttypes_addref(types_heap[new_idx][i] = types_heap[obj_idx][i])\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n\tcase /^\\$/:\n\t\ttypes_addref(types_heap[new_idx][\"params\"] = types_heap[obj_idx][\"params\"])\n\t\ttypes_addref(types_heap[new_idx][\"body\"] = types_heap[obj_idx][\"body\"])\n\t\tenv_addref(types_heap[new_idx][\"env\"] = types_heap[obj_idx][\"env\"])\n\t\treturn \"$\" new_idx\n\tcase /^&/:\n\t\ttypes_heap[new_idx][\"func\"] = obj_idx\n\t\treturn \"%\" new_idx\n\tcase /^%/:\n\t\ttypes_heap[new_idx][\"func\"] = types_heap[obj_idx][\"func\"]\n\t\treturn \"%\" new_idx\n\tdefault:\n\t\ttypes_release(\"{\" new_idx)\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'with-meta'. Expects list, vector, hash-map or function, supplied \" types_typename(lst) \".\"\n\t}\n}\n\nfunction core_atom(idx,    atom_idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'atom'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tatom_idx = types_allocate()\n\ttypes_addref(types_heap[atom_idx][\"obj\"] = types_heap[idx][1])\n\treturn \"?\" atom_idx\n}\n\nfunction core_atomp(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'atom?'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn types_heap[idx][1] ~ /^\\?/ ? \"#true\" : \"#false\"\n}\n\nfunction core_deref(idx,    atom)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'deref'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tatom = types_heap[idx][1]\n\tif (atom !~ /^\\?/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'deref'. Expects atom, supplied \" types_typename(atom) \".\"\n\t}\n\treturn types_addref(types_heap[substr(atom, 2)][\"obj\"])\n}\n\nfunction core_reset(idx,    atom, atom_idx)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'reset!'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tatom = types_heap[idx][1]\n\tif (atom !~ /^\\?/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'reset!'. Expects atom, supplied \" types_typename(atom) \".\"\n\t}\n\tatom_idx = substr(atom, 2)\n\ttypes_release(types_heap[atom_idx][\"obj\"])\n\treturn types_addref(types_heap[atom_idx][\"obj\"] = types_heap[idx][2])\n}\n\nfunction core_swap(idx,    expr,    atom, f, lst_idx, ret, f_idx, env, i, len, atom_idx)\n{\n\tlen = types_heap[idx][\"len\"]\n\tif (len < 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'swap!'. Expects at least 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tatom = types_heap[idx][1]\n\tif (atom !~ /^\\?/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function 'swap!'. Expects atom, supplied \" types_typename(atom) \".\"\n\t}\n\tf = types_heap[idx][2]\n\tif (f !~ /^[&$%]/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function 'swap!'. Expects function, supplied \" types_typename(f) \".\"\n\t}\n\tlst_idx = types_allocate()\n\tatom_idx = substr(atom, 2)\n\ttypes_addref(types_heap[lst_idx][0] = f)\n\ttypes_addref(types_heap[lst_idx][1] = types_heap[atom_idx][\"obj\"])\n\tfor (i = 3; i < len; ++i) {\n\t\ttypes_addref(types_heap[lst_idx][i - 1] = types_heap[idx][i])\n\t}\n\ttypes_heap[lst_idx][\"len\"] = len - 1\n\n\tf_idx = substr(f, 2)\n\tswitch (f) {\n\tcase /^\\$/:\n\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], lst_idx)\n\t\ttypes_release(\"(\" lst_idx)\n\t\tif (env ~ /^!/) {\n\t\t\treturn env\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[f_idx][\"body\"]), env)\n\t\tenv_release(env)\n\t\tbreak\n\tcase /^%/:\n\t\tf_idx = types_heap[f_idx][\"func\"]\n\tcase /^&/:\n\t\tret = @f_idx(lst_idx)\n\t\ttypes_release(\"(\" lst_idx)\n\t\tbreak\n\t}\n\n\tif (ret ~ /^!/) {\n\t\treturn ret\n\t}\n\ttypes_release(types_heap[atom_idx][\"obj\"])\n\treturn types_addref(types_heap[atom_idx][\"obj\"] = ret)\n}\n\nfunction core_init()\n{\n\tcore_ns[\"'=\"] = \"&core_eq\"\n\tcore_ns[\"'throw\"] = \"&core_throw\"\n\n\tcore_ns[\"'nil?\"] = \"&core_nilp\"\n\tcore_ns[\"'true?\"] = \"&core_truep\"\n\tcore_ns[\"'false?\"] = \"&core_falsep\"\n\tcore_ns[\"'string?\"] = \"&core_stringp\"\n\tcore_ns[\"'symbol\"] = \"&core_symbol\"\n\tcore_ns[\"'symbol?\"] = \"&core_symbolp\"\n\tcore_ns[\"'keyword\"] = \"&core_keyword\"\n\tcore_ns[\"'keyword?\"] = \"&core_keywordp\"\n\tcore_ns[\"'number?\"] = \"&core_numberp\"\n\tcore_ns[\"'fn?\"] = \"&core_fnp\"\n\tcore_ns[\"'macro?\"] = \"&core_macrop\"\n\n\tcore_ns[\"'pr-str\"] = \"&core_pr_str\"\n\tcore_ns[\"'str\"] = \"&core_str\"\n\tcore_ns[\"'prn\"] = \"&core_prn\"\n\tcore_ns[\"'println\"] = \"&core_println\"\n\tcore_ns[\"'read-string\"] = \"&core_read_string\"\n\tcore_ns[\"'readline\"] = \"&core_readline\"\n\tcore_ns[\"'slurp\"] = \"&core_slurp\"\n\n\tcore_ns[\"'<\"] = \"&core_lt\"\n\tcore_ns[\"'<=\"] = \"&core_le\"\n\tcore_ns[\"'>\"] = \"&core_gt\"\n\tcore_ns[\"'>=\"] = \"&core_ge\"\n\tcore_ns[\"'+\"] = \"&core_add\"\n\tcore_ns[\"'-\"] = \"&core_subtract\"\n\tcore_ns[\"'*\"] = \"&core_multiply\"\n\tcore_ns[\"'/\"] = \"&core_divide\"\n\tcore_ns[\"'time-ms\"] = \"&core_time_ms\"\n\n\tcore_ns[\"'list\"] = \"&core_list\"\n\tcore_ns[\"'list?\"] = \"&core_listp\"\n\tcore_ns[\"'vec\"] = \"&core_vec\"\n\tcore_ns[\"'vector\"] = \"&core_vector\"\n\tcore_ns[\"'vector?\"] = \"&core_vectorp\"\n\tcore_ns[\"'hash-map\"] = \"&core_hash_map\"\n\tcore_ns[\"'map?\"] = \"&core_mapp\"\n\tcore_ns[\"'assoc\"] = \"&core_assoc\"\n\tcore_ns[\"'dissoc\"] = \"&core_dissoc\"\n\tcore_ns[\"'get\"] = \"&core_get\"\n\tcore_ns[\"'contains?\"] = \"&core_containsp\"\n\tcore_ns[\"'keys\"] = \"&core_keys\"\n\tcore_ns[\"'vals\"] = \"&core_vals\"\n\n\tcore_ns[\"'sequential?\"] = \"&core_sequentialp\"\n\tcore_ns[\"'cons\"] = \"&core_cons\"\n\tcore_ns[\"'concat\"] = \"&core_concat\"\n\tcore_ns[\"'nth\"] = \"&core_nth\"\n\tcore_ns[\"'first\"] = \"&core_first\"\n\tcore_ns[\"'rest\"] = \"&core_rest\"\n\tcore_ns[\"'empty?\"] = \"&core_emptyp\"\n\tcore_ns[\"'count\"] = \"&core_count\"\n\tcore_ns[\"'apply\"] = \"&core_apply\"\n\tcore_ns[\"'map\"] = \"&core_map\"\n\n\tcore_ns[\"'conj\"] = \"&core_conj\"\n\tcore_ns[\"'seq\"] = \"&core_seq\"\n\n\tcore_ns[\"'meta\"] = \"&core_meta\"\n\tcore_ns[\"'with-meta\"] = \"&core_with_meta\"\n\tcore_ns[\"'atom\"] = \"&core_atom\"\n\tcore_ns[\"'atom?\"] = \"&core_atomp\"\n\tcore_ns[\"'deref\"] = \"&core_deref\"\n\tcore_ns[\"'reset!\"] = \"&core_reset\"\n\tcore_ns[\"'swap!\"] = \"&core_swap\"\n}\n\n\n\nBEGIN {\n\tcore_init()\n}\n"
  },
  {
    "path": "impls/awk/env.awk",
    "content": "function env_new(outer, params, args,    idx, len, i, j, lst, param)\n{\n\tif (params != \"\") {\n\t\tparams = substr(params, 2)\n\t\tlen = types_heap[params][\"len\"]\n\t\tif (len >= 2 && types_heap[params][len - 2] == \"'&\") {\n\t\t\tif (types_heap[args][\"len\"] < len - 1) {\n\t\t\t\treturn \"!\\\"Invalid argument length for the function. Expects at least \" (len - 2) \" arguments, supplied \" (types_heap[args][\"len\"] - 1) \".\"\n\t\t\t}\n\t\t} else {\n\t\t\tif (types_heap[args][\"len\"] != len + 1) {\n\t\t\t\treturn \"!\\\"Invalid argument length for the function. Expects exactly \" len \" arguments, supplied \" (types_heap[args][\"len\"] - 1) \".\"\n\t\t\t}\n\t\t}\n\t}\n\tenv_heap[env_heap_index][\"ref\"] = 1\n\tenv_heap[env_heap_index][\"outer\"] = outer\n\tif (params != \"\") {\n\t\tfor (i = 0; i < len; ++i) {\n\t\t\tparam = types_heap[params][i]\n\t\t\tif (param == \"'&\") {\n\t\t\t\tidx = types_allocate()\n\t\t\t\tenv_set(env_heap_index, types_heap[params][++i], \"(\" idx)\n\t\t\t\tlen = types_heap[args][\"len\"]\n\t\t\t\tfor (j = 0; i < len; ++j) {\n\t\t\t\t\ttypes_addref(types_heap[idx][j] = types_heap[args][i++])\n\t\t\t\t}\n\t\t\t\ttypes_heap[idx][\"len\"] = j\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tenv_set(env_heap_index, param, types_heap[args][i + 1])\n\t\t\ttypes_addref(types_heap[args][i + 1])\n\t\t}\n\t}\n\tif (outer != \"\") {\n\t\tenv_addref(outer)\n\t}\n\treturn env_heap_index++\n}\n\nfunction env_set(env, key, val)\n{\n\tif (key in env_heap[env]) {\n\t\ttypes_release(env_heap[env][key])\n\t}\n\tif (val ~ /^&/) {\n\t\tenv_builtinnames[substr(val, 2)] = substr(key, 2)\n\t}\n\tenv_heap[env][key] = val\n}\n\nfunction env_find(env, key)\n{\n\twhile (env != \"\") {\n\t\tif (key in env_heap[env]) {\n\t\t\treturn env\n\t\t}\n\t\tenv = env_heap[env][\"outer\"]\n\t}\n\treturn env\n}\n\nfunction env_get(env, key)\n{\n\tenv = env_find(env, key)\n\tif (env != \"\") {\n\t\treturn env_heap[env][key]\n\t}\n\treturn \"!\\\"'\" substr(key, 2) \"' not found\"\n}\n\nfunction env_addref(env)\n{\n\tenv_heap[env][\"ref\"]++\n}\n\nfunction env_release(env,    i, outer)\n{\n\twhile (env != \"\" && --env_heap[env][\"ref\"] == 0) {\n\t\tfor (i in env_heap[env]) {\n\t\t\tif (i ~ /^'/) {\n\t\t\t\ttypes_release(env_heap[env][i])\n\t\t\t}\n\t\t}\n\t\touter = env_heap[env][\"outer\"]\n\t\tdelete env_heap[env]\n\t\tenv = outer\n\t}\n}\n\nfunction env_dump(i, j)\n{\n\tfor (i = 0; i < env_heap_index; i++) {\n\t\tif (i in env_heap) {\n\t\t\tif (isarray(env_heap[i])) {\n\t\t\t\tif (!(\"checked\" in env_heap[i]) || env_heap[i][\"checked\"] != env_heap[i][\"ref\"]) {\n\t\t\t\t\tfor (j in env_heap[i]) {\n\t\t\t\t\t\tprint \"  env_heap[\" i \"][\" j \"] = \" env_heap[i][j]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprint \"  env_heap[\" i \"] = \" env_heap[i]\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction env_check(env,    i, outer)\n{\n\tif (env_heap[env][\"checked\"]++) {\n\t\treturn\n\t}\n\tfor (i in env_heap[env]) {\n\t\tif (i != \"ref\" && i != \"outer\") {\n\t\t\ttypes_check(env_heap[env][i])\n\t\t}\n\t}\n\touter = env_heap[env][\"outer\"]\n\tif (outer in env_heap) {\n\t\tenv_check(outer)\n\t}\n}\n\nBEGIN {\n\tenv_heap_index = 0\n}\n"
  },
  {
    "path": "impls/awk/printer.awk",
    "content": "function printer_pr_list(expr, print_readably,    idx, len, i, str)\n{\n\tidx = substr(expr, 2)\n\tlen = types_heap[idx][\"len\"]\n\tfor (i = 0; i < len; ++i) {\n\t\tstr = str printer_pr_str(types_heap[idx][i], print_readably) \" \"\n\t}\n\treturn substr(str, 1, length(str) - 1)\n}\n\nfunction printer_pr_hash(expr, print_readably,    idx, var, str)\n{\n\tidx = substr(expr, 2)\n\tfor (var in types_heap[idx]) {\n\t\tswitch (var) {\n\t\tcase /^\"/:\n\t\t\tstr = str printer_pr_string(var, print_readably) \" \" printer_pr_str(types_heap[idx][var], print_readably) \" \"\n\t\t\tbreak\n\t\tcase /^:/:\n\t\t\tstr = str substr(var, 2) \" \" printer_pr_str(types_heap[idx][var], print_readably) \" \"\n\t\t\tbreak\n\t\t}\n\t}\n\treturn substr(str, 1, length(str) - 1)\n}\n\nfunction printer_pr_string(expr, print_readably,    v, r)\n{\n\tif (!print_readably) {\n\t\treturn substr(expr, 2)\n\t}\n\texpr = substr(expr, 2)\n\twhile (match(expr, /[\"\\n\\\\]/, r)) {\n\t\tv = v substr(expr, 1, RSTART - 1) (r[0] == \"\\n\" ? \"\\\\n\" : \"\\\\\" r[0])\n\t\texpr = substr(expr, RSTART + RLENGTH)\n\t}\n\treturn \"\\\"\" v expr \"\\\"\"\n}\n\nfunction printer_pr_str(expr, print_readably,    var)\n{\n\tswitch (expr) {\n\tcase /^\\(/:\n\t\treturn \"(\" printer_pr_list(expr, print_readably) \")\"\n\tcase /^\\[/:\n\t\treturn \"[\" printer_pr_list(expr, print_readably) \"]\"\n\tcase /^\\{/:\n\t\treturn \"{\" printer_pr_hash(expr, print_readably) \"}\"\n\tcase /^\"/:\n\t\treturn printer_pr_string(expr, print_readably)\n\tcase /^\\$/:\n\t\tvar = substr(expr, 2)\n\t\treturn \"#<Function> (fn* \" printer_pr_str(types_heap[var][\"params\"], print_readably) \" \" printer_pr_str(types_heap[var][\"body\"], print_readably) \")\"\n\tcase /^&/:\n\t\treturn \"#<BuiltinFunction \" env_builtinnames[substr(expr, 2)] \">\"\n\tcase /^%/:\n\t\treturn \"#<BuiltinFunction \" env_builtinnames[types_heap[substr(expr, 2)][\"func\"]] \">\"\n\tcase /^\\?/:\n\t\treturn \"(atom \" printer_pr_str(types_heap[substr(expr, 2)][\"obj\"], print_readably) \")\"\n\tdefault:\n\t\treturn substr(expr, 2)\n\t}\n}\n"
  },
  {
    "path": "impls/awk/reader.awk",
    "content": "function reader_read_string(token,    v, r)\n{\n\ttoken = substr(token, 1, length(token) - 1)\n\tgsub(/\\\\\\\\/, \"\\xf7\", token)\n\tgsub(/\\\\\"/, \"\\\"\", token)\n\tgsub(/\\\\n/, \"\\n\", token)\n\tgsub(\"\\xf7\", \"\\\\\", token)\n\treturn token\n}\n\nfunction reader_read_atom(token)\n{\n\tswitch (token) {\n\tcase \"true\":\n\tcase \"false\":\n\tcase \"nil\":\n\t\treturn \"#\" token\n\tcase /^:/:\n\t\treturn \":\" token\n\tcase /^\"/:\n\t\tif (token ~ /^\"(\\\\.|[^\\\\\"])*\"$/) {\n\t\t\t\treturn reader_read_string(token)\n\t\t} else {\n\t\t\t\treturn \"!\\\"Expected '\\\"', got EOF.\"\n\t\t}\n\tcase /^-?[0-9]+$/:\n\t\treturn \"+\" token\n\tdefault:\n\t\treturn \"'\" token\n\t}\n}\n\nfunction reader_read_list(reader, type, end,    idx, len, ret)\n{\n\tidx = types_allocate()\n\tlen = 0\n\twhile (reader[\"curidx\"] in reader) {\n\t\tif (reader[reader[\"curidx\"]] == end) {\n\t\t\ttypes_heap[idx][\"len\"] = len\n\t\t\treader[\"curidx\"]++\n\t\t\treturn type idx\n\t\t}\n\t\tret = reader_read_from(reader)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_heap[idx][\"len\"] = len\n\t\t\ttypes_release(type idx)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_heap[idx][len++] = ret\n\t}\n\ttypes_heap[idx][\"len\"] = len\n\ttypes_release(type idx)\n\treturn \"!\\\"expected '\" end \"', got EOF\"\n}\n\nfunction reader_read_hash(reader,    idx, key, val)\n{\n\tidx = types_allocate()\n\twhile (reader[\"curidx\"] in reader) {\n\t\tif (reader[reader[\"curidx\"]] == \"}\") {\n\t\t\treader[\"curidx\"]++\n\t\t\treturn \"{\" idx\n\t\t}\n\t\tkey = reader_read_from(reader)\n\t\tif (key ~ /^!/) {\n\t\t\ttypes_release(\"{\" idx)\n\t\t\treturn key\n\t\t}\n\t\tif (key !~ /^[\":]/) {\n\t\t\ttypes_release(key)\n\t\t\ttypes_release(\"{\" idx)\n\t\t\treturn \"!\\\"Hash-map key must be string or keyword.\"\n\t\t}\n\t\tif (!(reader[\"curidx\"] in reader)) {\n\t\t\ttypes_release(\"{\" idx)\n\t\t\treturn \"!\\\"Element count of hash-map must be even.\"\n\t\t}\n\t\tval = reader_read_from(reader)\n\t\tif (val ~ /^!/) {\n\t\t\ttypes_release(\"{\" idx)\n\t\t\treturn val\n\t\t}\n\t\ttypes_heap[idx][key] = val\n\t}\n\ttypes_release(\"{\" idx)\n\treturn \"!\\\"expected '}', got EOF\"\n}\n\nfunction reader_read_abbrev(reader, symbol,    val, idx)\n{\n\tval = reader_read_from(reader)\n\tif (val ~ /^!/) {\n\t\treturn val\n\t}\n\tidx = types_allocate()\n\ttypes_heap[idx][\"len\"] = 2\n\ttypes_heap[idx][0] = symbol\n\ttypes_heap[idx][1] = val\n\treturn \"(\" idx\n}\n\nfunction reader_read_with_meta(reader,    meta, val, idx)\n{\n\tmeta = reader_read_from(reader)\n\tif (meta ~ /^!/) {\n\t\treturn meta\n\t}\n\tval = reader_read_from(reader)\n\tif (val ~ /^!/) {\n\t\ttypes_release(meta)\n\t\treturn val\n\t}\n\tidx = types_allocate()\n\ttypes_heap[idx][\"len\"] = 3\n\ttypes_heap[idx][0] = \"'with-meta\"\n\ttypes_heap[idx][1] = val\n\ttypes_heap[idx][2] = meta\n\treturn \"(\" idx\n}\n\nfunction reader_read_from(reader,    current)\n{\n\tcurrent = reader[reader[\"curidx\"]++]\n\tswitch (current) {\n\tcase \"(\":\n\t\treturn reader_read_list(reader, \"(\", \")\")\n\tcase \"[\":\n\t\treturn reader_read_list(reader, \"[\", \"]\")\n\tcase \"{\":\n\t\treturn reader_read_hash(reader)\n\tcase \")\":\n\tcase \"]\":\n\tcase \"}\":\n\t\treturn \"!\\\"Unexpected token '\" current \"'.\"\n\tcase \"'\":\n\t\treturn reader_read_abbrev(reader, \"'quote\")\n\tcase \"`\":\n\t\treturn reader_read_abbrev(reader, \"'quasiquote\")\n\tcase \"~\":\n\t\treturn reader_read_abbrev(reader, \"'unquote\")\n\tcase \"~@\":\n\t\treturn reader_read_abbrev(reader, \"'splice-unquote\")\n\tcase \"@\":\n\t\treturn reader_read_abbrev(reader, \"'deref\")\n\tcase \"^\":\n\t\treturn reader_read_with_meta(reader)\n\tdefault:\n\t\treturn reader_read_atom(current)\n\t}\n}\n\nfunction reader_tokenizer(str,    reader,    len, r)\n{\n\tfor (len = 0; match(str, /^[ \\t\\r\\n,]*(~@|[\\[\\]{}()'`~^@]|\\\"(\\\\.|[^\\\\\"])*\\\"?|;[^\\r\\n]*|[^ \\t\\r\\n\\[\\]{}('\"`,;)^~@][^ \\t\\r\\n\\[\\]{}('\"`,;)]*)/, r); ) {\n\t\tif (substr(r[1], 1, 1) != \";\") {\n\t\t\treader[len++] = r[1]\n\t\t}\n\t\tstr = substr(str, RSTART + RLENGTH)\n\t}\n\tif (str !~ /^[ \\t\\r\\n,]*$/) {\n\t\treturn \"!\\\"Cannot tokenize '\" str \"'.\"\n\t}\n\treader[\"len\"] = len\n\treturn \"\"\n}\n\nfunction reader_read_str(str,    reader, ret)\n{\n\tret = reader_tokenizer(str,    reader)\n\tif (ret != \"\") {\n\t\treturn ret\n\t}\n\tif (reader[\"len\"] == 0) {\n\t\treturn \"#nil\"\n\t}\n\tret = reader_read_from(reader)\n\tif (ret ~ /^!/) {\n\t\treturn ret\n\t}\n\tif (reader[\"len\"] != reader[\"curidx\"]) {\n\t\ttypes_release(ret)\n\t\treturn \"!\\\"Unexpected token '\" reader[reader[\"curidx\"]] \"'.\"\n\t}\n\treturn ret\n}\n"
  },
  {
    "path": "impls/awk/run",
    "content": "#!/usr/bin/env bash\nexec awk -O -f $(dirname $0)/${STEP:-stepA_mal}.awk \"${@}\"\n"
  },
  {
    "path": "impls/awk/step0_repl.awk",
    "content": "function READ(str)\n{\n\treturn str\n}\n\nfunction EVAL(ast)\n{\n\treturn ast\n}\n\nfunction PRINT(expr)\n{\n\treturn expr\n}\n\nfunction rep(str)\n{\n\treturn PRINT(EVAL(READ(str)))\n}\n\nfunction main(str)\n{\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tprint rep(str)\n\t}\n}\n\nBEGIN {\n\tmain()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step1_read_print.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\nfunction EVAL(ast)\n{\n\treturn ast\n}\n\nfunction PRINT(expr)\n{\n\treturn printer_pr_str(expr, 1)\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction main(str, ret)\n{\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step2_eval.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL(ast, env,    new_ast, ret, idx, f, f_idx)\n{\n\t# print \"EVAL: \" printer_pr_str(ast, 1)\n\n\tswitch (ast) {\n\tcase /^'/:      # symbol\n\t\tif (ast in env) {\n\t\t\tret = types_addref(env[ast])\n\t\t} else {\n\t\t\tret = \"!\\\"'\" substr(ast, 2) \"' not found\"\n\t\t}\n\t\ttypes_release(ast)\n\t\treturn ret\n\tcase /^\\[/:     # vector\n\t\tret = eval_ast(ast, env)\n\t\ttypes_release(ast)\n\t\treturn ret\n\tcase /^\\{/:     # map\n\t\tret = eval_map(ast, env)\n\t\ttypes_release(ast)\n\t\treturn ret\n\tcase /^[^(]/:    # not a list\n\t\ttypes_release(ast)\n\t\treturn ast\n\t}\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] == 0) {\n\t\treturn ast\n\t}\n\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\tif (f ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn f\n\t}\n\tnew_ast = eval_ast(ast, env)\n\ttypes_release(ast)\n\tif (new_ast ~ /^!/) {\n\t\treturn new_ast\n\t}\n\tidx = substr(new_ast, 2)\n\tif (f ~ /^&/) {\n\t\tf_idx = substr(f, 2)\n\t\tret = @f_idx(idx)\n\t\ttypes_release(new_ast)\n\t\treturn ret\n\t} else {\n\t\ttypes_release(new_ast)\n\t\treturn \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction add(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '+'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '+'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '+'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) + substr(rhs, 2))\n}\n\nfunction subtract(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '-'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '-'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '-'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) - substr(rhs, 2))\n}\n\nfunction multiply(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '*'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '*'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '*'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) * substr(rhs, 2))\n}\n\nfunction divide(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '/'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '/'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '/'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" int(substr(lhs, 2) / substr(rhs, 2))\n}\n\nfunction main(str, ret)\n{\n\trepl_env[\"'+\"] = \"&add\"\n\trepl_env[\"'-\"] = \"&subtract\"\n\trepl_env[\"'*\"] = \"&multiply\"\n\trepl_env[\"'/\"] = \"&divide\"\n\tenv_builtinnames[\"add\"] = \"+\"\n\tenv_builtinnames[\"subtract\"] = \"-\"\n\tenv_builtinnames[\"multiply\"] = \"*\"\n\tenv_builtinnames[\"divide\"] = \"/\"\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step3_env.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret = EVAL(body, new_env)\n\tenv_release(new_env)\n\treturn ret\n}\n\nfunction EVAL(ast, env,    new_ast, ret, idx, f, f_idx)\n{\n\tenv_addref(env)\n\n\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\tcase /^!/:\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tbreak\n\tdefault:\n\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t}\n\n\tswitch (ast) {\n\tcase /^'/:      # symbol\n\t\tret = env_get(env, ast)\n\t\tif (ret !~ /^!/) {\n\t\t\ttypes_addref(ret)\n\t\t}\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^\\[/:     # vector\n\t\tret = eval_ast(ast, env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^\\{/:     # map\n\t\tret = eval_map(ast, env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^[^(]/:    # not a list\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ast\n\t}\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] == 0) {\n\t\tenv_release(env)\n\t\treturn ast\n\t}\n\tswitch (types_heap[idx][0]) {\n\tcase \"'def!\":\n\t\treturn EVAL_def(ast, env)\n\tcase \"'let*\":\n\t\treturn EVAL_let(ast, env)\n\tdefault:\n\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\tif (f ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn f\n\t\t}\n\t\tnew_ast = eval_ast(ast, env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\tif (new_ast ~ /^!/) {\n\t\t\treturn new_ast\n\t\t}\n\t\tidx = substr(new_ast, 2)\n\t\tf_idx = substr(f, 2)\n\t\tswitch (f) {\n\t\tcase /^&/:\n\t\t\tret = @f_idx(idx)\n\t\t\ttypes_release(new_ast)\n\t\t\treturn ret\n\t\tdefault:\n\t\t\ttypes_release(new_ast)\n\t\t\treturn \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction add(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '+'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '+'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '+'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) + substr(rhs, 2))\n}\n\nfunction subtract(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '-'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '-'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '-'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) - substr(rhs, 2))\n}\n\nfunction multiply(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '*'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '*'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '*'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" (substr(lhs, 2) * substr(rhs, 2))\n}\n\nfunction divide(idx,    lhs, rhs)\n{\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\treturn \"!\\\"Invalid argument length for builtin function '/'. Expects exactly 2 arguments, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\tlhs = types_heap[idx][1]\n\tif (lhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 1 of builtin function '/'. Expects number, supplied \" types_typename(lhs) \".\"\n\t}\n\trhs = types_heap[idx][2]\n\tif (rhs !~ /^\\+/) {\n\t\treturn \"!\\\"Incompatible type for argument 2 of builtin function '/'. Expects number, supplied \" types_typename(rhs) \".\"\n\t}\n\treturn \"+\" int(substr(lhs, 2) / substr(rhs, 2))\n}\n\nfunction main(str, ret)\n{\n\trepl_env = env_new()\n\tenv_set(repl_env, \"'+\", \"&add\")\n\tenv_set(repl_env, \"'-\", \"&subtract\")\n\tenv_set(repl_env, \"'*\", \"&multiply\")\n\tenv_set(repl_env, \"'/\", \"&divide\")\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step4_if_fn_do.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret = EVAL(body, new_env)\n\tenv_release(new_env)\n\treturn ret\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\tret = EVAL(types_addref(types_heap[idx][len - 1]), env)\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\tret = EVAL(body, env)\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    new_ast, ret, idx, f, f_idx)\n{\n\tenv_addref(env)\n\n\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\tcase /^!/:\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tbreak\n\tdefault:\n\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t}\n\n\tswitch (ast) {\n\tcase /^'/:      # symbol\n\t\tret = env_get(env, ast)\n\t\tif (ret !~ /^!/) {\n\t\t\ttypes_addref(ret)\n\t\t}\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^\\[/:     # vector\n\t\tret = eval_ast(ast, env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^\\{/:     # map\n\t\tret = eval_map(ast, env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\tcase /^[^(]/:    # not a list\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ast\n\t}\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] == 0) {\n\t\tenv_release(env)\n\t\treturn ast\n\t}\n\tswitch (types_heap[idx][0]) {\n\tcase \"'def!\":\n\t\treturn EVAL_def(ast, env)\n\tcase \"'let*\":\n\t\treturn EVAL_let(ast, env)\n\tcase \"'do\":\n\t\treturn EVAL_do(ast, env)\n\tcase \"'if\":\n\t\treturn EVAL_if(ast, env)\n\tcase \"'fn*\":\n\t\treturn EVAL_fn(ast, env)\n\tdefault:\n\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\tif (f ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn f\n\t\t}\n\t\tnew_ast = eval_ast(ast, env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\tif (new_ast ~ /^!/) {\n\t\t\treturn new_ast\n\t\t}\n\t\tidx = substr(new_ast, 2)\n\t\tf_idx = substr(f, 2)\n\t\tswitch (f) {\n\t\tcase /^\\$/:\n\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\tif (env ~ /^!/) {\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn env\n\t\t\t}\n\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\ttypes_release(new_ast)\n\t\t\tret = EVAL(ast, env)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^&/:\n\t\t\tret = @f_idx(idx)\n\t\t\ttypes_release(new_ast)\n\t\t\treturn ret\n\t\tdefault:\n\t\t\ttypes_release(new_ast)\n\t\t\treturn \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction main(str, ret, i)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step5_tco.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    ret_env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret_env[0] = new_env\n\treturn body\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, body, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\ttypes_addref(body = types_heap[idx][len - 1])\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\tbody = \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)\n{\n\tenv_addref(env)\n\tfor (;;) {\n\n\t\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\t\tcase /^!/:\n\t\tcase \"#nil\":\n\t\tcase \"#false\":\n\t\t\tbreak\n\t\tdefault:\n\t\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t\t}\n\n\t\tswitch (ast) {\n\t\tcase /^'/:      # symbol\n\t\t\tret = env_get(env, ast)\n\t\t\tif (ret !~ /^!/) {\n\t\t\t\ttypes_addref(ret)\n\t\t\t}\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\[/:     # vector\n\t\t\tret = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\{/:     # map\n\t\t\tret = eval_map(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^[^(]/:    # not a list\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tif (len == 0) {\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tswitch (types_heap[idx][0]) {\n\t\tcase \"'def!\":\n\t\t\treturn EVAL_def(ast, env)\n\t\tcase \"'let*\":\n\t\t\tast = EVAL_let(ast, env,    ret_env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'do\":\n\t\t\tast = EVAL_do(ast, env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'if\":\n\t\t\tast = EVAL_if(ast, env)\n\t\t\tif (ast !~ /^['([{]/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'fn*\":\n\t\t\treturn EVAL_fn(ast, env)\n\t\tdefault:\n\t\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\t\tif (f ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn f\n\t\t\t}\n\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\treturn new_ast\n\t\t\t}\n\t\t\tidx = substr(new_ast, 2)\n\t\t\tf_idx = substr(f, 2)\n\t\t\tswitch (f) {\n\t\t\tcase /^\\$/:\n\t\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\tif (env ~ /^!/) {\n\t\t\t\t\ttypes_release(new_ast)\n\t\t\t\t\treturn env\n\t\t\t\t}\n\t\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\t\ttypes_release(f)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tcontinue\n\t\t\tcase /^&/:\n\t\t\t\tret = @f_idx(idx)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn ret\n\t\t\tdefault:\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tret = \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t\t\ttypes_release(f)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction main(str, ret, i)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step6_file.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    ret_env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret_env[0] = new_env\n\treturn body\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, body, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\ttypes_addref(body = types_heap[idx][len - 1])\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\tbody = \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)\n{\n\tenv_addref(env)\n\tfor (;;) {\n\n\t\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\t\tcase /^!/:\n\t\tcase \"#nil\":\n\t\tcase \"#false\":\n\t\t\tbreak\n\t\tdefault:\n\t\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t\t}\n\n\t\tswitch (ast) {\n\t\tcase /^'/:      # symbol\n\t\t\tret = env_get(env, ast)\n\t\t\tif (ret !~ /^!/) {\n\t\t\t\ttypes_addref(ret)\n\t\t\t}\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\[/:     # vector\n\t\t\tret = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\{/:     # map\n\t\t\tret = eval_map(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^[^(]/:    # not a list\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tif (len == 0) {\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tswitch (types_heap[idx][0]) {\n\t\tcase \"'def!\":\n\t\t\treturn EVAL_def(ast, env)\n\t\tcase \"'let*\":\n\t\t\tast = EVAL_let(ast, env,    ret_env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'do\":\n\t\t\tast = EVAL_do(ast, env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'if\":\n\t\t\tast = EVAL_if(ast, env)\n\t\t\tif (ast !~ /^['([{]/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'fn*\":\n\t\t\treturn EVAL_fn(ast, env)\n\t\tdefault:\n\t\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\t\tif (f ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn f\n\t\t\t}\n\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\treturn new_ast\n\t\t\t}\n\t\t\tidx = substr(new_ast, 2)\n\t\t\tf_idx = substr(f, 2)\n\t\t\tswitch (f) {\n\t\t\tcase /^\\$/:\n\t\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\tif (env ~ /^!/) {\n\t\t\t\t\ttypes_release(new_ast)\n\t\t\t\t\treturn env\n\t\t\t\t}\n\t\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\t\ttypes_release(f)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tcontinue\n\t\t\tcase /^&/:\n\t\t\t\tret = @f_idx(idx)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn ret\n\t\t\tdefault:\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tret = \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t\t\ttypes_release(f)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction eval(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'eval'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn EVAL(types_addref(types_heap[idx][1]), repl_env)\n}\n\nfunction main(str, ret, i, idx)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\tenv_set(repl_env, \"'eval\", \"&eval\")\n\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\n\tidx = types_allocate()\n\tenv_set(repl_env, \"'*ARGV*\", \"(\" idx)\n\tif (ARGC > 1) {\n\t\tfor (i = 2; i < ARGC; ++i) {\n\t\t\ttypes_heap[idx][i - 2] = \"\\\"\" ARGV[i]\n\t\t}\n\t\ttypes_heap[idx][\"len\"] = ARGC - 2\n\t\tARGC = 1\n\t\trep(\"(load-file \\\"\" ARGV[1] \"\\\")\")\n\t\treturn\n\t}\n\ttypes_heap[idx][\"len\"] = 0\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step7_quote.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\n# Return 0, an error or the unquote argument (second element of ast).\nfunction starts_with(ast, sym,    idx, len)\n{\n\tif (ast !~ /^\\(/)\n\t\treturn 0\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (!len || types_heap[idx][0] != sym)\n\t\treturn 0\n\tif (len != 2)\n\t\treturn  \"!\\\"'\" sym \"' expects 1 argument, not \" (len - 1) \".\"\n\treturn types_heap[idx][1]\n}\n\nfunction quasiquote(ast,    new_idx, ret, ast_idx, elt_i, elt, previous)\n{\n\tif (ast !~ /^[(['{]/) {\n\t\treturn ast\n\t}\n\tif (ast ~ /['\\{]/) {\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'quote\"\n\t\ttypes_heap[new_idx][1] = ast\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t\treturn \"(\" new_idx\n\t}\n\tret = starts_with(ast, \"'unquote\")\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tif (ret) {\n\t\ttypes_addref(ret)\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tnew_idx = types_allocate()\n\ttypes_heap[new_idx][\"len\"] = 0\n\tast_idx = substr(ast, 2)\n\tfor (elt_i=types_heap[ast_idx][\"len\"]-1; 0<=elt_i; elt_i--) {\n\t\telt = types_heap[ast_idx][elt_i]\n\t\tret = starts_with(elt, \"'splice-unquote\")\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(\"(\" new_idx)\n\t\t\ttypes_release(ast)\n\t\t\treturn ret\n\t\t}\n\t\tif (ret) {\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'concat\"\n\t\t\ttypes_heap[new_idx][1] = types_addref(ret)\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t} else {\n\t\t\tret = quasiquote(types_addref(elt))\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'cons\"\n\t\t\ttypes_heap[new_idx][1] = ret\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t}\n\t}\n\tif (ast ~ /^\\[/) {\n\t\tprevious = \"(\" new_idx\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'vec\"\n\t\ttypes_heap[new_idx][1] = previous\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t}\n\ttypes_release(ast)\n\treturn \"(\" new_idx\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    ret_env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret_env[0] = new_env\n\treturn body\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, body, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\ttypes_addref(body = types_heap[idx][len - 1])\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\tbody = \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)\n{\n\tenv_addref(env)\n\tfor (;;) {\n\n\t\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\t\tcase /^!/:\n\t\tcase \"#nil\":\n\t\tcase \"#false\":\n\t\t\tbreak\n\t\tdefault:\n\t\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t\t}\n\n\t\tswitch (ast) {\n\t\tcase /^'/:      # symbol\n\t\t\tret = env_get(env, ast)\n\t\t\tif (ret !~ /^!/) {\n\t\t\t\ttypes_addref(ret)\n\t\t\t}\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\[/:     # vector\n\t\t\tret = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\{/:     # map\n\t\t\tret = eval_map(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^[^(]/:    # not a list\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tif (len == 0) {\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tswitch (types_heap[idx][0]) {\n\t\tcase \"'def!\":\n\t\t\treturn EVAL_def(ast, env)\n\t\tcase \"'let*\":\n\t\t\tast = EVAL_let(ast, env,    ret_env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'quote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn body\n\t\tcase \"'quasiquote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quasiquote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tast = quasiquote(body)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'do\":\n\t\t\tast = EVAL_do(ast, env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'if\":\n\t\t\tast = EVAL_if(ast, env)\n\t\t\tif (ast !~ /^['([{]/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'fn*\":\n\t\t\treturn EVAL_fn(ast, env)\n\t\tdefault:\n\t\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\t\tif (f ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn f\n\t\t\t}\n\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\treturn new_ast\n\t\t\t}\n\t\t\tidx = substr(new_ast, 2)\n\t\t\tf_idx = substr(f, 2)\n\t\t\tswitch (f) {\n\t\t\tcase /^\\$/:\n\t\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\tif (env ~ /^!/) {\n\t\t\t\t\ttypes_release(new_ast)\n\t\t\t\t\treturn env\n\t\t\t\t}\n\t\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\t\ttypes_release(f)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tcontinue\n\t\t\tcase /^&/:\n\t\t\t\tret = @f_idx(idx)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn ret\n\t\t\tdefault:\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tret = \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t\t\ttypes_release(f)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction eval(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'eval'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn EVAL(types_addref(types_heap[idx][1]), repl_env)\n}\n\nfunction main(str, ret, i, idx)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\tenv_set(repl_env, \"'eval\", \"&eval\")\n\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\n\tidx = types_allocate()\n\tenv_set(repl_env, \"'*ARGV*\", \"(\" idx)\n\tif (ARGC > 1) {\n\t\tfor (i = 2; i < ARGC; ++i) {\n\t\t\ttypes_heap[idx][i - 2] = \"\\\"\" ARGV[i]\n\t\t}\n\t\ttypes_heap[idx][\"len\"] = ARGC - 2\n\t\tARGC = 1\n\t\trep(\"(load-file \\\"\" ARGV[1] \"\\\")\")\n\t\treturn\n\t}\n\ttypes_heap[idx][\"len\"] = 0\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step8_macros.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\n# Return 0, an error or the unquote argument (second element of ast).\nfunction starts_with(ast, sym,    idx, len)\n{\n\tif (ast !~ /^\\(/)\n\t\treturn 0\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (!len || types_heap[idx][0] != sym)\n\t\treturn 0\n\tif (len != 2)\n\t\treturn  \"!\\\"'\" sym \"' expects 1 argument, not \" (len - 1) \".\"\n\treturn types_heap[idx][1]\n}\n\nfunction quasiquote(ast,    new_idx, ret, ast_idx, elt_i, elt, previous)\n{\n\tif (ast !~ /^[(['{]/) {\n\t\treturn ast\n\t}\n\tif (ast ~ /['\\{]/) {\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'quote\"\n\t\ttypes_heap[new_idx][1] = ast\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t\treturn \"(\" new_idx\n\t}\n\tret = starts_with(ast, \"'unquote\")\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tif (ret) {\n\t\ttypes_addref(ret)\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tnew_idx = types_allocate()\n\ttypes_heap[new_idx][\"len\"] = 0\n\tast_idx = substr(ast, 2)\n\tfor (elt_i=types_heap[ast_idx][\"len\"]-1; 0<=elt_i; elt_i--) {\n\t\telt = types_heap[ast_idx][elt_i]\n\t\tret = starts_with(elt, \"'splice-unquote\")\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(\"(\" new_idx)\n\t\t\ttypes_release(ast)\n\t\t\treturn ret\n\t\t}\n\t\tif (ret) {\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'concat\"\n\t\t\ttypes_heap[new_idx][1] = types_addref(ret)\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t} else {\n\t\t\tret = quasiquote(types_addref(elt))\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'cons\"\n\t\t\ttypes_heap[new_idx][1] = ret\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t}\n\t}\n\tif (ast ~ /^\\[/) {\n\t\tprevious = \"(\" new_idx\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'vec\"\n\t\ttypes_heap[new_idx][1] = previous\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t}\n\ttypes_release(ast)\n\treturn \"(\" new_idx\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    ret_env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret_env[0] = new_env\n\treturn body\n}\n\nfunction EVAL_defmacro(ast, env,    idx, sym, ret, len, fun_idx, mac_idx)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'defmacro!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'defmacro!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\ttypes_release(ast)\n\tif (ret ~ /^!/) {\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\tif (ret !~ /^\\$/) {\n\t\ttypes_release(ret)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 2 of 'defmacro!'. Expects function, supplied \" types_typename(ret) \".\"\n\t}\n\n\t# Replace `ret` with a clone setting the `is_macro` bit.\n\tfun_idx = substr(ret, 2)\n\tmac_idx = types_allocate()\n\ttypes_addref(types_heap[mac_idx][\"params\"] = types_heap[fun_idx][\"params\"])\n\ttypes_addref(types_heap[mac_idx][\"body\"] = types_heap[fun_idx][\"body\"])\n\tenv_addref(types_heap[mac_idx][\"env\"] = types_heap[fun_idx][\"env\"])\n\ttypes_heap[mac_idx][\"is_macro\"] = 1\n\ttypes_release(ret)\n\tret = \"$\" mac_idx\n\n\tenv_set(env, sym, ret)\n\ttypes_addref(ret)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, body, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\ttypes_addref(body = types_heap[idx][len - 1])\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\tbody = \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)\n{\n\tenv_addref(env)\n\tfor (;;) {\n\n\t\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\t\tcase /^!/:\n\t\tcase \"#nil\":\n\t\tcase \"#false\":\n\t\t\tbreak\n\t\tdefault:\n\t\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t\t}\n\n\t\tswitch (ast) {\n\t\tcase /^'/:      # symbol\n\t\t\tret = env_get(env, ast)\n\t\t\tif (ret !~ /^!/) {\n\t\t\t\ttypes_addref(ret)\n\t\t\t}\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\[/:     # vector\n\t\t\tret = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\{/:     # map\n\t\t\tret = eval_map(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^[^(]/:    # not a list\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tif (len == 0) {\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tswitch (types_heap[idx][0]) {\n\t\tcase \"'def!\":\n\t\t\treturn EVAL_def(ast, env)\n\t\tcase \"'let*\":\n\t\t\tast = EVAL_let(ast, env,    ret_env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'quote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn body\n\t\tcase \"'quasiquote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quasiquote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tast = quasiquote(body)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'defmacro!\":\n\t\t\treturn EVAL_defmacro(ast, env)\n\t\tcase \"'do\":\n\t\t\tast = EVAL_do(ast, env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'if\":\n\t\t\tast = EVAL_if(ast, env)\n\t\t\tif (ast !~ /^['([{]/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'fn*\":\n\t\t\treturn EVAL_fn(ast, env)\n\t\tdefault:\n\t\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\t\tif (f ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn f\n\t\t\t}\n\t\t\tf_idx = substr(f, 2)\n\t\t\tswitch (f) {\n\t\t\tcase /^\\$/:\n\t\t\t\tif (types_heap[f_idx][\"is_macro\"]) {\n\t\t\t\t\tidx = substr(ast, 2)\n\t\t\t\t\tret = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\t\ttypes_release(ast)\n\t\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\t\ttypes_release(f)\n\t\t\t\t\t\ttypes_release(env)\n\t\t\t\t\t\treturn ret\n\t\t\t\t\t}\n\t\t\t\t\tast = EVAL(types_addref(types_heap[f_idx][\"body\"]), ret)\n\t\t\t\t\ttypes_release(ret)\n\t\t\t\t\ttypes_release(f)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\t\treturn new_ast\n\t\t\t\t}\n\t\t\t\tidx = substr(new_ast, 2)\n\t\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\tif (env ~ /^!/) {\n\t\t\t\t\ttypes_release(new_ast)\n\t\t\t\t\treturn env\n\t\t\t\t}\n\t\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\t\ttypes_release(f)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tcontinue\n\t\t\tcase /^&/:\n\t\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\t\treturn new_ast\n\t\t\t\t}\n\t\t\t\tidx = substr(new_ast, 2)\n\t\t\t\tret = @f_idx(idx)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn ret\n\t\t\tdefault:\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tret = \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t\t\ttypes_release(f)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction eval(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'eval'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn EVAL(types_addref(types_heap[idx][1]), repl_env)\n}\n\nfunction main(str, ret, i, idx)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\tenv_set(repl_env, \"'eval\", \"&eval\")\n\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\trep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\tidx = types_allocate()\n\tenv_set(repl_env, \"'*ARGV*\", \"(\" idx)\n\tif (ARGC > 1) {\n\t\tfor (i = 2; i < ARGC; ++i) {\n\t\t\ttypes_heap[idx][i - 2] = \"\\\"\" ARGV[i]\n\t\t}\n\t\ttypes_heap[idx][\"len\"] = ARGC - 2\n\t\tARGC = 1\n\t\trep(\"(load-file \\\"\" ARGV[1] \"\\\")\")\n\t\treturn\n\t}\n\ttypes_heap[idx][\"len\"] = 0\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/step9_try.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\n# Return 0, an error or the unquote argument (second element of ast).\nfunction starts_with(ast, sym,    idx, len)\n{\n\tif (ast !~ /^\\(/)\n\t\treturn 0\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (!len || types_heap[idx][0] != sym)\n\t\treturn 0\n\tif (len != 2)\n\t\treturn  \"!\\\"'\" sym \"' expects 1 argument, not \" (len - 1) \".\"\n\treturn types_heap[idx][1]\n}\n\nfunction quasiquote(ast,    new_idx, ret, ast_idx, elt_i, elt, previous)\n{\n\tif (ast !~ /^[(['{]/) {\n\t\treturn ast\n\t}\n\tif (ast ~ /['\\{]/) {\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'quote\"\n\t\ttypes_heap[new_idx][1] = ast\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t\treturn \"(\" new_idx\n\t}\n\tret = starts_with(ast, \"'unquote\")\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tif (ret) {\n\t\ttypes_addref(ret)\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tnew_idx = types_allocate()\n\ttypes_heap[new_idx][\"len\"] = 0\n\tast_idx = substr(ast, 2)\n\tfor (elt_i=types_heap[ast_idx][\"len\"]-1; 0<=elt_i; elt_i--) {\n\t\telt = types_heap[ast_idx][elt_i]\n\t\tret = starts_with(elt, \"'splice-unquote\")\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(\"(\" new_idx)\n\t\t\ttypes_release(ast)\n\t\t\treturn ret\n\t\t}\n\t\tif (ret) {\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'concat\"\n\t\t\ttypes_heap[new_idx][1] = types_addref(ret)\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t} else {\n\t\t\tret = quasiquote(types_addref(elt))\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'cons\"\n\t\t\ttypes_heap[new_idx][1] = ret\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t}\n\t}\n\tif (ast ~ /^\\[/) {\n\t\tprevious = \"(\" new_idx\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'vec\"\n\t\ttypes_heap[new_idx][1] = previous\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t}\n\ttypes_release(ast)\n\treturn \"(\" new_idx\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    ret_env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret_env[0] = new_env\n\treturn body\n}\n\nfunction EVAL_defmacro(ast, env,    idx, sym, ret, len, fun_idx, mac_idx)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'defmacro!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'defmacro!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\ttypes_release(ast)\n\tif (ret ~ /^!/) {\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\tif (ret !~ /^\\$/) {\n\t\ttypes_release(ret)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 2 of 'defmacro!'. Expects function, supplied \" types_typename(ret) \".\"\n\t}\n\n\t# Replace `ret` with a clone setting the `is_macro` bit.\n\tfun_idx = substr(ret, 2)\n\tmac_idx = types_allocate()\n\ttypes_addref(types_heap[mac_idx][\"params\"] = types_heap[fun_idx][\"params\"])\n\ttypes_addref(types_heap[mac_idx][\"body\"] = types_heap[fun_idx][\"body\"])\n\tenv_addref(types_heap[mac_idx][\"env\"] = types_heap[fun_idx][\"env\"])\n\ttypes_heap[mac_idx][\"is_macro\"] = 1\n\ttypes_release(ret)\n\tret = \"$\" mac_idx\n\n\tenv_set(env, sym, ret)\n\ttypes_addref(ret)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_try(ast, env,    catch_body, catch_env,    idx, catch, catch_idx, catch_sym, ret, len, str)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 2 && len != 3) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'try*'. Expects 1 or 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tif (len == 2) {\n\t\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\tcatch = types_heap[idx][2]\n\tif (catch !~ /^\\(/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 2 of 'try*'. Expects list, supplied \" types_typename(catch) \".\"\n\t}\n\tcatch_idx = substr(catch, 2)\n\tif (types_heap[catch_idx][\"len\"] != 3) {\n\t\tlen = types_heap[catch_idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 2 of 'try*'. Expects exactly 3 elements, supplied \" len \".\"\n\t}\n\tif (types_heap[catch_idx][0] != \"'catch*\") {\n\t\tstr = printer_pr_str(types_heap[catch_idx][0])\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid first element of argument 2 of 'try*'. Expects symbol 'catch*', supplied '\" str \"'.\"\n\t}\n\tcatch_sym = types_heap[catch_idx][1]\n\tif (catch_sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for second element of argument 2 of 'try*'. Expects symbol, supplied \" types_typename(catch_sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret !~ /^!/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\ttypes_addref(catch_body[0] = types_heap[catch_idx][2])\n\tcatch_env[0] = env_new(env)\n\tenv_release(env)\n\tenv_set(catch_env[0], catch_sym, substr(ret, 2))\n\ttypes_release(ast)\n\treturn \"\"\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, body, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\ttypes_addref(body = types_heap[idx][len - 1])\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\tbody = \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)\n{\n\tenv_addref(env)\n\tfor (;;) {\n\n\t\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\t\tcase /^!/:\n\t\tcase \"#nil\":\n\t\tcase \"#false\":\n\t\t\tbreak\n\t\tdefault:\n\t\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t\t}\n\n\t\tswitch (ast) {\n\t\tcase /^'/:      # symbol\n\t\t\tret = env_get(env, ast)\n\t\t\tif (ret !~ /^!/) {\n\t\t\t\ttypes_addref(ret)\n\t\t\t}\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\[/:     # vector\n\t\t\tret = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\{/:     # map\n\t\t\tret = eval_map(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^[^(]/:    # not a list\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tif (len == 0) {\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tswitch (types_heap[idx][0]) {\n\t\tcase \"'def!\":\n\t\t\treturn EVAL_def(ast, env)\n\t\tcase \"'let*\":\n\t\t\tast = EVAL_let(ast, env,    ret_env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'quote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn body\n\t\tcase \"'quasiquote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quasiquote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tast = quasiquote(body)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'defmacro!\":\n\t\t\treturn EVAL_defmacro(ast, env)\n\t\tcase \"'try*\":\n\t\t\tret = EVAL_try(ast, env,    ret_body, ret_env)\n\t\t\tif (ret != \"\") {\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tast = ret_body[0]\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'do\":\n\t\t\tast = EVAL_do(ast, env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'if\":\n\t\t\tast = EVAL_if(ast, env)\n\t\t\tif (ast !~ /^['([{]/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'fn*\":\n\t\t\treturn EVAL_fn(ast, env)\n\t\tdefault:\n\t\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\t\tif (f ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn f\n\t\t\t}\n\t\t\tf_idx = substr(f, 2)\n\t\t\tswitch (f) {\n\t\t\tcase /^\\$/:\n\t\t\t\tif (types_heap[f_idx][\"is_macro\"]) {\n\t\t\t\t\tidx = substr(ast, 2)\n\t\t\t\t\tret = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\t\ttypes_release(ast)\n\t\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\t\ttypes_release(f)\n\t\t\t\t\t\ttypes_release(env)\n\t\t\t\t\t\treturn ret\n\t\t\t\t\t}\n\t\t\t\t\tast = EVAL(types_addref(types_heap[f_idx][\"body\"]), ret)\n\t\t\t\t\ttypes_release(ret)\n\t\t\t\t\ttypes_release(f)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\t\treturn new_ast\n\t\t\t\t}\n\t\t\t\tidx = substr(new_ast, 2)\n\t\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\tif (env ~ /^!/) {\n\t\t\t\t\ttypes_release(new_ast)\n\t\t\t\t\treturn env\n\t\t\t\t}\n\t\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\t\ttypes_release(f)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tcontinue\n\t\t\tcase /^&/:\n\t\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\t\treturn new_ast\n\t\t\t\t}\n\t\t\t\tidx = substr(new_ast, 2)\n\t\t\t\tret = @f_idx(idx)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn ret\n\t\t\tdefault:\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tret = \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t\t\ttypes_release(f)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction eval(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'eval'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn EVAL(types_addref(types_heap[idx][1]), repl_env)\n}\n\nfunction main(str, ret, i, idx)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\tenv_set(repl_env, \"'eval\", \"&eval\")\n\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\trep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\tidx = types_allocate()\n\tenv_set(repl_env, \"'*ARGV*\", \"(\" idx)\n\tif (ARGC > 1) {\n\t\tfor (i = 2; i < ARGC; ++i) {\n\t\t\ttypes_heap[idx][i - 2] = \"\\\"\" ARGV[i]\n\t\t}\n\t\ttypes_heap[idx][\"len\"] = ARGC - 2\n\t\tARGC = 1\n\t\trep(\"(load-file \\\"\" ARGV[1] \"\\\")\")\n\t\treturn\n\t}\n\ttypes_heap[idx][\"len\"] = 0\n\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/stepA_mal.awk",
    "content": "@include \"types.awk\"\n@include \"reader.awk\"\n@include \"printer.awk\"\n@include \"env.awk\"\n@include \"core.awk\"\n\nfunction READ(str)\n{\n\treturn reader_read_str(str)\n}\n\n# Return 0, an error or the unquote argument (second element of ast).\nfunction starts_with(ast, sym,    idx, len)\n{\n\tif (ast !~ /^\\(/)\n\t\treturn 0\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (!len || types_heap[idx][0] != sym)\n\t\treturn 0\n\tif (len != 2)\n\t\treturn  \"!\\\"'\" sym \"' expects 1 argument, not \" (len - 1) \".\"\n\treturn types_heap[idx][1]\n}\n\nfunction quasiquote(ast,    new_idx, ret, ast_idx, elt_i, elt, previous)\n{\n\tif (ast !~ /^[(['{]/) {\n\t\treturn ast\n\t}\n\tif (ast ~ /['\\{]/) {\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'quote\"\n\t\ttypes_heap[new_idx][1] = ast\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t\treturn \"(\" new_idx\n\t}\n\tret = starts_with(ast, \"'unquote\")\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tif (ret) {\n\t\ttypes_addref(ret)\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\tnew_idx = types_allocate()\n\ttypes_heap[new_idx][\"len\"] = 0\n\tast_idx = substr(ast, 2)\n\tfor (elt_i=types_heap[ast_idx][\"len\"]-1; 0<=elt_i; elt_i--) {\n\t\telt = types_heap[ast_idx][elt_i]\n\t\tret = starts_with(elt, \"'splice-unquote\")\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(\"(\" new_idx)\n\t\t\ttypes_release(ast)\n\t\t\treturn ret\n\t\t}\n\t\tif (ret) {\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'concat\"\n\t\t\ttypes_heap[new_idx][1] = types_addref(ret)\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t} else {\n\t\t\tret = quasiquote(types_addref(elt))\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tprevious = \"(\" new_idx\n\t\t\tnew_idx = types_allocate()\n\t\t\ttypes_heap[new_idx][0] = \"'cons\"\n\t\t\ttypes_heap[new_idx][1] = ret\n\t\t\ttypes_heap[new_idx][2] = previous\n\t\t\ttypes_heap[new_idx][\"len\"] = 3\n\t\t}\n\t}\n\tif (ast ~ /^\\[/) {\n\t\tprevious = \"(\" new_idx\n\t\tnew_idx = types_allocate()\n\t\ttypes_heap[new_idx][0] = \"'vec\"\n\t\ttypes_heap[new_idx][1] = previous\n\t\ttypes_heap[new_idx][\"len\"] = 2\n\t}\n\ttypes_release(ast)\n\treturn \"(\" new_idx\n}\n\nfunction eval_ast(ast, env,    i, idx, len, new_idx, ret)\n# This function has two distinct purposes.\n# non empty list: a0 a1 .. an  ->  list: nil (eval a1) .. (eval an)\n# vector: a0 a1 .. an          ->  vector: (eval a0) (eval a1) .. (eval an)\n{\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tnew_idx = types_allocate()\n\t\tif (ast ~ /^\\(/) {\n\t\t\ttypes_heap[new_idx][0] = \"#nil\"\n\t\t\ti = 1\n\t\t} else {\n\t\t\ti = 0\n\t\t}\n\t\tfor (; i < len; ++i) {\n\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\tif (ret ~ /^!/) {\n\t\t\t\ttypes_heap[new_idx][\"len\"] = i\n\t\t\t\ttypes_release(substr(ast, 1, 1) new_idx)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\ttypes_heap[new_idx][i] = ret\n\t\t}\n\t\ttypes_heap[new_idx][\"len\"] = len\n\t\treturn substr(ast, 1, 1) new_idx\n}\n\nfunction eval_map(ast, env,    i, idx, new_idx, ret)\n{\n\t\tidx = substr(ast, 2)\n\t\tnew_idx = types_allocate()\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\ttypes_release(\"{\" new_idx)\n\t\t\t\t\treturn ret\n\t\t\t\t}\n\t\t\t\ttypes_heap[new_idx][i] = ret\n\t\t\t}\n\t\t}\n\t\treturn \"{\" new_idx\n}\n\nfunction EVAL_def(ast, env,    idx, sym, ret, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\tif (ret !~ /^!/) {\n\t\tenv_set(env, sym, ret)\n\t\ttypes_addref(ret)\n\t}\n\ttypes_release(ast)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_let(ast, env,    ret_env,    idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tif (params_len % 2 != 0) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied \" params_len \".\"\n\t}\n\tnew_env = env_new(env)\n\tenv_release(env)\n\tfor (i = 0; i < params_len; i += 2) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn \"!\\\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(new_env)\n\t\t\treturn ret\n\t\t}\n\t\tenv_set(new_env, sym, ret)\n\t}\n\ttypes_addref(body = types_heap[idx][2])\n\ttypes_release(ast)\n\tret_env[0] = new_env\n\treturn body\n}\n\nfunction EVAL_defmacro(ast, env,    idx, sym, ret, len, fun_idx, mac_idx)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'defmacro!'. Expects exactly 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tsym = types_heap[idx][1]\n\tif (sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'defmacro!'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][2]), env)\n\ttypes_release(ast)\n\tif (ret ~ /^!/) {\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\tif (ret !~ /^\\$/) {\n\t\ttypes_release(ret)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 2 of 'defmacro!'. Expects function, supplied \" types_typename(ret) \".\"\n\t}\n\n\t# Replace `ret` with a clone setting the `is_macro` bit.\n\tfun_idx = substr(ret, 2)\n\tmac_idx = types_allocate()\n\ttypes_addref(types_heap[mac_idx][\"params\"] = types_heap[fun_idx][\"params\"])\n\ttypes_addref(types_heap[mac_idx][\"body\"] = types_heap[fun_idx][\"body\"])\n\tenv_addref(types_heap[mac_idx][\"env\"] = types_heap[fun_idx][\"env\"])\n\ttypes_heap[mac_idx][\"is_macro\"] = 1\n\ttypes_release(ret)\n\tret = \"$\" mac_idx\n\n\tenv_set(env, sym, ret)\n\ttypes_addref(ret)\n\tenv_release(env)\n\treturn ret\n}\n\nfunction EVAL_try(ast, env,    catch_body, catch_env,    idx, catch, catch_idx, catch_sym, ret, len, str)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 2 && len != 3) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'try*'. Expects 1 or 2 arguments, supplied\" (len - 1) \".\"\n\t}\n\tif (len == 2) {\n\t\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\tcatch = types_heap[idx][2]\n\tif (catch !~ /^\\(/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 2 of 'try*'. Expects list, supplied \" types_typename(catch) \".\"\n\t}\n\tcatch_idx = substr(catch, 2)\n\tif (types_heap[catch_idx][\"len\"] != 3) {\n\t\tlen = types_heap[catch_idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid elements count for argument 2 of 'try*'. Expects exactly 3 elements, supplied \" len \".\"\n\t}\n\tif (types_heap[catch_idx][0] != \"'catch*\") {\n\t\tstr = printer_pr_str(types_heap[catch_idx][0])\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid first element of argument 2 of 'try*'. Expects symbol 'catch*', supplied '\" str \"'.\"\n\t}\n\tcatch_sym = types_heap[catch_idx][1]\n\tif (catch_sym !~ /^'/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for second element of argument 2 of 'try*'. Expects symbol, supplied \" types_typename(catch_sym) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret !~ /^!/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn ret\n\t}\n\ttypes_addref(catch_body[0] = types_heap[catch_idx][2])\n\tcatch_env[0] = env_new(env)\n\tenv_release(env)\n\tenv_set(catch_env[0], catch_sym, substr(ret, 2))\n\ttypes_release(ast)\n\treturn \"\"\n}\n\nfunction EVAL_do(ast, env,    idx, len, i, body, ret)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len == 1) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'do'. Expects at least 1 argument, supplied\" (len - 1) \".\"\n\t}\n\tfor (i = 1; i < len - 1; ++i) {\n\t\tret = EVAL(types_addref(types_heap[idx][i]), env)\n\t\tif (ret ~ /^!/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\t}\n\t\ttypes_release(ret)\n\t}\n\ttypes_addref(body = types_heap[idx][len - 1])\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_if(ast, env,    idx, len, ret, body)\n{\n\tidx = substr(ast, 2)\n\tlen = types_heap[idx][\"len\"]\n\tif (len != 3 && len != 4) {\n\t\ttypes_release(ast)\n\t\treturn \"!\\\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied \" (len - 1) \".\"\n\t}\n\tret = EVAL(types_addref(types_heap[idx][1]), env)\n\tif (ret ~ /^!/) {\n\t\ttypes_release(ast)\n\t\treturn ret\n\t}\n\ttypes_release(ret)\n\tswitch (ret) {\n\tcase \"#nil\":\n\tcase \"#false\":\n\t\tif (len == 3) {\n\t\t\tbody = \"#nil\"\n\t\t} else {\n\t\t\ttypes_addref(body = types_heap[idx][3])\n\t\t}\n\t\tbreak\n\tdefault:\n\t\ttypes_addref(body = types_heap[idx][2])\n\t\tbreak\n\t}\n\ttypes_release(ast)\n\treturn body\n}\n\nfunction EVAL_fn(ast, env,    idx, params, params_idx, params_len, i, sym, f_idx, len)\n{\n\tidx = substr(ast, 2)\n\tif (types_heap[idx][\"len\"] != 3) {\n\t\tlen = types_heap[idx][\"len\"]\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied \" (len - 1) \".\"\n\t}\n\tparams = types_heap[idx][1]\n\tif (params !~ /^[([]/) {\n\t\ttypes_release(ast)\n\t\tenv_release(env)\n\t\treturn \"!\\\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied \" types_typename(params) \".\"\n\t}\n\tparams_idx = substr(params, 2)\n\tparams_len = types_heap[params_idx][\"len\"]\n\tfor (i = 0; i < params_len; ++i) {\n\t\tsym = types_heap[params_idx][i]\n\t\tif (sym !~ /^'/) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied \" types_typename(sym) \".\"\n\t\t}\n\t\tif (sym == \"'&\" && i + 2 != params_len) {\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn \"!\\\"Symbol '&' should be followed by last parameter. Parameter list length is \" params_len \", position of symbol '&' is \" (i + 1) \".\"\n\t\t}\n\t}\n\tf_idx = types_allocate()\n\ttypes_addref(types_heap[f_idx][\"params\"] = types_heap[idx][1])\n\ttypes_addref(types_heap[f_idx][\"body\"] = types_heap[idx][2])\n\ttypes_heap[f_idx][\"env\"] = env\n\ttypes_release(ast)\n\treturn \"$\" f_idx\n}\n\nfunction EVAL(ast, env,    body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)\n{\n\tenv_addref(env)\n\tfor (;;) {\n\n\t\tswitch (env_get(env, \"'DEBUG-EVAL\")) {\n\t\tcase /^!/:\n\t\tcase \"#nil\":\n\t\tcase \"#false\":\n\t\t\tbreak\n\t\tdefault:\n\t\t\tprint \"EVAL: \" printer_pr_str(ast, 1)\n\t\t}\n\n\t\tswitch (ast) {\n\t\tcase /^'/:      # symbol\n\t\t\tret = env_get(env, ast)\n\t\t\tif (ret !~ /^!/) {\n\t\t\t\ttypes_addref(ret)\n\t\t\t}\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\[/:     # vector\n\t\t\tret = eval_ast(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^\\{/:     # map\n\t\t\tret = eval_map(ast, env)\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ret\n\t\tcase /^[^(]/:    # not a list\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tidx = substr(ast, 2)\n\t\tlen = types_heap[idx][\"len\"]\n\t\tif (len == 0) {\n\t\t\tenv_release(env)\n\t\t\treturn ast\n\t\t}\n\t\tswitch (types_heap[idx][0]) {\n\t\tcase \"'def!\":\n\t\t\treturn EVAL_def(ast, env)\n\t\tcase \"'let*\":\n\t\t\tast = EVAL_let(ast, env,    ret_env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'quote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tenv_release(env)\n\t\t\treturn body\n\t\tcase \"'quasiquote\":\n\t\t\tif (len != 2) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn \"!\\\"Invalid argument length for 'quasiquote'. Expects exactly 1 argument, supplied \" (len - 1) \".\"\n\t\t\t}\n\t\t\ttypes_addref(body = types_heap[idx][1])\n\t\t\ttypes_release(ast)\n\t\t\tast = quasiquote(body)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'defmacro!\":\n\t\t\treturn EVAL_defmacro(ast, env)\n\t\tcase \"'try*\":\n\t\t\tret = EVAL_try(ast, env,    ret_body, ret_env)\n\t\t\tif (ret != \"\") {\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tast = ret_body[0]\n\t\t\tenv = ret_env[0]\n\t\t\tcontinue\n\t\tcase \"'do\":\n\t\t\tast = EVAL_do(ast, env)\n\t\t\tif (ast ~ /^!/) {\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'if\":\n\t\t\tast = EVAL_if(ast, env)\n\t\t\tif (ast !~ /^['([{]/) {\n\t\t\t\tenv_release(env)\n\t\t\t\treturn ast\n\t\t\t}\n\t\t\tcontinue\n\t\tcase \"'fn*\":\n\t\t\treturn EVAL_fn(ast, env)\n\t\tdefault:\n\t\t\tf = EVAL(types_addref(types_heap[idx][0]), env)\n\t\t\tif (f ~ /^!/) {\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\treturn f\n\t\t\t}\n\t\t\tf_idx = substr(f, 2)\n\t\t\tswitch (f) {\n\t\t\tcase /^\\$/:\n\t\t\t\tif (types_heap[f_idx][\"is_macro\"]) {\n\t\t\t\t\tidx = substr(ast, 2)\n\t\t\t\t\tret = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\t\ttypes_release(ast)\n\t\t\t\t\tif (ret ~ /^!/) {\n\t\t\t\t\t\ttypes_release(f)\n\t\t\t\t\t\ttypes_release(env)\n\t\t\t\t\t\treturn ret\n\t\t\t\t\t}\n\t\t\t\t\tast = EVAL(types_addref(types_heap[f_idx][\"body\"]), ret)\n\t\t\t\t\ttypes_release(ret)\n\t\t\t\t\ttypes_release(f)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\t\treturn new_ast\n\t\t\t\t}\n\t\t\t\tidx = substr(new_ast, 2)\n\t\t\t\tenv = env_new(types_heap[f_idx][\"env\"], types_heap[f_idx][\"params\"], idx)\n\t\t\t\tif (env ~ /^!/) {\n\t\t\t\t\ttypes_release(new_ast)\n\t\t\t\t\treturn env\n\t\t\t\t}\n\t\t\t\ttypes_addref(ast = types_heap[f_idx][\"body\"])\n\t\t\t\ttypes_release(f)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tcontinue\n\t\t\tcase /^%/:\n\t\t\t\tf_idx = types_heap[f_idx][\"func\"]\n\t\t\t\ttypes_release(f)\n\t\t\tcase /^&/:\n\t\t\t\tnew_ast = eval_ast(ast, env)\n\t\t\t\ttypes_release(ast)\n\t\t\t\tenv_release(env)\n\t\t\t\tif (new_ast ~ /^!/) {\n\t\t\t\t\treturn new_ast\n\t\t\t\t}\n\t\t\t\tidx = substr(new_ast, 2)\n\t\t\t\tret = @f_idx(idx)\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\treturn ret\n\t\t\tdefault:\n\t\t\t\ttypes_release(new_ast)\n\t\t\t\tret = \"!\\\"First element of list must be function, supplied \" types_typename(f) \".\"\n\t\t\t\ttypes_release(f)\n\t\t\t\treturn ret\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction PRINT(expr,    str)\n{\n\tstr = printer_pr_str(expr, 1)\n\ttypes_release(expr)\n\treturn str\n}\n\nfunction rep(str,    ast, expr)\n{\n\tast = READ(str)\n\tif (ast ~ /^!/) {\n\t\treturn ast\n\t}\n\texpr = EVAL(ast, repl_env)\n\tif (expr ~ /^!/) {\n\t\treturn expr\n\t}\n\treturn PRINT(expr)\n}\n\nfunction eval(idx)\n{\n\tif (types_heap[idx][\"len\"] != 2) {\n\t\treturn \"!\\\"Invalid argument length for builtin function 'eval'. Expects exactly 1 argument, supplied \" (types_heap[idx][\"len\"] - 1) \".\"\n\t}\n\treturn EVAL(types_addref(types_heap[idx][1]), repl_env)\n}\n\nfunction main(str, ret, i, idx)\n{\n\trepl_env = env_new()\n\tfor (i in core_ns) {\n\t\tenv_set(repl_env, i, core_ns[i])\n\t}\n\n\tenv_set(repl_env, \"'eval\", \"&eval\")\n\n\trep(\"(def! *host-language* \\\"GNU awk\\\")\")\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\trep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\tidx = types_allocate()\n\tenv_set(repl_env, \"'*ARGV*\", \"(\" idx)\n\tif (ARGC > 1) {\n\t\tfor (i = 2; i < ARGC; ++i) {\n\t\t\ttypes_heap[idx][i - 2] = \"\\\"\" ARGV[i]\n\t\t}\n\t\ttypes_heap[idx][\"len\"] = ARGC - 2\n\t\tARGC = 1\n\t\trep(\"(load-file \\\"\" ARGV[1] \"\\\")\")\n\t\treturn\n\t}\n\ttypes_heap[idx][\"len\"] = 0\n\n\trep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n\twhile (1) {\n\t\tprintf(\"user> \")\n\t\tif (getline str <= 0) {\n\t\t\tbreak\n\t\t}\n\t\tret = rep(str)\n\t\tif (ret ~ /^!/) {\n\t\t\tprint \"ERROR: \" printer_pr_str(substr(ret, 2))\n\t\t} else {\n\t\t\tprint ret\n\t\t}\n\t}\n}\n\nBEGIN {\n\tmain()\n\tenv_check(0)\n\t#env_dump()\n\t#types_dump()\n\texit(0)\n}\n"
  },
  {
    "path": "impls/awk/tests/step5_tco.mal",
    "content": ";; awk: skipping non-TCO recursion\n;; Reason: completes up to 50,000\n"
  },
  {
    "path": "impls/awk/types.awk",
    "content": "\n# string\"\n# symbol  '\n# keyword :\n# number  +\n# nil     #\n# true    #\n# false   #\n# list    (\n# vector  [\n# hash    {\n# atom    ?\n# builtin function           &\n# builtin function with meta %\n# user defined function      $\n\nfunction types_allocate()\n{\n\ttypes_heap[types_heap_index][\"ref\"] = 1\n\treturn types_heap_index++\n}\n\nfunction types_addref(ast)\n{\n\tif (ast ~ /^[([{$%?]/) {\n\t\t++types_heap[substr(ast, 2)][\"ref\"]\n\t}\n\treturn ast\n}\n\nfunction types_release(ast,    idx, ref, i, len)\n{\n\tswitch (ast) {\n\tcase /^[([]/:\n\t\tidx = substr(ast, 2)\n\t\tref = --types_heap[idx][\"ref\"]\n\t\tif (ref <= 0) {\n\t\t\tif (ref < 0) {\n\t\t\t\tprint \"ref count error:\" ast \", \" ref\n\t\t\t}\n\t\t\tlen = types_heap[idx][\"len\"]\n\t\t\tfor (i = 0; i < len; ++i) {\n\t\t\t\ttypes_release(types_heap[idx][i])\n\t\t\t}\n\t\t\ttypes_release(types_heap[idx][\"meta\"])\n\t\t\tdelete types_heap[idx]\n\t\t}\n\t\treturn\n\tcase /^\\{/:\n\t\tidx = substr(ast, 2)\n\t\tref = --types_heap[idx][\"ref\"]\n\t\tif (ref <= 0) {\n\t\t\tif (ref < 0) {\n\t\t\t\tprint \"ref count error:\" ast \", \" ref\n\t\t\t}\n\t\t\tfor (i in types_heap[idx]) {\n\t\t\t\tif (i ~ /^[\":]/) {\n\t\t\t\t\ttypes_release(types_heap[idx][i])\n\t\t\t\t}\n\t\t\t}\n\t\t\ttypes_release(types_heap[idx][\"meta\"])\n\t\t\tdelete types_heap[idx]\n\t\t}\n\t\treturn\n\tcase /^\\$/:\n\t\tidx = substr(ast, 2)\n\t\tref = --types_heap[idx][\"ref\"]\n\t\tif (ref <= 0) {\n\t\t\tif (ref < 0) {\n\t\t\t\tprint \"ref count error:\" ast \", \" ref\n\t\t\t}\n\t\t\ttypes_release(types_heap[idx][\"params\"])\n\t\t\ttypes_release(types_heap[idx][\"body\"])\n\t\t\ttypes_release(types_heap[idx][\"meta\"])\n\t\t\tenv_release(types_heap[idx][\"env\"])\n\t\t\tdelete types_heap[idx]\n\t\t}\n\t\treturn\n\tcase /^%/:\n\t\tidx = substr(ast, 2)\n\t\tref = --types_heap[idx][\"ref\"]\n\t\tif (ref <= 0) {\n\t\t\tif (ref < 0) {\n\t\t\t\tprint \"ref count error:\" ast \", \" ref\n\t\t\t}\n\t\t\ttypes_release(types_heap[idx][\"meta\"])\n\t\t\tdelete types_heap[idx]\n\t\t}\n\t\treturn\n\tcase /^\\?/:\n\t\tidx = substr(ast, 2)\n\t\tref = --types_heap[idx][\"ref\"]\n\t\tif (ref <= 0) {\n\t\t\tif (ref < 0) {\n\t\t\t\tprint \"ref count error:\" ast \", \" ref\n\t\t\t}\n\t\t\ttypes_release(types_heap[idx][\"obj\"])\n\t\t\tdelete types_heap[idx]\n\t\t}\n\t}\n}\n\nfunction types_check(val,    idx, len, i)\n{\n\tif (val !~ /^[([{?%$]/) {\n\t\treturn\n\t}\n\tidx = substr(val, 2)\n\tif (!(idx in types_heap)) {\n\t\tprint \"dangling reference \" val\n\t\treturn\n\t}\n\tif (types_heap[idx][\"checked\"]++) {\n\t\treturn\n\t}\n\t#types_heap[idx][\"checked\"] = 1\n\tswitch (val) {\n\tcase /^[([]/:\n\t\tif (!(\"len\" in types_heap[idx])) {\n\t\t\tprint \"length not found in \" val\n\t\t\treturn\n\t\t}\n\t\tlen = types_heap[idx][\"len\"]\n\t\tfor (i = 0; i < len; ++i) {\n\t\t\tif (!(i in types_heap[idx])) {\n\t\t\t\tprint \"sequence corrupted in \" val \" of \" i\n\t\t\t} else {\n\t\t\t\ttypes_check(types_heap[idx][i])\n\t\t\t}\n\t\t}\n\t\ttypes_check(types_heap[idx][\"meta\"])\n\t\treturn\n\tcase /^\\{/:\n\t\tfor (i in types_heap[idx]) {\n\t\t\tif (i != \"ref\") {\n\t\t\t\ttypes_check(types_heap[idx][i])\n\t\t\t}\n\t\t}\n\t\treturn\n\tcase /^\\?/:\n\t\tif (!(\"obj\" in types_heap[idx])) {\n\t\t\tprint \"atom corrupted in \" val\n\t\t} else {\n\t\t\ttypes_check(types_heap[idx][\"obj\"])\n\t\t}\n\t\ttypes_check(types_heap[idx][\"meta\"])\n\t\treturn\n\tcase /^%/:\n\t\tif (!(\"func\" in types_heap[idx])) {\n\t\t\tprint \"function corrupted in \" val\n\t\t} else {\n\t\t\ttypes_check(types_heap[idx][\"func\"])\n\t\t}\n\t\ttypes_check(types_heap[idx][\"meta\"])\n\t\treturn\n\tcase /^\\$/:\n\t\tif (!(\"body\" in types_heap[idx])) {\n\t\t\tprint \"function body corrupted in \" val\n\t\t} else {\n\t\t\ttypes_check(types_heap[idx][\"body\"])\n\t\t}\n\t\tif (!(\"params\" in types_heap[idx])) {\n\t\t\tprint \"function params corrupted in \" val\n\t\t} else {\n\t\t\ttypes_check(types_heap[idx][\"params\"])\n\t\t}\n\t\tif (!(\"env\" in types_heap[idx])) {\n\t\t\tprint \"function env corrupted in \" val\n\t\t} else {\n\t\t\tenv_check(types_heap[idx][\"env\"])\n\t\t}\n\t\ttypes_check(types_heap[idx][\"meta\"])\n\t\treturn\n\tdefault:\n\t\tprint \"unknown type \" val\n\t\treturn\n\t}\n}\n\nfunction types_dump(i, j)\n{\n\tfor (i = 0; i < types_heap_index; i++) {\n\t\tif (i in types_heap) {\n\t\t\tif (isarray(types_heap[i])) {\n\t\t\t\tif (!(\"checked\" in types_heap[i]) || types_heap[i][\"checked\"] != types_heap[i][\"ref\"]) {\n\t\t\t\t\tfor (j in types_heap[i]) {\n\t\t\t\t\t\tprint \"  types_heap[\" i \"][\" j \"] = \" types_heap[i][j]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprint \"  types_heap[\" i \"] = \" types_heap[i]\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction types_typename(str)\n{\n\tswitch (str) {\n\tcase /^\"/:        return \"string\"\n\tcase /^'/:        return \"symbol\"\n\tcase /^:/:        return \"keyword\"\n\tcase /^\\+/:       return \"number\"\n\tcase /^#nil$/:    return \"nil\"\n\tcase /^#true$/:   return \"true\"\n\tcase /^#false$/:  return \"false\"\n\tcase /^\\(/:       return \"list\"\n\tcase /^\\[/:       return \"vector\"\n\tcase /^\\{/:       return \"hash\"\n\tcase /^\\?/:       return \"atom\"\n\tcase /^[&%]/:     return \"builtin function\"\n\tcase /^\\$/:       return \"user defined function\"\n\t}\n}\n\nBEGIN {\n\ttypes_heap_index = 0\n}\n"
  },
  {
    "path": "impls/bash/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Nothing additional needed for bash\n"
  },
  {
    "path": "impls/bash/Makefile",
    "content": "SOURCES_BASE = types.sh reader.sh printer.sh\nSOURCES_LISP = env.sh core.sh stepA_mal.sh\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.sh mal\n\nmal.sh: $(SOURCES)\n\tcat $+ | grep -v \"^source \" > $@\n\nmal: mal.sh\n\techo \"#!/usr/bin/env bash\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.sh mal\n"
  },
  {
    "path": "impls/bash/core.sh",
    "content": "#\n# mal (Make a Lisp) object types\n#\n\nif [ -z \"${__mal_core_included__}\" ]; then\n__mal_core_included=true\n\nsource $(dirname $0)/types.sh\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\n\n# Exceptions/Errors\n\nthrow() {\n    __ERROR=\"${1}\"\n    r=\n}\n\n\n# General functions\n\nobj_type () {\n    _obj_type \"${1}\"\n    _string \"${r}\"\n}\n\nequal? () {\n    _equal? \"${1}\" \"${2}\" && r=\"${__true}\" || r=\"${__false}\"\n}\n\n\n# Scalar functions\n\nnil? () { _nil? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\ntrue? () { _true? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\nfalse? () { _false? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\n\n# Symbol functions\n\nsymbol () { _symbol \"${ANON[\"${1}\"]}\"; }\n\nsymbol? () { _symbol? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\n\n# Keyword functions\n\nkeyword () { _keyword \"${ANON[\"${1}\"]}\"; }\n\nkeyword? () { _keyword? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\n\n# Number functions\n\nnumber? () { _number? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\nnum_plus     () { r=$(( ${ANON[\"${1}\"]} + ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nnum_minus    () { r=$(( ${ANON[\"${1}\"]} - ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nnum_multiply () { r=$(( ${ANON[\"${1}\"]} * ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nnum_divide   () { r=$(( ${ANON[\"${1}\"]} / ${ANON[\"${2}\"]} )); _number \"${r}\"; }\n\n_num_bool     () { [[ \"${1}\" = \"1\" ]] && r=\"${__true}\" || r=\"${__false}\"; }\nnum_gt       () { r=$(( ${ANON[\"${1}\"]} >  ${ANON[\"${2}\"]} )); _num_bool \"${r}\"; }\nnum_gte      () { r=$(( ${ANON[\"${1}\"]} >= ${ANON[\"${2}\"]} )); _num_bool \"${r}\"; }\nnum_lt       () { r=$(( ${ANON[\"${1}\"]} <  ${ANON[\"${2}\"]} )); _num_bool \"${r}\"; }\nnum_lte      () { r=$(( ${ANON[\"${1}\"]} <= ${ANON[\"${2}\"]} )); _num_bool \"${r}\"; }\n\n# return number of milliseconds since epoch\ntime_ms () {\n    local ms=$(date +%s%3N)\n    _number \"${ms}\"\n}\n\n\n# String functions\n\nstring? () { _string? \"${1}\" && ( ! _keyword? \"${1}\" ) && r=\"${__true}\" || r=\"${__false}\"; }\n\npr_str () {\n    local res=\"\"\n    for x in \"${@}\"; do _pr_str \"${x}\" yes; res=\"${res} ${r}\"; done\n    _string \"${res:1}\"\n}\n\nstr () {\n    local res=\"\"\n    for x in \"${@}\"; do _pr_str \"${x}\"; res=\"${res}${r}\"; done\n    _string \"${res}\"\n}\n\nprn () {\n    local res=\"\"\n    for x in \"${@}\"; do _pr_str \"${x}\" yes; res=\"${res} ${r}\"; done\n    echo \"${res:1}\"\n    r=\"${__nil}\";\n}\n\nprintln () {\n    local res=\"\"\n    for x in \"${@}\"; do _pr_str \"${x}\"; res=\"${res} ${r}\"; done\n    echo \"${res:1}\"\n    r=\"${__nil}\";\n}\n\nreadline () {\n    READLINE \"${ANON[\"${1}\"]}\" && _string \"${r}\" || r=\"${__nil}\"\n}\n\nread_string () {\n    READ_STR \"${ANON[\"${1}\"]}\"\n}\n\nslurp () {\n    local lines\n    mapfile lines < \"${ANON[\"${1}\"]}\"\n    local text=\"${lines[*]}\"; text=${text//$'\\n' /$'\\n'}\n    _string \"${text}\"\n}\n\n\n# Function functions\nfunction? () { _function? \"${1}\" && [ -z \"${ANON[\"${1}_ismacro_\"]}\" ] && r=\"${__true}\" || r=\"${__false}\"; }\nmacro? () { _function? \"${1}\" && [ \"${ANON[\"${1}_ismacro_\"]}\" ] && r=\"${__true}\" || r=\"${__false}\"; }\n\n\n# List functions\nlist? () { _list? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\n\n# Vector functions (same as lists for now)\nvector? () { _vector? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\n\n# Hash map (associative array) functions\nhash_map? () { _hash_map? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\n# Return new hash map with keys/values updated\nassoc () {\n    if ! _hash_map? \"${1}\"; then\n        _error \"assoc onto non-hash-map\"\n        return\n    fi\n    _copy_hash_map \"${1}\"; shift\n    local name=\"${r}\"\n    local obj=${ANON[\"${name}\"]}\n    declare -A -g ${obj}\n\n    while [[ \"${1}\" ]]; do\n        eval ${obj}[\\\"${ANON[\"${1}\"]}\\\"]=\\\"${2}\\\"\n        shift; shift\n    done\n    r=\"${name}\"\n}\n\ndissoc () {\n    if ! _hash_map? \"${1}\"; then\n        _error \"dissoc from non-hash-map\"\n        return\n    fi\n    _copy_hash_map \"${1}\"; shift\n    local name=\"${r}\"\n    local obj=${ANON[\"${name}\"]}\n    declare -A -g ${obj}\n\n    while [[ \"${1}\" ]]; do\n        eval unset ${obj}[\\\"${ANON[\"${1}\"]}\\\"]\n        shift\n    done\n    r=\"${name}\"\n}\n\n_get () {\n    _obj_type \"${1}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    hash_map)\n        local obj=\"${ANON[\"${1}\"]}\"\n        eval r=\"\\${${obj}[\\\"${2}\\\"]}\" ;;\n    list|vector)\n        _nth \"${1}\" \"${2}\" ;;\n    nil)\n        r=\"${__nil}\" ;;\n    esac\n}\nget () {\n    _get \"${1}\" \"${ANON[\"${2}\"]}\"\n    [[ \"${r}\" ]] || r=\"${__nil}\"\n}\n\ncontains? () { _contains? \"${1}\" \"${ANON[\"${2}\"]}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\nkeys () {\n    local obj=\"${ANON[\"${1}\"]}\"\n    local kstrs=\n    eval local keys=\"\\${!${obj}[@]}\"\n    for k in ${keys}; do\n        _string \"${k}\"\n        kstrs=\"${kstrs} ${r}\"\n    done\n\n    __new_obj_hash_code\n    r=\"list_${r}\"\n    ANON[\"${r}\"]=\"${kstrs:1}\"\n}\n\nvals () {\n    local obj=\"${ANON[\"${1}\"]}\"\n    local kvals=\n    local val=\n    eval local keys=\"\\${!${obj}[@]}\"\n    for k in ${keys}; do\n        eval val=\"\\${${obj}[\"\\${k}\"]}\"\n        kvals=\"${kvals} ${val}\"\n    done\n\n    __new_obj_hash_code\n    r=\"list_${r}\"\n    ANON[\"${r}\"]=\"${kvals:1}\"\n}\n\n\n# sequence operations\n\nsequential? () {\n    _sequential? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"\n}\n\ncons () {\n    _list ${1} ${ANON[\"${2}\"]}\n}\n\nconcat () {\n    _list\n    local acc=\"\"\n    for item in \"${@}\"; do\n        acc=\"${acc} ${ANON[\"${item}\"]}\"\n    done\n    ANON[\"${r}\"]=\"${acc:1}\"\n}\n\nnth () {\n    _nth \"${1}\" \"${ANON[\"${2}\"]}\"\n    if [ -z \"${r}\" ]; then\n        _error \"nth: index out of bounds\"\n        return\n    fi\n}\n\nempty? () { _empty? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\n\ncount () {\n    _count \"${1}\"\n    _number \"${r}\"\n}\n\napply () {\n    local f=\"${ANON[\"${1}\"]}\"; shift\n    local items=\"${@:1:$(( ${#@} -1 ))} ${ANON[\"${!#}\"]}\"\n    eval ${f%%@*} ${items}\n}\n\n# Takes a function object and an list object and invokes the function\n# on each element of the list, returning a new list of the results.\nmap () {\n    local f=\"${ANON[\"${1}\"]}\"; shift\n    #echo _map \"${f}\" \"${@}\"\n    _map \"${f}\" \"${@}\"\n}\n\nconj () {\n    local obj=\"${1}\"; shift\n    local obj_data=\"${ANON[\"${obj}\"]}\"\n    __new_obj_like \"${obj}\"\n    if _list? \"${obj}\"; then\n        ANON[\"${r}\"]=\"${obj_data:+${obj_data}}\"\n        for elem in ${@}; do\n            ANON[\"${r}\"]=\"${elem} ${ANON[\"${r}\"]}\"\n        done\n\n    else\n        ANON[\"${r}\"]=\"${obj_data:+${obj_data} }${*}\"\n    fi\n}\n\nseq () {\n    local obj=\"${1}\"; shift\n    local obj_data=\"${ANON[\"${obj}\"]}\"\n\n\n    if _list? \"${obj}\"; then\n        _count \"${obj}\"\n        if [ \"${r}\" -eq 0 ]; then r=\"${__nil}\"; return; fi\n        r=\"${obj}\"\n    elif _vector? \"${obj}\"; then\n        _count \"${obj}\"\n        if [ \"${r}\" -eq 0 ]; then r=\"${__nil}\"; return; fi\n        __new_obj_hash_code\n        r=\"list_${r}\"\n        ANON[\"${r}\"]=\"${obj_data}\"\n    elif _string? \"${obj}\"; then\n        if [ \"${#obj_data}\" -eq 0 ]; then r=\"${__nil}\"; return; fi\n        local i=0 acc=\"\"\n        for (( i=0; i < ${#obj_data}; i++ )); do\n            _string \"${obj_data:$i:1}\"\n            acc=\"${acc} ${r}\"\n        done\n        _list\n        ANON[\"${r}\"]=\"${acc:1}\"\n    elif _nil? \"${obj}\"; then\n        r=\"${__nil}\"\n    else\n        throw \"seq: called on non-sequence\"\n    fi\n}\n\n\n# Metadata functions\n\nwith_meta () {\n    local obj=\"${1}\"; shift\n    local meta_data=\"${1}\"; shift\n    __new_obj_like \"${obj}\"\n    ANON[\"${r}\"]=\"${ANON[\"${obj}\"]}\"\n    local meta_obj=\"meta_${r#*_}\"\n    ANON[\"${meta_obj}\"]=\"${meta_data}\"\n}\n\nmeta () {\n    r=\"${ANON[\"meta_${1#*_}\"]}\"\n    [[ \"${r}\" ]] || r=\"${__nil}\"\n}\n\n\n# Atom functions\n\natom? () { _atom? \"${1}\" && r=\"${__true}\" || r=\"${__false}\"; }\nderef () {\n    # TODO: double-check atom type\n    r=${ANON[\"${1}\"]}\n}\nreset_BANG () {\n    local atm=\"${1}\"; shift\n    ANON[\"${atm}\"]=\"${*}\"\n    r=\"${*}\"\n}\nswap_BANG () {\n    local atm=\"${1}\"; shift\n    local f=\"${ANON[\"${1}\"]}\"; shift\n    ${f%%@*} \"${ANON[\"${atm}\"]}\" \"${@}\"\n    ANON[\"${atm}\"]=\"${r}\"\n}\n\n\n\n# Namespace of core functions\n\ndeclare -A core_ns=(\n    [type]=obj_type\n    [=]=equal?\n    [throw]=throw\n    [nil?]=nil?\n    [true?]=true?\n    [false?]=false?\n    [string?]=string?\n    [symbol]=symbol\n    [symbol?]=symbol?\n    [keyword]=keyword\n    [keyword?]=keyword?\n    [number?]=number?\n    [fn?]=function?\n    [macro?]=macro?\n\n    [pr-str]=pr_str\n    [str]=str\n    [prn]=prn\n    [println]=println\n    [readline]=readline\n    [read-string]=read_string\n    [slurp]=slurp\n    ['<']=num_lt\n    ['<=']=num_lte\n    ['>']=num_gt\n    ['>=']=num_gte\n    [+]=num_plus\n    [-]=num_minus\n    [__STAR__]=num_multiply\n    [/]=num_divide\n    [time-ms]=time_ms\n\n    [list]=_list\n    [list?]=list?\n    [vector]=_vector\n    [vector?]=vector?\n    [hash-map]=_hash_map\n    [map?]=hash_map?\n    [assoc]=assoc\n    [dissoc]=dissoc\n    [get]=get\n    [contains?]=contains?\n    [keys]=keys\n    [vals]=vals\n\n    [sequential?]=sequential?\n    [cons]=cons\n    [concat]=concat\n    [vec]=vec\n    [nth]=nth\n    [first]=_first\n    [rest]=_rest\n    [empty?]=empty?\n    [count]=count\n    [apply]=apply\n    [map]=map\n\n    [conj]=conj\n    [seq]=seq\n\n    [with-meta]=with_meta\n    [meta]=meta\n    [atom]=_atom\n    [atom?]=atom?\n    [deref]=deref\n    [reset!]=reset_BANG\n    [swap!]=swap_BANG)\n\nfi\n"
  },
  {
    "path": "impls/bash/env.sh",
    "content": "#\n# mal (Make a Lisp) environment definition\n#\n\nif [ -z \"${__mal_env_included__}\" ]; then\n__mal_env_included=true\n\nsource $(dirname $0)/types.sh\n\n# Any environment is a hash_map with an __outer__ key that refers to\n# a parent environment (or nil)\nENV () {\n    r=\n    _hash_map\n    local env=\"${r}\"\n    if [[ \"${1}\" ]]; then\n        outer=\"${1}\"; shift\n        _assoc! \"${env}\" \"__outer__\" \"${outer}\"\n    else\n        _assoc! \"${env}\" \"__outer__\" \"${__nil}\"\n    fi\n    r=\"${env}\"\n\n    if [[ \"${1}\" && \"${@}\" ]]; then\n        local binds=(${ANON[\"${1}\"]}); shift\n        local idx=0\n        while [[ \"${binds[\"${idx}\"]}\" ]]; do\n            local fp=\"${ANON[\"${binds[\"${idx}\"]}\"]}\"\n            if [[ \"${fp}\" == \"&\" ]]; then\n                idx=$(( idx + 1 ))\n                fp=\"${ANON[\"${binds[\"${idx}\"]}\"]}\"\n                _list \"${@}\"\n                _assoc! \"${env}\" \"${fp}\" \"${r}\"\n                break\n            else\n                _assoc! \"${env}\" \"${fp}\" \"${1}\"\n                shift\n                idx=$(( idx + 1 ))\n            fi\n        done\n    fi\n    r=\"${env}\"\n}\n\n# Find the environment with the key set and return the environment\nENV_FIND () {\n    if _contains? \"${1}\" \"${ANON[\"${2}\"]}\"; then\n        r=\"${1}\"\n    else\n        local obj=\"${ANON[\"${1}\"]}\"\n        eval 'local outer=${'${obj}'[\"__outer__\"]}'\n        if [[ \"${outer}\" && \"${outer}\" != \"${__nil}\" ]]; then\n            ENV_FIND \"${outer}\" \"${2}\"\n        else\n            r=\n        fi\n    fi\n}\n\n# Find the environment with the key set and return the value of the\n# key in that environment. If no environment contains the key then\n# return an error\nENV_GET () {\n    ENV_FIND \"${1}\" \"${2}\"\n    local env=\"${r}\"\n    local key=\"${ANON[\"${2}\"]}\"\n    if [[ \"${r}\" ]]; then\n        local obj=\"${ANON[\"${env}\"]}\"\n        eval 'r=${'${obj}'[\"'${key}'\"]}'\n    else\n        _error \"'${key}' not found\"\n    fi\n}\n\nENV_SET () {\n    local key=\"${ANON[\"${2}\"]}\"\n    _assoc! \"${1}\" \"${key}\" \"${3}\"\n}\n\nfi\n"
  },
  {
    "path": "impls/bash/printer.sh",
    "content": "#\n# mal (Make a Lisp) printer\n#\n\nif [ -z \"${__mal_printer_included__}\" ]; then\n__mal_printer_included=true\n\nsource $(dirname $0)/types.sh\n\n_pr_str () {\n    local print_readably=\"${2}\"\n    _obj_type \"${1}\"; local ot=\"${r}\"\n    if [[ -z \"${ot}\" ]]; then\n        _error \"_pr_str failed on '${1}'\"\n        r=\"<${1}>\"\n    else\n        eval ${ot}_pr_str \"${1}\" \"${print_readably}\"\n    fi\n}\n\nnil_pr_str () { r=\"nil\"; }\ntrue_pr_str () { r=\"true\"; }\nfalse_pr_str () { r=\"false\"; }\n\nnumber_pr_str () { r=\"${ANON[\"${1}\"]}\"; }\n\nsymbol_pr_str () {\n    r=\"${ANON[\"${1}\"]}\"\n    r=\"${r//__STAR__/*}\"\n}\n\nkeyword_pr_str () {\n    string_pr_str \"${1}\"\n}\n\n_raw_string_pr_str () {\n    local s=\"${1}\"\n    local print_readably=\"${2}\"\n    if [[ \"${s:0:1}\" = \"${__keyw}\" ]]; then\n        r=\":${s:1}\"\n    elif [[ \"${s:0:2}\" = \"${__keyw}\" ]]; then\n        r=\":${s:2}\"\n    elif [ \"${print_readably}\" == \"yes\" ]; then\n        s=\"${s//\\\\/\\\\\\\\}\"\n        s=\"${s//\\\"/\\\\\\\"}\"\n        r=\"\\\"${s//$'\\n'/\\\\n}\\\"\"\n    else\n        r=\"${s}\"\n    fi\n    r=\"${r//__STAR__/$'*'}\"\n}\n\nstring_pr_str () {\n    _raw_string_pr_str \"${ANON[\"${1}\"]}\" \"${2}\"\n}\n\nfunction_pr_str () { r=\"${ANON[\"${1}\"]}\"; }\n\nbash_pr_str () {\n    r=\"$(declare -f -p ${1})\"\n}\n\nhash_map_pr_str () {\n    local print_readably=\"${2}\"\n    local res=\"\"; local val=\"\"\n    local hm=\"${ANON[\"${1}\"]}\"\n    eval local keys=\"\\${!${hm}[@]}\"\n    for key in ${keys}; do\n        _raw_string_pr_str \"${key}\" \"${print_readably}\"\n        res=\"${res} ${r}\"\n        eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n        _pr_str \"${val}\" \"${print_readably}\"\n        res=\"${res} ${r}\"\n    done\n    r=\"{${res:1}}\"\n}\n\nvector_pr_str () {\n    local print_readably=\"${2}\"\n    local res=\"\"\n    for elem in ${ANON[\"${1}\"]}; do\n        _pr_str \"${elem}\" \"${print_readably}\"\n        res=\"${res} ${r}\"\n    done\n    r=\"[${res:1}]\"\n}\n\nlist_pr_str () {\n    local print_readably=\"${2}\"\n    local res=\"\"\n    for elem in ${ANON[\"${1}\"]}; do\n        _pr_str \"${elem}\" \"${print_readably}\"\n        res=\"${res} ${r}\"\n    done\n    r=\"(${res:1})\"\n}\n\natom_pr_str () {\n    local print_readably=\"${2}\"\n    _pr_str \"${ANON[\"${1}\"]}\" \"${print_readably}\"\n    r=\"(atom ${r})\";\n}\n\nfi\n"
  },
  {
    "path": "impls/bash/reader.sh",
    "content": "#\n# mal (Make Lisp) Parser/Reader\n#\n\nif [ -z \"${__mal_readerr_included__}\" ]; then\n__mal_readerr_included=true\n\nsource $(dirname $0)/types.sh\n\nREAD_ATOM () {\n    local token=${__reader_tokens[${__reader_idx}]}\n    __reader_idx=$(( __reader_idx + 1 ))\n    case \"${token}\" in\n        [0-9]*)  _number \"${token}\" ;;\n        -[0-9]*) _number \"${token}\" ;;\n        \\\"*)    if [[ ! \"${token}\" =~ ^\\\"(\\\\.|[^\\\\\\\"])*\\\"$ ]]; then\n                    _error \"expected '\\\"', got EOF\"\n                    return\n                fi\n                token=\"${token:1:-1}\"\n                token=\"${token//\\\\\\\\/${__keyw}}\"\n                token=\"${token//\\\\\\\"/\\\"}\"\n                token=\"${token//\\\\n/$'\\n'}\"\n                token=\"${token//${__keyw}/\\\\}\"\n                _string \"${token}\" ;;\n        :*)     _keyword \"${token:1}\" ;;\n        nil)    r=\"${__nil}\" ;;\n        true)   r=\"${__true}\" ;;\n        false)  r=\"${__false}\" ;;\n        *)      _symbol \"${token}\" ;;\n    esac\n}\n\n# Return seqence of tokens into r.\n#   ${1}: Type of r (vector, list)\n#   ${2}: starting symbol\n#   ${3}: ending symbol\nREAD_SEQ () {\n    local start=\"${1}\"\n    local end=\"${2}\"\n    local items=\"\"\n    local token=${__reader_tokens[${__reader_idx}]}\n    __reader_idx=$(( __reader_idx + 1 ))\n    if [[ \"${token}\" != \"${start}\" ]]; then\n        r=\n        _error \"expected '${start}'\"\n        return\n    fi\n    token=${__reader_tokens[${__reader_idx}]}\n    while [[ \"${token}\" != \"${end}\" ]]; do\n        if [[ ! \"${token}\" ]]; then\n            r=\n            _error \"expected '${end}', got EOF\"\n            return\n        fi\n        READ_FORM\n        items=\"${items} ${r}\"\n        token=${__reader_tokens[${__reader_idx}]}\n    done\n    __reader_idx=$(( __reader_idx + 1 ))\n    r=\"${items:1}\"\n}\n\n# Return form in r\nREAD_FORM () {\n    local token=${__reader_tokens[${__reader_idx}]}\n    case \"${token}\" in\n        \\')     __reader_idx=$(( __reader_idx + 1 ))\n                _symbol quote; local q=\"${r}\"\n                READ_FORM; local f=\"${r}\"\n                _list \"${q}\" \"${f}\" ;;\n        \\`)     __reader_idx=$(( __reader_idx + 1 ))\n                _symbol quasiquote; local q=\"${r}\"\n                READ_FORM; local f=\"${r}\"\n                _list \"${q}\" \"${f}\" ;;\n        \\~)     __reader_idx=$(( __reader_idx + 1 ))\n                _symbol unquote; local q=\"${r}\"\n                READ_FORM; local f=\"${r}\"\n                _list \"${q}\" \"${f}\" ;;\n        \\~\\@)   __reader_idx=$(( __reader_idx + 1 ))\n                _symbol splice-unquote; local q=\"${r}\"\n                READ_FORM; local f=\"${r}\"\n                _list \"${q}\" \"${f}\" ;;\n        ^)      __reader_idx=$(( __reader_idx + 1 ))\n                _symbol with-meta; local wm=\"${r}\"\n                READ_FORM; local meta=\"${r}\"\n                READ_FORM; local obj=\"${r}\"\n                _list \"${wm}\" \"${obj}\" \"${meta}\" ;;\n        @)     __reader_idx=$(( __reader_idx + 1 ))\n                _symbol deref; local d=\"${r}\"\n                READ_FORM; local f=\"${r}\"\n                _list \"${d}\" \"${f}\" ;;\n        \\))     _error \"unexpected ')'\" ;; \n        \\()     READ_SEQ \"(\" \")\"\n                _list ${r} ;;\n        \\])     _error \"unexpected ']'\" ;; \n        \\[)     READ_SEQ \"[\" \"]\"\n                _vector ${r} ;;\n        \\})     _error \"unexpected '}'\" ;; \n        \\{)     READ_SEQ \"{\" \"}\"\n                _hash_map ${r} ;;\n        *)      READ_ATOM\n    esac\n}\n\nTOKEN_PAT=$'^^([][{}\\\\(\\\\)^@])|^(~@)|^(\"(\\\\\\\\.|[^\\\\\"])*\"?)|^(;[^\\n]*)|^([~\\'`])|^([^][ ~`\\'\";{}\\\\(\\\\)^@,\\n]+)|^(,)|^([[:space:]]+)'\n\n# Returns __reader_tokens as an indexed array of tokens\nTOKENIZE () {\n    local data=\"${*}\"\n    local datalen=${#data}\n    local idx=0\n    local chunk=0\n    local chunksz=500\n    local token=\n    local str=\n\n    __reader_idx=0\n    declare -a -g __reader_tokens=()  # global array\n    while true; do\n        if (( ${#str} < ( chunksz / 2) )) && (( chunk < datalen )); then\n            str=\"${str}${data:${chunk}:${chunksz}}\"\n            chunk=$(( chunk + ${chunksz} ))\n        fi\n        (( ${#str} == 0 )) && break\n        [[ \"${str}\" =~ ${TOKEN_PAT} ]]\n        token=${BASH_REMATCH[0]}\n        str=\"${str:${#token}}\"\n        token=\"${token}\"\n        #echo \"MATCH: '${token}' / [${str}]\"\n        if ! [[ \"${token}\" =~ (^[,]$|^[[:space:]]*;.*$|^[[:space:]]*$) ]]; then \n            __reader_tokens[${idx}]=\"${token}\"\n            idx=$(( idx + 1 ))\n        fi\n        if [ -z \"${token}\" ]; then\n            _error \"Tokenizing error at: ${str:0:50}\"\n            return 1\n        fi\n    done\n}\n\n# read-str from a raw \"string\" or from a string object. Retruns object\n# read in r.\nREAD_STR () {\n    declare -a __reader_tokens\n    TOKENIZE \"${*}\" || return 1  # sets __reader_tokens\n    #set | grep ^__reader_tokens\n    if [ -z \"${__reader_tokens[0]}\" ]; then\n        r=\n        return 1  # No tokens\n    fi\n    READ_FORM\n    #echo \"Token: ${r}: <${ANON[\"${r}\"]}>\"\n    return\n}\n\n# Call readline and save the history. Returns the string read in r.\nREADLINE_EOF=\nREADLINE_HISTORY_FILE=${HOME}/.mal-history\nREADLINE () {\n    history -r \"${READLINE_HISTORY_FILE}\" 2>/dev/null || true\n    read -r -e -p \"${1}\" r || return \"$?\"\n    history -s -- \"${r}\"\n    history -a \"${READLINE_HISTORY_FILE}\" 2>/dev/null || true\n}\n\nfi\n"
  },
  {
    "path": "impls/bash/run",
    "content": "#!/usr/bin/env bash\nexec bash $(dirname $0)/${STEP:-stepA_mal}.sh \"${@}\"\n"
  },
  {
    "path": "impls/bash/step0_repl.sh",
    "content": "#!/usr/bin/env bash\n\nREAD () {\n    read -u 0 -e -p \"user> \" r\n}\n\nEVAL () {\n    r=\"${1}\"\n}\n\nPRINT () {\n    r=\"${1}\"\n}\n\nwhile true; do\n    READ\n    EVAL \"${r}\"\n    PRINT \"${r}\"\n    echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step1_read_print.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\nEVAL () {\n    local ast=\"${1}\"\n    local env=\"${2}\"\n    r=\n    [[ \"${__ERROR}\" ]] && return 1\n    r=\"${ast}\"\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" no\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nREP () {\n    READ \"${1}\"\n    EVAL \"${r}\"\n    PRINT \"${r}\"\n}\n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step2_eval.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    #_pr_str \"${ast}\"; echo \"EVAL_AST '${ast}:${r} / ${env}'\"\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        local val=\"${ANON[\"${ast}\"]}\"\n        eval r=\"\\${${env}[\"${val}\"]}\"\n        [ \"${r}\" ] || _error \"'${val}' not found\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _map_with_type _list EVAL \"${ast}\" \"${env}\"\n    [[ \"${__ERROR}\" ]] && return 1\n    local el=\"${r}\"\n    _first \"${el}\"; local f=\"${r}\"\n    _rest \"${el}\"; local args=\"${ANON[\"${r}\"]}\"\n    #echo \"invoke: ${f} ${args}\"\n    eval ${f} ${args}\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\ndeclare -A REPL_ENV\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" REPL_ENV\n    PRINT \"${r}\"\n}\n\nplus     () { r=$(( ${ANON[\"${1}\"]} + ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nminus    () { r=$(( ${ANON[\"${1}\"]} - ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nmultiply () { r=$(( ${ANON[\"${1}\"]} * ${ANON[\"${2}\"]} )); _number \"${r}\"; }\ndivide   () { r=$(( ${ANON[\"${1}\"]} / ${ANON[\"${2}\"]} )); _number \"${r}\"; }\n\nREPL_ENV[\"+\"]=plus\nREPL_ENV[\"-\"]=minus\nREPL_ENV[\"__STAR__\"]=multiply\nREPL_ENV[\"/\"]=divide\n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step3_env.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              EVAL \"${a2}\" \"${let_env}\"\n              return ;;\n        *)    _map_with_type _list EVAL \"${ast}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              local el=\"${r}\"\n              _first \"${el}\"; local f=\"${r}\"\n              _rest \"${el}\"; local args=\"${ANON[\"${r}\"]}\"\n              #echo \"invoke: ${f} ${args}\"\n              eval ${f} ${args}\n              return ;;\n    esac\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\nplus     () { r=$(( ${ANON[\"${1}\"]} + ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nminus    () { r=$(( ${ANON[\"${1}\"]} - ${ANON[\"${2}\"]} )); _number \"${r}\"; }\nmultiply () { r=$(( ${ANON[\"${1}\"]} * ${ANON[\"${2}\"]} )); _number \"${r}\"; }\ndivide   () { r=$(( ${ANON[\"${1}\"]} / ${ANON[\"${2}\"]} )); _number \"${r}\"; }\n\n_symbol \"+\";        ENV_SET \"${REPL_ENV}\" \"${r}\" plus\n_symbol \"-\";        ENV_SET \"${REPL_ENV}\" \"${r}\" minus\n_symbol \"__STAR__\"; ENV_SET \"${REPL_ENV}\" \"${r}\" multiply\n_symbol \"/\";        ENV_SET \"${REPL_ENV}\" \"${r}\" divide\n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step4_if_fn_do.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              EVAL \"${a2}\" \"${let_env}\"\n              return ;;\n        do)   _rest \"${ast}\"\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${r}\"\n              return ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      EVAL \"${a3}\" \"${env}\"\n                  else\n                      r=\"${__nil}\"\n                  fi\n              else\n                  # eval true condition\n                  EVAL \"${a2}\" \"${env}\"\n              fi\n              return ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\"\n              return ;;\n        *)    _map_with_type _list EVAL \"${ast}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              local el=\"${r}\"\n              _first \"${el}\"; local f=\"${ANON[\"${r}\"]}\"\n              _rest \"${el}\"; local args=\"${ANON[\"${r}\"]}\"\n              #echo \"invoke: ${f} ${args}\"\n              eval ${f} ${args}\n              return ;;\n    esac\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n\n# core.mal: defined using the language itself\nREP \"(def! not (fn* (a) (if a false true)))\"\n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step5_tco.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    while true; do\n    r=\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              ast=\"${a2}\"\n              env=\"${let_env}\"\n              # Continue loop\n              ;;\n        do)   _count \"${ast}\"\n              _slice \"${ast}\" 1 $(( ${r} - 2 ))\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${ast}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      ast=\"${a3}\"\n                  else\n                      r=\"${__nil}\"\n                      return\n                  fi\n              else\n                  # eval true condition\n                  ast=\"${a2}\"\n              fi\n              # Continue loop\n              ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\" \\\n                        \"${a2}\" \"${env}\" \"${a1}\"\n              return ;;\n        *)    _map_with_type _list EVAL \"${ast}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              local el=\"${r}\"\n              _first \"${el}\"; local f=\"${ANON[\"${r}\"]}\"\n              _rest \"${el}\"; local args=\"${ANON[\"${r}\"]}\"\n              #echo \"invoke: [${f}] ${args}\"\n              if [[ \"${f//@/ }\" != \"${f}\" ]]; then\n                  set -- ${f//@/ }\n                  ast=\"${2}\"\n                  ENV \"${3}\" \"${4}\" ${args}\n                  env=\"${r}\"\n              else\n                  eval ${f%%@*} ${args}\n                  return\n              fi\n              # Continue loop\n              ;;\n    esac\n    done\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n\n# core.mal: defined using the language itself\nREP \"(def! not (fn* (a) (if a false true)))\"\n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step6_file.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    while true; do\n    r=\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              ast=\"${a2}\"\n              env=\"${let_env}\"\n              # Continue loop\n              ;;\n        do)   _count \"${ast}\"\n              _slice \"${ast}\" 1 $(( ${r} - 2 ))\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${ast}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      ast=\"${a3}\"\n                  else\n                      r=\"${__nil}\"\n                      return\n                  fi\n              else\n                  # eval true condition\n                  ast=\"${a2}\"\n              fi\n              # Continue loop\n              ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\" \\\n                        \"${a2}\" \"${env}\" \"${a1}\"\n              return ;;\n        *)    _map_with_type _list EVAL \"${ast}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              local el=\"${r}\"\n              _first \"${el}\"; local f=\"${ANON[\"${r}\"]}\"\n              _rest \"${el}\"; local args=\"${ANON[\"${r}\"]}\"\n              #echo \"invoke: [${f}] ${args}\"\n              if [[ \"${f//@/ }\" != \"${f}\" ]]; then\n                  set -- ${f//@/ }\n                  ast=\"${2}\"\n                  ENV \"${3}\" \"${4}\" ${args}\n                  env=\"${r}\"\n              else\n                  eval ${f%%@*} ${args}\n                  return\n              fi\n              # Continue loop\n              ;;\n    esac\n    done\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n_eval () { EVAL \"${1}\" \"${REPL_ENV}\"; }\n_fref \"eval\" _eval\n_list; argv=\"${r}\"\nfor _arg in \"${@:2}\"; do _string \"${_arg}\"; _conj! \"${argv}\" \"${r}\"; done\n_symbol \"__STAR__ARGV__STAR__\"\nENV_SET \"${REPL_ENV}\" \"${r}\" \"${argv}\";\n\n# core.mal: defined using the language itself\nREP \"(def! not (fn* (a) (if a false true)))\"\nREP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\n# load/run file from command line (then exit)\nif [[ \"${1}\" ]]; then\n    REP \"(load-file \\\"${1}\\\")\"\n    exit 0\nfi \n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step7_quote.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\nstarts_with () {\n    _list? \"$1\" && _first \"$1\" && _symbol? \"$r\" && [ \"${ANON[$r]}\" = \"$2\" ]\n}\n\nQUASIQUOTE () {\n    _obj_type \"$1\"\n    case \"$r\" in\n        list)\n            if starts_with \"$1\" unquote; then\n                _nth \"$1\" 1\n            else\n                qqIter \"$1\"\n            fi ;;\n        vector)\n            _symbol vec;  local a=\"$r\"\n            qqIter \"$1\"\n            _list \"$a\" \"$r\" ;;\n        symbol|hash_map)\n            _symbol quote\n            _list \"$r\" \"$1\" ;;\n        *)\n            r=\"$1\" ;;\n    esac\n}\n\nqqIter () {\n    if _empty? \"$1\"; then\n        _list\n    else\n        _nth \"${1}\" 0;        local a0=\"$r\"\n        if starts_with \"$a0\" splice-unquote; then\n            _symbol concat;   local a=\"$r\"\n            _nth \"$a0\" 1;     local b=\"$r\"\n        else\n            _symbol cons;     local a=\"$r\"\n            QUASIQUOTE \"$a0\"; local b=\"$r\"\n        fi\n        _rest \"$1\"\n        qqIter \"$r\"\n        _list \"$a\" \"$b\" \"$r\"\n    fi\n}\n\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    while true; do\n    r=\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              ast=\"${a2}\"\n              env=\"${let_env}\"\n              # Continue loop\n              ;;\n        quote)\n              r=\"${a1}\"\n              return ;;\n        quasiquote)\n              QUASIQUOTE \"${a1}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        do)   _count \"${ast}\"\n              _slice \"${ast}\" 1 $(( ${r} - 2 ))\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${ast}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      ast=\"${a3}\"\n                  else\n                      r=\"${__nil}\"\n                      return\n                  fi\n              else\n                  # eval true condition\n                  ast=\"${a2}\"\n              fi\n              # Continue loop\n              ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\" \\\n                        \"${a2}\" \"${env}\" \"${a1}\"\n              return ;;\n        *)    _map_with_type _list EVAL \"${ast}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              local el=\"${r}\"\n              _first \"${el}\"; local f=\"${ANON[\"${r}\"]}\"\n              _rest \"${el}\"; local args=\"${ANON[\"${r}\"]}\"\n              #echo \"invoke: [${f}] ${args}\"\n              if [[ \"${f//@/ }\" != \"${f}\" ]]; then\n                  set -- ${f//@/ }\n                  ast=\"${2}\"\n                  ENV \"${3}\" \"${4}\" ${args}\n                  env=\"${r}\"\n              else\n                  eval ${f%%@*} ${args}\n                  return\n              fi\n              # Continue loop\n              ;;\n    esac\n    done\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n_eval () { EVAL \"${1}\" \"${REPL_ENV}\"; }\n_fref \"eval\" _eval\n_list; argv=\"${r}\"\nfor _arg in \"${@:2}\"; do _string \"${_arg}\"; _conj! \"${argv}\" \"${r}\"; done\n_symbol \"__STAR__ARGV__STAR__\"\nENV_SET \"${REPL_ENV}\" \"${r}\" \"${argv}\";\n\n# core.mal: defined using the language itself\nREP \"(def! not (fn* (a) (if a false true)))\"\nREP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\n# load/run file from command line (then exit)\nif [[ \"${1}\" ]]; then\n    REP \"(load-file \\\"${1}\\\")\"\n    exit 0\nfi \n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step8_macros.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\nstarts_with () {\n    _list? \"$1\" && _first \"$1\" && _symbol? \"$r\" && [ \"${ANON[$r]}\" = \"$2\" ]\n}\n\nQUASIQUOTE () {\n    _obj_type \"$1\"\n    case \"$r\" in\n        list)\n            if starts_with \"$1\" unquote; then\n                _nth \"$1\" 1\n            else\n                qqIter \"$1\"\n            fi ;;\n        vector)\n            _symbol vec;  local a=\"$r\"\n            qqIter \"$1\"\n            _list \"$a\" \"$r\" ;;\n        symbol|hash_map)\n            _symbol quote\n            _list \"$r\" \"$1\" ;;\n        *)\n            r=\"$1\" ;;\n    esac\n}\n\nqqIter () {\n    if _empty? \"$1\"; then\n        _list\n    else\n        _nth \"${1}\" 0;        local a0=\"$r\"\n        if starts_with \"$a0\" splice-unquote; then\n            _symbol concat;   local a=\"$r\"\n            _nth \"$a0\" 1;     local b=\"$r\"\n        else\n            _symbol cons;     local a=\"$r\"\n            QUASIQUOTE \"$a0\"; local b=\"$r\"\n        fi\n        _rest \"$1\"\n        qqIter \"$r\"\n        _list \"$a\" \"$b\" \"$r\"\n    fi\n}\n\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    while true; do\n    r=\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              ast=\"${a2}\"\n              env=\"${let_env}\"\n              # Continue loop\n              ;;\n        quote)\n              r=\"${a1}\"\n              return ;;\n        quasiquote)\n              QUASIQUOTE \"${a1}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        defmacro!)\n              EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              local func=\"${r}\"\n              __new_obj_like \"${func}\"\n              ANON[\"${r}\"]=\"${ANON[\"${func}\"]}\"\n              ANON[\"${r}_ismacro_\"]=\"yes\"\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        do)   _count \"${ast}\"\n              _slice \"${ast}\" 1 $(( ${r} - 2 ))\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${ast}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      ast=\"${a3}\"\n                  else\n                      r=\"${__nil}\"\n                      return\n                  fi\n              else\n                  # eval true condition\n                  ast=\"${a2}\"\n              fi\n              # Continue loop\n              ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\" \\\n                        \"${a2}\" \"${env}\" \"${a1}\"\n              return ;;\n        *)    EVAL \"${a0}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              local f=\"${r}\"\n\n              _rest \"${ast}\"\n              # Should cause no error as ast is not empty.\n              local args=\"${r}\"\n\n              if [ \"${ANON[\"${f}_ismacro_\"]}\" ]; then\n                  f=\"${ANON[\"${f}\"]}\"\n                  ${f%%@*} ${ANON[\"${args}\"]}\n                  ast=\"${r}\"\n                  continue\n              fi\n\n              f=\"${ANON[\"${f}\"]}\"\n\n              _map_with_type _list EVAL \"${args}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              args=\"${ANON[\"${r}\"]}\"\n\n              #echo \"invoke: [${f}] ${args}\"\n              if [[ \"${f//@/ }\" != \"${f}\" ]]; then\n                  set -- ${f//@/ }\n                  ast=\"${2}\"\n                  ENV \"${3}\" \"${4}\" ${args}\n                  env=\"${r}\"\n              else\n                  eval ${f%%@*} ${args}\n                  return\n              fi\n              # Continue loop\n              ;;\n    esac\n    done\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n_eval () { EVAL \"${1}\" \"${REPL_ENV}\"; }\n_fref \"eval\" _eval\n_list; argv=\"${r}\"\nfor _arg in \"${@:2}\"; do _string \"${_arg}\"; _conj! \"${argv}\" \"${r}\"; done\n_symbol \"__STAR__ARGV__STAR__\"\nENV_SET \"${REPL_ENV}\" \"${r}\" \"${argv}\";\n\n# core.mal: defined using the language itself\nREP \"(def! not (fn* (a) (if a false true)))\"\nREP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nREP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\n# load/run file from command line (then exit)\nif [[ \"${1}\" ]]; then\n    REP \"(load-file \\\"${1}\\\")\"\n    exit 0\nfi \n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/step9_try.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\nstarts_with () {\n    _list? \"$1\" && _first \"$1\" && _symbol? \"$r\" && [ \"${ANON[$r]}\" = \"$2\" ]\n}\n\nQUASIQUOTE () {\n    _obj_type \"$1\"\n    case \"$r\" in\n        list)\n            if starts_with \"$1\" unquote; then\n                _nth \"$1\" 1\n            else\n                qqIter \"$1\"\n            fi ;;\n        vector)\n            _symbol vec;  local a=\"$r\"\n            qqIter \"$1\"\n            _list \"$a\" \"$r\" ;;\n        symbol|hash_map)\n            _symbol quote\n            _list \"$r\" \"$1\" ;;\n        *)\n            r=\"$1\" ;;\n    esac\n}\n\nqqIter () {\n    if _empty? \"$1\"; then\n        _list\n    else\n        _nth \"${1}\" 0;        local a0=\"$r\"\n        if starts_with \"$a0\" splice-unquote; then\n            _symbol concat;   local a=\"$r\"\n            _nth \"$a0\" 1;     local b=\"$r\"\n        else\n            _symbol cons;     local a=\"$r\"\n            QUASIQUOTE \"$a0\"; local b=\"$r\"\n        fi\n        _rest \"$1\"\n        qqIter \"$r\"\n        _list \"$a\" \"$b\" \"$r\"\n    fi\n}\n\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    while true; do\n    r=\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              ast=\"${a2}\"\n              env=\"${let_env}\"\n              # Continue loop\n              ;;\n        quote)\n              r=\"${a1}\"\n              return ;;\n        quasiquote)\n              QUASIQUOTE \"${a1}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        defmacro!)\n              EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              local func=\"${r}\"\n              __new_obj_like \"${func}\"\n              ANON[\"${r}\"]=\"${ANON[\"${func}\"]}\"\n              ANON[\"${r}_ismacro_\"]=\"yes\"\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        try__STAR__) EVAL \"${a1}\" \"${env}\"\n              [[ -z \"${__ERROR}\" ]] && return\n              _nth \"${a2}\" 0; local a20=\"${r}\"\n              if [ \"${ANON[\"${a20}\"]}\" == \"catch__STAR__\" ]; then\n                  _nth \"${a2}\" 1; local a21=\"${r}\"\n                  _nth \"${a2}\" 2; local a22=\"${r}\"\n                  _list \"${a21}\"; local binds=\"${r}\"\n                  ENV \"${env}\" \"${binds}\" \"${__ERROR}\"\n                  local try_env=\"${r}\"\n                  __ERROR=\n                  EVAL \"${a22}\" \"${try_env}\"\n              fi  # if no catch* clause, just propagate __ERROR\n              return ;;\n        do)   _count \"${ast}\"\n              _slice \"${ast}\" 1 $(( ${r} - 2 ))\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${ast}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      ast=\"${a3}\"\n                  else\n                      r=\"${__nil}\"\n                      return\n                  fi\n              else\n                  # eval true condition\n                  ast=\"${a2}\"\n              fi\n              # Continue loop\n              ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\" \\\n                        \"${a2}\" \"${env}\" \"${a1}\"\n              return ;;\n        *)    EVAL \"${a0}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              local f=\"${r}\"\n\n              _rest \"${ast}\"\n              # Should cause no error as ast is not empty.\n              local args=\"${r}\"\n\n              if [ \"${ANON[\"${f}_ismacro_\"]}\" ]; then\n                  f=\"${ANON[\"${f}\"]}\"\n                  ${f%%@*} ${ANON[\"${args}\"]}\n                  ast=\"${r}\"\n                  continue\n              fi\n\n              f=\"${ANON[\"${f}\"]}\"\n\n              _map_with_type _list EVAL \"${args}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              args=\"${ANON[\"${r}\"]}\"\n\n              #echo \"invoke: [${f}] ${args}\"\n              if [[ \"${f//@/ }\" != \"${f}\" ]]; then\n                  set -- ${f//@/ }\n                  ast=\"${2}\"\n                  ENV \"${3}\" \"${4}\" ${args}\n                  env=\"${r}\"\n              else\n                  eval ${f%%@*} ${args}\n                  return\n              fi\n              # Continue loop\n              ;;\n    esac\n    done\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n_eval () { EVAL \"${1}\" \"${REPL_ENV}\"; }\n_fref \"eval\" _eval\n_list; argv=\"${r}\"\nfor _arg in \"${@:2}\"; do _string \"${_arg}\"; _conj! \"${argv}\" \"${r}\"; done\n_symbol \"__STAR__ARGV__STAR__\"\nENV_SET \"${REPL_ENV}\" \"${r}\" \"${argv}\";\n\n# core.mal: defined using the language itself\nREP \"(def! not (fn* (a) (if a false true)))\"\nREP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nREP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\n# load/run file from command line (then exit)\nif [[ \"${1}\" ]]; then\n    REP \"(load-file \\\"${1}\\\")\"\n    exit 0\nfi \n\n# repl loop\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/stepA_mal.sh",
    "content": "#!/usr/bin/env bash\n\nsource $(dirname $0)/reader.sh\nsource $(dirname $0)/printer.sh\nsource $(dirname $0)/env.sh\nsource $(dirname $0)/core.sh\n\n# read\nREAD () {\n    [ \"${1}\" ] && r=\"${1}\" || READLINE\n    READ_STR \"${r}\"\n}\n\n# eval\nstarts_with () {\n    _list? \"$1\" && _first \"$1\" && _symbol? \"$r\" && [ \"${ANON[$r]}\" = \"$2\" ]\n}\n\nQUASIQUOTE () {\n    _obj_type \"$1\"\n    case \"$r\" in\n        list)\n            if starts_with \"$1\" unquote; then\n                _nth \"$1\" 1\n            else\n                qqIter \"$1\"\n            fi ;;\n        vector)\n            _symbol vec;  local a=\"$r\"\n            qqIter \"$1\"\n            _list \"$a\" \"$r\" ;;\n        symbol|hash_map)\n            _symbol quote\n            _list \"$r\" \"$1\" ;;\n        *)\n            r=\"$1\" ;;\n    esac\n}\n\nqqIter () {\n    if _empty? \"$1\"; then\n        _list\n    else\n        _nth \"${1}\" 0;        local a0=\"$r\"\n        if starts_with \"$a0\" splice-unquote; then\n            _symbol concat;   local a=\"$r\"\n            _nth \"$a0\" 1;     local b=\"$r\"\n        else\n            _symbol cons;     local a=\"$r\"\n            QUASIQUOTE \"$a0\"; local b=\"$r\"\n        fi\n        _rest \"$1\"\n        qqIter \"$r\"\n        _list \"$a\" \"$b\" \"$r\"\n    fi\n}\n\n_symbol DEBUG-EVAL; debug_eval=\"$r\"\n\nEVAL () {\n    local ast=\"${1}\" env=\"${2}\"\n    while true; do\n    r=\n\n    ENV_GET \"$env\" \"$debug_eval\"\n    if [ -n \"$__ERROR\" ]; then\n        __ERROR=\n    elif [ \"$r\" != \"$__false\" -a \"$r\" != \"$__nil\" ]; then\n        _pr_str \"$ast\" yes; echo \"EVAL: $r / $env\"\n    fi\n\n    _obj_type \"${ast}\"; local ot=\"${r}\"\n    case \"${ot}\" in\n    symbol)\n        ENV_GET \"${env}\" \"${ast}\"\n        return ;;\n    list)\n        ;;\n    vector)\n        _map_with_type _vector EVAL \"${ast}\" \"${env}\"\n        return ;;\n    hash_map)\n        local res=\"\" key= val=\"\" hm=\"${ANON[\"${ast}\"]}\"\n        _hash_map; local new_hm=\"${r}\"\n        eval local keys=\"\\${!${hm}[@]}\"\n        for key in ${keys}; do\n            eval val=\"\\${${hm}[\\\"${key}\\\"]}\"\n            EVAL \"${val}\" \"${env}\"\n            _assoc! \"${new_hm}\" \"${key}\" \"${r}\"\n        done\n        r=\"${new_hm}\"\n        return ;;\n    *)\n        r=\"${ast}\"\n        return ;;\n    esac\n\n    # apply list\n    _empty? \"${ast}\" && r=\"${ast}\" && return\n\n    _nth \"${ast}\" 0; local a0=\"${r}\"\n    _nth \"${ast}\" 1; local a1=\"${r}\"\n    _nth \"${ast}\" 2; local a2=\"${r}\"\n    case \"${ANON[\"${a0}\"]}\" in\n        def!) EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        let__STAR__) ENV \"${env}\"; local let_env=\"${r}\"\n              local let_pairs=(${ANON[\"${a1}\"]})\n              local idx=0\n              #echo \"let: [${let_pairs[*]}] for ${a2}\"\n              while [[ \"${let_pairs[\"${idx}\"]}\" ]]; do\n                  EVAL \"${let_pairs[$(( idx + 1))]}\" \"${let_env}\"\n                  ENV_SET \"${let_env}\" \"${let_pairs[${idx}]}\" \"${r}\"\n                  idx=$(( idx + 2))\n              done\n              ast=\"${a2}\"\n              env=\"${let_env}\"\n              # Continue loop\n              ;;\n        quote)\n              r=\"${a1}\"\n              return ;;\n        quasiquote)\n              QUASIQUOTE \"${a1}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        defmacro!)\n              EVAL \"${a2}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              local func=\"${r}\"\n              __new_obj_like \"${func}\"\n              ANON[\"${r}\"]=\"${ANON[\"${func}\"]}\"\n              ANON[\"${r}_ismacro_\"]=\"yes\"\n              ENV_SET \"${env}\" \"${a1}\" \"${r}\"\n              return ;;\n        sh__STAR__)  EVAL \"${a1}\" \"${env}\"\n              local output=\"\"\n              local line=\"\"\n              r=\"${ANON[\"${r}\"]}\"\n              r=\"${r//__STAR__/*}\"\n              while read -r line || [ -n \"${line}\" ]; do\n                output=\"${output}${line}\"$'\\n'\n              done < <(eval \"${r}\")\n              _string \"${output%$'\\n'}\"\n              return ;;\n        try__STAR__) EVAL \"${a1}\" \"${env}\"\n              [[ -z \"${__ERROR}\" ]] && return\n              _nth \"${a2}\" 0; local a20=\"${r}\"\n              if [ \"${ANON[\"${a20}\"]}\" == \"catch__STAR__\" ]; then\n                  _nth \"${a2}\" 1; local a21=\"${r}\"\n                  _nth \"${a2}\" 2; local a22=\"${r}\"\n                  _list \"${a21}\"; local binds=\"${r}\"\n                  ENV \"${env}\" \"${binds}\" \"${__ERROR}\"\n                  local try_env=\"${r}\"\n                  __ERROR=\n                  EVAL \"${a22}\" \"${try_env}\"\n              fi  # if no catch* clause, just propagate __ERROR\n              return ;;\n        do)   _count \"${ast}\"\n              _slice \"${ast}\" 1 $(( ${r} - 2 ))\n              _map_with_type _list EVAL \"${r}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              _last \"${ast}\"\n              ast=\"${r}\"\n              # Continue loop\n              ;;\n        if)   EVAL \"${a1}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              if [[ \"${r}\" == \"${__false}\" || \"${r}\" == \"${__nil}\" ]]; then\n                  # eval false form\n                  _nth \"${ast}\" 3; local a3=\"${r}\"\n                  if [[ \"${a3}\" ]]; then\n                      ast=\"${a3}\"\n                  else\n                      r=\"${__nil}\"\n                      return\n                  fi\n              else\n                  # eval true condition\n                  ast=\"${a2}\"\n              fi\n              # Continue loop\n              ;;\n        fn__STAR__)  _function \"ENV \\\"${env}\\\" \\\"${a1}\\\" \\\"\\${@}\\\"; \\\n                         EVAL \\\"${a2}\\\" \\\"\\${r}\\\"\" \\\n                        \"${a2}\" \"${env}\" \"${a1}\"\n              return ;;\n        *)    EVAL \"${a0}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && return 1\n              local f=\"${r}\"\n\n              _rest \"${ast}\"\n              # Should cause no error as ast is not empty.\n              local args=\"${r}\"\n\n              if [ \"${ANON[\"${f}_ismacro_\"]}\" ]; then\n                  f=\"${ANON[\"${f}\"]}\"\n                  ${f%%@*} ${ANON[\"${args}\"]}\n                  ast=\"${r}\"\n                  continue\n              fi\n\n              f=\"${ANON[\"${f}\"]}\"\n\n              _map_with_type _list EVAL \"${args}\" \"${env}\"\n              [[ \"${__ERROR}\" ]] && r= && return 1\n              args=\"${ANON[\"${r}\"]}\"\n\n              #echo \"invoke: [${f}] ${args}\"\n              if [[ \"${f//@/ }\" != \"${f}\" ]]; then\n                  set -- ${f//@/ }\n                  ast=\"${2}\"\n                  ENV \"${3}\" \"${4}\" ${args}\n                  env=\"${r}\"\n              else\n                  eval ${f%%@*} ${args}\n                  return\n              fi\n              # Continue loop\n              ;;\n    esac\n    done\n}\n\n# print\nPRINT () {\n    if [[ \"${__ERROR}\" ]]; then\n        _pr_str \"${__ERROR}\" yes\n        r=\"Error: ${r}\"\n        __ERROR=\n    else\n        _pr_str \"${1}\" yes\n    fi\n}\n\n# repl\nENV; REPL_ENV=\"${r}\"\nREP () {\n    r=\n    READ \"${1}\"\n    EVAL \"${r}\" \"${REPL_ENV}\"\n    PRINT \"${r}\"\n}\n\n# core.sh: defined using bash\n_fref () {\n    _symbol \"${1}\"; local sym=\"${r}\"\n    _function \"${2} \\\"\\${@}\\\"\"\n    ENV_SET \"${REPL_ENV}\" \"${sym}\" \"${r}\"\n}\nfor n in \"${!core_ns[@]}\"; do _fref \"${n}\" \"${core_ns[\"${n}\"]}\"; done\n_eval () { EVAL \"${1}\" \"${REPL_ENV}\"; }\n_fref \"eval\" _eval\n_list; argv=\"${r}\"\nfor _arg in \"${@:2}\"; do _string \"${_arg}\"; _conj! \"${argv}\" \"${r}\"; done\n_symbol \"__STAR__ARGV__STAR__\"\nENV_SET \"${REPL_ENV}\" \"${r}\" \"${argv}\";\n\n# core.mal: defined using the language itself\nREP \"(def! *host-language* \\\"bash\\\")\"\nREP \"(def! not (fn* (a) (if a false true)))\"\nREP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nREP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\n# load/run file from command line (then exit)\nif [[ \"${1}\" ]]; then\n    REP \"(load-file \\\"${1}\\\")\"\n    exit 0\nfi \n\n# repl loop\nREP \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"\nwhile true; do\n    READLINE \"user> \" || exit \"$?\"\n    [[ \"${r}\" ]] && REP \"${r}\" && echo \"${r}\"\ndone\n"
  },
  {
    "path": "impls/bash/tests/stepA_mal.mal",
    "content": ";; Testing basic bash interop\n\n(sh* \"echo 7\")\n;=>\"7\"\n\n(sh* \"echo >&2 hello\")\n;/hello\n;=>\"\"\n\n(sh* \"foo=8; echo ${foo}\")\n;=>\"8\"\n\n(sh* \"for x in a b c; do echo -n \\\"X${x}Y \\\"; done; echo\")\n;=>\"XaY XbY XcY\"\n\n(sh* \"for x in 1 2 3; do echo -n \\\"$((1+$x)) \\\"; done; echo\")\n;=>\"2 3 4\"\n\n(sh* \"for x in {1..10}; do echo $x; done\")\n;=>\"1\\n2\\n3\\n4\\n5\\n6\\n7\\n8\\n9\\n10\"\n\n(sh* \"echo -n {1..3}\")\n;=>\"1 2 3\"\n\n(sh* \"echo hello; echo foo; echo yes;\")\n;=>\"hello\\nfoo\\nyes\"\n\n(sh* \"grep -oE '\\[.*!\\]' core.sh\")\n;=>\"[reset!]\\n[swap!]\"\n\n(sh* \"ls cor*.sh\")\n;=>\"core.sh\"\n"
  },
  {
    "path": "impls/bash/types.sh",
    "content": "#\n# mal (Make a Lisp) object types\n#\n\nif [ -z \"${__mal_types_included__}\" ]; then\n__mal_types_included=true\n\ndeclare -A ANON\n\n__obj_magic=__5bal7\n__keyw=$(echo -en \"\\xCA\\x9E\") # \\u029E\n__obj_hash_code=${__obj_hash_code:-0}\n\n__new_obj_hash_code () {\n    __obj_hash_code=$(( __obj_hash_code + 1))\n    r=\"${__obj_hash_code}\"\n}\n\n__new_obj () {\n    __new_obj_hash_code\n    r=\"${1}_${r}\"\n}\n\n__new_obj_like () {\n    __new_obj_hash_code\n    r=\"${1%_*}_${r}\"\n}\n\n\n# Errors/Exceptions\n\n__ERROR=\n_error() {\n    _string \"${1}\"\n    __ERROR=\"${r}\"\n    r=\n}\n\n\n\n#\n# General functions\n#\n\n# Return the type of the object (or \"make\" if it's not a object\n_obj_type () {\n    local type=\"${1:0:4}\"\n    r=\n    case \"${type}\" in\n        symb) r=\"symbol\" ;;\n        list) r=\"list\" ;;\n        numb) r=\"number\" ;;\n        func) r=\"function\" ;;\n        strn)\n            local s=\"${ANON[\"${1}\"]}\"\n            if [[ \"${1:0:1}\" = \"${__keyw}\" ]] \\\n                || [[ \"${1:0:2}\" = \"${__keyw}\" ]]; then\n                r=\"keyword\"\n            else\n                r=\"string\"\n            fi ;;\n        _nil) r=\"nil\" ;;\n        true) r=\"true\" ;;\n        fals) r=\"false\" ;;\n        vect) r=\"vector\" ;;\n        hmap) r=\"hash_map\" ;;\n        atom) r=\"atom\" ;;\n        undf) r=\"undefined\" ;;\n        *)    r=\"bash\" ;;\n    esac\n}\n\n_equal? () {\n    _obj_type \"${1}\"; local ot1=\"${r}\"\n    _obj_type \"${2}\"; local ot2=\"${r}\"\n    if [[ \"${ot1}\" != \"${ot2}\" ]]; then\n        if ! _sequential? \"${1}\" || ! _sequential? \"${2}\"; then\n            return 1\n        fi\n    fi\n    case \"${ot1}\" in\n    string|symbol|keyword|number)\n        [[ \"${ANON[\"${1}\"]}\" == \"${ANON[\"${2}\"]}\" ]] ;;\n    list|vector)\n        _count \"${1}\"; local sz1=\"${r}\"\n        _count \"${2}\"; local sz2=\"${r}\"\n        [[ \"${sz1}\" == \"${sz2}\" ]] || return 1\n        local a1=(${ANON[\"${1}\"]})\n        local a2=(${ANON[\"${2}\"]})\n        for ((i=0;i<${#a1[*]};i++)); do\n            _equal? \"${a1[${i}]}\" \"${a2[${i}]}\" || return 1\n        done\n        ;;\n    hash_map)\n        local hm1=\"${ANON[\"${1}\"]}\"\n        eval local ks1=\"\\${!${hm1}[@]}\"\n        local hm2=\"${ANON[\"${2}\"]}\"\n        eval local ks2=\"\\${!${hm2}[@]}\"\n        [[ \"${#ks1}\" == \"${#ks2}\" ]] || return 1\n        for k in ${ks1}; do\n            eval v1=\"\\${${hm1}[\\\"${k}\\\"]}\"\n            eval v2=\"\\${${hm2}[\\\"${k}\\\"]}\"\n            [ \"${v1}\" ] || return 1\n            [ \"${v2}\" ] || return 1\n            _equal? \"${v1}\" \"${v2}\" || return 1\n        done\n        ;;\n    *)\n        [[ \"${1}\" == \"${2}\" ]] ;;\n    esac\n}\n\n# Constant atomic values\n\n__nil=_nil_0\n__true=true_0\n__false=fals_0\n\n_nil? () { [[ ${1} =~ ^_nil_ ]]; }\n_true? () { [[ ${1} =~ ^true_ ]]; }\n_false? () { [[ ${1} =~ ^fals_ ]]; }\n\n\n# Symbols\n\n_symbol () {\n    __new_obj_hash_code\n    r=\"symb_${r}\"\n    ANON[\"${r}\"]=\"${1//\\*/__STAR__}\"\n}\n_symbol? () { [[ ${1} =~ ^symb_ ]]; }\n\n\n# Keywords\n\n_keyword () {\n    local k=\"${1}\"\n    __new_obj_hash_code\n    r=\"strn_${r}\"\n    if [[ \"${1:0:1}\" = \"${__keyw}\" ]] \\\n        || [[ \"${1:0:2}\" = \"${__keyw}\" ]]; then\n        true\n    else\n        k=\"${__keyw}${1}\"\n    fi\n    ANON[\"${r}\"]=\"${k//\\*/__STAR__}\"\n}\n_keyword? () {\n    [[ ${1} =~ ^strn_ ]] || return 1\n    local s=\"${ANON[\"${1}\"]}\"\n    [[ \"${s:0:1}\" = \"${__keyw}\" ]] || [[ \"${s:0:2}\" = \"${__keyw}\" ]]\n}\n\n\n# Numbers\n\n_number () {\n    __new_obj_hash_code\n    r=\"numb_${r}\"\n    ANON[\"${r}\"]=\"${1}\"\n}\n_number? () { [[ ${1} =~ ^numb_ ]]; }\n\n\n# Strings\n\n_string () {\n    __new_obj_hash_code\n    r=\"strn_${r}\"\n    ANON[\"${r}\"]=\"${1//\\*/__STAR__}\"\n}\n_string? () { [[ ${1} =~ ^strn_ ]]; }\n\n\n# Functions\n# Return a function object. The first parameter is the\n# function 'source'.\n_function () {\n    __new_obj_hash_code\n    eval \"function ${__obj_magic}_func_${r} () { ${1%;} ; }\"\n    r=\"func_${r}\"\n    if [[ \"${2}\" ]]; then\n        # Native function\n        ANON[\"${r}\"]=\"${__obj_magic}_${r}@${2}@${3}@${4}\"\n    else\n        # Bash function\n        ANON[\"${r}\"]=\"${__obj_magic}_${r}\"\n    fi\n}\n_function? () { [[ ${1} =~ ^func_ ]]; }\n\n\n# Lists\n\n_list () {\n    __new_obj_hash_code\n    r=\"list_${r}\"\n    ANON[\"${r}\"]=\"${*}\"\n}\n_list? () { [[ ${1} =~ ^list_ ]]; }\n\n\n# Vectors\n\n_vector () {\n    __new_obj_hash_code\n    r=\"vector_${r}\"\n    ANON[\"${r}\"]=\"${*}\"\n}\n_vector? () { [[ ${1} =~ ^vector_ ]]; }\n\nvec () {\n    __new_obj_hash_code\n    r=\"vector_$r\"\n    ANON[\"$r\"]=${ANON[\"$1\"]}\n}\n\n\n# hash maps (associative arrays)\n\n_hash_map () {\n    __new_obj_hash_code\n    local name=\"hmap_${r}\"\n    local obj=\"${__obj_magic}_${name}\"\n    declare -A -g ${obj}; eval \"${obj}=()\"\n    ANON[\"${name}\"]=\"${obj}\"\n\n    while [[ \"${1}\" ]]; do\n        eval ${obj}[\\\"${ANON[\"${1}\"]}\\\"]=\\\"${2}\\\"\n        shift; shift\n    done\n\n    r=\"${name}\"\n}\n_hash_map? () { [[ ${1} =~ ^hmap_ ]]; }\n\n_contains? () {\n    local obj=\"${ANON[\"${1}\"]}\"\n    eval [[ \"\\${${obj}[\\\"${2}\\\"]+isset}\" ]]\n}\n\n_copy_hash_map () {\n    local orig_obj=\"${ANON[\"${1}\"]}\"\n    _hash_map\n    local name=\"${r}\"\n    local obj=\"${ANON[\"${name}\"]}\"\n\n    # Copy the existing key/values to the new object\n    local temp=$(typeset -p ${orig_obj})\n    eval ${temp/#declare -A ${orig_obj}=/declare -A -g ${obj}=}\n    r=\"${name}\"\n}\n\n# Return same hash map with keys/values added/mutated in place\n_assoc! () {\n    local obj=${ANON[\"${1}\"]}; shift\n    declare -A -g ${obj}\n\n    # Set the key/values specified\n    while [[ \"${1}\" ]]; do\n        eval ${obj}[\\\"${1}\\\"]=\\\"${2}\\\"\n        shift; shift\n    done\n}\n\n# Return same hash map with keys/values deleted/mutated in place\n_dissoc! () {\n    local obj=${ANON[\"${1}\"]}; shift\n    declare -A -g ${obj}\n\n    # Delete the key/values specified\n    while [[ \"${1}\" ]]; do\n        eval unset ${obj}[\\\"${1}\\\"]\n        shift\n    done\n}\n\n\n# Atoms\n\n_atom() {\n    __new_obj_hash_code\n    r=\"atom_${r}\"\n    ANON[\"${r}\"]=\"${*}\"\n}\n_atom? () { [[ ${1} =~ ^atom_ ]]; }\n\n\n# sequence operations\n\n_sequential? () {\n    _list? \"${1}\" || _vector? \"${1}\"\n}\n\n_nth () {\n    local temp=(${ANON[\"${1}\"]})\n    r=\"${temp[${2}]}\"\n}\n\n_first () {\n    local temp=\"${ANON[\"${1}\"]}\"\n    r=\"${temp%% *}\"\n    [ \"${r}\" ] || r=\"${__nil}\"\n}\n\n_last () {\n    local temp=\"${ANON[\"${1}\"]}\"\n    r=\"${temp##* }\"\n}\n\n# Creates a new vector/list of the everything after but the first\n# element\n_rest () {\n    local temp=\"${ANON[\"${1}\"]}\"\n    _list\n    if [[ \"${temp#* }\" == \"${temp}\" ]]; then\n        ANON[\"${r}\"]=\n    else\n        ANON[\"${r}\"]=\"${temp#* }\"\n    fi\n}\n\n\n_empty? () { [[ -z \"${ANON[\"${1}\"]}\" ]]; }\n\n# conj that mutates in place (and always appends)\n_conj! () {\n    local obj=\"${1}\"; shift\n    local obj_data=\"${ANON[\"${obj}\"]}\"\n    ANON[\"${obj}\"]=\"${obj_data:+${obj_data} }${*}\"\n    r=\"${1}\"\n}\n\n\n\n_count () {\n    if _nil? \"${1}\"; then\n        r=\"0\"\n    else\n        local temp=(${ANON[\"${1}\"]})\n        r=${#temp[*]}\n    fi\n}\n\n# Slice a sequence object $1 starting at $2 of length $3\n_slice () {\n    local temp=(${ANON[\"${1}\"]})\n    __new_obj_like \"${1}\"\n    ANON[\"${r}\"]=\"${temp[@]:${2}:${3}}\"\n}\n\n# Takes a bash function and an list object and invokes the function on\n# each element of the list, returning a new list (or vector) of the results.\n_map_with_type () {\n    local constructor=\"${1}\"; shift\n    local f=\"${1}\"; shift\n    local items=\"${ANON[\"${1}\"]}\"; shift\n    eval \"${constructor}\"; local new_seq=\"${r}\"\n    for v in ${items}; do\n        #echo eval ${f%%@*} \"${v}\" \"${@}\"\n        eval ${f%%@*} \"${v}\" \"${@}\"\n        [[ \"${__ERROR}\" ]] && r= && return 1\n        _conj! \"${new_seq}\" \"${r}\"\n    done\n    r=\"${new_seq}\"\n}\n\n_map () {\n    _map_with_type _list \"${@}\"\n}\n\nfi\n"
  },
  {
    "path": "impls/basic/.args.mal",
    "content": "(def! -*ARGS*- (list ))\n"
  },
  {
    "path": "impls/basic/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install \\\n    ca-certificates curl gcc g++ libasound2-dev \\\n    libglu1-mesa-dev mesa-common-dev patch unzip wget \\\n    xz-utils libncurses-dev\n\n# cbmbasic\n# Remove duplicate RAM (https://github.com/mist64/cbmbasic/commit/352a313313dd0a15a47288c8f8031b54ac8c92a2).\nRUN cd /tmp && \\\n    curl -L https://github.com/kanaka/cbmbasic/archive/master.zip -o cbmbasic.zip && \\\n    unzip cbmbasic.zip && \\\n    cd cbmbasic-master && \\\n    sed -i '/unsigned char RAM.65536.;/d' runtime.c && \\\n    make && \\\n    mv cbmbasic /usr/local/bin && \\\n    cd .. && \\\n    rm -r cbmbasic*\n\n# qbasic (using freebasic: `fbc -lang qb`)\nRUN cd /opt && \\\n    curl -L https://sourceforge.net/projects/fbc/files/FreeBASIC-1.10.1/Binaries-Linux/FreeBASIC-1.10.1-ubuntu-22.04-x86_64.tar.xz | tar xvJf - && \\\n    ln -sf /opt/FreeBASIC-1.10.1-ubuntu-22.04-x86_64/bin/fbc /usr/local/bin/fbc\n\n"
  },
  {
    "path": "impls/basic/Makefile",
    "content": "basic_MODE = cbm\nBASICPP_OPTS = --mode $(basic_MODE)\n\nFBC = fbc -lang qb\n\nSTEPS4_A = step4_if_fn_do.bas step5_tco.bas step6_file.bas \\\n           step7_quote.bas step8_macros.bas step9_try.bas stepA_mal.bas\nSTEPS3_A = step3_env.bas $(STEPS4_A)\nSTEPS1_A = step1_read_print.bas step2_eval.bas $(STEPS3_A)\nSTEPS0_A = step0_repl.bas $(STEPS1_A)\n\nall: $(if $(filter qbasic,$(basic_MODE)),$(subst .bas,,$(STEPS0_A)),$(STEPS0_A))\n\n$(STEPS0_A): debug.in.bas mem.in.bas readline.in.bas\n$(STEPS1_A): types.in.bas reader.in.bas printer.in.bas\n$(STEPS3_A): env.in.bas\n$(STEPS4_A): core.in.bas\n\n\n\nstep%.bas: step%.in.bas\n\t./basicpp.py $(BASICPP_OPTS) $< > $@\n\ntests/%.bas: tests/%.in.bas\n\t./basicpp.py $(BASICPP_OPTS) $< > $@\n\n# QBasic specific compilation rule\nstep%: step%.bas\n\t$(FBC) $< -x $@\n\n# CBM/C64 image rules\n\n%.prg: %.bas\n\tcat $< | tr \"A-Z\" \"a-z\" > $<.tmp\n\t#cat $< | sed 's/[\"]\\@<!\\<\\w\\+\\>[\"]\\@!/\\L&/g' > $<.tmp\n\tpetcat -w2 -nc -o $@ $<.tmp\n\t#rm $<.tmp\n\nmal.prg: stepA_mal.prg\n\tcp $< $@\n\n.args.mal.prg: .args.mal\n\tpetcat -text -w2 -o $@ $<\n\ncore.mal.prg: ../core.mal\n\tpetcat -text -w2 -o $@ $<\n\nmal.d64: mal.prg .args.mal.prg core.mal.prg\n\tc1541 -format \"mal,01\" d64 $@ \\\n\t    -attach $@ \\\n\t    -write $< mal \\\n\t    -write .args.mal.prg .args.mal \\\n\t    -write core.mal.prg core.mal\n\n\n# Clean and Stats rules\n\n.PHONY: clean\n\nclean:\n\trm -f $(STEPS0_A) $(subst .bas,,$(STEPS0_A)) *.d64 *.prg\n\trm -rf ./internal\n"
  },
  {
    "path": "impls/basic/basicpp.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport argparse\nimport re\nimport sys\n\ndef debug(*args, **kwargs):\n    print(*args, file=sys.stderr, **kwargs)\n\ndef parse_args():\n    parser = argparse.ArgumentParser(description='Preprocess Basic code.')\n    parser.add_argument('infiles', type=str, nargs='+',\n                        help='the Basic files to preprocess')\n    parser.add_argument('--mode', choices=[\"cbm\", \"qbasic\"], default=\"cbm\")\n    parser.add_argument('--keep-rems', action='store_true', default=False,\n                        help='The type of REMs to keep (0 (none) -> 4 (all)')\n    parser.add_argument('--keep-blank-lines', action='store_true', default=False,\n                        help='Keep blank lines from the original file')\n    parser.add_argument('--keep-indent', action='store_true', default=False,\n                        help='Keep line identing')\n    parser.add_argument('--skip-misc-fixups', action='store_true', default=False,\n                        help='Skip miscellaneous fixup/shrink fixups')\n    parser.add_argument('--skip-combine-lines', action='store_true', default=False,\n                        help='Do not combine lines using the \":\" separator')\n\n    args = parser.parse_args()\n    if args.keep_rems and not args.skip_combine_lines:\n        debug(\"Option --keep-rems implies --skip-combine-lines \")\n        args.skip_combine_lines = True\n\n    if args.mode == 'qbasic' and not args.skip_misc_fixups:\n        debug(\"Mode 'qbasic' implies --skip-misc-fixups\")\n        args.skip_misc_fixups = True\n\n    return args\n\n# pull in include files\ndef resolve_includes(orig_lines, args):\n    included = {}\n    lines = orig_lines[:]\n    position = 0\n    while position < len(lines):\n        line = lines[position]\n        m = re.match(r\"^(?:#([^ ]*) )? *REM \\$INCLUDE: '([^'\\n]*)' *$\", line)\n        if m:\n            mode = m.group(1)\n            f = m.group(2)\n            if mode and mode != args.mode:\n                position += 1\n            elif f not in included:\n                ilines = [l.rstrip() for l in open(f).readlines()]\n                if args.keep_rems: lines.append(\"REM vvv BEGIN '%s' vvv\" % f)\n                lines[position:position+1] = ilines\n                if args.keep_rems: lines.append(\"REM ^^^ END '%s' ^^^\" % f)\n            else:\n                debug(\"Ignoring already included file: %s\" % f)\n        else:\n            position += 1\n    return lines\n\ndef resolve_mode(orig_lines, args):\n    lines = []\n    for line in orig_lines:\n        m = re.match(r\"^ *#([^ \\n]*) *([^\\n]*)$\", line)\n        if m:\n            if m.group(1) == args.mode:\n                lines.append(m.group(2))\n            continue\n        lines.append(line)\n    return lines\n\ndef drop_blank_lines(orig_lines):\n    lines = []\n    for line in orig_lines:\n        if re.match(r\"^\\W*$\", line): continue\n        lines.append(line)\n    return lines\n\n\ndef drop_rems(orig_lines):\n    lines = []\n    for line in orig_lines:\n        if re.match(r\"^ *REM\", line):\n            continue\n        m = re.match(r\"^(.*): *REM .*$\", line)\n        if m:\n            lines.append(m.group(1))\n        else:\n            lines.append(line)\n    return lines\n\ndef remove_indent(orig_lines):\n    lines = []\n    for line in orig_lines:\n        m = re.match(r\"^ *([^ \\n].*)$\", line)\n        lines.append(m.group(1))\n    return lines\n\ndef misc_fixups(orig_lines):\n    text = \"\\n\".join(orig_lines)\n\n    # Remove GOTO after THEN\n    text = re.sub(r\"\\bTHEN GOTO\\b\", \"THEN\", text)\n\n    # Remove spaces after keywords\n    text = re.sub(r\"\\bIF \", \"IF\", text)\n    text = re.sub(r\"\\bPRINT *\", \"PRINT\", text)\n    text = re.sub(r\"\\bDIM \", \"DIM\", text)\n    text = re.sub(r\"\\bOPEN \", \"OPEN\", text)\n    text = re.sub(r\"\\bGET \", \"GET\", text)\n    text = re.sub(r\"\\bPOKE \", \"POKE\", text)\n    text = re.sub(r\"\\bCLOSE \", \"CLOSE\", text)\n    text = re.sub(r\"\\bFOR \", \"FOR\", text)\n    text = re.sub(r\" TO \", \"TO\", text)\n    text = re.sub(r\"\\bNEXT \", \"NEXT\", text)\n\n    # Remove spaces around GOTO/GOSUB/THEN\n    text = re.sub(r\" *GOTO *\", \"GOTO\", text)\n    text = re.sub(r\" *GOSUB *\", \"GOSUB\", text)\n    text = re.sub(r\" *THEN *\", r\"THEN\", text)\n\n    # Remove spaces around AND/OR except after ST\n    text = re.sub(r\"(?<!ST) *AND *\", r\"AND\", text)\n    text = re.sub(r\"([^A-Z]) *OR *\", r\"\\g<1>OR\", text)\n\n    return text.split(\"\\n\")\n\ndef finalize(lines, args):\n    labels_lines = {}\n    lines_labels = {}\n    call_index = {}\n\n    cur_sub = None\n\n    # number lines, remove labels (but track line number), and replace\n    # CALLs with a stack based GOTO\n    src_lines = lines\n    lines = []\n    lnum=1\n    for line in src_lines:\n\n        # Drop labels (track line number for GOTO/GOSUB)\n        m = re.match(r\"^ *([^ :\\n]*): *$\", line)\n        if m:\n            label = m.groups(1)[0]\n            labels_lines[label] = lnum\n            lines_labels[lnum] = label\n            continue\n\n        if re.match(r\".*CALL  *([^ :\\n]*) *:\", line):\n            raise Exception(\"CALL is not the last thing on line %s\" % lnum)\n\n        # Replace CALLs (track line number for replacement later)\n        #m = re.match(r\"\\bCALL  *([^ :]*) *$\", line)\n        m = re.match(r\"(.*)CALL  *([^ :\\n]*) *$\", line)\n        if m:\n            prefix = m.groups(1)[0]\n            sub = m.groups(1)[1]\n            if not sub in call_index:\n                call_index[sub] = 0\n            call_index[sub] += 1\n            label = sub+\"_\"+str(call_index[sub])\n\n            # Replace the CALL with stack based GOTO\n            if args.mode == \"cbm\":\n                lines.append(\"%s %sQ=%s:GOSUBPUSH_Q:GOTO%s\" % (\n                    lnum, prefix, call_index[sub], sub))\n            else:\n                lines.append(\"%s %sX=X+1:X%%(X)=%s:GOTO %s\" % (\n                    lnum, prefix, call_index[sub], sub))\n            lnum += 1\n\n            # Add the return spot\n            labels_lines[label] = lnum\n            lines_labels[lnum] = label\n            continue\n\n        lines.append(\"%s %s\" % (lnum, line))\n        lnum += 1\n\n    # remove SUB (but track lines), and replace END SUB with ON GOTO\n    # that returns to original caller\n    src_lines = lines\n    lines = []\n    lnum=1\n    for line in src_lines:\n        # Drop subroutine defs (track line number for CALLS)\n        m = re.match(r\"^([0-9][0-9]*)  *SUB  *([^ \\n]*) *$\", line)\n        if m:\n            lnum =  int(m.groups(1)[0])+1\n            label = m.groups(1)[1]\n            cur_sub = label\n            labels_lines[label] = lnum\n            lines_labels[lnum] = label\n            continue\n\n        # Drop END SUB (track line number for replacement later)\n        m = re.match(r\"^([0-9][0-9]*)  *END SUB *$\", line)\n        if m:\n            if cur_sub == None:\n                raise Exception(\"END SUB found without preceeding SUB\")\n            lnum =  int(m.groups(1)[0])\n            index = call_index[cur_sub]\n\n            ret_labels = [cur_sub+\"_\"+str(i) for i in range(1, index+1)]\n            if args.mode == \"cbm\":\n                line = \"%s GOSUBPOP_Q:ONQGOTO%s\" % (lnum, \",\".join(ret_labels))\n            else:\n                line = \"%s X=X-1:ON X%%(X+1) GOTO %s\" % (lnum, \",\".join(ret_labels))\n            cur_sub = None\n\n        lines.append(line)\n\n    def update_labels_lines(text, a, b):\n        stext = \"\"\n        while stext != text:\n            stext = text\n            text = re.sub(r\"(THEN *)%s\\b\" % a, r\"\\g<1>%s\" % b, stext)\n            #text = re.sub(r\"(THEN)%s\\b\" % a, r\"THEN%s\" % b, stext)\n            if args.mode == \"cbm\":\n                text = re.sub(r\"ON *([^:\\n]*) *GOTO *([^:\\n]*)\\b%s\\b\" % a, r\"ON\\g<1>GOTO\\g<2>%s\" % b, text)\n                text = re.sub(r\"ON *([^:\\n]*) *GOSUB *([^:\\n]*)\\b%s\\b\" % a, r\"ON\\g<1>GOSUB\\g<2>%s\" % b, text)\n            else:\n                text = re.sub(r\"(ON [^:\\n]* *GOTO *[^:\\n]*)\\b%s\\b\" % a, r\"\\g<1>%s\" % b, text)\n                text = re.sub(r\"(ON [^:\\n]* *GOSUB *[^:\\n]*)\\b%s\\b\" % a, r\"\\g<1>%s\" % b, text)\n            text = re.sub(r\"(GOSUB *)%s\\b\" % a, r\"\\g<1>%s\" % b, text)\n            text = re.sub(r\"(GOTO *)%s\\b\" % a, r\"\\g<1>%s\" % b, text)\n            #text = re.sub(r\"(GOTO)%s\\b\" % a, r\"\\1%s\" % b, text)\n        return text\n\n    # search for and replace GOTO/GOSUBs\n    src_lines = lines\n    text = \"\\n\".join(lines)\n    for label, lnum in labels_lines.items():\n        text = update_labels_lines(text, label, lnum)\n    lines = text.split(\"\\n\")\n\n    # combine lines\n    if not args.skip_combine_lines:\n        renumber = {}\n        src_lines = lines\n        lines = []\n        pos = 0\n        acc_line = \"\"\n        def renum(line):\n            lnum = len(lines)+1\n            renumber[old_num] = lnum\n            return \"%s %s\" % (lnum, line)\n        while pos < len(src_lines):\n            line = src_lines[pos]\n            m = re.match(r\"^([0-9]*) (.*)$\", line)\n            old_num = int(m.group(1))\n            line = m.group(2)\n\n            if acc_line == \"\":\n                # Starting a new line\n                acc_line = renum(line)\n            elif old_num in lines_labels or re.match(r\"^ *FOR\\b.*\", line):\n                # This is a GOTO/GOSUB target or FOR loop so it must\n                # be on a line by itself\n                lines.append(acc_line)\n                acc_line = renum(line)\n            elif re.match(r\".*(?:GOTO|THEN|RETURN).*\", acc_line):\n                # GOTO/THEN/RETURN are last thing on the line\n                lines.append(acc_line)\n                acc_line = renum(line)\n            # TODO: not sure why this is 88 rather than 80\n            elif len(acc_line) + 1 + len(line) < 88:\n                # Continue building up the line\n                acc_line = acc_line + \":\" + line\n                # GOTO/IF/RETURN must be the last things on a line so\n                # start a new line\n                if re.match(r\".*(?:GOTO|THEN|RETURN).*\", line):\n                    lines.append(acc_line)\n                    acc_line = \"\"\n            else:\n                # Too long so start a new line\n                lines.append(acc_line)\n                acc_line = renum(line)\n            pos += 1\n        if acc_line != \"\":\n            lines.append(acc_line)\n\n        # Finally renumber GOTO/GOSUBS\n        src_lines = lines\n        text = \"\\n\".join(lines)\n        # search for and replace GOTO/GOSUBs\n        for a in sorted(renumber.keys()):\n            b = renumber[a]\n            text = update_labels_lines(text, a, b)\n        lines = text.split(\"\\n\")\n\n    return lines\n\nif __name__ == '__main__':\n    args = parse_args()\n\n    debug(\"Preprocessing basic files: \"+\", \".join(args.infiles))\n\n    # read in lines\n    lines = [l.rstrip() for f in args.infiles\n            for l in open(f).readlines()]\n    debug(\"Original lines: %s\" % len(lines))\n\n    # pull in include files\n    lines = resolve_includes(lines, args)\n    debug(\"Lines after includes: %s\" % len(lines))\n\n    lines = resolve_mode(lines, args)\n    debug(\"Lines after resolving mode specific lines: %s\" % len(lines))\n\n    # drop blank lines\n    if not args.keep_blank_lines:\n        lines = drop_blank_lines(lines)\n        debug(\"Lines after dropping blank lines: %s\" % len(lines))\n\n    # keep/drop REMs\n    if not args.keep_rems:\n        lines = drop_rems(lines)\n        debug(\"Lines after dropping REMs: %s\" % len(lines))\n\n    # keep/remove the indenting\n    if not args.keep_indent:\n        lines = remove_indent(lines)\n\n    # apply some miscellaneous simple fixups/regex transforms\n    if not args.skip_misc_fixups:\n        lines = misc_fixups(lines)\n\n    # number lines, drop/keep labels, combine lines\n    lines = finalize(lines, args)\n    debug(\"Lines after finalizing: %s\" % len(lines))\n\n    print(\"\\n\".join(lines))\n"
  },
  {
    "path": "impls/basic/cbmbasic_console.patch",
    "content": "diff --git a/runtime.c b/runtime.c\nindex 3066580..c635bd4 100644\n--- a/runtime.c\n+++ b/runtime.c\n@@ -535,7 +535,8 @@ printf(\"CHROUT: %d @ %x,%x,%x,%x\\n\", A, a, b, c, d);\n                     left_cursor();\r\n                     break;\r\n                 case '\"':\r\n-                    kernal_quote = 1;\r\n+                    // jdm: this doesn't match C64 behavior\r\n+                    //kernal_quote = 1;\r\n                     // fallthrough\r\n                 default:\r\n                     putchar(A);\r\n@@ -838,8 +839,10 @@ GETIN() {\n         /*Notice that EOF is also turned off in non-canonical mode*/\r\n         A = getchar();\r\n         if (A == 255) { A = 4; } // map actual EOF to 4\r\n+\r\n+        // jdm: this doesn't match C64 behavior\r\n         /* Simulate echo */\r\n-        if (A != 0 && A != 4) { putchar(A); }\r\n+        //if (A != 0 && A != 4) { putchar(A); }\r\n \r\n         /*restore the old settings*/\r\n         tcsetattr( STDIN_FILENO, TCSANOW, &oldt);\r\n"
  },
  {
    "path": "impls/basic/core.in.bas",
    "content": "REM APPLY should really be in types.in.bas but it is here because it\nREM calls DO_TCO_FUNCTION so it will cause syntax errors for steps1-3\nREM if it is in types.in.bas because there are unresolved labels.\n\nREM APPLY(F, AR) -> R\nREM   - restores E\nREM   - call using GOTO and with return label/address on the stack\nSUB APPLY\n  REM if metadata, get the actual object\n  GOSUB TYPE_F\n  IF T=14 THEN F=Z%(F+1):GOSUB TYPE_F\n\n  ON T-8 GOTO APPLY_FUNCTION,APPLY_MAL_FUNCTION,APPLY_MAL_FUNCTION\n\n  APPLY_FUNCTION:\n    REM regular function\n    IF Z%(F+1)<64 THEN GOSUB DO_FUNCTION:GOTO APPLY_DONE\n    REM for recur functions (apply, map, swap!), use GOTO\n    IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n    GOTO APPLY_DONE\n\n  APPLY_MAL_FUNCTION:\n    Q=E:GOSUB PUSH_Q: REM save the current environment\n\n    REM create new environ using env and params stored in the\n    REM function and bind the params to the apply arguments\n    C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n    A=Z%(F+1):E=R:CALL EVAL\n\n    AY=E:GOSUB RELEASE: REM release the new environment\n\n    GOSUB POP_Q:E=Q: REM pop/restore the saved environment\n\n  APPLY_DONE:\nEND SUB\n\n\nREM DO_TCO_FUNCTION(F, AR)\nSUB DO_TCO_FUNCTION\n  G=Z%(F+1)\n\n  REM Get argument values\n  A=Z%(AR+2)\n  B=Z%(Z%(AR+1)+2)\n\nREM PRINT \"F:\"+STR$(F)+\", Z%(F):\"+STR$(Z%(F))+\", G:\"+STR$(G)\n  ON G-64 GOTO DO_APPLY,DO_MAP,DO_SWAP_BANG\n\n  DO_APPLY:\n    F=A\n    AR=Z%(AR+1)\n    A=AR:GOSUB COUNT:C=R\n\n    A=Z%(AR+2)\n    REM no intermediate args, but not a list, so convert it first\n    GOSUB TYPE_A\n    IF C<=1 AND T<>6 THEN T=6:GOSUB FORCE_SEQ_TYPE:GOTO DO_APPLY_2\n    REM no intermediate args, just call APPLY directly\n    IF C<=1 THEN GOTO DO_APPLY_1\n\n    REM prepend intermediate args to final args element\n    A=AR:B=0:C=C-1:GOSUB SLICE\n    REM release the terminator of new list (we skip over it)\n    REM we already checked for an empty list above, so R6 is pointer\n    REM a real non-empty list\n    AY=Z%(R6+1):GOSUB RELEASE\n    REM attach end of slice to final args element\n    A2=Z%(A+2)\n    Z%(R6+1)=A2\n    Z%(A2)=Z%(A2)+32\n\n    GOTO DO_APPLY_2\n\n    DO_APPLY_1:\n      AR=A:CALL APPLY\n\n      GOTO DO_TCO_FUNCTION_DONE\n\n    DO_APPLY_2:\n      GOSUB PUSH_R: REM push/save new args for release\n\n      AR=R:CALL APPLY\n\n      REM pop/release new args\n      GOSUB POP_Q:AY=Q\n      GOSUB RELEASE\n      GOTO DO_TCO_FUNCTION_DONE\n\n  DO_MAP:\n    F=A\n\n    REM setup the stack for the loop\n    T=6:GOSUB MAP_LOOP_START\n\n    DO_MAP_LOOP:\n      IF Z%(B+1)=0 THEN GOTO DO_MAP_DONE\n\n      REM create argument list for apply\n      T=6:L=6:M=Z%(B+2):GOSUB ALLOC\n\n      GOSUB PUSH_R: REM push argument list\n      Q=F:GOSUB PUSH_Q: REM push F\n      Q=B:GOSUB PUSH_Q: REM push B\n\n      AR=R:CALL APPLY\n\n      GOSUB POP_Q:B=Q: REM pop B\n      GOSUB POP_Q:F=Q: REM pop F\n      GOSUB POP_Q: REM pop apply args and release them\n      AY=Q:GOSUB RELEASE\n\n      REM main value is result of apply\n      M=R\n\n      B=Z%(B+1): REM go to the next element\n\n      REM if error, release the unattached element\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:GOTO DO_MAP_DONE\n\n      REM update the return sequence structure\n      REM release N since list takes full ownership\n      C=1:T=6:GOSUB MAP_LOOP_UPDATE\n\n      GOTO DO_MAP_LOOP\n\n    DO_MAP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO DO_TCO_FUNCTION_DONE\n\n  DO_SWAP_BANG:\n    F=B\n\n    REM add atom to front of the args list\n    T=6:L=Z%(Z%(AR+1)+1):M=Z%(A+1):GOSUB ALLOC: REM cons\n    AR=R\n\n    REM push args for release after\n    Q=AR:GOSUB PUSH_Q\n\n    REM push atom\n    GOSUB PUSH_A\n\n    CALL APPLY\n\n    REM pop atom\n    GOSUB POP_A\n\n    REM pop and release args\n    GOSUB POP_Q:AY=Q\n    GOSUB RELEASE\n\n    REM use reset to update the value\n    B=R:GOSUB DO_RESET_BANG\n\n    REM but decrease ref cnt of return by 1 (not sure why)\n    AY=R:GOSUB RELEASE\n\n    GOTO DO_TCO_FUNCTION_DONE\n\n  DO_TCO_FUNCTION_DONE:\nEND SUB\n\nREM DO_FUNCTION(F, AR)\nDO_FUNCTION:\n  REM Get the function number\n  G=Z%(F+1)\n\n  REM Get argument values\n  A=Z%(AR+2):A1=Z%(A+1)\n  B=Z%(Z%(AR+1)+2):B1=Z%(B+1)\n\n  REM Switch on the function number\n  REM MEMORY DEBUGGING:\n  REM IF G>60 THEN ER=-1:E$=\"unknown function\"+STR$(G):RETURN\n  ON INT(G/10)+1 GOTO DO_1_9,DO_10_19,DO_20_29,DO_30_39,DO_40_49,DO_50_59,DO_60_69\n\n  DO_1_9:\n  ON G GOTO DO_EQUAL_Q,DO_THROW,DO_NIL_Q,DO_TRUE_Q,DO_FALSE_Q,DO_STRING_Q,DO_SYMBOL,DO_SYMBOL_Q,DO_KEYWORD\n  DO_10_19:\n  ON G-9 GOTO DO_KEYWORD_Q,DO_NUMBER_Q,DO_FN_Q,DO_MACRO_Q,DO_PR_STR,DO_STR,DO_PRN,DO_PRINTLN,DO_READ_STRING,DO_READLINE\n  DO_20_29:\n  ON G-19 GOTO DO_SLURP,DO_LT,DO_LTE,DO_GT,DO_GTE,DO_ADD,DO_SUB,DO_MULT,DO_DIV,DO_TIME_MS\n  DO_30_39:\n  ON G-29 GOTO DO_LIST,DO_LIST_Q,DO_VECTOR,DO_VECTOR_Q,DO_HASH_MAP,DO_MAP_Q,DO_ASSOC,DO_THROW,DO_GET,DO_CONTAINS\n  DO_40_49:\n  ON G-39 GOTO DO_KEYS,DO_VALS,DO_SEQUENTIAL_Q,DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT\n  DO_50_59:\n  ON G-49 GOTO DO_CONJ,DO_SEQ,DO_WITH_META,DO_META,DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_EVAL,DO_READ_FILE\n  DO_60_69:\n  ON G-59 GOTO DO_VEC,DO_PR_MEMORY_SUMMARY\n\n  DO_EQUAL_Q:\n    GOSUB EQUAL_Q\n    GOTO RETURN_TRUE_FALSE\n  DO_THROW:\n    ER=A\n    Z%(ER)=Z%(ER)+32\n    R=-1\n    RETURN\n  DO_NIL_Q:\n    R=A=0\n    GOTO RETURN_TRUE_FALSE\n  DO_TRUE_Q:\n    R=A=4\n    GOTO RETURN_TRUE_FALSE\n  DO_FALSE_Q:\n    R=A=2\n    GOTO RETURN_TRUE_FALSE\n  DO_STRING_Q:\n    R=0\n    GOSUB TYPE_A\n    IF T<>4 THEN GOTO RETURN_TRUE_FALSE\n    IF MID$(S$(A1),1,1)=CHR$(127) THEN GOTO RETURN_TRUE_FALSE\n    R=1\n    GOTO RETURN_TRUE_FALSE\n  DO_SYMBOL:\n    B$=S$(A1)\n    T=5:GOSUB STRING\n    RETURN\n  DO_SYMBOL_Q:\n    GOSUB TYPE_A\n    R=T=5\n    GOTO RETURN_TRUE_FALSE\n  DO_KEYWORD:\n    B$=S$(A1)\n    IF MID$(B$,1,1)<>CHR$(127) THEN B$=CHR$(127)+B$\n    T=4:GOSUB STRING\n    RETURN\n  DO_KEYWORD_Q:\n    R=0\n    GOSUB TYPE_A\n    IF T<>4 THEN GOTO RETURN_TRUE_FALSE\n    IF MID$(S$(A1),1,1)<>CHR$(127) THEN GOTO RETURN_TRUE_FALSE\n    R=1\n    GOTO RETURN_TRUE_FALSE\n  DO_NUMBER_Q:\n    GOSUB TYPE_A\n    R=T=2\n    GOTO RETURN_TRUE_FALSE\n  DO_FN_Q:\n    GOSUB TYPE_A\n    R=T=9 OR T=10\n    GOTO RETURN_TRUE_FALSE\n  DO_MACRO_Q:\n    GOSUB TYPE_A\n    R=T=11\n    GOTO RETURN_TRUE_FALSE\n\n  DO_PR_STR:\n    AZ=AR:B=1:B$=\" \":GOSUB PR_STR_SEQ\n    B$=R$:T=4:GOSUB STRING\n    RETURN\n  DO_STR:\n    AZ=AR:B=0:B$=\"\":GOSUB PR_STR_SEQ\n    B$=R$:T=4:GOSUB STRING\n    RETURN\n  DO_PRN:\n    AZ=AR:B=1:B$=\" \":GOSUB PR_STR_SEQ\n    PRINT R$\n    R=0\n    GOTO INC_REF_R\n  DO_PRINTLN:\n    AZ=AR:B=0:B$=\" \":GOSUB PR_STR_SEQ\n    PRINT R$\n    R=0\n    GOTO INC_REF_R\n  DO_READ_STRING:\n    A$=S$(A1)\n    GOSUB READ_STR\n    RETURN\n  DO_READLINE:\n    A$=S$(A1):GOSUB READLINE\n    IF EZ>0 THEN EZ=0:R=0:GOTO INC_REF_R\n    B$=R$:T=4:GOSUB STRING\n    RETURN\n  DO_SLURP:\n    R$=\"\"\n    EZ=0\n    #cbm OPEN 2,8,0,S$(A1)\n    #qbasic A$=S$(A1)\n    #qbasic OPEN A$ FOR INPUT AS #2\n    #qbasic IF ERR()<>0 THEN ER=-1:E$=\"File not found\":RETURN\n    DO_SLURP_LOOP:\n      C$=\"\"\n      RJ=1:GOSUB READ_FILE_CHAR\n      #cbm IF ASC(C$)=10 THEN R$=R$+CHR$(13)\n      #qbasic IF ASC(C$)=10 THEN R$=R$+CHR$(10)\n      IF (ASC(C$)<>10) AND (C$<>\"\") THEN R$=R$+C$\n      IF EZ>0 THEN GOTO DO_SLURP_DONE\n      GOTO DO_SLURP_LOOP\n    DO_SLURP_DONE:\n      CLOSE 2\n      IF ER>-2 THEN RETURN\n      B$=R$:T=4:GOSUB STRING\n      RETURN\n\n  DO_LT:\n    R=A1<B1\n    GOTO RETURN_TRUE_FALSE\n  DO_LTE:\n    R=A1<=B1\n    GOTO RETURN_TRUE_FALSE\n  DO_GT:\n    R=A1>B1\n    GOTO RETURN_TRUE_FALSE\n  DO_GTE:\n    R=A1>=B1\n    GOTO RETURN_TRUE_FALSE\n\n  DO_ADD:\n    T=2:L=A1+B1:GOSUB ALLOC\n    RETURN\n  DO_SUB:\n    T=2:L=A1-B1:GOSUB ALLOC\n    RETURN\n  DO_MULT:\n    T=2:L=A1*B1:GOSUB ALLOC\n    RETURN\n  DO_DIV:\n    T=2:L=A1/B1:GOSUB ALLOC\n    RETURN\n  DO_TIME_MS:\n    #cbm T=2:L=INT((TI-BT)*16.667):GOSUB ALLOC\n    #qbasic T=2:L=INT((TIMER()-BT#)*1000):GOSUB ALLOC\n    RETURN\n\n  DO_LIST:\n    R=AR\n    GOTO INC_REF_R\n  DO_LIST_Q:\n    GOSUB LIST_Q\n    GOTO RETURN_TRUE_FALSE\n  DO_VECTOR:\n    A=AR:T=7:GOTO FORCE_SEQ_TYPE\n  DO_VECTOR_Q:\n    GOSUB TYPE_A\n    R=T=7\n    GOTO RETURN_TRUE_FALSE\n  DO_HASH_MAP:\n    REM setup the stack for the loop\n    T=8:GOSUB MAP_LOOP_START\n\n    A=AR\n    DO_HASH_MAP_LOOP:\n      IF Z%(A+1)=0 THEN GOTO DO_HASH_MAP_LOOP_DONE\n\n      M=Z%(A+2)\n      N=Z%(Z%(A+1)+2)\n\n      A=Z%(Z%(A+1)+1): REM skip two\n\n      REM update the return sequence structure\n      REM do not release M and N since we are pulling them from the\n      REM arguments (and not creating them here)\n      C=0:GOSUB MAP_LOOP_UPDATE\n\n      GOTO DO_HASH_MAP_LOOP\n\n    DO_HASH_MAP_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      RETURN\n\n  DO_MAP_Q:\n    GOSUB TYPE_A\n    R=T=8\n    GOTO RETURN_TRUE_FALSE\n  DO_ASSOC:\n    H=A\n    AR=Z%(AR+1)\n    DO_ASSOC_LOOP:\n      K=Z%(AR+2)\n      C=Z%(Z%(AR+1)+2)\n      Z%(H)=Z%(H)+32\n      GOSUB ASSOC1:H=R\n      AR=Z%(Z%(AR+1)+1)\n      IF AR=0 OR Z%(AR+1)=0 THEN RETURN\n      GOTO DO_ASSOC_LOOP\n  DO_GET:\n    IF A=0 THEN R=0:GOTO INC_REF_R\n    H=A:B$=S$(Z%(B+1)):GOSUB HASHMAP_GET\n    GOTO INC_REF_R\n  DO_CONTAINS:\n    H=A:B$=S$(Z%(B+1)):GOSUB HASHMAP_CONTAINS\n    GOTO RETURN_TRUE_FALSE\n  DO_KEYS:\n    T1=0\n    GOTO DO_KEYS_VALS\n  DO_VALS:\n    T1=1\n  DO_KEYS_VALS:\n    REM setup the stack for the loop\n    T=6:GOSUB MAP_LOOP_START\n\n    DO_KEYS_VALS_LOOP:\n      IF Z%(A+1)=0 THEN GOTO DO_KEYS_VALS_LOOP_DONE\n\n      IF T1=0 THEN M=Z%(A+2)\n      IF T1=1 THEN M=Z%(A+3)\n\n      A=Z%(A+1): REM next element\n\n      REM update the return sequence structure\n      REM do not release N since we are pulling it from the\n      REM hash-map (and not creating them here)\n      C=0:GOSUB MAP_LOOP_UPDATE\n\n      GOTO DO_KEYS_VALS_LOOP\n\n    DO_KEYS_VALS_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      RETURN\n\n  DO_SEQUENTIAL_Q:\n    GOSUB TYPE_A\n    R=T=6 OR T=7\n    GOTO RETURN_TRUE_FALSE\n  DO_CONS:\n    T=6:L=B:M=A:GOSUB ALLOC\n    RETURN\n  DO_CONCAT:\n    REM always a list\n    R=6:GOSUB INC_REF_R\n    GOSUB PUSH_R: REM current value\n    GOSUB PUSH_R: REM return value\n\n    DO_CONCAT_LOOP:\n      IF AR<16 THEN GOTO DO_CONCAT_DONE: REM no more elements\n\n      REM slice/copy current element to a list\n      A=Z%(AR+2)\n      IF A<16 THEN GOTO DO_CONCAT_LOOP_NEXT: REM skip empty elements\n      B=0:C=-1:GOSUB SLICE\n\n      GOSUB PEEK_Q: REM return value\n      REM if this is the first element, set return element\n      IF Q=6 THEN Q=R:GOSUB PUT_Q:GOTO DO_CONCAT_LOOP_AGAIN\n      REM otherwise Q<>6, so attach current to sliced\n      GOSUB PEEK_Q_1\n      Z%(Q+1)=R\n\n      DO_CONCAT_LOOP_AGAIN:\n        REM update current to end of sliced list\n        Q=R6:GOSUB PUT_Q_1\n        REM dec empty since no longer part of slice\n        AY=6:GOSUB RELEASE\n      DO_CONCAT_LOOP_NEXT:\n        REM next list element\n        AR=Z%(AR+1)\n        GOTO DO_CONCAT_LOOP\n\n    DO_CONCAT_DONE:\n      GOSUB POP_R: REM pop return value\n      GOSUB POP_Q: REM pop current\n      RETURN\n  DO_VEC:\n    T=7:GOTO FORCE_SEQ_TYPE\n\n  DO_NTH:\n    B=B1\n    GOSUB COUNT\n    IF R<=B THEN R=-1:ER=-1:E$=\"nth: index out of range\":RETURN\n    DO_NTH_LOOP:\n      IF B=0 THEN GOTO DO_NTH_DONE\n      B=B-1\n      A=Z%(A+1)\n      GOTO DO_NTH_LOOP\n    DO_NTH_DONE:\n      R=Z%(A+2)\n      GOTO INC_REF_R\n  DO_FIRST:\n    R=0\n    IF A=0 THEN GOTO INC_REF_R\n    IF A1<>0 THEN R=Z%(A+2)\n    GOTO INC_REF_R\n  DO_REST:\n    IF A=0 THEN R=6:GOTO INC_REF_R\n    IF A1<>0 THEN A=A1: REM get the next sequence element\n    T=6:GOSUB FORCE_SEQ_TYPE\n    RETURN\n  DO_EMPTY_Q:\n    R=A1=0\n    GOTO RETURN_TRUE_FALSE\n  DO_COUNT:\n    GOSUB COUNT\n    T=2:L=R:GOSUB ALLOC\n    RETURN\n  DO_CONJ:\n    R=0\n    GOTO INC_REF_R\n  DO_SEQ:\n    R=0\n    GOTO INC_REF_R\n\n  DO_WITH_META:\n    GOSUB TYPE_A\n    REM remove existing metadata first\n    IF T=14 THEN A=A1:GOTO DO_WITH_META\n    T=14:L=A:M=B:GOSUB ALLOC\n    RETURN\n  DO_META:\n    R=0\n    GOSUB TYPE_A\n    IF T=14 THEN R=Z%(A+2)\n    GOTO INC_REF_R\n  DO_ATOM:\n    T=12:L=A:GOSUB ALLOC\n    RETURN\n  DO_ATOM_Q:\n    GOSUB TYPE_A\n    R=T=12\n    GOTO RETURN_TRUE_FALSE\n  DO_DEREF:\n    R=A1\n    GOTO INC_REF_R\n  DO_RESET_BANG:\n    R=B\n    REM release current value\n    REM can't use A1 here because DO_RESET_BANG is called from swap!\n    AY=Z%(A+1):GOSUB RELEASE\n    REM inc ref by 2 for atom ownership and since we are returning it\n    Z%(R)=Z%(R)+64\n    REM update value\n    Z%(A+1)=R\n    RETURN\n\n  DO_EVAL:\n    Q=E:GOSUB PUSH_Q: REM push/save environment\n    E=D:CALL EVAL\n    GOSUB POP_Q:E=Q\n    RETURN\n\n  DO_READ_FILE:\n    A$=S$(A1)\n    GOSUB READ_FILE\n    RETURN\n\n  REM DO_PR_MEMORY:\n  REM   P1=ZT:P2=-1:GOSUB PR_MEMORY\n  REM   RETURN\n  DO_PR_MEMORY_SUMMARY:\n    REM GOSUB PR_MEMORY_SUMMARY\n    GOSUB PR_MEMORY_SUMMARY_SMALL\n    R=0\n    GOTO INC_REF_R\n    RETURN\n\nINIT_CORE_SET_FUNCTION:\n  T=9:L=A:GOSUB ALLOC: REM native function\n  C=R:GOSUB ENV_SET_S\n  A=A+1\n  RETURN\n\nREM INIT_CORE_NS(E)\nINIT_CORE_NS:\n  REM create the environment mapping\n  REM must match DO_FUNCTION mappings\n\n  A=1\n  B$=\"=\":GOSUB INIT_CORE_SET_FUNCTION: REM A=1\n  B$=\"throw\":GOSUB INIT_CORE_SET_FUNCTION: REM A=2\n  B$=\"nil?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=3\n  B$=\"true?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=4\n  B$=\"false?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=5\n  B$=\"string?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=6\n  B$=\"symbol\":GOSUB INIT_CORE_SET_FUNCTION: REM A=7\n  B$=\"symbol?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=8\n  B$=\"keyword\":GOSUB INIT_CORE_SET_FUNCTION: REM A=9\n  B$=\"keyword?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=10\n  B$=\"number?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=11\n  B$=\"fn?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=12\n  B$=\"macro?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=13\n\n  B$=\"pr-str\":GOSUB INIT_CORE_SET_FUNCTION: REM A=14\n  B$=\"str\":GOSUB INIT_CORE_SET_FUNCTION: REM A=15\n  B$=\"prn\":GOSUB INIT_CORE_SET_FUNCTION: REM A=16\n  B$=\"println\":GOSUB INIT_CORE_SET_FUNCTION: REM A=17\n  B$=\"read-string\":GOSUB INIT_CORE_SET_FUNCTION: REM A=18\n  B$=\"readline\":GOSUB INIT_CORE_SET_FUNCTION: REM A=19\n  B$=\"slurp\":GOSUB INIT_CORE_SET_FUNCTION: REM A=20\n\n  B$=\"<\":GOSUB INIT_CORE_SET_FUNCTION: REM A=21\n  B$=\"<=\":GOSUB INIT_CORE_SET_FUNCTION: REM A=22\n  B$=\">\":GOSUB INIT_CORE_SET_FUNCTION: REM A=23\n  B$=\">=\":GOSUB INIT_CORE_SET_FUNCTION: REM A=24\n  B$=\"+\":GOSUB INIT_CORE_SET_FUNCTION: REM A=25\n  B$=\"-\":GOSUB INIT_CORE_SET_FUNCTION: REM A=26\n  B$=\"*\":GOSUB INIT_CORE_SET_FUNCTION: REM A=27\n  B$=\"/\":GOSUB INIT_CORE_SET_FUNCTION: REM A=28\n  B$=\"time-ms\":GOSUB INIT_CORE_SET_FUNCTION: REM A=29\n\n  B$=\"list\":GOSUB INIT_CORE_SET_FUNCTION: REM A=30\n  B$=\"list?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=31\n  B$=\"vector\":GOSUB INIT_CORE_SET_FUNCTION: REM A=32\n  B$=\"vector?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=33\n  B$=\"hash-map\":GOSUB INIT_CORE_SET_FUNCTION: REM A=34\n  B$=\"map?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=35\n  B$=\"assoc\":GOSUB INIT_CORE_SET_FUNCTION: REM A=36\n  B$=\"dissoc\":GOSUB INIT_CORE_SET_FUNCTION: REM A=37\n  B$=\"get\":GOSUB INIT_CORE_SET_FUNCTION: REM A=38\n  B$=\"contains?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=39\n  B$=\"keys\":GOSUB INIT_CORE_SET_FUNCTION: REM A=40\n  B$=\"vals\":GOSUB INIT_CORE_SET_FUNCTION: REM A=41\n\n  B$=\"sequential?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=42\n  B$=\"cons\":GOSUB INIT_CORE_SET_FUNCTION: REM A=43\n  B$=\"concat\":GOSUB INIT_CORE_SET_FUNCTION: REM A=44\n  B$=\"nth\":GOSUB INIT_CORE_SET_FUNCTION: REM A=45\n  B$=\"first\":GOSUB INIT_CORE_SET_FUNCTION: REM A=46\n  B$=\"rest\":GOSUB INIT_CORE_SET_FUNCTION: REM A=47\n  B$=\"empty?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=48\n  B$=\"count\":GOSUB INIT_CORE_SET_FUNCTION: REM A=49\n\n  B$=\"conj\":GOSUB INIT_CORE_SET_FUNCTION: REM A=50\n  B$=\"seq\":GOSUB INIT_CORE_SET_FUNCTION: REM A=51\n\n  B$=\"with-meta\":GOSUB INIT_CORE_SET_FUNCTION: REM A=52\n  B$=\"meta\":GOSUB INIT_CORE_SET_FUNCTION: REM A=53\n  B$=\"atom\":GOSUB INIT_CORE_SET_FUNCTION: REM A=54\n  B$=\"atom?\":GOSUB INIT_CORE_SET_FUNCTION: REM A=55\n  B$=\"deref\":GOSUB INIT_CORE_SET_FUNCTION: REM A=56\n  B$=\"reset!\":GOSUB INIT_CORE_SET_FUNCTION: REM A=57\n\n  B$=\"eval\":GOSUB INIT_CORE_SET_FUNCTION: REM A=58\n  B$=\"read-file\":GOSUB INIT_CORE_SET_FUNCTION: REM A=59\n  B$=\"vec\":GOSUB INIT_CORE_SET_FUNCTION: REM A=60\n  B$=\"pr-memory-summary\":GOSUB INIT_CORE_SET_FUNCTION: REM A=61\n\n  REM these are in DO_TCO_FUNCTION\n  A=65\n  B$=\"apply\":GOSUB INIT_CORE_SET_FUNCTION: REM A=65\n  B$=\"map\":GOSUB INIT_CORE_SET_FUNCTION: REM A=66\n  B$=\"swap!\":GOSUB INIT_CORE_SET_FUNCTION: REM A=67\n\n  RETURN\n"
  },
  {
    "path": "impls/basic/debug.in.bas",
    "content": "REM CHECK_FREE_LIST() -> P2\nCHECK_FREE_LIST:\n  REM start and accumulator\n  P1=ZK\n  P2=0\n  CHECK_FREE_LIST_LOOP:\n    IF P1>=ZI THEN RETURN\n    REM MEMORY DEBUGGING:\n    REM IF (Z%(P1)AND 31)<>15 THEN PRINT \"corrupt free:\"+STR$(P1):END\n    P2=P2+(Z%(P1)AND-32)/32\n    P1=Z%(P1+1)\n    GOTO CHECK_FREE_LIST_LOOP\n\nPR_MEMORY_SUMMARY_SMALL:\n  #cbm P0=FRE(0)\n\n  GOSUB CHECK_FREE_LIST\n  #cbm PRINT \"Free:\"+STR$(FRE(0))+\", \";\n  PRINT \"Values:\"+STR$(ZI-1-P2)+\", Emptys:\";\n  FOR P=0 TO 4 STEP 2:GOSUB PR_MEMORY_SUMMARY_SMALL_1:NEXT P\n  FOR P=6 TO 12 STEP 3:GOSUB PR_MEMORY_SUMMARY_SMALL_1:NEXT P\n  PRINT\n  RETURN\n  PR_MEMORY_SUMMARY_SMALL_1:\n    PRINT STR$(INT(Z%(P)/32))+\",\";\n    RETURN\n\nREM REM COUNT_STRINGS() -> P2\nREM COUNT_STRINGS:\nREM   P1=0\nREM   P2=0\nREM   COUNT_STRINGS_LOOP:\nREM     IF P1>S-1 THEN RETURN\nREM     IF S%(P1)>0 THEN P2=P2+1\nREM     P1=P1+1\nREM     GOTO COUNT_STRINGS_LOOP\nREM \nREM PR_MEMORY_SUMMARY:\nREM   #cbm P0=FRE(0)\nREM \nREM   PRINT\nREM   #cbm PRINT \"Free (FRE)   :\"+STR$(P0)\nREM   GOSUB CHECK_FREE_LIST: REM get count in P2\nREM   PRINT \"Values (Z%)  :\"+STR$(ZI-1-P2)+\" /\"+STR$(Z1)\nREM   REM PRINT \"               max:\"+STR$(ZI-1);\nREM   REM PRINT \", freed:\"+STR$(P2)+\", after repl_env:\"+STR$(ZT)\nREM   GOSUB COUNT_STRINGS\nREM   PRINT \"Strings (S$) :\"+STR$(P2)+\" /\"+STR$(Z2)\nREM   #qbasic PRINT \"Stack (X%)   :\"+STR$(X+1)+\" /\"+STR$(Z3)\nREM   #cbm PRINT \"Stack        :\"+STR$(X+2-Z3)+\" / 1920\"\nREM   RETURN\nREM \nREM #cbm PR_MEMORY_MAP:\nREM   #cbm PRINT\nREM   #cbm P1=PEEK(43)+PEEK(44)*256\nREM   #cbm P2=PEEK(45)+PEEK(46)*256\nREM   #cbm P3=PEEK(47)+PEEK(48)*256\nREM   #cbm P4=PEEK(49)+PEEK(50)*256\nREM   #cbm P5=PEEK(51)+PEEK(52)*256\nREM   #cbm P6=PEEK(53)+PEEK(54)*256\nREM   #cbm P7=PEEK(55)+PEEK(56)*256\nREM   #cbm PRINT \"BASIC beg.   :\"STR$(P1)\nREM   #cbm PRINT \"Variable beg.:\"STR$(P2)\nREM   #cbm PRINT \"Array beg.   :\"STR$(P3)\nREM   #cbm PRINT \"Array end    :\"STR$(P4)\nREM   #cbm PRINT \"String beg.  :\"STR$(P5)\nREM   #cbm PRINT \"String cur.  :\"STR$(P6)\nREM   #cbm PRINT \"BASIC end    :\"STR$(P7)\nREM   #cbm PRINT\nREM   #cbm PRINT \"Program Code :\"STR$(P2-P1)\nREM   #cbm PRINT \"Variables    :\"STR$(P3-P2)\nREM   #cbm PRINT \"Arrays       :\"STR$(P4-P3)\nREM   #cbm PRINT \"String Heap  :\"STR$(P7-P5)\nREM   #cbm RETURN\nREM \nREM REM PR_MEMORY_VALUE(I) -> J:\nREM REM   - I is memory value to print\nREM REM   - I is returned as last byte of value printed\nREM REM   - J is returned as type\nREM PR_MEMORY_VALUE:\nREM   J=Z%(I)AND 31\nREM   P3=Z%(I+1)\nREM   PRINT \" \"+STR$(I)+\": type:\"+STR$(J);\nREM   IF J<>15 THEN PRINT \", refs:\"+STR$((Z%(I)-J)/32);\nREM   IF J=15 THEN PRINT \", size:\"+STR$((Z%(I)AND-32)/32);\nREM   PRINT \", [\"+STR$(Z%(I));+\" |\"+STR$(P3);\nREM   IF J<6 OR J=9 OR J=12 OR J=15 THEN PRINT \" | --- | --- ]\";:GOTO PR_MEM_SKIP\nREM   PRINT \" |\"+STR$(Z%(I+2));\nREM   IF J=6 OR J=7 OR J=13 OR J=14 THEN PRINT \" | --- ]\";:GOTO PR_MEM_SKIP\nREM   PRINT \" |\"+STR$(Z%(I+3))+\" ]\";\nREM   PR_MEM_SKIP:\nREM   PRINT \" >> \";\nREM   ON J+1 GOTO PR_ENTRY_NIL,PR_ENTRY_BOOL,PR_ENTRY_INT,PR_ENTRY_FLOAT,PR_ENTRY_STR,PR_ENTRY_SYM,PR_ENTRY_LIST,PR_ENTRY_VECTOR,PR_ENTRY_HASH_MAP,PR_ENTRY_FN,PR_ENTRY_MALFN,PR_ENTRY_MAC,PR_ENTRY_ATOM,PR_ENTRY_ENV,PR_ENTRY_META,PR_ENTRY_FREE\nREM   PRINT \"Unknown type:\"+STR$(J):END\nREM \nREM   PR_ENTRY_NIL:\nREM     PRINT \"nil\"\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_BOOL:\nREM     IF P3=0 THEN PRINT \"false\"\nREM     IF P3=1 THEN PRINT \"true\"\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_INT:\nREM   PR_ENTRY_FLOAT:\nREM     PRINT STR$(P3)\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_STR:\nREM     PRINT \"'\"+S$(P3)+\"'\"\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_SYM:\nREM     PRINT S$(P3)\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_LIST:\nREM     I=I+2\nREM     IF I<16 THEN PRINT \"()\":RETURN\nREM     PRINT \"(...\"+STR$(Z%(I))+\" ...)\"\nREM     RETURN\nREM   PR_ENTRY_VECTOR:\nREM     I=I+2\nREM     IF I<16 THEN PRINT \"[]\":RETURN\nREM     PRINT \"[...\"+STR$(Z%(I))+\" ...]\"\nREM     RETURN\nREM   PR_ENTRY_HASH_MAP:\nREM     I=I+3\nREM     IF I<16 THEN PRINT \"{}\":RETURN\nREM     IF J=8 THEN PRINT \"{... key:\"+STR$(Z%(I-1))+\", val:\"+STR$(Z%(I))+\" ...}\"\nREM     RETURN\nREM   PR_ENTRY_FN:\nREM     PRINT \"#<fn\"+STR$(P3)+\">\"\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_MALFN:\nREM   PR_ENTRY_MAC:\nREM     IF I=11 THEN PRINT \"MACRO \";\nREM     PRINT \"(fn* param:\"+STR$(Z%(I))+\", env:\"+STR$(Z%(I+1))+\")\"\nREM     I=I+3\nREM     RETURN\nREM   PR_ENTRY_ATOM:\nREM     PRINT \"(atom val:\"+STR$(P3)+\")\"\nREM     I=I+1\nREM     RETURN\nREM   PR_ENTRY_ENV:\nREM     PRINT \"#<env hm:\"+STR$(P3)+\", outer:\"+STR$(Z%(I+2))+\">\"\nREM     I=I+2\nREM     RETURN\nREM   PR_ENTRY_META:\nREM     PRINT \"#<meta obj:\"+STR$(P3)+\", meta:\"+STR$(Z%(I+2))+\">\"\nREM     I=I+2\nREM     RETURN\nREM   PR_ENTRY_FREE:\nREM     PRINT \"FREE next:\"+STR$(P3);\nREM     IF I=ZK THEN PRINT \" (free list start)\";\nREM     PRINT\nREM     I=I-1+(Z%(I)AND-32)/32\nREM     RETURN\nREM \nREM REM PR_OBJECT(P1) -> nil\nREM PR_OBJECT:\nREM   RD=0\nREM \nREM   IF P1=-1 THEN PRINT \"  \"+STR$(-1)+\": ---\":RETURN\nREM   RD=RD+1\nREM   Q=P1:GOSUB PUSH_Q\nREM \nREM   PR_OBJ_LOOP:\nREM     IF RD=0 THEN RETURN\nREM     RD=RD-1\nREM \nREM     GOSUB PEEK_Q:I=Q\nREM     REM IF I<15 THEN GOSUB POP_Q:GOTO PR_OBJ_LOOP\nREM     GOSUB PR_MEMORY_VALUE\nREM     REM J holds type now\nREM     GOSUB POP_Q:I=Q\nREM \nREM     IF J<6 OR J=9 THEN GOTO PR_OBJ_LOOP: REM no contained references\nREM     REM reference in first position\nREM     IF Z%(I+1)<>0 THEN RD=RD+1:Q=Z%(I+1):GOSUB PUSH_Q\nREM     IF J=12 OR J=15 THEN PR_OBJ_LOOP: REM no more reference\nREM     REM reference in second position\nREM     IF Z%(I+2)<>0 THEN RD=RD+1:Q=Z%(I+2):GOSUB PUSH_Q\nREM     IF J=6 OR J=7 OR J=13 OR J=14 THEN PR_OBJ_LOOP: REM no more references\nREM     IF Z%(I+3)<>0 THEN RD=RD+1:Q=Z%(I+3):GOSUB PUSH_Q\nREM     GOTO PR_OBJ_LOOP\nREM \nREM REM PR_MEMORY(P1, P2) -> nil\nREM PR_MEMORY:\nREM   IF P2<P1 THEN P2=ZI-1\nREM   PRINT \"Values (Z%)\"+STR$(P1)+\" ->\"+STR$(P2);\nREM   PRINT \" (ZI: \"+STR$(ZI)+\", ZK: \"+STR$(ZK)+\"):\"\nREM   IF P2<P1 THEN PRINT \"  ---\":GOTO PR_MEMORY_AFTER_VALUES\nREM   I=P1\nREM   PR_MEMORY_VALUE_LOOP:\nREM     IF I>P2 THEN GOTO PR_MEMORY_AFTER_VALUES\nREM     GOSUB PR_MEMORY_VALUE\nREM     I=I+1\nREM     GOTO PR_MEMORY_VALUE_LOOP\nREM   PR_MEMORY_AFTER_VALUES:\nREM   PRINT \"S$ String Memory (S: \"+STR$(S)+\"):\"\nREM   IF S<=0 THEN PRINT \"  ---\":GOTO PR_MEMORY_SKIP_STRINGS\nREM   FOR I=0 TO S-1\nREM     PRINT \" \"+STR$(I)+\": '\"+S$(I)+\"'\"\nREM     NEXT I\nREM   PR_MEMORY_SKIP_STRINGS:\nREM   PRINT \"X% Stack Memory (X: \"+STR$(X)+\"):\"\nREM #cbm  IF X<Z3 THEN PRINT \"  ---\":GOTO PR_MEMORY_SKIP_STACK\nREM #cbm  FOR I=Z3 TO X\nREM #cbm    PRINT \" \"+STR$(I)+\": \"+STR$(PEEK(X)+PEEK(X+1)*256)\nREM #cbm    NEXT I\nREM #qbasic  IF X<0 THEN PRINT \"  ---\":GOTO PR_MEMORY_SKIP_STACK\nREM #qbasic  FOR I=0 TO X\nREM #qbasic    #qbasic PRINT \" \"+STR$(I)+\": \"+STR$(X%(I))\nREM #qbasic    NEXT I\nREM   PR_MEMORY_SKIP_STACK:\nREM   RETURN\nREM \n"
  },
  {
    "path": "impls/basic/env.in.bas",
    "content": "\nREM ENV_NEW(C) -> R\nENV_NEW:\n  REM allocate the data hashmap\n  GOSUB HASHMAP\n  AY=R\n\n  REM set the outer and data pointer\n  T=13:L=R:M=C:GOSUB ALLOC\n  GOSUB RELEASE: REM environment takes ownership\n  RETURN\n\nREM see RELEASE types.in.bas for environment cleanup\n\nREM ENV_NEW_BINDS(C, A, B) -> R\nENV_NEW_BINDS:\n  GOSUB ENV_NEW\n  E=R\n  REM process bindings\n  ENV_NEW_BINDS_LOOP:\n    IF Z%(A+1)=0 THEN R=E:RETURN\n    REM get/deref the key from A\n    K=Z%(A+2)\n\n    IF S$(Z%(K+1))=\"&\" THEN GOTO EVAL_NEW_BINDS_VARGS\n\n    EVAL_NEW_BINDS_1x1:\n      REM get/deref the key from B\n      C=Z%(B+2)\n      REM set the binding in the environment data\n      GOSUB ENV_SET\n      REM go to next element of A and B\n      A=Z%(A+1)\n      B=Z%(B+1)\n      GOTO ENV_NEW_BINDS_LOOP\n\n    EVAL_NEW_BINDS_VARGS:\n      REM get/deref the key from next element of A\n      A=Z%(A+1)\n      K=Z%(A+2)\n      REM the value is the remaining list in B\n      A=B:T=6:GOSUB FORCE_SEQ_TYPE\n      C=R\n      REM set the binding in the environment data\n      GOSUB ENV_SET\n      R=E\n      AY=C:GOSUB RELEASE: REM list is owned by environment\n      RETURN\n\nREM ENV_SET(E, K, C) -> R\nENV_SET:\n  H=Z%(E+1)\n  GOSUB ASSOC1\n  Z%(E+1)=R\n  R=C\n  RETURN\n\nREM ENV_SET_S(E, B$, C) -> R\nENV_SET_S:\n  H=Z%(E+1)\n  GOSUB ASSOC1_S\n  Z%(E+1)=R\n  R=C\n  RETURN\n\nREM ENV_GET(E, B$) -> R\nREM   - R3=1 if the key was found, else 0\nSUB ENV_GET\n  T=E\n  ENV_FIND_LOOP:\n    H=Z%(T+1)\n    REM More efficient to use GET for value (R) and contains? (R3)\n    GOSUB HASHMAP_GET\n    REM if we found it, return it\n    IF R3=1 THEN GOTO ENV_FIND_DONE\n    T=Z%(T+2): REM get outer environment\n    IF T>0 THEN GOTO ENV_FIND_LOOP\n  ENV_FIND_DONE:\nEND SUB\n"
  },
  {
    "path": "impls/basic/mem.in.bas",
    "content": "REM Memory layout:\nREM\nREM type            bytes\nREM ----------      ----------\nREM nil             ref/ 0 |  0          |              |\nREM false           ref/ 1 |  0          |              |\nREM true            ref/ 1 |  1          |              |\nREM integer         ref/ 2 | int         |              |\nREM float           ref/ 3 | ???         |              |\nREM string/kw       ref/ 4 | S$ idx      |              |\nREM symbol          ref/ 5 | S$ idx      |              |\nREM list            ref/ 6 | next Z% idx | val Z% idx   |\nREM vector          ref/ 7 | next Z% idx | val Z% idx   |\nREM hashmap         ref/ 8 | next Z% idx | key Z% idx   | val Z% idx\nREM function        ref/ 9 | fn idx      |              |\nREM mal function    ref/10 | body Z% idx | param Z% idx | env Z% idx\nREM macro fn        ref/11 | body Z% idx | param Z% idx | env Z% idx\nREM atom            ref/12 | val Z% idx  |              |\nREM environment     ref/13 | hmap Z% idx | outer Z% idx |\nREM metadata        ref/14 | obj Z% idx  | meta Z% idx  |\nREM FREE             sz/15 | next Z% idx |              |\nREM\nREM Locations 0-15 are for constant/persistent values:\nREM    0: nil\nREM    2: false\nREM    4: true\nREM    6: empty list\nREM    9: empty vector\nREM   12: empty hash-map\n\nREM Note: DIM_MEMORY for C64 BASIC and the INIT_MEMORY function are at\nREM end of this file for efficiency on C64. The most commonly used\nREM function should be at the top since C64 BASIC scans line numbers\nREM for every GOTO/GOSUB. On the other hand, QBasic requires that\nREM arrays are dimensioned at the top of the file, not just as the\nREM first operation on that array so DIM_MEMORY for QBasic is here at\nREM the top.\n\n#qbasic DIM_MEMORY:\n#qbasic   T=0\n#qbasic\n#qbasic   Z1=8191+1424: REM Z% (boxed memory) size (2 bytes each)\n#qbasic   Z2=199: REM S$/S% (string memory) size (3+2 bytes each)\n#qbasic   Z3=200: REM X% (call stack) size (2 bytes each)\n#qbasic   Z4=64: REM Y% (release stack) size (4 bytes each)\n#qbasic\n#qbasic   REM boxed element memory\n#qbasic   DIM Z%(Z1): REM TYPE ARRAY\n#qbasic\n#qbasic   REM string memory storage\n#qbasic   S=0:DIM S$(Z2):DIM S%(Z2)\n#qbasic\n#qbasic   REM call/logic stack\n#qbasic   X=-1:DIM X%(Z3): REM stack of Z% indexes\n#qbasic\n#qbasic   REM pending release stack\n#qbasic   Y=-1:DIM Y%(Z4,1): REM stack of Z% indexes and level/LV values\n#qbasic\n#qbasic   RETURN\n\nREM stack functions\n\n#qbasic PUSH_A:\n#qbasic   X=X+1:X%(X)=A:RETURN\n#qbasic POP_A:\n#qbasic   A=X%(X):X=X-1:RETURN\n#qbasic\n#qbasic PUSH_R:\n#qbasic   X=X+1:X%(X)=R:RETURN\n#qbasic POP_R:\n#qbasic   R=X%(X):X=X-1:RETURN\n#qbasic\n#qbasic PUSH_Q:\n#qbasic   X=X+1:X%(X)=Q:RETURN\n#qbasic POP_Q:\n#qbasic   Q=X%(X):X=X-1:RETURN\n#qbasic PEEK_Q:\n#qbasic   Q=X%(X):RETURN\n#qbasic PEEK_Q_1:\n#qbasic   Q=X%(X-1):RETURN\n#qbasic PEEK_Q_2:\n#qbasic   Q=X%(X-2):RETURN\n#qbasic PEEK_Q_Q:\n#qbasic   Q=X%(X-Q):RETURN\n#qbasic PUT_Q:\n#qbasic   X%(X)=Q:RETURN\n#qbasic PUT_Q_1:\n#qbasic   X%(X-1)=Q:RETURN\n#qbasic PUT_Q_2:\n#qbasic   X%(X-2)=Q:RETURN\n\n#cbm PUSH_A:\n#cbm   X=X+2:POKE X,A AND255:POKE X+1,A/256:RETURN\n#cbm POP_A:\n#cbm   A=PEEK(X)+PEEK(X+1)*256:X=X-2:RETURN\n#cbm\n#cbm PUSH_R:\n#cbm   X=X+2:POKE X,R AND255:POKE X+1,R/256:RETURN\n#cbm POP_R:\n#cbm   R=PEEK(X)+PEEK(X+1)*256:X=X-2:RETURN\n#cbm\n#cbm PUSH_Q:\n#cbm   X=X+2:POKE X,Q AND255:POKE X+1,Q/256:RETURN\n#cbm POP_Q:\n#cbm   Q=PEEK(X)+PEEK(X+1)*256:X=X-2:RETURN\n#cbm PEEK_Q:\n#cbm   Q=PEEK(X)+PEEK(X+1)*256:RETURN\n#cbm PEEK_Q_1:\n#cbm   Q=PEEK(X-2)+PEEK(X-1)*256:RETURN\n#cbm PEEK_Q_2:\n#cbm   Q=PEEK(X-4)+PEEK(X-3)*256:RETURN\n#cbm PEEK_Q_Q:\n#cbm   Q=PEEK(X-Q*2)+PEEK(X-Q*2+1)*256:RETURN\n#cbm PUT_Q:\n#cbm   POKE X,Q AND255:POKE X+1,Q/256:RETURN\n#cbm PUT_Q_1:\n#cbm   POKE X-2,Q AND255:POKE X-1,Q/256:RETURN\n#cbm PUT_Q_2:\n#cbm   POKE X-4,Q AND255:POKE X-3,Q/256:RETURN\n\nREM memory functions\n\nREM ALLOC(T,L) -> R\nREM ALLOC(T,L,M) -> R\nREM ALLOC(T,L,M,N) -> R\nREM L is value for Z%(R+1)\nREM M is value for Z%(R+2), if SZ>2\nREM N is value for Z%(R+3), if SZ>3\nALLOC:\n  SZ=3\n  IF T<6 OR T=9 OR T=12 THEN SZ=2\n  IF T=8 OR T=10 OR T=11 THEN SZ=4\n  REM PRINT \"ALLOC T: \"+STR$(T)+\", SZ: \"+STR$(SZ)+\", ZK: \"+STR$(ZK)\n  U=ZK\n  R=ZK\n  ALLOC_LOOP:\n    IF R=ZI THEN GOTO ALLOC_UNUSED\n    REM TODO sanity check that type is 15\n    IF ((Z%(R)AND-32)/32)=SZ THEN GOTO ALLOC_MIDDLE\n    REM PRINT \"ALLOC search: U: \"+STR$(U)+\", R: \"+STR$(R)\n    U=R: REM previous set to current\n    R=Z%(R+1): REM current set to next\n    GOTO ALLOC_LOOP\n  ALLOC_MIDDLE:\n    REM PRINT \"ALLOC_MIDDLE: U: \"+STR$(U)+\", R: \"+STR$(R)\n    REM set free pointer (ZK) to next free\n    IF R=ZK THEN ZK=Z%(R+1)\n    REM set previous free to next free\n    IF R<>ZK THEN Z%(U+1)=Z%(R+1)\n    GOTO ALLOC_DONE\n  ALLOC_UNUSED:\n    REM PRINT \"ALLOC_UNUSED ZI: \"+STR$(ZI)+\", U: \"+STR$(U)+\", R: \"+STR$(R)\n    IF R+SZ>Z1 THEN GOSUB PR_MEMORY_SUMMARY_SMALL:PRINT \"Out of mal memory!\":END\n    ZI=ZI+SZ\n    IF U=R THEN ZK=ZI\n    REM set previous free to new memory top\n    IF U<>R THEN Z%(U+1)=ZI\n    GOTO ALLOC_DONE\n  ALLOC_DONE:\n    Z%(R)=T+32\n    REM set Z%(R+1) to default L\n    Z%(R+1)=L\n    IF T>5 AND T<>9 THEN Z%(L)=Z%(L)+32: REM value is a Z% idx\n    IF SZ>2 THEN Z%(M)=Z%(M)+32:Z%(R+2)=M\n    IF SZ>3 THEN Z%(N)=Z%(N)+32:Z%(R+3)=N\n\n    RETURN\n\nREM FREE(AY, SZ) -> nil\nFREE:\n  REM assumes reference count cleanup already (see RELEASE)\n  Z%(AY)=(SZ*32)+15: REM set type(15) and size\n  Z%(AY+1)=ZK\n  ZK=AY\n  IF SZ>=3 THEN Z%(AY+2)=0\n  IF SZ=4 THEN Z%(AY+3)=0\n  REM TODO: fail if SZ>4\n  RETURN\n\n\nREM RELEASE(AY) -> nil\nREM R should not be affected by this call\nRELEASE:\n  RC=0\n\n  GOTO RELEASE_ONE\n\n  RELEASE_TOP:\n\n  IF RC=0 THEN RETURN\n\n  REM pop next object to release, decrease remaining count\n  GOSUB POP_Q:AY=Q\n  RC=RC-1\n\n  RELEASE_ONE:\n  IF AY=-1 THEN RETURN\n\n  U=Z%(AY)AND 31: REM type\n  V=Z%(AY+1): REM main value/reference\n\n  REM set the size\n  REM TODO: share with ALLOC calculation\n  SZ=3\n  IF U<6 OR U=9 OR U=12 THEN SZ=2\n  IF U=8 OR U=10 OR U=11 THEN SZ=4\n\n  REM AZ=AY: B=1: GOSUB PR_STR\n  REM PRINT \"RELEASE AY:\"+STR$(AY)+\" [\"+R$+\"] (byte0:\"+STR$(Z%(AY))+\", SZ:\"+STR$(SZ)+\")\"\n\n  REM sanity check not already freed\n  REM MEMORY DEBUGGING:\n  REM IF U=15 THEN PRINT \"RELEASE of free:\"+STR$(AY):END\n  REM IF Z%(AY)<15 THEN PRINT \"RELEASE of unowned:\"+STR$(AY):END\n\n  REM decrease reference count by one\n  Z%(AY)=Z%(AY)-32\n\n  REM nil, false, true, empty sequences\n  REM MEMORY DEBUGGING:\n  REM IF AY<16 AND Z%(AY)<32 THEN PRINT \"RELEASE of empty:\"+STR$(AY):END\n  IF AY<16 THEN GOTO RELEASE_TOP\n\n  REM our reference count is not 0, so don't release\n  IF Z%(AY)>=32 GOTO RELEASE_TOP\n\n  REM switch on type\n  ON U+1 GOSUB RELEASE_SIMPLE,RELEASE_SIMPLE,RELEASE_SIMPLE,RELEASE_SIMPLE,RELEASE_STRING,RELEASE_STRING,RELEASE_SEQ,RELEASE_SEQ,RELEASE_HASH_MAP,RELEASE_SIMPLE,RELEASE_MAL_FUNCTION,RELEASE_MAL_FUNCTION,RELEASE_ATOM,RELEASE_ENV,RELEASE_METADATA\n\n  REM free the current element and continue, SZ already set\n  GOSUB FREE\n  GOTO RELEASE_TOP\n\n  RELEASE_SIMPLE:\n    RETURN\n  RELEASE_STRING:\n    REM string type, release interned string, then FREE reference\n    REM MEMORY DEBUGGING:\n    REM IF S%(V)=0 THEN PRINT \"RELEASE of free string:\"+STR$(S%(V)):END\n    S%(V)=S%(V)-1\n    IF S%(V)=0 THEN S$(V)=\"\": REM free BASIC string\n    REM free the atom itself\n    RETURN\n  RELEASE_SEQ:\n    IF V=0 THEN RETURN\n    REM add value and next element to stack\n    RC=RC+2\n    Q=Z%(AY+2):GOSUB PUSH_Q\n    Q=V:GOSUB PUSH_Q\n    RETURN\n  RELEASE_HASH_MAP:\n    IF V=0 THEN RETURN\n    REM add key, value and next element to stack\n    RC=RC+3\n    Q=Z%(AY+2):GOSUB PUSH_Q\n    Q=Z%(AY+3):GOSUB PUSH_Q\n    Q=V:GOSUB PUSH_Q\n    RETURN\n  RELEASE_ATOM:\n    REM add contained/referred value\n    RC=RC+1\n    Q=V:GOSUB PUSH_Q\n    REM free the atom itself\n    RETURN\n  RELEASE_MAL_FUNCTION:\n    REM add ast, params and environment to stack\n    RC=RC+3\n    Q=V:GOSUB PUSH_Q\n    Q=Z%(AY+2):GOSUB PUSH_Q\n    Q=Z%(AY+3):GOSUB PUSH_Q\n    REM free the current 3 element mal_function\n    RETURN\n  RELEASE_ENV:\n    REM add the hashmap data to the stack\n    RC=RC+1\n    Q=V:GOSUB PUSH_Q\n    REM if outer set, add outer env to stack\n    IF Z%(AY+2)<>0 THEN RC=RC+1:Q=Z%(AY+2):GOSUB PUSH_Q\n    RETURN\n  RELEASE_METADATA:\n    REM add object and metadata object\n    RC=RC+2\n    Q=V:GOSUB PUSH_Q\n    Q=Z%(AY+2):GOSUB PUSH_Q\n    RETURN\n\n\nREM INC_REF_R(R) -> R\nREM   - return R with 1 ref cnt increase\nREM   - call with GOTO to return at caller callsite\nREM   - call with GOSUB to return to caller\nINC_REF_R:\n  Z%(R)=Z%(R)+32\n  RETURN\n\nREM RETURN_TRUE_FALSE(R) -> R\nREM   - take BASIC true/false R, return mal true/false R with ref cnt\nREM   - called with GOTO as a return RETURN\nRETURN_TRUE_FALSE:\n  IF R THEN R=4\n  IF R=0 THEN R=2\n  GOTO INC_REF_R\n\n\nREM release stack functions\n\n#qbasic PEND_A_LV:\n#qbasic   Y=Y+1:Y%(Y,0)=A:Y%(Y,1)=LV:RETURN\n#qbasic\n#qbasic REM RELEASE_PEND(LV) -> nil\n#qbasic RELEASE_PEND:\n#qbasic   IF Y<0 THEN RETURN\n#qbasic   IF Y%(Y,1)<=LV THEN RETURN\n#qbasic   REM PRINT \"RELEASE_PEND releasing:\"+STR$(Y%(Y,0))\n#qbasic   AY=Y%(Y,0):GOSUB RELEASE\n#qbasic   Y=Y-1\n#qbasic   GOTO RELEASE_PEND\n\n#cbm PEND_A_LV:\n#cbm   Y=Y+4:POKE Y,A AND255:POKE Y+1,A/256\n#cbm         POKE Y+2,LV AND255:POKE Y+3,LV/256:RETURN\n#cbm\n#cbm REM RELEASE_PEND(LV) -> nil\n#cbm RELEASE_PEND:\n#cbm   IF Y<Z4 THEN RETURN\n#cbm   IF (PEEK(Y+2)+PEEK(Y+3)*256)<=LV THEN RETURN\n#cbm   REM PRINT \"RELEASE_PEND releasing:\"+STR$(Y%(Y,0))\n#cbm   AY=(PEEK(Y)+PEEK(Y+1)*256):GOSUB RELEASE\n#cbm   Y=Y-4\n#cbm   GOTO RELEASE_PEND\n\n\n\n#cbm DIM_MEMORY:\n#cbm   T=FRE(0)\n#cbm\n#cbm   Z1=8191+1424: REM Z% (boxed memory) size (2 bytes each)\n#cbm   Z2=199: REM S$/S% (string memory) size (3+2 bytes each)\n#cbm   Z3=49152: REM X starting point at $C000 (2 bytes each)\n#cbm   Z4=52992: REM Y starting point at $CF00 (4 bytes each)\n#cbm\n#cbm   REM TODO: for performance, define all/most non-array variables here\n#cbm   REM so that the array area doesn't have to be shifted down everytime\n#cbm   REM a new non-array variable is defined\n#cbm\n#cbm   REM boxed element memory\n#cbm   DIM Z%(Z1): REM TYPE ARRAY\n#cbm\n#cbm   REM string memory storage\n#cbm   S=0:DIM S$(Z2):DIM S%(Z2)\n#cbm\n#cbm   REM call/logic stack\n#cbm   X=Z3-2: REM stack of 1920 Z% indexes at $C000\n#cbm\n#cbm   REM pending release stack\n#cbm   Y=Z4-4: REM stack of 64 Y% indexes/levels at $CF00\n#cbm\n#cbm   RETURN\n\nINIT_MEMORY:\n  GOSUB DIM_MEMORY\n\n  REM global error state\n  REM  -2 : no error\n  REM  -1 : string error in E$\n  REM >=0 : pointer to error object\n  ER=-2\n  E$=\"\"\n\n  REM Predefine nil, false, true, and an empty sequences\n  FOR I=0 TO 15:Z%(I)=0:NEXT I\n  Z%(0)=32: REM nil\n  Z%(2)=1+32: REM false\n  Z%(4)=1+32:Z%(5)=1: REM true\n  Z%(6)=6+32: REM emtpy list\n  Z%(9)=7+32: REM empty vector\n  Z%(12)=8+32: REM empty hash-map\n\n  REM start of unused memory\n  ZI=16\n\n  REM start of free list\n  ZK=16\n\n  REM start of time clock\n  #cbm BT=TI\n  #qbasic BT#=TIMER()\n\n  RETURN\n\n\n"
  },
  {
    "path": "impls/basic/printer.in.bas",
    "content": "REM PR_STR(AZ, B) -> R$\nPR_STR:\n  R$=\"\"\n  PR_STR_RECUR:\n  T=Z%(AZ)AND 31\n  U=Z%(AZ+1)\n  REM PRINT \"AZ: \"+STR$(AZ)+\", T: \"+STR$(T)+\", U: \"+STR$(U)\n  IF T=0 THEN R$=\"nil\":RETURN\n  REM if metadata, then get actual object\n  IF T>=14 THEN AZ=U:GOTO PR_STR_RECUR\n  ON T GOTO PR_BOOLEAN,PR_INTEGER,PR_UNKNOWN,PR_STRING_MAYBE,PR_SYMBOL,PR_SEQ,PR_SEQ,PR_SEQ,PR_FUNCTION,PR_MAL_FUNCTION,PR_MAL_FUNCTION,PR_ATOM,PR_ENV,PR_RECUR,PR_FREE\n\n  PR_UNKNOWN:\n    REM MEMORY DEBUGGING:\n    REM R$=\"#<unknown>\"\n    RETURN\n  PR_RECUR:\n    AZ=U\n    GOTO PR_STR_RECUR\n  PR_BOOLEAN:\n    R$=\"true\"\n    IF U=0 THEN R$=\"false\"\n    RETURN\n  PR_INTEGER:\n    T$=STR$(U)\n    REM Remove initial space\n    IF U>=0 THEN T$=RIGHT$(T$,LEN(T$)-1)\n    R$=R$+T$\n    RETURN\n  PR_STRING_MAYBE:\n    R$=S$(U)\n    IF LEN(R$)=0 THEN GOTO PR_STRING\n    IF MID$(R$,1,1)=CHR$(127) THEN R$=\":\"+MID$(R$,2,LEN(R$)-1):RETURN\n  PR_STRING:\n    IF B=1 THEN GOTO PR_STRING_READABLY\n    RETURN\n  PR_STRING_READABLY:\n    S1$=\"\\\":S2$=\"\\\\\":GOSUB REPLACE: REM escape backslash \"\n    S1$=CHR$(34):S2$=\"\\\"+CHR$(34):GOSUB REPLACE: REM escape quotes \"\n    #cbm S1$=CHR$(13):S2$=\"\\n\":GOSUB REPLACE: REM escape newlines\n    #qbasic S1$=CHR$(10):S2$=\"\\n\":GOSUB REPLACE: REM escape newlines\n    R$=CHR$(34)+R$+CHR$(34)\n    RETURN\n  PR_SYMBOL:\n    R$=S$(U)\n    RETURN\n  PR_SEQ:\n    REM push the type and where we are in the sequence\n    Q=T:GOSUB PUSH_Q\n    Q=AZ:GOSUB PUSH_Q\n    REM save the current rendered string\n    S$(S)=R$:S=S+1\n    PR_SEQ_LOOP:\n      IF Z%(AZ+1)=0 THEN GOTO PR_SEQ_DONE\n      AZ=Z%(AZ+2):GOSUB PR_STR:GOSUB PEEK_Q_1:T=Q\n      REM append what we just rendered it\n      S$(S-1)=S$(S-1)+R$\n\n      REM if this is a hash-map, print the next element\n      IF T=8 THEN GOSUB PEEK_Q:AZ=Z%(Q+3):GOSUB PR_STR:S$(S-1)=S$(S-1)+\" \"+R$\n\n      REM restore current seq type\n      GOSUB PEEK_Q_1:T=Q\n      REM Go to next list element\n      GOSUB PEEK_Q\n      AZ=Z%(Q+1)\n      Q=AZ:GOSUB PUT_Q\n      IF Z%(AZ+1)<>0 THEN S$(S-1)=S$(S-1)+\" \"\n      GOTO PR_SEQ_LOOP\n    PR_SEQ_DONE:\n      REM restore the current string\n      S=S-1:R$=S$(S)\n      REM pop where we are the sequence and type\n      GOSUB POP_Q\n      GOSUB POP_Q:T=Q: REM get type\n      IF T=6 THEN R$=\"(\"+R$+\")\"\n      IF T=7 THEN R$=\"[\"+R$+\"]\"\n      IF T=8 THEN R$=\"{\"+R$+\"}\"\n      RETURN\n  PR_FUNCTION:\n    R$=\"#<fn\"+STR$(U)+\">\"\n    RETURN\n  PR_MAL_FUNCTION:\n    T1=AZ\n    AZ=Z%(T1+2):GOSUB PR_STR\n    REM append what we just rendered it\n    S$(S)=\"(fn* \"+R$:S=S+1\n    AZ=Z%(T1+1):GOSUB PR_STR\n    S=S-1\n    R$=S$(S)+\" \"+R$+\")\"\n    RETURN\n  PR_ATOM:\n    AZ=U:GOSUB PR_STR\n    R$=\"(atom \"+R$+\")\"\n    RETURN\n  PR_ENV:\n    R$=\"#<env\"+STR$(AZ)+\", data\"+STR$(U)+\">\"\n    RETURN\n  PR_FREE:\n    R$=\"#<free\"+STR$(AZ)+\", next\"+STR$(U)+\">\"\n    RETURN\n    \nREM PR_STR_SEQ(AZ, B, B$) -> R$\nREM   - B is print_readably\nREM   - B$ is the separator\nPR_STR_SEQ:\n  V=AZ\n  S$(S)=\"\":S=S+1\n  PR_STR_SEQ_LOOP:\n    IF Z%(V+1)=0 THEN S=S-1:R$=S$(S):RETURN\n    AZ=Z%(V+2):GOSUB PR_STR\n    REM goto the next sequence element\n    V=Z%(V+1)\n    IF Z%(V+1)=0 THEN S$(S-1)=S$(S-1)+R$\n    IF Z%(V+1)<>0 THEN S$(S-1)=S$(S-1)+R$+B$\n    GOTO PR_STR_SEQ_LOOP\n"
  },
  {
    "path": "impls/basic/reader.in.bas",
    "content": "REM READ_TOKEN(RF=0, A$, RI) -> T$\nREM READ_TOKEN(RF=1) -> T$\nREAD_TOKEN:\n  IF RF=1 THEN RF=2:T$=\"(\":RETURN\n  IF RF=2 THEN RF=3:T$=\"do\":RETURN\n  GOSUB SKIP_SPACES\n  REM PRINT \"READ_TOKEN: \"+STR$(RI)+\", \"+MID$(A$,RI,1)\n  GOSUB READ_CHAR\n  IF C$=\";\" THEN GOSUB SKIP_TO_EOL:GOTO READ_TOKEN\n  T$=C$\n  IF T$=\"(\" OR T$=\")\" OR T$=\"[\" OR T$=\"]\" OR T$=\"{\" OR T$=\"}\" OR T$=\"'\" OR T$=\"`\" OR T$=\"@\" THEN RETURN\n  IF T$=\"^\" THEN RETURN\n  GOSUB PEEK_CHAR: REM peek at next character\n  IF T$=\"~\" AND C$<>\"@\" THEN RETURN\n  S1=0:S2=0: REM S1: INSTRING?, S2: ESCAPED?\n  IF T$=CHR$(34) THEN S1=1\n  READ_TOKEN_LOOP:\n    GOSUB PEEK_CHAR: REM peek at next character\n    IF C$=\"\" THEN RETURN\n    IF S1 THEN GOTO READ_TOKEN_CONT\n    IF C$=\" \" OR C$=\",\" OR C$=CHR$(13) OR C$=CHR$(10) THEN RETURN\n    IF C$=\"(\" OR C$=\")\" OR C$=\"[\" OR C$=\"]\" OR C$=\"{\" OR C$=\"}\" THEN RETURN\n    READ_TOKEN_CONT:\n    GOSUB READ_CHAR\n    T$=T$+C$\n    IF T$=\"~@\" THEN RETURN\n    IF S1=0 OR S2=1 THEN S2=0:GOTO READ_TOKEN_LOOP\n    REM S1=1 (INSTRING?) and S2=0 (not ESCAPED?)\n    IF C$=CHR$(92) THEN S2=1\n    IF C$=CHR$(34) THEN RETURN\n    GOTO READ_TOKEN_LOOP\n\n\nREM READ_CHAR(A$, RI) -> C$\nREAD_CHAR:\n  RJ=1:GOSUB DO_READ_CHAR\n  RETURN\n\nREM PEEK_CHAR(A$, RI) -> C$\nPEEK_CHAR:\n  RJ=0:GOSUB DO_READ_CHAR\n  RETURN\n\nREM DO_READ_CHAR(RJ, A$, RI):\nREM   - RI is position in A$\nREM   - RJ=1 is read, RJ=0 is peek\nDO_READ_CHAR:\n  C$=\"\"\n  IF RF>0 THEN GOTO READ_FILE_CHAR\n  IF RI<=LEN(A$) THEN C$=MID$(A$,RI,1):RI=RI+RJ\n  RETURN\n\nREM READ_FILE_CHAR(RJ) -> C$\nREM   - RJ=1 is read, RJ=0 is peek\nREM   - D$ is global used for already read pending character\nREM   - EZ is global used for end of file state\nREAD_FILE_CHAR:\n  IF D$<>\"\" THEN C$=D$:IF RJ=0 THEN RETURN\n  IF D$<>\"\" AND RJ=1 THEN D$=\"\":RETURN\n  D$=\"\"\n  IF EZ>2 THEN C$=\"\"\n  IF EZ=2 THEN C$=\")\"\n  IF EZ=1 THEN C$=CHR$(10)\n  IF EZ>0 THEN EZ=EZ+RJ:RETURN\n  #cbm GET#2,C$\n  #qbasic C$=INPUT$(1,2)\n  #qbasic IF EOF(2) THEN EZ=1:RETURN\n  IF RJ=0 THEN D$=C$\n  #cbm IF (ST AND 64) THEN EZ=1:RETURN\n  #cbm IF (ST AND 255) THEN EZ=1:ER=-1:E$=\"File read error\"+STR$(ST)\n  RETURN\n\nSKIP_SPACES:\n  GOSUB PEEK_CHAR: REM peek at next character\n  IF C$=\" \" OR C$=\",\" OR C$=CHR$(13) OR C$=CHR$(10) THEN GOSUB READ_CHAR:GOTO SKIP_SPACES\n  RETURN\n\nSKIP_TO_EOL:\n  GOSUB READ_CHAR\n  IF C$=\"\" OR C$=CHR$(13) OR C$=CHR$(10) THEN RETURN\n  GOTO SKIP_TO_EOL\n\n\nREM READ_FORM(A$, RI, RF) -> R\nSUB READ_FORM\n  Q=T:GOSUB PUSH_Q: REM save current value of T\n  READ_FORM_RECUR:\n  IF ER<>-2 THEN GOTO READ_FORM_RETURN\n  GOSUB READ_TOKEN\n  REM PRINT \"READ_FORM T$: [\"+T$+\"]\"\n  IF T$=\"\" THEN R=0:GOSUB INC_REF_R:GOTO READ_FORM_RETURN\n  IF T$=\"nil\" THEN T=0:GOTO READ_NIL_BOOL\n  IF T$=\"false\" THEN T=1:GOTO READ_NIL_BOOL\n  IF T$=\"true\" THEN T=2:GOTO READ_NIL_BOOL\n  IF T$=\"'\" THEN B$=\"quote\":GOTO READ_MACRO\n  IF T$=\"`\" THEN B$=\"quasiquote\":GOTO READ_MACRO\n  IF T$=\"~\" THEN B$=\"unquote\":GOTO READ_MACRO\n  IF T$=\"~@\" THEN B$=\"splice-unquote\":GOTO READ_MACRO\n  IF T$=\"^\" THEN B$=\"with-meta\":GOTO READ_MACRO\n  IF T$=\"@\" THEN B$=\"deref\":GOTO READ_MACRO\n  C$=MID$(T$,1,1)\n  REM PRINT \"C$: [\"+C$+\"](\"+STR$(ASC(C$))+\")\"\n  IF C$>=\"0\" AND C$<=\"9\" THEN GOTO READ_NUMBER\n  IF C$=\"-\" THEN GOTO READ_SYMBOL_MAYBE\n\n  IF C$=CHR$(34) THEN GOTO READ_STRING\n  IF C$=\":\" THEN GOTO READ_KEYWORD\n  REM set end character in Q and read the sequence\n  IF C$=\"(\" THEN T=6:Q=41:GOTO READ_SEQ_START: REM \")\"\n  IF C$=\"[\" THEN T=7:Q=93:GOTO READ_SEQ_START: REM \"]\"\n  IF C$=\"{\" THEN T=8:Q=125:GOTO READ_SEQ_START: REM \"}\"\n  IF C$=\")\" OR C$=\"]\" OR C$=\"}\" THEN R=-1:ER=-1:E$=\"unexpected \"+C$:GOTO READ_FORM_RETURN\n  GOTO READ_SYMBOL\n\n  READ_NIL_BOOL:\n    REM PRINT \"READ_NIL_BOOL\"\n    R=T*2\n    GOSUB INC_REF_R\n    GOTO READ_FORM_RETURN\n  READ_NUMBER:\n    REM PRINT \"READ_NUMBER\"\n    T=2:L=VAL(T$):GOSUB ALLOC\n    GOTO READ_FORM_RETURN\n  READ_MACRO:\n    REM push macro type\n    Q=-1*(T$=\"^\"):GOSUB PUSH_Q\n\n    REM B$ is set above\n    T=5:GOSUB STRING\n    REM push string\n    GOSUB PUSH_R\n\n    CALL READ_FORM\n    REM push first form\n    GOSUB PUSH_R\n    IF ER>-2 THEN GOTO READ_MACRO_DONE\n\n    GOSUB PEEK_Q_2\n    IF Q THEN GOTO READ_MACRO_3\n\n    READ_MACRO_2:\n      GOSUB PEEK_Q_1:B=Q\n      GOSUB PEEK_Q:A=Q\n      GOSUB LIST2\n      GOTO READ_MACRO_DONE\n\n    READ_MACRO_3:\n      CALL READ_FORM\n      GOSUB PEEK_Q_1:C=Q\n      B=R\n      GOSUB PEEK_Q:A=Q\n      GOSUB LIST3\n      AY=C:GOSUB RELEASE\n\n    READ_MACRO_DONE:\n      REM release values, list has ownership\n      AY=B:GOSUB RELEASE\n      AY=A:GOSUB RELEASE\n\n      REM pop the stack\n      GOSUB POP_Q: REM pop first form\n      GOSUB POP_Q: REM pop string\n      GOSUB POP_Q: REM pop macro type\n      T$=\"\": REM necessary to prevent unexpected EOF errors\n      GOTO READ_FORM_RETURN\n\n  READ_STRING:\n    REM PRINT \"READ_STRING\"\n    C=ASC(MID$(T$,LEN(T$),1))\n    IF C<>34 THEN R=-1:ER=-1:E$=\"expected '\"+CHR$(34)+\"', got EOF\":GOTO READ_FORM_RETURN\n    J=2:R$=\"\"\n    READ_STRING_LOOP:\n    #qbasic I=INSTR(J,T$,CHR$(92))\n    #cbm I=J\n    #cbm INSTR_LOOP:\n    #cbm IF I>LEN(T$) THEN I=0:GOTO INSTR_DONE\n    #cbm IF MID$(T$,I,1)=CHR$(92) THEN GOTO INSTR_DONE\n    #cbm I=I+1\n    #cbm GOTO INSTR_LOOP\n    #cbm INSTR_DONE:\n    IF I=0 THEN GOTO READ_STRING_DONE\n    R$=R$+MID$(T$,J,I-J)\n    C$=MID$(T$,I+1,1)\n    #qbasic IF C$=\"n\" THEN R$=R$+CHR$(10) ELSE R$=R$+C$\n    #cbm IF C$=\"n\" THEN R$=R$+CHR$(13)\n    #cbm IF C$<>\"n\" THEN R$=R$+C$\n    J=I+2\n    GOTO READ_STRING_LOOP\n    READ_STRING_DONE:\n    IF J=LEN(T$)+1 THEN R=-1:ER=-1:E$=\"expected '\"+CHR$(34)+\"', got EOF\":GOTO READ_FORM_RETURN\n    R$=R$+MID$(T$,J,LEN(T$)-J)\n    REM intern string value\n    B$=R$:T=4:GOSUB STRING\n    GOTO READ_FORM_RETURN\n  READ_KEYWORD:\n    R$=CHR$(127)+MID$(T$,2,LEN(T$)-1)\n    B$=R$:T=4:GOSUB STRING\n    GOTO READ_FORM_RETURN\n  READ_SYMBOL_MAYBE:\n    C$=MID$(T$,2,1)\n    IF C$>=\"0\" AND C$<=\"9\" THEN GOTO READ_NUMBER\n  READ_SYMBOL:\n    REM PRINT \"READ_SYMBOL\"\n    B$=T$:T=5:GOSUB STRING\n    GOTO READ_FORM_RETURN\n\n  READ_SEQ_START:\n    SD=SD+1\n\n    GOSUB PUSH_Q: REM push return character\n\n    REM setup the stack for the loop, T has type\n    GOSUB MAP_LOOP_START\n\n  READ_SEQ_LOOP:\n\n    REM TODO: reduce redundancy with READ_TOKEN\n    GOSUB SKIP_SPACES\n    GOSUB PEEK_CHAR: REM peek at next character\n    IF C$=\"\" THEN ER=-1:E$=\"unexpected EOF\":GOTO READ_SEQ_DONE\n    IF C$=\";\" THEN GOSUB SKIP_TO_EOL:GOTO READ_SEQ_LOOP\n    Q=3:GOSUB PEEK_Q_Q\n    IF C$=CHR$(Q) THEN GOSUB READ_CHAR:GOTO READ_SEQ_DONE\n\n    CALL READ_FORM\n    M=R: REM value (or key for hash-maps)\n\n    REM if error, release the unattached element\n    IF ER<>-2 THEN AY=R:GOSUB RELEASE:GOTO READ_SEQ_DONE\n\n    REM if this is a hash-map, READ_FORM again\n    IF T=8 THEN GOSUB PUSH_R:CALL READ_FORM\n    IF T=8 THEN N=R:GOSUB POP_Q:M=Q: REM set key and value\n\n    REM update the return sequence structure\n    REM release N since list takes full ownership\n    C=1:GOSUB MAP_LOOP_UPDATE\n\n    GOTO READ_SEQ_LOOP\n\n  READ_SEQ_DONE:\n    SD=SD-1\n    REM cleanup stack and get return value\n    GOSUB MAP_LOOP_DONE\n\n    GOSUB POP_Q: REM pop end character ptr\n    GOTO READ_FORM_RETURN\n\n  READ_FORM_RETURN:\n    GOSUB POP_Q:T=Q: REM restore current value of T\n\nEND SUB\n\n\nREM READ_STR(A$) -> R\nREAD_STR:\n  RI=1: REM index into A$\n  RF=0: REM not reading from file\n  SD=0: REM sequence read depth\n  CALL READ_FORM\n  RETURN\n\nREM READ_FILE(A$) -> R\nREAD_FILE:\n  RF=1: REM reading from file\n  EZ=0: REM file read state (1: EOF)\n  SD=0: REM sequence read depth\n  D$=\"\": REM pending read/peek character\n  #cbm OPEN 2,8,0,A$\n  #qbasic OPEN A$ FOR INPUT AS #2\n  #qbasic IF ERR()<>0 THEN ER=-1:E$=\"File not found\":RETURN\n  REM READ_TOKEN adds \"(do ... )\"\n  CALL READ_FORM\n  CLOSE 2\n  EZ=0\n  RETURN\n"
  },
  {
    "path": "impls/basic/readline.in.bas",
    "content": "REM READLINE(A$) -> R$\nREADLINE:\n  EZ=0\n  PRINT A$;\n  C$=\"\":R$=\"\":C=0\n  READCH:\n    #cbm GET C$\n    #qbasic C$=INKEY$\n    IF C$=\"\" THEN GOTO READCH\n    C=ASC(C$)\n    #qbasic IF ASC(C$)=8 THEN C=20:C$=CHR$(20)\n    IF C=4 OR C=0 THEN EZ=1:GOTO RL_DONE: REM EOF\n    IF C=127 OR C=20 THEN GOSUB RL_BACKSPACE\n    IF C=127 OR C=20 THEN GOTO READCH\n    IF (C<32 OR C>127) AND C<>13 THEN GOTO READCH\n    PRINT C$;\n    IF LEN(R$)<255 AND C$<>CHR$(13) THEN R$=R$+C$\n    IF LEN(R$)<255 AND C$<>CHR$(13) THEN GOTO READCH\n  RL_DONE:\n    #qbasic PRINT\n    RETURN\n\n  REM Assumes R$ has input buffer\n  RL_BACKSPACE:\n    IF LEN(R$)=0 THEN RETURN\n    R$=LEFT$(R$,LEN(R$)-1)\n    #cbm PRINT CHR$(157)+\" \"+CHR$(157);\n    #qbasic LOCATE ,POS(0)-1\n    #qbasic PRINT \" \";\n    #qbasic LOCATE ,POS(0)-1\n    RETURN\n\n"
  },
  {
    "path": "impls/basic/run",
    "content": "#!/usr/bin/env bash\ncd $(dirname $0)\n(echo \"(def! -*ARGS*- (list $(for a in \"${@}\"; do echo -n \" \\\"${a}\\\"\"; done)))\") > .args.mal\ncase ${basic_MODE:-cbm} in\n    cbm)    exec cbmbasic ${STEP:-stepA_mal}.bas \"${@}\" ;;\n    qbasic) exec ./${STEP:-stepA_mal} \"${@}\" ;;\n    *)      echo \"Invalid basic_MODE: ${basic_MODE}\"; exit 2 ;;\nesac\n"
  },
  {
    "path": "impls/basic/step0_repl.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'readline.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL(A$) -> R$\nSUB EVAL\n  R$=A$\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM REP(A$) -> R$\nSUB REP\n  REM inlined READ (not affecting A$)\n  CALL EVAL\n  REM inlined PRINT (not affecting A$)\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB DIM_MEMORY\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    PRINT R$\n    GOTO REPL_LOOP\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    #cbm END\n    #qbasic SYSTEM\n"
  },
  {
    "path": "impls/basic/step1_read_print.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL(A) -> R\nSUB EVAL\n  R=A\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:CALL EVAL\n\n  RE_DONE:\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step2_eval.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A on the stack\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A off the stack\n    GOSUB POP_A\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A on the stack\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  REM AZ=A:B=1:GOSUB PR_STR\n  REM PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    H=E:B$=S$(Z%(A+1)):GOSUB HASHMAP_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n      W=R\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      R=-1:ER=-1:E$=\"apply of non-function\":GOTO EVAL_INVOKE_DONE\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n\n        GOSUB DO_FUNCTION\n\n      EVAL_INVOKE_DONE:\n        REM pop and release f/args\n      AY=W:GOSUB RELEASE\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A off the stack\n    GOSUB POP_A\n\nEND SUB\n\nREM DO_FUNCTION(F, AR)\nDO_FUNCTION:\n  REM Get the function number\n  G=Z%(F+1)\n\n  REM Get argument values\n  A=Z%(Z%(AR+2)+1)\n  B=Z%(Z%(Z%(AR+1)+2)+1)\n\n  REM Switch on the function number\n  IF G=1 THEN GOTO DO_ADD\n  IF G=2 THEN GOTO DO_SUB\n  IF G=3 THEN GOTO DO_MULT\n  IF G=4 THEN GOTO DO_DIV\n  ER=-1:E$=\"unknown function\"+STR$(G):RETURN\n\n  DO_ADD:\n    T=2:L=A+B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n  DO_SUB:\n    T=2:L=A-B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n  DO_MULT:\n    T=2:L=A*B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n  DO_DIV:\n    T=2:L=A/B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n\n  DO_FUNCTION_DONE:\n    RETURN\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  GOSUB HASHMAP:D=R\n\n  REM + function\n  T=9:L=1:GOSUB ALLOC: REM native function\n  H=D:B$=\"+\":C=R:GOSUB ASSOC1_S:D=R\n\n  REM - function\n  T=9:L=2:GOSUB ALLOC: REM native function\n  H=D:B$=\"-\":C=R:GOSUB ASSOC1_S:D=R\n\n  REM * function\n  T=9:L=3:GOSUB ALLOC: REM native function\n  H=D:B$=\"*\":C=R:GOSUB ASSOC1_S:D=R\n\n  REM / function\n  T=9:L=4:GOSUB ALLOC: REM native function\n  H=D:B$=\"/\":C=R:GOSUB ASSOC1_S:D=R\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step3_env.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:CALL EVAL: REM eval A2 using let_env\n        GOTO EVAL_RETURN\n    EVAL_INVOKE:\n      CALL EVAL_AST\n      W=R\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      R=-1:ER=-1:E$=\"apply of non-function\":GOTO EVAL_INVOKE_DONE\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n\n        GOSUB DO_FUNCTION\n\n      EVAL_INVOKE_DONE:\n        REM pop and release f/args\n      AY=W:GOSUB RELEASE\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM DO_FUNCTION(F, AR)\nDO_FUNCTION:\n  REM Get the function number\n  G=Z%(F+1)\n\n  REM Get argument values\n  A=Z%(Z%(AR+2)+1)\n  B=Z%(Z%(Z%(AR+1)+2)+1)\n\n  REM Switch on the function number\n  IF G=1 THEN GOTO DO_ADD\n  IF G=2 THEN GOTO DO_SUB\n  IF G=3 THEN GOTO DO_MULT\n  IF G=4 THEN GOTO DO_DIV\n  ER=-1:E$=\"unknown function\"+STR$(G):RETURN\n\n  DO_ADD:\n    T=2:L=A+B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n  DO_SUB:\n    T=2:L=A-B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n  DO_MULT:\n    T=2:L=A*B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n  DO_DIV:\n    T=2:L=A/B:GOSUB ALLOC\n    GOTO DO_FUNCTION_DONE\n\n  DO_FUNCTION_DONE:\n    RETURN\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n  E=D\n\n  REM + function\n  T=9:L=1:GOSUB ALLOC: REM native function\n  B$=\"+\":C=R:GOSUB ENV_SET_S\n\n  REM - function\n  T=9:L=2:GOSUB ALLOC: REM native function\n  B$=\"-\":C=R:GOSUB ENV_SET_S\n\n  REM * function\n  T=9:L=3:GOSUB ALLOC: REM native function\n  B$=\"*\":C=R:GOSUB ENV_SET_S\n\n  REM / function\n  T=9:L=4:GOSUB ALLOC: REM native function\n  B$=\"/\":C=R:GOSUB ENV_SET_S\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step4_if_fn_do.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:CALL EVAL: REM eval A2 using let_env\n        GOTO EVAL_RETURN\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n\n      CALL EVAL_AST\n\n      GOSUB PUSH_R: REM push eval'd list\n      A=R:GOSUB LAST: REM return the last element\n      GOSUB POP_Q:AY=Q: REM pop eval'd list\n      GOSUB RELEASE: REM release the eval'd list\n      GOTO EVAL_RETURN\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step5_tco.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM if we are returning to DO, then skip last element\n      REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to\n      REM return early and for TCO to work\n      Q=5:GOSUB PEEK_Q_Q\n      IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      Q=E:GOSUB PUSH_Q: REM push env for for later release\n\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:AY=Q: REM pop previous env\n\n        REM release previous environment if not the current EVAL env\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n      GOSUB PUSH_A: REM push/save A\n\n      REM this must be EVAL_AST call #2 for EVAL_AST to return early\n      REM and for TCO to work\n      CALL EVAL_AST\n\n      REM cleanup\n      AY=R: REM get eval'd list for release\n\n      GOSUB POP_A: REM pop/restore original A for LAST\n      GOSUB LAST: REM get last element for return\n      A=R: REM new recur AST\n\n      REM cleanup\n      GOSUB RELEASE: REM release eval'd list\n      AY=A:GOSUB RELEASE: REM release LAST value (not sure why)\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step6_file.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM if we are returning to DO, then skip last element\n      REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to\n      REM return early and for TCO to work\n      Q=5:GOSUB PEEK_Q_Q\n      IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      Q=E:GOSUB PUSH_Q: REM push env for for later release\n\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:AY=Q: REM pop previous env\n\n        REM release previous environment if not the current EVAL env\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n      GOSUB PUSH_A: REM push/save A\n\n      REM this must be EVAL_AST call #2 for EVAL_AST to return early\n      REM and for TCO to work\n      CALL EVAL_AST\n\n      REM cleanup\n      AY=R: REM get eval'd list for release\n\n      GOSUB POP_A: REM pop/restore original A for LAST\n      GOSUB LAST: REM get last element for return\n      A=R: REM new recur AST\n\n      REM cleanup\n      GOSUB RELEASE: REM release eval'd list\n      AY=A:GOSUB RELEASE: REM release LAST value (not sure why)\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(def! load-file (fn* (f) (do (eval (read-file f)) nil)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM load the args file\n  A$=\"(load-file \"+CHR$(34)+\".args.mal\"+CHR$(34)+\")\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  IF ER>-2 THEN GOSUB PRINT_ERROR:END\n\n  REM set the argument list\n  A$=\"(def! *ARGV* (rest -*ARGS*-))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM get the first argument\n  A$=\"(first -*ARGS*-)\"\n  GOSUB RE\n\n  REM no arguments, start REPL loop\n  REM if there is an argument, then run it as a program\n  IF 15<R THEN GOTO RUN_PROG\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  RUN_PROG:\n    REM free up first arg because we get it again\n    AY=R:GOSUB RELEASE\n    REM run a single mal program and exit\n    A$=\"(load-file (first -*ARGS*-))\"\n    GOSUB RE\n    IF ER<>-2 THEN GOSUB PRINT_ERROR\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step7_quote.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM QUASIQUOTE(A) -> R\nSUB QUASIQUOTE\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO QQ_SYMBOL,QQ_LIST,QQ_VECTOR,QQ_MAP\n\n  REM Return other types unchanged.\n    R=A\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_MAP:\n  QQ_SYMBOL:\n    REM Return a list containing 'quote and A.\n    B$=\"quote\":T=5:GOSUB STRING\n    B=R:GOSUB LIST2\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_VECTOR:\n    REM Return a list containing 'vec and the result of QQ_FOLDR on A.\n    CALL QQ_FOLDR\n    A=R\n    B$=\"vec\":T=5:GOSUB STRING:B=R\n    GOSUB LIST2\n    AY=A:GOSUB RELEASE\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_LIST:\n    REM Check if A contains 'unquote and a form.\n    IF (Z%(A+1)=0) THEN GOTO QQ_LIST_NORMAL\n    R=Z%(A+2)\n    IF (Z%(R)AND 31)<>5 THEN GOTO QQ_LIST_NORMAL\n    IF S$(Z%(R+1))<>\"unquote\" THEN GOTO QQ_LIST_NORMAL\n\n  REM Indeed.  Return a list containing 'unquote and the form.\n    R=Z%(Z%(A+1)+2)\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_LIST_NORMAL:\n    REM Normal list, process with QQ_FOLDR.\n    CALL QQ_FOLDR\n\nQQ_DONE:\nEND SUB\n\nREM Quasiquote right fold (A) -> R.\nREM Used for unquoted lists (GOTO), vectors (GOSUB),\nREM and recursively (GOSUB).\nSUB QQ_FOLDR\n  IF A=0 THEN GOTO QQ_EMPTY\n  IF Z%(A+1)=0 THEN GOTO QQ_EMPTY\n  GOTO QQ_NOTEMPTY\n\n  QQ_EMPTY:\n    REM empty list/vector -> empty list\n    R=6\n    GOSUB INC_REF_R\n\n    GOTO QQ_FOLDR_DONE\n\n  QQ_NOTEMPTY:\n    REM Execute QQ_FOLDR recursively with (rest A)\n    GOSUB PUSH_A\n    A=Z%(A+1):CALL QQ_FOLDR\n    GOSUB POP_A\n\n    REM Set A to elt = (first A)\n    A=Z%(A+2)\n\n    REM Quasiquote transition function:\n    REM A: current element, R: accumulator -> R: new accumulator\n\n    REM check if A is a list starting with splice-unquote\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO QQ_DEFAULT\n    IF (Z%(A+1)=0) THEN GOTO QQ_DEFAULT\n    B=Z%(A+2)\n    IF (Z%(B)AND 31)<>5 THEN GOTO QQ_DEFAULT\n    IF S$(Z%(B+1))<>\"splice-unquote\" THEN GOTO QQ_DEFAULT\n\n    REM ('concat, A[1], R)\n      B=Z%(Z%(A+1)+2)\n      A=R\n      B$=\"concat\":T=5:GOSUB STRING:C=R\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\n      GOTO QQ_FOLDR_DONE\n\n    QQ_DEFAULT:\n      REM ('cons, quasiquote(A), R)\n      GOSUB PUSH_R\n      CALL QUASIQUOTE\n      B=R\n      B$=\"cons\":T=5:GOSUB STRING:C=R\n      GOSUB POP_A\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=B:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\nQQ_FOLDR_DONE:\nEND SUB\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM if we are returning to DO, then skip last element\n      REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to\n      REM return early and for TCO to work\n      Q=5:GOSUB PEEK_Q_Q\n      IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"quote\" THEN GOTO EVAL_QUOTE\n    IF A$=\"quasiquote\" THEN GOTO EVAL_QUASIQUOTE\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      Q=E:GOSUB PUSH_Q: REM push env for for later release\n\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:AY=Q: REM pop previous env\n\n        REM release previous environment if not the current EVAL env\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n      GOSUB PUSH_A: REM push/save A\n\n      REM this must be EVAL_AST call #2 for EVAL_AST to return early\n      REM and for TCO to work\n      CALL EVAL_AST\n\n      REM cleanup\n      AY=R: REM get eval'd list for release\n\n      GOSUB POP_A: REM pop/restore original A for LAST\n      GOSUB LAST: REM get last element for return\n      A=R: REM new recur AST\n\n      REM cleanup\n      GOSUB RELEASE: REM release eval'd list\n      AY=A:GOSUB RELEASE: REM release LAST value (not sure why)\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_QUOTE:\n      R=Z%(Z%(A+1)+2)\n      GOSUB INC_REF_R\n      GOTO EVAL_RETURN\n\n    EVAL_QUASIQUOTE:\n      R=Z%(Z%(A+1)+2)\n      A=R:CALL QUASIQUOTE\n      A=R\n      REM add quasiquote result to pending release queue to free when\n      REM next lower EVAL level returns (LV)\n      GOSUB PEND_A_LV\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(def! load-file (fn* (f) (do (eval (read-file f)) nil)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM load the args file\n  A$=\"(load-file \"+CHR$(34)+\".args.mal\"+CHR$(34)+\")\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  IF ER>-2 THEN GOSUB PRINT_ERROR:END\n\n  REM set the argument list\n  A$=\"(def! *ARGV* (rest -*ARGS*-))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM get the first argument\n  A$=\"(first -*ARGS*-)\"\n  GOSUB RE\n\n  REM no arguments, start REPL loop\n  REM if there is an argument, then run it as a program\n  IF 15<R THEN GOTO RUN_PROG\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  RUN_PROG:\n    REM free up first arg because we get it again\n    AY=R:GOSUB RELEASE\n    REM run a single mal program and exit\n    A$=\"(load-file (first -*ARGS*-))\"\n    GOSUB RE\n    IF ER<>-2 THEN GOSUB PRINT_ERROR\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step8_macros.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM QUASIQUOTE(A) -> R\nSUB QUASIQUOTE\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO QQ_SYMBOL,QQ_LIST,QQ_VECTOR,QQ_MAP\n\n  REM Return other types unchanged.\n    R=A\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_MAP:\n  QQ_SYMBOL:\n    REM Return a list containing 'quote and A.\n    B$=\"quote\":T=5:GOSUB STRING\n    B=R:GOSUB LIST2\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_VECTOR:\n    REM Return a list containing 'vec and the result of QQ_FOLDR on A.\n    CALL QQ_FOLDR\n    A=R\n    B$=\"vec\":T=5:GOSUB STRING:B=R\n    GOSUB LIST2\n    AY=A:GOSUB RELEASE\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_LIST:\n    REM Check if A contains 'unquote and a form.\n    IF (Z%(A+1)=0) THEN GOTO QQ_LIST_NORMAL\n    R=Z%(A+2)\n    IF (Z%(R)AND 31)<>5 THEN GOTO QQ_LIST_NORMAL\n    IF S$(Z%(R+1))<>\"unquote\" THEN GOTO QQ_LIST_NORMAL\n\n  REM Indeed.  Return a list containing 'unquote and the form.\n    R=Z%(Z%(A+1)+2)\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_LIST_NORMAL:\n    REM Normal list, process with QQ_FOLDR.\n    CALL QQ_FOLDR\n\nQQ_DONE:\nEND SUB\n\nREM Quasiquote right fold (A) -> R.\nREM Used for unquoted lists (GOTO), vectors (GOSUB),\nREM and recursively (GOSUB).\nSUB QQ_FOLDR\n  IF A=0 THEN GOTO QQ_EMPTY\n  IF Z%(A+1)=0 THEN GOTO QQ_EMPTY\n  GOTO QQ_NOTEMPTY\n\n  QQ_EMPTY:\n    REM empty list/vector -> empty list\n    R=6\n    GOSUB INC_REF_R\n\n    GOTO QQ_FOLDR_DONE\n\n  QQ_NOTEMPTY:\n    REM Execute QQ_FOLDR recursively with (rest A)\n    GOSUB PUSH_A\n    A=Z%(A+1):CALL QQ_FOLDR\n    GOSUB POP_A\n\n    REM Set A to elt = (first A)\n    A=Z%(A+2)\n\n    REM Quasiquote transition function:\n    REM A: current element, R: accumulator -> R: new accumulator\n\n    REM check if A is a list starting with splice-unquote\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO QQ_DEFAULT\n    IF (Z%(A+1)=0) THEN GOTO QQ_DEFAULT\n    B=Z%(A+2)\n    IF (Z%(B)AND 31)<>5 THEN GOTO QQ_DEFAULT\n    IF S$(Z%(B+1))<>\"splice-unquote\" THEN GOTO QQ_DEFAULT\n\n    REM ('concat, A[1], R)\n      B=Z%(Z%(A+1)+2)\n      A=R\n      B$=\"concat\":T=5:GOSUB STRING:C=R\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\n      GOTO QQ_FOLDR_DONE\n\n    QQ_DEFAULT:\n      REM ('cons, quasiquote(A), R)\n      GOSUB PUSH_R\n      CALL QUASIQUOTE\n      B=R\n      B$=\"cons\":T=5:GOSUB STRING:C=R\n      GOSUB POP_A\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=B:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\nQQ_FOLDR_DONE:\nEND SUB\n\nREM MACROEXPAND(A, E) -> A:\nSUB MACROEXPAND\n  GOSUB PUSH_A\n\n  MACROEXPAND_LOOP:\n    REM list?\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO MACROEXPAND_DONE\n    REM non-empty?\n    IF Z%(A+1)=0 THEN GOTO MACROEXPAND_DONE\n    B=Z%(A+2)\n    REM symbol? in first position\n    IF (Z%(B)AND 31)<>5 THEN GOTO MACROEXPAND_DONE\n    REM defined in environment?\n    B$=S$(Z%(B+1)):CALL ENV_GET\n    IF R3=0 THEN GOTO MACROEXPAND_DONE\n    B=R\n    REM macro?\n    IF (Z%(B)AND 31)<>11 THEN GOTO MACROEXPAND_DONE\n\n    GOSUB INC_REF_R\n    F=B:AR=Z%(A+1):CALL APPLY\n    A=R\n\n    GOSUB PEEK_Q:AY=Q\n    REM if previous A was not the first A into macroexpand (i.e. an\n    REM intermediate form) then free it\n    IF A<>AY THEN GOSUB PEND_A_LV\n\n    IF ER<>-2 THEN GOTO MACROEXPAND_DONE\n    GOTO MACROEXPAND_LOOP\n\n  MACROEXPAND_DONE:\n    GOSUB POP_Q: REM pop original A\nEND SUB\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM if we are returning to DO, then skip last element\n      REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to\n      REM return early and for TCO to work\n      Q=5:GOSUB PEEK_Q_Q\n      IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  EVAL_NOT_LIST:\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n    CALL MACROEXPAND\n\n    GOSUB LIST_Q\n    IF R<>1 THEN GOTO EVAL_NOT_LIST\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"quote\" THEN GOTO EVAL_QUOTE\n    IF A$=\"quasiquote\" THEN GOTO EVAL_QUASIQUOTE\n    IF A$=\"defmacro!\" THEN GOTO EVAL_DEFMACRO\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      Q=E:GOSUB PUSH_Q: REM push env for for later release\n\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:AY=Q: REM pop previous env\n\n        REM release previous environment if not the current EVAL env\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n      GOSUB PUSH_A: REM push/save A\n\n      REM this must be EVAL_AST call #2 for EVAL_AST to return early\n      REM and for TCO to work\n      CALL EVAL_AST\n\n      REM cleanup\n      AY=R: REM get eval'd list for release\n\n      GOSUB POP_A: REM pop/restore original A for LAST\n      GOSUB LAST: REM get last element for return\n      A=R: REM new recur AST\n\n      REM cleanup\n      GOSUB RELEASE: REM release eval'd list\n      AY=A:GOSUB RELEASE: REM release LAST value (not sure why)\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_QUOTE:\n      R=Z%(Z%(A+1)+2)\n      GOSUB INC_REF_R\n      GOTO EVAL_RETURN\n\n    EVAL_QUASIQUOTE:\n      R=Z%(Z%(A+1)+2)\n      A=R:CALL QUASIQUOTE\n      A=R\n      REM add quasiquote result to pending release queue to free when\n      REM next lower EVAL level returns (LV)\n      GOSUB PEND_A_LV\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DEFMACRO:\n      REM PRINT \"defmacro!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q: REM push A1\n      A=A2:CALL EVAL: REM eval A2\n      GOSUB POP_Q:A1=Q: REM pop A1\n\n      REM change function to macro\n      Z%(R)=Z%(R)+1\n\n      REM set A1 in env to A2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(def! load-file (fn* (f) (do (eval (read-file f)) nil)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)\"\n  A$=A$+\" (if (> (count xs) 1) (nth xs 1) (throw \"+CHR$(34)+\"odd number of\"\n  A$=A$+\" forms to cond\"+CHR$(34)+\")) (cons 'cond (rest (rest xs)))))))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM load the args file\n  A$=\"(load-file \"+CHR$(34)+\".args.mal\"+CHR$(34)+\")\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  IF ER>-2 THEN GOSUB PRINT_ERROR:END\n\n  REM set the argument list\n  A$=\"(def! *ARGV* (rest -*ARGS*-))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM get the first argument\n  A$=\"(first -*ARGS*-)\"\n  GOSUB RE\n\n  REM no arguments, start REPL loop\n  REM if there is an argument, then run it as a program\n  IF 15<R THEN GOTO RUN_PROG\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  RUN_PROG:\n    REM free up first arg because we get it again\n    AY=R:GOSUB RELEASE\n    REM run a single mal program and exit\n    A$=\"(load-file (first -*ARGS*-))\"\n    GOSUB RE\n    IF ER<>-2 THEN GOSUB PRINT_ERROR\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/step9_try.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM QUASIQUOTE(A) -> R\nSUB QUASIQUOTE\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO QQ_SYMBOL,QQ_LIST,QQ_VECTOR,QQ_MAP\n\n  REM Return other types unchanged.\n    R=A\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_MAP:\n  QQ_SYMBOL:\n    REM Return a list containing 'quote and A.\n    B$=\"quote\":T=5:GOSUB STRING\n    B=R:GOSUB LIST2\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_VECTOR:\n    REM Return a list containing 'vec and the result of QQ_FOLDR on A.\n    CALL QQ_FOLDR\n    A=R\n    B$=\"vec\":T=5:GOSUB STRING:B=R\n    GOSUB LIST2\n    AY=A:GOSUB RELEASE\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_LIST:\n    REM Check if A contains 'unquote and a form.\n    IF (Z%(A+1)=0) THEN GOTO QQ_LIST_NORMAL\n    R=Z%(A+2)\n    IF (Z%(R)AND 31)<>5 THEN GOTO QQ_LIST_NORMAL\n    IF S$(Z%(R+1))<>\"unquote\" THEN GOTO QQ_LIST_NORMAL\n\n  REM Indeed.  Return a list containing 'unquote and the form.\n    R=Z%(Z%(A+1)+2)\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_LIST_NORMAL:\n    REM Normal list, process with QQ_FOLDR.\n    CALL QQ_FOLDR\n\nQQ_DONE:\nEND SUB\n\nREM Quasiquote right fold (A) -> R.\nREM Used for unquoted lists (GOTO), vectors (GOSUB),\nREM and recursively (GOSUB).\nSUB QQ_FOLDR\n  IF A=0 THEN GOTO QQ_EMPTY\n  IF Z%(A+1)=0 THEN GOTO QQ_EMPTY\n  GOTO QQ_NOTEMPTY\n\n  QQ_EMPTY:\n    REM empty list/vector -> empty list\n    R=6\n    GOSUB INC_REF_R\n\n    GOTO QQ_FOLDR_DONE\n\n  QQ_NOTEMPTY:\n    REM Execute QQ_FOLDR recursively with (rest A)\n    GOSUB PUSH_A\n    A=Z%(A+1):CALL QQ_FOLDR\n    GOSUB POP_A\n\n    REM Set A to elt = (first A)\n    A=Z%(A+2)\n\n    REM Quasiquote transition function:\n    REM A: current element, R: accumulator -> R: new accumulator\n\n    REM check if A is a list starting with splice-unquote\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO QQ_DEFAULT\n    IF (Z%(A+1)=0) THEN GOTO QQ_DEFAULT\n    B=Z%(A+2)\n    IF (Z%(B)AND 31)<>5 THEN GOTO QQ_DEFAULT\n    IF S$(Z%(B+1))<>\"splice-unquote\" THEN GOTO QQ_DEFAULT\n\n    REM ('concat, A[1], R)\n      B=Z%(Z%(A+1)+2)\n      A=R\n      B$=\"concat\":T=5:GOSUB STRING:C=R\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\n      GOTO QQ_FOLDR_DONE\n\n    QQ_DEFAULT:\n      REM ('cons, quasiquote(A), R)\n      GOSUB PUSH_R\n      CALL QUASIQUOTE\n      B=R\n      B$=\"cons\":T=5:GOSUB STRING:C=R\n      GOSUB POP_A\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=B:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\nQQ_FOLDR_DONE:\nEND SUB\n\nREM MACROEXPAND(A, E) -> A:\nSUB MACROEXPAND\n  GOSUB PUSH_A\n\n  MACROEXPAND_LOOP:\n    REM list?\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO MACROEXPAND_DONE\n    REM non-empty?\n    IF Z%(A+1)=0 THEN GOTO MACROEXPAND_DONE\n    B=Z%(A+2)\n    REM symbol? in first position\n    IF (Z%(B)AND 31)<>5 THEN GOTO MACROEXPAND_DONE\n    REM defined in environment?\n    B$=S$(Z%(B+1)):CALL ENV_GET\n    IF R3=0 THEN GOTO MACROEXPAND_DONE\n    B=R\n    REM macro?\n    IF (Z%(B)AND 31)<>11 THEN GOTO MACROEXPAND_DONE\n\n    GOSUB INC_REF_R\n    F=B:AR=Z%(A+1):CALL APPLY\n    A=R\n\n    GOSUB PEEK_Q:AY=Q\n    REM if previous A was not the first A into macroexpand (i.e. an\n    REM intermediate form) then free it\n    IF A<>AY THEN GOSUB PEND_A_LV\n\n    IF ER<>-2 THEN GOTO MACROEXPAND_DONE\n    GOTO MACROEXPAND_LOOP\n\n  MACROEXPAND_DONE:\n    GOSUB POP_Q: REM pop original A\nEND SUB\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM if we are returning to DO, then skip last element\n      REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to\n      REM return early and for TCO to work\n      Q=5:GOSUB PEEK_Q_Q\n      IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  EVAL_NOT_LIST:\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n    CALL MACROEXPAND\n\n    GOSUB LIST_Q\n    IF R<>1 THEN GOTO EVAL_NOT_LIST\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"quote\" THEN GOTO EVAL_QUOTE\n    IF A$=\"quasiquote\" THEN GOTO EVAL_QUASIQUOTE\n    IF A$=\"defmacro!\" THEN GOTO EVAL_DEFMACRO\n    IF A$=\"try*\" THEN GOTO EVAL_TRY\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      Q=E:GOSUB PUSH_Q: REM push env for for later release\n\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:AY=Q: REM pop previous env\n\n        REM release previous environment if not the current EVAL env\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n      GOSUB PUSH_A: REM push/save A\n\n      REM this must be EVAL_AST call #2 for EVAL_AST to return early\n      REM and for TCO to work\n      CALL EVAL_AST\n\n      REM cleanup\n      AY=R: REM get eval'd list for release\n\n      GOSUB POP_A: REM pop/restore original A for LAST\n      GOSUB LAST: REM get last element for return\n      A=R: REM new recur AST\n\n      REM cleanup\n      GOSUB RELEASE: REM release eval'd list\n      AY=A:GOSUB RELEASE: REM release LAST value (not sure why)\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_QUOTE:\n      R=Z%(Z%(A+1)+2)\n      GOSUB INC_REF_R\n      GOTO EVAL_RETURN\n\n    EVAL_QUASIQUOTE:\n      R=Z%(Z%(A+1)+2)\n      A=R:CALL QUASIQUOTE\n      A=R\n      REM add quasiquote result to pending release queue to free when\n      REM next lower EVAL level returns (LV)\n      GOSUB PEND_A_LV\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DEFMACRO:\n      REM PRINT \"defmacro!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q: REM push A1\n      A=A2:CALL EVAL: REM eval A2\n      GOSUB POP_Q:A1=Q: REM pop A1\n\n      REM change function to macro\n      Z%(R)=Z%(R)+1\n\n      REM set A1 in env to A2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_TRY:\n      REM PRINT \"try*\"\n      GOSUB EVAL_GET_A1: REM set A1\n\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL: REM eval A1\n      GOSUB POP_A: REM pop/restore A\n\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      REM if there is no error or catch block then return\n      IF ER=-2 OR A2=0 THEN GOTO EVAL_RETURN\n\n      REM create environment for the catch block eval\n      C=E:GOSUB ENV_NEW:E=R\n\n      A=A2:GOSUB EVAL_GET_A2: REM set A1 and A2 from catch block\n\n      REM create object for ER=-1 type raw string errors\n      IF ER=-1 THEN B$=E$:T=4:GOSUB STRING:ER=R:GOSUB INC_REF_R\n\n      REM bind the catch symbol to the error object\n      K=A1:C=ER:GOSUB ENV_SET\n      AY=R:GOSUB RELEASE: REM release our use, env took ownership\n\n      REM unset error for catch eval\n      ER=-2:E$=\"\"\n\n      A=A2:CALL EVAL\n\n      GOTO EVAL_RETURN\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(def! load-file (fn* (f) (do (eval (read-file f)) nil)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)\"\n  A$=A$+\" (if (> (count xs) 1) (nth xs 1) (throw \"+CHR$(34)+\"odd number of\"\n  A$=A$+\" forms to cond\"+CHR$(34)+\")) (cons 'cond (rest (rest xs)))))))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM load the args file\n  A$=\"(load-file \"+CHR$(34)+\".args.mal\"+CHR$(34)+\")\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  IF ER>-2 THEN GOSUB PRINT_ERROR:END\n\n  REM set the argument list\n  A$=\"(def! *ARGV* (rest -*ARGS*-))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM get the first argument\n  A$=\"(first -*ARGS*-)\"\n  GOSUB RE\n\n  REM no arguments, start REPL loop\n  REM if there is an argument, then run it as a program\n  IF 15<R THEN GOTO RUN_PROG\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  RUN_PROG:\n    REM free up first arg because we get it again\n    AY=R:GOSUB RELEASE\n    REM run a single mal program and exit\n    A$=\"(load-file (first -*ARGS*-))\"\n    GOSUB RE\n    IF ER<>-2 THEN GOSUB PRINT_ERROR\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    REM if the error is an object, then print and free it\n    IF ER>=0 THEN AZ=ER:B=0:GOSUB PR_STR:E$=R$:AY=ER:GOSUB RELEASE\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/stepA_mal.in.bas",
    "content": "GOTO MAIN\n\nREM $INCLUDE: 'mem.in.bas'\nREM $INCLUDE: 'types.in.bas'\nREM $INCLUDE: 'readline.in.bas'\nREM $INCLUDE: 'reader.in.bas'\nREM $INCLUDE: 'printer.in.bas'\nREM $INCLUDE: 'env.in.bas'\nREM $INCLUDE: 'core.in.bas'\n\nREM $INCLUDE: 'debug.in.bas'\n\nREM READ is inlined in RE\n\nREM QUASIQUOTE(A) -> R\nSUB QUASIQUOTE\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO QQ_SYMBOL,QQ_LIST,QQ_VECTOR,QQ_MAP\n\n  REM Return other types unchanged.\n    R=A\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_MAP:\n  QQ_SYMBOL:\n    REM Return a list containing 'quote and A.\n    B$=\"quote\":T=5:GOSUB STRING\n    B=R:GOSUB LIST2\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_VECTOR:\n    REM Return a list containing 'vec and the result of QQ_FOLDR on A.\n    CALL QQ_FOLDR\n    A=R\n    B$=\"vec\":T=5:GOSUB STRING:B=R\n    GOSUB LIST2\n    AY=A:GOSUB RELEASE\n    AY=B:GOSUB RELEASE\n    GOTO QQ_DONE\n\n  QQ_LIST:\n    REM Check if A contains 'unquote and a form.\n    IF (Z%(A+1)=0) THEN GOTO QQ_LIST_NORMAL\n    R=Z%(A+2)\n    IF (Z%(R)AND 31)<>5 THEN GOTO QQ_LIST_NORMAL\n    IF S$(Z%(R+1))<>\"unquote\" THEN GOTO QQ_LIST_NORMAL\n\n  REM Indeed.  Return a list containing 'unquote and the form.\n    R=Z%(Z%(A+1)+2)\n    GOSUB INC_REF_R\n    GOTO QQ_DONE\n\n  QQ_LIST_NORMAL:\n    REM Normal list, process with QQ_FOLDR.\n    CALL QQ_FOLDR\n\nQQ_DONE:\nEND SUB\n\nREM Quasiquote right fold (A) -> R.\nREM Used for unquoted lists (GOTO), vectors (GOSUB),\nREM and recursively (GOSUB).\nSUB QQ_FOLDR\n  IF A=0 THEN GOTO QQ_EMPTY\n  IF Z%(A+1)=0 THEN GOTO QQ_EMPTY\n  GOTO QQ_NOTEMPTY\n\n  QQ_EMPTY:\n    REM empty list/vector -> empty list\n    R=6\n    GOSUB INC_REF_R\n\n    GOTO QQ_FOLDR_DONE\n\n  QQ_NOTEMPTY:\n    REM Execute QQ_FOLDR recursively with (rest A)\n    GOSUB PUSH_A\n    A=Z%(A+1):CALL QQ_FOLDR\n    GOSUB POP_A\n\n    REM Set A to elt = (first A)\n    A=Z%(A+2)\n\n    REM Quasiquote transition function:\n    REM A: current element, R: accumulator -> R: new accumulator\n\n    REM check if A is a list starting with splice-unquote\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO QQ_DEFAULT\n    IF (Z%(A+1)=0) THEN GOTO QQ_DEFAULT\n    B=Z%(A+2)\n    IF (Z%(B)AND 31)<>5 THEN GOTO QQ_DEFAULT\n    IF S$(Z%(B+1))<>\"splice-unquote\" THEN GOTO QQ_DEFAULT\n\n    REM ('concat, A[1], R)\n      B=Z%(Z%(A+1)+2)\n      A=R\n      B$=\"concat\":T=5:GOSUB STRING:C=R\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\n      GOTO QQ_FOLDR_DONE\n\n    QQ_DEFAULT:\n      REM ('cons, quasiquote(A), R)\n      GOSUB PUSH_R\n      CALL QUASIQUOTE\n      B=R\n      B$=\"cons\":T=5:GOSUB STRING:C=R\n      GOSUB POP_A\n      GOSUB LIST3\n      REM release inner quasiquoted since outer list takes ownership\n      AY=A:GOSUB RELEASE\n      AY=B:GOSUB RELEASE\n      AY=C:GOSUB RELEASE\n\nQQ_FOLDR_DONE:\nEND SUB\n\nREM MACROEXPAND(A, E) -> A:\nSUB MACROEXPAND\n  GOSUB PUSH_A\n\n  MACROEXPAND_LOOP:\n    REM list?\n    GOSUB TYPE_A\n    IF T<>6 THEN GOTO MACROEXPAND_DONE\n    REM non-empty?\n    IF Z%(A+1)=0 THEN GOTO MACROEXPAND_DONE\n    B=Z%(A+2)\n    REM symbol? in first position\n    IF (Z%(B)AND 31)<>5 THEN GOTO MACROEXPAND_DONE\n    REM defined in environment?\n    B$=S$(Z%(B+1)):CALL ENV_GET\n    IF R3=0 THEN GOTO MACROEXPAND_DONE\n    B=R\n    REM macro?\n    IF (Z%(B)AND 31)<>11 THEN GOTO MACROEXPAND_DONE\n\n    GOSUB INC_REF_R\n    F=B:AR=Z%(A+1):CALL APPLY\n    A=R\n\n    GOSUB PEEK_Q:AY=Q\n    REM if previous A was not the first A into macroexpand (i.e. an\n    REM intermediate form) then free it\n    IF A<>AY THEN GOSUB PEND_A_LV\n\n    IF ER<>-2 THEN GOTO MACROEXPAND_DONE\n    GOTO MACROEXPAND_LOOP\n\n  MACROEXPAND_DONE:\n    GOSUB POP_Q: REM pop original A\nEND SUB\n\nREM EVAL_AST(A, E) -> R\nSUB EVAL_AST\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  IF ER<>-2 THEN GOTO EVAL_AST_RETURN\n\n  GOSUB TYPE_A\n  IF T<6 OR 8<T THEN R=-1:ER=-1:E$=\"EVAL_AST: bad type\":GOTO EVAL_AST_RETURN\n\n    REM setup the stack for the loop\n    GOSUB MAP_LOOP_START\n\n    EVAL_AST_SEQ_LOOP:\n      REM check if we are done evaluating the source sequence\n      IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM if we are returning to DO, then skip last element\n      REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to\n      REM return early and for TCO to work\n      Q=5:GOSUB PEEK_Q_Q\n      IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM call EVAL for each entry\n      GOSUB PUSH_A\n      IF T<>8 THEN A=Z%(A+2)\n      IF T=8 THEN A=Z%(A+3)\n      Q=T:GOSUB PUSH_Q: REM push/save type\n      CALL EVAL\n      GOSUB POP_Q:T=Q: REM pop/restore type\n      GOSUB POP_A\n      M=R\n\n      REM if error, release the unattached element\n      REM TODO: is R=0 correct?\n      IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE\n\n      REM for hash-maps, copy the key (inc ref since we are going to\n      REM release it below)\n      IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32\n\n      REM update the return sequence structure\n      REM release N (and M if T=8) since seq takes full ownership\n      C=1:GOSUB MAP_LOOP_UPDATE\n\n      REM process the next sequence entry from source list\n      A=Z%(A+1)\n\n      GOTO EVAL_AST_SEQ_LOOP\n    EVAL_AST_SEQ_LOOP_DONE:\n      REM cleanup stack and get return value\n      GOSUB MAP_LOOP_DONE\n      GOTO EVAL_AST_RETURN\n\n  EVAL_AST_RETURN:\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\nEND SUB\n\nREM EVAL(A, E) -> R\nSUB EVAL\n  LV=LV+1: REM track basic return stack level\n\n  REM push A and E on the stack\n  Q=E:GOSUB PUSH_Q\n  GOSUB PUSH_A\n\n  REM PRINT \"EVAL A:\"+STR$(A)+\",X:\"+STR$(X)+\",LV:\"+STR$(LV)+\",FRE:\"+STR$(FRE(0))\n\n  EVAL_TCO_RECUR:\n\n  IF ER<>-2 THEN GOTO EVAL_RETURN\n\n  EVAL_NOT_LIST:\n\n  B$=\"DEBUG-EVAL\":CALL ENV_GET\n  IF R3=0 OR R=0 OR R=2 THEN GOTO DEBUG_EVAL_DONE\n    AZ=A:B=1:GOSUB PR_STR\n    PRINT \"EVAL: \"+R$+\" [A:\"+STR$(A)+\", LV:\"+STR$(LV)+\"]\"\n  DEBUG_EVAL_DONE:\n\n  GOSUB TYPE_A\n  T=T-4\n  IF 0<T THEN ON T GOTO EVAL_SYMBOL,APPLY_LIST,EVAL_VECTOR,EVAL_MAP\n\n  REM ELSE\n    R=A\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_SYMBOL:\n    B$=S$(Z%(A+1)):CALL ENV_GET\n    IF R3=0 THEN R=-1:ER=-1:E$=\"'\"+B$+\"' not found\":GOTO EVAL_RETURN\n    GOSUB INC_REF_R\n    GOTO EVAL_RETURN\n\n  EVAL_MAP:\n  EVAL_VECTOR:\n    CALL EVAL_AST\n    GOTO EVAL_RETURN\n\n  APPLY_LIST:\n    CALL MACROEXPAND\n\n    GOSUB LIST_Q\n    IF R<>1 THEN GOTO EVAL_NOT_LIST\n\n    GOSUB EMPTY_Q\n    IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN\n\n    A0=Z%(A+2)\n\n    REM get symbol in A$\n    IF (Z%(A0)AND 31)<>5 THEN A$=\"\"\n    IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))\n\n    IF A$=\"def!\" THEN GOTO EVAL_DEF\n    IF A$=\"let*\" THEN GOTO EVAL_LET\n    IF A$=\"quote\" THEN GOTO EVAL_QUOTE\n    IF A$=\"quasiquote\" THEN GOTO EVAL_QUASIQUOTE\n    IF A$=\"defmacro!\" THEN GOTO EVAL_DEFMACRO\n    IF A$=\"try*\" THEN GOTO EVAL_TRY\n    IF A$=\"do\" THEN GOTO EVAL_DO\n    IF A$=\"if\" THEN GOTO EVAL_IF\n    IF A$=\"fn*\" THEN GOTO EVAL_FN\n    GOTO EVAL_INVOKE\n\n    EVAL_GET_A3:\n      A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)\n    EVAL_GET_A2:\n      A2=Z%(Z%(Z%(A+1)+1)+2)\n    EVAL_GET_A1:\n      A1=Z%(Z%(A+1)+2)\n      RETURN\n\n    EVAL_DEF:\n      REM PRINT \"def!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q\n      A=A2:CALL EVAL: REM eval a2\n      GOSUB POP_Q:A1=Q\n\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM set a1 in env to a2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_LET:\n      REM PRINT \"let*\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A2:GOSUB PUSH_Q: REM push/save A2\n      Q=E:GOSUB PUSH_Q: REM push env for for later release\n\n      REM create new environment with outer as current environment\n      C=E:GOSUB ENV_NEW\n      E=R\n      EVAL_LET_LOOP:\n        IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE\n\n        Q=A1:GOSUB PUSH_Q: REM push A1\n        REM eval current A1 odd element\n        A=Z%(Z%(A1+1)+2):CALL EVAL\n        GOSUB POP_Q:A1=Q: REM pop A1\n\n        IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE\n\n        REM set key/value in the environment\n        K=Z%(A1+2):C=R:GOSUB ENV_SET\n        AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership\n\n        REM skip to the next pair of A1 elements\n        A1=Z%(Z%(A1+1)+1)\n        GOTO EVAL_LET_LOOP\n\n      EVAL_LET_LOOP_DONE:\n        GOSUB POP_Q:AY=Q: REM pop previous env\n\n        REM release previous environment if not the current EVAL env\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        GOSUB POP_Q:A2=Q: REM pop A2\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DO:\n      A=Z%(A+1): REM rest\n      GOSUB PUSH_A: REM push/save A\n\n      REM this must be EVAL_AST call #2 for EVAL_AST to return early\n      REM and for TCO to work\n      CALL EVAL_AST\n\n      REM cleanup\n      AY=R: REM get eval'd list for release\n\n      GOSUB POP_A: REM pop/restore original A for LAST\n      GOSUB LAST: REM get last element for return\n      A=R: REM new recur AST\n\n      REM cleanup\n      GOSUB RELEASE: REM release eval'd list\n      AY=A:GOSUB RELEASE: REM release LAST value (not sure why)\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_QUOTE:\n      R=Z%(Z%(A+1)+2)\n      GOSUB INC_REF_R\n      GOTO EVAL_RETURN\n\n    EVAL_QUASIQUOTE:\n      R=Z%(Z%(A+1)+2)\n      A=R:CALL QUASIQUOTE\n      A=R\n      REM add quasiquote result to pending release queue to free when\n      REM next lower EVAL level returns (LV)\n      GOSUB PEND_A_LV\n\n      GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_DEFMACRO:\n      REM PRINT \"defmacro!\"\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      Q=A1:GOSUB PUSH_Q: REM push A1\n      A=A2:CALL EVAL: REM eval A2\n      GOSUB POP_Q:A1=Q: REM pop A1\n\n      REM change function to macro\n      Z%(R)=Z%(R)+1\n\n      REM set A1 in env to A2\n      K=A1:C=R:GOSUB ENV_SET\n      GOTO EVAL_RETURN\n\n    EVAL_TRY:\n      REM PRINT \"try*\"\n      GOSUB EVAL_GET_A1: REM set A1\n\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL: REM eval A1\n      GOSUB POP_A: REM pop/restore A\n\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n\n      REM if there is no error or catch block then return\n      IF ER=-2 OR A2=0 THEN GOTO EVAL_RETURN\n\n      REM create environment for the catch block eval\n      C=E:GOSUB ENV_NEW:E=R\n\n      A=A2:GOSUB EVAL_GET_A2: REM set A1 and A2 from catch block\n\n      REM create object for ER=-1 type raw string errors\n      IF ER=-1 THEN B$=E$:T=4:GOSUB STRING:ER=R:GOSUB INC_REF_R\n\n      REM bind the catch symbol to the error object\n      K=A1:C=ER:GOSUB ENV_SET\n      AY=R:GOSUB RELEASE: REM release our use, env took ownership\n\n      REM unset error for catch eval\n      ER=-2:E$=\"\"\n\n      A=A2:CALL EVAL\n\n      GOTO EVAL_RETURN\n\n    EVAL_IF:\n      GOSUB EVAL_GET_A1: REM set A1\n      GOSUB PUSH_A: REM push/save A\n      A=A1:CALL EVAL\n      GOSUB POP_A: REM pop/restore A\n      IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE\n\n      EVAL_IF_TRUE:\n        AY=R:GOSUB RELEASE\n        GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL\n        A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop\n      EVAL_IF_FALSE:\n        AY=R:GOSUB RELEASE\n        REM if no false case (A3), return nil\n        GOSUB COUNT\n        IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN\n        GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL\n        A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n    EVAL_FN:\n      GOSUB EVAL_GET_A2: REM set A1 and A2\n      T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function\n      GOTO EVAL_RETURN\n\n    EVAL_INVOKE:\n      CALL EVAL_AST\n\n      REM if error, return f/args for release by caller\n      IF ER<>-2 THEN GOTO EVAL_RETURN\n\n      REM push f/args for release after call\n      GOSUB PUSH_R\n\n      AR=Z%(R+1): REM rest\n      F=Z%(R+2)\n\n      REM if metadata, get the actual object\n      GOSUB TYPE_F\n      IF T=14 THEN F=Z%(F+1):GOSUB TYPE_F\n      T=T-8\n      IF 0<T THEN ON T GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION\n\n      REM if error, pop and return f/args for release by caller\n      GOSUB POP_R\n      ER=-1:E$=\"apply of non-function\":GOTO EVAL_RETURN\n\n      EVAL_DO_FUNCTION:\n        REM regular function\n        IF Z%(F+1)<65 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP\n        REM for recur functions (apply, map, swap!), use GOTO\n        IF Z%(F+1)>64 THEN CALL DO_TCO_FUNCTION\n        EVAL_DO_FUNCTION_SKIP:\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n        GOTO EVAL_RETURN\n\n      EVAL_DO_MAL_FUNCTION:\n        Q=E:GOSUB PUSH_Q: REM save the current environment for release\n\n        REM create new environ using env and params stored in function\n        C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS\n\n        REM release previous env if it is not the top one on the\n        REM stack (X%(X-2)) because our new env refers to it and\n        REM we no longer need to track it (since we are TCO recurring)\n        GOSUB POP_Q:AY=Q\n        GOSUB PEEK_Q_2\n        IF AY<>Q THEN GOSUB RELEASE\n\n        REM claim the AST before releasing the list containing it\n        A=Z%(F+1):Z%(A)=Z%(A)+32\n        REM add AST to pending release queue to free as soon as EVAL\n        REM actually returns (LV+1)\n        LV=LV+1:GOSUB PEND_A_LV:LV=LV-1\n\n        REM pop and release f/args\n        GOSUB POP_Q:AY=Q\n        GOSUB RELEASE\n\n        REM A set above\n        E=R:GOTO EVAL_TCO_RECUR: REM TCO loop\n\n  EVAL_RETURN:\n    REM AZ=R: B=1: GOSUB PR_STR\n    REM PRINT \"EVAL_RETURN R: [\"+R$+\"] (\"+STR$(R)+\"), LV:\"+STR$(LV)+\",ER:\"+STR$(ER)\n\n    REM release environment if not the top one on the stack\n    GOSUB PEEK_Q_1\n    IF E<>Q THEN AY=E:GOSUB RELEASE\n\n    LV=LV-1: REM track basic return stack level\n\n    REM release everything we couldn't release earlier\n    GOSUB RELEASE_PEND\n\n    REM trigger GC\n    #cbm T=FRE(0)\n    #qbasic T=0\n\n    REM pop A and E off the stack\n    GOSUB POP_A\n    GOSUB POP_Q:E=Q\n\nEND SUB\n\nREM PRINT is inlined in REP\n\nREM RE(A$) -> R\nREM Assume D has repl_env\nREM caller must release result\nRE:\n  R1=-1\n  GOSUB READ_STR: REM inlined READ\n  R1=R\n  IF ER<>-2 THEN GOTO RE_DONE\n\n  A=R:E=D:CALL EVAL\n\n  RE_DONE:\n    REM Release memory from READ\n    AY=R1:GOSUB RELEASE\n    RETURN: REM caller must release result of EVAL\n\nREM REP(A$) -> R$\nREM Assume D has repl_env\nSUB REP\n  R2=-1\n\n  GOSUB RE\n  R2=R\n  IF ER<>-2 THEN GOTO REP_DONE\n\n  AZ=R:B=1:GOSUB PR_STR: REM inlined PRINT\n\n  REP_DONE:\n    REM Release memory from EVAL\n    AY=R2:GOSUB RELEASE\nEND SUB\n\nREM MAIN program\nMAIN:\n  GOSUB INIT_MEMORY\n\n  LV=0\n\n  REM create repl_env\n  C=0:GOSUB ENV_NEW:D=R\n\n  REM core.EXT: defined in Basic\n  E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env\n\n  ZT=ZI: REM top of memory after base repl_env\n\n  REM core.mal: defined using the language itself\n  #cbm A$=\"(def! *host-language* \"+CHR$(34)+\"C64 BASIC\"+CHR$(34)+\")\"\n  #qbasic A$=\"(def! *host-language* \"+CHR$(34)+\"QBasic\"+CHR$(34)+\")\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(def! not (fn* (a) (if a false true)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(def! load-file (fn* (f) (do (eval (read-file f)) nil)))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  A$=\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)\"\n  A$=A$+\" (if (> (count xs) 1) (nth xs 1) (throw \"+CHR$(34)+\"odd number of\"\n  A$=A$+\" forms to cond\"+CHR$(34)+\")) (cons 'cond (rest (rest xs)))))))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM load the args file\n  A$=\"(load-file \"+CHR$(34)+\".args.mal\"+CHR$(34)+\")\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  IF ER>-2 THEN GOSUB PRINT_ERROR:END\n\n  REM set the argument list\n  A$=\"(def! *ARGV* (rest -*ARGS*-))\"\n  GOSUB RE:AY=R:GOSUB RELEASE\n\n  REM get the first argument\n  A$=\"(first -*ARGS*-)\"\n  GOSUB RE\n\n  REM no arguments, start REPL loop\n  REM if there is an argument, then run it as a program\n  IF 15<R THEN GOTO RUN_PROG\n\n  REM print the REPL startup header\n  REM save memory by printing this directly\n  #cbm PRINT \"Mal [C64 BASIC]\"\n  #qbasic PRINT \"Mal [QBasic]\"\n\n  REPL_LOOP:\n    A$=\"user> \":GOSUB READLINE: REM call input parser\n    IF EZ=1 THEN GOTO QUIT\n    IF R$=\"\" THEN GOTO REPL_LOOP\n\n    A$=R$:CALL REP\n\n    IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP\n    PRINT R$\n    GOTO REPL_LOOP\n\n  RUN_PROG:\n    REM free up first arg because we get it again\n    AY=R:GOSUB RELEASE\n    REM run a single mal program and exit\n    A$=\"(load-file (first -*ARGS*-))\"\n    GOSUB RE\n    IF ER<>-2 THEN GOSUB PRINT_ERROR\n\n  QUIT:\n    REM GOSUB PR_MEMORY_SUMMARY_SMALL\n    REM GOSUB PR_MEMORY_MAP\n    REM P1=0:P2=ZI:GOSUB PR_MEMORY\n    REM P1=D:GOSUB PR_OBJECT\n    REM P1=ZK:GOSUB PR_OBJECT\n    #cbm END\n    #qbasic SYSTEM\n\n  PRINT_ERROR:\n    REM if the error is an object, then print and free it\n    IF ER>=0 THEN AZ=ER:B=0:GOSUB PR_STR:E$=R$:AY=ER:GOSUB RELEASE\n    PRINT \"Error: \"+E$\n    ER=-2:E$=\"\"\n    RETURN\n"
  },
  {
    "path": "impls/basic/types.in.bas",
    "content": "REM general functions\n\nREM TYPE_A(A) -> T\nTYPE_A:\n  T=Z%(A)AND 31\n  RETURN\n\nREM TYPE_F(F) -> T\nTYPE_F:\n  T=Z%(F)AND 31\n  RETURN\n\nREM EQUAL_Q(A, B) -> R\nEQUAL_Q:\n  ED=0: REM recursion depth\n  R=-1: REM return value\n\n  EQUAL_Q_RECUR:\n\n  REM push A and B\n  GOSUB PUSH_A\n  Q=B:GOSUB PUSH_Q\n  ED=ED+1\n\n  GOSUB TYPE_A\n  T2=Z%(B)AND 31\n  IF T>5 AND T<8 AND T2>5 AND T2<8 THEN GOTO EQUAL_Q_SEQ\n  IF T=8 AND T2=8 THEN GOTO EQUAL_Q_HM\n\n  IF T<>T2 OR Z%(A+1)<>Z%(B+1) THEN R=0\n  GOTO EQUAL_Q_DONE\n\n  EQUAL_Q_SEQ:\n    IF Z%(A+1)=0 AND Z%(B+1)=0 THEN GOTO EQUAL_Q_DONE\n    IF Z%(A+1)=0 OR Z%(B+1)=0 THEN R=0:GOTO EQUAL_Q_DONE\n\n    REM compare the elements\n    A=Z%(A+2):B=Z%(B+2)\n    GOTO EQUAL_Q_RECUR\n\n  EQUAL_Q_SEQ_CONTINUE:\n    REM next elements of the sequences\n    GOSUB PEEK_Q_1:A=Q\n    GOSUB PEEK_Q:B=Q\n    A=Z%(A+1):B=Z%(B+1)\n    Q=A:GOSUB PUT_Q_1\n    Q=B:GOSUB PUT_Q\n    GOTO EQUAL_Q_SEQ\n\n  EQUAL_Q_HM:\n    R=0\n    GOTO EQUAL_Q_DONE\n\n  EQUAL_Q_DONE:\n    REM pop current A and B\n    GOSUB POP_Q\n    GOSUB POP_Q\n    ED=ED-1\n    IF R>-1 AND ED>0 THEN GOTO EQUAL_Q_DONE: REM unwind\n    IF ED=0 AND R=-1 THEN R=1\n    IF ED=0 THEN RETURN\n    GOTO EQUAL_Q_SEQ_CONTINUE\n\nREM string functions\n\nREM STRING(B$, T) -> R\nREM intern string and allocate reference (return Z% index)\nSTRING:\n  IF S=0 THEN GOTO STRING_NOT_FOUND\n\n  REM search for matching string in S$\n  I=0\n  STRING_FIND_LOOP:\n    IF I>S-1 THEN GOTO STRING_NOT_FOUND\n    IF S%(I)>0 AND B$=S$(I) THEN GOTO STRING_DONE\n    I=I+1\n    GOTO STRING_FIND_LOOP\n\n  STRING_NOT_FOUND:\n    I=S-1\n    STRING_FIND_GAP_LOOP:\n      REM TODO: don't search core function names (store position)\n      IF I=-1 THEN GOTO STRING_NEW\n      IF S%(I)=0 THEN GOTO STRING_SET\n      I=I-1\n      GOTO STRING_FIND_GAP_LOOP\n\n  STRING_NEW:\n    I=S\n    S=S+1\n    REM fallthrough\n\n  STRING_SET:\n    S$(I)=B$\n    REM fallthrough\n\n  STRING_DONE:\n    S%(I)=S%(I)+1\n    L=I:GOSUB ALLOC\n    RETURN\n\nREM REPLACE(R$, S1$, S2$) -> R$\nREPLACE:\n  T3$=R$\n  R$=\"\"\n  I=1\n  J=LEN(T3$)\n  REPLACE_LOOP:\n    IF I>J THEN RETURN\n    C$=MID$(T3$,I,LEN(S1$))\n    IF C$=S1$ THEN R$=R$+S2$:I=I+LEN(S1$)\n    IF C$<>S1$ THEN R$=R$+MID$(T3$,I,1):I=I+1\n    GOTO REPLACE_LOOP\n\n\nREM sequence functions\n\nREM FORCE_SEQ_TYPE(A,T) -> R\nFORCE_SEQ_TYPE:\n  REM if it's already the right type, inc ref cnt and return it\n  IF (Z%(A)AND 31)=T THEN R=A:GOTO INC_REF_R\n  REM if it's empty, return the empty sequence match T\n  IF A<16 THEN R=(T-4)*3:GOTO INC_REF_R\n  REM otherwise, copy first element to turn it into correct type\n  B=Z%(A+2): REM value to copy\n  L=Z%(A+1):M=B:GOSUB ALLOC: REM T already set\n  IF Z%(A+1)=0 THEN RETURN\n  RETURN\n\nREM MAP_LOOP_START(T):\nREM   - setup stack for map loop\nMAP_LOOP_START:\n  REM point to empty sequence to start off\n  R=(T-4)*3: REM calculate location of empty seq\n\n  GOSUB PUSH_R: REM push return ptr\n  GOSUB PUSH_R: REM push empty ptr\n  GOSUB PUSH_R: REM push current ptr\n  GOTO INC_REF_R\n\nREM MAP_LOOP_UPDATE(C,M):\nREM MAP_LOOP_UPDATE(C,M,N):\nREM  - called after M (and N if T=8) are set\nREM  - C indicates whether to free M (and N if T=8)\nREM  - update the structure of the return sequence\nMAP_LOOP_UPDATE:\n  GOSUB PEEK_Q_1:L=Q: REM empty ptr\n\n  GOSUB ALLOC: REM allocate new sequence element\n\n  REM sequence took ownership\n  AY=L:GOSUB RELEASE\n  IF C THEN AY=M:GOSUB RELEASE\n  IF C AND T=8 THEN AY=N:GOSUB RELEASE\n\n  REM if not first element, set current next to point to new element\n  GOSUB PEEK_Q\n  IF Q>14 THEN Z%(Q+1)=R\n  REM if first element, set return to new element\n  IF Q<15 THEN Q=R:GOSUB PUT_Q_2\n  Q=R:GOSUB PUT_Q: REM update current ptr to new element\n\n  RETURN\n\nREM MAP_LOOP_DONE() -> R\nREM   - cleanup stack and set return value\nMAP_LOOP_DONE:\n  GOSUB POP_Q: REM pop current ptr\n  GOSUB POP_Q: REM pop empty ptr\n  GOSUB POP_R: REM pop return ptr\n  RETURN\n\n\nREM LIST_Q(A) -> R\nLIST_Q:\n  R=0\n  GOSUB TYPE_A\n  IF T=6 THEN R=1\n  RETURN\n\nREM EMPTY_Q(A) -> R\nEMPTY_Q:\n  R=0\n  IF Z%(A+1)=0 THEN R=1\n  RETURN\n\nREM COUNT(A) -> R\nREM - returns length of list, not a Z% index\nCOUNT:\n  GOSUB PUSH_A\n  R=-1\n  DO_COUNT_LOOP:\n    R=R+1\n    IF Z%(A+1)<>0 THEN A=Z%(A+1):GOTO DO_COUNT_LOOP\n  GOSUB POP_A\n  RETURN\n\nREM LAST(A) -> R\nLAST:\n  REM TODO check that actually a list/vector\n  IF Z%(A+1)=0 THEN R=0:RETURN: REM empty seq, return nil\n  W=0\n  LAST_LOOP:\n    IF Z%(A+1)=0 THEN GOTO LAST_DONE: REM end, return previous value\n    W=A: REM current becomes previous entry\n    A=Z%(A+1): REM next entry\n    GOTO LAST_LOOP\n  LAST_DONE:\n    R=Z%(W+2)\n    GOTO INC_REF_R\n\nREM SLICE(A,B,C) -> R\nREM make copy of sequence A from index B to C\nREM returns R6 as reference to last element of slice before empty\nREM returns A as next element following slice (of original)\nSLICE:\n  I=0\n  R=6: REM always a list\n  GOSUB INC_REF_R\n  R6=-1: REM last list element before empty\n  W=R: REM temporary for return as R\n  REM advance A to position B\n  SLICE_FIND_B:\n    IF I<B AND Z%(A+1)<>0 THEN A=Z%(A+1):I=I+1:GOTO SLICE_FIND_B\n  SLICE_LOOP:\n    REM if current position is C, then return\n    IF C<>-1 AND I>=C THEN R=W:RETURN\n    REM if we reached end of A, then return\n    IF Z%(A+1)=0 THEN R=W:RETURN\n    REM allocate new list element with copied value\n    T=6:L=6:M=Z%(A+2):GOSUB ALLOC\n    REM sequence took ownership\n    AY=L:GOSUB RELEASE\n    REM if not first element, set last to point to new element\n    IF R6>-1 THEN Z%(R6+1)=R\n    REM if first element, set return value to new element\n    IF R6=-1 THEN W=R\n    R6=R: REM update last list element\n    REM advance to next element of A\n    A=Z%(A+1)\n    I=I+1\n    GOTO SLICE_LOOP\n\nREM LIST2(B,A) -> R\nLIST2:\n  REM last element is 3 (empty list), second element is A\n  T=6:L=6:M=A:GOSUB ALLOC\n\n  REM first element is B\n  T=6:L=R:M=B:GOSUB ALLOC\n  AY=L:GOSUB RELEASE: REM new list takes ownership of previous\n\n  RETURN\n\nREM LIST3(C,B,A) -> R\nLIST3:\n  GOSUB LIST2\n\n  REM first element is C\n  T=6:L=R:M=C:GOSUB ALLOC\n  AY=L:GOSUB RELEASE: REM new list takes ownership of previous\n\n  RETURN\n\n\nREM hashmap functions\n\nREM HASHMAP() -> R\nHASHMAP:\n  REM just point to static empty hash-map\n  R=12\n  GOTO INC_REF_R\n\nREM ASSOC1(H, K, C) -> R\nASSOC1:\n  REM create key/value entry\n  T=8:L=H:M=K:N=C:GOSUB ALLOC\n  AY=L:GOSUB RELEASE: REM we took ownership of previous hashmap\n  RETURN\n\nREM ASSOC1_S(H, B$, C) -> R\nASSOC1_S:\n  REM add the key string\n  T=4:GOSUB STRING\n  K=R:GOSUB ASSOC1\n  AY=K:GOSUB RELEASE: REM map took ownership of key\n  RETURN\n\nREM HASHMAP_GET(H, B$) -> R\nREM   - returns R3 with whether we found it or not\nHASHMAP_GET:\n  R3=0: REM whether found or not (for HASHMAP_CONTAINS)\n  R=0\n  HASHMAP_GET_LOOP:\n    REM no matching key found\n    IF Z%(H+1)=0 THEN R=0:RETURN\n    REM get search string is equal to key string we found it\n    IF B$=S$(Z%(Z%(H+2)+1)) THEN R3=1:R=Z%(H+3):RETURN\n    REM skip to next key/value\n    H=Z%(H+1)\n    GOTO HASHMAP_GET_LOOP\n\nREM HASHMAP_CONTAINS(H, K) -> R\nHASHMAP_CONTAINS:\n  GOSUB HASHMAP_GET\n  R=R3\n  RETURN\n\n"
  },
  {
    "path": "impls/basic/variables.txt",
    "content": "Global Unique:\n\nZ%  : boxed memory values\nZ1  : Z% size\nZ2  : S$ size\nZ3  : stack start address (cbm) or X% size (qbasic)\nZ4  : release stack start address (cbm) or Y% size (qbasic)\nZI  : start of unused memory (index into Z%)\nZK  : start of free list (index into Z%)\nZT  : top of memory after repl env allocations\n\nS$  : string memory storage\nS   : next free index in S$\n\nX%  : logic/call stack (Z% indexes)\nX   : top element of X% stack\n\nY%  : pending release stack [index into Z%, eval level]\nY   : top element of Y% stack\n\nD   : root repl environment\n\nBT  : begin time (TI)\nER  : error type (-2: none, -1: string, >=0: object)\nE$  : error string (ER=-1)\nEZ  : READLINE EOF return, READ_FILE EOF temp\n\nLV  : EVAL stack call level/depth\n\nRI  : reader current string position\nRJ  : READ_TOKEN current character index\n\n\nCalling arguments/temporaries:\n\nA   : common call argument (especially EVAL, EVAL_AST)\nA$  : common call argument (READLINE, reader, string temp, key value)\nB   : common call argument\nB$  : STRING arg for HASHMAP_GET, PR_STR_SEQ separator\n    : INIT_CORE_SET_FUNCTION, ENV_SET_S, ASSOC1_S\nC   : common call argument, DO_TCO_FUNCTION temp in DO_APPLY\nE   : environment (EVAL, EVAL_AST)\nF   : function\nH   : hash map\nK   : hash map key (Z% index)\nL   : ALLOC* Z%(R,1) default\nM   : ALLOC* Z%(R+1,0) default\nN   : ALLOC* Z%(R+1,1) default\nR   : common return value\nR$  : common string return value\nT   : type arg, common temp\nQ   : PUSH*, POP*, PEEK* return value (and PEEK_Q_Q call arg)\n\nAR  : APPLY, DO_*_FUNCTION arg list\nAY  : RELEASE/FREE arg\nAZ  : PR_STR arg\nP1  : PR_MEMORY*, PR_OBJECT, CHECK_FREE_LIST start\nP2  : PR_MEMORY*, PR_OBJECT, CHECK_FREE_LIST end\nP3  : PR_OBJECT, PR_MEMORY_VALUE\nR1  : REP, RE - MAL_READ result temp\nR2  : REP, RE - EVAL result temp\nR3  : HASHMAP_GET, DO_HASH_MAP, DO_KEYS_VALS temp and return value\nR6  : SLICE return value (last element)\nSZ  : size argument to ALLOC\nS1$ : REPLACE needle\nS2$ : REPLACE replacement\n\n\nOther temporaries:\n\nA0  : EVAL ast elements\nA1  : EVAL ast elements, DO_FUNCTION temp\nA2  : EVAL ast elements, DO_FUNCTION temp\nA3  : EVAL ast elements\nB1  : DO_FUNCTION temp\n\nCZ  : DO_CONCAT stack position\nED  : EQUAL_Q recursion depth counter\nRD  : PR_OBJECT recursion depth\nSD  : READ_STR sequence read recursion depth\n\nC$  : READ_TOKEN, SKIP_SPACES, SKIP_TO_EOL current character\nD$  : READ_TOKEN/READ_FILE_CHAR temp\nG   : function value ON GOTO switch flag, EVAL_AST changed flag\nI   : STRING, REPLACE, SLICE, PR_MEMORY, PR_OBJECT, PR_MEMORY_VALUE\nJ   : REPLACE, PR_MEMORY_VALUE\nU   : ALLOC, RELEASE, PR_STR temp\nV   : RELEASE, PR_STR_SEQ temp\nW   : SLICE, LAST, QUASIQUOTE, DO_HASH_MAP, DO_KEYS_VALS, step2-3 EVAL temp\nP   : PR_MEMORY_SUMMARY_SMALL\nRC  : RELEASE remaining number of elements to release\nRF  : reader reading from file flag\nS1  : READ_TOKEN in a string?\nS2  : READ_TOKEN escaped?\nT$  : READ_* current token string\nT1  : EQUAL_Q, PR_STR, DO_KEYS_VALS temp\nT2  : EQUAL_Q, DO_KEY_VALS, HASH_MAP_GET\nT3$ : REPLACE temp\n\n\nUnused:\n\nO\n\n\nCounting number of times each variable is assigned:\n\tsed 's/:/\\n /g' readline.in.bas types.in.bas reader.in.bas printer.in.bas env.in.bas core.in.bas stepA_mal.in.bas | grep \"[A-Z][A-Z0-9]*[%$]*=\" | sed 's/.*[^A-Z]\\([A-Z][A-Z0-9]*[%$]*\\)=.*/\\1/g' | sort | uniq -c | sort -n\n\n"
  },
  {
    "path": "impls/bbc-basic/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install brandy\n"
  },
  {
    "path": "impls/bbc-basic/Makefile",
    "content": "all:\n\n.PHONY: clean\n\nclean:\n"
  },
  {
    "path": "impls/bbc-basic/README.md",
    "content": "# Introduction\n\nThis is an implementation of mal in BBC BASIC V.  While there\nis already an implementation of mal in BASIC (in the \"basic\"\ndirectory), it's targeted at much more primitive versions of BASIC and\nrelies on a pre-processor, both of which make it fairly un-idiomatic\nas a BBC BASIC V program.\n\nBBC BASIC V is the version of BBC BASIC supplied with Acorn's\nARM-based computers from the mid-1980s.  It has substantial\nenhancements from the 6502-based versions of BBC BASIC, which were\nthemselves at the advanced end of 8-bit BASICs.  Mal uses many of the\nadvanced features of BBC BASIC V and porting it to older versions\nwould be difficult.\n\nMal is intended to run on all versions of BBC BASIC V and BBC BASIC\nVI, as well as on Brandy 1.20.1.  For compatibility with Brandy, it\navoids operating system calls where possible.  The only exception\nis that is has separate mechanisms for reading command-line arguments\nunder Brandy and RISC OS.\n\n# Running under Unix\n\nOn Unix systems, this mal implementation can run on the Brandy\ninterpreter.  The tests require the \"simple text\" build, but mal will\nwork interactively in graphical builds as well.  You can invoke mal\nlike this:\n\n```\ncd bbc-basic\nbrandy stepA_mal.bbc\n```\n\n# Running under RISC OS\n\nTo run mal under RISC OS, you obviously need to get the files onto\nyour RISC OS system, and you also need to arrange to tokenize the\nBASIC source files.  There are scripts to do the latter in the\n`riscos` directory, but they do require that the mal source tree be\navailable under RISC OS without its filenames' being truncated, which\nmay restrict with filing systems can be used.  The HostFS supplied\nwith ArcEm works fine.\n\nOnce you have the files in RISC OS, you can set things up by running:\n\n```\n*Dir bbc-basic.riscos\n*Run setup\n```\n\nThen you can invoke the interpreter directly:\n\n```\n*Run stepA_mal\n```\n\nAt present, there's no filename translation in the `slurp` function,\nso many of the example mal programs will fail because they can't load\n`core.mal`.\n\n# Interesting features\n\nThis appears to be the first mal implementation that uses an table-driven\ndeterministic finite automoton (a state machine) to implement its\ntokenizer.\n\nThe mal heap is represented as a large array of fixed-size objects.\nLists and vectors are linked lists of these objects, while hash-maps\nare crit-bit trees.\n\nMal exceptions are implemented as BBC BASIC errors.  Errors generated\nby mal are numbered from &40E80900.\n\n## Assigned error numbers\n\nNo.| Description\n---|------------\n&00| Native mal error generated by 'throw'\n&1x| Object not of type 'x'\n&1F| Miscellaneous type mismatch\n&20| Invalid operation on empty list\n&21| Wrong number of arguments to function\n&22| Undefined symbol\n&23| Subscript out of range\n&24| Invalid 'catch*' clause\n&30| Unexpected end of input\n&31| Unexpected ')'\n&32| Hash-map key mush be a string\n&40| File not found\n&50| Out of memory\n&Fx| Internal errors (indicating a bug in mal)\n&F0| Unprintable value\n&F1| Call to non-existent core function\n"
  },
  {
    "path": "impls/bbc-basic/core.bas",
    "content": "REM > core function library for mal in BBC BASIC\n\nREM  BBC BASIC doesn't have function pointers.  There are essentially\nREM  two ways to work around this.  One is to use the BASIC EVAL function,\nREM  constructing a string that will call an arbitrary function with the\nREM  specified arguments.  The other is to us a big CASE statement.\nREM  Following the suggestion in Hints.md, this code takes the latter\nREM  approach.\n\nDEF PROCcore_ns\n  RESTORE +0\n  REM  The actual DATA statements are embedded in the dispatch table below.\nENDPROC\n\nREM  Call a core function, taking the function number and a mal list of\nREM  objects to pass as arguments.\nDEF FNcore_call(fn%, args%)\n  LOCAL args%(), arg$\n  DIM args%(1)\n  CASE fn% OF\n    DATA +, 0\n    WHEN 0\n      PROCcore_prepare_args(\"ii\", \"+\")\n      =FNalloc_int(args%(0) + args%(1))\n    DATA -, 1\n    WHEN 1\n      PROCcore_prepare_args(\"ii\", \"-\")\n      =FNalloc_int(args%(0) - args%(1))\n    DATA *, 2\n    WHEN 2\n      PROCcore_prepare_args(\"ii\", \"*\")\n      =FNalloc_int(args%(0) * args%(1))\n    DATA /, 3\n    WHEN 3\n      PROCcore_prepare_args(\"ii\", \"/\")\n      =FNalloc_int(args%(0) DIV args%(1))\n    DATA list, 5\n    WHEN 5\n      =FNas_list(args%)\n    DATA list?, 6\n    WHEN 6\n      PROCcore_prepare_args(\"?\", \"list?\")\n      =FNalloc_boolean(FNis_list(args%(0)))\n    DATA empty?, 7\n    WHEN 7\n      PROCcore_prepare_args(\"l\", \"empty?\")\n      =FNalloc_boolean(FNis_empty(args%(0)))\n    DATA count, 8\n    WHEN 8\n      PROCcore_prepare_args(\"C\", \"count\")\n      IF FNis_nil(args%(0)) THEN =FNalloc_int(0)\n      =FNalloc_int(FNcount(args%(0)))\n    DATA =, 9\n    WHEN 9\n      PROCcore_prepare_args(\"??\", \"=\")\n      =FNalloc_boolean(FNcore_equal(args%(0), args%(1)))\n    DATA <, 10\n    WHEN 10\n      PROCcore_prepare_args(\"ii\", \"<\")\n      =FNalloc_boolean(args%(0) < args%(1))\n    DATA <=, 11\n    WHEN 11\n      PROCcore_prepare_args(\"ii\", \"<=\")\n      =FNalloc_boolean(args%(0) <= args%(1))\n    DATA >, 12\n    WHEN 12\n      PROCcore_prepare_args(\"ii\", \">\")\n      =FNalloc_boolean(args%(0) > args%(1))\n    DATA >=, 13\n    WHEN 13\n      PROCcore_prepare_args(\"ii\", \">=\")\n      =FNalloc_boolean(args%(0) >= args%(1))\n    DATA read-string, 14\n    WHEN 14\n      PROCcore_prepare_args(\"t\", \"read-string\")\n      =FNread_str(args%(0))\n    DATA slurp, 15\n    WHEN 15\n      PROCcore_prepare_args(\"s\", \"slurp\")\n      =FNcore_slurp(arg$)\n    DATA eval, 16\n    WHEN 16\n      PROCcore_prepare_args(\"?\", \"eval\")\n      =FNEVAL(args%(0), repl_env%)\n    DATA pr-str, 17\n    WHEN 17\n      =FNcore_print(TRUE, \" \", args%)\n    DATA str, 18\n    WHEN 18\n      =FNcore_print(FALSE, \"\", args%)\n    DATA prn, 4\n    WHEN 4\n      PRINT FNunbox_string(FNcore_print(TRUE, \" \", args%))\n      =FNnil\n    DATA println, 19\n    WHEN 19\n      PRINT FNunbox_string(FNcore_print(FALSE, \" \", args%))\n      =FNnil\n    DATA atom, 20\n    WHEN 20\n      PROCcore_prepare_args(\"?\", \"atom\")\n      =FNalloc_atom(args%(0))\n    DATA atom?, 21\n    WHEN 21\n      PROCcore_prepare_args(\"?\", \"atom?\")\n      =FNalloc_boolean(FNis_atom(args%(0)))\n    DATA deref, 22\n    WHEN 22\n      PROCcore_prepare_args(\"a\", \"deref\")\n      =FNatom_deref(args%(0))\n    DATA reset!, 23\n    WHEN 23\n      PROCcore_prepare_args(\"a?\", \"reset!\")\n      PROCatom_reset(args%(0), args%(1))\n      =args%(1)\n    DATA swap!, 24\n    WHEN 24\n      PROCcore_prepare_args(\"af*\", \"swap!\")\n      PROCatom_reset(args%(0), FNcore_apply(args%(1), FNalloc_pair(FNatom_deref(args%(0)), args%)))\n      =FNatom_deref(args%(0))\n    DATA cons, 25\n    WHEN 25\n      PROCcore_prepare_args(\"?l\", \"cons\")\n      =FNalloc_pair(args%(0), args%(1))\n    DATA concat, 26\n    WHEN 26\n      =FNcore_concat(args%)\n    DATA nth, 27\n    WHEN 27\n      PROCcore_prepare_args(\"li\", \"nth\")\n      =FNnth(args%(0), args%(1))\n    DATA first, 28\n    WHEN 28\n      PROCcore_prepare_args(\"C\", \"first\")\n      IF FNis_nil(args%(0)) THEN =FNnil\n      =FNfirst(args%(0))\n    DATA rest, 29\n    WHEN 29\n      PROCcore_prepare_args(\"C\", \"rest\")\n      IF FNis_nil(args%(0)) THEN =FNempty\n      =FNas_list(FNrest(args%(0)))\n    DATA throw, 30\n    WHEN 30\n      PROCcore_prepare_args(\"?\", \"throw\")\n      MAL_ERR% = args%(0)\n      ERROR &40E80900, \"Mal exception: \" + FNunbox_string(FNpr_str(args%(0), FALSE))\n    DATA apply, 31\n    WHEN 31\n      PROCcore_prepare_args(\"f?*\", \"apply\")\n      =FNcore_apply(args%(0), FNcore_apply_args(FNalloc_pair(args%(1), args%)))\n    DATA map, 32\n    WHEN 32\n      PROCcore_prepare_args(\"fl\", \"map\")\n      =FNcore_map(args%(0), args%(1))\n    DATA nil?, 33\n    WHEN 33\n      PROCcore_prepare_args(\"?\", \"nil?\")\n      =FNalloc_boolean(FNis_nil(args%(0)))\n    DATA true?, 34\n    WHEN 34\n      PROCcore_prepare_args(\"?\", \"true?\")\n      IF NOT FNis_boolean(args%(0)) THEN =FNalloc_boolean(FALSE)\n      =args%(0)\n    DATA false?, 35\n    WHEN 35\n      PROCcore_prepare_args(\"?\", \"false?\")\n      IF NOT FNis_boolean(args%(0)) THEN =FNalloc_boolean(FALSE)\n      =FNalloc_boolean(NOT FNunbox_boolean(args%(0)))\n    DATA symbol?, 36\n    WHEN 36\n      PROCcore_prepare_args(\"?\", \"symbol?\")\n      =FNalloc_boolean(FNis_symbol(args%(0)))\n    DATA symbol, 37\n    WHEN 37\n      PROCcore_prepare_args(\"s\", \"symbol\")\n      =FNalloc_symbol(arg$)\n    DATA keyword, 38\n    WHEN 38\n      PROCcore_prepare_args(\"s\", \"keyword\")\n      IF LEFT$(arg$, 1) <> CHR$(127) THEN arg$ = CHR$(127) + arg$\n      =FNalloc_string(arg$)\n    DATA keyword?, 39\n    WHEN 39\n      PROCcore_prepare_args(\"?\", \"keyword?\")\n      IF FNis_string(args%(0)) THEN\n        =FNalloc_boolean(LEFT$(FNunbox_string(args%(0)), 1) = CHR$(127))\n      ENDIF\n      =FNalloc_boolean(FALSE)\n    DATA vector, 40\n    WHEN 40\n      =FNas_vector(args%)\n    DATA vector?, 41\n    WHEN 41\n      PROCcore_prepare_args(\"?\", \"vector?\")\n      =FNalloc_boolean(FNis_vector(args%(0)))      \n    DATA sequential?, 42\n    WHEN 42\n      PROCcore_prepare_args(\"?\", \"sequential?\")\n      =FNalloc_boolean(FNis_seq(args%(0)))\n    DATA hash-map, 43\n    WHEN 43\n      =FNcore_assoc(FNempty_hashmap, args%)\n    DATA map?, 44\n    WHEN 44\n      PROCcore_prepare_args(\"?\", \"map?\")\n      =FNalloc_boolean(FNis_hashmap(args%(0)))\n    DATA assoc, 45\n    WHEN 45\n      PROCcore_prepare_args(\"h*\", \"assoc\")\n      =FNcore_assoc(args%(0), args%)\n    DATA dissoc, 46\n    WHEN 46\n      PROCcore_prepare_args(\"h*\", \"dissoc\")\n      WHILE NOT FNis_empty(args%)\n        args%(0) = FNhashmap_remove(args%(0), FNunbox_string(FNfirst(args%)))\n        args% = FNrest(args%)\n      ENDWHILE\n      =args%(0)\n    DATA get, 47\n    WHEN 47\n      IF FNis_nil(FNfirst(args%)) THEN =FNnil\n      PROCcore_prepare_args(\"hs\", \"get\")\n      =FNhashmap_get(args%(0), arg$)\n    DATA contains?, 48\n    WHEN 48\n      PROCcore_prepare_args(\"hs\", \"contains?\")\n      =FNalloc_boolean(FNhashmap_contains(args%(0), arg$))\n    DATA keys, 49\n    WHEN 49\n      PROCcore_prepare_args(\"h\", \"keys\")\n      =FNhashmap_keys(args%(0))\n    DATA vals, 50\n    WHEN 50\n      PROCcore_prepare_args(\"h\", \"vals\")\n      =FNhashmap_vals(args%(0))\n    DATA readline, 51\n    WHEN 51\n      PROCcore_prepare_args(\"s\", \"readline\")\n      PRINT arg$;\n      LINE INPUT \"\" arg$\n      =FNalloc_string(arg$)\n    DATA meta, 52\n    WHEN 52\n      PROCcore_prepare_args(\"?\", \"meta\")\n      =FNmeta(args%(0))\n    DATA with-meta, 53\n    WHEN 53\n      PROCcore_prepare_args(\"??\", \"with-meta\")\n      =FNwith_meta(args%(0), args%(1))\n    DATA time-ms, 54\n    WHEN 54\n      PROCcore_prepare_args(\"\", \"time-ms\")\n      =FNalloc_int(TIME * 10)\n    DATA conj, 55\n    WHEN 55\n      PROCcore_prepare_args(\"l*\", \"conj\")\n      IF FNis_list(args%(0)) THEN\n        WHILE NOT FNis_empty(args%)\n          args%(0) = FNalloc_pair(FNfirst(args%), args%(0))\n          args% = FNrest(args%)\n        ENDWHILE\n        =args%(0)\n      ELSE : REM args%(0) is a vector\n        =FNas_vector(FNcore_concat1(args%(0), args%))\n      ENDIF\n    DATA string?, 56\n    WHEN 56\n      PROCcore_prepare_args(\"?\", \"string?\")\n      IF FNis_string(args%(0)) THEN\n        =FNalloc_boolean(LEFT$(FNunbox_string(args%(0)), 1) <> CHR$(127))\n      ENDIF\n      =FNalloc_boolean(FALSE)\n    DATA number?, 57\n    WHEN 57\n      PROCcore_prepare_args(\"?\", \"number?\")\n      =FNalloc_boolean(FNis_int(args%(0)))\n    DATA fn?, 58\n    WHEN 58\n      PROCcore_prepare_args(\"?\", \"fn?\")\n      =FNalloc_boolean(FNis_nonmacro_fn(args%(0)) OR FNis_corefn(args%(0)))\n    DATA macro?, 59\n    WHEN 59\n      PROCcore_prepare_args(\"?\", \"macro?\")\n      =FNalloc_boolean(FNis_macro(args%(0)))\n    DATA seq, 60\n    WHEN 60\n      PROCcore_prepare_args(\"?\", \"seq\")\n      =FNcore_seq(args%(0))\n    DATA vec, 61\n    WHEN 61\n      PROCcore_prepare_args(\"l\", \"vec\")\n      =FNas_vector(args%(0))\n    DATA \"\", -1\n  ENDCASE\nERROR &40E809F1, \"Call to non-existent core function\"\n\nDEF PROCcore_prepare_args(spec$, fn$)\n  REM  Check that a core function is being provided with the correct\n  REM  number and type of arguments and unbox them as appropriate.\n  REM  spec$ is the argument specification as a string.  Each character\n  REM  represents an argument:\n\n  REM  \"i\" - Must be an integer;  unbox into args%()\n  REM  \"s\" - Must be a string;    unbox into arg$\n  REM  \"t\" - Must be a string;    stuff into args%()\n  REM  \"l\" - Must be a sequence;  stuff into args%()\n  REM  \"f\" - Must be a function;  stuff into args%()\n  REM  \"a\" - Must be an atom;     stuff into args%()\n  REM  \"h\" - Must be a hash-map;  stuff into args%()\n  REM  \"C\" - Must be 'count'able  stuff into args%()\n  REM  \"?\" - Any single argument  stuff into args%()\n  REM  \"*\" - Any number of (trailing) arguments; leave in args%\n\n  REM  This function shares some local variables with FNcore_call.\n\n  LOCAL i%, val%\n\n  IF RIGHT$(spec$) = \"*\" THEN\n    spec$ = LEFT$(spec$)\n    IF FNcount(args%) < LEN(spec$) THEN\n      ERROR &40E80921, \"Core function '\"+fn$+\"' requires at least \"+STR$(LEN(spec$))+\" arguments\"\n    ENDIF\n  ELSE\n    IF FNcount(args%) <> LEN(spec$) THEN\n      ERROR &40E80921, \"Core function '\"+fn$+\"' requires \"+STR$(LEN(spec$))+\" arguments\"\n    ENDIF\n  ENDIF\n  FOR i% = 1 TO LEN(spec$)\n    val% = FNfirst(args%)\n    CASE MID$(spec$, i%, 1) OF\n      WHEN \"i\"\n        IF NOT FNis_int(val%) THEN\n          ERROR &40E80911, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be an integer\"\n        ENDIF\n        args%(i% - 1) = FNunbox_int(val%)\n      WHEN \"s\"\n        IF NOT FNis_string(val%) THEN\n          ERROR &40E80914, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be a string\"\n        ENDIF\n        arg$ = FNunbox_string(val%)\n      WHEN \"t\"\n        IF NOT FNis_string(val%) THEN\n          ERROR &40E80914, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be a string\"\n        ENDIF\n        args%(i% - 1) = val%\n      WHEN \"l\"\n        IF NOT FNis_seq(val%) THEN\n          ERROR &40E80916, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be a sequence\"\n        ENDIF\n        args%(i% - 1) = val%\n      WHEN \"f\"\n        IF NOT FNis_fn(val%) AND NOT FNis_corefn(val%) THEN\n          ERROR &40E80919, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be a function\"\n        ENDIF\n        args%(i% - 1) = val%\n      WHEN \"a\"\n        IF NOT FNis_atom(val%) THEN\n          ERROR &40E8091C, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be an atom\"\n        ENDIF\n        args%(i% - 1) = val%\n      WHEN \"h\"\n        IF NOT FNis_hashmap(val%) THEN\n          ERROR &40E8091D, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be a hash-map\"\n        ENDIF\n        args%(i% - 1) = val%\n      WHEN \"C\"\n        IF NOT FNis_seq(val%) AND NOT FNis_nil(val%) THEN\n          ERROR &40E8091F, \"Argument \"+STR$(i%)+\" to core function '\"+fn$+\"' must be a countable value\"\n        ENDIF\n        args%(i% - 1) = val%\n      WHEN \"?\"\n        args%(i% - 1) = val%\n    ENDCASE\n    args% = FNrest(args%)\n  NEXT i%\nENDPROC\n\nREM  Innards of the '=' function.\nDEF FNcore_equal(a%, b%)\n  IF a% = b% THEN =TRUE\n  IF FNis_int(a%) AND FNis_int(b%) THEN =FNunbox_int(a%) = FNunbox_int(b%)\n  IF FNis_symbol(a%) AND FNis_symbol(b%) THEN\n    =FNunbox_symbol(a%) = FNunbox_symbol(b%)\n  ENDIF\n  IF FNis_string(a%) AND FNis_string(b%) THEN\n    =FNunbox_string(a%) = FNunbox_string(b%)\n  ENDIF\n  IF FNis_seq(a%) AND FNis_seq(b%) THEN\n    IF FNis_empty(a%) AND FNis_empty(b%) THEN =TRUE\n    IF FNis_empty(a%) <> FNis_empty(b%) THEN =FALSE\n    IF NOT FNcore_equal(FNfirst(a%), FNfirst(b%)) THEN =FALSE\n    =FNcore_equal(FNrest(a%), FNrest(b%))\n  ENDIF\n  IF FNis_hashmap(a%) AND FNis_hashmap(b%) THEN\n    REM Take advantage of the sorted keys in our hash-maps.\n    IF FNcore_equal(FNhashmap_keys(a%), FNhashmap_keys(b%)) THEN\n      IF FNcore_equal(FNhashmap_vals(a%), FNhashmap_vals(b%)) THEN =TRUE\n    ENDIF\n  ENDIF\n=FALSE\n\nREM  Innards of the 'slurp' function.\nDEF FNcore_slurp(file$)\n  LOCAL f%, out%\n  f% = OPENIN(file$)\n  IF f% = 0 THEN ERROR &40E80940, \"File '\"+file$+\"' not found\"\n  out% = FNcore_slurp_channel(f%)\n  CLOSE#f%\n=out%\n\nDEF FNcore_slurp_channel(f%)\n  LOCAL this%\n  IF EOF#f% THEN =FNalloc_string(\"\")\n  REM  GET$# doesn't include a trailing newline.\n  this% = FNalloc_string(GET$#f% + CHR$(10))\n=FNstring_concat(this%, FNcore_slurp_channel(f%))\n\nREM  General-purpose printing function\nDEF FNcore_print(print_readably%, sep$, args%)\n  LOCAL out%\n  IF FNis_empty(args%) THEN =FNalloc_string(\"\")\n  out% = FNpr_str(FNfirst(args%), print_readably%)\n  args% = FNrest(args%)\n  WHILE NOT FNis_empty(args%)\n    out% = FNstring_append(out%, sep$)\n    out% = FNstring_concat(out%, FNpr_str(FNfirst(args%), print_readably%))\n    args% = FNrest(args%)\n  ENDWHILE\n=out%\n\nREM  Innards of the 'apply' function, also used by 'swap!'\nDEF FNcore_apply(fn%, args%)\n  LOCAL ast%, env%\n  IF FNis_corefn(fn%) THEN =FNcore_call(FNunbox_corefn(fn%), args%)\n  IF FNis_fn(fn%) THEN\n    ast% = FNfn_ast(fn%)\n    env% = FNnew_env(FNfn_env(fn%), FNfn_params(fn%), args%)\n    =FNEVAL(ast%, env%)\n  ENDIF\nERROR &40E80918, \"Not a function\"\n\nREM  Innards of 'concat' function\nDEF FNcore_concat(args%)\n  LOCAL tail%\n  IF FNis_empty(args%) THEN =FNempty\n  tail% = FNcore_concat(FNrest(args%))\n=FNcore_concat1(FNfirst(args%), tail%)\n  \nDEF FNcore_concat1(prefix%, tail%)\n  IF FNis_empty(prefix%) THEN =tail%\n=FNalloc_pair(FNfirst(prefix%), FNcore_concat1(FNrest(prefix%), tail%))\n\nREM  Recursively assemble the argument list for 'apply'\nDEF FNcore_apply_args(args%)\n  IF FNis_empty(FNrest(args%)) THEN =FNfirst(args%)\n=FNalloc_pair(FNfirst(args%), FNcore_apply_args(FNrest(args%)))\n\nREM  Innards of the 'map' function\nDEF FNcore_map(fn%, args%)\n  LOCAL car%, cdr%\n  IF FNis_empty(args%) THEN =args%\n  car% = FNcore_apply(fn%, FNalloc_pair(FNfirst(args%), FNempty))\n  cdr% = FNcore_map(fn%, FNrest(args%))\n=FNalloc_pair(car%, cdr%)\n\nREM  Innards of the 'hash-map' function\nDEF FNcore_assoc(map%, args%)\n  LOCAL args%()\n  DIM args%(1)\n  WHILE NOT FNis_empty(args%)\n    PROCcore_prepare_args(\"s?*\", \"hash-map\")\n    map% = FNhashmap_set(map%, arg$, args%(1))\n  ENDWHILE\n=map%\n\nREM  Innards of the 'seq' function\nDEF FNcore_seq(val%)\n  LOCAL s$, i%\n  IF FNis_empty(val%) OR FNis_nil(val%) THEN =FNnil\n  IF FNis_list(val%) THEN =val%\n  IF FNis_vector(val%) THEN =FNas_list(val%)\n  IF FNis_string(val%) THEN\n    s$ = FNunbox_string(val%)\n    IF s$ = \"\" THEN =FNnil\n    val% = FNempty\n    FOR i% = LEN(s$) TO 1 STEP -1\n      val% = FNalloc_pair(FNalloc_string(MID$(s$, i%, 1)), val%)\n    NEXT i%\n    =val%\n  ENDIF\nERROR &40E8091F, \"Argument to 'seq' must be list, vector, string, or nil\"\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/env.bas",
    "content": "REM > env library for mal in BBC BASIC\n\nDEF FNnew_env(outer%, binds%, exprs%)\n  LOCAL env%, key$\n  env% = FNalloc_environment(outer%)\n  WHILE NOT FNis_empty(binds%)\n    key$ = FNunbox_symbol(FNfirst(binds%))\n    IF key$ = \"&\" THEN\n      PROCenv_set(env%, FNunbox_symbol(FNnth(binds%, 1)), FNas_list(exprs%))\n      binds% = FNempty\n    ELSE\n      PROCenv_set(env%, key$, FNfirst(exprs%))\n      binds% = FNrest(binds%) : exprs% = FNrest(exprs%)\n    ENDIF\n  ENDWHILE\n=env%\n\nDEF PROCenv_set(env%, key$, val%)\n  LOCAL data%\n  data% = FNenvironment_data(env%)\n  data% = FNhashmap_set(data%, key$, val%)\n  PROCenvironment_set_data(env%, data%)\nENDPROC\n\nDEF FNenv_find(env%, key$)\n  WHILE NOT FNis_nil(env%)\n    IF FNhashmap_contains(FNenvironment_data(env%), key$) THEN =env%\n    env% = FNenvironment_outer(env%)\n  ENDWHILE\n=FNnil\n\nDEF FNenv_get(env%, key$)\n  env% = FNenv_find(env%, key$)\n  IF FNis_nil(env%) THEN ERROR &40E80922, \"'\"+key$+\"' not found\"\n=FNhashmap_get(FNenvironment_data(env%), key$)\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/printer.bas",
    "content": "REM > printer library for mal in BBC BASIC\n\nDEF FNpr_str(val%, print_readably%)\n  LOCAL ret%, term$, val$, keys%, vals%\n  IF FNis_nil(val%) THEN =FNalloc_string(\"nil\")\n  IF FNis_boolean(val%) THEN\n    IF FNunbox_boolean(val%) THEN =FNalloc_string(\"true\")\n    =FNalloc_string(\"false\")\n  ENDIF\n  IF FNis_int(val%) THEN =FNalloc_string(STR$(FNunbox_int(val%)))\n  IF FNis_string(val%) THEN\n    IF FNstring_chr(val%, 1) = CHR$(127) THEN =FNalloc_string(\":\" + MID$(FNunbox_string(val%), 2))\n    IF print_readably% THEN =FNalloc_string(FNformat_string(FNunbox_string(val%))) ELSE =val%\n  ENDIF\n  IF FNis_symbol(val%) THEN =FNalloc_string(FNunbox_symbol(val%))\n  IF FNis_corefn(val%) OR FNis_fn(val%) THEN =FNalloc_string(\"#<function>\")\n  IF FNis_seq(val%) THEN\n    IF FNis_vector(val%) THEN\n      ret% = FNalloc_string(\"[\") : term$ = \"]\"\n    ELSE\n      ret% = FNalloc_string(\"(\") : term$ = \")\"\n    ENDIF\n    WHILE NOT FNis_empty(val%)\n      IF FNstring_len(ret%) > 1 THEN ret% = FNstring_append(ret%, \" \")\n      ret% = FNstring_concat(ret%, FNpr_str(FNfirst(val%), print_readably%))\n      val% = FNrest(val%)\n    ENDWHILE\n    =FNstring_append(ret%, term$)\n  ENDIF\n  IF FNis_hashmap(val%) THEN\n    ret% = FNalloc_string(\"{\")\n    keys% = FNhashmap_keys(val%)\n    vals% = FNhashmap_vals(val%)\n    WHILE NOT FNis_empty(keys%)\n      IF FNstring_len(ret%) > 1 THEN ret% = FNstring_append(ret%, \" \")\n      ret% = FNstring_concat(ret%, FNpr_str(FNfirst(keys%), print_readably%))\n      ret% = FNstring_append(ret%, \" \")\n      ret% = FNstring_concat(ret%, FNpr_str(FNfirst(vals%), print_readably%))\n      keys% = FNrest(keys%)\n      vals% = FNrest(vals%)\n    ENDWHILE\n    =FNstring_append(ret%, \"}\")\n  ENDIF\n  IF FNis_atom(val%) THEN\n    ret% = FNalloc_string(\"(atom \")\n    ret% = FNstring_concat(ret%, FNpr_str(FNatom_deref(val%), print_readably%))\n    =FNstring_append(ret%, \")\")\n  ENDIF\n  ERROR &40E809F0, \"Unprintable value\"\n\nDEF FNformat_string(strval$)\n  LOCAL ptr%, c$, out$\n  IF strval$ = \"\" THEN =\"\"\"\"\"\"\n  FOR ptr% = 1 TO LEN(strval$)\n    c$ = MID$(strval$, ptr%, 1)\n    CASE c$ OF\n      WHEN \"\\\", \"\"\"\": out$ += \"\\\" + c$\n      WHEN CHR$(10): out$ += \"\\n\"\n      OTHERWISE: out$ += c$\n    ENDCASE\n  NEXT ptr%\n=\"\"\"\" + out$ + \"\"\"\"\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/reader.bas",
    "content": "REM > reader library for mal in BBC BASIC\n\nREM  ** Reader **\n\nREM  The Reader object is implemented as an array and a mutable pointer.\n\nDEF FNreader_peek(tokens$(), RETURN tokptr%)\n=tokens$(tokptr%)\n\nDEF FNreader_next(token$(), RETURN tokptr%)\n  tokptr% += 1\n=tokens$(tokptr% - 1)\n\nDEF FNread_str(src%)\n  LOCAL ntokens%, tokptr%, tokens$()\n  DIM tokens$(2048)\n  ntokens% = FNtokenize(src%, tokens$())\n  tokptr% = 0\n=FNread_form(tokens$(), tokptr%)\n\nREM  ** Tokenizer **\n\nDEF FNtokenize(src%, tokens$())\n  REM  The tokenizer is implemented explicitly as a deterministic\n  REM  finite automaton.\n  LOCAL p%, state%, tok$, tokptr%, c$, rc$, match$, action%\n  LOCAL DATA\n\n  state% = 1\n  tokptr% = 0\n  tok$ = \"\"\n  FOR p% = 1 TO FNstring_len(src%)\n    c$ = FNstring_chr(src%, p%)\n    rc$ = c$\n    REM  Convert some characters to ones that are easier to put into\n    REM  DATA statements.  These substitutions are only used for\n    REM  matching: the token still contains the original character.\n    CASE ASC(c$) OF\n      REM  Fold some upper-case letters together so that we can re-use\n      REM  them to represent more awkward characters.\n      WHEN 78, 81: c$ = \"A\"\n      REM  Now convert newlines into \"N\"\n      WHEN 10: c$ = \"N\"\n      REM  These are the other characters that Perl's \"\\s\" escape matches.\n      WHEN 9, 11, 12, 13: c$ = \" \"\n      REM  Brandy has a bug whereby it doesn't correctly parse strings\n      REM  in DATA statements that begin with quotation marks, so convert\n      REM  quotation marks to \"Q\".\n      WHEN 34: c$ = \"Q\"\n    ENDCASE\n    REM  The state table consists of a DATA statement for each current\n    REM  state, which triples representing transitions.  Each triple\n    REM  consists of a string of characters to match, an action, and a\n    REM  next state.  A matching string of \"\" matches any character,\n    REM  and hence marks the end of a state.\n\n    REM  Actions are:\n    REM   0: Add this character to the current token\n    REM   1: Emit token; start a new token with this character\n    REM   5: Emit token; skip this character\n\n    RESTORE +state%\n    REM  state 1: Initial state, or inside a bare word\n    DATA \" N,\",5,1,          \"~\",1,5, \"[]{}()'`^@\",1,3, Q,1,7, \";\",5,11, \"\",0,1\n    REM  state 3: Just seen the end of a token\n    DATA \" N,\",5,1,          \"~\",1,5, \"[]{}()'`^@\",1,3, Q,1,7, \";\",5,11, \"\",1,1\n    REM  state 5: Just seen a \"~\"\n    DATA \" N,\",5,1, \"@\",0,3, \"~\",1,5, \"[]{}()'`^@\",1,3, Q,1,7, \";\",5,11, \"\",1,1\n    REM  state 7: Inside a quoted string\n    DATA \"\\\",0,9, Q,0,3, \"\",0,7\n    REM  state 9: After a backslash in a string\n    DATA \"\",0,7\n    REM  state 11: Inside a comment\n    DATA N,5,3, \"\",5,11\n\n    REM  Find a matching transition from the current state.\n    REM PRINT ;state%;\"-->\";\n    REPEAT\n      READ match$, action%, state%\n      REM PRINT \"[\";match$;\"](\";action%;\",\";state%;\")\";\n    UNTIL match$ = \"\" OR INSTR(match$, c$) > 0\n    REM PRINT ;\"-->\";state%\n\n    REM  Execute any actions.\n    IF action% AND 1 AND tokens$(tokptr%) <> \"\" THEN tokptr% += 1\n    IF (action% AND 4) = 0 THEN tokens$(tokptr%) += rc$\n  NEXT p%\n  IF tokens$(tokptr%) <> \"\" THEN tokptr% += 1\n=tokptr%        \n\nREM  ** More Reader **\n\nDEF FNread_form(tokens$(), RETURN tokptr%)\n  LOCAL tok$, x%\n  tok$ = FNreader_peek(tokens$(), tokptr%)\n  CASE tok$ OF\n    WHEN \"\" : ERROR &40E80930, \"Unexpected end of input\"\n    WHEN \"(\": =FNread_list(tokens$(), tokptr%)\n    WHEN \"[\": =FNread_vector(tokens$(), tokptr%)\n    WHEN \"{\": =FNread_hashmap(tokens$(), tokptr%)\n    WHEN \")\", \"]\", \"}\": ERROR &40E80931, \"Unexpected '\"+tok$ +\"'\"\n    WHEN \"'\": =FNreader_macro(\"quote\", tokens$(), tokptr%)\n    WHEN \"`\": =FNreader_macro(\"quasiquote\", tokens$(), tokptr%)\n    WHEN \"~\": =FNreader_macro(\"unquote\", tokens$(), tokptr%)\n    WHEN \"~@\":=FNreader_macro(\"splice-unquote\", tokens$(), tokptr%)\n    WHEN \"@\": =FNreader_macro(\"deref\", tokens$(), tokptr%)\n    WHEN \"^\": =FNread_with_meta(tokens$(), tokptr%)\n  ENDCASE\n=FNread_atom(tokens$(), tokptr%)\n\nDEF FNread_list(tokens$(), RETURN tokptr%)\n  LOCAL tok$\n  tok$ = FNreader_next(tokens$(), tokptr%) : REM skip over \"(\"\n=FNread_list_tail(tokens$(), tokptr%, \")\")\n\nDEF FNread_vector(tokens$(), RETURN tokptr%)\n  LOCAL tok$\n  tok$ = FNreader_next(tokens$(), tokptr%) : REM skip over \"[\"\n=FNas_vector(FNread_list_tail(tokens$(), tokptr%, \"]\"))\n\nDEF FNread_list_tail(tokens$(), RETURN tokptr%, term$)\n  LOCAL tok$, car%, cdr%\n  IF FNreader_peek(tokens$(), tokptr%) = term$ THEN\n     tok$ = FNreader_next(tokens$(), tokptr%)\n    =FNempty\n  ENDIF\n  car% = FNread_form(tokens$(), tokptr%)\n  cdr% = FNread_list_tail(tokens$(), tokptr%, term$)\n=FNalloc_pair(car%, cdr%)\n\nDEF FNread_hashmap(tokens$(), RETURN tokptr%)\n  LOCAL tok$, map%, key%, val%\n  tok$ = FNreader_next(tokens$(), tokptr%) : REM skip over \"{\"\n  map% = FNempty_hashmap\n  WHILE FNreader_peek(tokens$(), tokptr%) <> \"}\"\n    key% = FNread_form(tokens$(), tokptr%)\n    IF NOT FNis_string(key%) ERROR &40E80932, \"Hash-map key must be a string\"\n    val% = FNread_form(tokens$(), tokptr%)\n    map% = FNhashmap_set(map%, FNunbox_string(key%), val%)\n  ENDWHILE\n  tok$ = FNreader_next(tokens$(), tokptr%) : REM skip over \"}\"\n=map%\n  \nDEF FNreader_macro(quote$, token$(), RETURN tokptr%)\n  LOCAL tok$\n  tok$ = FNreader_next(tokens$(), tokptr%) : REM skip quoting token\n=FNalloc_list2(FNalloc_symbol(quote$), FNread_form(tokens$(), tokptr%))\n\nDEF FNread_with_meta(token$(), RETURN tokptr%)\n  LOCAL tok$, wm%, base%, meta%\n  tok$ = FNreader_next(tokens$(), tokptr%) : REM skip '^' token\n  wm% = FNalloc_symbol(\"with-meta\")\n  meta% = FNread_form(tokens$(), tokptr%)\n  base% = FNread_form(tokens$(), tokptr%)\n=FNalloc_list3(wm%, base%, meta%)\n\nDEF FNis_token_numeric(tok$)\n  LOCAL i%, c%\n  IF LEFT$(tok$, 1) = \"-\" THEN tok$ = MID$(tok$, 2)\n  IF LEN(tok$) = 0 THEN =FALSE\n  FOR i% = 1 TO LEN(tok$)\n    c% = ASC(MID$(tok$, i%, 1))\n    IF c% < &30 OR c% > &39 THEN =FALSE\n  NEXT i%\n=TRUE\n\nDEF FNread_atom(tokens$(), RETURN tokptr%)\n  LOCAL strval$\n  strval$ = FNreader_next(tokens$(), tokptr%)\n  IF strval$ = \"nil\" THEN =FNnil\n  IF strval$ = \"true\" THEN =FNalloc_boolean(TRUE)\n  IF strval$ = \"false\" THEN =FNalloc_boolean(FALSE)\n  IF LEFT$(strval$, 1) = \"\"\"\" THEN =FNalloc_string(FNunquote_string(strval$))\n  IF LEFT$(strval$, 1) = \":\" THEN =FNalloc_string(CHR$(127) + MID$(strval$, 2))\n  IF FNis_token_numeric(strval$) THEN =FNalloc_int(VAL(strval$))\n=FNalloc_symbol(strval$)\n\nDEF FNunquote_string(strval$)\n  LOCAL inptr%, bs%, out$, c$\n  IF RIGHT$(strval$, 1) <> \"\"\"\" THEN ERROR &40E80930, \"Unexpected end of input\"\n  inptr% = 2\n  REPEAT\n    bs% = INSTR(strval$, \"\\\", inptr%)\n    IF bs% > 0 THEN\n      out$ += MID$(strval$, inptr%, bs% - inptr%)\n      c$ = MID$(strval$, bs% + 1, 1)\n      IF c$ = \"n\" THEN c$ = CHR$(10)\n      out$ += c$\n      inptr% = bs% + 2\n    ENDIF\n  UNTIL bs% = 0\n  IF inptr% = LEN(strval$) + 1 THEN ERROR &40E80930, \"Unexpected end of input\"\n  out$ += MID$(strval$, inptr%, LEN(strval$) - inptr%)\n=out$\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/riscos/.gitignore",
    "content": "*,ffb\n"
  },
  {
    "path": "impls/bbc-basic/riscos/setup,feb",
    "content": "| This Obey file sets up the environment for running mal on RISC OS.\nBASIC { < tokenize }\n"
  },
  {
    "path": "impls/bbc-basic/riscos/tokenize,ffe",
    "content": "REM Tokenizing libraries...\nTEXTLOAD \"^.core/bas\"\nSAVE \"core\"\nTEXTLOAD \"^.env/bas\"\nSAVE \"env\"\nTEXTLOAD \"^.printer/bas\"\nSAVE \"printer\"\nTEXTLOAD \"^.reader/bas\"\nSAVE \"reader\"\nTEXTLOAD \"^.types/bas\"\nSAVE \"types\"\nREM Tokenizing steps...\nTEXTLOAD \"^.step0_repl/bas\"\nSAVE \"step0_repl\"\nTEXTLOAD \"^.step1_read_print/bas\"\nSAVE \"step1_read_print\"\nTEXTLOAD \"^.step2_eval/bas\"\nSAVE \"step2_eval\"\nTEXTLOAD \"^.step3_env/bas\"\nSAVE \"step3_env\"\nTEXTLOAD \"^.step4_if_fn_do/bas\"\nSAVE \"step4_if_fn_do\"\nTEXTLOAD \"^.step5_tco/bas\"\nSAVE \"step5_tco\"\nTEXTLOAD \"^.step6_file/bas\"\nSAVE \"step6_file\"\nTEXTLOAD \"^.step7_quote/bas\"\nSAVE \"step7_quote\"\nTEXTLOAD \"^.step8_macros/bas\"\nSAVE \"step8_macros\"\nTEXTLOAD \"^.step9_try/bas\"\nSAVE \"step9_try\"\nTEXTLOAD \"^.stepA_mal/bas\"\nSAVE \"stepA_mal\"\nREM All done.\nQUIT\n"
  },
  {
    "path": "impls/bbc-basic/run",
    "content": "#!/usr/bin/env bash\nexec \"${BRANDY:-sbrandy}\" -size 1024k \\\n    -path ../bbc-basic -quit $(dirname $0)/${STEP:-stepA_mal}.bas \"${@}\"\n"
  },
  {
    "path": "impls/bbc-basic/step0_repl.bas",
    "content": "REM Step 0 of mal in BBC BASIC\n\nREPEAT\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=a$\n\nDEF FNEVAL(a$)\n=a$\n\nDEF FNPRINT(a$)\n=a$\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$)))\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step1_read_print.bas",
    "content": "REM Step 1 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\n\nPROCtypes_init\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNEVAL(a%)\n=a%\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$)))\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step2_eval.bas",
    "content": "REM Step 2 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\n\nPROCtypes_init\n\nREM  These correspond with the CASE statement in FNcore_call\nrepl_env% = FNempty_hashmap\nrepl_env% = FNhashmap_set(repl_env%, \"+\", FNalloc_corefn(0))\nrepl_env% = FNhashmap_set(repl_env%, \"-\", FNalloc_corefn(1))\nrepl_env% = FNhashmap_set(repl_env%, \"*\", FNalloc_corefn(2))\nrepl_env% = FNhashmap_set(repl_env%, \"/\", FNalloc_corefn(3))\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNEVAL(ast%, env%)\n  LOCAL car%, val%, key$\n  REM PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n  IF FNis_symbol(ast%) THEN\n    val% = FNhashmap_get(env%, FNunbox_symbol(ast%))\n    IF val% = FNnil THEN ERROR &40E80922, \"Symbol not in environment\"\n    =val%\n  ENDIF\n  IF FNis_hashmap(ast%) THEN\n    val% = FNempty_hashmap\n    bindings% = FNhashmap_keys(ast%)\n    WHILE NOT FNis_empty(bindings%)\n      key$ = FNunbox_string(FNfirst(bindings%))\n      val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n      bindings% = FNrest(bindings%)\n    ENDWHILE\n    =val%\n  ENDIF\n  IF NOT FNis_seq(ast%) THEN =ast%\n  IF FNis_empty(ast%) THEN =ast%\n  car% = FNEVAL(FNfirst(ast%), env%)\n  IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, FNeval_ast(FNrest(ast%), env%))\n  =FNcore_call(FNunbox_corefn(car%), FNeval_ast(FNrest(ast%), env%))\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nREM  Call a core function, taking the function number and a mal list of\nREM  objects to pass as arguments.\nDEF FNcore_call(fn%, args%)\n  LOCAL x%, y%, z%\n  x% = FNunbox_int(FNfirst(args%))\n  y% = FNunbox_int(FNfirst(FNrest(args%)))\n  CASE fn% OF\n    WHEN 0 : z% = x%  +  y%\n    WHEN 1 : z% = x%  -  y%\n    WHEN 2 : z% = x%  *  y%\n    WHEN 3 : z% = x% DIV y%\n    OTHERWISE : ERROR &40E809F1, \"Call to non-existent core function\"\n  ENDCASE\n=FNalloc_int(z%)\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step3_env.bas",
    "content": "REM Step 3 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\n\nPROCtypes_init\n\nREM  These correspond with the CASE statement in FNcore_call\nrepl_env% = FNalloc_environment(FNnil)\nPROCenv_set(repl_env%, \"+\", FNalloc_corefn(0))\nPROCenv_set(repl_env%, \"-\", FNalloc_corefn(1))\nPROCenv_set(repl_env%, \"*\", FNalloc_corefn(2))\nPROCenv_set(repl_env%, \"/\", FNalloc_corefn(3))\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNEVAL(ast%, env%)\n  LOCAL car%, val%, bindings%, key$\n  val% = FNenv_find(env%, \"DEBUG-EVAL\")\n  IF NOT FNis_nil(val%) THEN\n    IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n      PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n    ENDIF\n  ENDIF\n  IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n  IF FNis_hashmap(ast%) THEN\n    val% = FNempty_hashmap\n    bindings% = FNhashmap_keys(ast%)\n    WHILE NOT FNis_empty(bindings%)\n      key$ = FNunbox_string(FNfirst(bindings%))\n      val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n      bindings% = FNrest(bindings%)\n    ENDWHILE\n    =val%\n  ENDIF\n  IF NOT FNis_seq(ast%) THEN =ast%\n  IF FNis_empty(ast%) THEN =ast%\n  car% = FNfirst(ast%)\n  IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n  IF FNis_symbol(car%) THEN\n    key$ = FNunbox_symbol(car%)\n    CASE key$ OF\n      REM  Special forms\n      WHEN \"def!\"\n        val% = FNEVAL(FNnth(ast%, 2), env%)\n        PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n        =val%\n      WHEN \"let*\"\n        env% = FNalloc_environment(env%)\n        bindings% = FNnth(ast%, 1)\n        WHILE NOT FNis_empty(bindings%)\n          PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n          bindings% = FNrest(FNrest(bindings%))\n        ENDWHILE\n        =FNEVAL(FNnth(ast%, 2), env%)\n      OTHERWISE\n        car% = FNenv_get(env%, key$)\n    ENDCASE\n  ELSE\n    car% = FNEVAL(car%, env%)\n  ENDIF\n  REM  This is the \"apply\" part.\n  ast% = FNeval_ast(FNrest(ast%), env%)\n  =FNcore_call(FNunbox_corefn(car%), ast%)\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nREM  Call a core function, taking the function number and a mal list of\nREM  objects to pass as arguments.\nDEF FNcore_call(fn%, args%)\n  LOCAL x%, y%, z%\n  x% = FNunbox_int(FNfirst(args%))\n  y% = FNunbox_int(FNfirst(FNrest(args%)))\n  CASE fn% OF\n    WHEN 0 : z% = x%  +  y%\n    WHEN 1 : z% = x%  -  y%\n    WHEN 2 : z% = x%  *  y%\n    WHEN 3 : z% = x% DIV y%\n    OTHERWISE : ERROR &40E809F1, \"Call to non-existent core function\"\n  ENDCASE\n=FNalloc_int(z%)\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step4_if_fn_do.bas",
    "content": "REM Step 4 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nval$ = FNrep(\"(def! not (fn* (a) (if a false true)))\")\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNEVAL(ast%, env%)\n  PROCgc\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n  LOCAL car%, val%, bindings%, key$\n  PROCgc_keep_only2(ast%, env%)\n  val% = FNenv_find(env%, \"DEBUG-EVAL\")\n  IF NOT FNis_nil(val%) THEN\n    IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n      PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n    ENDIF\n  ENDIF\n  IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n  IF FNis_hashmap(ast%) THEN\n    val% = FNempty_hashmap\n    bindings% = FNhashmap_keys(ast%)\n    WHILE NOT FNis_empty(bindings%)\n      key$ = FNunbox_string(FNfirst(bindings%))\n      val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n      bindings% = FNrest(bindings%)\n    ENDWHILE\n    =val%\n  ENDIF\n  IF NOT FNis_seq(ast%) THEN =ast%\n  IF FNis_empty(ast%) THEN =ast%\n  car% = FNfirst(ast%)\n  IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n  IF FNis_symbol(car%) THEN\n    key$ = FNunbox_symbol(car%)\n    CASE key$ OF\n      REM  Special forms\n      WHEN \"def!\"\n        val% = FNEVAL(FNnth(ast%, 2), env%)\n        PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n        =val%\n      WHEN \"let*\"\n        env% = FNalloc_environment(env%)\n        bindings% = FNnth(ast%, 1)\n        WHILE NOT FNis_empty(bindings%)\n          PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n          bindings% = FNrest(FNrest(bindings%))\n        ENDWHILE\n        =FNEVAL(FNnth(ast%, 2), env%)\n      WHEN \"do\"\n        WHILE TRUE\n          ast% = FNrest(ast%)\n          IF FNis_empty(ast%) THEN = val%\n          val% = FNEVAL(FNfirst(ast%), env%)\n        ENDWHILE\n      WHEN \"if\"\n        IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN =FNEVAL(FNnth(ast%, 2), env%)\n        IF FNcount(ast%) = 3 THEN =FNnil\n        =FNEVAL(FNnth(ast%, 3), env%)\n      WHEN \"fn*\"\n        =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n      OTHERWISE\n        car% = FNenv_get(env%, key$)\n    ENDCASE\n  ELSE\n    car% = FNEVAL(car%, env%)\n  ENDIF\n  REM  This is the \"apply\" part.\n  ast% = FNeval_ast(FNrest(ast%), env%)\n  IF FNis_corefn(car%) THEN\n    =FNcore_call(FNunbox_corefn(car%), ast%)\n  ENDIF\n  IF FNis_fn(car%) THEN\n    env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n    =FNEVAL(FNfn_ast(car%), env%)\n  ENDIF\n  ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step5_tco.bas",
    "content": "REM Step 5 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nval$ = FNrep(\"(def! not (fn* (a) (if a false true)))\")\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNEVAL(ast%, env%)\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n    LOCAL car%, val%, bindings%, key$\n31416 REM tail call optimization loop\n    PROCgc_keep_only2(ast%, env%)\n    val% = FNenv_find(env%, \"DEBUG-EVAL\")\n    IF NOT FNis_nil(val%) THEN\n      IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n        PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n      ENDIF\n    ENDIF\n    IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n    IF FNis_hashmap(ast%) THEN\n      val% = FNempty_hashmap\n      bindings% = FNhashmap_keys(ast%)\n      WHILE NOT FNis_empty(bindings%)\n        key$ = FNunbox_string(FNfirst(bindings%))\n        val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n        bindings% = FNrest(bindings%)\n      ENDWHILE\n      =val%\n    ENDIF\n    IF NOT FNis_seq(ast%) THEN =ast%\n    IF FNis_empty(ast%) THEN =ast%\n    car% = FNfirst(ast%)\n    IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n    IF FNis_symbol(car%) THEN\n      key$ = FNunbox_symbol(car%)\n      CASE key$ OF\n        REM  Special forms\n        WHEN \"def!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"let*\"\n          env% = FNalloc_environment(env%)\n          bindings% = FNnth(ast%, 1)\n          WHILE NOT FNis_empty(bindings%)\n            PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n            bindings% = FNrest(FNrest(bindings%))\n          ENDWHILE\n          ast% = FNnth(ast%, 2)\n          GOTO 31416\n        WHEN \"do\"\n          REM  The guide has us call FNeval_ast on the sub-list that excludes\n          REM  the last element of ast%, but that's a bit painful without\n          REM  native list slicing, so it's easier to just re-implement the\n          REM  bit of FNeval_ast that we need.\n          ast% = FNrest(ast%)\n          WHILE NOT FNis_empty(FNrest(ast%))\n            val% = FNEVAL(FNfirst(ast%), env%)\n            ast% = FNrest(ast%)\n          ENDWHILE\n          ast% = FNfirst(ast%)\n          GOTO 31416\n        WHEN \"if\"\n          IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN\n            ast% = FNnth(ast%, 2)\n          ELSE\n            IF FNcount(ast%) = 3 THEN =FNnil\n            ast% = FNnth(ast%, 3)\n          ENDIF\n          GOTO 31416\n        WHEN \"fn*\"\n          =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n        OTHERWISE\n          car% = FNenv_get(env%, key$)\n      ENDCASE\n    ELSE\n      car% = FNEVAL(car%, env%)\n    ENDIF\n    REM  This is the \"apply\" part.\n    ast% = FNeval_ast(FNrest(ast%), env%)\n    IF FNis_corefn(car%) THEN\n      =FNcore_call(FNunbox_corefn(car%), ast%)\n    ENDIF\n    IF FNis_fn(car%) THEN\n      env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n      ast% = FNfn_ast(car%)\n      GOTO 31416\n    ENDIF\n    ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step6_file.bas",
    "content": "REM Step 6 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nREM  Initial forms to evaluate\nRESTORE +0\nDATA (def! not (fn* (a) (if a false true)))\nDATA (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nDATA \"\"\nREPEAT\n  READ form$\n  IF form$ <> \"\" THEN val$ = FNrep(form$)\nUNTIL form$ = \"\"\n\nargv% = FNget_argv\n\nIF FNis_empty(argv%) THEN\n  PROCenv_set(repl_env%, \"*ARGV*\", FNempty)\nELSE\n  PROCenv_set(repl_env%, \"*ARGV*\", FNrest(argv%))\n  val$ = FNrep(\"(load-file \" + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + \")\")\n  END\nENDIF\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNEVAL(ast%, env%)\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n    LOCAL car%, val%, bindings%, key$\n31416 REM tail call optimization loop\n    PROCgc_keep_only2(ast%, env%)\n    val% = FNenv_find(env%, \"DEBUG-EVAL\")\n    IF NOT FNis_nil(val%) THEN\n      IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n        PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n      ENDIF\n    ENDIF\n    IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n    IF FNis_hashmap(ast%) THEN\n      val% = FNempty_hashmap\n      bindings% = FNhashmap_keys(ast%)\n      WHILE NOT FNis_empty(bindings%)\n        key$ = FNunbox_string(FNfirst(bindings%))\n        val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n        bindings% = FNrest(bindings%)\n      ENDWHILE\n      =val%\n    ENDIF\n    IF NOT FNis_seq(ast%) THEN =ast%\n    IF FNis_empty(ast%) THEN =ast%\n    car% = FNfirst(ast%)\n    IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n    IF FNis_symbol(car%) THEN\n      key$ = FNunbox_symbol(car%)\n      CASE key$ OF\n        REM  Special forms\n        WHEN \"def!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"let*\"\n          env% = FNalloc_environment(env%)\n          bindings% = FNnth(ast%, 1)\n          WHILE NOT FNis_empty(bindings%)\n            PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n            bindings% = FNrest(FNrest(bindings%))\n          ENDWHILE\n          ast% = FNnth(ast%, 2)\n          GOTO 31416\n        WHEN \"do\"\n          REM  The guide has us call FNeval_ast on the sub-list that excludes\n          REM  the last element of ast%, but that's a bit painful without\n          REM  native list slicing, so it's easier to just re-implement the\n          REM  bit of FNeval_ast that we need.\n          ast% = FNrest(ast%)\n          WHILE NOT FNis_empty(FNrest(ast%))\n            val% = FNEVAL(FNfirst(ast%), env%)\n            ast% = FNrest(ast%)\n          ENDWHILE\n          ast% = FNfirst(ast%)\n          GOTO 31416\n        WHEN \"if\"\n          IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN\n            ast% = FNnth(ast%, 2)\n          ELSE\n            IF FNcount(ast%) = 3 THEN =FNnil\n            ast% = FNnth(ast%, 3)\n          ENDIF\n          GOTO 31416\n        WHEN \"fn*\"\n          =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n        OTHERWISE\n          car% = FNenv_get(env%, key$)\n      ENDCASE\n    ELSE\n      car% = FNEVAL(car%, env%)\n    ENDIF\n    REM  This is the \"apply\" part.\n    ast% = FNeval_ast(FNrest(ast%), env%)\n    IF FNis_corefn(car%) THEN\n      =FNcore_call(FNunbox_corefn(car%), ast%)\n    ENDIF\n    IF FNis_fn(car%) THEN\n      env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n      ast% = FNfn_ast(car%)\n      GOTO 31416\n    ENDIF\n    ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nDEF FNget_argv\n  PROCgc_enter\n  LOCAL argv%, rargv%, cmdptr%, arg$, len%\n  argv% = FNempty\n  IF !PAGE = &D7C1C7C5 THEN\n    REM  Running under Brandy, so ARGC and ARGV$ are usable.\n    IF ARGC >= 1 THEN\n      FOR i% = ARGC TO 1 STEP -1\n        argv% = FNalloc_pair(FNalloc_string(ARGV$(i%)), argv%)\n      NEXT i%\n    ENDIF\n  ELSE\n    IF (INKEY(-256) AND &F0) = &A0 THEN\n      rargv% = FNempty\n      REM  Running under RISC OS\n      REM  Vexingly, we can only get the command line that was passed to\n      REM  the BASIC interpreter.  This means that we need to extract\n      REM  the arguments from that.  Typically, we will have been started\n      REM  with \"BASIC -quit <filename> <args>\".\n\n      DIM q% 256\n      SYS \"OS_GetEnv\" TO cmdptr%\n      WHILE ?cmdptr% >= 32\n        SYS \"OS_GSTrans\", cmdptr%, q%, &20000000 + 256 TO cmdptr%, , len%\n        q%?len% = 13\n        rargv% = FNalloc_pair(FNalloc_string($q%), rargv%)\n      ENDWHILE\n      REM  Put argv back into the right order.\n      WHILE NOT FNis_empty(rargv%)\n        argv% = FNalloc_pair(FNfirst(rargv%), argv%)\n        rargv% = FNrest(rargv%)\n      ENDWHILE\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"BASIC\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      IF FNunbox_string(FNfirst(argv%)) <> \"-quit\" THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"-quit\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip filename\n    ENDIF\n  ENDIF\n=FNgc_exit(argv%)\n\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step7_quote.bas",
    "content": "REM Step 7 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nREM  Initial forms to evaluate\nRESTORE +0\nDATA (def! not (fn* (a) (if a false true)))\nDATA (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nDATA \"\"\nREPEAT\n  READ form$\n  IF form$ <> \"\" THEN val$ = FNrep(form$)\nUNTIL form$ = \"\"\n\nargv% = FNget_argv\n\nIF FNis_empty(argv%) THEN\n  PROCenv_set(repl_env%, \"*ARGV*\", FNempty)\nELSE\n  PROCenv_set(repl_env%, \"*ARGV*\", FNrest(argv%))\n  val$ = FNrep(\"(load-file \" + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + \")\")\n  END\nENDIF\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNstarts_with(ast%, sym$)\n  LOCAL a0%\n  IF NOT FNis_list(ast%) THEN =FALSE\n  a0% = FNfirst(ast%)\n  IF NOT FNis_symbol(a0%) THEN =FALSE\n  =FNunbox_symbol(a0%) = sym$\n\nDEF FNqq_elts(seq%)\n  LOCAL elt%, acc%\n  IF FNis_empty(seq%) THEN =FNempty\n  elt% = FNfirst(seq%)\n  acc% = FNqq_elts(FNrest(seq%))\n  IF FNstarts_with(elt%, \"splice-unquote\") THEN\n    =FNalloc_list3(FNalloc_symbol(\"concat\"), FNnth(elt%, 1), acc%)\n  ENDIF\n  =FNalloc_list3(FNalloc_symbol(\"cons\"), FNquasiquote(elt%), acc%)\n\nDEF FNquasiquote(ast%)\n  IF FNstarts_with(ast%, \"unquote\") THEN =FNnth(ast%, 1)\n  IF FNis_list(ast%) THEN =FNqq_elts(ast%)\n  IF FNis_vector(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"vec\"), FNqq_elts(ast%))\n  ENDIF\n  IF FNis_symbol(ast%) OR FNis_hashmap(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"quote\"), ast%)\n  ENDIF\n  =ast%\n\nDEF FNEVAL(ast%, env%)\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n    LOCAL car%, val%, bindings%, key$\n31416 REM tail call optimization loop\n    PROCgc_keep_only2(ast%, env%)\n    val% = FNenv_find(env%, \"DEBUG-EVAL\")\n    IF NOT FNis_nil(val%) THEN\n      IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n        PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n      ENDIF\n    ENDIF\n    IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n    IF FNis_hashmap(ast%) THEN\n      val% = FNempty_hashmap\n      bindings% = FNhashmap_keys(ast%)\n      WHILE NOT FNis_empty(bindings%)\n        key$ = FNunbox_string(FNfirst(bindings%))\n        val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n        bindings% = FNrest(bindings%)\n      ENDWHILE\n      =val%\n    ENDIF\n    IF NOT FNis_seq(ast%) THEN =ast%\n    IF FNis_empty(ast%) THEN =ast%\n    car% = FNfirst(ast%)\n    IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n    IF FNis_symbol(car%) THEN\n      key$ = FNunbox_symbol(car%)\n      CASE key$ OF\n        REM  Special forms\n        WHEN \"def!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"let*\"\n          env% = FNalloc_environment(env%)\n          bindings% = FNnth(ast%, 1)\n          WHILE NOT FNis_empty(bindings%)\n            PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n            bindings% = FNrest(FNrest(bindings%))\n          ENDWHILE\n          ast% = FNnth(ast%, 2)\n          GOTO 31416\n        WHEN \"do\"\n          REM  The guide has us call FNeval_ast on the sub-list that excludes\n          REM  the last element of ast%, but that's a bit painful without\n          REM  native list slicing, so it's easier to just re-implement the\n          REM  bit of FNeval_ast that we need.\n          ast% = FNrest(ast%)\n          WHILE NOT FNis_empty(FNrest(ast%))\n            val% = FNEVAL(FNfirst(ast%), env%)\n            ast% = FNrest(ast%)\n          ENDWHILE\n          ast% = FNfirst(ast%)\n          GOTO 31416\n        WHEN \"if\"\n          IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN\n            ast% = FNnth(ast%, 2)\n          ELSE\n            IF FNcount(ast%) = 3 THEN =FNnil\n            ast% = FNnth(ast%, 3)\n          ENDIF\n          GOTO 31416\n        WHEN \"fn*\"\n          =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n        WHEN \"quote\"\n          =FNnth(ast%, 1)\n        WHEN \"quasiquote\"\n          ast% = FNquasiquote(FNnth(ast%, 1))\n          GOTO 31416\n        OTHERWISE\n          car% = FNenv_get(env%, key$)\n      ENDCASE\n    ELSE\n      car% = FNEVAL(car%, env%)\n    ENDIF\n    REM  This is the \"apply\" part.\n    ast% = FNeval_ast(FNrest(ast%), env%)\n    IF FNis_corefn(car%) THEN\n      =FNcore_call(FNunbox_corefn(car%), ast%)\n    ENDIF\n    IF FNis_fn(car%) THEN\n      env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n      ast% = FNfn_ast(car%)\n      GOTO 31416\n    ENDIF\n    ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nDEF FNget_argv\n  PROCgc_enter\n  LOCAL argv%, rargv%, cmdptr%, arg$, len%\n  argv% = FNempty\n  IF !PAGE = &D7C1C7C5 THEN\n    REM  Running under Brandy, so ARGC and ARGV$ are usable.\n    IF ARGC >= 1 THEN\n      FOR i% = ARGC TO 1 STEP -1\n        argv% = FNalloc_pair(FNalloc_string(ARGV$(i%)), argv%)\n      NEXT i%\n    ENDIF\n  ELSE\n    IF (INKEY(-256) AND &F0) = &A0 THEN\n      rargv% = FNempty\n      REM  Running under RISC OS\n      REM  Vexingly, we can only get the command line that was passed to\n      REM  the BASIC interpreter.  This means that we need to extract\n      REM  the arguments from that.  Typically, we will have been started\n      REM  with \"BASIC -quit <filename> <args>\".\n\n      DIM q% 256\n      SYS \"OS_GetEnv\" TO cmdptr%\n      WHILE ?cmdptr% >= 32\n        SYS \"OS_GSTrans\", cmdptr%, q%, &20000000 + 256 TO cmdptr%, , len%\n        q%?len% = 13\n        rargv% = FNalloc_pair(FNalloc_string($q%), rargv%)\n      ENDWHILE\n      REM  Put argv back into the right order.\n      WHILE NOT FNis_empty(rargv%)\n        argv% = FNalloc_pair(FNfirst(rargv%), argv%)\n        rargv% = FNrest(rargv%)\n      ENDWHILE\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"BASIC\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      IF FNunbox_string(FNfirst(argv%)) <> \"-quit\" THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"-quit\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip filename\n    ENDIF\n  ENDIF\n=FNgc_exit(argv%)\n\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step8_macros.bas",
    "content": "REM Step 8 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nREM  Initial forms to evaluate\nRESTORE +0\nDATA (def! not (fn* (a) (if a false true)))\nDATA (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nDATA (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\nDATA \"\"\nREPEAT\n  READ form$\n  IF form$ <> \"\" THEN val$ = FNrep(form$)\nUNTIL form$ = \"\"\n\nargv% = FNget_argv\n\nIF FNis_empty(argv%) THEN\n  PROCenv_set(repl_env%, \"*ARGV*\", FNempty)\nELSE\n  PROCenv_set(repl_env%, \"*ARGV*\", FNrest(argv%))\n  val$ = FNrep(\"(load-file \" + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + \")\")\n  END\nENDIF\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNstarts_with(ast%, sym$)\n  LOCAL a0%\n  IF NOT FNis_list(ast%) THEN =FALSE\n  a0% = FNfirst(ast%)\n  IF NOT FNis_symbol(a0%) THEN =FALSE\n  =FNunbox_symbol(a0%) = sym$\n\nDEF FNqq_elts(seq%)\n  LOCAL elt%, acc%\n  IF FNis_empty(seq%) THEN =FNempty\n  elt% = FNfirst(seq%)\n  acc% = FNqq_elts(FNrest(seq%))\n  IF FNstarts_with(elt%, \"splice-unquote\") THEN\n    =FNalloc_list3(FNalloc_symbol(\"concat\"), FNnth(elt%, 1), acc%)\n  ENDIF\n  =FNalloc_list3(FNalloc_symbol(\"cons\"), FNquasiquote(elt%), acc%)\n\nDEF FNquasiquote(ast%)\n  IF FNstarts_with(ast%, \"unquote\") THEN =FNnth(ast%, 1)\n  IF FNis_list(ast%) THEN =FNqq_elts(ast%)\n  IF FNis_vector(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"vec\"), FNqq_elts(ast%))\n  ENDIF\n  IF FNis_symbol(ast%) OR FNis_hashmap(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"quote\"), ast%)\n  ENDIF\n  =ast%\n\nDEF FNEVAL(ast%, env%)\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n    LOCAL car%, val%, bindings%, key$\n31416 REM tail call optimization loop\n    PROCgc_keep_only2(ast%, env%)\n    val% = FNenv_find(env%, \"DEBUG-EVAL\")\n    IF NOT FNis_nil(val%) THEN\n      IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n        PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n      ENDIF\n    ENDIF\n    IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n    IF FNis_hashmap(ast%) THEN\n      val% = FNempty_hashmap\n      bindings% = FNhashmap_keys(ast%)\n      WHILE NOT FNis_empty(bindings%)\n        key$ = FNunbox_string(FNfirst(bindings%))\n        val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n        bindings% = FNrest(bindings%)\n      ENDWHILE\n      =val%\n    ENDIF\n    IF NOT FNis_seq(ast%) THEN =ast%\n    IF FNis_empty(ast%) THEN =ast%\n    car% = FNfirst(ast%)\n    IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n    IF FNis_symbol(car%) THEN\n      key$ = FNunbox_symbol(car%)\n      CASE key$ OF\n        REM  Special forms\n        WHEN \"def!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"defmacro!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          IF FNis_fn(val%) THEN val% = FNas_macro(val%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"let*\"\n          env% = FNalloc_environment(env%)\n          bindings% = FNnth(ast%, 1)\n          WHILE NOT FNis_empty(bindings%)\n            PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n            bindings% = FNrest(FNrest(bindings%))\n          ENDWHILE\n          ast% = FNnth(ast%, 2)\n          GOTO 31416\n        WHEN \"do\"\n          REM  The guide has us call FNeval_ast on the sub-list that excludes\n          REM  the last element of ast%, but that's a bit painful without\n          REM  native list slicing, so it's easier to just re-implement the\n          REM  bit of FNeval_ast that we need.\n          ast% = FNrest(ast%)\n          WHILE NOT FNis_empty(FNrest(ast%))\n            val% = FNEVAL(FNfirst(ast%), env%)\n            ast% = FNrest(ast%)\n          ENDWHILE\n          ast% = FNfirst(ast%)\n          GOTO 31416\n        WHEN \"if\"\n          IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN\n            ast% = FNnth(ast%, 2)\n          ELSE\n            IF FNcount(ast%) = 3 THEN =FNnil\n            ast% = FNnth(ast%, 3)\n          ENDIF\n          GOTO 31416\n        WHEN \"fn*\"\n          =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n        WHEN \"quote\"\n          =FNnth(ast%, 1)\n        WHEN \"quasiquote\"\n          ast% = FNquasiquote(FNnth(ast%, 1))\n          GOTO 31416\n        OTHERWISE\n          car% = FNenv_get(env%, key$)\n      ENDCASE\n    ELSE\n      car% = FNEVAL(car%, env%)\n    ENDIF\n    REM  This is the \"apply\" part.\n    ast% = FNrest(ast%)\n    IF FNis_macro(car%) THEN\n      ast% = FNEVAL(FNfn_ast(car%), FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%))\n      GOTO 31416\n    ENDIF\n    ast% = FNeval_ast(ast%, env%)\n    IF FNis_corefn(car%) THEN\n      =FNcore_call(FNunbox_corefn(car%), ast%)\n    ENDIF\n    IF FNis_fn(car%) THEN\n      env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n      ast% = FNfn_ast(car%)\n      GOTO 31416\n    ENDIF\n    ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nDEF FNget_argv\n  PROCgc_enter\n  LOCAL argv%, rargv%, cmdptr%, arg$, len%\n  argv% = FNempty\n  IF !PAGE = &D7C1C7C5 THEN\n    REM  Running under Brandy, so ARGC and ARGV$ are usable.\n    IF ARGC >= 1 THEN\n      FOR i% = ARGC TO 1 STEP -1\n        argv% = FNalloc_pair(FNalloc_string(ARGV$(i%)), argv%)\n      NEXT i%\n    ENDIF\n  ELSE\n    IF (INKEY(-256) AND &F0) = &A0 THEN\n      rargv% = FNempty\n      REM  Running under RISC OS\n      REM  Vexingly, we can only get the command line that was passed to\n      REM  the BASIC interpreter.  This means that we need to extract\n      REM  the arguments from that.  Typically, we will have been started\n      REM  with \"BASIC -quit <filename> <args>\".\n\n      DIM q% 256\n      SYS \"OS_GetEnv\" TO cmdptr%\n      WHILE ?cmdptr% >= 32\n        SYS \"OS_GSTrans\", cmdptr%, q%, &20000000 + 256 TO cmdptr%, , len%\n        q%?len% = 13\n        rargv% = FNalloc_pair(FNalloc_string($q%), rargv%)\n      ENDWHILE\n      REM  Put argv back into the right order.\n      WHILE NOT FNis_empty(rargv%)\n        argv% = FNalloc_pair(FNfirst(rargv%), argv%)\n        rargv% = FNrest(rargv%)\n      ENDWHILE\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"BASIC\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      IF FNunbox_string(FNfirst(argv%)) <> \"-quit\" THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"-quit\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip filename\n    ENDIF\n  ENDIF\n=FNgc_exit(argv%)\n\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/step9_try.bas",
    "content": "REM Step 9 of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nREM  Initial forms to evaluate\nRESTORE +0\nDATA (def! not (fn* (a) (if a false true)))\nDATA (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nDATA (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\nDATA \"\"\nREPEAT\n  READ form$\n  IF form$ <> \"\" THEN val$ = FNrep(form$)\nUNTIL form$ = \"\"\n\nargv% = FNget_argv\n\nIF FNis_empty(argv%) THEN\n  PROCenv_set(repl_env%, \"*ARGV*\", FNempty)\nELSE\n  PROCenv_set(repl_env%, \"*ARGV*\", FNrest(argv%))\n  val$ = FNrep(\"(load-file \" + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + \")\")\n  END\nENDIF\n\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNstarts_with(ast%, sym$)\n  LOCAL a0%\n  IF NOT FNis_list(ast%) THEN =FALSE\n  a0% = FNfirst(ast%)\n  IF NOT FNis_symbol(a0%) THEN =FALSE\n  =FNunbox_symbol(a0%) = sym$\n\nDEF FNqq_elts(seq%)\n  LOCAL elt%, acc%\n  IF FNis_empty(seq%) THEN =FNempty\n  elt% = FNfirst(seq%)\n  acc% = FNqq_elts(FNrest(seq%))\n  IF FNstarts_with(elt%, \"splice-unquote\") THEN\n    =FNalloc_list3(FNalloc_symbol(\"concat\"), FNnth(elt%, 1), acc%)\n  ENDIF\n  =FNalloc_list3(FNalloc_symbol(\"cons\"), FNquasiquote(elt%), acc%)\n\nDEF FNquasiquote(ast%)\n  IF FNstarts_with(ast%, \"unquote\") THEN =FNnth(ast%, 1)\n  IF FNis_list(ast%) THEN =FNqq_elts(ast%)\n  IF FNis_vector(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"vec\"), FNqq_elts(ast%))\n  ENDIF\n  IF FNis_symbol(ast%) OR FNis_hashmap(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"quote\"), ast%)\n  ENDIF\n  =ast%\n\nDEF FNtry_catch(ast%, env%)\n  LOCAL is_error%, ret%\n  REM  If there's no 'catch*' clause then we just evaluate the 'try*'.\n  IF FNcount(ast%) < 3 THEN =FNEVAL(FNnth(ast%, 1), env%)\n  IF FNunbox_symbol(FNfirst(FNnth(ast%, 2))) <> \"catch*\" THEN\n    ERROR &40E80924, \"Invalid 'catch*' clause\"\n  ENDIF\n  ret% = FNtry(FNnth(ast%, 1), env%, is_error%)\n  IF is_error% THEN =FNcatch(FNnth(ast%, 2), env%, ret%)\n=ret%\n\nREM  Evaluate an expression, returning either the result or an exception\nREM  raised during evaluation.  is_error% indicates which it was.\nDEF FNtry(ast%, env%, RETURN is_error%)\n  LOCAL trysav%\n  trysav% = FNgc_save\n  is_error% = FALSE\n  LOCAL ERROR\n  ON ERROR LOCAL is_error% = TRUE : =FNgc_restore(trysav%, FNwrap_exception)\n=FNgc_restore(trysav%, FNEVAL(ast%, env%))\n\nREM  Return a mal value corresponding to the most-recently thrown exception.\nDEF FNwrap_exception\n  REM  There are three cases to handle.  When the error was generated\n  REM  by 'throw', we should return the value that 'throw' stashed in\n  REM  MAL_ERR%.  When the error was generated by mal, we should just\n  REM  return the error message.  When the error was generated by BASIC\n  REM  or the OS, we should wrap the message and the error number in\n  REM  a hash-map.\n  IF ERR = &40E80900 THEN =MAL_ERR% : REM  Error generated by 'throw'\n  IF (ERR AND &FFFFFF00) = &40E80900 THEN =FNalloc_string(REPORT$)\n  LOCAL e%\n  e% = FNhashmap_set(FNempty_hashmap, \"err\", FNalloc_int(ERR))\n=FNhashmap_set(e%, \"message\", FNalloc_string(REPORT$))\n\nDEF FNcatch(ast%, env%, err%)\n  LOCAL binds%, exprs%\n  binds% = FNalloc_pair(FNnth(ast%, 1), FNempty)\n  exprs% = FNalloc_pair(err%, FNempty)\n  env% = FNnew_env(env%, binds%, exprs%)\n=FNEVAL(FNnth(ast%, 2), env%)\n\nDEF FNEVAL(ast%, env%)\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n    LOCAL car%, val%, bindings%, key$\n31416 REM tail call optimization loop\n    PROCgc_keep_only2(ast%, env%)\n    val% = FNenv_find(env%, \"DEBUG-EVAL\")\n    IF NOT FNis_nil(val%) THEN\n      IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n        PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n      ENDIF\n    ENDIF\n    IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n    IF FNis_hashmap(ast%) THEN\n      val% = FNempty_hashmap\n      bindings% = FNhashmap_keys(ast%)\n      WHILE NOT FNis_empty(bindings%)\n        key$ = FNunbox_string(FNfirst(bindings%))\n        val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n        bindings% = FNrest(bindings%)\n      ENDWHILE\n      =val%\n    ENDIF\n    IF NOT FNis_seq(ast%) THEN =ast%\n    IF FNis_empty(ast%) THEN =ast%\n    car% = FNfirst(ast%)\n    IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n    IF FNis_symbol(car%) THEN\n      key$ = FNunbox_symbol(car%)\n      CASE key$ OF\n        REM  Special forms\n        WHEN \"def!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"defmacro!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          IF FNis_fn(val%) THEN val% = FNas_macro(val%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"let*\"\n          env% = FNalloc_environment(env%)\n          bindings% = FNnth(ast%, 1)\n          WHILE NOT FNis_empty(bindings%)\n            PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n            bindings% = FNrest(FNrest(bindings%))\n          ENDWHILE\n          ast% = FNnth(ast%, 2)\n          GOTO 31416\n        WHEN \"do\"\n          REM  The guide has us call FNeval_ast on the sub-list that excludes\n          REM  the last element of ast%, but that's a bit painful without\n          REM  native list slicing, so it's easier to just re-implement the\n          REM  bit of FNeval_ast that we need.\n          ast% = FNrest(ast%)\n          WHILE NOT FNis_empty(FNrest(ast%))\n            val% = FNEVAL(FNfirst(ast%), env%)\n            ast% = FNrest(ast%)\n          ENDWHILE\n          ast% = FNfirst(ast%)\n          GOTO 31416\n        WHEN \"if\"\n          IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN\n            ast% = FNnth(ast%, 2)\n          ELSE\n            IF FNcount(ast%) = 3 THEN =FNnil\n            ast% = FNnth(ast%, 3)\n          ENDIF\n          GOTO 31416\n        WHEN \"fn*\"\n          =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n        WHEN \"quote\"\n          =FNnth(ast%, 1)\n        WHEN \"quasiquote\"\n          ast% = FNquasiquote(FNnth(ast%, 1))\n          GOTO 31416\n        WHEN \"try*\"\n          =FNtry_catch(ast%, env%)\n        OTHERWISE\n          car% = FNenv_get(env%, key$)\n      ENDCASE\n    ELSE\n      car% = FNEVAL(car%, env%)\n    ENDIF\n    REM  This is the \"apply\" part.\n    ast% = FNrest(ast%)\n    IF FNis_macro(car%) THEN\n      ast% = FNEVAL(FNfn_ast(car%), FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%))\n      GOTO 31416\n    ENDIF\n    ast% = FNeval_ast(ast%, env%)\n    IF FNis_corefn(car%) THEN\n      =FNcore_call(FNunbox_corefn(car%), ast%)\n    ENDIF\n    IF FNis_fn(car%) THEN\n      env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n      ast% = FNfn_ast(car%)\n      GOTO 31416\n    ENDIF\n    ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nDEF FNget_argv\n  PROCgc_enter\n  LOCAL argv%, rargv%, cmdptr%, arg$, len%\n  argv% = FNempty\n  IF !PAGE = &D7C1C7C5 THEN\n    REM  Running under Brandy, so ARGC and ARGV$ are usable.\n    IF ARGC >= 1 THEN\n      FOR i% = ARGC TO 1 STEP -1\n        argv% = FNalloc_pair(FNalloc_string(ARGV$(i%)), argv%)\n      NEXT i%\n    ENDIF\n  ELSE\n    IF (INKEY(-256) AND &F0) = &A0 THEN\n      rargv% = FNempty\n      REM  Running under RISC OS\n      REM  Vexingly, we can only get the command line that was passed to\n      REM  the BASIC interpreter.  This means that we need to extract\n      REM  the arguments from that.  Typically, we will have been started\n      REM  with \"BASIC -quit <filename> <args>\".\n\n      DIM q% 256\n      SYS \"OS_GetEnv\" TO cmdptr%\n      WHILE ?cmdptr% >= 32\n        SYS \"OS_GSTrans\", cmdptr%, q%, &20000000 + 256 TO cmdptr%, , len%\n        q%?len% = 13\n        rargv% = FNalloc_pair(FNalloc_string($q%), rargv%)\n      ENDWHILE\n      REM  Put argv back into the right order.\n      WHILE NOT FNis_empty(rargv%)\n        argv% = FNalloc_pair(FNfirst(rargv%), argv%)\n        rargv% = FNrest(rargv%)\n      ENDWHILE\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"BASIC\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      IF FNunbox_string(FNfirst(argv%)) <> \"-quit\" THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"-quit\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip filename\n    ENDIF\n  ENDIF\n=FNgc_exit(argv%)\n\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/stepA_mal.bas",
    "content": "REM Step A of mal in BBC BASIC\n\nLIBRARY \"types\"\nLIBRARY \"reader\"\nLIBRARY \"printer\"\nLIBRARY \"env\"\nLIBRARY \"core\"\n\nPROCtypes_init\n\nrepl_env% = FNalloc_environment(FNnil)\nPROCcore_ns : REM This sets the data pointer\nREPEAT\n  READ sym$, i%\n  IF sym$ <> \"\" THEN\n    PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))\n  ENDIF\nUNTIL sym$ = \"\"\n\nREM  Initial forms to evaluate\nRESTORE +0\nDATA (def! not (fn* (a) (if a false true)))\nDATA (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nDATA (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\nDATA (def! *host-language* \"BBC BASIC V\")\nDATA \"\"\nREPEAT\n  READ form$\n  IF form$ <> \"\" THEN val$ = FNrep(form$)\nUNTIL form$ = \"\"\n\nargv% = FNget_argv\n\nIF FNis_empty(argv%) THEN\n  PROCenv_set(repl_env%, \"*ARGV*\", FNempty)\nELSE\n  PROCenv_set(repl_env%, \"*ARGV*\", FNrest(argv%))\n  val$ = FNrep(\"(load-file \" + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + \")\")\n  END\nENDIF\n\nval$ = FNrep(\"(println (str \"\"Mal [\"\" *host-language* \"\"]\"\"))\")\nsav% = FNgc_save\nREPEAT\n  REM  Catch all errors apart from \"Escape\".\n  ON ERROR LOCAL IF ERR = 17 ON ERROR OFF: ERROR ERR, REPORT$ ELSE PRINT REPORT$\n  PROCgc_restore(sav%)\n  sav% = FNgc_save\n  PRINT \"user> \";\n  LINE INPUT \"\" line$\n  PRINT FNrep(line$)\nUNTIL FALSE\n\nEND\n\nDEF FNREAD(a$)\n=FNread_str(FNalloc_string(a$))\n\nDEF FNstarts_with(ast%, sym$)\n  LOCAL a0%\n  IF NOT FNis_list(ast%) THEN =FALSE\n  a0% = FNfirst(ast%)\n  IF NOT FNis_symbol(a0%) THEN =FALSE\n  =FNunbox_symbol(a0%) = sym$\n\nDEF FNqq_elts(seq%)\n  LOCAL elt%, acc%\n  IF FNis_empty(seq%) THEN =FNempty\n  elt% = FNfirst(seq%)\n  acc% = FNqq_elts(FNrest(seq%))\n  IF FNstarts_with(elt%, \"splice-unquote\") THEN\n    =FNalloc_list3(FNalloc_symbol(\"concat\"), FNnth(elt%, 1), acc%)\n  ENDIF\n  =FNalloc_list3(FNalloc_symbol(\"cons\"), FNquasiquote(elt%), acc%)\n\nDEF FNquasiquote(ast%)\n  IF FNstarts_with(ast%, \"unquote\") THEN =FNnth(ast%, 1)\n  IF FNis_list(ast%) THEN =FNqq_elts(ast%)\n  IF FNis_vector(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"vec\"), FNqq_elts(ast%))\n  ENDIF\n  IF FNis_symbol(ast%) OR FNis_hashmap(ast%) THEN\n    =FNalloc_list2(FNalloc_symbol(\"quote\"), ast%)\n  ENDIF\n  =ast%\n\nDEF FNtry_catch(ast%, env%)\n  LOCAL is_error%, ret%\n  REM  If there's no 'catch*' clause then we just evaluate the 'try*'.\n  IF FNcount(ast%) < 3 THEN =FNEVAL(FNnth(ast%, 1), env%)\n  IF FNunbox_symbol(FNfirst(FNnth(ast%, 2))) <> \"catch*\" THEN\n    ERROR &40E80924, \"Invalid 'catch*' clause\"\n  ENDIF\n  ret% = FNtry(FNnth(ast%, 1), env%, is_error%)\n  IF is_error% THEN =FNcatch(FNnth(ast%, 2), env%, ret%)\n=ret%\n\nREM  Evaluate an expression, returning either the result or an exception\nREM  raised during evaluation.  is_error% indicates which it was.\nDEF FNtry(ast%, env%, RETURN is_error%)\n  LOCAL trysav%\n  trysav% = FNgc_save\n  is_error% = FALSE\n  LOCAL ERROR\n  ON ERROR LOCAL is_error% = TRUE : =FNgc_restore(trysav%, FNwrap_exception)\n=FNgc_restore(trysav%, FNEVAL(ast%, env%))\n\nREM  Return a mal value corresponding to the most-recently thrown exception.\nDEF FNwrap_exception\n  REM  There are three cases to handle.  When the error was generated\n  REM  by 'throw', we should return the value that 'throw' stashed in\n  REM  MAL_ERR%.  When the error was generated by mal, we should just\n  REM  return the error message.  When the error was generated by BASIC\n  REM  or the OS, we should wrap the message and the error number in\n  REM  a hash-map.\n  IF ERR = &40E80900 THEN =MAL_ERR% : REM  Error generated by 'throw'\n  IF (ERR AND &FFFFFF00) = &40E80900 THEN =FNalloc_string(REPORT$)\n  LOCAL e%\n  e% = FNhashmap_set(FNempty_hashmap, \"err\", FNalloc_int(ERR))\n=FNhashmap_set(e%, \"message\", FNalloc_string(REPORT$))\n\nDEF FNcatch(ast%, env%, err%)\n  LOCAL binds%, exprs%\n  binds% = FNalloc_pair(FNnth(ast%, 1), FNempty)\n  exprs% = FNalloc_pair(err%, FNempty)\n  env% = FNnew_env(env%, binds%, exprs%)\n=FNEVAL(FNnth(ast%, 2), env%)\n\nDEF FNEVAL(ast%, env%)\n  PROCgc_enter\n=FNgc_exit(FNEVAL_(ast%, env%))\n\nDEF FNEVAL_(ast%, env%)\n    LOCAL car%, val%, bindings%, key$\n31416 REM tail call optimization loop\n    PROCgc_keep_only2(ast%, env%)\n    val% = FNenv_find(env%, \"DEBUG-EVAL\")\n    IF NOT FNis_nil(val%) THEN\n      IF FNis_truish(FNenv_get(val%, \"DEBUG-EVAL\")) THEN\n        PRINT \"EVAL: \" + FNunbox_string(FNpr_str(ast%, TRUE))\n      ENDIF\n    ENDIF\n    IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))\n    IF FNis_hashmap(ast%) THEN\n      val% = FNempty_hashmap\n      bindings% = FNhashmap_keys(ast%)\n      WHILE NOT FNis_empty(bindings%)\n        key$ = FNunbox_string(FNfirst(bindings%))\n        val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))\n        bindings% = FNrest(bindings%)\n      ENDWHILE\n      =val%\n    ENDIF\n    IF NOT FNis_seq(ast%) THEN =ast%\n    IF FNis_empty(ast%) THEN =ast%\n    car% = FNfirst(ast%)\n    IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))\n    IF FNis_symbol(car%) THEN\n      key$ = FNunbox_symbol(car%)\n      CASE key$ OF\n        REM  Special forms\n        WHEN \"def!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"defmacro!\"\n          val% = FNEVAL(FNnth(ast%, 2), env%)\n          IF FNis_fn(val%) THEN val% = FNas_macro(val%)\n          PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)\n          =val%\n        WHEN \"let*\"\n          env% = FNalloc_environment(env%)\n          bindings% = FNnth(ast%, 1)\n          WHILE NOT FNis_empty(bindings%)\n            PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))\n            bindings% = FNrest(FNrest(bindings%))\n          ENDWHILE\n          ast% = FNnth(ast%, 2)\n          GOTO 31416\n        WHEN \"do\"\n          REM  The guide has us call FNeval_ast on the sub-list that excludes\n          REM  the last element of ast%, but that's a bit painful without\n          REM  native list slicing, so it's easier to just re-implement the\n          REM  bit of FNeval_ast that we need.\n          ast% = FNrest(ast%)\n          WHILE NOT FNis_empty(FNrest(ast%))\n            val% = FNEVAL(FNfirst(ast%), env%)\n            ast% = FNrest(ast%)\n          ENDWHILE\n          ast% = FNfirst(ast%)\n          GOTO 31416\n        WHEN \"if\"\n          IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN\n            ast% = FNnth(ast%, 2)\n          ELSE\n            IF FNcount(ast%) = 3 THEN =FNnil\n            ast% = FNnth(ast%, 3)\n          ENDIF\n          GOTO 31416\n        WHEN \"fn*\"\n          =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)\n        WHEN \"quote\"\n          =FNnth(ast%, 1)\n        WHEN \"quasiquote\"\n          ast% = FNquasiquote(FNnth(ast%, 1))\n          GOTO 31416\n        WHEN \"try*\"\n          =FNtry_catch(ast%, env%)\n        OTHERWISE\n          car% = FNenv_get(env%, key$)\n      ENDCASE\n    ELSE\n      car% = FNEVAL(car%, env%)\n    ENDIF\n    REM  This is the \"apply\" part.\n    ast% = FNrest(ast%)\n    IF FNis_macro(car%) THEN\n      ast% = FNEVAL(FNfn_ast(car%), FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%))\n      GOTO 31416\n    ENDIF\n    ast% = FNeval_ast(ast%, env%)\n    IF FNis_corefn(car%) THEN\n      =FNcore_call(FNunbox_corefn(car%), ast%)\n    ENDIF\n    IF FNis_fn(car%) THEN\n      env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)\n      ast% = FNfn_ast(car%)\n      GOTO 31416\n    ENDIF\n    ERROR &40E80918, \"Not a function\"\n\nDEF FNPRINT(a%)\n=FNunbox_string(FNpr_str(a%, TRUE))\n\nDEF FNrep(a$)\n=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))\n\nDEF FNeval_ast(ast%, env%)\n    IF FNis_empty(ast%) THEN =ast%\n    =FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))\n\nDEF FNget_argv\n  PROCgc_enter\n  LOCAL argv%, rargv%, cmdptr%, arg$, len%\n  argv% = FNempty\n  IF !PAGE = &D7C1C7C5 THEN\n    REM  Running under Brandy, so ARGC and ARGV$ are usable.\n    IF ARGC >= 1 THEN\n      FOR i% = ARGC TO 1 STEP -1\n        argv% = FNalloc_pair(FNalloc_string(ARGV$(i%)), argv%)\n      NEXT i%\n    ENDIF\n  ELSE\n    IF (INKEY(-256) AND &F0) = &A0 THEN\n      rargv% = FNempty\n      REM  Running under RISC OS\n      REM  Vexingly, we can only get the command line that was passed to\n      REM  the BASIC interpreter.  This means that we need to extract\n      REM  the arguments from that.  Typically, we will have been started\n      REM  with \"BASIC -quit <filename> <args>\".\n\n      DIM q% 256\n      SYS \"OS_GetEnv\" TO cmdptr%\n      WHILE ?cmdptr% >= 32\n        SYS \"OS_GSTrans\", cmdptr%, q%, &20000000 + 256 TO cmdptr%, , len%\n        q%?len% = 13\n        rargv% = FNalloc_pair(FNalloc_string($q%), rargv%)\n      ENDWHILE\n      REM  Put argv back into the right order.\n      WHILE NOT FNis_empty(rargv%)\n        argv% = FNalloc_pair(FNfirst(rargv%), argv%)\n        rargv% = FNrest(rargv%)\n      ENDWHILE\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"BASIC\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      IF FNunbox_string(FNfirst(argv%)) <> \"-quit\" THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip \"-quit\"\n      IF FNis_empty(argv%) THEN =FNgc_exit(argv%)\n      argv% = FNrest(argv%) : REM skip filename\n    ENDIF\n  ENDIF\n=FNgc_exit(argv%)\n\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/bbc-basic/types.bas",
    "content": "REM > types library for mal in BBC BASIC\n\nREM  This library should be the only thing that understands the\nREM  implementation of mal data types in BBC BASIC.  All other\nREM  code should use routines in this library to access them.\n\nREM  As far as other code is concerned, a mal object is just an\nREM  opaque 32-bit integer, which might be a pointer, or might not.\n\nREM  All mal objects live in an array, Z%(), with string values held\nREM  in a parallel array, Z$().  There's one row in Z%(), and one\nREM  entry in Z$(), for each mal object.\n\nREM  Z%(x,0) holds the type of an object and other small amounts of\nREM  information.  The bottom bit indicates the semantics of Z%(x,1):\n\nREM  &01 : Z%(x,1) is a pointer into Z%()\n\nREM  Z%(x,2) and Z%(x,3) are always pointers into Z%(), to 'nil' if nothing\nREM  else.\n\nREM  The &40 bit is used to distinguish empty lists, vectors and hash-maps.\nREM  The &80 bit distinguishes vectors from lists and macros from functions.\n\nREM  sS%() is a shadow stack, used to keep track of which mal values might\nREM  be referenced from local variables at a given depth of the BASIC call\nREM  stack.  It grows upwards.  sSP% points to the first unused word.  sFP%\nREM  points to the start of the current shadow stack frame.  The first word\nREM  of each shadow stack frame is the saved value of sFP%.  The rest are\nREM  mal values.\n\nREM  Types are:\nREM  &00  nil\nREM  &04  boolean\nREM  &08  integer\nREM  &0C  core function\nREM  &01  atom\nREM  &05  free block\nREM  &09  list/vector (each object is a cons cell)\nREM  &0D  environment\nREM  &11  hash-map internal node\nREM  &15  mal function (first part)\nREM  &19  mal function (second part)\nREM  &02  string/keyword\nREM  &06  symbol\nREM  &0A  hash-map leaf node\n\nREM  Formats of individual objects are defined below.\n\nDEF PROCtypes_init\n  REM  Mal's heap has to be statically dimensioned, but we also\n  REM  need to leave enough space for BASIC's stack and heap.\n  REM  The BASIC heap is where all strings live.\n  REM\n  REM  Each row of Z%() consumes 16 bytes.  The size of each entry\n  REM  in Z$() varies by platform: 5 bytes in ARM BBC BASIC V,\n  REM  8 bytes in Brandy on a 32-bit system, 16 bytes in Brandy on\n  REM  a 64-bit system.\n\n  DIM Z%((HIMEM-LOMEM)/110,3), Z$((HIMEM-LOMEM)/110)\n  DIM sS%((HIMEM-LOMEM)/64)\n\n  Z%(1,0) = &04 : REM false\n  Z%(2,0) = &04 : Z%(2,1) = TRUE : REM true\n  Z%(3,0) = &49 : Z%(3,1) = 3 : REM empty list\n  Z%(4,0) = &C9 : Z%(4,1) = 4 : REM empty vector\n  Z%(5,0) = &51 : REM empty hashmap\n  next_Z% = 6\n  sSP% = 1\n  sFP% = 0\n  F% = 0\nENDPROC\n\nDEF FNtype_of(val%)\n=Z%(val%,0) AND &1F\n\nDEF PROCgc_enter\n  REM PRINT ;sFP%;\n  sS%(sSP%) = sFP%\n  sFP% = sSP%\n  sSP% += 1\n  REM PRINT \" >>> \";sFP%\nENDPROC\n\nREM  FNgc_save is equivalent to PROCgc_enter except that it returns a\nREM  value that can be passed to PROCgc_restore to pop all the stack\nREM  frames back to (and including) the one pushed by FNgc_save.\nDEF FNgc_save\n  PROCgc_enter\n=sFP%\n\nDEF PROCgc_exit\n  REM PRINT ;sS%(sFP%);\" <<< \";sFP%\n  sSP% = sFP%\n  sFP% = sS%(sFP%)\nENDPROC\n\nDEF PROCgc_restore(oldFP%)\n  sFP% = oldFP%\n  REM PRINT \"!!! FP reset\"\n  PROCgc_exit\nENDPROC\n\nDEF FNref_local(val%)\n  sS%(sSP%) = val%\n  sSP% += 1\n=val%\n\nDEF FNgc_exit(val%)\n  PROCgc_exit\n=FNref_local(val%)\n\nDEF FNgc_restore(oldFP%, val%)\n  PROCgc_restore(oldFP%)\n=FNref_local(val%)\n\nDEF PROCgc_keep_only2(val1%, val2%)\n  PROCgc_exit\n  PROCgc_enter\n  val1% = FNref_local(val1%)\n  val2% = FNref_local(val2%)\nENDPROC\n\nDEF FNmalloc(type%)\n  LOCAL val%\n  REM  If the heap is full, collect garbage first.\n  IF F% = 0 AND next_Z% > DIM(Z%(),1) THEN\n    PROCgc\n    IF F% = 0 ERROR &40E80950, \"Out of mal heap memory\"\n  ENDIF\n  IF F% <> 0 THEN\n    val% = F%\n    F% = Z%(val%,1)\n  ELSE\n    val% = next_Z%\n    next_Z% += 1\n  ENDIF\n  Z%(val%,0) = type%\n=FNref_local(val%)\n\nDEF PROCfree(val%)\n  Z%(val%,0) = &05\n  Z%(val%,1) = F%\n  Z%(val%,2) = 0\n  Z%(val%,3) = 0\n  Z$(val%) = \"\"\n  F% = val%\nENDPROC\n\nDEF PROCgc\n  REM PRINT \"** START GC **\"\n  PROCgc_markall\n  PROCgc_sweep\n  REM PRINT \"** FINISH GC **\"\nENDPROC\n\nDEF PROCgc_markall\n  LOCAL sp%, fp%\n  fp% = sFP%\n  REM PRINT \">>marking...\";\n  FOR sp% = sSP% - 1 TO 0 STEP -1\n    IF sp% = fp% THEN\n      fp% = sS%(sp%)\n      REM PRINT \" / \";\n    ELSE PROCgc_mark(sS%(sp%))\n    ENDIF\n  NEXT sp%\n  REM PRINT\nENDPROC\n\nDEF PROCgc_mark(val%)\n  IF (Z%(val%,0) AND &100) = 0 THEN\n    REM PRINT \" \";val%;\n    Z%(val%,0) += &100\n    IF (Z%(val%,0) AND &01) THEN PROCgc_mark(Z%(val%,1))\n    PROCgc_mark(Z%(val%,2))\n    PROCgc_mark(Z%(val%,3))\n  ENDIF\nENDPROC\n\nDEF PROCgc_sweep\n  LOCAL val%\n  REM PRINT \">>sweeping ...\";\n  FOR val% = 6 TO next_Z% - 1\n    IF FNtype_of(val%) <> &05 AND (Z%(val%,0) AND &100) = 0 THEN\n      REM PRINT \" \";val%;\n      PROCfree(val%)\n    ELSE\n      Z%(val%,0) -= &100\n    ENDIF\n  NEXT val%\n  REM PRINT\nENDPROC\n\nDEF FNmeta(val%)\n=Z%(val%,3)\n\nDEF FNwith_meta(val%, meta%)\n  LOCAL newval%\n  newval% = FNmalloc(Z%(val%,0))\n  Z%(newval%,1) = Z%(val%,1)\n  Z%(newval%,2) = Z%(val%,2)\n  Z%(newval%,3) = meta%\n  Z$(newval%) = Z$(val%)\n=newval%\n\nREM ** Nil **\n\nDEF FNis_nil(val%)\n=FNtype_of(val%) = 0\n\nDEF FNnil\n=0\n\nREM ** Boolean **\n\nREM  Z%(x,1) = TRUE or FALSE\n\nDEF FNis_boolean(val%)\n=FNtype_of(val%) = &04\n\nDEF FNalloc_boolean(bval%)\n  IF bval% THEN =2\n=1\n\nDEF FNunbox_boolean(val%)\n  IF NOT FNis_boolean(val%) THEN ERROR &40E80911, \"Not a boolean\"\n=Z%(val%,1)\n\nDEF FNis_truish(val%)\n  IF FNis_nil(val%) THEN =FALSE\n  IF FNis_boolean(val%) THEN =FNunbox_boolean(val%)\n=TRUE\n\nREM ** Integers **\n\nREM  Z%(x,1) = integer value\n\nDEF FNis_int(val%)\n=FNtype_of(val%) = &08\n\nDEF FNalloc_int(ival%)\n  LOCAL val%\n  val% = FNmalloc(&08)\n  Z%(val%,1) = ival%\n=val%\n\nDEF FNunbox_int(val%)\n  IF NOT FNis_int(val%) THEN ERROR &40E80912, \"Not an integer\"\n=Z%(val%,1)\n\nREM ** Strings and keywords **\n\nREM  Z$(x) is the string value\nREM  Z%(x,2) points to the next part of the string\nREM  A keyword is a string with first character CHR$(127).\n\nDEF FNis_string(val%)\n=FNtype_of(val%) = &02\n\nDEF FNalloc_string(sval$)\n  LOCAL val%\n  val% = FNmalloc(&02)\n  Z$(val%) = sval$\n=val%\n\nDEF FNunbox_string(val%)\n  IF NOT FNis_string(val%) THEN ERROR &40E80914, \"Not a string\"\n  IF NOT FNis_nil(Z%(val%,2)) ERROR &40E80914, \"Cannot unbox a long string\"\n=Z$(val%)\n\nDEF FNstring_append(val%, add$)\n  LOCAL newval%\n  IF NOT FNis_string(val%) THEN ERROR &40E80914, \"Not a string\"\n  newval% = FNalloc_string(Z$(val%))\n  IF FNis_nil(Z%(val%,2)) THEN\n    IF LEN(Z$(newval%)) + LEN(add$) <= 255 THEN\n      Z$(newval%) += add$\n    ELSE\n      Z%(newval%,2) = FNalloc_string(add$)\n    ENDIF\n  ELSE\n    Z%(newval%,2) = FNstring_append(Z%(val%,2), add$)\n  ENDIF\n=newval%\n\nDEF FNstring_concat(val%, add%)\n  LOCAL newval%\n  IF NOT FNis_string(val%) THEN ERROR &40E80914, \"Not a string\"\n  IF NOT FNis_string(add%) THEN ERROR &40E80914, \"Not a string\"\n  newval% = FNalloc_string(Z$(val%))\n  IF FNis_nil(Z%(val%,2)) THEN\n    IF LEN(Z$(newval%)) + LEN(Z$(add%)) <= 255 THEN\n      Z$(newval%) += Z$(add%)\n      Z%(newval%,2) = Z%(add%,2)\n    ELSE\n      Z%(newval%,2) = add%\n    ENDIF\n  ELSE\n    Z%(newval%,2) = FNstring_concat(Z%(val%,2), add%)\n  ENDIF\n=newval%\n\nDEF FNstring_len(val%)\n  LOCAL len%\n  WHILE NOT FNis_nil(val%)\n    len% += LEN(Z$(val%))\n    val% = Z%(val%,2)\n  ENDWHILE\n=len%\n\nDEF FNstring_chr(val%, pos%)\n  WHILE pos% > LEN(Z$(val%))\n    pos% -= LEN(Z$(val%))\n    val% = Z%(val%,2)\n    IF FNis_nil(val%) THEN =\"\"\n  ENDWHILE\n=MID$(Z$(val%), pos%, 1)\n\nREM ** Symbols **\n\nREM  Z$(x) = value of the symbol\n\nDEF FNis_symbol(val%)\n=FNtype_of(val%) = &06\n\nDEF FNalloc_symbol(sval$)\n  LOCAL val%\n  val% = FNmalloc(&06)\n  Z$(val%) = sval$\n=val%\n\nDEF FNunbox_symbol(val%)\n  IF NOT FNis_symbol(val%) THEN ERROR &40E80915, \"Not a symbol\"\n=Z$(val%)\n\nREM ** Lists and vectors **\n\nREM  Lists and vectors are both represented as linked lists: the only\nREM  difference is in the state of the is_vector flag in the head cell\nREM  of the list.  Note that this means that the tail of a list may be\nREM  a vector, and vice versa.  FNas_list and FNas_vector can be used\nREM  to convert a sequence to a particular type as necessary.\n\nREM  Z%(x,0) AND &80 = is_vector flag\nREM  Z%(x,1) = index in Z%() of next pair\nREM  Z%(x,2) = index in Z%() of first element\n\nREM  The empty list is a distinguished value, with elements that match\nREM  the spec of 'first' and 'rest'.\n\nDEF FNempty\n=3\n\nDEF FNempty_vector\n=4\n\nDEF FNalloc_pair(car%, cdr%)\n  LOCAL val%\n  val% = FNmalloc(&09)\n  Z%(val%,2) = car%\n  Z%(val%,1) = cdr%\n=val%\n\nDEF FNalloc_vector_pair(car%, cdr%)\n  LOCAL val%\n  val% = FNalloc_pair(car%, cdr%)\n  Z%(val%,0) = Z%(val%,0) OR &80\n=val%\n\nDEF FNis_empty(val%)\n=(Z%(val%,0) AND &40) = &40\n\nDEF FNis_seq(val%)\n=FNtype_of(val%) = &09\n\nDEF FNis_list(val%)\n=FNtype_of(val%) = &09 AND (Z%(val%, 0) AND &80) = &00\n\nDEF FNis_vector(val%)\n=FNtype_of(val%) = &09 AND (Z%(val%, 0) AND &80) = &80\n\nDEF FNas_list(val%)\n  IF FNis_list(val%) THEN =val%\n  IF FNis_empty(val%) THEN =FNempty\n=FNalloc_pair(FNfirst(val%), FNrest(val%))\n\nDEF FNas_vector(val%)\n  IF FNis_vector(val%) THEN =val%\n  IF FNis_empty(val%) THEN =FNempty_vector\n=FNalloc_vector_pair(FNfirst(val%), FNrest(val%))\n\nDEF FNfirst(val%)\n  IF NOT FNis_seq(val%) THEN ERROR &40E80916, \"Can't get car of non-sequence\"\n=FNref_local(Z%(val%,2))\n\nDEF FNrest(val%)\n  IF NOT FNis_seq(val%) THEN ERROR &40E80916, \"Can't get cdr of non-sequence\"\n=FNref_local(Z%(val%,1))\n\nDEF FNalloc_list2(val0%, val1%)\n  =FNalloc_pair(val0%, FNalloc_pair(val1%, FNempty))\n\nDEF FNalloc_list3(val0%, val1%, val2%)\n  =FNalloc_pair(val0%, FNalloc_pair(val1%, FNalloc_pair(val2%, FNempty)))\n\nDEF FNcount(val%)\n  LOCAL i%\n  WHILE NOT FNis_empty(val%)\n    val% = FNrest(val%)\n    i% += 1\n  ENDWHILE\n= i%\n\nDEF FNnth(val%, n%)\n  WHILE n% > 0\n    IF FNis_empty(val%) THEN ERROR &40E80923, \"Subscript out of range\"\n    val% = FNrest(val%)\n    n% -= 1\n  ENDWHILE\n  IF FNis_empty(val%) THEN ERROR &40E80923, \"Subscript out of range\"\n=FNfirst(val%)\n\nREM ** Core functions **\n\nREM  Z%(x,1) = index of function in FNcore_call\n\nDEF FNis_corefn(val%)\n=FNtype_of(val%) = &0C\n\nDEF FNalloc_corefn(fn%)\n  LOCAL val%\n  val% = FNmalloc(&0C)\n  Z%(val%,1) = fn%\n=val%\n\nDEF FNunbox_corefn(val%)\n  IF NOT FNis_corefn(val%) THEN ERROR &40E80919, \"Not a core function\"\n=Z%(val%,1)\n\nREM ** Hash-maps **\n\nREM  Hash-maps are represented as a crit-bit tree.\n\nREM  An internal node has:\nREM  Z%(x,0) >> 16 = next bit of key to check\nREM  Z%(x,1) = index in Z%() of left child (if next bit of key is 0)\nREM  Z%(x,2) = index in Z%() of right child (if next bit of key is 1)\n\nREM  A leaf node has\nREM  Z$(x) = key\nREM  Z%(x,2) = index in Z%() of value\n\nREM  The empty hash-map is a special value containing no data.\n\nDEF FNempty_hashmap\n=5\n\nDEF FNhashmap_alloc_leaf(key$, val%)\n  LOCAL entry%\n  entry% = FNmalloc(&0A)\n  Z$(entry%) = key$\n  Z%(entry%,2) = val%\n=entry%  \n\nDEF FNhashmap_alloc_node(bit%, left%, right%)\n  LOCAL entry%\n  entry% = FNmalloc(&11)\n  Z%(entry%,0) += (bit% << 16)\n  Z%(entry%,1) = left%\n  Z%(entry%,2) = right%\n=entry%  \n\nDEF FNis_hashmap(val%)\n  LOCAL t%\n  t% = FNtype_of(val%)\n=t% = &11 OR t% = &0A\n\nDEF FNkey_bit(key$, bit%)\n  LOCAL cnum%\n  cnum% = bit% >> 3\n  IF cnum% >= LEN(key$) THEN =FALSE\n=ASC(MID$(key$, cnum% + 1, 1)) AND (&80 >> (bit% AND 7))  \n\nDEF FNkey_bitdiff(key1$, key2$)\n  LOCAL bit%\n  WHILE FNkey_bit(key1$, bit%) = FNkey_bit(key2$, bit%)\n    bit% += 1\n  ENDWHILE\n=bit%\n\nDEF FNhashmap_set(map%, key$, val%)\n  LOCAL bit%, nearest%\n  IF FNis_empty(map%) THEN =FNhashmap_alloc_leaf(key$, val%)\n  nearest% = FNhashmap_find(map%, key$)\n  IF Z$(nearest%) = key$ THEN =FNhashmap_replace(map%, key$, val%)\n  bit% = FNkey_bitdiff(key$, Z$(nearest%))\n=FNhashmap_insert(map%, bit%, key$, val%)\n\nDEF FNhashmap_insert(map%, bit%, key$, val%)\n  LOCAL left%, right%\n  IF FNtype_of(map%) = &11 AND (Z%(map%,0) >> 16) < bit% THEN\n    IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN\n      left% = Z%(map%,1)\n      right% = FNhashmap_insert(Z%(map%,2), bit%, key$, val%)\n    ELSE\n      left% = FNhashmap_insert(Z%(map%,1), bit%, key$, val%)\n      right% = Z%(map%,2)\n    ENDIF\n    =FNhashmap_alloc_node(Z%(map%,0)>>16, left%, right%)\n  ENDIF\n  IF FNkey_bit(key$, bit%) THEN\n    left% = map%\n    right% = FNhashmap_alloc_leaf(key$, val%)\n  ELSE\n    left% = FNhashmap_alloc_leaf(key$, val%)\n    right% = map%\n  ENDIF\n=FNhashmap_alloc_node(bit%, left%, right%)\n\n\nREM  Replace a known-present key in a non-empty hashmap.\nDEF FNhashmap_replace(map%, key$, val%)\n  LOCAL left%, right%\n  IF FNtype_of(map%) = &0A THEN =FNhashmap_alloc_leaf(key$, val%)\n  IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN\n    left% = Z%(map%,1)\n    right% = FNhashmap_replace(Z%(map%,2), key$, val%)\n  ELSE\n    left% = FNhashmap_replace(Z%(map%,1), key$, val%)\n    right% = Z%(map%,2)\n  ENDIF\n=FNhashmap_alloc_node(Z%(map%,0)>>16, left%, right%)\n\nDEF FNhashmap_remove(map%, key$)\n  LOCAL child%\n  IF FNis_empty(map%) THEN =map%\n  IF FNtype_of(map%) = &0A THEN\n    IF Z$(map%) = key$ THEN =FNempty_hashmap\n  ENDIF\n  IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN\n    child% = FNhashmap_remove(Z%(map%,2), key$)\n    IF FNis_empty(child%) THEN =Z%(map%,1)\n    =FNhashmap_alloc_node(Z%(map%,0)>>16, Z%(map%,1), child%)\n  ELSE\n    child% = FNhashmap_remove(Z%(map%,1), key$)\n    IF FNis_empty(child%) THEN =Z%(map%,2)\n    =FNhashmap_alloc_node(Z%(map%,0)>>16, child%, Z%(map%,2))\n  ENDIF\n\nREM  FNhashmap_find finds the nearest entry in a non-empty hash-map to\nREM  the key requested, and returns the entire entry.\nDEF FNhashmap_find(map%, key$)\n  WHILE FNtype_of(map%) = &11\n    IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN map% = Z%(map%,2) ELSE map% = Z%(map%,1)\n  ENDWHILE\n=map%\n\nDEF FNhashmap_get(map%, key$)\n  IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, \"Can't get item from a non-hashmap\"\n  IF FNis_empty(map%) THEN =FNnil\n  map% = FNhashmap_find(map%, key$)\nIF Z$(map%) = key$ THEN =FNref_local(Z%(map%,2)) ELSE =FNnil\n\nDEF FNhashmap_contains(map%, key$)\n  IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, \"Can't get item from a non-hashmap\"\n  IF FNis_empty(map%) THEN =FALSE\n  map% = FNhashmap_find(map%, key$)\n=Z$(map%) = key$\n\nDEF FNhashmap_keys(map%)\n=FNhashmap_keys1(map%, FNempty)\n\nDEF FNhashmap_keys1(map%, acc%)\n  IF FNis_empty(map%) THEN =acc%\n  IF FNtype_of(map%) = &0A THEN\n    =FNalloc_pair(FNalloc_string(Z$(map%)), acc%)\n  ENDIF\n=FNhashmap_keys1(Z%(map%,1), FNhashmap_keys1(Z%(map%,2), acc%))\n\nDEF FNhashmap_vals(map%)\n=FNhashmap_vals1(map%, FNempty)\n\nDEF FNhashmap_vals1(map%, acc%)\n  IF FNis_empty(map%) THEN =acc%\n  IF FNtype_of(map%) = &0A THEN\n    =FNalloc_pair(Z%(map%,2), acc%)\n  ENDIF\n=FNhashmap_vals1(Z%(map%,1), FNhashmap_vals1(Z%(map%,2), acc%))\n\nDEF PROChashmap_dump(map%)\n  IF FNis_empty(map%) THEN\n    PRINT \"[empty]\"\n  ELSE\n    PRINT \"[-----]\"\n    PROChashmap_dump_internal(map%, \"\")\n  ENDIF\nENDPROC\n\nDEF PROChashmap_dump_internal(map%, prefix$)\n  IF FNtype_of(map%) = &0A PRINT prefix$;Z$(map%)\n  IF FNtype_of(map%) = &11 THEN\n    PRINT prefix$;\"<\";Z%(map%,0) >> 16;\">\"\n    PROChashmap_dump_internal(Z%(map%,1), prefix$ + \"L \")\n    PROChashmap_dump_internal(Z%(map%,2), prefix$ + \"R \")\n  ENDIF\nENDPROC\n\nREM ** Functions **\n\nREM  A function is represented by two cells:\nREM  Z%(x,0) AND &80 = is_macro flag\nREM  Z%(x,1) = index in Z%() of ast\nREM  Z%(x,2) = y\n\nREM  Z%(y,1) = index in Z%() of params\nREM  Z%(y,2) = index in Z%() of env\n\nDEF FNis_fn(val%)\n=FNtype_of(val%) = &15\n\nDEF FNis_nonmacro_fn(val%)\n=FNtype_of(val%) = &15 AND (Z%(val%, 0) AND &80) = &00\n\nDEF FNis_macro(val%)\n=FNtype_of(val%) = &15 AND (Z%(val%, 0) AND &80) = &80\n\nDEF FNalloc_fn(ast%, params%, env%)\n  LOCAL val1%, val2%\n  val1% = FNmalloc(&15)\n  Z%(val1%,1) = ast%\n  val2% = FNmalloc(&19)\n  Z%(val1%,2) = val2%\n  Z%(val2%,1) = params%\n  Z%(val2%,2) = env%\n=val1%\n\nDEF FNas_macro(val%)\n  IF NOT FNis_fn(val%) THEN ERROR &40E8091A, \"Not a function\"\n  LOCAL newval%\n  newval% = FNmalloc(Z%(val%,0) OR &80)\n  Z%(newval%,1) = Z%(val%,1)\n  Z%(newval%,2) = Z%(val%,2)\n  Z%(newval%,3) = Z%(val%,3)\n=newval%\n\nDEF FNfn_ast(val%)\n  IF NOT FNis_fn(val%) THEN ERROR &40E8091A, \"Not a function\"\n=FNref_local(Z%(val%,1))\n\nDEF FNfn_params(val%)\n  IF NOT FNis_fn(val%) THEN ERROR &40E8091A, \"Not a function\"\n=FNref_local(Z%(Z%(val%,2),1))\n\nDEF FNfn_env(val%)\n  IF NOT FNis_fn(val%) THEN ERROR &40E8091A, \"Not a function\"\n=FNref_local(Z%(Z%(val%,2),2))\n\nREM ** Atoms **\n\nREM Z%(x,1) = index in Z% of current referent\n\nDEF FNis_atom(val%)\n=FNtype_of(val%) = &01\n\nDEF FNalloc_atom(contents%)\n  LOCAL val%\n  val% = FNmalloc(&01)\n  Z%(val%,1) = contents%\n=val%\n\nDEF FNatom_deref(val%)\n=FNref_local(Z%(val%,1))\n\nDEF PROCatom_reset(val%, contents%)\n  Z%(val%,1) = contents%\nENDPROC\n\nREM ** Environments **\n\nREM  Z%(x,1) = index in Z% of hash-map\nREM  Z%(x,2) = index in Z% of outer environment\n\nDEF FNis_environment(val%)\n=FNtype_of(val%) = &0D\n\nDEF FNalloc_environment(outer%)\n  LOCAL val%\n  val% = FNmalloc(&0D)\n  Z%(val%,1) = FNempty_hashmap\n  Z%(val%,2) = outer%\n=val%\n\nDEF FNenvironment_data(val%)\n  IF NOT FNis_environment(val%) THEN ERROR &40E8091D, \"Not an environment\"\n=FNref_local(Z%(val%,1))\n\nDEF PROCenvironment_set_data(val%, data%)\n  IF NOT FNis_environment(val%) THEN ERROR &40E8091D, \"Not an environment\"\n  Z%(val%,1) = data%\nENDPROC\n\nDEF FNenvironment_outer(val%)\n  IF NOT FNis_environment(val%) THEN ERROR &40E8091D, \"Not an environment\"\n=FNref_local(Z%(val%,2))\n\nREM Local Variables:\nREM indent-tabs-mode: nil\nREM End:\n"
  },
  {
    "path": "impls/c/Dockerfile",
    "content": "FROM ubuntu:vivid\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Install g++ for any C/C++ based implementations\nRUN apt-get -y install g++\n\n# Libraries needed for the C impl\nRUN apt-get -y install libglib2.0 libglib2.0-dev libffi-dev libgc-dev\n"
  },
  {
    "path": "impls/c/Makefile",
    "content": "USE_READLINE ?=\nUSE_GC ?= 1\nCFLAGS ?= -g -O2\nLDFLAGS ?= -g\n\n#####################\n\nSRCS = step0_repl.c step1_read_print.c step2_eval.c step3_env.c \\\n       step4_if_fn_do.c step5_tco.c step6_file.c step7_quote.c \\\n       step8_macros.c step9_try.c stepA_mal.c\nOBJS = $(SRCS:%.c=%.o)\nBINS = $(OBJS:%.o=%)\nOTHER_OBJS = types.o readline.o reader.o printer.o env.o core.o interop.o\nOTHER_HDRS = types.h readline.h reader.h printer.h       core.h interop.h\n\nGLIB_CFLAGS ?= $(shell pkg-config --cflags glib-2.0)\nGLIB_LDFLAGS ?= $(shell pkg-config --libs glib-2.0)\n\nFFI_CFLAGS ?= $(shell pkg-config libffi --cflags)\nFFI_LDFLAGS ?= $(shell pkg-config libffi --libs)\n\nifeq ($(shell uname -s),Darwin)\n  darwin_CPPFLAGS ?= -DOSX=1\nendif\n\nifeq (,$(USE_READLINE))\nRL_LIBRARY ?= edit\nelse\nRL_LIBRARY ?= readline\n  rl_CFLAGS ?= -DUSE_READLINE=1\nendif\n\nifneq (,$(USE_GC))\n  gc_CFLAGS ?= -DUSE_GC=1\n  gc_LIBS ?= -lgc\nendif\n\n# Rewrite CPPFLAGS for the Make recipes, but let existing user options\n# take precedence.\noverride CPPFLAGS := \\\n  ${darwin_CPPFLAGS} ${rl_CFLAGS} ${gc_CFLAGS} ${GLIB_CFLAGS} ${FFI_CFLAGS} \\\n  ${CPPFLAGS}\noverride LDLIBS += \\\n  ${gc_LIBS} -l${RL_LIBRARY} ${GLIB_LDFLAGS} ${FFI_LDFLAGS} -ldl\n\n#####################\n\nall: $(BINS)\n\ndist: mal\n\nmal: $(word $(words $(BINS)),$(BINS))\n\tcp $< $@\n\n$(OBJS) $(OTHER_OBJS): %.o: %.c  $(OTHER_HDRS)\n\n$(patsubst %.o,%,$(filter step%,$(OBJS))): $(OTHER_OBJS)\n$(BINS): %: %.o\n\nclean:\n\trm -f $(OBJS) $(BINS) $(OTHER_OBJS) mal\n"
  },
  {
    "path": "impls/c/core.c",
    "content": "#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"core.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n\n// Errors/Exceptions\nvoid throw(MalVal *obj) {\n    mal_error = obj;\n}\n\n\n// General functions\n\nMalVal *equal_Q(MalVal *a, MalVal *b) {\n    if (_equal_Q(a, b)) { return &mal_true; }\n    else { return &mal_false; }\n}\n\n\n// Misc predicates\n\nMalVal *nil_Q(MalVal *seq) { return seq->type & MAL_NIL ? &mal_true : &mal_false; }\nMalVal *true_Q(MalVal *seq) { return seq->type & MAL_TRUE ? &mal_true : &mal_false; }\nMalVal *false_Q(MalVal *seq) { return seq->type & MAL_FALSE ? &mal_true : &mal_false; }\nMalVal *string_Q(MalVal *seq) {\n    if ((seq->type & MAL_STRING) && (seq->val.string[0] != '\\x7f')) {\n        return &mal_true;\n    } else {\n        return &mal_false;\n    }\n}\nMalVal *number_Q(MalVal *obj) {\n    return obj->type & MAL_INTEGER || obj->type & MAL_FLOAT\n        ? &mal_true\n        : &mal_false;\n}\nMalVal *fn_Q(MalVal *obj) {\n    return (obj->type & MAL_FUNCTION_C || obj->type & MAL_FUNCTION_MAL) &&\n           !obj->ismacro\n        ? &mal_true\n        : &mal_false;\n}\nMalVal *macro_Q(MalVal *obj) { return obj->ismacro ? &mal_true : &mal_false; }\n\n\n\n// Symbol functions\n\nMalVal *symbol(MalVal *args) {\n    assert_type(args, MAL_STRING,\n                \"symbol called with non-string value\");\n    args->type = MAL_SYMBOL; // change string to symbol\n    return args;\n}\n\nMalVal *symbol_Q(MalVal *seq) {\n    return seq->type & MAL_SYMBOL ? &mal_true : &mal_false; }\n\n\n// Keyword functions\n\nMalVal *keyword(MalVal *args) {\n    assert_type(args, MAL_STRING,\n                \"keyword called with non-string value\");\n    if (args->val.string[0] == '\\x7f') {\n        return args;\n    } else {\n        return malval_new_keyword(args->val.string);\n    }\n}\n\nMalVal *keyword_Q(MalVal *seq) {\n    return seq->type & MAL_STRING && seq->val.string[0] == '\\x7f'\n        ? &mal_true\n        : &mal_false;\n}\n\n\n// String functions\n\n// Return a string representation of a MalVal sequence (in a format that can\n// be read by the reader). Returned string must be freed by caller.\nMalVal *pr_str(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"pr_str called with non-sequential args\");\n    return malval_new_string(_pr_str_args(args, \" \", 1));\n}\n\n// Return a string representation of a MalVal sequence with every item\n// concatenated together. Returned string must be freed by caller.\nMalVal *str(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"str called with non-sequential args\");\n    return malval_new_string(_pr_str_args(args, \"\", 0));\n}\n\n// Print a string representation of a MalVal sequence (in a format that can\n// be read by the reader) followed by a newline. Returns nil.\nMalVal *prn(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"prn called with non-sequential args\");\n    char *repr = _pr_str_args(args, \" \", 1);\n    puts(repr);\n    MAL_GC_FREE(repr);\n    return &mal_nil;\n}\n\n// Print a string representation of a MalVal sequence (for human consumption)\n// followed by a newline. Returns nil.\nMalVal *println(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"println called with non-sequential args\");\n    char *repr = _pr_str_args(args, \" \", 0);\n    puts(repr);\n    MAL_GC_FREE(repr);\n    return &mal_nil;\n}\n\nMalVal *mal_readline(MalVal *str) {\n    assert_type(str, MAL_STRING, \"readline of non-string\");\n    char * line = _readline(str->val.string);\n    if (line) { return malval_new_string(line); }\n    else      { return &mal_nil; }\n}\n\nMalVal *read_string(MalVal *str) {\n    assert_type(str, MAL_STRING, \"read_string of non-string\");\n    return read_str(str->val.string);\n}\n\nchar *slurp_raw(char *path) {\n    char *data;\n    struct stat fst;\n    int fd = open(path, O_RDONLY),\n        sz;\n    if (fd < 0) {\n        abort(\"slurp failed to open '%s'\", path);\n    }\n    if (fstat(fd, &fst) < 0) {\n        abort(\"slurp failed to stat '%s'\", path);\n    }\n    data = MAL_GC_MALLOC(fst.st_size+1);\n    sz = read(fd, data, fst.st_size);\n    if (sz < fst.st_size) {\n        abort(\"slurp failed to read '%s'\", path);\n    }\n    data[sz] = '\\0';\n    return data;\n}\nMalVal *slurp(MalVal *path) {\n    assert_type(path, MAL_STRING, \"slurp of non-string\");\n    char *data = slurp_raw(path->val.string);\n    if (!data || mal_error) { return NULL; }\n    return malval_new_string(data);\n}\n\n\n\n\n// Number functions\n\nWRAP_INTEGER_OP(plus,+)\nWRAP_INTEGER_OP(minus,-)\nWRAP_INTEGER_OP(multiply,*)\nWRAP_INTEGER_OP(divide,/)\nWRAP_INTEGER_CMP_OP(gt,>)\nWRAP_INTEGER_CMP_OP(gte,>=)\nWRAP_INTEGER_CMP_OP(lt,<)\nWRAP_INTEGER_CMP_OP(lte,<=)\n\nMalVal *time_ms(MalVal *_) {\n    struct timeval tv;\n    long msecs;\n    gettimeofday(&tv, NULL);\n    msecs = tv.tv_sec * 1000 + tv.tv_usec/1000.0 + 0.5;\n\n    return malval_new_integer(msecs);\n}\n\n\n// List functions\n\nMalVal *list(MalVal *args) { return _list(args); }\nMalVal *list_Q(MalVal *seq) { return _list_Q(seq) ? &mal_true : &mal_false; }\n\n\n// Vector functions\n\nMalVal *vector(MalVal *args) { return _vector(args); }\nMalVal *vector_Q(MalVal *seq) { return _vector_Q(seq) ? &mal_true : &mal_false; }\n\n\n// Hash map functions\n\nMalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; }\n\nMalVal *assoc(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"assoc called with non-sequential arguments\");\n    assert(_count(args) >= 2,\n           \"assoc needs at least 2 arguments\");\n    GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table);\n    MalVal *hm = malval_new_hash_map(htable);\n    return _assoc_BANG(hm, _rest(args));\n}\n\nMalVal *dissoc(MalVal* args) {\n    GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table);\n    MalVal *hm = malval_new_hash_map(htable);\n    return _dissoc_BANG(hm, _rest(args));\n}\n\nMalVal *keys(MalVal *obj) {\n    assert_type(obj, MAL_HASH_MAP,\n                \"keys called on non-hash-map\");\n\n    GHashTableIter iter;\n    gpointer key, value;\n    MalVal *seq = malval_new_list(MAL_LIST,\n                                  g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                    _count(obj)));\n    g_hash_table_iter_init (&iter, obj->val.hash_table);\n    while (g_hash_table_iter_next (&iter, &key, &value)) {\n        MalVal *kname = malval_new_string((char *)key);\n        g_array_append_val(seq->val.array, kname);\n    }\n    return seq;\n}\n\nMalVal *vals(MalVal *obj) {\n    assert_type(obj, MAL_HASH_MAP,\n                \"vals called on non-hash-map\");\n\n    GHashTableIter iter;\n    gpointer key, value;\n    MalVal *seq = malval_new_list(MAL_LIST,\n                                  g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                    _count(obj)));\n    g_hash_table_iter_init (&iter, obj->val.hash_table);\n    while (g_hash_table_iter_next (&iter, &key, &value)) {\n        g_array_append_val(seq->val.array, value);\n    }\n    return seq;\n}\n\n\n// hash map and vector functions\nMalVal *get(MalVal *obj, MalVal *key) {\n    MalVal *val;\n    switch (obj->type) {\n    case MAL_VECTOR:\n        return _nth(obj, key->val.intnum);\n    case MAL_HASH_MAP:\n        if (g_hash_table_lookup_extended(obj->val.hash_table,\n                                         key->val.string,\n                                         NULL, (gpointer*)&val)) {\n            return val;\n        } else {\n            return &mal_nil;\n        }\n    case MAL_NIL:\n        return &mal_nil;\n    default:\n        abort(\"get called on unsupported type %d\", obj->type);\n    }\n}\n\nMalVal *contains_Q(MalVal *obj, MalVal *key) {\n    switch (obj->type) {\n    case MAL_VECTOR:\n        if (key->val.intnum < obj->val.array->len) {\n            return &mal_true;\n        } else {\n            return &mal_false;\n        }\n    case MAL_HASH_MAP:\n        if (g_hash_table_contains(obj->val.hash_table, key->val.string)) {\n            return &mal_true;\n        } else {\n            return &mal_false;\n        }\n    default:\n        abort(\"contains? called on unsupported type %d\", obj->type);\n    }\n}\n\n\n// Sequence functions\n\nMalVal *sequential_Q(MalVal *seq) {\n    return _sequential_Q(seq) ? &mal_true : &mal_false;\n}\n\nMalVal *cons(MalVal *x, MalVal *seq) {\n    assert_type(seq, MAL_LIST|MAL_VECTOR,\n                \"second argument to cons is non-sequential\");\n    int i, len = _count(seq);\n    GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                        len+1);\n    g_array_append_val(new_arr, x);\n    for (i=0; i<len; i++) {\n        g_array_append_val(new_arr, g_array_index(seq->val.array, MalVal*, i));\n    }\n    return malval_new_list(MAL_LIST, new_arr);\n}\n\nMalVal *concat(MalVal *args) {\n    MalVal *arg, *e, *lst;\n    int i, j, arg_cnt = _count(args);\n    lst = malval_new_list(MAL_LIST,\n                        g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), arg_cnt));\n    for (i=0; i<arg_cnt; i++) {\n        arg = g_array_index(args->val.array, MalVal*, i);\n        assert_type(arg, MAL_LIST|MAL_VECTOR,\n                    \"concat called with non-sequential\");\n        for (j=0; j<_count(arg); j++) {\n            e = g_array_index(arg->val.array, MalVal*, j);\n            g_array_append_val(lst->val.array, e);\n        }\n    }\n    return lst;\n}\n\nMalVal *vec(MalVal *seq) {\n    switch(seq->type) {\n    case MAL_VECTOR:\n        return seq;\n    case MAL_LIST: {\n        const GArray * const src = seq->val.array;\n        const int len = src->len;\n        GArray * const dst = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len);\n        int i;\n        for (i=0; i<len; i++)\n            g_array_append_val(dst, g_array_index(seq->val.array, MalVal*, i));\n        return malval_new_list(MAL_VECTOR, dst);\n    }\n    default:\n        _error(\"vec called with non-sequential\");\n    }\n}\n\nMalVal *nth(MalVal *seq, MalVal *idx) {\n    return _nth(seq, idx->val.intnum);\n}\n\nMalVal *empty_Q(MalVal *seq) {\n    assert_type(seq, MAL_LIST|MAL_VECTOR,\n                \"empty? called with non-sequential\");\n    return (seq->val.array->len == 0) ? &mal_true : &mal_false;\n}\n\nMalVal *count(MalVal *seq) {\n    return malval_new_integer(_count(seq));\n}\n\nMalVal *apply(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"apply called with non-sequential\");\n    MalVal *f = _nth(args, 0);\n    MalVal *last_arg = _last(args);\n    assert_type(last_arg, MAL_LIST|MAL_VECTOR,\n                \"last argument to apply is non-sequential\");\n    int i, len = _count(args) - 2 + _count(last_arg);\n    GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                        len);\n    // Initial arguments\n    for (i=1; i<_count(args)-1; i++) {\n        g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));\n    }\n    // Add arguments from last_arg\n    for (i=0; i<_count(last_arg); i++) {\n        g_array_append_val(new_arr, g_array_index(last_arg->val.array, MalVal*, i));\n    }\n    return _apply(f, malval_new_list(MAL_LIST, new_arr));\n}\n\nMalVal *map(MalVal *mvf, MalVal *lst) {\n    MalVal *res, *el;\n    assert_type(mvf, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                \"map called with non-function\");\n    assert_type(lst, MAL_LIST|MAL_VECTOR,\n                \"map called with non-sequential\");\n    int i, len = _count(lst);\n    el = malval_new_list(MAL_LIST,\n                         g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len));\n    for (i=0; i<len; i++) {\n        // TODO: this is replicating some of apply functionality\n        if (mvf->type & MAL_FUNCTION_MAL) {\n            Env *fn_env = new_env(mvf->val.func.env,\n                                  mvf->val.func.args,\n                                  _slice(lst, i, i+1));\n            res = mvf->val.func.evaluator(mvf->val.func.body, fn_env);\n        } else {\n            res = mvf->val.f1(g_array_index(lst->val.array, MalVal*, i));\n        }\n        if (!res || mal_error) return NULL;\n        g_array_append_val(el->val.array, res);\n    }\n    return el;\n}\n\nMalVal *sconj(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"conj called with non-sequential\");\n    MalVal *src_lst = _nth(args, 0);\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"first argument to conj is non-sequential\");\n    int i, len = _count(src_lst) + _count(args) - 1;\n    GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                        len);\n    // Copy in src_lst\n    for (i=0; i<_count(src_lst); i++) {\n        g_array_append_val(new_arr, g_array_index(src_lst->val.array, MalVal*, i));\n    }\n    // Conj extra args\n    for (i=1; i<_count(args); i++) {\n        if (src_lst->type & MAL_LIST) {\n            g_array_prepend_val(new_arr, g_array_index(args->val.array, MalVal*, i));\n        } else {\n            g_array_append_val(new_arr, g_array_index(args->val.array, MalVal*, i));\n        }\n    }\n    return malval_new_list(src_lst->type, new_arr);\n}\n\nMalVal *seq(MalVal *obj) {\n    assert_type(obj, MAL_LIST|MAL_VECTOR|MAL_STRING|MAL_NIL,\n                \"seq: called with non-sequential\");\n    int cnt, i;\n    MalVal *lst, *mstr;\n    switch (obj->type) {\n    case MAL_LIST:\n        cnt = _count(obj);\n        if (cnt == 0) { return &mal_nil; }\n        return obj;\n    case MAL_VECTOR:\n        cnt = _count(obj);\n        if (cnt == 0) { return &mal_nil; }\n        lst = malval_new_list(MAL_LIST,\n                            g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), cnt));\n        lst->val.array = obj->val.array;\n        return lst;\n    case MAL_STRING:\n        cnt = strlen(obj->val.string);\n        if (cnt == 0) { return &mal_nil; }\n        lst = malval_new_list(MAL_LIST,\n                              g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), cnt));\n        for (i=0; i<cnt; i++) {\n            mstr = malval_new_string(g_strdup_printf(\"%c\", obj->val.string[i]));\n            g_array_append_val(lst->val.array, mstr);\n        }\n        return lst;\n    case MAL_NIL:\n        return &mal_nil;\n    }\n}\n\n\n// Metadata functions\n\nMalVal *with_meta(MalVal *obj, MalVal *meta) {\n    MalVal *new_obj = malval_new(obj->type, meta);\n    new_obj->val = obj->val;\n    return new_obj;\n}\n\nMalVal *meta(MalVal *obj) {\n    assert_type(obj, MAL_LIST|MAL_VECTOR|MAL_HASH_MAP|\n                     MAL_FUNCTION_C|MAL_FUNCTION_MAL|MAL_ATOM,\n                \"attempt to get metadata from non-collection type\");\n    if (obj->metadata == NULL) {\n        return &mal_nil;\n    } else {\n        return obj->metadata;\n    }\n}\n\n\n// Atoms\n\nMalVal *atom(MalVal *val) {\n    return malval_new_atom(val);\n}\n\nMalVal *atom_Q(MalVal *exp) { return _atom_Q(exp) ? &mal_true : &mal_false; }\n\nMalVal *deref(MalVal *atm) {\n    assert_type(atm, MAL_ATOM,\n                \"deref called on non-atom\");\n    return atm->val.atom_val;\n}\n\nMalVal *reset_BANG(MalVal *atm, MalVal *val) {\n    assert_type(atm, MAL_ATOM,\n                \"reset! called with non-atom\");\n    atm->val.atom_val = val;\n    return val;\n}\n\nMalVal *swap_BANG(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"swap! called with invalid arguments\");\n    assert(_count(args) >= 2,\n           \"swap! called with %d args, needs at least 2\", _count(args));\n    MalVal *atm = _nth(args, 0),\n           *f = _nth(args, 1),\n           *sargs = _slice(args, 2, _count(args)),\n           *fargs = cons(atm->val.atom_val, sargs),\n           *new_val = _apply(f, fargs);\n    if (mal_error) { return NULL; }\n    atm->val.atom_val = new_val;\n    return new_val;\n}\n\n\n\ncore_ns_entry core_ns[] = {\n    {\"=\", (void*(*)(void*))equal_Q, 2},\n    {\"throw\", (void*(*)(void*))throw, 1},\n    {\"nil?\", (void*(*)(void*))nil_Q, 1},\n    {\"true?\", (void*(*)(void*))true_Q, 1},\n    {\"false?\", (void*(*)(void*))false_Q, 1},\n    {\"string?\", (void*(*)(void*))string_Q, 1},\n    {\"symbol\", (void*(*)(void*))symbol, 1},\n    {\"symbol?\", (void*(*)(void*))symbol_Q, 1},\n    {\"keyword\", (void*(*)(void*))keyword, 1},\n    {\"keyword?\", (void*(*)(void*))keyword_Q, 1},\n    {\"number?\", (void*(*)(void*))number_Q, 1},\n    {\"fn?\", (void*(*)(void*))fn_Q, 1},\n    {\"macro?\", (void*(*)(void*))macro_Q, 1},\n\n    {\"pr-str\", (void*(*)(void*))pr_str, -1},\n    {\"str\", (void*(*)(void*))str, -1},\n    {\"prn\", (void*(*)(void*))prn, -1},\n    {\"println\", (void*(*)(void*))println, -1},\n    {\"readline\", (void*(*)(void*))mal_readline, 1},\n    {\"read-string\", (void*(*)(void*))read_string, 1},\n    {\"slurp\", (void*(*)(void*))slurp, 1},\n    {\"<\", (void*(*)(void*))int_lt, 2},\n    {\"<=\", (void*(*)(void*))int_lte, 2},\n    {\">\", (void*(*)(void*))int_gt, 2},\n    {\">=\", (void*(*)(void*))int_gte, 2},\n    {\"+\", (void*(*)(void*))int_plus, 2},\n    {\"-\", (void*(*)(void*))int_minus, 2},\n    {\"*\", (void*(*)(void*))int_multiply, 2},\n    {\"/\", (void*(*)(void*))int_divide, 2},\n    {\"time-ms\", (void*(*)(void*))time_ms, 0},\n\n    {\"list\", (void*(*)(void*))list, -1},\n    {\"list?\", (void*(*)(void*))list_Q, 1},\n    {\"vector\", (void*(*)(void*))vector, -1},\n    {\"vector?\", (void*(*)(void*))vector_Q, 1},\n    {\"hash-map\", (void*(*)(void*))_hash_map, -1},\n    {\"map?\", (void*(*)(void*))hash_map_Q, 1},\n    {\"assoc\", (void*(*)(void*))assoc, -1},\n    {\"dissoc\", (void*(*)(void*))dissoc, -1},\n    {\"get\", (void*(*)(void*))get, 2},\n    {\"contains?\", (void*(*)(void*))contains_Q, 2},\n    {\"keys\", (void*(*)(void*))keys, 1},\n    {\"vals\", (void*(*)(void*))vals, 1},\n\n    {\"sequential?\", (void*(*)(void*))sequential_Q, 1},\n    {\"cons\", (void*(*)(void*))cons, 2},\n    {\"concat\", (void*(*)(void*))concat, -1},\n    {\"vec\", (void*(*)(void*))vec, 1},\n    {\"nth\", (void*(*)(void*))nth, 2},\n    {\"first\", (void*(*)(void*))_first, 1},\n    {\"rest\", (void*(*)(void*))_rest, 1},\n    {\"last\", (void*(*)(void*))_last, 1},\n    {\"empty?\", (void*(*)(void*))empty_Q, 1},\n    {\"count\", (void*(*)(void*))count, 1},\n    {\"apply\", (void*(*)(void*))apply, -1},\n    {\"map\", (void*(*)(void*))map, 2},\n\n    {\"conj\", (void*(*)(void*))sconj, -1},\n    {\"seq\", (void*(*)(void*))seq, 1},\n\n    {\"with-meta\", (void*(*)(void*))with_meta, 2},\n    {\"meta\", (void*(*)(void*))meta, 1},\n    {\"atom\", (void*(*)(void*))atom, 1},\n    {\"atom?\", (void*(*)(void*))atom_Q, 1},\n    {\"deref\", (void*(*)(void*))deref, 1},\n    {\"reset!\", (void*(*)(void*))reset_BANG, 2},\n    {\"swap!\", (void*(*)(void*))swap_BANG, -1},\n    };\n"
  },
  {
    "path": "impls/c/core.h",
    "content": "#ifndef __MAL_CORE__\n#define __MAL_CORE__\n\n#include <glib.h>\n\n// namespace of type functions\ntypedef struct {\n    char *name;\n    void *(*func)(void*);\n    int arg_cnt;\n} core_ns_entry;\n\nextern core_ns_entry core_ns[62];\n\n#endif\n"
  },
  {
    "path": "impls/c/env.c",
    "content": "#include <stdlib.h>\n#include \"types.h\"\n\n// Env\n\nEnv *new_env(Env *outer, MalVal* binds, MalVal *exprs) {\n    Env *e = MAL_GC_MALLOC(sizeof(Env));\n    e->table = g_hash_table_new(g_str_hash, g_str_equal);\n    e->outer = outer;\n\n    if (binds && exprs) {\n        assert_type(binds, MAL_LIST|MAL_VECTOR,\n                    \"new_env called with non-sequential bindings\");\n        assert_type(exprs, MAL_LIST|MAL_VECTOR,\n                    \"new_env called with non-sequential expressions\");\n        int binds_len = _count(binds),\n            exprs_len = _count(exprs),\n            varargs = 0, i;\n        for (i=0; i<binds_len; i++) {\n            if (i > exprs_len) { break; }\n            if (_nth(binds, i)->val.string[0] == '&') {\n                varargs = 1;\n                env_set(e, _nth(binds, i+1)->val.string,\n                        _slice(exprs, i, _count(exprs)));\n                break;\n            } else {\n              env_set(e, _nth(binds, i)->val.string, _nth(exprs, i));\n            }\n        }\n        assert(varargs || (binds_len == exprs_len),\n               \"Arity mismatch: %d formal params vs %d actual params\",\n               binds_len, exprs_len);\n\n    }\n    return e;\n}\n\nMalVal *env_get(Env *env, const char *key) {\n    MalVal *val = g_hash_table_lookup(env->table, key);\n    if (val) {\n        return val;\n    } else if (env->outer) {\n        return env_get(env->outer, key);\n    } else {\n        return NULL;\n    }\n}\n\nvoid env_set(Env *env, char *key, MalVal *val) {\n    g_hash_table_insert(env->table, key, val);\n}\n"
  },
  {
    "path": "impls/c/interop.c",
    "content": "#include <string.h>\n#include <dlfcn.h>\n#if OSX\n  #include <ffi/ffi.h>\n#else\n  #include <ffi.h>\n#endif\n\n#include \"types.h\"\n\n\nGHashTable *loaded_dls = NULL;\n\nint get_byte_size(char *type) {\n    return 0;\n}\n\ntypedef struct Raw64 {\n    union {\n        gdouble floatnum;\n        gint64  integernum;\n        char   *string;\n    } v;\n} Raw64;\n\n\n// obj must be a pointer to the object to store\nffi_type *_get_ffi_type(char *type) {\n    if ((strcmp(\"void\", type) == 0)) {\n        return &ffi_type_void;\n    } else if ((strcmp(\"string\", type) == 0) ||\n               (strcmp(\"char*\", type) == 0) ||\n               (strcmp(\"char *\", type) == 0)) {\n        return &ffi_type_pointer;\n    } else if ((strcmp(\"integer\", type) == 0) ||\n               (strcmp(\"int64\", type) == 0)) {\n        return &ffi_type_sint64;\n    } else if ((strcmp(\"int32\", type) == 0)) {\n        return &ffi_type_sint32;\n    } else if (strcmp(\"double\", type) == 0) {\n        return &ffi_type_double;\n    } else if (strcmp(\"float\", type) == 0) {\n        return &ffi_type_float;\n    } else {\n        abort(\"_get_ffi_type of unknown type '%s'\", type);\n    }\n}\n\nMalVal *_malval_new_by_type(char *type) {\n    if ((strcmp(\"void\", type) == 0)) {\n        return NULL;\n    } else if ((strcmp(\"string\", type) == 0) ||\n               (strcmp(\"char*\", type) == 0) ||\n               (strcmp(\"char *\", type) == 0)) {\n        return malval_new(MAL_STRING, NULL);\n    } else if ((strcmp(\"integer\", type) == 0) ||\n               (strcmp(\"int64\", type) == 0)) {\n        return malval_new(MAL_INTEGER, NULL);\n    } else if ((strcmp(\"int32\", type) == 0)) {\n        return malval_new(MAL_INTEGER, NULL);\n    } else if (strcmp(\"double\", type) == 0) {\n        return malval_new(MAL_FLOAT, NULL);\n    } else if (strcmp(\"float\", type) == 0) {\n        return malval_new(MAL_FLOAT, NULL);\n    } else {\n        abort(\"_malval_new_by_type of unknown type '%s'\", type);\n    }\n}\n\n\n\n// Mal syntax:\n//   (.  {DYN_LIB_FILE|nil}  RETURN_TYPE  FUNC_NAME  [ARG_TYPE ARG]...)\nMalVal *invoke_native(MalVal *call_data) {\n    //g_print(\"invoke_native %s\\n\", pr_str(call_data));\n    int cd_len = call_data->val.array->len;\n    int arg_len = (cd_len - 3)/2;\n    char *error;\n    void *dl_handle;\n\n    assert_type(call_data, MAL_LIST,\n                \"invoke_native called with non-list call_data: %s\",\n                _pr_str(call_data,1));\n    assert(cd_len >= 3,\n           \"invoke_native called with %d args, needs at least 3\",\n           cd_len);\n    assert((cd_len % 2) == 1,\n           \"invoke_native called with an even number of args (%d)\",\n           cd_len);\n    assert(arg_len <= 3,\n           \"invoke_native called with more than 3 native args (%d)\",\n           arg_len);\n    MalVal *dl_file = _nth(call_data, 0),\n           *ftype   = _nth(call_data, 1),\n           *fname   = _nth(call_data, 2);\n    assert_type(dl_file, MAL_STRING|MAL_NIL,\n                \"invoke_native arg 1 (DYN_LIB_NAME) must be a string or nil\");\n    assert_type(ftype, MAL_STRING,\n                \"invoke_native arg 2 (RETURN_TYPE) must be a string\");\n    assert_type(fname, MAL_STRING,\n                \"invoke_native arg 3 (FUNC_NAME) must be a string\");\n\n    // Cached load of the dynamic library handle\n    if (dl_file->type == MAL_NIL) {\n        dl_handle = dlopen(NULL, RTLD_LAZY);\n    } else {\n        // Load the library\n        if (loaded_dls == NULL) {\n            loaded_dls = g_hash_table_new(g_str_hash, g_str_equal);\n        }\n        dl_handle = g_hash_table_lookup(loaded_dls, dl_file->val.string);\n        dlerror(); // clear any existing error\n        if (!dl_handle) {\n            dl_handle = dlopen(dl_file->val.string, RTLD_LAZY);\n        }\n        if ((error = dlerror()) != NULL) {\n            abort(\"Could not dlopen '%s': %s\", dl_file->val.string, error);\n        }\n        g_hash_table_insert(loaded_dls, dl_file->val.string, dl_handle);\n    }\n\n    void * func = dlsym(dl_handle, fname->val.string);\n    if ((error = dlerror()) != NULL) {\n        abort(\"Could not dlsym '%s': %s\", fname->val.string, error);\n    }\n\n\n    // \n    // Use FFI library to make a dynamic call\n    //\n    \n    // Based on:\n    // http://eli.thegreenplace.net/2013/03/04/flexible-runtime-interface-to-shared-libraries-with-libffi/\n    ffi_cif cif;\n    ffi_type *ret_type;\n    ffi_type *arg_types[20];\n    void     *arg_vals[20];\n    ffi_status status;\n    MalVal *ret_mv;\n\n    // Set return type\n    ret_type = _get_ffi_type(ftype->val.string);\n    ret_mv = _malval_new_by_type(ftype->val.string);\n    if (mal_error) { return NULL; }\n\n    // Set the argument types and values\n    int i;\n    for (i=0; i < arg_len; i++) {\n        arg_types[i] = _get_ffi_type(_nth(call_data, 3+i*2)->val.string);\n        if (arg_types[i] == NULL) {\n            return NULL;\n        }\n        arg_vals[i] = &_nth(call_data, 4+i*2)->val;\n    }\n\n    status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_len,\n                          ret_type, arg_types);\n    if (status != FFI_OK) {\n        abort(\"ffi_prep_cif failed: %d\\n\", status);\n    }\n\n    // Perform the call\n    //g_print(\"Calling %s[%p](%d)\\n\", fname->val.string, func, arg_len);\n    ffi_call(&cif, FFI_FN(func), &ret_mv->val, arg_vals);\n\n    if (ret_type == &ffi_type_void) {\n        return &mal_nil;\n    } else {\n        return ret_mv;\n    }\n}\n\n"
  },
  {
    "path": "impls/c/interop.h",
    "content": "#ifndef __MAL_INTEROP__\n#define __MAL_INTEROP__\n\nMalVal *invoke_native(MalVal *call_data);\n\n#endif\n"
  },
  {
    "path": "impls/c/printer.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include \"types.h\"\n#include \"printer.h\"\n\nchar *_pr_str_hash_map(MalVal *obj, int print_readably) {\n    int start = 1;\n    char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL,\n         *key2 = NULL;\n    GHashTableIter iter;\n    gpointer key, value;\n\n    repr = g_strdup_printf(\"{\");\n\n    g_hash_table_iter_init (&iter, obj->val.hash_table);\n    while (g_hash_table_iter_next (&iter, &key, &value)) {\n        //g_print (\"%s/%p \", (const char *) key, (void *) value);\n        if (((char*)key)[0] == '\\x7f') {\n            key2 = g_strdup_printf(\"%s\", (char*)key);\n            key2[0] = ':';\n        } else {\n            key2 = g_strdup_printf(\"\\\"%s\\\"\", (char*)key);\n        }\n\n        repr_tmp1 = _pr_str((MalVal*)value, print_readably);\n        if (start) {\n            start = 0;\n            repr = g_strdup_printf(\"{%s %s\", (char*)key2, repr_tmp1);\n        } else {\n            repr_tmp2 = repr;\n            repr = g_strdup_printf(\"%s %s %s\", repr_tmp2, (char*)key2, repr_tmp1);\n            MAL_GC_FREE(repr_tmp2);\n        }\n        MAL_GC_FREE(repr_tmp1);\n    }\n    repr_tmp2 = repr;\n    repr = g_strdup_printf(\"%s}\", repr_tmp2);\n    MAL_GC_FREE(repr_tmp2);\n    return repr;\n}\n\nchar *_pr_str_list(MalVal *obj, int print_readably, char start, char end) {\n    int i;\n    char *repr = NULL, *repr_tmp1 = NULL, *repr_tmp2 = NULL;\n    repr = g_strdup_printf(\"%c\", start);\n    for (i=0; i<_count(obj); i++) {\n        repr_tmp1 = _pr_str(g_array_index(obj->val.array, MalVal*, i),\n                            print_readably);\n        if (i == 0) {\n            repr = g_strdup_printf(\"%c%s\", start, repr_tmp1);\n        } else {\n            repr_tmp2 = repr;\n            repr = g_strdup_printf(\"%s %s\", repr_tmp2, repr_tmp1);\n            MAL_GC_FREE(repr_tmp2);\n        }\n        MAL_GC_FREE(repr_tmp1);\n    }\n    repr_tmp2 = repr;\n    repr = g_strdup_printf(\"%s%c\", repr_tmp2, end);\n    MAL_GC_FREE(repr_tmp2);\n    return repr;\n}\n\n// Return a string representation of the MalVal object. Returned string must\n// be freed by caller.\nchar *_pr_str(MalVal *obj, int print_readably) {\n    char *repr = NULL;\n    if (obj == NULL) { return NULL; }\n    switch (obj->type) {\n    case MAL_NIL:\n        repr = g_strdup_printf(\"nil\");\n        break;\n    case MAL_TRUE:\n        repr = g_strdup_printf(\"true\");\n        break;\n    case MAL_FALSE:\n        repr = g_strdup_printf(\"false\");\n        break;\n    case MAL_STRING:\n        if (obj->val.string[0] == '\\x7f') {\n            // Keyword\n            repr = g_strdup_printf(\"%s\", obj->val.string);\n            repr[0] = ':';\n        } else if (print_readably) {\n            char *repr_tmp = g_strescape(obj->val.string, \"\");\n            repr = g_strdup_printf(\"\\\"%s\\\"\", repr_tmp);\n            MAL_GC_FREE(repr_tmp);\n        } else {\n            repr = g_strdup_printf(\"%s\", obj->val.string);\n        }\n        break;\n    case MAL_SYMBOL:\n        repr = g_strdup_printf(\"%s\", obj->val.string);\n        break;\n    case MAL_INTEGER:\n        repr = g_strdup_printf(\"%\" G_GINT64_FORMAT, obj->val.intnum);\n        break;\n    case MAL_FLOAT:\n        repr = g_strdup_printf(\"%f\", obj->val.floatnum);\n        break;\n    case MAL_HASH_MAP:\n        repr = _pr_str_hash_map(obj, print_readably);\n        break;\n    case MAL_LIST:\n        repr = _pr_str_list(obj, print_readably, '(', ')');\n        break;\n    case MAL_VECTOR:\n        repr = _pr_str_list(obj, print_readably, '[', ']');\n        break;\n    case MAL_ATOM:\n        repr = g_strdup_printf(\"(atom %s)\",\n                               _pr_str(obj->val.atom_val, print_readably));\n        break;\n    case MAL_FUNCTION_C:\n        repr = g_strdup_printf(\"#<function@%p>\", obj->val.f0);\n        break;\n    case MAL_FUNCTION_MAL:\n        repr = g_strdup_printf(\"#<Function: (fn* %s %s)>\",\n                                _pr_str(obj->val.func.args, print_readably),\n                                _pr_str(obj->val.func.body, print_readably));\n        break;\n    default:\n        printf(\"pr_str unknown type %d\\n\", obj->type);\n        repr = g_strdup_printf(\"<unknown>\");\n    }\n    return repr;\n}\n\n// Return a string representation of the MalVal arguments. Returned string must\n// be freed by caller.\nchar *_pr_str_args(MalVal *args, char *sep, int print_readably) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"_pr_str called with non-sequential args\");\n    int i;\n    char *repr = g_strdup_printf(\"%s\", \"\"),\n         *repr2 = NULL;\n    for (i=0; i<_count(args); i++) {\n        MalVal *obj = g_array_index(args->val.array, MalVal*, i);\n        if (i != 0) {\n            repr2 = repr;\n            repr = g_strdup_printf(\"%s%s\", repr2, sep);\n            MAL_GC_FREE(repr2);\n        }\n        repr2 = repr;\n        repr = g_strdup_printf(\"%s%s\",\n                               repr2, _pr_str(obj, print_readably));\n        MAL_GC_FREE(repr2);\n    }\n    char* res = MAL_GC_STRDUP(repr);\n    MAL_GC_FREE(repr);\n    // TODO - check why STRDUP was needed here\n    return res;\n}\n\n"
  },
  {
    "path": "impls/c/printer.h",
    "content": "#ifndef __MAL_PRINTER__\n#define __MAL_PRINTER__\n\n#include \"types.h\"\n\nchar *_pr_str_args(MalVal *args, char *sep, int print_readably);\nchar *_pr_str(MalVal *obj, int print_readably);\n\n#endif\n"
  },
  {
    "path": "impls/c/reader.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n//#include <glib/gregex.h>\n//#include <glib-object.h>\n#include <glib.h>\n\n#include \"types.h\"\n#include \"reader.h\"\n\n// Declare\nMalVal *read_form(Reader *reader);\n\nReader *reader_new() {\n    Reader *reader = (Reader*)MAL_GC_MALLOC(sizeof(Reader));\n    reader->array = g_array_sized_new(TRUE, FALSE, sizeof(char *), 8);\n    reader->position = 0;\n    return reader;\n}\n\nint reader_append(Reader *reader, char* token) {\n    g_array_append_val(reader->array, token);\n    return TRUE;\n}\n\nchar *reader_peek(Reader *reader) {\n    return g_array_index(reader->array, char*, reader->position);\n}\n\nchar *reader_next(Reader *reader) {\n    if (reader->position >= reader->array->len) {\n        return NULL;\n    } else {\n        return g_array_index(reader->array, char*, reader->position++);\n    }\n}\n\nvoid reader_free(Reader *reader) {\n    int i;\n    for(i=0; i < reader->array->len; i++) {\n        MAL_GC_FREE(g_array_index(reader->array, char*, i));\n    }\n    g_array_free(reader->array, TRUE);\n    MAL_GC_FREE(reader);\n}\n\nReader *tokenize(char *line) {\n    GRegex *regex;\n    GMatchInfo *matchInfo;\n    GError *err = NULL;\n\n    Reader *reader = reader_new();\n\n    regex = g_regex_new (\"[\\\\s ,]*(~@|[\\\\[\\\\]{}()'`~@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s \\\\[\\\\]{}()'\\\"`~@,;]*)\", 0, 0, &err);\n    g_regex_match (regex, line, 0, &matchInfo);\n\n    if (err != NULL) {\n        fprintf(stderr, \"Tokenize error: %s\\n\", err->message);\n        return NULL;\n    }\n    \n    while (g_match_info_matches(matchInfo)) {\n        gchar *result = g_match_info_fetch(matchInfo, 1);\n        if (result[0] != '\\0' && result[0] != ';') {\n            reader_append(reader, result);\n        }\n        g_match_info_next(matchInfo, &err);\n    }\n    g_match_info_free(matchInfo);\n    g_regex_unref(regex);\n    if (reader->array->len == 0) {\n        reader_free(reader);\n        return NULL;\n    } else {\n        return reader;\n    }\n}\n\n\nMalVal *read_atom(Reader *reader) {\n    char *token;\n    GRegex *regex;\n    GMatchInfo *matchInfo;\n    GError *err = NULL;\n    gint pos;\n    MalVal *atom;\n\n    token = reader_next(reader);\n    //g_print(\"read_atom token: %s\\n\", token);\n    \n    regex = g_regex_new (\"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\\\"((?:[\\\\\\\\].|[^\\\\\\\\\\\"])*)\\\"$|^\\\"(.*)\\\"?$|:(.*)|(^[^\\\"]*$)\", 0, 0, &err);\n    g_regex_match (regex, token, 0, &matchInfo);\n\n    if (g_match_info_fetch_pos(matchInfo, 1, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom integer\\n\");\n        atom = malval_new_integer(g_ascii_strtoll(token, NULL, 10));\n    } else if (g_match_info_fetch_pos(matchInfo, 2, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom float\\n\");\n        atom = malval_new_float(g_ascii_strtod(token, NULL));\n    } else if (g_match_info_fetch_pos(matchInfo, 3, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom nil\\n\");\n        atom = &mal_nil;\n    } else if (g_match_info_fetch_pos(matchInfo, 4, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom true\\n\");\n        atom = &mal_true;\n    } else if (g_match_info_fetch_pos(matchInfo, 5, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom false\\n\");\n        atom = &mal_false;\n    } else if (g_match_info_fetch_pos(matchInfo, 6, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom string: %s\\n\", token);\n        int end = strlen(token)-1;\n        token[end] = '\\0';\n        atom = malval_new_string(g_strcompress(g_match_info_fetch(matchInfo, 6)));\n    } else if (g_match_info_fetch_pos(matchInfo, 7, &pos, NULL) && pos != -1) {\n        abort(\"expected '\\\"', got EOF\");\n    } else if (g_match_info_fetch_pos(matchInfo, 8, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom keyword\\n\");\n        atom = malval_new_keyword(MAL_GC_STRDUP(g_match_info_fetch(matchInfo, 8)));\n    } else if (g_match_info_fetch_pos(matchInfo, 9, &pos, NULL) && pos != -1) {\n        //g_print(\"read_atom symbol\\n\");\n        atom = malval_new_symbol(MAL_GC_STRDUP(g_match_info_fetch(matchInfo, 9)));\n    } else {\n        malval_free(atom);\n        atom = NULL;\n    }\n\n    return atom;\n}\n\nMalVal *read_list(Reader *reader, MalType type, char start, char end) {\n    MalVal *ast, *form;\n    char *token = reader_next(reader);\n    //g_print(\"read_list start token: %s\\n\", token);\n    if (token[0] != start) { abort(\"expected '(', '[', or '{'\"); }\n\n    ast = malval_new_list(type, g_array_new(TRUE, TRUE, sizeof(MalVal*)));\n\n    while ((token = reader_peek(reader)) &&\n           token[0] != end) {\n        //g_print(\"read_list internal token %s\\n\", token);\n        form = read_form(reader);\n        if (!form) {\n            if (!mal_error) { abort(\"unknown read_list failure\"); }\n            g_array_free(ast->val.array, TRUE);\n            malval_free(ast);\n            return NULL;\n        }\n        g_array_append_val(ast->val.array, form);\n    }\n    if (!token) { abort(\"expected ')', ']', or '}', got EOF\"); }\n    reader_next(reader);\n    //g_print(\"read_list end token: %s\\n\", token);\n    return ast;\n}\n\nMalVal *read_hash_map(Reader *reader) {\n    MalVal *lst = read_list(reader, MAL_LIST, '{', '}');\n    if (!lst) { return NULL; }\n    MalVal *hm = _hash_map(lst);\n    malval_free(lst);\n    return hm;\n}\n\n\nMalVal *read_form(Reader *reader) {\n    char *token;\n    MalVal *form = NULL, *tmp;\n\n//    while(token = reader_next(reader)) {\n//        printf(\"token: %s\\n\", token);\n//    }\n//    return NULL;\n\n    token = reader_peek(reader);\n\n    if (!token) { return NULL; }\n    //g_print(\"read_form token: %s\\n\", token);\n\n    switch (token[0]) {\n    case ';':\n        abort(\"comments not yet implemented\");\n        break;\n    case '\\'':\n        reader_next(reader);\n        form = _listX(2, malval_new_symbol(\"quote\"),\n                         read_form(reader));\n        break;\n    case '`':\n        reader_next(reader);\n        form = _listX(2, malval_new_symbol(\"quasiquote\"),\n                         read_form(reader));\n        break;\n    case '~':\n        reader_next(reader);\n        if (token[1] == '@') {\n            form = _listX(2, malval_new_symbol(\"splice-unquote\"),\n                             read_form(reader));\n        } else {\n            form = _listX(2, malval_new_symbol(\"unquote\"),\n                             read_form(reader));\n        };\n        break;\n    case '^':\n        reader_next(reader);\n        MalVal *meta = read_form(reader);\n        form = _listX(3, malval_new_symbol(\"with-meta\"),\n                         read_form(reader), meta);\n        break;\n    case '@':\n        reader_next(reader);\n        form = _listX(2, malval_new_symbol(\"deref\"),\n                         read_form(reader));\n        break;\n\n\n    // list\n    case ')':\n        abort(\"unexpected ')'\");\n        break;\n    case '(':\n        form = read_list(reader, MAL_LIST, '(', ')');\n        break;\n\n    // vector\n    case ']':\n        abort(\"unexpected ']'\");\n        break;\n    case '[':\n        form = read_list(reader, MAL_VECTOR, '[', ']');\n        break;\n\n    // hash-map\n    case '}':\n        abort(\"unexpected '}'\");\n        break;\n    case '{':\n        form = read_hash_map(reader);\n        break;\n\n    default:\n        form = read_atom(reader);\n        break;\n    }\n    return form;\n\n}\n\nMalVal *read_str (char *str) {\n    Reader *reader;\n    char *token;\n    MalVal *ast = NULL;\n\n    reader = tokenize(str);\n    if (reader) {\n        ast = read_form(reader);\n        reader_free(reader);\n    }\n\n    return ast;\n}\n"
  },
  {
    "path": "impls/c/reader.h",
    "content": "#ifndef __MAL_READER__\n#define __MAL_READER__\n\n#include <glib.h>\n#include <glib-object.h>\n\n#include \"types.h\"\n\ntypedef struct {\n    GArray *array;\n    int position;\n} Reader;\n\nReader *reader_new();\nint reader_append(Reader *reader, char* token);\nchar *reader_peek(Reader *reader);\nchar *reader_next(Reader *reader);\nvoid reader_free(Reader *reader);\n\nchar *_readline (char prompt[]);\nMalVal *read_str ();\n\n#endif\n"
  },
  {
    "path": "impls/c/readline.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#if USE_READLINE\n  #include <readline/readline.h>\n  #include <readline/history.h>\n  #include <readline/tilde.h>\n#else\n  #include <editline/readline.h>\n#endif\n\nint history_loaded = 0;\n\nchar HISTORY_FILE[] = \"~/.mal-history\";\n\nvoid load_history() {\n    if (history_loaded) { return; }\n    int ret;\n    char *hf = tilde_expand(HISTORY_FILE);\n    if (access(hf, F_OK) != -1) {\n        // TODO: check if file exists first, use non-static path\n#if USE_READLINE\n        ret = read_history(hf);\n#else\n        FILE *fp = fopen(hf, \"r\");\n        char *line = malloc(80); // getline reallocs as necessary\n        size_t sz = 80;\n        while ((ret = getline(&line, &sz, fp)) > 0) {\n            add_history(line); // Add line to in-memory history\n        }\n        free(line);\n        fclose(fp);\n#endif\n        history_loaded = 1;\n    }\n    free(hf);\n}\n\nvoid append_to_history() {\n    char *hf = tilde_expand(HISTORY_FILE);\n#ifdef USE_READLINE\n    append_history(1, hf);\n#else\n#if defined(RL_READLINE_VERSION)\n    HIST_ENTRY *he = history_get(history_base+history_length-1);\n#else\n    // libedit-2 segfaults if we add history_base\n    HIST_ENTRY *he = history_get(history_length-1);\n#endif\n    FILE *fp = fopen(hf, \"a\");\n    if (fp) {\n        fprintf(fp, \"%s\\n\", he->line);\n        fclose(fp);\n    }\n#endif\n    free(hf);\n}\n\n\n// line must be freed by caller\nchar *_readline (char prompt[]) {\n    char *line;\n\n    load_history();\n\n    line = readline(prompt);\n    if (!line) return NULL; // EOF\n    add_history(line); // Add input to in-memory history\n\n    append_to_history(); // Flush new line of history to disk\n\n    return line;\n}\n\n"
  },
  {
    "path": "impls/c/readline.h",
    "content": "#ifndef __MAL_READLINE__\n#define __MAL_READLINE__\n\nchar *_readline (char prompt[]);\n\n#endif\n"
  },
  {
    "path": "impls/c/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/c/step0_repl.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#ifdef USE_READLINE\n  #include <readline/readline.h>\n  #include <readline/history.h>\n#else\n  #include <editline/readline.h>\n#endif\n\nchar *READ(char prompt[]) {\n    char *line;\n    line = readline(prompt);\n    if (!line) return NULL; // EOF\n    add_history(line); // Add input to history.\n    return line;\n}\n\nchar *EVAL(char *ast, void *env) {\n    return ast;\n}\n\nchar *PRINT(char *exp) {\n    return exp;\n}\n\nint main()\n{\n    char *ast, *exp;\n    char prompt[100];\n\n    // Set the initial prompt\n    snprintf(prompt, sizeof(prompt), \"user> \");\n\n    for(;;) {\n        ast = READ(prompt);\n        if (!ast) return 0;\n        exp = EVAL(ast, NULL);\n        puts(PRINT(exp));\n\n        free(ast); // Free input string\n    }\n}\n"
  },
  {
    "path": "impls/c/step1_read_print.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nMalVal *EVAL(MalVal *ast, GHashTable *env) {\n    if (!ast || mal_error) return NULL;\n    return ast;\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(GHashTable *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\nint main()\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt\n    snprintf(prompt, sizeof(prompt), \"user> \");\n\n    // repl loop\n    for(;;) {\n        exp = RE(NULL, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step2_eval.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, GHashTable *env);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nMalVal *EVAL(MalVal *ast, GHashTable *env) {\n    if (!ast || mal_error) return NULL;\n    //g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        // TODO: check if not found\n        MalVal *res = g_hash_table_lookup(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    if (_count(ast) == 0) { return ast; }\n    MalVal *a0 = _nth(ast, 0);\n    assert_type(a0, MAL_SYMBOL, \"Cannot invoke %s\", _pr_str(a0,1));\n    MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n    if (!el || mal_error) { return NULL; }\n    MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el);\n    //g_print(\"eval_invoke el: %s\\n\", _pr_str(el,1));\n    return f(_nth(el, 1), _nth(el, 2));\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(GHashTable *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nGHashTable *repl_env;\n\nWRAP_INTEGER_OP(plus,+)\nWRAP_INTEGER_OP(minus,-)\nWRAP_INTEGER_OP(multiply,*)\nWRAP_INTEGER_OP(divide,/)\n\nvoid init_repl_env() {\n    repl_env = g_hash_table_new(g_str_hash, g_str_equal);\n\n    g_hash_table_insert(repl_env, \"+\", int_plus);\n    g_hash_table_insert(repl_env, \"-\", int_minus);\n    g_hash_table_insert(repl_env, \"*\", int_multiply);\n    g_hash_table_insert(repl_env, \"/\", int_divide);\n}\n\nint main()\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env();\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step3_env.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nMalVal *EVAL(MalVal *ast, Env *env) {\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    //g_print(\"EVAL apply list: %s\\n\", _pr_str(ast,1));\n    int i, len;\n    if (_count(ast) == 0) { return ast; }\n    MalVal *a0 = _nth(ast, 0);\n    assert_type(a0, MAL_SYMBOL, \"Cannot apply %s\", _pr_str(a0,1));\n    if (strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if (strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        return EVAL(a2, let_env);\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) { return NULL; }\n        MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el);\n        return f(_nth(el, 1), _nth(el, 2));\n    }\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nWRAP_INTEGER_OP(plus,+)\nWRAP_INTEGER_OP(minus,-)\nWRAP_INTEGER_OP(multiply,*)\nWRAP_INTEGER_OP(divide,/)\n\nvoid init_repl_env() {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    env_set(repl_env, \"+\", (MalVal *)int_plus);\n    env_set(repl_env, \"-\", (MalVal *)int_minus);\n    env_set(repl_env, \"*\", (MalVal *)int_multiply);\n    env_set(repl_env, \"/\", (MalVal *)int_divide);\n}\n\nint main()\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env();\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step4_if_fn_do.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nMalVal *EVAL(MalVal *ast, Env *env) {\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    //g_print(\"EVAL apply list: %s\\n\", _pr_str(ast,1));\n    int i, len;\n    if (_count(ast) == 0) { return ast; }\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        return EVAL(a2, let_env);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, _rest(ast), env);\n        return _last(el);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                return EVAL(_nth(ast, 3), env);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            MalVal *a2 = _nth(ast, 2);\n            return EVAL(a2, env);\n        }\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) { return NULL; }\n        MalVal *f = _first(el),\n               *args = _rest(el);\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        return _apply(f, args);\n    }\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nvoid init_repl_env() {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n}\n\nint main()\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env();\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step5_tco.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nMalVal *EVAL(MalVal *ast, Env *env) {\n    while (TRUE) {\n\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    //g_print(\"EVAL apply list: %s\\n\", _pr_str(ast,1));\n    int i, len;\n    if (_count(ast) == 0) { return ast; }\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        ast = a2;\n        env = let_env;\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast) - 1), env);\n        ast = _last(ast);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                ast = _nth(ast, 3);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            ast = _nth(ast, 2);\n        }\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) { return NULL; }\n        MalVal *f = _first(el),\n               *args = _rest(el);\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        if (f->type & MAL_FUNCTION_MAL) {\n            ast = f->val.func.body;\n            env = new_env(f->val.func.env, f->val.func.args, args);\n            // Continue loop\n        } else {\n            return _apply(f, args);\n        }\n    }\n\n    } // TCO while loop\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nvoid init_repl_env() {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n}\n\nint main()\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env();\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step6_file.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nMalVal *EVAL(MalVal *ast, Env *env) {\n    while (TRUE) {\n\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    //g_print(\"EVAL apply list: %s\\n\", _pr_str(ast,1));\n    int i, len;\n    if (_count(ast) == 0) { return ast; }\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        ast = a2;\n        env = let_env;\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);\n        ast = _last(ast);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                ast = _nth(ast, 3);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            ast = _nth(ast, 2);\n        }\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) { return NULL; }\n        MalVal *f = _first(el),\n               *args = _rest(el);\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        if (f->type & MAL_FUNCTION_MAL) {\n            ast = f->val.func.body;\n            env = new_env(f->val.func.env, f->val.func.args, args);\n            // Continue loop\n        } else {\n            return _apply(f, args);\n        }\n    }\n\n    } // TCO while loop\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nMalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); }\n\nvoid init_repl_env(int argc, char *argv[]) {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n    env_set(repl_env, \"eval\",\n            malval_new_function((void*(*)(void *))do_eval, 1));\n\n    MalVal *_argv = _listX(0);\n    for (i=2; i < argc; i++) {\n        MalVal *arg = malval_new_string(argv[i]);\n        g_array_append_val(_argv->val.array, arg);\n    }\n    env_set(repl_env, \"*ARGV*\", _argv);\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n    RE(repl_env, \"\",\n       \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n}\n\nint main(int argc, char *argv[])\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env(argc, argv);\n\n    if (argc > 1) {\n        char *cmd = g_strdup_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n        RE(repl_env, \"\", cmd);\n        return 0;\n    }\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step7_quote.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\nMalVal *quasiquote(MalVal *ast);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nint starts_with(MalVal *ast, const char *sym) {\n    if (ast->type != MAL_LIST)\n        return 0;\n    const MalVal * const a0 = _first(ast);\n    return (a0->type & MAL_SYMBOL) && ! strcmp(sym, a0->val.string);\n}\n\nMalVal *qq_iter(GArray *xs) {\n    MalVal *acc = _listX(0);\n    int i;\n    for (i=xs->len-1; 0<=i; i--) {\n        MalVal * const elt = g_array_index(xs, MalVal*, i);\n        if (starts_with(elt, \"splice-unquote\"))\n            acc = _listX(3, malval_new_symbol(\"concat\"), _nth(elt, 1), acc);\n        else\n            acc = _listX(3, malval_new_symbol(\"cons\"), quasiquote(elt), acc);\n    }\n    return acc;\n}\n\nMalVal *quasiquote(MalVal *ast) {\n    switch (ast->type) {\n    case MAL_LIST:\n        if (starts_with(ast, \"unquote\"))\n            return _nth(ast, 1);\n        else\n            return qq_iter(ast->val.array);\n    case MAL_VECTOR:\n        return _listX(2, malval_new_symbol(\"vec\"), qq_iter(ast->val.array));\n    case MAL_HASH_MAP:\n    case MAL_SYMBOL:\n        return _listX(2, malval_new_symbol(\"quote\"), ast);\n    default:\n        return ast;\n    }\n}\n\nMalVal *EVAL(MalVal *ast, Env *env) {\n    while (TRUE) {\n\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    //g_print(\"EVAL apply list: %s\\n\", _pr_str(ast,1));\n    int i, len;\n    if (_count(ast) == 0) { return ast; }\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        ast = a2;\n        env = let_env;\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quote\\n\");\n        return _nth(ast, 1);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quasiquote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quasiquote\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        ast = quasiquote(a1);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);\n        ast = _last(ast);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                ast = _nth(ast, 3);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            ast = _nth(ast, 2);\n        }\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) { return NULL; }\n        MalVal *f = _first(el),\n               *args = _rest(el);\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        if (f->type & MAL_FUNCTION_MAL) {\n            ast = f->val.func.body;\n            env = new_env(f->val.func.env, f->val.func.args, args);\n            // Continue loop\n        } else {\n            return _apply(f, args);\n        }\n    }\n\n    } // TCO while loop\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nMalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); }\n\nvoid init_repl_env(int argc, char *argv[]) {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n    env_set(repl_env, \"eval\",\n            malval_new_function((void*(*)(void *))do_eval, 1));\n\n    MalVal *_argv = _listX(0);\n    for (i=2; i < argc; i++) {\n        MalVal *arg = malval_new_string(argv[i]);\n        g_array_append_val(_argv->val.array, arg);\n    }\n    env_set(repl_env, \"*ARGV*\", _argv);\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n    RE(repl_env, \"\",\n       \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n}\n\nint main(int argc, char *argv[])\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env(argc, argv);\n\n    if (argc > 1) {\n        char *cmd = g_strdup_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n        RE(repl_env, \"\", cmd);\n        return 0;\n    }\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step8_macros.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\nMalVal *quasiquote(MalVal *ast);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nint starts_with(MalVal *ast, const char *sym) {\n    if (ast->type != MAL_LIST)\n        return 0;\n    const MalVal * const a0 = _first(ast);\n    return (a0->type & MAL_SYMBOL) && ! strcmp(sym, a0->val.string);\n}\n\nMalVal *qq_iter(GArray *xs) {\n    MalVal *acc = _listX(0);\n    int i;\n    for (i=xs->len-1; 0<=i; i--) {\n        MalVal * const elt = g_array_index(xs, MalVal*, i);\n        if (starts_with(elt, \"splice-unquote\"))\n            acc = _listX(3, malval_new_symbol(\"concat\"), _nth(elt, 1), acc);\n        else\n            acc = _listX(3, malval_new_symbol(\"cons\"), quasiquote(elt), acc);\n    }\n    return acc;\n}\n\nMalVal *quasiquote(MalVal *ast) {\n    switch (ast->type) {\n    case MAL_LIST:\n        if (starts_with(ast, \"unquote\"))\n            return _nth(ast, 1);\n        else\n            return qq_iter(ast->val.array);\n    case MAL_VECTOR:\n        return _listX(2, malval_new_symbol(\"vec\"), qq_iter(ast->val.array));\n    case MAL_HASH_MAP:\n    case MAL_SYMBOL:\n        return _listX(2, malval_new_symbol(\"quote\"), ast);\n    default:\n        return ast;\n    }\n}\n\nMalVal *EVAL(MalVal *ast, Env *env) {\n    while (TRUE) {\n\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    if (_count(ast) == 0) { return ast; }\n\n    int i, len;\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        ast = a2;\n        env = let_env;\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quote\\n\");\n        return _nth(ast, 1);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quasiquote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quasiquote\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        ast = quasiquote(a1);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"defmacro!\", a0->val.string) == 0) {\n        //g_print(\"eval apply defmacro!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *old = EVAL(a2, env);\n        if (mal_error) return NULL;\n        MalVal *res = malval_new(MAL_FUNCTION_MAL, NULL);\n        res->val.func = old->val.func;\n        res->ismacro = TRUE;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);\n        ast = _last(ast);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                ast = _nth(ast, 3);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            ast = _nth(ast, 2);\n        }\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->ismacro = FALSE;\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *f = EVAL(a0, env);\n        if (!f || mal_error) { return NULL; }\n        MalVal *rest = _rest(ast);\n        if (f->ismacro) {\n            ast = _apply(f, rest);\n            continue;\n        }\n        MalVal *args = _map2((MalVal *(*)(void*, void*))EVAL, rest, env);\n        if (!args || mal_error) { return NULL; }\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        if (f->type & MAL_FUNCTION_MAL) {\n            ast = f->val.func.body;\n            env = new_env(f->val.func.env, f->val.func.args, args);\n            // Continue loop\n        } else {\n            return _apply(f, args);\n        }\n    }\n\n    } // TCO while loop\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nMalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); }\n\nvoid init_repl_env(int argc, char *argv[]) {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n    env_set(repl_env, \"eval\",\n            malval_new_function((void*(*)(void *))do_eval, 1));\n\n    MalVal *_argv = _listX(0);\n    for (i=2; i < argc; i++) {\n        MalVal *arg = malval_new_string(argv[i]);\n        g_array_append_val(_argv->val.array, arg);\n    }\n    env_set(repl_env, \"*ARGV*\", _argv);\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n    RE(repl_env, \"\",\n       \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n    RE(repl_env, \"\", \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n}\n\nint main(int argc, char *argv[])\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env(argc, argv);\n\n    if (argc > 1) {\n        char *cmd = g_strdup_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n        RE(repl_env, \"\", cmd);\n        return 0;\n    }\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/step9_try.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n#include \"interop.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\nMalVal *quasiquote(MalVal *ast);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nint starts_with(MalVal *ast, const char *sym) {\n    if (ast->type != MAL_LIST)\n        return 0;\n    const MalVal * const a0 = _first(ast);\n    return (a0->type & MAL_SYMBOL) && ! strcmp(sym, a0->val.string);\n}\n\nMalVal *qq_iter(GArray *xs) {\n    MalVal *acc = _listX(0);\n    int i;\n    for (i=xs->len-1; 0<=i; i--) {\n        MalVal * const elt = g_array_index(xs, MalVal*, i);\n        if (starts_with(elt, \"splice-unquote\"))\n            acc = _listX(3, malval_new_symbol(\"concat\"), _nth(elt, 1), acc);\n        else\n            acc = _listX(3, malval_new_symbol(\"cons\"), quasiquote(elt), acc);\n    }\n    return acc;\n}\n\nMalVal *quasiquote(MalVal *ast) {\n    switch (ast->type) {\n    case MAL_LIST:\n        if (starts_with(ast, \"unquote\"))\n            return _nth(ast, 1);\n        else\n            return qq_iter(ast->val.array);\n    case MAL_VECTOR:\n        return _listX(2, malval_new_symbol(\"vec\"), qq_iter(ast->val.array));\n    case MAL_HASH_MAP:\n    case MAL_SYMBOL:\n        return _listX(2, malval_new_symbol(\"quote\"), ast);\n    default:\n        return ast;\n    }\n}\n\nMalVal *EVAL(MalVal *ast, Env *env) {\n    while (TRUE) {\n\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    if (_count(ast) == 0) { return ast; }\n\n    int i, len;\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        ast = a2;\n        env = let_env;\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quote\\n\");\n        return _nth(ast, 1);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quasiquote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quasiquote\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        ast = quasiquote(a1);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"defmacro!\", a0->val.string) == 0) {\n        //g_print(\"eval apply defmacro!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *old = EVAL(a2, env);\n        if (mal_error) return NULL;\n        MalVal *res = malval_new(MAL_FUNCTION_MAL, NULL);\n        res->val.func = old->val.func;\n        res->ismacro = TRUE;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"try*\", a0->val.string) == 0) {\n        //g_print(\"eval apply try*\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *res = EVAL(a1, env);\n        if (ast->val.array->len < 3) {\n            return &mal_nil;\n        }\n        MalVal *a2 = _nth(ast, 2);\n        if (!mal_error) { return res; }\n        MalVal *a20 = _nth(a2, 0);\n        if (strcmp(\"catch*\", a20->val.string) == 0) {\n            MalVal *a21 = _nth(a2, 1);\n            MalVal *a22 = _nth(a2, 2);\n            Env *catch_env = new_env(env,\n                                     _listX(1, a21),\n                                     _listX(1, mal_error));\n            //malval_free(mal_error);\n            mal_error = NULL;\n            res = EVAL(a22, catch_env);\n            return res;\n        } else {\n            return &mal_nil;\n        }\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);\n        ast = _last(ast);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                ast = _nth(ast, 3);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            ast = _nth(ast, 2);\n        }\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->ismacro = FALSE;\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *f = EVAL(a0, env);\n        if (!f || mal_error) { return NULL; }\n        MalVal *rest = _rest(ast);\n        if (f->ismacro) {\n            ast = _apply(f, rest);\n            continue;\n        }\n        MalVal *args = _map2((MalVal *(*)(void*, void*))EVAL, rest, env);\n        if (!args || mal_error) { return NULL; }\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        if (f->type & MAL_FUNCTION_MAL) {\n            ast = f->val.func.body;\n            env = new_env(f->val.func.env, f->val.func.args, args);\n            // Continue loop\n        } else {\n            return _apply(f, args);\n        }\n    }\n\n    } // TCO while loop\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nMalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); }\n\nvoid init_repl_env(int argc, char *argv[]) {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n    env_set(repl_env, \"eval\",\n            malval_new_function((void*(*)(void *))do_eval, 1));\n\n    MalVal *_argv = _listX(0);\n    for (i=2; i < argc; i++) {\n        MalVal *arg = malval_new_string(argv[i]);\n        g_array_append_val(_argv->val.array, arg);\n    }\n    env_set(repl_env, \"*ARGV*\", _argv);\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n    RE(repl_env, \"\",\n       \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n    RE(repl_env, \"\", \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n}\n\nint main(int argc, char *argv[])\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env(argc, argv);\n\n    if (argc > 1) {\n        char *cmd = g_strdup_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n        RE(repl_env, \"\", cmd);\n        return 0;\n    }\n\n    // repl loop\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/stepA_mal.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"types.h\"\n#include \"readline.h\"\n#include \"reader.h\"\n#include \"core.h\"\n#include \"interop.h\"\n\n// Declarations\nMalVal *EVAL(MalVal *ast, Env *env);\nMalVal *quasiquote(MalVal *ast);\n\n// read\nMalVal *READ(char prompt[], char *str) {\n    char *line;\n    MalVal *ast;\n    if (str) {\n        line = str;\n    } else {\n        line = _readline(prompt);\n        if (!line) {\n            _error(\"EOF\");\n            return NULL;\n        }\n    }\n    ast = read_str(line);\n    if (!str) { MAL_GC_FREE(line); }\n    return ast;\n}\n\n// eval\nint starts_with(MalVal *ast, const char *sym) {\n    if (ast->type != MAL_LIST)\n        return 0;\n    const MalVal * const a0 = _first(ast);\n    return (a0->type & MAL_SYMBOL) && ! strcmp(sym, a0->val.string);\n}\n\nMalVal *qq_iter(GArray *xs) {\n    MalVal *acc = _listX(0);\n    int i;\n    for (i=xs->len-1; 0<=i; i--) {\n        MalVal * const elt = g_array_index(xs, MalVal*, i);\n        if (starts_with(elt, \"splice-unquote\"))\n            acc = _listX(3, malval_new_symbol(\"concat\"), _nth(elt, 1), acc);\n        else\n            acc = _listX(3, malval_new_symbol(\"cons\"), quasiquote(elt), acc);\n    }\n    return acc;\n}\n\nMalVal *quasiquote(MalVal *ast) {\n    switch (ast->type) {\n    case MAL_LIST:\n        if (starts_with(ast, \"unquote\"))\n            return _nth(ast, 1);\n        else\n            return qq_iter(ast->val.array);\n    case MAL_VECTOR:\n        return _listX(2, malval_new_symbol(\"vec\"), qq_iter(ast->val.array));\n    case MAL_HASH_MAP:\n    case MAL_SYMBOL:\n        return _listX(2, malval_new_symbol(\"quote\"), ast);\n    default:\n        return ast;\n    }\n}\n\nMalVal *EVAL(MalVal *ast, Env *env) {\n    while (TRUE) {\n\n    if (!ast || mal_error) return NULL;\n\n    MalVal *dbgeval = env_get(env, \"DEBUG-EVAL\");\n    if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {\n        g_print(\"EVAL: %s\\n\", _pr_str(ast,1));\n    }\n\n    if (ast->type == MAL_SYMBOL) {\n        //g_print(\"EVAL symbol: %s\\n\", ast->val.string);\n        MalVal *res = env_get(env, ast->val.string);\n        assert(res, \"'%s' not found\", ast->val.string);\n        return res;\n    } else if (ast->type == MAL_LIST) {\n        // Proceed after this conditional.\n    } else if (ast->type == MAL_VECTOR) {\n        //g_print(\"EVAL sequential: %s\\n\", _pr_str(ast,1));\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);\n        if (!el || mal_error) return NULL;\n        el->type = ast->type;\n        return el;\n    } else if (ast->type == MAL_HASH_MAP) {\n        //g_print(\"EVAL hash_map: %s\\n\", _pr_str(ast,1));\n        GHashTableIter iter;\n        gpointer key, value;\n        MalVal *seq = malval_new_list(MAL_LIST,\n                                    g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                        _count(ast)));\n        g_hash_table_iter_init (&iter, ast->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            MalVal *kname = malval_new_string((char *)key);\n            g_array_append_val(seq->val.array, kname);\n            MalVal *new_val = EVAL((MalVal *)value, env);\n            g_array_append_val(seq->val.array, new_val);\n        }\n        return _hash_map(seq);\n    } else {\n        //g_print(\"EVAL scalar: %s\\n\", _pr_str(ast,1));\n        return ast;\n    }\n\n    // apply list\n    if (_count(ast) == 0) { return ast; }\n\n    int i, len;\n    MalVal *a0 = _nth(ast, 0);\n    if ((a0->type & MAL_SYMBOL) &&\n        strcmp(\"def!\", a0->val.string) == 0) {\n        //g_print(\"eval apply def!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *res = EVAL(a2, env);\n        if (mal_error) return NULL;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"let*\", a0->val.string) == 0) {\n        //g_print(\"eval apply let*\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2),\n               *key, *val;\n        assert_type(a1, MAL_LIST|MAL_VECTOR,\n                    \"let* bindings must be list or vector\");\n        len = _count(a1);\n        assert((len % 2) == 0, \"odd number of let* bindings forms\");\n        Env *let_env = new_env(env, NULL, NULL);\n        for(i=0; i<len; i+=2) {\n            key = g_array_index(a1->val.array, MalVal*, i);\n            val = g_array_index(a1->val.array, MalVal*, i+1);\n            assert_type(key, MAL_SYMBOL, \"let* bind to non-symbol\");\n            env_set(let_env, key->val.string, EVAL(val, let_env));\n        }\n        ast = a2;\n        env = let_env;\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quote\\n\");\n        return _nth(ast, 1);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"quasiquote\", a0->val.string) == 0) {\n        //g_print(\"eval apply quasiquote\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        ast = quasiquote(a1);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"defmacro!\", a0->val.string) == 0) {\n        //g_print(\"eval apply defmacro!\\n\");\n        MalVal *a1 = _nth(ast, 1),\n               *a2 = _nth(ast, 2);\n        MalVal *old = EVAL(a2, env);\n        if (mal_error) return NULL;\n        MalVal *res = malval_new(MAL_FUNCTION_MAL, NULL);\n        res->val.func = old->val.func;\n        res->ismacro = TRUE;\n        env_set(env, a1->val.string, res);\n        return res;\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\".\", a0->val.string) == 0) {\n        //g_print(\"eval apply .\\n\");\n        MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)), env);\n        if (!el || mal_error) return NULL;\n        return invoke_native(el);\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"try*\", a0->val.string) == 0) {\n        //g_print(\"eval apply try*\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *res = EVAL(a1, env);\n        if (ast->val.array->len < 3) {\n            return &mal_nil;\n        }\n        MalVal *a2 = _nth(ast, 2);\n        if (!mal_error) { return res; }\n        MalVal *a20 = _nth(a2, 0);\n        if (strcmp(\"catch*\", a20->val.string) == 0) {\n            MalVal *a21 = _nth(a2, 1);\n            MalVal *a22 = _nth(a2, 2);\n            Env *catch_env = new_env(env,\n                                     _listX(1, a21),\n                                     _listX(1, mal_error));\n            //malval_free(mal_error);\n            mal_error = NULL;\n            res = EVAL(a22, catch_env);\n            return res;\n        } else {\n            return &mal_nil;\n        }\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"do\", a0->val.string) == 0) {\n        //g_print(\"eval apply do\\n\");\n        _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);\n        ast = _last(ast);\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"if\", a0->val.string) == 0) {\n        //g_print(\"eval apply if\\n\");\n        MalVal *a1 = _nth(ast, 1);\n        MalVal *cond = EVAL(a1, env);\n        if (!cond || mal_error) return NULL;\n        if (cond->type & (MAL_FALSE|MAL_NIL)) {\n            // eval false slot form\n            if (ast->val.array->len > 3) {\n                ast = _nth(ast, 3);\n            } else {\n                return &mal_nil;\n            }\n        } else {\n            // eval true slot form\n            ast = _nth(ast, 2);\n        }\n        // Continue loop\n    } else if ((a0->type & MAL_SYMBOL) &&\n               strcmp(\"fn*\", a0->val.string) == 0) {\n        //g_print(\"eval apply fn*\\n\");\n        MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL);\n        mf->ismacro = FALSE;\n        mf->val.func.evaluator = EVAL;\n        mf->val.func.args = _nth(ast, 1);\n        mf->val.func.body = _nth(ast, 2);\n        mf->val.func.env = env;\n        return mf;\n    } else {\n        //g_print(\"eval apply\\n\");\n        MalVal *f = EVAL(a0, env);\n        if (!f || mal_error) { return NULL; }\n        MalVal *rest = _rest(ast);\n        if (f->ismacro) {\n            ast = _apply(f, rest);\n            continue;\n        }\n        MalVal *args = _map2((MalVal *(*)(void*, void*))EVAL, rest, env);\n        if (!args || mal_error) { return NULL; }\n        assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                    \"cannot apply '%s'\", _pr_str(f,1));\n        if (f->type & MAL_FUNCTION_MAL) {\n            ast = f->val.func.body;\n            env = new_env(f->val.func.env, f->val.func.args, args);\n            // Continue loop\n        } else {\n            return _apply(f, args);\n        }\n    }\n\n    } // TCO while loop\n}\n\n// print\nchar *PRINT(MalVal *exp) {\n    if (mal_error) {\n        return NULL;\n    }\n    return _pr_str(exp,1);\n}\n\n// repl\n\n// read and eval\nMalVal *RE(Env *env, char *prompt, char *str) {\n    MalVal *ast, *exp;\n    ast = READ(prompt, str);\n    if (!ast || mal_error) return NULL;\n    exp = EVAL(ast, env);\n    if (ast != exp) {\n        malval_free(ast);    // Free input structure\n    }\n    return exp;\n}\n\n// Setup the initial REPL environment\nEnv *repl_env;\n\nMalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); }\n\nvoid init_repl_env(int argc, char *argv[]) {\n    repl_env = new_env(NULL, NULL, NULL);\n\n    // core.c: defined using C\n    int i;\n    for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {\n        env_set(repl_env, core_ns[i].name,\n                malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));\n    }\n    env_set(repl_env, \"eval\",\n            malval_new_function((void*(*)(void *))do_eval, 1));\n\n    MalVal *_argv = _listX(0);\n    for (i=2; i < argc; i++) {\n        MalVal *arg = malval_new_string(argv[i]);\n        g_array_append_val(_argv->val.array, arg);\n    }\n    env_set(repl_env, \"*ARGV*\", _argv);\n\n    // core.mal: defined using the language itself\n    RE(repl_env, \"\", \"(def! *host-language* \\\"c\\\")\");\n    RE(repl_env, \"\", \"(def! not (fn* (a) (if a false true)))\");\n    RE(repl_env, \"\",\n       \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n    RE(repl_env, \"\", \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n}\n\nint main(int argc, char *argv[])\n{\n    MalVal *exp;\n    char *output;\n    char prompt[100];\n\n    MAL_GC_SETUP();\n\n    // Set the initial prompt and environment\n    snprintf(prompt, sizeof(prompt), \"user> \");\n    init_repl_env(argc, argv);\n\n    if (argc > 1) {\n        char *cmd = g_strdup_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n        RE(repl_env, \"\", cmd);\n        return 0;\n    }\n\n    // repl loop\n    RE(repl_env, \"\", \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n    for(;;) {\n        exp = RE(repl_env, prompt, NULL);\n        if (mal_error && strcmp(\"EOF\", mal_error->val.string) == 0) {\n            return 0;\n        }\n        output = PRINT(exp);\n\n        if (mal_error) {\n            fprintf(stderr, \"Error: %s\\n\", _pr_str(mal_error,1));\n            malval_free(mal_error);\n            mal_error = NULL;\n        } else if (output) {\n            puts(output);\n            MAL_GC_FREE(output);        // Free output string\n        }\n\n        //malval_free(exp);    // Free evaluated expression\n    }\n}\n"
  },
  {
    "path": "impls/c/tests/step5_tco.mal",
    "content": ";; C: skipping non-TCO recursion\n;; Reason: segfaults (unrecoverable)\n"
  },
  {
    "path": "impls/c/tests/stepA_mal.mal",
    "content": "\n;; Testing FFI of \"strlen\"\n(. nil \"int32\" \"strlen\" \"string\" \"abcde\")\n;=>5\n(. nil \"int32\" \"strlen\" \"string\" \"\")\n;=>0\n\n;; Testing FFI of \"strcmp\"\n\n(. nil \"int32\" \"strcmp\" \"string\" \"abc\" \"string\" \"abcA\")\n;=>-65\n(. nil \"int32\" \"strcmp\" \"string\" \"abcA\" \"string\" \"abc\")\n;=>65\n(. nil \"int32\" \"strcmp\" \"string\" \"abc\" \"string\" \"abc\")\n;=>0\n\n\n;; Testing FFI of \"pow\" (libm.so)\n\n(. \"libm.so\" \"double\" \"pow\" \"double\" 2.0 \"double\" 3.0)\n;=>8.000000\n(. \"libm.so\" \"double\" \"pow\" \"double\" 3.0 \"double\" 2.0)\n;=>9.000000\n"
  },
  {
    "path": "impls/c/types.c",
    "content": "#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"types.h\"\n#include \"printer.h\"\n\n#ifdef USE_GC\nvoid nop_free(void* ptr) {\n    (void)ptr; // Unused argument\n}\n\nstatic GMemVTable gc_gmem_vtable = {\n    .malloc = GC_malloc,\n    .realloc = GC_realloc,\n    .free = nop_free,\n    .calloc = NULL,\n    .try_malloc = NULL,\n    .try_realloc = NULL\n};\n\nvoid GC_setup() {\n    GC_INIT();\n    setenv(\"G_SLICE\", \"always-malloc\", 1);\n    g_mem_gc_friendly = TRUE;\n    g_mem_set_vtable(&gc_gmem_vtable);\n}\n\nchar* GC_strdup(const char *src) {\n    if (!src) {\n        return NULL;\n    }\n    char* dst = (char*)MAL_GC_MALLOC(strlen(src) + 1);\n    strcpy(dst, src);\n    return dst;\n}\n#endif\n\n\n// Errors/Exceptions\n\nMalVal *mal_error = NULL; // WARNGIN: global state\nvoid _error(const char *fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n    mal_error = malval_new_string(g_strdup_vprintf(fmt, args));\n}\n\n// Constant atomic values\n\nMalVal mal_nil = {MAL_NIL, NULL, {0}, 0};\nMalVal mal_true = {MAL_TRUE, NULL, {0}, 0};\nMalVal mal_false = {MAL_FALSE, NULL, {0}, 0};\n\n\n// General Functions\n\n// Print a hash table\n#include <glib-object.h>\nvoid g_hash_table_print(GHashTable *hash_table) {\n    GHashTableIter iter;\n    gpointer key, value;\n\n    g_hash_table_iter_init (&iter, hash_table);\n    while (g_hash_table_iter_next (&iter, &key, &value)) {\n        g_print (\"%s/%p \", (const char *) key, (void *) value);\n        //g_print (\"%s \", (const char *) key);\n    }\n}\n\nGHashTable *g_hash_table_copy(GHashTable *src_table) {\n    GHashTable *new_table = g_hash_table_new(g_str_hash, g_str_equal);\n    GHashTableIter iter;\n    gpointer key, value;\n\n    g_hash_table_iter_init (&iter, src_table);\n    while (g_hash_table_iter_next (&iter, &key, &value)) {\n        g_hash_table_insert(new_table, key, value);\n    }\n    return new_table;\n}\n\nint min(int a, int b) { return a < b ? a : b; }\nint max(int a, int b) { return a > b ? a : b; }\n\nint _count(MalVal *obj) {\n    switch (obj->type) {\n    case MAL_NIL:      return 0;\n    case MAL_LIST:     return obj->val.array->len;\n    case MAL_VECTOR:   return obj->val.array->len;\n    case MAL_HASH_MAP: return g_hash_table_size(obj->val.hash_table);\n    case MAL_STRING:   return strlen(obj->val.string);\n    default:\n        _error(\"count unsupported for type %d\\n\", obj->type);\n        return 0;\n    }\n}\n\n// Allocate a malval and set its type and value\nMalVal *malval_new(MalType type, MalVal *metadata) {\n    MalVal *mv = (MalVal*)MAL_GC_MALLOC(sizeof(MalVal));\n    mv->type = type;\n    mv->metadata = metadata;\n    return mv;\n}\n\nvoid malval_free(MalVal *mv) {\n    // TODO: free collection items\n    if (!(mv->type & (MAL_NIL|MAL_TRUE|MAL_FALSE))) {\n        MAL_GC_FREE(mv);\n    }\n}\n\nMalVal *malval_new_integer(gint64 val) {\n    MalVal *mv = malval_new(MAL_INTEGER, NULL);\n    mv->val.intnum = val;\n    return mv;\n}\n\nMalVal *malval_new_float(gdouble val) {\n    MalVal *mv = malval_new(MAL_FLOAT, NULL);\n    mv->val.floatnum = val;\n    return mv;\n}\n\nMalVal *malval_new_string(char *val) {\n    MalVal *mv = malval_new(MAL_STRING, NULL);\n    mv->val.string = val;\n    return mv;\n}\n\nMalVal *malval_new_symbol(char *val) {\n    MalVal *mv = malval_new(MAL_SYMBOL, NULL);\n    mv->val.string = val;\n    return mv;\n}\n\nMalVal *malval_new_keyword(char *val) {\n    MalVal *mv = malval_new(MAL_STRING, NULL);\n    mv->val.string = g_strdup_printf(\"\\x7f%s\", val);\n    return mv;\n}\n\nMalVal *malval_new_list(MalType type, GArray *val) {\n    MalVal *mv = malval_new(type, NULL);\n    mv->val.array = val;\n    return mv;\n}\n\nMalVal *malval_new_hash_map(GHashTable *val) {\n    MalVal *mv = malval_new(MAL_HASH_MAP, NULL);\n    mv->val.hash_table = val;\n    return mv;\n}\n\nMalVal *malval_new_atom(MalVal *val) {\n    MalVal *mv = malval_new(MAL_ATOM, NULL);\n    mv->val.atom_val = val;\n    return mv;\n}\n\n\nMalVal *malval_new_function(void *(*func)(void *), int arg_cnt) {\n    MalVal *mv = malval_new(MAL_FUNCTION_C, NULL);\n    mv->func_arg_cnt = arg_cnt;\n    assert(mv->func_arg_cnt <= 20,\n            \"native function restricted to 20 args (%d given)\",\n            mv->func_arg_cnt);\n    mv->ismacro = FALSE;\n    switch (arg_cnt) {\n    case -1: mv->val.f1  = (void *(*)(void*))func; break;\n    case 0:  mv->val.f0  = (void *(*)())func; break;\n    case 1:  mv->val.f1  = (void *(*)(void*))func; break;\n    case 2:  mv->val.f2  = (void *(*)(void*,void*))func; break;\n    case 3:  mv->val.f3  = (void *(*)(void*,void*,void*))func; break;\n    case 4:  mv->val.f4  = (void *(*)(void*,void*,void*,void*))func; break;\n    case 5:  mv->val.f5  = (void *(*)(void*,void*,void*,void*,void*))func; break;\n    case 6:  mv->val.f6  = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*))func; break;\n    case 7:  mv->val.f7  = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*))func; break;\n    case 8:  mv->val.f8  = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*))func; break;\n    case 9:  mv->val.f9  = (void *(*)(void*,void*,void*,void*,void*,\n                                       void*,void*,void*,void*))func; break;\n    case 10: mv->val.f10 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*))func; break;\n    case 11: mv->val.f11 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*))func; break;\n    case 12: mv->val.f12 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*))func; break;\n    case 13: mv->val.f13 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*))func; break;\n    case 14: mv->val.f14 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*))func; break;\n    case 15: mv->val.f15 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*))func; break;\n    case 16: mv->val.f16 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*))func; break;\n    case 17: mv->val.f17 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*))func; break;\n    case 18: mv->val.f18 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*))func; break;\n    case 19: mv->val.f19 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*))func; break;\n    case 20: mv->val.f20 = (void *(*)(void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*,\n                                      void*,void*,void*,void*,void*))func; break;\n    }\n    return mv;\n}\n\nMalVal *_apply(MalVal *f, MalVal *args) {\n    MalVal *res;\n    assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,\n                \"Cannot invoke %s\", _pr_str(f,1));\n    if (f->type & MAL_FUNCTION_MAL) {\n        Env *fn_env = new_env(f->val.func.env, f->val.func.args, args);\n        res = f->val.func.evaluator(f->val.func.body, fn_env);\n        return res;\n    } else {\n        MalVal *a = args;\n        assert((f->func_arg_cnt == -1) ||\n               (f->func_arg_cnt == _count(args)),\n               \"Length of formal params (%d) does not match actual parameters (%d)\",\n               f->func_arg_cnt, _count(args));\n        switch (f->func_arg_cnt) {\n        case -1: res=f->val.f1 (a); break;\n        case 0:  res=f->val.f0 (); break;\n        case 1:  res=f->val.f1 (_nth(a,0)); break;\n        case 2:  res=f->val.f2 (_nth(a,0),_nth(a,1)); break;\n        case 3:  res=f->val.f3 (_nth(a,0),_nth(a,1),_nth(a,2)); break;\n        case 4:  res=f->val.f4 (_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3)); break;\n        case 5:  res=f->val.f5 (_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4)); break;\n        case 6:  res=f->val.f6 (_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5)); break;\n        case 7:  res=f->val.f7 (_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6)); break;\n        case 8:  res=f->val.f8 (_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7)); break;\n        case 9:  res=f->val.f9 (_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8)); break;\n        case 10: res=f->val.f10(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9)); break;\n        case 11: res=f->val.f11(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10)); break;\n        case 12: res=f->val.f12(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11)); break;\n        case 13: res=f->val.f13(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12)); break;\n        case 14: res=f->val.f14(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13)); break;\n        case 15: res=f->val.f15(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13),_nth(a,14)); break;\n        case 16: res=f->val.f16(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13),_nth(a,14),\n                                _nth(a,15)); break;\n        case 17: res=f->val.f17(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13),_nth(a,14),\n                                _nth(a,15),_nth(a,16)); break;\n        case 18: res=f->val.f18(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13),_nth(a,14),\n                                _nth(a,15),_nth(a,16),_nth(a,17)); break;\n        case 19: res=f->val.f19(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13),_nth(a,14),\n                                _nth(a,15),_nth(a,16),_nth(a,17),_nth(a,18)); break;\n        case 20: res=f->val.f20(_nth(a,0),_nth(a,1),_nth(a,2),_nth(a,3),_nth(a,4),\n                                _nth(a,5),_nth(a,6),_nth(a,7),_nth(a,8),_nth(a,9),\n                                _nth(a,10),_nth(a,11),_nth(a,12),_nth(a,13),_nth(a,14),\n                                _nth(a,15),_nth(a,16),_nth(a,17),_nth(a,18),_nth(a,19)); break;\n        }\n        return res;\n    }\n}\n\n\nint _equal_Q(MalVal *a, MalVal *b) {\n    GHashTableIter iter;\n    gpointer key, value;\n\n    if (a == NULL || b == NULL) { return FALSE; }\n\n    // If types are the same or both are sequential then they might be equal\n    if (!((a->type == b->type) ||\n          (_sequential_Q(a) && _sequential_Q(b)))) {\n        return FALSE;\n    }\n    switch (a->type) {\n    case MAL_NIL:\n    case MAL_TRUE:\n    case MAL_FALSE:\n        return a->type == b->type;\n    case MAL_INTEGER:\n        return a->val.intnum == b->val.intnum;\n    case MAL_FLOAT:\n        return a->val.floatnum == b->val.floatnum;\n    case MAL_SYMBOL:\n    case MAL_STRING:\n        if (strcmp(a->val.string, b->val.string) == 0) {\n            return TRUE;\n        } else {\n            return FALSE;\n        }\n    case MAL_LIST:\n    case MAL_VECTOR:\n        if (a->val.array->len != b->val.array->len) {\n            return FALSE;\n        }\n        int i;\n        for (i=0; i<a->val.array->len; i++) {\n            if (! _equal_Q(g_array_index(a->val.array, MalVal*, i),\n                           g_array_index(b->val.array, MalVal*, i))) {\n                return FALSE;\n            }\n        }\n        return TRUE;\n    case MAL_HASH_MAP:\n        if (g_hash_table_size(a->val.hash_table) !=\n            g_hash_table_size(b->val.hash_table)) {\n            return FALSE;\n        }\n        g_hash_table_iter_init (&iter, a->val.hash_table);\n        while (g_hash_table_iter_next (&iter, &key, &value)) {\n            if (!g_hash_table_contains(b->val.hash_table, key)) {\n                return FALSE;\n            }\n            MalVal *aval = (MalVal *) g_hash_table_lookup(a->val.hash_table, key);\n            MalVal *bval = (MalVal *) g_hash_table_lookup(b->val.hash_table, key);\n            if (!_equal_Q(aval, bval)) {\n                return FALSE;\n            }\n        }\n        return TRUE;\n    case MAL_FUNCTION_C:\n    case MAL_FUNCTION_MAL:\n        return a->val.f0 == b->val.f0;\n    default:\n        _error(\"_equal_Q unsupported comparison type %d\\n\", a->type);\n        return FALSE;\n    }\n}\n\n\n// Lists\nMalVal *_listX(int count, ...) {\n    MalVal *seq = malval_new_list(MAL_LIST,\n                                  g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                                    count));\n    MalVal *v;\n    va_list ap;\n    va_start(ap, count);\n    while (count-- > 0) {\n        v = va_arg(ap, MalVal*);\n        g_array_append_val(seq->val.array, v);\n    }\n    va_end(ap);\n    return seq;\n}\n\nMalVal *_list(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"list called with invalid arguments\");\n    args->type = MAL_LIST;\n    return args;\n}\n\nint _list_Q(MalVal *seq) {\n    return seq->type & MAL_LIST;\n}\n\n\n// Vectors\nMalVal *_vector(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"vector called with invalid arguments\");\n    args->type = MAL_VECTOR;\n    return args;\n}\n\nint _vector_Q(MalVal *seq) {\n    return seq->type & MAL_VECTOR;\n}\n\n\n// Hash maps\nMalVal *_hash_map(MalVal *args) {\n    assert_type(args, MAL_LIST|MAL_VECTOR,\n                \"hash-map called with non-sequential arguments\");\n    GHashTable *htable = g_hash_table_new(g_str_hash, g_str_equal);\n    MalVal *hm = malval_new_hash_map(htable);\n    return _assoc_BANG(hm, args);\n}\n\nint _hash_map_Q(MalVal *seq) {\n    return seq->type & MAL_HASH_MAP;\n}\n\nMalVal *_assoc_BANG(MalVal* hm, MalVal *args) {\n    assert((_count(args) % 2) == 0,\n           \"odd number of parameters to assoc!\");\n    GHashTable *htable = hm->val.hash_table;\n    int i;\n    MalVal *k, *v;\n    for (i=0; i<_count(args); i+=2) {\n        k = g_array_index(args->val.array, MalVal*, i);\n        assert_type(k, MAL_STRING,\n                    \"assoc! called with non-string key\");\n        v = g_array_index(args->val.array, MalVal*, i+1);\n        g_hash_table_insert(htable, k->val.string, v);\n    }\n    return hm;\n}\n\nMalVal *_dissoc_BANG(MalVal* hm, MalVal *args) {\n    GHashTable *htable = hm->val.hash_table;\n    int i;\n    MalVal *k, *v;\n    for (i=0; i<_count(args); i++) {\n        k = g_array_index(args->val.array, MalVal*, i);\n        assert_type(k, MAL_STRING,\n                    \"dissoc! called with non-string key\");\n        g_hash_table_remove(htable, k->val.string);\n    }\n    return hm;\n}\n\n\n// Atoms\nint _atom_Q(MalVal *exp) {\n    return exp->type & MAL_ATOM;\n}\n\n\n// Sequence functions\nMalVal *_slice(MalVal *seq, int start, int end) {\n    int i, new_len = max(0, min(end-start,\n                                _count(seq)-start));\n    GArray *new_arr = g_array_sized_new(TRUE, TRUE, sizeof(MalVal*),\n                                        new_len);\n    for (i=start; i<start+new_len; i++) {\n        g_array_append_val(new_arr, g_array_index(seq->val.array, MalVal*, i));\n    }\n    return malval_new_list(MAL_LIST, new_arr);\n}\n\n\nint _sequential_Q(MalVal *seq) {\n    return seq->type & (MAL_LIST|MAL_VECTOR);\n}\n\nMalVal *_nth(MalVal *seq, int idx) {\n    assert_type(seq, MAL_LIST|MAL_VECTOR,\n                \"_nth called with non-sequential\");\n    if (idx >= _count(seq)) {\n        abort(\"nth: index out of range\");\n    }\n    return g_array_index(seq->val.array, MalVal*, idx);\n}\n\nMalVal *_first(MalVal *seq) {\n    assert_type(seq, MAL_NIL|MAL_LIST|MAL_VECTOR,\n                \"_first called with non-sequential\");\n    if (_count(seq) == 0) {\n        return &mal_nil;\n    }\n    return g_array_index(seq->val.array, MalVal*, 0);\n}\n\nMalVal *_last(MalVal *seq) {\n    assert_type(seq, MAL_LIST|MAL_VECTOR,\n                \"_last called with non-sequential\");\n    if (_count(seq) == 0) {\n        return &mal_nil;\n    }\n    return g_array_index(seq->val.array, MalVal*, _count(seq)-1);\n}\n\n\nMalVal *_rest(MalVal *seq) {\n    return _slice(seq, 1, _count(seq));\n}\n\n\nMalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2) {\n    MalVal *e, *el;\n    assert_type(lst, MAL_LIST|MAL_VECTOR,\n                \"_map called with non-sequential\");\n    int i, len = _count(lst);\n    el = malval_new_list(MAL_LIST,\n                         g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), len));\n    for (i=0; i<len; i++) {\n        e = func(g_array_index(lst->val.array, MalVal*, i), arg2);\n        if (!e || mal_error) return NULL;\n        g_array_append_val(el->val.array, e);\n    }\n    return el;\n}\n"
  },
  {
    "path": "impls/c/types.h",
    "content": "#ifndef __MAL_TYPES__\n#define __MAL_TYPES__\n\n#include <glib.h>\n\n#ifdef USE_GC\n\n#include <gc/gc.h>\nvoid nop_free(void* ptr);\nvoid GC_setup();\nchar* GC_strdup(const char *src);\n#define MAL_GC_SETUP()  GC_setup()\n#define MAL_GC_MALLOC   GC_MALLOC\n#define MAL_GC_FREE     nop_free\n#define MAL_GC_STRDUP   GC_strdup\n\n#else\n\n#include <string.h>\n#define MAL_GC_SETUP()\n#define MAL_GC_MALLOC   malloc\n#define MAL_GC_FREE     free\n#define MAL_GC_STRDUP   strdup\n\n#endif\n\nstruct MalVal; // pre-declare\n\n\n// Env (implentation in env.c)\n\ntypedef struct Env {\n    struct Env *outer;\n    GHashTable *table;\n} Env;\n\nEnv *new_env(Env *outer, struct MalVal* binds, struct MalVal *exprs);\nstruct MalVal *env_get(Env *env, const char *key);\n//  Returns NULL if the key is missing.\nvoid env_set(Env *env, char *key, struct MalVal *val);\n\n\n// Utility functiosn\nvoid g_hash_table_print(GHashTable *hash_table);\nGHashTable *g_hash_table_copy(GHashTable *src_table);\n\n\n// Errors/exceptions\n\nextern struct MalVal *mal_error;\nvoid _error(const char *fmt, ...);\n\n#define abort(format, ...) \\\n    { _error(format, ##__VA_ARGS__); return NULL; }\n\n#define assert(test, format, ...) \\\n    if (!(test)) { \\\n        _error(format, ##__VA_ARGS__); \\\n        return NULL; \\\n    }\n\n#define assert_type(mv, typ, format, ...) \\\n    if (!(mv->type & (typ))) { \\\n        _error(format, ##__VA_ARGS__); \\\n        return NULL; \\\n    }\n\n\ntypedef enum {\n    MAL_NIL = 1,\n    MAL_TRUE = 2,\n    MAL_FALSE = 4,\n    MAL_INTEGER = 8,\n    MAL_FLOAT = 16,\n    MAL_SYMBOL = 32,\n    MAL_STRING = 64,\n    MAL_LIST = 128,\n    MAL_VECTOR = 256,\n    MAL_HASH_MAP = 512,\n    MAL_ATOM = 1024,\n    MAL_FUNCTION_C = 2048,\n    MAL_FUNCTION_MAL = 4096,\n} MalType;\n\ntypedef struct MalVal {\n    MalType type;\n    struct MalVal *metadata;\n    union {\n        gint64 intnum;\n        gdouble floatnum;\n        char *string;\n        GArray *array;\n        GHashTable *hash_table;\n        struct MalVal *atom_val;\n        void *(*f0) ();\n        void *(*f1) (void*);\n        void *(*f2) (void*,void*);\n        void *(*f3) (void*,void*,void*);\n        void *(*f4) (void*,void*,void*,void*);\n        void *(*f5) (void*,void*,void*,void*,void*);\n        void *(*f6) (void*,void*,void*,void*,void*,void*);\n        void *(*f7) (void*,void*,void*,void*,void*,void*,void*);\n        void *(*f8) (void*,void*,void*,void*,void*,void*,void*,void*);\n        void *(*f9) (void*,void*,void*,void*,void*,void*,void*,void*,void*);\n        void *(*f10)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*);\n        void *(*f11)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*);\n        void *(*f12)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*);\n        void *(*f13)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*);\n        void *(*f14)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*);\n        void *(*f15)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*,void*);\n        void *(*f16)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*,void*,void*);\n        void *(*f17)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*,void*,void*,void*);\n        void *(*f18)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*,void*,void*,void*,void*);\n        void *(*f19)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*,void*,void*,void*,void*,void*);\n        void *(*f20)(void*,void*,void*,void*,void*,void*,void*,void*,void*,void*,\n                     void*,void*,void*,void*,void*,void*,void*,void*,void*,void*);\n        struct {\n            struct MalVal *(*evaluator)(struct MalVal *, Env *);\n            struct MalVal *args;\n            struct MalVal *body;\n            struct Env    *env;\n        } func;\n    } val;\n    int func_arg_cnt;\n    int ismacro;\n} MalVal;\n\n// Constants\n\nextern MalVal mal_nil;\nextern MalVal mal_true;\nextern MalVal mal_false;\n\n\n// Declare functions used internally (by other C code).\n// Mal visible functions are \"exported\" in types_ns\n\nMalVal *malval_new(MalType type, MalVal *metadata);\nvoid malval_free(MalVal *mv);\nMalVal *malval_new_integer(gint64 val);\nMalVal *malval_new_float(gdouble val);\nMalVal *malval_new_string(char *val);\nMalVal *malval_new_symbol(char *val);\nMalVal *malval_new_keyword(char *val);\nMalVal *malval_new_list(MalType type, GArray *val);\nMalVal *malval_new_hash_map(GHashTable *val);\nMalVal *malval_new_atom(MalVal *val);\nMalVal *malval_new_function(void *(*func)(void *), int arg_cnt);\n\n// Numbers\n#define WRAP_INTEGER_OP(name, op) \\\n    static MalVal *int_ ## name(MalVal *a, MalVal *b)     { \\\n        return malval_new_integer(a->val.intnum op b->val.intnum); \\\n    }\n#define WRAP_INTEGER_CMP_OP(name, op) \\\n    static MalVal *int_ ## name(MalVal *a, MalVal *b)     { \\\n        return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \\\n    }\n\n// Collections\nMalVal *_listX(int count, ...);\nMalVal *_list(MalVal *args);\nMalVal *_vector(MalVal *args);\nMalVal *_hash_map(MalVal *args);\nMalVal *_assoc_BANG(MalVal* hm, MalVal *args);\nMalVal *_dissoc_BANG(MalVal* hm, MalVal *args);\n\nMalVal *_apply(MalVal *f, MalVal *el);\n\nchar *_pr_str(MalVal *args, int print_readably);\n\nMalVal *_slice(MalVal *seq, int start, int end);\nMalVal *_nth(MalVal *seq, int idx);\nMalVal *_first(MalVal *seq);\nMalVal *_rest(MalVal *seq);\nMalVal *_last(MalVal *seq);\nint _count(MalVal *obj);\n\nint _atom_Q(MalVal *exp);\nint _sequential_Q(MalVal *seq);\nint _list_Q(MalVal *seq);\nint _vector_Q(MalVal *seq);\nint _hash_map_Q(MalVal *seq);\nint _equal_Q(MalVal *a, MalVal *b);\n\nMalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Duncan Watts <fungiblecog@gmail.com>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Install gcc\nRUN apt-get -y install gcc\n\n# Libraries needed for the C impl\nRUN apt-get -y install libffi-dev libgc-dev libedit-dev pkgconf\n"
  },
  {
    "path": "impls/c.2/Makefile",
    "content": "CC = gcc\n\nCFLAGS = -std=c99 -g -Wall -Wextra -fanalyzer\n\n# The code defines new format specifiers.\nCPPFLAGS = -Wno-format\n\nifdef debug_reader\n  CPPFLAGS += -DDEBUG_READER\nendif\nifdef debug_hash\n  CPPFLAGS += -DDEBUG_HASH\nendif\nifdef debug_hashmap\n  CPPFLAGS += -DDEBUG_HASHMAP\nendif\nifdef debug_hash_collisions\n  CPPFLAGS += -DDEBUG_HASH_COLLISIONS\nendif\nifndef no_fast\n  CFLAGS  += -flto -O3 -DNDEBUG\n  LDFLAGS += -flto\nendif\nifdef profile\n  CFLAGS  += -pg\n  LDFLAGS += -pg\nendif\nifdef readline\n  pkgconfig_modules += readline\n  CFLAGS += -DUSE_READLINE\nelse\n  pkgconfig_modules += libedit\nendif\nifndef no_ffi\n  pkgconfig_modules += libffi\n  CFLAGS += -DWITH_FFI\nendif\n\npkgconfig_modules += bdw-gc\nCFLAGS += $(shell pkg-config --cflags $(pkgconfig_modules))\nLDLIBS += $(shell pkg-config --libs   $(pkgconfig_modules))\n\nS0 = step0_repl\nS1 = step1_read_print\nS2 = step2_eval\nS3 = step3_env\nS4 = step4_if_fn_do\nS5 = step5_tco\nS6 = step6_file\nS7 = step7_quote\nS8 = step8_macros\nS9 = step9_try\nSA = stepA_mal\n\nS4+ := $(S4) $(S5) $(S6) $(S7) $(S8) $(S9) $(SA)\nS3+ := $(S3) $(S4+)\nS1+ := $(S1) $(S2) $(S3+)\nS0+ := $(S0) $(S1+)\n\nall: $(S0+)\n\n# GCC could create temporary objects files, but separate recipes for\n# .o objects give faster build cycles when debugging.\n$(S0+): readline.o\n$(S1+): error.o hashmap.o linked_list.o printer.o reader.o types.o vector.o\n$(S3+): env.o\n$(S4+): core.o\n\ninclude deps\ndeps:\n\t$(CC) -MM -MF- *.c > $@\n\nclean:\n\trm -f $(S0+) *.o deps gmon.out\n\n.PHONY: all clean\n"
  },
  {
    "path": "impls/c.2/README",
    "content": "make -Cimpls/c.2/ clean\nmake -Cimpls/c.2/ no_fast=1\nmake test^c.2 HARD=1 REGRESS=1\nmake test^mal HARD=1 MAL_IMPL=c.2\n\nmake -Cimpls/c.2/ clean\nmake -Cimpls/c.2/\nmake perf^c.2\n\nmake -Cimpls/c.2/ clean\nmake -Cimpls/c.2/ stepA_mal profile=1\nmake perf^c.2\n(cd impls/c.2/ && gprof stepA_mal | less)\n"
  },
  {
    "path": "impls/c.2/core.c",
    "content": "#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/time.h>\n\n#include <gc.h>\n\n/* only needed for ffi */\n#ifdef WITH_FFI\n#include <dlfcn.h>\n#include <ffi.h>\n#endif\n\n#include \"hashmap.h\"\n#include \"core.h\"\n#include \"printer.h\"\n#include \"reader.h\"\n#include \"error.h\"\n#include \"linked_list.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n/* forward references to main file */\nMalType apply(MalType fn, list args);\n\n// Helper functions\n\nMalType make_boolean(bool);\n\n/* core ns functions */\nMalType mal_add(list);\nMalType mal_sub(list);\nMalType mal_mul(list);\nMalType mal_div(list);\n\nMalType mal_prn(list);\nMalType mal_println(list);\nMalType mal_pr_str(list);\nMalType mal_str(list);\nMalType mal_read_string(list);\nMalType mal_slurp(list);\n\nMalType mal_list_questionmark(list);\nMalType mal_empty_questionmark(list);\nMalType mal_count(list);\nMalType mal_cons(list);\nMalType mal_concat(list);\nMalType mal_nth(list);\nMalType mal_first(list);\nMalType mal_rest(list);\n\nMalType mal_equals(list);\nMalType mal_lessthan(list);\nMalType mal_lessthanorequalto(list);\nMalType mal_greaterthan(list);\nMalType mal_greaterthanorequalto(list);\n\nMalType mal_atom(list);\nMalType mal_atom_questionmark(list);\nMalType mal_deref(list);\nMalType mal_reset_bang(list);\nMalType mal_swap_bang(list);\n\nMalType mal_throw(list);\nMalType mal_apply(list);\nMalType mal_map(list);\n\nMalType mal_nil_questionmark(list);\nMalType mal_true_questionmark(list);\nMalType mal_false_questionmark(list);\nMalType mal_symbol_questionmark(list);\nMalType mal_keyword_questionmark(list);\nMalType mal_symbol(list);\nMalType mal_keyword(list);\n\nMalType mal_vec(list);\nMalType mal_vector(list);\nMalType mal_vector_questionmark(list);\nMalType mal_sequential_questionmark(list);\nMalType mal_hash_map(list);\nMalType mal_map_questionmark(list);\nMalType mal_assoc(list);\nMalType mal_dissoc(list);\nMalType mal_get(list);\nMalType mal_contains_questionmark(list);\nMalType mal_keys(list);\nMalType mal_vals(list);\nMalType mal_string_questionmark(list);\nMalType mal_number_questionmark(list);\nMalType mal_fn_questionmark(list);\nMalType mal_macro_questionmark(list);\n\nMalType mal_time_ms(list);\nMalType mal_conj(list);\nMalType mal_seq(list);\nMalType mal_meta(list);\nMalType mal_with_meta(list);\n\nMalType mal_readline(list);\n\n/* only needed for ffi */\n#ifdef WITH_FFI\nMalType mal_dot(list);\n#endif\n\nstruct ns_s THE_CORE_NS[] = {\n\n  /* arithmetic */\n  { \"+\", mal_add },\n  { \"-\", mal_sub },\n  { \"*\", mal_mul },\n  { \"/\", mal_div },\n\n  /* strings */\n  { \"prn\", mal_prn },\n  { \"pr-str\", mal_pr_str },\n  { \"str\", mal_str },\n  { \"println\", mal_println },\n  { \"read-string\", mal_read_string },\n\n  /* files */\n  { \"slurp\", mal_slurp },\n\n  /* lists */\n  { \"list\", make_list },\n  { \"empty?\", mal_empty_questionmark },\n  { \"count\", mal_count },\n  { \"cons\", mal_cons },\n  { \"concat\", mal_concat },\n  { \"nth\", mal_nth },\n  { \"first\", mal_first },\n  { \"rest\", mal_rest },\n\n  /* predicates */\n  { \"=\", mal_equals },\n  { \"<\", mal_lessthan },\n  { \"<=\", mal_lessthanorequalto },\n  { \">\", mal_greaterthan },\n  { \">=\", mal_greaterthanorequalto },\n\n  { \"list?\", mal_list_questionmark },\n  { \"nil?\", mal_nil_questionmark },\n  { \"true?\", mal_true_questionmark },\n  { \"false?\", mal_false_questionmark },\n  { \"symbol?\", mal_symbol_questionmark },\n  { \"keyword?\", mal_keyword_questionmark },\n  { \"vector?\", mal_vector_questionmark },\n  { \"sequential?\", mal_sequential_questionmark },\n  { \"map?\", mal_map_questionmark },\n  { \"string?\", mal_string_questionmark },\n  { \"number?\", mal_number_questionmark },\n  { \"fn?\", mal_fn_questionmark },\n  { \"macro?\", mal_macro_questionmark },\n\n  /* atoms */\n  { \"atom\", mal_atom },\n  { \"atom?\", mal_atom_questionmark },\n  { \"deref\", mal_deref },\n  { \"reset!\", mal_reset_bang },\n  { \"swap!\", mal_swap_bang },\n\n  /* other */\n  { \"throw\", mal_throw },\n  { \"apply\", mal_apply },\n  { \"map\", mal_map },\n\n  { \"symbol\", mal_symbol },\n  { \"keyword\", mal_keyword },\n  { \"vec\", mal_vec },\n  { \"vector\", mal_vector },\n  { \"hash-map\", mal_hash_map },\n\n  /* hash-maps */\n  { \"contains?\", mal_contains_questionmark },\n  { \"assoc\", mal_assoc },\n  { \"dissoc\", mal_dissoc },\n  { \"get\", mal_get },\n  { \"keys\", mal_keys },\n  { \"vals\", mal_vals },\n\n  /* misc */\n  { \"time-ms\", mal_time_ms },\n  { \"conj\", mal_conj },\n  { \"seq\", mal_seq },\n  { \"meta\", mal_meta },\n  { \"with-meta\", mal_with_meta },\n\n  { \"readline\", mal_readline },\n\n  /* only needed for ffi */\n  #ifdef WITH_FFI\n  { \".\", mal_dot },\n  #endif\n};\n\nvoid ns_make_core(ns* core, size_t* size) {\n  *core = THE_CORE_NS;\n  *size = sizeof(THE_CORE_NS) / sizeof(struct ns_s);\n}\n\n/* core function definitons */\n\n#define generic_arithmetic(name, op, iconst, fconst)                   \\\n  MalType name(list args) {                                            \\\n    explode2(#op, args, a1, a2);                                       \\\n    long i1, i2;                                                       \\\n    double f1, f2;                                                     \\\n    if (is_integer(a1, &i1)) {                                         \\\n      if (is_integer(a2, &i2)) return iconst(i1 op i2);                \\\n      if (is_float  (a2, &f2)) return fconst(i1 op f2);                \\\n      bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a2);              \\\n    }                                                                  \\\n    if (is_float(a1, &f1)) {                                           \\\n      if (is_integer(a2, &i2)) return iconst(f1 op i2);                \\\n      if (is_float  (a2, &f2)) return fconst(f1 op f2);                \\\n      bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a2);              \\\n    }                                                                  \\\n    bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a1);                \\\n  }\ngeneric_arithmetic(mal_add,                  +,  make_integer, make_float)\ngeneric_arithmetic(mal_sub,                  -,  make_integer, make_float)\ngeneric_arithmetic(mal_mul,                  *,  make_integer, make_float)\ngeneric_arithmetic(mal_div,                  /,  make_integer, make_float)\ngeneric_arithmetic(mal_lessthan,             <,  make_boolean, make_boolean)\ngeneric_arithmetic(mal_lessthanorequalto,    <=, make_boolean, make_boolean)\ngeneric_arithmetic(mal_greaterthan,          >,  make_boolean, make_boolean)\ngeneric_arithmetic(mal_greaterthanorequalto, >=, make_boolean, make_boolean)\n\n#define generic_type_predicate(name, mask)   \\\n  MalType mal_##name##_questionmark(list args) { \\\n    explode1(#name \"?\", args, val);          \\\n    return make_boolean(type(val) & (mask)); \\\n  }\ngeneric_type_predicate(list, MALTYPE_LIST)\ngeneric_type_predicate(atom, MALTYPE_ATOM)\ngeneric_type_predicate(nil, MALTYPE_NIL)\ngeneric_type_predicate(true, MALTYPE_TRUE)\ngeneric_type_predicate(false, MALTYPE_FALSE)\ngeneric_type_predicate(symbol, MALTYPE_SYMBOL)\ngeneric_type_predicate(keyword, MALTYPE_KEYWORD)\ngeneric_type_predicate(vector, MALTYPE_VECTOR)\ngeneric_type_predicate(sequential, MALTYPE_LIST | MALTYPE_VECTOR)\ngeneric_type_predicate(map, MALTYPE_HASHMAP)\ngeneric_type_predicate(string, MALTYPE_STRING)\ngeneric_type_predicate(number, MALTYPE_FLOAT | MALTYPE_INTEGER)\ngeneric_type_predicate(fn, MALTYPE_CLOSURE | MALTYPE_FUNCTION)\ngeneric_type_predicate(macro, MALTYPE_MACRO)\n\nMalType mal_equals(list args) {\n  /* Accepts any type of arguments */\n\n  explode2(\"=\", args, first_val, second_val);\n  return make_boolean(equal_forms(first_val, second_val));\n}\n\nMalType mal_nth(list args) {\n\n  explode2(\"nth\", args, lst, n);\n\n  vector_t v;\n  list l;\n  long idx;\n  if (!is_integer(n, &idx)) {\n    bad_type(\"nth\", MALTYPE_INTEGER, n);\n  }\n  if(idx < 0) {\n    make_error(\"'nth': negative index: %d\", idx);\n  }\n  if (is_list(lst, &l)) {\n    while(l) {\n      if(!idx)\n        return l->data;\n      l = l->next;\n      idx--;\n    }\n  }\n  else if ((v = is_vector(lst))) {\n    if ((size_t)idx < v->count) {\n      return v->nth[idx];\n    }\n  } else {\n    bad_type(\"nth\", MALTYPE_LIST | MALTYPE_VECTOR, lst);\n  }\n  make_error(\"'nth': index %M out of bounds for: %M\", n, lst);\n}\n\nMalType mal_first(list args) {\n\n  explode1(\"first\", args, lst);\n\n  list result;\n  vector_t v;\n  if(is_nil(lst)) {\n    return make_nil();\n  }\n  else if ((v = is_vector(lst))) {\n    return v->count ? v->nth[0] : make_nil();\n  }\n  else if (!is_list(lst, &result)) {\n    bad_type(\"first\", MALTYPE_LIST | MALTYPE_VECTOR | MALTYPE_NIL, lst);\n  }\n\n  if (result) {\n    return result->data;\n  }\n  else {\n    return make_nil();\n  }\n}\n\nMalType mal_rest(list args) {\n\n  explode1(\"rest\", args, lst);\n\n  list result = NULL;\n  vector_t v;\n  if(is_nil(lst)) {\n    return make_list(NULL);\n  }\n  else if ((v = is_vector(lst))) {\n    for (size_t i = v->count; 1 < i--; ) {\n      result = list_push(result, v->nth[i]);\n    }\n    return make_list(result);\n  }\n  else if (!is_list(lst, &result)) {\n    bad_type(\"rest\", MALTYPE_LIST | MALTYPE_VECTOR | MALTYPE_NIL, lst);\n  }\n\n  if (result) {\n    result = result->next;\n  }\n  return make_list(result);\n}\n\n\nMalType mal_cons(list args) {\n\n  explode2(\"cons\", args, a1, lst);\n\n  list result = NULL;\n  vector_t v;\n  if ((v = is_vector(lst))) {\n    for (size_t i = v->count; i--; ) {\n      result = list_push(result, v->nth[i]);\n    }\n  }\n  else if (is_list(lst, &result)) {\n  }\n  else if (is_nil(lst)) {\n  }\n  else {\n    bad_type(\"cons\", MALTYPE_LIST | MALTYPE_VECTOR | MALTYPE_NIL, lst);\n  }\n  return make_list(list_push(result, a1));\n}\n\nMalType mal_concat(list args) {\n\n  //  Could reuse the last if it is not nil...\n\n  list new_list = NULL;\n  list* new_list_last = &new_list;\n  while (args) {\n\n    MalType val = args->data;\n\n    /* skip nils */\n    if (is_nil(val)) {\n    }\n    /* concatenate lists and vectors */\n    else if (type(val) & (MALTYPE_LIST | MALTYPE_VECTOR)) {\n      for (seq_cursor lst = seq_iter(val); seq_cont(val, lst); lst = seq_next(val, lst)) {\n        *new_list_last = list_push(NULL, seq_item(val, lst));\n        new_list_last = &(*new_list_last)->next;\n      }\n    }\n    /* raise an error for any non-sequence types */\n    else {\n      bad_type(\"concat\", MALTYPE_NIL | MALTYPE_LIST | MALTYPE_VECTOR, val);\n    }\n    args = args->next;\n  }\n  return make_list(new_list);\n}\n\nMalType mal_count(list args) {\n\n  explode1(\"count\", args, val);\n\n  vector_t v;\n  list mal_list;\n  if(is_nil(val)) {\n    return make_integer(0);\n  }\n  else if ((v = is_vector(val))) {\n    return make_integer(v->count);\n  }\n  else if (!is_list(val, &mal_list)) {\n    bad_type(\"count\", MALTYPE_LIST | MALTYPE_NIL | MALTYPE_VECTOR, val);\n  }\n  return make_integer((long)list_count(mal_list));\n}\n\nMalType mal_empty_questionmark(list args) {\n\n  explode1(\"empty?\", args, val);\n\n  vector_t v;\n  list l;\n  if ((v = is_vector(val))) {\n    return make_boolean(!v->count);\n  }\n  else if (is_list(val, &l)) {\n    return make_boolean(!l);\n  }\n  else {\n    bad_type(\"empty?\", MALTYPE_LIST | MALTYPE_VECTOR, val);\n  }\n}\n\nMalType mal_pr_str(list args) {\n  /* Accepts any number and type of arguments */\n  return make_string(mal_printf(\"%N\", args));\n}\n\nMalType mal_str(list args) {\n  /* Accepts any number and type of arguments */\n  return make_string(mal_printf(\"%# N\", args));\n}\n\nMalType mal_prn(list args) {\n  /* Accepts any number and type of arguments */\n  printf(\"%N\\n\", args);\n  return make_nil();\n}\n\nMalType mal_println(list args) {\n  /* Accepts any number and type of arguments */\n  printf(\"%#N\\n\", args);\n  return make_nil();\n}\n\nMalType mal_read_string(list args) {\n\n  explode1(\"read-string\", args, val);\n\n  const char* s = is_string(val);\n  if (!s) {\n    bad_type(\"read-string\", MALTYPE_STRING, val);\n  }\n  return read_str(s);\n  // Implicit error propagation\n}\n\nMalType mal_slurp(list args) {\n\n  explode1(\"slurp\", args, a1);\n\n  const char* filename = is_string(a1);\n  if (!filename) {\n    bad_type(\"slurp\", MALTYPE_STRING, a1);\n  }\n\n  FILE* file = fopen(filename, \"rb\");\n\n  if (!file){\n    make_error(\"'slurp': file not found '%s'\", filename);\n  }\n\n  fseek(file, 0, SEEK_END);\n  size_t file_length = ftell(file);\n  fseek(file, 0, SEEK_SET);\n\n  char* buffer = (char*)GC_MALLOC(sizeof(*buffer) * file_length + 1);\n  size_t read = fread(buffer, sizeof(*buffer), file_length, file);\n  // close before raising an exception\n  fclose(file);\n  if (file_length != read) {\n    make_error(\"'slurp': failed to read file '%s'\", filename);\n  }\n\n  buffer[file_length] = '\\0';\n  return make_string(buffer);\n}\n\nMalType mal_atom(list args) {\n  explode1(\"atom\", args, val);\n  return make_atom(val);\n}\nMalType mal_deref(list args) {\n  explode1(\"deref\", args, val);\n  MalType* atm = is_atom(val);\n  if (!atm) {\n    bad_type(\"deref\", MALTYPE_ATOM, val);\n  }\n  return *atm;\n}\nMalType mal_reset_bang(list args) {\n  explode2(\"reset!\", args, a1, a2);\n  MalType* atm = is_atom(a1);\n  if (!atm) {\n    bad_type(\"reset!\", MALTYPE_ATOM, a1);\n  }\n  *atm = a2;\n  return a2;\n}\nMalType mal_swap_bang(list args) {\n  if (!args || !args->next) {\n    bad_arg_count(\"swap!\", \"at least two arguments\", args);\n  }\n  MalType* atm = is_atom(args->data);\n  if (!atm) {\n    bad_type(\"swap!\", MALTYPE_ATOM, args->data);\n  }\n  MalType fn = args->next->data;\n  check_type(\"swap!\", MALTYPE_CLOSURE | MALTYPE_FUNCTION | MALTYPE_MACRO, fn);\n  list fn_args = list_push(args->next->next, *atm);\n  MalType result = apply(fn, fn_args);\n\n  if (mal_error) {\n    return NULL;\n  }\n  else {\n    *atm = result;\n    return result;\n  }\n}\n\nMalType mal_throw(list args) {\n  explode1(\"throw\", args, a1);\n\n  /* re-throw an existing exception */\n  assert(!mal_error);\n  /* create a new exception */\n  mal_error = a1;\n  return NULL;\n}\n\nMalType mal_apply(list args) {\n\n  if (!args || !args->next) {\n    bad_arg_count(\"apply\", \"at least two arguments\", args);\n  }\n  MalType func = args->data;\n  check_type(\"apply\", MALTYPE_CLOSURE | MALTYPE_FUNCTION | MALTYPE_MACRO, func);\n  args = args->next;\n\n  /* assemble loose arguments */\n  list lst = NULL;\n  list* lst_last = &lst;\n  while(args->next) {\n    *lst_last = list_push(NULL, args->data);\n    lst_last = &(*lst_last)->next;\n    args = args->next;\n  }\n\n  MalType final = args->data;\n\n  vector_t v = is_vector(final);\n  //  Append the elements of the final sequence,\n  //  efficiently if it is a list.\n  if (v) {\n    for (size_t i = v->count; i--; ) {\n      *lst_last = list_push(*lst_last, v->nth[i]);\n    }\n  }\n  else if (!is_list(final, lst_last)) {\n    bad_type(\"swap!\", MALTYPE_LIST | MALTYPE_VECTOR, final);\n  }\n\n  return apply(func, lst);\n  // Implicit error propagation\n}\n\nMalType mal_map(list args) {\n\n  explode2(\"map\", args, func, arg);\n\n  check_type(\"map\", MALTYPE_CLOSURE | MALTYPE_FUNCTION | MALTYPE_MACRO, func);\n    //  This check is not redundant when arg is empty.\n\n  check_type(\"map\", MALTYPE_LIST | MALTYPE_VECTOR, arg);\n  seq_cursor arg_list = seq_iter(arg);\n  list result_list = NULL;\n  list* result_list_last = &result_list;\n\n  while(seq_cont(arg, arg_list)) {\n\n    MalType result = apply(func, list_push(NULL, seq_item(arg, arg_list)));\n\n    /* early return if error */\n    if (mal_error) {\n      return NULL;\n    }\n    else {\n      *result_list_last = list_push(NULL, result);\n      result_list_last = &(*result_list_last)->next;\n\n    }\n    arg_list = seq_next(arg, arg_list);\n  }\n  return make_list(result_list);\n}\n\nMalType mal_symbol(list args) {\n  explode1(\"symbol\", args, val);\n\n  const char* s = is_string(val);\n  if (!s) {\n    bad_type(\"symbol\", MALTYPE_STRING, val);\n  }\n  return make_symbol(s);\n}\n\nMalType mal_keyword(list args) {\n\n  explode1(\"keyword\", args, val);\n\n  const char* s;\n  if ((s = is_string (val))) {\n    return make_keyword(s);\n  }\n  else if ((s = is_keyword(val))) {\n    return val;\n  }\n  else {\n    bad_type(\"keyword\", MALTYPE_KEYWORD | MALTYPE_STRING, val);\n  }\n}\n\nMalType mal_vector(list args) {\n  /* Accepts any number and type of arguments */\n  size_t capacity = list_count(args);\n  struct vector* v = vector_new(capacity);\n  while (args) {\n    vector_append(&capacity, &v, args->data);\n    args = args->next;\n  }\n  assert(v->count == capacity);\n  return make_vector(v);\n}\n\nMalType mal_vec(list args) {\n\n  /* Accepts a single argument */\n\n  explode1(\"vec\", args, val);\n\n  list l;\n  vector_t v;\n  if ((v = is_vector(val))) {\n    return val;\n  }\n  else if (is_list ( val, &l)) {\n    return mal_vector(l);\n  }\n  else {\n    bad_type(\"vec\", MALTYPE_LIST | MALTYPE_VECTOR, val);\n  }\n}\n\nMalType map_assoc_mutate(const char* context, struct map* new_lst, list args) {\n  for (list a = args; a ; a = a->next->next) {\n    check_type(context, MALTYPE_KEYWORD | MALTYPE_STRING, a->data);\n    if (!a->next) {\n      bad_arg_count(\"assoc\", \"an even count of key/value pairs\", args);\n    }\n    new_lst = hashmap_put(new_lst, a->data, a->next->data);\n  }\n  return make_hashmap(new_lst);\n}\n\nMalType mal_hash_map(list args) {\n  return map_assoc_mutate(\"hash-map\", map_empty(), args);\n}\n\nMalType mal_get(list args) {\n\n  explode2(\"get\", args, map, key);\n\n  check_type(\"get\", MALTYPE_KEYWORD | MALTYPE_STRING, key);\n\n  hashmap mal_list;\n  if(is_nil(map)) {\n    return make_nil();\n  }\n  else if(!(mal_list = is_hashmap(map))) {\n    bad_type(\"get\", MALTYPE_HASHMAP | MALTYPE_NIL, map);\n  }\n\n  MalType result = hashmap_get(mal_list, key);\n\n  if (!result) {\n    return make_nil();\n  }\n\n  return result;\n}\n\nMalType mal_contains_questionmark(list args) {\n\n  explode2(\"contains?\", args, map, key);\n\n  check_type(\"contains?\", MALTYPE_KEYWORD | MALTYPE_STRING, key);\n\n  hashmap mal_list;\n  if(is_nil(map)) {\n    return make_nil();\n  }\n  if (!(mal_list = is_hashmap(map))) {\n    bad_type(\"contains?\", MALTYPE_HASHMAP | MALTYPE_NIL, map);\n  }\n\n  MalType result = hashmap_get(mal_list, key);\n\n  return make_boolean(result);\n}\n\nMalType mal_assoc(list args) {\n  if (!args) {\n    bad_arg_count(\"assoc\", \"at least one argument\", args);\n  }\n  MalType map = args->data;\n  hashmap m = is_hashmap(map);\n  if (!m) {\n    bad_type(\"assoc\", MALTYPE_HASHMAP, map);\n  }\n  return map_assoc_mutate(\"assoc\", map_copy(m), args->next);\n}\n\nMalType mal_dissoc(list args) {\n\n  if (!args) {\n    bad_arg_count(\"dissoc\", \"at least one argument\", args);\n  }\n  MalType map = args->data;\n  hashmap m = is_hashmap(map);\n  if (!m) {\n    bad_type(\"dissoc\", MALTYPE_HASHMAP, map);\n  }\n  struct map* new_list = map_copy(m);\n\n  args = args->next;\n\n    list dis_args = args;\n\n    while(dis_args) {\n\n      check_type(\"dissoc\", MALTYPE_KEYWORD | MALTYPE_STRING, dis_args->data);\n      map_dissoc_mutate(new_list, dis_args->data);\n      dis_args = dis_args->next;\n    }\n\n  return make_hashmap(new_list);\n}\n\n\nMalType mal_keys(list args) {\n\n  explode1(\"keys\", args, map);\n\n  hashmap m = is_hashmap(map);\n  if (!m) {\n    bad_type(\"keys\", MALTYPE_HASHMAP, map);\n  }\n  map_cursor lst = map_iter(m);\n\n  list result = NULL;\n  while(map_cont(m, lst)) {\n\n      result = list_push(result, map_key(m, lst));\n      lst = map_next(m, lst);\n  }\n  return make_list(result);\n}\n\nMalType mal_vals(list args) {\n\n  explode1(\"vals\", args, map);\n\n  hashmap m = is_hashmap(map);\n  if (!m) {\n    bad_type(\"vals\", MALTYPE_HASHMAP, map);\n  }\n  map_cursor lst = map_iter(m);\n\n  list result = NULL;\n  while(map_cont(m, lst)) {\n\n    result = list_push(result, map_val(m, lst));\n    lst = map_next(m, lst);\n  }\n  return make_list(result);\n}\n\nMalType mal_time_ms(list args) {\n  explode0(\"time-ms\", args);\n\n  struct timeval tv;\n  gettimeofday(&tv, NULL);\n  long ms = tv.tv_sec * 1000 + tv.tv_usec/1000.0 + 0.5;\n\n  return make_float(ms);\n}\n\n\nMalType mal_conj(list args) {\n\n  if (!args) {\n    bad_arg_count(\"conj\", \"at least one argument\", args);\n  }\n  MalType lst = args->data;\n\n  list rest = args->next;\n\n  vector_t src;\n  list new_lst;\n  if (is_list(lst, &new_lst)) {\n\n    while(rest) {\n      new_lst = list_push(new_lst, rest->data);\n      rest = rest->next;\n    }\n    return make_list(new_lst);\n  }\n  else if ((src = is_vector(lst))) {\n\n    size_t capacity = src->count + list_count(rest);\n    struct vector* new_vec = vector_new(capacity);\n\n    for (size_t i = 0; i < src->count; i++) {\n      vector_append(&capacity, &new_vec, src->nth[i]);\n    }\n\n    while(rest) {\n      vector_append(&capacity, &new_vec, rest->data);\n      rest = rest->next;\n    }\n    assert(new_vec->count == capacity);\n    return make_vector(new_vec);\n  }\n  else {\n    bad_type(\"conj\", MALTYPE_LIST | MALTYPE_VECTOR, lst);\n  }\n}\n\nMalType mal_seq(list args) {\n\n  explode1(\"seq\", args, val);\n\n  vector_t v;\n  list lst = NULL;\n  const char* ch;\n\n  if (is_list(val, &lst)) {\n    return lst ? val : make_nil();\n  }\n  else if ((ch = is_string(val))) {\n\n    /* empty string */\n    if (*ch == '\\0') {\n      return make_nil();\n    }\n    else {\n      for (size_t i = strlen(ch); i--; ) {\n        char* new_ch = GC_MALLOC(2);\n        *new_ch = ch[i];\n        assert(!new_ch[1]);\n\n        lst = list_push(lst, make_string(new_ch));\n      }\n      return make_list(lst);\n    }\n  }\n  else if ((v = is_vector(val))) {\n    for (size_t i = v->count; i--; ) {\n      lst = list_push(lst, v->nth[i]);\n    }\n    return lst ? make_list(lst) : make_nil();\n  }\n  else if (is_nil(val)) {\n    return make_nil();\n  }\n  else {\n    bad_type(\"seq\", MALTYPE_LIST | MALTYPE_VECTOR | MALTYPE_NIL | MALTYPE_STRING, val);\n  }\n}\n\nMalType mal_meta(list args) {\n\n  explode1(\"meta\", args, val);\n\n  return meta(val);\n}\n\nMalType mal_with_meta(list args) {\n\n  explode2(\"with-meta\", args, val, metadata);\n\n  list l;\n  if (is_list(val, &l)) return make_list_m(l, metadata);\n  vector_t v = is_vector(val);\n  if (v) return make_vector_m(v, metadata);\n  hashmap m = is_hashmap(val);\n  if (m) return make_hashmap_m(m, metadata);\n  function_t f = is_function(val);\n  if (f) return make_function_m(f, metadata);\n  MalClosure c = is_closure(val);\n  if (c) return make_closure_m(c->env, c->fnstar_args, metadata);\n  bad_type(\"with-meta\",\n           MALTYPE_LIST | MALTYPE_VECTOR | MALTYPE_HASHMAP | MALTYPE_FUNCTION | MALTYPE_CLOSURE,\n           val);\n}\n\nMalType mal_readline(list args) {\n  explode1(\"readline\", args, prompt);\n\n  const char* prompt_str = is_string(prompt);\n  if (!prompt_str) {\n    bad_type(\"readline\", MALTYPE_STRING, prompt);\n  }\n  const char* str = readline_gc(prompt_str);\n  if(!str)\n    return make_nil();\n  return make_string(str);\n}\n\n\n/* helper functions */\n\ninline MalType make_boolean(bool x) {\n  return x ? make_true() : make_false();\n}\n\n\n#ifdef WITH_FFI\nstruct {\n  const char*     c_type;\n  enum mal_type_t mal_type;\n  ffi_type*       ffit;\n} core_ffi_translations[] = {\n  { \"void\",    MALTYPE_NIL,     &ffi_type_void },\n  { \"string\",  MALTYPE_STRING,  &ffi_type_pointer },\n  { \"char*\",   MALTYPE_STRING,  &ffi_type_pointer },\n  { \"char *\",  MALTYPE_STRING,  &ffi_type_pointer },\n  { \"integer\", MALTYPE_INTEGER, &ffi_type_sint64 },\n  { \"int64\",   MALTYPE_INTEGER, &ffi_type_sint64 },\n  { \"int32\",   MALTYPE_INTEGER, &ffi_type_sint32 },\n  { \"double\",  MALTYPE_FLOAT,   &ffi_type_double },\n  { \"float\",   MALTYPE_FLOAT,   &ffi_type_float },\n};\nsize_t core_ffi_find(const char *type) {\n  for (size_t i = 0;\n       i < sizeof(core_ffi_translations) / sizeof(*core_ffi_translations);\n       i++) {\n    if (!strcmp(core_ffi_translations[i].c_type, type)) {\n      return i;\n    }\n  }\n  make_error(\"'ffi': unknown type '%s'\", type);\n}\nMalType mal_dot(list args) {\n\n  /* (. \"lib\" \"return type\" \"function\" \"arg1 type\" \"arg 1\" ...) */\n\n  list a;\n  if (!args || !(a = args->next) || !a->next) {\n    bad_arg_count(\".\", \"at least three arguments\", args);\n  }\n\n  const char* lib_name = is_string(args->data);\n  if (!lib_name && !is_nil(args->data)) {\n    bad_type(\".\", MALTYPE_STRING | MALTYPE_NIL, args->data);\n  }\n\n  const char* return_type_str = is_string(a->data);\n  if (!return_type_str) {\n    bad_type(\".\", MALTYPE_STRING, a->data);\n  }\n  size_t return_type = core_ffi_find(return_type_str);\n  if (mal_error) return NULL;\n\n  a = a->next;\n\n  const char* fn_name = is_string(a->data);\n  if (!fn_name) {\n    bad_type(\".\", MALTYPE_STRING, a->data);\n  }\n\n  a = a->next;\n\n  int       arg_count = 0;\n  ffi_type* arg_types[20];\n  void*     arg_vals [20];\n  while (a) {\n    if (20 <= arg_count) {\n      bad_arg_count(\".\", \"less than 20 C arguments\", args);\n    }\n    const char* val_type = is_string(a->data);\n    if (!val_type) {\n      bad_type(\".\", MALTYPE_STRING, a->data);\n    }\n    size_t val_type_index = core_ffi_find(val_type);\n    if (mal_error) return NULL;\n    arg_types[arg_count] = core_ffi_translations[val_type_index].ffit;\n\n    a = a->next;\n\n    if (!a) {\n      bad_arg_count(\".\", \"an even number of argument types and values\", args);\n    }\n    arg_vals[arg_count] = mal_type_value_address(a->data);\n\n    a = a->next;\n    arg_count++;\n  }\n\n  /* open a shared library dynamically and get hold of a function */\n  void* lib_handle = dlopen(lib_name, RTLD_LAZY);\n\n  if (!lib_handle) {\n    make_error(\"'ffi': reports: %s\", dlerror());\n  }\n\n  void* fn = dlsym(lib_handle, fn_name);\n\n  const char* error = dlerror();\n  if (error) {\n    make_error(\"'ffi': dlsym could not get handle to function '%s': %s\", fn_name, error);\n  }\n\n  /* use libffi to call function */\n  /* perform the call */\n  ffi_cif cif;\n  ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_count,\n                                   core_ffi_translations[return_type].ffit,\n                                   arg_types);\n  if (status != FFI_OK) {\n    make_error(\"'ffi': call to ffi_prep_cif failed with code: %d\", status);\n  }\n\n  /* set return type */\n  MalType result;\n  switch (core_ffi_translations[return_type].mal_type) {\n  case MALTYPE_NIL: {\n    char retval;\n    ffi_call(&cif, FFI_FN(fn), &retval, arg_vals);\n    result = make_nil();\n    break;\n  }\n  case MALTYPE_STRING: {\n    char* retval;\n    ffi_call(&cif, FFI_FN(fn), &retval, arg_vals);\n    result = make_string(retval);\n    break;\n  }\n  case MALTYPE_INTEGER: {\n    long retval;\n    ffi_call(&cif, FFI_FN(fn), &retval, arg_vals);\n    result = make_integer(retval);\n    break;\n  }\n  case MALTYPE_FLOAT: {\n    double retval;\n    ffi_call(&cif, FFI_FN(fn), &retval, arg_vals);\n    result = make_float(retval);\n    break;\n  }\n  default:\n    assert(false);\n  }\n\n  /* close the library */\n  dlclose(lib_handle);\n\n  return result;\n}\n\n#endif\n"
  },
  {
    "path": "impls/c.2/core.h",
    "content": "#ifndef _MAL_CORE_H\n#define _MAL_CORE_H\n\n#include \"types.h\"\n\ntypedef const struct ns_s* ns;\n\nstruct ns_s {\n\n  const char* key;\n  function_t value;\n\n};\n\nvoid ns_make_core(ns* core, size_t* size);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/env.c",
    "content": "#include <gc.h>\n\n#include \"env.h\"\n#include \"hashmap.h\"\n\nstruct Env_s {\n  const Env*     outer;\n  struct map*    data;\n};\n\nEnv* env_make(const Env* outer) {\n  struct Env_s* env = GC_MALLOC(sizeof(*env));\n  env->outer = outer;\n  env->data = map_empty();\n  return env;\n}\n\ninline void env_set(Env* current, MalType symbol, MalType value) {\n  current->data = hashmap_put(current->data, symbol, value);\n}\n\nMalType env_get(const Env* current, MalType symbol) {\n  do {\n    MalType value = hashmap_get(current->data, symbol);\n    if (value) {\n      return value;\n    }\n  } while((current = current->outer));\n  return NULL;\n}\n\nhashmap env_as_map(const Env* current) {\n  return current->data;\n}\n"
  },
  {
    "path": "impls/c.2/env.h",
    "content": "#ifndef _MAL_ENV_H\n#define _MAL_ENV_H\n\n#include \"types.h\"\n\n//  types.h defines Env as struct Env_s.\n\nEnv* env_make(const Env* outer);\n\nvoid env_set(Env* current, MalType symbol, MalType value);\n\nMalType env_get(const Env* current, MalType symbol);\n/* Returns NULL if the symbol is not found. */\n\nhashmap env_as_map(const Env* current);\n// For debugging.\n\n#endif\n"
  },
  {
    "path": "impls/c.2/error.c",
    "content": "#include <stddef.h>\n\n#include \"error.h\"\n\nMalType mal_error = NULL;\n"
  },
  {
    "path": "impls/c.2/error.h",
    "content": "#ifndef MAL_ERROR_H\n#define MAL_ERROR_H\n\n#include \"types.h\"\n\nextern MalType mal_error;\n\n#define make_error(...) {                             \\\n    mal_error = make_string(mal_printf(__VA_ARGS__)); \\\n    return 0;                                         \\\n  }\n\n#define bad_type(context, mask, form)                     \\\n  make_error(\"'%s': bad argument type: expected %T, got %M\", context, mask, form)\n\n#define check_type(context, mask, form) \\\n  if (type(form) & ~(mask))             \\\n    bad_type(context, mask, form)\n\n#define bad_arg_count(context, expected, args)                 \\\n  make_error(\"'\" context \"': bad argument count: expected %s, got [%N]\", expected, args)\n\n#define explode0(context, args)                 \\\n  if (args)                                     \\\n    bad_arg_count(context, \"no argument\", args)\n\n#define explode1(context, args, var1)             \\\n  if (!args || args->next)                        \\\n    bad_arg_count(context, \"one argument\", args); \\\n  MalType var1 = args->data\n\n#define explode2(context, args, var1, var2)        \\\n  list _a;                                         \\\n  if (!args || !(_a = args->next) || _a->next)     \\\n    bad_arg_count(context, \"two arguments\", args); \\\n  MalType var1 = args->data;                       \\\n  MalType var2 = _a->data\n\n#define explode3(context, args, var1, var2, var3)                  \\\n  list _a, _b;                                                     \\\n  if (!args || !(_a = args->next) || !(_b = _a->next) || _b->next) \\\n    bad_arg_count(context, \"three arguments\", args);               \\\n  MalType var1 = args->data;                                       \\\n  MalType var2 = _a->data;                                         \\\n  MalType var3 = _b->data\n\n#endif\n"
  },
  {
    "path": "impls/c.2/hashmap.c",
    "content": "#include <assert.h>\n#include <string.h>\n\n#include <gc.h>\n\n#include \"hashmap.h\"\n\n#ifdef DEBUG_HASHMAP\n#  include <stdio.h>\n#  include \"printer.h\"\n#endif\n#ifdef DEBUG_HASH_COLLISIONS\n#  include <stdio.h>\n#  include \"printer.h\"\n#endif\n\n// Removals or redefinitions are rare.\n// Most structures are quite small, except two ones.\n//   the REPL environment and\n//   the map representing the hosted REPL environment.\n// Most maps are built once, then constant.\n// MAL spends a lot of its time searching DEBUG-EVAL in environments.\n\n// Either a map has less than 3 keys, or be generous.\n// After changing this, try \"make debug_hash_collisions=1\",\n// because a collison for DEBUG-EVAL in REPL is costly.\n#define MIN_BUCKETS 7\n#define GROW_FACTOR 25\n\nstruct map {\n  // Invariants:\n  //   table contains size buckets.\n  //   0 <= 2*used < size\n  // A bucket may have three states:\n  //   unused        (both key and value == NULL)\n  //   used normally (both key and value != NULL)\n  //   used deleted  (key != NULL but value == NULL)\n  // In case of collision, search after the intended one.\n  size_t used;\n  size_t size;\n  struct bucket {\n    MalType key;\n    void*   value;\n  } buckets[];\n};\n\nstruct map* map_empty() {\n  struct map* m = GC_MALLOC(sizeof(*m) + MIN_BUCKETS*sizeof(struct bucket));\n  // GC_MALLOC sets all the allocated space to zero.\n  m->size = MIN_BUCKETS;\n  return m;\n}\n\nstruct map* map_copy(hashmap map) {\n  size_t bytes = sizeof(*map) + map->size * sizeof(struct bucket);\n  struct map* m = GC_MALLOC(bytes);\n  memcpy(m, map, bytes);\n  return m;\n}\n\nsize_t search(hashmap map, MalType key) {\n  // The key of the returned index is either NULL or equal to key.\n\n  size_t index = get_hash(key) % map->size;\n  while (true) {\n    MalType current = map->buckets[index].key;\n    if (!current || equal_forms(key, current)) break;\n#ifdef DEBUG_HASH_COLLISIONS\n    printf(\"collision   %M(h:%u i:%u)    %M(h:%u i:%u)\\n\",\n           key,     get_hash(key),     get_hash(key)     % map->size,\n           current, get_hash(current), get_hash(current) % map->size);\n#endif\n    index++;\n    if (index == map->size) index = 0;\n  }\n#ifdef DEBUG_HASH_COLLISIONS\n  if (index != get_hash(key) % map->size) {\n    printf(\"collision (%.1f%% of %u) key %M stored in bucket %d instead of %d\\n\",\n           (float)(100*map->used) / (float)(map->size), map->size,\n           key, index, get_hash(key) % map->size);\n  }\n#endif\n#ifdef DEBUG_HASHMAP\n  printf(\"HASHMAP: search:%M  hash:%u  index:%u\\n\", key, get_hash(key), index);\n  for (size_t i = 0; i < map->size; i++) {\n    if (map->buckets[i].key) {\n      if (map->buckets[i].value) {\n        printf(\"  bucket:%u/%u  key:%M  val:%M\\n\",\n               i, map->size, map->buckets[i].key, map->buckets[i].value);\n      }\n      else {\n        printf(\"  bucket:%u/%u  key:%M  (removed)\\n\",\n               i, map->size, map->buckets[i].key);\n      }\n    }\n    else {\n      assert(!map->buckets[i].value);\n    }\n    printf(\"\");\n  }\n#endif\n  return index;\n}\n\nvoid put(struct map* map, MalType key, void* value) {\n  size_t i = search(map, key);\n  if (!map->buckets[i].key) {\n    map->used++;\n    map->buckets[i].key = key;\n  }\n  // else replace the existing/deleted value\n  map->buckets[i].value = value;\n}\n\nstruct map* hashmap_put(struct map* map, MalType key, void* value) {\n  assert(value);\n  if (map->size <= 2 * (map->used + 1)) {\n    // Reallocate.\n    size_t size = map->size * GROW_FACTOR;\n    struct map* m = GC_MALLOC(sizeof(*m) + size*sizeof(struct bucket));\n    // GC_MALLOC sets all the allocated space to zero.\n    m->size = size;\n    for (size_t i = 0; i < map->size; i++) {\n      if (map->buckets[i].key && map->buckets[i].value) {\n        put(m, map->buckets[i].key, map->buckets[i].value);\n      }\n    }\n    map = m;\n  }\n  put(map, key, value);\n  return map;\n}\n\ninline void* hashmap_get(hashmap map, MalType key) {\n  return map->buckets[search(map, key)].value; // may be null\n}\n\nvoid map_dissoc_mutate(struct map* map, MalType key) {\n  size_t i = search(map, key);\n  if (map->buckets[i].key) {\n    map->buckets[i].value = NULL;\n  }\n}\n\ninline size_t map_count(hashmap map) {\n  return map->used;\n}\n\nmap_cursor next_valid(hashmap map, size_t i) {\n  while ((i < map->size) && !(map->buckets[i].key && map->buckets[i].value)) {\n    i++;\n  }\n  return i;\n}\n\ninline map_cursor map_iter(hashmap map) {\n  return next_valid(map, 0);\n}\n\ninline bool map_cont(hashmap map, map_cursor position) {\n  return position < map->size;\n}\n\ninline MalType map_key(hashmap map, map_cursor position) {\n  assert(position < map->size);\n  assert(map->buckets[position].key);\n  assert(map->buckets[position].value);\n\n  return map->buckets[position].key;\n}\n\ninline void* map_val(hashmap map, map_cursor position) {\n  assert(position < map->size);\n  assert(map->buckets[position].key);\n  assert(map->buckets[position].value);\n\n  return map->buckets[position].value;\n}\n\ninline map_cursor map_next(hashmap map, map_cursor position) {\n  assert(position < map->size);\n  assert(map->buckets[position].key);\n  assert(map->buckets[position].value);\n\n  return next_valid(map, position + 1);\n}\n"
  },
  {
    "path": "impls/c.2/hashmap.h",
    "content": "#ifndef MAL_HASHMAP_H\n#define MAL_HASHMAP_H\n\n#include <stdbool.h>\n\n#include \"types.h\"\n\n// Keys must be keywords, strings or symbols.\n\nstruct map* map_empty();\n// not NULL\n\nstruct map* map_copy(hashmap);\n\nstruct map* hashmap_put(struct map* map, MalType key, void* value);\n// Value must not be NULL.\n// May reallocate.\n\nvoid* hashmap_get(hashmap map, MalType key);\n// Returns NULL if the map does not contain the key.\n\nvoid map_dissoc_mutate(struct map* map, MalType key);\n\nsize_t map_count(hashmap);\n\ntypedef size_t map_cursor;\n// The same (unmodified) container must be be provided to each\n// function during iteration.\n\nmap_cursor map_iter(hashmap);\nbool map_cont(hashmap, map_cursor);\nmap_cursor map_next(hashmap, map_cursor);\nMalType map_key(hashmap, map_cursor);\nvoid* map_val(hashmap, map_cursor);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/linked_list.c",
    "content": "#include <gc.h>\n\n#include \"linked_list.h\"\n\nlist list_push(list lst, MalType data_ptr) {\n\n  struct pair_s* new_head = GC_malloc(sizeof(*new_head));\n  new_head->data = data_ptr;\n  new_head->next = lst;\n\n  return new_head;\n}\n\nsize_t list_count(list lst) {\n\n  size_t counter = 0;\n\n  while(lst) {\n\n    counter++;\n    lst = lst->next;\n  }\n\n  return counter;\n}\n"
  },
  {
    "path": "impls/c.2/linked_list.h",
    "content": "#ifndef _MAL_LINKED_LIST_H\n#define _MAL_LINKED_LIST_H\n\n#include \"types.h\"\n\n/* linked list is constructed of pairs */\n/* a list is just a pointer to the pair at the head of the list */\nstruct pair_s {\n\n  MalType data;\n  list next;\n\n};\n\n/* interface */\nlist list_push(list lst, MalType data_ptr);\nsize_t list_count(list lst);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/printer.c",
    "content": "#include <assert.h>\n#include <printf.h>\n#include <stdio.h>\n\n#include <gc.h>\n\n#include \"linked_list.h\"\n#include \"printer.h\"\n#include \"hashmap.h\"\n#include \"vector.h\"\n\n#define PRINT_NIL \"nil\"\n#define PRINT_TRUE \"true\"\n#define PRINT_FALSE \"false\"\n\nint escape_string(FILE *stream, const char* str);\nint pr_str_vector(FILE* stream, const struct printf_info *i, vector_t v);\n\n// Execute count once.\n#define ADD(count) {  \\\n    int more = count; \\\n    if(more < 0)      \\\n      return more;    \\\n    written += more;  \\\n  }\n\nint print_M(FILE *stream, const struct printf_info *i, const void *const *a) {\n  MalType val = *((const MalType*)(*a));\n\n  int written = 0;\n\n  switch(type(val)) {\n\n  case MALTYPE_SYMBOL:\n\n    ADD(fprintf(stream, \"%s\", is_symbol(val)));\n    break;\n\n  case MALTYPE_KEYWORD:\n\n    ADD(fprintf(stream, \":%s\", is_keyword(val)));\n    break;\n\n  case MALTYPE_INTEGER:\n    {\n      long mal_integer;\n      is_integer(val, &mal_integer);\n      ADD(fprintf(stream, \"%ld\", mal_integer));\n      break;\n    }\n  case MALTYPE_FLOAT:\n    {\n      double mal_float;\n      is_float(val, &mal_float);\n      ADD(fprintf(stream, \"%lf\", mal_float));\n      break;\n    }\n  case MALTYPE_STRING:\n\n    if (!i->alt) {\n      ADD(escape_string(stream, is_string(val)));\n    }\n    else {\n      ADD(fprintf(stream, \"%s\", is_string(val)));\n    }\n    break;\n\n  case MALTYPE_TRUE:\n\n    ADD(fprintf(stream, PRINT_TRUE));\n    break;\n\n  case MALTYPE_FALSE:\n\n    ADD(fprintf(stream, PRINT_FALSE));\n    break;\n\n  case MALTYPE_NIL:\n\n    ADD(fprintf(stream, PRINT_NIL));\n    break;\n\n  case MALTYPE_LIST:\n    {\n      list mal_list;\n      is_list(val, &mal_list);\n      ADD(fprintf(stream, i->alt ? \"(%#N)\" : \"(%N)\", mal_list));\n      break;\n    }\n  case MALTYPE_VECTOR:\n\n    ADD(pr_str_vector(stream, i, is_vector(val)));\n    break;\n\n  case MALTYPE_HASHMAP:\n\n    ADD(fprintf(stream, i->alt ? \"{%#H}\" : \"{%H}\", is_hashmap(val)));\n    break;\n\n  case MALTYPE_FUNCTION:\n\n    ADD(fprintf(stream, \"#<core>\"));\n    break;\n\n  case MALTYPE_CLOSURE:\n\n    ADD(fprintf(stream, i->alt ? \"#<fn* %#N>\" : \"#<fn* %N>\",\n                is_closure(val)->fnstar_args));\n    break;\n\n  case MALTYPE_MACRO:\n\n    ADD(fprintf(stream, i->alt ? \"#<macro %#N>\" : \"#<macro %N>\",\n                is_macro(val)->fnstar_args));\n    break;\n\n  case MALTYPE_ATOM:\n\n    ADD(fprintf(stream, i->alt ? \"(atom %#M)\" : \"(atom %M)\", *is_atom(val)));\n\n  }\n\n  if (written < i->width) {\n    ADD(fprintf(stream, \"%*s\", i->width - written, \"\"));\n  }\n  return written;\n}\n\n\nint print_L(FILE* stream, const struct printf_info *i, const void *const *a) {\n\n  int written = 0;\n  for (list lst = *((const list*)(*a)); lst;  lst = lst->next) {\n    ADD(fprintf(stream, i->alt ? \"%s%#M\" : \"%s%M\",\n                !i->space && written ? \" \" : \"\", lst->data));\n  }\n  return written;\n}\n\nint pr_str_vector(FILE* stream, const struct printf_info *i, vector_t v) {\n  int written = 0;\n  ADD(fprintf(stream, \"[\"));\n  for (size_t j = 0; j < v->count; j++) {\n    ADD(fprintf(stream,\n                i->alt ? \"%s%#M\" : \"%s%M\",\n                j ? \" \" : \"\",\n                v->nth[j]));\n  }\n  ADD(fprintf(stream, \"]\"));\n  return written;\n}\n\nint pr_str_map(FILE* stream, const struct printf_info *i, const void *const *a) {\n  hashmap map = *((const hashmap*)(*a));\n  int written = 0;\n  for (map_cursor c = map_iter(map); map_cont(map, c); c = map_next(map, c)) {\n    ADD(fprintf(stream, i->alt ? \"%s%#M %#M\" : \"%s%M %M\", written ? \" \" : \"\",\n                map_key(map, c), map_val(map, c)));\n  }\n  return written;\n}\n\n\nint escape_string(FILE *stream, const char* str) {\n\n  int written = 0;\n\n  ADD(fprintf(stream, \"\\\"\"));\n\n  const char* curr = str;\n  while(*curr != '\\0') {\n\n    switch (*curr) {\n\n    case 0x0A:\n\n      ADD(fprintf(stream, \"\\\\n\"));\n      break;\n\n    case '\"':\n    case '\\\\':\n      ADD(fprintf(stream, \"\\\\\"));\n      // fall through\n\n    default:\n      ADD(fprintf(stream, \"%c\", *curr));\n    }\n    curr++;\n  }\n  ADD(fprintf(stream, \"\\\"\"));\n  return written;\n}\n\n// The order must match the one in types.c.\nconst char* print_T_table[] = {\n  \"a symbol\",\n  \"a keyword\",\n  \"an integer\",\n  \"a float\",\n  \"a string\",\n  \"true\",\n  \"false\",\n  \"nil\",\n  \"a list\",\n  \"a vector\",\n  \"a map\",\n  \"a function\",\n  \"a closure\",\n  \"an atom\",\n  \"a macro\",\n};\n\nint print_T(FILE *stream, const struct printf_info *, const void *const *a) {\n  enum mal_type_t mask = *((const enum mal_type_t*)(*a));\n  assert(0 < mask);\n  int written = 0;\n  const char** p = print_T_table;\n  while (mask) {\n    assert(p < print_T_table + sizeof(print_T_table) / sizeof(*print_T_table));\n    if (mask & 1) {\n      ADD(fprintf(stream, \"%s%s\", written ? \" or \" : \"\", *p));\n    }\n    p++;\n    mask >>= 1;\n  }\n  return written;\n}\n\n#define generic_arg(specifier, type)                       \\\n  int arg_##specifier(const struct printf_info*,           \\\n                      size_t n,int *argtypes, int *size) { \\\n    if(n < 1) return -1;                                   \\\n    argtypes[0] = PA_POINTER;                              \\\n    *size = sizeof(type);                                  \\\n    return 1;                                              \\\n  }\ngeneric_arg(M, MalType);\ngeneric_arg(N, list);\ngeneric_arg(T, enum mal_type_t);\ngeneric_arg(H, hashmap);\n\nvoid printer_init() {\n\n  int ret1 = register_printf_specifier('N', print_L, arg_N);\n  int ret2 = register_printf_specifier('M', print_M, arg_M);\n  int ret3 = register_printf_specifier('T', print_T, arg_T);\n  int ret4 = register_printf_specifier('H', pr_str_map, arg_H);\n  assert(!ret1);\n  assert(!ret2);\n  assert(!ret3);\n  assert(!ret4);\n#ifdef NDEBUG\n  (void)ret1;\n  (void)ret2;\n  (void)ret3;\n  (void)ret4;\n#endif\n}\n\nconst char* mal_printf(const char* fmt, ...) {\n\n  va_list argptr;\n\n  va_start(argptr, fmt);\n  int n = vsnprintf(NULL, 0, fmt, argptr);\n  assert(0 <= n);\n  va_end(argptr);\n\n  char* buffer = GC_MALLOC(n + 1);\n\n  va_start(argptr, fmt);\n  int again = vsnprintf(buffer, n+1, fmt, argptr);\n  assert(n == again);\n#ifdef NDEBUG\n  (void)again;\n#endif\n  va_end(argptr);\n\n  return buffer;\n}\n"
  },
  {
    "path": "impls/c.2/printer.h",
    "content": "#ifndef _PRINTER_H\n#define _PRINTER_H\n\n// This function must be called during startup.\nvoid printer_init();\n// It adds the following conversion specifiers (requires GNU libc).\n\n// specifier type            modifiers      meaning\n//\n// %M        MalType         #              no string escape\n//                           positive width right padding\n// %N        list            #              no string escape\n//                           ' '            no space separator\n// %T        enum mal_type_t\n// %H        hashmap         #              no string escape\n\n//  Similar to asprintf, except that\n//    the memory is allocated with GC_MALLOC instead of malloc,\n//    errors crash the program instead of being reported.\nconst char* mal_printf(const char* fmt, ...);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/reader.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n\n#include <gc.h>\n\n#include \"hashmap.h\"\n#include \"printer.h\"\n#include \"reader.h\"\n#include \"linked_list.h\"\n#include \"vector.h\"\n#include \"error.h\"\n\n#define SYMBOL_NIL \"nil\"\n#define SYMBOL_TRUE \"true\"\n#define SYMBOL_FALSE \"false\"\n\n#ifdef DEBUG_READER\n#  define DEBUG(fmt, ...) printf(\"READER: %s \\\"%s\\\": \" fmt \"\\n\", __func__, *reader, ## __VA_ARGS__)\n#else\n#  define DEBUG(...)\n#endif\n\ntypedef const char** Reader;\n\nMalType read_form(Reader reader);\nMalType read_with_meta(Reader reader);\nMalType read_string(Reader reader);\nMalType read_number(Reader reader);\nconst char* read_symbol (Reader reader);\nMalType read_list(Reader reader);\nMalType read_vector(Reader reader);\nMalType read_map(Reader reader);\nvoid skip_spaces(Reader reader);\nMalType make_symbol_list(Reader reader, MalType symbol_name);\n\nvoid skip_spaces(Reader reader) {\n\n  while(true) {\n    if(**reader == ';') {\n      do {\n        (*reader)++;\n        if(**reader == 0x00) return;\n      } while(**reader != 0x0A);\n    }\n    else if((**reader != ',') && !isspace(**reader)) {\n      return;\n    }\n    (*reader)++;\n  }\n}\n\nMalType read_str(const char* source) {\n\n  MalType result = read_form(&source);\n  if(mal_error) return NULL;\n  skip_spaces(&source);\n  if(*source)\n    make_error(\"reader: trailing characters (after %M): %s\",\n                          result, source);\n  return result;\n}\n\nconst char* read_symbol (Reader reader) {\n\n  DEBUG();\n\n  const char* start = *reader;\n  while(!isspace(**reader)) {\n    switch(**reader) {\n    case 0:\n      return start;\n    case '[':\n    case '{':\n    case '(':\n    case ']':\n    case '}':\n    case ')':\n    case '\\'':\n    case '@':\n    case '`':\n    case '^':\n    case '~':\n    case '\"':\n    case ',':\n    case ';':\n      goto finished;\n    default:\n      (*reader)++;\n    }\n  }\n finished:\n  size_t len = *reader - start;\n  char* result = GC_MALLOC(len + 1);\n  strncpy(result, start, len);\n  assert(!result[len]);\n  return result;\n}\n\nMalType read_form(Reader reader) {\n\n  DEBUG();\n\n  skip_spaces(reader);\n  switch (**reader) {\n  case 0:\n    make_error(\"reader: input string is empty\");\n  case '[':\n    return read_vector(reader);\n    // Implicit error propagation\n  case '{':\n    return read_map(reader);\n    // Implicit error propagation\n  case '(':\n    return read_list(reader);\n    // Implicit error propagation\n  case ']':\n  case '}':\n  case ')':\n    make_error(\"reader: unmatched '%c'\", **reader);\n  case '\\'':\n    return make_symbol_list(reader, SYMBOL_QUOTE);\n  case '@':\n    return make_symbol_list(reader, SYMBOL_DEREF);\n  case '`':\n    return make_symbol_list(reader, SYMBOL_QUASIQUOTE);\n  case '^':\n    return read_with_meta(reader);\n    // Implicit error propagation\n  case '~':\n    if(*(*reader + 1) == '@') {\n      (*reader)++;\n      return make_symbol_list(reader, SYMBOL_SPLICE_UNQUOTE);\n    }\n    return make_symbol_list(reader, SYMBOL_UNQUOTE);\n  case '\"':\n    return read_string(reader);\n    // Implicit error propagation\n  case '0':\n  case '1':\n  case '2':\n  case '3':\n  case '4':\n  case '5':\n  case '6':\n  case '7':\n  case '8':\n  case '9':\n    return read_number(reader);\n  case '+':\n  case '-':\n    if(isdigit(*(*reader + 1)))\n      return read_number(reader);\n    else\n      return make_symbol(read_symbol(reader));\n  case ':':\n    (*reader)++;\n    return make_keyword(read_symbol(reader));\n  default:\n    {\n      const char* sym = read_symbol(reader);\n      if(!strcmp(sym, SYMBOL_NIL))   return make_nil();\n      if(!strcmp(sym, SYMBOL_FALSE)) return make_false();\n      if(!strcmp(sym, SYMBOL_TRUE))  return make_true();\n      return make_symbol(sym);\n    }\n  }\n}\n\nMalType read_number(Reader reader) {\n\n  DEBUG();\n\n  const char* start = *reader;\n  // Skip the initial character, which is a digit or a +- sign\n  // (followed by a digit).\n  (*reader)++;\n\n  bool has_decimal_point = false;\n\n  while(true) {\n    if(**reader == '.') {\n      if(has_decimal_point) break;\n      has_decimal_point = true;\n      (*reader)++;\n    }\n    else if(isdigit(**reader))\n      (*reader)++;\n    else\n      break;\n  }\n\n  size_t len = *reader - start;\n  char buffer[len + 1];\n  strncpy(buffer, start, len);\n  buffer[len] = 0;\n\n  if(has_decimal_point)\n    return make_float(atof(buffer));\n  else\n    return make_integer(atol(buffer));\n}\n\nMalType read_with_meta(Reader reader) {\n\n          DEBUG();\n\n        /* create and return a MalType list (with-meta <second-form> <first-form>\n           where first form should ne a metadata map and second form is somethingh\n           that can have metadata attached */\n          (*reader)++;\n\n          /* grab the components of the list */\n          MalType symbol = SYMBOL_WITH_META;\n          MalType first_form = read_form(reader);\n          if(mal_error) return NULL;\n          MalType second_form = read_form(reader);\n          if(mal_error) return NULL;\n\n          /* push the symbol and the following forms onto a list */\n          list lst = NULL;\n          lst = list_push(lst, first_form);\n          lst = list_push(lst, second_form);\n          lst = list_push(lst, symbol);\n\n          return make_list(lst);\n }\n\nMalType read_list(Reader reader) {\n\n  (*reader)++;\n  list lst = NULL;\n  list* lst_last = &lst;\n\n    while(true) {\n      DEBUG(\"searching ')', already read: %N\", lst);\n      skip_spaces(reader);\n\n      if(!**reader) {\n        /* unbalanced parentheses */\n        make_error(\"reader: unbalanced '('\");\n      }\n\n      if(**reader == ')')\n        break;\n      MalType val = read_form(reader);\n      if(mal_error) return NULL;\n      *lst_last = list_push(NULL, val);\n      lst_last = &(*lst_last)->next;\n    }\n  (*reader)++;\n  return make_list(lst);\n}\n\nMalType read_vector(Reader reader) {\n  (*reader)++;\n  size_t capacity = 10;\n  struct vector* v = vector_new(capacity);\n  while(true) {\n    DEBUG(\"searching ']'\");\n    skip_spaces(reader);\n    if (!**reader) {\n      make_error(\"reader: unbalanced '['\");\n    }\n    if (**reader == ']')\n      break;\n    MalType val = read_form(reader);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &v, val);\n  }\n  (*reader)++;\n  return make_vector(v);\n}\n\nMalType read_map(Reader reader) {\n  (*reader)++;\n  struct map* map = map_empty();\n  while(true) {\n    DEBUG(\"searching '}' or key\");\n    skip_spaces(reader);\n    if (!**reader) {\n      make_error(\"reader: unbalanced '{'\");\n    }\n    if (**reader == '}')\n      break;\n    MalType key = read_form(reader);\n    if (mal_error) return NULL;\n    check_type(\"reading map literal\", MALTYPE_KEYWORD | MALTYPE_STRING, key);\n    DEBUG(\"searching map value for %M\", key);\n    skip_spaces(reader);\n    if (!**reader) {\n      make_error(\"reader: unbalanced '{'\");\n    }\n    if (**reader == '}') {\n      make_error(\"reader: odd count of bindings in map litteral\");\n    }\n    MalType value = read_form(reader);\n    if (mal_error) return NULL;\n    map = hashmap_put(map, key, value);\n  }\n  (*reader)++;\n  return make_hashmap(map);\n}\n\nMalType make_symbol_list(Reader reader, MalType symbol) {\n\n  DEBUG();\n\n  (*reader)++;\n  list lst = NULL;\n\n  /* push the symbol and the following form onto the list */\n  MalType form = read_form(reader);\n  if(mal_error) return NULL;\n  lst = list_push(lst, form);\n  lst = list_push(lst, symbol);\n\n  return make_list(lst);\n}\n\nMalType read_string(Reader reader) {\n\n  DEBUG();\n\n  (*reader)++; // initial '\"'\n  size_t count = 0;\n\n  //  Compute the length.\n  for(const char* p=*reader; *p!='\"'; p++) {\n    if(!*p)\n      make_error(\"reader: unbalanced '\\\"'\");\n    if(*p == '\\\\') {\n      p++;\n      switch(*p) {\n      case 0:\n        make_error(\"reader: incomplete \\\\ escape sequence\");\n      case '\\\\':\n      case 'n':\n      case '\"':\n        break;\n\n      default:\n        make_error(\"reader: incomplete escape sequence '\\\\%c'\", *p);\n      }\n    }\n    count++;\n  }\n\n  //  Copy/unescape the characters, add final 0.\n  char* result = GC_MALLOC(count + 1);\n  const char* src;\n  char* dst = result;\n  for(src=*reader; *src!='\"'; src++) {\n    if(*src == '\\\\') {\n      src++;\n      if(*src == 'n') {\n        *dst++ = 0x0A;\n        continue;\n      }\n    }\n    *dst++ = *src;\n  }\n  *dst = 0;\n\n  *reader = src + 1;\n  return make_string(result);\n}\n"
  },
  {
    "path": "impls/c.2/reader.h",
    "content": "#ifndef _MAL_READER_H\n#define _MAL_READER_H\n\n#include \"types.h\"\n\nMalType read_str(const char*);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/readline.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include <gc.h>\n#if USE_READLINE\n#  include <readline/readline.h>\n#  include <readline/history.h>\n#else\n#  include <readline.h>\n#  include <history.h>\n#endif\n\nconst char* readline_gc(const char* prompt) {\n\n  char* str = readline(prompt);\n  if (!str) {\n    return NULL;\n  }\n  add_history(str);\n  /* Copy the input into an area managed by libgc. */\n  size_t n = strlen(str) + 1;\n  char* result = GC_MALLOC(n);\n  memcpy(result, str, n);\n  free(str);\n  return result;\n}\n"
  },
  {
    "path": "impls/c.2/readline.h",
    "content": "#ifndef MAL_READLINE_H\n#define MAL_READLINE_H\n\nconst char* readline_gc(const char* prompt);\n// NULL if EOF\n\n#endif\n"
  },
  {
    "path": "impls/c.2/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/c.2/step0_repl.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include \"readline.h\"\n\n#define PROMPT_STRING \"user> \"\n\nconst char* READ(const char* str) {\n\n  return str;\n}\n\nconst char* EVAL(const char* ast) {\n\n  return ast;\n}\n\nvoid PRINT(const char* str) {\n\n  printf(\"%s\\n\", str);\n}\n\nvoid rep(const char* str) {\n\n  PRINT(EVAL(READ(str)));\n}\n\nint main() {\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input);\n    }\n    printf(\"\\n\");\n\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "impls/c.2/step1_read_print.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"error.h\"\n#include \"readline.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nMalType EVAL(MalType ast) {\n\n  return ast;\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    PRINT(EVAL(a));\n    return;\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\nint main() {\n\n  types_init();\n  printer_init();\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input);\n    }\n    printf(\"\\n\");\n\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "impls/c.2/step2_eval.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, hashmap);\nMalType evaluate_vector(vector_t, hashmap);\nMalType evaluate_hashmap(hashmap, hashmap);\n\n#define generic_arithmetic(name, op, iconst, fconst)      \\\n  MalType name(list args) {                               \\\n    explode2(#op, args, a1, a2);                          \\\n    long i1, i2;                                          \\\n    double f1, f2;                                        \\\n    if (is_integer(a1, &i1)) {                            \\\n      if (is_integer(a2, &i2)) return iconst(i1 op i2);   \\\n      if (is_float  (a2, &f2)) return fconst(i1 op f2);   \\\n      bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a2); \\\n    }                                                     \\\n    if (is_float(a1, &f1)) {                              \\\n      if (is_integer(a2, &i2)) return iconst(f1 op i2);   \\\n      if (is_float  (a2, &f2)) return fconst(f1 op f2);   \\\n      bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a2); \\\n    }                                                     \\\n    bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a1);   \\\n  }\ngeneric_arithmetic(mal_add, +, make_integer, make_float)\ngeneric_arithmetic(mal_sub, -, make_integer, make_float)\ngeneric_arithmetic(mal_mul, *, make_integer, make_float)\ngeneric_arithmetic(mal_div, /, make_integer, make_float)\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nMalType EVAL(MalType ast, hashmap env) {\n\n  /* printf(\"EVAL: %M\\n\", ast); */\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = hashmap_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_FUNCTION, func);\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, hashmap env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\nint main() {\n\n  types_init();\n  printer_init();\n\n  struct map* repl_env = map_empty();\n\n  repl_env = hashmap_put(repl_env, make_symbol(\"+\"), make_function(mal_add));\n  repl_env = hashmap_put(repl_env, make_symbol(\"-\"), make_function(mal_sub));\n  repl_env = hashmap_put(repl_env, make_symbol(\"*\"), make_function(mal_mul));\n  repl_env = hashmap_put(repl_env, make_symbol(\"/\"), make_function(mal_div));\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n\n  return EXIT_SUCCESS;\n}\n\nlist evaluate_list(list lst, hashmap env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, hashmap env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, hashmap env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr = is_function(fn);\n  assert(fun_ptr);\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n}\n"
  },
  {
    "path": "impls/c.2/step3_env.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env*);\nMalType eval_letstar(list, Env*);\n\ntypedef MalType (*special_t)(list, Env*);\nstruct map* specials;\n\n#define generic_arithmetic(name, op, iconst, fconst)      \\\n  MalType name(list args) {                               \\\n    explode2(#op, args, a1, a2);                          \\\n    long i1, i2;                                          \\\n    double f1, f2;                                        \\\n    if (is_integer(a1, &i1)) {                            \\\n      if (is_integer(a2, &i2)) return iconst(i1 op i2);   \\\n      if (is_float  (a2, &f2)) return fconst(i1 op f2);   \\\n      bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a2); \\\n    }                                                     \\\n    if (is_float(a1, &f1)) {                              \\\n      if (is_integer(a2, &i2)) return iconst(f1 op i2);   \\\n      if (is_float  (a2, &f2)) return fconst(f1 op f2);   \\\n      bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a2); \\\n    }                                                     \\\n    bad_type(#op, MALTYPE_INTEGER | MALTYPE_FLOAT, a1);   \\\n  }\ngeneric_arithmetic(mal_add, +, make_integer, make_float)\ngeneric_arithmetic(mal_sub, -, make_integer, make_float)\ngeneric_arithmetic(mal_mul, *, make_integer, make_float)\ngeneric_arithmetic(mal_div, /, make_integer, make_float)\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      return special(lst, env);\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_FUNCTION, func);\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\nint main() {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n\n  Env* repl_env = env_make(NULL);\n\n  env_set(repl_env, make_symbol(\"+\"), make_function(mal_add));\n  env_set(repl_env, make_symbol(\"-\"), make_function(mal_sub));\n  env_set(repl_env, make_symbol(\"*\"), make_function(mal_mul));\n  env_set(repl_env, make_symbol(\"/\"), make_function(mal_div));\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env* env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(env, defbang_symbol, result);\n  return result;\n}\n\nMalType eval_letstar(list lst, Env* env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  return EVAL(forms, letstar_env);\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr = is_function(fn);\n  assert(fun_ptr);\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n}\n"
  },
  {
    "path": "impls/c.2/step4_if_fn_do.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env*);\nMalType eval_letstar(list, Env*);\nMalType eval_if(list, Env*);\nMalType eval_fnstar(list, const Env*);\nMalType eval_do(list, Env*);\n\ntypedef MalType (*special_t)(list, Env*);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      return special(lst, env);\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION, func);\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      return EVAL(closure->fnstar_args->next->data,\n                  env_apply(closure, evlst));\n      // Implicit error propagation\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\nint main() {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n\n  Env* repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env* env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(env, defbang_symbol, result);\n  return result;\n}\n\nMalType eval_letstar(list lst, Env* env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  return EVAL(forms, letstar_env);\n}\n\nMalType eval_if(list lst, Env* env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return EVAL(else_form, env);\n    }\n    else {\n      return make_nil();\n    }\n\n  } else {\n    return EVAL(then_form, env);\n  }\n}\n\nMalType eval_do(list lst, Env* env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return EVAL(lst->data, env);\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, const Env* env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n\n  return make_closure(env, lst);\n}\n\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/step5_tco.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env**);\nMalType eval_letstar(list, Env**);\nMalType eval_if(list, Env**);\nMalType eval_fnstar(list, Env**);\nMalType eval_do(list, Env**);\n\ntypedef MalType (*special_t)(list, Env**);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  /* Use goto to jump here rather than calling eval for tail-call elimination */\n TCE_entry_point:\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      ast = special(lst, &env);\n      if (mal_error) return NULL;\n\n      if(!env) { return ast; }\n      goto TCE_entry_point;\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION, func);\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      /* TCE - modify ast and env directly and jump back to eval */\n      ast = closure->fnstar_args->next->data;\n      env = env_apply(closure, evlst);\n\n      if (mal_error) return NULL;\n      goto TCE_entry_point;\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\nint main() {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n\n  Env* repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env** env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_letstar(list lst, Env** env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(*env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  *env = letstar_env;\n  return forms;\n}\n\nMalType eval_if(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, *env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return else_form;\n    }\n    else {\n      *env = NULL; // no TCO\n      return make_nil();\n    }\n\n  } else {\n    return then_form;\n  }\n}\n\nMalType eval_do(list lst, Env** env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, *env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return lst->data;\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, Env** env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n  Env* fn_env = *env;\n  *env = NULL; // no TCO\n  return make_closure(fn_env, lst);\n}\n\n/* used by core functions but not EVAL as doesn't do TCE */\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/step6_file.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env**);\nMalType eval_letstar(list, Env**);\nMalType eval_if(list, Env**);\nMalType eval_fnstar(list, Env**);\nMalType eval_do(list, Env**);\n\ntypedef MalType (*special_t)(list, Env**);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  /* Use goto to jump here rather than calling eval for tail-call elimination */\n TCE_entry_point:\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      ast = special(lst, &env);\n      if (mal_error) return NULL;\n\n      if(!env) { return ast; }\n      goto TCE_entry_point;\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION, func);\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      /* TCE - modify ast and env directly and jump back to eval */\n      ast = closure->fnstar_args->next->data;\n      env = env_apply(closure, evlst);\n\n      if (mal_error) return NULL;\n      goto TCE_entry_point;\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\n/* declare as global so it can be accessed by mal_eval */\nEnv* repl_env;\n\nMalType mal_eval(list args) {\n\n  explode1(\"eval\", args, ast);\n  return EVAL(ast, repl_env);\n  // Implicit error propagation\n}\n\nint main(int argc, char** argv) {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n\n  repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  env_set(repl_env, make_symbol(\"eval\"), make_function(mal_eval));\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n\n  /* make command line arguments available in the environment */\n  list lst = NULL;\n  while(1 < --argc) {\n    lst = list_push(lst, make_string(argv[argc]));\n  }\n  env_set(repl_env, make_symbol(\"*ARGV*\"), make_list(lst));\n\n  /* run in script mode if a filename is given */\n  if (argc) {\n\n    /* first argument on command line is filename */\n    const char* load_command = mal_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n    re(load_command, repl_env);\n  }\n  /* run in repl mode when no cmd line args */\n  else {\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n  }\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env** env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_letstar(list lst, Env** env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(*env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  *env = letstar_env;\n  return forms;\n}\n\nMalType eval_if(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, *env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return else_form;\n    }\n    else {\n      *env = NULL; // no TCO\n      return make_nil();\n    }\n\n  } else {\n    return then_form;\n  }\n}\n\nMalType eval_do(list lst, Env** env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, *env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return lst->data;\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, Env** env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n  Env* fn_env = *env;\n  *env = NULL; // no TCO\n  return make_closure(fn_env, lst);\n}\n\n/* used by core functions but not EVAL as doesn't do TCE */\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/step7_quote.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env**);\nMalType eval_letstar(list, Env**);\nMalType eval_if(list, Env**);\nMalType eval_fnstar(list, Env**);\nMalType eval_do(list, Env**);\nMalType eval_quote(list, Env**);\nMalType eval_quasiquote(list, Env**);\nMalType quasiquote(MalType);\nMalType quasiquote_vector(vector_t);\nMalType quasiquote_list(list);\nMalType quasiquote_folder(MalType first, MalType qq_rest);\n\ntypedef MalType (*special_t)(list, Env**);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  /* Use goto to jump here rather than calling eval for tail-call elimination */\n TCE_entry_point:\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      ast = special(lst, &env);\n      if (mal_error) return NULL;\n\n      if(!env) { return ast; }\n      goto TCE_entry_point;\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION, func);\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      /* TCE - modify ast and env directly and jump back to eval */\n      ast = closure->fnstar_args->next->data;\n      env = env_apply(closure, evlst);\n\n      if (mal_error) return NULL;\n      goto TCE_entry_point;\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\n/* declare as global so it can be accessed by mal_eval */\nEnv* repl_env;\n\nMalType mal_eval(list args) {\n\n  explode1(\"eval\", args, ast);\n  return EVAL(ast, repl_env);\n  // Implicit error propagation\n}\n\nint main(int argc, char** argv) {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n  specials = hashmap_put(specials, SYMBOL_QUOTE,      eval_quote);\n  specials = hashmap_put(specials, SYMBOL_QUASIQUOTE, eval_quasiquote);\n\n  repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  env_set(repl_env, make_symbol(\"eval\"), make_function(mal_eval));\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n\n  /* make command line arguments available in the environment */\n  list lst = NULL;\n  while(1 < --argc) {\n    lst = list_push(lst, make_string(argv[argc]));\n  }\n  env_set(repl_env, make_symbol(\"*ARGV*\"), make_list(lst));\n\n  /* run in script mode if a filename is given */\n  if (argc) {\n\n    /* first argument on command line is filename */\n    const char* load_command = mal_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n    re(load_command, repl_env);\n  }\n  /* run in repl mode when no cmd line args */\n  else {\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n  }\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env** env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_letstar(list lst, Env** env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(*env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  *env = letstar_env;\n  return forms;\n}\n\nMalType eval_if(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, *env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return else_form;\n    }\n    else {\n      *env = NULL; // no TCO\n      return make_nil();\n    }\n\n  } else {\n    return then_form;\n  }\n}\n\nMalType eval_do(list lst, Env** env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, *env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return lst->data;\n}\n\nMalType eval_quote(list lst, Env** env) {\n\n  explode1(\"quote\", lst, form);\n  *env = NULL; // no TCO\n  return form;\n}\n\nMalType eval_quasiquote(list lst, Env**) {\n  explode1(\"quasiquote\", lst, form);\n  return quasiquote(form);\n  // Implicit error propagation.\n}\n\nMalType quasiquote(MalType ast) {\n\n  /* argument to quasiquote is a vector: (quasiquote [first rest]) */\n  list lst;\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n\n    return quasiquote_vector(vec);\n    // Implicit error propagation\n  }\n\n  /* argument to quasiquote is a list: (quasiquote (first rest)) */\n  else if (is_list(ast, &lst)){\n\n    if(lst) {\n      MalType first = lst->data;\n      if(equal_forms(first, SYMBOL_UNQUOTE)) {\n        lst = lst->next;\n        explode1(\"unquote\", lst, unquoted);\n        return unquoted;\n      }\n    }\n    return quasiquote_list(lst);\n    // Implicit error propagation\n  }\n  /* argument to quasiquote is not self-evaluating and isn't sequential: (quasiquote val)\n     => (quote val) */\n  else if(type(ast) & (MALTYPE_HASHMAP | MALTYPE_SYMBOL)) {\n\n    list lst = NULL;\n    lst = list_push(lst, ast);\n    lst = list_push(lst, SYMBOL_QUOTE);\n    return make_list(lst);\n  }\n  /* argument to quasiquote is self-evaluating: (quasiquote val)\n     => val */\n  else {\n    return ast;\n  }\n}\n\nMalType quasiquote_vector(vector_t vec) {\n\n  MalType result = make_list(NULL);\n  for (size_t i = vec->count; i--; ) {\n    result = quasiquote_folder(vec->nth[i], result);\n    if (mal_error) return NULL;\n  }\n\n    list lst = NULL;\n    lst = list_push(lst, result);\n    lst = list_push(lst, SYMBOL_VEC);\n\n    return make_list(lst);\n}\n\nMalType quasiquote_list(list args) {\n\n    /* handle empty list: (quasiquote ())\n       => () */\n    if (!args) {\n      return make_list(NULL);\n    }\n\n    MalType first = args->data;\n\n    MalType qq_rest = quasiquote_list(args->next);\n    if(mal_error) return NULL;\n\n    return quasiquote_folder(first, qq_rest);\n    // Implicit error propagation.\n}\n\nMalType quasiquote_folder(MalType first, MalType qq_rest) {\n\n    /* handle splice-unquote: (quasiquote ((splice-unquote first-second) rest))\n       => (concat first-second (quasiquote rest)) */\n    list lst;\n    if(is_list(first, &lst)) {\n      if(lst) {\n        MalType lst_first = lst->data;\n        if (equal_forms(lst_first, SYMBOL_SPLICE_UNQUOTE)) {\n          lst = lst->next;\n          explode1(\"splice-unquote\", lst, unquoted);\n          return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                               unquoted),\n                                     SYMBOL_CONCAT));\n        }\n      }\n    }\n    MalType qqted = quasiquote(first);\n    if(mal_error) return NULL;\n    return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                         qqted),\n                               SYMBOL_CONS));\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, Env** env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n  Env* fn_env = *env;\n  *env = NULL; // no TCO\n  return make_closure(fn_env, lst);\n}\n\n/* used by core functions but not EVAL as doesn't do TCE */\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/step8_macros.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env**);\nMalType eval_letstar(list, Env**);\nMalType eval_if(list, Env**);\nMalType eval_fnstar(list, Env**);\nMalType eval_do(list, Env**);\nMalType eval_quote(list, Env**);\nMalType eval_quasiquote(list, Env**);\nMalType quasiquote(MalType);\nMalType quasiquote_vector(vector_t);\nMalType quasiquote_list(list);\nMalType quasiquote_folder(MalType first, MalType qq_rest);\nMalType eval_defmacrobang(list, Env**);\n\ntypedef MalType (*special_t)(list, Env**);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  /* Use goto to jump here rather than calling eval for tail-call elimination */\n TCE_entry_point:\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      ast = special(lst, &env);\n      if (mal_error) return NULL;\n\n      if(!env) { return ast; }\n      goto TCE_entry_point;\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION | MALTYPE_MACRO, func);\n  if (type(func) == MALTYPE_MACRO) {\n    ast = apply(func, lst);\n    if (mal_error) { return NULL; }\n    goto TCE_entry_point;\n  }\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      /* TCE - modify ast and env directly and jump back to eval */\n      ast = closure->fnstar_args->next->data;\n      env = env_apply(closure, evlst);\n\n      if (mal_error) return NULL;\n      goto TCE_entry_point;\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\n/* declare as global so it can be accessed by mal_eval */\nEnv* repl_env;\n\nMalType mal_eval(list args) {\n\n  explode1(\"eval\", args, ast);\n  return EVAL(ast, repl_env);\n  // Implicit error propagation\n}\n\nint main(int argc, char** argv) {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n  specials = hashmap_put(specials, SYMBOL_QUOTE,      eval_quote);\n  specials = hashmap_put(specials, SYMBOL_QUASIQUOTE, eval_quasiquote);\n  specials = hashmap_put(specials, SYMBOL_DEFMACRO,   eval_defmacrobang);\n\n  repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  env_set(repl_env, make_symbol(\"eval\"), make_function(mal_eval));\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n  /* make command line arguments available in the environment */\n  list lst = NULL;\n  while(1 < --argc) {\n    lst = list_push(lst, make_string(argv[argc]));\n  }\n  env_set(repl_env, make_symbol(\"*ARGV*\"), make_list(lst));\n\n  /* run in script mode if a filename is given */\n  if (argc) {\n\n    /* first argument on command line is filename */\n    const char* load_command = mal_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n    re(load_command, repl_env);\n  }\n  /* run in repl mode when no cmd line args */\n  else {\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n  }\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env** env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_letstar(list lst, Env** env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(*env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  *env = letstar_env;\n  return forms;\n}\n\nMalType eval_if(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, *env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return else_form;\n    }\n    else {\n      *env = NULL; // no TCO\n      return make_nil();\n    }\n\n  } else {\n    return then_form;\n  }\n}\n\nMalType eval_do(list lst, Env** env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, *env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return lst->data;\n}\n\nMalType eval_quote(list lst, Env** env) {\n\n  explode1(\"quote\", lst, form);\n  *env = NULL; // no TCO\n  return form;\n}\n\nMalType eval_quasiquote(list lst, Env**) {\n  explode1(\"quasiquote\", lst, form);\n  return quasiquote(form);\n  // Implicit error propagation.\n}\n\nMalType quasiquote(MalType ast) {\n\n  /* argument to quasiquote is a vector: (quasiquote [first rest]) */\n  list lst;\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n\n    return quasiquote_vector(vec);\n    // Implicit error propagation\n  }\n\n  /* argument to quasiquote is a list: (quasiquote (first rest)) */\n  else if (is_list(ast, &lst)){\n\n    if(lst) {\n      MalType first = lst->data;\n      if(equal_forms(first, SYMBOL_UNQUOTE)) {\n        lst = lst->next;\n        explode1(\"unquote\", lst, unquoted);\n        return unquoted;\n      }\n    }\n    return quasiquote_list(lst);\n    // Implicit error propagation\n  }\n  /* argument to quasiquote is not self-evaluating and isn't sequential: (quasiquote val)\n     => (quote val) */\n  else if(type(ast) & (MALTYPE_HASHMAP | MALTYPE_SYMBOL)) {\n\n    list lst = NULL;\n    lst = list_push(lst, ast);\n    lst = list_push(lst, SYMBOL_QUOTE);\n    return make_list(lst);\n  }\n  /* argument to quasiquote is self-evaluating: (quasiquote val)\n     => val */\n  else {\n    return ast;\n  }\n}\n\nMalType quasiquote_vector(vector_t vec) {\n\n  MalType result = make_list(NULL);\n  for (size_t i = vec->count; i--; ) {\n    result = quasiquote_folder(vec->nth[i], result);\n    if (mal_error) return NULL;\n  }\n\n    list lst = NULL;\n    lst = list_push(lst, result);\n    lst = list_push(lst, SYMBOL_VEC);\n\n    return make_list(lst);\n}\n\nMalType quasiquote_list(list args) {\n\n    /* handle empty list: (quasiquote ())\n       => () */\n    if (!args) {\n      return make_list(NULL);\n    }\n\n    MalType first = args->data;\n\n    MalType qq_rest = quasiquote_list(args->next);\n    if(mal_error) return NULL;\n\n    return quasiquote_folder(first, qq_rest);\n    // Implicit error propagation.\n}\n\nMalType quasiquote_folder(MalType first, MalType qq_rest) {\n\n    /* handle splice-unquote: (quasiquote ((splice-unquote first-second) rest))\n       => (concat first-second (quasiquote rest)) */\n    list lst;\n    if(is_list(first, &lst)) {\n      if(lst) {\n        MalType lst_first = lst->data;\n        if (equal_forms(lst_first, SYMBOL_SPLICE_UNQUOTE)) {\n          lst = lst->next;\n          explode1(\"splice-unquote\", lst, unquoted);\n          return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                               unquoted),\n                                     SYMBOL_CONCAT));\n        }\n      }\n    }\n    MalType qqted = quasiquote(first);\n    if(mal_error) return NULL;\n    return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                         qqted),\n                               SYMBOL_CONS));\n}\n\nMalType eval_defmacrobang(list lst, Env** env) {\n\n  explode2(\"defmacro!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n\n  if (mal_error) return NULL;\n\n  MalClosure closure = is_closure(result);\n  if (!closure) {\n    bad_type(\"defmacro!\", MALTYPE_CLOSURE, result);\n  }\n  result = make_macro(closure->env, closure->fnstar_args);\n  check_type(\"defmacro!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, Env** env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n  Env* fn_env = *env;\n  *env = NULL; // no TCO\n  return make_closure(fn_env, lst);\n}\n\n/* used by core functions but not EVAL as doesn't do TCE */\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    if (!closure) closure = is_macro(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/step9_try.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env**);\nMalType eval_letstar(list, Env**);\nMalType eval_if(list, Env**);\nMalType eval_fnstar(list, Env**);\nMalType eval_do(list, Env**);\nMalType eval_quote(list, Env**);\nMalType eval_quasiquote(list, Env**);\nMalType quasiquote(MalType);\nMalType quasiquote_vector(vector_t);\nMalType quasiquote_list(list);\nMalType quasiquote_folder(MalType first, MalType qq_rest);\nMalType eval_defmacrobang(list, Env**);\nMalType eval_try(list, Env**);\n\ntypedef MalType (*special_t)(list, Env**);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  /* Use goto to jump here rather than calling eval for tail-call elimination */\n TCE_entry_point:\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      ast = special(lst, &env);\n      if (mal_error) return NULL;\n\n      if(!env) { return ast; }\n      goto TCE_entry_point;\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION | MALTYPE_MACRO, func);\n  if (type(func) == MALTYPE_MACRO) {\n    ast = apply(func, lst);\n    if (mal_error) { return NULL; }\n    goto TCE_entry_point;\n  }\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      /* TCE - modify ast and env directly and jump back to eval */\n      ast = closure->fnstar_args->next->data;\n      env = env_apply(closure, evlst);\n\n      if (mal_error) return NULL;\n      goto TCE_entry_point;\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\n/* declare as global so it can be accessed by mal_eval */\nEnv* repl_env;\n\nMalType mal_eval(list args) {\n\n  explode1(\"eval\", args, ast);\n  return EVAL(ast, repl_env);\n  // Implicit error propagation\n}\n\nint main(int argc, char** argv) {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n  specials = hashmap_put(specials, SYMBOL_QUOTE,      eval_quote);\n  specials = hashmap_put(specials, SYMBOL_QUASIQUOTE, eval_quasiquote);\n  specials = hashmap_put(specials, SYMBOL_DEFMACRO,   eval_defmacrobang);\n  specials = hashmap_put(specials, SYMBOL_TRY,        eval_try);\n\n  repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  env_set(repl_env, make_symbol(\"eval\"), make_function(mal_eval));\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n  /* make command line arguments available in the environment */\n  list lst = NULL;\n  while(1 < --argc) {\n    lst = list_push(lst, make_string(argv[argc]));\n  }\n  env_set(repl_env, make_symbol(\"*ARGV*\"), make_list(lst));\n\n  /* run in script mode if a filename is given */\n  if (argc) {\n\n    /* first argument on command line is filename */\n    const char* load_command = mal_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n    re(load_command, repl_env);\n  }\n  /* run in repl mode when no cmd line args */\n  else {\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n  }\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env** env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_letstar(list lst, Env** env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(*env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  *env = letstar_env;\n  return forms;\n}\n\nMalType eval_if(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, *env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return else_form;\n    }\n    else {\n      *env = NULL; // no TCO\n      return make_nil();\n    }\n\n  } else {\n    return then_form;\n  }\n}\n\nMalType eval_do(list lst, Env** env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, *env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return lst->data;\n}\n\nMalType eval_quote(list lst, Env** env) {\n\n  explode1(\"quote\", lst, form);\n  *env = NULL; // no TCO\n  return form;\n}\n\nMalType eval_quasiquote(list lst, Env**) {\n  explode1(\"quasiquote\", lst, form);\n  return quasiquote(form);\n  // Implicit error propagation.\n}\n\nMalType quasiquote(MalType ast) {\n\n  /* argument to quasiquote is a vector: (quasiquote [first rest]) */\n  list lst;\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n\n    return quasiquote_vector(vec);\n    // Implicit error propagation\n  }\n\n  /* argument to quasiquote is a list: (quasiquote (first rest)) */\n  else if (is_list(ast, &lst)){\n\n    if(lst) {\n      MalType first = lst->data;\n      if(equal_forms(first, SYMBOL_UNQUOTE)) {\n        lst = lst->next;\n        explode1(\"unquote\", lst, unquoted);\n        return unquoted;\n      }\n    }\n    return quasiquote_list(lst);\n    // Implicit error propagation\n  }\n  /* argument to quasiquote is not self-evaluating and isn't sequential: (quasiquote val)\n     => (quote val) */\n  else if(type(ast) & (MALTYPE_HASHMAP | MALTYPE_SYMBOL)) {\n\n    list lst = NULL;\n    lst = list_push(lst, ast);\n    lst = list_push(lst, SYMBOL_QUOTE);\n    return make_list(lst);\n  }\n  /* argument to quasiquote is self-evaluating: (quasiquote val)\n     => val */\n  else {\n    return ast;\n  }\n}\n\nMalType quasiquote_vector(vector_t vec) {\n\n  MalType result = make_list(NULL);\n  for (size_t i = vec->count; i--; ) {\n    result = quasiquote_folder(vec->nth[i], result);\n    if (mal_error) return NULL;\n  }\n\n    list lst = NULL;\n    lst = list_push(lst, result);\n    lst = list_push(lst, SYMBOL_VEC);\n\n    return make_list(lst);\n}\n\nMalType quasiquote_list(list args) {\n\n    /* handle empty list: (quasiquote ())\n       => () */\n    if (!args) {\n      return make_list(NULL);\n    }\n\n    MalType first = args->data;\n\n    MalType qq_rest = quasiquote_list(args->next);\n    if(mal_error) return NULL;\n\n    return quasiquote_folder(first, qq_rest);\n    // Implicit error propagation.\n}\n\nMalType quasiquote_folder(MalType first, MalType qq_rest) {\n\n    /* handle splice-unquote: (quasiquote ((splice-unquote first-second) rest))\n       => (concat first-second (quasiquote rest)) */\n    list lst;\n    if(is_list(first, &lst)) {\n      if(lst) {\n        MalType lst_first = lst->data;\n        if (equal_forms(lst_first, SYMBOL_SPLICE_UNQUOTE)) {\n          lst = lst->next;\n          explode1(\"splice-unquote\", lst, unquoted);\n          return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                               unquoted),\n                                     SYMBOL_CONCAT));\n        }\n      }\n    }\n    MalType qqted = quasiquote(first);\n    if(mal_error) return NULL;\n    return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                         qqted),\n                               SYMBOL_CONS));\n}\n\nMalType eval_defmacrobang(list lst, Env** env) {\n\n  explode2(\"defmacro!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n\n  if (mal_error) return NULL;\n\n  MalClosure closure = is_closure(result);\n  if (!closure) {\n    bad_type(\"defmacro!\", MALTYPE_CLOSURE, result);\n  }\n  result = make_macro(closure->env, closure->fnstar_args);\n  check_type(\"defmacro!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_try(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"try*\", \"one or two arguments\", lst);\n  }\n\n  MalType try_clause = lst->data;\n\n  list l = lst->next;\n  if (!l) {\n    /* no catch* clause */\n    return try_clause;\n  }\n\n  MalType catch_clause = l->data;\n  if (l->next) {\n    bad_arg_count(\"try*\", \"one or two arguments\", lst);\n  }\n\n  /* process catch* clause */\n  check_type(\"try*\", MALTYPE_LIST, catch_clause);\n  list catch_list;\n  if (!is_list(catch_clause, &catch_list)) {\n    bad_type(\"try*\", MALTYPE_LIST, catch_clause);\n  }\n  explode3(\"try*(catch clause)\", catch_list, catch_symbol, a2, handler);\n  if (!equal_forms(catch_symbol, SYMBOL_CATCH)) {\n    make_error(\"'try*': catch* clause is missing catch* symbol: %M\",\n                          catch_clause);\n  }\n  check_type(\"try*\", MALTYPE_SYMBOL, a2);\n\n  MalType try_result = EVAL(try_clause, *env);\n  if(!mal_error) {\n    *env = NULL;                // prevent TCO\n    return try_result;\n  }\n\n  /* bind the symbol to the exception */\n  Env* catch_env = env_make(*env);\n  env_set(catch_env,\n          a2, mal_error);\n  mal_error = NULL;\n  *env = catch_env;\n\n  return handler;\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, Env** env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n  Env* fn_env = *env;\n  *env = NULL; // no TCO\n  return make_closure(fn_env, lst);\n}\n\n/* used by core functions but not EVAL as doesn't do TCE */\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    if (!closure) closure = is_macro(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/stepA_mal.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n#include \"linked_list.h\"\n#include \"types.h\"\n#include \"reader.h\"\n#include \"printer.h\"\n#include \"env.h\"\n#include \"core.h\"\n#include \"error.h\"\n#include \"hashmap.h\"\n#include \"readline.h\"\n#include \"vector.h\"\n\n#define PROMPT_STRING \"user> \"\n\nMalType apply(MalType, list); // For the apply phase and core apply/map/swap.\nlist evaluate_list(list, Env*);\nMalType evaluate_vector(vector_t, Env*);\nMalType evaluate_hashmap(hashmap lst, Env* env);\nMalType eval_defbang(list, Env**);\nMalType eval_letstar(list, Env**);\nMalType eval_if(list, Env**);\nMalType eval_fnstar(list, Env**);\nMalType eval_do(list, Env**);\nMalType eval_quote(list, Env**);\nMalType eval_quasiquote(list, Env**);\nMalType quasiquote(MalType);\nMalType quasiquote_vector(vector_t);\nMalType quasiquote_list(list);\nMalType quasiquote_folder(MalType first, MalType qq_rest);\nMalType eval_defmacrobang(list, Env**);\nMalType eval_try(list, Env**);\n\ntypedef MalType (*special_t)(list, Env**);\nstruct map* specials;\n\nMalType READ(const char* str) {\n\n  return read_str(str);\n  // Implicit error propagation\n}\n\nEnv* env_apply(MalClosure closure, list args) {\n  //  Return the closure definition and update env if all went OK,\n  //  else return an error.\n  Env* fn_env = env_make(closure->env);\n  MalType params = closure->fnstar_args->data;\n\n  assert(type(params) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  seq_cursor c = seq_iter(params);\n  list a = args;\n  while (true) {\n    if (!seq_cont(params, c)) {\n      if (a) {\n        make_error(\"'apply': expected %M, got [%N]\", params, args);\n      }\n      break;\n    }\n    MalType parameter = seq_item(params, c);\n    if (equal_forms(parameter, SYMBOL_AMPERSAND)) {\n      c = seq_next(params, c);\n      assert(seq_cont(params, c));\n      env_set(fn_env, seq_item(params, c), make_list(a));\n      break;\n    }\n    if (!a) {\n      make_error(\"'apply': expected %M, got [%N]\", params, args);\n    }\n    env_set(fn_env, parameter, a->data);\n    c = seq_next(params, c);\n    a = a->next;\n  }\n  return fn_env;\n}\n\nMalType EVAL(MalType ast, Env* env) {\n\n  /* Use goto to jump here rather than calling eval for tail-call elimination */\n TCE_entry_point:\n\n  MalType dbgeval = env_get(env, SYMBOL_DEBUG_EVAL);\n  if (dbgeval && (type(dbgeval) & ~(MALTYPE_FALSE | MALTYPE_NIL)))\n    printf(\"EVAL: %50M env: %H\\n\", ast, env_as_map(env));\n\n  if (type(ast) == MALTYPE_SYMBOL) {\n    MalType symbol_value = env_get(env, ast);\n    if (symbol_value)\n      return symbol_value;\n    else\n      make_error(\"'%M' not found\", ast);\n  }\n\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n    return evaluate_vector(vec, env);\n    // Implicit error propagation\n  }\n\n  hashmap map;\n  if ((map = is_hashmap(ast))) {\n    return evaluate_hashmap(map, env);\n    // Implicit error propagation\n  }\n\n  /* not a list */\n  list lst;\n  if (!is_list(ast, &lst)) { return ast; }\n\n  /* empty list */\n  if(lst == NULL) { return ast; }\n\n  /* list */\n  MalType first = lst->data;\n  lst = lst->next;\n\n    /* handle special symbols first */\n  if (type(first) & MALTYPE_SYMBOL) {\n    special_t special = hashmap_get(specials, first);\n    if (special) {\n      ast = special(lst, &env);\n      if (mal_error) return NULL;\n\n      if(!env) { return ast; }\n      goto TCE_entry_point;\n    }\n  }\n\n  /* first element is not a special symbol */\n  MalType func = EVAL(first, env);\n  if (mal_error) { return NULL; }\n  check_type(\"apply phase\", MALTYPE_CLOSURE | MALTYPE_FUNCTION | MALTYPE_MACRO, func);\n  if (type(func) == MALTYPE_MACRO) {\n    ast = apply(func, lst);\n    if (mal_error) { return NULL; }\n    goto TCE_entry_point;\n  }\n  //  Evaluate the arguments\n  list evlst = evaluate_list(lst, env);\n  if (mal_error) return NULL;\n\n  /* apply the first element of the list to the arguments */\n  MalClosure closure;\n  if ((closure = is_closure(func))) {\n\n      /* TCE - modify ast and env directly and jump back to eval */\n      ast = closure->fnstar_args->next->data;\n      env = env_apply(closure, evlst);\n\n      if (mal_error) return NULL;\n      goto TCE_entry_point;\n  }\n  return apply(func, evlst);\n  // Implicit error propagation\n}\n\nvoid PRINT(MalType val) {\n\n  printf(\"%M\\n\", val);\n}\n\nvoid rep(const char* str, Env* env) {\n\n  MalType a = READ(str);\n  if (!mal_error) {\n    MalType b = EVAL(a, env);\n    if (!mal_error) {\n      PRINT(b);\n      return;\n    }\n  }\n  MalType e = mal_error;\n  mal_error = NULL; // before printing\n  printf(\"Uncaught error: %M\\n\", e);\n}\n\n//  Variant reporting errors during startup.\nvoid re(const char *str, Env* env) {\n    MalType a = READ(str);\n    if (!mal_error) {\n      EVAL(a, env);\n      if (!mal_error) {\n        return;\n      }\n    }\n    MalType result = mal_error;\n    mal_error = NULL; // before printing\n    printf(\"Error during startup: %M\\n\", result);\n    exit(EXIT_FAILURE);\n}\n\n/* declare as global so it can be accessed by mal_eval */\nEnv* repl_env;\n\nMalType mal_eval(list args) {\n\n  explode1(\"eval\", args, ast);\n  return EVAL(ast, repl_env);\n  // Implicit error propagation\n}\n\nint main(int argc, char** argv) {\n\n  types_init();\n  printer_init();\n\n  specials = map_empty();\n  specials = hashmap_put(specials, SYMBOL_DEF,        eval_defbang);\n  specials = hashmap_put(specials, SYMBOL_LET,        eval_letstar);\n  specials = hashmap_put(specials, SYMBOL_IF,         eval_if);\n  specials = hashmap_put(specials, SYMBOL_FN,         eval_fnstar);\n  specials = hashmap_put(specials, SYMBOL_DO,         eval_do);\n  specials = hashmap_put(specials, SYMBOL_QUOTE,      eval_quote);\n  specials = hashmap_put(specials, SYMBOL_QUASIQUOTE, eval_quasiquote);\n  specials = hashmap_put(specials, SYMBOL_DEFMACRO,   eval_defmacrobang);\n  specials = hashmap_put(specials, SYMBOL_TRY,        eval_try);\n\n  repl_env = env_make(NULL);\n\n  ns core;\n  size_t core_size;\n  ns_make_core(&core, &core_size);\n  while(core_size--) {\n    const char* symbol = core[core_size].key;\n    function_t function = core[core_size].value;\n    env_set(repl_env, make_symbol(symbol), make_function(function));\n  }\n\n  env_set(repl_env, make_symbol(\"eval\"), make_function(mal_eval));\n\n  /* add functions written in mal - not using rep as it prints the result */\n  re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n  /* make command line arguments available in the environment */\n  list lst = NULL;\n  while(1 < --argc) {\n    lst = list_push(lst, make_string(argv[argc]));\n  }\n  env_set(repl_env, make_symbol(\"*ARGV*\"), make_list(lst));\n  env_set(repl_env, make_symbol(\"*host-language*\"), make_string(\"c.2\"));\n\n  /* run in script mode if a filename is given */\n  if (argc) {\n\n    /* first argument on command line is filename */\n    const char* load_command = mal_printf(\"(load-file \\\"%s\\\")\", argv[1]);\n    re(load_command, repl_env);\n  }\n  /* run in repl mode when no cmd line args */\n  else {\n\n    /* Greeting message */\n    re(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env);\n\n    const char* input;\n    while((input = readline_gc(PROMPT_STRING))) {\n\n      /* print prompt and get input*/\n      /* Check for EOF (Ctrl-D) */\n\n      /* call Read-Eval-Print */\n      rep(input, repl_env);\n    }\n    printf(\"\\n\");\n  }\n  return EXIT_SUCCESS;\n}\n\nMalType eval_defbang(list lst, Env** env) {\n\n  explode2(\"def!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n  if (mal_error) {\n    return NULL;\n  }\n  check_type(\"def!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_letstar(list lst, Env** env) {\n\n  explode2(\"let*\", lst, bindings, forms);\n\n  check_type(\"let*\", MALTYPE_LIST | MALTYPE_VECTOR, bindings);\n\n  seq_cursor bindings_list = seq_iter(bindings);\n  Env* letstar_env = env_make(*env);\n\n  /* evaluate the bindings */\n  while(seq_cont(bindings, bindings_list)) {\n\n    MalType symbol = seq_item(bindings, bindings_list);\n    bindings_list = seq_next(bindings, bindings_list);\n    if(!seq_cont(bindings, bindings_list)) {\n      bad_arg_count(\"let*\", \"an even number of binding pairs\",\n                            bindings);\n    }\n    MalType value = EVAL(seq_item(bindings, bindings_list), letstar_env);\n\n    /* early return from error */\n    if (mal_error) {\n      return NULL;\n    }\n\n    check_type(\"let*\", MALTYPE_SYMBOL, symbol);\n    env_set(letstar_env, symbol, value);\n    bindings_list = seq_next(bindings, bindings_list);\n  }\n\n  *env = letstar_env;\n  return forms;\n}\n\nMalType eval_if(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType raw_condition = lst->data;\n  list l1 = lst->next;\n  if (!l1) {\n    bad_arg_count(\"if\", \"two or three arguments\", lst);\n  }\n  MalType then_form = l1->data;\n  list l2 = l1->next;\n  MalType else_form;\n  if (l2) {\n    else_form = l2->data;\n    if (l2->next) {\n      bad_arg_count(\"if\", \"two or three arguments\", lst);\n    }\n  }\n  else {\n    else_form = NULL;\n  }\n\n  MalType condition = EVAL(raw_condition, *env);\n\n  if (mal_error) {\n    return NULL;\n  }\n\n  if (type(condition) & (MALTYPE_FALSE | MALTYPE_NIL)) {\n\n    /* check whether false branch is present */\n    if(else_form) {\n      return else_form;\n    }\n    else {\n      *env = NULL; // no TCO\n      return make_nil();\n    }\n\n  } else {\n    return then_form;\n  }\n}\n\nMalType eval_do(list lst, Env** env) {\n\n  /* handle empty 'do' */\n  if (!lst) {\n    return make_nil();\n  }\n\n  /* evaluate all but the last form */\n  while (lst->next) {\n\n    EVAL(lst->data, *env);\n\n    /* return error early */\n    if (mal_error) {\n      return NULL;\n    }\n    lst = lst->next;\n  }\n  /* return the last form for TCE evaluation */\n  return lst->data;\n}\n\nMalType eval_quote(list lst, Env** env) {\n\n  explode1(\"quote\", lst, form);\n  *env = NULL; // no TCO\n  return form;\n}\n\nMalType eval_quasiquote(list lst, Env**) {\n  explode1(\"quasiquote\", lst, form);\n  return quasiquote(form);\n  // Implicit error propagation.\n}\n\nMalType quasiquote(MalType ast) {\n\n  /* argument to quasiquote is a vector: (quasiquote [first rest]) */\n  list lst;\n  vector_t vec;\n  if ((vec = is_vector(ast))) {\n\n    return quasiquote_vector(vec);\n    // Implicit error propagation\n  }\n\n  /* argument to quasiquote is a list: (quasiquote (first rest)) */\n  else if (is_list(ast, &lst)){\n\n    if(lst) {\n      MalType first = lst->data;\n      if(equal_forms(first, SYMBOL_UNQUOTE)) {\n        lst = lst->next;\n        explode1(\"unquote\", lst, unquoted);\n        return unquoted;\n      }\n    }\n    return quasiquote_list(lst);\n    // Implicit error propagation\n  }\n  /* argument to quasiquote is not self-evaluating and isn't sequential: (quasiquote val)\n     => (quote val) */\n  else if(type(ast) & (MALTYPE_HASHMAP | MALTYPE_SYMBOL)) {\n\n    list lst = NULL;\n    lst = list_push(lst, ast);\n    lst = list_push(lst, SYMBOL_QUOTE);\n    return make_list(lst);\n  }\n  /* argument to quasiquote is self-evaluating: (quasiquote val)\n     => val */\n  else {\n    return ast;\n  }\n}\n\nMalType quasiquote_vector(vector_t vec) {\n\n  MalType result = make_list(NULL);\n  for (size_t i = vec->count; i--; ) {\n    result = quasiquote_folder(vec->nth[i], result);\n    if (mal_error) return NULL;\n  }\n\n    list lst = NULL;\n    lst = list_push(lst, result);\n    lst = list_push(lst, SYMBOL_VEC);\n\n    return make_list(lst);\n}\n\nMalType quasiquote_list(list args) {\n\n    /* handle empty list: (quasiquote ())\n       => () */\n    if (!args) {\n      return make_list(NULL);\n    }\n\n    MalType first = args->data;\n\n    MalType qq_rest = quasiquote_list(args->next);\n    if(mal_error) return NULL;\n\n    return quasiquote_folder(first, qq_rest);\n    // Implicit error propagation.\n}\n\nMalType quasiquote_folder(MalType first, MalType qq_rest) {\n\n    /* handle splice-unquote: (quasiquote ((splice-unquote first-second) rest))\n       => (concat first-second (quasiquote rest)) */\n    list lst;\n    if(is_list(first, &lst)) {\n      if(lst) {\n        MalType lst_first = lst->data;\n        if (equal_forms(lst_first, SYMBOL_SPLICE_UNQUOTE)) {\n          lst = lst->next;\n          explode1(\"splice-unquote\", lst, unquoted);\n          return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                               unquoted),\n                                     SYMBOL_CONCAT));\n        }\n      }\n    }\n    MalType qqted = quasiquote(first);\n    if(mal_error) return NULL;\n    return make_list(list_push(list_push(list_push(NULL, qq_rest),\n                                         qqted),\n                               SYMBOL_CONS));\n}\n\nMalType eval_defmacrobang(list lst, Env** env) {\n\n  explode2(\"defmacro!\", lst, defbang_symbol, defbang_value);\n\n  MalType result = EVAL(defbang_value, *env);\n\n  if (mal_error) return NULL;\n\n  MalClosure closure = is_closure(result);\n  if (!closure) {\n    bad_type(\"defmacro!\", MALTYPE_CLOSURE, result);\n  }\n  result = make_macro(closure->env, closure->fnstar_args);\n  check_type(\"defmacro!\", MALTYPE_SYMBOL, defbang_symbol);\n  env_set(*env, defbang_symbol, result);\n  *env = NULL; // no TCO\n  return result;\n}\n\nMalType eval_try(list lst, Env** env) {\n\n  if (!lst) {\n    bad_arg_count(\"try*\", \"one or two arguments\", lst);\n  }\n\n  MalType try_clause = lst->data;\n\n  list l = lst->next;\n  if (!l) {\n    /* no catch* clause */\n    return try_clause;\n  }\n\n  MalType catch_clause = l->data;\n  if (l->next) {\n    bad_arg_count(\"try*\", \"one or two arguments\", lst);\n  }\n\n  /* process catch* clause */\n  check_type(\"try*\", MALTYPE_LIST, catch_clause);\n  list catch_list;\n  if (!is_list(catch_clause, &catch_list)) {\n    bad_type(\"try*\", MALTYPE_LIST, catch_clause);\n  }\n  explode3(\"try*(catch clause)\", catch_list, catch_symbol, a2, handler);\n  if (!equal_forms(catch_symbol, SYMBOL_CATCH)) {\n    make_error(\"'try*': catch* clause is missing catch* symbol: %M\",\n                          catch_clause);\n  }\n  check_type(\"try*\", MALTYPE_SYMBOL, a2);\n\n  MalType try_result = EVAL(try_clause, *env);\n  if(!mal_error) {\n    *env = NULL;                // prevent TCO\n    return try_result;\n  }\n\n  /* bind the symbol to the exception */\n  Env* catch_env = env_make(*env);\n  env_set(catch_env,\n          a2, mal_error);\n  mal_error = NULL;\n  *env = catch_env;\n\n  return handler;\n}\n\nlist evaluate_list(list lst, Env* env) {\n\n  list evlst = NULL;\n  list* evlst_last = &evlst;\n  while (lst) {\n\n    MalType val = EVAL(lst->data, env);\n\n    if (mal_error) {\n      return NULL;\n    }\n\n    *evlst_last = list_push(NULL, val);\n    evlst_last = &(*evlst_last)->next;\n    lst = lst->next;\n  }\n  return evlst;\n}\n\nMalType evaluate_vector(vector_t lst, Env* env) {\n  size_t capacity = lst->count;\n  struct vector* evlst = vector_new(capacity);\n  for (size_t i = 0; i < capacity; i++) {\n    MalType new = EVAL(lst->nth[i], env);\n    if (mal_error) return NULL;\n    vector_append(&capacity, &evlst, new);\n  }\n  assert(evlst->count == capacity);\n  return make_vector(evlst);\n}\n\nMalType evaluate_hashmap(hashmap lst, Env* env) {\n  // map_empty() would be OK, but we know the size in advance and can\n  // spare inefficient reallocations.\n  struct map* evlst = map_copy(lst);\n  for (map_cursor c = map_iter(lst); map_cont(lst, c); c = map_next(lst, c)) {\n    MalType new = EVAL(map_val(lst, c), env);\n    if (mal_error) return false;\n    evlst = hashmap_put(evlst, map_key(lst, c), new);\n  }\n  return make_hashmap(evlst);\n}\n\nMalType eval_fnstar(list lst, Env** env) {\n\n  if (!lst || !lst->next || lst->next->next) {\n    bad_arg_count(\"fn*\", \"two parameters\", lst);\n  }\n  MalType parameters = lst->data;\n  check_type(\"fn*\", MALTYPE_LIST | MALTYPE_VECTOR, parameters);\n\n  for (seq_cursor c = seq_iter(parameters);\n       seq_cont(parameters, c);\n       c = seq_next(parameters, c)) {\n\n    MalType val = seq_item(parameters, c);\n\n    if (!is_symbol(val)) {\n      bad_type(\"fn*\", MALTYPE_SYMBOL, val);\n    }\n\n    if (equal_forms(val, SYMBOL_AMPERSAND)) {\n      c = seq_next(parameters, c);\n      if (!val) {\n        make_error(\"'fn*': no symbol after &: '%N'\", lst);\n      }\n      val = seq_item(parameters, c);\n      /* & is found and there is a single symbol after */\n      check_type(\"fn*\", MALTYPE_SYMBOL, val);\n      /* & is found and there extra symbols after */\n      c = seq_next(parameters, c);\n      if (seq_cont(parameters, c)) {\n        make_error(\"'fn*': extra symbols after &: '%N'\", lst);\n      }\n      break;\n    }\n  }\n  Env* fn_env = *env;\n  *env = NULL; // no TCO\n  return make_closure(fn_env, lst);\n}\n\n/* used by core functions but not EVAL as doesn't do TCE */\nMalType apply(MalType fn, list args) {\n\n  function_t fun_ptr;\n  if ((fun_ptr = is_function(fn))) {\n\n    return (*fun_ptr)(args);\n    // Implicit error propagation\n  }\n  else {\n\n    MalClosure closure = is_closure(fn);\n    if (!closure) closure = is_macro(fn);\n    assert(closure);\n    MalType ast = closure->fnstar_args->next->data;\n    Env* env = env_apply(closure, args);\n    if (mal_error) return NULL;\n    return EVAL(ast, env);\n    // Implicit error propagation\n  }\n}\n"
  },
  {
    "path": "impls/c.2/tests/stepA_mal.mal",
    "content": ";; Testing FFI of \"strlen\"\n(. nil \"int32\" \"strlen\" \"string\" \"abcde\")\n;=>5\n(. nil \"int32\" \"strlen\" \"string\" \"\")\n;=>0\n\n;; Testing FFI of \"strcmp\"\n\n(. nil \"int32\" \"strcmp\" \"string\" \"abc\" \"string\" \"abcA\")\n;=>-65\n(. nil \"int32\" \"strcmp\" \"string\" \"abcA\" \"string\" \"abc\")\n;=>65\n(. nil \"int32\" \"strcmp\" \"string\" \"abc\" \"string\" \"abc\")\n;=>0\n\n\n;; Testing FFI of \"pow\" (libm.so)\n\n(. \"libm.so.6\" \"double\" \"pow\" \"double\" 2.0 \"double\" 3.0)\n;=>8.000000\n(. \"libm.so.6\" \"double\" \"pow\" \"double\" 3.0 \"double\" 2.0)\n;=>9.000000\n"
  },
  {
    "path": "impls/c.2/types.c",
    "content": "#include <assert.h>\n#ifdef DEBUG_HASH\n#  include \"stdio.h\"\n#endif\n#include <string.h>\n\n#include <gc.h>\n\n#include \"types.h\"\n#include \"vector.h\"\n#include \"hashmap.h\"\n#include \"linked_list.h\"\n#ifdef DEBUG_HASH\n#  include \"printer.h\"\n#endif\n\nstruct MalType_s {\n  enum mal_type_t type;\n\n  union MalType_u {\n    long mal_integer;\n    double mal_float;\n    struct {\n      const char* s;\n      size_t      hash;\n    } mal_string;\n    struct {\n      list    l;\n      MalType meta;\n    } mal_list;\n    struct {\n      vector_t v;\n      MalType  meta;\n    } mal_vector;\n    struct {\n      hashmap m;\n      MalType meta;\n    } mal_hashmap;\n    struct {\n      function_t f;\n      MalType    meta;\n    } mal_function;\n    struct {\n      struct MalClosure_s c;\n      MalType meta;\n    } mal_closure;\n    MalType mal_atom;\n  } value;\n};\n\nstruct MalType_s THE_NIL = { MALTYPE_NIL, {0}};\nstruct MalType_s THE_TRUE = { MALTYPE_TRUE, {0}};\nstruct MalType_s THE_FALSE = { MALTYPE_FALSE, {0}};\n\nbool is_nil(MalType val) { return val == &THE_NIL; }\nbool is_false(MalType val) { return val == &THE_FALSE; }\nbool is_true(MalType val) { return val == &THE_TRUE; }\n\ninline bool is_integer(MalType val, long* result) {\n  bool ok = val->type & MALTYPE_INTEGER;\n  if (ok) *result = val->value.mal_integer;\n  return ok;\n}\nMalType make_integer(long value) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_INTEGER, {.mal_integer=value}};\n  return mal_val;\n}\n\ninline bool is_float(MalType val, double* result) {\n  bool ok = val->type & MALTYPE_FLOAT;\n  if (ok) *result = val->value.mal_float;\n  return ok;\n}\nMalType make_float(double value) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_FLOAT, {.mal_float=value}};\n  return mal_val;\n}\n\n#define NO_HASH_YET (size_t)(-1)\n\nsize_t hash(const char* s) {\n# ifdef DEBUG_HASH\n  printf(\"HASH %s\\n\", s);\n# endif\n  size_t h = 0;\n  // 8 characters are sufficient to ensure distinct hashes for\n  // \"keyword\" and \"keyword?\".\n  for (size_t i = 0; i < 8; i++) {\n    unsigned char c = s[i];\n    if (!c) break;\n    h = h << 1 ^ c;\n#   ifdef DEBUG_HASH\n    printf(\"HASH  %c %08b %064lb\\n\", c, c, h);\n#   endif\n  }\n  return h;\n}\n\ninline const char* is_string(MalType val) {\n  return val->type & MALTYPE_STRING ? val->value.mal_string.s : NULL;\n}\nMalType make_string(const char* value) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_STRING, {.mal_string={value, NO_HASH_YET}}};\n  return mal_val;\n}\n\ninline const char* is_keyword(MalType val) {\n  return val->type & MALTYPE_KEYWORD ? val->value.mal_string.s : NULL;\n}\nMalType make_keyword(const char* value) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_KEYWORD, {.mal_string={value, NO_HASH_YET}}};\n  return mal_val;\n}\n\ninline const char* is_symbol(MalType val) {\n  return val->type & MALTYPE_SYMBOL ? val->value.mal_string.s : NULL;\n}\nMalType make_symbol(const char* value) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_SYMBOL, {.mal_string={value, NO_HASH_YET}}};\n  return mal_val;\n}\n\ninline bool is_list(MalType val, list* result) {\n  bool ok = val->type & MALTYPE_LIST;\n  if (ok) *result = val->value.mal_list.l;\n  return ok;\n}\nMalType make_list_m(list value, MalType metadata) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_LIST, {.mal_list={value, metadata}}};\n  return mal_val;\n}\ninline MalType make_list(list value) {\n  return make_list_m(value, &THE_NIL);\n}\n\ninline vector_t is_vector(MalType val) {\n  return val->type & MALTYPE_VECTOR ? val->value.mal_vector.v : NULL;\n}\nMalType make_vector_m(vector_t value, MalType metadata) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_VECTOR, {.mal_vector={value, metadata}}};\n  return mal_val;\n}\ninline MalType make_vector(vector_t value) {\n  return make_vector_m(value, &THE_NIL);\n}\n\ninline hashmap is_hashmap(MalType val) {\n  return val->type & MALTYPE_HASHMAP ? val->value.mal_hashmap.m : NULL;\n}\nMalType make_hashmap_m(hashmap value, MalType metadata) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_HASHMAP, {.mal_hashmap={value, metadata}}};\n  return mal_val;\n}\ninline MalType make_hashmap(hashmap value) {\n  return make_hashmap_m(value, &THE_NIL);\n}\n\ninline function_t is_function(MalType val) {\n  return val->type & MALTYPE_FUNCTION ? val->value.mal_function.f : NULL;\n}\nMalType make_function_m(function_t value, MalType metadata) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_FUNCTION, {.mal_function={value, metadata}}};\n  return mal_val;\n}\ninline MalType make_function(function_t value) {\n  return make_function_m(value, &THE_NIL);\n}\n\ninline MalClosure is_closure(MalType val) {\n  return val->type & MALTYPE_CLOSURE ? &val->value.mal_closure.c : NULL;\n}\nMalType make_closure_m(const Env* env, list fnstar_args, MalType metadata) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_CLOSURE, {.mal_closure={{env, fnstar_args}, metadata}}};\n  return mal_val;\n}\ninline MalType make_closure(const Env* env, list fnstar_args) {\n  return make_closure_m(env, fnstar_args, &THE_NIL);\n}\n\ninline MalClosure is_macro(MalType val) {\n  return val->type & MALTYPE_MACRO ? &val->value.mal_closure.c : NULL;\n}\nMalType make_macro(const Env* env, list fnstar_args) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_MACRO, {.mal_closure={{env, fnstar_args}, &THE_NIL}}};\n  return mal_val;\n}\n\ninline MalType* is_atom(MalType val) {\n  return val->type & MALTYPE_ATOM ? &val->value.mal_atom : NULL;\n}\nMalType make_atom(MalType value) {\n  struct MalType_s* mal_val = GC_MALLOC(sizeof(*mal_val));\n  *mal_val = (struct MalType_s){MALTYPE_ATOM, {.mal_atom=value}};\n  return mal_val;\n}\n\nMalType meta(MalType form) {\n  switch (form->type) {\n  case MALTYPE_LIST    : return form->value.mal_list    .meta;\n  case MALTYPE_VECTOR  : return form->value.mal_vector  .meta;\n  case MALTYPE_HASHMAP : return form->value.mal_hashmap .meta;\n  case MALTYPE_FUNCTION: return form->value.mal_function.meta;\n  case MALTYPE_CLOSURE : return form->value.mal_closure .meta;\n  default: return &THE_NIL;\n  }\n}\n\ninline enum mal_type_t type(MalType val) {\n  return val->type;\n}\n\ninline size_t get_hash(MalType form) {\n  assert(form->type & (MALTYPE_KEYWORD | MALTYPE_STRING | MALTYPE_SYMBOL));\n  if(form->value.mal_string.hash == NO_HASH_YET) {\n    form->value.mal_string.hash = hash(form->value.mal_string.s);\n  }\n  return form->value.mal_string.hash;\n}\n\nvoid* mal_type_value_address(MalType form) {\n  switch (form->type) {\n  case MALTYPE_NIL:\n  case MALTYPE_INTEGER:\n    return (void*)&form->value.mal_integer;\n  case MALTYPE_STRING:\n    return (void*)&form->value.mal_string.s;\n  case MALTYPE_FLOAT:\n    return (void*)&form->value.mal_integer;\n  default:\n    assert(false);\n    return NULL; // silent a warning when NDEBUG.\n  }\n}\n\nbool equal_forms(MalType first, MalType second) {\n  // Compare strings as soon as possible because EVAL, map_get and\n  // env_get call this function often.  Conclude early if the hashes\n  // do not match.\n\n  if (first->type & (MALTYPE_LIST | MALTYPE_VECTOR)) {\n    if (second->type & ~ (MALTYPE_LIST | MALTYPE_VECTOR)) return false;\n    seq_cursor c2 = seq_iter(second);\n    for (seq_cursor c1 = seq_iter(first);\n         seq_cont(first, c1);\n         c1 = seq_next(first, c1)) {\n      if (!seq_cont(second, c2)\n          || !equal_forms(seq_item(first, c1), seq_item(second, c2)))\n        return false;\n      c2 = seq_next(second, c2);\n    }\n    return !seq_cont(second, c2);\n  }\n\n  if (first->type != second->type) return false;\n\n  if (first->type & (MALTYPE_KEYWORD | MALTYPE_STRING | MALTYPE_SYMBOL)) {\n    // via get_hash because the hash may not be computed yet.\n    return get_hash(first) == get_hash(second)\n      && !strcmp(first->value.mal_string.s, second->value.mal_string.s);\n  }\n\n  if (first->type & (MALTYPE_NIL | MALTYPE_FALSE | MALTYPE_TRUE)) return true;\n\n  if (first->type == MALTYPE_INTEGER) {\n    return first->value.mal_integer == second->value.mal_integer;\n  }\n  if (first->type == MALTYPE_FLOAT) {\n    return first->value.mal_float == second->value.mal_float;\n  }\n  if (first->type == MALTYPE_HASHMAP) {\n    hashmap m1 = first->value.mal_hashmap.m;\n    hashmap m2 = second->value.mal_hashmap.m;\n    if (map_count(m1) != map_count(m2))\n      return false;\n    for (map_cursor c = map_iter(m1); map_cont(m1, c); c = map_next(m1, c)) {\n      MalType val2 = hashmap_get(m2, map_key(m1, c));\n        if (!val2 || !equal_forms(map_val(m1, c), val2))\n          return false;\n      }\n    return true;\n  }\n  return false;\n}\n\nMalType SYMBOL_AMPERSAND;\nMalType SYMBOL_CATCH;\nMalType SYMBOL_CONCAT;\nMalType SYMBOL_CONS;\nMalType SYMBOL_DEBUG_EVAL;\nMalType SYMBOL_DEF;\nMalType SYMBOL_DEFMACRO;\nMalType SYMBOL_DEREF;\nMalType SYMBOL_DO;\nMalType SYMBOL_FN;\nMalType SYMBOL_IF;\nMalType SYMBOL_LET;\nMalType SYMBOL_QUASIQUOTE;\nMalType SYMBOL_QUOTE;\nMalType SYMBOL_SPLICE_UNQUOTE;\nMalType SYMBOL_TRY;\nMalType SYMBOL_UNQUOTE;\nMalType SYMBOL_VEC;\nMalType SYMBOL_WITH_META;\n\nvoid types_init() {\n  SYMBOL_AMPERSAND      = make_symbol(\"&\");\n  SYMBOL_CATCH          = make_symbol(\"catch*\");\n  SYMBOL_CONCAT         = make_symbol(\"concat\");\n  SYMBOL_CONCAT         = make_symbol(\"concat\");\n  SYMBOL_CONS           = make_symbol(\"cons\");\n  SYMBOL_DEBUG_EVAL     = make_symbol(\"DEBUG-EVAL\");\n  SYMBOL_DEF            = make_symbol(\"def!\");\n  SYMBOL_DEFMACRO       = make_symbol(\"defmacro!\");\n  SYMBOL_DEREF          = make_symbol(\"deref\");\n  SYMBOL_DO             = make_symbol(\"do\");\n  SYMBOL_FN             = make_symbol(\"fn*\");\n  SYMBOL_IF             = make_symbol(\"if\");\n  SYMBOL_LET            = make_symbol(\"let*\");\n  SYMBOL_QUASIQUOTE     = make_symbol(\"quasiquote\");\n  SYMBOL_QUOTE          = make_symbol(\"quote\");\n  SYMBOL_SPLICE_UNQUOTE = make_symbol(\"splice-unquote\");\n  SYMBOL_TRY            = make_symbol(\"try*\");\n  SYMBOL_UNQUOTE        = make_symbol(\"unquote\");\n  SYMBOL_VEC            = make_symbol(\"vec\");\n  SYMBOL_WITH_META      = make_symbol(\"with-meta\");\n}\n\ninline MalType make_true() {\n  return &THE_TRUE;\n}\n\ninline MalType make_false() {\n  return &THE_FALSE;\n}\n\ninline MalType make_nil() {\n  return &THE_NIL;\n}\n"
  },
  {
    "path": "impls/c.2/types.h",
    "content": "#ifndef _MAL_TYPES_H\n#define _MAL_TYPES_H\n\n#include <stdbool.h>\n\n// The order must match the one in printer.c.\nenum mal_type_t {\n  MALTYPE_SYMBOL   = 1 <<  0,\n  MALTYPE_KEYWORD  = 1 <<  1,\n  MALTYPE_INTEGER  = 1 <<  2,\n  MALTYPE_FLOAT    = 1 <<  3,\n  MALTYPE_STRING   = 1 <<  4,\n  MALTYPE_TRUE     = 1 <<  5,\n  MALTYPE_FALSE    = 1 <<  6,\n  MALTYPE_NIL      = 1 <<  7,\n  MALTYPE_LIST     = 1 <<  8,\n  MALTYPE_VECTOR   = 1 <<  9,\n  MALTYPE_HASHMAP  = 1 << 10,\n  MALTYPE_FUNCTION = 1 << 11,\n  MALTYPE_CLOSURE  = 1 << 12,\n  MALTYPE_ATOM     = 1 << 13,\n  MALTYPE_MACRO    = 1 << 14,\n};\n\ntypedef struct MalType_s* MalType;\ntypedef const struct MalClosure_s* MalClosure;\ntypedef struct pair_s* list; // mutable for appends\ntypedef MalType(*function_t)(list);\ntypedef struct Env_s Env;\ntypedef const struct map* hashmap;\ntypedef const struct vector* vector_t;\n\nstruct MalClosure_s {\n\n  const Env* env;\n  list fnstar_args; // (parameters body)\n  // parameters is a list or vector of symbols\n  // If \"&\" is present, it stands right before the last symbol.\n\n};\n\nMalType make_symbol(const char* value);\nMalType make_integer(long value);\nMalType make_float(double value);\nMalType make_keyword(const char* value);\nMalType make_string(const char* value);\nMalType make_list(list value);\nMalType make_list_m(list value, MalType meta);\nMalType make_vector(vector_t value);\nMalType make_vector_m(vector_t value, MalType meta);\nMalType make_hashmap(hashmap value);\nMalType make_hashmap_m(hashmap value, MalType meta);\nMalType make_true();\nMalType make_false();\nMalType make_nil();\nMalType make_atom(MalType value);\nMalType make_function_m(function_t value, MalType meta);\nMalType make_function(function_t value);\nMalType make_closure_m(const Env* env, list fnstar_args, MalType meta);\nMalType make_closure(const Env* env, list fnstar_args);\nMalType make_macro(const Env* env, list fnstar_args);\n\n// A NULL result means that the type differs, except for lists.\nbool is_list(MalType val, list*);\nvector_t is_vector(MalType val);\nhashmap is_hashmap(MalType val);\nbool is_nil(MalType val);\nconst char* is_string(MalType val);\nbool is_false(MalType val);\nconst char* is_symbol(MalType val);\nconst char* is_keyword(MalType val);\nfunction_t is_function(MalType val);\nMalClosure is_closure(MalType val);\nMalClosure is_macro(MalType val);\nbool is_integer(MalType val, long*);\nbool is_float(MalType val, double*);\nMalType* is_atom(MalType val);\nbool is_true(MalType val);\n\n\nenum mal_type_t type(MalType);\nMalType meta(MalType);      // Returns nil for types without metadata.\nsize_t get_hash(MalType); // Crashes for types without hash.\n\n// These parts could be implemented outside types, but improve\n// readability in core, hashmap and steps.\n\n// This also improves efficiency because\n// a lost of allocations of the same symbol are avoided\n// amost symbol comparisons in EVAL will only need the precomputed hash.\nbool equal_forms(MalType, MalType);\nextern MalType SYMBOL_AMPERSAND;\nextern MalType SYMBOL_CATCH;\nextern MalType SYMBOL_CONCAT;\nextern MalType SYMBOL_CONS;\nextern MalType SYMBOL_DEBUG_EVAL;\nextern MalType SYMBOL_DEF;\nextern MalType SYMBOL_DEFMACRO;\nextern MalType SYMBOL_DEREF;\nextern MalType SYMBOL_DO;\nextern MalType SYMBOL_FN;\nextern MalType SYMBOL_IF;\nextern MalType SYMBOL_LET;\nextern MalType SYMBOL_QUASIQUOTE;\nextern MalType SYMBOL_QUOTE;\nextern MalType SYMBOL_SPLICE_UNQUOTE;\nextern MalType SYMBOL_TRY;\nextern MalType SYMBOL_UNQUOTE;\nextern MalType SYMBOL_VEC;\nextern MalType SYMBOL_WITH_META;\n\nvoid types_init();\n\n//  Evil trick for FFI.\n//  Should at least be const void*.\nvoid* mal_type_value_address(MalType);\n\n#endif\n"
  },
  {
    "path": "impls/c.2/vector.c",
    "content": "#include <assert.h>\n\n#include <gc.h>\n\n#include \"linked_list.h\"\n#include \"vector.h\"\n\nstruct vector* vector_new(size_t capacity) {\n  struct vector* v = GC_MALLOC(sizeof(*v) + capacity*sizeof(MalType));\n  v->count = 0;\n  return v;\n}\n\nvoid vector_append(size_t* capacity, struct vector** v, MalType new_item) {\n  if ((*v)->count == *capacity) {\n    // + 1 in case capacity is 0.\n    *capacity = (*capacity + 1) << 1;\n    *v = GC_REALLOC(*v, sizeof(**v) + *capacity * sizeof(MalType));\n  }\n  (*v)->nth[(*v)->count++] = new_item;\n}\n\nseq_cursor seq_iter(MalType container) {\n  list l;\n  if (is_list(container, &l)) {\n    return (seq_cursor){.l=l};\n  }\n  else {\n    assert(type(container) == MALTYPE_VECTOR);\n    return (seq_cursor){.i=0};\n  }\n}\n\nbool seq_cont(MalType container, seq_cursor position) {\n  assert(type(container) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  vector_t v;\n  if ((v = is_vector(container))) {\n    return position.i < v->count;\n  }\n  else {\n    return position.l != NULL;\n  }\n}\n\nseq_cursor seq_next(MalType container, seq_cursor position) {\n  assert(type(container) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  vector_t v;\n  if ((v = is_vector(container))) {\n    assert(position.i < v->count);\n    return (seq_cursor){.i=position.i + 1};\n  }\n  else {\n    return (seq_cursor){.l=position.l->next};\n  }\n}\n\nMalType seq_item(MalType container, seq_cursor position) {\n  assert(type(container) & (MALTYPE_LIST | MALTYPE_VECTOR));\n  vector_t v;\n  if ((v = is_vector(container))) {\n    assert(position.i < v->count);\n    return v->nth[position.i];\n  }\n  else {\n    return position.l->data;\n  }\n}\n"
  },
  {
    "path": "impls/c.2/vector.h",
    "content": "#ifndef MAL_VECTOR_H\n#define MAL_VECTOR_H\n\n#include <stdbool.h>\n\n#include \"types.h\"\n// typedef const struct vector* vector_t;\n\nstruct vector {\n  size_t count;\n  MalType nth[];\n};\n\nstruct vector* vector_new(size_t capacity);\n//  The capacity first additions cause no reallocation.\n\nvoid vector_append(size_t* capacity, struct vector** v, MalType new_item);\n\n// Convenient way to iterate either on a list or a vector.\n// The same (unmodified) container must be be provided to each\n// function during iteration.\n// It must be a list or a vector.\ntypedef union seq_cursor {\n  list l;\n  size_t i;\n} seq_cursor;\nseq_cursor seq_iter(MalType);\nbool seq_cont(MalType, seq_cursor);\nseq_cursor seq_next(MalType, seq_cursor);\nMalType seq_item(MalType, seq_cursor);\n\n#endif\n"
  },
  {
    "path": "impls/chuck/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Chuck\nRUN apt-get -y install bison gcc g++ flex\nRUN apt-get -y install libasound2-dev libsndfile1-dev\nRUN cd /tmp && curl -O https://chuck.cs.princeton.edu/release/files/chuck-1.5.2.5.tgz \\\n    && tar xvzf /tmp/chuck-1.5.2.5.tgz && cd chuck-1.5.2.5/src \\\n    && make linux-alsa && make install \\\n    && rm -r /tmp/chuck-1.5.2.5*\nRUN cd /tmp && curl -Lo chugins-chuck-1.5.2.5.tgz https://github.com/ccrma/chugins/archive/refs/tags/chuck-1.5.2.5.tar.gz \\\n    && tar xvzf /tmp/chugins-chuck-1.5.2.5.tgz && cd chugins-chuck-1.5.2.5/RegEx \\\n    && make linux && mkdir -p /usr/local/lib/chuck/1.5.2.5 \\\n    && cp RegEx.chug /usr/local/lib/chuck/1.5.2.5/RegEx.chug \\\n    && rm -r /tmp/chugins-chuck-1.5.2.5*\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/chuck/Makefile",
    "content": "all:\n\nclean:\n\n.PHONY: all clean\n"
  },
  {
    "path": "impls/chuck/chuck.md",
    "content": "- I've found a potential bug in their substring function:\n  https://github.com/ccrma/chuck/issues/55\n- later I've found one in their regex replace function, too:\n  https://github.com/ccrma/chuck/issues/60\n- this suggests there hasn't been much testing done on things\n  unrelated to audio which is not that unexpected in an audio\n  programming language, but still...\n- the manual isn't up to date, so you need to look at `VERSIONS` and\n  the examples instead, sometimes the sources, too\n- the manual only speaks of the debug syntax for printing\n  (`<<<foo>>>;` which goes to stderr), I've found a `chout` object you\n  can send strings to for outputting to stdout\n- quitting is done via `C-c` only\n- you'll want to use `--silent` to disable audio errors/processing,\n  but then the process will use 100% CPU and ignore any waiting\n- stdin handling is terrible:\n  - the manual shows a keyboard example with HID devices, but it\n    doesn't work on linux\n  - there's a \"hacked\" `ConsoleInput` class with only an example file\n    for it, it works for most of the part, but doesn't accept `C-d`\n  - the obvious alternative is printing a prompt manually, then\n    waiting for `KBHit` events and printing them, but that's rather\n    tedious as you'd have to convert the ascii numbers into chars\n    yourself and make a buffer-like thing\n  - I've also considered writing a thing sending OSC events per\n    keyboard hit and processing these in ChucK as they come in, but\n    that would most likely not work with the test harness ._.\n- the OOP system is seriously weird\n  - influenced by C++ *and* java\n  - one public class per file\n  - to export functionality, you must use a public class (and static\n    functions/variables)\n  - if you use static variables, you can't assign values to them\n    directly, you'll have to do that after the class has been defined\n  - worse, you can't even declare anything that's not a primitive, so\n    if you want to declare a reference type, use the reference\n    operator instead...\n  - no interfaces\n  - no generics (copy/paste code for all types you need!)\n  - no unions (use Object, then cast to the correct type)\n  - there is no obvious way of casting to arrays of types\n  - no private (things are public by default, public keyword is used\n    to export code)\n  - no self-references in classes (so no trees, static \"constructors\"\n    work though)\n  - no meaningful way of working with null for primitive types (mutate\n    a reference and look at the return code instead)\n  - no boxed versions of primitive types\n  - no automatic boxing/unboxing\n  - no upcasting/downcasting\n- No module system\n  - `Machine.add(file)` is the only mechanism available from code (no\n    read all file contents and eval), but if you use it, it defers\n    loading the files until the file it's used in, rendering it\n    useless\n  - Therefore the only way to make use of it is writing a file that\n    only consists of these instructions\n  - The only practical alternative is specifying all files you need\n    loaded in the right order when starting chuck\n  - That's why I wrote a runner script extracting `// @import file.ck`\n    lines (hello JS!) and running chuck with them\n- No real exception system\n  - The VM is able to throw exceptions (out of bounds, nullpointer),\n    but you can't do anything about them and only get a hint what kind\n    of operation caused it (no stacktrace or anything)\n  - No user-definable exceptions, no mechanism to catch or throw them\n    (other than intentionally doing something illegal)\n  - This means that you should use C-style error checking by\n    converting the potentially erroneous functions into returning a\n    status code and mutating a reference passed to them as argument\n    which is highly weird in a otherwise Java-like language\n  - An alternative is defining an error object (which belongs to the\n    same supertype as the other legal return values) and checking its\n    type by inspecting the user-tracked type field\n- No function pointers/functors/closures\n  - This is a bit unexpected as if you leave away the parentheses\n    holding the argument list and debug print a function, you'll see\n    it being recognized as a function, yet you can't store it anywhere\n    for passing it around\n  - This is not quite right as you can store it in an `Object`, just\n    not call it in any way or cast it to a function type\n  - So you get to implement functors and closures yourself...\n  - A functor is a class with a call method taking an argument list\n    and executing the code of the function you intend to pass around\n  - To use it, store an instance of its class somewhere, then use its\n    call method with an argument list\n  - Closures can be implemented with a data structure holding a\n    snapshot of the current environment, the parameter list and AST,\n    the last two being a way of representing an anonymous function.\n- Other oddities\n  - strict distinction between assigning values and references with\n    two separate operators for them (`<<` for array append doesn't\n    seem to care though)\n  - strings are supposedly reference types, yet you can assign them\n    with the regular operator...\n  - `<<` on an `type[]` gives you a weird error as you need to use an\n    `type[0]` (and a `type[]` is merely a reference...)\n  - The compiler will find lots of mistakes for you, but cannot figure\n    out code branches not returning anything which means that return\n    type violations will blow up in your face unless there's a\n    reasonable default value (null for `Object` isn't, 0 for `int` and\n    \"\" for `string` is)\n  - If you abuse the type system too much, chances are you get a\n    segfault or assert instead of an exception...\n  - Debug print shows the object and its type if you pass one\n    argument, if you pass more than one, it prints the concatenation\n    of their representations instead, so it's a bit hard to make out\n    what is a debug print and what isn't\n  - there are no hash maps, just the possibility to use a string key\n    on an array for storing and fetching contents (like in PHP, eww)\n    and no way of retrieving keys/values or even iterating over these\n  - I think I've spotted a weird scoping bug that prefers a member\n    variable over a local variable after nesting scopes, therefore I\n    consider the language to not implement proper lexical scoping\n  - another proof of it is declaring variables in consequent if-blocks\n    as that gives you an error instead of being permitted as they\n    should be in different local scopes...\n"
  },
  {
    "path": "impls/chuck/core.ck",
    "content": "public class Core\n{\n    static string names[];\n    static MalSubr ns[];\n}\n\n[\"+\", \"-\", \"*\", \"/\",\n \"list\", \"list?\", \"empty?\", \"count\",\n \"=\", \"<\", \"<=\", \">\", \">=\",\n \"pr-str\", \"str\", \"prn\", \"println\",\n \"read-string\", \"slurp\",\n \"atom\", \"atom?\", \"deref\", \"reset!\", \"swap!\",\n \"vec\", \"cons\", \"concat\",\n \"nth\", \"first\", \"rest\",\n \"throw\",\n \"apply\", \"map\",\n \"nil?\", \"true?\", \"false?\", \"number?\", \"symbol?\", \"keyword?\", \"vector?\", \"map?\",\n \"symbol\", \"keyword\", \"vector\", \"hash-map\",\n \"assoc\", \"dissoc\", \"get\", \"contains?\", \"keys\", \"vals\",\n \"sequential?\", \"fn?\", \"macro?\",\n \"readline\", \"meta\", \"with-meta\",\n \"time-ms\", \"conj\", \"string?\", \"seq\"] @=> Core.names;\nMalSubr ns[0] @=> Core.ns;\n\nnew MalAdd @=> Core.ns[\"+\"];\nnew MalSub @=> Core.ns[\"-\"];\nnew MalMul @=> Core.ns[\"*\"];\nnew MalDiv @=> Core.ns[\"/\"];\n\nnew MalListify @=> Core.ns[\"list\"];\nnew MalIsList @=> Core.ns[\"list?\"];\nnew MalIsEmpty @=> Core.ns[\"empty?\"];\nnew MalCount @=> Core.ns[\"count\"];\n\nnew MalEqual @=> Core.ns[\"=\"];\nnew MalLess @=> Core.ns[\"<\"];\nnew MalLessEqual @=> Core.ns[\"<=\"];\nnew MalGreater @=> Core.ns[\">\"];\nnew MalGreaterEqual @=> Core.ns[\">=\"];\n\nnew MalPrStr @=> Core.ns[\"pr-str\"];\nnew MalStr @=> Core.ns[\"str\"];\nnew MalPrn @=> Core.ns[\"prn\"];\nnew MalPrintln @=> Core.ns[\"println\"];\n\nnew MalReadStr @=> Core.ns[\"read-string\"];\nnew MalSlurp @=> Core.ns[\"slurp\"];\n\nnew MalAtomify @=> Core.ns[\"atom\"];\nnew MalIsAtom @=> Core.ns[\"atom?\"];\nnew MalDeref @=> Core.ns[\"deref\"];\nnew MalDoReset @=> Core.ns[\"reset!\"];\nnew MalDoSwap @=> Core.ns[\"swap!\"];\n\nnew MalVec @=> Core.ns[\"vec\"];\nnew MalCons @=> Core.ns[\"cons\"];\nnew MalConcat @=> Core.ns[\"concat\"];\n\nnew MalNth @=> Core.ns[\"nth\"];\nnew MalFirst @=> Core.ns[\"first\"];\nnew MalRest @=> Core.ns[\"rest\"];\n\nnew MalThrow @=> Core.ns[\"throw\"];\n\nnew MalApply @=> Core.ns[\"apply\"];\nnew MalMap @=> Core.ns[\"map\"];\n\nnew MalIsNil @=> Core.ns[\"nil?\"];\nnew MalIsTrue @=> Core.ns[\"true?\"];\nnew MalIsFalse @=> Core.ns[\"false?\"];\nnew MalIsNumber @=> Core.ns[\"number?\"];\nnew MalIsSymbol @=> Core.ns[\"symbol?\"];\nnew MalIsKeyword @=> Core.ns[\"keyword?\"];\nnew MalIsVector @=> Core.ns[\"vector?\"];\nnew MalIsHashMap @=> Core.ns[\"map?\"];\n\nnew MalSymbolify @=> Core.ns[\"symbol\"];\nnew MalKeywordify @=> Core.ns[\"keyword\"];\nnew MalVectorify @=> Core.ns[\"vector\"];\nnew MalHashMapify @=> Core.ns[\"hash-map\"];\n\nnew MalAssoc @=> Core.ns[\"assoc\"];\nnew MalDissoc @=> Core.ns[\"dissoc\"];\nnew MalGet @=> Core.ns[\"get\"];\nnew MalIsContains @=> Core.ns[\"contains?\"];\nnew MalKeys @=> Core.ns[\"keys\"];\nnew MalVals @=> Core.ns[\"vals\"];\n\nnew MalSequential @=> Core.ns[\"sequential?\"];\nnew MalIsFn @=> Core.ns[\"fn?\"];\nnew MalIsMacro @=> Core.ns[\"macro?\"];\n\nnew MalReadline @=> Core.ns[\"readline\"];\nnew MalMeta @=> Core.ns[\"meta\"];\nnew MalWithMeta @=> Core.ns[\"with-meta\"];\n\nnew MalTimeMs @=> Core.ns[\"time-ms\"];\nnew MalConj @=> Core.ns[\"conj\"];\nnew MalIsString @=> Core.ns[\"string?\"];\nnew MalSeq @=> Core.ns[\"seq\"];\n"
  },
  {
    "path": "impls/chuck/env.ck",
    "content": "public class Env extends MalObject\n{\n    MalObject outer; // this would ideally be Env, but isn't supported\n    MalObject data[0];\n\n    fun void init(MalObject env)\n    {\n        env @=> outer;\n    }\n\n    fun void init(MalObject env, string binds[], MalObject exprs[])\n    {\n        env @=> outer;\n\n        for( 0 => int i; i < binds.size(); i++ )\n        {\n            binds[i] => string bind;\n\n            if( bind == \"&\" )\n            {\n                MalObject.slice(exprs, i) @=> MalObject rest_binds[];\n                MalList.create(rest_binds) @=> data[binds[i+1]];\n                break;\n            }\n            else\n            {\n                exprs[i] @=> data[bind];\n            }\n        }\n    }\n\n    fun static Env create(MalObject env)\n    {\n        Env e;\n        e.init(env);\n        return e;\n    }\n\n    fun static Env create(MalObject env, string binds[], MalObject exprs[])\n    {\n        Env e;\n        e.init(env, binds, exprs);\n        return e;\n    }\n\n    fun MalObject clone()\n    {\n        Env value;\n\n        this.outer @=> value.outer;\n        this.data @=> value.data;\n\n        return value;\n    }\n\n    fun void set(string key, MalObject value)\n    {\n        value @=> data[key];\n    }\n\n    fun MalObject find(string key)\n    {\n        data[key] @=> MalObject value;\n\n        if( value != null )\n        {\n            return value;\n        }\n        else if( outer != null )\n        {\n            return (outer$Env).find(key);\n        }\n        else\n        {\n            return null;\n        }\n    }\n\n    fun MalObject get(string key)\n    {\n        find(key) @=> MalObject value;\n\n        if( value != null )\n        {\n            return value;\n        }\n        else\n        {\n            return MalError.create(\"'\" + key + \"' not found\");\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/func.ck",
    "content": "public class Func extends MalObject\n{\n    \"func\" => type;\n    Env env;\n    string args[];\n    MalObject ast;\n    int isMacro;\n\n    fun void init(Env env, string args[], MalObject ast)\n    {\n        env @=> this.env;\n        args @=> this.args;\n        ast @=> this.ast;\n    }\n\n    fun static Func create(Env env, string args[], MalObject ast)\n    {\n        Func func;\n        func.init(env, args, ast);\n        return func;\n    }\n\n    fun MalObject clone()\n    {\n        Func value;\n\n        this.type => value.type;\n        this.env @=> value.env;\n        this.args @=> value.args;\n        this.ast @=> value.ast;\n        this.isMacro @=> value.isMacro;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/notes.md",
    "content": "# Step 1\n\n- What if I don't have an OOP language?\n- types.qx could be more prominently mentioned...\n- A table with all types and suggested object names would be hugely\n  useful\n- Same for a list of all errors and their messages\n- Mention return types and argument types consistently\n- More on int/float and their grammar (int is mentioned implicitly in\n  the ASCII art, nothing on signs or bases or their lack of)\n- Note that a string must be parsed for the `print_readably` thing to\n  work and mention how one could do that (like, by using a `read` or\n  `eval`-like thing or alternatively, chopping off the surrounding\n  quotes and doing the inverse transformation of the printing)\n- How is an atom printed?\n\n# Step 2\n\n- What if my language doesn't support lambdas, let alone passing\n  around named functions? Ideally write something about\n  implementing/using functors/delegates or replacing that namespace\n  with a big switch as with VHDL.  Another problem is that if you\n  choose a different solution in step 4, step 2 could end up no longer\n  functional...\n- What kind of error (read: what message?) is raised when no value can\n  be looked up for the symbol?  Is it arbitrary?  Do I need to extend\n  my error handling to allow for format strings?\n- It would be worth a mention that you should extend the printer to\n  handle \"native\" functions (or in oldtimey terms, subrs)\n\n# Step 3\n\n- You should modify both eval_ast *and* EVAL\n- Suggest the trick with destructuring the AST into `a0`, `a1`,\n  etc. variables for easier access.  Perhaps this can be used to clear\n  up the general language used with AST manipulation (like, first\n  parameter and second list element)?\n- What does def! return?  Emacs Lisp for instance returns the symbol\n  whereas the tests suggest the value should be returned instead...\n\n# Step 4\n\n- \"Implement the strings functions\"\n- The \"no closures\" paragraph isn't quite clear.  Asides from that, do\n  native functions don't really need to be wrapped the same way as the\n  `fn*` objects, just introduce another type (like, a Subr and a Func\n  type) and do a check before applying the arguments to it\n- Why does the guide say that the first argument of `count` can be\n  treated as list, yet there's a test performing `(count nil)` and\n  expecting zero as result?\n- Does it make sense to compare, say, atoms in `=`?\n\n# Step 5\n\n- \"This is especially important in Lisp languages because they tend to\n  prefer using recursion instead of iteration for control structures.\"\n  <- I'd argue it's less of a lisp thing (see everything else related\n  to CL) and more a thing functional programming proponents have\n  considered more elegant than introducing iteration constructs (see\n  haskell, ocaml, erlang)\n- It's not really clear that the TCO change for `let*` involves the\n  form you'd normally pass to `EVAL` to become the new `ast`.  I had to\n  reread this a few more times to understand that the \"second `ast`\"\n  is actually its third argument...\n- Where did the check for `do` not being broken by TCO go?\n- What's the deal with the `quux/tests/step5_tco.qx` file?\n\n# Step 6\n\n- \"The closure calls the your EVAL function […].\"\n- I still don't have any closures.  How the heck do I implement\n  `eval`?  What about `swap!`?\n- It would be useful to mention that `swap!` sort of requires\n  implementing `apply` first...\n\n# Step 7\n\n- Why the scare quotes for splicing?\n- \"Before implementing the quoting forms, you will need to implement\n  some supporting functions in the core namespace:\" should be one list\n  item\n- \"this function takes a list as its second parameter and returns a\n  new list that has the first argument prepended to it.\" reads backwards\n- The quasiquote paragraph is hard to read\n- It's rather confusing to refer to the argument of `ast` and to an\n  `ast` parameter, perhaps name the latter a form?\n- What could also help would be a visualization of the four\n  conditionals:\n  - \\`42, \\`()\n  - \\`~foo\n  - \\`(~@foo) and more\n  - \\`(42 ~@foo) and everything else\n- Mal/mal is inconsistently capitalized\n- \"Expand the conditional with reader `read_form` function to add the\n  following four cases\" is again weird, better refer to the\n  `read_form` function in reader.qx\n- \"concat should support concatenation of lists, vectors, or a mix or\n  both.\" <- \"or a mix or both\" is redundant\n\n# Step 8\n\n- \"In the previous step, quoting enabled some simple manipulation [of]\n  data structures\"\n- The macroexpand function step refers to call/apply, it's unclear how\n  to proceed if you don't have such a thing\n- How should the exception for invalid `nth` access look like?  Also,\n  why is it an exception and not an error like with the reader?\n- How can `first` take a list (or vector), but work on `nil`?\n- The description of `rest` is inconsistent with the tests\n- \"In the main program, use the rep function to define two new control\n  structures macros.\"\n- Why does the definition of `cond` use `throw` although it's only\n  introduced in the next chapter?\n\n# Step 9\n\n- It's not really clear that you really just have a `try*` special\n  form, with `catch*` merely existing inside it...\n- Another thing to clarify is that the exception value is a string\n  containing the message you'd see (unless you're using `throw`)\n- Generally, it would be better to explain the general exception\n  handling mechanism (with some examples), then showing how one\n  implements it for both languages with and without exceptions\n- Another way than using a global variable is introducing an error\n  type next to the other MAL types and checking whether something a\n  function returned is one, although the hint about returning one at\n  each use of `EVAL` still stands...\n- Shouldn't either trick be mentioned at the beginning, simply because\n  you'll need it in a language without exceptions to do error handling?\n- Why this bizarre treatment for `keyword`?  Why is there no test for\n  it?\n- Is there a test for whether hash maps deduplicate identical keys\n  when using `hash-map` or `assoc`?\n- What exactly are keys the specification for `dissoc`, `get` and\n  `contains?` are speaking of?  Can I assume these are either strings\n  or keywords?\n- Why is it not documented that `get` may take `nil` instead of a map?\n- Perhaps it's worth adding more tests involving symbols to ensure\n  that functions using apply internally don't evaluate their args?\n\n# Step A\n\n- \"Add meta-data support to mal functions.\" <- Shouldn't you mention\n  that this involves implementing `with-meta` and `meta`?\n- \"TODO. Should be separate from the function macro flag.\" <- Why is\n  this even related?\n- It would be worth to mention that `with-meta` shall clone its\n  argument to avoid one of the more sneaky test failure reasons\n- \"The value of this entry should be a mal string containing the name\n  of the current implementation.\"\n- \"When the REPL starts up (as opposed to when it is called with a\n  script and/or arguments), call the rep function with this string to\n  print a startup header: `\"(println (str \\\"Mal\n  [\\\" *host-language* \\\"]\\\"))\".`\" <- proof that you better quote these\n  because the asterisks just disappear...\n"
  },
  {
    "path": "impls/chuck/printer.ck",
    "content": "public class Printer\n{\n    fun static string pr_str(MalObject m, int print_readably)\n    {\n        m.type => string type;\n\n        if( type == \"true\" || type == \"false\" || type == \"nil\" )\n        {\n            return type;\n        }\n        else if( type == \"int\" )\n        {\n            return Std.itoa(m.intValue);\n        }\n        else if( type == \"string\" )\n        {\n            if( print_readably )\n            {\n                return String.repr(m.stringValue);\n            }\n            else\n            {\n                return m.stringValue;\n            }\n        }\n        else if( type == \"symbol\" )\n        {\n            return m.stringValue;\n        }\n        else if( type == \"keyword\" )\n        {\n            return \":\" + m.stringValue;\n        }\n        else if( type == \"atom\" )\n        {\n            return \"(atom \" + pr_str(m.malObjectValue(), print_readably) + \")\";\n        }\n        else if( type == \"subr\" )\n        {\n            return \"#<Subr>\";\n        }\n        else if( type == \"func\" )\n        {\n            return \"#<Func>\";\n        }\n        else if( type == \"list\" )\n        {\n            return pr_list(m.malObjectValues(), print_readably, \"(\", \")\");\n        }\n        else if( type == \"vector\" )\n        {\n            return pr_list(m.malObjectValues(), print_readably, \"[\", \"]\");\n        }\n        else if( type == \"hashmap\" )\n        {\n            return pr_list(m.malObjectValues(), print_readably, \"{\", \"}\");\n        }\n        else\n        {\n            return \"Unknown type\";\n        }\n    }\n\n    fun static string pr_list(MalObject m[], int print_readably, string start, string end)\n    {\n        string parts[m.size()];\n\n        for( 0 => int i; i < m.size(); i++ )\n        {\n            pr_str(m[i], print_readably) => parts[i];\n        }\n\n        return start + String.join(parts, \" \") + end;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/reader.ck",
    "content": "public class Reader\n{\n    0 => int position;\n    string tokens[];\n\n    fun string peek()\n    {\n        return tokens[position];\n    }\n\n    fun string next()\n    {\n        return tokens[position++];\n    }\n\n    fun static string[] tokenizer(string input)\n    {\n        \"^[ \\n,]*(~@|[][{}()'`~^@]|\\\"(\\\\\\\\.|[^\\\\\\\"])*\\\"|;[^\\n]*|[^][ \\n{}()^~@'`,;\\\"]*)\" => string tokenRe;\n        \"^([ \\n,]*|;[^\\n]*)$\" => string blankRe;\n\n        string tokens[0];\n\n        while( true )\n        {\n            string matches[1];\n            RegEx.match(tokenRe, input, matches);\n            matches[1] => string token;\n\n            if( token.length() == 0 && !RegEx.match(blankRe, input) )\n            {\n                tokens << input;\n                break;\n            }\n\n            if( !RegEx.match(blankRe, token) )\n            {\n                tokens << token;\n            }\n\n            matches[0].length() => int tokenStart;\n            String.slice(input, tokenStart) => input;\n\n            if( input.length() == 0 )\n            {\n                break;\n            }\n        }\n\n        return tokens;\n    }\n\n    fun static MalObject read_str(string input)\n    {\n        Reader reader;\n        tokenizer(input) @=> reader.tokens;\n\n        if( reader.tokens.size() == 0 )\n        {\n            return MalError.create(\"empty input\");\n        }\n        else\n        {\n            return read_form(reader);\n        }\n    }\n\n    fun static MalObject read_form(Reader reader)\n    {\n        reader.peek() => string token;\n        if( token == \"(\" )\n        {\n            return read_list(reader, \"(\", \")\");\n        }\n        else if( token == \"[\" )\n        {\n            return read_list(reader, \"[\", \"]\");\n        }\n        else if( token == \"{\" )\n        {\n            return read_list(reader, \"{\", \"}\");\n        }\n        else if( token == \")\" || token == \"]\" || token == \"}\" )\n        {\n            return MalError.create(\"unexpected '\" + token + \"'\");\n        }\n        else if( token == \"'\" )\n        {\n            return read_simple_reader_macro(reader, \"quote\");\n        }\n        else if( token == \"`\" )\n        {\n            return read_simple_reader_macro(reader, \"quasiquote\");\n        }\n        else if( token == \"~\" )\n        {\n            return read_simple_reader_macro(reader, \"unquote\");\n        }\n        else if( token == \"~@\" )\n        {\n            return read_simple_reader_macro(reader, \"splice-unquote\");\n        }\n        else if( token == \"@\" )\n        {\n            return read_simple_reader_macro(reader, \"deref\");\n        }\n        else if( token == \"^\" )\n        {\n            return read_meta_reader_macro(reader);\n        }\n        else\n        {\n            return read_atom(reader);\n        }\n    }\n\n    fun static MalObject read_list(Reader reader, string start, string end)\n    {\n        MalObject items[0];\n\n        reader.next(); // discard list start token\n\n        while( true )\n        {\n            // HACK: avoid checking for reader.peek() returning null\n            // (as doing that directly isn't possible and too\n            // bothersome to do indirectly)\n            if( reader.position == reader.tokens.size() )\n            {\n                return MalError.create(\"expected '\" + end + \"', got EOF\");\n            }\n\n            if( reader.peek() == end )\n            {\n                break;\n            }\n\n            read_form(reader) @=> MalObject item;\n\n            if( item.type == \"error\" )\n            {\n                return item;\n            }\n            else\n            {\n                items << item;\n            }\n        }\n\n        reader.next(); // discard list end token\n\n        if( start == \"(\" )\n        {\n            return MalList.create(items);\n        }\n        else if( start == \"[\" )\n        {\n            return MalVector.create(items);\n        }\n        else if( start == \"{\" )\n        {\n            return MalHashMap.create(items);\n        }\n        else\n        {\n            Util.panic(\"Programmer error (failed to specify correct start token)\");\n            return null;\n        }\n    }\n\n    fun static MalObject read_atom(Reader reader)\n    {\n        \"^[+-]?[0-9]+$\" => string intRe;\n        \"^\\\"(\\\\\\\\.|[^\\\\\\\"])*\\\"$\" => string stringRe;\n\n        reader.next() => string token;\n\n        if( token == \"true\" )\n        {\n            return Constants.TRUE;\n        }\n        else if( token == \"false\" )\n        {\n            return Constants.FALSE;\n        }\n        else if( token == \"nil\" )\n        {\n            return Constants.NIL;\n        }\n        else if( RegEx.match(intRe, token) )\n        {\n            return MalInt.create(Std.atoi(token));\n        }\n        else if( token.substring(0, 1) == \"\\\"\" )\n        {\n            if( RegEx.match(stringRe, token) )\n            {\n                return MalString.create(String.parse(token));\n            }\n            else\n            {\n                return MalError.create(\"expected '\\\"', got EOF\");\n            }\n        }\n        else if( token.substring(0, 1) == \":\" )\n        {\n            return MalKeyword.create(String.slice(token, 1));\n        }\n        else\n        {\n            return MalSymbol.create(token);\n        }\n    }\n\n    fun static MalObject read_simple_reader_macro(Reader reader, string symbol)\n    {\n        reader.next(); // discard reader macro token\n\n        read_form(reader) @=> MalObject form;\n        if( form.type == \"error\" )\n        {\n            return form;\n        }\n\n        return MalList.create([MalSymbol.create(symbol), form]);\n    }\n\n    fun static MalObject read_meta_reader_macro(Reader reader)\n    {\n        reader.next(); // discard reader macro token\n\n        read_form(reader) @=> MalObject meta;\n        if( meta.type == \"error\" )\n        {\n            return meta;\n        }\n\n        read_form(reader) @=> MalObject form;\n        if( form.type == \"error\" )\n        {\n            return meta;\n        }\n\n        return MalList.create([MalSymbol.create(\"with-meta\"), form, meta]);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/readline.ck",
    "content": "public class Readline\n{\n    fun static string readline(string prompt)\n    {\n        int done;\n        string input;\n        KBHit kb;\n        int char;\n        string repr;\n\n        [\"NUL\", \"SOH\", \"STX\", \"ETX\", \"EOT\", \"ENQ\", \"ACK\", \"BEL\",\n         \"BS\", \"HT\", \"LF\", \"VT\", \"FF\", \"CR\", \"SO\", \"SI\",\n         \"DLE\", \"DC1\", \"DC2\", \"DC3\", \"DC4\", \"NAK\", \"SYN\", \"ETB\",\n         \"CAN\", \"EM\", \"SUB\", \"ESC\", \"FS\", \"GS\", \"RS\", \"US\",\n         \" \", \"!\", \"\\\"\", \"#\", \"$\", \"%\", \"&\", \"'\",\n         \"(\", \")\", \"*\", \"+\", \",\", \"-\", \".\", \"/\",\n         \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\",\n         \"8\", \"9\", \":\", \";\", \"<\", \"=\", \">\", \"?\",\n         \"@\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\",\n         \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\",\n         \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\",\n         \"X\", \"Y\", \"Z\", \"[\", \"\\\\\", \"]\", \"^\", \"_\",\n         \"`\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\",\n         \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\",\n         \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\",\n         \"x\", \"y\", \"z\", \"{\", \"|\", \"}\", \"~\", \"DEL\"] @=> string asciiTable[];\n\n        chout <= prompt;\n        chout.flush();\n\n        while( !done )\n        {\n            kb => now;\n\n            while( kb.more() && !done )\n            {\n                kb.getchar() => char;\n                asciiTable[char] => repr;\n\n                if( repr == \"EOT\" || repr == \"LF\" || repr == \"CR\" )\n                {\n                    true => done;\n                }\n                else if( repr == \"DEL\" && Std.getenv(\"TERM\") != \"dumb\")\n                {\n                    if( input.length() > 0)\n                    {\n                        chout <= \"\\033[1D\\033[0K\";\n                        chout.flush();\n                        input.substring(0, input.length()-1) => input;\n                    }\n                }\n                else\n                {\n                    chout <= repr;\n                    chout.flush();\n                    repr +=> input;\n                }\n            }\n        }\n\n        chout <= \"\\n\";\n\n        if( repr == \"EOT\" )\n        {\n            return null;\n        }\n\n        return input;\n    }\n}\n\n"
  },
  {
    "path": "impls/chuck/run",
    "content": "#!/usr/bin/env bash\nregex_chugin=${REGEX_CHUGIN:-/usr/local/lib/chuck/1.5.2.5/RegEx.chug}\nif [[ ! -f \"$regex_chugin\" ]]; then\n    echo \"Set \\$REGEX_CHUGIN to the absolute path of RegEx.chug\"; exit 1\nfi\n\nimports=$(grep \"^ *// *@import\" \"$(dirname $0)/${STEP:-stepA_mal}.ck\" | awk '{print $3}')\nimports=$(for i in ${imports}; do ls $(dirname $0)/${i}; done)\nold_IFS=\"${IFS}\"; IFS=$'\\a'; export CHUCK_ARGS=\"${*}\"; IFS=\"${old_IFS}\"\n\nexec chuck --caution-to-the-wind --silent --chugin:\"$regex_chugin\" ${imports} $(dirname $-1)/${STEP:-stepA_mal}.ck\n"
  },
  {
    "path": "impls/chuck/step0_repl.ck",
    "content": "// @import readline.ck\n\nfun string READ(string input)\n{\n    return input;\n}\n\nfun string EVAL(string input)\n{\n    return input;\n}\n\nfun string PRINT(string input)\n{\n    return input;\n}\n\nfun string rep(string input)\n{\n    return input => READ => EVAL => PRINT;\n}\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            chout <= rep(input) + \"\\n\";\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nmain();\n"
  },
  {
    "path": "impls/chuck/step1_read_print.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun MalObject EVAL(MalObject m)\n{\n    return m;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n    else\n    {\n        return PRINT(EVAL(m));\n    }\n}\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nmain();\n"
  },
  {
    "path": "impls/chuck/step2_eval.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun MalObject EVAL(MalObject m, MalSubr env[])\n{\n\n    //  Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n\n    if( m.type == \"symbol\" )\n    {\n        m.stringValue => string symbol;\n        env[symbol] @=> MalSubr subr;\n\n        if( subr == null )\n        {\n            return MalError.create(\"'\" + symbol + \"' not found\");\n        }\n        else\n        {\n            return subr;\n        }\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n\n        MalObject args[ast.size() - 1];\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            EVAL(ast[i + 1], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> args[i];\n        }\n        if( first.type == \"subr\" )\n        {\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else\n        {\n            Util.panic(\"Programmer error: cannot apply\");\n            return null;\n        }\n    }\n    else\n    {\n        return m;\n    }\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nMalSubr repl_env[0];\nnew MalAdd @=> repl_env[\"+\"];\nnew MalSub @=> repl_env[\"-\"];\nnew MalMul @=> repl_env[\"*\"];\nnew MalDiv @=> repl_env[\"/\"];\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nmain();\n"
  },
  {
    "path": "impls/chuck/step3_env.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                return EVAL(ast[2], let_env);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n\n        MalObject args[ast.size() - 1];\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            EVAL(ast[i + 1], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> args[i];\n        }\n        if( first.type == \"subr\" )\n        {\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else\n        {\n            Util.panic(\"Programmer error: cannot apply\");\n            return null;\n        }\n    }\n    else\n    {\n        return m;\n    }\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nrepl_env.set(\"+\", new MalAdd);\nrepl_env.set(\"-\", new MalSub);\nrepl_env.set(\"*\", new MalMul);\nrepl_env.set(\"/\", new MalDiv);\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nmain();\n"
  },
  {
    "path": "impls/chuck/step4_if_fn_do.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                return EVAL(ast[2], let_env);\n            }\n            else if( a0 == \"do\" )\n            {\n              MalObject value;\n              for( 1 => int i; i < ast.size(); i++ )\n              {\n                EVAL(ast[i], env) @=> value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n              return value;\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    return EVAL(ast[2], env);\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        return EVAL(ast[3], env);\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n\n        MalObject args[ast.size() - 1];\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            EVAL(ast[i + 1], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> args[i];\n        }\n        if( first.type == \"subr\" )\n        {\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            return EVAL(func.ast, eval_env);\n        }\n        else\n        {\n            Util.panic(\"Programmer error: cannot apply\");\n            return null;\n        }\n    }\n    else\n    {\n        return m;\n    }\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nmain();\n"
  },
  {
    "path": "impls/chuck/step5_tco.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n  while( true )\n  {\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                let_env @=> env;\n                ast[2] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"do\" )\n            {\n              for( 1 => int i; i < ast.size() - 1; i++ )\n              {\n                EVAL(ast[i], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n\n                // HACK: this assumes do gets at least one argument...\n                ast[ast.size()-1] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    ast[2] @=> m;\n                    continue; // TCO\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        ast[3] @=> m;\n                        continue; // TCO\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n\n        MalObject args[ast.size() - 1];\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            EVAL(ast[i + 1], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> args[i];\n        }\n        if( first.type == \"subr\" )\n        {\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            eval_env @=> env;\n            func.ast @=> m;\n            continue; // TCO\n        }\n    }\n    else\n    {\n        return m;\n    }\n  }\n  Util.panic(\"Programmer error: TCO loop left incorrectly\");\n  return null;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nmain();\n"
  },
  {
    "path": "impls/chuck/step6_file.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n  while( true )\n  {\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                let_env @=> env;\n                ast[2] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"do\" )\n            {\n              for( 1 => int i; i < ast.size() - 1; i++ )\n              {\n                EVAL(ast[i], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n\n                // HACK: this assumes do gets at least one argument...\n                ast[ast.size()-1] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    ast[2] @=> m;\n                    continue; // TCO\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        ast[3] @=> m;\n                        continue; // TCO\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n\n        MalObject args[ast.size() - 1];\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            EVAL(ast[i + 1], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> args[i];\n        }\n        if( first.type == \"subr\" )\n        {\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            eval_env @=> env;\n            func.ast @=> m;\n            continue; // TCO\n        }\n    }\n    else\n    {\n        return m;\n    }\n  }\n  Util.panic(\"Programmer error: TCO loop left incorrectly\");\n  return null;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\n// HACK, HACK, HACK\nclass MalEval extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject m;\n        return EVAL(args[0], repl_env);\n    }\n\n    fun MalObject apply(MalObject f, MalObject args[])\n    {\n        if( f.type == \"subr\" )\n        {\n            return (f$MalSubr).call(args);\n        }\n        else // f.type == \"func\"\n        {\n            f$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            return EVAL(func.ast, eval_env);\n        }\n    }\n}\n\nnew MalEval @=> MalEval eval;\nrepl_env.set(\"eval\", new MalEval);\neval @=> (repl_env.get(\"swap!\")$MalSubr).eval;\n\nfun MalObject[] MalArgv(string args[])\n{\n    MalObject values[0];\n\n    for( 1 => int i; i < args.size(); i++ )\n    {\n        values << MalString.create(args[i]);\n    }\n\n    return values;\n}\n\n// NOTE: normally I'd use \\0, but strings are null-terminated...\nString.split(Std.getenv(\"CHUCK_ARGS\"), \"\\a\") @=> string args[];\nrepl_env.set(\"*ARGV*\", MalList.create(MalArgv(args)));\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nif( args.size() > 0 )\n{\n    args[0] => string filename;\n    rep(\"(load-file \\\"\" + filename + \"\\\")\");\n}\nelse\n{\n    main();\n}\n"
  },
  {
    "path": "impls/chuck/step7_quote.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun int startsWith(MalObject a[], string sym)\n{\n    if (a.size() != 2)\n    {\n        return false;\n    }\n\n    a[0] @=> MalObject a0;\n    return a0.type == \"symbol\" && a0.stringValue == sym;\n}\n\nfun MalList qqLoop(MalObject elt, MalList acc)\n{\n    if( elt.type == \"list\" )\n    {\n        elt.malObjectValues() @=> MalObject ast[];\n\n        if( startsWith(ast, \"splice-unquote\") )\n        {\n            return MalList.create([MalSymbol.create(\"concat\"), ast[1], acc]);\n        }\n    }\n    return MalList.create([MalSymbol.create(\"cons\"), quasiquote(elt), acc]);\n}\n\nfun MalList qqFoldr(MalObject a[])\n{\n    MalObject empty[0];  //  empty, but typed\n    MalList.create(empty) @=> MalList acc;\n\n    for( a.size() - 1 => int i; 0 <= i; i-- )\n    {\n        qqLoop(a[i], acc) @=> acc;\n    }\n\n    return acc;\n}\n\nfun MalObject quasiquote(MalObject ast)\n{\n    ast.type => string type;\n    if (type == \"list\") {\n        ast.malObjectValues() @=> MalObject a[];\n        if (startsWith(a, \"unquote\"))\n        {\n            return a[1];\n        }\n        return qqFoldr(a);\n    }\n\n    if (type == \"vector\")\n    {\n        return MalList.create([MalSymbol.create(\"vec\"), qqFoldr(ast.malObjectValues())]);\n    }\n\n    if (type == \"symbol\" || type == \"hashmap\")\n    {\n        return MalList.create([MalSymbol.create(\"quote\"), ast]);\n    }\n\n    return ast;\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n  while( true )\n  {\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                let_env @=> env;\n                ast[2] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"quote\" )\n            {\n                return ast[1];\n            }\n            else if( a0 == \"quasiquote\" )\n            {\n                quasiquote(ast[1]) @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"do\" )\n            {\n              for( 1 => int i; i < ast.size() - 1; i++ )\n              {\n                EVAL(ast[i], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n\n                // HACK: this assumes do gets at least one argument...\n                ast[ast.size()-1] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    ast[2] @=> m;\n                    continue; // TCO\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        ast[3] @=> m;\n                        continue; // TCO\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n\n        MalObject args[ast.size() - 1];\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            EVAL(ast[i + 1], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> args[i];\n        }\n        if( first.type == \"subr\" )\n        {\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            eval_env @=> env;\n            func.ast @=> m;\n            continue; // TCO\n        }\n    }\n    else\n    {\n        return m;\n    }\n  }\n  Util.panic(\"Programmer error: TCO loop left incorrectly\");\n  return null;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\n// HACK, HACK, HACK\nclass MalEval extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject m;\n        return EVAL(args[0], repl_env);\n    }\n\n    fun MalObject apply(MalObject f, MalObject args[])\n    {\n        if( f.type == \"subr\" )\n        {\n            return (f$MalSubr).call(args);\n        }\n        else // f.type == \"func\"\n        {\n            f$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            return EVAL(func.ast, eval_env);\n        }\n    }\n}\n\nnew MalEval @=> MalEval eval;\nrepl_env.set(\"eval\", new MalEval);\neval @=> (repl_env.get(\"swap!\")$MalSubr).eval;\n\nfun MalObject[] MalArgv(string args[])\n{\n    MalObject values[0];\n\n    for( 1 => int i; i < args.size(); i++ )\n    {\n        values << MalString.create(args[i]);\n    }\n\n    return values;\n}\n\n// NOTE: normally I'd use \\0, but strings are null-terminated...\nString.split(Std.getenv(\"CHUCK_ARGS\"), \"\\a\") @=> string args[];\nrepl_env.set(\"*ARGV*\", MalList.create(MalArgv(args)));\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nif( args.size() > 0 )\n{\n    args[0] => string filename;\n    rep(\"(load-file \\\"\" + filename + \"\\\")\");\n}\nelse\n{\n    main();\n}\n"
  },
  {
    "path": "impls/chuck/step8_macros.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun int startsWith(MalObject a[], string sym)\n{\n    if (a.size() != 2)\n    {\n        return false;\n    }\n\n    a[0] @=> MalObject a0;\n    return a0.type == \"symbol\" && a0.stringValue == sym;\n}\n\nfun MalList qqLoop(MalObject elt, MalList acc)\n{\n    if( elt.type == \"list\" )\n    {\n        elt.malObjectValues() @=> MalObject ast[];\n\n        if( startsWith(ast, \"splice-unquote\") )\n        {\n            return MalList.create([MalSymbol.create(\"concat\"), ast[1], acc]);\n        }\n    }\n    return MalList.create([MalSymbol.create(\"cons\"), quasiquote(elt), acc]);\n}\n\nfun MalList qqFoldr(MalObject a[])\n{\n    MalObject empty[0];  //  empty, but typed\n    MalList.create(empty) @=> MalList acc;\n\n    for( a.size() - 1 => int i; 0 <= i; i-- )\n    {\n        qqLoop(a[i], acc) @=> acc;\n    }\n\n    return acc;\n}\n\nfun MalObject quasiquote(MalObject ast)\n{\n    ast.type => string type;\n    if (type == \"list\") {\n        ast.malObjectValues() @=> MalObject a[];\n        if (startsWith(a, \"unquote\"))\n        {\n            return a[1];\n        }\n        return qqFoldr(a);\n    }\n\n    if (type == \"vector\")\n    {\n        return MalList.create([MalSymbol.create(\"vec\"), qqFoldr(ast.malObjectValues())]);\n    }\n\n    if (type == \"symbol\" || type == \"hashmap\")\n    {\n        return MalList.create([MalSymbol.create(\"quote\"), ast]);\n    }\n\n    return ast;\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n  while( true )\n  {\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                let_env @=> env;\n                ast[2] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"quote\" )\n            {\n                return ast[1];\n            }\n            else if( a0 == \"quasiquote\" )\n            {\n                quasiquote(ast[1]) @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"defmacro!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                value.clone() @=> value;\n                true => (value$Func).isMacro;\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"do\" )\n            {\n              for( 1 => int i; i < ast.size() - 1; i++ )\n              {\n                EVAL(ast[i], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n\n                // HACK: this assumes do gets at least one argument...\n                ast[ast.size()-1] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    ast[2] @=> m;\n                    continue; // TCO\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        ast[3] @=> m;\n                        continue; // TCO\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n        else if( first.type == \"subr\" )\n        {\n            MalObject args[ast.size() - 1];\n            for( 0 => int i; i < args.size(); i++ )\n            {\n                EVAL(ast[i + 1], env) @=> MalObject result;\n                if( result.type == \"error\" )\n                {\n                    return result;\n                }\n                result @=> args[i];\n            }\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            if( func.isMacro )\n            {\n                MalObject.slice(ast, 1) @=> MalObject args[];\n                Env.create(func.env, func.args, args) @=> Env eval_env;\n                EVAL(func.ast, eval_env) @=> m;\n                continue; // TCO\n            }\n            MalObject args[ast.size() - 1];\n            for( 0 => int i; i < args.size(); i++ )\n            {\n                EVAL(ast[i + 1], env) @=> MalObject result;\n                if( result.type == \"error\" )\n                {\n                    return result;\n                }\n                result @=> args[i];\n            }\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            eval_env @=> env;\n            func.ast @=> m;\n            continue; // TCO\n        }\n    }\n    else\n    {\n        return m;\n    }\n  }\n  Util.panic(\"Programmer error: TCO loop left incorrectly\");\n  return null;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\n// HACK, HACK, HACK\nclass MalEval extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject m;\n        return EVAL(args[0], repl_env);\n    }\n\n    fun MalObject apply(MalObject f, MalObject args[])\n    {\n        if( f.type == \"subr\" )\n        {\n            return (f$MalSubr).call(args);\n        }\n        else // f.type == \"func\"\n        {\n            f$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            return EVAL(func.ast, eval_env);\n        }\n    }\n}\n\nnew MalEval @=> MalEval eval;\nrepl_env.set(\"eval\", new MalEval);\neval @=> (repl_env.get(\"swap!\")$MalSubr).eval;\n\nfun MalObject[] MalArgv(string args[])\n{\n    MalObject values[0];\n\n    for( 1 => int i; i < args.size(); i++ )\n    {\n        values << MalString.create(args[i]);\n    }\n\n    return values;\n}\n\n// NOTE: normally I'd use \\0, but strings are null-terminated...\nString.split(Std.getenv(\"CHUCK_ARGS\"), \"\\a\") @=> string args[];\nrepl_env.set(\"*ARGV*\", MalList.create(MalArgv(args)));\n\nfun string errorMessage(MalObject m)\n{\n    return \"exception: \" + String.repr(m.malObjectValue().stringValue);\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nif( args.size() > 0 )\n{\n    args[0] => string filename;\n    rep(\"(load-file \\\"\" + filename + \"\\\")\");\n}\nelse\n{\n    main();\n}\n"
  },
  {
    "path": "impls/chuck/step9_try.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun int startsWith(MalObject a[], string sym)\n{\n    if (a.size() != 2)\n    {\n        return false;\n    }\n\n    a[0] @=> MalObject a0;\n    return a0.type == \"symbol\" && a0.stringValue == sym;\n}\n\nfun MalList qqLoop(MalObject elt, MalList acc)\n{\n    if( elt.type == \"list\" )\n    {\n        elt.malObjectValues() @=> MalObject ast[];\n\n        if( startsWith(ast, \"splice-unquote\") )\n        {\n            return MalList.create([MalSymbol.create(\"concat\"), ast[1], acc]);\n        }\n    }\n    return MalList.create([MalSymbol.create(\"cons\"), quasiquote(elt), acc]);\n}\n\nfun MalList qqFoldr(MalObject a[])\n{\n    MalObject empty[0];  //  empty, but typed\n    MalList.create(empty) @=> MalList acc;\n\n    for( a.size() - 1 => int i; 0 <= i; i-- )\n    {\n        qqLoop(a[i], acc) @=> acc;\n    }\n\n    return acc;\n}\n\nfun MalObject quasiquote(MalObject ast)\n{\n    ast.type => string type;\n    if (type == \"list\") {\n        ast.malObjectValues() @=> MalObject a[];\n        if (startsWith(a, \"unquote\"))\n        {\n            return a[1];\n        }\n        return qqFoldr(a);\n    }\n\n    if (type == \"vector\")\n    {\n        return MalList.create([MalSymbol.create(\"vec\"), qqFoldr(ast.malObjectValues())]);\n    }\n\n    if (type == \"symbol\" || type == \"hashmap\")\n    {\n        return MalList.create([MalSymbol.create(\"quote\"), ast]);\n    }\n\n    return ast;\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n  while( true )\n  {\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                let_env @=> env;\n                ast[2] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"quote\" )\n            {\n                return ast[1];\n            }\n            else if( a0 == \"quasiquote\" )\n            {\n                quasiquote(ast[1]) @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"defmacro!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                value.clone() @=> value;\n                true => (value$Func).isMacro;\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"try*\" )\n            {\n                EVAL(ast[1], env) @=> MalObject value;\n\n                if( (value.type != \"error\") || (ast.size() < 3) )\n                {\n                    return value;\n                }\n\n                ast[2].malObjectValues() @=> MalObject form[];\n                form[1].stringValue => string name;\n                value.malObjectValue() @=> MalObject error;\n\n                Env.create(env, [name], [error]) @=> Env error_env;\n                return EVAL(form[2], error_env);\n            }\n            else if( a0 == \"do\" )\n            {\n              for( 1 => int i; i < ast.size() - 1; i++ )\n              {\n                EVAL(ast[i], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n\n                // HACK: this assumes do gets at least one argument...\n                ast[ast.size()-1] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    ast[2] @=> m;\n                    continue; // TCO\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        ast[3] @=> m;\n                        continue; // TCO\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n        else if( first.type == \"subr\" )\n        {\n            MalObject args[ast.size() - 1];\n            for( 0 => int i; i < args.size(); i++ )\n            {\n                EVAL(ast[i + 1], env) @=> MalObject result;\n                if( result.type == \"error\" )\n                {\n                    return result;\n                }\n                result @=> args[i];\n            }\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            if( func.isMacro )\n            {\n                MalObject.slice(ast, 1) @=> MalObject args[];\n                Env.create(func.env, func.args, args) @=> Env eval_env;\n                EVAL(func.ast, eval_env) @=> m;\n                continue; // TCO\n            }\n            MalObject args[ast.size() - 1];\n            for( 0 => int i; i < args.size(); i++ )\n            {\n                EVAL(ast[i + 1], env) @=> MalObject result;\n                if( result.type == \"error\" )\n                {\n                    return result;\n                }\n                result @=> args[i];\n            }\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            eval_env @=> env;\n            func.ast @=> m;\n            continue; // TCO\n        }\n    }\n    else\n    {\n        return m;\n    }\n  }\n  Util.panic(\"Programmer error: TCO loop left incorrectly\");\n  return null;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\n// HACK, HACK, HACK\nclass MalEval extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject m;\n        return EVAL(args[0], repl_env);\n    }\n\n    fun MalObject apply(MalObject f, MalObject args[])\n    {\n        if( f.type == \"subr\" )\n        {\n            return (f$MalSubr).call(args);\n        }\n        else // f.type == \"func\"\n        {\n            f$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            return EVAL(func.ast, eval_env);\n        }\n    }\n}\n\nnew MalEval @=> MalEval eval;\nrepl_env.set(\"eval\", new MalEval);\neval @=> (repl_env.get(\"swap!\")$MalSubr).eval;\neval @=> (repl_env.get(\"apply\")$MalSubr).eval;\neval @=> (repl_env.get(\"map\")$MalSubr).eval;\n\nfun MalObject[] MalArgv(string args[])\n{\n    MalObject values[0];\n\n    for( 1 => int i; i < args.size(); i++ )\n    {\n        values << MalString.create(args[i]);\n    }\n\n    return values;\n}\n\n// NOTE: normally I'd use \\0, but strings are null-terminated...\nString.split(Std.getenv(\"CHUCK_ARGS\"), \"\\a\") @=> string args[];\nrepl_env.set(\"*ARGV*\", MalList.create(MalArgv(args)));\n\nfun string errorMessage(MalObject m)\n{\n    m.malObjectValue() @=> MalObject e;\n    string message;\n\n    if( e.type == \"string\" )\n    {\n        String.repr(e.stringValue) => message;\n    }\n    else\n    {\n        Printer.pr_str(e, true) => message;\n    }\n\n    return \"exception: \" + message;\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nif( args.size() > 0 )\n{\n    args[0] => string filename;\n    rep(\"(load-file \\\"\" + filename + \"\\\")\");\n}\nelse\n{\n    main();\n}\n"
  },
  {
    "path": "impls/chuck/stepA_mal.ck",
    "content": "// @import readline.ck\n// @import types/MalObject.ck\n// @import types/mal/MalAtom.ck\n// @import types/mal/MalString.ck\n// @import types/mal/MalError.ck\n// @import types/mal/MalNil.ck\n// @import types/mal/MalFalse.ck\n// @import types/mal/MalTrue.ck\n// @import types/mal/MalInt.ck\n// @import types/mal/MalSymbol.ck\n// @import types/mal/MalKeyword.ck\n// @import types/mal/MalList.ck\n// @import types/mal/MalVector.ck\n// @import types/mal/MalHashMap.ck\n// @import util/*.ck\n// @import reader.ck\n// @import printer.ck\n// @import env.ck\n// @import func.ck\n// @import types/MalSubr.ck\n// @import types/subr/*.ck\n// @import core.ck\n\nfun MalObject READ(string input)\n{\n    return Reader.read_str(input);\n}\n\nfun int startsWith(MalObject a[], string sym)\n{\n    if (a.size() != 2)\n    {\n        return false;\n    }\n\n    a[0] @=> MalObject a0;\n    return a0.type == \"symbol\" && a0.stringValue == sym;\n}\n\nfun MalList qqLoop(MalObject elt, MalList acc)\n{\n    if( elt.type == \"list\" )\n    {\n        elt.malObjectValues() @=> MalObject ast[];\n\n        if( startsWith(ast, \"splice-unquote\") )\n        {\n            return MalList.create([MalSymbol.create(\"concat\"), ast[1], acc]);\n        }\n    }\n    return MalList.create([MalSymbol.create(\"cons\"), quasiquote(elt), acc]);\n}\n\nfun MalList qqFoldr(MalObject a[])\n{\n    MalObject empty[0];  //  empty, but typed\n    MalList.create(empty) @=> MalList acc;\n\n    for( a.size() - 1 => int i; 0 <= i; i-- )\n    {\n        qqLoop(a[i], acc) @=> acc;\n    }\n\n    return acc;\n}\n\nfun MalObject quasiquote(MalObject ast)\n{\n    ast.type => string type;\n    if (type == \"list\") {\n        ast.malObjectValues() @=> MalObject a[];\n        if (startsWith(a, \"unquote\"))\n        {\n            return a[1];\n        }\n        return qqFoldr(a);\n    }\n\n    if (type == \"vector\")\n    {\n        return MalList.create([MalSymbol.create(\"vec\"), qqFoldr(ast.malObjectValues())]);\n    }\n\n    if (type == \"symbol\" || type == \"hashmap\")\n    {\n        return MalList.create([MalSymbol.create(\"quote\"), ast]);\n    }\n\n    return ast;\n}\n\nfun MalObject EVAL(MalObject m, Env env)\n{\n  while( true )\n  {\n    env.find(\"DEBUG-EVAL\") @=> MalObject debugEval;\n    if( debugEval != null && (debugEval.type != \"false\" &&\n                              debugEval.type != \"nil\" ) )\n    {\n        Util.println(\"EVAL: \" + Printer.pr_str(m, true));\n    }\n\n    if( m.type == \"symbol\" )\n    {\n        return env.get(m.stringValue);\n    }\n    else if( m.type == \"vector\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            EVAL(values[i], env) @=> MalObject result;\n            if( result.type == \"error\" )\n            {\n                return result;\n            }\n            result @=> results[i];\n        }\n        return MalVector.create(results);\n    }\n    else if( m.type == \"hashmap\" )\n    {\n        m.malObjectValues() @=> MalObject values[];\n        MalObject results[values.size()];\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            if( i % 2 == 0 )\n            {\n                values[i] @=> results[i];\n            }\n            else\n            {\n                EVAL(values[i], env) @=> results[i];\n            }\n        }\n        return MalHashMap.create(results);\n    }\n    else if( m.type == \"list\" )\n    {\n        m.malObjectValues() @=> MalObject ast[];\n\n        if( ast.size() == 0 )\n        {\n            return m;\n        }\n        else if( ast[0].type == \"symbol\" )\n        {\n            ast[0].stringValue => string a0;\n\n            if( a0 == \"def!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"let*\" )\n            {\n                Env.create(env) @=> Env let_env;\n                ast[1].malObjectValues() @=> MalObject bindings[];\n\n                for( 0 => int i; i < bindings.size(); 2 +=> i)\n                {\n                    bindings[i].stringValue => string symbol;\n                    EVAL(bindings[i+1], let_env) @=> MalObject value;\n\n                    if( value.type == \"error\" )\n                    {\n                        return value;\n                    }\n\n                    let_env.set(symbol, value);\n                }\n\n                let_env @=> env;\n                ast[2] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"quote\" )\n            {\n                return ast[1];\n            }\n            else if( a0 == \"quasiquote\" )\n            {\n                quasiquote(ast[1]) @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"defmacro!\" )\n            {\n                ast[1].stringValue => string a1;\n\n                EVAL(ast[2], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n\n                value.clone() @=> value;\n                true => (value$Func).isMacro;\n\n                env.set(a1, value);\n                return value;\n            }\n            else if( a0 == \"try*\" )\n            {\n                EVAL(ast[1], env) @=> MalObject value;\n\n                if( (value.type != \"error\") || (ast.size() < 3) )\n                {\n                    return value;\n                }\n\n                ast[2].malObjectValues() @=> MalObject form[];\n                form[1].stringValue => string name;\n                value.malObjectValue() @=> MalObject error;\n\n                Env.create(env, [name], [error]) @=> Env error_env;\n                return EVAL(form[2], error_env);\n            }\n            else if( a0 == \"do\" )\n            {\n              for( 1 => int i; i < ast.size() - 1; i++ )\n              {\n                EVAL(ast[i], env) @=> MalObject value;\n                if( value.type == \"error\" )\n                {\n                    return value;\n                }\n              }\n\n                // HACK: this assumes do gets at least one argument...\n                ast[ast.size()-1] @=> m;\n                continue; // TCO\n            }\n            else if( a0 == \"if\" )\n            {\n                EVAL(ast[1], env) @=> MalObject condition;\n\n                if( condition.type == \"error\" )\n                {\n                    return condition;\n                }\n\n                if( !(condition.type == \"nil\") && !(condition.type == \"false\") )\n                {\n                    ast[2] @=> m;\n                    continue; // TCO\n                }\n                else\n                {\n                    if( ast.size() < 4 )\n                    {\n                        return Constants.NIL;\n                    }\n                    else\n                    {\n                        ast[3] @=> m;\n                        continue; // TCO\n                    }\n                }\n            }\n            else if( a0 == \"fn*\" )\n            {\n                ast[1].malObjectValues() @=> MalObject arg_values[];\n                string args[arg_values.size()];\n\n                for( 0 => int i; i < arg_values.size(); i++ )\n                {\n                    arg_values[i].stringValue => args[i];\n                }\n\n                ast[2] @=> MalObject _ast;\n\n                return Func.create(env, args, _ast);\n            }\n        }\n\n        EVAL(ast[0], env) @=> MalObject first;\n        if( first.type == \"error\" )\n        {\n            return first;\n        }\n        else if( first.type == \"subr\" )\n        {\n            MalObject args[ast.size() - 1];\n            for( 0 => int i; i < args.size(); i++ )\n            {\n                EVAL(ast[i + 1], env) @=> MalObject result;\n                if( result.type == \"error\" )\n                {\n                    return result;\n                }\n                result @=> args[i];\n            }\n            first$MalSubr @=> MalSubr subr;\n            return subr.call(args);\n        }\n        else if( first.type == \"func\" )\n        {\n            first$Func @=> Func func;\n            if( func.isMacro )\n            {\n                MalObject.slice(ast, 1) @=> MalObject args[];\n                Env.create(func.env, func.args, args) @=> Env eval_env;\n                EVAL(func.ast, eval_env) @=> m;\n                continue; // TCO\n            }\n            MalObject args[ast.size() - 1];\n            for( 0 => int i; i < args.size(); i++ )\n            {\n                EVAL(ast[i + 1], env) @=> MalObject result;\n                if( result.type == \"error\" )\n                {\n                    return result;\n                }\n                result @=> args[i];\n            }\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            eval_env @=> env;\n            func.ast @=> m;\n            continue; // TCO\n        }\n    }\n    else\n    {\n        return m;\n    }\n  }\n  Util.panic(\"Programmer error: TCO loop left incorrectly\");\n  return null;\n}\n\nfun string PRINT(MalObject m)\n{\n    return Printer.pr_str(m, true);\n}\n\nEnv.create(null) @=> Env repl_env;\nfor( 0 => int i; i < Core.names.size(); i++ )\n{\n    Core.names[i] => string name;\n    repl_env.set(name, Core.ns[name]);\n}\n\n// HACK, HACK, HACK\nclass MalEval extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject m;\n        return EVAL(args[0], repl_env);\n    }\n\n    fun MalObject apply(MalObject f, MalObject args[])\n    {\n        if( f.type == \"subr\" )\n        {\n            return (f$MalSubr).call(args);\n        }\n        else // f.type == \"func\"\n        {\n            f$Func @=> Func func;\n            Env.create(func.env, func.args, args) @=> Env eval_env;\n            return EVAL(func.ast, eval_env);\n        }\n    }\n}\n\nnew MalEval @=> MalEval eval;\nrepl_env.set(\"eval\", new MalEval);\neval @=> (repl_env.get(\"swap!\")$MalSubr).eval;\neval @=> (repl_env.get(\"apply\")$MalSubr).eval;\neval @=> (repl_env.get(\"map\")$MalSubr).eval;\n\nfun MalObject[] MalArgv(string args[])\n{\n    MalObject values[0];\n\n    for( 1 => int i; i < args.size(); i++ )\n    {\n        values << MalString.create(args[i]);\n    }\n\n    return values;\n}\n\n// NOTE: normally I'd use \\0, but strings are null-terminated...\nString.split(Std.getenv(\"CHUCK_ARGS\"), \"\\a\") @=> string args[];\nrepl_env.set(\"*ARGV*\", MalList.create(MalArgv(args)));\n\nrepl_env.set(\"*host-language*\", MalString.create(\"chuck\"));\n\nfun string errorMessage(MalObject m)\n{\n    m.malObjectValue() @=> MalObject e;\n    string message;\n\n    if( e.type == \"string\" )\n    {\n        String.repr(e.stringValue) => message;\n    }\n    else\n    {\n        Printer.pr_str(e, true) => message;\n    }\n\n    return \"exception: \" + message;\n}\n\nfun string rep(string input)\n{\n    READ(input) @=> MalObject m;\n\n    if( m.type == \"error\" )\n    {\n        return errorMessage(m);\n    }\n\n    EVAL(m, repl_env) @=> MalObject result;\n    if( result.type == \"error\" )\n    {\n        return errorMessage(result);\n    }\n\n    return PRINT(result);\n}\n\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nfun void main()\n{\n    int done;\n\n    while( !done )\n    {\n        Readline.readline(\"user> \") => string input;\n\n        if( input != null )\n        {\n            rep(input) => string output;\n\n            if( output == \"exception: \\\"empty input\\\"\" )\n            {\n                // proceed immediately with prompt\n            }\n            else\n            {\n                Util.println(output);\n            }\n        }\n        else\n        {\n            true => done;\n        }\n    }\n}\n\nif( args.size() > 0 )\n{\n    args[0] => string filename;\n    rep(\"(load-file \\\"\" + filename + \"\\\")\");\n}\nelse\n{\n    rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n    main();\n}\n"
  },
  {
    "path": "impls/chuck/tests/step5_tco.mal",
    "content": ";; ChucK: skipping non-TCO recursion\n;; Reason: stackoverflow (non-recoverable)\n"
  },
  {
    "path": "impls/chuck/types/MalObject.ck",
    "content": "public class MalObject\n{\n    string type;\n\n    int intValue;\n    string stringValue;\n    // HACK: data types can't be self-referential, so Object it is\n    Object object;\n    Object objects[];\n    // NOTE: an object member does *not* default to null...\n    null => Object meta;\n\n    fun MalObject malObjectValue()\n    {\n        return object$MalObject;\n    }\n\n    fun MalObject[] malObjectValues()\n    {\n        MalObject values[objects.size()];\n\n        for( 0 => int i; i < objects.size(); i++ )\n        {\n            objects[i]$MalObject @=> values[i];\n        }\n\n        return values;\n    }\n\n    fun MalObject clone()\n    {\n        MalObject value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n\n    // helpers for sequence types\n\n    fun static Object[] toObjectArray(MalObject objects[])\n    {\n        Object values[objects.size()];\n\n        for( 0 => int i; i < objects.size(); i++ )\n        {\n            objects[i]$Object @=> values[i];\n        }\n\n        return values;\n    }\n\n    fun static MalObject[] slice(MalObject objects[], int index)\n    {\n        MalObject values[objects.size() - index];\n\n        for( index => int i; i < objects.size(); i++ )\n        {\n            objects[i] @=> values[i - index];\n        }\n\n        return values;\n    }\n\n    fun static MalObject[] slice(MalObject objects[], int from, int to)\n    {\n        MalObject values[0];\n\n        for( from => int i; i < to; i++ )\n        {\n            values << objects[i];\n        }\n\n        return values;\n    }\n\n    fun static MalObject[] append(MalObject as[], MalObject bs[])\n    {\n        MalObject output[as.size()+bs.size()];\n\n        for( 0 => int i; i < as.size(); i++ )\n        {\n            as[i] @=> output[i];\n        }\n\n        for( 0 => int i; i < bs.size(); i++ )\n        {\n            bs[i] @=> output[as.size()+i];\n        }\n\n        return output;\n    }\n\n    fun static MalObject[] reverse(MalObject objects[])\n    {\n        MalObject output[objects.size()];\n\n        for( 0 => int i; i < output.size(); i++ )\n        {\n            objects[i] @=> output[output.size()-i-1];\n        }\n\n        return output;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/MalSubr.ck",
    "content": "public class MalSubr extends MalObject\n{\n    \"subr\" => type;\n    string name;\n    // HACK\n    MalObject eval;\n\n    fun MalObject call(MalObject args[])\n    {\n        return new MalObject;\n    }\n\n    fun MalObject apply(MalObject f, MalObject args[])\n    {\n        return new MalObject;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalAtom.ck",
    "content": "public class MalAtom extends MalObject\n{\n    \"atom\" => type;\n\n    fun void init(MalObject value)\n    {\n        value @=> object;\n    }\n\n    fun static MalObject create(MalObject value)\n    {\n        MalAtom m;\n        m.init(value);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalAtom value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalError.ck",
    "content": "public class MalError extends MalObject\n{\n    \"error\" => type;\n\n    fun void init(MalObject value)\n    {\n        value @=> object;\n    }\n\n    fun static MalError create(string value)\n    {\n        MalError m;\n        m.init(MalString.create(value));\n        return m;\n    }\n\n    fun static MalError create(MalObject value)\n    {\n        MalError m;\n        m.init(value);\n        return m;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalFalse.ck",
    "content": "public class MalFalse extends MalObject\n{\n    \"false\" => type;\n\n    fun void init()\n    {\n        0 => intValue;\n    }\n\n    fun static MalFalse create()\n    {\n        MalFalse m;\n        m.init();\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalFalse value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalHashMap.ck",
    "content": "// HACK: it's hard to pull in util before data types\n\npublic class MalHashMap extends MalObject\n{\n    \"hashmap\" => type;\n\n\n    fun string keyName(MalObject m)\n    {\n        if( m.type == \"string\" || m.type == \"keyword\" )\n        {\n            return m.stringValue;\n        }\n        else\n        {\n            cherr <= \"User error (non-string/keyword key)\\n\";\n            return \"\";\n        }\n    }\n\n    fun void init(MalObject values[])\n    {\n        MalObject result[0];\n        MalObject cachedKeys[0];\n        MalObject cachedValues[0];\n        string keys[0];\n\n        for( 0 => int i; i < values.size(); 2 +=> i )\n        {\n            keyName(values[i]) => string key;\n\n            if( cachedValues[key] == null )\n            {\n                keys << key;\n            }\n\n            values[i] @=> cachedKeys[key];\n            values[i+1] @=> cachedValues[key];\n        }\n\n        for( 0 => int i; i < keys.size(); i++ )\n        {\n            keys[i] => string key;\n            result << cachedKeys[key];\n            result << cachedValues[key];\n        }\n\n        MalObject.toObjectArray(result) @=> objects;\n    }\n\n    fun static MalHashMap create(MalObject values[])\n    {\n        MalHashMap m;\n        m.init(values);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalHashMap value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalInt.ck",
    "content": "public class MalInt extends MalObject\n{\n    \"int\" => type;\n\n    fun void init(int value)\n    {\n        value => intValue;\n    }\n\n    fun static MalInt create(int value)\n    {\n        MalInt m;\n        m.init(value);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalInt value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalKeyword.ck",
    "content": "public class MalKeyword extends MalObject\n{\n    \"keyword\" => type;\n\n    fun void init(string value)\n    {\n        value => stringValue;\n    }\n\n    fun static MalKeyword create(string value)\n    {\n        MalKeyword m;\n        m.init(value);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalKeyword value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalList.ck",
    "content": "public class MalList extends MalObject\n{\n    \"list\" => type;\n\n    fun void init(MalObject values[])\n    {\n        MalObject.toObjectArray(values) @=> objects;\n    }\n\n    fun static MalList create(MalObject values[])\n    {\n        MalList m;\n        m.init(values);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalList value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalNil.ck",
    "content": "public class MalNil extends MalObject\n{\n    \"nil\" => type;\n\n    fun void init()\n    {\n        -1 => intValue;\n    }\n\n    fun static MalNil create()\n    {\n        MalNil m;\n        m.init();\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalNil value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalString.ck",
    "content": "public class MalString extends MalObject\n{\n    \"string\" => type;\n\n    fun void init(string value)\n    {\n       value => stringValue;\n    }\n\n    fun static MalString create(string value)\n    {\n        MalString m;\n        m.init(value);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalString value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalSymbol.ck",
    "content": "public class MalSymbol extends MalObject\n{\n    \"symbol\" => type;\n\n    fun void init(string value)\n    {\n        value => stringValue;\n    }\n\n    fun static MalSymbol create(string value)\n    {\n        MalSymbol m;\n        m.init(value);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalSymbol value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalTrue.ck",
    "content": "public class MalTrue extends MalObject\n{\n    \"true\" => type;\n\n    fun void init()\n    {\n        1 => intValue;\n    }\n\n    fun static MalTrue create()\n    {\n        MalTrue m;\n        m.init();\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalTrue value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/mal/MalVector.ck",
    "content": "public class MalVector extends MalObject\n{\n    \"vector\" => type;\n\n    fun void init(MalObject values[])\n    {\n        MalObject.toObjectArray(values) @=> objects;\n    }\n\n    fun static MalVector create(MalObject values[])\n    {\n        MalVector m;\n        m.init(values);\n        return m;\n    }\n\n    fun MalObject clone()\n    {\n        MalVector value;\n\n        this.type => value.type;\n        this.object @=> value.object;\n        this.objects @=> value.objects;\n        this.meta @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalAdd.ck",
    "content": "public class MalAdd extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalInt.create(args[0].intValue + args[1].intValue);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalApply.ck",
    "content": "public class MalApply extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject f;\n        MalObject.slice(args, 1, args.size()-1) @=> MalObject _args[];\n        args[args.size()-1].malObjectValues() @=> MalObject rest[];\n\n        MalObject.append(_args, rest) @=> _args;\n        return (eval$MalSubr).apply(f, _args);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalAssoc.ck",
    "content": "public class MalAssoc extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject map[];\n        MalObject.slice(args, 1) @=> MalObject kvs[];\n\n        MalObject result[0];\n        MalObject cachedKeys[0];\n        MalObject cachedValues[0];\n        string keys[0];\n\n        for( 0 => int i; i < map.size(); 2 +=> i )\n        {\n            map[i].stringValue => string key;\n\n            keys << key;\n\n            map[i] @=> cachedKeys[key];\n            map[i+1] @=> cachedValues[key];\n        }\n\n        for( 0 => int i; i < kvs.size(); 2 +=> i )\n        {\n            kvs[i].stringValue => string key;\n\n            if( cachedValues[key] == null )\n            {\n                keys << key;\n            }\n\n            kvs[i] @=> cachedKeys[key];\n            kvs[i+1] @=> cachedValues[key];\n        }\n\n        for( 0 => int i; i < keys.size(); i++ )\n        {\n            keys[i] => string key;\n            result << cachedKeys[key];\n            result << cachedValues[key];\n        }\n\n        return MalHashMap.create(result);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalAtomify.ck",
    "content": "public class MalAtomify extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject m;\n        return MalAtom.create(m);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalConcat.ck",
    "content": "public class MalConcat extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        MalObject value[0];\n\n        for( 0 => int i; i < args.size(); i++ )\n        {\n            args[i].malObjectValues() @=> MalObject list[];\n            MalObject.append(value, list) @=> value;\n        }\n\n        return MalList.create(value);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalConj.ck",
    "content": "public class MalConj extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject list[];\n        MalObject.slice(args, 1) @=> MalObject rest[];\n\n        if( args[0].type == \"list\" )\n        {\n            return MalList.create(MalObject.append(MalObject.reverse(rest), list));\n        }\n        else // args[0].type == \"vector\"\n        {\n            return MalVector.create(MalObject.append(list, rest));\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalCons.ck",
    "content": "public class MalCons extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n        args[1].malObjectValues() @=> MalObject list[];\n        return MalList.create(MalObject.append([arg], list));\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalCount.ck",
    "content": "public class MalCount extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].type => string kind;\n        if( kind == \"list\" || kind == \"vector\" )\n        {\n            return MalInt.create(args[0].objects.size());\n        }\n        else\n        {\n            return MalInt.create(0);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalDeref.ck",
    "content": "public class MalDeref extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return args[0].malObjectValue();\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalDissoc.ck",
    "content": "public class MalDissoc extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject map[];\n        MalObject.slice(args, 1) @=> MalObject ks[];\n\n        MalObject result[0];\n        string cachedKeys[0];\n\n        for( 0 => int i; i < ks.size(); i++ )\n        {\n            ks[i].type => cachedKeys[ks[i].stringValue];\n        }\n\n        for( 0 => int i; i < map.size(); 2 +=> i )\n        {\n            map[i] @=> MalObject key;\n            map[i+1] @=> MalObject value;\n\n            if (   cachedKeys[key.stringValue] == null\n                || cachedKeys[key.stringValue] != key.type )\n            {\n                result << key;\n                result << value;\n            }\n        }\n\n        return MalHashMap.create(result);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalDiv.ck",
    "content": "public class MalDiv extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalInt.create(args[0].intValue / args[1].intValue);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalDoReset.ck",
    "content": "public class MalDoReset extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0]$MalAtom @=> MalAtom atom;\n        args[1]$MalObject @=> MalObject value;\n\n        value @=> atom.object;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalDoSwap.ck",
    "content": "public class MalDoSwap extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0]$MalAtom @=> MalAtom atom;\n        atom.malObjectValue() @=> MalObject value;\n        args[1] @=> MalObject f;\n        MalObject.slice(args, 2) @=> MalObject _args[];\n        MalObject.append([value], _args) @=> _args;\n\n        (eval$MalSubr).apply(f, _args) @=> value;\n        value @=> atom.object;\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalEqual.ck",
    "content": "public class MalEqual extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject a;\n        args[1] @=> MalObject b;\n\n        if( ( a.type == \"list\" || a.type == \"vector\" ) &&\n            ( b.type == \"list\" || b.type == \"vector\" ) )\n        {\n            a.malObjectValues() @=> MalObject as[];\n            b.malObjectValues() @=> MalObject bs[];\n\n            if( as.size() != bs.size() )\n            {\n                return Constants.FALSE;\n            }\n\n            for( 0 => int i; i < as.size(); i++ )\n            {\n                call([as[i], bs[i]]) @=> MalObject value;\n                if( value.type != \"true\" )\n                {\n                    return Constants.FALSE;\n                }\n            }\n\n            return Constants.TRUE;\n        }\n\n        if( a.type == \"hashmap\" && b.type == \"hashmap\" )\n        {\n            a.malObjectValues() @=> MalObject akvs[];\n            b.malObjectValues() @=> MalObject bkvs[];\n\n            if( akvs.size() != bkvs.size() )\n            {\n                return Constants.FALSE;\n            }\n\n            MalObject bmap[0];\n\n            for( 0 => int i; i < bkvs.size(); 2 +=> i )\n            {\n                bkvs[i].stringValue => string keyName;\n                bkvs[i+1] @=> bmap[keyName];\n            }\n\n\n            for( 0 => int i; i < akvs.size(); 2 +=> i )\n            {\n                akvs[i] @=> MalObject key;\n                akvs[i+1] @=> MalObject value;\n                key.stringValue => string keyName;\n\n                if( bmap[keyName] == null ||\n                    call([value, bmap[keyName]]).type != \"true\" )\n                {\n                    return Constants.FALSE;\n                }\n            }\n\n            return Constants.TRUE;\n        }\n\n        if( a.type != b.type )\n        {\n            return Constants.FALSE;\n        }\n\n        // NOTE: normally I'd go for a type variable, but its scope\n        // isn't handled properly in the presence of a member variable\n        a.type => string kind;\n        if( kind == \"true\" || kind == \"false\" || kind == \"nil\" )\n        {\n            return Constants.TRUE;\n        }\n        else if( kind == \"int\" )\n        {\n            if( a.intValue == b.intValue )\n            {\n                return Constants.TRUE;\n            }\n            else\n            {\n                return Constants.FALSE;\n            }\n        }\n        else if( kind == \"string\" )\n        {\n            if( a.stringValue == b.stringValue )\n            {\n                return Constants.TRUE;\n            }\n            else\n            {\n                return Constants.FALSE;\n            }\n        }\n        else if( kind == \"symbol\" )\n        {\n            if( a.stringValue == b.stringValue )\n            {\n                return Constants.TRUE;\n            }\n            else\n            {\n                return Constants.FALSE;\n            }\n        }\n        else if( kind == \"keyword\" )\n        {\n            if( a.stringValue == b.stringValue )\n            {\n                return Constants.TRUE;\n            }\n            else\n            {\n                return Constants.FALSE;\n            }\n        }\n\n        // HACK: return false for everything unknown for now\n        return Constants.FALSE;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalFirst.ck",
    "content": "public class MalFirst extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"nil\" )\n        {\n            return Constants.NIL;\n        }\n\n        arg.malObjectValues() @=> MalObject list[];\n\n        if( list.size() > 0 )\n        {\n            return list[0];\n        }\n        else\n        {\n            return Constants.NIL;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalGet.ck",
    "content": "public class MalGet extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"nil\" )\n        {\n            return Constants.NIL;\n        }\n\n        args[0].malObjectValues() @=> MalObject map[];\n        args[1].stringValue => string keyName;\n\n        MalObject mapKey;\n        MalObject mapValue;\n        false => int isKeyPresent;\n        0 => int i;\n\n        while( !isKeyPresent && i < map.size() )\n        {\n            map[i] @=> mapKey;\n            map[i+1] @=> mapValue;\n\n            if( keyName == mapKey.stringValue && args[1].type == mapKey.type )\n            {\n                true => isKeyPresent;\n            }\n\n            2 +=> i;\n        }\n\n        if( isKeyPresent )\n        {\n            return mapValue;\n        }\n        else\n        {\n            return Constants.NIL;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalGreater.ck",
    "content": "public class MalGreater extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].intValue > args[1].intValue )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalGreaterEqual.ck",
    "content": "public class MalGreaterEqual extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].intValue >= args[1].intValue )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalHashMapify.ck",
    "content": "public class MalHashMapify extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalHashMap.create(args);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsAtom.ck",
    "content": "public class MalIsAtom extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"atom\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsContains.ck",
    "content": "public class MalIsContains extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject map[];\n        args[1].stringValue => string keyName;\n\n        MalObject mapKey;\n        MalObject mapValue;\n        false => int isKeyPresent;\n        0 => int i;\n\n        while( !isKeyPresent && i < map.size() )\n        {\n            map[i] @=> mapKey;\n\n            if( keyName == mapKey.stringValue && args[1].type == mapKey.type )\n            {\n                true => isKeyPresent;\n            }\n\n            2 +=> i;\n        }\n\n        if( isKeyPresent )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsEmpty.ck",
    "content": "public class MalIsEmpty extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].objects.size() == 0 )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsFalse.ck",
    "content": "public class MalIsFalse extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"false\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsFn.ck",
    "content": "public class MalIsFn extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"subr\" || ( args[0].type == \"func\" &&\n                                        !(args[0]$Func).isMacro ) )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsHashMap.ck",
    "content": "public class MalIsHashMap extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"hashmap\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsKeyword.ck",
    "content": "public class MalIsKeyword extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"keyword\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsList.ck",
    "content": "public class MalIsList extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"list\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsMacro.ck",
    "content": "public class MalIsMacro extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"func\" && (args[0]$Func).isMacro )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsNil.ck",
    "content": "public class MalIsNil extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"nil\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsNumber.ck",
    "content": "public class MalIsNumber extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"int\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsString.ck",
    "content": "public class MalIsString extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].type == \"string\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsSymbol.ck",
    "content": "public class MalIsSymbol extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"symbol\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsTrue.ck",
    "content": "public class MalIsTrue extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"true\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalIsVector.ck",
    "content": "public class MalIsVector extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"vector\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalKeys.ck",
    "content": "public class MalKeys extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject map[];\n        MalObject results[0];\n\n        for( 0 => int i; i < map.size(); 2 +=> i )\n        {\n            results << map[i];\n        }\n\n        return MalList.create(results);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalKeywordify.ck",
    "content": "public class MalKeywordify extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalKeyword.create(args[0].stringValue);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalLess.ck",
    "content": "public class MalLess extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].intValue < args[1].intValue )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalLessEqual.ck",
    "content": "public class MalLessEqual extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if( args[0].intValue <= args[1].intValue )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalListify.ck",
    "content": "public class MalListify extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalList.create(args);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalMap.ck",
    "content": "public class MalMap extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject f;\n        args[1].malObjectValues() @=> MalObject list[];\n\n        for( 0 => int i; i < list.size(); i++ )\n        {\n            (eval$MalSubr).apply(f, [list[i]]) @=> MalObject value;\n\n            if( value.type == \"error\" )\n            {\n                return value;\n            }\n\n            value @=> list[i];\n        }\n\n        return MalList.create(list);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalMeta.ck",
    "content": "public class MalMeta extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.meta == null )\n        {\n            return Constants.NIL;\n        }\n        else\n        {\n            return (arg.meta)$MalObject;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalMul.ck",
    "content": "public class MalMul extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalInt.create(args[0].intValue * args[1].intValue);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalNth.ck",
    "content": "public class MalNth extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject list[];\n        args[1].intValue => int n;\n\n        if( n < list.size() )\n        {\n            return list[n];\n        }\n        else\n        {\n            return MalError.create(\"out of bounds\");\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalPrStr.ck",
    "content": "public class MalPrStr extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        string values[args.size()];\n\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            Printer.pr_str(args[i], true) => values[i];\n        }\n\n        return MalString.create(String.join(values, \" \"));\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalPrintln.ck",
    "content": "public class MalPrintln extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        string values[args.size()];\n\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            Printer.pr_str(args[i], false) => values[i];\n        }\n\n        Util.println(String.join(values, \" \"));\n        return Constants.NIL;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalPrn.ck",
    "content": "public class MalPrn extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        string values[args.size()];\n\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            Printer.pr_str(args[i], true) => values[i];\n        }\n\n        Util.println(String.join(values, \" \"));\n        return Constants.NIL;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalReadStr.ck",
    "content": "public class MalReadStr extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].stringValue => string input;\n        return Reader.read_str(input);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalReadline.ck",
    "content": "public class MalReadline extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].stringValue => string prompt;\n        Readline.readline(prompt) => string input;\n\n        if( input == null )\n        {\n            return Constants.NIL;\n        }\n        else\n        {\n            return MalString.create(input);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalRest.ck",
    "content": "public class MalRest extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n        MalObject result[0];\n\n        if( arg.type == \"nil\" )\n        {\n            return MalList.create(result);\n        }\n\n        args[0].malObjectValues() @=> MalObject list[];\n\n        if( list.size() > 0 )\n        {\n            MalObject.slice(list, 1) @=> result;\n        }\n\n        return MalList.create(result);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalSeq.ck",
    "content": "public class MalSeq extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"nil\" )\n        {\n            return Constants.NIL;\n        }\n        else if( arg.type == \"list\" || arg.type == \"vector\" )\n        {\n            args[0].malObjectValues() @=> MalObject list[];\n\n            if( list.size() > 0 )\n            {\n                return MalList.create(list);\n            }\n            else\n            {\n                return Constants.NIL;\n            }\n        }\n        else if( arg.type == \"string\" )\n        {\n            args[0].stringValue => string value;\n\n            if( value.length() > 0 )\n            {\n                MalObject chars[value.length()];\n\n                for( 0 => int i; i < value.length(); i++ )\n                {\n                    MalString.create(value.substring(i, 1)) @=> chars[i];\n                }\n\n                return MalList.create(chars);\n            }\n            else\n            {\n                return Constants.NIL;\n            }\n        }\n        else\n        {\n            return MalError.create(\"Invalid argument\");\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalSequential.ck",
    "content": "public class MalSequential extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n\n        if( arg.type == \"list\" || arg.type == \"vector\" )\n        {\n            return Constants.TRUE;\n        }\n        else\n        {\n            return Constants.FALSE;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalSlurp.ck",
    "content": "public class MalSlurp extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].stringValue => string filename;\n        FileIO f;\n        string output[0];\n\n        f.open(filename, FileIO.READ);\n\n        while( f.more() )\n        {\n            output << f.readLine();\n        }\n\n        // HACK: not only do we assume files are joined by \\n, but the\n        // final newline cannot be detected otherwise\n        String.join(output, \"\\n\") => string content;\n        if( f.size() == content.length() + 1 )\n        {\n           \"\\n\"  +=> content;\n        }\n\n        f.close();\n        return MalString.create(content);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalStr.ck",
    "content": "public class MalStr extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        string values[args.size()];\n\n        for( 0 => int i; i < values.size(); i++ )\n        {\n            Printer.pr_str(args[i], false) => values[i];\n        }\n\n        return MalString.create(String.join(values, \"\"));\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalSub.ck",
    "content": "public class MalSub extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalInt.create(args[0].intValue - args[1].intValue);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalSymbolify.ck",
    "content": "public class MalSymbolify extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].stringValue => string name;\n        return MalSymbol.create(name);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalThrow.ck",
    "content": "public class MalThrow extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalError.create(args[0]);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalTimeMs.ck",
    "content": "public class MalTimeMs extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        // HACK: Std.system returns the status code only...\n        \"/tmp/chuck-date.\" + Std.rand2(1000,9999) => string temp_file;\n        Std.system(\"date +%s%3N > \" + temp_file);\n\n        FileIO f;\n        f.open(temp_file, FileIO.READ);\n        f => int timestamp;\n        f.close();\n\n        Std.system(\"rm \" + temp_file);\n\n        return MalInt.create(timestamp);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalVals.ck",
    "content": "public class MalVals extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0].malObjectValues() @=> MalObject map[];\n        MalObject results[0];\n\n        for( 1 => int i; i < map.size(); 2 +=> i )\n        {\n            results << map[i];\n        }\n\n        return MalList.create(results);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalVec.ck",
    "content": "public class MalVec extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        if (args.size() == 1) {\n            args[0] @=> MalObject a0;\n            if (a0.type == \"vector\") {\n                return a0;\n            } else if (a0.type == \"list\") {\n                return MalVector.create(a0.malObjectValues());\n            }\n        }\n        return MalError.create(\"vec: wrong arguments\");\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalVectorify.ck",
    "content": "public class MalVectorify extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        return MalVector.create(args);\n    }\n}\n"
  },
  {
    "path": "impls/chuck/types/subr/MalWithMeta.ck",
    "content": "public class MalWithMeta extends MalSubr\n{\n    fun MalObject call(MalObject args[])\n    {\n        args[0] @=> MalObject arg;\n        args[1] @=> MalObject meta;\n\n        MalObject value;\n        arg.clone() @=> value;\n\n        meta$Object @=> value.meta;\n\n        return value;\n    }\n}\n"
  },
  {
    "path": "impls/chuck/util/Constants.ck",
    "content": "public class Constants\n{\n    static MalTrue @ TRUE;\n    static MalFalse @ FALSE;\n    static MalNil @ NIL;\n}\n\nMalTrue.create() @=> Constants.TRUE;\nMalFalse.create() @=> Constants.FALSE;\nMalNil.create() @=> Constants.NIL;\n"
  },
  {
    "path": "impls/chuck/util/String.ck",
    "content": "public class String\n{\n    // \"x\".substring(1) errors out (bug?), this doesn't\n    fun static string slice(string input, int index)\n    {\n        if( index == input.length() )\n        {\n            return \"\";\n        }\n        else\n        {\n            return input.substring(index);\n        }\n    }\n\n    fun static string slice(string input, int start, int end)\n    {\n        if( start == input.length() )\n        {\n            return \"\";\n        }\n        else\n        {\n            return input.substring(start, end - start);\n        }\n    }\n\n    fun static string join(string parts[], string separator)\n    {\n        if( parts.size() == 0 )\n        {\n            return \"\";\n        }\n\n        parts[0] => string output;\n\n        for( 1 => int i; i < parts.size(); i++ )\n        {\n            output + separator + parts[i] => output;\n        }\n\n        return output;\n    }\n\n    fun static string[] split(string input, string separator)\n    {\n        string output[0];\n\n        if( input == \"\" )\n        {\n            return output;\n        }\n\n        0 => int offset;\n        int index;\n\n        while( true )\n        {\n            input.find(separator, offset) => index;\n\n            if( index == -1 )\n            {\n                output << input.substring(offset);\n                break;\n            }\n\n            output << input.substring(offset, index - offset);\n            index + separator.length() => offset;\n        }\n\n        return output;\n    }\n\n    fun static string replaceAll(string input, string pat, string rep)\n    {\n        0 => int offset;\n        input => string output;\n        int index;\n\n        while( true )\n        {\n            if( offset >= output.length() )\n            {\n                break;\n            }\n\n            output.find(pat, offset) => index;\n\n            if( index == -1 )\n            {\n                break;\n            }\n\n            output.replace(index, pat.length(), rep);\n            index + rep.length() => offset;\n        }\n\n        return output;\n    }\n\n    fun static string parse(string input)\n    {\n        slice(input, 1, input.length() - 1) => string output;\n        replaceAll(output, \"\\\\\\\\\", \"\\177\") => output;\n        replaceAll(output, \"\\\\\\\"\", \"\\\"\") => output;\n        replaceAll(output, \"\\\\n\", \"\\n\") => output;\n        replaceAll(output, \"\\177\", \"\\\\\") => output;\n        return output;\n    }\n\n    fun static string repr(string input)\n    {\n        input => string output;\n        replaceAll(output, \"\\\\\", \"\\\\\\\\\") => output;\n        replaceAll(output, \"\\n\", \"\\\\n\") => output;\n        replaceAll(output, \"\\\"\", \"\\\\\\\"\") => output;\n        return \"\\\"\" + output + \"\\\"\";\n    }\n}\n"
  },
  {
    "path": "impls/chuck/util/Util.ck",
    "content": "public class Util\n{\n    fun static void println(string message)\n    {\n        chout <= message + \"\\n\";\n    }\n\n    fun static void panic(string message)\n    {\n        println(\"This shouldn't happen because: \" + message);\n        Machine.crash();\n    }\n}\n"
  },
  {
    "path": "impls/clojure/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\n#\n# Clojure (Java and lein)\n#\n\nRUN apt-get -y install leiningen\nENV LEIN_HOME /mal/.lein\nENV LEIN_JVM_OPTS -Duser.home=/mal\n\n#\n# ClojureScript (Node and Lumo)\n#\n\n# For building node modules\nRUN apt-get -y install g++\n\n# Add nodesource apt repo config for 10.x stable\nRUN apt-get -y install gnupg\nRUN curl -sL https://deb.nodesource.com/setup_10.x | bash -\n\n# Install nodejs\nRUN apt-get -y install nodejs\n\nENV NPM_CONFIG_CACHE /mal/.npm\n\n## Install ffi and lumo-cljs modules globally\n#RUN npm install -g ffi lumo-cljs\n\nENV HOME=/mal\n"
  },
  {
    "path": "impls/clojure/Makefile",
    "content": "clojure_MODE ?= clj\nSOURCES_UTIL = src/mal/readline.$(clojure_MODE)\nSOURCES_BASE = $(SOURCES_UTIL) src/mal/reader.cljc src/mal/printer.cljc\nSOURCES_LISP = src/mal/env.cljc src/mal/core.cljc src/mal/stepA_mal.cljc\nSRCS = $(SOURCES_BASE) src/mal/env.cljc src/mal/core.cljc\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nDEPS = $(if $(filter cljs,$(clojure_MODE)),node_modules,deps)\n\ndist: $(if $(filter cljs,$(clojure_MODE)),node_modules,mal.jar mal)\n\ndeps:\n\tlein deps\n\nmal.jar: $(SOURCES)\n\tlein with-profile stepA uberjar\n\tcp target/stepA_mal.jar $@\n\nSHELL := bash\nmal: mal.jar\n\tcat <(echo -e '#!/bin/sh\\nexec java -jar \"$$0\" \"$$@\"') mal.jar > $@\n\tchmod +x mal\n\nsrc/mal/%.cljc: $(DEPS)\n\t@true\n\n#src/mal/stepA_mal.cljc: $(DEPS)\n\ntarget/%.jar: src/mal/%.cljc $(SRCS)\n\tlein with-profile $(word 1,$(subst _, ,$*)) uberjar\n\nnode_modules:\n\tnpm install\n\nclean:\n\trm -rf target/ mal.jar mal\n"
  },
  {
    "path": "impls/clojure/package.json",
    "content": "{\n    \"name\": \"mal\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in ClojureScript\",\n    \"dependencies\": {\n        \"ffi-napi\": \"2.4.x\",\n        \"lumo-cljs\": \"1.10.1\"\n    }\n}\n"
  },
  {
    "path": "impls/clojure/project.clj",
    "content": "(defproject mal \"0.0.1-SNAPSHOT\"\n  :description \"Make-A-Lisp\"\n\n  :dependencies [[org.clojure/clojure \"1.10.0\"]\n                 [net.n01se/clojure-jna \"1.0.0\"]]\n\n  ;; To run a step with correct readline behavior:\n  ;;   lein trampoline with-profile stepX run\n  ;; To generate a executable uberjar (in target/) for a step:\n  ;;   lein with-profile stepX repl\n  :profiles {:step0 {:main mal.step0-repl\n                     :uberjar-name \"step0_repl.jar\"\n                     :aot [mal.step0-repl]}\n             :step1 {:main mal.step1-read-print\n                     :uberjar-name \"step1_read_print.jar\"\n                     :aot [mal.step1-read-print]}\n             :step2 {:main mal.step2-eval\n                     :uberjar-name \"step2_eval.jar\"\n                     :aot [mal.step2-eval]}\n             :step3 {:main mal.step3-env\n                     :uberjar-name \"step3_env.jar\"\n                     :aot [mal.step3-env]}\n             :step4 {:main mal.step4-if-fn-do\n                     :uberjar-name \"step4_if_fn_do.jar\"\n                     :aot [mal.step4-if-fn-do]}\n             :step5 {:main mal.step5-tco\n                     :uberjar-name \"step5_tco.jar\"\n                     :aot [mal.step5-tco]}\n             :step6 {:main mal.step6-file\n                     :uberjar-name \"step6_file.jar\"\n                     :aot [mal.step6-file]}\n             :step7 {:main mal.step7-quote\n                     :uberjar-name \"step7_quote.jar\"\n                     :aot [mal.step7-quote]}\n             :step8 {:main mal.step8-macros\n                     :uberjar-name \"step8_macros.jar\"\n                     :aot [mal.step8-macros]}\n             :step9 {:main mal.step9-try\n                     :uberjar-name \"step9_try.jar\"\n                     :aot [mal.step9-try]}\n             :stepA {:main mal.stepA-mal\n                     :uberjar-name \"stepA_mal.jar\"\n                     :aot [mal.stepA-mal]}})\n\n"
  },
  {
    "path": "impls/clojure/run",
    "content": "#!/usr/bin/env bash\nexport PATH=$PATH:$(dirname $0)/node_modules/.bin\nSTEP=${STEP:-stepA_mal}\nif [ \"${clojure_MODE}\" = \"cljs\" ]; then\n  exec lumo -c $(dirname $0)/src -m mal.${STEP//_/-} \"${@}\"\nelse\n  exec java -jar $(dirname $0)/target/${STEP}.jar \"${@}\"\nfi\n"
  },
  {
    "path": "impls/clojure/src/mal/core.cljc",
    "content": "(ns mal.core\n  (:refer-clojure :exclude [pr-str])\n  (:require [clojure.string :refer [join]]\n            [mal.readline :as readline]\n            [mal.reader :as reader]\n            [mal.printer :refer [pr-str atom?]]))\n\n;; Errors/exceptions\n(defn mal_throw [obj]\n  (throw (ex-info \"mal exception\" {:data obj})))\n\n;; String functions\n#?(:cljs (defn slurp [f] (.readFileSync (js/require \"fs\") f \"utf-8\")))\n\n;; Numeric functions\n#?(:clj  (defn time-ms [] (System/currentTimeMillis))\n   :cljs (defn time-ms [] (.getTime (js/Date.))))\n\n;; Metadata functions\n;; - store metadata at :meta key of the real metadata\n(defn mal_with_meta [obj m]\n  (let [new-meta (assoc (meta obj) :meta m)]\n    (with-meta obj new-meta)))\n\n(defn mal_meta [obj]\n  (:meta (meta obj)))\n\n;; core_ns is core namespaces functions\n(def core_ns\n  [['= =]\n   ['throw mal_throw]\n   ['nil? nil?]\n   ['true? true?]\n   ['false? false?]\n   ['string? string?]\n   ['symbol symbol]\n   ['symbol? symbol?]\n   ['keyword keyword]\n   ['keyword? keyword?]\n   ['number? number?]\n   ['fn? (fn [o] (if (and (fn? o) (not (:ismacro (meta o)))) true false))]\n   ['macro? (fn [o] (if (and (fn? o) (:ismacro (meta o))) true false))]\n\n   ['pr-str (fn [& xs] (join \" \" (map #(pr-str % true) xs)))]\n   ['str (fn [& xs] (join \"\" (map #(pr-str % false) xs)))]\n   ['prn (fn [& xs] (println (join \" \" (map #(pr-str % true) xs))))]\n   ['println (fn [& xs] (println (join \" \" (map #(pr-str % false) xs))))]\n   ['readline readline/readline]\n   ['read-string reader/read-string]\n   ['slurp slurp]\n   ['< <]\n   ['<= <=]\n   ['> >]\n   ['>= >=]\n   ['+ +]\n   ['- -]\n   ['* *]\n   ['/ /]\n   ['time-ms time-ms]\n\n   ['list list]\n   ['list? seq?]\n   ['vector vector]\n   ['vector? vector?]\n   ['hash-map hash-map]\n   ['map? map?]\n   ['assoc assoc]\n   ['dissoc dissoc]\n   ['get get]\n   ['contains? contains?]\n   ['keys (fn [hm] (let [ks (keys hm)] (if (nil? ks) '() ks)))]\n   ['vals (fn [hm] (let [vs (vals hm)] (if (nil? vs) '() vs)))]\n\n   ['sequential? sequential?]\n   ['vec vec]\n   ['cons cons]\n   ['concat #(apply list (apply concat %&))]\n   ['nth nth]\n   ['first first]\n   ['rest rest]\n   ['empty? empty?]\n   ['count count]\n   ['apply apply]\n   ['map #(apply list (map %1 %2))]\n\n   ['conj conj]\n   ['seq (fn [obj] (seq (if (string? obj) (map str obj) obj)))]\n\n   ['with-meta mal_with_meta]\n   ['meta mal_meta]\n   ['atom atom]\n   ['atom? atom?]\n   ['deref deref]\n   ['reset! reset!]\n   ['swap! swap!]])\n"
  },
  {
    "path": "impls/clojure/src/mal/env.cljc",
    "content": "(ns mal.env)\n\n(defn env [& [outer binds exprs]]\n  ;;(prn \"env\" binds exprs)\n  ;; (when (not= (count binds) (count exprs))\n  ;;  (throw (Exception. \"Arity mistmatch in env call\")))\n  (atom\n    (loop [env {:outer outer}\n           b binds\n           e exprs]\n      (cond\n        (= nil b)\n        env\n\n        (= '& (first b))\n        (assoc env (nth b 1) e)\n\n        :else\n        (recur (assoc env (first b) (first e)) (next b) (rest e))))))\n\n(defn env-find [env k]\n  (cond\n    (contains? @env k) env\n    (:outer @env) (env-find (:outer @env) k)\n    :else nil))\n\n(defn env-get [env k]\n  (let [e (env-find env k)]\n    (when-not e\n      (throw (#?(:clj Exception.\n                 :cljs js/Error.) (str \"'\" k \"' not found\"))))\n    (get @e k)))\n\n(defn env-set [env k v]\n  (swap! env assoc k v)\n  v)\n"
  },
  {
    "path": "impls/clojure/src/mal/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nvar ffi = require('ffi-napi'),\n    fs = require('fs');\n\nvar rllib = ffi.Library(RL_LIB, {\n    'readline':    [ 'string', [ 'string' ] ],\n    'add_history': [ 'int',    [ 'string' ] ]});\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = typeof prompt !== 'undefined' ? prompt : \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { rllib.add_history(lines[i]); }\n        }\n    }\n\n    var line = rllib.readline(prompt);\n    if (line) {\n        rllib.add_history(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\nvar readline = exports;\n"
  },
  {
    "path": "impls/clojure/src/mal/printer.cljc",
    "content": "(ns mal.printer\n  (:refer-clojure :exclude [pr-str])\n  (:require [clojure.string :as S]))\n\n;; atom?\n#?(:clj  (defn atom? [atm] (= (type atm) clojure.lang.Atom))\n   :cljs (defn atom? [atm] (satisfies? IAtom atm)))\n\n(defn escape [s]\n  (-> s (S/replace \"\\\\\" \"\\\\\\\\\")\n        (S/replace \"\\\"\" \"\\\\\\\"\")\n        (S/replace \"\\n\" \"\\\\n\")))\n\n(defn pr-str\n  ([obj] (pr-str obj true))\n  ([obj print-readably]\n   (let [_r print-readably]\n     (cond\n       (= nil obj)   \"nil\"\n       (string? obj) (if _r (str \"\\\"\" (escape obj) \"\\\"\") obj)\n\n       (list? obj)   (str \"(\" (S/join \" \" (map #(pr-str % _r) obj)) \")\")\n       (vector? obj) (str \"[\" (S/join \" \" (map #(pr-str % _r) obj)) \"]\")\n       (map? obj)    (str \"{\" (S/join \" \" (map (fn [[k v]]\n                                                 (str (pr-str k _r) \" \"\n                                                      (pr-str v _r))) obj)) \"}\")\n       (atom? obj)   (str \"(atom \" (pr-str @obj _r) \")\")\n       :else         (str obj)))))\n\n"
  },
  {
    "path": "impls/clojure/src/mal/reader.cljc",
    "content": "(ns mal.reader\n  (:refer-clojure :exclude [read-string])\n  (:require [clojure.string :as S]))\n\n(defn throw-str [s]\n  (throw #?(:cljs (js/Error. s)\n           :clj (Exception. s))))\n\n(defn rdr [tokens]\n  {:tokens (vec tokens) :position (atom 0)})\n\n(defn rdr-peek [rdr]\n  (get (vec (:tokens rdr)) @(:position rdr)))\n\n(defn rdr-next [rdr]\n  (get (vec (:tokens rdr)) (dec (swap! (:position rdr) inc))))\n\n(def tok-re #\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\\\"(?:[\\\\].|[^\\\\\\\"])*\\\"?|;.*|[^\\s\\[\\]{}()'\\\"`@,;]+)\")\n(def int-re #\"^-?[0-9]+$\")\n(def str-re #\"^\\\"((?:[\\\\].|[^\\\\\\\"])*)\\\"$\")\n(def badstr-re #\"^\\\"\")\n\n(defn tokenize [s]\n  (filter #(not= \\; (first %))\n          (map second (re-seq tok-re s))))\n\n(defn unescape [s]\n  (-> s (S/replace \"\\\\\\\\\" \"\\u029e\")\n        (S/replace \"\\\\\\\"\" \"\\\"\")\n        (S/replace \"\\\\n\" \"\\n\")\n        (S/replace \"\\u029e\" \"\\\\\")))\n\n(defn read-atom [rdr]\n  (let [token (rdr-next rdr)]\n    (cond\n     (re-seq int-re token)    #?(:cljs (js/parseInt token)\n                                 :clj (Integer/parseInt token))\n     (re-seq str-re token)    (unescape (second (re-find str-re token)))\n     (re-seq badstr-re token) (throw-str (str \"expected '\\\"', got EOF\"))\n     (= \\: (get token 0))     (keyword (subs token 1))\n     (= \"nil\" token)          nil\n     (= \"true\" token)         true\n     (= \"false\" token)        false\n     :else                    (symbol token))))\n\n(declare read-form)\n\n(defn read-seq [rdr start end]\n  (assert (= start (rdr-next rdr))) ;; pull off start\n  (loop [lst []]\n    (let [token (rdr-peek rdr)]\n      (cond\n        (= token end) (do (rdr-next rdr) lst)\n        (not token) (throw-str (str \"expected '\" end \"', got EOF\"))\n        :else (recur (conj lst (read-form rdr)))))))\n\n(defn read-form [rdr]\n  (let [tok (rdr-peek rdr)]\n    (cond\n      (= \"'\" tok)  (do (rdr-next rdr) (list 'quote (read-form rdr)))\n      (= \"`\" tok)  (do (rdr-next rdr) (list 'quasiquote (read-form rdr)))\n      (= \"~\" tok)  (do (rdr-next rdr) (list 'unquote (read-form rdr)))\n      (= \"~@\" tok) (do (rdr-next rdr) (list 'splice-unquote (read-form rdr)))\n\n      (= \"^\" tok)  (do (rdr-next rdr) (let [m (read-form rdr)]\n                                          (list 'with-meta (read-form rdr) m)))\n      (= \"@\" tok)  (do (rdr-next rdr) (list 'deref (read-form rdr)))\n\n      (= \")\" tok)  (throw-str \"unexpected ')'\")\n      (= \"(\" tok)  (apply list (read-seq rdr \"(\" \")\"))\n\n      (= \"]\" tok)  (throw-str \"unexpected ']'\")\n      (= \"[\" tok)  (vec (read-seq rdr \"[\" \"]\"))\n\n      (= \"}\" tok)  (throw-str \"unexpected '}'\")\n      (= \"{\" tok)  (apply hash-map (read-seq rdr \"{\" \"}\"))\n\n      :else        (read-atom rdr))))\n\n(defn read-string [s]\n  (read-form (rdr (tokenize s))))\n"
  },
  {
    "path": "impls/clojure/src/mal/readline.clj",
    "content": "(ns mal.readline\n    (:require [clojure.string :refer [split]]\n              [clojure.java.io :refer [file]]\n              [net.n01se.clojure-jna :as jna]))\n\n(defonce history-loaded (atom nil))\n(def HISTORY-FILE (str (System/getProperty \"user.home\") \"/.mal-history\"))\n\n;;\n;; Uncomment one of the following readline libraries\n;;\n\n;; editline (BSD)\n#_\n(do\n  (def readline-call (jna/to-fn String edit/readline))\n  (def add-history (jna/to-fn Void edit/add_history))\n  (def load-history #(doseq [line (split (slurp %) #\"\\n\")]\n                       (jna/invoke Void edit/add_history line))))\n\n;; GNU Readline (GPL)\n;; WARNING: distributing your code with GNU readline enabled means you\n;; must release your program as GPL\n;#_\n(do \n  (def readline-call (jna/to-fn String readline/readline))\n  (def add-history (jna/to-fn Void readline/add_history))\n  (def load-history (jna/to-fn Integer readline/read_history)))\n\n(defn readline [prompt & [lib]]\n  (when (not @history-loaded)\n    (reset! history-loaded true)\n    (when (.canRead (file HISTORY-FILE))\n      (load-history HISTORY-FILE)))\n  (let [line (readline-call prompt)]\n    (when line\n      (add-history line)\n      (when (.canWrite (file HISTORY-FILE))\n        (spit HISTORY-FILE (str line \"\\n\") :append true)))\n    line))\n"
  },
  {
    "path": "impls/clojure/src/mal/readline.cljs",
    "content": "(ns mal.readline)\n\n(def readline (.-readline (js/require \"../src/mal/node_readline.js\")))\n"
  },
  {
    "path": "impls/clojure/src/mal/step0_repl.cljc",
    "content": "(ns mal.step0-repl\n  (:require [mal.readline :as readline])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  strng)\n\n;; eval\n(defn EVAL [ast env]\n  ast)\n\n;; print\n(defn PRINT [exp]\n  exp)\n\n;; repl\n(defn rep [strng] (PRINT (EVAL (READ strng), {})))\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (println (rep line)))\n      (recur))))\n\n(defn -main [& args]\n  (repl-loop))\n"
  },
  {
    "path": "impls/clojure/src/mal/step1_read_print.cljc",
    "content": "(ns mal.step1-read-print\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(defn EVAL [ast env]\n  ast)\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) {})))\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (repl-loop))\n"
  },
  {
    "path": "impls/clojure/src/mal/step2_eval.cljc",
    "content": "(ns mal.step2-eval\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(defn EVAL [ast env]\n\n  ;; (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n  ;; (flush)\n\n  (cond\n    (symbol? ast) (or (get env ast)\n                      (throw (#?(:clj Error.\n                                 :cljs js/Error.) (str ast \" not found\"))))\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n            ;; indented to match later steps\n            (if (empty? ast)\n              ast\n              (let [el (map #(EVAL % env) ast)\n                    f (first el)\n                    args (rest el)]\n                (apply f args)))\n\n    :else ;; not a list, map, symbol or vector\n    ast))\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env {'+ +\n               '- -\n               '* *\n               '/ /})\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (repl-loop))\n"
  },
  {
    "path": "impls/clojure/src/mal/step3_env.cljc",
    "content": "(ns mal.step3-env\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(defn EVAL [ast env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (EVAL a2 let-env))\n\n              ;; apply\n              (let [el (map #(EVAL % env) ast)\n                    f (first el)\n                    args (rest el)]\n                (apply f args))))\n\n    :else ;; not a list, map, symbol or vector\n    ast))\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n(env/env-set repl-env '+ +)\n(env/env-set repl-env '- -)\n(env/env-set repl-env '* *)\n(env/env-set repl-env '/ /)\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (repl-loop))\n"
  },
  {
    "path": "impls/clojure/src/mal/step4_if_fn_do.cljc",
    "content": "(ns mal.step4-if-fn-do\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(defn EVAL [ast env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (EVAL a2 let-env))\n\n              'do\n              (last (doall (map #(EVAL % env) (rest ast))))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (EVAL a3 env)\n                    nil)\n                  (EVAL a2 env)))\n\n              'fn*\n              (fn [& args]\n                (EVAL a2 (env/env env a1 (or args '()))))\n\n              ;; apply\n              (let [el (map #(EVAL % env) ast)\n                    f (first el)\n                    args (rest el)]\n                (apply f args))))\n\n    :else ;; not a list, map, symbol or vector\n    ast))\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* [a] (if a false true)))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (repl-loop))\n"
  },
  {
    "path": "impls/clojure/src/mal/step5_tco.cljc",
    "content": "(ns mal.step5-tco\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(defn EVAL [ast env]\n  (loop [ast ast\n         env env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (recur a2 let-env))\n\n              'do\n              (do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))\n                  (recur (last ast) env))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (recur a3 env)\n                    nil)\n                  (recur a2 env)))\n\n              'fn*\n              (with-meta\n                (fn [& args]\n                  (EVAL a2 (env/env env a1 (or args '()))))\n                {:expression a2\n                 :environment env\n                 :parameters a1})\n\n              ;; apply\n              (let [el (map #(EVAL % env) ast)\n                    f (first el)\n                    args (rest el)\n                    {:keys [expression environment parameters]} (meta f)]\n                (if expression\n                  (recur expression (env/env environment parameters args))\n                  (apply f args)))))\n\n    :else ;; not a list, map, symbol or vector\n    ast)))\n\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* [a] (if a false true)))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (repl-loop))\n"
  },
  {
    "path": "impls/clojure/src/mal/step6_file.cljc",
    "content": "(ns mal.step6-file\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(defn EVAL [ast env]\n  (loop [ast ast\n         env env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (recur a2 let-env))\n\n              'do\n              (do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))\n                  (recur (last ast) env))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (recur a3 env)\n                    nil)\n                  (recur a2 env)))\n\n              'fn*\n              (with-meta\n                (fn [& args]\n                  (EVAL a2 (env/env env a1 (or args '()))))\n                {:expression a2\n                 :environment env\n                 :parameters a1})\n\n              ;; apply\n              (let [el (map #(EVAL % env) ast)\n                    f (first el)\n                    args (rest el)\n                    {:keys [expression environment parameters]} (meta f)]\n                (if expression\n                  (recur expression (env/env environment parameters args))\n                  (apply f args)))))\n\n    :else ;; not a list, map, symbol or vector\n    ast)))\n\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env)))\n(env/env-set repl-env '*ARGV* ())\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* [a] (if a false true)))\")\n(rep \"(def! load-file (fn* [f] (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (env/env-set repl-env '*ARGV* (rest args))\n  (if args\n    (rep (str \"(load-file \\\"\" (first args) \"\\\")\"))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/clojure/src/mal/step7_quote.cljc",
    "content": "(ns mal.step7-quote\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(declare quasiquote)\n(defn starts_with [ast sym]\n  (and (seq? ast)\n       (= (first ast) sym)))\n(defn qq-iter [seq]\n  (if (empty? seq)\n    ()\n    (let [elt (first seq)\n          acc (qq-iter (rest seq))]\n      (if (starts_with elt 'splice-unquote)\n        (list 'concat (second elt)     acc)\n        (list 'cons   (quasiquote elt) acc)))))\n(defn quasiquote [ast]\n  (cond (starts_with ast 'unquote) (second ast)\n        (seq? ast)                 (qq-iter ast)\n        (vector? ast)              (list 'vec (qq-iter ast))\n        (or (symbol? ast) (map? ast)) (list 'quote ast)\n        :else                         ast))\n\n(defn EVAL [ast env]\n  (loop [ast ast\n         env env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (recur a2 let-env))\n\n              'quote\n              a1\n\n              'quasiquote\n              (recur (quasiquote a1) env)\n\n              'do\n              (do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))\n                  (recur (last ast) env))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (recur a3 env)\n                    nil)\n                  (recur a2 env)))\n\n              'fn*\n              (with-meta\n                (fn [& args]\n                  (EVAL a2 (env/env env a1 (or args '()))))\n                {:expression a2\n                 :environment env\n                 :parameters a1})\n\n              ;; apply\n              (let [el (map #(EVAL % env) ast)\n                    f (first el)\n                    args (rest el)\n                    {:keys [expression environment parameters]} (meta f)]\n                (if expression\n                  (recur expression (env/env environment parameters args))\n                  (apply f args)))))\n\n    :else ;; not a list, map, symbol or vector\n    ast)))\n\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env)))\n(env/env-set repl-env '*ARGV* ())\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* [a] (if a false true)))\")\n(rep \"(def! load-file (fn* [f] (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (env/env-set repl-env '*ARGV* (rest args))\n  (if args\n    (rep (str \"(load-file \\\"\" (first args) \"\\\")\"))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/clojure/src/mal/step8_macros.cljc",
    "content": "(ns mal.step8-macros\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(declare quasiquote)\n(defn starts_with [ast sym]\n  (and (seq? ast)\n       (= (first ast) sym)))\n(defn qq-iter [seq]\n  (if (empty? seq)\n    ()\n    (let [elt (first seq)\n          acc (qq-iter (rest seq))]\n      (if (starts_with elt 'splice-unquote)\n        (list 'concat (second elt)     acc)\n        (list 'cons   (quasiquote elt) acc)))))\n(defn quasiquote [ast]\n  (cond (starts_with ast 'unquote) (second ast)\n        (seq? ast)                 (qq-iter ast)\n        (vector? ast)              (list 'vec (qq-iter ast))\n        (or (symbol? ast) (map? ast)) (list 'quote ast)\n        :else                         ast))\n\n(defn EVAL [ast env]\n  (loop [ast ast\n         env env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (recur a2 let-env))\n\n              'quote\n              a1\n\n              'quasiquote\n              (recur (quasiquote a1) env)\n\n              'defmacro!\n              (let [func (EVAL a2 env)\n                    ;; Preserve unadorned function to workaround\n                    ;; ClojureScript function-with-meta arity limit\n                    mac (with-meta func {:orig (:orig (meta func))\n                                         :ismacro true})]\n                (env/env-set env a1 mac))\n\n              'do\n              (do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))\n                  (recur (last ast) env))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (recur a3 env)\n                    nil)\n                  (recur a2 env)))\n\n              'fn*\n              (let [func (fn [& args]\n                           (EVAL a2 (env/env env a1 (or args '()))))]\n                (with-meta\n                  func\n                  ;; Preserve unadorned function to workaround\n                  ;; ClojureScript function-with-meta arity limit\n                  {:orig func\n                   :expression a2\n                   :environment env\n                   :parameters a1}))\n\n              ;; apply\n              (let [f (EVAL a0 env)\n                    unevaluated_args (rest ast)]\n               (if (:ismacro (meta f))\n                (recur (apply (:orig (meta f)) unevaluated_args) env)\n                (let [args (map #(EVAL % env) unevaluated_args)\n                    {:keys [expression environment parameters]} (meta f)]\n                (if expression\n                  (recur expression (env/env environment parameters args))\n                  (apply f args)))))))\n\n    :else ;; not a list, map, symbol or vector\n    ast)))\n\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env)))\n(env/env-set repl-env '*ARGV* ())\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* [a] (if a false true)))\")\n(rep \"(def! load-file (fn* [f] (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (env/env-set repl-env '*ARGV* (rest args))\n  (if args\n    (rep (str \"(load-file \\\"\" (first args) \"\\\")\"))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/clojure/src/mal/step9_try.cljc",
    "content": "(ns mal.step9-try\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(declare quasiquote)\n(defn starts_with [ast sym]\n  (and (seq? ast)\n       (= (first ast) sym)))\n(defn qq-iter [seq]\n  (if (empty? seq)\n    ()\n    (let [elt (first seq)\n          acc (qq-iter (rest seq))]\n      (if (starts_with elt 'splice-unquote)\n        (list 'concat (second elt)     acc)\n        (list 'cons   (quasiquote elt) acc)))))\n(defn quasiquote [ast]\n  (cond (starts_with ast 'unquote) (second ast)\n        (seq? ast)                 (qq-iter ast)\n        (vector? ast)              (list 'vec (qq-iter ast))\n        (or (symbol? ast) (map? ast)) (list 'quote ast)\n        :else                         ast))\n\n(defn EVAL [ast env]\n  (loop [ast ast\n         env env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (recur a2 let-env))\n\n              'quote\n              a1\n\n              'quasiquote\n              (recur (quasiquote a1) env)\n\n              'defmacro!\n              (let [func (EVAL a2 env)\n                    ;; Preserve unadorned function to workaround\n                    ;; ClojureScript function-with-meta arity limit\n                    mac (with-meta func {:orig (:orig (meta func))\n                                         :ismacro true})]\n                (env/env-set env a1 mac))\n\n              'try*\n              (if (= 'catch* (nth a2 0))\n                (try\n                  (EVAL a1 env)\n                  (catch #?(:clj  clojure.lang.ExceptionInfo\n                            :cljs ExceptionInfo) ei\n                    (EVAL (nth a2 2) (env/env env\n                                              [(nth a2 1)]\n                                              [(:data (ex-data ei))])))\n                  (catch #?(:clj Throwable :cljs :default) t\n                    (EVAL (nth a2 2) (env/env env\n                                              [(nth a2 1)]\n                                              [#?(:clj (or (.getMessage t)\n                                                           (.toString t))\n                                                  :cljs (.-message t))]))))\n                (EVAL a1 env))\n\n              'do\n              (do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))\n                  (recur (last ast) env))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (recur a3 env)\n                    nil)\n                  (recur a2 env)))\n\n              'fn*\n              (let [func (fn [& args]\n                           (EVAL a2 (env/env env a1 (or args '()))))]\n                (with-meta\n                  func\n                  ;; Preserve unadorned function to workaround\n                  ;; ClojureScript function-with-meta arity limit\n                  {:orig func\n                   :expression a2\n                   :environment env\n                   :parameters a1}))\n\n              ;; apply\n              (let [f (EVAL a0 env)\n                    unevaluated_args (rest ast)]\n               (if (:ismacro (meta f))\n                (recur (apply (:orig (meta f)) unevaluated_args) env)\n                (let [args (map #(EVAL % env) unevaluated_args)\n                    {:keys [expression environment parameters]} (meta f)]\n                (if expression\n                  (recur expression (env/env environment parameters args))\n                  (apply f args)))))))\n\n    :else ;; not a list, map, symbol or vector\n    ast)))\n\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env)))\n(env/env-set repl-env '*ARGV* ())\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* [a] (if a false true)))\")\n(rep \"(def! load-file (fn* [f] (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:cljs (catch ExceptionInfo e\n                     (println \"Error:\" (or (:data (ex-data e))\n                                           (.-stack e)))))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (env/env-set repl-env '*ARGV* (rest args))\n  (if args\n    (rep (str \"(load-file \\\"\" (first args) \"\\\")\"))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/clojure/src/mal/stepA_mal.cljc",
    "content": "(ns mal.stepA-mal\n  (:require [mal.readline :as readline]\n            #?(:clj [clojure.repl])\n            [mal.reader :as reader]\n            [mal.printer :as printer]\n            [mal.env :as env]\n            [mal.core :as core])\n  #?(:clj (:gen-class)))\n\n;; read\n(defn READ [& [strng]]\n  (reader/read-string strng))\n\n;; eval\n(declare quasiquote)\n(defn starts_with [ast sym]\n  (and (seq? ast)\n       (= (first ast) sym)))\n(defn qq-iter [seq]\n  (if (empty? seq)\n    ()\n    (let [elt (first seq)\n          acc (qq-iter (rest seq))]\n      (if (starts_with elt 'splice-unquote)\n        (list 'concat (second elt)     acc)\n        (list 'cons   (quasiquote elt) acc)))))\n(defn quasiquote [ast]\n  (cond (starts_with ast 'unquote) (second ast)\n        (seq? ast)                 (qq-iter ast)\n        (vector? ast)              (list 'vec (qq-iter ast))\n        (or (symbol? ast) (map? ast)) (list 'quote ast)\n        :else                         ast))\n\n(defn EVAL [ast env]\n  (loop [ast ast\n         env env]\n\n  (let [e (env/env-find env 'DEBUG-EVAL)]\n    (when e\n      (let [v (env/env-get e 'DEBUG-EVAL)]\n        (when (and (not= v nil)\n                   (not= v false))\n          (println \"EVAL:\" (printer/pr-str ast) (keys @env))\n          (flush)))))\n\n  (cond\n    (symbol? ast) (env/env-get env ast)\n\n    (vector? ast) (vec (map #(EVAL % env) ast))\n\n    (map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))\n\n    (seq? ast)\n      ;; apply list\n          ;; indented to match later steps\n          (let [[a0 a1 a2 a3] ast]\n            (condp = a0\n              nil\n              ast\n\n              'def!\n              (env/env-set env a1 (EVAL a2 env))\n\n              'let*\n              (let [let-env (env/env env)]\n                (doseq [[b e] (partition 2 a1)]\n                  (env/env-set let-env b (EVAL e let-env)))\n                (recur a2 let-env))\n\n              'quote\n              a1\n\n              'quasiquote\n              (recur (quasiquote a1) env)\n\n              'defmacro!\n              (let [func (EVAL a2 env)\n                    ;; Preserve unadorned function to workaround\n                    ;; ClojureScript function-with-meta arity limit\n                    mac (with-meta func {:orig (:orig (meta func))\n                                         :ismacro true})]\n                (env/env-set env a1 mac))\n\n              'clj*\n              #?(:clj  (eval (reader/read-string a1))\n                 :cljs (throw (ex-info \"clj* unsupported in ClojureScript mode\" {})))\n\n              'js*\n              #?(:clj  (throw (ex-info \"js* unsupported in Clojure mode\" {}))\n                 :cljs (js->clj (js/eval a1)))\n\n              'try*\n              (if (= 'catch* (nth a2 0))\n                (try\n                  (EVAL a1 env)\n                  (catch #?(:clj  clojure.lang.ExceptionInfo\n                            :cljs ExceptionInfo) ei\n                    (EVAL (nth a2 2) (env/env env\n                                              [(nth a2 1)]\n                                              [(:data (ex-data ei))])))\n                  (catch #?(:clj Throwable :cljs :default) t\n                    (EVAL (nth a2 2) (env/env env\n                                              [(nth a2 1)]\n                                              [#?(:clj (or (.getMessage t)\n                                                           (.toString t))\n                                                  :cljs (.-message t))]))))\n                (EVAL a1 env))\n\n              'do\n              (do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))\n                  (recur (last ast) env))\n\n              'if\n              (let [cond (EVAL a1 env)]\n                (if (or (= cond nil) (= cond false))\n                  (if (> (count ast) 2)\n                    (recur a3 env)\n                    nil)\n                  (recur a2 env)))\n\n              'fn*\n              (let [func (fn [& args]\n                           (EVAL a2 (env/env env a1 (or args '()))))]\n                (with-meta\n                  func\n                  ;; Preserve unadorned function to workaround\n                  ;; ClojureScript function-with-meta arity limit\n                  {:orig func\n                   :expression a2\n                   :environment env\n                   :parameters a1}))\n\n              ;; apply\n              (let [f (EVAL a0 env)\n                    unevaluated_args (rest ast)]\n               (if (:ismacro (meta f))\n                (recur (apply (:orig (meta f)) unevaluated_args) env)\n                (let [args (map #(EVAL % env) unevaluated_args)\n                    {:keys [expression environment parameters]} (meta f)]\n                (if expression\n                  (recur expression (env/env environment parameters args))\n                  (apply f args)))))))\n\n    :else ;; not a list, map, symbol or vector\n    ast)))\n\n\n;; print\n(defn PRINT [exp] (printer/pr-str exp))\n\n;; repl\n(def repl-env (env/env))\n(defn rep\n  [strng]\n  (PRINT (EVAL (READ strng) repl-env)))\n\n;; core.clj: defined using Clojure\n(doseq [[k v] core/core_ns] (env/env-set repl-env k v))\n(env/env-set repl-env 'eval (fn [ast] (EVAL ast repl-env)))\n(env/env-set repl-env '*ARGV* ())\n\n;; core.mal: defined using the language itself\n#?(:clj  (rep \"(def! *host-language* \\\"clojure\\\")\")\n   :cljs (rep \"(def! *host-language* \\\"clojurescript\\\")\"))\n(rep \"(def! not (fn* [a] (if a false true)))\")\n(rep \"(def! load-file (fn* [f] (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n;; repl loop\n(defn repl-loop []\n  (let [line (readline/readline \"user> \")]\n    (when line\n      (when-not (re-seq #\"^\\s*$|^\\s*;.*$\" line) ; blank/comment\n        (try\n          (println (rep line))\n          #?(:cljs (catch ExceptionInfo e\n                     (println \"Error:\" (or (:data (ex-data e))\n                                           (.-stack e)))))\n          #?(:clj  (catch Throwable e (clojure.repl/pst e))\n             :cljs (catch js/Error e (println (.-stack e))))))\n      (recur))))\n\n(defn -main [& args]\n  (env/env-set repl-env '*ARGV* (rest args))\n  (if args\n    (rep (str \"(load-file \\\"\" (first args) \"\\\")\"))\n    (do\n      (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n      (repl-loop))))\n"
  },
  {
    "path": "impls/clojure/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/clojure/tests/stepA_mal.mal",
    "content": ";; Testing basic clojure/clojurescript interop\n\n(def! clj (= *host-language* \"clojure\"))\n(def! cljs (= *host-language* \"clojurescript\"))\n\n(if clj (clj* \"7\") (js* \"7\"))\n;=>7\n\n(if clj (clj* \"\\\"abc\\\"\") (js* \"\\\"abc\\\"\"))\n;=>\"abc\"\n\n(if clj (clj* \"{\\\"abc\\\" 123}\") {\"abc\" 123})\n;=>{\"abc\" 123}\n\n(if clj (clj* \"(prn \\\"foo\\\")\") (js* \"console.log('\\\"foo\\\"')\"))\n;/\"foo\"\n;=>nil\n\n(if clj (clj* \"(apply list (for [x [1 2 3]] (+ 1 x)))\") '(2 3 4))\n;=>(2 3 4)\n(if cljs (js* \"[1,2,3].map(function(x) {return 1+x})\") [2 3 4])\n;=>[2 3 4]\n"
  },
  {
    "path": "impls/coffee/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ libreadline-dev nodejs npm\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install coffeescript\nENV NPM_CONFIG_CACHE /mal/.npm\nRUN touch /.coffee_history && chmod go+w /.coffee_history\n"
  },
  {
    "path": "impls/coffee/Makefile",
    "content": "SOURCES_BASE = node_readline.coffee types.coffee \\\n\t       reader.coffee printer.coffee\nSOURCES_LISP = env.coffee core.coffee stepA_mal.coffee\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nSTEPS = step0_repl.coffee step1_read_print.coffee \\\n\tstep2_eval.coffee step3_env.coffee step4_if_fn_do.coffee \\\n\tstep5_tco.coffee step6_file.coffee step7_quote.coffee \\\n\tstep8_macros.coffee step9_try.coffee stepA_mal.coffee\n\nall: node_modules dist\n\nnode_modules:\n\tnpm install\n\n$(STEPS): node_modules\n\ndist: mal.coffee mal\n\nmal.coffee: $(SOURCES)\n\tcat $+ | grep -v \"= *require('./\" > $@\n\nmal: mal.coffee\n\techo \"#!/usr/bin/env coffee\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.coffee mal\n\n"
  },
  {
    "path": "impls/coffee/core.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\n[_pr_str, println] = [printer._pr_str, printer.println]\n\n# Sequence functions\nconj = (seq, args...) ->\n  switch types._obj_type(seq)\n    when 'list'\n      lst = types._clone(seq)\n      lst.unshift(x) for x in args\n      lst\n    when 'vector'\n      lst = types._clone(seq)\n      lst.push(args...)\n      types._vector(lst...)\n    else throw new Error \"conj called on \" + types._obj_type(seq)\n\nseq = (obj) ->\n  switch types._obj_type(obj)\n    when 'list'\n      if obj.length == 0 then null else obj\n    when 'vector'\n      if obj.length == 0 then null else obj[0..-1]\n    when 'string'\n      if obj.length == 0 then null else obj.split('')\n    when 'nil'\n      null\n    else throw new Error \"seq: called on non-sequential \" + types._obj_type(seq)\n\n# Metadata functions\nwith_meta = (obj,m) ->\n  new_obj = types._clone(obj)\n  new_obj.__meta__ = m\n  new_obj\n\n\nexports.ns = {\n  '=': (a,b) -> types._equal_Q(a,b),\n  'throw': (a) -> throw {\"object\": a},\n  'nil?': types._nil_Q,\n  'true?': types._true_Q,\n  'false?': types._false_Q,\n  'string?': types._string_Q,\n  'symbol': types._symbol,\n  'symbol?': types._symbol_Q,\n  'keyword': types._keyword,\n  'keyword?': types._keyword_Q,\n  'number?': (a) -> typeof a == 'number',\n  'fn?': (a) -> typeof a == 'function' and not types._macro_Q(a),\n  'macro?': types._macro_Q,\n\n  'pr-str': (a...) -> a.map((exp) -> _pr_str(exp,true)).join(\" \"),\n  'str': (a...) -> a.map((exp) -> _pr_str(exp,false)).join(\"\"),\n  'prn': (a...) -> println(a.map((exp) -> _pr_str(exp,true))...),\n  'println': (a...) -> println(a.map((exp) -> _pr_str(exp,false))...),\n  'readline': readline.readline,\n  'read-string': reader.read_str,\n  'slurp': (a) -> require('fs').readFileSync(a, 'utf-8'),\n  '<':  (a,b) -> a<b,\n  '<=': (a,b) -> a<=b,\n  '>':  (a,b) -> a>b,\n  '>=': (a,b) -> a>=b,\n  '+': (a,b) -> a+b,\n  '-': (a,b) -> a-b,\n  '*': (a,b) -> a*b,\n  '/': (a,b) -> a/b,\n  'time-ms': () -> new Date().getTime(),\n\n  'list': (a...) -> a,\n  'list?': types._list_Q,\n  'vector': (a...) -> types._vector(a...),\n  'vector?': types._vector_Q,\n  'hash-map': (a...) -> types._hash_map(a...),\n  'map?': types._hash_map_Q,\n  'assoc': (a,b...) -> types._assoc_BANG(types._clone(a), b...),\n  'dissoc': (a,b...) -> types._dissoc_BANG(types._clone(a), b...),\n  'get': (a,b) -> if a != null and b of a then a[b] else null,\n  'contains?': (a,b) -> b of a,\n  'keys': (a) -> k for k of a,\n  'vals': (a) -> v for k,v of a,\n\n  'sequential?': types._sequential_Q,\n  'cons': (a,b) -> [a].concat(b),\n  'concat': (a=[],b...) -> a.concat(b...),\n  'vec': (a) -> types._vector a...,\n  'nth': (a,b) -> if a.length > b then a[b] else\n    throw new Error \"nth: index out of bounds\",\n  'first': (a) -> if a != null and a.length > 0 then a[0] else null,\n  'rest': (a) -> if a == null then [] else a[1..],\n  'empty?': (a) -> a.length == 0,\n  'count': (a) -> if a == null then 0 else a.length,\n  'apply': (a,b...) -> a(b[0..-2].concat(b[b.length-1])...),\n  'map': (a,b) -> b.map((x) -> a(x)),\n\n  'conj': conj,\n  'seq': seq,\n\n  'with-meta': with_meta,\n  'meta': (a) -> a.__meta__ or null,\n  'atom': types._atom,\n  'atom?': types._atom_Q,\n  'deref': (a) -> a.val,\n  'reset!': (a,b) -> a.val = b,\n  'swap!': (a,b,c...) -> a.val = b([a.val].concat(c)...), }\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/env.coffee",
    "content": "types = require \"./types.coffee\"\n\n# Env\nexports.Env = class Env\n  constructor: (@outer=null, @binds=[], @exprs=[]) ->\n    @data = {}\n    if @binds.length > 0\n      for b,i in @binds\n        if types._symbol_Q(b) && b.name == \"&\"\n          @data[@binds[i+1].name] = @exprs[i..]\n          break\n        else\n          @data[b.name] = @exprs[i]\n  find: (key) ->\n    if key of @data then @\n    else if @outer then @outer.find(key)\n    else null\n  set: (key, value) ->\n    if not types._symbol_Q(key)\n      throw new Error(\"env.set key must be symbol\")\n    @data[key.name] = value\n  get: (key) ->\n    env = @find(key)\n    throw new Error(\"'\" + key + \"' not found\") if !env\n    env.data[key]\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/node_readline.coffee",
    "content": "# IMPORTANT: choose one\nRL_LIB = \"libreadline.so.8\"  # NOTE: libreadline is GPL\n#RL_LIB = \"libedit.so.2\"\n\nHISTORY_FILE = require('path').join(process.env.HOME, '.mal-history')\n\nrlwrap = {} # namespace for this module in web context\n\nkoffi = require('koffi')\nfs = require('fs')\n\nrllib = null\ntry\n  rllib = koffi.load(RL_LIB)\ncatch e\n  console.error 'ERROR loading RL_LIB:', RL_LIB, e\n  throw e\n\nreadlineFunc = rllib.func('char *readline(char *)')\naddHistoryFunc = rllib.func('int add_history(char *)')\n\nrl_history_loaded = false\n\nexports.readline = rlwrap.readline = (prompt = 'user> ') ->\n  if !rl_history_loaded\n    rl_history_loaded = true\n    lines = []\n    if fs.existsSync(HISTORY_FILE)\n      lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\")\n    # Max of 2000 lines\n    lines = lines[Math.max(lines.length - 2000, 0)..]\n    for line in lines when line != \"\"\n      addHistoryFunc line\n\n  line = readlineFunc prompt\n  if line\n    addHistoryFunc line\n    try\n      fs.appendFileSync HISTORY_FILE, line + \"\\n\"\n    catch exc\n      # ignored\n      true\n\n  line\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/package.json",
    "content": "{\n    \"name\": \"mal\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in CoffeeScript\",\n    \"dependencies\": {\n        \"coffeescript\": \"^2.7.0\",\n        \"koffi\": \"^2.12.1\"\n    }\n}\n"
  },
  {
    "path": "impls/coffee/printer.coffee",
    "content": "types = require \"./types.coffee\"\n\nexports.println = (args...) -> console.log(args.join(\" \")) || null\n\nexports._pr_str = _pr_str = (obj, print_readably=true) ->\n  _r = print_readably\n  switch types._obj_type obj\n    when 'list' then '(' + obj.map((e) -> _pr_str(e,_r)).join(' ') + ')'\n    when 'vector' then '[' + obj.map((e) -> _pr_str(e,_r)).join(' ') + ']'\n    when 'hash-map'\n      ret = []\n      ret.push(_pr_str(k,_r), _pr_str(v,_r)) for k,v of obj\n      '{' + ret.join(' ') + '}'\n    when 'string'\n      if _r then '\"' + (obj.replace(/\\\\/g, '\\\\\\\\')\n                           .replace(/\"/g, '\\\\\"')\n                           .replace(/\\n/g, '\\\\n')) + '\"'\n      else obj\n    when 'keyword' then \":\" + obj.slice(1)\n    when 'symbol' then obj.name\n    when 'nil' then 'nil'\n    when 'atom' then \"(atom \" + _pr_str(obj.val,_r) + \")\"\n    else obj.toString()\n    \n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/reader.coffee",
    "content": "types = require \"./types.coffee\"\n_symbol = types._symbol\n\n\nclass Reader\n  constructor: (@tokens) -> @position = 0\n  next: -> @tokens[@position++]\n  peek: -> @tokens[@position]\n  skip: ->\n    @position++\n    @\n\ntokenize = (str) ->\n    re = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g\n    results = []\n    while (match = re.exec(str)[1]) != \"\"\n      continue if match[0] == ';'\n      results.push(match)\n    results\n\nread_atom = (rdr) ->\n  token = rdr.next()\n  if token.match /^-?[0-9]+$/ then parseInt token,10\n  else if token.match /^-?[0-9][0-9.]*$/ then parseFloat token,10\n  else if token.match /^\"(?:\\\\.|[^\\\\\"])*\"$/\n    token.slice(1, token.length-1)\n      .replace(/\\\\(.)/g, (_, c) -> if c == 'n' then '\\n' else c)\n  else if token[0] == '\"'\n    throw new Error \"expected '\\\"', got EOF\"\n  else if token[0] == ':' then types._keyword(token[1..])\n  else if token == \"nil\" then null\n  else if token == \"true\" then true\n  else if token == \"false\" then false\n  else _symbol(token)\n\nread_list = (rdr, start='(', end=')') ->\n  ast = []\n  token = rdr.next()\n  throw new Error \"expected '\" + start + \"'\" if token != start\n  while (token = rdr.peek()) != end\n    throw new Error \"expected '\" + end + \"', got EOF\" if !token\n    ast.push read_form rdr\n  rdr.next()\n  ast\n\nread_vector = (rdr) ->\n  types._vector(read_list(rdr, '[', ']')...)\n\nread_hash_map = (rdr) ->\n  types._hash_map(read_list(rdr, '{', '}')...)\n\nread_form = (rdr) ->\n  token = rdr.peek()\n  switch token\n    when '\\'' then [_symbol('quote'), read_form(rdr.skip())]\n    when '`'  then [_symbol('quasiquote'), read_form(rdr.skip())]\n    when '~'  then [_symbol('unquote'), read_form(rdr.skip())]\n    when '~@' then [_symbol('splice-unquote'), read_form(rdr.skip())]\n    when '^'\n      meta = read_form(rdr.skip())\n      [_symbol('with-meta'), read_form(rdr), meta]\n    when '@' then [_symbol('deref'), read_form(rdr.skip())]\n\n    # list\n    when ')' then throw new Error \"unexpected ')'\"\n    when '(' then read_list(rdr)\n    # vector\n    when ']' then throw new Error \"unexpected ']'\"\n    when '[' then read_vector(rdr)\n    # hash-map\n    when '}' then throw new Error \"unexpected '}'\"\n    when '{' then read_hash_map(rdr)\n    # atom\n    else read_atom(rdr)\n\n\nexports.BlankException = BlankException = (msg) -> null\n\nexports.read_str = read_str = (str) ->\n  tokens = tokenize(str)\n  throw new BlankException() if tokens.length == 0\n  read_form(new Reader(tokens))\n\n#console.log read_str \"(1 \\\"two\\\" three)\"\n#console.log read_str \"[1 2 3]\"\n#console.log read_str '{\"abc\" 123 \"def\" 456}'\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/run",
    "content": "#!/usr/bin/env bash\nexec coffee $(dirname $0)/${STEP:-stepA_mal}.coffee \"${@}\"\n"
  },
  {
    "path": "impls/coffee/step0_repl.coffee",
    "content": "readline = require \"./node_readline.coffee\"\n\n# read\nREAD = (str) -> str\n\n# eval\nEVAL = (ast, env) -> ast\n\n# print\nPRINT = (exp) -> exp\n\n# repl\nrep = (str) -> PRINT(EVAL(READ(str), {}))\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  console.log rep line\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step1_read_print.coffee",
    "content": "readline = require \"./node_readline.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nEVAL = (ast, env) -> ast\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrep = (str) -> PRINT(EVAL(READ(str), {}))\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step2_eval.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nEVAL = (ast, env) ->\n  # console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env[ast.name]\n  else if types._list_Q(ast) then # exit this switch\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  if ast.length == 0 then return ast\n\n  # apply list\n  [f, args...] = ast.map((a) -> EVAL(a, env))\n  f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = {}\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\nrepl_env[\"+\"] = (a,b) -> a+b\nrepl_env[\"-\"] = (a,b) -> a-b\nrepl_env[\"*\"] = (a,b) -> a*b\nrepl_env[\"/\"] = (a,b) -> a/b\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step3_env.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nEVAL = (ast, env) ->\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  if ast.length == 0 then return ast\n\n  # apply list\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      EVAL(a2, let_env)\n    else\n      [f, args...] = ast.map((a) -> EVAL(a, env))\n      f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\nrepl_env.set types._symbol(\"+\"), (a,b) -> a+b\nrepl_env.set types._symbol(\"-\"), (a,b) -> a-b\nrepl_env.set types._symbol(\"*\"), (a,b) -> a*b\nrepl_env.set types._symbol(\"/\"), (a,b) -> a/b\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step4_if_fn_do.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nEVAL = (ast, env) ->\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  if ast.length == 0 then return ast\n\n  # apply list\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      EVAL(a2, let_env)\n    when \"do\"\n      el = ast[1..].map((a) -> EVAL(a, env))\n      el[el.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then EVAL(a3, env) else null\n      else\n        EVAL(a2, env)\n    when \"fn*\"\n      (args...) -> EVAL(a2, new Env(env, a1, args))\n    else\n      [f, args...] = ast.map((a) -> EVAL(a, env))\n      f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\n\n# core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step5_tco.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nEVAL = (ast, env) ->\n loop\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  if ast.length == 0 then return ast\n\n  # apply list\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      return env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      ast = a2\n      env = let_env\n    when \"do\"\n      ast[1..-2].map((a) -> EVAL(a, env))\n      ast = ast[ast.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then ast = a3 else return null\n      else\n        ast = a2\n    when \"fn*\"\n      return types._function(EVAL, a2, env, a1)\n    else\n      [f, args...] = ast.map((a) -> EVAL(a, env))\n      if types._function_Q(f)\n        ast = f.__ast__\n        env = f.__gen_env__(args)\n      else\n        return f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\n\n# core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step6_file.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nEVAL = (ast, env) ->\n loop\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  if ast.length == 0 then return ast\n\n  # apply list\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      return env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      ast = a2\n      env = let_env\n    when \"do\"\n      ast[1..-2].map((a) -> EVAL(a, env))\n      ast = ast[ast.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then ast = a3 else return null\n      else\n        ast = a2\n    when \"fn*\"\n      return types._function(EVAL, a2, env, a1)\n    else\n      [f, args...] = ast.map((a) -> EVAL(a, env))\n      if types._function_Q(f)\n        ast = f.__ast__\n        env = f.__gen_env__(args)\n      else\n        return f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\nrepl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)\nrepl_env.set types._symbol('*ARGV*'), []\n\n# core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nif process? && process.argv.length > 2\n  repl_env.set types._symbol('*ARGV*'), process.argv[3..]\n  rep('(load-file \"' + process.argv[2] + '\")')\n  process.exit 0\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step7_quote.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nstarts_with = (ast, sym) ->\n  types._list_Q(ast) && 0<ast.length && ast[0]!=null && types._symbol_Q(ast[0]) && ast[0].name==sym\n\nqq_iter = (accumulator, elt) ->\n  if starts_with(elt, 'splice-unquote') then [types._symbol('concat'), elt[1], accumulator]\n  else [types._symbol('cons'), quasiquote(elt), accumulator]\n\nquasiquote = (ast) ->\n  if starts_with(ast, 'unquote') then ast[1]\n  else if types._list_Q(ast) then ast.reduceRight(qq_iter, [])\n  else if types._vector_Q(ast) then [types._symbol('vec'), ast.reduceRight(qq_iter, [])]\n  else if types._symbol_Q(ast) || types._hash_map_Q(ast) then [types._symbol('quote'), ast]\n  else ast\n    \n    \n\nEVAL = (ast, env) ->\n loop\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  if ast.length == 0 then return ast\n\n  # apply list\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      return env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      ast = a2\n      env = let_env\n    when \"quote\"\n      return a1\n    when \"quasiquote\"\n      ast = quasiquote(a1)\n    when \"do\"\n      ast[1..-2].map((a) -> EVAL(a, env))\n      ast = ast[ast.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then ast = a3 else return null\n      else\n        ast = a2\n    when \"fn*\"\n      return types._function(EVAL, a2, env, a1)\n    else\n      [f, args...] = ast.map((a) -> EVAL(a, env))\n      if types._function_Q(f)\n        ast = f.__ast__\n        env = f.__gen_env__(args)\n      else\n        return f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\nrepl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)\nrepl_env.set types._symbol('*ARGV*'), []\n\n# core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nif process? && process.argv.length > 2\n  repl_env.set types._symbol('*ARGV*'), process.argv[3..]\n  rep('(load-file \"' + process.argv[2] + '\")')\n  process.exit 0\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step8_macros.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nstarts_with = (ast, sym) ->\n  types._list_Q(ast) && 0<ast.length && ast[0]!=null && types._symbol_Q(ast[0]) && ast[0].name==sym\n\nqq_iter = (accumulator, elt) ->\n  if starts_with(elt, 'splice-unquote') then [types._symbol('concat'), elt[1], accumulator]\n  else [types._symbol('cons'), quasiquote(elt), accumulator]\n\nquasiquote = (ast) ->\n  if starts_with(ast, 'unquote') then ast[1]\n  else if types._list_Q(ast) then ast.reduceRight(qq_iter, [])\n  else if types._vector_Q(ast) then [types._symbol('vec'), ast.reduceRight(qq_iter, [])]\n  else if types._symbol_Q(ast) || types._hash_map_Q(ast) then [types._symbol('quote'), ast]\n  else ast\n\nEVAL = (ast, env) ->\n loop\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  # apply list\n  if ast.length == 0 then return ast\n\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      return env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      ast = a2\n      env = let_env\n    when \"quote\"\n      return a1\n    when \"quasiquote\"\n      ast = quasiquote(a1)\n    when \"defmacro!\"\n      f = EVAL(a2, env)\n      f = types._clone(f)\n      f.__ismacro__ = true\n      return env.set(a1, f)\n    when \"do\"\n      ast[1..-2].map((a) -> EVAL(a, env))\n      ast = ast[ast.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then ast = a3 else return null\n      else\n        ast = a2\n    when \"fn*\"\n      return types._function(EVAL, a2, env, a1)\n    else\n      f = EVAL(a0, env)\n      if f.__ismacro__\n        ast = EVAL(f.__ast__, f.__gen_env__(ast[1..]))\n        continue\n      args = ast[1..].map((a) -> EVAL(a, env))\n      if types._function_Q(f)\n        ast = f.__ast__\n        env = f.__gen_env__(args)\n      else\n        return f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\nrepl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)\nrepl_env.set types._symbol('*ARGV*'), []\n\n# core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif process? && process.argv.length > 2\n  repl_env.set types._symbol('*ARGV*'), process.argv[3..]\n  rep('(load-file \"' + process.argv[2] + '\")')\n  process.exit 0\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack? then console.log exc.stack\n    else                    console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/step9_try.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nstarts_with = (ast, sym) ->\n  types._list_Q(ast) && 0<ast.length && ast[0]!=null && types._symbol_Q(ast[0]) && ast[0].name==sym\n\nqq_iter = (accumulator, elt) ->\n  if starts_with(elt, 'splice-unquote') then [types._symbol('concat'), elt[1], accumulator]\n  else [types._symbol('cons'), quasiquote(elt), accumulator]\n\nquasiquote = (ast) ->\n  if starts_with(ast, 'unquote') then ast[1]\n  else if types._list_Q(ast) then ast.reduceRight(qq_iter, [])\n  else if types._vector_Q(ast) then [types._symbol('vec'), ast.reduceRight(qq_iter, [])]\n  else if types._symbol_Q(ast) || types._hash_map_Q(ast) then [types._symbol('quote'), ast]\n  else ast\n\nEVAL = (ast, env) ->\n loop\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  # apply list\n  if ast.length == 0 then return ast\n\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      return env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      ast = a2\n      env = let_env\n    when \"quote\"\n      return a1\n    when \"quasiquote\"\n      ast = quasiquote(a1)\n    when \"defmacro!\"\n      f = EVAL(a2, env)\n      f = types._clone(f)\n      f.__ismacro__ = true\n      return env.set(a1, f)\n    when \"try*\"\n      try return EVAL(a1, env)\n      catch exc\n        if a2 && a2[0].name == \"catch*\"\n          if exc.object? then exc = exc.object\n          else exc = exc.message\n          return EVAL a2[2], new Env(env, [a2[1]], [exc])\n        else\n          throw exc\n    when \"do\"\n      ast[1..-2].map((a) -> EVAL(a, env))\n      ast = ast[ast.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then ast = a3 else return null\n      else\n        ast = a2\n    when \"fn*\"\n      return types._function(EVAL, a2, env, a1)\n    else\n      f = EVAL(a0, env)\n      if f.__ismacro__\n        ast = EVAL(f.__ast__, f.__gen_env__(ast[1..]))\n        continue\n      args = ast[1..].map((a) -> EVAL(a, env))\n      if types._function_Q(f)\n        ast = f.__ast__\n        env = f.__gen_env__(args)\n      else\n        return f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\nrepl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)\nrepl_env.set types._symbol('*ARGV*'), []\n\n# core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif process? && process.argv.length > 2\n  repl_env.set types._symbol('*ARGV*'), process.argv[3..]\n  rep('(load-file \"' + process.argv[2] + '\")')\n  process.exit 0\n\n# repl loop\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack?\n      console.log exc.stack\n    else if exc.object?\n      console.log \"Error:\", printer._pr_str exc.object, true\n    else\n      console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/stepA_mal.coffee",
    "content": "readline = require \"./node_readline.coffee\"\ntypes = require \"./types.coffee\"\nreader = require \"./reader.coffee\"\nprinter = require \"./printer.coffee\"\nEnv = require(\"./env.coffee\").Env\ncore = require(\"./core.coffee\")\n\n# read\nREAD = (str) -> reader.read_str str\n\n# eval\nstarts_with = (ast, sym) ->\n  types._list_Q(ast) && 0<ast.length && ast[0]!=null && types._symbol_Q(ast[0]) && ast[0].name==sym\n\nqq_iter = (accumulator, elt) ->\n  if starts_with(elt, 'splice-unquote') then [types._symbol('concat'), elt[1], accumulator]\n  else [types._symbol('cons'), quasiquote(elt), accumulator]\n\nquasiquote = (ast) ->\n  if starts_with(ast, 'unquote') then ast[1]\n  else if types._list_Q(ast) then ast.reduceRight(qq_iter, [])\n  else if types._vector_Q(ast) then [types._symbol('vec'), ast.reduceRight(qq_iter, [])]\n  else if types._symbol_Q(ast) || types._hash_map_Q(ast) then [types._symbol('quote'), ast]\n  else ast\n\nEVAL = (ast, env) ->\n loop\n  dbgenv = env.find(\"DEBUG-EVAL\")\n  if dbgenv\n    dbgeval = dbgenv.get(\"DEBUG-EVAL\")\n    if dbgeval != null and dbgeval != false\n      console.log \"EVAL:\", printer._pr_str ast\n\n  if types._symbol_Q(ast) then return env.get ast.name\n  else if types._list_Q(ast) then # exit this switch\n  else if types._vector_Q(ast)\n    return types._vector(ast.map((a) -> EVAL(a, env))...)\n  else if types._hash_map_Q(ast)\n    new_hm = {}\n    new_hm[k] = EVAL(v, env) for k,v of ast\n    return new_hm\n  else return ast\n\n  # apply list\n  if ast.length == 0 then return ast\n\n  [a0, a1, a2, a3] = ast\n  switch a0.name\n    when \"def!\"\n      return env.set(a1, EVAL(a2, env))\n    when \"let*\"\n      let_env = new Env(env)\n      for k,i in a1 when i %% 2 == 0\n        let_env.set(a1[i], EVAL(a1[i+1], let_env))\n      ast = a2\n      env = let_env\n    when \"quote\"\n      return a1\n    when \"quasiquote\"\n      ast = quasiquote(a1)\n    when \"defmacro!\"\n      f = EVAL(a2, env)\n      f = types._clone(f)\n      f.__ismacro__ = true\n      return env.set(a1, f)\n    when \"try*\"\n      try return EVAL(a1, env)\n      catch exc\n        if a2 && a2[0].name == \"catch*\"\n          if exc.object? then exc = exc.object\n          else exc = exc.message || exc.toString()\n          return EVAL a2[2], new Env(env, [a2[1]], [exc])\n        else\n          throw exc\n    when \"js*\"\n      res = eval(a1.toString())\n      return if typeof(res) == 'undefined' then null else res\n    when \".\"\n      el = ast[2..].map((a) -> EVAL(a, env))\n      return eval(a1.toString())(el...)\n    when \"do\"\n      ast[1..-2].map((a) -> EVAL(a, env))\n      ast = ast[ast.length-1]\n    when \"if\"\n      cond = EVAL(a1, env)\n      if cond == null or cond == false\n        if a3? then ast = a3 else return null\n      else\n        ast = a2\n    when \"fn*\"\n      return types._function(EVAL, a2, env, a1)\n    else\n      f = EVAL(a0, env)\n      if f.__ismacro__\n        ast = EVAL(f.__ast__, f.__gen_env__(ast[1..]))\n        continue\n      args = ast[1..].map((a) -> EVAL(a, env))\n      if types._function_Q(f)\n        ast = f.__ast__\n        env = f.__gen_env__(args)\n      else\n        return f(args...)\n\n\n# print\nPRINT = (exp) -> printer._pr_str exp, true\n\n# repl\nrepl_env = new Env()\nrep = (str) -> PRINT(EVAL(READ(str), repl_env))\n\n# core.coffee: defined using CoffeeScript\nrepl_env.set types._symbol(k), v for k,v of core.ns\nrepl_env.set types._symbol('eval'), (ast) -> EVAL(ast, repl_env)\nrepl_env.set types._symbol('*ARGV*'), []\n\n# core.mal: defined using the language itself\nrep(\"(def! *host-language* \\\"CoffeeScript\\\")\")\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif process? && process.argv.length > 2\n  repl_env.set types._symbol('*ARGV*'), process.argv[3..]\n  rep('(load-file \"' + process.argv[2] + '\")')\n  process.exit 0\n\n# repl loop\nrep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nwhile (line = readline.readline(\"user> \")) != null\n  continue if line == \"\"\n  try\n    console.log rep line\n  catch exc\n    continue if exc instanceof reader.BlankException\n    if exc.stack? and exc.stack.length > 2000\n      console.log exc.stack.slice(0,1000) + \"\\n  ...\" + exc.stack.slice(-1000)\n    else if exc.stack?\n      console.log exc.stack\n    else if exc.object?\n      console.log \"Error:\", printer._pr_str exc.object, true\n    else\n      console.log exc\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/coffee/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/coffee/tests/stepA_mal.mal",
    "content": ";; Testing basic bash interop\n\n(js* \"7\")\n;=>7\n\n(js* \"'7'\")\n;=>\"7\"\n\n(js* \"[7,8,9]\")\n;=>(7 8 9)\n\n(js* \"console.log('hello');\")\n;/hello\n;=>nil\n\n(js* \"foo=8;\")\n(js* \"foo;\")\n;=>8\n\n(js* \"['a','b','c'].map(function(x){return 'X'+x+'Y'}).join(' ')\")\n;=>\"XaY XbY XcY\"\n\n(js* \"[1,2,3].map(function(x){return 1+x})\")\n;=>(2 3 4)\n"
  },
  {
    "path": "impls/coffee/types.coffee",
    "content": "Env = require(\"./env.coffee\").Env\n\nE = exports\n\n# General functions\nE._obj_type = _obj_type = (obj) ->\n  if _symbol_Q(obj)        then 'symbol'\n  else if _list_Q(obj)     then 'list'\n  else if _vector_Q(obj)   then 'vector'\n  else if _hash_map_Q(obj) then 'hash-map'\n  else if _nil_Q(obj)      then 'nil'\n  else if _true_Q(obj)     then 'true'\n  else if _false_Q(obj)    then 'false'\n  else if _atom_Q(obj)     then 'atom'\n  else\n    switch typeof obj\n      when 'number'   then 'number'\n      when 'function' then 'function'\n      when 'string'\n        if obj[0] == '\\u029e' then 'keyword' else 'string'\n      else throw new Error \"Unknown type '\" + typeof(obj) + \"'\"\n\nE._sequential_Q = _sequential_Q = (o) -> _list_Q(o) or _vector_Q(o)\n\nE._equal_Q = _equal_Q = (a,b) ->\n  [ota, otb] = [_obj_type(a), _obj_type(b)]\n  if !(ota == otb or (_sequential_Q(a) && _sequential_Q(b)))\n    return false\n  switch (ota)\n    when 'symbol' then a.name == b.name\n    when 'list', 'vector'\n      return false if a.length != b.length\n      for av,i in a\n        return false if !_equal_Q(av, b[i])\n      true\n    when 'hash-map'\n      akeys = (key for key of a)\n      bkeys = (key for key of b)\n      return false if akeys.length != bkeys.length\n      for akey,i in akeys\n        return false if !_equal_Q(a[akey], b[akey])\n      true\n    else a == b\n\nE._clone = _clone = (obj) ->\n  switch _obj_type(obj)\n    when 'list' then obj[0..-1]\n    when 'vector' then _vector(obj[0..-1]...)\n    when 'hash-map'\n      new_obj = {}\n      new_obj[k] = v for k,v of obj\n      new_obj\n    when 'function'\n      new_obj = (args...) -> obj(args...)\n      new_obj[k] = v for k,v of obj\n      new_obj\n    else throw new Error \"clone called on non-collection\" + _obj_type(obj)\n\n\n# Scalars\nE._nil_Q = _nil_Q = (o) -> o == null\nE._true_Q = _true_Q = (o) -> o == true\nE._false_Q = _false_Q = (o) -> o == false\nE._string_Q = _string_Q = (o) -> _obj_type(o) == 'string'\n\n# Symbols\nclass Symbol\n  constructor: (@name) ->\nE._symbol = (str) -> new Symbol str\nE._symbol_Q = _symbol_Q = (o) -> o instanceof Symbol\n\n# Keywords\nE._keyword = _keyword = (o) ->\n  _keyword_Q(o) && o || (\"\\u029e\" + o)\nE._keyword_Q = _keyword_Q = (o) ->\n  typeof o == 'string' && o[0] == \"\\u029e\"\n\n# Functions\nE._function = (evalfn, ast, env, params) ->\n  fn = (args...) -> evalfn(ast, new Env(env, params, args))\n  fn.__ast__ = ast\n  fn.__gen_env__ = (args) -> new Env(env, params, args)\n  fn.__ismacro__ = false\n  fn\nE._function_Q = _function_Q = (o) -> !!o.__ast__\nE._macro_Q = _macro_Q = (o) -> _function_Q(o) and o.__ismacro__\n\n# Lists\nE._list_Q = _list_Q = (o) -> Array.isArray(o) && !o.__isvector__\n\n# Vectors\nE._vector = _vector = (args...) ->\n  v = args\n  v.__isvector__ = true\n  v\nE._vector_Q = _vector_Q = (o) -> Array.isArray(o) && !!o.__isvector__\n\n# Hash Maps\nE._hash_map = (args...) ->\n  args = [{}].concat args\n  _assoc_BANG(args...)\nE._assoc_BANG = _assoc_BANG = (hm, args...) ->\n  if args.length %% 2 == 1\n    throw new Error \"Odd number of hash map arguments\"\n  hm[k] = args[i+1] for k, i in args when i %% 2 == 0\n  hm\nE._dissoc_BANG = (hm, args...) ->\n  delete hm[k] for k, i in args\n  hm\nE._hash_map_Q = _hash_map_Q = (o) ->\n  typeof o == \"object\" && !Array.isArray(o) &&\n                          !(o == null) &&\n                          !(o instanceof Symbol) &&\n                          !(o instanceof Atom)\n\n\n# Atoms\nclass Atom\n  constructor: (@val) ->\nE._atom = (val) -> new Atom val\nE._atom_Q = _atom_Q = (o) -> o instanceof Atom\n\n# vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/common-lisp/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install curl git libreadline-dev libedit-dev\n\n# Install sbcl\nRUN apt-get -y install sbcl\n\n# Install cl-asdf (CLISP does not seem to come with it)\nRUN apt-get -y install cl-launch cl-asdf cl-ppcre\n\nRUN cd /tmp && \\\n    git clone https://gitlab.common-lisp.net/xcvb/cl-launch.git && \\\n    cd cl-launch && \\\n    make install\n\n# Install wget needed to install quicklisp\nRUN apt-get -y install wget\n\n# Install quicklisp\nRUN HOME=/ && \\\n    cd /tmp && \\\n    wget https://beta.quicklisp.org/quicklisp.lisp && \\\n    sbcl --load quicklisp.lisp --quit --eval '(quicklisp-quickstart:install)' --eval '(ql-util:without-prompting (ql:add-to-init-file))'\n\nRUN chmod -R a+rwx /quicklisp\nRUN chmod a+rwx /.sbclrc\n\nRUN mkdir -p /.cache\nRUN chmod -R a+rwx /.cache\n"
  },
  {
    "path": "impls/common-lisp/Makefile",
    "content": "# Helper functions\ndefine record_lisp\n\t$(shell (test -f \"hist/$(1)_impl\" && grep -q $(2) \"hist/$(1)_impl\") || echo $(2) > \"hist/$(1)_impl\")\nendef\n\ndefine steps\n\t$(if $(MAKECMDGOALS),\\\n\t\t$(if $(findstring all,$(MAKECMDGOALS)),\\\n\t\t\tstepA_mal,\\\n\t\t\t$(filter step%, $(MAKECMDGOALS))),\\\n\t\tstepA_mal)\nendef\n\nLISP ?= sbcl\nABCL ?= abcl\nMKCL ?= mkcl\n\nBINS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do \\\n       step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\n\n# TODO: In theory cl-launch should be able to build standalone executable using\n# MKCL unfortunately the executable crashes on startup\nSTANDALONE_EXE = sbcl clisp ccl ecl cmucl\n\nROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))\n\n# Record the Common Lisp implementation used for all steps built in this\n# invocation This is used in the targets to rebuild the step if the\n# implementation changes\n$(foreach step, $(call steps), $(call record_lisp,$(patsubst step%,%,$(step)),$(LISP)))\n\n.PRECIOUS: hist/%_impl\n\nall : $(BINS)\n\nhist/%_impl: ;\n\n# CL_LAUNCH_VERSION is only defined while building it. We change to the\n# directory of the CL_LAUNCH_FILE in --wrap script so that the script can find the dumped\n# image even if invoked from some directory different from where it\n# currently resides\nstep% : src/step%.lisp src/utils.lisp src/types.lisp src/env.lisp src/printer.lisp src/reader.lisp src/core.lisp hist/%_impl\n\nifeq ($(LISP),clisp)\n\t@echo \"==============================================================\"\n\t@echo \"WARNING: This build might fail since GNU Clisp does not have bundled version of asdf (yet)\"\n\t@echo \"Please do something like below to make it work\"\n\t@echo \"(mkdir -p ~/common-lisp/ && cd ~/common-lisp && git clone -b release https://gitlab.common-lisp.net/asdf/asdf.git && cd asdf && make)\"\n\t@echo \"==============================================================\"\nendif\n\nifneq ($(filter $(LISP),$(STANDALONE_EXE)),)\n\tsbcl --eval '(load \"~/quicklisp/setup.lisp\")' --eval '(asdf:initialize-source-registry `(:source-registry (:tree \"$(ROOT_DIR)\") :inherit-configuration))' --eval '(ql:quickload :uiop)' --eval '(ql:quickload :cl-readline)' --eval '(ql:quickload :genhash)' --eval '(asdf:load-system \"$@\")' --eval '(asdf:operate :build-op \"$@\")' --eval \"(save-lisp-and-die \\\"$@\\\" :executable t :toplevel #'(lambda () (mal:main)))\" --quit\nelse ifeq ($(LISP),abcl)\n\techo -n '#!/bin/sh\\ncd `dirname $$0` ; $(ABCL) --noinform --noinit --nosystem --load run-abcl.lisp -- $@ $$@' > $@\n\tchmod +x $@\nelse ifeq ($(LISP),mkcl)\n\t$(MKCL) -eval '(progn (require \"asdf\") (push *default-pathname-defaults* asdf:*central-registry*) (asdf:load-system \"$@\") (quit))'\n\techo -n '#!/bin/sh\\ncd `dirname $$0` ; $(MKCL) -q -load run-mkcl.lisp -- $@ $$@' > $@\n\tchmod +x $@\nelse ifeq ($(LISP),allegro)\n\tcl-launch --wrap 'if [ -z \"$$CL_LAUNCH_VERSION\" ] ; then cd \"$$(dirname $$CL_LAUNCH_FILE)\" ; fi' --verbose --lisp $(LISP) --source-registry $(ROOT_DIR) --system $@ --dump images/$@.$(LISP).image -o $@ --entry 'mal:main'\nelse\n\t@echo \"Unsupported Lisp implementation $(LISP)\"\n\t@exit 1\nendif\n\nclean:\n\tfind . -maxdepth 1 -name 'step*' -executable -delete\n\trm -f *.lib *.fas[l] images/* hist/*_impl\n"
  },
  {
    "path": "impls/common-lisp/README.org",
    "content": "* Implementation of MAL in Common Lisp\n\n** Introduction\n\nThis is a portable implementation of MAL in Common Lisp. It has been tested to\nwork with following Common Lisp implementations\n\n- Steel Bank Common Lisp [[http://sbcl.org/]]\n- Clozure Common Lisp [[http://ccl.clozure.com/]]\n- CMU Common Lisp [[https://www.cons.org/cmucl/]]\n- GNU CLISP [[http://www.clisp.org/]]\n- Embeddable Common Lisp [[https://common-lisp.net/project/ecl/]]\n- ManKai Common Lisp https://common-lisp.net/project/mkcl/\n- Allegro CL [[http://franz.com/products/allegro-common-lisp/]]\n- Armed Bear Common Lisp [[http://abcl.org/]]\n\n[[http://www.cliki.net/cl-launch][cl-launch]] to build executable/wrapper scripts for most of the above implementations.\n\n** Dependencies\n\n- cl-launch\n  For building command line executable scripts. See [[http://www.cliki.net/cl-launch][cl-launch]]\n\n- quicklisp\n  For installing dependencies. See [[https://www.quicklisp.org/beta/][quicklisp]]\n\n- readline\n  For readline integration. You can install it on Ubuntu using apt the package\n  is ~libreadline-dev~. If you wish to run the implementation using Allegro CL,\n  you will also have to install the 32 bit version of readline\n  (~lib32readline-dev~ on Ubuntu)\n\n- (Optional) asdf\n  This is needed if you want to run the implementation using GNU CLISP, since\n  GNU CLISP does not ship with ~asdf~ and ~cl-launch~ depends on it. You can\n  install it on Ubuntu using apt the package is ~cl-asdf~\n\n** Running using different implementations\n\nBy default the MAL is built using ~sbcl~, you can control this using ~LISP~\nenvironment variable. The variable should be set to the cl-launch \"nickname\" for\nimplementation. The nicknames that work currently are\n\n|------------------------+----------|\n| Implementation         | Nickname |\n|------------------------+----------|\n| Steel Bank Common Lisp | sbcl     |\n| Clozure Common Lisp    | ccl      |\n| CMU Common Lisp        | cmucl    |\n| GNU CLISP              | clisp    |\n| Embeddable Common Lisp | ecl      |\n| ManKai Common Lisp     | mkcl     |\n| Allegro CL             | allegro  |\n| Armed Bear Common Lisp | abcl     |\n|------------------------+----------|\n\nFor example to build with GNU CLISP, you need to do the following\n\n#+BEGIN_SRC sh\n  cd common-lisp ; LISP=clisp make\n#+END_SRC\n\nYou can control the implementation binary used for the build using environment\nvariables.  For a given implementation nickname, the environment variable will\nbe the capitalization of the given nickname.\n\n|------------------------+-------------|\n| Implementation         | Binary Path |\n|------------------------+-------------|\n| Steel Bank Common Lisp | SBCL        |\n| Clozure Common Lisp    | CCL         |\n| CMU Common Lisp        | CMUCL       |\n| GNU CLISP              | CLISP       |\n| Embeddable Common Lisp | ECL         |\n| ManKai Common Lisp     | MKCL        |\n| Allegro CL             | ALLEGRO     |\n| Armed Bear Common Lisp | ABCL        |\n|------------------------+-------------|\n\nFor example to build MAL with Clozure CL installed in\n~\\~/.roswell/impls/x86-64/linux/ccl-bin/1.11/lx86cl64~, you need to do the\nfollowing\n\n#+BEGIN_SRC sh\n  cd common-lisp ; LISP=ccl CCL=~/.roswell/impls/x86-64/linux/ccl-bin/1.11/lx86cl64 make\n#+END_SRC\n\nYou can use the variables ~*cl-implementation*~ and ~*cl-version*~ can be used\nto in MAL REPL to check the Common Lisp implementation and the version used for\nbuilding it.\n\n** Interop\n\nThere is some basic interop in the form ~cl-eval~ which takes a string and\nevaluates it as Common Lisp code, the result is returned in form of a MAL value,\nas such you are limited to code that produces values that have MAL counterparts.\n\n** Known Issues\n   ABCL takes a long to boot as such it needs to be run with ~TEST_OPTS~ set to\n   ~--start-timeout 120~\n"
  },
  {
    "path": "impls/common-lisp/fake-readline.lisp",
    "content": ";; For some reason MKCL fails to find libreadline.so as a result cl-readline\n;; fails. To avoid conditionals in the code we fake the cl-readline interface\n;; and use it in asdf definitions when running under MKCL\n(defpackage   :cl-readline\n  (:nicknames :rl)\n  (:use :common-lisp))\n\n(in-package :cl-readline)\n\n(defun readline (&keys prompt already-prompted num-chars\n                       erase-empty-line add-history novelty-check)\n  (declare (ignorable ignored))\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun register-function (&rest ignored)\n  (declare (ignorable ignored)))\n"
  },
  {
    "path": "impls/common-lisp/hist/.keepdir",
    "content": ""
  },
  {
    "path": "impls/common-lisp/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/common-lisp/run-abcl.lisp",
    "content": "(require 'asdf)\n(push *default-pathname-defaults* asdf:*central-registry*)\n\n;; Suppress compilation output\n(let ((*error-output* (make-broadcast-stream))\n      (*standard-output* (make-broadcast-stream)))\n  (asdf:load-system (car ext:*command-line-argument-list*) :verbose nil))\n\n(mal:main (cdr ext:*command-line-argument-list*))\n(cl-user::quit)\n"
  },
  {
    "path": "impls/common-lisp/run-mkcl.lisp",
    "content": "(require 'asdf)\n(push *default-pathname-defaults* asdf:*central-registry*)\n\n(defvar *raw-command-line-args* (loop\n                                   :for index\n                                   :from 1\n                                   :below (mkcl:argc)\n                                   :collect (mkcl:argv index)))\n\n(defvar *command-line-args* (subseq *raw-command-line-args*\n                                    (min (1+ (position \"--\" *raw-command-line-args* :test #'string=))\n                                         (length *raw-command-line-args*))))\n\n;; Suppress compilation output\n(let ((*error-output* (make-broadcast-stream))\n      (*standard-output* (make-broadcast-stream)))\n  (format *standard-output* \"~a\" *command-line-args*)\n  (asdf:load-system (car *command-line-args*) :verbose nil))\n\n(mal:main (cdr *command-line-args*))\n(quit)\n"
  },
  {
    "path": "impls/common-lisp/src/core.lisp",
    "content": "(defpackage :core\n  (:use :common-lisp\n        :utils\n        :types\n        :reader\n        :printer\n        :genhash\n        :alexandria)\n  (:export :ns))\n\n(in-package :core)\n\n(define-condition index-error (mal-error)\n  ((size :initarg :size :reader index-error-size)\n   (index :initarg :index :reader index-error-index)\n   (sequence :initarg :sequence :reader index-error-sequence))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"Index out of range (~a), length is ~a but index given was ~a\"\n                     (printer:pr-str (index-error-sequence condition))\n                     (index-error-size condition)\n                     (index-error-index condition)))))\n\n(defmacro wrap-boolean (form)\n  `(if ,form mal-true mal-false))\n\n(defvar ns nil)\n\n(defmacro defmal (name arglist &rest body)\n  (let* ((symbol-name (if (stringp name)\n                          name\n                          ;; Since common lisp intern all the symbols in\n                          ;; uppercase (by default) we need to convert the\n                          ;; symbol to lowercase while introducing it in MAL\n                          ;; environment\n                          (string-downcase (symbol-name name))))\n         (internal-name (format nil \"MAL-~a\" (string-upcase symbol-name))))\n    `(push (cons (make-mal-symbol ,symbol-name)\n                 (make-mal-builtin-fn (defun ,(intern internal-name) ,arglist ,@body)))\n           ns)))\n\n(defmal + (value1 value2)\n  (make-mal-number (+ (mal-data-value value1) (mal-data-value value2))))\n\n(defmal - (value1 value2)\n  (make-mal-number (- (mal-data-value value1) (mal-data-value value2))))\n\n(defmal * (value1 value2)\n  (make-mal-number (* (mal-data-value value1) (mal-data-value value2))))\n\n(defmal / (value1 value2)\n  (make-mal-number (round (/ (mal-data-value value1) (mal-data-value value2)))))\n\n(defmal prn (&rest strings)\n  ;; Using write-line instead of (format *standard-output* ... ) since the later prints\n  ;; and extra newline at start in GNU CLISP, if environment variable PERL_RL is true\n  ;; or terminal is dumb\n  (write-line (format nil\n                      \"~{~a~^ ~}\"\n                      (mapcar (lambda (string) (printer:pr-str string t))\n                              strings)))\n  mal-nil)\n\n(defmal println (&rest strings)\n  ;; Using write-line instead of (format *standard-output* ... ) since the later prints\n  ;; and extra newline at start in GNU CLISP, if environment variable PERL_RL is true\n  ;; or terminal is dumb\n  (write-line (format nil\n                      \"~{~a~^ ~}\"\n                      (mapcar (lambda (string) (printer:pr-str string nil))\n                              strings)))\n  mal-nil)\n\n(defmal pr-str (&rest strings)\n  (make-mal-string (format nil\n                           \"~{~a~^ ~}\"\n                           (mapcar (lambda (string) (printer:pr-str string t))\n                                   strings))))\n\n(defmal str (&rest strings)\n  (make-mal-string (format nil\n                           \"~{~a~}\"\n                           (mapcar (lambda (string) (printer:pr-str string nil))\n                                   strings))))\n\n(defmal list (&rest values)\n  (make-mal-list values))\n\n(defmal list? (value)\n  (wrap-boolean (mal-list-p value)))\n\n(defmal empty? (value)\n  (wrap-boolean (zerop (length (mal-data-value value)))))\n\n(defmal count (value)\n  (make-mal-number (length (mal-data-value value))))\n\n(defmal = (value1 value2)\n  (wrap-boolean (mal-data-value= value1 value2)))\n\n(defmal < (value1 value2)\n  (wrap-boolean (< (mal-data-value value1) (mal-data-value value2))))\n\n(defmal > (value1 value2)\n  (wrap-boolean (> (mal-data-value value1) (mal-data-value value2))))\n\n(defmal <= (value1 value2)\n  (wrap-boolean (<= (mal-data-value value1) (mal-data-value value2))))\n\n(defmal >= (value1 value2)\n  (wrap-boolean (>= (mal-data-value value1) (mal-data-value value2))))\n\n(defmal read-string (value)\n  (reader:read-str (mal-data-value value)))\n\n(defmal slurp (filename)\n  (make-mal-string (read-file-string (mal-data-value filename))))\n\n(defmal atom (value)\n  (make-mal-atom value))\n\n(defmal atom? (value)\n  (wrap-boolean (mal-atom-p value)))\n\n(defmal deref (atom)\n  (mal-data-value atom))\n\n(defmal reset! (atom value)\n  (setf (mal-data-value atom) value))\n\n(defmal swap! (atom fn &rest args)\n  (setf (mal-data-value atom)\n        (apply (mal-data-value fn)\n               (append (list (mal-data-value atom)) args))))\n\n(defmal vec (list)\n  (make-mal-vector (listify (mal-data-value list))))\n\n(defmal cons (element list)\n  (make-mal-list (cons element (listify (mal-data-value list)))))\n\n(defmal concat (&rest lists)\n  (make-mal-list (apply #'concatenate 'list (mapcar #'mal-data-value lists))))\n\n(defmal nth (sequence index)\n  (or (nth (mal-data-value index)\n           (listify (mal-data-value sequence)))\n      (error 'index-error\n             :size (length (mal-data-value sequence))\n             :index (mal-data-value index)\n             :sequence sequence)))\n\n(defmal first (sequence)\n  (or (first (listify (mal-data-value sequence))) mal-nil))\n\n(defmal rest (sequence)\n  (make-mal-list (rest (listify (mal-data-value sequence)))))\n\n(defmal throw (value)\n  (error 'mal-user-exception :data value))\n\n(defmal apply (fn &rest values)\n  (let ((last (listify (mal-data-value (car (last values)))))\n        (butlast (butlast values)))\n    (apply (mal-data-value fn) (append butlast last))))\n\n(defmal map (fn sequence)\n  (let ((applicants (listify (mal-data-value sequence))))\n    (make-mal-list (mapcar (mal-data-value fn) applicants))))\n\n(defmal nil? (value)\n  (wrap-boolean (mal-nil-p value)))\n\n(defmal true? (value)\n  (wrap-boolean (and (mal-boolean-p value) (mal-data-value value))))\n\n(defmal false? (value)\n  (wrap-boolean (and (mal-boolean-p value) (not (mal-data-value value)))))\n\n(defmal number? (value)\n  (wrap-boolean (mal-number-p value)))\n\n(defmal symbol (string)\n  (make-mal-symbol (mal-data-value string)))\n\n(defmal symbol? (value)\n  (wrap-boolean (mal-symbol-p value)))\n\n(defmal keyword (keyword)\n  (if (mal-keyword-p keyword)\n      keyword\n      (make-mal-keyword (format nil \":~a\" (mal-data-value keyword)))))\n\n(defmal keyword? (value)\n  (wrap-boolean (mal-keyword-p value)))\n\n(defmal vector (&rest elements)\n  (make-mal-vector (map 'vector #'identity elements)))\n\n(defmal vector? (value)\n  (wrap-boolean (mal-vector-p value)))\n\n(defmal fn? (value)\n  (wrap-boolean (or (mal-builtin-fn-p value)\n                    (and (mal-fn-p value)\n                         (not (cdr (assoc :is-macro (mal-data-attrs value))))))))\n\n(defmal macro? (value)\n  (wrap-boolean (and (mal-fn-p value)\n                     (cdr (assoc :is-macro (mal-data-attrs value))))))\n\n(defmal hash-map (&rest elements)\n  (let ((hash-map (make-mal-value-hash-table)))\n   (loop for (key value) on elements\n       by #'cddr\n       do (setf (hashref key hash-map) value))\n    (make-mal-hash-map hash-map)))\n\n(defmal map? (value)\n  (wrap-boolean (mal-hash-map-p value)))\n\n(defmal assoc (hash-map &rest elements)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-map (make-mal-value-hash-table)))\n\n    (hashmap (lambda (key value)\n               (declare (ignorable value))\n               (setf (hashref key new-hash-map)\n                     (hashref key hash-map-value)))\n             hash-map-value)\n\n    (loop for (key value) on elements\n       by #'cddr\n       do (setf (hashref key new-hash-map) value))\n\n    (make-mal-hash-map new-hash-map)))\n\n(defmal dissoc (hash-map &rest elements)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-map (make-mal-value-hash-table)))\n\n    (hashmap (lambda (key value)\n               (declare (ignorable value))\n               (when (not (member key elements :test #'mal-data-value=))\n                 (setf (hashref key new-hash-map)\n                       (hashref key hash-map-value))))\n             hash-map-value)\n\n    (make-mal-hash-map new-hash-map)))\n\n(defmal get (hash-map key)\n  (or (and (mal-hash-map-p hash-map) (hashref key (mal-data-value hash-map)))\n      types:mal-nil))\n\n(defmal contains? (hash-map key)\n  (if (genhash:hashref key (types:mal-data-value hash-map)) types:mal-true types:mal-false))\n\n(defmal keys (hash-map)\n  (let ((hash-map-value (mal-data-value hash-map))\n        keys)\n\n    (hashmap (lambda (key value)\n               (declare (ignorable value))\n               (push key keys))\n             hash-map-value)\n\n    (make-mal-list (nreverse keys))))\n\n(defmal vals (hash-map)\n  (let ((hash-map-value (mal-data-value hash-map))\n        values)\n\n    (hashmap (lambda (key value)\n               (declare (ignorable key))\n               (push value values))\n             hash-map-value)\n\n    (make-mal-list (nreverse values))))\n\n(defmal sequential? (value)\n  (wrap-boolean (or (mal-vector-p value) (mal-list-p value))))\n\n(defmal readline (prompt)\n  (format *standard-output* (mal-data-value prompt))\n  (force-output *standard-output*)\n  (make-mal-string (read-line *standard-input* nil)))\n\n(defmal string? (value)\n  (wrap-boolean (mal-string-p value)))\n\n(defmal time-ms ()\n  (make-mal-number (round (/ (get-internal-real-time)\n                             (/ internal-time-units-per-second\n                                1000)))))\n\n(defmal conj (value &rest elements)\n  (cond ((mal-list-p value)\n         (make-mal-list (append (nreverse elements)\n                                (mal-data-value value))))\n        ((mal-vector-p value)\n         (make-mal-vector (concatenate 'vector\n                                       (mal-data-value value)\n                                       elements)))\n        (t (error 'mal-user-exception))))\n\n(defmal seq (value)\n  (if (zerop (length (mal-data-value value)))\n      mal-nil\n      (cond ((mal-list-p value) value)\n            ((mal-vector-p value)\n             (make-mal-list (listify (mal-data-value value))))\n            ((mal-string-p value)\n             (make-mal-list (mapcar (alexandria:compose #'make-mal-string #'string)\n                                    (coerce (mal-data-value value) 'list))))\n            (t (error 'mal-user-exception)))))\n\n(defmal with-meta (value meta)\n  (funcall (switch-mal-type value\n             (types:string #'make-mal-string)\n             (types:symbol #'make-mal-symbol)\n             (types:list #'make-mal-list)\n             (types:vector #'make-mal-vector)\n             (types:hash-map #'make-mal-hash-map)\n             (types:fn #'make-mal-fn)\n             (types:builtin-fn #'make-mal-builtin-fn))\n           (mal-data-value value)\n           :meta meta\n           :attrs (mal-data-attrs value)))\n\n(defmal meta (value)\n  (or (types:mal-data-meta value) types:mal-nil))\n\n(defun wrap-value (value &optional booleanp listp)\n  (typecase value\n    (number (make-mal-number value))\n    ;; This needs to be before symbol since nil is a symbol\n    (null (cond (booleanp mal-false)\n                (listp (make-mal-list value))\n                (t mal-nil)))\n    ;; This needs to before symbol since t, nil are symbols\n    (boolean (if value mal-true mal-nil))\n    (keyword (make-mal-keyword value))\n    (symbol (make-mal-symbol (symbol-name value)))\n    (string (make-mal-string value))\n    (list (make-mal-list (map 'list #'wrap-value value)))\n    (vector (make-mal-vector (map 'vector #'wrap-value value)))\n    (hash-table (make-mal-hash-map (let ((new-hash-table (make-mal-value-hash-table)))\n                                     (hashmap (lambda (key value)\n                                                (setf (hashref (wrap-value key) new-hash-table)\n                                                      (wrap-value value)))\n                                              value)\n                                     new-hash-table)))))\n\n;; Since a nil in Common LISP may mean an empty list or boolean false or\n;; simply nil, the caller can specify the preferred type while evaluating an\n;; expression\n(defmal cl-eval (code &optional booleanp listp)\n  (wrap-value (eval (read-from-string (mal-data-value code)))\n              (and booleanp (mal-data-value booleanp))\n              (and listp (mal-data-value listp))))\n"
  },
  {
    "path": "impls/common-lisp/src/env.lisp",
    "content": "(defpackage :env\n  (:use :common-lisp :types)\n  (:shadow :symbol)\n  (:export :undefined-symbol\n           :create-mal-env\n           :get-env\n           :set-env\n           :mal-env-bindings))\n\n(in-package :env)\n\n(define-condition undefined-symbol (mal-runtime-exception)\n  ((symbol :initarg :symbol :reader symbol))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"'~a' not found\"\n                     (symbol condition)))))\n\n(define-condition arity-mismatch (mal-runtime-exception)\n  ((required :initarg :required :reader required)\n   (provided :initarg :provided :reader provided))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"Unexpected number of arguments provided, expected ~a, got ~a\"\n                     (required condition)\n                     (provided condition)))))\n\n(defstruct mal-env\n  (bindings (make-hash-table :test 'equal) :read-only t)\n  (parent nil :read-only t))\n\n(defun get-env (env symbol)\n  (or (gethash symbol (mal-env-bindings env))\n      (let ((outer (mal-env-parent env)))\n        (if outer\n          (get-env outer symbol)\n          nil))))\n\n(defun set-env (env symbol value)\n  (setf (gethash (mal-data-value symbol) (mal-env-bindings env)) value))\n\n(defun create-mal-env (&key parent binds exprs)\n  (let ((env (make-mal-env :parent parent))\n        (params-length (length binds))\n        (arg-length (length exprs)))\n\n    (flet ((arity-mismatch ()\n             (error 'arity-mismatch\n                    :required params-length\n                    :provided arg-length)))\n      (loop\n         for key = (pop binds)\n         while key\n         do (if (string/= (mal-data-value key) \"&\")\n                (set-env env key (or (pop exprs)\n                                     (arity-mismatch)))\n                (progn (set-env env\n                                (or (pop binds) (arity-mismatch))\n                                (make-mal-list exprs))\n                       (setq binds nil))))\n      env)))\n"
  },
  {
    "path": "impls/common-lisp/src/printer.lisp",
    "content": "(defpackage :printer\n  (:use :common-lisp\n        :types)\n  (:import-from :genhash\n                :hashmap)\n  (:import-from :cl-ppcre\n                :regex-replace)\n  (:import-from :utils\n                :replace-all\n                :listify)\n  (:export :pr-str))\n\n(in-package :printer)\n\n(defun pr-mal-sequence (start-delimiter sequence end-delimiter &optional (print-readably t))\n  (format nil\n          \"~a~{~a~^ ~}~a\"\n          start-delimiter\n          (mapcar (lambda (value)\n                    (pr-str value print-readably))\n                  (listify (mal-data-value sequence)))\n          end-delimiter))\n\n(defun pr-mal-hash-map (hash-map &optional (print-readably t) &aux repr)\n  (hashmap (lambda (key value)\n             (push (pr-str value print-readably) repr)\n             (push (pr-str key   print-readably) repr))\n           (mal-data-value hash-map))\n  (format nil \"{~{~a ~a~^ ~}}\" repr))\n\n(defun pr-string (ast &optional (print-readably t))\n  (if print-readably\n      (replace-all (prin1-to-string (mal-data-value ast))\n                   \"\n\"\n                   \"\\\\n\")\n      (mal-data-value ast)))\n\n(defun pr-str (ast &optional (print-readably t))\n  (when ast\n    (switch-mal-type ast\n      (types:number (format nil \"~d\" (mal-data-value ast)))\n      (types:boolean (if (mal-data-value ast) \"true\" \"false\"))\n      (types:nil \"nil\")\n      (types:string (pr-string ast print-readably))\n      (types:symbol (format nil \"~a\" (mal-data-value ast)))\n      (types:keyword (format nil \"~a\" (mal-data-value ast)))\n      (types:list (pr-mal-sequence \"(\" ast \")\" print-readably))\n      (types:vector (pr-mal-sequence \"[\" ast \"]\" print-readably))\n      (types:hash-map (pr-mal-hash-map ast print-readably))\n      (types:atom (format nil \"(atom ~a)\" (pr-str (mal-data-value ast))))\n      (types:fn \"#<func>\")\n      (types:builtin-fn \"#<builtin-func>\"))))\n"
  },
  {
    "path": "impls/common-lisp/src/reader.lisp",
    "content": "(defpackage :reader\n  (:use :common-lisp\n        :types\n        :alexandria)\n  (:import-from :genhash\n                :hashref)\n  (:import-from :cl-ppcre\n                :create-scanner\n                :do-matches-as-strings\n                :scan)\n  (:import-from :utils\n                :replace-all)\n  (:export :read-str\n           :eof\n           :unexpected-token))\n\n(in-package :reader)\n\n;; Possible errors that can be raised while reading a string\n(define-condition unexpected-token (error)\n  ((expected :initarg :expected :reader expected-token)\n   (actual :initarg :actual :reader actual-token))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"Unexpected token (~a) encountered while reading, expected ~a\"\n                     (actual-token condition)\n                     (expected-token condition))))\n  (:documentation \"Error raised when an unexpected token is encountered while reading.\"))\n\n(define-condition eof (error)\n  ((context :initarg :context :reader context))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"EOF encountered while reading '~a'\"\n                     (context condition))))\n  (:documentation \"Error raised when EOF is encountered while reading.\"))\n\n(defvar *tokenizer-re* (create-scanner \"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\")\n  \"Regular expression to tokenize Lisp code\")\n\n(defvar *number-re* (create-scanner \"^(-|\\\\+)?[\\\\d]+$\")\n  \"Regular expression to match a number\")\n\n(defvar *string-re* (create-scanner \"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\")\n  \"Regular expression to match a string\")\n\n(defvar *whitespace-chars*\n  '(#\\Space #\\Newline #\\Backspace #\\Tab\n    #\\Linefeed #\\Page #\\Return #\\Rubout #\\,)\n  \"Characters to treat as whitespace, these are trimmed in `tokenize'\")\n\n(defun tokenize (string)\n  \"Tokenize given string.\n\nThis function extracts all tokens from the string using *tokenizer-re*\ncomments are ignored.\n\nImplementation notes: The regex scan generates some empty tokens, not really\nsure why.\"\n  (let (tokens)\n    (do-matches-as-strings (match *tokenizer-re* string)\n      (let ((token (string-trim *whitespace-chars* match)))\n        (unless (or (zerop (length token))\n                    (char= (char token 0) #\\;))\n          (push token tokens))))\n    (nreverse tokens)))\n\n;; Reader\n(defstruct (token-reader)\n  (tokens nil))\n\n(defun peek (reader)\n  \"Returns the next token in the reader without advancing the token stream.\"\n  (car (token-reader-tokens reader)))\n\n(defun next (reader)\n  \"Returns the next token and advances the token stream.\"\n  (pop (token-reader-tokens reader)))\n\n(defun consume (reader &optional (token nil token-provided-p))\n  \"Consume the next token and advance the token stream.\n\nIf the optional argument token is provided the token stream is advanced only\nif token being consumes matches it otherwise and unexpected token error is\nraised\"\n  (let ((actual-token (pop (token-reader-tokens reader))))\n    (when (and token-provided-p\n               (not (equal actual-token token)))\n      (error 'unexpected-token :expected token :actual actual-token)))\n  reader)\n\n(defun parse-string (token)\n  ;; read-from-string doesn't handle \\n\n  (if (and (> (length token) 1)\n           (scan *string-re* token))\n      (let ((input (subseq token 1 (1- (length token)))))\n        (with-output-to-string (out)\n          (with-input-from-string (in input)\n            (loop while (peek-char nil in nil)\n                  do (let ((char (read-char in)))\n                       (if (eql char #\\\\ )\n                           (let ((char (read-char in)))\n                             (if (eql char #\\n)\n                                 (terpri out)\n                               (princ char out)))\n                         (princ char out)))))))\n      (error 'eof :context \"string\")))\n\n(defun expand-quote (reader)\n  (let ((quote-sym (make-mal-symbol (switch ((next reader) :test #'string=)\n                                      (\"'\" \"quote\")\n                                      (\"`\" \"quasiquote\")\n                                      (\"~\" \"unquote\")\n                                      (\"~@\" \"splice-unquote\")\n                                      (\"@\" \"deref\")))))\n    (make-mal-list (list quote-sym (read-form reader)))))\n\n(defun read-mal-sequence (reader &optional (type 'list) &aux forms)\n  (let ((context (string-downcase (symbol-name type)))\n        (delimiter (if (equal type 'list) \")\" \"]\")))\n\n    ;; Consume the opening brace\n    (consume reader)\n\n    (setf forms (loop\n                   until (string= (peek reader) delimiter)\n                   collect (read-form-or-eof reader context)))\n\n    ;; Consume the closing brace\n    (consume reader)\n\n    (apply type forms)))\n\n(defun read-hash-map (reader)\n  (let ((map (make-mal-value-hash-table))\n        (context \"hash-map\"))\n\n    ;; Consume the open brace\n    (consume reader)\n\n    (loop\n       until (string= (peek reader) \"}\")\n       do (setf (hashref (read-form-or-eof reader context) map)\n                (read-form-or-eof reader context)))\n\n    ;; Consume the closing brace\n    (consume reader)\n\n    map))\n\n(defun read-atom (reader)\n  (let ((token (next reader)))\n    (cond ((string= token \"false\") mal-false)\n          ((string= token \"true\") mal-true)\n          ((string= token \"nil\") mal-nil)\n          ((char= (char token 0) #\\\") (make-mal-string (parse-string token)))\n          ((char= (char token 0) #\\:) (make-mal-keyword token))\n          ((scan *number-re* token) (make-mal-number (read-from-string token)))\n          (t (make-mal-symbol token)))))\n\n(defun read-form-with-meta (reader)\n  (consume reader)\n\n  (let ((meta (read-form-or-eof reader \"object meta\"))\n        (value (read-form-or-eof reader \"object meta\")))\n    (make-mal-list (list (make-mal-symbol \"with-meta\") value meta))))\n\n(defun read-form (reader)\n  (switch ((peek reader) :test #'equal)\n    (nil nil)\n    (\"(\" (make-mal-list (read-mal-sequence reader 'list)))\n    (\"[\" (make-mal-vector (read-mal-sequence reader 'vector)))\n    (\"{\" (make-mal-hash-map (read-hash-map reader)))\n    (\"^\" (read-form-with-meta reader))\n    (\"'\" (expand-quote reader))\n    (\"`\" (expand-quote reader))\n    (\"~\" (expand-quote reader))\n    (\"~@\" (expand-quote reader))\n    (\"@\" (expand-quote reader))\n    (t (read-atom reader))))\n\n(defun read-form-or-eof (reader context)\n  (or (read-form reader)\n      (error 'eof :context context)))\n\n(defun read-str (string)\n  (read-form (make-token-reader :tokens (tokenize string))))\n"
  },
  {
    "path": "impls/common-lisp/src/step0_repl.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp)\n  (:import-from :uiop\n                :getenv)\n  (:import-from :cl-readline\n                :readline)\n  (:export :main))\n\n(in-package :mal)\n\n(defun mal-read (string)\n  string)\n\n(defun mal-eval (ast)\n  ast)\n\n(defun mal-print (expression)\n  expression)\n\n(defun rep (string)\n  (mal-print (mal-eval (mal-read string))))\n\n(defvar *use-readline-p* nil)\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun main (&optional (argv nil argv-provided-p))\n  (declare (ignorable argv argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (uiop:getenv \"PERL_RL\") \"false\")\n                                  (string= (uiop:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line (mal-writeline (rep line)) (return)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step1_read_print.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :reader\n        :printer)\n  (:import-from :utils\n                :getenv)\n  (:import-from :cl-readline\n                :readline)\n  (:export :main))\n\n(in-package :mal)\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (declare (ignorable env))\n  ast)\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) (make-hash-table :test #'equal)))\n    (reader:eof (condition)\n      (format nil \"~a\" condition))))\n\n(defvar *use-readline-p* nil)\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun main (&optional (argv nil argv-provided-p))\n  (declare (ignorable argv argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line (mal-writeline (rep line)) (return)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step2_eval.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(defvar *repl-env* (make-hash-table :test 'equal))\n\n(setf (gethash \"+\" *repl-env*)\n      (make-mal-builtin-fn (lambda (value1 value2)\n                                   (make-mal-number (+ (mal-data-value value1)\n                                                       (mal-data-value value2))))))\n\n(setf (gethash \"-\" *repl-env*)\n      (make-mal-builtin-fn (lambda (value1 value2)\n                                   (make-mal-number (- (mal-data-value value1)\n                                                       (mal-data-value value2))))))\n\n(setf (gethash \"*\" *repl-env*)\n      (make-mal-builtin-fn (lambda (value1 value2)\n                                   (make-mal-number (* (mal-data-value value1)\n                                                       (mal-data-value value2))))))\n\n(setf (gethash \"/\" *repl-env*)\n      (make-mal-builtin-fn (lambda (value1 value2)\n                                   (make-mal-number (/ (mal-data-value value1)\n                                                       (mal-data-value value2))))))\n\n(defun lookup-env (symbol env)\n  (let ((key (mal-data-value symbol)))\n    (multiple-value-bind (value present-p) (gethash key env)\n      (if present-p\n        value\n        (error 'env:undefined-symbol\n               :symbol (format nil \"~a\" key))))))\n\n(defun eval-sequence (type sequence env)\n  (map type\n       (lambda (ast) (mal-eval ast env))\n       (mal-data-value sequence)))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun mal-eval (ast env)\n  ;; (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n  ;; (force-output *standard-output*)\n  (switch-mal-type ast\n    (types:symbol (lookup-env ast env))\n    (types:list (eval-list ast env))\n    (types:vector (make-mal-vector (eval-sequence 'vector ast env)))\n    (types:hash-map (eval-hash-map ast env ))\n    (types:any ast)))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun eval-list (ast env)\n  (if (null (mal-data-value ast))\n    ast\n    (let ((evaluated-list (eval-sequence 'list ast env)))\n           (apply (mal-data-value (car evaluated-list))\n                  (cdr evaluated-list)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (error (condition)\n      (format nil \"~a\" condition))))\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of *repl-env*\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun main (&optional (argv nil argv-provided-p))\n  (declare (ignorable argv argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line (mal-writeline (rep line)) (return)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step3_env.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :genhash)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(defvar *repl-env* (env:create-mal-env))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"+\")\n             (make-mal-builtin-fn (lambda (value1 value2)\n                                    (make-mal-number (+ (mal-data-value value1)\n                                                        (mal-data-value value2))))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"-\")\n             (make-mal-builtin-fn (lambda (value1 value2)\n                                    (make-mal-number (- (mal-data-value value1)\n                                                        (mal-data-value value2))))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"*\")\n             (make-mal-builtin-fn (lambda (value1 value2)\n                                    (make-mal-number (* (mal-data-value value1)\n                                                        (mal-data-value value2))))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"/\")\n             (make-mal-builtin-fn (lambda (value1 value2)\n                                    (make-mal-number (/ (mal-data-value value1)\n                                                        (mal-data-value value2))))))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n\n(defun eval-sequence (type sequence env)\n  (map type\n       (lambda (ast) (mal-eval ast env))\n       (mal-data-value sequence)))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun mal-eval (ast env)\n  (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n    (when (and debug-eval\n               (not (mal-data-value= debug-eval mal-false))\n               (not (mal-data-value= debug-eval mal-false)))\n      (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n      (force-output *standard-output*)))\n  (switch-mal-type ast\n    (types:symbol\n     (let ((key (mal-data-value ast)))\n       (or (env:get-env env key)\n           (error 'undefined-symbol :symbol (format nil \"~a\" key)))))\n    (types:list (eval-list ast env))\n    (types:vector (make-mal-vector (eval-sequence 'vector ast env)))\n    (types:hash-map (eval-hash-map ast env ))\n    (types:any ast)))\n\n(defun eval-let* (forms env)\n  (let ((new-env (env:create-mal-env :parent env))\n        (bindings (utils:listify (mal-data-value (second forms)))))\n\n    (mapcar (lambda (binding)\n              (env:set-env new-env\n                           (car binding)\n                           (mal-eval (or (cdr binding)\n                                         mal-nil)\n                                     new-env)))\n            (loop\n               for (symbol value) on bindings\n               by #'cddr\n               collect (cons symbol value)))\n\n    (mal-eval (third forms) new-env)))\n\n(defun eval-list (ast env)\n  (let ((forms (mal-data-value ast)))\n    (cond\n      ((zerop (length forms)) ast)\n      ((mal-data-value= mal-def! (first forms))\n       (env:set-env env (second forms) (mal-eval (third forms) env)))\n      ((mal-data-value= mal-let* (first forms))\n       (eval-let* forms env))\n      (t (let ((evaluated-list (eval-sequence 'list ast env)))\n           (apply (mal-data-value (car evaluated-list))\n                  (cdr evaluated-list)))))))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (error (condition)\n      (format nil \"~a\" condition))))\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun main (&optional (argv nil argv-provided-p))\n  (declare (ignorable argv argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line (mal-writeline (rep line)) (return)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step4_if_fn_do.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n\n(defun eval-sequence (type sequence env)\n  (map type\n       (lambda (ast) (mal-eval ast env))\n       (mal-data-value sequence)))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun mal-eval (ast env)\n  (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n    (when (and debug-eval\n               (not (mal-data-value= debug-eval mal-false))\n               (not (mal-data-value= debug-eval mal-false)))\n      (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n      (force-output *standard-output*)))\n  (switch-mal-type ast\n    (types:symbol\n     (let ((key (mal-data-value ast)))\n       (or (env:get-env env key)\n           (error 'undefined-symbol :symbol (format nil \"~a\" key)))))\n    (types:list (eval-list ast env))\n    (types:vector (make-mal-vector (eval-sequence 'vector ast env)))\n    (types:hash-map (eval-hash-map ast env))\n    (types:any ast)))\n\n(defun eval-let* (forms env)\n  (let ((new-env (env:create-mal-env :parent env))\n        (bindings (utils:listify (mal-data-value (second forms)))))\n\n    (mapcar (lambda (binding)\n              (env:set-env new-env\n                           (car binding)\n                           (mal-eval (or (cdr binding)\n                                         mal-nil)\n                                     new-env)))\n            (loop\n               for (symbol value) on bindings\n               by #'cddr\n               collect (cons symbol value)))\n\n    (mal-eval (third forms) new-env)))\n\n(defun eval-list (ast env)\n  (let ((forms (mal-data-value ast)))\n    (cond\n      ((zerop (length forms)) ast)\n      ((mal-data-value= mal-def! (first forms))\n       (env:set-env env (second forms) (mal-eval (third forms) env)))\n      ((mal-data-value= mal-let* (first forms))\n       (eval-let* forms env))\n      ((mal-data-value= mal-do (first forms))\n       (car (last (mapcar (lambda (form) (mal-eval form env))\n                          (cdr forms)))))\n      ((mal-data-value= mal-if (first forms))\n       (let ((predicate (mal-eval (second forms) env)))\n         (mal-eval (if (or (mal-data-value= predicate mal-nil)\n                           (mal-data-value= predicate mal-false))\n                       (or (fourth forms) mal-nil)\n                       (third forms))\n                   env)))\n      ((mal-data-value= mal-fn* (first forms))\n       (make-mal-fn (let ((arglist (second forms))\n                                (body (third forms)))\n                            (lambda (&rest args)\n                              (mal-eval body (env:create-mal-env :parent env\n                                                                 :binds (listify (mal-data-value arglist))\n                                                                 :exprs args))))))\n      (t (let* ((evaluated-list (eval-sequence 'list ast env))\n               (function (car evaluated-list)))\n         ;; If first element is a mal function unwrap it\n         (apply (mal-data-value function)\n                (cdr evaluated-list)))))))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (error (condition)\n      (format nil \"~a\" condition))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun main (&optional (argv nil argv-provided-p))\n  (declare (ignorable argv argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line (mal-writeline (rep line)) (return)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step5_tco.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n\n(defun eval-sequence (type sequence env)\n  (map type\n       (lambda (ast) (mal-eval ast env))\n       (mal-data-value sequence)))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (loop\n     do (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n          (when (and debug-eval\n                     (not (mal-data-value= debug-eval mal-false))\n                     (not (mal-data-value= debug-eval mal-false)))\n            (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n            (force-output *standard-output*)))\n     do (switch-mal-type ast\n          (types:symbol\n           (return\n             (let ((key (mal-data-value ast)))\n               (or (env:get-env env key)\n                   (error 'undefined-symbol :symbol (format nil \"~a\" key))))))\n          (types:vector (return (make-mal-vector (eval-sequence 'vector ast env))))\n          (types:hash-map (return (eval-hash-map ast env)))\n          (types:list\n            (let ((forms (mal-data-value ast)))\n               (cond\n                 ((null forms)\n                  (return ast))\n\n                 ((mal-data-value= mal-def! (first forms))\n                  (return (env:set-env env (second forms) (mal-eval (third forms) env))))\n\n                 ((mal-data-value= mal-let* (first forms))\n                  (let ((new-env (env:create-mal-env :parent env))\n                        (bindings (utils:listify (mal-data-value (second forms)))))\n\n                    (mapcar (lambda (binding)\n                              (env:set-env new-env\n                                           (car binding)\n                                           (mal-eval (or (cdr binding)\n                                                         mal-nil)\n                                                     new-env)))\n                            (loop\n                               for (symbol value) on bindings\n                               by #'cddr\n                               collect (cons symbol value)))\n                    (setf ast (third forms)\n                          env new-env)))\n\n                 ((mal-data-value= mal-do (first forms))\n                  (mapc (lambda (form) (mal-eval form env))\n                        (butlast (cdr forms)))\n                  (setf ast (car (last forms))))\n\n                 ((mal-data-value= mal-if (first forms))\n                  (let ((predicate (mal-eval (second forms) env)))\n                    (setf ast (if (or (mal-data-value= predicate mal-nil)\n                                      (mal-data-value= predicate mal-false))\n                                  (or (fourth forms) mal-nil)\n                                  (third forms)))))\n\n                 ((mal-data-value= mal-fn* (first forms))\n                  (return (let ((arglist (second forms))\n                                (body (third forms)))\n                            (make-mal-fn (lambda (&rest args)\n                                           (mal-eval body (env:create-mal-env :parent env\n                                                                              :binds (listify (mal-data-value arglist))\n                                                                              :exprs args)))\n                                         :attrs (list (cons :params arglist)\n                                                      (cons :ast body)\n                                                      (cons :env env))))))\n\n                 (t (let* ((evaluated-list (eval-sequence 'list ast env))\n                           (function (car evaluated-list)))\n                      ;; If first element is a mal function unwrap it\n                      (if (not (mal-fn-p function))\n                          (return (apply (mal-data-value function)\n                                         (cdr evaluated-list)))\n                          (let* ((attrs (mal-data-attrs function)))\n                            (setf ast (cdr (assoc :ast attrs))\n                                  env (env:create-mal-env :parent (cdr (assoc :env attrs))\n                                                          :binds (map 'list\n                                                                      #'identity\n                                                                      (mal-data-value (cdr (assoc :params attrs))))\n                                                          :exprs (cdr evaluated-list))))))))))\n          (types:any (return ast)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (error (condition)\n      (format nil \"~a\" condition))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun main (&optional (argv nil argv-provided-p))\n  (declare (ignorable argv argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line (mal-writeline (rep line)) (return)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step6_file.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n\n(defun eval-sequence (type sequence env)\n  (map type\n       (lambda (ast) (mal-eval ast env))\n       (mal-data-value sequence)))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (loop\n     do (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n          (when (and debug-eval\n                     (not (mal-data-value= debug-eval mal-false))\n                     (not (mal-data-value= debug-eval mal-false)))\n            (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n            (force-output *standard-output*)))\n     do (switch-mal-type ast\n          (types:symbol\n           (return\n             (let ((key (mal-data-value ast)))\n               (or (env:get-env env key)\n                   (error 'undefined-symbol :symbol (format nil \"~a\" key))))))\n          (types:vector (return (make-mal-vector (eval-sequence 'vector ast env))))\n          (types:hash-map (return (eval-hash-map ast env)))\n          (types:list\n            (let ((forms (mal-data-value ast)))\n               (cond\n                 ((null forms)\n                  (return ast))\n\n                 ((mal-data-value= mal-def! (first forms))\n                  (return (env:set-env env (second forms) (mal-eval (third forms) env))))\n\n                 ((mal-data-value= mal-let* (first forms))\n                  (let ((new-env (env:create-mal-env :parent env))\n                        (bindings (utils:listify (mal-data-value (second forms)))))\n\n                    (mapcar (lambda (binding)\n                              (env:set-env new-env\n                                           (car binding)\n                                           (mal-eval (or (cdr binding)\n                                                         mal-nil)\n                                                     new-env)))\n                            (loop\n                               for (symbol value) on bindings\n                               by #'cddr\n                               collect (cons symbol value)))\n                    (setf ast (third forms)\n                          env new-env)))\n\n                 ((mal-data-value= mal-do (first forms))\n                  (mapc (lambda (form) (mal-eval form env))\n                        (butlast (cdr forms)))\n                  (setf ast (car (last forms))))\n\n                 ((mal-data-value= mal-if (first forms))\n                  (let ((predicate (mal-eval (second forms) env)))\n                    (setf ast (if (or (mal-data-value= predicate mal-nil)\n                                      (mal-data-value= predicate mal-false))\n                                  (or (fourth forms) mal-nil)\n                                  (third forms)))))\n\n                 ((mal-data-value= mal-fn* (first forms))\n                  (return (let ((arglist (second forms))\n                                (body (third forms)))\n                            (make-mal-fn (lambda (&rest args)\n                                                 (mal-eval body (env:create-mal-env :parent env\n                                                                                    :binds (listify (mal-data-value arglist))\n                                                                                    :exprs args)))\n                                               :attrs (list (cons :params arglist)\n                                                            (cons :ast body)\n                                                            (cons :env env))))))\n\n                 (t (let* ((evaluated-list (eval-sequence 'list ast env))\n                           (function (car evaluated-list)))\n                      ;; If first element is a mal function unwrap it\n                      (if (not (mal-fn-p function))\n                          (return (apply (mal-data-value function)\n                                         (cdr evaluated-list)))\n                          (let* ((attrs (mal-data-attrs function)))\n                            (setf ast (cdr (assoc :ast attrs))\n                                  env (env:create-mal-env :parent (cdr (assoc :env attrs))\n                                                          :binds (map 'list\n                                                                      #'identity\n                                                                      (mal-data-value (cdr (assoc :params attrs))))\n                                                          :exprs (cdr evaluated-list))))))))))\n          (types:any (return ast)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (error (condition)\n      (format nil \"~a\" condition))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"eval\")\n             (make-mal-builtin-fn (lambda (ast)\n                                          (mal-eval ast *repl-env*))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun repl ()\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line\n                 (mal-writeline (rep line))\n                 (return)))))\n\n(defun run-file (file)\n  (rep (format nil \"(load-file \\\"~a\\\")\" file)))\n\n(defun main (&optional (argv nil argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (let ((args (if argv-provided-p\n                  argv\n                  (cdr (utils:raw-command-line-arguments)))))\n    (env:set-env *repl-env*\n                 (make-mal-symbol \"*ARGV*\")\n                 (make-mal-list (mapcar #'make-mal-string (cdr args))))\n    (if (null args)\n        (repl)\n        (run-file (car args)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step7_quote.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n(defvar mal-quote (make-mal-symbol \"quote\"))\n(defvar mal-quasiquote (make-mal-symbol \"quasiquote\"))\n(defvar mal-unquote (make-mal-symbol \"unquote\"))\n(defvar mal-splice-unquote (make-mal-symbol \"splice-unquote\"))\n(defvar mal-vec (make-mal-symbol \"vec\"))\n(defvar mal-cons (make-mal-symbol \"cons\"))\n(defvar mal-concat (make-mal-symbol \"concat\"))\n\n(defun eval-sequence (type sequence env)\n  (map type\n       (lambda (ast) (mal-eval ast env))\n       (mal-data-value sequence)))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun qq-reducer (elt acc)\n  (make-mal-list\n    (if (and (mal-list-p elt)\n             (mal-data-value= (first (mal-data-value elt)) mal-splice-unquote))\n      (list mal-concat (second (mal-data-value elt)) acc)\n      (list mal-cons (quasiquote elt) acc))))\n(defun qq-iter (elts)\n  (reduce #'qq-reducer elts :from-end t :initial-value (make-mal-list ())))\n(defun quasiquote (ast)\n  (switch-mal-type ast\n    (types:list     (if (mal-data-value= (first (mal-data-value ast)) mal-unquote)\n                      (second (mal-data-value ast))\n                      (qq-iter (mal-data-value ast))))\n    (types:vector   (make-mal-list (list mal-vec (qq-iter (listify (mal-data-value ast))))))\n    (types:hash-map (make-mal-list (list mal-quote ast)))\n    (types:symbol   (make-mal-list (list mal-quote ast)))\n    (types:any      ast)))\n\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (loop\n     do (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n          (when (and debug-eval\n                     (not (mal-data-value= debug-eval mal-false))\n                     (not (mal-data-value= debug-eval mal-false)))\n            (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n            (force-output *standard-output*)))\n     do (switch-mal-type ast\n          (types:symbol\n           (return\n             (let ((key (mal-data-value ast)))\n               (or (env:get-env env key)\n                   (error 'undefined-symbol :symbol (format nil \"~a\" key))))))\n          (types:vector (return (make-mal-vector (eval-sequence 'vector ast env))))\n          (types:hash-map (return (eval-hash-map ast env)))\n          (types:list\n            (let ((forms (mal-data-value ast)))\n               (cond\n                 ((null forms)\n                  (return ast))\n\n                 ((mal-data-value= mal-quote (first forms))\n                  (return (second forms)))\n\n                 ((mal-data-value= mal-quasiquote (first forms))\n                  (setf ast (quasiquote (second forms))))\n\n                 ((mal-data-value= mal-def! (first forms))\n                  (return (env:set-env env (second forms) (mal-eval (third forms) env))))\n\n                 ((mal-data-value= mal-let* (first forms))\n                  (let ((new-env (env:create-mal-env :parent env))\n                        (bindings (utils:listify (mal-data-value (second forms)))))\n\n                    (mapcar (lambda (binding)\n                              (env:set-env new-env\n                                           (car binding)\n                                           (mal-eval (or (cdr binding)\n                                                         mal-nil)\n                                                     new-env)))\n                            (loop\n                               for (symbol value) on bindings\n                               by #'cddr\n                               collect (cons symbol value)))\n                    (setf ast (third forms)\n                          env new-env)))\n\n                 ((mal-data-value= mal-do (first forms))\n                  (mapc (lambda (form) (mal-eval form env))\n                        (butlast (cdr forms)))\n                  (setf ast (car (last forms))))\n\n                 ((mal-data-value= mal-if (first forms))\n                  (let ((predicate (mal-eval (second forms) env)))\n                    (setf ast (if (or (mal-data-value= predicate mal-nil)\n                                      (mal-data-value= predicate mal-false))\n                                  (or (fourth forms) mal-nil)\n                                  (third forms)))))\n\n                 ((mal-data-value= mal-fn* (first forms))\n                  (return (let ((arglist (second forms))\n                                (body (third forms)))\n                            (make-mal-fn (lambda (&rest args)\n                                           (mal-eval body (env:create-mal-env :parent env\n                                                                              :binds (listify (mal-data-value arglist))\n                                                                              :exprs args)))\n                                         :attrs (list (cons :params arglist)\n                                                      (cons :ast body)\n                                                      (cons :env env))))))\n\n                 (t (let* ((evaluated-list (eval-sequence 'list ast env))\n                           (function (car evaluated-list)))\n                      ;; If first element is a mal function unwrap it\n                      (if (not (mal-fn-p function))\n                          (return (apply (mal-data-value function)\n                                         (cdr evaluated-list)))\n                          (let* ((attrs (mal-data-attrs function)))\n                            (setf ast (cdr (assoc :ast attrs))\n                                  env (env:create-mal-env :parent (cdr (assoc :env attrs))\n                                                          :binds (map 'list\n                                                                      #'identity\n                                                                      (mal-data-value (cdr (assoc :params attrs))))\n                                                          :exprs (cdr evaluated-list))))))))))\n          (types:any (return ast)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (error (condition)\n      (format nil \"~a\" condition))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"eval\")\n             (make-mal-builtin-fn (lambda (ast)\n                                    (mal-eval ast *repl-env*))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun repl ()\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line\n                 (mal-writeline (rep line))\n                 (return)))))\n\n(defun run-file (file)\n  (rep (format nil \"(load-file \\\"~a\\\")\" file)))\n\n(defun main (&optional (argv nil argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (let ((args (if argv-provided-p\n                  argv\n                  (cdr (utils:raw-command-line-arguments)))))\n    (env:set-env *repl-env*\n                 (make-mal-symbol \"*ARGV*\")\n                 (make-mal-list (mapcar #'make-mal-string (cdr args))))\n    (if (null args)\n        (repl)\n        (run-file (car args)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step8_macros.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(define-condition invalid-function (mal-runtime-exception)\n  ((form :initarg :form :reader form)\n   (context :initarg :context :reader context))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"Invalid function '~a' provided while ~a\"\n                     (printer:pr-str (form condition))\n                     (if (string= (context condition) \"apply\")\n                         \"applying\"\n                         \"defining macro\")))))\n\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n(defvar mal-quote (make-mal-symbol \"quote\"))\n(defvar mal-quasiquote (make-mal-symbol \"quasiquote\"))\n(defvar mal-unquote (make-mal-symbol \"unquote\"))\n(defvar mal-splice-unquote (make-mal-symbol \"splice-unquote\"))\n(defvar mal-vec (make-mal-symbol \"vec\"))\n(defvar mal-cons (make-mal-symbol \"cons\"))\n(defvar mal-concat (make-mal-symbol \"concat\"))\n(defvar mal-defmacro! (make-mal-symbol \"defmacro!\"))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun qq-reducer (elt acc)\n  (make-mal-list\n    (if (and (mal-list-p elt)\n             (mal-data-value= (first (mal-data-value elt)) mal-splice-unquote))\n      (list mal-concat (second (mal-data-value elt)) acc)\n      (list mal-cons (quasiquote elt) acc))))\n(defun qq-iter (elts)\n  (reduce #'qq-reducer elts :from-end t :initial-value (make-mal-list ())))\n(defun quasiquote (ast)\n  (switch-mal-type ast\n    (types:list     (if (mal-data-value= (first (mal-data-value ast)) mal-unquote)\n                      (second (mal-data-value ast))\n                      (qq-iter (mal-data-value ast))))\n    (types:vector   (make-mal-list (list mal-vec (qq-iter (listify (mal-data-value ast))))))\n    (types:hash-map (make-mal-list (list mal-quote ast)))\n    (types:symbol   (make-mal-list (list mal-quote ast)))\n    (types:any      ast)))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (loop\n     do (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n          (when (and debug-eval\n                     (not (mal-data-value= debug-eval mal-false))\n                     (not (mal-data-value= debug-eval mal-false)))\n            (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n            (force-output *standard-output*)))\n     do (switch-mal-type ast\n          (types:symbol\n           (return\n             (let ((key (mal-data-value ast)))\n               (or (env:get-env env key)\n                   (error 'undefined-symbol :symbol (format nil \"~a\" key))))))\n          (types:vector\n           (return (make-mal-vector (map 'vector (lambda (x) (mal-eval x env))\n                                         (mal-data-value ast)))))\n          (types:hash-map (return (eval-hash-map ast env)))\n          (types:list\n            (let ((forms (mal-data-value ast)))\n               (cond\n                 ((null forms)\n                  (return ast))\n\n                 ((mal-data-value= mal-quote (first forms))\n                  (return (second forms)))\n\n                 ((mal-data-value= mal-quasiquote (first forms))\n                  (setf ast (quasiquote (second forms))))\n\n                 ((mal-data-value= mal-def! (first forms))\n                  (return (env:set-env env (second forms) (mal-eval (third forms) env))))\n\n                 ((mal-data-value= mal-defmacro! (first forms))\n                  (let ((value (mal-eval (third forms) env)))\n                    (return (if (mal-fn-p value)\n                                (env:set-env env\n                                             (second forms)\n                                             (progn\n                                               (setf (cdr (assoc :is-macro (mal-data-attrs value))) t)\n                                               value))\n                                (error 'invalid-function\n                                       :form value\n                                       :context \"macro\")))))\n\n                 ((mal-data-value= mal-let* (first forms))\n                  (let ((new-env (env:create-mal-env :parent env))\n                        (bindings (utils:listify (mal-data-value (second forms)))))\n\n                    (mapcar (lambda (binding)\n                              (env:set-env new-env\n                                           (car binding)\n                                           (mal-eval (or (cdr binding)\n                                                         mal-nil)\n                                                     new-env)))\n                            (loop\n                               for (symbol value) on bindings\n                               by #'cddr\n                               collect (cons symbol value)))\n                    (setf ast (third forms)\n                          env new-env)))\n\n                 ((mal-data-value= mal-do (first forms))\n                  (mapc (lambda (form) (mal-eval form env))\n                        (butlast (cdr forms)))\n                  (setf ast (car (last forms))))\n\n                 ((mal-data-value= mal-if (first forms))\n                  (let ((predicate (mal-eval (second forms) env)))\n                    (setf ast (if (or (mal-data-value= predicate mal-nil)\n                                      (mal-data-value= predicate mal-false))\n                                  (or (fourth forms) mal-nil)\n                                  (third forms)))))\n\n                 ((mal-data-value= mal-fn* (first forms))\n                  (return (let ((arglist (second forms))\n                                (body (third forms)))\n                            (make-mal-fn (lambda (&rest args)\n                                           (mal-eval body (env:create-mal-env :parent env\n                                                                              :binds (listify (mal-data-value arglist))\n                                                                              :exprs args)))\n                                         :attrs (list (cons :params arglist)\n                                                      (cons :ast body)\n                                                      (cons :env env)\n                                                      (cons :is-macro nil))))))\n\n                 (t (let ((function (mal-eval (car forms) env))\n                          (args     (cdr forms)))\n                      ;; If first element is a mal function unwrap it\n                      (cond ((mal-fn-p function)\n                             (let ((attrs (mal-data-attrs function)))\n                              (if (cdr (assoc :is-macro attrs))\n                               (setf ast (apply (mal-data-value function) args))\n                               (setf ast (cdr (assoc :ast attrs))\n                                     env (env:create-mal-env :parent (cdr (assoc :env attrs))\n                                                             :binds (map 'list\n                                                                         #'identity\n                                                                         (mal-data-value (cdr (assoc :params attrs))))\n                                                             :exprs (map 'list (lambda (x) (mal-eval x env)) args))))))\n                            ((mal-builtin-fn-p function)\n                             (return (apply (mal-data-value function)\n                                            (map 'list (lambda (x) (mal-eval x env)) args))))\n                            (t (error 'invalid-function\n                                      :form function\n                                      :context \"apply\"))))))))\n          (types:any (return ast)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (mal-error (condition)\n      (format nil \"~a\" condition))\n    (error (condition)\n      (format nil \"Internal error: ~a\" condition))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"eval\")\n             (make-mal-builtin-fn (lambda (ast)\n                                    (mal-eval ast *repl-env*))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun repl ()\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line\n                 (mal-writeline (rep line))\n                 (return)))))\n\n(defun run-file (file)\n  (rep (format nil \"(load-file \\\"~a\\\")\" file)))\n\n(defun main (&optional (argv nil argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (let ((args (if argv-provided-p\n                  argv\n                  (cdr (utils:raw-command-line-arguments)))))\n    (env:set-env *repl-env*\n                 (make-mal-symbol \"*ARGV*\")\n                 (make-mal-list (mapcar #'make-mal-string (cdr args))))\n    (if (null args)\n        (repl)\n        (run-file (car args)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/step9_try.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(define-condition invalid-function (mal-runtime-exception)\n  ((form :initarg :form :reader form)\n   (context :initarg :context :reader context))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"Invalid function '~a' provided while ~a\"\n                     (printer:pr-str (form condition))\n                     (if (string= (context condition) \"apply\")\n                         \"applying\"\n                         \"defining macro\")))))\n\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n(defvar mal-quote (make-mal-symbol \"quote\"))\n(defvar mal-quasiquote (make-mal-symbol \"quasiquote\"))\n(defvar mal-unquote (make-mal-symbol \"unquote\"))\n(defvar mal-splice-unquote (make-mal-symbol \"splice-unquote\"))\n(defvar mal-vec (make-mal-symbol \"vec\"))\n(defvar mal-cons (make-mal-symbol \"cons\"))\n(defvar mal-concat (make-mal-symbol \"concat\"))\n(defvar mal-defmacro! (make-mal-symbol \"defmacro!\"))\n(defvar mal-try* (make-mal-symbol \"try*\"))\n(defvar mal-catch* (make-mal-symbol \"catch*\"))\n(defvar mal-throw (make-mal-symbol \"throw\"))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun qq-reducer (elt acc)\n  (make-mal-list\n    (if (and (mal-list-p elt)\n             (mal-data-value= (first (mal-data-value elt)) mal-splice-unquote))\n      (list mal-concat (second (mal-data-value elt)) acc)\n      (list mal-cons (quasiquote elt) acc))))\n(defun qq-iter (elts)\n  (reduce #'qq-reducer elts :from-end t :initial-value (make-mal-list ())))\n(defun quasiquote (ast)\n  (switch-mal-type ast\n    (types:list     (if (mal-data-value= (first (mal-data-value ast)) mal-unquote)\n                      (second (mal-data-value ast))\n                      (qq-iter (mal-data-value ast))))\n    (types:vector   (make-mal-list (list mal-vec (qq-iter (listify (mal-data-value ast))))))\n    (types:hash-map (make-mal-list (list mal-quote ast)))\n    (types:symbol   (make-mal-list (list mal-quote ast)))\n    (types:any      ast)))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (loop\n     do (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n          (when (and debug-eval\n                     (not (mal-data-value= debug-eval mal-false))\n                     (not (mal-data-value= debug-eval mal-false)))\n            (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n            (force-output *standard-output*)))\n     do (switch-mal-type ast\n          (types:symbol\n           (return\n             (let ((key (mal-data-value ast)))\n               (or (env:get-env env key)\n                   (error 'undefined-symbol :symbol (format nil \"~a\" key))))))\n          (types:vector\n           (return (make-mal-vector (map 'vector (lambda (x) (mal-eval x env))\n                                         (mal-data-value ast)))))\n          (types:hash-map (return (eval-hash-map ast env)))\n          (types:list\n            (let ((forms (mal-data-value ast)))\n               (cond\n                 ((null forms)\n                  (return ast))\n\n                 ((mal-data-value= mal-quote (first forms))\n                  (return (second forms)))\n\n                 ((mal-data-value= mal-quasiquote (first forms))\n                  (setf ast (quasiquote (second forms))))\n\n                 ((mal-data-value= mal-def! (first forms))\n                  (return (env:set-env env (second forms) (mal-eval (third forms) env))))\n\n                 ((mal-data-value= mal-defmacro! (first forms))\n                  (let ((value (mal-eval (third forms) env)))\n                    (return (if (mal-fn-p value)\n                                (env:set-env env\n                                             (second forms)\n                                             (progn\n                                               (setf (cdr (assoc :is-macro (mal-data-attrs value))) t)\n                                               value))\n                                (error 'invalid-function\n                                       :form value\n                                       :context \"macro\")))))\n\n                 ((mal-data-value= mal-let* (first forms))\n                  (let ((new-env (env:create-mal-env :parent env))\n                        (bindings (utils:listify (mal-data-value (second forms)))))\n\n                    (mapcar (lambda (binding)\n                              (env:set-env new-env\n                                           (car binding)\n                                           (mal-eval (or (cdr binding)\n                                                         mal-nil)\n                                                     new-env)))\n                            (loop\n                               for (symbol value) on bindings\n                               by #'cddr\n                               collect (cons symbol value)))\n                    (setf ast (third forms)\n                          env new-env)))\n\n                 ((mal-data-value= mal-do (first forms))\n                  (mapc (lambda (form) (mal-eval form env))\n                        (butlast (cdr forms)))\n                  (setf ast (car (last forms))))\n\n                 ((mal-data-value= mal-if (first forms))\n                  (let ((predicate (mal-eval (second forms) env)))\n                    (setf ast (if (or (mal-data-value= predicate mal-nil)\n                                      (mal-data-value= predicate mal-false))\n                                  (or (fourth forms) mal-nil)\n                                  (third forms)))))\n\n                 ((mal-data-value= mal-fn* (first forms))\n                  (return (let ((arglist (second forms))\n                                (body (third forms)))\n                            (make-mal-fn (lambda (&rest args)\n                                           (mal-eval body (env:create-mal-env :parent env\n                                                                              :binds (listify (mal-data-value arglist))\n                                                                              :exprs args)))\n                                         :attrs (list (cons :params arglist)\n                                                      (cons :ast body)\n                                                      (cons :env env)\n                                                      (cons :is-macro nil))))))\n\n                 ((mal-data-value= mal-try* (first forms))\n                  (if (not (third forms))\n                    (return (mal-eval (second forms) env))\n                    (handler-case\n                        (return (mal-eval (second forms) env))\n                      (error (condition)\n                        (let ((catch-forms (mal-data-value (third forms))))\n                          (when (mal-data-value= mal-catch*\n                                                 (first catch-forms))\n                            (return (mal-eval (third catch-forms)\n                                              (env:create-mal-env :parent env\n                                                                  :binds (list (second catch-forms))\n                                                                  :exprs (list (if (typep condition 'mal-user-exception)\n                                                                                   (mal-exception-data condition)\n                                                                                   (make-mal-string (format nil \"~a\" condition)))))))))))))\n\n                 (t (let ((function (mal-eval (car forms) env))\n                          (args     (cdr forms)))\n                      ;; If first element is a mal function unwrap it\n                      (cond ((mal-fn-p function)\n                             (let ((attrs (mal-data-attrs function)))\n                              (if (cdr (assoc :is-macro attrs))\n                               (setf ast (apply (mal-data-value function) args))\n                               (setf ast (cdr (assoc :ast attrs))\n                                     env (env:create-mal-env :parent (cdr (assoc :env attrs))\n                                                             :binds (map 'list\n                                                                         #'identity\n                                                                         (mal-data-value (cdr (assoc :params attrs))))\n                                                             :exprs (map 'list (lambda (x) (mal-eval x env)) args))))))\n                            ((mal-builtin-fn-p function)\n                             (return (apply (mal-data-value function)\n                                            (map 'list (lambda (x) (mal-eval x env)) args))))\n                            (t (error 'invalid-function\n                                      :form function\n                                      :context \"apply\"))))))))\n          (types:any (return ast)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (mal-error (condition)\n      (format nil \"Error: ~a\" condition))\n    (mal-runtime-exception (condition)\n      (format nil \"Exception: ~a\" condition))\n    (mal-user-exception (condition)\n      (format nil \"Exception: ~a\" (pr-str (mal-exception-data condition))))\n    (error (condition)\n      (format nil \"Internal error: ~a\" condition))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"eval\")\n             (make-mal-builtin-fn (lambda (ast)\n                                    (mal-eval ast *repl-env*))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun repl ()\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line\n                 (mal-writeline (rep line))\n                 (return)))))\n\n(defun run-file (file)\n  (rep (format nil \"(load-file \\\"~a\\\")\" file)))\n\n(defun main (&optional (argv nil argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (uiop:getenv \"PERL_RL\") \"false\")\n                                  (string= (uiop:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n\n  (let ((args (if argv-provided-p\n                  argv\n                  (cdr (utils:raw-command-line-arguments)))))\n    (env:set-env *repl-env*\n                 (make-mal-symbol \"*ARGV*\")\n                 (make-mal-list (mapcar #'make-mal-string (cdr args))))\n    (if (null args)\n        (repl)\n        (run-file (car args)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail.\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/stepA_mal.lisp",
    "content": "(defpackage :mal\n  (:use :common-lisp\n        :types\n        :env\n        :reader\n        :printer\n        :core)\n  (:import-from :cl-readline\n                :readline\n                :register-function)\n  (:import-from :genhash\n                :hashref\n                :hashmap)\n  (:import-from :utils\n                :listify\n                :getenv\n                :common-prefix)\n  (:export :main))\n\n(in-package :mal)\n\n(define-condition invalid-function (mal-runtime-exception)\n  ((form :initarg :form :reader form)\n   (context :initarg :context :reader context))\n  (:report (lambda (condition stream)\n             (format stream\n                     \"Invalid function '~a' provided while ~a\"\n                     (printer:pr-str (form condition))\n                     (if (string= (context condition) \"apply\")\n                         \"applying\"\n                         \"defining macro\")))))\n\n(defvar *repl-env* (env:create-mal-env))\n\n(dolist (binding core:ns)\n  (env:set-env *repl-env* (car binding) (cdr binding)))\n\n(defvar mal-def! (make-mal-symbol \"def!\"))\n(defvar mal-let* (make-mal-symbol \"let*\"))\n(defvar mal-do (make-mal-symbol \"do\"))\n(defvar mal-if (make-mal-symbol \"if\"))\n(defvar mal-fn* (make-mal-symbol \"fn*\"))\n(defvar mal-quote (make-mal-symbol \"quote\"))\n(defvar mal-quasiquote (make-mal-symbol \"quasiquote\"))\n(defvar mal-unquote (make-mal-symbol \"unquote\"))\n(defvar mal-splice-unquote (make-mal-symbol \"splice-unquote\"))\n(defvar mal-vec (make-mal-symbol \"vec\"))\n(defvar mal-cons (make-mal-symbol \"cons\"))\n(defvar mal-concat (make-mal-symbol \"concat\"))\n(defvar mal-defmacro! (make-mal-symbol \"defmacro!\"))\n(defvar mal-try* (make-mal-symbol \"try*\"))\n(defvar mal-catch* (make-mal-symbol \"catch*\"))\n(defvar mal-throw (make-mal-symbol \"throw\"))\n\n(defun eval-hash-map (hash-map env)\n  (let ((hash-map-value (mal-data-value hash-map))\n        (new-hash-table (make-mal-value-hash-table)))\n    (genhash:hashmap (lambda (key value)\n                       (setf (genhash:hashref key new-hash-table)\n                             (mal-eval value env)))\n                     hash-map-value)\n    (make-mal-hash-map new-hash-table)))\n\n(defun qq-reducer (elt acc)\n  (make-mal-list\n    (if (and (mal-list-p elt)\n             (mal-data-value= (first (mal-data-value elt)) mal-splice-unquote))\n      (list mal-concat (second (mal-data-value elt)) acc)\n      (list mal-cons (quasiquote elt) acc))))\n(defun qq-iter (elts)\n  (reduce #'qq-reducer elts :from-end t :initial-value (make-mal-list ())))\n(defun quasiquote (ast)\n  (switch-mal-type ast\n    (types:list     (if (mal-data-value= (first (mal-data-value ast)) mal-unquote)\n                      (second (mal-data-value ast))\n                      (qq-iter (mal-data-value ast))))\n    (types:vector   (make-mal-list (list mal-vec (qq-iter (listify (mal-data-value ast))))))\n    (types:hash-map (make-mal-list (list mal-quote ast)))\n    (types:symbol   (make-mal-list (list mal-quote ast)))\n    (types:any      ast)))\n\n(defun mal-read (string)\n  (reader:read-str string))\n\n(defun mal-eval (ast env)\n  (loop\n     do (let ((debug-eval (env:get-env env \"DEBUG-EVAL\")))\n          (when (and debug-eval\n                     (not (mal-data-value= debug-eval mal-false))\n                     (not (mal-data-value= debug-eval mal-false)))\n            (write-line (format nil \"EVAL: ~a\" (pr-str ast)))\n            (force-output *standard-output*)))\n     do (switch-mal-type ast\n          (types:symbol\n           (return\n             (let ((key (mal-data-value ast)))\n               (or (env:get-env env key)\n                   (error 'undefined-symbol :symbol (format nil \"~a\" key))))))\n          (types:vector\n           (return (make-mal-vector (map 'vector (lambda (x) (mal-eval x env))\n                                         (mal-data-value ast)))))\n          (types:hash-map (return (eval-hash-map ast env)))\n          (types:list\n            (let ((forms (mal-data-value ast)))\n               (cond\n                 ((null forms)\n                  (return ast))\n\n                 ((mal-data-value= mal-quote (first forms))\n                  (return (second forms)))\n\n                 ((mal-data-value= mal-quasiquote (first forms))\n                  (setf ast (quasiquote (second forms))))\n\n                 ((mal-data-value= mal-def! (first forms))\n                  (return (env:set-env env (second forms) (mal-eval (third forms) env))))\n\n                 ((mal-data-value= mal-defmacro! (first forms))\n                  (let ((value (mal-eval (third forms) env)))\n                    (return (if (mal-fn-p value)\n                                (env:set-env env\n                                             (second forms)\n                                             (progn\n                                               (setf (cdr (assoc :is-macro (mal-data-attrs value))) t)\n                                               value))\n                                (error 'invalid-function\n                                       :form value\n                                       :context \"macro\")))))\n\n                 ((mal-data-value= mal-let* (first forms))\n                  (let ((new-env (env:create-mal-env :parent env))\n                        (bindings (utils:listify (mal-data-value (second forms)))))\n\n                    (mapcar (lambda (binding)\n                              (env:set-env new-env\n                                           (car binding)\n                                           (mal-eval (or (cdr binding)\n                                                         mal-nil)\n                                                     new-env)))\n                            (loop\n                               for (symbol value) on bindings\n                               by #'cddr\n                               collect (cons symbol value)))\n                    (setf ast (third forms)\n                          env new-env)))\n\n                 ((mal-data-value= mal-do (first forms))\n                  (mapc (lambda (form) (mal-eval form env))\n                        (butlast (cdr forms)))\n                  (setf ast (car (last forms))))\n\n                 ((mal-data-value= mal-if (first forms))\n                  (let ((predicate (mal-eval (second forms) env)))\n                    (setf ast (if (or (mal-data-value= predicate mal-nil)\n                                      (mal-data-value= predicate mal-false))\n                                  (or (fourth forms) mal-nil)\n                                  (third forms)))))\n\n                 ((mal-data-value= mal-fn* (first forms))\n                  (return (let ((arglist (second forms))\n                                (body (third forms)))\n                            (make-mal-fn (lambda (&rest args)\n                                           (mal-eval body (env:create-mal-env :parent env\n                                                                              :binds (listify (mal-data-value arglist))\n                                                                              :exprs args)))\n                                         :attrs (list (cons :params arglist)\n                                                      (cons :ast body)\n                                                      (cons :env env)\n                                                      (cons :is-macro nil))))))\n\n                 ((mal-data-value= mal-try* (first forms))\n                  (if (not (third forms))\n                    (return (mal-eval (second forms) env))\n                    (handler-case\n                        (return (mal-eval (second forms) env))\n                      (error (condition)\n                        (let ((catch-forms (mal-data-value (third forms))))\n                          (when (mal-data-value= mal-catch*\n                                                 (first catch-forms))\n                            (return (mal-eval (third catch-forms)\n                                              (env:create-mal-env :parent env\n                                                                  :binds (list (second catch-forms))\n                                                                  :exprs (list (if (typep condition 'mal-user-exception)\n                                                                                   (mal-exception-data condition)\n                                                                                 (make-mal-string (format nil \"~a\" condition)))))))))))))\n\n                 (t (let ((function (mal-eval (car forms) env))\n                          (args     (cdr forms)))\n                      ;; If first element is a mal function unwrap it\n                      (cond ((mal-fn-p function)\n                             (let ((attrs (mal-data-attrs function)))\n                              (if (cdr (assoc :is-macro attrs))\n                               (setf ast (apply (mal-data-value function) args))\n                               (setf ast (cdr (assoc :ast attrs))\n                                     env (env:create-mal-env :parent (cdr (assoc :env attrs))\n                                                             :binds (map 'list\n                                                                         #'identity\n                                                                         (mal-data-value (cdr (assoc :params attrs))))\n                                                             :exprs (map 'list (lambda (x) (mal-eval x env)) args))))))\n                            ((mal-builtin-fn-p function)\n                             (return (apply (mal-data-value function)\n                                            (map 'list (lambda (x) (mal-eval x env)) args))))\n                            (t (error 'invalid-function\n                                      :form function\n                                      :context \"apply\"))))))))\n          (types:any (return ast)))))\n\n(defun mal-print (expression)\n  (printer:pr-str expression))\n\n(defun rep (string)\n  (handler-case\n      (mal-print (mal-eval (mal-read string) *repl-env*))\n    (mal-error (condition)\n      (format nil \"Error: ~a\" condition))\n    (mal-runtime-exception (condition)\n      (format nil \"Exception: ~a\" condition))\n    (mal-user-exception (condition)\n      (format nil \"Exception: ~a\" (pr-str (mal-exception-data condition))))\n    (error (condition)\n      (format nil \"Internal error: ~a\" condition))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"eval\")\n             (make-mal-builtin-fn (lambda (ast)\n                                    (mal-eval ast *repl-env*))))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"*cl-implementation*\")\n             (make-mal-string (lisp-implementation-type)))\n\n(env:set-env *repl-env*\n             (make-mal-symbol \"*cl-version*\")\n             (make-mal-string (lisp-implementation-version)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n(rep \"(def! *host-language* \\\"common-lisp\\\")\")\n\n(defvar *use-readline-p* nil)\n\n(defun complete-toplevel-symbols (input &rest ignored)\n  (declare (ignorable ignored))\n\n  (let (candidates)\n    (loop for key being the hash-keys of (env:mal-env-bindings *repl-env*)\n       when (let ((pos (search input key))) (and pos (zerop pos)))\n       do (push key candidates))\n\n    (if (= 1 (length candidates))\n        (cons (car candidates) candidates)\n        (cons (apply #'utils:common-prefix candidates) candidates))))\n\n(defun raw-input (prompt)\n  (format *standard-output* prompt)\n  (force-output *standard-output*)\n  (read-line *standard-input* nil))\n\n(defun mal-readline (prompt)\n  (if *use-readline-p*\n      (rl:readline :prompt prompt :add-history t :novelty-check #'string/=)\n      (raw-input prompt)))\n\n(defun mal-writeline (string)\n  (when string\n    (write-line string)\n    (force-output *standard-output*)))\n\n(defun repl ()\n  (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n  (loop do (let ((line (mal-readline \"user> \")))\n             (if line\n                 (mal-writeline (rep line))\n                 (return)))))\n\n(defun run-file (file)\n  (rep (format nil \"(load-file \\\"~a\\\")\" file)))\n\n(defun main (&optional (argv nil argv-provided-p))\n\n  (setf *use-readline-p* (not (or (string= (utils:getenv \"PERL_RL\") \"false\")\n                                  (string= (utils:getenv \"TERM\") \"dumb\"))))\n\n  ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort\n  ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment\n  ;; variable which the test runner sets causing `read-line' on *standard-input*\n  ;; to fail with an empty stream error. The following reinitializes the\n  ;; standard streams\n  ;;\n  ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html\n  #+clisp (setf *standard-input* (ext:make-stream :input)\n                *standard-output* (ext:make-stream :output :buffered t)\n                *error-output* (ext:make-stream :error :buffered t))\n\n  ;; CCL fails with a error while registering completion function\n  ;; See also https://github.com/mrkkrp/cl-readline/issues/5\n  #-ccl (rl:register-function :complete #'complete-toplevel-symbols)\n\n  (let ((args (if argv-provided-p\n                  argv\n                  (cdr (utils:raw-command-line-arguments)))))\n    (env:set-env *repl-env*\n                 (make-mal-symbol \"*ARGV*\")\n                 (make-mal-list (mapcar #'make-mal-string (cdr args))))\n    (if (null args)\n        (repl)\n        (run-file (car args)))))\n\n;;; Workaround for CMUCL's printing of \"Reloaded library ... \" messages when an\n;;; image containing foreign libraries is restored. The extra messages cause the\n;;; MAL testcases to fail\n\n#+cmucl (progn\n          (defvar *old-standard-output* *standard-output*\n            \"Keep track of current value standard output, this is restored after image restore completes\")\n\n          (defun muffle-output ()\n            (setf *standard-output* (make-broadcast-stream)))\n\n          (defun restore-output ()\n            (setf *standard-output* *old-standard-output*))\n\n          (pushnew #'muffle-output ext:*after-save-initializations*)\n          (setf ext:*after-save-initializations*\n                (append ext:*after-save-initializations* (list #'restore-output))))\n"
  },
  {
    "path": "impls/common-lisp/src/types.lisp",
    "content": "(defpackage :types\n  (:use :common-lisp\n        :genhash)\n  (:import-from :utils\n                :listify)\n  (:export :mal-data-value=\n           ;; Accessors\n           :mal-data-value\n           :mal-data-type\n           :mal-data-meta\n           :mal-data-attrs\n           ;; Mal values\n           :number\n           :make-mal-number\n           :mal-number-p\n\n           :boolean\n           :mal-boolean-p\n\n           :nil\n           :mal-nil-p\n\n           :string\n           :make-mal-string\n           :mal-string-p\n\n           :symbol\n           :make-mal-symbol\n           :mal-symbol-p\n\n           :keyword\n           :make-mal-keyword\n           :mal-keyword-p\n\n           :list\n           :make-mal-list\n           :mal-list-p\n\n           :vector\n           :make-mal-vector\n           :mal-vector-p\n\n           :hash-map\n           :make-mal-hash-map\n           :mal-hash-map-p\n\n           :atom\n           :make-mal-atom\n           :mal-atom-p\n\n           :builtin-fn\n           :make-mal-builtin-fn\n           :mal-builtin-fn-p\n\n           :fn\n           :make-mal-fn\n           :mal-fn-p\n\n           :any\n           :switch-mal-type\n\n           ;; Singleton values\n           :mal-nil\n           :mal-true\n           :mal-false\n\n           ;; Hashing mal values\n           :make-mal-value-hash-table\n           ;; Error types\n           :mal-exception\n           :mal-exception-data\n           ;; Exceptions raised by the runtime\n           :mal-runtime-exception\n           ;; Exception raised by user code\n           :mal-user-exception\n           ;; Error\n           :mal-error))\n\n(in-package :types)\n\n(define-condition mal-error (error) nil)\n\n(define-condition mal-exception (error) nil)\n\n(define-condition mal-runtime-exception (mal-exception) nil)\n\n(define-condition mal-user-exception (mal-exception)\n  ((data :accessor mal-exception-data :initarg :data)))\n\n(defstruct mal-data\n  (value nil)\n  (type nil :read-only t)\n  meta\n  attrs)\n\n;; Create a constructor and predicate for given type\n(defmacro define-mal-type (type)\n  (let ((constructor (intern (format nil \"MAKE-MAL-~a\" (symbol-name type))))\n        (predicate (intern (format nil \"MAL-~a-P\" (symbol-name type)))))\n    `(progn (defun ,constructor (value &key meta attrs)\n              (make-mal-data :type ',type\n                             :value value\n                             :meta meta\n                             :attrs attrs))\n\n            (defun ,predicate (value)\n              (when (typep value 'mal-data)\n                (eq (mal-data-type value) ',type))))))\n\n(define-mal-type number)\n(define-mal-type symbol)\n(define-mal-type keyword)\n(define-mal-type string)\n(define-mal-type boolean)\n(define-mal-type nil)\n\n(define-mal-type list)\n(define-mal-type vector)\n(define-mal-type hash-map)\n\n(define-mal-type atom)\n\n(define-mal-type fn)\n(define-mal-type builtin-fn)\n\n(defvar mal-nil (make-mal-nil nil))\n(defvar mal-true (make-mal-boolean t))\n(defvar mal-false (make-mal-boolean nil))\n\n;; Generic type\n(defvar any)\n\n(defmacro switch-mal-type (ast &body forms)\n  `(let ((type (mal-data-type ,ast)))\n     (cond\n       ,@(mapcar (lambda (form)\n                   (list (or (equal (car form) t)\n                             (equal (car form) 'any)\n                             (list 'equal (list 'quote (car form)) 'type))\n                         (cadr form)))\n                 forms))))\n\n(defun mal-sequence= (value1 value2)\n  (let ((sequence1 (listify (mal-data-value value1)))\n        (sequence2 (listify (mal-data-value value2))))\n\n    (when (= (length sequence1) (length sequence2))\n      (every #'identity (loop for x in sequence1\n                           for y in sequence2\n                           collect (mal-data-value= x y))))))\n\n(defun mal-hash-map= (value1 value2)\n  (let ((map1 (mal-data-value value1))\n        (map2 (mal-data-value value2))\n        (identical t))\n    (when (= (generic-hash-table-count map1)\n             (generic-hash-table-count map2))\n      (hashmap (lambda (key value)\n                 (declare (ignorable value))\n                 (setf identical\n                       (and identical (mal-data-value= (hashref key map1)\n                                                       (hashref key map2)))))\n               map1)\n      identical)))\n\n(defun mal-data-value= (value1 value2)\n  (when (and (typep value1 'mal-data)\n             (typep value2 'mal-data))\n\n    (if (equal (mal-data-type value1) (mal-data-type value2))\n        (switch-mal-type value1\n          (list (mal-sequence= value1 value2))\n          (vector (mal-sequence= value1 value2))\n          (hash-map (mal-hash-map= value1 value2))\n          (any (equal (mal-data-value value1) (mal-data-value value2))))\n        (when (or (and (mal-list-p value1) (mal-vector-p value2))\n                  (and (mal-list-p value2) (mal-vector-p value1)))\n          (mal-sequence= value1 value2)))))\n\n(defun mal-sxhash (value)\n  (sxhash (mal-data-value value)))\n\n(defun make-mal-value-hash-table ()\n  (unless (gethash 'mal-data-value-hash genhash::*hash-test-designator-map*)\n    ;; sxhash does not work well with compound types, use a custom\n    ;; hash function which hashes the underlying value instead\n    (let ((hash-function #'mal-sxhash))\n      (register-test-designator 'mal-data-value-hash\n                                hash-function\n                                #'mal-data-value=)))\n  (make-generic-hash-table :test 'mal-data-value-hash))\n"
  },
  {
    "path": "impls/common-lisp/src/utils.lisp",
    "content": "(defpackage :utils\n  (:use :common-lisp\n        :uiop)\n  (:export :replace-all\n           :getenv\n           :read-file-string\n           :raw-command-line-arguments\n           :listify\n           :common-prefix))\n\n(in-package :utils)\n\n(defun replace-all (string part replacement &key (test #'char=))\n  \"Returns a new string in which all the occurences of the part\nis replaced with replacement.\"\n  (with-output-to-string (out)\n    (loop with part-length = (length part)\n       for old-pos = 0 then (+ pos part-length)\n       for pos = (search part string\n                         :start2 old-pos\n                         :test test)\n       do (write-string string out\n                        :start old-pos\n                        :end (or pos (length string)))\n       when pos do (write-string replacement out)\n       while pos)))\n\n(defun listify (sequence)\n  \"Convert a sequence to a list\"\n  (map 'list #'identity sequence))\n\n(defun common-prefix (&rest strings)\n  (if (not strings)\n      \"\"\n      (let* ((char-lists (mapcar (lambda (string) (coerce string 'list)) strings))\n             (char-tuples (apply #'mapcar #'list char-lists))\n             (count 0))\n        (loop for char-tuple in char-tuples\n           while (every (lambda (char) (equal char (car char-tuple))) char-tuple)\n           do (incf count))\n\n        (subseq (car strings) 0 count))))\n"
  },
  {
    "path": "impls/common-lisp/step0_repl.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step0_repl\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 0 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"step0_repl\"))\n  :depends-on (:uiop :cl-readline)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step1_read_print.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step1_read_print\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 1 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"step1_read_print\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step2_eval.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step2_eval\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 2 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"step2_eval\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step3_env.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step3_env\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 3 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"step3_env\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step4_if_fn_do.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step4_if_fn_do\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 4 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"step4_if_fn_do\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step5_tco.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step5_tco\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 5 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"step5_tco\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step6_file.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step6_file\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 6 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"step6_file\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step7_quote.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step7_quote\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 7 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"step7_quote\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step8_macros.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step8_macros\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 8 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"step8_macros\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/step9_try.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"step9_try\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of step 9 of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"step9_try\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/stepA_mal.asd",
    "content": "#-quicklisp\n(let ((quicklisp-init (merge-pathnames \"quicklisp/setup.lisp\"\n                                       (user-homedir-pathname))))\n  (when (probe-file quicklisp-init)\n    (load quicklisp-init)))\n\n(ql:quickload :uiop :silent t :verbose nil)\n(ql:quickload :cl-ppcre :silent t)\n(ql:quickload :genhash :silent t)\n(ql:quickload :alexandria :silent t)\n\n#-mkcl (ql:quickload :cl-readline :silent t)\n#+mkcl (load \"fake-readline.lisp\")\n\n(defpackage #:mal-asd\n  (:use :cl :asdf))\n\n(in-package :mal-asd)\n\n(defsystem \"stepA_mal\"\n  :name \"MAL\"\n  :version \"1.0\"\n  :author \"Iqbal Ansari\"\n  :description \"Implementation of MAL in Common Lisp\"\n  :serial t\n  :components ((:file \"utils\")\n               (:file \"types\")\n               (:file \"env\")\n               (:file \"reader\")\n               (:file \"printer\")\n               (:file \"core\")\n               (:file \"stepA_mal\"))\n  :depends-on (:uiop :cl-readline :cl-ppcre :genhash)\n  :pathname \"src/\")\n"
  },
  {
    "path": "impls/common-lisp/tests/stepA_mal.mal",
    "content": ";; Testing clisp interop\n\n(cl-eval \"42\")\n;=>42\n\n(cl-eval \"(+ 1 1)\")\n;=>2\n\n(cl-eval \"(setq foo 1 bar 2 baz 3)\")\n\n(cl-eval \"(list foo bar baz)\")\n;=>(1 2 3)\n\n(cl-eval \"7\")\n;=>7\n\n;;\n;; Testing boolean flag\n(cl-eval \"(= 123 123)\" true)\n;=>true\n\n(cl-eval \"(= 123 456)\")\n;=>nil\n\n(cl-eval \"(= 123 456)\" true)\n;=>false\n\n;;\n;; Testing list flag\n(cl-eval \"(last nil)\" false true)\n;=>()\n\n(cl-eval \"nil\" false true)\n;=>()\n\n(cl-eval \"nil\")\n;=>nil\n\n;;\n;; Testing creation of Common Lisp Objects\n(cl-eval \"#(1 2)\")\n;=>[1 2]\n\n;;; Not testing with elements since order in hashtable cannot be guaranteed\n(cl-eval \"(make-hash-table)\")\n;=>{}\n\n(cl-eval \"(defun redundant-identity (x) x)\"))\n;=>REDUNDANT-IDENTITY\n\n(cl-eval \"(redundant-identity 2)\"))\n;=>2\n\n(cl-eval \"(defun range (max &key (min 0) (step 1)) (loop for n from min below max by step collect n))\")\n;=>RANGE\n\n(cl-eval \"(range 10 :min 0 :step 1)\")\n;=>(0 1 2 3 4 5 6 7 8 9)\n\n(cl-eval \"(mapcar #'1+ (range 10 :min 0 :step 1))\")\n;=>(1 2 3 4 5 6 7 8 9 10)\n"
  },
  {
    "path": "impls/cpp/.gitignore",
    "content": ".deps\n*.o\n*.a\nstep0_repl\nstep1_read_print\n"
  },
  {
    "path": "impls/cpp/Core.cpp",
    "content": "#include \"MAL.h\"\n#include \"Environment.h\"\n#include \"StaticList.h\"\n#include \"Types.h\"\n\n#include <chrono>\n#include <fstream>\n#include <iostream>\n\n#define CHECK_ARGS_IS(expected) \\\n    checkArgsIs(name.c_str(), expected, \\\n                  std::distance(argsBegin, argsEnd))\n\n#define CHECK_ARGS_BETWEEN(min, max) \\\n    checkArgsBetween(name.c_str(), min, max, \\\n                       std::distance(argsBegin, argsEnd))\n\n#define CHECK_ARGS_AT_LEAST(expected) \\\n    checkArgsAtLeast(name.c_str(), expected, \\\n                        std::distance(argsBegin, argsEnd))\n\nstatic String printValues(malValueIter begin, malValueIter end,\n                           const String& sep, bool readably);\n\nstatic StaticList<malBuiltIn*> handlers;\n\n#define ARG(type, name) type* name = VALUE_CAST(type, *argsBegin++)\n\n#define FUNCNAME(uniq) builtIn ## uniq\n#define HRECNAME(uniq) handler ## uniq\n#define BUILTIN_DEF(uniq, symbol) \\\n    static malBuiltIn::ApplyFunc FUNCNAME(uniq); \\\n    static StaticList<malBuiltIn*>::Node HRECNAME(uniq) \\\n        (handlers, new malBuiltIn(symbol, FUNCNAME(uniq))); \\\n    malValuePtr FUNCNAME(uniq)(const String& name, \\\n        malValueIter argsBegin, malValueIter argsEnd)\n\n#define BUILTIN(symbol)  BUILTIN_DEF(__LINE__, symbol)\n\n#define BUILTIN_ISA(symbol, type) \\\n    BUILTIN(symbol) { \\\n        CHECK_ARGS_IS(1); \\\n        return mal::boolean(DYNAMIC_CAST(type, *argsBegin)); \\\n    }\n\n#define BUILTIN_IS(op, constant) \\\n    BUILTIN(op) { \\\n        CHECK_ARGS_IS(1); \\\n        return mal::boolean(*argsBegin == mal::constant()); \\\n    }\n\n#define BUILTIN_INTOP(op, checkDivByZero) \\\n    BUILTIN(#op) { \\\n        CHECK_ARGS_IS(2); \\\n        ARG(malInteger, lhs); \\\n        ARG(malInteger, rhs); \\\n        if (checkDivByZero) { \\\n            MAL_CHECK(rhs->value() != 0, \"Division by zero\"); \\\n        } \\\n        return mal::integer(lhs->value() op rhs->value()); \\\n    }\n\nBUILTIN_ISA(\"atom?\",        malAtom);\nBUILTIN_ISA(\"keyword?\",     malKeyword);\nBUILTIN_ISA(\"list?\",        malList);\nBUILTIN_ISA(\"map?\",         malHash);\nBUILTIN_ISA(\"number?\",      malInteger);\nBUILTIN_ISA(\"sequential?\",  malSequence);\nBUILTIN_ISA(\"string?\",      malString);\nBUILTIN_ISA(\"symbol?\",      malSymbol);\nBUILTIN_ISA(\"vector?\",      malVector);\n\nBUILTIN_INTOP(+,            false);\nBUILTIN_INTOP(/,            true);\nBUILTIN_INTOP(*,            false);\nBUILTIN_INTOP(%,            true);\n\nBUILTIN_IS(\"true?\",         trueValue);\nBUILTIN_IS(\"false?\",        falseValue);\nBUILTIN_IS(\"nil?\",          nilValue);\n\nBUILTIN(\"-\")\n{\n    int argCount = CHECK_ARGS_BETWEEN(1, 2);\n    ARG(malInteger, lhs);\n    if (argCount == 1) {\n        return mal::integer(- lhs->value());\n    }\n\n    ARG(malInteger, rhs);\n    return mal::integer(lhs->value() - rhs->value());\n}\n\nBUILTIN(\"<=\")\n{\n    CHECK_ARGS_IS(2);\n    ARG(malInteger, lhs);\n    ARG(malInteger, rhs);\n\n    return mal::boolean(lhs->value() <= rhs->value());\n}\n\nBUILTIN(\">=\")\n{\n    CHECK_ARGS_IS(2);\n    ARG(malInteger, lhs);\n    ARG(malInteger, rhs);\n\n    return mal::boolean(lhs->value() >= rhs->value());\n}\n\nBUILTIN(\"<\")\n{\n    CHECK_ARGS_IS(2);\n    ARG(malInteger, lhs);\n    ARG(malInteger, rhs);\n\n    return mal::boolean(lhs->value() < rhs->value());\n}\n\nBUILTIN(\">\")\n{\n    CHECK_ARGS_IS(2);\n    ARG(malInteger, lhs);\n    ARG(malInteger, rhs);\n\n    return mal::boolean(lhs->value() > rhs->value());\n}\n\nBUILTIN(\"=\")\n{\n    CHECK_ARGS_IS(2);\n    const malValue* lhs = (*argsBegin++).ptr();\n    const malValue* rhs = (*argsBegin++).ptr();\n\n    return mal::boolean(lhs->isEqualTo(rhs));\n}\n\nBUILTIN(\"apply\")\n{\n    CHECK_ARGS_AT_LEAST(2);\n    malValuePtr op = *argsBegin++; // this gets checked in APPLY\n\n    // Copy the first N-1 arguments in.\n    malValueVec args(argsBegin, argsEnd-1);\n\n    // Then append the argument as a list.\n    const malSequence* lastArg = VALUE_CAST(malSequence, *(argsEnd-1));\n    for (int i = 0; i < lastArg->count(); i++) {\n        args.push_back(lastArg->item(i));\n    }\n\n    return APPLY(op, args.begin(), args.end());\n}\n\nBUILTIN(\"assoc\")\n{\n    CHECK_ARGS_AT_LEAST(1);\n    ARG(malHash, hash);\n\n    return hash->assoc(argsBegin, argsEnd);\n}\n\nBUILTIN(\"atom\")\n{\n    CHECK_ARGS_IS(1);\n\n    return mal::atom(*argsBegin);\n}\n\nBUILTIN(\"concat\")\n{\n    int count = 0;\n    for (auto it = argsBegin; it != argsEnd; ++it) {\n        const malSequence* seq = VALUE_CAST(malSequence, *it);\n        count += seq->count();\n    }\n\n    malValueVec* items = new malValueVec(count);\n    int offset = 0;\n    for (auto it = argsBegin; it != argsEnd; ++it) {\n        const malSequence* seq = STATIC_CAST(malSequence, *it);\n        std::copy(seq->begin(), seq->end(), items->begin() + offset);\n        offset += seq->count();\n    }\n\n    return mal::list(items);\n}\n\nBUILTIN(\"conj\")\n{\n    CHECK_ARGS_AT_LEAST(1);\n    ARG(malSequence, seq);\n\n    return seq->conj(argsBegin, argsEnd);\n}\n\nBUILTIN(\"cons\")\n{\n    CHECK_ARGS_IS(2);\n    malValuePtr first = *argsBegin++;\n    ARG(malSequence, rest);\n\n    malValueVec* items = new malValueVec(1 + rest->count());\n    items->at(0) = first;\n    std::copy(rest->begin(), rest->end(), items->begin() + 1);\n\n    return mal::list(items);\n}\n\nBUILTIN(\"contains?\")\n{\n    CHECK_ARGS_IS(2);\n    if (*argsBegin == mal::nilValue()) {\n        return *argsBegin;\n    }\n    ARG(malHash, hash);\n    return mal::boolean(hash->contains(*argsBegin));\n}\n\nBUILTIN(\"count\")\n{\n    CHECK_ARGS_IS(1);\n    if (*argsBegin == mal::nilValue()) {\n        return mal::integer(0);\n    }\n\n    ARG(malSequence, seq);\n    return mal::integer(seq->count());\n}\n\nBUILTIN(\"deref\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malAtom, atom);\n\n    return atom->deref();\n}\n\nBUILTIN(\"dissoc\")\n{\n    CHECK_ARGS_AT_LEAST(1);\n    ARG(malHash, hash);\n\n    return hash->dissoc(argsBegin, argsEnd);\n}\n\nBUILTIN(\"empty?\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malSequence, seq);\n\n    return mal::boolean(seq->isEmpty());\n}\n\nBUILTIN(\"eval\")\n{\n    CHECK_ARGS_IS(1);\n    return EVAL(*argsBegin, NULL);\n}\n\nBUILTIN(\"first\")\n{\n    CHECK_ARGS_IS(1);\n    if (*argsBegin == mal::nilValue()) {\n        return mal::nilValue();\n    }\n    ARG(malSequence, seq);\n    return seq->first();\n}\n\nBUILTIN(\"fn?\")\n{\n    CHECK_ARGS_IS(1);\n    malValuePtr arg = *argsBegin++;\n\n    // Lambdas are functions, unless they're macros.\n    if (const malLambda* lambda = DYNAMIC_CAST(malLambda, arg)) {\n        return mal::boolean(!lambda->isMacro());\n    }\n    // Builtins are functions.\n    return mal::boolean(DYNAMIC_CAST(malBuiltIn, arg));\n}\n\nBUILTIN(\"get\")\n{\n    CHECK_ARGS_IS(2);\n    if (*argsBegin == mal::nilValue()) {\n        return *argsBegin;\n    }\n    ARG(malHash, hash);\n    return hash->get(*argsBegin);\n}\n\nBUILTIN(\"hash-map\")\n{\n    return mal::hash(argsBegin, argsEnd, true);\n}\n\nBUILTIN(\"keys\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malHash, hash);\n    return hash->keys();\n}\n\nBUILTIN(\"keyword\")\n{\n    CHECK_ARGS_IS(1);\n    const malValuePtr arg = *argsBegin++;\n    if (malKeyword* s = DYNAMIC_CAST(malKeyword, arg))\n      return s;\n    if (const malString* s = DYNAMIC_CAST(malString, arg))\n      return mal::keyword(\":\" + s->value());\n    MAL_FAIL(\"keyword expects a keyword or string\");\n}\n\nBUILTIN(\"list\")\n{\n    return mal::list(argsBegin, argsEnd);\n}\n\nBUILTIN(\"macro?\")\n{\n    CHECK_ARGS_IS(1);\n\n    // Macros are implemented as lambdas, with a special flag.\n    const malLambda* lambda = DYNAMIC_CAST(malLambda, *argsBegin);\n    return mal::boolean((lambda != NULL) && lambda->isMacro());\n}\n\nBUILTIN(\"map\")\n{\n    CHECK_ARGS_IS(2);\n    malValuePtr op = *argsBegin++; // this gets checked in APPLY\n    ARG(malSequence, source);\n\n    const int length = source->count();\n    malValueVec* items = new malValueVec(length);\n    auto it = source->begin();\n    for (int i = 0; i < length; i++) {\n      items->at(i) = APPLY(op, it+i, it+i+1);\n    }\n\n    return  mal::list(items);\n}\n\nBUILTIN(\"meta\")\n{\n    CHECK_ARGS_IS(1);\n    malValuePtr obj = *argsBegin++;\n\n    return obj->meta();\n}\n\nBUILTIN(\"nth\")\n{\n    CHECK_ARGS_IS(2);\n    ARG(malSequence, seq);\n    ARG(malInteger,  index);\n\n    int i = index->value();\n    MAL_CHECK(i >= 0 && i < seq->count(), \"Index out of range\");\n\n    return seq->item(i);\n}\n\nBUILTIN(\"pr-str\")\n{\n    return mal::string(printValues(argsBegin, argsEnd, \" \", true));\n}\n\nBUILTIN(\"println\")\n{\n    std::cout << printValues(argsBegin, argsEnd, \" \", false) << \"\\n\";\n    return mal::nilValue();\n}\n\nBUILTIN(\"prn\")\n{\n    std::cout << printValues(argsBegin, argsEnd, \" \", true) << \"\\n\";\n    return mal::nilValue();\n}\n\nBUILTIN(\"read-string\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malString, str);\n\n    return readStr(str->value());\n}\n\nBUILTIN(\"readline\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malString, str);\n\n    return readline(str->value());\n}\n\nBUILTIN(\"reset!\")\n{\n    CHECK_ARGS_IS(2);\n    ARG(malAtom, atom);\n    return atom->reset(*argsBegin);\n}\n\nBUILTIN(\"rest\")\n{\n    CHECK_ARGS_IS(1);\n    if (*argsBegin == mal::nilValue()) {\n        return mal::list(new malValueVec(0));\n    }\n    ARG(malSequence, seq);\n    return seq->rest();\n}\n\nBUILTIN(\"seq\")\n{\n    CHECK_ARGS_IS(1);\n    malValuePtr arg = *argsBegin++;\n    if (arg == mal::nilValue()) {\n        return mal::nilValue();\n    }\n    if (const malSequence* seq = DYNAMIC_CAST(malSequence, arg)) {\n        return seq->isEmpty() ? mal::nilValue()\n                              : mal::list(seq->begin(), seq->end());\n    }\n    if (const malString* strVal = DYNAMIC_CAST(malString, arg)) {\n        const String str = strVal->value();\n        int length = str.length();\n        if (length == 0)\n            return mal::nilValue();\n\n        malValueVec* items = new malValueVec(length);\n        for (int i = 0; i < length; i++) {\n            (*items)[i] = mal::string(str.substr(i, 1));\n        }\n        return mal::list(items);\n    }\n    MAL_FAIL(\"%s is not a string or sequence\", arg->print(true).c_str());\n}\n\n\nBUILTIN(\"slurp\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malString, filename);\n\n    std::ios_base::openmode openmode =\n        std::ios::ate | std::ios::in | std::ios::binary;\n    std::ifstream file(filename->value().c_str(), openmode);\n    MAL_CHECK(!file.fail(), \"Cannot open %s\", filename->value().c_str());\n\n    String data;\n    data.reserve(file.tellg());\n    file.seekg(0, std::ios::beg);\n    data.append(std::istreambuf_iterator<char>(file.rdbuf()),\n                std::istreambuf_iterator<char>());\n\n    return mal::string(data);\n}\n\nBUILTIN(\"str\")\n{\n    return mal::string(printValues(argsBegin, argsEnd, \"\", false));\n}\n\nBUILTIN(\"swap!\")\n{\n    CHECK_ARGS_AT_LEAST(2);\n    ARG(malAtom, atom);\n\n    malValuePtr op = *argsBegin++; // this gets checked in APPLY\n\n    malValueVec args(1 + argsEnd - argsBegin);\n    args[0] = atom->deref();\n    std::copy(argsBegin, argsEnd, args.begin() + 1);\n\n    malValuePtr value = APPLY(op, args.begin(), args.end());\n    return atom->reset(value);\n}\n\nBUILTIN(\"symbol\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malString, token);\n    return mal::symbol(token->value());\n}\n\nBUILTIN(\"throw\")\n{\n    CHECK_ARGS_IS(1);\n    throw *argsBegin;\n}\n\nBUILTIN(\"time-ms\")\n{\n    CHECK_ARGS_IS(0);\n\n    using namespace std::chrono;\n    milliseconds ms = duration_cast<milliseconds>(\n        high_resolution_clock::now().time_since_epoch()\n    );\n\n    return mal::integer(ms.count());\n}\n\nBUILTIN(\"vals\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malHash, hash);\n    return hash->values();\n}\n\nBUILTIN(\"vec\")\n{\n    CHECK_ARGS_IS(1);\n    ARG(malSequence, s);\n    return mal::vector(s->begin(), s->end());\n}\n\nBUILTIN(\"vector\")\n{\n    return mal::vector(argsBegin, argsEnd);\n}\n\nBUILTIN(\"with-meta\")\n{\n    CHECK_ARGS_IS(2);\n    malValuePtr obj  = *argsBegin++;\n    malValuePtr meta = *argsBegin++;\n    return obj->withMeta(meta);\n}\n\nvoid installCore(malEnvPtr env) {\n    for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) {\n        malBuiltIn* handler = *it;\n        env->set(handler->name(), handler);\n    }\n}\n\nstatic String printValues(malValueIter begin, malValueIter end,\n                          const String& sep, bool readably)\n{\n    String out;\n\n    if (begin != end) {\n        out += (*begin)->print(readably);\n        ++begin;\n    }\n\n    for ( ; begin != end; ++begin) {\n        out += sep;\n        out += (*begin)->print(readably);\n    }\n\n    return out;\n}\n"
  },
  {
    "path": "impls/cpp/Debug.h",
    "content": "#ifndef INCLUDE_DEBUG_H\n#define INCLUDE_DEBUG_H\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#define DEBUG_TRACE                    1\n//#define DEBUG_OBJECT_LIFETIMES         1\n//#define DEBUG_ENV_LIFETIMES            1\n\n#define DEBUG_TRACE_FILE    stderr\n\n#define NOOP    do { } while (false)\n#define NOTRACE(...)    NOOP\n\n#if DEBUG_TRACE\n    #define TRACE(...) fprintf(DEBUG_TRACE_FILE, __VA_ARGS__)\n#else\n    #define TRACE NOTRACE\n#endif\n\n#if DEBUG_OBJECT_LIFETIMES\n    #define TRACE_OBJECT TRACE\n#else\n    #define TRACE_OBJECT NOTRACE\n#endif\n\n#if DEBUG_ENV_LIFETIMES\n    #define TRACE_ENV TRACE\n#else\n    #define TRACE_ENV NOTRACE\n#endif\n\n#define _ASSERT(file, line, condition, ...) \\\n    if (!(condition)) { \\\n        printf(\"Assertion failed at %s(%d): \", file, line); \\\n        printf(__VA_ARGS__); \\\n        exit(1); \\\n    } else { }\n\n\n#define ASSERT(condition, ...) \\\n    _ASSERT(__FILE__, __LINE__, condition, __VA_ARGS__)\n\n#endif // INCLUDE_DEBUG_H\n"
  },
  {
    "path": "impls/cpp/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Install g++ for any C/C++ based implementations\nRUN apt-get -y install g++ libreadline-dev\n"
  },
  {
    "path": "impls/cpp/Environment.cpp",
    "content": "#include \"Environment.h\"\n#include \"Types.h\"\n\n#include <algorithm>\n\nmalEnv::malEnv(malEnvPtr outer)\n: m_outer(outer)\n{\n    TRACE_ENV(\"Creating malEnv %p, outer=%p\\n\", this, m_outer.ptr());\n}\n\nmalEnv::malEnv(malEnvPtr outer, const StringVec& bindings,\n               malValueIter argsBegin, malValueIter argsEnd)\n: m_outer(outer)\n{\n    TRACE_ENV(\"Creating malEnv %p, outer=%p\\n\", this, m_outer.ptr());\n    int n = bindings.size();\n    auto it = argsBegin;\n    for (int i = 0; i < n; i++) {\n        if (bindings[i] == \"&\") {\n            MAL_CHECK(i == n - 2, \"There must be one parameter after the &\");\n\n            set(bindings[n-1], mal::list(it, argsEnd));\n            return;\n        }\n        MAL_CHECK(it != argsEnd, \"Not enough parameters\");\n        set(bindings[i], *it);\n        ++it;\n    }\n    MAL_CHECK(it == argsEnd, \"Too many parameters\");\n}\n\nmalEnv::~malEnv()\n{\n    TRACE_ENV(\"Destroying malEnv %p, outer=%p\\n\", this, m_outer.ptr());\n}\n\nmalEnvPtr malEnv::find(const String& symbol)\n{\n    for (malEnvPtr env = this; env; env = env->m_outer) {\n        if (env->m_map.find(symbol) != env->m_map.end()) {\n            return env;\n        }\n    }\n    return NULL;\n}\n\nmalValuePtr malEnv::get(const String& symbol)\n{\n    for (malEnvPtr env = this; env; env = env->m_outer) {\n        auto it = env->m_map.find(symbol);\n        if (it != env->m_map.end()) {\n            return it->second;\n        }\n    }\n    MAL_FAIL(\"'%s' not found\", symbol.c_str());\n}\n\nmalValuePtr malEnv::set(const String& symbol, malValuePtr value)\n{\n    m_map[symbol] = value;\n    return value;\n}\n\nmalEnvPtr malEnv::getRoot()\n{\n    // Work our way down the the global environment.\n    for (malEnvPtr env = this; ; env = env->m_outer) {\n        if (!env->m_outer) {\n            return env;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cpp/Environment.h",
    "content": "#ifndef INCLUDE_ENVIRONMENT_H\n#define INCLUDE_ENVIRONMENT_H\n\n#include \"MAL.h\"\n\n#include <map>\n\nclass malEnv : public RefCounted {\npublic:\n    malEnv(malEnvPtr outer = NULL);\n    malEnv(malEnvPtr outer,\n           const StringVec& bindings,\n           malValueIter argsBegin,\n           malValueIter argsEnd);\n\n    ~malEnv();\n\n    malValuePtr get(const String& symbol);\n    malEnvPtr   find(const String& symbol);\n    malValuePtr set(const String& symbol, malValuePtr value);\n    malEnvPtr   getRoot();\n\nprivate:\n    typedef std::map<String, malValuePtr> Map;\n    Map m_map;\n    malEnvPtr m_outer;\n};\n\n#endif // INCLUDE_ENVIRONMENT_H\n"
  },
  {
    "path": "impls/cpp/MAL.h",
    "content": "#ifndef INCLUDE_MAL_H\n#define INCLUDE_MAL_H\n\n#include \"Debug.h\"\n#include \"RefCountedPtr.h\"\n#include \"String.h\"\n#include \"Validation.h\"\n\n#include <vector>\n\nclass malValue;\ntypedef RefCountedPtr<malValue>  malValuePtr;\ntypedef std::vector<malValuePtr> malValueVec;\ntypedef malValueVec::iterator    malValueIter;\n\nclass malEnv;\ntypedef RefCountedPtr<malEnv>     malEnvPtr;\n\n// step*.cpp\nextern malValuePtr APPLY(malValuePtr op,\n                         malValueIter argsBegin, malValueIter argsEnd);\nextern malValuePtr EVAL(malValuePtr ast, malEnvPtr env);\nextern malValuePtr readline(const String& prompt);\nextern String rep(const String& input, malEnvPtr env);\n\n// Core.cpp\nextern void installCore(malEnvPtr env);\n\n// Reader.cpp\nextern malValuePtr readStr(const String& input);\n\n#endif // INCLUDE_MAL_H\n"
  },
  {
    "path": "impls/cpp/Makefile",
    "content": "uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n\nCXX=g++\nifeq ($(uname_S),Darwin)\n\t# Native build on yosemite. Requires: brew install readline\n\tREADLINE=/usr/local/opt/readline\n\tINCPATHS=-I$(READLINE)/include\n\tLIBPATHS=-L$(READLINE)/lib\nendif\n\nLD=$(CXX)\nAR=ar\n\nDEBUG=-ggdb\nCXXFLAGS=-O3 -Wall $(DEBUG) $(INCPATHS) -std=c++11\nLDFLAGS=-O3 $(DEBUG) $(LIBPATHS) -L. -lreadline -lhistory\n\nLIBSOURCES=Core.cpp Environment.cpp Reader.cpp ReadLine.cpp String.cpp \\\n\t\t\tTypes.cpp Validation.cpp\nLIBOBJS=$(LIBSOURCES:%.cpp=%.o)\n\nMAINS=$(wildcard step*.cpp)\nTARGETS=$(MAINS:%.cpp=%)\n\n.PHONY:\tall clean\n\n.SUFFIXES: .cpp .o\n\nall: $(TARGETS)\n\ndist: mal\n\nmal: stepA_mal\n\tcp $< $@\n\n.deps: *.cpp *.h\n\t$(CXX) $(CXXFLAGS) -MM *.cpp > .deps\n\n$(TARGETS): %: %.o libmal.a\n\t$(LD) $^ -o $@ $(LDFLAGS)\n\nlibmal.a: $(LIBOBJS)\n\t$(AR) rcs $@ $^\n\n.cpp.o:\n\t$(CXX) $(CXXFLAGS) -c $< -o $@\n\nclean:\n\trm -rf *.o $(TARGETS) libmal.a .deps mal\n\n-include .deps\n"
  },
  {
    "path": "impls/cpp/README.md",
    "content": "# Compilation notes\n\n## Mac OSX\n\nThis C++ implementation was developed on Mac OS X Yosemite, and uses the\nstock g++ compiler.\n\nThe only other requirement is GNU Readline, which I got from homebrew.\n\n    brew install readline\n\nYou may need to edit the READLINE path in the Makefile.\n\n## Ubuntu 14.10/15.04\n\nThis should compile on Ubuntu 14.10 and 15.04 with the following packages\n\n    apt-get install clang-3.5 libreadline-dev make\n\n## Docker\n\nFor everyone else, there is a Dockerfile and associated docker.sh script which\ncan be used to make and run this implementation.\n\n    * build the docker image\n\n        ./docker build\n\n    * make the MAL binaries:\n\n        ./docker make\n\n    * run one of the implementations:\n\n        ./docker run ./stepA_mal\n\n    * open a shell inside the docker container:\n\n        ./docker run\n\n"
  },
  {
    "path": "impls/cpp/ReadLine.cpp",
    "content": "#include \"ReadLine.h\"\n#include \"String.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include <readline/readline.h>\n#include <readline/history.h>\n#include <readline/tilde.h>\n\nReadLine::ReadLine(const String& historyFile)\n: m_historyPath(copyAndFree(tilde_expand(historyFile.c_str())))\n{\n    read_history(m_historyPath.c_str());\n}\n\nReadLine::~ReadLine()\n{\n}\n\nbool ReadLine::get(const String& prompt, String& out)\n{\n    char *line = readline(prompt.c_str());\n    if (line == NULL) {\n        return false;\n    }\n    add_history(line); // Add input to in-memory history\n    append_history(1, m_historyPath.c_str());\n\n    out = line;\n    free(line);\n\n    return true;\n}\n"
  },
  {
    "path": "impls/cpp/ReadLine.h",
    "content": "#ifndef INCLUDE_READLINE_H\n#define INCLUDE_READLINE_H\n\n#include \"String.h\"\n\nclass ReadLine {\npublic:\n    ReadLine(const String& historyFile);\n    ~ReadLine();\n\n    bool get(const String& prompt, String& line);\n\nprivate:\n    String m_historyPath;\n};\n\n#endif // INCLUDE_READLINE_H\n"
  },
  {
    "path": "impls/cpp/Reader.cpp",
    "content": "#include \"MAL.h\"\n#include \"Types.h\"\n\n#include <regex>\n\ntypedef std::regex              Regex;\n\nstatic const Regex intRegex(\"^[-+]?\\\\d+$\");\nstatic const Regex closeRegex(\"[\\\\)\\\\]}]\");\n\nstatic const Regex whitespaceRegex(\"[\\\\s,]+|;.*\");\nstatic const Regex tokenRegexes[] = {\n    Regex(\"~@\"),\n    Regex(\"[\\\\[\\\\]{}()'`~^@]\"),\n    Regex(\"\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"\"),\n    Regex(\"[^\\\\s\\\\[\\\\]{}('\\\"`,;)]+\"),\n};\n\nclass Tokeniser\n{\npublic:\n    Tokeniser(const String& input);\n\n    String peek() const {\n        ASSERT(!eof(), \"Tokeniser reading past EOF in peek\\n\");\n        return m_token;\n    }\n\n    String next() {\n        ASSERT(!eof(), \"Tokeniser reading past EOF in next\\n\");\n        String ret = peek();\n        nextToken();\n        return ret;\n    }\n\n    bool eof() const {\n        return m_iter == m_end;\n    }\n\nprivate:\n    void skipWhitespace();\n    void nextToken();\n\n    bool matchRegex(const Regex& regex);\n\n    typedef String::const_iterator StringIter;\n\n    String      m_token;\n    StringIter  m_iter;\n    StringIter  m_end;\n};\n\nTokeniser::Tokeniser(const String& input)\n:   m_iter(input.begin())\n,   m_end(input.end())\n{\n    nextToken();\n}\n\nbool Tokeniser::matchRegex(const Regex& regex)\n{\n    if (eof()) {\n        return false;\n    }\n\n    std::smatch match;\n    auto flags = std::regex_constants::match_continuous;\n    if (!std::regex_search(m_iter, m_end, match, regex, flags)) {\n        return false;\n    }\n\n    ASSERT(match.size() == 1, \"Should only have one submatch, not %lu\\n\",\n                              match.size());\n    ASSERT(match.position(0) == 0, \"Need to match first character\\n\");\n    ASSERT(match.length(0) > 0, \"Need to match a non-empty string\\n\");\n\n    // Don't advance  m_iter now, do it after we've consumed the token in\n    // next().  If we do it now, we hit eof() when there's still one token left.\n    m_token = match.str(0);\n\n    return true;\n}\n\nvoid Tokeniser::nextToken()\n{\n    m_iter += m_token.size();\n\n    skipWhitespace();\n    if (eof()) {\n        return;\n    }\n\n    for (auto &it : tokenRegexes) {\n        if (matchRegex(it)) {\n            return;\n        }\n    }\n\n    String mismatch(m_iter, m_end);\n    if (mismatch[0] == '\"') {\n        MAL_CHECK(false, \"expected '\\\"', got EOF\");\n    }\n    else {\n        MAL_CHECK(false, \"unexpected '%s'\", mismatch.c_str());\n    }\n}\n\nvoid Tokeniser::skipWhitespace()\n{\n    while (matchRegex(whitespaceRegex)) {\n        m_iter += m_token.size();\n    }\n}\n\nstatic malValuePtr readAtom(Tokeniser& tokeniser);\nstatic malValuePtr readForm(Tokeniser& tokeniser);\nstatic void readList(Tokeniser& tokeniser, malValueVec* items,\n                      const String& end);\nstatic malValuePtr processMacro(Tokeniser& tokeniser, const String& symbol);\n\nmalValuePtr readStr(const String& input)\n{\n    Tokeniser tokeniser(input);\n    if (tokeniser.eof()) {\n        throw malEmptyInputException();\n    }\n    return readForm(tokeniser);\n}\n\nstatic malValuePtr readForm(Tokeniser& tokeniser)\n{\n    MAL_CHECK(!tokeniser.eof(), \"expected form, got EOF\");\n    String token = tokeniser.peek();\n\n    MAL_CHECK(!std::regex_match(token, closeRegex),\n            \"unexpected '%s'\", token.c_str());\n\n    if (token == \"(\") {\n        tokeniser.next();\n        std::unique_ptr<malValueVec> items(new malValueVec);\n        readList(tokeniser, items.get(), \")\");\n        return mal::list(items.release());\n    }\n    if (token == \"[\") {\n        tokeniser.next();\n        std::unique_ptr<malValueVec> items(new malValueVec);\n        readList(tokeniser, items.get(), \"]\");\n        return mal::vector(items.release());\n    }\n    if (token == \"{\") {\n        tokeniser.next();\n        malValueVec items;\n        readList(tokeniser, &items, \"}\");\n        return mal::hash(items.begin(), items.end(), false);\n    }\n    return readAtom(tokeniser);\n}\n\nstatic malValuePtr readAtom(Tokeniser& tokeniser)\n{\n    struct ReaderMacro {\n        const char* token;\n        const char* symbol;\n    };\n    ReaderMacro macroTable[] = {\n        { \"@\",   \"deref\" },\n        { \"`\",   \"quasiquote\" },\n        { \"'\",   \"quote\" },\n        { \"~@\",  \"splice-unquote\" },\n        { \"~\",   \"unquote\" },\n    };\n\n    struct Constant {\n        const char* token;\n        malValuePtr value;\n    };\n    Constant constantTable[] = {\n        { \"false\",  mal::falseValue()  },\n        { \"nil\",    mal::nilValue()          },\n        { \"true\",   mal::trueValue()   },\n    };\n\n    String token = tokeniser.next();\n    if (token[0] == '\"') {\n        return mal::string(unescape(token));\n    }\n    if (token[0] == ':') {\n        return mal::keyword(token);\n    }\n    if (token == \"^\") {\n        malValuePtr meta = readForm(tokeniser);\n        malValuePtr value = readForm(tokeniser);\n        // Note that meta and value switch places\n        return mal::list(mal::symbol(\"with-meta\"), value, meta);\n    }\n    for (auto &constant : constantTable) {\n        if (token == constant.token) {\n            return constant.value;\n        }\n    }\n    for (auto &macro : macroTable) {\n        if (token == macro.token) {\n            return processMacro(tokeniser, macro.symbol);\n        }\n    }\n    if (std::regex_match(token, intRegex)) {\n        return mal::integer(token);\n    }\n    return mal::symbol(token);\n}\n\nstatic void readList(Tokeniser& tokeniser, malValueVec* items,\n                      const String& end)\n{\n    while (1) {\n        MAL_CHECK(!tokeniser.eof(), \"expected '%s', got EOF\", end.c_str());\n        if (tokeniser.peek() == end) {\n            tokeniser.next();\n            return;\n        }\n        items->push_back(readForm(tokeniser));\n    }\n}\n\nstatic malValuePtr processMacro(Tokeniser& tokeniser, const String& symbol)\n{\n    return mal::list(mal::symbol(symbol), readForm(tokeniser));\n}\n"
  },
  {
    "path": "impls/cpp/RefCountedPtr.h",
    "content": "#ifndef INCLUDE_REFCOUNTEDPTR_H\n#define INCLUDE_REFCOUNTEDPTR_H\n\n#include \"Debug.h\"\n\n#include <cstddef>\n\nclass RefCounted {\npublic:\n    RefCounted() : m_refCount(0) { }\n    virtual ~RefCounted() { }\n\n    const RefCounted* acquire() const { m_refCount++; return this; }\n    int release() const { return --m_refCount; }\n    int refCount() const { return m_refCount; }\n\nprivate:\n    RefCounted(const RefCounted&); // no copy ctor\n    RefCounted& operator = (const RefCounted&); // no assignments\n\n    mutable int m_refCount;\n};\n\ntemplate<class T>\nclass RefCountedPtr {\npublic:\n    RefCountedPtr() : m_object(0) { }\n\n    RefCountedPtr(T* object) : m_object(0)\n    { acquire(object); }\n\n    RefCountedPtr(const RefCountedPtr& rhs) : m_object(0)\n    { acquire(rhs.m_object); }\n\n    const RefCountedPtr& operator = (const RefCountedPtr& rhs) {\n        acquire(rhs.m_object);\n        return *this;\n    }\n\n    bool operator == (const RefCountedPtr& rhs) const {\n        return m_object == rhs.m_object;\n    }\n\n    bool operator != (const RefCountedPtr& rhs) const {\n        return m_object != rhs.m_object;\n    }\n\n    operator bool () const {\n        return m_object != NULL;\n    }\n\n    ~RefCountedPtr() {\n        release();\n    }\n\n    T* operator -> () const { return m_object; }\n    T* ptr() const { return m_object; }\n\nprivate:\n    void acquire(T* object) {\n        if (object != NULL) {\n            object->acquire();\n        }\n        release();\n        m_object = object;\n    }\n\n    void release() {\n        if ((m_object != NULL) && (m_object->release() == 0)) {\n            delete m_object;\n        }\n    }\n\n    T* m_object;\n};\n\n#endif // INCLUDE_REFCOUNTEDPTR_H\n"
  },
  {
    "path": "impls/cpp/StaticList.h",
    "content": "#ifndef INCLUDE_STATICLIST_H\n#define INCLUDE_STATICLIST_H\n\ntemplate<typename T>\nclass StaticList\n{\npublic:\n    StaticList() : m_head(NULL) { }\n\n    class Iterator;\n    Iterator begin() { return Iterator(m_head); }\n    Iterator end()   { return Iterator(NULL);   }\n\n    class Node {\n    public:\n        Node(StaticList<T>& list, T item)\n        : m_item(item), m_next(list.m_head) {\n            list.m_head = this;\n        }\n\n    private:\n        friend class Iterator;\n        T m_item;\n        Node* m_next;\n    };\n\n    class Iterator {\n    public:\n        Iterator& operator ++ () {\n            m_node = m_node->m_next;\n            return *this;\n        }\n\n        T& operator * () { return m_node->m_item; }\n        bool operator != (const Iterator& that) {\n            return m_node != that.m_node;\n        }\n\n    private:\n        friend class StaticList<T>;\n        Iterator(Node* node) : m_node(node) { }\n        Node* m_node;\n    };\n\nprivate:\n    friend class Node;\n    Node*  m_head;\n};\n\n#endif // INCLUDE_STATICLIST_H\n"
  },
  {
    "path": "impls/cpp/String.cpp",
    "content": "#include \"Debug.h\"\n#include \"String.h\"\n\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// Adapted from: http://stackoverflow.com/questions/2342162\nString stringPrintf(const char* fmt, ...) {\n    int size = strlen(fmt); // make a guess\n    String str;\n    va_list ap;\n    while (1) {\n        str.resize(size);\n        va_start(ap, fmt);\n        int n = vsnprintf((char *)str.data(), size, fmt, ap);\n        va_end(ap);\n        if (n > -1 && n < size) {  // Everything worked\n            str.resize(n);\n            return str;\n        }\n        if (n > -1)  // Needed size returned\n            size = n + 1;   // For null char\n        else\n            size *= 2;      // Guess at a larger size (OS specific)\n    }\n    return str;\n}\n\nString copyAndFree(char* mallocedString)\n{\n    String ret(mallocedString);\n    free(mallocedString);\n    return ret;\n}\n\nString escape(const String& in)\n{\n    String out;\n    out.reserve(in.size() * 2 + 2); // each char may get escaped + two \"'s\n    out += '\"';\n    for (auto it = in.begin(), end = in.end(); it != end; ++it) {\n        char c = *it;\n        switch (c) {\n            case '\\\\': out += \"\\\\\\\\\"; break;\n            case '\\n': out += \"\\\\n\"; break;\n            case '\"':  out += \"\\\\\\\"\"; break;\n            default:   out += c;      break;\n        };\n    }\n    out += '\"';\n    out.shrink_to_fit();\n    return out;\n}\n\nstatic char unescape(char c)\n{\n    switch (c) {\n        case '\\\\':  return '\\\\';\n        case 'n':   return '\\n';\n        case '\"':   return '\"';\n        default:    return c;\n    }\n}\n\nString unescape(const String& in)\n{\n    String out;\n    out.reserve(in.size()); // unescaped string will always be shorter\n\n    // in will have double-quotes at either end, so move the iterators in\n    for (auto it = in.begin()+1, end = in.end()-1; it != end; ++it) {\n        char c = *it;\n        if (c == '\\\\') {\n            ++it;\n            if (it != end) {\n                out += unescape(*it);\n            }\n        }\n        else {\n            out += c;\n        }\n    }\n    out.shrink_to_fit();\n    return out;\n}\n\n"
  },
  {
    "path": "impls/cpp/String.h",
    "content": "#ifndef INCLUDE_STRING_H\n#define INCLUDE_STRING_H\n\n#include <string>\n#include <vector>\n\ntypedef std::string         String;\ntypedef std::vector<String> StringVec;\n\n#define STRF        stringPrintf\n#define PLURAL(n)   &(\"s\"[(n)==1])\n\nextern String stringPrintf(const char* fmt, ...);\nextern String copyAndFree(char* mallocedString);\nextern String escape(const String& s);\nextern String unescape(const String& s);\n\n#endif // INCLUDE_STRING_H\n"
  },
  {
    "path": "impls/cpp/Types.cpp",
    "content": "#include \"Debug.h\"\n#include \"Environment.h\"\n#include \"Types.h\"\n\n#include <algorithm>\n#include <memory>\n#include <typeinfo>\n\nnamespace mal {\n    malValuePtr atom(malValuePtr value) {\n        return malValuePtr(new malAtom(value));\n    };\n\n    malValuePtr boolean(bool value) {\n        return value ? trueValue() : falseValue();\n    }\n\n    malValuePtr builtin(const String& name, malBuiltIn::ApplyFunc handler) {\n        return malValuePtr(new malBuiltIn(name, handler));\n    };\n\n    malValuePtr falseValue() {\n        static malValuePtr c(new malConstant(\"false\"));\n        return malValuePtr(c);\n    };\n\n\n    malValuePtr hash(const malHash::Map& map) {\n        return malValuePtr(new malHash(map));\n    }\n\n    malValuePtr hash(malValueIter argsBegin, malValueIter argsEnd,\n                     bool isEvaluated) {\n        return malValuePtr(new malHash(argsBegin, argsEnd, isEvaluated));\n    }\n\n    malValuePtr integer(int64_t value) {\n        return malValuePtr(new malInteger(value));\n    };\n\n    malValuePtr integer(const String& token) {\n        return integer(std::stoi(token));\n    };\n\n    malValuePtr keyword(const String& token) {\n        return malValuePtr(new malKeyword(token));\n    };\n\n    malValuePtr lambda(const StringVec& bindings,\n                       malValuePtr body, malEnvPtr env) {\n        return malValuePtr(new malLambda(bindings, body, env));\n    }\n\n    malValuePtr list(malValueVec* items) {\n        return malValuePtr(new malList(items));\n    };\n\n    malValuePtr list(malValueIter begin, malValueIter end) {\n        return malValuePtr(new malList(begin, end));\n    };\n\n    malValuePtr list(malValuePtr a) {\n        malValueVec* items = new malValueVec(1);\n        items->at(0) = a;\n        return malValuePtr(new malList(items));\n    }\n\n    malValuePtr list(malValuePtr a, malValuePtr b) {\n        malValueVec* items = new malValueVec(2);\n        items->at(0) = a;\n        items->at(1) = b;\n        return malValuePtr(new malList(items));\n    }\n\n    malValuePtr list(malValuePtr a, malValuePtr b, malValuePtr c) {\n        malValueVec* items = new malValueVec(3);\n        items->at(0) = a;\n        items->at(1) = b;\n        items->at(2) = c;\n        return malValuePtr(new malList(items));\n    }\n\n    malValuePtr macro(const malLambda& lambda) {\n        return malValuePtr(new malLambda(lambda, true));\n    };\n\n    malValuePtr nilValue() {\n        static malValuePtr c(new malConstant(\"nil\"));\n        return malValuePtr(c);\n    };\n\n    malValuePtr string(const String& token) {\n        return malValuePtr(new malString(token));\n    }\n\n    malValuePtr symbol(const String& token) {\n        return malValuePtr(new malSymbol(token));\n    };\n\n    malValuePtr trueValue() {\n        static malValuePtr c(new malConstant(\"true\"));\n        return malValuePtr(c);\n    };\n\n    malValuePtr vector(malValueVec* items) {\n        return malValuePtr(new malVector(items));\n    };\n\n    malValuePtr vector(malValueIter begin, malValueIter end) {\n        return malValuePtr(new malVector(begin, end));\n    };\n};\n\nmalValuePtr malBuiltIn::apply(malValueIter argsBegin,\n                              malValueIter argsEnd) const\n{\n    return m_handler(m_name, argsBegin, argsEnd);\n}\n\nstatic String makeHashKey(malValuePtr key)\n{\n    if (const malString* skey = DYNAMIC_CAST(malString, key)) {\n        return skey->print(true);\n    }\n    else if (const malKeyword* kkey = DYNAMIC_CAST(malKeyword, key)) {\n        return kkey->print(true);\n    }\n    MAL_FAIL(\"%s is not a string or keyword\", key->print(true).c_str());\n}\n\nstatic malHash::Map addToMap(malHash::Map& map,\n    malValueIter argsBegin, malValueIter argsEnd)\n{\n    // This is intended to be called with pre-evaluated arguments.\n    for (auto it = argsBegin; it != argsEnd; ++it) {\n        String key = makeHashKey(*it++);\n        map[key] = *it;\n    }\n\n    return map;\n}\n\nstatic malHash::Map createMap(malValueIter argsBegin, malValueIter argsEnd)\n{\n    MAL_CHECK(std::distance(argsBegin, argsEnd) % 2 == 0,\n            \"hash-map requires an even-sized list\");\n\n    malHash::Map map;\n    return addToMap(map, argsBegin, argsEnd);\n}\n\nmalHash::malHash(malValueIter argsBegin, malValueIter argsEnd, bool isEvaluated)\n: m_map(createMap(argsBegin, argsEnd))\n, m_isEvaluated(isEvaluated)\n{\n\n}\n\nmalHash::malHash(const malHash::Map& map)\n: m_map(map)\n, m_isEvaluated(true)\n{\n\n}\n\nmalValuePtr\nmalHash::assoc(malValueIter argsBegin, malValueIter argsEnd) const\n{\n    MAL_CHECK(std::distance(argsBegin, argsEnd) % 2 == 0,\n            \"assoc requires an even-sized list\");\n\n    malHash::Map map(m_map);\n    return mal::hash(addToMap(map, argsBegin, argsEnd));\n}\n\nbool malHash::contains(malValuePtr key) const\n{\n    auto it = m_map.find(makeHashKey(key));\n    return it != m_map.end();\n}\n\nmalValuePtr\nmalHash::dissoc(malValueIter argsBegin, malValueIter argsEnd) const\n{\n    malHash::Map map(m_map);\n    for (auto it = argsBegin; it != argsEnd; ++it) {\n        String key = makeHashKey(*it);\n        map.erase(key);\n    }\n    return mal::hash(map);\n}\n\nmalValuePtr malHash::eval(malEnvPtr env)\n{\n    if (m_isEvaluated) {\n        return malValuePtr(this);\n    }\n\n    malHash::Map map;\n    for (auto it = m_map.begin(), end = m_map.end(); it != end; ++it) {\n        map[it->first] = EVAL(it->second, env);\n    }\n    return mal::hash(map);\n}\n\nmalValuePtr malHash::get(malValuePtr key) const\n{\n    auto it = m_map.find(makeHashKey(key));\n    return it == m_map.end() ? mal::nilValue() : it->second;\n}\n\nmalValuePtr malHash::keys() const\n{\n    malValueVec* keys = new malValueVec();\n    keys->reserve(m_map.size());\n    for (auto it = m_map.begin(), end = m_map.end(); it != end; ++it) {\n        if (it->first[0] == '\"') {\n            keys->push_back(mal::string(unescape(it->first)));\n        }\n        else {\n            keys->push_back(mal::keyword(it->first));\n        }\n    }\n    return mal::list(keys);\n}\n\nmalValuePtr malHash::values() const\n{\n    malValueVec* keys = new malValueVec();\n    keys->reserve(m_map.size());\n    for (auto it = m_map.begin(), end = m_map.end(); it != end; ++it) {\n        keys->push_back(it->second);\n    }\n    return mal::list(keys);\n}\n\nString malHash::print(bool readably) const\n{\n    String s = \"{\";\n\n    auto it = m_map.begin(), end = m_map.end();\n    if (it != end) {\n        s += it->first + \" \" + it->second->print(readably);\n        ++it;\n    }\n    for ( ; it != end; ++it) {\n        s += \" \" + it->first + \" \" + it->second->print(readably);\n    }\n\n    return s + \"}\";\n}\n\nbool malHash::doIsEqualTo(const malValue* rhs) const\n{\n    const malHash::Map& r_map = static_cast<const malHash*>(rhs)->m_map;\n    if (m_map.size() != r_map.size()) {\n        return false;\n    }\n\n    for (auto it0 = m_map.begin(), end0 = m_map.end(), it1 = r_map.begin();\n         it0 != end0; ++it0, ++it1) {\n\n        if (it0->first != it1->first) {\n            return false;\n        }\n        if (!it0->second->isEqualTo(it1->second.ptr())) {\n            return false;\n        }\n    }\n    return true;\n}\n\nmalLambda::malLambda(const StringVec& bindings,\n                     malValuePtr body, malEnvPtr env)\n: m_bindings(bindings)\n, m_body(body)\n, m_env(env)\n, m_isMacro(false)\n{\n\n}\n\nmalLambda::malLambda(const malLambda& that, malValuePtr meta)\n: malApplicable(meta)\n, m_bindings(that.m_bindings)\n, m_body(that.m_body)\n, m_env(that.m_env)\n, m_isMacro(that.m_isMacro)\n{\n\n}\n\nmalLambda::malLambda(const malLambda& that, bool isMacro)\n: malApplicable(that.m_meta)\n, m_bindings(that.m_bindings)\n, m_body(that.m_body)\n, m_env(that.m_env)\n, m_isMacro(isMacro)\n{\n\n}\n\nmalValuePtr malLambda::apply(malValueIter argsBegin,\n                             malValueIter argsEnd) const\n{\n    return EVAL(m_body, makeEnv(argsBegin, argsEnd));\n}\n\nmalValuePtr malLambda::doWithMeta(malValuePtr meta) const\n{\n    return new malLambda(*this, meta);\n}\n\nmalEnvPtr malLambda::makeEnv(malValueIter argsBegin, malValueIter argsEnd) const\n{\n    return malEnvPtr(new malEnv(m_env, m_bindings, argsBegin, argsEnd));\n}\n\nmalValuePtr malList::conj(malValueIter argsBegin,\n                          malValueIter argsEnd) const\n{\n    int oldItemCount = std::distance(begin(), end());\n    int newItemCount = std::distance(argsBegin, argsEnd);\n\n    malValueVec* items = new malValueVec(oldItemCount + newItemCount);\n    std::reverse_copy(argsBegin, argsEnd, items->begin());\n    std::copy(begin(), end(), items->begin() + newItemCount);\n\n    return mal::list(items);\n}\n\nmalValuePtr malList::eval(malEnvPtr env)\n{\n    // Note, this isn't actually called since the TCO updates, but\n    // is required for the earlier steps, so don't get rid of it.\n    if (count() == 0) {\n        return malValuePtr(this);\n    }\n\n    std::unique_ptr<malValueVec> items(evalItems(env));\n    auto it = items->begin();\n    malValuePtr op = *it;\n    return APPLY(op, ++it, items->end());\n}\n\nString malList::print(bool readably) const\n{\n    return '(' + malSequence::print(readably) + ')';\n}\n\nmalValuePtr malValue::eval(malEnvPtr env)\n{\n    // Default case of eval is just to return the object itself.\n    return malValuePtr(this);\n}\n\nbool malValue::isEqualTo(const malValue* rhs) const\n{\n    // Special-case. Vectors and Lists can be compared.\n    bool matchingTypes = (typeid(*this) == typeid(*rhs)) ||\n        (dynamic_cast<const malSequence*>(this) &&\n         dynamic_cast<const malSequence*>(rhs));\n\n    return matchingTypes && doIsEqualTo(rhs);\n}\n\nbool malValue::isTrue() const\n{\n    return (this != mal::falseValue().ptr())\n        && (this != mal::nilValue().ptr());\n}\n\nmalValuePtr malValue::meta() const\n{\n    return m_meta.ptr() == NULL ? mal::nilValue() : m_meta;\n}\n\nmalValuePtr malValue::withMeta(malValuePtr meta) const\n{\n    return doWithMeta(meta);\n}\n\nmalSequence::malSequence(malValueVec* items)\n: m_items(items)\n{\n\n}\n\nmalSequence::malSequence(malValueIter begin, malValueIter end)\n: m_items(new malValueVec(begin, end))\n{\n\n}\n\nmalSequence::malSequence(const malSequence& that, malValuePtr meta)\n: malValue(meta)\n, m_items(new malValueVec(*(that.m_items)))\n{\n\n}\n\nmalSequence::~malSequence()\n{\n    delete m_items;\n}\n\nbool malSequence::doIsEqualTo(const malValue* rhs) const\n{\n    const malSequence* rhsSeq = static_cast<const malSequence*>(rhs);\n    if (count() != rhsSeq->count()) {\n        return false;\n    }\n\n    for (malValueIter it0 = m_items->begin(),\n                      it1 = rhsSeq->begin(),\n                      end = m_items->end(); it0 != end; ++it0, ++it1) {\n\n        if (! (*it0)->isEqualTo((*it1).ptr())) {\n            return false;\n        }\n    }\n    return true;\n}\n\nmalValueVec* malSequence::evalItems(malEnvPtr env) const\n{\n    malValueVec* items = new malValueVec;;\n    items->reserve(count());\n    for (auto it = m_items->begin(), end = m_items->end(); it != end; ++it) {\n        items->push_back(EVAL(*it, env));\n    }\n    return items;\n}\n\nmalValuePtr malSequence::first() const\n{\n    return count() == 0 ? mal::nilValue() : item(0);\n}\n\nString malSequence::print(bool readably) const\n{\n    String str;\n    auto end = m_items->cend();\n    auto it = m_items->cbegin();\n    if (it != end) {\n        str += (*it)->print(readably);\n        ++it;\n    }\n    for ( ; it != end; ++it) {\n        str += \" \";\n        str += (*it)->print(readably);\n    }\n    return str;\n}\n\nmalValuePtr malSequence::rest() const\n{\n    malValueIter start = (count() > 0) ? begin() + 1 : end();\n    return mal::list(start, end());\n}\n\nString malString::escapedValue() const\n{\n    return escape(value());\n}\n\nString malString::print(bool readably) const\n{\n    return readably ? escapedValue() : value();\n}\n\nmalValuePtr malSymbol::eval(malEnvPtr env)\n{\n    return env->get(value());\n}\n\nmalValuePtr malVector::conj(malValueIter argsBegin,\n                            malValueIter argsEnd) const\n{\n    int oldItemCount = std::distance(begin(), end());\n    int newItemCount = std::distance(argsBegin, argsEnd);\n\n    malValueVec* items = new malValueVec(oldItemCount + newItemCount);\n    std::copy(begin(), end(), items->begin());\n    std::copy(argsBegin, argsEnd, items->begin() + oldItemCount);\n\n    return mal::vector(items);\n}\n\nmalValuePtr malVector::eval(malEnvPtr env)\n{\n    return mal::vector(evalItems(env));\n}\n\nString malVector::print(bool readably) const\n{\n    return '[' + malSequence::print(readably) + ']';\n}\n"
  },
  {
    "path": "impls/cpp/Types.h",
    "content": "#ifndef INCLUDE_TYPES_H\n#define INCLUDE_TYPES_H\n\n#include \"MAL.h\"\n\n#include <exception>\n#include <map>\n\nclass malEmptyInputException : public std::exception { };\n\nclass malValue : public RefCounted {\npublic:\n    malValue() {\n        TRACE_OBJECT(\"Creating malValue %p\\n\", this);\n    }\n    malValue(malValuePtr meta) : m_meta(meta) {\n        TRACE_OBJECT(\"Creating malValue %p\\n\", this);\n    }\n    virtual ~malValue() {\n        TRACE_OBJECT(\"Destroying malValue %p\\n\", this);\n    }\n\n    malValuePtr withMeta(malValuePtr meta) const;\n    virtual malValuePtr doWithMeta(malValuePtr meta) const = 0;\n    malValuePtr meta() const;\n\n    bool isTrue() const;\n\n    bool isEqualTo(const malValue* rhs) const;\n\n    virtual malValuePtr eval(malEnvPtr env);\n\n    virtual String print(bool readably) const = 0;\n\nprotected:\n    virtual bool doIsEqualTo(const malValue* rhs) const = 0;\n\n    malValuePtr m_meta;\n};\n\ntemplate<class T>\nT* value_cast(malValuePtr obj, const char* typeName) {\n    T* dest = dynamic_cast<T*>(obj.ptr());\n    MAL_CHECK(dest != NULL, \"%s is not a %s\",\n              obj->print(true).c_str(), typeName);\n    return dest;\n}\n\n#define VALUE_CAST(Type, Value)    value_cast<Type>(Value, #Type)\n#define DYNAMIC_CAST(Type, Value)  (dynamic_cast<Type*>((Value).ptr()))\n#define STATIC_CAST(Type, Value)   (static_cast<Type*>((Value).ptr()))\n\n#define WITH_META(Type) \\\n    virtual malValuePtr doWithMeta(malValuePtr meta) const { \\\n        return new Type(*this, meta); \\\n    } \\\n\nclass malConstant : public malValue {\npublic:\n    malConstant(String name) : m_name(name) { }\n    malConstant(const malConstant& that, malValuePtr meta)\n        : malValue(meta), m_name(that.m_name) { }\n\n    virtual String print(bool readably) const { return m_name; }\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return this == rhs; // these are singletons\n    }\n\n    WITH_META(malConstant);\n\nprivate:\n    const String m_name;\n};\n\nclass malInteger : public malValue {\npublic:\n    malInteger(int64_t value) : m_value(value) { }\n    malInteger(const malInteger& that, malValuePtr meta)\n        : malValue(meta), m_value(that.m_value) { }\n\n    virtual String print(bool readably) const {\n        return std::to_string(m_value);\n    }\n\n    int64_t value() const { return m_value; }\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return m_value == static_cast<const malInteger*>(rhs)->m_value;\n    }\n\n    WITH_META(malInteger);\n\nprivate:\n    const int64_t m_value;\n};\n\nclass malStringBase : public malValue {\npublic:\n    malStringBase(const String& token)\n        : m_value(token) { }\n    malStringBase(const malStringBase& that, malValuePtr meta)\n        : malValue(meta), m_value(that.value()) { }\n\n    virtual String print(bool readably) const { return m_value; }\n\n    String value() const { return m_value; }\n\nprivate:\n    const String m_value;\n};\n\nclass malString : public malStringBase {\npublic:\n    malString(const String& token)\n        : malStringBase(token) { }\n    malString(const malString& that, malValuePtr meta)\n        : malStringBase(that, meta) { }\n\n    virtual String print(bool readably) const;\n\n    String escapedValue() const;\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return value() == static_cast<const malString*>(rhs)->value();\n    }\n\n    WITH_META(malString);\n};\n\nclass malKeyword : public malStringBase {\npublic:\n    malKeyword(const String& token)\n        : malStringBase(token) { }\n    malKeyword(const malKeyword& that, malValuePtr meta)\n        : malStringBase(that, meta) { }\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return value() == static_cast<const malKeyword*>(rhs)->value();\n    }\n\n    WITH_META(malKeyword);\n};\n\nclass malSymbol : public malStringBase {\npublic:\n    malSymbol(const String& token)\n        : malStringBase(token) { }\n    malSymbol(const malSymbol& that, malValuePtr meta)\n        : malStringBase(that, meta) { }\n\n    virtual malValuePtr eval(malEnvPtr env);\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return value() == static_cast<const malSymbol*>(rhs)->value();\n    }\n\n    WITH_META(malSymbol);\n};\n\nclass malSequence : public malValue {\npublic:\n    malSequence(malValueVec* items);\n    malSequence(malValueIter begin, malValueIter end);\n    malSequence(const malSequence& that, malValuePtr meta);\n    virtual ~malSequence();\n\n    virtual String print(bool readably) const;\n\n    malValueVec* evalItems(malEnvPtr env) const;\n    int count() const { return m_items->size(); }\n    bool isEmpty() const { return m_items->empty(); }\n    malValuePtr item(int index) const { return (*m_items)[index]; }\n\n    malValueIter begin() const { return m_items->begin(); }\n    malValueIter end()   const { return m_items->end(); }\n\n    virtual bool doIsEqualTo(const malValue* rhs) const;\n\n    virtual malValuePtr conj(malValueIter argsBegin,\n                              malValueIter argsEnd) const = 0;\n\n    malValuePtr first() const;\n    virtual malValuePtr rest() const;\n\nprivate:\n    malValueVec* const m_items;\n};\n\nclass malList : public malSequence {\npublic:\n    malList(malValueVec* items) : malSequence(items) { }\n    malList(malValueIter begin, malValueIter end)\n        : malSequence(begin, end) { }\n    malList(const malList& that, malValuePtr meta)\n        : malSequence(that, meta) { }\n\n    virtual String print(bool readably) const;\n    virtual malValuePtr eval(malEnvPtr env);\n\n    virtual malValuePtr conj(malValueIter argsBegin,\n                             malValueIter argsEnd) const;\n\n    WITH_META(malList);\n};\n\nclass malVector : public malSequence {\npublic:\n    malVector(malValueVec* items) : malSequence(items) { }\n    malVector(malValueIter begin, malValueIter end)\n        : malSequence(begin, end) { }\n    malVector(const malVector& that, malValuePtr meta)\n        : malSequence(that, meta) { }\n\n    virtual malValuePtr eval(malEnvPtr env);\n    virtual String print(bool readably) const;\n\n    virtual malValuePtr conj(malValueIter argsBegin,\n                             malValueIter argsEnd) const;\n\n    WITH_META(malVector);\n};\n\nclass malApplicable : public malValue {\npublic:\n    malApplicable() { }\n    malApplicable(malValuePtr meta) : malValue(meta) { }\n\n    virtual malValuePtr apply(malValueIter argsBegin,\n                               malValueIter argsEnd) const = 0;\n};\n\nclass malHash : public malValue {\npublic:\n    typedef std::map<String, malValuePtr> Map;\n\n    malHash(malValueIter argsBegin, malValueIter argsEnd, bool isEvaluated);\n    malHash(const malHash::Map& map);\n    malHash(const malHash& that, malValuePtr meta)\n    : malValue(meta), m_map(that.m_map), m_isEvaluated(that.m_isEvaluated) { }\n\n    malValuePtr assoc(malValueIter argsBegin, malValueIter argsEnd) const;\n    malValuePtr dissoc(malValueIter argsBegin, malValueIter argsEnd) const;\n    bool contains(malValuePtr key) const;\n    malValuePtr eval(malEnvPtr env);\n    malValuePtr get(malValuePtr key) const;\n    malValuePtr keys() const;\n    malValuePtr values() const;\n\n    virtual String print(bool readably) const;\n\n    virtual bool doIsEqualTo(const malValue* rhs) const;\n\n    WITH_META(malHash);\n\nprivate:\n    const Map m_map;\n    const bool m_isEvaluated;\n};\n\nclass malBuiltIn : public malApplicable {\npublic:\n    typedef malValuePtr (ApplyFunc)(const String& name,\n                                    malValueIter argsBegin,\n                                    malValueIter argsEnd);\n\n    malBuiltIn(const String& name, ApplyFunc* handler)\n    : m_name(name), m_handler(handler) { }\n\n    malBuiltIn(const malBuiltIn& that, malValuePtr meta)\n    : malApplicable(meta), m_name(that.m_name), m_handler(that.m_handler) { }\n\n    virtual malValuePtr apply(malValueIter argsBegin,\n                              malValueIter argsEnd) const;\n\n    virtual String print(bool readably) const {\n        return STRF(\"#builtin-function(%s)\", m_name.c_str());\n    }\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return this == rhs; // these are singletons\n    }\n\n    String name() const { return m_name; }\n\n    WITH_META(malBuiltIn);\n\nprivate:\n    const String m_name;\n    ApplyFunc* m_handler;\n};\n\nclass malLambda : public malApplicable {\npublic:\n    malLambda(const StringVec& bindings, malValuePtr body, malEnvPtr env);\n    malLambda(const malLambda& that, malValuePtr meta);\n    malLambda(const malLambda& that, bool isMacro);\n\n    virtual malValuePtr apply(malValueIter argsBegin,\n                              malValueIter argsEnd) const;\n\n    malValuePtr getBody() const { return m_body; }\n    malEnvPtr makeEnv(malValueIter argsBegin, malValueIter argsEnd) const;\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return this == rhs; // do we need to do a deep inspection?\n    }\n\n    virtual String print(bool readably) const {\n        return STRF(\"#user-%s(%p)\", m_isMacro ? \"macro\" : \"function\", this);\n    }\n\n    bool isMacro() const { return m_isMacro; }\n\n    virtual malValuePtr doWithMeta(malValuePtr meta) const;\n\nprivate:\n    const StringVec   m_bindings;\n    const malValuePtr m_body;\n    const malEnvPtr   m_env;\n    const bool        m_isMacro;\n};\n\nclass malAtom : public malValue {\npublic:\n    malAtom(malValuePtr value) : m_value(value) { }\n    malAtom(const malAtom& that, malValuePtr meta)\n        : malValue(meta), m_value(that.m_value) { }\n\n    virtual bool doIsEqualTo(const malValue* rhs) const {\n        return this->m_value->isEqualTo(rhs);\n    }\n\n    virtual String print(bool readably) const {\n        return \"(atom \" + m_value->print(readably) + \")\";\n    };\n\n    malValuePtr deref() const { return m_value; }\n\n    malValuePtr reset(malValuePtr value) { return m_value = value; }\n\n    WITH_META(malAtom);\n\nprivate:\n    malValuePtr m_value;\n};\n\nnamespace mal {\n    malValuePtr atom(malValuePtr value);\n    malValuePtr boolean(bool value);\n    malValuePtr builtin(const String& name, malBuiltIn::ApplyFunc handler);\n    malValuePtr falseValue();\n    malValuePtr hash(malValueIter argsBegin, malValueIter argsEnd,\n                     bool isEvaluated);\n    malValuePtr hash(const malHash::Map& map);\n    malValuePtr integer(int64_t value);\n    malValuePtr integer(const String& token);\n    malValuePtr keyword(const String& token);\n    malValuePtr lambda(const StringVec&, malValuePtr, malEnvPtr);\n    malValuePtr list(malValueVec* items);\n    malValuePtr list(malValueIter begin, malValueIter end);\n    malValuePtr list(malValuePtr a);\n    malValuePtr list(malValuePtr a, malValuePtr b);\n    malValuePtr list(malValuePtr a, malValuePtr b, malValuePtr c);\n    malValuePtr macro(const malLambda& lambda);\n    malValuePtr nilValue();\n    malValuePtr string(const String& token);\n    malValuePtr symbol(const String& token);\n    malValuePtr trueValue();\n    malValuePtr vector(malValueVec* items);\n    malValuePtr vector(malValueIter begin, malValueIter end);\n};\n\n#endif // INCLUDE_TYPES_H\n"
  },
  {
    "path": "impls/cpp/Validation.cpp",
    "content": "#include \"Validation.h\"\n\nint checkArgsIs(const char* name, int expected, int got)\n{\n    MAL_CHECK(got == expected,\n           \"\\\"%s\\\" expects %d arg%s, %d supplied\",\n           name, expected, PLURAL(expected), got);\n    return got;\n}\n\nint checkArgsBetween(const char* name, int min, int max, int got)\n{\n    MAL_CHECK((got >= min) && (got <= max),\n           \"\\\"%s\\\" expects between %d and %d arg%s, %d supplied\",\n           name, min, max, PLURAL(max), got);\n    return got;\n}\n\nint checkArgsAtLeast(const char* name, int min, int got)\n{\n    MAL_CHECK(got >= min,\n           \"\\\"%s\\\" expects at least %d arg%s, %d supplied\",\n           name, min, PLURAL(min), got);\n    return got;\n}\n\nint checkArgsEven(const char* name, int got)\n{\n    MAL_CHECK(got % 2 == 0,\n           \"\\\"%s\\\" expects an even number of args, %d supplied\",\n           name, got);\n    return got;\n}\n"
  },
  {
    "path": "impls/cpp/Validation.h",
    "content": "#ifndef INCLUDE_VALIDATION_H\n#define INCLUDE_VALIDATION_H\n\n#include \"String.h\"\n\n#define MAL_CHECK(condition, ...)  \\\n    if (!(condition)) { throw STRF(__VA_ARGS__); } else { }\n\n#define MAL_FAIL(...) MAL_CHECK(false, __VA_ARGS__)\n\nextern int checkArgsIs(const char* name, int expected, int got);\nextern int checkArgsBetween(const char* name, int min, int max, int got);\nextern int checkArgsAtLeast(const char* name, int min, int got);\nextern int checkArgsEven(const char* name, int got);\n\n#endif // INCLUDE_VALIDATION_H\n"
  },
  {
    "path": "impls/cpp/docker.sh",
    "content": "#!/usr/bin/env bash\n\nIMAGE_NAME=mal-cpp\nCONTAINER_NAME=mal-cpp-running\n\nrun() {\n    docker rm -f $CONTAINER_NAME > /dev/null 2>/dev/null\n    docker run -v $PWD:/mal -ti --name $CONTAINER_NAME $IMAGE_NAME \"$@\"\n}\n\ncase $1 in\n\n    build)\n        docker build -t $IMAGE_NAME .\n        ;;\n\n    run)\n        shift\n        run \"$@\"\n        ;;\n\n    make)\n        shift\n        run make \"$@\"\n        ;;\n\n    *)\n        echo \"usage: $0 [build|run|make]\"\n        exit 1\n\n        ;;\n\nesac\n\n"
  },
  {
    "path": "impls/cpp/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/cpp/step0_repl.cpp",
    "content": "#include \"String.h\"\n#include \"ReadLine.h\"\n\n#include <iostream>\n#include <memory>\n\nString READ(const String& input);\nString EVAL(const String& ast);\nString PRINT(const String& ast);\nString rep(const String& input);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    while (s_readLine.get(prompt, input)) {\n        std::cout << rep(input) << \"\\n\";\n    }\n    return 0;\n}\n\nString rep(const String& input)\n{\n    return PRINT(EVAL(READ(input)));\n}\n\nString READ(const String& input)\n{\n    return input;\n}\n\nString EVAL(const String& ast)\n{\n    return ast;\n}\n\nString PRINT(const String& ast)\n{\n    return ast;\n}\n"
  },
  {
    "path": "impls/cpp/step1_read_print.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic String rep(const String& input);\nstatic malValuePtr EVAL(malValuePtr ast);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    while (s_readLine.get(prompt, input)) {\n        String out;\n        try {\n            out = rep(input);\n        }\n        catch (malEmptyInputException&) {\n            continue; // no output\n        }\n        catch (String& s) {\n            out = s;\n        };\n        std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nstatic String rep(const String& input)\n{\n    return PRINT(EVAL(READ(input)));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nstatic malValuePtr EVAL(malValuePtr ast)\n{\n    return ast;\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\n// These have been added after step 1 to keep the linker happy.\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr)\n{\n    return ast;\n}\n\nmalValuePtr APPLY(malValuePtr ast, malValueIter, malValueIter)\n{\n    return ast;\n}\n"
  },
  {
    "path": "impls/cpp/step2_eval.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\nstatic malBuiltIn::ApplyFunc\n    builtIn_add, builtIn_sub, builtIn_mul, builtIn_div;\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    malEnvPtr replEnv(new malEnv);\n    replEnv->set(\"+\", mal::builtin(\"+\", &builtIn_add));\n    replEnv->set(\"-\", mal::builtin(\"-\", &builtIn_sub));\n    replEnv->set(\"*\", mal::builtin(\"+\", &builtIn_mul));\n    replEnv->set(\"/\", mal::builtin(\"/\", &builtIn_div));\n    while (s_readLine.get(prompt, input)) {\n        String out;\n        try {\n            out = rep(input, replEnv);\n        }\n        catch (malEmptyInputException&) {\n            continue; // no output\n        }\n        catch (String& s) {\n            out = s;\n        };\n        std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    // std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n\n    return ast->eval(env);\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\n#define ARG(type, name) type* name = VALUE_CAST(type, *argsBegin++)\n\n#define CHECK_ARGS_IS(expected) \\\n    checkArgsIs(name.c_str(), expected, std::distance(argsBegin, argsEnd))\n\n#define CHECK_ARGS_BETWEEN(min, max) \\\n    checkArgsBetween(name.c_str(), min, max, std::distance(argsBegin, argsEnd))\n\n\nstatic malValuePtr builtIn_add(const String& name,\n    malValueIter argsBegin, malValueIter argsEnd)\n{\n        CHECK_ARGS_IS(2);\n        ARG(malInteger, lhs);\n        ARG(malInteger, rhs);\n        return mal::integer(lhs->value() + rhs->value());\n}\n\nstatic malValuePtr builtIn_sub(const String& name,\n    malValueIter argsBegin, malValueIter argsEnd)\n{\n        int argCount = CHECK_ARGS_BETWEEN(1, 2);\n        ARG(malInteger, lhs);\n        if (argCount == 1) {\n            return mal::integer(- lhs->value());\n        }\n        ARG(malInteger, rhs);\n        return mal::integer(lhs->value() - rhs->value());\n}\n\nstatic malValuePtr builtIn_mul(const String& name,\n    malValueIter argsBegin, malValueIter argsEnd)\n{\n        CHECK_ARGS_IS(2);\n        ARG(malInteger, lhs);\n        ARG(malInteger, rhs);\n        return mal::integer(lhs->value() * rhs->value());\n}\n\nstatic malValuePtr builtIn_div(const String& name,\n    malValueIter argsBegin, malValueIter argsEnd)\n{\n        CHECK_ARGS_IS(2);\n        ARG(malInteger, lhs);\n        ARG(malInteger, rhs);\n        MAL_CHECK(rhs->value() != 0, \"Division by zero\"); \\\n        return mal::integer(lhs->value() / rhs->value());\n}\n"
  },
  {
    "path": "impls/cpp/step3_env.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    while (s_readLine.get(prompt, input)) {\n        String out;\n        try {\n            out = rep(input, replEnv);\n        }\n        catch (malEmptyInputException&) {\n            continue; // no output\n        }\n        catch (String& s) {\n            out = s;\n        };\n        std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n\n    const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n    if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n        std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n    }\n\n    const malList* list = DYNAMIC_CAST(malList, ast);\n    if (!list || (list->count() == 0)) {\n        return ast->eval(env);\n    }\n\n    // From here on down we are evaluating a non-empty list.\n    // First handle the special forms.\n    if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n        String special = symbol->value();\n        int argCount = list->count() - 1;\n\n        if (special == \"def!\") {\n            checkArgsIs(\"def!\", 2, argCount);\n            const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n            return env->set(id->value(), EVAL(list->item(2), env));\n        }\n\n        if (special == \"let*\") {\n            checkArgsIs(\"let*\", 2, argCount);\n            const malSequence* bindings =\n                VALUE_CAST(malSequence, list->item(1));\n            int count = checkArgsEven(\"let*\", bindings->count());\n            malEnvPtr inner(new malEnv(env));\n            for (int i = 0; i < count; i += 2) {\n                const malSymbol* var =\n                    VALUE_CAST(malSymbol, bindings->item(i));\n                inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n            }\n            return EVAL(list->item(2), inner);\n        }\n    }\n\n    // Now we're left with the case of a regular list to be evaluated.\n    std::unique_ptr<malValueVec> items(list->evalItems(env));\n    malValuePtr op = items->at(0);\n    return APPLY(op, items->begin()+1, items->end());\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/step4_if_fn_do.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    while (s_readLine.get(prompt, input)) {\n        String out;\n        try {\n            out = rep(input, replEnv);\n        }\n        catch (malEmptyInputException&) {\n            continue; // no output\n        }\n        catch (String& s) {\n            out = s;\n        };\n        std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n\n    const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n    if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n        std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n    }\n\n    const malList* list = DYNAMIC_CAST(malList, ast);\n    if (!list || (list->count() == 0)) {\n        return ast->eval(env);\n    }\n\n    // From here on down we are evaluating a non-empty list.\n    // First handle the special forms.\n    if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n        String special = symbol->value();\n        int argCount = list->count() - 1;\n\n        if (special == \"def!\") {\n            checkArgsIs(\"def!\", 2, argCount);\n            const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n            return env->set(id->value(), EVAL(list->item(2), env));\n        }\n\n        if (special == \"do\") {\n            checkArgsAtLeast(\"do\", 1, argCount);\n\n            for (int i = 1; i < argCount; i++) {\n                EVAL(list->item(i), env);\n            }\n            return EVAL(list->item(argCount), env);\n        }\n\n        if (special == \"fn*\") {\n            checkArgsIs(\"fn*\", 2, argCount);\n\n            const malSequence* bindings =\n                VALUE_CAST(malSequence, list->item(1));\n            StringVec params;\n            for (int i = 0; i < bindings->count(); i++) {\n                const malSymbol* sym =\n                    VALUE_CAST(malSymbol, bindings->item(i));\n                params.push_back(sym->value());\n            }\n\n            return mal::lambda(params, list->item(2), env);\n        }\n\n        if (special == \"if\") {\n            checkArgsBetween(\"if\", 2, 3, argCount);\n\n            bool isTrue = EVAL(list->item(1), env)->isTrue();\n            if (!isTrue && (argCount == 2)) {\n                return mal::nilValue();\n            }\n            return EVAL(list->item(isTrue ? 2 : 3), env);\n        }\n\n        if (special == \"let*\") {\n            checkArgsIs(\"let*\", 2, argCount);\n            const malSequence* bindings =\n                VALUE_CAST(malSequence, list->item(1));\n            int count = checkArgsEven(\"let*\", bindings->count());\n            malEnvPtr inner(new malEnv(env));\n            for (int i = 0; i < count; i += 2) {\n                const malSymbol* var =\n                    VALUE_CAST(malSymbol, bindings->item(i));\n                inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n            }\n            return EVAL(list->item(2), inner);\n        }\n    }\n\n    // Now we're left with the case of a regular list to be evaluated.\n    std::unique_ptr<malValueVec> items(list->evalItems(env));\n    malValuePtr op = items->at(0);\n    if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n        return EVAL(lambda->getBody(),\n                    lambda->makeEnv(items->begin()+1, items->end()));\n    }\n    else {\n        return APPLY(op, items->begin()+1, items->end());\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(def! not (fn* (cond) (if cond false true)))\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/step5_tco.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    while (s_readLine.get(prompt, input)) {\n        String out;\n        try {\n            out = rep(input, replEnv);\n        }\n        catch (malEmptyInputException&) {\n            continue; // no output\n        }\n        catch (String& s) {\n            out = s;\n        };\n        std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n    while (1) {\n\n       const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n       if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n           std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n       }\n\n        const malList* list = DYNAMIC_CAST(malList, ast);\n        if (!list || (list->count() == 0)) {\n            return ast->eval(env);\n        }\n\n        // From here on down we are evaluating a non-empty list.\n        // First handle the special forms.\n        if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n            String special = symbol->value();\n            int argCount = list->count() - 1;\n\n            if (special == \"def!\") {\n                checkArgsIs(\"def!\", 2, argCount);\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                return env->set(id->value(), EVAL(list->item(2), env));\n            }\n\n            if (special == \"do\") {\n                checkArgsAtLeast(\"do\", 1, argCount);\n\n                for (int i = 1; i < argCount; i++) {\n                    EVAL(list->item(i), env);\n                }\n                ast = list->item(argCount);\n                continue; // TCO\n            }\n\n            if (special == \"fn*\") {\n                checkArgsIs(\"fn*\", 2, argCount);\n\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                StringVec params;\n                for (int i = 0; i < bindings->count(); i++) {\n                    const malSymbol* sym =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    params.push_back(sym->value());\n                }\n\n                return mal::lambda(params, list->item(2), env);\n            }\n\n            if (special == \"if\") {\n                checkArgsBetween(\"if\", 2, 3, argCount);\n\n                bool isTrue = EVAL(list->item(1), env)->isTrue();\n                if (!isTrue && (argCount == 2)) {\n                    return mal::nilValue();\n                }\n                ast = list->item(isTrue ? 2 : 3);\n                continue; // TCO\n            }\n\n            if (special == \"let*\") {\n                checkArgsIs(\"let*\", 2, argCount);\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                int count = checkArgsEven(\"let*\", bindings->count());\n                malEnvPtr inner(new malEnv(env));\n                for (int i = 0; i < count; i += 2) {\n                    const malSymbol* var =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n                }\n                ast = list->item(2);\n                env = inner;\n                continue; // TCO\n            }\n        }\n\n        // Now we're left with the case of a regular list to be evaluated.\n        std::unique_ptr<malValueVec> items(list->evalItems(env));\n        malValuePtr op = items->at(0);\n        if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n            ast = lambda->getBody();\n            env = lambda->makeEnv(items->begin()+1, items->end());\n            continue; // TCO\n        }\n        else {\n            return APPLY(op, items->begin()+1, items->end());\n        }\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(def! not (fn* (cond) (if cond false true)))\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/step6_file.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[]);\nstatic String safeRep(const String& input, malEnvPtr env);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    makeArgv(replEnv, argc - 2, argv + 2);\n    if (argc > 1) {\n        String filename = escape(argv[1]);\n        safeRep(STRF(\"(load-file %s)\", filename.c_str()), replEnv);\n        return 0;\n    }\n    while (s_readLine.get(prompt, input)) {\n        String out = safeRep(input, replEnv);\n        if (out.length() > 0)\n            std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nstatic String safeRep(const String& input, malEnvPtr env)\n{\n    try {\n        return rep(input, env);\n    }\n    catch (malEmptyInputException&) {\n        return String();\n    }\n    catch (String& s) {\n        return s;\n    };\n}\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[])\n{\n    malValueVec* args = new malValueVec();\n    for (int i = 0; i < argc; i++) {\n        args->push_back(mal::string(argv[i]));\n    }\n    env->set(\"*ARGV*\", mal::list(args));\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n    while (1) {\n\n       const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n       if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n           std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n       }\n\n        const malList* list = DYNAMIC_CAST(malList, ast);\n        if (!list || (list->count() == 0)) {\n            return ast->eval(env);\n        }\n\n        // From here on down we are evaluating a non-empty list.\n        // First handle the special forms.\n        if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n            String special = symbol->value();\n            int argCount = list->count() - 1;\n\n            if (special == \"def!\") {\n                checkArgsIs(\"def!\", 2, argCount);\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                return env->set(id->value(), EVAL(list->item(2), env));\n            }\n\n            if (special == \"do\") {\n                checkArgsAtLeast(\"do\", 1, argCount);\n\n                for (int i = 1; i < argCount; i++) {\n                    EVAL(list->item(i), env);\n                }\n                ast = list->item(argCount);\n                continue; // TCO\n            }\n\n            if (special == \"fn*\") {\n                checkArgsIs(\"fn*\", 2, argCount);\n\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                StringVec params;\n                for (int i = 0; i < bindings->count(); i++) {\n                    const malSymbol* sym =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    params.push_back(sym->value());\n                }\n\n                return mal::lambda(params, list->item(2), env);\n            }\n\n            if (special == \"if\") {\n                checkArgsBetween(\"if\", 2, 3, argCount);\n\n                bool isTrue = EVAL(list->item(1), env)->isTrue();\n                if (!isTrue && (argCount == 2)) {\n                    return mal::nilValue();\n                }\n                ast = list->item(isTrue ? 2 : 3);\n                continue; // TCO\n            }\n\n            if (special == \"let*\") {\n                checkArgsIs(\"let*\", 2, argCount);\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                int count = checkArgsEven(\"let*\", bindings->count());\n                malEnvPtr inner(new malEnv(env));\n                for (int i = 0; i < count; i += 2) {\n                    const malSymbol* var =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n                }\n                ast = list->item(2);\n                env = inner;\n                continue; // TCO\n            }\n        }\n\n        // Now we're left with the case of a regular list to be evaluated.\n        std::unique_ptr<malValueVec> items(list->evalItems(env));\n        malValuePtr op = items->at(0);\n        if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n            ast = lambda->getBody();\n            env = lambda->makeEnv(items->begin()+1, items->end());\n            continue; // TCO\n        }\n        else {\n            return APPLY(op, items->begin()+1, items->end());\n        }\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(def! not (fn* (cond) (if cond false true)))\",\n    \"(def! load-file (fn* (filename) \\\n        (eval (read-string (str \\\"(do \\\" (slurp filename) \\\"\\nnil)\\\")))))\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/step7_quote.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[]);\nstatic String safeRep(const String& input, malEnvPtr env);\nstatic malValuePtr quasiquote(malValuePtr obj);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    makeArgv(replEnv, argc - 2, argv + 2);\n    if (argc > 1) {\n        String filename = escape(argv[1]);\n        safeRep(STRF(\"(load-file %s)\", filename.c_str()), replEnv);\n        return 0;\n    }\n    while (s_readLine.get(prompt, input)) {\n        String out = safeRep(input, replEnv);\n        if (out.length() > 0)\n            std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nstatic String safeRep(const String& input, malEnvPtr env)\n{\n    try {\n        return rep(input, env);\n    }\n    catch (malEmptyInputException&) {\n        return String();\n    }\n    catch (String& s) {\n        return s;\n    };\n}\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[])\n{\n    malValueVec* args = new malValueVec();\n    for (int i = 0; i < argc; i++) {\n        args->push_back(mal::string(argv[i]));\n    }\n    env->set(\"*ARGV*\", mal::list(args));\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n    while (1) {\n\n       const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n       if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n           std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n       }\n\n        const malList* list = DYNAMIC_CAST(malList, ast);\n        if (!list || (list->count() == 0)) {\n            return ast->eval(env);\n        }\n\n        // From here on down we are evaluating a non-empty list.\n        // First handle the special forms.\n        if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n            String special = symbol->value();\n            int argCount = list->count() - 1;\n\n            if (special == \"def!\") {\n                checkArgsIs(\"def!\", 2, argCount);\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                return env->set(id->value(), EVAL(list->item(2), env));\n            }\n\n            if (special == \"do\") {\n                checkArgsAtLeast(\"do\", 1, argCount);\n\n                for (int i = 1; i < argCount; i++) {\n                    EVAL(list->item(i), env);\n                }\n                ast = list->item(argCount);\n                continue; // TCO\n            }\n\n            if (special == \"fn*\") {\n                checkArgsIs(\"fn*\", 2, argCount);\n\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                StringVec params;\n                for (int i = 0; i < bindings->count(); i++) {\n                    const malSymbol* sym =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    params.push_back(sym->value());\n                }\n\n                return mal::lambda(params, list->item(2), env);\n            }\n\n            if (special == \"if\") {\n                checkArgsBetween(\"if\", 2, 3, argCount);\n\n                bool isTrue = EVAL(list->item(1), env)->isTrue();\n                if (!isTrue && (argCount == 2)) {\n                    return mal::nilValue();\n                }\n                ast = list->item(isTrue ? 2 : 3);\n                continue; // TCO\n            }\n\n            if (special == \"let*\") {\n                checkArgsIs(\"let*\", 2, argCount);\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                int count = checkArgsEven(\"let*\", bindings->count());\n                malEnvPtr inner(new malEnv(env));\n                for (int i = 0; i < count; i += 2) {\n                    const malSymbol* var =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n                }\n                ast = list->item(2);\n                env = inner;\n                continue; // TCO\n            }\n\n            if (special == \"quasiquote\") {\n                checkArgsIs(\"quasiquote\", 1, argCount);\n                ast = quasiquote(list->item(1));\n                continue; // TCO\n            }\n\n            if (special == \"quote\") {\n                checkArgsIs(\"quote\", 1, argCount);\n                return list->item(1);\n            }\n        }\n\n        // Now we're left with the case of a regular list to be evaluated.\n        std::unique_ptr<malValueVec> items(list->evalItems(env));\n        malValuePtr op = items->at(0);\n        if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n            ast = lambda->getBody();\n            env = lambda->makeEnv(items->begin()+1, items->end());\n            continue; // TCO\n        }\n        else {\n            return APPLY(op, items->begin()+1, items->end());\n        }\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic bool isSymbol(malValuePtr obj, const String& text)\n{\n    const malSymbol* sym = DYNAMIC_CAST(malSymbol, obj);\n    return sym && (sym->value() == text);\n}\n\n//  Return arg when ast matches ('sym, arg), else NULL.\nstatic malValuePtr starts_with(const malValuePtr ast, const char* sym)\n{\n    const malList* list = DYNAMIC_CAST(malList, ast);\n    if (!list || list->isEmpty() || !isSymbol(list->item(0), sym))\n        return NULL;\n    checkArgsIs(sym, 1, list->count() - 1);\n    return list->item(1);\n}\n\nstatic malValuePtr quasiquote(malValuePtr obj)\n{\n    if (DYNAMIC_CAST(malSymbol, obj) || DYNAMIC_CAST(malHash, obj))\n        return mal::list(mal::symbol(\"quote\"), obj);\n\n    const malSequence* seq = DYNAMIC_CAST(malSequence, obj);\n    if (!seq)\n        return obj;\n\n    const malValuePtr unquoted = starts_with(obj, \"unquote\");\n    if (unquoted)\n        return unquoted;\n\n    malValuePtr res = mal::list(new malValueVec(0));\n    for (int i=seq->count()-1; 0<=i; i--) {\n        const malValuePtr elt     = seq->item(i);\n        const malValuePtr spl_unq = starts_with(elt, \"splice-unquote\");\n        if (spl_unq)\n            res = mal::list(mal::symbol(\"concat\"), spl_unq, res);\n         else\n            res = mal::list(mal::symbol(\"cons\"), quasiquote(elt), res);\n    }\n    if (DYNAMIC_CAST(malVector, obj))\n        res = mal::list(mal::symbol(\"vec\"), res);\n    return res;\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(def! not (fn* (cond) (if cond false true)))\",\n    \"(def! load-file (fn* (filename) \\\n        (eval (read-string (str \\\"(do \\\" (slurp filename) \\\"\\nnil)\\\")))))\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/step8_macros.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n//  Installs functions and macros implemented in MAL.\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[]);\nstatic String safeRep(const String& input, malEnvPtr env);\nstatic malValuePtr quasiquote(malValuePtr obj);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    makeArgv(replEnv, argc - 2, argv + 2);\n    if (argc > 1) {\n        String filename = escape(argv[1]);\n        safeRep(STRF(\"(load-file %s)\", filename.c_str()), replEnv);\n        return 0;\n    }\n    while (s_readLine.get(prompt, input)) {\n        String out = safeRep(input, replEnv);\n        if (out.length() > 0)\n            std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nstatic String safeRep(const String& input, malEnvPtr env)\n{\n    try {\n        return rep(input, env);\n    }\n    catch (malEmptyInputException&) {\n        return String();\n    }\n    catch (String& s) {\n        return s;\n    };\n}\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[])\n{\n    malValueVec* args = new malValueVec();\n    for (int i = 0; i < argc; i++) {\n        args->push_back(mal::string(argv[i]));\n    }\n    env->set(\"*ARGV*\", mal::list(args));\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n    while (1) {\n\n       const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n       if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n           std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n       }\n\n        const malList* list = DYNAMIC_CAST(malList, ast);\n        if (!list || (list->count() == 0)) {\n            return ast->eval(env);\n        }\n\n        // From here on down we are evaluating a non-empty list.\n        // First handle the special forms.\n        if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n            String special = symbol->value();\n            int argCount = list->count() - 1;\n\n            if (special == \"def!\") {\n                checkArgsIs(\"def!\", 2, argCount);\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                return env->set(id->value(), EVAL(list->item(2), env));\n            }\n\n            if (special == \"defmacro!\") {\n                checkArgsIs(\"defmacro!\", 2, argCount);\n\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                malValuePtr body = EVAL(list->item(2), env);\n                const malLambda* lambda = VALUE_CAST(malLambda, body);\n                return env->set(id->value(), mal::macro(*lambda));\n            }\n\n            if (special == \"do\") {\n                checkArgsAtLeast(\"do\", 1, argCount);\n\n                for (int i = 1; i < argCount; i++) {\n                    EVAL(list->item(i), env);\n                }\n                ast = list->item(argCount);\n                continue; // TCO\n            }\n\n            if (special == \"fn*\") {\n                checkArgsIs(\"fn*\", 2, argCount);\n\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                StringVec params;\n                for (int i = 0; i < bindings->count(); i++) {\n                    const malSymbol* sym =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    params.push_back(sym->value());\n                }\n\n                return mal::lambda(params, list->item(2), env);\n            }\n\n            if (special == \"if\") {\n                checkArgsBetween(\"if\", 2, 3, argCount);\n\n                bool isTrue = EVAL(list->item(1), env)->isTrue();\n                if (!isTrue && (argCount == 2)) {\n                    return mal::nilValue();\n                }\n                ast = list->item(isTrue ? 2 : 3);\n                continue; // TCO\n            }\n\n            if (special == \"let*\") {\n                checkArgsIs(\"let*\", 2, argCount);\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                int count = checkArgsEven(\"let*\", bindings->count());\n                malEnvPtr inner(new malEnv(env));\n                for (int i = 0; i < count; i += 2) {\n                    const malSymbol* var =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n                }\n                ast = list->item(2);\n                env = inner;\n                continue; // TCO\n            }\n\n            if (special == \"quasiquote\") {\n                checkArgsIs(\"quasiquote\", 1, argCount);\n                ast = quasiquote(list->item(1));\n                continue; // TCO\n            }\n\n            if (special == \"quote\") {\n                checkArgsIs(\"quote\", 1, argCount);\n                return list->item(1);\n            }\n        }\n\n        // Now we're left with the case of a regular list to be evaluated.\n        malValuePtr op = EVAL(list->item(0), env);\n        if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n            if (lambda->isMacro()) {\n                ast = lambda->apply(list->begin()+1, list->end());\n                continue; // TCO\n            }\n            malValueVec* items = STATIC_CAST(malList, list->rest())->evalItems(env);\n            ast = lambda->getBody();\n            env = lambda->makeEnv(items->begin(), items->end());\n            continue; // TCO\n        }\n        else {\n            malValueVec* items = STATIC_CAST(malList, list->rest())->evalItems(env);\n            return APPLY(op, items->begin(), items->end());\n        }\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic bool isSymbol(malValuePtr obj, const String& text)\n{\n    const malSymbol* sym = DYNAMIC_CAST(malSymbol, obj);\n    return sym && (sym->value() == text);\n}\n\n//  Return arg when ast matches ('sym, arg), else NULL.\nstatic malValuePtr starts_with(const malValuePtr ast, const char* sym)\n{\n    const malList* list = DYNAMIC_CAST(malList, ast);\n    if (!list || list->isEmpty() || !isSymbol(list->item(0), sym))\n        return NULL;\n    checkArgsIs(sym, 1, list->count() - 1);\n    return list->item(1);\n}\n\nstatic malValuePtr quasiquote(malValuePtr obj)\n{\n    if (DYNAMIC_CAST(malSymbol, obj) || DYNAMIC_CAST(malHash, obj))\n        return mal::list(mal::symbol(\"quote\"), obj);\n\n    const malSequence* seq = DYNAMIC_CAST(malSequence, obj);\n    if (!seq)\n        return obj;\n\n    const malValuePtr unquoted = starts_with(obj, \"unquote\");\n    if (unquoted)\n        return unquoted;\n\n    malValuePtr res = mal::list(new malValueVec(0));\n    for (int i=seq->count()-1; 0<=i; i--) {\n        const malValuePtr elt     = seq->item(i);\n        const malValuePtr spl_unq = starts_with(elt, \"splice-unquote\");\n        if (spl_unq)\n            res = mal::list(mal::symbol(\"concat\"), spl_unq, res);\n         else\n            res = mal::list(mal::symbol(\"cons\"), quasiquote(elt), res);\n    }\n    if (DYNAMIC_CAST(malVector, obj))\n        res = mal::list(mal::symbol(\"vec\"), res);\n    return res;\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\",\n    \"(def! not (fn* (cond) (if cond false true)))\",\n    \"(def! load-file (fn* (filename) \\\n        (eval (read-string (str \\\"(do \\\" (slurp filename) \\\"\\nnil)\\\")))))\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/step9_try.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n//  Installs functions and macros implemented in MAL.\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[]);\nstatic String safeRep(const String& input, malEnvPtr env);\nstatic malValuePtr quasiquote(malValuePtr obj);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    makeArgv(replEnv, argc - 2, argv + 2);\n    if (argc > 1) {\n        String filename = escape(argv[1]);\n        safeRep(STRF(\"(load-file %s)\", filename.c_str()), replEnv);\n        return 0;\n    }\n    while (s_readLine.get(prompt, input)) {\n        String out = safeRep(input, replEnv);\n        if (out.length() > 0)\n            std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nstatic String safeRep(const String& input, malEnvPtr env)\n{\n    try {\n        return rep(input, env);\n    }\n    catch (malEmptyInputException&) {\n        return String();\n    }\n    catch (malValuePtr& mv) {\n        return \"Error: \" + mv->print(true);\n    }\n    catch (String& s) {\n        return \"Error: \" + s;\n    };\n}\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[])\n{\n    malValueVec* args = new malValueVec();\n    for (int i = 0; i < argc; i++) {\n        args->push_back(mal::string(argv[i]));\n    }\n    env->set(\"*ARGV*\", mal::list(args));\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n    while (1) {\n\n       const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n       if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n           std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n       }\n\n        const malList* list = DYNAMIC_CAST(malList, ast);\n        if (!list || (list->count() == 0)) {\n            return ast->eval(env);\n        }\n\n        // From here on down we are evaluating a non-empty list.\n        // First handle the special forms.\n        if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n            String special = symbol->value();\n            int argCount = list->count() - 1;\n\n            if (special == \"def!\") {\n                checkArgsIs(\"def!\", 2, argCount);\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                return env->set(id->value(), EVAL(list->item(2), env));\n            }\n\n            if (special == \"defmacro!\") {\n                checkArgsIs(\"defmacro!\", 2, argCount);\n\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                malValuePtr body = EVAL(list->item(2), env);\n                const malLambda* lambda = VALUE_CAST(malLambda, body);\n                return env->set(id->value(), mal::macro(*lambda));\n            }\n\n            if (special == \"do\") {\n                checkArgsAtLeast(\"do\", 1, argCount);\n\n                for (int i = 1; i < argCount; i++) {\n                    EVAL(list->item(i), env);\n                }\n                ast = list->item(argCount);\n                continue; // TCO\n            }\n\n            if (special == \"fn*\") {\n                checkArgsIs(\"fn*\", 2, argCount);\n\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                StringVec params;\n                for (int i = 0; i < bindings->count(); i++) {\n                    const malSymbol* sym =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    params.push_back(sym->value());\n                }\n\n                return mal::lambda(params, list->item(2), env);\n            }\n\n            if (special == \"if\") {\n                checkArgsBetween(\"if\", 2, 3, argCount);\n\n                bool isTrue = EVAL(list->item(1), env)->isTrue();\n                if (!isTrue && (argCount == 2)) {\n                    return mal::nilValue();\n                }\n                ast = list->item(isTrue ? 2 : 3);\n                continue; // TCO\n            }\n\n            if (special == \"let*\") {\n                checkArgsIs(\"let*\", 2, argCount);\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                int count = checkArgsEven(\"let*\", bindings->count());\n                malEnvPtr inner(new malEnv(env));\n                for (int i = 0; i < count; i += 2) {\n                    const malSymbol* var =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n                }\n                ast = list->item(2);\n                env = inner;\n                continue; // TCO\n            }\n\n            if (special == \"quasiquote\") {\n                checkArgsIs(\"quasiquote\", 1, argCount);\n                ast = quasiquote(list->item(1));\n                continue; // TCO\n            }\n\n            if (special == \"quote\") {\n                checkArgsIs(\"quote\", 1, argCount);\n                return list->item(1);\n            }\n\n            if (special == \"try*\") {\n                malValuePtr tryBody = list->item(1);\n\n                if (argCount == 1) {\n                    ast = tryBody;\n                    continue; // TCO\n                }\n                checkArgsIs(\"try*\", 2, argCount);\n                const malList* catchBlock = VALUE_CAST(malList, list->item(2));\n\n                checkArgsIs(\"catch*\", 2, catchBlock->count() - 1);\n                MAL_CHECK(VALUE_CAST(malSymbol,\n                    catchBlock->item(0))->value() == \"catch*\",\n                    \"catch block must begin with catch*\");\n\n                // We don't need excSym at this scope, but we want to check\n                // that the catch block is valid always, not just in case of\n                // an exception.\n                const malSymbol* excSym =\n                    VALUE_CAST(malSymbol, catchBlock->item(1));\n\n                malValuePtr excVal;\n\n                try {\n                    return EVAL(tryBody, env);\n                }\n                catch(String& s) {\n                    excVal = mal::string(s);\n                }\n                catch (malEmptyInputException&) {\n                    // Not an error, continue as if we got nil\n                    ast = mal::nilValue();\n                }\n                catch(malValuePtr& o) {\n                    excVal = o;\n                };\n\n                if (excVal) {\n                    // we got some exception\n                    env = malEnvPtr(new malEnv(env));\n                    env->set(excSym->value(), excVal);\n                    ast = catchBlock->item(2);\n                }\n                continue; // TCO\n            }\n        }\n\n        // Now we're left with the case of a regular list to be evaluated.\n        malValuePtr op = EVAL(list->item(0), env);\n        if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n            if (lambda->isMacro()) {\n                ast = lambda->apply(list->begin()+1, list->end());\n                continue; // TCO\n            }\n            malValueVec* items = STATIC_CAST(malList, list->rest())->evalItems(env);\n            ast = lambda->getBody();\n            env = lambda->makeEnv(items->begin(), items->end());\n            continue; // TCO\n        }\n        else {\n            malValueVec* items = STATIC_CAST(malList, list->rest())->evalItems(env);\n            return APPLY(op, items->begin(), items->end());\n        }\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic bool isSymbol(malValuePtr obj, const String& text)\n{\n    const malSymbol* sym = DYNAMIC_CAST(malSymbol, obj);\n    return sym && (sym->value() == text);\n}\n\n//  Return arg when ast matches ('sym, arg), else NULL.\nstatic malValuePtr starts_with(const malValuePtr ast, const char* sym)\n{\n    const malList* list = DYNAMIC_CAST(malList, ast);\n    if (!list || list->isEmpty() || !isSymbol(list->item(0), sym))\n        return NULL;\n    checkArgsIs(sym, 1, list->count() - 1);\n    return list->item(1);\n}\n\nstatic malValuePtr quasiquote(malValuePtr obj)\n{\n    if (DYNAMIC_CAST(malSymbol, obj) || DYNAMIC_CAST(malHash, obj))\n        return mal::list(mal::symbol(\"quote\"), obj);\n\n    const malSequence* seq = DYNAMIC_CAST(malSequence, obj);\n    if (!seq)\n        return obj;\n\n    const malValuePtr unquoted = starts_with(obj, \"unquote\");\n    if (unquoted)\n        return unquoted;\n\n    malValuePtr res = mal::list(new malValueVec(0));\n    for (int i=seq->count()-1; 0<=i; i--) {\n        const malValuePtr elt     = seq->item(i);\n        const malValuePtr spl_unq = starts_with(elt, \"splice-unquote\");\n        if (spl_unq)\n            res = mal::list(mal::symbol(\"concat\"), spl_unq, res);\n         else\n            res = mal::list(mal::symbol(\"cons\"), quasiquote(elt), res);\n    }\n    if (DYNAMIC_CAST(malVector, obj))\n        res = mal::list(mal::symbol(\"vec\"), res);\n    return res;\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\",\n    \"(def! not (fn* (cond) (if cond false true)))\",\n    \"(def! load-file (fn* (filename) \\\n        (eval (read-string (str \\\"(do \\\" (slurp filename) \\\"\\nnil)\\\")))))\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/stepA_mal.cpp",
    "content": "#include \"MAL.h\"\n\n#include \"Environment.h\"\n#include \"ReadLine.h\"\n#include \"Types.h\"\n\n#include <iostream>\n#include <memory>\n\nmalValuePtr READ(const String& input);\nString PRINT(malValuePtr ast);\nstatic void installFunctions(malEnvPtr env);\n//  Installs functions, macros and constants implemented in MAL.\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[]);\nstatic String safeRep(const String& input, malEnvPtr env);\nstatic malValuePtr quasiquote(malValuePtr obj);\n\nstatic ReadLine s_readLine(\"~/.mal-history\");\n\nstatic malEnvPtr replEnv(new malEnv);\n\nint main(int argc, char* argv[])\n{\n    String prompt = \"user> \";\n    String input;\n    installCore(replEnv);\n    installFunctions(replEnv);\n    makeArgv(replEnv, argc - 2, argv + 2);\n    if (argc > 1) {\n        String filename = escape(argv[1]);\n        safeRep(STRF(\"(load-file %s)\", filename.c_str()), replEnv);\n        return 0;\n    }\n    rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", replEnv);\n    while (s_readLine.get(prompt, input)) {\n        String out = safeRep(input, replEnv);\n        if (out.length() > 0)\n            std::cout << out << \"\\n\";\n    }\n    return 0;\n}\n\nstatic String safeRep(const String& input, malEnvPtr env)\n{\n    try {\n        return rep(input, env);\n    }\n    catch (malEmptyInputException&) {\n        return String();\n    }\n    catch (malValuePtr& mv) {\n        return \"Error: \" + mv->print(true);\n    }\n    catch (String& s) {\n        return \"Error: \" + s;\n    };\n}\n\nstatic void makeArgv(malEnvPtr env, int argc, char* argv[])\n{\n    malValueVec* args = new malValueVec();\n    for (int i = 0; i < argc; i++) {\n        args->push_back(mal::string(argv[i]));\n    }\n    env->set(\"*ARGV*\", mal::list(args));\n}\n\nString rep(const String& input, malEnvPtr env)\n{\n    return PRINT(EVAL(READ(input), env));\n}\n\nmalValuePtr READ(const String& input)\n{\n    return readStr(input);\n}\n\nmalValuePtr EVAL(malValuePtr ast, malEnvPtr env)\n{\n    if (!env) {\n        env = replEnv;\n    }\n    while (1) {\n\n       const malEnvPtr dbgenv = env->find(\"DEBUG-EVAL\");\n       if (dbgenv && dbgenv->get(\"DEBUG-EVAL\")->isTrue()) {\n           std::cout << \"EVAL: \" << PRINT(ast) << \"\\n\";\n       }\n\n        const malList* list = DYNAMIC_CAST(malList, ast);\n        if (!list || (list->count() == 0)) {\n            return ast->eval(env);\n        }\n\n        // From here on down we are evaluating a non-empty list.\n        // First handle the special forms.\n        if (const malSymbol* symbol = DYNAMIC_CAST(malSymbol, list->item(0))) {\n            String special = symbol->value();\n            int argCount = list->count() - 1;\n\n            if (special == \"def!\") {\n                checkArgsIs(\"def!\", 2, argCount);\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                return env->set(id->value(), EVAL(list->item(2), env));\n            }\n\n            if (special == \"defmacro!\") {\n                checkArgsIs(\"defmacro!\", 2, argCount);\n\n                const malSymbol* id = VALUE_CAST(malSymbol, list->item(1));\n                malValuePtr body = EVAL(list->item(2), env);\n                const malLambda* lambda = VALUE_CAST(malLambda, body);\n                return env->set(id->value(), mal::macro(*lambda));\n            }\n\n            if (special == \"do\") {\n                checkArgsAtLeast(\"do\", 1, argCount);\n\n                for (int i = 1; i < argCount; i++) {\n                    EVAL(list->item(i), env);\n                }\n                ast = list->item(argCount);\n                continue; // TCO\n            }\n\n            if (special == \"fn*\") {\n                checkArgsIs(\"fn*\", 2, argCount);\n\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                StringVec params;\n                for (int i = 0; i < bindings->count(); i++) {\n                    const malSymbol* sym =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    params.push_back(sym->value());\n                }\n\n                return mal::lambda(params, list->item(2), env);\n            }\n\n            if (special == \"if\") {\n                checkArgsBetween(\"if\", 2, 3, argCount);\n\n                bool isTrue = EVAL(list->item(1), env)->isTrue();\n                if (!isTrue && (argCount == 2)) {\n                    return mal::nilValue();\n                }\n                ast = list->item(isTrue ? 2 : 3);\n                continue; // TCO\n            }\n\n            if (special == \"let*\") {\n                checkArgsIs(\"let*\", 2, argCount);\n                const malSequence* bindings =\n                    VALUE_CAST(malSequence, list->item(1));\n                int count = checkArgsEven(\"let*\", bindings->count());\n                malEnvPtr inner(new malEnv(env));\n                for (int i = 0; i < count; i += 2) {\n                    const malSymbol* var =\n                        VALUE_CAST(malSymbol, bindings->item(i));\n                    inner->set(var->value(), EVAL(bindings->item(i+1), inner));\n                }\n                ast = list->item(2);\n                env = inner;\n                continue; // TCO\n            }\n\n            if (special == \"quasiquote\") {\n                checkArgsIs(\"quasiquote\", 1, argCount);\n                ast = quasiquote(list->item(1));\n                continue; // TCO\n            }\n\n            if (special == \"quote\") {\n                checkArgsIs(\"quote\", 1, argCount);\n                return list->item(1);\n            }\n\n            if (special == \"try*\") {\n                malValuePtr tryBody = list->item(1);\n\n                if (argCount == 1) {\n                    ast = tryBody;\n                    continue; // TCO\n                }\n                checkArgsIs(\"try*\", 2, argCount);\n                const malList* catchBlock = VALUE_CAST(malList, list->item(2));\n\n                checkArgsIs(\"catch*\", 2, catchBlock->count() - 1);\n                MAL_CHECK(VALUE_CAST(malSymbol,\n                    catchBlock->item(0))->value() == \"catch*\",\n                    \"catch block must begin with catch*\");\n\n                // We don't need excSym at this scope, but we want to check\n                // that the catch block is valid always, not just in case of\n                // an exception.\n                const malSymbol* excSym =\n                    VALUE_CAST(malSymbol, catchBlock->item(1));\n\n                malValuePtr excVal;\n\n                try {\n                    return EVAL(tryBody, env);\n                }\n                catch(String& s) {\n                    excVal = mal::string(s);\n                }\n                catch (malEmptyInputException&) {\n                    // Not an error, continue as if we got nil\n                    ast = mal::nilValue();\n                }\n                catch(malValuePtr& o) {\n                    excVal = o;\n                };\n\n                if (excVal) {\n                    // we got some exception\n                    env = malEnvPtr(new malEnv(env));\n                    env->set(excSym->value(), excVal);\n                    ast = catchBlock->item(2);\n                }\n                continue; // TCO\n            }\n        }\n\n        // Now we're left with the case of a regular list to be evaluated.\n        malValuePtr op = EVAL(list->item(0), env);\n        if (const malLambda* lambda = DYNAMIC_CAST(malLambda, op)) {\n            if (lambda->isMacro()) {\n                ast = lambda->apply(list->begin()+1, list->end());\n                continue; // TCO\n            }\n            malValueVec* items = STATIC_CAST(malList, list->rest())->evalItems(env);\n            ast = lambda->getBody();\n            env = lambda->makeEnv(items->begin(), items->end());\n            continue; // TCO\n        }\n        else {\n            malValueVec* items = STATIC_CAST(malList, list->rest())->evalItems(env);\n            return APPLY(op, items->begin(), items->end());\n        }\n    }\n}\n\nString PRINT(malValuePtr ast)\n{\n    return ast->print(true);\n}\n\nmalValuePtr APPLY(malValuePtr op, malValueIter argsBegin, malValueIter argsEnd)\n{\n    const malApplicable* handler = DYNAMIC_CAST(malApplicable, op);\n    MAL_CHECK(handler != NULL,\n              \"\\\"%s\\\" is not applicable\", op->print(true).c_str());\n\n    return handler->apply(argsBegin, argsEnd);\n}\n\nstatic bool isSymbol(malValuePtr obj, const String& text)\n{\n    const malSymbol* sym = DYNAMIC_CAST(malSymbol, obj);\n    return sym && (sym->value() == text);\n}\n\n//  Return arg when ast matches ('sym, arg), else NULL.\nstatic malValuePtr starts_with(const malValuePtr ast, const char* sym)\n{\n    const malList* list = DYNAMIC_CAST(malList, ast);\n    if (!list || list->isEmpty() || !isSymbol(list->item(0), sym))\n        return NULL;\n    checkArgsIs(sym, 1, list->count() - 1);\n    return list->item(1);\n}\n\nstatic malValuePtr quasiquote(malValuePtr obj)\n{\n    if (DYNAMIC_CAST(malSymbol, obj) || DYNAMIC_CAST(malHash, obj))\n        return mal::list(mal::symbol(\"quote\"), obj);\n\n    const malSequence* seq = DYNAMIC_CAST(malSequence, obj);\n    if (!seq)\n        return obj;\n\n    const malValuePtr unquoted = starts_with(obj, \"unquote\");\n    if (unquoted)\n        return unquoted;\n\n    malValuePtr res = mal::list(new malValueVec(0));\n    for (int i=seq->count()-1; 0<=i; i--) {\n        const malValuePtr elt     = seq->item(i);\n        const malValuePtr spl_unq = starts_with(elt, \"splice-unquote\");\n        if (spl_unq)\n            res = mal::list(mal::symbol(\"concat\"), spl_unq, res);\n         else\n            res = mal::list(mal::symbol(\"cons\"), quasiquote(elt), res);\n    }\n    if (DYNAMIC_CAST(malVector, obj))\n        res = mal::list(mal::symbol(\"vec\"), res);\n    return res;\n}\n\nstatic const char* malFunctionTable[] = {\n    \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\",\n    \"(def! not (fn* (cond) (if cond false true)))\",\n    \"(def! load-file (fn* (filename) \\\n        (eval (read-string (str \\\"(do \\\" (slurp filename) \\\"\\nnil)\\\")))))\",\n    \"(def! *host-language* \\\"C++\\\")\",\n};\n\nstatic void installFunctions(malEnvPtr env) {\n    for (auto &function : malFunctionTable) {\n        rep(function, env);\n    }\n}\n\n// Added to keep the linker happy at step A\nmalValuePtr readline(const String& prompt)\n{\n    String input;\n    if (s_readLine.get(prompt, input)) {\n        return mal::string(input);\n    }\n    return mal::nilValue();\n}\n\n"
  },
  {
    "path": "impls/cpp/tests/step5_tco.mal",
    "content": ";; C++: skipping non-TCO recursion\n;; Reason: completes at 10,000, segfaults at 20,000\n"
  },
  {
    "path": "impls/crystal/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install \\\n  ca-certificates crystal git libreadline-dev shards\n"
  },
  {
    "path": "impls/crystal/Makefile",
    "content": "STEPS = step0_repl step1_read_print step2_eval step3_env \\\n  step4_if_fn_do step5_tco step6_file step7_quote step8_macros \\\n  step9_try stepA_mal\nall: $(STEPS)\n$(STEPS):\n\tshards build $@ --release\nclean:\n\trm -rf .cache/crystal/ .cache/shards/ bin/ lib/\n.PHONY: all clean $(STEPS)\n"
  },
  {
    "path": "impls/crystal/core.cr",
    "content": "require \"time\"\n\nrequire \"readline\"\nrequire \"./types\"\nrequire \"./error\"\nrequire \"./printer\"\nrequire \"./reader\"\n\nmodule Mal\n  macro calc_op(op)\n    -> (args : Array(Mal::Type)) {\n      x, y = args[0].unwrap, args[1].unwrap\n      eval_error \"invalid arguments for binary operator {{op.id}}\" unless x.is_a?(Int64) && y.is_a?(Int64)\n      Mal::Type.new(x {{op.id}} y)\n    }\n  end\n\n  def self.list(args)\n    args.to_mal\n  end\n\n  def self.list?(args)\n    args.first.unwrap.is_a? Mal::List\n  end\n\n  def self.empty?(args)\n    a = args.first.unwrap\n    a.is_a?(Array) ? a.empty? : false\n  end\n\n  def self.count(args)\n    a = args.first.unwrap\n    case a\n    when Array\n      a.size.to_i64\n    when Nil\n      0i64\n    else\n      eval_error \"invalid argument for function 'count'\"\n    end\n  end\n\n  def self.pr_str_(args)\n    args.map { |a| pr_str(a) }.join(\" \")\n  end\n\n  def self.str(args)\n    args.map { |a| pr_str(a, false) }.join\n  end\n\n  def self.prn(args)\n    puts self.pr_str_(args)\n    nil\n  end\n\n  def self.println(args)\n    puts args.map { |a| pr_str(a, false) }.join(\" \")\n    nil\n  end\n\n  def self.read_string(args)\n    head = args.first.unwrap\n    eval_error \"argument of read-str must be string\" unless head.is_a? String\n    read_str head\n  end\n\n  def self.slurp(args)\n    head = args.first.unwrap\n    eval_error \"argument of slurp must be string\" unless head.is_a? String\n    begin\n      File.read head\n    rescue\n      nil\n    end\n  end\n\n  def self.cons(args)\n    head, tail = args[0].as(Mal::Type), args[1].unwrap\n    eval_error \"2nd arg of cons must be list\" unless tail.is_a? Array\n    ([head] + tail).to_mal\n  end\n\n  def self.concat(args)\n    args.each_with_object(Mal::List.new) do |arg, list|\n      a = arg.unwrap\n      eval_error \"arguments of concat must be list\" unless a.is_a?(Array)\n      a.each { |e| list << e }\n    end\n  end\n\n  def self.vec(args)\n    arg = args.first.unwrap\n    arg.is_a? Array || eval_error \"argument of vec must be a sequence\"\n    arg.to_mal(Mal::Vector)\n  end\n\n  def self.nth(args)\n    a0, a1 = args[0].unwrap, args[1].unwrap\n    eval_error \"1st argument of nth must be list or vector\" unless a0.is_a? Array\n    eval_error \"2nd argument of nth must be integer\" unless a1.is_a? Int64\n    a0[a1]\n  end\n\n  def self.first(args)\n    a0 = args[0].unwrap\n\n    return nil if a0.nil?\n    eval_error \"1st argument of first must be list or vector or nil\" unless a0.is_a? Array\n    a0.empty? ? nil : a0.first\n  end\n\n  def self.rest(args)\n    a0 = args[0].unwrap\n\n    return Mal::List.new if a0.nil?\n    eval_error \"1st argument of first must be list or vector or nil\" unless a0.is_a? Array\n    return Mal::List.new if a0.empty?\n    a0[1..-1].to_mal\n  end\n\n  def self.apply(args)\n    eval_error \"apply must take at least 2 arguments\" unless args.size >= 2\n\n    head = args.first.unwrap\n    last = args.last.unwrap\n\n    eval_error \"last argument of apply must be list or vector\" unless last.is_a? Array\n\n    case head\n    when Mal::Closure\n      head.fn.call(args[1..-2] + last)\n    when Mal::Func\n      head.call(args[1..-2] + last)\n    else\n      eval_error \"1st argument of apply must be function or closure\"\n    end\n  end\n\n  def self.map(args)\n    func = args.first.unwrap\n    list = args[1].unwrap\n\n    eval_error \"2nd argument of map must be list or vector\" unless list.is_a? Array\n\n    f = case func\n        when Mal::Closure then func.fn\n        when Mal::Func    then func\n        else                   eval_error \"1st argument of map must be function\"\n        end\n\n    list.each_with_object(Mal::List.new) do |elem, mapped|\n      mapped << f.call([elem])\n    end\n  end\n\n  def self.nil_value?(args)\n    args.first.unwrap.nil?\n  end\n\n  def self.true?(args)\n    a = args.first.unwrap\n    a.is_a?(Bool) && a\n  end\n\n  def self.false?(args)\n    a = args.first.unwrap\n    a.is_a?(Bool) && !a\n  end\n\n  def self.symbol?(args)\n    args.first.unwrap.is_a?(Mal::Symbol)\n  end\n\n  def self.symbol(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of symbol function must be string\" unless head.is_a? String\n    Mal::Symbol.new head\n  end\n\n  def self.string?(args)\n    head = args.first.unwrap\n    head.is_a?(String) && (head.empty? || head[0] != '\\u029e')\n  end\n\n  def self.keyword(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of symbol function must be string\" unless head.is_a? String\n    if ! head.empty? && head[0] == '\\u029e'\n       return head\n    end\n    \"\\u029e\" + head\n  end\n\n  def self.keyword?(args)\n    head = args.first.unwrap\n    head.is_a?(String) && !head.empty? && head[0] == '\\u029e'\n  end\n\n  def self.number?(args)\n    args.first.unwrap.is_a?(Int64)\n  end\n\n  def self.fn?(args)\n    return false if args.first.macro?\n    head = args.first.unwrap\n    head.is_a?(Mal::Func) || head.is_a?(Mal::Closure)\n  end\n\n  def self.macro?(args)\n    args.first.macro?\n  end\n\n  def self.vector(args)\n    args.to_mal(Mal::Vector)\n  end\n\n  def self.vector?(args)\n    args.first.unwrap.is_a? Mal::Vector\n  end\n\n  def self.hash_map(args)\n    eval_error \"hash-map must take even number of arguments\" unless args.size.even?\n    map = Mal::HashMap.new\n    args.each_slice(2) do |kv|\n      k = kv[0].unwrap\n      eval_error \"key must be string\" unless k.is_a? String\n      map[k] = kv[1]\n    end\n    map\n  end\n\n  def self.map?(args)\n    args.first.unwrap.is_a? Mal::HashMap\n  end\n\n  def self.assoc(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of assoc must be hashmap\" unless head.is_a? Mal::HashMap\n    eval_error \"assoc must take a list and even number of arguments\" unless (args.size - 1).even?\n\n    map = Mal::HashMap.new\n    head.each { |k, v| map[k] = v }\n\n    args[1..-1].each_slice(2) do |kv|\n      k = kv[0].unwrap\n      eval_error \"key must be string\" unless k.is_a? String\n      map[k] = kv[1]\n    end\n\n    map\n  end\n\n  def self.dissoc(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of assoc must be hashmap\" unless head.is_a? Mal::HashMap\n\n    map = Mal::HashMap.new\n    head.each { |k, v| map[k] = v }\n\n    args[1..-1].each do |arg|\n      key = arg.unwrap\n      eval_error \"key must be string\" unless key.is_a? String\n      map.delete key\n    end\n\n    map\n  end\n\n  def self.get(args)\n    a0, a1 = args[0].unwrap, args[1].unwrap\n    return nil unless a0.is_a? Mal::HashMap\n    eval_error \"2nd argument of get must be string\" unless a1.is_a? String\n\n    # a0[a1]? isn't available because type ofa0[a1] is infered NoReturn\n    a0.has_key?(a1) ? a0[a1] : nil\n  end\n\n  def self.contains?(args)\n    a0, a1 = args[0].unwrap, args[1].unwrap\n    eval_error \"1st argument of get must be hashmap\" unless a0.is_a? Mal::HashMap\n    eval_error \"2nd argument of get must be string\" unless a1.is_a? String\n    a0.has_key? a1\n  end\n\n  def self.keys(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of assoc must be hashmap\" unless head.is_a? Mal::HashMap\n    head.keys.each_with_object(Mal::List.new) { |e, l| l << Mal::Type.new(e) }\n  end\n\n  def self.vals(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of assoc must be hashmap\" unless head.is_a? Mal::HashMap\n    head.values.to_mal\n  end\n\n  def self.sequential?(args)\n    args.first.unwrap.is_a? Array\n  end\n\n  def self.readline(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of readline must be string\" unless head.is_a? String\n    Readline.readline(head, true)\n  end\n\n  def self.meta(args)\n    m = args.first.meta\n    m.nil? ? nil : m\n  end\n\n  def self.with_meta(args)\n    t = args.first.dup\n    t.meta = args[1]\n    t\n  end\n\n  def self.atom(args)\n    Mal::Atom.new args.first\n  end\n\n  def self.atom?(args)\n    args.first.unwrap.is_a? Mal::Atom\n  end\n\n  def self.deref(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of deref must be atom\" unless head.is_a? Mal::Atom\n    head.val\n  end\n\n  def self.reset!(args)\n    head = args.first.unwrap\n    eval_error \"1st argument of reset! must be atom\" unless head.is_a? Mal::Atom\n    head.val = args[1]\n  end\n\n  def self.swap!(args)\n    atom = args.first.unwrap\n    eval_error \"1st argument of swap! must be atom\" unless atom.is_a? Mal::Atom\n\n    a = [atom.val] + args[2..-1]\n\n    func = args[1].unwrap\n    case func\n    when Mal::Func\n      atom.val = func.call a\n    when Mal::Closure\n      atom.val = func.fn.call a\n    else\n      eval_error \"2nd argumetn of swap! must be function\"\n    end\n  end\n\n  def self.conj(args)\n    seq = args.first.unwrap\n    case seq\n    when Mal::List\n      (args[1..-1].reverse + seq).to_mal\n    when Mal::Vector\n      (seq + args[1..-1]).to_mal(Mal::Vector)\n    else\n      eval_error \"1st argument of conj must be list or vector\"\n    end\n  end\n\n  def self.seq(args)\n    obj = args.first.unwrap\n    case obj\n    when nil\n      nil\n    when Mal::List\n      return nil if obj.empty?\n      obj\n    when Mal::Vector\n      return nil if obj.empty?\n      obj.to_mal\n    when String\n      return nil if obj.empty?\n      obj.split(\"\").each_with_object(Mal::List.new) { |e, l| l << Mal::Type.new(e) }\n    else\n      eval_error \"argument of seq must be list or vector or string or nil\"\n    end\n  end\n\n  def self.time_ms(args)\n    Time.utc.to_unix_ms\n  end\n\n  # Note:\n  # Simply using ->self.some_func doesn't work\n  macro func(name)\n    -> (args : Array(Mal::Type)) { Mal::Type.new self.{{name.id}}(args) }\n  end\n\n  macro rel_op(op)\n    -> (args : Array(Mal::Type)) { Mal::Type.new (args[0] {{op.id}} args[1]) }\n  end\n\n  NS = {\n    \"+\"           => calc_op(:+),\n    \"-\"           => calc_op(:-),\n    \"*\"           => calc_op(:*),\n    \"/\"           => calc_op(://),\n    \"list\"        => func(:list),\n    \"list?\"       => func(:list?),\n    \"empty?\"      => func(:empty?),\n    \"count\"       => func(:count),\n    \"=\"           => rel_op(:==),\n    \"<\"           => rel_op(:<),\n    \">\"           => rel_op(:>),\n    \"<=\"          => rel_op(:<=),\n    \">=\"          => rel_op(:>=),\n    \"pr-str\"      => func(:pr_str_),\n    \"str\"         => func(:str),\n    \"prn\"         => func(:prn),\n    \"println\"     => func(:println),\n    \"read-string\" => func(:read_string),\n    \"slurp\"       => func(:slurp),\n    \"cons\"        => func(:cons),\n    \"concat\"      => func(:concat),\n    \"vec\"         => func(:vec),\n    \"nth\"         => func(:nth),\n    \"first\"       => func(:first),\n    \"rest\"        => func(:rest),\n    \"throw\"       => ->(args : Array(Mal::Type)) { raise Mal::RuntimeException.new args[0] },\n    \"apply\"       => func(:apply),\n    \"map\"         => func(:map),\n    \"nil?\"        => func(:nil_value?),\n    \"true?\"       => func(:true?),\n    \"false?\"      => func(:false?),\n    \"symbol?\"     => func(:symbol?),\n    \"symbol\"      => func(:symbol),\n    \"string?\"     => func(:string?),\n    \"keyword\"     => func(:keyword),\n    \"keyword?\"    => func(:keyword?),\n    \"number?\"     => func(:number?),\n    \"fn?\"         => func(:fn?),\n    \"macro?\"      => func(:macro?),\n    \"vector\"      => func(:vector),\n    \"vector?\"     => func(:vector?),\n    \"hash-map\"    => func(:hash_map),\n    \"map?\"        => func(:map?),\n    \"assoc\"       => func(:assoc),\n    \"dissoc\"      => func(:dissoc),\n    \"get\"         => func(:get),\n    \"contains?\"   => func(:contains?),\n    \"keys\"        => func(:keys),\n    \"vals\"        => func(:vals),\n    \"sequential?\" => func(:sequential?),\n    \"readline\"    => func(:readline),\n    \"meta\"        => func(:meta),\n    \"with-meta\"   => func(:with_meta),\n    \"atom\"        => func(:atom),\n    \"atom?\"       => func(:atom?),\n    \"deref\"       => func(:deref),\n    \"deref\"       => func(:deref),\n    \"reset!\"      => func(:reset!),\n    \"swap!\"       => func(:swap!),\n    \"conj\"        => func(:conj),\n    \"seq\"         => func(:seq),\n    \"time-ms\"     => func(:time_ms),\n  } of String => Mal::Func\nend\n"
  },
  {
    "path": "impls/crystal/env.cr",
    "content": "require \"./types\"\nrequire \"./error\"\n\nmodule Mal\n  class Env\n    property data\n\n    def initialize(@outer : Env?)\n      @data = {} of String => Mal::Type\n    end\n\n    def initialize(@outer : Env, binds, exprs : Array(Mal::Type))\n      @data = {} of String => Mal::Type\n\n      eval_error \"binds must be list or vector\" unless binds.is_a? Array\n\n      # Note:\n      # Array#zip() can't be used because overload resolution failed\n      (0...binds.size).each do |idx|\n        sym = binds[idx].unwrap\n        eval_error \"bind name must be symbol\" unless sym.is_a? Mal::Symbol\n\n        if sym.str == \"&\"\n          eval_error \"missing variable parameter name\" if binds.size == idx\n          next_param = binds[idx + 1].unwrap\n          eval_error \"bind name must be symbol\" unless next_param.is_a? Mal::Symbol\n          var_args = Mal::List.new\n          exprs[idx..-1].each { |e| var_args << e } if idx < exprs.size\n          @data[next_param.str] = Mal::Type.new var_args\n          break\n        end\n\n        @data[sym.str] = exprs[idx]\n      end\n    end\n\n    def dump\n      puts \"ENV BEGIN\".colorize.red\n      @data.each do |k, v|\n        puts \"  #{k} -> #{print(v)}\".colorize.red\n      end\n      puts \"ENV END\".colorize.red\n    end\n\n    def set(key, value)\n      @data[key] = value\n    end\n\n    def get(key)\n      return @data[key] if @data.has_key? key\n\n      o = @outer\n      if o\n        o.get key\n      else\n        nil\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "impls/crystal/error.cr",
    "content": "require \"./types\"\n\nmodule Mal\n  class ParseException < Exception\n  end\n\n  class EvalException < Exception\n  end\n\n  class RuntimeException < Exception\n    getter :thrown\n\n    def initialize(@thrown : Type)\n      super()\n    end\n  end\nend\n\ndef eval_error(msg)\n  raise Mal::EvalException.new msg\nend\n\ndef parse_error(msg)\n  raise Mal::ParseException.new msg\nend\n"
  },
  {
    "path": "impls/crystal/printer.cr",
    "content": "require \"./types\"\n\ndef pr_str(value, print_readably = true)\n  case value\n  when Nil          then \"nil\"\n  when Bool         then value.to_s\n  when Int64        then value.to_s\n  when Mal::List    then \"(#{value.map { |v| pr_str(v, print_readably).as(String) }.join(\" \")})\"\n  when Mal::Vector  then \"[#{value.map { |v| pr_str(v, print_readably).as(String) }.join(\" \")}]\"\n  when Mal::Symbol  then value.str.to_s\n  when Mal::Func    then \"<function>\"\n  when Mal::Closure then \"<closure>\"\n  when Mal::HashMap\n    # step1_read_print.cr requires specifying type\n    \"{#{value.map { |k, v| \"#{pr_str(k, print_readably)} #{pr_str(v, print_readably)}\".as(String) }.join(\" \")}}\"\n  when String\n    case\n    when value.empty?\n      print_readably ? value.inspect : value\n    when value[0] == '\\u029e'\n      \":#{value[1..-1]}\"\n    else\n      print_readably ? value.inspect : value\n    end\n  when Mal::Atom\n    \"(atom #{pr_str(value.val, print_readably)})\"\n  else\n    raise \"invalid MalType: #{value.to_s}\"\n  end\nend\n\ndef pr_str(t : Mal::Type, print_readably = true)\n  pr_str(t.unwrap, print_readably) + (t.macro? ? \" (macro)\" : \"\")\nend\n"
  },
  {
    "path": "impls/crystal/reader.cr",
    "content": "require \"./types\"\nrequire \"./error\"\n\nclass Reader\n  def initialize(@tokens : Array(String))\n    @pos = 0\n  end\n\n  def current_token\n    @tokens[@pos] rescue nil\n  end\n\n  def peek\n    t = current_token\n\n    if t && t[0] == ';'\n      @pos += 1\n      peek\n    else\n      t\n    end\n  end\n\n  def next\n    peek\n  ensure\n    @pos += 1\n  end\n\n  def read_sequence(init, open, close)\n    token = self.next\n    parse_error \"expected '#{open}', got EOF\" unless token\n    parse_error \"expected '#{open}', got #{token}\" unless token[0] == open\n\n    loop do\n      token = peek\n      parse_error \"expected '#{close}', got EOF\" unless token\n      break if token[0] == close\n\n      init << read_form\n      peek\n    end\n\n    self.next\n    init\n  end\n\n  def read_list\n    Mal::Type.new read_sequence(Mal::List.new, '(', ')')\n  end\n\n  def read_vector\n    Mal::Type.new read_sequence(Mal::Vector.new, '[', ']')\n  end\n\n  def read_hashmap\n    types = read_sequence([] of Mal::Type, '{', '}')\n\n    parse_error \"odd number of elements for hash-map: #{types.size}\" if types.size.odd?\n    map = Mal::HashMap.new\n\n    types.each_slice(2) do |kv|\n      k, v = kv[0].unwrap, kv[1]\n      case k\n      when String\n        map[k] = v\n      else\n        parse_error(\"key of hash-map must be string or keyword\")\n      end\n    end\n\n    Mal::Type.new map\n  end\n\n  def read_atom\n    token = self.next\n    parse_error \"expected Atom but got EOF\" unless token\n\n    Mal::Type.new case\n    when token =~ /^-?\\d+$/ then token.to_i64\n    when token == \"true\"    then true\n    when token == \"false\"   then false\n    when token == \"nil\"     then nil\n    when token =~ /^\"(?:\\\\.|[^\\\\\"])*\"$/\n      token[1..-2].gsub(/\\\\(.)/, {\"\\\\\\\"\" => \"\\\"\",\n                                  \"\\\\n\"  => \"\\n\",\n                                  \"\\\\\\\\\" => \"\\\\\"})\n    when token[0] == '\"' then parse_error \"expected '\\\"', got EOF\"\n    when token[0] == ':' then \"\\u029e#{token[1..-1]}\"\n    else                      Mal::Symbol.new token\n    end\n  end\n\n  def list_of(symname)\n    Mal::List.new << gen_type(Mal::Symbol, symname) << read_form\n  end\n\n  def read_form\n    token = peek\n\n    parse_error \"unexpected EOF\" unless token\n    parse_error \"unexpected comment\" if token[0] == ';'\n\n    Mal::Type.new case token\n    when \"(\"  then read_list\n    when \")\"  then parse_error \"unexpected ')'\"\n    when \"[\"  then read_vector\n    when \"]\"  then parse_error \"unexpected ']'\"\n    when \"{\"  then read_hashmap\n    when \"}\"  then parse_error \"unexpected '}'\"\n    when \"'\"  then self.next; list_of(\"quote\")\n    when \"`\"  then self.next; list_of(\"quasiquote\")\n    when \"~\"  then self.next; list_of(\"unquote\")\n    when \"~@\" then self.next; list_of(\"splice-unquote\")\n    when \"@\"  then self.next; list_of(\"deref\")\n    when \"^\"\n      self.next\n      meta = read_form\n      list_of(\"with-meta\") << meta\n    else read_atom\n    end\n  end\nend\n\ndef tokenize(str)\n  regex = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/\n  str.scan(regex).map { |m| m[1] }.reject(&.empty?)\nend\n\ndef read_str(str)\n  r = Reader.new(tokenize(str))\n  begin\n    r.read_form\n  ensure\n    unless r.peek.nil?\n      raise Mal::ParseException.new \"expected EOF, got #{r.peek.to_s}\"\n    end\n  end\nend\n"
  },
  {
    "path": "impls/crystal/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/bin/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/crystal/shard.yml",
    "content": "name: make-a-lisp\nversion: 0.1.0\ntargets:\n  step0_repl:\n    main: step0_repl.cr\n  step1_read_print:\n    main: step1_read_print.cr\n  step2_eval:\n    main: step2_eval.cr\n  step3_env:\n    main: step3_env.cr\n  step4_if_fn_do:\n    main: step4_if_fn_do.cr\n  step5_tco:\n    main: step5_tco.cr\n  step6_file:\n    main: step6_file.cr\n  step7_quote:\n    main: step7_quote.cr\n  step8_macros:\n    main: step8_macros.cr\n  step9_try:\n    main: step9_try.cr\n  stepA_mal:\n    main: stepA_mal.cr\ndependencies:\n  readline:\n    github: crystal-lang/crystal-readline\n"
  },
  {
    "path": "impls/crystal/step0_repl.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\ndef read(x)\n  x\nend\n\ndef eval(x)\n  x\nend\n\ndef print(x)\n  x\nend\n\ndef rep(x)\n  read(eval(print(x)))\nend\n\nwhile line = Readline.readline(\"user> \")\n  puts rep(line)\nend\n"
  },
  {
    "path": "impls/crystal/step1_read_print.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def read(str)\n    read_str str\n  end\n\n  def eval(x)\n    x\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str)))\n  end\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step2_eval.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\ndef eval_error(msg)\n  raise Mal::EvalException.new msg\nend\n\ndef num_func(func)\n  ->(args : Array(Mal::Type)) {\n    x, y = args[0].unwrap, args[1].unwrap\n    eval_error \"invalid arguments\" unless x.is_a?(Int64) && y.is_a?(Int64)\n    Mal::Type.new func.call(x, y)\n  }\nend\n\nREPL_ENV = {\n  \"+\" => Mal::Type.new(num_func(->(x : Int64, y : Int64) { x + y })),\n  \"-\" => Mal::Type.new(num_func(->(x : Int64, y : Int64) { x - y })),\n  \"*\" => Mal::Type.new(num_func(->(x : Int64, y : Int64) { x * y })),\n  \"/\" => Mal::Type.new(num_func(->(x : Int64, y : Int64) { x // y })),\n} of String => Mal::Type\n\nmodule Mal\n  extend self\n\n  def read(str)\n    read_str str\n  end\n\n  def eval(ast, env)\n    # puts \"EVAL: #{print(ast)}\"\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      eval_error \"'#{val.str}' not found\" unless env.has_key? val.str\n      return env[val.str]\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n        f = eval(list.first, env).unwrap\n        case f\n        when Mal::Func\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n\n    else\n      return Mal::Type.new val\n    end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step3_env.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\ndef eval_error(msg)\n  raise Mal::EvalException.new msg\nend\n\ndef num_func(func)\n  ->(args : Array(Mal::Type)) {\n    x, y = args[0].unwrap, args[1].unwrap\n    eval_error \"invalid arguments\" unless x.is_a?(Int64) && y.is_a?(Int64)\n    Mal::Type.new func.call(x, y)\n  }\nend\n\nREPL_ENV = Mal::Env.new nil\nREPL_ENV.set(\"+\", Mal::Type.new num_func(->(x : Int64, y : Int64) { x + y }))\nREPL_ENV.set(\"-\", Mal::Type.new num_func(->(x : Int64, y : Int64) { x - y }))\nREPL_ENV.set(\"*\", Mal::Type.new num_func(->(x : Int64, y : Int64) { x * y }))\nREPL_ENV.set(\"/\", Mal::Type.new num_func(->(x : Int64, y : Int64) { x // y }))\n\nmodule Mal\n  extend self\n\n  def read(str)\n    read_str str\n  end\n\n  def eval(ast, env)\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        return eval(list[2], new_env)\n      else\n        f = eval(list.first, env).unwrap\n        case f\n        when Mal::Func\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step4_if_fn_do.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def eval(ast, env)\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        return eval(list[2], new_env)\n      when \"do\"\n        if list.empty?\n          return Mal::Type.new(nil)\n        end\n        return list[1..-1].map { |n| eval(n, env) }.last\n      when \"if\"\n        if eval(list[1], env).unwrap\n          return eval(list[2], env)\n        elsif list.size >= 4\n          return eval(list[3], env)\n        else\n          return Mal::Type.new(nil)\n        end\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      else\n        f = eval(list.first, env).unwrap\n        case f\n        when Mal::Closure\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return eval(f.ast, Mal::Env.new(f.env, f.params, args))\n        when Mal::Func\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step5_tco.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def eval(ast, env)\n   while true\n\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        ast, env = list[2], new_env\n        next # TCO\n      when \"do\"\n        if list.empty?\n          ast = Mal::Type.new nil\n          next\n        end\n\n        list[1..-2].map { |n| eval(n, env) }\n        ast = list.last\n        next # TCO\n      when \"if\"\n        ast = unless eval(list[1], env).unwrap\n          list.size >= 4 ? list[3] : Mal::Type.new(nil)\n        else\n          list[2]\n        end\n        next # TCO\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      else\n        f = eval(list.first, env).unwrap\n        case f\n        when Mal::Closure\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          ast = f.ast\n          env = Mal::Env.new(f.env, f.params, args)\n          next # TCO\n        when Mal::Func\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n   end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step6_file.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def eval(ast, env)\n   while true\n\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        ast, env = list[2], new_env\n        next # TCO\n      when \"do\"\n        if list.empty?\n          ast = Mal::Type.new nil\n          next\n        end\n\n        list[1..-2].map { |n| eval(n, env) }\n        ast = list.last\n        next # TCO\n      when \"if\"\n        ast = unless eval(list[1], env).unwrap\n          list.size >= 4 ? list[3] : Mal::Type.new(nil)\n        else\n          list[2]\n        end\n        next # TCO\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      else\n        f = eval(list.first, env).unwrap\n        case f\n        when Mal::Closure\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          ast = f.ast\n          env = Mal::Env.new(f.env, f.params, args)\n          next # TCO\n        when Mal::Func\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n   end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nREPL_ENV.set(\"eval\", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0], REPL_ENV) })\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\nMal.rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nargv = Mal::List.new\nREPL_ENV.set(\"*ARGV*\", Mal::Type.new argv)\n\nunless ARGV.empty?\n  if ARGV.size > 1\n    ARGV[1..-1].each do |a|\n      argv << Mal::Type.new(a)\n    end\n  end\n\n  begin\n    Mal.rep \"(load-file \\\"#{ARGV[0]}\\\")\"\n  rescue e\n    STDERR.puts e\n  end\n  exit\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step7_quote.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def starts_with(list, symbol)\n    if list.size == 2\n      head = list.first.unwrap\n      head.is_a? Mal::Symbol && head.str == symbol\n    end\n  end\n\n  def quasiquote_elts(list)\n    acc = Mal::Type.new(Mal::List.new)\n    list.reverse.each do |elt|\n      elt_val = elt.unwrap\n      if elt_val.is_a? Mal::List && starts_with(elt_val, \"splice-unquote\")\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"concat\") << elt_val[1] << acc\n        )\n      else\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"cons\") << quasiquote(elt) << acc\n        )\n      end\n    end\n    acc\n  end\n\n  def quasiquote(ast)\n    ast_val = ast.unwrap\n    case ast_val\n    when Mal::List\n      if starts_with(ast_val,\"unquote\")\n        ast_val[1]\n      else\n        quasiquote_elts(ast_val)\n      end\n    when Mal::Vector\n      Mal::Type.new(\n        Mal::List.new << gen_type(Mal::Symbol, \"vec\") << quasiquote_elts(ast_val)\n      )\n    when Mal::HashMap, Mal::Symbol\n      Mal::Type.new (\n        Mal::List.new << gen_type(Mal::Symbol, \"quote\") << ast\n      )\n    else\n      ast\n    end\n  end\n\n  def eval(ast, env)\n   while true\n\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        ast, env = list[2], new_env\n        next # TCO\n      when \"do\"\n        if list.empty?\n          ast = Mal::Type.new nil\n          next\n        end\n\n        list[1..-2].map { |n| eval(n, env) }\n        ast = list.last\n        next # TCO\n      when \"if\"\n        ast = unless eval(list[1], env).unwrap\n          list.size >= 4 ? list[3] : Mal::Type.new(nil)\n        else\n          list[2]\n        end\n        next # TCO\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      when \"quote\"\n        return Mal::Type.new list[1]\n      when \"quasiquote\"\n        ast = quasiquote list[1]\n        next # TCO\n      else\n        f = eval(list.first, env).unwrap\n        case f\n        when Mal::Closure\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          ast = f.ast\n          env = Mal::Env.new(f.env, f.params, args)\n          next # TCO\n        when Mal::Func\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n   end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nREPL_ENV.set(\"eval\", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0], REPL_ENV) })\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\nMal.rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nargv = Mal::List.new\nREPL_ENV.set(\"*ARGV*\", Mal::Type.new argv)\n\nunless ARGV.empty?\n  if ARGV.size > 1\n    ARGV[1..-1].each do |a|\n      argv << Mal::Type.new(a)\n    end\n  end\n\n  begin\n    Mal.rep \"(load-file \\\"#{ARGV[0]}\\\")\"\n  rescue e\n    STDERR.puts e\n  end\n  exit\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step8_macros.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def starts_with(list, symbol)\n    if list.size == 2\n      head = list.first.unwrap\n      head.is_a? Mal::Symbol && head.str == symbol\n    end\n  end\n\n  def quasiquote_elts(list)\n    acc = Mal::Type.new(Mal::List.new)\n    list.reverse.each do |elt|\n      elt_val = elt.unwrap\n      if elt_val.is_a? Mal::List && starts_with(elt_val, \"splice-unquote\")\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"concat\") << elt_val[1] << acc\n        )\n      else\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"cons\") << quasiquote(elt) << acc\n        )\n      end\n    end\n    acc\n  end\n\n  def quasiquote(ast)\n    ast_val = ast.unwrap\n    case ast_val\n    when Mal::List\n      if starts_with(ast_val,\"unquote\")\n        ast_val[1]\n      else\n        quasiquote_elts(ast_val)\n      end\n    when Mal::Vector\n      Mal::Type.new(\n        Mal::List.new << gen_type(Mal::Symbol, \"vec\") << quasiquote_elts(ast_val)\n      )\n    when Mal::HashMap, Mal::Symbol\n      Mal::Type.new (\n        Mal::List.new << gen_type(Mal::Symbol, \"quote\") << ast\n      )\n    else\n      ast\n    end\n  end\n\n  def eval(ast, env)\n   while true\n\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        ast, env = list[2], new_env\n        next # TCO\n      when \"do\"\n        if list.empty?\n          ast = Mal::Type.new nil\n          next\n        end\n\n        list[1..-2].map { |n| eval(n, env) }\n        ast = list.last\n        next # TCO\n      when \"if\"\n        ast = unless eval(list[1], env).unwrap\n          list.size >= 4 ? list[3] : Mal::Type.new(nil)\n        else\n          list[2]\n        end\n        next # TCO\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      when \"quote\"\n        return Mal::Type.new list[1]\n      when \"quasiquote\"\n        ast = quasiquote list[1]\n        next # TCO\n      when \"defmacro!\"\n        eval_error \"wrong number of argument for 'defmacro!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'defmacro!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        mac = eval(list[2], env).dup\n        mac.is_macro = true\n        return Mal::Type.new env.set(a1.str, mac)\n      else\n        evaluated_first = eval(list.first, env)\n        f = evaluated_first.unwrap\n        case f\n        when Mal::Closure\n          if evaluated_first.macro?\n            ast = f.fn.call(list[1..-1])\n            next # TCO\n          end\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          ast = f.ast\n          env = Mal::Env.new(f.env, f.params, args)\n          next # TCO\n        when Mal::Func\n          if evaluated_first.macro?\n            ast = f.call(list[1..-1])\n            next # TCO\n          end\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n   end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nREPL_ENV.set(\"eval\", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0], REPL_ENV) })\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\nMal.rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nMal.rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\nargv = Mal::List.new\nREPL_ENV.set(\"*ARGV*\", Mal::Type.new argv)\n\nunless ARGV.empty?\n  if ARGV.size > 1\n    ARGV[1..-1].each do |a|\n      argv << Mal::Type.new(a)\n    end\n  end\n\n  begin\n    Mal.rep \"(load-file \\\"#{ARGV[0]}\\\")\"\n  rescue e\n    STDERR.puts e\n  end\n  exit\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/step9_try.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def starts_with(list, symbol)\n    if list.size == 2\n      head = list.first.unwrap\n      head.is_a? Mal::Symbol && head.str == symbol\n    end\n  end\n\n  def quasiquote_elts(list)\n    acc = Mal::Type.new(Mal::List.new)\n    list.reverse.each do |elt|\n      elt_val = elt.unwrap\n      if elt_val.is_a? Mal::List && starts_with(elt_val, \"splice-unquote\")\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"concat\") << elt_val[1] << acc\n        )\n      else\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"cons\") << quasiquote(elt) << acc\n        )\n      end\n    end\n    acc\n  end\n\n  def quasiquote(ast)\n    ast_val = ast.unwrap\n    case ast_val\n    when Mal::List\n      if starts_with(ast_val,\"unquote\")\n        ast_val[1]\n      else\n        quasiquote_elts(ast_val)\n      end\n    when Mal::Vector\n      Mal::Type.new(\n        Mal::List.new << gen_type(Mal::Symbol, \"vec\") << quasiquote_elts(ast_val)\n      )\n    when Mal::HashMap, Mal::Symbol\n      Mal::Type.new (\n        Mal::List.new << gen_type(Mal::Symbol, \"quote\") << ast\n      )\n    else\n      ast\n    end\n  end\n\n  def eval(ast, env)\n   while true\n\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        ast, env = list[2], new_env\n        next # TCO\n      when \"do\"\n        if list.empty?\n          ast = Mal::Type.new nil\n          next\n        end\n\n        list[1..-2].map { |n| eval(n, env) }\n        ast = list.last\n        next # TCO\n      when \"if\"\n        ast = unless eval(list[1], env).unwrap\n          list.size >= 4 ? list[3] : Mal::Type.new(nil)\n        else\n          list[2]\n        end\n        next # TCO\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      when \"quote\"\n        return Mal::Type.new list[1]\n      when \"quasiquote\"\n        ast = quasiquote list[1]\n        next # TCO\n      when \"defmacro!\"\n        eval_error \"wrong number of argument for 'defmacro!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'defmacro!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        mac = eval(list[2], env).dup\n        mac.is_macro = true\n        return Mal::Type.new env.set(a1.str, mac)\n      when \"try*\"\n        catch_list = list.size >= 3 ? list[2].unwrap : Mal::Type.new(nil)\n        return eval(list[1], env) unless catch_list.is_a? Mal::List\n\n        catch_head = catch_list.first.unwrap\n        return eval(list[1], env) unless catch_head.is_a? Mal::Symbol\n        return eval(list[1], env) unless catch_head.str == \"catch*\"\n\n        begin\n          return eval(list[1], env)\n        rescue e : Mal::RuntimeException\n          new_env = Mal::Env.new(env, [catch_list[1]], [e.thrown])\n          return Mal::Type.new eval(catch_list[2], new_env)\n        rescue e\n          new_env = Mal::Env.new(env, [catch_list[1]], [Mal::Type.new e.message])\n          return Mal::Type.new eval(catch_list[2], new_env)\n        end\n      else\n        evaluated_first = eval(list.first, env)\n        f = evaluated_first.unwrap\n        case f\n        when Mal::Closure\n          if evaluated_first.macro?\n            ast = f.fn.call(list[1..-1])\n            next # TCO\n          end\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          ast = f.ast\n          env = Mal::Env.new(f.env, f.params, args)\n          next # TCO\n        when Mal::Func\n          if evaluated_first.macro?\n            ast = f.call(list[1..-1])\n            next # TCO\n          end\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n   end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nREPL_ENV.set(\"eval\", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0], REPL_ENV) })\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\nMal.rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nMal.rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\nargv = Mal::List.new\nREPL_ENV.set(\"*ARGV*\", Mal::Type.new argv)\n\nunless ARGV.empty?\n  if ARGV.size > 1\n    ARGV[1..-1].each do |a|\n      argv << Mal::Type.new(a)\n    end\n  end\n\n  begin\n    Mal.rep \"(load-file \\\"#{ARGV[0]}\\\")\"\n  rescue e\n    STDERR.puts e\n  end\n  exit\nend\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/stepA_mal.cr",
    "content": "#! /usr/bin/env crystal run\n\nrequire \"colorize\"\n\nrequire \"readline\"\nrequire \"./reader\"\nrequire \"./printer\"\nrequire \"./types\"\nrequire \"./env\"\nrequire \"./core\"\nrequire \"./error\"\n\n# Note:\n# Employed downcase names because Crystal prohibits uppercase names for methods\n\nmodule Mal\n  extend self\n\n  def func_of(env, binds, body)\n    ->(args : Array(Mal::Type)) {\n      new_env = Mal::Env.new(env, binds, args)\n      eval(body, new_env)\n    }.as(Mal::Func)\n  end\n\n  def read(str)\n    read_str str\n  end\n\n  def starts_with(list, symbol)\n    if list.size == 2\n      head = list.first.unwrap\n      head.is_a? Mal::Symbol && head.str == symbol\n    end\n  end\n\n  def quasiquote_elts(list)\n    acc = Mal::Type.new(Mal::List.new)\n    list.reverse.each do |elt|\n      elt_val = elt.unwrap\n      if elt_val.is_a? Mal::List && starts_with(elt_val, \"splice-unquote\")\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"concat\") << elt_val[1] << acc\n        )\n      else\n        acc = Mal::Type.new(\n          Mal::List.new << gen_type(Mal::Symbol, \"cons\") << quasiquote(elt) << acc\n        )\n      end\n    end\n    acc\n  end\n\n  def quasiquote(ast)\n    ast_val = ast.unwrap\n    case ast_val\n    when Mal::List\n      if starts_with(ast_val,\"unquote\")\n        ast_val[1]\n      else\n        quasiquote_elts(ast_val)\n      end\n    when Mal::Vector\n      Mal::Type.new(\n        Mal::List.new << gen_type(Mal::Symbol, \"vec\") << quasiquote_elts(ast_val)\n      )\n    when Mal::HashMap, Mal::Symbol\n      Mal::Type.new (\n        Mal::List.new << gen_type(Mal::Symbol, \"quote\") << ast\n      )\n    else\n      ast\n    end\n  end\n\n  def debug(ast)\n    puts print(ast).colorize.red\n  end\n\n  def eval(ast, env)\n   while true\n\n    puts \"EVAL: #{print(ast)}\" if env.get(\"DEBUG-EVAL\")\n\n    val = ast.unwrap\n\n    case val\n    when Mal::Symbol\n      e = env.get(val.str)\n      eval_error \"'#{val.str}' not found\" unless e\n      return e\n    when Mal::Vector\n      new_vec = val.each_with_object(Mal::Vector.new) { |n, l| l << eval(n, env) }\n      return Mal::Type.new new_vec\n    when Mal::HashMap\n      new_map = Mal::HashMap.new\n      val.each { |k, v| new_map[k] = eval(v, env) }\n      return Mal::Type.new new_map\n    when Mal::List\n      list = val\n      return ast if list.empty?\n\n      head = list.first.unwrap\n      if head.is_a? Mal::Symbol\n         a0sym = head.str\n      else\n         a0sym = \"\"\n      end\n      case a0sym\n      when \"def!\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'def!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        return Mal::Type.new env.set(a1.str, eval(list[2], env))\n      when \"let*\"\n        eval_error \"wrong number of argument for 'def!'\" unless list.size == 3\n\n        bindings = list[1].unwrap\n        eval_error \"1st argument of 'let*' must be list or vector\" unless bindings.is_a? Array\n        eval_error \"size of binding list must be even\" unless bindings.size.even?\n\n        new_env = Mal::Env.new env\n        bindings.each_slice(2) do |binding|\n          key, value = binding\n          name = key.unwrap\n          eval_error \"name of binding must be specified as symbol #{name}\" unless name.is_a? Mal::Symbol\n          new_env.set(name.str, eval(value, new_env))\n        end\n\n        ast, env = list[2], new_env\n        next # TCO\n      when \"do\"\n        if list.empty?\n          ast = Mal::Type.new nil\n          next\n        end\n\n        list[1..-2].map { |n| eval(n, env) }\n        ast = list.last\n        next # TCO\n      when \"if\"\n        ast = unless eval(list[1], env).unwrap\n          list.size >= 4 ? list[3] : Mal::Type.new(nil)\n        else\n          list[2]\n        end\n        next # TCO\n      when \"fn*\"\n        params = list[1].unwrap\n        unless params.is_a? Array\n          eval_error \"'fn*' parameters must be list or vector: #{params}\"\n        end\n        return Mal::Type.new Mal::Closure.new(list[2], params, env, func_of(env, params, list[2]))\n      when \"quote\"\n        return Mal::Type.new list[1]\n      when \"quasiquote\"\n        ast = quasiquote list[1]\n        next # TCO\n      when \"defmacro!\"\n        eval_error \"wrong number of argument for 'defmacro!'\" unless list.size == 3\n        a1 = list[1].unwrap\n        eval_error \"1st argument of 'defmacro!' must be symbol: #{a1}\" unless a1.is_a? Mal::Symbol\n        mac = eval(list[2], env).dup\n        mac.is_macro = true\n        return Mal::Type.new env.set(a1.str, mac)\n      when \"try*\"\n        catch_list = list.size >= 3 ? list[2].unwrap : Mal::Type.new(nil)\n        return eval(list[1], env) unless catch_list.is_a? Mal::List\n\n        catch_head = catch_list.first.unwrap\n        return eval(list[1], env) unless catch_head.is_a? Mal::Symbol\n        return eval(list[1], env) unless catch_head.str == \"catch*\"\n\n        begin\n          return eval(list[1], env)\n        rescue e : Mal::RuntimeException\n          new_env = Mal::Env.new(env, [catch_list[1]], [e.thrown])\n          return Mal::Type.new eval(catch_list[2], new_env)\n        rescue e\n          new_env = Mal::Env.new(env, [catch_list[1]], [Mal::Type.new e.message])\n          return Mal::Type.new eval(catch_list[2], new_env)\n        end\n      else\n        evaluated_first = eval(list.first, env)\n        f = evaluated_first.unwrap\n        case f\n        when Mal::Closure\n          if evaluated_first.macro?\n            ast = f.fn.call(list[1..-1])\n            next # TCO\n          end\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          ast = f.ast\n          env = Mal::Env.new(f.env, f.params, args)\n          next # TCO\n        when Mal::Func\n          if evaluated_first.macro?\n            ast = f.call(list[1..-1])\n            next # TCO\n          end\n          args = list[1..-1].map { |n| eval(n, env).as(Mal::Type) }\n          return f.call args\n        else\n          eval_error \"expected function as the first argument: #{f}\"\n        end\n      end\n    else\n      return Mal::Type.new val\n    end\n   end\n  end\n\n  def print(result)\n    pr_str(result, true)\n  end\n\n  def rep(str)\n    print(eval(read(str), REPL_ENV))\n  end\nend\n\nREPL_ENV = Mal::Env.new nil\nMal::NS.each { |k, v| REPL_ENV.set(k, Mal::Type.new(v)) }\nREPL_ENV.set(\"eval\", Mal::Type.new ->(args : Array(Mal::Type)) { Mal.eval(args[0], REPL_ENV) })\nMal.rep \"(def! not (fn* (a) (if a false true)))\"\nMal.rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nMal.rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\nMal.rep(\"(def! *host-language* \\\"crystal\\\")\")\n\nargv = Mal::List.new\nREPL_ENV.set(\"*ARGV*\", Mal::Type.new argv)\n\nunless ARGV.empty?\n  if ARGV.size > 1\n    ARGV[1..-1].each do |a|\n      argv << Mal::Type.new(a)\n    end\n  end\n\n  begin\n    Mal.rep \"(load-file \\\"#{ARGV[0]}\\\")\"\n  rescue e\n    STDERR.puts e\n  end\n  exit\nend\n\nMal.rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n\nwhile line = Readline.readline(\"user> \", true)\n  begin\n    puts Mal.rep(line)\n  rescue e : Mal::RuntimeException\n    STDERR.puts \"Error: #{pr_str(e.thrown, true)}\"\n  rescue e\n    STDERR.puts \"Error: #{e}\"\n  end\nend\n"
  },
  {
    "path": "impls/crystal/tests/step5_tco.mal",
    "content": ";; Crystal: skipping non-TCO recursion\n;; Reason: completes at 1,000,000\n"
  },
  {
    "path": "impls/crystal/types.cr",
    "content": "require \"./printer\"\n\nmodule Mal\n  class Type\n    alias Func = (Array(Type) -> Type)\n\n    # It is (now) probably possible to only store is_macro for Closures.\n    property :is_macro, :meta\n\n    def initialize(@val : ValueType)\n      @is_macro = false\n      @meta = nil.as(Type | Nil)\n    end\n\n    def initialize(other : Type)\n      @val = other.unwrap\n      @is_macro = other.is_macro\n      @meta = other.meta\n    end\n\n    def unwrap\n      @val\n    end\n\n    def macro?\n      @is_macro\n    end\n\n    def to_s\n      pr_str(self)\n    end\n\n    def dup\n      Type.new(@val).tap do |t|\n        t.is_macro = @is_macro\n        t.meta = @meta\n      end\n    end\n\n    def ==(other : Type)\n      @val == other.unwrap\n    end\n\n    macro rel_op(*ops)\n      {% for op in ops %}\n        def {{op.id}}(other : Mal::Type)\n          l, r = @val, other.unwrap\n            {% for t in [Int64, String] %}\n              if l.is_a?({{t}}) && r.is_a?({{t}})\n                return (l) {{op.id}} (r)\n              end\n            {% end %}\n            if l.is_a?(Symbol) && r.is_a?(Symbol)\n              return l.str {{op.id}} r.str\n            end\n          false\n        end\n      {% end %}\n    end\n\n    rel_op :<, :>, :<=, :>=\n  end\n\n  class Symbol\n    property :str\n\n    def initialize(@str : String)\n    end\n\n    def ==(other : Symbol)\n      @str == other.str\n    end\n  end\n\n  class List < Array(Type)\n  end\n\n  class Vector < Array(Type)\n  end\n\n  class HashMap < Hash(String, Type)\n  end\n\n  class Atom\n    property :val\n\n    def initialize(@val : Type)\n    end\n\n    def ==(rhs : Atom)\n      @val == rhs.val\n    end\n  end\n\n  class Closure\n    property :ast, :params, :env, :fn\n\n    def initialize(@ast : Type, @params : Array(Mal::Type) | List | Vector, @env : Env, @fn : Func)\n    end\n  end\n\n  alias Type::ValueType = Nil | Bool | Int64 | String | Symbol | List | Vector | HashMap | Func | Closure | Atom\n  alias Func = Type::Func\nend\n\nmacro gen_type(t, *args)\n  Mal::Type.new {{t.id}}.new({{*args}})\nend\n\nclass Array\n  def to_mal(t = Mal::List)\n    each_with_object(t.new) { |e, l| l << e }\n  end\nend\n"
  },
  {
    "path": "impls/cs/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Deps for Mono-based languages (C#, VB.Net)\nRUN apt-get -y install tzdata mono-runtime mono-mcs mono-vbnc mono-devel\n"
  },
  {
    "path": "impls/cs/Makefile",
    "content": "#####################\n\nDEBUG =\n\nSOURCES_BASE = readline.cs types.cs reader.cs printer.cs\nSOURCES_LISP = env.cs core.cs stepA_mal.cs\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nOTHER_SOURCES = getline.cs\n\n#####################\n\nSRCS = step0_repl.cs step1_read_print.cs step2_eval.cs step3_env.cs \\\n       step4_if_fn_do.cs step5_tco.cs step6_file.cs step7_quote.cs \\\n       step8_macros.cs step9_try.cs stepA_mal.cs\n\nLIB_SRCS = $(filter-out step%,$(OTHER_SOURCES) $(SOURCES))\n\nFLAGS = $(if $(strip $(DEBUG)),-debug+,)\n\n#####################\n\nall: $(patsubst %.cs,%.exe,$(SRCS))\n\ndist: mal.exe mal\n\nmal.exe: $(patsubst %.cs,%.exe,$(word $(words $(SOURCES)),$(SOURCES)))\n\tcp $< $@\n\n# NOTE/WARNING: static linking triggers mono libraries LGPL\n# distribution requirements.\n# http://www.mono-project.com/archived/guiderunning_mono_applications/\nmal: $(patsubst %.cs,%.exe,$(word $(words $(SOURCES)),$(SOURCES))) mal.dll\n\tmkbundle --static -o $@ $+ --deps\n\nmal.dll: $(LIB_SRCS)\n\tmcs $(FLAGS) -target:library $+ -out:$@\n\n%.exe: %.cs mal.dll\n\tmcs $(FLAGS) -r:mal.dll $<\n\nclean:\n\trm -f mal *.dll *.exe *.mdb\n"
  },
  {
    "path": "impls/cs/core.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections.Generic;\nusing MalVal = Mal.types.MalVal;\nusing MalConstant = Mal.types.MalConstant;\nusing MalInt = Mal.types.MalInt;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalString = Mal.types.MalString;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalAtom = Mal.types.MalAtom;\nusing MalFunc = Mal.types.MalFunc;\n\nnamespace Mal {\n    public class core {\n        static MalConstant Nil = Mal.types.Nil;\n        static MalConstant True = Mal.types.True;\n        static MalConstant False = Mal.types.False;\n\n        // Errors/Exceptions\n        static public MalFunc mal_throw = new MalFunc(\n            a => { throw new Mal.types.MalException(a[0]); });\n\n        // Scalar functions\n        static MalFunc nil_Q = new MalFunc(\n            a => a[0] == Nil ? True : False);\n\n        static MalFunc true_Q = new MalFunc(\n            a => a[0] == True ? True : False);\n\n        static MalFunc false_Q = new MalFunc(\n            a => a[0] == False ? True : False);\n\n        static MalFunc symbol_Q = new MalFunc(\n            a => a[0] is MalSymbol ? True : False);\n\n        static MalFunc string_Q = new MalFunc(\n            a => {\n                if (a[0] is MalString) {\n                    var s = ((MalString)a[0]).getValue();\n                    return (s.Length == 0 || s[0] != '\\u029e') ? True : False;\n                } else {\n                    return False;\n                }\n            } );\n\n        static MalFunc keyword = new MalFunc(\n            a => {\n                if (a[0] is MalString &&\n                    ((MalString)a[0]).getValue()[0] == '\\u029e') {\n                    return a[0];\n                } else {\n                    return new MalString(\"\\u029e\" + ((MalString)a[0]).getValue());\n                }\n            } );\n\n        static MalFunc keyword_Q = new MalFunc(\n            a => {\n                if (a[0] is MalString) {\n                    var s = ((MalString)a[0]).getValue();\n                    return (s.Length > 0 && s[0] == '\\u029e') ? True : False;\n                } else {\n                    return False;\n                }\n            } );\n\n        static MalFunc number_Q = new MalFunc(\n            a => a[0] is MalInt ? True : False);\n\n        static MalFunc function_Q = new MalFunc(\n            a => a[0] is MalFunc && !((MalFunc)a[0]).isMacro() ? True : False);\n\n        static MalFunc macro_Q = new MalFunc(\n            a => a[0] is MalFunc && ((MalFunc)a[0]).isMacro() ? True : False);\n\n\n        // Number functions\n        static MalFunc time_ms = new MalFunc(\n            a => new MalInt(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond));\n\n        // String functions\n        static public MalFunc pr_str = new MalFunc(\n            a => new MalString(printer._pr_str_args(a, \" \", true)) );\n\n        static public MalFunc str = new MalFunc(\n            a => new MalString(printer._pr_str_args(a, \"\", false)) );\n\n        static public MalFunc prn = new MalFunc(\n            a => { \n                Console.WriteLine(printer._pr_str_args(a, \" \", true));\n                return Nil;\n            } );\n\n        static public MalFunc println = new MalFunc(\n            a => {\n                Console.WriteLine(printer._pr_str_args(a, \" \", false));\n                return Nil;\n            } );\n\n        static public MalFunc mal_readline = new MalFunc(\n            a => {\n                var line = readline.Readline(((MalString)a[0]).getValue());\n                if (line == null) { return types.Nil; }\n                else {              return new MalString(line); }\n            } );\n\n        static public MalFunc read_string = new MalFunc(\n            a => reader.read_str(((MalString)a[0]).getValue()));\n\n        static public MalFunc slurp = new MalFunc(\n            a => new MalString(File.ReadAllText(\n                        ((MalString)a[0]).getValue())));\n\n\n        // List/Vector functions\n        static public MalFunc list_Q = new MalFunc(\n            a => a[0].GetType() == typeof(MalList) ? True : False);\n\n        static public MalFunc vector_Q = new MalFunc(\n            a => a[0].GetType() == typeof(MalVector) ? True : False);\n\n        // HashMap functions\n        static public MalFunc hash_map_Q = new MalFunc(\n            a => a[0].GetType() == typeof(MalHashMap) ? True : False);\n\n        static MalFunc contains_Q = new MalFunc(\n            a => {\n                string key = ((MalString)a[1]).getValue();\n                var dict = ((MalHashMap)a[0]).getValue();\n                return dict.ContainsKey(key) ? True : False;\n            });\n\n        static MalFunc assoc = new MalFunc(\n            a => {\n                var new_hm = ((MalHashMap)a[0]).copy();\n                return new_hm.assoc_BANG((MalList)a.slice(1));\n            });\n\n        static MalFunc dissoc = new MalFunc(\n            a => {\n                var new_hm = ((MalHashMap)a[0]).copy();\n                return new_hm.dissoc_BANG((MalList)a.slice(1));\n            });\n\n        static MalFunc get = new MalFunc(\n            a => {\n                string key = ((MalString)a[1]).getValue();\n                if (a[0] == Nil) {\n                    return Nil;\n                } else {\n                    var dict = ((MalHashMap)a[0]).getValue();\n                    return dict.ContainsKey(key) ? dict[key] : Nil;\n                }\n            });\n\n        static MalFunc keys = new MalFunc(\n            a => {\n                var dict = ((MalHashMap)a[0]).getValue();\n                MalList key_lst = new MalList();\n                foreach (var key in dict.Keys) {\n                    key_lst.conj_BANG(new MalString(key));\n                }\n                return key_lst;\n            });\n\n        static MalFunc vals = new MalFunc(\n            a => {\n                var dict = ((MalHashMap)a[0]).getValue();\n                MalList val_lst = new MalList();\n                foreach (var val in dict.Values) {\n                    val_lst.conj_BANG(val);\n                }\n                return val_lst;\n            });\n\n        // Sequence functions\n        static public MalFunc sequential_Q = new MalFunc(\n            a => a[0] is MalList ? True : False);\n\n        static MalFunc cons = new MalFunc(\n            a => {\n                var lst = new List<MalVal>();\n                lst.Add(a[0]);\n                lst.AddRange(((MalList)a[1]).getValue());\n                return (MalVal)new MalList(lst);\n            });\n\n        static MalFunc concat = new MalFunc(\n            a => {\n                if (a.size() == 0) { return new MalList(); }\n                var lst = new List<MalVal>();\n                lst.AddRange(((MalList)a[0]).getValue());\n                for(int i=1; i<a.size(); i++) {\n                    lst.AddRange(((MalList)a[i]).getValue());\n                }\n                return (MalVal)new MalList(lst);\n            });\n\n        static MalFunc nth = new MalFunc(\n            a => {\n                var idx = (int)((MalInt)a[1]).getValue();\n                if (idx < ((MalList)a[0]).size()) {\n                    return ((MalList)a[0])[idx];\n                } else {\n                    throw new Mal.types.MalException(\n                        \"nth: index out of range\");\n                }\n            });\n\n        static MalFunc first = new MalFunc(\n            a => a[0] == Nil ? Nil : ((MalList)a[0])[0]);\n\n        static MalFunc rest = new MalFunc(\n            a => a[0] == Nil ? new MalList() : ((MalList)a[0]).rest());\n\n        static MalFunc empty_Q = new MalFunc(\n            a => ((MalList)a[0]).size() == 0 ? True : False);\n\n        static MalFunc count = new MalFunc(\n            a => {\n                return (a[0] == Nil)\n                    ? new MalInt(0)\n                    :new MalInt(((MalList)a[0]).size());\n            });\n\n        static MalFunc conj = new MalFunc(\n            a => {\n                var src_lst = ((MalList)a[0]).getValue();\n                var new_lst = new List<MalVal>();\n                new_lst.AddRange(src_lst);\n                if (a[0] is MalVector) {\n                    for(int i=1; i<a.size(); i++) {\n                        new_lst.Add(a[i]);\n                    }\n                    return new MalVector(new_lst);\n                } else {\n                    for(int i=1; i<a.size(); i++) {\n                        new_lst.Insert(0, a[i]);\n                    }\n                    return new MalList(new_lst);\n                }\n            });\n\n\n        static MalFunc seq = new MalFunc(\n            a => {\n                if (a[0] == Nil) {\n                    return Nil;\n                } else if (a[0] is MalVector) {\n                    return (((MalVector)a[0]).size() == 0)\n                        ? (MalVal)Nil\n                        : new MalList(((MalVector)a[0]).getValue());\n                } else if (a[0] is MalList) {\n                    return (((MalList)a[0]).size() == 0)\n                        ? Nil\n                        : a[0];\n                } else if (a[0] is MalString) {\n                    var s = ((MalString)a[0]).getValue();\n                    if (s.Length == 0) {\n                        return Nil;\n                    }\n                    var chars_list = new List<MalVal>();\n                    foreach (var c in s) {\n                        chars_list.Add(new MalString(c.ToString()));\n                    }\n                    return new MalList(chars_list);\n                }\n                return Nil;\n            });\n\n        // General list related functions\n        static MalFunc apply = new MalFunc(\n            a => {\n                var f = (MalFunc)a[0];\n                var lst = new List<MalVal>();\n                lst.AddRange(a.slice(1,a.size()-1).getValue());\n                lst.AddRange(((MalList)a[a.size()-1]).getValue());\n                return f.apply(new MalList(lst));\n            });\n\n        static MalFunc map = new MalFunc(\n            a => {\n                MalFunc f = (MalFunc) a[0];\n                var src_lst = ((MalList)a[1]).getValue();\n                var new_lst = new List<MalVal>();\n                for(int i=0; i<src_lst.Count; i++) {\n                    new_lst.Add(f.apply(new MalList(src_lst[i])));\n                }\n                return new MalList(new_lst);\n            });\n\n\n        // Metadata functions\n        static MalFunc meta = new MalFunc(\n            a => a[0].getMeta());\n\n        static MalFunc with_meta = new MalFunc(\n            a => ((MalVal)a[0]).copy().setMeta(a[1]));\n\n\n        // Atom functions\n        static MalFunc atom_Q = new MalFunc(\n            a => a[0] is MalAtom ? True : False);\n\n        static MalFunc deref = new MalFunc(\n            a => ((MalAtom)a[0]).getValue());\n\n        static MalFunc reset_BANG = new MalFunc(\n            a => ((MalAtom)a[0]).setValue(a[1]));\n\n        static MalFunc swap_BANG = new MalFunc(\n            a => {\n                MalAtom atm = (MalAtom)a[0];\n                MalFunc f = (MalFunc)a[1];\n                var new_lst = new List<MalVal>();\n                new_lst.Add(atm.getValue());\n                new_lst.AddRange(((MalList)a.slice(2)).getValue());\n                return atm.setValue(f.apply(new MalList(new_lst)));\n            });\n\n\n\n        static public Dictionary<string, MalVal> ns =\n                  new Dictionary<string, MalVal> {\n            {\"=\",  new MalFunc(\n                a => Mal.types._equal_Q(a[0], a[1]) ? True : False)},\n            {\"throw\", mal_throw},\n            {\"nil?\", nil_Q},\n            {\"true?\", true_Q},\n            {\"false?\", false_Q},\n            {\"symbol\", new MalFunc(a => new MalSymbol((MalString)a[0]))},\n            {\"symbol?\", symbol_Q},\n            {\"string?\", string_Q},\n            {\"keyword\", keyword},\n            {\"keyword?\", keyword_Q},\n            {\"number?\", number_Q},\n            {\"fn?\", function_Q},\n            {\"macro?\", macro_Q},\n\n            {\"pr-str\", pr_str},\n            {\"str\", str},\n            {\"prn\", prn},\n            {\"println\", println},\n            {\"readline\", mal_readline},\n            {\"read-string\", read_string},\n            {\"slurp\", slurp},\n            {\"<\",  new MalFunc(a => (MalInt)a[0] <  (MalInt)a[1])},\n            {\"<=\", new MalFunc(a => (MalInt)a[0] <= (MalInt)a[1])},\n            {\">\",  new MalFunc(a => (MalInt)a[0] >  (MalInt)a[1])},\n            {\">=\", new MalFunc(a => (MalInt)a[0] >= (MalInt)a[1])},\n            {\"+\",  new MalFunc(a => (MalInt)a[0] +  (MalInt)a[1])},\n            {\"-\",  new MalFunc(a => (MalInt)a[0] -  (MalInt)a[1])},\n            {\"*\",  new MalFunc(a => (MalInt)a[0] *  (MalInt)a[1])},\n            {\"/\",  new MalFunc(a => (MalInt)a[0] /  (MalInt)a[1])},\n            {\"time-ms\", time_ms},\n\n            {\"list\",  new MalFunc(a => new MalList(a.getValue()))},\n            {\"list?\", list_Q},\n            {\"vector\",  new MalFunc(a => new MalVector(a.getValue()))},\n            {\"vector?\", vector_Q},\n            {\"hash-map\",  new MalFunc(a => new MalHashMap(a))},\n            {\"map?\", hash_map_Q},\n            {\"contains?\", contains_Q},\n            {\"assoc\", assoc},\n            {\"dissoc\", dissoc},\n            {\"get\", get},\n            {\"keys\", keys},\n            {\"vals\", vals},\n\n            {\"sequential?\", sequential_Q},\n            {\"cons\", cons},\n            {\"concat\", concat},\n            {\"vec\",  new MalFunc(a => new MalVector(((MalList)a[0]).getValue()))},\n            {\"nth\", nth},\n            {\"first\", first},\n            {\"rest\",  rest},\n            {\"empty?\", empty_Q},\n            {\"count\", count},\n            {\"conj\", conj},\n            {\"seq\", seq},\n            {\"apply\", apply},\n            {\"map\", map},\n\n            {\"with-meta\", with_meta},\n            {\"meta\", meta},\n            {\"atom\", new MalFunc(a => new MalAtom(a[0]))},\n            {\"atom?\", atom_Q},\n            {\"deref\", deref},\n            {\"reset!\", reset_BANG},\n            {\"swap!\", swap_BANG},\n        };\n    }\n}\n"
  },
  {
    "path": "impls/cs/env.cs",
    "content": "using System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalList = Mal.types.MalList;\n\nnamespace Mal {\n    public class env {\n        public class Env {\n            Env outer = null;\n            Dictionary<string, MalVal> data = new Dictionary<string, MalVal>();\n\n            public Env(Env outer) {\n                this.outer = outer;\n            }\n            public Env(Env outer, MalList binds, MalList exprs) {\n                this.outer = outer;\n                for (int i=0; i<binds.size(); i++) {\n                    string sym = ((MalSymbol)binds.nth(i)).getName();\n                    if (sym == \"&\") {\n                        data[((MalSymbol)binds.nth(i+1)).getName()] = exprs.slice(i);\n                        break;\n                    } else {\n                        data[sym] = exprs.nth(i);\n                    }\n                }\n            }\n            \n            public MalVal get(string key) {\n                if (data.ContainsKey(key)) {\n                    return data[key];\n                } else if (outer != null) {\n                    return outer.get(key);\n                } else {\n                    return null;\n                }\n            }\n\n            public Env set(MalSymbol key, MalVal value) {\n                data[key.getName()] = value;\n                return this;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/getline.cs",
    "content": "//\n// getline.cs: A command line editor\n//\n// Authors:\n//   Miguel de Icaza (miguel@novell.com)\n//\n// Copyright 2008 Novell, Inc.\n//\n// Dual-licensed under the terms of the MIT X11 license or the\n// Apache License 2.0\n//\n// USE -define:DEMO to build this as a standalone file and test it\n//\n// TODO:\n//    Enter an error (a = 1);  Notice how the prompt is in the wrong line\n//\t\tThis is caused by Stderr not being tracked by System.Console.\n//    Completion support\n//    Why is Thread.Interrupt not working?   Currently I resort to Abort which is too much.\n//\n// Limitations in System.Console:\n//    Console needs SIGWINCH support of some sort\n//    Console needs a way of updating its position after things have been written\n//    behind its back (P/Invoke puts for example).\n//    System.Console needs to get the DELETE character, and report accordingly.\n//\n\nusing System;\nusing System.Text;\nusing System.IO;\nusing System.Threading;\nusing System.Reflection;\n\nnamespace Mono.Terminal {\n\n\tpublic class LineEditor {\n\n\t\tpublic class Completion {\n\t\t\tpublic string [] Result;\n\t\t\tpublic string Prefix;\n\n\t\t\tpublic Completion (string prefix, string [] result)\n\t\t\t{\n\t\t\t\tPrefix = prefix;\n\t\t\t\tResult = result;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic delegate Completion AutoCompleteHandler (string text, int pos);\n\t\t\n\t\t//static StreamWriter log;\n\t\t\n\t\t// The text being edited.\n\t\tStringBuilder text;\n\n\t\t// The text as it is rendered (replaces (char)1 with ^A on display for example).\n\t\tStringBuilder rendered_text;\n\n\t\t// The prompt specified, and the prompt shown to the user.\n\t\tstring prompt;\n\t\tstring shown_prompt;\n\t\t\n\t\t// The current cursor position, indexes into \"text\", for an index\n\t\t// into rendered_text, use TextToRenderPos\n\t\tint cursor;\n\n\t\t// The row where we started displaying data.\n\t\tint home_row;\n\n\t\t// The maximum length that has been displayed on the screen\n\t\tint max_rendered;\n\n\t\t// If we are done editing, this breaks the interactive loop\n\t\tbool done = false;\n\n\t\t// The thread where the Editing started taking place\n\t\tThread edit_thread;\n\n\t\t// Our object that tracks history\n\t\tHistory history;\n\n\t\t// The contents of the kill buffer (cut/paste in Emacs parlance)\n\t\tstring kill_buffer = \"\";\n\n\t\t// The string being searched for\n\t\tstring search;\n\t\tstring last_search;\n\n\t\t// whether we are searching (-1= reverse; 0 = no; 1 = forward)\n\t\tint searching;\n\n\t\t// The position where we found the match.\n\t\tint match_at;\n\t\t\n\t\t// Used to implement the Kill semantics (multiple Alt-Ds accumulate)\n\t\tKeyHandler last_handler;\n\t\t\n\t\tdelegate void KeyHandler ();\n\t\t\n\t\tstruct Handler {\n\t\t\tpublic ConsoleKeyInfo CKI;\n\t\t\tpublic KeyHandler KeyHandler;\n\n\t\t\tpublic Handler (ConsoleKey key, KeyHandler h)\n\t\t\t{\n\t\t\t\tCKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);\n\t\t\t\tKeyHandler = h;\n\t\t\t}\n\n\t\t\tpublic Handler (char c, KeyHandler h)\n\t\t\t{\n\t\t\t\tKeyHandler = h;\n\t\t\t\t// Use the \"Zoom\" as a flag that we only have a character.\n\t\t\t\tCKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);\n\t\t\t}\n\n\t\t\tpublic Handler (ConsoleKeyInfo cki, KeyHandler h)\n\t\t\t{\n\t\t\t\tCKI = cki;\n\t\t\t\tKeyHandler = h;\n\t\t\t}\n\t\t\t\n\t\t\tpublic static Handler Control (char c, KeyHandler h)\n\t\t\t{\n\t\t\t\treturn new Handler ((char) (c - 'A' + 1), h);\n\t\t\t}\n\n\t\t\tpublic static Handler Alt (char c, ConsoleKey k, KeyHandler h)\n\t\t\t{\n\t\t\t\tConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);\n\t\t\t\treturn new Handler (cki, h);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t///   Invoked when the user requests auto-completion using the tab character\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t///    The result is null for no values found, an array with a single\n\t\t///    string, in that case the string should be the text to be inserted\n\t\t///    for example if the word at pos is \"T\", the result for a completion\n\t\t///    of \"ToString\" should be \"oString\", not \"ToString\".\n\t\t///\n\t\t///    When there are multiple results, the result should be the full\n\t\t///    text\n\t\t/// </remarks>\n\t\tpublic AutoCompleteHandler AutoCompleteEvent;\n\t\t\n\t\tstatic Handler [] handlers;\n\n\t\tpublic LineEditor (string name) : this (name, 10) { }\n\t\t\n\t\tpublic LineEditor (string name, int histsize)\n\t\t{\n\t\t\thandlers = new Handler [] {\n\t\t\t\tnew Handler (ConsoleKey.Home,       CmdHome),\n\t\t\t\tnew Handler (ConsoleKey.End,        CmdEnd),\n\t\t\t\tnew Handler (ConsoleKey.LeftArrow,  CmdLeft),\n\t\t\t\tnew Handler (ConsoleKey.RightArrow, CmdRight),\n\t\t\t\tnew Handler (ConsoleKey.UpArrow,    CmdHistoryPrev),\n\t\t\t\tnew Handler (ConsoleKey.DownArrow,  CmdHistoryNext),\n\t\t\t\tnew Handler (ConsoleKey.Enter,      CmdDone),\n\t\t\t\tnew Handler (ConsoleKey.Backspace,  CmdBackspace),\n\t\t\t\tnew Handler (ConsoleKey.Delete,     CmdDeleteChar),\n\t\t\t\tnew Handler (ConsoleKey.Tab,        CmdTabOrComplete),\n\t\t\t\t\n\t\t\t\t// Emacs keys\n\t\t\t\tHandler.Control ('A', CmdHome),\n\t\t\t\tHandler.Control ('E', CmdEnd),\n\t\t\t\tHandler.Control ('B', CmdLeft),\n\t\t\t\tHandler.Control ('F', CmdRight),\n\t\t\t\tHandler.Control ('P', CmdHistoryPrev),\n\t\t\t\tHandler.Control ('N', CmdHistoryNext),\n\t\t\t\tHandler.Control ('K', CmdKillToEOF),\n\t\t\t\tHandler.Control ('Y', CmdYank),\n\t\t\t\tHandler.Control ('D', CmdDeleteChar),\n\t\t\t\tHandler.Control ('L', CmdRefresh),\n\t\t\t\tHandler.Control ('R', CmdReverseSearch),\n\t\t\t\tHandler.Control ('G', delegate {} ),\n\t\t\t\tHandler.Alt ('B', ConsoleKey.B, CmdBackwardWord),\n\t\t\t\tHandler.Alt ('F', ConsoleKey.F, CmdForwardWord),\n\t\t\t\t\n\t\t\t\tHandler.Alt ('D', ConsoleKey.D, CmdDeleteWord),\n\t\t\t\tHandler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),\n\t\t\t\t\n\t\t\t\t// DEBUG\n\t\t\t\t//Handler.Control ('T', CmdDebug),\n\n\t\t\t\t// quote\n\t\t\t\tHandler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })\n\t\t\t};\n\n\t\t\trendered_text = new StringBuilder ();\n\t\t\ttext = new StringBuilder ();\n\n\t\t\thistory = new History (name, histsize);\n\t\t\t\n\t\t\t//if (File.Exists (\"log\"))File.Delete (\"log\");\n\t\t\t//log = File.CreateText (\"log\"); \n\t\t}\n\n\t\tvoid CmdDebug ()\n\t\t{\n\t\t\thistory.Dump ();\n\t\t\tConsole.WriteLine ();\n\t\t\tRender ();\n\t\t}\n\n\t\tvoid Render ()\n\t\t{\n\t\t\tConsole.Write (shown_prompt);\n\t\t\tConsole.Write (rendered_text);\n\n\t\t\tint max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);\n\t\t\t\n\t\t\tfor (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)\n\t\t\t\tConsole.Write (' ');\n\t\t\tmax_rendered = shown_prompt.Length + rendered_text.Length;\n\n\t\t\t// Write one more to ensure that we always wrap around properly if we are at the\n\t\t\t// end of a line.\n\t\t\tConsole.Write (' ');\n\n\t\t\tUpdateHomeRow (max);\n\t\t}\n\n\t\tvoid UpdateHomeRow (int screenpos)\n\t\t{\n\t\t\tint lines = 1 + (screenpos / Console.WindowWidth);\n\n\t\t\thome_row = Console.CursorTop - (lines - 1);\n\t\t\tif (home_row < 0)\n\t\t\t\thome_row = 0;\n\t\t}\n\t\t\n\n\t\tvoid RenderFrom (int pos)\n\t\t{\n\t\t\tint rpos = TextToRenderPos (pos);\n\t\t\tint i;\n\t\t\t\n\t\t\tfor (i = rpos; i < rendered_text.Length; i++)\n\t\t\t\tConsole.Write (rendered_text [i]);\n\n\t\t\tif ((shown_prompt.Length + rendered_text.Length) > max_rendered)\n\t\t\t\tmax_rendered = shown_prompt.Length + rendered_text.Length;\n\t\t\telse {\n\t\t\t\tint max_extra = max_rendered - shown_prompt.Length;\n\t\t\t\tfor (; i < max_extra; i++)\n\t\t\t\t\tConsole.Write (' ');\n\t\t\t}\n\t\t}\n\n\t\tvoid ComputeRendered ()\n\t\t{\n\t\t\trendered_text.Length = 0;\n\n\t\t\tfor (int i = 0; i < text.Length; i++){\n\t\t\t\tint c = (int) text [i];\n\t\t\t\tif (c < 26){\n\t\t\t\t\tif (c == '\\t')\n\t\t\t\t\t\trendered_text.Append (\"    \");\n\t\t\t\t\telse {\n\t\t\t\t\t\trendered_text.Append ('^');\n\t\t\t\t\t\trendered_text.Append ((char) (c + (int) 'A' - 1));\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\trendered_text.Append ((char)c);\n\t\t\t}\n\t\t}\n\n\t\tint TextToRenderPos (int pos)\n\t\t{\n\t\t\tint p = 0;\n\n\t\t\tfor (int i = 0; i < pos; i++){\n\t\t\t\tint c;\n\n\t\t\t\tc = (int) text [i];\n\t\t\t\t\n\t\t\t\tif (c < 26){\n\t\t\t\t\tif (c == 9)\n\t\t\t\t\t\tp += 4;\n\t\t\t\t\telse\n\t\t\t\t\t\tp += 2;\n\t\t\t\t} else\n\t\t\t\t\tp++;\n\t\t\t}\n\n\t\t\treturn p;\n\t\t}\n\n\t\tint TextToScreenPos (int pos)\n\t\t{\n\t\t\treturn shown_prompt.Length + TextToRenderPos (pos);\n\t\t}\n\t\t\n\t\tstring Prompt {\n\t\t\tget { return prompt; }\n\t\t\tset { prompt = value; }\n\t\t}\n\n\t\tint LineCount {\n\t\t\tget {\n\t\t\t\treturn (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid ForceCursor (int newpos)\n\t\t{\n\t\t\tcursor = newpos;\n\n\t\t\tint actual_pos = shown_prompt.Length + TextToRenderPos (cursor);\n\t\t\tint row = home_row + (actual_pos/Console.WindowWidth);\n\t\t\tint col = actual_pos % Console.WindowWidth;\n\n\t\t\tif (row >= Console.BufferHeight)\n\t\t\t\trow = Console.BufferHeight-1;\n\t\t\tConsole.SetCursorPosition (col, row);\n\t\t\t\n\t\t\t//log.WriteLine (\"Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}\", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);\n\t\t\t//log.Flush ();\n\t\t}\n\n\t\tvoid UpdateCursor (int newpos)\n\t\t{\n\t\t\tif (cursor == newpos)\n\t\t\t\treturn;\n\n\t\t\tForceCursor (newpos);\n\t\t}\n\n\t\tvoid InsertChar (char c)\n\t\t{\n\t\t\tint prev_lines = LineCount;\n\t\t\ttext = text.Insert (cursor, c);\n\t\t\tComputeRendered ();\n\t\t\tif (prev_lines != LineCount){\n\n\t\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\t\tRender ();\n\t\t\t\tForceCursor (++cursor);\n\t\t\t} else {\n\t\t\t\tRenderFrom (cursor);\n\t\t\t\tForceCursor (++cursor);\n\t\t\t\tUpdateHomeRow (TextToScreenPos (cursor));\n\t\t\t}\n\t\t}\n\n\t\t//\n\t\t// Commands\n\t\t//\n\t\tvoid CmdDone ()\n\t\t{\n\t\t\tdone = true;\n\t\t}\n\n\t\tvoid CmdTabOrComplete ()\n\t\t{\n\t\t\tbool complete = false;\n\n\t\t\tif (AutoCompleteEvent != null){\n\t\t\t\tif (TabAtStartCompletes)\n\t\t\t\t\tcomplete = true;\n\t\t\t\telse {\n\t\t\t\t\tfor (int i = 0; i < cursor; i++){\n\t\t\t\t\t\tif (!Char.IsWhiteSpace (text [i])){\n\t\t\t\t\t\t\tcomplete = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (complete){\n\t\t\t\t\tCompletion completion = AutoCompleteEvent (text.ToString (), cursor);\n\t\t\t\t\tstring [] completions = completion.Result;\n\t\t\t\t\tif (completions == null)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tint ncompletions = completions.Length;\n\t\t\t\t\tif (ncompletions == 0)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tif (completions.Length == 1){\n\t\t\t\t\t\tInsertTextAtCursor (completions [0]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint last = -1;\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (int p = 0; p < completions [0].Length; p++){\n\t\t\t\t\t\t\tchar c = completions [0][p];\n\n\n\t\t\t\t\t\t\tfor (int i = 1; i < ncompletions; i++){\n\t\t\t\t\t\t\t\tif (completions [i].Length < p)\n\t\t\t\t\t\t\t\t\tgoto mismatch;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (completions [i][p] != c){\n\t\t\t\t\t\t\t\t\tgoto mismatch;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlast = p;\n\t\t\t\t\t\t}\n\t\t\t\t\tmismatch:\n\t\t\t\t\t\tif (last != -1){\n\t\t\t\t\t\t\tInsertTextAtCursor (completions [0].Substring (0, last+1));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\t\tforeach (string s in completions){\n\t\t\t\t\t\t\tConsole.Write (completion.Prefix);\n\t\t\t\t\t\t\tConsole.Write (s);\n\t\t\t\t\t\t\tConsole.Write (' ');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\t\tRender ();\n\t\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\tHandleChar ('\\t');\n\t\t\t} else\n\t\t\t\tHandleChar ('t');\n\t\t}\n\t\t\n\t\tvoid CmdHome ()\n\t\t{\n\t\t\tUpdateCursor (0);\n\t\t}\n\n\t\tvoid CmdEnd ()\n\t\t{\n\t\t\tUpdateCursor (text.Length);\n\t\t}\n\t\t\n\t\tvoid CmdLeft ()\n\t\t{\n\t\t\tif (cursor == 0)\n\t\t\t\treturn;\n\n\t\t\tUpdateCursor (cursor-1);\n\t\t}\n\n\t\tvoid CmdBackwardWord ()\n\t\t{\n\t\t\tint p = WordBackward (cursor);\n\t\t\tif (p == -1)\n\t\t\t\treturn;\n\t\t\tUpdateCursor (p);\n\t\t}\n\n\t\tvoid CmdForwardWord ()\n\t\t{\n\t\t\tint p = WordForward (cursor);\n\t\t\tif (p == -1)\n\t\t\t\treturn;\n\t\t\tUpdateCursor (p);\n\t\t}\n\n\t\tvoid CmdRight ()\n\t\t{\n\t\t\tif (cursor == text.Length)\n\t\t\t\treturn;\n\n\t\t\tUpdateCursor (cursor+1);\n\t\t}\n\n\t\tvoid RenderAfter (int p)\n\t\t{\n\t\t\tForceCursor (p);\n\t\t\tRenderFrom (p);\n\t\t\tForceCursor (cursor);\n\t\t}\n\t\t\n\t\tvoid CmdBackspace ()\n\t\t{\n\t\t\tif (cursor == 0)\n\t\t\t\treturn;\n\n\t\t\ttext.Remove (--cursor, 1);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tvoid CmdDeleteChar ()\n\t\t{\n\t\t\t// If there is no input, this behaves like EOF\n\t\t\tif (text.Length == 0){\n\t\t\t\tdone = true;\n\t\t\t\ttext = null;\n\t\t\t\tConsole.WriteLine ();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (cursor == text.Length)\n\t\t\t\treturn;\n\t\t\ttext.Remove (cursor, 1);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tint WordForward (int p)\n\t\t{\n\t\t\tif (p >= text.Length)\n\t\t\t\treturn -1;\n\n\t\t\tint i = p;\n\t\t\tif (Char.IsPunctuation (text [p]) || Char.IsSymbol (text [p]) || Char.IsWhiteSpace (text[p])){\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i != p)\n\t\t\t\treturn i;\n\t\t\treturn -1;\n\t\t}\n\n\t\tint WordBackward (int p)\n\t\t{\n\t\t\tif (p == 0)\n\t\t\t\treturn -1;\n\n\t\t\tint i = p-1;\n\t\t\tif (i == 0)\n\t\t\t\treturn 0;\n\t\t\t\n\t\t\tif (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text[i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t\t\n\t\t\tif (i != p)\n\t\t\t\treturn i;\n\n\t\t\treturn -1;\n\t\t}\n\t\t\n\t\tvoid CmdDeleteWord ()\n\t\t{\n\t\t\tint pos = WordForward (cursor);\n\n\t\t\tif (pos == -1)\n\t\t\t\treturn;\n\n\t\t\tstring k = text.ToString (cursor, pos-cursor);\n\t\t\t\n\t\t\tif (last_handler == CmdDeleteWord)\n\t\t\t\tkill_buffer = kill_buffer + k;\n\t\t\telse\n\t\t\t\tkill_buffer = k;\n\t\t\t\n\t\t\ttext.Remove (cursor, pos-cursor);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\t\t\n\t\tvoid CmdDeleteBackword ()\n\t\t{\n\t\t\tint pos = WordBackward (cursor);\n\t\t\tif (pos == -1)\n\t\t\t\treturn;\n\n\t\t\tstring k = text.ToString (pos, cursor-pos);\n\t\t\t\n\t\t\tif (last_handler == CmdDeleteBackword)\n\t\t\t\tkill_buffer = k + kill_buffer;\n\t\t\telse\n\t\t\t\tkill_buffer = k;\n\t\t\t\n\t\t\ttext.Remove (pos, cursor-pos);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (pos);\n\t\t}\n\t\t\n\t\t//\n\t\t// Adds the current line to the history if needed\n\t\t//\n\t\tvoid HistoryUpdateLine ()\n\t\t{\n\t\t\thistory.Update (text.ToString ());\n\t\t}\n\t\t\n\t\tvoid CmdHistoryPrev ()\n\t\t{\n\t\t\tif (!history.PreviousAvailable ())\n\t\t\t\treturn;\n\n\t\t\tHistoryUpdateLine ();\n\t\t\t\n\t\t\tSetText (history.Previous ());\n\t\t}\n\n\t\tvoid CmdHistoryNext ()\n\t\t{\n\t\t\tif (!history.NextAvailable())\n\t\t\t\treturn;\n\n\t\t\thistory.Update (text.ToString ());\n\t\t\tSetText (history.Next ());\n\t\t\t\n\t\t}\n\n\t\tvoid CmdKillToEOF ()\n\t\t{\n\t\t\tkill_buffer = text.ToString (cursor, text.Length-cursor);\n\t\t\ttext.Length = cursor;\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tvoid CmdYank ()\n\t\t{\n\t\t\tInsertTextAtCursor (kill_buffer);\n\t\t}\n\n\t\tvoid InsertTextAtCursor (string str)\n\t\t{\n\t\t\tint prev_lines = LineCount;\n\t\t\ttext.Insert (cursor, str);\n\t\t\tComputeRendered ();\n\t\t\tif (prev_lines != LineCount){\n\t\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\t\tRender ();\n\t\t\t\tcursor += str.Length;\n\t\t\t\tForceCursor (cursor);\n\t\t\t} else {\n\t\t\t\tRenderFrom (cursor);\n\t\t\t\tcursor += str.Length;\n\t\t\t\tForceCursor (cursor);\n\t\t\t\tUpdateHomeRow (TextToScreenPos (cursor));\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid SetSearchPrompt (string s)\n\t\t{\n\t\t\tSetPrompt (\"(reverse-i-search)`\" + s + \"': \");\n\t\t}\n\n\t\tvoid ReverseSearch ()\n\t\t{\n\t\t\tint p;\n\n\t\t\tif (cursor == text.Length){\n\t\t\t\t// The cursor is at the end of the string\n\t\t\t\t\n\t\t\t\tp = text.ToString ().LastIndexOf (search);\n\t\t\t\tif (p != -1){\n\t\t\t\t\tmatch_at = p;\n\t\t\t\t\tcursor = p;\n\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// The cursor is somewhere in the middle of the string\n\t\t\t\tint start = (cursor == match_at) ? cursor - 1 : cursor;\n\t\t\t\tif (start != -1){\n\t\t\t\t\tp = text.ToString ().LastIndexOf (search, start);\n\t\t\t\t\tif (p != -1){\n\t\t\t\t\t\tmatch_at = p;\n\t\t\t\t\t\tcursor = p;\n\t\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Need to search backwards in history\n\t\t\tHistoryUpdateLine ();\n\t\t\tstring s = history.SearchBackward (search);\n\t\t\tif (s != null){\n\t\t\t\tmatch_at = -1;\n\t\t\t\tSetText (s);\n\t\t\t\tReverseSearch ();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid CmdReverseSearch ()\n\t\t{\n\t\t\tif (searching == 0){\n\t\t\t\tmatch_at = -1;\n\t\t\t\tlast_search = search;\n\t\t\t\tsearching = -1;\n\t\t\t\tsearch = \"\";\n\t\t\t\tSetSearchPrompt (\"\");\n\t\t\t} else {\n\t\t\t\tif (search == \"\"){\n\t\t\t\t\tif (last_search != \"\" && last_search != null){\n\t\t\t\t\t\tsearch = last_search;\n\t\t\t\t\t\tSetSearchPrompt (search);\n\n\t\t\t\t\t\tReverseSearch ();\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tReverseSearch ();\n\t\t\t} \n\t\t}\n\n\t\tvoid SearchAppend (char c)\n\t\t{\n\t\t\tsearch = search + c;\n\t\t\tSetSearchPrompt (search);\n\n\t\t\t//\n\t\t\t// If the new typed data still matches the current text, stay here\n\t\t\t//\n\t\t\tif (cursor < text.Length){\n\t\t\t\tstring r = text.ToString (cursor, text.Length - cursor);\n\t\t\t\tif (r.StartsWith (search))\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tReverseSearch ();\n\t\t}\n\t\t\n\t\tvoid CmdRefresh ()\n\t\t{\n\t\t\tConsole.Clear ();\n\t\t\tmax_rendered = 0;\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\n\t\tvoid InterruptEdit (object sender, ConsoleCancelEventArgs a)\n\t\t{\n\t\t\t// Do not abort our program:\n\t\t\ta.Cancel = true;\n\n\t\t\t// Interrupt the editor\n\t\t\tedit_thread.Abort();\n\t\t}\n\n\t\tvoid HandleChar (char c)\n\t\t{\n\t\t\tif (searching != 0)\n\t\t\t\tSearchAppend (c);\n\t\t\telse\n\t\t\t\tInsertChar (c);\n\t\t}\n\n\t\tvoid EditLoop ()\n\t\t{\n\t\t\tConsoleKeyInfo cki;\n\n\t\t\twhile (!done){\n\t\t\t\tConsoleModifiers mod;\n\t\t\t\t\n\t\t\t\tcki = Console.ReadKey (true);\n\t\t\t\tif (cki.Key == ConsoleKey.Escape){\n\t\t\t\t\tcki = Console.ReadKey (true);\n\n\t\t\t\t\tmod = ConsoleModifiers.Alt;\n\t\t\t\t} else\n\t\t\t\t\tmod = cki.Modifiers;\n\t\t\t\t\n\t\t\t\tbool handled = false;\n\n\t\t\t\tforeach (Handler handler in handlers){\n\t\t\t\t\tConsoleKeyInfo t = handler.CKI;\n\n\t\t\t\t\tif (t.Key == cki.Key && t.Modifiers == mod){\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\thandler.KeyHandler ();\n\t\t\t\t\t\tlast_handler = handler.KeyHandler;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\thandler.KeyHandler ();\n\t\t\t\t\t\tlast_handler = handler.KeyHandler;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (handled){\n\t\t\t\t\tif (searching != 0){\n\t\t\t\t\t\tif (last_handler != CmdReverseSearch){\n\t\t\t\t\t\t\tsearching = 0;\n\t\t\t\t\t\t\tSetPrompt (prompt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (cki.KeyChar != (char) 0)\n\t\t\t\t\tHandleChar (cki.KeyChar);\n\t\t\t} \n\t\t}\n\n\t\tvoid InitText (string initial)\n\t\t{\n\t\t\ttext = new StringBuilder (initial);\n\t\t\tComputeRendered ();\n\t\t\tcursor = text.Length;\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\n\t\tvoid SetText (string newtext)\n\t\t{\n\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\tInitText (newtext);\n\t\t}\n\n\t\tvoid SetPrompt (string newprompt)\n\t\t{\n\t\t\tshown_prompt = newprompt;\n\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\t\t\n\t\tpublic string Edit (string prompt, string initial)\n\t\t{\n\t\t\tedit_thread = Thread.CurrentThread;\n\t\t\tsearching = 0;\n\t\t\tConsole.CancelKeyPress += InterruptEdit;\n\t\t\t\n\t\t\tdone = false;\n\t\t\thistory.CursorToEnd ();\n\t\t\tmax_rendered = 0;\n\t\t\t\n\t\t\tPrompt = prompt;\n\t\t\tshown_prompt = prompt;\n\t\t\tInitText (initial);\n\t\t\thistory.Append (initial);\n\n\t\t\tdo {\n\t\t\t\ttry {\n\t\t\t\t\tEditLoop ();\n\t\t\t\t} catch (ThreadAbortException){\n\t\t\t\t\tsearching = 0;\n\t\t\t\t\tThread.ResetAbort ();\n\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\tSetPrompt (prompt);\n\t\t\t\t\tSetText (\"\");\n\t\t\t\t}\n\t\t\t} while (!done);\n\t\t\tConsole.WriteLine ();\n\t\t\t\n\t\t\tConsole.CancelKeyPress -= InterruptEdit;\n\n\t\t\tif (text == null){\n\t\t\t\thistory.Close ();\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tstring result = text.ToString ();\n\t\t\tif (result != \"\")\n\t\t\t\thistory.Accept (result);\n\t\t\telse\n\t\t\t\thistory.RemoveLast ();\n\n\t\t\treturn result;\n\t\t}\n\t\t\n\t\tpublic void SaveHistory ()\n\t\t{\n\t\t\tif (history != null) {\n\t\t\t\thistory.Close ();\n\t\t\t}\n\t\t}\n\n\t\tpublic bool TabAtStartCompletes { get; set; }\n\t\t\t\n\t\t//\n\t\t// Emulates the bash-like behavior, where edits done to the\n\t\t// history are recorded\n\t\t//\n\t\tclass History {\n\t\t\tstring [] history;\n\t\t\tint head, tail;\n\t\t\tint cursor, count;\n\t\t\tstring histfile;\n\t\t\t\n\t\t\tpublic History (string app, int size)\n\t\t\t{\n\t\t\t\tif (size < 1)\n\t\t\t\t\tthrow new ArgumentException (\"size\");\n\n\t\t\t\tif (app != null){\n\t\t\t\t\tstring dir = Environment.GetFolderPath (Environment.SpecialFolder.Personal);\n\t\t\t\t\t//Console.WriteLine (dir);\n//\t\t\t\t\tif (!Directory.Exists (dir)){\n//\t\t\t\t\t\ttry {\n//\t\t\t\t\t\t\tDirectory.CreateDirectory (dir);\n//\t\t\t\t\t\t} catch {\n//\t\t\t\t\t\t\tapp = null;\n//\t\t\t\t\t\t}\n//\t\t\t\t\t}\n//\t\t\t\t\tif (app != null)\n//\t\t\t\t\t\thistfile = Path.Combine (dir, app) + \".history\";\n\t\t\t\t\thistfile = Path.Combine (dir, \".mal-history\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\thistory = new string [size];\n\t\t\t\thead = tail = cursor = 0;\n\n\t\t\t\tif (File.Exists (histfile)){\n\t\t\t\t\tusing (StreamReader sr = File.OpenText (histfile)){\n\t\t\t\t\t\tstring line;\n\t\t\t\t\t\t\n\t\t\t\t\t\twhile ((line = sr.ReadLine ()) != null){\n\t\t\t\t\t\t\tif (line != \"\")\n\t\t\t\t\t\t\t\tAppend (line);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void Close ()\n\t\t\t{\n\t\t\t\tif (histfile == null)\n\t\t\t\t\treturn;\n\n\t\t\t\ttry {\n\t\t\t\t\tusing (StreamWriter sw = File.CreateText (histfile)){\n\t\t\t\t\t\tint start = (count == history.Length) ? head : tail;\n\t\t\t\t\t\tfor (int i = start; i < start+count; i++){\n\t\t\t\t\t\t\tint p = i % history.Length;\n\t\t\t\t\t\t\tsw.WriteLine (history [p]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//\n\t\t\t// Appends a value to the history\n\t\t\t//\n\t\t\tpublic void Append (string s)\n\t\t\t{\n\t\t\t\t//Console.WriteLine (\"APPENDING {0} head={1} tail={2}\", s, head, tail);\n\t\t\t\thistory [head] = s;\n\t\t\t\thead = (head+1) % history.Length;\n\t\t\t\tif (head == tail)\n\t\t\t\t\ttail = (tail+1 % history.Length);\n\t\t\t\tif (count != history.Length)\n\t\t\t\t\tcount++;\n\t\t\t\t//Console.WriteLine (\"DONE: head={1} tail={2}\", s, head, tail);\n\t\t\t}\n\n\t\t\t//\n\t\t\t// Updates the current cursor location with the string,\n\t\t\t// to support editing of history items.   For the current\n\t\t\t// line to participate, an Append must be done before.\n\t\t\t//\n\t\t\tpublic void Update (string s)\n\t\t\t{\n\t\t\t\thistory [cursor] = s;\n\t\t\t}\n\n\t\t\tpublic void RemoveLast ()\n\t\t\t{\n\t\t\t\thead = head-1;\n\t\t\t\tif (head < 0)\n\t\t\t\t\thead = history.Length-1;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Accept (string s)\n\t\t\t{\n\t\t\t\tint t = head-1;\n\t\t\t\tif (t < 0)\n\t\t\t\t\tt = history.Length-1;\n\t\t\t\t\n\t\t\t\thistory [t] = s;\n\t\t\t}\n\t\t\t\n\t\t\tpublic bool PreviousAvailable ()\n\t\t\t{\n\t\t\t\t//Console.WriteLine (\"h={0} t={1} cursor={2}\", head, tail, cursor);\n\t\t\t\tif (count == 0)\n\t\t\t\t\treturn false;\n\t\t\t\tint next = cursor-1;\n\t\t\t\tif (next < 0)\n\t\t\t\t\tnext = count-1;\n\n\t\t\t\tif (next == head)\n\t\t\t\t\treturn false;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tpublic bool NextAvailable ()\n\t\t\t{\n\t\t\t\tif (count == 0)\n\t\t\t\t\treturn false;\n\t\t\t\tint next = (cursor + 1) % history.Length;\n\t\t\t\tif (next == head)\n\t\t\t\t\treturn false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\t\n\t\t\t//\n\t\t\t// Returns: a string with the previous line contents, or\n\t\t\t// nul if there is no data in the history to move to.\n\t\t\t//\n\t\t\tpublic string Previous ()\n\t\t\t{\n\t\t\t\tif (!PreviousAvailable ())\n\t\t\t\t\treturn null;\n\n\t\t\t\tcursor--;\n\t\t\t\tif (cursor < 0)\n\t\t\t\t\tcursor = history.Length - 1;\n\n\t\t\t\treturn history [cursor];\n\t\t\t}\n\n\t\t\tpublic string Next ()\n\t\t\t{\n\t\t\t\tif (!NextAvailable ())\n\t\t\t\t\treturn null;\n\n\t\t\t\tcursor = (cursor + 1) % history.Length;\n\t\t\t\treturn history [cursor];\n\t\t\t}\n\n\t\t\tpublic void CursorToEnd ()\n\t\t\t{\n\t\t\t\tif (head == tail)\n\t\t\t\t\treturn;\n\n\t\t\t\tcursor = head;\n\t\t\t}\n\n\t\t\tpublic void Dump ()\n\t\t\t{\n\t\t\t\tConsole.WriteLine (\"Head={0} Tail={1} Cursor={2} count={3}\", head, tail, cursor, count);\n\t\t\t\tfor (int i = 0; i < history.Length;i++){\n\t\t\t\t\tConsole.WriteLine (\" {0} {1}: {2}\", i == cursor ? \"==>\" : \"   \", i, history[i]);\n\t\t\t\t}\n\t\t\t\t//log.Flush ();\n\t\t\t}\n\n\t\t\tpublic string SearchBackward (string term)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < count; i++){\n\t\t\t\t\tint slot = cursor-i-1;\n\t\t\t\t\tif (slot < 0)\n\t\t\t\t\t\tslot = history.Length+slot;\n\t\t\t\t\tif (slot >= history.Length)\n\t\t\t\t\t\tslot = 0;\n\t\t\t\t\tif (history [slot] != null && history [slot].IndexOf (term) != -1){\n\t\t\t\t\t\tcursor = slot;\n\t\t\t\t\t\treturn history [slot];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\n#if DEMO\n\tclass Demo {\n\t\tstatic void Main ()\n\t\t{\n\t\t\tLineEditor le = new LineEditor (\"foo\");\n\t\t\tstring s;\n\t\t\t\n\t\t\twhile ((s = le.Edit (\"shell> \", \"\")) != null){\n\t\t\t\tConsole.WriteLine (\"----> [{0}]\", s);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "impls/cs/interop.cs",
    "content": "using System;\nusing System.CodeDom.Compiler;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Microsoft.CSharp;\n\npublic static class EvalProvider\n{\n    public static Func<T, TResult> CreateEvalMethod<T, TResult>(string code, string[] usingStatements = null, string[] assemblies = null)\n    {\n        Type returnType = typeof(TResult);\n        Type inputType = typeof(T);\n\n        var includeUsings = new HashSet<string>(new[] { \"System\" });\n        includeUsings.Add(returnType.Namespace);\n        includeUsings.Add(inputType.Namespace);\n        if (usingStatements != null)\n            foreach (var usingStatement in usingStatements)\n                includeUsings.Add(usingStatement);\n\n        using (CSharpCodeProvider compiler = new CSharpCodeProvider())\n        {\n            var name = \"F\" + Guid.NewGuid().ToString().Replace(\"-\", string.Empty);\n            var includeAssemblies = new HashSet<string>(new[] { \"system.dll\" });\n            if (assemblies != null)\n                foreach (var assembly in assemblies)\n                    includeAssemblies.Add(assembly);\n\n            var parameters = new CompilerParameters(includeAssemblies.ToArray())\n            {\n                GenerateInMemory = true\n            };\n\n            string source = string.Format(@\"\n{0}\nnamespace {1}\n{{\n    public static class EvalClass\n    {{\n        public static {2} Eval({3} arg)\n        {{\n            {4}\n        }}\n    }}\n}}\", GetUsing(includeUsings), name, returnType.Name, inputType.Name, code);\n\n            var compilerResult = compiler.CompileAssemblyFromSource(parameters, source);\n            var compiledAssembly = compilerResult.CompiledAssembly;\n            var type = compiledAssembly.GetType(string.Format(\"{0}.EvalClass\", name));\n            var method = type.GetMethod(\"Eval\");\n            return (Func<T, TResult>)Delegate.CreateDelegate(typeof(Func<T, TResult>), method);\n        }\n    }\n\n    private static string GetUsing(HashSet<string> usingStatements)\n    {\n        StringBuilder result = new StringBuilder();\n        foreach (string usingStatement in usingStatements)\n        {\n            result.AppendLine(string.Format(\"using {0};\", usingStatement));\n        }\n        return result.ToString();\n    }\n}\n\n"
  },
  {
    "path": "impls/cs/printer.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalList = Mal.types.MalList;\n\nnamespace Mal {\n    public class printer {\n        public static string join(List<MalVal> value,\n                                string delim, bool print_readably) {\n            List<string> strs = new List<string>();\n            foreach (MalVal mv in value) {\n                strs.Add(mv.ToString(print_readably));\n            }\n            return String.Join(delim, strs.ToArray());\n        }\n\n        public static string join(Dictionary<string,MalVal> value,\n                                string delim, bool print_readably) {\n            List<string> strs = new List<string>();\n            foreach (KeyValuePair<string, MalVal> entry in value) {\n                if (entry.Key.Length > 0 && entry.Key[0] == '\\u029e') {\n                    strs.Add(\":\" + entry.Key.Substring(1));\n                } else if (print_readably) {\n                    strs.Add(\"\\\"\" + entry.Key.ToString() + \"\\\"\");\n                } else {\n                    strs.Add(entry.Key.ToString());\n                }\n                strs.Add(entry.Value.ToString(print_readably));\n            }\n            return String.Join(delim, strs.ToArray());\n        }\n\n        public static string _pr_str(MalVal mv, bool print_readably) {\n            return mv.ToString(print_readably);\n        }\n\n        public static string _pr_str_args(MalList args, String sep,\n                                          bool print_readably) {\n            return join(args.getValue(), sep, print_readably);\n        }\n\n        public static string escapeString(string str) {\n            return Regex.Escape(str);\n        }\n\n    }\n}\n"
  },
  {
    "path": "impls/cs/reader.cs",
    "content": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalThrowable = Mal.types.MalThrowable;\nusing MalContinue = Mal.types.MalContinue;\n\nnamespace Mal {\n    public class reader {\n        public class ParseError : MalThrowable {\n            public ParseError(string msg) : base(msg) { }\n        }\n\n        public class Reader {\n            List<string> tokens;\n            int position;\n            public Reader(List<string> t) {\n                tokens = t;\n                position = 0;\n            }\n\n            public string peek() {\n                if (position >= tokens.Count) {\n                    return null;\n                } else {\n                    return tokens[position];\n                }\n            }\n            public string next() {\n                return tokens[position++];\n            }\n        }\n\n        public static List<string> tokenize(string str) {\n            List<string> tokens = new List<string>();\n            string pattern = @\"[\\s ,]*(~@|[\\[\\]{}()'`~@]|\"\"(?:[\\\\].|[^\\\\\"\"])*\"\"?|;.*|[^\\s \\[\\]{}()'\"\"`~@,;]*)\";\n            Regex regex = new Regex(pattern);\n            foreach (Match match in regex.Matches(str)) {\n                string token = match.Groups[1].Value;\n                if ((token != null) && !(token == \"\") && !(token[0] == ';')) {\n                    //Console.WriteLine(\"match: ^\" + match.Groups[1] + \"$\");\n                    tokens.Add(token);\n                }\n            }\n            return tokens;\n        }\n\n        public static MalVal read_atom(Reader rdr) {\n            string token = rdr.next();\n            string pattern = @\"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|(^\"\"(?:[\\\\].|[^\\\\\"\"])*\"\"$)|(^\"\".*$)|:(.*)|(^[^\"\"]*$)\";\n            Regex regex = new Regex(pattern);\n            Match match = regex.Match(token);\n            //Console.WriteLine(\"token: ^\" + token + \"$\");\n            if (!match.Success) {\n                throw new ParseError(\"unrecognized token '\" + token + \"'\");\n            }\n            if (match.Groups[1].Value != String.Empty) {\n                return new Mal.types.MalInt(int.Parse(match.Groups[1].Value));\n            } else if (match.Groups[3].Value != String.Empty) {\n                return Mal.types.Nil;\n            } else if (match.Groups[4].Value != String.Empty) {\n                return Mal.types.True;\n            } else if (match.Groups[5].Value != String.Empty) {\n                return Mal.types.False;\n            } else if (match.Groups[6].Value != String.Empty) {\n                string str = match.Groups[6].Value;\n                str = str.Substring(1, str.Length-2)\n                    .Replace(\"\\\\\\\\\",   \"\\u029e\")\n                    .Replace(\"\\\\\\\"\",   \"\\\"\")\n                    .Replace(\"\\\\n\",    \"\\n\")\n                    .Replace(\"\\u029e\", \"\\\\\");\n                return new Mal.types.MalString(str);\n            } else if (match.Groups[7].Value != String.Empty) {\n                throw new ParseError(\"expected '\\\"', got EOF\");\n            } else if (match.Groups[8].Value != String.Empty) {\n                return new Mal.types.MalString(\"\\u029e\" + match.Groups[8].Value);\n            } else if (match.Groups[9].Value != String.Empty) {\n                return new Mal.types.MalSymbol(match.Groups[9].Value);\n            } else {\n                throw new ParseError(\"unrecognized '\" + match.Groups[0] + \"'\");\n            }\n        }\n\n        public static MalVal read_list(Reader rdr, MalList lst, char start, char end) {\n            string token = rdr.next();\n            if (token[0] != start) {\n                throw new ParseError(\"expected '\" + start + \"'\");\n            }\n\n            while ((token = rdr.peek()) != null && token[0] != end) {\n                lst.conj_BANG(read_form(rdr));\n            }\n\n            if (token == null) {\n                throw new ParseError(\"expected '\" + end + \"', got EOF\");\n            }\n            rdr.next();\n\n            return lst;\n        }\n\n        public static MalVal read_hash_map(Reader rdr) {\n            MalList lst = (MalList)read_list(rdr, new MalList(), '{', '}');\n            return new MalHashMap(lst);\n        }\n\n\n        public static MalVal read_form(Reader rdr) {\n            string token = rdr.peek();\n            if (token == null) { throw new MalContinue(); }\n            MalVal form = null;\n\n            switch (token) {\n                case \"'\": rdr.next();\n                    return new MalList(new MalSymbol(\"quote\"),\n                                       read_form(rdr));\n                case \"`\": rdr.next();\n                    return new MalList(new MalSymbol(\"quasiquote\"),\n                                       read_form(rdr));\n                case \"~\":\n                    rdr.next();\n                    return new MalList(new MalSymbol(\"unquote\"),\n                                       read_form(rdr));\n                case \"~@\":\n                    rdr.next();\n                    return new MalList(new MalSymbol(\"splice-unquote\"),\n                                       read_form(rdr));\n                case \"^\": rdr.next();\n                    MalVal meta = read_form(rdr);\n                    return new MalList(new MalSymbol(\"with-meta\"),\n                                       read_form(rdr),\n                                       meta);\n                case \"@\": rdr.next();\n                    return new MalList(new MalSymbol(\"deref\"),\n                                       read_form(rdr));\n\n                case \"(\": form = read_list(rdr, new MalList(), '(' , ')'); break;\n                case \")\": throw new ParseError(\"unexpected ')'\");\n                case \"[\": form = read_list(rdr, new MalVector(), '[' , ']'); break;\n                case \"]\": throw new ParseError(\"unexpected ']'\");\n                case \"{\": form = read_hash_map(rdr); break;\n                case \"}\": throw new ParseError(\"unexpected '}'\");\n                default:  form = read_atom(rdr); break;\n            }\n            return form;\n        }\n\n\n        public static MalVal read_str(string str) {\n            return read_form(new Reader(tokenize(str)));\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/readline.cs",
    "content": "using System;\nusing Mono.Terminal; // LineEditor (getline.cs)\n\nnamespace Mal {\n    public class readline {\n        public enum Mode { Terminal, Raw };\n        public static Mode mode = Mode.Terminal;\n\n        static LineEditor lineedit = null;\n\n        public static string Readline(string prompt) {\n            if (mode == Mode.Terminal) {\n                if (lineedit == null) {\n                    lineedit = new LineEditor(\"Mal\");\n                }\n                return lineedit.Edit(prompt, \"\");\n            } else {\n                Console.Write(prompt);\n                Console.Out.Flush();\n                return Console.ReadLine();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/run",
    "content": "#!/usr/bin/env bash\nexec mono $(dirname $0)/${STEP:-stepA_mal}.exe ${RAW:+--raw} \"${@}\"\n"
  },
  {
    "path": "impls/cs/step0_repl.cs",
    "content": "using System;\nusing System.IO;\nusing Mal;\n\nnamespace Mal {\n    class step0_repl {\n        // read\n        static string READ(string str) {\n            return str;\n        }\n\n        // eval\n        static string EVAL(string ast, string env) {\n            return ast;\n        }\n\n        // print\n        static string PRINT(string exp) {\n            return exp;\n        }\n\n        // repl\n        static string RE(string env, string str) {\n            return EVAL(READ(str), env);\n        }\n\n        static void Main(string[] args) {\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                Console.WriteLine(PRINT(RE(null, line)));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step1_read_print.cs",
    "content": "using System;\nusing System.IO;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\n\nnamespace Mal {\n    class step1_read_print {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        static MalVal EVAL(MalVal ast, string env) {\n            return ast;\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), \"\");\n\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step2_eval.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\n\nnamespace Mal {\n    class step2_eval {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        static MalVal EVAL(MalVal orig_ast, Dictionary<string, MalVal> env) {\n            MalVal a0;\n            // Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                MalSymbol sym = (MalSymbol)orig_ast;\n                return (MalVal)env[sym.getName()];\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n            if (!(a0 is MalSymbol)) {\n                throw new Mal.types.MalError(\"attempt to apply on non-symbol '\"\n                        + Mal.printer._pr_str(a0,true) + \"'\");\n            }\n            MalFunc f = (MalFunc)EVAL(ast[0], env);\n            MalList arguments = new MalList();\n            foreach (MalVal mv in ast.rest().getValue()) {\n                arguments.conj_BANG(EVAL(mv, env));\n            }\n            return f.apply(arguments);\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Dictionary<string, MalVal> {\n                {\"+\", new MalFunc(a => (MalInt)a[0] + (MalInt)a[1]) },\n                {\"-\", new MalFunc(a => (MalInt)a[0] - (MalInt)a[1]) },\n                {\"*\", new MalFunc(a => (MalInt)a[0] * (MalInt)a[1]) },\n                {\"/\", new MalFunc(a => (MalInt)a[0] / (MalInt)a[1]) },\n            };\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step3_env.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step3_env {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n            if (!(a0 is MalSymbol)) {\n                throw new Mal.types.MalError(\"attempt to apply on non-symbol '\"\n                        + Mal.printer._pr_str(a0,true) + \"'\");\n            }\n\n            switch (((MalSymbol)a0).getName()) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                return EVAL(a2, let_env);\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                return f.apply(arguments);\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            repl_env.set(new MalSymbol(\"+\"), new MalFunc(\n                        a => (MalInt)a[0] + (MalInt)a[1]) );\n            repl_env.set(new MalSymbol(\"-\"), new MalFunc(\n                        a => (MalInt)a[0] - (MalInt)a[1]) );\n            repl_env.set(new MalSymbol(\"*\"), new MalFunc(\n                        a => (MalInt)a[0] * (MalInt)a[1]) );\n            repl_env.set(new MalSymbol(\"/\"), new MalFunc(\n                        a => (MalInt)a[0] / (MalInt)a[1]) );\n\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step4_if_fn_do.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step4_if_fn_do {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                return EVAL(a2, let_env);\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                return EVAL(ast[ast.size()-1], env);\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        return EVAL(ast[3], env);\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    a2 = ast[2];\n                    return EVAL(a2, env);\n                }\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                return f.apply(arguments);\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n\n            // core.mal: defined using the language itself\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step5_tco.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step5_tco {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            while (true) {\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                orig_ast = a2;\n                env = let_env;\n                break;\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                orig_ast = ast[ast.size()-1];\n                break;\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        orig_ast = ast[3];\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    orig_ast = ast[2];\n                }\n                break;\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(a2f, env, a1f,\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                MalVal fnast = f.getAst();\n                if (fnast != null) {\n                    orig_ast = fnast;\n                    env = f.genEnv(arguments);\n                } else {\n                    return f.apply(arguments);\n                }\n                break;\n            }\n\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n\n            // core.mal: defined using the language itself\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step6_file.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalString = Mal.types.MalString;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step6_file {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            while (true) {\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                orig_ast = a2;\n                env = let_env;\n                break;\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                orig_ast = ast[ast.size()-1];\n                break;\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        orig_ast = ast[3];\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    orig_ast = ast[2];\n                }\n                break;\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(a2f, env, a1f,\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                MalVal fnast = f.getAst();\n                if (fnast != null) {\n                    orig_ast = fnast;\n                    env = f.genEnv(arguments);\n                } else {\n                    return f.apply(arguments);\n                }\n                break;\n            }\n\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n            repl_env.set(new MalSymbol(\"eval\"), new MalFunc(\n                        a => EVAL(a[0], repl_env)));\n            int fileIdx = 0;\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n                fileIdx = 1;\n            }\n            MalList _argv = new MalList();\n            for (int i=fileIdx+1; i < args.Length; i++) {\n                _argv.conj_BANG(new MalString(args[i]));\n            }\n            repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n            // core.mal: defined using the language itself\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n            RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\n            if (args.Length > fileIdx) {\n                RE(\"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n                return;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step7_quote.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalString = Mal.types.MalString;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step7_quote {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        public static bool starts_with(MalVal ast, string sym) {\n            if (ast is MalList && !(ast is MalVector)) {\n                MalList list = (MalList)ast;\n                if (list.size() == 2 && list[0] is MalSymbol) {\n                    MalSymbol a0 = (MalSymbol)list[0];\n                    return a0.getName() == sym;\n                }\n            }\n            return false;\n        }\n\n        public static MalVal qq_loop(MalList ast) {\n            MalVal acc = new MalList();\n            for(int i=ast.size()-1; 0<=i; i-=1) {\n                MalVal elt = ast[i];\n                if (starts_with(elt, \"splice-unquote\")) {\n                    acc = new MalList(new MalSymbol(\"concat\"), ((MalList)elt)[1], acc);\n                } else {\n                    acc = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), acc);\n                }\n            }\n            return acc;\n        }\n        public static MalVal quasiquote(MalVal ast) {\n            //  Check Vector subclass before List.\n            if (ast is MalVector) {\n                return new MalList(new MalSymbol(\"vec\"), qq_loop(((MalList)ast)));\n            } else if (starts_with(ast, \"unquote\")) {\n                return ((MalList)ast)[1];\n            } else if (ast is MalList) {\n                return qq_loop((MalList)ast);\n            } else if (ast is MalSymbol || ast is MalHashMap) {\n                return new MalList(new MalSymbol(\"quote\"), ast);\n            } else {\n                return ast;\n            }\n        }\n\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            while (true) {\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                orig_ast = a2;\n                env = let_env;\n                break;\n            case \"quote\":\n                return ast[1];\n            case \"quasiquote\":\n                orig_ast = quasiquote(ast[1]);\n                break;\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                orig_ast = ast[ast.size()-1];\n                break;\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        orig_ast = ast[3];\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    orig_ast = ast[2];\n                }\n                break;\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(a2f, env, a1f,\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                MalVal fnast = f.getAst();\n                if (fnast != null) {\n                    orig_ast = fnast;\n                    env = f.genEnv(arguments);\n                } else {\n                    return f.apply(arguments);\n                }\n                break;\n            }\n\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n            repl_env.set(new MalSymbol(\"eval\"), new MalFunc(\n                        a => EVAL(a[0], repl_env)));\n            int fileIdx = 0;\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n                fileIdx = 1;\n            }\n            MalList _argv = new MalList();\n            for (int i=fileIdx+1; i < args.Length; i++) {\n                _argv.conj_BANG(new MalString(args[i]));\n            }\n            repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n            // core.mal: defined using the language itself\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n            RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\n            if (args.Length > fileIdx) {\n                RE(\"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n                return;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step8_macros.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalString = Mal.types.MalString;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step8_macros {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        public static bool starts_with(MalVal ast, string sym) {\n            if (ast is MalList && !(ast is MalVector)) {\n                MalList list = (MalList)ast;\n                if (list.size() == 2 && list[0] is MalSymbol) {\n                    MalSymbol a0 = (MalSymbol)list[0];\n                    return a0.getName() == sym;\n                }\n            }\n            return false;\n        }\n\n        public static MalVal qq_loop(MalList ast) {\n            MalVal acc = new MalList();\n            for(int i=ast.size()-1; 0<=i; i-=1) {\n                MalVal elt = ast[i];\n                if (starts_with(elt, \"splice-unquote\")) {\n                    acc = new MalList(new MalSymbol(\"concat\"), ((MalList)elt)[1], acc);\n                } else {\n                    acc = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), acc);\n                }\n            }\n            return acc;\n        }\n        public static MalVal quasiquote(MalVal ast) {\n            //  Check Vector subclass before List.\n            if (ast is MalVector) {\n                return new MalList(new MalSymbol(\"vec\"), qq_loop(((MalList)ast)));\n            } else if (starts_with(ast, \"unquote\")) {\n                return ((MalList)ast)[1];\n            } else if (ast is MalList) {\n                return qq_loop((MalList)ast);\n            } else if (ast is MalSymbol || ast is MalHashMap) {\n                return new MalList(new MalSymbol(\"quote\"), ast);\n            } else {\n                return ast;\n            }\n        }\n\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            while (true) {\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                orig_ast = a2;\n                env = let_env;\n                break;\n            case \"quote\":\n                return ast[1];\n            case \"quasiquote\":\n                orig_ast = quasiquote(ast[1]);\n                break;\n            case \"defmacro!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                res = res.copy();\n                ((MalFunc)res).setMacro();\n                env.set(((MalSymbol)a1), res);\n                return res;\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                orig_ast = ast[ast.size()-1];\n                break;\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        orig_ast = ast[3];\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    orig_ast = ast[2];\n                }\n                break;\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(a2f, env, a1f,\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                if (f.isMacro()) {\n                    orig_ast = f.apply(ast.rest());\n                    break;\n                }\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                MalVal fnast = f.getAst();\n                if (fnast != null) {\n                    orig_ast = fnast;\n                    env = f.genEnv(arguments);\n                } else {\n                    return f.apply(arguments);\n                }\n                break;\n            }\n\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n            repl_env.set(new MalSymbol(\"eval\"), new MalFunc(\n                        a => EVAL(a[0], repl_env)));\n            int fileIdx = 0;\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n                fileIdx = 1;\n            }\n            MalList _argv = new MalList();\n            for (int i=fileIdx+1; i < args.Length; i++) {\n                _argv.conj_BANG(new MalString(args[i]));\n            }\n            repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n            // core.mal: defined using the language itself\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n            RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n            RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n            if (args.Length > fileIdx) {\n                RE(\"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n                return;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/step9_try.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalString = Mal.types.MalString;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class step9_try {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        public static bool starts_with(MalVal ast, string sym) {\n            if (ast is MalList && !(ast is MalVector)) {\n                MalList list = (MalList)ast;\n                if (list.size() == 2 && list[0] is MalSymbol) {\n                    MalSymbol a0 = (MalSymbol)list[0];\n                    return a0.getName() == sym;\n                }\n            }\n            return false;\n        }\n\n        public static MalVal qq_loop(MalList ast) {\n            MalVal acc = new MalList();\n            for(int i=ast.size()-1; 0<=i; i-=1) {\n                MalVal elt = ast[i];\n                if (starts_with(elt, \"splice-unquote\")) {\n                    acc = new MalList(new MalSymbol(\"concat\"), ((MalList)elt)[1], acc);\n                } else {\n                    acc = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), acc);\n                }\n            }\n            return acc;\n        }\n        public static MalVal quasiquote(MalVal ast) {\n            //  Check Vector subclass before List.\n            if (ast is MalVector) {\n                return new MalList(new MalSymbol(\"vec\"), qq_loop(((MalList)ast)));\n            } else if (starts_with(ast, \"unquote\")) {\n                return ((MalList)ast)[1];\n            } else if (ast is MalList) {\n                return qq_loop((MalList)ast);\n            } else if (ast is MalSymbol || ast is MalHashMap) {\n                return new MalList(new MalSymbol(\"quote\"), ast);\n            } else {\n                return ast;\n            }\n        }\n\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            while (true) {\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                orig_ast = a2;\n                env = let_env;\n                break;\n            case \"quote\":\n                return ast[1];\n            case \"quasiquote\":\n                orig_ast = quasiquote(ast[1]);\n                break;\n            case \"defmacro!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                res = res.copy();\n                ((MalFunc)res).setMacro();\n                env.set(((MalSymbol)a1), res);\n                return res;\n            case \"try*\":\n                try {\n                    return EVAL(ast[1], env);\n                } catch (Exception e) {\n                    if (ast.size() > 2) {\n                        MalVal exc;\n                        a2 = ast[2];\n                        MalVal a20 = ((MalList)a2)[0];\n                        if (((MalSymbol)a20).getName() == \"catch*\") {\n                            if (e is Mal.types.MalException) {\n                                exc = ((Mal.types.MalException)e).getValue();\n                            } else {\n                                exc = new MalString(e.Message);\n                            }\n                            return EVAL(((MalList)a2)[2],\n                                        new Env(env, ((MalList)a2).slice(1,2),\n                                                new MalList(exc)));\n                        }\n                    }\n                    throw e;\n                }\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                orig_ast = ast[ast.size()-1];\n                break;\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        orig_ast = ast[3];\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    orig_ast = ast[2];\n                }\n                break;\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(a2f, env, a1f,\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                if (f.isMacro()) {\n                    orig_ast = f.apply(ast.rest());\n                    break;\n                }\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                MalVal fnast = f.getAst();\n                if (fnast != null) {\n                    orig_ast = fnast;\n                    env = f.genEnv(arguments);\n                } else {\n                    return f.apply(arguments);\n                }\n                break;\n            }\n\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n            repl_env.set(new MalSymbol(\"eval\"), new MalFunc(\n                        a => EVAL(a[0], repl_env)));\n            int fileIdx = 0;\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n                fileIdx = 1;\n            }\n            MalList _argv = new MalList();\n            for (int i=fileIdx+1; i < args.Length; i++) {\n                _argv.conj_BANG(new MalString(args[i]));\n            }\n            repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n            // core.mal: defined using the language itself\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n            RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n            RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n            if (args.Length > fileIdx) {\n                RE(\"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n                return;\n            }\n\n            // repl loop\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Mal.types.MalException e) {\n                    Console.WriteLine(\"Error: \" +\n                            printer._pr_str(e.getValue(), false));\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/stepA_mal.cs",
    "content": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing Mal;\nusing MalVal = Mal.types.MalVal;\nusing MalString = Mal.types.MalString;\nusing MalSymbol = Mal.types.MalSymbol;\nusing MalInt = Mal.types.MalInt;\nusing MalList = Mal.types.MalList;\nusing MalVector = Mal.types.MalVector;\nusing MalHashMap = Mal.types.MalHashMap;\nusing MalFunc = Mal.types.MalFunc;\nusing Env = Mal.env.Env;\n\nnamespace Mal {\n    class stepA_mal {\n        // read\n        static MalVal READ(string str) {\n            return reader.read_str(str);\n        }\n\n        // eval\n        public static bool starts_with(MalVal ast, string sym) {\n            if (ast is MalList && !(ast is MalVector)) {\n                MalList list = (MalList)ast;\n                if (list.size() == 2 && list[0] is MalSymbol) {\n                    MalSymbol a0 = (MalSymbol)list[0];\n                    return a0.getName() == sym;\n                }\n            }\n            return false;\n        }\n\n        public static MalVal qq_loop(MalList ast) {\n            MalVal acc = new MalList();\n            for(int i=ast.size()-1; 0<=i; i-=1) {\n                MalVal elt = ast[i];\n                if (starts_with(elt, \"splice-unquote\")) {\n                    acc = new MalList(new MalSymbol(\"concat\"), ((MalList)elt)[1], acc);\n                } else {\n                    acc = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), acc);\n                }\n            }\n            return acc;\n        }\n        public static MalVal quasiquote(MalVal ast) {\n            //  Check Vector subclass before List.\n            if (ast is MalVector) {\n                return new MalList(new MalSymbol(\"vec\"), qq_loop(((MalList)ast)));\n            } else if (starts_with(ast, \"unquote\")) {\n                return ((MalList)ast)[1];\n            } else if (ast is MalList) {\n                return qq_loop((MalList)ast);\n            } else if (ast is MalSymbol || ast is MalHashMap) {\n                return new MalList(new MalSymbol(\"quote\"), ast);\n            } else {\n                return ast;\n            }\n        }\n\n        static MalVal EVAL(MalVal orig_ast, Env env) {\n            MalVal a0, a1, a2, res;\n            while (true) {\n            MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n            if (dbgeval != null && dbgeval != Mal.types.Nil\n                && dbgeval != Mal.types.False)\n                Console.WriteLine(\"EVAL: \" + printer._pr_str(orig_ast, true));\n            if (orig_ast is MalSymbol) {\n                string key = ((MalSymbol)orig_ast).getName();\n                res = env.get(key);\n                if (res == null)\n                    throw new Mal.types.MalException(\"'\" + key + \"' not found\");\n                return res;\n            } else if (orig_ast is MalVector) {\n                MalVector old_lst = (MalVector)orig_ast;\n                MalVector new_lst = new MalVector();\n                foreach (MalVal mv in old_lst.getValue()) {\n                    new_lst.conj_BANG(EVAL(mv, env));\n                }\n                return new_lst;\n            } else if (orig_ast is MalHashMap) {\n                var new_dict = new Dictionary<string, MalVal>();\n                foreach (var entry in ((MalHashMap)orig_ast).getValue()) {\n                    new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));\n                }\n                return new MalHashMap(new_dict);\n            } else if (!(orig_ast is MalList)) {\n                return orig_ast;\n            }\n\n            // apply list\n            MalList ast = (MalList) orig_ast;\n\n            if (ast.size() == 0) { return ast; }\n            a0 = ast[0];\n\n            String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()\n                                           : \"__<*fn*>__\";\n\n            switch (a0sym) {\n            case \"def!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                env.set((MalSymbol)a1, res);\n                return res;\n            case \"let*\":\n                a1 = ast[1];\n                a2 = ast[2];\n                MalSymbol key;\n                MalVal val;\n                Env let_env = new Env(env);\n                for(int i=0; i<((MalList)a1).size(); i+=2) {\n                    key = (MalSymbol)((MalList)a1)[i];\n                    val = ((MalList)a1)[i+1];\n                    let_env.set(key, EVAL(val, let_env));\n                }\n                orig_ast = a2;\n                env = let_env;\n                break;\n            case \"quote\":\n                return ast[1];\n            case \"quasiquote\":\n                orig_ast = quasiquote(ast[1]);\n                break;\n            case \"defmacro!\":\n                a1 = ast[1];\n                a2 = ast[2];\n                res = EVAL(a2, env);\n                res = res.copy();\n                ((MalFunc)res).setMacro();\n                env.set(((MalSymbol)a1), res);\n                return res;\n            case \"try*\":\n                try {\n                    return EVAL(ast[1], env);\n                } catch (Exception e) {\n                    if (ast.size() > 2) {\n                        MalVal exc;\n                        a2 = ast[2];\n                        MalVal a20 = ((MalList)a2)[0];\n                        if (((MalSymbol)a20).getName() == \"catch*\") {\n                            if (e is Mal.types.MalException) {\n                                exc = ((Mal.types.MalException)e).getValue();\n                            } else {\n                                exc = new MalString(e.Message);\n                            }\n                            return EVAL(((MalList)a2)[2],\n                                        new Env(env, ((MalList)a2).slice(1,2),\n                                                new MalList(exc)));\n                        }\n                    }\n                    throw e;\n                }\n            case \"do\":\n                foreach (MalVal mv in ast.slice(1, ast.size()-1).getValue()) {\n                    EVAL(mv, env);\n                }\n                orig_ast = ast[ast.size()-1];\n                break;\n            case \"if\":\n                a1 = ast[1];\n                MalVal cond = EVAL(a1, env);\n                if (cond == Mal.types.Nil || cond == Mal.types.False) {\n                    // eval false slot form\n                    if (ast.size() > 3) {\n                        orig_ast = ast[3];\n                    } else {\n                        return Mal.types.Nil;\n                    }\n                } else {\n                    // eval true slot form\n                    orig_ast = ast[2];\n                }\n                break;\n            case \"fn*\":\n                MalList a1f = (MalList)ast[1];\n                MalVal a2f = ast[2];\n                Env cur_env = env;\n                return new MalFunc(a2f, env, a1f,\n                    args => EVAL(a2f, new Env(cur_env, a1f, args)) );\n            default:\n                MalFunc f = (MalFunc)EVAL(ast[0], env);\n                if (f.isMacro()) {\n                    orig_ast = f.apply(ast.rest());\n                    break;\n                }\n                MalList arguments = new MalList();\n                foreach (MalVal mv in ast.rest().getValue()) {\n                    arguments.conj_BANG(EVAL(mv, env));\n                }\n                MalVal fnast = f.getAst();\n                if (fnast != null) {\n                    orig_ast = fnast;\n                    env = f.genEnv(arguments);\n                } else {\n                    return f.apply(arguments);\n                }\n                break;\n            }\n\n            }\n        }\n\n        // print\n        static string PRINT(MalVal exp) {\n            return printer._pr_str(exp, true);\n        }\n\n        // repl\n        static void Main(string[] args) {\n            var repl_env = new Mal.env.Env(null);\n            Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);\n            \n            // core.cs: defined using C#\n            foreach (var entry in core.ns) {\n                repl_env.set(new MalSymbol(entry.Key), entry.Value);\n            }\n            repl_env.set(new MalSymbol(\"eval\"), new MalFunc(\n                        a => EVAL(a[0], repl_env)));\n            int fileIdx = 0;\n            if (args.Length > 0 && args[0] == \"--raw\") {\n                Mal.readline.mode = Mal.readline.Mode.Raw;\n                fileIdx = 1;\n            }\n            MalList _argv = new MalList();\n            for (int i=fileIdx+1; i < args.Length; i++) {\n                _argv.conj_BANG(new MalString(args[i]));\n            }\n            repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n            // core.mal: defined using the language itself\n            RE(\"(def! *host-language* \\\"c#\\\")\");\n            RE(\"(def! not (fn* (a) (if a false true)))\");\n            RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n            RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n            if (args.Length > fileIdx) {\n                RE(\"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n                return;\n            }\n\n            // repl loop\n            RE(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n            while (true) {\n                string line;\n                try {\n                    line = Mal.readline.Readline(\"user> \");\n                    if (line == null) { break; }\n                    if (line == \"\") { continue; }\n                } catch (IOException e) {\n                    Console.WriteLine(\"IOException: \" + e.Message);\n                    break;\n                }\n                try {\n                    Console.WriteLine(PRINT(RE(line)));\n                } catch (Mal.types.MalContinue) {\n                    continue;\n                } catch (Mal.types.MalException e) {\n                    Console.WriteLine(\"Error: \" +\n                            printer._pr_str(e.getValue(), false));\n                    continue;\n                } catch (Exception e) {\n                    Console.WriteLine(\"Error: \" + e.Message);\n                    Console.WriteLine(e.StackTrace);\n                    continue;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/cs/tests/step5_tco.mal",
    "content": ";; C#: skipping non-TCO recursion\n;; Reason: unrecoverable stack overflow at 10,000\n"
  },
  {
    "path": "impls/cs/types.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing Mal;\n\nnamespace Mal {\n    public class types {\n        //\n        // Exceptions/Errors\n        //\n        public class MalThrowable : Exception {\n            public MalThrowable() : base() { }\n            public MalThrowable(string msg) : base(msg) {  }\n        }\n        public class MalError : MalThrowable {\n            public MalError(string msg) :base(msg) { }\n        }\n        public class MalContinue : MalThrowable { }\n\n        // Thrown by throw function\n        public class MalException : MalThrowable {\n            MalVal value;\n            //string Message;\n            public MalException(MalVal value) {\n                this.value = value;\n            }\n            public MalException(string value) :base(value) {\n                this.value = new MalString(value);\n            }\n            public MalVal getValue() { return value; }\n        }\n\n        //\n        // General functions\n        //\n\n        public static bool _equal_Q(MalVal a, MalVal b) {\n            Type ota = a.GetType(), otb = b.GetType();\n            if (!((ota == otb) ||\n                (a is MalList && b is MalList))) {\n                return false;\n            } else {\n                if (a is MalInt) {\n                    return ((MalInt)a).getValue() ==\n                        ((MalInt)b).getValue();\n                } else if (a is MalSymbol) {\n                    return ((MalSymbol)a).getName() ==\n                        ((MalSymbol)b).getName();\n                } else if (a is MalString) {\n                    return ((MalString)a).getValue() ==\n                        ((MalString)b).getValue();\n                } else if (a is MalList) {\n                    if (((MalList)a).size() != ((MalList)b).size()) {\n                        return false;\n                    }\n                    for (int i=0; i<((MalList)a).size(); i++) {\n                        if (! _equal_Q(((MalList)a)[i], ((MalList)b)[i])) {\n                            return false;\n                        }\n                    }\n                    return true;\n                } else if (a is MalHashMap) {\n                    var akeys = ((MalHashMap)a).getValue().Keys;\n                    var bkeys = ((MalHashMap)b).getValue().Keys;\n                    if (akeys.Count != bkeys.Count) {\n                        return false;\n                    }\n                    foreach (var k in akeys) {\n                        if (!_equal_Q(((MalHashMap)a).getValue()[k],\n                                      ((MalHashMap)b).getValue()[k])) {\n                            return false;\n                        }\n                    }\n                    return true;\n                } else {\n                    return a == b;\n                }\n            }\n        }\n\n\n        public abstract class MalVal {\n            MalVal meta = Nil;\n            public virtual MalVal copy() {\n                return (MalVal)this.MemberwiseClone();\n            }\n\n            // Default is just to call regular toString()\n            public virtual string ToString(bool print_readably) {\n                return this.ToString();\n            }\n            public MalVal getMeta() { return meta; }\n            public MalVal setMeta(MalVal m) { meta = m; return this; }\n            public virtual bool list_Q() { return false; }\n        }\n\n        public class MalConstant : MalVal {\n            string value;\n            public MalConstant(string name) { value = name; }\n            public new MalConstant copy() { return this; }\n\n            public override string ToString() {\n                return value;\n            }\n            public override string ToString(bool print_readably) {\n                return value;\n            }\n        }\n\n        static public MalConstant Nil = new MalConstant(\"nil\");\n        static public MalConstant True = new MalConstant(\"true\");\n        static public MalConstant False = new MalConstant(\"false\");\n\n        public class MalInt : MalVal {\n            Int64 value;\n            public MalInt(Int64 v) { value = v; }\n            public new MalInt copy() { return this; }\n\n            public Int64 getValue() { return value; }\n            public override string ToString() {\n                return value.ToString();\n            }\n            public override string ToString(bool print_readably) {\n                return value.ToString();\n            }\n            public static MalConstant operator <(MalInt a, MalInt b) {\n                return a.getValue() < b.getValue() ? True : False;\n            }\n            public static MalConstant operator <=(MalInt a, MalInt b) {\n                return a.getValue() <= b.getValue() ? True : False;\n            }\n            public static MalConstant operator >(MalInt a, MalInt b) {\n                return a.getValue() > b.getValue() ? True : False;\n            }\n            public static MalConstant operator >=(MalInt a, MalInt b) {\n                return a.getValue() >= b.getValue() ? True : False;\n            }\n            public static MalInt operator +(MalInt a, MalInt b) {\n                return new MalInt(a.getValue() + b.getValue());\n            }\n            public static MalInt operator -(MalInt a, MalInt b) {\n                return new MalInt(a.getValue() - b.getValue());\n            }\n            public static MalInt operator *(MalInt a, MalInt b) {\n                return new MalInt(a.getValue() * b.getValue());\n            }\n            public static MalInt operator /(MalInt a, MalInt b) {\n                return new MalInt(a.getValue() / b.getValue());\n            }\n        }\n\n        public class MalSymbol : MalVal {\n            string value;\n            public MalSymbol(string v) { value = v; }\n            public MalSymbol(MalString v) { value = v.getValue(); }\n            public new MalSymbol copy() { return this; }\n\n            public string getName() { return value; }\n            public override string ToString() {\n                return value;\n            }\n            public override string ToString(bool print_readably) {\n                return value;\n            }\n        }\n\n        public class MalString : MalVal {\n            string value;\n            public MalString(string v) { value = v; }\n            public new MalString copy() { return this; }\n\n            public string getValue() { return value; }\n            public override string ToString() {\n                return \"\\\"\" + value + \"\\\"\";\n            }\n            public override string ToString(bool print_readably) {\n                if (value.Length > 0 && value[0] == '\\u029e') {\n                    return \":\" + value.Substring(1);\n                } else if (print_readably) {\n                    return \"\\\"\" + value.Replace(\"\\\\\", \"\\\\\\\\\")\n                        .Replace(\"\\\"\", \"\\\\\\\"\")\n                        .Replace(\"\\n\", \"\\\\n\") + \"\\\"\";\n                } else {\n                    return value;\n                }\n            }\n        }\n\n\n\n        public class MalList : MalVal {\n            public string start = \"(\", end = \")\";\n            List<MalVal> value;\n            public MalList() {\n                value = new List<MalVal>();\n            }\n            public MalList(List<MalVal> val) {\n                value = val;\n            }\n            public MalList(params MalVal[] mvs) {\n                value = new List<MalVal>();\n                conj_BANG(mvs);\n            }\n\n            public List<MalVal> getValue() { return value; }\n            public override bool list_Q() { return true; }\n\n            public override string ToString() {\n                return start + printer.join(value, \" \", true) + end;\n            }\n            public override string ToString(bool print_readably) {\n                return start + printer.join(value, \" \", print_readably) + end;\n            }\n\n            public MalList conj_BANG(params MalVal[] mvs) {\n                for (int i = 0; i < mvs.Length; i++) {\n                    value.Add(mvs[i]);\n                }\n                return this;\n            }\n\n            public int size() { return value.Count; }\n            public MalVal nth(int idx) {\n                return value.Count > idx ? value[idx] : Nil;\n            }\n            public MalVal this[int idx] {\n                get { return value.Count > idx ? value[idx] : Nil; }\n            }\n            public MalList rest() {\n                if (size() > 0) {\n                    return new MalList(value.GetRange(1, value.Count-1));\n                } else {\n                    return new MalList();\n                }\n            }\n            public virtual MalList slice(int start) {\n                return new MalList(value.GetRange(start, value.Count-start));\n            }\n            public virtual MalList slice(int start, int end) {\n                return new MalList(value.GetRange(start, end-start));\n            }\n\n        }\n\n        public class MalVector : MalList {\n            // Same implementation except for instantiation methods\n            public MalVector() :base() {\n                start = \"[\";\n                end = \"]\";\n            }\n            public MalVector(List<MalVal> val)\n                    :base(val) {\n                start = \"[\";\n                end = \"]\";\n            }\n\n            public override bool list_Q() { return false; }\n\n            public override MalList slice(int start, int end) {\n                var val = this.getValue();\n                return new MalVector(val.GetRange(start, val.Count-start));\n            }\n        }\n\n        public class MalHashMap : MalVal {\n            Dictionary<string, MalVal> value;\n            public MalHashMap(Dictionary<string, MalVal> val) {\n                value = val;\n            }\n            public MalHashMap(MalList lst) {\n                value = new Dictionary<String, MalVal>();\n                assoc_BANG(lst);\n            }\n            public new MalHashMap copy() {\n                var new_self = (MalHashMap)this.MemberwiseClone();\n                new_self.value = new Dictionary<string, MalVal>(value);\n                return new_self;\n            }\n\n            public Dictionary<string, MalVal> getValue() { return value; }\n\n            public override string ToString() {\n                return \"{\" + printer.join(value, \" \", true) + \"}\";\n            }\n            public override string ToString(bool print_readably) {\n                return \"{\" + printer.join(value, \" \", print_readably) + \"}\";\n            }\n\n            public MalHashMap assoc_BANG(MalList lst) {\n                for (int i=0; i<lst.size(); i+=2) {\n                    value[((MalString)lst[i]).getValue()] = lst[i+1];\n                }\n                return this;\n            }\n\n            public MalHashMap dissoc_BANG(MalList lst) {\n                for (int i=0; i<lst.size(); i++) {\n                    value.Remove(((MalString)lst[i]).getValue());\n                }\n                return this;\n            }\n        }\n\n        public class MalAtom : MalVal {\n            MalVal value;\n            public MalAtom(MalVal value) { this.value = value; }\n            //public MalAtom copy() { return new MalAtom(value); }\n            public MalVal getValue() { return value; }\n            public MalVal setValue(MalVal value) { return this.value = value; }\n            public override string ToString() {\n                return \"(atom \" + printer._pr_str(value, true) + \")\";\n            }\n            public override string ToString(Boolean print_readably) {\n                return \"(atom \" + printer._pr_str(value, print_readably) + \")\";\n            }\n        }\n\n        public class MalFunc : MalVal {\n            Func<MalList, MalVal> fn = null;\n            MalVal ast = null;\n            Mal.env.Env env = null;\n            MalList fparams;\n            bool macro = false;\n            public MalFunc(Func<MalList, MalVal> fn) {\n                this.fn = fn;\n            }\n            public MalFunc(MalVal ast, Mal.env.Env env, MalList fparams,\n                               Func<MalList, MalVal> fn) {\n                this.fn = fn;\n                this.ast = ast;\n                this.env = env;\n                this.fparams = fparams;\n            }\n\n            public override string ToString() {\n                if (ast != null) {\n                    return \"<fn* \" + Mal.printer._pr_str(fparams,true) +\n                           \" \" + Mal.printer._pr_str(ast, true) + \">\";\n                } else {\n                    return \"<builtin_function \" + fn.ToString() + \">\";\n                }\n            }\n\n            public MalVal apply(MalList args) {\n                return fn(args);\n            }\n\n            public MalVal getAst() { return ast; }\n            public Mal.env.Env getEnv() { return env; }\n            public MalList getFParams() { return fparams; }\n            public Mal.env.Env genEnv(MalList args) {\n                return new Mal.env.Env(env, fparams, args);\n            }\n            public bool isMacro() { return macro; }\n            public void setMacro() { macro = true; }\n\n        }\n    }\n}\n"
  },
  {
    "path": "impls/d/Dockerfile",
    "content": "FROM ubuntu:bionic\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install gcc gdc ldc gpg wget\n\nRUN wget https://dlang.org/install.sh -q -O install.sh && \\\n    bash install.sh -p /usr/local/dlang && \\\n    chmod 755 /usr/local/dlang/dmd* && \\\n    ln -sf /usr/local/dlang/dmd-*/linux/bin64/dmd /usr/bin/dmd\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/d/Makefile",
    "content": "d_MODE ?= gdc\n\nD ?= $(d_MODE)\n\nifeq ($(D),gdc)\nCFLAGS += -g -O2 -Wall\nLDFLAGS += -lreadline\nOF = -o $@\nelse ifeq ($(D),ldc2)\nCFLAGS += -g -O2\nLDFLAGS += -L-lreadline\nOF = -of $@\nelse ifeq ($(D),dmd)\nCFLAGS += -g -O\nLDFLAGS += -L-lreadline\nOF = -of=$@\nelse\n\t@echo \"Unsupported D implementation $(D)\"\n\t@exit 1\nendif\n\n#####################\n\nEARLY_SRCS = step0_repl.d step1_read_print.d step2_eval.d\nLATE_SRCS = step3_env.d step4_if_fn_do.d step5_tco.d step6_file.d \\\n\t    step7_quote.d step8_macros.d step9_try.d stepA_mal.d\nSRCS = $(EARLY_SRCS) $(LATE_SRCS)\nOBJS = $(SRCS:%.d=%.o)\nBINS = $(OBJS:%.o=%)\nEARLY_OBJS = types.o readline.o reader.o printer.o env.o\nOTHER_OBJS = $(EARLY_OBJS) mal_core.o\nEARLY_STEPS_BINS = $(EARLY_SRCS:%.d=%)\nLATE_STEPS_BINS = $(LATE_SRCS:%.d=%)\n\n#####################\n\nall: $(BINS)\n\ndist: mal\n\nmal: $(word $(words $(BINS)),$(BINS))\n\tcp $< $@\n\n$(OBJS) $(OTHER_OBJS): %.o: %.d\n\t$(D) $(CFLAGS) -c $(@:%.o=%.d) $(OF)\n\n$(EARLY_STEPS_BINS): $(EARLY_OBJS)\n$(LATE_STEPS_BINS): $(OTHER_OBJS)\n\n$(BINS): %: %.o\n\t$(D) $+ $(OF) $(LDFLAGS)\n\nclean:\n\trm -f $(OBJS) $(BINS) $(OTHER_OBJS) mal\n"
  },
  {
    "path": "impls/d/env.d",
    "content": "import types;\n\nclass Env {\n    Env outer;\n    MalType[string] data;\n\n    this(Env outer_v, MalType[] binds = [], MalType[] exprs = [])\n    {\n        outer = outer_v;\n        foreach (i, MalType b; binds)\n        {\n            auto arg_name = verify_cast!MalSymbol(b);\n            if (arg_name.name == \"&\")\n            {\n                auto rest_arg_name = verify_cast!MalSymbol(binds[i + 1]);\n                auto rest_exprs = new MalList(exprs[i..$]);\n                set(rest_arg_name.name, rest_exprs);\n                break;\n            }\n            else\n            {\n                set(arg_name.name, exprs[i]);\n            }\n        }\n    }\n\n    MalType set(string key, MalType val)\n    {\n        data[key] = val;\n        return val;\n    }\n\n    MalType get(string key)\n    {\n        auto val = (key in data);\n        if (val !is null) {\n            return data[key];\n        } else if (outer is null) {\n            return null;\n        } else {\n            return outer.get(key);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/d/main.di",
    "content": "import types : MalType;\nimport env : Env;\n\nMalType EVAL(MalType ast, Env env);\n"
  },
  {
    "path": "impls/d/mal_core.d",
    "content": "import core.time;\nimport std.algorithm;\nimport std.array;\nimport std.datetime;\nimport std.file;\nimport std.stdio;\nimport env;\nimport main;\nimport reader;\nimport readline;\nimport types;\nimport printer;\n\nstatic MalType mal_equal(MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    return bool_to_mal(a[0] == a[1]);\n}\n\nstatic MalType mal_throw(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    throw new MalException(a[0]);\n}\n\nstatic MalType mal_symbol(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto s = verify_cast!MalString(a[0]);\n    return new MalSymbol(s.val);\n}\n\nstatic MalType mal_string_q(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto s = cast(MalString) a[0];\n    if (s is null) return mal_false;\n    return bool_to_mal(!s.is_keyword());\n}\n\nstatic MalType mal_keyword(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto s = verify_cast!MalString(a[0]);\n    if (s.is_keyword()) return s;\n    return new MalString(\"\\u029e\" ~ s.val);\n}\n\nstatic MalType mal_keyword_q(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto s = cast(MalString) a[0];\n    if (s is null) return mal_false;\n    return bool_to_mal(s.is_keyword());\n}\n\nstatic MalType mal_fn_q(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto builtinfn = cast(MalBuiltinFunc) a[0];\n    if (builtinfn !is null) return mal_true;\n    auto malfunc = cast(MalFunc) a[0];\n    if (malfunc !is null) return bool_to_mal(!malfunc.is_macro);\n    return mal_false;\n}\n\nstatic MalType mal_macro_q(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto malfunc = cast(MalFunc) a[0];\n    if (malfunc !is null) return bool_to_mal(malfunc.is_macro);\n    return mal_false;\n}\n\nstatic MalType mal_pr_str(MalType[] a ...)\n{\n    auto items_strs = a.map!(e => pr_str(e, true));\n    return new MalString(array(items_strs).join(\" \"));\n}\n\nstatic MalType mal_str(MalType[] a ...)\n{\n    auto items_strs = a.map!(e => pr_str(e, false));\n    return new MalString(array(items_strs).join(\"\"));\n}\n\nstatic MalType mal_prn(MalType[] a ...)\n{\n    auto items_strs = a.map!(e => pr_str(e, true));\n    writeln(array(items_strs).join(\" \"));\n    return mal_nil;\n}\n\nstatic MalType mal_println(MalType[] a ...)\n{\n    auto items_strs = a.map!(e => pr_str(e, false));\n    writeln(array(items_strs).join(\" \"));\n    return mal_nil;\n}\n\nstatic MalType mal_read_string(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto s = verify_cast!MalString(a[0]);\n    return read_str(s.val);\n}\n\nstatic MalType mal_readline(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto s = verify_cast!MalString(a[0]);\n    auto line = _readline(s.val);\n    return line is null ? mal_nil : new MalString(line);\n}\n\nstatic MalType mal_slurp(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto filename = verify_cast!MalString(a[0]).val;\n    auto content = cast(string) std.file.read(filename);\n    return new MalString(content);\n}\n\nalias TwoIntFunc = MalType function(long x, long y);\n\nMalType binary_int_op(TwoIntFunc f, MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    MalInteger i0 = verify_cast!MalInteger(a[0]);\n    MalInteger i1 = verify_cast!MalInteger(a[1]);\n    return f(i0.val, i1.val);\n}\n\nstatic MalType mal_time_ms(MalType[] a ...)\n{\n    immutable epoch = SysTime(unixTimeToStdTime(0));\n    immutable hnsecs_since_epoch = Clock.currTime(UTC()) - epoch;\n    immutable ms = hnsecs_since_epoch.total!\"msecs\"();\n    return new MalInteger(ms);\n}\n\nstatic bool is_nil(MalType v)\n{\n    return cast(MalNil)(v) !is null;\n}\n\nstatic MalType mal_assoc(MalType[] a ...)\n{\n    verify_min_args_count(a, 1);\n    auto hm = verify_cast!MalHashmap(a[0]);\n    auto new_hm = new MalHashmap(hm.data.dup);\n    new_hm.put_kv_list(a[1..$]);\n    return new_hm;\n}\n\nstatic MalType mal_dissoc(MalType[] a ...)\n{\n    verify_min_args_count(a, 1);\n    auto hm = verify_cast!MalHashmap(a[0]);\n    auto new_hm = new MalHashmap(hm.data.dup);\n    foreach (k; a[1..$])\n    {\n        new_hm.remove(k);\n    }\n    return new_hm;\n}\n\nstatic MalType mal_get(MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    if (is_nil(a[0])) return mal_nil;\n    auto hm = verify_cast!MalHashmap(a[0]);\n    return hm.get(a[1]);\n}\n\nstatic MalType mal_contains_q(MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    if (is_nil(a[0])) return mal_false;\n    auto hm = verify_cast!MalHashmap(a[0]);\n    return bool_to_mal(hm.contains(a[1]));\n}\n\nstatic MalType mal_keys(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto hm = verify_cast!MalHashmap(a[0]);\n    auto keys = hm.data.keys.map!(s => cast(MalType)(new MalString(s)));\n    return new MalList(array(keys));\n}\n\nstatic MalType mal_vals(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto hm = verify_cast!MalHashmap(a[0]);\n    return new MalList(hm.data.values);\n}\n\nstatic MalType mal_cons(MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    auto lst = verify_cast!MalSequential(a[1]);\n    return new MalList([a[0]] ~ lst.elements);\n}\n\nstatic MalType mal_concat(MalType[] a ...)\n{\n    MalType[] res;\n    foreach (e; a)\n    {\n        auto lst = verify_cast!MalSequential(e);\n        res ~= lst.elements;\n    }\n    return new MalList(res);\n}\n\nstatic MalType mal_vec(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    return new MalVector(verify_cast!MalSequential(a[0]).elements);\n}\n\nstatic MalType mal_nth(MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    if (is_nil(a[0]))\n    {\n        throw new Exception(\"nth: index out of range\");\n    }\n    auto seq = verify_cast!MalSequential(a[0]);\n    auto index = verify_cast!MalInteger(a[1]).val;\n    if (index >= seq.elements.length)\n    {\n        throw new Exception(\"nth: index out of range\");\n    }\n    return seq.elements[index];\n}\n\nstatic MalType mal_first(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    if (is_nil(a[0])) return mal_nil;\n    auto seq = verify_cast!MalSequential(a[0]);\n    if (seq.elements.length == 0) return mal_nil;\n    return seq.elements[0];\n}\n\nstatic MalType mal_rest(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    if (is_nil(a[0])) return new MalList([]);\n    auto seq = verify_cast!MalSequential(a[0]);\n    if (seq.elements.length == 0) return new MalList([]);\n    return new MalList(seq.elements[1..$]);\n}\n\n\nstatic MalType mal_empty_q(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    if (is_nil(a[0]))\n    {\n        return mal_true;\n    }\n    auto s = verify_cast!MalSequential(a[0]);\n    return bool_to_mal(s.elements.length == 0);\n}\n\nstatic MalType mal_count(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    if (is_nil(a[0]))\n    {\n        return new MalInteger(0);\n    }\n    auto s = verify_cast!MalSequential(a[0]);\n    return new MalInteger(cast(int)(s.elements.length));\n}\n\nstatic MalType mal_apply(MalType[] a ...)\n{\n    verify_min_args_count(a, 2);\n    auto last_seq_elems = verify_cast!MalSequential(a[$-1]).elements;\n    auto funcargs = a.length == 2 ? last_seq_elems : (a[1..$-1] ~ last_seq_elems);\n\n    auto builtinfn = cast(MalBuiltinFunc) a[0];\n    if (builtinfn !is null)\n    {\n        return builtinfn.fn(funcargs);\n    }\n\n    auto malfunc = verify_cast!MalFunc(a[0]);\n    auto callenv = new Env(malfunc.def_env, malfunc.arg_names, funcargs);\n\n    return EVAL(malfunc.func_body, callenv);\n}\n\nstatic MalType mal_map(MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    auto seq = verify_cast!MalSequential(a[1]);\n    auto mapped_items = seq.elements.map!(e => mal_apply(a[0], new MalList([e])));\n    return new MalList(array(mapped_items));\n}\n\nstatic MalType mal_conj(MalType[] a ...)\n{\n    verify_min_args_count(a, 1);\n    auto seq = verify_cast!MalSequential(a[0]);\n    return reduce!((s,e) => s.conj(e))(seq, a[1..$]);\n}\n\nstatic MalType mal_seq(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto seqobj = cast(HasSeq) a[0];\n    if (seqobj is null) return mal_nil;\n    return seqobj.seq();\n}\n\nstatic MalType mal_meta(MalType[] a ...)\n{\n    verify_args_count(a, 1);\n    auto metaobj = cast(MalMeta) a[0];\n    if (metaobj is null) return mal_nil;\n    return metaobj.meta();\n}\n\nstatic MalType mal_with_meta(return MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    if (auto metaobj = cast(MalMeta) a[0])\n      return metaobj.with_meta(a[1]);\n    return a[0];\n}\n\nstatic MalType mal_reset_bang(return MalType[] a ...)\n{\n    verify_args_count(a, 2);\n    verify_cast!MalAtom(a[0]).val = a[1];\n    return a[1];\n}\n\nstatic MalType mal_swap_bang(MalType[] a ...)\n{\n    verify_min_args_count(a, 2);\n    auto atom = verify_cast!MalAtom(a[0]);\n    auto args = [atom.val] ~ a[2..$];\n    auto newval = mal_apply([a[1], new MalList(args)]);\n    return mal_reset_bang([atom, newval]);\n}\n\nBuiltinStaticFuncType[string] core_ns;\n\nstatic this()\n{\n    core_ns = [\n        \"=\":        &mal_equal,\n        \"throw\":    &mal_throw,\n\n        \"nil?\":     (a ...) => mal_type_q!MalNil(a),\n        \"true?\":    (a ...) => mal_type_q!MalTrue(a),\n        \"false?\":   (a ...) => mal_type_q!MalFalse(a),\n        \"symbol\":   &mal_symbol,\n        \"symbol?\":  (a ...) => mal_type_q!MalSymbol(a),\n        \"string?\":  &mal_string_q,\n        \"keyword\":  &mal_keyword,\n        \"keyword?\": &mal_keyword_q,\n        \"number?\":  (a ...) => mal_type_q!MalInteger(a),\n        \"fn?\":      &mal_fn_q,\n        \"macro?\":   &mal_macro_q,\n\n        \"pr-str\":   &mal_pr_str,\n        \"str\":      &mal_str,\n        \"prn\":      &mal_prn,\n        \"println\":  &mal_println,\n        \"read-string\": &mal_read_string,\n        \"readline\": &mal_readline,\n        \"slurp\":    &mal_slurp,\n\n        \"<\":        (a ...) => binary_int_op((x,y) => bool_to_mal(x < y), a),\n        \"<=\":       (a ...) => binary_int_op((x,y) => bool_to_mal(x <= y), a),\n        \">\":        (a ...) => binary_int_op((x,y) => bool_to_mal(x > y), a),\n        \">=\":       (a ...) => binary_int_op((x,y) => bool_to_mal(x >= y), a),\n        \"+\":        (a ...) => binary_int_op((x,y) => new MalInteger(x + y), a),\n        \"-\":        (a ...) => binary_int_op((x,y) => new MalInteger(x - y), a),\n        \"*\":        (a ...) => binary_int_op((x,y) => new MalInteger(x * y), a),\n        \"/\":        (a ...) => binary_int_op((x,y) => new MalInteger(x / y), a),\n        \"time-ms\":  &mal_time_ms,\n\n        \"list\":     (a ...) => new MalList(a),\n        \"list?\":    (a ...) => mal_type_q!MalList(a),\n        \"vector\":   (a ...) => new MalVector(a),\n        \"vector?\":  (a ...) => mal_type_q!MalVector(a),\n        \"hash-map\": (a ...) => new MalHashmap(a),\n        \"map?\":     (a ...) => mal_type_q!MalHashmap(a),\n        \"assoc\":    &mal_assoc,\n        \"dissoc\":   &mal_dissoc,\n        \"get\":      &mal_get,\n        \"contains?\": &mal_contains_q,\n        \"keys\":     &mal_keys,\n        \"vals\":     &mal_vals,\n\n        \"sequential?\": (a ...) => mal_type_q!MalSequential(a),\n        \"cons\":     &mal_cons,\n        \"concat\":   &mal_concat,\n        \"vec\":      &mal_vec,\n        \"nth\":      &mal_nth,\n        \"first\":    &mal_first,\n        \"rest\":     &mal_rest,\n        \"empty?\":   &mal_empty_q,\n        \"count\":    &mal_count,\n        \"apply\":    &mal_apply,\n        \"map\":      &mal_map,\n\n        \"conj\":     &mal_conj,\n        \"seq\":      &mal_seq,\n\n        \"meta\":     &mal_meta,\n        \"with-meta\": &mal_with_meta,\n        \"atom\":     (a ...) => new MalAtom(verify_args_count(a, 1)[0]),\n        \"atom?\":    (a ...) => mal_type_q!MalAtom(a),\n        \"deref\":    (a ...) => verify_cast!MalAtom(verify_args_count(a, 1)[0]).val,\n        \"reset!\":   &mal_reset_bang,\n        \"swap!\":    &mal_swap_bang\n    ];\n}\n"
  },
  {
    "path": "impls/d/printer.d",
    "content": "import types;\n\nstring pr_str(MalType obj, bool readable = true)\n{\n    return obj.print(readable);\n}\n"
  },
  {
    "path": "impls/d/reader.d",
    "content": "import std.array;\nimport std.regex;\nimport std.stdio;\nimport types;\n\nMalSymbol sym_quote;\nMalSymbol sym_quasiquote;\nMalSymbol sym_unquote;\nMalSymbol sym_splice_unquote;\nMalSymbol sym_deref;\nMalSymbol sym_with_meta;\n\nstatic this()\n{\n    sym_quote = new MalSymbol(\"quote\");\n    sym_quasiquote = new MalSymbol(\"quasiquote\");\n    sym_unquote = new MalSymbol(\"unquote\");\n    sym_splice_unquote = new MalSymbol(\"splice-unquote\");\n    sym_deref = new MalSymbol(\"deref\");\n    sym_with_meta = new MalSymbol(\"with-meta\");\n}\n\nclass Reader\n{\n    int pos = 0;\n    const string[] tokens;\n\n    this(string[] the_tokens)\n    {\n        tokens = the_tokens.dup;\n    }\n\n    string peek()\n    {\n        if (pos >= tokens.length) return null;\n        return tokens[pos];\n    }\n\n    string next()\n    {\n        auto token = peek();\n        pos++;\n        return token;\n    }\n}\n\nauto tokenize_ctr = ctRegex!(r\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\" ~ `\"` ~ `(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"` ~ r\"`,;)]*)\");\n\nstring[] tokenize(string str)\n{\n    string[] tokens;\n    foreach(c; matchAll(str, tokenize_ctr))\n    {\n        auto token = c[1];\n        if (token.length == 0) continue;\n        if (token[0] == ';') continue;\n        tokens ~= token;\n    }\n    return tokens;\n}\n\nMalString parse_string(string token)\n{\n    // TODO: this could be done with replaceAll\n    // https://dlang.org/library/std/regex/replace_all.html\n    string unescaped =\n        token[1..$-1] // Remove surrounding quotes\n        .replace(\"\\\\\\\\\", \"\\u029e\")\n        .replace(\"\\\\n\", \"\\n\")\n        .replace(\"\\\\\\\"\", \"\\\"\")\n        .replace(\"\\u029e\", \"\\\\\");\n    return new MalString(unescaped);\n}\n\nauto integer_ctr = ctRegex!(r\"^-?[0-9]+$\");\nauto string_ctr = ctRegex!(`^\"(?:\\\\.|[^\\\\\"])*\"$`);\n\nMalType read_atom(Reader reader)\n{\n    auto token = reader.next();\n    switch (token)\n    {\n        case \"nil\": return mal_nil;\n        case \"false\": return mal_false;\n        case \"true\": return mal_true;\n        default:\n            switch (token[0]) {\n                case ':':\n                    return new MalString(\"\\u029e\" ~ token[1..$]);\n                case '\"':\n                    auto captures = matchFirst(token, string_ctr);\n                    if (captures.empty())\n                    {\n                        throw new Exception(\"expected '\\\"', got EOF\");\n                    }\n                    return parse_string(token);\n                default:\n                    auto captures = matchFirst(token, integer_ctr);\n                    if (!captures.empty())\n                    {\n                        return new MalInteger(token);\n                    }\n\n                    return new MalSymbol(token);\n            }\n    }\n}\n\nMalType[] read_items(Reader reader, string start, string end)\n{\n    auto open_paren = reader.next();\n    if (open_paren != start) throw new Exception(\"expected '\" ~ start ~ \"', got EOF\");\n\n    string token;\n    MalType[] res;\n    while ((token = reader.peek()) != end)\n    {\n        if (token is null)\n        {\n            throw new Exception(\"expected '\" ~ end ~ \"', got EOF\");\n        }\n        res ~= read_form(reader);\n    }\n    reader.next(); // consume the ')'\n    return res;\n}\n\nMalList read_list(Reader reader)\n{\n    return new MalList(read_items(reader, \"(\", \")\"));\n}\n\nMalVector read_vector(Reader reader)\n{\n    return new MalVector(read_items(reader, \"[\", \"]\"));\n}\n\nMalHashmap read_hashmap(Reader reader)\n{\n    return new MalHashmap(read_items(reader, \"{\", \"}\"));\n}\n\nMalList read_quote_shortcut(Reader reader, MalSymbol sym)\n{\n    reader.next(); // consume the special quote char\n    return new MalList([sym, read_form(reader)]);\n}\n\nMalType read_form(Reader reader)\n{\n    auto token = reader.peek();\n    if (token is null) return new MalNil();\n    switch(token)\n    {\n        case \"'\":\n            return read_quote_shortcut(reader, sym_quote);\n        case \"`\":\n            return read_quote_shortcut(reader, sym_quasiquote);\n        case \"~\":\n            return read_quote_shortcut(reader, sym_unquote);\n        case \"~@\":\n            return read_quote_shortcut(reader, sym_splice_unquote);\n        case \"@\":\n            return read_quote_shortcut(reader, sym_deref);\n        case \"^\":\n            reader.next(); // consume the caret char\n            auto meta = read_form(reader);\n            return new MalList([sym_with_meta, read_form(reader), meta]);\n        case \"(\":\n            return read_list(reader);\n        case \")\":\n            throw new Exception(\"unexpected ')'\");\n        case \"[\":\n            return read_vector(reader);\n        case \"]\":\n            throw new Exception(\"unexpected ']'\");\n        case \"{\":\n            return read_hashmap(reader);\n        case \"}\":\n            throw new Exception(\"unexpected '}'\");\n        default:\n            return read_atom(reader);\n    }\n}\n\nMalType read_str(string str)\n{\n    auto tokens = tokenize(str);\n    auto reader = new Reader(tokens);\n    return read_form(reader);\n}\n"
  },
  {
    "path": "impls/d/readline.d",
    "content": "import std.string;\nimport std.path;\nimport std.file;\n\nimport core.stdc.string;\nimport core.stdc.stdlib;\n\n// readline/readline.h\nextern (C) char* readline(const char* prompt);\n\n// readline/history.h\nextern (C) void using_history();\nextern (C) void add_history(const char *line);\nextern (C) int read_history(const char *filename);\nextern (C) int append_history(int nelement, const char *filename);\n\nbool history_loaded = false;\nconst string history_file = \"~/.mal-history\";\n\nvoid load_history()\n{\n    if (history_loaded) return;\n    using_history();\n    string hf = expandTilde(history_file);\n    std.file.append(hf, \"\"); // Create the file if needed\n    read_history(toStringz(hf));\n    history_loaded = true;\n}\n\nvoid append_to_history()\n{\n    string hf = expandTilde(history_file);\n    append_history(1, toStringz(hf));\n}\n\n// Convert from C-string to D-string (making a copy)\npure string fromCstr(char* cstr)\n{\n    auto len = core.stdc.string.strlen(cstr);\n    if (len == 0) return \"\";\n    string line = cstr[0..len].dup;\n    return line;\n}\n\nstring _readline(in string prompt)\n{\n    load_history();\n\n    auto cstr = readline(toStringz(prompt));\n    if (cstr is null) return null;\n    scope(exit) { core.stdc.stdlib.free(cstr); }\n\n    if (cstr[0] != '\\0')\n    {\n        add_history(cstr);   // Add input to in-memory history\n        append_to_history(); // Flush new line of history to disk\n    }\n\n    return fromCstr(cstr);\n}\n"
  },
  {
    "path": "impls/d/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/d/step0_repl.d",
    "content": "import std.stdio;\nimport std.string;\nimport readline;\n\nstring READ(string str)\n{\n    return str;\n}\n\nstring EVAL(string ast)\n{\n    return ast;\n}\n\nstring PRINT(string ast)\n{\n    return ast;\n}\n\nstring rep(string str)\n{\n    return PRINT(EVAL(READ(str)));\n}\n\nvoid main()\n{\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        writeln(rep(line));\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step1_read_print.d",
    "content": "import std.stdio;\nimport std.string;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast)\n{\n    return ast;\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nstring rep(string str)\n{\n    return PRINT(EVAL(READ(str)));\n}\n\nvoid main()\n{\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step2_eval.d",
    "content": "import std.algorithm;\nimport std.array;\nimport std.stdio;\nimport std.string;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nalias Env = MalType[string];\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n    if (auto dbgeval = (\"DEBUG-EVAL\" in env))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        auto v = (sym.name in env);\n        if (v is null) throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n        return *v;\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n  // todo: indent right\n  else if (auto ast_list = cast(MalList)ast)\n  {\n    if (ast_list.elements.length == 0)\n    {\n        return ast;\n    }\n    auto fobj = verify_cast!MalBuiltinFunc(EVAL(ast_list.elements[0], env));\n    auto args = array(ast_list.elements[1..$].map!(e => EVAL(e, env)));\n    return fobj.fn(args);\n  }\n  else\n  {\n        return ast;\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(EVAL(READ(str), env));\n}\n\nstatic MalType mal_add(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val + i1.val);\n}\n\nstatic MalType mal_sub(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val - i1.val);\n}\n\nstatic MalType mal_mul(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val * i1.val);\n}\n\nstatic MalType mal_div(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val / i1.val);\n}\n\nvoid main()\n{\n    Env repl_env;\n    repl_env[\"+\"] = new MalBuiltinFunc(&mal_add, \"+\");\n    repl_env[\"-\"] = new MalBuiltinFunc(&mal_sub, \"-\");\n    repl_env[\"*\"] = new MalBuiltinFunc(&mal_mul, \"*\");\n    repl_env[\"/\"] = new MalBuiltinFunc(&mal_div, \"/\");\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step3_env.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport env;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n  // todo: indent right\n  else if (auto ast_list = cast(MalList)ast)\n  {\n    if (ast_list.elements.length == 0)\n    {\n        return ast;\n    }\n\n    auto a0_sym = verify_cast!MalSymbol(ast_list.elements[0]);\n    switch (a0_sym.name)\n    {\n        case \"def!\":\n            auto a1 = verify_cast!MalSymbol(ast_list.elements[1]);\n            return env.set(a1.name, EVAL(ast_list.elements[2], env));\n\n        case \"let*\":\n            auto a1 = verify_cast!MalSequential(ast_list.elements[1]);\n            auto let_env = new Env(env);\n            foreach (kv; chunks(a1.elements, 2))\n            {\n                if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                auto var_name = verify_cast!MalSymbol(kv[0]);\n                let_env.set(var_name.name, EVAL(kv[1], let_env));\n            }\n            return EVAL(ast_list.elements[2], let_env);\n\n        default:\n            auto fobj = verify_cast!MalBuiltinFunc(EVAL(ast_list.elements[0], env));\n            auto args = array(ast_list.elements[1..$].map!(e => EVAL(e, env)));\n            return fobj.fn(args);\n    }\n  }\n  else\n  {\n        return ast;\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(EVAL(READ(str), env));\n}\n\nstatic MalType mal_add(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val + i1.val);\n}\n\nstatic MalType mal_sub(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val - i1.val);\n}\n\nstatic MalType mal_mul(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val * i1.val);\n}\n\nstatic MalType mal_div(MalType[] a ...)\n{\n     verify_args_count(a, 2);\n     MalInteger i0 = verify_cast!MalInteger(a[0]);\n     MalInteger i1 = verify_cast!MalInteger(a[1]);\n     return new MalInteger(i0.val / i1.val);\n}\n\nvoid main()\n{\n    auto repl_env = new Env(null);\n    repl_env.set(\"+\", new MalBuiltinFunc(&mal_add, \"+\"));\n    repl_env.set(\"-\", new MalBuiltinFunc(&mal_sub, \"-\"));\n    repl_env.set(\"*\", new MalBuiltinFunc(&mal_mul, \"*\"));\n    repl_env.set(\"/\", new MalBuiltinFunc(&mal_div, \"/\"));\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step4_if_fn_do.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n  // todo: indent right\n  else if (auto ast_list = cast(MalList)ast)\n  {\n    auto aste = ast_list.elements;\n    if (aste.length == 0)\n    {\n        return ast;\n    }\n    auto a0_sym = cast(MalSymbol) aste[0];\n    auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n    switch (sym_name)\n    {\n        case \"def!\":\n            auto a1 = verify_cast!MalSymbol(aste[1]);\n            return env.set(a1.name, EVAL(aste[2], env));\n\n        case \"let*\":\n            auto a1 = verify_cast!MalSequential(aste[1]);\n            auto let_env = new Env(env);\n            foreach (kv; chunks(a1.elements, 2))\n            {\n                if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                auto var_name = verify_cast!MalSymbol(kv[0]);\n                let_env.set(var_name.name, EVAL(kv[1], let_env));\n            }\n            return EVAL(aste[2], let_env);\n\n        case \"do\":\n            foreach (elt; aste[1..$-1]) {\n                EVAL(elt, env);\n            }\n            return EVAL(aste[$-1], env);\n\n        case \"if\":\n            auto cond = EVAL(aste[1], env);\n            if (cond.is_truthy())\n                return EVAL(aste[2], env);\n            else\n                if (aste.length > 3)\n                    return EVAL(aste[3], env);\n                else\n                    return mal_nil;\n\n        case \"fn*\":\n            auto args_list = verify_cast!MalSequential(aste[1]);\n            return new MalFunc(args_list.elements, aste[2], env);\n\n        default:\n            auto first = EVAL(aste[0], env);\n            auto rest = array(aste[1..$].map!(e => EVAL(e, env)));\n            if (auto funcobj = cast(MalFunc)first)\n            {\n                auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                return EVAL(funcobj.func_body, callenv);\n            }\n            else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n            {\n                return builtinfuncobj.fn(rest);\n            }\n            else\n            {\n                throw new Exception(\"Expected a function\");\n            }\n    }\n  }\n  else\n  {\n        return ast;\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nvoid main()\n{\n    auto repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step5_tco.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n  for (;;)\n  {\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n    else if (auto ast_list = cast(MalList)ast)\n    {\n        auto aste = ast_list.elements;\n        if (aste.length == 0)\n        {\n            return ast;\n        }\n        auto a0_sym = cast(MalSymbol) aste[0];\n        auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n        switch (sym_name)\n        {\n            case \"def!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                return env.set(a1.name, EVAL(aste[2], env));\n\n            case \"let*\":\n                auto a1 = verify_cast!MalSequential(aste[1]);\n                auto let_env = new Env(env);\n                foreach (kv; chunks(a1.elements, 2))\n                {\n                    if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                    auto var_name = verify_cast!MalSymbol(kv[0]);\n                    let_env.set(var_name.name, EVAL(kv[1], let_env));\n                }\n                ast = aste[2];\n                env = let_env;\n                continue; // TCO\n\n            case \"do\":\n                foreach (elt; aste[1..$-1]) {\n                    EVAL(elt, env);\n                }\n                ast = aste[$-1];\n                continue; // TCO\n\n            case \"if\":\n                auto cond = EVAL(aste[1], env);\n                if (cond.is_truthy())\n                {\n                    ast = aste[2];\n                    continue; // TCO\n                }\n                else\n                    if (aste.length > 3)\n                    {\n                        ast = aste[3];\n                        continue; // TCO\n                    }\n                    else\n                    {\n                        return mal_nil;\n                    }\n\n            case \"fn*\":\n                auto args_list = verify_cast!MalSequential(aste[1]);\n                return new MalFunc(args_list.elements, aste[2], env);\n\n            default:\n                auto first = EVAL(aste[0], env);\n                auto rest = array(aste[1..$].map!(e => EVAL(e, env)));\n                if (auto funcobj = cast(MalFunc)first)\n                {\n                    auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                    ast = funcobj.func_body;\n                    env = callenv;\n                    continue; // TCO\n                }\n                else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n                {\n                    return builtinfuncobj.fn(rest);\n                }\n                else\n                {\n                    throw new Exception(\"Expected a function\");\n                }\n        }\n    }\n    else\n    {\n        return ast;\n    }\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nvoid main()\n{\n    auto repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step6_file.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport core.stdc.stdlib;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n  for (;;)\n  {\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n    else if (auto ast_list = cast(MalList)ast)\n    {\n        auto aste = ast_list.elements;\n        if (aste.length == 0)\n        {\n            return ast;\n        }\n        auto a0_sym = cast(MalSymbol) aste[0];\n        auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n        switch (sym_name)\n        {\n            case \"def!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                return env.set(a1.name, EVAL(aste[2], env));\n\n            case \"let*\":\n                auto a1 = verify_cast!MalSequential(aste[1]);\n                auto let_env = new Env(env);\n                foreach (kv; chunks(a1.elements, 2))\n                {\n                    if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                    auto var_name = verify_cast!MalSymbol(kv[0]);\n                    let_env.set(var_name.name, EVAL(kv[1], let_env));\n                }\n                ast = aste[2];\n                env = let_env;\n                continue; // TCO\n\n            case \"do\":\n                foreach (elt; aste[1..$-1]) {\n                    EVAL(elt, env);\n                }\n                ast = aste[$-1];\n                continue; // TCO\n\n            case \"if\":\n                auto cond = EVAL(aste[1], env);\n                if (cond.is_truthy())\n                {\n                    ast = aste[2];\n                    continue; // TCO\n                }\n                else\n                    if (aste.length > 3)\n                    {\n                        ast = aste[3];\n                        continue; // TCO\n                    }\n                    else\n                    {\n                        return mal_nil;\n                    }\n\n            case \"fn*\":\n                auto args_list = verify_cast!MalSequential(aste[1]);\n                return new MalFunc(args_list.elements, aste[2], env);\n\n            default:\n                auto first = EVAL(aste[0], env);\n                auto rest = array(aste[1..$].map!(e => EVAL(e, env)));\n                if (auto funcobj = cast(MalFunc)first)\n                {\n                    auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                    ast = funcobj.func_body;\n                    env = callenv;\n                    continue; // TCO\n                }\n                else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n                {\n                    return builtinfuncobj.fn(rest);\n                }\n                else\n                {\n                    throw new Exception(\"Expected a function\");\n                }\n        }\n    }\n    else\n    {\n        return ast;\n    }\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nstatic MalList create_argv_list(string[] args)\n{\n    if (args.length <= 2) return new MalList([]);\n    return new MalList(array(args[2..$].map!(s => cast(MalType)(new MalString(s)))));\n}\n\nvoid main(string[] args)\n{\n    Env repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    BuiltinFuncType eval_func = (a ...) {\n        verify_args_count(a, 1);\n        return EVAL(a[0], repl_env);\n    };\n    repl_env.set(\"eval\", new MalBuiltinFunc(eval_func, \"eval\"));\n    repl_env.set(\"*ARGV*\", create_argv_list(args));\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n\n    if (args.length > 1)\n    {\n        try\n        {\n            rep(\"(load-file \\\"\" ~ args[1] ~ \"\\\")\", repl_env);\n            return;\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n            exit(1);\n        }\n    }\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step7_quote.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport core.stdc.stdlib;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nbool starts_with(MalType ast, MalSymbol sym)\n{\n    auto lst = cast(MalList) ast;\n    if (lst is null) return false;\n    auto lste = lst.elements;\n    return lste.length > 0 && lste[0] == sym;\n}\n\nMalType quasiquote(MalType ast)\n{\n    if (cast(MalSymbol)ast || cast(MalHashmap)ast)\n        return new MalList([sym_quote, ast]);\n\n    auto ast_seq = cast(MalSequential) ast;\n    if (ast_seq is null)\n        return ast;\n\n    auto aste = ast_seq.elements;\n    if (starts_with(ast, sym_unquote))\n        return aste[1];\n\n    MalType res = new MalList([]);\n    foreach_reverse (elt; ast_seq.elements)\n        if (starts_with(elt, sym_splice_unquote))\n            res = new MalList([new MalSymbol(\"concat\"), (cast(MalList) elt).elements[1], res]);\n        else\n            res = new MalList([new MalSymbol(\"cons\"), quasiquote(elt), res]);\n    if (cast(MalVector) ast)\n        res = new MalList([new MalSymbol(\"vec\"), res]);\n    return res;\n}\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n  for (;;)\n  {\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n    else if (auto ast_list = cast(MalList)ast)\n    {\n        auto aste = ast_list.elements;\n        if (aste.length == 0)\n        {\n            return ast;\n        }\n        auto a0_sym = cast(MalSymbol) aste[0];\n        auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n        switch (sym_name)\n        {\n            case \"def!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                return env.set(a1.name, EVAL(aste[2], env));\n\n            case \"let*\":\n                auto a1 = verify_cast!MalSequential(aste[1]);\n                auto let_env = new Env(env);\n                foreach (kv; chunks(a1.elements, 2))\n                {\n                    if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                    auto var_name = verify_cast!MalSymbol(kv[0]);\n                    let_env.set(var_name.name, EVAL(kv[1], let_env));\n                }\n                ast = aste[2];\n                env = let_env;\n                continue; // TCO\n\n            case \"quote\":\n                return aste[1];\n\n            case \"quasiquote\":\n                ast = quasiquote(aste[1]);\n                continue; // TCO\n\n            case \"do\":\n                foreach (elt; aste[1..$-1]) {\n                    EVAL(elt, env);\n                }\n                ast = aste[$-1];\n                continue; // TCO\n\n            case \"if\":\n                auto cond = EVAL(aste[1], env);\n                if (cond.is_truthy())\n                {\n                    ast = aste[2];\n                    continue; // TCO\n                }\n                else\n                    if (aste.length > 3)\n                    {\n                        ast = aste[3];\n                        continue; // TCO\n                    }\n                    else\n                    {\n                        return mal_nil;\n                    }\n\n            case \"fn*\":\n                auto args_list = verify_cast!MalSequential(aste[1]);\n                return new MalFunc(args_list.elements, aste[2], env);\n\n            default:\n                auto first = EVAL(aste[0], env);\n                auto rest = array(aste[1..$].map!(e => EVAL(e, env)));\n                if (auto funcobj = cast(MalFunc)first)\n                {\n                    auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                    ast = funcobj.func_body;\n                    env = callenv;\n                    continue; // TCO\n                }\n                else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n                {\n                    return builtinfuncobj.fn(rest);\n                }\n                else\n                {\n                    throw new Exception(\"Expected a function\");\n                }\n        }\n    }\n    else\n    {\n        return ast;\n    }\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nstatic MalList create_argv_list(string[] args)\n{\n    if (args.length <= 2) return new MalList([]);\n    return new MalList(array(args[2..$].map!(s => cast(MalType)(new MalString(s)))));\n}\n\nvoid main(string[] args)\n{\n    Env repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    BuiltinFuncType eval_func = (a ...) {\n        verify_args_count(a, 1);\n        return EVAL(a[0], repl_env);\n    };\n    repl_env.set(\"eval\", new MalBuiltinFunc(eval_func, \"eval\"));\n    repl_env.set(\"*ARGV*\", create_argv_list(args));\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n\n    if (args.length > 1)\n    {\n        try\n        {\n            rep(\"(load-file \\\"\" ~ args[1] ~ \"\\\")\", repl_env);\n            return;\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n            exit(1);\n        }\n    }\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step8_macros.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport core.stdc.stdlib;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nbool starts_with(MalType ast, MalSymbol sym)\n{\n    auto lst = cast(MalList) ast;\n    if (lst is null) return false;\n    auto lste = lst.elements;\n    return lste.length > 0 && lste[0] == sym;\n}\n\nMalType quasiquote(MalType ast)\n{\n    if (cast(MalSymbol)ast || cast(MalHashmap)ast)\n        return new MalList([sym_quote, ast]);\n\n    auto ast_seq = cast(MalSequential) ast;\n    if (ast_seq is null)\n        return ast;\n\n    auto aste = ast_seq.elements;\n    if (starts_with(ast, sym_unquote))\n        return aste[1];\n\n    MalType res = new MalList([]);\n    foreach_reverse (elt; ast_seq.elements)\n        if (starts_with(elt, sym_splice_unquote))\n            res = new MalList([new MalSymbol(\"concat\"), (cast(MalList) elt).elements[1], res]);\n        else\n            res = new MalList([new MalSymbol(\"cons\"), quasiquote(elt), res]);\n    if (cast(MalVector) ast)\n        res = new MalList([new MalSymbol(\"vec\"), res]);\n    return res;\n}\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n  for (;;)\n  {\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n    else if (auto ast_list = cast(MalList)ast)\n    {\n        auto aste = ast_list.elements;\n        if (aste.length == 0)\n        {\n            return ast;\n        }\n        auto a0_sym = cast(MalSymbol) aste[0];\n        auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n        switch (sym_name)\n        {\n            case \"def!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                return env.set(a1.name, EVAL(aste[2], env));\n\n            case \"let*\":\n                auto a1 = verify_cast!MalSequential(aste[1]);\n                auto let_env = new Env(env);\n                foreach (kv; chunks(a1.elements, 2))\n                {\n                    if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                    auto var_name = verify_cast!MalSymbol(kv[0]);\n                    let_env.set(var_name.name, EVAL(kv[1], let_env));\n                }\n                ast = aste[2];\n                env = let_env;\n                continue; // TCO\n\n            case \"quote\":\n                return aste[1];\n\n            case \"quasiquote\":\n                ast = quasiquote(aste[1]);\n                continue; // TCO\n\n            case \"defmacro!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                auto mac = verify_cast!MalFunc(EVAL(aste[2], env));\n                mac = new MalFunc(mac.arg_names, mac.func_body, mac.def_env);\n                mac.is_macro = true;\n                return env.set(a1.name, mac);\n\n            case \"do\":\n                foreach (elt; aste[1..$-1]) {\n                    EVAL(elt, env);\n                }\n                ast = aste[$-1];\n                continue; // TCO\n\n            case \"if\":\n                auto cond = EVAL(aste[1], env);\n                if (cond.is_truthy())\n                {\n                    ast = aste[2];\n                    continue; // TCO\n                }\n                else\n                    if (aste.length > 3)\n                    {\n                        ast = aste[3];\n                        continue; // TCO\n                    }\n                    else\n                    {\n                        return mal_nil;\n                    }\n\n            case \"fn*\":\n                auto args_list = verify_cast!MalSequential(aste[1]);\n                return new MalFunc(args_list.elements, aste[2], env);\n\n            default:\n                auto first = EVAL(aste[0], env);\n                auto rest = aste[1..$];\n                if (auto funcobj = cast(MalFunc)first)\n                {\n                    if (funcobj.is_macro) {\n                        auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                        ast = EVAL(funcobj.func_body, callenv);\n                        continue; // TCO\n                    }\n                    rest = array(rest.map!(e => EVAL(e, env)));\n                    auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                    ast = funcobj.func_body;\n                    env = callenv;\n                    continue; // TCO\n                }\n                else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n                {\n                    rest = array(rest.map!(e => EVAL(e, env)));\n                    return builtinfuncobj.fn(rest);\n                }\n                else\n                {\n                    throw new Exception(\"Expected a function\");\n                }\n        }\n    }\n    else\n    {\n        return ast;\n    }\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nstatic MalList create_argv_list(string[] args)\n{\n    if (args.length <= 2) return new MalList([]);\n    return new MalList(array(args[2..$].map!(s => cast(MalType)(new MalString(s)))));\n}\n\nvoid main(string[] args)\n{\n    Env repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    BuiltinFuncType eval_func = (a ...) {\n        verify_args_count(a, 1);\n        return EVAL(a[0], repl_env);\n    };\n    repl_env.set(\"eval\", new MalBuiltinFunc(eval_func, \"eval\"));\n    repl_env.set(\"*ARGV*\", create_argv_list(args));\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n    re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n    if (args.length > 1)\n    {\n        try\n        {\n            rep(\"(load-file \\\"\" ~ args[1] ~ \"\\\")\", repl_env);\n            return;\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n            exit(1);\n        }\n    }\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/step9_try.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport core.stdc.stdlib;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nbool starts_with(MalType ast, MalSymbol sym)\n{\n    auto lst = cast(MalList) ast;\n    if (lst is null) return false;\n    auto lste = lst.elements;\n    return lste.length > 0 && lste[0] == sym;\n}\n\nMalType quasiquote(MalType ast)\n{\n    if (cast(MalSymbol)ast || cast(MalHashmap)ast)\n        return new MalList([sym_quote, ast]);\n\n    auto ast_seq = cast(MalSequential) ast;\n    if (ast_seq is null)\n        return ast;\n\n    auto aste = ast_seq.elements;\n    if (starts_with(ast, sym_unquote))\n        return aste[1];\n\n    MalType res = new MalList([]);\n    foreach_reverse (elt; ast_seq.elements)\n        if (starts_with(elt, sym_splice_unquote))\n            res = new MalList([new MalSymbol(\"concat\"), (cast(MalList) elt).elements[1], res]);\n        else\n            res = new MalList([new MalSymbol(\"cons\"), quasiquote(elt), res]);\n    if (cast(MalVector) ast)\n        res = new MalList([new MalSymbol(\"vec\"), res]);\n    return res;\n}\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n  for (;;)\n  {\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n    else if (auto ast_list = cast(MalList)ast)\n    {\n        auto aste = ast_list.elements;\n        if (aste.length == 0)\n        {\n            return ast;\n        }\n        auto a0_sym = cast(MalSymbol) aste[0];\n        auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n        switch (sym_name)\n        {\n            case \"def!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                return env.set(a1.name, EVAL(aste[2], env));\n\n            case \"let*\":\n                auto a1 = verify_cast!MalSequential(aste[1]);\n                auto let_env = new Env(env);\n                foreach (kv; chunks(a1.elements, 2))\n                {\n                    if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                    auto var_name = verify_cast!MalSymbol(kv[0]);\n                    let_env.set(var_name.name, EVAL(kv[1], let_env));\n                }\n                ast = aste[2];\n                env = let_env;\n                continue; // TCO\n\n            case \"quote\":\n                return aste[1];\n\n            case \"quasiquote\":\n                ast = quasiquote(aste[1]);\n                continue; // TCO\n\n            case \"defmacro!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                auto mac = verify_cast!MalFunc(EVAL(aste[2], env));\n                mac = new MalFunc(mac.arg_names, mac.func_body, mac.def_env);\n                mac.is_macro = true;\n                return env.set(a1.name, mac);\n\n            case \"try*\":\n                if (aste.length < 2) return mal_nil;\n                if (aste.length < 3)\n                {\n                    ast = aste[1];\n                    continue; // TCO\n                }\n                MalType exc;\n                try\n                {\n                    // d seems to do erroneous tco all by itself without this\n                    // little distraction\n                    pr_str(aste[1]);\n                    return EVAL(aste[1], env);\n                }\n                catch (MalException e)\n                {\n                    exc = e.data;\n                }\n                catch (Exception e)\n                {\n                    exc = new MalString(e.msg);\n                }\n                auto catch_clause = verify_cast!MalList(aste[2]);\n                auto catch_env = new Env(env, [catch_clause.elements[1]], [exc]);\n                ast = catch_clause.elements[2];\n                env = catch_env;\n                continue; // TCO\n\n            case \"do\":\n                foreach (elt; aste[1..$-1]) {\n                    EVAL(elt, env);\n                }\n                ast = aste[$-1];\n                continue; // TCO\n\n            case \"if\":\n                auto cond = EVAL(aste[1], env);\n                if (cond.is_truthy())\n                {\n                    ast = aste[2];\n                    continue; // TCO\n                }\n                else\n                    if (aste.length > 3)\n                    {\n                        ast = aste[3];\n                        continue; // TCO\n                    }\n                    else\n                    {\n                        return mal_nil;\n                    }\n\n            case \"fn*\":\n                auto args_list = verify_cast!MalSequential(aste[1]);\n                return new MalFunc(args_list.elements, aste[2], env);\n\n            default:\n                auto first = EVAL(aste[0], env);\n                auto rest = aste[1..$];\n                if (auto funcobj = cast(MalFunc)first)\n                {\n                    if (funcobj.is_macro) {\n                        auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                        ast = EVAL(funcobj.func_body, callenv);\n                        continue; // TCO\n                    }\n                    rest = array(rest.map!(e => EVAL(e, env)));\n                    auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                    ast = funcobj.func_body;\n                    env = callenv;\n                    continue; // TCO\n                }\n                else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n                {\n                    rest = array(rest.map!(e => EVAL(e, env)));\n                    return builtinfuncobj.fn(rest);\n                }\n                else\n                {\n                    throw new Exception(\"Expected a function\");\n                }\n        }\n    }\n    else\n    {\n        return ast;\n    }\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nstatic MalList create_argv_list(string[] args)\n{\n    if (args.length <= 2) return new MalList([]);\n    return new MalList(array(args[2..$].map!(s => cast(MalType)(new MalString(s)))));\n}\n\nvoid main(string[] args)\n{\n    Env repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    BuiltinFuncType eval_func = (a ...) {\n        verify_args_count(a, 1);\n        return EVAL(a[0], repl_env);\n    };\n    repl_env.set(\"eval\", new MalBuiltinFunc(eval_func, \"eval\"));\n    repl_env.set(\"*ARGV*\", create_argv_list(args));\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n    re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n    if (args.length > 1)\n    {\n        try\n        {\n            rep(\"(load-file \\\"\" ~ args[1] ~ \"\\\")\", repl_env);\n            return;\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n            exit(1);\n        }\n    }\n\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (MalException e)\n        {\n            writeln(\"Error: \", pr_str(e.data));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/stepA_mal.d",
    "content": "module main;\n\nimport std.algorithm;\nimport std.compiler;\nimport std.array;\nimport std.range;\nimport std.stdio;\nimport std.string;\nimport core.stdc.stdlib;\nimport env;\nimport mal_core;\nimport readline;\nimport reader;\nimport printer;\nimport types;\n\nbool starts_with(MalType ast, MalSymbol sym)\n{\n    auto lst = cast(MalList) ast;\n    if (lst is null) return false;\n    auto lste = lst.elements;\n    return lste.length > 0 && lste[0] == sym;\n}\n\nMalType quasiquote(MalType ast)\n{\n    if (cast(MalSymbol)ast || cast(MalHashmap)ast)\n        return new MalList([sym_quote, ast]);\n\n    auto ast_seq = cast(MalSequential) ast;\n    if (ast_seq is null)\n        return ast;\n\n    auto aste = ast_seq.elements;\n    if (starts_with(ast, sym_unquote))\n        return aste[1];\n\n    MalType res = new MalList([]);\n    foreach_reverse (elt; ast_seq.elements)\n        if (starts_with(elt, sym_splice_unquote))\n            res = new MalList([new MalSymbol(\"concat\"), (cast(MalList) elt).elements[1], res]);\n        else\n            res = new MalList([new MalSymbol(\"cons\"), quasiquote(elt), res]);\n    if (cast(MalVector) ast)\n        res = new MalList([new MalSymbol(\"vec\"), res]);\n    return res;\n}\n\nMalType READ(string str)\n{\n    return read_str(str);\n}\n\nMalType EVAL(MalType ast, Env env)\n{\n  for (;;)\n  {\n    if (auto dbgeval = env.get(\"DEBUG-EVAL\"))\n        if (dbgeval.is_truthy())\n            writeln(\"EVAL: \", pr_str(ast));\n\n    if (auto sym = cast(MalSymbol)ast)\n    {\n        if (auto val = env.get(sym.name))\n            return val;\n        else\n            throw new Exception(\"'\" ~ sym.name ~ \"' not found\");\n    }\n    else if (auto lst = cast(MalVector)ast)\n    {\n        auto el = array(lst.elements.map!(e => EVAL(e, env)));\n        return new MalVector(el);\n    }\n    else if (auto hm = cast(MalHashmap)ast)\n    {\n        typeof(hm.data) new_data;\n        foreach (string k, MalType v; hm.data)\n        {\n            new_data[k] = EVAL(v, env);\n        }\n        return new MalHashmap(new_data);\n    }\n    else if (auto ast_list = cast(MalList)ast)\n    {\n        auto aste = ast_list.elements;\n        if (aste.length == 0)\n        {\n            return ast;\n        }\n        auto a0_sym = cast(MalSymbol) aste[0];\n        auto sym_name = a0_sym is null ? \"\" : a0_sym.name;\n        switch (sym_name)\n        {\n            case \"def!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                return env.set(a1.name, EVAL(aste[2], env));\n\n            case \"let*\":\n                auto a1 = verify_cast!MalSequential(aste[1]);\n                auto let_env = new Env(env);\n                foreach (kv; chunks(a1.elements, 2))\n                {\n                    if (kv.length < 2) throw new Exception(\"let* requires even number of elements\");\n                    auto var_name = verify_cast!MalSymbol(kv[0]);\n                    let_env.set(var_name.name, EVAL(kv[1], let_env));\n                }\n                ast = aste[2];\n                env = let_env;\n                continue; // TCO\n\n            case \"quote\":\n                return aste[1];\n\n            case \"quasiquote\":\n                ast = quasiquote(aste[1]);\n                continue; // TCO\n\n            case \"defmacro!\":\n                auto a1 = verify_cast!MalSymbol(aste[1]);\n                auto mac = verify_cast!MalFunc(EVAL(aste[2], env));\n                mac = new MalFunc(mac.arg_names, mac.func_body, mac.def_env);\n                mac.is_macro = true;\n                return env.set(a1.name, mac);\n\n            case \"try*\":\n                if (aste.length < 2) return mal_nil;\n                if (aste.length < 3)\n                {\n                    ast = aste[1];\n                    continue; // TCO\n                }\n                MalType exc;\n                try\n                {\n                    // d seems to do erroneous tco all by itself without this\n                    // little distraction\n                    pr_str(aste[1]);\n                    return EVAL(aste[1], env);\n                }\n                catch (MalException e)\n                {\n                    exc = e.data;\n                }\n                catch (Exception e)\n                {\n                    exc = new MalString(e.msg);\n                }\n                auto catch_clause = verify_cast!MalList(aste[2]);\n                auto catch_env = new Env(env, [catch_clause.elements[1]], [exc]);\n                ast = catch_clause.elements[2];\n                env = catch_env;\n                continue; // TCO\n\n            case \"do\":\n                foreach (elt; aste[1..$-1]) {\n                    EVAL(elt, env);\n                }\n                ast = aste[$-1];\n                continue; // TCO\n\n            case \"if\":\n                auto cond = EVAL(aste[1], env);\n                if (cond.is_truthy())\n                {\n                    ast = aste[2];\n                    continue; // TCO\n                }\n                else\n                    if (aste.length > 3)\n                    {\n                        ast = aste[3];\n                        continue; // TCO\n                    }\n                    else\n                    {\n                        return mal_nil;\n                    }\n\n            case \"fn*\":\n                auto args_list = verify_cast!MalSequential(aste[1]);\n                return new MalFunc(args_list.elements, aste[2], env);\n\n            default:\n                auto first = EVAL(aste[0], env);\n                auto rest = aste[1..$];\n                if (auto funcobj = cast(MalFunc)first)\n                {\n                    if (funcobj.is_macro) {\n                        auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                        ast = EVAL(funcobj.func_body, callenv);\n                        continue; // TCO\n                    }\n                    rest = array(rest.map!(e => EVAL(e, env)));\n                    auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);\n                    ast = funcobj.func_body;\n                    env = callenv;\n                    continue; // TCO\n                }\n                else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)\n                {\n                    rest = array(rest.map!(e => EVAL(e, env)));\n                    return builtinfuncobj.fn(rest);\n                }\n                else\n                {\n                    throw new Exception(\"Expected a function\");\n                }\n        }\n    }\n    else\n    {\n        return ast;\n    }\n  }\n}\n\nstring PRINT(MalType ast)\n{\n    return pr_str(ast);\n}\n\nMalType re(string str, Env env)\n{\n    return EVAL(READ(str), env);\n}\n\nstring rep(string str, Env env)\n{\n    return PRINT(re(str, env));\n}\n\nstatic MalList create_argv_list(string[] args)\n{\n    if (args.length <= 2) return new MalList([]);\n    return new MalList(array(args[2..$].map!(s => cast(MalType)(new MalString(s)))));\n}\n\nvoid main(string[] args)\n{\n    Env repl_env = new Env(null);\n    foreach (string sym_name, BuiltinStaticFuncType f; core_ns)\n    {\n        repl_env.set(sym_name, new MalBuiltinFunc(f, sym_name));\n    }\n\n    BuiltinFuncType eval_func = (a ...) {\n        verify_args_count(a, 1);\n        return EVAL(a[0], repl_env);\n    };\n    repl_env.set(\"eval\", new MalBuiltinFunc(eval_func, \"eval\"));\n    repl_env.set(\"*ARGV*\", create_argv_list(args));\n\n    // core.mal: defined using the language itself\n    re(\"(def! *host-language* \\\"\" ~ std.compiler.name ~ \"\\\")\", repl_env);\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n    re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n    if (args.length > 1)\n    {\n        try\n        {\n            rep(\"(load-file \\\"\" ~ args[1] ~ \"\\\")\", repl_env);\n            return;\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n            exit(1);\n        }\n    }\n\n    re(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env);\n    for (;;)\n    {\n        string line = _readline(\"user> \");\n        if (line is null) break;\n        if (line.length == 0) continue;\n        try\n        {\n            writeln(rep(line, repl_env));\n        }\n        catch (MalException e)\n        {\n            writeln(\"Error: \", pr_str(e.data));\n        }\n        catch (Exception e)\n        {\n            writeln(\"Error: \", e.msg);\n        }\n    }\n    writeln(\"\");\n}\n"
  },
  {
    "path": "impls/d/tests/step5_tco.mal",
    "content": ";; D: skipping non-TCO recursion\n;; Reason: completes at 10,000, segfaults at 40,000\n"
  },
  {
    "path": "impls/d/types.d",
    "content": "import std.algorithm;\nimport std.array;\nimport std.conv;\nimport std.functional;\nimport std.range;\nimport env;\n\nabstract class MalType\n{\n    string print(bool readable) const;\n    bool is_truthy() const { return true; }\n}\n\ninterface MalMeta\n{\n    MalType meta();\n    MalType with_meta(MalType new_meta);\n}\n\ninterface HasSeq\n{\n    MalType seq();\n}\n\nclass MalNil : MalType, HasSeq\n{\n    override string print(bool readable) const { return \"nil\"; }\n    override bool is_truthy() const { return false; }\n    override bool opEquals(Object o) { return (cast(MalNil)(o) !is null); }\n    override MalType seq() { return this; }\n}\n\nclass MalFalse : MalType\n{\n    override string print(bool readable) const { return \"false\"; }\n    override bool is_truthy() const { return false; }\n    override bool opEquals(Object o) { return (cast(MalFalse)(o) !is null); }\n}\n\nclass MalTrue : MalType\n{\n    override string print(bool readable) const { return \"true\"; }\n    override bool opEquals(Object o) { return (cast(MalTrue)(o) !is null); }\n}\n\nMalNil mal_nil;\nMalFalse mal_false;\nMalTrue mal_true;\n\nstatic this()\n{\n    mal_nil = new MalNil;\n    mal_false = new MalFalse;\n    mal_true = new MalTrue;\n}\n\nMalType bool_to_mal(in bool b)\n{\n    return b ? mal_true : mal_false;\n}\n\nclass MalSymbol : MalType\n{\n    const string name;\n    this(in string token) { name = token; }\n    override string print(bool readable) const { return name; }\n\n    override size_t toHash()\n    {\n        return typeid(name).getHash(&name);\n    }\n\n    override int opCmp(Object other)\n    {\n        MalSymbol o = cast(MalSymbol) other;\n        return cmp(name, o.name);\n    }\n\n    override bool opEquals(Object other)\n    {\n        auto o = cast(MalSymbol) other;\n        return (o !is null && name == o.name);\n    }\n}\n\nclass MalInteger : MalType\n{\n    const long val;\n    this(string token) { val = to!long(token); }\n    this(long v) { val = v; }\n    override string print(bool readable) const { return to!string(val); }\n\n    override bool opEquals(Object o)\n    {\n        auto oint = cast(MalInteger)(o);\n        return (oint !is null && val == oint.val);\n    }\n}\n\nclass MalString : MalType, HasSeq\n{\n    const string val;\n    this(in string token) { val = token; }\n    override string print(bool readable) const\n    {\n        if (is_keyword()) return \":\" ~ val[2..$];\n        if (readable)\n        {\n            string escaped = val.replace(\"\\\\\", \"\\\\\\\\\")\n                                .replace(\"\\\"\", \"\\\\\\\"\")\n                                .replace(\"\\n\", \"\\\\n\");\n            return \"\\\"\" ~ escaped ~ \"\\\"\";\n        }\n        else\n        {\n            return val;\n        }\n    }\n\n    bool is_keyword() const\n    {\n        return val.length > 1 && val[0..2] == \"\\u029e\";\n    }\n\n    override bool opEquals(Object o)\n    {\n        auto ostr = cast(MalString)(o);\n        return (ostr !is null && val == ostr.val);\n    }\n\n    override MalType seq() {\n        if (is_keyword() || val.length == 0) return mal_nil;\n        auto chars = val.map!(c => cast(MalType)(new MalString(to!string(c))));\n        return new MalList(array(chars));\n    }\n}\n\nabstract class MalSequential : MalType, HasSeq, MalMeta\n{\n    MalType[] elements;\n    MalType meta_val;\n\n    this(MalType[] lst) {\n        elements = lst;\n        meta_val = mal_nil;\n    }\n\n    override bool opEquals(Object o)\n    {\n        auto oseq = cast(MalSequential)(o);\n        return (oseq !is null && elements == oseq.elements);\n    }\n\n    MalSequential conj(MalType element);\n\n    MalType seq() {\n        if (elements.length == 0) return mal_nil;\n        return new MalList(elements);\n    }\n}\n\nclass MalList : MalSequential, MalMeta\n{\n    this(MalType[] lst) { super(lst); }\n    this(MalList that, MalType new_meta)\n    {\n        super(that.elements);\n        meta_val = new_meta;\n    }\n\n    override string print(bool readable) const\n    {\n        auto items_strs = elements.map!(e => e.print(readable));\n        return \"(\" ~ array(items_strs).join(\" \") ~ \")\";\n    }\n\n    override MalSequential conj(MalType element)\n    {\n        return new MalList([element] ~ elements);\n    }\n\n    override MalType meta() { return meta_val; }\n    override MalType with_meta(MalType new_meta)\n    {\n        return new MalList(this, new_meta);\n    }\n}\n\nclass MalVector : MalSequential, MalMeta\n{\n    this(MalType[] lst) { super(lst); }\n    this(MalVector that, MalType new_meta)\n    {\n        super(that.elements);\n        meta_val = new_meta;\n    }\n\n    override string print(bool readable) const\n    {\n        auto items_strs = elements.map!(e => e.print(readable));\n        return \"[\" ~ array(items_strs).join(\" \") ~ \"]\";\n    }\n\n    override MalSequential conj(MalType element)\n    {\n        return new MalVector(elements ~ [element]);\n    }\n\n    override MalType meta() { return meta_val; }\n    override MalType with_meta(MalType new_meta)\n    {\n        return new MalVector(this, new_meta);\n    }\n}\n\nclass MalHashmap : MalType, MalMeta\n{\n    MalType[string] data;\n    MalType meta_val;\n\n    this(MalType[string] map)\n    {\n        data = map;\n        meta_val = mal_nil;\n    }\n    this(MalType[] lst)\n    {\n        put_kv_list(lst);\n        meta_val = mal_nil;\n    }\n    this(MalHashmap that, MalType new_meta)\n    {\n        data = that.data;\n        meta_val = new_meta;\n    }\n\n    bool contains(in MalType key)\n    {\n        auto valp = (make_hash_key(key) in data);\n        return valp !is null;\n    }\n\n    MalType get(in MalType key)\n    {\n        auto valp = (make_hash_key(key) in data);\n        return valp is null ? mal_nil : *valp;\n    }\n\n    void remove(in MalType key)\n    {\n        data.remove(make_hash_key(key));\n    }\n\n    void put(in MalType key, MalType val)\n    {\n        data[make_hash_key(key)] = val;\n    }\n\n    void put_kv_list(MalType[] lst)\n    {\n        foreach (kv; chunks(lst, 2))\n        {\n            if (kv.length < 2) throw new Exception(\"requires even number of elements\");\n            put(kv[0], kv[1]);\n        }\n    }\n\n    private string make_hash_key(in MalType key)\n    {\n        return verify_cast!MalString(key).val;\n    }\n\n    override string print(bool readable) const\n    {\n        string[] parts;\n        foreach (k, v; data)\n        {\n            parts ~= (new MalString(k)).print(readable);\n            parts ~= v.print(readable);\n        }\n        return \"{\" ~ parts.join(\" \") ~ \"}\";\n    }\n\n    override bool opEquals(Object o)\n    {\n        auto ohm = cast(MalHashmap)(o);\n        return (ohm !is null && data == ohm.data);\n    }\n\n    override MalType meta() { return meta_val; }\n    override MalType with_meta(MalType new_meta)\n    {\n        return new MalHashmap(this, new_meta);\n    }\n}\n\nalias BuiltinStaticFuncType = MalType function(MalType[] a ...);\nalias BuiltinFuncType = MalType delegate(MalType[] a ...);\n\nclass MalBuiltinFunc : MalType, MalMeta\n{\n    const BuiltinFuncType fn;\n    const string name;\n    MalType meta_val;\n\n    this(in BuiltinFuncType fn_v, in string name_v)\n    {\n        fn = fn_v;\n        name = name_v;\n        meta_val = mal_nil;\n    }\n\n    this(in BuiltinStaticFuncType static_fn_v, in string name_v)\n    {\n        fn = toDelegate(static_fn_v);\n        name = name_v;\n        meta_val = mal_nil;\n    }\n\n    this(MalBuiltinFunc that, MalType new_meta)\n    {\n        fn = that.fn;\n        name = that.name;\n        meta_val = new_meta;\n    }\n\n    override string print(bool readable) const\n    {\n        return \"<BuiltinFunc:\" ~ name ~ \">\";\n    }\n\n    override MalType meta() { return meta_val; }\n\n    override MalType with_meta(MalType new_meta)\n    {\n        return new MalBuiltinFunc(this, new_meta);\n    }\n}\n\nclass MalFunc : MalType, MalMeta\n{\n    MalType[] arg_names;\n    MalType func_body;\n    Env def_env;\n    bool is_macro;\n    MalType meta_val;\n\n    this(MalType[] arg_names_v, MalType func_body_v, Env def_env_v)\n    {\n        arg_names = arg_names_v;\n        func_body = func_body_v;\n        def_env = def_env_v;\n        is_macro = false;\n        meta_val = mal_nil;\n    }\n\n    this(MalFunc that, MalType new_meta)\n    {\n        arg_names = that.arg_names;\n        func_body = that.func_body;\n        def_env = that.def_env;\n        is_macro = that.is_macro;\n        meta_val = new_meta;\n    }\n\n    override string print(bool readable) const\n    {\n        return \"<Function:args=\" ~ array(arg_names.map!(e => e.print(true))).join(\",\") ~ \">\";\n    }\n\n    override MalType meta() { return meta_val; }\n\n    override MalType with_meta(MalType new_meta)\n    {\n        return new MalFunc(this, new_meta);\n    }\n}\n\nclass MalAtom : MalType, MalMeta\n{\n    MalType val;\n    MalType meta_val;\n\n    this(MalType v)\n    {\n        val = v;\n        meta_val = mal_nil;\n    }\n\n    this(MalAtom that, MalType new_meta)\n    {\n        val = that.val;\n        meta_val = new_meta;\n    }\n\n    override string print(bool readable) const\n    {\n        return \"(atom \" ~ val.print(readable) ~ \")\";\n    }\n\n    override bool opEquals(Object other)\n    {\n        auto o = cast(MalAtom) other;\n        return (o !is null && val == o.val);\n    }\n\n    override MalType meta() { return meta_val; }\n\n    override MalType with_meta(MalType new_meta)\n    {\n        return new MalAtom(this, new_meta);\n    }\n}\n\nclass MalException : Exception\n{\n    MalType data;\n\n    this(MalType val)\n    {\n        super(\"MalException\");\n        data = val;\n    }\n}\n\nT verify_cast(T)(in MalType v)\n{\n    if (T res = cast(T) v) return res;\n    throw new Exception(\"Expected \" ~ typeid(T).name);\n}\n\nMalType mal_type_q(T)(in MalType[] a)\n{\n    verify_args_count(a, 1);\n    T res = cast(T) a[0];\n    return bool_to_mal(res !is null);\n}\n\ninout(MalType[]) verify_args_count(inout MalType[] args, in int expected_length)\n{\n    if (args.length != expected_length)\n    {\n        throw new Exception(\"Expected \" ~ to!string(expected_length) ~ \" arguments\");\n    }\n    return args;\n}\n\nvoid verify_min_args_count(in MalType[] args, in int min_expected_length)\n{\n    if (args.length < min_expected_length)\n    {\n        throw new Exception(\"Expected at least \" ~ to!string(min_expected_length) ~ \" arguments\");\n    }\n}\n"
  },
  {
    "path": "impls/dart/.analysis_options",
    "content": "analyzer:\n  strong-mode: true\n  exclude:\n    - step2_eval.dart\n    - step3_env.dart\n    - step4_if_fn_do.dart\n    - step5_tco.dart\n"
  },
  {
    "path": "impls/dart/.packages",
    "content": "# Generated by pub on 2016-08-20 13:39:08.695546.\nmal:lib/\n"
  },
  {
    "path": "impls/dart/Dockerfile",
    "content": "FROM ubuntu:vivid\nMAINTAINER Harry Terkelsen <harry@terkelsen.io>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install apt-transport-https\nRUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -\nRUN curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list\nRUN apt-get -y update\n\nRUN apt-get -y install dart\n"
  },
  {
    "path": "impls/dart/Makefile",
    "content": "all:\n\t@true\n\n\nclean:\n"
  },
  {
    "path": "impls/dart/core.dart",
    "content": "import 'dart:io';\n\nimport 'printer.dart';\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nMap<String, MalBuiltin> ns = <String, MalBuiltin>{\n  '+': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value + b.value);\n  }),\n  '-': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value - b.value);\n  }),\n  '*': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value * b.value);\n  }),\n  '/': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value ~/ b.value);\n  }),\n  'list':\n      new MalBuiltin((List<MalType> args) => new MalList(args.toList())),\n  'list?': new MalBuiltin(\n      (List<MalType> args) => new MalBool(args.single is MalList)),\n  'empty?': new MalBuiltin((List<MalType> args) {\n    var a = args.single as MalIterable;\n    return new MalBool(a.elements.isEmpty);\n  }),\n  'count': new MalBuiltin((List<MalType> args) {\n    var a = args.first as MalIterable;\n    return new MalInt(a.elements.length);\n  }),\n  '=': new MalBuiltin((List<MalType> args) {\n    var a = args[0];\n    var b = args[1];\n    return new MalBool(a == b);\n  }),\n  '<': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalBool(a.value < b.value);\n  }),\n  '<=': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalBool(a.value <= b.value);\n  }),\n  '>': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalBool(a.value > b.value);\n  }),\n  '>=': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalBool(a.value >= b.value);\n  }),\n  'pr-str': new MalBuiltin((List<MalType> args) {\n    return new MalString(\n        args.map((a) => pr_str(a, print_readably: true)).join(' '));\n  }),\n  'str': new MalBuiltin((List<MalType> args) {\n    return new MalString(\n        args.map((a) => pr_str(a, print_readably: false)).join());\n  }),\n  'prn': new MalBuiltin((List<MalType> args) {\n    print(args.map((a) => pr_str(a, print_readably: true)).join(' '));\n    return new MalNil();\n  }),\n  'println': new MalBuiltin((List<MalType> args) {\n    print(args.map((a) => pr_str(a, print_readably: false)).join(' '));\n    return new MalNil();\n  }),\n  'read-string': new MalBuiltin((List<MalType> args) {\n    var code = args.single as MalString;\n    return reader.read_str(code.value);\n  }),\n  'slurp': new MalBuiltin((List<MalType> args) {\n    var fileName = args.single as MalString;\n    var file = new File(fileName.value);\n    return new MalString(file.readAsStringSync());\n  }),\n  'atom': new MalBuiltin((List<MalType> args) {\n    var value = args.single;\n    return new MalAtom(value);\n  }),\n  'atom?': new MalBuiltin((List<MalType> args) {\n    var value = args.single;\n    return new MalBool(value is MalAtom);\n  }),\n  'deref': new MalBuiltin((List<MalType> args) {\n    var atom = args.single as MalAtom;\n    return atom.value;\n  }),\n  'reset!': new MalBuiltin((List<MalType> args) {\n    var atom = args[0] as MalAtom;\n    var newValue = args[1];\n    atom.value = newValue;\n    return newValue;\n  }),\n  'swap!': new MalBuiltin((List<MalType> args) {\n    var atom = args[0] as MalAtom;\n    var func = args[1] as MalCallable;\n    var fnArgs = [atom.value]..addAll(args.sublist(2));\n    var result = func.call(fnArgs);\n    atom.value = result;\n    return result;\n  }),\n  'cons': new MalBuiltin((List<MalType> args) {\n    var x = args[0];\n    var xs = args[1] as MalIterable;\n    return new MalList([x]..addAll(xs));\n  }),\n  'concat': new MalBuiltin((List<MalType> args) {\n    var results = <MalType>[];\n    for (MalIterable element in args) {\n      results.addAll(element);\n    }\n    return new MalList(results);\n  }),\n  'vec': new MalBuiltin((List<MalType> args) {\n    if (args.length == 1) {\n      if (args[0] is MalVector) return args[0];\n      if (args[0] is MalList) return new MalVector((args[0] as MalList).elements);\n    }\n    throw new MalException(new MalString(\"vec: wrong arguments\"));\n  }),\n  'nth': new MalBuiltin((List<MalType> args) {\n    var indexable = args[0] as MalIterable;\n    var index = args[1] as MalInt;\n    try {\n      return indexable[index.value];\n    } on RangeError catch (e) {\n      throw new MalException(new MalString(e.toString()));\n    }\n  }),\n  'first': new MalBuiltin((List<MalType> args) {\n    var list = args.first as MalIterable;\n    if (list.isEmpty) return new MalNil();\n    return list.first;\n  }),\n  'rest': new MalBuiltin((List<MalType> args) {\n    var list = args.first as MalIterable;\n    if (list.isEmpty) return new MalList(<MalType>[]);\n    return new MalList(list.sublist(1));\n  }),\n  'throw': new MalBuiltin((List<MalType> args) {\n    throw new MalException(args.first);\n  }),\n  'nil?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalNil);\n  }),\n  'true?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalBool && (args.first as MalBool).value);\n  }),\n  'false?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalBool && !(args.first as MalBool).value);\n  }),\n  'symbol': new MalBuiltin((List<MalType> args) {\n    return new MalSymbol((args.first as MalString).value);\n  }),\n  'symbol?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalSymbol);\n  }),\n  'keyword': new MalBuiltin((List<MalType> args) {\n    if (args.first is MalKeyword) return args.first;\n    return new MalKeyword((args.first as MalString).value);\n  }),\n  'keyword?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalKeyword);\n  }),\n  'number?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalInt);\n  }),\n  'fn?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalCallable && !(args.first.isMacro));\n  }),\n  'macro?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalCallable && args.first.isMacro);\n  }),\n  'vector': new MalBuiltin((List<MalType> args) {\n    return new MalVector(args);\n  }),\n  'vector?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalVector);\n  }),\n  'hash-map': new MalBuiltin((List<MalType> args) {\n    return new MalHashMap.fromSequence(args);\n  }),\n  'map?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalHashMap);\n  }),\n  'assoc': new MalBuiltin((List<MalType> args) {\n    var map = args.first as MalHashMap;\n    var assoc = new MalHashMap.fromSequence(args.skip(1).toList());\n    var newMap = new Map<MalType, MalType>.from(map.value);\n    newMap.addAll(assoc.value);\n    return new MalHashMap(newMap);\n  }),\n  'dissoc': new MalBuiltin((List<MalType> args) {\n    var map = args.first as MalHashMap;\n    var newMap = new Map<MalType, MalType>.from(map.value);\n    for (var key in args.skip(1)) {\n      newMap.remove(key);\n    }\n    return new MalHashMap(newMap);\n  }),\n  'get': new MalBuiltin((List<MalType> args) {\n    if (args[0] is MalNil) return new MalNil();\n    var map = args[0] as MalHashMap;\n    var key = args[1];\n    return map.value[key] ?? new MalNil();\n  }),\n  'contains?': new MalBuiltin((List<MalType> args) {\n    var map = args[0] as MalHashMap;\n    var key = args[1];\n    return new MalBool(map.value.containsKey(key));\n  }),\n  'keys': new MalBuiltin((List<MalType> args) {\n    return new MalList((args.first as MalHashMap).value.keys.toList());\n  }),\n  'vals': new MalBuiltin((List<MalType> args) {\n    return new MalList((args.first as MalHashMap).value.values.toList());\n  }),\n  'sequential?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalList || args.first is MalVector);\n  }),\n  'readline': new MalBuiltin((List<MalType> args) {\n    var message = args.first as MalString;\n    stdout.write(message.value);\n    var input = stdin.readLineSync();\n    if (input == null) return new MalNil();\n    return new MalString(input);\n  }),\n  'time-ms': new MalBuiltin((List<MalType> args) {\n    assert(args.isEmpty);\n    return new MalInt(new DateTime.now().millisecondsSinceEpoch);\n  }),\n  'conj': new MalBuiltin((List<MalType> args) {\n    var collection = args.first;\n    var elements = args.sublist(1);\n    if (collection is MalList) {\n      return new MalList(\n          elements.reversed.toList()..addAll(collection.elements));\n    }\n    if (collection is MalVector) {\n      return new MalVector(collection.elements.toList()..addAll(elements));\n    }\n    throw new MalException(new MalString('\"conj\" takes a list or vector'));\n  }),\n  'string?': new MalBuiltin((List<MalType> args) {\n    return new MalBool(args.first is MalString);\n  }),\n  'seq': new MalBuiltin((List<MalType> args) {\n    var arg = args.first;\n    if (arg is MalIterable && arg.isEmpty) return new MalNil();\n    if (arg is MalString && arg.value.isEmpty) return new MalNil();\n\n    if (arg is MalNil || arg is MalList) return arg;\n    if (arg is MalVector) return new MalList(arg.elements.toList());\n    if (arg is MalString) {\n      var chars = <MalString>[];\n      for (var i = 0; i < arg.value.length; i++) {\n        chars.add(new MalString(arg.value[i]));\n      }\n      return new MalList(chars);\n    }\n    throw new MalException(new MalString('bad argument to \"seq\"'));\n  }),\n  'map': new MalBuiltin((List<MalType> args) {\n    var fn = args[0] as MalCallable;\n    var list = args[1] as MalIterable;\n    var newList = <MalType>[];\n    for (var element in list) {\n      newList.add(fn.call([element]));\n    }\n    return new MalList(newList);\n  }),\n  'apply': new MalBuiltin((List<MalType> args) {\n    var func = args.first as MalCallable;\n    var argList = args.last as MalIterable;\n    var newArgs = args.sublist(1, args.length - 1);\n    newArgs.addAll(argList);\n    return func.call(newArgs);\n  }),\n  'meta': new MalBuiltin((List<MalType> args) {\n    var arg = args.first;\n    return arg.meta ?? new MalNil();\n  }),\n  'with-meta': new MalBuiltin((List<MalType> args) {\n    var evaled = args.first;\n    var evaledWithMeta = evaled.clone();\n    evaledWithMeta.meta = args[1];\n    return evaledWithMeta;\n  }),\n};\n"
  },
  {
    "path": "impls/dart/env.dart",
    "content": "import 'types.dart';\n\nclass Env {\n  final Env outer;\n\n  final data = <String, MalType>{};\n\n  Env([this.outer, List<MalSymbol> binds, List<MalType> exprs]) {\n    if (binds == null) {\n      assert(exprs == null);\n    } else {\n      assert(exprs != null &&\n          (binds.length == exprs.length || binds.contains(new MalSymbol('&'))));\n      for (var i = 0; i < binds.length; i++) {\n        if (binds[i].value == '&') {\n          set(binds[i + 1].value, new MalList(exprs.sublist(i)));\n          break;\n        }\n        set(binds[i].value, exprs[i]);\n      }\n    }\n  }\n\n  void set(String key, MalType value) {\n    data[key] = value;\n  }\n\n  MalType get(String key) {\n    var value = data[key];\n    if (value != null) {\n      return value;\n    }\n    if (outer != null) {\n      return outer.get(key);\n    }\n    return null;\n  }\n}\n\nclass NotFoundException implements Exception {\n  /// The name of the symbol that was not found.\n  final String value;\n\n  NotFoundException(this.value);\n\n  String toString() => \"'$value' not found\";\n}\n"
  },
  {
    "path": "impls/dart/printer.dart",
    "content": "import 'types.dart';\n\nString pr_str(MalType data, {bool print_readably: true}) {\n  if (data is MalSymbol) {\n    return data.value;\n  } else if (data is MalInt) {\n    return '${data.value}';\n  } else if (data is MalList) {\n    var printedElements =\n        data.elements.map((e) => pr_str(e, print_readably: print_readably));\n    return '(${printedElements.join(\" \")})';\n  } else if (data is MalVector) {\n    var printedElements =\n        data.elements.map((e) => pr_str(e, print_readably: print_readably));\n    return '[${printedElements.join(\" \")}]';\n  } else if (data is MalHashMap) {\n    var printedElements = <String>[];\n    data.value.forEach((key, value) {\n      printedElements.add(pr_str(key, print_readably: print_readably));\n      printedElements.add(pr_str(value, print_readably: print_readably));\n    });\n    return '{${printedElements.join(\" \")}}';\n  } else if (data is MalString) {\n    if (print_readably) {\n      var readableValue = data.value\n          .replaceAll('\\\\', r'\\\\')\n          .replaceAll('\\n', r'\\n')\n          .replaceAll('\\\"', r'\\\"');\n      return '\"$readableValue\"';\n    } else {\n      return '${data.value}';\n    }\n  } else if (data is MalKeyword) {\n    return ':${data.value}';\n  } else if (data is MalBool) {\n    return '${data.value}';\n  } else if (data is MalNil) {\n    return 'nil';\n  } else if (data is MalBuiltin) {\n    return '#<built in function>';\n  } else if (data is MalClosure) {\n    return '#<function>';\n  } else if (data is MalAtom) {\n    return \"(atom ${pr_str(data.value, print_readably: print_readably)})\";\n  }\n  throw new ArgumentError(\"Unrecognized type: ${data.runtimeType}\");\n}\n"
  },
  {
    "path": "impls/dart/pubspec.yaml",
    "content": "name: mal\nauthor: Harry Terkelsen <harry@terkelsen.io>\nversion: 0.0.1\n"
  },
  {
    "path": "impls/dart/reader.dart",
    "content": "import 'types.dart';\n\nfinal malRegExp = new RegExp(\n    r\"\"\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)\"\"\");\nfinal strRegExp = new RegExp(\n    r\"\"\"^\"(?:\\\\.|[^\\\\\"])*\"$\"\"\");\n\nclass Reader {\n  final List<String> tokens;\n  int _position = 0;\n\n  Reader(this.tokens);\n\n  String next() {\n    var token = peek();\n    _position++;\n    return token;\n  }\n\n  String peek() {\n    if (_position >= tokens.length) return null;\n    return tokens[_position];\n  }\n}\n\nclass ParseException implements Exception {\n  final String message;\n\n  ParseException(this.message);\n}\n\nclass NoInputException implements Exception {}\n\nMalType read_str(String code) {\n  var tokens = tokenizer(code);\n  if (tokens.isEmpty) {\n    throw new NoInputException();\n  }\n  var reader = new Reader(tokens);\n  return read_form(reader);\n}\n\nList<String> tokenizer(String code) {\n  var matches = malRegExp.allMatches(code);\n  return matches\n      .map((m) => m.group(1))\n      .where((token) => token.isNotEmpty && !token.startsWith(';'))\n      .toList();\n}\n\nMalType read_form(Reader reader) {\n  const macros = const <String, String>{\n    \"'\": 'quote',\n    '`': 'quasiquote',\n    '~': 'unquote',\n    '~@': 'splice-unquote',\n    '@': 'deref',\n    '^': 'with-meta',\n  };\n  const sequenceStarters = const <String, String>{'(': ')', '[': ']', '{': '}'};\n  var token = reader.peek();\n  if (sequenceStarters.containsKey(token)) {\n    var elements = read_sequence(reader, token, sequenceStarters[token]);\n    if (token == '(') {\n      return new MalList(elements);\n    }\n    if (token == '[') {\n      return new MalVector(elements);\n    }\n\n    if (token == '{') {\n      return new MalHashMap.fromSequence(elements);\n    }\n\n    throw new StateError(\"Impossible!\");\n  } else if (macros.containsKey(token)) {\n    var macro = new MalSymbol(macros[token]);\n    reader.next();\n    var form = read_form(reader);\n    if (token == '^') {\n      var meta = read_form(reader);\n      return new MalList([macro, meta, form]);\n    } else {\n      return new MalList([macro, form]);\n    }\n  } else {\n    return read_atom(reader);\n  }\n}\n\nList<MalType> read_sequence(Reader reader, String open, String close) {\n  // Consume opening token\n  var actualOpen = reader.next();\n  assert(actualOpen == open);\n\n  var elements = <MalType>[];\n  for (var token = reader.peek();; token = reader.peek()) {\n    if (token == null) {\n      throw new ParseException(\"expected '$close', got EOF\");\n    }\n    if (token == close) break;\n    elements.add(read_form(reader));\n  }\n\n  var actualClose = reader.next();\n  assert(actualClose == close);\n\n  return elements;\n}\n\nMalType read_atom(Reader reader) {\n  var token = reader.next();\n\n  var intAtom = int.parse(token, onError: (_) => null);\n  if (intAtom != null) {\n    return new MalInt(intAtom);\n  }\n\n  if (strRegExp.matchAsPrefix(token) != null) {\n    var sanitizedToken = token\n        // remove surrounding quotes\n        .substring(1, token.length - 1)\n        .replaceAllMapped(new RegExp(\"\\\\\\\\(.)\"),\n                          (Match m) => m[1] == 'n' ? '\\n' : m[1]);\n    return new MalString(sanitizedToken);\n  }\n\n  if (token[0] == '\"') {\n    throw new ParseException(\"expected '\\\"', got EOF\");\n  }\n\n  if (token[0] == ':') {\n    return new MalKeyword(token.substring(1));\n  }\n\n  if (token == 'nil') {\n    return new MalNil();\n  }\n\n  if (token == 'true') {\n    return new MalBool(true);\n  }\n\n  if (token == 'false') {\n    return new MalBool(false);\n  }\n\n  return new MalSymbol(token);\n}\n"
  },
  {
    "path": "impls/dart/run",
    "content": "#!/usr/bin/env bash\nexec dart --checked $(dirname $0)/${STEP:-stepA_mal}.dart \"${@}\"\n"
  },
  {
    "path": "impls/dart/step0_repl.dart",
    "content": "import 'dart:io';\n\nString READ(String x) => x;\n\nString EVAL(String x) => x;\n\nString PRINT(String x) => x;\n\nString rep(String x) => PRINT(EVAL(READ(x)));\n\nconst prompt = 'user> ';\nmain() {\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output = rep(input);\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step1_read_print.dart",
    "content": "import 'dart:io';\n\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType x) => x;\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x)));\n}\n\nconst prompt = 'user> ';\nmain() {\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step2_eval.dart",
    "content": "import 'dart:io';\n\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Map<String, MalType> replEnv = <String, MalType>{\n  '+': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value + b.value);\n  }),\n  '-': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value - b.value);\n  }),\n  '*': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value * b.value);\n  }),\n  '/': new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value ~/ b.value);\n  })\n};\n\nMalType READ(String x) => reader.read_str(x);\n\nclass NotFoundException implements Exception {\n  /// The name of the symbol that was not found.\n  final String value;\n\n  NotFoundException(this.value);\n}\n\nMalType EVAL(MalType ast, Map<String, MalType> env) {\n  // stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n\n  if (ast is MalSymbol) {\n    var result = env[ast.value];\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n    // ast is a list. todo: indent left.\n    var forms = (ast as MalList).elements;\n    if (forms.isEmpty) {\n      return ast;\n    } else {\n      MalBuiltin f = EVAL(forms.first, env);\n      List<MalType> args = forms.sublist(1).map((x) => EVAL(x, env)).toList();\n      return f.call(args);\n    }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain() {\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step3_env.dart",
    "content": "import 'dart:io';\n\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv() {\n  replEnv.set('+', new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value + b.value);\n  }));\n  replEnv.set('-', new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value - b.value);\n  }));\n  replEnv.set('*', new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value * b.value);\n  }));\n  replEnv.set('/', new MalBuiltin((List<MalType> args) {\n    var a = args[0] as MalInt;\n    var b = args[1] as MalInt;\n    return new MalInt(a.value ~/ b.value);\n  }));\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n    // ast is a list. todo: indent left.\n    if ((ast as MalList).elements.isEmpty) {\n      return ast;\n    } else {\n      var list = ast as MalList;\n      if (list.elements.first is MalSymbol) {\n        var symbol = list.elements.first as MalSymbol;\n        var args = list.elements.sublist(1);\n        if (symbol.value == \"def!\") {\n          MalSymbol key = args.first;\n          MalType value = EVAL(args[1], env);\n          env.set(key.value, value);\n          return value;\n        } else if (symbol.value == \"let*\") {\n          // TODO(het): If elements.length is not even, give helpful error\n          Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n            for (var i = 0; i < elements.length; i += 2) {\n              yield [elements[i], elements[i + 1]];\n            }\n          }\n\n          var newEnv = new Env(env);\n          MalIterable bindings = args.first;\n          for (var pair in pairs(bindings.elements)) {\n            MalSymbol key = pair[0];\n            MalType value = EVAL(pair[1], newEnv);\n            newEnv.set(key.value, value);\n          }\n          return EVAL(args[1], newEnv);\n        }\n      }\n      MalBuiltin f = EVAL(list.elements.first, env);\n      List<MalType> args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n      return f.call(args);\n    }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain() {\n  setupEnv();\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step4_if_fn_do.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv() {\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n    // ast is a list. todo: indent left.\n    if ((ast as MalList).elements.isEmpty) {\n      return ast;\n    } else {\n      var list = ast as MalList;\n      if (list.elements.first is MalSymbol) {\n        var symbol = list.elements.first as MalSymbol;\n        var args = list.elements.sublist(1);\n        if (symbol.value == \"def!\") {\n          MalSymbol key = args.first;\n          MalType value = EVAL(args[1], env);\n          env.set(key.value, value);\n          return value;\n        } else if (symbol.value == \"let*\") {\n          // TODO(het): If elements.length is not even, give helpful error\n          Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n            for (var i = 0; i < elements.length; i += 2) {\n              yield [elements[i], elements[i + 1]];\n            }\n          }\n\n          var newEnv = new Env(env);\n          MalIterable bindings = args.first;\n          for (var pair in pairs(bindings.elements)) {\n            MalSymbol key = pair[0];\n            MalType value = EVAL(pair[1], newEnv);\n            newEnv.set(key.value, value);\n          }\n          return EVAL(args[1], newEnv);\n        } else if (symbol.value == \"do\") {\n          return args.map((e) => EVAL(e, env)).toList().last;\n        } else if (symbol.value == \"if\") {\n          var condition = EVAL(args[0], env);\n          if (condition is MalNil ||\n              condition is MalBool && condition.value == false) {\n            // False side of branch\n            if (args.length < 3) {\n              return new MalNil();\n            }\n            return EVAL(args[2], env);\n          } else {\n            // True side of branch\n            return EVAL(args[1], env);\n          }\n        } else if (symbol.value == \"fn*\") {\n          var params = (args[0] as MalIterable)\n              .elements\n              .map((e) => e as MalSymbol)\n              .toList();\n          return new MalClosure(\n              params,\n              args[1],\n              env,\n              (List<MalType> funcArgs) =>\n                  EVAL(args[1], new Env(env, params, funcArgs)));\n        }\n      }\n      var f = EVAL(list.elements.first, env);\n      var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n      if (f is MalCallable) {\n        return f.call(args);\n      } else {\n        throw 'bad!';\n      }\n    }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain() {\n  setupEnv();\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step5_tco.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv() {\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  while (true) {\n\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n      // ast is a list. todo: indent left.\n      if ((ast as MalList).elements.isEmpty) {\n        return ast;\n      } else {\n        var list = ast as MalList;\n        if (list.elements.first is MalSymbol) {\n          var symbol = list.elements.first as MalSymbol;\n          var args = list.elements.sublist(1);\n          if (symbol.value == \"def!\") {\n            MalSymbol key = args.first;\n            MalType value = EVAL(args[1], env);\n            env.set(key.value, value);\n            return value;\n          } else if (symbol.value == \"let*\") {\n            // TODO(het): If elements.length is not even, give helpful error\n            Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n              for (var i = 0; i < elements.length; i += 2) {\n                yield [elements[i], elements[i + 1]];\n              }\n            }\n\n            var newEnv = new Env(env);\n            MalIterable bindings = args.first;\n            for (var pair in pairs(bindings.elements)) {\n              MalSymbol key = pair[0];\n              MalType value = EVAL(pair[1], newEnv);\n              newEnv.set(key.value, value);\n            }\n            ast = args[1];\n            env = newEnv;\n            continue;\n          } else if (symbol.value == \"do\") {\n            for (var elt in args.sublist(0, args.length - 1)) {\n              EVAL(elt, env);\n            }\n            ast = args.last;\n            continue;\n          } else if (symbol.value == \"if\") {\n            var condition = EVAL(args[0], env);\n            if (condition is MalNil ||\n                condition is MalBool && condition.value == false) {\n              // False side of branch\n              if (args.length < 3) {\n                return new MalNil();\n              }\n              ast = args[2];\n              continue;\n            } else {\n              // True side of branch\n              ast = args[1];\n              continue;\n            }\n          } else if (symbol.value == \"fn*\") {\n            var params = (args[0] as MalIterable)\n                .elements\n                .map((e) => e as MalSymbol)\n                .toList();\n            return new MalClosure(\n                params,\n                args[1],\n                env,\n                (List<MalType> funcArgs) =>\n                    EVAL(args[1], new Env(env, params, funcArgs)));\n          }\n        }\n        var f = EVAL(list.elements.first, env);\n        var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n        if (f is MalBuiltin) {\n          return f.call(args);\n        } else if (f is MalClosure) {\n          ast = f.ast;\n          env = new Env(f.env, f.params, args);\n          continue;\n        } else {\n          throw 'bad!';\n        }\n      }\n  }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain() {\n  setupEnv();\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step6_file.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv(List<String> argv) {\n  // TODO(het): use replEnv#set once generalized tearoffs are implemented\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  replEnv.set('eval',\n      new MalBuiltin((List<MalType> args) => EVAL(args.single, replEnv)));\n\n  replEnv.set('*ARGV*',\n      new MalList(argv.map((s) => new MalString(s)).toList()));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n  rep(\"(def! load-file \"\n      \"(fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  while (true) {\n\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n      // ast is a list. todo: indent left.\n      if ((ast as MalList).elements.isEmpty) {\n        return ast;\n      } else {\n        var list = ast as MalList;\n        if (list.elements.first is MalSymbol) {\n          var symbol = list.elements.first as MalSymbol;\n          var args = list.elements.sublist(1);\n          if (symbol.value == \"def!\") {\n            MalSymbol key = args.first;\n            MalType value = EVAL(args[1], env);\n            env.set(key.value, value);\n            return value;\n          } else if (symbol.value == \"let*\") {\n            // TODO(het): If elements.length is not even, give helpful error\n            Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n              for (var i = 0; i < elements.length; i += 2) {\n                yield [elements[i], elements[i + 1]];\n              }\n            }\n\n            var newEnv = new Env(env);\n            MalIterable bindings = args.first;\n            for (var pair in pairs(bindings.elements)) {\n              MalSymbol key = pair[0];\n              MalType value = EVAL(pair[1], newEnv);\n              newEnv.set(key.value, value);\n            }\n            ast = args[1];\n            env = newEnv;\n            continue;\n          } else if (symbol.value == \"do\") {\n            for (var elt in args.sublist(0, args.length - 1)) {\n              EVAL(elt, env);\n            }\n            ast = args.last;\n            continue;\n          } else if (symbol.value == \"if\") {\n            var condition = EVAL(args[0], env);\n            if (condition is MalNil ||\n                condition is MalBool && condition.value == false) {\n              // False side of branch\n              if (args.length < 3) {\n                return new MalNil();\n              }\n              ast = args[2];\n              continue;\n            } else {\n              // True side of branch\n              ast = args[1];\n              continue;\n            }\n          } else if (symbol.value == \"fn*\") {\n            var params = (args[0] as MalIterable)\n                .elements\n                .map((e) => e as MalSymbol)\n                .toList();\n            return new MalClosure(\n                params,\n                args[1],\n                env,\n                (List<MalType> funcArgs) =>\n                    EVAL(args[1], new Env(env, params, funcArgs)));\n          }\n        }\n        var f = EVAL(list.elements.first, env);\n        var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n        if (f is MalBuiltin) {\n          return f.call(args);\n        } else if (f is MalClosure) {\n          ast = f.ast;\n          env = new Env(f.env, f.params, args);\n          continue;\n        } else {\n          throw 'bad!';\n        }\n      }\n  }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain(List<String> args) {\n  setupEnv(args.isEmpty ? const <String>[] : args.sublist(1));\n  if (args.isNotEmpty) {\n    rep(\"(load-file \\\"${args.first}\\\")\");\n    return;\n  }\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step7_quote.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv(List<String> argv) {\n  // TODO(het): use replEnv#set once generalized tearoffs are implemented\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  replEnv.set('eval',\n      new MalBuiltin((List<MalType> args) => EVAL(args.single, replEnv)));\n\n  replEnv.set('*ARGV*',\n      new MalList(argv.map((s) => new MalString(s)).toList()));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n  rep(\"(def! load-file \"\n      \"(fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n}\n\nbool starts_with(MalType ast, String sym) {\n  return ast is MalList && ast.length == 2 && ast.first == new MalSymbol(sym);\n}\n\nMalType qq_loop(List<MalType> xs) {\n  var acc = new MalList([]);\n  for (var i=xs.length-1; 0<=i; i-=1) {\n    if (starts_with(xs[i], \"splice-unquote\")) {\n      acc = new MalList([new MalSymbol(\"concat\"), (xs[i] as MalList)[1], acc]);\n    } else {\n      acc = new MalList([new MalSymbol(\"cons\"), quasiquote(xs[i]), acc]);\n    }\n  }\n  return acc;\n}\n\nMalType quasiquote(MalType ast) {\n  if (starts_with(ast, \"unquote\")) {\n    return (ast as MalList).elements[1];\n  } else if (ast is MalList) {\n    return qq_loop(ast.elements);\n  } else if (ast is MalVector) {\n    return new MalList([new MalSymbol(\"vec\"), qq_loop(ast.elements)]);\n  } else if (ast is MalSymbol || ast is MalHashMap) {\n    return new MalList([new MalSymbol(\"quote\"), ast]);\n  } else {\n    return ast;\n  }\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  while (true) {\n\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n      // ast is a list. todo: indent left.\n      if ((ast as MalList).elements.isEmpty) {\n        return ast;\n      } else {\n        var list = ast as MalList;\n        if (list.elements.first is MalSymbol) {\n          var symbol = list.elements.first as MalSymbol;\n          var args = list.elements.sublist(1);\n          if (symbol.value == \"def!\") {\n            MalSymbol key = args.first;\n            MalType value = EVAL(args[1], env);\n            env.set(key.value, value);\n            return value;\n          } else if (symbol.value == \"let*\") {\n            // TODO(het): If elements.length is not even, give helpful error\n            Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n              for (var i = 0; i < elements.length; i += 2) {\n                yield [elements[i], elements[i + 1]];\n              }\n            }\n\n            var newEnv = new Env(env);\n            MalIterable bindings = args.first;\n            for (var pair in pairs(bindings.elements)) {\n              MalSymbol key = pair[0];\n              MalType value = EVAL(pair[1], newEnv);\n              newEnv.set(key.value, value);\n            }\n            ast = args[1];\n            env = newEnv;\n            continue;\n          } else if (symbol.value == \"do\") {\n            for (var elt in args.sublist(0, args.length - 1)) {\n              EVAL(elt, env);\n            }\n            ast = args.last;\n            continue;\n          } else if (symbol.value == \"if\") {\n            var condition = EVAL(args[0], env);\n            if (condition is MalNil ||\n                condition is MalBool && condition.value == false) {\n              // False side of branch\n              if (args.length < 3) {\n                return new MalNil();\n              }\n              ast = args[2];\n              continue;\n            } else {\n              // True side of branch\n              ast = args[1];\n              continue;\n            }\n          } else if (symbol.value == \"fn*\") {\n            var params = (args[0] as MalIterable)\n                .elements\n                .map((e) => e as MalSymbol)\n                .toList();\n            return new MalClosure(\n                params,\n                args[1],\n                env,\n                (List<MalType> funcArgs) =>\n                    EVAL(args[1], new Env(env, params, funcArgs)));\n          } else if (symbol.value == \"quote\") {\n            return args.single;\n          } else if (symbol.value == \"quasiquote\") {\n            ast = quasiquote(args.first);\n            continue;\n          }\n        }\n        var f = EVAL(list.elements.first, env);\n        var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n        if (f is MalBuiltin) {\n          return f.call(args);\n        } else if (f is MalClosure) {\n          ast = f.ast;\n          env = new Env(f.env, f.params, args);\n          continue;\n        } else {\n          throw 'bad!';\n        }\n      }\n  }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain(List<String> args) {\n  setupEnv(args.isEmpty ? const <String>[] : args.sublist(1));\n  if (args.isNotEmpty) {\n    rep(\"(load-file \\\"${args.first}\\\")\");\n    return;\n  }\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step8_macros.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv(List<String> argv) {\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  replEnv.set('eval',\n      new MalBuiltin((List<MalType> args) => EVAL(args.single, replEnv)));\n\n  replEnv.set('*ARGV*',\n      new MalList(argv.map((s) => new MalString(s)).toList()));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n  rep(\"(def! load-file \"\n      \"  (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n  rep(\"(defmacro! cond \"\n      \"  (fn* (& xs) (if (> (count xs) 0) \"\n      \"    (list 'if (first xs) \"\n      \"      (if (> (count xs) 1) \"\n      \"          (nth xs 1) \"\n      \"          (throw \\\"odd number of forms to cond\\\")) \"\n      \"      (cons 'cond (rest (rest xs)))))))\");\n}\n\nbool starts_with(MalType ast, String sym) {\n  return ast is MalList && ast.length == 2 && ast.first == new MalSymbol(sym);\n}\n\nMalType qq_loop(List<MalType> xs) {\n  var acc = new MalList([]);\n  for (var i=xs.length-1; 0<=i; i-=1) {\n    if (starts_with(xs[i], \"splice-unquote\")) {\n      acc = new MalList([new MalSymbol(\"concat\"), (xs[i] as MalList)[1], acc]);\n    } else {\n      acc = new MalList([new MalSymbol(\"cons\"), quasiquote(xs[i]), acc]);\n    }\n  }\n  return acc;\n}\n\nMalType quasiquote(MalType ast) {\n  if (starts_with(ast, \"unquote\")) {\n    return (ast as MalList).elements[1];\n  } else if (ast is MalList) {\n    return qq_loop(ast.elements);\n  } else if (ast is MalVector) {\n    return new MalList([new MalSymbol(\"vec\"), qq_loop(ast.elements)]);\n  } else if (ast is MalSymbol || ast is MalHashMap) {\n    return new MalList([new MalSymbol(\"quote\"), ast]);\n  } else {\n    return ast;\n  }\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  while (true) {\n\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n     && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n        // ast is a list. todo: indent left.\n        if ((ast as MalList).isEmpty) return ast;\n\n        var list = ast as MalList;\n\n        if (list.elements.first is MalSymbol) {\n          var symbol = list.elements.first as MalSymbol;\n          var args = list.elements.sublist(1);\n          if (symbol.value == \"def!\") {\n            MalSymbol key = args.first;\n            MalType value = EVAL(args[1], env);\n            env.set(key.value, value);\n            return value;\n          } else if (symbol.value == \"defmacro!\") {\n            MalSymbol key = args.first;\n            MalClosure macro = (EVAL(args[1], env) as MalClosure).clone();\n            macro.isMacro = true;\n            env.set(key.value, macro);\n            return macro;\n          } else if (symbol.value == \"let*\") {\n            // TODO(het): If elements.length is not even, give helpful error\n            Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n              for (var i = 0; i < elements.length; i += 2) {\n                yield [elements[i], elements[i + 1]];\n              }\n            }\n\n            var newEnv = new Env(env);\n            MalIterable bindings = args.first;\n            for (var pair in pairs(bindings.elements)) {\n              MalSymbol key = pair[0];\n              MalType value = EVAL(pair[1], newEnv);\n              newEnv.set(key.value, value);\n            }\n            ast = args[1];\n            env = newEnv;\n            continue;\n          } else if (symbol.value == \"do\") {\n            for (var elt in args.sublist(0, args.length - 1)) {\n              EVAL(elt, env);\n            }\n            ast = args.last;\n            continue;\n          } else if (symbol.value == \"if\") {\n            var condition = EVAL(args[0], env);\n            if (condition is MalNil ||\n                condition is MalBool && condition.value == false) {\n              // False side of branch\n              if (args.length < 3) {\n                return new MalNil();\n              }\n              ast = args[2];\n              continue;\n            } else {\n              // True side of branch\n              ast = args[1];\n              continue;\n            }\n          } else if (symbol.value == \"fn*\") {\n            var params = (args[0] as MalIterable)\n                .elements\n                .map((e) => e as MalSymbol)\n                .toList();\n            return new MalClosure(\n                params,\n                args[1],\n                env,\n                (List<MalType> funcArgs) =>\n                    EVAL(args[1], new Env(env, params, funcArgs)));\n          } else if (symbol.value == \"quote\") {\n            return args.single;\n          } else if (symbol.value == \"quasiquote\") {\n            ast = quasiquote(args.first);\n            continue;\n          }\n        }\n        var f = EVAL(list.elements.first, env);\n        if (f is MalCallable && f.isMacro) {\n          ast = f.call(list.elements.sublist(1));\n          continue;\n        }\n        var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n        if (f is MalBuiltin) {\n          return f.call(args);\n        } else if (f is MalClosure) {\n          ast = f.ast;\n          env = new Env(f.env, f.params, args);\n          continue;\n        } else {\n          throw 'bad!';\n        }\n  }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain(List<String> args) {\n  setupEnv(args.isEmpty ? const <String>[] : args.sublist(1));\n  if (args.isNotEmpty) {\n    rep(\"(load-file \\\"${args.first}\\\")\");\n    return;\n  }\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/step9_try.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv(List<String> argv) {\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  replEnv.set('eval',\n      new MalBuiltin((List<MalType> args) => EVAL(args.single, replEnv)));\n\n  replEnv.set('*ARGV*',\n      new MalList(argv.map((s) => new MalString(s)).toList()));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n  rep(\"(def! load-file \"\n      \"  (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n  rep(\"(defmacro! cond \"\n      \"  (fn* (& xs) (if (> (count xs) 0) \"\n      \"    (list 'if (first xs) \"\n      \"      (if (> (count xs) 1) \"\n      \"          (nth xs 1) \"\n      \"          (throw \\\"odd number of forms to cond\\\")) \"\n      \"      (cons 'cond (rest (rest xs)))))))\");\n}\n\nbool starts_with(MalType ast, String sym) {\n  return ast is MalList && ast.length == 2 && ast.first == new MalSymbol(sym);\n}\n\nMalType qq_loop(List<MalType> xs) {\n  var acc = new MalList([]);\n  for (var i=xs.length-1; 0<=i; i-=1) {\n    if (starts_with(xs[i], \"splice-unquote\")) {\n      acc = new MalList([new MalSymbol(\"concat\"), (xs[i] as MalList)[1], acc]);\n    } else {\n      acc = new MalList([new MalSymbol(\"cons\"), quasiquote(xs[i]), acc]);\n    }\n  }\n  return acc;\n}\n\nMalType quasiquote(MalType ast) {\n  if (starts_with(ast, \"unquote\")) {\n    return (ast as MalList).elements[1];\n  } else if (ast is MalList) {\n    return qq_loop(ast.elements);\n  } else if (ast is MalVector) {\n    return new MalList([new MalSymbol(\"vec\"), qq_loop(ast.elements)]);\n  } else if (ast is MalSymbol || ast is MalHashMap) {\n    return new MalList([new MalSymbol(\"quote\"), ast]);\n  } else {\n    return ast;\n  }\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  while (true) {\n\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n        // ast is a list. todo: indent left.\n        if ((ast as MalList).isEmpty) return ast;\n\n        var list = ast as MalList;\n\n        if (list.elements.first is MalSymbol) {\n          var symbol = list.elements.first as MalSymbol;\n          var args = list.elements.sublist(1);\n          if (symbol.value == \"def!\") {\n            MalSymbol key = args.first;\n            MalType value = EVAL(args[1], env);\n            env.set(key.value, value);\n            return value;\n          } else if (symbol.value == \"defmacro!\") {\n            MalSymbol key = args.first;\n            MalClosure macro = (EVAL(args[1], env) as MalClosure).clone();\n            macro.isMacro = true;\n            env.set(key.value, macro);\n            return macro;\n          } else if (symbol.value == \"let*\") {\n            // TODO(het): If elements.length is not even, give helpful error\n            Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n              for (var i = 0; i < elements.length; i += 2) {\n                yield [elements[i], elements[i + 1]];\n              }\n            }\n\n            var newEnv = new Env(env);\n            MalIterable bindings = args.first;\n            for (var pair in pairs(bindings.elements)) {\n              MalSymbol key = pair[0];\n              MalType value = EVAL(pair[1], newEnv);\n              newEnv.set(key.value, value);\n            }\n            ast = args[1];\n            env = newEnv;\n            continue;\n          } else if (symbol.value == \"do\") {\n            for (var elt in args.sublist(0, args.length - 1)) {\n              EVAL(elt, env);\n            }\n            ast = args.last;\n            continue;\n          } else if (symbol.value == \"if\") {\n            var condition = EVAL(args[0], env);\n            if (condition is MalNil ||\n                condition is MalBool && condition.value == false) {\n              // False side of branch\n              if (args.length < 3) {\n                return new MalNil();\n              }\n              ast = args[2];\n              continue;\n            } else {\n              // True side of branch\n              ast = args[1];\n              continue;\n            }\n          } else if (symbol.value == \"fn*\") {\n            var params = (args[0] as MalIterable)\n                .elements\n                .map((e) => e as MalSymbol)\n                .toList();\n            return new MalClosure(\n                params,\n                args[1],\n                env,\n                (List<MalType> funcArgs) =>\n                    EVAL(args[1], new Env(env, params, funcArgs)));\n          } else if (symbol.value == \"quote\") {\n            return args.single;\n          } else if (symbol.value == \"quasiquote\") {\n            ast = quasiquote(args.first);\n            continue;\n          } else if (symbol.value == 'try*') {\n            var body = args.first;\n            if (args.length < 2) {\n                ast = EVAL(body, env);\n                continue;\n            }\n            var catchClause = args[1] as MalList;\n            try {\n              return EVAL(body, env);\n            } catch (e) {\n              assert((catchClause.first as MalSymbol).value == 'catch*');\n              var exceptionSymbol = catchClause[1] as MalSymbol;\n              var catchBody = catchClause[2];\n              MalType exceptionValue;\n              if (e is MalException) {\n                exceptionValue = e.value;\n              } else if (e is reader.ParseException) {\n                exceptionValue = new MalString(e.message);\n              } else {\n                exceptionValue = new MalString(e.toString());\n              }\n              var newEnv = new Env(env, [exceptionSymbol], [exceptionValue]);\n              ast = EVAL(catchBody, newEnv);\n            }\n            continue;\n          }\n        }\n        var f = EVAL(list.elements.first, env);\n        if (f is MalCallable && f.isMacro) {\n          ast = f.call(list.elements.sublist(1));\n          continue;\n        }\n        var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n        if (f is MalBuiltin) {\n          return f.call(args);\n        } else if (f is MalClosure) {\n          ast = f.ast;\n          env = new Env(f.env, f.params, args);\n          continue;\n        } else {\n          throw 'bad!';\n        }\n  }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain(List<String> args) {\n  setupEnv(args.isEmpty ? const <String>[] : args.sublist(1));\n  if (args.isNotEmpty) {\n    rep(\"(load-file \\\"${args.first}\\\")\");\n    return;\n  }\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/stepA_mal.dart",
    "content": "import 'dart:io';\n\nimport 'core.dart';\nimport 'env.dart';\nimport 'printer.dart' as printer;\nimport 'reader.dart' as reader;\nimport 'types.dart';\n\nfinal Env replEnv = new Env();\n\nvoid setupEnv(List<String> argv) {\n  ns.forEach((sym, fun) => replEnv.set(sym, fun));\n\n  replEnv.set('eval',\n      new MalBuiltin((List<MalType> args) => EVAL(args.single, replEnv)));\n\n  replEnv.set('*ARGV*',\n      new MalList(argv.map((s) => new MalString(s)).toList()));\n\n  replEnv.set('*host-language*', new MalString('dart'));\n\n  rep('(def! not (fn* (a) (if a false true)))');\n  rep(\"(def! load-file \"\n      \"  (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n  rep(\"(defmacro! cond \"\n      \"  (fn* (& xs) (if (> (count xs) 0) \"\n      \"    (list 'if (first xs) \"\n      \"      (if (> (count xs) 1) \"\n      \"          (nth xs 1) \"\n      \"          (throw \\\"odd number of forms to cond\\\")) \"\n      \"      (cons 'cond (rest (rest xs)))))))\");\n}\n\nbool starts_with(MalType ast, String sym) {\n  return ast is MalList && ast.length == 2 && ast.first == new MalSymbol(sym);\n}\n\nMalType qq_loop(List<MalType> xs) {\n  var acc = new MalList([]);\n  for (var i=xs.length-1; 0<=i; i-=1) {\n    if (starts_with(xs[i], \"splice-unquote\")) {\n      acc = new MalList([new MalSymbol(\"concat\"), (xs[i] as MalList)[1], acc]);\n    } else {\n      acc = new MalList([new MalSymbol(\"cons\"), quasiquote(xs[i]), acc]);\n    }\n  }\n  return acc;\n}\n\nMalType quasiquote(MalType ast) {\n  if (starts_with(ast, \"unquote\")) {\n    return (ast as MalList).elements[1];\n  } else if (ast is MalList) {\n    return qq_loop(ast.elements);\n  } else if (ast is MalVector) {\n    return new MalList([new MalSymbol(\"vec\"), qq_loop(ast.elements)]);\n  } else if (ast is MalSymbol || ast is MalHashMap) {\n    return new MalList([new MalSymbol(\"quote\"), ast]);\n  } else {\n    return ast;\n  }\n}\n\nMalType READ(String x) => reader.read_str(x);\n\nMalType EVAL(MalType ast, Env env) {\n  while (true) {\n\n  var dbgeval = env.get(\"DEBUG-EVAL\");\n  if (dbgeval != null && !(dbgeval is MalNil)\n      && !(dbgeval is MalBool && dbgeval.value == false)) {\n      stdout.writeln(\"EVAL: ${printer.pr_str(ast)}\");\n  }\n\n  if (ast is MalSymbol) {\n    var result = env.get(ast.value);\n    if (result == null) {\n      throw new NotFoundException(ast.value);\n    }\n    return result;\n  } else if (ast is MalList) {\n    // Exit this switch.\n  } else if (ast is MalVector) {\n    return new MalVector(ast.elements.map((x) => EVAL(x, env)).toList());\n  } else if (ast is MalHashMap) {\n    var newMap = new Map<MalType, MalType>.from(ast.value);\n    for (var key in newMap.keys) {\n      newMap[key] = EVAL(newMap[key], env);\n    }\n    return new MalHashMap(newMap);\n  } else {\n    return ast;\n  }\n        // ast is a list. todo: indent left.\n        if ((ast as MalList).isEmpty) return ast;\n\n        var list = ast as MalList;\n\n        if (list.elements.first is MalSymbol) {\n          var symbol = list.elements.first as MalSymbol;\n          var args = list.elements.sublist(1);\n          if (symbol.value == \"def!\") {\n            MalSymbol key = args.first;\n            MalType value = EVAL(args[1], env);\n            env.set(key.value, value);\n            return value;\n          } else if (symbol.value == \"defmacro!\") {\n            MalSymbol key = args.first;\n            MalClosure macro = (EVAL(args[1], env) as MalClosure).clone();\n            macro.isMacro = true;\n            env.set(key.value, macro);\n            return macro;\n          } else if (symbol.value == \"let*\") {\n            // TODO(het): If elements.length is not even, give helpful error\n            Iterable<List<MalType>> pairs(List<MalType> elements) sync* {\n              for (var i = 0; i < elements.length; i += 2) {\n                yield [elements[i], elements[i + 1]];\n              }\n            }\n\n            var newEnv = new Env(env);\n            MalIterable bindings = args.first;\n            for (var pair in pairs(bindings.elements)) {\n              MalSymbol key = pair[0];\n              MalType value = EVAL(pair[1], newEnv);\n              newEnv.set(key.value, value);\n            }\n            ast = args[1];\n            env = newEnv;\n            continue;\n          } else if (symbol.value == \"do\") {\n            for (var elt in args.sublist(0, args.length - 1)) {\n              EVAL(elt, env);\n            }\n            ast = args.last;\n            continue;\n          } else if (symbol.value == \"if\") {\n            var condition = EVAL(args[0], env);\n            if (condition is MalNil ||\n                condition is MalBool && condition.value == false) {\n              // False side of branch\n              if (args.length < 3) {\n                return new MalNil();\n              }\n              ast = args[2];\n              continue;\n            } else {\n              // True side of branch\n              ast = args[1];\n              continue;\n            }\n          } else if (symbol.value == \"fn*\") {\n            var params = (args[0] as MalIterable)\n                .elements\n                .map((e) => e as MalSymbol)\n                .toList();\n            return new MalClosure(\n                params,\n                args[1],\n                env,\n                (List<MalType> funcArgs) =>\n                    EVAL(args[1], new Env(env, params, funcArgs)));\n          } else if (symbol.value == \"quote\") {\n            return args.single;\n          } else if (symbol.value == \"quasiquote\") {\n            ast = quasiquote(args.first);\n            continue;\n          } else if (symbol.value == 'try*') {\n            var body = args.first;\n            if (args.length < 2) {\n                ast = EVAL(body, env);\n                continue;\n            }\n            var catchClause = args[1] as MalList;\n            try {\n              return EVAL(body, env);\n            } catch (e) {\n              assert((catchClause.first as MalSymbol).value == 'catch*');\n              var exceptionSymbol = catchClause[1] as MalSymbol;\n              var catchBody = catchClause[2];\n              MalType exceptionValue;\n              if (e is MalException) {\n                exceptionValue = e.value;\n              } else if (e is reader.ParseException) {\n                exceptionValue = new MalString(e.message);\n              } else {\n                exceptionValue = new MalString(e.toString());\n              }\n              var newEnv = new Env(env, [exceptionSymbol], [exceptionValue]);\n              ast = EVAL(catchBody, newEnv);\n            }\n            continue;\n          }\n        }\n        var f = EVAL(list.elements.first, env);\n        if (f is MalCallable && f.isMacro) {\n          ast = f.call(list.elements.sublist(1));\n          continue;\n        }\n        var args = list.elements.sublist(1).map((x) => EVAL(x, env)).toList();\n        if (f is MalBuiltin) {\n          return f.call(args);\n        } else if (f is MalClosure) {\n          ast = f.ast;\n          env = new Env(f.env, f.params, args);\n          continue;\n        } else {\n          throw 'bad!';\n        }\n  }\n}\n\nString PRINT(MalType x) => printer.pr_str(x);\n\nString rep(String x) {\n  return PRINT(EVAL(READ(x), replEnv));\n}\n\nconst prompt = 'user> ';\nmain(List<String> args) {\n  setupEnv(args.isEmpty ? const <String>[] : args.sublist(1));\n  if (args.isNotEmpty) {\n    rep(\"(load-file \\\"${args.first}\\\")\");\n    return;\n  }\n  rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n  while (true) {\n    stdout.write(prompt);\n    var input = stdin.readLineSync();\n    if (input == null) return;\n    var output;\n    try {\n      output = rep(input);\n    } on reader.ParseException catch (e) {\n      stdout.writeln(\"Error: '${e.message}'\");\n      continue;\n    } on NotFoundException catch (e) {\n      stdout.writeln(\"Error: '${e.value}' not found\");\n      continue;\n    } on MalException catch (e) {\n      stdout.writeln(\"Error: ${printer.pr_str(e.value)}\");\n      continue;\n    } on reader.NoInputException {\n      continue;\n    }\n    stdout.writeln(output);\n  }\n}\n"
  },
  {
    "path": "impls/dart/types.dart",
    "content": "import 'dart:collection';\nimport 'env.dart';\n\nabstract class MalType {\n  bool get isMacro => false;\n  MalType meta;\n\n  MalType clone();\n}\n\nabstract class MalIterable extends MalType\n    with ListMixin<MalType>\n    implements List<MalType> {\n  final List<MalType> elements;\n\n  MalIterable(this.elements);\n\n  MalType operator [](int index) => elements[index];\n  void operator []=(int index, MalType value) {\n    elements[index] = value;\n  }\n\n  int get length => elements.length;\n  void set length(int newLength) {\n    elements.length = newLength;\n  }\n\n  bool operator ==(other) {\n    if (other is! MalIterable) return false;\n\n    // apparently (= (list) nil) should be false...\n    if (other is MalNil) return false;\n\n    if (elements.length != other.elements.length) return false;\n    for (var i = 0; i < elements.length; i++) {\n      if (elements[i] != other.elements[i]) return false;\n    }\n    return true;\n  }\n\n  @override\n  MalIterable clone();\n}\n\nclass MalList extends MalIterable {\n  MalList(List<MalType> elements) : super(elements);\n\n  @override\n  MalList clone() {\n    return new MalList(elements.toList());\n  }\n}\n\nclass MalVector extends MalIterable {\n  MalVector(List<MalType> elements) : super(elements);\n\n  @override\n  MalVector clone() {\n    return new MalVector(elements.toList());\n  }\n}\n\nclass MalHashMap extends MalType {\n  final Map<MalType, MalType> value;\n\n  MalHashMap(this.value);\n\n  MalHashMap.fromSequence(List<MalType> elements)\n      : value = _mapFromSequence(elements);\n\n  static Map<MalType, MalType> _mapFromSequence(List<MalType> elements) {\n    var result = <MalType, MalType>{};\n\n    var readingKey = true;\n    MalType pendingKey;\n    for (var malType in elements) {\n      if (readingKey) {\n        if (malType is MalString || malType is MalKeyword) {\n          pendingKey = malType;\n        } else {\n          throw new ArgumentError('hash-map keys must be strings or keywords');\n        }\n      } else {\n        result[pendingKey] = malType;\n      }\n      readingKey = !readingKey;\n    }\n\n    return result;\n  }\n\n  bool operator ==(other) {\n    if (other is! MalHashMap) return false;\n    var otherMap = (other as MalHashMap).value;\n    if (otherMap.length != value.length) return false;\n    for (var key in value.keys) {\n      if (!otherMap.containsKey(key)) return false;\n      if (value[key] != otherMap[key]) return false;\n    }\n    return true;\n  }\n\n  @override\n  MalHashMap clone() {\n    return new MalHashMap(new Map.from(value));\n  }\n}\n\nclass MalInt extends MalType {\n  final int value;\n\n  MalInt(this.value);\n\n  bool operator ==(other) {\n    if (other is! MalInt) return false;\n    return other.value == value;\n  }\n\n  @override\n  MalInt clone() {\n    return new MalInt(value);\n  }\n}\n\nclass MalSymbol extends MalType {\n  final String value;\n\n  MalSymbol(this.value);\n\n  int get hashCode => value.hashCode;\n\n  bool operator ==(other) {\n    if (other is! MalSymbol) return false;\n    return value == other.value;\n  }\n\n  @override\n  MalSymbol clone() {\n    return new MalSymbol(value);\n  }\n}\n\nclass MalKeyword extends MalType {\n  final String value;\n\n  MalKeyword(this.value);\n\n  int get hashCode => value.hashCode;\n\n  bool operator ==(other) {\n    if (other is! MalKeyword) return false;\n    return value == other.value;\n  }\n\n  @override\n  MalKeyword clone() {\n    return new MalKeyword(value);\n  }\n}\n\nclass MalString extends MalType {\n  final String value;\n\n  MalString(this.value);\n\n  int get hashCode => value.hashCode;\n\n  bool operator ==(other) {\n    if (other is! MalString) return false;\n    return other.value == value;\n  }\n\n  @override\n  MalString clone() {\n    return new MalString(value);\n  }\n}\n\nclass MalBool extends MalType {\n  final bool value;\n\n  MalBool(this.value);\n\n  bool operator ==(other) {\n    if (other is! MalBool) return false;\n    return other.value == value;\n  }\n\n  @override\n  MalBool clone() {\n    return new MalBool(value);\n  }\n}\n\nclass MalNil extends MalIterable {\n  MalNil() : super(const <MalType>[]);\n\n  bool operator ==(other) => other is MalNil;\n\n  @override\n  MalNil clone() {\n    return new MalNil();\n  }\n}\n\nclass MalAtom extends MalType {\n  MalType value;\n\n  MalAtom(this.value);\n\n  @override\n  MalAtom clone() {\n    return new MalAtom(value);\n  }\n}\n\nabstract class MalCallable extends MalType {\n  MalType call(List<MalType> args);\n\n  bool get isMacro => false;\n}\n\ntypedef MalType BuiltinFunc(List<MalType> args);\n\nclass MalBuiltin extends MalCallable {\n  final BuiltinFunc func;\n\n  MalBuiltin(this.func);\n\n  MalType call(List<MalType> args) {\n    return func(args);\n  }\n\n  @override\n  MalBuiltin clone() {\n    return new MalBuiltin(func);\n  }\n}\n\ntypedef MalType EvalFun(MalType ast, Env env);\n\nclass MalClosure extends MalCallable {\n  final List<MalSymbol> params;\n  final MalType ast;\n  final Env env;\n  final Function func;\n\n  @override\n  bool isMacro = false;\n\n  MalClosure(this.params, this.ast, this.env, this.func);\n\n  MalType call(List<MalType> args) {\n    return func(args);\n  }\n\n  @override\n  MalClosure clone() {\n    var closure =\n        new MalClosure(this.params.toList(), this.ast, this.env, this.func);\n    closure.isMacro = this.isMacro;\n    return closure;\n  }\n}\n\nclass MalException implements Exception {\n  final MalType value;\n\n  MalException(this.value);\n}\n"
  },
  {
    "path": "impls/elisp/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install emacs-nox\n"
  },
  {
    "path": "impls/elisp/Makefile",
    "content": "all:\n\temacs -Q --batch -L . --eval '(byte-recompile-directory \".\" 0)'\n\n# For debugging, it is sometimes useful to attempt a run without byte compation.\nnocompile: clean\n\texec emacs -Q --batch -L . --eval \"(setq text-quoting-style 'straight)\" --load stepA_mal.el\n\nclean:\n\trm -f *.elc *~ mal/*.elc mal/*~\n"
  },
  {
    "path": "impls/elisp/mal/core.el",
    "content": "(require 'seq)\n(require 'mal/types)\n\n(defun mal-boolean (value) (if value mal-true mal-false))\n\n(defun mal-= (a b)\n  (let (va vb)\n    (cond\n     ((or (setq va (mal-seq-value a)) (mal-list-p a))\n      (and (or (setq vb (mal-seq-value b)) (mal-list-p b))\n           (mal-seq-= va vb)))\n     ((setq va (mal-number-value a))              (equal va (mal-number-value b)))\n     ((setq va (mal-string-value a))              (equal va (mal-string-value b)))\n     ((setq va (mal-symbol-value a))              (eq va (mal-symbol-value b)))\n     ((setq va (mal-keyword-value a))             (equal va (mal-keyword-value b)))\n     ((setq va (mal-map-value a))                 (and (setq vb (mal-map-value b))\n                                                       (mal-map-= va vb)))\n     (t                                           (eq a b)))))\n\n(defun mal-seq-= (a b)\n  (let* ((len (seq-length a))\n         (res (= len (seq-length b))))\n    (while (and res (< 0 len))\n      (setq len (1- len))\n      (unless (mal-= (seq-elt a len) (seq-elt b len))\n        (setq res nil)))\n    res))\n\n(defun mal-map-= (a b)\n  (when (= (hash-table-count a)\n           (hash-table-count b))\n    (catch 'return\n      (maphash (lambda (key a-value)\n                 (let ((b-value (gethash key b)))\n                   (unless (and b-value\n                                (mal-= a-value b-value))\n                     (throw 'return nil))))\n               a)\n      ;; if we made it this far, the maps are equal\n      t)))\n\n(define-hash-table-test 'mal-= 'mal-= 'sxhash)\n\n(defun mal-conj (seq &rest args)\n  (let (value)\n    (cond\n     ((setq value (mal-vector-value seq))\n      (mal-vector (vconcat value args)))\n     ((setq value (mal-list-value seq))\n      (mal-list (append (reverse args) value)))\n     ((mal-list-p seq)\n      (mal-list (reverse args)))\n     (t (error \"seq: bad type\")))))\n\n(defun elisp-to-mal (arg)\n  (cond\n   ((not arg)\n    mal-nil)\n   ((eq arg t)\n    mal-true)\n   ((numberp arg)\n    (mal-number arg))\n   ((stringp arg)\n    (mal-string arg))\n   ((keywordp arg)\n    (mal-keyword (symbol-name arg)))\n   ((symbolp arg)\n    (mal-symbol arg))\n   ((consp arg)\n    (mal-list (mapcar 'elisp-to-mal arg)))\n   ((vectorp arg)\n    (mal-vector (vconcat (mapcar 'elisp-to-mal arg))))\n   ((hash-table-p arg)\n    (let ((output (make-hash-table :test 'mal-=)))\n      (maphash\n       (lambda (key value)\n         (puthash (elisp-to-mal key) (elisp-to-mal value) output))\n       arg)\n      (mal-map output)))\n   (t\n    ;; represent anything else as printed arg\n    (mal-string (format \"%S\" arg)))))\n\n(defconst core-ns\n  '((+ . (lambda (a b) (mal-number (+ (mal-number-value a) (mal-number-value b)))))\n    (- . (lambda (a b) (mal-number (- (mal-number-value a) (mal-number-value b)))))\n    (* . (lambda (a b) (mal-number (* (mal-number-value a) (mal-number-value b)))))\n    (/ . (lambda (a b) (mal-number (/ (mal-number-value a) (mal-number-value b)))))\n\n    (< . (lambda (a b) (mal-boolean (< (mal-number-value a) (mal-number-value b)))))\n    (<= . (lambda (a b) (mal-boolean (<= (mal-number-value a) (mal-number-value b)))))\n    (> . (lambda (a b) (mal-boolean (> (mal-number-value a) (mal-number-value b)))))\n    (>= . (lambda (a b) (mal-boolean (>= (mal-number-value a) (mal-number-value b)))))\n\n    (= . (lambda (a b) (mal-boolean (mal-= a b))))\n\n    (list . (lambda (&rest args) (mal-list args)))\n    (list? . (lambda (mal-object) (mal-boolean (mal-list-p mal-object))))\n    (empty? . (lambda (seq) (mal-boolean (seq-empty-p (mal-seq-value seq)))))\n    (count . (lambda (seq) (mal-number (length (mal-seq-value seq)))))\n\n    (pr-str . (lambda (&rest args) (mal-string (pr-join args t \" \"))))\n    (str . (lambda (&rest args) (mal-string (pr-join args nil \"\"))))\n    (prn . (lambda (&rest args)\n             (println (pr-join args t \" \"))\n             mal-nil))\n    (println . (lambda (&rest args)\n                 (println (pr-join args nil \" \"))\n                 mal-nil))\n\n    (read-string . (lambda (input) (read-str (mal-string-value input))))\n    (slurp . (lambda (file)\n                        (with-temp-buffer\n                          (insert-file-contents-literally (mal-string-value file))\n                          (mal-string (buffer-string)))))\n\n    (atom . mal-atom)\n    (atom? . (lambda (mal-object) (mal-boolean (mal-atom-value mal-object))))\n    (deref . mal-atom-value)\n    (reset! . (lambda (atom value)\n                (mal-reset atom value)\n                value))\n    (swap! . (lambda (atom fn &rest args)\n               (let ((value (apply (or (mal-func-value fn)\n                                       (mal-fn-core-value fn))\n                                   (mal-atom-value atom)\n                                   args)))\n                 (mal-reset atom value)\n                 value)))\n\n    (vec . (lambda (seq)\n             (if (mal-vector-value seq)\n                 seq\n               (mal-vector (seq-into (mal-list-value seq) 'vector)))))\n    (cons . (lambda (arg seq)\n              (let ((value (mal-vector-value seq)))\n                (mal-list (cons arg (if value\n                                        (seq-into value 'list)\n                                      (mal-list-value seq)))))))\n    (concat . (lambda (&rest lists)\n                (mal-list (seq-mapcat 'mal-seq-value lists 'list))))\n\n    (nth . (lambda (seq index)\n             (let ((list (mal-seq-value seq))\n                   (i (mal-number-value index)))\n               ;; seq-elt returns nil for a list and a bad index\n               (or (seq-elt (mal-seq-value seq) (mal-number-value index))\n                   (error \"Args out of range: %s, %d\" (pr-str seq t) i)))))\n\n    (first . (lambda (seq)\n               (let ((value (mal-seq-value seq)))\n                 (if (seq-empty-p value)\n                            mal-nil\n                   (seq-first value)))))\n    (rest . (lambda (seq)\n              (let ((value(mal-vector-value seq)))\n                (mal-list (cdr (if value\n                                   (seq-into value 'list)\n                                 (mal-list-value seq)))))))\n\n    (throw . (lambda (mal-object) (signal 'mal-custom (list mal-object))))\n\n    (apply . (lambda (fn &rest args)\n               (let ((butlast (butlast args))\n                     (last (mal-seq-value (car (last args))))\n                     (fn* (or (mal-func-value fn)\n                              (mal-fn-core-value fn)\n                              (mal-macro-value fn))))\n                 (apply fn* (seq-concatenate 'list butlast last)))))\n    (map . (lambda (fn seq)\n             (mal-list (mapcar (or (mal-func-value fn) (mal-fn-core-value fn))\n                               (mal-seq-value seq)))))\n\n    (nil? . (lambda (arg) (mal-boolean (eq mal-nil arg))))\n    (true? . (lambda (arg) (mal-boolean (eq mal-true arg))))\n    (false? . (lambda (arg) (mal-boolean (eq mal-false arg))))\n\n    (number? . (lambda (arg) (mal-boolean (mal-number-value arg))))\n    (symbol? . (lambda (arg) (mal-boolean (mal-symbol-value arg))))\n    (keyword? . (lambda (arg) (mal-boolean (mal-keyword-value arg))))\n    (string? . (lambda (arg) (mal-boolean (mal-string-value arg))))\n    (vector? . (lambda (arg) (mal-boolean (mal-vector-value arg))))\n    (map? . (lambda (arg) (mal-boolean (mal-map-value arg))))\n\n    (symbol . (lambda (string) (mal-symbol (intern (mal-string-value string)))))\n    (keyword . (lambda (x)\n                 (let ((value (mal-string-value x)))\n                   (if value\n                       (mal-keyword (concat \":\" value))\n                     x))))\n\n    (vector . (lambda (&rest args) (mal-vector (seq-into args 'vector))))\n    (hash-map . (lambda (&rest args)\n                           (let ((map (make-hash-table :test 'mal-=)))\n                             (while args\n                               (puthash (pop args) (pop args) map))\n                             (mal-map map))))\n\n    (sequential? . (lambda (mal-object)\n                     (mal-boolean (or (mal-list-p mal-object)\n                                      (mal-vector-value mal-object)))))\n    (fn? . (lambda (arg) (mal-boolean (or (mal-fn-core-value arg)\n                                          (mal-func-value arg)))))\n    (macro? . (lambda (arg) (mal-boolean (mal-macro-value arg))))\n\n    (get . (lambda (map key)\n             (or (let ((value (mal-map-value map)))\n                   (when value\n                     (gethash key value)))\n                 mal-nil)))\n    (contains? . (lambda (map key)\n                   (mal-boolean (gethash key (mal-map-value map)))))\n    (assoc . (lambda (map &rest args)\n                        (let ((map* (copy-hash-table (mal-map-value map))))\n                          (while args\n                            (puthash (pop args) (pop args) map*))\n                          (mal-map map*))))\n    (dissoc . (lambda (map &rest args)\n                         (let ((map* (copy-hash-table (mal-map-value map))))\n                           (dolist (k args)\n                             (remhash k map*))\n                           (mal-map map*))))\n    (keys . (lambda (map) (let (keys)\n                                     (maphash (lambda (key _value) (push key keys))\n                                              (mal-map-value map))\n                                     (mal-list keys))))\n    (vals . (lambda (map) (let (vals)\n                                     (maphash (lambda (_key value) (push value vals))\n                                              (mal-map-value map))\n                                     (mal-list vals))))\n\n    (readline . (lambda (prompt)\n                  (or (mal-string (readln (mal-string-value prompt)))\n                      mal-nil)))\n\n    (meta . mal-meta)\n    (with-meta . with-meta)\n\n    (time-ms . (lambda () (mal-number (floor (* (float-time) 1000)))))\n\n    (conj . mal-conj)\n    (seq . (lambda (mal-object)\n             (let (value)\n               (or\n                        (cond\n                         ((setq value (mal-list-value mal-object))\n                          mal-object)\n                         ((and (setq value (mal-vector-value mal-object))\n                               (not (seq-empty-p value)))\n                          (mal-list (seq-into value 'list)))\n                         ((and (setq value (mal-string-value mal-object))\n                               (not (seq-empty-p value)))\n                          (mal-list (mapcar (lambda (item) (mal-string (char-to-string item)))\n                                            value))))\n                        mal-nil))))\n\n    (elisp-eval . (lambda (string)\n                    (elisp-to-mal (eval (read (mal-string-value string))))))\n    ))\n\n(provide 'mal/core)\n"
  },
  {
    "path": "impls/elisp/mal/env.el",
    "content": "(require 'mal/types)\n\n;; An env is represented by an elisp list of hash-tables. In other words\n;;  * car: a hash-table\n;;  * cdr: the outer environment or ()\n;; Keys are elisp symbols.\n\n(defun mal-env (&optional outer binds exprs)\n  (let ((env (cons (make-hash-table :test 'eq) outer))\n        key)\n    (while (setq key (pop binds))\n      (if (eq key '&)\n          (mal-env-set env (pop binds) (mal-list exprs))\n        (mal-env-set env key (pop exprs))))\n    env))\n\n(defun mal-env-set (env key value)\n  (let ((data (car env)))\n    (puthash key value data)))\n\n(defun mal-env-get (env key)\n  (let (value)\n    (while (and (not (setq value (gethash key (pop env))))\n                env))\n    value))\n\n(provide 'mal/env)\n"
  },
  {
    "path": "impls/elisp/mal/printer.el",
    "content": "(require 'mal/types)\n\n(defun pr-str (form print-readably)\n  (let (value)\n    (cond\n     ((eq mal-nil form)\n      \"nil\")\n     ((eq mal-true form)\n      \"true\")\n     ((eq mal-false form)\n      \"false\")\n     ((setq value (mal-number-value form))\n      (number-to-string value))\n     ((setq value (mal-string-value form))\n      (if print-readably\n          (let ((print-escape-newlines t))\n            (prin1-to-string value))\n        value))\n     ((setq value (mal-symbol-value form))\n      (symbol-name value))\n     ((setq value (mal-keyword-value form))\n      value)\n     ((setq value (mal-list-value form))\n      (pr-list value print-readably))\n     ((mal-list-p form)\n      \"()\")\n     ((setq value (mal-vector-value form))\n      (pr-vector value print-readably))\n     ((setq value (mal-map-value form))\n      (pr-map value print-readably))\n     ((or (mal-fn-core-value form) (mal-func-value form))\n      \"#<function>\")\n     ((mal-macro-value form)\n      \"#<macro>\")\n     ((setq value (mal-atom-value form))\n      (format \"(atom %s)\" (pr-str value print-readably)))\n     (t (error \"pr-str: unknown type: %s\" form)))))\n\n(defun pr-list (form print-readably)\n  (let ((items (pr-join form print-readably \" \")))\n    (concat \"(\" items \")\")))\n\n(defun pr-vector (form print-readably)\n  (let ((items (pr-join form print-readably \" \")))\n    (concat \"[\" items \"]\")))\n\n(defun pr-map (form print-readably)\n  (let (pairs)\n    (maphash\n     (lambda (key value)\n       (push value pairs)\n       (push key pairs))\n     form)\n    (let ((items (pr-join pairs print-readably \" \")))\n      (concat \"{\" items \"}\"))))\n\n(defun pr-join (forms print-readably separator)\n  (mapconcat (lambda (item) (pr-str item print-readably)) forms separator))\n\n(provide 'mal/printer)\n"
  },
  {
    "path": "impls/elisp/mal/reader.el",
    "content": "(require 'mal/types)\n\n;; HACK: `text-quoting-style' prettifies quotes in error messages on\n;; Emacs 25, but no longer does from 26 upwards...\n(when (= emacs-major-version 25)\n  (setq text-quoting-style 'grave))\n\n(defvar reader--tokens nil)\n\n(defun peek ()\n  (car reader--tokens))\n\n(defun next ()\n  (pop reader--tokens))\n\n(defun read-str (input)\n  (setq reader--tokens (tokenizer input))\n  (read-form))\n\n(defun tokenizer (input)\n  (let (output)\n    (with-temp-buffer\n      (insert input)\n      (goto-char (point-min))\n      (while (not (eobp))\n        (when (looking-at token-re)\n          (let ((token (match-string 1)))\n            (if (= (length token) 0)\n                (let ((remainder (buffer-substring (point) (point-max))))\n                  (push remainder output)\n                  (goto-char (point-max)))\n              (when (not (string-match-p comment-re token))\n                (push token output))\n              (goto-char (match-end 1))))))\n      (nreverse output))))\n\n(defun read-form ()\n  (pcase (peek)\n    (\"'\"\n      (read-quote))\n    (\"`\"\n      (read-quasiquote))\n    (\"~\"\n      (read-unquote))\n    (\"~@\"\n      (read-splice-unquote))\n    (\"@\"\n      (read-deref))\n    (\"^\"\n      (read-with-meta))\n    (\"(\"\n      (read-list))\n    (\"[\"\n      (read-vector))\n    (\"{\"\n      (read-map))\n    (_\n      ;; assume anything else is an atom\n      (read-atom))))\n\n(defun read-simple-reader-macro (symbol)\n  (next) ; pop reader macro token\n  ;; turn form into (symbol form)\n  (mal-list (list (mal-symbol symbol) (read-form))))\n\n(defun read-quote ()\n  (read-simple-reader-macro 'quote))\n\n(defun read-quasiquote ()\n  (read-simple-reader-macro 'quasiquote))\n\n(defun read-unquote ()\n  (read-simple-reader-macro 'unquote))\n\n(defun read-splice-unquote ()\n  (read-simple-reader-macro 'splice-unquote))\n\n(defun read-deref ()\n  (read-simple-reader-macro 'deref))\n\n(defun read-with-meta ()\n  (next) ; pop with-meta token\n  (let ((meta (read-form)))\n    (mal-list (list (mal-symbol 'with-meta) (read-form) meta))))\n\n(defun read-list ()\n  (next) ; pop list start\n  (let (output end-of-list)\n    (while (not end-of-list)\n      (let ((token (peek)))\n        (cond\n         ((string= token \")\")\n          (next) ; pop list end\n          (setq end-of-list t))\n         ((not token)\n          (signal 'unterminated-sequence '(list)))\n         (t\n          (push (read-form) output)))))\n    (mal-list (nreverse output))))\n\n(defun read-vector ()\n  (next) ; pop vector start\n  (let (output end-of-vector)\n    (while (not end-of-vector)\n      (let ((token (peek)))\n        (cond\n         ((string= token \"]\")\n          (next) ; pop vector end\n          (setq end-of-vector t))\n         ((not token)\n          (signal 'unterminated-sequence '(vector)))\n         (t\n          (push (read-form) output)))))\n    (mal-vector (vconcat (nreverse output)))))\n\n;; HACK overriden by core.el in later steps\n(define-hash-table-test 'mal-= 'equal 'sxhash)\n\n(defun read-map ()\n  (next) ; pop map start\n  (let ((output (make-hash-table :test 'mal-=))\n        end-of-map)\n    (while (not end-of-map)\n      (let ((token (peek)))\n        (cond\n         ((string= token \"}\")\n          (next) ; pop map end\n          (setq end-of-map t))\n         ((not token)\n          (signal 'unterminated-sequence '(map)))\n         (t\n          (puthash (read-form) (read-form) output)))))\n    (mal-map output)))\n\n(defun read-atom ()\n  (let ((token (next)))\n    (if token\n        (cond\n         ((string= token \"nil\")\n          mal-nil)\n         ((string= token \"true\")\n          mal-true)\n         ((string= token \"false\")\n          mal-false)\n         ((string-match number-re token)\n          (mal-number (string-to-number token)))\n         ((= (aref token 0) ?\\\")\n          (if (string-match string-re token)\n              (mal-string (read token))\n            (signal 'unterminated-sequence '(string))))\n         ((= (aref token 0) ?:)\n          (mal-keyword token))\n         (t\n          ;; assume anything else is a symbol\n          (mal-symbol (intern token))))\n      (signal 'end-of-token-stream nil))))\n\n(provide 'mal/reader)\n"
  },
  {
    "path": "impls/elisp/mal/types.el",
    "content": ";; Structural pattern matching is ideal, but too slow for MAL.\n\n;; So we use a mal-foo-value getter that returns nil in case of bad\n;; type (or if a list is empty, unfortunately).\n\n(defmacro mal-object (name)\n  (let ((constructor (intern (format \"mal-%s\" name)))\n        (accessor (intern (format \"mal-%s-value\" name))))\n    `(progn\n       (defsubst ,constructor (value)\n         (record ',name value))\n       (defun ,accessor (arg)\n         (and (recordp arg)\n              (eq (aref arg 0) ',name)\n              (aref arg 1))))))\n\n(defconst mal-nil   #&8\"n\")\n(defconst mal-false #&8\"f\")\n(defconst mal-true  #&8\"t\")\n\n(defsubst mal-number (elisp-number) elisp-number)\n(defsubst mal-number-value (obj) (and (numberp obj) obj))\n\n(defsubst mal-symbol (elisp-symbol) elisp-symbol)\n;; A nil result means either 'not a symbol' or 'the nil symbol'.\n(defsubst mal-symbol-value (obj) (and (symbolp obj)obj))\n\n(defsubst mal-string (elisp-string) elisp-string)\n(defsubst mal-string-value (obj) (and (stringp obj) obj))\n\n;; In elisp, keywords are symbols.  Using them would cause confusion,\n;; or at least make mal-symbol-value more complex, for little benefit.\n;; The wrapped value is an elisp string including the initial colon.\n(mal-object keyword)\n\n;; Use the native type when possible, but #s(type value meta ...) for\n;; the empty list or when metadata is present.\n\n(defsubst mal-vector (elisp-vector) elisp-vector)\n(defun mal-vector-value (obj)\n  (if (vectorp obj)\n      obj\n    (and (recordp obj) (eq (aref obj 0) 'vector) (aref obj 1))))\n\n(defsubst mal-map (elisp-hash-table) elisp-hash-table)\n(defun mal-map-value (obj)\n  (if (hash-table-p obj)\n      obj\n    (and (recordp obj) (eq (aref obj 0) 'map) (aref obj 1))))\n\n(defconst mal-empty-list #s(list nil))\n(defsubst mal-list (elisp-list) (or elisp-list mal-empty-list))\n;; A nil result means either 'not a list' or 'empty list'.\n(defun mal-list-value (obj)\n  (if (listp obj) obj\n    (and (recordp obj) (eq (aref obj 0) 'list) (aref obj 1))))\n(defun mal-list-p (obj)\n  (or (listp obj)\n      (and (recordp obj) (eq (aref obj 0) 'list))))\n\n;; A nil result means either 'not a list' or 'empty list'.\n(defun mal-seq-value (arg) (or (mal-vector-value arg) (mal-list-value arg)))\n\n(mal-object atom)\n(defun mal-reset (atom value) (setf (aref atom 1) value))\n\n(mal-object fn-core)\n(mal-object macro)\n\n;; Function created by fn*.\n(defsubst mal-func (value body params env)\n  (record 'func value body params env))\n(defun mal-func-value ( obj)\n  (and (recordp obj) (eq (aref obj 0) 'func) (aref obj 1)))\n(defsubst mal-func-body   (obj) (aref obj 2))\n(defsubst mal-func-params (obj) (aref obj 3))\n(defsubst mal-func-env    (obj) (aref obj 4))\n\n(defun with-meta (obj meta)\n  (cond\n   ((vectorp      obj)  (record 'vector obj meta))\n   ((hash-table-p obj)  (record 'map    obj meta))\n   ((listp        obj)  (record 'list   obj meta))\n   ((< (length obj) 4)  (record (aref obj 0) (aref obj 1) meta))\n   (t                   (record (aref obj 0) (aref obj 1)\n                                (aref obj 2) (aref obj 3)\n                                (aref obj 4) meta))))\n\n(defun mal-meta (obj)\n  (if (and (recordp obj) (member (length obj) '(3 6)))\n      (aref obj (1- (length obj)))\n    mal-nil))\n\n;;; regex\n\n(defvar token-re\n  (rx (* (any white ?,))                     ;; leading whitespace\n      (group\n       (or\n        \"~@\"                                 ;; special 2-char token\n        (any \"[]{}()'`~^@\")                  ;; special 1-char tokens\n        (and ?\\\" (* (or (and ?\\\\ anything)\n                        (not (any \"\\\\\\\"\"))))\n             ?\\\")                            ;; string with escapes\n        (and ?\\; (* not-newline))            ;; comment\n        (* (not (any white \"[]{}()'\\\"`,;\"))) ;; catch-all\n        ))))\n\n(defvar whitespace-re\n  (rx bos (* (any white ?,)) eos))\n\n(defvar comment-re\n  (rx bos ?\\; (* anything)))\n\n(defvar sequence-end-re\n  (rx bos (any \")]}\") eos))\n\n(defvar number-re\n  (rx bos (? (any \"+-\")) (+ (char digit)) eos))\n\n(defvar string-re\n  (rx bos ?\\\" (* (or (and ?\\\\ anything)\n                     (not (any \"\\\\\\\"\"))))\n      ?\\\" eos))\n\n;;; errors\n\n(when (not (fboundp 'define-error))\n  (defun define-error (name message &optional parent)\n  \"Define NAME as a new error signal.\nMESSAGE is a string that will be output to the echo area if such an error\nis signaled without being caught by a `condition-case'.\nPARENT is either a signal or a list of signals from which it inherits.\nDefaults to `error'.\"\n  (unless parent (setq parent 'error))\n  (let ((conditions\n         (if (consp parent)\n             (apply #'nconc\n                    (mapcar (lambda (parent)\n                              (cons parent\n                                    (or (get parent 'error-conditions)\n                                        (error \"Unknown signal `%s'\" parent))))\n                            parent))\n           (cons parent (get parent 'error-conditions)))))\n    (put name 'error-conditions\n         (delete-dups (copy-sequence (cons name conditions))))\n    (when message (put name 'error-message message)))))\n\n(define-error 'mal \"MAL error\")\n(define-error 'unterminated-sequence \"Unexpected end of input during token sequence\" 'mal)\n(define-error 'end-of-token-stream \"End of token stream\" 'mal)\n(define-error 'mal-custom \"Custom error\" 'mal)\n\n(provide 'mal/types)\n"
  },
  {
    "path": "impls/elisp/run",
    "content": "#!/bin/sh\ndir=$(dirname $0)\nexec emacs -Q --batch -L $dir --eval \"(setq text-quoting-style 'straight)\" --load $dir/${STEP:-stepA_mal}.elc \"${@}\"\n"
  },
  {
    "path": "impls/elisp/step0_repl.el",
    "content": "(defun READ (input)\n  input)\n\n(defun EVAL (input)\n  input)\n\n(defun PRINT (input)\n  input)\n\n(defun rep (input)\n  (PRINT (EVAL (READ input))))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defun main ()\n  (let (input)\n    (while (setq input (readln \"user> \"))\n      (println (rep input)))\n    ;; print final newline\n    (terpri)))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step1_read_print.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/reader)\n(require 'mal/printer)\n\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (input)\n  input)\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input)\n  (PRINT (EVAL (READ input))))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (let (input)\n    (while (setq input (readln \"user> \"))\n      (with-error-handling\n       (println (rep input))))\n    ;; print final newline\n    (terpri)))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step2_eval.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/reader)\n(require 'mal/printer)\n\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (a)\n\n     ;; (println \"EVAL: %s\\n\" (PRINT ast))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n      (let ((fn* (mal-fn-core-value (EVAL (car a) env)))\n            (args (mapcar (lambda (x) (EVAL x env)) (cdr a))))\n        (apply fn*  args)))\n     ((setq a (mal-symbol-value ast))\n      (or (gethash a env) (error \"'%s' not found\" a)))\n     ((setq a (mal-vector-value ast))\n      (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env)) a))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (mal-map map)))\n     (t\n      ;; return as is\n      ast))))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (make-hash-table :test 'eq))\n\n  (dolist (binding\n           '((+ . (lambda (a b) (mal-number (+ (mal-number-value a) (mal-number-value b)))))\n             (- . (lambda (a b) (mal-number (- (mal-number-value a) (mal-number-value b)))))\n             (* . (lambda (a b) (mal-number (* (mal-number-value a) (mal-number-value b)))))\n             (/ . (lambda (a b) (mal-number (/ (mal-number-value a) (mal-number-value b)))))))\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (puthash symbol (mal-fn-core fn) repl-env)))\n\n  (let (input)\n    (while (setq input (readln \"user> \"))\n      (with-error-handling\n       (println (rep input repl-env))))\n    ;; print final newline\n    (terpri)))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step3_env.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (a)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (mal-env-set env identifier value)))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (EVAL form env*)))\n         (t\n          ;; not a special form\n          (let ((fn* (mal-fn-core-value (EVAL (car a) env)))\n                (args (mapcar (lambda (x) (EVAL x env)) (cdr a))))\n            (apply fn*  args)))))\n     ((setq a (mal-symbol-value ast))\n      (or (mal-env-get env a) (error \"'%s' not found\" a)))\n     ((setq a (mal-vector-value ast))\n      (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env)) a))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (mal-map map)))\n     (t\n      ;; return as is\n      ast))))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding\n           '((+ . (lambda (a b) (mal-number (+ (mal-number-value a) (mal-number-value b)))))\n             (- . (lambda (a b) (mal-number (- (mal-number-value a) (mal-number-value b)))))\n             (* . (lambda (a b) (mal-number (* (mal-number-value a) (mal-number-value b)))))\n             (/ . (lambda (a b) (mal-number (/ (mal-number-value a) (mal-number-value b)))))))\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (let (input)\n    (while (setq input (readln \"user> \"))\n      (with-error-handling\n       (println (rep input repl-env))))\n    ;; print final newline\n    (terpri)))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step4_if_fn_do.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (a)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (mal-env-set env identifier value)))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (EVAL form env*)))\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (EVAL (car a) env))\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (EVAL (cadddr a) env)\n                  mal-nil)\n              (EVAL (caddr a) env))))\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                      body binds env)))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((mal-func-value fn)\n              (EVAL (mal-func-body fn)\n                    (mal-env (mal-func-env fn)\n                             (mal-func-params fn)\n                             (mapcar (lambda (x) (EVAL x env)) args))))\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (apply fn* (mapcar (lambda (x) (EVAL x env)) args)))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (or (mal-env-get env a) (error \"'%s' not found\" a)))\n     ((setq a (mal-vector-value ast))\n      (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env)) a))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (mal-map map)))\n     (t\n      ;; return as is\n      ast))))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n\n  (let (input)\n    (while (setq input (readln \"user> \"))\n      (with-error-handling\n       (println (rep input repl-env))))\n    ;; print final newline\n    (terpri)))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step5_tco.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (return a)\n    (while (not return)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (setq return (mal-env-set env identifier value))))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (setq env env*\n                  ast form))) ; TCO\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (setq ast (car a))) ; TCO\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (setq ast (cadddr a)) ; TCO\n                  (setq return mal-nil))\n              (setq ast (caddr a))))) ; TCO\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (setq return (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                          body binds env))))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((mal-func-value fn)\n              (setq env (mal-env (mal-func-env fn)\n                                     (mal-func-params fn)\n                                     (mapcar (lambda (x) (EVAL x env)) args))\n                    ast (mal-func-body fn))) ; TCO\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (setq return (apply fn* (mapcar (lambda (x) (EVAL x env)) args))))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (setq return (or (mal-env-get env a)\n                       (error \"'%s' not found\" a))))\n     ((setq a (mal-vector-value ast))\n      (setq return\n             (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env))\n                                          a)))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (setq return (mal-map map))))\n     (t\n      ;; return as is\n      (setq return ast))))\n\n    ;; End of the TCO loop\n    return))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n\n  (let (input)\n    (while (setq input (readln \"user> \"))\n      (with-error-handling\n       (println (rep input repl-env))))\n    ;; print final newline\n    (terpri)))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step6_file.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (return a)\n    (while (not return)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (setq return (mal-env-set env identifier value))))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (setq env env*\n                  ast form))) ; TCO\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (setq ast (car a))) ; TCO\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (setq ast (cadddr a)) ; TCO\n                  (setq return mal-nil))\n              (setq ast (caddr a))))) ; TCO\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (setq return (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                          body binds env))))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((mal-func-value fn)\n              (setq env (mal-env (mal-func-env fn)\n                                     (mal-func-params fn)\n                                     (mapcar (lambda (x) (EVAL x env)) args))\n                    ast (mal-func-body fn))) ; TCO\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (setq return (apply fn* (mapcar (lambda (x) (EVAL x env)) args))))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (setq return (or (mal-env-get env a)\n                       (error \"'%s' not found\" a))))\n     ((setq a (mal-vector-value ast))\n      (setq return\n             (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env))\n                                          a)))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (setq return (mal-map map))))\n     (t\n      ;; return as is\n      (setq return ast))))\n\n    ;; End of the TCO loop\n    return))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (mal-env-set repl-env 'eval (mal-fn-core (byte-compile (lambda (form) (EVAL form repl-env)))))\n  (mal-env-set repl-env '*ARGV* (mal-list (mapcar 'mal-string (cdr argv))))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n  (rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n        \\\"\\nnil)\\\")))))\" repl-env)\n\n  (if argv\n      (with-error-handling\n       (rep (format \"(load-file \\\"%s\\\")\" (car argv)) repl-env))\n    (let (input)\n      (while (setq input (readln \"user> \"))\n              (with-error-handling\n               (println (rep input repl-env))))\n      ;; print final newline\n      (terpri))))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step7_quote.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun qq-reducer (elt acc)\n  (let ((value (mal-list-value elt)))\n    (mal-list (if (eq 'splice-unquote (mal-symbol-value (car value)))\n                  (list (mal-symbol 'concat) (cadr value) acc)\n                (list (mal-symbol 'cons) (quasiquote elt) acc)))))\n\n(defun qq-iter (elts)\n  (cl-reduce 'qq-reducer elts :from-end t :initial-value (mal-list nil)))\n\n(defun quasiquote (ast)\n  (let (value)\n    (cond\n     ((setq value (mal-list-value ast)) ; not empty\n      (if (eq 'unquote (mal-symbol-value (car value)))\n          (cadr value)\n        (qq-iter value)))\n     ((setq value (mal-vector-value ast))\n      (mal-list (list (mal-symbol 'vec) (qq-iter value))))\n     ((or (mal-map-value ast)\n          (mal-symbol-value ast))\n      (mal-list (list (mal-symbol 'quote) ast)))\n     (t                                 ; including the empty list case\n      ast))))\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (return a)\n    (while (not return)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (setq return (mal-env-set env identifier value))))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (setq env env*\n                  ast form))) ; TCO\n         (quote\n          (setq return (cadr a)))\n         (quasiquote\n          (setq ast (quasiquote (cadr a)))) ; TCO\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (setq ast (car a))) ; TCO\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (setq ast (cadddr a)) ; TCO\n                  (setq return mal-nil))\n              (setq ast (caddr a))))) ; TCO\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (setq return (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                          body binds env))))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((mal-func-value fn)\n              (setq env (mal-env (mal-func-env fn)\n                                     (mal-func-params fn)\n                                     (mapcar (lambda (x) (EVAL x env)) args))\n                    ast (mal-func-body fn))) ; TCO\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (setq return (apply fn* (mapcar (lambda (x) (EVAL x env)) args))))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (setq return (or (mal-env-get env a)\n                       (error \"'%s' not found\" a))))\n     ((setq a (mal-vector-value ast))\n      (setq return\n             (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env))\n                                          a)))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (setq return (mal-map map))))\n     (t\n      ;; return as is\n      (setq return ast))))\n\n    ;; End of the TCO loop\n    return))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (mal-env-set repl-env 'eval (mal-fn-core (byte-compile (lambda (form) (EVAL form repl-env)))))\n  (mal-env-set repl-env '*ARGV* (mal-list (mapcar 'mal-string (cdr argv))))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n  (rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n        \\\"\\nnil)\\\")))))\" repl-env)\n\n  (if argv\n      (with-error-handling\n       (rep (format \"(load-file \\\"%s\\\")\" (car argv)) repl-env))\n    (let (input)\n      (while (setq input (readln \"user> \"))\n              (with-error-handling\n               (println (rep input repl-env))))\n      ;; print final newline\n      (terpri))))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step8_macros.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun qq-reducer (elt acc)\n  (let ((value (mal-list-value elt)))\n    (mal-list (if (eq 'splice-unquote (mal-symbol-value (car value)))\n                  (list (mal-symbol 'concat) (cadr value) acc)\n                (list (mal-symbol 'cons) (quasiquote elt) acc)))))\n\n(defun qq-iter (elts)\n  (cl-reduce 'qq-reducer elts :from-end t :initial-value (mal-list nil)))\n\n(defun quasiquote (ast)\n  (let (value)\n    (cond\n     ((setq value (mal-list-value ast)) ; not empty\n      (if (eq 'unquote (mal-symbol-value (car value)))\n          (cadr value)\n        (qq-iter value)))\n     ((setq value (mal-vector-value ast))\n      (mal-list (list (mal-symbol 'vec) (qq-iter value))))\n     ((or (mal-map-value ast)\n          (mal-symbol-value ast))\n      (mal-list (list (mal-symbol 'quote) ast)))\n     (t                                 ; including the empty list case\n      ast))))\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (return a)\n    (while (not return)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (setq return (mal-env-set env identifier value))))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (setq env env*\n                  ast form))) ; TCO\n         (quote\n          (setq return (cadr a)))\n         (quasiquote\n          (setq ast (quasiquote (cadr a)))) ; TCO\n         (defmacro!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (mal-macro (mal-func-value (EVAL (caddr a) env)))))\n             (setq return (mal-env-set env identifier value))))\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (setq ast (car a))) ; TCO\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (setq ast (cadddr a)) ; TCO\n                  (setq return mal-nil))\n              (setq ast (caddr a))))) ; TCO\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (setq return (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                          body binds env))))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((setq fn* (mal-macro-value fn))\n              (setq ast (apply fn* args))) ; TCO\n             ((mal-func-value fn)\n              (setq env (mal-env (mal-func-env fn)\n                                     (mal-func-params fn)\n                                     (mapcar (lambda (x) (EVAL x env)) args))\n                    ast (mal-func-body fn))) ; TCO\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (setq return (apply fn* (mapcar (lambda (x) (EVAL x env)) args))))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (setq return (or (mal-env-get env a)\n                       (error \"'%s' not found\" a))))\n     ((setq a (mal-vector-value ast))\n      (setq return\n             (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env))\n                                          a)))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (setq return (mal-map map))))\n     (t\n      ;; return as is\n      (setq return ast))))\n\n    ;; End of the TCO loop\n    return))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (mal-env-set repl-env 'eval (mal-fn-core (byte-compile (lambda (form) (EVAL form repl-env)))))\n  (mal-env-set repl-env '*ARGV* (mal-list (mapcar 'mal-string (cdr argv))))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n  (rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n        \\\"\\nnil)\\\")))))\" repl-env)\n  (rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first\n        xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to\n        cond\\\")) (cons 'cond (rest (rest xs)))))))\" repl-env)\n\n  (if argv\n      (with-error-handling\n       (rep (format \"(load-file \\\"%s\\\")\" (car argv)) repl-env))\n    (let (input)\n      (while (setq input (readln \"user> \"))\n              (with-error-handling\n               (println (rep input repl-env))))\n      ;; print final newline\n      (terpri))))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/step9_try.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun qq-reducer (elt acc)\n  (let ((value (mal-list-value elt)))\n    (mal-list (if (eq 'splice-unquote (mal-symbol-value (car value)))\n                  (list (mal-symbol 'concat) (cadr value) acc)\n                (list (mal-symbol 'cons) (quasiquote elt) acc)))))\n\n(defun qq-iter (elts)\n  (cl-reduce 'qq-reducer elts :from-end t :initial-value (mal-list nil)))\n\n(defun quasiquote (ast)\n  (let (value)\n    (cond\n     ((setq value (mal-list-value ast)) ; not empty\n      (if (eq 'unquote (mal-symbol-value (car value)))\n          (cadr value)\n        (qq-iter value)))\n     ((setq value (mal-vector-value ast))\n      (mal-list (list (mal-symbol 'vec) (qq-iter value))))\n     ((or (mal-map-value ast)\n          (mal-symbol-value ast))\n      (mal-list (list (mal-symbol 'quote) ast)))\n     (t                                 ; including the empty list case\n      ast))))\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (return a)\n    (while (not return)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (setq return (mal-env-set env identifier value))))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (setq env env*\n                  ast form))) ; TCO\n         (quote\n          (setq return (cadr a)))\n         (quasiquote\n          (setq ast (quasiquote (cadr a)))) ; TCO\n         (defmacro!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (mal-macro (mal-func-value (EVAL (caddr a) env)))))\n             (setq return (mal-env-set env identifier value))))\n         (try*\n          (if (cddr a)\n          (condition-case err\n              (setq return (EVAL (cadr a) env))\n            (error\n                 (let* ((a2* (mal-list-value (caddr a)))\n                        (identifier (mal-symbol-value (cadr a2*)))\n                        (form (caddr a2*))\n                        (err* (if (eq (car err) 'mal-custom)\n                                  ;; throw\n                                  (cadr err)\n                                ;; normal error\n                                (mal-string (error-message-string err))))\n                        (env* (mal-env env)))\n                   (mal-env-set env* identifier err*)\n                   (setq env env*\n                         ast form)))) ; TCO\n          (setq ast (cadr a)))) ; TCO\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (setq ast (car a))) ; TCO\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (setq ast (cadddr a)) ; TCO\n                  (setq return mal-nil))\n              (setq ast (caddr a))))) ; TCO\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (setq return (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                          body binds env))))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((setq fn* (mal-macro-value fn))\n              (setq ast (apply fn* args))) ; TCO\n             ((mal-func-value fn)\n              (setq env (mal-env (mal-func-env fn)\n                                     (mal-func-params fn)\n                                     (mapcar (lambda (x) (EVAL x env)) args))\n                    ast (mal-func-body fn))) ; TCO\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (setq return (apply fn* (mapcar (lambda (x) (EVAL x env)) args))))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (setq return (or (mal-env-get env a)\n                       (error \"'%s' not found\" a))))\n     ((setq a (mal-vector-value ast))\n      (setq return\n             (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env))\n                                          a)))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (setq return (mal-map map))))\n     (t\n      ;; return as is\n      (setq return ast))))\n\n    ;; End of the TCO loop\n    return))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (mal-env-set repl-env 'eval (mal-fn-core (byte-compile (lambda (form) (EVAL form repl-env)))))\n  (mal-env-set repl-env '*ARGV* (mal-list (mapcar 'mal-string (cdr argv))))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n  (rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n        \\\"\\nnil)\\\")))))\" repl-env)\n  (rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first\n        xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to\n        cond\\\")) (cons 'cond (rest (rest xs)))))))\" repl-env)\n\n  (if argv\n      (with-error-handling\n       (rep (format \"(load-file \\\"%s\\\")\" (car argv)) repl-env))\n    (let (input)\n      (while (setq input (readln \"user> \"))\n              (with-error-handling\n               (println (rep input repl-env))))\n      ;; print final newline\n      (terpri))))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/stepA_mal.el",
    "content": ";; -*- lexical-binding: t; -*-\n\n(require 'cl-lib)\n(require 'mal/types)\n(require 'mal/env)\n(require 'mal/reader)\n(require 'mal/printer)\n(require 'mal/core)\n\n(defun qq-reducer (elt acc)\n  (let ((value (mal-list-value elt)))\n    (mal-list (if (eq 'splice-unquote (mal-symbol-value (car value)))\n                  (list (mal-symbol 'concat) (cadr value) acc)\n                (list (mal-symbol 'cons) (quasiquote elt) acc)))))\n\n(defun qq-iter (elts)\n  (cl-reduce 'qq-reducer elts :from-end t :initial-value (mal-list nil)))\n\n(defun quasiquote (ast)\n  (let (value)\n    (cond\n     ((setq value (mal-list-value ast)) ; not empty\n      (if (eq 'unquote (mal-symbol-value (car value)))\n          (cadr value)\n        (qq-iter value)))\n     ((setq value (mal-vector-value ast))\n      (mal-list (list (mal-symbol 'vec) (qq-iter value))))\n     ((or (mal-map-value ast)\n          (mal-symbol-value ast))\n      (mal-list (list (mal-symbol 'quote) ast)))\n     (t                                 ; including the empty list case\n      ast))))\n\n(defun READ (input)\n  (read-str input))\n\n(defun EVAL (ast env)\n  (let (return a)\n    (while (not return)\n\n     (let ((dbgeval (mal-env-get env 'DEBUG-EVAL)))\n       (if (not (memq dbgeval (list nil mal-nil mal-false)))\n         (println \"EVAL: %s\\n\" (PRINT ast))))\n\n     (cond\n\n     ((setq a (mal-list-value ast))\n        (cl-case (mal-symbol-value (car a))\n         (def!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (EVAL (caddr a) env)))\n             (setq return (mal-env-set env identifier value))))\n         (let*\n             (let ((env* (mal-env env))\n                   (bindings (mal-seq-value (cadr a)))\n                   (form (caddr a))\n                   key)\n               (seq-do (lambda (current)\n                         (if key\n                             (let ((value (EVAL current env*)))\n                               (mal-env-set env* key value)\n                               (setq key nil))\n                           (setq key (mal-symbol-value current))))\n                       bindings)\n            (setq env env*\n                  ast form))) ; TCO\n         (quote\n          (setq return (cadr a)))\n         (quasiquote\n          (setq ast (quasiquote (cadr a)))) ; TCO\n         (defmacro!\n           (let ((identifier (mal-symbol-value (cadr a)))\n                 (value (mal-macro (mal-func-value (EVAL (caddr a) env)))))\n             (setq return (mal-env-set env identifier value))))\n         (try*\n          (if (cddr a)\n          (condition-case err\n              (setq return (EVAL (cadr a) env))\n            (error\n                 (let* ((a2* (mal-list-value (caddr a)))\n                        (identifier (mal-symbol-value (cadr a2*)))\n                        (form (caddr a2*))\n                        (err* (if (eq (car err) 'mal-custom)\n                                  ;; throw\n                                  (cadr err)\n                                ;; normal error\n                                (mal-string (error-message-string err))))\n                        (env* (mal-env env)))\n                   (mal-env-set env* identifier err*)\n                   (setq env env*\n                         ast form)))) ; TCO\n          (setq ast (cadr a)))) ; TCO\n         (do\n          (setq a (cdr a))              ; skip 'do\n          (while (cdr a)\n            (EVAL (pop a) env))\n          (setq ast (car a))) ; TCO\n         (if\n          (let ((condition (EVAL (cadr a) env)))\n            (if (memq condition (list mal-nil mal-false))\n                (if (cdddr a)\n                    (setq ast (cadddr a)) ; TCO\n                  (setq return mal-nil))\n              (setq ast (caddr a))))) ; TCO\n         (fn*\n          (let ((binds (mapcar 'mal-symbol-value (mal-seq-value (cadr a))))\n                (body (caddr a)))\n            (setq return (mal-func\n                      (lambda (&rest args)\n                            (EVAL body (mal-env env binds args)))\n                          body binds env))))\n         (t\n          ;; not a special form\n          (let ((fn (EVAL (car a) env))\n                (args (cdr a))\n                fn*)\n            (cond\n             ((setq fn* (mal-macro-value fn))\n              (setq ast (apply fn* args))) ; TCO\n             ((mal-func-value fn)\n              (setq env (mal-env (mal-func-env fn)\n                                     (mal-func-params fn)\n                                     (mapcar (lambda (x) (EVAL x env)) args))\n                    ast (mal-func-body fn))) ; TCO\n             ((setq fn* (mal-fn-core-value fn))\n              ;; built-in function\n              (setq return (apply fn* (mapcar (lambda (x) (EVAL x env)) args))))\n             (t (error \"cannot apply %s\" (PRINT ast))))))))\n     ((setq a (mal-symbol-value ast))\n      (setq return (or (mal-env-get env a)\n                       (error \"'%s' not found\" a))))\n     ((setq a (mal-vector-value ast))\n      (setq return\n             (mal-vector (vconcat (mapcar (lambda (item) (EVAL item env))\n                                          a)))))\n     ((setq a (mal-map-value ast))\n      (let ((map (copy-hash-table a)))\n        (maphash (lambda (key val)\n                   (puthash key (EVAL val env) map))\n                 map)\n        (setq return (mal-map map))))\n     (t\n      ;; return as is\n      (setq return ast))))\n\n    ;; End of the TCO loop\n    return))\n\n(defun PRINT (input)\n  (pr-str input t))\n\n(defun rep (input repl-env)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(defun readln (prompt)\n  ;; C-d throws an error\n  (ignore-errors (read-from-minibuffer prompt)))\n\n(defun println (format-string &rest args)\n  (princ (if args\n             (apply 'format format-string args)\n           format-string))\n  (terpri))\n\n(defmacro with-error-handling (&rest body)\n  `(condition-case err\n       (progn ,@body)\n     (end-of-token-stream\n      ;; empty input, carry on\n      )\n     (unterminated-sequence\n      (princ (format \"Expected '%c', got EOF\\n\"\n                     (cl-case (cadr err)\n                       (string ?\\\")\n                       (list   ?\\))\n                       (vector ?\\])\n                       (map    ?})))))\n     (error ; catch-all\n      (println (error-message-string err)))))\n\n(defun main ()\n  (defvar repl-env (mal-env))\n\n  (dolist (binding core-ns)\n    (let ((symbol (car binding))\n          (fn (cdr binding)))\n      (mal-env-set repl-env symbol (mal-fn-core fn))))\n\n  (mal-env-set repl-env 'eval (mal-fn-core (byte-compile (lambda (form) (EVAL form repl-env)))))\n  (mal-env-set repl-env '*ARGV* (mal-list (mapcar 'mal-string (cdr argv))))\n  (mal-env-set repl-env '*host-language* (mal-string \"elisp\"))\n\n  (rep \"(def! not (fn* (a) (if a false true)))\" repl-env)\n  (rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n        \\\"\\nnil)\\\")))))\" repl-env)\n  (rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first\n        xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to\n        cond\\\")) (cons 'cond (rest (rest xs)))))))\" repl-env)\n\n  (if argv\n      (with-error-handling\n       (rep (format \"(load-file \\\"%s\\\")\" (car argv)) repl-env))\n    (let (input)\n      (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\" repl-env)\n      (while (setq input (readln \"user> \"))\n              (with-error-handling\n               (println (rep input repl-env))))\n      ;; print final newline\n      (terpri))))\n\n(main)\n"
  },
  {
    "path": "impls/elisp/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/elisp/tests/stepA_mal.mal",
    "content": ";; Testing basic elisp interop\n\n(elisp-eval \"42\")\n;=>42\n\n(elisp-eval \"(+ 1 1)\")\n;=>2\n\n(elisp-eval \"[foo bar baz]\")\n;=>[foo bar baz]\n\n(elisp-eval \"(mapcar '1+ (number-sequence 0 2))\")\n;=>(1 2 3)\n\n(elisp-eval \"(progn (princ \\\"Hello World!\\n\\\") nil)\")\n;/Hello World!\n;=>nil\n\n(elisp-eval \"(setq emacs-version-re (rx (+ digit) \\\".\\\" digit))\")\n(elisp-eval \"(and (string-match-p emacs-version-re emacs-version) t)\")\n;=>true\n"
  },
  {
    "path": "impls/elixir/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Elixir\nRUN apt-get install -y elixir\n"
  },
  {
    "path": "impls/elixir/Makefile",
    "content": "SOURCES_BASE = lib/mal/types.ex lib/mal/reader.ex lib/mal/printer.ex\nSOURCES_LISP = lib/mal/env.ex lib/mal/core.ex lib/mix/tasks/stepA_mal.ex\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\tmix compile\n\ndist: mal\n\nmal: $(SOURCES)\n\tmix escript.build\n\nclean:\n\tmix clean\n\trm -f mal\n\n.PHONY: clean\n"
  },
  {
    "path": "impls/elixir/lib/mal/atom.ex",
    "content": "defmodule Mal.Atom do\n  alias Mal.Function\n\n  def new(value) do\n    {:ok, pid} = Agent.start_link(fn -> value end)\n    pid\n  end\n\n  def deref({:atom, pid}) do\n    Agent.get(pid, fn value -> value end)\n  end\n\n  def reset!({:atom, pid}, new_value) do\n    Agent.update(pid, fn _ -> new_value end)\n    new_value\n  end\n\n  def swap!({:atom, pid}, %Function{value: func}, args) do\n    Agent.get_and_update(pid, fn state ->\n      func_args = [state | args]\n      new = func.(func_args)\n      {new, new}\n    end)\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mal/core.ex",
    "content": "defmodule Mal.Core do\n  import Mal.Types\n  alias Mal.Function\n\n  def namespace do\n    raw = %{\n      \"+\" => fn [a, b] -> a + b end,\n      \"-\" => fn [a, b] -> a - b end,\n      \"*\" => fn [a, b] -> a * b end,\n      \"/\" => fn [a, b] -> div(a, b) end,\n      \">\" => fn [a, b] -> a > b end,\n      \"<\" => fn [a, b] -> a < b end,\n      \"<=\" => fn [a, b] -> a <= b end,\n      \">=\" => fn [a, b] -> a >= b end,\n      \"concat\" => &concat/1,\n      \"=\" => &equal/1,\n      \"list?\" => &list?/1,\n      \"empty?\" => &empty?/1,\n      \"count\" => &count/1,\n      \"pr-str\" => &pr_str/1,\n      \"str\" => &str/1,\n      \"prn\" => &prn/1,\n      \"println\" => &println/1,\n      \"slurp\" => &slurp/1,\n      \"nth\" => &nth/1,\n      \"first\" => &first/1,\n      \"rest\" => &rest/1,\n      \"map\" => &map/1,\n      \"apply\" => &apply/1,\n      \"keyword\" => &keyword/1,\n      \"symbol?\" => &symbol?/1,\n      \"cons\" => &cons/1,\n      \"vec\" => &vec/1,\n      \"vector?\" => &vector?/1,\n      \"assoc\" => &assoc/1,\n      \"dissoc\" => &dissoc/1,\n      \"get\" => &get/1,\n      \"map?\" => &map?/1,\n      \"list\" => &list/1,\n      \"vector\" => &vector/1,\n      \"hash-map\" => &hash_map/1,\n      \"meta\" => &meta/1,\n      \"with-meta\" => &with_meta/1,\n      \"atom\" => &atom/1,\n      \"atom?\" => &atom?/1,\n      \"deref\" => &deref/1,\n      \"reset!\" => &reset!/1,\n      \"swap!\" => &swap!/1,\n      \"conj\" => &conj/1,\n      \"seq\" => &seq/1,\n      \"fn?\" => &fn?/1,\n      \"macro?\" => &macro?/1,\n      \"time-ms\" => fn _ -> :erlang.system_time(:milli_seconds) end,\n      \"readline\" => fn [prompt] -> readline(prompt) end,\n      \"sequential?\" => fn arg -> vector?(arg) or list?(arg) end,\n      \"keyword?\" => fn [type] -> is_atom(type) end,\n      \"nil?\" => fn [type] -> type == nil end,\n      \"true?\" => fn [type] -> type == true end,\n      \"false?\" => fn [type] -> type == false end,\n      \"string?\" => fn [obj] -> String.valid?(obj) end,\n      \"number?\" => fn [obj] -> is_number(obj) end,\n      \"symbol\" => fn [name] -> {:symbol, name} end,\n      \"read-string\" => fn [input] -> Mal.Reader.read_str(input) end,\n      \"throw\" => fn [arg] -> throw({:error, arg}) end,\n      \"contains?\" => fn [{:map, map, _}, key] -> Map.has_key?(map, key) end,\n      \"keys\" => fn [{:map, map, _}] -> Map.keys(map) |> list end,\n      \"vals\" => fn [{:map, map, _}] -> Map.values(map) |> list end\n    }\n\n    convert(raw)\n  end\n\n  defp convert(map) do\n    for {name, func} <- map, into: %{} do\n      {name, %Function{value: func}}\n    end\n  end\n\n  def readline(prompt) do\n    IO.write(:stdio, prompt)\n    IO.read(:stdio, :line)\n      |> String.trim(\"\\n\")\n  end\n\n  defp convert_vector({type, ast, meta}) when type == :map do\n    new_ast = Enum.map(ast, fn {key, value} ->\n      {key, convert_vector(value)}\n    end)\n    {:map, new_ast, meta}\n  end\n  defp convert_vector({type, ast, meta}) when type in [:list, :vector] do\n    new_ast = Enum.map(ast, &convert_vector/1)\n    {:list, new_ast, meta}\n  end\n  defp convert_vector(other), do: other\n\n  defp equal([a, b]) do\n    convert_vector(a) == convert_vector(b)\n  end\n\n  defp empty?([{_type, [], _meta}]), do: true\n  defp empty?(_), do: false\n\n  defp count([{_type, ast, _meta}]), do: length(ast)\n  defp count(_), do: 0\n\n  defp pr_str(args) do\n    args\n      |> Enum.map(&Mal.Printer.print_str/1)\n      |> Enum.join(\" \")\n  end\n\n  defp str(args) do\n    args\n      |> Enum.map(&(Mal.Printer.print_str(&1, false)))\n      |> Enum.join(\"\")\n  end\n\n  defp prn(args) do\n    args\n      |> pr_str\n      |> IO.puts\n    nil\n  end\n\n  defp println(args) do\n    args\n      |> Enum.map(&(Mal.Printer.print_str(&1, false)))\n      |> Enum.join(\" \")\n      |> IO.puts\n    nil\n  end\n\n  defp slurp([file_name]) do\n    case File.read(file_name) do\n      {:ok, content} -> content\n      {:error, :enoent} -> throw({:error, \"can't find file #{file_name}\"})\n      {:error, :eisdir} -> throw({:error, \"can't read directory #{file_name}\"})\n      {:error, :eaccess} -> throw({:error, \"missing permissions #{file_name}\"})\n      {:error, reason} -> throw({:error, \"can't read file #{file_name}, #{reason}\"})\n    end\n  end\n\n  defp nth([{_type, ast, _meta}, index]) do\n    case Enum.at(ast, index, :error) do\n      :error -> throw({:error, \"index out of bounds\"})\n      any -> any\n    end\n  end\n\n  defp first([{_type, [head | _tail], _}]), do: head\n  defp first(_), do: nil\n\n  defp rest([{_type, [_head | tail], _}]), do: list(tail)\n  defp rest([{_type, [], _}]), do: list([])\n  defp rest([nil]), do: list([])\n\n  defp map([%Function{value: function}, ast]), do: do_map(function, ast)\n  defp map([function, ast]), do: do_map(function, ast)\n\n  defp do_map(function, {_type, ast, _meta}) do\n    ast\n      |> Enum.map(fn arg -> function.([arg]) end)\n      |> list\n  end\n\n  defp apply([%Function{value: function} | tail]), do: do_apply(function, tail)\n  defp apply([function | tail]), do: do_apply(function, tail)\n\n  defp do_apply(function, tail) do\n    [{_type, ast, _meta} | reversed_args] = Enum.reverse(tail)\n    args = Enum.reverse(reversed_args)\n    func_args = Enum.concat(args, ast)\n    function.(func_args)\n  end\n\n  defp keyword([atom]) when is_atom(atom), do: atom\n  defp keyword([atom]), do: String.to_atom(atom)\n\n  defp cons([prepend, {_type, ast, meta}]), do: {:list, [prepend | ast], meta}\n\n  defp concat(args) do\n    args\n      |> Enum.map(fn tuple -> elem(tuple, 1) end)\n      |> Enum.concat\n      |> list\n  end\n\n  defp vec([{:list,   xs, _}]), do: vector(xs)\n  defp vec([{:vector, xs, _}]), do: vector(xs)\n  defp vec([_]),                do: throw({:error, \"vec: arg type\"})\n  defp vec(_),                  do: throw({:error, \"vec: arg count\"})\n\n  defp assoc([{:map, hash_map, meta} | pairs]) do\n    {:map, merge, _} = hash_map(pairs)\n    {:map, Map.merge(hash_map, merge), meta}\n  end\n\n  defp dissoc([{:map, hash_map, meta} | keys]) do\n    {:map, Map.drop(hash_map, keys), meta}\n  end\n\n  defp get([{:map, map, _}, key]), do: Map.get(map, key, nil)\n  defp get(_), do: nil\n\n  defp meta([{_type, _ast, meta}]), do: meta\n  defp meta([%Function{meta: meta}]), do: meta\n  defp meta(_), do: nil\n\n  defp with_meta([{type, ast, _old_meta}, meta]), do: {type, ast, meta}\n  defp with_meta([%Function{} = func, meta]), do: %{func | meta: meta}\n  defp with_meta(_), do: nil\n\n  defp deref(args) do\n    apply(&Mal.Atom.deref/1, args)\n  end\n\n  defp reset!(args) do\n    apply(&Mal.Atom.reset!/2, args)\n  end\n\n  defp swap!([atom, function | args]) do\n    Mal.Atom.swap!(atom, function, args)\n  end\n\n  defp conj([{:list, ast, meta} | args]) do\n    new_list = Enum.reverse(args) ++ ast\n    {:list, new_list, meta}\n  end\n\n  defp conj([{:vector, ast, meta} | args]) do\n    {:vector, ast ++ args, meta}\n  end\n\n  defp seq([nil]), do: nil\n  defp seq([{:list, [], _meta}]), do: nil\n  defp seq([{:list, ast, meta}]), do: {:list, ast, meta}\n  defp seq([{:vector, [], _meta}]), do: nil\n  defp seq([{:vector, ast, meta}]), do: {:list, ast, meta}\n  defp seq([\"\"]), do: nil\n  defp seq([s]), do: {:list, String.split(s, \"\", trim: true), nil}\n  defp seq(_), do: nil\n\n  defp fn?([%Function{macro: false}]), do: true\n  defp fn?(_), do: false\n\n  defp macro?([%Function{macro: true}]), do: true\n  defp macro?(_), do: false\nend\n"
  },
  {
    "path": "impls/elixir/lib/mal/env.ex",
    "content": "defmodule Mal.Env do\n  import Mal.Types\n\n  def new(outer \\\\ nil, binds \\\\ [], exprs \\\\ [])\n  def new(outer, binds, exprs) do\n    {:ok, pid} = Agent.start_link(fn ->\n      %{outer: outer, env: %{}}\n    end)\n\n    set_bindings(pid, binds, exprs)\n  end\n\n  defp set_bindings(pid, [], []), do: pid\n  defp set_bindings(pid, [\"&\", key], exprs) do\n    set(pid, key, list(exprs))\n    pid\n  end\n\n  defp set_bindings(pid, [key | binds], [value | exprs]) do\n    set(pid, key, value)\n    set_bindings(pid, binds, exprs)\n  end\n\n  def set(pid, key, value) do\n    Agent.update(pid, fn map ->\n      %{map | :env => Map.put(map.env, key, value)}\n    end)\n  end\n\n  def merge(pid, env_values) do\n    Agent.update(pid, fn map ->\n      %{map | :env => Map.merge(map.env, env_values)}\n    end)\n  end\n\n  def find(pid, key) do\n    Agent.get(pid, fn map ->\n      case Map.has_key?(map.env, key) do\n        true -> pid\n        false -> map.outer && find(map.outer, key)\n      end\n    end)\n  end\n\n  def retrieve_key(pid, key) do\n    Agent.get(pid, fn map ->\n      case Map.fetch(map.env, key) do\n        {:ok, value} -> {:ok, value}\n        :error -> :not_found\n      end\n    end)\n  end\n\n  def get(pid, key) do\n    case find(pid, key) do\n      nil -> :not_found\n      env -> retrieve_key(env, key)\n    end\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mal/printer.ex",
    "content": "defmodule Mal.Printer do\n  alias Mal.Function\n\n  def print_str(mal, print_readably \\\\ true)\n  def print_str(mal, _) when is_atom(mal), do: inspect(mal)\n  def print_str(mal, _) when is_integer(mal), do: Integer.to_string(mal)\n  def print_str(mal, _) when is_function(mal), do: inspect(mal)\n  def print_str(%Function{value: mal, macro: true}, _), do: \"#Macro<#{inspect(mal)}\"\n  def print_str(%Function{value: mal}, _), do: inspect(mal)\n  def print_str({:symbol, value}, _), do: value\n  def print_str({:exception, exception}, print_readably) do\n    print_str(exception, print_readably)\n  end\n  def print_str(mal, false) when is_bitstring(mal), do: mal\n  def print_str(mal, true) when is_bitstring(mal), do: inspect(mal)\n\n  def print_str({:atom, _pid} = atom, print_readably) do\n    output = atom\n      |> Mal.Atom.deref\n      |> print_str(print_readably)\n\n    \"(atom #{output})\"\n  end\n\n  def print_str({:map, mal, _}, print_readably) do\n    evaluate_pair = fn {key, value} ->\n      \"#{print_str(key, print_readably)} #{print_str(value, print_readably)}\"\n    end\n\n    output = mal\n      |> Enum.map(evaluate_pair)\n      |> Enum.join(\" \")\n\n    \"{#{output}}\"\n  end\n\n  def print_str({:vector, vector, _}, print_readably) do\n    \"[#{print_list(vector, print_readably)}]\"\n  end\n\n  def print_str({:list, mal, _}, print_readably) do\n    \"(#{print_list(mal, print_readably)})\"\n  end\n\n  defp print_list(list, print_readably) do\n    list\n      |> Enum.map(fn(x) -> print_str(x, print_readably) end)\n      |> Enum.join(\" \")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mal/reader.ex",
    "content": "defmodule Mal.Reader do\n  import Mal.Types\n\n  def read_str(input) do\n    case tokenize(input) do\n      [] -> nil\n      tokens -> tokens\n        |> read_form\n        |> elem(0)\n    end\n  end\n\n  def tokenize(input) do\n    regex = ~r/[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/\n    Regex.scan(regex, input, capture: :all_but_first)\n      |> List.flatten\n      |> List.delete_at(-1) # Remove the last match, which is an empty string\n      |> Enum.filter(fn token -> not String.starts_with?(token, \";\") end)\n  end\n\n  defp read_form([next | rest] = tokens) do\n    case next do\n      \"(\" -> read_list(tokens)\n      \"[\" -> read_vector(tokens)\n      \"{\" -> read_hash_map(tokens)\n      \"'\" -> create_quote(\"quote\", rest)\n      \"`\" -> create_quote(\"quasiquote\", rest)\n      \"~\" -> create_quote(\"unquote\", rest)\n      \"~@\" -> create_quote(\"splice-unquote\", rest)\n      \"@\" -> create_quote(\"deref\", rest)\n      \"^\" -> create_meta(rest)\n      \")\" -> throw({:error, \"unexpected )\"})\n      \"]\" -> throw({:error, \"unexpected ]\"})\n      \"}\" -> throw({:error, \"unexpected }\"})\n      _ ->\n        token = read_atom(next)\n        {token, rest}\n    end\n  end\n\n  defp create_meta(tokens) do\n    {meta, meta_rest} = read_form(tokens)\n    {token, rest_tokens} = read_form(meta_rest)\n    new_token = list([{:symbol, \"with-meta\"}, token, meta])\n    {new_token, rest_tokens}\n  end\n\n  defp create_quote(quote_type, tokens) do\n    {token, rest_tokens} = read_form(tokens)\n    new_token = list([{:symbol, quote_type}, token])\n    {new_token, rest_tokens}\n  end\n\n  defp read_list([_ | tokens]) do\n    {ast, rest} = do_read_sequence(tokens, [], \"(\", \")\")\n    {list(ast), rest}\n  end\n\n  defp read_vector([_ | tokens]) do\n    {ast, rest} = do_read_sequence(tokens, [], \"[\", \"]\")\n    {vector(ast), rest}\n  end\n\n  defp read_hash_map([_ | tokens]) do\n    {map, rest} = do_read_sequence(tokens, [], \"{\", \"}\")\n    {hash_map(map), rest}\n  end\n\n  defp do_read_sequence([], _acc, _start_sep, end_sep), do: throw({:error, \"expected #{end_sep}, got EOF\"})\n  defp do_read_sequence([head | tail] = tokens, acc, start_sep, end_sep) do\n    cond do\n      String.starts_with?(head, end_sep) ->\n        {Enum.reverse(acc), tail}\n      true ->\n        {token, rest} = read_form(tokens)\n        do_read_sequence(rest, [token | acc], start_sep, end_sep)\n    end\n  end\n\n  defp read_atom(\"nil\"), do: nil\n  defp read_atom(\"true\"), do: true\n  defp read_atom(\"false\"), do: false\n  defp read_atom(\":\" <> rest), do: String.to_atom(rest)\n  defp read_atom(token) do\n    cond do\n      String.match?(token, ~r/^\"(?:\\\\.|[^\\\\\"])*\"$/) ->\n        token\n          |> Code.string_to_quoted\n          |> elem(1)\n\n      String.starts_with?(token, \"\\\"\") ->\n        throw({:error, \"expected '\\\"', got EOF\"})\n\n      integer?(token) ->\n        Integer.parse(token)\n          |> elem(0)\n\n      true -> {:symbol, token}\n    end\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mal/types.ex",
    "content": "defmodule Mal.Types do\n  def integer?(input) do\n    Regex.match?(~r/^-?[0-9]+$/, input)\n  end\n\n  def hash_map(ast) do\n    map = ast\n      |> Enum.chunk(2)\n      |> Enum.map(&List.to_tuple/1)\n      |> Enum.into(%{})\n\n    {:map, map, nil}\n  end\n\n  def map?([{:map, _ast, _meta}]), do: true\n  def map?(_), do: false\n\n  def list(ast), do: {:list, ast, nil}\n\n  def list?([{:list, _, _}]), do: true\n  def list?(_), do: false\n\n  def vector(ast), do: {:vector, ast, nil}\n\n  def vector?([{:vector, _ast, _meta}]), do: true\n  def vector?(_), do: false\n\n  def symbol?([{:symbol, _}]), do: true\n  def symbol?(_), do: false\n\n  def atom([value]) do\n    pid = Mal.Atom.new(value)\n    {:atom, pid}\n  end\n\n  def atom?([{:atom, _}]), do: true\n  def atom?(_), do: false\nend\n\ndefmodule Mal.Function do\n  defstruct value: nil, macro: false, meta: nil\nend\n"
  },
  {
    "path": "impls/elixir/lib/mal.ex",
    "content": "defmodule Mal do\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step0_repl.ex",
    "content": "defmodule Mix.Tasks.Step0Repl do\n  def run(_), do: loop()\n\n  defp loop do\n    Mal.Core.readline(\"user> \")\n      |> read_eval_print\n      |> IO.puts\n\n    loop()\n  end\n\n  defp read(input) do\n    input\n  end\n\n  defp eval(input) do\n    input\n  end\n\n  defp print(input) do\n    input\n  end\n\n  defp read_eval_print(:eof), do: exit(:normal)\n  defp read_eval_print(line) do\n    read(line)\n      |> eval\n      |> print\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step1_read_print.ex",
    "content": "defmodule Mix.Tasks.Step1ReadPrint do\n  def run(_), do: loop()\n\n  defp loop do\n    Mal.Core.readline(\"user> \")\n      |> read_eval_print\n      |> IO.puts\n\n    loop()\n  end\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval(ast), do: ast\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof), do: exit(:normal)\n  defp read_eval_print(line) do\n    read(line)\n      |> eval\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step2_eval.ex",
    "content": "defmodule Mix.Tasks.Step2Eval do\n  @repl_env %{\n    \"+\" => &+/2,\n    \"-\" => &-/2,\n    \"*\" => &*/2,\n    \"/\" => &div/2\n  }\n\n  def run(_), do: loop()\n\n  defp loop do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print\n      |> IO.puts\n\n    loop()\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Map.fetch(env, symbol) do\n      {:ok, value} -> value\n      :error -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval(ast, env) do\n    # IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    args = Enum.map(args, fn elem -> eval(elem, env) end)\n    apply(func, args)\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof), do: exit(:normal)\n  defp read_eval_print(line) do\n    read(line)\n      |> eval(@repl_env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step3_env.ex",
    "content": "defmodule Mix.Tasks.Step3Env do\n  @initial_env %{\n    \"+\" => &+/2,\n    \"-\" => &-/2,\n    \"*\" => &*/2,\n    \"/\" => &div/2\n  }\n\n  def run(_) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, @initial_env)\n    loop(env)\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    args = Enum.map(args, fn elem -> eval(elem, env) end)\n    apply(func, args)\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step4_if_fn_do.ex",
    "content": "defmodule Mix.Tasks.Step4IfFnDo do\n  import Mal.Types\n  alias Mal.Function\n\n  def run(_) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(env)\n    loop(env)\n  end\n\n  defp bootstrap(env) do\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    args = Enum.map(args, fn elem -> eval(elem, env) end)\n    func.value.(args)\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step5_tco.ex",
    "content": "defmodule Mix.Tasks.Step5Tco do\n  import Mal.Types\n  alias Mal.Function\n\n  def run(_) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(env)\n    loop(env)\n  end\n\n  defp bootstrap(env) do\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    args = Enum.map(args, fn elem -> eval(elem, env) end)\n    case func do\n      %Function{value: closure} -> closure.(args)\n      _ -> func.(args)\n    end\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step6_file.ex",
    "content": "defmodule Mix.Tasks.Step6File do\n  import Mal.Types\n  alias Mal.Function\n\n  def run(args) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(args, env)\n    loop(env)\n  end\n\n  defp load_file(file_name, env) do\n    read_eval_print(\"\"\"\n      (load-file \"#{file_name}\")\n      \"\"\", env)\n    exit(:normal)\n  end\n\n  defp bootstrap(args, env) do\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    # load-file:\n    read_eval_print(\"\"\"\n      (def! load-file\n        (fn* (f)\n          (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n\n    case args do\n      [file_name | rest] ->\n        Mal.Env.set(env, \"*ARGV*\", list(rest))\n        load_file(file_name, env)\n\n      [] ->\n        Mal.Env.set(env, \"*ARGV*\", list([]))\n    end\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    args = Enum.map(args, fn elem -> eval(elem, env) end)\n    func.value.(args)\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step7_quote.ex",
    "content": "defmodule Mix.Tasks.Step7Quote do\n  import Mal.Types\n  alias Mal.Function\n\n  def run(args) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(args, env)\n    loop(env)\n  end\n\n  defp load_file(file_name, env) do\n    read_eval_print(\"\"\"\n      (load-file \"#{file_name}\")\n      \"\"\", env)\n    exit(:normal)\n  end\n\n  defp bootstrap(args, env) do\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    # load-file:\n    read_eval_print(\"\"\"\n      (def! load-file\n        (fn* (f)\n          (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n\n    case args do\n      [file_name | rest] ->\n        Mal.Env.set(env, \"*ARGV*\", list(rest))\n        load_file(file_name, env)\n\n      [] ->\n        Mal.Env.set(env, \"*ARGV*\", list([]))\n    end\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp quasiquote({:list, [{:symbol, \"unquote\"}, arg], _}), do: arg\n  defp quasiquote({:list, [{:symbol, \"unquote\"}|   _], _}), do: throw({:error, \"unquote: arg count\"})\n  defp quasiquote({:list,   xs, _}), do:                         qq_foldr(xs)\n  defp quasiquote({:vector, xs, _}), do: list([{:symbol, \"vec\"}, qq_foldr(xs)])\n  defp quasiquote({:symbol, sym}),    do: list([{:symbol, \"quote\"}, {:symbol, sym}])\n  defp quasiquote({:map, ast, meta}), do: list([{:symbol, \"quote\"}, {:map, ast, meta}])\n  defp quasiquote(ast), do: ast\n\n  defp qq_foldr([]),     do: list([])\n  defp qq_foldr([x|xs]), do: qq_loop(x, qq_foldr xs)\n\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}, arg], _}, acc), do: list([{:symbol, \"concat\"}, arg, acc])\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}|   _], _},   _), do: throw({:error, \"splice-unquote: arg count\"})\n  defp qq_loop(elt, acc), do: list([{:symbol, \"cons\"}, quasiquote(elt), acc])\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([{:symbol, \"quote\"}, arg], _env, _), do: arg\n\n  defp eval_list([{:symbol, \"quasiquote\"}, ast], env, _) do\n    ast |> quasiquote\n      |> eval(env)\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    args = Enum.map(args, fn elem -> eval(elem, env) end)\n    func.value.(args)\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step8_macros.ex",
    "content": "defmodule Mix.Tasks.Step8Macros do\n  import Mal.Types\n  alias Mal.Function\n\n  def run(args) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(args, env)\n    loop(env)\n  end\n\n  defp load_file(file_name, env) do\n    read_eval_print(\"\"\"\n      (load-file \"#{file_name}\")\n      \"\"\", env)\n    exit(:normal)\n  end\n\n  defp bootstrap(args, env) do\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    # load-file:\n    read_eval_print(\"\"\"\n      (def! load-file\n        (fn* (f)\n          (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n      \"\"\", env)\n\n    # cond\n    read_eval_print(\"\"\"\n      (defmacro! cond\n        (fn* (& xs)\n          (if (> (count xs) 0)\n            (list 'if (first xs)\n              (if (> (count xs) 1)\n                (nth xs 1)\n                (throw \\\"odd number of forms to cond\\\"))\n              (cons 'cond (rest (rest xs)))))))\"\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n\n    case args do\n      [file_name | rest] ->\n        Mal.Env.set(env, \"*ARGV*\", list(rest))\n        load_file(file_name, env)\n\n      [] ->\n        Mal.Env.set(env, \"*ARGV*\", list([]))\n    end\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp quasiquote({:list, [{:symbol, \"unquote\"}, arg], _}), do: arg\n  defp quasiquote({:list, [{:symbol, \"unquote\"}|   _], _}), do: throw({:error, \"unquote: arg count\"})\n  defp quasiquote({:list,   xs, _}), do:                         qq_foldr(xs)\n  defp quasiquote({:vector, xs, _}), do: list([{:symbol, \"vec\"}, qq_foldr(xs)])\n  defp quasiquote({:symbol, sym}),    do: list([{:symbol, \"quote\"}, {:symbol, sym}])\n  defp quasiquote({:map, ast, meta}), do: list([{:symbol, \"quote\"}, {:map, ast, meta}])\n  defp quasiquote(ast), do: ast\n\n  defp qq_foldr([]),     do: list([])\n  defp qq_foldr([x|xs]), do: qq_loop(x, qq_foldr xs)\n\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}, arg], _}, acc), do: list([{:symbol, \"concat\"}, arg, acc])\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}|   _], _},   _), do: throw({:error, \"splice-unquote: arg count\"})\n  defp qq_loop(elt, acc), do: list([{:symbol, \"cons\"}, quasiquote(elt), acc])\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"defmacro!\"}, {:symbol, key}, function], env, _) do\n    macro = %{eval(function, env) | macro: true}\n    Mal.Env.set(env, key, macro)\n    macro\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([{:symbol, \"quote\"}, arg], _env, _), do: arg\n\n  defp eval_list([{:symbol, \"quasiquote\"}, ast], env, _) do\n    ast |> quasiquote\n      |> eval(env)\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    case func do\n      %Function{macro: true} -> func.value.(args) |> eval(env)\n      _ -> func.value.(Enum.map(args, fn elem -> eval(elem, env) end))\n    end\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, message} -> IO.puts(\"Error: #{message}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/step9_try.ex",
    "content": "defmodule Mix.Tasks.Step9Try do\n  import Mal.Types\n  alias Mal.Function\n\n  def run(args) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(args, env)\n    loop(env)\n  end\n\n  defp load_file(file_name, env) do\n    read_eval_print(\"\"\"\n      (load-file \"#{file_name}\")\n      \"\"\", env)\n    exit(:normal)\n  end\n\n  defp bootstrap(args, env) do\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    # load-file:\n    read_eval_print(\"\"\"\n      (def! load-file\n        (fn* (f)\n          (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n      \"\"\", env)\n\n    # cond\n    read_eval_print(\"\"\"\n      (defmacro! cond\n        (fn* (& xs)\n          (if (> (count xs) 0)\n            (list 'if (first xs)\n              (if (> (count xs) 1)\n                (nth xs 1)\n                (throw \\\"odd number of forms to cond\\\"))\n              (cons 'cond (rest (rest xs)))))))\"\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n\n    case args do\n      [file_name | rest] ->\n        Mal.Env.set(env, \"*ARGV*\", list(rest))\n        load_file(file_name, env)\n\n      [] ->\n        Mal.Env.set(env, \"*ARGV*\", list([]))\n    end\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp quasiquote({:list, [{:symbol, \"unquote\"}, arg], _}), do: arg\n  defp quasiquote({:list, [{:symbol, \"unquote\"}|   _], _}), do: throw({:error, \"unquote: arg count\"})\n  defp quasiquote({:list,   xs, _}), do:                         qq_foldr(xs)\n  defp quasiquote({:vector, xs, _}), do: list([{:symbol, \"vec\"}, qq_foldr(xs)])\n  defp quasiquote({:symbol, sym}),    do: list([{:symbol, \"quote\"}, {:symbol, sym}])\n  defp quasiquote({:map, ast, meta}), do: list([{:symbol, \"quote\"}, {:map, ast, meta}])\n  defp quasiquote(ast), do: ast\n\n  defp qq_foldr([]),     do: list([])\n  defp qq_foldr([x|xs]), do: qq_loop(x, qq_foldr xs)\n\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}, arg], _}, acc), do: list([{:symbol, \"concat\"}, arg, acc])\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}|   _], _},   _), do: throw({:error, \"splice-unquote: arg count\"})\n  defp qq_loop(elt, acc), do: list([{:symbol, \"cons\"}, quasiquote(elt), acc])\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"defmacro!\"}, {:symbol, key}, function], env, _) do\n    macro = %{eval(function, env) | macro: true}\n    Mal.Env.set(env, key, macro)\n    macro\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([{:symbol, \"quote\"}, arg], _env, _), do: arg\n\n  defp eval_list([{:symbol, \"quasiquote\"}, ast], env, _) do\n    ast |> quasiquote\n      |> eval(env)\n  end\n\n  # (try* A (catch* B C))\n  defp eval_list([{:symbol, \"try*\"}, try_form, {:list, catch_list, _meta}], env, _) do\n    eval_try(try_form, catch_list, env)\n  end\n  defp eval_list([{:symbol, \"try*\"}, try_form], env, _) do\n    eval(try_form, env)\n  end\n  defp eval_list([{:symbol, \"try*\"}, _try_form, _], _env, _) do\n    throw({:error, \"try* requires a list as the second parameter\"})\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    case func do\n      %Function{macro: true} -> func.value.(args) |> eval(env)\n      _ -> func.value.(Enum.map(args, fn elem -> eval(elem, env) end))\n    end\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp eval_try(try_form,\n  [{:symbol, \"catch*\"}, {:symbol, exception}, catch_form], env) do\n    try do\n      eval(try_form, env)\n    catch\n      {:error, message}->\n        catch_env = Mal.Env.new(env)\n        Mal.Env.set(catch_env, exception, {:exception, message})\n        eval(catch_form, catch_env)\n    end\n  end\n  defp eval_try(_try_form, _catch_list, _env) do\n    throw({:error, \"catch* requires two arguments\"})\n  end\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, exception} ->\n      IO.puts(\"Error: #{Mal.Printer.print_str(exception)}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/lib/mix/tasks/stepA_mal.ex",
    "content": "defmodule Mix.Tasks.StepAMal do\n  import Mal.Types\n  alias Mal.Function\n\n  # for escript execution\n  def main(args) do\n    run(args)\n  end\n\n  def run(args) do\n    env = Mal.Env.new()\n    Mal.Env.merge(env, Mal.Core.namespace)\n    bootstrap(args, env)\n    loop(env)\n  end\n\n  defp load_file(file_name, env) do\n    read_eval_print(\"\"\"\n      (load-file \"#{file_name}\")\n      \"\"\", env)\n    exit(:normal)\n  end\n\n  defp bootstrap(args, env) do\n    # *host-language*\n    read_eval_print(\"(def! *host-language* \\\"Elixir\\\")\", env)\n\n    # not:\n    read_eval_print(\"\"\"\n      (def! not\n        (fn* (a) (if a false true)))\n      \"\"\", env)\n\n    # load-file:\n    read_eval_print(\"\"\"\n      (def! load-file\n        (fn* (f)\n          (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n      \"\"\", env)\n\n    # cond\n    read_eval_print(\"\"\"\n      (defmacro! cond\n        (fn* (& xs)\n          (if (> (count xs) 0)\n            (list 'if (first xs)\n              (if (> (count xs) 1)\n                (nth xs 1)\n                (throw \\\"odd number of forms to cond\\\"))\n              (cons 'cond (rest (rest xs)))))))\"\n      \"\"\", env)\n\n    Mal.Env.set(env, \"eval\", %Function{value: fn [ast] ->\n      eval(ast, env)\n    end})\n\n    case args do\n      [file_name | rest] ->\n        Mal.Env.set(env, \"*ARGV*\", list(rest))\n        load_file(file_name, env)\n\n      [] ->\n        Mal.Env.set(env, \"*ARGV*\", list([]))\n        read_eval_print(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", env)\n    end\n  end\n\n  defp loop(env) do\n    IO.write(:stdio, \"user> \")\n    IO.read(:stdio, :line)\n      |> read_eval_print(env)\n      |> IO.puts\n\n    loop(env)\n  end\n\n  defp eval_ast({:list, ast, meta}, env) when is_list(ast) do\n    eval_list(ast, env, meta)\n  end\n\n  defp eval_ast({:map, ast, meta}, env) do\n    map = for {key, value} <- ast, into: %{} do\n      {key, eval(value, env)}\n    end\n\n    {:map, map, meta}\n  end\n\n  defp eval_ast({:vector, ast, meta}, env) do\n    {:vector, Enum.map(ast, fn elem -> eval(elem, env) end), meta}\n  end\n\n  defp eval_ast({:symbol, symbol}, env) do\n    case Mal.Env.get(env, symbol) do\n      {:ok, value} -> value\n      :not_found -> throw({:error, \"'#{symbol}' not found\"})\n    end\n  end\n\n  defp eval_ast(ast, _env), do: ast\n\n  defp read(input) do\n    Mal.Reader.read_str(input)\n  end\n\n  defp eval_bindings([], env), do: env\n  defp eval_bindings([{:symbol, key}, binding | tail], env) do\n    evaluated = eval(binding, env)\n    Mal.Env.set(env, key, evaluated)\n    eval_bindings(tail, env)\n  end\n  defp eval_bindings(_bindings, _env), do: throw({:error, \"Unbalanced let* bindings\"})\n\n  defp quasiquote({:list, [{:symbol, \"unquote\"}, arg], _}), do: arg\n  defp quasiquote({:list, [{:symbol, \"unquote\"}|   _], _}), do: throw({:error, \"unquote: arg count\"})\n  defp quasiquote({:list,   xs, _}), do:                         qq_foldr(xs)\n  defp quasiquote({:vector, xs, _}), do: list([{:symbol, \"vec\"}, qq_foldr(xs)])\n  defp quasiquote({:symbol, sym}),    do: list([{:symbol, \"quote\"}, {:symbol, sym}])\n  defp quasiquote({:map, ast, meta}), do: list([{:symbol, \"quote\"}, {:map, ast, meta}])\n  defp quasiquote(ast), do: ast\n\n  defp qq_foldr([]),     do: list([])\n  defp qq_foldr([x|xs]), do: qq_loop(x, qq_foldr xs)\n\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}, arg], _}, acc), do: list([{:symbol, \"concat\"}, arg, acc])\n  defp qq_loop({:list, [{:symbol, \"splice-unquote\"}|   _], _},   _), do: throw({:error, \"splice-unquote: arg count\"})\n  defp qq_loop(elt, acc), do: list([{:symbol, \"cons\"}, quasiquote(elt), acc])\n\n  defp eval(ast, env) do\n    case Mal.Env.get(env, \"DEBUG-EVAL\") do\n      :not_found   -> :ok\n      {:ok, nil}   -> :ok\n      {:ok, false} -> :ok\n      _            -> IO.puts(\"EVAL: #{Mal.Printer.print_str(ast)}\")\n    end\n    eval_ast(ast, env)\n  end\n\n  defp eval_list([{:symbol, \"if\"}, condition, if_true | if_false], env, _) do\n    result = eval(condition, env)\n    if result == nil or result == false do\n      case if_false do\n        [] -> nil\n        [body] -> eval(body, env)\n      end\n    else\n      eval(if_true, env)\n    end\n  end\n\n  defp eval_list([{:symbol, \"do\"} | ast], env, _) do\n    ast\n      |> List.delete_at(-1)\n      |> Enum.map(fn elem -> eval(elem, env) end)\n    eval(List.last(ast), env)\n  end\n\n  defp eval_list([{:symbol, \"def!\"}, {:symbol, key}, value], env, _) do\n    evaluated = eval(value, env)\n    Mal.Env.set(env, key, evaluated)\n    evaluated\n  end\n\n  defp eval_list([{:symbol, \"defmacro!\"}, {:symbol, key}, function], env, _) do\n    macro = %{eval(function, env) | macro: true}\n    Mal.Env.set(env, key, macro)\n    macro\n  end\n\n  defp eval_list([{:symbol, \"let*\"}, {list_type, bindings, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    let_env = Mal.Env.new(env)\n    eval_bindings(bindings, let_env)\n    eval(body, let_env)\n  end\n\n  defp eval_list([{:symbol, \"fn*\"}, {list_type, params, _}, body], env, _)\n  when list_type == :list or list_type == :vector do\n    param_symbols = for {:symbol, symbol} <- params, do: symbol\n\n    closure = fn args ->\n      inner = Mal.Env.new(env, param_symbols, args)\n      eval(body, inner)\n    end\n\n    %Function{value: closure}\n  end\n\n  defp eval_list([{:symbol, \"quote\"}, arg], _env, _), do: arg\n\n  defp eval_list([{:symbol, \"quasiquote\"}, ast], env, _) do\n    ast |> quasiquote\n      |> eval(env)\n  end\n\n  # (try* A (catch* B C))\n  defp eval_list([{:symbol, \"try*\"}, try_form, {:list, catch_list, _meta}], env, _) do\n    eval_try(try_form, catch_list, env)\n  end\n  defp eval_list([{:symbol, \"try*\"}, try_form], env, _) do\n    eval(try_form, env)\n  end\n  defp eval_list([{:symbol, \"try*\"}, _try_form, _], _env, _) do\n    throw({:error, \"try* requires a list as the second parameter\"})\n  end\n\n  defp eval_list([a0 | args], env, _meta) do\n    func = eval(a0, env)\n    case func do\n      %Function{macro: true} -> func.value.(args) |> eval(env)\n      _ -> func.value.(Enum.map(args, fn elem -> eval(elem, env) end))\n    end\n  end\n\n  defp eval_list([], _env, meta), do: {:list, [], meta}\n\n  defp eval_try(try_form,\n  [{:symbol, \"catch*\"}, {:symbol, exception}, catch_form], env) do\n    try do\n      eval(try_form, env)\n    catch\n      {:error, message}->\n        catch_env = Mal.Env.new(env)\n        Mal.Env.set(catch_env, exception, {:exception, message})\n        eval(catch_form, catch_env)\n    end\n  end\n  defp eval_try(_try_form, _catch_list, _env) do\n    throw({:error, \"catch* requires two arguments\"})\n  end\n\n  defp print(value) do\n    Mal.Printer.print_str(value)\n  end\n\n  defp read_eval_print(:eof, _env), do: exit(:normal)\n  defp read_eval_print(line, env) do\n    read(line)\n      |> eval(env)\n      |> print\n  catch\n    {:error, exception} ->\n      IO.puts(\"Error: #{Mal.Printer.print_str(exception)}\")\n  end\nend\n"
  },
  {
    "path": "impls/elixir/mix.exs",
    "content": "defmodule Mal.Mixfile do\n  use Mix.Project\n\n  def project do\n    [app: :mal,\n     version: \"0.0.1\",\n     elixir: \"~> 1.5\",\n     build_embedded: Mix.env == :prod,\n     start_permanent: Mix.env == :prod,\n     deps: deps(),\n     default_task: \"stepA_mal\",\n     escript: escript()]\n  end\n\n  def escript do\n    [main_module: Mix.Tasks.StepAMal]\n  end\n\n  # Configuration for the OTP application\n  #\n  # Type `mix help compile.app` for more information\n  def application do\n    [applications: [:logger]]\n  end\n\n  # Dependencies can be Hex packages:\n  #\n  #   {:mydep, \"~> 0.3.0\"}\n  #\n  # Or git/path repositories:\n  #\n  #   {:mydep, git: \"https://github.com/elixir-lang/mydep.git\", tag: \"0.1.0\"}\n  #\n  # Type `mix help deps` for more examples and options\n  defp deps do\n    []\n  end\nend\n"
  },
  {
    "path": "impls/elixir/run",
    "content": "#!/usr/bin/env bash\ncd $(dirname $0)\nexec mix ${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/elixir/tests/step5_tco.mal",
    "content": ";; Elixir: skipping non-TCO recursion\n;; Reason: Elixir has TCO, test always completes.\n"
  },
  {
    "path": "impls/elm/.dockerignore",
    "content": "node_modules\n"
  },
  {
    "path": "impls/elm/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ libreadline-dev nodejs npm\n\nENV HOME /mal\nENV NPM_CONFIG_CACHE /mal/.npm\n"
  },
  {
    "path": "impls/elm/Makefile",
    "content": "SOURCES = src/Step0_repl.elm src/Step1_read_print.elm src/Step2_eval.elm \\\n\t  src/Step3_env.elm src/Step4_if_fn_do.elm src/Step5_tco.elm src/Step6_file.elm \\\n\t  src/Step7_quote.elm src/Step8_macros.elm src/Step9_try.elm src/StepA_mal.elm\n\nBINS = $(SOURCES:src/Step%.elm=step%.js)\n\nELM = node_modules/.bin/elm\n\nall: node_modules $(BINS)\n\nnode_modules:\n\tnpm install\n\nstep%.js: src/Step%.elm node_modules\n\t$(ELM) make $< --output $@\n\nSTEP0_SOURCES = src/IO.elm\nSTEP1_SOURCES = $(STEP0_SOURCES) src/Reader.elm src/Printer.elm src/Utils.elm src/Types.elm src/Env.elm\nSTEP2_SOURCES = $(STEP1_SOURCES)\nSTEP3_SOURCES = $(STEP2_SOURCES)\nSTEP4_SOURCES = $(STEP3_SOURCES) src/Core.elm src/Eval.elm\n\nstep0_repl.js: $(STEP0_SOURCES)\nstep1_read_print.js: $(STEP1_SOURCES)\nstep2_eval.js: $(STEP2_SOURCES)\nstep3_env.js: $(STEP3_SOURCES)\nstep4_if_fn_do.js: $(STEP4_SOURCES)\nstep5_tco.js: $(STEP4_SOURCES)\nstep6_file.js: $(STEP4_SOURCES)\nstep7_quote.js: $(STEP4_SOURCES)\nstep8_macros.js: $(STEP4_SOURCES)\nstep9_try.js: $(STEP4_SOURCES)\nstepA_mal.js: $(STEP4_SOURCES)\n\nclean:\n\trm -f $(BINS)\n"
  },
  {
    "path": "impls/elm/bootstrap.js",
    "content": "var readline = require('./node_readline');\nvar fs = require('fs');\n\n// The first two arguments are: 'node' and 'bootstrap.js'\n// The third argument is the name of the Elm module to load.\nvar args = process.argv.slice(2);\nvar mod = require('./' + args[0]);\n\nvar app = mod.Elm['S' + args[0].slice(1)].init({\n    flags: {\n        args: args.slice(1)\n    }\n});\n\n// Hook up the writeLine and readLine ports of the app.\napp.ports.writeLine.subscribe(function(line) {\n    console.log(line);\n    app.ports.input.send({\"tag\": \"lineWritten\"});\n});\n\napp.ports.readLine.subscribe(function(prompt) {\n    var line = readline.readline(prompt);\n    app.ports.input.send({\"tag\": \"lineRead\", \"line\": line});\n});\n\n// Read the contents of a file.\nif ('readFile' in app.ports) {\n    app.ports.readFile.subscribe(function(filename) {\n        try {\n            var contents = fs.readFileSync(filename, 'utf8');\n            app.ports.input.send({\"tag\": \"fileRead\", \"contents\": contents});\n        } catch (e) {\n            app.ports.input.send({\"tag\": \"exception\", \"message\": e.message});\n        }\n    });\n}\n"
  },
  {
    "path": "impls/elm/elm.json",
    "content": "{\n    \"type\": \"application\",\n    \"source-directories\": [\n        \"src\"\n    ],\n    \"elm-version\": \"0.19.1\",\n    \"dependencies\": {\n        \"direct\": {\n            \"elm/core\": \"1.0.5\",\n            \"elm/json\": \"1.1.3\",\n            \"elm/parser\": \"1.1.0\",\n            \"elm/regex\": \"1.0.0\",\n            \"elm/time\": \"1.0.0\"\n        },\n        \"indirect\": {}\n    },\n    \"test-dependencies\": {\n        \"direct\": {},\n        \"indirect\": {}\n    }\n}\n"
  },
  {
    "path": "impls/elm/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline.so.8\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit.so.2\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nvar koffi = require('koffi'),\n    fs = require('fs');\n\nvar rllib = null;\ntry {\n    rllib = koffi.load(RL_LIB);\n} catch (e) {\n    console.error('ERROR loading RL_LIB:', RL_LIB, e);\n    throw e;\n}\nvar readlineFunc = rllib.func('char *readline(char *)');\nvar addHistoryFunc = rllib.func('int add_history(char *)');\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = typeof prompt !== 'undefined' ? prompt : \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { addHistoryFunc(lines[i]); }\n        }\n    }\n\n    var line = readlineFunc(prompt);\n    if (line) {\n        addHistoryFunc(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\nvar readline = exports;\n\n"
  },
  {
    "path": "impls/elm/package.json",
    "content": "{\n  \"name\": \"mal-elm\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"bootstrap.js\",\n  \"dependencies\": {\n    \"koffi\": \"^2.12.1\"\n  },\n  \"devDependencies\": {\n    \"elm\": \"^0.19.1\"\n  },\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "impls/elm/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/bootstrap.js ${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/elm/src/Core.elm",
    "content": "module Core exposing (..)\n\nimport Array\nimport Dict\nimport Env\nimport Eval\nimport IO exposing (IO(..))\nimport Printer exposing (printString)\nimport Reader\nimport Task\nimport Time\nimport Types exposing (..)\nimport Utils exposing (zip)\n\n\nns : Env\nns =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        binaryOp fn retType args =\n            case args of\n                [ MalInt x, MalInt y ] ->\n                    Eval.succeed (retType (fn x y))\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        {- list -}\n        core_list =\n            Eval.succeed << MalList Nothing\n\n        {- list? -}\n        isList args =\n            case args of\n                [ MalList _ _ ] ->\n                    Eval.succeed (MalBool True)\n\n                _ ->\n                    Eval.succeed (MalBool False)\n\n        {- empty? -}\n        isEmpty args =\n            case args of\n                [ MalList _ list ] ->\n                    Eval.succeed <| MalBool (List.isEmpty list)\n\n                [ MalVector _ vec ] ->\n                    Eval.succeed <| MalBool (Array.isEmpty vec)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        {- count -}\n        count args =\n            case args of\n                [ MalNil ] ->\n                    Eval.succeed (MalInt 0)\n\n                [ MalList _ list ] ->\n                    Eval.succeed <| MalInt (List.length list)\n\n                [ MalVector _ vec ] ->\n                    Eval.succeed <| MalInt (Array.length vec)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        equalLists a b =\n            case ( a, b ) of\n                ( [], [] ) ->\n                    True\n\n                ( x :: xs, y :: ys ) ->\n                    if deepEquals (x, y) then\n                        equalLists xs ys\n\n                    else\n                        False\n\n                _ ->\n                    False\n\n        compareListTo list other =\n            case other of\n                MalList _ otherList ->\n                    equalLists list otherList\n\n                MalVector _ vec ->\n                    equalLists list (Array.toList vec)\n\n                _ ->\n                    False\n\n        equalMaps a b =\n            if Dict.keys a /= Dict.keys b then\n                False\n\n            else\n                zip (Dict.values a) (Dict.values b)\n                    |> List.map deepEquals\n                    |> List.all identity\n\n        deepEquals c =\n            case c of\n                ( MalList _ list, MalList _ otherList ) ->\n                    equalLists list otherList\n\n                ( MalList _ list, MalVector _ vec ) ->\n                    equalLists list (Array.toList vec)\n\n                ( MalList _ _, _ ) ->\n                    False\n\n                ( MalVector _ vec, MalList _ list ) ->\n                    equalLists (Array.toList vec) list\n\n                ( MalVector _ vec, MalVector _ otherVec ) ->\n                    equalLists (Array.toList vec) (Array.toList otherVec)\n\n                ( MalVector _ _, _ ) ->\n                    False\n\n                ( MalMap _ map, MalMap _ otherMap ) ->\n                    equalMaps map otherMap\n\n                ( MalMap _ _, _ ) ->\n                    False\n\n                ( _, MalMap _ _ ) ->\n                    False\n\n                (a, b) ->\n                    a == b\n\n        {- = -}\n        equals args =\n            case args of\n                [ a, b ] ->\n                    Eval.succeed <| MalBool (deepEquals (a, b))\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        {- pr-str -}\n        prStr args =\n            Eval.withEnv\n                (\\env ->\n                    args\n                        |> List.map (printString env True)\n                        |> String.join \" \"\n                        |> MalString\n                        |> Eval.succeed\n                )\n\n        {- str -}\n        core_str args =\n            Eval.withEnv\n                (\\env ->\n                    args\n                        |> List.map (printString env False)\n                        |> String.join \"\"\n                        |> MalString\n                        |> Eval.succeed\n                )\n\n        {- helper function to write a string to stdout -}\n        writeLine str =\n            Eval.io (IO.writeLine str)\n                (\\msg ->\n                    case msg of\n                        LineWritten ->\n                            Eval.succeed MalNil\n\n                        _ ->\n                            Eval.fail \"wrong IO, expected LineWritten\"\n                )\n\n        prn args =\n            Eval.withEnv\n                (\\env ->\n                    args\n                        |> List.map (printString env True)\n                        |> String.join \" \"\n                        |> writeLine\n                )\n\n        println args =\n            Eval.withEnv\n                (\\env ->\n                    args\n                        |> List.map (printString env False)\n                        |> String.join \" \"\n                        |> writeLine\n                )\n\n        printEnv args =\n            case args of\n                [] ->\n                    Eval.withEnv (Printer.printEnv >> writeLine)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        readString args =\n            case args of\n                [ MalString str ] ->\n                    case Reader.readString str of\n                        Ok ast ->\n                            Eval.succeed ast\n\n                        Err msg ->\n                            Eval.fail msg\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        slurp args =\n            case args of\n                [ MalString filename ] ->\n                    Eval.io (IO.readFile filename)\n                        (\\msg ->\n                            case msg of\n                                FileRead contents ->\n                                    Eval.succeed <| MalString contents\n\n                                Exception errMsg ->\n                                    Eval.fail errMsg\n\n                                _ ->\n                                    Eval.fail \"wrong IO, expected FileRead\"\n                        )\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        atom args =\n            case args of\n                [ value ] ->\n                    Eval.withEnv\n                        (\\env ->\n                            case Env.newAtom value env of\n                                ( newEnv, atomId ) ->\n                                    Eval.setEnv newEnv\n                                        |> Eval.map (\\_ -> MalAtom atomId)\n                        )\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        isAtom args =\n            case args of\n                [ MalAtom _ ] ->\n                    Eval.succeed <| MalBool True\n\n                _ ->\n                    Eval.succeed <| MalBool False\n\n        deref args =\n            case args of\n                [ MalAtom atomId ] ->\n                    Eval.withEnv (Env.getAtom atomId >> Eval.succeed)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        reset args =\n            case args of\n                [ MalAtom atomId, value ] ->\n                    Eval.modifyEnv (Env.setAtom atomId value)\n                        |> Eval.map (always value)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        {- helper function for calling a core or user function -}\n        callFn func args =\n            case func of\n                CoreFunc _ fn ->\n                    fn args\n\n                UserFunc { eagerFn } ->\n                    eagerFn args\n\n        swap args =\n            case args of\n                (MalAtom atomId) :: (MalFunction func) :: moreArgs ->\n                    Eval.withEnv\n                        (\\env ->\n                            let\n                                value =\n                                    Env.getAtom atomId env\n                            in\n                            callFn func (value :: moreArgs)\n                        )\n                        |> Eval.andThen\n                            (\\res ->\n                                Eval.modifyEnv (Env.setAtom atomId res)\n                                    |> Eval.map (always res)\n                            )\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        gc args =\n            Eval.withEnv (Env.gc MalNil >> Printer.printEnv >> writeLine)\n\n        setDebug enabled =\n            Eval.modifyEnv\n                (\\env ->\n                    { env | debug = enabled }\n                )\n                |> Eval.andThen (\\_ -> Eval.succeed MalNil)\n\n        debug args =\n            case args of\n                [ MalBool value ] ->\n                    setDebug value\n\n                _ ->\n                    Eval.withEnv\n                        (\\env ->\n                            Eval.succeed (MalBool env.debug)\n                        )\n\n        typeof args =\n            case args of\n                [ MalInt _ ] ->\n                    Eval.succeed <| MalSymbol \"int\"\n\n                [ MalBool _ ] ->\n                    Eval.succeed <| MalSymbol \"bool\"\n\n                [ MalString _ ] ->\n                    Eval.succeed <| MalSymbol \"string\"\n\n                [ MalKeyword _ ] ->\n                    Eval.succeed <| MalSymbol \"keyword\"\n\n                [ MalSymbol _ ] ->\n                    Eval.succeed <| MalSymbol \"symbol\"\n\n                [ MalNil ] ->\n                    Eval.succeed <| MalSymbol \"nil\"\n\n                [ MalList _ _ ] ->\n                    Eval.succeed <| MalSymbol \"vector\"\n\n                [ MalVector _ _ ] ->\n                    Eval.succeed <| MalSymbol \"vector\"\n\n                [ MalMap _ _ ] ->\n                    Eval.succeed <| MalSymbol \"vector\"\n\n                [ MalFunction _ ] ->\n                    Eval.succeed <| MalSymbol \"function\"\n\n                [ MalAtom _ ] ->\n                    Eval.succeed <| MalSymbol \"atom\"\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        cons args =\n            case args of\n                [ e, MalList _ list ] ->\n                    Eval.succeed <| MalList Nothing (e :: list)\n\n                [ e, MalVector _ vec ] ->\n                    Eval.succeed <| MalList Nothing (e :: (Array.toList vec))\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        concat args =\n            let\n                go arg acc =\n                    case arg of\n                        MalList _ list ->\n                            Eval.succeed (acc ++ list)\n\n                        MalVector _ vec ->\n                            Eval.succeed (acc ++ Array.toList vec)\n\n                        _ ->\n                            Eval.fail \"unsupported arguments\"\n            in\n            List.foldl (go >> Eval.andThen) (Eval.succeed []) args\n                |> Eval.map (MalList Nothing)\n\n        core_vec args =\n            case args of\n                [MalVector _ xs] -> Eval.succeed <| MalVector Nothing xs\n                [MalList _   xs] -> Eval.succeed <| MalVector Nothing <| Array.fromList xs\n                [_]            -> Eval.fail \"vec: arg type\"\n                _              -> Eval.fail \"vec: arg count\"\n\n        nth args =\n            let\n                get list index =\n                    if index < 0 then\n                        Nothing\n\n                    else if index == 0 then\n                        List.head list\n\n                    else\n                        case list of\n                            [] ->\n                                Nothing\n\n                            _ :: rest ->\n                                get rest (index - 1)\n\n                make res =\n                    case res of\n                        Just value ->\n                            Eval.succeed value\n\n                        Nothing ->\n                            Eval.fail \"index out of bounds\"\n            in\n            case args of\n                [ MalList _ list, MalInt index ] ->\n                    make <| get list index\n\n                [ MalVector _ vec, MalInt index ] ->\n                    make <| Array.get index vec\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        first args =\n            let\n                make =\n                    Eval.succeed << Maybe.withDefault MalNil\n            in\n            case args of\n                [ MalNil ] ->\n                    Eval.succeed MalNil\n\n                [ MalList _ list ] ->\n                    make <| List.head list\n\n                [ MalVector _ vec ] ->\n                    make <| Array.get 0 vec\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        core_rest args =\n            case args of\n                [ MalNil ] ->\n                    Eval.succeed <| MalList Nothing []\n\n                [ MalList _ [] ] ->\n                    Eval.succeed <| MalList Nothing []\n\n                [ MalList _ (head :: tail) ] ->\n                    Eval.succeed <| MalList Nothing tail\n\n                [ MalVector _ vec ] ->\n                    Array.toList vec\n                        |> List.tail\n                        |> Maybe.withDefault []\n                        |> MalList Nothing\n                        |> Eval.succeed\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        throw args =\n            case args of\n                ex :: _ ->\n                    Eval.throw ex\n\n                _ ->\n                    Eval.fail \"undefined exception\"\n\n        apply args =\n            case args of\n                (MalFunction func) :: rest ->\n                    case List.reverse rest of\n                        (MalList _ last) :: middle ->\n                            callFn func ((List.reverse middle) ++ last)\n\n                        (MalVector _ last) :: middle ->\n                            callFn func\n                                ((List.reverse middle)\n                                    ++ (Array.toList last)\n                                )\n\n                        _ ->\n                            Eval.fail \"apply expected the last argument to be a list or vector\"\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        core_map args =\n            let\n                go func list acc =\n                    case list of\n                        [] ->\n                            Eval.succeed <| MalList Nothing <| List.reverse acc\n\n                        inv :: rest ->\n                            callFn func [ inv ]\n                                |> Eval.andThen\n                                    (\\outv ->\n                                        Eval.pushRef outv (go func rest (outv :: acc))\n                                    )\n            in\n            case args of\n                [ MalFunction func, MalList _ list ] ->\n                    Eval.withStack (go func list [])\n\n                [ MalFunction func, MalVector _ vec ] ->\n                    go func (Array.toList vec) []\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        isNil args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        MalNil :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isTrue args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalBool True) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isFalse args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalBool False) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isNumber args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalInt _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isSymbol args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalSymbol _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isKeyword args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalKeyword _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isVector args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalVector _ _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isMap args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalMap _ _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isString args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalString _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isSequential args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalList _ _) :: _ ->\n                            True\n\n                        (MalVector _ _) :: _ ->\n                            True\n\n                        _ ->\n                            False\n\n        isFn args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalFunction (CoreFunc _ _)) :: _ ->\n                            True\n                        (MalFunction (UserFunc fn)) :: _ ->\n                            not fn.isMacro\n\n                        _ ->\n                            False\n\n        isMacro args =\n            Eval.succeed <|\n                MalBool <|\n                    case args of\n                        (MalFunction (UserFunc fn)) :: _ ->\n                            fn.isMacro\n\n                        _ ->\n                            False\n\n        symbol args =\n            case args of\n                [ MalString str ] ->\n                    Eval.succeed <| MalSymbol str\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        core_keyword args =\n            case args of\n                [ MalString str ] ->\n                    Eval.succeed <| MalKeyword str\n\n                [ (MalKeyword _) as kw ] ->\n                    Eval.succeed kw\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        vector args =\n            Eval.succeed <| MalVector Nothing <| Array.fromList args\n\n        parseKey key =\n            case key of\n                MalString str ->\n                    Ok str\n\n                MalKeyword keyword ->\n                    Ok <| String.cons keywordPrefix keyword\n\n                _ ->\n                    Err \"map key must be a symbol or keyword\"\n\n        buildMap list acc =\n            case list of\n                [] ->\n                    Eval.succeed <| MalMap Nothing acc\n\n                key :: value :: rest ->\n                    parseKey key\n                        |> Eval.fromResult\n                        |> Eval.andThen\n                            (\\k ->\n                                buildMap rest (Dict.insert k value acc)\n                            )\n\n                _ ->\n                    Eval.fail \"expected an even number of key-value pairs\"\n\n        hashMap args =\n            buildMap args Dict.empty\n\n        assoc args =\n            case args of\n                (MalMap _ dict) :: rest ->\n                    buildMap rest dict\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        dissoc args =\n            let\n                go keys acc =\n                    case keys of\n                        [] ->\n                            Eval.succeed <| MalMap Nothing acc\n\n                        key :: rest ->\n                            parseKey key\n                                |> Eval.fromResult\n                                |> Eval.andThen\n                                    (\\k ->\n                                        go rest (Dict.remove k acc)\n                                    )\n            in\n            case args of\n                (MalMap _ dict) :: keys ->\n                    go keys dict\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        core_get args =\n            case args of\n                [ MalNil, key ] ->\n                    Eval.succeed MalNil\n\n                [ MalMap _ dict, key ] ->\n                    parseKey key\n                        |> Eval.fromResult\n                        |> Eval.map\n                            (\\k ->\n                                Dict.get k dict\n                                    |> Maybe.withDefault MalNil\n                            )\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        contains args =\n            case args of\n                [ MalMap _ dict, key ] ->\n                    parseKey key\n                        |> Eval.fromResult\n                        |> Eval.map (\\k -> Dict.member k dict)\n                        |> Eval.map MalBool\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        unparseKey key =\n            case String.uncons key of\n                Just ( prefix, rest ) ->\n                    if prefix == keywordPrefix then\n                        MalKeyword rest\n\n                    else\n                        MalString key\n\n                _ ->\n                    MalString key\n\n        core_keys args =\n            case args of\n                [ MalMap _ dict ] ->\n                    Dict.keys dict\n                        |> List.map unparseKey\n                        |> MalList Nothing\n                        |> Eval.succeed\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        vals args =\n            case args of\n                [ MalMap _ dict ] ->\n                    Dict.values dict\n                        |> MalList Nothing\n                        |> Eval.succeed\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        readLine args =\n            case args of\n                [ MalString prompt ] ->\n                    Eval.io (IO.readLine prompt)\n                        (\\msg ->\n                            case msg of\n                                LineRead (Just line) ->\n                                    Eval.succeed (MalString line)\n\n                                LineRead Nothing ->\n                                    Eval.succeed MalNil\n\n                                _ ->\n                                    Eval.fail \"wrong IO, expected LineRead\"\n                        )\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        withMeta args =\n            case args of\n                [ MalFunction (UserFunc func), meta ] ->\n                    Eval.succeed <| MalFunction <| UserFunc { func | meta = Just meta }\n\n                [ MalList _ xs, meta ] ->\n                    Eval.succeed <| MalList (Just meta) xs\n\n                [ MalVector _ xs, meta ] ->\n                    Eval.succeed <| MalVector (Just meta) xs\n\n                [ MalMap _ map, meta ] ->\n                    Eval.succeed <| MalMap (Just meta) map\n\n                [ MalFunction (CoreFunc _ f), meta ] ->\n                    Eval.succeed <| MalFunction (CoreFunc (Just meta) f)\n\n                _ ->\n                    Eval.fail \"with-meta expected a user function and a map\"\n\n        core_meta args =\n            case args of\n                [ MalFunction (UserFunc { meta }) ] ->\n                    Eval.succeed (Maybe.withDefault MalNil meta)\n\n                [ MalFunction (CoreFunc meta f) ] ->\n                    Eval.succeed (Maybe.withDefault MalNil meta)\n\n                [ MalList meta _ ] ->\n                    Eval.succeed (Maybe.withDefault MalNil meta)\n\n                [ MalVector meta _ ] ->\n                    Eval.succeed (Maybe.withDefault MalNil meta)\n\n                [ MalMap meta _ ] ->\n                    Eval.succeed (Maybe.withDefault MalNil meta)\n\n                _ ->\n                    Eval.succeed MalNil\n\n        conj args =\n            case args of\n                (MalList _ list) :: rest ->\n                    Eval.succeed <|\n                        MalList Nothing <|\n                            List.reverse rest\n                                ++ list\n\n                (MalVector _ vec) :: rest ->\n                    Eval.succeed <|\n                        MalVector Nothing <|\n                            Array.append\n                                vec\n                                (Array.fromList rest)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        seq args =\n            case args of\n                [ MalNil ] ->\n                    Eval.succeed MalNil\n\n                [ MalList _ [] ] ->\n                    Eval.succeed MalNil\n\n                [ MalString \"\" ] ->\n                    Eval.succeed MalNil\n\n                [ MalList _ xs ] ->\n                    Eval.succeed (MalList Nothing xs)\n\n                [ MalVector _ vec ] ->\n                    Eval.succeed <|\n                        if Array.isEmpty vec then\n                            MalNil\n\n                        else\n                            MalList Nothing <| Array.toList vec\n\n                [ MalString str ] ->\n                    Eval.succeed <|\n                        MalList Nothing <|\n                            (String.toList str\n                                |> List.map String.fromChar\n                                |> List.map MalString\n                            )\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        requestTime =\n            Task.perform (GotTime >> Ok >> Input) Time.now\n\n        timeMs args =\n            case args of\n                [] ->\n                    Eval.io requestTime\n                        (\\msg ->\n                            case msg of\n                                GotTime time ->\n                                    Time.posixToMillis time\n                                        |> MalInt\n                                        |> Eval.succeed\n\n                                _ ->\n                                    Eval.fail \"wrong IO, expected GotTime\"\n                        )\n\n                _ ->\n                    Eval.fail \"time-ms takes no arguments\"\n    in\n    Env.global\n        |> Env.set \"+\" (makeFn <| binaryOp (+) MalInt)\n        |> Env.set \"-\" (makeFn <| binaryOp (-) MalInt)\n        |> Env.set \"*\" (makeFn <| binaryOp (*) MalInt)\n        |> Env.set \"/\" (makeFn <| binaryOp (//) MalInt)\n        |> Env.set \"<\" (makeFn <| binaryOp (<) MalBool)\n        |> Env.set \">\" (makeFn <| binaryOp (>) MalBool)\n        |> Env.set \"<=\" (makeFn <| binaryOp (<=) MalBool)\n        |> Env.set \">=\" (makeFn <| binaryOp (>=) MalBool)\n        |> Env.set \"list\" (makeFn core_list)\n        |> Env.set \"list?\" (makeFn isList)\n        |> Env.set \"empty?\" (makeFn isEmpty)\n        |> Env.set \"count\" (makeFn count)\n        |> Env.set \"=\" (makeFn equals)\n        |> Env.set \"pr-str\" (makeFn prStr)\n        |> Env.set \"str\" (makeFn core_str)\n        |> Env.set \"prn\" (makeFn prn)\n        |> Env.set \"println\" (makeFn println)\n        |> Env.set \"pr-env\" (makeFn printEnv)\n        |> Env.set \"read-string\" (makeFn readString)\n        |> Env.set \"slurp\" (makeFn slurp)\n        |> Env.set \"atom\" (makeFn atom)\n        |> Env.set \"atom?\" (makeFn isAtom)\n        |> Env.set \"deref\" (makeFn deref)\n        |> Env.set \"reset!\" (makeFn reset)\n        |> Env.set \"swap!\" (makeFn swap)\n        |> Env.set \"gc\" (makeFn gc)\n        |> Env.set \"debug!\" (makeFn debug)\n        |> Env.set \"typeof\" (makeFn typeof)\n        |> Env.set \"cons\" (makeFn cons)\n        |> Env.set \"concat\" (makeFn concat)\n        |> Env.set \"vec\" (makeFn core_vec)\n        |> Env.set \"nth\" (makeFn nth)\n        |> Env.set \"first\" (makeFn first)\n        |> Env.set \"rest\" (makeFn core_rest)\n        |> Env.set \"throw\" (makeFn throw)\n        |> Env.set \"apply\" (makeFn apply)\n        |> Env.set \"map\" (makeFn core_map)\n        |> Env.set \"nil?\" (makeFn isNil)\n        |> Env.set \"true?\" (makeFn isTrue)\n        |> Env.set \"false?\" (makeFn isFalse)\n        |> Env.set \"number?\" (makeFn isNumber)\n        |> Env.set \"symbol?\" (makeFn isSymbol)\n        |> Env.set \"keyword?\" (makeFn isKeyword)\n        |> Env.set \"vector?\" (makeFn isVector)\n        |> Env.set \"map?\" (makeFn isMap)\n        |> Env.set \"string?\" (makeFn isString)\n        |> Env.set \"sequential?\" (makeFn isSequential)\n        |> Env.set \"fn?\" (makeFn isFn)\n        |> Env.set \"macro?\" (makeFn isMacro)\n        |> Env.set \"symbol\" (makeFn symbol)\n        |> Env.set \"keyword\" (makeFn core_keyword)\n        |> Env.set \"vector\" (makeFn vector)\n        |> Env.set \"hash-map\" (makeFn hashMap)\n        |> Env.set \"assoc\" (makeFn assoc)\n        |> Env.set \"dissoc\" (makeFn dissoc)\n        |> Env.set \"get\" (makeFn core_get)\n        |> Env.set \"contains?\" (makeFn contains)\n        |> Env.set \"keys\" (makeFn core_keys)\n        |> Env.set \"vals\" (makeFn vals)\n        |> Env.set \"readline\" (makeFn readLine)\n        |> Env.set \"with-meta\" (makeFn withMeta)\n        |> Env.set \"meta\" (makeFn core_meta)\n        |> Env.set \"conj\" (makeFn conj)\n        |> Env.set \"seq\" (makeFn seq)\n        |> Env.set \"time-ms\" (makeFn timeMs)\n"
  },
  {
    "path": "impls/elm/src/Env.elm",
    "content": "module Env exposing\n    ( debug\n    , enter\n    , gc\n    , get\n    , getAtom\n    , global\n    , globalFrameId\n    , leave\n    , newAtom\n    , pop\n    , push\n    , pushRef\n    , ref\n    , restoreRefs\n    , set\n    , setAtom\n    )\n\nimport Array\nimport Dict\nimport Set\nimport Types exposing (Env, Frame, MalExpr(..), MalFunction(..))\nimport Utils exposing (flip)\n\n\ndebug : Env -> String -> a -> a\ndebug env msg value =\n    if env.debug then\n        Debug.log msg value\n\n    else\n        value\n\n\nglobalFrameId : Int\nglobalFrameId =\n    0\n\n\ndefaultGcInterval : Int\ndefaultGcInterval =\n    10\n\n\nglobal : Env\nglobal =\n    { frames = Dict.singleton globalFrameId (emptyFrame Nothing Nothing)\n    , nextFrameId = globalFrameId + 1\n    , currentFrameId = globalFrameId\n    , atoms = Dict.empty\n    , nextAtomId = 0\n    , debug = False\n    , gcInterval = defaultGcInterval\n    , gcCounter = 0\n    , stack = []\n    , keepFrames = []\n    }\n\n\ngetFrame : Env -> Int -> Frame\ngetFrame env frameId =\n    case Dict.get frameId env.frames of\n        Just frame ->\n            frame\n\n        Nothing ->\n            Debug.todo <| \"frame #\" ++ String.fromInt frameId ++ \" not found\"\n\n\nemptyFrame : Maybe Int -> Maybe Int -> Frame\nemptyFrame outerId exitId =\n    { outerId = outerId\n    , exitId = exitId\n    , data = Dict.empty\n    , refCnt = 1\n    }\n\n\nset : String -> MalExpr -> Env -> Env\nset name expr env =\n    let\n        frameId =\n            env.currentFrameId\n\n        updateFrame =\n            Maybe.map\n                (\\frame ->\n                    { frame | data = Dict.insert name expr frame.data }\n                )\n\n        newFrames =\n            Dict.update frameId updateFrame env.frames\n    in\n    { env | frames = newFrames }\n\n\nget : String -> Env -> Result String MalExpr\nget name env =\n    let\n        go frameId =\n            let\n                frame =\n                    getFrame env frameId\n            in\n            case Dict.get name frame.data of\n                Just value ->\n                    Ok value\n\n                Nothing ->\n                    frame.outerId\n                        |> Maybe.map go\n                        |> Maybe.withDefault (Err <| \"'\" ++ name ++ \"' not found\")\n    in\n    go env.currentFrameId\n\n\nnewAtom : MalExpr -> Env -> ( Env, Int )\nnewAtom value env =\n    let\n        atomId =\n            env.nextAtomId\n\n        newEnv =\n            { env\n                | atoms = Dict.insert atomId value env.atoms\n                , nextAtomId = atomId + 1\n            }\n    in\n    ( newEnv, atomId )\n\n\ngetAtom : Int -> Env -> MalExpr\ngetAtom atomId env =\n    case Dict.get atomId env.atoms of\n        Just value ->\n            value\n\n        Nothing ->\n            Debug.todo <| \"atom \" ++ String.fromInt atomId ++ \" not found\"\n\n\nsetAtom : Int -> MalExpr -> Env -> Env\nsetAtom atomId value env =\n    { env\n        | atoms = Dict.insert atomId value env.atoms\n    }\n\n\npush : Env -> Env\npush env =\n    let\n        frameId =\n            env.nextFrameId\n\n        newFrame =\n            emptyFrame (Just env.currentFrameId) Nothing\n\n        bogus =\n            debug env \"push\" frameId\n    in\n    { env\n        | currentFrameId = frameId\n        , frames = Dict.insert frameId newFrame env.frames\n        , nextFrameId = env.nextFrameId + 1\n    }\n\n\npop : Env -> Env\npop env =\n    let\n        frameId =\n            env.currentFrameId\n\n        frame =\n            getFrame env frameId\n\n        bogus =\n            debug env \"pop\" frameId\n    in\n    case frame.outerId of\n        Just outerId ->\n            { env\n                | currentFrameId = outerId\n                , frames = Dict.update frameId free env.frames\n            }\n\n        _ ->\n            Debug.todo \"tried to pop global frame\"\n\n\nsetBinds : List ( String, MalExpr ) -> Frame -> Frame\nsetBinds binds frame =\n    case binds of\n        [] ->\n            frame\n\n        ( name, expr ) :: rest ->\n            setBinds rest\n                { frame | data = Dict.insert name expr frame.data }\n\n\n{-| Enter a new frame with a set of binds\n-}\nenter : Int -> List ( String, MalExpr ) -> Env -> Env\nenter outerId binds env =\n    let\n        frameId =\n            debug env \"enter #\" env.nextFrameId\n\n        exitId =\n            env.currentFrameId\n\n        newFrame =\n            setBinds binds (emptyFrame (Just outerId) (Just exitId))\n    in\n    { env\n        | currentFrameId = frameId\n        , frames = Dict.insert frameId newFrame env.frames\n        , nextFrameId = env.nextFrameId + 1\n    }\n\n\nleave : Env -> Env\nleave env =\n    let\n        frameId =\n            debug env \"leave #\" env.currentFrameId\n\n        frame =\n            getFrame env frameId\n\n        exitId =\n            case frame.exitId of\n                Just exitId2 ->\n                    exitId2\n\n                Nothing ->\n                    Debug.todo <|\n                        \"frame #\"\n                            ++ String.fromInt frameId\n                            ++ \" doesn't have an exitId\"\n    in\n    { env\n        | currentFrameId = exitId\n        , frames =\n            env.frames\n                |> Dict.insert frameId { frame | exitId = Nothing }\n                |> Dict.update frameId free\n    }\n\n\n{-| Increase refCnt for the current frame,\nand all it's parent frames.\n-}\nref : Env -> Env\nref originalEnv =\n    let\n        go frameId env =\n            let\n                frame =\n                    getFrame env frameId\n\n                newFrame =\n                    { frame | refCnt = frame.refCnt + 1 }\n\n                newEnv =\n                    { env | frames = Dict.insert frameId newFrame env.frames }\n            in\n            case frame.outerId of\n                Just outerId ->\n                    go outerId newEnv\n\n                Nothing ->\n                    newEnv\n\n        newEnv2 =\n            go originalEnv.currentFrameId originalEnv\n    in\n    { newEnv2 | gcCounter = newEnv2.gcCounter + 1 }\n\n\nfree : Maybe Frame -> Maybe Frame\nfree =\n    Maybe.andThen\n        (\\frame ->\n            if frame.refCnt == 1 then\n                Nothing\n\n            else\n                Just { frame | refCnt = frame.refCnt - 1 }\n        )\n\n\npushRef : MalExpr -> Env -> Env\npushRef ref_arg env =\n    { env | stack = ref_arg :: env.stack }\n\n\nrestoreRefs : List MalExpr -> Env -> Env\nrestoreRefs refs env =\n    { env | stack = refs }\n\n\n{-| Given an Env see which frames are not reachable from the\nglobal frame, or from the current expression.\n\nReturn a new Env with the unreachable frames removed.\n\n-}\ngc : MalExpr -> Env -> Env\ngc expr env =\n    let\n        countList acc =\n            List.foldl countExpr acc\n\n        countFrame { data } acc =\n            data |> Dict.values |> countList acc\n\n        recur frameId acc =\n            if not (Set.member frameId acc) then\n                let\n                    frame =\n                        getFrame env frameId\n\n                    newAcc =\n                        Set.insert frameId acc\n                in\n                countFrame frame newAcc\n\n            else\n                acc\n\n        countBound bound acc =\n            bound\n                |> List.map Tuple.second\n                |> countList acc\n\n        countExpr expr_arg acc =\n            case expr_arg of\n                MalFunction (UserFunc { frameId }) ->\n                    recur frameId acc\n\n                MalApply { frameId, bound } ->\n                    recur frameId acc\n                        |> countBound bound\n\n                MalList _ list ->\n                    countList acc list\n\n                MalVector _ vec ->\n                    countList acc (Array.toList vec)\n\n                MalMap _ map ->\n                    countList acc (Dict.values map)\n\n                MalAtom atomId ->\n                    let\n                        value =\n                            getAtom atomId env\n                    in\n                    countExpr value acc\n\n                _ ->\n                    acc\n\n        initSet =\n            Set.fromList\n                ([ globalFrameId, env.currentFrameId ]\n                    ++ env.keepFrames\n                )\n\n        countFrames frames acc =\n            Set.toList frames\n                |> List.map (getFrame env)\n                |> List.foldl countFrame acc\n\n        expand frameId frame fn acc =\n            case fn frame of\n                Nothing ->\n                    acc\n\n                Just parentId ->\n                    Set.insert parentId acc\n\n        expandBoth frameId =\n            let\n                frame =\n                    getFrame env frameId\n            in\n            expand frameId frame .outerId\n                >> expand frameId frame .exitId\n\n        expandParents frames =\n            Set.foldl expandBoth frames frames\n\n        loop acc =\n            let\n                newAcc =\n                    expandParents acc\n\n                newParents =\n                    Set.diff newAcc acc\n            in\n            if Set.isEmpty newParents then\n                newAcc\n\n            else\n                loop <| countFrames newParents newAcc\n\n        makeNewEnv newFrames =\n            { env\n                | frames = newFrames\n                , gcCounter = 0\n            }\n\n        keepFilter keep frameId _ =\n            Set.member frameId keep\n\n        filterFrames frames keep =\n            Dict.filter (keepFilter keep) frames\n    in\n    countFrames initSet initSet\n        |> countExpr expr\n        |> flip countList env.stack\n        |> loop\n        |> filterFrames env.frames\n        |> makeNewEnv\n"
  },
  {
    "path": "impls/elm/src/Eval.elm",
    "content": "module Eval exposing (..)\n\nimport Env\nimport IO exposing (IO)\nimport Types exposing (..)\n\n\napply : Eval a -> Env -> EvalContext a\napply f env =\n    f env\n\n\nrun : Env -> Eval a -> EvalContext a\nrun env e =\n    apply e env\n\n\nwithEnv : (Env -> Eval a) -> Eval a\nwithEnv f env =\n    apply (f env) env\n\n\nsetEnv : Env -> Eval ()\nsetEnv env _ =\n    apply (succeed ()) env\n\n\nmodifyEnv : (Env -> Env) -> Eval ()\nmodifyEnv f env =\n    apply (succeed ()) (f env)\n\n\nsucceed : a -> Eval a\nsucceed res env =\n    ( env, EvalOk res )\n\n\nio : Cmd Msg -> (IO -> Eval a) -> Eval a\nio cmd cont env =\n    ( env, EvalIO cmd cont )\n\n\nmap : (a -> b) -> Eval a -> Eval b\nmap f e env0 =\n    case apply e env0 of\n        ( env, EvalOk res ) ->\n            ( env, EvalOk (f res) )\n\n        ( env, EvalErr msg ) ->\n            ( env, EvalErr msg )\n\n        ( env, EvalIO cmd cont ) ->\n            ( env, EvalIO cmd (cont >> map f) )\n\n\n{-| Chain two Eval's together. The function f takes the result from\nthe left eval and generates a new Eval.\n-}\nandThen : (a -> Eval b) -> Eval a -> Eval b\nandThen f e env0 =\n    case apply e env0 of\n        ( env, EvalOk res ) ->\n            apply (f res) env\n\n        ( env, EvalErr msg ) ->\n            ( env, EvalErr msg )\n\n        ( env, EvalIO cmd cont ) ->\n            ( env, EvalIO cmd (cont >> andThen f) )\n\n\n{-| Apply a transformation to the Env, for a Ok and a Err.\n-}\nfinally : (Env -> Env) -> Eval a -> Eval a\nfinally f e env0 =\n    case apply e env0 of\n        ( env, EvalOk res ) ->\n            ( f env, EvalOk res )\n\n        ( env, EvalErr msg ) ->\n            ( f env, EvalErr msg )\n\n        ( env, EvalIO cmd cont ) ->\n            ( env, EvalIO cmd (cont >> finally f) )\n\n\ngcPass : Eval MalExpr -> Eval MalExpr\ngcPass e env0 =\n    let\n        go env t expr =\n            if env.gcCounter >= env.gcInterval then\n                --Debug.log\n                --    (\"before GC: \"\n                --        ++ (printEnv env)\n                --    )\n                --    \"\"\n                --    |> always ( Env.gc env, t expr )\n                ( Env.gc expr env, t expr )\n\n            else\n                ( env, t expr )\n    in\n    case apply e env0 of\n        ( env, EvalOk res ) ->\n            go env EvalOk res\n\n        ( env, EvalErr msg ) ->\n            go env EvalErr msg\n\n        ( env, EvalIO cmd cont ) ->\n            ( env, EvalIO cmd (cont >> gcPass) )\n\n\ncatchError : (MalExpr -> Eval a) -> Eval a -> Eval a\ncatchError f e env0 =\n    case apply e env0 of\n        ( env, EvalOk res ) ->\n            ( env, EvalOk res )\n\n        ( env, EvalErr msg ) ->\n            apply (f msg) env\n\n        ( env, EvalIO cmd cont ) ->\n            ( env, EvalIO cmd (cont >> catchError f) )\n\n\nfail : String -> Eval a\nfail msg env =\n    ( env, EvalErr <| MalString msg )\n\n\nthrow : MalExpr -> Eval a\nthrow ex env =\n    ( env, EvalErr ex )\n\n\n{-| Apply f to expr repeatedly.\nContinues iterating if f returns (Left eval).\nStops if f returns (Right expr).\n\nTail call optimized.\n\n-}\nrunLoop : (MalExpr -> Env -> Either (Eval MalExpr) MalExpr) -> MalExpr -> Eval MalExpr\nrunLoop f expr0 env0 =\n    case f expr0 env0 of\n        Left e ->\n            case apply e env0 of\n                ( env, EvalOk expr ) ->\n                    runLoop f expr env\n\n                ( env, EvalErr msg ) ->\n                    ( env, EvalErr msg )\n\n                ( env, EvalIO cmd cont ) ->\n                    ( env, EvalIO cmd (cont >> andThen (runLoop f)) )\n\n        Right expr ->\n            ( env0, EvalOk expr )\n\n\nfromResult : Result String a -> Eval a\nfromResult res =\n    case res of\n        Ok val ->\n            succeed val\n\n        Err msg ->\n            fail msg\n\n\n{-| Chain the left and right Eval but ignore the right's result.\n-}\nignore : Eval b -> Eval a -> Eval a\nignore right left =\n    left\n        |> andThen\n            (\\res ->\n                right\n                    |> andThen (\\_ -> succeed res)\n            )\n\n\nwithStack : Eval a -> Eval a\nwithStack e =\n    withEnv\n        (\\env ->\n            e\n                |> ignore\n                    (modifyEnv\n                        (Env.restoreRefs env.stack)\n                    )\n        )\n\n\npushRef : MalExpr -> Eval a -> Eval a\npushRef ref e =\n    modifyEnv (Env.pushRef ref)\n        |> andThen (always e)\n\n\ninGlobal : Eval a -> Eval a\ninGlobal body =\n    let\n        enter env =\n            setEnv\n                { env\n                    | keepFrames = env.currentFrameId :: env.keepFrames\n                    , currentFrameId = Env.globalFrameId\n                }\n\n        leave oldEnv newEnv =\n            { newEnv\n                | keepFrames = oldEnv.keepFrames\n                , currentFrameId = oldEnv.currentFrameId\n            }\n    in\n    withEnv\n        (\\env ->\n            if env.currentFrameId /= Env.globalFrameId then\n                enter env\n                    |> andThen (always body)\n                    |> finally (leave env)\n\n            else\n                body\n        )\n\n\nrunSimple : Eval a -> Result MalExpr a\nrunSimple e =\n    case run Env.global e of\n        ( _, EvalOk res ) ->\n            Ok res\n\n        ( _, EvalErr msg ) ->\n            Err msg\n\n        _ ->\n            Debug.todo \"can't happen\"\n"
  },
  {
    "path": "impls/elm/src/IO.elm",
    "content": "port module IO exposing\n    ( IO(..)\n    , decodeIO\n    , input\n    , readFile\n    , readLine\n    , writeLine\n    )\n\nimport Json.Decode exposing (..)\nimport Time exposing (Posix)\n\n\n{-| Output a string to stdout\n-}\nport writeLine : String -> Cmd msg\n\n\n{-| Read a line from the stdin\n-}\nport readLine : String -> Cmd msg\n\n\n{-| Read the contents of a file\n-}\nport readFile : String -> Cmd msg\n\n\n{-| Received a response for a command.\n-}\nport input : (Value -> msg) -> Sub msg\n\n\ntype IO\n    = LineRead (Maybe String)\n    | LineWritten\n    | FileRead String\n    | Exception String\n    | GotTime Posix\n\n\ndecodeIO : Decoder IO\ndecodeIO =\n    field \"tag\" string\n        |> andThen decodeTag\n\n\ndecodeTag : String -> Decoder IO\ndecodeTag tag =\n    case tag of\n        \"lineRead\" ->\n            field \"line\" (nullable string)\n                |> map LineRead\n\n        \"lineWritten\" ->\n            succeed LineWritten\n\n        \"fileRead\" ->\n            field \"contents\" string\n                |> map FileRead\n\n        \"exception\" ->\n            field \"message\" string\n                |> map Exception\n\n        _ ->\n            fail <|\n                \"Trying to decode IO, but tag \"\n                    ++ tag\n                    ++ \" is not supported.\"\n"
  },
  {
    "path": "impls/elm/src/Printer.elm",
    "content": "module Printer exposing (..)\n\nimport Array exposing (Array)\nimport Dict exposing (Dict)\nimport Env\nimport Types exposing (Env, MalExpr(..), MalFunction(..), keywordPrefix)\nimport Utils exposing (encodeString, wrap)\n\n\nprintStr : Bool -> MalExpr -> String\nprintStr =\n    printString Env.global\n\n\nprintString : Env -> Bool -> MalExpr -> String\nprintString env readably ast =\n    case ast of\n        MalNil ->\n            \"nil\"\n\n        MalBool True ->\n            \"true\"\n\n        MalBool False ->\n            \"false\"\n\n        MalInt int ->\n            String.fromInt int\n\n        MalString str ->\n            printRawString env readably str\n\n        MalSymbol sym ->\n            sym\n\n        MalKeyword kw ->\n            \":\" ++ kw\n\n        MalList _ list ->\n            printList env readably list\n\n        MalVector _ vec ->\n            printVector env readably vec\n\n        MalMap _ map ->\n            printMap env readably map\n\n        MalFunction _ ->\n            \"#<function>\"\n\n        MalAtom atomId ->\n            let\n                value =\n                    Env.getAtom atomId env\n            in\n            \"(atom \" ++ printString env True value ++ \")\"\n\n        MalApply _ ->\n            \"#<apply>\"\n\n\nprintBound : Env -> Bool -> List ( String, MalExpr ) -> String\nprintBound env readably =\n    let\n        printEntry ( name, value ) =\n            name ++ \"=\" ++ printString env readably value\n    in\n    List.map printEntry\n        >> String.join \" \"\n        >> wrap \"(\" \")\"\n\n\nprintRawString : Env -> Bool -> String -> String\nprintRawString env readably str =\n    if readably then\n        encodeString str\n\n    else\n        str\n\n\nprintList : Env -> Bool -> List MalExpr -> String\nprintList env readably =\n    List.map (printString env readably)\n        >> String.join \" \"\n        >> wrap \"(\" \")\"\n\n\nprintVector : Env -> Bool -> Array MalExpr -> String\nprintVector env readably =\n    Array.map (printString env readably)\n        >> Array.toList\n        >> String.join \" \"\n        >> wrap \"[\" \"]\"\n\n\nprintMap : Env -> Bool -> Dict String MalExpr -> String\nprintMap env readably =\n    let\n        -- Strip off the keyword prefix if it is there.\n        printKey k =\n            case String.uncons k of\n                Just ( prefix, rest ) ->\n                    if prefix == keywordPrefix then\n                        \":\" ++ rest\n\n                    else\n                        printRawString env readably k\n\n                _ ->\n                    printRawString env readably k\n\n        printEntry ( k, v ) =\n            printKey k ++ \" \" ++ printString env readably v\n    in\n    Dict.toList\n        >> List.map printEntry\n        >> String.join \" \"\n        >> wrap \"{\" \"}\"\n\n\nprintEnv : Env -> String\nprintEnv env =\n    let\n        printOuterId =\n            Maybe.map String.fromInt >> Maybe.withDefault \"nil\"\n\n        printHeader frameId { outerId, exitId, refCnt } =\n            \"#\"\n                ++ String.fromInt frameId\n                ++ \" outer=\"\n                ++ printOuterId outerId\n                ++ \" exit=\"\n                ++ printOuterId exitId\n                ++ \" refCnt=\"\n                ++ String.fromInt refCnt\n\n        printFrame frameId frame =\n            String.join \"\\n\"\n                (printHeader frameId frame\n                    :: Dict.foldr printDatum [] frame.data\n                )\n\n        printFrameAcc k v acc =\n            printFrame k v :: acc\n\n        printDatum k v acc =\n            (k ++ \" = \" ++ printString env False v) :: acc\n    in\n    \"--- Environment ---\\n\"\n        ++ \"Current frame: #\"\n        ++ String.fromInt env.currentFrameId\n        ++ \"\\n\\n\"\n        ++ String.join \"\\n\\n\" (Dict.foldr printFrameAcc [] env.frames)\n"
  },
  {
    "path": "impls/elm/src/Reader.elm",
    "content": "module Reader exposing (..)\n\nimport Array\nimport Dict\nimport Parser exposing (DeadEnd, Parser, lazy, (|.), (|=))\nimport Types exposing (MalExpr(..), keywordPrefix)\nimport Utils exposing (decodeString, makeCall)\n\n\ncomment : Parser ()\ncomment =\n    Parser.lineComment \";\"\n\n\nws : Parser ()\nws =\n    let\n        isSpaceChar : Char -> Bool\n        isSpaceChar c = List.member c [' ', '\\n', '\\r', ',']\n    in\n    Parser.succeed ()\n        |. Parser.sequence\n            { start     = \"\"\n            , separator = \"\"\n            , end       = \"\"\n            , spaces    = Parser.chompWhile isSpaceChar\n            , item      = comment\n            , trailing  = Parser.Optional\n            }\n\n\nint : Parser MalExpr\nint =\n    --  Parser.map MalInt Parser.int fails with elm/parser 1.1.0\n    let\n        isDigit : Char -> Bool\n        isDigit c = '0' <= c && c <= '9'\n        toInt s = case String.toInt s of\n            Just r  -> MalInt r\n            Nothing -> Debug.todo \"should not happen\"\n    in\n    Parser.map toInt <| Parser.getChompedString <|\n        Parser.chompIf isDigit\n        |. Parser.chompWhile isDigit\n\n\nsymbolString : Parser String\nsymbolString =\n    let\n        isSymbolChar : Char -> Bool\n        isSymbolChar c =\n            not (List.member c [' ', '\\n', '\\r', ',', '\\\\', '[', ']',\n                '{', '}', '(', '\\'', '\"', '`', ';', ')'])\n    in\n    Parser.getChompedString <|\n        Parser.chompIf isSymbolChar\n        |. Parser.chompWhile isSymbolChar\n\n\nsymbolOrConst : Parser MalExpr\nsymbolOrConst =\n    let\n        make sym =\n            case sym of\n                \"nil\" ->\n                    MalNil\n\n                \"true\" ->\n                    MalBool True\n\n                \"false\" ->\n                    MalBool False\n\n                _ ->\n                    MalSymbol sym\n    in\n    Parser.map make symbolString\n\n\nkeywordString : Parser String\nkeywordString =\n    Parser.succeed identity\n        |. Parser.token \":\"\n        |= symbolString\n\n\nkeyword : Parser MalExpr\nkeyword =\n    Parser.map MalKeyword keywordString\n\n\nlist : Parser MalExpr\nlist =\n    Parser.map (MalList Nothing) <| Parser.sequence\n        { start     = \"(\"\n        , separator = \"\"\n        , end       = \")\"\n        , spaces    = ws\n        , item      = form\n        , trailing  = Parser.Optional\n        }\n\n\nvector : Parser MalExpr\nvector =\n    Parser.map (MalVector Nothing << Array.fromList) <| Parser.sequence\n        { start     = \"[\"\n        , separator = \"\"\n        , end       = \"]\"\n        , spaces    = ws\n        , item      = form\n        , trailing  = Parser.Optional\n        }\n\n\nmapKey : Parser String\nmapKey =\n    Parser.oneOf\n        [ Parser.map (String.cons keywordPrefix) keywordString\n        , Parser.map decodeString strString\n        ]\n\n\nmapEntry : Parser ( String, MalExpr )\nmapEntry =\n    Parser.succeed Tuple.pair |= mapKey |= form\n\n\nmap : Parser MalExpr\nmap =\n    Parser.map (MalMap Nothing << Dict.fromList) <| Parser.sequence\n        { start     = \"{\"\n        , separator = \"\"\n        , end       = \"}\"\n        , spaces    = ws\n        , item      = mapEntry\n        , trailing  = Parser.Optional\n      }\n\n\natom : Parser MalExpr\natom =\n    Parser.oneOf\n        [ Parser.succeed identity\n          |. Parser.token \"-\"\n          |= Parser.oneOf\n              [ Parser.map (MalInt << negate) Parser.int\n              , Parser.map (MalSymbol << (++) \"-\") symbolString\n              , Parser.succeed (MalSymbol \"-\")\n              ]\n        , int\n        , keyword\n        , symbolOrConst\n        , str\n        ]\n\n\nform : Parser MalExpr\nform =\n    lazy <|\n        \\() ->\n            let\n                parsers =\n                    [ list\n                    , vector\n                    , map\n                    , simpleMacro \"'\" \"quote\"\n                    , simpleMacro \"`\" \"quasiquote\"\n                    , simpleMacro \"~@\" \"splice-unquote\"\n                    , simpleMacro \"~\" \"unquote\"\n                    , simpleMacro \"@\" \"deref\"\n                    , withMeta\n                    , atom\n                    ]\n            in\n            Parser.succeed identity |. ws |= Parser.oneOf parsers\n\n\nsimpleMacro : String -> String -> Parser MalExpr\nsimpleMacro token symbol =\n  Parser.succeed (makeCall symbol << List.singleton)\n      |. Parser.token token\n      |= form\n\n\nwithMeta : Parser MalExpr\nwithMeta =\n            let\n                make meta expr =\n                    makeCall \"with-meta\" [ expr, meta ]\n            in\n            Parser.succeed make\n                |. Parser.token \"^\"\n                |= form\n                |= form\n\n\nreadString : String -> Result String MalExpr\nreadString str2 =\n    case Parser.run (form |. ws |. Parser.end) str2 of\n        Ok ast ->\n            Ok ast\n\n        Err deadEnds ->\n            --  Should become Err <| Parser.deadEndsToString deadEnds\n            --  once the function is implemented.\n            Err <| formatError deadEnds\n\n\nformatError : List DeadEnd -> String\nformatError =\n    let\n        format1 deadEnd =\n            Debug.toString deadEnd.problem\n            ++ \" at \"\n            ++ String.fromInt deadEnd.row\n            ++ \":\"\n            ++ String.fromInt deadEnd.col\n    in\n    (++) \"end of input\\n\" << String.join \"\\n\" << List.map format1\n\n\nstr : Parser MalExpr\nstr =\n    Parser.map (MalString << decodeString) strString\n\n\nstrString : Parser String\nstrString =\n    let\n        isStringNormalChar : Char -> Bool\n        isStringNormalChar c = not <| List.member c ['\"', '\\\\']\n    in\n    Parser.getChompedString <|\n        Parser.sequence\n            { start     = \"\\\"\"\n            , separator = \"\"\n            , end       = \"\\\"\"\n            , spaces    = Parser.succeed ()\n            , item      = Parser.oneOf\n                [ Parser.chompIf isStringNormalChar\n                    |. Parser.chompWhile isStringNormalChar\n                , Parser.token \"\\\\\"\n                    |. Parser.chompIf (\\_ -> True)\n                ]\n            , trailing  = Parser.Forbidden\n            }\n"
  },
  {
    "path": "impls/elm/src/Step0_repl.elm",
    "content": "module Step0_repl exposing (..)\n\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Flags =\n    { args : List String\n    }\n\n\ntype alias Model =\n    { args : List String\n    }\n\n\ntype Msg\n    = Input (Result String IO)\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit flags =\n    ( flags, readLine prompt )\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case msg of\n        Input (Ok (LineRead (Just line))) ->\n            ( model, writeLine (rep line) )\n\n        Input (Ok LineWritten) ->\n            ( model, readLine prompt )\n\n        Input (Ok (LineRead Nothing)) ->\n            ( model, Cmd.none )\n\n        Input (Ok _) ->\n            ( model, Cmd.none )\n\n        Input (Err msg2) ->\n            Debug.log msg2 ( model, Cmd.none )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> String\nread ast =\n    ast\n\n\neval : String -> String\neval ast =\n    ast\n\n\nprint : String -> String\nprint ast =\n    ast\n\n\nrep : String -> String\nrep =\n    read >> eval >> print\n"
  },
  {
    "path": "impls/elm/src/Step1_read_print.elm",
    "content": "module Step1_read_print exposing (..)\n\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printStr)\nimport Reader exposing (readString)\nimport Types exposing (MalExpr(..))\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Flags =\n    { args : List String\n    }\n\n\ntype alias Model =\n    { args : List String\n    }\n\n\ntype Msg\n    = Input (Result String IO)\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit flags =\n    ( flags, readLine prompt )\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case msg of\n        Input (Ok (LineRead (Just line))) ->\n            ( model, writeLine (rep line) )\n\n        Input (Ok LineWritten) ->\n            ( model, readLine prompt )\n\n        Input (Ok (LineRead Nothing)) ->\n            ( model, Cmd.none )\n\n        Input (Ok io) ->\n            Debug.todo \"unexpected IO received: \" io\n\n        Input (Err msg2) ->\n            Debug.todo msg2 ( model, Cmd.none )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\neval : MalExpr -> MalExpr\neval ast =\n    ast\n\n\nprint : MalExpr -> String\nprint =\n    printStr True\n\n\n{-| Read-Eval-Print\n-}\nrep : String -> String\nrep =\n    let\n        formatResult result =\n            case result of\n                Ok optStr ->\n                    optStr\n\n                Err msg ->\n                    msg\n    in\n    readString\n        >> Result.map (eval >> print)\n        >> formatResult\n"
  },
  {
    "path": "impls/elm/src/Step2_eval.elm",
    "content": "module Step2_eval exposing (..)\n\nimport Array\nimport Dict exposing (Dict)\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printStr)\nimport Reader exposing (readString)\nimport Tuple exposing (mapFirst, second)\nimport Types exposing (..)\nimport Utils exposing (maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Flags =\n    { args : List String\n    }\n\n\ntype alias ReplEnv =\n    Dict String MalExpr\n\n\ntype alias Model =\n    { args : List String\n    , env : ReplEnv\n    }\n\n\ntype Msg\n    = Input (Result String IO)\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    ( { args = args, env = initReplEnv }, readLine prompt )\n\n\ninitReplEnv : ReplEnv\ninitReplEnv =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        binaryOp fn args =\n            case args of\n                [ MalInt x, MalInt y ] ->\n                    Eval.succeed <| MalInt (fn x y)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n    in\n    Dict.fromList\n        [ ( \"+\", makeFn <| binaryOp (+) )\n        , ( \"-\", makeFn <| binaryOp (-) )\n        , ( \"*\", makeFn <| binaryOp (*) )\n        , ( \"/\", makeFn <| binaryOp (//) )\n        ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case msg of\n        Input (Ok (LineRead (Just line))) ->\n            let ( result, newEnv) = rep model.env line\n            in ( { model | env = newEnv }, writeLine (makeOutput result) )\n\n        Input (Ok LineWritten) ->\n            ( model, readLine prompt )\n\n        Input (Ok (LineRead Nothing)) ->\n            ( model, Cmd.none )\n\n        Input (Ok io) ->\n            Debug.todo \"unexpected IO received: \" io\n\n        Input (Err msg2) ->\n            Debug.todo msg2 ( model, Cmd.none )\n\n\nmakeOutput : Result String String -> String\nmakeOutput result =\n    case result of\n        Ok str ->\n            str\n\n        Err msg ->\n            \"Error: \" ++ msg\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\neval : ReplEnv -> MalExpr -> ( Result String MalExpr, ReplEnv )\neval env ast =\n  -- let\n  --   _ = Debug.log (\"EVAL: \" ++ printStr env True ast) ()\n  --       --  The output ends with an ugly \": ()\", but that does not hurt.\n  -- in\n    case ast of\n        MalList _ [] ->\n            ( Ok ast, env )\n\n        MalList _ list ->\n            case evalList env list [] of\n                ( Ok newList, newEnv ) ->\n                    case newList of\n                        [] ->\n                            ( Err \"can't happen\", newEnv )\n\n                        (MalFunction (CoreFunc _ fn)) :: args ->\n                            case Eval.runSimple (fn args) of\n                                Ok res ->\n                                    ( Ok res, newEnv )\n\n                                Err msg ->\n                                    ( Err (print msg), newEnv )\n\n                        fn :: _ ->\n                            ( Err ((print fn) ++ \" is not a function\"), newEnv )\n\n                ( Err msg, newEnv ) ->\n                    ( Err msg, newEnv )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            case Dict.get sym env of\n                Just val ->\n                    ( Ok val, env )\n\n                Nothing ->\n                    ( Err (\"symbol '\" ++ sym ++ \"' not found\"), env )\n\n        MalVector _ vec ->\n            evalList env (Array.toList vec) []\n                |> mapFirst (Result.map (Array.fromList >> MalVector Nothing))\n\n        MalMap _ map ->\n            evalList env (Dict.values map) []\n                |> mapFirst\n                    (Result.map\n                        (zip (Dict.keys map)\n                            >> Dict.fromList\n                            >> MalMap Nothing\n                        )\n                    )\n\n        _ ->\n            ( Ok ast, env )\n\n\nevalList : ReplEnv -> List MalExpr -> List MalExpr -> ( Result String (List MalExpr), ReplEnv )\nevalList env list acc =\n    case list of\n        [] ->\n            ( Ok (List.reverse acc), env )\n\n        x :: rest ->\n            case eval env x of\n                ( Ok val, newEnv ) ->\n                    evalList newEnv rest (val :: acc)\n\n                ( Err msg, newEnv ) ->\n                    ( Err msg, newEnv )\n\n\n{-| Try to map a list with a fn that can return a Err.\n\nMaps the list from left to right. As soon as a error\noccurs it will not process any more elements and return\nthe error.\n\n-}\ntryMapList : (a -> Result e b) -> List a -> Result e (List b)\ntryMapList fn list =\n    let\n        go x =\n            Result.andThen\n                (\\acc ->\n                    case fn x of\n                        Ok val ->\n                            Ok (val :: acc)\n\n                        Err msg ->\n                            Err msg\n                )\n    in\n    List.foldl go (Ok []) list\n        |> Result.map List.reverse\n\n\nprint : MalExpr -> String\nprint =\n    printStr True\n\n\n{-| Read-Eval-Print\n-}\nrep : ReplEnv -> String -> ( Result String String, ReplEnv )\nrep env input =\n    let\n        evalPrint =\n            eval env >> mapFirst (Result.map print)\n    in\n    case readString input of\n        Err msg ->\n            ( Err msg, env )\n\n        Ok ast ->\n            evalPrint ast\n"
  },
  {
    "path": "impls/elm/src/Step3_env.elm",
    "content": "module Step3_env exposing (..)\n\nimport Array\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Tuple exposing (mapFirst, mapSecond, second)\nimport Types exposing (..)\nimport Utils exposing (maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Flags =\n    { args : List String\n    }\n\n\ntype alias Model =\n    { args : List String\n    , env : Env\n    }\n\n\ntype Msg\n    = Input (Result String IO)\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    ( { args = args, env = initReplEnv }, readLine prompt )\n\n\ninitReplEnv : Env\ninitReplEnv =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        binaryOp fn args =\n            case args of\n                [ MalInt x, MalInt y ] ->\n                    Eval.succeed <| MalInt (fn x y)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n    in\n    Env.global\n        |> Env.set \"+\" (makeFn <| binaryOp (+))\n        |> Env.set \"-\" (makeFn <| binaryOp (-))\n        |> Env.set \"*\" (makeFn <| binaryOp (*))\n        |> Env.set \"/\" (makeFn <| binaryOp (//))\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case msg of\n        Input (Ok (LineRead (Just line))) ->\n            let ( result, newEnv) = rep model.env line\n            in ( { model | env = newEnv }, writeLine (makeOutput result) )\n\n        Input (Ok LineWritten) ->\n            ( model, readLine prompt )\n\n        Input (Ok (LineRead Nothing)) ->\n            ( model, Cmd.none )\n\n        Input (Ok io) ->\n            Debug.todo \"unexpected IO received: \" io\n\n        Input (Err msg2) ->\n            Debug.todo msg2 ( model, Cmd.none )\n\n\nmakeOutput : Result String String -> String\nmakeOutput result =\n    case result of\n        Ok str ->\n            str\n\n        Err msg ->\n            \"Error: \" ++ msg\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\neval : Env -> MalExpr -> ( Result String MalExpr, Env )\neval env ast =\n  let\n    _ = case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  in\n  case ast of\n        MalList _ [] ->\n            ( Ok ast, env )\n\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef env args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet env args\n\n        MalList _ list ->\n            case evalList env list [] of\n                ( Ok newList, newEnv ) ->\n                    case newList of\n                        [] ->\n                            ( Err \"can't happen\", newEnv )\n\n                        (MalFunction (CoreFunc _ fn)) :: args ->\n                            case Eval.runSimple (fn args) of\n                                Ok res ->\n                                    ( Ok res, newEnv )\n\n                                Err msg ->\n                                    ( Err (print msg), newEnv )\n\n                        fn :: _ ->\n                            ( Err ((print fn) ++ \" is not a function\"), newEnv )\n\n                ( Err msg, newEnv ) ->\n                    ( Err msg, newEnv )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            case Env.get sym env of\n                Ok val ->\n                    ( Ok val, env )\n\n                Err msg ->\n                    ( Err msg, env )\n\n        MalVector _ vec ->\n            evalList env (Array.toList vec) []\n                |> mapFirst (Result.map (Array.fromList >> MalVector Nothing))\n\n        MalMap _ map ->\n            evalList env (Dict.values map) []\n                |> mapFirst\n                    (Result.map\n                        (zip (Dict.keys map)\n                            >> Dict.fromList\n                            >> MalMap Nothing\n                        )\n                    )\n\n        _ ->\n            ( Ok ast, env )\n\n\nevalList : Env -> List MalExpr -> List MalExpr -> ( Result String (List MalExpr), Env )\nevalList env list acc =\n    case list of\n        [] ->\n            ( Ok (List.reverse acc), env )\n\n        x :: rest ->\n            case eval env x of\n                ( Ok val, newEnv ) ->\n                    evalList newEnv rest (val :: acc)\n\n                ( Err msg, newEnv ) ->\n                    ( Err msg, newEnv )\n\n\nevalDef : Env -> List MalExpr -> ( Result String MalExpr, Env )\nevalDef env args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            case eval env uneValue of\n                ( Ok value, newEnv ) ->\n                    ( Ok value, Env.set name value newEnv )\n\n                err ->\n                    err\n\n        _ ->\n            ( Err \"def! expected two args: name and value\", env )\n\n\nevalLet : Env -> List MalExpr -> ( Result String MalExpr, Env )\nevalLet env args =\n    let\n        evalBinds env2 binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    case eval env2 expr of\n                        ( Ok value, newEnv ) ->\n                            let\n                                newEnv2 =\n                                    Env.set name value env2\n                            in\n                            if List.isEmpty rest then\n                                Ok newEnv2\n                            else\n                                evalBinds newEnv2 rest\n\n                        ( Err msg, _ ) ->\n                            Err msg\n\n                _ ->\n                    Err \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            case evalBinds (Env.push env) binds of\n                Ok newEnv ->\n                    eval newEnv body\n                        |> mapSecond (\\_ -> Env.pop newEnv)\n\n                Err msg ->\n                    ( Err msg, env )\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            ( Err \"let* expected two args: binds and a body\", env )\n\n\n{-| Try to map a list with a fn that can return a Err.\n\nMaps the list from left to right. As soon as a error\noccurs it will not process any more elements and return\nthe error.\n\n-}\ntryMapList : (a -> Result e b) -> List a -> Result e (List b)\ntryMapList fn list =\n    let\n        go x =\n            Result.andThen\n                (\\acc ->\n                    case fn x of\n                        Ok val ->\n                            Ok (val :: acc)\n\n                        Err msg ->\n                            Err msg\n                )\n    in\n    List.foldl go (Ok []) list\n        |> Result.map List.reverse\n\n\nprint : MalExpr -> String\nprint =\n    printString Env.global True\n\n\n{-| Read-Eval-Print\n-}\nrep : Env -> String -> ( Result String String, Env )\nrep env input =\n    let\n        evalPrint =\n            eval env >> mapFirst (Result.map print)\n    in\n    case readString input of\n        Err msg ->\n            ( Err msg, env )\n\n        Ok ast ->\n            evalPrint ast\n"
  },
  {
    "path": "impls/elm/src/Step4_if_fn_do.elm",
    "content": "module Step4_if_fn_do exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Flags =\n    { args : List String\n    }\n\n\ntype Model\n    = InitIO Env (IO -> Eval MalExpr)\n    | InitError\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        initEnv =\n            Core.ns\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        InitError ->\n            -- ignore all\n            ( model, Cmd.none )\n\n        InitIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay, start REPL.\n            ( ReplActive env, readLine prompt )\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( InitError, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ [] ->\n            Eval.succeed ast\n\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ list ->\n            evalList list\n                |> Eval.andThen\n                    (\\newList ->\n                        case newList of\n                            [] ->\n                                Eval.fail \"can't happen\"\n\n                            (MalFunction (CoreFunc _ fn)) :: args ->\n                                fn args\n\n                            (MalFunction (UserFunc { eagerFn })) :: args ->\n                                eagerFn args\n\n                            fn :: _ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv\n                (\\env ->\n                    case Env.get sym env of\n                        Ok val ->\n                            Eval.succeed val\n\n                        Err msg ->\n                            Eval.fail msg\n                )\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                go rest (val :: acc)\n                            )\n    in\n    go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> eval body)\n                |> Eval.andThen\n                    (\\res ->\n                        Eval.modifyEnv Env.pop\n                            |> Eval.map (\\_ -> res)\n                    )\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    let\n        returnLast list =\n            case last list of\n                Just value ->\n                    Eval.succeed value\n\n                Nothing ->\n                    Eval.fail \"do expected at least one arg\"\n    in\n    evalList args\n        |> Eval.andThen returnLast\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        eval\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    fn args =\n                        case binder args of\n                            Ok bound ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.modifyEnv (Env.enter frameId bound)\n                                            |> Eval.andThen (always (eval body))\n                                            |> Eval.finally Env.leave\n                                    )\n\n                            Err msg ->\n                                Eval.fail msg\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = fn\n                    , eagerFn = fn\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            case extractAndParse bindsList of\n                Ok binder ->\n                    Eval.modifyEnv Env.ref\n                        -- reference the current frame.\n                        |> Eval.andThen\n                            (\\_ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.succeed\n                                            (makeFn env.currentFrameId binder body)\n                                    )\n                            )\n\n                Err msg ->\n                    Eval.fail msg\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/Step5_tco.elm",
    "content": "module Step5_tco exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Flags =\n    { args : List String\n    }\n\n\ntype Model\n    = InitIO Env (IO -> Eval MalExpr)\n    | InitError\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        initEnv =\n            Core.ns\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        InitError ->\n            -- ignore all\n            ( model, Cmd.none )\n\n        InitIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay, start REPL.\n            ( ReplActive env, readLine prompt )\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( InitError, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\ndebug : String -> (Env -> a) -> Eval b -> Eval b\ndebug msg f e =\n    Eval.withEnv\n        (\\env ->\n            Env.debug env msg (f env)\n                |> always e\n        )\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n    let\n        apply expr env =\n            case expr of\n                MalApply app ->\n                    Left\n                        (debug \"evalApply\"\n                            (\\env2 -> printString env2 True expr)\n                            (evalApply app)\n                        )\n\n                _ ->\n                    Right expr\n    in\n    evalNoApply ast\n        |> Eval.andThen (Eval.runLoop apply)\n\n\nevalApply : ApplyRec -> Eval MalExpr\nevalApply { frameId, bound, body } =\n    Eval.withEnv\n        (\\env ->\n            Eval.modifyEnv (Env.enter frameId bound)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.leave\n                |> Eval.gcPass\n        )\n\n\nevalNoApply : MalExpr -> Eval MalExpr\nevalNoApply ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ [] ->\n            Eval.succeed ast\n\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ list ->\n            evalList list\n                |> Eval.andThen\n                    (\\newList ->\n                        case newList of\n                            [] ->\n                                Eval.fail \"can't happen\"\n\n                            (MalFunction (CoreFunc _ fn)) :: args ->\n                                fn args\n\n                            (MalFunction (UserFunc { lazyFn })) :: args ->\n                                lazyFn args\n\n                            fn :: _ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv\n                (\\env ->\n                    case Env.get sym env of\n                        Ok val ->\n                            Eval.succeed val\n\n                        Err msg ->\n                            Eval.fail msg\n                )\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                go rest (val :: acc)\n                            )\n    in\n    go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.andThen\n                    (\\res ->\n                        Eval.modifyEnv Env.pop\n                            |> Eval.map (\\_ -> res)\n                    )\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    case List.reverse args of\n        last :: rest ->\n            evalList (List.reverse rest)\n                |> Eval.andThen (\\_ -> evalNoApply last)\n\n        [] ->\n            Eval.fail \"do expected at least one arg\"\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        evalNoApply\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    lazyFn args =\n                        case binder args of\n                            Ok bound ->\n                                Eval.succeed <|\n                                    MalApply\n                                        { frameId = frameId\n                                        , bound = bound\n                                        , body = body\n                                        }\n\n                            Err msg ->\n                                Eval.fail msg\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = lazyFn\n                    , eagerFn = lazyFn >> Eval.andThen eval\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            case extractAndParse bindsList of\n                Ok binder ->\n                    Eval.modifyEnv Env.ref\n                        -- reference the current frame.\n                        |> Eval.andThen\n                            (\\_ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.succeed\n                                            (makeFn env.currentFrameId binder body)\n                                    )\n                            )\n\n                Err msg ->\n                    Eval.fail msg\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/Step6_file.elm",
    "content": "module Step6_file exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Args =\n    List String\n\n\ntype alias Flags =\n    { args : Args\n    }\n\n\ntype Model\n    = InitIO Args Env (IO -> Eval MalExpr)\n    | ScriptIO Env (IO -> Eval MalExpr)\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n    | Stopped\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        initEnv =\n            Core.ns\n                |> Env.set \"eval\" (makeFn malEval)\n                |> Env.set \"*ARGV*\" (MalList Nothing (args |> List.map MalString))\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit args initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    , \"\"\"(def! load-file\n            (fn* (f)\n                (eval (read-string\n                    (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        Stopped ->\n            ( model, Cmd.none )\n\n        InitIO args env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit args env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ScriptIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runScriptLoop env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit args env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay.\n            case args of\n                -- If we got no args: start REPL.\n                [] ->\n                    ( ReplActive env, readLine prompt )\n\n                -- Run the script in the first argument.\n                -- Put the rest of the arguments as *ARGV*.\n                filename :: argv ->\n                    runScript filename argv env\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO args env cont, cmd )\n\n\nrunScript : String -> List String -> Env -> ( Model, Cmd Msg )\nrunScript filename argv env =\n    let\n        malArgv =\n            MalList Nothing (List.map MalString argv)\n\n        newEnv =\n            env |> Env.set \"*ARGV*\" malArgv\n\n        program =\n            MalList Nothing\n                [ MalSymbol \"load-file\"\n                , MalString filename\n                ]\n    in\n    runScriptLoop newEnv (eval program)\n\n\nrunScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunScriptLoop env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( Stopped, Cmd.none )\n\n        ( env, EvalErr msg ) ->\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ScriptIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\ndebug : String -> (Env -> a) -> Eval b -> Eval b\ndebug msg f e =\n    Eval.withEnv\n        (\\env ->\n            Env.debug env msg (f env)\n                |> always e\n        )\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n    let\n        apply expr env =\n            case expr of\n                MalApply app ->\n                    Left\n                        (debug \"evalApply\"\n                            (\\env2 -> printString env2 True expr)\n                            (evalApply app)\n                        )\n\n                _ ->\n                    Right expr\n    in\n    evalNoApply ast\n        |> Eval.andThen (Eval.runLoop apply)\n\n\nmalEval : List MalExpr -> Eval MalExpr\nmalEval args =\n    case args of\n        [ expr ] ->\n            Eval.inGlobal (eval expr)\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalApply : ApplyRec -> Eval MalExpr\nevalApply { frameId, bound, body } =\n    Eval.withEnv\n        (\\env ->\n            Eval.modifyEnv (Env.enter frameId bound)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.leave\n                |> Eval.gcPass\n        )\n\n\nevalNoApply : MalExpr -> Eval MalExpr\nevalNoApply ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ [] ->\n            Eval.succeed ast\n\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ list ->\n            evalList list\n                |> Eval.andThen\n                    (\\newList ->\n                        case newList of\n                            [] ->\n                                Eval.fail \"can't happen\"\n\n                            (MalFunction (CoreFunc _ fn)) :: args ->\n                                fn args\n\n                            (MalFunction (UserFunc { lazyFn })) :: args ->\n                                lazyFn args\n\n                            fn :: _ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv\n                (\\env ->\n                    case Env.get sym env of\n                        Ok val ->\n                            Eval.succeed val\n\n                        Err msg ->\n                            Eval.fail msg\n                )\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                go rest (val :: acc)\n                            )\n    in\n    go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.andThen\n                    (\\res ->\n                        Eval.modifyEnv Env.pop\n                            |> Eval.map (\\_ -> res)\n                    )\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    case List.reverse args of\n        last :: rest ->\n            evalList (List.reverse rest)\n                |> Eval.andThen (\\_ -> evalNoApply last)\n\n        [] ->\n            Eval.fail \"do expected at least one arg\"\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        evalNoApply\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    lazyFn args =\n                        case binder args of\n                            Ok bound ->\n                                Eval.succeed <|\n                                    MalApply\n                                        { frameId = frameId\n                                        , bound = bound\n                                        , body = body\n                                        }\n\n                            Err msg ->\n                                Eval.fail msg\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = lazyFn\n                    , eagerFn = lazyFn >> Eval.andThen eval\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            case extractAndParse bindsList of\n                Ok binder ->\n                    Eval.modifyEnv Env.ref\n                        -- reference the current frame.\n                        |> Eval.andThen\n                            (\\_ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.succeed\n                                            (makeFn env.currentFrameId binder body)\n                                    )\n                            )\n\n                Err msg ->\n                    Eval.fail msg\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/Step7_quote.elm",
    "content": "module Step7_quote exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, makeCall, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Args =\n    List String\n\n\ntype alias Flags =\n    { args : Args\n    }\n\n\ntype Model\n    = InitIO Args Env (IO -> Eval MalExpr)\n    | ScriptIO Env (IO -> Eval MalExpr)\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n    | Stopped\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        initEnv =\n            Core.ns\n                |> Env.set \"eval\" (makeFn malEval)\n                |> Env.set \"*ARGV*\" (MalList Nothing (args |> List.map MalString))\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit args initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    , \"\"\"(def! load-file\n            (fn* (f)\n                (eval (read-string\n                    (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        Stopped ->\n            ( model, Cmd.none )\n\n        InitIO args env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit args env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ScriptIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runScriptLoop env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit args env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay.\n            case args of\n                -- If we got no args: start REPL.\n                [] ->\n                    ( ReplActive env, readLine prompt )\n\n                -- Run the script in the first argument.\n                -- Put the rest of the arguments as *ARGV*.\n                filename :: argv ->\n                    runScript filename argv env\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO args env cont, cmd )\n\n\nrunScript : String -> List String -> Env -> ( Model, Cmd Msg )\nrunScript filename argv env =\n    let\n        malArgv =\n            MalList Nothing (List.map MalString argv)\n\n        newEnv =\n            env |> Env.set \"*ARGV*\" malArgv\n\n        program =\n            MalList Nothing\n                [ MalSymbol \"load-file\"\n                , MalString filename\n                ]\n    in\n    runScriptLoop newEnv (eval program)\n\n\nrunScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunScriptLoop env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( Stopped, Cmd.none )\n\n        ( env, EvalErr msg ) ->\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ScriptIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\ndebug : String -> (Env -> a) -> Eval b -> Eval b\ndebug msg f e =\n    Eval.withEnv\n        (\\env ->\n            Env.debug env msg (f env)\n                |> always e\n        )\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n    let\n        apply expr env =\n            case expr of\n                MalApply app ->\n                    Left\n                        (debug \"evalApply\"\n                            (\\env2 -> printString env2 True expr)\n                            (evalApply app)\n                        )\n\n                _ ->\n                    Right expr\n    in\n    evalNoApply ast\n        |> Eval.andThen (Eval.runLoop apply)\n\n\nmalEval : List MalExpr -> Eval MalExpr\nmalEval args =\n    case args of\n        [ expr ] ->\n            Eval.inGlobal (eval expr)\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalApply : ApplyRec -> Eval MalExpr\nevalApply { frameId, bound, body } =\n    Eval.withEnv\n        (\\env ->\n            Eval.modifyEnv (Env.enter frameId bound)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.leave\n                |> Eval.gcPass\n        )\n\n\nevalNoApply : MalExpr -> Eval MalExpr\nevalNoApply ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ [] ->\n            Eval.succeed ast\n\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ ((MalSymbol \"quote\") :: args) ->\n            evalQuote args\n\n        MalList _ ((MalSymbol \"quasiquote\") :: args) ->\n            case args of\n                [ expr ] ->\n                    -- TCO.\n                    evalNoApply (evalQuasiQuote expr)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        MalList _ list ->\n            evalList list\n                |> Eval.andThen\n                    (\\newList ->\n                        case newList of\n                            [] ->\n                                Eval.fail \"can't happen\"\n\n                            (MalFunction (CoreFunc _ fn)) :: args ->\n                                fn args\n\n                            (MalFunction (UserFunc { lazyFn })) :: args ->\n                                lazyFn args\n\n                            fn :: _ ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv (Env.get sym >> Eval.fromResult)\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                go rest (val :: acc)\n                            )\n    in\n    go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.pop\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    case List.reverse args of\n        last :: rest ->\n            evalList (List.reverse rest)\n                |> Eval.andThen (\\_ -> evalNoApply last)\n\n        [] ->\n            Eval.fail \"do expected at least one arg\"\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        evalNoApply\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    lazyFn =\n                        binder\n                            >> Eval.fromResult\n                            >> Eval.map\n                                (\\bound ->\n                                    MalApply\n                                        { frameId = frameId\n                                        , bound = bound\n                                        , body = body\n                                        }\n                                )\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = lazyFn\n                    , eagerFn = lazyFn >> Eval.andThen eval\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            extractAndParse bindsList\n                |> Eval.fromResult\n                -- reference the current frame.\n                |> Eval.ignore (Eval.modifyEnv Env.ref)\n                |> Eval.andThen\n                    (\\binder ->\n                        Eval.withEnv\n                            (\\env ->\n                                Eval.succeed\n                                    (makeFn env.currentFrameId binder body)\n                            )\n                    )\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nevalQuote : List MalExpr -> Eval MalExpr\nevalQuote args =\n    case args of\n        [ expr ] ->\n            Eval.succeed expr\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalQuasiQuote : MalExpr -> MalExpr\nevalQuasiQuote expr =\n    let\n        qq_loop : MalExpr -> MalExpr -> MalExpr\n        qq_loop elt acc =\n            case elt of\n                (MalList _ [MalSymbol \"splice-unquote\", form]) ->\n                    makeCall \"concat\" [ form, acc ]\n                _ ->\n                    makeCall \"cons\" [ evalQuasiQuote elt, acc ]\n    in\n    case expr of\n        MalList _ [MalSymbol \"unquote\", form] ->\n                form\n\n        MalList _ xs ->\n                List.foldr qq_loop (MalList Nothing []) xs\n\n        MalVector _ xs ->\n                makeCall \"vec\" [ Array.foldr qq_loop (MalList Nothing []) xs ]\n\n        MalSymbol _ ->\n                makeCall \"quote\" [ expr ]\n\n        MalMap _ _ ->\n                makeCall \"quote\" [ expr ]\n\n        _ ->\n                expr\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/Step8_macros.elm",
    "content": "module Step8_macros exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, makeCall, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Args =\n    List String\n\n\ntype alias Flags =\n    { args : Args\n    }\n\n\ntype Model\n    = InitIO Args Env (IO -> Eval MalExpr)\n    | ScriptIO Env (IO -> Eval MalExpr)\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n    | Stopped\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        initEnv =\n            Core.ns\n                |> Env.set \"eval\" (makeFn malEval)\n                |> Env.set \"*ARGV*\" (MalList Nothing (args |> List.map MalString))\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit args initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    , \"\"\"(def! load-file\n            (fn* (f)\n                (eval (read-string\n                    (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\"\n    , \"\"\"(defmacro! cond\n            (fn* (& xs)\n                (if (> (count xs) 0)\n                    (list 'if (first xs)\n                        (if (> (count xs) 1)\n                            (nth xs 1)\n                            (throw \"odd number of forms to cond\"))\n                        (cons 'cond (rest (rest xs)))))))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        Stopped ->\n            ( model, Cmd.none )\n\n        InitIO args env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit args env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ScriptIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runScriptLoop env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit args env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay.\n            case args of\n                -- If we got no args: start REPL.\n                [] ->\n                    ( ReplActive env, readLine prompt )\n\n                -- Run the script in the first argument.\n                -- Put the rest of the arguments as *ARGV*.\n                filename :: argv ->\n                    runScript filename argv env\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO args env cont, cmd )\n\n\nrunScript : String -> List String -> Env -> ( Model, Cmd Msg )\nrunScript filename argv env =\n    let\n        malArgv =\n            MalList Nothing (List.map MalString argv)\n\n        newEnv =\n            env |> Env.set \"*ARGV*\" malArgv\n\n        program =\n            MalList Nothing\n                [ MalSymbol \"load-file\"\n                , MalString filename\n                ]\n    in\n    runScriptLoop newEnv (eval program)\n\n\nrunScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunScriptLoop env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( Stopped, Cmd.none )\n\n        ( env, EvalErr msg ) ->\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ScriptIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\ndebug : String -> (Env -> a) -> Eval b -> Eval b\ndebug msg f e =\n    Eval.withEnv\n        (\\env ->\n            Env.debug env msg (f env)\n                |> always e\n        )\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n    let\n        apply expr env =\n            case expr of\n                MalApply app ->\n                    Left\n                        (debug \"evalApply\"\n                            (\\env2 -> printString env2 True expr)\n                            (evalApply app)\n                        )\n\n                _ ->\n                    Right expr\n    in\n    evalNoApply ast\n        |> Eval.andThen (Eval.runLoop apply)\n\n\nmalEval : List MalExpr -> Eval MalExpr\nmalEval args =\n    case args of\n        [ expr ] ->\n            Eval.inGlobal (eval expr)\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalApply : ApplyRec -> Eval MalExpr\nevalApply { frameId, bound, body } =\n    Eval.withEnv\n        (\\env ->\n            Eval.modifyEnv (Env.enter frameId bound)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.leave\n                |> Eval.gcPass\n        )\n\n\nevalNoApply : MalExpr -> Eval MalExpr\nevalNoApply ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ ((MalSymbol \"quote\") :: args) ->\n            evalQuote args\n\n        MalList _ ((MalSymbol \"quasiquote\") :: args) ->\n            case args of\n                [ expr ] ->\n                    -- TCO.\n                    evalNoApply (evalQuasiQuote expr)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        MalList _ ((MalSymbol \"defmacro!\") :: args) ->\n            evalDefMacro args\n\n        MalList _ (a0 :: rest) ->\n            eval a0\n                |> Eval.andThen\n                    (\\f ->\n                        case f of\n                            MalFunction (CoreFunc _ fn) ->\n                                let args = evalList rest in Eval.andThen\n                                fn args\n\n                            MalFunction (UserFunc {isMacro, eagerFn, lazyFn}) ->\n                              if isMacro then\n                                Eval.andThen evalNoApply (eagerFn rest)\n\n                              else\n                                let args = evalList rest in Eval.andThen\n                                lazyFn args\n\n                            fn ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv (Env.get sym >> Eval.fromResult)\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                go rest (val :: acc)\n                            )\n    in\n    go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalDefMacro : List MalExpr -> Eval MalExpr\nevalDefMacro args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        case value of\n                            MalFunction (UserFunc fn) ->\n                                let\n                                    macroFn =\n                                        MalFunction (UserFunc { fn | isMacro = True })\n                                in\n                                Eval.modifyEnv (Env.set name macroFn)\n                                    |> Eval.andThen (\\_ -> Eval.succeed macroFn)\n\n                            _ ->\n                                Eval.fail \"defmacro! is only supported on a user function\"\n                    )\n\n        _ ->\n            Eval.fail \"defmacro! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.pop\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    case List.reverse args of\n        last :: rest ->\n            evalList (List.reverse rest)\n                |> Eval.andThen (\\_ -> evalNoApply last)\n\n        [] ->\n            Eval.fail \"do expected at least one arg\"\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        evalNoApply\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    lazyFn =\n                        binder\n                            >> Eval.fromResult\n                            >> Eval.map\n                                (\\bound ->\n                                    MalApply\n                                        { frameId = frameId\n                                        , bound = bound\n                                        , body = body\n                                        }\n                                )\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = lazyFn\n                    , eagerFn = lazyFn >> Eval.andThen eval\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            extractAndParse bindsList\n                |> Eval.fromResult\n                -- reference the current frame.\n                |> Eval.ignore (Eval.modifyEnv Env.ref)\n                |> Eval.andThen\n                    (\\binder ->\n                        Eval.withEnv\n                            (\\env ->\n                                Eval.succeed\n                                    (makeFn env.currentFrameId binder body)\n                            )\n                    )\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nevalQuote : List MalExpr -> Eval MalExpr\nevalQuote args =\n    case args of\n        [ expr ] ->\n            Eval.succeed expr\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalQuasiQuote : MalExpr -> MalExpr\nevalQuasiQuote expr =\n    let\n        qq_loop : MalExpr -> MalExpr -> MalExpr\n        qq_loop elt acc =\n            case elt of\n                (MalList _ [MalSymbol \"splice-unquote\", form]) ->\n                    makeCall \"concat\" [ form, acc ]\n                _ ->\n                    makeCall \"cons\" [ evalQuasiQuote elt, acc ]\n    in\n    case expr of\n        MalList _ [MalSymbol \"unquote\", form] ->\n                form\n\n        MalList _ xs ->\n                List.foldr qq_loop (MalList Nothing []) xs\n\n        MalVector _ xs ->\n                makeCall \"vec\" [ Array.foldr qq_loop (MalList Nothing []) xs ]\n\n        MalSymbol _ ->\n                makeCall \"quote\" [ expr ]\n\n        MalMap _ _ ->\n                makeCall \"quote\" [ expr ]\n\n        _ ->\n                expr\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/Step9_try.elm",
    "content": "module Step9_try exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, makeCall, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Args =\n    List String\n\n\ntype alias Flags =\n    { args : Args\n    }\n\n\ntype Model\n    = InitIO Args Env (IO -> Eval MalExpr)\n    | ScriptIO Env (IO -> Eval MalExpr)\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n    | Stopped\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        initEnv =\n            Core.ns\n                |> Env.set \"eval\" (makeFn malEval)\n                |> Env.set \"*ARGV*\" (MalList Nothing (args |> List.map MalString))\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit args initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    , \"\"\"(def! load-file\n            (fn* (f)\n                (eval (read-string\n                    (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\"\n    , \"\"\"(defmacro! cond\n            (fn* (& xs)\n                (if (> (count xs) 0)\n                    (list 'if (first xs)\n                        (if (> (count xs) 1)\n                            (nth xs 1)\n                            (throw \"odd number of forms to cond\"))\n                        (cons 'cond (rest (rest xs)))))))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        Stopped ->\n            ( model, Cmd.none )\n\n        InitIO args env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit args env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ScriptIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runScriptLoop env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit args env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay.\n            case args of\n                -- If we got no args: start REPL.\n                [] ->\n                    ( ReplActive env, readLine prompt )\n\n                -- Run the script in the first argument.\n                -- Put the rest of the arguments as *ARGV*.\n                filename :: argv ->\n                    runScript filename argv env\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO args env cont, cmd )\n\n\nrunScript : String -> List String -> Env -> ( Model, Cmd Msg )\nrunScript filename argv env =\n    let\n        malArgv =\n            MalList Nothing (List.map MalString argv)\n\n        newEnv =\n            env |> Env.set \"*ARGV*\" malArgv\n\n        program =\n            MalList Nothing\n                [ MalSymbol \"load-file\"\n                , MalString filename\n                ]\n    in\n    runScriptLoop newEnv (eval program)\n\n\nrunScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunScriptLoop env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( Stopped, Cmd.none )\n\n        ( env, EvalErr msg ) ->\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ScriptIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\ndebug : String -> (Env -> a) -> Eval b -> Eval b\ndebug msg f e =\n    Eval.withEnv\n        (\\env ->\n            Env.debug env msg (f env)\n                |> always e\n        )\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n    let\n        apply expr env =\n            case expr of\n                MalApply app ->\n                    Left\n                        (debug \"evalApply\"\n                            (\\env2 -> printString env2 True expr)\n                            (evalApply app)\n                        )\n\n                _ ->\n                    Right expr\n    in\n    evalNoApply ast\n        |> Eval.andThen (Eval.runLoop apply)\n\n\nmalEval : List MalExpr -> Eval MalExpr\nmalEval args =\n    case args of\n        [ expr ] ->\n            Eval.inGlobal (eval expr)\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalApply : ApplyRec -> Eval MalExpr\nevalApply { frameId, bound, body } =\n    Eval.withEnv\n        (\\env ->\n            Eval.modifyEnv (Env.enter frameId bound)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.leave\n                |> Eval.gcPass\n        )\n\n\nevalNoApply : MalExpr -> Eval MalExpr\nevalNoApply ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ ((MalSymbol \"quote\") :: args) ->\n            evalQuote args\n\n        MalList _ ((MalSymbol \"quasiquote\") :: args) ->\n            case args of\n                [ expr ] ->\n                    -- TCO.\n                    evalNoApply (evalQuasiQuote expr)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        MalList _ ((MalSymbol \"defmacro!\") :: args) ->\n            evalDefMacro args\n\n        MalList _ ((MalSymbol \"try*\") :: args) ->\n            evalTry args\n\n        MalList _ (a0 :: rest) ->\n            eval a0\n                |> Eval.andThen\n                    (\\f ->\n                        case f of\n                            MalFunction (CoreFunc _ fn) ->\n                                let args = evalList rest in Eval.andThen\n                                fn args\n\n                            MalFunction (UserFunc {isMacro, eagerFn, lazyFn}) ->\n                              if isMacro then\n                                Eval.andThen evalNoApply (eagerFn rest)\n\n                              else\n                                let args = evalList rest in Eval.andThen\n                                lazyFn args\n\n                            fn ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv (Env.get sym >> Eval.fromResult)\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                go rest (val :: acc)\n                            )\n    in\n    go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalDefMacro : List MalExpr -> Eval MalExpr\nevalDefMacro args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        case value of\n                            MalFunction (UserFunc fn) ->\n                                let\n                                    macroFn =\n                                        MalFunction (UserFunc { fn | isMacro = True })\n                                in\n                                Eval.modifyEnv (Env.set name macroFn)\n                                    |> Eval.andThen (\\_ -> Eval.succeed macroFn)\n\n                            _ ->\n                                Eval.fail \"defmacro! is only supported on a user function\"\n                    )\n\n        _ ->\n            Eval.fail \"defmacro! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.pop\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    case List.reverse args of\n        last :: rest ->\n            evalList (List.reverse rest)\n                |> Eval.andThen (\\_ -> evalNoApply last)\n\n        [] ->\n            Eval.fail \"do expected at least one arg\"\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        evalNoApply\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    lazyFn =\n                        binder\n                            >> Eval.fromResult\n                            >> Eval.map\n                                (\\bound ->\n                                    MalApply\n                                        { frameId = frameId\n                                        , bound = bound\n                                        , body = body\n                                        }\n                                )\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = lazyFn\n                    , eagerFn = lazyFn >> Eval.andThen eval\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            extractAndParse bindsList\n                |> Eval.fromResult\n                -- reference the current frame.\n                |> Eval.ignore (Eval.modifyEnv Env.ref)\n                |> Eval.andThen\n                    (\\binder ->\n                        Eval.withEnv\n                            (\\env ->\n                                Eval.succeed\n                                    (makeFn env.currentFrameId binder body)\n                            )\n                    )\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nevalQuote : List MalExpr -> Eval MalExpr\nevalQuote args =\n    case args of\n        [ expr ] ->\n            Eval.succeed expr\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalQuasiQuote : MalExpr -> MalExpr\nevalQuasiQuote expr =\n    let\n        qq_loop : MalExpr -> MalExpr -> MalExpr\n        qq_loop elt acc =\n            case elt of\n                (MalList _ [MalSymbol \"splice-unquote\", form]) ->\n                    makeCall \"concat\" [ form, acc ]\n                _ ->\n                    makeCall \"cons\" [ evalQuasiQuote elt, acc ]\n    in\n    case expr of\n        MalList _ [MalSymbol \"unquote\", form] ->\n                form\n\n        MalList _ xs ->\n                List.foldr qq_loop (MalList Nothing []) xs\n\n        MalVector _ xs ->\n                makeCall \"vec\" [ Array.foldr qq_loop (MalList Nothing []) xs ]\n\n        MalSymbol _ ->\n                makeCall \"quote\" [ expr ]\n\n        MalMap _ _ ->\n                makeCall \"quote\" [ expr ]\n\n        _ ->\n                expr\n\n\nevalTry : List MalExpr -> Eval MalExpr\nevalTry args =\n    case args of\n        [ body ] ->\n            eval body\n\n        [ body, MalList _ [ MalSymbol \"catch*\", MalSymbol sym, handler ] ] ->\n            eval body\n                |> Eval.catchError\n                    (\\ex ->\n                        Eval.modifyEnv Env.push\n                            |> Eval.andThen\n                                (\\_ ->\n                                    Eval.modifyEnv (Env.set sym ex)\n                                )\n                            |> Eval.andThen (\\_ -> eval handler)\n                            |> Eval.finally Env.pop\n                    )\n\n        _ ->\n            Eval.fail \"try* expected a body a catch block\"\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/StepA_mal.elm",
    "content": "module StepA_mal exposing (..)\n\nimport Array\nimport Core\nimport Dict exposing (Dict)\nimport Env\nimport Eval\nimport IO exposing (..)\nimport Json.Decode exposing (decodeValue, errorToString)\nimport Platform exposing (worker)\nimport Printer exposing (printString)\nimport Reader exposing (readString)\nimport Types exposing (..)\nimport Utils exposing (justValues, last, makeCall, maybeToList, zip)\n\n\nmain : Program Flags Model Msg\nmain =\n    worker\n        { init = init\n        , update = update\n        , subscriptions =\n            \\model -> input (decodeValue decodeIO >> (\\x -> case x of\n                Err e -> Err (errorToString e)\n                Ok a  -> Ok a\n            ) >>  Input)\n        }\n\n\ntype alias Args =\n    List String\n\n\ntype alias Flags =\n    { args : Args\n    }\n\n\ntype Model\n    = InitIO Args Env (IO -> Eval MalExpr)\n    | ScriptIO Env (IO -> Eval MalExpr)\n    | ReplActive Env\n    | ReplIO Env (IO -> Eval MalExpr)\n    | Stopped\n\n\ninit : Flags -> ( Model, Cmd Msg )\ninit { args } =\n    let\n        makeFn =\n            CoreFunc Nothing >> MalFunction\n\n        initEnv =\n            Core.ns\n                |> Env.set \"eval\" (makeFn malEval)\n                |> Env.set \"*ARGV*\" (MalList Nothing (args |> List.map MalString))\n                |> Env.set \"*host-language*\" (MalString \"elm\")\n\n        evalMalInit =\n            malInit\n                |> List.map rep\n                |> List.foldl\n                    (\\b a -> a |> Eval.andThen (\\_ -> b))\n                    (Eval.succeed MalNil)\n    in\n    runInit args initEnv evalMalInit\n\n\nmalInit : List String\nmalInit =\n    [ \"\"\"(def! not\n            (fn* (a)\n                (if a false true)))\"\"\"\n    , \"\"\"(def! load-file\n            (fn* (f)\n                (eval (read-string\n                    (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\"\n    , \"\"\"(defmacro! cond\n            (fn* (& xs)\n                (if (> (count xs) 0)\n                    (list 'if (first xs)\n                        (if (> (count xs) 1)\n                            (nth xs 1)\n                            (throw \"odd number of forms to cond\"))\n                        (cons 'cond (rest (rest xs)))))))\"\"\"\n    ]\n\n\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case model of\n        Stopped ->\n            ( model, Cmd.none )\n\n        InitIO args env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runInit args env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ScriptIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    runScriptLoop env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplActive env ->\n            case msg of\n                Input (Ok (LineRead (Just line))) ->\n                    run env (rep line)\n\n                Input (Ok LineWritten) ->\n                    ( model, readLine prompt )\n\n                Input (Ok (LineRead Nothing)) ->\n                    -- Ctrl+D = The End.\n                    ( model, Cmd.none )\n\n                Input (Ok io) ->\n                    Debug.todo \"unexpected IO received: \" io\n\n                Input (Err msg2) ->\n                    Debug.todo msg2\n\n        ReplIO env cont ->\n            case msg of\n                Input (Ok io) ->\n                    run env (cont io)\n\n                Input (Err msg2) ->\n                    Debug.todo msg2 ( model, Cmd.none )\n\n\nrunInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunInit args env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            -- Init went okay.\n            case args of\n                -- If we got no args: start REPL.\n                [] ->\n                    ( ReplActive env, readLine prompt )\n\n                -- Run the script in the first argument.\n                -- Put the rest of the arguments as *ARGV*.\n                filename :: argv ->\n                    runScript filename argv env\n\n        ( env, EvalErr msg ) ->\n            -- Init failed, don't start REPL.\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            -- IO in init.\n            ( InitIO args env cont, cmd )\n\n\nrunScript : String -> List String -> Env -> ( Model, Cmd Msg )\nrunScript filename argv env =\n    let\n        malArgv =\n            MalList Nothing (List.map MalString argv)\n\n        newEnv =\n            env |> Env.set \"*ARGV*\" malArgv\n\n        program =\n            MalList Nothing\n                [ MalSymbol \"load-file\"\n                , MalString filename\n                ]\n    in\n    runScriptLoop newEnv (eval program)\n\n\nrunScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrunScriptLoop env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( Stopped, Cmd.none )\n\n        ( env, EvalErr msg ) ->\n            ( Stopped, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ScriptIO env cont, cmd )\n\n\nrun : Env -> Eval MalExpr -> ( Model, Cmd Msg )\nrun env0 expr0 =\n    case Eval.run env0 expr0 of\n        ( env, EvalOk expr ) ->\n            ( ReplActive env, writeLine (print env expr) )\n\n        ( env, EvalErr msg ) ->\n            ( ReplActive env, writeLine (printError env msg) )\n\n        ( env, EvalIO cmd cont ) ->\n            ( ReplIO env cont, cmd )\n\n\nprompt : String\nprompt =\n    \"user> \"\n\n\nread : String -> Result String MalExpr\nread =\n    readString\n\n\ndebug : String -> (Env -> a) -> Eval b -> Eval b\ndebug msg f e =\n    Eval.withEnv\n        (\\env ->\n            Env.debug env msg (f env)\n                |> always e\n        )\n\n\neval : MalExpr -> Eval MalExpr\neval ast =\n    let\n        apply expr env =\n            case expr of\n                MalApply app ->\n                    Left\n                        (debug \"evalApply\"\n                            (\\env2 -> printString env2 True expr)\n                            (evalApply app)\n                        )\n\n                _ ->\n                    Right expr\n    in\n    evalNoApply ast\n        |> Eval.andThen (Eval.runLoop apply)\n        |> Eval.gcPass\n\n\nmalEval : List MalExpr -> Eval MalExpr\nmalEval args =\n    case args of\n        [ expr ] ->\n            Eval.inGlobal (eval expr)\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalApply : ApplyRec -> Eval MalExpr\nevalApply { frameId, bound, body } =\n    Eval.withEnv\n        (\\env ->\n            Eval.modifyEnv (Env.enter frameId bound)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.leave\n                |> Eval.gcPass\n        )\n\n\nevalNoApply : MalExpr -> Eval MalExpr\nevalNoApply ast =\n  Eval.withEnv (\\env -> Eval.succeed <|\n    case Env.get \"DEBUG-EVAL\" env of\n        Err _              -> ()\n        Ok MalNil          -> ()\n        Ok (MalBool False) -> ()\n        _ -> Debug.log (\"EVAL: \" ++ printString env True ast) ()\n        --  The output ends with an ugly \": ()\", but that does not hurt.\n  ) |> Eval.andThen (\\_ ->\n    case ast of\n        MalList _ ((MalSymbol \"def!\") :: args) ->\n            evalDef args\n\n        MalList _ ((MalSymbol \"let*\") :: args) ->\n            evalLet args\n\n        MalList _ ((MalSymbol \"do\") :: args) ->\n            evalDo args\n\n        MalList _ ((MalSymbol \"if\") :: args) ->\n            evalIf args\n\n        MalList _ ((MalSymbol \"fn*\") :: args) ->\n            evalFn args\n\n        MalList _ ((MalSymbol \"quote\") :: args) ->\n            evalQuote args\n\n        MalList _ ((MalSymbol \"quasiquote\") :: args) ->\n            case args of\n                [ expr ] ->\n                    -- TCO.\n                    evalNoApply (evalQuasiQuote expr)\n\n                _ ->\n                    Eval.fail \"unsupported arguments\"\n\n        MalList _ ((MalSymbol \"defmacro!\") :: args) ->\n            evalDefMacro args\n\n        MalList _ ((MalSymbol \"try*\") :: args) ->\n            evalTry args\n\n        MalList _ (a0 :: rest) ->\n            eval a0\n                |> Eval.andThen\n                    (\\f ->\n                        case f of\n                            MalFunction (CoreFunc _ fn) ->\n                                let args = evalList rest in Eval.andThen\n                                fn args\n\n                            MalFunction (UserFunc {isMacro, eagerFn, lazyFn}) ->\n                              if isMacro then\n                                Eval.andThen evalNoApply (eagerFn rest)\n\n                              else\n                                let args = evalList rest in Eval.andThen\n                                lazyFn args\n\n                            fn ->\n                                Eval.withEnv\n                                    (\\env ->\n                                        Eval.fail (printString env True fn ++ \" is not a function\")\n                                    )\n                    )\n\n        MalSymbol sym ->\n            -- Lookup symbol in env and return value or raise error if not found.\n            Eval.withEnv (Env.get sym >> Eval.fromResult)\n\n        MalVector _ vec ->\n            evalList (Array.toList vec)\n                |> Eval.map (Array.fromList >> MalVector Nothing)\n\n        MalMap _ map ->\n            evalList (Dict.values map)\n                |> Eval.map\n                    (zip (Dict.keys map)\n                        >> Dict.fromList\n                        >> MalMap Nothing\n                    )\n\n        _ ->\n            Eval.succeed ast\n\n  ) |> Eval.andThen (\\res ->\n    debug \"evalNoApply\"\n    (\\env -> (printString env True ast) ++ \" = \" ++ (printString env True res))\n        (Eval.succeed res)\n  )\n\n\nevalList : List MalExpr -> Eval (List MalExpr)\nevalList list =\n    let\n        go lst acc =\n            case lst of\n                [] ->\n                    Eval.succeed (List.reverse acc)\n\n                x :: rest ->\n                    eval x\n                        |> Eval.andThen\n                            (\\val ->\n                                Eval.pushRef val <| go rest (val :: acc)\n                            )\n    in\n    Eval.withStack <| go list []\n\n\nevalDef : List MalExpr -> Eval MalExpr\nevalDef args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        Eval.modifyEnv (Env.set name value)\n                            |> Eval.andThen (\\_ -> Eval.succeed value)\n                    )\n\n        _ ->\n            Eval.fail \"def! expected two args: name and value\"\n\n\nevalDefMacro : List MalExpr -> Eval MalExpr\nevalDefMacro args =\n    case args of\n        [ MalSymbol name, uneValue ] ->\n            eval uneValue\n                |> Eval.andThen\n                    (\\value ->\n                        case value of\n                            MalFunction (UserFunc fn) ->\n                                let\n                                    macroFn =\n                                        MalFunction (UserFunc { fn | isMacro = True })\n                                in\n                                Eval.modifyEnv (Env.set name macroFn)\n                                    |> Eval.andThen (\\_ -> Eval.succeed macroFn)\n\n                            _ ->\n                                Eval.fail \"defmacro! is only supported on a user function\"\n                    )\n\n        _ ->\n            Eval.fail \"defmacro! expected two args: name and value\"\n\n\nevalLet : List MalExpr -> Eval MalExpr\nevalLet args =\n    let\n        evalBinds binds =\n            case binds of\n                (MalSymbol name) :: expr :: rest ->\n                    eval expr\n                        |> Eval.andThen\n                            (\\value ->\n                                Eval.modifyEnv (Env.set name value)\n                                    |> Eval.andThen\n                                        (\\_ ->\n                                            if List.isEmpty rest then\n                                                Eval.succeed ()\n\n                                            else\n                                                evalBinds rest\n                                        )\n                            )\n\n                _ ->\n                    Eval.fail \"let* expected an even number of binds (symbol expr ..)\"\n\n        go binds body =\n            Eval.modifyEnv Env.push\n                |> Eval.andThen (\\_ -> evalBinds binds)\n                |> Eval.andThen (\\_ -> evalNoApply body)\n                |> Eval.finally Env.pop\n    in\n    case args of\n        [ MalList _ binds, body ] ->\n            go binds body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"let* expected two args: binds and a body\"\n\n\nevalDo : List MalExpr -> Eval MalExpr\nevalDo args =\n    case List.reverse args of\n        last :: rest ->\n            evalList (List.reverse rest)\n                |> Eval.andThen (\\_ -> evalNoApply last)\n\n        [] ->\n            Eval.fail \"do expected at least one arg\"\n\n\nevalIf : List MalExpr -> Eval MalExpr\nevalIf args =\n    let\n        isTruthy expr =\n            expr /= MalNil && expr /= MalBool False\n\n        go condition trueExpr falseExpr =\n            eval condition\n                |> Eval.andThen\n                    (\\cond ->\n                        evalNoApply\n                            (if isTruthy cond then\n                                trueExpr\n\n                             else\n                                falseExpr\n                            )\n                    )\n    in\n    case args of\n        [ condition, trueExpr ] ->\n            go condition trueExpr MalNil\n\n        [ condition, trueExpr, falseExpr ] ->\n            go condition trueExpr falseExpr\n\n        _ ->\n            Eval.fail \"if expected at least two args\"\n\n\nevalFn : List MalExpr -> Eval MalExpr\nevalFn parms =\n    let\n        {- Extract symbols from the binds list and verify their uniqueness -}\n        extractSymbols acc list =\n            case list of\n                [] ->\n                    Ok (List.reverse acc)\n\n                (MalSymbol name) :: rest ->\n                    if List.member name acc then\n                        Err \"all binds must have unique names\"\n\n                    else\n                        extractSymbols (name :: acc) rest\n\n                _ ->\n                    Err \"all binds in fn* must be a symbol\"\n\n        parseBinds list =\n            case List.reverse list of\n                var :: \"&\" :: rest ->\n                    Ok <| bindVarArgs (List.reverse rest) var\n\n                _ ->\n                    if List.member \"&\" list then\n                        Err \"varargs separator '&' is used incorrectly\"\n\n                    else\n                        Ok <| bindArgs list\n\n        extractAndParse =\n            extractSymbols [] >> Result.andThen parseBinds\n\n        bindArgs binds args =\n            let\n                numBinds =\n                    List.length binds\n            in\n            if List.length args /= numBinds then\n                Err <|\n                    \"function expected \"\n                        ++ String.fromInt numBinds\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args\n\n        bindVarArgs binds var args =\n            let\n                minArgs =\n                    List.length binds\n\n                varArgs =\n                    MalList Nothing (List.drop minArgs args)\n            in\n            if List.length args < minArgs then\n                Err <|\n                    \"function expected at least \"\n                        ++ String.fromInt minArgs\n                        ++ \" arguments\"\n\n            else\n                Ok <| zip binds args ++ [ ( var, varArgs ) ]\n\n        makeFn frameId binder body =\n            MalFunction <|\n                let\n                    lazyFn =\n                        binder\n                            >> Eval.fromResult\n                            >> Eval.map\n                                (\\bound ->\n                                    MalApply\n                                        { frameId = frameId\n                                        , bound = bound\n                                        , body = body\n                                        }\n                                )\n                in\n                UserFunc\n                    { frameId = frameId\n                    , lazyFn = lazyFn\n                    , eagerFn = lazyFn >> Eval.andThen eval\n                    , isMacro = False\n                    , meta = Nothing\n                    }\n\n        go bindsList body =\n            extractAndParse bindsList\n                |> Eval.fromResult\n                -- reference the current frame.\n                |> Eval.ignore (Eval.modifyEnv Env.ref)\n                |> Eval.andThen\n                    (\\binder ->\n                        Eval.withEnv\n                            (\\env ->\n                                Eval.succeed\n                                    (makeFn env.currentFrameId binder body)\n                            )\n                    )\n    in\n    case parms of\n        [ MalList _ bindsList, body ] ->\n            go bindsList body\n\n        [ MalVector _ bindsVec, body ] ->\n            go (Array.toList bindsVec) body\n\n        _ ->\n            Eval.fail \"fn* expected two args: binds list and body\"\n\n\nevalQuote : List MalExpr -> Eval MalExpr\nevalQuote args =\n    case args of\n        [ expr ] ->\n            Eval.succeed expr\n\n        _ ->\n            Eval.fail \"unsupported arguments\"\n\n\nevalQuasiQuote : MalExpr -> MalExpr\nevalQuasiQuote expr =\n    let\n        qq_loop : MalExpr -> MalExpr -> MalExpr\n        qq_loop elt acc =\n            case elt of\n                (MalList _ [MalSymbol \"splice-unquote\", form]) ->\n                    makeCall \"concat\" [ form, acc ]\n                _ ->\n                    makeCall \"cons\" [ evalQuasiQuote elt, acc ]\n    in\n    case expr of\n        MalList _ [MalSymbol \"unquote\", form] ->\n                form\n\n        MalList _ xs ->\n                List.foldr qq_loop (MalList Nothing []) xs\n\n        MalVector _ xs ->\n                makeCall \"vec\" [ Array.foldr qq_loop (MalList Nothing []) xs ]\n\n        MalSymbol _ ->\n                makeCall \"quote\" [ expr ]\n\n        MalMap _ _ ->\n                makeCall \"quote\" [ expr ]\n\n        _ ->\n                expr\n\n\nevalTry : List MalExpr -> Eval MalExpr\nevalTry args =\n    case args of\n        [ body ] ->\n            eval body\n\n        [ body, MalList _ [ MalSymbol \"catch*\", MalSymbol sym, handler ] ] ->\n            eval body\n                |> Eval.catchError\n                    (\\ex ->\n                        Eval.modifyEnv Env.push\n                            |> Eval.andThen\n                                (\\_ ->\n                                    Eval.modifyEnv (Env.set sym ex)\n                                )\n                            |> Eval.andThen (\\_ -> eval handler)\n                            |> Eval.finally Env.pop\n                    )\n\n        _ ->\n            Eval.fail \"try* expected a body a catch block\"\n\n\nprint : Env -> MalExpr -> String\nprint env =\n    printString env True\n\n\nprintError : Env -> MalExpr -> String\nprintError env expr =\n    \"Error: \" ++ printString env False expr\n\n\n{-| Read-Eval-Print.\n\nDoesn't actually run the Eval but returns the monad.\n\n-}\nrep : String -> Eval MalExpr\nrep input =\n    case readString input of\n        Err msg ->\n            Eval.fail msg\n\n        Ok ast ->\n            eval ast\n"
  },
  {
    "path": "impls/elm/src/Types.elm",
    "content": "module Types exposing (..)\n\nimport Array exposing (Array)\nimport Dict exposing (Dict)\nimport IO exposing (IO)\n\n\ntype Either a b\n    = Left a\n    | Right b\n\n\ntype Msg\n    = Input (Result String IO)\n\n\ntype alias Frame =\n    { outerId : Maybe Int\n    , exitId : Maybe Int\n    , data : Dict String MalExpr\n    , refCnt : Int\n    }\n\n\ntype alias Env =\n    { frames : Dict Int Frame\n    , nextFrameId : Int\n    , currentFrameId : Int\n    , atoms : Dict Int MalExpr\n    , nextAtomId : Int\n    , debug : Bool\n    , gcInterval : Int\n    , gcCounter : Int\n    , stack : List MalExpr\n    , keepFrames : List Int\n    }\n\n\ntype alias EvalCont a =\n    IO -> Eval a\n\n\ntype EvalResult res\n    = EvalErr MalExpr\n    | EvalOk res\n    | EvalIO (Cmd Msg) (EvalCont res)\n\n\ntype alias EvalContext res =\n    ( Env, EvalResult res )\n\n\ntype alias Eval res =\n    Env -> EvalContext res\n\n\ntype alias MalFn =\n    List MalExpr -> Eval MalExpr\n\n\ntype MalFunction\n    = CoreFunc (Maybe MalExpr) MalFn\n    | UserFunc\n        { frameId : Int\n        , lazyFn : MalFn\n        , eagerFn : MalFn\n        , isMacro : Bool\n        , meta : Maybe MalExpr\n        }\n\n\ntype alias ApplyRec =\n    { frameId : Int, bound : Bound, body : MalExpr }\n\n\ntype alias TcoFn =\n    () -> Eval MalExpr\n\n\ntype alias Bound =\n    List ( String, MalExpr )\n\n\ntype MalExpr\n    = MalNil\n    | MalBool Bool\n    | MalInt Int\n    | MalString String\n    | MalKeyword String\n    | MalSymbol String\n    | MalList (Maybe MalExpr) (List MalExpr)\n    | MalVector (Maybe MalExpr) (Array MalExpr)\n    | MalMap (Maybe MalExpr) (Dict String MalExpr)\n    | MalFunction MalFunction\n    | MalApply ApplyRec\n    | MalAtom Int\n\n\n{-| Keywords are prefixed by this char for usage in a MalMap.\nElm doesn't support user defined types as keys in a Dict.\n\nThe unicode char is: '\\\\x029e'\n\n-}\nkeywordPrefix : Char\nkeywordPrefix =\n    'ʞ'\n"
  },
  {
    "path": "impls/elm/src/Utils.elm",
    "content": "module Utils exposing\n    ( decodeString\n    , encodeString\n    , flip\n    , justValues\n    , last\n    , makeCall\n    , maybeToList\n    , wrap\n    , zip\n    )\n\nimport Regex\nimport Types exposing (MalExpr(..))\n\n\ndecodeString : String -> String\ndecodeString =\n    let\n        unescape { match } =\n            case match of\n                \"\\\\n\" ->\n                    \"\\n\"\n\n                \"\\\\\\\"\" ->\n                    \"\\\"\"\n\n                \"\\\\\\\\\" ->\n                    \"\\\\\"\n\n                other ->\n                    other\n    in\n    String.slice 1 -1\n        >> Regex.replace (regex \"\\\\\\\\[\\\\\\\"\\\\\\\\n]\") unescape\n\n\n\n-- helps replace all the encodes found into a string\n\n\nregex : String -> Regex.Regex\nregex str =\n    case Regex.fromString str of\n        Nothing -> Debug.todo \"invalid regex\"\n        Just r  -> r\n\n\nencodeString : String -> String\nencodeString =\n    let\n        escape { match } =\n            case match of\n                \"\\n\" ->\n                    \"\\\\n\"\n\n                \"\\\"\" ->\n                    \"\\\\\\\"\"\n\n                \"\\\\\" ->\n                    \"\\\\\\\\\"\n\n                other ->\n                    other\n    in\n    wrap \"\\\"\" \"\\\"\"\n        << Regex.replace (regex \"[\\\\n\\\\\\\"\\\\\\\\]\") escape\n\n\nmakeCall : String -> List MalExpr -> MalExpr\nmakeCall symbol args =\n    MalList Nothing <| MalSymbol symbol :: args\n\n\nwrap : String -> String -> String -> String\nwrap prefix suffix str =\n    prefix ++ str ++ suffix\n\n\nmaybeToList : Maybe a -> List a\nmaybeToList m =\n    case m of\n        Just x ->\n            [ x ]\n\n        Nothing ->\n            []\n\n\nzip : List a -> List b -> List ( a, b )\nzip a b =\n    case ( a, b ) of\n        ( [], _ ) ->\n            []\n\n        ( _, [] ) ->\n            []\n\n        ( x :: xs, y :: ys ) ->\n            ( x, y ) :: zip xs ys\n\n\nlast : List a -> Maybe a\nlast list =\n    case list of\n        [] ->\n            Nothing\n\n        [ x ] ->\n            Just x\n\n        x :: xs ->\n            last xs\n\n\njustValues : List (Maybe a) -> List a\njustValues list =\n    case list of\n        [] ->\n            []\n\n        (Just x) :: rest ->\n            x :: justValues rest\n\n        Nothing :: rest ->\n            justValues rest\n\n\nflip : (a -> b -> c) -> (b -> a -> c)\nflip f b a =\n    f a b\n"
  },
  {
    "path": "impls/erlang/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install erlang rebar\n"
  },
  {
    "path": "impls/erlang/Makefile",
    "content": "#####################\n\nSOURCES_BASE = src/atom.erl src/printer.erl src/reader.erl\nSOURCES_LISP = src/core.erl src/env.erl src/types.erl src/stepA_mal.erl\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\n#####################\n\nSRCS = step0_repl.erl step1_read_print.erl step2_eval.erl step3_env.erl step4_if_fn_do.erl \\\n\tstep5_tco.erl step6_file.erl step7_quote.erl step8_macros.erl step9_try.erl stepA_mal.erl\nBINS = $(SRCS:%.erl=%)\n\n#####################\n\n.PHONY: all dist clean\n\nall: $(BINS)\n\ndist: mal\n\nmal: $(SOURCES)\n\tsed 's/stepA_mal/mal/' src/stepA_mal.erl > src/mal.erl\n\tMAL_STEP=mal rebar compile escriptize\n\trm src/mal.erl\n\n\ndefine dep_template\n.PHONY: $(1)\n$(1): src/$(1).erl\n\tMAL_STEP=$(1) rebar compile escriptize\nendef\n\n$(foreach b,$(BINS),$(eval $(call dep_template,$(b))))\n\nclean:\n\trebar clean\n\trm -f mal\n"
  },
  {
    "path": "impls/erlang/rebar.config",
    "content": "%%\n%% rebar configuration file (https://github.com/rebar/rebar)\n%%\n\n{erl_opts, [debug_info, fail_on_warning]}.\n\n{clean_files, [\n    \"ebin\",\n    \"src/*.beam\",\n    \"mal\",\n    \"step0_repl\",\n    \"step1_read_print\",\n    \"step2_eval\",\n    \"step3_env\",\n    \"step4_if_fn_do\",\n    \"step5_tco\",\n    \"step6_file\",\n    \"step7_quote\",\n    \"step8_macros\",\n    \"step9_try\",\n    \"stepA_mal\"\n]}.\n"
  },
  {
    "path": "impls/erlang/rebar.config.script",
    "content": "%%\n%% rebar dynamic configuration file\n%% (https://github.com/rebar/rebar/wiki/Dynamic-configuration)\n%%\n\ncase os:getenv(\"MAL_STEP\") of\n    false -> CONFIG; % env var not defined\n    []    -> CONFIG; % env var set to empty string\n    Step  -> CONFIG ++ [{escript_name, Step}];\n    mal   -> CONFIG ++ [{escript_name, mal}]\nend.\n"
  },
  {
    "path": "impls/erlang/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/erlang/src/atom.erl",
    "content": "%%%\n%%% Atom\n%%%\n%%% Atoms in MAL represent mutable data, which is not native to Erlang. The\n%%% lightweight technique for representing mutable data in Erlang is with a\n%%% lightweight process.\n%%%\n\n-module(atom).\n-behavior(gen_server).\n\n-export([new/1, deref/1, reset/2]).\n-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).\n\n-record(state, {atom}).\n\n%%\n%% Public API\n%%\n\n-spec new(Atom) -> Pid\n    when Atom :: term(),\n         Pid  :: pid().\nnew(Atom) ->\n    case gen_server:start(?MODULE, [Atom], []) of\n        {ok, Pid} -> Pid;\n        {error, Reason} -> error(Reason)\n    end.\n\n-spec deref(Pid) -> Value\n    when Pid   :: pid(),\n         Value :: term().\nderef(Pid) ->\n\tgen_server:call(Pid, deref).\n\n-spec reset(Pid, Value) -> ok\n    when Pid   :: pid(),\n         Value :: term().\nreset(Pid, Value) ->\n    gen_server:call(Pid, {reset, Value}).\n\n%%\n%% gen_server callbacks\n%%\n\ninit([]) ->\n    init([nil]);\ninit([Value]) ->\n    {ok, #state{atom=Value}}.\n\nhandle_call(deref, _From, State) ->\n    {reply, State#state.atom, State};\nhandle_call({reset, Value}, _From, _State) ->\n    {reply, Value, #state{atom=Value}};\nhandle_call(terminate, _From, State) ->\n    {stop, normal, ok, State}.\n\nhandle_cast(_Msg, State) ->\n    {noreply, State}.\n\nhandle_info(Msg, State) ->\n    error_logger:info_msg(\"unexpected message: ~p~n\", [Msg]),\n    {noreply, State}.\n\nterminate(_Reason, _State) ->\n    ok.\n\ncode_change(_OldVsn, State, _Extra) ->\n    {ok, State}.\n"
  },
  {
    "path": "impls/erlang/src/core.erl",
    "content": "%%%\n%%% Core functions\n%%%\n\n-module(core).\n-compile(export_all).\n\nnil_p([Arg]) ->\n    Arg == nil;\nnil_p(_) ->\n    {error, \"nil? takes a single argument\"}.\n\ntrue_p([Arg]) ->\n    Arg == true;\ntrue_p(_) ->\n    {error, \"true? takes a single argument\"}.\n\nfalse_p([Arg]) ->\n    Arg == false;\nfalse_p(_) ->\n    {error, \"false? takes a single argument\"}.\n\nnumber_p([{integer, _}]) ->\n    true;\nnumber_p([_]) ->\n    false;\nnumber_p(_) ->\n    {error, \"number? takes a single argument\"}.\n\nfn_p([{function, _, _}]) ->\n    true;\nfn_p([{closure, _, _, _, _, _}]) ->\n    true;\nfn_p([_]) ->\n    false;\nfn_p(_) ->\n    {error, \"fn? takes a single argument\"}.\n\nmacro_p([{macro, _, _, _, _}]) ->\n    true;\nmacro_p([_]) ->\n    false;\nmacro_p(_) ->\n    {error, \"macro? takes a single argument\"}.\n\ncount([{Type, List, _Meta}]) when Type == list orelse Type == vector ->\n    {integer, length(List)};\ncount([nil]) ->\n    {integer, 0};\ncount([_]) ->\n    {error, \"count called on non-sequence\"};\ncount([]) ->\n    {error, \"count called with no arguments\"};\ncount(_) ->\n    {error, \"count expects one list argument\"}.\n\nempty_q([{Type, List, _Meta}]) when Type == list orelse Type == vector ->\n    length(List) == 0;\nempty_q([_]) ->\n    {error, \"empty? called on non-sequence\"};\nempty_q([]) ->\n    {error, \"empty? called with no arguments\"};\nempty_q(_) ->\n    {error, \"empty? expects one list argument\"}.\n\nnth([{Type, List, _Meta}, {integer, Index}]) when Type == list orelse Type == vector ->\n    try lists:nth(Index+1, List) of\n        Result -> Result\n    catch\n        error:_Error ->\n            % raise rather than returning an {error}\n            error(\"nth: index out of range\")\n    end;\nnth([_]) ->\n    {error, \"nth expects two arguments\"}.\n\nfirst([{Type, [First|_Rest], _Meta}]) when Type == list orelse Type == vector ->\n    First;\nfirst([{Type, [], _Meta}]) when Type == list orelse Type == vector ->\n    nil;\nfirst([nil]) ->\n    nil;\nfirst([_]) ->\n    {error, \"first called on non-sequence\"};\nfirst([]) ->\n    {error, \"first called with no arguments\"};\nfirst(_) ->\n    {error, \"first expects one list argument\"}.\n\nrest([{Type, [_First|Rest], _Meta}]) when Type == list orelse Type == vector ->\n    {list, Rest, nil};\nrest([{Type, [], _Meta}]) when Type == list orelse Type == vector ->\n    {list, [], nil};\nrest([nil]) ->\n    {list, [], nil};\nrest([_]) ->\n    {error, \"rest called on non-sequence\"};\nrest([]) ->\n    {error, \"rest called with no arguments\"};\nrest(_) ->\n    {error, \"rest expects one list argument\"}.\n\nseq([{list, [], _Meta}]) ->\n    nil;\nseq([{list, List, _Meta}]) ->\n    {list, List, nil};\nseq([{vector, [], _Meta}]) ->\n    nil;\nseq([{vector, List, _Meta}]) ->\n    {list, List, nil};\nseq([{string, []}]) ->\n    nil;\nseq([{string, S}]) ->\n    {list, lists:map(fun(C) -> {string, [C]} end, S), nil};\nseq([nil]) ->\n    nil;\nseq(_) ->\n    {error, \"seq expects one list/vector/string/nil argument\"}.\n\nequal_q(Args) ->\n    case Args of\n        [nil, nil] -> true;\n        [true, true] -> true;\n        [false, false] -> true;\n        [{integer, I}, {integer, J}] -> I == J;\n        [{string, S}, {string, T}] -> S == T;\n        [{keyword, K}, {keyword, J}] -> K == J;\n        [{symbol, S}, {symbol, T}] -> S == T;\n        [{list, L1, _M1}, {list, L2, _M2}] -> equal_seqs(L1, L2);\n        [{vector, L1, _M1}, {vector, L2, _M2}] -> equal_seqs(L1, L2);\n        [{list, L1, _M1}, {vector, L2, _M2}] -> equal_seqs(L1, L2);\n        [{vector, L1, _M1}, {list, L2, _M2}] -> equal_seqs(L1, L2);\n        [{map, M1, _M1}, {map, M2, _M2}] -> equal_maps(M1, M2);\n        [_A, _B] -> false;\n        _ -> {error, \"equal? expects two arguments\"}\n    end.\n\nequal_seqs([], []) ->\n    true;\nequal_seqs([X|Xs], [Y|Ys]) ->\n    equal_q([X, Y]) andalso equal_seqs(Xs, Ys);\nequal_seqs(_, _) ->\n    false.\n\nequal_maps(M1, M2) ->\n    maps:size(M1) == maps:size(M2) andalso equal_maps_for_keys(maps:keys(M1), M1, M2).\n\nequal_maps_for_keys([], _M1, _M2) ->\n    true;\nequal_maps_for_keys([K|Ks], M1, M2) ->\n    equal_values_for_key(K, M1, M2) andalso equal_maps_for_keys(Ks, M1, M2).\n\nequal_values_for_key(K, M1, M2) ->\n    case [maps:find(K, M1), maps:find(K, M2)] of\n        [{ok, V1}, {ok, V2}] -> equal_q([V1, V2]);\n        _ -> false\n    end.\n\nint_op(F, [A0,A1]) ->\n    case A0 of\n        {integer, I0} ->\n            case A1 of\n                {integer, I1} ->\n                    {integer, F(I0, I1)};\n                _ -> {error, \"second argument must be an integer\"}\n            end;\n        _ -> {error, \"first argument must be an integer\"}\n    end;\nint_op(_F, _L) ->\n    {error, \"must have two arguments\"}.\n\nint_add(Args) ->\n    int_op(fun(I, J) -> I + J end, Args).\n\nint_sub(Args) ->\n    int_op(fun(I, J) -> I - J end, Args).\n\nint_mul(Args) ->\n    int_op(fun(I, J) -> I * J end, Args).\n\nint_div(Args) ->\n    int_op(fun(I, J) -> I div J end, Args).\n\nbool_op(F, [A0,A1]) ->\n    case A0 of\n        {integer, I0} ->\n            case A1 of\n                {integer, I1} ->\n                    % the true or false is our return value\n                    F(I0, I1);\n                _ -> {error, \"second argument must be an integer\"}\n            end;\n        _ -> {error, \"first argument must be an integer\"}\n    end;\nbool_op(_F, _L) ->\n    {error, \"must have two arguments\"}.\n\nbool_lt(Args) ->\n    bool_op(fun(I, J) -> I < J end, Args).\n\nbool_lte(Args) ->\n    bool_op(fun(I, J) -> I =< J end, Args).\n\nbool_gt(Args) ->\n    bool_op(fun(I, J) -> I > J end, Args).\n\nbool_gte(Args) ->\n    bool_op(fun(I, J) -> I >= J end, Args).\n\npr_str(Args) ->\n    {string, printer:pr_list(Args, \"\", \"\", \" \", true)}.\n\nstr(Args) ->\n    {string, printer:pr_list(Args, \"\", \"\", \"\", false)}.\n\nprn(Args) ->\n    io:format(\"~s~n\", [printer:pr_list(Args, \"\", \"\", \" \", true)]),\n    nil.\n\nprintln(Args) ->\n    io:format(\"~s~n\", [printer:pr_list(Args, \"\", \"\", \" \", false)]),\n    nil.\n\nread_string([{string, Input}]) ->\n    case reader:read_str(Input) of\n        {ok, none} -> nil;\n        {ok, AST} -> AST;\n        {error, Reason} -> {error, Reason}\n    end;\nread_string(_) ->\n    {error, \"read-string expects a single string argument\"}.\n\nslurp([{string, Filepath}]) ->\n    case file:read_file(Filepath) of\n        {ok, Binary} -> {string, binary_to_list(Binary)};\n        {error, Reason} -> {error, Reason}\n    end;\nslurp(_) ->\n    {error, \"slurp called with non-string\"}.\n\ncons([Elem, {Type, List, _Meta}]) when Type == list orelse Type == vector ->\n    {list, [Elem|List], nil};\ncons([_,_]) ->\n    {error, \"second argument to cons must be a sequence\"};\ncons(_) ->\n    {error, \"cons expects two arguments\"}.\n\nconj([{Type, _List, _Meta}]) when Type == list orelse Type == vector ->\n    {error, \"conj expects additional arguments\"};\nconj([{list, List, _Meta}|Args]) ->\n    {list, lists:foldl(fun(Elem, AccIn) -> [Elem|AccIn] end, List, Args), nil};\nconj([{vector, List, _Meta}|Args]) ->\n    % why is vector backward from list?\n    {vector, List ++ Args, nil};\nconj(_) ->\n    {error, \"conj expects a list and one or more arguments\"}.\n\nconcat(Args) ->\n    PushAll = fun(Elem, AccIn) ->\n        case Elem of\n            {Type, List, _Meta} when Type == list orelse Type == vector ->\n                AccIn ++ List;\n            _ -> error(\"concat called with non-sequence\")\n        end\n    end,\n    try lists:foldl(PushAll, [], Args) of\n        Result -> {list, Result, nil}\n    catch\n        error:Reason -> {error, Reason}\n    end.\n\nvec([{list,   List, _Meta}]) -> {vector, List, nil};\nvec([{vector, List, _Meta}]) -> {vector, List, nil};\nvec([_])                     -> {error, \"vec: arg type\"};\nvec(_)                       -> {error, \"vec: arg count\"}.\n\nmal_throw([Reason]) ->\n    throw(Reason);\nmal_throw(_) ->\n    {error, \"throw expects a list with one argument\"}.\n\nmap_f([{closure, Eval, Binds, Body, CE, _M1}, {Type, Args, _M2}]) when Type == list orelse Type == vector ->\n    Apply = fun(Arg) ->\n        NewEnv = env:new(CE),\n        env:bind(NewEnv, Binds, [Arg]),\n        Eval(Body, NewEnv)\n    end,\n    {list, lists:map(Apply, Args), nil};\nmap_f([{function, F, _M}, {Type, Args, _Meta}]) when Type == list orelse Type == vector ->\n    {list, [erlang:apply(F, [[Arg]]) || Arg <- Args], nil};\nmap_f(_) ->\n    {error, \"map expects a function and list argument\"}.\n\nflatten_args(Args) ->\n    % Convert the apply arguments into a flat list, such that no element\n    % consists of {list,...} or {vector,...} (i.e. just [A, B, C, ...]).\n    Delist = fun(Elem) ->\n        case Elem of\n            {T, L, _M} when T == list orelse T == vector -> L;\n            _ -> Elem\n        end\n    end,\n    lists:flatten(lists:map(Delist, lists:flatten(Args))).\n\napply_f([{closure, Eval, Binds, Body, CE, _M1}|Args]) ->\n    NewEnv = env:new(CE),\n    env:bind(NewEnv, Binds, flatten_args(Args)),\n    Eval(Body, NewEnv);\napply_f([{macro, Eval, Binds, Body, CE}|Args]) ->\n    NewEnv = env:new(CE),\n    env:bind(NewEnv, Binds, flatten_args(Args)),\n    Eval(Body, NewEnv);\napply_f([{function, F, _M}|Args]) ->\n    erlang:apply(F, [flatten_args(Args)]);\napply_f(_) ->\n    {error, \"apply expects a function followed by arguments\"}.\n\nreadline([{string, Prompt}]) ->\n    case io:get_line(standard_io, Prompt) of\n        % When user presses Ctrl-d it seems like io:get_line/2 cannot be\n        % called again, and we seem unable to signal to MAL to terminate,\n        % so just error out.\n        eof -> exit(goodbye);\n        {error, Reason} -> {error, Reason};\n        Line -> {string, string:strip(Line, both, $\\n)}\n    end;\nreadline(_) ->\n    {error, \"readline expects a string argument\"}.\n\ntime_ms(_) ->\n    {Mega, Sec, Micro} = os:timestamp(),\n    {integer, Mega * 1000000000 + Sec * 1000 + Micro div 1000}.\n\nns() ->\n    Builtins = #{\n        \"*\" => fun int_mul/1,\n        \"+\" => fun int_add/1,\n        \"-\" => fun int_sub/1,\n        \"/\" => fun int_div/1,\n        \"<\" => fun bool_lt/1,\n        \"<=\" => fun bool_lte/1,\n        \"=\" => fun equal_q/1,\n        \">\" => fun bool_gt/1,\n        \">=\" => fun bool_gte/1,\n        \"apply\" => fun apply_f/1,\n        \"assoc\" => fun types:assoc/1,\n        \"atom\" => fun types:atom/1,\n        \"atom?\" => fun types:atom_p/1,\n        \"concat\" => fun concat/1,\n        \"conj\" => fun conj/1,\n        \"cons\" => fun cons/1,\n        \"contains?\" => fun types:contains_p/1,\n        \"count\" => fun count/1,\n        \"deref\" => fun types:deref/1,\n        \"dissoc\" => fun types:dissoc/1,\n        \"empty?\" => fun empty_q/1,\n        \"false?\" => fun false_p/1,\n        \"first\" => fun first/1,\n        \"fn?\" => fun fn_p/1,\n        \"get\" => fun types:map_get/1,\n        \"hash-map\" => fun types:hash_map/1,\n        \"keys\" => fun types:map_keys/1,\n        \"keyword\" => fun types:keyword/1,\n        \"keyword?\" => fun types:keyword_p/1,\n        \"list\" => fun types:list/1,\n        \"list?\" => fun types:list_p/1,\n        \"macro?\" => fun macro_p/1,\n        \"map\" => fun map_f/1,\n        \"map?\" => fun types:map_p/1,\n        \"meta\" => fun types:meta/1,\n        \"nil?\" => fun nil_p/1,\n        \"nth\" => fun nth/1,\n        \"number?\" => fun number_p/1,\n        \"pr-str\" => fun pr_str/1,\n        \"println\" => fun println/1,\n        \"prn\" => fun prn/1,\n        \"read-string\" => fun read_string/1,\n        \"readline\" => fun readline/1,\n        \"reset!\" => fun types:reset/1,\n        \"rest\" => fun rest/1,\n        \"seq\" => fun seq/1,\n        \"sequential?\" => fun types:sequential_p/1,\n        \"slurp\" => fun slurp/1,\n        \"str\" => fun str/1,\n        \"string?\" => fun types:string_p/1,\n        \"swap!\" => fun types:swap/1,\n        \"symbol\" => fun types:symbol/1,\n        \"symbol?\" => fun types:symbol_p/1,\n        \"throw\" => fun mal_throw/1,\n        \"time-ms\" => fun time_ms/1,\n        \"true?\" => fun true_p/1,\n        \"vals\" => fun types:map_values/1,\n        \"vec\" => fun vec/1,\n        \"vector\" => fun types:vector/1,\n        \"vector?\" => fun types:vector_p/1,\n        \"with-meta\" => fun types:with_meta/1\n    },\n    Env = env:new(undefined),\n    SetEnv = fun(K, V) ->\n        env:set(Env, {symbol, K}, types:func(V))\n    end,\n    maps:map(SetEnv, Builtins),\n    Env.\n"
  },
  {
    "path": "impls/erlang/src/env.erl",
    "content": "%%%\n%%% Environment\n%%%\n%%% We need an \"object\" to represent the environment: something whose state can\n%%% change over time, while keeping a single, unchanging reference to that\n%%% object. This is done in Erlang using lightweight processes. Fortunately, OTP\n%%% makes this easy.\n%%%\n\n-module(env).\n-behavior(gen_server).\n\n-export([new/1, bind/3, find/2, get/2, set/3, root/1]).\n-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).\n\n-record(state, {outer, data}).\n\n%%\n%% Public API\n%%\n\n-spec new(Outer) -> Pid\n    when Outer :: #state{},\n         Pid   :: pid().\n% @doc Pass 'undefined' for Outer if no parent environment.\nnew(Outer) ->\n    case gen_server:start(?MODULE, [Outer], []) of\n        {ok, Pid} -> Pid;\n        {error, Reason} -> error(Reason)\n    end.\n\n-spec bind(Pid, Names, Values) -> ok\n    when Pid    :: pid(),\n         Names  :: [term()],\n         Values :: [term()].\nbind(Pid, Names, Values) ->\n    gen_server:call(Pid, {bind, Names, Values}).\n\n-spec find(Pid1, Key) -> Pid2\n    when Pid1 :: pid(),\n         Key  :: {symbol, string()},\n         Pid2 :: pid() | nil.\nfind(Pid, {symbol, Name}) ->\n    gen_server:call(Pid, {find_pid, Name}).\n\n-spec get(Pid, Key) -> Value\n    when Pid   :: pid(),\n         Key   :: {symbol, string()},\n         Value :: term().\nget(Pid, {symbol, Name}) ->\n\tcase gen_server:call(Pid, {get, Name}) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end;\nget(_Pid, _Key) ->\n    error(\"env:get/2 called with non-symbol key\").\n\n-spec set(Pid, Key, Value) -> ok\n    when Pid   :: pid(),\n         Key   :: {symbol, string()},\n         Value :: term().\nset(Pid, {symbol, Name}, Value) ->\n    gen_server:call(Pid, {set, Name, Value});\nset(_Env, _Key, _Value) ->\n    error(\"env:set/3 called with non-symbol key\").\n\n-spec root(Pid1) -> Pid2\n    when Pid1 :: pid(),\n         Pid2 :: pid().\nroot(Pid) ->\n    gen_server:call(Pid, root).\n\n%%\n%% gen_server callbacks\n%%\n\ninit([]) ->\n    init([undefined]);\ninit([Outer]) ->\n    {ok, #state{outer=Outer, data=#{}}}.\n\nhandle_call({bind, Names, Values}, _From, State) ->\n    NewEnv = env_bind(State, Names, Values),\n    {reply, ok, NewEnv};\nhandle_call({find_env, Name}, _From, State) ->\n    {reply, env_find(State, Name), State};\nhandle_call({find_pid, Name}, _From, State) ->\n    {reply, pid_find(State, Name), State};\nhandle_call({get, Name}, _From, State) ->\n    {reply, env_get(State, Name), State};\nhandle_call({set, Name, Value}, _From, State) ->\n    {reply, ok, env_set(State, Name, Value)};\nhandle_call(root, _From, State) ->\n    {reply, env_root(State), State};\nhandle_call(terminate, _From, State) ->\n    {stop, normal, ok, State}.\n\nhandle_cast(_Msg, State) ->\n    {noreply, State}.\n\nhandle_info(Msg, State) ->\n    error_logger:info_msg(\"unexpected message: ~p~n\", [Msg]),\n    {noreply, State}.\n\nterminate(_Reason, _State) ->\n    ok.\n\ncode_change(_OldVsn, State, _Extra) ->\n    {ok, State}.\n\n%%\n%% Internal functions\n%%\n\npid_find(Env, Name) ->\n    case maps:is_key(Name, Env#state.data) of\n        true  -> self();\n        false ->\n            case Env#state.outer of\n                undefined -> nil;\n                Outer     -> gen_server:call(Outer, {find_pid, Name})\n            end\n    end.\n\nenv_find(Env, Name) ->\n    case maps:is_key(Name, Env#state.data) of\n        true  -> Env;\n        false ->\n            case Env#state.outer of\n                undefined -> nil;\n                Outer     -> gen_server:call(Outer, {find_env, Name})\n            end\n    end.\n\n-spec env_bind(Env1, Names, Values) -> Env2\n    when Env1   :: #state{},\n         Names  :: [term()],\n         Values :: [term()],\n         Env2   :: #state{}.\nenv_bind(Env, [], []) ->\n    Env;\nenv_bind(Env, [{symbol, \"&\"}, {symbol, Name}], Values) ->\n    env_set(Env, Name, {list, Values, nil});\nenv_bind(Env, [{symbol, Name}|Ntail], [Value|Vtail]) ->\n    env_bind(env_set(Env, Name, Value), Ntail, Vtail).\n\n-spec env_get(Env, Key) -> {ok, Value} | {error, string()}\n    when Env   :: #state{},\n         Key   :: {symbol, string()},\n         Value :: term().\nenv_get(Env, Name) ->\n    case env_find(Env, Name) of\n        nil -> {error, io_lib:format(\"'~s' not found\", [Name])};\n        E   -> {ok, maps:get(Name, E#state.data)}\n    end.\n\n-spec env_set(Env1, Key, Value) -> Env2\n    when Env1  :: #state{},\n         Key   :: {symbol, string()},\n         Value :: term(),\n         Env2  :: #state{}.\nenv_set(Env, Name, Value) ->\n    Map = maps:put(Name, Value, Env#state.data),\n    #state{outer=Env#state.outer, data=Map}.\n\n-spec env_root(Env1) -> Env2\n    when Env1 :: #state{},\n         Env2 :: #state{}.\nenv_root(Env) ->\n    case Env#state.outer of\n        undefined -> self();\n        Outer     -> gen_server:call(Outer, root)\n    end.\n"
  },
  {
    "path": "impls/erlang/src/mal.app.src",
    "content": "{application, mal, [\n    {description, \"Make-a-Lisp Erlang\"},\n    {vsn, \"1\"},\n    {registered, []},\n    {applications, [\n        kernel,\n        stdlib\n    ]},\n    {mod, {mal_app, []}},\n    {env, []}\n]}.\n"
  },
  {
    "path": "impls/erlang/src/printer.erl",
    "content": "%%%\n%%% Printer\n%%%\n\n-module(printer).\n\n-export([pr_str/2, pr_list/5]).\n\n-spec pr_str(term(), true|false) -> string().\npr_str(Value, Readably) ->\n\tcase Value of\n\t\tnil -> \"nil\";\n\t\ttrue -> \"true\";\n\t\tfalse -> \"false\";\n        {atom, Atom} ->\n            AtomStr = pr_str(atom:deref(Atom), Readably),\n            io_lib:format(\"(atom ~s)\", [AtomStr]);\n        {integer, Num} -> integer_to_list(Num);\n        {string, String} when Readably == true -> escape_str(String);\n        {string, String} when Readably == false -> String;\n        {keyword, Keyword} -> [$:|Keyword];\n        {symbol, Symbol} -> Symbol;\n        {list, List, _Meta} -> pr_list(List, \"(\", \")\", \" \", Readably);\n        {vector, Vector, _Meta} -> pr_list(Vector, \"[\", \"]\", \" \", Readably);\n        {map, Map, _Meta} -> pr_map(Map, Readably);\n        {closure, _Eval, Binds, Body, _Env, _Meta} ->\n            BindsStr = pr_str({list, Binds, nil}, Readably),\n            BodyStr = pr_str(Body, Readably),\n            io_lib:format(\"(fn* ~s ~s)\", [BindsStr, BodyStr]);\n        {function, _Func, _Meta} -> \"#<function>\";\n        {macro, _Eval, _Binds, _Body, _Env} -> \"#<macro>\";\n        {error, Reason} -> io_lib:format(\"error: ~s\", [Reason])\n\tend.\n\n-spec pr_list([term()], string(), string(), string(), boolean()) -> string().\npr_list(Seq, Start, End, Join, Readably) ->\n    Print = fun(Elem) ->\n        pr_str(Elem, Readably)\n    end,\n    L = string:join(lists:map(Print, Seq), Join),\n    Start ++ L ++ End.\n\npr_map(Map, Readably) ->\n    AppendKV = fun({Key, Value}, AccIn) ->\n        AccIn ++ [Key, Value]\n    end,\n    Elements = lists:foldl(AppendKV, [], maps:to_list(Map)),\n    pr_list(Elements, \"{\", \"}\", \" \", Readably).\n\nescape_str(String) ->\n    Escape = fun(C, AccIn) ->\n        case C of\n            $\"  -> [C, $\\\\|AccIn];\n            $\\\\ -> [C, $\\\\|AccIn];\n            $\\n -> [$n, $\\\\|AccIn];\n            _   -> [C|AccIn]\n        end\n    end,\n    \"\\\"\" ++ lists:reverse(lists:foldl(Escape, [], String)) ++ \"\\\"\".\n"
  },
  {
    "path": "impls/erlang/src/reader.erl",
    "content": "%%%\n%%% Reader\n%%%\n\n-module(reader).\n\n-export([read_str/1, list_to_map/1]).\n\n-record(reader, {\n    tokens=[],  % the input tokens remaining\n    tree        % the subtree parsed by a read_* function\n}).\n\n-spec read_str(string()) -> {ok, term()} | {error, term()}.\nread_str(Input) ->\n    case tokenize(Input) of\n        {ok, []} -> {ok, none};\n        {ok, Tokens} ->\n            case read_form(#reader{tokens=Tokens}) of\n                % extract the final result of parsing\n                {ok, Reader} -> {ok, Reader#reader.tree};\n                {error, Reason} -> {error, Reason}\n            end;\n        {error, Reason} -> {error, Reason}\n    end.\n\n-spec read_form(#reader{}) -> {ok, #reader{}} | {error, term()}.\nread_form(Reader) ->\n    Token = peek(Reader),\n    case Token of\n        close_list       -> {error, \"unexected ')'\"};\n        close_vector     -> {error, \"unexected ']'\"};\n        close_map        -> {error, \"unexected '}'\"};\n        open_list        -> read_list(Reader);\n        open_vector      -> read_vector(Reader);\n        open_map         -> read_map(Reader);\n        quote            -> read_quoted(Reader, Token);\n        quasiquote       -> read_quoted(Reader, Token);\n        unquote          -> read_quoted(Reader, Token);\n        'splice-unquote' -> read_quoted(Reader, Token);\n        deref            -> read_quoted(Reader, Token);\n        'with-meta'      -> read_meta(Reader);\n        _ -> read_atom(Reader)\n    end.\n\nread_list(Reader) ->\n    read_seq(Reader, $), open_list, close_list, list).\n\nread_vector(Reader) ->\n    % Erlang has no array/vector type, so just use list\n    read_seq(Reader, $], open_vector, close_vector, vector).\n\nread_map(Reader) ->\n    case read_seq(Reader, $}, open_map, close_map, map) of\n        {ok, Reader1} ->\n            {map, Map, Meta} = Reader1#reader.tree,\n            case list_to_map(Map) of\n                {error, Reason} -> {error, Reason};\n                NewMap ->\n                    Tokens = Reader1#reader.tokens,\n                    {ok, #reader{tokens=Tokens, tree={map, NewMap, Meta}}}\n            end;\n        {error, Reason} -> {error, Reason}\n    end.\n\nread_seq(Reader, CloseChar, OpenDelim, CloseDelim, Type) ->\n    {First, Reader1} = next(Reader),\n    case First of\n        OpenDelim ->\n            case read_seq_tail(Reader1, CloseChar, CloseDelim, []) of\n                {ok, Reader2} ->\n                    % prepend our type tag to the result\n                    Result = {Type, Reader2#reader.tree, nil},\n                    Reader3 = #reader{tokens=Reader2#reader.tokens, tree=Result},\n                    {ok, Reader3};\n                {error, Reason} -> {error, Reason}\n            end;\n        Bogey -> {error, io_lib:format(\"error in read_seq, expected ~p but got ~p\",\n                                       [OpenDelim, Bogey])}\n    end.\n\nread_seq_tail(Reader, CloseChar, CloseDelim, AccIn) ->\n    Token = peek(Reader),\n    case Token of\n        [] -> {error, io_lib:format(\"expected '~c', got EOF\", [CloseChar])};\n        CloseDelim ->\n            {_Token, Reader1} = next(Reader),\n            Reader2 = #reader{tokens=Reader1#reader.tokens, tree=lists:reverse(AccIn)},\n            {ok, Reader2};\n        _ ->\n            case read_form(Reader) of\n                {ok, Reader3} ->\n                    read_seq_tail(Reader3, CloseChar, CloseDelim, [Reader3#reader.tree|AccIn]);\n                {error, Reason} -> {error, Reason}\n            end\n    end.\n\n% Convert a list of key/value pairs into a map. The elements are not\n% tuples; the keys are the odd numbered members, and the values are the\n% even numbered members. Fails if list has an odd number of members.\nlist_to_map(L) ->\n    list_to_map(L, #{}).\n\nlist_to_map([], AccIn) ->\n    AccIn;\nlist_to_map([_H], _AccIn) ->\n    {error, \"odd number of hash-map keys/values\"};\nlist_to_map([K,V|T], AccIn) ->\n    list_to_map(T, maps:put(K, V, AccIn)).\n\n% Convert syntactic sugar into normalized form (e.g. ` => (quasiquote)).\nread_quoted(Reader, Token) ->\n    % discard the quoted token\n    {_T, Reader1} = next(Reader),\n    case read_form(Reader1) of\n        {ok, Reader2} ->\n            Result = {list, [{symbol, atom_to_list(Token)}, Reader2#reader.tree], nil},\n            {ok, #reader{tokens=Reader2#reader.tokens, tree=Result}};\n        {error, Reason} -> {error, Reason}\n    end.\n\nread_meta(Reader) ->\n    % discard the meta token\n    {_T, Reader1} = next(Reader),\n    case read_form(Reader1) of\n        {ok, Reader2} ->\n            M = Reader2#reader.tree,\n            case read_form(Reader2) of\n                {ok, Reader3} ->\n                    X = Reader3#reader.tree,\n                    Result = {list, [{symbol, \"with-meta\"}, X, M], nil},\n                    {ok, #reader{tokens=Reader3#reader.tokens, tree=Result}};\n                {error, Reason} -> {error, Reason}\n            end;\n        {error, Reason} -> {error, Reason}\n    end.\n\nread_atom(Reader) ->\n    {Token, Reader1} = next(Reader),\n    Result = case Token of\n        {integer, Value} -> {integer, list_to_integer(Value)};\n        {string, _String} -> Token;\n        {keyword, _Keyword} -> Token;\n        {symbol, Symbol} ->\n            case Symbol of\n                \"true\" -> true;\n                \"false\" -> false;\n                \"nil\" -> nil;\n                _ -> Token\n            end\n    end,\n    {ok, #reader{tokens=Reader1#reader.tokens, tree=Result}}.\n\npeek(Reader) ->\n    case Reader#reader.tokens of\n        [] -> [];\n        [H|_T] -> H\n    end.\n\nnext(Reader) ->\n    [H|NewTokens] = Reader#reader.tokens,\n    {H, #reader{tokens=NewTokens}}.\n\n-spec tokenize(string()) -> {ok, [term()]} | {error, term()}.\ntokenize(Input) ->\n    tokenize(Input, []).\n\n-spec tokenize(string(), [term()]) -> {ok, [term()]} | {error, term()}.\ntokenize(Input, Tokens) ->\n    case lex_single(Input) of\n        eof -> {ok, lists:reverse(Tokens)};\n        {error, Reason} -> {error, Reason};\n        {ignored, Rest} -> tokenize(Rest, Tokens);\n        {Token, Rest} -> tokenize(Rest, [Token|Tokens])\n    end.\n\nlex_single([]) ->\n    eof;\nlex_single([Char|Rest]) ->\n    case Char of\n        $( -> {open_list, Rest};\n        $) -> {close_list, Rest};\n        $[ -> {open_vector, Rest};\n        $] -> {close_vector, Rest};\n        ${ -> {open_map, Rest};\n        $} -> {close_map, Rest};\n        $\" -> lex_string(Rest, []);\n        $; -> lex_comment(Rest);\n        $: -> lex_symbol(Rest, keyword);\n        $' -> {quote, Rest};\n        $` -> {quasiquote, Rest};\n        $~ -> lex_unquote(Rest);\n        $@ -> {deref, Rest};\n        $^ -> {'with-meta', Rest};\n        N when N >= $0, N =< $9 -> lex_number(Rest, [Char]);\n        S when S == $- -> lex_minus_word(Char, Rest);\n        S when S == 32; S == $,; S == $\\r; S == $\\n; S == $\\t -> lex_spaces(Rest);\n        $\\\\ -> {error, io_lib:format(\"bare escape literal ~c~c\", [Char, hd(Rest)])};\n        $. -> {error, \"bare dot (.) not supported\"};\n        _ -> lex_symbol([Char|Rest], symbol)\n    end.\n\nlex_comment([]) ->\n    {ignored, []};\nlex_comment([C|Rest]) when C == $\\r; C == $\\n ->\n    {ignored, Rest};\nlex_comment([_C|Rest]) ->\n    lex_comment(Rest).\n\nlex_spaces([C|Rest]) when C == 32; C == $,; C == $\\r; C == $\\n; C == $\\t ->\n    lex_spaces(Rest);\nlex_spaces(Rest) ->\n    {ignored, Rest}.\n\nlex_string([], _String) ->\n    {error, \"expected '\\\"', got EOF\"};\nlex_string([$\\\\,Escaped|Rest], String) ->\n    % unescape the string while building it\n    case Escaped of\n        []  -> {error, \"end of string reached in escape\"};\n        $n  -> lex_string(Rest, [$\\n|String]);\n        _   -> lex_string(Rest, [Escaped|String])\n    end;\nlex_string([$\"|Rest], String) ->\n    {{string, lists:reverse(String)}, Rest};\nlex_string([C|Rest], String) ->\n    lex_string(Rest, [C|String]).\n\nlex_number([N|Rest], Number) when N >= $0, N =< $9 ->\n    lex_number(Rest, [N|Number]);\nlex_number(Rest, Number) ->\n    {{integer, lists:reverse(Number)}, Rest}.\n\nlex_minus_word(Minus, [N|Rest]) when N >= $0, N =< $9 ->\n    lex_number([N|Rest], [Minus]);\nlex_minus_word(Minus, Rest) ->\n    lex_symbol([Minus|Rest], symbol).\n\n% Lex the remainder of either a keyword or a symbol. The Type is used as\n% the tag for the returned tuple (e.g. the atoms keyword or symbol).\nlex_symbol(Input, Type) ->\n    IsSymbol = fun(C) ->\n        is_letter(C) orelse is_digit(C) orelse is_symbol(C)\n    end,\n    Symbol = lists:takewhile(IsSymbol, Input),\n    case Symbol of\n        [] -> {error, io_lib:format(\"invalid symbol: ~10s\", [Input])};\n        _ -> {{Type, Symbol}, lists:sublist(Input, length(Symbol) + 1, length(Input))}\n    end.\n\nis_digit(C) ->\n    C >= $0 andalso C =< $9.\n\nis_letter(C) ->\n    C >= $a andalso C =< $z orelse C >= $A andalso C =< $Z.\n\nis_symbol(C) ->\n    lists:member(C, \"!#$%&*+-/:<=>?@^_|\\~\").\n\nlex_unquote([$@|Rest]) ->\n    {'splice-unquote', Rest};\nlex_unquote(Rest) ->\n    {unquote, Rest}.\n"
  },
  {
    "path": "impls/erlang/src/step0_repl.erl",
    "content": "%%%\n%%% Step 0: REPL\n%%%\n\n-module(step0_repl).\n\n-export([main/1]).\n\nmain(_) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof ->\n            % break out of the loop\n            io:format(\"~n\"),\n            ok;\n        {error, Reason} ->\n            io:format(\"Error reading input: ~p~n\", [Reason]),\n            exit(ioerr);\n        Line ->\n            io:format(\"~s~n\", [print(eval(read(string:strip(Line, both, $\\n))))]),\n            main(\"\")\n    end.\n\nread(String) ->\n    String.\n\neval(String) ->\n    String.\n\nprint(String) ->\n    String.\n"
  },
  {
    "path": "impls/erlang/src/step1_read_print.erl",
    "content": "%%%\n%%% Step 1: read/print\n%%%\n\n-module(step1_read_print).\n\n-export([main/1]).\n\nmain(_) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof ->\n            % break out of the loop\n            io:format(\"~n\"),\n            ok;\n        {error, Reason} ->\n            io:format(\"Error reading input: ~s~n\", [Reason]),\n            exit(ioerr);\n        Line ->\n            print(eval(read(string:strip(Line, both, $\\n)))),\n            main(\"\")\n    end.\n\nread(String) ->\n    case reader:read_str(String) of\n        {ok, Value} -> Value;\n        {error, Reason} -> io:format(\"error: ~s~n\", [Reason]), nil\n    end.\n\neval(Value) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [printer:pr_str(Value, true)]).\n"
  },
  {
    "path": "impls/erlang/src/step2_eval.erl",
    "content": "%%%\n%%% Step 2: eval\n%%%\n\n-module(step2_eval).\n\n-export([main/1]).\n\nmain(_) ->\n    Env = #{\n        \"+\" => fun core:int_add/1,\n        \"-\" => fun core:int_sub/1,\n        \"*\" => fun core:int_mul/1,\n        \"/\" => fun core:int_div/1\n    },\n    loop(Env).\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof ->\n            % break out of the loop\n            io:format(\"~n\"),\n            ok;\n        {error, Reason} ->\n            io:format(\"Error reading input: ~s~n\", [Reason]),\n            exit(ioerr);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    AST = read(Input),\n    try eval(AST, Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(String) ->\n    case reader:read_str(String) of\n        {ok, Value} -> Value;\n        {error, Reason} -> io:format(\"error: ~s~n\", [Reason]), nil\n    end.\n\neval({list, [], _Meta}=AST, _Env) ->\n    AST;\neval({list, List, _Meta}, Env) ->\n    case lists:map(fun(Elem) -> eval(Elem, Env) end, List) of\n        [F|Args] -> erlang:apply(F, [Args]);\n        _ -> {error, \"expected a list\"}\n    end;\neval({symbol, Sym}, Env) ->\n    case maps:is_key(Sym, Env) of\n        true  -> maps:get(Sym, Env);\n        false -> error(io_lib:format(\"'~s' not found\", [Sym]))\n    end;\neval({vector, V, Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, V), Meta};\neval({map, M, Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), Meta};\neval(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n"
  },
  {
    "path": "impls/erlang/src/step3_env.erl",
    "content": "%%%\n%%% Step 3: env\n%%%\n\n-module(step3_env).\n\n-export([main/1]).\n\nmain(_) ->\n    loop(core:ns()).\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    env:set(Env, {symbol, A1}, Result),\n    Result;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    % (let* (p (+ 2 3) q (+ 2 p)) (+ p q))\n    % ;=>12\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n"
  },
  {
    "path": "impls/erlang/src/step4_if_fn_do.erl",
    "content": "%%%\n%%% Step 4: if, fn, do\n%%%\n\n-module(step4_if_fn_do).\n\n-export([main/1]).\n\nmain(_) ->\n    Env = core:ns(),\n    % define the not function using mal itself\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    loop(Env).\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    env:set(Env, {symbol, A1}, Result),\n    Result;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env,  nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n"
  },
  {
    "path": "impls/erlang/src/step5_tco.erl",
    "content": "%%%\n%%% Step 5: Tail call optimization\n%%%\n\n-module(step5_tco).\n\n-export([main/1]).\n\nmain(_) ->\n    Env = core:ns(),\n    % define the not function using mal itself\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    loop(Env).\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    env:set(Env, {symbol, A1}, Result),\n    Result;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env,  nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n"
  },
  {
    "path": "impls/erlang/src/step6_file.erl",
    "content": "%%%\n%%% Step 6: File and evil\n%%%\n\n-module(step6_file).\n\n-export([main/1]).\n\nmain([File|Args]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [{string,Arg} || Arg <- Args], nil}),\n    rep(\"(load-file \\\"\" ++ File ++ \"\\\")\", Env);\nmain([]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [], nil}),\n    loop(Env).\n\ninit() ->\n    Env = core:ns(),\n    % define the load-file and not functions using mal itself\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    eval(read(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"), Env),\n    Env.\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    env:set(Env, {symbol, A1}, Result),\n    Result;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env,  nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [{symbol, \"eval\"}, AST], _Meta}, Env) ->\n    % Must use the root environment so the variables set within the parsed\n    % expression will be visible within the repl.\n    eval(eval(AST, Env), env:root(Env));\neval_list({list, [{symbol, \"eval\"}|_], _Meta}, _Env) ->\n    error(\"eval requires 1 argument\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n"
  },
  {
    "path": "impls/erlang/src/step7_quote.erl",
    "content": "%%%\n%%% Step 7: Quoting\n%%%\n\n-module(step7_quote).\n\n-export([main/1]).\n\nmain([File|Args]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [{string,Arg} || Arg <- Args], nil}),\n    rep(\"(load-file \\\"\" ++ File ++ \"\\\")\", Env);\nmain([]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [], nil}),\n    loop(Env).\n\ninit() ->\n    Env = core:ns(),\n    % define the load-file and not functions using mal itself\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    eval(read(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"), Env),\n    Env.\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    env:set(Env, {symbol, A1}, Result),\n    Result;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env,  nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [{symbol, \"eval\"}, AST], _Meta}, Env) ->\n    % Must use the root environment so the variables set within the parsed\n    % expression will be visible within the repl.\n    eval(eval(AST, Env), env:root(Env));\neval_list({list, [{symbol, \"eval\"}|_], _Meta}, _Env) ->\n    error(\"eval requires 1 argument\");\neval_list({list, [{symbol, \"quote\"}, AST], _Meta}, _Env) ->\n    AST;\neval_list({list, [{symbol, \"quote\"}|_], _Meta}, _Env) ->\n    error(\"quote requires 1 argument\");\neval_list({list, [{symbol, \"quasiquote\"}, AST], _Meta}, Env) ->\n    eval(quasiquote(AST), Env);\neval_list({list, [{symbol, \"quasiquote\"}|_], _Meta}, _Env) ->\n    error(\"quasiquote requires 1 argument\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n\nqqLoop ({list, [{symbol, \"splice-unquote\"}, Arg], _Meta}, Acc) ->\n    {list, [{symbol, \"concat\"}, Arg, Acc], nil};\nqqLoop({list, [{symbol, \"splice-unquote\"}|_], _Meta}, _Acc) ->\n    {error, \"splice-unquote requires an argument\"};\nqqLoop(Elt, Acc) ->\n    {list, [{symbol, \"cons\"}, quasiquote(Elt), Acc], nil}.\n\nquasiquote({list, [{symbol, \"unquote\"}, Arg], _Meta}) ->\n    Arg;\nquasiquote({list, [{symbol, \"unquote\"}|_], _Meta}) ->\n    error(\"unquote requires 1 argument\");\nquasiquote({list, List, _Meta}) ->\n    lists:foldr(fun qqLoop/2, {list, [], nil}, List);\nquasiquote({vector, List, _Meta}) ->\n    {list, [{symbol, \"vec\"}, lists:foldr(fun qqLoop/2, {list, [], nil}, List)], nil};\nquasiquote({symbol, _Symbol} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote({map, _Map, _Meta} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote(Arg) ->\n    Arg.\n"
  },
  {
    "path": "impls/erlang/src/step8_macros.erl",
    "content": "%%%\n%%% Step 8: Macros\n%%%\n\n-module(step8_macros).\n\n-export([main/1]).\n\nmain([File|Args]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [{string,Arg} || Arg <- Args], nil}),\n    rep(\"(load-file \\\"\" ++ File ++ \"\\\")\", Env);\nmain([]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [], nil}),\n    loop(Env).\n\ninit() ->\n    Env = core:ns(),\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    eval(read(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"), Env),\n    eval(read(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"), Env),\n    Env.\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    case Result of\n        {error, _R1} -> Result;\n        _ ->\n            env:set(Env, {symbol, A1}, Result),\n            Result\n    end;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env,  nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [{symbol, \"eval\"}, AST], _Meta}, Env) ->\n    % Must use the root environment so the variables set within the parsed\n    % expression will be visible within the repl.\n    eval(eval(AST, Env), env:root(Env));\neval_list({list, [{symbol, \"eval\"}|_], _Meta}, _Env) ->\n    error(\"eval requires 1 argument\");\neval_list({list, [{symbol, \"quote\"}, AST], _Meta}, _Env) ->\n    AST;\neval_list({list, [{symbol, \"quote\"}|_], _Meta}, _Env) ->\n    error(\"quote requires 1 argument\");\neval_list({list, [{symbol, \"quasiquote\"}, AST], _Meta}, Env) ->\n    eval(quasiquote(AST), Env);\neval_list({list, [{symbol, \"quasiquote\"}|_], _Meta}, _Env) ->\n    error(\"quasiquote requires 1 argument\");\neval_list({list, [{symbol, \"defmacro!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    case eval(A2, Env) of\n        {closure, _Eval, Binds, Body, CE, _M1} ->\n            Result = {macro, fun eval/2, Binds, Body, CE},\n            env:set(Env, {symbol, A1}, Result),\n            Result;\n        Result -> env:set(Env, {symbol, A1}, Result), Result\n    end,\n    Result;\neval_list({list, [{symbol, \"defmacro!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"defmacro! called with non-symbol\");\neval_list({list, [{symbol, \"defmacro!\"}|_], _Meta}, _Env) ->\n    error(\"defmacro! requires exactly two arguments\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {macro, _Eval, Binds, Body, ME} ->\n            NewEnv = env:new(ME),\n            env:bind(NewEnv, Binds, lists:flatten([Args])),\n            NewAst = eval(Body, NewEnv),\n            eval(NewAst, Env);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n\nqqLoop ({list, [{symbol, \"splice-unquote\"}, Arg], _Meta}, Acc) ->\n    {list, [{symbol, \"concat\"}, Arg, Acc], nil};\nqqLoop({list, [{symbol, \"splice-unquote\"}|_], _Meta}, _Acc) ->\n    {error, \"splice-unquote requires an argument\"};\nqqLoop(Elt, Acc) ->\n    {list, [{symbol, \"cons\"}, quasiquote(Elt), Acc], nil}.\n\nquasiquote({list, [{symbol, \"unquote\"}, Arg], _Meta}) ->\n    Arg;\nquasiquote({list, [{symbol, \"unquote\"}|_], _Meta}) ->\n    error(\"unquote requires 1 argument\");\nquasiquote({list, List, _Meta}) ->\n    lists:foldr(fun qqLoop/2, {list, [], nil}, List);\nquasiquote({vector, List, _Meta}) ->\n    {list, [{symbol, \"vec\"}, lists:foldr(fun qqLoop/2, {list, [], nil}, List)], nil};\nquasiquote({symbol, _Symbol} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote({map, _Map, _Meta} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote(Arg) ->\n    Arg.\n"
  },
  {
    "path": "impls/erlang/src/step9_try.erl",
    "content": "%%%\n%%% Step 9: Try\n%%%\n\n-module(step9_try).\n\n-export([main/1]).\n\nmain([File|Args]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [{string,Arg} || Arg <- Args], nil}),\n    rep(\"(load-file \\\"\" ++ File ++ \"\\\")\", Env);\nmain([]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [], nil}),\n    loop(Env).\n\ninit() ->\n    Env = core:ns(),\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    eval(read(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"), Env),\n    eval(read(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"), Env),\n    Env.\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true);\n        throw:Reason -> printer:pr_str({error, printer:pr_str(Reason, true)}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    case Result of\n        {error, _R1} -> Result;\n        _ ->\n            env:set(Env, {symbol, A1}, Result),\n            Result\n    end;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [{symbol, \"eval\"}, AST], _Meta}, Env) ->\n    % Must use the root environment so the variables set within the parsed\n    % expression will be visible within the repl.\n    eval(eval(AST, Env), env:root(Env));\neval_list({list, [{symbol, \"eval\"}|_], _Meta}, _Env) ->\n    error(\"eval requires 1 argument\");\neval_list({list, [{symbol, \"quote\"}, AST], _Meta}, _Env) ->\n    AST;\neval_list({list, [{symbol, \"quote\"}|_], _Meta}, _Env) ->\n    error(\"quote requires 1 argument\");\neval_list({list, [{symbol, \"quasiquote\"}, AST], _Meta}, Env) ->\n    eval(quasiquote(AST), Env);\neval_list({list, [{symbol, \"quasiquote\"}|_], _Meta}, _Env) ->\n    error(\"quasiquote requires 1 argument\");\neval_list({list, [{symbol, \"defmacro!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    case eval(A2, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            Result = {macro, fun eval/2, Binds, Body, CE},\n            env:set(Env, {symbol, A1}, Result),\n            Result;\n        Result -> env:set(Env, {symbol, A1}, Result), Result\n    end,\n    Result;\neval_list({list, [{symbol, \"defmacro!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"defmacro! called with non-symbol\");\neval_list({list, [{symbol, \"defmacro!\"}|_], _Meta}, _Env) ->\n    error(\"defmacro! requires exactly two arguments\");\neval_list({list, [{symbol, \"try*\"}, A, {list, [{symbol, \"catch*\"}, B, C], _M1}], _M2}, Env) ->\n    try eval(A, Env) of\n        Result -> Result\n    catch\n        error:Reason ->\n            NewEnv = env:new(Env),\n            env:bind(NewEnv, [B], [{string, Reason}]),\n            eval(C, NewEnv);\n        throw:Reason ->\n            NewEnv = env:new(Env),\n            env:bind(NewEnv, [B], [Reason]),\n            eval(C, NewEnv)\n    end;\neval_list({list, [{symbol, \"try*\"}, AST], _Meta}, Env) ->\n    eval(AST, Env);\neval_list({list, [{symbol, \"try*\"}|_], _Meta}, _Env) ->\n    error(\"try*/catch* must be of the form (try* A (catch* B C))\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {macro, _Eval, Binds, Body, ME} ->\n            NewEnv = env:new(ME),\n            env:bind(NewEnv, Binds, lists:flatten([Args])),\n            NewAst = eval(Body, NewEnv),\n            eval(NewAst, Env);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n\nqqLoop ({list, [{symbol, \"splice-unquote\"}, Arg], _Meta}, Acc) ->\n    {list, [{symbol, \"concat\"}, Arg, Acc], nil};\nqqLoop({list, [{symbol, \"splice-unquote\"}|_], _Meta}, _Acc) ->\n    {error, \"splice-unquote requires an argument\"};\nqqLoop(Elt, Acc) ->\n    {list, [{symbol, \"cons\"}, quasiquote(Elt), Acc], nil}.\n\nquasiquote({list, [{symbol, \"unquote\"}, Arg], _Meta}) ->\n    Arg;\nquasiquote({list, [{symbol, \"unquote\"}|_], _Meta}) ->\n    error(\"unquote requires 1 argument\");\nquasiquote({list, List, _Meta}) ->\n    lists:foldr(fun qqLoop/2, {list, [], nil}, List);\nquasiquote({vector, List, _Meta}) ->\n    {list, [{symbol, \"vec\"}, lists:foldr(fun qqLoop/2, {list, [], nil}, List)], nil};\nquasiquote({symbol, _Symbol} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote({map, _Map, _Meta} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote(Arg) ->\n    Arg.\n"
  },
  {
    "path": "impls/erlang/src/stepA_mal.erl",
    "content": "%%%\n%%% Step A: Mutation, Self-hosting and Interop\n%%%\n\n-module(stepA_mal).\n\n-export([main/1]).\n\nmain([File|Args]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [{string,Arg} || Arg <- Args], nil}),\n    rep(\"(load-file \\\"\" ++ File ++ \"\\\")\", Env);\nmain([]) ->\n    Env = init(),\n    env:set(Env, {symbol, \"*ARGV*\"}, {list, [], nil}),\n    eval(read(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"), Env),\n    loop(Env).\n\ninit() ->\n    Env = core:ns(),\n    eval(read(\"(def! *host-language* \\\"Erlang\\\")\"), Env),\n    eval(read(\"(def! not (fn* (a) (if a false true)))\"), Env),\n    eval(read(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"), Env),\n    eval(read(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"), Env),\n    Env.\n\nloop(Env) ->\n    case io:get_line(standard_io, \"user> \") of\n        eof -> io:format(\"~n\");\n        {error, Reason} -> exit(Reason);\n        Line ->\n            print(rep(string:strip(Line, both, $\\n), Env)),\n            loop(Env)\n    end.\n\nrep(Input, Env) ->\n    try eval(read(Input), Env) of\n        none -> none;\n        Result -> printer:pr_str(Result, true)\n    catch\n        error:Reason -> printer:pr_str({error, Reason}, true);\n        throw:Reason -> printer:pr_str({error, printer:pr_str(Reason, true)}, true)\n    end.\n\nread(Input) ->\n    case reader:read_str(Input) of\n        {ok, Value} -> Value;\n        {error, Reason} -> error(Reason)\n    end.\n\neval(Value, Env) ->\n    case env:find(Env, {symbol, \"DEBUG-EVAL\"}) of\n        nil -> none;\n        Env2 ->\n            case env:get(Env2, {symbol, \"DEBUG-EVAL\"}) of\n                Cond when Cond == false orelse Cond == nil -> none;\n                _ -> io:format(\"EVAL: ~s~n\", [printer:pr_str(Value, true)])\n            end\n    end,\n    eval_ast(Value, Env).\n\neval_list({list, [], _Meta}=AST, _Env) ->\n    AST;\neval_list({list, [{symbol, \"def!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    Result = eval(A2, Env),\n    case Result of\n        {error, _R1} -> Result;\n        _ ->\n            env:set(Env, {symbol, A1}, Result),\n            Result\n    end;\neval_list({list, [{symbol, \"def!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"def! called with non-symbol\");\neval_list({list, [{symbol, \"def!\"}|_], _Meta}, _Env) ->\n    error(\"def! requires exactly two arguments\");\neval_list({list, [{symbol, \"let*\"}, A1, A2], _Meta}, Env) ->\n    NewEnv = env:new(Env),\n    let_star(NewEnv, A1),\n    eval(A2, NewEnv);\neval_list({list, [{symbol, \"let*\"}|_], _Meta}, _Env) ->\n    error(\"let* requires exactly two arguments\");\neval_list({list, [{symbol, \"do\"}|Args], _Meta}, Env) ->\n    lists:map(fun(Elem) -> eval(Elem, Env) end, lists:droplast(Args)),\n    eval(lists:last(Args), Env);\neval_list({list, [{symbol, \"if\"}, Test, Consequent|Alternate], _Meta}, Env) ->\n    case eval(Test, Env) of\n        Cond when Cond == false orelse Cond == nil ->\n            case Alternate of\n                []  -> nil;\n                [A] -> eval(A, Env);\n                _   -> error(\"if takes 2 or 3 arguments\")\n            end;\n        _ -> eval(Consequent, Env)\n    end;\neval_list({list, [{symbol, \"if\"}|_], _Meta}, _Env) ->\n    error(\"if requires test and consequent\");\neval_list({list, [{symbol, \"fn*\"}, {vector, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}, {list, Binds, _M1}, Body], _Meta}, Env) ->\n    {closure, fun eval/2, Binds, Body, Env, nil};\neval_list({list, [{symbol, \"fn*\"}|_], _Meta}, _Env) ->\n    error(\"fn* requires 2 arguments\");\neval_list({list, [{symbol, \"eval\"}, AST], _Meta}, Env) ->\n    % Must use the root environment so the variables set within the parsed\n    % expression will be visible within the repl.\n    eval(eval(AST, Env), env:root(Env));\neval_list({list, [{symbol, \"eval\"}|_], _Meta}, _Env) ->\n    error(\"eval requires 1 argument\");\neval_list({list, [{symbol, \"quote\"}, AST], _Meta}, _Env) ->\n    AST;\neval_list({list, [{symbol, \"quote\"}|_], _Meta}, _Env) ->\n    error(\"quote requires 1 argument\");\neval_list({list, [{symbol, \"quasiquote\"}, AST], _Meta}, Env) ->\n    eval(quasiquote(AST), Env);\neval_list({list, [{symbol, \"quasiquote\"}|_], _Meta}, _Env) ->\n    error(\"quasiquote requires 1 argument\");\neval_list({list, [{symbol, \"defmacro!\"}, {symbol, A1}, A2], _Meta}, Env) ->\n    case eval(A2, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            Result = {macro, fun eval/2, Binds, Body, CE},\n            env:set(Env, {symbol, A1}, Result),\n            Result;\n        Result -> env:set(Env, {symbol, A1}, Result), Result\n    end,\n    Result;\neval_list({list, [{symbol, \"defmacro!\"}, _A1, _A2], _Meta}, _Env) ->\n    error(\"defmacro! called with non-symbol\");\neval_list({list, [{symbol, \"defmacro!\"}|_], _Meta}, _Env) ->\n    error(\"defmacro! requires exactly two arguments\");\neval_list({list, [{symbol, \"try*\"}, A, {list, [{symbol, \"catch*\"}, B, C], _M1}], _M2}, Env) ->\n    try eval(A, Env) of\n        Result -> Result\n    catch\n        error:Reason ->\n            NewEnv = env:new(Env),\n            env:bind(NewEnv, [B], [{string, Reason}]),\n            eval(C, NewEnv);\n        throw:Reason ->\n            NewEnv = env:new(Env),\n            env:bind(NewEnv, [B], [Reason]),\n            eval(C, NewEnv)\n    end;\neval_list({list, [{symbol, \"try*\"}, AST], _Meta}, Env) ->\n    eval(AST, Env);\neval_list({list, [{symbol, \"try*\"}|_], _Meta}, _Env) ->\n    error(\"try*/catch* must be of the form (try* A (catch* B C))\");\neval_list({list, [A0 | Args], _Meta}, Env) ->\n    case eval(A0, Env) of\n        {closure, _Eval, Binds, Body, CE, _MC} ->\n            % The args may be a single element or a list, so always make it\n            % a list and then flatten it so it becomes a list.\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            NewEnv = env:new(CE),\n            env:bind(NewEnv, Binds, lists:flatten([A])),\n            eval(Body, NewEnv);\n        {function, F, _MF} ->\n            A = lists:map(fun(Elem) -> eval(Elem, Env) end, Args),\n            erlang:apply(F, [A]);\n        {macro, _Eval, Binds, Body, ME} ->\n            NewEnv = env:new(ME),\n            env:bind(NewEnv, Binds, lists:flatten([Args])),\n            NewAst = eval(Body, NewEnv),\n            eval(NewAst, Env);\n        {error, Reason} -> {error, Reason}\n    end.\n\neval_ast({symbol, _Sym}=Value, Env) ->\n    env:get(Env, Value);\neval_ast({list, Seq, Meta}, Env) ->\n    eval_list({list, Seq, Meta}, Env);\neval_ast({vector, Seq, _Meta}, Env) ->\n    {vector, lists:map(fun(Elem) -> eval(Elem, Env) end, Seq), nil};\neval_ast({map, M, _Meta}, Env) ->\n    {map, maps:map(fun(_Key, Val) -> eval(Val, Env) end, M), nil};\neval_ast(Value, _Env) ->\n    Value.\n\nprint(none) ->\n    % if nothing meaningful was entered, print nothing at all\n    ok;\nprint(Value) ->\n    io:format(\"~s~n\", [Value]).\n\nlet_star(Env, Bindings) ->\n    Bind = fun({Name, Expr}) ->\n        case Name of\n            {symbol, _Sym} -> env:set(Env, Name, eval(Expr, Env));\n            _ -> error(\"let* with non-symbol binding\")\n        end\n    end,\n    case Bindings of\n        {Type, Binds, _Meta} when Type == list orelse Type == vector ->\n            case list_to_proplist(Binds) of\n                {error, Reason} -> error(Reason);\n                Props -> lists:foreach(Bind, Props)\n            end;\n        _ -> error(\"let* with non-list bindings\")\n    end.\n\nlist_to_proplist(L) ->\n    list_to_proplist(L, []).\n\nlist_to_proplist([], AccIn) ->\n    lists:reverse(AccIn);\nlist_to_proplist([_H], _AccIn) ->\n    {error, \"mismatch in let* name/value bindings\"};\nlist_to_proplist([K,V|T], AccIn) ->\n    list_to_proplist(T, [{K, V}|AccIn]).\n\nqqLoop ({list, [{symbol, \"splice-unquote\"}, Arg], _Meta}, Acc) ->\n    {list, [{symbol, \"concat\"}, Arg, Acc], nil};\nqqLoop({list, [{symbol, \"splice-unquote\"}|_], _Meta}, _Acc) ->\n    {error, \"splice-unquote requires an argument\"};\nqqLoop(Elt, Acc) ->\n    {list, [{symbol, \"cons\"}, quasiquote(Elt), Acc], nil}.\n\nquasiquote({list, [{symbol, \"unquote\"}, Arg], _Meta}) ->\n    Arg;\nquasiquote({list, [{symbol, \"unquote\"}|_], _Meta}) ->\n    error(\"unquote requires 1 argument\");\nquasiquote({list, List, _Meta}) ->\n    lists:foldr(fun qqLoop/2, {list, [], nil}, List);\nquasiquote({vector, List, _Meta}) ->\n    {list, [{symbol, \"vec\"}, lists:foldr(fun qqLoop/2, {list, [], nil}, List)], nil};\nquasiquote({symbol, _Symbol} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote({map, _Map, _Meta} = Arg) ->\n    {list, [{symbol, \"quote\"}, Arg], nil};\nquasiquote(Arg) ->\n    Arg.\n"
  },
  {
    "path": "impls/erlang/src/types.erl",
    "content": "%%%\n%%% Types and their functions\n%%%\n\n-module(types).\n-compile(export_all).\n\nlist(Args) ->\n\t{list, Args, nil}.\n\nlist_p([Args]) ->\n    case Args of\n        {list, _L, _M} -> true;\n        _ -> false\n    end;\nlist_p([]) ->\n    {error, \"list? called with no arguments\"};\nlist_p(_) ->\n    {error, \"list? expects one list argument\"}.\n\nfunc(Func) ->\n    {function, Func, nil}.\n\nsymbol_p([{symbol, _S}]) ->\n    true;\nsymbol_p([_A]) ->\n    false;\nsymbol_p(_) ->\n    {error, \"symbol? takes a single argument\"}.\n\nsymbol([{string, Name}]) ->\n    {symbol, Name};\nsymbol(_) ->\n    {error, \"symbol expects a single string argument\"}.\n\nstring_p([{string, _S}]) ->\n    true;\nstring_p([_A]) ->\n    false;\nstring_p(_) ->\n    {error, \"string? takes a single argument\"}.\n\nkeyword_p([{keyword, _K}]) ->\n    true;\nkeyword_p([_A]) ->\n    false;\nkeyword_p(_) ->\n    {error, \"keyword? takes a single argument\"}.\n\nkeyword([{string,  Name}]) -> {keyword, Name};\nkeyword([{keyword, Name}]) -> {keyword, Name};\nkeyword([_])               -> {error, \"keyword: expectst a keyword or string.\"};\nkeyword(_)                 -> {error, \"keyword: takes a single argument.\"}.\n\nvector_p([{vector, _V, _Meta}]) ->\n    true;\nvector_p([_]) ->\n    false;\nvector_p(_) ->\n    {error, \"vector? takes a single argument\"}.\n\nvector(Args) ->\n    {vector, Args, nil}.\n\nhash_map(Args) ->\n    {map, reader:list_to_map(Args), nil}.\n\nmap_p([{map, _M, _Meta}]) ->\n    true;\nmap_p([_]) ->\n    false;\nmap_p(_) ->\n    {error, \"map? takes a single argument\"}.\n\nassoc([{map, Map, Meta}|Args]) ->\n    case reader:list_to_map(Args) of\n        {error, Reason} -> {error, Reason};\n        Addend -> {map, maps:merge(Map, Addend), Meta}\n    end;\nassoc(_) ->\n    {error, \"assoc expects a map argument followed by pairs\"}.\n\ndissoc([{map, Map, Meta}|Keys]) ->\n    {map, lists:foldl(fun(Key, AccIn) -> maps:remove(Key, AccIn) end, Map, Keys), Meta};\ndissoc(_) ->\n    {error, \"dissoc expects a map argument followed by keys\"}.\n\nmap_get([{map, Map, _Meta}, Key]) ->\n    maps:get(Key, Map, nil);\nmap_get([_Thing1, _Thing2]) ->\n    nil;\nmap_get(_) ->\n    {error, \"get expects a map argument followed by key\"}.\n\ncontains_p([{map, Map, _Meta}, Key]) ->\n    maps:is_key(Key, Map);\ncontains_p(_) ->\n    {error, \"contains? expects a map argument followed by key\"}.\n\nmap_keys([{map, Map, _Meta}]) ->\n    {list, maps:keys(Map), nil};\nmap_keys(_) ->\n    {error, \"keys expects a map argument\"}.\n\nmap_values([{map, Map, _Meta}]) ->\n    {list, maps:values(Map), nil};\nmap_values(_) ->\n    {error, \"vals expects a map argument\"}.\n\nsequential_p([{Type, _L, _M}]) when Type == list orelse  Type == vector ->\n    true;\nsequential_p([_]) ->\n    false;\nsequential_p(_) ->\n    {error, \"sequential? expects a single argument\"}.\n\natom([Atom]) ->\n    {atom, atom:new(Atom)};\natom(_) ->\n    {error, \"atom expects a single argument\"}.\n\natom_p([{atom, _A}]) ->\n    true;\natom_p([_]) ->\n    false;\natom_p(_) ->\n    {error, \"atom? expects a single argument\"}.\n\nderef([{atom, Atom}]) ->\n    atom:deref(Atom);\nderef(_) ->\n    {error, \"deref expects a single atom argument\"}.\n\nreset([{atom, Atom}, Value]) ->\n    atom:reset(Atom, Value);\nreset(_) ->\n    {error, \"reset expects an atom and a value\"}.\n\nswap([{atom, Atom}, {closure, Eval, Binds, Body, Env, _MC}|Args]) ->\n    NewEnv = env:new(Env),\n    Values = [atom:deref(Atom) | Args],\n    env:bind(NewEnv, Binds, Values),\n    atom:reset(Atom, Eval(Body, NewEnv));\nswap([{atom, Atom}, {function, F, _MF}|Args]) ->\n    atom:reset(Atom, erlang:apply(F, [[atom:deref(Atom) | Args]]));\nswap(_) ->\n    {error, \"atom expects an atom, function, and optional arguments\"}.\n\nmeta([{T, _List, Meta}]) when T == list orelse T == vector orelse T == map ->\n    Meta;\nmeta([{closure, _Eval, _Binds, _Body, _Env, Meta}]) ->\n    Meta;\nmeta([{function, _Func, Meta}]) ->\n    Meta;\nmeta(_) ->\n    {error, \"meta expects a single collection or function argument\"}.\n\nwith_meta([{T, Seq, _M}, Meta]) when T == list orelse T == vector orelse T == map ->\n    {T, Seq, Meta};\nwith_meta([{closure, Eval, Binds, Body, Env, _M}, Meta]) ->\n    {closure, Eval, Binds, Body, Env, Meta};\nwith_meta([{function, Func, _Meta}, Meta]) ->\n    {function, Func, Meta}.\n"
  },
  {
    "path": "impls/erlang/tests/step5_tco.mal",
    "content": ";; Erlang: skipping non-TCO recursion\n;; Reason: Erlang has TCO, test always completes.\n"
  },
  {
    "path": "impls/es6/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ libreadline-dev nodejs npm\n\nENV NPM_CONFIG_CACHE /mal/.npm\n"
  },
  {
    "path": "impls/es6/Makefile",
    "content": "SOURCES_BASE = node_readline.js types.mjs reader.mjs printer.mjs\nSOURCES_LISP = env.mjs core.mjs stepA_mal.mjs\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nSTEPS = step0_repl.mjs step1_read_print.mjs step2_eval.mjs step3_env.mjs \\\n\tstep4_if_fn_do.mjs step5_tco.mjs step6_file.mjs \\\n\tstep7_quote.mjs step8_macros.mjs step9_try.mjs stepA_mal.mjs\n\nall: node_modules\n\ndist: mal.js mal\n\nnode_modules:\n\tnpm install\n\n$(STEPS): node_modules\n\nmal.js: $(SOURCES)\n\tcat $+ | sed 's/^export //' | grep -v \"^import \" >> $@\n\nmal: mal.js\n\techo \"#!/usr/bin/env node\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\n\nclean:\n\trm -f mal.js mal\n\trm -rf node_modules\n"
  },
  {
    "path": "impls/es6/core.mjs",
    "content": "import { _equal_Q, _clone, _keyword, _keyword_Q } from './types.mjs'\nimport { _list_Q, Vector, _assoc_BANG, Atom } from './types.mjs'\nimport { pr_str } from './printer.mjs'\nimport rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { read_str } from './reader.mjs'\nimport { readFileSync } from 'fs'\n\nfunction _error(e) { throw new Error(e) }\n\n// String functions\nfunction slurp(f) {\n    if (typeof process !== 'undefined') {\n        return readFileSync(f, 'utf-8')\n    } else {\n        var req = new XMLHttpRequest()\n        req.open('GET', f, false)\n        req.send()\n        if (req.status !== 200) {\n            _error(`Failed to slurp file: ${f}`)\n        }\n        return req.responseText\n    }\n}\n\n// Sequence functions\nfunction seq(obj) {\n    if (_list_Q(obj)) {\n        return obj.length > 0 ? obj : null\n    } else if (obj instanceof Vector) {\n        return obj.length > 0 ? Array.from(obj.slice(0)) : null\n    } else if (typeof obj === \"string\" && !_keyword_Q(obj)) {\n        return obj.length > 0 ? obj.split('') : null\n    } else if (obj === null) {\n        return null\n    } else {\n        _error('seq: called on non-sequence')\n    }\n}\n\n// core_ns is namespace of type functions\nexport const core_ns = new Map([\n    ['=', _equal_Q],\n    ['throw', a => { throw a }],\n\n    ['nil?', a => a === null],\n    ['true?', a => a === true],\n    ['false?', a => a === false],\n    ['number?', a => typeof a === 'number'],\n    ['string?', a => typeof a === \"string\" && !_keyword_Q(a)],\n    ['symbol', a => Symbol.for(a)],\n    ['symbol?', a => typeof a === 'symbol'],\n    ['keyword', _keyword],\n    ['keyword?', _keyword_Q],\n    ['fn?', a => typeof a === 'function' && !a.ismacro ],\n    ['macro?', a => typeof a === 'function' && !!a.ismacro ],\n\n    ['pr-str', (...a) => a.map(e => pr_str(e,1)).join(' ')],\n    ['str', (...a) => a.map(e => pr_str(e,0)).join('')],\n    ['prn', (...a) => console.log(...a.map(e => pr_str(e,1))) || null],\n    ['println', (...a) => console.log(...a.map(e => pr_str(e,0))) || null],\n    ['read-string', read_str],\n    ['readline', readline],\n    ['slurp', slurp],\n\n    ['<' , (a,b) => a<b],\n    ['<=', (a,b) => a<=b],\n    ['>' , (a,b) => a>b],\n    ['>=', (a,b) => a>=b],\n    ['+' , (a,b) => a+b],\n    ['-' , (a,b) => a-b],\n    ['*' , (a,b) => a*b],\n    ['/' , (a,b) => a/b],\n    [\"time-ms\", () => new Date().getTime()],\n\n    ['list', (...a) => a],\n    ['list?', _list_Q],\n    ['vector', (...a) => Vector.from(a)],\n    ['vector?', a => a instanceof Vector],\n    ['hash-map', (...a) => _assoc_BANG(new Map(), ...a)],\n    ['map?', a => a instanceof Map],\n    ['assoc', (m,...a) => _assoc_BANG(_clone(m), ...a)],\n    ['dissoc', (m,...a) => { let n = _clone(m); a.forEach(k => n.delete(k));\n                             return n}],\n    ['get', (m,a) => m === null ? null : m.has(a) ? m.get(a) : null],\n    ['contains?', (m,a) => m.has(a)],\n    ['keys', a => Array.from(a.keys())],\n    ['vals', a => Array.from(a.values())],\n\n    ['sequential?', a => Array.isArray(a)],\n    ['cons', (a,b) => [a].concat(b)],\n    ['concat', (...a) => a.reduce((x,y) => x.concat(y), [])],\n    ['vec', (a) => Vector.from(a)],\n    ['nth', (a,b) => b < a.length ? a[b] : _error('nth: index out of range')],\n    ['first', a => a !== null && a.length > 0 ? a[0] : null],\n    ['rest', a => a === null ? [] : Array.from(a.slice(1))],\n    ['empty?', a => a.length === 0],\n    ['count', a => a === null ? 0 : a.length],\n    ['apply', (f,...a) => f(...a.slice(0, -1).concat(a[a.length-1]))],\n    ['map', (f,a) => Array.from(a.map(x => f(x)))],\n\n    ['conj', (s,...a) => _list_Q(s) ? a.reverse().concat(s)\n                                    : Vector.from(s.concat(a))],\n    ['seq', seq],\n\n    ['meta', a => 'meta' in a ? a['meta'] : null],\n    ['with-meta', (a,b) => { let c = _clone(a); c.meta = b; return c }],\n    ['atom', a => new Atom(a)],\n    ['atom?', a => a instanceof Atom],\n    ['deref', atm => atm.val],\n    ['reset!', (atm,a) => atm.val = a],\n    ['swap!', (atm,f,...args) => atm.val = f(...[atm.val].concat(args))]\n    ])\n"
  },
  {
    "path": "impls/es6/env.mjs",
    "content": "export function new_env(outer={}, binds=[], exprs=[]) {\n    var e = Object.setPrototypeOf({}, outer)\n    // Bind symbols in binds to values in exprs\n    for (var i=0; i<binds.length; i++) {\n        if (Symbol.keyFor(binds[i]) === \"&\") {\n            e[binds[i+1]] = exprs.slice(i) // variable length arguments\n            break\n        }\n        e[binds[i]] = exprs[i]\n    }\n    return e\n}\nexport const env_get = (env, sym) => {\n    if (sym in env) { return env[sym] }\n    throw Error(`'${Symbol.keyFor(sym)}' not found`)\n}\nexport const env_set = (env, sym, val) => env[sym] = val\n"
  },
  {
    "path": "impls/es6/node_readline.mjs",
    "content": "// IMPORTANT: choose one\nconst RL_LIB = \"libreadline.so.8\";  // NOTE: libreadline is GPL\n//const RL_LIB = \"libedit.so.2\";\n\nimport path from 'path';\nimport fs from 'fs';\nconst koffiCjs = await import('koffi');\nconst koffi = koffiCjs.default || koffiCjs;\n\nconst HISTORY_FILE = path.join(process.env.HOME, '.mal-history');\nconst rllib = koffi.load(RL_LIB);\nconst readlineFunc = rllib.func('char *readline(char *)');\nconst addHistoryFunc = rllib.func('int add_history(char *)');\n\nvar rl_history_loaded = false;\n\nfunction readline(prompt) {\n    prompt = prompt || \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { addHistoryFunc(lines[i]); }\n        }\n    }\n\n    var line = readlineFunc(prompt);\n    if (line) {\n        addHistoryFunc(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\n\nexport default { readline };\n"
  },
  {
    "path": "impls/es6/package.json",
    "content": "{\n    \"name\": \"mal\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in ES6 (ECMAScript 6 / ECMAScript 2015)\",\n    \"dependencies\": {\n        \"koffi\": \"^2.12.1\"\n    },\n    \"type\": \"module\"\n}\n"
  },
  {
    "path": "impls/es6/printer.mjs",
    "content": "import { _list_Q, _keyword_Q, Vector, Atom } from './types.mjs'\n\nexport function pr_str(obj, print_readably) {\n    if (typeof print_readably === 'undefined') { print_readably = true }\n    var _r = print_readably\n    if (_list_Q(obj)) {\n        return \"(\" + obj.map(e => pr_str(e,_r)).join(' ') + \")\"\n    } else if (obj instanceof Vector) {\n        return \"[\" + obj.map(e => pr_str(e,_r)).join(' ') + \"]\"\n    } else if (obj instanceof Map) {\n        var ret = []\n        for (let [k,v] of obj) {\n            ret.push(pr_str(k,_r), pr_str(v,_r))\n        }\n        return \"{\" + ret.join(' ') + \"}\"\n    } else if (typeof obj === \"string\") {\n        if (_keyword_Q(obj)) {\n            return ':' + obj.slice(1)\n        } else if (_r) {\n            return '\"' + obj.replace(/\\\\/g, \"\\\\\\\\\")\n                            .replace(/\"/g, '\\\\\"')\n                            .replace(/\\n/g, \"\\\\n\") + '\"'\n        } else {\n            return obj\n        }\n    } else if (typeof obj === 'symbol') {\n        return Symbol.keyFor(obj)\n    } else if (obj === null) {\n        return \"nil\"\n    } else if (obj instanceof Atom) {\n        return \"(atom \" + pr_str(obj.val,_r) + \")\"\n    } else {\n        return obj.toString()\n    }\n}\n"
  },
  {
    "path": "impls/es6/reader.mjs",
    "content": "import { _keyword, _assoc_BANG, Vector } from './types.mjs';\n\nexport class BlankException extends Error {}\n\nclass Reader {\n    constructor(tokens) {\n        this.tokens = tokens\n        this.position = 0\n    }\n    next() { return this.tokens[this.position++] }\n    peek() { return this.tokens[this.position] }\n}\n\nfunction tokenize(str) {\n    const re = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g\n    let match = null\n    let results = []\n    while ((match = re.exec(str)[1]) != '') {\n        if (match[0] === ';') { continue }\n        results.push(match)\n    }\n    return results\n}\n\nfunction read_atom (reader) {\n    const token = reader.next()\n    //console.log(\"read_atom:\", token)\n    if (token.match(/^-?[0-9]+$/)) {\n        return parseInt(token,10)        // integer\n    } else if (token.match(/^-?[0-9][0-9.]*$/)) {\n        return parseFloat(token,10)     // float\n    } else if (token.match(/^\"(?:\\\\.|[^\\\\\"])*\"$/)) {\n        return token.slice(1,token.length-1)\n            .replace(/\\\\(.)/g, (_, c) => c === \"n\" ? \"\\n\" : c)\n    } else if (token[0] === \"\\\"\") {\n        throw new Error(\"expected '\\\"', got EOF\");\n    } else if (token[0] === \":\") {\n        return _keyword(token.slice(1))\n    } else if (token === \"nil\") {\n        return null\n    } else if (token === \"true\") {\n        return true\n    } else if (token === \"false\") {\n        return false\n    } else {\n        return Symbol.for(token) // symbol\n    }\n}\n\n// read list of tokens\nfunction read_list(reader, start, end) {\n    start = start || '('\n    end = end || ')'\n    var ast = []\n    var token = reader.next()\n    if (token !== start) {\n        throw new Error(\"expected '\" + start + \"'\")\n    }\n    while ((token = reader.peek()) !== end) {\n        if (!token) {\n            throw new Error(\"expected '\" + end + \"', got EOF\")\n        }\n        ast.push(read_form(reader))\n    }\n    reader.next()\n    return ast\n}\n\n// read vector of tokens\nfunction read_vector(reader) {\n    return Vector.from(read_list(reader, '[', ']'));\n}\n\n// read hash-map key/value pairs\nfunction read_hash_map(reader) {\n    return _assoc_BANG(new Map(), ...read_list(reader, '{', '}'))\n}\n\nfunction read_form(reader) {\n    var token = reader.peek()\n    switch (token) {\n    // reader macros/transforms\n    case ';': return null // Ignore comments\n    case '\\'': reader.next()\n               return [Symbol.for('quote'), read_form(reader)]\n    case '`': reader.next()\n              return [Symbol.for('quasiquote'), read_form(reader)]\n    case '~': reader.next()\n              return [Symbol.for('unquote'), read_form(reader)]\n    case '~@': reader.next()\n               return [Symbol.for('splice-unquote'), read_form(reader)]\n    case '^': reader.next()\n              var meta = read_form(reader)\n              return [Symbol.for('with-meta'), read_form(reader), meta]\n    case '@': reader.next()\n              return [Symbol.for('deref'), read_form(reader)]\n\n    // list\n    case ')': throw new Error(\"unexpected ')'\")\n    case '(': return read_list(reader)\n\n    // vector\n    case ']': throw new Error(\"unexpected ']'\")\n    case '[': return read_vector(reader)\n\n    // hash-map\n    case '}': throw new Error(\"unexpected '}'\")\n    case '{': return read_hash_map(reader)\n\n    // atom\n    default:  return read_atom(reader)\n    }\n}\n\nexport function read_str(str) {\n    var tokens = tokenize(str)\n    if (tokens.length === 0) { throw new BlankException() }\n    return read_form(new Reader(tokens))\n}\n\n"
  },
  {
    "path": "impls/es6/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/${STEP:-stepA_mal}.mjs \"${@}\"\n"
  },
  {
    "path": "impls/es6/step0_repl.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\n\n// read\nconst READ = str => str\n\n// eval\nconst EVAL = (ast, env) => ast\n\n// print\nconst PRINT = exp => exp\n\n// repl\nconst REP = str => PRINT(EVAL(READ(str), {}))\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    if (line) { console.log(REP(line)) }\n}\n"
  },
  {
    "path": "impls/es6/step1_read_print.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst EVAL = (ast, env) => ast\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nconst REP = str => PRINT(EVAL(READ(str), {}))\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step2_eval.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _list_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst EVAL = (ast, env) => {\n    // console.log('EVAL:', pr_str(ast, true))\n\n    if (typeof ast === 'symbol') {\n        if (ast in env) {\n            return env[ast]\n        } else {\n            throw Error(`'${Symbol.keyFor(ast)}' not found`)\n        }\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [f, ...args] =ast.map(x => EVAL(x, env))\n    return f(...args)\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nvar repl_env = {[Symbol.for('+')]: (a,b) => a+b,\n                [Symbol.for('-')]: (a,b) => a-b,\n                [Symbol.for('*')]: (a,b) => a*b,\n                [Symbol.for('/')]: (a,b) => a/b}\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step3_env.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _list_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            return EVAL(a2, let_env)\n        default:\n            const [f, ...args] = ast.map(x => EVAL(x, env))\n            return f(...args)\n    }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nenv_set(repl_env, Symbol.for('+'), (a,b) => a+b)\nenv_set(repl_env, Symbol.for('-'), (a,b) => a-b)\nenv_set(repl_env, Symbol.for('*'), (a,b) => a*b)\nenv_set(repl_env, Symbol.for('/'), (a,b) => a/b)\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step4_if_fn_do.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _list_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            return EVAL(a2, let_env)\n        case 'do':\n            return ast.slice(1).map(x => EVAL(x, env))[ast.length-2]\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                return typeof a3 !== 'undefined' ? EVAL(a3, env) : null\n            } else {\n                return EVAL(a2, env)\n            }\n        case 'fn*':\n            return (...args) => EVAL(a2, new_env(env, a1, args))\n        default:\n            const [f, ...args] = ast.map(x => EVAL(x, env))\n            return f(...args)\n    }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\n\n// core.mal: defined using language itself\nREP('(def! not (fn* (a) (if a false true)))')\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step5_tco.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _list_Q, _malfunc, _malfunc_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n  while (true) {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            env = let_env\n            ast = a2\n            break // continue TCO loop\n        case 'do':\n            ast.slice(1, -1).map(x => EVAL(x, env))\n            ast = ast[ast.length-1]\n            break // continue TCO loop\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                ast = (typeof a3 !== 'undefined') ? a3 : null\n            } else {\n                ast = a2\n            }\n            break // continue TCO loop\n        case 'fn*':\n            return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),\n                            a2, env, a1)\n        default:\n            const [f, ...args] = ast.map(x => EVAL(x, env))\n            if (_malfunc_Q(f)) {\n                env = new_env(f.env, f.params, args)\n                ast = f.ast\n                break // continue TCO loop\n            } else {\n                return f(...args)\n            }\n    }\n  }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\n\n// core.mal: defined using language itself\nREP('(def! not (fn* (a) (if a false true)))')\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step6_file.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _list_Q, _malfunc, _malfunc_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n  while (true) {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            env = let_env\n            ast = a2\n            break // continue TCO loop\n        case 'do':\n            ast.slice(1, -1).map(x => EVAL(x, env))\n            ast = ast[ast.length-1]\n            break // continue TCO loop\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                ast = (typeof a3 !== 'undefined') ? a3 : null\n            } else {\n                ast = a2\n            }\n            break // continue TCO loop\n        case 'fn*':\n            return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),\n                            a2, env, a1)\n        default:\n            const [f, ...args] = ast.map(x => EVAL(x, env))\n            if (_malfunc_Q(f)) {\n                env = new_env(f.env, f.params, args)\n                ast = f.ast\n                break // continue TCO loop\n            } else {\n                return f(...args)\n            }\n    }\n  }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\nenv_set(repl_env, Symbol.for('eval'), a => EVAL(a, repl_env))\nenv_set(repl_env, Symbol.for('*ARGV*'), [])\n\n// core.mal: defined using language itself\nREP('(def! not (fn* (a) (if a false true)))')\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\nif (process.argv.length > 2) {\n    env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3))\n    REP(`(load-file \"${process.argv[2]}\")`)\n    process.exit(0)\n}\n\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step7_quote.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _list_Q, _malfunc, _malfunc_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst qq_loop = (acc, elt) => {\n    if (_list_Q(elt) && elt.length == 2\n        && elt[0] === Symbol.for('splice-unquote')) {\n        return [Symbol.for('concat'), elt[1], acc]\n    } else {\n        return [Symbol.for('cons'), quasiquote (elt), acc]\n    }\n}\nconst quasiquote = ast => {\n    if (_list_Q(ast)) {\n        if (ast.length == 2 && ast[0] === Symbol.for('unquote')) {\n            return ast[1]\n        } else {\n            return ast.reduceRight(qq_loop, [])\n        }\n    } else if (ast instanceof Vector) {\n        return [Symbol.for('vec'), ast.reduceRight(qq_loop, [])]\n    } else if (typeof ast === 'symbol' || ast instanceof Map) {\n        return [Symbol.for('quote'), ast]\n    } else {\n        return ast\n    }\n}\n\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n  while (true) {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            env = let_env\n            ast = a2\n            break // continue TCO loop\n        case 'quote':\n            return a1\n        case 'quasiquote':\n            ast = quasiquote(a1)\n            break // continue TCO loop\n        case 'do':\n            ast.slice(1, -1).map(x => EVAL(x, env))\n            ast = ast[ast.length-1]\n            break // continue TCO loop\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                ast = (typeof a3 !== 'undefined') ? a3 : null\n            } else {\n                ast = a2\n            }\n            break // continue TCO loop\n        case 'fn*':\n            return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),\n                            a2, env, a1)\n        default:\n            const [f, ...args] = ast.map(x => EVAL(x, env))\n            if (_malfunc_Q(f)) {\n                env = new_env(f.env, f.params, args)\n                ast = f.ast\n                break // continue TCO loop\n            } else {\n                return f(...args)\n            }\n    }\n  }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\nenv_set(repl_env, Symbol.for('eval'), a => EVAL(a, repl_env))\nenv_set(repl_env, Symbol.for('*ARGV*'), [])\n\n// core.mal: defined using language itself\nREP('(def! not (fn* (a) (if a false true)))')\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\nif (process.argv.length > 2) {\n    env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3))\n    REP(`(load-file \"${process.argv[2]}\")`)\n    process.exit(0)\n}\n\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step8_macros.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _clone, _list_Q, _malfunc, _malfunc_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst qq_loop = (acc, elt) => {\n    if (_list_Q(elt) && elt.length == 2\n        && elt[0] === Symbol.for('splice-unquote')) {\n        return [Symbol.for('concat'), elt[1], acc]\n    } else {\n        return [Symbol.for('cons'), quasiquote (elt), acc]\n    }\n}\nconst quasiquote = ast => {\n    if (_list_Q(ast)) {\n        if (ast.length == 2 && ast[0] === Symbol.for('unquote')) {\n            return ast[1]\n        } else {\n            return ast.reduceRight(qq_loop, [])\n        }\n    } else if (ast instanceof Vector) {\n        return [Symbol.for('vec'), ast.reduceRight(qq_loop, [])]\n    } else if (typeof ast === 'symbol' || ast instanceof Map) {\n        return [Symbol.for('quote'), ast]\n    } else {\n        return ast\n    }\n}\n\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n  while (true) {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            env = let_env\n            ast = a2\n            break // continue TCO loop\n        case 'quote':\n            return a1\n        case 'quasiquote':\n            ast = quasiquote(a1)\n            break // continue TCO loop\n        case 'defmacro!':\n            let func = _clone(EVAL(a2, env))\n            func.ismacro = true\n            return env_set(env, a1, func)\n        case 'do':\n            ast.slice(1, -1).map(x => EVAL(x, env))\n            ast = ast[ast.length-1]\n            break // continue TCO loop\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                ast = (typeof a3 !== 'undefined') ? a3 : null\n            } else {\n                ast = a2\n            }\n            break // continue TCO loop\n        case 'fn*':\n            return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),\n                            a2, env, a1)\n        default:\n            const f = EVAL(a0, env)\n            if (f.ismacro) {\n                ast = f(...ast.slice(1))\n                break // continue TCO loop\n            }\n            const args = ast.slice(1).map(x => EVAL(x, env))\n            if (_malfunc_Q(f)) {\n                env = new_env(f.env, f.params, args)\n                ast = f.ast\n                break // continue TCO loop\n            } else {\n                return f(...args)\n            }\n    }\n  }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\nenv_set(repl_env, Symbol.for('eval'), a => EVAL(a, repl_env))\nenv_set(repl_env, Symbol.for('*ARGV*'), [])\n\n// core.mal: defined using language itself\nREP('(def! not (fn* (a) (if a false true)))')\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\nREP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list \\'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons \\'cond (rest (rest xs)))))))')\n\nif (process.argv.length > 2) {\n    env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3))\n    REP(`(load-file \"${process.argv[2]}\")`)\n    process.exit(0)\n}\n\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${exc}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/step9_try.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _clone, _list_Q, _malfunc, _malfunc_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst qq_loop = (acc, elt) => {\n    if (_list_Q(elt) && elt.length == 2\n        && elt[0] === Symbol.for('splice-unquote')) {\n        return [Symbol.for('concat'), elt[1], acc]\n    } else {\n        return [Symbol.for('cons'), quasiquote (elt), acc]\n    }\n}\nconst quasiquote = ast => {\n    if (_list_Q(ast)) {\n        if (ast.length == 2 && ast[0] === Symbol.for('unquote')) {\n            return ast[1]\n        } else {\n            return ast.reduceRight(qq_loop, [])\n        }\n    } else if (ast instanceof Vector) {\n        return [Symbol.for('vec'), ast.reduceRight(qq_loop, [])]\n    } else if (typeof ast === 'symbol' || ast instanceof Map) {\n        return [Symbol.for('quote'), ast]\n    } else {\n        return ast\n    }\n}\n\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n  while (true) {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            env = let_env\n            ast = a2\n            break // continue TCO loop\n        case 'quote':\n            return a1\n        case 'quasiquote':\n            ast = quasiquote(a1)\n            break // continue TCO loop\n        case 'defmacro!':\n            let func = _clone(EVAL(a2, env))\n            func.ismacro = true\n            return env_set(env, a1, func)\n        case 'try*':\n            try {\n                return EVAL(a1, env)\n            } catch (exc) {\n                if (a2 && a2[0] === Symbol.for('catch*')) {\n                    if (exc instanceof Error) { exc = exc.message }\n                    return EVAL(a2[2], new_env(env, [a2[1]], [exc]))\n                } else {\n                    throw exc\n                }\n            }\n        case 'do':\n            ast.slice(1, -1).map(x => EVAL(x, env))\n            ast = ast[ast.length-1]\n            break // continue TCO loop\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                ast = (typeof a3 !== 'undefined') ? a3 : null\n            } else {\n                ast = a2\n            }\n            break // continue TCO loop\n        case 'fn*':\n            return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),\n                            a2, env, a1)\n        default:\n            const f = EVAL(a0, env)\n            if (f.ismacro) {\n                ast = f(...ast.slice(1))\n                break // continue TCO loop\n            }\n            const args = ast.slice(1).map(x => EVAL(x, env))\n            if (_malfunc_Q(f)) {\n                env = new_env(f.env, f.params, args)\n                ast = f.ast\n                break // continue TCO loop\n            } else {\n                return f(...args)\n            }\n    }\n  }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\nenv_set(repl_env, Symbol.for('eval'), a => EVAL(a, repl_env))\nenv_set(repl_env, Symbol.for('*ARGV*'), [])\n\n// core.mal: defined using language itself\nREP('(def! not (fn* (a) (if a false true)))')\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\nREP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list \\'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons \\'cond (rest (rest xs)))))))')\n\nif (process.argv.length > 2) {\n    env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3))\n    REP(`(load-file \"${process.argv[2]}\")`)\n    process.exit(0)\n}\n\n\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${pr_str(exc, true)}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/stepA_mal.mjs",
    "content": "import rl from './node_readline.mjs'\nconst readline = rl.readline\nimport { _clone, _list_Q, _malfunc, _malfunc_Q, Vector } from './types.mjs'\nimport { BlankException, read_str } from './reader.mjs'\nimport { pr_str } from './printer.mjs'\nimport { new_env, env_set, env_get } from './env.mjs'\nimport { core_ns } from './core.mjs'\n\n// read\nconst READ = str => read_str(str)\n\n// eval\nconst qq_loop = (acc, elt) => {\n    if (_list_Q(elt) && elt.length == 2\n        && elt[0] === Symbol.for('splice-unquote')) {\n        return [Symbol.for('concat'), elt[1], acc]\n    } else {\n        return [Symbol.for('cons'), quasiquote (elt), acc]\n    }\n}\nconst quasiquote = ast => {\n    if (_list_Q(ast)) {\n        if (ast.length == 2 && ast[0] === Symbol.for('unquote')) {\n            return ast[1]\n        } else {\n            return ast.reduceRight(qq_loop, [])\n        }\n    } else if (ast instanceof Vector) {\n        return [Symbol.for('vec'), ast.reduceRight(qq_loop, [])]\n    } else if (typeof ast === 'symbol' || ast instanceof Map) {\n        return [Symbol.for('quote'), ast]\n    } else {\n        return ast\n    }\n}\n\nconst dbgevalsym = Symbol.for(\"DEBUG-EVAL\")\n\nconst EVAL = (ast, env) => {\n  while (true) {\n    if (dbgevalsym in env) {\n        const dbgeval = env_get(env, dbgevalsym)\n        if (dbgeval !== null && dbgeval !== false) {\n            console.log('EVAL:', pr_str(ast, true))\n        }\n    }\n\n    if (typeof ast === 'symbol') {\n        return env_get(env, ast)\n    } else if (ast instanceof Vector) {\n        return ast.map(x => EVAL(x, env))\n    } else if (ast instanceof Map) {\n        let new_hm = new Map()\n        ast.forEach((v, k) => new_hm.set(k, EVAL(v, env)))\n        return new_hm\n    } else if (!_list_Q(ast)) {\n        return ast\n    }\n\n    if (ast.length === 0) { return ast }\n\n    const [a0, a1, a2, a3] = ast\n    switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) {\n        case 'def!':\n            return env_set(env, a1, EVAL(a2, env))\n        case 'let*':\n            let let_env = new_env(env)\n            for (let i=0; i < a1.length; i+=2) {\n                env_set(let_env, a1[i], EVAL(a1[i+1], let_env))\n            }\n            env = let_env\n            ast = a2\n            break // continue TCO loop\n        case 'quote':\n            return a1\n        case 'quasiquote':\n            ast = quasiquote(a1)\n            break // continue TCO loop\n        case 'defmacro!':\n            let func = _clone(EVAL(a2, env))\n            func.ismacro = true\n            return env_set(env, a1, func)\n        case 'try*':\n            try {\n                return EVAL(a1, env)\n            } catch (exc) {\n                if (a2 && a2[0] === Symbol.for('catch*')) {\n                    if (exc instanceof Error) { exc = exc.message }\n                    return EVAL(a2[2], new_env(env, [a2[1]], [exc]))\n                } else {\n                    throw exc\n                }\n            }\n        case 'do':\n            ast.slice(1, -1).map(x => EVAL(x, env))\n            ast = ast[ast.length-1]\n            break // continue TCO loop\n        case 'if':\n            let cond = EVAL(a1, env)\n            if (cond === null || cond === false) {\n                ast = (typeof a3 !== 'undefined') ? a3 : null\n            } else {\n                ast = a2\n            }\n            break // continue TCO loop\n        case 'fn*':\n            return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),\n                            a2, env, a1)\n        default:\n            const f = EVAL(a0, env)\n            if (f.ismacro) {\n                ast = f(...ast.slice(1))\n                break // continue TCO loop\n            }\n            const args = ast.slice(1).map(x => EVAL(x, env))\n            if (_malfunc_Q(f)) {\n                env = new_env(f.env, f.params, args)\n                ast = f.ast\n                break // continue TCO loop\n            } else {\n                return f(...args)\n            }\n    }\n  }\n}\n\n// print\nconst PRINT = exp => pr_str(exp, true)\n\n// repl\nlet repl_env = new_env()\nconst REP = str => PRINT(EVAL(READ(str), repl_env))\n\n// core.EXT: defined using ES6\nfor (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) }\nenv_set(repl_env, Symbol.for('eval'), a => EVAL(a, repl_env))\nenv_set(repl_env, Symbol.for('*ARGV*'), [])\n\n// core.mal: defined using language itself\nREP('(def! *host-language* \"ecmascript6\")')\nREP('(def! not (fn* (a) (if a false true)))')\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\nREP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list \\'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons \\'cond (rest (rest xs)))))))')\n\nif (process.argv.length > 2) {\n    env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3))\n    REP(`(load-file \"${process.argv[2]}\")`)\n    process.exit(0)\n}\n\nREP('(println (str \"Mal [\" *host-language* \"]\"))')\nwhile (true) {\n    let line = readline('user> ')\n    if (line == null) break\n    try {\n        if (line) { console.log(REP(line)) }\n    } catch (exc) {\n        if (exc instanceof BlankException) { continue }\n        if (exc instanceof Error) { console.warn(exc.stack) }\n        else { console.warn(`Error: ${pr_str(exc, true)}`) }\n    }\n}\n"
  },
  {
    "path": "impls/es6/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/es6/types.mjs",
    "content": "// General functions\nexport function _equal_Q (a, b) {\n    if (Array.isArray(a) && Array.isArray(b)) {\n        if (a.length !== b.length) { return false }\n        for (let i=0; i<a.length; i++) {\n            if (! _equal_Q(a[i], b[i])) { return false }\n        }\n        return true\n    } else if (a instanceof Map && b instanceof Map) {\n        if (a.size !== b.size) { return false }\n        for (let k of a.keys()) {\n            if (! _equal_Q(a.get(k), b.get(k))) { return false }\n        }\n        return true\n    } else {\n        return a === b\n    }\n}\n\nexport function _clone(obj, new_meta) {\n    let new_obj = null\n    if (_list_Q(obj)) {\n        new_obj = obj.slice(0)\n    } else if (obj instanceof Vector) {\n        new_obj = Vector.from(obj)\n    } else if (obj instanceof Map) {\n        new_obj = new Map(obj.entries())\n    } else if (obj instanceof Function) {\n        let f = (...a) => obj.apply(f, a)  // new function instance\n        new_obj = Object.assign(f, obj)    // copy original properties\n    } else {\n        throw Error('Unsupported type for clone')\n    }\n    if (typeof new_meta !== 'undefined') { new_obj.meta = new_meta }\n    return new_obj\n}\n\n// Functions\nexport function _malfunc(f, ast, env, params, meta=null, ismacro=false) {\n    return Object.assign(f, {ast, env, params, meta, ismacro})\n}\nexport const _malfunc_Q = f => f.ast ? true : false\n\n\n// Keywords\nexport const _keyword = obj => _keyword_Q(obj) ? obj : '\\u029e' + obj\nexport const _keyword_Q = obj => typeof obj === 'string' && obj[0] === '\\u029e'\n\n// Lists\nexport const _list_Q = obj => Array.isArray(obj) && !(obj instanceof Vector)\n\n// Vectors\nexport class Vector extends Array { }\n\n// Maps\nexport function _assoc_BANG(hm, ...args) {\n    if (args.length % 2 === 1) {\n        throw new Error('Odd number of assoc arguments')\n    }\n    for (let i=0; i<args.length; i+=2) { hm.set(args[i], args[i+1]) }\n    return hm\n}\n\n\n// Atoms\nexport class Atom {\n    constructor(val) { this.val = val }\n}\n"
  },
  {
    "path": "impls/factor/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install ca-certificates curl libgtkglext1-dev libreadline-dev\n\nRUN curl https://downloads.factorcode.org/releases/0.98/factor-linux-x86-64-0.98.tar.gz | tar -xzC/opt\nENV PATH /opt/factor:$PATH\n\n# Allow /mal/factor to create the $HOME/.cache directory.\nENV HOME /mal\n"
  },
  {
    "path": "impls/factor/Makefile",
    "content": "SOURCES_BASE = lib/types/types.factor lib/reader/reader.factor lib/printer/printer.factor\nSOURCES_LISP = lib/env/env.factor lib/core/core.factor stepA_mal/stepA_mal.factor\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.factor mal\n\n# dependency order (env must come before types)\nORDERED_SOURCES = $(filter %env.factor,$(SOURCES)) $(filter-out %env.factor,$(SOURCES))\nmal.factor: $(ORDERED_SOURCES)\n\tcat $+ |  sed '/^USING:/,/;/ s/ *lib.[a-z]*//g' > $@\n\nmal: mal.factor\n\techo '#!/usr/bin/env factor' > $@\n\tcat $< >> $@\n\tchmod +x $@\n\n# TODO: standalone compiled app\n#mal.factor: $(SOURCES)\n#\tmkdir -p dist_tmp; \\\n#\tFDIR=$$(dirname $$(readlink -f $$(which factor))); \\\n#\tfor f in $${FDIR}/*; do ln -sf $$f dist_tmp/; done; \\\n#\trm dist_tmp/factor; \\\n#\tcp $${FDIR}/factor dist_tmp/factor; \\\n#\tHOME=/mal FACTOR_ROOTS=. dist_tmp/factor dist.factor\n#\t#cat $+ | sed 's///' >> $@\n\nclean:\n\trm -f mal.factor\n"
  },
  {
    "path": "impls/factor/lib/core/core-tests.factor",
    "content": "USING: assocs effects kernel sequences stack-checker tools.test ;\nIN: lib.core\n\n{ t } [\n    ns values [\n        infer ( x -- * ) ( x -- x ) [ effect= ] bi-curry@ bi or\n    ] all?\n] unit-test\n"
  },
  {
    "path": "impls/factor/lib/core/core.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators\ncombinators.short-circuit fry grouping hash-sets hashtables io\nio.encodings.utf8 io.files kernel lists lib.env lib.printer\nlib.reader lib.types math namespaces quotations readline sequences sets\nstrings system vectors ;\nIN: lib.core\n\nSYMBOL: mal-apply\n\n: pr-str-stack ( exprs readably? glue -- str )\n    [ '[ _ (pr-str) ] map ] dip join ;\n\nCONSTANT: empty-env T{ malenv f f H{ } }\n\nCONSTANT: ns H{\n    { \"+\" [ first2 + ] }\n    { \"-\" [ first2 - ] }\n    { \"*\" [ first2 * ] }\n    { \"/\" [ first2 / ] }\n    { \"list\" [ >array ] }\n    { \"list?\" [ first array? ] }\n    { \"empty?\" [ first empty? ] }\n    { \"count\" [ first dup nil? [ drop 0 ] [ length ] if ] }\n    { \"=\" [ first2 mal= ] }\n    { \"<\" [ first2 < ] }\n    { \">\" [ first2 > ] }\n    { \">=\" [ first2 >= ] }\n    { \"<=\" [ first2 <= ] }\n    { \"pr-str\" [ t \" \" pr-str-stack ] }\n    { \"str\" [ f \"\" pr-str-stack ] }\n    { \"prn\" [ t \" \" pr-str-stack print flush nil ] }\n    { \"println\" [ f \" \" pr-str-stack print flush nil ] }\n    { \"read-string\" [ first read-str ] }\n    { \"slurp\" [ first utf8 file-contents ] }\n    { \"cons\" [ first2 swap prefix { } like ] }\n    { \"concat\" [ concat { } like ] }\n    { \"vec\" [ first >vector ] }\n    { \"nth\" [ first2 swap nth ] }\n    { \"first\" [ first dup nil? [ drop nil ] [ [ nil ] [ first ] if-empty ] if ] }\n    { \"rest\" [ first dup nil? [ drop { } ] [ [ { } ] [ rest { } like ] if-empty ] if ] }\n    { \"throw\" [ first throw ] }\n    { \"apply\" [ unclip [ unclip-last append ] dip mal-apply get call( args fn -- maltype ) ] }\n    { \"map\" [ first2 swap '[ 1array _ mal-apply get call( args fn -- maltype ) ] map { } like ] }\n    { \"nil?\" [ first nil? ] }\n    { \"true?\" [ first t = ] }\n    { \"false?\" [ first f = ] }\n    { \"symbol\" [ first <malsymbol> ] }\n    { \"symbol?\" [ first malsymbol? ] }\n    { \"string?\" [ first string? ] }\n    { \"keyword\" [ first dup string? [ <malkeyword> ] when ] }\n    { \"keyword?\" [ first malkeyword? ] }\n    { \"number?\" [ first number? ] }\n    { \"fn?\" [ first { [ callable? ] [ { [ malfn? ] [ macro?>> not ] } 1&& ] } 1|| ] }\n    { \"macro?\" [ first { [ malfn? ] [ macro?>> ] } 1&& ] }\n    { \"vector\" [ >vector ] }\n    { \"vector?\" [ first vector? ] }\n    { \"hash-map\" [ 2 group parse-hashtable ] }\n    { \"map?\" [ first hashtable? ] }\n    { \"assoc\" [ unclip swap 2 group parse-hashtable assoc-union ] }\n    { \"dissoc\" [ unclip swap >hash-set '[ drop _ in? not ] assoc-filter ] }\n    { \"get\" [ first2 swap dup nil? [ nip ] [ ?at [ drop nil ] unless ] if ] }\n    { \"contains?\" [ first2 swap dup nil? [ nip ] [ at* nip ] if ] }\n    { \"keys\" [ first keys ] }\n    { \"vals\" [ first values ] }\n    { \"sequential?\" [ first { [ vector? ] [ array? ] } 1|| ] }\n    { \"readline\" [ first readline ] }\n    { \"meta\" [ first dup malfn? [ meta>> ] [ drop f ] if [ nil ] unless* ] }\n    { \"with-meta\" [ first2 over malfn? [ [ clone ] dip >>meta ] [ drop ] if ] }\n    { \"atom\" [ first <malatom> ] }\n    { \"atom?\" [ first malatom? ] }\n    { \"deref\" [ first val>> ] }\n    { \"reset!\" [ first2 >>val val>> ] }\n    { \"swap!\" [ { [ first ] [ second ] [ 2 tail ] [ first val>> ] } cleave\n                prefix swap mal-apply get call( args fn -- maltype ) >>val val>> ] }\n    { \"conj\" [ unclip swap over array? [ reverse prepend ] [ append ] if ] }\n    { \"seq\" [ first {\n                  { [ dup nil? ]    [ drop nil ] }\n                  { [ dup empty? ]  [ drop nil ] }\n                  { [ dup array? ]  [ ] }\n                  { [ dup vector? ] [ >array ] }\n                  { [ dup string? ] [ [ 1string ] { } map-as ] }\n              } cond ] }\n    { \"time-ms\" [ drop nano-count 1,000,000 /i ] }\n}\n"
  },
  {
    "path": "impls/factor/lib/env/env-tests.factor",
    "content": "USING: assocs kernel lib.types tools.test ;\nIN: lib.env\n\n{ \"1\" } [\n    T{ malsymbol { name \"foo\" } }\n    T{ malenv\n        { outer T{ malenv f f H{ { \"foo\" \"2\" } } } }\n        { data H{ { \"foo\" \"1\" } } }\n    } env-get\n] unit-test\n\n{ \"2\" } [\n    T{ malsymbol { name \"foo\" } }\n    T{ malenv\n        { outer T{ malenv f f H{ { \"foo\" \"2\" } } } }\n        { data H{ { \"bar\" \"1\" } } }\n    } env-get\n] unit-test\n\n{ \"3\" } [\n    T{ malsymbol { name \"foo\" } }\n    T{ malenv { outer f } { data H{ } } }\n    [ [ \"3\" ] 2dip env-set ] [ env-get ] 2bi\n] unit-test\n\n[\n    T{ malsymbol { name \"baz\" } }\n    T{ malenv\n        { outer T{ malenv f f H{ { \"foo\" \"2\" } } } }\n        { data H{ { \"bar\" \"1\" } } }\n    } env-get\n] must-fail\n"
  },
  {
    "path": "impls/factor/lib/env/env.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors assocs formatting hashtables kernel math\nsequences typed ;\nIN: lib.env\n\nTUPLE: malenv\n{ outer read-only }\n{ data hashtable read-only } ;\n\n! set outer to f if top level env\n\nC: <malenv> malenv\n\n: new-env ( outer -- malenv ) H{ } clone malenv boa ;\n\nTYPED: env-find ( key malenv: malenv -- value/f ? )\n    2dup [ name>> ] [ data>> ] bi* at* [\n        [ 2drop ] 2dip\n    ] [\n        drop outer>> [ env-find ] [ drop f f ] if*\n    ] if* ;\n\nTYPED: env-set ( value key malenv: malenv -- )\n    [ name>> ] [ data>> ] bi* set-at ;\n\n: env-get ( key assoc -- value )\n    dupd env-find [\n        nip\n    ] [\n        drop name>> \"'%s' not found\" sprintf throw\n    ] if ;\n"
  },
  {
    "path": "impls/factor/lib/printer/printer-tests.factor",
    "content": "USING: lists lib.types tools.test ;\nIN: lib.printer\n\n{ \"(atom \\\"foo\\\")\" } [ T{ malatom { val \"foo\" } } pr-str ] unit-test\n{ \"#<fn>\" } [ T{ malfn } pr-str ] unit-test\n{ \":foo\" } [ T{ malkeyword { name \"foo\" } } pr-str ] unit-test\n{ \"foo\" } [ T{ malsymbol { name \"foo\" } } pr-str ] unit-test\n{ \"14\" } [ 14 pr-str ] unit-test\n{ \"\\\"\\\\\\\\foo\\\\\\\"\\\"\" } [ \"\\\\foo\\\"\" pr-str ] unit-test\n{ \"(1 2 3 4)\" } [ { 1 2 3 4 } pr-str ] unit-test\n{ \"[1 2 3 4]\" } [ V{ 1 2 3 4 } pr-str ] unit-test\n{ \"{1 2}\" } [ H{ { 1 2 } } pr-str ] unit-test\n{ \"true\" } [ t pr-str ] unit-test\n{ \"false\" } [ f pr-str ] unit-test\n{ \"nil\" } [ +nil+ pr-str ] unit-test\n"
  },
  {
    "path": "impls/factor/lib/printer/printer.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs fry hashtables kernel lists\nlib.types math math.parser sequences splitting strings summary\nvectors ;\nIN: lib.printer\n\nGENERIC#: (pr-str) 1 ( maltype readably? -- str )\nM: object (pr-str) drop summary ;\nM: malatom (pr-str) [ val>> ] dip (pr-str) \"(atom \" \")\" surround ;\nM: malfn (pr-str) 2drop \"#<fn>\" ;\nM: malkeyword (pr-str) drop name>> \":\" prepend ;\nM: malsymbol (pr-str) drop name>> ;\nM: number (pr-str) drop number>string ;\nM: string (pr-str)\n    [\n        \"\\\\\" \"\\\\\\\\\" replace\n        \"\\\"\" \"\\\\\\\"\" replace\n        \"\\n\" \"\\\\n\" replace\n        \"\\\"\" dup surround\n    ] when ;\nM: array (pr-str) '[ _ (pr-str) ] map \" \" join \"(\" \")\" surround ;\nM: vector (pr-str) '[ _ (pr-str) ] map \" \" join \"[\" \"]\" surround ;\nM: hashtable (pr-str)\n    [ unzip ] dip '[ [ _ (pr-str) ] bi@ \" \" glue ] 2map\n    \" \" join \"{\" \"}\" surround ;\nM: t (pr-str) 2drop \"true\" ;\nM: f (pr-str) 2drop \"false\" ;\nM: +nil+ (pr-str) 2drop \"nil\" ;\n\n: pr-str ( maltype -- str )\n    t (pr-str) ;\n"
  },
  {
    "path": "impls/factor/lib/reader/reader-tests.factor",
    "content": "USING: lists lib.types tools.test ;\nIN: lib.reader\n\n{ \"foo\" } [ \"\\\"foo\\\"\" read-atom ] unit-test\n{ T{ malkeyword { name \"foo\" } } } [ \":foo\" read-atom ] unit-test\n{ f } [ \"false\" read-atom ] unit-test\n{ t } [ \"true\" read-atom ] unit-test\n{ +nil+ } [ \"nil\" read-atom ] unit-test\n{ T{ malsymbol { name \"foo\" } } } [ \"foo\" read-atom ] unit-test\n{ 14 } [ \"14\" read-atom ] unit-test\n{ 1.5 } [ \"1.5\" read-atom ] unit-test\n{ 2/3 } [ \"2/3\" read-atom ] unit-test\n"
  },
  {
    "path": "impls/factor/lib/reader/reader.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: arrays combinators grouping hashtables kernel lists locals\nmake lib.types math.parser regexp sequences splitting strings ;\nIN: lib.reader\n\nCONSTANT: token-regex R/ (~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)~^@]+)/\n\nDEFER: read-form\n\n: (read-string) ( str -- maltype )\n!    dup last CHAR: \" = [\n    dup R/ ^\"(?:\\\\.|[^\\\\\"])*\"$/ matches? [\n        rest but-last R/ \\\\./ [\n            {\n                { [ dup >string \"\\\\\\\\\" = ] [ drop \"\\\\\" ] }\n                { [ dup >string \"\\\\n\"  = ] [ drop \"\\n\" ] }\n                { [ dup >string \"\\\\\\\"\" = ] [ drop \"\\\"\" ] }\n                [ ]\n            } cond\n        ] re-replace-with\n    ] [\n        \"expected '\\\"', got EOF\" throw\n    ] if ;\n\n: (read-atom) ( str -- maltype )\n    {\n        { [ dup first CHAR: \" = ] [ (read-string) ] }\n        { [ dup first CHAR: : = ] [ rest <malkeyword> ] }\n        { [ dup \"false\" = ]       [ drop f ] }\n        { [ dup \"true\" = ]        [ drop t ] }\n        { [ dup \"nil\" = ]         [ drop nil ] }\n        [ <malsymbol> ]\n    } cond ;\n\n: read-atom ( str -- maltype )\n    dup string>number [ nip ] [ (read-atom) ] if* ;\n\n:: read-sequence ( seq closer exemplar -- seq maltype )\n    seq [\n        [\n            [ \"expected '\" closer \"', got EOF\" append append throw ]\n            [ dup first closer = ] if-empty\n        ] [\n            read-form ,\n        ] until rest\n    ] exemplar make ;\n\n: read-list ( seq -- seq maltype )\n    \")\" { } read-sequence ;\n\n: read-vector ( seq -- seq maltype )\n    \"]\" V{ } read-sequence ;\n\n: read-hashmap ( seq -- seq maltype )\n    \"}\" V{ } read-sequence 2 group parse-hashtable ;\n\n: consume-next-into-list ( seq symname -- seq maltype )\n    [ read-form ] dip <malsymbol> swap 2array ;\n\n: read-form ( seq -- seq maltype )\n    unclip {\n        { \"(\" [ read-list ] }\n        { \"[\" [ read-vector ] }\n        { \"{\" [ read-hashmap ] }\n        { \"'\" [ \"quote\" consume-next-into-list ] }\n        { \"`\" [ \"quasiquote\" consume-next-into-list ] }\n        { \"~\" [ \"unquote\" consume-next-into-list ] }\n        { \"~@\" [ \"splice-unquote\" consume-next-into-list ] }\n        { \"^\" [ read-form [ read-form ] dip 2array \"with-meta\" <malsymbol> prefix ] }\n        { \"@\" [ \"deref\" consume-next-into-list ] }\n        [ read-atom ]\n    } case ;\n\n: tokenize ( str -- seq )\n    token-regex all-matching-subseqs\n    [ first CHAR: ; = not ] filter ;\n\n: read-str ( str -- maltype )\n    tokenize [ \" \" throw ] [ read-form nip ] if-empty ;\n"
  },
  {
    "path": "impls/factor/lib/types/types.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors assocs combinators.short-circuit hashtables\nkernel locals lib.env sequences strings ;\nIN: lib.types\n\nTUPLE: malsymbol { name string read-only } ;\n\nC: <malsymbol> malsymbol\n\n: symeq? ( string other -- ? )\n    dup malsymbol? [ name>> = ] [ 2drop f ] if ;\n\nTUPLE: malfn\n    { env malenv read-only }\n    { binds sequence read-only }\n    { exprs read-only }\n    { macro? boolean read-only }\n    { meta assoc } ;\n\n: malmacro ( fn -- fn )\n    [ env>> ] [ binds>> ] [ exprs>> ] tri t f malfn boa ;\n\n: <malfn> ( env binds exprs -- fn )\n    f f malfn boa ;\n\nTUPLE: malatom { val } ;\n\nC: <malatom> malatom\n\nTUPLE: malkeyword { name string read-only } ;\n\nC: <malkeyword> malkeyword\n\nDEFER: mal=\n\n: mal-sequence= ( seq1 seq2 -- ? )\n    2dup [ length ] bi@ =\n    [ [ mal= ] 2all? ] [ 2drop f ] if ;\n\n:: mal-hashtable= ( h1 h2 -- ? )\n    h1 assoc-size h2 assoc-size = [\n        h1 [| k1 v1 | k1 h2 at* drop v1 mal= ] assoc-all?\n    ] [ f ] if ;\n\n: mal= ( obj1 obj2 -- ? )\n    2dup [ hashtable? ] bi@ and\n    [ mal-hashtable= ] [\n        2dup [ { [ ] [ sequence? ] [ string? not ] } 1&& ] bi@ and\n        [ mal-sequence= ] [ = ] if\n    ] if ;\n"
  },
  {
    "path": "impls/factor/run",
    "content": "#!/usr/bin/env bash\nexec factor $(dirname $0)/${STEP:-stepA_mal}/${STEP:-stepA_mal}.factor \"${@}\"\n"
  },
  {
    "path": "impls/factor/step0_repl/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step0_repl\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step0_repl/step0_repl.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: io kernel readline sequences ;\nIN: step0_repl\n\n: READ ( x -- x ) ;\n\n: EVAL ( x -- x ) ;\n\n: PRINT ( x -- x ) ;\n\n: REP ( x -- x ) READ EVAL PRINT ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n             [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\nMAIN: REPL\n"
  },
  {
    "path": "impls/factor/step1_read_print/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step1_read_print\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step1_read_print/step1_read_print.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: continuations io kernel lib.printer lib.reader readline\nsequences ;\nIN: step1_read_print\n\n: READ ( str -- maltype ) read-str ;\n\n: EVAL ( maltype -- maltype ) ;\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\nMAIN: REPL\n"
  },
  {
    "path": "impls/factor/step2_eval/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step2_eval\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step2_eval/step2_eval.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators combinators.short-circuit\ncontinuations fry hashtables io kernel math lib.printer lib.reader lib.types\nquotations readline sequences vectors ;\nIN: step2_eval\n\nCONSTANT: repl-env H{\n    { \"+\" [ + ] }\n    { \"-\" [ - ] }\n    { \"*\" [ * ] }\n    { \"/\" [ / ] }\n}\n\nDEFER: EVAL\n\n: READ ( str -- maltype ) read-str ;\n\n: apply ( maltype env -- maltype )\n    dup quotation? [ drop \"not a fn\" throw ] unless\n    with-datastack\n    first ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    '[ _ EVAL ] map\n    dup empty? [ unclip apply ] unless ;\nM: malsymbol EVAL-switch\n    [ name>> ] dip ?at [ \"no variable \" prepend throw ] unless ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    ! \"EVAL: \" pick pr-str append print flush\n    EVAL-switch ;\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\nMAIN: REPL\n"
  },
  {
    "path": "impls/factor/step3_env/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step3_env\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step3_env/step3_env.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators combinators.short-circuit\ncontinuations fry grouping hashtables io kernel lists locals lib.env lib.printer\nlib.reader lib.types math namespaces quotations readline sequences vectors ;\nIN: step3_env\n\nCONSTANT: repl-bindings H{\n    { \"+\" [ + ] }\n    { \"-\" [ - ] }\n    { \"*\" [ * ] }\n    { \"/\" [ / ] }\n}\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep EVAL ;\n\n: READ ( str -- maltype ) read-str ;\n\n: apply ( maltype env -- maltype )\n    dup quotation? [ drop \"not a fn\" throw ] unless\n    with-datastack\n    first ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n        over first dup malsymbol? [ name>> ] when {\n            { \"def!\" [ [ rest first2 ] dip eval-def! ] }\n            { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n            [ drop '[ _ EVAL ] map unclip apply ]\n        } case\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    f repl-bindings <malenv> repl-env set\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\nMAIN: REPL\n"
  },
  {
    "path": "impls/factor/step4_if_fn_do/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step4_if_fn_do\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step4_if_fn_do/step4_if_fn_do.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators combinators.short-circuit\ncontinuations fry grouping hashtables io kernel lists locals lib.core lib.env\nlib.printer lib.reader lib.types math namespaces quotations readline sequences\nsplitting vectors ;\nIN: step4_if_fn_do\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep EVAL ;\n\n:: eval-if ( params env -- maltype )\n    params first env EVAL { f +nil+ } index not [\n        params second env EVAL\n    ] [\n        params length 2 > [ params third env EVAL ] [ nil ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> EVAL ;\n\nM: callable apply call( x -- y ) ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n        over first dup malsymbol? [ name>> ] when {\n            { \"def!\" [ [ rest first2 ] dip eval-def! ] }\n            { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n            { \"do\" [ [ rest ] dip '[ _ EVAL ] map last ] }\n            { \"if\" [ [ rest ] dip eval-if ] }\n            { \"fn*\" [ [ rest ] dip eval-fn* ] }\n            [ drop '[ _ EVAL ] map unclip apply ]\n        } case\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\nf ns <malenv> repl-env set-global\n\"(def! not (fn* (a) (if a false true)))\" REP drop\n\nMAIN: REPL\n"
  },
  {
    "path": "impls/factor/step5_tco/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step5_tco\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step5_tco/step5_tco.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators combinators.short-circuit\ncontinuations fry grouping hashtables io kernel lists locals lib.core lib.env\nlib.printer lib.reader lib.types math namespaces quotations readline sequences\nsplitting vectors ;\nIN: step5_tco\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype env )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep ;\n\n:: eval-do ( exprs env -- lastform env/f )\n    exprs [\n        { } f\n    ] [\n        unclip-last [ '[ env EVAL drop ] each ] dip env\n    ] if-empty ;\n\n:: eval-if ( params env -- maltype env/f )\n    params first env EVAL { f +nil+ } index not [\n        params second env\n    ] [\n        params length 2 > [ params third env ] [ nil f ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype newenv/f )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> ;\n\nM: callable apply call( x -- y ) f ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n        over first dup malsymbol? [ name>> ] when {\n            { \"def!\" [ [ rest first2 ] dip eval-def! f ] }\n            { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n            { \"do\" [ [ rest ] dip eval-do ] }\n            { \"if\" [ [ rest ] dip eval-if ] }\n            { \"fn*\" [ [ rest ] dip eval-fn* f ] }\n            [ drop '[ _ EVAL ] map unclip apply ]\n        } case [ EVAL ] when*\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\nf ns <malenv> repl-env set-global\n\n\"(def! not (fn* (a) (if a false true)))\" REP drop\n\nMAIN: REPL\n"
  },
  {
    "path": "impls/factor/step6_file/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step6_file\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step6_file/step6_file.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators combinators.short-circuit\ncommand-line continuations fry grouping hashtables io kernel lists locals\nlib.core lib.env lib.printer lib.reader lib.types math namespaces quotations\nreadline sequences splitting vectors ;\nIN: step6_file\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype env )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep ;\n\n:: eval-do ( exprs env -- lastform env/f )\n    exprs [\n        { } f\n    ] [\n        unclip-last [ '[ env EVAL drop ] each ] dip env\n    ] if-empty ;\n\n:: eval-if ( params env -- maltype env/f )\n    params first env EVAL { f +nil+ } index not [\n        params second env\n    ] [\n        params length 2 > [ params third env ] [ nil f ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype newenv/f )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> ;\n\nM: callable apply call( x -- y ) f ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n        over first dup malsymbol? [ name>> ] when {\n            { \"def!\" [ [ rest first2 ] dip eval-def! f ] }\n            { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n            { \"do\" [ [ rest ] dip eval-do ] }\n            { \"if\" [ [ rest ] dip eval-if ] }\n            { \"fn*\" [ [ rest ] dip eval-fn* f ] }\n            [ drop '[ _ EVAL ] map unclip apply ]\n        } case [ EVAL ] when*\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n[ apply [ EVAL ] when* ] mal-apply set-global\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\n: main ( -- )\n    command-line get\n    [ REPL ]\n    [ first \"(load-file \\\"\" \"\\\")\" surround REP drop ]\n    if-empty ;\n\nf ns clone\n[ first repl-env get EVAL ] \"eval\" pick set-at\ncommand-line get dup empty? [ rest ] unless \"*ARGV*\" pick set-at\n<malenv> repl-env set-global\n\n\"\n(def! not (fn* (a) (if a false true)))\n(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\n\" string-lines harvest [ REP drop ] each\n\nMAIN: main\n"
  },
  {
    "path": "impls/factor/step7_quote/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step7_quote\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step7_quote/step7_quote.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators\ncombinators.short-circuit command-line continuations fry\ngrouping hashtables io kernel lists locals lib.core lib.env\nlib.printer lib.reader lib.types math namespaces quotations\nreadline sequences splitting vectors ;\nIN: step7_quote\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype env )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep ;\n\n:: eval-do ( exprs env -- lastform env/f )\n    exprs [\n        { } f\n    ] [\n        unclip-last [ '[ env EVAL drop ] each ] dip env\n    ] if-empty ;\n\n:: eval-if ( params env -- maltype env/f )\n    params first env EVAL { f +nil+ } index not [\n        params second env\n    ] [\n        params length 2 > [ params third env ] [ nil f ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype newenv/f )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> ;\n\nM: callable apply call( x -- y ) f ;\n\nDEFER: quasiquote\n\n: qq_loop ( elt acc -- maltype )\n    [\n        { [ dup array? ]\n          [ dup length 2 = ]\n          [ \"splice-unquote\" over first symeq? ] } 0&& [\n            second \"concat\"\n        ] [\n            quasiquote \"cons\"\n        ] if\n        <malsymbol> swap\n    ]\n    dip 3array ;\n\n: qq_foldr ( xs -- maltype )\n    dup length 0 = [\n        drop { }\n    ] [\n        unclip swap qq_foldr qq_loop\n    ] if ;\n\nGENERIC: quasiquote ( maltype -- maltype )\nM: array     quasiquote\n    { [ dup length 2 = ] [ \"unquote\" over first symeq? ] } 0&&\n    [ second ] [ qq_foldr ] if ;\nM: vector    quasiquote qq_foldr \"vec\" <malsymbol> swap 2array ;\nM: malsymbol quasiquote \"quote\" <malsymbol> swap 2array ;\nM: hashtable quasiquote \"quote\" <malsymbol> swap 2array ;\nM: object    quasiquote ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n        over first dup malsymbol? [ name>> ] when {\n            { \"def!\" [ [ rest first2 ] dip eval-def! f ] }\n            { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n            { \"do\" [ [ rest ] dip eval-do ] }\n            { \"if\" [ [ rest ] dip eval-if ] }\n            { \"fn*\" [ [ rest ] dip eval-fn* f ] }\n            { \"quote\" [ drop second f ] }\n            { \"quasiquote\" [ [ second quasiquote ] dip ] }\n            [ drop '[ _ EVAL ] map unclip apply ]\n        } case [ EVAL ] when*\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n[ apply [ EVAL ] when* ] mal-apply set-global\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\n: main ( -- )\n    command-line get\n    [ REPL ]\n    [ first \"(load-file \\\"\" \"\\\")\" surround REP drop ]\n    if-empty ;\n\nf ns clone\n[ first repl-env get EVAL ] \"eval\" pick set-at\ncommand-line get dup empty? [ rest ] unless \"*ARGV*\" pick set-at\n<malenv> repl-env set-global\n\n\"\n(def! not (fn* (a) (if a false true)))\n(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\n\" string-lines harvest [ REP drop ] each\n\nMAIN: main\n"
  },
  {
    "path": "impls/factor/step8_macros/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step8_macros\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step8_macros/step8_macros.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators\ncombinators.short-circuit command-line continuations fry\ngrouping hashtables io kernel lists locals lib.core lib.env\nlib.printer lib.reader lib.types math namespaces quotations\nreadline sequences splitting vectors ;\nIN: step8_macros\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n:: eval-defmacro! ( key value env -- maltype )\n    value env EVAL malmacro [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype env )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep ;\n\n:: eval-do ( exprs env -- lastform env/f )\n    exprs [\n        { } f\n    ] [\n        unclip-last [ '[ env EVAL drop ] each ] dip env\n    ] if-empty ;\n\n:: eval-if ( params env -- maltype env/f )\n    params first env EVAL { f +nil+ } index not [\n        params second env\n    ] [\n        params length 2 > [ params third env ] [ nil f ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype newenv/f )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> ;\n\nM: callable apply call( x -- y ) f ;\n\nDEFER: quasiquote\n\n: qq_loop ( elt acc -- maltype )\n    [\n        { [ dup array? ]\n          [ dup length 2 = ]\n          [ \"splice-unquote\" over first symeq? ] } 0&& [\n            second \"concat\"\n        ] [\n            quasiquote \"cons\"\n        ] if\n        <malsymbol> swap\n    ]\n    dip 3array ;\n\n: qq_foldr ( xs -- maltype )\n    dup length 0 = [\n        drop { }\n    ] [\n        unclip swap qq_foldr qq_loop\n    ] if ;\n\nGENERIC: quasiquote ( maltype -- maltype )\nM: array     quasiquote\n    { [ dup length 2 = ] [ \"unquote\" over first symeq? ] } 0&&\n    [ second ] [ qq_foldr ] if ;\nM: vector    quasiquote qq_foldr \"vec\" <malsymbol> swap 2array ;\nM: malsymbol quasiquote \"quote\" <malsymbol> swap 2array ;\nM: hashtable quasiquote \"quote\" <malsymbol> swap 2array ;\nM: object    quasiquote ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n            over first dup malsymbol? [ name>> ] when {\n                { \"def!\" [ [ rest first2 ] dip eval-def! f ] }\n                { \"defmacro!\" [ [ rest first2 ] dip eval-defmacro! f ] }\n                { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n                { \"do\" [ [ rest ] dip eval-do ] }\n                { \"if\" [ [ rest ] dip eval-if ] }\n                { \"fn*\" [ [ rest ] dip eval-fn* f ] }\n                { \"quote\" [ drop second f ] }\n                { \"quasiquote\" [ [ second quasiquote ] dip ] }\n                [ drop swap                                 ! env ast\n                  unclip                                    ! env rest first\n                  pick EVAL                                 ! env rest fn\n                  dup { [ malfn? ] [ macro?>> ] } 1&& [\n                      apply                                 ! env maltype newenv\n                      EVAL swap\n                  ] [\n                      [ swap '[ _ EVAL ] map ] dip          ! args fn\n                      apply\n                  ] if\n                ]\n            } case [ EVAL ] when*\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n[ apply [ EVAL ] when* ] mal-apply set-global\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\n: main ( -- )\n    command-line get\n    [ REPL ]\n    [ first \"(load-file \\\"\" \"\\\")\" surround REP drop ]\n    if-empty ;\n\nf ns clone\n[ first repl-env get EVAL ] \"eval\" pick set-at\ncommand-line get dup empty? [ rest ] unless \"*ARGV*\" pick set-at\n<malenv> repl-env set-global\n\n\"\n(def! not (fn* (a) (if a false true)))\n(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\n\" string-lines harvest [ REP drop ] each\n\nMAIN: main\n"
  },
  {
    "path": "impls/factor/step9_try/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"step9_try\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/step9_try/step9_try.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators\ncombinators.short-circuit command-line continuations fry\ngrouping hashtables io kernel lists locals lib.core lib.env\nlib.printer lib.reader lib.types math namespaces quotations\nreadline sequences splitting vectors ;\nIN: step9_try\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n:: eval-defmacro! ( key value env -- maltype )\n    value env EVAL malmacro [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype env )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep ;\n\n:: eval-do ( exprs env -- lastform env/f )\n    exprs [\n        { } f\n    ] [\n        unclip-last [ '[ env EVAL drop ] each ] dip env\n    ] if-empty ;\n\n:: eval-if ( params env -- maltype env/f )\n    params first env EVAL { f +nil+ } index not [\n        params second env\n    ] [\n        params length 2 > [ params third env ] [ nil f ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n:: eval-try* ( params env -- maltype )\n    [ params first env EVAL ]\n    [\n        params length 1 > [\n            params second second env new-env [ env-set ] keep\n            params second third swap EVAL\n        ] [\n            throw\n        ] if\n    ] recover ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype newenv/f )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> ;\n\nM: callable apply call( x -- y ) f ;\n\nDEFER: quasiquote\n\n: qq_loop ( elt acc -- maltype )\n    [\n        { [ dup array? ]\n          [ dup length 2 = ]\n          [ \"splice-unquote\" over first symeq? ] } 0&& [\n            second \"concat\"\n        ] [\n            quasiquote \"cons\"\n        ] if\n        <malsymbol> swap\n    ]\n    dip 3array ;\n\n: qq_foldr ( xs -- maltype )\n    dup length 0 = [\n        drop { }\n    ] [\n        unclip swap qq_foldr qq_loop\n    ] if ;\n\nGENERIC: quasiquote ( maltype -- maltype )\nM: array     quasiquote\n    { [ dup length 2 = ] [ \"unquote\" over first symeq? ] } 0&&\n    [ second ] [ qq_foldr ] if ;\nM: vector    quasiquote qq_foldr \"vec\" <malsymbol> swap 2array ;\nM: malsymbol quasiquote \"quote\" <malsymbol> swap 2array ;\nM: hashtable quasiquote \"quote\" <malsymbol> swap 2array ;\nM: object    quasiquote ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n            over first dup malsymbol? [ name>> ] when {\n                { \"def!\" [ [ rest first2 ] dip eval-def! f ] }\n                { \"defmacro!\" [ [ rest first2 ] dip eval-defmacro! f ] }\n                { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n                { \"do\" [ [ rest ] dip eval-do ] }\n                { \"if\" [ [ rest ] dip eval-if ] }\n                { \"fn*\" [ [ rest ] dip eval-fn* f ] }\n                { \"quote\" [ drop second f ] }\n                { \"quasiquote\" [ [ second quasiquote ] dip ] }\n                { \"try*\" [ [ rest ] dip eval-try* f ] }\n                [ drop swap                                 ! env ast\n                  unclip                                    ! env rest first\n                  pick EVAL                                 ! env rest fn\n                  dup { [ malfn? ] [ macro?>> ] } 1&& [\n                      apply                                 ! env maltype newenv\n                      EVAL swap\n                  ] [\n                      [ swap '[ _ EVAL ] map ] dip          ! args fn\n                      apply\n                  ] if\n                ]\n            } case [ EVAL ] when*\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n[ apply [ EVAL ] when* ] mal-apply set-global\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\n: main ( -- )\n    command-line get\n    [ REPL ]\n    [ first \"(load-file \\\"\" \"\\\")\" surround REP drop ]\n    if-empty ;\n\nf ns clone\n[ first repl-env get EVAL ] \"eval\" pick set-at\ncommand-line get dup empty? [ rest ] unless \"*ARGV*\" pick set-at\n<malenv> repl-env set-global\n\n\"\n(def! not (fn* (a) (if a false true)))\n(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\n\" string-lines harvest [ REP drop ] each\n\nMAIN: main\n"
  },
  {
    "path": "impls/factor/stepA_mal/deploy.factor",
    "content": "USING: tools.deploy.config ;\nH{\n    { deploy-c-types? f }\n    { deploy-help? f }\n    { deploy-name \"stepA_mal\" }\n    { \"stop-after-last-window?\" t }\n    { deploy-unicode? f }\n    { deploy-console? t }\n    { deploy-io 3 }\n    { deploy-reflection 1 }\n    { deploy-ui? f }\n    { deploy-word-defs? f }\n    { deploy-threads? t }\n    { deploy-math? t }\n    { deploy-word-props? f }\n}\n"
  },
  {
    "path": "impls/factor/stepA_mal/stepA_mal.factor",
    "content": "! Copyright (C) 2015 Jordan Lewis.\n! See http://factorcode.org/license.txt for BSD license.\nUSING: accessors arrays assocs combinators\ncombinators.short-circuit command-line continuations fry\ngrouping hashtables io kernel lists locals lib.core lib.env\nlib.printer lib.reader lib.types math namespaces quotations\nreadline sequences splitting strings vectors ;\nIN: stepA_mal\n\nSYMBOL: repl-env\n\nDEFER: EVAL\n\n:: eval-def! ( key value env -- maltype )\n    value env EVAL [ key env env-set ] keep ;\n\n:: eval-defmacro! ( key value env -- maltype )\n    value env EVAL malmacro [ key env env-set ] keep ;\n\n: eval-let* ( bindings body env -- maltype env )\n    [ swap 2 group ] [ new-env ] bi* [\n        dup '[ first2 _ EVAL swap _ env-set ] each\n    ] keep ;\n\n:: eval-do ( exprs env -- lastform env/f )\n    exprs [\n        { } f\n    ] [\n        unclip-last [ '[ env EVAL drop ] each ] dip env\n    ] if-empty ;\n\n:: eval-if ( params env -- maltype env/f )\n    params first env EVAL { f +nil+ } index not [\n        params second env\n    ] [\n        params length 2 > [ params third env ] [ nil f ] if\n    ] if ;\n\n:: eval-fn* ( params env -- maltype )\n    env params first [ name>> ] map params second <malfn> ;\n\n:: eval-try* ( params env -- maltype )\n    [ params first env EVAL ]\n    [\n        params length 1 > [\n            params second second env new-env [ env-set ] keep\n            params second third swap EVAL\n        ] [\n            throw\n        ] if\n    ] recover ;\n\n: args-split ( bindlist -- bindlist restbinding/f )\n    { \"&\" } split1 ?first ;\n\n: make-bindings ( args bindlist restbinding/f -- bindingshash )\n    swapd [ over length cut [ zip ] dip ] dip\n    [ swap 2array suffix ] [ drop ] if* >hashtable ;\n\nGENERIC: apply ( args fn -- maltype newenv/f )\n\nM: malfn apply\n    [ exprs>> nip ]\n    [ env>> nip ]\n    [ binds>> args-split make-bindings ] 2tri <malenv> ;\n\nM: callable apply call( x -- y ) f ;\n\nDEFER: quasiquote\n\n: qq_loop ( elt acc -- maltype )\n    [\n        { [ dup array? ]\n          [ dup length 2 = ]\n          [ \"splice-unquote\" over first symeq? ] } 0&& [\n            second \"concat\"\n        ] [\n            quasiquote \"cons\"\n        ] if\n        <malsymbol> swap\n    ]\n    dip 3array ;\n\n: qq_foldr ( xs -- maltype )\n    dup length 0 = [\n        drop { }\n    ] [\n        unclip swap qq_foldr qq_loop\n    ] if ;\n\nGENERIC: quasiquote ( maltype -- maltype )\nM: array     quasiquote\n    { [ dup length 2 = ] [ \"unquote\" over first symeq? ] } 0&&\n    [ second ] [ qq_foldr ] if ;\nM: vector    quasiquote qq_foldr \"vec\" <malsymbol> swap 2array ;\nM: malsymbol quasiquote \"quote\" <malsymbol> swap 2array ;\nM: hashtable quasiquote \"quote\" <malsymbol> swap 2array ;\nM: object    quasiquote ;\n\n: READ ( str -- maltype ) read-str ;\n\nGENERIC#: EVAL-switch 1 ( maltype env -- maltype )\nM: array EVAL-switch\n    over empty? [ drop ] [\n            over first dup malsymbol? [ name>> ] when {\n                { \"def!\" [ [ rest first2 ] dip eval-def! f ] }\n                { \"defmacro!\" [ [ rest first2 ] dip eval-defmacro! f ] }\n                { \"let*\" [ [ rest first2 ] dip eval-let* ] }\n                { \"do\" [ [ rest ] dip eval-do ] }\n                { \"if\" [ [ rest ] dip eval-if ] }\n                { \"fn*\" [ [ rest ] dip eval-fn* f ] }\n                { \"quote\" [ drop second f ] }\n                { \"quasiquote\" [ [ second quasiquote ] dip ] }\n                { \"try*\" [ [ rest ] dip eval-try* f ] }\n                [ drop swap                                 ! env ast\n                  unclip                                    ! env rest first\n                  pick EVAL                                 ! env rest fn\n                  dup { [ malfn? ] [ macro?>> ] } 1&& [\n                      apply                                 ! env maltype newenv\n                      EVAL swap\n                  ] [\n                      [ swap '[ _ EVAL ] map ] dip          ! args fn\n                      apply\n                  ] if\n                ]\n            } case [ EVAL ] when*\n    ] if ;\nM: malsymbol EVAL-switch env-get ;\nM: vector    EVAL-switch '[ _ EVAL ] map ;\nM: hashtable EVAL-switch '[ _ EVAL ] assoc-map ;\nM: object    EVAL-switch drop ;\n\n: EVAL ( maltype env -- maltype )\n    \"DEBUG-EVAL\" <malsymbol> over env-find [\n        { f +nil+ } index not\n        [\n            \"EVAL: \" pick pr-str append print flush\n        ] when\n    ] [ drop ] if\n    EVAL-switch ;\n\n[ apply [ EVAL ] when* ] mal-apply set-global\n\n: PRINT ( maltype -- str ) pr-str ;\n\n: REP ( str -- str )\n    [\n        READ repl-env get EVAL PRINT\n    ] [\n        nip pr-str \"Error: \" swap append\n    ] recover ;\n\n: REPL ( -- )\n    \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\" REP drop\n    [\n        \"user> \" readline [\n            [ REP print flush ] unless-empty\n        ] keep\n    ] loop ;\n\n: main ( -- )\n    command-line get\n    [ REPL ]\n    [ first \"(load-file \\\"\" \"\\\")\" surround REP drop ]\n    if-empty ;\n\nf ns clone\n[ first repl-env get EVAL ] \"eval\" pick set-at\ncommand-line get dup empty? [ rest ] unless \"*ARGV*\" pick set-at\n<malenv> repl-env set-global\n\n\"\n(def! *host-language* \\\"factor\\\")\n(def! not (fn* (a) (if a false true)))\n(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\n\" string-lines harvest [ READ repl-env get EVAL drop ] each\n\nMAIN: main\n"
  },
  {
    "path": "impls/factor/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/fantom/Dockerfile",
    "content": "FROM ubuntu:bionic\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Java and Unzip\nRUN apt-get -y install openjdk-8-jdk unzip\n\n# Fantom and JLine\nRUN cd /tmp && curl -sfLO https://github.com/fantom-lang/fantom/releases/download/v1.0.75/fantom-1.0.75.zip \\\n    && unzip -q fantom-1.0.75.zip \\\n    && rm fantom-1.0.75.zip \\\n    && mv fantom-1.0.75 /opt/fantom \\\n    && cd /opt/fantom \\\n    && bash adm/unixsetup \\\n    && curl -sfL -o /opt/fantom/lib/java/jline.jar https://repo1.maven.org/maven2/jline/jline/2.14.6/jline-2.14.6.jar \\\n    && sed -i '/java.options/ s/^\\/\\/ *\\(.*\\)$/\\1 -Djline.expandevents=false/' /opt/fantom/etc/sys/config.props\n\nENV PATH /opt/fantom/bin:$PATH\nENV HOME /mal\n"
  },
  {
    "path": "impls/fantom/Makefile",
    "content": "all: dist\n\ndist: lib/fan/mal.pod\n\nlib/fan:\n\tmkdir -p $@\n\nlib/fan/mal.pod: lib/fan/stepA_mal.pod\n\tcp -a $< $@\n\nlib/fan/step%.pod: src/step%/build.fan src/step%/fan/*.fan lib/fan/mallib.pod\n\tFAN_ENV=util::PathEnv FAN_ENV_PATH=. fan $<\n\nlib/fan/mallib.pod: src/mallib/build.fan src/mallib/fan/*.fan lib/fan\n\tFAN_ENV=util::PathEnv FAN_ENV_PATH=. fan $<\n\nclean:\n\trm -rf lib\n"
  },
  {
    "path": "impls/fantom/run",
    "content": "#!/usr/bin/env bash\nexport FAN_ENV=util::PathEnv\nexport FAN_ENV_PATH=\"$(dirname $0)\"\nexec fan ${STEP:-stepA_mal} \"$@\"\n"
  },
  {
    "path": "impls/fantom/src/mallib/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"mallib\"\n    summary = \"mal library pod\"\n    depends = [\"sys 1.0\", \"compiler 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/mallib/fan/core.fan",
    "content": "class Core\n{\n  static private MalVal prn(MalVal[] a)\n  {\n    echo(a.join(\" \") { it.toString(true) })\n    return MalNil.INSTANCE\n  }\n\n  static private MalVal println(MalVal[] a)\n  {\n    echo(a.join(\" \") { it.toString(false) })\n    return MalNil.INSTANCE\n  }\n\n  static private MalVal readline(MalVal[] a)\n  {\n    line := Env.cur.prompt((a[0] as MalString).value)\n    return line == null ? MalNil.INSTANCE : MalString.make(line)\n  }\n\n  static private MalVal concat(MalVal[] a)\n  {\n    return MalList(a.reduce(MalVal[,]) |MalVal[] r, MalSeq v -> MalVal[]| { r.addAll(v.value) })\n  }\n\n  static private MalVal apply(MalVal[] a)\n  {\n    f := a[0] as MalFunc\n    args := a[1..-2]\n    args.addAll(((MalSeq)a[-1]).value)\n    return f.call(args)\n  }\n\n  static private MalVal swap_bang(MalVal[] a)\n  {\n    atom := a[0] as MalAtom\n    MalVal[] args := [atom.value]\n    args.addAll(a[2..-1])\n    f := a[1] as MalFunc\n    return atom.set(f.call(args))\n  }\n\n  static Str:MalFunc ns()\n  {\n    return [\n      \"=\":           MalFunc { MalTypes.toMalBool(it[0] == it[1]) },\n      \"throw\":       MalFunc { throw MalException(it[0]) },\n\n      \"nil?\":        MalFunc { MalTypes.toMalBool(it[0] is MalNil) },\n      \"true?\":       MalFunc { MalTypes.toMalBool(it[0] is MalTrue) },\n      \"false?\":      MalFunc { MalTypes.toMalBool(it[0] is MalFalse) },\n      \"string?\":     MalFunc { MalTypes.toMalBool(it[0] is MalString && !((MalString)it[0]).isKeyword) },\n      \"symbol\":      MalFunc { MalSymbol.makeFromVal(it[0]) },\n      \"symbol?\":     MalFunc { MalTypes.toMalBool(it[0] is MalSymbol) },\n      \"keyword\":     MalFunc { MalString.makeKeyword((it[0] as MalString).value) },\n      \"keyword?\":    MalFunc { MalTypes.toMalBool(it[0] is MalString && ((MalString)it[0]).isKeyword) },\n      \"number?\":     MalFunc { MalTypes.toMalBool(it[0] is MalInteger) },\n      \"fn?\":         MalFunc { MalTypes.toMalBool(it[0] is MalFunc && !((it[0] as MalUserFunc)?->isMacro ?: false)) },\n      \"macro?\":      MalFunc { MalTypes.toMalBool(it[0] is MalUserFunc && ((MalUserFunc)it[0]).isMacro) },\n\n      \"pr-str\":      MalFunc { MalString.make(it.join(\" \") |MalVal e -> Str| { e.toString(true) }) },\n      \"str\":         MalFunc { MalString.make(it.join(\"\") |MalVal e -> Str| { e.toString(false) }) },\n      \"prn\":         MalFunc(#prn.func),\n      \"println\":     MalFunc(#println.func),\n      \"read-string\": MalFunc { Reader.read_str((it[0] as MalString).value) },\n      \"readline\":    MalFunc(#readline.func),\n      \"slurp\":       MalFunc { MalString.make(File((it[0] as MalString).value.toUri).readAllStr) },\n\n      \"<\":           MalFunc { MalTypes.toMalBool((it[0] as MalInteger).value < (it[1] as MalInteger).value) },\n      \"<=\":          MalFunc { MalTypes.toMalBool((it[0] as MalInteger).value <= (it[1] as MalInteger).value) },\n      \">\":           MalFunc { MalTypes.toMalBool((it[0] as MalInteger).value > (it[1] as MalInteger).value) },\n      \">=\":          MalFunc { MalTypes.toMalBool((it[0] as MalInteger).value >= (it[1] as MalInteger).value) },\n      \"+\":           MalFunc { MalInteger((it[0] as MalInteger).value + (it[1] as MalInteger).value) },\n      \"-\":           MalFunc { MalInteger((it[0] as MalInteger).value - (it[1] as MalInteger).value) },\n      \"*\":           MalFunc { MalInteger((it[0] as MalInteger).value * (it[1] as MalInteger).value) },\n      \"/\":           MalFunc { MalInteger((it[0] as MalInteger).value / (it[1] as MalInteger).value) },\n      \"time-ms\":     MalFunc { MalInteger(DateTime.nowTicks / 1000000) },\n\n      \"list\":        MalFunc { MalList(it) },\n      \"list?\":       MalFunc { MalTypes.toMalBool(it[0] is MalList) },\n      \"vector\":      MalFunc { MalVector(it) },\n      \"vector?\":     MalFunc { MalTypes.toMalBool(it[0] is MalVector) },\n      \"hash-map\":    MalFunc { MalHashMap.fromList(it) },\n      \"map?\":        MalFunc { MalTypes.toMalBool(it[0] is MalHashMap) },\n      \"assoc\":       MalFunc { (it[0] as MalHashMap).assoc(it[1..-1]) },\n      \"dissoc\":      MalFunc { (it[0] as MalHashMap).dissoc(it[1..-1]) },\n      \"get\":         MalFunc { it[0] is MalNil ? MalNil.INSTANCE : (it[0] as MalHashMap).get2((MalString)it[1], MalNil.INSTANCE) },\n      \"contains?\":   MalFunc { MalTypes.toMalBool((it[0] as MalHashMap).containsKey((MalString)it[1])) },\n      \"keys\":        MalFunc { MalList((it[0] as MalHashMap).keys) },\n      \"vals\":        MalFunc { MalList((it[0] as MalHashMap).vals) },\n\n      \"sequential?\": MalFunc { MalTypes.toMalBool(it[0] is MalSeq) },\n      \"cons\":        MalFunc { MalList([it[0]].addAll((it[1] as MalSeq).value)) },\n      \"concat\":      MalFunc(#concat.func),\n      \"vec\":         MalFunc { MalVector((it[0] as MalSeq).value) },\n      \"nth\":         MalFunc { (it[0] as MalSeq).nth((it[1] as MalInteger).value) },\n      \"first\":       MalFunc { (it[0] as MalSeq)?.first ?: MalNil.INSTANCE },\n      \"rest\":        MalFunc { (it[0] as MalSeq)?.rest ?: MalList([,]) },\n      \"empty?\":      MalFunc { MalTypes.toMalBool((it[0] as MalSeq).isEmpty) },\n      \"count\":       MalFunc { MalInteger(it[0].count) },\n      \"apply\":       MalFunc(#apply.func),\n      \"map\":         MalFunc { (it[1] as MalSeq).map(it[0]) },\n\n      \"conj\":        MalFunc { (it[0] as MalSeq).conj(it[1..-1]) },\n      \"seq\":         MalFunc { it[0].seq },\n\n      \"meta\":        MalFunc { it[0].meta() },\n      \"with-meta\":   MalFunc { it[0].with_meta(it[1]) },\n      \"atom\":        MalFunc { MalAtom(it[0]) },\n      \"atom?\":       MalFunc { MalTypes.toMalBool(it[0] is MalAtom) },\n      \"deref\":       MalFunc { (it[0] as MalAtom).value },\n      \"reset!\":      MalFunc { (it[0] as MalAtom).set(it[1]) },\n      \"swap!\":       MalFunc(#swap_bang.func),\n\n      \"fantom-eval\": MalFunc { Interop.fantomEvaluate((it[0] as MalString).value) }\n    ]\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/mallib/fan/env.fan",
    "content": "class MalEnv\n{\n  private Str:MalVal data := [:]\n  private MalEnv? outer\n\n  new make(MalEnv? outer := null, MalSeq? binds := null, MalSeq? exprs := null)\n  {\n    this.outer = outer\n    if (binds != null && exprs != null)\n    {\n      for (i := 0; i < binds.count; i++)\n      {\n        if ((binds[i] as MalSymbol).value == \"&\")\n        {\n          set(binds[i + 1], MalList(exprs[i..-1]))\n          break\n        }\n        else\n          set(binds[i], exprs[i])\n      }\n    }\n  }\n\n  MalVal set(MalSymbol key, MalVal value)\n  {\n    data[key.value] = value\n    return value\n  }\n\n  MalVal? get(Str key)\n  {\n    return data.containsKey(key) ? data[key] : outer?.get(key)\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/mallib/fan/interop.fan",
    "content": "using compiler\n\ninternal class Interop\n{\n  static Pod? compile(Str innerBody)\n  {\n    ci := CompilerInput\n    {\n      podName     = \"mal_fantom_interop_${DateTime.nowUnique}\"\n      summary     = \"\"\n      isScript    = true\n      version     = Version.defVal\n      log.level   = LogLevel.silent\n      output      = CompilerOutputMode.transientPod\n      mode        = CompilerInputMode.str\n      srcStr      = \"class InteropDummyClass {\\nstatic Obj? _evalfunc() {\\n $innerBody \\n}\\n}\"\n      srcStrLoc   = Loc(\"mal_fantom_interop\")\n    }\n    try\n      return Compiler(ci).compile.transientPod\n    catch (CompilerErr e)\n      return null\n  }\n\n  static Obj? evaluate(Str line)\n  {\n    p := compile(line)\n    if (p == null)\n      p = compile(\"return $line\")\n    if (p == null)\n      p = compile(\"$line\\nreturn null\")\n    if (p == null)\n      return null\n    method := p.types.first.method(\"_evalfunc\")\n    try\n      return method.call()\n    catch (Err e)\n      return null\n  }\n\n  static MalVal fantomToMal(Obj? obj)\n  {\n    if (obj == null)\n      return MalNil.INSTANCE\n    else if (obj is Bool)\n      return MalTypes.toMalBool((Bool)obj)\n    else if (obj is Int)\n      return MalInteger((Int)obj)\n    else if (obj is List)\n      return MalList((obj as List).map |Obj? e -> MalVal| { fantomToMal(e) })\n    else if (obj is Map)\n    {\n      m := [Str:MalVal][:]\n      (obj as Map).each |v, k| { m.set(k.toStr, fantomToMal(v)) }\n      return MalHashMap.fromMap(m)\n    }\n    else\n      return MalString.make(obj.toStr)\n  }\n\n  static MalVal fantomEvaluate(Str line)\n  {\n    return fantomToMal(evaluate(line))\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/mallib/fan/reader.fan",
    "content": "internal class TokenReader\n{\n  const Str[] tokens\n  private Int position := 0\n\n  new make(Str[] new_tokens) { tokens = new_tokens }\n\n  Str? peek()\n  {\n    if (position >= tokens.size) return null\n    return tokens[position]\n  }\n\n  Str next() { return tokens[position++] }\n}\n\nclass Reader\n{\n  private static Str[] tokenize(Str s)\n  {\n    r := Regex <|[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)|>\n    m := r.matcher(s)\n    tokens := Str[,]\n    while (m.find())\n    {\n      token := m.group(1)\n      if (token.isEmpty || token[0] == ';') continue\n      tokens.add(m.group(1))\n    }\n    return tokens\n  }\n\n  private static Str unescape_str(Str s)\n  {\n    return s.replace(\"\\\\\\\\\", \"\\u029e\").replace(\"\\\\\\\"\", \"\\\"\").replace(\"\\\\n\", \"\\n\").replace(\"\\u029e\", \"\\\\\")\n  }\n\n  private static MalVal read_atom(TokenReader reader)\n  {\n    token := reader.next\n    intRegex := Regex <|^-?\\d+$|>\n    strRegex := Regex <|^\"(?:\\\\.|[^\\\\\"])*\"|>\n    strBadRegex := Regex <|^\".*|>\n    if (token == \"nil\") return MalNil.INSTANCE\n    if (token == \"true\") return MalTrue.INSTANCE\n    if (token == \"false\") return MalFalse.INSTANCE\n    if (intRegex.matches(token)) return MalInteger(token.toInt)\n    if (strRegex.matches(token)) return MalString.make(unescape_str(token[1..-2]))\n    if (strBadRegex.matches(token)) throw Err(\"expected '\\\"', got EOF\")\n    if (token[0] == '\"') return MalString.make(unescape_str(token[1..-2]))\n    if (token[0] == ':') return MalString.makeKeyword(token[1..-1])\n    return MalSymbol(token)\n  }\n\n  private static MalVal[] read_seq(TokenReader reader, Str open, Str close)\n  {\n    reader.next\n    values := MalVal[,]\n    token := reader.peek\n    while (token != close)\n    {\n      if (token == null) throw Err(\"expected '$close', got EOF\")\n      values.add(read_form(reader))\n      token = reader.peek\n    }\n    if (token != close) throw Err(\"Missing '$close'\")\n    reader.next\n    return values\n  }\n\n  private static MalVal read_form(TokenReader reader)\n  {\n    switch (reader.peek)\n    {\n      case \"\\'\":\n        reader.next\n        return MalList([MalSymbol(\"quote\"), read_form(reader)])\n      case \"`\":\n        reader.next\n        return MalList([MalSymbol(\"quasiquote\"), read_form(reader)])\n      case \"~\":\n        reader.next\n        return MalList([MalSymbol(\"unquote\"), read_form(reader)])\n      case \"~@\":\n        reader.next\n        return MalList([MalSymbol(\"splice-unquote\"), read_form(reader)])\n      case \"^\":\n        reader.next\n        meta := read_form(reader)\n        return MalList([MalSymbol(\"with-meta\"), read_form(reader), meta])\n      case \"@\":\n        reader.next\n        return MalList([MalSymbol(\"deref\"), read_form(reader)])\n      case \"(\": return MalList(read_seq(reader, \"(\", \")\"))\n      case \")\": throw Err(\"unexpected ')'\")\n      case \"[\": return MalVector(read_seq(reader, \"[\", \"]\"))\n      case \"]\": throw Err(\"unexpected ']'\")\n      case \"{\": return MalHashMap.fromList(read_seq(reader, \"{\", \"}\"))\n      case \"}\": throw Err(\"unexpected '}'\")\n      default:  return read_atom(reader)\n    }\n  }\n\n  static MalVal read_str(Str s)\n  {\n    return read_form(TokenReader(tokenize(s)));\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/mallib/fan/types.fan",
    "content": "mixin MalVal\n{\n  virtual Str toString(Bool readable) { return toStr }\n  virtual Int count() { throw Err(\"count not implemented\") }\n  virtual MalVal seq() { throw Err(\"seq not implemented\") }\n  abstract MalVal meta()\n  abstract MalVal with_meta(MalVal newMeta)\n}\n\nconst mixin MalValNoMeta : MalVal\n{\n  override MalVal meta() { return MalNil.INSTANCE }\n  override MalVal with_meta(MalVal newMeta) { return this }\n}\n\nconst mixin MalFalseyVal\n{\n}\n\nconst class MalNil : MalValNoMeta, MalFalseyVal\n{\n  static const MalNil INSTANCE := MalNil()\n  override Bool equals(Obj? that) { return that is MalNil }\n  override Str toString(Bool readable) { return \"nil\" }\n  override Int count() { return 0 }\n  override MalVal seq() { return this }\n}\n\nconst class MalTrue : MalValNoMeta\n{\n  static const MalTrue INSTANCE := MalTrue()\n  override Bool equals(Obj? that) { return that is MalTrue }\n  override Str toString(Bool readable) { return \"true\" }\n}\n\nconst class MalFalse : MalValNoMeta, MalFalseyVal\n{\n  static const MalFalse INSTANCE := MalFalse()\n  override Bool equals(Obj? that) { return that is MalFalse }\n  override Str toString(Bool readable) { return \"false\" }\n}\n\nconst class MalInteger : MalValNoMeta\n{\n  const Int value\n  new make(Int v) { value = v }\n  override Bool equals(Obj? that) { return that is MalInteger && (that as MalInteger).value == value }\n  override Str toString(Bool readable) { return value.toStr }\n}\n\nabstract class MalValBase : MalVal\n{\n  private MalVal? metaVal := null\n  override Str toString(Bool readable) { return toStr }\n  override Int count() { throw Err(\"count not implemented\") }\n  override MalVal seq() { throw Err(\"seq not implemented\") }\n  abstract This dup()\n  override MalVal meta() { return metaVal ?: MalNil.INSTANCE }\n  override MalVal with_meta(MalVal newMeta)\n  {\n    v := dup\n    v.metaVal = newMeta\n    return v\n  }\n}\n\nclass MalSymbol : MalValBase\n{\n  const Str value\n  new make(Str v) { value = v }\n  new makeFromVal(MalVal v)\n  {\n    if (v is MalSymbol) return v\n    value = (v as MalString).value\n  }\n  override Bool equals(Obj? that) { return that is MalSymbol && (that as MalSymbol).value == value }\n  override Str toString(Bool readable) { return value }\n  override This dup() { return make(value) }\n}\n\nclass MalString : MalValBase\n{\n  const Str value\n  new make(Str v) { value = v }\n  new makeKeyword(Str v) { value = v[0] == '\\u029e' ? v : \"\\u029e$v\" }\n  override Bool equals(Obj? that) { return that is MalString && (that as MalString).value == value }\n  override Str toString(Bool readable)\n  {\n    if (isKeyword) return \":${value[1..-1]}\"\n    if (readable)\n      return \"\\\"${escapeStr(value)}\\\"\"\n    else\n      return value\n  }\n  Bool isKeyword() { return !value.isEmpty && value[0] == '\\u029e' }\n  static Str escapeStr(Str s)\n  {\n    return s.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\").replace(\"\\n\", \"\\\\n\")\n  }\n  override MalVal seq()\n  {\n    if (value.size == 0) return MalNil.INSTANCE\n    return MalList(value.chars.map |Int c -> MalString| { MalString.make(Str.fromChars([c])) })\n  }\n  override This dup() { return make(value) }\n}\n\nabstract class MalSeq : MalValBase\n{\n  MalVal[] value { protected set }\n  new make(MalVal[] v) { value = v.ro }\n  override Bool equals(Obj? that) { return that is MalSeq && (that as MalSeq).value == value }\n  Bool isEmpty() { return value.isEmpty }\n  override Int count() { return value.size }\n  @Operator MalVal get(Int index) { return value[index] }\n  @Operator MalVal[] getRange(Range range) { return value[range] }\n  protected Str serialize(Bool readable) { return value.join(\" \") { it.toString(readable) } }\n  abstract MalSeq drop(Int n)\n  MalVal nth(Int index) { return index < count ? get(index) : throw Err(\"nth: index out of range\") }\n  MalVal first() { return isEmpty ? MalNil.INSTANCE : value[0] }\n  MalList rest() { return MalList(isEmpty ? [,] : value[1..-1]) }\n  MalList map(MalFunc f) { return MalList(value.map |MalVal v -> MalVal| { f.call([v]) } ) }\n  abstract MalSeq conj(MalVal[] args)\n}\n\nclass MalList : MalSeq\n{\n  new make(MalVal[] v) : super.make(v) {}\n  override Str toString(Bool readable) { return \"(${serialize(readable)})\" }\n  override MalList drop(Int n) { return make(value[n..-1]) }\n  override MalVal seq() { return isEmpty ? MalNil.INSTANCE : this }\n  override MalList conj(MalVal[] args) { return MalList(value.rw.insertAll(0, args.reverse)) }\n  override This dup() { return make(value) }\n}\n\nclass MalVector : MalSeq\n{\n  new make(MalVal[] v) : super.make(v) {}\n  override Str toString(Bool readable) { return \"[${serialize(readable)}]\" }\n  override MalVector drop(Int n) { return make(value[n..-1]) }\n  override MalVal seq() { return isEmpty ? MalNil.INSTANCE : MalList(value) }\n  override MalVector conj(MalVal[] args) { return MalVector(value.rw.addAll(args)) }\n  override This dup() { return make(value) }\n}\n\nclass MalHashMap : MalValBase\n{\n  Str:MalVal value { private set }\n  new fromList(MalVal[] lst) {\n    m := [Str:MalVal][:]\n    for (i := 0; i < lst.size; i += 2)\n      m.add((lst[i] as MalString).value, (MalVal)lst[i + 1])\n    value = m.ro\n  }\n  new fromMap(Str:MalVal m) { value = m.ro }\n  override Bool equals(Obj? that) { return that is MalHashMap && (that as MalHashMap).value == value }\n  override Str toString(Bool readable)\n  {\n    elements := Str[,]\n    value.each(|MalVal v, Str k| { elements.add(MalString.make(k).toString(readable)); elements.add(v.toString(readable)) })\n    s := elements.join(\" \")\n    return \"{$s}\"\n  }\n  override Int count() { return value.size }\n  @Operator MalVal get(Str key) { return value[key] }\n  MalVal get2(MalString key, MalVal? def := null) { return value.get(key.value, def) }\n  Bool containsKey(MalString key) { return value.containsKey(key.value) }\n  MalVal[] keys() { return value.keys.map |Str k -> MalVal| { MalString.make(k) } }\n  MalVal[] vals() { return value.vals }\n  MalHashMap assoc(MalVal[] args)\n  {\n    newValue := value.dup\n    for (i := 0; i < args.size; i += 2)\n      newValue.set((args[i] as MalString).value, args[i + 1])\n    return fromMap(newValue)\n  }\n  MalHashMap dissoc(MalVal[] args)\n  {\n    newValue := value.dup\n    args.each { newValue.remove((it as MalString).value) }\n    return fromMap(newValue)\n  }\n  override This dup() { return fromMap(value) }\n}\n\nclass MalFunc : MalValBase\n{\n  protected |MalVal[] a -> MalVal| f\n  new make(|MalVal[] a -> MalVal| func) { f = func }\n  MalVal call(MalVal[] a) { return f(a) }\n  override Str toString(Bool readable) { return \"<Function>\" }\n  override This dup() { return make(f) }\n}\n\nclass MalUserFunc : MalFunc\n{\n  MalVal ast { private set }\n  private MalEnv env\n  private MalSeq params\n  Bool isMacro := false\n  new make(MalVal ast, MalEnv env, MalSeq params, |MalVal[] a -> MalVal| func, Bool isMacro := false) : super.make(func)\n  {\n    this.ast = ast\n    this.env = env\n    this.params = params\n    this.isMacro = isMacro\n  }\n  MalEnv genEnv(MalSeq args) { return MalEnv(env, params, args) }\n  override Str toString(Bool readable) { return \"<Function:args=${params.toString(readable)}, isMacro=${isMacro}>\" }\n  override This dup() { return make(ast, env, params, f, isMacro) }\n}\n\nclass MalAtom : MalValBase\n{\n  MalVal value\n  new make(MalVal v) { value = v }\n  override Str toString(Bool readable) { return \"(atom ${value.toString(readable)})\" }\n  override Bool equals(Obj? that) { return that is MalAtom && (that as MalAtom).value == value }\n  MalVal set(MalVal v) { value = v; return value }\n  override This dup() { return make(value) }\n}\n\nclass MalTypes\n{\n  static MalVal toMalBool(Bool cond) { return cond ? MalTrue.INSTANCE : MalFalse.INSTANCE }\n  static Bool isPair(MalVal a) { return a is MalSeq && !(a as MalSeq).isEmpty }\n}\n\nconst class MalException : Err\n{\n  const Str serializedValue\n  new make(MalVal v) : super.make(\"Mal exception\") { serializedValue = v.toString(true) }\n  MalVal getValue() { return Reader.read_str(serializedValue) }\n}\n"
  },
  {
    "path": "impls/fantom/src/step0_repl/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step0_repl\"\n    summary = \"mal step0_repl pod\"\n    depends = [\"sys 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step0_repl/fan/main.fan",
    "content": "class Main\n{\n  static Str READ(Str s)\n  {\n    return s\n  }\n\n  static Str EVAL(Str ast, Str env)\n  {\n    return ast\n  }\n\n  static Str PRINT(Str exp)\n  {\n    return exp\n  }\n\n  static Str REP(Str s, Str env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main()\n  {\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      echo(REP(line, \"\"))\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step1_read_print/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step1_read_print\"\n    summary = \"mal step1_read_print pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step1_read_print/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static MalVal EVAL(MalVal ast, Str env)\n  {\n    return ast\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, Str env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main()\n  {\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, \"\"))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step2_eval/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step2_eval\"\n    summary = \"mal step2_eval pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step2_eval/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static MalVal EVAL(MalVal ast, Str:MalVal env)\n  {\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env[varName] ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }\n\n                  malfunc := f as MalFunc\n                  return malfunc.call(args)\n\n        default:\n          return ast\n      }\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, Str:MalVal env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main()\n  {\n    repl_env := [\n      \"+\": MalFunc { MalInteger((it[0] as MalInteger).value + (it[1] as MalInteger).value) },\n      \"-\": MalFunc { MalInteger((it[0] as MalInteger).value - (it[1] as MalInteger).value) },\n      \"*\": MalFunc { MalInteger((it[0] as MalInteger).value * (it[1] as MalInteger).value) },\n      \"/\": MalFunc { MalInteger((it[0] as MalInteger).value / (it[1] as MalInteger).value) }\n    ]\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step3_env/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step3_env\"\n    summary = \"mal step3_env pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step3_env/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              return EVAL(astList[2], let_env)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }\n\n                  malfunc := f as MalFunc\n                  return malfunc.call(args)\n\n          }\n        default:\n          return ast\n      }\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main()\n  {\n    repl_env := MalEnv()\n    repl_env.set(MalSymbol(\"+\"), MalFunc { MalInteger((it[0] as MalInteger).value + (it[1] as MalInteger).value) })\n    repl_env.set(MalSymbol(\"-\"), MalFunc { MalInteger((it[0] as MalInteger).value - (it[1] as MalInteger).value) })\n    repl_env.set(MalSymbol(\"*\"), MalFunc { MalInteger((it[0] as MalInteger).value * (it[1] as MalInteger).value) })\n    repl_env.set(MalSymbol(\"/\"), MalFunc { MalInteger((it[0] as MalInteger).value / (it[1] as MalInteger).value) })\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step4_if_fn_do/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step4_if_fn_do\"\n    summary = \"mal step4_if_fn_do pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step4_if_fn_do/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              return EVAL(astList[2], let_env)\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              return EVAL(astList[-1], env)\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                return astList.count > 3 ? EVAL(astList[3], env) : MalNil.INSTANCE\n              else\n                return EVAL(astList[2], env)\n            case \"fn*\":\n              return MalFunc { EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(it))) }\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }\n              switch (f.typeof)\n              {\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main()\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step5_tco/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step5_tco\"\n    summary = \"mal step5_tco pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step5_tco/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n    while (true)\n    {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              env = let_env\n              ast = astList[2]\n              // TCO\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              ast = astList[-1]\n              // TCO\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                ast = astList.count > 3 ? astList[3] : MalNil.INSTANCE\n              else\n                ast = astList[2]\n              // TCO\n            case \"fn*\":\n              f := |MalVal[] a -> MalVal|\n              {\n                return EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(a)))\n              }\n              return MalUserFunc(astList[2], env, (MalSeq)astList[1], f)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }\n              switch (f.typeof)\n              {\n                case MalUserFunc#:\n                  user_fn := f as MalUserFunc\n                  ast = user_fn.ast\n                  env = user_fn.genEnv(MalList(args))\n                  // TCO\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n    }\n    return MalNil.INSTANCE // never reached\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main()\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step6_file/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step6_file\"\n    summary = \"mal step6_file pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step6_file/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n    while (true)\n    {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              env = let_env\n              ast = astList[2]\n              // TCO\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              ast = astList[-1]\n              // TCO\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                ast = astList.count > 3 ? astList[3] : MalNil.INSTANCE\n              else\n                ast = astList[2]\n              // TCO\n            case \"fn*\":\n              f := |MalVal[] a -> MalVal|\n              {\n                return EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(a)))\n              }\n              return MalUserFunc(astList[2], env, (MalSeq)astList[1], f)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }\n              switch (f.typeof)\n              {\n                case MalUserFunc#:\n                  user_fn := f as MalUserFunc\n                  ast = user_fn.ast\n                  env = user_fn.genEnv(MalList(args))\n                  // TCO\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n    }\n    return MalNil.INSTANCE // never reached\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main(Str[] args)\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n    repl_env.set(MalSymbol(\"eval\"), MalFunc { EVAL(it[0], repl_env) })\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList((args.isEmpty ? args : args[1..-1]).map { MalString.make(it) }))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\n    if (!args.isEmpty)\n    {\n      REP(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n      return\n    }\n\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step7_quote/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step7_quote\"\n    summary = \"mal step7_quote pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step7_quote/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n\n  static MalList qq_loop(MalVal elt, MalList acc)\n  {\n    lst := elt as MalList\n    if (lst?.count == 2 && (lst[0] as MalSymbol)?.value == \"splice-unquote\")\n      return MalList(MalVal[MalSymbol(\"concat\"), lst[1], acc])\n    else\n      return MalList(MalVal[MalSymbol(\"cons\"), quasiquote(elt), acc])\n  }\n\n  static MalList qq_foldr(MalSeq xs)\n  {\n    acc := MalList([,])\n    for (i:=xs.count-1; 0<=i; i-=1)\n      acc = qq_loop(xs[i], acc)\n    return acc\n  }\n\n  static MalVal quasiquote(MalVal ast)\n  {\n    switch (ast.typeof)\n    {\n      case MalList#:\n        lst := ast as MalList\n        if (lst.count == 2 && (lst[0] as MalSymbol)?.value == \"unquote\")\n          return  lst[1]\n        else\n          return qq_foldr((MalSeq)ast)\n      case MalVector#:\n        return MalList(MalVal[MalSymbol(\"vec\"), qq_foldr((MalSeq)ast)])\n      case MalSymbol#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      case MalHashMap#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      default:\n        return ast\n    }\n  }\n\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n    while (true)\n    {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              env = let_env\n              ast = astList[2]\n              // TCO\n            case \"quote\":\n              return astList[1]\n            case \"quasiquote\":\n              ast = quasiquote(astList[1])\n              // TCO\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              ast = astList[-1]\n              // TCO\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                ast = astList.count > 3 ? astList[3] : MalNil.INSTANCE\n              else\n                ast = astList[2]\n              // TCO\n            case \"fn*\":\n              f := |MalVal[] a -> MalVal|\n              {\n                return EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(a)))\n              }\n              return MalUserFunc(astList[2], env, (MalSeq)astList[1], f)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1].map |MalVal v -> MalVal| { EVAL(v, env) }\n              switch (f.typeof)\n              {\n                case MalUserFunc#:\n                  user_fn := f as MalUserFunc\n                  ast = user_fn.ast\n                  env = user_fn.genEnv(MalList(args))\n                  // TCO\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n    }\n    return MalNil.INSTANCE // never reached\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main(Str[] args)\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n    repl_env.set(MalSymbol(\"eval\"), MalFunc { EVAL(it[0], repl_env) })\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList((args.isEmpty ? args : args[1..-1]).map { MalString.make(it) }))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\n    if (!args.isEmpty)\n    {\n      REP(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n      return\n    }\n\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step8_macros/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step8_macros\"\n    summary = \"mal step8_macros pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step8_macros/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n\n  static MalList qq_loop(MalVal elt, MalList acc)\n  {\n    lst := elt as MalList\n    if (lst?.count == 2 && (lst[0] as MalSymbol)?.value == \"splice-unquote\")\n      return MalList(MalVal[MalSymbol(\"concat\"), lst[1], acc])\n    else\n      return MalList(MalVal[MalSymbol(\"cons\"), quasiquote(elt), acc])\n  }\n\n  static MalList qq_foldr(MalSeq xs)\n  {\n    acc := MalList([,])\n    for (i:=xs.count-1; 0<=i; i-=1)\n      acc = qq_loop(xs[i], acc)\n    return acc\n  }\n\n  static MalVal quasiquote(MalVal ast)\n  {\n    switch (ast.typeof)\n    {\n      case MalList#:\n        lst := ast as MalList\n        if (lst.count == 2 && (lst[0] as MalSymbol)?.value == \"unquote\")\n          return  lst[1]\n        else\n          return qq_foldr((MalSeq)ast)\n      case MalVector#:\n        return MalList(MalVal[MalSymbol(\"vec\"), qq_foldr((MalSeq)ast)])\n      case MalSymbol#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      case MalHashMap#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      default:\n        return ast\n    }\n  }\n\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n    while (true)\n    {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              env = let_env\n              ast = astList[2]\n              // TCO\n            case \"quote\":\n              return astList[1]\n            case \"quasiquote\":\n              ast = quasiquote(astList[1])\n              // TCO\n            case \"defmacro!\":\n              f := (EVAL(astList[2], env) as MalUserFunc).dup\n              f.isMacro = true\n              return env.set(astList[1], f)\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              ast = astList[-1]\n              // TCO\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                ast = astList.count > 3 ? astList[3] : MalNil.INSTANCE\n              else\n                ast = astList[2]\n              // TCO\n            case \"fn*\":\n              f := |MalVal[] a -> MalVal|\n              {\n                return EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(a)))\n              }\n              return MalUserFunc(astList[2], env, (MalSeq)astList[1], f)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1]\n              switch (f.typeof)\n              {\n                case MalUserFunc#:\n                  user_fn := f as MalUserFunc\n                  if (user_fn.isMacro) {\n                    ast = user_fn.call(args)\n                    continue // TCO\n                  }\n                  args = args.map |MalVal v -> MalVal| { EVAL(v, env) }\n                  ast = user_fn.ast\n                  env = user_fn.genEnv(MalList(args))\n                  // TCO\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  args = args.map |MalVal v -> MalVal| { EVAL(v, env) }\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n    }\n    return MalNil.INSTANCE // never reached\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main(Str[] args)\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n    repl_env.set(MalSymbol(\"eval\"), MalFunc { EVAL(it[0], repl_env) })\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList((args.isEmpty ? args : args[1..-1]).map { MalString.make(it) }))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if (!args.isEmpty)\n    {\n      REP(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n      return\n    }\n\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step9_try/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"step9_try\"\n    summary = \"mal step9_try pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/step9_try/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n\n  static MalList qq_loop(MalVal elt, MalList acc)\n  {\n    lst := elt as MalList\n    if (lst?.count == 2 && (lst[0] as MalSymbol)?.value == \"splice-unquote\")\n      return MalList(MalVal[MalSymbol(\"concat\"), lst[1], acc])\n    else\n      return MalList(MalVal[MalSymbol(\"cons\"), quasiquote(elt), acc])\n  }\n\n  static MalList qq_foldr(MalSeq xs)\n  {\n    acc := MalList([,])\n    for (i:=xs.count-1; 0<=i; i-=1)\n      acc = qq_loop(xs[i], acc)\n    return acc\n  }\n\n  static MalVal quasiquote(MalVal ast)\n  {\n    switch (ast.typeof)\n    {\n      case MalList#:\n        lst := ast as MalList\n        if (lst.count == 2 && (lst[0] as MalSymbol)?.value == \"unquote\")\n          return  lst[1]\n        else\n          return qq_foldr((MalSeq)ast)\n      case MalVector#:\n        return MalList(MalVal[MalSymbol(\"vec\"), qq_foldr((MalSeq)ast)])\n      case MalSymbol#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      case MalHashMap#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      default:\n        return ast\n    }\n  }\n\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n    while (true)\n    {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              env = let_env\n              ast = astList[2]\n              // TCO\n            case \"quote\":\n              return astList[1]\n            case \"quasiquote\":\n              ast = quasiquote(astList[1])\n              // TCO\n            case \"defmacro!\":\n              f := (EVAL(astList[2], env) as MalUserFunc).dup\n              f.isMacro = true\n              return env.set(astList[1], f)\n            case \"try*\":\n              if (astList.count < 3)\n                return EVAL(astList[1], env)\n              MalVal exc := MalNil.INSTANCE\n              try\n                return EVAL(astList[1], env)\n              catch (MalException e)\n                exc = e.getValue\n              catch (Err e)\n                exc = MalString.make(e.msg)\n              catchClause := astList[2] as MalList\n              return EVAL(catchClause[2], MalEnv(env, MalList([catchClause[1]]), MalList([exc])))\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              ast = astList[-1]\n              // TCO\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                ast = astList.count > 3 ? astList[3] : MalNil.INSTANCE\n              else\n                ast = astList[2]\n              // TCO\n            case \"fn*\":\n              f := |MalVal[] a -> MalVal|\n              {\n                return EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(a)))\n              }\n              return MalUserFunc(astList[2], env, (MalSeq)astList[1], f)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1]\n              switch (f.typeof)\n              {\n                case MalUserFunc#:\n                  user_fn := f as MalUserFunc\n                  if (user_fn.isMacro) {\n                    ast = user_fn.call(args)\n                    continue // TCO\n                  }\n                  args = args.map |MalVal v -> MalVal| { EVAL(v, env) }\n                  ast = user_fn.ast\n                  env = user_fn.genEnv(MalList(args))\n                  // TCO\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  args = args.map |MalVal v -> MalVal| { EVAL(v, env) }\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n    }\n    return MalNil.INSTANCE // never reached\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main(Str[] args)\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n    repl_env.set(MalSymbol(\"eval\"), MalFunc { EVAL(it[0], repl_env) })\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList((args.isEmpty ? args : args[1..-1]).map { MalString.make(it) }))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if (!args.isEmpty)\n    {\n      REP(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n      return\n    }\n\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (MalException e)\n        echo(\"Error: ${e.serializedValue}\")\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/stepA_mal/build.fan",
    "content": "class Build : build::BuildPod\n{\n  new make()\n  {\n    podName = \"stepA_mal\"\n    summary = \"mal stepA_mal pod\"\n    depends = [\"sys 1.0\", \"mallib 1.0\"]\n    srcDirs = [`fan/`]\n    outPodDir = `lib/fan/`\n  }\n}\n"
  },
  {
    "path": "impls/fantom/src/stepA_mal/fan/main.fan",
    "content": "using mallib\n\nclass Main\n{\n\n  static MalList qq_loop(MalVal elt, MalList acc)\n  {\n    lst := elt as MalList\n    if (lst?.count == 2 && (lst[0] as MalSymbol)?.value == \"splice-unquote\")\n      return MalList(MalVal[MalSymbol(\"concat\"), lst[1], acc])\n    else\n      return MalList(MalVal[MalSymbol(\"cons\"), quasiquote(elt), acc])\n  }\n\n  static MalList qq_foldr(MalSeq xs)\n  {\n    acc := MalList([,])\n    for (i:=xs.count-1; 0<=i; i-=1)\n      acc = qq_loop(xs[i], acc)\n    return acc\n  }\n\n  static MalVal quasiquote(MalVal ast)\n  {\n    switch (ast.typeof)\n    {\n      case MalList#:\n        lst := ast as MalList\n        if (lst.count == 2 && (lst[0] as MalSymbol)?.value == \"unquote\")\n          return  lst[1]\n        else\n          return qq_foldr((MalSeq)ast)\n      case MalVector#:\n        return MalList(MalVal[MalSymbol(\"vec\"), qq_foldr((MalSeq)ast)])\n      case MalSymbol#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      case MalHashMap#:\n        return MalList(MalVal[MalSymbol(\"quote\"), ast])\n      default:\n        return ast\n    }\n  }\n\n  static MalVal READ(Str s)\n  {\n    return Reader.read_str(s)\n  }\n\n  static Void debug_eval(MalVal ast, MalEnv env)\n  {\n    value := env.get(\"DEBUG-EVAL\")\n    if ((value != null) && !(value is MalFalseyVal))\n        echo(\"EVAL: ${PRINT(ast)}\")\n  }\n\n  static MalVal EVAL(MalVal ast, MalEnv env)\n  {\n    while (true)\n    {\n      debug_eval(ast, env)\n      switch (ast.typeof)\n      {\n        case MalSymbol#:\n          varName := (ast as MalSymbol).value\n          return env.get(varName) ?: throw Err(\"'$varName' not found\")\n        case MalVector#:\n          newElements := (ast as MalVector).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalVector(newElements)\n        case MalHashMap#:\n          newElements := (ast as MalHashMap).value.map |MalVal v -> MalVal| { EVAL(v, env) }\n          return MalHashMap.fromMap(newElements)\n        case MalList#:\n          astList := ast as MalList\n          if (astList.isEmpty) return ast\n          switch ((astList[0] as MalSymbol)?.value)\n          {\n            case \"def!\":\n              value := EVAL(astList[2], env)\n              return env.set(astList[1], value)\n            case \"let*\":\n              let_env := MalEnv(env)\n              varList := astList[1] as MalSeq\n              for (i := 0; i < varList.count; i += 2)\n                let_env.set(varList[i], EVAL(varList[i + 1], let_env))\n              env = let_env\n              ast = astList[2]\n              // TCO\n            case \"quote\":\n              return astList[1]\n            case \"quasiquote\":\n              ast = quasiquote(astList[1])\n              // TCO\n            case \"defmacro!\":\n              f := (EVAL(astList[2], env) as MalUserFunc).dup\n              f.isMacro = true\n              return env.set(astList[1], f)\n            case \"try*\":\n              if (astList.count < 3)\n                return EVAL(astList[1], env)\n              MalVal exc := MalNil.INSTANCE\n              try\n                return EVAL(astList[1], env)\n              catch (MalException e)\n                exc = e.getValue\n              catch (Err e)\n                exc = MalString.make(e.msg)\n              catchClause := astList[2] as MalList\n              return EVAL(catchClause[2], MalEnv(env, MalList([catchClause[1]]), MalList([exc])))\n            case \"do\":\n              for (i:=1; i<astList.count-1; i+=1)\n                EVAL(astList[i], env);\n              ast = astList[-1]\n              // TCO\n            case \"if\":\n              if (EVAL(astList[1], env) is MalFalseyVal)\n                ast = astList.count > 3 ? astList[3] : MalNil.INSTANCE\n              else\n                ast = astList[2]\n              // TCO\n            case \"fn*\":\n              f := |MalVal[] a -> MalVal|\n              {\n                return EVAL(astList[2], MalEnv(env, (astList[1] as MalSeq), MalList(a)))\n              }\n              return MalUserFunc(astList[2], env, (MalSeq)astList[1], f)\n            default:\n              f := EVAL(astList[0], env)\n              args := astList.value[1..-1]\n              switch (f.typeof)\n              {\n                case MalUserFunc#:\n                  user_fn := f as MalUserFunc\n                  if (user_fn.isMacro) {\n                    ast = user_fn.call(args)\n                    continue // TCO\n                  }\n                  args = args.map |MalVal v -> MalVal| { EVAL(v, env) }\n                  ast = user_fn.ast\n                  env = user_fn.genEnv(MalList(args))\n                  // TCO\n                case MalFunc#:\n                  malfunc := f as MalFunc\n                  args = args.map |MalVal v -> MalVal| { EVAL(v, env) }\n                  return malfunc.call(args)\n                default:\n                  throw Err(\"Unknown type\")\n              }\n          }\n        default:\n          return ast\n      }\n    }\n    return MalNil.INSTANCE // never reached\n  }\n\n  static Str PRINT(MalVal exp)\n  {\n    return exp.toString(true)\n  }\n\n  static Str REP(Str s, MalEnv env)\n  {\n    return PRINT(EVAL(READ(s), env))\n  }\n\n  static Void main(Str[] args)\n  {\n    repl_env := MalEnv()\n    // core.fan: defined using Fantom\n    Core.ns.each |MalFunc V, Str K| { repl_env.set(MalSymbol(K), V) }\n    repl_env.set(MalSymbol(\"eval\"), MalFunc { EVAL(it[0], repl_env) })\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList((args.isEmpty ? args : args[1..-1]).map { MalString.make(it) }))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! *host-language* \\\"fantom\\\")\", repl_env)\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if (!args.isEmpty)\n    {\n      REP(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n      return\n    }\n\n    REP(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env)\n    while (true) {\n      line := Env.cur.prompt(\"user> \")\n      if (line == null) break\n      if (line.isSpace) continue\n      try\n        echo(REP(line, repl_env))\n      catch (MalException e)\n        echo(\"Error: ${e.serializedValue}\")\n      catch (Err e)\n        echo(\"Error: $e.msg\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/fantom/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/fantom/tests/stepA_mal.mal",
    "content": ";; Testing basic fantom interop\n\n(fantom-eval \"7\")\n;=>7\n\n(fantom-eval \"return 3 * 9\")\n;=>27\n\n(fantom-eval \"\\\"7\\\"\")\n;=>\"7\"\n\n(fantom-eval \"\\\"abcd\\\".upper\")\n;=>\"ABCD\"\n\n(fantom-eval \"[7,8,9]\")\n;=>(7 8 9)\n\n(= () (fantom-eval \"[,]\"))\n;=>true\n\n(fantom-eval \"[\\\"abc\\\": 789]\")\n;=>{\"abc\" 789}\n\n(= {} (fantom-eval \"[:]\"))\n;=>true\n\n(fantom-eval \"echo(\\\"hello\\\")\")\n;/hello\n;=>nil\n\n(fantom-eval \"[\\\"a\\\",\\\"b\\\",\\\"c\\\"].join(\\\" \\\") { \\\"X${it}Y\\\" }\")\n;=>\"XaY XbY XcY\"\n\n(fantom-eval \"[1,2,3].map { 1 + it }\")\n;=>(2 3 4)\n\n(fantom-eval \"Env.cur.runtime\")\n;=>\"java\"\n"
  },
  {
    "path": "impls/fennel/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\nENV DEBIAN_FRONTEND=noninteractive\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# fennel\n\nRUN apt-get -y install gcc wget unzip libpcre3-dev\n\n# lua\nRUN \\\nwget http://www.lua.org/ftp/lua-5.4.1.tar.gz && \\\ntar -zxf lua-5.4.1.tar.gz && \\\ncd lua-5.4.1 && \\\nmake linux test && \\\nmake install\n\n# luarocks\nRUN \\\nwget https://luarocks.org/releases/luarocks-3.3.1.tar.gz && \\\ntar zxpf luarocks-3.3.1.tar.gz && \\\ncd luarocks-3.3.1 && \\\n./configure && \\\nmake && \\\nmake install\n\n# fennel, lpeg\nRUN luarocks install fennel\nRUN luarocks install lpeg\n\n# luarocks .cache directory is relative to HOME\nENV HOME /mal"
  },
  {
    "path": "impls/fennel/Makefile",
    "content": "all:\n\ttrue\n"
  },
  {
    "path": "impls/fennel/core.fnl",
    "content": "(local t (require :types))\n(local u (require :utils))\n(local printer (require :printer))\n(local reader (require :reader))\n(local fennel (require :fennel))\n\n(local mal-list\n  (t.make-fn\n    (fn [asts]\n      (t.make-list asts))))\n\n(local mal-list?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"list? takes 1 argument\")))\n      (t.make-boolean (t.list?* (. asts 1))))))\n\n(local mal-empty?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"empty? takes 1 argument\")))\n      (let [arg-ast (. asts 1)]\n        (if (t.nil?* arg-ast)\n            t.mal-true\n            (t.make-boolean (t.empty?* arg-ast)))))))\n\n(local mal-count\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"count takes 1 argument\")))\n      (let [arg-ast (. asts 1)]\n        (if (t.nil?* arg-ast)\n            (t.make-number 0)\n            (t.make-number (length (t.get-value arg-ast))))))))\n\n(local mal-=\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"= takes 2 arguments\")))\n      (let [ast-1 (. asts 1)\n            ast-2 (. asts 2)]\n        (if (t.equals?* ast-1 ast-2)\n            t.mal-true\n            t.mal-false)))))\n\n(local mal-pr-str\n  (t.make-fn\n    (fn [asts]\n      (local buf [])\n      (when (> (length asts) 0)\n        (each [i ast (ipairs asts)]\n          (table.insert buf (printer.pr_str ast true))\n          (table.insert buf \" \"))\n        ;; remove extra space at end\n        (table.remove buf))\n      (t.make-string (table.concat buf)))))\n\n(local mal-str\n  (t.make-fn\n    (fn [asts]\n      (local buf [])\n      (when (> (length asts) 0)\n        (each [i ast (ipairs asts)]\n          (table.insert buf (printer.pr_str ast false))))\n      (t.make-string (table.concat buf)))))\n\n(local mal-prn\n  (t.make-fn\n    (fn [asts]\n      (local buf [])\n      (when (> (length asts) 0)\n        (each [i ast (ipairs asts)]\n          (table.insert buf (printer.pr_str ast true))\n          (table.insert buf \" \"))\n        ;; remove extra space at end\n        (table.remove buf))\n      (print (table.concat buf))\n      t.mal-nil)))\n\n(local mal-println\n  (t.make-fn\n    (fn [asts]\n      (local buf [])\n      (when (> (length asts) 0)\n        (each [i ast (ipairs asts)]\n          (table.insert buf (printer.pr_str ast false))\n          (table.insert buf \" \"))\n        ;; remove extra space at end\n        (table.remove buf))\n      (print (table.concat buf))\n      t.mal-nil)))\n\n(local mal-read-string\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"read-string takes 1 argument\")))\n      (let [res (reader.read_str (t.get-value (. asts 1)))]\n        (if res\n            res\n            (u.throw* (t.make-string \"No code content\")))))))\n\n(local mal-slurp\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"slurp takes 1 argument\")))\n      (let [a-str (t.get-value (. asts 1))]\n        ;; XXX: error handling?\n        (with-open [f (io.open a-str)]\n          ;; XXX: escaping?\n          (t.make-string (f:read \"*a\")))))))\n\n(local mal-atom\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"atom takes 1 argument\")))\n      (t.make-atom (. asts 1)))))\n\n(local mal-atom?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"atom? takes 1 argument\")))\n      (if (t.atom?* (. asts 1))\n        t.mal-true\n        t.mal-false))))\n\n(local mal-deref\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"deref takes 1 argument\")))\n      (let [ast (. asts 1)]\n        (t.deref* ast)))))\n\n(local mal-reset!\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"reset! takes 2 arguments\")))\n      (let [atom-ast (. asts 1)\n            val-ast (. asts 2)]\n        (t.reset!* atom-ast val-ast)))))\n\n(local mal-swap!\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"swap! takes at least 2 arguments\")))\n      (let [atom-ast (. asts 1)\n            fn-ast (. asts 2)\n            args-asts (u.slice asts 3 -1)\n            args-tbl [(t.deref* atom-ast) (table.unpack args-asts)]]\n        (t.reset!* atom-ast\n                   ((t.get-value fn-ast) args-tbl))))))\n\n(local mal-cons\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"cons takes 2 arguments\")))\n      (let [head-ast (. asts 1)\n            tail-ast (. asts 2)]\n        (t.make-list [head-ast\n                      (table.unpack (t.get-value tail-ast))])))))\n\n(local mal-concat\n  (t.make-fn\n    (fn [asts]\n      (local acc [])\n      (for [i 1 (length asts)]\n         (each [j elt (ipairs (t.get-value (. asts i)))]\n           (table.insert acc elt)))\n      (t.make-list acc))))\n\n(local mal-vec\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"vec takes 1 argument\")))\n      (let [ast (. asts 1)]\n        (if (t.vector?* ast)\n            ast\n            ;;\n            (t.list?* ast)\n            (t.make-vector (t.get-value ast))\n            ;;\n            (t.nil?* ast)\n            (t.make-vector [])\n            ;;\n            (u.throw* (t.make-string \"vec takes a vector, list, or nil\")))))))\n\n(local mal-nth\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"nth takes 2 arguments\")))\n      (let [elts (t.get-value (. asts 1))\n            i (t.get-value (. asts 2))]\n        (if (< i (length elts))\n            (. elts (+ i 1))\n            (u.throw* (t.make-string (.. \"Index out of range: \" i))))))))\n\n(local mal-first\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"first takes 1 argument\")))\n      (let [coll-or-nil-ast (. asts 1)]\n        (if (or (t.nil?* coll-or-nil-ast)\n                (t.empty?* coll-or-nil-ast))\n            t.mal-nil\n            (. (t.get-value coll-or-nil-ast) 1))))))\n\n(local mal-rest\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"rest takes 1 argument\")))\n      (let [coll-or-nil-ast (. asts 1)]\n        (if (or (t.nil?* coll-or-nil-ast)\n                (t.empty?* coll-or-nil-ast))\n            (t.make-list [])\n            (t.make-list (u.slice (t.get-value coll-or-nil-ast) 2 -1)))))))\n\n(local mal-throw\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"throw takes 1 argument\")))\n      (u.throw* (. asts 1)))))\n\n;; (apply F A B [C D]) is equivalent to (F A B C D)\n(local mal-apply\n  (t.make-fn\n    (fn [asts]\n      (let [n-asts (length asts)]\n        (when (< n-asts 1)\n          (u.throw* (t.make-string \"apply takes at least 1 argument\")))\n        (let [the-fn (t.get-value (. asts 1))] ; e.g. F\n          (if (= n-asts 1)\n              (the-fn [])\n              (= n-asts 2)\n              (the-fn [(table.unpack (t.get-value (. asts 2)))])\n              (let [args-asts (u.slice asts 2 -2) ; e.g. [A B]\n                    last-asts (t.get-value (u.last asts)) ; e.g. [C D]\n                    fn-args-tbl []]\n                (each [i elt (ipairs args-asts)]\n                  (table.insert fn-args-tbl elt))\n                (each [i elt (ipairs last-asts)]\n                  (table.insert fn-args-tbl elt))\n                (the-fn fn-args-tbl))))))))\n\n(local mal-map\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"map takes at least 2 arguments\")))\n      (let [the-fn (t.get-value (. asts 1))\n            coll (t.get-value (. asts 2))]\n        (t.make-list (u.map #(the-fn [$]) coll))))))\n\n(local mal-nil?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"nil? takes 1 argument\")))\n      (if (t.nil?* (. asts 1))\n          t.mal-true\n          t.mal-false))))\n\n(local mal-true?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"true? takes 1 argument\")))\n      (if (t.true?* (. asts 1))\n        t.mal-true\n        t.mal-false))))\n\n(local mal-false?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"false? takes 1 argument\")))\n      (if (t.false?* (. asts 1))\n        t.mal-true\n        t.mal-false))))\n\n(local mal-symbol?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"symbol? takes 1 argument\")))\n      (if (t.symbol?* (. asts 1))\n        t.mal-true\n        t.mal-false))))\n\n(local mal-symbol\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"symbol takes 1 argument\")))\n      ;; XXX: check that type is string?\n      (t.make-symbol (t.get-value (. asts 1))))))\n\n(local mal-keyword\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"keyword takes 1 argument\")))\n      (let [arg-ast (. asts 1)]\n        (if (t.keyword?* arg-ast)\n            arg-ast\n            ;;\n            (t.string?* arg-ast)\n            (t.make-keyword (.. \":\" (t.get-value arg-ast)))\n            ;;\n            (u.throw* (t.make-string \"Expected string\")))))))\n\n(local mal-keyword?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"keyword? takes 1 argument\")))\n      (if (t.keyword?* (. asts 1))\n          t.mal-true\n          t.mal-false))))\n\n(local mal-vector\n  (t.make-fn\n    (fn [asts]\n      (t.make-vector asts))))\n\n(local mal-vector?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"vector? takes 1 argument\")))\n      (if (t.vector?* (. asts 1))\n          t.mal-true\n          t.mal-false))))\n\n(local mal-sequential?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"sequential? takes 1 argument\")))\n      (if (or (t.list?* (. asts 1))\n              (t.vector?* (. asts 1)))\n          t.mal-true\n          t.mal-false))))\n\n(local mal-map?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"map? takes 1 argument\")))\n      (if (t.hash-map?* (. asts 1))\n          t.mal-true\n          t.mal-false))))\n\n(local mal-hash-map\n  (t.make-fn\n    (fn [asts]\n      (when (= 1 (% (length asts) 2))\n        (u.throw* (t.make-string\n                   \"hash-map takes an even number of arguments\")))\n      (t.make-hash-map asts))))\n\n(local mal-assoc\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 3)\n        (u.throw* (t.make-string \"assoc takes at least 3 arguments\")))\n      (let [head-ast (. asts 1)]\n        (when (not (or (t.hash-map?* head-ast)\n                       (t.nil?* head-ast)))\n          (u.throw* (t.make-string\n                     \"assoc first argument should be a hash-map or nil\")))\n        (if (t.nil?* head-ast)\n            t.mal-nil\n            (let [item-tbl []\n                  kv-asts (u.slice asts 2 -1)\n                  hash-items (t.get-value head-ast)]\n              (for [i 1 (/ (length hash-items) 2)]\n                (let [key (. hash-items (- (* 2 i) 1))]\n                  (var idx 1)\n                  (var found false)\n                  (while (and (not found)\n                              (<= idx (length kv-asts)))\n                    (if (t.equals?* key (. kv-asts idx))\n                        (set found true)\n                        (set idx (+ idx 2))))\n                  (if (not found)\n                      (do\n                       (table.insert item-tbl key)\n                       (table.insert item-tbl (. hash-items (* 2 i))))\n                      (do\n                       (table.insert item-tbl key)\n                       (table.insert item-tbl (. kv-asts (+ idx 1)))\n                       (table.remove kv-asts (+ idx 1))\n                       (table.remove kv-asts idx)))))\n              (each [i elt (ipairs kv-asts)]\n                (table.insert item-tbl elt))\n              (t.make-hash-map item-tbl)))))))\n\n(local mal-dissoc\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"dissoc takes at least 2 arguments\")))\n      (let [head-ast (. asts 1)]\n        (when (not (or (t.hash-map?* head-ast)\n                       (t.nil?* head-ast)))\n          (u.throw* (t.make-string\n                     \"dissoc first argument should be a hash-map or nil\")))\n        (if (t.nil?* head-ast)\n            t.mal-nil\n            (let [item-tbl []\n                  key-asts (u.slice asts 2 -1)\n                  hash-items (t.get-value head-ast)]\n              (for [i 1 (/ (length hash-items) 2)]\n                (let [key (. hash-items (- (* 2 i) 1))]\n                  (var idx 1)\n                  (var found false)\n                  (while (and (not found)\n                              (<= idx (length key-asts)))\n                    (if (t.equals?* key (. key-asts idx))\n                        (set found true)\n                        (set idx (+ idx 1))))\n                  (when (not found)\n                    (table.insert item-tbl key)\n                    (table.insert item-tbl (. hash-items (* 2 i))))))\n              (t.make-hash-map item-tbl)))))))\n\n(local mal-get\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"get takes 2 arguments\")))\n      (let [head-ast (. asts 1)]\n        (when (not (or (t.hash-map?* head-ast)\n                       (t.nil?* head-ast)))\n          (u.throw* (t.make-string\n                     \"get first argument should be a hash-map or nil\")))\n        (if (t.nil?* head-ast)\n            t.mal-nil\n            (let [hash-items (t.get-value head-ast)\n                  key-ast (. asts 2)]\n              (var idx 1)\n              (var found false)\n              (while (and (not found)\n                          (<= idx (length hash-items)))\n                (if (t.equals?* key-ast (. hash-items idx))\n                    (set found true)\n                    (set idx (+ idx 1))))\n              (if found\n                  (. hash-items (+ idx 1))\n                  t.mal-nil)))))))\n\n(local mal-contains?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"contains? takes 2 arguments\")))\n      (let [head-ast (. asts 1)]\n        (when (not (or (t.hash-map?* head-ast)\n                       (t.nil?* head-ast)))\n          (u.throw* (t.make-string\n                     \"contains? first argument should be a hash-map or nil\")))\n        (if (t.nil?* head-ast)\n            t.mal-nil\n            (let [hash-items (t.get-value head-ast)\n                  key-ast (. asts 2)]\n              (var idx 1)\n              (var found false)\n              (while (and (not found)\n                          (<= idx (length hash-items)))\n                (if (t.equals?* key-ast (. hash-items idx))\n                    (set found true)\n                    (set idx (+ idx 1))))\n              (if found\n                  t.mal-true\n                  t.mal-false)))))))\n\n(local mal-keys\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"keys takes 1 argument\")))\n      (let [head-ast (. asts 1)]\n        (when (not (or (t.hash-map?* head-ast)\n                       (t.nil?* head-ast)))\n          (u.throw* (t.make-string\n                     \"keys first argument should be a hash-map or nil\")))\n        (if (t.nil?* head-ast)\n            t.mal-nil\n            (let [item-tbl []\n                  hash-items (t.get-value head-ast)]\n              (for [i 1 (/ (length hash-items) 2)]\n                (let [key (. hash-items (- (* 2 i) 1))]\n                  (table.insert item-tbl key)))\n              (t.make-list item-tbl)))))))\n\n(local mal-vals\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"vals takes 1 argument\")))\n      (let [head-ast (. asts 1)]\n        (when (not (or (t.hash-map?* head-ast)\n                       (t.nil?* head-ast)))\n          (u.throw* (t.make-string\n                     \"vals first argument should be a hash-map or nil\")))\n        (if (t.nil?* head-ast)\n            t.mal-nil\n            (let [item-tbl []\n                  hash-items (t.get-value head-ast)]\n              (for [i 1 (/ (length hash-items) 2)]\n                (let [value (. hash-items (* 2 i))]\n                  (table.insert item-tbl value)))\n              (t.make-list item-tbl)))))))\n\n(local mal-readline\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"vals takes 1 argument\")))\n      (let [prompt (t.get-value (. asts 1))]\n        (io.write prompt)\n        (io.flush)\n        (let [input (io.read)\n              trimmed (string.match input \"^%s*(.-)%s*$\")]\n          (if (> (length trimmed) 0)\n              (t.make-string trimmed)\n              t.mal-nil))))))\n\n(local mal-meta\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"meta takes 1 argument\")))\n      (let [head-ast (. asts 1)]\n        (if (or (t.list?* head-ast)\n                (t.vector?* head-ast)\n                (t.hash-map?* head-ast)\n                (t.fn?* head-ast))\n            (t.get-md head-ast)\n            t.mal-nil)))))\n\n(local mal-with-meta\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"with-meta takes 2 arguments\")))\n      (let [target-ast (. asts 1)\n            meta-ast (. asts 2)]\n        (if (t.list?* target-ast)\n            (t.make-list (t.get-value target-ast) meta-ast)\n            ;;\n            (t.vector?* target-ast)\n            (t.make-vector (t.get-value target-ast) meta-ast)\n            ;;\n            (t.hash-map?* target-ast)\n            (t.make-hash-map (t.get-value target-ast) meta-ast)\n            ;;\n            (t.fn?* target-ast)\n            (t.clone-with-meta target-ast meta-ast)\n            ;;\n            (u.throw*\n             (t.make-string \"Expected list, vector, hash-map, or fn\")))))))\n\n(local mal-string?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"string? takes 1 argument\")))\n      (t.make-boolean (t.string?* (. asts 1))))))\n\n(local mal-number?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"number? takes 1 argument\")))\n      (t.make-boolean (t.number?* (. asts 1))))))\n\n(local mal-fn?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"fn? takes 1 argument\")))\n      (let [target-ast (. asts 1)]\n        (if (and (t.fn?* target-ast)\n                 (not (t.get-is-macro target-ast)))\n            t.mal-true\n            t.mal-false)))))\n\n(local mal-macro?\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"macro? requires 1 argument\")))\n      (let [the-ast (. asts 1)]\n        (if (t.macro?* the-ast)\n            t.mal-true\n            t.mal-false)))))\n\n(local mal-conj\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u.throw* (t.make-string \"conj takes at least 2 arguments\")))\n      (let [coll-ast (. asts 1)\n            item-asts (u.slice asts 2 -1)]\n        (if (t.nil?* coll-ast)\n            (t.make-list (u.reverse item-asts))\n            ;;\n            (t.list?* coll-ast)\n            (t.make-list (u.concat-two (u.reverse item-asts)\n                                       (t.get-value coll-ast)))\n            ;;\n            (t.vector?* coll-ast)\n            (t.make-vector (u.concat-two (t.get-value coll-ast)\n                                         item-asts))\n            ;;\n            (u.throw* (t.make-string \"Expected list, vector, or nil\")))))))\n\n(local mal-seq\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"seq takes 1 argument\")))\n      (let [arg-ast (. asts 1)]\n        (if (t.list?* arg-ast)\n            (if (t.empty?* arg-ast)\n                t.mal-nil\n                arg-ast)\n            ;;\n            (t.vector?* arg-ast)\n            (if (t.empty?* arg-ast)\n                t.mal-nil\n                (t.make-list (t.get-value arg-ast)))\n            ;;\n            (t.string?* arg-ast)\n            (let [a-str (t.get-value arg-ast)\n                  str-len (length a-str)]\n              (if (= str-len 0)\n                  t.mal-nil\n                  (do\n                   (local str-tbl [])\n                   (for [i 1 (length a-str)]\n                     (table.insert str-tbl\n                                   (t.make-string (string.sub a-str i i))))\n                   (t.make-list str-tbl))))\n            ;;\n            (t.nil?* arg-ast)\n            arg-ast\n            ;;\n            (u.throw*\n             (t.make-string \"Expected list, vector, string, or nil\")))))))\n\n(local mal-time-ms\n  (t.make-fn\n    (fn [asts]\n        (t.make-number\n         (math.floor (* 1000000 (os.clock)))))))\n\n(fn fennel-eval*\n  [fennel-val]\n  (if (= \"nil\" (type fennel-val))\n      t.mal-nil\n      (= \"boolean\" (type fennel-val))\n      (t.make-boolean fennel-val)\n      (= \"string\" (type fennel-val))\n      (t.make-string fennel-val)\n      (= \"number\" (type fennel-val))\n      (t.make-number fennel-val)\n      (= \"table\" (type fennel-val))\n      (t.make-list (u.map fennel-eval* fennel-val))\n      (u.throw*\n       (t.make-string (.. \"Unsupported type: \" (type fennel-val))))))\n\n(local mal-fennel-eval\n  (t.make-fn\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u.throw* (t.make-string \"fennel-eval takes 1 argument\")))\n      (let [head-ast (. asts 1)]\n        (when (not (t.string?* head-ast))\n          (u.throw* (t.make-string\n                     \"fennel-eval first argument should be a string\")))\n        (let [(ok? result) (pcall fennel.eval (t.get-value head-ast))]\n          (if ok?\n              (fennel-eval* result)\n              (u.throw*\n               (t.make-string (.. \"Eval failed: \" result)))))))))\n\n{\"+\" (t.make-fn (fn [asts]\n                  (var total 0)\n                  (each [i val (ipairs asts)]\n                    (set total\n                         (+ total (t.get-value val))))\n                  (t.make-number total)))\n \"-\" (t.make-fn (fn [asts]\n                  (var total 0)\n                  (let [n-args (length asts)]\n                    (if (= 0 n-args)\n                        (t.make-number 0)\n                        (= 1 n-args)\n                        (t.make-number (- 0 (t.get-value (. asts 1))))\n                        (do\n                         (set total (t.get-value (. asts 1)))\n                         (for [idx 2 n-args]\n                           (let [cur (t.get-value (. asts idx))]\n                             (set total\n                                  (- total cur))))\n                         (t.make-number total))))))\n \"*\" (t.make-fn (fn [asts]\n                  (var total 1)\n                  (each [i val (ipairs asts)]\n                    (set total\n                         (* total (t.get-value val))))\n                  (t.make-number total)))\n \"/\" (t.make-fn (fn [asts]\n                  (var total 1)\n                  (let [n-args (length asts)]\n                    (if (= 0 n-args)\n                        (t.make-number 1)\n                        (= 1 n-args)\n                        (t.make-number (/ 1 (t.get-value (. asts 1))))\n                        (do\n                         (set total (t.get-value (. asts 1)))\n                         (for [idx 2 n-args]\n                           (let [cur (t.get-value (. asts idx))]\n                             (set total\n                                  (/ total cur))))\n                          (t.make-number total))))))\n \"list\" mal-list\n \"list?\" mal-list?\n \"empty?\" mal-empty?\n \"count\" mal-count\n \"=\" mal-=\n \"<\" (t.make-fn (fn [asts]\n                  (let [val-1 (t.get-value (. asts 1))\n                        val-2 (t.get-value (. asts 2))]\n                    (t.make-boolean (< val-1 val-2)))))\n \"<=\" (t.make-fn (fn [asts]\n                   (let [val-1 (t.get-value (. asts 1))\n                         val-2 (t.get-value (. asts 2))]\n                     (t.make-boolean (<= val-1 val-2)))))\n \">\" (t.make-fn (fn [asts]\n                  (let [val-1 (t.get-value (. asts 1))\n                        val-2 (t.get-value (. asts 2))]\n                    (t.make-boolean (> val-1 val-2)))))\n \">=\" (t.make-fn (fn [asts]\n                   (let [val-1 (t.get-value (. asts 1))\n                         val-2 (t.get-value (. asts 2))]\n                     (t.make-boolean (>= val-1 val-2)))))\n \"pr-str\" mal-pr-str\n \"str\" mal-str\n \"prn\" mal-prn\n \"println\" mal-println\n \"read-string\" mal-read-string\n \"slurp\" mal-slurp\n \"atom\" mal-atom\n \"atom?\" mal-atom?\n \"deref\" mal-deref\n \"reset!\" mal-reset!\n \"swap!\" mal-swap!\n \"cons\" mal-cons\n \"concat\" mal-concat\n \"vec\" mal-vec\n \"nth\" mal-nth\n \"first\" mal-first\n \"rest\" mal-rest\n \"throw\" mal-throw\n \"apply\" mal-apply\n \"map\" mal-map\n \"nil?\" mal-nil?\n \"true?\" mal-true?\n \"false?\" mal-false?\n \"symbol?\" mal-symbol?\n \"symbol\" mal-symbol\n \"keyword\" mal-keyword\n \"keyword?\" mal-keyword?\n \"vector\" mal-vector\n \"vector?\" mal-vector?\n \"sequential?\" mal-sequential?\n \"map?\" mal-map?\n \"hash-map\" mal-hash-map\n \"assoc\" mal-assoc\n \"dissoc\" mal-dissoc\n \"get\" mal-get\n \"contains?\" mal-contains?\n \"keys\" mal-keys\n \"vals\" mal-vals\n \"readline\" mal-readline\n \"meta\" mal-meta\n \"with-meta\" mal-with-meta\n \"string?\" mal-string?\n \"number?\" mal-number?\n \"fn?\" mal-fn?\n \"macro?\" mal-macro?\n \"conj\" mal-conj\n \"seq\" mal-seq\n \"time-ms\" mal-time-ms\n \"fennel-eval\" mal-fennel-eval\n}\n"
  },
  {
    "path": "impls/fennel/env.fnl",
    "content": "(local t (require :types))\n(local u (require :utils))\n\n(fn make-env\n  [outer binds exprs]\n  (local tbl {})\n  (when binds\n    (local n-binds (length binds))\n    (var found-amp false)\n    (var i 1)\n    (while (and (not found-amp)\n                (<= i n-binds))\n      (local c-bind (. binds i))\n      (if (= (t.get-value c-bind) \"&\")\n          (set found-amp true)\n          (set i (+ i 1))))\n    (if (not found-amp)\n        (for [j 1 n-binds]\n          (tset tbl\n                (t.get-value (. binds j))\n                (. exprs j)))\n        (do ; houston, there was an ampersand\n          (for [j 1 (- i 1)] ; things before &\n            (tset tbl\n                  (t.get-value (. binds j))\n                  (. exprs j)))\n          (tset tbl ; after &, put things in a list\n                (t.get-value (. binds (+ i 1)))\n                (t.make-list (u.slice exprs i -1))))))\n  {:outer outer\n   :data tbl})\n\n(fn env-set\n  [env sym-ast val-ast]\n  (tset (. env :data)\n        (t.get-value sym-ast)\n        val-ast)\n  env)\n\n(fn env-get\n  [env key]\n  (or (. env :data key)\n      (let [outer (. env :outer)]\n        (when outer\n              (env-get outer key)))))\n\n(comment\n\n (local test-env (make-env {}))\n\n (env-set test-env\n          (t.make-symbol \"fun\")\n          (t.make-number 1))\n\n (env-get test-env (t.make-symbol \"fun\"))\n\n (local test-env-2 (make-env nil))\n\n (env-set test-env-2\n          (t.make-symbol \"smile\")\n          (t.make-keyword \":yay\"))\n\n (env-get test-env-2 (t.make-symbol \"smile\"))\n\n (local test-env-3 (make-env nil))\n\n (env-set test-env-3\n          (t.make-symbol \"+\")\n          (fn [ast-1 ast-2]\n            (t.make-number (+ (t.get-value ast-1)\n                              (t.get-value ast-2)))))\n\n (env-get test-env-3 (t.make-symbol \"+\"))\n\n )\n\n{:make-env make-env\n :env-set env-set\n :env-get env-get}\n"
  },
  {
    "path": "impls/fennel/printer.fnl",
    "content": "(local t (require :types))\n\n(fn escape\n  [a-str]\n  (pick-values 1\n               (-> a-str\n                   (string.gsub \"\\\\\" \"\\\\\\\\\")\n                   (string.gsub \"\\\"\" \"\\\\\\\"\")\n                   (string.gsub \"\\n\" \"\\\\n\"))))\n\n(fn code*\n  [ast buf print_readably]\n  (let [value (t.get-value ast)]\n    (if (t.nil?* ast)\n        (table.insert buf value)\n        ;;\n        (t.boolean?* ast)\n        (table.insert buf (if value \"true\" \"false\"))\n        ;;\n        (t.number?* ast)\n        (table.insert buf (tostring value))\n        ;;\n        (t.keyword?* ast)\n        (table.insert buf value)\n        ;;\n        (t.symbol?* ast)\n        (table.insert buf value)\n        ;;\n        (t.string?* ast)\n        (if print_readably\n            (do\n             (table.insert buf \"\\\"\")\n             (table.insert buf (escape value))\n             (table.insert buf \"\\\"\"))\n            (table.insert buf value))\n        ;;\n        (t.list?* ast)\n        (do\n         (table.insert buf \"(\")\n         (var remove false)\n         (each [idx elt (ipairs value)]\n               (code* elt buf print_readably)\n               (table.insert buf \" \")\n               (set remove true))\n         (when remove\n           (table.remove buf))\n         (table.insert buf \")\"))\n        ;;\n        (t.vector?* ast)\n        (do\n         (table.insert buf \"[\")\n         (var remove false)\n         (each [idx elt (ipairs value)]\n               (code* elt buf print_readably)\n               (table.insert buf \" \")\n               (set remove true))\n         (when remove\n           (table.remove buf))\n         (table.insert buf \"]\"))\n        ;;\n        (t.hash-map?* ast)\n        (do\n         (table.insert buf \"{\")\n         (var remove false)\n         (each [idx elt (ipairs value)]\n               (code* elt buf print_readably)\n               (table.insert buf \" \")\n               (set remove true))\n         (when remove\n           (table.remove buf))\n         (table.insert buf \"}\"))\n        ;;\n        (t.atom?* ast)\n        (do\n         (table.insert buf \"(atom \")\n         (code* (t.get-value ast) buf print_readably)\n         (table.insert buf \")\")))\n    buf))\n\n(fn pr_str\n  [ast print_readably]\n  (let [buf []]\n    (code* ast buf print_readably)\n    (table.concat buf)))\n\n(comment\n\n (pr_str (t.make-number 1) false)\n\n )\n\n{:pr_str pr_str}\n"
  },
  {
    "path": "impls/fennel/reader.fnl",
    "content": "(local t (require :types))\n(local u (require :utils))\n\n(local lpeg (require :lpeg))\n\n(local P lpeg.P)\n\n(local S lpeg.S)\n\n(local C lpeg.C)\n\n(local V lpeg.V)\n\n(local Cmt lpeg.Cmt)\n\n(fn unescape\n  [a-str]\n  (pick-values 1\n               (-> a-str\n                   (string.gsub \"\\\\\\\\\" \"\\u{029e}\") ;; temporarily hide\n                   (string.gsub \"\\\\\\\"\" \"\\\"\")\n                   (string.gsub \"\\\\n\" \"\\n\")\n                   (string.gsub \"\\u{029e}\" \"\\\\\")))) ;; now replace\n\n(local grammar\n  {1 \"main\"\n  \"main\" (^ (V \"input\") 1)\n  \"input\" (+ (V \"gap\") (V \"form\"))\n  \"gap\" (+ (V \"ws\") (V \"comment\"))\n  \"ws\" (^ (S \" \\f\\n\\r\\t,\") 1)\n  \"comment\" (* \";\"\n               (^ (- (P 1) (S \"\\r\\n\"))\n                  0))\n  \"form\" (+ (V \"boolean\") (V \"nil\")\n            (V \"number\") (V \"keyword\") (V \"symbol\") (V \"string\")\n            (V \"list\") (V \"vector\") (V \"hash-map\")\n            (V \"deref\") (V \"quasiquote\") (V \"quote\")\n            (V \"splice-unquote\")\n            (V \"unquote\")\n            (V \"with-meta\"))\n  \"name-char\" (- (P 1)\n                 (S \" \\f\\n\\r\\t,[]{}()'`~^@\\\";\"))\n  \"nil\" (Cmt (C (* (P \"nil\")\n                   (- (V \"name-char\"))))\n             (fn [s i a]\n                 (values i t.mal-nil)))\n  \"boolean\" (Cmt (C (* (+ (P \"false\") (P \"true\"))\n                       (- (V \"name-char\"))))\n                 (fn [s i a]\n                     (values i (if (= a \"true\")\n                                   t.mal-true\n                                   t.mal-false))))\n  \"number\" (Cmt (C (^ (- (P 1)\n                         (S \" \\f\\n\\r\\t,[]{}()'`~^@\\\";\"))\n                      1))\n                (fn [s i a]\n                    (let [result (tonumber a)]\n                      (if result\n                          (values i (t.make-number result))\n                          nil))))\n  \"keyword\" (Cmt (C (* \":\"\n                       (^ (V \"name-char\") 0)))\n                 (fn [s i a]\n                     (values i (t.make-keyword a))))\n  \"symbol\" (Cmt (^ (V \"name-char\") 1)\n                (fn [s i a]\n                    (values i (t.make-symbol a))))\n  \"string\" (* (P \"\\\"\")\n              (Cmt (C (* (^ (- (P 1)\n                               (S \"\\\"\\\\\"))\n                            0)\n                         (^ (* (P \"\\\\\")\n                               (P 1)\n                               (^ (- (P 1)\n                                     (S \"\\\"\\\\\"))\n                                  0))\n                            0)))\n                   (fn [s i a]\n                       (values i (t.make-string (unescape a)))))\n              (+ (P \"\\\"\")\n                 (P (fn [s i]\n                        (error \"unbalanced \\\"\")))))\n  \"list\" (* (P \"(\")\n            (Cmt (C (^ (V \"input\") 0))\n                 (fn [s i a ...]\n                     (values i (t.make-list [...]))))\n            (+ (P \")\")\n               (P (fn [s i]\n                      (error \"unbalanced )\")))))\n  \"vector\" (* (P \"[\")\n              (Cmt (C (^ (V \"input\") 0))\n                   (fn [s i a ...]\n                       (values i (t.make-vector [...]))))\n              (+ (P \"]\")\n                 (P (fn [s i]\n                        (error \"unbalanced ]\")))))\n  \"hash-map\" (* (P \"{\")\n                (Cmt (C (^ (V \"input\") 0))\n                     (fn [s i a ...]\n                         (values i (t.make-hash-map [...]))))\n                (+ (P \"}\")\n                   (P (fn [s i]\n                          (error \"unbalanced }\")))))\n  \"deref\" (Cmt (C (* (P \"@\")\n                     (V \"form\")))\n               (fn [s i ...]\n                   (let [content [(t.make-symbol \"deref\")]]\n                     (table.insert content (. [...] 2))\n                     (values i (t.make-list content)))))\n  \"quasiquote\" (Cmt (C (* (P \"`\")\n                          (V \"form\")))\n                    (fn [s i ...]\n                        (let [content [(t.make-symbol \"quasiquote\")]]\n                          (table.insert content (. [...] 2))\n                          (values i (t.make-list content)))))\n  \"quote\" (Cmt (C (* (P \"'\")\n                     (V \"form\")))\n               (fn [s i ...]\n                   (let [content [(t.make-symbol \"quote\")]]\n                     (table.insert content (. [...] 2))\n                     (values i (t.make-list content)))))\n  \"splice-unquote\" (Cmt (C (* (P \"~@\")\n                              (V \"form\")))\n                        (fn [s i ...]\n                            (let [content [(t.make-symbol \"splice-unquote\")]]\n                              (table.insert content (. [...] 2))\n                              (values i (t.make-list content)))))\n  \"unquote\" (Cmt (C (* (P \"~\")\n                       (V \"form\")))\n                 (fn [s i ...]\n                     (let [content [(t.make-symbol \"unquote\")]]\n                       (table.insert content (. [...] 2))\n                       (values i (t.make-list content)))))\n  \"with-meta\" (Cmt (C (* (P \"^\")\n                         (V \"form\")\n                         (^ (V \"gap\") 1)\n                         (V \"form\")))\n                   (fn [s i ...]\n                       (let [content [(t.make-symbol \"with-meta\")]]\n                         (table.insert content (. [...] 3))\n                         (table.insert content (. [...] 2))\n                         (values i (t.make-list content)))))\n  })\n\n(comment\n\n (lpeg.match grammar \"; hello\")\n\n (lpeg.match grammar \"nil\")\n\n (lpeg.match grammar \"true\")\n\n (lpeg.match grammar \"false\")\n\n (lpeg.match grammar \"1.2\")\n\n (lpeg.match grammar \"(+ 1 1)\")\n\n (lpeg.match grammar \"[:a :b :c]\")\n\n (lpeg.match grammar \"\\\"hello there\\\"\")\n\n (lpeg.match grammar \"\\\"hello\\\" there\\\"\")\n\n)\n\n(fn read_str\n  [a-str]\n  (let [(ok? result) (pcall lpeg.match grammar a-str)]\n    (if ok?\n        (let [res-type (type result)]\n          (if (= res-type \"table\")\n              result\n              (u.throw* t.mal-nil)))\n        (u.throw*\n         (t.make-string result)))))\n\n(comment\n\n (read_str \"; hello\")\n\n (read_str \"nil\")\n\n (read_str \"true\")\n\n (read_str \"false\")\n\n (read_str \"1.2\")\n\n (read_str \"(+ 1 1)\")\n\n (read_str \"[:a :b :c]\")\n\n (read_str \"\\\"hello there\\\"\")\n\n (read_str \"\\\"hello\\\" there\\\"\")\n\n )\n\n{:read_str read_str}\n"
  },
  {
    "path": "impls/fennel/run",
    "content": "#!/usr/bin/env bash\n\nexec fennel $(dirname $0)/${STEP:-stepA_mal}.fnl \"${@}\"\n"
  },
  {
    "path": "impls/fennel/step0_repl.fnl",
    "content": "(fn READ [code-str]\n    code-str)\n\n(fn EVAL [ast]\n    ast)\n\n(fn PRINT [ast]\n    ast)\n\n(fn rep [code-str]\n    (PRINT (EVAL (READ code-str))))\n\n(var done false)\n\n(while (not done)\n  (io.write \"user> \")\n  (io.flush)\n  (let [input (io.read)]\n    (if (not input)\n        (set done true)\n        (print (rep input)))))\n"
  },
  {
    "path": "impls/fennel/step1_read_print.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn EVAL\n  [ast]\n  ast)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str))))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(var done false)\n\n(while (not done)\n  (io.write \"user> \")\n  (io.flush)\n  (let [input (io.read)]\n    (if (not input)\n        (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))\n"
  },
  {
    "path": "impls/fennel/step2_eval.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local u (require :utils))\n\n(local repl_env\n  {\"+\" (fn [ast-1 ast-2]\n         (t.make-number (+ (t.get-value ast-1)\n                           (t.get-value ast-2))))\n   \"-\" (fn [ast-1 ast-2]\n         (t.make-number (- (t.get-value ast-1)\n                          (t.get-value ast-2))))\n   \"*\" (fn [ast-1 ast-2]\n         (t.make-number (* (t.get-value ast-1)\n                           (t.get-value ast-2))))\n   \"/\" (fn [ast-1 ast-2]\n         (t.make-number (/ (t.get-value ast-1)\n                           (t.get-value ast-2))))})\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn EVAL\n  [ast env]\n  ;; (print (.. \"EVAL: \" (printer.pr_str ast true)))\n  (if (t.symbol?* ast)\n      (. env (t.get-value ast))\n      ;;\n      (t.vector?* ast)\n      (t.make-vector (u.map (fn [elt-ast]\n                              (EVAL elt-ast env))\n                            (t.get-value ast)))\n      ;;\n      (t.hash-map?* ast)\n      (t.make-hash-map (u.map (fn [elt-ast]\n                                (EVAL elt-ast env))\n                              (t.get-value ast)))\n      ;;\n          (or (not (t.list?* ast)) (t.empty?* ast))\n          ast\n          ;;\n          (let [eval-list (u.map (fn [x] (EVAL x env)) (t.get-value ast))\n                f (u.first eval-list)\n                args (u.slice eval-list 2 -1)]\n            (f (table.unpack args)))))\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(var done false)\n\n(while (not done)\n  (io.write \"user> \")\n  (io.flush)\n  (let [input (io.read)]\n    (if (not input)\n        (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))\n\n"
  },
  {
    "path": "impls/fennel/step3_env.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local u (require :utils))\n\n(local repl_env\n  (-> (e.make-env nil)\n      (e.env-set (t.make-symbol \"+\")\n                 (fn [ast-1 ast-2]\n                   (t.make-number (+ (t.get-value ast-1)\n                                     (t.get-value ast-2)))))\n      (e.env-set (t.make-symbol \"-\")\n                 (fn [ast-1 ast-2]\n                   (t.make-number (- (t.get-value ast-1)\n                                     (t.get-value ast-2)))))\n      (e.env-set (t.make-symbol \"*\")\n                 (fn [ast-1 ast-2]\n                   (t.make-number (* (t.get-value ast-1)\n                                     (t.get-value ast-2)))))\n      (e.env-set (t.make-symbol \"/\")\n                 (fn [ast-1 ast-2]\n                   (t.make-number (/ (t.get-value ast-1)\n                                     (t.get-value ast-2)))))))\n\n(fn READ\n  [arg]\n  (reader.read_str arg))\n\n(fn EVAL\n  [ast env]\n  (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n    (when (and dbgeval\n               (not (t.nil?* dbgeval))\n               (not (t.false?* dbgeval)))\n      (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n  (if (t.symbol?* ast)\n      (let [key (t.get-value ast)]\n        (or (e.env-get env key)\n            (u.throw* (t.make-string (.. \"'\" key \"' not found\")))))\n      ;;\n      (t.vector?* ast)\n      (t.make-vector (u.map (fn [elt-ast]\n                              (EVAL elt-ast env))\n                            (t.get-value ast)))\n      ;;\n      (t.hash-map?* ast)\n      (t.make-hash-map (u.map (fn [elt-ast]\n                                (EVAL elt-ast env))\n                              (t.get-value ast)))\n      ;;\n          (or (not (t.list?* ast)) (t.empty?* ast))\n          ast\n          ;;\n          (let [ast-elts (t.get-value ast)\n                head-name (t.get-value (. ast-elts 1))]\n            ;; XXX: want to check for symbol, but that screws up logic below\n            (if (= \"def!\" head-name)\n                (let [def-name (. ast-elts 2)\n                      def-val (EVAL (. ast-elts 3) env)]\n                  (e.env-set env\n                             def-name def-val)\n                  def-val)\n                ;;\n                (= \"let*\" head-name)\n                (let [new-env (e.make-env env)\n                      bindings (t.get-value (. ast-elts 2))\n                      stop (/ (length bindings) 2)]\n                  (for [idx 1 stop]\n                       (let [b-name (. bindings (- (* 2 idx) 1))\n                             b-val (EVAL (. bindings (* 2 idx)) new-env)]\n                         (e.env-set new-env\n                                    b-name b-val)))\n                  (EVAL (. ast-elts 3) new-env))\n                ;;\n                (let [eval-list (u.map (fn [x] (EVAL x env)) ast-elts)\n                      f (. eval-list 1)\n                      args (u.slice eval-list 2 -1)]\n                  (f (table.unpack args)))))))\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(var done false)\n\n(while (not done)\n  (io.write \"user> \")\n  (io.flush)\n  (let [input (io.read)]\n    (if (not input)\n        (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))\n"
  },
  {
    "path": "impls/fennel/step4_if_fn_do.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn EVAL\n  [ast env]\n  (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n    (when (and dbgeval\n               (not (t.nil?* dbgeval))\n               (not (t.false?* dbgeval)))\n      (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n  (if (t.symbol?* ast)\n      (let [key (t.get-value ast)]\n        (or (e.env-get env key)\n            (u.throw* (t.make-string (.. \"'\" key \"' not found\")))))\n      ;;\n      (t.vector?* ast)\n      (t.make-vector (u.map (fn [elt-ast]\n                              (EVAL elt-ast env))\n                            (t.get-value ast)))\n      ;;\n      (t.hash-map?* ast)\n      (t.make-hash-map (u.map (fn [elt-ast]\n                                (EVAL elt-ast env))\n                              (t.get-value ast)))\n      ;;\n          (or (not (t.list?* ast)) (t.empty?* ast))\n          ast\n          ;;\n          (let [ast-elts (t.get-value ast)\n                head-name (t.get-value (. ast-elts 1))]\n            ;; XXX: want to check for symbol, but that screws up logic below\n            (if (= \"def!\" head-name)\n                (let [def-name (. ast-elts 2)\n                      def-val (EVAL (. ast-elts 3) env)]\n                  (e.env-set env\n                             def-name def-val)\n                  def-val)\n                ;;\n                (= \"let*\" head-name)\n                (let [new-env (e.make-env env)\n                      bindings (t.get-value (. ast-elts 2))\n                      stop (/ (length bindings) 2)]\n                  (for [idx 1 stop]\n                       (let [b-name (. bindings (- (* 2 idx) 1))\n                             b-val (EVAL (. bindings (* 2 idx)) new-env)]\n                         (e.env-set new-env\n                                    b-name b-val)))\n                  (EVAL (. ast-elts 3) new-env))\n                ;;\n                (= \"do\" head-name)\n                (u.last (u.map (fn [x] (EVAL x env)) (u.slice ast-elts 2 -1)))\n                ;;\n                (= \"if\" head-name)\n                (let [cond-res (EVAL (. ast-elts 2) env)]\n                  (if (or (t.nil?* cond-res)\n                          (t.false?* cond-res))\n                      (let [else-ast (. ast-elts 4)]\n                        (if (not else-ast)\n                            t.mal-nil\n                            (EVAL else-ast env)))\n                      (EVAL (. ast-elts 3) env)))\n                ;;\n                (= \"fn*\" head-name)\n                (let [args (t.get-value (. ast-elts 2))\n                      body (. ast-elts 3)]\n                  (t.make-fn (fn [params]\n                               (EVAL body\n                                     (e.make-env env args params)))))\n                ;;\n                (let [eval-list (u.map (fn [x] (EVAL x env)) ast-elts)\n                      f (. eval-list 1)\n                      args (u.slice eval-list 2 -1)]\n                  ((t.get-value f) args))))))\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(var done false)\n\n(while (not done)\n  (io.write \"user> \")\n  (io.flush)\n  (let [input (io.read)]\n    (if (not input)\n        (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))\n                ;; (fn [exc]\n                ;;   (if (t.nil?* exc)\n                ;;       (print)\n                ;;       (= \"string\" (type exc))\n                ;;       (print exc)\n                ;;       (print (PRINT exc))))))))\n"
  },
  {
    "path": "impls/fennel/step5_tco.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn EVAL\n    [ast-param env-param]\n    (var ast ast-param)\n    (var env env-param)\n    (var result nil)\n    (while (not result)\n      (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n        (when (and dbgeval\n                   (not (t.nil?* dbgeval))\n                   (not (t.false?* dbgeval)))\n          (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n      (if (t.symbol?* ast)\n          (let [key (t.get-value ast)]\n            (set result (or (e.env-get env key)\n                        (u.throw* (t.make-string (.. \"'\" key\n                                                   \"' not found\"))))))\n          ;;\n          (t.vector?* ast)\n          (set result (t.make-vector (u.map (fn [x] (EVAL x env))\n                                            (t.get-value ast))))\n          ;;\n          (t.hash-map?* ast)\n          (set result (t.make-hash-map (u.map (fn [x] (EVAL x env))\n                                              (t.get-value ast))))\n          ;;\n          (or (not (t.list?* ast)) (t.empty?* ast))\n          (set result ast)\n          ;;\n          (let [ast-elts (t.get-value ast)\n                head-name (t.get-value (. ast-elts 1))]\n            ;; XXX: want to check for symbol, but that screws up logic below\n            (if (= \"def!\" head-name)\n                (let [def-name (. ast-elts 2)\n                      def-val (EVAL (. ast-elts 3) env)]\n                  (e.env-set env\n                             def-name def-val)\n                  (set result def-val))\n                ;;\n                (= \"let*\" head-name)\n                (let [new-env (e.make-env env)\n                      bindings (t.get-value (. ast-elts 2))\n                      stop (/ (length bindings) 2)]\n                  (for [idx 1 stop]\n                       (let [b-name (. bindings (- (* 2 idx) 1))\n                             b-val (EVAL (. bindings (* 2 idx)) new-env)]\n                         (e.env-set new-env\n                                    b-name b-val)))\n                  ;; tco\n                  (set ast (. ast-elts 3))\n                  (set env new-env))\n                ;;\n                (= \"do\" head-name)\n                (let [most-forms (u.slice ast-elts 2 -2) ;; XXX\n                      last-body-form (u.last ast-elts)\n                      res-ast (u.map (fn [x] (EVAL x env)) most-forms)]\n                  ;; tco\n                  (set ast last-body-form))\n                ;;\n                (= \"if\" head-name)\n                (let [cond-res (EVAL (. ast-elts 2) env)]\n                  (if (or (t.nil?* cond-res)\n                          (t.false?* cond-res))\n                      (let [else-ast (. ast-elts 4)]\n                        (if (not else-ast)\n                            ;; tco\n                            (set result t.mal-nil)\n                            (set ast else-ast)))\n                      ;; tco\n                      (set ast (. ast-elts 3))))\n                ;;\n                (= \"fn*\" head-name)\n                (let [params (t.get-value (. ast-elts 2))\n                      body (. ast-elts 3)]\n                  ;; tco\n                  (set result\n                       (t.make-fn (fn [args]\n                                    (EVAL body\n                                          (e.make-env env params args)))\n                                  body params env)))\n                ;;\n                (let [f (EVAL (. ast-elts 1) env)\n                      args (u.map (fn [x] (EVAL x env)) (u.slice ast-elts 2 -1))\n                      body (t.get-ast f)] ;; tco\n                    (if body\n                        (do\n                          (set ast body)\n                          (set env (e.make-env (t.get-env f)\n                                               (t.get-params f) args)))\n                        (set result\n                             ((t.get-value f) args))))))))\n    result)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(var done false)\n\n(while (not done)\n  (io.write \"user> \")\n  (io.flush)\n  (let [input (io.read)]\n    (if (not input)\n        (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))\n"
  },
  {
    "path": "impls/fennel/step6_file.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn EVAL\n    [ast-param env-param]\n    (var ast ast-param)\n    (var env env-param)\n    (var result nil)\n    (while (not result)\n      (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n        (when (and dbgeval\n                   (not (t.nil?* dbgeval))\n                   (not (t.false?* dbgeval)))\n          (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n      (if (t.symbol?* ast)\n          (let [key (t.get-value ast)]\n            (set result (or (e.env-get env key)\n                        (u.throw* (t.make-string (.. \"'\" key\n                                                   \"' not found\"))))))\n          ;;\n          (t.vector?* ast)\n          (set result (t.make-vector (u.map (fn [x] (EVAL x env))\n                                            (t.get-value ast))))\n          ;;\n          (t.hash-map?* ast)\n          (set result (t.make-hash-map (u.map (fn [x] (EVAL x env))\n                                              (t.get-value ast))))\n          ;;\n          (or (not (t.list?* ast)) (t.empty?* ast))\n          (set result ast)\n          ;;\n          (let [ast-elts (t.get-value ast)\n                head-name (t.get-value (. ast-elts 1))]\n            ;; XXX: want to check for symbol, but that screws up logic below\n            (if (= \"def!\" head-name)\n                (let [def-name (. ast-elts 2)\n                      def-val (EVAL (. ast-elts 3) env)]\n                  (e.env-set env\n                             def-name def-val)\n                  (set result def-val))\n                ;;\n                (= \"let*\" head-name)\n                (let [new-env (e.make-env env)\n                      bindings (t.get-value (. ast-elts 2))\n                      stop (/ (length bindings) 2)]\n                  (for [idx 1 stop]\n                       (let [b-name (. bindings (- (* 2 idx) 1))\n                             b-val (EVAL (. bindings (* 2 idx)) new-env)]\n                         (e.env-set new-env\n                                    b-name b-val)))\n                  ;; tco\n                  (set ast (. ast-elts 3))\n                  (set env new-env))\n                ;;\n                (= \"do\" head-name)\n                (let [most-forms (u.slice ast-elts 2 -2) ;; XXX\n                      last-body-form (u.last ast-elts)\n                      res-ast (u.map (fn [x] (EVAL x env)) most-forms)]\n                  ;; tco\n                  (set ast last-body-form))\n                ;;\n                (= \"if\" head-name)\n                (let [cond-res (EVAL (. ast-elts 2) env)]\n                  (if (or (t.nil?* cond-res)\n                          (t.false?* cond-res))\n                      (let [else-ast (. ast-elts 4)]\n                        (if (not else-ast)\n                            ;; tco\n                            (set result t.mal-nil)\n                            (set ast else-ast)))\n                      ;; tco\n                      (set ast (. ast-elts 3))))\n                ;;\n                (= \"fn*\" head-name)\n                (let [params (t.get-value (. ast-elts 2))\n                      body (. ast-elts 3)]\n                  ;; tco\n                  (set result\n                       (t.make-fn (fn [args]\n                                    (EVAL body\n                                          (e.make-env env params args)))\n                                  body params env)))\n                ;;\n                (let [f (EVAL (. ast-elts 1) env)\n                      args (u.map (fn [x] (EVAL x env)) (u.slice ast-elts 2 -1))\n                      body (t.get-ast f)] ;; tco\n                    (if body\n                        (do\n                          (set ast body)\n                          (set env (e.make-env (t.get-env f)\n                                               (t.get-params f) args)))\n                        (set result\n                             ((t.get-value f) args))))))))\n    result)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"eval\")\n           (t.make-fn\n             (fn [asts]\n               (when (< (length asts) 1)\n                 ;; XXX\n                 (error \"eval takes 1 arguments\"))\n               (EVAL (u.first asts) repl_env))))\n\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"*ARGV*\")\n           (t.make-list (u.map t.make-string (u.slice arg 2))))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(if (<= 1 (length arg))\n    (xpcall (fn []\n              (rep (.. \"(load-file \\\"\" (. arg 1) \"\\\")\"))) ;; XXX: escaping?\n            handle-error)\n    (do\n     (var done false)\n     (while (not done)\n       (io.write \"user> \")\n       (io.flush)\n       (let [input (io.read)]\n         (if (not input)\n             (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))))\n;                (fn [exc]\n                  ;; (if (t.nil?* exc)\n                  ;;     (print)\n                  ;;     (= \"string\" (type exc))\n                  ;;     (print exc)\n                  ;;     (print (PRINT exc))))))))))\n"
  },
  {
    "path": "impls/fennel/step7_quote.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn starts-with\n  [ast name]\n  (when (and (t.list?* ast)\n             (not (t.empty?* ast)))\n    (let [head-ast (. (t.get-value ast) 1)]\n      (and (t.symbol?* head-ast)\n           (= name (t.get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(fn qq-iter\n  [ast]\n  (if (t.empty?* ast)\n      (t.make-list [])\n      (let [ast-value (t.get-value ast)\n            elt (. ast-value 1)\n            acc (qq-iter (t.make-list (u.slice ast-value 2 -1)))]\n        (if (starts-with elt \"splice-unquote\")\n            (t.make-list [(t.make-symbol \"concat\")\n                          (. (t.get-value elt) 2)\n                          acc])\n            (t.make-list [(t.make-symbol \"cons\")\n                          (quasiquote* elt)\n                          acc])))))\n\n(set quasiquote*\n  (fn [ast]\n    (if (starts-with ast \"unquote\")\n        (. (t.get-value ast) 2)\n        ;;\n        (t.list?* ast)\n        (qq-iter ast)\n        ;;\n        (t.vector?* ast)\n        (t.make-list [(t.make-symbol \"vec\") (qq-iter ast)])\n        ;;\n        (or (t.symbol?* ast)\n            (t.hash-map?* ast))\n        (t.make-list [(t.make-symbol \"quote\") ast])\n        ;;\n        ast)))\n\n(fn EVAL\n    [ast-param env-param]\n    (var ast ast-param)\n    (var env env-param)\n    (var result nil)\n    (while (not result)\n      (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n        (when (and dbgeval\n                   (not (t.nil?* dbgeval))\n                   (not (t.false?* dbgeval)))\n          (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n      (if (t.symbol?* ast)\n          (let [key (t.get-value ast)]\n            (set result (or (e.env-get env key)\n                        (u.throw* (t.make-string (.. \"'\" key\n                                                   \"' not found\"))))))\n          ;;\n          (t.vector?* ast)\n          (set result (t.make-vector (u.map (fn [x] (EVAL x env))\n                                            (t.get-value ast))))\n          ;;\n          (t.hash-map?* ast)\n          (set result (t.make-hash-map (u.map (fn [x] (EVAL x env))\n                                              (t.get-value ast))))\n          ;;\n          (or (not (t.list?* ast)) (t.empty?* ast))\n          (set result ast)\n          ;;\n          (let [ast-elts (t.get-value ast)\n                head-name (t.get-value (. ast-elts 1))]\n            ;; XXX: want to check for symbol, but that screws up logic below\n            (if (= \"def!\" head-name)\n                (let [def-name (. ast-elts 2)\n                      def-val (EVAL (. ast-elts 3) env)]\n                  (e.env-set env\n                             def-name def-val)\n                  (set result def-val))\n                ;;\n                (= \"let*\" head-name)\n                (let [new-env (e.make-env env)\n                      bindings (t.get-value (. ast-elts 2))\n                      stop (/ (length bindings) 2)]\n                  (for [idx 1 stop]\n                       (let [b-name (. bindings (- (* 2 idx) 1))\n                             b-val (EVAL (. bindings (* 2 idx)) new-env)]\n                         (e.env-set new-env\n                                    b-name b-val)))\n                  ;; tco\n                  (set ast (. ast-elts 3))\n                  (set env new-env))\n                ;;\n                (= \"quote\" head-name)\n                ;; tco\n                (set result (. ast-elts 2))\n                ;;\n                (= \"quasiquote\" head-name)\n                ;; tco\n                (set ast (quasiquote* (. ast-elts 2)))\n                ;;\n                (= \"do\" head-name)\n                (let [most-forms (u.slice ast-elts 2 -2) ;; XXX\n                      last-body-form (u.last ast-elts)\n                      res-ast (u.map (fn [x] (EVAL x env)) most-forms)]\n                  ;; tco\n                  (set ast last-body-form))\n                ;;\n                (= \"if\" head-name)\n                (let [cond-res (EVAL (. ast-elts 2) env)]\n                  (if (or (t.nil?* cond-res)\n                          (t.false?* cond-res))\n                      (let [else-ast (. ast-elts 4)]\n                        (if (not else-ast)\n                            ;; tco\n                            (set result t.mal-nil)\n                            (set ast else-ast)))\n                      ;; tco\n                      (set ast (. ast-elts 3))))\n                ;;\n                (= \"fn*\" head-name)\n                (let [params (t.get-value (. ast-elts 2))\n                      body (. ast-elts 3)]\n                  ;; tco\n                  (set result\n                       (t.make-fn (fn [args]\n                                    (EVAL body\n                                          (e.make-env env params args)))\n                                  body params env)))\n                ;;\n                (let [f (EVAL (. ast-elts 1) env)\n                      args (u.map (fn [x] (EVAL x env)) (u.slice ast-elts 2 -1))\n                      body (t.get-ast f)] ;; tco\n                    (if body\n                        (do\n                          (set ast body)\n                          (set env (e.make-env (t.get-env f)\n                                               (t.get-params f) args)))\n                        (set result\n                             ((t.get-value f) args))))))))\n    result)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"eval\")\n           (t.make-fn\n             (fn [asts]\n               (when (< (length asts) 1)\n                 ;; XXX\n                 (error \"eval takes 1 arguments\"))\n               (EVAL (u.first asts) repl_env))))\n\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"*ARGV*\")\n           (t.make-list (u.map t.make-string (u.slice arg 2 -1))))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(if (<= 1 (length arg))\n    (xpcall (fn []\n              (rep (.. \"(load-file \\\"\" (. arg 1) \"\\\")\"))) ;; XXX: escaping?\n            handle-error)\n    (do\n     (var done false)\n     (while (not done)\n       (io.write \"user> \")\n       (io.flush)\n       (let [input (io.read)]\n         (if (not input)\n             (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))))\n"
  },
  {
    "path": "impls/fennel/step8_macros.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn starts-with\n  [ast name]\n  (when (and (t.list?* ast)\n             (not (t.empty?* ast)))\n    (let [head-ast (. (t.get-value ast) 1)]\n      (and (t.symbol?* head-ast)\n           (= name (t.get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(fn qq-iter\n  [ast]\n  (if (t.empty?* ast)\n      (t.make-list [])\n      (let [ast-value (t.get-value ast)\n            elt (. ast-value 1)\n            acc (qq-iter (t.make-list (u.slice ast-value 2 -1)))]\n        (if (starts-with elt \"splice-unquote\")\n            (t.make-list [(t.make-symbol \"concat\")\n                          (. (t.get-value elt) 2)\n                          acc])\n            (t.make-list [(t.make-symbol \"cons\")\n                          (quasiquote* elt)\n                          acc])))))\n\n(set quasiquote*\n  (fn [ast]\n    (if (starts-with ast \"unquote\")\n        (. (t.get-value ast) 2)\n        ;;\n        (t.list?* ast)\n        (qq-iter ast)\n        ;;\n        (t.vector?* ast)\n        (t.make-list [(t.make-symbol \"vec\") (qq-iter ast)])\n        ;;\n        (or (t.symbol?* ast)\n            (t.hash-map?* ast))\n        (t.make-list [(t.make-symbol \"quote\") ast])\n        ;;\n        ast)))\n\n(fn EVAL\n    [ast-param env-param]\n    (var ast ast-param)\n    (var env env-param)\n    (var result nil)\n    (while (not result)\n               (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n                 (when (and dbgeval\n                            (not (t.nil?* dbgeval))\n                            (not (t.false?* dbgeval)))\n                   (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n               (if (t.symbol?* ast)\n                   (let [key (t.get-value ast)]\n                     (set result (or (e.env-get env key)\n                                 (u.throw* (t.make-string (.. \"'\" key\n                                                            \"' not found\"))))))\n                   ;;\n                   (t.vector?* ast)\n                   (set result (t.make-vector (u.map (fn [x] (EVAL x env))\n                                                     (t.get-value ast))))\n                   ;;\n                   (t.hash-map?* ast)\n                   (set result (t.make-hash-map (u.map (fn [x] (EVAL x env))\n                                                       (t.get-value ast))))\n                   ;;\n                   (or (not (t.list?* ast)) (t.empty?* ast))\n                   (set result ast)\n                   ;;\n                   (let [ast-elts (t.get-value ast)\n                         head-name (t.get-value (. ast-elts 1))]\n                     ;; XXX: want to check for symbol, but...\n                     (if (= \"def!\" head-name)\n                         (let [def-name (. ast-elts 2)\n                               def-val (EVAL (. ast-elts 3) env)]\n                           (e.env-set env\n                                      def-name def-val)\n                           (set result def-val))\n                         ;;\n                         (= \"defmacro!\" head-name)\n                         (let [def-name (. ast-elts 2)\n                               def-val (EVAL (. ast-elts 3) env)\n                               macro-ast (t.macrofy def-val)]\n                           (e.env-set env\n                                      def-name macro-ast)\n                           (set result macro-ast))\n                         ;;\n                         (= \"let*\" head-name)\n                         (let [new-env (e.make-env env)\n                               bindings (t.get-value (. ast-elts 2))\n                               stop (/ (length bindings) 2)]\n                           (for [idx 1 stop]\n                                (let [b-name\n                                      (. bindings (- (* 2 idx) 1))\n                                      b-val\n                                      (EVAL (. bindings (* 2 idx)) new-env)]\n                                  (e.env-set new-env\n                                             b-name b-val)))\n                           ;; tco\n                           (set ast (. ast-elts 3))\n                           (set env new-env))\n                         ;;\n                         (= \"quote\" head-name)\n                         ;; tco\n                         (set result (. ast-elts 2))\n                         ;;\n                         (= \"quasiquote\" head-name)\n                         ;; tco\n                         (set ast (quasiquote* (. ast-elts 2)))\n                         ;;\n                         (= \"do\" head-name)\n                         (let [most-forms (u.slice ast-elts 2 -2) ;; XXX\n                               last-body-form (u.last ast-elts)\n                               res-ast (u.map (fn [x] (EVAL x env)) most-forms)]\n                           ;; tco\n                           (set ast last-body-form))\n                         ;;\n                         (= \"if\" head-name)\n                         (let [cond-res (EVAL (. ast-elts 2) env)]\n                           (if (or (t.nil?* cond-res)\n                                   (t.false?* cond-res))\n                               (let [else-ast (. ast-elts 4)]\n                                 (if (not else-ast)\n                                     ;; tco\n                                     (set result t.mal-nil)\n                                     (set ast else-ast)))\n                               ;; tco\n                               (set ast (. ast-elts 3))))\n                         ;;\n                         (= \"fn*\" head-name)\n                         (let [params (t.get-value (. ast-elts 2))\n                               body (. ast-elts 3)]\n                           ;; tco\n                           (set result\n                                (t.make-fn\n                                 (fn [args]\n                                   (EVAL body\n                                         (e.make-env env params args)))\n                                 body params env false)))\n                         ;;\n                         (let [f (EVAL (. ast-elts 1) env)\n                               ast-rest (u.slice ast-elts 2 -1)]\n                          (if (t.macro?* f)\n                            (set ast ((t.get-value f) ast-rest))\n                            (let [args (u.map (fn [x] (EVAL x env)) ast-rest)\n                                  body (t.get-ast f)] ;; tco\n                             (if body\n                                 (do\n                                  (set ast body)\n                                  (set env\n                                       (e.make-env (t.get-env f)\n                                                   (t.get-params f)\n                                                   args)))\n                                 (set result\n                                      ((t.get-value f) args))))))))))\n    result)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"eval\")\n           (t.make-fn\n             (fn [asts]\n               (when (< (length asts) 1)\n                 ;; XXX\n                 (error \"eval takes 1 arguments\"))\n               (EVAL (u.first asts) repl_env))))\n\n(rep\n (.. \"(def! load-file \"\n     \"  (fn* (f) \"\n     \"    (eval \"\n     \"      (read-string \"\n     \"        (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"))\n\n(rep\n (.. \"(defmacro! cond \"\n     \"  (fn* (& xs) \"\n     \"    (if (> (count xs) 0) \"\n     \"        (list 'if (first xs) \"\n     \"                  (if (> (count xs) 1) \"\n     \"                      (nth xs 1) \"\n     \"                      (throw \\\"odd number of forms to cond\\\")) \"\n     \"                  (cons 'cond (rest (rest xs)))))))\"))\n\n(e.env-set repl_env\n           (t.make-symbol \"*ARGV*\")\n           (t.make-list (u.map t.make-string (u.slice arg 2 -1))))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(if (<= 1 (length arg))\n    (xpcall (fn []\n              (rep (.. \"(load-file \\\"\" (. arg 1) \"\\\")\"))) ;; XXX: escaping?\n            handle-error)\n    (do\n     (var done false)\n     (while (not done)\n       (io.write \"user> \")\n       (io.flush)\n       (let [input (io.read)]\n         (if (not input)\n             (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))))\n"
  },
  {
    "path": "impls/fennel/step9_try.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn starts-with\n  [ast name]\n  (when (and (t.list?* ast)\n             (not (t.empty?* ast)))\n    (let [head-ast (. (t.get-value ast) 1)]\n      (and (t.symbol?* head-ast)\n           (= name (t.get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(fn qq-iter\n  [ast]\n  (if (t.empty?* ast)\n      (t.make-list [])\n      (let [ast-value (t.get-value ast)\n            elt (. ast-value 1)\n            acc (qq-iter (t.make-list (u.slice ast-value 2 -1)))]\n        (if (starts-with elt \"splice-unquote\")\n            (t.make-list [(t.make-symbol \"concat\")\n                          (. (t.get-value elt) 2)\n                          acc])\n            (t.make-list [(t.make-symbol \"cons\")\n                          (quasiquote* elt)\n                          acc])))))\n\n(set quasiquote*\n  (fn [ast]\n    (if (starts-with ast \"unquote\")\n        (. (t.get-value ast) 2)\n        ;;\n        (t.list?* ast)\n        (qq-iter ast)\n        ;;\n        (t.vector?* ast)\n        (t.make-list [(t.make-symbol \"vec\") (qq-iter ast)])\n        ;;\n        (or (t.symbol?* ast)\n            (t.hash-map?* ast))\n        (t.make-list [(t.make-symbol \"quote\") ast])\n        ;;\n        ast)))\n\n(fn EVAL\n    [ast-param env-param]\n    (var ast ast-param)\n    (var env env-param)\n    (var result nil)\n    (while (not result)\n               (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n                 (when (and dbgeval\n                            (not (t.nil?* dbgeval))\n                            (not (t.false?* dbgeval)))\n                   (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n               (if (t.symbol?* ast)\n                   (let [key (t.get-value ast)]\n                     (set result (or (e.env-get env key)\n                                 (u.throw* (t.make-string (.. \"'\" key\n                                                            \"' not found\"))))))\n                   ;;\n                   (t.vector?* ast)\n                   (set result (t.make-vector (u.map (fn [x] (EVAL x env))\n                                                     (t.get-value ast))))\n                   ;;\n                   (t.hash-map?* ast)\n                   (set result (t.make-hash-map (u.map (fn [x] (EVAL x env))\n                                                       (t.get-value ast))))\n                   ;;\n                   (or (not (t.list?* ast)) (t.empty?* ast))\n                   (set result ast)\n                   ;;\n                   (let [ast-elts (t.get-value ast)\n                         head-name (t.get-value (. ast-elts 1))]\n                     ;; XXX: want to check for symbol, but...\n                     (if (= \"def!\" head-name)\n                         (let [def-name (. ast-elts 2)\n                               def-val (EVAL (. ast-elts 3) env)]\n                           (e.env-set env\n                                      def-name def-val)\n                           (set result def-val))\n                         ;;\n                         (= \"defmacro!\" head-name)\n                         (let [def-name (. ast-elts 2)\n                               def-val (EVAL (. ast-elts 3) env)\n                               macro-ast (t.macrofy def-val)]\n                           (e.env-set env\n                                      def-name macro-ast)\n                           (set result macro-ast))\n                         ;;\n                         (= \"let*\" head-name)\n                         (let [new-env (e.make-env env)\n                               bindings (t.get-value (. ast-elts 2))\n                               stop (/ (length bindings) 2)]\n                           (for [idx 1 stop]\n                                (let [b-name\n                                      (. bindings (- (* 2 idx) 1))\n                                      b-val\n                                      (EVAL (. bindings (* 2 idx)) new-env)]\n                                  (e.env-set new-env\n                                             b-name b-val)))\n                           ;; tco\n                           (set ast (. ast-elts 3))\n                           (set env new-env))\n                         ;;\n                         (= \"quote\" head-name)\n                         ;; tco\n                         (set result (. ast-elts 2))\n                         ;;\n                         (= \"quasiquote\" head-name)\n                         ;; tco\n                         (set ast (quasiquote* (. ast-elts 2)))\n                         ;;\n                         (= \"try*\" head-name)\n                         (set result\n                              (let [(ok? res)\n                                    (pcall EVAL (. ast-elts 2) env)]\n                                (if (not ok?)\n                                    (let [maybe-catch-ast (. ast-elts 3)]\n                                      (if (not maybe-catch-ast)\n                                          (u.throw* res)\n                                          (if (not (starts-with maybe-catch-ast\n                                                                \"catch*\"))\n                                              (u.throw*\n                                               (t.make-string\n                                                \"Expected catch* form\"))\n                                              (let [catch-asts\n                                                    (t.get-value\n                                                     maybe-catch-ast)]\n                                                (if (< (length catch-asts) 2)\n                                                    (u.throw*\n                                                     (t.make-string\n                                                      (.. \"catch* requires at \"\n                                                          \"least 2 \"\n                                                          \"arguments\")))\n                                                    (let [catch-sym-ast\n                                                          (. catch-asts 2)\n                                                          catch-body-ast\n                                                          (. catch-asts 3)]\n                                                      (EVAL catch-body-ast\n                                                            (e.make-env\n                                                             env\n                                                             [catch-sym-ast]\n                                                             [res]))))))))\n                                    res)))\n                         ;;\n                         (= \"do\" head-name)\n                         (let [most-forms (u.slice ast-elts 2 -2) ;; XXX\n                               last-body-form (u.last ast-elts)\n                               res-ast (u.map (fn [x] (EVAL x env)) most-forms)]\n                           ;; tco\n                           (set ast last-body-form))\n                         ;;\n                         (= \"if\" head-name)\n                         (let [cond-res (EVAL (. ast-elts 2) env)]\n                           (if (or (t.nil?* cond-res)\n                                   (t.false?* cond-res))\n                               (let [else-ast (. ast-elts 4)]\n                                 (if (not else-ast)\n                                     ;; tco\n                                     (set result t.mal-nil)\n                                     (set ast else-ast)))\n                               ;; tco\n                               (set ast (. ast-elts 3))))\n                         ;;\n                         (= \"fn*\" head-name)\n                         (let [params (t.get-value (. ast-elts 2))\n                               body (. ast-elts 3)]\n                           ;; tco\n                           (set result\n                                (t.make-fn\n                                 (fn [args]\n                                   (EVAL body\n                                         (e.make-env env params args)))\n                                 body params env false)))\n                         ;;\n                         (let [f (EVAL (. ast-elts 1) env)\n                               ast-rest (u.slice ast-elts 2 -1)]\n                          (if (t.macro?* f)\n                            (set ast ((t.get-value f) ast-rest))\n                            (let [args (u.map (fn [x] (EVAL x env)) ast-rest)\n                                  body (t.get-ast f)] ;; tco\n                             (if body\n                                 (do\n                                  (set ast body)\n                                  (set env\n                                       (e.make-env (t.get-env f)\n                                                   (t.get-params f)\n                                                   args)))\n                                 (set result\n                                      ((t.get-value f) args))))))))))\n    result)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"eval\")\n           (t.make-fn\n             (fn [asts]\n               (when (< (length asts) 1)\n                 (u.throw*\n                  (t.make-string \"eval takes 1 argument\")))\n               (EVAL (u.first asts) repl_env))))\n\n(rep\n (.. \"(def! load-file \"\n     \"  (fn* (f) \"\n     \"    (eval \"\n     \"      (read-string \"\n     \"        (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"))\n\n(rep\n (.. \"(defmacro! cond \"\n     \"  (fn* (& xs) \"\n     \"    (if (> (count xs) 0) \"\n     \"        (list 'if (first xs) \"\n     \"                  (if (> (count xs) 1) \"\n     \"                      (nth xs 1) \"\n     \"                      (throw \\\"odd number of forms to cond\\\")) \"\n     \"                  (cons 'cond (rest (rest xs)))))))\"))\n\n(e.env-set repl_env\n           (t.make-symbol \"*ARGV*\")\n           (t.make-list (u.map t.make-string (u.slice arg 2 -1))))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(if (<= 1 (length arg))\n    (xpcall (fn []\n              (rep (.. \"(load-file \\\"\" (. arg 1) \"\\\")\"))) ;; XXX: escaping?\n            handle-error)\n    (do\n     (var done false)\n     (while (not done)\n       (io.write \"user> \")\n       (io.flush)\n       (let [input (io.read)]\n         (if (not input)\n             (set done true)\n        (xpcall (fn []\n                  (print (rep input)))\n                handle-error))))))\n"
  },
  {
    "path": "impls/fennel/stepA_mal.fnl",
    "content": "(local printer (require :printer))\n(local reader (require :reader))\n(local t (require :types))\n(local e (require :env))\n(local core (require :core))\n(local u (require :utils))\n\n(local repl_env\n  (let [env (e.make-env)]\n    (each [name func (pairs core)]\n      (e.env-set env\n                 (t.make-symbol name)\n                 func))\n    env))\n\n(fn READ\n  [code-str]\n  (reader.read_str code-str))\n\n(fn starts-with\n  [ast name]\n  (when (and (t.list?* ast)\n             (not (t.empty?* ast)))\n    (let [head-ast (. (t.get-value ast) 1)]\n      (and (t.symbol?* head-ast)\n           (= name (t.get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(fn qq-iter\n  [ast]\n  (if (t.empty?* ast)\n      (t.make-list [])\n      (let [ast-value (t.get-value ast)\n            elt (. ast-value 1)\n            acc (qq-iter (t.make-list (u.slice ast-value 2 -1)))]\n        (if (starts-with elt \"splice-unquote\")\n            (t.make-list [(t.make-symbol \"concat\")\n                          (. (t.get-value elt) 2)\n                          acc])\n            (t.make-list [(t.make-symbol \"cons\")\n                          (quasiquote* elt)\n                          acc])))))\n\n(set quasiquote*\n  (fn [ast]\n    (if (starts-with ast \"unquote\")\n        (. (t.get-value ast) 2)\n        ;;\n        (t.list?* ast)\n        (qq-iter ast)\n        ;;\n        (t.vector?* ast)\n        (t.make-list [(t.make-symbol \"vec\") (qq-iter ast)])\n        ;;\n        (or (t.symbol?* ast)\n            (t.hash-map?* ast))\n        (t.make-list [(t.make-symbol \"quote\") ast])\n        ;;\n        ast)))\n\n(fn EVAL\n    [ast-param env-param]\n    (var ast ast-param)\n    (var env env-param)\n    (var result nil)\n    (while (not result)\n               (let [dbgeval (e.env-get env \"DEBUG-EVAL\")]\n                 (when (and dbgeval\n                            (not (t.nil?* dbgeval))\n                            (not (t.false?* dbgeval)))\n                   (print (.. \"EVAL: \" (printer.pr_str ast true)))))\n               (if (t.symbol?* ast)\n                   (let [key (t.get-value ast)]\n                     (set result (or (e.env-get env key)\n                                 (u.throw* (t.make-string (.. \"'\" key\n                                                            \"' not found\"))))))\n                   ;;\n                   (t.vector?* ast)\n                   (set result (t.make-vector (u.map (fn [x] (EVAL x env))\n                                                     (t.get-value ast))))\n                   ;;\n                   (t.hash-map?* ast)\n                   (set result (t.make-hash-map (u.map (fn [x] (EVAL x env))\n                                                       (t.get-value ast))))\n                   ;;\n                   (or (not (t.list?* ast)) (t.empty?* ast))\n                   (set result ast)\n                   ;;\n                   (let [ast-elts (t.get-value ast)\n                         head-name (t.get-value (. ast-elts 1))]\n                     ;; XXX: want to check for symbol, but...\n                     (if (= \"def!\" head-name)\n                         (let [def-name (. ast-elts 2)\n                               def-val (EVAL (. ast-elts 3) env)]\n                           (e.env-set env\n                                      def-name def-val)\n                           (set result def-val))\n                         ;;\n                         (= \"defmacro!\" head-name)\n                         (let [def-name (. ast-elts 2)\n                               def-val (EVAL (. ast-elts 3) env)\n                               macro-ast (t.macrofy def-val)]\n                           (e.env-set env\n                                      def-name macro-ast)\n                           (set result macro-ast))\n                         ;;\n                         (= \"let*\" head-name)\n                         (let [new-env (e.make-env env)\n                               bindings (t.get-value (. ast-elts 2))\n                               stop (/ (length bindings) 2)]\n                           (for [idx 1 stop]\n                                (let [b-name\n                                      (. bindings (- (* 2 idx) 1))\n                                      b-val\n                                      (EVAL (. bindings (* 2 idx)) new-env)]\n                                  (e.env-set new-env\n                                             b-name b-val)))\n                           ;; tco\n                           (set ast (. ast-elts 3))\n                           (set env new-env))\n                         ;;\n                         (= \"quote\" head-name)\n                         ;; tco\n                         (set result (. ast-elts 2))\n                         ;;\n                         (= \"quasiquote\" head-name)\n                         ;; tco\n                         (set ast (quasiquote* (. ast-elts 2)))\n                         ;;\n                         (= \"try*\" head-name)\n                         (set result\n                              (let [(ok? res)\n                                    (pcall EVAL (. ast-elts 2) env)]\n                                (if (not ok?)\n                                    (let [maybe-catch-ast (. ast-elts 3)]\n                                      (if (not maybe-catch-ast)\n                                          (u.throw* res)\n                                          (if (not (starts-with maybe-catch-ast\n                                                                \"catch*\"))\n                                              (u.throw*\n                                               (t.make-string\n                                                \"Expected catch* form\"))\n                                              (let [catch-asts\n                                                    (t.get-value\n                                                     maybe-catch-ast)]\n                                                (if (< (length catch-asts) 2)\n                                                    (u.throw*\n                                                     (t.make-string\n                                                      (.. \"catch* requires at \"\n                                                          \"least 2 \"\n                                                          \"arguments\")))\n                                                    (let [catch-sym-ast\n                                                          (. catch-asts 2)\n                                                          catch-body-ast\n                                                          (. catch-asts 3)]\n                                                      (EVAL catch-body-ast\n                                                            (e.make-env\n                                                             env\n                                                             [catch-sym-ast]\n                                                             [res]))))))))\n                                    res)))\n                         ;;\n                         (= \"do\" head-name)\n                         (let [most-forms (u.slice ast-elts 2 -2) ;; XXX\n                               last-body-form (u.last ast-elts)\n                               res-ast (u.map (fn [x] (EVAL x env)) most-forms)]\n                           ;; tco\n                           (set ast last-body-form))\n                         ;;\n                         (= \"if\" head-name)\n                         (let [cond-res (EVAL (. ast-elts 2) env)]\n                           (if (or (t.nil?* cond-res)\n                                   (t.false?* cond-res))\n                               (let [else-ast (. ast-elts 4)]\n                                 (if (not else-ast)\n                                     ;; tco\n                                     (set result t.mal-nil)\n                                     (set ast else-ast)))\n                               ;; tco\n                               (set ast (. ast-elts 3))))\n                         ;;\n                         (= \"fn*\" head-name)\n                         (let [params (t.get-value (. ast-elts 2))\n                               body (. ast-elts 3)]\n                           ;; tco\n                           (set result\n                                (t.make-fn\n                                 (fn [args]\n                                   (EVAL body\n                                         (e.make-env env params args)))\n                                 body params env false nil)))\n                         ;;\n                         (let [f (EVAL (. ast-elts 1) env)\n                               ast-rest (u.slice ast-elts 2 -1)]\n                          (if (t.macro?* f)\n                            (set ast ((t.get-value f) ast-rest))\n                            (let [args (u.map (fn [x] (EVAL x env)) ast-rest)\n                                  body (t.get-ast f)] ;; tco\n                             (if body\n                                 (do\n                                  (set ast body)\n                                  (set env\n                                       (e.make-env (t.get-env f)\n                                                   (t.get-params f)\n                                                   args)))\n                                 (set result\n                                      ((t.get-value f) args))))))))))\n    result)\n\n(fn PRINT\n  [ast]\n  (printer.pr_str ast true))\n\n(fn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e.env-set repl_env\n           (t.make-symbol \"eval\")\n           (t.make-fn\n             (fn [asts]\n               (when (< (length asts) 1)\n                 (u.throw*\n                  (t.make-string \"eval takes 1 argument\")))\n               (EVAL (u.first asts) repl_env))))\n\n(rep\n (.. \"(def! load-file \"\n     \"  (fn* (f) \"\n     \"    (eval \"\n     \"      (read-string \"\n     \"        (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"))\n\n(rep\n (.. \"(defmacro! cond \"\n     \"  (fn* (& xs) \"\n     \"    (if (> (count xs) 0) \"\n     \"        (list 'if (first xs) \"\n     \"                  (if (> (count xs) 1) \"\n     \"                      (nth xs 1) \"\n     \"                      (throw \\\"odd number of forms to cond\\\")) \"\n     \"                  (cons 'cond (rest (rest xs)))))))\"))\n\n(e.env-set repl_env\n           (t.make-symbol \"*host-language*\")\n           (t.make-string \"fennel\"))\n\n(e.env-set repl_env\n           (t.make-symbol \"*ARGV*\")\n           (t.make-list (u.map t.make-string (u.slice arg 2 -1))))\n\n(fn handle-error\n  [err]\n  (if (t.nil?* err)\n      (print)\n      (= \"string\" (type err))\n      (print err)\n      (print (.. \"Error: \" (PRINT err)))))\n\n(if (<= 1 (length arg))\n    (xpcall (fn []\n              (rep (.. \"(load-file \\\"\" (. arg 1) \"\\\")\"))) ;; XXX: escaping?\n            handle-error)\n    (do\n     (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n     (var done false)\n     (while (not done)\n       (io.write \"user> \")\n       (io.flush)\n       (let [input (io.read)]\n         (if (not input)\n             (set done true)\n             (xpcall (fn []\n                       (print (rep input)))\n                     handle-error))))))\n"
  },
  {
    "path": "impls/fennel/types.fnl",
    "content": "(fn make-nil\n  [a-str]\n  {:tag :nil\n   :content \"nil\"})\n\n(fn make-boolean\n  [a-bool]\n  {:tag :boolean\n   :content a-bool})\n\n(fn make-number\n  [a-num]\n  {:tag :number\n   :content a-num})\n\n(fn make-keyword\n  [a-str]\n  {:tag :keyword\n   :content a-str})\n\n(fn make-symbol\n  [a-str]\n  {:tag :symbol\n   :content a-str})\n\n(fn make-string\n  [a-str]\n  {:tag :string\n   :content a-str})\n\n(local mal-nil (make-nil))\n\n(fn make-list\n  [elts md]\n  (local md (if md md mal-nil))\n  {:tag :list\n   :content elts\n   :md md})\n\n(fn make-vector\n  [elts md]\n  (local md (if md md mal-nil))\n  {:tag :vector\n   :content elts\n   :md md})\n\n(fn make-hash-map\n  [elts md]\n  (local md (if md md mal-nil))\n  {:tag :hash-map\n   :content elts\n   :md md})\n\n(fn make-fn\n  [a-fn ast params env is-macro md]\n  (local is-macro (if is-macro is-macro false))\n  (local md (if md md mal-nil))\n  {:tag :fn\n   :content a-fn\n   :ast ast\n   :params params\n   :env env\n   :is-macro is-macro\n   :md md})\n\n(fn make-atom\n  [ast]\n  {:tag :atom\n   :content ast})\n\n(local mal-true (make-boolean true))\n\n(local mal-false (make-boolean false))\n\n;;\n\n(fn get-value\n  [ast]\n  (. ast :content))\n\n(fn get-type\n  [ast]\n  (. ast :tag))\n\n(fn get-md\n  [ast]\n  (. ast :md))\n\n;;\n\n(fn get-is-macro\n  [ast]\n  (. ast :is-macro))\n\n(fn get-ast\n  [ast]\n  (. ast :ast))\n\n(fn get-params\n  [ast]\n  (. ast :params))\n\n(fn get-env\n  [ast]\n  (. ast :env))\n\n;;\n\n(fn nil?*\n  [ast]\n  (= :nil (. ast :tag)))\n\n(fn boolean?*\n  [ast]\n  (= :boolean (. ast :tag)))\n\n(fn number?*\n  [ast]\n  (= :number (. ast :tag)))\n\n(fn keyword?*\n  [ast]\n  (= :keyword (. ast :tag)))\n\n(fn symbol?*\n  [ast]\n  (= :symbol (. ast :tag)))\n\n(fn string?*\n  [ast]\n  (= :string (. ast :tag)))\n\n(fn list?*\n  [ast]\n  (= :list (. ast :tag)))\n\n(fn vector?*\n  [ast]\n  (= :vector (. ast :tag)))\n\n(fn hash-map?*\n  [ast]\n  (= :hash-map (. ast :tag)))\n\n(fn fn?*\n  [ast]\n  (= :fn (. ast :tag)))\n\n(fn atom?*\n  [ast]\n  (= :atom (. ast :tag)))\n\n(fn macro?*\n  [ast]\n  (and (fn?* ast)\n       (get-is-macro ast)))\n\n;;\n\n(fn macrofy\n  [fn-ast]\n  (local macro-ast {})\n  (each [k v (pairs fn-ast)]\n    (tset macro-ast k v))\n  (tset macro-ast\n        :is-macro true)\n  macro-ast)\n\n(fn clone-with-meta\n  [fn-ast meta-ast]\n  (local new-fn-ast {})\n  (each [k v (pairs fn-ast)]\n    (tset new-fn-ast k v))\n  (tset new-fn-ast\n        :md meta-ast)\n  new-fn-ast)\n\n;;\n\n(fn set-atom-value!\n  [atom-ast value-ast]\n  (tset atom-ast\n        :content value-ast))\n\n(fn deref*\n  [ast]\n  (if (not (atom?* ast))\n      ;; XXX\n      (error (.. \"Expected atom, got: \" (get-type ast)))\n      (get-value ast)))\n\n(fn reset!*\n  [atom-ast val-ast]\n  (set-atom-value! atom-ast val-ast)\n  val-ast)\n\n;;\n\n(fn empty?*\n  [ast]\n  (when (or (list?* ast)\n            (vector?* ast))\n    (= (length (get-value ast)) 0)))\n\n(fn true?*\n  [ast]\n  (and (boolean?* ast)\n       (= true (get-value ast))))\n\n(fn false?*\n  [ast]\n  (and (boolean?* ast)\n       (= false (get-value ast))))\n\n(fn equals?*\n  [ast-1 ast-2]\n  (let [type-1 (get-type ast-1)\n        type-2 (get-type ast-2)]\n    (if (and (not= type-1 type-2)\n             ;; XXX: not elegant\n             (not (and (list?* ast-1) (vector?* ast-2)))\n             (not (and (list?* ast-2) (vector?* ast-1))))\n      false\n      (let [val-1 (get-value ast-1)\n            val-2 (get-value ast-2)]\n        ;; XXX: when not a collection...\n        (if (and (not (list?* ast-1))\n                 (not (vector?* ast-1))\n                 (not (hash-map?* ast-1)))\n          (= val-1 val-2)\n          (if (not= (length val-1) (length val-2))\n            false\n            (if (and (not (hash-map?* ast-1))\n                     (not (hash-map?* ast-2)))\n              (do\n                (var found-unequal false)\n                (var idx 1)\n                (while (and (not found-unequal)\n                            (<= idx (length val-1)))\n                  (let [v1 (. val-1 idx)\n                        v2 (. val-2 idx)]\n                    (when (not (equals?* v1 v2))\n                      (set found-unequal true))\n                    (set idx (+ idx 1))))\n                (not found-unequal))\n              (if (or (not (hash-map?* ast-1))\n                      (not (hash-map?* ast-2)))\n                false\n                (do\n                 (var found-unequal false)\n                 (var idx-in-1 1)\n                 (while (and (not found-unequal)\n                             (<= idx-in-1 (length val-1)))\n                   (let [k1 (. val-1 idx-in-1)]\n                     (var found-in-2 false)\n                     (var idx-in-2 1)\n                     (while (and (not found-in-2)\n                                 (<= idx-in-2 (length val-2)))\n                       (let [k2 (. val-2 idx-in-2)]\n                         (if (equals?* k1 k2)\n                             (set found-in-2 true)\n                             (set idx-in-2 (+ idx-in-2 2)))))\n                     (if (not found-in-2)\n                         (set found-unequal true)\n                         (let [v1 (. val-1 (+ idx-in-1 1))\n                               v2 (. val-2 (+ idx-in-2 1))]\n                           (if (not (equals?* v1 v2))\n                               (set found-unequal true)\n                               (set idx-in-1 (+ idx-in-1 2)))))))\n                  (not found-unequal))))))))))\n\n{\n :make-nil make-nil\n :make-boolean make-boolean\n :make-number make-number\n :make-keyword make-keyword\n :make-symbol make-symbol\n :make-string make-string\n :make-list make-list\n :make-vector make-vector\n :make-hash-map make-hash-map\n :make-fn make-fn\n :make-atom make-atom\n ;;\n :mal-nil mal-nil\n :mal-true mal-true\n :mal-false mal-false\n ;;\n :get-value get-value\n :get-md get-md\n :get-is-macro get-is-macro\n :get-ast get-ast\n :get-params get-params\n :get-env get-env\n ;;\n :nil?* nil?*\n :boolean?* boolean?*\n :number?* number?*\n :keyword?* keyword?*\n :symbol?* symbol?*\n :string?* string?*\n :list?* list?*\n :vector?* vector?*\n :hash-map?* hash-map?*\n :fn?* fn?*\n :atom?* atom?*\n :macro?* macro?*\n ;;\n :macrofy macrofy\n :clone-with-meta clone-with-meta\n ;;\n :set-atom-value! set-atom-value!\n :deref* deref*\n :reset!* reset!*\n ;;\n :empty?* empty?*\n :true?* true?*\n :false?* false?*\n :equals?* equals?*\n}\n"
  },
  {
    "path": "impls/fennel/utils.fnl",
    "content": "(fn throw*\n  [ast]\n  (error ast))\n\n(fn abs-index\n  [i len]\n  (if (> i 0)\n      i\n      (< i 0)\n      (+ len i 1)\n      nil))\n\n(comment\n\n (abs-index 0 9)\n ;; => nil\n\n (abs-index 1 9)\n ;; => 1\n\n (abs-index -1 9)\n ;; => 9\n\n (abs-index -2 9)\n ;; => 8\n\n )\n\n(fn slice\n  [tbl beg end]\n  (local len-tbl (length tbl))\n  (local new-beg\n    (if beg (abs-index beg len-tbl) 1))\n  (local new-end\n    (if end (abs-index end len-tbl) len-tbl))\n  (local start\n    (if (< new-beg 1) 1 new-beg))\n  (local fin\n    (if (< len-tbl new-end) len-tbl new-end))\n  (local new-tbl [])\n  (for [idx start fin]\n    (tset new-tbl\n          (+ (length new-tbl) 1)\n          (. tbl idx)))\n  new-tbl)\n\n(comment\n\n (slice [7 8 9] 2 -1)\n ;; => [8 9]\n\n (slice [1 2 3] 1 2)\n ;; => [1 2]\n\n )\n\n(fn first\n  [tbl]\n  (. tbl 1))\n\n(comment\n\n (first [7 8 9])\n ;; => 7\n\n )\n\n(fn last\n  [tbl]\n  (. tbl (length tbl)))\n\n(comment\n\n (last [7 8 9])\n ;; => 9\n\n )\n\n(fn map\n  [a-fn tbl]\n  (local new-tbl [])\n  (each [i elt (ipairs tbl)]\n    (tset new-tbl i (a-fn elt)))\n  new-tbl)\n\n(comment\n\n (map (fn [x] (+ x 1)) [7 8 9])\n ;; => [8 9 10]\n\n (map (fn [n] [n (+ n 1)]) [1 2 3])\n ;; => [[1 2] [2 3] [3 4]]\n\n )\n\n(fn reverse\n  [tbl]\n  (local new-tbl [])\n  (for [i (length tbl) 1 -1]\n    (table.insert new-tbl (. tbl i)))\n  new-tbl)\n\n(comment\n\n (reverse [:a :b :c])\n ;; => [\"c\" \"b\" \"a\"]\n\n )\n\n(fn concat-two\n  [tbl-1 tbl-2]\n  (local new-tbl [])\n  (each [i elt (ipairs tbl-1)]\n    (table.insert new-tbl elt))\n  (each [i elt (ipairs tbl-2)]\n    (table.insert new-tbl elt))\n  new-tbl)\n\n(comment\n\n (concat-two [:a :b :c] [:d :e :f])\n ;; => [\"a\" \"b\" \"c\" \"d\" \"e\" \"f\"]\n\n (concat-two {1 :a 2 :b 3 :c} {1 :d 2 :e 3 :f})\n ;; => [\"a\" \"b\" \"c\" \"d\" \"e\" \"f\"]\n\n )\n\n{\n :throw* throw*\n :slice slice\n :first first\n :last last\n :map map\n :reverse reverse\n :concat-two concat-two\n}\n"
  },
  {
    "path": "impls/forth/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install gforth\n"
  },
  {
    "path": "impls/forth/Makefile",
    "content": "SOURCES_BASE = str.fs types.fs reader.fs printer.fs\nSOURCES_LISP = env.fs core.fs stepA_mal.fs\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.fs mal\n\nmal.fs: $(SOURCES)\n\tcat $+ | egrep -v \"^require |^droprequire \" > $@\n\nmal: mal.fs\n\techo \"#! /usr/bin/env gforth\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.fs mal\n"
  },
  {
    "path": "impls/forth/core.fs",
    "content": "require env.fs\n\n0 MalEnv. constant core\n\n: args-as-native { argv argc -- entry*argc... }\n    argc 0 ?do\n        argv i cells + @ as-native\n    loop ;\n\n: defcore* ( sym xt )\n    MalNativeFn. core env/set ;\n\n: defcore\n    parse-allot-name MalSymbol. ( xt )\n    ['] defcore* :noname ;\n\ndefcore +  args-as-native +  MalInt. ;;\ndefcore -  args-as-native -  MalInt. ;;\ndefcore *  args-as-native *  MalInt. ;;\ndefcore /  args-as-native /  MalInt. ;;\ndefcore <  args-as-native <  mal-bool ;;\ndefcore >  args-as-native >  mal-bool ;;\ndefcore <= args-as-native <= mal-bool ;;\ndefcore >= args-as-native >= mal-bool ;;\n\ndefcore list { argv argc }\n    argc cells allocate throw { start }\n    argv  start  argc cells  cmove\n    start argc MalList. ;;\n\ndefcore vector { argv argc }\n    argc cells allocate throw { start }\n    argv  start  argc cells  cmove\n    start argc MalList.\n    MalVector new swap over MalVector/list ! ;;\n\ndefcore empty? drop @ empty? ;;\ndefcore count drop @ mal-count ;;\n\ndefcore = drop dup @ swap cell+ @ swap m= mal-bool ;;\n\n: pr-str-multi ( readably? argv argc )\n    ?dup 0= if drop 0 0\n    else\n        { argv argc }\n        new-str\n        argv @ pr-buf\n        argc 1 ?do\n            a-space\n            argv i cells + @ pr-buf\n        loop\n    endif ;\n\ndefcore prn true -rot pr-str-multi type cr drop mal-nil ;;\ndefcore pr-str true -rot pr-str-multi MalString. nip ;;\ndefcore println false -rot pr-str-multi type cr drop mal-nil ;;\ndefcore str ( argv argc )\n    dup 0= if\n        MalString.\n    else\n        { argv argc }\n        false new-str\n        argc 0 ?do\n            argv i cells + @ pr-buf\n        loop\n        MalString. nip\n    endif ;;\n\ndefcore read-string drop @ unpack-str read-str ;;\ndefcore slurp drop @ unpack-str slurp-file MalString. ;;\n\ncreate core-buff 128 allot\ndefcore readline ( argv argc -- mal-string )\n    drop @ unpack-str type stdout flush-file drop\n    core-buff 128 stdin read-line throw\n    if core-buff swap MalString. else drop mal-nil endif ;;\n\n\ndefcore cons ( argv[item,coll] argc )\n    drop dup @ swap cell+ @ ( item coll )\n    to-list conj ;;\n\ndefcore concat { lists argc }\n    MalList new\n    lists over MalList/start !\n    argc over MalList/count !\n    MalList/concat ;;\n\ndefcore vec ( argv[coll] argc )\n    drop\n    @\n    dup mal-type @ MalList = if\n        MalVector new tuck MalVector/list !\n    endif ;;\n\ndefcore conj { argv argc }\n    argv @ ( coll )\n    argc 1 ?do\n        argv i cells + @ swap conj\n    loop ;;\n\ndefcore seq drop @ seq ;;\n\ndefcore assoc { argv argc }\n    argv @ ( coll )\n    argv argc cells +  argv cell+  +do\n        i @ \\ key\n        i cell+ @ \\ val\n        rot assoc\n    2 cells +loop ;;\n\ndefcore keys ( argv argc )\n    drop @ MalMap/list @\n    dup MalList/start @ swap MalList/count @ { start count }\n    here\n    start count cells +  start  +do\n        i @ ,\n    2 cells +loop\n    here>MalList ;;\n\ndefcore vals ( argv argc )\n    drop @ MalMap/list @\n    dup MalList/start @ swap MalList/count @ { start count }\n    here\n    start count cells +  start cell+  +do\n        i @ ,\n    2 cells +loop\n    here>MalList ;;\n\ndefcore dissoc { argv argc }\n    argv @ \\ coll\n    argv argc cells +  argv cell+  +do\n        i @ swap dissoc\n    cell +loop ;;\n\ndefcore hash-map { argv argc }\n    MalMap/Empty\n    argc cells argv +  argv  +do\n        i @  i cell+ @  rot  assoc\n    2 cells +loop ;;\n\ndefcore get { argv argc }\n    argc 3 < if mal-nil else argv cell+ cell+ @ endif\n    argv cell+ @ \\ key\n    argv @ \\ coll\n    get ;;\n\ndefcore contains? { argv argc }\n    0\n    argv cell+ @ \\ key\n    argv @ \\ coll\n    get 0 <> mal-bool ;;\n\ndefcore nth ( argv[coll,i] argc )\n    drop dup @ to-list ( argv list )\n    swap cell+ @ MalInt/int @ ( list i )\n    over MalList/count @ ( list i count )\n    2dup >= if { i count }\n        0 0\n        new-str i int>str str-append s\\\" \\040>= \" count int>str\n        s\" nth out of bounds: \" ...throw-str\n    endif drop ( list i )\n    cells swap ( c-offset list )\n    MalList/start @ + @ ;;\n\ndefcore first ( argv[coll] argc )\n    drop @ to-list\n    dup MalList/count @ 0= if\n        drop mal-nil\n    else\n        MalList/start @ @\n    endif ;;\n\ndefcore rest ( argv[coll] argc )\n    drop @ to-list MalList/rest ;;\n\ndefcore meta ( argv[obj] argc )\n    drop @ mal-meta @\n    ?dup 0= if mal-nil endif ;;\n\ndefcore with-meta ( argv[obj,meta] argc )\n    drop ( argv )\n    dup cell+ @  swap @  ( meta obj )\n    dup mal-type @ MalTypeType-struct @ ( meta obj obj-size )\n    dup allocate throw { new-obj } ( meta obj obj-size )\n    new-obj swap cmove ( meta )\n    new-obj mal-meta ! ( )\n    new-obj ;;\n\ndefcore atom ( argv[val] argc )\n    drop @ Atom. ;;\n\ndefcore deref ( argv[atom] argc )\n    drop @ Atom/val @ ;;\n\ndefcore reset! ( argv[atom,val] argc )\n    drop dup cell+ @ ( argv val )\n    dup -rot swap @ Atom/val ! ;;\n\ndefcore apply { argv argc -- val }\n    \\ argv is (fn args... more-args)\n    argv argc 1- cells + @ to-list { more-args }\n    argc 2 - { list0len }\n    more-args MalList/count @ list0len + { final-argc }\n    final-argc cells allocate throw { final-argv }\n    argv cell+   final-argv   list0len cells   cmove\n    more-args MalList/start @   final-argv list0len cells +  final-argc list0len - cells  cmove\n    final-argv final-argc argv @  invoke ;;\n\ndefcore throw ( argv argc -- )\n    drop @ to exception-object\n    1 throw ;;\n\ndefcore map?     drop @ mal-type @ MalMap     = mal-bool ;;\ndefcore list?    drop @ mal-type @ MalList    = mal-bool ;;\ndefcore vector?  drop @ mal-type @ MalVector  = mal-bool ;;\ndefcore keyword? drop @ mal-type @ MalKeyword = mal-bool ;;\ndefcore symbol?  drop @ mal-type @ MalSymbol  = mal-bool ;;\ndefcore string?  drop @ mal-type @ MalString  = mal-bool ;;\ndefcore atom?    drop @ mal-type @ Atom       = mal-bool ;;\ndefcore true?    drop @ mal-true  = mal-bool ;;\ndefcore false?   drop @ mal-false = mal-bool ;;\ndefcore nil?     drop @ mal-nil   = mal-bool ;;\ndefcore number?  drop @ mal-type @ MalInt = mal-bool ;;\ndefcore fn?\n    drop @\n    dup mal-type @ MalUserFn = if\n        MalUserFn/is-macro? @ if\n            mal-false\n        else\n            mal-true\n        endif\n    else\n        mal-type @ MalNativeFn = if\n            mal-true\n        else\n            mal-false\n        endif\n    endif ;;\ndefcore macro?   drop @ dup mal-type @ MalUserFn =\n                        swap MalUserFn/is-macro? @\n                        and mal-bool ;;\n\ndefcore sequential? drop @ sequential? ;;\n\ndefcore keyword  drop @ unpack-str MalKeyword. ;;\ndefcore symbol   drop @ unpack-str MalSymbol. ;;\n\ndefcore time-ms 2drop utime d>s 1000 / MalInt. ;;\n"
  },
  {
    "path": "impls/forth/env.fs",
    "content": "require types.fs\n\nMalType%\n   cell% field MalEnv/outer\n   cell% field MalEnv/data\ndeftype MalEnv\n\n: MalEnv. { outer -- env }\n    MalEnv new { env }\n    outer env MalEnv/outer !\n    MalMap/Empty env MalEnv/data !\n    env ;\n\n: env/set { key val env -- }\n    key val env MalEnv/data @ assoc\n    env MalEnv/data ! ;\n\n: env/get-addr { key env -- val-addr }\n    env\n    begin ( env )\n        key over MalEnv/data @ MalMap/get-addr ( env addr-or-0 )\n        ?dup 0= if ( env )\n            MalEnv/outer @ dup 0= ( env-or-0 done-looping? )\n        else ( env addr )\n            nip -1 \\ found it! ( addr -1 )\n        endif\n    until ;\n\nMalEnv\n  extend pr-buf { env }\n    env MalEnv/data @ pr-buf\n    a-space s\" outer: \" str-append\n    env MalEnv/outer @ ?dup 0= if\n        s\" <none>\" str-append\n    else\n        pr-buf\n    endif ;;\ndrop\n"
  },
  {
    "path": "impls/forth/misc-tests.fs",
    "content": "require printer.fs\n\n\\ === basic testing util === /\n: test=\n  2dup m= if\n    2drop\n  else\n    cr .\" assert failed on line \" sourceline# .\n    swap cr .\" | got \" . cr .\" | expected \" . cr\n  endif ;\n\n\\ array function tests\ncreate za 2 , 6 , 7 , 10 , 15 , 80 , 81 ,\n\n7 za 2 array-find   -1 test= 0 test=\n7 za 6 array-find   -1 test= 1 test=\n7 za 10 array-find  -1 test= 3 test=\n7 za 81 array-find  -1 test= 6 test=\n7 za 12 array-find   0 test= 4 test=\n7 za 8  array-find   0 test= 3 test=\n7 za 100 array-find  0 test= 7 test=\n7 za 1 array-find    0 test= 0 test=\n6 za 81 array-find   0 test= 6 test=\n\n10 new-array\n1 swap 0  5 array-insert\n2 swap 1  7 array-insert\n3 swap 3 12 array-insert\n4 swap 4 15 array-insert\n5 swap 5 20 array-insert\n\ndup 0 cells + @   5 test=\ndup 1 cells + @   7 test=\ndup 2 cells + @  10 test=\ndup 3 cells + @  12 test=\ndup 4 cells + @  15 test=\ndup 5 cells + @  20 test=\n\n\n\\ Protocol tests\n\n: t1\nmal-nil\n42 MalInt. mal-nil conj\n10 MalInt. mal-nil conj conj\n20 MalInt. swap conj\n23 MalInt. mal-nil conj conj conj\npr-str s\" (nil (20 (42) 10) 23)\" str= -1 test=\n\n1500 MalInt. 1500 MalInt. test=\n\n\\ MalList tests\n\nhere 1 MalInt. , 2 MalInt. , 3 MalInt. , here>MalList\n4 MalInt. swap conj\n5 MalInt. swap conj\npr-str s\" (5 4 1 2 3)\" str= -1 test=\n\n\\ map tests\n\ns\" one\" MalString. s\" one\" MalString. test=\ns\" one\" MalString. s\" x\" MalString. m= 0 test=\n\nMalMap/Empty\n1000 MalInt. 1100 rot assoc\n2000 MalInt. 2100 rot assoc\n3000 MalInt. 3100 rot assoc\n\ndup 99 2000 MalInt. rot get 2100 test=\ndup 99 4000 MalInt. rot get 99 test=\ndrop\n\nMalMap/Empty\ns\" one\" MalString. s\" first\" MalString. rot assoc\ns\" two\" MalString. s\" second\" MalString. rot assoc\ns\" three\" MalString. s\" third\" MalString. rot assoc\n\ndup 99 s\" two\" MalString. rot get s\" second\" MalString. test=\ndup 99 s\" none\" MalString. rot get 99 test=\ndrop\n\n99 MalInt. 10 MalInt. MalMap/Empty get 99 MalInt. test=\n\n;\nt1\n\n\\ eval tests\n\nrequire step2_eval.fs\n\n: t2\nmal-nil\n    1 MalInt. swap conj\n    2 MalInt. swap conj\n    3 MalInt. swap conj\nmal-eval\n;\nt2\n\nbye\n"
  },
  {
    "path": "impls/forth/printer.fs",
    "content": "require str.fs\nrequire types.fs\n\n\\ === printer protocol and implementations === /\n\ndef-protocol-method pr-buf ( readably? str-addr str-len this -- str-addr str-len )\ndef-protocol-method pr-seq-buf ( readably? str-addr str-len this -- str-addr str-len )\n\n: pr-str { obj }\n    true new-str obj pr-buf rot drop ;\n\n\\ Examples of extending existing protocol methods to existing type\nMalDefault\n  extend pr-buf\n    { this }\n    s\" #<\" str-append\n    this mal-type @ type-name str-append\n    a-space\n    this int>str str-append\n    s\" >\" str-append ;;\ndrop\n\nMalNil   extend pr-buf drop s\" nil\"   str-append ;; drop\nMalTrue  extend pr-buf drop s\" true\"  str-append ;; drop\nMalFalse extend pr-buf drop s\" false\" str-append ;; drop\n\nMalList\n  extend pr-buf\n    -rot s\" (\" str-append ( list str-addr str-len )\n    rot pr-seq-buf\n    s\" )\" str-append ;;\n  extend pr-seq-buf { list }\n    list MalList/count @ 0 > if\n        list MalList/start @ { start }\n        start @ pr-buf\n        list MalList/count @ 1 ?do\n            a-space\n            start i cells + @ pr-buf\n        loop\n    endif ;;\ndrop\n\nMalVector\n  extend pr-buf\n    MalVector/list @\n    -rot s\" [\" str-append ( list str-addr str-len )\n    rot pr-seq-buf\n    s\" ]\" str-append ;;\ndrop\n\nMalMap\n  extend pr-buf\n    MalMap/list @\n    -rot s\" {\" str-append ( list str-addr str-len )\n    rot { list }\n    list MalList/count @ { count }\n    count 0 > if\n        list MalList/start @ { start }\n        start @ pr-buf a-space start cell+ @ pr-buf\n        count 2 / 1 ?do\n            a-space\n            start i 2 * cells + @ pr-buf a-space\n            start i 2 * 1+ cells + @ pr-buf\n        loop\n    endif\n    s\" }\" str-append ;;\ndrop\n\nMalInt\n  extend pr-buf\n    MalInt/int @ int>str str-append ;;\ndrop\n\nMalSymbol\n  extend pr-buf\n    unpack-sym str-append ;;\ndrop\n\nMalKeyword\n  extend pr-buf { kw }\n    s\" :\" str-append\n    kw unpack-keyword str-append ;;\ndrop\n\n: escape-str { addr len }\n    s\\\" \\\"\" str-append\n    addr len +  addr  ?do\n        i c@ case\n            [char] \" of s\\\" \\\\\\\"\" str-append endof\n            [char] \\ of s\\\" \\\\\\\\\" str-append endof\n            10 of s\\\" \\\\n\" str-append endof\n            13 of s\\\" \\\\r\" str-append endof\n            -rot i 1 str-append rot\n        endcase\n    loop\n    s\\\" \\\"\" str-append ;\n\nMalString\n  extend pr-buf\n    dup MalString/str-addr @\n    swap MalString/str-len @\n    4 pick if\n        escape-str\n    else\n        str-append\n    endif ;;\ndrop\n\nAtom\n  extend pr-buf { this }\n    s\" (atom \" str-append\n    this Atom/val @ pr-buf\n    s\" )\" str-append ;;\ndrop"
  },
  {
    "path": "impls/forth/reader.fs",
    "content": "require types.fs\nrequire printer.fs\n\n\\ Drop a char off the front of string by advancing the addr and\n\\ decrementing the length, and fetch next char\n: adv-str ( str-addr str-len -- str-addr str-len char )\n    swap 1+ swap 1-\n    dup 0= if 0 ( eof )\n    else over c@ endif ;\n\n: mal-digit? ( char -- flag )\n    dup [char] 9 <= if\n        [char] 0 >=\n    else\n        drop 0\n    endif ;\n\n: char-in-str? ( char str-addr str-len )\n    rot { needle }\n    false -rot\n    over + swap ?do\n        i c@ needle = if drop true leave endif\n    loop ;\n\n: sym-char? ( char -- flag )\n    s\\\" \\n\\r\\t\\000[]{}()'\\\"`,; \" char-in-str? 0= ;\n\n: skip-spaces ( str-addr str-len char -- str-addr str-len non-space-char )\n    begin\n        begin\n            dup s\\\" \\n\\r\\t, \" char-in-str?\n        while ( str-addr str-len space-char )\n            drop adv-str\n        repeat\n        dup [char] ; = if\n            drop\n            begin\n                adv-str s\\\" \\n\\r\\000\" char-in-str?\n            until\n            adv-str false\n        else\n            true\n        endif\n    until ;\n\ndefer read-form ( str-addr str-len -- str-addr str-len mal-obj )\n\n: read-int ( str-addr str-len digit-char -- str-addr str-len non-digit-char mal-int )\n    0 { int }\n    0 { neg }\n    dup [char] - = if drop adv-str 1 to neg endif\n    begin ( str-addr str-len digit-char )\n        [char] 0 - int 10 * + to int ( str-addr str-len )\n        adv-str dup mal-digit? 0= ( str-addr str-len digit-char )\n    until\n    neg if 0 int - to int endif\n    int MalInt. ;\n\n: read-symbol-str ( str-addr str-len sym-char -- str-addr str-len char sym-addr sym-len )\n    new-str { sym-addr sym-len }\n    begin ( str-addr str-len sym-char )\n        sym-addr sym-len rot str-append-char to sym-len to sym-addr\n        adv-str dup sym-char? 0=\n    until\n    sym-addr sym-len ;\n\n: read-string-literal ( in-addr in-len quote-char -- in-addr in-len mal-string )\n    new-str { out-addr out-len }\n    drop \\ drop leading quote\n    begin ( in-addr in-len )\n        adv-str over 0= if\n            2drop 0 0 s\\\" expected '\\\"', got EOF\" ...throw-str\n        endif\n        dup [char] \" <>\n    while\n        dup [char] \\ = if\n            drop adv-str\n            dup [char] n = if drop 10 endif\n            dup [char] r = if drop 13 endif\n        endif\n        out-addr out-len rot str-append-char to out-len to out-addr\n    repeat\n    drop adv-str \\ skip trailing quote\n    out-addr out-len MalString. ;\n\n: read-list ( str-addr str-len open-paren-char close-paren-char\n                  -- str-addr str-len non-paren-char mal-list )\n    here { close-char old-here }\n    drop adv-str\n    begin ( str-addr str-len char )\n        skip-spaces ( str-addr str-len non-space-char )\n        over 0= if\n            drop 2drop 0 0 s\" ', got EOF\"\n            close-char pad ! pad 1\n            s\" expected '\" ...throw-str\n        endif\n        dup close-char <>\n    while ( str-addr str-len non-space-non-paren-char )\n            read-form ,\n    repeat\n    drop adv-str\n    old-here here>MalList ;\n\ns\" deref\"          MalSymbol. constant deref-sym\ns\" quote\"          MalSymbol. constant quote-sym\ns\" quasiquote\"     MalSymbol. constant quasiquote-sym\ns\" splice-unquote\" MalSymbol. constant splice-unquote-sym\ns\" unquote\"        MalSymbol. constant unquote-sym\n\n: read-wrapped ( buf-addr buf-len quote-char sym-addr sym-len -- buf-addr buf-len char mal-list )\n    here { old-here }\n    , ( buf-addr buf-len char )\n    read-form , ( buf-addr buf-len char )\n    old-here here>MalList ;\n\n: read-form2 ( str-addr str-len char -- str-addr str-len char mal-obj )\n    skip-spaces\n    dup [char] - = 3 pick 1 + c@ mal-digit? and if read-int else\n    dup mal-digit? if read-int else\n    dup [char] ( = if [char] ) read-list else\n    dup [char] [ = if [char] ] read-list MalVector new tuck MalVector/list ! else\n    dup [char] { = if [char] } read-list MalMap new tuck MalMap/list ! else\n    dup [char] \" = if read-string-literal else\n    dup [char] : = if drop adv-str read-symbol-str MalKeyword. else\n    dup [char] @ = if drop adv-str deref-sym read-wrapped else\n    dup [char] ' = if drop adv-str quote-sym read-wrapped else\n    dup [char] ` = if drop adv-str quasiquote-sym read-wrapped else\n    dup [char] ~ = if\n        drop adv-str\n        dup [char] @ = if drop adv-str splice-unquote-sym read-wrapped\n        else unquote-sym read-wrapped\n        endif\n    else\n    dup [char] ^ = if\n        drop adv-str\n        read-form { meta } read-form { obj }\n        meta mal-nil conj\n        obj swap conj\n        s\" with-meta\" MalSymbol. swap conj\n    else\n        read-symbol-str\n        2dup s\" true\" str= if 2drop mal-true\n        else 2dup s\" false\" str= if 2drop mal-false\n        else 2dup s\" nil\" str= if 2drop mal-nil\n        else\n          MalSymbol.\n    endif endif endif endif endif endif endif endif endif endif endif endif endif endif endif ;\n' read-form2 is read-form\n\n: read-str ( str-addr str-len - mal-obj )\n    over c@ read-form { obj } drop 2drop obj ;\n"
  },
  {
    "path": "impls/forth/run",
    "content": "#!/usr/bin/env bash\nexec gforth $(dirname $0)/${STEP:-stepA_mal}.fs \"${@}\"\n"
  },
  {
    "path": "impls/forth/step0_repl.fs",
    "content": "require types.fs\n\n: read ;\n: eval ;\n: print ;\n\n: rep\n    read\n    eval\n    print ;\n\ncreate buff 128 allot\n\n: read-lines\n    begin\n      .\" user> \"\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap\n        rep type cr\n      endif\n    repeat ;\n\nread-lines\n"
  },
  {
    "path": "impls/forth/step1_read_print.fs",
    "content": "require reader.fs\nrequire printer.fs\n\n: read read-str ;\n: eval ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    eval\n    print ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: read-lines\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\nread-lines\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step2_eval.fs",
    "content": "require reader.fs\nrequire printer.fs\n\n: args-as-native { argv argc -- entry*argc... }\n    argc 0 ?do\n        argv i cells + @ as-native\n    loop ;\n\n: env-assoc ( map sym-str-addr sym-str-len xt )\n    -rot MalSymbol. swap MalNativeFn. rot assoc ;\n\nMalMap/Empty\n    s\" +\" :noname args-as-native + MalInt. ; env-assoc\n    s\" -\" :noname args-as-native - MalInt. ; env-assoc\n    s\" *\" :noname args-as-native * MalInt. ; env-assoc\n    s\" /\" :noname args-as-native / MalInt. ; env-assoc\nconstant repl-env\n\n: read read-str ;\n: eval ( env obj )\n    \\ .\" EVAL: \" dup pr-str safe-type cr\n    mal-eval ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke ( env list this -- list )\n    MalNativeFn/xt @ { xt }\n    eval-rest ( argv argc )\n    xt execute ( return-val ) ;;\ndrop\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    0 sym env get\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: read-lines\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\nread-lines\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step3_env.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire env.fs\n\n: args-as-native { argv argc -- entry*argc... }\n    argc 0 ?do\n        argv i cells + @ as-native\n    loop ;\n\n0 MalEnv. constant repl-env\ns\" +\" MalSymbol.  :noname args-as-native + MalInt. ; MalNativeFn.  repl-env env/set\ns\" -\" MalSymbol.  :noname args-as-native - MalInt. ; MalNativeFn.  repl-env env/set\ns\" *\" MalSymbol.  :noname args-as-native * MalInt. ; MalNativeFn.  repl-env env/set\ns\" /\" MalSymbol.  :noname args-as-native / MalInt. ; MalNativeFn.  repl-env env/set\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    over debug-eval-sym swap env/get-addr ?dup-if\n        @ dup mal-false <> swap mal-nil <> and if\n            .\" EVAL: \" dup pr-str safe-type cr\n        endif\n    endif\n    mal-eval ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke ( env list this -- list )\n    MalNativeFn/xt @ { xt }\n    eval-rest ( argv argc )\n    xt execute ( return-val ) ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ eval\n    \\ TODO: dec refcount of env\n    ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: read-lines\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\nread-lines\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step4_if_fn_do.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    over debug-eval-sym swap env/get-addr ?dup-if\n        @ dup mal-false <> swap mal-nil <> and if\n            .\" EVAL: \" dup pr-str safe-type cr\n        endif\n    endif\n    mal-eval ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke ( env list this -- list )\n    MalNativeFn/xt @ { xt }\n    eval-rest ( argv argc )\n    xt execute ( return-val ) ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @\n    0\n    list MalList/count @ 1 ?do\n        drop\n        dup i cells + @ env swap eval\n    loop\n    nip ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    call-env list eval-rest { argv argc }\n\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            f-args i 1+ cells + @ ( more-args-symbol )\n            MalList new ( sym more-args )\n            argc i - dup { c } over MalList/count !\n            c cells allocate throw dup { start } over MalList/start !\n            argv i cells +  start  c cells  cmove\n            env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n\n    env   mal-fn MalUserFn/body @   eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\n\n: read-lines\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\nread-lines\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step5_tco.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n99999999 constant TCO-eval\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    begin\n        over debug-eval-sym swap env/get-addr ?dup-if\n            @ dup mal-false <> swap mal-nil <> and if\n                .\" EVAL: \" dup pr-str safe-type cr\n            endif\n        endif\n        mal-eval\n        dup TCO-eval =\n    while\n        drop\n    repeat ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  TCO-eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke ( env list this -- list )\n    MalNativeFn/xt @ { xt }\n    eval-rest ( argv argc )\n    xt execute ( return-val ) ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ TCO-eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @ { start }\n    list MalList/count @ dup 1- { last } 1 ?do\n        env   start i cells + @\n        i last = if\n            TCO-eval\n        else\n            eval drop\n        endif\n    loop ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ TCO-eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ TCO-eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    call-env list eval-rest { argv argc }\n\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            f-args i 1+ cells + @ ( more-args-symbol )\n            MalList new ( sym more-args )\n            argc i - dup { c } over MalList/count !\n            c cells allocate throw dup { start } over MalList/start !\n            argv i cells +  start  c cells  cmove\n            env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n\n    env   mal-fn MalUserFn/body @   TCO-eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\n\n: read-lines\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\nread-lines\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step6_file.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n99999999 constant TCO-eval\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    begin\n        over debug-eval-sym swap env/get-addr ?dup-if\n            @ dup mal-false <> swap mal-nil <> and if\n                .\" EVAL: \" dup pr-str safe-type cr\n            endif\n        endif\n        mal-eval\n        dup TCO-eval =\n    while\n        drop\n    repeat ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  TCO-eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke { env list this -- list }\n    env list eval-rest ( argv argc )\n    this invoke ;;\n  extend invoke ( argv argc this -- val )\n    MalNativeFn/xt @ execute ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ TCO-eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @ { start }\n    list MalList/count @ dup 1- { last } 1 ?do\n        env   start i cells + @\n        i last = if\n            TCO-eval\n        else\n            eval drop\n        endif\n    loop ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ TCO-eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ TCO-eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\n: new-user-fn-env { argv argc mal-fn -- env }\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            argc i - { c }\n            c cells allocate throw { start }\n            argv i cells +  start  c cells  cmove\n            f-args i 1+ cells + @ ( more-args-symbol )\n            start c MalList. env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n    env ;\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    call-env list eval-rest\n    mal-fn invoke ;;\n\n  extend invoke ( argv argc mal-fn )\n    dup { mal-fn } new-user-fn-env { env }\n    env   mal-fn MalUserFn/body @   TCO-eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\ndefcore eval ( argv argc )\n  drop @ repl-env swap eval ;;\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\n: mk-args-list ( -- )\n    here\n    begin\n        next-arg 2dup 0 0 d<> while\n            MalString. ,\n    repeat\n    2drop here>MalList ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: nop ;\n\ndefcore swap! { argv argc -- val }\n    \\ argv is  (atom fn args...)\n    argv @ { atom }\n    argv cell+ @ { fn }\n    argc 1- { call-argc }\n    call-argc cells allocate throw { call-argv }\n    atom Atom/val   call-argv    1 cells   cmove\n    argv cell+ cell+   call-argv cell+   call-argc 1- cells   cmove\n    call-argv call-argc fn  invoke\n    dup TCO-eval = if drop eval endif { new-val }\n    new-val atom Atom/val !\n    new-val ;;\n\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\ns\\\" (def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" rep 2drop\n\n: repl ( -- )\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\n: main ( -- )\n    mk-args-list { args-list }\n    args-list MalList/count @ 0= if\n        s\" *ARGV*\" MalSymbol. MalList/Empty repl-env env/set\n        repl\n    else\n        args-list MalList/start @ @ { filename }\n        s\" *ARGV*\" MalSymbol. args-list MalList/rest repl-env env/set\n\n        repl-env\n        here s\" load-file\" MalSymbol. , filename , here>MalList\n        eval print\n    endif ;\n\nmain\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step7_quote.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n99999999 constant TCO-eval\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    begin\n        over debug-eval-sym swap env/get-addr ?dup-if\n            @ dup mal-false <> swap mal-nil <> and if\n                .\" EVAL: \" dup pr-str safe-type cr\n            endif\n        endif\n        mal-eval\n        dup TCO-eval =\n    while\n        drop\n    repeat ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  TCO-eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke { env list this -- list }\n    env list eval-rest ( argv argc )\n    this invoke ;;\n  extend invoke ( argv argc this -- val )\n    MalNativeFn/xt @ execute ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ns\" concat\" MalSymbol. constant concat-sym\ns\" cons\" MalSymbol. constant cons-sym\ns\" vec\" MalSymbol. constant vec-sym\n\ndefer quasiquote\n\n( If the list has two elements and the first is sym, return the second )\n( element and true, else return the list unchanged and false. )\n: qq_extract_unquote ( list symbol -- form f )\n    over MalList/count @ 2 = if\n        over MalList/start @ tuck @ m= if         ( list start - )\n            cell+ @\n            nip\n            true\n            exit\n        endif\n    endif\n    drop\n    false ;\n\n( Transition function for the following quasiquote folder. )\n: qq_loop ( acc elt -- form )\n    dup mal-type @ MalList = if\n        splice-unquote-sym qq_extract_unquote if\n            here concat-sym , swap , swap , here>MalList\n            exit\n        endif\n    endif\n    quasiquote\n    here cons-sym , swap , swap , here>MalList ;\n\n( Right-fold quasiquoting each element of a list. )\n: qq_foldr ( list -- form )\n    dup MalList/count @ if\n        dup MalList/rest recurse\n        swap MalList/start @ @\n        qq_loop\n    endif ;\n\n: quasiquote0 ( ast -- form )\n    dup mal-type @ case\n    MalList of\n        unquote-sym qq_extract_unquote if\n            ( the work is already done )\n        else\n            qq_foldr\n        endif\n    endof\n    MalVector of\n        MalVector/list @ qq_foldr\n        here vec-sym , swap , here>MalList\n    endof\n    MalSymbol of\n        here quote-sym , swap , here>MalList\n    endof\n    MalMap of\n        here quote-sym , swap , here>MalList\n    endof\n    ( other types are returned unchanged )\n    endcase ;\n' quasiquote0 is quasiquote\n\ndefspecial quasiquote ( env list )\n    MalList/start @ cell+ @ ( ast )\n    quasiquote TCO-eval ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ TCO-eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @ { start }\n    list MalList/count @ dup 1- { last } 1 ?do\n        env   start i cells + @\n        i last = if\n            TCO-eval\n        else\n            eval drop\n        endif\n    loop ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ TCO-eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ TCO-eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\n: new-user-fn-env { argv argc mal-fn -- env }\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            argc i - { c }\n            c cells allocate throw { start }\n            argv i cells +  start  c cells  cmove\n            f-args i 1+ cells + @ ( more-args-symbol )\n            start c MalList. env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n    env ;\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    call-env list eval-rest\n    mal-fn invoke ;;\n\n  extend invoke ( argv argc mal-fn )\n    dup { mal-fn } new-user-fn-env { env }\n    env   mal-fn MalUserFn/body @   TCO-eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\ndefcore eval ( argv argc )\n  drop @ repl-env swap eval ;;\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\n: mk-args-list ( -- )\n    here\n    begin\n        next-arg 2dup 0 0 d<> while\n            MalString. ,\n    repeat\n    2drop here>MalList ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: nop ;\n\ndefcore swap! { argv argc -- val }\n    \\ argv is  (atom fn args...)\n    argv @ { atom }\n    argv cell+ @ { fn }\n    argc 1- { call-argc }\n    call-argc cells allocate throw { call-argv }\n    atom Atom/val   call-argv    1 cells   cmove\n    argv cell+ cell+   call-argv cell+   call-argc 1- cells   cmove\n    call-argv call-argc fn  invoke\n    dup TCO-eval = if drop eval endif { new-val }\n    new-val atom Atom/val !\n    new-val ;;\n\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\ns\\\" (def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" rep 2drop\n\n: repl ( -- )\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\n: main ( -- )\n    mk-args-list { args-list }\n    args-list MalList/count @ 0= if\n        s\" *ARGV*\" MalSymbol. MalList/Empty repl-env env/set\n        repl\n    else\n        args-list MalList/start @ @ { filename }\n        s\" *ARGV*\" MalSymbol. args-list MalList/rest repl-env env/set\n\n        repl-env\n        here s\" load-file\" MalSymbol. , filename , here>MalList\n        eval print\n    endif ;\n\nmain\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step8_macros.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n99999999 constant TCO-eval\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    begin\n        over debug-eval-sym swap env/get-addr ?dup-if\n            @ dup mal-false <> swap mal-nil <> and if\n                .\" EVAL: \" dup pr-str safe-type cr\n            endif\n        endif\n        mal-eval\n        dup TCO-eval =\n    while\n        drop\n    repeat ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  TCO-eval\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke { env list this -- list }\n    env list eval-rest ( argv argc )\n    this invoke ;;\n  extend invoke ( argv argc this -- val )\n    MalNativeFn/xt @ execute ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ns\" concat\" MalSymbol. constant concat-sym\ns\" cons\" MalSymbol. constant cons-sym\ns\" vec\" MalSymbol. constant vec-sym\n\ndefer quasiquote\n\n( If the list has two elements and the first is sym, return the second )\n( element and true, else return the list unchanged and false. )\n: qq_extract_unquote ( list symbol -- form f )\n    over MalList/count @ 2 = if\n        over MalList/start @ tuck @ m= if         ( list start - )\n            cell+ @\n            nip\n            true\n            exit\n        endif\n    endif\n    drop\n    false ;\n\n( Transition function for the following quasiquote folder. )\n: qq_loop ( acc elt -- form )\n    dup mal-type @ MalList = if\n        splice-unquote-sym qq_extract_unquote if\n            here concat-sym , swap , swap , here>MalList\n            exit\n        endif\n    endif\n    quasiquote\n    here cons-sym , swap , swap , here>MalList ;\n\n( Right-fold quasiquoting each element of a list. )\n: qq_foldr ( list -- form )\n    dup MalList/count @ if\n        dup MalList/rest recurse\n        swap MalList/start @ @\n        qq_loop\n    endif ;\n\n: quasiquote0 ( ast -- form )\n    dup mal-type @ case\n    MalList of\n        unquote-sym qq_extract_unquote if\n            ( the work is already done )\n        else\n            qq_foldr\n        endif\n    endof\n    MalVector of\n        MalVector/list @ qq_foldr\n        here vec-sym , swap , here>MalList\n    endof\n    MalSymbol of\n        here quote-sym , swap , here>MalList\n    endof\n    MalMap of\n        here quote-sym , swap , here>MalList\n    endof\n    ( other types are returned unchanged )\n    endcase ;\n' quasiquote0 is quasiquote\n\ndefspecial quasiquote ( env list )\n    MalList/start @ cell+ @ ( ast )\n    quasiquote TCO-eval ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial defmacro! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval\n    asMacro { val }\n    val env env/set\n    val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ TCO-eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @ { start }\n    list MalList/count @ dup 1- { last } 1 ?do\n        env   start i cells + @\n        i last = if\n            TCO-eval\n        else\n            eval drop\n        endif\n    loop ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ TCO-eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ TCO-eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\n: new-user-fn-env { argv argc mal-fn -- env }\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            argc i - { c }\n            c cells allocate throw { start }\n            argv i cells +  start  c cells  cmove\n            f-args i 1+ cells + @ ( more-args-symbol )\n            start c MalList. env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n    env ;\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    mal-fn MalUserFn/is-macro? @ if\n        list MalList/start @ cell+  \\ argv\n        list MalList/count @ 1-     \\ argc\n        mal-fn new-user-fn-env { env }\n        env   mal-fn MalUserFn/body @   eval\n        call-env swap TCO-eval\n    else\n        call-env list eval-rest\n        mal-fn invoke\n    endif ;;\n\n  extend invoke ( argv argc mal-fn )\n    dup { mal-fn } new-user-fn-env { env }\n    env   mal-fn MalUserFn/body @   TCO-eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    false over MalUserFn/is-macro? !\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\ndefcore eval ( argv argc )\n  drop @ repl-env swap eval ;;\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\n: mk-args-list ( -- )\n    here\n    begin\n        next-arg 2dup 0 0 d<> while\n            MalString. ,\n    repeat\n    2drop here>MalList ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: nop ;\n\ndefcore swap! { argv argc -- val }\n    \\ argv is  (atom fn args...)\n    argv @ { atom }\n    argv cell+ @ { fn }\n    argc 1- { call-argc }\n    call-argc cells allocate throw { call-argv }\n    atom Atom/val   call-argv    1 cells   cmove\n    argv cell+ cell+   call-argv cell+   call-argc 1- cells   cmove\n    call-argv call-argc fn  invoke\n    dup TCO-eval = if drop eval endif { new-val }\n    new-val atom Atom/val !\n    new-val ;;\n\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\ns\\\" (def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" rep 2drop\ns\\\" (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" rep 2drop\n\n: repl ( -- )\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\n: main ( -- )\n    mk-args-list { args-list }\n    args-list MalList/count @ 0= if\n        s\" *ARGV*\" MalSymbol. MalList/Empty repl-env env/set\n        repl\n    else\n        args-list MalList/start @ @ { filename }\n        s\" *ARGV*\" MalSymbol. args-list MalList/rest repl-env env/set\n\n        repl-env\n        here s\" load-file\" MalSymbol. , filename , here>MalList\n        eval print\n    endif ;\n\nmain\ncr\nbye\n"
  },
  {
    "path": "impls/forth/step9_try.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n99999999 constant TCO-eval\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    begin\n        over debug-eval-sym swap env/get-addr ?dup-if\n            @ dup mal-false <> swap mal-nil <> and if\n                .\" EVAL: \" dup pr-str safe-type cr\n            endif\n        endif\n        mal-eval\n        dup TCO-eval =\n    while\n        drop\n    repeat ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  TCO-eval\n        else\n            mal-nil\n        endif\n    endif ;;\n  extend invoke { argv argc kw -- val }\n    0   kw   argv @   get\n    ?dup 0= if\n        argc 1 > if\n            argv cell+ @\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke { env list this -- list }\n    env list eval-rest ( argv argc )\n    this invoke ;;\n  extend invoke ( argv argc this -- val )\n    MalNativeFn/xt @ execute ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ns\" concat\" MalSymbol. constant concat-sym\ns\" cons\" MalSymbol. constant cons-sym\ns\" vec\" MalSymbol. constant vec-sym\n\ndefer quasiquote\n\n( If the list has two elements and the first is sym, return the second )\n( element and true, else return the list unchanged and false. )\n: qq_extract_unquote ( list symbol -- form f )\n    over MalList/count @ 2 = if\n        over MalList/start @ tuck @ m= if         ( list start - )\n            cell+ @\n            nip\n            true\n            exit\n        endif\n    endif\n    drop\n    false ;\n\n( Transition function for the following quasiquote folder. )\n: qq_loop ( acc elt -- form )\n    dup mal-type @ MalList = if\n        splice-unquote-sym qq_extract_unquote if\n            here concat-sym , swap , swap , here>MalList\n            exit\n        endif\n    endif\n    quasiquote\n    here cons-sym , swap , swap , here>MalList ;\n\n( Right-fold quasiquoting each element of a list. )\n: qq_foldr ( list -- form )\n    dup MalList/count @ if\n        dup MalList/rest recurse\n        swap MalList/start @ @\n        qq_loop\n    endif ;\n\n: quasiquote0 ( ast -- form )\n    dup mal-type @ case\n    MalList of\n        unquote-sym qq_extract_unquote if\n            ( the work is already done )\n        else\n            qq_foldr\n        endif\n    endof\n    MalVector of\n        MalVector/list @ qq_foldr\n        here vec-sym , swap , here>MalList\n    endof\n    MalSymbol of\n        here quote-sym , swap , here>MalList\n    endof\n    MalMap of\n        here quote-sym , swap , here>MalList\n    endof\n    ( other types are returned unchanged )\n    endcase ;\n' quasiquote0 is quasiquote\n\ndefspecial quasiquote ( env list )\n    MalList/start @ cell+ @ ( ast )\n    quasiquote TCO-eval ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial defmacro! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval\n    asMacro { val }\n    val env env/set\n    val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ TCO-eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @ { start }\n    list MalList/count @ dup 1- { last } 1 ?do\n        env   start i cells + @\n        i last = if\n            TCO-eval\n        else\n            eval drop\n        endif\n    loop ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ TCO-eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ TCO-eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\n: new-user-fn-env { argv argc mal-fn -- env }\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            argc i - { c }\n            c cells allocate throw { start }\n            argv i cells +  start  c cells  cmove\n            f-args i 1+ cells + @ ( more-args-symbol )\n            start c MalList. env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n    env ;\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    mal-fn MalUserFn/is-macro? @ if\n        list MalList/start @ cell+  \\ argv\n        list MalList/count @ 1-     \\ argc\n        mal-fn new-user-fn-env { env }\n        env   mal-fn MalUserFn/body @   eval\n        call-env swap TCO-eval\n    else\n        call-env list eval-rest\n        mal-fn invoke\n    endif ;;\n\n  extend invoke ( argv argc mal-fn )\n    dup { mal-fn } new-user-fn-env { env }\n    env   mal-fn MalUserFn/body @   TCO-eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    false over MalUserFn/is-macro? !\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\n5555555555 constant pre-try\n\ndefspecial try* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    list MalList/count @ 3 < if\n        env arg0 @ eval\n    else\n        pre-try\n        env arg0 @ ['] eval catch ?dup 0= if\n            nip\n        else { errno }\n            begin pre-try = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            arg0 cell+ @ ( list[catch*,sym,form] )\n            MalList/start @ cell+ { catch0 }\n            env MalEnv. { catch-env }\n            catch0 @ exception-object catch-env env/set\n            catch-env  catch0 cell+ @  TCO-eval\n        endif\n    endif ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\ndefcore eval ( argv argc )\n  drop @ repl-env swap eval ;;\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\n: mk-args-list ( -- )\n    here\n    begin\n        next-arg 2dup 0 0 d<> while\n            MalString. ,\n    repeat\n    2drop here>MalList ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: nop ;\n\ndefcore swap! { argv argc -- val }\n    \\ argv is  (atom fn args...)\n    argv @ { atom }\n    argv cell+ @ { fn }\n    argc 1- { call-argc }\n    call-argc cells allocate throw { call-argv }\n    atom Atom/val   call-argv    1 cells   cmove\n    argv cell+ cell+   call-argv cell+   call-argc 1- cells   cmove\n    call-argv call-argc fn  invoke\n    dup TCO-eval = if drop eval endif { new-val }\n    new-val atom Atom/val !\n    new-val ;;\n\ndefcore map ( argv argc -- list )\n    drop dup @ swap cell+ @ to-list { fn list }\n    here\n    list MalList/start @   list MalList/count @  cells   over + swap   +do\n        i 1 fn invoke\n        dup TCO-eval = if drop eval endif\n        ,\n    cell +loop\n    here>MalList ;;\n\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\ns\\\" (def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" rep 2drop\ns\\\" (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" rep 2drop\n\n: repl ( -- )\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\n: main ( -- )\n    mk-args-list { args-list }\n    args-list MalList/count @ 0= if\n        s\" *ARGV*\" MalSymbol. MalList/Empty repl-env env/set\n        repl\n    else\n        args-list MalList/start @ @ { filename }\n        s\" *ARGV*\" MalSymbol. args-list MalList/rest repl-env env/set\n\n        repl-env\n        here s\" load-file\" MalSymbol. , filename , here>MalList\n        eval print\n    endif ;\n\nmain\ncr\nbye\n"
  },
  {
    "path": "impls/forth/stepA_mal.fs",
    "content": "require reader.fs\nrequire printer.fs\nrequire core.fs\n\ncore MalEnv. constant repl-env\n\n99999999 constant TCO-eval\n\n: read read-str ;\ns\" DEBUG-EVAL\" MalSymbol. constant debug-eval-sym\n: eval ( env obj )\n    begin\n        over debug-eval-sym swap env/get-addr ?dup-if\n            @ dup mal-false <> swap mal-nil <> and if\n                .\" EVAL: \" dup pr-str safe-type cr\n            endif\n        endif\n        mal-eval\n        dup TCO-eval =\n    while\n        drop\n    repeat ;\n: print\n    \\ .\" Type: \" dup mal-type @ type-name safe-type cr\n    pr-str ;\n\nMalDefault extend mal-eval nip ;; drop \\ By default, evalutate to yourself\n\nMalKeyword\n  extend eval-invoke { env list kw -- val }\n    0   kw   env list MalList/start @ cell+ @ eval   get\n    ?dup 0= if\n        \\ compute not-found value\n        list MalList/count @ 1 > if\n            env  list MalList/start @ 2 cells + @  TCO-eval\n        else\n            mal-nil\n        endif\n    endif ;;\n  extend invoke { argv argc kw -- val }\n    0   kw   argv @   get\n    ?dup 0= if\n        argc 1 > if\n            argv cell+ @\n        else\n            mal-nil\n        endif\n    endif ;;\ndrop\n\n\\ eval all but the first item of list\n: eval-rest { env list -- argv argc }\n    list MalList/start @ cell+ { expr-start }\n    list MalList/count @ 1- { argc }\n    argc cells allocate throw { target }\n    argc 0 ?do\n        env expr-start i cells + @ eval\n        target i cells + !\n    loop\n    target argc ;\n\nMalNativeFn\n  extend eval-invoke { env list this -- list }\n    env list eval-rest ( argv argc )\n    this invoke ;;\n  extend invoke ( argv argc this -- val )\n    MalNativeFn/xt @ execute ;;\ndrop\n\nSpecialOp\n  extend eval-invoke ( env list this -- list )\n    SpecialOp/xt @ execute ;;\ndrop\n\n: install-special ( symbol xt )\n    SpecialOp. repl-env env/set ;\n\n: defspecial\n    parse-allot-name MalSymbol.\n    ['] install-special\n    :noname\n    ;\n\ndefspecial quote ( env list -- form )\n    nip MalList/start @ cell+ @ ;;\n\ns\" concat\" MalSymbol. constant concat-sym\ns\" cons\" MalSymbol. constant cons-sym\ns\" vec\" MalSymbol. constant vec-sym\n\ndefer quasiquote\n\n( If the list has two elements and the first is sym, return the second )\n( element and true, else return the list unchanged and false. )\n: qq_extract_unquote ( list symbol -- form f )\n    over MalList/count @ 2 = if\n        over MalList/start @ tuck @ m= if         ( list start - )\n            cell+ @\n            nip\n            true\n            exit\n        endif\n    endif\n    drop\n    false ;\n\n( Transition function for the following quasiquote folder. )\n: qq_loop ( acc elt -- form )\n    dup mal-type @ MalList = if\n        splice-unquote-sym qq_extract_unquote if\n            here concat-sym , swap , swap , here>MalList\n            exit\n        endif\n    endif\n    quasiquote\n    here cons-sym , swap , swap , here>MalList ;\n\n( Right-fold quasiquoting each element of a list. )\n: qq_foldr ( list -- form )\n    dup MalList/count @ if\n        dup MalList/rest recurse\n        swap MalList/start @ @\n        qq_loop\n    endif ;\n\n: quasiquote0 ( ast -- form )\n    dup mal-type @ case\n    MalList of\n        unquote-sym qq_extract_unquote if\n            ( the work is already done )\n        else\n            qq_foldr\n        endif\n    endof\n    MalVector of\n        MalVector/list @ qq_foldr\n        here vec-sym , swap , here>MalList\n    endof\n    MalSymbol of\n        here quote-sym , swap , here>MalList\n    endof\n    MalMap of\n        here quote-sym , swap , here>MalList\n    endof\n    ( other types are returned unchanged )\n    endcase ;\n' quasiquote0 is quasiquote\n\ndefspecial quasiquote ( env list )\n    MalList/start @ cell+ @ ( ast )\n    quasiquote TCO-eval ;;\n\ndefspecial def! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval dup { val } ( key val )\n    env env/set val ;;\n\ndefspecial defmacro! { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    arg0 @ ( key )\n    env arg0 cell+ @ eval\n    asMacro { val }\n    val env env/set\n    val ;;\n\ndefspecial let* { old-env list -- val }\n    old-env MalEnv. { env }\n    list MalList/start @ cell+ dup { arg0 }\n    @ to-list\n    dup MalList/start @ { bindings-start } ( list )\n    MalList/count @ 0 +do\n        bindings-start i cells + dup @ swap cell+ @ ( sym expr )\n        env swap eval\n        env env/set\n    2 +loop\n    env arg0 cell+ @ TCO-eval\n    \\ TODO: dec refcount of env\n    ;;\n\ndefspecial do { env list -- val }\n    list MalList/start @ { start }\n    list MalList/count @ dup 1- { last } 1 ?do\n        env   start i cells + @\n        i last = if\n            TCO-eval\n        else\n            eval drop\n        endif\n    loop ;;\n\ndefspecial if { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    env arg0 @ eval ( test-val )\n    dup mal-false = if\n        drop -1\n    else\n        mal-nil =\n    endif\n    if\n        \\ branch to false\n        list MalList/count @ 3 > if\n            env arg0 cell+ cell+ @ TCO-eval\n        else\n            mal-nil\n        endif\n    else\n        \\ branch to true\n        env arg0 cell+ @ TCO-eval\n    endif ;;\n\ns\" &\" MalSymbol. constant &-sym\n\n: new-user-fn-env { argv argc mal-fn -- env }\n    mal-fn MalUserFn/formal-args @ { f-args-list }\n    mal-fn MalUserFn/env @ MalEnv. { env }\n\n    f-args-list MalList/start @ { f-args }\n    f-args-list MalList/count @ ?dup 0= if else\n        \\ pass empty list for last arg, unless overridden below\n        1- cells f-args + @ MalList new env env/set\n    endif\n    argc 0 ?do\n        f-args i cells + @\n        dup &-sym m= if\n            drop\n            argc i - { c }\n            c cells allocate throw { start }\n            argv i cells +  start  c cells  cmove\n            f-args i 1+ cells + @ ( more-args-symbol )\n            start c MalList. env env/set\n            leave\n        endif\n        argv i cells + @\n        env env/set\n    loop\n    env ;\n\nMalUserFn\n  extend eval-invoke { call-env list mal-fn -- list }\n    mal-fn MalUserFn/is-macro? @ if\n        list MalList/start @ cell+  \\ argv\n        list MalList/count @ 1-     \\ argc\n        mal-fn new-user-fn-env { env }\n        env   mal-fn MalUserFn/body @   eval\n        call-env swap TCO-eval\n    else\n        call-env list eval-rest\n        mal-fn invoke\n    endif ;;\n\n  extend invoke ( argv argc mal-fn )\n    dup { mal-fn } new-user-fn-env { env }\n    env   mal-fn MalUserFn/body @   TCO-eval ;;\ndrop\n\ndefspecial fn* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    MalUserFn new\n    false over MalUserFn/is-macro? !\n    env over MalUserFn/env !\n    arg0 @ to-list over MalUserFn/formal-args !\n    arg0 cell+ @ over MalUserFn/body ! ;;\n\n5555555555 constant pre-try\n\ndefspecial try* { env list -- val }\n    list MalList/start @ cell+ { arg0 }\n    list MalList/count @ 3 < if\n        env arg0 @ eval\n    else\n        pre-try\n        env arg0 @ ['] eval catch ?dup 0= if\n            nip\n        else { errno }\n            begin pre-try = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            arg0 cell+ @ ( list[catch*,sym,form] )\n            MalList/start @ cell+ { catch0 }\n            env MalEnv. { catch-env }\n            catch0 @ exception-object catch-env env/set\n            catch-env  catch0 cell+ @  TCO-eval\n        endif\n    endif ;;\n\ndefspecial . { env coll -- rtn-list }\n    depth { old-depth }\n    coll to-list dup MalList/count @ swap MalList/start @ { count start }\n    count cells start +  start cell+  +do\n        env i @ eval as-native\n    cell +loop ;;\n\nMalSymbol\n  extend mal-eval { env sym -- val }\n    sym env env/get-addr\n    dup 0= if\n        drop\n        0 0 s\" ' not found\" sym pr-str s\" '\" ...throw-str\n    else\n        @\n    endif ;;\ndrop\n\n: eval-ast { env list -- list }\n    here\n    list MalList/start @ { expr-start }\n    list MalList/count @ 0 ?do\n        env expr-start i cells + @ eval ,\n    loop\n    here>MalList ;\n\nMalList\n  extend mal-eval { env list -- val }\n    list MalList/count @ 0= if\n        list\n    else\n        env list MalList/start @ @ eval\n        env list rot eval-invoke\n    endif ;;\ndrop\n\nMalVector\n  extend mal-eval ( env vector -- vector )\n    MalVector/list @ eval-ast\n    MalVector new swap over MalVector/list ! ;;\ndrop\n\nMalMap\n  extend mal-eval ( env map -- map )\n    MalMap/list @ eval-ast\n    MalMap new swap over MalMap/list ! ;;\ndrop\n\ndefcore eval ( argv argc )\n  drop @ repl-env swap eval ;;\n\n: rep ( str-addr str-len -- str-addr str-len )\n    read\n    repl-env swap eval\n    print ;\n\n: mk-args-list ( -- )\n    here\n    begin\n        next-arg 2dup 0 0 d<> while\n            MalString. ,\n    repeat\n    2drop here>MalList ;\n\ncreate buff 128 allot\n77777777777 constant stack-leak-detect\n\n: nop ;\n\ndefcore swap! { argv argc -- val }\n    \\ argv is  (atom fn args...)\n    argv @ { atom }\n    argv cell+ @ { fn }\n    argc 1- { call-argc }\n    call-argc cells allocate throw { call-argv }\n    atom Atom/val   call-argv    1 cells   cmove\n    argv cell+ cell+   call-argv cell+   call-argc 1- cells   cmove\n    call-argv call-argc fn  invoke\n    dup TCO-eval = if drop eval endif { new-val }\n    new-val atom Atom/val !\n    new-val ;;\n\ndefcore map ( argv argc -- list )\n    drop dup @ swap cell+ @ to-list { fn list }\n    here\n    list MalList/start @   list MalList/count @  cells   over + swap   +do\n        i 1 fn invoke\n        dup TCO-eval = if drop eval endif\n        ,\n    cell +loop\n    here>MalList ;;\n\ns\\\" (def! *host-language* \\\"forth\\\")\" rep 2drop\ns\\\" (def! not (fn* (x) (if x false true)))\" rep 2drop\ns\\\" (def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" rep 2drop\ns\\\" (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" rep 2drop\n\n: repl ( -- )\n    s\\\" (println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\" rep 2drop\n    begin\n      .\" user> \"\n      stack-leak-detect\n      buff 128 stdin read-line throw\n    while ( num-bytes-read )\n      dup 0 <> if\n        buff swap ( str-addr str-len )\n        ['] rep\n        \\ execute ['] nop \\ uncomment to see stack traces\n        catch ?dup 0= if\n            safe-type cr\n            stack-leak-detect <> if .\" --stack leak--\" cr endif\n        else { errno }\n            begin stack-leak-detect = until\n            errno 1 <> if\n                s\" forth-errno\" MalKeyword. errno MalInt. MalMap/Empty assoc\n                to exception-object\n            endif\n            .\" Uncaught exception: \"\n            exception-object pr-str safe-type cr\n        endif\n      endif\n    repeat ;\n\n: main ( -- )\n    mk-args-list { args-list }\n    args-list MalList/count @ 0= if\n        s\" *ARGV*\" MalSymbol. MalList/Empty repl-env env/set\n        repl\n    else\n        args-list MalList/start @ @ { filename }\n        s\" *ARGV*\" MalSymbol. args-list MalList/rest repl-env env/set\n\n        repl-env\n        here s\" load-file\" MalSymbol. , filename , here>MalList\n        eval print\n    endif ;\n\nmain\ncr\nbye\n"
  },
  {
    "path": "impls/forth/str.fs",
    "content": ": safe-type ( str-addr str-len -- )\n    dup 256 > if\n        drop 256 type .\" ...<lots more>\"\n    else\n        type\n    endif ;\n\n\\ === mutable string buffer === /\n\\ string buffer that maintains an allocation larger than the current\n\\ string size.  When appending would cause the string size exceed the\n\\ current allocation, resize is used to double the allocation. The\n\\ current allocation is not stored anywhere, but computed based on\n\\ current string size or str-base-size, whichever is larger.\n64 constant str-base-size\n\n: new-str ( -- addr length )\n  str-base-size allocate throw 0 ;\n\n: round-up ( n -- n )\n  2\n  begin\n    1 lshift 2dup <\n  until\n  nip ;\n\n: str-append { buf-addr buf-str-len str-addr str-len }\n  buf-str-len str-len +\n  { new-len }\n  new-len str-base-size >= if\n    buf-str-len new-len xor buf-str-len > if\n      buf-addr new-len round-up resize throw\n      to buf-addr\n    endif\n  endif\n  str-addr buf-addr buf-str-len + str-len cmove\n  buf-addr new-len ;\n\n\\ define a-space, to append a space char to a string\nbl c,\nhere constant space-str\n: a-space space-str 1 str-append ;\n\n: str-append-char ( buf-addr buf-str-len char -- buf-addr buf-str-len )\n    pad ! pad 1 str-append ;\n\n\\ from gforth docs, there named 'my-.'\n: int>str ( num -- str-addr str-len )\n    \\ handling negatives.. behaves like Standard .\n    s>d            \\ convert to signed double\n    swap over dabs \\ leave sign byte followed by unsigned double\n    <<#            \\ start conversion\n    #s             \\ convert all digits\n    rot sign       \\ get at sign byte, append \"-\" if needed\n    #>             \\ complete conversion\n    #>> ;          \\ release hold area\n\ndefer MalString.\n\n: ...str\n    new-str\n    begin\n        2swap\n        over 0 <>\n    while\n        str-append\n    repeat\n    2drop MalString. ;\n\nnil value exception-object\n\n: ...throw-str\n    ...str to exception-object\n    1 throw ;\n"
  },
  {
    "path": "impls/forth/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/forth/tests/stepA_mal.mal",
    "content": ";; Basic interop\n(. 5 'MalInt.)\n;=>5\n(. 11 31 '+ 'MalInt.)\n;=>42\n(. \"greetings\" 'MalString.)\n;=>\"greetings\"\n(. \"hello\" 'type 'cr 'mal-nil)\n;/hello\n;=>nil\n\n;; Interop on non-literals\n(. (+ 15 27) 'MalInt.)\n;=>42\n(let* [a 17] (. a 25 '+ 'MalInt.))\n;=>42\n(let* [a \"hello\"] (. a 1 '- 'MalString.))\n;=>\"hell\"\n\n;; Use of annoyingly-named forth words\n(. 1 'MalInt. (symbol \",\") 'here (symbol \"@\"))\n;=>1\n(let* (i 'MalInt.) (. 5 i))\n;=>5\n(let* (comma (symbol \",\") fetch (symbol \"@\")) (. 'here 42 'MalInt. comma fetch))\n;=>42\n\n;; Multiple .-forms interacting via heap memory and mal locals\n(def! string-parts (fn* (s) (. s 'MalInt. 'swap 'MalInt. 'here '-rot (symbol \",\") (symbol \",\") 'here>MalList)))\n(first (rest (string-parts \"sketchy\")))\n;=>7\n(def! prn-chars (fn* (start count) (if (> count 0) (do (prn (. start 1 'MalString.)) (prn-chars (+ start 1) (- count 1))))))\n(let* (msg (string-parts \"sketchy\")) (prn-chars (first msg) (first (rest msg))))\n;/\"s\"\n;/\"k\"\n;/\"e\"\n;/\"t\"\n;/\"c\"\n;/\"h\"\n;/\"y\"\n;=>nil\n"
  },
  {
    "path": "impls/forth/types.fs",
    "content": "require str.fs\n\n\\ === sorted-array === /\n\\ Here are a few utility functions useful for creating and maintaining\n\\ the deftype* method tables. The keys array is kept in sorted order,\n\\ and the methods array is maintained in parallel so that an index into\n\\ one corresponds to an index in the other.\n\n\\ Search a sorted array for key, returning the index of where it was\n\\ found. If key is not in the array, return the index where it would\n\\ be if added.\n: array-find { a-length a-addr key -- index found? }\n  0 a-length           ( start end )\n  begin\n    \\ cr 2dup . .\n    2dup + 2 / dup     ( start end middle middle )\n    cells a-addr + @   ( start end middle mid-val )\n    dup key < if\n      drop rot         ( end middle start )\n      2dup = if\n        2drop dup      ( end end )\n      else\n        drop swap      ( middle end )\n      endif\n    else\n      key > if         ( start end middle )\n        nip            ( start middle )\n      else\n        -rot 2drop dup ( middle middle )\n      endif\n    endif\n  2dup = until\n  dup a-length = if\n      drop false\n  else\n      cells a-addr + @ key =\n  endif ;\n\n\\ Create a new array, one cell in length, initialized the provided value\n: new-array { value -- array }\n  cell allocate throw value over ! ;\n\n\\ Resize a heap-allocated array to be one cell longer, inserting value\n\\ at idx, and shifting the tail of the array as necessary. Returns the\n\\ (possibly new) array address\n: array-insert { old-array-length old-array idx value -- array }\n  old-array old-array-length 1+ cells resize throw\n  { a }\n  a idx cells +   dup cell+   old-array-length idx - cells   cmove>\n  value a idx cells + !\n  a\n  ;\n\n\n\\ === deftype* -- protocol-enabled structs === /\n\\ Each type has MalTypeType% struct allocated on the stack, with\n\\ mutable fields pointing to all class-shared resources, specifically\n\\ the data needed to allocate new instances, and the table of protocol\n\\ methods that have been extended to the type.\n\\ Use 'deftype*' to define a new type, and 'new' to create new\n\\ instances of that type.\n\nstruct\n  cell% field mal-type\n  cell% field mal-meta\n  \\ cell% field ref-count \\ Ha, right.\nend-struct MalType%\n\nstruct\n  cell% 2 * field MalTypeType-struct\n  cell% field MalTypeType-methods\n  cell% field MalTypeType-method-keys\n  cell% field MalTypeType-method-vals\n  cell% field MalTypeType-name-addr\n  cell% field MalTypeType-name-len\nend-struct MalTypeType%\n\n: new ( MalTypeType -- obj )\n  dup MalTypeType-struct 2@ %allocate throw ( MalTypeType obj ) \\ create struct\n  dup -rot mal-type !                       ( obj ) \\ set struct's type pointer to this type\n  nil over mal-meta !\n  ;\n\n: deftype* ( struct-align struct-len -- MalTypeType )\n  MalTypeType% %allot                      ( s-a s-l MalTypeType )\n  dup 2swap rot                            ( MalTypeType s-a s-l MalTypeType )\n  MalTypeType-struct 2!                    ( MalTypeType ) \\ store struct info\n  dup MalTypeType-methods     0   swap !   ( MalTypeType )\n  dup MalTypeType-method-keys nil swap !   ( MalTypeType )\n  dup MalTypeType-method-vals nil swap !   ( MalTypeType )\n  dup MalTypeType-name-len    0   swap !   ( MalTypeType )\n  ;\n\n\\ parse-name uses temporary space, so copy into dictionary stack:\n: parse-allot-name { -- new-str-addr str-len }\n    parse-name { str-addr str-len }\n    here { new-str-addr } str-len allot\n    str-addr new-str-addr str-len cmove\n    new-str-addr str-len ;\n\n: deftype ( struct-align struct-len R:type-name -- )\n    parse-allot-name { name-addr name-len }\n\n    \\ allot and initialize type structure\n    deftype* { mt }\n    name-addr mt MalTypeType-name-addr !\n    name-len  mt MalTypeType-name-len  !\n    \\ .\" Defining \" mt MalTypeType-name-addr @ mt MalTypeType-name-len @ type cr\n    mt name-addr name-len nextname 1 0 const-does> ;\n\n: type-name ( mal-type )\n    dup  MalTypeType-name-addr @ ( mal-type name-addr )\n    swap MalTypeType-name-len @ ( name-addr name-len )\n    ;\n\nMalType% deftype MalDefault\n\n\\ nil type and instance to support extending protocols to it\nMalType% deftype MalNil   MalNil   new constant mal-nil\nMalType% deftype MalTrue  MalTrue  new constant mal-true\nMalType% deftype MalFalse MalFalse new constant mal-false\n\n: mal-bool\n    0= if mal-false else mal-true endif ;\n\n: not-object? ( obj -- bool )\n    dup 7 and 0 <> if\n        drop true\n    else\n        1000000 <\n    endif ;\n\n\\ === protocol methods === /\n\nstruct\n    cell% field call-site/type\n    cell% field call-site/xt\nend-struct call-site%\n\n\\ Used by protocol methods to find the appropriate implementation of\n\\ themselves for the given object, and then execute that implementation.\n: execute-method { obj pxt call-site -- }\n  obj not-object? if\n      0 0 obj int>str s\" ' on non-object: \" pxt >name name>string\n      s\" Refusing to invoke protocol fn '\" ...throw-str\n  endif\n  \\ .\" Calling '\" pxt >name name>string type .\" ' on \" obj mal-type @ type-name type .\" , cs \" call-site .\n\n  obj mal-type @ ( type )\n  dup call-site call-site/type @ = if\n      \\ .\" hit!\" cr\n      drop\n      call-site call-site/xt @\n  else\n      \\ .\" miss!\" cr\n      dup MalTypeType-methods 2@ swap ( type methods method-keys )\n      dup 0= if \\ No protocols extended to this type; check for a default\n          2drop drop MalDefault MalTypeType-methods 2@ swap\n      endif\n\n      pxt array-find ( type idx found? )\n      dup 0= if \\ No implementation found for this method; check for a default\n          2drop drop MalDefault dup MalTypeType-methods 2@ swap\n          pxt array-find ( type idx found? )\n      endif\n      0= if ( type idx )\n          2drop\n          0 0 s\" '\" obj mal-type @ type-name s\" ' extended to type '\"\n          pxt >name name>string s\" No protocol fn '\" ...throw-str\n      endif\n\n      cells over MalTypeType-method-vals @ + @ ( type xt )\n      swap call-site call-site/type ! ( xt )\n      dup call-site call-site/xt ! ( xt )\n  endif\n  obj swap execute ;\n\n\\ Extend a type with a protocol method. This mutates the MalTypeType\n\\ object that represents the MalType being extended.\n: extend-method* { type pxt ixt -- type }\n  \\ .\" Extend '\" pxt dup . >name name>string safe-type .\" ' to \" type type-name safe-type .\" , \"\n  \\ type MalTypeType-methods 2@ ( method-keys methods )\n  \\   0 ?do\n  \\       dup i cells + @ >name name>string safe-type .\" , \"\n  \\       \\ dup i cells + @ .\n  \\   loop\n  \\   drop cr\n\n  type MalTypeType-methods 2@ swap ( methods method-keys )\n  dup 0= if \\ no protocols extended to this type\n    2drop\n    1 type MalTypeType-methods !\n    pxt new-array type MalTypeType-method-keys !\n    ixt new-array type MalTypeType-method-vals !\n  else\n    pxt array-find { idx found? }\n    found? if \\ overwrite\n      .\" Warning: overwriting protocol method implementation '\"\n        pxt >name name>string safe-type .\" ' on \" type type-name safe-type .\" , \" idx . found? . cr\n\n      type MalTypeType-method-vals @ idx cells + ixt !\n    else \\ resize\n      type MalTypeType-methods dup @ 1+ dup rot ! ( new-count )\n      1- dup type MalTypeType-method-keys @ idx pxt array-insert ( old-count new-array )\n        type MalTypeType-method-keys ! ( old-count )\n      type MalTypeType-method-vals @ idx ixt array-insert ( new-array )\n        type MalTypeType-method-vals !\n    endif\n  endif\n  type\n  ;\n\n\n\\ Define a new protocol function.  For example:\n\\   def-protocol-method pr-str\n\\ When called as above, defines a new word 'pr-str' and stores there its\n\\ own xt (known as pxt). When a usage of pr-str is compiled, it\n\\ allocates a call-site object on the heap and injects a reference to\n\\ both that and the pxt into the compilation, along with a call to\n\\ execute-method. Thus when pr-str runs, execute-method can check the\n\\ call-site object to see if the type of the target object is the same\n\\ as the last call for this site. If so, it executes the implementation\n\\ immediately. Otherwise, it searches the target type's method list and\n\\ if necessary MalDefault's method list. If an implementation of pxt is\n\\ found, it is cached in the call-site, and then executed.\n: make-call-site { pxt -- }\n    pxt postpone literal \\ transfer pxt into call site\n    call-site% %allocate throw dup postpone literal \\ allocate call-site, push reference\n    \\ dup .\" Make cs '\" pxt >name name>string type .\" ' \" . cr\n    0 swap call-site/type !\n    postpone execute-method ;\n\n: def-protocol-method ( parse: name -- )\n    : latestxt postpone literal postpone make-call-site postpone ; immediate\n    ;\n\n: extend ( type -- type pxt install-xt <noname...>)\n    parse-name find-name name>int ( type pxt )\n    ['] extend-method*\n    :noname\n    ;\n\n: ;; ( type pxt <noname...> -- type )\n    [compile] ; ( type pxt install-xt ixt )\n    swap execute\n    ; immediate\n\n(\n\\ These whole-protocol names are only needed for 'satisfies?':\nprotocol IPrintable\n  def-protocol-method pr-str\nend-protocol\n\nMalList IPrintable extend\n  ' pr-str :noname drop s\" <unprintable>\" ; extend-method*\n\n  extend-method pr-str\n    drop s\" <unprintable>\" ;;\nend-extend\n)\n\n\\ === Mal types and protocols === /\n\ndef-protocol-method conj ( obj this -- this )\ndef-protocol-method seq ( obj -- mal-list|nil )\ndef-protocol-method assoc ( k v this -- this )\ndef-protocol-method dissoc ( k this -- this )\ndef-protocol-method get ( not-found k this -- value )\ndef-protocol-method mal= ( a b -- bool )\ndef-protocol-method as-native ( obj --  )\n\ndef-protocol-method to-list ( obj -- mal-list )\ndef-protocol-method empty? ( obj -- mal-bool )\ndef-protocol-method mal-count ( obj -- mal-int )\ndef-protocol-method sequential? ( obj -- mal-bool )\ndef-protocol-method get-map-hint ( obj -- hint )\ndef-protocol-method set-map-hint! ( hint obj -- )\n\n\n\\ Fully evalutate any Mal object:\ndef-protocol-method mal-eval ( env ast -- val )\n\n\\ Invoke an object, given whole env and unevaluated argument forms:\ndef-protocol-method eval-invoke ( env list obj -- ... )\n\n\\ Invoke a function, given parameter values\ndef-protocol-method invoke ( argv argc mal-fn -- ... )\n\n\n: m= ( a b -- bool )\n    2dup = if\n        2drop true\n    else\n        mal=\n    endif ;\n\n\nMalType%\n  cell% field MalInt/int\ndeftype MalInt\n\n: MalInt. { int -- mal-int }\n    MalInt new dup MalInt/int int swap ! ;\n\nMalInt\n  extend mal= ( other this -- bool )\n    over mal-type @ MalInt = if\n        MalInt/int @ swap MalInt/int @ =\n    else\n        2drop 0\n    endif ;;\n\n  extend as-native ( mal-int -- int )\n    MalInt/int @ ;;\ndrop\n\n\nMalType%\n  cell% field MalList/count\n  cell% field MalList/start\ndeftype MalList\n\n: MalList. ( start count -- mal-list )\n    MalList new\n    swap over MalList/count ! ( start list )\n    swap over MalList/start ! ( list ) ;\n\n: here>MalList ( old-here -- mal-list )\n    here over - { bytes } ( old-here )\n    MalList new bytes ( old-here mal-list bytes )\n    allocate throw dup { target } over MalList/start ! ( old-here mal-list )\n    bytes cell / over MalList/count ! ( old-here mal-list )\n    swap target bytes cmove ( mal-list )\n    0 bytes - allot \\ pop list contents from dictionary stack\n    ;\n\n: MalList/concat ( list-of-lists )\n    dup MalList/start @ swap MalList/count @ { lists argc }\n    0   lists argc cells +  lists  +do ( count )\n        i @ to-list MalList/count @ +\n    cell +loop { count }\n    count cells allocate throw { start }\n    start   lists argc cells +  lists  +do ( target )\n        i @ to-list MalList/count @ cells  2dup  i @ to-list MalList/start @  -rot  ( target bytes src target bytes )\n        cmove ( target bytes )\n        + ( new-target )\n    cell +loop\n    drop start count MalList. ;\n\nMalList\n  extend to-list ;;\n  extend sequential? drop mal-true ;;\n  extend conj { elem old-list -- list }\n    old-list MalList/count @ 1+ { new-count }\n    new-count cells allocate throw { new-start }\n    elem new-start !\n    new-count 1 > if\n      old-list MalList/start @   new-start cell+   new-count 1- cells  cmove\n    endif\n    new-start new-count MalList. ;;\n  extend seq\n    dup MalList/count @ 0= if\n        drop mal-nil\n    endif ;;\n  extend empty? MalList/count @ 0= mal-bool ;;\n  extend mal-count MalList/count @ MalInt. ;;\n  extend mal=\n    over mal-nil = if\n        2drop false\n    else\n        swap to-list dup 0= if\n            nip\n        else\n            2dup MalList/count @ swap MalList/count @ over = if ( list-a list-b count )\n                -rot MalList/start @ swap MalList/start @ { start-b start-a }\n                true swap ( return-val count )\n                0 ?do\n                    start-a i cells + @\n                    start-b i cells + @\n                    m= if else\n                        drop false leave\n                    endif\n                loop\n            else\n                drop 2drop false\n            endif\n        endif\n    endif ;;\ndrop\n\nMalList new 0 over MalList/count ! constant MalList/Empty\n\n: MalList/rest { list -- list }\n    list MalList/start @   cell+\n    list MalList/count @   1-\n    MalList. ;\n\n\nMalType%\n  cell% field MalVector/list\ndeftype MalVector\n\nMalVector\n  extend sequential? drop mal-true ;;\n  extend to-list\n    MalVector/list @ ;;\n  extend empty?\n    MalVector/list @\n    MalList/count @ 0= mal-bool ;;\n  extend mal-count\n    MalVector/list @\n    MalList/count @ MalInt. ;;\n  extend mal=\n    MalVector/list @ swap m= ;;\n  extend conj\n    MalVector/list @ { elem old-list }\n    old-list MalList/count @ { old-count }\n    old-count 1+ cells allocate throw { new-start }\n    elem new-start old-count cells + !\n    old-list MalList/start @   new-start old-count cells  cmove\n    new-start   old-count 1+  MalList.\n    MalVector new swap\n    over MalVector/list ! ;;\n  extend seq\n    MalVector/list @ seq ;;\ndrop\n\nMalType%\n  cell% field MalMap/list\ndeftype MalMap\n\nMalMap new MalList/Empty over MalMap/list ! constant MalMap/Empty\n\n: MalMap/get-addr ( k map -- addr-or-nil )\n    MalMap/list @\n    dup MalList/start @\n    swap MalList/count @ { k start count }\n    true \\ need to search?\n    k get-map-hint { hint-idx }\n    hint-idx -1 <> if\n        hint-idx count < if\n            hint-idx cells start + { key-addr }\n            key-addr @ k m= if\n                key-addr cell+\n                nip false\n            endif\n        endif\n    endif\n    if \\ search\n        nil ( addr )\n        count cells start +  start  +do\n            i @ k m= if\n                drop i\n                dup start - cell / k set-map-hint!\n                cell+ leave\n            endif\n        [ 2 cells ] literal +loop\n    endif ;\n\nMalMap\n  extend conj ( kv map -- map )\n    MalMap/list @ \\ get list\n    over MalList/start @ cell+ @ swap conj \\ add value\n    swap MalList/start @ @ swap conj \\ add key\n    MalMap new dup -rot MalMap/list ! \\ put back in map\n    ;;\n  extend assoc ( k v map -- map )\n    MalMap/list @ \\ get list\n    conj conj\n    MalMap new tuck MalMap/list ! \\ put back in map\n    ;;\n  extend dissoc { k map -- map }\n    map MalMap/list @\n    dup MalList/start @ swap MalList/count @ { start count }\n    map \\ return original if key not found\n    count 0 +do\n        start i cells + @ k mal= if\n            drop here\n            start i MalList. ,\n            start i 2 + cells +  count i - 2 - MalList. ,\n            here>MalList MalList/concat\n            MalMap new dup -rot MalMap/list ! \\ put back in map\n        endif\n    2 +loop ;;\n  extend get ( not-found k map -- value )\n    MalMap/get-addr ( not-found addr-or-nil )\n    dup 0= if drop else nip @ endif ;;\n  extend empty?\n    MalMap/list @\n    MalList/count @ 0= mal-bool ;;\n  extend mal-count\n    MalMap/list @\n    MalList/count @ 2 / MalInt. ;;\n  extend mal= { b a -- bool }\n    b mal-type @ MalMap = if\n        a MalMap/list @ MalList/count @ { a-count }\n        b MalMap/list @ MalList/count @ { b-count }\n        a-count b-count = if\n            a MalMap/list @ MalList/start @ { a-start }\n            true ( return-val )\n            a-count 0 +do\n                a-start i cells + @ ( return-val key )\n                dup a MalMap/get-addr swap b MalMap/get-addr ( return-val a-val-addr b-val-addr )\n                dup 0= if\n                    drop 2drop false leave\n                else\n                    @ swap @ ( return-val b-val a-val )\n                    m= if else\n                        drop false leave\n                    endif\n                endif\n            2 +loop\n        else\n            false\n        endif\n    else\n        false\n    endif ;;\ndrop\n\n\\ Examples of extending existing protocol methods to existing type\nMalDefault\n  extend conj   ( obj this -- this )\n    nip ;;\n  extend to-list drop 0 ;;\n  extend empty? drop mal-true ;;\n  extend sequential? drop mal-false ;;\n  extend mal= = ;;\n  extend get-map-hint drop -1 ;;\n  extend set-map-hint! 2drop ;;\ndrop\n\nMalNil\n  extend conj ( item nil -- mal-list )\n    drop MalList/Empty conj ;;\n  extend seq drop mal-nil ;;\n  extend as-native drop nil ;;\n  extend get 2drop ;;\n  extend to-list drop MalList/Empty ;;\n  extend empty? drop mal-true ;;\n  extend mal-count drop 0 MalInt. ;;\n  extend mal= drop mal-nil = ;;\ndrop\n\nMalType%\n  cell% field MalSymbol/sym-addr\n  cell% field MalSymbol/sym-len\n  cell% field MalSymbol/map-hint\ndeftype MalSymbol\n\n: MalSymbol. { str-addr str-len -- mal-sym }\n    MalSymbol new { sym }\n    str-addr sym MalSymbol/sym-addr !\n    str-len  sym MalSymbol/sym-len !\n    -1       sym MalSymbol/map-hint !\n    sym ;\n\n: unpack-sym ( mal-string -- addr len )\n    dup MalSymbol/sym-addr @\n    swap MalSymbol/sym-len @ ;\n\nMalSymbol\n  extend mal= ( other this -- bool )\n    over mal-type @ MalSymbol = if\n        unpack-sym rot unpack-sym str=\n    else\n        2drop 0\n    endif ;;\n  extend get-map-hint MalSymbol/map-hint @ ;;\n  extend set-map-hint! MalSymbol/map-hint ! ;;\n  extend as-native ( this )\n    unpack-sym evaluate ;;\ndrop\n\nMalType%\n  cell% field MalKeyword/str-addr\n  cell% field MalKeyword/str-len\ndeftype MalKeyword\n\n: unpack-keyword ( mal-keyword -- addr len )\n    dup MalKeyword/str-addr @\n    swap MalKeyword/str-len @ ;\n\nMalKeyword\n  extend mal= ( other this -- bool )\n    over mal-type @ MalKeyword = if\n        unpack-keyword rot unpack-keyword str=\n    else\n        2drop 0\n    endif ;;\n  ' as-native ' unpack-keyword extend-method*\ndrop\n\n: MalKeyword. { str-addr str-len -- mal-keyword }\n    MalKeyword new { kw }\n    str-addr kw MalKeyword/str-addr !\n    str-len  kw MalKeyword/str-len  !\n    kw ;\n\nMalType%\n  cell% field MalString/str-addr\n  cell% field MalString/str-len\ndeftype MalString\n\n: MalString.0 { str-addr str-len -- mal-str }\n    MalString new { str }\n    str-addr str MalString/str-addr !\n    str-len  str MalString/str-len  !\n    str ;\n' MalString.0 is MalString.\n\n: unpack-str ( mal-string -- addr len )\n    dup MalString/str-addr @\n    swap MalString/str-len @ ;\n\nMalString\n  extend mal= ( other this -- bool )\n    over mal-type @ MalString = if\n        unpack-str rot unpack-str str=\n    else\n        2drop 0\n    endif ;;\n  ' as-native ' unpack-str extend-method*\n  extend seq { str }\n    str MalString/str-len @ { len }\n    len 0= if\n      mal-nil\n    else\n      len cells allocate throw { list-start }\n      len 0 ?do\n        str MalString/str-addr @ i + 1 MalString. ( new-char-string )\n        list-start i cells + !\n      loop\n      list-start len MalList.\n    endif ;;\ndrop\n\n\nMalType%\n  cell% field MalNativeFn/xt\ndeftype MalNativeFn\n\n: MalNativeFn. { xt -- mal-fn }\n    MalNativeFn new { mal-fn }\n    xt mal-fn MalNativeFn/xt !\n    mal-fn ;\n\n\nMalType%\n  cell% field MalUserFn/is-macro?\n  cell% field MalUserFn/env\n  cell% field MalUserFn/formal-args\n  cell% field MalUserFn/var-arg\n  cell% field MalUserFn/body\ndeftype MalUserFn\n\n: asMacro ( fn -- macro )\n    MalUserFn new\n    true                         over MalUserFn/is-macro?   !\n    over MalUserFn/env         @ over MalUserFn/env         !\n    over MalUserFn/formal-args @ over MalUserFn/formal-args !\n    over MalUserFn/var-arg     @ over MalUserFn/var-arg     !\n    swap MalUserFn/body        @ over MalUserFn/body        !\n;\n\n\nMalType%\n  cell% field SpecialOp/xt\ndeftype SpecialOp\n\n: SpecialOp.\n    SpecialOp new swap over SpecialOp/xt ! ;\n\nMalType%\n  cell% field Atom/val\ndeftype Atom\n\n: Atom. Atom new swap over Atom/val ! ;\n"
  },
  {
    "path": "impls/fsharp/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install fsharp\n"
  },
  {
    "path": "impls/fsharp/Makefile",
    "content": "#####################\n\nDEBUG = \n\nSOURCES_BASE = types.fs error.fs node.fs printer.fs tokenizer.fs reader.fs \\\n\t       readline.fs\nSOURCES_LISP = core.fs env.fs stepA_mal.fs\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nTERMINAL_SOURCES = terminal.cs\n\n#####################\n\nSRCS = step0_repl.fs step1_read_print.fs step2_eval.fs step3_env.fs \\\n       step4_if_fn_do.fs step5_tco.fs step6_file.fs step7_quote.fs \\\n       step8_macros.fs step9_try.fs stepA_mal.fs\nDLL_SOURCES = $(filter-out stepA_mal.fs,$(SOURCES))\n\nFSFLAGS = $(if $(strip $(DEBUG)),--debug+,--debug- --optimize+ --tailcalls+)\nCSFLAGS = $(if $(strip $(DEBUG)),-debug+,)\n#####################\n\nall: $(patsubst %.fs,%.exe,$(SRCS))\n\ndist: mal.exe mal\n\nmal.exe: stepA_mal.exe\n\tcp $< $@\n\n# NOTE/WARNING: static linking triggers mono libraries LGPL\n# distribution requirements.\n# http://www.mono-project.com/archived/guiderunning_mono_applications/\nmal: $(patsubst %.fs,%.exe,$(word $(words $(SOURCES)),$(SOURCES))) Mono.Terminal.dll mal.dll\n\tmkbundle --static -o $@ $+ --deps\n\nMono.Terminal.dll: $(TERMINAL_SOURCES)\n\tmcs $(CSFLAGS) -target:library $+ -out:$@\n\nmal.dll: $(DLL_SOURCES) Mono.Terminal.dll\n\tfsharpc $(FSFLAGS) -o $@ -r Mono.Terminal.dll -a $(DLL_SOURCES)\n\n%.exe: %.fs mal.dll\n\tfsharpc $(FSFLAGS) -o $@ -r mal.dll $<\n\nclean:\n\trm -f mal *.dll *.exe *.mdb\n"
  },
  {
    "path": "impls/fsharp/core.fs",
    "content": "module Core\n    \n    open System\n    open Types\n\n    let inline toBool b = if b then Node.TRUE else Node.FALSE\n\n    let inline twoNumberOp (f : int64 -> int64 -> Node) = function\n        | [Number(a); Number(b)] -> f a b\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n    \n    let inline twoNodeOp (f : Node -> Node -> Node) = function\n        | [a; b] -> f a b\n        | _ -> raise <| Error.wrongArity ()\n \n    let add = twoNumberOp (fun a b -> a + b |> Number)\n    let subtract = twoNumberOp (fun a b -> a - b |> Number)\n    let multiply = twoNumberOp (fun a b -> a * b |> Number)\n    let divide = twoNumberOp (fun a b -> a / b |> Number)\n    let lt = twoNodeOp (fun a b -> a < b |> toBool)\n    let le = twoNodeOp (fun a b -> a <= b |> toBool)\n    let ge = twoNodeOp (fun a b -> a >= b |> toBool)\n    let gt = twoNodeOp (fun a b -> a > b |> toBool)\n    let eq = twoNodeOp (fun a b -> a = b |> toBool)\n\n    let time_ms _ =\n        DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond |> int64 |> Number\n\n    let list = Node.makeList\n    let isList = function\n        | [List(_, _)] -> Node.TRUE\n        | [_] -> Node.FALSE\n        | _ -> raise <| Error.wrongArity ()\n\n    let isEmpty = function\n        | [List(_, [])] -> Node.TRUE\n        | [Vector(_, seg)] when seg.Count <= 0 -> Node.TRUE\n        | _ -> Node.FALSE\n\n    let count = function\n        | [List(_, lst)] -> lst |> List.length |> int64 |> Number\n        | [Vector(_, seg)] -> seg.Count |> int64 |> Number\n        | [Nil] -> Node.ZERO\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let pr_str nodes = nodes |> Printer.pr_str |> String\n    let str nodes = nodes |> Printer.str |> String\n    let prn nodes = nodes |> Printer.prn |> printfn \"%s\"; Nil\n    let println nodes = nodes |> Printer.println |> printfn \"%s\"; Nil\n\n    let read_str = function\n        | [String(s)] ->\n            match Reader.read_str s with\n            | [node] -> node\n            | nodes -> Symbol(\"do\")::nodes |> Node.makeList\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let slurp = function\n        | [String(s)] -> System.IO.File.ReadAllText s |> String\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let cons = function\n        | [node; List(_, lst)] -> node::lst |> Node.makeList\n        | [node; Vector(_, seg)] -> node::(List.ofSeq seg) |> Node.makeList\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let concat nodes =\n        let cons st node = node::st\n        let accumNode acc = function\n            | List(_, lst) -> lst |> List.fold cons acc\n            | Vector(_, seg) -> seg |> Seq.fold cons acc\n            | _ -> raise <| Error.argMismatch ()\n\n        nodes\n        |> List.fold accumNode []\n        |> List.rev\n        |> Node.makeList\n\n    let vec = function\n        | [Vector(_, _) as v] -> v\n        | [List(_, xs)] -> Node.ofArray <| Array.ofSeq xs\n        | [_] -> raise <| Error.argMismatch ()\n        | _   -> raise <| Error.wrongArity ()\n\n    let nth = function\n        | [List(_, lst); Number(n)] ->\n            let rec nth_list n = function\n                | [] -> raise <| Error.indexOutOfBounds ()\n                | h::_ when n = 0L -> h\n                | _::t -> nth_list (n - 1L) t\n            nth_list n lst\n        | [Vector(_, seg); Number(n)] ->\n            if n < 0L || n >= int64(seg.Count) then\n                raise <| Error.indexOutOfBounds ()\n            else\n                seg.Array.[int(n)]\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let first = function\n        | [List(_, [])] -> Node.NIL\n        | [List(_, h::_)] -> h\n        | [Vector(_, seg)] when seg.Count > 0 -> seg.Array.[0]\n        | [Vector(_, _)] -> Node.NIL\n        | [Nil] -> Node.NIL\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let rest = function\n        | [List(_, [])] -> Node.EmptyLIST\n        | [List(_, _::t)] -> t |> Node.makeList\n        | [Vector(_, seg)] when seg.Count < 2 -> Node.EmptyLIST\n        | [Vector(_, seg)] -> seg |> Seq.skip 1 |> List.ofSeq |> Node.makeList\n        | [Nil] -> Node.EmptyLIST\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let throw = function\n        | [node] -> raise <| Error.MalError(node)\n        | _ -> raise <| Error.wrongArity ()\n\n    let map = function\n        | [BuiltInFunc(_, _, f); Node.Seq seq]\n        | [Func(_, _, f, _, _, _); Node.Seq seq] ->\n            seq |> Seq.map (fun node -> f [node]) |> List.ofSeq |> Node.makeList\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let apply = function\n        | BuiltInFunc(_, _, f)::rest\n        | Macro(_, _, f, _, _, _)::rest\n        | Func(_, _, f, _, _, _)::rest ->\n            let rec getArgsAndCall acc = function\n                | [] -> raise <| Error.wrongArity ()\n                | [Node.Seq seq] ->\n                    seq |> Seq.fold (fun acc node -> node::acc) acc |> List.rev |> f\n                | [_] -> raise <| Error.argMismatch ()\n                | h::rest -> getArgsAndCall (h::acc) rest\n            getArgsAndCall [] rest\n        | _::_ -> raise <| Error.argMismatch ()\n        | [] -> raise <| Error.wrongArity ()\n\n    let isConst cmp = function\n        | [node] -> if node = cmp then Node.TRUE else Node.FALSE\n        | _ -> raise <| Error.wrongArity ()\n\n    let isPattern f = function\n        | [node] -> if f node then Node.TRUE else Node.FALSE\n        | _ -> raise <| Error.wrongArity ()\n\n    let isSymbol = isPattern (function Symbol(_) -> true | _ -> false)\n    let isKeyword = isPattern (function Keyword(_) -> true | _ -> false)\n    let isString = isPattern (function String(_) -> true | _ -> false)\n    let isNumber = isPattern (function Number(_) -> true | _ -> false)\n    let isFn = isPattern (function BuiltInFunc(_, _, _) | Func(_, _, _, _, _, _) -> true | _ -> false)\n    let isMacro = isPattern (function Macro(_, _, _, _, _, _) -> true | _ -> false)\n    let isSequential = isPattern (function Node.Seq(_) -> true | _ -> false)\n    let isVector = isPattern (function Vector(_, _) -> true | _ -> false)\n    let isMap = isPattern (function Map(_, _) -> true | _ -> false)\n    let isAtom = isPattern (function Atom(_, _) -> true | _ -> false)\n\n    let symbol = function\n        | [String(s)] -> Symbol s\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let keyword = function\n        | [String(s)] -> Keyword s\n        | [Keyword(_) as k] -> k\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let vector lst =  lst |> Array.ofList |> Node.ofArray\n\n    let rec getPairs lst =\n        seq {\n            match lst with\n            | first::second::t ->\n                yield first, second\n                yield! getPairs t\n            | [_] -> raise <| Error.expectedEvenNodeCount ()\n            | [] -> ()\n        }\n\n    let mapOpN f = function\n        | Map(_, map)::rest -> f rest map\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let mapOp1 f =\n        mapOpN (fun rest map ->\n                    match rest with\n                    | [v] -> f v map\n                    | _ -> raise <| Error.wrongArity ())\n\n    let mapOp0 f =\n        mapOpN (fun rest map ->\n                    match rest with\n                    | [] -> f map\n                    | _ -> raise <| Error.wrongArity ())\n\n    let mapKV f =\n        mapOp0 (fun map -> map |> Map.toSeq |> Seq.map f |> List.ofSeq |> Node.makeList)\n\n    let hashMap lst = lst |> getPairs |> Map.ofSeq |> Node.makeMap\n    let assoc = mapOpN (fun rest map ->\n                            rest\n                            |> getPairs\n                            |> Seq.fold (fun map (k, v) -> Map.add k v map) map\n                            |> Node.makeMap)\n    let dissoc = mapOpN (fun keys map ->\n                            keys\n                            |> List.fold (fun map k -> Map.remove k map) map\n                            |> Node.makeMap)\n    let get = function\n        | [Nil; _] -> Node.NIL\n        | _ as rest ->\n            rest |> mapOp1 (fun key map ->\n                                match Map.tryFind key map with\n                                | Some(node) -> node\n                                | None -> Node.NIL)\n    let containsKey key map = if Map.containsKey key map then Node.TRUE else Node.FALSE\n    let contains = mapOp1 containsKey\n    let keys = mapKV (fun (k, v) -> k)\n    let vals = mapKV (fun (k, v) -> v)\n\n    let atom nextValue = function\n        | [node] -> Atom((nextValue ()), ref node)\n        | _ -> raise <| Error.wrongArity ()\n\n    let deref = function\n        | [Atom(_, r)] -> !r\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let reset = function\n        | [Atom(_, r); node] ->\n            r := node\n            !r\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let swap = function\n        | Atom(_, r)\n            ::(BuiltInFunc(_, _, f) | Func(_, _, f, _, _, _))\n            ::rest ->\n                r := f (!r::rest)\n                !r\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let conj = function\n        | List(_, lst)::rest ->\n            rest \n            |> List.fold (fun lst node -> node::lst) lst \n            |> Node.makeList\n        | Vector(_, seg)::rest ->\n            (* Might be nice to implement a persistent vector here someday. *)\n            let cnt = List.length rest\n            if cnt > 0 then\n                let target : Node array = seg.Count + cnt |> Array.zeroCreate\n                System.Array.Copy(seg.Array :> System.Array, seg.Offset, \n                    target :> System.Array, 0, seg.Count)\n                let rec copyElem i = function\n                    | h::t -> \n                        Array.set target i h\n                        copyElem (i + 1) t\n                    | [] -> ()\n                copyElem (seg.Count) rest\n                target |> Node.ofArray\n            else\n                seg |> Node.makeVector\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let seq = function\n        | [Nil] -> Node.NIL\n        | [List(_, [])] -> Node.NIL\n        | [List(_, _) as l] -> l\n        | [Vector(_, seg)] when seg.Count < 1 -> Node.NIL\n        | [Vector(_, seg)] -> seg |> List.ofSeq |> Node.makeList\n        | [String(s)] when String.length s < 1 -> Node.NIL\n        | [String(s)] -> s |> Seq.map Node.ofChar |> List.ofSeq |> Node.makeList\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let withMeta = function\n        | [List(_, lst); m] -> List(m, lst)\n        | [Vector(_, seg); m] -> Vector(m, seg)\n        | [Map(_, map); m] -> Map(m, map)\n        | [BuiltInFunc(_, tag, f); m] -> BuiltInFunc(m, tag, f)\n        | [Func(_, tag, f, a, b, c); m] -> Func(m, tag, f, a, b, c)\n        | [Macro(_, tag, f, a, b, c); m] -> Macro(m, tag, f, a, b, c)\n        | [_; _] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let meta = function\n        | [List(m, _)]\n        | [Vector(m, _)]\n        | [Map(m, _)]\n        | [BuiltInFunc(m, _, _)]\n        | [Func(m, _, _, _, _, _)]\n        | [Macro(m, _, _, _, _, _)] -> m\n        | [_] -> Node.NIL\n        | _ -> raise <| Error.wrongArity ()\n"
  },
  {
    "path": "impls/fsharp/env.fs",
    "content": "module Env\n\n    open Types\n\n    let makeEmpty () = Env()\n\n    let ofList lst =\n        let env = makeEmpty ()\n        let accumulate (e : Env) (k, v) = e.Add(k, v); e\n        List.fold accumulate env lst\n\n    let set (env : EnvChain) key node =\n        match env with\n        | head::_ -> head.[key] <- node\n        | _ -> raise <| Error.noEnvironment ()\n\n    let rec get (chain : EnvChain) key =\n        match chain with\n        | [] -> None\n        | env::rest ->\n            match env.TryGetValue(key) with\n            | true, v -> Some(v)\n            | false, _ -> get rest key\n\n    let private getNextValue =\n        let counter = ref 0\n        fun () -> System.Threading.Interlocked.Increment(counter)\n\n    let makeBuiltInFunc f =\n        BuiltInFunc(Node.NIL, getNextValue (), f)\n\n    let makeFunc f body binds env =\n        Func(Node.NIL, getNextValue (), f, body, binds, env)\n\n    let makeMacro f body binds env =\n        Macro(Node.NIL, getNextValue (), f, body, binds, env)\n\n    let makeRootEnv () =\n        let wrap name f = name, makeBuiltInFunc f\n        let env =\n            [ wrap \"+\" Core.add\n              wrap \"-\" Core.subtract\n              wrap \"*\" Core.multiply\n              wrap \"/\" Core.divide\n              wrap \"list\" Core.list\n              wrap \"list?\" Core.isList\n              wrap \"empty?\" Core.isEmpty\n              wrap \"count\" Core.count\n              wrap \"=\" Core.eq\n              wrap \"<\" Core.lt\n              wrap \"<=\" Core.le\n              wrap \">=\" Core.ge\n              wrap \">\" Core.gt\n              wrap \"time-ms\" Core.time_ms\n              wrap \"pr-str\" Core.pr_str\n              wrap \"str\" Core.str\n              wrap \"prn\" Core.prn\n              wrap \"println\" Core.println\n              wrap \"read-string\" Core.read_str\n              wrap \"slurp\" Core.slurp\n              wrap \"cons\" Core.cons\n              wrap \"concat\" Core.concat\n              wrap \"vec\" Core.vec\n              wrap \"nth\" Core.nth\n              wrap \"first\" Core.first\n              wrap \"rest\" Core.rest\n              wrap \"throw\" Core.throw\n              wrap \"map\" Core.map\n              wrap \"apply\" Core.apply\n              wrap \"nil?\" (Core.isConst Node.NIL)\n              wrap \"true?\" (Core.isConst Node.TRUE)\n              wrap \"false?\" (Core.isConst Node.FALSE)\n              wrap \"symbol?\" Core.isSymbol\n              wrap \"symbol\" Core.symbol\n              wrap \"string?\" Core.isString\n              wrap \"keyword?\" Core.isKeyword\n              wrap \"keyword\" Core.keyword\n              wrap \"number?\" Core.isNumber\n              wrap \"fn?\" Core.isFn\n              wrap \"macro?\" Core.isMacro\n              wrap \"sequential?\" Core.isSequential\n              wrap \"vector?\" Core.isVector\n              wrap \"vector\" Core.vector\n              wrap \"map?\" Core.isMap\n              wrap \"hash-map\" Core.hashMap\n              wrap \"assoc\" Core.assoc\n              wrap \"dissoc\" Core.dissoc\n              wrap \"get\" Core.get\n              wrap \"contains?\" Core.contains\n              wrap \"keys\" Core.keys\n              wrap \"vals\" Core.vals\n              wrap \"atom\" (Core.atom getNextValue)\n              wrap \"atom?\" Core.isAtom\n              wrap \"deref\" Core.deref\n              wrap \"reset!\" Core.reset\n              wrap \"swap!\" Core.swap\n              wrap \"conj\" Core.conj\n              wrap \"seq\" Core.seq\n              wrap \"meta\" Core.meta\n              wrap \"with-meta\" Core.withMeta ]\n            |> ofList\n        [ env ]\n\n    let makeNew outer symbols nodes =\n        let env = (makeEmpty ())::outer\n        let rec loop symbols nodes =\n            match symbols, nodes with\n            | [Symbol(\"&\"); Symbol(s)], nodes ->\n                set env s (Node.makeList nodes)\n                env\n            | Symbol(\"&\")::_, _ -> raise <| Error.onlyOneSymbolAfterAmp ()\n            | Symbol(s)::symbols, n::nodes -> \n                set env s n\n                loop symbols nodes\n            | [], [] -> env\n            | _, [] -> raise <| Error.notEnoughValues ()\n            | [], _ -> raise <| Error.tooManyValues ()\n            | _, _ -> raise <| Error.errExpectedX \"symbol\"\n        loop symbols nodes\n"
  },
  {
    "path": "impls/fsharp/error.fs",
    "content": "module Error\n\n    exception ReaderError of string\n    exception EvalError of string\n    exception MalError of Types.Node\n    \n    let expectedXButEOF x = ReaderError(sprintf \"Expected %s, got EOF\" x)\n    let expectedX x = ReaderError(sprintf \"Expected %s\" x)\n    let unexpectedChar () = ReaderError(\"Unexpected char\")\n    let invalidToken () = ReaderError(\"Invalid token\")\n    \n    let expectedEvenNodeCount () = EvalError(\"Expected even node count\")\n    let wrongArity () = EvalError(\"Arity: wrong number of arguments\")\n    let argMismatch () = EvalError(\"Argument mismatch\")\n    let symbolNotFound s = EvalError(sprintf \"'%s' not found\" s)\n    let noEnvironment () = EvalError(\"No environment\")\n    let tooManyValues () = EvalError(\"Too many values\")\n    let notEnoughValues () = EvalError(\"Not enough values\")\n    let onlyOneSymbolAfterAmp () = EvalError(\"only one symbol after &\")\n    let errExpectedX x = EvalError(sprintf \"expected %s\" x)\n    let indexOutOfBounds () = EvalError(\"Index out of bounds\")\n"
  },
  {
    "path": "impls/fsharp/node.fs",
    "content": "module Node\n\n    open Types\n\n    let TRUE = Bool(true)\n    let SomeTRUE = Some(TRUE)\n    let FALSE = Bool(false)\n    let SomeFALSE = Some(FALSE)\n    let NIL = Nil\n    let SomeNIL = Some(NIL)\n    let ZERO = Number(0L)\n\n    let makeVector seg = Vector(NIL, seg)\n    let makeList lst = List(NIL, lst)\n    let makeMap map = Map(NIL, map)\n\n    let EmptyLIST = [] |> makeList\n    let EmptyVECTOR = System.ArraySegment([| |]) |> makeVector\n    let EmptyMAP = Map.empty |> makeMap\n\n    let ofArray arr = System.ArraySegment(arr) |> makeVector\n\n    let ofChar chr = sprintf \"%c\" chr |> String\n\n    let toArray = function\n        | List(_, lst) -> Array.ofList lst\n        | Vector(_, seg) -> Array.sub seg.Array seg.Offset seg.Count\n        | node -> [| node |]\n\n    let length = function\n        | List(_, lst) -> List.length lst\n        | Vector(_, seg) -> seg.Count\n        | Map(_, m) -> m.Count\n        | _ -> 1\n\n    (* Active Patterns to help with pattern matching nodes *)\n    let inline (|Elements|_|) num node =\n        let rec accumList acc idx lst =\n            let len = Array.length acc\n            match lst with\n            | [] when idx = len -> Some(Elements acc)\n            | h::t when idx < len ->\n                acc.[idx] <- h\n                accumList acc (idx + 1) t\n            | _ -> None\n        match node with\n        | List(_, lst) -> accumList (Array.zeroCreate num) 0 lst\n        | Vector(_, seg) when seg.Count = num -> Some(toArray node)\n        | _ -> None\n\n    let inline (|Cons|_|) node =\n        match node with\n        | List(_, h::t) -> Some(Cons(h, makeList t))\n        | Vector(_, seg) when seg.Count > 0 ->\n            let h = seg.Array.[seg.Offset]\n            let t = System.ArraySegment(seg.Array, seg.Offset + 1, seg.Count - 1)\n                    |> makeVector\n            Some(Cons(h, t))\n        | _ -> None\n\n    let inline (|Empty|_|) node =\n        match node with\n        | List(_, []) -> Some(Empty)\n        | Vector(_, seg) when seg.Count = 0 -> Some(Empty)\n        | _ -> None\n\n    let inline (|Pair|_|) node =\n        match node with\n        | List(_, a::b::t) -> Some(a, b, makeList t)\n        | List(_, []) -> None\n        | List(_, _) -> raise <| Error.expectedEvenNodeCount ()\n        | Vector(_, seg) ->\n            match seg.Count with\n            | 0 -> None\n            | 1 -> raise <| Error.expectedEvenNodeCount ()\n            | _ ->\n                let a = seg.Array.[seg.Offset]\n                let b = seg.Array.[seg.Offset + 1]\n                let t = System.ArraySegment(seg.Array, seg.Offset + 2, seg.Count - 2)\n                        |> makeVector\n                Some(a, b, t)\n        | _ -> None\n\n    let inline (|Seq|_|) node =\n        match node with\n        | List(_, lst) -> Some(Seq.ofList lst)\n        | Vector(_, seg) -> Some(seg :> Node seq)\n        | _ -> None\n"
  },
  {
    "path": "impls/fsharp/printer.fs",
    "content": "module Printer\n    open System.Text\n    open Types\n\n    type Profile = { Pretty : bool; Separator : string }\n    let pr_str_profile = { Pretty = true; Separator = \" \" }\n    let str_profile = { Pretty = false; Separator = \"\" }\n    let prn_profile = { Pretty = true; Separator = \" \" }\n    let println_profile = { Pretty = false; Separator = \" \" }\n    \n    let print profile nodes =\n        let acc = StringBuilder()\n        let appendStr (str : string) = acc.Append(str) |> ignore\n        let rec pr_node = function\n            | Nil -> appendStr \"nil\"\n            | List(_, nodes) -> pr_list nodes\n            | Vector(_, nodes) -> pr_vector nodes\n            | Map(_, map) -> pr_map map\n            | Symbol(symbol) -> appendStr symbol\n            | Keyword(keyword) -> appendStr \":\"; appendStr keyword\n            | Number(num) -> acc.Append(num) |> ignore\n            | String(str) when profile.Pretty -> pr_str_pretty str\n            | String(str) -> appendStr str\n            | Bool(true) -> appendStr \"true\"\n            | Bool(false) -> appendStr \"false\"\n            | BuiltInFunc(_, tag, _) | Func(_, tag, _, _, _, _) ->\n                pr_func \"func\" tag\n            | Macro(_, tag, _, _, _, _) -> pr_func \"macro\" tag\n            | Atom(tag, r) -> pr_atom tag !r\n\n        and pr separator prefix node =\n            appendStr prefix\n            pr_node node\n            separator\n\n        and std_pr = pr \" \"\n\n        and pr_str_pretty str =\n            let appendChar = function\n                | '\\t' -> appendStr \"\\\\t\"\n                | '\\b' -> appendStr \"\\\\b\"\n                | '\\n' -> appendStr \"\\\\n\"\n                | '\\r' -> appendStr \"\\\\r\"\n                | '\\f' -> appendStr \"\\\\f\"\n                | '\"' -> appendStr \"\\\\\\\"\"\n                | '\\\\' -> appendStr \"\\\\\\\\\"\n                | ch -> acc.Append(ch) |> ignore\n            appendStr \"\\\"\"\n            str |> Seq.iter appendChar\n            appendStr \"\\\"\"\n\n        and pr_func ftype tag =\n            sprintf \"#<%s %d>\" ftype tag |> appendStr\n\n        and pr_atom tag node =\n            appendStr \"(atom \"\n            pr_node node\n            appendStr \")\"\n\n        and pr_list nodes =\n            appendStr \"(\"\n            nodes |> List.fold std_pr  \"\" |> ignore\n            appendStr \")\"\n\n        and pr_vector nodes =\n            appendStr \"[\"\n            nodes |> Seq.fold std_pr \"\" |> ignore\n            appendStr \"]\"           \n\n        and pr_map map =\n            let pr prefix key value =\n                appendStr prefix\n                pr_node key\n                appendStr \" \"\n                pr_node value\n                \" \"\n            appendStr \"{\"\n            map |> Map.fold pr \"\" |> ignore\n            appendStr \"}\"\n        \n        nodes |> Seq.fold (pr profile.Separator) \"\" |> ignore\n        acc.ToString()\n\n    let pr_str : seq<Node> -> string = print pr_str_profile\n    let str : seq<Node> -> string = print str_profile\n    let prn : seq<Node> -> string = print prn_profile\n    let println : seq<Node> -> string = print println_profile\n"
  },
  {
    "path": "impls/fsharp/reader.fs",
    "content": "module Reader\n    open System\n    open Tokenizer\n    open Types\n    open Node\n\n    type MutableList = System.Collections.Generic.List<Node>\n    let inline addToMutableList (lst:MutableList) item = lst.Add(item); lst\n\n    let quote = Symbol(\"quote\")\n    let quasiquote = Symbol(\"quasiquote\")\n    let unquote = Symbol(\"unquote\")\n    let spliceUnquote = Symbol(\"splice-unquote\")\n    let deref = Symbol(\"deref\")\n    let withMeta = Symbol(\"with-meta\")\n\n    let rec readForm = function\n        | OpenParen::rest -> readList [] rest\n        | OpenBracket::rest -> readVector (MutableList()) rest\n        | OpenBrace::rest -> readMap [] rest\n        | SingleQuote::rest -> wrapForm quote rest\n        | Backtick::rest -> wrapForm quasiquote rest\n        | Tilde::rest -> wrapForm unquote rest\n        | SpliceUnquote::rest -> wrapForm spliceUnquote rest\n        | At::rest -> wrapForm deref rest\n        | Caret::rest -> readMeta rest\n        | tokens -> readAtom tokens\n\n    and wrapForm node tokens = \n        match readForm tokens with\n        | Some(form), rest -> Some(makeList [node; form]), rest\n        | None, _ -> raise <| Error.expectedXButEOF \"form\"\n\n    and readList acc = function\n        | CloseParen::rest -> Some(acc |> List.rev |> makeList), rest\n        | [] -> raise <| Error.expectedXButEOF \"')'\"\n        | tokens -> \n            match readForm tokens with\n            | Some(form), rest -> readList (form::acc) rest\n            | None, _ -> raise <| Error.expectedXButEOF \"')'\"\n\n    and readVector acc = function\n        | CloseBracket::rest -> Some(acc.ToArray() |> Node.ofArray), rest\n        | [] -> raise <| Error.expectedXButEOF \"']'\"\n        | tokens -> \n            match readForm tokens with\n            | Some(form), rest -> readVector (addToMutableList acc form) rest\n            | None, _ -> raise <| Error.expectedXButEOF \"']'\"\n\n    and readMap acc = function\n        | CloseBrace::rest -> Some(acc |> List.rev |> Map.ofList |> makeMap), rest\n        | [] -> raise <| Error.expectedXButEOF \"'}'\"\n        | tokens -> \n            match readForm tokens with\n            | Some(key), rest ->\n                match readForm rest with\n                | Some(v), rest -> readMap ((key, v)::acc) rest\n                | None, _ -> raise <| Error.expectedXButEOF \"'}'\"\n            | None, _ -> raise <| Error.expectedXButEOF \"'}'\"\n\n    and readMeta = function\n        | OpenBrace::rest ->\n            let meta, rest = readMap [] rest\n            match readForm rest with\n            | Some(form), rest -> Some([withMeta; form; meta.Value] |> makeList), rest\n            | None, _ -> raise <| Error.expectedXButEOF \"form\"\n        | _ -> raise <| Error.expectedXButEOF \"map\"\n\n    and readAtom = function\n        | Token(\"nil\")::rest -> Node.SomeNIL, rest\n        | Token(\"true\")::rest -> Node.SomeTRUE, rest\n        | Token(\"false\")::rest -> Node.SomeFALSE, rest\n        | Tokenizer.String(str)::rest -> Some(String(str)), rest\n        | Tokenizer.Keyword(kw)::rest -> Some(Keyword(kw)), rest\n        | Tokenizer.Number(num)::rest -> Some(Number(Int64.Parse(num))), rest\n        | Token(sym)::rest -> Some(Symbol(sym)), rest\n        | [] -> None, []\n        | _ -> raise <| Error.invalidToken ()\n        \n    let rec readForms acc = function\n        | [] -> List.rev acc\n        | tokens -> \n            match readForm tokens with\n            | Some(form), rest -> readForms (form::acc) rest\n            | None, rest -> readForms acc rest\n\n    let read_str str =\n        tokenize str |> readForms []\n"
  },
  {
    "path": "impls/fsharp/readline.fs",
    "content": "module Readline\n    open System\n    open Mono.Terminal\n\n    type Mode =\n    | Terminal\n    | Raw\n\n    let read prompt = function\n    | Terminal \n        -> let editor = LineEditor(\"Mal\")\n           editor.Edit(prompt, \"\")\n    | Raw\n        -> Console.Write(prompt)\n           Console.Out.Flush()\n           Console.ReadLine() \n"
  },
  {
    "path": "impls/fsharp/run",
    "content": "#!/usr/bin/env bash\nexec mono $(dirname $0)/${STEP:-stepA_mal}.exe ${RAW:+--raw} \"${@}\"\n"
  },
  {
    "path": "impls/fsharp/step0_repl.fs",
    "content": "module REPL\n    let READ input =\n        input\n\n    let EVAL ast =\n        ast\n\n    let PRINT v =\n        printfn \"%s\" v\n\n    let REP input =\n        input\n        |> READ\n        |> EVAL\n        |> PRINT\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    [<EntryPoint>]\n    let rec main args =\n        let mode = getReadlineMode args\n        match Readline.read \"user> \" mode with\n        | null -> 0\n        | input -> \n            REP input\n            main args\n"
  },
  {
    "path": "impls/fsharp/step1_read_print.fs",
    "content": "module REPL\n    open System\n\n    let READ input =\n        try\n            Reader.read_str input\n        with\n        | Error.ReaderError(msg) ->\n            printfn \"%s\" msg\n            []\n\n    let EVAL ast =\n        Some(ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let REP input =\n        READ input\n        |> Seq.ofList\n        |> Seq.map (fun form -> EVAL form)\n        |> Seq.filter Option.isSome\n        |> Seq.iter (fun value -> PRINT value.Value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let rec loop () =\n            match Readline.read \"user> \" mode with\n            | null -> 0\n            | input -> \n                REP input\n                loop()\n        loop ()\n"
  },
  {
    "path": "impls/fsharp/step2_eval.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec eval env ast =\n        (* Printer.pr_str [ast] |> printfn \"EVAL: %s\" *)\n        match ast with\n        | Symbol(sym) -> match Env.get env sym with\n                         | Some(value) -> value\n                         | None -> Error.symbolNotFound sym |> raise\n        | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n        | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n        | List(_, (a0 :: rest)) ->\n            match eval env a0 with\n            | BuiltInFunc(_, _, f) -> List.map (eval env) rest |> f\n            | _ -> raise <| Error.errExpectedX \"func\"\n        | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let REP env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let env = Env.makeRootEnv ()\n        let rec loop () =\n            match Readline.read \"user> \" mode with\n            | null -> 0\n            | input ->\n                try\n                    REP env input\n                with\n                | Error.EvalError(str)\n                | Error.ReaderError(str) ->\n                    printfn \"Error: %s\" str\n                | ex ->\n                    printfn \"Error: %s\" (ex.Message)\n                loop ()\n        loop ()\n"
  },
  {
    "path": "impls/fsharp/step3_env.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec defBang env = function\n        | [sym; node] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env node\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStar env = function\n        | [bindings; form] ->\n            let newEnv = Env.makeNew env [] []\n            let binder = setBinding newEnv\n            match bindings with\n            | List(_, _) | Vector(_, _) -> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            eval newEnv form\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n        | Symbol(sym) -> match Env.get env sym with\n                         | Some(value) -> value\n                         | None -> Error.symbolNotFound sym |> raise\n        | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n        | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n        | List(_, Symbol(\"def!\")::rest) -> defBang env rest\n        | List(_, Symbol(\"let*\")::rest) -> letStar env rest\n        | List(_, (a0 :: rest)) ->\n            match eval env a0 with\n            | BuiltInFunc(_, _, f) -> List.map (eval env) rest |> f\n            | _ -> raise <| Error.errExpectedX \"func\"\n        | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let REP env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let env = Env.makeRootEnv ()\n        let rec loop () =\n            match Readline.read \"user> \" mode with\n            | null -> 0\n            | input ->\n                try\n                    REP env input\n                with\n                | Error.EvalError(str)\n                | Error.ReaderError(str) ->\n                    printfn \"Error: %s\" str\n                | ex ->\n                    printfn \"Error: %s\" (ex.Message)\n                loop ()\n        loop ()\n"
  },
  {
    "path": "impls/fsharp/step4_if_fn_do.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm env = function\n        | [bindings; form] ->\n            let newEnv = Env.makeNew env [] []\n            let binder = setBinding newEnv\n            match bindings with\n            | List(_, _) | Vector(_, _) -> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            eval newEnv form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> eval env falseForm\n        | _ -> eval env trueForm\n\n    and doForm env = function\n        | [a] -> eval env a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n        | Symbol(sym) -> match Env.get env sym with\n                         | Some(value) -> value\n                         | None -> Error.symbolNotFound sym |> raise\n        | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n        | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n        | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n        | List(_, Symbol(\"let*\")::rest) -> letStarForm env rest\n        | List(_, Symbol(\"if\")::rest) -> ifForm env rest\n        | List(_, Symbol(\"do\")::rest) -> doForm env rest\n        | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n        | List(_, (a0 :: rest)) ->\n            let args = List.map (eval env) rest\n            match eval env a0 with\n            | BuiltInFunc(_, _, f) -> f args\n            | Func(_, _, _, body, binds, outer) ->\n                let inner = Env.makeNew outer binds args\n                body |> eval inner\n            | _ -> raise <| Error.errExpectedX \"func\"\n        | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let env = Env.makeRootEnv ()\n\n        RE env \"(def! not (fn* (a) (if a false true)))\" |> Seq.iter ignore\n\n        let rec loop () =\n            match Readline.read \"user> \" mode with\n            | null -> 0\n            | input ->\n                try\n                    REP env input\n                with\n                | Error.EvalError(str)\n                | Error.ReaderError(str) ->\n                    printfn \"Error: %s\" str\n                | ex ->\n                    printfn \"Error: %s\" (ex.Message)\n                loop ()\n        loop ()\n"
  },
  {
    "path": "impls/fsharp/step5_tco.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm outer = function\n        | [bindings; form] ->\n            let inner = Env.makeNew outer [] []\n            let binder = setBinding inner\n            match bindings with\n            | List(_, _) | Vector(_, _)-> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            inner, form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> falseForm\n        | _ -> trueForm\n\n    and doForm env = function\n        | [a] -> a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n        | Symbol(sym) -> match Env.get env sym with\n                         | Some(value) -> value\n                         | None -> Error.symbolNotFound sym |> raise\n        | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n        | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n        | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n        | List(_, Symbol(\"let*\")::rest) ->\n            let inner, form = letStarForm env rest\n            form |> eval inner\n        | List(_, Symbol(\"if\")::rest) -> ifForm env rest |> eval env\n        | List(_, Symbol(\"do\")::rest) -> doForm env rest |> eval env\n        | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n        | List(_, (a0 :: rest)) ->\n            let args = List.map (eval env) rest\n            match eval env a0 with\n            | BuiltInFunc(_, _, f) -> f args\n            | Func(_, _, _, body, binds, outer) ->\n                let inner = Env.makeNew outer binds args\n                body |> eval inner\n            | _ -> raise <| Error.errExpectedX \"func\"\n        | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let env = Env.makeRootEnv ()\n\n        RE env \"(def! not (fn* (a) (if a false true)))\" |> Seq.iter ignore\n\n        let rec loop () =\n            match Readline.read \"user> \" mode with\n            | null -> 0\n            | input ->\n                try\n                    REP env input\n                with\n                | Error.EvalError(str)\n                | Error.ReaderError(str) ->\n                    printfn \"Error: %s\" str\n                | ex ->\n                    printfn \"Error: %s\" (ex.Message)\n                loop ()\n        loop ()\n"
  },
  {
    "path": "impls/fsharp/step6_file.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm outer = function\n        | [bindings; form] ->\n            let inner = Env.makeNew outer [] []\n            let binder = setBinding inner\n            match bindings with\n            | List(_, _) | Vector(_, _)-> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            inner, form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> falseForm\n        | _ -> trueForm\n\n    and doForm env = function\n        | [a] -> a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n        | Symbol(sym) -> match Env.get env sym with\n                         | Some(value) -> value\n                         | None -> Error.symbolNotFound sym |> raise\n        | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n        | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n        | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n        | List(_, Symbol(\"let*\")::rest) ->\n            let inner, form = letStarForm env rest\n            form |> eval inner\n        | List(_, Symbol(\"if\")::rest) -> ifForm env rest |> eval env\n        | List(_, Symbol(\"do\")::rest) -> doForm env rest |> eval env\n        | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n        | List(_, (a0 :: rest)) ->\n            let args = List.map (eval env) rest\n            match eval env a0 with\n            | BuiltInFunc(_, _, f) -> f args\n            | Func(_, _, _, body, binds, outer) ->\n                let inner = Env.makeNew outer binds args\n                body |> eval inner\n            | _ -> raise <| Error.errExpectedX \"func\"\n        | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    let eval_func env = function\n        | [ast] -> eval env ast\n        | _ -> raise <| Error.wrongArity ()\n\n    let argv_func = function\n        | file::rest -> rest |> List.map Types.String |> makeList\n        | [] -> EmptyLIST\n\n    let configureEnv args =\n        let env = Env.makeRootEnv ()\n\n        Env.set env \"eval\" <| Env.makeBuiltInFunc (fun nodes -> eval_func env nodes)\n        Env.set env \"*ARGV*\" <| argv_func args\n\n        RE env \"\"\"\n            (def! not (fn* (a) (if a false true)))\n            (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n            \"\"\" |> Seq.iter ignore\n\n        env\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let args = Seq.ofArray args |> Seq.filter (fun e -> e <> \"--raw\") |> List.ofSeq\n        let env = configureEnv args\n\n        match args with\n        | file::_ ->\n            System.IO.File.ReadAllText file\n            |> RE env |> Seq.iter ignore\n            0\n        | _ ->\n            let rec loop () =\n                match Readline.read \"user> \" mode with\n                | null -> 0\n                | input ->\n                    try\n                        REP env input\n                    with\n                    | Error.EvalError(str)\n                    | Error.ReaderError(str) ->\n                        printfn \"Error: %s\" str\n                    | ex ->\n                        printfn \"Error: %s\" (ex.Message)\n                    loop ()\n            loop ()\n"
  },
  {
    "path": "impls/fsharp/step7_quote.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec qqLoop elt acc =\n        match elt with\n        | List(_, [Symbol(\"splice-unquote\");list]) -> makeList [Symbol \"concat\"; list; acc]\n        | List(_,  Symbol(\"splice-unquote\")::_)    -> raise <| Error.wrongArity ()\n        | _ -> makeList [Symbol \"cons\"; quasiquote elt; acc]\n    and quasiquote = function\n        | List(_, [Symbol(\"unquote\");form]) -> form\n        | List(_,  Symbol(\"unquote\")::_)    -> raise <| Error.wrongArity ()\n        | List (_, list) -> List.foldBack qqLoop list Node.EmptyLIST\n        | Vector(_, segment) ->\n            let array = Array.sub segment.Array segment.Offset segment.Count\n            let folded = Array.foldBack qqLoop array Node.EmptyLIST\n            makeList [Symbol \"vec\"; folded]\n        | Map(_)    as ast -> makeList [Symbol \"quote\"; ast]\n        | Symbol(_) as ast -> makeList [Symbol \"quote\"; ast]\n        | ast -> ast\n\n    let quoteForm = function\n        | [node] -> node\n        | _ -> raise <| Error.wrongArity ()\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm outer = function\n        | [bindings; form] ->\n            let inner = Env.makeNew outer [] []\n            let binder = setBinding inner\n            match bindings with\n            | List(_) | Vector(_) -> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            inner, form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> falseForm\n        | _ -> trueForm\n\n    and doForm env = function\n        | [a] -> a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n        | Symbol(sym) -> match Env.get env sym with\n                         | Some(value) -> value\n                         | None -> Error.symbolNotFound sym |> raise\n        | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n        | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n        | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n        | List(_, Symbol(\"let*\")::rest) ->\n            let inner, form = letStarForm env rest\n            form |> eval inner\n        | List(_, Symbol(\"if\")::rest) -> ifForm env rest |> eval env\n        | List(_, Symbol(\"do\")::rest) -> doForm env rest |> eval env\n        | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n        | List(_, Symbol(\"quote\")::rest) -> quoteForm rest\n        | List(_, [Symbol(\"quasiquote\");form]) -> eval env <| quasiquote form\n        | List(_,  Symbol(\"quasiquote\")::_)    -> raise <| Error.wrongArity ()\n        | List(_, (a0 :: rest)) ->\n            let args = List.map (eval env) rest\n            match eval env a0 with\n            | BuiltInFunc(_, _, f) -> f args\n            | Func(_, _, _, body, binds, outer) ->\n                let inner = Env.makeNew outer binds args\n                body |> eval inner\n            | _ -> raise <| Error.errExpectedX \"func\"\n        | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    let eval_func env = function\n        | [ast] -> eval env ast\n        | _ -> raise <| Error.wrongArity ()\n\n    let argv_func = function\n        | file::rest -> rest |> List.map Types.String |> makeList\n        | [] -> EmptyLIST\n\n    let configureEnv args =\n        let env = Env.makeRootEnv ()\n\n        Env.set env \"eval\" <| Env.makeBuiltInFunc (fun nodes -> eval_func env nodes)\n        Env.set env \"*ARGV*\" <| argv_func args\n\n        RE env \"\"\"\n            (def! not (fn* (a) (if a false true)))\n            (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n            \"\"\" |> Seq.iter ignore\n\n        env\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let args = Seq.ofArray args |> Seq.filter (fun e -> e <> \"--raw\") |> List.ofSeq\n        let env = configureEnv args\n\n        match args with\n        | file::_ ->\n            System.IO.File.ReadAllText file\n            |> RE env |> Seq.iter ignore\n            0\n        | _ ->\n            let rec loop () =\n                match Readline.read \"user> \" mode with\n                | null -> 0\n                | input ->\n                    try\n                        REP env input\n                    with\n                    | Error.EvalError(str)\n                    | Error.ReaderError(str) ->\n                        printfn \"Error: %s\" str\n                    | ex ->\n                        printfn \"Error: %s\" (ex.Message)\n                    loop ()\n            loop ()\n"
  },
  {
    "path": "impls/fsharp/step8_macros.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec qqLoop elt acc =\n        match elt with\n        | List(_, [Symbol(\"splice-unquote\");list]) -> makeList [Symbol \"concat\"; list; acc]\n        | List(_,  Symbol(\"splice-unquote\")::_)    -> raise <| Error.wrongArity ()\n        | _ -> makeList [Symbol \"cons\"; quasiquote elt; acc]\n    and quasiquote = function\n        | List(_, [Symbol(\"unquote\");form]) -> form\n        | List(_,  Symbol(\"unquote\")::_)    -> raise <| Error.wrongArity ()\n        | List (_, list) -> List.foldBack qqLoop list Node.EmptyLIST\n        | Vector(_, segment) ->\n            let array = Array.sub segment.Array segment.Offset segment.Count\n            let folded = Array.foldBack qqLoop array Node.EmptyLIST\n            makeList [Symbol \"vec\"; folded]\n        | Map(_)    as ast -> makeList [Symbol \"quote\"; ast]\n        | Symbol(_) as ast -> makeList [Symbol \"quote\"; ast]\n        | ast -> ast\n\n    let quoteForm = function\n        | [node] -> node\n        | _ -> raise <| Error.wrongArity ()\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and defMacroForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                match node with\n                | Func(_, _, f, body, binds, outer) ->\n                    let node = Env.makeMacro f body binds outer\n                    Env.set env sym node\n                    node\n                | _ -> raise <| Error.errExpectedX \"user defined func\"\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm outer = function\n        | [bindings; form] ->\n            let inner = Env.makeNew outer [] []\n            let binder = setBinding inner\n            match bindings with\n            | List(_) | Vector(_) -> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            inner, form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> falseForm\n        | _ -> trueForm\n\n    and doForm env = function\n        | [a] -> a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n            | Symbol(sym) -> match Env.get env sym with\n                             | Some(value) -> value\n                             | None -> Error.symbolNotFound sym |> raise\n            | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n            | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n            | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n            | List(_, Symbol(\"defmacro!\")::rest) -> defMacroForm env rest\n            | List(_, Symbol(\"let*\")::rest) ->\n                let inner, form = letStarForm env rest\n                form |> eval inner\n            | List(_, Symbol(\"if\")::rest) -> ifForm env rest |> eval env\n            | List(_, Symbol(\"do\")::rest) -> doForm env rest |> eval env\n            | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n            | List(_, Symbol(\"quote\")::rest) -> quoteForm rest\n            | List(_, [Symbol(\"quasiquote\");form]) -> eval env <| quasiquote form\n            | List(_,  Symbol(\"quasiquote\")::_)    -> raise <| Error.wrongArity ()\n            | List(_, (a0 :: args)) ->\n                match eval env a0 with\n                | Macro(_, _, f, _, _, _) -> f args |> eval env\n                | BuiltInFunc(_, _, f) -> List.map (eval env) args |> f\n                | Func(_, _, _, body, binds, outer) ->\n                    let inner = List.map (eval env) args |> Env.makeNew outer binds\n                    body |> eval inner\n                | _ -> raise <| Error.errExpectedX \"func\"\n            | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    let eval_func env = function\n        | [ast] -> eval env ast\n        | _ -> raise <| Error.wrongArity ()\n\n    let argv_func = function\n        | file::rest -> rest |> List.map Types.String |> makeList\n        | [] -> EmptyLIST\n\n    let configureEnv args =\n        let env = Env.makeRootEnv ()\n\n        Env.set env \"eval\" <| Env.makeBuiltInFunc (fun nodes -> eval_func env nodes)\n        Env.set env \"*ARGV*\" <| argv_func args\n\n        RE env \"\"\"\n            (def! not (fn* (a) (if a false true)))\n            (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n            (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\n            \"\"\" |> Seq.iter ignore\n\n        env\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let args = Seq.ofArray args |> Seq.filter (fun e -> e <> \"--raw\") |> List.ofSeq\n        let env = configureEnv args\n\n        match args with\n        | file::_ ->\n            System.IO.File.ReadAllText file\n            |> RE env |> Seq.iter ignore\n            0\n        | _ ->\n            let rec loop () =\n                match Readline.read \"user> \" mode with\n                | null -> 0\n                | input ->\n                    try\n                        REP env input\n                    with\n                    | Error.EvalError(str)\n                    | Error.ReaderError(str) ->\n                        printfn \"Error: %s\" str\n                    | ex ->\n                        printfn \"Error: %s\" (ex.Message)\n                    loop ()\n            loop ()\n"
  },
  {
    "path": "impls/fsharp/step9_try.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec qqLoop elt acc =\n        match elt with\n        | List(_, [Symbol(\"splice-unquote\");list]) -> makeList [Symbol \"concat\"; list; acc]\n        | List(_,  Symbol(\"splice-unquote\")::_)    -> raise <| Error.wrongArity ()\n        | _ -> makeList [Symbol \"cons\"; quasiquote elt; acc]\n    and quasiquote = function\n        | List(_, [Symbol(\"unquote\");form]) -> form\n        | List(_,  Symbol(\"unquote\")::_)    -> raise <| Error.wrongArity ()\n        | List (_, list) -> List.foldBack qqLoop list Node.EmptyLIST\n        | Vector(_, segment) ->\n            let array = Array.sub segment.Array segment.Offset segment.Count\n            let folded = Array.foldBack qqLoop array Node.EmptyLIST\n            makeList [Symbol \"vec\"; folded]\n        | Map(_)    as ast -> makeList [Symbol \"quote\"; ast]\n        | Symbol(_) as ast -> makeList [Symbol \"quote\"; ast]\n        | ast -> ast\n\n    let quoteForm = function\n        | [node] -> node\n        | _ -> raise <| Error.wrongArity ()\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and defMacroForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                match node with\n                | Func(_, _, f, body, binds, outer) ->\n                    let node = Env.makeMacro f body binds outer\n                    Env.set env sym node\n                    node\n                | _ -> raise <| Error.errExpectedX \"user defined func\"\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm outer = function\n        | [bindings; form] ->\n            let inner = Env.makeNew outer [] []\n            let binder = setBinding inner\n            match bindings with\n            | List(_) | Vector(_) -> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            inner, form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> falseForm\n        | _ -> trueForm\n\n    and doForm env = function\n        | [a] -> a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and catchForm env err = function\n        | List(_, [Symbol(\"catch*\"); Symbol(_) as sym; catchBody]) ->\n            let inner = Env.makeNew env [sym] [err]\n            catchBody |> eval inner\n        | List(_, [_; _; _]) -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n\n    and tryForm env = function\n        | [exp] ->\n            eval env exp\n        | [exp; catchClause] ->\n            try\n                eval env exp\n            with\n            | Error.EvalError(str) -> catchForm env (String(str)) catchClause\n            | Error.MalError(node) -> catchForm env node catchClause\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n            | Symbol(sym) -> match Env.get env sym with\n                             | Some(value) -> value\n                             | None -> Error.symbolNotFound sym |> raise\n            | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n            | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n            | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n            | List(_, Symbol(\"defmacro!\")::rest) -> defMacroForm env rest\n            | List(_, Symbol(\"let*\")::rest) ->\n                let inner, form = letStarForm env rest\n                form |> eval inner\n            | List(_, Symbol(\"if\")::rest) -> ifForm env rest |> eval env\n            | List(_, Symbol(\"do\")::rest) -> doForm env rest |> eval env\n            | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n            | List(_, Symbol(\"quote\")::rest) -> quoteForm rest\n            | List(_, [Symbol(\"quasiquote\");form]) -> eval env <| quasiquote form\n            | List(_,  Symbol(\"quasiquote\")::_)    -> raise <| Error.wrongArity ()\n            | List(_, Symbol(\"try*\")::rest) -> tryForm env rest\n            | List(_, (a0 :: args)) ->\n                match eval env a0 with\n                | Macro(_, _, f, _, _, _) -> f args |> eval env\n                | BuiltInFunc(_, _, f) -> List.map (eval env) args |> f\n                | Func(_, _, _, body, binds, outer) ->\n                    let inner = List.map (eval env) args |> Env.makeNew outer binds\n                    body |> eval inner\n                | _ -> raise <| Error.errExpectedX \"func\"\n            | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    let eval_func env = function\n        | [ast] -> eval env ast\n        | _ -> raise <| Error.wrongArity ()\n\n    let argv_func = function\n        | file::rest -> rest |> List.map Types.String |> makeList\n        | [] -> EmptyLIST\n\n    let configureEnv args =\n        let env = Env.makeRootEnv ()\n\n        Env.set env \"eval\" <| Env.makeBuiltInFunc (fun nodes -> eval_func env nodes)\n        Env.set env \"*ARGV*\" <| argv_func args\n\n        RE env \"\"\"\n            (def! not (fn* (a) (if a false true)))\n            (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n            (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\n            \"\"\" |> Seq.iter ignore\n\n        env\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let args = Seq.ofArray args |> Seq.filter (fun e -> e <> \"--raw\") |> List.ofSeq\n        let env = configureEnv args\n\n        match args with\n        | file::_ ->\n            System.IO.File.ReadAllText file\n            |> RE env |> Seq.iter ignore\n            0\n        | _ ->\n            let rec loop () =\n                match Readline.read \"user> \" mode with\n                | null -> 0\n                | input ->\n                    try\n                        REP env input\n                    with\n                    | Error.EvalError(str)\n                    | Error.ReaderError(str) ->\n                        printfn \"Error: %s\" str\n                    | Error.MalError(node) ->\n                        printfn \"Error: %s\" (Printer.pr_str [node])\n                    | ex ->\n                        printfn \"Error: %s\" (ex.Message)\n                    loop ()\n            loop ()\n"
  },
  {
    "path": "impls/fsharp/stepA_mal.fs",
    "content": "module REPL\n    open System\n    open Node\n    open Types\n\n    let rec iterPairs f = function\n        | Pair(first, second, t) ->\n            f first second\n            iterPairs f t\n        | Empty -> ()\n        | _ -> raise <| Error.errExpectedX \"list or vector\"\n\n    let rec qqLoop elt acc =\n        match elt with\n        | List(_, [Symbol(\"splice-unquote\");list]) -> makeList [Symbol \"concat\"; list; acc]\n        | List(_,  Symbol(\"splice-unquote\")::_)    -> raise <| Error.wrongArity ()\n        | _ -> makeList [Symbol \"cons\"; quasiquote elt; acc]\n    and quasiquote = function\n        | List(_, [Symbol(\"unquote\");form]) -> form\n        | List(_,  Symbol(\"unquote\")::_)    -> raise <| Error.wrongArity ()\n        | List (_, list) -> List.foldBack qqLoop list Node.EmptyLIST\n        | Vector(_, segment) ->\n            let array = Array.sub segment.Array segment.Offset segment.Count\n            let folded = Array.foldBack qqLoop array Node.EmptyLIST\n            makeList [Symbol \"vec\"; folded]\n        | Map(_)    as ast -> makeList [Symbol \"quote\"; ast]\n        | Symbol(_) as ast -> makeList [Symbol \"quote\"; ast]\n        | ast -> ast\n\n    let quoteForm = function\n        | [node] -> node\n        | _ -> raise <| Error.wrongArity ()\n\n    let rec defBangForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                Env.set env sym node\n                node\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and defMacroForm env = function\n        | [sym; form] ->\n            match sym with\n            | Symbol(sym) ->\n                let node = eval env form\n                match node with\n                | Func(_, _, f, body, binds, outer) ->\n                    let node = Env.makeMacro f body binds outer\n                    Env.set env sym node\n                    node\n                | _ -> raise <| Error.errExpectedX \"user defined func\"\n            | _ -> raise <| Error.errExpectedX \"symbol\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and setBinding env first second =\n        let s = match first with\n                | Symbol(s) -> s\n                | _ -> raise <| Error.errExpectedX \"symbol\"\n        let form = eval env second\n        Env.set env s form\n\n    and letStarForm outer = function\n        | [bindings; form] ->\n            let inner = Env.makeNew outer [] []\n            let binder = setBinding inner\n            match bindings with\n            | List(_) | Vector(_) -> iterPairs binder bindings\n            | _ -> raise <| Error.errExpectedX \"list or vector\"\n            inner, form\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm env = function\n        | [condForm; trueForm; falseForm] -> ifForm3 env condForm trueForm falseForm\n        | [condForm; trueForm] -> ifForm3 env condForm trueForm Nil\n        | _ -> raise <| Error.wrongArity ()\n\n    and ifForm3 env condForm trueForm falseForm =\n        match eval env condForm with\n        | Bool(false) | Nil -> falseForm\n        | _ -> trueForm\n\n    and doForm env = function\n        | [a] -> a\n        | a::rest ->\n            eval env a |> ignore\n            doForm env rest\n        | _ -> raise <| Error.wrongArity ()\n\n    and fnStarForm outer nodes =\n        let makeFunc binds body =\n            let f = fun nodes ->\n                        let inner = Env.makeNew outer binds nodes\n                        eval inner body\n            Env.makeFunc f body binds outer\n\n        match nodes with\n        | [List(_, binds); body] -> makeFunc binds body\n        | [Vector(_, seg); body] -> makeFunc (List.ofSeq seg) body\n        | [_; _] -> raise <| Error.errExpectedX \"bindings of list or vector\"\n        | _ -> raise <| Error.wrongArity ()\n\n    and catchForm env err = function\n        | List(_, [Symbol(\"catch*\"); Symbol(_) as sym; catchBody]) ->\n            let inner = Env.makeNew env [sym] [err]\n            catchBody |> eval inner\n        | List(_, [_; _; _]) -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n\n    and tryForm env = function\n        | [exp] ->\n            eval env exp\n        | [exp; catchClause] ->\n            try\n                eval env exp\n            with\n            | Error.EvalError(str)\n            | Error.ReaderError(str) -> catchForm env (String(str)) catchClause\n            | Error.MalError(node) -> catchForm env node catchClause\n        | _ -> raise <| Error.wrongArity ()\n\n    and eval env ast =\n        ignore <| match Env.get env \"DEBUG-EVAL\" with\n                  | None | Some(Bool(false)) | Some(Nil) -> ()\n                  | _ -> Printer.pr_str [ast] |> printfn \"EVAL: %s\"\n        match ast with\n            | Symbol(sym) -> match Env.get env sym with\n                             | Some(value) -> value\n                             | None -> Error.symbolNotFound sym |> raise\n            | Vector(_, seg) -> seg |> Seq.map (eval env) |> Array.ofSeq |> Node.ofArray\n            | Map(_, map) -> map |> Map.map (fun k v -> eval env v) |> makeMap\n            | List(_, Symbol(\"def!\")::rest) -> defBangForm env rest\n            | List(_, Symbol(\"defmacro!\")::rest) -> defMacroForm env rest\n            | List(_, Symbol(\"let*\")::rest) ->\n                let inner, form = letStarForm env rest\n                form |> eval inner\n            | List(_, Symbol(\"if\")::rest) -> ifForm env rest |> eval env\n            | List(_, Symbol(\"do\")::rest) -> doForm env rest |> eval env\n            | List(_, Symbol(\"fn*\")::rest) -> fnStarForm env rest\n            | List(_, Symbol(\"quote\")::rest) -> quoteForm rest\n            | List(_, [Symbol(\"quasiquote\");form]) -> eval env <| quasiquote form\n            | List(_,  Symbol(\"quasiquote\")::_)    -> raise <| Error.wrongArity ()\n            | List(_, Symbol(\"try*\")::rest) -> tryForm env rest\n            | List(_, (a0 :: args)) ->\n                match eval env a0 with\n                | Macro(_, _, f, _, _, _) -> f args |> eval env\n                | BuiltInFunc(_, _, f) -> List.map (eval env) args |> f\n                | Func(_, _, _, body, binds, outer) ->\n                    let inner = List.map (eval env) args |> Env.makeNew outer binds\n                    body |> eval inner\n                | _ -> raise <| Error.errExpectedX \"func\"\n            | _ -> ast\n\n    let READ input =\n        Reader.read_str input\n\n    let EVAL env ast =\n        Some(eval env ast)\n\n    let PRINT v =\n        v\n        |> Seq.singleton\n        |> Printer.pr_str\n        |> printfn \"%s\"\n\n    let RE env input =\n        READ input\n        |> Seq.ofList\n        |> Seq.choose (fun form -> EVAL env form)\n\n    let REP env input =\n        input\n        |> RE env\n        |> Seq.iter (fun value -> PRINT value)\n\n    let getReadlineMode args =\n        if args |> Array.exists (fun e -> e = \"--raw\") then\n            Readline.Mode.Raw\n        else\n            Readline.Mode.Terminal\n\n    let eval_func env = function\n        | [ast] -> eval env ast\n        | _ -> raise <| Error.wrongArity ()\n\n    let argv_func = function\n        | file::rest -> rest |> List.map Types.String |> makeList\n        | [] -> EmptyLIST\n\n    let readline_func mode = function\n        | [String(prompt)] ->\n            match Readline.read prompt mode with\n            | null -> Node.NIL\n            | input -> String(input)\n        | [_] -> raise <| Error.argMismatch ()\n        | _ -> raise <| Error.wrongArity ()\n\n    let configureEnv args mode =\n        let env = Env.makeRootEnv ()\n\n        Env.set env \"eval\" <| Env.makeBuiltInFunc (eval_func env)\n        Env.set env \"*ARGV*\" <| argv_func args\n        Env.set env \"readline\" <| Env.makeBuiltInFunc (readline_func mode)\n\n        RE env \"\"\"\n            (def! *host-language* \"fsharp\")\n            (def! not (fn* (a) (if a false true)))\n            (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n            (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\n            \"\"\" |> Seq.iter ignore\n\n        env\n\n    [<EntryPoint>]\n    let main args =\n        let mode = getReadlineMode args\n        let args = Seq.ofArray args |> Seq.filter (fun e -> e <> \"--raw\") |> List.ofSeq\n        let env = configureEnv args mode\n\n        match args with\n        | file::_ ->\n            System.IO.File.ReadAllText file\n            |> RE env |> Seq.iter ignore\n            0\n        | _ ->\n            RE env \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\" |> Seq.iter ignore\n            let rec loop () =\n                match Readline.read \"user> \" mode with\n                | null -> 0\n                | input ->\n                    try\n                        REP env input\n                    with\n                    | Error.EvalError(str)\n                    | Error.ReaderError(str) ->\n                        printfn \"Error: %s\" str\n                    | Error.MalError(node) ->\n                        printfn \"Error: %s\" (Printer.pr_str [node])\n                    | ex ->\n                        printfn \"Error: %s\" (ex.Message)\n                    loop ()\n            loop ()\n"
  },
  {
    "path": "impls/fsharp/terminal.cs",
    "content": "//\n// getline.cs: A command line editor\n//\n// Authors:\n//   Miguel de Icaza (miguel@novell.com)\n//\n// Copyright 2008 Novell, Inc.\n//\n// Dual-licensed under the terms of the MIT X11 license or the\n// Apache License 2.0\n//\n// USE -define:DEMO to build this as a standalone file and test it\n//\n// TODO:\n//    Enter an error (a = 1);  Notice how the prompt is in the wrong line\n//\t\tThis is caused by Stderr not being tracked by System.Console.\n//    Completion support\n//    Why is Thread.Interrupt not working?   Currently I resort to Abort which is too much.\n//\n// Limitations in System.Console:\n//    Console needs SIGWINCH support of some sort\n//    Console needs a way of updating its position after things have been written\n//    behind its back (P/Invoke puts for example).\n//    System.Console needs to get the DELETE character, and report accordingly.\n//\n\nusing System;\nusing System.Text;\nusing System.IO;\nusing System.Threading;\nusing System.Reflection;\n\nnamespace Mono.Terminal {\n\n\tpublic class LineEditor {\n\n\t\tpublic class Completion {\n\t\t\tpublic string [] Result;\n\t\t\tpublic string Prefix;\n\n\t\t\tpublic Completion (string prefix, string [] result)\n\t\t\t{\n\t\t\t\tPrefix = prefix;\n\t\t\t\tResult = result;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic delegate Completion AutoCompleteHandler (string text, int pos);\n\t\t\n\t\t//static StreamWriter log;\n\t\t\n\t\t// The text being edited.\n\t\tStringBuilder text;\n\n\t\t// The text as it is rendered (replaces (char)1 with ^A on display for example).\n\t\tStringBuilder rendered_text;\n\n\t\t// The prompt specified, and the prompt shown to the user.\n\t\tstring prompt;\n\t\tstring shown_prompt;\n\t\t\n\t\t// The current cursor position, indexes into \"text\", for an index\n\t\t// into rendered_text, use TextToRenderPos\n\t\tint cursor;\n\n\t\t// The row where we started displaying data.\n\t\tint home_row;\n\n\t\t// The maximum length that has been displayed on the screen\n\t\tint max_rendered;\n\n\t\t// If we are done editing, this breaks the interactive loop\n\t\tbool done = false;\n\n\t\t// The thread where the Editing started taking place\n\t\tThread edit_thread;\n\n\t\t// Our object that tracks history\n\t\tHistory history;\n\n\t\t// The contents of the kill buffer (cut/paste in Emacs parlance)\n\t\tstring kill_buffer = \"\";\n\n\t\t// The string being searched for\n\t\tstring search;\n\t\tstring last_search;\n\n\t\t// whether we are searching (-1= reverse; 0 = no; 1 = forward)\n\t\tint searching;\n\n\t\t// The position where we found the match.\n\t\tint match_at;\n\t\t\n\t\t// Used to implement the Kill semantics (multiple Alt-Ds accumulate)\n\t\tKeyHandler last_handler;\n\t\t\n\t\tdelegate void KeyHandler ();\n\t\t\n\t\tstruct Handler {\n\t\t\tpublic ConsoleKeyInfo CKI;\n\t\t\tpublic KeyHandler KeyHandler;\n\n\t\t\tpublic Handler (ConsoleKey key, KeyHandler h)\n\t\t\t{\n\t\t\t\tCKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);\n\t\t\t\tKeyHandler = h;\n\t\t\t}\n\n\t\t\tpublic Handler (char c, KeyHandler h)\n\t\t\t{\n\t\t\t\tKeyHandler = h;\n\t\t\t\t// Use the \"Zoom\" as a flag that we only have a character.\n\t\t\t\tCKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);\n\t\t\t}\n\n\t\t\tpublic Handler (ConsoleKeyInfo cki, KeyHandler h)\n\t\t\t{\n\t\t\t\tCKI = cki;\n\t\t\t\tKeyHandler = h;\n\t\t\t}\n\t\t\t\n\t\t\tpublic static Handler Control (char c, KeyHandler h)\n\t\t\t{\n\t\t\t\treturn new Handler ((char) (c - 'A' + 1), h);\n\t\t\t}\n\n\t\t\tpublic static Handler Alt (char c, ConsoleKey k, KeyHandler h)\n\t\t\t{\n\t\t\t\tConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);\n\t\t\t\treturn new Handler (cki, h);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t///   Invoked when the user requests auto-completion using the tab character\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t///    The result is null for no values found, an array with a single\n\t\t///    string, in that case the string should be the text to be inserted\n\t\t///    for example if the word at pos is \"T\", the result for a completion\n\t\t///    of \"ToString\" should be \"oString\", not \"ToString\".\n\t\t///\n\t\t///    When there are multiple results, the result should be the full\n\t\t///    text\n\t\t/// </remarks>\n\t\tpublic AutoCompleteHandler AutoCompleteEvent;\n\t\t\n\t\tstatic Handler [] handlers;\n\n\t\tpublic LineEditor (string name) : this (name, 10) { }\n\t\t\n\t\tpublic LineEditor (string name, int histsize)\n\t\t{\n\t\t\thandlers = new Handler [] {\n\t\t\t\tnew Handler (ConsoleKey.Home,       CmdHome),\n\t\t\t\tnew Handler (ConsoleKey.End,        CmdEnd),\n\t\t\t\tnew Handler (ConsoleKey.LeftArrow,  CmdLeft),\n\t\t\t\tnew Handler (ConsoleKey.RightArrow, CmdRight),\n\t\t\t\tnew Handler (ConsoleKey.UpArrow,    CmdHistoryPrev),\n\t\t\t\tnew Handler (ConsoleKey.DownArrow,  CmdHistoryNext),\n\t\t\t\tnew Handler (ConsoleKey.Enter,      CmdDone),\n\t\t\t\tnew Handler (ConsoleKey.Backspace,  CmdBackspace),\n\t\t\t\tnew Handler (ConsoleKey.Delete,     CmdDeleteChar),\n\t\t\t\tnew Handler (ConsoleKey.Tab,        CmdTabOrComplete),\n\t\t\t\t\n\t\t\t\t// Emacs keys\n\t\t\t\tHandler.Control ('A', CmdHome),\n\t\t\t\tHandler.Control ('E', CmdEnd),\n\t\t\t\tHandler.Control ('B', CmdLeft),\n\t\t\t\tHandler.Control ('F', CmdRight),\n\t\t\t\tHandler.Control ('P', CmdHistoryPrev),\n\t\t\t\tHandler.Control ('N', CmdHistoryNext),\n\t\t\t\tHandler.Control ('K', CmdKillToEOF),\n\t\t\t\tHandler.Control ('Y', CmdYank),\n\t\t\t\tHandler.Control ('D', CmdDeleteChar),\n\t\t\t\tHandler.Control ('L', CmdRefresh),\n\t\t\t\tHandler.Control ('R', CmdReverseSearch),\n\t\t\t\tHandler.Control ('G', delegate {} ),\n\t\t\t\tHandler.Alt ('B', ConsoleKey.B, CmdBackwardWord),\n\t\t\t\tHandler.Alt ('F', ConsoleKey.F, CmdForwardWord),\n\t\t\t\t\n\t\t\t\tHandler.Alt ('D', ConsoleKey.D, CmdDeleteWord),\n\t\t\t\tHandler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),\n\t\t\t\t\n\t\t\t\t// DEBUG\n\t\t\t\t//Handler.Control ('T', CmdDebug),\n\n\t\t\t\t// quote\n\t\t\t\tHandler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })\n\t\t\t};\n\n\t\t\trendered_text = new StringBuilder ();\n\t\t\ttext = new StringBuilder ();\n\n\t\t\thistory = new History (name, histsize);\n\t\t\t\n\t\t\t//if (File.Exists (\"log\"))File.Delete (\"log\");\n\t\t\t//log = File.CreateText (\"log\"); \n\t\t}\n\n\t\tvoid CmdDebug ()\n\t\t{\n\t\t\thistory.Dump ();\n\t\t\tConsole.WriteLine ();\n\t\t\tRender ();\n\t\t}\n\n\t\tvoid Render ()\n\t\t{\n\t\t\tConsole.Write (shown_prompt);\n\t\t\tConsole.Write (rendered_text);\n\n\t\t\tint max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);\n\t\t\t\n\t\t\tfor (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)\n\t\t\t\tConsole.Write (' ');\n\t\t\tmax_rendered = shown_prompt.Length + rendered_text.Length;\n\n\t\t\t// Write one more to ensure that we always wrap around properly if we are at the\n\t\t\t// end of a line.\n\t\t\tConsole.Write (' ');\n\n\t\t\tUpdateHomeRow (max);\n\t\t}\n\n\t\tvoid UpdateHomeRow (int screenpos)\n\t\t{\n\t\t\tint lines = 1 + (screenpos / Console.WindowWidth);\n\n\t\t\thome_row = Console.CursorTop - (lines - 1);\n\t\t\tif (home_row < 0)\n\t\t\t\thome_row = 0;\n\t\t}\n\t\t\n\n\t\tvoid RenderFrom (int pos)\n\t\t{\n\t\t\tint rpos = TextToRenderPos (pos);\n\t\t\tint i;\n\t\t\t\n\t\t\tfor (i = rpos; i < rendered_text.Length; i++)\n\t\t\t\tConsole.Write (rendered_text [i]);\n\n\t\t\tif ((shown_prompt.Length + rendered_text.Length) > max_rendered)\n\t\t\t\tmax_rendered = shown_prompt.Length + rendered_text.Length;\n\t\t\telse {\n\t\t\t\tint max_extra = max_rendered - shown_prompt.Length;\n\t\t\t\tfor (; i < max_extra; i++)\n\t\t\t\t\tConsole.Write (' ');\n\t\t\t}\n\t\t}\n\n\t\tvoid ComputeRendered ()\n\t\t{\n\t\t\trendered_text.Length = 0;\n\n\t\t\tfor (int i = 0; i < text.Length; i++){\n\t\t\t\tint c = (int) text [i];\n\t\t\t\tif (c < 26){\n\t\t\t\t\tif (c == '\\t')\n\t\t\t\t\t\trendered_text.Append (\"    \");\n\t\t\t\t\telse {\n\t\t\t\t\t\trendered_text.Append ('^');\n\t\t\t\t\t\trendered_text.Append ((char) (c + (int) 'A' - 1));\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\trendered_text.Append ((char)c);\n\t\t\t}\n\t\t}\n\n\t\tint TextToRenderPos (int pos)\n\t\t{\n\t\t\tint p = 0;\n\n\t\t\tfor (int i = 0; i < pos; i++){\n\t\t\t\tint c;\n\n\t\t\t\tc = (int) text [i];\n\t\t\t\t\n\t\t\t\tif (c < 26){\n\t\t\t\t\tif (c == 9)\n\t\t\t\t\t\tp += 4;\n\t\t\t\t\telse\n\t\t\t\t\t\tp += 2;\n\t\t\t\t} else\n\t\t\t\t\tp++;\n\t\t\t}\n\n\t\t\treturn p;\n\t\t}\n\n\t\tint TextToScreenPos (int pos)\n\t\t{\n\t\t\treturn shown_prompt.Length + TextToRenderPos (pos);\n\t\t}\n\t\t\n\t\tstring Prompt {\n\t\t\tget { return prompt; }\n\t\t\tset { prompt = value; }\n\t\t}\n\n\t\tint LineCount {\n\t\t\tget {\n\t\t\t\treturn (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid ForceCursor (int newpos)\n\t\t{\n\t\t\tcursor = newpos;\n\n\t\t\tint actual_pos = shown_prompt.Length + TextToRenderPos (cursor);\n\t\t\tint row = home_row + (actual_pos/Console.WindowWidth);\n\t\t\tint col = actual_pos % Console.WindowWidth;\n\n\t\t\tif (row >= Console.BufferHeight)\n\t\t\t\trow = Console.BufferHeight-1;\n\t\t\tConsole.SetCursorPosition (col, row);\n\t\t\t\n\t\t\t//log.WriteLine (\"Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}\", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);\n\t\t\t//log.Flush ();\n\t\t}\n\n\t\tvoid UpdateCursor (int newpos)\n\t\t{\n\t\t\tif (cursor == newpos)\n\t\t\t\treturn;\n\n\t\t\tForceCursor (newpos);\n\t\t}\n\n\t\tvoid InsertChar (char c)\n\t\t{\n\t\t\tint prev_lines = LineCount;\n\t\t\ttext = text.Insert (cursor, c);\n\t\t\tComputeRendered ();\n\t\t\tif (prev_lines != LineCount){\n\n\t\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\t\tRender ();\n\t\t\t\tForceCursor (++cursor);\n\t\t\t} else {\n\t\t\t\tRenderFrom (cursor);\n\t\t\t\tForceCursor (++cursor);\n\t\t\t\tUpdateHomeRow (TextToScreenPos (cursor));\n\t\t\t}\n\t\t}\n\n\t\t//\n\t\t// Commands\n\t\t//\n\t\tvoid CmdDone ()\n\t\t{\n\t\t\tdone = true;\n\t\t}\n\n\t\tvoid CmdTabOrComplete ()\n\t\t{\n\t\t\tbool complete = false;\n\n\t\t\tif (AutoCompleteEvent != null){\n\t\t\t\tif (TabAtStartCompletes)\n\t\t\t\t\tcomplete = true;\n\t\t\t\telse {\n\t\t\t\t\tfor (int i = 0; i < cursor; i++){\n\t\t\t\t\t\tif (!Char.IsWhiteSpace (text [i])){\n\t\t\t\t\t\t\tcomplete = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (complete){\n\t\t\t\t\tCompletion completion = AutoCompleteEvent (text.ToString (), cursor);\n\t\t\t\t\tstring [] completions = completion.Result;\n\t\t\t\t\tif (completions == null)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tint ncompletions = completions.Length;\n\t\t\t\t\tif (ncompletions == 0)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tif (completions.Length == 1){\n\t\t\t\t\t\tInsertTextAtCursor (completions [0]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint last = -1;\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (int p = 0; p < completions [0].Length; p++){\n\t\t\t\t\t\t\tchar c = completions [0][p];\n\n\n\t\t\t\t\t\t\tfor (int i = 1; i < ncompletions; i++){\n\t\t\t\t\t\t\t\tif (completions [i].Length < p)\n\t\t\t\t\t\t\t\t\tgoto mismatch;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (completions [i][p] != c){\n\t\t\t\t\t\t\t\t\tgoto mismatch;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlast = p;\n\t\t\t\t\t\t}\n\t\t\t\t\tmismatch:\n\t\t\t\t\t\tif (last != -1){\n\t\t\t\t\t\t\tInsertTextAtCursor (completions [0].Substring (0, last+1));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\t\tforeach (string s in completions){\n\t\t\t\t\t\t\tConsole.Write (completion.Prefix);\n\t\t\t\t\t\t\tConsole.Write (s);\n\t\t\t\t\t\t\tConsole.Write (' ');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\t\tRender ();\n\t\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\tHandleChar ('\\t');\n\t\t\t} else\n\t\t\t\tHandleChar ('t');\n\t\t}\n\t\t\n\t\tvoid CmdHome ()\n\t\t{\n\t\t\tUpdateCursor (0);\n\t\t}\n\n\t\tvoid CmdEnd ()\n\t\t{\n\t\t\tUpdateCursor (text.Length);\n\t\t}\n\t\t\n\t\tvoid CmdLeft ()\n\t\t{\n\t\t\tif (cursor == 0)\n\t\t\t\treturn;\n\n\t\t\tUpdateCursor (cursor-1);\n\t\t}\n\n\t\tvoid CmdBackwardWord ()\n\t\t{\n\t\t\tint p = WordBackward (cursor);\n\t\t\tif (p == -1)\n\t\t\t\treturn;\n\t\t\tUpdateCursor (p);\n\t\t}\n\n\t\tvoid CmdForwardWord ()\n\t\t{\n\t\t\tint p = WordForward (cursor);\n\t\t\tif (p == -1)\n\t\t\t\treturn;\n\t\t\tUpdateCursor (p);\n\t\t}\n\n\t\tvoid CmdRight ()\n\t\t{\n\t\t\tif (cursor == text.Length)\n\t\t\t\treturn;\n\n\t\t\tUpdateCursor (cursor+1);\n\t\t}\n\n\t\tvoid RenderAfter (int p)\n\t\t{\n\t\t\tForceCursor (p);\n\t\t\tRenderFrom (p);\n\t\t\tForceCursor (cursor);\n\t\t}\n\t\t\n\t\tvoid CmdBackspace ()\n\t\t{\n\t\t\tif (cursor == 0)\n\t\t\t\treturn;\n\n\t\t\ttext.Remove (--cursor, 1);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tvoid CmdDeleteChar ()\n\t\t{\n\t\t\t// If there is no input, this behaves like EOF\n\t\t\tif (text.Length == 0){\n\t\t\t\tdone = true;\n\t\t\t\ttext = null;\n\t\t\t\tConsole.WriteLine ();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (cursor == text.Length)\n\t\t\t\treturn;\n\t\t\ttext.Remove (cursor, 1);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tint WordForward (int p)\n\t\t{\n\t\t\tif (p >= text.Length)\n\t\t\t\treturn -1;\n\n\t\t\tint i = p;\n\t\t\tif (Char.IsPunctuation (text [p]) || Char.IsSymbol (text [p]) || Char.IsWhiteSpace (text[p])){\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i != p)\n\t\t\t\treturn i;\n\t\t\treturn -1;\n\t\t}\n\n\t\tint WordBackward (int p)\n\t\t{\n\t\t\tif (p == 0)\n\t\t\t\treturn -1;\n\n\t\t\tint i = p-1;\n\t\t\tif (i == 0)\n\t\t\t\treturn 0;\n\t\t\t\n\t\t\tif (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text[i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t\t\n\t\t\tif (i != p)\n\t\t\t\treturn i;\n\n\t\t\treturn -1;\n\t\t}\n\t\t\n\t\tvoid CmdDeleteWord ()\n\t\t{\n\t\t\tint pos = WordForward (cursor);\n\n\t\t\tif (pos == -1)\n\t\t\t\treturn;\n\n\t\t\tstring k = text.ToString (cursor, pos-cursor);\n\t\t\t\n\t\t\tif (last_handler == CmdDeleteWord)\n\t\t\t\tkill_buffer = kill_buffer + k;\n\t\t\telse\n\t\t\t\tkill_buffer = k;\n\t\t\t\n\t\t\ttext.Remove (cursor, pos-cursor);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\t\t\n\t\tvoid CmdDeleteBackword ()\n\t\t{\n\t\t\tint pos = WordBackward (cursor);\n\t\t\tif (pos == -1)\n\t\t\t\treturn;\n\n\t\t\tstring k = text.ToString (pos, cursor-pos);\n\t\t\t\n\t\t\tif (last_handler == CmdDeleteBackword)\n\t\t\t\tkill_buffer = k + kill_buffer;\n\t\t\telse\n\t\t\t\tkill_buffer = k;\n\t\t\t\n\t\t\ttext.Remove (pos, cursor-pos);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (pos);\n\t\t}\n\t\t\n\t\t//\n\t\t// Adds the current line to the history if needed\n\t\t//\n\t\tvoid HistoryUpdateLine ()\n\t\t{\n\t\t\thistory.Update (text.ToString ());\n\t\t}\n\t\t\n\t\tvoid CmdHistoryPrev ()\n\t\t{\n\t\t\tif (!history.PreviousAvailable ())\n\t\t\t\treturn;\n\n\t\t\tHistoryUpdateLine ();\n\t\t\t\n\t\t\tSetText (history.Previous ());\n\t\t}\n\n\t\tvoid CmdHistoryNext ()\n\t\t{\n\t\t\tif (!history.NextAvailable())\n\t\t\t\treturn;\n\n\t\t\thistory.Update (text.ToString ());\n\t\t\tSetText (history.Next ());\n\t\t\t\n\t\t}\n\n\t\tvoid CmdKillToEOF ()\n\t\t{\n\t\t\tkill_buffer = text.ToString (cursor, text.Length-cursor);\n\t\t\ttext.Length = cursor;\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tvoid CmdYank ()\n\t\t{\n\t\t\tInsertTextAtCursor (kill_buffer);\n\t\t}\n\n\t\tvoid InsertTextAtCursor (string str)\n\t\t{\n\t\t\tint prev_lines = LineCount;\n\t\t\ttext.Insert (cursor, str);\n\t\t\tComputeRendered ();\n\t\t\tif (prev_lines != LineCount){\n\t\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\t\tRender ();\n\t\t\t\tcursor += str.Length;\n\t\t\t\tForceCursor (cursor);\n\t\t\t} else {\n\t\t\t\tRenderFrom (cursor);\n\t\t\t\tcursor += str.Length;\n\t\t\t\tForceCursor (cursor);\n\t\t\t\tUpdateHomeRow (TextToScreenPos (cursor));\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid SetSearchPrompt (string s)\n\t\t{\n\t\t\tSetPrompt (\"(reverse-i-search)`\" + s + \"': \");\n\t\t}\n\n\t\tvoid ReverseSearch ()\n\t\t{\n\t\t\tint p;\n\n\t\t\tif (cursor == text.Length){\n\t\t\t\t// The cursor is at the end of the string\n\t\t\t\t\n\t\t\t\tp = text.ToString ().LastIndexOf (search);\n\t\t\t\tif (p != -1){\n\t\t\t\t\tmatch_at = p;\n\t\t\t\t\tcursor = p;\n\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// The cursor is somewhere in the middle of the string\n\t\t\t\tint start = (cursor == match_at) ? cursor - 1 : cursor;\n\t\t\t\tif (start != -1){\n\t\t\t\t\tp = text.ToString ().LastIndexOf (search, start);\n\t\t\t\t\tif (p != -1){\n\t\t\t\t\t\tmatch_at = p;\n\t\t\t\t\t\tcursor = p;\n\t\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Need to search backwards in history\n\t\t\tHistoryUpdateLine ();\n\t\t\tstring s = history.SearchBackward (search);\n\t\t\tif (s != null){\n\t\t\t\tmatch_at = -1;\n\t\t\t\tSetText (s);\n\t\t\t\tReverseSearch ();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid CmdReverseSearch ()\n\t\t{\n\t\t\tif (searching == 0){\n\t\t\t\tmatch_at = -1;\n\t\t\t\tlast_search = search;\n\t\t\t\tsearching = -1;\n\t\t\t\tsearch = \"\";\n\t\t\t\tSetSearchPrompt (\"\");\n\t\t\t} else {\n\t\t\t\tif (search == \"\"){\n\t\t\t\t\tif (last_search != \"\" && last_search != null){\n\t\t\t\t\t\tsearch = last_search;\n\t\t\t\t\t\tSetSearchPrompt (search);\n\n\t\t\t\t\t\tReverseSearch ();\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tReverseSearch ();\n\t\t\t} \n\t\t}\n\n\t\tvoid SearchAppend (char c)\n\t\t{\n\t\t\tsearch = search + c;\n\t\t\tSetSearchPrompt (search);\n\n\t\t\t//\n\t\t\t// If the new typed data still matches the current text, stay here\n\t\t\t//\n\t\t\tif (cursor < text.Length){\n\t\t\t\tstring r = text.ToString (cursor, text.Length - cursor);\n\t\t\t\tif (r.StartsWith (search))\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tReverseSearch ();\n\t\t}\n\t\t\n\t\tvoid CmdRefresh ()\n\t\t{\n\t\t\tConsole.Clear ();\n\t\t\tmax_rendered = 0;\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\n\t\tvoid InterruptEdit (object sender, ConsoleCancelEventArgs a)\n\t\t{\n\t\t\t// Do not abort our program:\n\t\t\ta.Cancel = true;\n\n\t\t\t// Interrupt the editor\n\t\t\tedit_thread.Abort();\n\t\t}\n\n\t\tvoid HandleChar (char c)\n\t\t{\n\t\t\tif (searching != 0)\n\t\t\t\tSearchAppend (c);\n\t\t\telse\n\t\t\t\tInsertChar (c);\n\t\t}\n\n\t\tvoid EditLoop ()\n\t\t{\n\t\t\tConsoleKeyInfo cki;\n\n\t\t\twhile (!done){\n\t\t\t\tConsoleModifiers mod;\n\t\t\t\t\n\t\t\t\tcki = Console.ReadKey (true);\n\t\t\t\tif (cki.Key == ConsoleKey.Escape){\n\t\t\t\t\tcki = Console.ReadKey (true);\n\n\t\t\t\t\tmod = ConsoleModifiers.Alt;\n\t\t\t\t} else\n\t\t\t\t\tmod = cki.Modifiers;\n\t\t\t\t\n\t\t\t\tbool handled = false;\n\n\t\t\t\tforeach (Handler handler in handlers){\n\t\t\t\t\tConsoleKeyInfo t = handler.CKI;\n\n\t\t\t\t\tif (t.Key == cki.Key && t.Modifiers == mod){\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\thandler.KeyHandler ();\n\t\t\t\t\t\tlast_handler = handler.KeyHandler;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\thandler.KeyHandler ();\n\t\t\t\t\t\tlast_handler = handler.KeyHandler;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (handled){\n\t\t\t\t\tif (searching != 0){\n\t\t\t\t\t\tif (last_handler != CmdReverseSearch){\n\t\t\t\t\t\t\tsearching = 0;\n\t\t\t\t\t\t\tSetPrompt (prompt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (cki.KeyChar != (char) 0)\n\t\t\t\t\tHandleChar (cki.KeyChar);\n\t\t\t} \n\t\t}\n\n\t\tvoid InitText (string initial)\n\t\t{\n\t\t\ttext = new StringBuilder (initial);\n\t\t\tComputeRendered ();\n\t\t\tcursor = text.Length;\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\n\t\tvoid SetText (string newtext)\n\t\t{\n\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\tInitText (newtext);\n\t\t}\n\n\t\tvoid SetPrompt (string newprompt)\n\t\t{\n\t\t\tshown_prompt = newprompt;\n\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\t\t\n\t\tpublic string Edit (string prompt, string initial)\n\t\t{\n\t\t\tedit_thread = Thread.CurrentThread;\n\t\t\tsearching = 0;\n\t\t\tConsole.CancelKeyPress += InterruptEdit;\n\t\t\t\n\t\t\tdone = false;\n\t\t\thistory.CursorToEnd ();\n\t\t\tmax_rendered = 0;\n\t\t\t\n\t\t\tPrompt = prompt;\n\t\t\tshown_prompt = prompt;\n\t\t\tInitText (initial);\n\t\t\thistory.Append (initial);\n\n\t\t\tdo {\n\t\t\t\ttry {\n\t\t\t\t\tEditLoop ();\n\t\t\t\t} catch (ThreadAbortException){\n\t\t\t\t\tsearching = 0;\n\t\t\t\t\tThread.ResetAbort ();\n\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\tSetPrompt (prompt);\n\t\t\t\t\tSetText (\"\");\n\t\t\t\t}\n\t\t\t} while (!done);\n\t\t\tConsole.WriteLine ();\n\t\t\t\n\t\t\tConsole.CancelKeyPress -= InterruptEdit;\n\n\t\t\tif (text == null){\n\t\t\t\thistory.Close ();\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tstring result = text.ToString ();\n\t\t\tif (result != \"\")\n\t\t\t\thistory.Accept (result);\n\t\t\telse\n\t\t\t\thistory.RemoveLast ();\n\n\t\t\treturn result;\n\t\t}\n\t\t\n\t\tpublic void SaveHistory ()\n\t\t{\n\t\t\tif (history != null) {\n\t\t\t\thistory.Close ();\n\t\t\t}\n\t\t}\n\n\t\tpublic bool TabAtStartCompletes { get; set; }\n\t\t\t\n\t\t//\n\t\t// Emulates the bash-like behavior, where edits done to the\n\t\t// history are recorded\n\t\t//\n\t\tclass History {\n\t\t\tstring [] history;\n\t\t\tint head, tail;\n\t\t\tint cursor, count;\n\t\t\tstring histfile;\n\t\t\t\n\t\t\tpublic History (string app, int size)\n\t\t\t{\n\t\t\t\tif (size < 1)\n\t\t\t\t\tthrow new ArgumentException (\"size\");\n\n\t\t\t\tif (app != null){\n\t\t\t\t\tstring dir = Environment.GetFolderPath (Environment.SpecialFolder.Personal);\n\t\t\t\t\t//Console.WriteLine (dir);\n\t\t\t\t\t/*\n\t\t\t\t\tif (!Directory.Exists (dir)){\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tDirectory.CreateDirectory (dir);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tapp = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (app != null)\n\t\t\t\t\t\thistfile = Path.Combine (dir, app) + \".history\";\n\t\t\t\t\t*/\n\t\t\t\t\thistfile = Path.Combine (dir, \".mal-history\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\thistory = new string [size];\n\t\t\t\thead = tail = cursor = 0;\n\n\t\t\t\tif (File.Exists (histfile)){\n\t\t\t\t\tusing (StreamReader sr = File.OpenText (histfile)){\n\t\t\t\t\t\tstring line;\n\t\t\t\t\t\t\n\t\t\t\t\t\twhile ((line = sr.ReadLine ()) != null){\n\t\t\t\t\t\t\tif (line != \"\")\n\t\t\t\t\t\t\t\tAppend (line);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void Close ()\n\t\t\t{\n\t\t\t\tif (histfile == null)\n\t\t\t\t\treturn;\n\n\t\t\t\ttry {\n\t\t\t\t\tusing (StreamWriter sw = File.CreateText (histfile)){\n\t\t\t\t\t\tint start = (count == history.Length) ? head : tail;\n\t\t\t\t\t\tfor (int i = start; i < start+count; i++){\n\t\t\t\t\t\t\tint p = i % history.Length;\n\t\t\t\t\t\t\tsw.WriteLine (history [p]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//\n\t\t\t// Appends a value to the history\n\t\t\t//\n\t\t\tpublic void Append (string s)\n\t\t\t{\n\t\t\t\t//Console.WriteLine (\"APPENDING {0} head={1} tail={2}\", s, head, tail);\n\t\t\t\thistory [head] = s;\n\t\t\t\thead = (head+1) % history.Length;\n\t\t\t\tif (head == tail)\n\t\t\t\t\ttail = (tail+1 % history.Length);\n\t\t\t\tif (count != history.Length)\n\t\t\t\t\tcount++;\n\t\t\t\t//Console.WriteLine (\"DONE: head={1} tail={2}\", s, head, tail);\n\t\t\t}\n\n\t\t\t//\n\t\t\t// Updates the current cursor location with the string,\n\t\t\t// to support editing of history items.   For the current\n\t\t\t// line to participate, an Append must be done before.\n\t\t\t//\n\t\t\tpublic void Update (string s)\n\t\t\t{\n\t\t\t\thistory [cursor] = s;\n\t\t\t}\n\n\t\t\tpublic void RemoveLast ()\n\t\t\t{\n\t\t\t\thead = head-1;\n\t\t\t\tif (head < 0)\n\t\t\t\t\thead = history.Length-1;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Accept (string s)\n\t\t\t{\n\t\t\t\tint t = head-1;\n\t\t\t\tif (t < 0)\n\t\t\t\t\tt = history.Length-1;\n\t\t\t\t\n\t\t\t\thistory [t] = s;\n\t\t\t}\n\t\t\t\n\t\t\tpublic bool PreviousAvailable ()\n\t\t\t{\n\t\t\t\t//Console.WriteLine (\"h={0} t={1} cursor={2}\", head, tail, cursor);\n\t\t\t\tif (count == 0)\n\t\t\t\t\treturn false;\n\t\t\t\tint next = cursor-1;\n\t\t\t\tif (next < 0)\n\t\t\t\t\tnext = count-1;\n\n\t\t\t\tif (next == head)\n\t\t\t\t\treturn false;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tpublic bool NextAvailable ()\n\t\t\t{\n\t\t\t\tif (count == 0)\n\t\t\t\t\treturn false;\n\t\t\t\tint next = (cursor + 1) % history.Length;\n\t\t\t\tif (next == head)\n\t\t\t\t\treturn false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\t\n\t\t\t//\n\t\t\t// Returns: a string with the previous line contents, or\n\t\t\t// nul if there is no data in the history to move to.\n\t\t\t//\n\t\t\tpublic string Previous ()\n\t\t\t{\n\t\t\t\tif (!PreviousAvailable ())\n\t\t\t\t\treturn null;\n\n\t\t\t\tcursor--;\n\t\t\t\tif (cursor < 0)\n\t\t\t\t\tcursor = history.Length - 1;\n\n\t\t\t\treturn history [cursor];\n\t\t\t}\n\n\t\t\tpublic string Next ()\n\t\t\t{\n\t\t\t\tif (!NextAvailable ())\n\t\t\t\t\treturn null;\n\n\t\t\t\tcursor = (cursor + 1) % history.Length;\n\t\t\t\treturn history [cursor];\n\t\t\t}\n\n\t\t\tpublic void CursorToEnd ()\n\t\t\t{\n\t\t\t\tif (head == tail)\n\t\t\t\t\treturn;\n\n\t\t\t\tcursor = head;\n\t\t\t}\n\n\t\t\tpublic void Dump ()\n\t\t\t{\n\t\t\t\tConsole.WriteLine (\"Head={0} Tail={1} Cursor={2} count={3}\", head, tail, cursor, count);\n\t\t\t\tfor (int i = 0; i < history.Length;i++){\n\t\t\t\t\tConsole.WriteLine (\" {0} {1}: {2}\", i == cursor ? \"==>\" : \"   \", i, history[i]);\n\t\t\t\t}\n\t\t\t\t//log.Flush ();\n\t\t\t}\n\n\t\t\tpublic string SearchBackward (string term)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < count; i++){\n\t\t\t\t\tint slot = cursor-i-1;\n\t\t\t\t\tif (slot < 0)\n\t\t\t\t\t\tslot = history.Length+slot;\n\t\t\t\t\tif (slot >= history.Length)\n\t\t\t\t\t\tslot = 0;\n\t\t\t\t\tif (history [slot] != null && history [slot].IndexOf (term) != -1){\n\t\t\t\t\t\tcursor = slot;\n\t\t\t\t\t\treturn history [slot];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\n#if DEMO\n\tclass Demo {\n\t\tstatic void Main ()\n\t\t{\n\t\t\tLineEditor le = new LineEditor (\"foo\");\n\t\t\tstring s;\n\t\t\t\n\t\t\twhile ((s = le.Edit (\"shell> \", \"\")) != null){\n\t\t\t\tConsole.WriteLine (\"----> [{0}]\", s);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "impls/fsharp/tests/step5_tco.mal",
    "content": ";; F#: skipping non-TCO recursion\n;; Reason: completes at 10,000, unrecoverable segfault at 20,000\n"
  },
  {
    "path": "impls/fsharp/tokenizer.fs",
    "content": "module Tokenizer\n\n    open System\n    open Types\n    \n    type Token =\n        | EOF\n        | OpenBracket | CloseBracket\n        | OpenBrace | CloseBrace\n        | OpenParen | CloseParen\n        | SingleQuote\n        | Backtick\n        | Tilde | SpliceUnquote\n        | Caret\n        | At\n        | String of string\n        | Token of string\n        | Keyword of string\n        | Number of string\n\n\n    let tokenize (str : string) =\n        let len = str.Length\n        \n        let inline isWhiteSpace ch = ch = ',' || Char.IsWhiteSpace(ch)\n        let inline isNotNewline ch = ch <> '\\r' && ch <> '\\n'\n        let inline isDigit ch = Char.IsDigit(ch)\n        let inline isTokenChar ch =\n            match ch with\n            | '[' | ']' | '{' | '}' | '(' | ')'\n            | '\\'' | '\"' | '`' | ',' | ';' -> false\n            | ch when Char.IsWhiteSpace(ch) -> false\n            | _ -> true\n       \n        let rec skipWhile pred p =\n            if p >= len then p\n            elif pred (str.[p]) then p + 1 |> skipWhile pred\n            else p\n\n        let rec accumulateWhile pred (f : string -> Token) start p =\n            if p >= len then str.Substring(start, p - start) |> f, p\n            elif pred (str.[p]) then p + 1 |> accumulateWhile pred f start\n            else str.Substring(start, p - start) |> f, p\n\n        let accumulateString p =\n            let b = System.Text.StringBuilder()\n            let rec accChar (ch : char) n =\n                b.Append(ch) |> ignore\n                accChars n\n            and accChars p =\n                let n = p + 1\n                if p >= len then raise <| Error.expectedXButEOF \"'\\\"'\"\n                match str.[p] with\n                | '\\\\' -> accEscaped n\n                | '\"' -> n\n                | ch -> accChar ch n\n            and accEscaped p =\n                let n = p + 1\n                if p >= len then raise <| Error.expectedXButEOF \"char\"\n                match str.[p] with\n                | 't'  -> accChar '\\t' n\n                | 'b'  -> accChar '\\b' n\n                | 'n'  -> accChar '\\n' n\n                | 'r'  -> accChar '\\r' n\n                | 'f'  -> accChar '\\f' n\n                | '\\'' -> accChar '\\'' n\n                | '\"'  -> accChar '\"' n\n                | '\\\\' -> accChar '\\\\' n\n                | _ -> raise <| Error.expectedXButEOF \"valid escape char\"\n            let n = accChars p\n            String(b.ToString()), n\n\n        let accumulateKeyword p =\n            let n = p + 1\n            if p >= len then raise <| Error.expectedXButEOF \"keyword\"\n            elif isTokenChar str.[p] then accumulateWhile isTokenChar Keyword p n\n            else raise <| Error.expectedX \"keyword char\"\n\n        let accumulateSpliceUnquote p =\n            if p >= len then Tilde, p\n            elif str.[p] = '@' then SpliceUnquote, (p + 1)\n            else Tilde, p\n\n        let rec getToken p =\n            if p >= len then\n                EOF, p\n            else\n                let n = p + 1\n                match str.[p] with\n                | ch when isWhiteSpace ch -> getToken n\n                | ';' -> skipWhile isNotNewline n |> getToken\n                | '[' -> OpenBracket, n\n                | ']' -> CloseBracket, n\n                | '{' -> OpenBrace, n\n                | '}' -> CloseBrace, n\n                | '(' -> OpenParen, n\n                | ')' -> CloseParen, n\n                | '\\'' -> SingleQuote, n\n                | '`' -> Backtick, n\n                | '~' -> accumulateSpliceUnquote n\n                | '^' -> Caret, n\n                | '@' -> At, n\n                | '\"' -> accumulateString n\n                | ':' -> accumulateKeyword n\n                | '-' when n < len && isDigit str.[n] -> accumulateWhile isDigit Number p n\n                | ch when isDigit ch -> accumulateWhile isDigit Number p n\n                | ch when isTokenChar ch -> accumulateWhile isTokenChar Token p n\n                | _ -> raise <| Error.unexpectedChar ()\n\n        let rec accumulate acc p = \n            match getToken p with\n            | EOF, p -> List.rev acc\n            | tok, p -> accumulate (tok::acc) p\n\n        accumulate [] 0\n"
  },
  {
    "path": "impls/fsharp/types.fs",
    "content": "module Types\n\n    [<CustomEquality; CustomComparison>]\n    type Node =\n        | Nil\n        | List of Metadata * Node list\n        | Vector of Metadata * Node System.ArraySegment\n        | Map of Metadata * Collections.Map<Node, Node>\n        | Symbol of string\n        | Keyword of string\n        | Number of int64\n        | String of string\n        | Bool of bool\n        | BuiltInFunc of Metadata * int * (Node list -> Node)\n        | Func of Metadata * int * (Node list -> Node) * Node * Node list * EnvChain\n        | Macro of Metadata * int * (Node list -> Node) * Node * Node list * EnvChain\n        | Atom of int * Node Ref\n\n        static member private hashSeq (s : seq<Node>) =\n            let iter st node = (st * 397) ^^^ node.GetHashCode()\n            s |> Seq.fold iter 0\n\n        static member private allEqual (x : seq<Node>) (y : seq<Node>) =\n            use ex = x.GetEnumerator()\n            use ey = y.GetEnumerator()\n            let rec loop () =\n                match ex.MoveNext(), ey.MoveNext() with\n                | false, false -> true\n                | false, true\n                | true, false -> false\n                | true, true ->\n                    if ex.Current = ey.Current then\n                        loop ()\n                    else\n                        false\n            loop ()\n\n        static member private allCompare (x : seq<Node>) (y : seq<Node>) =\n            use ex = x.GetEnumerator()\n            use ey = y.GetEnumerator()\n            let rec loop () =\n                match ex.MoveNext(), ey.MoveNext() with\n                | false, false -> 0\n                | false, true -> -1\n                | true, false -> 1\n                | true, true ->\n                    let cmp = compare ex.Current ey.Current\n                    if cmp = 0 then loop () else cmp\n            loop ()\n\n        static member private rank x =\n            match x with\n            | Nil -> 0\n            | List(_, _) -> 1\n            | Vector(_, _) -> 2\n            | Map(_, _) -> 3\n            | Symbol(_) -> 4\n            | Keyword(_) -> 5\n            | Number(_) -> 6\n            | String(_) -> 7\n            | Bool(_) -> 8\n            | BuiltInFunc(_, _, _)\n            | Func(_, _, _, _, _, _)\n            | Macro(_, _, _, _, _, _) -> 9\n            | Atom(_, _) -> 10\n\n        static member private equals x y =\n            match x, y with\n            | Nil, Nil -> true\n            | List(_, a), List(_, b) -> a = b\n            | List(_, a), Vector(_, b) -> Node.allEqual a b\n            | Vector(_, a), List(_, b) -> Node.allEqual a b\n            | Vector(_, a), Vector(_, b) -> Node.allEqual a b\n            | Map(_, a), Map(_, b) -> a = b\n            | Symbol(a), Symbol(b) -> a = b\n            | Keyword(a), Keyword(b) -> a = b\n            | Number(a), Number(b) -> a = b\n            | String(a), String(b) -> a = b\n            | Bool(a), Bool(b) -> a = b\n            | (BuiltInFunc(_, a, _) | Func(_, a, _, _, _, _) | Macro(_, a, _, _, _, _)),\n              (BuiltInFunc(_, b, _) | Func(_, b, _, _, _, _) | Macro(_, b, _, _, _, _)) ->\n                a = b\n            | Atom(a, _), Atom(b, _) -> a = b\n            | _, _ -> false\n\n        static member private compare x y =\n            match x, y with\n            | Nil, Nil -> 0\n            | List(_, a), List(_, b) -> compare a b\n            | List(_, a), Vector(_, b) -> Node.allCompare a b\n            | Vector(_, a), List(_, b) -> Node.allCompare a b\n            | Vector(_, a), Vector(_, b) -> Node.allCompare a b\n            | Map(_, a), Map(_, b) -> compare a b\n            | Symbol(a), Symbol(b) -> compare a b\n            | Keyword(a), Keyword(b) -> compare a b\n            | Number(a), Number(b) -> compare a b\n            | String(a), String(b) -> compare a b\n            | Bool(a), Bool(b) -> compare a b\n            | (BuiltInFunc(_, a, _) | Func(_, a, _, _, _, _) | Macro(_, a, _, _, _, _)),\n              (BuiltInFunc(_, b, _) | Func(_, b, _, _, _, _) | Macro(_, b, _, _, _, _)) ->\n                compare a b\n            | Atom(a, _), Atom(b, _) -> compare a b\n            | a, b -> compare (Node.rank a) (Node.rank b)\n\n        override x.Equals yobj =\n            match yobj with\n            | :? Node as y -> Node.equals x y\n            | _ -> false\n\n        override x.GetHashCode() =\n            match x with\n            | Nil -> 0\n            | List(_, lst) -> hash lst\n            | Vector(_, vec) -> Node.hashSeq vec\n            | Map(_, map) -> hash map\n            | Symbol(sym) -> hash sym\n            | Keyword(key) -> hash key\n            | Number(num) -> hash num\n            | String(str) -> hash str\n            | Bool(b) -> hash b\n            | BuiltInFunc(_, tag, _) | Func(_, tag, _, _, _, _) | Macro(_, tag, _, _, _, _) ->\n                hash tag\n            | Atom(tag, _) -> hash tag\n\n        interface System.IComparable with\n            member x.CompareTo yobj =\n                match yobj with\n                | :? Node as y -> Node.compare x y\n                | _ -> invalidArg \"yobj\" \"Cannot compare values of different types.\"\n\n\n    and Env = System.Collections.Generic.Dictionary<string, Node>\n    and EnvChain = Env list\n    and Metadata = Node\n"
  },
  {
    "path": "impls/gnu-smalltalk/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install gnu-smalltalk libreadline-dev\n"
  },
  {
    "path": "impls/gnu-smalltalk/Makefile",
    "content": "all:\n\nclean:\n"
  },
  {
    "path": "impls/gnu-smalltalk/core.st",
    "content": "Object subclass: Core [\n    Ns := Dictionary new.\n    Core class >> Ns [ ^Ns ]\n\n    Core class >> coerce: block [\n        block value ifTrue: [ ^MALObject True ] ifFalse: [ ^MALObject False ]\n    ]\n\n    Core class >> nilable: args else: block [\n        args first type = #nil ifTrue: [\n            ^MALObject Nil\n        ] ifFalse: [\n            ^block value\n        ]\n    ]\n\n    Core class >> printedArgs: args readable: readable sep: sep [\n        | items |\n        items := args collect:\n            [ :arg | Printer prStr: arg printReadably: readable ].\n        \"NOTE: {} join returns the unchanged array\"\n        items isEmpty ifTrue: [ ^'' ] ifFalse: [ ^items join: sep ]\n    ]\n]\n\nCore Ns at: #+ put:\n    (Fn new: [ :args | MALNumber new: args first value + args second value ]).\nCore Ns at: #- put:\n    (Fn new: [ :args | MALNumber new: args first value - args second value ]).\nCore Ns at: #* put:\n    (Fn new: [ :args | MALNumber new: args first value * args second value ]).\nCore Ns at: #/ put:\n    (Fn new: [ :args | MALNumber new: args first value // args second value ]).\n\nCore Ns at: #'pr-str' put:\n    (Fn new: [ :args | MALString new: (Core printedArgs: args readable: true\n                                            sep: ' ') ]).\nCore Ns at: #str put:\n    (Fn new: [ :args | MALString new: (Core printedArgs: args readable: false\n                                           sep: '') ]).\nCore Ns at: #prn put:\n    (Fn new: [ :args |\n        (Core printedArgs: args readable: true sep: ' ') displayNl.\n        MALObject Nil ]).\nCore Ns at: #println put:\n    (Fn new: [ :args |\n        (Core printedArgs: args readable: false sep: ' ') displayNl.\n        MALObject Nil ]).\n\nCore Ns at: #list put:\n    (Fn new: [ :args | MALList new: (OrderedCollection from: args) ]).\nCore Ns at: #'list?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #list ] ]).\nCore Ns at: #'empty?' put:\n    (Fn new: [ :args | Core coerce: [ args first value isEmpty ] ]).\nCore Ns at: #count put:\n    (Fn new: [ :args | MALNumber new: args first value size ]).\n\nCore Ns at: #= put:\n    (Fn new: [ :args | Core coerce: [ args first = args second ] ]).\n\nCore Ns at: #< put:\n    (Fn new: [ :args | Core coerce: [ args first value < args second value ] ]).\nCore Ns at: #<= put:\n    (Fn new: [ :args | Core coerce: [ args first value <= args second value ] ]).\nCore Ns at: #> put:\n    (Fn new: [ :args | Core coerce: [ args first value > args second value ] ]).\nCore Ns at: #>= put:\n    (Fn new: [ :args | Core coerce: [ args first value >= args second value ] ]).\n\nCore Ns at: #'read-string' put:\n    (Fn new: [ :args | Reader readStr: args first value ]).\nCore Ns at: #slurp put:\n    (Fn new: [ :args | MALString new: (File path: args first value) contents ]).\nCore Ns at: #throw put:\n    (Fn new: [ :args | MALCustomError new signal: args first ]).\nCore Ns at: #readline put:\n    (Fn new: [ :args |\n        | result |\n        result := ReadLine readLine: args first value.\n        result isString ifTrue: [\n            MALString new: result\n        ] ifFalse: [\n            MALObject Nil\n        ] ]).\nCore Ns at: #'time-ms' put:\n    (Fn new: [ :args | MALNumber new: Time millisecondClock ]).\nCore Ns at: #'gst-eval' put:\n    (Fn new: [ :args | (Behavior evaluate: args first value) toMALValue ]).\n\nCore Ns at: #atom put:\n    (Fn new: [ :args | MALAtom new: args first ]).\nCore Ns at: #'atom?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #atom ] ]).\nCore Ns at: #deref put:\n    (Fn new: [ :args | args first value ]).\nCore Ns at: #'reset!' put:\n    (Fn new: [ :args | args first value: args second. args second ]).\nCore Ns at: #'swap!' put:\n    (Fn new: [ :args |\n        | a f x xs result |\n        a := args first.\n        f := args second fn.\n        x := a value.\n        xs := args allButFirst: 2.\n        result := f value: (xs copyWithFirst: x).\n        a value: result.\n        result ]).\n\nCore Ns at: #cons put:\n    (Fn new: [ :args | MALList new: (args second value copyWithFirst: args first) ]).\nCore Ns at: #concat put:\n    (Fn new: [ :args | MALList new: (OrderedCollection join:\n        (args collect: [ :arg | arg value ])) ]).\nCore Ns at: #nth put:\n    (Fn new: [ :args |\n        | items index |\n        items := args first value.\n        index := args second value + 1.\n        items at: index ifAbsent: [ MALOutOfBounds new signal ] ]).\nCore Ns at: #first put:\n    (Fn new: [ :args | Core nilable: args else: [\n        args first value at: 1 ifAbsent: [ MALObject Nil ] ] ]).\nCore Ns at: #rest put:\n    (Fn new: [ :args |\n        | items rest |\n        items := args first value.\n        (args first type = #nil or: [ items isEmpty  ]) ifTrue: [\n            rest := {}\n        ] ifFalse: [\n            rest := items allButFirst\n        ].\n        MALList new: (OrderedCollection from: rest) ]).\nCore Ns at: #conj put:\n    (Fn new: [ :args |\n        | kind result items |\n        kind := args first type.\n        result := args first value.\n        items := args allButFirst.\n\n        kind = #list ifTrue: [\n            MALList new: (OrderedCollection from: items reverse, result)\n        ] ifFalse: [\n            MALVector new: (OrderedCollection from: result, items)\n        ] ]).\nCore Ns at: #seq put:\n    (Fn new: [ :args |\n        | kind storage result |\n        kind := args first type.\n        storage := args first value.\n        Core nilable: args else: [\n            storage isEmpty ifTrue: [\n                MALObject Nil\n            ] ifFalse: [\n               kind = #string ifTrue: [\n                   result := (OrderedCollection from: storage) collect:\n                       [ :char | MALString new: char asString ].\n                   MALList new: result\n               ] ifFalse: [\n                   MALList new: (OrderedCollection from: storage)\n               ]\n            ]\n        ] ]).\n\nCore Ns at: #apply put:\n    (Fn new: [ :args |\n        | f rest result |\n        f := args first fn.\n        args size < 3 ifTrue: [\n            rest := {}\n        ] ifFalse: [\n            rest := args copyFrom: 2 to: args size - 1\n        ].\n        rest := rest, args last value.\n        f value: rest ]).\nCore Ns at: #map put:\n    (Fn new: [ :args |\n        | items f result |\n        f := args first fn.\n        items := args second value.\n        result := items collect: [ :item | f value: {item} ].\n        MALList new: (OrderedCollection from: result) ]).\n\nCore Ns at: #meta put:\n    (Fn new: [ :args |\n        | meta |\n        meta := args first meta.\n        meta isNil ifTrue: [ MALObject Nil ] ifFalse: [ meta ] ]).\nCore Ns at: #'with-meta' put:\n    (Fn new: [ :args | args first withMeta: args second ]).\n\nCore Ns at: #'nil?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #nil ] ]).\nCore Ns at: #'true?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #true ] ]).\nCore Ns at: #'false?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #false ] ]).\nCore Ns at: #'number?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #number ] ]).\nCore Ns at: #'symbol?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #symbol ] ]).\nCore Ns at: #'keyword?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #keyword ] ]).\nCore Ns at: #'string?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #string ] ]).\nCore Ns at: #'vector?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #vector ] ]).\nCore Ns at: #'map?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #map ] ]).\nCore Ns at: #'sequential?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #list or:\n                                     [ args first type = #vector ] ] ]).\nCore Ns at: #'fn?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #fn or:\n                                     [ args first type = #func and:\n                                      [ args first isMacro not ] ] ] ]).\nCore Ns at: #'macro?' put:\n    (Fn new: [ :args | Core coerce: [ args first type = #func and:\n                                     [ args first isMacro ] ] ]).\n\nCore Ns at: #symbol put:\n    (Fn new: [ :args | MALSymbol new: args first value asSymbol ]).\nCore Ns at: #keyword put:\n    (Fn new: [ :args | MALKeyword new: args first value asSymbol ]).\nCore Ns at: #'vec' put:\n    (Fn new: [ :args | MALVector new: args first value ]).\nCore Ns at: #vector put:\n    (Fn new: [ :args | MALVector new: (OrderedCollection from: args) ]).\nCore Ns at: #'hash-map' put:\n    (Fn new: [ :args | MALMap new: args asDictionary ]).\n\nCore Ns at: #assoc put:\n    (Fn new: [ :args |\n        | result keyVals |\n        result := Dictionary from: args first value associations.\n        keyVals := args allButFirst.\n        1 to: keyVals size by: 2 do:\n            [ :i | result add: (keyVals at: i) -> (keyVals at: i + 1) ].\n        MALMap new: result ]).\nCore Ns at: #dissoc put:\n    (Fn new: [ :args |\n        | result keys |\n        result := Dictionary from: args first value associations.\n        keys := args allButFirst.\n        keys do: [ :key | result removeKey: key ifAbsent: [ nil ] ].\n        MALMap new: result ]).\nCore Ns at: #get put:\n    (Fn new: [ :args | Core nilable: args else:\n        [ args first value at: args second ifAbsent: [ MALObject Nil ] ] ]).\nCore Ns at: #'contains?' put:\n    (Fn new: [ :args | Core coerce: [ args first value includesKey: args second ] ]).\nCore Ns at: #keys put:\n    (Fn new: [ :args | MALList new: (OrderedCollection from: args first value keys) ]).\nCore Ns at: #vals put:\n    (Fn new: [ :args | MALList new: (OrderedCollection from: args first value values) ]).\n"
  },
  {
    "path": "impls/gnu-smalltalk/env.st",
    "content": "Object subclass: Env [\n    | data outer |\n\n    Env class >> new: outerEnv [\n        ^self new: outerEnv binds: {} exprs: {}\n    ]\n\n    Env class >> new: outerEnv binds: binds exprs: exprs [\n        | env |\n        env := super new.\n        env init: outerEnv binds: binds exprs: exprs.\n        ^env\n    ]\n\n    init: env binds: binds exprs: exprs [\n        data := Dictionary new.\n        outer := env.\n        1 to: binds size do:\n            [ :i | (binds at: i) = #& ifTrue: [\n                | rest |\n                rest := OrderedCollection from: (exprs copyFrom: i).\n                self set: (binds at: i + 1) value: (MALList new: rest).\n                ^nil\n            ] ifFalse: [\n                self set: (binds at: i) value: (exprs at: i)\n            ] ]\n    ]\n\n    set: key value: value [\n        data at: key put: value.\n    ]\n\n    get: key [\n        ^data at: key ifAbsent: [\n            outer isNil ifFalse: [\n                outer get: key\n            ]\n        ]\n    ]\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/func.st",
    "content": "MALObject subclass: Func [\n    | ast params env fn isMacro |\n\n    ast [ ^ast ]\n    params [ ^params ]\n    env [ ^env ]\n    fn [ ^fn ]\n    isMacro [ ^isMacro ]\n\n    isMacro: bool [\n        isMacro := bool\n    ]\n\n    Func class >> new: ast params: params env: env fn: fn [\n        | func |\n        func := super new: #func value: fn meta: nil.\n        func init: ast params: params env: env fn: fn.\n        ^func\n    ]\n\n    init: anAst params: someParams env: anEnv fn: aFn [\n        ast := anAst.\n        params := someParams.\n        env := anEnv.\n        fn := aFn.\n        isMacro := false\n    ]\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/printer.st",
    "content": "Object subclass: Printer [\n    Printer class >> prStr: sexp printReadably: printReadably [\n        sexp type = #fn ifTrue: [ ^'#<fn>' ].\n        sexp type = #func ifTrue: [ ^'#<func>' ].\n        sexp type = #true ifTrue: [ ^'true' ].\n        sexp type = #false ifTrue: [ ^'false' ].\n        sexp type = #nil ifTrue: [ ^'nil' ].\n\n        sexp type = #number ifTrue: [ ^sexp value asString ].\n        sexp type = #symbol ifTrue: [ ^sexp value asString ].\n        sexp type = #keyword ifTrue: [ ^':', sexp value ].\n\n        sexp type = #string ifTrue: [\n            printReadably ifTrue: [\n                ^sexp value repr\n            ] ifFalse: [\n                ^sexp value\n            ]\n        ].\n\n        sexp type = #list ifTrue: [\n            ^self prList: sexp printReadably: printReadably\n                  starter: '(' ender: ')'\n        ].\n        sexp type = #vector ifTrue: [\n            ^self prList: sexp printReadably: printReadably\n                  starter: '[' ender: ']'\n        ].\n        sexp type = #map ifTrue: [\n            ^self prMap: sexp printReadably: printReadably\n        ].\n\n        sexp type = #atom ifTrue: [\n            ^'(atom ', (self prStr: sexp value printReadably: printReadably), ')'\n        ].\n\n        Error halt: 'unimplemented type'\n    ]\n\n    Printer class >> prList: sexp printReadably: printReadably\n            starter: starter ender: ender [\n        | items |\n        items := sexp value collect:\n            [ :item | self prStr: item printReadably: printReadably ].\n        ^starter, (items join: ' ') , ender\n    ]\n\n    Printer class >> prMap: sexp printReadably: printReadably [\n        | items |\n        items := sexp value associations collect:\n            [ :item |\n                (self prStr: item key printReadably: printReadably), ' ',\n                    (self prStr: item value printReadably: printReadably) ].\n        ^'{', (items join: ' '), '}'\n    ]\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/reader.st",
    "content": "Object subclass: Reader [\n    | storage index |\n\n    TokenRegex := '[\\s,]*(~@|[\\[\\]{}()''`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\\s\\[\\]{}(''\"`,;)]*)'.\n    CommentRegex := ';.*'.\n    NumberRegex := '-?[0-9]+(?:\\.[0-9]+)?'.\n    StringRegex := '\"(?:\\\\.|[^\\\\\"])*\"'.\n\n    Reader class >> tokenizer: input [\n        | tokens token hit pos done |\n        tokens := OrderedCollection new.\n        pos := 1.\n        done := false.\n\n        [done] whileFalse: [\n            hit := input searchRegex: TokenRegex startingAt: pos.\n            token := hit at: 1.\n            token size = 0 ifTrue: [\n                tokens add: (input copyFrom: pos to: input size) trimSeparators.\n                done := true.\n            ].\n            (token size = 0 or: [token matchRegex: CommentRegex]) ifFalse: [\n                tokens add: token\n            ].\n            pos := pos + (hit match size).\n            pos > input size ifTrue: [\n                done := true.\n            ].\n        ].\n        ^tokens\n    ]\n\n    Reader class >> readStr: input [\n        | tokens reader form |\n        tokens := self tokenizer: input.\n        reader := self new: tokens.\n        tokens isEmpty ifTrue: [\n            ^MALEmptyInput new signal\n        ].\n        ^self readForm: reader.\n    ]\n\n    Reader class >> readForm: reader [\n        | token |\n        token := reader peek.\n        token = '(' ifTrue: [\n            ^self readList: reader class: MALList ender: ')'\n        ].\n        token = '[' ifTrue: [\n            ^self readList: reader class: MALVector ender: ']'\n        ].\n        token = '{' ifTrue: [\n            ^self readList: reader class: MALMap ender: '}'\n        ].\n\n        (token matchRegex: '[])}]') ifTrue: [\n            ^MALUnexpectedToken new signal: token\n        ].\n\n        token = '''' ifTrue: [\n            ^self readSimpleMacro: reader name: #quote\n        ].\n        token = '`' ifTrue: [\n            ^self readSimpleMacro: reader name: #quasiquote\n        ].\n        token = '~' ifTrue: [\n            ^self readSimpleMacro: reader name: #unquote\n        ].\n        token = '~@' ifTrue: [\n            ^self readSimpleMacro: reader name: #'splice-unquote'\n        ].\n        token = '@' ifTrue: [\n            ^self readSimpleMacro: reader name: #deref\n        ].\n\n        token = '^' ifTrue: [\n            ^self readWithMetaMacro: reader\n        ].\n\n        ^self readAtom: reader\n    ]\n\n    Reader class >> readList: reader class: aClass ender: ender [\n        | storage token |\n        storage := OrderedCollection new.\n        \"pop opening token\"\n        reader next.\n        [ token := reader peek. token isNil ] whileFalse: [\n            token = ender ifTrue: [\n                ender = '}' ifTrue: [\n                    storage := storage asDictionary.\n                ].\n                \"pop closing token\"\n                reader next.\n                ^aClass new: storage\n            ].\n            storage add: (self readForm: reader).\n        ].\n        ^MALUnterminatedSequence new signal: ender\n    ]\n\n    Reader class >> readAtom: reader [\n        | token |\n        token := reader next.\n\n        token = 'true' ifTrue: [ ^MALObject True ].\n        token = 'false' ifTrue: [ ^MALObject False ].\n        token = 'nil' ifTrue: [ ^MALObject Nil ].\n\n        (token matchRegex: StringRegex) ifTrue: [\n            ^MALString new: token parse\n        ].\n        (token first = $\") ifTrue: [\n            ^MALUnterminatedSequence new signal: '\"'\n        ].\n\n        (token matchRegex: NumberRegex) ifTrue: [\n            ^MALNumber new: token asNumber\n        ].\n\n        (token first = $:) ifTrue: [\n            ^MALKeyword new: token allButFirst asSymbol\n        ].\n\n        ^MALSymbol new: token asSymbol\n    ]\n\n    Reader class >> readSimpleMacro: reader name: name [\n        | form list |\n        \"pop reader macro token\"\n        reader next.\n        form := self readForm: reader.\n        list := OrderedCollection from: { MALSymbol new: name. form }.\n        ^MALList new: list\n    ]\n\n    Reader class >> readWithMetaMacro: reader [\n        | form meta list |\n        \"pop reader macro token\"\n        reader next.\n        meta := self readForm: reader.\n        form := self readForm: reader.\n        list := OrderedCollection from:\n            { MALSymbol new: #'with-meta'. form. meta }.\n        ^MALList new: list\n    ]\n\n    Reader class >> new: tokens [\n        | reader |\n        reader := super new.\n        reader init: tokens.\n        ^reader\n    ]\n\n    init: tokens [\n        storage := tokens.\n        index := 1.\n    ]\n\n    peek [\n        ^storage at: index ifAbsent: [ nil ]\n    ]\n\n    next [\n        | token |\n        token := self peek.\n        index := index + 1.\n        ^token\n    ]\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/readline.st",
    "content": "DLD addLibrary: 'libreadline'.\nDLD addLibrary: 'libhistory'.\n\nObject subclass: ReadLine [\n    ReadLine class >> readLine: prompt [\n        <cCall: 'readline' returning: #stringOut args: #(#string)>\n    ]\n\n    ReadLine class >> addHistory: item [\n        <cCall: 'add_history' returning: #void args: #(#string)>\n    ]\n\n    ReadLine class >> readHistory: filePath [\n        <cCall: 'read_history' returning: #int args: #(#string)>\n    ]\n\n    ReadLine class >> writeHistory: filePath [\n        <cCall: 'write_history' returning: #int args: #(#string)>\n    ]\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/run",
    "content": "#!/usr/bin/env bash\nexec gst -f $(dirname $0)/${STEP:-stepA_mal}.st \"${@}\"\n"
  },
  {
    "path": "impls/gnu-smalltalk/step0_repl.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^input\n    ]\n\n    MAL class >> EVAL: sexp [\n        ^sexp\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^sexp\n    ]\n\n    MAL class >> rep: input [\n        ^self PRINT: (self EVAL: (self READ: input))\n    ]\n]\n\n| input historyFile |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\n\n[ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n    input isEmpty ifFalse: [\n        ReadLine addHistory: input.\n        ReadLine writeHistory: historyFile.\n        (MAL rep: input) displayNl.\n    ]\n]\n\n'' displayNl.\n"
  },
  {
    "path": "impls/gnu-smalltalk/step1_read_print.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> EVAL: sexp [\n        ^sexp\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input [\n        ^self PRINT: (self EVAL: (self READ: input))\n    ]\n]\n\n| input historyFile |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\n\n[ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n    input isEmpty ifFalse: [\n        ReadLine addHistory: input.\n        ReadLine writeHistory: historyFile.\n        [ (MAL rep: input) displayNl ]\n            on: MALEmptyInput do: [ #return ]\n            on: MALError do:\n                [ :err | ('error: ', err messageText) displayNl. #return ].\n    ]\n]\n\n'' displayNl.\n"
  },
  {
    "path": "impls/gnu-smalltalk/step2_eval.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> EVAL: sexp env: env [\n        | forms function args |\n\n        \" ('EVAL: ' , (Printer prStr: sexp printReadably: true)) displayNl. \"\n\n        sexp type = #symbol ifTrue: [\n            ^env at: sexp value ifAbsent: [\n                ^MALUnknownSymbol new signal: sexp value\n            ].\n        ].\n\n        sexp type = #vector ifTrue: [\n            ^MALVector new: (self evalList: sexp value env: env)\n        ].\n        sexp type = #map ifTrue: [\n            ^MALMap new: (self evalList: sexp value env: env)\n        ].\n        sexp type ~= #list ifTrue: [\n            ^sexp\n        ].\n        sexp value isEmpty ifTrue: [\n            ^sexp\n        ].\n\n        forms := self evalList: sexp value env: env.\n        function := forms first.\n        args := forms allButFirst asArray.\n        ^function valueWithArguments: args\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Dictionary from:\n    { #+ -> [ :a :b | MALNumber new: a value + b value ].\n      #- -> [ :a :b | MALNumber new: a value - b value ].\n      #* -> [ :a :b | MALNumber new: a value * b value ].\n      #/ -> [ :a :b | MALNumber new: a value // b value ] }.\n\n[ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n    input isEmpty ifFalse: [\n        ReadLine addHistory: input.\n        ReadLine writeHistory: historyFile.\n        [ (MAL rep: input env: replEnv) displayNl ]\n            on: MALEmptyInput do: [ #return ]\n            on: MALError do:\n                [ :err | ('error: ', err messageText) displayNl. #return ].\n    ]\n]\n\n'' displayNl.\n"
  },
  {
    "path": "impls/gnu-smalltalk/step3_env.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> EVAL: sexp env: env [\n        | ast a0_ a1 a1_ a2 forms function args |\n\n        a2 := env get: #'DEBUG-EVAL'.\n        (a2 isNil or: [ a2 type = #false or: [ a2 type = #nil ] ] )\n        ifFalse: [\n            ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n            displayNl.\n        ].\n\n        sexp type = #symbol ifTrue: [\n            | key value |\n            key := sexp value.\n            value := env get: key.\n            value isNil ifTrue: [\n                ^MALUnknownSymbol new signal: key\n            ].\n            ^value\n        ].\n        sexp type = #vector ifTrue: [\n            ^MALVector new: (self evalList: sexp value env: env)\n        ].\n        sexp type = #map ifTrue: [\n            ^MALMap new: (self evalList: sexp value env: env)\n        ].\n        sexp type ~= #list ifTrue: [\n            ^sexp\n        ].\n        sexp value isEmpty ifTrue: [\n            ^sexp\n        ].\n\n        ast := sexp value.\n        a0_ := ast first value.\n        a0_ = #'def!' ifTrue: [\n            | result |\n            a1_ := ast second value.\n            a2 := ast third.\n            result := self EVAL: a2 env: env.\n            env set: a1_ value: result.\n            ^result\n        ].\n\n        a0_ = #'let*' ifTrue: [\n            | env_ |\n            env_ := Env new: env.\n            a1_ := ast second value.\n            a2 := ast third.\n            1 to: a1_ size by: 2 do:\n                [ :i | env_ set: (a1_ at: i) value\n                            value: (self EVAL: (a1_ at: i + 1) env: env_) ].\n            ^self EVAL: a2 env: env_\n        ].\n\n        forms := self evalList: sexp value env: env.\n        function := forms first.\n        args := forms allButFirst asArray.\n        ^function valueWithArguments: args\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nreplEnv set: #+ value: [ :a :b | MALNumber new: a value + b value ].\nreplEnv set: #- value: [ :a :b | MALNumber new: a value - b value ].\nreplEnv set: #* value: [ :a :b | MALNumber new: a value * b value ].\nreplEnv set: #/ value: [ :a :b | MALNumber new: a value // b value ].\n\n[ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n    input isEmpty ifFalse: [\n        ReadLine addHistory: input.\n        ReadLine writeHistory: historyFile.\n        [ (MAL rep: input env: replEnv) displayNl ]\n            on: MALEmptyInput do: [ #return ]\n            on: MALError do:\n                [ :err | ('error: ', err messageText) displayNl. #return ].\n    ]\n]\n\n'' displayNl.\n"
  },
  {
    "path": "impls/gnu-smalltalk/step4_if_fn_do.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> EVAL: sexp env: env [\n        | ast a0_ a1 a1_ a2 a3 forms function args |\n        a1 := env get: #'DEBUG-EVAL'.\n        (a1 isNil or: [ a1 type = #false or: [ a1 type = #nil ] ] )\n        ifFalse: [\n            ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n            displayNl.\n        ].\n\n        sexp type = #symbol ifTrue: [\n            | key value |\n            key := sexp value.\n            value := env get: key.\n            value isNil ifTrue: [\n                ^MALUnknownSymbol new signal: key\n            ].\n            ^value\n        ].\n        sexp type = #vector ifTrue: [\n            ^MALVector new: (self evalList: sexp value env: env)\n        ].\n        sexp type = #map ifTrue: [\n            ^MALMap new: (self evalList: sexp value env: env)\n        ].\n        sexp type ~= #list ifTrue: [\n            ^sexp\n        ].\n        sexp value isEmpty ifTrue: [\n            ^sexp\n        ].\n\n        ast := sexp value.\n        a0_ := ast first value.\n        a0_ = #'def!' ifTrue: [\n            | result |\n            a1_ := ast second value.\n            a2 := ast third.\n            result := self EVAL: a2 env: env.\n            env set: a1_ value: result.\n            ^result\n        ].\n\n        a0_ = #'let*' ifTrue: [\n            | env_ |\n            env_ := Env new: env.\n            a1_ := ast second value.\n            a2 := ast third.\n            1 to: a1_ size by: 2 do:\n                [ :i | env_ set: (a1_ at: i) value\n                            value: (self EVAL: (a1_ at: i + 1) env: env_) ].\n            ^self EVAL: a2 env: env_\n        ].\n\n        a0_ = #do ifTrue: [\n            ^(self evalList: ast allButFirst env: env) last\n        ].\n\n        a0_ = #if ifTrue: [\n            | condition |\n            a1 := ast second.\n            a2 := ast third.\n            a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n            condition := self EVAL: a1 env: env.\n\n            (condition type = #false or: [ condition type = #nil ]) ifTrue: [\n                ^self EVAL: a3 env: env\n            ] ifFalse: [\n                ^self EVAL: a2 env: env\n            ]\n        ].\n\n        a0_ = #'fn*' ifTrue: [\n            | binds |\n            a1_ := ast second value.\n            binds := a1_ collect: [ :item | item value ].\n            a2 := ast third.\n            ^Fn new: [ :args | self EVAL: a2 env:\n                           (Env new: env binds: binds exprs: args) ]\n        ].\n\n        forms := self evalList: sexp value env: env.\n        function := forms first fn.\n        args := forms allButFirst asArray.\n        ^function value: args\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\n\n[ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n    input isEmpty ifFalse: [\n        ReadLine addHistory: input.\n        ReadLine writeHistory: historyFile.\n        [ (MAL rep: input env: replEnv) displayNl ]\n            on: MALEmptyInput do: [ #return ]\n            on: MALError do:\n                [ :err | ('error: ', err messageText) displayNl. #return ].\n    ]\n]\n\n'' displayNl.\n"
  },
  {
    "path": "impls/gnu-smalltalk/step5_tco.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'func.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> EVAL: aSexp env: anEnv [\n        | sexp env ast a0_ a1 a1_ a2 a3 forms function args |\n\n        \"NOTE: redefinition of method arguments is not allowed\"\n        sexp := aSexp.\n        env := anEnv.\n\n        [\n            [ :continue |\n\n                a1 := env get: #'DEBUG-EVAL'.\n                (a1 isNil or: [ a1 type = #false or: [ a1 type = #nil ] ] )\n                ifFalse: [\n                    ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n                    displayNl.\n                ].\n\n                sexp type = #symbol ifTrue: [\n                    | key value |\n                    key := sexp value.\n                    value := env get: key.\n                    value isNil ifTrue: [\n                        ^MALUnknownSymbol new signal: key\n                    ].\n                    ^value\n                ].\n                sexp type = #vector ifTrue: [\n                    ^MALVector new: (self evalList: sexp value env: env)\n                ].\n                sexp type = #map ifTrue: [\n                    ^MALMap new: (self evalList: sexp value env: env)\n                ].\n                sexp type ~= #list ifTrue: [\n                    ^sexp\n                ].\n                sexp value isEmpty ifTrue: [\n                    ^sexp\n                ].\n\n                ast := sexp value.\n                a0_ := ast first value.\n\n                a0_ = #'def!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := self EVAL: a2 env: env.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'let*' ifTrue: [\n                    | env_ |\n                    env_ := Env new: env.\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    1 to: a1_ size by: 2 do:\n                        [ :i | env_ set: (a1_ at: i) value\n                                    value: (self EVAL: (a1_ at: i + 1)\n                                                 env: env_) ].\n                    env := env_.\n                    sexp := a2.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #do ifTrue: [\n                    | forms last |\n                    ast size < 2 ifTrue: [\n                        forms := {}.\n                        last := MALObject Nil.\n                    ] ifFalse: [\n                        forms := ast copyFrom: 2 to: ast size - 1.\n                        last := ast last.\n                    ].\n\n                    forms do: [ :form | self EVAL: form env: env ].\n                    sexp := last.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #if ifTrue: [\n                    | condition |\n                    a1 := ast second.\n                    a2 := ast third.\n                    a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n                    condition := self EVAL: a1 env: env.\n\n                    (condition type = #false or:\n                     [ condition type = #nil ]) ifTrue: [\n                        sexp := a3\n                    ] ifFalse: [\n                        sexp := a2\n                    ].\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #'fn*' ifTrue: [\n                    | binds env_ fn |\n                    a1_ := ast second value.\n                    binds := a1_ collect: [ :item | item value ].\n                    a2 := ast third.\n                    fn := [ :args |\n                        self EVAL: a2 env:\n                            (Env new: env binds: binds exprs: args) ].\n                    ^Func new: a2 params: binds env: env fn: fn\n                ].\n\n                forms := self evalList: sexp value env: env.\n                function := forms first.\n                args := forms allButFirst asArray.\n\n                function type = #fn ifTrue: [ ^function fn value: args ].\n                function type = #func ifTrue: [\n                    | env_ |\n                    sexp := function ast.\n                    env_ := Env new: function env binds: function params\n                                exprs: args.\n                    env := env_.\n                    continue value \"TCO\"\n                ]\n            ] valueWithExit\n        ] repeat.\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\n\n[ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n    input isEmpty ifFalse: [\n        ReadLine addHistory: input.\n        ReadLine writeHistory: historyFile.\n        [ (MAL rep: input env: replEnv) displayNl ]\n            on: MALEmptyInput do: [ #return ]\n            on: MALError do:\n                [ :err | ('error: ', err messageText) displayNl. #return ].\n    ]\n]\n\n'' displayNl.\n"
  },
  {
    "path": "impls/gnu-smalltalk/step6_file.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'func.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> EVAL: aSexp env: anEnv [\n        | sexp env ast a0 a0_ a1 a1_ a2 a3 forms function args |\n\n        \"NOTE: redefinition of method arguments is not allowed\"\n        sexp := aSexp.\n        env := anEnv.\n\n        [\n            [ :continue |\n\n                a0 := env get: #'DEBUG-EVAL'.\n                (a0 isNil or: [ a0 type = #false or: [ a0 type = #nil ] ] )\n                ifFalse: [\n                    ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n                    displayNl.\n                ].\n\n                sexp type = #symbol ifTrue: [\n                    | key value |\n                    key := sexp value.\n                    value := env get: key.\n                    value isNil ifTrue: [\n                        ^MALUnknownSymbol new signal: key\n                    ].\n                    ^value\n                ].\n                sexp type = #vector ifTrue: [\n                    ^MALVector new: (self evalList: sexp value env: env)\n                ].\n                sexp type = #map ifTrue: [\n                    ^MALMap new: (self evalList: sexp value env: env)\n                ].\n                sexp type ~= #list ifTrue: [\n                    ^sexp\n                ].\n                sexp value isEmpty ifTrue: [\n                    ^sexp\n                ].\n\n                ast := sexp value.\n                a0 := ast first.\n\n                a0_ := ast first value.\n                a0_ = #'def!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := self EVAL: a2 env: env.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'let*' ifTrue: [\n                    | env_ |\n                        env_ := Env new: env.\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    1 to: a1_ size by: 2 do:\n                        [ :i | env_ set: (a1_ at: i) value\n                                    value: (self EVAL: (a1_ at: i + 1)\n                                                 env: env_) ].\n                    env := env_.\n                    sexp := a2.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #do ifTrue: [\n                    | forms last |\n                    ast size < 2 ifTrue: [\n                        forms := {}.\n                        last := MALObject Nil.\n                    ] ifFalse: [\n                        forms := ast copyFrom: 2 to: ast size - 1.\n                        last := ast last.\n                    ].\n\n                    forms do: [ :form | self EVAL: form env: env ].\n                    sexp := last.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #if ifTrue: [\n                    | condition |\n                    a1 := ast second.\n                    a2 := ast third.\n                    a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n                    condition := self EVAL: a1 env: env.\n\n                    (condition type = #false or:\n                         [ condition type = #nil ]) ifTrue: [\n                             sexp := a3\n                         ] ifFalse: [\n                             sexp := a2\n                         ].\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #'fn*' ifTrue: [\n                    | binds env_ fn |\n                    a1_ := ast second value.\n                    binds := a1_ collect: [ :item | item value ].\n                    a2 := ast third.\n                    fn := [ :args |\n                        self EVAL: a2 env:\n                            (Env new: env binds: binds exprs: args) ].\n                    ^Func new: a2 params: binds env: env fn: fn\n                ].\n\n                forms := self evalList: sexp value env: env.\n                function := forms first.\n                args := forms allButFirst asArray.\n\n                function type = #fn ifTrue: [ ^function fn value: args ].\n                function type = #func ifTrue: [\n                    | env_ |\n                    sexp := function ast.\n                    env_ := Env new: function env binds: function params\n                                exprs: args.\n                    env := env_.\n                    continue value \"TCO\"\n                ]\n            ] valueWithExit\n        ] repeat.\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv argv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nargv := Smalltalk arguments.\nargv notEmpty ifTrue: [ argv := argv allButFirst ].\nargv := OrderedCollection from: (argv collect: [ :arg | MALString new: arg ]).\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\nreplEnv set: #eval value: (Fn new: [ :args | MAL EVAL: args first env: replEnv ]).\nreplEnv set: #'*ARGV*' value: (MALList new: argv).\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\nMAL rep: '(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))' env: replEnv.\n\nSmalltalk arguments notEmpty ifTrue: [\n    MAL rep: '(load-file \"', Smalltalk arguments first, '\")' env: replEnv\n] ifFalse: [\n    [ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n        input isEmpty ifFalse: [\n            ReadLine addHistory: input.\n            ReadLine writeHistory: historyFile.\n            [ (MAL rep: input env: replEnv) displayNl ]\n                on: MALEmptyInput do: [ #return ]\n                on: MALError do:\n                    [ :err | ('error: ', err messageText) displayNl. #return ].\n        ]\n    ].\n\n    '' displayNl.\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/step7_quote.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'func.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> starts_with: ast sym: sym  [\n        | a a0 |\n        ast type = #list ifFalse: [ ^false. ].\n        a := ast value.\n        a isEmpty ifTrue: [ ^false. ].\n        a0 := a first.\n        ^a0 type = #symbol and: [ a0 value = sym ].\n    ]\n\n    MAL class >> quasiquote: ast [\n        | result acc |\n        (ast type = #symbol or: [ ast type = #map ]) ifTrue: [\n            result := {MALSymbol new: #quote. ast}.\n            ^MALList new: (OrderedCollection from: result)\n        ].\n        (ast type = #list or: [ ast type = #vector ]) ifFalse: [\n            ^ast\n        ].\n\n        (self starts_with: ast sym: #unquote) ifTrue: [\n            ^ast value second\n        ].\n\n        result := {}.\n        acc := MALList new: (OrderedCollection from: result).\n        ast value reverseDo: [ : elt |\n            (self starts_with: elt sym: #'splice-unquote') ifTrue: [\n                result := {MALSymbol new: #concat. elt value second. acc}\n            ] ifFalse: [\n                result := {MALSymbol new: #cons. self quasiquote: elt. acc}\n            ].\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ast type = #vector ifTrue: [\n            result := {MALSymbol new: #vec. acc}.\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ^acc\n    ]\n\n    MAL class >> EVAL: aSexp env: anEnv [\n        | sexp env ast a0 a0_ a1 a1_ a2 a3 forms function args |\n\n        \"NOTE: redefinition of method arguments is not allowed\"\n        sexp := aSexp.\n        env := anEnv.\n\n        [\n            [ :continue |\n\n                a0 := env get: #'DEBUG-EVAL'.\n                (a0 isNil or: [ a0 type = #false or: [ a0 type = #nil ] ] )\n                ifFalse: [\n                    ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n                    displayNl.\n                ].\n\n                sexp type = #symbol ifTrue: [\n                    | key value |\n                    key := sexp value.\n                    value := env get: key.\n                    value isNil ifTrue: [\n                        ^MALUnknownSymbol new signal: key\n                    ].\n                    ^value\n                ].\n                sexp type = #vector ifTrue: [\n                    ^MALVector new: (self evalList: sexp value env: env)\n                ].\n                sexp type = #map ifTrue: [\n                    ^MALMap new: (self evalList: sexp value env: env)\n                ].\n                sexp type ~= #list ifTrue: [\n                    ^sexp\n                ].\n                sexp value isEmpty ifTrue: [\n                    ^sexp\n                ].\n\n                ast := sexp value.\n                a0 := ast first.\n\n                a0_ := ast first value.\n                a0_ = #'def!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := self EVAL: a2 env: env.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'let*' ifTrue: [\n                    | env_ |\n                        env_ := Env new: env.\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    1 to: a1_ size by: 2 do:\n                        [ :i | env_ set: (a1_ at: i) value\n                                    value: (self EVAL: (a1_ at: i + 1)\n                                                 env: env_) ].\n                    env := env_.\n                    sexp := a2.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #do ifTrue: [\n                    | forms last |\n                    ast size < 2 ifTrue: [\n                        forms := {}.\n                        last := MALObject Nil.\n                    ] ifFalse: [\n                        forms := ast copyFrom: 2 to: ast size - 1.\n                        last := ast last.\n                    ].\n\n                    forms do: [ :form | self EVAL: form env: env ].\n                    sexp := last.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #if ifTrue: [\n                    | condition |\n                    a1 := ast second.\n                    a2 := ast third.\n                    a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n                    condition := self EVAL: a1 env: env.\n\n                    (condition type = #false or:\n                         [ condition type = #nil ]) ifTrue: [\n                             sexp := a3\n                         ] ifFalse: [\n                             sexp := a2\n                         ].\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #quote ifTrue: [\n                    a1 := ast second.\n                    ^a1\n                ].\n\n                a0_ = #quasiquote ifTrue: [\n                    | result |\n                    a1 := ast second.\n                    sexp := self quasiquote: a1.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #'fn*' ifTrue: [\n                    | binds env_ fn |\n                        a1_ := ast second value.\n                    binds := a1_ collect: [ :item | item value ].\n                    a2 := ast third.\n                    fn := [ :args |\n                        self EVAL: a2 env:\n                            (Env new: env binds: binds exprs: args) ].\n                    ^Func new: a2 params: binds env: env fn: fn\n                ].\n\n                forms := self evalList: sexp value env: env.\n                function := forms first.\n                args := forms allButFirst asArray.\n\n                function type = #fn ifTrue: [ ^function fn value: args ].\n                function type = #func ifTrue: [\n                    | env_ |\n                    sexp := function ast.\n                    env_ := Env new: function env binds: function params\n                                exprs: args.\n                    env := env_.\n                    continue value \"TCO\"\n                ]\n            ] valueWithExit\n        ] repeat.\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv argv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nargv := Smalltalk arguments.\nargv notEmpty ifTrue: [ argv := argv allButFirst ].\nargv := OrderedCollection from: (argv collect: [ :arg | MALString new: arg ]).\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\nreplEnv set: #eval value: (Fn new: [ :args | MAL EVAL: args first env: replEnv ]).\nreplEnv set: #'*ARGV*' value: (MALList new: argv).\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\nMAL rep: '(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))' env: replEnv.\n\nSmalltalk arguments notEmpty ifTrue: [\n    MAL rep: '(load-file \"', Smalltalk arguments first, '\")' env: replEnv\n] ifFalse: [\n    [ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n        input isEmpty ifFalse: [\n            ReadLine addHistory: input.\n            ReadLine writeHistory: historyFile.\n            [ (MAL rep: input env: replEnv) displayNl ]\n                on: MALEmptyInput do: [ #return ]\n                on: MALError do:\n                    [ :err | ('error: ', err messageText) displayNl. #return ].\n        ]\n    ].\n\n    '' displayNl.\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/step8_macros.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'func.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> starts_with: ast sym: sym  [\n        | a a0 |\n        ast type = #list ifFalse: [ ^false. ].\n        a := ast value.\n        a isEmpty ifTrue: [ ^false. ].\n        a0 := a first.\n        ^a0 type = #symbol and: [ a0 value = sym ].\n    ]\n\n    MAL class >> quasiquote: ast [\n        | result acc |\n        (ast type = #symbol or: [ ast type = #map ]) ifTrue: [\n            result := {MALSymbol new: #quote. ast}.\n            ^MALList new: (OrderedCollection from: result)\n        ].\n        (ast type = #list or: [ ast type = #vector ]) ifFalse: [\n            ^ast\n        ].\n\n        (self starts_with: ast sym: #unquote) ifTrue: [\n            ^ast value second\n        ].\n\n        result := {}.\n        acc := MALList new: (OrderedCollection from: result).\n        ast value reverseDo: [ : elt |\n            (self starts_with: elt sym: #'splice-unquote') ifTrue: [\n                result := {MALSymbol new: #concat. elt value second. acc}\n            ] ifFalse: [\n                result := {MALSymbol new: #cons. self quasiquote: elt. acc}\n            ].\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ast type = #vector ifTrue: [\n            result := {MALSymbol new: #vec. acc}.\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ^acc\n    ]\n\n    MAL class >> EVAL: aSexp env: anEnv [\n        | sexp env ast a0 a0_ a1 a1_ a2 a3 function args |\n\n        \"NOTE: redefinition of method arguments is not allowed\"\n        sexp := aSexp.\n        env := anEnv.\n\n        [\n            [ :continue |\n\n                a0 := env get: #'DEBUG-EVAL'.\n                (a0 isNil or: [ a0 type = #false or: [ a0 type = #nil ] ] )\n                ifFalse: [\n                    ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n                    displayNl.\n                ].\n\n                sexp type = #symbol ifTrue: [\n                    | key value |\n                    key := sexp value.\n                    value := env get: key.\n                    value isNil ifTrue: [\n                        ^MALUnknownSymbol new signal: key\n                    ].\n                    ^value\n                ].\n                sexp type = #vector ifTrue: [\n                    ^MALVector new: (self evalList: sexp value env: env)\n                ].\n                sexp type = #map ifTrue: [\n                    ^MALMap new: (self evalList: sexp value env: env)\n                ].\n                sexp type ~= #list ifTrue: [\n                    ^sexp\n                ].\n                sexp value isEmpty ifTrue: [\n                    ^sexp\n                ].\n\n                ast := sexp value.\n                a0 := ast first.\n\n                a0_ := ast first value.\n                a0_ = #'def!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := self EVAL: a2 env: env.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'defmacro!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := (self EVAL: a2 env: env) deepCopy.\n                    result isMacro: true.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'let*' ifTrue: [\n                    | env_ |\n                        env_ := Env new: env.\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    1 to: a1_ size by: 2 do:\n                        [ :i | env_ set: (a1_ at: i) value\n                                    value: (self EVAL: (a1_ at: i + 1)\n                                                 env: env_) ].\n                    env := env_.\n                    sexp := a2.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #do ifTrue: [\n                    | forms last |\n                    ast size < 2 ifTrue: [\n                        forms := {}.\n                        last := MALObject Nil.\n                    ] ifFalse: [\n                        forms := ast copyFrom: 2 to: ast size - 1.\n                        last := ast last.\n                    ].\n\n                    forms do: [ :form | self EVAL: form env: env ].\n                    sexp := last.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #if ifTrue: [\n                    | condition |\n                    a1 := ast second.\n                    a2 := ast third.\n                    a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n                    condition := self EVAL: a1 env: env.\n\n                    (condition type = #false or:\n                         [ condition type = #nil ]) ifTrue: [\n                             sexp := a3\n                         ] ifFalse: [\n                             sexp := a2\n                         ].\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #quote ifTrue: [\n                    a1 := ast second.\n                    ^a1\n                ].\n\n                a0_ = #quasiquote ifTrue: [\n                    | result |\n                    a1 := ast second.\n                    sexp := self quasiquote: a1.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #'fn*' ifTrue: [\n                    | binds env_ fn |\n                        a1_ := ast second value.\n                    binds := a1_ collect: [ :item | item value ].\n                    a2 := ast third.\n                    fn := [ :args |\n                        self EVAL: a2 env:\n                            (Env new: env binds: binds exprs: args) ].\n                    ^Func new: a2 params: binds env: env fn: fn\n                ].\n\n                function := self EVAL: a0 env: env.\n                args := ast allButFirst asArray.\n                (function type = #func and: [ function isMacro ]) ifTrue: [\n                    sexp := function fn value: args.\n                    continue value TCO\n                ].\n                args := self evalList: args env: env.\n                function type = #fn ifTrue: [ ^function fn value: args ].\n                function type = #func ifTrue: [\n                    | env_ |\n                    sexp := function ast.\n                    env_ := Env new: function env binds: function params\n                                exprs: args.\n                    env := env_.\n                    continue value \"TCO\"\n                ]\n            ] valueWithExit\n        ] repeat.\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv argv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nargv := Smalltalk arguments.\nargv notEmpty ifTrue: [ argv := argv allButFirst ].\nargv := OrderedCollection from: (argv collect: [ :arg | MALString new: arg ]).\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\nreplEnv set: #eval value: (Fn new: [ :args | MAL EVAL: args first env: replEnv ]).\nreplEnv set: #'*ARGV*' value: (MALList new: argv).\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\nMAL rep: '(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))' env: replEnv.\nMAL rep: '(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))' env: replEnv.\n\nSmalltalk arguments notEmpty ifTrue: [\n    MAL rep: '(load-file \"', Smalltalk arguments first, '\")' env: replEnv\n] ifFalse: [\n    [ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n        input isEmpty ifFalse: [\n            ReadLine addHistory: input.\n            ReadLine writeHistory: historyFile.\n            [ (MAL rep: input env: replEnv) displayNl ]\n                on: MALEmptyInput do: [ #return ]\n                on: MALError do:\n                    [ :err | ('error: ', err messageText) displayNl. #return ].\n        ]\n    ].\n\n    '' displayNl.\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/step9_try.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'func.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> starts_with: ast sym: sym  [\n        | a a0 |\n        ast type = #list ifFalse: [ ^false. ].\n        a := ast value.\n        a isEmpty ifTrue: [ ^false. ].\n        a0 := a first.\n        ^a0 type = #symbol and: [ a0 value = sym ].\n    ]\n\n    MAL class >> quasiquote: ast [\n        | result acc |\n        (ast type = #symbol or: [ ast type = #map ]) ifTrue: [\n            result := {MALSymbol new: #quote. ast}.\n            ^MALList new: (OrderedCollection from: result)\n        ].\n        (ast type = #list or: [ ast type = #vector ]) ifFalse: [\n            ^ast\n        ].\n\n        (self starts_with: ast sym: #unquote) ifTrue: [\n            ^ast value second\n        ].\n\n        result := {}.\n        acc := MALList new: (OrderedCollection from: result).\n        ast value reverseDo: [ : elt |\n            (self starts_with: elt sym: #'splice-unquote') ifTrue: [\n                result := {MALSymbol new: #concat. elt value second. acc}\n            ] ifFalse: [\n                result := {MALSymbol new: #cons. self quasiquote: elt. acc}\n            ].\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ast type = #vector ifTrue: [\n            result := {MALSymbol new: #vec. acc}.\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ^acc\n    ]\n\n    MAL class >> EVAL: aSexp env: anEnv [\n        | sexp env ast a0 a0_ a1 a1_ a2 a2_ a3 function args |\n\n        \"NOTE: redefinition of method arguments is not allowed\"\n        sexp := aSexp.\n        env := anEnv.\n\n        [\n            [ :continue |\n\n                a0 := env get: #'DEBUG-EVAL'.\n                (a0 isNil or: [ a0 type = #false or: [ a0 type = #nil ] ] )\n                ifFalse: [\n                    ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n                    displayNl.\n                ].\n\n                sexp type = #symbol ifTrue: [\n                    | key value |\n                    key := sexp value.\n                    value := env get: key.\n                    value isNil ifTrue: [\n                        ^MALUnknownSymbol new signal: key\n                    ].\n                    ^value\n                ].\n                sexp type = #vector ifTrue: [\n                    ^MALVector new: (self evalList: sexp value env: env)\n                ].\n                sexp type = #map ifTrue: [\n                    ^MALMap new: (self evalList: sexp value env: env)\n                ].\n                sexp type ~= #list ifTrue: [\n                    ^sexp\n                ].\n                sexp value isEmpty ifTrue: [\n                    ^sexp\n                ].\n\n                ast := sexp value.\n                a0 := ast first.\n\n                a0_ := ast first value.\n                a0_ = #'def!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := self EVAL: a2 env: env.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'defmacro!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := (self EVAL: a2 env: env) deepCopy.\n                    result isMacro: true.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'let*' ifTrue: [\n                    | env_ |\n                        env_ := Env new: env.\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    1 to: a1_ size by: 2 do:\n                        [ :i | env_ set: (a1_ at: i) value\n                                    value: (self EVAL: (a1_ at: i + 1)\n                                                 env: env_) ].\n                    env := env_.\n                    sexp := a2.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #do ifTrue: [\n                    | forms last |\n                    ast size < 2 ifTrue: [\n                        forms := {}.\n                        last := MALObject Nil.\n                    ] ifFalse: [\n                        forms := ast copyFrom: 2 to: ast size - 1.\n                        last := ast last.\n                    ].\n\n                    forms do: [ :form | self EVAL: form env: env ].\n                    sexp := last.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #if ifTrue: [\n                    | condition |\n                    a1 := ast second.\n                    a2 := ast third.\n                    a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n                    condition := self EVAL: a1 env: env.\n\n                    (condition type = #false or:\n                         [ condition type = #nil ]) ifTrue: [\n                             sexp := a3\n                         ] ifFalse: [\n                             sexp := a2\n                         ].\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #quote ifTrue: [\n                    a1 := ast second.\n                    ^a1\n                ].\n\n                a0_ = #quasiquote ifTrue: [\n                    | result |\n                    a1 := ast second.\n                    sexp := self quasiquote: a1.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #'try*' ifTrue: [\n                    | A B C |\n                    A := ast second.\n                    ast at: 3 ifAbsent: [\n                        ^self EVAL: A env: env.\n                    ].\n                    a2_ := ast third value.\n                    B := a2_ second value.\n                    C := a2_ third.\n                    ^[ self EVAL: A env: env ] on: MALError do:\n                        [ :err |\n                            | data env_ result |\n                            data := err data.\n                            data isString ifTrue: [\n                                data := MALString new: data\n                            ].\n                            env_ := Env new: env binds: {B} exprs: {data}.\n                            err return: (self EVAL: C env: env_)\n                        ]\n                ].\n\n                a0_ = #'fn*' ifTrue: [\n                    | binds env_ fn |\n                        a1_ := ast second value.\n                    binds := a1_ collect: [ :item | item value ].\n                    a2 := ast third.\n                    fn := [ :args |\n                        self EVAL: a2 env:\n                            (Env new: env binds: binds exprs: args) ].\n                    ^Func new: a2 params: binds env: env fn: fn\n                ].\n\n                function := self EVAL: a0 env: env.\n                args := ast allButFirst asArray.\n                (function type = #func and: [ function isMacro ]) ifTrue: [\n                    sexp := function fn value: args.\n                    continue value TCO\n                ].\n                args := self evalList: args env: env.\n                function type = #fn ifTrue: [ ^function fn value: args ].\n                function type = #func ifTrue: [\n                    | env_ |\n                    sexp := function ast.\n                    env_ := Env new: function env binds: function params\n                                exprs: args.\n                    env := env_.\n                    continue value \"TCO\"\n                ]\n            ] valueWithExit\n        ] repeat.\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv argv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nargv := Smalltalk arguments.\nargv notEmpty ifTrue: [ argv := argv allButFirst ].\nargv := OrderedCollection from: (argv collect: [ :arg | MALString new: arg ]).\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\nreplEnv set: #eval value: (Fn new: [ :args | MAL EVAL: args first env: replEnv ]).\nreplEnv set: #'*ARGV*' value: (MALList new: argv).\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\nMAL rep: '(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))' env: replEnv.\nMAL rep: '(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))' env: replEnv.\n\nSmalltalk arguments notEmpty ifTrue: [\n    MAL rep: '(load-file \"', Smalltalk arguments first, '\")' env: replEnv\n] ifFalse: [\n    [ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n        input isEmpty ifFalse: [\n            ReadLine addHistory: input.\n            ReadLine writeHistory: historyFile.\n            [ (MAL rep: input env: replEnv) displayNl ]\n                on: MALEmptyInput do: [ #return ]\n                on: MALError do:\n                    [ :err | ('error: ', err messageText) displayNl. #return ].\n        ]\n    ].\n\n    '' displayNl.\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/stepA_mal.st",
    "content": "String extend [\n    String >> loadRelative [\n        | scriptPath scriptDirectory |\n        scriptPath := thisContext currentFileName.\n        scriptDirectory := FilePath stripFileNameFor: scriptPath.\n        FileStream fileIn: (FilePath append: self to: scriptDirectory)\n    ]\n]\n\n'readline.st' loadRelative.\n'util.st' loadRelative.\n'types.st' loadRelative.\n'reader.st' loadRelative.\n'printer.st' loadRelative.\n'env.st' loadRelative.\n'func.st' loadRelative.\n'core.st' loadRelative.\n\nObject subclass: MAL [\n    MAL class >> READ: input [\n        ^Reader readStr: input\n    ]\n\n    MAL class >> evalList: list env: env [\n        ^list collect:\n            [ :item | self EVAL: item env: env ].\n    ]\n\n    MAL class >> starts_with: ast sym: sym  [\n        | a a0 |\n        ast type = #list ifFalse: [ ^false. ].\n        a := ast value.\n        a isEmpty ifTrue: [ ^false. ].\n        a0 := a first.\n        ^a0 type = #symbol and: [ a0 value = sym ].\n    ]\n\n    MAL class >> quasiquote: ast [\n        | result acc |\n        (ast type = #symbol or: [ ast type = #map ]) ifTrue: [\n            result := {MALSymbol new: #quote. ast}.\n            ^MALList new: (OrderedCollection from: result)\n        ].\n        (ast type = #list or: [ ast type = #vector ]) ifFalse: [\n            ^ast\n        ].\n\n        (self starts_with: ast sym: #unquote) ifTrue: [\n            ^ast value second\n        ].\n\n        result := {}.\n        acc := MALList new: (OrderedCollection from: result).\n        ast value reverseDo: [ : elt |\n            (self starts_with: elt sym: #'splice-unquote') ifTrue: [\n                result := {MALSymbol new: #concat. elt value second. acc}\n            ] ifFalse: [\n                result := {MALSymbol new: #cons. self quasiquote: elt. acc}\n            ].\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ast type = #vector ifTrue: [\n            result := {MALSymbol new: #vec. acc}.\n            acc := MALList new: (OrderedCollection from: result)\n        ].\n        ^acc\n    ]\n\n    MAL class >> EVAL: aSexp env: anEnv [\n        | sexp env ast a0 a0_ a1 a1_ a2 a2_ a3 function args |\n\n        \"NOTE: redefinition of method arguments is not allowed\"\n        sexp := aSexp.\n        env := anEnv.\n\n        [\n            [ :continue |\n\n                a0 := env get: #'DEBUG-EVAL'.\n                (a0 isNil or: [ a0 type = #false or: [ a0 type = #nil ] ] )\n                ifFalse: [\n                    ('EVAL: ' , (Printer prStr: sexp printReadably: true))\n                    displayNl.\n                ].\n\n                sexp type = #symbol ifTrue: [\n                    | key value |\n                    key := sexp value.\n                    value := env get: key.\n                    value isNil ifTrue: [\n                        ^MALUnknownSymbol new signal: key\n                    ].\n                    ^value\n                ].\n                sexp type = #vector ifTrue: [\n                    ^MALVector new: (self evalList: sexp value env: env)\n                ].\n                sexp type = #map ifTrue: [\n                    ^MALMap new: (self evalList: sexp value env: env)\n                ].\n                sexp type ~= #list ifTrue: [\n                    ^sexp\n                ].\n                sexp value isEmpty ifTrue: [\n                    ^sexp\n                ].\n\n                ast := sexp value.\n                a0 := ast first.\n\n                a0_ := ast first value.\n                a0_ = #'def!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := self EVAL: a2 env: env.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'defmacro!' ifTrue: [\n                    | result |\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    result := (self EVAL: a2 env: env) deepCopy.\n                    result isMacro: true.\n                    env set: a1_ value: result.\n                    ^result\n                ].\n\n                a0_ = #'let*' ifTrue: [\n                    | env_ |\n                        env_ := Env new: env.\n                    a1_ := ast second value.\n                    a2 := ast third.\n                    1 to: a1_ size by: 2 do:\n                        [ :i | env_ set: (a1_ at: i) value\n                                    value: (self EVAL: (a1_ at: i + 1)\n                                                 env: env_) ].\n                    env := env_.\n                    sexp := a2.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #do ifTrue: [\n                    | forms last |\n                    ast size < 2 ifTrue: [\n                        forms := {}.\n                        last := MALObject Nil.\n                    ] ifFalse: [\n                        forms := ast copyFrom: 2 to: ast size - 1.\n                        last := ast last.\n                    ].\n\n                    forms do: [ :form | self EVAL: form env: env ].\n                    sexp := last.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #if ifTrue: [\n                    | condition |\n                    a1 := ast second.\n                    a2 := ast third.\n                    a3 := ast at: 4 ifAbsent: [ MALObject Nil ].\n                    condition := self EVAL: a1 env: env.\n\n                    (condition type = #false or:\n                         [ condition type = #nil ]) ifTrue: [\n                             sexp := a3\n                         ] ifFalse: [\n                             sexp := a2\n                         ].\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #quote ifTrue: [\n                    a1 := ast second.\n                    ^a1\n                ].\n\n                a0_ = #quasiquote ifTrue: [\n                    | result |\n                    a1 := ast second.\n                    sexp := self quasiquote: a1.\n                    continue value \"TCO\"\n                ].\n\n                a0_ = #'try*' ifTrue: [\n                    | A B C |\n                    A := ast second.\n                    ast at: 3 ifAbsent: [\n                        ^self EVAL: A env: env.\n                    ].\n                    a2_ := ast third value.\n                    B := a2_ second value.\n                    C := a2_ third.\n                    ^[ self EVAL: A env: env ] on: MALError do:\n                        [ :err |\n                            | data env_ result |\n                            data := err data.\n                            data isString ifTrue: [\n                                data := MALString new: data\n                            ].\n                            env_ := Env new: env binds: {B} exprs: {data}.\n                            err return: (self EVAL: C env: env_)\n                        ]\n                ].\n\n                a0_ = #'fn*' ifTrue: [\n                    | binds env_ fn |\n                        a1_ := ast second value.\n                    binds := a1_ collect: [ :item | item value ].\n                    a2 := ast third.\n                    fn := [ :args |\n                        self EVAL: a2 env:\n                            (Env new: env binds: binds exprs: args) ].\n                    ^Func new: a2 params: binds env: env fn: fn\n                ].\n\n                function := self EVAL: a0 env: env.\n                args := ast allButFirst asArray.\n                (function type = #func and: [ function isMacro ]) ifTrue: [\n                    sexp := function fn value: args.\n                    continue value TCO\n                ].\n                args := self evalList: args env: env.\n                function type = #fn ifTrue: [ ^function fn value: args ].\n                function type = #func ifTrue: [\n                    | env_ |\n                    sexp := function ast.\n                    env_ := Env new: function env binds: function params\n                                exprs: args.\n                    env := env_.\n                    continue value \"TCO\"\n                ]\n            ] valueWithExit\n        ] repeat.\n    ]\n\n    MAL class >> PRINT: sexp [\n        ^Printer prStr: sexp printReadably: true\n    ]\n\n    MAL class >> rep: input env: env [\n        ^self PRINT: (self EVAL: (self READ: input) env: env)\n    ]\n]\n\n| input historyFile replEnv argv |\n\nhistoryFile := '.mal_history'.\nReadLine readHistory: historyFile.\nreplEnv := Env new: nil.\n\nargv := Smalltalk arguments.\nargv notEmpty ifTrue: [ argv := argv allButFirst ].\nargv := OrderedCollection from: (argv collect: [ :arg | MALString new: arg ]).\n\nCore Ns keysAndValuesDo: [ :op :block | replEnv set: op value: block ].\nreplEnv set: #eval value: (Fn new: [ :args | MAL EVAL: args first env: replEnv ]).\nreplEnv set: #'*ARGV*' value: (MALList new: argv).\nreplEnv set: #'*host-language*' value: (MALString new: 'smalltalk').\n\nMAL rep: '(def! not (fn* (a) (if a false true)))' env: replEnv.\nMAL rep: '(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))' env: replEnv.\nMAL rep: '(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))' env: replEnv.\n\nSmalltalk arguments notEmpty ifTrue: [\n    MAL rep: '(load-file \"', Smalltalk arguments first, '\")' env: replEnv\n] ifFalse: [\n    MAL rep: '(println (str \"Mal [\" *host-language* \"]\"))' env: replEnv.\n    [ input := ReadLine readLine: 'user> '. input isNil ] whileFalse: [\n        input isEmpty ifFalse: [\n            ReadLine addHistory: input.\n            ReadLine writeHistory: historyFile.\n            [ (MAL rep: input env: replEnv) displayNl ]\n                on: MALEmptyInput do: [ #return ]\n                on: MALError do:\n                    [ :err | ('error: ', err messageText) displayNl. #return ].\n        ]\n    ].\n\n    '' displayNl.\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/tests/stepA_mal.mal",
    "content": "(gst-eval \"1 + 1\")\n;=>2\n\n(gst-eval \"{1. 2. 3}\")\n;=>[1 2 3]\n\n(gst-eval \"#('a' 'b' 'c') join: ' '\")\n;=>\"a b c\"\n\n(gst-eval \"'Hello World!' displayNl\")\n;/Hello World!\n"
  },
  {
    "path": "impls/gnu-smalltalk/types.st",
    "content": "Object subclass: MALObject [\n    | type value meta |\n\n    type [ ^type ]\n    value [ ^value ]\n    meta [ ^meta ]\n\n    value: aValue [\n        value := aValue.\n    ]\n\n    meta: aMeta [\n        meta := aMeta.\n    ]\n\n    MALObject class >> new: type value: value meta: meta [\n        | object |\n        object := super new.\n        object init: type value: value meta: meta.\n        ^object\n    ]\n\n    init: aType value: aValue meta: aMeta [\n        type := aType.\n        value := aValue.\n        meta := aMeta.\n    ]\n\n    withMeta: meta [\n        | object |\n        object := self deepCopy.\n        object meta: meta.\n        ^object\n    ]\n\n    printOn: stream [\n        stream nextPutAll: '<';\n            nextPutAll: self class printString;\n            nextPutAll: ': ';\n            nextPutAll: value printString.\n        meta notNil ifTrue: [\n            stream nextPutAll: ' | '\n                nextPutAll: meta printString.\n        ].\n        stream nextPutAll: '>'.\n    ]\n\n    = x [\n        self type ~= x type ifTrue: [ ^false ].\n        ^self value = x value\n    ]\n\n    hash [\n        ^self value hash\n    ]\n]\n\nMALObject subclass: MALTrue [\n    MALTrue class >> new [\n        ^super new: #true value: true meta: nil.\n    ]\n]\n\nMALObject subclass: MALFalse [\n    MALFalse class >> new [\n        ^super new: #false value: false meta: nil.\n    ]\n]\n\nMALObject subclass: MALNil [\n    MALNil class >> new [\n        ^super new: #nil value: nil meta: nil.\n    ]\n]\n\nMALObject class extend [\n    True := MALTrue new.\n    False := MALFalse new.\n    Nil := MALNil new.\n\n    True [ ^True ]\n    False [ ^False ]\n    Nil [ ^Nil ]\n]\n\nMALObject subclass: MALNumber [\n    MALNumber class >> new: value [\n        ^super new: #number value: value meta: nil.\n    ]\n]\n\nMALObject subclass: MALString [\n    MALString class >> new: value [\n        ^super new: #string value: value meta: nil.\n    ]\n]\n\nMALObject subclass: MALSymbol [\n    MALSymbol class >> new: value [\n        ^super new: #symbol value: value meta: nil.\n    ]\n]\n\nMALObject subclass: MALKeyword [\n    MALKeyword class >> new: value [\n        ^super new: #keyword value: value meta: nil.\n    ]\n]\n\nMALObject subclass: MALList [\n    MALList class >> new: value [\n        ^super new: #list value: value meta: nil.\n    ]\n\n    = x [\n        (x type ~= #list and: [ x type ~= #vector ]) ifTrue: [ ^false ].\n        ^self value = x value\n    ]\n]\n\nMALObject subclass: MALVector [\n    MALVector class >> new: value [\n        ^super new: #vector value: value meta: nil.\n    ]\n\n    = x [\n        (x type ~= #vector and: [ x type ~= #list ]) ifTrue: [ ^false ].\n        ^self value = x value\n    ]\n]\n\nMALObject subclass: MALMap [\n    MALMap class >> new: value [\n        ^super new: #map value: value meta: nil.\n    ]\n]\n\nMALObject subclass: MALAtom [\n    MALAtom class >> new: value [\n        ^super new: #atom value: value meta: nil.\n    ]\n]\n\nMALObject subclass: Fn [\n    | fn |\n\n    fn [ ^fn ]\n\n    Fn class >> new: fn [\n        | f |\n        f := super new: #fn value: fn meta: nil.\n        f init: fn.\n        ^f\n    ]\n\n    init: f [\n        fn := f.\n    ]\n]\n\nError subclass: MALError [\n    description [ ^'A MAL-related error' ]\n    isResumable [ ^true ]\n\n    data [ ^self messageText ]\n]\n\nMALError subclass: MALUnterminatedSequence [\n    MALUnterminatedSequence class >> new [ ^super new ]\n\n    messageText [ ^'expected ''', self basicMessageText, ''', got EOF' ]\n]\n\nMALError subclass: MALUnexpectedToken [\n    MALUnexpectedToken class >> new [ ^super new ]\n\n    messageText [ ^'unexpected token: ''', self basicMessageText, '''']\n]\n\nMALError subclass: MALEmptyInput [\n    MALEmptyInput class >> new [ ^super new ]\n\n    messageText [ ^'Empty input' ]\n]\n\nMALError subclass: MALUnknownSymbol [\n    MALUnknownSymbol class >> new [ ^super new ]\n\n    messageText [ ^'''', self basicMessageText, ''' not found']\n]\n\nMALError subclass: MALOutOfBounds [\n    MALOutOfBounds class >> new [ ^super new ]\n\n    messageText [ ^'Out of bounds' ]\n]\n\nMALError subclass: MALCustomError [\n    MALCustomError class >> new [ ^super new ]\n\n    messageText [ ^Printer prStr: self basicMessageText printReadably: true ]\n    data [ ^self basicMessageText ]\n]\n"
  },
  {
    "path": "impls/gnu-smalltalk/util.st",
    "content": "SequenceableCollection extend [\n    asDictionary [\n        | dict assoc |\n        dict := Dictionary new.\n        1 to: self size by: 2 do:\n            [ :i | dict add: (self at: i) -> (self at: i + 1) ].\n        ^dict\n    ]\n]\n\nString extend [\n    parse [\n        |text canary|\n        canary := 8r177 asCharacter asString.\n        text := self copyFrom: 2 to: self size - 1.\n        text := text copyReplaceAll: '\\\\' with: canary.\n        text := text copyReplaceAll: '\\\"' with: '\"'.\n        text := text copyReplaceAll: '\\n' with: '\n'.\n        text := text copyReplaceAll: canary with: '\\'.\n        ^text\n    ]\n\n    repr [\n        |text|\n        text := self copyReplaceAll: '\\' with: '\\\\'.\n        text := text copyReplaceAll: '\n' with: '\\n'.\n        text := text copyReplaceAll: '\"' with: '\\\"'.\n        ^'\"', text, '\"'\n    ]\n]\n\nBlockClosure extend [\n    valueWithExit [\n        ^self value: [ ^nil ]\n    ]\n]\n\nObject extend [\n    toMALValue [\n        self = true ifTrue: [ ^MALObject True ].\n        self = false ifTrue: [ ^MALObject False ].\n        self = nil ifTrue: [ ^MALObject Nil ].\n        self isNumber ifTrue: [ ^MALNumber new: self ].\n        self isString ifTrue: [ ^MALString new: self ].\n        self isSymbol ifTrue: [ ^MALSymbol new: self ].\n        self isArray ifTrue: [\n            ^MALVector new: (self asOrderedCollection collect:\n                                 [ :item | item toMALValue ])\n        ].\n        self isSequenceable ifTrue: [\n            ^MALList new: (self asOrderedCollection collect:\n                               [ :item | item toMALValue ])\n        ].\n        self class = Dictionary ifTrue: [\n            | result |\n            result := Dictionary new.\n            self keysAndValuesDo: [ :key :value |\n                result at: key toMALValue put: value toMALValue\n            ].\n            ^MALMap new: result\n        ]\n    ]\n]\n\n\"NOTE: bugfix version from 3.2.91 for 3.2.4\"\nNamespace current: Kernel [\n\nMatchingRegexResults extend [\n    at: anIndex [\n\t<category: 'accessing'>\n\t| reg text |\n\tanIndex = 0 ifTrue: [^self match].\n\tcache isNil ifTrue: [cache := Array new: registers size].\n\t(cache at: anIndex) isNil\n\t    ifTrue:\n\t\t[reg := registers at: anIndex.\n\t\ttext := reg isNil\n\t\t\t    ifTrue: [nil]\n\t\t\t    ifFalse: [\n\t\t\t\treg isEmpty\n\t\t\t\t\tifTrue: ['']\n\t\t\t\t\tifFalse: [self subject copyFrom: reg first to: reg last]].\n\t\tcache at: anIndex put: text].\n\t^cache at: anIndex\n    ]\n]\n\n]\n"
  },
  {
    "path": "impls/go/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ golang libreadline-dev libedit-dev pkg-config\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/go/Makefile",
    "content": "#####################\n\nSOURCES_BASE = src/types/types.go src/readline/readline.go \\\n\t       src/reader/reader.go src/printer/printer.go \\\n\t       src/env/env.go src/core/core.go\n\n#####################\n\nSRCS = step0_repl.go step1_read_print.go step2_eval.go step3_env.go \\\n       step4_if_fn_do.go step5_tco.go step6_file.go step7_quote.go \\\n       step8_macros.go step9_try.go stepA_mal.go\nBINS = $(SRCS:%.go=%)\n\n#####################\n\nall: $(BINS)\n\ndist: mal\n\nmal: $(word $(words $(BINS)),$(BINS))\n\tcp $< $@\n\ndefine dep_template\n$(1): $(SOURCES_BASE) src/$(1)/$(1).go\n\tgo build -o $$@ ./src/$(1)\nendef\n\n$(foreach b,$(BINS),$(eval $(call dep_template,$(b))))\n\nclean:\n\trm -f $(BINS) mal\n"
  },
  {
    "path": "impls/go/go.mod",
    "content": "module mal\n\ngo 1.22.2\n"
  },
  {
    "path": "impls/go/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/go/src/core/core.go",
    "content": "package core\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"strings\"\n\t\"time\"\n)\n\nimport (\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// Errors/Exceptions\nfunc throw(a []MalType) (MalType, error) {\n\treturn nil, MalError{a[0]}\n}\n\nfunc fn_q(a []MalType) (MalType, error) {\n\tswitch f := a[0].(type) {\n\tcase MalFunc:\n\t\treturn !f.GetMacro(), nil\n\tcase Func:\n\t\treturn true, nil\n\tcase func([]MalType) (MalType, error):\n\t\treturn true, nil\n\tdefault:\n\t\treturn false, nil\n\t}\n}\n\n// String functions\n\nfunc pr_str(a []MalType) (MalType, error) {\n\treturn printer.Pr_list(a, true, \"\", \"\", \" \"), nil\n}\n\nfunc str(a []MalType) (MalType, error) {\n\treturn printer.Pr_list(a, false, \"\", \"\", \"\"), nil\n}\n\nfunc prn(a []MalType) (MalType, error) {\n\tfmt.Println(printer.Pr_list(a, true, \"\", \"\", \" \"))\n\treturn nil, nil\n}\n\nfunc println(a []MalType) (MalType, error) {\n\tfmt.Println(printer.Pr_list(a, false, \"\", \"\", \" \"))\n\treturn nil, nil\n}\n\nfunc slurp(a []MalType) (MalType, error) {\n\tb, e := ioutil.ReadFile(a[0].(string))\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\treturn string(b), nil\n}\n\n// Number functions\nfunc time_ms(a []MalType) (MalType, error) {\n\treturn int(time.Now().UnixNano() / int64(time.Millisecond)), nil\n}\n\n// Hash Map functions\nfunc copy_hash_map(hm HashMap) HashMap {\n\tnew_hm := HashMap{map[string]MalType{}, nil}\n\tfor k, v := range hm.Val {\n\t\tnew_hm.Val[k] = v\n\t}\n\treturn new_hm\n}\n\nfunc assoc(a []MalType) (MalType, error) {\n\tif len(a) < 3 {\n\t\treturn nil, errors.New(\"assoc requires at least 3 arguments\")\n\t}\n\tif len(a)%2 != 1 {\n\t\treturn nil, errors.New(\"assoc requires odd number of arguments\")\n\t}\n\tif !HashMap_Q(a[0]) {\n\t\treturn nil, errors.New(\"assoc called on non-hash map\")\n\t}\n\tnew_hm := copy_hash_map(a[0].(HashMap))\n\tfor i := 1; i < len(a); i += 2 {\n\t\tkey := a[i]\n\t\tif !String_Q(key) {\n\t\t\treturn nil, errors.New(\"assoc called with non-string key\")\n\t\t}\n\t\tnew_hm.Val[key.(string)] = a[i+1]\n\t}\n\treturn new_hm, nil\n}\n\nfunc dissoc(a []MalType) (MalType, error) {\n\tif len(a) < 2 {\n\t\treturn nil, errors.New(\"dissoc requires at least 3 arguments\")\n\t}\n\tif !HashMap_Q(a[0]) {\n\t\treturn nil, errors.New(\"dissoc called on non-hash map\")\n\t}\n\tnew_hm := copy_hash_map(a[0].(HashMap))\n\tfor i := 1; i < len(a); i += 1 {\n\t\tkey := a[i]\n\t\tif !String_Q(key) {\n\t\t\treturn nil, errors.New(\"dissoc called with non-string key\")\n\t\t}\n\t\tdelete(new_hm.Val, key.(string))\n\t}\n\treturn new_hm, nil\n}\n\nfunc get(a []MalType) (MalType, error) {\n\tif Nil_Q(a[0]) {\n\t\treturn nil, nil\n\t}\n\tif !HashMap_Q(a[0]) {\n\t\treturn nil, errors.New(\"get called on non-hash map\")\n\t}\n\tif !String_Q(a[1]) {\n\t\treturn nil, errors.New(\"get called with non-string key\")\n\t}\n\treturn a[0].(HashMap).Val[a[1].(string)], nil\n}\n\nfunc contains_Q(hm MalType, key MalType) (MalType, error) {\n\tif Nil_Q(hm) {\n\t\treturn false, nil\n\t}\n\tif !HashMap_Q(hm) {\n\t\treturn nil, errors.New(\"get called on non-hash map\")\n\t}\n\tif !String_Q(key) {\n\t\treturn nil, errors.New(\"get called with non-string key\")\n\t}\n\t_, ok := hm.(HashMap).Val[key.(string)]\n\treturn ok, nil\n}\n\nfunc keys(a []MalType) (MalType, error) {\n\tif !HashMap_Q(a[0]) {\n\t\treturn nil, errors.New(\"keys called on non-hash map\")\n\t}\n\tslc := []MalType{}\n\tfor k, _ := range a[0].(HashMap).Val {\n\t\tslc = append(slc, k)\n\t}\n\treturn List{slc, nil}, nil\n}\n\nfunc vals(a []MalType) (MalType, error) {\n\tif !HashMap_Q(a[0]) {\n\t\treturn nil, errors.New(\"keys called on non-hash map\")\n\t}\n\tslc := []MalType{}\n\tfor _, v := range a[0].(HashMap).Val {\n\t\tslc = append(slc, v)\n\t}\n\treturn List{slc, nil}, nil\n}\n\n// Sequence functions\n\nfunc cons(a []MalType) (MalType, error) {\n\tval := a[0]\n\tlst, e := GetSlice(a[1])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\treturn List{append([]MalType{val}, lst...), nil}, nil\n}\n\nfunc concat(a []MalType) (MalType, error) {\n\tif len(a) == 0 {\n\t\treturn List{}, nil\n\t}\n\tslc1, e := GetSlice(a[0])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tfor i := 1; i < len(a); i += 1 {\n\t\tslc2, e := GetSlice(a[i])\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tslc1 = append(slc1, slc2...)\n\t}\n\treturn List{slc1, nil}, nil\n}\n\nfunc vec(a []MalType) (MalType, error) {\n\tswitch obj := a[0].(type) {\n\tcase Vector:\n\t\treturn obj, nil\n\tcase List:\n\t\treturn Vector{obj.Val, nil}, nil\n\tdefault:\n\t\treturn nil, errors.New(\"vec: expects a sequence\")\n\t}\n}\n\nfunc nth(a []MalType) (MalType, error) {\n\tslc, e := GetSlice(a[0])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tidx := a[1].(int)\n\tif idx < len(slc) {\n\t\treturn slc[idx], nil\n\t} else {\n\t\treturn nil, errors.New(\"nth: index out of range\")\n\t}\n}\n\nfunc first(a []MalType) (MalType, error) {\n\tif len(a) == 0 {\n\t\treturn nil, nil\n\t}\n\tif a[0] == nil {\n\t\treturn nil, nil\n\t}\n\tslc, e := GetSlice(a[0])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tif len(slc) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn slc[0], nil\n}\n\nfunc rest(a []MalType) (MalType, error) {\n\tif a[0] == nil {\n\t\treturn List{}, nil\n\t}\n\tslc, e := GetSlice(a[0])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tif len(slc) == 0 {\n\t\treturn List{}, nil\n\t}\n\treturn List{slc[1:], nil}, nil\n}\n\nfunc empty_Q(a []MalType) (MalType, error) {\n\tswitch obj := a[0].(type) {\n\tcase List:\n\t\treturn len(obj.Val) == 0, nil\n\tcase Vector:\n\t\treturn len(obj.Val) == 0, nil\n\tcase nil:\n\t\treturn true, nil\n\tdefault:\n\t\treturn nil, errors.New(\"empty? called on non-sequence\")\n\t}\n}\n\nfunc count(a []MalType) (MalType, error) {\n\tswitch obj := a[0].(type) {\n\tcase List:\n\t\treturn len(obj.Val), nil\n\tcase Vector:\n\t\treturn len(obj.Val), nil\n\tcase map[string]MalType:\n\t\treturn len(obj), nil\n\tcase nil:\n\t\treturn 0, nil\n\tdefault:\n\t\treturn nil, errors.New(\"count called on non-sequence\")\n\t}\n}\n\nfunc apply(a []MalType) (MalType, error) {\n\tif len(a) < 2 {\n\t\treturn nil, errors.New(\"apply requires at least 2 args\")\n\t}\n\tf := a[0]\n\targs := []MalType{}\n\tfor _, b := range a[1 : len(a)-1] {\n\t\targs = append(args, b)\n\t}\n\tlast, e := GetSlice(a[len(a)-1])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\targs = append(args, last...)\n\treturn Apply(f, args)\n}\n\nfunc do_map(a []MalType) (MalType, error) {\n\tf := a[0]\n\tresults := []MalType{}\n\targs, e := GetSlice(a[1])\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tfor _, arg := range args {\n\t\tres, e := Apply(f, []MalType{arg})\n\t\tresults = append(results, res)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t}\n\treturn List{results, nil}, nil\n}\n\nfunc conj(a []MalType) (MalType, error) {\n\tif len(a) < 2 {\n\t\treturn nil, errors.New(\"conj requires at least 2 arguments\")\n\t}\n\tswitch seq := a[0].(type) {\n\tcase List:\n\t\tnew_slc := []MalType{}\n\t\tfor i := len(a) - 1; i > 0; i -= 1 {\n\t\t\tnew_slc = append(new_slc, a[i])\n\t\t}\n\t\treturn List{append(new_slc, seq.Val...), nil}, nil\n\tcase Vector:\n\t\tnew_slc := seq.Val\n\t\tfor _, x := range a[1:] {\n\t\t\tnew_slc = append(new_slc, x)\n\t\t}\n\t\treturn Vector{new_slc, nil}, nil\n\t}\n\n\tif !HashMap_Q(a[0]) {\n\t\treturn nil, errors.New(\"dissoc called on non-hash map\")\n\t}\n\tnew_hm := copy_hash_map(a[0].(HashMap))\n\tfor i := 1; i < len(a); i += 1 {\n\t\tkey := a[i]\n\t\tif !String_Q(key) {\n\t\t\treturn nil, errors.New(\"dissoc called with non-string key\")\n\t\t}\n\t\tdelete(new_hm.Val, key.(string))\n\t}\n\treturn new_hm, nil\n}\n\nfunc seq(a []MalType) (MalType, error) {\n\tif a[0] == nil {\n\t\treturn nil, nil\n\t}\n\tswitch arg := a[0].(type) {\n\tcase List:\n\t\tif len(arg.Val) == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn arg, nil\n\tcase Vector:\n\t\tif len(arg.Val) == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn List{arg.Val, nil}, nil\n\tcase string:\n\t\tif len(arg) == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\t\tnew_slc := []MalType{}\n\t\tfor _, ch := range strings.Split(arg, \"\") {\n\t\t\tnew_slc = append(new_slc, ch)\n\t\t}\n\t\treturn List{new_slc, nil}, nil\n\t}\n\treturn nil, errors.New(\"seq requires string or list or vector or nil\")\n}\n\n// Metadata functions\nfunc with_meta(a []MalType) (MalType, error) {\n\tobj := a[0]\n\tm := a[1]\n\tswitch tobj := obj.(type) {\n\tcase List:\n\t\treturn List{tobj.Val, m}, nil\n\tcase Vector:\n\t\treturn Vector{tobj.Val, m}, nil\n\tcase HashMap:\n\t\treturn HashMap{tobj.Val, m}, nil\n\tcase Func:\n\t\treturn Func{tobj.Fn, m}, nil\n\tcase MalFunc:\n\t\tfn := tobj\n\t\tfn.Meta = m\n\t\treturn fn, nil\n\tdefault:\n\t\treturn nil, errors.New(\"with-meta not supported on type\")\n\t}\n}\n\nfunc meta(a []MalType) (MalType, error) {\n\tobj := a[0]\n\tswitch tobj := obj.(type) {\n\tcase List:\n\t\treturn tobj.Meta, nil\n\tcase Vector:\n\t\treturn tobj.Meta, nil\n\tcase HashMap:\n\t\treturn tobj.Meta, nil\n\tcase Func:\n\t\treturn tobj.Meta, nil\n\tcase MalFunc:\n\t\treturn tobj.Meta, nil\n\tdefault:\n\t\treturn nil, errors.New(\"meta not supported on type\")\n\t}\n}\n\n// Atom functions\nfunc deref(a []MalType) (MalType, error) {\n\tif !Atom_Q(a[0]) {\n\t\treturn nil, errors.New(\"deref called with non-atom\")\n\t}\n\treturn a[0].(*Atom).Val, nil\n}\n\nfunc reset_BANG(a []MalType) (MalType, error) {\n\tif !Atom_Q(a[0]) {\n\t\treturn nil, errors.New(\"reset! called with non-atom\")\n\t}\n\ta[0].(*Atom).Set(a[1])\n\treturn a[1], nil\n}\n\nfunc swap_BANG(a []MalType) (MalType, error) {\n\tif !Atom_Q(a[0]) {\n\t\treturn nil, errors.New(\"swap! called with non-atom\")\n\t}\n\tatm := a[0].(*Atom)\n\targs := []MalType{atm.Val}\n\tf := a[1]\n\targs = append(args, a[2:]...)\n\tres, e := Apply(f, args)\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tatm.Set(res)\n\treturn res, nil\n}\n\n// core namespace\nvar NS = map[string]MalType{\n\t\"=\":       call2b(Equal_Q),\n\t\"throw\":   call1e(throw),\n\t\"nil?\":    call1b(Nil_Q),\n\t\"true?\":   call1b(True_Q),\n\t\"false?\":  call1b(False_Q),\n\t\"symbol\":  call1e(func(a []MalType) (MalType, error) { return Symbol{a[0].(string)}, nil }),\n\t\"symbol?\": call1b(Symbol_Q),\n\t\"string?\": call1e(func(a []MalType) (MalType, error) { return (String_Q(a[0]) && !Keyword_Q(a[0])), nil }),\n\t\"keyword\": call1e(func(a []MalType) (MalType, error) {\n\t\tif Keyword_Q(a[0]) {\n\t\t\treturn a[0], nil\n\t\t} else {\n\t\t\treturn NewKeyword(a[0].(string))\n\t\t}\n\t}),\n\t\"keyword?\":    call1b(Keyword_Q),\n\t\"number?\":     call1b(Number_Q),\n\t\"fn?\":         call1e(fn_q),\n\t\"macro?\":      call1e(func(a []MalType) (MalType, error) { return MalFunc_Q(a[0]) && a[0].(MalFunc).GetMacro(), nil }),\n\t\"pr-str\":      callNe(pr_str),\n\t\"str\":         callNe(str),\n\t\"prn\":         callNe(prn),\n\t\"println\":     callNe(println),\n\t\"read-string\": call1e(func(a []MalType) (MalType, error) { return reader.Read_str(a[0].(string)) }),\n\t\"slurp\":       call1e(slurp),\n\t\"readline\":    call1e(func(a []MalType) (MalType, error) { return readline.Readline(a[0].(string)) }),\n\t\"<\":           call2e(func(a []MalType) (MalType, error) { return a[0].(int) < a[1].(int), nil }),\n\t\"<=\":          call2e(func(a []MalType) (MalType, error) { return a[0].(int) <= a[1].(int), nil }),\n\t\">\":           call2e(func(a []MalType) (MalType, error) { return a[0].(int) > a[1].(int), nil }),\n\t\">=\":          call2e(func(a []MalType) (MalType, error) { return a[0].(int) >= a[1].(int), nil }),\n\t\"+\":           call2e(func(a []MalType) (MalType, error) { return a[0].(int) + a[1].(int), nil }),\n\t\"-\":           call2e(func(a []MalType) (MalType, error) { return a[0].(int) - a[1].(int), nil }),\n\t\"*\":           call2e(func(a []MalType) (MalType, error) { return a[0].(int) * a[1].(int), nil }),\n\t\"/\":           call2e(func(a []MalType) (MalType, error) { return a[0].(int) / a[1].(int), nil }),\n\t\"time-ms\":     call0e(time_ms),\n\t\"list\":        callNe(func(a []MalType) (MalType, error) { return List{a, nil}, nil }),\n\t\"list?\":       call1b(List_Q),\n\t\"vector\":      callNe(func(a []MalType) (MalType, error) { return Vector{a, nil}, nil }),\n\t\"vector?\":     call1b(Vector_Q),\n\t\"hash-map\":    callNe(func(a []MalType) (MalType, error) { return NewHashMap(List{a, nil}) }),\n\t\"map?\":        call1b(HashMap_Q),\n\t\"assoc\":       callNe(assoc),  // at least 3\n\t\"dissoc\":      callNe(dissoc), // at least 2\n\t\"get\":         call2e(get),\n\t\"contains?\":   call2e(func(a []MalType) (MalType, error) { return contains_Q(a[0], a[1]) }),\n\t\"keys\":        call1e(keys),\n\t\"vals\":        call1e(vals),\n\t\"sequential?\": call1b(Sequential_Q),\n\t\"cons\":        call2e(cons),\n\t\"concat\":      callNe(concat),\n\t\"vec\":         call1e(vec),\n\t\"nth\":         call2e(nth),\n\t\"first\":       call1e(first),\n\t\"rest\":        call1e(rest),\n\t\"empty?\":      call1e(empty_Q),\n\t\"count\":       call1e(count),\n\t\"apply\":       callNe(apply), // at least 2\n\t\"map\":         call2e(do_map),\n\t\"conj\":        callNe(conj), // at least 2\n\t\"seq\":         call1e(seq),\n\t\"with-meta\":   call2e(with_meta),\n\t\"meta\":        call1e(meta),\n\t\"atom\":        call1e(func(a []MalType) (MalType, error) { return &Atom{a[0], nil}, nil }),\n\t\"atom?\":       call1b(Atom_Q),\n\t\"deref\":       call1e(deref),\n\t\"reset!\":      call2e(reset_BANG),\n\t\"swap!\":       callNe(swap_BANG),\n}\n\n// callXX functions check the number of arguments\nfunc call0e(f func([]MalType) (MalType, error)) func([]MalType) (MalType, error) {\n\treturn func(args []MalType) (MalType, error) {\n\t\tif len(args) != 0 {\n\t\t\treturn nil, fmt.Errorf(\"wrong number of arguments (%d instead of 0)\", len(args))\n\t\t}\n\t\treturn f(args)\n\t}\n}\n\nfunc call1e(f func([]MalType) (MalType, error)) func([]MalType) (MalType, error) {\n\treturn func(args []MalType) (MalType, error) {\n\t\tif len(args) != 1 {\n\t\t\treturn nil, fmt.Errorf(\"wrong number of arguments (%d instead of 1)\", len(args))\n\t\t}\n\t\treturn f(args)\n\t}\n}\n\nfunc call2e(f func([]MalType) (MalType, error)) func([]MalType) (MalType, error) {\n\treturn func(args []MalType) (MalType, error) {\n\t\tif len(args) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"wrong number of arguments (%d instead of 2)\", len(args))\n\t\t}\n\t\treturn f(args)\n\t}\n}\n\nfunc callNe(f func([]MalType) (MalType, error)) func([]MalType) (MalType, error) {\n\t// just for documenting purposes, does not check anything\n\treturn func(args []MalType) (MalType, error) {\n\t\treturn f(args)\n\t}\n}\n\nfunc call1b(f func(MalType) bool) func([]MalType) (MalType, error) {\n\treturn func(args []MalType) (MalType, error) {\n\t\tif len(args) != 1 {\n\t\t\treturn nil, fmt.Errorf(\"wrong number of arguments (%d instead of 1)\", len(args))\n\t\t}\n\t\treturn f(args[0]), nil\n\t}\n}\n\nfunc call2b(f func(MalType, MalType) bool) func([]MalType) (MalType, error) {\n\treturn func(args []MalType) (MalType, error) {\n\t\tif len(args) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"wrong number of arguments (%d instead of 2)\", len(args))\n\t\t}\n\t\treturn f(args[0], args[1]), nil\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/env/env.go",
    "content": "package env\n\nimport (\n\t\"errors\"\n\t//\"fmt\"\n)\n\nimport (\n\t. \"mal/src/types\"\n)\n\ntype Env struct {\n\tdata  map[string]MalType\n\touter EnvType\n}\n\nfunc NewEnv(outer EnvType, binds_mt MalType, exprs_mt MalType) (EnvType, error) {\n\tenv := Env{map[string]MalType{}, outer}\n\n\tif binds_mt != nil && exprs_mt != nil {\n\t\tbinds, e := GetSlice(binds_mt)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\texprs, e := GetSlice(exprs_mt)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\t// Return a new Env with symbols in binds boudn to\n\t\t// corresponding values in exprs\n\t\tfor i := 0; i < len(binds); i += 1 {\n\t\t\tif Symbol_Q(binds[i]) && binds[i].(Symbol).Val == \"&\" {\n\t\t\t\tenv.data[binds[i+1].(Symbol).Val] = List{exprs[i:], nil}\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tenv.data[binds[i].(Symbol).Val] = exprs[i]\n\t\t\t}\n\t\t}\n\t}\n\t//return &et, nil\n\treturn env, nil\n}\n\nfunc (e Env) Find(key Symbol) EnvType {\n\tif _, ok := e.data[key.Val]; ok {\n\t\treturn e\n\t} else if e.outer != nil {\n\t\treturn e.outer.Find(key)\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc (e Env) Set(key Symbol, value MalType) MalType {\n\te.data[key.Val] = value\n\treturn value\n}\n\nfunc (e Env) Get(key Symbol) (MalType, error) {\n\tenv := e.Find(key)\n\tif env == nil {\n\t\treturn nil, errors.New(\"'\" + key.Val + \"' not found\")\n\t}\n\treturn env.(Env).data[key.Val], nil\n}\n"
  },
  {
    "path": "impls/go/src/printer/printer.go",
    "content": "package printer\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/types\"\n)\n\nfunc Pr_list(lst []types.MalType, pr bool,\n\tstart string, end string, join string) string {\n\tstr_list := make([]string, 0, len(lst))\n\tfor _, e := range lst {\n\t\tstr_list = append(str_list, Pr_str(e, pr))\n\t}\n\treturn start + strings.Join(str_list, join) + end\n}\n\nfunc Pr_str(obj types.MalType, print_readably bool) string {\n\tswitch tobj := obj.(type) {\n\tcase types.List:\n\t\treturn Pr_list(tobj.Val, print_readably, \"(\", \")\", \" \")\n\tcase types.Vector:\n\t\treturn Pr_list(tobj.Val, print_readably, \"[\", \"]\", \" \")\n\tcase types.HashMap:\n\t\tstr_list := make([]string, 0, len(tobj.Val)*2)\n\t\tfor k, v := range tobj.Val {\n\t\t\tstr_list = append(str_list, Pr_str(k, print_readably))\n\t\t\tstr_list = append(str_list, Pr_str(v, print_readably))\n\t\t}\n\t\treturn \"{\" + strings.Join(str_list, \" \") + \"}\"\n\tcase string:\n\t\tif strings.HasPrefix(tobj, \"\\u029e\") {\n\t\t\treturn \":\" + tobj[2:len(tobj)]\n\t\t} else if print_readably {\n\t\t\treturn `\"` + strings.Replace(\n\t\t\t\tstrings.Replace(\n\t\t\t\t\tstrings.Replace(tobj, `\\`, `\\\\`, -1),\n\t\t\t\t\t`\"`, `\\\"`, -1),\n\t\t\t\t\"\\n\", `\\n`, -1) + `\"`\n\t\t} else {\n\t\t\treturn tobj\n\t\t}\n\tcase types.Symbol:\n\t\treturn tobj.Val\n\tcase nil:\n\t\treturn \"nil\"\n\tcase types.MalFunc:\n\t\treturn \"(fn* \" +\n\t\t\tPr_str(tobj.Params, true) + \" \" +\n\t\t\tPr_str(tobj.Exp, true) + \")\"\n\tcase func([]types.MalType) (types.MalType, error):\n\t\treturn fmt.Sprintf(\"<function %v>\", obj)\n\tcase *types.Atom:\n\t\treturn \"(atom \" +\n\t\t\tPr_str(tobj.Val, true) + \")\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"%v\", obj)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/reader/reader.go",
    "content": "package reader\n\nimport (\n\t\"errors\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t//\"fmt\"\n)\n\nimport (\n\t. \"mal/src/types\"\n)\n\ntype Reader interface {\n\tnext() *string\n\tpeek() *string\n}\n\ntype TokenReader struct {\n\ttokens   []string\n\tposition int\n}\n\nfunc (tr *TokenReader) next() *string {\n\tif tr.position >= len(tr.tokens) {\n\t\treturn nil\n\t}\n\ttoken := tr.tokens[tr.position]\n\ttr.position = tr.position + 1\n\treturn &token\n}\n\nfunc (tr *TokenReader) peek() *string {\n\tif tr.position >= len(tr.tokens) {\n\t\treturn nil\n\t}\n\treturn &tr.tokens[tr.position]\n}\n\nfunc tokenize(str string) []string {\n\tresults := make([]string, 0, 1)\n\t// Work around lack of quoting in backtick\n\tre := regexp.MustCompile(`[\\s,]*(~@|[\\[\\]{}()'` + \"`\" +\n\t\t`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"` + \"`\" +\n\t\t`,;)]*)`)\n\tfor _, group := range re.FindAllStringSubmatch(str, -1) {\n\t\tif (group[1] == \"\") || (group[1][0] == ';') {\n\t\t\tcontinue\n\t\t}\n\t\tresults = append(results, group[1])\n\t}\n\treturn results\n}\n\nfunc read_atom(rdr Reader) (MalType, error) {\n\ttoken := rdr.next()\n\tif token == nil {\n\t\treturn nil, errors.New(\"read_atom underflow\")\n\t}\n\tif match, _ := regexp.MatchString(`^-?[0-9]+$`, *token); match {\n\t\tvar i int\n\t\tvar e error\n\t\tif i, e = strconv.Atoi(*token); e != nil {\n\t\t\treturn nil, errors.New(\"number parse error\")\n\t\t}\n\t\treturn i, nil\n\t} else if match, _ :=\n\t\t  regexp.MatchString(`^\"(?:\\\\.|[^\\\\\"])*\"$`, *token); match {\n\t\tstr := (*token)[1 : len(*token)-1]\n\t\treturn strings.Replace(\n\t\t\tstrings.Replace(\n\t\t\t strings.Replace(\n\t\t\t  strings.Replace(str, `\\\\`, \"\\u029e\", -1),\n\t\t\t  `\\\"`, `\"`, -1),\n\t\t\t `\\n`, \"\\n\", -1),\n\t\t\t\"\\u029e\", \"\\\\\", -1), nil\n\t} else if (*token)[0] == '\"' {\n\t\treturn nil, errors.New(\"expected '\\\"', got EOF\")\n\t} else if (*token)[0] == ':' {\n\t\treturn NewKeyword((*token)[1:len(*token)])\n\t} else if *token == \"nil\" {\n\t\treturn nil, nil\n\t} else if *token == \"true\" {\n\t\treturn true, nil\n\t} else if *token == \"false\" {\n\t\treturn false, nil\n\t} else {\n\t\treturn Symbol{*token}, nil\n\t}\n\treturn token, nil\n}\n\nfunc read_list(rdr Reader, start string, end string) (MalType, error) {\n\ttoken := rdr.next()\n\tif token == nil {\n\t\treturn nil, errors.New(\"read_list underflow\")\n\t}\n\tif *token != start {\n\t\treturn nil, errors.New(\"expected '\" + start + \"'\")\n\t}\n\n\tast_list := []MalType{}\n\ttoken = rdr.peek()\n\tfor ; true; token = rdr.peek() {\n\t\tif token == nil {\n\t\t\treturn nil, errors.New(\"exepected '\" + end + \"', got EOF\")\n\t\t}\n\t\tif *token == end {\n\t\t\tbreak\n\t\t}\n\t\tf, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tast_list = append(ast_list, f)\n\t}\n\trdr.next()\n\treturn List{ast_list, nil}, nil\n}\n\nfunc read_vector(rdr Reader) (MalType, error) {\n\tlst, e := read_list(rdr, \"[\", \"]\")\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tvec := Vector{lst.(List).Val, nil}\n\treturn vec, nil\n}\n\nfunc read_hash_map(rdr Reader) (MalType, error) {\n\tmal_lst, e := read_list(rdr, \"{\", \"}\")\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\treturn NewHashMap(mal_lst)\n}\n\nfunc read_form(rdr Reader) (MalType, error) {\n\ttoken := rdr.peek()\n\tif token == nil {\n\t\treturn nil, errors.New(\"read_form underflow\")\n\t}\n\tswitch *token {\n\n\tcase `'`:\n\t\trdr.next()\n\t\tform, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn List{[]MalType{Symbol{\"quote\"}, form}, nil}, nil\n\tcase \"`\":\n\t\trdr.next()\n\t\tform, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn List{[]MalType{Symbol{\"quasiquote\"}, form}, nil}, nil\n\tcase `~`:\n\t\trdr.next()\n\t\tform, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn List{[]MalType{Symbol{\"unquote\"}, form}, nil}, nil\n\tcase `~@`:\n\t\trdr.next()\n\t\tform, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn List{[]MalType{Symbol{\"splice-unquote\"}, form}, nil}, nil\n\tcase `^`:\n\t\trdr.next()\n\t\tmeta, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tform, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn List{[]MalType{Symbol{\"with-meta\"}, form, meta}, nil}, nil\n\tcase `@`:\n\t\trdr.next()\n\t\tform, e := read_form(rdr)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn List{[]MalType{Symbol{\"deref\"}, form}, nil}, nil\n\n\t// list\n\tcase \")\":\n\t\treturn nil, errors.New(\"unexpected ')'\")\n\tcase \"(\":\n\t\treturn read_list(rdr, \"(\", \")\")\n\n\t// vector\n\tcase \"]\":\n\t\treturn nil, errors.New(\"unexpected ']'\")\n\tcase \"[\":\n\t\treturn read_vector(rdr)\n\n\t// hash-map\n\tcase \"}\":\n\t\treturn nil, errors.New(\"unexpected '}'\")\n\tcase \"{\":\n\t\treturn read_hash_map(rdr)\n\tdefault:\n\t\treturn read_atom(rdr)\n\t}\n\treturn read_atom(rdr)\n}\n\nfunc Read_str(str string) (MalType, error) {\n\tvar tokens = tokenize(str)\n\tif len(tokens) == 0 {\n\t\treturn nil, errors.New(\"<empty line>\")\n\t}\n\n\treturn read_form(&TokenReader{tokens: tokens, position: 0})\n}\n"
  },
  {
    "path": "impls/go/src/readline/readline.go",
    "content": "package readline\n\n/*\n// IMPORTANT: choose one\n#cgo LDFLAGS: -ledit\n//#cgo LDFLAGS: -lreadline // NOTE: libreadline is GPL\n\n// free()\n#include <stdlib.h>\n// readline()\n#include <stdio.h> // FILE *\n#include <readline/readline.h>\n// add_history()\n#include <readline/history.h>\n*/\nimport \"C\"\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"unsafe\"\n)\n\nvar HISTORY_FILE = \".mal-history\"\nvar history_path string\n\nfunc loadHistory(filename string) error {\n\tcontent, err := ioutil.ReadFile(history_path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, add_line := range strings.Split(string(content), \"\\n\") {\n\t\tif add_line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tc_add_line := C.CString(add_line)\n\t\tC.add_history(c_add_line)\n\t\tC.free(unsafe.Pointer(c_add_line))\n\t}\n\n\treturn nil\n}\n\nfunc init() {\n\thistory_path = filepath.Join(os.Getenv(\"HOME\"), HISTORY_FILE)\n\tloadHistory(history_path)\n}\n\nfunc Readline(prompt string) (string, error) {\n\tc_prompt := C.CString(prompt)\n\tdefer C.free(unsafe.Pointer(c_prompt))\n\n\tc_line := C.readline(c_prompt)\n\tdefer C.free(unsafe.Pointer(c_line))\n\tline := C.GoString(c_line)\n\n\tif c_line == nil {\n\t\treturn \"\", errors.New(\"C.readline call failed\")\n\t}\n\tC.add_history(c_line)\n\n\t// append to file\n\tf, e := os.OpenFile(history_path, os.O_APPEND|os.O_WRONLY, 0600)\n\tif e == nil {\n\t\tdefer f.Close()\n\n\t\t_, e = f.WriteString(line + \"\\n\")\n\t\tif e != nil {\n\t\t\tfmt.Printf(\"error writing to history\")\n\t\t}\n\t}\n\n\treturn line, nil\n}\n"
  },
  {
    "path": "impls/go/src/step0_repl/step0_repl.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/readline\"\n)\n\n// read\nfunc READ(str string) string {\n\treturn str\n}\n\n// eval\nfunc EVAL(ast string, env string) string {\n\treturn ast\n}\n\n// print\nfunc PRINT(exp string) string {\n\treturn exp\n}\n\n// repl\nfunc rep(str string) string {\n\treturn PRINT(EVAL(READ(str), \"\"))\n}\n\nfunc main() {\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tfmt.Println(rep(text))\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step1_read_print/step1_read_print.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc EVAL(ast MalType, env string) (MalType, error) {\n\treturn ast, nil\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, \"\"); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step2_eval/step2_eval.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc eval_ast(ast MalType, env map[string]MalType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\tk := ast.(Symbol).Val\n\t\texp, ok := env[k]\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"'\" + k + \"' not found\")\n\t\t}\n\t\treturn exp, nil\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env map[string]MalType) (MalType, error) {\n\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\tswitch ast.(type) {\n\tcase List: // continue\n\tdefault:\n\t\treturn eval_ast(ast, env)\n\t}\n\n\tif len(ast.(List).Val) == 0 {\n\t\treturn ast, nil\n\t}\n\n\t// apply list\n\tel, e := eval_ast(ast, env)\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tf, ok := el.(List).Val[0].(func([]MalType) (MalType, error))\n\tif !ok {\n\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t}\n\treturn f(el.(List).Val[1:])\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env = map[string]MalType{\n\t\"+\": func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) + a[1].(int), nil\n\t},\n\t\"-\": func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) - a[1].(int), nil\n\t},\n\t\"*\": func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) * a[1].(int), nil\n\t},\n\t\"/\": func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) / a[1].(int), nil\n\t},\n}\n\nfunc assertArgNum(a []MalType, n int) error {\n\tif len(a) != n {\n\t\treturn errors.New(\"wrong number of arguments\")\n\t}\n\treturn nil\n}\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step3_env/step3_env.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\tswitch ast.(type) {\n\tcase List: // continue\n\tdefault:\n\t\treturn eval_ast(ast, env)\n\t}\n\n\tif len(ast.(List).Val) == 0 {\n\t\treturn ast, nil\n\t}\n\n\t// apply list\n\ta0 := ast.(List).Val[0]\n\tvar a1 MalType = nil\n\tvar a2 MalType = nil\n\tswitch len(ast.(List).Val) {\n\tcase 1:\n\t\ta1 = nil\n\t\ta2 = nil\n\tcase 2:\n\t\ta1 = ast.(List).Val[1]\n\t\ta2 = nil\n\tdefault:\n\t\ta1 = ast.(List).Val[1]\n\t\ta2 = ast.(List).Val[2]\n\t}\n\ta0sym := \"__<*fn*>__\"\n\tif Symbol_Q(a0) {\n\t\ta0sym = a0.(Symbol).Val\n\t}\n\tswitch a0sym {\n\tcase \"def!\":\n\t\tres, e := EVAL(a2, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn env.Set(a1.(Symbol), res), nil\n\tcase \"let*\":\n\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tarr1, e := GetSlice(a1)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t}\n\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t}\n\t\treturn EVAL(a2, let_env)\n\tdefault:\n\t\tel, e := eval_ast(ast, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tf, ok := el.(List).Val[0].(func([]MalType) (MalType, error))\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t}\n\t\treturn f(el.(List).Val[1:])\n\t}\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\trepl_env.Set(Symbol{\"+\"}, func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) + a[1].(int), nil\n\t})\n\trepl_env.Set(Symbol{\"-\"}, func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) - a[1].(int), nil\n\t})\n\trepl_env.Set(Symbol{\"*\"}, func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) * a[1].(int), nil\n\t})\n\trepl_env.Set(Symbol{\"/\"}, func(a []MalType) (MalType, error) {\n\t\tif e := assertArgNum(a, 2); e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn a[0].(int) / a[1].(int), nil\n\t})\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n\nfunc assertArgNum(a []MalType, n int) error {\n\tif len(a) != n {\n\t\treturn errors.New(\"wrong number of arguments\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "impls/go/src/step4_if_fn_do/step4_if_fn_do.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\tswitch ast.(type) {\n\tcase List: // continue\n\tdefault:\n\t\treturn eval_ast(ast, env)\n\t}\n\n\tif len(ast.(List).Val) == 0 {\n\t\treturn ast, nil\n\t}\n\n\t// apply list\n\ta0 := ast.(List).Val[0]\n\tvar a1 MalType = nil\n\tvar a2 MalType = nil\n\tswitch len(ast.(List).Val) {\n\tcase 1:\n\t\ta1 = nil\n\t\ta2 = nil\n\tcase 2:\n\t\ta1 = ast.(List).Val[1]\n\t\ta2 = nil\n\tdefault:\n\t\ta1 = ast.(List).Val[1]\n\t\ta2 = ast.(List).Val[2]\n\t}\n\ta0sym := \"__<*fn*>__\"\n\tif Symbol_Q(a0) {\n\t\ta0sym = a0.(Symbol).Val\n\t}\n\tswitch a0sym {\n\tcase \"def!\":\n\t\tres, e := EVAL(a2, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn env.Set(a1.(Symbol), res), nil\n\tcase \"let*\":\n\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tarr1, e := GetSlice(a1)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t}\n\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t}\n\t\treturn EVAL(a2, let_env)\n\tcase \"do\":\n\t\tel, e := eval_ast(List{ast.(List).Val[1:], nil}, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tlst := el.(List).Val\n\t\tif len(lst) == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn lst[len(lst)-1], nil\n\tcase \"if\":\n\t\tcond, e := EVAL(a1, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tif cond == nil || cond == false {\n\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\treturn EVAL(ast.(List).Val[3], env)\n\t\t\t} else {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t} else {\n\t\t\treturn EVAL(a2, env)\n\t\t}\n\tcase \"fn*\":\n\t\treturn func(arguments []MalType) (MalType, error) {\n\t\t\tnew_env, e := NewEnv(env, a1, List{arguments, nil})\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn EVAL(a2, new_env)\n\t\t}, nil\n\tdefault:\n\t\tel, e := eval_ast(ast, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tf, ok := el.(List).Val[0].(func([]MalType) (MalType, error))\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t}\n\t\treturn f(el.(List).Val[1:])\n\t}\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, v)\n\t}\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step5_tco/step5_tco.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\tfor {\n\n\t\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\t\tswitch ast.(type) {\n\t\tcase List: // continue\n\t\tdefault:\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\n\t\tif len(ast.(List).Val) == 0 {\n\t\t\treturn ast, nil\n\t\t}\n\n\t\t// apply list\n\t\ta0 := ast.(List).Val[0]\n\t\tvar a1 MalType = nil\n\t\tvar a2 MalType = nil\n\t\tswitch len(ast.(List).Val) {\n\t\tcase 1:\n\t\t\ta1 = nil\n\t\t\ta2 = nil\n\t\tcase 2:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = nil\n\t\tdefault:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = ast.(List).Val[2]\n\t\t}\n\t\ta0sym := \"__<*fn*>__\"\n\t\tif Symbol_Q(a0) {\n\t\t\ta0sym = a0.(Symbol).Val\n\t\t}\n\t\tswitch a0sym {\n\t\tcase \"def!\":\n\t\t\tres, e := EVAL(a2, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), res), nil\n\t\tcase \"let*\":\n\t\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tarr1, e := GetSlice(a1)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t\t}\n\t\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t\t}\n\t\t\tast = a2\n\t\t\tenv = let_env\n\t\tcase \"do\":\n\t\t\tlst := ast.(List).Val\n\t\t\t_, e := eval_ast(List{lst[1 : len(lst)-1], nil}, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif len(lst) == 1 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tast = lst[len(lst)-1]\n\t\tcase \"if\":\n\t\t\tcond, e := EVAL(a1, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif cond == nil || cond == false {\n\t\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\t\tast = ast.(List).Val[3]\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tast = a2\n\t\t\t}\n\t\tcase \"fn*\":\n\t\t\tfn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}\n\t\t\treturn fn, nil\n\t\tdefault:\n\t\t\tel, e := eval_ast(ast, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tf := el.(List).Val[0]\n\t\t\tif MalFunc_Q(f) {\n\t\t\t\tfn := f.(MalFunc)\n\t\t\t\tast = fn.Exp\n\t\t\t\tenv, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:], nil})\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn, ok := f.(Func)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t\t\t}\n\t\t\t\treturn fn.Fn(el.(List).Val[1:])\n\t\t\t}\n\t\t}\n\n\t} // TCO loop\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, Func{v.(func([]MalType) (MalType, error)), nil})\n\t}\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step6_file/step6_file.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\tfor {\n\n\t\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\t\tswitch ast.(type) {\n\t\tcase List: // continue\n\t\tdefault:\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\n\t\tif len(ast.(List).Val) == 0 {\n\t\t\treturn ast, nil\n\t\t}\n\n\t\t// apply list\n\t\ta0 := ast.(List).Val[0]\n\t\tvar a1 MalType = nil\n\t\tvar a2 MalType = nil\n\t\tswitch len(ast.(List).Val) {\n\t\tcase 1:\n\t\t\ta1 = nil\n\t\t\ta2 = nil\n\t\tcase 2:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = nil\n\t\tdefault:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = ast.(List).Val[2]\n\t\t}\n\t\ta0sym := \"__<*fn*>__\"\n\t\tif Symbol_Q(a0) {\n\t\t\ta0sym = a0.(Symbol).Val\n\t\t}\n\t\tswitch a0sym {\n\t\tcase \"def!\":\n\t\t\tres, e := EVAL(a2, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), res), nil\n\t\tcase \"let*\":\n\t\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tarr1, e := GetSlice(a1)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t\t}\n\t\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t\t}\n\t\t\tast = a2\n\t\t\tenv = let_env\n\t\tcase \"do\":\n\t\t\tlst := ast.(List).Val\n\t\t\t_, e := eval_ast(List{lst[1 : len(lst)-1], nil}, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif len(lst) == 1 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tast = lst[len(lst)-1]\n\t\tcase \"if\":\n\t\t\tcond, e := EVAL(a1, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif cond == nil || cond == false {\n\t\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\t\tast = ast.(List).Val[3]\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tast = a2\n\t\t\t}\n\t\tcase \"fn*\":\n\t\t\tfn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}\n\t\t\treturn fn, nil\n\t\tdefault:\n\t\t\tel, e := eval_ast(ast, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tf := el.(List).Val[0]\n\t\t\tif MalFunc_Q(f) {\n\t\t\t\tfn := f.(MalFunc)\n\t\t\t\tast = fn.Exp\n\t\t\t\tenv, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:], nil})\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn, ok := f.(Func)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t\t\t}\n\t\t\t\treturn fn.Fn(el.(List).Val[1:])\n\t\t\t}\n\t\t}\n\n\t} // TCO loop\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, Func{v.(func([]MalType) (MalType, error)), nil})\n\t}\n\trepl_env.Set(Symbol{\"eval\"}, Func{func(a []MalType) (MalType, error) {\n\t\treturn EVAL(a[0], repl_env)\n\t}, nil})\n\trepl_env.Set(Symbol{\"*ARGV*\"}, List{})\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n\t// called with mal script to load and eval\n\tif len(os.Args) > 1 {\n\t\targs := make([]MalType, 0, len(os.Args)-2)\n\t\tfor _, a := range os.Args[2:] {\n\t\t\targs = append(args, a)\n\t\t}\n\t\trepl_env.Set(Symbol{\"*ARGV*\"}, List{args, nil})\n\t\tif _, e := rep(\"(load-file \\\"\" + os.Args[1] + \"\\\")\"); e != nil {\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tos.Exit(0)\n\t}\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step7_quote/step7_quote.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc starts_with(xs []MalType, sym string) bool {\n\tif 0 < len(xs) {\n\t\tswitch s := xs[0].(type) {\n\t\tcase Symbol:\n\t\t\treturn s.Val == sym\n\t\tdefault:\n\t\t}\n\t}\n\treturn false\n}\n\nfunc qq_loop(xs []MalType) MalType {\n\tacc := NewList()\n\tfor i := len(xs) - 1; 0<=i; i -= 1 {\n\t\telt := xs[i]\n\t\tswitch e := elt.(type) {\n\t\tcase List:\n\t\t\tif starts_with(e.Val, \"splice-unquote\") {\n\t\t\t\tacc = NewList(Symbol{\"concat\"}, e.Val[1], acc)\n\t\t\t\tcontinue\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t\tacc = NewList(Symbol{\"cons\"}, quasiquote(elt), acc)\n\t}\n\treturn acc\n}\n\nfunc quasiquote(ast MalType) MalType {\n\tswitch a := ast.(type) {\n\tcase Vector:\n\t\treturn NewList(Symbol{\"vec\"}, qq_loop(a.Val))\n\tcase HashMap, Symbol:\n\t\treturn NewList(Symbol{\"quote\"}, ast)\n\tcase List:\n\t\tif starts_with(a.Val,\"unquote\") {\n\t\t\treturn a.Val[1]\n\t\t} else {\n\t\t\treturn qq_loop(a.Val)\n\t\t}\n\tdefault:\n\t\treturn ast\n\t}\n}\n\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\tfor {\n\n\t\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\t\tswitch ast.(type) {\n\t\tcase List: // continue\n\t\tdefault:\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\n\t\tif len(ast.(List).Val) == 0 {\n\t\t\treturn ast, nil\n\t\t}\n\n\t\t// apply list\n\t\ta0 := ast.(List).Val[0]\n\t\tvar a1 MalType = nil\n\t\tvar a2 MalType = nil\n\t\tswitch len(ast.(List).Val) {\n\t\tcase 1:\n\t\t\ta1 = nil\n\t\t\ta2 = nil\n\t\tcase 2:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = nil\n\t\tdefault:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = ast.(List).Val[2]\n\t\t}\n\t\ta0sym := \"__<*fn*>__\"\n\t\tif Symbol_Q(a0) {\n\t\t\ta0sym = a0.(Symbol).Val\n\t\t}\n\t\tswitch a0sym {\n\t\tcase \"def!\":\n\t\t\tres, e := EVAL(a2, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), res), nil\n\t\tcase \"let*\":\n\t\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tarr1, e := GetSlice(a1)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t\t}\n\t\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t\t}\n\t\t\tast = a2\n\t\t\tenv = let_env\n\t\tcase \"quote\":\n\t\t\treturn a1, nil\n\t\tcase \"quasiquoteexpand\":\n\t\t\treturn quasiquote(a1), nil\n\t\tcase \"quasiquote\":\n\t\t\tast = quasiquote(a1)\n\t\tcase \"do\":\n\t\t\tlst := ast.(List).Val\n\t\t\t_, e := eval_ast(List{lst[1 : len(lst)-1], nil}, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif len(lst) == 1 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tast = lst[len(lst)-1]\n\t\tcase \"if\":\n\t\t\tcond, e := EVAL(a1, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif cond == nil || cond == false {\n\t\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\t\tast = ast.(List).Val[3]\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tast = a2\n\t\t\t}\n\t\tcase \"fn*\":\n\t\t\tfn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}\n\t\t\treturn fn, nil\n\t\tdefault:\n\t\t\tel, e := eval_ast(ast, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tf := el.(List).Val[0]\n\t\t\tif MalFunc_Q(f) {\n\t\t\t\tfn := f.(MalFunc)\n\t\t\t\tast = fn.Exp\n\t\t\t\tenv, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:], nil})\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn, ok := f.(Func)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t\t\t}\n\t\t\t\treturn fn.Fn(el.(List).Val[1:])\n\t\t\t}\n\t\t}\n\n\t} // TCO loop\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, Func{v.(func([]MalType) (MalType, error)), nil})\n\t}\n\trepl_env.Set(Symbol{\"eval\"}, Func{func(a []MalType) (MalType, error) {\n\t\treturn EVAL(a[0], repl_env)\n\t}, nil})\n\trepl_env.Set(Symbol{\"*ARGV*\"}, List{})\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n\t// called with mal script to load and eval\n\tif len(os.Args) > 1 {\n\t\targs := make([]MalType, 0, len(os.Args)-2)\n\t\tfor _, a := range os.Args[2:] {\n\t\t\targs = append(args, a)\n\t\t}\n\t\trepl_env.Set(Symbol{\"*ARGV*\"}, List{args, nil})\n\t\tif _, e := rep(\"(load-file \\\"\" + os.Args[1] + \"\\\")\"); e != nil {\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tos.Exit(0)\n\t}\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step8_macros/step8_macros.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc starts_with(xs []MalType, sym string) bool {\n\tif 0 < len(xs) {\n\t\tswitch s := xs[0].(type) {\n\t\tcase Symbol:\n\t\t\treturn s.Val == sym\n\t\tdefault:\n\t\t}\n\t}\n\treturn false\n}\n\nfunc qq_loop(xs []MalType) MalType {\n\tacc := NewList()\n\tfor i := len(xs) - 1; 0<=i; i -= 1 {\n\t\telt := xs[i]\n\t\tswitch e := elt.(type) {\n\t\tcase List:\n\t\t\tif starts_with(e.Val, \"splice-unquote\") {\n\t\t\t\tacc = NewList(Symbol{\"concat\"}, e.Val[1], acc)\n\t\t\t\tcontinue\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t\tacc = NewList(Symbol{\"cons\"}, quasiquote(elt), acc)\n\t}\n\treturn acc\n}\n\nfunc quasiquote(ast MalType) MalType {\n\tswitch a := ast.(type) {\n\tcase Vector:\n\t\treturn NewList(Symbol{\"vec\"}, qq_loop(a.Val))\n\tcase HashMap, Symbol:\n\t\treturn NewList(Symbol{\"quote\"}, ast)\n\tcase List:\n\t\tif starts_with(a.Val,\"unquote\") {\n\t\t\treturn a.Val[1]\n\t\t} else {\n\t\t\treturn qq_loop(a.Val)\n\t\t}\n\tdefault:\n\t\treturn ast\n\t}\n}\n\nfunc is_macro_call(ast MalType, env EnvType) bool {\n\tif List_Q(ast) {\n\t\tslc, _ := GetSlice(ast)\n\t\tif len(slc) == 0 {\n\t\t\treturn false\n\t\t}\n\t\ta0 := slc[0]\n\t\tif Symbol_Q(a0) && env.Find(a0.(Symbol)) != nil {\n\t\t\tmac, e := env.Get(a0.(Symbol))\n\t\t\tif e != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif MalFunc_Q(mac) {\n\t\t\t\treturn mac.(MalFunc).GetMacro()\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc macroexpand(ast MalType, env EnvType) (MalType, error) {\n\tvar mac MalType\n\tvar e error\n\tfor is_macro_call(ast, env) {\n\t\tslc, _ := GetSlice(ast)\n\t\ta0 := slc[0]\n\t\tmac, e = env.Get(a0.(Symbol))\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tfn := mac.(MalFunc)\n\t\tast, e = Apply(fn, slc[1:])\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t}\n\treturn ast, nil\n}\n\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\tvar e error\n\tfor {\n\n\t\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\t\tswitch ast.(type) {\n\t\tcase List: // continue\n\t\tdefault:\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\n\t\t// apply list\n\t\tast, e = macroexpand(ast, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tif !List_Q(ast) {\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\t\tif len(ast.(List).Val) == 0 {\n\t\t\treturn ast, nil\n\t\t}\n\n\t\ta0 := ast.(List).Val[0]\n\t\tvar a1 MalType = nil\n\t\tvar a2 MalType = nil\n\t\tswitch len(ast.(List).Val) {\n\t\tcase 1:\n\t\t\ta1 = nil\n\t\t\ta2 = nil\n\t\tcase 2:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = nil\n\t\tdefault:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = ast.(List).Val[2]\n\t\t}\n\t\ta0sym := \"__<*fn*>__\"\n\t\tif Symbol_Q(a0) {\n\t\t\ta0sym = a0.(Symbol).Val\n\t\t}\n\t\tswitch a0sym {\n\t\tcase \"def!\":\n\t\t\tres, e := EVAL(a2, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), res), nil\n\t\tcase \"let*\":\n\t\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tarr1, e := GetSlice(a1)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t\t}\n\t\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t\t}\n\t\t\tast = a2\n\t\t\tenv = let_env\n\t\tcase \"quote\":\n\t\t\treturn a1, nil\n\t\tcase \"quasiquoteexpand\":\n\t\t\treturn quasiquote(a1), nil\n\t\tcase \"quasiquote\":\n\t\t\tast = quasiquote(a1)\n\t\tcase \"defmacro!\":\n\t\t\tfn, e := EVAL(a2, env)\n\t\t\tfn = fn.(MalFunc).SetMacro()\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), fn), nil\n\t\tcase \"macroexpand\":\n\t\t\treturn macroexpand(a1, env)\n\t\tcase \"do\":\n\t\t\tlst := ast.(List).Val\n\t\t\t_, e := eval_ast(List{lst[1 : len(lst)-1], nil}, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif len(lst) == 1 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tast = lst[len(lst)-1]\n\t\tcase \"if\":\n\t\t\tcond, e := EVAL(a1, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif cond == nil || cond == false {\n\t\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\t\tast = ast.(List).Val[3]\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tast = a2\n\t\t\t}\n\t\tcase \"fn*\":\n\t\t\tfn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}\n\t\t\treturn fn, nil\n\t\tdefault:\n\t\t\tel, e := eval_ast(ast, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tf := el.(List).Val[0]\n\t\t\tif MalFunc_Q(f) {\n\t\t\t\tfn := f.(MalFunc)\n\t\t\t\tast = fn.Exp\n\t\t\t\tenv, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:], nil})\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn, ok := f.(Func)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t\t\t}\n\t\t\t\treturn fn.Fn(el.(List).Val[1:])\n\t\t\t}\n\t\t}\n\n\t} // TCO loop\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, Func{v.(func([]MalType) (MalType, error)), nil})\n\t}\n\trepl_env.Set(Symbol{\"eval\"}, Func{func(a []MalType) (MalType, error) {\n\t\treturn EVAL(a[0], repl_env)\n\t}, nil})\n\trepl_env.Set(Symbol{\"*ARGV*\"}, List{})\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\trep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\t// called with mal script to load and eval\n\tif len(os.Args) > 1 {\n\t\targs := make([]MalType, 0, len(os.Args)-2)\n\t\tfor _, a := range os.Args[2:] {\n\t\t\targs = append(args, a)\n\t\t}\n\t\trepl_env.Set(Symbol{\"*ARGV*\"}, List{args, nil})\n\t\tif _, e := rep(\"(load-file \\\"\" + os.Args[1] + \"\\\")\"); e != nil {\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tos.Exit(0)\n\t}\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/step9_try/step9_try.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc starts_with(xs []MalType, sym string) bool {\n\tif 0 < len(xs) {\n\t\tswitch s := xs[0].(type) {\n\t\tcase Symbol:\n\t\t\treturn s.Val == sym\n\t\tdefault:\n\t\t}\n\t}\n\treturn false\n}\n\nfunc qq_loop(xs []MalType) MalType {\n\tacc := NewList()\n\tfor i := len(xs) - 1; 0<=i; i -= 1 {\n\t\telt := xs[i]\n\t\tswitch e := elt.(type) {\n\t\tcase List:\n\t\t\tif starts_with(e.Val, \"splice-unquote\") {\n\t\t\t\tacc = NewList(Symbol{\"concat\"}, e.Val[1], acc)\n\t\t\t\tcontinue\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t\tacc = NewList(Symbol{\"cons\"}, quasiquote(elt), acc)\n\t}\n\treturn acc\n}\n\nfunc quasiquote(ast MalType) MalType {\n\tswitch a := ast.(type) {\n\tcase Vector:\n\t\treturn NewList(Symbol{\"vec\"}, qq_loop(a.Val))\n\tcase HashMap, Symbol:\n\t\treturn NewList(Symbol{\"quote\"}, ast)\n\tcase List:\n\t\tif starts_with(a.Val,\"unquote\") {\n\t\t\treturn a.Val[1]\n\t\t} else {\n\t\t\treturn qq_loop(a.Val)\n\t\t}\n\tdefault:\n\t\treturn ast\n\t}\n}\n\nfunc is_macro_call(ast MalType, env EnvType) bool {\n\tif List_Q(ast) {\n\t\tslc, _ := GetSlice(ast)\n\t\tif len(slc) == 0 {\n\t\t\treturn false\n\t\t}\n\t\ta0 := slc[0]\n\t\tif Symbol_Q(a0) && env.Find(a0.(Symbol)) != nil {\n\t\t\tmac, e := env.Get(a0.(Symbol))\n\t\t\tif e != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif MalFunc_Q(mac) {\n\t\t\t\treturn mac.(MalFunc).GetMacro()\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc macroexpand(ast MalType, env EnvType) (MalType, error) {\n\tvar mac MalType\n\tvar e error\n\tfor is_macro_call(ast, env) {\n\t\tslc, _ := GetSlice(ast)\n\t\ta0 := slc[0]\n\t\tmac, e = env.Get(a0.(Symbol))\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tfn := mac.(MalFunc)\n\t\tast, e = Apply(fn, slc[1:])\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t}\n\treturn ast, nil\n}\n\nfunc eval_ast(ast MalType, env EnvType) (MalType, error) {\n\t//fmt.Printf(\"eval_ast: %#v\\n\", ast)\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if List_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(List).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn List{lst, nil}, nil\n\t} else if Vector_Q(ast) {\n\t\tlst := []MalType{}\n\t\tfor _, a := range ast.(Vector).Val {\n\t\t\texp, e := EVAL(a, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tlst = append(lst, exp)\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else {\n\t\treturn ast, nil\n\t}\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\tvar e error\n\tfor {\n\n\t\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\t\tswitch ast.(type) {\n\t\tcase List: // continue\n\t\tdefault:\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\n\t\t// apply list\n\t\tast, e = macroexpand(ast, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tif !List_Q(ast) {\n\t\t\treturn eval_ast(ast, env)\n\t\t}\n\t\tif len(ast.(List).Val) == 0 {\n\t\t\treturn ast, nil\n\t\t}\n\n\t\ta0 := ast.(List).Val[0]\n\t\tvar a1 MalType = nil\n\t\tvar a2 MalType = nil\n\t\tswitch len(ast.(List).Val) {\n\t\tcase 1:\n\t\t\ta1 = nil\n\t\t\ta2 = nil\n\t\tcase 2:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = nil\n\t\tdefault:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = ast.(List).Val[2]\n\t\t}\n\t\ta0sym := \"__<*fn*>__\"\n\t\tif Symbol_Q(a0) {\n\t\t\ta0sym = a0.(Symbol).Val\n\t\t}\n\t\tswitch a0sym {\n\t\tcase \"def!\":\n\t\t\tres, e := EVAL(a2, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), res), nil\n\t\tcase \"let*\":\n\t\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tarr1, e := GetSlice(a1)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t\t}\n\t\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t\t}\n\t\t\tast = a2\n\t\t\tenv = let_env\n\t\tcase \"quote\":\n\t\t\treturn a1, nil\n\t\tcase \"quasiquoteexpand\":\n\t\t\treturn quasiquote(a1), nil\n\t\tcase \"quasiquote\":\n\t\t\tast = quasiquote(a1)\n\t\tcase \"defmacro!\":\n\t\t\tfn, e := EVAL(a2, env)\n\t\t\tfn = fn.(MalFunc).SetMacro()\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), fn), nil\n\t\tcase \"macroexpand\":\n\t\t\treturn macroexpand(a1, env)\n\t\tcase \"try*\":\n\t\t\tvar exc MalType\n\t\t\texp, e := EVAL(a1, env)\n\t\t\tif e == nil {\n\t\t\t\treturn exp, nil\n\t\t\t} else {\n\t\t\t\tif a2 != nil && List_Q(a2) {\n\t\t\t\t\ta2s, _ := GetSlice(a2)\n\t\t\t\t\tif Symbol_Q(a2s[0]) && (a2s[0].(Symbol).Val == \"catch*\") {\n\t\t\t\t\t\tswitch e.(type) {\n\t\t\t\t\t\tcase MalError:\n\t\t\t\t\t\t\texc = e.(MalError).Obj\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\texc = e.Error()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbinds := NewList(a2s[1])\n\t\t\t\t\t\tnew_env, e := NewEnv(env, binds, NewList(exc))\n\t\t\t\t\t\tif e != nil {\n\t\t\t\t\t\t\treturn nil, e\n\t\t\t\t\t\t}\n\t\t\t\t\t\texp, e = EVAL(a2s[2], new_env)\n\t\t\t\t\t\tif e == nil {\n\t\t\t\t\t\t\treturn exp, nil\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\tcase \"do\":\n\t\t\tlst := ast.(List).Val\n\t\t\t_, e := eval_ast(List{lst[1 : len(lst)-1], nil}, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif len(lst) == 1 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tast = lst[len(lst)-1]\n\t\tcase \"if\":\n\t\t\tcond, e := EVAL(a1, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif cond == nil || cond == false {\n\t\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\t\tast = ast.(List).Val[3]\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tast = a2\n\t\t\t}\n\t\tcase \"fn*\":\n\t\t\tfn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}\n\t\t\treturn fn, nil\n\t\tdefault:\n\t\t\tel, e := eval_ast(ast, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tf := el.(List).Val[0]\n\t\t\tif MalFunc_Q(f) {\n\t\t\t\tfn := f.(MalFunc)\n\t\t\t\tast = fn.Exp\n\t\t\t\tenv, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:], nil})\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn, ok := f.(Func)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t\t\t}\n\t\t\t\treturn fn.Fn(el.(List).Val[1:])\n\t\t\t}\n\t\t}\n\n\t} // TCO loop\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, Func{v.(func([]MalType) (MalType, error)), nil})\n\t}\n\trepl_env.Set(Symbol{\"eval\"}, Func{func(a []MalType) (MalType, error) {\n\t\treturn EVAL(a[0], repl_env)\n\t}, nil})\n\trepl_env.Set(Symbol{\"*ARGV*\"}, List{})\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\trep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\t// called with mal script to load and eval\n\tif len(os.Args) > 1 {\n\t\targs := make([]MalType, 0, len(os.Args)-2)\n\t\tfor _, a := range os.Args[2:] {\n\t\t\targs = append(args, a)\n\t\t}\n\t\trepl_env.Set(Symbol{\"*ARGV*\"}, List{args, nil})\n\t\tif _, e := rep(\"(load-file \\\"\" + os.Args[1] + \"\\\")\"); e != nil {\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tos.Exit(0)\n\t}\n\n\t// repl loop\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/stepA_mal/stepA_mal.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nimport (\n\t\"mal/src/core\"\n\t. \"mal/src/env\"\n\t\"mal/src/printer\"\n\t\"mal/src/reader\"\n\t\"mal/src/readline\"\n\t. \"mal/src/types\"\n)\n\n// read\nfunc READ(str string) (MalType, error) {\n\treturn reader.Read_str(str)\n}\n\n// eval\nfunc starts_with(xs []MalType, sym string) bool {\n\tif 0 < len(xs) {\n\t\tswitch s := xs[0].(type) {\n\t\tcase Symbol:\n\t\t\treturn s.Val == sym\n\t\tdefault:\n\t\t}\n\t}\n\treturn false\n}\n\nfunc qq_loop(xs []MalType) MalType {\n\tacc := NewList()\n\tfor i := len(xs) - 1; 0<=i; i -= 1 {\n\t\telt := xs[i]\n\t\tswitch e := elt.(type) {\n\t\tcase List:\n\t\t\tif starts_with(e.Val, \"splice-unquote\") {\n\t\t\t\tacc = NewList(Symbol{\"concat\"}, e.Val[1], acc)\n\t\t\t\tcontinue\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t\tacc = NewList(Symbol{\"cons\"}, quasiquote(elt), acc)\n\t}\n\treturn acc\n}\n\nfunc quasiquote(ast MalType) MalType {\n\tswitch a := ast.(type) {\n\tcase Vector:\n\t\treturn NewList(Symbol{\"vec\"}, qq_loop(a.Val))\n\tcase HashMap, Symbol:\n\t\treturn NewList(Symbol{\"quote\"}, ast)\n\tcase List:\n\t\tif starts_with(a.Val,\"unquote\") {\n\t\t\treturn a.Val[1]\n\t\t} else {\n\t\t\treturn qq_loop(a.Val)\n\t\t}\n\tdefault:\n\t\treturn ast\n\t}\n}\n\nfunc map_eval(xs []MalType, env EnvType) ([]MalType, error) {\n\tlst := []MalType{}\n\tfor _, a := range xs {\n\t\texp, e := EVAL(a, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\tlst = append(lst, exp)\n\t}\n\treturn lst, nil\n}\n\nfunc EVAL(ast MalType, env EnvType) (MalType, error) {\n\tfor {\n\t//fmt.Printf(\"EVAL: %v\\n\", printer.Pr_str(ast, true))\n\n\tif Symbol_Q(ast) {\n\t\treturn env.Get(ast.(Symbol))\n\t} else if Vector_Q(ast) {\n\t\tlst, e := map_eval(ast.(Vector).Val, env)\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn Vector{lst, nil}, nil\n\t} else if HashMap_Q(ast) {\n\t\tm := ast.(HashMap)\n\t\tnew_hm := HashMap{map[string]MalType{}, nil}\n\t\tfor k, v := range m.Val {\n\t\t\tkv, e2 := EVAL(v, env)\n\t\t\tif e2 != nil {\n\t\t\t\treturn nil, e2\n\t\t\t}\n\t\t\tnew_hm.Val[k] = kv\n\t\t}\n\t\treturn new_hm, nil\n\t} else if !List_Q(ast) {\n\t\treturn ast, nil\n\t} else {\n\t\t// apply list\n\t\tif len(ast.(List).Val) == 0 {\n\t\t\treturn ast, nil\n\t\t}\n\n\t\ta0 := ast.(List).Val[0]\n\t\tvar a1 MalType = nil\n\t\tvar a2 MalType = nil\n\t\tswitch len(ast.(List).Val) {\n\t\tcase 1:\n\t\t\ta1 = nil\n\t\t\ta2 = nil\n\t\tcase 2:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = nil\n\t\tdefault:\n\t\t\ta1 = ast.(List).Val[1]\n\t\t\ta2 = ast.(List).Val[2]\n\t\t}\n\t\ta0sym := \"__<*fn*>__\"\n\t\tif Symbol_Q(a0) {\n\t\t\ta0sym = a0.(Symbol).Val\n\t\t}\n\t\tswitch a0sym {\n\t\tcase \"def!\":\n\t\t\tres, e := EVAL(a2, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), res), nil\n\t\tcase \"let*\":\n\t\t\tlet_env, e := NewEnv(env, nil, nil)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tarr1, e := GetSlice(a1)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tfor i := 0; i < len(arr1); i += 2 {\n\t\t\t\tif !Symbol_Q(arr1[i]) {\n\t\t\t\t\treturn nil, errors.New(\"non-symbol bind value\")\n\t\t\t\t}\n\t\t\t\texp, e := EVAL(arr1[i+1], let_env)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tlet_env.Set(arr1[i].(Symbol), exp)\n\t\t\t}\n\t\t\tast = a2\n\t\t\tenv = let_env\n\t\tcase \"quote\":\n\t\t\treturn a1, nil\n\t\tcase \"quasiquote\":\n\t\t\tast = quasiquote(a1)\n\t\tcase \"defmacro!\":\n\t\t\tfn, e := EVAL(a2, env)\n\t\t\tfn = fn.(MalFunc).SetMacro()\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\treturn env.Set(a1.(Symbol), fn), nil\n\t\tcase \"try*\":\n\t\t\tvar exc MalType\n\t\t\texp, e := EVAL(a1, env)\n\t\t\tif e == nil {\n\t\t\t\treturn exp, nil\n\t\t\t} else {\n\t\t\t\tif a2 != nil && List_Q(a2) {\n\t\t\t\t\ta2s, _ := GetSlice(a2)\n\t\t\t\t\tif Symbol_Q(a2s[0]) && (a2s[0].(Symbol).Val == \"catch*\") {\n\t\t\t\t\t\tswitch e.(type) {\n\t\t\t\t\t\tcase MalError:\n\t\t\t\t\t\t\texc = e.(MalError).Obj\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\texc = e.Error()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbinds := NewList(a2s[1])\n\t\t\t\t\t\tnew_env, e := NewEnv(env, binds, NewList(exc))\n\t\t\t\t\t\tif e != nil {\n\t\t\t\t\t\t\treturn nil, e\n\t\t\t\t\t\t}\n\t\t\t\t\t\texp, e = EVAL(a2s[2], new_env)\n\t\t\t\t\t\tif e == nil {\n\t\t\t\t\t\t\treturn exp, nil\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\tcase \"do\":\n\t\t\tlst := ast.(List).Val\n\t\t\t_, e := map_eval(lst[1 : len(lst)-1], env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif len(lst) == 1 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tast = lst[len(lst)-1]\n\t\tcase \"if\":\n\t\t\tcond, e := EVAL(a1, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif cond == nil || cond == false {\n\t\t\t\tif len(ast.(List).Val) >= 4 {\n\t\t\t\t\tast = ast.(List).Val[3]\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tast = a2\n\t\t\t}\n\t\tcase \"fn*\":\n\t\t\tfn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}\n\t\t\treturn fn, nil\n\t\tdefault:\n\t\t\tf, e := EVAL(a0, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\targs := ast.(List).Val[1:]\n\t\t\tif MalFunc_Q(f) && f.(MalFunc).GetMacro() {\n\t\t\t\tnew_ast, e := Apply(f.(MalFunc), args)\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t\tast = new_ast\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\targs, e = map_eval(args, env)\n\t\t\tif e != nil {\n\t\t\t\treturn nil, e\n\t\t\t}\n\t\t\tif MalFunc_Q(f) {\n\t\t\t\tfn := f.(MalFunc)\n\t\t\t\tast = fn.Exp\n\t\t\t\tenv, e = NewEnv(fn.Env, fn.Params, List{args, nil})\n\t\t\t\tif e != nil {\n\t\t\t\t\treturn nil, e\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfn, ok := f.(Func)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"attempt to call non-function\")\n\t\t\t\t}\n\t\t\t\treturn fn.Fn(args)\n\t\t\t}\n\t\t}\n\t}\n\t} // TCO loop\n}\n\n// print\nfunc PRINT(exp MalType) (string, error) {\n\treturn printer.Pr_str(exp, true), nil\n}\n\nvar repl_env, _ = NewEnv(nil, nil, nil)\n\n// repl\nfunc rep(str string) (MalType, error) {\n\tvar exp MalType\n\tvar res string\n\tvar e error\n\tif exp, e = READ(str); e != nil {\n\t\treturn nil, e\n\t}\n\tif exp, e = EVAL(exp, repl_env); e != nil {\n\t\treturn nil, e\n\t}\n\tif res, e = PRINT(exp); e != nil {\n\t\treturn nil, e\n\t}\n\treturn res, nil\n}\n\nfunc main() {\n\t// core.go: defined using go\n\tfor k, v := range core.NS {\n\t\trepl_env.Set(Symbol{k}, Func{v.(func([]MalType) (MalType, error)), nil})\n\t}\n\trepl_env.Set(Symbol{\"eval\"}, Func{func(a []MalType) (MalType, error) {\n\t\treturn EVAL(a[0], repl_env)\n\t}, nil})\n\trepl_env.Set(Symbol{\"*ARGV*\"}, List{})\n\n\t// core.mal: defined using the language itself\n\trep(\"(def! *host-language* \\\"go\\\")\")\n\trep(\"(def! not (fn* (a) (if a false true)))\")\n\trep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\trep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\t// called with mal script to load and eval\n\tif len(os.Args) > 1 {\n\t\targs := make([]MalType, 0, len(os.Args)-2)\n\t\tfor _, a := range os.Args[2:] {\n\t\t\targs = append(args, a)\n\t\t}\n\t\trepl_env.Set(Symbol{\"*ARGV*\"}, List{args, nil})\n\t\tif _, e := rep(\"(load-file \\\"\" + os.Args[1] + \"\\\")\"); e != nil {\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tos.Exit(0)\n\t}\n\n\t// repl loop\n\trep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n\tfor {\n\t\ttext, err := readline.Readline(\"user> \")\n\t\ttext = strings.TrimRight(text, \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar out MalType\n\t\tvar e error\n\t\tif out, e = rep(text); e != nil {\n\t\t\tif e.Error() == \"<empty line>\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfmt.Printf(\"Error: %v\\n\", e)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%v\\n\", out)\n\t}\n}\n"
  },
  {
    "path": "impls/go/src/types/types.go",
    "content": "package types\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n)\n\n// Errors/Exceptions\ntype MalError struct {\n\tObj MalType\n}\n\nfunc (e MalError) Error() string {\n\treturn fmt.Sprintf(\"%#v\", e.Obj)\n}\n\n// General types\ntype MalType interface {\n}\n\ntype EnvType interface {\n\tFind(key Symbol) EnvType\n\tSet(key Symbol, value MalType) MalType\n\tGet(key Symbol) (MalType, error)\n}\n\n// Scalars\nfunc Nil_Q(obj MalType) bool {\n\treturn obj == nil\n}\n\nfunc True_Q(obj MalType) bool {\n\tb, ok := obj.(bool)\n\treturn ok && b == true\n}\n\nfunc False_Q(obj MalType) bool {\n\tb, ok := obj.(bool)\n\treturn ok && b == false\n}\n\nfunc Number_Q(obj MalType) bool {\n\t_, ok := obj.(int)\n\treturn ok\n}\n\n// Symbols\ntype Symbol struct {\n\tVal string\n}\n\nfunc Symbol_Q(obj MalType) bool {\n\t_, ok := obj.(Symbol)\n\treturn ok\n}\n\n// Keywords\nfunc NewKeyword(s string) (MalType, error) {\n\treturn \"\\u029e\" + s, nil\n}\n\nfunc Keyword_Q(obj MalType) bool {\n\ts, ok := obj.(string)\n\treturn ok && strings.HasPrefix(s, \"\\u029e\")\n}\n\n// Strings\nfunc String_Q(obj MalType) bool {\n\t_, ok := obj.(string)\n\treturn ok\n}\n\n// Functions\ntype Func struct {\n\tFn   func([]MalType) (MalType, error)\n\tMeta MalType\n}\n\nfunc Func_Q(obj MalType) bool {\n\t_, ok := obj.(Func)\n\treturn ok\n}\n\ntype MalFunc struct {\n\tEval    func(MalType, EnvType) (MalType, error)\n\tExp     MalType\n\tEnv     EnvType\n\tParams  MalType\n\tIsMacro bool\n\tGenEnv  func(EnvType, MalType, MalType) (EnvType, error)\n\tMeta    MalType\n}\n\nfunc MalFunc_Q(obj MalType) bool {\n\t_, ok := obj.(MalFunc)\n\treturn ok\n}\n\nfunc (f MalFunc) SetMacro() MalType {\n\tf.IsMacro = true\n\treturn f\n}\n\nfunc (f MalFunc) GetMacro() bool {\n\treturn f.IsMacro\n}\n\n// Take either a MalFunc or regular function and apply it to the\n// arguments\nfunc Apply(f_mt MalType, a []MalType) (MalType, error) {\n\tswitch f := f_mt.(type) {\n\tcase MalFunc:\n\t\tenv, e := f.GenEnv(f.Env, f.Params, List{a, nil})\n\t\tif e != nil {\n\t\t\treturn nil, e\n\t\t}\n\t\treturn f.Eval(f.Exp, env)\n\tcase Func:\n\t\treturn f.Fn(a)\n\tcase func([]MalType) (MalType, error):\n\t\treturn f(a)\n\tdefault:\n\t\treturn nil, errors.New(\"Invalid function to Apply\")\n\t}\n}\n\n// Lists\ntype List struct {\n\tVal  []MalType\n\tMeta MalType\n}\n\nfunc NewList(a ...MalType) MalType {\n\treturn List{a, nil}\n}\n\nfunc List_Q(obj MalType) bool {\n\t_, ok := obj.(List)\n\treturn ok\n}\n\n// Vectors\ntype Vector struct {\n\tVal  []MalType\n\tMeta MalType\n}\n\nfunc Vector_Q(obj MalType) bool {\n\t_, ok := obj.(Vector)\n\treturn ok\n}\n\nfunc GetSlice(seq MalType) ([]MalType, error) {\n\tswitch obj := seq.(type) {\n\tcase List:\n\t\treturn obj.Val, nil\n\tcase Vector:\n\t\treturn obj.Val, nil\n\tdefault:\n\t\treturn nil, errors.New(\"GetSlice called on non-sequence\")\n\t}\n}\n\n// Hash Maps\ntype HashMap struct {\n\tVal  map[string]MalType\n\tMeta MalType\n}\n\nfunc NewHashMap(seq MalType) (MalType, error) {\n\tlst, e := GetSlice(seq)\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\tif len(lst)%2 == 1 {\n\t\treturn nil, errors.New(\"Odd number of arguments to NewHashMap\")\n\t}\n\tm := map[string]MalType{}\n\tfor i := 0; i < len(lst); i += 2 {\n\t\tstr, ok := lst[i].(string)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"expected hash-map key string\")\n\t\t}\n\t\tm[str] = lst[i+1]\n\t}\n\treturn HashMap{m, nil}, nil\n}\n\nfunc HashMap_Q(obj MalType) bool {\n\t_, ok := obj.(HashMap)\n\treturn ok\n}\n\n// Atoms\ntype Atom struct {\n\tVal  MalType\n\tMeta MalType\n}\n\nfunc (a *Atom) Set(val MalType) MalType {\n\ta.Val = val\n\treturn a\n}\n\nfunc Atom_Q(obj MalType) bool {\n\t_, ok := obj.(*Atom)\n\treturn ok\n}\n\n// General functions\n\nfunc _obj_type(obj MalType) string {\n\tif obj == nil {\n\t\treturn \"nil\"\n\t}\n\treturn reflect.TypeOf(obj).Name()\n}\n\nfunc Sequential_Q(seq MalType) bool {\n\tif seq == nil {\n\t\treturn false\n\t}\n\treturn (reflect.TypeOf(seq).Name() == \"List\") ||\n\t\t(reflect.TypeOf(seq).Name() == \"Vector\")\n}\n\nfunc Equal_Q(a MalType, b MalType) bool {\n\tota := reflect.TypeOf(a)\n\totb := reflect.TypeOf(b)\n\tif !((ota == otb) || (Sequential_Q(a) && Sequential_Q(b))) {\n\t\treturn false\n\t}\n\t//av := reflect.ValueOf(a); bv := reflect.ValueOf(b)\n\t//fmt.Printf(\"here2: %#v\\n\", reflect.TypeOf(a).Name())\n\t//switch reflect.TypeOf(a).Name() {\n\tswitch a.(type) {\n\tcase Symbol:\n\t\treturn a.(Symbol).Val == b.(Symbol).Val\n\tcase List:\n\t\tas, _ := GetSlice(a)\n\t\tbs, _ := GetSlice(b)\n\t\tif len(as) != len(bs) {\n\t\t\treturn false\n\t\t}\n\t\tfor i := 0; i < len(as); i += 1 {\n\t\t\tif !Equal_Q(as[i], bs[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\tcase Vector:\n\t\tas, _ := GetSlice(a)\n\t\tbs, _ := GetSlice(b)\n\t\tif len(as) != len(bs) {\n\t\t\treturn false\n\t\t}\n\t\tfor i := 0; i < len(as); i += 1 {\n\t\t\tif !Equal_Q(as[i], bs[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\tcase HashMap:\n\t\tam := a.(HashMap).Val\n\t\tbm := b.(HashMap).Val\n\t\tif len(am) != len(bm) {\n\t\t\treturn false\n\t\t}\n\t\tfor k, v := range am {\n\t\t\tif !Equal_Q(v, bm[k]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\tdefault:\n\t\treturn a == b\n\t}\n}\n"
  },
  {
    "path": "impls/go/tests/step2_eval.mal",
    "content": ";; Testing evaluation of excessive arguments\n(+ 1 2 3)\n;=>Error: wrong number of arguments\n\n;; Valid call\n(+ 1 2)\n;=>3\n\n\n;; Testing evaluation of missing arguments\n(+ 1)\n;=>Error: wrong number of arguments\n\n;; Testing evaluation of missing arguments\n(+)\n;=>Error: wrong number of arguments\n\n;; Testing evaluation of excessive arguments\n(- 1 2 3)\n;=>Error: wrong number of arguments\n\n;; Valid call\n(- 1 2)\n;=>-1\n\n\n;; Testing evaluation of missing arguments\n(- 1)\n;=>Error: wrong number of arguments\n\n;; Testing evaluation of missing arguments\n(-)\n;=>Error: wrong number of arguments\n\n"
  },
  {
    "path": "impls/go/tests/step4_if_fn_do.mal",
    "content": ";; Testing evaluation of excessive arguments\n(+ 1 2 3)\n;=>Error: wrong number of arguments (3 instead of 2)\n\n;; Valid call\n(+ 1 2)\n;=>3\n\n\n;; Testing evaluation of missing arguments\n(+ 1)\n;=>Error: wrong number of arguments (1 instead of 2)\n\n;; Testing evaluation of missing arguments\n(+)\n;=>Error: wrong number of arguments (0 instead of 2)\n\n;; Testing evaluation of excessive arguments\n(= 1 2 3)\n;=>Error: wrong number of arguments (3 instead of 2)\n\n;; Valid call\n(= 1 2)\n;=>false\n\n\n;; Testing evaluation of missing arguments\n(= 1)\n;=>Error: wrong number of arguments (1 instead of 2)\n\n;; Testing evaluation of missing arguments\n(=)\n;=>Error: wrong number of arguments (0 instead of 2)\n\n"
  },
  {
    "path": "impls/go/tests/step5_tco.mal",
    "content": ";; Go: skipping non-TCO recursion\n;; Reason: completes even at 100,000\n"
  },
  {
    "path": "impls/groovy/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install groovy\n"
  },
  {
    "path": "impls/groovy/GroovyWrapper.groovy",
    "content": "/* From:\n * http://groovy.jmiguel.eu/groovy.codehaus.org/WrappingGroovyScript.html\n */\n/*\n * Copyright 2002-2007 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Wrap a script and groovy jars to an executable jar\n */\ndef cli = new CliBuilder()\ncli.h( longOpt: 'help', required: false, 'show usage information' )\ncli.d( longOpt: 'destfile', argName: 'destfile', required: false, args: 1, 'jar destintation filename, defaults to {mainclass}.jar' )\ncli.m( longOpt: 'mainclass', argName: 'mainclass', required: true, args: 1, 'fully qualified main class, eg. HelloWorld' )\ncli.c( longOpt: 'groovyc', required: false, 'Run groovyc' )\n\n//--------------------------------------------------------------------------\ndef opt = cli.parse(args)\nif (!opt) { return }\nif (opt.h) {\n  cli.usage();\n  return\n}\n\ndef mainClass = opt.m\ndef scriptBase = mainClass.replace( '.', '/' )\ndef scriptFile = new File( scriptBase + '.groovy' )\nif (!scriptFile.canRead()) {\n   println \"Cannot read script file: '${scriptFile}'\"\n   return\n}\ndef destFile = scriptBase + '.jar'\nif (opt.d) {\n  destFile = opt.d\n}\n\n//--------------------------------------------------------------------------\ndef ant = new AntBuilder()\n\nif (opt.c) {\n  ant.echo( \"Compiling ${scriptFile}\" )\n  org.codehaus.groovy.tools.FileSystemCompiler.main( [ scriptFile ] as String[] )\n}\n\ndef GROOVY_HOME = new File( System.getenv('GROOVY_HOME') )\nif (!GROOVY_HOME.canRead()) {\n  ant.echo( \"Missing environment variable GROOVY_HOME: '${GROOVY_HOME}'\" )\n  return\n}\n\nant.jar( destfile: destFile, compress: true, index: true ) {\n  //fileset( dir: '.', includes: scriptBase + '*.class' )\n  fileset( dir: '.', includes: '*.class' )\n\n  zipgroupfileset( dir: GROOVY_HOME, includes: 'embeddable/groovy-all-*.jar' )\n  zipgroupfileset( dir: GROOVY_HOME, includes: 'lib/commons*.jar' )\n  // add more jars here\n\n  manifest {\n    attribute( name: 'Main-Class', value: mainClass )\n  }\n}\n\nant.echo( \"Run script using: \\'java -jar ${destFile} ...\\'\" )\n"
  },
  {
    "path": "impls/groovy/Makefile",
    "content": "CLASSES = types.class reader.class printer.class env.class core.class\n\nall: ${CLASSES}\n\ndist: mal.jar\n\nstep1_read_print.groovy: types.class reader.class printer.class\nstep2_eval.groovy: types.class reader.class printer.class\nstep3_env.groovy: types.class reader.class printer.class env.class\nstep4_if_fn_do.groovy step6_file.groovy step7_quote.groovy step8_macros.groovy step9_try.groovy stepA_mal.groovy: ${CLASSES}\n\ntypes.class: types.groovy\n\tgroovyc $<\n\nenv.class: env.groovy\n\tgroovyc $<\n\nreader.class: reader.groovy\n\tgroovyc $<\n\nprinter.class: printer.groovy\n\tgroovyc $<\n\ncore.class: core.groovy types.class reader.class printer.class\n\tgroovyc $<\n\nmal.jar: ${CLASSES}\n\tgroovyc stepA_mal.groovy\n\tGROOVY_HOME=/usr/share/groovy groovy GroovyWrapper -d $@ -m stepA_mal\n\nSHELL := bash\nmal: mal.jar\n\tcat <(echo -e '#!/bin/sh\\nexec java -jar \"$$0\" \"$$@\"') mal.jar > $@\n\tchmod +x mal\n\nclean:\n\trm -f *.class classes/* mal.jar mal\n\trmdir classes || true\n"
  },
  {
    "path": "impls/groovy/core.groovy",
    "content": "import types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport reader\nimport printer\n\nclass core {\n    def static do_pr_str(args) {\n        return printer._pr_list(args, \" \", true)\n    }\n    def static do_str(args) {\n        return printer._pr_list(args, \"\", false)\n    }\n    def static do_prn(args) {\n        println(printer._pr_list(args, \" \", true))\n    }\n    def static do_println(args) {\n        println(printer._pr_list(args, \" \", false))\n    }\n\n    def static do_concat(args) {\n        args.inject([], { a, b -> a + (b as List) })\n    }\n    def static do_nth(args) {\n        if (args[0].size() <= args[1]) {\n            throw new MalException(\"nth: index out of range\")\n        }\n        args[0][args[1]]\n    }\n    def static do_apply(args) {\n        def start_args = args.drop(1).take(args.size()-2) as List\n        args[0](start_args + (args.last() as List))\n    }\n\n    def static do_swap_BANG(args) {\n        def (atm,f) = [args[0], args[1]]\n        atm.value = f([atm.value] + (args.drop(2) as List))\n    }\n\n    def static do_conj(args) {\n        if (types.list_Q(args[0])) {\n            args.drop(1).inject(args[0], { a, b -> [b] + a })\n        } else {\n            types.vector(args.drop(1).inject(args[0], { a, b -> a + [b] }))\n        }\n    }\n    def static do_seq(args) {\n        def obj = args[0]\n        switch (obj) {\n            case { types.list_Q(obj) }:\n                return obj.size() == 0 ? null : obj\n            case { types.vector_Q(obj) }:\n                return obj.size() == 0 ? null : obj.clone()\n            case { types.string_Q(obj) }:\n                return obj.size() == 0 ? null : obj.collect{ it.toString() }\n            case null:\n                return null\n            default:\n                throw new MalException(\"seq: called on non-sequence\")\n        }\n    }\n\n    static ns = [\n        \"=\": { a -> a[0]==a[1]},\n        \"throw\": { a -> throw new MalException(a[0]) },\n\n        \"nil?\": { a -> a[0] == null },\n        \"true?\": { a -> a[0] == true },\n        \"false?\": { a -> a[0] == false },\n        \"string?\": { a -> types.string_Q(a[0]) },\n        \"symbol\": { a -> new MalSymbol(a[0]) },\n        \"symbol?\": { a -> a[0] instanceof MalSymbol },\n        \"keyword\": { a -> types.keyword(a[0]) },\n        \"keyword?\": { a -> types.keyword_Q(a[0]) },\n        \"number?\": { a -> a[0] instanceof Integer },\n        \"fn?\": { a -> (a[0] instanceof MalFunc && !a[0].ismacro) ||\n                      a[0] instanceof Closure },\n        \"macro?\": { a -> a[0] instanceof MalFunc && a[0].ismacro },\n\n        \"pr-str\": core.&do_pr_str,\n        \"str\": core.&do_str,\n        \"prn\": core.&do_prn,\n        \"println\": core.&do_println,\n        \"read-string\": reader.&read_str,\n        \"readline\": { a -> System.console().readLine(a[0]) },\n        \"slurp\": { a -> new File(a[0]).text },\n\n        \"<\":  { a -> a[0]<a[1]},\n        \"<=\": { a -> a[0]<=a[1]},\n        \">\":  { a -> a[0]>a[1]},\n        \">=\": { a -> a[0]>=a[1]},\n        \"+\":  { a -> a[0]+a[1]},\n        \"-\":  { a -> a[0]-a[1]},\n        \"*\":  { a -> a[0]*a[1]},\n        \"/\":  { a -> a[0]/a[1]},  // /\n        \"time-ms\": { a -> System.currentTimeMillis() },\n\n        \"list\": { a -> a},\n        \"list?\": { a -> types.list_Q(a[0]) },\n        \"vector\": { a -> types.vector(a) },\n        \"vector?\": { a -> types.vector_Q(a[0]) },\n        \"hash-map\": { a -> types.hash_map(a) },\n        \"map?\": { a -> types.hash_map_Q(a[0]) },\n        \"assoc\": { a -> types.assoc_BANG(types.copy(a[0]), a.drop(1)) },\n        \"dissoc\": { a -> types.dissoc_BANG(types.copy(a[0]), a.drop(1)) },\n        \"get\": { a ->  a[0] == null ? null : a[0][a[1]] },\n        \"contains?\": { a -> a[0].containsKey(a[1]) },\n        \"keys\": { a -> a[0].keySet() as List },\n        \"vals\": { a -> a[0].values() as List },\n\n        \"sequential?\": { a -> types.&sequential_Q(a[0]) },\n        \"cons\": { a -> [a[0]] + (a[1] as List) },\n        \"concat\": core.&do_concat,\n        \"vec\": { a -> types.vector_Q(a[0]) ? a[0] : types.vector(a[0]) },\n        \"nth\": core.&do_nth,\n        \"first\": { a -> a[0] == null || a[0].size() == 0 ? null : a[0][0] },\n        \"rest\": { a -> a[0] == null ? [] as List : a[0].drop(1) },\n        \"empty?\": { a -> a[0] == null || a[0].size() == 0 },\n        \"count\": { a -> a[0] == null ? 0 : a[0].size() },\n        \"apply\": core.&do_apply,\n        \"map\": { a -> a[1].collect { x -> a[0].call([x]) } },\n\n        \"conj\": core.&do_conj,\n        \"seq\": core.&do_seq,\n\n        \"meta\": { a -> a[0].hasProperty(\"meta\") ? a[0].getProperties().meta : null },\n        \"with-meta\": { a -> def b = types.copy(a[0]); b.getMetaClass().meta = a[1]; b },\n        \"atom\": { a -> new types.MalAtom(a[0]) },\n        \"atom?\": { a -> a[0] instanceof types.MalAtom },\n        \"deref\": { a -> a[0].value },\n        \"reset!\": { a -> a[0].value = a[1] },\n        \"swap!\": core.&do_swap_BANG\n    ]\n}\n\n"
  },
  {
    "path": "impls/groovy/env.groovy",
    "content": "import types.MalException\nimport types.MalSymbol\n\nclass env {\n    static class Env {\n        def data\n        def outer\n\n        Env() {\n            outer = null\n            data = [:]\n        }\n        Env(Env outer_env) {\n            outer = outer_env\n            data = [:]\n        }\n        Env(Env outer_env, binds, exprs) {\n            outer = outer_env\n            data = [:]\n            for (int i=0; i<binds.size; i++) {\n                if (binds[i].value == \"&\") {\n                    data[binds[i+1].value] = (exprs.size() > i) ? exprs[i..-1] : []\n                    break\n                } else {\n                    data[binds[i].value] = exprs[i]\n                }\n            }\n        }\n\n        def set(MalSymbol key, def val) {\n            data[key.value] = val\n        }\n\n        def find(String key) {\n            if (data.containsKey(key)) {\n                this\n            } else if (outer != null) {\n                outer.find(key)\n            } else {\n                null\n            }\n        }\n\n        def get(String key) {\n            def e = find(key)\n            if (e == null) {\n                throw new MalException(\"'${key}' not found\")\n            } else {\n                e.data.get(key)\n            }\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "impls/groovy/printer.groovy",
    "content": "import groovy.json.StringEscapeUtils\nimport types\nimport types.MalSymbol\nimport types.MalAtom\n\n\nclass printer {\n    def static _pr_list(lst, sep, Boolean print_readably) {\n        return lst.collect{ e -> pr_str(e, print_readably) }.join(sep)\n    }\n\n    def static pr_str(exp, Boolean print_readably) {\n        def _r = print_readably\n        switch (exp) {\n            case { types.list_Q(exp) }:\n                def lst = exp.collect { pr_str(it, _r) }\n                return \"(${lst.join(\" \")})\"\n            case { types.vector_Q(exp) }:\n                def lst = exp.collect { pr_str(it, _r) }\n                return \"[${lst.join(\" \")}]\"\n            case Map:\n                def lst = []\n                exp.each { k,v -> lst.add(pr_str(k,_r)); lst.add(pr_str(v,_r)) }\n                return \"{${lst.join(\" \")}}\"\n            case String:\n                if (types.keyword_Q(exp)) {\n                    return \":\" + exp.drop(1)\n                } else if (print_readably) {\n                    return \"\\\"${StringEscapeUtils.escapeJava(exp)}\\\"\"\n                } else {\n                    return exp\n                }\n            case null:\n                return 'nil'\n            case MalSymbol:\n                return exp.value\n            case MalAtom:\n                return \"(atom ${exp.value})\"\n            default:\n                return exp.toString()\n        }\n    }\n}\n\n"
  },
  {
    "path": "impls/groovy/reader.groovy",
    "content": "import groovy.json.StringEscapeUtils\nimport types\nimport types.MalException\nimport types.MalSymbol\n\nclass reader {\n    static class Reader {\n        def tokens\n        def position\n        Reader(def toks) {\n            tokens = toks\n            position = 0\n        }\n\n        def peek() {\n            if (position >= tokens.size) {\n                null\n            } else {\n                tokens[position]\n            }\n        }\n        def next() {\n            if (position >= tokens.size) {\n                null\n            } else {\n                tokens[position++]\n            }\n        }\n    }\n\n    def static tokenizer(String str) {\n        def m = str =~ /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/\n        def tokens = []\n        while (m.find()) {\n            String token = m.group(1)\n            if (token != null &&\n                !(token == \"\") &&\n                !(token[0] == ';')) {\n                tokens.add(token)\n            }\n        }\n        return tokens\n    }\n\n    def static read_atom(Reader rdr) {\n        def token = rdr.next()\n        def m = token =~ /(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"((?:\\\\.|[^\\\\\"])*)\"$|^\"(.*)$|:(.*)|(^[^\"]*$)/\n        if (!m.find()) {\n            throw new MalException(\"unrecognized token '$token'\")\n        }\n        if (m.group(1) != null) {\n            Integer.parseInt(m.group(1))\n        } else if (m.group(3) != null) {\n            null\n        } else if (m.group(4) != null) {\n            true\n        } else if (m.group(5) != null) {\n            false\n        } else if (m.group(6) != null) {\n            if (token[token.length() - 1] != '\"') {\n                throw new MalException(\"expected '\\\"', got EOF\")\n            }\n            StringEscapeUtils.unescapeJava(m.group(6))\n        } else if (m.group(7) != null) {\n            throw new MalException(\"expected '\\\"', got EOF\")\n        } else if (m.group(8) != null) {\n            \"\\u029e\" + m.group(8)\n        } else if (m.group(9) != null) {\n            new MalSymbol(m.group(9))\n        } else {\n            throw new MalException(\"unrecognized '${m.group(0)}'\")\n        }\n    }\n\n    def static read_list(Reader rdr, char start, char end) {\n        def token = rdr.next()\n        def lst = []\n        if (token.charAt(0) != start) {\n            throw new MalException(\"expected '${start}'\")\n        }\n\n        while ((token = rdr.peek()) != null && token.charAt(0) != end) {\n            lst.add(read_form(rdr))\n        }\n\n        if (token == null) {\n            throw new MalException(\"expected '${end}', got EOF\")\n        }\n        rdr.next()\n\n        return lst\n    }\n\n    def static read_vector(Reader rdr) {\n        def lst = read_list(rdr, '[' as char, ']' as char)\n        return types.vector(lst)\n    }\n\n    def static read_hash_map(Reader rdr) {\n        def lst = read_list(rdr, '{' as char, '}' as char)\n        return types.hash_map(lst)\n    }\n\n    def static read_form(Reader rdr) {\n        def token = rdr.peek()\n        switch (token) {\n            // reader macros/transforms\n            case \"'\":\n                rdr.next()\n                return [new MalSymbol(\"quote\"), read_form(rdr)]\n            case '`':\n                rdr.next()\n                return [new MalSymbol(\"quasiquote\"), read_form(rdr)]\n            case '~':\n                rdr.next()\n                return [new MalSymbol(\"unquote\"), read_form(rdr)]\n            case '~@':\n                rdr.next()\n                return [new MalSymbol(\"splice-unquote\"), read_form(rdr)]\n            case '^':\n                rdr.next()\n                def meta = read_form(rdr);\n                return [new MalSymbol(\"with-meta\"), read_form(rdr), meta]\n            case '@':\n                rdr.next()\n                return [new MalSymbol(\"deref\"), read_form(rdr)]\n\n            // list\n            case ')': throw new MalException(\"unexpected ')'\")\n            case '(': return read_list(rdr, '(' as char, ')' as char)\n\n            // vector\n            case ']': throw new MalException(\"unexpected ']'\")\n            case '[': return read_vector(rdr)\n\n            // hash-map\n            case '}': throw new MalException(\"unexpected '}'\")\n            case '{': return read_hash_map(rdr)\n\n            // atom\n            default: return read_atom(rdr)\n        }\n    }\n    \n    def static read_str(String str) {\n        def tokens = tokenizer(str)\n        if (tokens.size() == 0) {\n            return null;\n        }\n        //println \"tokens ${tokens}\"\n        def rdr = new Reader(tokens)\n        read_form(rdr)\n    }\n}\n\n"
  },
  {
    "path": "impls/groovy/run",
    "content": "#!/usr/bin/env bash\nexec groovy $(dirname $0)/${STEP:-stepA_mal}.groovy \"${@}\"\n"
  },
  {
    "path": "impls/groovy/step0_repl.groovy",
    "content": "// READ\nREAD = { str ->\n    str\n}\n\n// EVAL\nEVAL = { ast, env ->\n    ast\n}\n\n// PRINT\nPRINT = { exp ->\n    exp\n}\n\n// REPL\nREP = { str ->\n    PRINT(EVAL(READ(str), [:]))\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break\n    }\n    try {\n        println REP(line)\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step1_read_print.groovy",
    "content": "import reader\nimport printer\nimport types.MalException\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nEVAL = { ast, env ->\n    ast\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nREP = { str ->\n    PRINT(EVAL(READ(str), [:]))\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step2_eval.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nEVAL = { ast, env ->\n    // println(\"EVAL: ${printer.pr_str(ast,true)}\")\n\n    switch (ast) {\n        case MalSymbol:\n            if (env.containsKey(ast.value)) return env.get(ast.value)\n            throw new MalException(\"'${ast.value}' not found\")\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:\n            def new_hm = [:]\n            ast.each { k,v ->\n                new_hm[k] = EVAL(v, env)\n            }\n            return new_hm\n        default:\n            return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    def el = ast.collect { EVAL(it, env) }\n    def (f, args) = [el[0], el[1..-1]]\n    f(args)\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = [\n    \"+\": { a -> a[0]+a[1]},\n    \"-\": { a -> a[0]-a[1]},\n    \"*\": { a -> a[0]*a[1]},\n    \"/\": { a -> a[0]/a[1]}]  // /\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step3_env.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport env.Env\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nEVAL = { ast, env ->\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        return EVAL(ast[2], let_env)\n    default:\n        def el = ast.collect { EVAL(it, env) }\n        def (f, args) = [el[0], el[1..-1]]\n        f(args)\n    }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nrepl_env.set(new MalSymbol(\"+\"), { a -> a[0]+a[1]});\nrepl_env.set(new MalSymbol(\"-\"), { a -> a[0]-a[1]});\nrepl_env.set(new MalSymbol(\"*\"), { a -> a[0]*a[1]});\nrepl_env.set(new MalSymbol(\"/\"), { a -> a[0]/a[1]});  // /\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step4_if_fn_do.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nEVAL = { ast, env ->\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        return EVAL(ast[2], let_env)\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        return (ast[1..-1].collect { EVAL(it, env) })[-1]\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                return EVAL(ast[3], env)\n            } else {\n                return null\n            }\n        } else {\n            return EVAL(ast[2], env)\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def el = ast.collect { EVAL(it, env) }\n        def (f, args) = [el[0], el.size() > 1 ? el[1..-1] : []]\n        f(args)\n    }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\n\n// core.mal: defined using mal itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\n\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step5_tco.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nEVAL = { ast, env ->\n  while (true) {\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        env = let_env\n        ast = ast[2]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        ast.size() > 2 ? ast[1..-2].collect { EVAL(it, env) } : null\n        ast = ast[-1]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                ast = ast[3]\n                break // TCO\n            } else {\n                return null\n            }\n        } else {\n            ast = ast[2]\n            break // TCO\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def el = ast.collect { EVAL(it, env) }\n        def (f, args) = [el[0], el.size() > 1 ? el[1..-1] : []]\n        if (f instanceof MalFunc) {\n            env = new Env(f.env, f.params, args)\n            ast = f.ast\n            break // TCO\n        } else {\n            return f(args)\n        }\n    }\n  }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\n\n// core.mal: defined using mal itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\n\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(StackOverflowError ex) {\n        println \"Error: ${ex}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step6_file.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nEVAL = { ast, env ->\n  while (true) {\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        env = let_env\n        ast = ast[2]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        ast.size() > 2 ? ast[1..-2].collect { EVAL(it, env) } : null\n        ast = ast[-1]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                ast = ast[3]\n                break // TCO\n            } else {\n                return null\n            }\n        } else {\n            ast = ast[2]\n            break // TCO\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def el = ast.collect { EVAL(it, env) }\n        def (f, args) = [el[0], el.drop(1)]\n        if (f instanceof MalFunc) {\n            env = new Env(f.env, f.params, args)\n            ast = f.ast\n            break // TCO\n        } else {\n            return f(args)\n        }\n    }\n  }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\nrepl_env.set(new MalSymbol(\"eval\"), { a -> EVAL(a[0], repl_env)})\nrepl_env.set(new MalSymbol(\"*ARGV*\"), this.args as List)\n\n// core.mal: defined using mal itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif (this.args.size() > 0) {\n    repl_env.set(new MalSymbol(\"*ARGV*\"), this.args.drop(1) as List)\n    REP(\"(load-file \\\"${this.args[0]}\\\")\")\n    System.exit(0)\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(StackOverflowError ex) {\n        println \"Error: ${ex}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step7_quote.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nstarts_with = { lst, sym ->\n    lst.size() == 2 && lst[0].class == MalSymbol && lst[0].value == sym\n}\nqq_loop = { elt, acc ->\n    if (types.list_Q(elt) && starts_with(elt, \"splice-unquote\")) {\n        return [new MalSymbol(\"concat\"), elt[1], acc]\n    } else {\n        return [new MalSymbol(\"cons\"), quasiquote(elt), acc]\n    }\n}\nqq_foldr = { xs ->\n    def acc = []\n    for (int i=xs.size()-1; 0<=i; i-=1) {\n        acc = qq_loop(xs[i], acc)\n    }\n    return acc\n}\nquasiquote = { ast ->\n    switch (ast) {\n    case List:\n        if (types.vector_Q(ast)) {\n            return [new MalSymbol(\"vec\"), qq_foldr(ast)]\n        } else if (starts_with(ast, \"unquote\")) {\n            return ast[1]\n        } else {\n            return qq_foldr(ast)\n        }\n    case MalSymbol: return [new MalSymbol(\"quote\"), ast]\n    case Map:       return [new MalSymbol(\"quote\"), ast]\n    default:        return ast\n    }\n}\n\nEVAL = { ast, env ->\n  while (true) {\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        env = let_env\n        ast = ast[2]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"quote\" }:\n        return ast[1]\n    case { it instanceof MalSymbol && it.value == \"quasiquote\" }:\n        ast = quasiquote(ast[1])\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        ast.size() > 2 ? ast[1..-2].collect { EVAL(it, env) } : null\n        ast = ast[-1]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                ast = ast[3]\n                break // TCO\n            } else {\n                return null\n            }\n        } else {\n            ast = ast[2]\n            break // TCO\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def el = ast.collect { EVAL(it, env) }\n        def (f, args) = [el[0], el.drop(1)]\n        if (f instanceof MalFunc) {\n            env = new Env(f.env, f.params, args)\n            ast = f.ast\n            break // TCO\n        } else {\n            return f(args)\n        }\n    }\n  }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\nrepl_env.set(new MalSymbol(\"eval\"), { a -> EVAL(a[0], repl_env)})\nrepl_env.set(new MalSymbol(\"*ARGV*\"), this.args as List)\n\n// core.mal: defined using mal itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif (this.args.size() > 0) {\n    repl_env.set(new MalSymbol(\"*ARGV*\"), this.args.drop(1) as List)\n    REP(\"(load-file \\\"${this.args[0]}\\\")\")\n    System.exit(0)\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(StackOverflowError ex) {\n        println \"Error: ${ex}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step8_macros.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nstarts_with = { lst, sym ->\n    lst.size() == 2 && lst[0].class == MalSymbol && lst[0].value == sym\n}\nqq_loop = { elt, acc ->\n    if (types.list_Q(elt) && starts_with(elt, \"splice-unquote\")) {\n        return [new MalSymbol(\"concat\"), elt[1], acc]\n    } else {\n        return [new MalSymbol(\"cons\"), quasiquote(elt), acc]\n    }\n}\nqq_foldr = { xs ->\n    def acc = []\n    for (int i=xs.size()-1; 0<=i; i-=1) {\n        acc = qq_loop(xs[i], acc)\n    }\n    return acc\n}\nquasiquote = { ast ->\n    switch (ast) {\n    case List:\n        if (types.vector_Q(ast)) {\n            return [new MalSymbol(\"vec\"), qq_foldr(ast)]\n        } else if (starts_with(ast, \"unquote\")) {\n            return ast[1]\n        } else {\n            return qq_foldr(ast)\n        }\n    case MalSymbol: return [new MalSymbol(\"quote\"), ast]\n    case Map:       return [new MalSymbol(\"quote\"), ast]\n    default:        return ast\n    }\n}\n\nEVAL = { ast, env ->\n  while (true) {\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        env = let_env\n        ast = ast[2]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"quote\" }:\n        return ast[1]\n    case { it instanceof MalSymbol && it.value == \"quasiquote\" }:\n        ast = quasiquote(ast[1])\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"defmacro!\" }:\n        def f = EVAL(ast[2], env)\n        f = f.clone()\n        f.ismacro = true\n        return env.set(ast[1], f)\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        ast.size() > 2 ? ast[1..-2].collect { EVAL(it, env) } : null\n        ast = ast[-1]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                ast = ast[3]\n                break // TCO\n            } else {\n                return null\n            }\n        } else {\n            ast = ast[2]\n            break // TCO\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def f = EVAL(ast[0], env)\n        def args = ast.drop(1)\n        if (f instanceof MalFunc && f.ismacro) {\n            ast = f(args)\n            break // TCO\n        }\n        args = args.collect { EVAL(it, env) }\n        if (f instanceof MalFunc) {\n            env = new Env(f.env, f.params, args)\n            ast = f.ast\n            break // TCO\n        } else {\n            return f(args)\n        }\n    }\n  }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\nrepl_env.set(new MalSymbol(\"eval\"), { a -> EVAL(a[0], repl_env)})\nrepl_env.set(new MalSymbol(\"*ARGV*\"), this.args as List)\n\n// core.mal: defined using mal itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nREP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n\nif (this.args.size() > 0) {\n    repl_env.set(new MalSymbol(\"*ARGV*\"), this.args.drop(1) as List)\n    REP(\"(load-file \\\"${this.args[0]}\\\")\")\n    System.exit(0)\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(StackOverflowError ex) {\n        println \"Error: ${ex}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/step9_try.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nstarts_with = { lst, sym ->\n    lst.size() == 2 && lst[0].class == MalSymbol && lst[0].value == sym\n}\nqq_loop = { elt, acc ->\n    if (types.list_Q(elt) && starts_with(elt, \"splice-unquote\")) {\n        return [new MalSymbol(\"concat\"), elt[1], acc]\n    } else {\n        return [new MalSymbol(\"cons\"), quasiquote(elt), acc]\n    }\n}\nqq_foldr = { xs ->\n    def acc = []\n    for (int i=xs.size()-1; 0<=i; i-=1) {\n        acc = qq_loop(xs[i], acc)\n    }\n    return acc\n}\nquasiquote = { ast ->\n    switch (ast) {\n    case List:\n        if (types.vector_Q(ast)) {\n            return [new MalSymbol(\"vec\"), qq_foldr(ast)]\n        } else if (starts_with(ast, \"unquote\")) {\n            return ast[1]\n        } else {\n            return qq_foldr(ast)\n        }\n    case MalSymbol: return [new MalSymbol(\"quote\"), ast]\n    case Map:       return [new MalSymbol(\"quote\"), ast]\n    default:        return ast\n    }\n}\n\nEVAL = { ast, env ->\n  while (true) {\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        env = let_env\n        ast = ast[2]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"quote\" }:\n        return ast[1]\n    case { it instanceof MalSymbol && it.value == \"quasiquote\" }:\n        ast = quasiquote(ast[1])\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"defmacro!\" }:\n        def f = EVAL(ast[2], env)\n        f = f.clone()\n        f.ismacro = true\n        return env.set(ast[1], f)\n    case { it instanceof MalSymbol && it.value == \"try*\" }:\n        try {\n            return EVAL(ast[1], env)\n        } catch(exc) {\n            if (ast.size() > 2 && \n                    ast[2][0] instanceof MalSymbol &&\n                    ast[2][0].value == \"catch*\") {\n                def e = null\n                if (exc instanceof MalException) {\n                    e = exc.obj\n                } else {\n                    e = exc.message\n                }\n                return EVAL(ast[2][2], new Env(env, [ast[2][1]], [e]))\n            } else {\n                throw exc\n            }\n        }\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        ast.size() > 2 ? ast[1..-2].collect { EVAL(it, env) } : null\n        ast = ast[-1]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                ast = ast[3]\n                break // TCO\n            } else {\n                return null\n            }\n        } else {\n            ast = ast[2]\n            break // TCO\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def f = EVAL(ast[0], env)\n        def args = ast.drop(1)\n        if (f instanceof MalFunc && f.ismacro) {\n            ast = f(args)\n            break // TCO\n        }\n        args = args.collect { EVAL(it, env) }\n        if (f instanceof MalFunc) {\n            env = new Env(f.env, f.params, args)\n            ast = f.ast\n            break // TCO\n        } else {\n            return f(args)\n        }\n    }\n  }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\nrepl_env.set(new MalSymbol(\"eval\"), { a -> EVAL(a[0], repl_env)})\nrepl_env.set(new MalSymbol(\"*ARGV*\"), this.args as List)\n\n// core.mal: defined using mal itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nREP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n\nif (this.args.size() > 0) {\n    repl_env.set(new MalSymbol(\"*ARGV*\"), this.args.drop(1) as List)\n    REP(\"(load-file \\\"${this.args[0]}\\\")\")\n    System.exit(0)\n}\n\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(StackOverflowError ex) {\n        println \"Error: ${ex}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/stepA_mal.groovy",
    "content": "import reader\nimport printer\nimport types\nimport types.MalException\nimport types.MalSymbol\nimport types.MalFunc\nimport env.Env\nimport core\n\n// READ\nREAD = { str ->\n    reader.read_str str\n}\n\n// EVAL\nstarts_with = { lst, sym ->\n    lst.size() == 2 && lst[0].class == MalSymbol && lst[0].value == sym\n}\nqq_loop = { elt, acc ->\n    if (types.list_Q(elt) && starts_with(elt, \"splice-unquote\")) {\n        return [new MalSymbol(\"concat\"), elt[1], acc]\n    } else {\n        return [new MalSymbol(\"cons\"), quasiquote(elt), acc]\n    }\n}\nqq_foldr = { xs ->\n    def acc = []\n    for (int i=xs.size()-1; 0<=i; i-=1) {\n        acc = qq_loop(xs[i], acc)\n    }\n    return acc\n}\nquasiquote = { ast ->\n    switch (ast) {\n    case List:\n        if (types.vector_Q(ast)) {\n            return [new MalSymbol(\"vec\"), qq_foldr(ast)]\n        } else if (starts_with(ast, \"unquote\")) {\n            return ast[1]\n        } else {\n            return qq_foldr(ast)\n        }\n    case MalSymbol: return [new MalSymbol(\"quote\"), ast]\n    case Map:       return [new MalSymbol(\"quote\"), ast]\n    default:        return ast\n    }\n}\n\nEVAL = { ast, env ->\n  while (true) {\n    def dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv != null) {\n      def dbgeval = env.get(\"DEBUG-EVAL\");\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: ${printer.pr_str(ast,true)}\")\n      }\n   }\n\n    switch (ast) {\n        case MalSymbol: return env.get(ast.value);\n        case List:      if (types.vector_Q(ast)) {\n                            return types.vector(ast.collect { EVAL(it, env) })\n                        }\n                        break;\n        case Map:       def new_hm = [:]\n                        ast.each { k,v ->\n                            new_hm[k] = EVAL(v, env)\n                        }\n                        return new_hm\n        default:        return ast\n    }\n\n    if (ast.size() == 0) return ast\n\n    switch (ast[0]) {\n    case { it instanceof MalSymbol && it.value == \"def!\" }:\n        return env.set(ast[1], EVAL(ast[2], env))\n    case { it instanceof MalSymbol && it.value == \"let*\" }:\n        def let_env = new Env(env)\n        for (int i=0; i < ast[1].size(); i += 2) {\n            let_env.set(ast[1][i], EVAL(ast[1][i+1], let_env))\n        }\n        env = let_env\n        ast = ast[2]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"quote\" }:\n        return ast[1]\n    case { it instanceof MalSymbol && it.value == \"quasiquote\" }:\n        ast = quasiquote(ast[1])\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"defmacro!\" }:\n        def f = EVAL(ast[2], env)\n        f = f.clone()\n        f.ismacro = true\n        return env.set(ast[1], f)\n    case { it instanceof MalSymbol && it.value == \"try*\" }:\n        try {\n            return EVAL(ast[1], env)\n        } catch(exc) {\n            if (ast.size() > 2 && \n                    ast[2][0] instanceof MalSymbol &&\n                    ast[2][0].value == \"catch*\") {\n                def e = null\n                if (exc instanceof MalException) {\n                    e = exc.obj\n                } else {\n                    e = exc.message\n                }\n                return EVAL(ast[2][2], new Env(env, [ast[2][1]], [e]))\n            } else {\n                throw exc\n            }\n        }\n    case { it instanceof MalSymbol && it.value == \"do\" }:\n        ast.size() > 2 ? ast[1..-2].collect { EVAL(it, env) } : null\n        ast = ast[-1]\n        break // TCO\n    case { it instanceof MalSymbol && it.value == \"if\" }:\n        def cond = EVAL(ast[1], env)\n        if (cond == false || cond == null) {\n            if (ast.size > 3) {\n                ast = ast[3]\n                break // TCO\n            } else {\n                return null\n            }\n        } else {\n            ast = ast[2]\n            break // TCO\n        }\n    case { it instanceof MalSymbol && it.value == \"fn*\" }:\n        return new MalFunc(EVAL, ast[2], env, ast[1])\n    default:\n        def f = EVAL(ast[0], env)\n        def args = ast.drop(1)\n        if (f instanceof MalFunc && f.ismacro) {\n            ast = f(args)\n            break // TCO\n        }\n        args = args.collect { EVAL(it, env) }\n        if (f instanceof MalFunc) {\n            env = new Env(f.env, f.params, args)\n            ast = f.ast\n            break // TCO\n        } else {\n            return f(args)\n        }\n    }\n  }\n}\n\n// PRINT\nPRINT = { exp ->\n    printer.pr_str exp, true\n}\n\n// REPL\nrepl_env = new Env();\nREP = { str ->\n    PRINT(EVAL(READ(str), repl_env))\n}\n\n// core.EXT: defined using Groovy\ncore.ns.each { k,v ->\n    repl_env.set(new MalSymbol(k), v)\n}\nrepl_env.set(new MalSymbol(\"eval\"), { a -> EVAL(a[0], repl_env)})\nrepl_env.set(new MalSymbol(\"*ARGV*\"), this.args as List)\n\n// core.mal: defined using mal itself\nREP(\"(def! *host-language* \\\"groovy\\\")\")\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nREP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif (this.args.size() > 0) {\n    repl_env.set(new MalSymbol(\"*ARGV*\"), this.args.drop(1) as List)\n    REP(\"(load-file \\\"${this.args[0]}\\\")\")\n    System.exit(0)\n}\n\nREP(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nwhile (true) {\n    line = System.console().readLine 'user> '\n    if (line == null) {\n        break;\n    }\n    try {\n        println REP(line)\n    } catch(MalException ex) {\n        println \"Error: ${printer.pr_str(ex.obj, true)}\"\n    } catch(StackOverflowError ex) {\n        println \"Error: ${ex}\"\n    } catch(ex) {\n        println \"Error: $ex\"\n        ex.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "impls/groovy/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/groovy/types.groovy",
    "content": "import groovy.transform.InheritConstructors\nimport groovy.transform.AutoClone\n\nclass types {\n    def static copy(obj) {\n        def new_obj = obj\n        if (obj instanceof Collection || obj instanceof Map) {\n            new_obj = obj.clone()\n            if (obj.hasProperty(\"meta\")) {\n                new_obj.getMetaClass().meta = obj.getProperties().meta\n            }\n            if (obj.hasProperty(\"isvector\")) {\n                new_obj.getMetaClass().isvector = obj.getProperties().isvector\n            }\n        } else if (obj instanceof Object) {\n            new_obj = obj.clone()\n        }\n        return new_obj\n    }\n\n    @InheritConstructors\n    static class MalException extends Exception {\n        def obj\n        MalException(String message) {\n            super(message)\n            obj = message\n        }\n        MalException(_obj) {\n            super(\"mal exception containing object\")\n            obj = _obj\n        }\n    }\n\n    def static string_Q(o) {\n        return o instanceof String && (o.size() == 0 || o[0] != \"\\u029e\")\n    }\n\n    @AutoClone\n    static class MalSymbol implements Comparable {\n        String value\n        MalSymbol(String name) {\n            value = name\n        }\n        int compareTo(o) { value <=> o.value }\n    }\n\n    def static keyword(o) {\n        types.&keyword_Q(o) ? o : (\"\\u029e\" + o)\n    }\n    def static keyword_Q(o) {\n        return o instanceof String && o.size() > 0 && o[0] == \"\\u029e\"\n    }\n\n    def static list_Q(o) {\n        //return (o instanceof List || o instanceof Object[]) &&\n        return o instanceof List && !o.hasProperty(\"isvector\")\n    }\n\n    def static vector(o) {\n        def v = o.collect()\n        v.metaClass.isvector = true\n        v\n    }\n    def static vector_Q(o) {\n        return o instanceof List && o.hasProperty(\"isvector\") && o.isvector\n    }\n\n    def static hash_map(lst) {\n        def m = [:]\n        assoc_BANG(m, lst)\n    }\n    def static assoc_BANG(m, kvs) {\n        for (int i=0; i<kvs.size(); i+=2) {\n            m[kvs[i]] = kvs[i+1];\n        }\n        return m\n    }\n    def static dissoc_BANG(m, ks) {\n        for (int i=0; i<ks.size(); i++) {\n            m.remove(ks[i])\n        }\n        return m\n    }\n    def static hash_map_Q(o) {\n        return o instanceof Map\n    }\n\n    def static sequential_Q(o) {\n        return types.list_Q(o) || types.vector_Q(o)\n    }\n\n    @AutoClone\n    static class MalFunc {\n        def EVAL\n        def ast\n        def env\n        def params\n        def ismacro\n\n        MalFunc(_EVAL, _ast, _env, _params) {\n            EVAL = _EVAL\n            ast = _ast\n            env = _env\n            params = _params\n            ismacro = false\n        }\n\n        def call(args) {\n            def new_env = env.class.newInstance([env, params, args] as Object[])\n            return EVAL(ast, new_env)\n        }\n    }\n\n    @AutoClone\n    static class MalAtom {\n        def value\n        MalAtom(_value) {\n            value = _value\n        }\n    }\n}\n"
  },
  {
    "path": "impls/guile/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Guile\nRUN apt-get -y install guile-3.0 libpcre3-dev\n"
  },
  {
    "path": "impls/guile/Makefile",
    "content": "SOURCES_BASE = readline.scm types.scm reader.scm printer.scm\nSOURCES_LISP = env.scm core.scm stepA_mal.scm\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.scm\n\nmal.scm: $(SOURCES)\n\techo \"#! /usr/bin/env guile\" > $@\n\techo \"!#\" >> $@\n\tcat $+ | sed $(foreach f,$(+),-e 's/(readline)//') >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.scm\n"
  },
  {
    "path": "impls/guile/core.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(library (core)\n  (export core.ns ->list)\n  (import (guile) (rnrs) (types) (reader) (printer) (ice-9 match) (readline)))\n\n(define (->list o) ((if (vector? o) vector->list identity) o))\n\n(define (vec lst) (if (vector? lst) lst (list->vector lst)))\n\n(define (_count obj)\n  (cond\n   ((_nil? obj) 0)\n   ((vector? obj) (vector-length obj))\n   (else (length obj))))\n\n(define (_empty? obj) (zero? (_count obj)))\n\n;; Well, strange spec...\n(define (_equal? o1 o2)\n  (define (equal-lists? lst1 lst2)\n    (and (= (length lst1) (length lst2))\n         (for-all _equal? lst1 lst2)))\n  (define (equal-hash-tables? ht1 ht2)\n    (define (equal-values? k)\n      (_equal? (_get ht1 k) (_get ht2 k)))\n    (let ((keys1 (_keys ht1)))\n      (and (= (length keys1) (length (_keys ht2)))\n           (for-all equal-values? keys1))))\n  (cond\n    ((and (_sequential? o1) (_sequential? o2))\n     (equal-lists? (->list o1) (->list o2)))\n    ((and (hash-table? o1) (hash-table? o2))\n     (equal-hash-tables? o1 o2))\n    (else\n     (equal? o1 o2))))\n\n(define (pr-str . args)\n  (define (pr x) (pr_str x #t))\n  (string-join (map pr args) \" \"))\n\n(define (str . args)\n  (define (pr x) (pr_str x #f))\n  (string-join (map pr args) \"\"))\n\n(define (prn . args)\n  (format #t \"~a~%\" (apply pr-str args))\n  nil)\n\n(define (println . args)\n  (define (pr x) (pr_str x #f))\n  (format #t \"~{~a~^ ~}~%\" (map pr args))\n  nil)\n\n(define (slurp filename)\n  (when (not (file-exists? filename))\n    (throw 'mal-error (format #f \"File/dir '~a' doesn't exist\" filename)))\n  (call-with-input-file filename get-string-all))\n\n(define (_cons x y)\n  (cons x (->list y)))\n\n(define (concat . args)\n  (apply append (map ->list args)))\n\n(define (_nth lst n)\n  (define ll (->list lst))\n  (when (>= n (length ll))\n    (throw 'mal-error \"nth: index out of range\"))\n  (list-ref ll n))\n\n(define (_first lst)\n  (define ll (->list lst))\n  (cond\n    ((_nil? lst) nil)\n    ((null? ll) nil)\n    (else (car ll))))\n\n(define (_rest lst)\n  (define ll (->list lst))\n  (cond\n    ((_nil? lst) '())\n    ((null? ll) '())\n    (else (cdr ll))))\n\n(define (_map f lst) (map (callable-closure f) (->list lst)))\n\n(define (_apply f . args)\n  (define ll\n    (let lp((next args) (ret '()))\n      (cond\n       ((null? next) (reverse ret))\n       (else\n        (let ((n (->list (car next))))\n          (lp (cdr next) (if (list? n)\n                             (append (reverse n) ret)\n                             (cons n ret))))))))\n  (callable-apply f ll))\n\n(define (->symbol x)\n  ((if (symbol? x) identity string->symbol) x))\n\n(define (->keyword x)\n  ((if (_keyword? x) identity string->keyword) x))\n\n(define (_hash-map . lst) (list->hash-map lst))\n\n(define (_assoc ht . lst) (list->hash-map lst (hash-table-clone ht)))\n\n(define (_get ht k)\n  (if (_nil? ht)\n      nil\n      (hash-ref ht k nil)))\n\n(define (_dissoc ht . lst)\n  (define ht2 (hash-table-clone ht))\n  (for-each (lambda (k) (hash-remove! ht2 k)) lst)\n  ht2)\n\n(define (_keys ht) (hash-map->list (lambda (k v) k) ht))\n\n(define (_vals ht) (hash-map->list (lambda (k v) v) ht))\n\n(define (_contains? ht k)\n  (let ((v (hash-ref ht k '*mal-null*)))\n    (if (eq? v '*mal-null*)\n        #f\n        #t)))\n\n(define (_sequential? o) (or (list? o) (vector? o)))\n\n(define (_meta c)\n  (if (callable? c)\n      (callable-meta-info c)\n      (or (object-property c 'meta) nil)))\n\n(define (_with-meta c ht)\n  (cond\n   ((callable? c)\n    (let ((cc (make-callable ht\n                             (callable-unbox c)\n                             #f\n                             (callable-closure c))))\n      cc))\n   (else\n    (let ((cc (box c)))\n      (set-object-property! cc 'meta ht)\n      cc))))\n\n;; Apply closure 'c' with atom-val as one of arguments, then\n;; set the result as the new val of atom. \n(define (_swap! atom c . rest)\n  (let* ((args (cons (atom-val atom) rest))\n         (val (callable-apply c args)))\n    (atom-val-set! atom val)\n    val))\n\n(define (_conj lst . args)\n  (cond\n   ((vector? lst)\n    (list->vector (append (->list lst) args)))\n   ((list? lst)\n    (append (reverse args) (->list lst)))\n   (else (throw 'mal-error (format #f \"conj: '~a' is not list/vector\" lst)))))\n\n(define (_seq obj)\n  (cond\n   ((_nil? obj) nil)\n   ((_string? obj)\n    (if (string-null? obj) nil (map string (string->list obj))))\n   ((_empty? obj) nil)\n   (else (->list obj))))\n\n(define (__readline prompt)\n  (let ((str (_readline prompt)))\n    (if (eof-object? str)\n        #f\n        str)))\n\n(define (_true? x) (eq? x #t))\n(define (_false? x) (eq? x #f))\n\n;; We need regular named procedure for better debug\n(define (_atom x) (make-atom x))\n(define (_atom? x) (atom? x))\n(define (_deref x) (atom-val x))\n(define (_reset! x v) (atom-val-set! x v))\n\n(define (time-ms)\n  (let ((t (gettimeofday)))\n    (round\n     (+ (* (car t) 1000.0) (/ (cdr t) 1000.0) 0.5))))\n\n(define *primitives*\n  `((list        ,list)\n    (list?       ,list?)\n    (empty?      ,_empty?)\n    (count       ,_count)\n    (=           ,_equal?)\n    (<           ,<)\n    (<=          ,<=)\n    (>           ,>)\n    (>=          ,>=)\n    (+           ,+)\n    (-           ,-)\n    (*           ,*)\n    (/           ,/)\n    (pr-str      ,pr-str)\n    (str         ,str)\n    (prn         ,prn)\n    (println     ,println)\n    (read-string ,read_str)\n    (slurp       ,slurp)\n    (cons        ,_cons)\n    (concat      ,concat)\n    (vec         ,vec)\n    (nth         ,_nth)\n    (first       ,_first)\n    (rest        ,_rest)\n    (map         ,_map)\n    (apply       ,_apply)\n    (nil?        ,_nil?)\n    (true?       ,_true?)\n    (false?      ,_false?)\n    (number?     ,number?)\n    (symbol?     ,symbol?)\n    (symbol      ,->symbol)\n    (string?     ,_string?)\n    (keyword     ,->keyword)\n    (keyword?    ,_keyword?)\n    (vector?     ,vector?)\n    (vector      ,vector)\n    (hash-map    ,_hash-map)\n    (map?        ,hash-table?)\n    (assoc       ,_assoc)\n    (get         ,_get)\n    (dissoc      ,_dissoc)\n    (keys        ,_keys)\n    (vals        ,_vals)\n    (contains?   ,_contains?)\n    (sequential? ,_sequential?)\n    (fn?         ,is-func?)\n    (macro?      ,is-macro?)\n    (readline    ,__readline)\n    (meta        ,_meta)\n    (with-meta   ,_with-meta)\n    (atom        ,_atom)\n    (atom?       ,_atom?)\n    (deref       ,_deref)\n    (reset!      ,_reset!)\n    (swap!       ,_swap!)\n    (conj        ,_conj)\n    (seq         ,_seq)\n    (time-ms     ,time-ms)))\n\n;; Well, we have to rename it to this strange name...\n(define core.ns *primitives*)\n"
  },
  {
    "path": "impls/guile/env.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(library (env)\n  (export make-Env env-has env-check)\n  (import (guile) (types)))\n\n(define (env-check sym env)\n  (env-has sym env (lambda _ #f)))\n\n(define (sym-err-throw sym)\n  (throw 'mal-error (format #f \"'~a' not found\" sym)))\n\n(define* (env-has sym env #:optional (err sym-err-throw))\n  (let ((v ((env 'get) sym)))\n    (if (equal? v '*mal-null*)\n        (err sym)\n        v)))\n\n(define* (make-Env #:key (outer nil) (binds '()) (exprs '()))\n  (define _env (make-hash-table))\n  (define (_set k v) (hash-set! _env k v))\n  (define (_get k)\n    (let ((v (hash-ref _env k '*mal-null*)))\n      (if (equal? v '*mal-null*)\n          (if (_nil? outer)\n              '*mal-null*\n              ((outer 'get) k))\n          v)))\n  (define (_find k) (_get k))\n  (define (_show)\n    (hash-for-each (lambda (k v) (format #t \"~a : ~a~%\" k v)) _env)\n    (display \"outer:\\n\")\n    (and (not (_nil? outer)) ((outer 'show))))\n  (let lp((b binds) (e exprs))\n    (cond\n     ((null? b) #t)\n     ((eq? (car b) '&) (hash-set! _env (cadr b) e)) ; handle varglist\n     (else ; normal binding\n      (when (not (symbol? (car b)))\n        (throw 'mal-error (format #f \"Invalid binding key! '~a'\" (car b))))\n      (when (null? e)\n        (throw 'mal-error \"Invalid pattern for this macro\"))\n      (hash-set! _env (car b) (car e))\n      (lp (cdr b) (cdr e)))))\n  (lambda (cmd)\n    (case cmd\n      ((set) _set)\n      ((find) _find)\n      ((get) _get)\n      ((show) _show)\n      (else (throw 'mal-error (format #f \"BUG: Invalid cmd '~a'\" cmd))))))\n"
  },
  {
    "path": "impls/guile/pcre.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(library (pcre)\n  (export new-pcre\n          pcre-match\n          pcre-get-substring\n          pcre-search)\n  (import (guile) (rnrs) (system foreign)))\n\n(define (make-blob-pointer len)\n  (bytevector->pointer (make-bytevector len)))\n\n(define pcre-ffi (dynamic-link \"libpcre\"))\n\n(define %pcre-compile2\n  (pointer->procedure '*\n                       (dynamic-func \"pcre_compile2\" pcre-ffi)\n                       (list '* int '* '* '* '*)))\n\n(define %pcre-compile\n  (pointer->procedure '*\n                       (dynamic-func \"pcre_compile\" pcre-ffi)\n                       (list '* int '* '* '*)))\n\n(define %pcre-exec\n  (pointer->procedure int\n                      (dynamic-func \"pcre_exec\" pcre-ffi)\n                      (list '* '* '* int int int '* int)))\n\n(define %pcre-study\n  (pointer->procedure '*\n                      (dynamic-func \"pcre_study\" pcre-ffi)\n                      (list '* int '*)))\n\n(define %pcre-get-substring\n  (pointer->procedure '*\n                      (dynamic-func \"pcre_get_substring\" pcre-ffi)\n                      (list '* '* int int '*)))\n\n(define %pcre-free\n  (pointer->procedure void\n                      (dynamic-func \"pcre_free\" pcre-ffi)\n                      (list '*)))\n  \n(define %pcre-free-study (dynamic-func \"pcre_free_study\" pcre-ffi))\n\n(define %pcre-free-substring (dynamic-func \"pcre_free_substring\" pcre-ffi))\n\n(define-record-type pcre\n  (fields\n   errptr\n   (mutable strptr)\n   (mutable ovector)\n   (mutable matched)\n   (mutable code)\n   (mutable extra)))\n   \n(define (%new-pcre)\n  (make-pcre (make-blob-pointer (sizeof ptrdiff_t)) ; errptr\n             #f #f 0 #f #f))\n\n(define* (new-pcre re #:optional (options 0))\n  (let ((reptr (string->pointer re))\n        ;;(errcodeptr (make-blob-pointer int))\n        (erroffset (make-blob-pointer int))\n        (tableptr %null-pointer)\n        (pcre (%new-pcre)))\n    ;; FIXME: add exception handling\n    (pcre-code-set! pcre (%pcre-compile reptr options (pcre-errptr pcre)\n                                        erroffset tableptr))\n    ;;(set-pointer-finalizer! (pcre-code pcre) %pcre-free)\n    pcre))\n\n(define* (pcre-match pcre str #:key (study-options 0) (exec-options 0)\n                     (ovecsize 30) (offset 0))\n  (let ((extra (%pcre-study (pcre-code pcre) study-options (pcre-errptr pcre)))\n        (strptr (string->pointer str))\n        (ovector (make-blob-pointer (* int ovecsize))))\n    (pcre-matched-set! pcre\n                       (%pcre-exec (pcre-code pcre)\n                                   extra\n                                   strptr\n                                   (string-length str)\n                                   offset\n                                   exec-options\n                                   ovector\n                                   ovecsize))\n    (pcre-ovector-set! pcre ovector)\n    (pcre-strptr-set! pcre strptr)\n    (set-pointer-finalizer! extra %pcre-free-study)\n    pcre))\n\n(define (pcre-get-substring pcre index)\n  (let ((strptr (pcre-strptr pcre))\n        (ovector (pcre-ovector pcre))\n        (matched (pcre-matched pcre))\n        (buf (make-blob-pointer (sizeof ptrdiff_t))))\n    (%pcre-get-substring strptr ovector matched index buf)\n    (let ((ret (pointer->string (dereference-pointer buf))))\n      (set-pointer-finalizer! (dereference-pointer buf) %pcre-free-substring)\n      ret)))\n\n(define* (pcre-search pcre str #:key (study-options 0) (exec-options 0)\n                      (exclude \" \"))\n  (define (trim s)\n    (string-trim-both s (lambda (x) (string-contains exclude (string x)))))\n  (define len (string-length str))\n  (let lp((i 0) (ret '()))\n    (cond\n     ((>= i len) (reverse ret))\n     (else\n      (pcre-match pcre str #:study-options study-options #:exec-options exec-options #:offset i)\n      (if (<= (pcre-matched pcre) 0)\n          (lp len ret)\n          (let ((hit (trim (pcre-get-substring pcre 1)))\n                (sublen (string-length (pcre-get-substring pcre 0))))\n            (if (zero? sublen)\n                (lp len ret)\n                (lp (+ i sublen) (cons hit ret)))))))))\n\n(define (pcre-free pcre)\n  (and (not (null-pointer? (pcre-code pcre)))\n       (%pcre-free (pcre-code pcre))))\n"
  },
  {
    "path": "impls/guile/printer.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(library (printer)\n         (export pr_str)\n         (import (guile) (types) (ice-9 match) (ice-9 regex)))\n\n(define (print-hashmap hm p)\n  (call-with-output-string\n   (lambda (port)\n     (display \"{\" port)\n     (display\n      (string-join\n       (hash-map->list\n        (lambda (k v)\n          (format #f \"~a ~a\" (p k) (p v)))\n        hm)\n       \" \")\n      port)\n     (display \"}\" port))))\n\n(define (pr_str obj readable?)\n  (define (->str s)\n    (string-sub\n     (string-sub\n      (string-sub s \"\\\\\\\\\" \"\\\\\\\\\")\n      \"\\\"\" \"\\\\\\\"\")\n     \"\\n\" \"\\\\n\"))\n  (define (%pr_str o) (pr_str o readable?))\n  (match obj\n    ((? box?) (%pr_str (unbox obj)))\n    ((? is-func?) \"#<function>\")\n    ((? is-macro?) \"#<macro>\")\n    ((? list?) (format #f \"(~{~a~^ ~})\" (map %pr_str obj)))\n    ((? vector?) (format #f \"[~{~a~^ ~}]\" (map %pr_str (vector->list obj))))\n    ((? hash-table?) (print-hashmap obj %pr_str))\n    ((? string?)\n     (cond\n      ((_keyword? obj)\n       => (lambda (m) (format #f \":~a\" (substring obj 1))))\n      (else (if readable? (format #f \"\\\"~a\\\"\" (->str obj)) obj))))\n    ;;((? number?) (format #f \"~a\" obj))\n    ;;((? symbol?) (format #f \"~a\" obj))\n    ((? atom?) (format #f \"(atom ~a)\" (%pr_str (atom-val obj))))\n    ((? _nil?) \"nil\")\n    (#t \"true\")\n    (#f \"false\")\n    (else (format #f \"~a\" obj))))\n"
  },
  {
    "path": "impls/guile/reader.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(library (reader)\n         (export read_str)\n         (import (guile) (pcre) (ice-9 match) (srfi srfi-1)\n                 (ice-9 regex) (types) (ice-9 format)))\n\n(define (make-Reader tokens)\n  (lambda (cmd)\n    (case cmd\n      ((next)\n       (if (null? tokens)\n           '()\n           (let ((r (car tokens))) (set! tokens (cdr tokens)) r)))\n      ((peek) (if (null? tokens) '() (car tokens)))\n      (else (error \"Reader: Invalid cmd!\" cmd)))))\n\n(define *token-re*\n  (new-pcre \"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"|;[^\\n]*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\"))\n\n(define *str-re*\n  (new-pcre \"^(\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\")$\"))\n\n(define (tokenizer str)\n  (filter (lambda (s) (and (not (string-null? s)) (not (string=? (substring s 0 1) \";\"))))\n          (pcre-search *token-re* str)))\n\n(define (delim-read reader delim)\n  (let lp((next (reader 'peek)) (ret '()))\n    (cond\n     ((null? next) (throw 'mal-error (format #f \"expected '~a', got EOF\" delim)))\n     ((string=? next delim) (reader 'next) (reverse ret))\n     (else\n      (let* ((cur (read_form reader))\n             (n (reader 'peek)))\n        (lp n (cons cur ret)))))))\n\n(define (read_list reader)\n  (cond\n   ((string=? \")\" (reader 'peek))\n    (reader 'next)\n    '())\n   (else (delim-read reader \")\"))))\n\n(define (read_vector reader)\n  (cond\n   ((string=? \"]\" (reader 'peek))\n    (reader 'next)\n    #())\n   (else (list->vector (delim-read reader \"]\")))))\n\n(define (read_hashmap reader)\n  (define ht (make-hash-table))\n  (define lst (delim-read reader \"}\"))\n  (cond\n   ((null? lst) ht)\n   (else\n    (let lp((next lst))\n      (cond\n       ((null? next) ht)\n       (else\n        (when (null? (cdr next))\n          (throw 'mal-error\n                 (format #f \"read_hashmap: '~a' lack of value\" (car next))))\n        (let ((k (car next))\n              (v (cadr next)))\n          (hash-set! ht k v)\n          (lp (cddr next)))))))))\n\n(define (read_atom reader)\n  (let ((token (reader 'next)))\n    (cond\n     ((string-match \"^-?[0-9][0-9.]*$\" token)\n      => (lambda (m) (string->number (match:substring m 0))))\n     ((> (length (pcre-search *str-re* token)) 0)\n      (with-input-from-string token read))\n     ((eqv? (string-ref token 0) #\\\")\n      (throw 'mal-error \"expected '\\\"', got EOF\"))\n     ((string-match \"^:(.*)\" token)\n      => (lambda (m) (string->keyword (match:substring m 1))))\n     ((string=? \"nil\" token) nil)\n     ((string=? \"true\" token) #t)\n     ((string=? \"false\" token) #f)\n     (else (string->symbol token)))))\n\n(define (read_form reader)\n  (define (clean x)\n    (if (string? x)\n        (string-trim-both\n         x\n         (lambda (c) (char-set-contains? char-set:whitespace c)))\n        x)) \n  (define (next) (reader 'next))\n  (define (more) (read_form reader))\n  (match (clean (reader 'peek)) \n    (() (throw 'mal-error \"blank line\")) ; FIXME: what should be returned?\n    (\"'\" (next) (list 'quote (more)))\n    (\"`\" (next) (list 'quasiquote (more)))\n    (\"~\" (next) (list 'unquote (more)))\n    (\"~@\" (next) (list 'splice-unquote (more)))\n    (\"^\" (next) (let ((meta (more))) `(with-meta ,(more) ,meta)))\n    (\"@\" (next) `(deref ,(more)))\n    (\")\" (next) (throw 'mal-error \"unexpected ')'\"))\n    (\"(\" (next) (read_list reader))\n    (\"]\" (throw 'mal-error \"unexpected ']'\"))\n    (\"[\" (next) (read_vector reader))\n    (\"}\" (throw 'mal-error \"unexpected '}'\"))\n    (\"{\" (next) (read_hashmap reader))\n    (\"\" (next) (read_form reader))\n    (else (read_atom reader))))\n\n(define (read_str str)\n  (if (eof-object? str)\n      str\n      (let* ((tokens (tokenizer str))\n             (t (if (null? tokens)\n                    (if (char=? (string-ref str 0) #\\;)\n                        '()\n                        (list str))\n                    tokens)))\n        (read_form (make-Reader t)))))\n"
  },
  {
    "path": "impls/guile/readline.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n;;(use-modules (ice-9 readline))\n\n(library (readline)\n  (export _readline)\n  (import (guile) (ice-9 readline)))\n\n(define mal-history\n  (format #f \"~a/.mal-history\" (getenv \"HOME\")))\n\n(setenv \"GUILE_HISTORY\" mal-history)\n(readline-set! bounce-parens 0)\n(activate-readline)\n\n(define (_readline prompt)\n  (let ((str (readline prompt)))\n    (and (not (eof-object? str)) (add-history str))\n    str))\n"
  },
  {
    "path": "impls/guile/run",
    "content": "#!/usr/bin/env bash\n# XDG_CACHE_HOME is where guile stores the compiled files\nXDG_CACHE_HOME=.cache/ exec guile -L $(dirname $0) $(dirname $0)/${STEP:-stepA_mal}.scm \"${@}\"\n"
  },
  {
    "path": "impls/guile/step0_repl.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline))\n\n(define (READ str)\n  str)\n\n(define (EVAL ast env) ast)\n\n(define (PRINT str)\n  (format #t \"~a~%\" str))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (PRINT (EVAL (READ line) '())))))))\n\n(REPL)\n"
  },
  {
    "path": "impls/guile/step1_read_print.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer))\n\n(define (READ str)\n  (read_str str))\n\n(define (EVAL ast env) ast)\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) '())))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n(REPL)\n"
  },
  {
    "path": "impls/guile/step2_eval.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43) (types))\n\n(define *toplevel*\n  `((+ . ,+)\n    (- . ,-)\n    (* . ,*)\n    (/ . ,/)))\n\n(define (READ str)\n  (read_str str))\n\n(define (EVAL ast env)\n  ; (format #t \"EVAL: ~a~%\" (pr_str ast #t))\n  (match ast\n    ((? symbol? sym)\n     (or (assoc-ref env sym)\n         (throw 'mal-error (format #f \"'~a' not found\" sym))))\n    ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n    ((? hash-table? ht)\n     (define new-ht (make-hash-table))\n     (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n     new-ht)\n    ((? non-list?) ast)\n    (() ast)\n    (else\n      (let ((el (map (lambda (x) (EVAL x env)) ast)))\n        (apply (car el) (cdr el))))))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n(REPL)\n"
  },
  {
    "path": "impls/guile/step3_env.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (types))\n\n(define *primitives*\n  `((+ ,+)\n    (- ,-)\n    (* ,*)\n    (/ ,/)))\n\n(define *toplevel*\n  (receive (b e) (unzip2 *primitives*) \n    (make-Env #:binds b #:exprs e)))\n\n(define (READ str)\n  (read_str str))\n\n(define (EVAL ast env)\n  (define (->list kvs) ((if (vector? kvs) vector->list identity) kvs))\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  (when (cond-true? (env-check 'DEBUG-EVAL env))\n    (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n  (match ast\n    ((? symbol? sym) (env-has sym env))\n    ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n    ((? hash-table? ht)\n     (define new-ht (make-hash-table))\n     (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n     new-ht)\n    ((? non-list?) ast)\n    (() ast)\n    (('def! k v) ((env 'set) k (EVAL v env)))\n    (('let* kvs body)\n     (let* ((new-env (make-Env #:outer env))\n            (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n       (receive (keys vals) (%unzip2 (->list kvs))\n         (for-each setter keys vals))\n       (EVAL body new-env)))\n    (else\n      (let ((el (map (lambda (x) (EVAL x env)) ast)))\n        (apply (car el) (cdr el))))))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n(REPL)\n"
  },
  {
    "path": "impls/guile/step4_if_fn_do.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (make-Env #:binds b #:exprs e)))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  (when (cond-true? (env-check 'DEBUG-EVAL env))\n    (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n  (match ast\n    ((? symbol? sym) (env-has sym env))\n    ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n    ((? hash-table? ht)\n     (define new-ht (make-hash-table))\n     (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n     new-ht)\n    ((? non-list?) ast)\n    (() ast)\n    (('def! k v) ((env 'set) k (EVAL v env)))\n    (('let* kvs body)\n     (let* ((new-env (make-Env #:outer env))\n            (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n       (receive (keys vals) (%unzip2 (->list kvs))\n         (for-each setter keys vals))\n       (EVAL body new-env)))\n    (('do rest ...)\n     (eval_seq rest env))\n    (('if cnd thn els ...)\n     (cond\n      ((and (not (null? els)) (not (null? (cdr els))))\n       ;; Invalid `if' form\n       (throw 'mal-error\n              (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n      ((cond-true? (EVAL cnd env)) (EVAL thn env))\n      (else (if (null? els) nil (EVAL (car els) env)))))\n    (('fn* params body ...) ; function definition\n     (lambda args\n       (eval_seq body (make-Env #:outer env #:binds (->list params) #:exprs args))))\n    (else\n      (let ((el (map (lambda (x) (EVAL x env)) ast)))\n        (apply (car el) (cdr el))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n\n(REPL)\n"
  },
  {
    "path": "impls/guile/step5_tco.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (make-Env #:binds b #:exprs (map make-func e))))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  ;; NOTE: I wish I can use (while #t ...) for that, but this is not Lispy, which means\n  ;;       it'll bring some trouble in control flow. We have to use continuations to return\n  ;;       and use non-standard `break' feature. In a word, not elegant at all.\n  ;;       The named let loop is natural for Scheme, but it looks a bit cheating. But NO!\n  ;;       Such kind of loop is actually `while loop' in Scheme, I don't take advantage of\n  ;;       TCO in Scheme to implement TCO, but it's the same principle with normal loop.\n  ;;       If you're Lispy enough, there's no recursive at all while you saw named let loop.\n  (let tco-loop((ast ast) (env env))\n    (when (cond-true? (env-check 'DEBUG-EVAL env))\n      (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n    (match ast\n      ((? symbol? sym) (env-has sym env))\n      ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n      ((? hash-table? ht)\n       (define new-ht (make-hash-table))\n       (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n       new-ht)\n      ((? non-list?) ast)\n      (() ast)\n      (('def! k v) ((env 'set) k (EVAL v env)))\n      (('let* kvs body)\n       (let* ((new-env (make-Env #:outer env))\n              (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n         (receive (keys vals) (%unzip2 (->list kvs))\n                  (for-each setter keys vals))\n         (tco-loop body new-env)))\n       (('do rest ...)\n        (cond\n         ((null? rest)\n          (throw 'mal-error (format #f \"do: Invalid form! '~a'\" rest)))\n         ((= 1 (length rest)) (tco-loop (car rest) env))\n         (else\n          (let ((mexpr (take rest (1- (length rest))))\n                (tail-call (car (take-right rest 1))))\n            (eval_seq mexpr env)\n            (tco-loop tail-call env)))))\n       (('if cnd thn els ...)\n        (cond\n         ((and (not (null? els)) (not (null? (cdr els))))\n          ;; Invalid `if' form\n          (throw 'mal-error\n                 (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n         ((cond-true? (EVAL cnd env)) (tco-loop thn env))\n         (else (if (null? els) nil (tco-loop (car els) env)))))\n       (('fn* params body ...) ; function definition\n\t(make-func\n         (lambda args\n           (let ((nenv (make-Env #:outer env #:binds (->list params) #:exprs args)))\n             (cond\n              ((null? body)\n               (throw 'mal-error (format #f \"fn*: bad lambda in form '~a'\" ast)))\n              ((= 1 (length body)) (tco-loop (car body) nenv))\n              (else\n               (let ((mexpr (take body (1- (length body))))\n                     (tail-call (car (take-right body 1))))\n                 (eval_seq mexpr nenv)\n                 (tco-loop tail-call nenv))))))))\n       (else\n         (let ((el (map (lambda (x) (EVAL x env)) ast)))\n           (callable-apply (car el) (cdr el)))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n\n(REPL)\n"
  },
  {
    "path": "impls/guile/step6_file.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (make-Env #:binds b #:exprs (map make-func e))))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  ;; NOTE: I wish I can use (while #t ...) for that, but this is not Lispy, which means\n  ;;       it'll bring some trouble in control flow. We have to use continuations to return\n  ;;       and use non-standard `break' feature. In a word, not elegant at all.\n  ;;       The named let loop is natural for Scheme, but it looks a bit cheating. But NO!\n  ;;       Such kind of loop is actually `while loop' in Scheme, I don't take advantage of\n  ;;       TCO in Scheme to implement TCO, but it's the same principle with normal loop.\n  ;;       If you're Lispy enough, there's no recursive at all while you saw named let loop.\n  (let tco-loop((ast ast) (env env))\n    (when (cond-true? (env-check 'DEBUG-EVAL env))\n      (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n    (match ast\n      ((? symbol? sym) (env-has sym env))\n      ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n      ((? hash-table? ht)\n       (define new-ht (make-hash-table))\n       (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n       new-ht)\n      ((? non-list?) ast)\n      (() ast)\n      (('def! k v) ((env 'set) k (EVAL v env)))\n      (('let* kvs body)\n       (let* ((new-env (make-Env #:outer env))\n              (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n         (receive (keys vals) (%unzip2 (->list kvs))\n                  (for-each setter keys vals))\n         (tco-loop body new-env)))\n       (('do rest ...)\n        (cond\n         ((null? rest)\n          (throw 'mal-error (format #f \"do: Invalid form! '~a'\" rest)))\n         ((= 1 (length rest)) (tco-loop (car rest) env))\n         (else\n          (let ((mexpr (take rest (1- (length rest))))\n                (tail-call (car (take-right rest 1))))\n            (eval_seq mexpr env)\n            (tco-loop tail-call env)))))\n       (('if cnd thn els ...)\n        (cond\n         ((and (not (null? els)) (not (null? (cdr els))))\n          ;; Invalid `if' form\n          (throw 'mal-error\n                 (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n         ((cond-true? (EVAL cnd env)) (tco-loop thn env))\n         (else (if (null? els) nil (tco-loop (car els) env)))))\n       (('fn* params body ...) ; function definition\n\t(make-func\n         (lambda args\n           (let ((nenv (make-Env #:outer env #:binds (->list params) #:exprs args)))\n             (cond\n              ((null? body)\n               (throw 'mal-error (format #f \"fn*: bad lambda in form '~a'\" ast)))\n              ((= 1 (length body)) (tco-loop (car body) nenv))\n              (else\n               (let ((mexpr (take body (1- (length body))))\n                     (tail-call (car (take-right body 1))))\n                 (eval_seq mexpr nenv)\n                 (tco-loop tail-call nenv))))))))\n       (else\n         (let ((el (map (lambda (x) (EVAL x env)) ast)))\n           (callable-apply (car el) (cdr el)))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n;; initialization\n((*toplevel* 'set) 'eval (make-func (lambda (ast) (EVAL ast *toplevel*))))\n((*toplevel* 'set) '*ARGV* '())\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n(EVAL-string \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(let ((args (cdr (command-line))))\n  (cond\n   ((> (length args) 0)\n    ((*toplevel* 'set) '*ARGV* (cdr args))\n    (EVAL-string (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n   (else (REPL))))\n"
  },
  {
    "path": "impls/guile/step7_quote.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (make-Env #:binds b #:exprs (map make-func e))))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (qqIter elt acc)\n  (match elt\n    (('splice-unquote x) (list 'concat x acc))\n    (else                (list 'cons (_quasiquote elt) acc))))\n(define (_quasiquote ast)\n  (match ast\n    (('unquote x)    x)\n    ( (xs ...)       (fold-right qqIter '() xs))\n    (#(xs ...)       (list 'vec (fold-right qqIter '() xs)))\n    ((? hash-table?) (list 'quote ast))\n    ((? symbol?)     (list 'quote ast))\n    (else            ast)))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  (let tco-loop((ast ast) (env env))\n    (when (cond-true? (env-check 'DEBUG-EVAL env))\n      (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n    (match ast\n      ((? symbol? sym) (env-has sym env))\n      ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n      ((? hash-table? ht)\n       (define new-ht (make-hash-table))\n       (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n       new-ht)\n      ((? non-list?) ast)\n      (() ast)\n      (('quote obj) obj)\n      (('quasiquote obj) (EVAL (_quasiquote obj) env))\n      (('def! k v) ((env 'set) k (EVAL v env)))\n      (('let* kvs body)\n       (let* ((new-env (make-Env #:outer env))\n              (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n         (receive (keys vals) (%unzip2 (->list kvs))\n                  (for-each setter keys vals))\n         (tco-loop body new-env)))\n       (('do rest ...)\n        (cond\n         ((null? rest)\n          (throw 'mal-error (format #f \"do: Invalid form! '~a'\" rest)))\n         ((= 1 (length rest)) (tco-loop (car rest) env))\n         (else\n          (let ((mexpr (take rest (1- (length rest))))\n                (tail-call (car (take-right rest 1))))\n            (eval_seq mexpr env)\n            (tco-loop tail-call env)))))\n       (('if cnd thn els ...)\n        (cond\n         ((and (not (null? els)) (not (null? (cdr els))))\n          ;; Invalid `if' form\n          (throw 'mal-error\n                 (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n         ((cond-true? (EVAL cnd env)) (tco-loop thn env))\n         (else (if (null? els) nil (tco-loop (car els) env)))))\n       (('fn* params body ...) ; function definition\n\t(make-func\n         (lambda args\n           (let ((nenv (make-Env #:outer env #:binds (->list params) #:exprs args)))\n             (cond\n              ((null? body)\n               (throw 'mal-error (format #f \"fn*: bad lambda in form '~a'\" ast)))\n              ((= 1 (length body)) (tco-loop (car body) nenv))\n              (else\n               (let ((mexpr (take body (1- (length body))))\n                     (tail-call (car (take-right body 1))))\n                 (eval_seq mexpr nenv)\n                 (tco-loop tail-call nenv))))))))\n       (else\n         (let ((el (map (lambda (x) (EVAL x env)) ast)))\n           (callable-apply (car el) (cdr el)))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n;; initialization\n((*toplevel* 'set) 'eval (make-func (lambda (ast) (EVAL ast *toplevel*))))\n((*toplevel* 'set) '*ARGV* '())\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n(EVAL-string \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(let ((args (cdr (command-line))))\n  (cond\n   ((> (length args) 0)\n    ((*toplevel* 'set) '*ARGV* (cdr args))\n    (EVAL-string (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n   (else (REPL))))\n"
  },
  {
    "path": "impls/guile/step8_macros.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (make-Env #:binds b #:exprs (map make-func e))))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (qqIter elt acc)\n  (match elt\n    (('splice-unquote x) (list 'concat x acc))\n    (else                (list 'cons (_quasiquote elt) acc))))\n(define (_quasiquote ast)\n  (match ast\n    (('unquote x)    x)\n    ( (xs ...)       (fold-right qqIter '() xs))\n    (#(xs ...)       (list 'vec (fold-right qqIter '() xs)))\n    ((? hash-table?) (list 'quote ast))\n    ((? symbol?)     (list 'quote ast))\n    (else            ast)))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  (let tco-loop((ast ast) (env env)) ; expand as possible\n      (when (cond-true? (env-check 'DEBUG-EVAL env))\n        (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n      (match ast\n        ((? symbol? sym) (env-has sym env))\n        ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n        ((? hash-table? ht)\n         (define new-ht (make-hash-table))\n         (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n         new-ht)\n        ((? non-list?) ast)\n        (() ast)\n        (('defmacro! k v)\n         (let ((c (EVAL v env)))\n           ((env 'set) k (callable-as-macro c))))\n        (('quote obj) obj)\n        (('quasiquote obj) (EVAL (_quasiquote obj) env))\n        (('def! k v) ((env 'set) k (EVAL v env)))\n        (('let* kvs body)\n         (let* ((new-env (make-Env #:outer env))\n                (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n           (receive (keys vals) (%unzip2 (->list kvs))\n                    (for-each setter keys vals))\n           (tco-loop body new-env)))\n        (('do rest ...)\n         (cond\n          ((null? rest)\n           (throw 'mal-error (format #f \"do: Invalid form! '~a'\" rest)))\n          ((= 1 (length rest)) (tco-loop (car rest) env))\n          (else\n           (let ((mexpr (take rest (1- (length rest))))\n                 (tail-call (car (take-right rest 1))))\n             (eval_seq mexpr env)\n             (tco-loop tail-call env)))))\n        (('if cnd thn els ...)\n         (cond\n          ((and (not (null? els)) (not (null? (cdr els))))\n           ;; Invalid `if' form\n           (throw 'mal-error\n                  (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n          ((cond-true? (EVAL cnd env)) (tco-loop thn env))\n          (else (if (null? els) nil (tco-loop (car els) env)))))\n        (('fn* params body ...) ; function definition\n         (make-anonymous-func\n          (lambda args\n            (let ((nenv (make-Env #:outer env #:binds (->list params) #:exprs args)))\n              (cond\n               ((null? body)\n                (throw 'mal-error (format #f \"fn*: bad lambda in form '~a'\" ast)))\n               ((= 1 (length body)) (tco-loop (car body) nenv))\n               (else\n                (let ((mexpr (take body (1- (length body))))\n                      (tail-call (car (take-right body 1))))\n                  (eval_seq mexpr nenv)\n                  (tco-loop tail-call nenv))))))))\n        (else\n          (let ((f (EVAL (car ast) env))\n                (args (cdr ast)))\n            (if (is-macro f)\n              (EVAL (callable-apply f args) env)\n              (callable-apply f (map (lambda (x) (EVAL x env)) args))))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n;; initialization\n((*toplevel* 'set) 'eval (make-func (lambda (ast) (EVAL ast *toplevel*))))\n((*toplevel* 'set) '*ARGV* '())\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n(EVAL-string \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(EVAL-string \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(let ((args (cdr (command-line))))\n  (cond\n   ((> (length args) 0)\n    ((*toplevel* 'set) '*ARGV* (cdr args))\n    (EVAL-string (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n   (else (REPL))))\n"
  },
  {
    "path": "impls/guile/step9_try.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n;; Primitives which doesn't unbox args in default.\n;; This is a trick to implement meta-info taking advange of the original\n;; types of Guile as possible.\n(define *unbox-exception* '(meta assoc swap!))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (let ((env (make-Env #:binds b #:exprs (map make-func e))))\n      (for-each (lambda (f)\n                  (callable-unbox-set! ((env 'get) f) #f))\n                *unbox-exception*)\n      env)))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (qqIter elt acc)\n  (match elt\n    (('splice-unquote x) (list 'concat x acc))\n    (else                (list 'cons (_quasiquote elt) acc))))\n(define (_quasiquote ast)\n  (match ast\n    (('unquote x)    x)\n    ( (xs ...)       (fold-right qqIter '() xs))\n    (#(xs ...)       (list 'vec (fold-right qqIter '() xs)))\n    ((? hash-table?) (list 'quote ast))\n    ((? symbol?)     (list 'quote ast))\n    (else            ast)))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  (let tco-loop((ast ast) (env env)) ; expand as possible\n      (when (cond-true? (env-check 'DEBUG-EVAL env))\n        (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n      (match ast\n        ((? symbol? sym) (env-has sym env))\n        ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n        ((? hash-table? ht)\n         (define new-ht (make-hash-table))\n         (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n         new-ht)\n        ((? non-list?) ast)\n        (() ast)\n        (('defmacro! k v)\n         (let ((c (EVAL v env)))\n           ((env 'set) k (callable-as-macro c))))\n        (('quote obj) obj)\n        (('quasiquote obj) (EVAL (_quasiquote obj) env))\n        (('def! k v) ((env 'set) k (EVAL v env)))\n        (('let* kvs body)\n         (let* ((new-env (make-Env #:outer env))\n                (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n           (receive (keys vals) (%unzip2 (->list kvs))\n                    (for-each setter keys vals))\n           (tco-loop body new-env)))\n        (('do rest ...)\n         (cond\n          ((null? rest)\n           (throw 'mal-error (format #f \"do: Invalid form! '~a'\" rest)))\n          ((= 1 (length rest)) (tco-loop (car rest) env))\n          (else\n           (let ((mexpr (take rest (1- (length rest))))\n                 (tail-call (car (take-right rest 1))))\n             (eval_seq mexpr env)\n             (tco-loop tail-call env)))))\n        (('if cnd thn els ...)\n         (cond\n          ((and (not (null? els)) (not (null? (cdr els))))\n           ;; Invalid `if' form\n           (throw 'mal-error\n                  (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n          ((cond-true? (EVAL cnd env)) (tco-loop thn env))\n          (else (if (null? els) nil (tco-loop (car els) env)))))\n        (('fn* params body ...) ; function definition\n         (make-anonymous-func\n          (lambda args\n            (let ((nenv (make-Env #:outer env #:binds (->list params) #:exprs args)))\n              (cond\n               ((null? body)\n                (throw 'mal-error (format #f \"fn*: bad lambda in form '~a'\" ast)))\n               ((= 1 (length body)) (tco-loop (car body) nenv))\n               (else\n                (let ((mexpr (take body (1- (length body))))\n                      (tail-call (car (take-right body 1))))\n                  (eval_seq mexpr nenv)\n                  (tco-loop tail-call nenv))))))))\n        (('try* A)\n         (EVAL A env))\n        (('try* A ('catch* B C))\n         (catch\n          #t\n          (lambda () (EVAL A env))\n          (lambda e\n            (let ((nenv (make-Env #:outer env #:binds (list B) #:exprs (cdr e))))\n              (EVAL C nenv)))))\n        (else\n          (let ((f (EVAL (car ast) env))\n                (args (cdr ast)))\n            (if (is-macro f)\n              (EVAL (callable-apply f args) env)\n              (callable-apply f (map (lambda (x) (EVAL x env)) args))))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n;; initialization\n((*toplevel* 'set) 'eval (make-func (lambda (ast) (EVAL ast *toplevel*))))\n((*toplevel* 'set) 'throw (make-func (lambda (val) (throw 'mal-error val))))\n((*toplevel* 'set) '*ARGV* '())\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n(EVAL-string \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(EVAL-string \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(let ((args (cdr (command-line))))\n  (cond\n   ((> (length args) 0)\n    ((*toplevel* 'set) '*ARGV* (cdr args))\n    (EVAL-string (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n   (else (REPL))))\n"
  },
  {
    "path": "impls/guile/stepA_mal.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(import (readline) (reader) (printer) (ice-9 match) (srfi srfi-43)\n        (srfi srfi-1) (ice-9 receive) (env) (core) (types))\n\n;; Primitives which doesn't unbox args in default.\n;; This is a trick to implement meta-info taking advange of the original\n;; types of Guile as possible.\n(define *unbox-exception* '(meta assoc swap!))\n\n(define *toplevel*\n  (receive (b e) (unzip2 core.ns)\n    (let ((env (make-Env #:binds b #:exprs (map make-func e))))\n      (for-each (lambda (f)\n                  (callable-unbox-set! ((env 'get) f) #f))\n                *unbox-exception*)\n      env)))\n\n(define (READ str)\n  (read_str str))\n\n(define (eval_seq ast env)\n  (cond\n   ((null? ast) nil)\n   ((null? (cdr ast)) (EVAL (car ast) env))\n   (else\n    (EVAL (car ast) env)\n    (eval_seq (cdr ast) env))))\n\n(define (qqIter elt acc)\n  (match elt\n    (('splice-unquote x) (list 'concat x acc))\n    (else                (list 'cons (_quasiquote elt) acc))))\n(define (_quasiquote ast)\n  (match ast\n    (('unquote x)    x)\n    ( (xs ...)       (fold-right qqIter '() xs))\n    (#(xs ...)       (list 'vec (fold-right qqIter '() xs)))\n    ((? hash-table?) (list 'quote ast))\n    ((? symbol?)     (list 'quote ast))\n    (else            ast)))\n\n(define (EVAL ast env)\n  (define (%unzip2 kvs)\n    (let lp((next kvs) (k '()) (v '()))\n      (cond\n       ;; NOTE: reverse is very important here!\n       ((null? next) (values (reverse k) (reverse v)))\n       ((null? (cdr next))\n        (throw 'mal-error (format #f \"let*: Invalid binding form '~a'\" kvs))) \n       (else (lp (cddr next) (cons (car next) k) (cons (cadr next) v))))))\n  (let tco-loop((ast ast) (env env)) ; expand as possible\n      (when (cond-true? (env-check 'DEBUG-EVAL env))\n        (format #t \"EVAL: ~a~%\" (pr_str ast #t)))\n      (match ast\n        ((? symbol? sym) (env-has sym env))\n        ((? vector? vec) (vector-map (lambda (i x) (EVAL x env)) vec))\n        ((? hash-table? ht)\n         (define new-ht (make-hash-table))\n         (hash-for-each (lambda (k v) (hash-set! new-ht k (EVAL v env))) ht)\n         new-ht)\n        ((? non-list?) ast)\n        (() ast)\n        (('defmacro! k v)\n         (let ((c (EVAL v env)))\n           ((env 'set) k (callable-as-macro c))))\n        (('quote obj) obj)\n        (('quasiquote obj) (EVAL (_quasiquote obj) env))\n        (('def! k v) ((env 'set) k (EVAL v env)))\n        (('let* kvs body)\n         (let* ((new-env (make-Env #:outer env))\n                (setter (lambda (k v) ((new-env 'set) k (EVAL v new-env)))))\n           (receive (keys vals) (%unzip2 (->list kvs))\n                    (for-each setter keys vals))\n           (tco-loop body new-env)))\n        (('do rest ...)\n         (cond\n          ((null? rest)\n           (throw 'mal-error (format #f \"do: Invalid form! '~a'\" rest)))\n          ((= 1 (length rest)) (tco-loop (car rest) env))\n          (else\n           (let ((mexpr (take rest (1- (length rest))))\n                 (tail-call (car (take-right rest 1))))\n             (eval_seq mexpr env)\n             (tco-loop tail-call env)))))\n        (('if cnd thn els ...)\n         (cond\n          ((and (not (null? els)) (not (null? (cdr els))))\n           ;; Invalid `if' form\n           (throw 'mal-error\n                  (format #f \"if: failed to match any pattern in form '~a'\" ast)))\n          ((cond-true? (EVAL cnd env)) (tco-loop thn env))\n          (else (if (null? els) nil (tco-loop (car els) env)))))\n        (('fn* params body ...) ; function definition\n         (make-anonymous-func\n          (lambda args\n            (let ((nenv (make-Env #:outer env #:binds (->list params) #:exprs args)))\n              (cond\n               ((null? body)\n                (throw 'mal-error (format #f \"fn*: bad lambda in form '~a'\" ast)))\n               ((= 1 (length body)) (tco-loop (car body) nenv))\n               (else\n                (let ((mexpr (take body (1- (length body))))\n                      (tail-call (car (take-right body 1))))\n                  (eval_seq mexpr nenv)\n                  (tco-loop tail-call nenv))))))))\n        (('try* A)\n         (EVAL A env))\n        (('try* A ('catch* B C))\n         (catch\n          #t\n          (lambda () (EVAL A env))\n          (lambda e\n            (let ((nenv (make-Env #:outer env #:binds (list B) #:exprs (cdr e))))\n              (EVAL C nenv)))))\n        (else\n          (let ((f (EVAL (car ast) env))\n                (args (cdr ast)))\n            (if (is-macro f)\n              (EVAL (callable-apply f args) env)\n              (callable-apply f (map (lambda (x) (EVAL x env)) args))))))))\n\n(define (EVAL-string str)\n  (EVAL (read_str str) *toplevel*))\n\n(define (PRINT exp)\n  (and (not (eof-object? exp))\n       (format #t \"~a~%\" (pr_str exp #t))))\n\n(define (LOOP continue?)\n  (and continue? (REPL)))\n\n(define (REPL)\n  (LOOP\n   (let ((line (_readline \"user> \")))\n     (cond\n       ((eof-object? line) #f)\n       ((string=? line \"\") #t)\n       (else\n         (catch 'mal-error\n                (lambda () (PRINT (EVAL (READ line) *toplevel*)))\n                (lambda (k . e)\n                  (format #t \"Error: ~a~%\" (pr_str (car e) #t)))))))))\n\n;; initialization\n((*toplevel* 'set) 'eval (make-func (lambda (ast) (EVAL ast *toplevel*))))\n((*toplevel* 'set) 'throw (make-func (lambda (val) (throw 'mal-error val))))\n((*toplevel* 'set) '*ARGV* '())\n(EVAL-string \"(def! not (fn* (x) (if x false true)))\")\n(EVAL-string \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(EVAL-string \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n(EVAL-string \"(def! *host-language* \\\"guile\\\")\")\n\n(let ((args (cdr (command-line))))\n  (cond\n   ((> (length args) 0)\n    ((*toplevel* 'set) '*ARGV* (cdr args))\n    (EVAL-string (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n   (else\n    (EVAL-string \"(println (str \\\"Mal (\\\" *host-language* \\\")\\\"))\")\n    (REPL))))\n"
  },
  {
    "path": "impls/guile/types.scm",
    "content": ";;  Copyright (C) 2015\n;;      \"Mu Lei\" known as \"NalaGinrut\" <NalaGinrut@gmail.com>\n;;  This file is free software: you can redistribute it and/or modify\n;;  it under the terms of the GNU General Public License as published by\n;;  the Free Software Foundation, either version 3 of the License, or\n;;  (at your option) any later version.\n\n;;  This file is distributed in the hope that it will be useful,\n;;  but WITHOUT ANY WARRANTY; without even the implied warranty of\n;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;;  GNU General Public License for more details.\n\n;;  You should have received a copy of the GNU General Public License\n;;  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n(library (types)\n  (export string-sub *eof* non-list?\n          string->keyword _keyword? _string?\n          nil _nil? list->hash-map\n          cond-true? make-anonymous-func\n          make-atom atom? atom-val atom-val-set!\n          make-callable callable? callable-is_macro\n          callable-as-macro callable-closure\n          is-func is-func? is-macro is-macro? make-func callable-apply\n          callable-unbox-set! callable-unbox\n          callable-meta-info hash-table-clone\n          box? box unbox)\n  (import (guile) (only (rnrs) define-record-type) (ice-9 regex) (ice-9 session)))\n\n(define (non-list? x) (not (list? x)))\n\n\n(define (string-sub str p1 p2)\n  (regexp-substitute/global #f p1 str 'pre p2 'post))\n\n(define *eof* (call-with-input-string \"\" read))\n\n(define (string->keyword str)\n  (when (not (string? str))\n    (throw 'mal-error (format #f \"string->keyword: '~a' is not a string\" str)))\n  (string-append \"\\u029e\" str))\n\n(define (_keyword? k)\n  (and (string? k)\n       (> (string-length k) 0)\n       (char=? #\\1236 (string-ref k 0))))\n\n(define (_string? s)\n  (and (string? s) (not (_keyword? s))))\n\n(define-record-type mal-nil)\n\n(define nil (make-mal-nil))\n\n(define (_nil? obj) (mal-nil? obj))\n\n(define (cond-true? obj)\n  (and (not (_nil? obj)) obj))\n\n(define-record-type atom (fields (mutable val)))\n\n(define-record-type callable\n  (fields\n   meta-info\n   (mutable unbox)\n   (mutable is_macro)\n   closure))\n\n(define (make-func closure) (make-callable nil #t #f closure))\n(define (make-anonymous-func closure) (make-callable nil #f #f closure))\n\n(define (callable-apply c arglst)\n  (apply (callable-closure c) (if (callable-unbox c) (map unbox arglst) arglst)))\n\n(define (callable-check c b)\n  (and (callable? c)\n       (eq? (callable-is_macro c) b)\n       c))\n\n(define (is-func c) (callable-check c #f))\n(define (is-func? c) (and (is-func c) #t))\n(define (is-macro c) (callable-check c #t))\n(define (is-macro? c) (and (is-macro c) #t))\n(define (callable-as-macro c)\n  (make-callable nil (callable-unbox c) #t (callable-closure c)))\n\n(define (hash-table-clone ht)\n  (list->hash-map (hash-fold (lambda (k v p) (cons k (cons v p))) '() ht)))\n\n(define-record-type box (fields val))\n\n(define (box o) (make-box o))\n(define (unbox o)\n  (if (box? o) (box-val o) o))\n\n(define* (list->hash-map lst #:optional (ht (make-hash-table)))\n  (cond\n   ((null? lst) ht)\n   (else\n    (let lp((next lst))\n      (cond\n       ((null? next) ht)\n       (else\n        (when (null? (cdr next))\n          (throw 'mal-error\n                 (format #f \"hash-map: '~a' lack of value\" (car next))))\n        (let ((k (car next))\n              (v (cadr next)))\n          (hash-set! ht k v)\n          (lp (cddr next)))))))))\n"
  },
  {
    "path": "impls/hare/.gitignore",
    "content": "!mal"
  },
  {
    "path": "impls/hare/Dockerfile",
    "content": "FROM debian:testing\nMAINTAINER Lou Woell <lou@reptitions.de>\nLABEL org.opencontainers.image.source=https://github.com/kanaka/mal\nLABEL org.opencontainers.image.description=\"mal test container: hare\"\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nENV HARECACHE='/mal/.cache/hare/'\nRUN apt-get -y install binutils hare"
  },
  {
    "path": "impls/hare/makefile",
    "content": "CC= hare build\n\nBINS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\n\n.PHONY: all\nall: $(BINS)\n\n%: %.ha $(wildcard mal/*.ha)\n\t$(CC) -o $@ $<\n\n.PHONY: clean\nclean:\n\trm $(BINS)\n"
  },
  {
    "path": "impls/hare/mal/core.ha",
    "content": "use fmt;\nuse os;\nuse io;\nuse memio;\nuse strings;\nuse bufio;\nuse time;\n\nexport type ns_entry = (str, (MalType | *fn([]MalType) (MalType | error)));\nexport type namespace = []ns_entry;\n\nexport fn load_namespace(ns: namespace, env: *env) (void | error) = {\n\tfor(let e.. ns){\n\t\tlet v: MalType = match(e.1){\n\t\tcase let v: MalType =>\n\t\t\tyield v;\n\t\tcase let f: *fn([]MalType) (MalType | error) =>\n\t\t\tyield make_intrinsic(f);\n\t\tcase =>\n\t\t\treturn (\"MalType\", nil): type_error;\n\t\t};\n\t\tenv_set(env, e.0: symbol, v);\n\t};\n};\n\nexport let core: namespace = [\n\t(\"pr\", &prn),\n\t(\"list\", &mallist),\n\t(\"count\", &count),\n\t(\"list?\", &listp),\n\t(\"empty?\", &emptyp),\n\t(\"not\", &not),\n\t(\"+\", &plus),\n\t(\"-\", &minus),\n\t(\"*\", &mult),\n\t(\"/\", &div),\n\t(\">\", &greater_than),\n\t(\"<\", &smaller_than),\n\t(\">=\", &greq_than),\n\t(\"<=\", &seq_than),\n\t(\"=\", &mal_eq),\n\t(\"prn\", &prn),\n\t(\"println\", &prn_line),\n\t(\"pr-str\", &pr_str),\n\t(\"str\", &pr_str_ugly),\n\t(\"read-string\", &r_string),\n\t(\"slurp\", &slurp),\n\t(\"atom\", &mal_atom),\n\t(\"atom?\", &atomp),\n\t(\"deref\", &atom_deref),\n\t(\"reset!\", &atom_reset),\n\t(\"swap!\", &atom_swap),\n\t(\"cons\", &cons),\n\t(\"concat\", &concat),\n\t(\"vec\", &vec),\n\t(\"nth\", &nth),\n\t(\"first\", &first),\n\t(\"rest\", &rest),\n\t(\"macro?\", &macrop),\n\t(\"throw\", &throw),\n\t(\"apply\", &apply),\n\t(\"map\", &map),\n\t(\"nil?\", &nilp),\n\t(\"true?\", &truep),\n\t(\"false?\", &falsep),\n\t(\"symbol?\", &symbolp),\n\t(\"map?\", &mapp),\n\t(\"vector\", &malvector),\n\t(\"vector?\", &vectorp),\n\t(\"sequential?\", &sequentialp),\n\t(\"symbol\", &malsymbol),\n\t(\"keyword?\", &keywordp),\n\t(\"keyword\", &malkeyword),\n\t(\"hash-map\", &malhash_map),\n\t(\"get\", &malhmget),\n\t(\"contains?\", &containsp),\n\t(\"assoc\", &assoc),\n\t(\"dissoc\", &dissoc),\n\t(\"vals\", &vals),\n\t(\"keys\", &keys),\n\t(\"readline\", &readline),\n\t(\"time-ms\", &time_ms),\n\t(\"string?\", &stringp),\n\t(\"number?\", &numberp),\n\t(\"seq\", &seq),\n\t(\"conj\", &conj),\n\t(\"meta\", &meta),\n\t(\"with-meta\", &with_meta),\n\t(\"fn?\", &fnp),\n];\n\nexport fn plus (args: []MalType) (MalType | error) = {\n\n\tlet result: number = 0;\n\n\tfor(let n .. args) {\n\t\tmatch(n){\n\t\tcase let n: number =>\n\t\t\tresult += n;\n\t\tcase =>\n\t\t\treturn (\"number\", args): type_error;\n\t\t};\n\t};\n\n\treturn result;\n};\n\nexport fn minus (args: []MalType) (MalType | error) = {\n\n\tlet result: number = args[0] as number;\n\n\tfor(let n .. args[1..]) {\n\t\tmatch(n){\n\t\tcase let n: number =>\n\t\t\tresult -= n;\n\t\tcase =>\n\t\t\treturn (\"number\", args): type_error;\n\t\t};\n\t};\n\n\treturn result;\n};\n\nexport fn mult (args: []MalType) (MalType | error) = {\n\n\tlet result: number = 1;\n\n\tfor(let n .. args) {\n\t\tmatch(n){\n\t\tcase let n: number =>\n\t\t\tresult *= n;\n\t\tcase =>\n\t\t\treturn (\"number\", args): type_error;\n\t\t};\n\t};\n\n\treturn result;\n};\n\n\nexport fn div (args: []MalType) (MalType | error) = {\n\n\tlet x = match(args[0]){\n\tcase let x: number =>\n\t\tyield x;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\tlet y = switch(len(args)){\n\tcase 2 =>\n\t\tyield match(args[1]){\n\t\tcase let y: number =>\n\t\t\tyield y;\n\t\tcase =>\n\t\t\treturn (\"number\", args): type_error;\n\t\t};\n\tcase 1 =>\n\t\tyield 1: number;\n\tcase 0 =>\n\t\tyield 1: number;\n\tcase =>\n\t\tyield div(args[1..])? as number;\n\t};\n\n\treturn x / y;\n};\n\nfn mallist (args: []MalType) (MalType | error) = {\n\treturn make_list(len(args), args);\n};\n\nfn listp (args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'listp': Too few arguments\", args): syntax_error;\n\n\treturn args[0] is list;\n};\n\nfn emptyp (args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'emptyp': Too few arguments\", args): syntax_error;\n\n\tlet a: []MalType = match(args[0]){\n\tcase let a: vector =>\n\t\tyield a.data;\n\tcase let a: list =>\n\t\tyield a.data;\n\tcase => return nil;\n\t};\n\n\treturn len(a) == 0;\n};\n\nfn count (args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'count': Too few arguments\", args): syntax_error;\n\n\tconst arg: []MalType = match(args[0]) {\n\tcase let a: list =>\n\t\tyield a.data;\n\tcase let a: vector =>\n\t\tyield a.data;\n\tcase nil =>\n\t\treturn 0;\n\tcase =>\n\t\treturn (\"list\", args): type_error;\n\t};\n\treturn len(arg): number;\n};\n\nfn greater_than (args: []MalType) (MalType | error) = {\n\n\tif(len(args) != 2)\n\t\treturn (\"> expected exactly 2 args, got:\", args): syntax_error;\n\n\tconst x = match(args[0]){\n\tcase let x: number =>\n\t\tyield x;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\tconst y = match(args[1]){\n\tcase let y: number =>\n\t\tyield y;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\treturn x > y;\n};\n\nfn smaller_than (args: []MalType) (MalType | error) = {\n\n\tif(len(args) != 2)\n\t\treturn (\"< expected exactly 2 args, got:\", args): syntax_error;\n\n\tconst x = match(args[0]){\n\tcase let x: number =>\n\t\tyield x;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\tconst y = match(args[1]){\n\tcase let y: number =>\n\t\tyield y;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\treturn x < y;\n};\n\n\nfn greq_than (args: []MalType) (MalType | error) = {\n\n\tif(len(args) != 2)\n\t\treturn (\">= expected exactly 2 args, got:\", args): syntax_error;\n\n\tconst x = match(args[0]){\n\tcase let x: number =>\n\t\tyield x;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\tconst y = match(args[1]){\n\tcase let y: number =>\n\t\tyield y;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\treturn x >= y;\n};\n\nfn seq_than (args: []MalType) (MalType | error) = {\n\tif(len(args) != 2)\n\t\treturn (\"<= expected exactly 2 args, got:\", args): syntax_error;\n\n\tconst x = match(args[0]){\n\tcase let x: number =>\n\t\tyield x;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\tconst y = match(args[1]){\n\tcase let y: number =>\n\t\tyield y;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\treturn x <= y;\n};\n\nfn list_cmp (ls: []MalType, ls2: []MalType) bool = {\n\tif(!(len(ls) == len(ls2)))\n\t\treturn false;\n\n\tfor(let i: size = 0; i < len(ls); i += 1){\n\t\tif(!(mal_eq(([ls[i], ls2[i]]: []MalType)) as bool)){\n\t\t\treturn false;\n\t\t};\n\t};\n\treturn true;\n};\n\nfn mal_eq (args: []MalType) (MalType | error) = {\n\n\tif(len(args) != 2)\n\t\treturn (\"'=': expected exactly 2 args, got:\", args):\n\t\tsyntax_error;\n\n\tmatch(args[0]){\n\tcase let x: number =>\n\t\tif(args[1] is number) {\n\t\t\treturn x == args[1] as number;\n\t\t};\n\tcase let x: bool =>\n\t\tif(args[1] is bool) {\n\t\t\treturn x == args[1] as bool;\n\t\t};\n\tcase let x: list =>\n\t\tmatch(args[1]){\n\t\tcase let y: vector =>\n\t\t\treturn list_cmp(x.data, y.data);\n\t\tcase let y: list =>\n\t\t\treturn list_cmp(x.data, y.data);\n\t\tcase => void;\n\t\t};\n\tcase let x: vector =>\n\t\tmatch(args[1]){\n\t\tcase let y: vector =>\n\t\t\treturn list_cmp(x.data, y.data);\n\t\tcase let y: list =>\n\t\t\treturn list_cmp(x.data, y.data);\n\t\tcase => void;\n\t\t};\n\tcase let x: nil =>\n\t\tif(args[1] is nil) {\n\t\t\treturn true;\n\t\t};\n\tcase let x: string =>\n\t\tmatch(args[1]){\n\t\tcase let y: string =>\n\t\t     return x.data == y.data;\n\t\tcase => void;\n\t\t};\n\tcase let s: symbol =>\n\t\tif(args[1] is symbol){\n\t\t\treturn s == args[1] as symbol;\n\t\t};\n\tcase let hm: hashmap =>\n\t\tif(args[1] is hashmap){\n\t\t\treturn hash_cmp(hm, args[1] as hashmap);\n\t\t};\n\tcase => void;\n\t};\n\treturn false;\n};\n\nfn not (args: []MalType) (MalType | error) = {\n\tif(len(args) == 0)\n\t\treturn (\"'not': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let b: bool =>\n\t\treturn !b;\n\tcase nil =>\n\t\treturn true;\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn prn (args: []MalType) (MalType | error) = {\n\n\tfor(let i: size = 0; i < len(args); i += 1) {\n\t\tprint_form(os::stdout, args[i]);\n\t\tif (i < len(args) - 1)\n\t\t\tfmt::fprint(os::stdout, \" \")!;\n\t};\n\tfmt::fprint(os::stdout, \"\\n\")!;\n\treturn nil;\n};\n\nfn prn_line (args: []MalType) (MalType | error) = {\n\n\tfor(let i: size = 0; i < len(args); i += 1) {\n\t\tprint_form(os::stdout, args[i], false);\n\t\tif (i < len(args) - 1)\n\t\t\tfmt::fprint(os::stdout, \" \")!;\n\t};\n\tfmt::fprint(os::stdout, \"\\n\")!;\n\treturn nil;\n};\n\nfn pr_str(args: []MalType) (MalType | error) = {\n\n\tlet strbuf = memio::dynamic();\n\tdefer io::close(&strbuf)!;\n\tfor(let i: size = 0; i < len(args); i += 1) {\n\t\tprint_form(&strbuf, args[i]);\n\t\tif (i < len(args) - 1)\n\t\t\tfmt::fprint(&strbuf, \" \")!;\n\t};\n\n\tlet s: str = memio::string(&strbuf)!;\n\treturn make_string(s);\n};\n\n\nfn pr_str_ugly(args: []MalType) (MalType | error) = {\n\n\tlet strbuf = memio::dynamic();\n\tdefer io::close(&strbuf)!;\n\n\tfor(let i: size = 0; i < len(args); i += 1) {\n\t\tprint_form(&strbuf, args[i], false);\n\t};\n\n\tlet s: str = memio::string(&strbuf)!;\n\treturn make_string(s);\n};\n\nfn r_string(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'read-string': too few arguments\", args): syntax_error;\n\n\tlet input: str = match(args[0]){\n\tcase let s: string =>\n\t\tyield s.data;\n\tcase =>\n\t\treturn (\"string\", args[0]): type_error;\n\t};\n\n\tmatch(read_str(strings::toutf8(input))) {\n\tcase io::EOF =>\n\t\treturn unexpected_eof;\n\tcase let res: (MalType | error) =>\n\t\treturn res;\n\t};\n};\n\nfn slurp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'slurp': too few arguments\", args): syntax_error;\n\n\tlet file_name: str = match(args[0]) {\n\tcase let s: string =>\n\t\tyield s.data;\n\tcase =>\n\t\treturn (\"string\", args[0]): type_error;\n\t};\n\n\tlet file = os::open(file_name)?;\n\tlet fcontent = io::drain(file)?;\n\tio::close(file)?;\n\n\tlet s: str = strings::fromutf8(fcontent)!;\n\treturn make_string(s);\n};\n\nfn mal_atom (args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'atom': too few arguments\", args): syntax_error;\n\n\treturn make_atom(args[0]);\n};\n\nfn atomp  (args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'atomp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is atom;\n};\n\nfn atom_deref (args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'deref': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let a: atom =>\n\t\treturn *a;\n\tcase =>\n\t\treturn (\"atom\", args[0]): type_error;\n\t};\n};\n\nfn atom_reset (args: []MalType) (MalType | error) ={\n\n\tif(len(args) < 2)\n\t\treturn (\"'reset': too few arguments\", args): syntax_error;\n\n\tlet a: atom = match(args[0]){\n\tcase let a: atom =>\n\t\tyield a;\n\tcase =>\n\t\treturn (\"atom\", args[0]): type_error;\n\t};\n\n\tlet v: MalType = match(args[1]){\n\tcase let v: MalType =>\n\t\tyield v;\n\tcase =>\n\t\treturn (\"atom\", args[0]): type_error;\n\t};\n\n\t*a = v;\n\treturn v;\n};\n\nfn atom_swap (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'swap': too few arguments\", args): syntax_error;\n\n\tlet a: atom = match(args[0]){\n\tcase let a: atom =>\n\t\tyield a;\n\tcase =>\n\t\treturn (\"atom\", args[0]): type_error;\n\t};\n\n\n\tlet func = match(args[1]){\n\tcase let f: (function | intrinsic) =>\n\t\tyield f;\n\tcase =>\n\t\treturn (\"function\", args[1]): type_error;\n\t};\n\n\tlet appls: list = make_list(len(args[1..]), args[1..]);\n\tappls.data[0] = *a;\n\n\t*a = apply([func, appls])?;\n\n\treturn *a;\n};\n\nfn cons(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'cons': too few arguments\", args): syntax_error;\n\n\tlet ls: []MalType = match(args[1]){\n\n\tcase let ls: list =>\n\t\tyield ls.data;\n\tcase let ls: vector =>\n\t\tyield ls.data;\n\tcase =>\n\t\treturn(\"list\", args[1]): type_error;\n\t};\n\n\tlet new: list = make_list(len(ls)+1);\n\tnew.data[0] = args[0];\n\tnew.data[1..] = ls;\n\treturn new;\n};\n\nfn concat(args: []MalType) (MalType | error) = {\n\n\tlet length: size = 0;\n\tfor(let i: size = 0; i < len(args); i += 1){\n\t\tmatch(args[i]){\n\t\tcase let ls: list =>\n\t\t\tlength += len(ls.data);\n\t\tcase let ls: vector =>\n\t\t\tlength += len(ls.data);\n\t\tcase =>\n\t\t\treturn(\"list\", args[1]): type_error;\n\t\t};\n\t};\n\n\tif(length == 0) return make_list(0);\n\n\tlet new: list = make_list(length);\n\tlet nlen: size = 0;\n\tfor(let i: size = 0; i < len(args); i += 1){\n\t\tlet ls: []MalType = match(args[i]){\n\t\tcase let ls: list =>\n\t\t\tyield ls.data;\n\t\tcase let ls: vector =>\n\t\t\tyield ls.data;\n\t\t};\n\n\t\tconst n = nlen + len(ls);\n\t\tnew.data[nlen..n] = ls;\n\t\tnlen = n;\n\t};\n\treturn new;\n};\n\nfn vec(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'vec': too few arguments\", args): syntax_error;\n\n\tlet ls: []MalType = match(args[0]){\n\t\tcase let ls: vector =>\n\t\t\treturn ls;\n\t\tcase let ls: list =>\n\t\t\tyield ls.data;\n\t\tcase =>\n\t\t\treturn (\"list or vector\",\n\t\t\t\targs[0]): type_error;\n\t};\n\n\tlet new: vector = make_vec(len(ls));\n\n\tif(len(ls) > 0){\n\t\tnew.data[0..] = ls;\n\t};\n\n\treturn new;\n};\n\nfn nth(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'nth': too few arguments\", args): syntax_error;\n\n\tlet ls: []MalType = match(args[0]){\n\tcase let ls: list =>\n\t\tyield ls.data;\n\tcase let ls: vector =>\n\t\tyield ls.data;\n\tcase =>\n\t\treturn (\"list\", args): type_error;\n\t};\n\n\tlet index: number = match(args[1]){\n\tcase let i: number =>\n\t\tyield i;\n\tcase =>\n\t\treturn (\"number\", args): type_error;\n\t};\n\n\tif(index >= len(ls): int)\n\t\treturn (\"bounds error\", args): syntax_error;\n\n\treturn ls[index];\n};\n\nfn first(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'first': too few arguments\", args): syntax_error;\n\n\tlet ls: []MalType = match(args[0]){\n\tcase let ls: list =>\n\t\tyield ls.data;\n\tcase let ls: vector =>\n\t\tyield ls.data;\n\tcase let ls: nil =>\n\t\treturn nil;\n\tcase =>\n\t\treturn (\"list\", args): type_error;\n\t};\n\n\tif(0 == len(ls)) return nil;\n\n\treturn ls[0];\n};\n\nfn rest(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'rest': too few arguments\", args): syntax_error;\n\n\tlet ls: []MalType = match(args[0]){\n\tcase let ls: list =>\n\t\tyield ls.data;\n\tcase let ls: vector =>\n\t\tyield ls.data;\n\tcase let ls: nil =>\n\t\treturn make_list(0);\n\tcase =>\n\t\treturn (\"list\", args): type_error;\n\t};\n\n\tif(0 == len(ls) || 0 == len(ls[1..]))\n\t\treturn make_list(0);\n\n\treturn make_list(len(ls[1..]), ls[1..]);\n};\n\nfn macrop(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'macrop': too few arguments\", args): syntax_error;\n\n\treturn args[0] is macro;\n};\n\nfn throw(args: []MalType) (MalType | error) ={\n\n\tif(len(args) == 0)\n\t\treturn (\"'throw': too few arguments\", args): syntax_error;\n\n\treturn (\"error\", args[0]): malerror;\n};\n\nfn map(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'map': too few arguments\", args): syntax_error;\n\n\tconst ls: []MalType = match(args[1]){\n\tcase let l: list =>\n\t\tyield l.data;\n\tcase let l: vector =>\n\t\tyield l.data;\n\tcase =>\n\t\treturn (\"list\", args): type_error;\n\t};\n\n\tconst length = len(ls);\n\n\tconst new = make_list(length);\n\n\n\tfor(let i: size = 0; i < len(ls); i += 1){\n\n\t\tlet argls: []MalType = [ls[i]];\n\t\tnew.data[i] = apply([args[0], &argls: list])?;\n\t};\n\n\treturn new;\n};\n\nfn apply(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'apply': too few arguments\", args): syntax_error;\n\n\tconst last = args[len(args)-1];\n\tconst rest = args[1..len(args)-1];\n\n\tconst last: []MalType = match(args[len(args)-1]){\n\tcase let l: list =>\n\t\tyield l.data;\n\tcase let l: vector =>\n\t\tyield l.data;\n\tcase =>\n\t\treturn (\"list\", args): type_error;\n\t};\n\n\tconst length: size = len(rest) + len(last);\n\n\tconst ls: []MalType = switch(length){\n\tcase 0 =>\n\t\tyield [];\n\tcase =>\n\t\tyield alloc([nil...], length)!;\n\t};\n\tdefer free(ls);\n\n\tls[0 .. len(rest)] = rest;\n\tls[len(rest)..] = last;\n\n\tmatch(args[0]){\n\tcase let func: function =>\n\t\tlet env = env_init(func.envi);\n\t\tenv_bind(env, func.args, ls);\n\t\treturn func.eval(func.body, env);\n\tcase let func: macro =>\n\t\tlet env = env_init(func.envi);\n\t\tenv_bind(env, func.args, ls);\n\t\treturn func.eval(func.body, env);\n\tcase let f: intrinsic =>\n\t\treturn f.eval(ls);\n\tcase =>\n\t\treturn (\"function\", args): type_error;\n\t};\n};\n\nfn nilp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'nilp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is nil;\n};\n\nfn symbolp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'symbolp': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let s: symbol =>\n\t\tif (!(strings::hasprefix(s, \":\")))\n\t\t\treturn true;\n\tcase => void;\n\t};\n\treturn false;\n};\n\nfn keywordp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'keywordp': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let s: symbol =>\n\t\tif (strings::hasprefix(s, \":\"))\n\t\t\treturn true;\n\tcase => void;\n\t};\n\treturn false;\n};\n\nfn vectorp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'vectorp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is vector;\n};\n\nfn sequentialp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'sequentialp': too few arguments\", args):\n\t\t\tsyntax_error;\n\n\treturn args[0] is (list | vector);\n};\n\nfn mapp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'mapp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is hashmap;\n};\n\nfn truep(args: []MalType) (MalType | error) = {\n\tmatch(args[0]){\n\tcase let b: bool =>\n\t\treturn b;\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn falsep(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'falsep': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let b: bool =>\n\t\treturn !b;\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn malvector(args: []MalType) (MalType | error) = {\n\treturn make_vec(len(args), args);\n};\n\nfn malsymbol(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'symbol': too few arguments\", args): syntax_error;\n\n\tlet s: str = match(args[0]){\n\tcase let s: string =>\n\t\tyield s.data;\n\tcase =>\n\t\treturn (\"string\", args): type_error;\n\t};\n\treturn make_symbol(s);\n};\n\nfn malkeyword(args: []MalType) (MalType | error) = {\n\n\tif(len(args) == 0)\n\t\treturn (\"'keyword': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let s: string =>\n\t\tlet name = strings::lpad(s.data, ':', len(s.data) + 1)!;\n\t\tdefer free(name);\n\t\treturn make_symbol(name);\n\tcase let k: symbol =>\n\t\tif(strings::hasprefix(k, ':'))\n\t\t\treturn k;\n\t\treturn false;\n\tcase =>\n\t\treturn (\"string\", args): type_error;\n\t};\n};\n\nfn malhash_map(args: []MalType) (MalType | error) = {\n\n\tlet new = hm_init();\n\n\tif (len(args) % 2 != 0)\n\t\treturn (\"odd number of arguments\", args): syntax_error;\n\n\tfor(let i: size = 0; i < len(args); i += 2){\n\t\tmatch(args[i]){\n\t\tcase let s: (symbol | string) =>\n\t\t\thm_add(new, s, args[i+1]);\n\t\tcase =>\n\t\t\treturn (\"symbol or string\",\n\t\t\t\targs): type_error;\n\t\t};\n\t};\n\n\treturn new;\n};\n\nfn malhmget(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'get': too few arguments\", args): syntax_error;\n\n\tconst hm = match(args[0]){\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\tcase nil => return nil;\n\tcase =>\n\t\treturn (\"hashmap\", args): type_error;\n\t};\n\n\tconst key = match(args[1]){\n\tcase let hm: (string | symbol) =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"symbol or string\", args): type_error;\n\t};\n\n\tmatch (hm_get(hm, key)){\n\tcase let e: undefined_key =>\n\t\treturn nil;\n\tcase let v: MalType =>\n\t\treturn v;\n\t};\n};\n\nfn containsp(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 2)\n\t\treturn (\"'containsp': too few arguments\", args): syntax_error;\n\n\tconst hm = match(args[0]){\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"hashmap\", args): type_error;\n\t};\n\n\tconst key = match(args[1]){\n\tcase let hm: (string | symbol) =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"symbol or string\", args): type_error;\n\t};\n\n\tmatch(hm_get(hm, key)){\n\tcase undefined_key => return false;\n\tcase => return true;\n\t};\n};\n\nfn assoc(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'assoc': too few arguments\", args): syntax_error;\n\n\tlet hm: hashmap = match(args[0]){\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"hashmap\", args): type_error;\n\t};\n\n\tlet new: hashmap = hm_copy(hm);\n\n\tassert(len(hm.data) == len(new.data));\n\n\tlet ls = args[1..];\n\tfor(let i: size = 0; i < len(ls); i += 2){\n\t\tmatch(ls[i]){\n\t\tcase let s: (symbol | string) =>\n\t\t\thm_set(new, s, ls[i+1]);\n\t\tcase =>\n\t\t\treturn (\"symbol or string\",\n\t\t\t\targs): type_error;\n\t\t};\n\t};\n\n\treturn new;\n};\n\nfn dissoc(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'dissoc': too few arguments\", args): syntax_error;\n\n\tlet hm: hashmap = match(args[0]){\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"hashmap\", args): type_error;\n\t};\n\n\tlet ls = args[1..];\n\tlet new: hashmap = hm_copy(hm, ls: [](string | symbol));\n\n\treturn new;\n};\n\nfn vals(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'vals': too few arguments\", args): syntax_error;\n\n\tlet hm: hashmap = match(args[0]){\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"hashmap\", args): type_error;\n\t};\n\n\treturn hm_val_list(hm);\n};\n\nfn keys(args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'keys': too few arguments\", args): syntax_error;\n\n\tlet hm: hashmap = match(args[0]){\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\tcase =>\n\t\treturn (\"hashmap\", args): type_error;\n\t};\n\n\treturn hm_key_list(hm);\n};\n\nfn readline (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'readline': too few arguments\", args): syntax_error;\n\n\tconst prompt: str = match(args[0]){\n\tcase let p: string =>\n\t\tyield p.data;\n\tcase =>\n\t\treturn (\"string\", args): type_error;\n\t};\n\n\tfmt::printf(prompt)!;\n\tbufio::flush(os::stdout)!;\n\n\tconst input =  match(bufio::read_line(os::stdin)){\n\tcase let input: []u8 =>\n\t\tyield input;\n\tcase io::EOF =>\n\t\treturn nil;\n\tcase let e: io::error =>\n\t\treturn e;\n\t};\n\n\tconst s = strings::fromutf8(input)!;\n\tconst ret = make_string(s);\n\tfree(input);\n\treturn ret;\n};\n\nfn time_ms (args: []MalType) (MalType | error) = {\n\tlet now = time::now(time::clock::REALTIME);\n\tlet base = time::instant{sec = 0, ...};\n\tlet diff = time::diff(base, now) / time::MILLISECOND;\n\treturn diff: number;\n};\n\nfn stringp (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'stringp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is string;\n};\n\nfn numberp (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'numberp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is number;\n};\n\nfn fnp (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'fnp': too few arguments\", args): syntax_error;\n\n\treturn args[0] is (function | intrinsic);\n};\n\nfn seq (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'seq': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let s: string =>\n\t\tif(len(s.data) == 0) return nil;\n\n\t\tlet new = make_list(len(s.data));\n\n\t\tlet it = strings::iter(s.data);\n\n\t\tfor(let i: size = 0; i < len(s.data); i += 1){\n\t\t\tmatch(strings::next(&it)){\n\t\t\tcase let rn: rune =>\n\t\t\t\tlet s: str = strings::fromutf8([rn: u8])!;\n\t\t\t\tnew.data[i] = make_string(s);\n\t\t\tcase =>\n\t\t\t\tbreak;\n\t\t\t};\n\t\t};\n\n\t\treturn new;\n\tcase let s: list =>\n\t\tif(len(s.data) == 0) return nil;\n\t\treturn s;\n\tcase let s: vector =>\n\t\tif(len(s.data) == 0) return nil;\n\t\treturn make_list(len(s.data), s.data);\n\tcase let s: nil =>\n\t\treturn nil;\n\t};\n\n};\n\nfn conj (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'conj': too few arguments\", args): syntax_error;\n\n\tlet old = args[1..];\n\tlet length = len(old);\n\n\tmatch(args[0]){\n\tcase let ls: list =>\n\t\tlength += len(ls.data);\n\t\tlet new = make_list(length);\n\t\tnew.data[len(old)..] = ls.data;\n\n\t\tfor(let i: size = len(old); i > 0; i -= 1){\n\t\t\tnew.data[i-1] = old[len(old) - i];\n\t\t};\n\t\treturn new;\n\tcase let ls: vector =>\n\t\tlength += len(ls.data);\n\t\tlet new = make_vec(length, ls.data);\n\t\tnew.data[len(ls.data)..] = old;\n\t\treturn new;\n\tcase => return (\"list or vector\", args): type_error;\n\t};\n};\n\nfn meta (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'meta': too few arguments\", args): syntax_error;\n\n\tmatch(args[0]){\n\tcase let func: function =>\n\t\treturn func.meta;\n\tcase let func: intrinsic =>\n\t\treturn func.meta;\n\tcase let hm: hashmap =>\n\t\treturn hm.meta;\n\tcase let s: string =>\n\t\treturn s.meta;\n\tcase let l: list =>\n\t\treturn l.meta;\n\tcase let v: vector =>\n\t\treturn v.meta;\n\tcase =>\n\t\treturn not_implemented;\n\t};\n};\n\nfn with_meta (args: []MalType) (MalType | error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'with-meta': too few arguments\", args):\n\t\t\tsyntax_error;\n\n\tmatch(args[0]){\n\tcase let func: function =>\n\t\tlet new = make_func(func.eval, func.envi, func.args,\n\t\t\tfunc.body);\n\t\tnew.meta = args[1];\n\t\treturn new;\n\tcase let hm: hashmap =>\n\t\tlet new = assoc([hm])?:hashmap;\n\t\tnew.meta = args[1];\n\t\treturn new;\n\tcase let s: string =>\n\t\tlet new = make_string(s.data);\n\t\tnew.meta = args[1];\n\t\treturn new;\n\tcase let f: intrinsic =>\n\t\tlet new = make_intrinsic(f.eval);\n\t\tnew.meta = args[1];\n\t\treturn new;\n\tcase let ls: list =>\n\t\tlet new: list = make_list(len(ls.data), ls.data);\n\t\tnew.meta = args[1];\n\t\treturn new;\n\tcase let v: vector =>\n\t\tlet new: vector = make_vec(len(v.data), v.data);\n\t\tnew.meta = args[1];\n\t\treturn new;\n\tcase =>\n\t\treturn not_implemented;\n\t};\n};\n"
  },
  {
    "path": "impls/hare/mal/env.ha",
    "content": "export type env = struct {\n\touter: nullable *env,\n\tdata: hashmap,\n};\n\nexport fn env_init(outer: nullable * env = null) *env ={\n\tconst new = alloc(env {\n\t\touter = outer,\n\t\tdata = hm_init(),\n\t})!;\n\n\tappend(gc.memory.envs, new)!;\n\treturn new;\n};\n\nexport fn env_bind(\n\tenv: *env,\n\tbindings: []MalType,\n\texprs: []MalType\n) void = {\n\n\tlet more: bool = false;\n\tfor(let i: size = 0; i < len(bindings); i += 1){\n\t\tif (!(bindings[i] is symbol)){\n\t\t\treturn void;\n\t\t};\n\t\tif (more) {\n\t\t\tlet tail = exprs[i - 1..];\n\t\t\tlet new = make_list(len(tail), tail);\n\t\t\tenv_set(env, bindings[i] as symbol, new);\n\t\t\tbreak;\n\t\t} else if (bindings[i] as symbol == \"&\": symbol){\n\t\t\tmore = true;\n\t\t\tcontinue;\n\t\t} else {\n\t\t\tenv_set(env, bindings[i] as symbol, exprs[i]);\n\t\t};\n\t};\n};\n\n\nexport fn env_set(env: *env, key: symbol, val: MalType) void = {\n\thm_set(env.data, key, val);\n\treturn void;\n};\n\nexport fn env_get(envi: *env, key: symbol) (MalType | undefined_symbol) = {\n\n\tmatch(hm_get(envi.data, key)) {\n\tcase undefined_key =>\n\t\tmatch(envi.outer){\n\t\tcase null =>\n\t\t\treturn (\"env_get\", key): undefined_symbol;\n\t\tcase let outer: *env =>\n\t\t\treturn env_get(outer, key);\n\t\t};\n\tcase let result: MalType =>\n\t\treturn result;\n\t};\n};\n"
  },
  {
    "path": "impls/hare/mal/error.ha",
    "content": "use bufio;\nuse io;\nuse fmt;\nuse os;\nuse fs;\n\nexport type malerror = !(str, MalType);\n\nexport type not_implemented = !void;\nexport type unexpected_eof = !void;\nexport type unbalanced = !void;\n\nexport type undefined_key = !(str, (symbol | string));\nexport type undefined_symbol = !(str, symbol);\nexport type syntax_error = !(str, (MalType | []MalType));\nexport type type_error = !(str, (MalType | []MalType));\n\nexport type error = !(malerror | fs::error | io::error | unexpected_eof |\n\tunbalanced | not_implemented | undefined_symbol | undefined_key |\n\tsyntax_error | type_error);\n\nexport fn format_error(strbuf: io::handle, e: error) void = {\n\n\tmatch(e){\n\tcase let e: type_error =>\n\t\tfmt::fprint(strbuf, \"Type Error: expected\", e.0, \"got:\")!;\n\t\tprint_form(strbuf, e.1, false);\n\t\tfmt::fprint(strbuf, \"\\n\")!;\n\tcase let e: syntax_error =>\n\t\tfmt::fprintln(strbuf, \"Syntax Error:\", e.0)!;\n\t\tprint_form(strbuf, e.1, false);\n\t\tfmt::fprint(strbuf, \"\\n\")!;\n\tcase let e: undefined_symbol =>\n\t\tfmt::fprintf(strbuf, \"'{}' not found\", e.1)!;\n\t\tfmt::print(\"\\n\")!;\n\tcase unexpected_eof =>\n\t\tfmt::fprintln(strbuf, \"Unexpected EOF!\")!;\n\tcase let e: malerror =>\n\t\tprint_form(strbuf, e.1, false);\n\t\tfmt::print(\"\\n\")!;\n\tcase unbalanced =>\n\t\tfmt::fprintln(strbuf, \"Unbalanced Delimiters\")!;\n\tcase not_implemented =>\n\t\tfmt::fprintln(strbuf, \"not implemented\")!;\n\tcase let e: io::error =>\n\t\tfmt::fprintln(strbuf, io::strerror(e))!;\n\tcase let e: fs::error =>\n\t\tfmt::fprintln(strbuf, fs::strerror(e))!;\n\tcase =>\n\t\tfmt::fatal(\"unknown error\");\n\t};\n};\n"
  },
  {
    "path": "impls/hare/mal/gc.ha",
    "content": "// Some inspirations taken from https://git.sr.ht/~jummit/rekkyo\n\ntype memory = struct {\n\tenvs: []*env,\n\thashs: []hashmap,\n\tsymbols: (void | hashmap),\n\tfuncs: []function,\n\tlists: []list,\n\tvecs: []vector,\n\tstrings: []string,\n\tatoms: []atom,\n\tintrinsics: []intrinsic,\n};\n\ntype garbage_collector = struct {\n\tmarked: memory,\n\tmemory: memory,\n};\n\nlet gc = garbage_collector {\n\tmarked = memory {\n\t\tsymbols = void,\n\t\tfuncs = [],\n\t\t...\n\t},\n\tmemory = memory {\n\t\tsymbols = void,\n\t\tfuncs = [],\n\t\t...\n\t},\n};\n\nfn reset_memory(memory: *memory) void = {\n\n\tmemory.envs = memory.envs[..0];\n\tmemory.hashs = memory.hashs[..0];\n\tmemory.funcs = memory.funcs[..0];\n\tmemory.lists = memory.lists[..0];\n\tmemory.vecs = memory.vecs[..0];\n\tmemory.strings = memory.strings[..0];\n\tmemory.atoms = memory.atoms[..0];\n\tmemory.intrinsics = memory.intrinsics[..0];\n\n\tmatch(memory.symbols){\n\tcase let hm: hashmap =>\n\t\thm.data = hm.data[..0];\n\tcase void =>\n\t\tvoid;\n\t};\n};\n\nfn finish_memory(memory: memory) void = {\n\n\tfree(memory.envs);\n\tfree(memory.hashs);\n\tfree(memory.funcs);\n\tfree(memory.lists);\n\tfree(memory.vecs);\n\tfree(memory.strings);\n\tfree(memory.atoms);\n\tfree(memory.intrinsics);\n\n\tmatch(memory.symbols){\n\tcase let hm: hashmap =>\n\t\thm_free(hm);\n\tcase void =>\n\t\tvoid;\n\t};\n};\n\nfn mark_hash(hm: hashmap) void = {\n\n\tappend(gc.marked.hashs, hm)!;\n\tmark(hm.meta);\n\n\tfor(let v .. hm.data){\n\t\tmark(v.key);\n\t\tmark(v.val);\n\t};\n};\n\nfn mark_env(envi: *env) void = {\n\n\tfor(let e .. gc.marked.envs){\n\t\tif(e == envi) return void;\n\t};\n\n\tappend(gc.marked.envs, envi)!;\n\tmark(envi.data);\n\n\tmatch(envi.outer){\n\tcase null => void;\n\tcase let e: *env =>\n\t\tmark_env(e);\n\t};\n};\n\nfn mark_col(col: []MalType) void = {\n\tfor(let v .. col) {\n\t\tmark(v);\n\t};\n};\n\nfn mark (val: MalType) void = {\n\n\tmatch(gc.marked.symbols){\n\tcase void =>\n\t\tgc.marked.symbols = hm_init(false);\n\tcase => void;\n\t};\n\n\tmatch(val){\n\tcase let v: vector =>\n\t\tfor(let x .. gc.marked.vecs){\n\t\t\tif(x == v) return void;\n\t\t};\n\t\tappend(gc.marked.vecs, v)!;\n\t\tmark_col(v.data);\n\t\tmark(v.meta);\n\tcase let l: list =>\n\t\tfor(let x .. gc.marked.lists){\n\t\t\tif(x == l) return void;\n\t\t};\n\t\tappend(gc.marked.lists, l)!;\n\t\tmark_col(l.data);\n\t\tmark(l.meta);\n\tcase  let f: function =>\n\t\tfor(let x .. gc.marked.funcs){\n\t\t\tif(x == f) return void;\n\t\t};\n\t\tappend(gc.marked.funcs, f)!;\n\t\tmark(f.meta);\n\t\tmark(f.body);\n\t\tmark_col(f.args);\n\t\tmark_env(f.envi);\n\tcase let i: intrinsic =>\n\t\tfor(let x .. gc.marked.intrinsics){\n\t\t\tif(x == i) return void;\n\t\t};\n\t\tappend(gc.marked.intrinsics, i)!;\n\t\tmark(i.meta);\n\tcase let m: macro =>\n\t\tlet m = m:function;\n\t\tfor(let x .. gc.marked.funcs){\n\t\t\tif(x == m) return void;\n\t\t};\n\t\tappend(gc.marked.funcs, m)!;\n\t\tmark(m.meta);\n\t\tmark(m.body);\n\t\tmark_col(m.args);\n\t\tmark_env(m.envi);\n\tcase let h: hashmap =>\n\t\tfor(let x .. gc.marked.hashs){\n\t\t\tif(x == h) return void;\n\t\t};\n\t\tmark_hash(h);\n\tcase let s: symbol =>\n\t\tmatch(hm_get(gc.marked.symbols: hashmap, s)){\n\t\tcase undefined_key =>\n\t\t\thm_add(gc.marked.symbols: hashmap, s, s);\n\t\tcase =>  void;\n\t\t};\n\tcase let s: string =>\n\t\tfor(let x .. gc.marked.strings){\n\t\t\tif(x == s) return void;\n\t\t};\n\t\tappend(gc.marked.strings, s)!;\n\t\tmark(s.meta);\n\tcase let a: atom =>\n\t\tfor(let x .. gc.marked.atoms){\n\t\t\tif(x == a) return void;\n\t\t};\n\t\tappend(gc.marked.atoms, a)!;\n\t\tmark(*a);\n\tcase => void;\n\t};\n};\n\nfn sweep() void ={\n\n\tconst marked_symbols = match(gc.marked.symbols){\n\t\tcase void =>\n\t\t     gc.marked.symbols = hm_init(false);\n\t\tyield gc.marked.symbols: hashmap;\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\t};\n\n\tconst memory_symbols = match(gc.memory.symbols){\n\tcase void =>\n\t\tgc.memory.symbols = hm_init(false);\n\t\tyield gc.memory.symbols: hashmap;\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\t};\n\n\tfor (let i: size = 0; len(memory_symbols.data) > i; i += 1) {\n\t\tmatch(hm_get(marked_symbols, memory_symbols.data[i].key)){\n\t\tcase undefined_key =>\n\t\t\tfree(memory_symbols.data[i].key: symbol);\n\t\tcase =>\n\t\t\tvoid;\n\t\t};\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.atoms) > i; i += 1) {\n\t\tfor(let x .. gc.marked.atoms){\n\t\t\tif(x == gc.memory.atoms[i]) continue :sweep;\n\t\t};\n\t\tfree(gc.memory.atoms[i]);\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.strings) > i; i += 1) {\n\t\tfor(let x .. gc.marked.strings){\n\t\t\tif(x == gc.memory.strings[i]) continue :sweep;\n\t\t};\n\t\tfree_string(gc.memory.strings[i]);\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.hashs) > i; i += 1) {\n\t\tfor(let x .. gc.marked.hashs){\n\t\t\tif(x == gc.memory.hashs[i]) continue :sweep;\n\t\t};\n\t\thm_free(gc.memory.hashs[i]);\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.envs) > i; i += 1) {\n\t\tfor(let x .. gc.marked.envs){\n\t\t\tif(x == gc.memory.envs[i]) continue :sweep;\n\t\t};\n\t\tfree(gc.memory.envs[i]); //.data is collected as a hashmap\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.vecs) > i; i += 1) {\n\t\tfor(let x .. gc.marked.vecs){\n\t\t\tif(x == gc.memory.vecs[i]) continue :sweep;\n\t\t};\n\t\tfree_vec(gc.memory.vecs[i]);\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.lists) > i; i += 1) {\n\t\tfor(let x .. gc.marked.lists){\n\t\t\tif(x == gc.memory.lists[i]) continue :sweep;\n\t\t};\n\t\tfree_list(gc.memory.lists[i]);\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.funcs) > i; i += 1) {\n\t\tfor(let x .. gc.marked.funcs){\n\t\t\tif(x == gc.memory.funcs[i]) continue :sweep;\n\t\t};\n\t\tfree_func(gc.memory.funcs[i]);\n\t};\n\tfor :sweep (let i: size = 0; len(gc.memory.intrinsics) > i; i += 1) {\n\t\tfor(let x .. gc.marked.intrinsics){\n\t\t\tif(x == gc.memory.intrinsics[i]) continue :sweep;\n\t\t};\n\t\tfree(gc.memory.intrinsics[i]);\n\t};\n\n\treset_memory(&gc.memory);\n\n\tgc = garbage_collector {\n\t\tmarked = gc.memory,\n\t\tmemory = gc.marked,\n\t};\n};\n\n// it doesn't make sense to call this with anything but the global repl_env,\n// because as of this version there's no way to keep track of objects reachable\n// through the ast of the current evaluation and it's possible continuations.\n\nexport fn run_gc(envi: *env) void = {\n\n\tmark_env(envi);\n\tsweep();\n};\n"
  },
  {
    "path": "impls/hare/mal/hashmap.ha",
    "content": "// The hashmap implmentation follows this idea:\n// https://nullprogram.com/blog/2023/09/30/\n\nuse io;\nuse fmt;\nuse hash::fnv;\n\nexport type hashmap = *struct {\n\tdata: []hmap,\n\tmeta: MalType,\n};\n\nexport type hmap = struct {\n\tkey: (symbol | string),\n\tval: MalType,\n\tchild: [4](size | void),\n};\n\ntype pos = struct {\n\texists: bool,\n\tindex: size,\n\tchild: (size | void),\n};\n\nexport fn hm_init(gcd: bool = true) hashmap = {\n\tlet new: hashmap = alloc(struct {\n\t\tdata: []hmap = [],\n\t\tmeta: MalType = nil,\n\t})!;\n\n\tif(gcd) append(gc.memory.hashs, new)!;\n\n\treturn new;\n};\n\nfn hm_free(hm: hashmap) void = {\n\tfree(hm.data);\n\tfree(hm);\n};\n\nfn new(\n\thm: hashmap,\n\tp: pos,\n\tk: (symbol | string),\n\tv: MalType\n) void = {\n\tconst new = hmap {\n\t\tkey = k,\n\t\tval = v,\n\t\tchild: [4](size | void) = [void...],\n\t};\n\n\tappend(hm.data, new)!;\n\n\tmatch(p.child) {\n\tcase void =>\n\t\treturn void;\n\tcase let i: size =>\n\t\thm.data[p.index].child[i] = len(hm.data) - 1;\n\t};\n\n};\n\nexport fn keycmp(x: (symbol | string), y: (symbol | string)) bool = {\n\n\tconst kx: str = match(x){\n\tcase let k: symbol =>\n\t\tyield k: str;\n\tcase let k: string =>\n\t\tyield k.data;\n\t};\n\n\tconst ky: str = match(y){\n\tcase let k: symbol =>\n\t\tyield k: str;\n\tcase let k: string =>\n\t\tyield k.data;\n\t};\n\n\treturn kx == ky;\n};\n\nfn hm_find(hm: hashmap, key: (symbol | string)) pos = {\n\n\tlet index: size = 0;\n\n\tconst k: str = match(key){\n\tcase let k: symbol =>\n\t\tyield k: str;\n\tcase let k: string =>\n\t\tyield k.data;\n\t};\n\n\tlet hash: u32 = fnv::string32(k);\n\n\tif(len(hm.data) == 0)\n\t\treturn pos {\n\t\t\texists = false,\n\t\t\tindex = 0,\n\t\t\tchild = void,\n\t\t};\n\n\tfor(true){\n\t\tif (keycmp(key, hm.data[index].key)){\n\t\t\treturn pos {\n\t\t\t\texists = true,\n\t\t\t\tindex = index,\n\t\t\t\tchild = void,\n\t\t\t};\n\t\t};\n\n\t\tlet c = hash >> 30;\n\n\t\tmatch(hm.data[index].child[c]){\n\t\tcase void =>\n\t\t\treturn pos {\n\t\t\t\texists = false,\n\t\t\t\tindex = index,\n\t\t\t\tchild = c,\n\t\t};\n\t\tcase let i: size =>\n\t\t\tindex = i;\n\t\t\thash <<= 2;\n\t\t\tcontinue;\n\t\t};\n\t};\n};\n\nexport fn hm_set(\n\thm: hashmap,\n\tkey: (symbol | string),\n\tval: MalType,\n) void = {\n\n\tlet p: pos = hm_find(hm, key);\n\n\tif(p.exists){\n\t\thm.data[p.index].val = val;\n\t} else {\n\t\tnew(hm, p, key, val);\n\t};\n\n};\n\nexport fn hm_add(\n\thm: hashmap,\n\tkey: (symbol | string),\n\tval: MalType,\n) void = {\n\n\tlet p: pos = hm_find(hm, key);\n\n\tif(p.exists){\n\t\treturn void;\n\t} else {\n\t\tnew(hm, p, key, val);\n\t};\n};\n\nexport fn hm_get(\n\thm: hashmap,\n\tkey: (symbol | string)\n) (MalType | error) = {\n\n\tif(len(hm.data) == 0){\n\t\treturn (\"hm_get 0\", key):undefined_key;\n\t};\n\n\tlet p: pos = hm_find(hm, key);\n\n\tif(p.exists) {\n\t\treturn hm.data[p.index].val;\n\t} else  {\n\t\treturn (\"hm_get\", key):undefined_key;\n\t};\n};\n\nfn hm_copy(hm: hashmap, filter: [](string | symbol) = []) hashmap = {\n\tconst new = hm_init();\n\n\tif(len(filter) == 0){\n\t\tfor(let e .. hm.data) {\n\t\t\tappend(new.data, e)!;\n\t\t};\n\t} else {\n\t\tfor :map (let e .. hm.data) {\n\t\t\tfor(let f .. filter) {\n\t\t\t\tif(keycmp(f, e.key))\n\t\t\t\t\tcontinue :map;\n\t\t\t};\n\n\t\t\thm_add(new, e.key, e.val);\n\t\t};\n\t};\n\n\treturn new;\n};\n\nfn hm_print(\n\tstrbuf: io::handle,\n\thm: hashmap,\n\tpp: bool,\n) void = {\n\tfor (let i: size = 0; i < len(hm.data); i += 1){\n\n\t\tlet e = hm.data[i];\n\n\t\tprint_form(strbuf, e.key, pp);\n\t\tfmt::fprint(strbuf, \" \")!;\n\t\tprint_form(strbuf, e.val, pp);\n\t\tif(!(i + 1 == len(hm.data))) fmt::fprint(strbuf, \" \")!;\n\t};\n};\n\nfn hash_cmp(hm1: hashmap, hm2: hashmap) bool = {\n\n\tif(len(hm1.data) != len(hm2.data)){\n\t\treturn false;\n\t};\n\n\tfor(let i: size = 0; i < len(hm1.data); i += 1) {\n\t\tmatch(hm_get(hm2, hm1.data[i].key)){\n\t\tcase undefined_key =>\n\t\t\treturn false;\n\t\tcase let v: MalType =>\n\t\t     if(!(mal_eq([hm1.data[i].val, v]) as bool))\n\t\t\t\treturn false;\n\t\t};\n\t};\n\n\treturn true;\n};\n\nfn hm_val_list(hm: hashmap) list = {\n\tconst length = len(hm.data);\n\tconst new = make_list(length);\n\n\tfor(let i: size = 0; i < length; i += 1){\n\t\tnew.data[i] = hm.data[i].val;\n\t};\n\n\treturn new;\n};\n\nfn hm_key_list(hm: hashmap) list = {\n\tconst length = len(hm.data);\n\tconst new = make_list(length);\n\n\tfor(let i: size = 0; i < length; i += 1){\n\t\tnew.data[i] = hm.data[i].key;\n\t};\n\treturn new;\n};\n\nexport fn eval_hash(\n\thm: hashmap,\n\teval: *fn(MalType, *env) (MalType | error),\n\tenv: *env,\n) (hashmap | error) = {\n\n\tconst new = hm_init();\n\n\tfor(let e .. hm.data){\n\t\thm_add(new, e.key, eval(e.val, env)?);\n\t};\n\n\treturn new;\n};\n"
  },
  {
    "path": "impls/hare/mal/printer.ha",
    "content": "use io;\nuse memio;\nuse fmt;\nuse strings;\n\nexport fn print_form(\n\tstrbuf: io::handle,\n\tform: (MalType | []MalType),\n\tprint_readably: bool = true\n) void = {\n\n\tmatch(form){\n\tcase let l: list =>\n\t\tprint_list(strbuf, l.data, list_beg, print_readably);\n\tcase let v: vector =>\n\t\tprint_list(strbuf, v.data, vec_beg, print_readably);\n\tcase let c: []MalType =>\n\t\tprint_list(strbuf, c, list_beg, print_readably);\n\tcase let h: hashmap =>\n\t\tprint_hash(strbuf, h, print_readably);\n\tcase let s: string =>\n\t\tprint_string(strbuf, s, print_readably);\n\tcase nil =>\n\t\tmemio::concat(strbuf, \"nil\")!;\n\tcase let b: bool =>\n\t\tfmt::fprint(strbuf, b)!;\n\tcase let s: symbol =>\n\t\tmemio::concat(strbuf, s: str)!;\n\tcase let i: number =>\n\t\tfmt::fprint(strbuf, i: int)!;\n\tcase let a: atom =>\n\t\tprint_list(strbuf, [\"atom\": symbol, *a],\n\t\t\tlist_beg, print_readably);\n\tcase let func: (intrinsic | function) =>\n\t\tmemio::concat(strbuf, \"#<function>\")!;\n\tcase => void;\n\t};\n};\n\nfn print_string(\n\tstrbuf: io::handle,\n\ts: string,\n\tprint_readable: bool\n) void = {\n\n\tlet runes = strings::torunes(s.data)!;\n\n\n\tif(!print_readable){\n\t\tmemio::concat(strbuf, s.data)!;\n\t} else {\n\t\tmemio::appendrune(strbuf, '\"')!;\n\t\tfor(let rn .. runes){\n\t\t\tlet ret = switch (rn) {\n\t\t\tcase '\"' =>\n\t\t\t\tyield \"\\\\\\\"\";\n\t\t\tcase '\\\\' =>\n\t\t\t\tyield \"\\\\\\\\\";\n\t\t\tcase '\\b' =>\n\t\t\t\tyield \"\\\\b\";\n\t\t\tcase '\\f' =>\n\t\t\t\tyield \"\\\\f\";\n\t\t\tcase '\\n' =>\n\t\t\t\tyield \"\\\\n\";\n\t\t\tcase '\\r' =>\n\t\t\t\tyield \"\\\\r\";\n\t\t\tcase '\\t' =>\n\t\t\t\tyield \"\\\\t\";\n\t\t\tcase =>\n\t\t\t\tyield rn;\n\t\t\t};\n\n\t\t\tmatch(ret) {\n\t\t\tcase let rn: rune =>\n\t\t\t\tmemio::appendrune(strbuf, rn)!;\n\t\t\tcase let rn: str =>\n\t\t\t\tmemio::concat(strbuf, rn)!;\n\t\t\t};\n\t\t};\n\t\tmemio::appendrune(strbuf, '\"')!;\n\t};\n\n};\n\nfn print_hash(strbuf: io::handle, hm: hashmap, pp: bool) void ={\n\n\tconst open = '{';\n\tconst close = '}';\n\n\tfmt::fprint(strbuf, open)!;\n\thm_print(strbuf, hm, pp);\n\tfmt::fprint(strbuf, close)!;\n};\n\n\nfn print_list(\n\tstrbuf: io::handle,\n\tls: []MalType,\n\tt: coll_beg,\n\tpp: bool\n) void = {\n\n\tlet open = '(';\n\tlet close = ')';\n\n\tif(t is vec_beg){\n\t\topen = '[';\n\t\tclose = ']';\n\t};\n\n\tmemio::appendrune(strbuf, open)!;\n\tfor(let i: size = 0; i < len(ls); i += 1) {\n\t\tlet form = print_form(strbuf, ls[i], pp);\n\t\tif(!(i == len(ls)-1)){\n\t\t\tfmt::fprint(strbuf, \" \")!;\n\t\t};\n\t};\n\tmemio::appendrune(strbuf, close)!;\n};\n"
  },
  {
    "path": "impls/hare/mal/reader.ha",
    "content": "use io;\nuse fmt;\nuse memio;\nuse strings;\n\nexport fn read_str(input: []u8) (MalType | error | io::EOF) = {\n\n\tconst tk: tokenizer = tokenizer_init(input);\n\n\tmatch(read_form(&tk)?){\n\tcase let res: MalType =>\n\t\treturn res;\n\tcase let e: coll_end =>\n\t\treturn unbalanced;\n\tcase let res: io::EOF =>\n\t\treturn io::EOF;\n\t};\n\n};\n\nfn read_form(tk: *tokenizer) (...MalType | ...coll_end | io::EOF | error) = {\n\tfor(true){\n\t\tmatch(tokenizer_next(tk)?) {\n\t\tcase let t: coll_beg =>\n\t\t\treturn read_collection(tk, t);\n\t\tcase let t: coll_end =>\n\t\t\treturn t;\n\t\tcase let s: str =>\n\t\t\treturn read_string(s);\n\t\tcase let c: comment => void;\n\t\tcase let a: word =>\n\t\t\treturn read_symbol(a);\n\t\tcase let q: quote_tk =>\n\t\t\treturn read_quote(tk, q);\n\t\tcase let m: mal_meta =>\n\t\t\treturn read_meta(tk);\n\t\tcase let i: int =>\n\t\t\treturn i: number;\n\t\tcase io::EOF =>\n\t\t\treturn io::EOF;\n\t\t};\n\t};\n};\n\nfn read_meta(tk: *tokenizer) (list | error) = {\n\n\tlet res: []MalType = [];\n\tdefer free(res);\n\n\tconst meta = match(read_form(tk)?){\n\tcase let l: MalType =>\n\t\tyield l;\n\tcase coll_end =>\n\t\treturn unbalanced;\n\tcase io::EOF =>\n\t\treturn unexpected_eof;\n\t};\n\n\tconst next_form = match(read_form(tk)?){\n\tcase let l: MalType =>\n\t\tyield l;\n\tcase coll_end =>\n\t\treturn unbalanced;\n\tcase io::EOF =>\n\t\treturn unexpected_eof;\n\t};\n\n\treturn make_list(3, [\"with-meta\": symbol, next_form, meta]);\n};\n\nfn read_quote(tk: *tokenizer, t: quote_tk) (list | error) = {\n\n\tconst qs: symbol = match(t){\n\tcase quote =>\n\t\tyield \"quote\";\n\tcase unquote =>\n\t\tyield \"unquote\";\n\tcase quasiquote =>\n\t\tyield \"quasiquote\";\n\tcase unquote_splice =>\n\t\tyield \"splice-unquote\";\n\tcase at =>\n\t\tyield \"deref\";\n\t};\n\n\tconst form: MalType = match(read_form(tk)?){\n\tcase let l: MalType =>\n\t\tyield l;\n\tcase coll_end =>\n\t\treturn unbalanced;\n\tcase io::EOF =>\n\t\treturn unexpected_eof;\n\t};\n\n\treturn make_list(2, [qs: symbol, form]);\n\n};\n\nfn read_hashmap(tk: *tokenizer) (hashmap | error) = {\n\n\tconst res = hm_init();\n\n\tfor(true){\n\t\tlet key = match(read_form(tk)?){\n\t\tcase hash_end =>\n\t\t\tbreak;\n\t\tcase let key: (string | symbol) =>\n\t\t\tyield key;\n\t\tcase io::EOF =>\n\t\t\treturn unexpected_eof;\n\t\t};\n\n\n\t\tlet val = match(read_form(tk)?){\n\t\tcase hash_end =>\n\t\t\treturn unbalanced;\n\t\tcase let form: MalType =>\n\t\t\tyield form;\n\t\tcase io::EOF =>\n\t\t\treturn unexpected_eof;\n\t\t};\n\n\t\tlet d = hm_add(res, key, val);\n\t};\n\n\treturn res;\n};\n\nfn read_collection(\n\ttk: *tokenizer,\n\tt: coll_beg\n) (hashmap | list | vector | error) = {\n\n\tif(t is hash_beg){\n\t\treturn read_hashmap(tk);\n\t};\n\n\tlet res: []MalType = [];\n\tdefer free(res);\n\n\tfor(true){\n\t\tmatch(read_form(tk)?){\n\t\tcase list_end =>\n\t\t\tif(!(t is list_beg)){\n\t\t\t\treturn unbalanced;\n\t\t\t};\n\t\t\treturn make_list(len(res), res);\n\t\tcase vec_end =>\n\t\t\tif(!(t is vec_beg)){\n\t\t\t\treturn unbalanced;\n\t\t\t};\n\t\t\treturn make_vec(len(res), res);\n\t\tcase  hash_end =>\n\t\t\treturn unbalanced;\n\t\tcase let form: MalType =>\n\t\t\tappend(res, form)!;\n\t\t\tcontinue;\n\t\tcase io::EOF =>\n\t\t\treturn unexpected_eof;\n\t\t};\n\t};\n};\n\n//todo: keywords as a distinct type\nfn read_symbol(s: word) MalType = {\n\tswitch(s){\n\tcase \"true\" =>\n\t\treturn true;\n\tcase \"false\" =>\n\t\treturn false;\n\tcase \"nil\" =>\n\t\treturn nil;\n\tcase =>\n\t\treturn make_symbol(s);\n\t};\n\n};\n\nfn read_string(s: str) (string | error) = {\n\n\tlet strbuf = memio::dynamic();\n\tdefer io::close(&strbuf)!;\n\tlet runes = strings::torunes(s)!;\n\n\tfor (let i: size = 0; i < len(runes); i += 1) {\n\t\tlet rn = switch (runes[i]) {\n\t\tcase '\\\\' =>\n\t\t\ti += 1;\n\t\t\tyield scan_escape(runes[i]);\n\t\tcase =>\n\t\t\tyield runes[i];\n\t\t};\n\t\tmemio::appendrune(&strbuf, rn)!;\n\t};\n\n\tlet s: str = memio::string(&strbuf)!;\n\treturn make_string(s);\n};\n\nfn scan_escape(rn: rune) rune = {\n\tswitch (rn) {\n\tcase '\\\"' =>\n\t\treturn '\\\"';\n\tcase '\\\\' =>\n\t\treturn '\\\\';\n\tcase 'b' =>\n\t\treturn '\\b';\n\tcase 'f' =>\n\t\treturn '\\f';\n\tcase 'n' =>\n\t\treturn '\\n';\n\tcase 'r' =>\n\t\treturn '\\r';\n\tcase 't' =>\n\t\treturn '\\t';\n\tcase =>\n\t\treturn rn;\n\t};\n};\n"
  },
  {
    "path": "impls/hare/mal/tokenizer.ha",
    "content": "use fmt;\nuse io;\nuse strings;\nuse strconv;\nuse ascii;\n\ntype undefined = !void;\n\ntype comment = str;\n\ntype list_beg = void;\ntype vec_beg = void;\ntype hash_beg = void;\ntype coll_beg = (list_beg | vec_beg | hash_beg);\n\ntype list_end = void;\ntype vec_end = void;\ntype hash_end = void;\ntype coll_end = (list_end | vec_end | hash_end);\n\ntype mal_meta = void;\n\ntype at = void;\ntype unquote = void;\ntype unquote_splice = void;\ntype quote = void;\ntype quasiquote = void;\ntype quote_tk = ( unquote_splice | unquote | quote | quasiquote | at);\n\ntype word = str;\n\ntype token = (int | str | io::EOF | undefined | ...coll_beg | mal_meta |\n\t...quote_tk | vec_beg | ...coll_end | comment | word);\n\ntype tokenizer = struct {\n\tbuffer: []u8,\n\tun: (token | void),\n\trb: (rune | void),\n\tcursor: size,\n\tloc: size,\n\tprev_rn: size,\n\tprev_t: size,\n\tnext_t: size,\n};\n\nfn tokenizer_init(input: []u8) tokenizer = {\n\treturn tokenizer {\n\t\tbuffer = input,\n\t\tun = void,\n\t\trb = void,\n\t\tcursor = 0,\n\t\t...\n\t};\n};\n\nfn unget_rune(tk: *tokenizer, rn: rune) void = {\n\tassert(tk.rb is void);\n\ttk.rb = rn;\n\ttk.loc = tk.prev_rn;\n};\n\nfn unget_token(tk: *tokenizer, tok: token) void = {\n\tassert(tk.un is void);\n\ttk.un = tok;\n\ttk.next_t = tk.loc;\n\ttk.loc = tk.prev_t;\n};\n\nfn nextrune(tk: *tokenizer) (rune | io::EOF) = {\n\n\tif(tk.rb is rune){\n\t\tconst rn = tk.rb as rune;\n\t\ttk.rb = void;\n\t\ttk.prev_rn = tk.loc;\n\t\ttk.loc += 1;\n\t\treturn rn;\n\t};\n\n\tif (tk.cursor >= len(tk.buffer)) {\n\t\treturn io::EOF;\n\t};\n\n\tlet rn: rune = tk.buffer[tk.cursor]: rune;\n\n\ttk.prev_rn = tk.loc;\n\ttk.loc = tk.cursor;\n\ttk.cursor += 1;\n\n\treturn rn;\n};\n\nfn iswhitespace(rn: rune) bool = {\n\tif(ascii::isspace(rn) || rn == ','){\n\t\treturn true;\n\t};\n\treturn false;\n};\n\nfn nextrunews(tk: *tokenizer) (rune | io::EOF ) = {\n\n\tfor (true) {\n\t\tmatch (nextrune(tk)) {\n\t\tcase let rn: rune =>\n\t\t\tif (iswhitespace(rn)) {\n\t\t\t\tcontinue;\n\t\t\t};\n\t\t\treturn rn;\n\t\tcase io::EOF =>\n\t\t\treturn io::EOF;\n\t\t};\n\t};\n};\n\nfn scan_string(tk: *tokenizer) (token | error) = {\n\n\tconst start = tk.cursor;\n\tlet esc: bool = false;\n\n\tfor(true){\n\t\tconst rn = match(nextrune(tk)) {\n\t\tcase let rn: rune =>\n\t\t\tyield rn;\n\t\tcase io::EOF =>\n\t\t\treturn unexpected_eof;\n\t\t};\n\n\t\tswitch(rn){\n\t\tcase '\\\\' =>\n\t\t\tesc = !esc;\n\t\t\tcontinue;\n\t\tcase '\"' =>\n\t\t\tif(esc){\n\t\t\t\tesc = false;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t};\n\t\tcase =>\n\t\t\tesc = false;\n\t\t\tcontinue;\n\t\t};\n\t};\n\n\treturn strings::fromutf8(tk.buffer[start .. tk.loc])!;\n};\n\nfn scan_comment(tk: *tokenizer) comment = {\n\n\tconst start = tk.loc;\n\tlet end = start;\n\n\tfor(true){\n\t\tconst rn = match(nextrune(tk)){\n\t\tcase let rn: rune =>\n\t\t\tyield rn;\n\t\tcase io::EOF =>\n\t\t\tend = tk.cursor;\n\t\t\tbreak;\n\t\t};\n\t\tswitch(rn){\n\t\tcase '\\n' =>\n\t\t\tend = tk.loc;\n\t\t\tbreak;\n\t\tcase => continue;\n\t\t};\n\n\t};\n\n\treturn (strings::fromutf8(tk.buffer[start .. end])!);\n\n};\n\nfn tokenizer_next(tk: *tokenizer) (token | error) = {\n\n\tmatch(tk.un){\n\tcase let tok: token =>\n\t\ttk.un = void;\n\t\ttk.prev_t = tk.loc;\n\t\ttk.loc = tk.next_t;\n\t\treturn tok;\n\tcase void =>\n\t\ttk.prev_t = tk.loc;\n\t};\n\n\tconst rn = match(nextrunews(tk)) {\n\tcase let rn: rune =>\n\t\tyield rn;\n\tcase io::EOF =>\n\t\treturn io::EOF;\n\t};\n\n\tswitch (rn) {\n\tcase '(' => return list_beg;\n\tcase ')' => return list_end;\n\tcase '[' => return vec_beg;\n\tcase ']' => return vec_end;\n\tcase '{' => return hash_beg;\n\tcase '}' => return hash_end;\n\tcase '\"' => return scan_string(tk);\n\tcase ';' => return scan_comment(tk);\n\tcase '^' => return mal_meta;\n\tcase '\\'' => return quote;\n\tcase '`' => return quasiquote;\n\tcase '~' => return scan_quote(tk);\n\tcase '@' => return at;\n\tcase => return scan_atom(tk, rn);\n\t};\n};\n\nfn scan_atom(tk: *tokenizer, rn: rune) (token | error) = {\n\n\tif (rn == '-') {\n\t\tlet nrn = match(nextrune(tk)){\n\t\tcase io::EOF =>\n\t\t\tyield 'n';\n\t\tcase let nrn: rune =>\n\t\t\tunget_rune(tk, nrn);\n\t\t\tyield nrn;\n\t\t};\n\n\t\tif(ascii::isdigit(nrn)){\n\t\t\treturn scan_number(tk);\n\t\t};\n\t} else\n\tif(ascii::isdigit(rn)){\n\t\treturn scan_number(tk);\n\t};\n\n\treturn scan_word(tk)!;\n};\n\nfn scan_number(tk: *tokenizer) (token | error) = {\n\n\tconst start = tk.loc;\n\tlet end: size = start;\n\n\tfor(true){\n\t\tconst rn = match(nextrune(tk)){\n\t\tcase io::EOF =>\n\t\t\tend = tk.cursor;\n\t\t\tbreak;\n\t\tcase let rn: rune =>\n\t\t\tyield rn;\n\t\t};\n\n\t\tif(!ascii::isdigit(rn)){\n\t\t\tend = tk.loc;\n\t\t\tunget_rune(tk, rn);\n\t\t\tbreak;\n\t\t};\n\n\t};\n\n\treturn strconv::stoi(strings::fromutf8(tk.buffer[start .. end])!)!;\n};\n\nfn scan_word(tk: *tokenizer) (token | error) = {\n\n\tconst start = tk.loc;\n\tlet end: size = start;\n\n\tfor(true){\n\t\tconst rn = match(nextrune(tk)){\n\t\tcase io::EOF =>\n\t\t\tend = tk.cursor;\n\t\t\tbreak;\n\t\tcase let rn: rune =>\n\t\t\tyield rn;\n\t\t};\n\n\t\tif(!iswordrn(rn)){\n\t\t\tend = tk.loc;\n\t\t\tunget_rune(tk, rn);\n\t\t\tbreak;\n\t\t};\n\n\t};\n\n\treturn strings::fromutf8(tk.buffer[start .. end])!: word;\n};\n\nfn iswordrn(rn: rune) bool = {\n\tif(ascii::isalnum(rn)){\n\t\treturn true;\n\t};\n\n\tswitch(rn){\n\tcase '-' => return true;\n\tcase '_' => return true;\n\tcase '?' => return true;\n\tcase '!' => return true;\n\tcase '>' => return true;\n\tcase '=' => return true;\n\tcase '<' => return true;\n\tcase '*' => return true;\n\tcase '/' => return true;\n\tcase ':' => return true;\n\tcase => void;\n\t};\n\n\treturn false;\n};\n\nfn scan_quote(tk: *tokenizer) (token | error) = {\n\tmatch(tokenizer_next(tk)?){\n\tcase at =>\n\t\treturn unquote_splice;\n\tcase let res: token =>\n\t\tunget_token(tk, res);\n\t\treturn unquote;\n\t};\n};\n"
  },
  {
    "path": "impls/hare/mal/types.ha",
    "content": "use strings;\nuse types;\n\nexport type nil = void;\nexport type symbol = str;\nexport type number = i64;\nexport type atom = *MalType;\n\nexport type vector = *struct {\n\tdata: []MalType,\n\tmeta: MalType,\n};\n\nexport type list = *struct {\n\tdata: []MalType,\n\tmeta: MalType,\n};\n\nexport type string = *struct {\n\tdata: str,\n\tmeta: MalType,\n};\n\nexport type intrinsic = *struct {\n\teval: *fn([]MalType) (MalType | error),\n\tmeta: MalType,\n};\n\nexport type function = *struct {\n\teval: *fn(MalType, *env) (MalType | error),\n\tenvi: *env,\n\targs: []MalType,\n\tbody: MalType,\n\tmeta: MalType,\n};\n\nexport type macro = function;\n\nexport type MalType = (macro | function | intrinsic | atom | bool |\n\tstring | hashmap | list | vector | number | symbol | nil);\n\n// Any mal object that is supposed to persist should be created by one of these\n// functions. Any allocations done by other functions should be freed manually.\n//\n// Envs & Hashmaps are treated separately in their implementation files.\n\nexport fn make_intrinsic(\n\tfunc: *fn([]MalType) (MalType | error),\n) intrinsic = {\n\n\tconst new = alloc(struct {\n\t\teval: *fn([]MalType) (MalType | error) = func,\n\t\tmeta: MalType = nil,\n\t})!;\n\n\tappend(gc.memory.intrinsics, new)!;\n\treturn new;\n};\n\nexport fn make_func(\n\teval: *fn(MalType, *env) (MalType | error),\n\tenvi: *env,\n\targs: []MalType,\n\tbody: MalType,\n) function = {\n\n\tlet arg_list: []MalType = [];\n\tif(len(args) > 0) {\n\t\targ_list = alloc([nil...], len(args))!;\n\t\targ_list[0..] = args;\n\t};\n\n\tconst new = alloc(struct{\n\t\teval: *fn(MalType, *env) (MalType | error)  = eval,\n\t\tenvi: *env= envi,\n\t\targs: []MalType = arg_list,\n\t\tbody: MalType = body,\n\t\tmeta: MalType = nil })!;\n\n\tappend(gc.memory.funcs, new)!;\n\treturn new;\n};\n\nfn free_func(f: function) void = {\n\tfree(f.args);\n\tfree(f);\n};\n\nexport fn make_list(s: size, init: []MalType = []) list = {\n\n\tconst new: list = alloc(struct {\n\t\tdata: []MalType = [],\n\t\tmeta: MalType = nil,\n\t})!;\n\n\tif (s == 0) return new;\n\n\tnew.data = alloc([nil...], s)!;\n\tnew.data[0..len(init)] = init;\n\n\tappend(gc.memory.lists, new)!;\n\treturn new;\n};\n\nfn free_list(l: list) void = {\n\tfree(l.data);\n\tfree(l);\n};\n\nexport fn make_vec(s: size, init: []MalType = []) vector = {\n\n\tconst new: vector = alloc(struct {\n\t\tdata: []MalType = [],\n\t\tmeta: MalType = nil,\n\t})!;\n\n\tif (s == 0) return new;\n\n\tnew.data = alloc([nil...], s)!;\n\tnew.data[0..len(init)] = init;\n\n\tappend(gc.memory.vecs, new)!;\n\treturn new;\n};\n\nfn free_vec(v: vector) void = {\n\tfree(v.data);\n\tfree(v);\n};\n\nexport fn make_symbol(name: str) symbol = {\n\n\tlet hm: hashmap = match(gc.memory.symbols){\n\tcase void =>\n\t\tgc.memory.symbols = hm_init(false);\n\t\tyield gc.memory.symbols: hashmap;\n\tcase let hm: hashmap =>\n\t\tyield hm;\n\t};\n\n\tmatch(hm_get(hm, name: symbol)) {\n\tcase undefined_key => void;\n\tcase let s: symbol =>\n\t\treturn s;\n\t};\n\n\tconst new = strings::dup(name)!: symbol;\n\thm_add(gc.memory.symbols: hashmap, new, new);\n\n\treturn new;\n};\n\nexport fn make_string(s: str) string = {\n\n\tconst new_str = strings::dup(s)!;\n\n\tconst new = alloc(struct {\n\t\tdata: str = new_str,\n\t\tmeta: MalType = nil,\n\t})!;\n\n\tappend(gc.memory.strings, new)!;\n\treturn new;\n};\n\nfn free_string(s: string) void = {\n\tfree(s.data);\n\tfree(s);\n};\n\nexport fn make_atom(ref: MalType) atom = {\n\n\tconst new = alloc(ref)!;\n\tappend(gc.memory.atoms, new)!;\n\treturn new;\n};\n\n// check if two strings share the same buffer in memory\n// Does not check for substrings!\nfn str_memeq(s1: str, s2: str) bool = {\n\n\tconst ts1 = &s1: *types::string;\n\tconst ts2 = &s2: *types::string;\n\n\treturn ts1.data == ts2.data;\n};\n"
  },
  {
    "path": "impls/hare/run",
    "content": "#!/usr/bin/env bash\n\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\""
  },
  {
    "path": "impls/hare/step0_repl.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse os;\nuse strings;\n\nfn read (input: []u8) []u8 = {\n\treturn input;\n};\n\nfn eval (input: []u8) []u8 = {\n\treturn input;\n};\n\nfn print (input: []u8) str = {\n\treturn strings::fromutf8(input)!;\n};\n\nfn rep (input: []u8) str = {\n\treturn print(eval(read(input)));\n};\n\nexport fn main() void = {\n\n\tfor(true){\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\t\tcase let input: []u8 =>\n\t\t\t\tyield input;\n\t\t\tcase io::EOF =>\n\t\t\t\tbreak;\n\t\t\tcase io::error =>\n\t\t\t\tbreak;\n\t\t};\n\n\t\tdefer free(input);\n\n\t\tfmt::println(rep(input))!;\n\t};\n\n};\n"
  },
  {
    "path": "impls/hare/step1_read_print.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input)?;\n};\n\nfn eval (input: mal::MalType) mal::MalType = {\n\treturn input;\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n};\n\nfn rep (input: []u8) void = {\n\tmatch (read(input)){\n\tcase let e: mal::error =>\n\t\tmal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tprint(eval(form));\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n};\n\nexport fn main() void = {\n\n\tfor(true){\n\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\tdefer free(input);\n\t\trep(input);\n\t};\n\n};\n"
  },
  {
    "path": "impls/hare/step2_eval.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input)?;\n};\n\nfn eval_list(ls: mal::list, env: mal::hashmap) mal::MalType = {\n\n\tif(len(ls.data) == 0) return ls;\n\n\tconst func = match(eval(ls.data[0], env)){\n\tcase let func: mal::intrinsic =>\n\t\tyield func;\n\tcase => return ls;\n\t};\n\n\tfor(let i: size = 1; i < len(ls.data); i += 1){\n\t\tls.data[i] = eval(ls.data[i], env);\n\t};\n\n\treturn func.eval(ls.data[1..])!;\n};\n\n\nfn eval_vec(vec: mal::vector, env: mal::hashmap) mal::vector ={\n\n\tif(len(vec.data) == 0) return vec;\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tvec.data[i] = eval(vec.data[i], env);\n\t};\n\treturn vec;\n};\n\nfn eval_hash(\n\tmap: mal::hashmap,\n\tenv: mal::hashmap,\n) mal::hashmap = {\n\n\tlet res = mal::hm_init();\n\n\tfor(let e .. map.data) {\n\t\tmal::hm_add(res, e.key, eval(e.val, env));\n\t};\n\n\treturn res;\n};\n\nfn eval (ast: mal::MalType, env: mal::hashmap) mal::MalType = {\n\n\tlet res: mal::MalType = match(ast){\n\tcase let key: mal::symbol =>\n\t\tlet v: mal::MalType = match(mal::hm_get(env, key)){\n\t\tcase let v: mal::MalType =>\n\t\t\tyield v;\n\t\tcase =>\n\t\t\tyield mal::nil;\n\t\t};\n\t\tyield eval(v, env);\n\tcase let ls: mal::list =>\n\t\tyield eval_list(ls, env);\n\tcase let vec: mal::vector =>\n\t\tyield eval_vec(vec, env);\n\tcase let hash: mal::hashmap =>\n\t\tyield eval_hash(hash, env);\n\tcase let func: mal::intrinsic =>\n\t\tyield func;\n\tcase =>\n\t\tyield ast;\n\t};\n\n\treturn res;\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n};\n\nfn rep (input: []u8, env: mal::hashmap) void = {\n\tmatch (read(input)){\n\tcase let e: mal::error =>\n\t\tmal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tprint(eval(form, env));\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n};\n\nexport fn main() void = {\n\n\tconst env = mal::hm_init();\n\n\tmal::hm_add(env, \"+\": mal::symbol, mal::make_intrinsic(&mal::plus));\n\tmal::hm_add(env, \"-\": mal::symbol, mal::make_intrinsic(&mal::minus));\n\tmal::hm_add(env, \"*\": mal::symbol, mal::make_intrinsic(&mal::mult));\n\tmal::hm_add(env, \"/\": mal::symbol, mal::make_intrinsic(&mal::div));\n\n\tfor(true){\n\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\tdefer free(input);\n\t\trep(input, env);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step3_env.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input)?;\n};\n\nfn eval_let(\n\tenv: *mal::env,\n\tbindings: []mal::MalType,\n\tbody: mal::MalType...\n) (mal::MalType | mal::error) = {\n\n\tlet let_env = mal::env_init(env);\n\n\tfor(let i: size = 0; i < len(bindings); i += 2){\n\t\tmal::env_set(let_env, bindings[i] as mal::symbol,\n\t\t\t     eval(bindings[i+1], let_env)?);\n\t};\n\n\tlet result: mal::MalType = mal::nil;\n\tfor(let form .. body){\n\t\tresult = eval(form, let_env)?;\n\t};\n\treturn result;\n};\n\nfn eval_list(ls: mal::list, env: *mal::env) (mal::MalType | mal::error) = {\n\n\tif(len(ls.data) == 0) return ls;\n\n\t// handle special cases of 'let*' and 'def!' forms\n\tmatch(ls.data[0]){\n\tcase let sym: mal::symbol =>\n\t\tif(sym == \"def!\"){\n\t\t\tif(len(ls.data) != 3)\n\t\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\t\tls): mal::syntax_error;\n\n\t\t\tlet val = eval(ls.data[2], env)?;\n\t\t\tmal::env_set(env, ls.data[1] as mal::symbol, val);\n\t\t\treturn val;\n\n\t\t} else if(sym == \"let*\"){\n\t\t\tif(len(ls.data) < 3)\n\t\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\t\tls): mal::syntax_error;\n\n\t\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\t\tcase let b: mal::list =>\n\t\t\t\tyield b.data;\n\t\t\tcase let b: mal::vector =>\n\t\t\t\tyield b.data;\n\t\t\tcase =>\n\t\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t\t};\n\t\treturn eval_let(env, bindings, ls.data[2..]...);\n\t\t};\n\tcase => void;\n\t};\n\n\tconst func = match(eval(ls.data[0], env)?){\n\t\tcase let func: mal::intrinsic =>\n\t\t\tyield func;\n\t\tcase => return ls;\n\t};\n\n\tfor(let i: size = 1; i < len(ls.data); i += 1){\n\t\tls.data[i] = eval(ls.data[i], env)?;\n\t};\n\n\treturn func.eval(ls.data[1..]);\n};\n\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tif(len(vec.data) == 0) return vec;\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol =>\n\t\tvoid;\n\tcase mal::nil =>\n\t\tvoid;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet res: mal::MalType = match(ast){\n\tcase let key: mal::symbol =>\n\t\tyield eval(mal::env_get(env, key)?, env)?;\n\tcase let ls: mal::list =>\n\t\tyield eval_list(ls, env)?;\n\tcase let vec: mal::vector =>\n\t\tyield eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\tyield mal::eval_hash(hash, &eval, env)?;\n\tcase let func: mal::intrinsic =>\n\t\tyield func;\n\tcase => yield ast;\n\t};\n\n\treturn res;\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n};\n\nfn rep (input: []u8, env: *mal::env) void = {\n\tlet ast = match (read(input)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tprint(result);\n};\n\nexport fn main() void = {\n\n\tconst env = mal::env_init();\n\n\tmal::env_set(env, \"nil\":mal::symbol, mal::nil);\n\tmal::load_namespace(mal::core, env)!;\n\n\tfor(true){\n\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input = match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\tdefer free(input);\n\t\trep(input, env);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step4_if_fn_do.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_let(\n\tenv: *mal::env,\n\tbindings: []mal::MalType,\n\tbody: mal::MalType...\n) (mal::MalType | mal::error) = {\n\n\tlet let_env = mal::env_init(env);\n\n\tfor(let i: size = 0; i < len(bindings); i += 2){\n\t\tmal::env_set(let_env, bindings[i] as mal::symbol,\n\t\t\teval(bindings[i+1], let_env)?);\n\t};\n\n\tlet result: mal::MalType = mal::nil;\n\tfor(let form .. body){\n\t\tresult = eval(form, let_env)?;\n\t};\n\treturn result;\n};\n\nfn eval_list(ls: mal::list, env: *mal::env) (mal::MalType | mal::error) = {\n\n\tif(len(ls.data) == 0) return ls;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*' and 'def!' forms\n\tmatch(ls.data[0]){\n\tcase let sym: mal::symbol =>\n\t\tswitch(sym){\n\t\tcase \"def!\" =>\n\t\t\tif(len(ls.data) != 3)\n\t\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\t\tls): mal::syntax_error;\n\n\t\t\tlet val = eval(ls.data[2], env)?;\n\t\t\tmal::env_set(env, ls.data[1] as mal::symbol, val);\n\t\t\treturn val;\n\n\t\tcase \"let*\" =>\n\t\t\tif(len(ls.data) < 3)\n\t\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\t\tls): mal::syntax_error;\n\n\t\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\t\tcase let b: mal::list =>\n\t\t\t\tyield b.data;\n\t\t\tcase let b: mal::vector =>\n\t\t\t\tyield b.data;\n\t\t\tcase =>\n\t\t\t     return (\"let*\", ls): mal::syntax_error;\n\t\t\t};\n\t\t\treturn eval_let(env, bindings, ls.data[2..]...);\n\t\tcase \"do\" =>\n\t\t\tlet result: mal::MalType = mal::nil;\n\t\t\tfor(let form .. ls.data[1..]){\n\t\t\t\tresult = eval(form, env)?;\n\t\t\t};\n\t\t\treturn result;\n\t\tcase \"if\" =>\n\t\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\t\tls): mal::syntax_error;\n\t\t\tmatch(eval(ls.data[1], env)?){\n\t\t\tcase mal::nil =>\n\t\t\t\tif(len(ls.data) == 4){\n\t\t\t\t\treturn eval(ls.data[3], env);\n\t\t\t\t} else {\n\t\t\t\t\treturn mal::nil;\n\t\t\t\t};\n\t\t\tcase let b: bool =>\n\t\t\t\tif(b){\n\t\t\t\t\treturn eval(ls.data[2], env);\n\t\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\t\treturn eval(ls.data[3], env);\n\t\t\t\t} else {\n\t\t\t\t\treturn mal::nil;\n\t\t\t\t};\n\t\t\tcase =>\n\t\t\t\treturn eval(ls.data[2], env);\n\t\t\t};\n\t\tcase \"fn*\" =>\n\t\t\tlet args = match(ls.data[1]){\n\t\t\tcase let a: mal::list =>\n\t\t\t\tyield a.data;\n\t\t\tcase let a: mal::vector =>\n\t\t\t\tyield a.data;\n\t\t\t};\n\t\t\tlet body = match(ls.data[2]){\n\t\t\tcase let b: mal::MalType =>\n\t\t\t\tyield b;\n\t\t\tcase => return mal::nil;\n\t\t\t};\n\t\t\treturn mal::make_func(&eval, env, args, body);\n\t\tcase => void;\n\t\t};\n\tcase => void;\n\t};\n\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tlet local = mal::env_init(func.envi);\n\t\tmal::env_bind(local, func.args, args);\n\t\treturn eval(func.body, local);\n\tcase => return ls;\n\t};\n};\n\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i],env)?;\n\t};\n\treturn res;\n};\n\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol => void;\n\tcase mal::nil => void;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\tlet res: mal::MalType = match(ast){\n\tcase let key: mal::symbol =>\n\t\tyield if(strings::hasprefix(key, ':')){\n\t\t\tyield key;\n\t\t} else {\n\t\t\tyield mal::env_get(env, key)?;\n\t\t};\n\tcase let ls: mal::list =>\n\t\tyield eval_list(ls, env)?;\n\tcase let vec: mal::vector =>\n\t\tyield eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\tyield mal::eval_hash(hash, &eval, env)?;\n\tcase let func: mal::intrinsic =>\n\t\tyield func;\n\tcase let func: mal::function =>\n\t\tyield func;\n\tcase => yield ast;\n\t};\n\n\treturn res;\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n};\n\nfn rep (input: []u8, env: *mal::env) void = {\n\tlet ast = match(read(input)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tprint(result);\n};\n\nexport fn main() void = {\n\n\tconst env = mal::env_init();\n\tmal::load_namespace(mal::core, env)!;\n\n\tfor(true){\n\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\tdefer free(input);\n\t\trep(input, env);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step5_tco.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\nfor(true){\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol => void;\n\tcase mal::nil => void;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet ls: mal::list = match(ast){\n\tcase let key: mal::symbol =>\n\t\tif(strings::hasprefix(key, ':')){\n\t\t\treturn key;\n\t\t} else {\n\t\t\treturn mal::env_get(env, key)?;\n\t\t};\n\tcase let vec: mal::vector =>\n\t\treturn eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\treturn mal::eval_hash(hash, &eval, env)?;\n\tcase let ls: mal::list =>\n\t\tyield ls;\n\tcase =>\n\t\treturn ast;\n\t};\n\n\tif(len(ls.data) == 0) return ast;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*' and 'def!' forms\n\tmatch(ls.data[0]){\n\n\tcase let sym: mal::symbol =>\n\n\tswitch(sym){\n\tcase \"def!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet val = eval(ls.data[2], env)?;\n\t\tmal::env_set(env, ls.data[1] as mal::symbol, val);\n\t\treturn val;\n\n\tcase \"let*\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\tcase let b: mal::list =>\n\t\t\tyield b.data;\n\t\tcase let b: mal::vector =>\n\t\t\tyield b.data;\n\t\tcase =>\n\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t};\n\n\t\tlet let_env = mal::env_init(env);\n\n\t\tfor(let i: size = 0; i < len(bindings); i += 2){\n\t\t\tmal::env_set(let_env, bindings[i] as mal::symbol,\n\t\t\teval(bindings[i+1], let_env)?);\n\t\t};\n\n\t\tenv = let_env;\n\t\tast = ls.data[2];\n\t\tcontinue;\n\tcase \"do\" =>\n\t\tlet result: mal::MalType = mal::nil;\n\t\tfor(let form .. ls.data[1..len(ls.data)-1]){\n\t\t\tresult = eval(form, env)?;\n\t\t};\n\t\tast = ls.data[len(ls.data)-1];\n\t\tcontinue;\n\tcase \"if\" =>\n\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tmatch(eval(ls.data[1], env)?){\n\t\tcase mal::nil =>\n\t\t\tif(len(ls.data) == 4){\n\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase let b: bool =>\n\t\t\tif(b){\n\t\t\t\tast = ls.data[2];\n\t\t\t\tcontinue;\n\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase =>\n\t\t\tast = ls.data[2];\n\t\t\tcontinue;\n\t\t};\n\tcase \"fn*\" =>\n\t\tlet args = match(ls.data[1]){\n\t\tcase let a: mal::list =>\n\t\t\tyield a.data;\n\t\tcase let a: mal::vector =>\n\t\t\tyield a.data;\n\t\t};\n\t\tlet body = match(ls.data[2]){\n\t\tcase let b: mal::MalType =>\n\t\t\tyield b;\n\t\tcase => return mal::nil;\n\t\t};\n\t\treturn mal::make_func(&eval, env, args, body);\n\tcase => void;\n\t};\n\tcase => void;\n\t};\n\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tenv = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\tast = func.body;\n\t\tfree(args);\n\t\tcontinue;\n\tcase => return (\"not a function:\", ls.data[0]): mal::syntax_error;\n\t};\n};};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n};\n\nfn rep (input: []u8, env: *mal::env) void = {\n\tlet ast = match(read(input)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tprint(result);\n};\n\nexport fn main() void = {\n\n\tconst env = mal::env_init();\n\tmal::load_namespace(mal::core, env)!;\n\n\tfor(true){\n\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\tdefer free(input);\n\t\trep(input, env);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step6_file.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\nfor(true){\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol => void;\n\tcase mal::nil => void;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet ls: mal::list = match(ast){\n\tcase let key: mal::symbol =>\n\t\tif(strings::hasprefix(key, ':')){\n\t\t\treturn key;\n\t\t} else {\n\t\t\treturn mal::env_get(env, key)?;\n\t\t};\n\tcase let vec: mal::vector =>\n\t\treturn eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\treturn mal::eval_hash(hash, &eval, env)?;\n\tcase let ls: mal::list =>\n\t\tyield ls;\n\tcase =>\n\t\treturn ast;\n\t};\n\n\tif(len(ls.data) == 0) return ast;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*' and 'def!' forms\n\tmatch(ls.data[0]){\n\n\tcase let sym: mal::symbol =>\n\n\tswitch(sym){\n\tcase \"def!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet val = eval(ls.data[2], env)?;\n\t\tmal::env_set(env, ls.data[1] as mal::symbol, val);\n\t\treturn val;\n\n\tcase \"let*\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\tcase let b: mal::list =>\n\t\t\tyield b.data;\n\t\tcase let b: mal::vector =>\n\t\t\tyield b.data;\n\t\tcase =>\n\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t};\n\n\t\tlet let_env = mal::env_init(env);\n\n\t\tfor(let i: size = 0; i < len(bindings); i += 2){\n\t\t\tmal::env_set(let_env, bindings[i] as mal::symbol,\n\t\t\teval(bindings[i+1], let_env)?);\n\t\t};\n\n\t\tenv = let_env;\n\t\tast = ls.data[2];\n\t\tcontinue;\n\tcase \"do\" =>\n\t\tlet result: mal::MalType = mal::nil;\n\t\tfor(let form .. ls.data[1..len(ls.data)-1]){\n\t\t\tresult = eval(form, env)?;\n\t\t};\n\t\tast = ls.data[len(ls.data)-1];\n\t\tcontinue;\n\tcase \"if\" =>\n\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tmatch(eval(ls.data[1], env)?){\n\t\tcase mal::nil =>\n\t\t\tif(len(ls.data) == 4){\n\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase let b: bool =>\n\t\t\tif(b){\n\t\t\t\tast = ls.data[2];\n\t\t\t\tcontinue;\n\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase =>\n\t\t\tast = ls.data[2];\n\t\t\tcontinue;\n\t\t};\n\tcase \"fn*\" =>\n\t\tlet args = match(ls.data[1]){\n\t\tcase let a: mal::list =>\n\t\t\tyield a.data;\n\t\tcase let a: mal::vector =>\n\t\t\tyield a.data;\n\t\t};\n\t\tlet body = match(ls.data[2]){\n\t\tcase let b: mal::MalType =>\n\t\t\tyield b;\n\t\tcase => return mal::nil;\n\t\t};\n\t\treturn mal::make_func(&eval, env, args, body);\n\tcase => void;\n\t};\n\tcase => void;\n\t};\n\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tenv = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\tast = func.body;\n\t\tcontinue;\n\tcase => return (\"not a function:\", ls.data[0]): mal::syntax_error;\n\t};\n};};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n\t};\n\nfn rep (input: []u8, env: *mal::env, printp: bool = true) void = {\n\tlet ast = match(read(input)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tif(printp) print(result);\n};\n\n\nlet repl_env: nullable *mal::env = null;\n\nfn do_eval(args: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'do_eval': too few arguments\", args):\n\t\t\tmal::syntax_error;\n\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\treturn mal::not_implemented;\n\t};\n\treturn eval(args[0], env);\n};\n\nexport fn main() void = {\n\n\trepl_env = mal::env_init();\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\tfmt::fatal(\"No repl environment initialized!\");\n\t};\n\n\tmal::env_set(env, \"eval\", mal::make_intrinsic(&do_eval));\n\tmal::load_namespace(mal::core, env)!;\n\tlet load_file = \"(def! load-file (fn* (f) (eval (read-string\n\t\t(str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\";\n\n\trep(strings::toutf8(load_file), env, false);\n\n\tconst args = os::args;\n\n\tlet argvlen: size = if (len(args) > 2) {\n\t\tyield len(args)-2;\n\t} else {\n\t\tyield 0;\n\t};\n\n\tlet argv = mal::make_list(argvlen);\n\n\tif (len(args) > 2){\n\t\tfor(let i: size = 2; i < len(args); i += 1){\n\t\t\targv.data[i-2] = &args[i]: mal::string;\n\t\t};\n\t};\n\n\tmal::env_set(env, \"*ARGV*\", argv);\n\n\tif(len(args) > 1){\n\t\tlet exec_str = strings::join(\"\", \"(load-file \\\"\", args[1],\n\t\t\t\"\\\")\")!;\n\t\trep(strings::toutf8(exec_str), env, false);\n\t\tfree(exec_str);\n\t\tos::exit(0);\n\t};\n\n\tfor(true){\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\trep(input, env);\n\t\tfree(input);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step7_quote.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn starts_with(ast: mal::MalType, sym: str) bool = {\n\tmatch(ast){\n\tcase let ls: mal::list=>\n\t\tif(len(ls.data) < 1) return false;\n\t\tmatch(ls.data[0]){\n\t\tcase let s: mal::symbol =>\n\t\t\treturn s == sym;\n\t\tcase =>\n\t\t\treturn false;\n\t\t};\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn qq_iter(ast: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tlet acc = mal::make_list(0);\n\n\tfor(let i: size = len(ast); 0 < i ; i -= 1){\n\n\t\tlet elt: mal::MalType  = ast[i - 1];\n\n\t\tif(starts_with(elt, \"splice-unquote\")){\n\t\t\tlet elt: mal::list = match(elt){\n\t\t\tcase let l: mal::list =>\n\t\t\t\tyield l;\n\t\t\tcase =>\n\t\t\t\treturn (\"list\", ast): mal::type_error;\n\t\t\t};\n\n\t\t\tacc = mal::make_list(3, [\"concat\":mal::symbol,\n\t\t\t\t\t\t elt.data[1], acc]);\n\t\t} else {\n\t\t\tacc = mal::make_list(3, [\"cons\":mal::symbol,\n\t\t\t\t\t\t quasiquote(elt)?, acc]);\n\t\t};\n\t};\n\n\treturn acc;\n};\n\nfn quasiquote(ast: mal::MalType) (mal::MalType | mal::error) = {\n\tmatch(ast) {\n\tcase let ls: mal::list =>\n\t\tif(starts_with(ls, \"unquote\")) {\n\t\t\treturn ls.data[1];\n\t\t} else {\n\t\t\treturn qq_iter(ls.data);\n\t\t};\n\tcase let ls: mal::vector =>\n\t\tlet res: mal::list = mal::make_list(2, [\"vec\":mal::symbol,\n\t\t\t\t\t\t\tqq_iter(ls.data)?]);\n\t\treturn res;\n\tcase let hm: (mal::symbol | mal::hashmap) =>\n\t\tlet res: mal::list = mal::make_list(2, [\"quote\":mal::symbol,\n\t\t\t\t\t\t\tast]);\n\t\treturn res;\n\tcase =>\n\t\treturn ast;\n\t};\n};\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\nfor(true){\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol => void;\n\tcase mal::nil => void;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet ls: mal::list = match(ast){\n\tcase let key: mal::symbol =>\n\t\tif(strings::hasprefix(key, ':')){\n\t\t\treturn key;\n\t\t} else {\n\t\t\treturn mal::env_get(env, key)?;\n\t\t};\n\tcase let vec: mal::vector =>\n\t\treturn eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\treturn mal::eval_hash(hash, &eval, env)?;\n\tcase let ls: mal::list =>\n\t\tyield ls;\n\tcase =>\n\t\treturn ast;\n\t};\n\n\tif(len(ls.data) == 0) return ast;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*' and 'def!' forms\n\tmatch(ls.data[0]){\n\n\tcase let sym: mal::symbol =>\n\n\tswitch(sym){\n\tcase \"quasiquote\" =>\n\t\tast = quasiquote(ls.data[1])?;\n\t\tcontinue;\n\tcase \"quote\" =>\n\t\treturn ls.data[1];\n\tcase \"def!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet val = eval(ls.data[2], env)?;\n\t\tmal::env_set(env, ls.data[1] as mal::symbol, val);\n\t\treturn val;\n\n\tcase \"let*\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\tcase let b: mal::list =>\n\t\t\tyield b.data;\n\t\tcase let b: mal::vector =>\n\t\t\tyield b.data;\n\t\tcase =>\n\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t};\n\n\t\tlet let_env = mal::env_init(env);\n\n\t\tfor(let i: size = 0; i < len(bindings); i += 2){\n\t\t\tmal::env_set(let_env, bindings[i] as mal::symbol,\n\t\t\teval(bindings[i+1], let_env)?);\n\t\t};\n\n\t\tenv = let_env;\n\t\tast = ls.data[2];\n\t\tcontinue;\n\tcase \"do\" =>\n\t\tlet result: mal::MalType = mal::nil;\n\t\tfor(let form .. ls.data[1..len(ls.data)-1]){\n\t\t\tresult = eval(form, env)?;\n\t\t};\n\t\tast = ls.data[len(ls.data)-1];\n\t\tcontinue;\n\tcase \"if\" =>\n\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tmatch(eval(ls.data[1], env)?){\n\t\tcase mal::nil =>\n\t\t\tif(len(ls.data) == 4){\n\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase let b: bool =>\n\t\t\tif(b){\n\t\t\t\tast = ls.data[2];\n\t\t\t\tcontinue;\n\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase =>\n\t\t\tast = ls.data[2];\n\t\t\tcontinue;\n\t\t};\n\tcase \"fn*\" =>\n\t\tlet args = match(ls.data[1]){\n\t\tcase let a: mal::vector =>\n\t\t\tyield a.data;\n\t\tcase let a: mal::list =>\n\t\t\tyield a.data;\n\t\t};\n\t\tlet body = match(ls.data[2]){\n\t\tcase let b: mal::MalType =>\n\t\t\tyield b;\n\t\tcase => return mal::nil;\n\t\t};\n\t\treturn mal::make_func(&eval, env, args, body);\n\tcase => void;\n\t};\n\tcase => void;\n\t};\n\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tenv = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\tfree(args);\n\t\tast = func.body;\n\t\tcontinue;\n\tcase => return (\"not a function:\", ls.data[0]): mal::syntax_error;\n\t};\n};};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n\t};\n\nfn rep (input: []u8, env: *mal::env, printp: bool = true) void = {\n\tlet ast = match(read(input)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tif(printp) print(result);\n};\n\n\nlet repl_env: nullable *mal::env = null;\n\nfn do_eval(args: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'do_eval': too few arguments\", args):\n\t\t\tmal::syntax_error;\n\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\treturn mal::not_implemented;\n\t};\n\treturn eval(args[0], env);\n};\n\nexport fn main() void = {\n\n\trepl_env = mal::env_init();\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\tfmt::fatal(\"No repl environment initialized!\");\n\t};\n\n\tmal::env_set(env, \"eval\", mal::make_intrinsic(&do_eval));\n\tmal::load_namespace(mal::core, env)!;\n\tlet load_file = \"(def! load-file (fn* (f)\n\t\t(eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\";\n\n\trep(strings::toutf8(load_file), env, false);\n\n\t// handle command line arguments\n\tconst args = os::args;\n\n\tlet argvlen: size = if (len(args) > 2) {\n\t\tyield len(args)-2;\n\t} else {\n\t\tyield 0;\n\t};\n\n\tlet argv = mal::make_list(argvlen);\n\n\tif (len(args) > 2){\n\t\tfor(let i: size = 2; i < len(args); i += 1){\n\t\t\targv.data[i-2] = &args[i]: mal::string;\n\t\t};\n\t};\n\n\tmal::env_set(env, \"*ARGV*\", argv);\n\n\tif(len(args) > 1){\n\t\tlet exec_str = strings::join(\"\", \"(load-file \\\"\", args[1],\n\t\t\t\"\\\")\")!;\n\t\trep(strings::toutf8(exec_str), env, false);\n\t\tfree(exec_str);\n\t\tos::exit(0);\n\t};\n\n\tfor(true){\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\trep(input, env);\n\t\tfree(input);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step8_macros.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn starts_with(ast: mal::MalType, sym: str) bool = {\n\tmatch(ast){\n\tcase let ls: mal::list=>\n\t\tif(len(ls.data) < 1) return false;\n\t\tmatch(ls.data[0]){\n\t\tcase let s: mal::symbol =>\n\t\t     return s == sym;\n\t\tcase =>\n\t\t     return false;\n\t\t};\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn qq_iter(ast: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tlet acc = mal::make_list(0);\n\n\tfor(let i: size = len(ast); 0 < i ; i -= 1){\n\n\t\tlet elt: mal::MalType  = ast[i - 1];\n\n\t\tif(starts_with(elt, \"splice-unquote\")){\n\t\t\tlet elt: mal::list = match(elt){\n\t\t\tcase let l: mal::list =>\n\t\t\t\tyield l;\n\t\t\tcase =>\n\t\t\t\treturn (\"list\", ast): mal::type_error;\n\t\t\t};\n\n\t\t\tacc = mal::make_list(3, [\"concat\":mal::symbol,\n\t\t\t\t\t\t elt.data[1], acc]);\n\t\t} else {\n\t\t\tacc = mal::make_list(3, [\"cons\":mal::symbol,\n\t\t\t\t\t\t quasiquote(elt)?, acc]);\n\t\t};\n\t};\n\n\treturn acc;\n};\n\nfn quasiquote(ast: mal::MalType) (mal::MalType | mal::error) = {\n\tmatch(ast) {\n\tcase let ls: mal::list =>\n\t\tif(starts_with(ls, \"unquote\")) {\n\t\t\treturn ls.data[1];\n\t\t} else {\n\t\t\treturn qq_iter(ls.data);\n\t\t};\n\tcase let ls: mal::vector =>\n\t\tlet res: mal::list = mal::make_list(2, [\"vec\":mal::symbol,\n\t\t\t\t\t\t\tqq_iter(ls.data)?]);\n\t\treturn res;\n\tcase let hm: (mal::symbol | mal::hashmap) =>\n\t\tlet res: mal::list = mal::make_list(2, [\"quote\":mal::symbol,\n\t\t\t\t\t\t\tast]);\n\t\treturn res;\n\tcase =>\n\t\treturn ast;\n\t};\n};\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\nfor(true){\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol => void;\n\tcase mal::nil => void;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet ls: mal::list = match(ast){\n\tcase let key: mal::symbol =>\n\t\tif(strings::hasprefix(key, ':')){\n\t\t\treturn key;\n\t\t} else {\n\t\t\treturn mal::env_get(env, key)?;\n\t\t};\n\tcase let vec: mal::vector =>\n\t\treturn eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\treturn mal::eval_hash(hash, &eval, env)?;\n\tcase let ls: mal::list =>\n\t\tyield ls;\n\tcase =>\n\t\treturn ast;\n\t};\n\n\tif(len(ls.data) == 0) return ast;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*', 'defmacro!' and\n\t// 'def!' forms.\n\tmatch(ls.data[0]){\n\n\tcase let sym: mal::symbol =>\n\n\tswitch(sym){\n\tcase \"quasiquote\" =>\n\t\tast = quasiquote(ls.data[1])?;\n\t\tcontinue;\n\tcase \"quote\" =>\n\t\treturn ls.data[1];\n\tcase \"defmacro!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"defmacro! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet name: mal::symbol = match(ls.data[1]){\n\t\tcase let name: mal::symbol =>\n\t\t\tyield name;\n\t\tcase =>\n\t\t\treturn (\"symbol\",\n\t\t\t\tls.data[1]): mal::type_error;\n\t\t};\n\t\tlet res: mal::macro = match(eval(ls.data[2], env)) {\n\t\tcase let func: mal::function =>\n\t\t\tyield func;\n\t\tcase =>\n\t\t\treturn (\"function\",\n\t\t\t\tls.data[2]): mal::type_error;\n\t\t};\n\t\tmal::env_set(env, name, res);\n\t\treturn res;\n\tcase \"def!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet val = eval(ls.data[2], env)?;\n\n\t\tlet name: mal::symbol = match(ls.data[1]){\n\t\tcase let name: mal::symbol =>\n\t\t\tyield name;\n\t\tcase =>\n\t\t\treturn (\"symbol\", ls.data[1]): mal::type_error;\n\t\t};\n\n\t\tmal::env_set(env, name, val);\n\t\treturn val;\n\n\tcase \"let*\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\tcase let b: mal::list =>\n\t\t\tyield b.data;\n\t\tcase let b: mal::vector =>\n\t\t\tyield b.data;\n\t\tcase =>\n\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t};\n\n\t\tlet let_env = mal::env_init(env);\n\n\t\tfor(let i: size = 0; i < len(bindings); i += 2){\n\n\t\t\tlet name: mal::symbol = match(bindings[i]){\n\t\t\tcase let name: mal::symbol =>\n\t\t\t\tyield name;\n\t\t\tcase =>\n\t\t\t\treturn (\"symbol\", ls.data[1]):\n\t\t\t\t\tmal::type_error;\n\t\t\t};\n\n\t\t\tmal::env_set(let_env, name, eval(bindings[i+1],\n\t\t\t\t\t\t\t let_env)?);\n\t\t};\n\n\t\tenv = let_env;\n\t\tast = ls.data[2];\n\t\tcontinue;\n\tcase \"do\" =>\n\t\tlet result: mal::MalType = mal::nil;\n\t\tfor(let form .. ls.data[1..len(ls.data)-1]){\n\t\t\tresult = eval(form, env)?;\n\t\t};\n\t\tast = ls.data[len(ls.data)-1];\n\t\tcontinue;\n\tcase \"if\" =>\n\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tmatch(eval(ls.data[1], env)?){\n\t\tcase mal::nil =>\n\t\t\tif(len(ls.data) == 4){\n\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase let b: bool =>\n\t\t\tif(b){\n\t\t\t\tast = ls.data[2];\n\t\t\t\tcontinue;\n\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase =>\n\t\t\tast = ls.data[2];\n\t\t\tcontinue;\n\t\t};\n\tcase \"fn*\" =>\n\t\tlet args = match(ls.data[1]){\n\t\tcase let a: mal::vector =>\n\t\t\tyield a.data;\n\t\tcase let a: mal::list =>\n\t\t\tyield a.data;\n\t\t};\n\t\tlet body = match(ls.data[2]){\n\t\tcase let b: mal::MalType =>\n\t\t\tyield b;\n\t\tcase => return mal::nil;\n\t\t};\n\t\treturn mal::make_func(&eval, env, args, body);\n\tcase => void;\n\t};\n\tcase => void;\n\t};\n\n\t// apply\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let mac: mal::macro =>\n\t\tast = _apply(mac, ls.data[1..])?;\n\t\tcontinue;\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tenv = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\tfree(args);\n\t\tast = func.body;\n\t\tcontinue;\n\tcase => return (\"not a function:\", ls.data[0]): mal::syntax_error;\n\t};\n};};\n\nfn _apply(\n\tfunc: (mal::function | mal::intrinsic),\n\targs: []mal::MalType\n) (mal::MalType | mal::error) = {\n\n\tmatch(func){\n\tcase let func: mal::function =>\n\t\tlet env = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\treturn func.eval(func.body, env);\n\tcase let func: mal::intrinsic =>\n\t\treturn func.eval(args);\n\t};\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n\t};\n\nfn rep (input: []u8, env: *mal::env, printp: bool = true) void = {\n\tlet ast = match(read(input)){\n\tcase let e :mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tif(printp) print(result);\n};\n\n\nlet repl_env: nullable *mal::env = null;\n\nfn do_eval(args: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'do_eval': too few arguments\", args):\n\t\t\tmal::syntax_error;\n\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\treturn mal::not_implemented;\n\t};\n\treturn eval(args[0], env);\n};\n\nexport fn main() void = {\n\n\trepl_env = mal::env_init();\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\tfmt::fatal(\"No repl environment initialized!\");\n\t};\n\n\tmal::env_set(env, \"eval\", mal::make_intrinsic(&do_eval));\n\tmal::load_namespace(mal::core, env)!;\n\n\tlet load_file = \"(def! load-file (fn* (f)\n(eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\";\n\n\tlet cond = \"(defmacro! cond\n(fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1)\n(nth xs 1) (throw \\\"odd number of forms to cond\\\"))\n(cons 'cond (rest (rest xs)))))))\";\n\n\trep(strings::toutf8(cond), env, false);\n\trep(strings::toutf8(load_file), env, false);\n\n\t// handle command line arguments\n\tconst args = os::args;\n\n\tlet argvlen: size = if (len(args) > 2) {\n\t\tyield len(args)-2;\n\t} else {\n\t\tyield 0;\n\t};\n\n\tlet argv = mal::make_list(argvlen);\n\n\tif (len(args) > 2){\n\t\tfor(let i: size = 2; i < len(args); i += 1){\n\t\t\targv.data[i-2] = &args[i]: mal::string;\n\t\t};\n\t};\n\n\tmal::env_set(env, \"*ARGV*\", argv);\n\n\tif(len(args) > 1){\n\t\tlet exec_str = strings::join(\"\", \"(load-file \\\"\", args[1],\n\t\t\t\t\t     \"\\\")\")!;\n\t\trep(strings::toutf8(exec_str), env, false);\n\t\tfree(exec_str);\n\t\tos::exit(0);\n\t};\n\n\tfor(true){\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\trep(input, env);\n\t\tfree(input);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/step9_try.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse memio;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn starts_with(ast: mal::MalType, sym: str) bool = {\n\tmatch(ast){\n\tcase let ls: mal::list=>\n\t\tif(len(ls.data) < 1) return false;\n\t\tmatch(ls.data[0]){\n\t\tcase let s: mal::symbol =>\n\t\t\treturn s == sym;\n\t\tcase =>\n\t\t\treturn false;\n\t\t};\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn qq_iter(ast: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tlet acc = mal::make_list(0);\n\n\tfor(let i: size = len(ast); 0 < i ; i -= 1){\n\n\t\tlet elt: mal::MalType  = ast[i - 1];\n\n\t\tif(starts_with(elt, \"splice-unquote\")){\n\t\t\tlet elt: mal::list = match(elt){\n\t\t\tcase let l: mal::list =>\n\t\t\t\tyield l;\n\t\t\tcase =>\n\t\t\t\treturn (\"list\", ast): mal::type_error;\n\t\t\t};\n\n\t\t\tacc = mal::make_list(3, [\"concat\":mal::symbol,\n\t\t\t\t\t\t elt.data[1], acc]);\n\t\t} else {\n\t\t\tacc = mal::make_list(3, [\"cons\":mal::symbol,\n\t\t\t\t\t\t quasiquote(elt)?, acc]);\n\t\t};\n\t};\n\n\treturn acc;\n};\n\nfn quasiquote(ast: mal::MalType) (mal::MalType | mal::error) = {\n\tmatch(ast) {\n\tcase let ls: mal::list =>\n\t\tif(starts_with(ls, \"unquote\")) {\n\t\t\treturn ls.data[1];\n\t\t} else {\n\t\t\treturn qq_iter(ls.data);\n\t\t};\n\tcase let ls: mal::vector =>\n\t\tlet res: mal::list =\n\t\t\tmal::make_list(2, [\"vec\":mal::symbol,\n\t\t\t\t\t   qq_iter(ls.data)?]);\n\t\treturn res;\n\tcase let hm: (mal::symbol | mal::hashmap) =>\n\t\tlet res: mal::list =\n\t\t\tmal::make_list(2, [\"quote\":mal::symbol, ast]);\n\t\treturn res;\n\tcase =>\n\t\treturn ast;\n\t};\n};\n\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\n\tfor(true){\n\n\t\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\t\t\tcase mal::undefined_symbol => void;\n\t\tcase mal::nil => void;\n\t\tcase =>\n\t\t     fmt::print(\"EVAL: \")!;\n\t\t     mal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet ls: mal::list = match(ast){\n\tcase let key: mal::symbol =>\n\t\tif(strings::hasprefix(key, ':')){\n\t\t\treturn key;\n\t\t} else {\n\t\t\treturn mal::env_get(env, key)?;\n\t\t};\n\tcase let vec: mal::vector =>\n\t\treturn eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\treturn mal::eval_hash(hash, &eval, env)?;\n\tcase let ls: mal::list =>\n\t\tyield ls;\n\tcase =>\n\t\treturn ast;\n\t};\n\n\tif(len(ls.data) == 0) return ast;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*', 'defmacro!' and\n\t// 'def!' forms.\n\tmatch(ls.data[0]){\n\n\tcase let sym: mal::symbol =>\n\n\tswitch(sym){\n\tcase \"try*\" =>\n\t\tmatch(eval(ls.data[1], env)){\n\t\tcase let e: mal::error =>\n\t\t\tlet s: mal::MalType = match(e){\n\t\t\tcase let e: mal::malerror =>\n\t\t\t\tyield e.1;\n\t\t\tcase =>\n\t\t\t\tlet buf = memio::dynamic();\n\t\t\t\tmal::format_error(&buf, e);\n\t\t\t\tlet s = memio::string(&buf)!;\n\t\t\t\tlet ret = mal::make_string(s);\n\t\t\t\tio::close(&buf)!;\n\t\t\t\tyield ret;\n\t\t\t};\n\n\t\t\tenv = mal::env_init(env);\n\n\t\t\tif (len(ls.data) < 3) return e;\n\n\t\t\tmatch(ls.data[2]){\n\t\t\tcase let l: mal::list =>\n\t\t\t\tif(!(starts_with(l, \"catch*\")))\n\t\t\t\t\treturn (\"expected catch* phrase\",\n\t\t\t\t\t\tl): mal::syntax_error;\n\t\t\t\tmal::env_set(\n\t\t\t\t\tenv, l.data[1] as mal::symbol, s);\n\t\t\t\tast = l.data[2];\n\t\t\t\tcontinue;\n\t\t\tcase =>\n\t\t\t\treturn (\"list\", ls): mal::type_error;\n\t\t\t};\n\t\tcase let c: mal::MalType=>\n\t\t\treturn c;\n\t\t};\n\tcase \"quasiquote\" =>\n\t\tast = quasiquote(ls.data[1])?;\n\t\tcontinue;\n\tcase \"quote\" =>\n\t\treturn ls.data[1];\n\tcase \"defmacro!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"defmacro! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tlet name: mal::symbol = match(ls.data[1]){\n\t\tcase let name: mal::symbol =>\n\t\t\tyield name;\n\t\tcase =>\n\t\t\treturn (\"symbol\", ls.data[1]): mal::type_error;\n\t\t};\n\t\tlet res: mal::macro = match(eval(ls.data[2], env)) {\n\t\tcase let func: mal::function =>\n\t\t\tyield func;\n\t\tcase =>\n\t\t\treturn (\"function\", ls.data[2]): mal::type_error;\n\t\t};\n\t\tmal::env_set(env, name, res);\n\t\treturn res;\n\tcase \"def!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet val = eval(ls.data[2], env)?;\n\n\t\tlet name: mal::symbol = match(ls.data[1]){\n\t\tcase let name: mal::symbol =>\n\t\t\tyield name;\n\t\tcase =>\n\t\t\treturn (\"symbol\", ls.data[1]): mal::type_error;\n\t\t};\n\n\t\tmal::env_set(env, name, val);\n\t\treturn val;\n\n\tcase \"let*\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"let*: too few arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\tcase let b: mal::list =>\n\t\t\tyield b.data;\n\t\tcase let b: mal::vector =>\n\t\t\tyield b.data;\n\t\tcase =>\n\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t};\n\n\t\tlet let_env = mal::env_init(env);\n\n\t\tfor(let i: size = 0; i < len(bindings); i += 2){\n\n\t\t\tlet name: mal::symbol = match(bindings[i]){\n\t\t\tcase let name: mal::symbol =>\n\t\t\t\tyield name;\n\t\t\tcase =>\n\t\t\t\treturn (\"symbol\",\n\t\t\t\t\tls.data[1]): mal::type_error;\n\t\t\t};\n\n\t\t\tmal::env_set(let_env, name, eval(bindings[i+1],\n\t\t\t\t\t\t\t let_env)?);\n\t\t};\n\n\t\tenv = let_env;\n\t\tast = ls.data[2];\n\t\tcontinue;\n\tcase \"do\" =>\n\t\tlet result: mal::MalType = mal::nil;\n\t\tfor(let form .. ls.data[1..len(ls.data)-1]){\n\t\t\tresult = eval(form, env)?;\n\t\t};\n\t\tast = ls.data[len(ls.data)-1];\n\t\tcontinue;\n\tcase \"if\" =>\n\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tmatch(eval(ls.data[1], env)?){\n\t\tcase mal::nil =>\n\t\t\tif(len(ls.data) == 4){\n\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase let b: bool =>\n\t\t\tif(b){\n\t\t\t\tast = ls.data[2];\n\t\t\t\tcontinue;\n\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase =>\n\t\t\tast = ls.data[2];\n\t\t\tcontinue;\n\t\t};\n\tcase \"fn*\" =>\n\t\tlet args = match(ls.data[1]){\n\t\tcase let a: mal::vector =>\n\t\t\tyield a.data;\n\t\tcase let a: mal::list =>\n\t\t\tyield a.data;\n\t\t};\n\t\tlet body = match(ls.data[2]){\n\t\tcase let b: mal::MalType =>\n\t\t\tyield b;\n\t\tcase => return mal::nil;\n\t\t};\n\t\treturn mal::make_func(&eval, env, args, body);\n\tcase => void;\n\t};\n\tcase => void;\n\t};\n\n\t// apply\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let mac: mal::macro =>\n\t\tast = _apply(mac, ls.data[1..])?;\n\t\tcontinue;\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tenv = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\tfree(args);\n\t\tast = func.body;\n\t\tcontinue;\n\tcase => return (\"not a function:\", ls.data[0]): mal::syntax_error;\n\t};\n};};\n\nfn _apply(func: (mal::function | mal::intrinsic), args: []mal::MalType)\n\t(mal::MalType | mal::error) = {\n\n\tmatch(func){\n\tcase let func: mal::function =>\n\t\tlet env = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\treturn func.eval(func.body, env);\n\tcase let func: mal::intrinsic =>\n\t\treturn func.eval(args);\n\t};\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n\t};\n\nfn rep (input: []u8, env: *mal::env, printp: bool = true) void = {\n\tlet ast = match(read(input)){\n\tcase let e: mal::error =>\n\t\tfmt::errorln(\"Exception:\")!;\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\tfmt::errorln(\"Exception:\")!;\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tif(printp) print(result);\n};\n\nlet repl_env: nullable *mal::env = null;\n\nfn do_eval(args: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'do_eval': too few arguments\", args):\n\t\t\tmal::syntax_error;\n\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\treturn mal::not_implemented;\n\t};\n\treturn eval(args[0], env);\n};\n\nexport fn main() void = {\n\n\trepl_env = mal::env_init();\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\tfmt::fatal(\"No repl environment initialized!\");\n\t};\n\n\tmal::env_set(env, \"eval\", mal::make_intrinsic(&do_eval));\n\tmal::load_namespace(mal::core, env)!;\n\n\tlet load_file = \"(def! load-file\n\t\t(fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n\t\t\\\"\\nnil)\\\")))))\";\n\n\tlet cond = \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0)\n\t\t(list 'if (first xs) (if (> (count xs) 1) (nth xs 1)\n\t\t(throw \\\"odd number of forms to cond\\\")) (cons 'cond\n\t\t(rest (rest xs)))))))\";\n\n\trep(strings::toutf8(cond), env, false);\n\trep(strings::toutf8(load_file), env, false);\n\n\t// handle command line arguments\n\tconst args = os::args;\n\n\tlet argvlen: size = if (len(args) > 2) {\n\t\tyield len(args)-2;\n\t} else {\n\t\tyield 0;\n\t};\n\n\tlet argv = mal::make_list(argvlen);\n\n\tif (len(args) > 2){\n\t\tfor(let i: size = 2; i < len(args); i += 1){\n\t\t\targv.data[i-2] = &args[i]: mal::string;\n\t\t};\n\t};\n\n\tmal::env_set(env, \"*ARGV*\", argv);\n\n\tif(len(args) > 1){\n\t\tlet exec_str = strings::join(\"\", \"(load-file \\\"\", args[1],\n\t\t\t\"\\\")\")!;\n\t\trep(strings::toutf8(exec_str), env, false);\n\t\tfree(exec_str);\n\t\tos::exit(0);\n\t};\n\n\tfor(true){\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\trep(input, env);\n\t\tfree(input);\n\t};\n};\n"
  },
  {
    "path": "impls/hare/stepA_mal.ha",
    "content": "use bufio;\nuse fmt;\nuse io;\nuse mal;\nuse memio;\nuse os;\nuse strings;\n\nfn read (input: []u8) (mal::MalType | io::EOF | mal::error) = {\n\treturn mal::read_str(input);\n};\n\nfn eval_vec(vec: mal::vector, env: *mal::env) (mal::vector | mal::error) ={\n\n\tif(len(vec.data) == 0) return vec;\n\tlet res: mal::vector = mal::make_vec(len(vec.data));\n\n\tfor(let i: size = 0; i < len(vec.data); i += 1){\n\t\tres.data[i] = eval(vec.data[i], env)?;\n\t};\n\treturn res;\n};\n\nfn starts_with(ast: mal::MalType, sym: str) bool = {\n\tmatch(ast){\n\tcase let ls: mal::list=>\n\t\tif(len(ls.data) < 1) return false;\n\t\tmatch(ls.data[0]){\n\t\tcase let s: mal::symbol =>\n\t\t\treturn s == sym;\n\t\tcase =>\n\t\t\treturn false;\n\t\t};\n\tcase =>\n\t\treturn false;\n\t};\n};\n\nfn qq_iter(ast: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tlet acc = mal::make_list(0);\n\n\tfor(let i: size = len(ast); 0 < i ; i -= 1){\n\n\t\tlet elt: mal::MalType  = ast[i - 1];\n\n\t\tif(starts_with(elt, \"splice-unquote\")){\n\t\t\tlet elt: mal::list = match(elt){\n\t\t\tcase let l: mal::list =>\n\t\t\t\tyield l;\n\t\t\tcase =>\n\t\t\t\treturn (\"list\", ast): mal::type_error;\n\t\t\t};\n\n\t\t\tacc = mal::make_list(3, [\"concat\":mal::symbol,\n\t\t\t\t\t\t elt.data[1], acc]);\n\t\t} else {\n\t\t\tacc = mal::make_list(3, [\"cons\":mal::symbol,\n\t\t\t\t\t\t quasiquote(elt)?, acc]);\n\t\t};\n\t};\n\n\treturn acc;\n};\n\nfn quasiquote(ast: mal::MalType) (mal::MalType | mal::error) = {\n\tmatch(ast) {\n\tcase let ls: mal::list =>\n\t\tif(starts_with(ls, \"unquote\")) {\n\t\t\treturn ls.data[1];\n\t\t} else {\n\t\t\treturn qq_iter(ls.data);\n\t\t};\n\tcase let ls: mal::vector =>\n\t\tlet res: mal::list =\n\t\t\tmal::make_list(2, [\"vec\":mal::symbol,\n\t\t\t\t\t   qq_iter(ls.data)?]);\n\t\treturn res;\n\tcase let hm: (mal::symbol | mal::hashmap) =>\n\t\tlet res: mal::list =\n\t\t\tmal::make_list(2, [\"quote\":mal::symbol, ast]);\n\t\treturn res;\n\tcase =>\n\t\treturn ast;\n\t};\n};\n\nfn eval (ast: mal::MalType, env: *mal::env) (mal::MalType | mal::error) = {\n\n\tfor(true){\n\n\tmatch(mal::env_get(env, \"DEBUG-EVAL\")){\n\tcase mal::undefined_symbol => void;\n\tcase mal::nil => void;\n\tcase =>\n\t\tfmt::print(\"EVAL: \")!;\n\t\tmal::print_form(os::stdout, ast);\n\t\tfmt::print(\"\\n\")!;\n\t\tmal::print_form(os::stdout, env.data);\n\t\tfmt::print(\"\\n\")!;\n\t};\n\n\tlet ls: mal::list = match(ast){\n\tcase let key: mal::symbol =>\n\t\tif(strings::hasprefix(key, ':')){\n\t\t\treturn key;\n\t\t} else {\n\t\t\treturn mal::env_get(env, key)?;\n\t\t};\n\tcase let vec: mal::vector =>\n\t\treturn eval_vec(vec, env)?;\n\tcase let hash: mal::hashmap =>\n\t\treturn mal::eval_hash(hash, &eval, env)?;\n\tcase let ls: mal::list =>\n\t\tyield ls;\n\tcase =>\n\t\treturn ast;\n\t};\n\n\tif(len(ls.data) == 0) return ast;\n\n\t// handle special cases of 'if' 'fn*', 'do', 'let*', 'defmacro!' and\n\t// 'def!' forms.\n\tmatch(ls.data[0]){\n\n\tcase let sym: mal::symbol =>\n\n\tswitch(sym){\n\tcase \"try*\" =>\n\t\tmatch(eval(ls.data[1], env)){\n\t\tcase let e: mal::error =>\n\t\t\tlet s: mal::MalType = match(e){\n\t\t\tcase let e: mal::malerror =>\n\t\t\t\tyield e.1;\n\t\t\tcase =>\n\t\t\t\tlet buf = memio::dynamic();\n\t\t\t\tmal::format_error(&buf, e);\n\t\t\t\tlet s = memio::string(&buf)!;\n\t\t\t\tlet ret = mal::make_string(s);\n\t\t\t\tio::close(&buf)!;\n\t\t\t\tyield ret;\n\t\t\t};\n\n\t\t\tenv = mal::env_init(env);\n\n\t\t\tif (len(ls.data) < 3) return e;\n\n\t\t\tmatch(ls.data[2]){\n\t\t\tcase let l: mal::list =>\n\t\t\t\tif(!(starts_with(l, \"catch*\")))\n\t\t\t\t\treturn (\"expected catch* phrase\",\n\t\t\t\t\t\tl): mal::syntax_error;\n\t\t\t\tmal::env_set(env,\n\t\t\t\t\t     l.data[1] as mal::symbol, s);\n\t\t\t\tast = l.data[2];\n\t\t\t\tcontinue;\n\t\t\tcase =>\n\t\t\t\treturn (\"list\", ls): mal::type_error;\n\t\t\t};\n\t\tcase let c: mal::MalType=>\n\t\t\treturn c;\n\t\t};\n\tcase \"quasiquote\" =>\n\t\tast = quasiquote(ls.data[1])?;\n\t\tcontinue;\n\tcase \"quote\" =>\n\t\treturn ls.data[1];\n\tcase \"defmacro!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"defmacro! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tlet name: mal::symbol = match(ls.data[1]){\n\t\tcase let name: mal::symbol =>\n\t\t\tyield name;\n\t\tcase =>\n\t\t\treturn (\"symbol\", ls.data[1]): mal::type_error;\n\t\t};\n\t\tlet res: mal::macro = match(eval(ls.data[2], env)) {\n\t\tcase let func: mal::function =>\n\t\t\tyield func;\n\t\tcase =>\n\t\t\treturn (\"function\", ls.data[2]): mal::type_error;\n\t\t};\n\t\tmal::env_set(env, name, res);\n\t\treturn res;\n\tcase \"def!\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"def! expects 2 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\n\t\tlet val = eval(ls.data[2], env)?;\n\n\t\tlet name: mal::symbol = match(ls.data[1]){\n\t\tcase let name: mal::symbol =>\n\t\t\tyield name;\n\t\tcase =>\n\t\t\treturn (\"symbol\", ls.data[1]): mal::type_error;\n\t\t};\n\n\t\tmal::env_set(env, name, val);\n\t\treturn val;\n\n\tcase \"let*\" =>\n\t\tif(len(ls.data) != 3)\n\t\t\treturn (\"let*: too few arguments\", ls):\n\t\t\t\tmal::syntax_error;\n\n\t\tlet bindings: []mal::MalType = match(ls.data[1]){\n\t\tcase let b: mal::list =>\n\t\t\tyield b.data;\n\t\tcase let b: mal::vector =>\n\t\t\tyield b.data;\n\t\tcase =>\n\t\t\treturn (\"let*\", ls): mal::syntax_error;\n\t\t};\n\n\t\tlet let_env = mal::env_init(env);\n\n\t\tfor(let i: size = 0; i < len(bindings); i += 2){\n\n\t\t\tlet name: mal::symbol = match(bindings[i]){\n\t\t\tcase let name: mal::symbol =>\n\t\t\t\tyield name;\n\t\t\tcase =>\n\t\t\t\treturn (\"symbol\", ls.data[1]):\n\t\t\t\t\tmal::type_error;\n\t\t\t};\n\n\t\t\tmal::env_set(let_env, name, eval(bindings[i+1],\n\t\t\t\t\t\t\t let_env)?);\n\t\t};\n\n\t\tenv = let_env;\n\t\tast = ls.data[2];\n\t\tcontinue;\n\tcase \"do\" =>\n\t\tlet result: mal::MalType = mal::nil;\n\t\tfor(let form .. ls.data[1..len(ls.data)-1]){\n\t\t\tresult = eval(form, env)?;\n\t\t};\n\t\tast = ls.data[len(ls.data)-1];\n\t\tcontinue;\n\tcase \"if\" =>\n\t\tif(len(ls.data) > 4 || len(ls.data) < 3)\n\t\t\treturn (\"if expects 2 or 3 arguments\",\n\t\t\t\tls): mal::syntax_error;\n\t\tmatch(eval(ls.data[1], env)?){\n\t\tcase mal::nil =>\n\t\t\tif(len(ls.data) == 4){\n\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase let b: bool =>\n\t\t\tif(b){\n\t\t\t\tast = ls.data[2];\n\t\t\t\tcontinue;\n\t\t\t} else if(len(ls.data) == 4){\n\t\t\t\tast = ls.data[3];\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\treturn mal::nil;\n\t\t\t};\n\t\tcase =>\n\t\t\tast = ls.data[2];\n\t\t\tcontinue;\n\t\t};\n\tcase \"fn*\" =>\n\t\tlet args = match(ls.data[1]){\n\t\tcase let a: mal::vector =>\n\t\t\tyield a.data;\n\t\tcase let a: mal::list =>\n\t\t\tyield a.data;\n\t\t};\n\t\tlet body = match(ls.data[2]){\n\t\tcase let b: mal::MalType =>\n\t\t\tyield b;\n\t\tcase => return mal::nil;\n\t\t};\n\t\treturn mal::make_func(&eval, env, args, body);\n\tcase => void;\n\t};\n\tcase => void;\n\t};\n\n\t// apply\n\n\tmatch(eval(ls.data[0], env)?){\n\tcase let func: mal::intrinsic =>\n\t\tlet args: []mal::MalType = [];\n\t\tdefer free(args);\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\treturn func.eval(args);\n\tcase let mac: mal::macro =>\n\t\tast = _apply(mac, ls.data[1..])?;\n\t\tcontinue;\n\tcase let func: mal::function =>\n\t\tlet args: []mal::MalType = [];\n\t\tfor(let arg .. ls.data[1..]){\n\t\t\tappend(args, eval(arg, env)?)!;\n\t\t};\n\t\tenv = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\tfree(args);\n\t\tast = func.body;\n\t\tcontinue;\n\tcase => return (\"not a function:\", ls.data[0]): mal::syntax_error;\n\t};\n};};\n\nfn _apply(func: (mal::function | mal::intrinsic), args: []mal::MalType)\n\t(mal::MalType | mal::error) = {\n\n\tmatch(func){\n\tcase let func: mal::function =>\n\t\tlet env = mal::env_init(func.envi);\n\t\tmal::env_bind(env, func.args, args);\n\t\treturn func.eval(func.body, env);\n\tcase let func: mal::intrinsic =>\n\t\treturn func.eval(args);\n\t};\n};\n\nfn print (input: mal::MalType) void = {\n\tmal::print_form(os::stdout, input);\n\tfmt::print(\"\\n\")!;\n\t};\n\nfn rep (input: []u8, env: *mal::env, printp: bool = true) void = {\n\tlet ast = match(read(input)){\n\tcase let e: mal::error =>\n\t\tfmt::errorln(\"Exception:\")!;\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\tcase io::EOF =>\n\t\treturn void;\n\t};\n\n\tlet result = match(eval(ast, env)){\n\tcase let e: mal::error =>\n\t\tfmt::errorln(\"Exception:\")!;\n\t\treturn mal::format_error(os::stderr, e);\n\tcase let form: mal::MalType =>\n\t\tyield form;\n\t};\n\n\tif(printp) print(result);\n\n\tmal::run_gc(env);\n};\n\nlet repl_env: nullable *mal::env = null;\n\nfn do_eval(args: []mal::MalType) (mal::MalType | mal::error) = {\n\n\tif(len(args) < 1)\n\t\treturn (\"'do_eval': too few arguments\", args):\n\t\t\tmal::syntax_error;\n\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\treturn mal::not_implemented;\n\t};\n\treturn eval(args[0], env);\n};\n\nexport fn main() void = {\n\n\trepl_env = mal::env_init();\n\tconst env = match(repl_env){\n\tcase let env: *mal::env =>\n\t\tyield env;\n\tcase =>\n\t\tfmt::fatal(\"No repl environment initialized!\");\n\t};\n\n\tmal::env_set(env, \"*host-language*\", mal::make_string(\"hare\"));\n\tmal::env_set(env, \"eval\", mal::make_intrinsic(&do_eval));\n\tmal::load_namespace(mal::core, env)!;\n\n\tlet load_file = \"(def! load-file\n\t\t(fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f)\n\t\t\\\"\\nnil)\\\")))))\";\n\n\tlet cond = \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0)\n\t\t(list 'if (first xs) (if (> (count xs) 1) (nth xs 1)\n\t\t(throw \\\"odd number of forms to cond\\\")) (cons 'cond\n\t\t(rest (rest xs)))))))\";\n\n\trep(strings::toutf8(load_file), env, false);\n\trep(strings::toutf8(cond), env, false);\n\n\t// handle command line arguments\n\tconst args = os::args;\n\n\tlet argvlen: size = if (len(args) > 2) {\n\t\tyield len(args)-2;\n\t} else {\n\t\tyield 0;\n\t};\n\n\tlet argv = mal::make_list(argvlen);\n\n\tif (len(args) > 2){\n\t\tfor(let i: size = 2; i < len(args); i += 1){\n\t\t\targv.data[i-2] = &args[i]: mal::string;\n\t\t};\n\t};\n\n\tmal::env_set(env, \"*ARGV*\", argv);\n\n\tif(len(args) > 1){\n\t\tlet exec_str = strings::join(\"\", \"(load-file \\\"\", args[1],\n\t\t\t\"\\\")\")!;\n\t\trep(strings::toutf8(exec_str), env, false);\n\t\tfree(exec_str);\n\t\tos::exit(0);\n\t};\n\n\trep(strings::toutf8(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"),\n\t\tenv, false);\n\n\tfor(true){\n\t\tfmt::printf(\"user> \")!;\n\t\tbufio::flush(os::stdout)!;\n\n\t\tconst input =  match(bufio::read_line(os::stdin)){\n\t\tcase let input: []u8 =>\n\t\t\tyield input;\n\t\tcase io::EOF =>\n\t\t\tbreak;\n\t\tcase io::error =>\n\t\t\tbreak;\n\t\t};\n\n\t\trep(input, env);\n\t\tfree(input);\n\t};\n};\n"
  },
  {
    "path": "impls/haskell/Core.hs",
    "content": "module Core\n( ns )\nwhere\n\nimport Control.Monad.Except (throwError)\nimport Control.Monad.Trans (liftIO)\nimport qualified Data.Map.Strict as Map\nimport Data.Time.Clock.POSIX (getPOSIXTime)\nimport Data.IORef (newIORef, readIORef, writeIORef)\n\nimport Readline (readline)\nimport Reader (read_str)\nimport Types\nimport Printer (_pr_list)\n\n-- General functions\n\nequal_Q :: Fn\nequal_Q [a, b] = return $ MalBoolean $ a == b\nequal_Q _ = throwStr \"illegal arguments to =\"\n\n-- Error/Exception functions\n\nthrow :: Fn\nthrow [mv] = throwError mv\nthrow _ = throwStr \"illegal arguments to throw\"\n\n-- Unary predicates\n\npred1 :: String -> (MalVal -> Bool) -> (String, Fn)\npred1 name op = (name, fn) where\n  fn :: Fn\n  fn [a] = return $ MalBoolean $ op a\n  fn _ = throwStr $ \"illegal arguments to \" ++ name\n\natom_Q :: MalVal -> Bool\natom_Q (MalAtom _ _) = True\natom_Q _             = False\n\nfalse_Q :: MalVal -> Bool\nfalse_Q (MalBoolean False) = True\nfalse_Q _                  = False\n\nfn_Q :: MalVal -> Bool\nfn_Q (MalFunction _ _) = True\nfn_Q _                 = False\n\nmacro_Q :: MalVal -> Bool\nmacro_Q (MalMacro _) = True\nmacro_Q _            = False\n\nmap_Q :: MalVal -> Bool\nmap_Q (MalHashMap _ _) = True\nmap_Q _                = False\n\nkeyword_Q :: MalVal -> Bool\nkeyword_Q (MalKeyword _) = True\nkeyword_Q _              = False\n\nlist_Q :: MalVal -> Bool\nlist_Q (MalSeq _ (Vect False) _) = True\nlist_Q _                         = False\n\nnil_Q :: MalVal -> Bool\nnil_Q Nil = True\nnil_Q _   = False\n\nnumber_Q :: MalVal -> Bool\nnumber_Q (MalNumber _) = True\nnumber_Q _             = False\n\nstring_Q :: MalVal -> Bool\nstring_Q (MalString _) = True\nstring_Q _             = False\n\nsymbol_Q :: MalVal -> Bool\nsymbol_Q (MalSymbol _) = True\nsymbol_Q _             = False\n\ntrue_Q :: MalVal -> Bool\ntrue_Q (MalBoolean True) = True\ntrue_Q _                 = False\n\nvector_Q :: MalVal -> Bool\nvector_Q (MalSeq _ (Vect True) _) = True\nvector_Q _                        = False\n\n-- Scalar functions\n\nsymbol :: Fn\nsymbol [MalString s] = return $ MalSymbol s\nsymbol _ = throwStr \"symbol called with non-string\"\n\nkeyword :: Fn\nkeyword [kw@(MalKeyword _)] = return kw\nkeyword [MalString s] = return $ MalKeyword s\nkeyword _ = throwStr \"keyword called with non-string\"\n\n-- String functions\n\npr_str :: Fn\npr_str args = liftIO $ MalString <$> _pr_list True \" \" args\n\nstr :: Fn\nstr args = liftIO $ MalString <$> _pr_list False \"\" args\n\nprn :: Fn\nprn args = liftIO $ do\n    putStrLn =<< _pr_list True \" \" args\n    return Nil\n\nprintln :: Fn\nprintln args = liftIO $ do\n    putStrLn =<< _pr_list False \" \" args\n    return Nil\n\nslurp :: Fn\nslurp [MalString path] = MalString <$> liftIO (readFile path)\nslurp _ = throwStr \"invalid arguments to slurp\"\n\ndo_readline :: Fn\ndo_readline [MalString prompt] = do\n    maybeLine <- liftIO $ readline prompt\n    case maybeLine of\n        Nothing -> return Nil\n        Just line -> return $ MalString line\ndo_readline _ = throwStr \"invalid arguments to readline\"\n\nread_string :: Fn\nread_string [MalString s] = read_str s\nread_string _ = throwStr \"invalid read-string\"\n\n-- Numeric functions\n\nnum_op :: String -> (Int -> Int -> a) -> (a -> MalVal) -> (String, Fn)\nnum_op name op constructor = (name, fn) where\n  fn :: Fn\n  fn [MalNumber a, MalNumber b] = return $ constructor $ op a b\n  fn _ = throwStr $ \"illegal arguments to \" ++ name\n\ntime_ms :: Fn\ntime_ms [] = MalNumber . round . (* 1000) <$> liftIO getPOSIXTime\ntime_ms _ = throwStr \"invalid time-ms\"\n\n\n-- List functions\n\nlist :: Fn\nlist = return . toList\n\n-- Vector functions\n\nvector :: Fn\nvector = return . MalSeq (MetaData Nil) (Vect True)\n\n-- Hash Map functions\n\nhash_map :: Fn\nhash_map kvs = case kv2map Map.empty kvs of\n        Just m     -> return m\n        Nothing    -> throwStr \"invalid call to hash-map\"\n\nassoc :: Fn\nassoc (MalHashMap _ hm : kvs) = case kv2map hm kvs of\n        Just m     -> return m\n        Nothing    -> throwStr \"invalid assoc\"\nassoc _ = throwStr \"invalid call to assoc\"\n\ndissoc :: Fn\ndissoc (MalHashMap _ hm : ks) = MalHashMap (MetaData Nil) . foldl\n    (flip Map.delete) hm <$> mapM encodeKey ks\ndissoc _ = throwStr \"invalid call to dissoc\"\n\nget :: Fn\nget [MalHashMap _ hm, k] = orNil . flip Map.lookup hm <$> encodeKey k\n  where\n    orNil (Just v) = v\n    orNil Nothing  = Nil\nget [Nil, k] = const Nil <$> encodeKey k\nget _ = throwStr \"invalid call to get\"\n\ncontains_Q :: Fn\ncontains_Q [MalHashMap _ m, k] = MalBoolean . flip Map.member m <$> encodeKey k\ncontains_Q [Nil, k] = MalBoolean . const False <$> encodeKey k\ncontains_Q _ = throwStr \"invalid call to contains?\"\n\nkeys :: Fn\nkeys [MalHashMap _ hm] = return $ toList $ decodeKey <$> Map.keys hm\nkeys _ = throwStr \"invalid call to keys\"\n\nvals :: Fn\nvals [MalHashMap _ hm] = return $ toList $ Map.elems hm\nvals _ = throwStr \"invalid call to vals\"\n\n-- Sequence functions\n\nsequential_Q :: MalVal -> Bool\nsequential_Q (MalSeq _ _ _) = True\nsequential_Q _              = False\n\ncons :: Fn\ncons [x, Nil           ] = return $ toList [x]\ncons [x, MalSeq _ _ lst] = return $ toList (x : lst)\ncons _ = throwStr \"illegal call to cons\"\n\nunwrapSeq :: MalVal -> IOThrows [MalVal]\nunwrapSeq (MalSeq _ _ xs) = return xs\nunwrapSeq _ = throwStr \"invalid concat\"\n\ndo_concat :: Fn\ndo_concat args = toList . concat <$> mapM unwrapSeq args\n\nvec :: Fn\nvec [MalSeq _ _ xs] = return $ MalSeq (MetaData Nil) (Vect True) xs\nvec [_]             = throwStr \"vec: arg type\"\nvec _               = throwStr \"vec: arg count\"\n\nnth :: Fn\nnth [MalSeq _ _ lst, MalNumber idx] =\n    case drop idx lst of\n        x : _ -> return x\n        []    -> throwStr \"nth: index out of range\"\n--  See https://wiki.haskell.org/Avoiding_partial_functions\nnth _ = throwStr \"invalid call to nth\"\n\nfirst :: Fn\nfirst [Nil               ] = return Nil\nfirst [MalSeq _ _ []     ] = return Nil\nfirst [MalSeq _ _ (x : _)] = return x\nfirst _ = throwStr \"illegal call to first\"\n\nrest :: Fn\nrest [Nil                ] = return $ toList []\nrest [MalSeq _ _ []      ] = return $ toList []\nrest [MalSeq _ _ (_ : xs)] = return $ toList xs\nrest _ = throwStr \"illegal call to rest\"\n\nempty_Q :: Fn\nempty_Q [Nil]           = return $ MalBoolean True\nempty_Q [MalSeq _ _ xs] = return $ MalBoolean $ xs == []\nempty_Q _               = throwStr \"illegal call to empty?\"\n\ncount :: Fn\ncount [Nil           ] = return $ MalNumber 0\ncount [MalSeq _ _ lst] = return $ MalNumber $ length lst\ncount _ = throwStr \"non-sequence passed to count\"\n\nconcatLast :: [MalVal] -> IOThrows [MalVal]\nconcatLast [MalSeq _ _ lst] = return lst\nconcatLast (a : as)         = (a :) <$> concatLast as\nconcatLast _ = throwStr \"last argument of apply must be a sequence\"\n\napply :: Fn\napply (MalFunction _ f : xs) = f =<< concatLast xs\napply (MalMacro      f : xs) = f =<< concatLast xs\napply _ = throwStr \"Illegal call to apply\"\n\ndo_map :: Fn\ndo_map [MalFunction _ f, MalSeq _ _ args] = toList <$> mapM (\\x -> f [x]) args\ndo_map _ = throwStr \"Illegal call to map\"\n\nconj :: Fn\nconj (MalSeq _ (Vect False) lst : args) = return $ toList $ reverse args ++ lst\nconj (MalSeq _ (Vect True)  lst : args) = return $ MalSeq (MetaData Nil) (Vect True) $ lst ++ args\nconj _ = throwStr \"illegal arguments to conj\"\n\ndo_seq :: Fn\ndo_seq [Nil            ] = return Nil\ndo_seq [MalSeq _ _ []  ] = return Nil\ndo_seq [MalSeq _ _ lst ] = return $ toList lst\ndo_seq [MalString \"\"   ] = return Nil\ndo_seq [MalString s    ] = return $ toList $ MalString <$> pure <$> s\ndo_seq _ = throwStr \"seq: called on non-sequence\"\n\n-- Metadata functions\n\nwith_meta :: Fn\nwith_meta [MalSeq _ v x,       m] = return $ MalSeq (MetaData m) v x\nwith_meta [MalHashMap _ x,     m] = return $ MalHashMap (MetaData m) x\nwith_meta [MalAtom _ x,        m] = return $ MalAtom (MetaData m) x\nwith_meta [MalFunction _ f,    m] = return $ MalFunction (MetaData m) f\nwith_meta _ = throwStr \"invalid with-meta call\"\n\ndo_meta :: Fn\ndo_meta [MalSeq (MetaData m) _ _  ] = return m\ndo_meta [MalHashMap (MetaData m) _] = return m\ndo_meta [MalAtom (MetaData m) _   ] = return m\ndo_meta [MalFunction (MetaData m) _] = return m\ndo_meta _ = throwStr \"invalid meta call\"\n\n-- Atom functions\n\natom :: Fn\natom [val] = MalAtom (MetaData Nil) <$> liftIO (newIORef val)\natom _ = throwStr \"invalid atom call\"\n\nderef :: Fn\nderef [MalAtom _ ref] = liftIO $ readIORef ref\nderef _ = throwStr \"invalid deref call\"\n\nreset_BANG :: Fn\nreset_BANG [MalAtom _ ref, val] = do\n    liftIO $ writeIORef ref val\n    return val\nreset_BANG _ = throwStr \"invalid reset!\"\n\nswap_BANG :: Fn\nswap_BANG (MalAtom _ ref : MalFunction _ f : args) = do\n    val <- liftIO $ readIORef ref\n    new_val <- f (val : args)\n    liftIO $ writeIORef ref new_val\n    return new_val\nswap_BANG _ = throwStr \"Illegal swap!\"\n\nns :: [(String, Fn)]\nns = [\n    (\"=\",           equal_Q),\n    (\"throw\",       throw),\n    (pred1 \"nil?\"   nil_Q),\n    (pred1 \"true?\"  true_Q),\n    (pred1 \"false?\" false_Q),\n    (pred1 \"string?\" string_Q),\n    (\"symbol\",      symbol),\n    (pred1 \"symbol?\" symbol_Q),\n    (\"keyword\",     keyword),\n    (pred1 \"keyword?\" keyword_Q),\n    (pred1 \"number?\" number_Q),\n    (pred1 \"fn?\"    fn_Q),\n    (pred1 \"macro?\" macro_Q),\n\n    (\"pr-str\",      pr_str),\n    (\"str\",         str),\n    (\"prn\",         prn),\n    (\"println\",     println),\n    (\"readline\",    do_readline),\n    (\"read-string\", read_string),\n    (\"slurp\",       slurp),\n\n    num_op \"<\"  (<)  MalBoolean,\n    num_op \"<=\" (<=) MalBoolean,\n    num_op \">\"  (>)  MalBoolean,\n    num_op \">=\" (>=) MalBoolean,\n    num_op \"+\"  (+)  MalNumber,\n    num_op \"-\"  (-)  MalNumber,\n    num_op \"*\"  (*)  MalNumber,\n    num_op \"/\"  div  MalNumber,\n    (\"time-ms\",     time_ms),\n\n    (\"list\",        list),\n    (pred1 \"list?\" list_Q),\n    (\"vector\",      vector),\n    (pred1 \"vector?\" vector_Q),\n    (\"hash-map\",    hash_map),\n    (pred1 \"map?\" map_Q),\n    (\"assoc\",       assoc),\n    (\"dissoc\",      dissoc),\n    (\"get\",         get),\n    (\"contains?\",   contains_Q),\n    (\"keys\",        keys),\n    (\"vals\",        vals),\n\n    (pred1 \"sequential?\" sequential_Q),\n    (\"cons\",        cons),\n    (\"concat\",      do_concat),\n    (\"vec\",         vec),\n    (\"nth\",         nth),\n    (\"first\",       first),\n    (\"rest\",        rest),\n    (\"empty?\",      empty_Q),\n    (\"count\",       count),\n    (\"apply\",       apply),\n    (\"map\",         do_map),\n\n    (\"conj\",        conj),\n    (\"seq\",         do_seq),\n\n    (\"with-meta\",   with_meta),\n    (\"meta\",        do_meta),\n    (\"atom\",        atom),\n    (pred1 \"atom?\" atom_Q),\n    (\"deref\",       deref),\n    (\"reset!\",      reset_BANG),\n    (\"swap!\",       swap_BANG)]\n"
  },
  {
    "path": "impls/haskell/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install make python\nRUN apt-get install -y ghc libghc-readline-dev\n"
  },
  {
    "path": "impls/haskell/Env.hs",
    "content": "module Env\n( Env, env_get, env_new, env_put, env_set )\nwhere\n\nimport Data.IORef (IORef, modifyIORef, newIORef, readIORef)\nimport qualified Data.Map.Strict as Map\n\nimport Printer (_pr_str)\nimport Types\n\ndata Env = Env (Maybe Env) (IORef (Map.Map String MalVal))\n\nenv_new :: Maybe Env -> IO Env\nenv_new outer = Env outer <$> newIORef Map.empty\n\nenv_get :: Env -> String -> IO (Maybe MalVal)\nenv_get (Env maybeOuter ref) key = do\n    m <- readIORef ref\n    case Map.lookup key m of\n      Nothing -> case maybeOuter of\n          Nothing    -> return Nothing\n          Just outer -> env_get outer key\n      justVal -> return justVal\n\nenv_set :: Env -> String -> MalVal -> IO ()\nenv_set (Env _ ref) key value = modifyIORef ref $ Map.insert key value\n\nput1 :: (String, MalVal) -> IO ()\nput1 (key, value) = do\n  putChar ' '\n  putStr key\n  putChar ':'\n  putStr =<< _pr_str True value\n\nenv_put :: Env -> IO ()\nenv_put (Env _ ref) = mapM_ put1 =<< Map.assocs <$> readIORef ref\n"
  },
  {
    "path": "impls/haskell/Makefile",
    "content": "BINS4 = step4_if_fn_do step5_tco step6_file step7_quote step8_macros \\\n  step9_try stepA_mal\nBINS3 = step3_env $(BINS4)\nBINS1 = step1_read_print step2_eval $(BINS3)\nBINS = step0_repl $(BINS1)\nghc_flags = -Wall -Wextra\nLDLIBS = -lreadline\n\n#####################\n\nall: $(BINS)\n\n$(BINS): %: %.hs\n\tghc ${ghc_flags} --make $< $(LDLIBS) -o $@\n\n$(BINS1): Types.hs Reader.hs Printer.hs\n$(BINS3): Env.hs\n$(BINS4): Core.hs\n$(BINS): Readline.hs\n\nclean:\n\trm -f $(BINS) *.hi *.o\n"
  },
  {
    "path": "impls/haskell/Printer.hs",
    "content": "module Printer\n( _pr_str, _pr_list )\nwhere\n\nimport qualified Data.Map.Strict as Map\nimport Data.IORef (readIORef)\nimport Data.List (intercalate)\n\nimport Types\n\n_pr_list :: Bool -> String -> [MalVal] -> IO String\n_pr_list pr sep = fmap (intercalate sep) . mapM (_pr_str pr)\n\nenclose :: String -> String -> String -> String\nenclose open close middle = open ++ middle ++ close\n\nescape :: Char -> String -> String\nescape '\\n' acc = '\\\\' : 'n'  : acc\nescape '\\\\' acc = '\\\\' : '\\\\' : acc\nescape '\"'  acc = '\\\\' : '\"'  : acc\nescape c    acc = c           : acc\n\n_pr_str :: Bool -> MalVal -> IO String\n_pr_str _     (MalKeyword kwd)   = return $ ':' : kwd\n_pr_str True  (MalString str)    = return $ enclose \"\\\"\" \"\\\"\" $ foldr escape [] str\n_pr_str False (MalString str)    = return str\n_pr_str _     (MalSymbol name)   = return name\n_pr_str _     (MalNumber num)    = return $ show num\n_pr_str _     (MalBoolean True)  = return \"true\"\n_pr_str _     (MalBoolean False) = return \"false\"\n_pr_str _     Nil                = return \"nil\"\n_pr_str pr (MalSeq _ (Vect False) xs) = enclose \"(\" \")\" <$> _pr_list pr \" \" xs\n_pr_str pr (MalSeq _ (Vect True)  xs) = enclose \"[\" \"]\" <$> _pr_list pr \" \" xs\n_pr_str pr (MalHashMap _ m)      = enclose \"{\" \"}\" <$> _pr_list pr \" \"\n                                    (Map.foldMapWithKey (\\k v -> [decodeKey k, v]) m)\n_pr_str pr (MalAtom _ r)         = enclose \"(atom \" \")\" <$> (_pr_str pr =<< readIORef r)\n_pr_str _ (MalFunction _ _)      = return \"<fn>\"\n_pr_str _ (MalMacro _)           = return \"<macro>\"\n"
  },
  {
    "path": "impls/haskell/Reader.hs",
    "content": "module Reader\n( read_str )\nwhere\n\nimport qualified Data.Map.Strict as Map\nimport Text.ParserCombinators.Parsec (\n    Parser, parse, char, digit, anyChar,\n    (<|>), oneOf, noneOf, many, many1)\n\nimport Types\n\n----------------------------------------------------------------------\n--  A MAL grammar and a possible parsing are described here.\n\n--  If you are only interested in the grammar, please ignore the\n--  left-hand side of <$> and =<< operators (second column).\n\n--  *>  <*  <*>                     all mean concatenation\n--  <|>                             means alternative\n--  many  p = (many1 p) | empty     means p*, zero or more p\n--  many1 p = p (many p)            means p+, one or more p\n\n--  For efficiency, the alternative operator <|> expects each branch\n--  to either:\n--  * succeed,\n--  * fall after looking at the next character without consuming it,\n--  * or consume some input and fail, indicating that the input is\n--    incorrect and no remaining branches should be ignored.\n\nallowedChar   :: Parser Char\nallowedChar =                    noneOf \"\\n\\r \\\"(),;[\\\\]{}\"\n\nsep           :: Parser String\nsep         =                    many (oneOf  \", \\n\"\n                                       <|> char ';' <* many (noneOf \"\\n\"))\n\nstringChar    :: Parser Char\nstringChar  = unescapeChar  <$> (char '\\\\' *>  anyChar)\n          <|>                    noneOf \"\\\"\"\n\nafterMinus    :: Parser MalVal\nafterMinus  = negative      <$>  many1 digit\n          <|> hyphenSymbol  <$>  many allowedChar\n\nafterTilde    :: Parser MalVal\nafterTilde  = spliceUnquote <$> (char '@' *> sep *> form)\n          <|> unquote       <$> (sep *> form)\n\nform          :: Parser MalVal\nform        = MalString     <$> (char '\"'  *> many stringChar <* char '\"')\n          <|> MalKeyword    <$> (char ':'  *> many1 allowedChar)\n          <|>                    char '-'  *> afterMinus\n          <|> toList        <$> (char '('  *> sep *> many (form <* sep) <* char ')')\n          <|> vector        <$> (char '['  *> sep *> many (form <* sep) <* char ']')\n          <|> (toMap        =<<  char '{'  *> sep *> many (form <* sep) <* char '}')\n          <|> quote         <$> (char '\\'' *> sep *> form)\n          <|> quasiquote    <$> (char '`'  *> sep *> form)\n          <|> deref         <$> (char '@'  *> sep *> form)\n          <|>                    char '~'  *> afterTilde\n          <|> withMeta      <$> (char '^'  *> sep *> form <* sep) <*> form\n          <|> positive      <$>  many1 digit\n          <|> symbol        <$>  many1 allowedChar\n\nread_form     :: Parser MalVal\nread_form   =                    sep *> form\n\n----------------------------------------------------------------------\n--  Part specific to Haskell\n\naddPrefix :: String -> MalVal -> MalVal\naddPrefix s x = toList [MalSymbol s, x]\n\nderef :: MalVal -> MalVal\nderef = addPrefix \"deref\"\n\nhyphenSymbol :: String -> MalVal\nhyphenSymbol = MalSymbol . (:) '-'\n\nnegative ::  String ->  MalVal\nnegative = MalNumber . negate . read\n\npositive :: String -> MalVal\npositive = MalNumber . read\n\nquasiquote :: MalVal -> MalVal\nquasiquote = addPrefix \"quasiquote\"\n\nquote :: MalVal -> MalVal\nquote = addPrefix \"quote\"\n\nspliceUnquote :: MalVal -> MalVal\nspliceUnquote = addPrefix \"splice-unquote\"\n\ntoMap :: [MalVal] -> Parser MalVal\ntoMap kvs = case kv2map Map.empty kvs of\n  Just m -> return m\n  Nothing -> fail \"invalid contents in map braces\"\n\nunquote :: MalVal -> MalVal\nunquote = addPrefix \"unquote\"\n\nsymbol ::  String -> MalVal\nsymbol \"true\"  = MalBoolean True\nsymbol \"false\" = MalBoolean False\nsymbol \"nil\"   = Nil\nsymbol s       = MalSymbol s\n\nunescapeChar :: Char -> Char\nunescapeChar 'n' = '\\n'\nunescapeChar c   = c\n\nvector :: [MalVal] -> MalVal\nvector = MalSeq (MetaData Nil) (Vect True)\n\nwithMeta :: MalVal -> MalVal -> MalVal\nwithMeta m d = toList [MalSymbol \"with-meta\", d, m]\n\n--  The only exported function\n\nread_str :: String -> IOThrows MalVal\nread_str str = case parse read_form \"Mal\" str of\n    Left err -> throwStr $ show err\n    Right val -> return val\n"
  },
  {
    "path": "impls/haskell/Readline.hs",
    "content": "module Readline\n( addHistory, readline, load_history )\nwhere\n\n-- Pick one of these:\n-- GPL license\nimport qualified System.Console.Readline as RL\n-- BSD license\n--import qualified System.Console.Editline.Readline as RL\n\nimport Control.Monad (when)\nimport System.Directory (getHomeDirectory, doesFileExist)\nimport System.IO (hFlush, stdout)\nimport System.IO.Error (tryIOError)\n\nhistory_file :: IO String\nhistory_file = do\n    home <- getHomeDirectory\n    return $ home ++ \"/.mal-history\"\n\nload_history :: IO ()\nload_history = do\n    hfile <- history_file\n    fileExists <- doesFileExist hfile\n    when fileExists $ do\n        content <- readFile hfile\n        mapM_ RL.addHistory (lines content)\n\nreadline :: String -> IO (Maybe String)\nreadline prompt = do\n    hFlush stdout\n    RL.readline prompt\n\naddHistory :: String -> IO ()\naddHistory line = do\n    hfile <- history_file\n    _ <- tryIOError (appendFile hfile (line ++ \"\\n\"))\n    RL.addHistory line\n"
  },
  {
    "path": "impls/haskell/Types.hs",
    "content": "module Types\n( MalVal (..), IOThrows, Fn, MetaData (..), Vect (..),\n  decodeKey, encodeKey, kv2map,\n  throwStr, toList)\nwhere\n\nimport Data.IORef (IORef)\nimport qualified Data.Map.Strict as Map\n--  The documentation recommends strict except in specific cases.\nimport Control.Monad.Except (ExceptT, throwError)\n\n-- Base Mal types --\ntype Fn = [MalVal] -> IOThrows MalVal\n\n-- Use type safety for unnamed components, without runtime penalty.\nnewtype MetaData = MetaData MalVal\nnewtype Vect     = Vect     Bool\n\ndata MalVal = Nil\n            | MalBoolean  Bool\n            | MalNumber   Int\n            | MalString   String\n            | MalSymbol   String\n            | MalKeyword  String\n            | MalSeq      MetaData Vect [MalVal]\n            | MalHashMap  MetaData (Map.Map MapKey MalVal)\n            | MalAtom     MetaData (IORef MalVal)\n            | MalFunction MetaData Fn\n            | MalMacro    Fn\n\n--  Stored into maps to distinguish keywords and symbols.\n--  MapKey is not exported, other modules use encodeKey or kv2map.\ndata MapKey = MapKeyKeyword String | MapKeyString String\ninstance Eq MapKey where\n    MapKeyString  a == MapKeyString  b = a == b\n    MapKeyKeyword a == MapKeyKeyword b = a == b\n    _               == _               = False\ninstance Ord MapKey where\n    compare (MapKeyString  a) (MapKeyString  b) = compare a b\n    compare (MapKeyKeyword a) (MapKeyKeyword b) = compare a b\n    compare (MapKeyKeyword _) (MapKeyString  _) = LT\n    compare (MapKeyString  _) (MapKeyKeyword _) = GT\n\nencodeKey :: MalVal -> IOThrows MapKey\nencodeKey (MalString  key) = pure $ MapKeyString  key\nencodeKey (MalKeyword key) = pure $ MapKeyKeyword key\nencodeKey _ = throwStr \"map keys must be keywords or strings\"\n\ndecodeKey :: MapKey -> MalVal\ndecodeKey (MapKeyString  k) = MalString  k\ndecodeKey (MapKeyKeyword k) = MalKeyword k\n\ninstance Eq MalVal where\n    Nil              == Nil              = True\n    (MalBoolean a)   == (MalBoolean b)   = a == b\n    (MalNumber a)    == (MalNumber b)    = a == b\n    (MalString a)    == (MalString b)    = a == b\n    (MalKeyword a)   == (MalKeyword b)   = a == b\n    (MalSymbol a)    == (MalSymbol b)    = a == b\n    (MalSeq _ _ a)   == (MalSeq _ _ b)   = a == b\n    (MalHashMap _ a) == (MalHashMap _ b) = a == b\n    (MalAtom _ a)    == (MalAtom _ b)    = a == b\n    _                == _                = False\n\n--- Errors/Exceptions ---\n\ntype IOThrows = ExceptT MalVal IO\n\nthrowStr :: String -> IOThrows a\nthrowStr = throwError . MalString\n\n--  Convenient shortcuts for common situations.\n\ntoList :: [MalVal] -> MalVal\ntoList = MalSeq (MetaData Nil) (Vect False)\n\n--  Use Maybe because Core throws while Reader fails.\nkv2map :: Map.Map MapKey MalVal -> [MalVal] -> Maybe MalVal\nkv2map start forms = MalHashMap (MetaData Nil) <$> assoc1 start forms where\n  assoc1 :: Map.Map MapKey MalVal -> [MalVal] -> Maybe (Map.Map MapKey MalVal)\n  assoc1 acc (MalKeyword s : v : kvs) = assoc1 (Map.insert (MapKeyKeyword s) v acc) kvs\n  assoc1 acc (MalString  s : v : kvs) = assoc1 (Map.insert (MapKeyString  s) v acc) kvs\n  assoc1 acc [] = Just acc\n  assoc1 _ _ = Nothing\n"
  },
  {
    "path": "impls/haskell/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/haskell/step0_repl.hs",
    "content": "import Readline (addHistory, readline, load_history)\n\ntype MalVal = String\n\n-- read\n\nmal_read :: String -> MalVal\nmal_read = id\n\n-- eval\n\neval :: MalVal -> MalVal\neval = id\n\n-- print\n\nmal_print :: MalVal -> String\nmal_print = id\n\n-- repl\n\nrepl_loop :: IO ()\nrepl_loop = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop\n        Just str -> do\n            addHistory str\n            let out = mal_print $ eval $ mal_read str\n            putStrLn out\n            repl_loop\n\nmain :: IO ()\nmain = do\n    load_history\n    repl_loop\n"
  },
  {
    "path": "impls/haskell/step1_read_print.hs",
    "content": "import Control.Monad.Except (runExceptT)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer (_pr_str)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\neval :: MalVal -> MalVal\neval = id\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: IO ()\nrepl_loop = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval <$> mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop\n\nmain :: IO ()\nmain = do\n    load_history\n    repl_loop\n"
  },
  {
    "path": "impls/haskell/step2_eval.hs",
    "content": "import Control.Monad.Except (liftIO, runExceptT)\nimport qualified Data.Map.Strict as Map\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\n\ntype Env = Map.Map String MalVal\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    --  putStr \"EVAL: \"\n    --  putStrLn =<< mal_print ast\n    case ast of\n        MalSymbol sym -> do\n            let maybeVal = Map.lookup sym env\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nadd :: Fn\nadd [MalNumber a, MalNumber b] = return $ MalNumber $ a + b\nadd _ = throwStr $ \"illegal arguments to +\"\n\nsub :: Fn\nsub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b\nsub _ = throwStr $ \"illegal arguments to -\"\n\nmult :: Fn\nmult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b\nmult _ = throwStr $ \"illegal arguments to *\"\n\ndivd :: Fn\ndivd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b\ndivd _ = throwStr $ \"illegal arguments to /\"\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\nmain :: IO ()\nmain = do\n    let repl_env = Map.fromList [(\"+\", MalFunction (MetaData Nil) add),\n                                 (\"-\", MalFunction (MetaData Nil) sub),\n                                 (\"*\", MalFunction (MetaData Nil) mult),\n                                 (\"/\", MalFunction (MetaData Nil) divd)]\n\n    load_history\n    repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step3_env.hs",
    "content": "import Control.Monad.Except (liftIO, runExceptT)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer (_pr_list, _pr_str)\nimport Env\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nadd :: Fn\nadd [MalNumber a, MalNumber b] = return $ MalNumber $ a + b\nadd _ = throwStr $ \"illegal arguments to +\"\n\nsub :: Fn\nsub [MalNumber a, MalNumber b] = return $ MalNumber $ a - b\nsub _ = throwStr $ \"illegal arguments to -\"\n\nmult :: Fn\nmult [MalNumber a, MalNumber b] = return $ MalNumber $ a * b\nmult _ = throwStr $ \"illegal arguments to *\"\n\ndivd :: Fn\ndivd [MalNumber a, MalNumber b] = return $ MalNumber $ a `div` b\ndivd _ = throwStr $ \"illegal arguments to /\"\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\ndefBuiltIn :: Env -> String -> Fn -> IO ()\ndefBuiltIn env sym f =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nmain :: IO ()\nmain = do\n    repl_env <- env_new Nothing\n\n    defBuiltIn repl_env \"+\" add\n    defBuiltIn repl_env \"-\" sub\n    defBuiltIn repl_env \"*\" mult\n    defBuiltIn repl_env \"/\" divd\n\n    load_history\n    repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step4_if_fn_do.hs",
    "content": "import Control.Monad.Except (liftIO, runExceptT)\nimport Data.Foldable (foldlM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nmain :: IO ()\nmain = do\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n\n    load_history\n    repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step5_tco.hs",
    "content": "import Control.Monad.Except (liftIO, runExceptT)\nimport Data.Foldable (foldlM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nmain :: IO ()\nmain = do\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n\n    load_history\n    repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step6_file.hs",
    "content": "import System.Environment (getArgs)\nimport Control.Monad.Except (liftIO, runExceptT)\nimport Data.Foldable (foldlM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nevalFn :: Env -> Fn\nevalFn env [ast] = eval env ast\nevalFn _ _ = throwStr \"illegal call of eval\"\n\nmain :: IO ()\nmain = do\n    args <- getArgs\n\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n    defBuiltIn repl_env (\"eval\", evalFn repl_env)\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n    re repl_env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\n    case args of\n        script : scriptArgs -> do\n            env_set repl_env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n            re repl_env $ \"(load-file \\\"\" ++ script ++ \"\\\")\"\n        [] -> do\n            env_set repl_env \"*ARGV*\" $ toList []\n\n            load_history\n            repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step7_quote.hs",
    "content": "import System.Environment (getArgs)\nimport Control.Monad.Except (liftIO, runExceptT)\nimport Data.Foldable (foldlM, foldrM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nqqIter :: MalVal -> MalVal -> IOThrows MalVal\nqqIter (MalSeq _ (Vect False) [MalSymbol \"splice-unquote\", x]) acc = return $ toList [MalSymbol \"concat\", x, acc]\nqqIter (MalSeq _ (Vect False) (MalSymbol \"splice-unquote\" : _)) _ = throwStr \"invalid splice-unquote\"\nqqIter elt acc = do\n    qqted <- quasiquote elt\n    return $ toList [MalSymbol \"cons\", qqted, acc]\n\nquasiquote :: MalVal -> IOThrows MalVal\nquasiquote (MalSeq _ (Vect False) [MalSymbol \"unquote\", x]) = return x\nquasiquote (MalSeq _ (Vect False) (MalSymbol \"unquote\" : _)) = throwStr \"invalid unquote\"\nquasiquote (MalSeq _ (Vect False) ys) = foldrM qqIter (toList []) ys\nquasiquote (MalSeq _ (Vect True) ys) = do\n  lst <- foldrM qqIter (toList []) ys\n  return $ toList [MalSymbol \"vec\", lst]\nquasiquote ast@(MalHashMap _ _) = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast@(MalSymbol _)    = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast = return ast\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"quote\") [a1] _ = return a1\napply_ast (MalSymbol \"quote\") _ _ = throwStr \"invalid quote\"\n\napply_ast (MalSymbol \"quasiquote\") [a1] env = eval env =<< quasiquote a1\napply_ast (MalSymbol \"quasiquote\") _ _ = throwStr \"invalid quasiquote\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nevalFn :: Env -> Fn\nevalFn env [ast] = eval env ast\nevalFn _ _ = throwStr \"illegal call of eval\"\n\nmain :: IO ()\nmain = do\n    args <- getArgs\n\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n    defBuiltIn repl_env (\"eval\", evalFn repl_env)\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n    re repl_env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\n    case args of\n        script : scriptArgs -> do\n            env_set repl_env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n            re repl_env $ \"(load-file \\\"\" ++ script ++ \"\\\")\"\n        [] -> do\n            env_set repl_env \"*ARGV*\" $ toList []\n\n            load_history\n            repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step8_macros.hs",
    "content": "import System.Environment (getArgs)\nimport Control.Monad.Except (liftIO, runExceptT)\nimport Data.Foldable (foldlM, foldrM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nqqIter :: MalVal -> MalVal -> IOThrows MalVal\nqqIter (MalSeq _ (Vect False) [MalSymbol \"splice-unquote\", x]) acc = return $ toList [MalSymbol \"concat\", x, acc]\nqqIter (MalSeq _ (Vect False) (MalSymbol \"splice-unquote\" : _)) _ = throwStr \"invalid splice-unquote\"\nqqIter elt acc = do\n    qqted <- quasiquote elt\n    return $ toList [MalSymbol \"cons\", qqted, acc]\n\nquasiquote :: MalVal -> IOThrows MalVal\nquasiquote (MalSeq _ (Vect False) [MalSymbol \"unquote\", x]) = return x\nquasiquote (MalSeq _ (Vect False) (MalSymbol \"unquote\" : _)) = throwStr \"invalid unquote\"\nquasiquote (MalSeq _ (Vect False) ys) = foldrM qqIter (toList []) ys\nquasiquote (MalSeq _ (Vect True) ys) = do\n  lst <- foldrM qqIter (toList []) ys\n  return $ toList [MalSymbol \"vec\", lst]\nquasiquote ast@(MalHashMap _ _) = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast@(MalSymbol _)    = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast = return ast\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"quote\") [a1] _ = return a1\napply_ast (MalSymbol \"quote\") _ _ = throwStr \"invalid quote\"\n\napply_ast (MalSymbol \"quasiquote\") [a1] env = eval env =<< quasiquote a1\napply_ast (MalSymbol \"quasiquote\") _ _ = throwStr \"invalid quasiquote\"\n\napply_ast (MalSymbol \"defmacro!\") [MalSymbol a1, a2] env = do\n    func <- eval env a2\n    case func of\n        MalFunction _ f -> do\n            let m = MalMacro f\n            liftIO $ env_set env a1 m\n            return m\n        _ -> throwStr \"defmacro! on non-function\"\napply_ast (MalSymbol \"defmacro!\") _ _ = throwStr \"invalid defmacro!\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        MalMacro m      -> eval env =<< m rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nevalFn :: Env -> Fn\nevalFn env [ast] = eval env ast\nevalFn _ _ = throwStr \"illegal call of eval\"\n\nmain :: IO ()\nmain = do\n    args <- getArgs\n\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n    defBuiltIn repl_env (\"eval\", evalFn repl_env)\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n    re repl_env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n    re repl_env \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\n    case args of\n        script : scriptArgs -> do\n            env_set repl_env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n            re repl_env $ \"(load-file \\\"\" ++ script ++ \"\\\")\"\n        [] -> do\n            env_set repl_env \"*ARGV*\" $ toList []\n\n            load_history\n            repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/step9_try.hs",
    "content": "import System.Environment (getArgs)\nimport Control.Monad.Except (catchError, liftIO, runExceptT)\nimport Data.Foldable (foldlM, foldrM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nqqIter :: MalVal -> MalVal -> IOThrows MalVal\nqqIter (MalSeq _ (Vect False) [MalSymbol \"splice-unquote\", x]) acc = return $ toList [MalSymbol \"concat\", x, acc]\nqqIter (MalSeq _ (Vect False) (MalSymbol \"splice-unquote\" : _)) _ = throwStr \"invalid splice-unquote\"\nqqIter elt acc = do\n    qqted <- quasiquote elt\n    return $ toList [MalSymbol \"cons\", qqted, acc]\n\nquasiquote :: MalVal -> IOThrows MalVal\nquasiquote (MalSeq _ (Vect False) [MalSymbol \"unquote\", x]) = return x\nquasiquote (MalSeq _ (Vect False) (MalSymbol \"unquote\" : _)) = throwStr \"invalid unquote\"\nquasiquote (MalSeq _ (Vect False) ys) = foldrM qqIter (toList []) ys\nquasiquote (MalSeq _ (Vect True) ys) = do\n  lst <- foldrM qqIter (toList []) ys\n  return $ toList [MalSymbol \"vec\", lst]\nquasiquote ast@(MalHashMap _ _) = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast@(MalSymbol _)    = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast = return ast\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"quote\") [a1] _ = return a1\napply_ast (MalSymbol \"quote\") _ _ = throwStr \"invalid quote\"\n\napply_ast (MalSymbol \"quasiquote\") [a1] env = eval env =<< quasiquote a1\napply_ast (MalSymbol \"quasiquote\") _ _ = throwStr \"invalid quasiquote\"\n\napply_ast (MalSymbol \"defmacro!\") [MalSymbol a1, a2] env = do\n    func <- eval env a2\n    case func of\n        MalFunction _ f -> do\n            let m = MalMacro f\n            liftIO $ env_set env a1 m\n            return m\n        _ -> throwStr \"defmacro! on non-function\"\napply_ast (MalSymbol \"defmacro!\") _ _ = throwStr \"invalid defmacro!\"\n\napply_ast (MalSymbol \"try*\") [a1] env = eval env a1\napply_ast (MalSymbol \"try*\") [a1, MalSeq _ (Vect False) [MalSymbol \"catch*\", MalSymbol a21, a22]] env =\n    catchError (eval env a1) $ \\exc -> do\n        try_env <- liftIO $ env_new $ Just env\n        liftIO $ env_set try_env a21 exc\n        eval try_env a22\napply_ast (MalSymbol \"try*\") _ _ = throwStr \"invalid try*\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        MalMacro m      -> eval env =<< m rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nevalFn :: Env -> Fn\nevalFn env [ast] = eval env ast\nevalFn _ _ = throwStr \"illegal call of eval\"\n\nmain :: IO ()\nmain = do\n    args <- getArgs\n\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n    defBuiltIn repl_env (\"eval\", evalFn repl_env)\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n    re repl_env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n    re repl_env \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\n    case args of\n        script : scriptArgs -> do\n            env_set repl_env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n            re repl_env $ \"(load-file \\\"\" ++ script ++ \"\\\")\"\n        [] -> do\n            env_set repl_env \"*ARGV*\" $ toList []\n\n            load_history\n            repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/stepA_mal.hs",
    "content": "import System.Environment (getArgs)\nimport Control.Monad.Except (catchError, liftIO, runExceptT)\nimport Data.Foldable (foldlM, foldrM)\n\nimport Readline (addHistory, readline, load_history)\nimport Types\nimport Reader (read_str)\nimport Printer(_pr_list, _pr_str)\nimport Env\nimport Core (ns)\n\n-- read\n\nmal_read :: String -> IOThrows MalVal\nmal_read = read_str\n\n-- eval\n\nqqIter :: MalVal -> MalVal -> IOThrows MalVal\nqqIter (MalSeq _ (Vect False) [MalSymbol \"splice-unquote\", x]) acc = return $ toList [MalSymbol \"concat\", x, acc]\nqqIter (MalSeq _ (Vect False) (MalSymbol \"splice-unquote\" : _)) _ = throwStr \"invalid splice-unquote\"\nqqIter elt acc = do\n    qqted <- quasiquote elt\n    return $ toList [MalSymbol \"cons\", qqted, acc]\n\nquasiquote :: MalVal -> IOThrows MalVal\nquasiquote (MalSeq _ (Vect False) [MalSymbol \"unquote\", x]) = return x\nquasiquote (MalSeq _ (Vect False) (MalSymbol \"unquote\" : _)) = throwStr \"invalid unquote\"\nquasiquote (MalSeq _ (Vect False) ys) = foldrM qqIter (toList []) ys\nquasiquote (MalSeq _ (Vect True) ys) = do\n  lst <- foldrM qqIter (toList []) ys\n  return $ toList [MalSymbol \"vec\", lst]\nquasiquote ast@(MalHashMap _ _) = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast@(MalSymbol _)    = return $ toList [MalSymbol \"quote\", ast]\nquasiquote ast = return ast\n\nlet_bind :: Env -> [MalVal] -> IOThrows ()\nlet_bind _ [] = return ()\nlet_bind env (MalSymbol b : e : xs) = do\n    liftIO . env_set env b =<< eval env e\n    let_bind env xs\nlet_bind _ _ = throwStr \"invalid let*\"\n\napply_ast :: MalVal -> [MalVal] -> Env -> IOThrows MalVal\n\napply_ast (MalSymbol \"def!\") [MalSymbol a1, a2] env = do\n    evd <- eval env a2\n    liftIO $ env_set env a1 evd\n    return evd\napply_ast (MalSymbol \"def!\") _ _ = throwStr \"invalid def!\"\n\napply_ast (MalSymbol \"let*\") [MalSeq _ _ params, a2] env = do\n    let_env <- liftIO $ env_new $ Just env\n    let_bind let_env params\n    eval let_env a2\napply_ast (MalSymbol \"let*\") _ _ = throwStr \"invalid let*\"\n\napply_ast (MalSymbol \"quote\") [a1] _ = return a1\napply_ast (MalSymbol \"quote\") _ _ = throwStr \"invalid quote\"\n\napply_ast (MalSymbol \"quasiquote\") [a1] env = eval env =<< quasiquote a1\napply_ast (MalSymbol \"quasiquote\") _ _ = throwStr \"invalid quasiquote\"\n\napply_ast (MalSymbol \"defmacro!\") [MalSymbol a1, a2] env = do\n    func <- eval env a2\n    case func of\n        MalFunction _ f -> do\n            let m = MalMacro f\n            liftIO $ env_set env a1 m\n            return m\n        _ -> throwStr \"defmacro! on non-function\"\napply_ast (MalSymbol \"defmacro!\") _ _ = throwStr \"invalid defmacro!\"\n\napply_ast (MalSymbol \"try*\") [a1] env = eval env a1\napply_ast (MalSymbol \"try*\") [a1, MalSeq _ (Vect False) [MalSymbol \"catch*\", MalSymbol a21, a22]] env =\n    catchError (eval env a1) $ \\exc -> do\n        try_env <- liftIO $ env_new $ Just env\n        liftIO $ env_set try_env a21 exc\n        eval try_env a22\napply_ast (MalSymbol \"try*\") _ _ = throwStr \"invalid try*\"\n\napply_ast (MalSymbol \"do\") args env = foldlM (const $ eval env) Nil args\n\napply_ast (MalSymbol \"if\") [a1, a2, a3] env = do\n    cond <- eval env a1\n    eval env $ case cond of\n        Nil              -> a3\n        MalBoolean False -> a3\n        _                -> a2\napply_ast (MalSymbol \"if\") [a1, a2] env = do\n    cond <- eval env a1\n    case cond of\n        Nil              -> return Nil\n        MalBoolean False -> return Nil\n        _                -> eval env a2\napply_ast (MalSymbol \"if\") _ _ = throwStr \"invalid if\"\n\napply_ast (MalSymbol \"fn*\") [MalSeq _ _ params, ast] env = return $ MalFunction (MetaData Nil) fn where\n    fn :: [MalVal] -> IOThrows MalVal\n    fn args = do\n        fn_env <- liftIO $ env_new $ Just env\n        let loop [] [] = eval fn_env ast\n            loop [MalSymbol \"&\", k] vs = loop [k] [toList vs]\n            loop (MalSymbol k : ks) (v : vs) = do\n                liftIO $ env_set fn_env k v\n                loop ks vs\n            loop _ _ = do\n                p <- liftIO $ _pr_list True \" \" params\n                a <- liftIO $ _pr_list True \" \" args\n                throwStr $ \"actual parameters: \" ++ a ++ \" do not match signature: \" ++ p\n        loop params args\napply_ast (MalSymbol \"fn*\") _ _ = throwStr \"invalid fn*\"\n\napply_ast first rest env = do\n    evd <- eval env first\n    case evd of\n        MalFunction _ f -> f =<< mapM (eval env) rest\n        MalMacro m      -> eval env =<< m rest\n        _               -> throwStr . (++) \"invalid apply: \" =<< liftIO (_pr_list True \" \" $ first : rest)\n\neval :: Env -> MalVal -> IOThrows MalVal\neval env ast = do\n    traceEval <- liftIO $ env_get env \"DEBUG-EVAL\"\n    case traceEval of\n      Nothing                 -> pure ()\n      Just Nil                -> pure ()\n      Just (MalBoolean False) -> pure ()\n      Just _                  -> liftIO $ do\n            putStr \"EVAL: \"\n            putStr =<< _pr_str True ast\n            putStr \"   \"\n            env_put env\n            putStrLn \"\"\n    case ast of\n        MalSymbol sym -> do\n            maybeVal <- liftIO $ env_get env sym\n            case maybeVal of\n                Nothing  -> throwStr $ \"'\" ++ sym ++ \"' not found\"\n                Just val -> return val\n        MalSeq _ (Vect False) (a1 : as) -> apply_ast a1 as env\n        MalSeq _ (Vect True)  xs        -> MalSeq (MetaData Nil) (Vect True) <$> mapM (eval env) xs\n        MalHashMap _ xs                 -> MalHashMap (MetaData Nil) <$> mapM (eval env) xs\n        _ -> return ast\n\n-- print\n\nmal_print :: MalVal -> IO String\nmal_print = _pr_str True\n\n-- repl\n\nrepl_loop :: Env -> IO ()\nrepl_loop env = do\n    line <- readline \"user> \"\n    case line of\n        Nothing -> return ()\n        Just \"\" -> repl_loop env\n        Just str -> do\n            addHistory str\n            res <- runExceptT $ eval env =<< mal_read str\n            out <- case res of\n                Left mv -> (++) \"Error: \" <$> mal_print mv\n                Right val -> mal_print val\n            putStrLn out\n            repl_loop env\n\n--  Read and evaluate a line.  Ignore successful results, else print\n--  an error message case of error.\n--  The error function seems appropriate, but has no effect.\nre :: Env -> String -> IO ()\nre repl_env line = do\n    res <- runExceptT $ eval repl_env =<< mal_read line\n    case res of\n        Left mv -> putStrLn . (++) \"Startup failed: \" =<< _pr_str True mv\n        Right _ -> return ()\n\ndefBuiltIn :: Env -> (String, Fn) -> IO ()\ndefBuiltIn env (sym, f) =\n    env_set env sym $ MalFunction (MetaData Nil) f\n\nevalFn :: Env -> Fn\nevalFn env [ast] = eval env ast\nevalFn _ _ = throwStr \"illegal call of eval\"\n\nmain :: IO ()\nmain = do\n    args <- getArgs\n\n    repl_env <- env_new Nothing\n\n    -- core.hs: defined using Haskell\n    mapM_ (defBuiltIn repl_env) Core.ns\n    defBuiltIn repl_env (\"eval\", evalFn repl_env)\n\n    -- core.mal: defined using the language itself\n    re repl_env \"(def! *host-language* \\\"haskell\\\")\"\n    re repl_env \"(def! not (fn* (a) (if a false true)))\"\n    re repl_env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n    re repl_env \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\n    case args of\n        script : scriptArgs -> do\n            env_set repl_env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n            re repl_env $ \"(load-file \\\"\" ++ script ++ \"\\\")\"\n        [] -> do\n            env_set repl_env \"*ARGV*\" $ toList []\n            re repl_env \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"\n\n            load_history\n            repl_loop repl_env\n"
  },
  {
    "path": "impls/haskell/tests/step5_tco.mal",
    "content": ";; Haskell: skipping non-TCO recursion\n;; Reason: completes up to 100,000, stackoverflow at 1,000,000\n"
  },
  {
    "path": "impls/haxe/Compat.hx",
    "content": "#if js\n    @:native(\"console\")\n    extern class Console {\n        public static function log(s:Dynamic):Void;\n    }\n\n    @:native(\"process\")\n    extern class Process {\n        public static var argv(default,null):Array<String>;\n        public static function exit(code:Int):Void;\n    }\n\n    @:jsRequire(\"fs\")\n    extern class FS {\n        static function readFileSync(filename:String,\n                                     options:{encoding:String}):String;\n    }\n\n    @:jsRequire(\"./node_readline\")\n    extern class RL {\n        static function readline(prompt:String):Null<String>;\n    }\n#end\n\nclass Compat {\n    public static function println(s:String) {\n        #if js\n            Console.log(s);\n        #else\n            Sys.println(s);\n        #end\n    }\n\n    public static function slurp(filename:String) {\n        #if js\n            return FS.readFileSync(filename, {encoding: \"utf-8\"});\n        #else\n            return sys.io.File.getContent(filename);\n        #end\n    }\n\n    public static function exit(code:Int) {\n        #if js\n            Process.exit(0);\n        #else\n            Sys.exit(0);\n        #end\n    }\n\n    public static function cmdline_args() {\n        #if js\n            return Process.argv.slice(2);\n        #else\n            return Sys.args();\n        #end\n    }\n\n    public static function readline(prompt:String) {\n        #if js\n            var line = RL.readline(prompt);\n            if (line == null) { throw new haxe.io.Eof(); }\n        #else\n            Sys.print(prompt);\n            var line = Sys.stdin().readLine();\n        #end\n        return line;\n    }\n\n\n}\n"
  },
  {
    "path": "impls/haxe/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Haxe\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ libreadline-dev nodejs npm\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install haxe\n\nENV NPM_CONFIG_CACHE /mal/.npm\nENV HOME /\n\nRUN mkdir /haxelib && haxelib setup /haxelib\n# Install support for C++ compilation\nRUN haxelib install hxcpp\n"
  },
  {
    "path": "impls/haxe/Makefile",
    "content": "STEP1_DEPS = Compat.hx types/Types.hx reader/Reader.hx printer/Printer.hx\nSTEP3_DEPS = $(STEP1_DEPS) env/Env.hx\nSTEP4_DEPS = $(STEP3_DEPS) core/Core.hx\n\nSTEPS = step0_repl step1_read_print step2_eval step3_env \\\n\tstep4_if_fn_do step5_tco step6_file step7_quote \\\n\tstep8_macros step9_try stepA_mal\n\nhaxe_MODE ?= neko\ndist_neko = mal.n\ndist_python = mal.py\ndist_cpp = cpp/mal\n\nall: all-$(haxe_MODE)\n\nall-neko: $(foreach x,$(STEPS),$(x).n)\n\nall-python: $(foreach x,$(STEPS),$(x).py)\n\nall-cpp: $(foreach x,$(STEPS),cpp/$(x))\n\nall-js: $(foreach x,$(STEPS),$(x).js)\n\ndist: mal.n mal.py cpp/mal mal.js mal\n\nmal.n: stepA_mal.n\n\tcp $< $@\n\nmal.py: stepA_mal.py\n\tcp $< $@\n\ncpp/mal: cpp/stepA_mal\n\tcp $< $@\n\nmal.js: stepA_mal.js\n\tcp $< $@\n\n\nmal: $(dist_$(haxe_MODE))\n\t$(if $(filter cpp,$(haxe_MODE)),\\\n\t  cp $< $@;,\\\n        $(if $(filter neko,$(haxe_MODE)),\\\n\t  nekotools boot $<;,\\\n        $(if $(filter js,$(haxe_MODE)),\\\n\t  echo \"#!/usr/bin/env node\" > $@;\\\n\t  cat $< >> $@;,\\\n        $(if $(filter python,$(haxe_MODE)),\\\n\t  echo \"#!/usr/bin/env python3\" > $@;\\\n\t  cat $< >> $@;,\\\n\t$(error Invalid haxe_MODE: $(haxe_MODE))))))\n\tchmod +x $@\n\n\n# Neko target (neko)\n\ns%.n: S%.hx\n\thaxe -main $(patsubst %.hx,%,$<) -neko $@\n\nstep1_read_print.n step2_eval.n: $(STEP1_DEPS)\nstep3_env.n: $(STEP3_DEPS)\nstep4_if_fn_do.n step5_tco.n step6_file.n step7_quote.n step8_macros.n step9_try.n stepA_mal.n: $(STEP4_DEPS)\n\n\n# Python 3 target (python)\n\ns%.py: S%.hx\n\thaxe -main $(patsubst %.hx,%,$<) -python $@\n\nstep1_read_print.py step2_eval.py: $(STEP1_DEPS)\nstep3_env.py: $(STEP3_DEPS)\nstep4_if_fn_do.py step5_tco.py step6_file.py step7_quote.py step8_macros.py step9_try.py stepA_mal.py: $(STEP4_DEPS)\n\n\n# C++ target (cpp)\n\ncpp/s%: S%.hx\n\thaxe -main $(patsubst %.hx,%,$<) -cpp cpp\n\tcp $(patsubst cpp/s%,cpp/S%,$@) $@\n\ncpp/step1_read_print cpp/step2_eval: $(STEP1_DEPS)\ncpp/step3_env: $(STEP3_DEPS)\ncpp/step4_if_fn_do cpp/step5_tco cpp/step6_file cpp/step7_quote cpp/step8_macros cpp/step9_try cpp/stepA_mal: $(STEP4_DEPS)\n\n\n# JavaScript target (js)\n\ns%.js: S%.hx\n\thaxe -main $(patsubst %.hx,%,$<) -js $@\n\nJS_DEPS = node_readline.js node_modules\nstep0_repl.js: $(JS_DEPS)\nstep1_read_print.js step2_eval.js: $(STEP1_DEPS) $(JS_DEPS)\nstep3_env.js: $(STEP3_DEPS) $(JS_DEPS)\nstep4_if_fn_do.js step5_tco.js step6_file.js step7_quote.js step8_macros.js step9_try.js stepA_mal.js: $(STEP4_DEPS) $(JS_DEPS)\n\nnode_modules:\n\tnpm install\n\n###\n\nclean:\n\trm -f mal.n mal.py cpp/mal mal.js mal\n\trm -f step*.py step*.js step*.n\n\t[ -e cpp/ ] && rm -r cpp/ || true\n"
  },
  {
    "path": "impls/haxe/Step0_repl.hx",
    "content": "import Compat;\n\nclass Step0_repl {\n    // READ\n    static function READ(str:String) {\n        return str;\n    }\n\n    // EVAL\n    static function EVAL(ast:String, env:String) {\n        return ast;\n    }\n\n    // PRINT\n    static function PRINT(exp:String) {\n        return exp;\n    }\n\n    // repl\n    static function rep(line:String) {\n        return PRINT(EVAL(READ(line), \"\"));\n    }\n\n    public static function main() {\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                Compat.println(rep(line));\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                Compat.println(\"Error: \" + exc);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step1_read_print.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport reader.*;\nimport printer.*;\n\nclass Step1_read_print {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function EVAL(ast:MalType, env:String) {\n        return ast;\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static function rep(line:String) {\n        return PRINT(EVAL(READ(line), \"\"));\n    }\n\n    public static function main() {\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                Compat.println(\"Error: \" + exc);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step2_eval.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport reader.*;\nimport printer.*;\n\nclass Step2_eval {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function EVAL(ast:MalType, env:Map<String,MalType>) {\n        // Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                if (env.exists(s)) {\n                    return env.get(s);\n                } else {\n                    throw \"'\" + s + \"' not found\";\n                }\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch ( EVAL(alst[0], env)) {\n            case MalFunc(f,_,_,_,_,_):\n                var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                return f(args);\n            case _: throw \"Call of non-function\";\n        }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static function NumOp(op):MalType {\n        return MalFunc(function(args:Array<MalType>) {\n            return switch (args) {\n                case [MalInt(a), MalInt(b)]: MalInt(op(a,b));\n                case _: throw \"Invalid numeric op call\"; \n            }\n            \n        },null,null,null,false,nil);\n    }\n    static var repl_env:Map<String,MalType> = \n        [\"+\" => NumOp(function(a,b) {return a+b;}),\n         \"-\" => NumOp(function(a,b) {return a-b;}),\n         \"*\" => NumOp(function(a,b) {return a*b;}),\n         \"/\" => NumOp(function(a,b) {return Std.int(a/b);})];\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                Compat.println(\"Error: \" + exc);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step3_env.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport reader.*;\nimport printer.*;\nimport env.*;\n\nclass Step3_env {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function EVAL(ast:MalType, env:Env):MalType {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            return EVAL(alst[2], let_env);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,_,_,_,_,_):\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    return f(args);\n                case _: throw \"Call of non-function\";\n            }\n        }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static function NumOp(op):MalType {\n        return MalFunc(function(args:Array<MalType>) {\n            return switch (args) {\n                case [MalInt(a), MalInt(b)]: MalInt(op(a,b));\n                case _: throw \"Invalid numeric op call\"; \n            }\n            \n        },null,null,null,false,nil);\n    }\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        repl_env.set(MalSymbol(\"+\"), NumOp(function(a,b) {return a+b;}));\n        repl_env.set(MalSymbol(\"-\"), NumOp(function(a,b) {return a-b;}));\n        repl_env.set(MalSymbol(\"*\"), NumOp(function(a,b) {return a*b;}));\n        repl_env.set(MalSymbol(\"/\"), NumOp(function(a,b) {return Std.int(a/b);}));\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                Compat.println(\"Error: \" + exc);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step4_if_fn_do.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\n\nclass Step4_if_fn_do {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function EVAL(ast:MalType, env:Env):MalType {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            return EVAL(alst[2], let_env);\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            return EVAL(alst[alst.length-1], env);\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                return EVAL(alst[2], env);\n            } else if (alst.length > 3) {\n                return EVAL(alst[3], env);\n            } else {\n                return MalNil;\n            }\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },null,null,null,false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,_,_,_,_,_):\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    return f(args);\n                case _: throw \"Call of non-function\";\n            }\n        }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        // core.mal: defined using the language itself\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step5_tco.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\n\nclass Step5_tco {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function EVAL(ast:MalType, env:Env):MalType {\n      while (true) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            ast = alst[2];\n            env = let_env;\n            continue; // TCO\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            ast = alst[alst.length-1];\n            continue; // TCO\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                ast = alst[2];\n            } else if (alst.length > 3) {\n                ast = alst[3];\n            } else {\n                return MalNil;\n            }\n            continue; // TCO\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },alst[2],env,alst[1],false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,a,e,params,_,_):\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    if (a != null) {\n                        ast = a;\n                        env = new Env(e, _list(params), args);\n                        continue; // TCO\n                    } else {\n                        return f(args);\n                    }\n                case _: throw \"Call of non-function\";\n            }\n        }\n      }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        // core.mal: defined using the language itself\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step6_file.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\n\nclass Step6_file {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function EVAL(ast:MalType, env:Env):MalType {\n      while (true) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            ast = alst[2];\n            env = let_env;\n            continue; // TCO\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            ast = alst[alst.length-1];\n            continue; // TCO\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                ast = alst[2];\n            } else if (alst.length > 3) {\n                ast = alst[3];\n            } else {\n                return MalNil;\n            }\n            continue; // TCO\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },alst[2],env,alst[1],false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,a,e,params,_,_):\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    if (a != null) {\n                        ast = a;\n                        env = new Env(e, _list(params), args);\n                        continue; // TCO\n                    } else {\n                        return f(args);\n                    }\n                case _: throw \"Call of non-function\";\n            }\n        }\n      }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        var evalfn = MalFunc(function(args) {\n            return EVAL(args[0], repl_env);\n        },null,null,null,false,nil);\n        repl_env.set(MalSymbol(\"eval\"), evalfn);\n\n        var cmdargs = Compat.cmdline_args();\n        var argarray = cmdargs.map(function(a) { return MalString(a); });\n        repl_env.set(MalSymbol(\"*ARGV*\"), MalList(argarray.slice(1)));\n\n        // core.mal: defined using the language itself\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n        rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\n        if (cmdargs.length > 0) {\n            rep('(load-file \"${cmdargs[0]}\")');\n            Compat.exit(0);\n        }\n\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step7_quote.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\n\nclass Step7_quote {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function qq_loop(elt:MalType, acc:MalType) {\n        switch elt {\n            case MalList([MalSymbol(\"splice-unquote\"), arg]):\n                return MalList([MalSymbol(\"concat\"), arg, acc]);\n            case _:\n                return MalList([MalSymbol(\"cons\"), quasiquote(elt), acc]);\n        }\n    }\n    static function qq_foldr(xs:Array<MalType>) {\n        var acc = MalList([]);\n        for (i in 1 ... xs.length+1) {\n            acc = qq_loop (xs[xs.length-i], acc);\n        }\n        return acc;\n    }\n    static function quasiquote(ast:MalType) {\n        return switch(ast) {\n            case MalList([MalSymbol(\"unquote\"), arg]): arg;\n            case MalList(l): qq_foldr(l);\n            case MalVector(l): MalList([MalSymbol(\"vec\"), qq_foldr(l)]);\n            case MalSymbol(_) | MalHashMap(_): MalList([MalSymbol(\"quote\"), ast]);\n            case _: ast;\n        }\n    }\n\n    static function EVAL(ast:MalType, env:Env):MalType {\n      while (true) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            ast = alst[2];\n            env = let_env;\n            continue; // TCO\n        case MalSymbol(\"quote\"):\n            return alst[1];\n        case MalSymbol(\"quasiquote\"):\n            ast = quasiquote(alst[1]);\n            continue; // TCO\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            ast = alst[alst.length-1];\n            continue; // TCO\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                ast = alst[2];\n            } else if (alst.length > 3) {\n                ast = alst[3];\n            } else {\n                return MalNil;\n            }\n            continue; // TCO\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },alst[2],env,alst[1],false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,a,e,params,_,_):\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    if (a != null) {\n                        ast = a;\n                        env = new Env(e, _list(params), args);\n                        continue; // TCO\n                    } else {\n                        return f(args);\n                    }\n                case _: throw \"Call of non-function\";\n            }\n        }\n      }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        var evalfn = MalFunc(function(args) {\n            return EVAL(args[0], repl_env);\n        },null,null,null,false,nil);\n        repl_env.set(MalSymbol(\"eval\"), evalfn);\n\n        var cmdargs = Compat.cmdline_args();\n        var argarray = cmdargs.map(function(a) { return MalString(a); });\n        repl_env.set(MalSymbol(\"*ARGV*\"), MalList(argarray.slice(1)));\n\n        // core.mal: defined using the language itself\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n        rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\n        if (cmdargs.length > 0) {\n            rep('(load-file \"${cmdargs[0]}\")');\n            Compat.exit(0);\n        }\n\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step8_macros.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\n\nclass Step8_macros {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function qq_loop(elt:MalType, acc:MalType) {\n        switch elt {\n            case MalList([MalSymbol(\"splice-unquote\"), arg]):\n                return MalList([MalSymbol(\"concat\"), arg, acc]);\n            case _:\n                return MalList([MalSymbol(\"cons\"), quasiquote(elt), acc]);\n        }\n    }\n    static function qq_foldr(xs:Array<MalType>) {\n        var acc = MalList([]);\n        for (i in 1 ... xs.length+1) {\n            acc = qq_loop (xs[xs.length-i], acc);\n        }\n        return acc;\n    }\n    static function quasiquote(ast:MalType) {\n        return switch(ast) {\n            case MalList([MalSymbol(\"unquote\"), arg]): arg;\n            case MalList(l): qq_foldr(l);\n            case MalVector(l): MalList([MalSymbol(\"vec\"), qq_foldr(l)]);\n            case MalSymbol(_) | MalHashMap(_): MalList([MalSymbol(\"quote\"), ast]);\n            case _: ast;\n        }\n    }\n\n    static function EVAL(ast:MalType, env:Env):MalType {\n      while (true) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            ast = alst[2];\n            env = let_env;\n            continue; // TCO\n        case MalSymbol(\"quote\"):\n            return alst[1];\n        case MalSymbol(\"quasiquote\"):\n            ast = quasiquote(alst[1]);\n            continue; // TCO\n        case MalSymbol(\"defmacro!\"):\n            var func = EVAL(alst[2], env);\n            return switch (func) {\n                case MalFunc(f,ast,e,params,_,_):\n                    env.set(alst[1], MalFunc(f,ast,e,params,true,nil));\n                case _:\n                    throw \"Invalid defmacro! call\";\n            }\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            ast = alst[alst.length-1];\n            continue; // TCO\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                ast = alst[2];\n            } else if (alst.length > 3) {\n                ast = alst[3];\n            } else {\n                return MalNil;\n            }\n            continue; // TCO\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },alst[2],env,alst[1],false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,a,e,params,ismacro,_):\n                    if (ismacro) {\n                        ast = f(alst.slice(1));\n                        continue; // TCO\n                    }\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    if (a != null) {\n                        ast = a;\n                        env = new Env(e, _list(params), args);\n                        continue; // TCO\n                    } else {\n                        return f(args);\n                    }\n                case _: throw \"Call of non-function\";\n            }\n        }\n      }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        var evalfn = MalFunc(function(args) {\n            return EVAL(args[0], repl_env);\n        },null,null,null,false,nil);\n        repl_env.set(MalSymbol(\"eval\"), evalfn);\n\n        var cmdargs = Compat.cmdline_args();\n        var argarray = cmdargs.map(function(a) { return MalString(a); });\n        repl_env.set(MalSymbol(\"*ARGV*\"), MalList(argarray.slice(1)));\n\n        // core.mal: defined using the language itself\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n        rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n\n        if (cmdargs.length > 0) {\n            rep('(load-file \"${cmdargs[0]}\")');\n            Compat.exit(0);\n        }\n\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/Step9_try.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\nimport haxe.rtti.Meta;\n\nclass Step9_try {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function qq_loop(elt:MalType, acc:MalType) {\n        switch elt {\n            case MalList([MalSymbol(\"splice-unquote\"), arg]):\n                return MalList([MalSymbol(\"concat\"), arg, acc]);\n            case _:\n                return MalList([MalSymbol(\"cons\"), quasiquote(elt), acc]);\n        }\n    }\n    static function qq_foldr(xs:Array<MalType>) {\n        var acc = MalList([]);\n        for (i in 1 ... xs.length+1) {\n            acc = qq_loop (xs[xs.length-i], acc);\n        }\n        return acc;\n    }\n    static function quasiquote(ast:MalType) {\n        return switch(ast) {\n            case MalList([MalSymbol(\"unquote\"), arg]): arg;\n            case MalList(l): qq_foldr(l);\n            case MalVector(l): MalList([MalSymbol(\"vec\"), qq_foldr(l)]);\n            case MalSymbol(_) | MalHashMap(_): MalList([MalSymbol(\"quote\"), ast]);\n            case _: ast;\n        }\n    }\n\n    static function EVAL(ast:MalType, env:Env):MalType {\n      while (true) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            ast = alst[2];\n            env = let_env;\n            continue; // TCO\n        case MalSymbol(\"quote\"):\n            return alst[1];\n        case MalSymbol(\"quasiquote\"):\n            ast = quasiquote(alst[1]);\n            continue; // TCO\n        case MalSymbol(\"defmacro!\"):\n            var func = EVAL(alst[2], env);\n            return switch (func) {\n                case MalFunc(f,ast,e,params,_,_):\n                    env.set(alst[1], MalFunc(f,ast,e,params,true,nil));\n                case _:\n                    throw \"Invalid defmacro! call\";\n            }\n        case MalSymbol(\"try*\"):\n            try {\n                return EVAL(alst[1], env);\n            } catch (err:Dynamic) {\n                if (alst.length > 2) {\n                    switch (alst[2]) {\n                        case MalList([MalSymbol(\"catch*\"), a21, a22]): \n                            var exc;\n                            if (Type.getClass(err) == MalException) {\n                                exc = err.obj;\n                            } else {\n                                exc = MalString(Std.string(err));\n                            };\n                            return EVAL(a22, new Env(env, [a21], [exc]));\n                        case _:\n                            throw err;\n                    }\n                } else {\n                    throw err;\n                }\n            }\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            ast = alst[alst.length-1];\n            continue; // TCO\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                ast = alst[2];\n            } else if (alst.length > 3) {\n                ast = alst[3];\n            } else {\n                return MalNil;\n            }\n            continue; // TCO\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },alst[2],env,alst[1],false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,a,e,params,ismacro,_):\n                    if (ismacro) {\n                        ast = f(alst.slice(1));\n                        continue; // TCO\n                    }\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    if (a != null) {\n                        ast = a;\n                        env = new Env(e, _list(params), args);\n                        continue; // TCO\n                    } else {\n                        return f(args);\n                    }\n                case _: throw \"Call of non-function\";\n            }\n        }\n      }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        var evalfn = MalFunc(function(args) {\n            return EVAL(args[0], repl_env);\n        },null,null,null,false,nil);\n        repl_env.set(MalSymbol(\"eval\"), evalfn);\n\n        var cmdargs = Compat.cmdline_args();\n        var argarray = cmdargs.map(function(a) { return MalString(a); });\n        repl_env.set(MalSymbol(\"*ARGV*\"), MalList(argarray.slice(1)));\n\n        // core.mal: defined using the language itself\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n        rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n\n        if (cmdargs.length > 0) {\n            rep('(load-file \"${cmdargs[0]}\")');\n            Compat.exit(0);\n        }\n\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/StepA_mal.hx",
    "content": "import Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport reader.*;\nimport printer.*;\nimport env.*;\nimport core.*;\nimport haxe.rtti.Meta;\n\nclass StepA_mal {\n    // READ\n    static function READ(str:String):MalType {\n        return Reader.read_str(str);\n    }\n\n    // EVAL\n    static function qq_loop(elt:MalType, acc:MalType) {\n        switch elt {\n            case MalList([MalSymbol(\"splice-unquote\"), arg]):\n                return MalList([MalSymbol(\"concat\"), arg, acc]);\n            case _:\n                return MalList([MalSymbol(\"cons\"), quasiquote(elt), acc]);\n        }\n    }\n    static function qq_foldr(xs:Array<MalType>) {\n        var acc = MalList([]);\n        for (i in 1 ... xs.length+1) {\n            acc = qq_loop (xs[xs.length-i], acc);\n        }\n        return acc;\n    }\n    static function quasiquote(ast:MalType) {\n        return switch(ast) {\n            case MalList([MalSymbol(\"unquote\"), arg]): arg;\n            case MalList(l): qq_foldr(l);\n            case MalVector(l): MalList([MalSymbol(\"vec\"), qq_foldr(l)]);\n            case MalSymbol(_) | MalHashMap(_): MalList([MalSymbol(\"quote\"), ast]);\n            case _: ast;\n        }\n    }\n\n    static function EVAL(ast:MalType, env:Env):MalType {\n      while (true) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != MalFalse && dbgeval != MalNil)\n            Compat.println(\"EVAL: \" + PRINT(ast));\n        var alst;\n        switch (ast) {\n            case MalSymbol(s):\n                 var  res = env.get(s);\n                 if (res == null) throw \"'\" + s + \"' not found\";\n                 return res;\n            case MalList(l):\n                 alst = l;\n            case MalVector(l):\n                return MalVector(l.map(function(x) { return EVAL(x, env); }));\n            case MalHashMap(m):\n                var new_map = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_map[k] = EVAL(m[k], env);\n                }\n                return MalHashMap(new_map);\n            case _: return ast;\n        }\n        // apply\n        if (alst.length == 0) { return ast; }\n        switch (alst[0]) {\n        case MalSymbol(\"def!\"):\n            return env.set(alst[1], EVAL(alst[2], env));\n        case MalSymbol(\"let*\"):\n            var let_env = new Env(env);\n            switch (alst[1]) {\n                case MalList(l) | MalVector(l):\n                    for (i in 0...l.length) {\n                        if ((i%2) > 0) { continue; }\n                        let_env.set(l[i], EVAL(l[i+1], let_env));\n                    }\n                case _: throw \"Invalid let*\";\n            }\n            ast = alst[2];\n            env = let_env;\n            continue; // TCO\n        case MalSymbol(\"quote\"):\n            return alst[1];\n        case MalSymbol(\"quasiquote\"):\n            ast = quasiquote(alst[1]);\n            continue; // TCO\n        case MalSymbol(\"defmacro!\"):\n            var func = EVAL(alst[2], env);\n            return switch (func) {\n                case MalFunc(f,ast,e,params,_,_):\n                    env.set(alst[1], MalFunc(f,ast,e,params,true,nil));\n                case _:\n                    throw \"Invalid defmacro! call\";\n            }\n        case MalSymbol(\"try*\"):\n            try {\n                return EVAL(alst[1], env);\n            } catch (err:Dynamic) {\n                if (alst.length > 2) {\n                    switch (alst[2]) {\n                        case MalList([MalSymbol(\"catch*\"), a21, a22]): \n                            var exc;\n                            if (Type.getClass(err) == MalException) {\n                                exc = err.obj;\n                            } else {\n                                exc = MalString(Std.string(err));\n                            };\n                            return EVAL(a22, new Env(env, [a21], [exc]));\n                        case _:\n                            throw err;\n                    }\n                } else {\n                    throw err;\n                }\n            }\n        case MalSymbol(\"do\"):\n            for (i in 1...alst.length-1)\n                EVAL(alst[i], env);\n            ast = alst[alst.length-1];\n            continue; // TCO\n        case MalSymbol(\"if\"):\n            var cond = EVAL(alst[1], env);\n            if (cond != MalFalse && cond != MalNil) {\n                ast = alst[2];\n            } else if (alst.length > 3) {\n                ast = alst[3];\n            } else {\n                return MalNil;\n            }\n            continue; // TCO\n        case MalSymbol(\"fn*\"):\n            return MalFunc(function (args) {\n                return EVAL(alst[2], new Env(env, _list(alst[1]), args));\n            },alst[2],env,alst[1],false,nil);\n        case _:\n            switch ( EVAL(alst[0], env)) {\n                case MalFunc(f,a,e,params,ismacro,_):\n                    if (ismacro) {\n                        ast = f(alst.slice(1));\n                        continue; // TCO\n                    }\n                    var args = alst.slice(1).map(function(x) { return EVAL(x, env); });\n                    if (a != null) {\n                        ast = a;\n                        env = new Env(e, _list(params), args);\n                        continue; // TCO\n                    } else {\n                        return f(args);\n                    }\n                case _: throw \"Call of non-function\";\n            }\n        }\n      }\n    }\n\n    // PRINT\n    static function PRINT(exp:MalType):String {\n        return Printer.pr_str(exp, true);\n    }\n\n    // repl\n    static var repl_env = new Env(null);\n\n    static function rep(line:String):String {\n        return PRINT(EVAL(READ(line), repl_env));\n    }\n\n    public static function main() {\n        // core.EXT: defined using Haxe\n        for (k in Core.ns.keys()) {\n            repl_env.set(MalSymbol(k), MalFunc(Core.ns[k],null,null,null,false,nil));\n        }\n\n        var evalfn = MalFunc(function(args) {\n            return EVAL(args[0], repl_env);\n        },null,null,null,false,nil);\n        repl_env.set(MalSymbol(\"eval\"), evalfn);\n\n        var cmdargs = Compat.cmdline_args();\n        var argarray = cmdargs.map(function(a) { return MalString(a); });\n        repl_env.set(MalSymbol(\"*ARGV*\"), MalList(argarray.slice(1)));\n\n        // core.mal: defined using the language itself\n        rep(\"(def! *host-language* \\\"haxe\\\")\");\n        rep(\"(def! not (fn* (a) (if a false true)))\");\n        rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n\n        if (cmdargs.length > 0) {\n            rep('(load-file \"${cmdargs[0]}\")');\n            Compat.exit(0);\n        }\n\n        rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n        while (true) {\n            try {\n                var line = Compat.readline(\"user> \");\n                if (line == \"\") { continue; }\n                Compat.println(rep(line));\n            } catch (exc:BlankLine) {\n                continue;\n            } catch (exc:haxe.io.Eof) {\n                Compat.exit(0);\n            } catch (exc:Dynamic) {\n                if (Type.getClass(exc) == MalException) {\n                    Compat.println(\"Error: \" + Printer.pr_str(exc.obj, true));\n                } else {\n                    Compat.println(\"Error: \" + exc);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/core/Core.hx",
    "content": "package core;\n\nimport Compat;\nimport types.Types.MalType;\nimport types.Types.*;\nimport types.MalException;\nimport printer.Printer;\nimport reader.Reader;\nimport haxe.Timer;\n\nclass Core {\n    static function BoolFn(v) {\n        if (v) { return MalTrue; }\n        else   { return MalFalse; }\n    }\n\n    static function BoolOp(op) {\n        return function(args:Array<MalType>) {\n            return switch (args) {\n                case [MalInt(a), MalInt(b)]: BoolFn(op(a,b));\n                case _: throw \"Invalid boolean op call\"; \n            }\n            \n        };\n    }\n\n    static function NumOp(op) {\n        return function(args:Array<MalType>) {\n            return switch (args) {\n                case [MalInt(a), MalInt(b)]: MalInt(op(a,b));\n                case _: throw \"Invalid numeric op call\"; \n            }\n            \n        };\n    }\n\n    static var start = Timer.stamp();\n    static function time_ms(args) {\n        return MalInt(Std.int(1000 * (Timer.stamp()-start)));\n    }\n\n    static function equal_Q(args) {\n        return BoolFn(_equal_Q(args[0],args[1]));\n    }\n\n    static function pr_str(args) {\n        return MalString(\n            args.map(function(s) { return Printer.pr_str(s,true); }).join(\" \")\n        );\n    }\n    static function str(args) {\n        return MalString(\n            args.map(function(s) { return Printer.pr_str(s,false); }).join(\"\")\n        );\n    }\n    static function prn(args) {\n        Compat.println(args.map(function(s) { return Printer.pr_str(s,true); }).join(\" \"));\n        return nil;\n    }\n    static function println(args) {\n        Compat.println(args.map(function(s) { return Printer.pr_str(s,false); }).join(\" \"));\n        return nil;\n    }\n\n    static function symbol(args) {\n        return switch (args[0]) {\n            case MalString(s): MalSymbol(s);\n            case MalSymbol(_): args[0];\n            case _: throw \"Invalid symbol call\";\n        }\n    }\n\n    static function keyword(args) {\n        return switch (args[0]) {\n            case MalString(s):\n                if (keyword_Q(args[0])) {\n                    args[0];\n                } else {\n                    MalString(\"\\x7f\" + s);\n                }\n            case _: throw \"Invalid keyword call\";\n        }\n    }\n\n    static function read_string(args) {\n        return switch (args[0]) {\n            case MalString(s): Reader.read_str(s);\n            case _: throw \"invalid read_str call\";\n        }\n    }\n\n    static function readline(args) {\n        return switch (args[0]) {\n            case MalString(prompt):\n                try {\n                    MalString(Compat.readline(prompt));\n                } catch (exc:haxe.io.Eof) {\n                    nil;\n                }\n            case _: throw \"invalid readline call\";\n        }\n    }\n\n    static function slurp(args) {\n        return switch (args[0]) {\n            case MalString(s):\n                MalString(Compat.slurp(s));\n            case _: throw \"invalid slurp call\";\n        }\n    }\n\n    // sequential functions\n    static function sequential_Q(args) {\n        return BoolFn(list_Q(args[0]) || vector_Q(args[0]));\n    }\n\n    static function cons(args) {\n        return switch [args[0], args[1]] {\n            case [a, MalList(l)] |\n                 [a, MalVector(l)]:\n                MalList([a].concat(l));\n            case [a, MalNil]:\n                MalList([a]);\n            case _: throw \"Invalid cons call\";\n        }\n    }\n\n    static function do_concat(args:Array<MalType>) {\n        var res:Array<MalType> = [];\n        for (a in args) {\n            switch (a) {\n                case MalList(l) | MalVector(l): \n                    res = res.concat(l);\n                case MalNil:\n                    continue;\n                case _:\n                    throw \"concat called with non-sequence\";\n            }\n        }\n        return MalList(res);\n    }\n\n    static function do_vec(args:Array<MalType>) {\n        switch (args[0]) {\n            case MalList(l):\n                return MalVector(l);\n            case MalVector(l):\n                return args[0];\n            case _:\n                throw \"vec called with non-sequence\";\n        }\n    }\n\n    static function nth(args) {\n        return switch [args[0], args[1]] {\n            case [seq, MalInt(idx)]:\n                _nth(seq, idx);\n            case _: throw \"Invalid nth call\";\n        }\n    }\n\n    static function empty_Q(args) {\n        return switch (args[0]) {\n            case MalList(l) | MalVector(l): \n                if (l.length == 0) { MalTrue; }\n                else               { MalFalse; }\n            case MalNil: MalTrue;\n            case _: MalFalse;\n        }\n    }\n\n    static function count(args) {\n        return switch (args[0]) {\n            case MalList(l) | MalVector(l): MalInt(l.length);\n            case MalNil: MalInt(0);\n            case _: throw \"count called on non-sequence\";\n        }\n    }\n\n    static function apply(args) {\n        return switch [args[0], args[args.length-1]] {\n            case [MalFunc(f,_,_,_,_), MalList(l)] |\n                 [MalFunc(f,_,_,_,_), MalVector(l)]:\n                var fargs = args.slice(1,args.length-1).concat(l);\n                return f(fargs);\n            case _: throw \"Invalid apply call\";\n        }\n    }\n\n    static function do_map(args) {\n        return switch [args[0], args[1]] {\n            case [MalFunc(f,_,_,_,_), MalList(l)] |\n                 [MalFunc(f,_,_,_,_), MalVector(l)]:\n                return MalList(l.map(function(x) { return f([x]); }));\n            case _: throw \"Invalid map call\";\n        }\n    }\n\n    static function conj(args) {\n        return switch (args[0]) {\n            case MalList(l):\n                var elems = args.slice(1);\n                elems.reverse();\n                MalList(elems.concat(l));\n            case MalVector(l):\n                MalVector(l.concat(args.slice(1)));\n            case _: throw \"Invalid conj call\";\n        }\n    }\n\n    static function seq(args) {\n        return switch (args[0]) {\n            case MalList(l):\n                l.length > 0 ? args[0] : nil;\n            case MalVector(l):\n                l.length > 0 ? MalList(l.slice(0)) : nil;\n            case MalString(s):\n                if (s.length == 0) { return nil; }\n                MalList(s.split(\"\").map(function(c) { return MalString(c); }));\n            case MalNil:\n                nil;\n            case _: throw \"seq: called on non-sequence\";\n        }\n    }\n\n\n    // hash-map functions\n\n    public static function get(hm:MalType, key:MalType) {\n        return switch [hm, key] {\n            case [MalHashMap(m), MalString(k)]:\n                if (m.exists(k)) {\n                    m[k];\n                } else {\n                    nil;\n                }\n            case [nil, MalString(k)]:\n                nil;\n            case _: throw \"invalid get call\";\n        }\n    }\n\n    public static function assoc(args) {\n        return switch (args[0]) {\n            case MalHashMap(m):\n                var new_m = _clone(args[0]);\n                MalHashMap(assoc_BANG(new_m, args.slice(1)));\n            case _: throw \"invalid assoc call\";\n        }\n    }\n\n    public static function dissoc(args) {\n        return switch (args[0]) {\n            case MalHashMap(m):\n                var new_m = _clone(args[0]);\n                MalHashMap(dissoc_BANG(new_m, args.slice(1)));\n            case _: throw \"invalid dissoc call\";\n        }\n    }\n\n    public static function contains_Q(hm:MalType, key:MalType) {\n        return switch [hm, key] {\n            case [MalHashMap(m), MalString(k)]:\n                m.exists(k);\n            case _: throw \"invalid contains? call\";\n        }\n    }\n\n    public static function keys(hm:MalType) {\n        return switch (hm) {\n            case MalHashMap(m):\n                MalList([for (k in m.keys()) MalString(k)]);\n            case _: throw \"invalid keys call\";\n        }\n    }\n\n    public static function vals(hm:MalType) {\n        return switch (hm) {\n            case MalHashMap(m):\n                MalList([for (k in m.keys()) m[k]]);\n            case _: throw \"invalid vals call\";\n        }\n    }\n\n    // metadata functions\n    static function meta(args) {\n        return switch (args[0]) {\n            case MalFunc(f,_,_,_,_,meta): meta;\n            case _: throw \"meta called on non-function\";\n        }\n    }\n\n    static function with_meta(args) {\n        return switch (args[0]) {\n            case MalFunc(f,a,e,p,mac,_):\n                MalFunc(f,a,e,p,mac,args[1]);\n            case _: throw \"with_meta called on non-function\";\n        }\n    }\n\n\n\n    // atom functions\n\n    static function deref(args) {\n        return switch (args[0]) {\n            case MalAtom(v): v.val;\n            case _: throw \"deref called on non-atom\";\n        }\n    }\n\n    static function reset_BANG(args) {\n        return switch (args[0]) {\n            case MalAtom(v): v.val = args[1];\n            case _: throw \"reset! called on non-atom\";\n        }\n    }\n\n    static function swap_BANG(args) {\n        return switch [args[0], args[1]] {\n            case [MalAtom(v), MalFunc(f,_,_,_,_)]:\n                var fargs = [v.val].concat(args.slice(2));\n                v.val = f(fargs);\n                v.val;\n            case _: throw \"swap! called on non-atom\";\n        }\n    }\n\n\n    public static var ns:Map<String,Array<MalType> -> MalType> = [\n        \"=\" => function(a) { return BoolFn(_equal_Q(a[0],a[1])); },\n        \"throw\" => function(a) { throw new MalException(a[0]); },\n\n        \"nil?\" => function(a) { return BoolFn(nil_Q(a[0])); },\n        \"true?\" => function(a) { return BoolFn(true_Q(a[0])); },\n        \"false?\" => function(a) { return BoolFn(false_Q(a[0])); },\n        \"string?\" => function(a) { return BoolFn(string_Q(a[0])); },\n        \"symbol\" => symbol,\n        \"symbol?\" => function(a) { return BoolFn(symbol_Q(a[0])); },\n        \"keyword\" => keyword,\n        \"keyword?\" => function(a) { return BoolFn(keyword_Q(a[0])); },\n        \"number?\" => function(a) { return BoolFn(number_Q(a[0])); },\n        \"fn?\" => function(a) { return BoolFn(_fn_Q(a[0])); },\n        \"macro?\" => function(a) { return BoolFn(_macro_Q(a[0])); },\n\n        \"pr-str\" => pr_str,\n        \"str\" => str,\n        \"prn\" => prn,\n        \"println\" => println,\n        \"read-string\" => read_string,\n        \"readline\" => readline,\n        \"slurp\" => slurp,\n\n        \"<\" => BoolOp(function(a,b) {return a<b;}),\n        \"<=\" => BoolOp(function(a,b) {return a<=b;}),\n        \">\" => BoolOp(function(a,b) {return a>b;}),\n        \">=\" => BoolOp(function(a,b) {return a>=b;}),\n        \"+\" => NumOp(function(a,b) {return a+b;}),\n        \"-\" => NumOp(function(a,b) {return a-b;}),\n        \"*\" => NumOp(function(a,b) {return a*b;}),\n        \"/\" => NumOp(function(a,b) {return Std.int(a/b);}),\n        \"time-ms\" => time_ms,\n\n        \"list\" => function(a) { return MalList(a); },\n        \"list?\" => function(a) { return BoolFn(list_Q(a[0])); },\n        \"vector\" => function(a) { return MalVector(a); },\n        \"vector?\" => function(a) { return BoolFn(vector_Q(a[0])); },\n        \"hash-map\" => hash_map,\n        \"map?\" => function(a) { return BoolFn(hash_map_Q(a[0])); },\n        \"assoc\" => assoc,\n        \"dissoc\" => dissoc,\n        \"get\" => function(a) { return get(a[0],a[1]); },\n        \"contains?\" => function(a) { return BoolFn(contains_Q(a[0], a[1])); },\n        \"keys\" => function(a) { return keys(a[0]); } ,\n        \"vals\" => function(a) { return vals(a[0]); } ,\n\n        \"sequential?\" => sequential_Q,\n        \"cons\" => cons,\n        \"concat\" => do_concat,\n        \"vec\" => do_vec,\n\n        \"nth\" => nth,\n        \"first\" => function(a) { return first(a[0]); },\n        \"rest\" => function(a) { return rest(a[0]); },\n        \"empty?\" => empty_Q,\n        \"count\" => count,\n        \"apply\" => apply,\n        \"map\" => do_map,\n\n        \"conj\" => conj,\n        \"seq\" => seq,\n\n        \"meta\" => meta,\n        \"with-meta\" => with_meta,\n        \"atom\" => function(a) { return MalAtom({val:a[0]}); },\n        \"atom?\" => function(a) { return BoolFn(atom_Q(a[0])); },\n        \"deref\" => deref,\n        \"reset!\" => reset_BANG,\n        \"swap!\" => swap_BANG\n    ];\n}\n"
  },
  {
    "path": "impls/haxe/env/Env.hx",
    "content": "package env;\n\nimport types.Types.MalType;\nimport types.Types.*;\n\nclass Env {\n    var data = new Map<String,MalType>();\n    var outer:Env = null;\n    \n    public function new(outer:Env,\n                        binds:Array<MalType> = null,\n                        exprs:Array<MalType> = null) {\n        this.outer = outer;\n\n        if (binds != null) {\n            for (i in 0...binds.length) {\n                var b = binds[i], e = exprs[i];\n                switch (b) {\n                    case MalSymbol(\"&\"):\n                        switch (binds[i+1]) {\n                            case MalSymbol(b2):\n                                data[b2] = MalList(exprs.slice(i));\n                            case _:\n                                throw \"invalid vararg binding\";\n                        }\n                        break;\n                    case MalSymbol(s):\n                        data[s] = e;\n                    case _: throw \"invalid bind\";\n                }\n            }\n        }\n    }\n\n    public function set(key:MalType, val:MalType) {\n        switch (key) {\n            case MalSymbol(s): data[s] = val;\n            case _: throw \"Invalid Env.set call\";\n        }\n        return val;\n    }\n\n    public function get(key:String):MalType {\n        if (data.exists(key))   return data.get(key);\n        else if (outer != null) return outer.get(key);\n        else                    return null;\n    }\n}\n"
  },
  {
    "path": "impls/haxe/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nvar ffi = require('ffi-napi'),\n    fs = require('fs');\n\nvar rllib = ffi.Library(RL_LIB, {\n    'readline':    [ 'string', [ 'string' ] ],\n    'add_history': [ 'int',    [ 'string' ] ]});\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = prompt || \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { rllib.add_history(lines[i]); }\n        }\n    }\n\n    var line = rllib.readline(prompt);\n    if (line) {\n        rllib.add_history(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\nvar readline = exports;\n"
  },
  {
    "path": "impls/haxe/package.json",
    "content": "{\n    \"name\": \"mal\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in Haxe/Javascript\",\n    \"dependencies\": {\n        \"ffi-napi\": \"4.0.3\"\n    }\n}\n"
  },
  {
    "path": "impls/haxe/printer/Printer.hx",
    "content": "package printer;\n\nimport types.Types.MalType;\nimport types.Types.MalType.*;\n\nclass Printer {\n    public static function pr_str(exp:MalType, print_readably:Bool = true) {\n        var _r = print_readably;\n        return switch(exp) {\n            case MalNil: \"nil\";\n            case MalTrue: \"true\";\n            case MalFalse: \"false\";\n            case MalInt(v): Std.string(v);\n            case MalSymbol(v): v;\n            case MalString(v):\n                var re1 = ~/\\\\/g,\n                    re2 = ~/\"/g,\n                    re3 = ~/\\n/g;\n                //if (haxe.Utf8.charCodeAt(v, 0) == 255) {\n                if (v.charAt(0) == \"\\x7f\") {\n                    \":\" + v.substr(1);\n                } else if (_r) {\n                    '\"' + re3.replace(\n                            re2.replace(\n                              re1.replace(v, \"\\\\\\\\\"),\n                              '\\\\\"'),\n                            \"\\\\n\") + '\"';\n                } else {\n                    v;\n                }\n            case MalList(l):\n                var lst = l.map(function(e) {return pr_str(e,_r);});\n                '(${lst.join(\" \")})';\n            case MalVector(l):\n                var lst = l.map(function(e) {return pr_str(e,_r);});\n                '[${lst.join(\" \")}]';\n            case MalHashMap(m):\n                var elems = [];\n                for (k in m.keys()) {\n                    elems.push(pr_str(MalString(k), _r));\n                    elems.push(pr_str(m[k], _r));\n                }\n                '{${elems.join(\" \")}}';\n            case MalAtom(v):\n                '(atom ${pr_str(v.val,_r)})';\n            case MalFunc(f,ast,_,params,_):\n                if (ast != null) {\n                    '(fn* ${pr_str(params,true)} ${pr_str(ast)})';\n                } else {\n                    \"#<native function>\";\n                }\n            case _: throw \"unknown type for printing\";\n        }\n    }\n}\n"
  },
  {
    "path": "impls/haxe/reader/BlankLine.hx",
    "content": "package reader;\n\nclass BlankLine {\n    public function new() {\n    }\n}\n"
  },
  {
    "path": "impls/haxe/reader/Reader.hx",
    "content": "package reader;\n\nimport types.Types.MalType;\nimport types.Types.*;\n\nclass Reader {\n    // Reader class implementation\n    var tokens:Array<String>;\n    var position:Int = 0;\n\n    public function new(toks:Array<String>) {\n        tokens = toks;\n    }\n\n    public function next() {\n        return tokens[position++];\n    }\n\n    public function peek() {\n        if (tokens.length > position) {\n            return tokens[position];\n        } else {\n            return null;\n        }\n    }\n\n\n    // Static functions grouped with Reader class\n    static function tokenize(str:String) {\n        var re = ~/[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g;\n        var tokens = new Array<String>();\n        var pos = 0;\n        while (re.matchSub(str, pos)) {\n            var t = re.matched(1);\n            if (t == \"\") { break; }\n            var pos_len = re.matchedPos();\n            pos = pos_len.pos + pos_len.len;\n            if (t.charAt(0) == \";\") { continue; }\n            tokens.push(t);\n\n        }\n        return tokens;\n    }\n\n    static function read_atom(rdr:Reader) {\n        var re_int = ~/^-?[0-9][0-9]*$/;\n        var re_str = ~/^\"(?:\\\\.|[^\\\\\"])*\"$/;\n        var re_str_bad = ~/^\".*$/;\n        var token = rdr.next();\n        return switch (token) {\n            case \"nil\":\n                MalNil;\n            case \"true\":\n                MalTrue;\n            case \"false\":\n                MalFalse;\n            case _ if (token.charAt(0) == \":\"):\n                MalString(\"\\x7f\" + token.substr(1));\n            case _ if (re_int.match(token)):\n                MalInt(Std.parseInt(token));\n            case _ if (re_str.match(token)):\n                var re1 = ~/\\\\\\\\/g,\n                    re2 = ~/\\\\n/g,\n                    re3 = ~/\\\\\"/g,\n                    re4 = ~/\\x7f/g,\n                    s = token.substr(1, token.length-2);\n                MalString(re4.replace(\n                            re3.replace(\n                              re2.replace(\n                                re1.replace(\n                                  s,\n                                  \"\\x7f\"),\n                                \"\\n\"),\n                              \"\\\"\"),\n                            \"\\\\\"));\n            case _ if (re_str_bad.match(token)):\n                throw 'expected \\'\"\\', got EOF';\n            case _:\n                MalSymbol(token);\n        }\n    }\n\n    static function read_seq(rdr:Reader, start, end) {\n        var lst = [];\n        var token = rdr.next();\n        if (token != start) {\n            throw 'expected \\'${start}\\'';\n        }\n        while ((token = rdr.peek()) != end) {\n            if (token == null) {\n                throw 'expected \\'${end}\\', got EOF';\n            }\n            lst.push(read_form(rdr));\n        }\n        rdr.next();\n        return lst;\n    }\n\n    static function read_form(rdr:Reader):MalType {\n        var token = rdr.peek();\n        return switch (token) {\n            // reader macros/transforms\n            case \"'\": rdr.next();\n                      MalList([MalSymbol(\"quote\"), read_form(rdr)]);\n            case \"`\": rdr.next();\n                      MalList([MalSymbol(\"quasiquote\"), read_form(rdr)]);\n            case \"~\": rdr.next();\n                      MalList([MalSymbol(\"unquote\"), read_form(rdr)]);\n            case \"~@\": rdr.next();\n                      MalList([MalSymbol(\"splice-unquote\"), read_form(rdr)]);\n            case \"^\": rdr.next();\n                      var meta = read_form(rdr);\n                      MalList([MalSymbol(\"with-meta\"), read_form(rdr), meta]);\n            case \"@\": rdr.next();\n                      MalList([MalSymbol(\"deref\"), read_form(rdr)]);\n\n            // list\n            case \")\": throw(\"unexpected ')'\");\n            case \"(\": MalList(read_seq(rdr, '(', ')'));\n\n            // vector\n            case \"]\": throw(\"unexpected ']'\");\n            case \"[\": MalVector(read_seq(rdr, '[', ']'));\n\n            // hashmap\n            case \"}\": throw(\"unexpected '}'\");\n            case \"{\": hash_map(read_seq(rdr, '{', '}'));\n            case _: read_atom(rdr);\n        }\n    }\n\n    public static function read_str(str:String):MalType {\n        var tokens = tokenize(str);\n        if (tokens.length == 0) { throw(new BlankLine()); }\n        return read_form(new Reader(tokens));\n    }\n}\n"
  },
  {
    "path": "impls/haxe/run",
    "content": "#!/usr/bin/env bash\ncase ${haxe_MODE:-neko} in\n    neko)   exec neko    $(dirname $0)/${STEP:-stepA_mal}.n   \"${@}\" ;;\n    python) exec python3 $(dirname $0)/${STEP:-stepA_mal}.py  \"${@}\" ;;\n    js)     exec node    $(dirname $0)/${STEP:-stepA_mal}.js  \"${@}\" ;;\n    cpp)    exec         $(dirname $0)/cpp/${STEP:-stepA_mal} \"${@}\" ;;\n    *) echo \"Invalid haxe_MODE: ${haxe_MODE}\"; exit 2 ;;\nesac\n"
  },
  {
    "path": "impls/haxe/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 100000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/haxe/types/MalException.hx",
    "content": "package types;\n\nimport types.Types.MalType;\n\nclass MalException {\n    public var obj:MalType = null;\n    public function new(obj:MalType) {\n        this.obj = obj;\n    }\n}\n"
  },
  {
    "path": "impls/haxe/types/Types.hx",
    "content": "package types;\n\nimport env.Env;\n\nclass MalAtomContainer {\n}\n\nenum MalType {\n    MalNil;\n    MalTrue;\n    MalFalse;\n    MalInt(val:Int);\n    MalString(val:String);\n    MalSymbol(val:String);\n    MalList(val:Array<MalType>);\n    MalVector(val:Array<MalType>);\n    MalHashMap(val:Map<String,MalType>);\n    MalAtom(val:{val:MalType});\n    MalFunc(val:(Array<MalType>)->MalType,\n            ast:MalType,\n            env:Env,\n            params:MalType,\n            ismacro:Bool,\n            meta:MalType);\n}\n\nclass Types {\n    public static var nil:MalType = MalNil;\n\n    public static function _equal_Q(a:MalType, b:MalType) {\n        return switch [a, b] {\n            case [MalInt(va), MalInt(vb)]: va == vb;\n            case [MalString(va), MalString(vb)] |\n                 [MalSymbol(va), MalSymbol(vb)]: va == vb;\n            case [MalList(la), MalList(lb)] |\n                 [MalList(la), MalVector(lb)] |\n                 [MalVector(la), MalList(lb)] |\n                 [MalVector(la), MalVector(lb)]:\n                if (la.length != lb.length) { return false; }\n                for (i in 0...la.length) {\n                    if (!_equal_Q(la[i], lb[i])) {\n                        false;\n                    }\n                }\n                true;\n            case [MalHashMap(ma), MalHashMap(mb)]:\n                var maks = ma.keys(),\n                    mbks = mb.keys(),\n                    malen = 0,\n                    mblen = 0;\n                for (k in maks) {\n                    malen += 1;\n                    if ((!mb.exists(k)) || !_equal_Q(ma[k], mb[k])) {\n                        return false;\n                    }\n                }\n                for (k in mbks) { mblen += 1; }\n                if (malen != mblen) { return false; }\n                true;\n            case _: a == b;\n        }\n    }\n\n    public static function _clone(a:MalType) {\n        return switch (a) {\n            case MalHashMap(m):\n                var new_m = new Map<String,MalType>();\n                for (k in m.keys()) {\n                    new_m[k] = m[k];\n                }\n                return new_m;\n            case _: throw \"unsupported clone call\";\n        }\n    }\n\n    public static function _fn_Q(x:MalType) {\n        return switch (x) {\n            case MalFunc(_,_,_,_,ismacro,_): !ismacro;\n            case _: false;\n        }\n    }\n\n    public static function _macro_Q(x:MalType) {\n        return switch (x) {\n            case MalFunc(_,_,_,_,ismacro,_): ismacro;\n            case _: false;\n        }\n    }\n\n    public static function nil_Q(x:MalType) {\n        return switch (x) {\n            case MalNil: true;\n            case _: false;\n        }\n    }\n\n    public static function true_Q(x:MalType) {\n        return switch (x) {\n            case MalTrue: true;\n            case _: false;\n        }\n    }\n\n    public static function false_Q(x:MalType) {\n        return switch (x) {\n            case MalFalse: true;\n            case _: false;\n        }\n    }\n\n    public static function string_Q(x:MalType) {\n        return switch (x) {\n            case MalString(s): s.charAt(0) != \"\\x7f\";\n            case _: false;\n        }\n    }\n\n\n    public static function symbol_Q(x:MalType) {\n        return switch (x) {\n            case MalSymbol(_): true;\n            case _: false;\n        }\n    }\n\n    public static function keyword_Q(x:MalType) {\n        return switch (x) {\n            case MalString(s):\n                s.charAt(0) == \"\\x7f\";\n            case _: false;\n        }\n    }\n\n    public static function number_Q(x:MalType) {\n        return switch (x) {\n            case MalInt(_): true;\n            case _: false;\n        }\n    }\n\n\n    // Sequence operations\n    public static function list_Q(x:MalType) {\n        return switch (x) {\n            case MalList(_): true;\n            case _: false;\n        }\n    }\n\n    public static function vector_Q(x:MalType) {\n        return switch (x) {\n            case MalVector(_): true;\n            case _: false;\n        }\n    }\n\n    public static function first(seq:MalType) {\n        return switch (seq) {\n            case MalList(l) | MalVector(l):\n                if (l.length == 0) { nil; }\n                else               { l[0]; }\n            case MalNil: MalNil;\n            case _: throw \"first called on non-sequence\";\n        }\n    }\n\n    public static function rest(seq:MalType) {\n        return switch (seq) {\n            case MalList(l) | MalVector(l):\n                if (l.length <= 1) { MalList([]); }\n                else               { MalList(l.slice(1)); }\n            case MalNil: MalList([]);\n            case _: throw \"rest called on non-sequence\";\n        }\n    }\n\n    public static function _nth(seq:MalType, idx:Int) {\n        return switch (seq) {\n            case MalList(l) | MalVector(l):\n                if (l.length > idx) {\n                    l[idx];\n                } else {\n                    throw \"nth index out of bounds\";\n                }\n            case _: throw \"nth called on non-sequence\";\n        }\n    }\n\n    public static function _list(seq:MalType) {\n        return switch (seq) {\n            case MalList(l) | MalVector(l): l;\n            case _: throw \"_array called on non-sequence\";\n        }\n    }\n\n    public static function _map(hm:MalType) {\n        return switch (hm) {\n            case MalHashMap(m): m;\n            case _: throw \"_map called on non-hash-map\";\n        }\n    }\n\n    public static function last(seq:MalType) {\n        return switch (seq) {\n            case MalList(l) | MalVector(l):\n                if (l.length == 0) { nil; }\n                else               { l[l.length-1]; }\n            case _: throw \"last called on non-sequence\";\n        }\n    }\n\n    public static function hash_map(kvs:Array<MalType>) {\n        var m = new Map<String,MalType>();\n        return MalHashMap(assoc_BANG(m, kvs));\n    }\n\n    public static function assoc_BANG(m:Map<String,MalType>,\n                                      kvs:Array<MalType>) {\n        for (i in 0...kvs.length) {\n            if (i % 2 > 0) { continue; }\n            switch (kvs[i]) {\n                case MalString(k):\n                    m[k] = kvs[i+1];\n                case _: throw \"invalid assoc! call\";\n            }\n        }\n        return m;\n    }\n\n    public static function dissoc_BANG(m:Map<String,MalType>,\n                                      ks:Array<MalType>) {\n        for (i in 0...ks.length) {\n            switch (ks[i]) {\n                case MalString(k):\n                    m.remove(k);\n                case _: throw \"invalid dissoc! call\";\n            }\n        }\n        return m;\n    }\n\n    public static function hash_map_Q(x:MalType) {\n        return switch (x) {\n            case MalHashMap(_): true;\n            case _: false;\n        }\n    }\n\n    public static function atom_Q(x:MalType) {\n        return switch (x) {\n            case MalAtom(_): true;\n            case _: false;\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "impls/hy/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Hy\nRUN apt-get -y install python-pip && \\\n    pip install hy && \\\n    mkdir /.cache && \\\n    chmod uog+rwx /.cache\n"
  },
  {
    "path": "impls/hy/Makefile",
    "content": "all: mal.hy\n\nmal.hy: stepA_mal.hy\n\tcp $< $@\n\nclean:\n\trm -f mal.hy *.pyc\n"
  },
  {
    "path": "impls/hy/core.hy",
    "content": "(import [hy.models [HyKeyword :as Keyword HyString :as Str HySymbol :as Sym]])\n(import [copy [copy]])\n(import [time [time]])\n(import [mal_types [MalException Atom clone]])\n(import [reader [read-str]])\n(import [printer [pr-str]])\n\n(defn sequential? [a]\n  (or (instance? tuple a) (instance? list a)))\n\n(defn equal [a b]\n  (if (and (sequential? a) (sequential? b) (= (len a) (len b)))\n      (every? (fn [[a b]] (equal a b)) (zip a b))\n\n      (and (instance? dict a) (instance? dict b) (= (.keys a) (.keys b)))\n      (every? (fn [k] (and (equal (get a k) (get b k)))) a)\n\n      (= (type a) (type b))\n      (= a b)\n\n      False))\n\n(def ns\n  {\"=\"        equal\n   \"throw\"    (fn [a] (raise (MalException a)))\n\n   \"nil?\"     none?\n   \"true?\"    (fn [a] (and (instance? bool a) (= a True)))\n   \"false?\"   (fn [a] (and (instance? bool a) (= a False)))\n   \"number?\"  (fn [a] (and (not (instance? bool a)) (instance? int a)))\n   \"string?\"  (fn [a] (and (string? a) (not (keyword? a))))\n   \"symbol\"   (fn [a] (Sym a))\n   \"symbol?\"  (fn [a] (instance? Sym a))\n   \"keyword\"  (fn [a] (Keyword (if (keyword? a) a (+ \":\" a))))\n   \"keyword?\" (fn [a] (keyword? a))\n   \"fn?\"      (fn [a] (and (callable a) (or (not (hasattr a \"macro\"))\n                                            (not a.macro))))\n   \"macro?\"   (fn [a] (and (callable a) (and (hasattr a \"macro\") a.macro)))\n\n   \"pr-str\"  (fn [&rest a] (Str (.join \" \" (map (fn [e] (pr-str e True)) a))))\n   \"str\"     (fn [&rest a] (Str (.join \"\" (map (fn [e] (pr-str e False)) a))))\n   \"prn\"     (fn [&rest a] (print (.join \" \" (map (fn [e] (pr-str e True)) a))))\n   \"println\" (fn [&rest a] (print (.join \" \" (map (fn [e] (pr-str e False)) a))))\n   \"read-string\" read-str\n   \"readline\" (fn [a] (Str (raw_input a)))\n   \"slurp\"   (fn [a] (Str (-> a open .read)))\n\n   \"<\"  <\n   \"<=\" <=\n   \">\"  >\n   \">=\" >=\n   \"+\"  +\n   \"-\"  -\n   \"*\"  *\n   \"/\"  (fn [a b] (int (/ a b)))\n   \"time-ms\"   (fn [] (int (* 1000 (time))))\n\n   \"list\"      (fn [&rest args] (tuple args))\n   \"list?\"     (fn [a] (instance? tuple a))\n   \"vector\"    (fn [&rest a] (list a))\n   \"vector?\"   (fn [a] (instance? list a))\n   \"hash-map\"  (fn [&rest a] (dict (partition a 2)))\n   \"map?\"      (fn [a] (instance? dict a))\n   \"assoc\"     (fn [m &rest a] (setv m (copy m))\n                               (for [[k v] (partition a 2)] (assoc m k v)) m)\n   \"dissoc\"    (fn [m &rest a] (setv m (copy m))\n                               (for [k a] (if (.has_key m k) (.pop m k))) m)\n   \"get\"       (fn [m a] (if (and m (.has_key m a)) (get m a)))\n   \"contains?\" (fn [m a] (if (none? m) None (.has_key m a)))\n   \"keys\"      (fn [m] (tuple (.keys m)))\n   \"vals\"      (fn [m] (tuple (.values m)))\n\n   \"sequential?\" sequential?\n   \"cons\"   (fn [a b] (tuple (chain [a] b)))\n   \"concat\" (fn [&rest a] (tuple (apply chain a)))\n   \"vec\"    (fn [a] (list a))\n   \"nth\"    (fn [a b] (get a b))\n   \"first\"  (fn [a] (if (none? a) None (first a)))\n   \"rest\"   (fn [a] (if (none? a) (,) (tuple (rest a))))\n   \"empty?\" empty?\n   \"count\"  (fn [a] (if (none? a) 0 (len a)))\n   \"apply\"  (fn [f &rest a] (apply f (+ (list (butlast a)) (list (last a)))))\n   \"map\"    (fn [f a] (tuple (map f a)))\n\n   \"conj\"   (fn [a &rest xs] (if (instance? list a) (+ a (list xs))\n                                 (tuple (+ (tuple (reversed xs)) a))))\n   \"seq\"    (fn [a] (if (or (none? a) (empty? a)) None\n                        (string? a) (tuple (map Str a))\n                        (tuple a)))\n\n   \"meta\"   (fn [a] (if (hasattr a \"meta\") a.meta))\n   \"with-meta\" (fn [a b] (setv a (clone a)) (setv a.meta b) a)\n   \"atom\"   (fn [a] (Atom a))\n   \"atom?\"  (fn [a] (instance? Atom a))\n   \"deref\"  (fn [a] a.val)\n   \"reset!\" (fn [a b] (do (setv a.val b) b))\n   \"swap!\"  (fn [a f &rest xs] (do (setv a.val (apply f (+ (, a.val) xs))) a.val))\n   })\n"
  },
  {
    "path": "impls/hy/env.hy",
    "content": "(import [hy.models [HySymbol :as Sym]])\n\n(defn env-new [&optional [outer None] [binds []] [exprs []]]\n  (setv env {:outer outer})\n  (while binds\n    (if\n      (= (Sym \"&\") (first binds))\n      (do (assoc env (nth binds 1) (tuple exprs)) (break))\n\n      True\n      (do (assoc env (first binds) (first exprs))\n          (setv binds (list (rest binds))\n                exprs (list (rest exprs))))))\n  env)\n\n(defn env-find [env k]\n  (if\n    (.has_key env k)  env\n    (get env ':outer) (env-find (get env ':outer) k)\n    True              None))\n\n(defn env-get [env k]\n  (setv e (env-find env k))\n  (if-not e\n    (raise (Exception (+ \"'\" k \"' not found\"))))\n  (get e k))\n\n(defn env-set [env k v]\n  (assoc env k v)\n  v)\n\n"
  },
  {
    "path": "impls/hy/mal_types.hy",
    "content": "(import [types :as pytypes])\n\n(defclass MalException [Exception]\n  (defn --init-- [self val] (setv self.val val)))\n\n(defclass Atom []\n  (defn --init-- [self val] (setv self.val val)))\n\n(defn clone [obj]\n  (if (= (type obj) pytypes.FunctionType)\n    (pytypes.FunctionType obj.__code__ obj.__globals__\n                          :name obj.__name__\n                          :argdefs obj.__defaults__\n                          :closure obj.__closure__)\n    obj))\n"
  },
  {
    "path": "impls/hy/printer.hy",
    "content": "(import [hy.models [HyInteger :as Int HyKeyword :as Keyword\n                    HyString :as Str HySymbol :as Sym]])\n(import [mal_types [Atom]])\n\n(defn escape [s]\n  (-> (str s) (.replace \"\\\\\" \"\\\\\\\\\")\n              (.replace \"\\\"\" \"\\\\\\\"\")\n              (.replace \"\\n\" \"\\\\n\")))\n\n(defn pr-str [obj &optional [print-readably True]]\n  (setv _r print-readably\n        t (type obj))\n  (Str\n    (if\n      (none? obj)   \"nil\"\n      (= t bool)    (if obj \"true\" \"false\")\n      (= t Keyword) (+ \":\" (name obj))\n      (= t Str)     (if _r (+ \"\\\"\" (escape obj) \"\\\"\") obj)\n      (= t tuple)   (+ \"(\" (.join \" \" (map (fn [x] (pr-str x _r)) obj)) \")\")\n      (= t list)    (+ \"[\" (.join \" \" (map (fn [x] (pr-str x _r)) obj)) \"]\")\n      (= t dict)    (+ \"{\" (.join \" \" (map (fn [k] (+ (pr-str k _r) \" \"\n                                                      (pr-str (get obj k) _r)))\n                                           obj)) \"}\")\n      (instance? Atom obj) (+ \"(atom \" (pr-str obj.val _r) \")\")\n      True          (str obj))))\n"
  },
  {
    "path": "impls/hy/reader.hy",
    "content": "(import [hy.models [HyInteger :as Int HyKeyword :as Keyword\n                    HyString :as Str HySymbol :as Sym]]\n        [re])\n\n(defclass Blank [Exception])\n\n(defclass Reader []\n  (defn --init-- [self tokens &optional [position 0]]\n    (setv self.tokens tokens self.position position))\n  (defn next [self]\n    (setv self.position (+ 1 self.position))\n    (get self.tokens (- self.position 1)))\n  (defn peek [self]\n    (if (> (len self.tokens) self.position)\n      (get self.tokens self.position)\n      None)))\n\n(def tok-re (.compile re \"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}()'\\\"`@,;]+)\"))\n(def int-re (.compile re \"-?[0-9]+$\"))\n(def str-re (.compile re \"^\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"$\"))\n(def str-bad-re (.compile re \"^\\\".*$\"))\n\n(defn tokenize [str]\n  (list-comp\n    t\n    (t (.findall re tok-re str))\n    (!= (get t 0) \";\")))\n\n(defn unescape [s]\n  (-> s (.replace \"\\\\\\\\\"   \"\\u029e\")\n        (.replace \"\\\\\\\"\"   \"\\\"\")\n        (.replace \"\\\\n\"    \"\\n\")\n        (.replace \"\\u029e\" \"\\\\\")))\n\n(defn read-atom [rdr]\n  (setv token (.next rdr))\n  (if\n    (.match re int-re token) (int token)\n    (.match re str-re token) (Str (unescape (cut token 1 -1)))\n    (.match re str-bad-re token) (raise (Exception \"expected '\\\"', got EOF\"))\n    (= \":\" (get token 0))    (Keyword token)\n    (= \"nil\" token)          None\n    (= \"true\" token)         True\n    (= \"false\" token)        False\n    True                     (Sym token)))\n\n(defn read-seq [rdr &optional [start \"(\"] [end \")\"]]\n  (setv ast   (list)\n        token (.next rdr))\n  (if (!= token start)\n    (raise (Exception (+ \"expected '\" start \"'\")))\n    (do \n      (setv token (.peek rdr))\n      (while (!= token end)\n        (if (not token) (raise (Exception (+ \"expected '\" end\n                                             \", got EOF\"))))\n        (.append ast (read-form rdr))\n        (setv token (.peek rdr)))\n      (.next rdr)\n      ast)))\n\n(defn read-form [rdr]\n  (setv token (.peek rdr))\n  (if\n    (= \";\" (get token 0)) (.next rdr)\n\n    (= \"'\" token)  (do (.next rdr)\n                       (tuple [(Sym \"quote\") (read-form rdr)]))\n    (= \"`\" token)  (do (.next rdr)\n                       (tuple [(Sym \"quasiquote\") (read-form rdr)]))\n    (= \"~\" token)  (do (.next rdr)\n                       (tuple [(Sym \"unquote\") (read-form rdr)]))\n    (= \"~@\" token) (do (.next rdr)\n                       (tuple [(Sym \"splice-unquote\")\n                                      (read-form rdr)]))\n    (= \"^\" token)  (do (.next rdr)\n                       (setv meta (read-form rdr))\n                       (tuple [(Sym \"with-meta\") (read-form rdr) meta]))\n    (= \"@\" token)  (do (.next rdr)\n                       (tuple [(Sym \"deref\") (read-form rdr)]))\n\n    (= \")\" token)  (raise (Exception \"unexpected ')'\"))\n    (= \"(\" token)  (tuple (read-seq rdr \"(\" \")\"))\n\n    (= \"]\" token)  (raise (Exception \"unexpected ')'\"))\n    (= \"[\" token)  (read-seq rdr \"[\" \"]\")\n\n    (= \"}\" token)  (raise (Exception \"unexpected '}'\"))\n    (= \"{\" token)  (dict (partition (read-seq rdr \"{\" \"}\") 2))\n\n    True           (read-atom rdr)))\n\n(defn read-str [str]\n  (setv tokens (tokenize str))\n  (if (= 0 (len tokens)) (raise (Blank \"blank line\")))\n  (read-form (Reader tokens)))\n"
  },
  {
    "path": "impls/hy/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal}.hy \"${@}\"\n"
  },
  {
    "path": "impls/hy/step0_repl.hy",
    "content": "#!/usr/bin/env hy\n\n(defn READ [str]\n  str)\n\n(defn EVAL [ast env]\n  ast)\n\n(defn PRINT [exp]\n exp)\n\n(defn REP [str]\n  (PRINT (EVAL (READ str) {})))\n\n(defmain [&rest args]\n  ;; indented to match later steps\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break)))))\n"
  },
  {
    "path": "impls/hy/step1_read_print.hy",
    "content": "#!/usr/bin/env hy\n\n(import sys traceback)\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n\n(defn READ [str]\n  (read-str str))\n\n(defn EVAL [ast env]\n  ast)\n\n(defn PRINT [exp]\n  (pr-str exp True))\n\n(defn REP [str]\n  (PRINT (EVAL (READ str) {})))\n\n(defmain [&rest args]\n  ;; indented to match later steps\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (print (.join \"\" (apply traceback.format_exception\n                                    (.exc_info sys))))))))\n"
  },
  {
    "path": "impls/hy/step2_eval.hy",
    "content": "#!/usr/bin/env hy\n\n(import sys traceback)\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn EVAL [ast env]\n  ;; indented to match later steps\n      (if\n        (symbol? ast)\n        (if (.has_key env ast) (get env ast)\n          (raise (Exception (+ ast \" not found\"))))\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n                ;; apply\n                (do\n                  (setv el (list (map (fn [x] (EVAL x env)) ast))\n                        f (first el)\n                        args (list (rest el)))\n                  (apply f args))))\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env {'+ +\n               '- -\n               '* *\n               '/ (fn [a b] (int (/ a b)))})\n\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n(defmain [&rest args]\n  ;; indented to match later steps\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (print (.join \"\" (apply traceback.format_exception\n                                    (.exc_info sys))))))))\n"
  },
  {
    "path": "impls/hy/step3_env.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HySymbol :as Sym]])\n(import sys traceback)\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn EVAL [ast env]\n  ;; indented to match later steps\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (EVAL a2 env))\n\n                ;; apply\n                (do\n                  (setv el (list (map (fn [x] (EVAL x env)) ast))\n                        f (first el)\n                        args (list (rest el)))\n                  (apply f args))))))\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n(env-set repl-env '+ +)\n(env-set repl-env '- -)\n(env-set repl-env '* *)\n(env-set repl-env '/ /)\n\n(defmain [&rest args]\n  ;; indented to match later steps\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (print (.join \"\" (apply traceback.format_exception\n                                    (.exc_info sys))))))))\n"
  },
  {
    "path": "impls/hy/step4_if_fn_do.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn EVAL [ast env]\n  ;; indented to match later steps\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (EVAL a2 env))\n\n                (= (Sym \"do\") a0)\n                (last (list (map (fn [x] (EVAL x env)) (list (rest ast)))))\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (EVAL (nth ast 3) env)\n                      None)\n                    (EVAL a2 env)))\n\n                (= (Sym \"fn*\") a0)\n                (fn [&rest args]\n                  (EVAL a2 (env-new env a1 (or args []))))\n\n                ;; apply\n                (do\n                  (setv el (list (map (fn [x] (EVAL x env)) ast))\n                        f (first el)\n                        args (list (rest el)))\n                  (apply f args))))))\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n\n;; core.mal: defined using the language itself\n(REP \"(def! not (fn* [a] (if a false true)))\")\n\n(defmain [&rest args]\n  ;; indented to match later steps\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))\n"
  },
  {
    "path": "impls/hy/step5_tco.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn EVAL [ast env]\n  (setv res None)\n  (while True\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n    (setv res\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (setv ast a2)\n                  (continue)) ;; TCO\n\n                (= (Sym \"do\") a0)\n                (do (list (map (fn [x] (EVAL x env))\n                               (list (butlast (rest ast)))))\n                    (setv ast (last ast))\n                    (continue)) ;; TCO\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (do (setv ast (nth ast 3)) (continue)) ;; TCO\n                      None)\n                    (do (setv ast a2) (continue)))) ;; TCO\n\n                (= (Sym \"fn*\") a0)\n                (do\n                  (setv func (fn [&rest args]\n                               (EVAL a2 (env-new env a1 (or args []))))\n                        func.ast a2\n                        func.env env\n                        func.params a1)\n                  func)\n\n                ;; apply\n                (do\n                  (setv el (list (map (fn [x] (EVAL x env)) ast))\n                        f (first el)\n                        args (list (rest el)))\n                  (if (hasattr f \"ast\")\n                    (do (setv ast f.ast\n                              env (env-new f.env f.params args))\n                        (continue)) ;; TCO\n                    (apply f args)))))))\n    (break))\n  res)\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n\n;; core.mal: defined using the language itself\n(REP \"(def! not (fn* [a] (if a false true)))\")\n\n(defmain [&rest args]\n  ;; indented to match later steps\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))\n"
  },
  {
    "path": "impls/hy/step6_file.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HyString :as Str HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn EVAL [ast env]\n  (setv res None)\n  (while True\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n    (setv res\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (setv ast a2)\n                  (continue)) ;; TCO\n\n                (= (Sym \"do\") a0)\n                (do (list (map (fn [x] (EVAL x env))\n                               (list (butlast (rest ast)))))\n                    (setv ast (last ast))\n                    (continue)) ;; TCO\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (do (setv ast (nth ast 3)) (continue)) ;; TCO\n                      None)\n                    (do (setv ast a2) (continue)))) ;; TCO\n\n                (= (Sym \"fn*\") a0)\n                (do\n                  (setv func (fn [&rest args]\n                               (EVAL a2 (env-new env a1 (or args []))))\n                        func.ast a2\n                        func.env env\n                        func.params a1)\n                  func)\n\n                ;; apply\n                (do\n                  (setv el (list (map (fn [x] (EVAL x env)) ast))\n                        f (first el)\n                        args (list (rest el)))\n                  (if (hasattr f \"ast\")\n                    (do (setv ast f.ast\n                              env (env-new f.env f.params args))\n                        (continue)) ;; TCO\n                    (apply f args)))))))\n    (break))\n  res)\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n(env-set repl-env (Sym \"eval\") (fn [ast] (EVAL ast repl-env)))\n(env-set repl-env (Sym \"*ARGV*\") (, ))\n\n;; core.mal: defined using the language itself\n(REP \"(def! not (fn* [a] (if a false true)))\")\n(REP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(defmain [&rest args]\n  (if (>= (len args) 2)\n    (do\n      (env-set repl-env (Sym \"*ARGV*\") (tuple (map Str (rest (rest args)))))\n      (REP (+ \"(load-file \\\"\" (get args 1) \"\\\")\")))\n    (do\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))))\n"
  },
  {
    "path": "impls/hy/step7_quote.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HyString :as Str HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn qq-loop [elt acc]\n  (if (and (instance? tuple elt)\n           (= (first elt) (Sym \"splice-unquote\")))\n    (tuple [(Sym \"concat\") (get elt 1) acc])\n    (tuple [(Sym \"cons\") (QUASIQUOTE elt) acc])))\n(defn qq-foldr [xs]\n  (if (empty? xs)\n    (,)\n    (qq-loop (first xs) (qq-foldr (tuple (rest xs))))))\n(defn QUASIQUOTE [ast]\n  (if\n    (instance? list ast)            (tuple [(Sym \"vec\") (qq-foldr ast)])\n    (symbol? ast)                   (tuple [(Sym \"quote\") ast])\n    (instance? dict ast)            (tuple [(Sym \"quote\") ast])\n    (not (instance? tuple ast))     ast\n    (= (first ast) (Sym \"unquote\")) (get ast 1)\n    True                            (qq-foldr ast)))\n\n(defn EVAL [ast env]\n  (setv res None)\n  (while True\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n    (setv res\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (setv ast a2)\n                  (continue)) ;; TCO\n\n                (= (Sym \"quote\") a0)\n                a1\n\n                (= (Sym \"quasiquote\") a0)\n                (do (setv ast (QUASIQUOTE a1)) (continue)) ;; TCO\n\n                (= (Sym \"do\") a0)\n                (do (list (map (fn [x] (EVAL x env))\n                               (list (butlast (rest ast)))))\n                    (setv ast (last ast))\n                    (continue)) ;; TCO\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (do (setv ast (nth ast 3)) (continue)) ;; TCO\n                      None)\n                    (do (setv ast a2) (continue)))) ;; TCO\n\n                (= (Sym \"fn*\") a0)\n                (do\n                  (setv func (fn [&rest args]\n                               (EVAL a2 (env-new env a1 (or args []))))\n                        func.ast a2\n                        func.env env\n                        func.params a1)\n                  func)\n\n                ;; apply\n                (do\n                  (setv el (list (map (fn [x] (EVAL x env)) ast))\n                        f (first el)\n                        args (list (rest el)))\n                  (if (hasattr f \"ast\")\n                    (do (setv ast f.ast\n                              env (env-new f.env f.params args))\n                        (continue)) ;; TCO\n                    (apply f args)))))))\n    (break))\n  res)\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n(env-set repl-env (Sym \"eval\") (fn [ast] (EVAL ast repl-env)))\n(env-set repl-env (Sym \"*ARGV*\") (, ))\n\n;; core.mal: defined using the language itself\n(REP \"(def! not (fn* [a] (if a false true)))\")\n(REP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(defmain [&rest args]\n  (if (>= (len args) 2)\n    (do\n      (env-set repl-env (Sym \"*ARGV*\") (tuple (map Str (rest (rest args)))))\n      (REP (+ \"(load-file \\\"\" (get args 1) \"\\\")\")))\n    (do\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))))\n"
  },
  {
    "path": "impls/hy/step8_macros.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HyString :as Str HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn qq-loop [elt acc]\n  (if (and (instance? tuple elt)\n           (= (first elt) (Sym \"splice-unquote\")))\n    (tuple [(Sym \"concat\") (get elt 1) acc])\n    (tuple [(Sym \"cons\") (QUASIQUOTE elt) acc])))\n(defn qq-foldr [xs]\n  (if (empty? xs)\n    (,)\n    (qq-loop (first xs) (qq-foldr (tuple (rest xs))))))\n(defn QUASIQUOTE [ast]\n  (if\n    (instance? list ast)            (tuple [(Sym \"vec\") (qq-foldr ast)])\n    (symbol? ast)                   (tuple [(Sym \"quote\") ast])\n    (instance? dict ast)            (tuple [(Sym \"quote\") ast])\n    (not (instance? tuple ast))     ast\n    (= (first ast) (Sym \"unquote\")) (get ast 1)\n    True                            (qq-foldr ast)))\n\n(defn EVAL [ast env]\n  (setv res None)\n  (while True\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n    (setv res\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (setv ast a2)\n                  (continue)) ;; TCO\n\n                (= (Sym \"quote\") a0)\n                a1\n\n                (= (Sym \"quasiquote\") a0)\n                (do (setv ast (QUASIQUOTE a1)) (continue)) ;; TCO\n\n                (= (Sym \"defmacro!\") a0)\n                (do (setv func (EVAL a2 env)\n                          func.macro True)\n                    (env-set env a1 func))\n\n                (= (Sym \"do\") a0)\n                (do (list (map (fn [x] (EVAL x env))\n                               (list (butlast (rest ast)))))\n                    (setv ast (last ast))\n                    (continue)) ;; TCO\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (do (setv ast (nth ast 3)) (continue)) ;; TCO\n                      None)\n                    (do (setv ast a2) (continue)))) ;; TCO\n\n                (= (Sym \"fn*\") a0)\n                (do\n                  (setv func (fn [&rest args]\n                               (EVAL a2 (env-new env a1 (or args []))))\n                        func.ast a2\n                        func.env env\n                        func.params a1)\n                  func)\n\n                ;; apply\n                (do\n                  (setv f (EVAL a0 env))\n                  (if (and (hasattr f \"macro\") f.macro)\n                    (do (setv ast (apply f (list (rest ast))))\n                        (continue))) ;; TCO\n                  (setv args (list (map (fn [x] (EVAL x env))\n                                        (list (rest ast)))))\n                  (if (hasattr f \"ast\")\n                    (do (setv ast f.ast\n                              env (env-new f.env f.params args))\n                        (continue)) ;; TCO\n                    (apply f args)))))))\n    (break))\n  res)\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n(env-set repl-env (Sym \"eval\") (fn [ast] (EVAL ast repl-env)))\n(env-set repl-env (Sym \"*ARGV*\") (, ))\n\n;; core.mal: defined using the language itself\n(REP \"(def! not (fn* [a] (if a false true)))\")\n(REP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(REP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(defmain [&rest args]\n  (if (>= (len args) 2)\n    (do\n      (env-set repl-env (Sym \"*ARGV*\") (tuple (map Str (rest (rest args)))))\n      (REP (+ \"(load-file \\\"\" (get args 1) \"\\\")\")))\n    (do\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))))\n"
  },
  {
    "path": "impls/hy/step9_try.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HyString :as Str HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn qq-loop [elt acc]\n  (if (and (instance? tuple elt)\n           (= (first elt) (Sym \"splice-unquote\")))\n    (tuple [(Sym \"concat\") (get elt 1) acc])\n    (tuple [(Sym \"cons\") (QUASIQUOTE elt) acc])))\n(defn qq-foldr [xs]\n  (if (empty? xs)\n    (,)\n    (qq-loop (first xs) (qq-foldr (tuple (rest xs))))))\n(defn QUASIQUOTE [ast]\n  (if\n    (instance? list ast)            (tuple [(Sym \"vec\") (qq-foldr ast)])\n    (symbol? ast)                   (tuple [(Sym \"quote\") ast])\n    (instance? dict ast)            (tuple [(Sym \"quote\") ast])\n    (not (instance? tuple ast))     ast\n    (= (first ast) (Sym \"unquote\")) (get ast 1)\n    True                            (qq-foldr ast)))\n\n(defn EVAL [ast env]\n  (setv res None)\n  (while True\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n    (setv res\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (setv ast a2)\n                  (continue)) ;; TCO\n\n                (= (Sym \"quote\") a0)\n                a1\n\n                (= (Sym \"quasiquote\") a0)\n                (do (setv ast (QUASIQUOTE a1)) (continue)) ;; TCO\n\n                (= (Sym \"defmacro!\") a0)\n                (do (setv func (EVAL a2 env)\n                          func.macro True)\n                    (env-set env a1 func))\n\n                (= (Sym \"try*\") a0)\n                (if (and a2 (= (Sym \"catch*\") (nth a2 0)))\n                  (try\n                    (EVAL a1 env)\n                    (except [e Exception]\n                      (if (instance? MalException e)\n                        (setv exc e.val)\n                        (setv exc (Str (get e.args 0))))\n                      (do (setv ast (nth a2 2)\n                                env (env-new env [(nth a2 1)]\n                                                 [exc]))\n                          (continue)))) ;; TCO\n                  (do (setv ast a1) (continue))) ;; TCO\n\n                (= (Sym \"do\") a0)\n                (do (list (map (fn [x] (EVAL x env))\n                               (list (butlast (rest ast)))))\n                    (setv ast (last ast))\n                    (continue)) ;; TCO\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (do (setv ast (nth ast 3)) (continue)) ;; TCO\n                      None)\n                    (do (setv ast a2) (continue)))) ;; TCO\n\n                (= (Sym \"fn*\") a0)\n                (do\n                  (setv func (fn [&rest args]\n                               (EVAL a2 (env-new env a1 (or args []))))\n                        func.ast a2\n                        func.env env\n                        func.params a1)\n                  func)\n\n                ;; apply\n                (do\n                  (setv f (EVAL a0 env))\n                  (if (and (hasattr f \"macro\") f.macro)\n                    (do (setv ast (apply f (list (rest ast))))\n                        (continue))) ;; TCO\n                  (setv args (list (map (fn [x] (EVAL x env))\n                                        (list (rest ast)))))\n                  (if (hasattr f \"ast\")\n                    (do (setv ast f.ast\n                              env (env-new f.env f.params args))\n                        (continue)) ;; TCO\n                    (apply f args)))))))\n    (break))\n  res)\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n(env-set repl-env (Sym \"eval\") (fn [ast] (EVAL ast repl-env)))\n(env-set repl-env (Sym \"*ARGV*\") (, ))\n\n;; core.mal: defined using the language itself\n(REP \"(def! not (fn* [a] (if a false true)))\")\n(REP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(REP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(defmain [&rest args]\n  (if (>= (len args) 2)\n    (do\n      (env-set repl-env (Sym \"*ARGV*\") (tuple (map Str (rest (rest args)))))\n      (REP (+ \"(load-file \\\"\" (get args 1) \"\\\")\")))\n    (do\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))))\n"
  },
  {
    "path": "impls/hy/stepA_mal.hy",
    "content": "#!/usr/bin/env hy\n\n(import [hy.models [HyString :as Str HySymbol :as Sym]])\n(import sys traceback)\n(import [mal_types [MalException]])\n(import [reader [read-str Blank]])\n(import [printer [pr-str]])\n(import [env [env-new env-get env-set env-find]])\n(import core)\n\n;; read\n(defn READ [str]\n  (read-str str))\n\n;; eval\n(defn qq-loop [elt acc]\n  (if (and (instance? tuple elt)\n           (= (first elt) (Sym \"splice-unquote\")))\n    (tuple [(Sym \"concat\") (get elt 1) acc])\n    (tuple [(Sym \"cons\") (QUASIQUOTE elt) acc])))\n(defn qq-foldr [xs]\n  (if (empty? xs)\n    (,)\n    (qq-loop (first xs) (qq-foldr (tuple (rest xs))))))\n(defn QUASIQUOTE [ast]\n  (if\n    (instance? list ast)            (tuple [(Sym \"vec\") (qq-foldr ast)])\n    (symbol? ast)                   (tuple [(Sym \"quote\") ast])\n    (instance? dict ast)            (tuple [(Sym \"quote\") ast])\n    (not (instance? tuple ast))     ast\n    (= (first ast) (Sym \"unquote\")) (get ast 1)\n    True                            (qq-foldr ast)))\n\n(defn EVAL [ast env]\n  (setv res None)\n  (while True\n    (setv [dbgevalenv] [(env-find env (Sym \"DEBUG-EVAL\"))])\n    (if dbgevalenv\n      (do (setv [dbgevalsym] [(env-get dbgevalenv (Sym \"DEBUG-EVAL\"))])\n          (if (not (none? dbgevalsym))\n            (print \"EVAL:\" (pr-str ast True)))))\n    (setv res\n      (if\n        (symbol? ast)\n        (env-get env ast)\n\n        (instance? dict ast)\n        (dict (map (fn [k]\n                     [k (EVAL (get ast k) env)])\n                   ast))\n\n        (instance? list ast)\n        (list (map (fn [x] (EVAL x env)) ast))\n\n        (not (instance? tuple ast))\n        ast\n\n        (empty? ast)\n        ast\n\n        ;; apply list\n            (do\n              (setv [a0 a1 a2] [(nth ast 0) (nth ast 1) (nth ast 2)])\n              (if\n                (= (Sym \"def!\") a0)\n                (env-set env a1 (EVAL a2 env))\n\n                (= (Sym \"let*\") a0)\n                (do\n                  (setv env (env-new env))\n                  (for [[b e] (partition a1 2)]\n                    (env-set env b (EVAL e env)))\n                  (setv ast a2)\n                  (continue)) ;; TCO\n\n                (= (Sym \"quote\") a0)\n                a1\n\n                (= (Sym \"quasiquote\") a0)\n                (do (setv ast (QUASIQUOTE a1)) (continue)) ;; TCO\n\n                (= (Sym \"defmacro!\") a0)\n                (do (setv func (EVAL a2 env)\n                          func.macro True)\n                    (env-set env a1 func))\n\n                (= (Sym \"try*\") a0)\n                (if (and a2 (= (Sym \"catch*\") (nth a2 0)))\n                  (try\n                    (EVAL a1 env)\n                    (except [e Exception]\n                      (if (instance? MalException e)\n                        (setv exc e.val)\n                        (setv exc (Str (get e.args 0))))\n                      (do (setv ast (nth a2 2)\n                                env (env-new env [(nth a2 1)]\n                                                 [exc]))\n                          (continue)))) ;; TCO\n                  (do (setv ast a1) (continue))) ;; TCO\n\n                (= (Sym \"do\") a0)\n                (do (list (map (fn [x] (EVAL x env))\n                               (list (butlast (rest ast)))))\n                    (setv ast (last ast))\n                    (continue)) ;; TCO\n\n                (= (Sym \"if\") a0)\n                (do\n                  (setv cond (EVAL a1 env))\n                  (if (or (none? cond) (and (instance? bool cond)\n                                            (= cond False)))\n                    (if (> (len ast) 2)\n                      (do (setv ast (nth ast 3)) (continue)) ;; TCO\n                      None)\n                    (do (setv ast a2) (continue)))) ;; TCO\n\n                (= (Sym \"fn*\") a0)\n                (do\n                  (setv func (fn [&rest args]\n                               (EVAL a2 (env-new env a1 (or args []))))\n                        func.ast a2\n                        func.env env\n                        func.params a1)\n                  func)\n\n                ;; apply\n                (do\n                  (setv f (EVAL a0 env))\n                  (if (and (hasattr f \"macro\") f.macro)\n                    (do (setv ast (apply f (list (rest ast))))\n                        (continue))) ;; TCO\n                  (setv args (list (map (fn [x] (EVAL x env))\n                                        (list (rest ast)))))\n                  (if (hasattr f \"ast\")\n                    (do (setv ast f.ast\n                              env (env-new f.env f.params args))\n                        (continue)) ;; TCO\n                    (apply f args)))))))\n    (break))\n  res)\n\n;; print\n(defn PRINT [exp]\n  (pr-str exp True))\n\n;; repl\n(def repl-env (env-new))\n(defn REP [str]\n  (PRINT (EVAL (READ str) repl-env)))\n\n;; core.hy: defined using Hy\n(for [k core.ns]\n  (env-set repl-env (Sym k) (get core.ns k)))\n(env-set repl-env (Sym \"eval\") (fn [ast] (EVAL ast repl-env)))\n(env-set repl-env (Sym \"*ARGV*\") (, ))\n\n;; core.mal: defined using the language itself\n(REP \"(def! *host-language* \\\"Hy\\\")\")\n(REP \"(def! not (fn* [a] (if a false true)))\")\n(REP \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(REP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(defmain [&rest args]\n  (if (>= (len args) 2)\n    (do\n      (env-set repl-env (Sym \"*ARGV*\") (tuple (map Str (rest (rest args)))))\n      (REP (+ \"(load-file \\\"\" (get args 1) \"\\\")\")))\n    (do\n      (REP \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n      (while True\n        (try\n          (do (setv line (raw_input \"user> \"))\n              (if (= \"\" line) (continue))\n              (print (REP line)))\n          (except [EOFError] (break))\n          (except [Blank])\n          (except [e Exception]\n            (setv msg (.rstrip (.join \"\" (apply traceback.format_exception\n                                                (.exc_info sys)))))\n            (if (instance? MalException e)\n              (setv msg (+ (.rstrip msg) \": \" (pr-str e.val True))))\n            (print msg)))))))\n"
  },
  {
    "path": "impls/hy/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/io/Dockerfile",
    "content": "FROM ubuntu:24.04 AS base\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN apt-get -y install libpcre3-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Compile the io interpreter\n##########################################################\n\nFROM base AS builder\n\nRUN apt-get -y install git cmake gcc\n\nRUN cd /tmp \\\n    && git clone --recursive -q --depth=1 https://github.com/IoLanguage/io.git \\\n    && cd /tmp/io \\\n    && mkdir build && cd build \\\n    && cmake -DCMAKE_BUILD_TYPE=release .. && make && make install\n\n# Force eerie (Io package manager) to install itself and the packages in /opt/.eerie\nENV HOME=/opt\n\nRUN cd /tmp/io/eerie \\\n    && mkdir -p /opt \\\n    && . ./install_unix.sh --notouch \\\n    && eerie install https://github.com/IoLanguage/Range.git \\\n    && eerie install https://github.com/IoLanguage/ReadLine.git \\\n    && eerie install https://github.com/IoLanguage/Regex.git\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nFROM base AS io\n\nCOPY --from=builder /usr/local/lib/ /usr/lib/\nCOPY --from=builder /usr/local/bin/ /usr/bin/\nCOPY --from=builder /opt/.eerie/ /opt/.eerie/\n\nENV HOME=/mal\n"
  },
  {
    "path": "impls/io/Env.io",
    "content": "Env := Object clone do(\n    outer ::= nil\n    data ::= nil\n\n    with := method(aOuter, aBinds, aExprs,\n        self clone setOuter(aOuter) setData(Map clone) initBinds(aBinds, aExprs)\n    )\n\n    initBinds := method(aBinds, aExprs,\n        if(aBinds isNil not,\n            aBinds foreach(i, b,\n                if(b val == \"&\",\n                    set(aBinds at(i + 1), aExprs slice(i)) break,\n                    set(b, aExprs at(i))\n                )\n            )\n        )\n        self\n    )\n\n    set := method(key, val,\n        data atPut(key val, val)\n        val\n    )\n\n    find := method(key,\n        keyStr := key val\n        if(data hasKey(keyStr),\n            self,\n            if(outer isNil,\n                nil,\n                outer find(key)\n            )\n        )\n    )\n\n    get := method(key,\n        keyStr := key val\n        foundEnv := find(key)\n        if(foundEnv isNil,\n            Exception raise(\"'\" .. keyStr .. \"' not found\"),\n            (foundEnv data) at(keyStr)\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/Makefile",
    "content": "STEPS = step0_repl.io step1_read_print.io step2_eval.io step3_env.io step4_if_fn_do.io step5_tco.io \\\n        step6_file.io step7_quote.io step8_macros.io step9_try.io stepA_mal.io\n\nall: eerie\n\neerie:\n\tln -s /opt/.eerie eerie\n\n$(STEPS): eerie\n\nclean:\n"
  },
  {
    "path": "impls/io/MalCore.io",
    "content": "MalCore := Object clone do(\n    slurp := block(a,\n        f := File with(a at(0))\n        res := f contents\n        f close\n        res\n    )\n\n    dissoc := block(a,\n        res := MalMap withMap(a at(0))\n        a rest foreach(k, res removeKey(k))\n        res\n    )\n\n    vec := block(a,\n        coll := a at(0)\n        coll type switch(\n            \"MalVector\", coll,\n            \"MalList\",   MalVector with(coll),\n             Exception raise(\"vec: arg type\")))\n\n    nth := block(a,\n        if(a at(1) < a at(0) size,\n            a at(0) at(a at(1)),\n            Exception raise(\"nth: index out of range\")\n        )\n    )\n\n    conj := block(a,\n        coll := a at(0)\n        coll type switch(\n            \"MalList\",\n                MalList with(a rest reverse appendSeq(coll)),\n            \"MalVector\",\n                MalVector with(coll appendSeq(a rest))\n        )\n    )\n\n    seq := block(a,\n        obj := a at(0)\n        (obj isNil) ifTrue(return(nil))\n        (obj type == \"MalList\") ifTrue(return(if(obj isEmpty, nil, obj)))\n        (obj type == \"MalVector\") ifTrue(return(if(obj isEmpty, nil, MalList with(obj))))\n        (obj type == \"Sequence\") ifTrue(\n            if(obj isEmpty, return(nil))\n            lst := list()\n            obj foreach(i, c, lst append(obj inclusiveSlice(i, i)))\n            return(MalList with(lst))\n        )\n        nil\n    )\n\n    swapBang := block(a,\n        atom := a at(0)\n        newVal := a at(1) call(MalList with(list(atom val)) appendSeq(a slice(2)))\n        atom setVal(newVal) val\n    )\n\n    ioToMal := method(v,\n        (v isNil) ifTrue(return(v))\n        (v == true) ifTrue(return(v))\n        (v == false) ifTrue(return(v))\n        (v type == \"Number\") ifTrue(return(v))\n        (v type == \"Sequence\") ifTrue(return(v))\n        (v type == \"List\") ifTrue(return(MalList with(v map(e, ioToMal(e)))))\n        (v type == \"Map\") ifTrue(\n            lst := list()\n            v foreach(key, val,\n                lst push(key asString)\n                lst push(ioToMal(val))\n            )\n            return(MalMap withList(lst))\n        )\n        v asString\n    )\n\n    ioEval := block(a,\n        MalCore ioToMal(doString(a at(0)))\n    )\n\n    NS := Map with(\n        \"=\",     block(a, a at(0) == a at(1)),\n        \"throw\", block(a, MalException with(a at(0)) raise),\n\n        \"nil?\",     block(a, a at(0) isNil),\n        \"true?\",    block(a, a at(0) == true),\n        \"false?\",   block(a, a at(0) == false),\n        \"string?\",  block(a, a at(0) type == \"Sequence\"),\n        \"symbol\",   block(a, MalSymbol with(a at(0))),\n        \"symbol?\",  block(a, a at(0) type == \"MalSymbol\"),\n        \"keyword\",  block(a, MalKeyword with(a at(0))),\n        \"keyword?\", block(a, a at(0) type == \"MalKeyword\"),\n        \"number?\",  block(a, a at(0) type == \"Number\"),\n        \"fn?\",      block(a, (a at(0) type == \"Block\") or\n                             ((a at(0) type == \"MalFunc\") and (a at(0) isMacro not))),\n        \"macro?\",   block(a, (a at(0) type == \"MalFunc\") and (a at(0) isMacro)),\n\n        \"pr-str\",  block(a, a map(s, s malPrint(true)) join(\" \")),\n        \"str\",     block(a, a map(s, s malPrint(false)) join(\"\")),\n        \"prn\",     block(a, a map(s, s malPrint(true)) join(\" \") println ; nil),\n        \"println\", block(a, a map(s, s malPrint(false)) join(\" \") println ; nil),\n        \"read-string\", block(a, MalReader read_str(a at(0))),\n        \"readline\",    block(a, MalReadline readLine(a at(0))),\n        \"slurp\",   slurp,\n\n        \"<\",  block(a, a at(0) <  a at(1)),\n        \"<=\", block(a, a at(0) <= a at(1)),\n        \">\",  block(a, a at(0) >  a at(1)),\n        \">=\", block(a, a at(0) >= a at(1)),\n        \"+\",  block(a, a at(0) +  a at(1)),\n        \"-\",  block(a, a at(0) -  a at(1)),\n        \"*\",  block(a, a at(0) *  a at(1)),\n        \"/\",  block(a, a at(0) /  a at(1)),\n        \"time-ms\", block(a, (Date now asNumber * 1000.0) round),\n\n        \"list\",      block(a, a),\n        \"list?\",     block(a, a at(0) type == \"MalList\"),\n        \"vector\",    block(a, MalVector with(a)),\n        \"vector?\",   block(a, a at(0) type == \"MalVector\"),\n        \"hash-map\",  block(a, MalMap withList(a)),\n        \"map?\",      block(a, a at(0) type == \"MalMap\"),\n        \"assoc\",     block(a, MalMap withMap(a at(0) merge(MalMap withList(a rest)))),\n        \"dissoc\",    dissoc,\n        \"get\",       block(a, a at(0) ifNil(return nil) get(a at(1))),\n        \"contains?\", block(a, a at(0) ifNil(return nil) contains(a at(1))),\n        \"keys\",      block(a, a at(0) malKeys),\n        \"vals\",      block(a, a at(0) malVals),\n\n        \"sequential?\", block(a, if(a at(0) ?isSequential, true, false)),\n        \"cons\",   block(a, MalList with(list(a at(0)) appendSeq(a at(1)))),\n        \"concat\", block(a, MalList with(a reduce(appendSeq, list()))),\n        \"vec\",    vec,\n        \"nth\",    nth,\n        \"first\",  block(a, a at(0) ifNil(return nil) first),\n        \"rest\",   block(a, a at(0) ifNil(return MalList with(list())) rest),\n        \"empty?\", block(a, a at(0) ifNil(true) isEmpty),\n        \"count\",  block(a, a at(0) ifNil(return(0)) size),\n        \"apply\",  block(a, a at(0) call(MalList with(a slice(1, -1) appendSeq(a last)))),\n        \"map\",    block(a, MalList with(a at(1) map(e, a at(0) call(MalList with(list(e)))))),\n\n        \"conj\",   conj,\n        \"seq\",    seq,\n\n        \"meta\",      block(a, a at(0) ?meta),\n        \"with-meta\", block(a, a at(0) clone setMeta(a at(1))),\n        \"atom\",      block(a, MalAtom with(a at(0))),\n        \"atom?\",     block(a, a at(0) type == \"MalAtom\"),\n        \"deref\",     block(a, a at(0) val),\n        \"reset!\",    block(a, a at(0) setVal(a at(1)) ; a at(1)),\n        \"swap!\",     swapBang,\n\n        \"io-eval\",   ioEval\n    )\n)\n"
  },
  {
    "path": "impls/io/MalReader.io",
    "content": "MalReader := Object clone do (\n\n    Reader := Object clone do (\n        pos ::= 0\n        tokens ::= list()\n\n        with := method(theTokens,\n            self clone setTokens(theTokens)\n        )\n\n        peek := method(tokens at(pos))\n\n        next := method(\n            pos = pos + 1\n            tokens at(pos - 1)\n        )\n    )\n\n    tokenizerRegex := Regex with(\"[\\\\s ,]*(~@|[\\\\[\\\\]{}()'`~@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s \\\\[\\\\]{}()'\\\"`~@,;]*)\")\n\n    tokenize := method(str,\n        tokenizerRegex matchesIn(str) \\\n            map(m, m at(1) asMutable strip) \\\n            select(t, t size > 0) \\\n            select(t, t exSlice(0, 1) != \";\")\n    )\n\n    numberRegex := Regex with(\"^-?[0-9]+$\")\n    stringRegex := Regex with(\"^\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"$\")\n\n    read_string := method(token,\n        placeholder := 127 asCharacter\n        token exSlice(1, -1) replaceSeq(\"\\\\\\\\\", placeholder) replaceSeq(\"\\\\\\\"\", \"\\\"\") replaceSeq(\"\\\\n\", \"\\n\") replaceSeq(placeholder, \"\\\\\")\n    )\n\n    read_atom := method(rdr,\n        token := rdr next\n        (token hasMatchOfRegex(numberRegex)) ifTrue(return(token asNumber))\n        (token == \"true\") ifTrue(return(true))\n        (token == \"false\") ifTrue(return(false))\n        (token == \"nil\") ifTrue(return(nil))\n        (token beginsWithSeq(\":\")) ifTrue(return(MalKeyword with(token exSlice(1))))\n        (token hasMatchOfRegex(stringRegex)) ifTrue(return(read_string(token)))\n        (token beginsWithSeq(\"\\\"\")) ifTrue(Exception raise(\"expected '\\\"', got EOF\"))\n        MalSymbol with(token)\n    )\n\n    read_list := method(rdr, start, end,\n        token := rdr next\n        if(token != start, Exception raise(\"expected '\" .. start .. \"'\"))\n        ast := list()\n        token = rdr peek\n        while(token != end,\n            if(token isNil, Exception raise(\"expected '\" .. end .. \"', got EOF\"))\n            ast push(read_form(rdr))\n            token = rdr peek\n        )\n        rdr next\n        ast\n    )\n\n    reader_macro := method(symbol, rdr,\n        rdr next\n        MalList with(list(MalSymbol with(symbol), read_form(rdr)))\n    )\n\n    read_form := method(rdr,\n        token := rdr peek\n        (token == \"'\") ifTrue(return(reader_macro(\"quote\", rdr)))\n        (token == \"`\") ifTrue(return(reader_macro(\"quasiquote\", rdr)))\n        (token == \"~\") ifTrue(return(reader_macro(\"unquote\", rdr)))\n        (token == \"~@\") ifTrue(return(reader_macro(\"splice-unquote\", rdr)))\n        (token == \"^\") ifTrue(\n            rdr next\n            meta := read_form(rdr)\n            return(MalList with(list(MalSymbol with(\"with-meta\"), read_form(rdr), meta)))\n        )\n        (token == \"@\") ifTrue(return(reader_macro(\"deref\", rdr)))\n        (token == \"(\") ifTrue(return(MalList with(read_list(rdr, \"(\", \")\"))))\n        (token == \")\") ifTrue(Exception raise(\"unexepcted ')'\"))\n        (token == \"[\") ifTrue(return(MalVector with(read_list(rdr, \"[\", \"]\"))))\n        (token == \"]\") ifTrue(Exception raise(\"unexepcted ']'\"))\n        (token == \"{\") ifTrue(return(MalMap withList(read_list(rdr, \"{\", \"}\"))))\n        (token == \"}\") ifTrue(Exception raise(\"unexepcted '}'\"))\n        read_atom(rdr)\n    )\n\n    read_str := method(str,\n        tokens := tokenize(str)\n        if(tokens isEmpty, nil, read_form(Reader with(tokens)))\n    )\n)\n"
  },
  {
    "path": "impls/io/MalReadline.io",
    "content": "MalReadline := Object clone do (\n    historyLoaded := false\n    historyFile := (System getEnvironmentVariable(\"HOME\")) .. \"/.mal-history\"\n\n    loadHistory := method(\n        if(File exists(historyFile), ReadLine loadHistory(historyFile))\n        historyLoaded = true\n    )\n\n    readLine := method(prompt,\n        if(historyLoaded not, loadHistory)\n        line := ReadLine readLine(prompt)\n        if(line isNil, return(nil))\n        if(line isEmpty, return(line))\n        ReadLine addHistory(line)\n        ReadLine saveHistory(historyFile)\n        line\n    )\n)\n"
  },
  {
    "path": "impls/io/MalTypes.io",
    "content": "MalTypes := Object clone\n\nnil malPrint := method(readable, self asString)\ntrue malPrint := method(readable, self asString)\nfalse malPrint := method(readable, self asString)\nNumber malPrint := method(readable, self asString)\n\n// Io strings are of type Sequence\nSequence malPrint := method(readable,\n    if(readable,\n       \"\\\"\" .. (self asString asMutable replaceSeq(\"\\\\\", \"\\\\\\\\\") replaceSeq(\"\\\"\", \"\\\\\\\"\") replaceSeq(\"\\n\", \"\\\\n\")) .. \"\\\"\",\n       self asString)\n)\n\nMalMeta := Object clone do(\n    meta ::= nil\n)\n\nMalSymbol := Object clone appendProto(MalMeta) do (\n    val ::= nil\n    with := method(str, self clone setVal(if(str ?val, str val, str)))\n    malPrint := method(readable, val)\n    == := method(other, (self type == other type) and (val == other val))\n)\n\nMalKeyword := Object clone do (\n    val ::= nil\n    with := method(str, self clone setVal(if(str ?val, str val, str)))\n    malPrint := method(readable, \":\" .. val)\n    == := method(other, (self type == other type) and (val == other val))\n)\n\nMalSequential := Object clone do(\n    isSequential := method(true)\n    equalSequence := method(other,\n        if((other ?isSequential) not, return false)\n        if(self size != other size, return false)\n        unequalElement := self detect(i, valA,\n            (valA == (other at(i))) not\n        )\n        if(unequalElement, false, true)\n    )\n)\n\nMalList := List clone appendProto(MalSequential) appendProto(MalMeta) do (\n    with := method(lst, self clone copy(lst))\n    malPrint := method(readable,\n        \"(\" ..  (self map(e, e malPrint(readable)) join(\" \")) .. \")\"\n    )\n    rest := method(MalList with(resend))\n    slice := method(MalList with(resend))\n    == := method(other, equalSequence(other))\n)\n\nMalVector := List clone appendProto(MalSequential) appendProto(MalMeta) do (\n    with := method(lst, self clone copy(lst))\n    malPrint := method(readable,\n        \"[\" ..  (self map(e, e malPrint(readable)) join(\" \")) .. \"]\"\n    )\n    rest := method(MalList with(resend))\n    slice := method(MalList with(resend))\n    == := method(other, equalSequence(other))\n)\n\nMalMap := Map clone appendProto(MalMeta) do (\n    withList := method(lst,\n        obj := self clone\n        k := nil\n        lst foreach(i, e,\n            if(i % 2 == 0,\n                k := e,\n                obj atPut(objToKey(k), e)\n            )\n        )\n        obj\n    )\n    withMap := method(aMap, self clone merge(aMap))\n    objToKey := method(obj,\n        if(obj type == \"MalKeyword\", \"K_\" .. (obj val), \"S_\" .. obj)\n    )\n    keyToObj := method(s,\n        if(s beginsWithSeq(\"K_\"),\n            MalKeyword with(s exSlice(2)),\n            s exSlice(2)\n        )\n    )\n    malPrint := method(readable,\n        \"{\" ..\n            (self map(k, v,\n                (keyToObj(k) malPrint(readable)) .. \" \" .. (v malPrint(readable))\n            ) join(\" \")) .. \"}\"\n    )\n    contains := method(obj, hasKey(objToKey(obj)))\n    get := method(obj, at(objToKey(obj)))\n    malKeys := method(MalList with(keys map(k, keyToObj(k))))\n    malVals := method(MalList with(values))\n    removeKey := method(obj, removeAt(objToKey(obj)))\n    == := method(other,\n        if(self type != other type, return false)\n        if(keys size != other keys size, return false)\n        unequalElement := self detect(k, valA,\n            (valA == (other at(k))) not\n        )\n        if(unequalElement, false, true)\n    )\n)\n\nBlock malPrint := method(readable, \"#<NativeFunction>\")\nBlock appendProto(MalMeta)\n\nMalFunc := Object clone appendProto(MalMeta) do (\n    ast ::= nil\n    params ::= nil\n    env ::= nil\n    blk ::= nil\n    isMacro ::= false\n    with := method(aAst, aParams, aEnv, aBlk,\n        self clone setAst(aAst) setParams(aParams) setEnv(aEnv) setBlk(aBlk)\n    )\n    malPrint := method(readable, \"#<Function:params=\" ..  (params malPrint(true)) .. \">\")\n    call := method(args, blk call(args))\n)\n\nMalAtom := Object clone appendProto(MalMeta) do (\n    val ::= nil\n    with := method(str, self clone setVal(str))\n    malPrint := method(readable, \"(atom \" .. (val malPrint(true)) .. \")\")\n    == := method(other, (self type == other type) and (val == other val))\n)\n\nMalException := Exception clone do (\n    val ::= nil\n    with := method(str, self clone setVal(str))\n)\n"
  },
  {
    "path": "impls/io/run",
    "content": "#!/usr/bin/env bash\n\nio $(dirname $0)/${STEP:-stepA_mal}.io \"$@\"\n"
  },
  {
    "path": "impls/io/step0_repl.io",
    "content": "Regex\n\nREAD := method(str, str)\n\nEVAL := method(ast, env, ast)\n\nPRINT := method(exp, exp)\n\nRE := method(str, EVAL(READ(str), nil))\n\nREP := method(str, PRINT(RE(str)))\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    REP(line) println\n)\n"
  },
  {
    "path": "impls/io/step1_read_print.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\nEVAL := method(ast, env, ast)\n\nPRINT := method(exp, exp malPrint(true))\n\nRE := method(str, EVAL(READ(str), nil))\n\nREP := method(str, PRINT(RE(str)))\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        (\"Error: \" .. (e error)) println\n    )\n)\n"
  },
  {
    "path": "impls/io/step2_eval.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env at(ast val) ifNil(Exception raise(\"'\" .. (ast val) \"' not found\")),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\nEVAL := method(ast, env,\n\n    //  (\"EVAL: \" .. PRINT(ast)) println\n\n    if(ast type != \"MalList\", return(eval_ast(ast, env)))\n    if(ast isEmpty, return ast)\n    el := eval_ast(ast, env)\n    f := el at(0)\n    args := el rest\n    f callWithArgList(args)\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Map with(\n    \"+\", block(a, b, a + b),\n    \"-\", block(a, b, a - b),\n    \"*\", block(a, b, a * b),\n    \"/\", block(a, b, a / b)\n)\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        (\"Error: \" .. (e error)) println\n    )\n)\n"
  },
  {
    "path": "impls/io/step3_env.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n\n    debugEvalEnv := env find(debugEvalSymbol)\n    if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n        (\"EVAL: \" .. PRINT(ast)) println)\n\n    if(ast type != \"MalList\", return(eval_ast(ast, env)))\n    if(ast isEmpty, return ast)\n    if(ast at(0) type == \"MalSymbol\",\n        ast at(0) val switch(\n            \"def!\",\n                return(env set(ast at(1), EVAL(ast at(2), env))),\n            \"let*\",\n                letEnv := Env with(env)\n                varName := nil\n                ast at(1) foreach(i, e,\n                    if(i % 2 == 0,\n                        varName := e,\n                        letEnv set(varName, EVAL(e, letEnv))\n                    )\n                )\n                return(EVAL(ast at(2), letEnv))\n        )\n    )\n\n    // Apply\n    el := eval_ast(ast, env)\n    f := el at(0)\n    args := el rest\n    f callWithArgList(args)\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Env with(nil)\nrepl_env set(MalSymbol with(\"+\"), block(a, b, a + b))\nrepl_env set(MalSymbol with(\"-\"), block(a, b, a - b))\nrepl_env set(MalSymbol with(\"*\"), block(a, b, a * b))\nrepl_env set(MalSymbol with(\"/\"), block(a, b, a / b))\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        (\"Error: \" .. (e error)) println\n    )\n)\n"
  },
  {
    "path": "impls/io/step4_if_fn_do.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n\n    debugEvalEnv := env find(debugEvalSymbol)\n    if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n        (\"EVAL: \" .. PRINT(ast)) println)\n\n    if(ast type != \"MalList\", return(eval_ast(ast, env)))\n    if(ast isEmpty, return ast)\n    if(ast at(0) type == \"MalSymbol\",\n        ast at(0) val switch(\n            \"def!\",\n                return(env set(ast at(1), EVAL(ast at(2), env))),\n            \"do\",\n                return(eval_ast(ast rest, env) last),\n            \"if\",\n                return(EVAL(if(EVAL(ast at(1), env), ast at(2), ast at(3)), env)),\n            \"fn*\",\n                return(block(a, EVAL(ast at(2), Env with(env, ast at(1), a)))),\n            \"let*\",\n                letEnv := Env with(env)\n                varName := nil\n                ast at(1) foreach(i, e,\n                    if(i % 2 == 0,\n                        varName := e,\n                        letEnv set(varName, EVAL(e, letEnv))\n                    )\n                )\n                return(EVAL(ast at(2), letEnv))\n        )\n    )\n\n    // Apply\n    el := eval_ast(ast, env)\n    f := el at(0)\n    args := el rest\n    f call(args)\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nrepl_env := Env with(nil)\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\n\n// core.mal: defined using the language itself\nRE(\"(def! not (fn* (a) (if a false true)))\")\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/step5_tco.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n    loop(\n\n        debugEvalEnv := env find(debugEvalSymbol)\n        if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n            (\"EVAL: \" .. PRINT(ast)) println)\n\n        if(ast type != \"MalList\", return(eval_ast(ast, env)))\n        if(ast isEmpty, return ast)\n        if(ast at(0) type == \"MalSymbol\",\n            ast at(0) val switch(\n                \"def!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env))),\n                \"do\",\n                    eval_ast(ast slice(1,-1), env)\n                    ast = ast last\n                    continue, // TCO\n                \"if\",\n                    ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))\n                    continue, // TCO\n                \"fn*\",\n                    return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),\n                \"let*\",\n                    letEnv := Env with(env)\n                    varName := nil\n                    ast at(1) foreach(i, e,\n                        if(i % 2 == 0,\n                            varName := e,\n                            letEnv set(varName, EVAL(e, letEnv))\n                        )\n                    )\n                    ast = ast at(2)\n                    env = letEnv\n                    continue // TCO\n            )\n        )\n\n        // Apply\n        el := eval_ast(ast, env)\n        f := el at(0)\n        args := el rest\n        f type switch(\n            \"Block\",\n                return(f call(args)),\n            \"MalFunc\",\n                ast = f ast\n                env = Env with(f env, f params, args)\n                continue, // TCO\n            Exception raise(\"Unknown function type\")\n        )\n    )\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nrepl_env := Env with(nil)\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\n\n// core.mal: defined using the language itself\nRE(\"(def! not (fn* (a) (if a false true)))\")\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/step6_file.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n    loop(\n\n        debugEvalEnv := env find(debugEvalSymbol)\n        if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n            (\"EVAL: \" .. PRINT(ast)) println)\n\n        if(ast type != \"MalList\", return(eval_ast(ast, env)))\n        if(ast isEmpty, return ast)\n        if(ast at(0) type == \"MalSymbol\",\n            ast at(0) val switch(\n                \"def!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env))),\n                \"do\",\n                    eval_ast(ast slice(1,-1), env)\n                    ast = ast last\n                    continue, // TCO\n                \"if\",\n                    ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))\n                    continue, // TCO\n                \"fn*\",\n                    return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),\n                \"let*\",\n                    letEnv := Env with(env)\n                    varName := nil\n                    ast at(1) foreach(i, e,\n                        if(i % 2 == 0,\n                            varName := e,\n                            letEnv set(varName, EVAL(e, letEnv))\n                        )\n                    )\n                    ast = ast at(2)\n                    env = letEnv\n                    continue // TCO\n            )\n        )\n\n        // Apply\n        el := eval_ast(ast, env)\n        f := el at(0)\n        args := el rest\n        f type switch(\n            \"Block\",\n                return(f call(args)),\n            \"MalFunc\",\n                ast = f ast\n                env = Env with(f env, f params, args)\n                continue, // TCO\n            Exception raise(\"Unknown function type\")\n        )\n    )\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Env with(nil)\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\nrepl_env set(MalSymbol with(\"eval\"), block(a, EVAL(a at(0), repl_env)))\nrepl_env set(MalSymbol with(\"*ARGV*\"), MalList with(System args slice(2)))\n\n// core.mal: defined using the language itself\nRE(\"(def! not (fn* (a) (if a false true)))\")\nRE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif(System args size > 1,\n    REP(\"(load-file \\\"\" .. (System args at(1)) .. \"\\\")\")\n    System exit(0)\n)\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/step7_quote.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\nqq_foldr := method(xs,\n    xs reverseReduce(acc, elt,\n        if((elt type == \"MalList\") and (elt size == 2) and (elt at(0) == MalSymbol with(\"splice-unquote\")),\n            MalList with(list(MalSymbol with(\"concat\"), elt at(1), acc)),\n            MalList with(list(MalSymbol with(\"cons\"), quasiquote(elt), acc))),\n        MalList with(list())))\n\nquasiquote := method(ast,\n    ast type switch(\n        \"MalSymbol\", MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalMap\",    MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalVector\", MalList with(list(MalSymbol with(\"vec\"), qq_foldr(ast))),\n        \"MalList\",   if((ast size == 2) and (ast at(0) == MalSymbol with(\"unquote\")),\n                         ast at(1),\n                         qq_foldr(ast)),\n        ast))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n    loop(\n\n        debugEvalEnv := env find(debugEvalSymbol)\n        if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n            (\"EVAL: \" .. PRINT(ast)) println)\n\n        if(ast type != \"MalList\", return(eval_ast(ast, env)))\n        if(ast isEmpty, return ast)\n        if(ast at(0) type == \"MalSymbol\",\n            ast at(0) val switch(\n                \"def!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env))),\n                \"do\",\n                    eval_ast(ast slice(1,-1), env)\n                    ast = ast last\n                    continue, // TCO\n                \"if\",\n                    ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))\n                    continue, // TCO\n                \"fn*\",\n                    return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),\n                \"let*\",\n                    letEnv := Env with(env)\n                    varName := nil\n                    ast at(1) foreach(i, e,\n                        if(i % 2 == 0,\n                            varName := e,\n                            letEnv set(varName, EVAL(e, letEnv))\n                        )\n                    )\n                    ast = ast at(2)\n                    env = letEnv\n                    continue, // TCO\n                \"quote\",\n                    return(ast at(1)),\n                \"quasiquote\",\n                    ast = quasiquote(ast at(1))\n                    continue // TCO\n            )\n        )\n\n        // Apply\n        el := eval_ast(ast, env)\n        f := el at(0)\n        args := el rest\n        f type switch(\n            \"Block\",\n                return(f call(args)),\n            \"MalFunc\",\n                ast = f ast\n                env = Env with(f env, f params, args)\n                continue, // TCO\n            Exception raise(\"Unknown function type\")\n        )\n    )\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Env with(nil)\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\nrepl_env set(MalSymbol with(\"eval\"), block(a, EVAL(a at(0), repl_env)))\nrepl_env set(MalSymbol with(\"*ARGV*\"), MalList with(System args slice(2)))\n\n// core.mal: defined using the language itself\nRE(\"(def! not (fn* (a) (if a false true)))\")\nRE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif(System args size > 1,\n    REP(\"(load-file \\\"\" .. (System args at(1)) .. \"\\\")\")\n    System exit(0)\n)\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/step8_macros.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\nqq_foldr := method(xs,\n    xs reverseReduce(acc, elt,\n        if((elt type == \"MalList\") and (elt size == 2) and (elt at(0) == MalSymbol with(\"splice-unquote\")),\n            MalList with(list(MalSymbol with(\"concat\"), elt at(1), acc)),\n            MalList with(list(MalSymbol with(\"cons\"), quasiquote(elt), acc))),\n        MalList with(list())))\n\nquasiquote := method(ast,\n    ast type switch(\n        \"MalSymbol\", MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalMap\",    MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalVector\", MalList with(list(MalSymbol with(\"vec\"), qq_foldr(ast))),\n        \"MalList\",   if((ast size == 2) and (ast at(0) == MalSymbol with(\"unquote\")),\n                         ast at(1),\n                         qq_foldr(ast)),\n        ast))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n    loop(\n\n        debugEvalEnv := env find(debugEvalSymbol)\n        if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n            (\"EVAL: \" .. PRINT(ast)) println)\n\n        if(ast type != \"MalList\", return(eval_ast(ast, env)))\n        if(ast isEmpty, return ast)\n\n        if(ast at(0) type == \"MalSymbol\",\n            ast at(0) val switch(\n                \"def!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env))),\n                \"do\",\n                    eval_ast(ast slice(1,-1), env)\n                    ast = ast last\n                    continue, // TCO\n                \"if\",\n                    ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))\n                    continue, // TCO\n                \"fn*\",\n                    return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),\n                \"let*\",\n                    letEnv := Env with(env)\n                    varName := nil\n                    ast at(1) foreach(i, e,\n                        if(i % 2 == 0,\n                            varName := e,\n                            letEnv set(varName, EVAL(e, letEnv))\n                        )\n                    )\n                    ast = ast at(2)\n                    env = letEnv\n                    continue, // TCO\n                \"quote\",\n                    return(ast at(1)),\n                \"quasiquote\",\n                    ast = quasiquote(ast at(1))\n                    continue, // TCO\n                \"defmacro!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env) clone setIsMacro(true)))\n            )\n        )\n\n        // Apply\n        f := EVAL(ast at(0), env)\n        raw_args := ast rest\n        f type switch(\n            \"Block\",\n                args := eval_ast(raw_args, env)\n                return(f call(args)),\n            \"MalFunc\",\n                if(f isMacro,\n                    ast = f blk call(raw_args)\n                    continue) // TCO\n                args := eval_ast(raw_args, env)\n                ast = f ast\n                env = Env with(f env, f params, args)\n                continue, // TCO\n            Exception raise(\"Unknown function type\")\n        )\n    )\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Env with(nil)\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\nrepl_env set(MalSymbol with(\"eval\"), block(a, EVAL(a at(0), repl_env)))\nrepl_env set(MalSymbol with(\"*ARGV*\"), MalList with(System args slice(2)))\n\n// core.mal: defined using the language itself\nRE(\"(def! not (fn* (a) (if a false true)))\")\nRE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nRE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif(System args size > 1,\n    REP(\"(load-file \\\"\" .. (System args at(1)) .. \"\\\")\")\n    System exit(0)\n)\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/step9_try.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\nqq_foldr := method(xs,\n    xs reverseReduce(acc, elt,\n        if((elt type == \"MalList\") and (elt size == 2) and (elt at(0) == MalSymbol with(\"splice-unquote\")),\n            MalList with(list(MalSymbol with(\"concat\"), elt at(1), acc)),\n            MalList with(list(MalSymbol with(\"cons\"), quasiquote(elt), acc))),\n        MalList with(list())))\n\nquasiquote := method(ast,\n    ast type switch(\n        \"MalSymbol\", MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalMap\",    MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalVector\", MalList with(list(MalSymbol with(\"vec\"), qq_foldr(ast))),\n        \"MalList\",   if((ast size == 2) and (ast at(0) == MalSymbol with(\"unquote\")),\n                         ast at(1),\n                         qq_foldr(ast)),\n        ast))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n    loop(\n\n        debugEvalEnv := env find(debugEvalSymbol)\n        if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n            (\"EVAL: \" .. PRINT(ast)) println)\n\n        if(ast type != \"MalList\", return(eval_ast(ast, env)))\n        if(ast isEmpty, return ast)\n\n        if(ast at(0) type == \"MalSymbol\",\n            ast at(0) val switch(\n                \"def!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env))),\n                \"do\",\n                    eval_ast(ast slice(1,-1), env)\n                    ast = ast last\n                    continue, // TCO\n                \"if\",\n                    ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))\n                    continue, // TCO\n                \"fn*\",\n                    return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),\n                \"let*\",\n                    letEnv := Env with(env)\n                    varName := nil\n                    ast at(1) foreach(i, e,\n                        if(i % 2 == 0,\n                            varName := e,\n                            letEnv set(varName, EVAL(e, letEnv))\n                        )\n                    )\n                    ast = ast at(2)\n                    env = letEnv\n                    continue, // TCO\n                \"quote\",\n                    return(ast at(1)),\n                \"quasiquote\",\n                    ast = quasiquote(ast at(1))\n                    continue, // TCO\n                \"defmacro!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env) clone setIsMacro(true))),\n                \"try*\",\n                    if(ast at(2) == nil, return(EVAL(ast at(1), env)))\n                    e := try(result := EVAL(ast at(1), env))\n                    e catch(Exception,\n                        exc := if(e type == \"MalException\", e val, e error)\n                        catchAst := ast at(2)\n                        catchEnv := Env with(env)\n                        catchEnv set(catchAst at(1), exc)\n                        result := EVAL(catchAst at(2), catchEnv)\n                    )\n                    return(result)\n            )\n        )\n\n        // Apply\n        f := EVAL(ast at(0), env)\n        raw_args := ast rest\n        f type switch(\n            \"Block\",\n                args := eval_ast(raw_args, env)\n                return(f call(args)),\n            \"MalFunc\",\n                if(f isMacro,\n                    ast = f blk call(raw_args)\n                    continue) // TCO\n                args := eval_ast(raw_args, env)\n                ast = f ast\n                env = Env with(f env, f params, args)\n                continue, // TCO\n            Exception raise(\"Unknown function type\")\n        )\n    )\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Env with(nil)\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\nrepl_env set(MalSymbol with(\"eval\"), block(a, EVAL(a at(0), repl_env)))\nrepl_env set(MalSymbol with(\"*ARGV*\"), MalList with(System args slice(2)))\n\n// core.mal: defined using the language itself\nRE(\"(def! not (fn* (a) (if a false true)))\")\nRE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nRE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif(System args size > 1,\n    REP(\"(load-file \\\"\" .. (System args at(1)) .. \"\\\")\")\n    System exit(0)\n)\n\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/stepA_mal.io",
    "content": "MalTypes\nMalReader\n\nREAD := method(str, MalReader read_str(str))\n\nqq_foldr := method(xs,\n    xs reverseReduce(acc, elt,\n        if((elt type == \"MalList\") and (elt size == 2) and (elt at(0) == MalSymbol with(\"splice-unquote\")),\n            MalList with(list(MalSymbol with(\"concat\"), elt at(1), acc)),\n            MalList with(list(MalSymbol with(\"cons\"), quasiquote(elt), acc))),\n        MalList with(list())))\n\nquasiquote := method(ast,\n    ast type switch(\n        \"MalSymbol\", MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalMap\",    MalList with(list(MalSymbol with(\"quote\"), ast)),\n        \"MalVector\", MalList with(list(MalSymbol with(\"vec\"), qq_foldr(ast))),\n        \"MalList\",   if((ast size == 2) and (ast at(0) == MalSymbol with(\"unquote\")),\n                         ast at(1),\n                         qq_foldr(ast)),\n        ast))\n\neval_ast := method(ast, env,\n   (ast type) switch(\n       \"MalSymbol\", env get(ast),\n       \"MalList\", MalList with(ast map(a, EVAL(a, env))),\n       \"MalVector\", MalVector with(ast map(a, EVAL(a, env))),\n       \"MalMap\",\n           m := MalMap clone\n           ast foreach(k, v,\n               m atPut(k, EVAL(v, env))\n           )\n           m,\n       ast\n   )\n)\n\ndebugEvalSymbol := MalSymbol with(\"DEBUG-EVAL\")\n\nEVAL := method(ast, env,\n    loop(\n\n        debugEvalEnv := env find(debugEvalSymbol)\n        if((debugEvalEnv isNil not) and (debugEvalEnv get(debugEvalSymbol)),\n            (\"EVAL: \" .. PRINT(ast)) println)\n\n        if(ast type != \"MalList\", return(eval_ast(ast, env)))\n        if(ast isEmpty, return ast)\n\n        if(ast at(0) type == \"MalSymbol\",\n            ast at(0) val switch(\n                \"def!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env))),\n                \"do\",\n                    eval_ast(ast slice(1,-1), env)\n                    ast = ast last\n                    continue, // TCO\n                \"if\",\n                    ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))\n                    continue, // TCO\n                \"fn*\",\n                    return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),\n                \"let*\",\n                    letEnv := Env with(env)\n                    varName := nil\n                    ast at(1) foreach(i, e,\n                        if(i % 2 == 0,\n                            varName := e,\n                            letEnv set(varName, EVAL(e, letEnv))\n                        )\n                    )\n                    ast = ast at(2)\n                    env = letEnv\n                    continue, // TCO\n                \"quote\",\n                    return(ast at(1)),\n                \"quasiquote\",\n                    ast = quasiquote(ast at(1))\n                    continue, // TCO\n                \"defmacro!\",\n                    return(env set(ast at(1), EVAL(ast at(2), env) clone setIsMacro(true))),\n                \"try*\",\n                    if(ast at(2) == nil, return(EVAL(ast at(1), env)))\n                    e := try(result := EVAL(ast at(1), env))\n                    e catch(Exception,\n                        exc := if(e type == \"MalException\", e val, e error)\n                        catchAst := ast at(2)\n                        catchEnv := Env with(env)\n                        catchEnv set(catchAst at(1), exc)\n                        result := EVAL(catchAst at(2), catchEnv)\n                    )\n                    return(result)\n            )\n        )\n\n        // Apply\n        f := EVAL(ast at(0), env)\n        raw_args := ast rest\n        f type switch(\n            \"Block\",\n                args := eval_ast(raw_args, env)\n                return(f call(args)),\n            \"MalFunc\",\n                if(f isMacro,\n                    ast = f blk call(raw_args)\n                    continue) // TCO\n                args := eval_ast(raw_args, env)\n                ast = f ast\n                env = Env with(f env, f params, args)\n                continue, // TCO\n            Exception raise(\"Unknown function type\")\n        )\n    )\n)\n\nPRINT := method(exp, exp malPrint(true))\n\nrepl_env := Env with(nil)\n\nRE := method(str, EVAL(READ(str), repl_env))\n\nREP := method(str, PRINT(RE(str)))\n\nMalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))\nrepl_env set(MalSymbol with(\"eval\"), block(a, EVAL(a at(0), repl_env)))\nrepl_env set(MalSymbol with(\"*ARGV*\"), MalList with(System args slice(2)))\n\n// core.mal: defined using the language itself\nRE(\"(def! *host-language* \\\"io\\\")\")\nRE(\"(def! not (fn* (a) (if a false true)))\")\nRE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nRE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif(System args size > 1,\n    REP(\"(load-file \\\"\" .. (System args at(1)) .. \"\\\")\")\n    System exit(0)\n)\n\nRE(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nloop(\n    line := MalReadline readLine(\"user> \")\n    if(line isNil, break)\n    if(line isEmpty, continue)\n    e := try(REP(line) println)\n    e catch(Exception,\n        if(e type == \"MalException\",\n            (\"Error: \" .. ((e val) malPrint(true))) println,\n            (\"Error: \" .. (e error)) println\n        )\n    )\n)\n"
  },
  {
    "path": "impls/io/tests/step5_tco.mal",
    "content": ";; Io: skipping non-TCO recursion\n;; Reason: never completes, never segfaults\n"
  },
  {
    "path": "impls/io/tests/stepA_mal.mal",
    "content": ";; Testing basic Io interop\n\n(io-eval \"7\")\n;=>7\n\n(io-eval \"\\\"7\\\"\")\n;=>\"7\"\n\n(io-eval \"123 == 123\")\n;=>true\n\n(io-eval \"123 == 456\")\n;=>false\n\n(io-eval \"list(7, 8, 9)\")\n;=>(7 8 9)\n\n(io-eval \"Map with(\\\"abc\\\", 789)\")\n;=>{\"abc\" 789}\n\n(io-eval \"\\\"hello\\\" println\")\n;/hello\n;=>\"hello\"\n\n(io-eval \"Lobby foo := 8\")\n(io-eval \"Lobby foo\")\n;=>8\n\n(io-eval \"list(\\\"a\\\", \\\"b\\\", \\\"c\\\") map(x, \\\"X\\\" .. x .. \\\"Y\\\") join(\\\" \\\")\")\n;=>\"XaY XbY XcY\"\n\n(io-eval \"list(1, 2, 3) map(x, 1 + x)\")\n;=>(2 3 4)\n"
  },
  {
    "path": "impls/janet/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install \\\n    ca-certificates wget\n\nRUN wget -O- \\\n    https://github.com/janet-lang/janet/releases/download/v1.36.0/janet-v1.36.0-linux-x64.tar.gz \\\n    | tar -xzC/opt\nRUN ln -sf /opt/janet-v1.36.0-linux/bin/janet /usr/local/bin/janet\n"
  },
  {
    "path": "impls/janet/Makefile",
    "content": "all:\n\ttrue\n"
  },
  {
    "path": "impls/janet/core.janet",
    "content": "(import ./types :as t)\n(import ./utils :as u)\n(import ./printer)\n(import ./reader)\n\n(defn deref*\n  [ast]\n  (if (not (t/atom?* ast))\n    (u/throw* (t/make-string (string \"Expected atom, got: \" (t/get-type ast))))\n    (t/get-value ast)))\n\n(defn reset!*\n  [atom-ast val-ast]\n  (t/set-atom-value! atom-ast val-ast)\n  val-ast)\n\n(defn cons*\n  [head-ast tail-ast]\n  [head-ast ;(t/get-value tail-ast)])\n\n(defn concat*\n  [& list-asts]\n  (reduce (fn [acc list-ast]\n            [;acc ;(t/get-value list-ast)])\n          []\n          list-asts))\n\n(defn nth*\n  [coll-ast num-ast]\n  (let [elts (t/get-value coll-ast)\n        n-elts (length elts)\n        i (t/get-value num-ast)]\n    (if (< i n-elts)\n      (in elts i)\n      (u/throw* (t/make-string (string \"Index out of range: \" i))))))\n\n(defn first*\n  [coll-or-nil-ast]\n  (if (or (t/nil?* coll-or-nil-ast)\n          (t/empty?* coll-or-nil-ast))\n    t/mal-nil\n    (in (t/get-value coll-or-nil-ast) 0)))\n\n(defn rest*\n  [coll-or-nil-ast]\n  (if (or (t/nil?* coll-or-nil-ast)\n          (t/empty?* coll-or-nil-ast))\n    (t/make-list [])\n    (t/make-list (slice (t/get-value coll-or-nil-ast) 1))))\n\n(defn janet-eval*\n  [janet-val]\n  (case (type janet-val)\n    :nil\n    t/mal-nil\n    ##\n    :boolean\n    (t/make-boolean janet-val)\n    ##\n    :number # XXX: there may be some incompatibilities\n    (t/make-number janet-val)\n    ##\n    :string\n    (t/make-string janet-val)\n    ##\n    :keyword # XXX: there may be some incompatibilities\n    (t/make-keyword (string \":\" janet-val))\n    ##\n    :symbol # XXX: there may be some incompatibilities\n    (t/make-symbol (string janet-val))\n    ##\n    :tuple\n    (t/make-list (map janet-eval* janet-val))\n    ##\n    :array\n    (t/make-list (map janet-eval* janet-val))\n    ##\n    :struct\n    (t/make-hash-map (struct ;(map janet-eval* (kvs janet-val))))\n    ##\n    :table\n    (t/make-hash-map (struct ;(map janet-eval* (kvs janet-val))))\n    ##\n    (u/throw* (t/make-string (string \"Unsupported type: \" (type janet-val))))))\n\n(defn arith-fn\n  [op]\n  (t/make-function\n    (fn [asts]\n      (t/make-number\n        (op ;(map |(t/get-value $)\n                  asts))))))\n\n(defn cmp-fn\n  [op]\n  (t/make-function\n    (fn [asts]\n      (if (op ;(map |(t/get-value $) asts))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-symbol\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"symbol requires 1 argument\")))\n      (t/make-symbol (t/get-value (in asts 0))))))\n\n(def mal-keyword\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"keyword requires 1 argument\")))\n      (let [arg-ast (in asts 0)]\n        (cond\n          (t/keyword?* arg-ast)\n          arg-ast\n          ##\n          (t/string?* arg-ast)\n          (t/make-keyword (string \":\" (t/get-value arg-ast)))\n          ##\n          (u/throw* (t/make-string \"Expected string\")))))))\n\n(def mal-list\n  (t/make-function\n    (fn [asts]\n      (t/make-list asts))))\n\n(def mal-vector\n  (t/make-function\n    (fn [asts]\n      (t/make-vector asts))))\n\n(def mal-vec\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"vec requires 1 argument\")))\n      (let [ast (in asts 0)]\n        (cond\n          (t/vector?* ast)\n          ast\n          ##\n          (t/list?* ast)\n          (t/make-vector (t/get-value ast))\n          ##\n          (t/nil?* ast)\n          (t/make-vector ())\n          ##\n          (u/throw* (t/make-string \"vec requires a vector, list, or nil\")))))))\n\n(def mal-hash-map\n  (t/make-function\n    (fn [asts]\n      (when (= 1 (% (length asts) 2))\n        (u/throw* (t/make-string\n                    \"hash-map requires an even number of arguments\")))\n      (t/make-hash-map asts))))\n\n(def mal-atom\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"atom requires 1 argument\")))\n      (t/make-atom (in asts 0)))))\n\n(def mal-nil?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"nil? requires 1 argument\")))\n      (if (t/nil?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-true?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"true? requires 1 argument\")))\n      (if (t/true?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-false?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"false? requires 1 argument\")))\n      (if (t/false?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-number?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"number? requires 1 argument\")))\n      (if (t/number?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-symbol?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"symbol? requires 1 argument\")))\n      (if (t/symbol?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-keyword?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"keyword? requires 1 argument\")))\n      (if (t/keyword?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-string?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"string? requires 1 argument\")))\n      (if (t/string?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-list?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"list? requires 1 argument\")))\n      (if (t/list?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-vector?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"vector? requires 1 argument\")))\n      (if (t/vector?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-map?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"map? requires 1 argument\")))\n      (if (t/hash-map?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-fn?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"fn? requires 1 argument\")))\n      (let [target-ast (in asts 0)]\n        (if (and (t/fn?* target-ast)\n                 (not (t/get-is-macro target-ast)))\n          t/mal-true\n          t/mal-false)))))\n\n(def mal-macro?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"macro? requires 1 argument\")))\n      (let [the-ast (in asts 0)]\n        (if (t/macro?* the-ast)\n          t/mal-true\n          t/mal-false)))))\n\n(def mal-atom?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"atom? requires 1 argument\")))\n      (if (t/atom?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-sequential?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"sequential? requires 1 argument\")))\n      (if (or (t/list?* (in asts 0))\n              (t/vector?* (in asts 0)))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-=\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"= requires 2 arguments\")))\n      (let [ast-1 (in asts 0)\n            ast-2 (in asts 1)]\n        (if (t/equals?* ast-1 ast-2)\n          t/mal-true\n          t/mal-false)))))\n\n(def mal-empty?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"empty? requires 1 argument\")))\n      (if (t/empty?* (in asts 0))\n        t/mal-true\n        t/mal-false))))\n\n(def mal-contains?\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"contains? requires 2 arguments\")))\n      (let [head-ast (in asts 0)]\n        (when (not (or (t/hash-map?* head-ast)\n                       (t/nil?* head-ast)))\n          (u/throw* (t/make-string\n                      \"contains? first argument should be a hash-map or nil\")))\n        (if (t/nil?* head-ast)\n          t/mal-nil\n          (let [item-struct (t/get-value head-ast)\n                key-ast (in asts 1)]\n            (if-let [val-ast (get item-struct key-ast)]\n              t/mal-true\n              t/mal-false)))))))\n\n(def mal-deref\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"deref requires 1 argument\")))\n      (let [ast (in asts 0)]\n        (deref* ast)))))\n\n(def mal-reset!\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"reset! requires 2 arguments\")))\n      (let [atom-ast (in asts 0)\n            val-ast (in asts 1)]\n        (reset!* atom-ast val-ast)))))\n\n(def mal-swap!\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"swap! requires at least 2 arguments\")))\n      (let [atom-ast (in asts 0)\n            fn-ast (in asts 1)\n            args-asts (slice asts 2)\n            inner-ast (deref* atom-ast)]\n        (reset!* atom-ast\n                 ((t/get-value fn-ast) [inner-ast ;args-asts]))))))\n\n(def mal-pr-str\n  (t/make-function\n    (fn [asts]\n      (def buf @\"\")\n      (when (> (length asts) 0)\n        (each ast asts\n              (buffer/push-string buf (printer/pr_str ast true))\n              (buffer/push-string buf \" \"))\n        # remove extra space at end\n        (buffer/popn buf 1))\n      (t/make-string (string buf)))))\n\n(def mal-str\n  (t/make-function\n    (fn [asts]\n      (def buf @\"\")\n      (when (> (length asts) 0)\n        (each ast asts\n              (buffer/push-string buf (printer/pr_str ast false))))\n      (t/make-string (string buf)))))\n\n(def mal-prn\n  (t/make-function\n    (fn [asts]\n      (def buf @\"\")\n      (when (> (length asts) 0)\n        (each ast asts\n              (buffer/push-string buf (printer/pr_str ast true))\n              (buffer/push-string buf \" \"))\n        # remove extra space at end\n        (buffer/popn buf 1))\n      (print (string buf))\n      t/mal-nil)))\n\n(def mal-println\n  (t/make-function\n    (fn [asts]\n      (def buf @\"\")\n      (when (> (length asts) 0)\n        (each ast asts\n              (buffer/push-string buf (printer/pr_str ast false))\n              (buffer/push-string buf \" \"))\n        # remove extra space at end\n        (buffer/popn buf 1))\n      (print (string buf))\n      t/mal-nil)))\n\n(def mal-read-string\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"read-string requires 1 argument\")))\n      (if-let [res (reader/read_str (t/get-value (in asts 0)))]\n        res\n        (u/throw* (t/make-string \"No code content\"))))))\n\n(def mal-slurp\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"slurp requires 1 argument\")))\n      (let [a-str (t/get-value (in asts 0))]\n        (if (not (os/stat a-str))\n          (u/throw* (string \"File not found: \" a-str))\n          # XXX: escaping?\n          (t/make-string (slurp a-str)))))))\n\n(def mal-count\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"count requires 1 argument\")))\n      (let [ast (in asts 0)]\n        (if (t/nil?* ast)\n          (t/make-number 0)\n          (t/make-number (length (t/get-value ast))))))))\n\n(def mal-cons\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"cons requires 2 arguments\")))\n      (let [head-ast (in asts 0)\n            tail-ast (in asts 1)]\n        (t/make-list (cons* head-ast tail-ast))))))\n\n(def mal-concat\n  (t/make-function\n    (fn [asts]\n      (t/make-list (concat* ;asts)))))\n\n(def mal-nth\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"nth requires 2 arguments\")))\n      (let [coll-ast (in asts 0)\n            num-ast (in asts 1)]\n        (nth* coll-ast num-ast)))))\n\n(def mal-first\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"first requires 1 argument\")))\n      (let [coll-or-nil-ast (in asts 0)]\n        (first* coll-or-nil-ast)))))\n\n(def mal-rest\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"rest requires 1 argument\")))\n      (let [coll-or-nil-ast (in asts 0)]\n        (rest* coll-or-nil-ast)))))\n\n(def mal-assoc\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 3)\n        (u/throw* (t/make-string \"assoc requires at least 3 arguments\")))\n      (let [head-ast (in asts 0)]\n        (when (not (or (t/hash-map?* head-ast)\n                       (t/nil?* head-ast)))\n          (u/throw* (t/make-string\n                      \"assoc first argument should be a hash-map or nil\")))\n        (if (t/nil?* head-ast)\n          t/mal-nil\n          (let [item-table (table ;(kvs (t/get-value head-ast)))\n                kv-asts (slice asts 1 -1)]\n            (each [key-ast val-ast] (partition 2 kv-asts)\n                  (put item-table key-ast val-ast))\n            (t/make-hash-map (table/to-struct item-table))))))))\n\n(def mal-dissoc\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"dissoc requires at least 2 arguments\")))\n      (let [head-ast (in asts 0)]\n        (when (not (or (t/hash-map?* head-ast)\n                       (t/nil?* head-ast)))\n          (u/throw* (t/make-string\n                      \"dissoc first argument should be a hash-map or nil\")))\n        (if (t/nil?* head-ast)\n          t/mal-nil\n          (let [item-table (table ;(kvs (t/get-value head-ast)))\n                key-asts (slice asts 1 -1)]\n            (each key-ast key-asts\n                  (put item-table key-ast nil))\n            (t/make-hash-map (table/to-struct item-table))))))))\n\n(def mal-get\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"get requires 2 arguments\")))\n      (let [head-ast (in asts 0)]\n        (when (not (or (t/hash-map?* head-ast)\n                       (t/nil?* head-ast)))\n          (u/throw* (t/make-string\n                      \"get first argument should be a hash-map or nil\")))\n        (if (t/nil?* head-ast)\n          t/mal-nil\n          (let [item-struct (t/get-value head-ast)\n                key-ast (in asts 1)]\n            (if-let [val-ast (get item-struct key-ast)]\n              val-ast\n              t/mal-nil)))))))\n\n(def mal-keys\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"keys requires 1 argument\")))\n      (let [head-ast (in asts 0)]\n        (when (not (or (t/hash-map?* head-ast)\n                       (t/nil?* head-ast)))\n          (u/throw* (t/make-string\n                      \"keys first argument should be a hash-map or nil\")))\n        (if (t/nil?* head-ast)\n          t/mal-nil\n          (let [item-struct (t/get-value head-ast)]\n            (t/make-list (keys item-struct))))))))\n\n(def mal-vals\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"vals requires 1 argument\")))\n      (let [head-ast (in asts 0)]\n        (when (not (or (t/hash-map?* head-ast)\n                       (t/nil?* head-ast)))\n          (u/throw* (t/make-string\n                      \"vals first argument should be a hash-map or nil\")))\n        (if (t/nil?* head-ast)\n          t/mal-nil\n          (let [item-struct (t/get-value head-ast)]\n            (t/make-list (values item-struct))))))))\n\n(def mal-conj\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"conj requires at least 2 arguments\")))\n      (let [coll-ast (in asts 0)\n            item-asts (slice asts 1)]\n        (cond\n          (t/nil?* coll-ast)\n          (t/make-list [;(reverse item-asts)])\n          ##\n          (t/list?* coll-ast)\n          (t/make-list [;(reverse item-asts) ;(t/get-value coll-ast)])\n          ##\n          (t/vector?* coll-ast)\n          (t/make-vector [;(t/get-value coll-ast) ;item-asts])\n          ##\n          (u/throw* (t/make-string \"Expected list or vector\")))))))\n\n(def mal-seq\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"seq requires 1 argument\")))\n      (let [arg-ast (in asts 0)]\n        (cond\n          (t/list?* arg-ast)\n          (if (t/empty?* arg-ast)\n            t/mal-nil\n            arg-ast)\n          ##\n          (t/vector?* arg-ast)\n          (if (t/empty?* arg-ast)\n            t/mal-nil\n            (t/make-list (t/get-value arg-ast)))\n          ##\n          (t/string?* arg-ast)\n          (if (t/empty?* arg-ast)\n            t/mal-nil\n            (let [str-asts (map |(t/make-string (string/from-bytes $))\n                                (t/get-value arg-ast))]\n              (t/make-list str-asts)))\n          ##\n          (t/nil?* arg-ast)\n          arg-ast\n          ##\n          (u/throw* (t/make-string \"Expected list, vector, string, or nil\")))))))\n\n(def mal-map\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"map requires at least 2 arguments\")))\n      (let [the-fn (t/get-value (in asts 0))\n            coll (t/get-value (in asts 1))]\n        (t/make-list (map |(the-fn [$])\n                        coll))))))\n\n# (apply F A B [C D]) is equivalent to (F A B C D)\n(def mal-apply\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"apply requires at least 1 argument\")))\n      (let [the-fn (t/get-value (in asts 0))] # e.g. F\n        (if (= (length asts) 1)\n          (the-fn [])\n          (let [last-asts (t/get-value (get (slice asts -2) 0)) # e.g. [C D]\n                args-asts (slice asts 1 -2)] # e.g. [A B]\n            (the-fn [;args-asts ;last-asts])))))))\n\n(def mal-meta\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"meta requires 1 argument\")))\n      (let [head-ast (in asts 0)]\n        (if (or (t/list?* head-ast)\n                (t/vector?* head-ast)\n                (t/hash-map?* head-ast)\n                (t/fn?* head-ast))\n          (t/get-meta (in asts 0))\n          t/mal-nil)))))\n\n(def mal-with-meta\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 2)\n        (u/throw* (t/make-string \"with-meta requires 2 arguments\")))\n      (let [target-ast (in asts 0)\n            meta-ast (in asts 1)]\n        (cond\n          (t/list?* target-ast)\n          (t/make-list (t/get-value target-ast) meta-ast)\n          ##\n          (t/vector?* target-ast)\n          (t/make-vector (t/get-value target-ast) meta-ast)\n          ##\n          (t/hash-map?* target-ast)\n          (t/make-hash-map (t/get-value target-ast) meta-ast)\n          ##\n          (t/fn?* target-ast)\n          (t/clone-with-meta target-ast meta-ast)\n          ##\n          (u/throw* (t/make-string \"Expected list, vector, hash-map, or fn\")))))))\n\n(def mal-throw\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"throw requires 1 argument\")))\n      (u/throw* (in asts 0)))))\n\n(def mal-readline\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"readline requires 1 argument\")))\n      (let [prompt (t/get-value (in asts 0))\n            buf @\"\"]\n        (file/write stdout prompt)\n        (file/flush stdout)\n        (file/read stdin :line buf)\n        (if (< 0 (length buf))\n          (t/make-string (string/trimr buf))\n          t/mal-nil)))))\n\n(def mal-time-ms\n  (t/make-function\n    (fn [asts]\n      (t/make-number\n        (math/floor (* 1000 (os/clock)))))))\n\n(def mal-janet-eval\n  (t/make-function\n    (fn [asts]\n      (when (< (length asts) 1)\n        (u/throw* (t/make-string \"janet-eval requires 1 argument\")))\n      (let [head-ast (in asts 0)]\n        (when (not (t/string?* head-ast))\n          (u/throw* (t/make-string\n                      \"janet-eval first argument should be a string\")))\n        (let [res (try\n                    (eval-string (t/get-value head-ast)) # XXX: escaping?\n                    ([err]\n                     (u/throw* (t/make-string (string \"Eval failed: \" err)))))]\n          (janet-eval* res))))))\n\n(def unimplemented mal-throw)\n\n(def ns\n  {(t/make-symbol \"+\") (arith-fn +)\n   (t/make-symbol \"-\") (arith-fn -)\n   (t/make-symbol \"*\") (arith-fn *)\n   (t/make-symbol \"/\") (arith-fn /)\n   (t/make-symbol \"list\") mal-list\n   (t/make-symbol \"list?\") mal-list?\n   (t/make-symbol \"vec\") mal-vec\n   (t/make-symbol \"vector?\") mal-vector?\n   (t/make-symbol \"empty?\") mal-empty?\n   (t/make-symbol \"count\") mal-count\n   (t/make-symbol \"=\") mal-=\n   (t/make-symbol \"<\") (cmp-fn <)\n   (t/make-symbol \"<=\") (cmp-fn <=)\n   (t/make-symbol \">\") (cmp-fn >)\n   (t/make-symbol \">=\") (cmp-fn >=)\n   (t/make-symbol \"pr-str\") mal-pr-str\n   (t/make-symbol \"str\") mal-str\n   (t/make-symbol \"prn\") mal-prn\n   (t/make-symbol \"println\") mal-println\n   (t/make-symbol \"read-string\") mal-read-string\n   (t/make-symbol \"slurp\") mal-slurp\n   (t/make-symbol \"atom\") mal-atom\n   (t/make-symbol \"atom?\") mal-atom?\n   (t/make-symbol \"deref\") mal-deref\n   (t/make-symbol \"reset!\") mal-reset!\n   (t/make-symbol \"swap!\") mal-swap!\n   (t/make-symbol \"cons\") mal-cons\n   (t/make-symbol \"concat\") mal-concat\n   (t/make-symbol \"nth\") mal-nth\n   (t/make-symbol \"first\") mal-first\n   (t/make-symbol \"rest\") mal-rest\n   (t/make-symbol \"throw\") mal-throw\n   (t/make-symbol \"apply\") mal-apply\n   (t/make-symbol \"map\") mal-map\n   (t/make-symbol \"nil?\") mal-nil?\n   (t/make-symbol \"true?\") mal-true?\n   (t/make-symbol \"false?\") mal-false?\n   (t/make-symbol \"symbol?\") mal-symbol?\n   (t/make-symbol \"symbol\") mal-symbol\n   (t/make-symbol \"keyword\") mal-keyword\n   (t/make-symbol \"keyword?\") mal-keyword?\n   (t/make-symbol \"vector\") mal-vector\n   (t/make-symbol \"sequential?\") mal-sequential?\n   (t/make-symbol \"hash-map\") mal-hash-map\n   (t/make-symbol \"map?\") mal-map?\n   (t/make-symbol \"assoc\") mal-assoc\n   (t/make-symbol \"dissoc\") mal-dissoc\n   (t/make-symbol \"get\") mal-get\n   (t/make-symbol \"contains?\") mal-contains?\n   (t/make-symbol \"keys\") mal-keys\n   (t/make-symbol \"vals\") mal-vals\n   (t/make-symbol \"readline\") mal-readline\n   (t/make-symbol \"time-ms\") mal-time-ms\n   (t/make-symbol \"meta\") mal-meta\n   (t/make-symbol \"with-meta\") mal-with-meta\n   (t/make-symbol \"fn?\") mal-fn?\n   (t/make-symbol \"string?\") mal-string?\n   (t/make-symbol \"number?\") mal-number?\n   (t/make-symbol \"conj\") mal-conj\n   (t/make-symbol \"seq\") mal-seq\n   (t/make-symbol \"macro?\") mal-macro?\n   (t/make-symbol \"janet-eval\") mal-janet-eval\n})\n"
  },
  {
    "path": "impls/janet/env.janet",
    "content": "(import ./types :as t)\n(import ./utils :as u)\n\n(defn make-env\n  [&opt outer binds exprs]\n  (default binds [])\n  (default exprs [])\n  (def n-binds (length binds))\n  (var found-amp false)\n  (var idx 0)\n  (while (and (not found-amp)\n              (< idx n-binds))\n    (def c-bind (in binds idx))\n    (when (= (t/get-value c-bind) \"&\")\n      (set found-amp true)\n      (break))\n    (++ idx))\n  (def new-binds\n    (if found-amp\n      (array/concat (array ;(slice binds 0 idx))\n                    (in binds (inc idx)))\n      binds))\n  (def new-exprs\n    (if found-amp\n      (array/concat (array ;(slice exprs 0 idx))\n                    (array (t/make-list (slice exprs idx))))\n      exprs))\n  # XXX: would length mismatches of new-binds / new-exprs ever be an issue?\n  @{:data (zipcoll new-binds new-exprs)\n    :outer outer})\n\n(defn env-set\n  [env sym value]\n  (put-in env [:data sym]\n              value))\n\n(defn env-get\n  [env sym]\n  (or (get-in env [:data sym])\n      (if-let [outer (get env :outer)]\n        (env-get outer sym))))\n"
  },
  {
    "path": "impls/janet/printer.janet",
    "content": "(import ./types :as t)\n\n(defn escape\n  [a-str]\n  (->> (buffer a-str)\n       (peg/replace-all \"\\\\\" \"\\\\\\\\\")\n       (peg/replace-all \"\\\"\" \"\\\\\\\"\")\n       (peg/replace-all \"\\n\" \"\\\\n\")\n       string))\n\n(defn code*\n  [ast buf print_readably]\n  (cond\n    (or (t/boolean?* ast)\n        (t/nil?* ast)\n        (t/keyword?* ast)\n        (t/symbol?* ast))\n    (buffer/push-string buf (t/get-value ast))\n    ##\n    (t/number?* ast)\n    (buffer/push-string buf (string (t/get-value ast)))\n    ##\n    (t/string?* ast)\n    (if print_readably\n      (buffer/push-string buf (string \"\\\"\"\n                                      (escape (t/get-value ast))\n                                      \"\\\"\"))\n      (buffer/push-string buf (t/get-value ast)))\n    ##\n    (t/list?* ast)\n    (do\n      (buffer/push-string buf \"(\")\n      (var remove false)\n      (each elt (t/get-value ast)\n            (code* elt buf print_readably)\n            (buffer/push-string buf \" \")\n            (set remove true))\n      (when remove\n        (buffer/popn buf 1))\n      (buffer/push-string buf \")\"))\n    ##\n    (t/hash-map?* ast)\n    (do\n      (buffer/push-string buf \"{\")\n      (var remove false)\n      (eachp [k v] (t/get-value ast)\n            (code* k buf print_readably)\n            (buffer/push-string buf \" \")\n            (code* v buf print_readably)\n            (buffer/push-string buf \" \")\n            (set remove true))\n      (when remove\n        (buffer/popn buf 1))\n      (buffer/push-string buf \"}\"))\n    ##\n    (t/vector?* ast)\n    (do\n      (buffer/push-string buf \"[\")\n      (var remove false)\n      (each elt (t/get-value ast)\n            (code* elt buf print_readably)\n            (buffer/push-string buf \" \")\n            (set remove true))\n      (when remove\n        (buffer/popn buf 1))\n      (buffer/push-string buf \"]\"))\n    ## XXX: what about macro?\n    (t/fn?* ast)\n    (buffer/push-string buf \"#<function>\")\n    ##\n    (t/atom?* ast)\n    (do\n      (buffer/push-string buf \"(atom \")\n      (code* (t/get-value ast) buf print_readably)\n      (buffer/push-string buf \")\"))\n    ##\n    (t/exception?* ast)\n    (do\n      (buffer/push-string buf \"Error: \")\n      (code* (t/get-value ast) buf print_readably))))\n\n(comment\n\n  (let [buf @\"\"]\n    (code* (make-number 1) buf false))\n  # => @\"1\"\n\n  )\n\n(defn pr_str\n  [ast print_readably]\n  (let [buf @\"\"]\n    (code* ast buf print_readably)\n    buf))\n\n(comment\n\n  (pr_str (make-number 1) false)\n  # => @\"1\"\n\n  )\n"
  },
  {
    "path": "impls/janet/reader.janet",
    "content": "(import ./types :as t)\n(import ./utils :as u)\n\n(def grammar\n  ~{:main (capture (some :input))\n    :input (choice :gap :form)\n    :gap (choice :ws :comment)\n    :ws (set \" \\f\\n\\r\\t,\")\n    :comment (sequence \";\"\n                       (any (if-not (set \"\\r\\n\")\n                                    1)))\n    :form (choice :boolean :nil :number :keyword :symbol\n                  :string :list :vector :hash-map\n                  :deref :quasiquote :quote :splice-unquote :unquote\n                  :with-meta)\n    :name-char (if-not (set \" \\f\\n\\r\\t,[]{}()'`~^@\\\";\")\n                       1)\n    :boolean (sequence (choice \"false\" \"true\")\n                       (not :name-char))\n    :nil (sequence \"nil\"\n                   (not :name-char))\n    :number (drop (cmt\n                   (capture (some :name-char))\n                   ,scan-number))\n    :keyword (sequence \":\"\n                       (any :name-char))\n    :symbol (some :name-char)\n    :string (sequence \"\\\"\"\n                      (any (if-not (set \"\\\"\\\\\")\n                                   1))\n                      (any (sequence \"\\\\\"\n                                     1\n                                     (any (if-not (set \"\\\"\\\\\")\n                                                  1))))\n                      (choice \"\\\"\"\n                              (error (constant \"unbalanced \\\"\"))))\n    :hash-map (sequence \"{\"\n                        (any :input)\n                        (choice \"}\"\n                                (error (constant \"unbalanced }\"))))\n    :list (sequence \"(\"\n                    (any :input)\n                    (choice \")\"\n                            (error (constant \"unbalanced )\"))))\n    :vector (sequence \"[\"\n                      (any :input)\n                      (choice \"]\"\n                              (error (constant \"unbalanced ]\"))))\n    :deref (sequence \"@\" :form)\n    :quasiquote (sequence \"`\" :form)\n    :quote (sequence \"'\" :form)\n    :splice-unquote (sequence \"~@\" :form)\n    :unquote (sequence \"~\" :form)\n    :with-meta (sequence \"^\" :form (some :gap) :form)\n    }\n  )\n\n(comment\n\n  (peg/match grammar \" \")\n  # => @[\" \"]\n\n  (peg/match grammar \"; hello\")\n  # => @[\"; hello\"]\n\n  (peg/match grammar \"true\")\n  # => @[\"true\"]\n\n  (peg/match grammar \"false\")\n  # => @[\"false\"]\n\n  (peg/match grammar \"nil\")\n  # => @[\"nil\"]\n\n  (peg/match grammar \"18\")\n  # => @[\"18\"]\n\n  (peg/match grammar \"sym\")\n  # => @[\"sym\"]\n\n  (peg/match grammar \":alpha\")\n  # => @[\":alpha\"]\n\n  (peg/match grammar \"\\\"a string\\\"\")\n  # => @[\"\\\"a string\\\"\"]\n\n  (peg/match grammar \"(+ 1 2)\")\n  # => @[\"(+ 1 2)\"]\n\n  (peg/match grammar \"[:a :b :c]\")\n  # => @[\"[:a :b :c]\"]\n\n  (peg/match grammar \"{:a 1 :b 2}\")\n  # => @{\"{:a 1 :b 2}\"]\n\n  )\n\n(defn unescape\n  [a-str]\n  (->> a-str\n       (peg/replace-all \"\\\\\\\\\" \"\\u029e\") # XXX: a hack?\n       (peg/replace-all \"\\\\\\\"\" \"\\\"\")\n       (peg/replace-all \"\\\\n\" \"\\n\")\n       (peg/replace-all \"\\u029e\" \"\\\\\")\n       string))\n\n(def enlive-grammar\n  (let [cg (table ;(kvs grammar))]\n    (each kwd [# :comment # XX: don't capture comments\n               :boolean :keyword :nil\n               :symbol\n               # :ws # XXX: dont' capture whitespace\n              ]\n          (put cg kwd\n               ~(cmt (capture ,(in cg kwd))\n                     ,|{:tag (keyword kwd)\n                        :content $})))\n    (put cg :number\n            ~(cmt (capture ,(in cg :number))\n                  ,|{:tag :number\n                     :content (scan-number $)}))\n    (put cg :string\n            ~(cmt (capture ,(in cg :string))\n                  ,|{:tag :string\n                     # discard surrounding double quotes\n                     :content (unescape (slice $ 1 -2))}))\n    (each kwd [:deref :quasiquote :quote :splice-unquote :unquote]\n          (put cg kwd\n               ~(cmt (capture ,(in cg kwd))\n                     ,|{:tag :list\n                        :content [{:tag :symbol\n                                   :content (string kwd)}\n                                  ;(slice $& 0 -2)]})))\n    (each kwd [:list :vector]\n          (put cg kwd\n               (tuple # array needs to be converted\n                 ;(put (array ;(in cg kwd))\n                       2 ~(cmt (capture ,(get-in cg [kwd 2]))\n                               ,|{:tag (keyword kwd)\n                                  :content (slice $& 0 -2)})))))\n    (put cg :hash-map\n         (tuple # array needs to be converted\n           ;(put (array ;(in cg :hash-map))\n                 2 ~(cmt (capture ,(get-in cg [:hash-map 2]))\n                         ,|{:tag :hash-map\n                            :content (struct ;(slice $& 0 -2))}))))\n    (put cg :with-meta\n            ~(cmt (capture ,(in cg :with-meta))\n                  ,|{:tag :list\n                     :content [{:tag :symbol\n                                :content \"with-meta\"}\n                               (get $& 1)\n                               (get $& 0)]}))\n    # tried using a table with a peg but had a problem, so use a struct\n    (table/to-struct cg)))\n\n(comment\n\n  (peg/match enlive-grammar \"nil\")\n  # => @[{:content \"nil\" :tag :nil} \"nil\"]\n\n  (peg/match enlive-grammar \"true\")\n  # => @[{:content \"true\" :tag :boolean} \"true\"]\n\n  (peg/match enlive-grammar \":hi\")\n  # => @[{:content \":hi\" :tag :keyword} \":hi\"]\n\n  (peg/match enlive-grammar \"sym\")\n  # => @[{:content \"sym\" :tag :symbol} \"sym\"]\n\n  (peg/match enlive-grammar \"'a\")\n  ``\n  '@[{:content ({:content \"quote\"\n                 :tag :symbol}\n                {:content \"a\"\n                 :tag :symbol})\n      :tag :list} \"'a\"]\n  ``\n\n  (peg/match enlive-grammar \"@a\")\n  ``\n  '@[{:content ({:content \"deref\"\n                 :tag :symbol}\n                {:content \"a\"\n                 :tag :symbol})\n      :tag :list} \"@a\"]\n  ``\n\n  (peg/match enlive-grammar \"`a\")\n  ``\n  '@[{:content ({:content \"quasiquote\"\n                 :tag :symbol}\n                {:content \"a\"\n                 :tag :symbol})\n      :tag :list} \"`a\"]\n  ``\n\n  (peg/match enlive-grammar \"~a\")\n  ``\n  '@[{:content ({:content \"unquote\"\n                 :tag :symbol}\n                {:content \"a\"\n                 :tag :symbol})\n      :tag :list} \"~a\"]\n  ``\n\n  (peg/match enlive-grammar \"~@a\")\n  ``\n  '@[{:content ({:content \"splice-unquote\"\n                 :tag :symbol}\n                {:content \"a\"\n                 :tag :symbol})\n      :tag :list} \"~@a\"]\n  ``\n\n  (peg/match enlive-grammar \"(a b c)\")\n  ``\n  '@[{:content ({:content \"a\"\n                 :tag :symbol}\n                {:content \"b\"\n                 :tag :symbol}\n                {:content \"c\"\n                 :tag :symbol})\n      :tag :list} \"(a b c)\"]\n  ``\n\n  (peg/match enlive-grammar \"(a [:x :y] c)\")\n  ``\n  '@[{:content ({:content \"a\"\n                 :tag :symbol}\n                {:content ({:content \":x\"\n                            :tag :keyword}\n                           {:content \":y\"\n                            :tag :keyword})\n                 :tag :vector}\n                {:content \"c\"\n                 :tag :symbol})\n      :tag :list} \"(a [:x :y] c)\"]\n  ``\n\n  (peg/match enlive-grammar \"^{:a 1} [:x :y]\")\n  ``\n  '@[{:content ({:content \"with-meta\"\n                 :tag :symbol}\n                {:content ({:content \":x\"\n                            :tag :keyword}\n                           {:content \":y\"\n                            :tag :keyword})\n                 :tag :vector}\n                {:content {{:content \":a\"\n                            :tag :keyword}\n                           {:content \"1\"\n                            :tag :number}}\n                 :tag :hash-map})\n      :tag :list} \"^{:a 1} [:x :y]\"]\n  ``\n\n  (peg/match enlive-grammar \";; hi\")\n  # => @[\";; hi\"]\n\n  (peg/match enlive-grammar \"[:x ;; hi\\n :y]\")\n  ``\n  '@[{:content ({:content \":x\"\n                 :tag :keyword}\n                {:content \":y\"\n                 :tag :keyword})\n      :tag :vector} \"[:x ;; hi\\n :y]\"]\n  ``\n\n  (peg/match enlive-grammar \"  7  \")\n  # => @[{:content 7 :tag :number} \"  7  \"]\n\n  (peg/match enlive-grammar \"  abc  \")\n  # => @[{:content \"abc\" :tag :symbol} \"  abc  \"]\n\n  (peg/match enlive-grammar \"  \\nabc  \")\n  # => @[{:content \"abc\" :tag :symbol} \"  \\nabc  \"]\n\n  )\n\n(defn read_str\n  [code-str]\n  (let [[parsed _]\n        (try\n          (peg/match enlive-grammar code-str)\n          ([err]\n           (u/throw* (t/make-string err))))]\n    (if (= (type parsed) :struct)\n      parsed\n      (u/throw* t/mal-nil))))\n\n(comment\n\n  (read_str \"(+ 1 2)\")\n  ``\n  '{:content ({:content \"+\"\n               :tag :symbol}\n              {:content 1\n               :tag :number}\n              {:content 2\n               :tag :number})\n    :tag :list}\n  ``\n\n  (read_str \";; hello\")\n  # => nil\n\n  (read_str \"\\\"1\\\"\")\n  # => {:content \"1\" :tag :string}\n\n  )\n"
  },
  {
    "path": "impls/janet/run",
    "content": "#!/bin/sh\nexec janet $(dirname $0)/${STEP:-stepA_mal}.janet \"${@}\"\n"
  },
  {
    "path": "impls/janet/step0_repl.janet",
    "content": "(defn READ\n  [code-str]\n  code-str)\n\n(defn EVAL\n  [ast]\n  ast)\n\n(defn PRINT\n  [ast]\n  ast)\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str))))\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn main\n  [& args]\n  (var buf nil)\n  (while true\n    (set buf @\"\")\n    (getstdin \"user> \" buf)\n    (if (< 0 (length buf))\n      (prin (rep buf))\n      (break))))\n"
  },
  {
    "path": "impls/janet/step1_read_print.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(defn EVAL\n  [ast]\n  ast)\n\n(defn PRINT\n  [value]\n  (printer/pr_str value true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str))))\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (var buf nil)\n  (while true\n    (set buf @\"\")\n    (getstdin \"user> \" buf)\n    (if (= 0 (length buf))\n      (break)\n      (try\n        (print (rep buf))\n        ([err]\n         (handle-error err))))))\n"
  },
  {
    "path": "impls/janet/step2_eval.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(defn arith-fn\n  [op]\n  (fn [ast-1 ast-2]\n    (t/make-number (op (t/get-value ast-1)\n                       (t/get-value ast-2)))))\n\n(def repl_env\n  {(t/make-symbol \"+\") (arith-fn +)\n   (t/make-symbol \"-\") (arith-fn -)\n   (t/make-symbol \"*\") (arith-fn *)\n   (t/make-symbol \"/\") (arith-fn /)})\n\n(var EVAL nil)\n\n(defn EVAL\n  [ast env]\n\n  # (print (string \"EVAL: \" (printer/pr_str ast true)))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (or (env ast)\n      (error\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                   (kvs (t/get-value ast)))))\n\n    :vector\n    (t/make-vector (map |(EVAL $0 env)\n                        (t/get-value ast)))\n\n    :list\n    (if (t/empty?* ast)\n      ast\n      (let [ast-head (in (t/get-value ast) 0)\n                f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))\n                args (map |(EVAL $0 env) raw-args)]\n        (apply f args)))\n\n    # Neither a list, map, symbol or vector.\n    ast))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (var buf nil)\n  (while true\n    (set buf @\"\")\n    (getstdin \"user> \" buf)\n    (if (= 0 (length buf))\n      (break)\n      (try\n        (print (rep buf))\n        ([err]\n         (handle-error err))))))\n"
  },
  {
    "path": "impls/janet/step3_env.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(defn arith-fn\n  [op]\n  (fn [ast-1 ast-2]\n    (t/make-number (op (t/get-value ast-1)\n                       (t/get-value ast-2)))))\n\n(def repl_env\n  (let [env (e/make-env)]\n    (e/env-set env (t/make-symbol \"+\") (arith-fn +))\n    (e/env-set env (t/make-symbol \"-\") (arith-fn -))\n    (e/env-set env (t/make-symbol \"*\") (arith-fn *))\n    (e/env-set env (t/make-symbol \"/\") (arith-fn /))\n    env))\n\n(var EVAL nil)\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast env]\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (or (e/env-get env ast)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                   (kvs (t/get-value ast)))))\n\n    :vector\n    (t/make-vector (map |(EVAL $0 env)\n                        (t/get-value ast)))\n\n    :list\n    (if (t/empty?* ast)\n      ast\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            def-val)\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            (EVAL (in (t/get-value ast) 2) new-env))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))\n                args (map |(EVAL $0 env) raw-args)]\n            (apply f args)))))\n\n    # Neither a list, map, symbol or vector.\n    ast))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (var buf nil)\n  (while true\n    (set buf @\"\")\n    (getstdin \"user> \" buf)\n    (if (= 0 (length buf))\n      (break)\n      (try\n        (print (rep buf))\n        ([err]\n         (handle-error err))))))\n"
  },
  {
    "path": "impls/janet/step4_if_fn_do.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast env]\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (or (e/env-get env ast)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                   (kvs (t/get-value ast)))))\n\n    :vector\n    (t/make-vector (map |(EVAL $0 env)\n                        (t/get-value ast)))\n\n    :list\n    (if (t/empty?* ast)\n      ast\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            def-val)\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            (EVAL (in (t/get-value ast) 2) new-env))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            (EVAL last-body-form env))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                (EVAL else-ast env)\n                t/mal-nil)\n              (EVAL (in (t/get-value ast) 2) env)))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                       (e/make-env env params args)))))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))\n                args (map |(EVAL $0 env) raw-args)]\n            ((t/get-value f) args)))))\n\n    # Neither a list, map, symbol or vector.\n    ast))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (var buf nil)\n  (while true\n    (set buf @\"\")\n    (getstdin \"user> \" buf)\n    (if (= 0 (length buf))\n      (break)\n      (try\n        (print (rep buf))\n        ([err]\n         (handle-error err))))))\n"
  },
  {
    "path": "impls/janet/step5_tco.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast-param env-param]\n  (var ast ast-param)\n  (var env env-param)\n  (label result\n    (while true\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (if-let [value (e/env-get env ast)]\n      (return result value)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (return result\n      (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                     (kvs (t/get-value ast))))))\n\n    :vector\n    (return result\n      (t/make-vector (map |(EVAL $0 env)\n                          (t/get-value ast))))\n\n    :list\n    (if (t/empty?* ast)\n      (return result ast)\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            (return result def-val))\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            ## tco\n            (set ast (in (t/get-value ast) 2))\n            (set env new-env))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            ## tco\n            (set ast last-body-form))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                ## tco\n                (set ast else-ast)\n                (return result t/mal-nil))\n              ## tco\n              (set ast (in (t/get-value ast) 2))))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n            ## tco\n            (return result\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                   (e/make-env env params args)))\n                               nil false\n                               body params env)))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))\n                args (map |(EVAL $0 env) raw-args)]\n            (if-let [body (t/get-ast f)] ## tco\n              (do\n                (set ast body)\n                (set env (e/make-env (t/get-env f) (t/get-params f) args)))\n              (return result\n                ((t/get-value f) args)))))))\n\n    # Neither a list, map, symbol or vector.\n    (return result ast)))))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (var buf nil)\n  (while true\n    (set buf @\"\")\n    (getstdin \"user> \" buf)\n    (if (= 0 (length buf))\n      (break)\n      (try\n        (print (rep buf))\n        ([err]\n         (handle-error err))))))\n"
  },
  {
    "path": "impls/janet/step6_file.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast-param env-param]\n  (var ast ast-param)\n  (var env env-param)\n  (label result\n    (while true\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (if-let [value (e/env-get env ast)]\n      (return result value)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (return result\n      (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                     (kvs (t/get-value ast))))))\n\n    :vector\n    (return result\n      (t/make-vector (map |(EVAL $0 env)\n                          (t/get-value ast))))\n\n    :list\n    (if (t/empty?* ast)\n      (return result ast)\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            (return result def-val))\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            ## tco\n            (set ast (in (t/get-value ast) 2))\n            (set env new-env))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            ## tco\n            (set ast last-body-form))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                ## tco\n                (set ast else-ast)\n                (return result t/mal-nil))\n              ## tco\n              (set ast (in (t/get-value ast) 2))))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n            ## tco\n            (return result\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                   (e/make-env env params args)))\n                               nil false\n                               body params env)))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))\n                args (map |(EVAL $0 env) raw-args)]\n            (if-let [body (t/get-ast f)] ## tco\n              (do\n                (set ast body)\n                (set env (e/make-env (t/get-env f) (t/get-params f) args)))\n              (return result\n                ((t/get-value f) args)))))))\n\n    # Neither a list, map, symbol or vector.\n    (return result ast)))))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e/env-set repl_env\n           (t/make-symbol \"eval\")\n           (t/make-function (fn [asts]\n                              (EVAL (in asts 0) repl_env))))\n\n(rep ``\n  (def! load-file\n        (fn* (fpath)\n          (eval\n            (read-string (str \"(do \"\n                                (slurp fpath) \"\\n\"\n                                \"nil)\")))))\n``)\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (let [args-len (length args)\n        argv (if (<= 2 args-len)\n               (drop 2 args)\n               ())]\n    (e/env-set repl_env\n               (t/make-symbol \"*ARGV*\")\n               (t/make-list (map t/make-string argv)))\n    (if (< 1 args-len)\n      (try\n        (rep\n          (string \"(load-file \\\"\" (in args 1) \"\\\")\")) # XXX: escaping?\n        ([err]\n         (handle-error err)))\n      (do\n        (var buf nil)\n        (while true\n          (set buf @\"\")\n          (getstdin \"user> \" buf)\n          (if (= 0 (length buf))\n            (break)\n            (try\n              (print (rep buf))\n              ([err]\n               (handle-error err)))))))))\n"
  },
  {
    "path": "impls/janet/step7_quote.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(defn starts-with\n  [ast name]\n  (when (and (t/list?* ast)\n             (not (t/empty?* ast)))\n    (let [head-ast (in (t/get-value ast) 0)]\n      (and (t/symbol?* head-ast)\n           (= name (t/get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(defn qq-iter\n  [ast]\n  (if (t/empty?* ast)\n    (t/make-list ())\n    (let [elt (in (t/get-value ast) 0)\n          acc (qq-iter (t/make-list (slice (t/get-value ast) 1)))]\n      (if (starts-with elt \"splice-unquote\")\n        (t/make-list [(t/make-symbol \"concat\")\n                      (in (t/get-value elt) 1)\n                      acc])\n        (t/make-list [(t/make-symbol \"cons\")\n                      (quasiquote* elt)\n                      acc])))))\n\n(varfn quasiquote*\n  [ast]\n  (cond\n    (starts-with ast \"unquote\")\n    (in (t/get-value ast) 1)\n    ##\n    (t/list?* ast)\n    (qq-iter ast)\n    ##\n    (t/vector?* ast)\n    (t/make-list [(t/make-symbol \"vec\") (qq-iter ast)])\n    ##\n    (or (t/symbol?* ast)\n        (t/hash-map?* ast))\n    (t/make-list [(t/make-symbol \"quote\") ast])\n    ##\n    ast))\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast-param env-param]\n  (var ast ast-param)\n  (var env env-param)\n  (label result\n    (while true\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (if-let [value (e/env-get env ast)]\n      (return result value)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (return result\n      (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                     (kvs (t/get-value ast))))))\n\n    :vector\n    (return result\n      (t/make-vector (map |(EVAL $0 env)\n                          (t/get-value ast))))\n\n    :list\n    (if (t/empty?* ast)\n      (return result ast)\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            (return result def-val))\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            ## tco\n            (set ast (in (t/get-value ast) 2))\n            (set env new-env))\n          ##\n          \"quote\"\n          (return result (in (t/get-value ast) 1))\n          ##\n          \"quasiquote\"\n          ## tco\n          (set ast (quasiquote* (in (t/get-value ast) 1)))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            ## tco\n            (set ast last-body-form))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                ## tco\n                (set ast else-ast)\n                (return result t/mal-nil))\n              ## tco\n              (set ast (in (t/get-value ast) 2))))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n            ## tco\n            (return result\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                   (e/make-env env params args)))\n                               nil false\n                               body params env)))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))\n                args (map |(EVAL $0 env) raw-args)]\n            (if-let [body (t/get-ast f)] ## tco\n              (do\n                (set ast body)\n                (set env (e/make-env (t/get-env f) (t/get-params f) args)))\n              (return result\n                ((t/get-value f) args)))))))\n\n    # Neither a list, map, symbol or vector.\n    (return result ast)))))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e/env-set repl_env\n           (t/make-symbol \"eval\")\n           (t/make-function (fn [asts]\n                              (EVAL (in asts 0) repl_env))))\n\n(rep ``\n  (def! load-file\n        (fn* (fpath)\n          (eval\n            (read-string (str \"(do \"\n                                (slurp fpath) \"\\n\"\n                                \"nil)\")))))\n``)\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (let [args-len (length args)\n        argv (if (<= 2 args-len)\n               (drop 2 args)\n               ())]\n    (e/env-set repl_env\n               (t/make-symbol \"*ARGV*\")\n               (t/make-list (map t/make-string argv)))\n    (if (< 1 args-len)\n      (try\n        (rep\n          (string \"(load-file \\\"\" (in args 1) \"\\\")\")) # XXX: escaping?\n        ([err]\n         (handle-error err)))\n      (do\n        (var buf nil)\n        (while true\n          (set buf @\"\")\n          (getstdin \"user> \" buf)\n          (if (= 0 (length buf))\n            (break)\n            (try\n              (print (rep buf))\n              ([err]\n               (handle-error err)))))))))\n"
  },
  {
    "path": "impls/janet/step8_macros.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(defn starts-with\n  [ast name]\n  (when (and (t/list?* ast)\n             (not (t/empty?* ast)))\n    (let [head-ast (in (t/get-value ast) 0)]\n      (and (t/symbol?* head-ast)\n           (= name (t/get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(defn qq-iter\n  [ast]\n  (if (t/empty?* ast)\n    (t/make-list ())\n    (let [elt (in (t/get-value ast) 0)\n          acc (qq-iter (t/make-list (slice (t/get-value ast) 1)))]\n      (if (starts-with elt \"splice-unquote\")\n        (t/make-list [(t/make-symbol \"concat\")\n                      (in (t/get-value elt) 1)\n                      acc])\n        (t/make-list [(t/make-symbol \"cons\")\n                      (quasiquote* elt)\n                      acc])))))\n\n(varfn quasiquote*\n  [ast]\n  (cond\n    (starts-with ast \"unquote\")\n    (in (t/get-value ast) 1)\n    ##\n    (t/list?* ast)\n    (qq-iter ast)\n    ##\n    (t/vector?* ast)\n    (t/make-list [(t/make-symbol \"vec\") (qq-iter ast)])\n    ##\n    (or (t/symbol?* ast)\n        (t/hash-map?* ast))\n    (t/make-list [(t/make-symbol \"quote\") ast])\n    ##\n    ast))\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast-param env-param]\n  (var ast ast-param)\n  (var env env-param)\n  (label result\n    (while true\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (if-let [value (e/env-get env ast)]\n      (return result value)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (return result\n      (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                     (kvs (t/get-value ast))))))\n\n    :vector\n    (return result\n      (t/make-vector (map |(EVAL $0 env)\n                          (t/get-value ast))))\n\n    :list\n    (if (t/empty?* ast)\n      (return result ast)\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            (return result def-val))\n          ##\n          \"defmacro!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)\n                macro-ast (t/macrofy def-val)]\n            (e/env-set env\n                       def-name macro-ast)\n            (return result macro-ast))\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            ## tco\n            (set ast (in (t/get-value ast) 2))\n            (set env new-env))\n          ##\n          \"quote\"\n          (return result (in (t/get-value ast) 1))\n          ##\n          \"quasiquote\"\n          ## tco\n          (set ast (quasiquote* (in (t/get-value ast) 1)))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            ## tco\n            (set ast last-body-form))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                ## tco\n                (set ast else-ast)\n                (return result t/mal-nil))\n              ## tco\n              (set ast (in (t/get-value ast) 2))))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n            ## tco\n            (return result\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                   (e/make-env env params args)))\n                               nil false\n                               body params env)))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))]\n          (if (t/macro?* f)\n          (set ast ((t/get-value f) raw-args))\n          (let [args (map |(EVAL $0 env) raw-args)]\n            (if-let [body (t/get-ast f)] ## tco\n              (do\n                (set ast body)\n                (set env (e/make-env (t/get-env f) (t/get-params f) args)))\n              (return result\n                ((t/get-value f) args)))))))))\n\n    # Neither a list, map, symbol or vector.\n    (return result ast)))))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e/env-set repl_env\n           (t/make-symbol \"eval\")\n           (t/make-function (fn [asts]\n                              (EVAL (in asts 0) repl_env))))\n\n(rep ``\n  (def! load-file\n        (fn* (fpath)\n          (eval\n            (read-string (str \"(do \"\n                                (slurp fpath) \"\\n\"\n                                \"nil)\")))))\n``)\n\n(rep ``\n  (defmacro! cond\n    (fn* (& xs)\n      (if (> (count xs) 0)\n        (list 'if\n              (first xs)\n              (if (> (count xs) 1)\n                  (nth xs 1)\n                  (throw \"odd number of forms to cond\"))\n              (cons 'cond (rest (rest xs)))))))\n``)\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (let [args-len (length args)\n        argv (if (<= 2 args-len)\n               (drop 2 args)\n               ())]\n    (e/env-set repl_env\n               (t/make-symbol \"*ARGV*\")\n               (t/make-list (map t/make-string argv)))\n    (if (< 1 args-len)\n      (try\n        (rep\n          (string \"(load-file \\\"\" (in args 1) \"\\\")\")) # XXX: escaping?\n        ([err]\n         (handle-error err)))\n      (do\n        (var buf nil)\n        (while true\n          (set buf @\"\")\n          (getstdin \"user> \" buf)\n          (if (= 0 (length buf))\n            (break)\n            (try\n              (print (rep buf))\n              ([err]\n               (handle-error err)))))))))\n"
  },
  {
    "path": "impls/janet/step9_try.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(defn starts-with\n  [ast name]\n  (when (and (t/list?* ast)\n             (not (t/empty?* ast)))\n    (let [head-ast (in (t/get-value ast) 0)]\n      (and (t/symbol?* head-ast)\n           (= name (t/get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(defn qq-iter\n  [ast]\n  (if (t/empty?* ast)\n    (t/make-list ())\n    (let [elt (in (t/get-value ast) 0)\n          acc (qq-iter (t/make-list (slice (t/get-value ast) 1)))]\n      (if (starts-with elt \"splice-unquote\")\n        (t/make-list [(t/make-symbol \"concat\")\n                      (in (t/get-value elt) 1)\n                      acc])\n        (t/make-list [(t/make-symbol \"cons\")\n                      (quasiquote* elt)\n                      acc])))))\n\n(varfn quasiquote*\n  [ast]\n  (cond\n    (starts-with ast \"unquote\")\n    (in (t/get-value ast) 1)\n    ##\n    (t/list?* ast)\n    (qq-iter ast)\n    ##\n    (t/vector?* ast)\n    (t/make-list [(t/make-symbol \"vec\") (qq-iter ast)])\n    ##\n    (or (t/symbol?* ast)\n        (t/hash-map?* ast))\n    (t/make-list [(t/make-symbol \"quote\") ast])\n    ##\n    ast))\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast-param env-param]\n  (var ast ast-param)\n  (var env env-param)\n  (label result\n    (while true\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (if-let [value (e/env-get env ast)]\n      (return result value)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (return result\n      (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                     (kvs (t/get-value ast))))))\n\n    :vector\n    (return result\n      (t/make-vector (map |(EVAL $0 env)\n                          (t/get-value ast))))\n\n    :list\n    (if (t/empty?* ast)\n      (return result ast)\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            (return result def-val))\n          ##\n          \"defmacro!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)\n                macro-ast (t/macrofy def-val)]\n            (e/env-set env\n                       def-name macro-ast)\n            (return result macro-ast))\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            ## tco\n            (set ast (in (t/get-value ast) 2))\n            (set env new-env))\n          ##\n          \"quote\"\n          (return result (in (t/get-value ast) 1))\n          ##\n          \"quasiquote\"\n          ## tco\n          (set ast (quasiquote* (in (t/get-value ast) 1)))\n          ##\n          \"try*\"\n          (let [res\n                (try\n                  (EVAL (in (t/get-value ast) 1) env)\n                  ([err]\n                   (if-let [maybe-catch-ast (get (t/get-value ast) 2)]\n                     (if (starts-with maybe-catch-ast \"catch*\")\n                       (let [catch-asts (t/get-value maybe-catch-ast)]\n                         (if (>= (length catch-asts) 2)\n                           (let [catch-sym-ast (in catch-asts 1)\n                                 catch-body-ast (in catch-asts 2)]\n                             (EVAL catch-body-ast (e/make-env env\n                                                              [catch-sym-ast]\n                                                              [err])))\n                           (u/throw*\n                             (t/make-string\n                               \"catch* requires at least 2 arguments\"))))\n                       (u/throw*\n                         (t/make-string\n                           \"Expected catch* form\")))\n                     # XXX: is this appropriate?  show error message?\n                     (u/throw* err))))]\n            (return result res))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            ## tco\n            (set ast last-body-form))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                ## tco\n                (set ast else-ast)\n                (return result t/mal-nil))\n              ## tco\n              (set ast (in (t/get-value ast) 2))))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n            ## tco\n            (return result\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                   (e/make-env env params args)))\n                               nil false\n                               body params env)))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))]\n          (if (t/macro?* f)\n          (set ast ((t/get-value f) raw-args))\n          (let [args (map |(EVAL $0 env) raw-args)]\n            (if-let [body (t/get-ast f)] ## tco\n              (do\n                (set ast body)\n                (set env (e/make-env (t/get-env f) (t/get-params f) args)))\n              (return result\n                ((t/get-value f) args)))))))))\n\n    # Neither a list, map, symbol or vector.\n    (return result ast)))))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e/env-set repl_env\n           (t/make-symbol \"eval\")\n           (t/make-function (fn [asts]\n                              (EVAL (in asts 0) repl_env))))\n\n(rep ``\n  (def! load-file\n        (fn* (fpath)\n          (eval\n            (read-string (str \"(do \"\n                                (slurp fpath) \"\\n\"\n                                \"nil)\")))))\n``)\n\n(rep ``\n  (defmacro! cond\n    (fn* (& xs)\n      (if (> (count xs) 0)\n        (list 'if\n              (first xs)\n              (if (> (count xs) 1)\n                  (nth xs 1)\n                  (throw \"odd number of forms to cond\"))\n              (cons 'cond (rest (rest xs)))))))\n``)\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (let [args-len (length args)\n        argv (if (<= 2 args-len)\n               (drop 2 args)\n               ())]\n    (e/env-set repl_env\n               (t/make-symbol \"*ARGV*\")\n               (t/make-list (map t/make-string argv)))\n    (if (< 1 args-len)\n      (try\n        (rep\n          (string \"(load-file \\\"\" (in args 1) \"\\\")\")) # XXX: escaping?\n        ([err]\n         (handle-error err)))\n      (do\n        (var buf nil)\n        (while true\n          (set buf @\"\")\n          (getstdin \"user> \" buf)\n          (if (= 0 (length buf))\n            (break)\n            (try\n              (print (rep buf))\n              ([err]\n               (handle-error err)))))))))\n"
  },
  {
    "path": "impls/janet/stepA_mal.janet",
    "content": "(import ./reader)\n(import ./printer)\n(import ./types :as t)\n(import ./utils :as u)\n(import ./env :as e)\n(import ./core)\n\n(def repl_env\n  (let [env (e/make-env)]\n    (eachp [k v] core/ns\n      (e/env-set env k v))\n    env))\n\n(defn READ\n  [code-str]\n  (reader/read_str code-str))\n\n(var EVAL nil)\n\n(defn starts-with\n  [ast name]\n  (when (and (t/list?* ast)\n             (not (t/empty?* ast)))\n    (let [head-ast (in (t/get-value ast) 0)]\n      (and (t/symbol?* head-ast)\n           (= name (t/get-value head-ast))))))\n\n(var quasiquote* nil)\n\n(defn qq-iter\n  [ast]\n  (if (t/empty?* ast)\n    (t/make-list ())\n    (let [elt (in (t/get-value ast) 0)\n          acc (qq-iter (t/make-list (slice (t/get-value ast) 1)))]\n      (if (starts-with elt \"splice-unquote\")\n        (t/make-list [(t/make-symbol \"concat\")\n                      (in (t/get-value elt) 1)\n                      acc])\n        (t/make-list [(t/make-symbol \"cons\")\n                      (quasiquote* elt)\n                      acc])))))\n\n(varfn quasiquote*\n  [ast]\n  (cond\n    (starts-with ast \"unquote\")\n    (in (t/get-value ast) 1)\n    ##\n    (t/list?* ast)\n    (qq-iter ast)\n    ##\n    (t/vector?* ast)\n    (t/make-list [(t/make-symbol \"vec\") (qq-iter ast)])\n    ##\n    (or (t/symbol?* ast)\n        (t/hash-map?* ast))\n    (t/make-list [(t/make-symbol \"quote\") ast])\n    ##\n    ast))\n\n(var DEBUG-EVAL (t/make-symbol \"DEBUG-EVAL\"))\n\n(varfn EVAL\n  [ast-param env-param]\n  (var ast ast-param)\n  (var env env-param)\n  (label result\n    (while true\n\n    (if-let [dbgeval (e/env-get env DEBUG-EVAL)]\n      (if (not (or (t/nil?* dbgeval)\n                   (t/false?* dbgeval)))\n        (print (string \"EVAL: \" (printer/pr_str ast true)))))\n\n    (case (t/get-type ast)\n\n    :symbol\n    (if-let [value (e/env-get env ast)]\n      (return result value)\n      (u/throw*\n        (t/make-string\n          (string \"'\" (t/get-value ast) \"'\" \" not found\" ))))\n\n    :hash-map\n    (return result\n      (t/make-hash-map (struct ;(map |(EVAL $0 env)\n                                     (kvs (t/get-value ast))))))\n\n    :vector\n    (return result\n      (t/make-vector (map |(EVAL $0 env)\n                          (t/get-value ast))))\n\n    :list\n    (if (t/empty?* ast)\n      (return result ast)\n      (let [ast-head (in (t/get-value ast) 0)\n            head-name (t/get-value ast-head)]\n        (case head-name\n          \"def!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)]\n            (e/env-set env\n                       def-name def-val)\n            (return result def-val))\n          ##\n          \"defmacro!\"\n          (let [def-name (in (t/get-value ast) 1)\n                def-val (EVAL (in (t/get-value ast) 2) env)\n                macro-ast (t/macrofy def-val)]\n            (e/env-set env\n                       def-name macro-ast)\n            (return result macro-ast))\n          ##\n          \"let*\"\n          (let [new-env (e/make-env env)\n                bindings (t/get-value (in (t/get-value ast) 1))]\n            (each [let-name let-val] (partition 2 bindings)\n                  (e/env-set new-env\n                             let-name (EVAL let-val new-env)))\n            ## tco\n            (set ast (in (t/get-value ast) 2))\n            (set env new-env))\n          ##\n          \"quote\"\n          (return result (in (t/get-value ast) 1))\n          ##\n          \"quasiquote\"\n          ## tco\n          (set ast (quasiquote* (in (t/get-value ast) 1)))\n          ##\n          \"try*\"\n          (let [res\n                (try\n                  (EVAL (in (t/get-value ast) 1) env)\n                  ([err]\n                   (if-let [maybe-catch-ast (get (t/get-value ast) 2)]\n                     (if (starts-with maybe-catch-ast \"catch*\")\n                       (let [catch-asts (t/get-value maybe-catch-ast)]\n                         (if (>= (length catch-asts) 2)\n                           (let [catch-sym-ast (in catch-asts 1)\n                                 catch-body-ast (in catch-asts 2)]\n                             (EVAL catch-body-ast (e/make-env env\n                                                              [catch-sym-ast]\n                                                              [err])))\n                           (u/throw*\n                             (t/make-string\n                               \"catch* requires at least 2 arguments\"))))\n                       (u/throw*\n                         (t/make-string\n                           \"Expected catch* form\")))\n                     # XXX: is this appropriate?  show error message?\n                     (u/throw* err))))]\n            (return result res))\n          ##\n          \"do\"\n          (let [most-do-body-forms (slice (t/get-value ast) 1 -2)\n                last-body-form (last (t/get-value ast))]\n            (each x most-do-body-forms (EVAL x env))\n            ## tco\n            (set ast last-body-form))\n          ##\n          \"if\"\n          (let [cond-res (EVAL (in (t/get-value ast) 1) env)]\n            (if (or (t/nil?* cond-res)\n                    (t/false?* cond-res))\n              (if-let [else-ast (get (t/get-value ast) 3)]\n                ## tco\n                (set ast else-ast)\n                (return result t/mal-nil))\n              ## tco\n              (set ast (in (t/get-value ast) 2))))\n          ##\n          \"fn*\"\n          (let [params (t/get-value (in (t/get-value ast) 1))\n                body (in (t/get-value ast) 2)]\n            ## tco\n            (return result\n              (t/make-function (fn [args]\n                                 (EVAL body\n                                   (e/make-env env params args)))\n                               nil false\n                               body params env)))\n          ##\n          (let [f (EVAL ast-head env)\n                raw-args (drop 1 (t/get-value ast))]\n          (if (t/macro?* f)\n          (set ast ((t/get-value f) raw-args))\n          (let [args (map |(EVAL $0 env) raw-args)]\n            (if-let [body (t/get-ast f)] ## tco\n              (do\n                (set ast body)\n                (set env (e/make-env (t/get-env f) (t/get-params f) args)))\n              (return result\n                ((t/get-value f) args)))))))))\n\n    # Neither a list, map, symbol or vector.\n    (return result ast)))))\n\n(defn PRINT\n  [ast]\n  (printer/pr_str ast true))\n\n(defn rep\n  [code-str]\n  (PRINT (EVAL (READ code-str) repl_env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(e/env-set repl_env\n           (t/make-symbol \"eval\")\n           (t/make-function (fn [asts]\n                              (EVAL (in asts 0) repl_env))))\n\n(rep ``\n  (def! load-file\n        (fn* (fpath)\n          (eval\n            (read-string (str \"(do \"\n                                (slurp fpath) \"\\n\"\n                                \"nil)\")))))\n``)\n\n(rep ``\n  (defmacro! cond\n    (fn* (& xs)\n      (if (> (count xs) 0)\n        (list 'if\n              (first xs)\n              (if (> (count xs) 1)\n                  (nth xs 1)\n                  (throw \"odd number of forms to cond\"))\n              (cons 'cond (rest (rest xs)))))))\n``)\n\n(e/env-set repl_env\n           (t/make-symbol \"*host-language*\")\n           (t/make-string \"janet\"))\n\n# getline gives problems\n(defn getstdin [prompt buf]\n  (file/write stdout prompt)\n  (file/flush stdout)\n  (file/read stdin :line buf))\n\n(defn handle-error\n  [err]\n  (cond\n    (t/nil?* err)\n    (print)\n    ##\n    (string? err)\n    (print err)\n    ##\n    (print (string \"Error: \" (PRINT err)))))\n\n(defn main\n  [& args]\n  (let [args-len (length args)\n        argv (if (<= 2 args-len)\n               (drop 2 args)\n               ())]\n    (e/env-set repl_env\n               (t/make-symbol \"*ARGV*\")\n               (t/make-list (map t/make-string argv)))\n    (if (< 1 args-len)\n      (try\n        (rep\n          (string \"(load-file \\\"\" (in args 1) \"\\\")\")) # XXX: escaping?\n        ([err]\n         (handle-error err)))\n      (do\n        (var buf nil)\n        (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n        (while true\n          (set buf @\"\")\n          (getstdin \"user> \" buf)\n          (if (= 0 (length buf))\n            (break)\n            (try\n              (print (rep buf))\n              ([err]\n               (handle-error err)))))))))\n"
  },
  {
    "path": "impls/janet/tests/stepA_mal.mal",
    "content": ";; Testing basic Janet interop\n\n(janet-eval \"7\")\n;=>7\n\n(janet-eval \"\\\"7\\\"\")\n;=>\"7\"\n\n(janet-eval \"nil\")\n;=>nil\n\n(janet-eval \"(= 123 123)\")\n;=>true\n\n(janet-eval \"(= 123 456)\")\n;=>false\n\n(janet-eval \":my-keyword\")\n;=>:my-keyword\n\n(janet-eval \"'(7 8 9)\")\n;=>(7 8 9)\n\n(janet-eval \"{:abc 789}\")\n;=>{:abc 789}\n\n(janet-eval \"(print \\\"hello\\\")\")\n;/hello\n;=>nil\n\n(janet-eval \"(defn foo [] 8)\")\n(janet-eval \"(foo)\")\n;=>8\n\n(janet-eval \"(let [tup [:a 1 :b 2]] (struct ;tup))\")\n;=>{:a 1 :b 2}\n\n(janet-eval \"(do (def tbl @{}) (put tbl :x 8) tbl)\")\n;=>{:x 8}\n\n(janet-eval \"(do (var mut 1) (set mut 2) mut)\")\n;=>2\n"
  },
  {
    "path": "impls/janet/types.janet",
    "content": "(defn make-nil\n  []\n  {:tag :nil\n   :content \"nil\"})\n\n(defn make-boolean\n  [bool]\n  {:tag :boolean\n   :content (string bool)})\n\n(defn make-keyword\n  [a-str]\n  {:tag :keyword\n   :content a-str})\n\n(defn make-number\n  [a-num]\n  {:tag :number\n   :content a-num})\n\n(defn make-string\n  [a-str]\n  {:tag :string\n   :content a-str})\n\n(defn make-symbol\n  [a-str]\n  {:tag :symbol\n   :content a-str})\n\n(defn make-hash-map\n  [items &opt meta]\n  (default meta (make-nil))\n  (let [a-struct (if (dictionary? items)\n                   items\n                   (struct ;items))]\n    {:tag :hash-map\n     :content a-struct\n     :meta meta}))\n\n(defn make-list\n  [items &opt meta]\n  (default meta (make-nil))\n  {:tag :list\n   :content items\n   :meta meta})\n\n(defn make-vector\n  [items &opt meta]\n  (default meta (make-nil))\n  {:tag :vector\n   :content items\n   :meta meta})\n\n(defn make-function\n  [a-fn &opt meta is-macro ast params env]\n  (default meta (make-nil))\n  (default is-macro false)\n  {:tag :function\n   :content a-fn\n   :meta meta\n   :is-macro is-macro\n   :ast ast\n   :params params\n   :env env})\n\n(defn make-atom\n  [ast]\n  @{:tag :atom\n    :content ast})\n\n(defn set-atom-value!\n  [atom-ast value-ast]\n  (put atom-ast\n       :content value-ast))\n\n(defn make-exception\n  [ast]\n  {:tag :exception\n   :content ast})\n\n## common accessors\n\n(defn get-value\n  [ast]\n  (ast :content))\n\n(defn get-type\n  [ast]\n  (ast :tag))\n\n(defn get-meta\n  [ast]\n  (ast :meta))\n\n## function-specific accessors\n\n(defn get-is-macro\n  [ast]\n  (ast :is-macro))\n\n(defn get-ast\n  [ast]\n  (ast :ast))\n\n(defn get-params\n  [ast]\n  (ast :params))\n\n(defn get-env\n  [ast]\n  (ast :env))\n\n## function-specific functions\n\n(defn macrofy\n  [fn-ast]\n  (merge fn-ast {:is-macro true}))\n\n(defn clone-with-meta\n  [fn-ast meta-ast]\n  (merge fn-ast {:meta meta-ast}))\n\n## predicates\n\n(defn nil?*\n  [ast]\n  (= :nil (get-type ast)))\n\n(defn boolean?*\n  [ast]\n  (= :boolean (get-type ast)))\n\n(defn true?*\n  [ast]\n  (and (boolean?* ast)\n       (= \"true\" (get-value ast))))\n\n(defn false?*\n  [ast]\n  (and (boolean?* ast)\n       (= \"false\" (get-value ast))))\n\n(defn number?*\n  [ast]\n  (= :number (get-type ast)))\n\n(defn symbol?*\n  [ast]\n  (= :symbol (get-type ast)))\n\n(defn keyword?*\n  [ast]\n  (= :keyword (get-type ast)))\n\n(defn string?*\n  [ast]\n  (= :string (get-type ast)))\n\n(defn list?*\n  [ast]\n  (= :list (get-type ast)))\n\n(defn vector?*\n  [ast]\n  (= :vector (get-type ast)))\n\n(defn hash-map?*\n  [ast]\n  (= :hash-map (get-type ast)))\n\n(defn fn?*\n  [ast]\n  (= :function (get-type ast)))\n\n(defn macro?*\n  [ast]\n  (and (fn?* ast)\n       (get-is-macro ast)))\n\n(defn atom?*\n  [ast]\n  (= :atom (get-type ast)))\n\n(defn exception?*\n  [ast]\n  (= :exception (get-type ast)))\n\n(defn empty?*\n  [ast]\n  (empty? (get-value ast)))\n\n# XXX: likely this could be simpler\n(defn equals?*\n  [ast-1 ast-2]\n  (let [type-1 (get-type ast-1)\n        type-2 (get-type ast-2)]\n    (if (and (not= type-1 type-2)\n             # XXX: not elegant\n             (not (and (list?* ast-1) (vector?* ast-2)))\n             (not (and (list?* ast-2) (vector?* ast-1))))\n      false\n      (let [val-1 (get-value ast-1)\n            val-2 (get-value ast-2)]\n        # XXX: when not a collection...\n        (if (and (not (list?* ast-1))\n                 (not (vector?* ast-1))\n                 (not (hash-map?* ast-1)))\n          (= val-1 val-2)\n          (if (not= (length val-1) (length val-2))\n            false\n            (if (and (not (hash-map?* ast-1))\n                     (not (hash-map?* ast-2)))\n              (do\n                (var found-unequal false)\n                (each [v1 v2] (partition 2 (interleave val-1 val-2))\n                      (when (not (equals?* v1 v2))\n                        (set found-unequal true)\n                        (break)))\n                (not found-unequal))\n              (if (or (not (hash-map?* ast-1))\n                      (not (hash-map?* ast-2)))\n                false\n                (do\n                  (var found-unequal false)\n                  (each [k1 k2] (partition 2 (interleave (keys val-1)\n                                                         (keys val-2)))\n                        (when (not (equals?* k1 k2))\n                          (set found-unequal true)\n                          (break))\n                        (when (not (equals?* (val-1 k1) (val-2 k2)))\n                          (set found-unequal true)\n                          (break)))\n                  (not found-unequal))))))))))\n\n## highlander types\n\n(def mal-nil\n  (make-nil))\n\n(def mal-true\n  (make-boolean true))\n\n(def mal-false\n  (make-boolean false))\n"
  },
  {
    "path": "impls/janet/utils.janet",
    "content": "(defn throw*\n  [ast]\n  (error ast))\n"
  },
  {
    "path": "impls/java/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Java and maven\nRUN apt-get -y install default-jdk-headless maven\nENV MAVEN_OPTS -Duser.home=/mal\n"
  },
  {
    "path": "impls/java/Makefile",
    "content": "\nSOURCES_BASE = src/main/java/mal/readline.java src/main/java/mal/types.java \\\n               src/main/java/mal/reader.java src/main/java/mal/printer.java\nSOURCES_LISP = src/main/java/mal/env.java src/main/java/mal/core.java \\\n               src/main/java/mal/stepA_mal.java\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\tmvn install\n\ndist: mal.jar mal\n\nmal.jar: target/classes/mal/stepA_mal.class\n\tmvn assembly:assembly\n\tcp target/mal-0.0.1.jar $@\n\nSHELL := bash\nmal: mal.jar\n\tcat <(echo -e '#!/bin/sh\\nexec java -jar \"$$0\" \"$$@\"') mal.jar > $@\n\tchmod +x mal\n\nsrc/main/mal/%.java:\n\tmvn install\n\ntarget/classes/mal/step%.class: src/main/java/mal/step%.java ${SOURCES}\n\tmvn install\n\nclean:\n\tmvn clean\n\trm -f mal.jar mal\n"
  },
  {
    "path": "impls/java/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>org.martintribe</groupId>\n    <artifactId>mal</artifactId>\n    <packaging>jar</packaging>\n    <version>0.0.1</version>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <version>16.0.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.3</version>\n        </dependency>\n        <dependency>\n            <groupId>net.java.dev.jna</groupId>\n            <artifactId>jna</artifactId>\n            <version>4.0.0</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.0</version>\n                <configuration>\n                    <source>1.7</source>\n                    <target>1.7</target>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>exec-maven-plugin</artifactId>\n                <version>1.2.1</version>\n                <executions>\n                    <execution>\n                    <goals>\n                        <goal>java</goal>\n                    </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <!--\n                    <mainClass>mal.stepA_mal</mainClass>\n                    <arguments>\n                        <argument>foo</argument>\n                        <argument>bar</argument>\n                    </arguments>\n                    -->\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-shade-plugin</artifactId>\n                <version>1.7.1</version>\n                <executions>\n                    <execution>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>shade</goal>\n                        </goals>\n                        <configuration>\n                            <transformers>\n                                <transformer\n                                    implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n                                    <mainClass>mal.stepA_mal</mainClass>\n                                </transformer>\n                            </transformers>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-assembly-plugin</artifactId>\n                <configuration>\n                        <descriptorRefs>\n                            <descriptorRef>jar-with-dependencies</descriptorRef>\n                        </descriptorRefs>\n                        <archive>\n                            <manifest>\n                                <mainClass>mal.stepA_mal</mainClass>\n                            </manifest>\n                        </archive>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "impls/java/run",
    "content": "#!/usr/bin/env bash\nargs=\"\"\nif [ \"$#\" -gt 0 ]; then\n    args=\"-Dexec.args='$1'\"\n    for a in \"${@:2}\"; do\n        args=\"$args '$a'\"\n    done\nfi\nexec mvn -quiet -e exec:java -Dexec.mainClass=\"mal.${STEP:-stepA_mal}\" ${args:+\"$args\"}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/core.java",
    "content": "package mal;\n\nimport java.util.List;\nimport java.util.ArrayList;\nimport java.util.Set;\nimport java.util.Map;\nimport java.util.HashMap;\nimport com.google.common.collect.ImmutableMap;\n\nimport java.io.IOException;\nimport java.io.FileNotFoundException;\nimport java.util.Scanner;\nimport java.io.File;\n\nimport mal.types.*;\nimport mal.printer;\nimport mal.readline;\n\npublic class core {\n    // Local references for convenience\n    static MalConstant Nil = mal.types.Nil;\n    static MalConstant True = mal.types.True;\n    static MalConstant False = mal.types.False;\n\n\n    // Errors/Exceptions\n    static MalFunction mal_throw = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            throw new MalException(a.nth(0));\n        }\n    };\n\n\n    // Scalar functions\n    static MalFunction nil_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return args.nth(0) == Nil ? True : False;\n        }\n    };\n\n    static MalFunction true_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return args.nth(0) == True ? True : False;\n        }\n    };\n\n    static MalFunction false_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return args.nth(0) == False ? True : False;\n        }\n    };\n    static MalFunction number_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return args.nth(0) instanceof MalInteger ? True : False;\n        }\n    };\n    static MalFunction string_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            if (!(args.nth(0) instanceof MalString)) { return False; }\n            String s = ((MalString)args.nth(0)).getValue();\n            if (s.length() != 0 && s.charAt(0) == '\\u029e') { return False; }\n            return True;\n        }\n    };\n\n    static MalFunction symbol = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return new MalSymbol((MalString)args.nth(0));\n        }\n    };\n    static MalFunction symbol_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return args.nth(0) instanceof MalSymbol ? True : False;\n        }\n    };\n    static MalFunction keyword = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            if (args.nth(0) instanceof MalString &&\n                (((MalString)args.nth(0)).getValue().charAt(0) == '\\u029e')) {\n                return (MalString)args.nth(0);\n            } else {\n                return new MalString(\n                        \"\\u029e\" + ((MalString)args.nth(0)).getValue());\n            }\n        }\n    };\n    static MalFunction keyword_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            if (!(args.nth(0) instanceof MalString)) { return False; }\n            String s = ((MalString)args.nth(0)).getValue();\n            if (s.length() == 0 || s.charAt(0) != '\\u029e') { return False; }\n            return True;\n        }\n    };\n    static MalFunction fn_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            if (!(args.nth(0) instanceof MalFunction)) { return False; }\n            return ((MalFunction)args.nth(0)).isMacro() ? False : True;\n        }\n    };\n    static MalFunction macro_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            if (!(args.nth(0) instanceof MalFunction)) { return False; }\n            return ((MalFunction)args.nth(0)).isMacro() ? True : False;\n        }\n    };\n\n\n    // String functions\n    static MalFunction pr_str = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return new MalString(printer._pr_str_args(args, \" \", true));\n        }\n    };\n\n    static MalFunction str = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return new MalString(printer._pr_str_args(args, \"\", false));\n        }\n    };\n\n    static MalFunction prn = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            System.out.println(printer._pr_str_args(args, \" \", true));\n            return Nil;\n        }\n    };\n\n    static MalFunction println = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            System.out.println(printer._pr_str_args(args, \" \", false));\n            return Nil;\n        }\n    };\n\n\n    static MalFunction equal_Q = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return types._equal_Q(args.nth(0), args.nth(1)) ? True : False;\n        }\n    };\n\n    static MalFunction mal_readline = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            String prompt = ((MalString)args.nth(0)).getValue();\n            try {\n                return new MalString(readline.readline(prompt));\n            } catch (IOException e) {\n                throw new MalException(new MalString(e.getMessage()));\n            } catch (readline.EOFException e) {\n                return Nil;\n            }\n        }\n    };\n\n    static MalFunction read_string = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            try {\n                return reader.read_str(((MalString)args.nth(0)).getValue());\n            } catch (MalContinue c) {\n                return types.Nil;\n            }\n        }\n    };\n\n    static MalFunction slurp = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            String fname = ((MalString)args.nth(0)).getValue();\n            try {\n                // Scanner drops final newline, so add it back\n                return new MalString(\n                    new Scanner(new File(fname)).useDelimiter(\"\\\\Z\").next()\n                                + \"\\n\");\n            } catch (FileNotFoundException e) {\n                throw new MalError(e.getMessage());\n            }\n        }\n    };\n\n\n    // Number functions\n    static MalFunction add = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction subtract = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction multiply = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction divide = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1));\n        }\n    };\n\n    static MalFunction lt = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).lt((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction lte = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).lte((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction gt = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).gt((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction gte = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).gte((MalInteger)a.nth(1));\n        }\n    };\n\n    static MalFunction time_ms = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return new MalInteger((int)System.currentTimeMillis());\n        }\n    };\n\n\n    // List functions\n    static MalFunction new_list = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return new MalList(a.value);\n        }\n    };\n\n    static public Boolean _list_Q(MalVal mv) {\n        return mv.getClass().equals(MalList.class);\n    }\n    static MalFunction list_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return _list_Q(a.nth(0)) ? True : False;\n        }\n    };\n\n\n    // Vector functions\n    static MalFunction new_vector = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return new MalVector(a.value);\n        }\n    };\n\n    static public Boolean _vector_Q(MalVal mv) {\n        return mv.getClass().equals(MalVector.class);\n    }\n    static MalFunction vector_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return _vector_Q(a.nth(0)) ? True : False;\n        }\n    };\n\n    // HashMap functions\n    static MalFunction new_hash_map = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return new MalHashMap(a);\n        }\n    };\n    static MalFunction hash_map_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return a.nth(0) instanceof MalHashMap ? True : False;\n        }\n    };\n\n    static MalFunction contains_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            String key = ((MalString)a.nth(1)).getValue();\n            MalHashMap mhm = (MalHashMap)a.nth(0);\n            HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n            return hm.containsKey(key) ? True : False;\n        }\n    };\n\n    static MalFunction assoc = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalHashMap mhm = (MalHashMap)a.nth(0);\n            HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n            MalHashMap new_mhm = new MalHashMap((Map)hm.clone());\n            new_mhm.assoc_BANG((MalList)a.slice(1));\n            return new_mhm;\n        }\n    };\n\n    static MalFunction dissoc = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalHashMap mhm = (MalHashMap)a.nth(0);\n            HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n            MalHashMap new_mhm = new MalHashMap((Map)hm.clone());\n            new_mhm.dissoc_BANG((MalList)a.slice(1));\n            return new_mhm;\n        }\n    };\n\n    static MalFunction get = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            if (a.nth(0) == Nil) {\n                return Nil;\n            } else {\n                String key = ((MalString)a.nth(1)).getValue();\n                MalHashMap mhm = (MalHashMap)a.nth(0);\n                HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n                if (hm.containsKey(key)) {\n                    return hm.get(key);\n                } else {\n                    return Nil;\n                }\n            }\n        }\n    };\n\n    static MalFunction keys = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalHashMap mhm = (MalHashMap)a.nth(0);\n            HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n            MalList key_lst = new MalList();\n            for (String key : hm.keySet()) {\n                key_lst.conj_BANG(new MalString(key));\n            }\n            return key_lst;\n        }\n    };\n\n    static MalFunction vals = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalHashMap mhm = (MalHashMap)a.nth(0);\n            HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n            //return new ArrayList<MalVal>(((HashMap<String,MalVal>)hm).values());\n            MalList val_lst = new MalList();\n            for (MalVal val : hm.values()) {\n                val_lst.conj_BANG(val);\n            }\n            return val_lst;\n        }\n    };\n\n\n    // Sequence functions\n    static MalFunction sequential_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return a.nth(0) instanceof MalList ? True : False;\n        }\n    };\n\n    static MalFunction count = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            if (a.nth(0) == Nil) {\n                return new MalInteger(0);\n            } else {\n                return new MalInteger(((MalList)a.nth(0)).size());\n            }\n        }\n    };\n\n    static MalFunction empty_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalVal exp = a.nth(0);\n            if (exp == Nil || (exp instanceof MalList &&\n                               ((MalList)exp).size() == 0)) {\n                return True;\n            } else {\n                return False;\n            }\n        }\n    };\n\n    static MalFunction cons = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            List<MalVal> lst = new ArrayList<MalVal>();\n            lst.add(a.nth(0));\n            lst.addAll(((MalList)a.nth(1)).getList());\n            return (MalVal)new MalList(lst);\n        }\n    };\n\n    static MalFunction concat = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            if (a.size() == 0) { return new MalList(); }\n            List<MalVal> lst = new ArrayList<MalVal>();\n            lst.addAll(((MalList)a.nth(0)).value);\n            for(Integer i=1; i<a.size(); i++) {\n                lst.addAll(((MalList)a.nth(i)).value);\n            }\n            return (MalVal)new MalList(lst);\n        }\n    };\n\n    static MalFunction vec = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return new MalVector(((MalList)a.nth(0)).getList());\n        }\n    };\n\n    static MalFunction first = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalVal exp = a.nth(0);\n            if (exp == Nil) {\n                return Nil;\n            }\n            MalList ml = ((MalList)exp);\n            return ml.size() > 0 ? ml.nth(0) : Nil;\n        }\n    };\n\n    static MalFunction rest = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalVal exp = a.nth(0);\n            if (exp == Nil) {\n                return new MalList();\n            }\n            MalList ml = ((MalList)exp);\n            return ml.rest();\n        }\n    };\n\n    static MalFunction nth = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            Integer idx = ((MalInteger)a.nth(1)).getValue();\n            if (idx < ((MalList)a.nth(0)).size()) {\n                return ((MalList)a.nth(0)).nth(idx);\n            } else {\n                throw new MalError(\"nth: index out of range\");\n            }\n        }\n    };\n\n    // General sequence functions\n    static MalFunction apply = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalFunction f = (MalFunction)a.nth(0);\n            MalList args = a.slice(1,a.size()-1);\n            args.value.addAll( ((MalList)a.nth(a.size()-1)).value);\n            return f.apply(args);\n        }\n    };\n\n    static MalFunction map = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalFunction f = (MalFunction) a.nth(0);\n            MalList src_lst = (MalList) a.nth(1);\n            MalList new_lst = new MalList();\n            for(Integer i=0; i<src_lst.size(); i++) {\n                new_lst.value.add(\n                        f.apply(new MalList(src_lst.nth(i))));\n            }\n            return new_lst;\n        }\n    };\n\n    static MalFunction conj = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalList src_seq = (MalList)a.nth(0), new_seq;\n            if (a.nth(0) instanceof MalVector) {\n                new_seq = new MalVector();\n                new_seq.value.addAll(src_seq.value);\n                for(Integer i=1; i<a.size(); i++) {\n                    new_seq.value.add(a.nth(i));\n                }\n            } else {\n                new_seq = new MalList();\n                new_seq.value.addAll(src_seq.value);\n                for(Integer i=1; i<a.size(); i++) {\n                    new_seq.value.add(0, a.nth(i));\n                }\n            }\n            return (MalVal) new_seq;\n        }\n    };\n\n    static MalFunction seq = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalVal mv = (MalVal)a.nth(0);\n            if (mv instanceof MalVector) {\n                if (((MalVector)mv).size() == 0) { return Nil; }\n                return new MalList(((MalVector)mv).getList());\n            } else if (mv instanceof MalList) {\n                if (((MalList)mv).size() == 0) { return Nil; }\n                return mv;\n            } else if (mv instanceof MalString) {\n                String s = ((MalString)mv).getValue();\n                if (s.length() == 0) { return Nil; }\n                List<MalVal> lst = new ArrayList<MalVal>();\n                for (String c : s.split(\"(?!^)\")) {\n                    lst.add(new MalString(c));\n                }\n                return new MalList(lst);\n            } else if (mv == Nil) {\n                return Nil;\n            } else {\n                throw new MalError(\"seq: called on non-sequence\");\n            }\n        }\n    };\n\n\n    // Metadata functions\n\n    static MalFunction meta = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            return args.nth(0).getMeta();\n        }\n    };\n\n    static MalFunction with_meta = new MalFunction() {\n        public MalVal apply(MalList args) throws MalThrowable {\n            MalVal new_mv = ((MalVal)args.nth(0)).copy();\n            new_mv.setMeta(args.nth(1));\n            return new_mv;\n        }\n    };\n\n\n    // Atom functions\n    static MalFunction new_atom = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return new MalAtom(a.nth(0));\n        }\n    };\n\n    static MalFunction atom_Q = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return a.nth(0) instanceof MalAtom ? True : False;\n        }\n    };\n\n    static MalFunction deref = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalAtom)a.nth(0)).value;\n        }\n    };\n\n    static MalFunction reset_BANG = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalAtom)a.nth(0)).value = a.nth(1);\n        }\n    };\n\n    static MalFunction swap_BANG = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            MalAtom atm = (MalAtom)a.nth(0);\n            MalFunction f = (MalFunction)a.nth(1);\n            MalList new_args = new MalList();\n            new_args.value.addAll(((MalList)a.slice(2)).value);\n            new_args.value.add(0, atm.value);\n            atm.value = f.apply(new_args);\n            return atm.value;\n        }\n    };\n\n    \n\n\n\n    // types_ns is namespace of type functions\n    static Map<String, MalVal> ns = ImmutableMap.<String, MalVal>builder()\n        .put(\"=\",         equal_Q)\n        .put(\"throw\",     mal_throw)\n        .put(\"nil?\",      nil_Q)\n        .put(\"true?\",     true_Q)\n        .put(\"false?\",    false_Q)\n        .put(\"number?\",   number_Q)\n        .put(\"string?\",   string_Q)\n        .put(\"symbol\",    symbol)\n        .put(\"symbol?\",   symbol_Q)\n        .put(\"keyword\",   keyword)\n        .put(\"keyword?\",  keyword_Q)\n        .put(\"fn?\",       fn_Q)\n        .put(\"macro?\",    macro_Q)\n\n        .put(\"pr-str\",    pr_str)\n        .put(\"str\",       str)\n        .put(\"prn\",       prn)\n        .put(\"println\",   println)\n        .put(\"readline\",  mal_readline)\n        .put(\"read-string\", read_string)\n        .put(\"slurp\",     slurp)\n        .put(\"<\",         lt)\n        .put(\"<=\",        lte)\n        .put(\">\",         gt)\n        .put(\">=\",        gte)\n        .put(\"+\",         add)\n        .put(\"-\",         subtract)\n        .put(\"*\",         multiply)\n        .put(\"/\",         divide)\n        .put(\"time-ms\",   time_ms)\n\n        .put(\"list\",      new_list)\n        .put(\"list?\",     list_Q)\n        .put(\"vector\",    new_vector)\n        .put(\"vector?\",   vector_Q)\n        .put(\"hash-map\",  new_hash_map)\n        .put(\"map?\",      hash_map_Q)\n        .put(\"assoc\",     assoc)\n        .put(\"dissoc\",    dissoc)\n        .put(\"contains?\", contains_Q)\n        .put(\"get\",       get)\n        .put(\"keys\",      keys)\n        .put(\"vals\",      vals)\n\n        .put(\"sequential?\", sequential_Q)\n        .put(\"cons\",      cons)\n        .put(\"concat\",    concat)\n        .put(\"vec\",       vec)\n        .put(\"nth\",       nth)\n        .put(\"first\",     first)\n        .put(\"rest\",      rest)\n        .put(\"empty?\",    empty_Q)\n        .put(\"count\",     count)\n        .put(\"apply\",     apply)\n        .put(\"map\",       map)\n\n        .put(\"conj\",      conj)\n        .put(\"seq\",       seq)\n\n        .put(\"with-meta\", with_meta)\n        .put(\"meta\",      meta)\n        .put(\"atom\",      new_atom)\n        .put(\"atom?\",     atom_Q)\n        .put(\"deref\",     deref)\n        .put(\"reset!\",    reset_BANG)\n        .put(\"swap!\",     swap_BANG)\n        .build();\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/env.java",
    "content": "package mal;\n\nimport java.util.HashMap;\n\nimport mal.types.MalThrowable;\nimport mal.types.MalException;\nimport mal.types.MalVal;\nimport mal.types.MalSymbol;\nimport mal.types.MalList;\n\npublic class env {\n    public static class Env {\n        Env outer = null;\n        HashMap<String,MalVal> data = new HashMap<String,MalVal>();\n\n        public Env(Env outer) {\n            this.outer = outer;\n        }\n        public Env(Env outer, MalList binds, MalList exprs) {\n            this.outer = outer;\n            for (Integer i=0; i<binds.size(); i++) {\n                String sym = ((MalSymbol)binds.nth(i)).getName();\n                if (sym.equals(\"&\")) {\n                    data.put(((MalSymbol)binds.nth(i+1)).getName(),\n                            exprs.slice(i));\n                    break;\n                } else {\n                    data.put(sym, exprs.nth(i));\n                }\n            }\n        }\n        \n        public MalVal get(String key) {\n            MalVal res = data.get(key);\n            if (res != null) {\n                return res;\n            } else if (outer != null) {\n                return outer.get(key);\n            } else {\n                return null;\n            }\n        }\n\n        public Env set(MalSymbol key, MalVal value) {\n            data.put(key.getName(), value);\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/printer.java",
    "content": "package mal;\n\nimport java.util.List;\nimport java.util.ArrayList;\nimport com.google.common.base.Joiner;\nimport java.util.Map;\nimport org.apache.commons.lang3.StringEscapeUtils;\n\nimport mal.types.MalVal;\nimport mal.types.MalList;\n\npublic class printer {\n\n    public static String join(List<MalVal> value,\n                              String delim, Boolean print_readably) {\n        ArrayList<String> strs = new ArrayList<String>();\n        for (MalVal mv : value) {\n            strs.add(mv.toString(print_readably));\n        }\n        return Joiner.on(delim).join(strs);\n    }\n\n    public static String join(Map<String,MalVal> value,\n                              String delim, Boolean print_readably) {\n        ArrayList<String> strs = new ArrayList<String>();\n        for (Map.Entry<String, MalVal> entry : value.entrySet()) {\n            if (entry.getKey().length() > 0 &&\n                entry.getKey().charAt(0) == '\\u029e') {\n                strs.add(\":\" + entry.getKey().substring(1));\n            } else if (print_readably) {\n                strs.add(\"\\\"\" + entry.getKey().toString() + \"\\\"\");\n            } else {\n                strs.add(entry.getKey().toString());\n            }\n            strs.add(entry.getValue().toString(print_readably));\n        }\n        return Joiner.on(\" \").join(strs);\n    }\n\n    public static String _pr_str(MalVal mv,\n                                 Boolean print_readably) {\n        return mv.toString(print_readably);\n    }\n\n    public static String _pr_str_args(MalList args,\n                                      String sep, Boolean print_readably) {\n        return join(args.getList(), sep, print_readably);\n    }\n\n    public static String escapeString(String value) {\n        return StringEscapeUtils.escapeJava(value);\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/reader.java",
    "content": "package mal;\n\nimport java.util.ArrayList;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport org.apache.commons.lang3.StringEscapeUtils;\nimport mal.types.*;\n\npublic class reader {\n    public static class ParseError extends MalThrowable {\n        public ParseError(String msg) {\n            super(msg);\n        }\n    }\n\n    public static class Reader {\n        ArrayList<String> tokens;\n        Integer position;\n        public Reader(ArrayList<String> t) {\n            tokens = t;\n            position = 0;\n        }\n\n        public String peek() {\n            if (position >= tokens.size()) {\n                return null;\n            } else {\n                return tokens.get(position);\n            }\n        }\n        public String next() {\n            return tokens.get(position++);\n        }\n    }\n\n    public static ArrayList<String> tokenize(String str) {\n        ArrayList<String> tokens = new ArrayList<String>();\n        Pattern pattern = Pattern.compile(\"[\\\\s ,]*(~@|[\\\\[\\\\]{}()'`~@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s \\\\[\\\\]{}()'\\\"`~@,;]*)\");\n        Matcher matcher = pattern.matcher(str);\n        while (matcher.find()) {\n            String token = matcher.group(1);\n            if (token != null &&\n                !token.equals(\"\") &&\n                !(token.charAt(0) == ';')) {\n                tokens.add(token);\n            }\n        }\n        return tokens;\n    }\n\n    public static MalVal read_atom(Reader rdr)\n            throws ParseError {\n        String token = rdr.next();\n        Pattern pattern = Pattern.compile(\"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\\\"((?:[\\\\\\\\].|[^\\\\\\\\\\\"])*)\\\"$|^\\\"(.*)$|:(.*)|(^[^\\\"]*$)\");\n        Matcher matcher = pattern.matcher(token);\n        if (!matcher.find()) {\n            throw new ParseError(\"unrecognized token '\" + token + \"'\");\n        }\n        if (matcher.group(1) != null) {\n            return new MalInteger(Integer.parseInt(matcher.group(1)));\n        } else if (matcher.group(3) != null) {\n            return types.Nil;\n        } else if (matcher.group(4) != null) {\n            return types.True;\n        } else if (matcher.group(5) != null) {\n            return types.False;\n        } else if (matcher.group(6) != null) {\n            return new MalString(StringEscapeUtils.unescapeJava(matcher.group(6)));\n        } else if (matcher.group(7) != null) {\n            throw new ParseError(\"expected '\\\"', got EOF\");\n        } else if (matcher.group(8) != null) {\n            return new MalString(\"\\u029e\" + matcher.group(8));\n        } else if (matcher.group(9) != null) {\n            return new MalSymbol(matcher.group(9));\n        } else {\n            throw new ParseError(\"unrecognized '\" + matcher.group(0) + \"'\");\n        }\n    }\n\n    public static MalVal read_list(Reader rdr, MalList lst, char start, char end)\n            throws MalContinue, ParseError {\n        String token = rdr.next();\n        if (token.charAt(0) != start) {\n            throw new ParseError(\"expected '\" + start + \"'\");\n        }\n\n        while ((token = rdr.peek()) != null && token.charAt(0) != end) {\n            lst.conj_BANG(read_form(rdr));\n        }\n\n        if (token == null) {\n            throw new ParseError(\"expected '\" + end + \"', got EOF\");\n        }\n        rdr.next();\n\n        return lst;\n    }\n\n    public static MalVal read_hash_map(Reader rdr)\n            throws MalContinue, ParseError {\n        MalList lst = (MalList)read_list(rdr, new MalList(), '{', '}');\n        return new MalHashMap(lst);\n    }\n\n    public static MalVal read_form(Reader rdr)\n            throws MalContinue, ParseError {\n        String token = rdr.peek();\n        if (token == null) { throw new MalContinue(); }\n        MalVal form;\n\n        switch (token.charAt(0)) {\n            case '\\'': rdr.next();\n                       return new MalList(new MalSymbol(\"quote\"),\n                                          read_form(rdr));\n            case '`': rdr.next();\n                      return new MalList(new MalSymbol(\"quasiquote\"),\n                                         read_form(rdr));\n            case '~':\n                if (token.equals(\"~\")) {\n                    rdr.next();\n                    return new MalList(new MalSymbol(\"unquote\"),\n                                       read_form(rdr));\n                } else {\n                    rdr.next();\n                    return new MalList(new MalSymbol(\"splice-unquote\"),\n                                       read_form(rdr));\n                }\n            case '^': rdr.next();\n                      MalVal meta = read_form(rdr);\n                      return new MalList(new MalSymbol(\"with-meta\"),\n                                         read_form(rdr),\n                                         meta);\n            case '@': rdr.next();\n                      return new MalList(new MalSymbol(\"deref\"),\n                                         read_form(rdr));\n            case '(': form = read_list(rdr, new MalList(), '(' , ')'); break;\n            case ')': throw new ParseError(\"unexpected ')'\");\n            case '[': form = read_list(rdr, new MalVector(), '[' , ']'); break;\n            case ']': throw new ParseError(\"unexpected ']'\");\n            case '{': form = read_hash_map(rdr); break;\n            case '}': throw new ParseError(\"unexpected '}'\");\n            default:  form = read_atom(rdr);\n        }\n        return form;\n    }\n\n    public static MalVal read_str(String str)\n            throws MalContinue, ParseError {\n        return read_form(new Reader(tokenize(str)));\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/readline.java",
    "content": "package mal;\n\nimport java.io.IOException;\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.BufferedWriter;\nimport java.io.FileWriter;\n\nimport java.io.File;\nimport com.google.common.io.Files;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport com.sun.jna.Library;\nimport com.sun.jna.Native;\nimport com.sun.jna.Platform;\n\nclass readline {\n    public enum Mode { JNA, JAVA }\n    static Mode mode = Mode.JNA;\n\n    static String HISTORY_FILE = null;\n    static Boolean historyLoaded = false;\n\n    static {\n        HISTORY_FILE = System.getProperty(\"user.home\") + \"/.mal-history\";\n    }\n\n    public static class EOFException extends Exception {\n    }\n\n    public interface RLLibrary extends Library {\n        // Select a library to use.\n        // WARNING: GNU readline is GPL.\n\n        // GNU readline (GPL)\n            RLLibrary INSTANCE = (RLLibrary)\n                Native.loadLibrary(\"readline\", RLLibrary.class);\n        // Libedit (BSD)\n//            RLLibrary INSTANCE = (RLLibrary)\n//                Native.loadLibrary(\"edit\", RLLibrary.class);\n\n        String readline(String prompt);\n        void add_history(String line);\n    }\n\n    public static void loadHistory(String filename) {\n        File file = new File(filename);\n        try {\n            List<String> lines = Files.readLines(file,\n                                                StandardCharsets.UTF_8);\n            for (String line : lines) {\n                RLLibrary.INSTANCE.add_history(line);\n            }\n        } catch (IOException e) {\n            // ignore\n        }\n    }\n\n    public static void appendHistory(String filename, String line) {\n        try {\n            BufferedWriter w;\n            w = new BufferedWriter(new FileWriter(filename, true));\n            w.append(line + \"\\n\");\n            w.close();\n        } catch (IOException e) {\n            // ignore\n        }\n    }\n\n    public static String jna_readline(String prompt)\n            throws EOFException, IOException {\n        if (!historyLoaded) {\n            loadHistory(HISTORY_FILE);\n        }\n        String line = RLLibrary.INSTANCE.readline(prompt);\n        if (line == null) {\n            throw new EOFException();\n        }\n        RLLibrary.INSTANCE.add_history(line);\n        appendHistory(HISTORY_FILE, line);\n        return line;\n    }\n\n    // Just java readline (no history, or line editing)\n    public static String java_readline(String prompt)\n            throws EOFException, IOException {\n        System.out.print(prompt);\n        BufferedReader buffer=new BufferedReader(new InputStreamReader(System.in));\n        String line=buffer.readLine();\n        if (line == null) {\n            throw new EOFException();\n        }\n        return line;\n    }\n\n    public static String readline(String prompt)\n            throws EOFException, IOException {\n        if (mode == Mode.JNA) {\n            return jna_readline(prompt);\n        } else {\n            return java_readline(prompt);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step0_repl.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport mal.readline;\n\npublic class step0_repl {\n    // read\n    public static String READ(String str) {\n        return str;\n    }\n\n    // eval\n    public static String EVAL(String ast, String env) {\n        return ast;\n    }\n\n    // print\n    public static String PRINT(String exp) {\n        return exp;\n    }\n\n    // repl\n    public static String RE(String env, String str) {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) {\n        String prompt = \"user> \";\n\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            System.out.println(PRINT(RE(null, line)));\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step1_read_print.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\n\npublic class step1_read_print {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static MalVal EVAL(MalVal ast, String env) {\n        return ast;\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(String env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(null, line)));\n            } catch (MalContinue e) {\n                continue;\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n                continue;\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n                continue;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step2_eval.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.HashMap;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\n\npublic class step2_eval {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static MalVal EVAL(MalVal orig_ast, Map<String, MalVal> env) throws MalThrowable {\n        // System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        final MalVal f = EVAL(ast.nth(0), env);\n        if (!(f instanceof ILambda))\n            throw new MalError(\"cannot apply \" + printer._pr_str(ast, true));\n        final MalList args = new MalList();\n        for (int i=1; i<ast.size(); i++)\n            args.conj_BANG(EVAL(ast.nth(i), env));\n        return ((ILambda)f).apply(args);\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Map<String, MalVal> env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    static MalFunction add = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction subtract = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction multiply = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction divide = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1));\n        }\n    };\n\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        Map<String, MalVal> repl_env = new HashMap<String, MalVal>();\n        repl_env.put(\"+\", add);\n        repl_env.put(\"-\", subtract);\n        repl_env.put(\"*\", multiply);\n        repl_env.put(\"/\", divide);\n\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step3_env.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\n\npublic class step3_env {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        if (!(a0 instanceof MalSymbol)) {\n            throw new MalError(\"attempt to apply on non-symbol '\"\n                    + printer._pr_str(a0,true) + \"'\");\n        }\n\n        switch (((MalSymbol)a0).getName()) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            return EVAL(a2, let_env);\n        default:\n            final ILambda f = (ILambda)EVAL(a0, env);\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            return f.apply(args);\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    static MalFunction add = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).add((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction subtract = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).subtract((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction multiply = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).multiply((MalInteger)a.nth(1));\n        }\n    };\n    static MalFunction divide = new MalFunction() {\n        public MalVal apply(MalList a) throws MalThrowable {\n            return ((MalInteger)a.nth(0)).divide((MalInteger)a.nth(1));\n        }\n    };\n\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        Env repl_env = new Env(null);\n        repl_env.set(new MalSymbol(\"+\"), add);\n        repl_env.set(new MalSymbol(\"-\"), subtract);\n        repl_env.set(new MalSymbol(\"*\"), multiply);\n        repl_env.set(new MalSymbol(\"/\"), divide);\n\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step4_if_fn_do.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class step4_if_fn_do {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            return EVAL(a2, let_env);\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            return EVAL(ast.nth(ast.size() - 1), env);\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    a3 = ast.nth(3);\n                    return EVAL(a3, env);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                a2 = ast.nth(2);\n                return EVAL(a2, env);\n            }\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction () {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            return f.apply(args);\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        \n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step5_tco.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class step5_tco {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        while (true) {\n\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            orig_ast = a2;\n            env = let_env;\n            break;\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            orig_ast = ast.nth(ast.size()-1);\n            break;\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    orig_ast = ast.nth(3);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                orig_ast = ast.nth(2);\n            }\n            break;\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction (a2f, (mal.env.Env)env, a1f) {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            MalVal fnast = f.getAst();\n            if (fnast != null) {\n                orig_ast = fnast;\n                env = f.genEnv(args);\n            } else {\n                return f.apply(args);\n            }\n        }\n\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        \n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step6_file.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class step6_file {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        while (true) {\n\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            orig_ast = a2;\n            env = let_env;\n            break;\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            orig_ast = ast.nth(ast.size()-1);\n            break;\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    orig_ast = ast.nth(3);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                orig_ast = ast.nth(2);\n            }\n            break;\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction (a2f, (mal.env.Env)env, a1f) {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            MalVal fnast = f.getAst();\n            if (fnast != null) {\n                orig_ast = fnast;\n                env = f.genEnv(args);\n            } else {\n                return f.apply(args);\n            }\n        }\n\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        final Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n        repl_env.set(new MalSymbol(\"eval\"), new MalFunction() {\n            public MalVal apply(MalList args) throws MalThrowable {\n                return EVAL(args.nth(0), repl_env);\n            }\n        });\n        MalList _argv = new MalList();\n        for (Integer i=1; i < args.length; i++) {\n            _argv.conj_BANG(new MalString(args[i]));\n        }\n        repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        RE(repl_env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        \n        Integer fileIdx = 0;\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n            fileIdx = 1;\n        }\n        if (args.length > fileIdx) {\n            RE(repl_env, \"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n            return;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step7_quote.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class step7_quote {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static Boolean starts_with(MalVal ast, String sym) {\n        //  Liskov, forgive me\n        if (ast instanceof MalList && !(ast instanceof MalVector) && ((MalList)ast).size() == 2) {\n            MalVal a0 = ((MalList)ast).nth(0);\n            return a0 instanceof MalSymbol && ((MalSymbol)a0).getName().equals(sym);\n        }\n        return false;\n    }\n\n    public static MalVal quasiquote(MalVal ast) {\n        if ((ast instanceof MalSymbol || ast instanceof MalHashMap))\n            return new MalList(new MalSymbol(\"quote\"), ast);\n\n        if (!(ast instanceof MalList))\n            return ast;\n\n        if (starts_with(ast, \"unquote\"))\n            return ((MalList)ast).nth(1);\n\n        MalVal res = new MalList();\n        for (Integer i=((MalList)ast).size()-1; 0<=i; i--) {\n            MalVal elt = ((MalList)ast).nth(i);\n            if (starts_with(elt, \"splice-unquote\"))\n                res = new MalList(new MalSymbol(\"concat\"), ((MalList)elt).nth(1), res);\n            else\n                res = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), res);\n        }\n        if (ast instanceof MalVector)\n            res = new MalList(new MalSymbol(\"vec\"), res);\n        return res;\n    }\n\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        while (true) {\n\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            orig_ast = a2;\n            env = let_env;\n            break;\n        case \"quote\":\n            return ast.nth(1);\n        case \"quasiquote\":\n            orig_ast = quasiquote(ast.nth(1));\n            break;\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            orig_ast = ast.nth(ast.size()-1);\n            break;\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    orig_ast = ast.nth(3);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                orig_ast = ast.nth(2);\n            }\n            break;\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction (a2f, (mal.env.Env)env, a1f) {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            MalVal fnast = f.getAst();\n            if (fnast != null) {\n                orig_ast = fnast;\n                env = f.genEnv(args);\n            } else {\n                return f.apply(args);\n            }\n        }\n\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        final Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n        repl_env.set(new MalSymbol(\"eval\"), new MalFunction() {\n            public MalVal apply(MalList args) throws MalThrowable {\n                return EVAL(args.nth(0), repl_env);\n            }\n        });\n        MalList _argv = new MalList();\n        for (Integer i=1; i < args.length; i++) {\n            _argv.conj_BANG(new MalString(args[i]));\n        }\n        repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        RE(repl_env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        \n        Integer fileIdx = 0;\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n            fileIdx = 1;\n        }\n        if (args.length > fileIdx) {\n            RE(repl_env, \"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n            return;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step8_macros.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class step8_macros {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static Boolean starts_with(MalVal ast, String sym) {\n        //  Liskov, forgive me\n        if (ast instanceof MalList && !(ast instanceof MalVector) && ((MalList)ast).size() == 2) {\n            MalVal a0 = ((MalList)ast).nth(0);\n            return a0 instanceof MalSymbol && ((MalSymbol)a0).getName().equals(sym);\n        }\n        return false;\n    }\n\n    public static MalVal quasiquote(MalVal ast) {\n        if ((ast instanceof MalSymbol || ast instanceof MalHashMap))\n            return new MalList(new MalSymbol(\"quote\"), ast);\n\n        if (!(ast instanceof MalList))\n            return ast;\n\n        if (starts_with(ast, \"unquote\"))\n            return ((MalList)ast).nth(1);\n\n        MalVal res = new MalList();\n        for (Integer i=((MalList)ast).size()-1; 0<=i; i--) {\n            MalVal elt = ((MalList)ast).nth(i);\n            if (starts_with(elt, \"splice-unquote\"))\n                res = new MalList(new MalSymbol(\"concat\"), ((MalList)elt).nth(1), res);\n            else\n                res = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), res);\n        }\n        if (ast instanceof MalVector)\n            res = new MalList(new MalSymbol(\"vec\"), res);\n        return res;\n    }\n\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        while (true) {\n\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            orig_ast = a2;\n            env = let_env;\n            break;\n        case \"quote\":\n            return ast.nth(1);\n        case \"quasiquote\":\n            orig_ast = quasiquote(ast.nth(1));\n            break;\n        case \"defmacro!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            res = res.copy();\n            ((MalFunction)res).setMacro();\n            env.set((MalSymbol)a1, res);\n            return res;\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            orig_ast = ast.nth(ast.size()-1);\n            break;\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    orig_ast = ast.nth(3);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                orig_ast = ast.nth(2);\n            }\n            break;\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction (a2f, (mal.env.Env)env, a1f) {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            if (f.isMacro()) {\n                orig_ast = f.apply(ast.rest());\n                continue;\n            }\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            MalVal fnast = f.getAst();\n            if (fnast != null) {\n                orig_ast = fnast;\n                env = f.genEnv(args);\n            } else {\n                return f.apply(args);\n            }\n        }\n\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        final Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n        repl_env.set(new MalSymbol(\"eval\"), new MalFunction() {\n            public MalVal apply(MalList args) throws MalThrowable {\n                return EVAL(args.nth(0), repl_env);\n            }\n        });\n        MalList _argv = new MalList();\n        for (Integer i=1; i < args.length; i++) {\n            _argv.conj_BANG(new MalString(args[i]));\n        }\n        repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        RE(repl_env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        RE(repl_env, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        \n        Integer fileIdx = 0;\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n            fileIdx = 1;\n        }\n        if (args.length > fileIdx) {\n            RE(repl_env, \"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n            return;\n        }\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/step9_try.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.io.StringWriter;\nimport java.io.PrintWriter;\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class step9_try {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static Boolean starts_with(MalVal ast, String sym) {\n        //  Liskov, forgive me\n        if (ast instanceof MalList && !(ast instanceof MalVector) && ((MalList)ast).size() == 2) {\n            MalVal a0 = ((MalList)ast).nth(0);\n            return a0 instanceof MalSymbol && ((MalSymbol)a0).getName().equals(sym);\n        }\n        return false;\n    }\n\n    public static MalVal quasiquote(MalVal ast) {\n        if ((ast instanceof MalSymbol || ast instanceof MalHashMap))\n            return new MalList(new MalSymbol(\"quote\"), ast);\n\n        if (!(ast instanceof MalList))\n            return ast;\n\n        if (starts_with(ast, \"unquote\"))\n            return ((MalList)ast).nth(1);\n\n        MalVal res = new MalList();\n        for (Integer i=((MalList)ast).size()-1; 0<=i; i--) {\n            MalVal elt = ((MalList)ast).nth(i);\n            if (starts_with(elt, \"splice-unquote\"))\n                res = new MalList(new MalSymbol(\"concat\"), ((MalList)elt).nth(1), res);\n            else\n                res = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), res);\n        }\n        if (ast instanceof MalVector)\n            res = new MalList(new MalSymbol(\"vec\"), res);\n        return res;\n    }\n\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        while (true) {\n\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            orig_ast = a2;\n            env = let_env;\n            break;\n        case \"quote\":\n            return ast.nth(1);\n        case \"quasiquote\":\n            orig_ast = quasiquote(ast.nth(1));\n            break;\n        case \"defmacro!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            res = res.copy();\n            ((MalFunction)res).setMacro();\n            env.set((MalSymbol)a1, res);\n            return res;\n        case \"try*\":\n            try {\n                return EVAL(ast.nth(1), env);\n            } catch (Throwable t) {\n                if (ast.size() > 2) {\n                    MalVal exc;\n                    a2 = ast.nth(2);\n                    MalVal a20 = ((MalList)a2).nth(0);\n                    if (((MalSymbol)a20).getName().equals(\"catch*\")) {\n                        if (t instanceof MalException) {\n                            exc = ((MalException)t).getValue();\n                        } else {\n                            StringWriter sw = new StringWriter();\n                            t.printStackTrace(new PrintWriter(sw));\n                            String tstr = sw.toString();\n                            exc = new MalString(t.getMessage() + \": \" + tstr);\n                        }\n                        return EVAL(((MalList)a2).nth(2),\n                                    new Env(env, ((MalList)a2).slice(1,2),\n                                                 new MalList(exc)));\n                    }\n                }\n                throw t;\n            }\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            orig_ast = ast.nth(ast.size()-1);\n            break;\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    orig_ast = ast.nth(3);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                orig_ast = ast.nth(2);\n            }\n            break;\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction (a2f, (mal.env.Env)env, a1f) {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            if (f.isMacro()) {\n                orig_ast = f.apply(ast.rest());\n                continue;\n            }\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            MalVal fnast = f.getAst();\n            if (fnast != null) {\n                orig_ast = fnast;\n                env = f.genEnv(args);\n            } else {\n                return f.apply(args);\n            }\n        }\n\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        final Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n        repl_env.set(new MalSymbol(\"eval\"), new MalFunction() {\n            public MalVal apply(MalList args) throws MalThrowable {\n                return EVAL(args.nth(0), repl_env);\n            }\n        });\n        MalList _argv = new MalList();\n        for (Integer i=1; i < args.length; i++) {\n            _argv.conj_BANG(new MalString(args[i]));\n        }\n        repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        RE(repl_env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        RE(repl_env, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        \n        Integer fileIdx = 0;\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n            fileIdx = 1;\n        }\n        if (args.length > fileIdx) {\n            RE(repl_env, \"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n            return;\n        }\n\n        // repl loop\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/stepA_mal.java",
    "content": "package mal;\n\nimport java.io.IOException;\n\nimport java.io.StringWriter;\nimport java.io.PrintWriter;\nimport java.util.List;\nimport java.util.Map;\nimport mal.types.*;\nimport mal.readline;\nimport mal.reader;\nimport mal.printer;\nimport mal.env.Env;\nimport mal.core;\n\npublic class stepA_mal {\n    // read\n    public static MalVal READ(String str) throws MalThrowable {\n        return reader.read_str(str);\n    }\n\n    // eval\n    public static Boolean starts_with(MalVal ast, String sym) {\n        //  Liskov, forgive me\n        if (ast instanceof MalList && !(ast instanceof MalVector) && ((MalList)ast).size() == 2) {\n            MalVal a0 = ((MalList)ast).nth(0);\n            return a0 instanceof MalSymbol && ((MalSymbol)a0).getName().equals(sym);\n        }\n        return false;\n    }\n\n    public static MalVal quasiquote(MalVal ast) {\n        if ((ast instanceof MalSymbol || ast instanceof MalHashMap))\n            return new MalList(new MalSymbol(\"quote\"), ast);\n\n        if (!(ast instanceof MalList))\n            return ast;\n\n        if (starts_with(ast, \"unquote\"))\n            return ((MalList)ast).nth(1);\n\n        MalVal res = new MalList();\n        for (Integer i=((MalList)ast).size()-1; 0<=i; i--) {\n            MalVal elt = ((MalList)ast).nth(i);\n            if (starts_with(elt, \"splice-unquote\"))\n                res = new MalList(new MalSymbol(\"concat\"), ((MalList)elt).nth(1), res);\n            else\n                res = new MalList(new MalSymbol(\"cons\"), quasiquote(elt), res);\n        }\n        if (ast instanceof MalVector)\n            res = new MalList(new MalSymbol(\"vec\"), res);\n        return res;\n    }\n\n    public static MalVal EVAL(MalVal orig_ast, Env env) throws MalThrowable {\n        while (true) {\n\n        final MalVal dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval != null && dbgeval != types.Nil && dbgeval != types.False)\n            System.out.println(\"EVAL: \" + printer._pr_str(orig_ast, true));\n\n        if (orig_ast instanceof MalSymbol) {\n            final String key = ((MalSymbol)orig_ast).getName();\n            final MalVal val = env.get(key);\n            if (val == null)\n                throw new MalException(\"'\" + key + \"' not found\");\n            return val;\n        } else if (orig_ast instanceof MalVector) {\n            final MalList old_lst = (MalList)orig_ast;\n            final MalVector new_lst = new MalVector();\n            for (MalVal mv : (List<MalVal>)old_lst.value) {\n                new_lst.conj_BANG(EVAL(mv, env));\n            }\n            return new_lst;\n        } else if (orig_ast instanceof MalHashMap) {\n            final Map<String, MalVal> old_hm = ((MalHashMap)orig_ast).value;\n            MalHashMap new_hm = new MalHashMap();\n            for (Map.Entry<String, MalVal> entry : old_hm.entrySet()) {\n                new_hm.value.put(entry.getKey(), EVAL((MalVal)entry.getValue(), env));\n            }\n            return new_hm;\n        } else if (!orig_ast.list_Q()) {\n            return orig_ast;\n        }\n        final MalList ast = (MalList)orig_ast;\n        MalVal a0, a1,a2, a3, res;\n        // apply list\n        if (ast.size() == 0) { return ast; }\n        a0 = ast.nth(0);\n        String a0sym = a0 instanceof MalSymbol ? ((MalSymbol)a0).getName()\n                                               : \"__<*fn*>__\";\n        switch (a0sym) {\n        case \"def!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            env.set(((MalSymbol)a1), res);\n            return res;\n        case \"let*\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            MalSymbol key;\n            MalVal val;\n            Env let_env = new Env(env);\n            for(int i=0; i<((MalList)a1).size(); i+=2) {\n                key = (MalSymbol)((MalList)a1).nth(i);\n                val = ((MalList)a1).nth(i+1);\n                let_env.set(key, EVAL(val, let_env));\n            }\n            orig_ast = a2;\n            env = let_env;\n            break;\n        case \"quote\":\n            return ast.nth(1);\n        case \"quasiquote\":\n            orig_ast = quasiquote(ast.nth(1));\n            break;\n        case \"defmacro!\":\n            a1 = ast.nth(1);\n            a2 = ast.nth(2);\n            res = EVAL(a2, env);\n            res = res.copy();\n            ((MalFunction)res).setMacro();\n            env.set((MalSymbol)a1, res);\n            return res;\n        case \"try*\":\n            try {\n                return EVAL(ast.nth(1), env);\n            } catch (Throwable t) {\n                if (ast.size() > 2) {\n                    MalVal exc;\n                    a2 = ast.nth(2);\n                    MalVal a20 = ((MalList)a2).nth(0);\n                    if (((MalSymbol)a20).getName().equals(\"catch*\")) {\n                        if (t instanceof MalException) {\n                            exc = ((MalException)t).getValue();\n                        } else {\n                            StringWriter sw = new StringWriter();\n                            t.printStackTrace(new PrintWriter(sw));\n                            String tstr = sw.toString();\n                            exc = new MalString(t.getMessage() + \": \" + tstr);\n                        }\n                        return EVAL(((MalList)a2).nth(2),\n                                    new Env(env, ((MalList)a2).slice(1,2),\n                                                 new MalList(exc)));\n                    }\n                }\n                throw t;\n            }\n        case \"do\":\n            for (int i=1; i<ast.size()-1; i++)\n                EVAL(ast.nth(i), env);\n            orig_ast = ast.nth(ast.size()-1);\n            break;\n        case \"if\":\n            a1 = ast.nth(1);\n            MalVal cond = EVAL(a1, env);\n            if (cond == types.Nil || cond == types.False) {\n                // eval false slot form\n                if (ast.size() > 3) {\n                    orig_ast = ast.nth(3);\n                } else {\n                    return types.Nil;\n                }\n            } else {\n                // eval true slot form\n                orig_ast = ast.nth(2);\n            }\n            break;\n        case \"fn*\":\n            final MalList a1f = (MalList)ast.nth(1);\n            final MalVal a2f = ast.nth(2);\n            final Env cur_env = env;\n            return new MalFunction (a2f, (mal.env.Env)env, a1f) {\n                public MalVal apply(MalList args) throws MalThrowable {\n                    return EVAL(a2f, new Env(cur_env, a1f, args));\n                }\n            };\n        default:\n            final MalFunction f = (MalFunction)EVAL(a0, env);\n            if (f.isMacro()) {\n                orig_ast = f.apply(ast.rest());\n                continue;\n            }\n            final MalList args = new MalList();\n            for (int i=1; i<ast.size(); i++)\n                args.conj_BANG(EVAL(ast.nth(i), env));\n            MalVal fnast = f.getAst();\n            if (fnast != null) {\n                orig_ast = fnast;\n                env = f.genEnv(args);\n            } else {\n                return f.apply(args);\n            }\n        }\n\n        }\n    }\n\n    // print\n    public static String PRINT(MalVal exp) {\n        return printer._pr_str(exp, true);\n    }\n\n    // repl\n    public static MalVal RE(Env env, String str) throws MalThrowable {\n        return EVAL(READ(str), env);\n    }\n\n    public static void main(String[] args) throws MalThrowable {\n        String prompt = \"user> \";\n\n        final Env repl_env = new Env(null);\n\n        // core.java: defined using Java\n        for (String key : core.ns.keySet()) {\n            repl_env.set(new MalSymbol(key), core.ns.get(key));\n        }\n        repl_env.set(new MalSymbol(\"eval\"), new MalFunction() {\n            public MalVal apply(MalList args) throws MalThrowable {\n                return EVAL(args.nth(0), repl_env);\n            }\n        });\n        MalList _argv = new MalList();\n        for (Integer i=1; i < args.length; i++) {\n            _argv.conj_BANG(new MalString(args[i]));\n        }\n        repl_env.set(new MalSymbol(\"*ARGV*\"), _argv);\n\n\n        // core.mal: defined using the language itself\n        RE(repl_env, \"(def! *host-language* \\\"java\\\")\");\n        RE(repl_env, \"(def! not (fn* (a) (if a false true)))\");\n        RE(repl_env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        RE(repl_env, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        \n        Integer fileIdx = 0;\n        if (args.length > 0 && args[0].equals(\"--raw\")) {\n            readline.mode = readline.Mode.JAVA;\n            fileIdx = 1;\n        }\n        if (args.length > fileIdx) {\n            RE(repl_env, \"(load-file \\\"\" + args[fileIdx] + \"\\\")\");\n            return;\n        }\n\n        // repl loop\n        RE(repl_env, \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n        while (true) {\n            String line;\n            try {\n                line = readline.readline(prompt);\n                if (line == null) { continue; }\n            } catch (readline.EOFException e) {\n                break;\n            } catch (IOException e) {\n                System.out.println(\"IOException: \" + e.getMessage());\n                break;\n            }\n            try {\n                System.out.println(PRINT(RE(repl_env, line)));\n            } catch (MalContinue e) {\n            } catch (MalException e) {\n                System.out.println(\"Error: \" + printer._pr_str(e.getValue(), false));\n            } catch (MalThrowable t) {\n                System.out.println(\"Error: \" + t.getMessage());\n            } catch (Throwable t) {\n                System.out.println(\"Uncaught \" + t + \": \" + t.getMessage());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java/src/main/java/mal/types.java",
    "content": "package mal;\n\nimport java.util.List;\nimport java.util.ArrayList;\nimport java.util.Set;\nimport java.util.Map;\nimport java.util.HashMap;\n\nimport mal.printer;\nimport mal.env.Env;\n\npublic class types {\n    //\n    // Exceptions/Errors\n    //\n    public static class MalThrowable extends Exception {\n        public MalThrowable() { }\n        public MalThrowable(String msg) { super(msg); }\n    }\n    public static class MalError extends MalThrowable {\n        public MalError(String msg) { super(msg); }\n    }\n    public static class MalContinue extends MalThrowable { }\n\n    // Thrown by throw function\n    public static class MalException extends MalThrowable {\n        MalVal value;\n        public MalException(MalVal value) {\n            this.value = value;\n        }\n        public MalException(String value) {\n            this.value = new MalString(value);\n        }\n        public MalVal getValue() { return value; }\n    }\n\n    //\n    // General functions\n    //\n\n    public static Boolean _equal_Q(MalVal a, MalVal b) {\n        Class ota = a.getClass(), otb = b.getClass();\n        if (!((ota == otb) ||\n              (a instanceof MalList && b instanceof MalList))) {\n            return false;\n        } else {\n            if (a instanceof MalInteger) {\n                return ((MalInteger)a).getValue() ==\n                       ((MalInteger)b).getValue();\n            } else if (a instanceof MalSymbol) {\n                return ((MalSymbol)a).getName().equals(\n                       ((MalSymbol)b).getName());\n            } else if (a instanceof MalString) {\n                return ((MalString)a).getValue().equals(\n                       ((MalString)b).getValue());\n            } else if (a instanceof MalList) {\n                if (((MalList)a).size() != ((MalList)b).size()) {\n                    return false;\n                }\n                for (Integer i=0; i<((MalList)a).size(); i++) {\n                    if (! _equal_Q(((MalList)a).nth(i),\n                                   ((MalList)b).nth(i))) {\n                        return false;\n                    }\n                }\n                return true;\n            } else if (a instanceof MalHashMap) {\n                if (((MalHashMap)a).value.size() != ((MalHashMap)b).value.size()) {\n                    return false;\n                }\n                //HashMap<String,MalVal> hm = (HashMap<String,MalVal>)a.value;\n                MalHashMap mhm = ((MalHashMap)a);\n                HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;\n                for (String k : hm.keySet()) {\n                    if (! _equal_Q(((MalVal)((MalHashMap)a).value.get(k)),\n                                   ((MalVal)((MalHashMap)b).value.get(k)))) {\n                        return false;\n                    }\n                }\n                return true;\n            } else {\n                return a == b;\n            }\n        }\n    }\n\n    //\n    // Mal boxed types\n    //\n    abstract public static class MalVal {\n        MalVal meta = Nil;\n        abstract public MalVal copy() throws MalThrowable;\n\n        // Default is just to call regular toString()\n        public String toString(Boolean print_readably) {\n            return this.toString();\n        }\n        public MalVal getMeta() { return meta; }\n        public void setMeta(MalVal m) { meta = m; }\n        public Boolean list_Q() { return false; }\n    }\n    public static class MalConstant extends MalVal {\n        String value;\n        public MalConstant(String name) { value = name; }\n        public MalConstant copy() throws MalThrowable { return this; }\n\n        public String toString() { return value; }\n    }\n    public static MalConstant Nil = new MalConstant(\"nil\");\n    public static MalConstant True = new MalConstant(\"true\");\n    public static MalConstant False = new MalConstant(\"false\");\n\n    public static class MalInteger extends MalVal {\n        Integer value;\n        public MalInteger(Integer v) { value = v; }\n        public MalInteger copy() throws MalThrowable { return this; }\n\n        public Integer getValue() { return value; }\n        @Override public String toString() {\n            return value.toString();\n        }\n        public MalInteger add(MalInteger other) {\n            return new MalInteger(value + other.getValue());\n        }\n        public MalInteger subtract(MalInteger other) {\n            return new MalInteger(value - other.getValue());\n        }\n        public MalInteger multiply(MalInteger other) {\n            return new MalInteger(value * other.getValue());\n        }\n        public MalInteger divide(MalInteger other) {\n            return new MalInteger(value / other.getValue());\n        }\n        public MalConstant lt(MalInteger other) {\n            return (value < other.getValue()) ? True : False;\n        }\n        public MalConstant lte(MalInteger other) {\n            return (value <= other.getValue()) ? True : False;\n        }\n        public MalConstant gt(MalInteger other) {\n            return (value > other.getValue()) ? True : False;\n        }\n        public MalConstant gte(MalInteger other) {\n            return (value >= other.getValue()) ? True : False;\n        }\n    }\n\n    public static class MalSymbol extends MalVal {\n        String value;\n        public MalSymbol(String v) { value = v; }\n        public MalSymbol(MalString v) { value = v.getValue(); }\n        public MalSymbol copy() throws MalThrowable { return this; }\n\n        public String getName() { return value; }\n        @Override public String toString() {\n            return value;\n        }\n    }\n\n    public static class MalString extends MalVal {\n        String value;\n        public MalString(String v) { value = v; }\n        public MalString copy() throws MalThrowable { return this; }\n\n        public String getValue() { return value; }\n        @Override public String toString() {\n            return \"\\\"\" + value + \"\\\"\";\n        }\n        public String toString(Boolean print_readably) {\n            if (value.length() > 0 && value.charAt(0) == '\\u029e') {\n                return \":\" + value.substring(1);\n            } else if (print_readably) {\n                return \"\\\"\" + printer.escapeString(value) + \"\\\"\";\n            } else {\n                return value;\n            }\n        }\n    }\n\n    public static class MalList extends MalVal {\n        String start = \"(\", end = \")\";\n        List value;\n        public MalList(List val) {\n            value = val;\n        }\n        public MalList(MalVal... mvs) {\n            value = new ArrayList<MalVal>();\n            conj_BANG(mvs);\n        }\n        public MalList copy() throws MalThrowable {\n            MalList new_ml = new MalList();\n            new_ml.value.addAll(value);\n            new_ml.meta = meta;\n            return new_ml;\n        }\n\n        @Override public String toString() {\n            return start + printer.join(value, \" \", true) + end;\n        }\n        public String toString(Boolean print_readably) {\n            return start + printer.join(value, \" \", print_readably) + end;\n        }\n\n        public List getList() { return value; }\n        public Boolean list_Q() { return true; }\n        \n        public MalList conj_BANG(MalVal... mvs) {\n            for (MalVal mv : mvs) {\n                value.add(mv);\n            }\n            return this;\n        }\n\n        public Integer size() {\n            return value.size();\n        }\n\n        public MalVal nth(Integer idx) {\n            return (MalVal)value.get(idx);\n        }\n        public MalList rest () {\n            if (size() > 0) {\n                return new MalList(value.subList(1, value.size()));\n            } else {\n                return new MalList();\n            }\n        }\n\n\n        public MalList slice(Integer start, Integer end) {\n            return new MalList(value.subList(start, end));\n        }\n        public MalList slice(Integer start) {\n            return slice(start, value.size());\n        }\n    }\n\n    public static class MalVector extends MalList {\n        // Same implementation except for instantiation methods\n        public MalVector(List val) {\n            value = val;\n            start = \"[\";\n            end = \"]\";\n        }\n        public MalVector(MalVal... mvs) {\n            super(mvs);\n            start = \"[\";\n            end = \"]\";\n        }\n        public MalVector copy() throws MalThrowable {\n            MalVector new_mv = new MalVector();\n            new_mv.value.addAll(value);\n            new_mv.meta = meta;\n            return new_mv;\n        }\n\n        public Boolean list_Q() { return false; }\n\n        public MalVector slice(Integer start, Integer end) {\n            return new MalVector(value.subList(start, end));\n        }\n    }\n\n    public static class MalHashMap extends MalVal {\n        Map value;\n        public MalHashMap(Map val) {\n            value = val;\n        }\n        public MalHashMap(MalList lst) {\n            value = new HashMap<String, MalVal>();\n            assoc_BANG(lst);\n        }\n        public MalHashMap(MalVal... mvs) {\n            value = new HashMap<String, MalVal>();\n            assoc_BANG(mvs);\n        }\n        public MalHashMap copy() throws MalThrowable {\n            Map<String,MalVal> shallowCopy = new HashMap<String,MalVal>();\n            shallowCopy.putAll(value);\n            MalHashMap new_hm = new MalHashMap(shallowCopy);\n            new_hm.meta = meta;\n            return new_hm;\n        }\n\n        @Override public String toString() {\n            return \"{\" + printer.join(value, \" \", true) + \"}\";\n        }\n        public String toString(Boolean print_readably) {\n            return \"{\" + printer.join(value, \" \", print_readably) + \"}\";\n        }\n\n        public Set _entries() {\n            return value.entrySet();\n        }\n        \n        public MalHashMap assoc_BANG(MalVal... mvs) {\n            for (Integer i=0; i<mvs.length; i+=2) {\n                value.put(((MalSymbol)mvs[i]).getName(),\n                          mvs[i+1]);\n            }\n            return this;\n        }\n\n        public MalHashMap assoc_BANG(MalList lst) {\n            for (Integer i=0; i<lst.value.size(); i+=2) {\n                value.put(((MalString)lst.nth(i)).getValue(),\n                          lst.nth(i+1));\n            }\n            return this;\n        }\n\n        public MalHashMap dissoc_BANG(MalList lst) {\n            for (Integer i=0; i<lst.value.size(); i++) {\n                value.remove(((MalString)lst.nth(i)).getValue());\n            }\n            return this;\n        }\n\n        public Integer size() {\n            return value.size();\n        }\n    }\n\n    public static class MalAtom extends MalVal {\n        MalVal value;\n        public MalAtom(MalVal value) { this.value = value; }\n        public MalAtom copy() throws MalThrowable { return new MalAtom(value); }\n        @Override public String toString() {\n            return \"(atom \" + printer._pr_str(value, true) + \")\";\n        }\n        public String toString(Boolean print_readably) {\n            return \"(atom \" + printer._pr_str(value, print_readably) + \")\";\n        }\n    }\n\n    public static interface ILambda {\n        public MalVal apply(MalList args) throws MalThrowable;\n    }\n\n    public static abstract class MalFunction extends MalVal\n            implements ILambda, java.lang.Cloneable {\n        public MalVal ast = null;\n        public Env env = null;\n        public MalList params = null;\n        public Boolean macro = false;\n        public MalFunction() { }\n        public MalFunction(MalVal ast, Env env, MalList params) {\n            this.ast = ast;\n            this.env = env;\n            this.params = params;\n        }\n        public MalFunction copy() throws MalThrowable {\n            try {\n                // WARNING: clone() is broken:\n                //   http://www.artima.com/intv/bloch13.html\n                // However, this doesn't work:\n                //   MalFunction new_mf = this.getClass().newInstance();\n                // So for now it's clone.\n                MalFunction new_mf = (MalFunction) this.clone();\n                new_mf.ast = ast;\n                new_mf.env = env;\n                new_mf.params = params;\n                new_mf.macro = macro;\n                return new_mf;\n            } catch (Throwable t) {\n                // not much we can do\n                t.printStackTrace();\n                throw new MalError(\"Could not copy MalFunction: \" + this);\n            }\n        }\n\n        public MalVal getAst() { return ast; }\n        public Env getEnv() { return env; }\n        public MalList getParams() { return params; }\n        public Env genEnv(MalList args) {\n            return new Env(env, params, args);\n        }\n        public Boolean isMacro() { return macro; }\n        public void setMacro() { macro = true; }\n    }\n}\n"
  },
  {
    "path": "impls/java/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/java-truffle/.gitignore",
    "content": ".classpath\n.project\n.settings\ntarget\n/.gradle/\n/build/\n.factorypath\n.apt_generated\nbin\ngraal_dumps\n"
  },
  {
    "path": "impls/java-truffle/Dockerfile",
    "content": "FROM ghcr.io/graalvm/graalvm-ce:21.1.0\r\n\r\nRUN microdnf install python3 unzip && \\\r\n    ln -sf /usr/bin/python3 /usr/bin/python && \\\r\n    curl -o gradle.zip \"https://downloads.gradle-dn.com/distributions/gradle-7.0.2-bin.zip\" && \\\r\n    mkdir /opt/gradle && \\\r\n    unzip -d /opt/gradle gradle.zip\r\n\r\nRUN mkdir -p /mal\r\nWORKDIR /mal\r\nENV GRADLE_USER_HOME=/tmp/.gradle\r\nENV PATH=\"$PATH:/opt/gradle/gradle-7.0.2/bin\"\r\n"
  },
  {
    "path": "impls/java-truffle/Makefile",
    "content": "all:\n\tgradle build\n\nbuild/classes/java/main/truffle/mal/step%.class: src/main/java/truffle/mal/*.java\n\tgradle build\n\nclean:\n\tgradle clean\n"
  },
  {
    "path": "impls/java-truffle/README.md",
    "content": "# Truffle Mal\n\nThis Mal is implemented in Java using the [Truffle Framework](https://github.com/oracle/graal/blob/master/truffle/README.md).\nTruffle is a library for implementing interpreters. When\nthese interpreters are run on GraalVM, the GraalVM compiler\nis able to JIT compile interpreted programs using a technique\ncalled [partial evaluation](https://en.wikipedia.org/wiki/Partial_evaluation).\n\nPartially evaluating an interpreter plus a program to produce compiled\ncode requires a careful balance. If every last bit of interpreter code\n(including supporting libraries, etc.)\nis subject to partial evaluation, the result will explode to\nunreasonable size. Boundaries must be drawn. Exclude too much, though,\nand the speed up resulting from compilation may not be worth the\neffort of the compilation.\n\nTruffle's \"thesis\" is that a small set of primitives are sufficient to make\nJIT compilation via partial evaluation practical.\nThese primitives feed runtime data collected by the executing interpreter\nto the compiler, allowing it to _specialize_, or optimistically\nsimplify, the interpreter code at compilation time. The compiler inserts\nlightweight runtime checks of the assumptions that justify its\nsimplifications. If the checks fail, the compiled code is _de-optimized_,\nand control is returned to the interpreter.\nSee [Practical Partial Evaluation for High-Performance Dynamic Language Runtimes](http://chrisseaton.com/rubytruffle/pldi17-truffle/pldi17-truffle.pdf), from PLDI 2017, for a deeper treatment of the ideas behind Truffle.\n\nThe Truffle Mal implementation is my attempt at putting the Truffle thesis\nto the test.\n\nCan I, an engineer without a background in compiler design, use Truffle to\nimplement an interpreter for a dynamic language (Mal) that substantially\noutperforms the existing Java interpreter for Mal?\n\n*The Short Answer: Yup.*\n\n```bash\n   # Recursive Fibonacci on OpenJDK 11 with java mal\n   $ ./run ../tests/fib.mal 30 10\n   Times (in ms) for (fib 30) on java: [2062 1809 1814 1777 1772 1791 1725 1723 1786 1745]\n   \n   # Recursive Fibonacci on GraalVM with java-truffle mal\n   $ ./run ../tests/fib.mal 30 10\n   Times (in ms) for (fib 30) on java-truffle: [280 142 21 26 22 75 21 26 21 24]\n   \n   # That's an 82x speed-up! Just out of curiosity...\n   # How does Clojure on OpenJDK 11? We'll even throw in a type hint.\n   $ lein repl\n   Clojure 1.10.0\n   OpenJDK 64-Bit Server VM 11.0.7+10-post-Ubuntu-2ubuntu218.04\n   user=> (defn fib [^long n] (if (= n 0) 1 (if (= n 1) 1 (+ (fib (- n 1)) (fib (- n 2))))))\n   #'user/fib\n   user=> (dotimes [i 5] (time (fib 30)))\n   \"Elapsed time: 32.0791 msecs\"\n   \"Elapsed time: 31.7552 msecs\"\n   \"Elapsed time: 31.5361 msecs\"\n   \"Elapsed time: 31.4796 msecs\"\n   \"Elapsed time: 31.4541 msecs\"\n```\n\nA recursive Fibonacci computation is _obviously_ not sufficient to characterize the\nperformance of our implementation (and as we'll see, it turns out to be\nsomething of a best-case scenario), but it sure looks impressive!\n\nDo more complicated Mal programs show similar speed-ups?\n\nHow much simplicity did we have to sacrifice in the name of performance?\n\nWas it worth it?\n\nHow much of the speed-up is really attributable to the Truffle/GraalVM combo,\nand how much came from putting more time into the code itself?\n\nWe'll explore the answers to these questions together in the remainder!\n\n## Disclaimers\n\n*First and foremost*: To the extend that this experiment _succeeds_ in its goal of\nproducing an efficient Mal implementation, the credit is due to the teams\nbehind Truffle and GraalVM. To the extend that this experiment _fails_, the blame\nfalls on *me*! The reader should assume, by default, that any deficiencies in\nthis Mal implementation are due to my own failure to understand or\nproperly apply the tools at my disposal, and _not_ due to any fundamental\nlimitations of Truffle or GraalVM.\n\n*Second:* This Mal implementation is _not_ idiomatic Java, and it's _not_ an\nidiomatic application of Truffle. The project's\nunusual organization (large numbers of package-private classes bundled\ninto single files like Types.java, substantial duplication between step files)\nrepresent my attempt to adhere both to the spirit of Mal's\npedagogical approach and the organization of the existing Java implementation.\nConsequently I have abused Truffle in several ways (that I am aware of, and perhaps\nothers that I am not?). Each Mal step\nregisters a distinct Truffle implementation whose language id has the form \"mal_step${n}\".\nThe languages for each step have distinct AST node sub-classes, but they share\nthe built-in AST nodes in Core.java and the runtime types in Types.java. This sharing\ncreates some awkwardness in Core.java.\n\n## Prerequisites\n\n[GraalVM Community Edition](https://www.graalvm.org/downloads/) (version 20.1.0 or higher)\nshould be on your PATH and pointed to by JAVA_HOME.\n\nYou'll also need to [install Gradle](https://gradle.org/install/)\nif you're going to build without using the provided Docker image.\n\n## Outline of Approach\n\nFor step 0 through step A, I've purposefully avoided Truffle-specific optimizations.\nStep A is intended to be a fully naive application of Truffle, where\na 'pure' interpreter is developed using Truffle AST nodes, but without any attempt\nto leverage Truffle primitives to specialize compiled code.\n\nBy comparing Truffle step A on OpenJDK to the existing Java step A, we can get a sense of the\noverhead imposed by the Truffle framework on interpreter performance.\n\nBy comparing Truffle step A on OpenJDK to Truffle step A on GraalVM, we can get a sense of how\nmuch performance the GraalVM compiler can give the language implementor \"for free\".\n\nEach step _after_ A employs Truffle primitives to enable specialization\nof code during compilation.\n\n* Step B specializes function calls by assuming that the same function will\n  always be called (i.e. that call sites are _monomorphic_), until proven otherwise.\n  At call sites where the same function _actually is_ always called, the compiler\n  can eliminate some code and perform inlining.\n\n* Step C optimizes and specializes environment lookups, allowing\n  us to avoid HashMap-related overhead for lookups of symbols that are statically in\n  scope (i.e. function arguments and let bindings) under the assumption that some\n  def! doesn't dynamically bind the looked-up symbols at runtime in scopes where they\n  aren't declared.\n\n* Step D enables _further_ specialization of environment lookups for closed-over\n  environments, allowing us to skip the lookups entirely under the assumption that\n  the symbols have not been rebound.\n\n* Step E specializes macro expansion, allowing the results of a macro expansion to\n  _replace_ the apply form entirely. We have to 'cheat' in this step, and extend Mal's\n  macro semantics (in a backward-compatible way!). The results are worth it!\n\n## Performance Evaluation Method\n\nTruffle Mal performance is evaluated relative to Java Mal on several benchmarks.\nFor each benchmark, we run Java Mal and Truffle Mal on both OpenJDK and GraalVM.\n\n```bash\n   # OpenJDK\n   $ java -version\n   openjdk version \"11.0.7\" 2020-04-14\n   OpenJDK Runtime Environment (build 11.0.7+10-post-Ubuntu-2ubuntu218.04)\n   OpenJDK 64-Bit Server VM (build 11.0.7+10-post-Ubuntu-2ubuntu218.04, mixed mode, sharing)\n   \n   # GraalVM\n   $ java -version\n   openjdk version \"11.0.7\" 2020-04-14\n   OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)\n   OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing)\n```\n\nIt must be said that Truffle Mal leverage Clojure's implementations of persistent\nvectors and maps. This likely has little to no impact on the perf4 and fib benchmarks,\nwhich don't operate on vectors or maps. Self-hosted Mal, however, depends on\nthe host Mal's map implementation for its environments. Since Java Mal's maps\nare built on java.util.HashMap and don't take advantage of structural sharing,\nwe expect the complexity of Java Mal's assoc and dissoc functions to be strictly\nworse than Truffle Mal's ( O(n) versus O(lg(n)) ). Whether or not this actually\ntips things in favor of Truffle Mal isn't clear; the sizes of the environments\nin question are quite small. I have not made any attempt to account for this\nin the results.\n\n### Fib\n\nThis simple benchmark focuses on symbol lookups, arithmetic, and function application.\nWe use the naive recursive approach to computing the 30th Fibonacci number. We run\nthe computation 10 times, and select the fastest result.\n\n### Busywork\n\nThe busywork.mal benchmark is a refactoring of the perf3.mal benchmark,\nwhich primarily tests macro and atom performance.\n\nWe measure how long it takes to execute 10,000 iterations of a 'busywork' function.\nAs with fib.mal, this is done 10 times and we use the fastest result.\n\n### Fib on Mal\n\nFor a more interesting test, we run the `fib.mal` benchmark using self-hosted\nMal. This gives each implementation a more comprehensive workout. We compute\nthe 15th Fibonacci number 10 times, and take the fastest execution time.\n\nNote that self-hosted Mal does not support tail call optimization, and so consumes more\nstack the longer it runs. For Truffle Mal, we need to increase the stack size from the\ndefault of 1MB to 8MB to avoid stack overflow.\n\n## Results\n\nTruffle performance is given in absolute terms, and relative to the faster of the\nJava implementation's OpenJDK and GraalVM runs for the same benchmark. \n\n### Step A: No Optimizations\n\nStep A represents a naive Mal interpreter written using Truffle AST nodes, but with\nno special effort made to leverage Truffle primitives to assist the GraalVM compiler.\n\n| Benchmark  | Java (OpenJDK) | Truffle (OpenJDK) | Truffle (GraalVM) |\n| ---------- | -------------- | ----------------- | ----------------- |\n| Fib        | 1700 ms        | 1293 ms (1.3x)    | 675 ms (2.5x)     |\n| Busywork   | 781 ms         | 914 ms            | 888 ms            |\n| Fib on Mal | 686 ms         | 2101 ms           | 1664 ms           |\n\nOn the Fib benchmark, the Java and Truffle implementations of Mal are in the same\nball park on OpenJDK, with Truffle being 1.3x faster. However, when we run the\nTruffle implementation on GraalVM, we see nearly a 2x speed-up over OpenJDK effectively\nfor free, putting it at 2.5x faster than plain old Java.\n\nThe Busywork benchmark is a different story, with the Truffle implementation _slightly_\nslower on both OpenJDK and GraalVM, and with GraalVM providing very little extra performance.\n\nFib on Mal is stranger yet: the Truffle implementation is 3x _slower_ on OpenJDK, and GraalVM\ndoesn't offer much help. What's going on?!\n\nA bit of profiling quickly yields the answer: Macros.\n\nFrom `truffle.mal.stepA_mal$ApplyNode`:\n\n```java\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var result = applyMacro(env, fn);\n                var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition);\n                var target = Truffle.getRuntime().createCallTarget(newRoot);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n```\n\nA Truffle `CallTarget` represents an AST that can be called from other code. Call Target construction\nis a heavy-weight operation that traverses the entire AST to do various initialization things.\nThe cost of this is _supposed_ to be amortized over the many calls to the code, and offset by the\ngains we see for code that is called often enough to be JIT compiled. Truffle ASTs support self-modification.\nIdeally, we'd expand a macro once, and then replace the macro application node with the result.\n\nMal's macro semantics, alas, prevent us from doing so.\nA Mal macro can choose to expand code one way or another based on the current value of any in-scope\nenvironment, or even user input. Even worse, Mal's incremental macro expansion behavior is such that it\nis allowable to write 'tail-recursive' macros that would, if eagerly expanded, take up space\nexponential in their inputs. Consider a sumdown macro:\n\n```\n   (defmacro! sumdown-via-macro* (fn* [acc n]\n     `(if (<= ~n 0)\n       ~acc\n       (sumdown-via-macro* ~(+ acc n) ~(- n 1)))))\n\n   (defmacro! sumdown-via-macro2 (fn* [n]\n     `(sumdown-via-macro* 0 ~(eval n))))\n```\n\nThis executes without issue in any conforming Mal implementation!\n\nWe'll return to macros in Step E, but before we do, we'll see what we can specialize\nwithin the confines of Mal's semantics.\n\n### Step B: Specializing Function Calls\n\nIn Step A, all function call sites are represented in the AST using Truffle's\n`IndirectCallNode`. Truffle also provides a `DirectCallNode` for use at call sites\nwhere the same function is always called. Direct function calls may be inlined by\nthe GraalVM compiler.\n\nMal's semantics make it difficult (and sometimes impossible?) to prove statically\nthat the same function will always be called at a given call site. However, it's\ntrivial for our interpreter to _assume_ that a call site is direct up until we\nlearn that it isn't. If we use Truffle properly, we can express this assumption\nin a way that the GraalVM compiler understands.\n\nHere's what the Steb B version of `InvokeNode` looks like:\n\n```java\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedTarget;\n        @CompilationFinal private CallTarget cachedTarget;\n        @CompilationFinal @Child private DirectCallNode directCallNode;\n        @CompilationFinal @Child private IndirectCallNode indirectCallNode;\n\n        /* SNIP */\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                if (!initialized) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    initialized = true;\n                    usingCachedTarget = true;\n                    cachedTarget = target;\n                    directCallNode = Truffle.getRuntime().createDirectCallNode(target);\n                }\n                while (true) {\n                    try {\n                        if (usingCachedTarget) {\n                            if (cachedTarget == target) {\n                                return directCallNode.call(args);\n                            }\n                            CompilerDirectives.transferToInterpreterAndInvalidate();\n                            usingCachedTarget = false;\n                            indirectCallNode = Truffle.getRuntime().createIndirectCallNode();\n                        }\n                        return indirectCallNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n```\n\nIt _looks_ like it should be slower now, with all the branching. What have we done?\n\nNotice that all the new member variables have been annotated with `@CompilationFinal`.\nThis tells the compiler to treat these variables as if they were `final`, because their values\nwill not change in compiled code.\n\nWe _ensure_ that they do not change in compiled code\nby inserting the `CompilerDirectives.transferToInterpreterAndInvalidate()` intrinsic.\nIn interpreted code, this is a no-op. In _compiled_ code, it is replaced with an instruction\nthat causes the compiler to _de-optimize_ the compiled code and return to the interpreter\nto continue execution.\n\nSuppose a function containing a call site that is not in tail position has been executed\nenough times to trigger compilation,\nand each time the invoked function has been the same. When compilation kicks in, the\nvariables `initialized` and `usingCachedTarget` would be true, and `tailPosition` would\nbe false.\nAccordingly, the invoke code simplifies to:\n\n```java\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            while (true) {\n                try {\n                    if (cachedTarget == target) {\n                        return directCallNode.call(args);\n                    }\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                } catch (TailCallException ex) {\n                    target = ex.callTarget;\n                    args = ex.args;\n                }\n            }\n        }\n```\n\nMuch better!\n\nBecause we're using a `DirectCallNode`, the compiler might decide to inline the called\nfunction as well. Function inlining allows the partial evaluation algorithm to extend\nacross function boundaries.\n\nLet's see if there's an improvement in practice...\n\n| Benchmark  | Java (OpenJDK) | Truffle (OpenJDK) | Truffle (GraalVM) |\n| ---------- | -------------- | ----------------- | ----------------- |\n| Fib        | 1700 ms        | 991 ms (1.7x)     | 430 ms (3.9x)     |\n| Busywork   | 781 ms         | 671 ms (1.2x)     | 409 ms (1.9x)     |\n| Fib on Mal | 686 ms         | 1912 ms (0.35x)   | 1407 ms (0.48x)   |\n\nWe see modest improvements over Step A in all cases, with the Busywork benchmark\nhaving a 2x improvement over Step A on GraalVM.\n\n### Step C: Static Symbol Lookup\n\nA little profiling shows that quite a lot of the 'work' that goes into executing a Mal\nprogram is just environment maintenance: constructing HashMaps, putting symbol/value pairs\ninto them, and looking them back up again. For code that does a lot of function calling\n(like our Fib benchmark), this adds up to a lot of overhead.\n\nWhy do we need the HashMaps at all? Why can't we build environments around Object arrays?\nDuring construction of an AST from a Mal form, we can keep track of the variables in\neach lexical scope, and assign each one a _slot_ (an index in the Object array for the\nenvironment associated with that scope). During execution, we can construct\nenvironments out of Object arrays, and get/set values using these slots. No more\nHashMaps! Right?\n\nThe trouble, of course, is that `def!` can mutate environments at runtime, adding\nbindings for symbols that were never 'declared' via `let*` or `fn*`. Consider this\nfunction:\n\n```\n   (def! f (fn* [x b] (do (who-knows? b) y)))\n```\n\nThe symbol `y` isn't lexically in scope, so we wouldn't assign\nit a slot; we'd have to try to look it up in the global environment\nat execution time. But what if, at execution time, `who-knows?` turns out\nto resolve to a _macro_ like:\n\n```\n   (fn* [b] (if b `(def! y 42)))\n```\n\nIf `b` is truthy, the `y` symbol ends up bound in the function body's environment after all,\nbut there's no slot for it in the environment's object array. Drat!\n\nBut the power of Truffle is that we don't _need_ to statically prove that our slot\nassignments and usage are valid. We're not writing a compiler! Instead, we can just\n_assume_ that the slot assignments we make are valid, right up until we find that they\naren't. Then we can fall back on a less efficient but more general approach.\n\nI won't elaborate much on the details of the code too much in step, it involves the most significant changes.\nAt a high level, here's what we do:\n\n* Introduce a `LexicalScope` class that assigns symbols to array indices, and\n  thread `LexicalScope` objects through our AST construction methods.\n* Extend `MalEnv` with a `staticBindings` Object array _in addition to_ the normal\n  `bindings` HashMap. The Object array is constructed based on the number of symbols in\n  the associated `LexicalScope`. The `bindings` HashMap is only constructed _lazily_,\n  if a symbol that isn't in a `LexicalScope` is bound via a `def!`.\n* Further extend `MalEnv` with slot-based `get` and `set` methods, in addition to the\n  existing symbol-based `get` and `set` methods.\n* Extend the AST nodes for `let*` and `fn*` to introduce new `LexicalScope` objects\n  with the right symbols, assign slots to those symbols,\n  and use the slot-based `get` and `set` methods on `MalEnv` to bind symbols.\n* Modify the AST node for symbol lookups to speculatively use slot-based lookups\n  when the symbol in question is in a lexical scope _under the assumption that it has\n  not been re-defined via `def!`.\n\nThat last bit is the key to the whole thing: We use Truffle's `Assumption` abstraction\nto tell the compiler about the assumption that our slot-based symbol look-ups depend on.\nWhen a `LexicalScope` assigns a slot, it creates an `Assumption` that the symbol\nhas not been bound by `def!` in that or any outer `LexicalScope`. The slot-based\nsymbol lookup code is guarded by that assumption. The 'dynamic' `set` method of `MalEnv`\n(the one used by `def!`) is modified to _invalidate_ that assumption, triggering\nde-optimization of any symbol lookups that might have been rendered incorrect.\n\nAfter slot assignment, where do we stand?\n\n| Benchmark  | Java (OpenJDK) | Truffle (OpenJDK) | Truffle (GraalVM) |\n| ---------- | -------------- | ----------------- | ----------------- |\n| Fib        | 1700 ms        | 829 ms (2.1x)     | 219 ms (7.8x)     |\n| Busywork   | 781 ms         | 686 ms (1.1x)     | 394 ms (2.0x)     |\n| Fib on Mal | 686 ms         | 1932 ms (0.35x)   | 1507 ms (0.46x)   |\n\nThis optimization starts to show off the real power of the Truffle framework on GraalVM,\nat least for the Fib benchmark.\nOn the JDK, we see a modest improvement (1.2x) over Step B that comes from eliminating\nsome of the HashMap overhead. Given the complexity that we had to introduce,\nthis isn't very satisfying, On GraalVM, though, we see a better than 2x speed-up, taking\nus to almost 8x faster than the Java interpreter.\n\nHowever, the other two benchmarks show no meaningful improvement at all. Fib on Mal\neven seems to have become slower! Once again, we're bit by macros here. Recall that\nsince we currently create a new AST each and every time we\nencounter a macro, the compiler never has a chance to compile it. We pay all the overhead\nof our extra book-keeping, and get absolutely no benefit.\n\n### Step D: Caching Symbol Lookups\n\nWe can take the symbol lookup improvements much further, now that we've laid the groundwork!\n\nSymbol lookups for symbols that are declared in some lexical scope will now use the fast-path Object array\nlookups instead of the HashMap lookups, and Truffle _should_ even be able to unroll the loops\nthat walk up the chain of environments for us. For local symbol lookups, we probably won't do\nmuch better.\n\nBut what about symbols in a function body that _aren't_ lexically in scope? In a well-behaved\nMal program that isn't doing anything fancy with `def!`, these symbols will either produce\nruntime environments, or resolve to the global environment. In practice, they're almost always\nlooking up core functions, whose values are unlikely (but not impossible!) to change over\nthe lifetime of the program.\n\nWe can _further_ specialize symbol lookups by simply caching looked-up values for symbols\nthat are not lexically in scope, and _skipping subsequent lookups entirely_ unless the\nlooked-up symbol gets rebound. Once again, we create an `Assumption` for each cached\nlookup to represent that we assume it has not been redefined, update `def!` to invalidate\nthat assumption.\n\n| Benchmark  | Java (OpenJDK) | Truffle (OpenJDK) | Truffle (GraalVM) |\n| ---------- | -------------- | ----------------- | ----------------- |\n| Fib        | 1700 ms        | 733 ms (2.3x)     | 18 ms (94x !!)    |\n| Busywork   | 781 ms         | 657 ms (1.2x)     | 311 ms (2.5x)     |\n| Fib on Mal | 686 ms         | 1971 ms (0.35x)   | 1474 ms (0.47x)   |\n\nOn our Fib benchmark, caching symbol lookups makes a _huge_ difference. Look at the\ncode for `fib`:\n\n```\n(def! fib (fn* [n]\n  (if (= n 0)\n    1\n    (if (= n 1)\n      1\n      (+ (fib (- n 1))\n         (fib (- n 2)))))))\n```\n\nThere are 7 look-ups of symbols not in lexical scope (`=`, `=`, `+`, `fib`, `-`, `fib`, and `-`),\nand we've effectively eliminated all of them. All that's left are fast slot-based lookups for `n`,\ntwo comparisons, and three arithmetic operations. All of those end up getting inlined by the compiler.\nMoreover, the compiler actually 'unrolls' the recorsion several levels for us by inlining `fib` into itself.\nThe result is quite fast, even out-performing type-hinted Clojure (Mal's inspiration)... on OpenJDK, anyway.\n\nAlas, the macros still defeat us on the other benchmarks, for the same reasons. The time has come to do something\nabout that.\n\n### Step E: Macro Inlining\n\nIf we stay within the confines of Mal's semantics, macros are a\nshow-stopper performance killer for us. Mal's\nmacro semantics are just too dynamic for their own good. Sure, you _can_\nwrite tail recursive macros... but why _would_ you?\n\nIn practice, macros are often just introducing 'syntactic sugar' to improve expressiveness.\nConsider the macros `cond`, `or`, `and`, `->`, and `->>`. Their\nexpansion behavior does not depend on runtime values (so they expand the same\nway on each application), and they produce code that is linear in the size of\ntheir inputs.\n\nWhy do all the work to re-expand them on every application? Why not expand them _once_,\nand then just substitute the result? Clojure macros, for example, work this way.\n\nTo make further progress, we're going to have to \"cheat\" our way into fast macros.\nWe extend Mal's semantics\nsuch that a macro with a map for metadata containing the entry `:inline? true`\nis expanded once, and the result is _inlined_ in place of the macro application\nforever after. We then mark all of the above macros as inlined macros.\n\nThis isn't a Truffle-specific optimization by any means. Any Mal interpreter\nthat supports these semantics will see _substantial_ performance gains. However,\nthe immutable nature of Mal data structures might make the refactoring of\nthese interpreters a bit trickier than we'd like.\n\nUsing Truffle, though, it's a trivial change. Truffle ASTs are explicitly self-modifying.\nIt boils down to this:\n\n```java\n            if (fn.isMacro) {\n                var expanded = applyMacro(env, fn);\n                if (isInlinableMacro(fn)) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    var newNode = expanded.body;\n                    this.replace(newNode);\n                    return newNode.executeGeneric(frame, env);\n                } else {\n                    return invokeMacro(expanded);\n                }\n            else {\n```\n\nA few extra lines is all it takes. Look what happens now...\n\n| Benchmark  | Java (OpenJDK) | Truffle (OpenJDK) | Truffle (GraalVM) |\n| ---------- | -------------- | ----------------- | ----------------- |\n| Fib        | 1700 ms        | 718 ms (2.3x)     | 21 ms (81x)       |\n| Busywork   | 781 ms         | 19 ms (41x)       | 12 ms (65x)       |\n| Fib on Mal | 686 ms         | 104 ms (6.6x)     | 25 ms (27x)       |\n\nNo substantial difference on Fib, which makes sense: that benchmark doesn't use macros.\n\n_Huge_ gains on Busywork and Fib on Mal, because both are so dependent on macros.\nIt's a bit suspicious, though, that there isn't more of a performance difference between\nthe OpenJDK and GraalVM runs. Maybe the test runs so fast we're not sufficiently warmed up?\nLet's crank up the number of iterations from 10k to 100k and see what happens.\n\n\n| Benchmark    | Java (OpenJDK) | Truffle (OpenJDK) | Truffle (GraalVM) |\n| ------------ | -------------- | ----------------- | ----------------- |\n| Busywork 10x | 7264 ms        | 223 ms (32x)      | 37 ms (196x)      |\n\nThat's more like it. Recall that before this macro optimization, Java\nand Java Truffle were close in performance. If we implemented macro inlining\nin Java Mal, to make for a fair comparison, it's still likely that Truffle Mal\nwins by around 6-7x, which is pretty decent!\n\nWhat about the Fib on Mal benchmark? Why don't we see a bigger difference\nbetween the OpenJDK and GraalVM runs? It's not insufficient warm-up this time.\nDoing some profiling shows that we're spending quite a bit of time in\ncode that isn't partially evaluated. For example, self-hosted Mal's environment\nimplementation turns symbols into strings, and uses the strings as\nkeys in environment maps, instead of just using the symbols themselves.\nThe code for turning objects into strings in Printer depends heavily on\nJDK-provided classes that were not designed with partial evaluation in mind,\nso we must exclude them from partial evaluation to avoid an explosion in\ncode size.\n\n## Conclusions\n\nDoes Truffle deliver on the promise of high-performance JIT-ed code via\npartial evaluation of interpreter code? Based on my experience, it certainly does.\nIt's not exactly magic pixie dust that gives you 100x for free, but it\ndoesn't claim to be. It _does_ enable order-of-magnitude speed improvements\nover plain old interpreters with _much_ less than an order-of-magnitude\nincrease in effort.\n\nLet's revisit the questions we started with:\n\n*Do more complicated Mal programs show similar speed-ups?*\n\nNo, GraalVM JIT compilation does not provide arbitrary Mal programs with the\nmassive performance gains we see on the Fib benchmark. This should be\ntotally unsurprising.\n\n*How much of the speed-up is really attributable to the Truffle/GraalVM combo,\nand how much came from optimizations that could be applied to any Mal interpreter?*\n\nOur benchmarks show that the answer depends heavily on the nature of the\nprogram. Let's look at the performance of Truffle Mal on GraalVM relative\nto its performance on OpenJDK (where we don't have the benefit of Truffle-\nenabled partial evaluation):\n\n| Benchmark    | TruffleMal (GraalVM relative to OpenJDK) |\n| ------------ | ---------------------------------------- |\n| Fib          | 34x |\n| Busywork 10x | 6x  |\n| Fib On Mal   | 4x  |\n\nIn extreme cases, for programs that are heavy on arithmetic and function calls,\nour use of Truffle/GraalVM buys us 30x _after accounting for our optimizations_.\n\nThat's pretty amazing.\n\nRealistically, though, we're likely to see more 3-6x speed-ups directly attributable\nto Truffle/GraalVM. Still impressive!\n\n*How much simplicity did we have to sacrifice in the name of performance?*\n\nLet's look at the size, in lines of code, of each implementation.\n\n| File           | LOC (Java) | LOC (Truffle Step A) | LOC (Truffle Step E) |\n| -------------- | ---------- | -------------------- | -------------------- |\n| stepA_mal.java | 310        | 757                  | 886                  |\n| env.java       | 58         | 145                  | 370                  |\n| printer.java   | 53         | 100                  | 100                  |\n| reader.java    | 151        | 166                  | 166                  |\n| types.java     | 381        | 532                  | 545                  |\n| core.java      | 633        | 1506                 | 1511                 |\n| *Total*        | 1586       | 3206 (2x)            | 3578 (2.25x)         |\n\nThe Truffle-based implementation, before optimizations, weighs in at about\n2x the size of the Java implementation.\nMuch of this can be attributed to 'boilerplate' associated with use of the Truffle framework.\nIn my opinion, this boilerplate adds effectively nothing to the conceptual complexity of\nthe implementation. In fact, much of the extra weight comes from the core functions.\nThe LOC count is longer because we make use of the Truffle DSL, a feature not covered in\nthis write-up, to trivially allow specialization of core functions based on argument type.\nI would argue that while this increases code _size_, it may actually _reduce_ code complexity\nvia a form of pattern matching.\n\nOur specializations to the interpreter nodes themselves added about 15%, or 120 lines.\nMore significantly, we increased the size of the environment implementation by 2.5x,\nadding substantial complexity in the process.\n\n*Was it worth it?*\n\nThis is both totally subjective and a gross over-simplification,\nbut let's just guess that we've increased the complexity of the baseline Java interpreter\noverall by roughly 1.5 x, and environments in particular by 3x.\nIn exchange for this increase in complexity, we've managed to obtain between from 25x to 80x\nbetter performance over the baseline Java interpreter, depending on the Mal\nprogram.\n\nWe could perform most of our optimizations on that Java interpreter _without_\nusing Truffle. However, we'd end up at a similar level of complexity, and\nwould see substantially smaller performance gains.\n\nBased on these results, if I were to attempt a 'production quality' Mal implementation,\nI'd probably do it with Truffle and GraalVM. The performance gains alone seem to justify it.\n\nIt's also worth observing that the Truffle/GraalVM provide _other_ interesting benefits\nthat are not performance-related. I won't cover them here. I think the most interesting\nnon-performance benefit is the promise of interoperability with other Truffle languages.\n\n## Bonus: AOT-compiled Mal\n\nGraalVM can ahead-of-time compile Java into a stand-alone executable (with some caveats)\ncalled a _native image_.\nThis works even for Truffle interpreters! With AOT-compiled Mal, we get all the JIT compilation\ngoodness of Truffle, _and_ we ditch the need for a Java runtime, **and** we skip the long JVM\nstart-up time! A GraalVM native image of our Mal interpreter is well suited for scripts and\ncommand line applications.\n\nThe `make-native.sh` script can be used to compile a native image of any Mal step.\nTo run it, though, you'll need some additional\n[prerequisites](https://www.graalvm.org/reference-manual/native-image/#prerequisites).\n\nThe `make-native.sh` script\n\n* assumes you've already run `gradle build` to compile all Java classes\n* takes as its only argument a step name, e.g. `step3_env`\n** when no argument is supplied, `stepE_macros` is selected by default\n* produces a `build/${STEP}` native image\n\n"
  },
  {
    "path": "impls/java-truffle/build.gradle",
    "content": "/*\n * This file was generated by the Gradle 'init' task.\n */\n\nplugins {\n    id 'java'\n}\n\nrepositories {\n    mavenLocal()\n    maven {\n        url = uri('https://repo.maven.apache.org/maven2')\n    }\n}\n\ndependencies {\n    implementation 'org.graalvm.truffle:truffle-api:21.1.0'\n    implementation 'org.organicdesign:Paguro:3.2.0'\n    annotationProcessor 'org.graalvm.truffle:truffle-dsl-processor:21.1.0'\n}\n\ngroup = 'com.github.mmcgill'\nversion = '0.0.1'\nsourceCompatibility = '11'\n\ntask printClasspath {\n  println sourceSets.main.runtimeClasspath.getAsPath()\n}\n"
  },
  {
    "path": "impls/java-truffle/make-native.sh",
    "content": "#!/usr/bin/env bash\n\nSTEP=${1:-stepE_macros}\n\nCP=$(gradle -q --console plain printClasspath)\nnative-image --macro:truffle --no-fallback --initialize-at-build-time \\\n\t-H:+TruffleCheckBlackListedMethods \\\n\t-cp \"$CP\" truffle.mal.$STEP build/$STEP\n"
  },
  {
    "path": "impls/java-truffle/run",
    "content": "#!/usr/bin/env bash\n\nCP=$(gradle -q --console plain printClasspath)\n\n#     -Dgraal.LogVerbose=true \\\n#     -Dgraal.TraceTruffleStackTraceLimit=100 \\\n#     -Dgraal.TruffleCompilationThreshold=100 \\\n#     -Dgraal.TraceTruffleCompilationDetails=true \\\n#     -Dgraal.Dump=Truffle:2 \\\n#     -Dgraal.TraceTruffleCompilation=true \\\n#    -Dgraal.TruffleFunctionInlining=true \\\n#    -Dgraal.TruffleCompilationExceptionsArePrinted=true \\\njava \\\n     -Dgraalvm.locatorDisabled=true \\\n     -Xss8m \\\n     --add-opens org.graalvm.truffle/com.oracle.truffle.api=ALL-UNNAMED \\\n     --add-opens org.graalvm.truffle/com.oracle.truffle.api.interop=ALL-UNNAMED \\\n     --add-opens org.graalvm.truffle/com.oracle.truffle.api.nodes=ALL-UNNAMED \\\n     -classpath $CP \\\n     truffle.mal.${STEP:-stepE_macros} \"$@\"\n"
  },
  {
    "path": "impls/java-truffle/settings.gradle",
    "content": "/*\n * This file was generated by the Gradle 'init' task.\n */\n\nrootProject.name = 'truffle-mal'\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/Core.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.io.StringWriter;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Stack;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.dsl.Fallback;\nimport com.oracle.truffle.api.dsl.GenerateNodeFactory;\nimport com.oracle.truffle.api.dsl.NodeChild;\nimport com.oracle.truffle.api.dsl.NodeFactory;\nimport com.oracle.truffle.api.dsl.Specialization;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\n\nclass Core {\n    static final Map<String, NodeFactory<? extends BuiltinNode>> NS = new HashMap<>();\n\n    static {\n        NS.put(\"+\", AddBuiltinFactory.getInstance());\n        NS.put(\"-\", SubtractBuiltinFactory.getInstance());\n        NS.put(\"*\", MultiplyBuiltinFactory.getInstance());\n        NS.put(\"/\", DivideBuiltinFactory.getInstance());\n\n        NS.put(\"prn\", PrnBuiltinFactory.getInstance());\n        NS.put(\"list\", ListBuiltinFactory.getInstance());\n        NS.put(\"list?\", IsListBuiltinFactory.getInstance());\n        NS.put(\"empty?\", IsEmptyBuiltinFactory.getInstance());\n        NS.put(\"count\", CountBuiltinFactory.getInstance());\n        NS.put(\"=\", EqualsBuiltinFactory.getInstance());\n        NS.put(\"<\", LessThanBuiltinFactory.getInstance());\n        NS.put(\"<=\", LessThanEqualBuiltinFactory.getInstance());\n        NS.put(\">\", GreaterThanBuiltinFactory.getInstance());\n        NS.put(\">=\", GreaterThanEqualBuiltinFactory.getInstance());\n        NS.put(\"pr-str\", PrStrBuiltinFactory.getInstance());\n        NS.put(\"str\", StrBuiltinFactory.getInstance());\n        NS.put(\"println\", PrintlnBuiltinFactory.getInstance());\n\n        NS.put(\"read-string\", ReadStringBuiltinFactory.getInstance());\n        NS.put(\"slurp\", SlurpBuiltinFactory.getInstance());\n        NS.put(\"eval\", EvalBuiltinFactory.getInstance());\n        NS.put(\"atom\", AtomBuiltinFactory.getInstance());\n        NS.put(\"atom?\", IsAtomBuiltinFactory.getInstance());\n        NS.put(\"deref\", DerefBuiltinFactory.getInstance());\n        NS.put(\"reset!\", ResetBuiltinFactory.getInstance());\n        NS.put(\"swap!\", SwapBuiltinFactory.getInstance());\n\n        NS.put(\"cons\", ConsBuiltinFactory.getInstance());\n        NS.put(\"concat\", ConcatBuiltinFactory.getInstance());\n        NS.put(\"vec\", VecBuiltinFactory.getInstance());\n\n        NS.put(\"nth\", NthBuiltinFactory.getInstance());\n        NS.put(\"first\", FirstBuiltinFactory.getInstance());\n        NS.put(\"rest\", RestBuiltinFactory.getInstance());\n\n        NS.put(\"throw\", ThrowBuiltinFactory.getInstance());\n        NS.put(\"apply\", ApplyBuiltinFactory.getInstance());\n        NS.put(\"map\", MapBuiltinFactory.getInstance());\n        NS.put(\"nil?\", IsNilBuiltinFactory.getInstance());\n        NS.put(\"true?\", IsTrueBuiltinFactory.getInstance());\n        NS.put(\"false?\", IsFalseBuiltinFactory.getInstance());\n        NS.put(\"symbol?\", IsSymbolBuiltinFactory.getInstance());\n        NS.put(\"symbol\", SymbolBuiltinFactory.getInstance());\n        NS.put(\"keyword\", KeywordBuiltinFactory.getInstance());\n        NS.put(\"keyword?\", IsKeywordBuiltinFactory.getInstance());\n        NS.put(\"vector\", VectorBuiltinFactory.getInstance());\n        NS.put(\"vector?\", IsVectorBuiltinFactory.getInstance());\n        NS.put(\"sequential?\", IsSequentialBuiltinFactory.getInstance());\n        NS.put(\"hash-map\", HashMapBuiltinFactory.getInstance());\n        NS.put(\"map?\", IsMapBuiltinFactory.getInstance());\n        NS.put(\"assoc\", AssocBuiltinFactory.getInstance());\n        NS.put(\"dissoc\", DissocBuiltinFactory.getInstance());\n        NS.put(\"get\", GetBuiltinFactory.getInstance());\n        NS.put(\"contains?\", ContainsBuiltinFactory.getInstance());\n        NS.put(\"keys\", KeysBuiltinFactory.getInstance());\n        NS.put(\"vals\", ValsBuiltinFactory.getInstance());\n\n        NS.put(\"readline\", ReadlineBuiltinFactory.getInstance());\n        NS.put(\"meta\", MetaBuiltinFactory.getInstance());\n        NS.put(\"with-meta\", WithMetaBuiltinFactory.getInstance());\n        NS.put(\"time-ms\", TimeMsBuiltinFactory.getInstance());\n        NS.put(\"conj\", ConjBuiltinFactory.getInstance());\n        NS.put(\"string?\", IsStringBuiltinFactory.getInstance());\n        NS.put(\"number?\", IsNumberBuiltinFactory.getInstance());\n        NS.put(\"fn?\", IsFnBuiltinFactory.getInstance());\n        NS.put(\"macro?\", IsMacroBuiltinFactory.getInstance());\n        NS.put(\"seq\", SeqBuiltinFactory.getInstance());\n    }\n\n    static MalEnv newGlobalEnv(Class<? extends TruffleLanguage<?>> languageClass, TruffleLanguage<?> language) {\n        var env = new MalEnv(languageClass);\n        for (var entry : NS.entrySet()) {\n            var root = new BuiltinRootNode(language, entry.getValue());\n            var fnVal = new MalFunction(\n                    Truffle.getRuntime().createCallTarget(root), null, root.getNumArgs(),\n                    // Built-in functions should not be tail called. It doesn't help with\n                    // stack consumption, since they aren't recursive, and it *does*\n                    // invalidate direct call sites, which hurts performance.\n                    false);\n            env.set(MalSymbol.get(entry.getKey()), fnVal);\n        }\n        return env;\n    }\n}\n\nabstract class AbstractInvokeNode extends Node {\n    abstract Object invoke(CallTarget target, Object[] args);\n}\n/** A hack to make certain nodes sharable across languages.\n */\ninterface IMalLanguage {\n    CallTarget evalForm(Object form);\n    AbstractInvokeNode invokeNode();\n    PrintStream out();\n    BufferedReader in();\n}\n\nabstract class BuiltinNode extends Node {\n    protected IMalLanguage language;\n\n    protected void setLanguage(IMalLanguage language) {\n        this.language = language;\n    }\n\n    @TruffleBoundary\n    protected static MalException illegalArgumentException(String expectedType, Object obj) {\n        return new MalException(\"Illegal argument: '\"+obj.toString()+\"' is not of type \"+expectedType);\n    }\n\n    final String name;\n\n    protected BuiltinNode(String name) {\n        this.name = name;\n    }\n\n    abstract Object executeGeneric(VirtualFrame frame);\n\n    long executeLong(VirtualFrame frame) throws UnexpectedResultException {\n        var value = executeGeneric(frame);\n        if (value instanceof Long) {\n            return (long)value;\n        }\n        throw new UnexpectedResultException(value);\n    }\n\n    boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException {\n        var value = executeGeneric(frame);\n        if (value instanceof Boolean) {\n            return (boolean)value;\n        }\n        throw new UnexpectedResultException(value);\n    }\n}\n\nclass ReadArgNode extends Node {\n    final int argNum;\n\n    ReadArgNode(int argNum) {\n        this.argNum = argNum;\n    }\n\n    Object executeGeneric(VirtualFrame frame) {\n        return frame.getArguments()[argNum];\n    }\n}\n\nclass ReadArgsNode extends Node {\n    final int argPos;\n\n    ReadArgsNode(int argPos) {\n        this.argPos = argPos;\n    }\n\n    Object executeGeneric(VirtualFrame frame) {\n        Object[] args = frame.getArguments();\n        final var len = args.length - argPos;\n        var result = new Object[len];\n        System.arraycopy(args, argPos, result, 0, len);\n        return result;\n    }\n}\n\nclass BuiltinRootNode extends RootNode {\n    private final int numArgs;\n    @Child private BuiltinNode node;\n\n    public BuiltinRootNode(TruffleLanguage<?> lang, NodeFactory<? extends BuiltinNode> nodeFactory) {\n        super(lang);\n        var sig = nodeFactory.getExecutionSignature();\n        int numArgs = nodeFactory.getExecutionSignature().size();\n        Object[] readArgNodes = new Node[numArgs];\n        for (int i=0; i < numArgs; ++i) {\n            if (sig.get(i).equals(ReadArgsNode.class)) {\n                assert i == numArgs-1 : \"ReadArgsNode must be last argument\";\n                readArgNodes[i] = new ReadArgsNode(i+1);\n                numArgs = -1; // variadic\n            } else {\n                readArgNodes[i] = new ReadArgNode(i+1);\n            }\n        }\n        node = nodeFactory.createNode(readArgNodes);\n        if (lang instanceof IMalLanguage) {\n            node.setLanguage((IMalLanguage)lang);\n        }\n        this.numArgs = numArgs;\n    }\n\n    public int getNumArgs() {\n        return numArgs;\n    }\n\n    @Override\n    public Object execute(VirtualFrame frame) {\n        return node.executeGeneric(frame);\n    }\n\n    @Override\n    public String toString() {\n        return \"#<builtin \"+node.name+\">\";\n    }\n}\n\n/************** MATH *******************/\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class AddBuiltin extends BuiltinNode {\n\n    protected AddBuiltin() { super(\"+\"); }\n\n    @Specialization\n    protected long add(long lhs, long rhs) {\n        return lhs + rhs;\n    }\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class SubtractBuiltin extends BuiltinNode {\n\n    protected SubtractBuiltin() { super(\"-\"); }\n\n    @Specialization\n    protected long subtract(long lhs, long rhs) {\n        return lhs - rhs;\n    }\n\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class MultiplyBuiltin extends BuiltinNode {\n\n    protected MultiplyBuiltin() { super(\"*\"); }\n\n    @Specialization\n    protected long multiply(long lhs, long rhs) {\n        return lhs * rhs;\n    }\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class DivideBuiltin extends BuiltinNode {\n    protected DivideBuiltin() { super(\"/\"); }\n\n    @Specialization\n    protected long divide(long lhs, long rhs) {\n        return lhs / rhs;\n    }\n}\n\n/************** STRINGS *******************/\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class PrnBuiltin extends BuiltinNode {\n    protected PrnBuiltin() { super(\"prn\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected Object prn(Object[] args) {\n        var buf = new StringBuilder();\n        if (args.length > 0) {\n            Printer.prStr(buf, args[0], true);\n        }\n        for (int i=1; i < args.length; ++i) {\n            buf.append(' ');\n            Printer.prStr(buf, args[i], true);\n        }\n        language.out().println(buf.toString());\n        return MalNil.NIL;\n    }\n}\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class PrStrBuiltin extends BuiltinNode {\n\n    protected PrStrBuiltin() { super(\"pr-str\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected String prStr(Object... args) {\n        var buf = new StringBuilder();\n        if (args.length > 0) {\n            Printer.prStr(buf, args[0], true);\n        }\n        for (int i=1; i < args.length; ++i) {\n            buf.append(' ');\n            Printer.prStr(buf, args[i], true);\n        }\n        return buf.toString();\n    }\n}\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class StrBuiltin extends BuiltinNode {\n\n    protected StrBuiltin() { super(\"str\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected String prStr(Object... args) {\n        var buf = new StringBuilder();\n        for (int i=0; i < args.length; ++i) {\n            Printer.prStr(buf, args[i], false);\n        }\n        return buf.toString();\n    }\n}\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class PrintlnBuiltin extends BuiltinNode {\n\n    protected PrintlnBuiltin() { super(\"println\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalNil println(Object... args) {\n        var buf = new StringBuilder();\n        if (args.length > 0) {\n            Printer.prStr(buf, args[0], false);\n        }\n        for (int i=1; i < args.length; ++i) {\n            buf.append(' ');\n            Printer.prStr(buf, args[i], false);\n        }\n        // The correct thing is to use the output stream associated with our language context.\n        // However, since each step is effectively its own language, and we wish\n        // to share this node among them, we'll just cheat and call System.out directly.\n        language.out().println(buf.toString());\n        return MalNil.NIL;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ReadStringBuiltin extends BuiltinNode {\n\n    protected ReadStringBuiltin() { super(\"read-string\"); }\n\n    @TruffleBoundary\n    @Specialization\n    protected Object readString(String s) {\n        return Reader.readStr(s);\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class SlurpBuiltin extends BuiltinNode {\n\n    protected SlurpBuiltin() { super(\"slurp\"); }\n\n    @TruffleBoundary\n    @Specialization\n    protected String slurp(String path) {\n        try {\n            var writer = new StringWriter();\n            var reader = new InputStreamReader(new FileInputStream(path));\n            try {\n                reader.transferTo(writer);\n                return writer.toString();\n            } finally {\n                reader.close();\n            }\n        } catch (FileNotFoundException ex) {\n            throw new MalException(ex.getMessage());\n        } catch (IOException ex) {\n            throw new MalException(ex.getMessage());\n        }\n    }\n}\n\n/************ COLLECTIONS *****************/\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class ListBuiltin extends BuiltinNode {\n\n    protected ListBuiltin() { super(\"list\"); }\n\n    @Specialization\n    protected MalList list(Object[] args) {\n        var result = MalList.EMPTY;\n        for (int i=args.length-1; i >= 0; --i) {\n            result = result.cons(args[i]);\n        }\n        return result;\n    }\n}\n\n@NodeChild(value = \"list\", type = ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsListBuiltin extends BuiltinNode {\n\n    protected IsListBuiltin() { super(\"list?\"); }\n\n    @Specialization\n    public boolean isList(MalList list) {\n        return true;\n    }\n\n    @Fallback\n    public boolean isList(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsEmptyBuiltin extends BuiltinNode {\n\n    protected IsEmptyBuiltin() { super(\"empty?\"); }\n\n    @Specialization\n    protected boolean isEmpty(MalList list) {\n        return list.head == null;\n    }\n\n    @Specialization\n    protected boolean isEmpty(MalVector vector) {\n        return vector.size() == 0;\n    }\n\n    @Fallback\n    protected Object typeError(Object arg) {\n        throw illegalArgumentException(\"list\", arg);\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class CountBuiltin extends BuiltinNode {\n\n    protected CountBuiltin() { super(\"count\"); }\n\n    @Specialization\n    protected long count(MalList arg) {\n        return arg.length;\n    }\n\n    @Specialization\n    protected long count(MalVector arg) {\n        return arg.size();\n    }\n\n    @Specialization\n    protected long count(MalNil arg) {\n        return 0;\n    }\n\n    @Fallback\n    protected Object count(Object arg) {\n        throw illegalArgumentException(\"list\", arg);\n    }\n}\n\n@NodeChild(value=\"obj\", type=ReadArgNode.class)\n@NodeChild(value=\"list\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ConsBuiltin extends BuiltinNode {\n\n    protected ConsBuiltin() { super(\"cons\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalList cons(Object obj, MalVector vec) {\n        return cons(obj, vec.toList());\n    }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalList cons(Object obj, MalList list) {\n        return list.cons(obj);\n    }\n}\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class ConcatBuiltin extends BuiltinNode {\n\n    protected ConcatBuiltin() { super(\"concat\"); }\n\n    private MalList concat1(MalList a, MalList b) {\n        var elems = new Stack<Object>();\n        for (Object elem : a) {\n            elems.push(elem);\n        }\n        while (!elems.isEmpty()) {\n            b = b.cons(elems.pop());\n        }\n        return b;\n    }\n\n    private MalList concat1(MalVector a, MalList b) {\n        for (int i=a.size()-1; i >= 0; i--) {\n            b = b.cons(a.get(i));\n        }\n        return b;\n    }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalList concat(Object... args) {\n        if (args.length == 0) {\n            return MalList.EMPTY;\n        }\n        Object arg = args[args.length-1];\n        MalList result;\n        if (arg instanceof MalVector) {\n            result = ((MalVector) arg).toList();\n        } else {\n            result = (MalList)arg;\n        }\n        for (int i=args.length-2; i >= 0; --i) {\n            arg = args[i];\n            if (arg instanceof MalVector) {\n                result = concat1((MalVector)arg, result);\n            } else {\n                result = concat1((MalList)arg, result);\n            }\n        }\n        return result;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class VecBuiltin extends BuiltinNode {\n\n    protected VecBuiltin() { super(\"vec\"); }\n    \n    @Specialization\n    protected MalVector vec(MalVector v) {\n        return v;\n    }\n\n    @Specialization\n    protected MalVector vec(MalList l) {\n        return MalVector.EMPTY.concat(l);\n    }\n}\n\n@NodeChild(value=\"list\", type=ReadArgNode.class)\n@NodeChild(value=\"n\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class NthBuiltin extends BuiltinNode {\n\n    protected NthBuiltin() { super(\"nth\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected Object nth(MalVector vec, long n) {\n        if (n >= vec.size()) {\n            throwInvalidArgument();\n        }\n        return vec.get((int)n);\n    }\n\n    private void throwInvalidArgument() {\n        throw new MalException(\"Out of bounds\");\n    }\n\n    @Specialization\n    protected Object nth(MalList list, long n) {\n        if (n >= list.length) {\n            throwInvalidArgument();\n        }\n        while (--n >= 0) {\n            list = list.tail;\n        }\n        return list.head;\n    }\n}\n\n@GenerateNodeFactory\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\nabstract class FirstBuiltin extends BuiltinNode {\n    protected FirstBuiltin() { super(\"first\"); }\n\n    @Specialization\n    protected MalNil first(MalNil nil) {\n        return MalNil.NIL;\n    }\n\n    @Specialization\n    protected Object first(MalVector vec) {\n        if (vec.size() == 0)\n            return MalNil.NIL;\n        return vec.get(0);\n    }\n\n    @Specialization\n    protected Object first(MalList list) {\n        if (list.head == null) {\n            return MalNil.NIL;\n        }\n        return list.head;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class RestBuiltin extends BuiltinNode {\n\n    protected RestBuiltin() { super(\"rest\"); }\n\n    @Specialization\n    protected MalList rest(MalNil nil) {\n        return MalList.EMPTY;\n    }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalList rest(MalVector vec) {\n        return rest(vec.toList());\n    }\n\n    @Specialization\n    protected MalList rest(MalList list) {\n        if (list.head == null) {\n            return list;\n        }\n        return list.tail;\n    }\n}\n\n@NodeChild(value=\"fn\", type=ReadArgNode.class)\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class ApplyBuiltin extends BuiltinNode {\n    @Child private AbstractInvokeNode invokeNode;\n\n    protected ApplyBuiltin() {\n        super(\"apply\");\n    }\n\n    @Override\n    protected void setLanguage(IMalLanguage language) {\n        super.setLanguage(language);\n        this.invokeNode = language.invokeNode();\n    }\n\n    @TruffleBoundary\n    private Object[] getArgs(Object[] args) {\n        Object[] fnArgs;\n        if (args.length == 0) {\n            fnArgs = args;\n        } else {\n            Object lastArg = args[args.length-1];\n            int lastArgSize;\n            if (lastArg instanceof MalVector) {\n                lastArgSize = ((MalVector)lastArg).size();\n            } else {\n                lastArgSize = (int)((MalList)lastArg).length;\n            }\n            fnArgs = new Object[args.length + lastArgSize];\n            for (int i=0; i < args.length-1; i++) {\n                fnArgs[i+1] = args[i];\n            }\n            int i = args.length;\n            assert lastArg instanceof Iterable<?>;\n            for (Object obj : ((Iterable<?>)lastArg)) {\n                fnArgs[i++] = obj;\n            }\n        }\n        return fnArgs;\n    }\n\n    @Specialization\n    protected Object apply(VirtualFrame frame, MalFunction fn, Object[] args) {\n        var fnArgs = getArgs(args);\n        fnArgs[0] = fn.closedOverEnv;\n        return invokeNode.invoke(fn.callTarget, fnArgs);\n    }\n}\n\n@NodeChild(value=\"fn\", type=ReadArgNode.class)\n@NodeChild(value=\"col\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class MapBuiltin extends BuiltinNode {\n    @Child private AbstractInvokeNode invokeNode;\n\n    protected MapBuiltin() {\n        super(\"map\");\n    }\n\n    @Override\n    protected void setLanguage(IMalLanguage language) {\n        super.setLanguage(language);\n        invokeNode = language.invokeNode();\n    }\n\n    @TruffleBoundary\n    private Object doMap(MalFunction fn, Iterable<Object> vals) {\n        var result = new ArrayList<Object>();\n        Object[] args = new Object[2];\n        args[0] = fn.closedOverEnv;\n        for (Object obj : vals) {\n            args[1] = obj;\n            result.add(invokeNode.invoke(fn.callTarget, args));\n        }\n        return MalList.from(result);\n    }\n\n    @Specialization\n    protected Object map(MalFunction fn, MalVector vec) {\n        return doMap(fn, vec);\n    }\n\n    @Specialization\n    protected Object map(MalFunction fn, MalList list) {\n        return doMap(fn, list);\n    }\n}\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class VectorBuiltin extends BuiltinNode {\n\n    protected VectorBuiltin() { super(\"vector\"); }\n\n    @TruffleBoundary\n    @Specialization\n    public MalVector vector(Object[] args) {\n        MalVector v = MalVector.EMPTY;\n        for (Object arg : args) {\n            v = v.append(arg);\n        }\n        return v;\n    }\n}\n\n@NodeChild(value=\"col\", type=ReadArgNode.class)\n@NodeChild(value=\"elems\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class ConjBuiltin extends BuiltinNode {\n\n    protected ConjBuiltin() { super(\"conj\"); }\n\n    @Specialization\n    protected MalList conj(MalList list, Object[] elems) {\n        for (int i=0; i < elems.length; i++) {\n            list = list.cons(elems[i]);\n        }\n        return list;\n    }\n\n    @Specialization\n    protected MalVector conj(MalVector vec, Object[] elems) {\n        for (int i=0; i < elems.length; i++) {\n            vec = vec.append(elems[i]);\n        }\n        return vec;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class SeqBuiltin extends BuiltinNode {\n\n    protected SeqBuiltin() { super(\"seq\"); }\n\n    @Specialization\n    protected Object seq(MalList list) {\n        if (list.length == 0) {\n            return MalNil.NIL;\n        }\n        return list;\n    }\n    @Specialization\n    protected Object seq(MalVector vec) {\n        if (vec.size() == 0) {\n            return MalNil.NIL;\n        }\n        return vec.toList();\n    }\n    @Specialization\n    protected Object seq(String str) {\n        if (str.isEmpty()) {\n            return MalNil.NIL;\n        }\n        MalList l = MalList.EMPTY;\n        for (int i=str.length()-1; i >= 0; i--) {\n            l = l.cons(str.substring(i, i+1));\n        }\n        return l;\n    }\n    @Specialization\n    protected MalNil seq(MalNil nil) {\n        return nil;\n    }\n}\n\n/************* Maps ********************/\n\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class HashMapBuiltin extends BuiltinNode {\n\n    protected HashMapBuiltin() { super(\"hash-map\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalMap hashMap(Object[] args) {\n        MalMap map = MalMap.EMPTY;\n        for (int i=0; i < args.length; i += 2) {\n            map = map.assoc(args[i], args[i+1]);\n        }\n        return map;\n    }\n}\n\n@NodeChild(value=\"map\", type=ReadArgNode.class)\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class AssocBuiltin extends BuiltinNode {\n\n    protected AssocBuiltin() { super(\"assoc\"); }\n\n    @Specialization\n    protected Object assoc(MalMap map, Object[] args) {\n        for (int i=0; i < args.length; i+=2) {\n            map = map.assoc(args[i], args[i+1]);\n        }\n        return map;\n    }\n}\n\n@NodeChild(value=\"map\", type=ReadArgNode.class)\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class DissocBuiltin extends BuiltinNode {\n\n    protected DissocBuiltin() { super(\"dissoc\"); }\n\n    @Specialization\n    protected MalMap dissoc(MalMap map, Object[] args) {\n        for (Object arg : args) {\n            map = map.dissoc(arg);\n        }\n        return map;\n    }\n}\n\n@NodeChild(value=\"map\", type=ReadArgNode.class)\n@NodeChild(value=\"key\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class GetBuiltin extends BuiltinNode {\n\n    protected GetBuiltin() { super(\"get\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected Object get(MalMap map, Object key) {\n        return map.map.getOrDefault(key, MalNil.NIL);\n    }\n\n    @Specialization\n    protected Object get(MalNil nil, Object key) {\n        return MalNil.NIL;\n    }\n}\n\n@NodeChild(value=\"map\", type=ReadArgNode.class)\n@NodeChild(value=\"key\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ContainsBuiltin extends BuiltinNode {\n\n    protected ContainsBuiltin() { super(\"contains?\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected boolean contains(MalMap map, Object key) {\n        return map.map.containsKey(key);\n    }\n}\n\n@NodeChild(value=\"map\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class KeysBuiltin extends BuiltinNode {\n\n    protected KeysBuiltin() { super(\"keys\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected MalList keys(MalMap map) {\n        MalList list = MalList.EMPTY;\n        var iter = map.map.keyIterator();\n        while (iter.hasNext()) {\n            list = list.cons(iter.next());\n        }\n        return list;\n    }\n}\n\n@NodeChild(value=\"map\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ValsBuiltin extends BuiltinNode {\n\n    protected ValsBuiltin() { super(\"vals\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected Object vals(MalMap map) {\n        MalList list = MalList.EMPTY;\n        var iter = map.map.valIterator();\n        while (iter.hasNext()) {\n            list = list.cons(iter.next());\n        }\n        return list;\n    }\n}\n\n/************* COMPARISONS *************/\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class EqualsBuiltin extends BuiltinNode {\n\n    protected EqualsBuiltin() { super(\"=\"); }\n\n    @Specialization\n    protected boolean equals(long lhs, long rhs) {\n        return lhs == rhs;\n    }\n\n    @Specialization\n    protected boolean equals(boolean lhs, boolean rhs) {\n        return lhs == rhs;\n    }\n\n    @TruffleBoundary\n    @Specialization\n    protected boolean equals(String lhs, String rhs) {\n        return lhs.equals(rhs);\n    }\n\n    @Specialization\n    protected boolean equals(MalFunction lhs, MalFunction rhs) {\n        return lhs == rhs;\n    }\n\n    @Specialization\n    protected boolean equals(MalNil lhs, MalNil rhs) {\n        return lhs == rhs;\n    }\n\n    @TruffleBoundary\n    @Specialization\n    protected boolean equals(MalValue lhs, MalValue rhs) {\n        if (lhs == null) {\n            return lhs == rhs;\n        } else {\n            return lhs.equals(rhs);\n        }\n    }\n\n    @Fallback\n    protected boolean equals(Object lhs, Object rhs) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class GreaterThanBuiltin extends BuiltinNode {\n\n    protected GreaterThanBuiltin() { super(\">\"); }\n\n    @Specialization\n    protected boolean greaterThan(long lhs, long rhs) {\n        return lhs > rhs;\n    }\n\n    @Specialization\n    protected Object typeError(Object lhs, long rhs) {\n        throw illegalArgumentException(\"integer\", lhs);\n    }\n\n    @Fallback\n    protected Object typeError(Object lhs, Object rhs) {\n        throw illegalArgumentException(\"integer\", rhs);\n    }\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class GreaterThanEqualBuiltin extends BuiltinNode {\n\n    protected GreaterThanEqualBuiltin() { super(\">=\"); }\n\n    @Specialization\n    protected boolean greaterThanEqual(long lhs, long rhs) {\n        return lhs >= rhs;\n    }\n\n    @Specialization\n    protected Object typeError(Object lhs, long rhs) {\n        throw illegalArgumentException(\"integer\", lhs);\n    }\n\n    @Fallback\n    protected Object typeError(Object lhs, Object rhs) {\n        throw illegalArgumentException(\"integer\", rhs);\n    }\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class LessThanBuiltin extends BuiltinNode {\n\n    protected LessThanBuiltin() { super(\"<\"); }\n\n    @Specialization\n    protected boolean lessThan(long lhs, long rhs) {\n        return lhs < rhs;\n    }\n\n    @Specialization\n    protected Object typeError(Object lhs, long rhs) {\n        throw illegalArgumentException(\"integer\", lhs);\n    }\n\n    @Fallback\n    protected Object typeError(Object lhs, Object rhs) {\n        throw illegalArgumentException(\"integer\", rhs);\n    }\n}\n\n@NodeChild(value=\"lhs\", type=ReadArgNode.class)\n@NodeChild(value=\"rhs\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class LessThanEqualBuiltin extends BuiltinNode {\n\n    protected LessThanEqualBuiltin() { super(\"<=\"); }\n\n    @Specialization\n    protected boolean lessThanEqual(long lhs, long rhs) {\n        return lhs <= rhs;\n    }\n\n    @Specialization\n    protected Object typeError(Object lhs, long rhs) {\n        throw illegalArgumentException(\"integer\", lhs);\n    }\n\n    @Fallback\n    protected Object typeError(Object lhs, Object rhs) {\n        throw illegalArgumentException(\"integer\", rhs);\n    }\n}\n\n/*************** Atoms ********************/\n\n@NodeChild(value=\"val\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class AtomBuiltin extends BuiltinNode {\n    protected AtomBuiltin() { super(\"atom\"); }\n\n    @Specialization\n    protected MalAtom atom(Object val) {\n        return new MalAtom(val);\n    }\n}\n\n@NodeChild(value=\"val\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsAtomBuiltin extends BuiltinNode {\n\n    protected IsAtomBuiltin() { super(\"atom?\"); }\n\n    @Specialization\n    protected boolean isAtom(Object obj) {\n        return obj instanceof MalAtom;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class DerefBuiltin extends BuiltinNode {\n\n    protected DerefBuiltin() { super(\"deref\"); }\n\n    @Specialization\n    protected Object deref(MalAtom atom) {\n        return atom.deref();\n    }\n}\n\n@NodeChild(value=\"atom\", type=ReadArgNode.class)\n@NodeChild(value=\"val\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ResetBuiltin extends BuiltinNode {\n\n    protected ResetBuiltin() { super(\"reset!\"); }\n\n    @Specialization\n    protected Object reset(MalAtom atom, Object val) {\n        atom.reset(val);\n        return val;\n    }\n}\n\n@NodeChild(value=\"atom\", type=ReadArgNode.class)\n@NodeChild(value=\"fn\", type=ReadArgNode.class)\n@NodeChild(value=\"args\", type=ReadArgsNode.class)\n@GenerateNodeFactory\nabstract class SwapBuiltin extends BuiltinNode {\n    @Child private AbstractInvokeNode invokeNode;\n\n    protected SwapBuiltin() {\n        super(\"swap!\");\n    }\n\n    @Override\n    protected void setLanguage(IMalLanguage language) {\n        super.setLanguage(language);\n        this.invokeNode = language.invokeNode();\n    }\n\n    @Specialization\n    protected Object swap(MalAtom atom, MalFunction fn, Object... args) {\n        synchronized (atom) {\n            Object[] fnArgs = new Object[2+args.length];\n            fnArgs[0] = fn.closedOverEnv;\n            fnArgs[1] = atom.deref();\n            for (int i=0; i < args.length; i++) {\n                fnArgs[i+2] = args[i];\n            }\n            Object newVal = invokeNode.invoke(fn.callTarget, fnArgs);\n            atom.reset(newVal);\n            return newVal;\n        }\n    }\n}\n\n/*************** Predicates ***************/\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsNilBuiltin extends BuiltinNode {\n    protected IsNilBuiltin() { super(\"nil?\"); }\n\n    @Specialization\n    protected boolean isNil(MalNil nil) {\n        return true;\n    }\n\n    @Fallback\n    protected boolean isNil(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsTrueBuiltin extends BuiltinNode {\n    protected IsTrueBuiltin() { super(\"true?\"); }\n\n    @Specialization\n    protected boolean isTrue(boolean b) {\n        return b == true;\n    }\n\n    @Fallback\n    protected boolean isTrue(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsFalseBuiltin extends BuiltinNode {\n    protected IsFalseBuiltin() { super(\"false?\"); }\n\n    @Specialization\n    protected boolean isFalse(boolean b) {\n        return b == false;\n    }\n\n    @Fallback\n    protected boolean isFalse(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsSymbolBuiltin extends BuiltinNode {\n    protected IsSymbolBuiltin() { super(\"symbol?\"); }\n\n    @Specialization\n    protected boolean isSymbol(MalSymbol sym) {\n        return true;\n    }\n\n    @Fallback\n    protected boolean isSymbol(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsKeywordBuiltin extends BuiltinNode {\n\n    protected IsKeywordBuiltin() { super(\"keyword?\"); }\n\n    @Specialization\n    protected boolean isKeyword(MalKeyword kw) {\n        return true;\n    }\n\n    @Fallback\n    protected boolean isKeyword(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsVectorBuiltin extends BuiltinNode {\n\n    protected IsVectorBuiltin() { super(\"vector?\"); }\n\n    @Specialization\n    protected boolean isVector(MalVector vec) {\n        return true;\n    }\n\n    @Fallback\n    protected boolean isVector(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsSequentialBuiltin extends BuiltinNode {\n\n    protected IsSequentialBuiltin() { super(\"sequential?\"); }\n\n    @Specialization\n    protected Object isSequential(MalList list) {\n        return true;\n    }\n    @Specialization\n    protected Object isSequential(MalVector vec) {\n        return true;\n    }\n    @Fallback\n    protected Object isSequential(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsMapBuiltin extends BuiltinNode {\n\n    protected IsMapBuiltin() { super(\"map?\"); }\n\n    @Specialization\n    protected boolean isMap(MalMap map) {\n        return true;\n    }\n    @Fallback\n    protected boolean isMap(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsStringBuiltin extends BuiltinNode {\n\n    protected IsStringBuiltin() { super(\"string?\"); }\n\n    @Specialization\n    protected boolean isString(String val) {\n        return true;\n    }\n\n    @Fallback\n    protected boolean isString(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsNumberBuiltin extends BuiltinNode {\n\n    protected IsNumberBuiltin() { super(\"number?\"); }\n\n    @Specialization\n    protected boolean isNumber(long n) {\n        return true;\n    }\n\n    @Fallback\n    protected boolean isNumber(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsFnBuiltin extends BuiltinNode {\n\n    protected IsFnBuiltin() { super(\"fn?\"); }\n\n    @Specialization\n    protected boolean isFn(MalFunction fn) {\n        return !fn.isMacro;\n    }\n\n    @Fallback\n    protected boolean isFn(Object obj) {\n        return false;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class IsMacroBuiltin extends BuiltinNode {\n\n    protected IsMacroBuiltin() { super(\"macro?\"); }\n\n    @Specialization\n    protected boolean isMacro(MalFunction fn) {\n        return fn.isMacro;\n    }\n\n    @Fallback\n    protected boolean isMacro(Object obj) {\n        return false;\n    }\n}\n\n/*************** Other ********************/\n\n@NodeChild(value=\"ast\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class EvalBuiltin extends BuiltinNode {\n\n    protected EvalBuiltin() { super(\"eval\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected Object eval(Object ast) {\n        return language.evalForm(ast).call();\n    }\n}\n\n@NodeChild(value=\"obj\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ThrowBuiltin extends BuiltinNode {\n\n    protected ThrowBuiltin() { super(\"throw\"); }\n\n    @TruffleBoundary\n    @Specialization\n    protected Object throwException(String obj) {\n        throw new MalException(obj);\n    }\n\n    @TruffleBoundary\n    @Fallback\n    protected Object throwException(Object obj) {\n        throw new MalException(obj);\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class SymbolBuiltin extends BuiltinNode {\n\n    protected SymbolBuiltin() { super(\"symbol\"); }\n\n    @Specialization\n    protected MalSymbol symbol(String str) {\n        return MalSymbol.get(str);\n    }\n\n    @Specialization\n    protected MalSymbol symbol(MalSymbol sym) {\n        return sym;\n    }\n}\n\n@GenerateNodeFactory\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\nabstract class KeywordBuiltin extends BuiltinNode {\n\n    protected KeywordBuiltin() { super(\"keyword\"); }\n\n    @Specialization\n    protected MalKeyword keyword(String arg) {\n        return MalKeyword.get(arg);\n    }\n\n    @Specialization\n    protected MalKeyword keyword(MalKeyword kw) {\n        return kw;\n    }\n}\n\n@NodeChild(value=\"prompt\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class ReadlineBuiltin extends BuiltinNode {\n\n    protected ReadlineBuiltin() { super(\"readline\"); }\n\n    @Specialization\n    @TruffleBoundary\n    protected Object readline(String prompt) {\n        language.out().print(prompt);\n        language.out().flush();\n        try {\n            String s = language.in().readLine();\n            return s == null ? MalNil.NIL : s;\n        } catch (IOException ex) {\n            throw new MalException(ex.getMessage());\n        }\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class MetaBuiltin extends BuiltinNode {\n\n    protected MetaBuiltin() { super(\"meta\"); }\n\n    @Specialization\n    protected <T> Object meta(MetaHolder<T> arg) {\n        return arg.getMeta();\n    }\n\n    @Fallback\n    protected Object meta(Object obj) {\n        return MalNil.NIL;\n    }\n}\n\n@NodeChild(value=\"arg\", type=ReadArgNode.class)\n@NodeChild(value=\"meta\", type=ReadArgNode.class)\n@GenerateNodeFactory\nabstract class WithMetaBuiltin extends BuiltinNode {\n\n    protected WithMetaBuiltin() { super(\"with-meta\"); }\n\n    @Specialization\n    protected <T> Object withMeta(MetaHolder<T> holder, Object meta) {\n        return holder.withMeta(meta);\n    }\n}\n\n@GenerateNodeFactory\nabstract class TimeMsBuiltin extends BuiltinNode {\n\n    protected TimeMsBuiltin() { super(\"time-ms\"); }\n\n    @TruffleBoundary\n    @Specialization\n    protected long timeMs() {\n        return System.nanoTime() / 1000000;\n    }\n}"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/MalEnv.java",
    "content": "package truffle.mal;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.oracle.truffle.api.Assumption;\nimport com.oracle.truffle.api.CompilerDirectives;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.interop.InteropLibrary;\nimport com.oracle.truffle.api.interop.InvalidArrayIndexException;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.library.ExportLibrary;\nimport com.oracle.truffle.api.library.ExportMessage;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.utilities.UnionAssumption;\n\nimport truffle.mal.LexicalScope.EnvSlot;\n\n@ExportLibrary(InteropLibrary.class)\nclass MalEnv implements TruffleObject {\n    final Class<? extends TruffleLanguage<?>> language;\n    final MalEnv outer;\n    // bindings is initialized lazily, to avoid the overhead of creating a new HashMap\n    // in cases where nothing will be bound (e.g. invoking a function with no arguments)\n    private Map<MalSymbol, Object> bindings;\n    final LexicalScope scope;\n    final Object[] staticBindings;\n    private Map<MalSymbol, CachedResult> cachedResults;\n\n    private MalEnv(Class<? extends TruffleLanguage<?>> language, MalEnv outer, LexicalScope scope, Object[] staticBindings) {\n        this.language = language;\n        this.outer = outer;\n        this.scope = scope;\n        this.staticBindings = staticBindings;\n    }\n\n    MalEnv(Class<? extends TruffleLanguage<?>> language) {\n        this(language, null, null, null);\n    }\n\n    MalEnv(MalEnv outer) {\n        this(outer.language, outer, null, null);\n    }\n\n    MalEnv(Class<? extends TruffleLanguage<?>> language, LexicalScope scope) {\n        this(language, null, scope, new Object[scope.getStaticBindingCount()]);\n    }\n\n    MalEnv(MalEnv outer, LexicalScope scope) {\n        this(outer.language, outer, scope, new Object[scope.getStaticBindingCount()]);\n    }\n\n    /**\n     * Dynamic set, for use by def! to bind a symbol that wasn't assigned a slot via a LexicalScope.\n     * \n     * @param symbol    the symbol  to bind\n     * @param value     its new value\n     */\n    @TruffleBoundary\n    void set(MalSymbol symbol, Object value) {\n        if (bindings == null) {\n            bindings = new HashMap<>();\n        }\n        if (!bindings.containsKey(symbol) && scope != null) {\n            scope.wasDynamicallyBound(symbol);\n        }\n        if (cachedResults != null) {\n            var result = cachedResults.get(symbol);\n            if (result != null) {\n                result.notRedefined.invalidate();\n            }\n        }\n        bindings.put(symbol, value);\n    }\n\n    /**\n     * Bind a symbol that was assigned a slot via a LexicalScope.\n     * @param slot      the slot assigned to the symbol\n     * @param value     the symbol's new value\n     */\n    void set(EnvSlot slot, Object value) {\n        assert slot.height == 0;\n        staticBindings[slot.slotNum] = value;\n    }\n\n    /**\n     * Dynamic get, for when the looked-up symbol has been assigned a slot\n     * but isn't guaranteed to resolve from that lexical scope, e.g. because a def!\n     * may have dynamically bound it in an inner scope.\n     * \n     * @param symbol\n     * @param slot\n     * @return\n     */\n    @TruffleBoundary\n    Object get(MalSymbol symbol, EnvSlot slot) {\n        var env = this;\n        int height = 0;\n        while (height < slot.height) {\n            Object result = null;\n            if (env.bindings != null) {\n                result = env.bindings.get(symbol);\n            }\n            if (result != null) {\n                return result;\n            }\n            env = env.outer;\n            height++;\n        }\n        return env.staticBindings[slot.slotNum];\n    }\n\n    /**\n     * Dynamic get, for when the looked-up symbol has no statically assigned slot.\n     * \n     * @param symbol    the symbol to look up\n     * @return          its current value, or null if unbound\n     */\n    @TruffleBoundary\n    Object get(MalSymbol symbol) {\n        MalEnv env = this;\n        while (env != null) {\n            if (env.bindings != null) {\n                var result = env.bindings.get(symbol);\n                if (result != null) {\n                    return result;\n                }\n            }\n            env = env.outer;\n        }\n        return null;\n    }\n\n    @TruffleBoundary\n    CachedResult cachedGet(MalSymbol symbol) {\n        if (cachedResults == null) {\n            cachedResults = new HashMap<>();\n        }\n        var result = cachedResults.get(symbol);\n        if (result == null) {\n            Object obj = null;\n            if (bindings != null) {\n                obj = bindings.get(symbol);\n            }\n            if (obj == null && outer != null) {\n                result = outer.cachedGet(symbol);\n            } else {\n                result = new CachedResult(obj);\n            }\n            cachedResults.put(symbol, result);\n        }\n        return result;\n    }\n\n    /**\n     * Static get, for when the looked-up symbol is guaranteed to resolve from a particular lexical scope.\n     * @param slot\n     * @return\n     */\n    @ExplodeLoop\n    Object get(EnvSlot slot) {\n        MalEnv env = this;\n        for (int i=0; i < slot.height; i++) {\n            env = env.outer;\n        }\n        return env.staticBindings[slot.slotNum];\n    }\n\n    @ExportMessage\n    boolean hasLanguage() {\n        return true;\n    }\n\n    @ExportMessage\n    Class<? extends TruffleLanguage<?>> getLanguage() {\n        return language;\n    }\n\n    @ExportMessage\n    boolean hasMembers() {\n        return true;\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    Object readMember(String member) {\n        return bindings.get(MalSymbol.get(member));\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    boolean isMemberReadable(String member) {\n        return bindings.containsKey(MalSymbol.get(member));\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    Object toDisplayString(boolean allowSideEffects) {\n        return \"#<environment>\";\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    boolean isMemberInsertable(String member) {\n        return !bindings.containsKey(MalSymbol.get(member));\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    boolean isMemberModifiable(String member) {\n        return bindings.containsKey(MalSymbol.get(member));\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    void writeMember(String member, Object value) {\n        set(MalSymbol.get(member), value);\n    }\n\n    @ExportMessage\n    @TruffleBoundary\n    Object getMembers(boolean includeInternal) {\n        Object[] names = new Object[bindings.size()];\n        int i=0;\n        for (MalSymbol sym : bindings.keySet()) {\n            names[i++] = sym.symbol;\n        }\n        return new EnvMembersObject(names);\n    }\n\n    static class CachedResult {\n        final Object result;\n        final Assumption notRedefined = Truffle.getRuntime().createAssumption();\n\n        CachedResult(Object result) {\n            this.result = result;\n        }\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nfinal class EnvMembersObject implements TruffleObject {\n    private final Object[] names;\n\n    EnvMembersObject(Object[] names) {\n        this.names = names;\n    }\n    @ExportMessage\n    boolean hasArrayElements() {\n        return true;\n    }\n    @ExportMessage\n    boolean isArrayElementReadable(long index) {\n        return index >= 0 && index < names.length;\n    }\n    @ExportMessage\n    long getArraySize() {\n        return names.length;\n    }\n    @ExportMessage\n    Object readArrayElement(long index) throws InvalidArrayIndexException {\n        if (!isArrayElementReadable(index)) {\n            CompilerDirectives.transferToInterpreter();\n            throw InvalidArrayIndexException.create(index);\n        }\n        return names[(int)index];\n    }\n}\n\n/**\n * A LexicalScope tracks the variables known statically to be in a given lexical scope, and keeps track of\n * associated environment slots.\n */\nclass LexicalScope {\n    final LexicalScope parent;\n    final int depth;\n    final Map<MalSymbol, EnvSlot> slots;\n    private int staticBindingCount;\n    final Map<MalSymbol, Assumption> notDynamicallyBound;\n\n    LexicalScope() {\n        this(null);\n    }\n\n    LexicalScope(LexicalScope parent) {\n        this.parent = parent;\n        this.depth = parent == null? 0 : parent.depth+1;\n        this.slots = new HashMap<>();\n        this.staticBindingCount = 0;\n        this.notDynamicallyBound = new HashMap<>();\n    }\n\n    private Assumption getNotDynamicallyBound(MalSymbol symbol) {\n        var assumption = notDynamicallyBound.get(symbol);\n        if (assumption == null) {\n            assumption = Truffle.getRuntime().createAssumption(symbol.symbol+\" not dynamically shadowed\");\n            notDynamicallyBound.put(symbol, assumption);\n        }\n        return assumption;\n    }\n\n    /**\n     * Allocate a slot for a symbol in this lexical scope, or return the slot already bound to the symbol.\n     * \n     * @param symbol\n     * @return\n     */\n    @TruffleBoundary\n    public EnvSlot allocateSlot(MalSymbol symbol) {\n        var slot = new EnvSlot(0, slots.size(), getNotDynamicallyBound(symbol));\n        slots.put(symbol, slot);\n        staticBindingCount++;\n        return slot;\n    }\n\n    /**\n     * If symbols is statically known to be in scope, returns a slot that can be used to look up\n     * the bound symbol efficiently. Otherwise, returns null;\n     * \n     * @param symbol\n     * @return\n     */\n    @TruffleBoundary\n    public EnvSlot getSlot(MalEnv env, MalSymbol symbol) {\n        int height = 0;\n        var scope = this;\n        Assumption assumption = getNotDynamicallyBound(symbol);\n        while (scope != null) {\n            if (scope.slots.containsKey(symbol)) {\n                var slot = scope.slots.get(symbol);\n                if (env.get(slot) != null) {\n                    if (height == 0) {\n                        return slot;\n                    } else {\n                        return new EnvSlot(height, scope.slots.get(symbol).slotNum, assumption);\n                    }\n                }\n            }\n            height++;\n            scope = scope.parent;\n            env = env.outer;\n            if (scope != null) {\n                assumption = new UnionAssumption(assumption, scope.getNotDynamicallyBound(symbol));\n            }\n        }\n        return null;\n    }\n\n    @TruffleBoundary\n    public void wasDynamicallyBound(MalSymbol sym) {\n        var assumption = notDynamicallyBound.get(sym);\n        if (assumption != null) {\n            assumption.invalidate();\n        }\n    }\n\n    public int getStaticBindingCount() {\n        return staticBindingCount;\n    }\n\n    static class EnvSlot {\n        public final int height;\n        public final int slotNum;\n        public final Assumption notDynamicallyBound;\n\n        private EnvSlot(int height, int slotNum, Assumption notDynamicallyBound) {\n            this.height = height;\n            this.slotNum = slotNum;\n            this.notDynamicallyBound = notDynamicallyBound;\n        }\n    }\n}"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/Printer.java",
    "content": "package truffle.mal;\n\npublic class Printer {\n\n    public static String prStr(Object form, boolean printReadably) {\n        var buf = new StringBuilder();\n        prStr(buf, form, printReadably);\n        return buf.toString();\n    }\n\n    public static void prStr(StringBuilder buf, Object form, boolean printReadably) {\n        if (form instanceof Boolean) {\n\n            buf.append((boolean)form);\n\n        } else if (form instanceof Long) {\n\n            buf.append((long)form);\n\n        } else if (form instanceof String) {\n\n            var s = (String)form;\n            if (printReadably) {\n                buf.append('\"');\n                buf.append(s.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\n\", \"\\\\n\").replace(\"\\\"\", \"\\\\\\\"\"));\n                buf.append('\"');\n            } else {\n                buf.append(s);\n            }\n\n        } else if (form instanceof MalSymbol) {\n\n            buf.append(((MalSymbol)form).symbol);\n\n        } else if (form instanceof MalKeyword) {\n\n            buf.append(':');\n            buf.append(((MalKeyword)form).keyword);\n\n        } else if (form instanceof MalNil) {\n\n            buf.append(\"nil\");\n\n        } else if (form instanceof MalList) {\n\n            var list = (MalList)form;\n            buf.append(\"(\");\n            MalList l = list;\n            while (l != null && l.head != null) {\n                prStr(buf, l.head, printReadably);\n                l = l.tail;\n                if (l.head != null) {\n                    buf.append(' ');\n                }\n            }\n            buf.append(\")\");\n\n        } else if (form instanceof MalVector) {\n\n            var vector = (MalVector)form;\n            final int size = vector.size();\n            buf.append('[');\n            for (int i=0; i < size; ++i) {\n                prStr(buf, vector.get(i), printReadably);\n                if (i < size-1) {\n                    buf.append(' ');\n                }\n            }\n            buf.append(']');\n\n        } else if (form instanceof MalMap) {\n\n            var map = (MalMap)form;\n            int i = 0;\n            buf.append('{');\n            for (var entry : map.map) {\n                prStr(buf, entry.getKey(), printReadably);\n                buf.append(' ');\n                prStr(buf, entry.getValue(), printReadably);\n                if (++i < map.map.size()) {\n                    buf.append(' ');\n                }\n            }\n            buf.append('}');\n\n        } else if (form instanceof MalFunction) {\n\n            buf.append(\"#<function>\");\n\n        } else if (form instanceof MalAtom) {\n\n            buf.append(\"(atom \");\n            prStr(buf, ((MalAtom)form).deref(), printReadably);\n            buf.append(\")\");\n\n        } else {\n            throw new RuntimeException(\"Not a MAL type: \"+form.getClass().getCanonicalName());\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/Reader.java",
    "content": "package truffle.mal;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\npublic class Reader {\n    private static final Pattern TOKEN_PATTERN = Pattern.compile(\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\");\n\n    public static List<String> tokenize(String s) {\n        var m = TOKEN_PATTERN.matcher(s);\n        var result = new ArrayList<String>();\n        while (m.find()) {\n            String t = m.group(1);\n            if (!t.isEmpty()) {\n                result.add(t);\n            }\n        }\n        return result;\n    }\n\n    public static Object readStr(String s) {\n        return new Reader(tokenize(s)).readForm();\n    }\n\n    private int i = 0;\n    private final List<String> tokens;\n\n    private Reader(List<String> tokens) {\n        this.tokens = tokens;\n    }\n\n    private boolean hasNext() {\n        return i < tokens.size();\n    }\n\n    private String peek() {\n        if (!hasNext()) {\n            throw new MalException(\"EOF\");\n        }\n        return tokens.get(i);\n    }\n\n    private String next() {\n        if (!hasNext()) {\n            throw new MalException(\"EOF\");\n        }\n        return tokens.get(i++);\n    }\n\n    private Object readForm() {\n        if (!hasNext()) {\n            return MalNil.NIL;\n        }\n        String t = peek();\n        if (t.equals(\"'\")) {\n            next();\n            return MalList.EMPTY.cons(readForm()).cons(MalSymbol.QUOTE);\n        } else if (t.equals(\"`\")) {\n            next();\n            return MalList.EMPTY.cons(readForm()).cons(MalSymbol.QUASIQUOTE);\n        } else if (t.equals(\"@\")) {\n            next();\n            return MalList.EMPTY.cons(readForm()).cons(MalSymbol.DEREF);\n        } else if (t.equals(\"~\")) {\n            next();\n            return MalList.EMPTY.cons(readForm()).cons(MalSymbol.UNQUOTE);\n        } else if (t.equals(\"~@\")) {\n            next();\n            return MalList.EMPTY.cons(readForm()).cons(MalSymbol.SPLICE_UNQUOTE);\n        } else if (t.equals(\"^\")) {\n            next();\n            var meta = readForm();\n            var obj = readForm();\n            return MalList.EMPTY.cons(meta).cons(obj).cons(MalSymbol.get(\"with-meta\"));\n        } else if (t.equals(\"(\")) {\n            return readList();\n        } else if (t.equals(\"[\")) {\n            return readVector();\n        } else if (t.equals(\"{\")) {\n            return readMap();\n        } else if (t.startsWith(\";\")) {\n            // gobble up consecutive comments without consuming stack space\n            while (t.startsWith(\";\")) {\n                next();\n                if (!hasNext())\n                    break;\n                t = peek();\n            }\n            return readForm();\n        } else {\n            return readAtom();\n        }\n    }\n\n    private MalVector readVector() {\n        var elements = new ArrayList<Object>();\n        next(); // consume '['\n        while (!peek().equals(\"]\")) {\n            elements.add(readForm());\n        }\n        next(); // consume ']'\n        return MalVector.EMPTY.concat(elements);\n    }\n\n    private MalList readList() {\n        var elements = new ArrayList<Object>();\n        next(); // consume '('\n        while (!peek().equals(\")\")) {\n            elements.add(readForm());\n        }\n        next(); // consume ')'\n        MalList result = MalList.EMPTY;\n        var iter = elements.listIterator(elements.size());\n        while (iter.hasPrevious()) {\n            result = result.cons(iter.previous());\n        }\n        return result;\n    }\n\n    private MalMap readMap() {\n        MalMap map = MalMap.EMPTY;\n        next(); // consume '{'\n        while (!peek().equals(\"}\")) {\n            map = map.assoc(readForm(), readForm());\n        }\n        next(); // consume '}'\n        return map;\n    }\n\n    private Object readAtom() {\n        String t = next();\n        if (t.charAt(0) == '\"') {\n            StringBuilder sb = new StringBuilder();\n            int i=1;\n            for (int j=t.indexOf('\\\\', i); j != -1; j=t.indexOf('\\\\', i)) {\n                sb.append(t.subSequence(i, j));\n                switch (t.charAt(j+1)) {\n                case 'n': sb.append('\\n'); break;\n                case '\"': sb.append('\"'); break;\n                case '\\\\': sb.append('\\\\'); break;\n                }\n                i = j+2;\n            }\n            if (i > t.length()-1 || t.charAt(t.length()-1) != '\"') {\n                throw new MalException(\"EOF\");\n            }\n            sb.append(t.substring(i, t.length()-1));\n            return sb.toString();\n        } else if (t.charAt(0) == ':') {\n            return MalKeyword.get(t.substring(1));\n        } else if (t.charAt(0) >= '0' && t.charAt(0) <= '9') {\n            return Long.parseLong(t);\n        } else if (t.length() > 1 && t.charAt(0) == '-' && t.charAt(1) >= '0' && t.charAt(1) <= '9') {\n            return Long.parseLong(t);\n        } else if (t.equals(\"true\")) {\n            return true;\n        } else if (t.equals(\"false\")) {\n            return false;\n        } else if (t.equals(\"nil\")) {\n            return MalNil.NIL;\n        } else {\n            return MalSymbol.get(t);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/Types.java",
    "content": "package truffle.mal;\n\nimport java.util.Iterator;\nimport java.util.Stack;\n\nimport org.organicdesign.fp.collections.PersistentHashMap;\nimport org.organicdesign.fp.collections.PersistentVector;\n\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.TruffleException;\nimport com.oracle.truffle.api.interop.InteropLibrary;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.library.ExportLibrary;\nimport com.oracle.truffle.api.library.ExportMessage;\nimport com.oracle.truffle.api.nodes.Node;\n\npublic class Types {\n}\n\ninterface MetaHolder<T> {\n    Object getMeta();\n    T withMeta(Object meta);\n}\n\n@SuppressWarnings(\"serial\")\nclass MalException extends RuntimeException implements TruffleException {\n    final Object obj;\n\n    MalException(String message) {\n        super(message);\n        this.obj = message;\n    }\n\n    MalException(Object obj) {\n        super(Printer.prStr(obj, true));\n        this.obj = obj;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n    @Override\n    public Node getLocation() {\n        return null;\n    }\n}\n\nabstract class MalValue {\n    @Override\n    @TruffleBoundary\n    public String toString() {\n        return Printer.prStr(this, true);\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalNil extends MalValue implements TruffleObject {\n    public static final MalNil NIL = new MalNil();\n\n    private MalNil() {}\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalList extends MalValue implements TruffleObject, Iterable<Object>, MetaHolder<MalList> {\n    public static final MalList EMPTY = new MalList();\n\n    @TruffleBoundary\n    public static MalList from(Iterable<? extends Object> list) {\n        var result = EMPTY;\n        var stack = new Stack<Object>();\n        list.forEach(stack::add);\n        while (!stack.isEmpty()) {\n            result = result.cons(stack.pop());\n        }\n        return result;\n    }\n\n    private static int computeHash(Object head, MalList tail) {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + head.hashCode();\n        result = prime * result + tail.hashCode();\n        return result;\n    }\n\n    public final Object head;\n    public final MalList tail;\n    private final int hash;\n    // The lazy programmer's way of ensuring constant-time size() calls: waste lots of memory!\n    public final int length;\n    public final Object meta;\n\n    @TruffleBoundary\n    private MalList() {\n        this.head = null;\n        this.tail = null;\n        this.hash = 31;\n        this.length = 0;\n        this.meta = MalNil.NIL;\n    }\n\n    @TruffleBoundary\n    private MalList(MalList list, Object meta) {\n        this.head = list.head;\n        this.tail = list.tail;\n        this.hash = list.hash;\n        this.length = list.length;\n        this.meta = meta;\n    }\n\n    @TruffleBoundary\n    private MalList(Object head, MalList tail, Object meta) {\n        this.head = head;\n        this.tail = tail;\n        this.hash = computeHash(head, tail);\n        this.length = tail.length+1;\n        this.meta = meta;\n    }\n\n    public boolean isEmpty() {\n        return head == null;\n    }\n\n    @TruffleBoundary\n    public MalList cons(Object val) {\n        return new MalList(val, this, this.meta);\n    }\n\n    @Override\n    public int hashCode() {\n        return hash;\n    }\n\n    @Override\n    @TruffleBoundary\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (obj instanceof MalVector) {\n            MalVector other = (MalVector)obj;\n            if (this.length != other.size())\n                return false;\n            int i=0;\n            MalList list = this;\n            while (!list.isEmpty()) {\n                if (!list.head.equals(other.get(i))) {\n                    return false;\n                }\n                i++;\n                list = list.tail;\n            }\n            return true;\n        }\n        if (this.getClass() != obj.getClass())\n            return false;\n\n        MalList other = (MalList) obj;\n        if (head == null) {\n            if (other.head != null)\n                return false;\n        } else if (!head.equals(other.head))\n            return false;\n        if (tail == null) {\n            if (other.tail != null)\n                return false;\n        } else if (!tail.equals(other.tail))\n            return false;\n        return true;\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n\n    @Override\n    public Iterator<Object> iterator() {\n        return new MalListIterator(this);\n    }\n\n    private static class MalListIterator implements Iterator<Object> {\n        private MalList list;\n\n        MalListIterator(MalList list) {\n            this.list = list;\n        }\n\n        @Override\n        public boolean hasNext() {\n            return !list.equals(MalList.EMPTY);\n        }\n\n        @Override\n        public Object next() {\n            Object obj = list.head;\n            list = list.tail;\n            return obj;\n        }\n    }\n\n    @Override\n    public Object getMeta() {\n        return meta;\n    }\n\n    @Override\n    public MalList withMeta(Object meta) {\n        return new MalList(this, meta);\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalVector extends MalValue implements TruffleObject, Iterable<Object>, MetaHolder<MalVector> {\n    public static final MalVector EMPTY = new MalVector();\n\n    private final PersistentVector<Object> vector;\n    private final Object meta;\n\n    private MalVector() {\n        vector = PersistentVector.empty();\n        meta = MalNil.NIL;\n    }\n\n    private MalVector(PersistentVector<Object> vector, Object meta) {\n        this.vector = vector;\n        this.meta = meta;\n    }\n\n    @TruffleBoundary\n    public MalVector append(Object obj) {\n        return new MalVector(vector.append(obj), this.meta);\n    }\n\n    @TruffleBoundary\n    public MalVector concat(Object[] objs) {\n        var v = vector.mutable();\n        for (int i=0; i < objs.length; ++i) {\n            v.append(objs[i]);\n        }\n        return new MalVector(v.immutable(), meta);\n    }\n\n    @TruffleBoundary\n    public MalVector concat(Iterable<? extends Object> objs) {\n        return new MalVector(vector.concat(objs), meta);\n    }\n\n    public int size() {\n        return vector.size();\n    }\n\n    public Object get(int i) {\n        return vector.get(i);\n    }\n\n    @Override\n    public int hashCode() {\n        return vector.hashCode();\n    }\n\n    @Override\n    @TruffleBoundary\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (obj instanceof MalList)\n            return obj.equals(this);\n        if (getClass() != obj.getClass())\n            return false;\n        MalVector other = (MalVector) obj;\n        return vector.equals(other.vector);\n    }\n\n    @Override\n    public Iterator<Object> iterator() {\n        return vector.iterator();\n    }\n\n    @TruffleBoundary\n    public MalList toList() {\n        MalList result = MalList.EMPTY;\n        for (int i=vector.size()-1; i >= 0; i--) {\n            result = result.cons(vector.get(i));\n        }\n        return result;\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n\n    @Override\n    public Object getMeta() {\n        return meta;\n    }\n\n    @Override\n    public MalVector withMeta(Object meta) {\n        return new MalVector(this.vector, meta);\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalMap extends MalValue implements TruffleObject, MetaHolder<MalMap> {\n    public static final MalMap EMPTY = new MalMap();\n\n    public final PersistentHashMap<Object, Object> map;\n    private final Object meta;\n\n    private MalMap() {\n        map = PersistentHashMap.EMPTY;\n        meta = MalNil.NIL;\n    }\n\n    private MalMap(PersistentHashMap<Object,Object> map, Object meta) {\n        this.map = map;\n        this.meta = meta;\n    }\n\n    @TruffleBoundary\n    public MalMap assoc(Object key, Object val) {\n        return new MalMap(map.assoc(key, val), meta);\n    }\n\n    @TruffleBoundary\n    public MalMap dissoc(Object key) {\n        return new MalMap(map.without(key), meta);\n    }\n\n    @TruffleBoundary\n    public Object get(Object key) {\n        if (map.containsKey(key)) {\n            return map.get(key);\n        } else {\n            return MalNil.NIL;\n        }\n    }\n\n    @TruffleBoundary\n    @Override\n    public int hashCode() {\n        return map.hashCode();\n    }\n\n    @TruffleBoundary\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        MalMap other = (MalMap) obj;\n        return map.equals(other.map);\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n\n    @Override\n    public Object getMeta() {\n        return meta;\n    }\n\n    @Override\n    public MalMap withMeta(Object meta) {\n        return new MalMap(map, meta);\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalKeyword extends MalValue implements TruffleObject {\n    public static final MalKeyword INLINE_Q = MalKeyword.get(\"inline?\");\n\n    public final String keyword;\n\n    public static MalKeyword get(String keyword) {\n        return new MalKeyword(keyword);\n    }\n\n    private MalKeyword(String keyword) {\n        this.keyword = keyword;\n    }\n\n    @Override\n    public int hashCode() {\n        return keyword.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof MalKeyword)) {\n            return false;\n        }\n        return keyword.equals(((MalKeyword)obj).keyword);\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalSymbol extends MalValue implements TruffleObject {\n    public static MalSymbol get(String symbol) {\n        return new MalSymbol(symbol);\n    }\n\n    public static final MalSymbol LET_STAR = MalSymbol.get(\"let*\");\n    public static final MalSymbol DEF_BANG = MalSymbol.get(\"def!\");\n    public static final MalSymbol DO = MalSymbol.get(\"do\");\n    public static final MalSymbol IF = MalSymbol.get(\"if\");\n    public static final MalSymbol FN_STAR = MalSymbol.get(\"fn*\");\n    public static final MalSymbol AMPERSAND = MalSymbol.get(\"&\");\n    public static final MalSymbol QUOTE = MalSymbol.get(\"quote\");\n    public static final MalSymbol QUASIQUOTE = MalSymbol.get(\"quasiquote\");\n    public static final MalSymbol UNQUOTE = MalSymbol.get(\"unquote\");\n    public static final MalSymbol SPLICE_UNQUOTE = MalSymbol.get(\"splice-unquote\");\n    public static final MalSymbol DEFMACRO = MalSymbol.get(\"defmacro!\");\n    public static final MalSymbol MACROEXPAND = MalSymbol.get(\"macroexpand\");\n    public static final MalSymbol DEREF = MalSymbol.get(\"deref\");\n    public static final MalSymbol TRY = MalSymbol.get(\"try*\");\n    public static final MalSymbol CATCH = MalSymbol.get(\"catch*\");\n\n    public final String symbol;\n\n    private MalSymbol(String symbol) {\n        this.symbol = symbol;\n    }\n\n    @Override\n    public int hashCode() {\n        return symbol.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        MalSymbol other = (MalSymbol) obj;\n        if (symbol == null) {\n            if (other.symbol != null)\n                return false;\n        } else if (!symbol.equals(other.symbol))\n            return false;\n        return true;\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalFunction extends MalValue implements TruffleObject, MetaHolder<MalFunction> {\n    final RootCallTarget callTarget;\n    final MalEnv closedOverEnv;\n    final int numArgs;\n    final boolean isMacro;\n    final Object meta;\n    final boolean canBeTailCalled;\n\n    MalFunction(RootCallTarget callTarget, MalEnv closedOverEnv, int numArgs, boolean canBeTailCalled) {\n        this.callTarget = callTarget;\n        this.closedOverEnv = closedOverEnv;\n        this.numArgs = numArgs;\n        this.isMacro = false;\n        this.meta = MalNil.NIL;\n        this.canBeTailCalled = canBeTailCalled;\n    }\n\n    MalFunction(RootCallTarget callTarget, MalEnv closedOverEnv, int numArgs) {\n        this(callTarget, closedOverEnv, numArgs, true);\n    }\n\n    MalFunction(MalFunction f, boolean isMacro) {\n        this(f, f.meta, isMacro, true);\n    }\n\n    MalFunction(MalFunction f, Object meta, boolean isMacro) {\n        this(f, meta, isMacro, true);\n    }\n\n    MalFunction(MalFunction f, Object meta, boolean isMacro, boolean canBeTailCalled) {\n        this.callTarget = f.callTarget;\n        this.closedOverEnv = f.closedOverEnv;\n        this.numArgs = f.numArgs;\n        this.isMacro = isMacro;\n        this.meta = meta;\n        this.canBeTailCalled = canBeTailCalled;\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n\n    @Override\n    public Object getMeta() {\n        return meta;\n    }\n\n    @Override\n    public MalFunction withMeta(Object meta) {\n        return new MalFunction(this, meta, this.isMacro);\n    }\n}\n\n@ExportLibrary(InteropLibrary.class)\nclass MalAtom extends MalValue implements TruffleObject {\n    private Object value;\n\n    public MalAtom(Object initialValue) {\n        this.value = initialValue;\n    }\n\n    public Object deref() {\n        return value;\n    }\n\n    public Object reset(Object newValue) {\n        this.value = newValue;\n        return newValue;\n    }\n\n    @ExportMessage\n    Object toDisplayString(boolean allowSideEffects) {\n        return this.toString();\n    }\n}"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step0_repl.java",
    "content": "package truffle.mal;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.IOException;\r\nimport java.io.InputStreamReader;\r\n\r\npublic class step0_repl {\r\n    private static String READ(String s) {\r\n        return s;\r\n    }\r\n\r\n    private static void PRINT(String s) {\r\n        System.out.println(s);\r\n    }\r\n\r\n    private static void rep(String s) {\r\n        PRINT(READ(s));\r\n    }\r\n\r\n    public static void main(String[] args) throws IOException {\r\n        boolean done = false;\r\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\r\n        while (!done) {\r\n            System.out.print(\"user> \");\r\n            String s = reader.readLine();\r\n            if (s == null) {\r\n                done = true;\r\n            } else {\r\n                rep(s);\r\n            }\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step1_read_print.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\n\npublic class step1_read_print {\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        var reader = new BufferedReader(new InputStreamReader(System.in));\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    System.out.println(Printer.prStr(Reader.readStr(s), true));\n                } catch (MalException ex) {\n                    System.out.println(ex.getMessage());\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step2_eval.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step2_eval {\n    static final String LANGUAGE_ID = \"mal_step2\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n        var context = Context.create(LANGUAGE_ID);\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    System.out.println(val.toString());\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n    static Map<MalSymbol, BuiltinFn> replEnv = new HashMap<>();\n    static {\n        replEnv.put(MalSymbol.get(\"+\"), new BuiltinFn(args -> { return (long)args[0]+(long)args[1]; }));\n        replEnv.put(MalSymbol.get(\"-\"), new BuiltinFn(args -> { return (long)args[0]-(long)args[1]; }));\n        replEnv.put(MalSymbol.get(\"*\"), new BuiltinFn(args -> { return (long)args[0]*(long)args[1]; }));\n        replEnv.put(MalSymbol.get(\"/\"), new BuiltinFn(args -> { return (long)args[0]/(long)args[1]; }));\n    };\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame);\n\n        public long executeLong(VirtualFrame frame) throws UnexpectedResultException {\n            var value = executeGeneric(frame);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException {\n            var value = executeGeneric(frame);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static MalNode formToNode(Object form) {\n        if (form instanceof MalSymbol) {\n\n            return new LookupNode((MalSymbol)form);\n\n        } else if (form instanceof MalVector) {\n\n            return new VectorNode((MalVector)form);\n\n        } else if (form instanceof MalMap) {\n\n            return new MapNode((MalMap)form);\n\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n\n            return new ApplyNode((MalList)form);\n\n        } else {\n\n            return new LiteralNode(form);\n\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(vector.get(i));\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame) {\n            var elements = new Object[elementNodes.length];\n            for (int i=0; i < elementNodes.length; i++) {\n                elements[i] = elementNodes[i].executeGeneric(frame);\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(entry.getKey());\n                nodes[i++] = formToNode(entry.getValue());\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame), nodes[i+1].executeGeneric(frame));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @TruffleBoundary\n        private Object lookup() {\n            var result = replEnv.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame) {\n            return lookup();\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n\n        ApplyNode(MalList list) {\n            super(list);\n            fnNode = formToNode(list.head);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(list.head);\n                list = list.tail;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame) {\n            var fn = (BuiltinFn)fnNode.executeGeneric(frame);\n            var args = new Object[argNodes.length];\n            for (int i=0; i < args.length; i++) {\n                args[i] = argNodes[i].executeGeneric(frame);\n            }\n            return fn.fn.apply(args);\n        }\n    }\n\n    static class MalRootNode extends RootNode {\n        final Object form;\n        @Child MalNode body;\n\n        MalRootNode(TruffleLanguage<?> language, Object form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            this.body = formToNode(form);\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame);\n        }\n    }\n\n    public final static class MalContext {\n        \n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext();\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            var root = new MalRootNode(this, Reader.readStr(s));\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step3_env.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step3_env {\n    static final String LANGUAGE_ID = \"mal_step3\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n        var context = Context.create(LANGUAGE_ID);\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    System.out.println(val.toString());\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static MalNode formToNode(Object form) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode((MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode((MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head)) {\n                return new DefNode(list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(list);\n            } else {\n                return new ApplyNode(list);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(vector.get(i));\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(entry.getKey());\n                nodes[i++] = formToNode(entry.getValue());\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n\n        ApplyNode(MalList list) {\n            super(list);\n            fnNode = formToNode(list.head);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(list.head);\n                list = list.tail;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (BuiltinFn)fnNode.executeGeneric(frame, env);\n            var args = new Object[argNodes.length];\n            for (int i=0; i < args.length; i++) {\n                args[i] = argNodes[i].executeGeneric(frame, env);\n            }\n            return fn.fn.apply(args);\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        DefNode(MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.valueNode = formToNode(list.tail.tail.head);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n        LetBindingNode(MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(valueForm);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalList form) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode((MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(form.tail.tail.head);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    static class MalRootNode extends RootNode {\n        final Object form;\n        @Child MalNode body;\n\n        MalRootNode(TruffleLanguage<?> language, Object form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            this.body = formToNode(form);\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var ctx = lookupContextReference(MalLanguage.class).get();\n            return body.executeGeneric(frame, ctx.globalEnv);\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv = new MalEnv(MalLanguage.class);\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> {\n        @Override\n        protected MalContext createContext(Env env) {\n            var ctx = new MalContext();\n            ctx.globalEnv.set(MalSymbol.get(\"+\"), new BuiltinFn(args -> { return (long)args[0]+(long)args[1]; }));\n            ctx.globalEnv.set(MalSymbol.get(\"-\"), new BuiltinFn(args -> { return (long)args[0]-(long)args[1]; }));\n            ctx.globalEnv.set(MalSymbol.get(\"*\"), new BuiltinFn(args -> { return (long)args[0]*(long)args[1]; }));\n            ctx.globalEnv.set(MalSymbol.get(\"/\"), new BuiltinFn(args -> { return (long)args[0]/(long)args[1]; }));\n            return ctx;\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            var root = new MalRootNode(this, Reader.readStr(s));\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step4_if_fn_do.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step4_if_fn_do {\n    static final String LANGUAGE_ID = \"mal_step4\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static MalNode formToNode(MalLanguage language, Object form) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else {\n                return new ApplyNode(language, list);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i));\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey());\n                nodes[i++] = formToNode(language, entry.getValue());\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode() {\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return callNode.call(target, args);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        ApplyNode(MalLanguage language, MalList list) {\n            super(list);\n            fnNode = formToNode(language, list.head);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head);\n                list = list.tail;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            var args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; i++) {\n                args[i+1] = argNodes[i].executeGeneric(frame, env);\n            }\n            return callNode.call(fn.callTarget, args);\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.valueNode = formToNode(language, list.tail.tail.head);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    static class MalRootNode extends RootNode {\n        final Object form;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            this.body = formToNode(language, form);\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var ctx = lookupContextReference(MalLanguage.class).get();\n            return body.executeGeneric(frame, ctx.globalEnv);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head);\n            trueNode = formToNode(language, form.tail.tail.head);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var root = new MalRootNode(this, form);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode();\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step5_tco.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step5_tco {\n    static final String LANGUAGE_ID = \"mal_step5\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            if (tailPosition) {\n                throw new TailCallException(target, args);\n            } else {\n                while (true) {\n                    try {\n                        return callNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            var args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; i++) {\n                args[i+1] = argNodes[i].executeGeneric(frame, env);\n            }\n            return invokeNode.invoke(fn.callTarget, args);\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a top-level evaluated form.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, false);\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var ctx = lookupContextReference(MalLanguage.class).get();\n            return body.executeGeneric(frame, ctx.globalEnv);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-2);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var root = new MalRootNode(this, form);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step6_file.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step6_file {\n    static final String LANGUAGE_ID = \"mal_step6\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            if (tailPosition) {\n                throw new TailCallException(target, args);\n            } else {\n                while (true) {\n                    try {\n                        return callNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            var args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; i++) {\n                args[i+1] = argNodes[i].executeGeneric(frame, env);\n            }\n            return invokeNode.invoke(fn.callTarget, args);\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a top-level evaluated form.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, false);\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var ctx = lookupContextReference(MalLanguage.class).get();\n            return body.executeGeneric(frame, ctx.globalEnv);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-2);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var root = new MalRootNode(this, form);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step7_quote.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step7_quote {\n    static final String LANGUAGE_ID = \"mal_step7\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            if (tailPosition) {\n                throw new TailCallException(target, args);\n            } else {\n                while (true) {\n                    try {\n                        return callNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            var args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; i++) {\n                args[i+1] = argNodes[i].executeGeneric(frame, env);\n            }\n            return invokeNode.invoke(fn.callTarget, args);\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a top-level evaluated form.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, false);\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var ctx = lookupContextReference(MalLanguage.class).get();\n            return body.executeGeneric(frame, ctx.globalEnv);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-2);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var root = new MalRootNode(this, form);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step8_macros.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step8_macros {\n    static final String LANGUAGE_ID = \"mal_step8\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(symbol+\" not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                while (true) {\n                    try {\n                        return callNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            this.language = language;\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private CallTarget applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            var result = macroexpand(invokeNode, env, form);\n            var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition);\n            return Truffle.getRuntime().createCallTarget(newRoot);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var target = applyMacro(env, fn);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-2);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var env = getCurrentContext(MalLanguage.class).globalEnv;\n            var root = new MalRootNode(this, form, env, false);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/step9_try.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class step9_try {\n    static final String LANGUAGE_ID = \"mal_step9\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else if (MalSymbol.TRY.equals(head)) {\n                return new TryNode(language, list, tailPosition);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(\"'\"+symbol+\"' not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                while (true) {\n                    try {\n                        return callNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            this.language = language;\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private CallTarget applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            Object form = invokeNode.invoke(fn.callTarget, args, false);\n            var result = macroexpand(invokeNode, env, form);\n            var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition);\n            return Truffle.getRuntime().createCallTarget(newRoot);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var target = applyMacro(env, fn);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-2);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    static class TryNode extends MalNode {\n        @Child private MalNode tryBody;\n        @Child private MalNode catchBody;\n        final MalSymbol exSymbol;\n\n        TryNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var tryForm = form.tail.head;\n            var catchForm = (MalList)form.tail.tail.head;\n            // We don't allow tail calls inside a try body, because\n            // they'd get thrown past the catch that should catch subsequent failures.\n            this.tryBody = formToNode(language, tryForm, false);\n            if (catchForm != null && MalSymbol.CATCH.equals(catchForm.head)) {\n                exSymbol = (MalSymbol)catchForm.tail.head;\n                catchBody = formToNode(language, catchForm.tail.tail.head, tailPosition);\n            } else {\n                exSymbol = null;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            try {\n                return tryBody.executeGeneric(frame, env);\n            } catch (MalException ex) {\n                if (catchBody == null) {\n                    throw ex;\n                }\n                var catchEnv = new MalEnv(env);\n                catchEnv.set(exSymbol, ex.obj);\n                return catchBody.executeGeneric(frame, catchEnv);\n            }\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var env = getCurrentContext(MalLanguage.class).globalEnv;\n            var root = new MalRootNode(this, form, env, false);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/stepA_mal.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class stepA_mal {\n    static final String LANGUAGE_ID = \"mal_stepA\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        context.eval(LANGUAGE_ID, \"(def! *host-language* \\\"java-truffle\\\")\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    @TruffleBoundary\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else if (MalSymbol.TRY.equals(head)) {\n                return new TryNode(language, list, tailPosition);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(\"'\"+symbol+\"' not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @Child private IndirectCallNode callNode = Truffle.getRuntime().createIndirectCallNode();\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                while (true) {\n                    try {\n                        return callNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            this.language = language;\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private CallTarget applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            Object form = invokeNode.invoke(fn.callTarget, args, false);\n            var result = macroexpand(invokeNode, env, form);\n            var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition);\n            return Truffle.getRuntime().createCallTarget(newRoot);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var target = applyMacro(env, fn);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-1);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return form.toString();\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    static class TryNode extends MalNode {\n        @Child private MalNode tryBody;\n        @Child private MalNode catchBody;\n        final MalSymbol exSymbol;\n\n        TryNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var tryForm = form.tail.head;\n            var catchForm = (MalList)form.tail.tail.head;\n            // We don't allow tail calls inside a try body, because\n            // they'd get thrown past the catch that should catch subsequent failures.\n            this.tryBody = formToNode(language, tryForm, false);\n            if (catchForm != null && MalSymbol.CATCH.equals(catchForm.head)) {\n                exSymbol = (MalSymbol)catchForm.tail.head;\n                catchBody = formToNode(language, catchForm.tail.tail.head, tailPosition);\n            } else {\n                exSymbol = null;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            try {\n                return tryBody.executeGeneric(frame, env);\n            } catch (MalException ex) {\n                if (catchBody == null) {\n                    throw ex;\n                }\n                var catchEnv = new MalEnv(env);\n                catchEnv.set(exSymbol, ex.obj);\n                return catchBody.executeGeneric(frame, catchEnv);\n            }\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var env = getCurrentContext(MalLanguage.class).globalEnv;\n            var root = new MalRootNode(this, form, env, false);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/stepB_calls.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives;\nimport com.oracle.truffle.api.CompilerDirectives.CompilationFinal;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.DirectCallNode;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\npublic class stepB_calls {\n    static final String LANGUAGE_ID = \"mal_stepB\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        context.eval(LANGUAGE_ID, \"(def! *host-language* \\\"java-truffle\\\")\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    @TruffleBoundary\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else if (MalSymbol.TRY.equals(head)) {\n                return new TryNode(language, list, tailPosition);\n            } else {\n                return new ApplyNode(language, list, tailPosition);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false);\n                nodes[i++] = formToNode(language, entry.getValue(), false);\n            }\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                result = result.assoc(nodes[i].executeGeneric(frame, env), nodes[i+1].executeGeneric(frame, env));\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n\n        LookupNode(MalSymbol symbol) {\n            super(symbol);\n            this.symbol = symbol;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = env.get(symbol);\n            if (result == null) {\n                throw new MalException(\"'\"+symbol+\"' not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        @TruffleBoundary\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedTarget;\n        @CompilationFinal private CallTarget cachedTarget;\n        @CompilationFinal @Child private DirectCallNode directCallNode;\n        @CompilationFinal @Child private IndirectCallNode indirectCallNode;\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                if (!initialized) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    initialized = true;\n                    usingCachedTarget = true;\n                    cachedTarget = target;\n                    directCallNode = Truffle.getRuntime().createDirectCallNode(target);\n                }\n                while (true) {\n                    try {\n                        if (usingCachedTarget) {\n                            if (cachedTarget == target) {\n                                return directCallNode.call(args);\n                            }\n                            CompilerDirectives.transferToInterpreterAndInvalidate();\n                            usingCachedTarget = false;\n                            indirectCallNode = Truffle.getRuntime().createIndirectCallNode();\n                        }\n                        return indirectCallNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedFn;\n        @CompilationFinal private MalFunction cachedFn;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition) {\n            super(list);\n            this.language = language;\n            fnNode = formToNode(language, list.head, false);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private CallTarget applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            Object form = invokeNode.invoke(fn.callTarget, args, false);\n            var result = macroexpand(invokeNode, env, form);\n            var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition);\n            return Truffle.getRuntime().createCallTarget(newRoot);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                cachedFn = fn;\n                usingCachedFn = true;\n            }\n            if (usingCachedFn) {\n                if (fn != cachedFn) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    usingCachedFn = false;\n                } else {\n                    fn = cachedFn;\n                }\n            }\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var target = applyMacro(env, fn);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args, fn.canBeTailCalled);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        @Child private MalNode valueNode;\n\n        DefNode(MalLanguage language, MalList list) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.valueNode = formToNode(language, list.tail.tail.head, false);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            env.set(symbol, value);\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final MalSymbol symbol;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm) {\n            this.symbol = symbol;\n            this.valueNode = formToNode(language, valueForm, false);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1));\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-1);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || val == Boolean.FALSE) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final MalSymbol symbol;\n        protected final int argPos;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos) {\n            this.symbol = symbol;\n            this.argPos = argPos;\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos) {\n            super(symbol, argPos);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(symbol, buildVarArgsList(frame.getArguments()));\n        }\n    }\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0]);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return form.toString();\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form) {\n            super(form);\n            fnRoot = new FnRootNode(language, form);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    static class TryNode extends MalNode {\n        @Child private MalNode tryBody;\n        @Child private MalNode catchBody;\n        final MalSymbol exSymbol;\n\n        TryNode(MalLanguage language, MalList form, boolean tailPosition) {\n            super(form);\n            var tryForm = form.tail.head;\n            var catchForm = (MalList)form.tail.tail.head;\n            // We don't allow tail calls inside a try body, because\n            // they'd get thrown past the catch that should catch subsequent failures.\n            this.tryBody = formToNode(language, tryForm, false);\n            if (catchForm != null && MalSymbol.CATCH.equals(catchForm.head)) {\n                exSymbol = (MalSymbol)catchForm.tail.head;\n                catchBody = formToNode(language, catchForm.tail.tail.head, tailPosition);\n            } else {\n                exSymbol = null;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            try {\n                return tryBody.executeGeneric(frame, env);\n            } catch (MalException ex) {\n                if (catchBody == null) {\n                    throw ex;\n                }\n                var catchEnv = new MalEnv(env);\n                catchEnv.set(exSymbol, ex.obj);\n                return catchBody.executeGeneric(frame, catchEnv);\n            }\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var env = getCurrentContext(MalLanguage.class).globalEnv;\n            var root = new MalRootNode(this, form, env, false);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/stepC_slots.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives;\nimport com.oracle.truffle.api.CompilerDirectives.CompilationFinal;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.DirectCallNode;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\nimport truffle.mal.LexicalScope.EnvSlot;\n\npublic class stepC_slots {\n    static final String LANGUAGE_ID = \"mal_stepC\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        context.eval(LANGUAGE_ID, \"(def! *host-language* \\\"java-truffle\\\")\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    @TruffleBoundary\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition, LexicalScope scope) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form, scope);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form, scope);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form, scope);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list, scope);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list, scope);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition, scope);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else if (MalSymbol.TRY.equals(head)) {\n                return new TryNode(language, list, tailPosition, scope);\n            } else {\n                return new ApplyNode(language, list, tailPosition, scope);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector, LexicalScope scope) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false, scope);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map, LexicalScope scope) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false, scope);\n                nodes[i++] = formToNode(language, entry.getValue(), false, scope);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                var k = nodes[i].executeGeneric(frame, env);\n                var v = nodes[i+1].executeGeneric(frame, env);\n                result = result.assoc(k, v);\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n        private final LexicalScope scope;\n        @CompilationFinal boolean initialized = false;\n        @CompilationFinal EnvSlot slot;\n\n        LookupNode(MalSymbol symbol, LexicalScope scope) {\n            super(symbol);\n            this.symbol = symbol;\n            this.scope = scope;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                slot = scope.getSlot(env, symbol);\n            }\n            Object result = null;\n            if (slot != null) {\n                if (slot.notDynamicallyBound.isValid()) {\n                    result = env.get(slot);\n                } else {\n                    result = env.get(symbol, slot);\n                }\n            } else {\n                result = env.get(symbol);\n            }\n            if (result == null) {\n                throw new MalException(\"'\"+symbol.symbol+\"' not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedTarget;\n        @CompilationFinal private CallTarget cachedTarget;\n        @CompilationFinal @Child private DirectCallNode directCallNode;\n        @CompilationFinal @Child private IndirectCallNode indirectCallNode;\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                if (!initialized) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    initialized = true;\n                    usingCachedTarget = true;\n                    cachedTarget = target;\n                    directCallNode = Truffle.getRuntime().createDirectCallNode(target);\n                }\n                while (true) {\n                    try {\n                        if (usingCachedTarget) {\n                            if (cachedTarget == target) {\n                                return directCallNode.call(args);\n                            }\n                            CompilerDirectives.transferToInterpreterAndInvalidate();\n                            usingCachedTarget = false;\n                            indirectCallNode = Truffle.getRuntime().createIndirectCallNode();\n                        }\n                        return indirectCallNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        final LexicalScope scope;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedFn;\n        @CompilationFinal private MalFunction cachedFn;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition, LexicalScope scope) {\n            super(list);\n            this.language = language;\n            this.scope = scope;\n            fnNode = formToNode(language, list.head, false, scope);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false, scope);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private CallTarget applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            Object form = invokeNode.invoke(fn.callTarget, args, false);\n            var result = macroexpand(invokeNode, env, form);\n            var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition, scope);\n            return Truffle.getRuntime().createCallTarget(newRoot);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                cachedFn = fn;\n                usingCachedFn = true;\n            }\n            if (usingCachedFn) {\n                if (fn != cachedFn) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    usingCachedFn = false;\n                } else {\n                    fn = cachedFn;\n                }\n            }\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var target = applyMacro(env, fn);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args, fn.canBeTailCalled);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        private final LexicalScope scope;\n        @Child private MalNode valueNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private EnvSlot slot;\n\n        DefNode(MalLanguage language, MalList list, LexicalScope scope) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.scope = scope;\n            this.valueNode = formToNode(language, list.tail.tail.head, false, scope);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                var slot = scope.getSlot(env, symbol);\n                if (slot != null && slot.height == 0) {\n                    this.slot = slot;\n                }\n            }\n            if (slot != null) {\n                env.set(slot, value);\n            } else {\n                env.set(symbol, value);\n            }\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final EnvSlot slot;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm, LexicalScope scope) {\n            this.slot = scope.allocateSlot(symbol);\n            this.valueNode = formToNode(language, valueForm, false, scope);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(slot, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        private final LexicalScope scope;\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope outerScope) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            scope = new LexicalScope(outerScope);\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1), scope);\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition, scope);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv, scope);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition, LexicalScope scope) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition, scope);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-1, scope);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false, scope);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition, scope);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition, scope);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final int argPos;\n        protected final EnvSlot slot;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            this.argPos = argPos;\n            this.slot = scope.allocateSlot(symbol);\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            super(symbol, argPos, scope);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(slot, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            super(symbol, argPos, scope);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(slot, buildVarArgsList(frame.getArguments()));\n        }\n    }\n\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        final LexicalScope scope;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form, LexicalScope outerScope) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            this.scope = new LexicalScope(outerScope);\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1, scope);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1, scope);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true, scope);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0], scope);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return form.toString();\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form, LexicalScope scope) {\n            super(form);\n            fnRoot = new FnRootNode(language, form, scope);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    static class TryNode extends MalNode {\n        @Child private MalNode tryBody;\n        @Child private MalNode catchBody;\n        final EnvSlot exSlot;\n        final LexicalScope catchScope;\n\n        TryNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            var tryForm = form.tail.head;\n            var catchForm = (MalList)form.tail.tail.head;\n            // We don't allow tail calls inside a try body, because\n            // they'd get thrown past the catch that should catch subsequent failures.\n            this.tryBody = formToNode(language, tryForm, false, scope);\n            if (catchForm != null && MalSymbol.CATCH.equals(catchForm.head)) {\n                catchScope = new LexicalScope(scope);\n                var exSymbol = (MalSymbol)catchForm.tail.head;\n                exSlot = catchScope.allocateSlot(exSymbol);\n                catchBody = formToNode(language, catchForm.tail.tail.head, tailPosition, catchScope);\n            } else {\n                catchScope = null;\n                exSlot = null;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            try {\n                return tryBody.executeGeneric(frame, env);\n            } catch (MalException ex) {\n                if (catchBody == null) {\n                    throw ex;\n                }\n                var catchEnv = new MalEnv(env, catchScope);\n                catchEnv.set(exSlot, ex.obj);\n                return catchBody.executeGeneric(frame, catchEnv);\n            }\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final LexicalScope globalScope;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            globalScope = new LexicalScope();\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var ctx = getCurrentContext(MalLanguage.class);\n            var root = new MalRootNode(this, form, ctx.globalEnv, false, ctx.globalScope);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/stepD_caching.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.Assumption;\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives;\nimport com.oracle.truffle.api.CompilerDirectives.CompilationFinal;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.DirectCallNode;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\nimport truffle.mal.LexicalScope.EnvSlot;\nimport truffle.mal.MalEnv.CachedResult;\n\npublic class stepD_caching {\n    static final String LANGUAGE_ID = \"mal_stepD\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        context.eval(LANGUAGE_ID, \"(def! *host-language* \\\"java-truffle\\\")\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    @TruffleBoundary\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition, LexicalScope scope) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form, scope);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form, scope);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form, scope);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list, scope);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list, scope);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition, scope);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else if (MalSymbol.TRY.equals(head)) {\n                return new TryNode(language, list, tailPosition, scope);\n            } else {\n                return new ApplyNode(language, list, tailPosition, scope);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector, LexicalScope scope) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false, scope);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new ArrayList<>(elementNodes.length);\n            for (int i=0; i < elementNodes.length; i++) {\n                elements.add(elementNodes[i].executeGeneric(frame, env));\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map, LexicalScope scope) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false, scope);\n                nodes[i++] = formToNode(language, entry.getValue(), false, scope);\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                var k = nodes[i].executeGeneric(frame, env);\n                var v = nodes[i+1].executeGeneric(frame, env);\n                result = result.assoc(k, v);\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n        private final LexicalScope scope;\n        @CompilationFinal boolean initialized = false;\n        @CompilationFinal EnvSlot slot;\n        @CompilationFinal CachedResult cachedResult;\n        @CompilationFinal Assumption notRedefined;\n\n        LookupNode(MalSymbol symbol, LexicalScope scope) {\n            super(symbol);\n            this.symbol = symbol;\n            this.scope = scope;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                slot = scope.getSlot(env, symbol);\n                if (slot == null) {\n                    cachedResult = env.cachedGet(symbol);\n                    notRedefined = cachedResult.notRedefined;\n                }\n            }\n            Object result = null;\n            if (slot != null) {\n                if (slot.notDynamicallyBound.isValid()) {\n                    result = env.get(slot);\n                } else {\n                    result = env.get(symbol, slot);\n                }\n            } else {\n                if (notRedefined.isValid()) {\n                    result = cachedResult.result;\n                } else {\n                    result = env.get(symbol);\n                }\n            }\n            if (result == null) {\n                throw new MalException(\"'\"+symbol.symbol+\"' not found\");\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedTarget;\n        @CompilationFinal private CallTarget cachedTarget;\n        @CompilationFinal @Child private DirectCallNode directCallNode;\n        @CompilationFinal @Child private IndirectCallNode indirectCallNode;\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                if (!initialized) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    initialized = true;\n                    usingCachedTarget = true;\n                    cachedTarget = target;\n                    directCallNode = Truffle.getRuntime().createDirectCallNode(target);\n                }\n                while (true) {\n                    try {\n                        if (usingCachedTarget) {\n                            if (cachedTarget == target) {\n                                return directCallNode.call(args);\n                            }\n                            CompilerDirectives.transferToInterpreterAndInvalidate();\n                            usingCachedTarget = false;\n                            indirectCallNode = Truffle.getRuntime().createIndirectCallNode();\n                        }\n                        return indirectCallNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        final LexicalScope scope;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedFn;\n        @CompilationFinal private MalFunction cachedFn;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition, LexicalScope scope) {\n            super(list);\n            this.language = language;\n            this.scope = scope;\n            fnNode = formToNode(language, list.head, false, scope);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false, scope);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private CallTarget applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            Object form = invokeNode.invoke(fn.callTarget, args, false);\n            var result = macroexpand(invokeNode, env, form);\n            var newRoot = new MalRootNode(language, result, env, invokeNode.tailPosition, scope);\n            return Truffle.getRuntime().createCallTarget(newRoot);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                cachedFn = fn;\n                usingCachedFn = true;\n            }\n            if (usingCachedFn) {\n                if (fn != cachedFn) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    usingCachedFn = false;\n                } else {\n                    fn = cachedFn;\n                }\n            }\n            if (fn.isMacro) {\n                // Mal's macro semantics are... interesting. To preserve them in the\n                // general case, we must re-expand a macro each time it's applied.\n                // Executing the result means turning it into a Truffle AST, creating\n                // a CallTarget, calling it, and then throwing it away.\n                // This is TERRIBLE for performance! Truffle should not be used like this!\n                var target = applyMacro(env, fn);\n                return invokeNode.invoke(target, new Object[] {}, false);\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args, fn.canBeTailCalled);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        private final LexicalScope scope;\n        @Child private MalNode valueNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private EnvSlot slot;\n\n        DefNode(MalLanguage language, MalList list, LexicalScope scope) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.scope = scope;\n            this.valueNode = formToNode(language, list.tail.tail.head, false, scope);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                var slot = scope.getSlot(env, symbol);\n                if (slot != null && slot.height == 0) {\n                    this.slot = slot;\n                }\n            }\n            if (slot != null) {\n                env.set(slot, value);\n            } else {\n                env.set(symbol, value);\n            }\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final EnvSlot slot;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm, LexicalScope scope) {\n            this.slot = scope.allocateSlot(symbol);\n            this.valueNode = formToNode(language, valueForm, false, scope);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(slot, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        private final LexicalScope scope;\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope outerScope) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            scope = new LexicalScope(outerScope);\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1), scope);\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition, scope);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv, scope);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition, LexicalScope scope) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition, scope);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-1, scope);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false, scope);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition, scope);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition, scope);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final int argPos;\n        protected final EnvSlot slot;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            this.argPos = argPos;\n            this.slot = scope.allocateSlot(symbol);\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            super(symbol, argPos, scope);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(slot, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            super(symbol, argPos, scope);\n        }\n\n        @TruffleBoundary\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(slot, buildVarArgsList(frame.getArguments()));\n        }\n    }\n\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        final LexicalScope scope;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form, LexicalScope outerScope) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            this.scope = new LexicalScope(outerScope);\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1, scope);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1, scope);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true, scope);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0], scope);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return form.toString();\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form, LexicalScope scope) {\n            super(form);\n            fnRoot = new FnRootNode(language, form, scope);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    static class TryNode extends MalNode {\n        @Child private MalNode tryBody;\n        @Child private MalNode catchBody;\n        final EnvSlot exSlot;\n        final LexicalScope catchScope;\n\n        TryNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            var tryForm = form.tail.head;\n            var catchForm = (MalList)form.tail.tail.head;\n            // We don't allow tail calls inside a try body, because\n            // they'd get thrown past the catch that should catch subsequent failures.\n            this.tryBody = formToNode(language, tryForm, false, scope);\n            if (catchForm != null && MalSymbol.CATCH.equals(catchForm.head)) {\n                catchScope = new LexicalScope(scope);\n                var exSymbol = (MalSymbol)catchForm.tail.head;\n                exSlot = catchScope.allocateSlot(exSymbol);\n                catchBody = formToNode(language, catchForm.tail.tail.head, tailPosition, catchScope);\n            } else {\n                catchScope = null;\n                exSlot = null;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            try {\n                return tryBody.executeGeneric(frame, env);\n            } catch (MalException ex) {\n                if (catchBody == null) {\n                    throw ex;\n                }\n                var catchEnv = new MalEnv(env, catchScope);\n                catchEnv.set(exSlot, ex.obj);\n                return catchBody.executeGeneric(frame, catchEnv);\n            }\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final LexicalScope globalScope;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            globalScope = new LexicalScope();\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var ctx = getCurrentContext(MalLanguage.class);\n            var root = new MalRootNode(this, form, ctx.globalEnv, false, ctx.globalScope);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/java-truffle/src/main/java/truffle/mal/stepE_macros.java",
    "content": "package truffle.mal;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.function.Function;\n\nimport org.graalvm.polyglot.Context;\nimport org.graalvm.polyglot.PolyglotException;\nimport org.graalvm.polyglot.Value;\n\nimport com.oracle.truffle.api.Assumption;\nimport com.oracle.truffle.api.CallTarget;\nimport com.oracle.truffle.api.CompilerDirectives;\nimport com.oracle.truffle.api.CompilerDirectives.CompilationFinal;\nimport com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;\nimport com.oracle.truffle.api.RootCallTarget;\nimport com.oracle.truffle.api.Scope;\nimport com.oracle.truffle.api.Truffle;\nimport com.oracle.truffle.api.TruffleLanguage;\nimport com.oracle.truffle.api.frame.FrameDescriptor;\nimport com.oracle.truffle.api.frame.VirtualFrame;\nimport com.oracle.truffle.api.interop.TruffleObject;\nimport com.oracle.truffle.api.nodes.ControlFlowException;\nimport com.oracle.truffle.api.nodes.DirectCallNode;\nimport com.oracle.truffle.api.nodes.ExplodeLoop;\nimport com.oracle.truffle.api.nodes.IndirectCallNode;\nimport com.oracle.truffle.api.nodes.Node;\nimport com.oracle.truffle.api.nodes.RootNode;\nimport com.oracle.truffle.api.nodes.UnexpectedResultException;\nimport com.oracle.truffle.api.source.Source;\n\nimport truffle.mal.LexicalScope.EnvSlot;\nimport truffle.mal.MalEnv.CachedResult;\n\npublic class stepE_macros {\n    static final String LANGUAGE_ID = \"mal_stepE\";\n\n    public static void main(String[] args) throws IOException {\n        boolean done = false;\n        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));\n\n        var context = Context.create(LANGUAGE_ID);\n        context.eval(LANGUAGE_ID, \"(def! not (fn* [a] (if a false true)))\");\n        context.eval(LANGUAGE_ID, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n        context.eval(LANGUAGE_ID, \"(defmacro! cond ^{:inline? true} (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n        context.eval(LANGUAGE_ID, \"(def! *host-language* \\\"java-truffle\\\")\");\n\n        var buf = new StringBuilder();\n        buf.append(\"(def! *ARGV* (list\");\n        for (int i=1; i < args.length; i++) {\n            buf.append(' ');\n            buf.append(Printer.prStr(args[i], true));\n        }\n        buf.append(\"))\");\n        context.eval(LANGUAGE_ID, buf.toString());\n\n        if (args.length > 0) {\n            context.eval(LANGUAGE_ID, \"(load-file \\\"\"+args[0]+\"\\\")\");\n            return;\n        }\n\n        while (!done) {\n            System.out.print(\"user> \");\n            String s = reader.readLine();\n            if (s == null) {\n                done = true;\n            } else {\n                try {\n                    Value val = context.eval(LANGUAGE_ID, s);\n                    context.getBindings(LANGUAGE_ID).putMember(\"*1\", val);\n                    context.eval(LANGUAGE_ID, \"(prn *1)\");\n                } catch (PolyglotException ex) {\n                    if (ex.isGuestException()) {\n                        System.out.println(\"Error: \"+ex.getMessage());\n                    } else {\n                        throw ex;\n                    }\n                }\n            }\n        }\n    }\n\n    static class BuiltinFn implements TruffleObject {\n        final Function<Object[], Object> fn;\n        BuiltinFn(Function<Object[], Object> fn) {\n            this.fn = fn;\n        }\n    }\n\n    static abstract class MalNode extends Node {\n        final Object form;\n        protected MalNode(Object form) {\n            this.form = form;\n        }\n\n        public abstract Object executeGeneric(VirtualFrame frame, MalEnv env);\n\n        public long executeLong(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Long) {\n                return (long)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n\n        public boolean executeBoolean(VirtualFrame frame, MalEnv env) throws UnexpectedResultException {\n            var value = executeGeneric(frame, env);\n            if (value instanceof Boolean) {\n                return (boolean)value;\n            }\n            throw new UnexpectedResultException(value);\n        }\n    }\n\n    private static boolean isPair(Object obj) {\n        return (obj instanceof MalList && ((MalList)obj).length > 0)\n               ||\n               (obj instanceof MalVector && ((MalVector)obj).size() > 0);\n    }\n\n    private static Object quasiquote(Object form) {\n        if (!isPair(form)) {\n            return MalList.EMPTY.cons(form).cons(MalSymbol.QUOTE);\n        }\n        MalList list = (form instanceof MalVector) ? ((MalVector)form).toList() : (MalList)form;\n        if (MalSymbol.UNQUOTE.equals(list.head)) {\n            return list.tail.head;\n        }\n        var result = new ArrayList<Object>();\n        if (isPair(list.head) && MalSymbol.SPLICE_UNQUOTE.equals(((MalList)list.head).head)) {\n            result.add(MalSymbol.get(\"concat\"));\n            result.add(((MalList)list.head).tail.head);\n        } else {\n            result.add(MalSymbol.get(\"cons\"));\n            result.add(quasiquote(list.head));\n        }\n        result.add(quasiquote(list.tail));\n        return MalList.from(result);\n    }\n\n    @TruffleBoundary\n    private static MalNode formToNode(MalLanguage language, Object form, boolean tailPosition, LexicalScope scope) {\n        if (form instanceof MalSymbol) {\n            return new LookupNode((MalSymbol)form, scope);\n        } else if (form instanceof MalVector) {\n            return new VectorNode(language, (MalVector)form, scope);\n        } else if (form instanceof MalMap) {\n            return new MapNode(language, (MalMap)form, scope);\n        } else if (form instanceof MalList && !((MalList)form).isEmpty()) {\n            var list = (MalList)form;\n            var head = list.head;\n            if (MalSymbol.DEF_BANG.equals(head) || MalSymbol.DEFMACRO.equals(head)) {\n                return new DefNode(language, list, scope);\n            } else if (MalSymbol.LET_STAR.equals(head)) {\n                return new LetNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.DO.equals(head)) {\n                return new DoNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.IF.equals(head)) {\n                return new IfNode(language, list, tailPosition, scope);\n            } else if (MalSymbol.FN_STAR.equals(head)) {\n                return new FnNode(language, list, scope);\n            } else if (MalSymbol.QUOTE.equals(head)) {\n                return new QuoteNode(language, list);\n            } else if (MalSymbol.QUASIQUOTE.equals(head)) {\n                return formToNode(language, quasiquote(list.tail.head), tailPosition, scope);\n            } else if (MalSymbol.MACROEXPAND.equals(head)) {\n                return new MacroexpandNode(list);\n            } else if (MalSymbol.TRY.equals(head)) {\n                return new TryNode(language, list, tailPosition, scope);\n            } else {\n                return new ApplyNode(language, list, tailPosition, scope);\n            }\n        } else {\n            return new LiteralNode(form);\n        }\n    }\n\n    static class LiteralNode extends MalNode {\n        LiteralNode(Object form) {\n            super(form);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return form;\n        }\n    }\n\n    static class VectorNode extends MalNode {\n        @Children private MalNode[] elementNodes;\n\n        VectorNode(MalLanguage language, MalVector vector, LexicalScope scope) {\n            super(vector);\n            this.elementNodes = new MalNode[vector.size()];\n            for (int i=0; i < vector.size(); i++) {\n                elementNodes[i] = formToNode(language, vector.get(i), false, scope);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var elements = new Object[elementNodes.length];\n            for (int i=0; i < elementNodes.length; i++) {\n                elements[i] = elementNodes[i].executeGeneric(frame, env);\n            }\n            return MalVector.EMPTY.concat(elements);\n        }\n    }\n\n    static class MapNode extends MalNode {\n        @Children private MalNode[] nodes;\n        MapNode(MalLanguage language, MalMap map, LexicalScope scope) {\n            super(map);\n            nodes = new MalNode[map.map.size()*2];\n            int i=0;\n            for (var entry : map.map) {\n                nodes[i++] = formToNode(language, entry.getKey(), false, scope);\n                nodes[i++] = formToNode(language, entry.getValue(), false, scope);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var result = MalMap.EMPTY;\n            for (int i=0; i < nodes.length; i += 2) {\n                var k = nodes[i].executeGeneric(frame, env);\n                var v = nodes[i+1].executeGeneric(frame, env);\n                result = result.assoc(k, v);\n            }\n            return result;\n        }\n    }\n\n    static class LookupNode extends MalNode {\n        private final MalSymbol symbol;\n        private final LexicalScope scope;\n        @CompilationFinal boolean initialized = false;\n        @CompilationFinal EnvSlot slot;\n        @CompilationFinal CachedResult cachedResult;\n        @CompilationFinal Assumption notRedefined;\n\n        LookupNode(MalSymbol symbol, LexicalScope scope) {\n            super(symbol);\n            this.symbol = symbol;\n            this.scope = scope;\n        }\n\n        @TruffleBoundary\n        private void throwNotFound() {\n            throw new MalException(\"'\"+symbol.symbol+\"' not found\");\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                slot = scope.getSlot(env, symbol);\n                if (slot == null) {\n                    cachedResult = env.cachedGet(symbol);\n                    notRedefined = cachedResult.notRedefined;\n                }\n            }\n            Object result = null;\n            if (slot != null) {\n                if (slot.notDynamicallyBound.isValid()) {\n                    result = env.get(slot);\n                } else {\n                    result = env.get(symbol, slot);\n                }\n            } else {\n                if (notRedefined.isValid()) {\n                    result = cachedResult.result;\n                } else {\n                    result = env.get(symbol);\n                }\n            }\n            if (result == null) {\n                throwNotFound();\n            }\n            return result;\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    static class TailCallException extends ControlFlowException {\n        final CallTarget callTarget;\n        final Object[] args;\n        TailCallException(CallTarget target, Object[] args) {\n            this.callTarget = target;\n            this.args = args;\n        }\n    }\n\n    static class InvokeNode extends AbstractInvokeNode {\n        final boolean tailPosition;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedTarget;\n        @CompilationFinal private CallTarget cachedTarget;\n        @CompilationFinal @Child private DirectCallNode directCallNode;\n        @CompilationFinal @Child private IndirectCallNode indirectCallNode;\n\n        InvokeNode(boolean tailPosition) {\n            this.tailPosition = tailPosition;\n        }\n\n        Object invoke(CallTarget target, Object[] args) {\n            return invoke(target, args, true);\n        }\n\n        Object invoke(CallTarget target, Object[] args, boolean allowTailCall) {\n            if (tailPosition && allowTailCall) {\n                throw new TailCallException(target, args);\n            } else {\n                if (!initialized) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    initialized = true;\n                    usingCachedTarget = true;\n                    cachedTarget = target;\n                    directCallNode = Truffle.getRuntime().createDirectCallNode(target);\n                }\n                while (true) {\n                    try {\n                        if (usingCachedTarget) {\n                            if (cachedTarget == target) {\n                                return directCallNode.call(args);\n                            }\n                            CompilerDirectives.transferToInterpreterAndInvalidate();\n                            usingCachedTarget = false;\n                            indirectCallNode = Truffle.getRuntime().createIndirectCallNode();\n                        }\n                        return indirectCallNode.call(target, args);\n                    } catch (TailCallException ex) {\n                        target = ex.callTarget;\n                        args = ex.args;\n                    }\n                }\n            }\n        }\n    }\n\n    private static MalFunction getMacroFn(MalEnv env, Object form) {\n        if (!(form instanceof MalList))\n            return null;\n        MalList list = (MalList)form;\n        if (!(list.head instanceof MalSymbol))\n            return null;\n        MalSymbol fnSym = (MalSymbol)list.head;\n        var obj = env.get(fnSym);\n        if (obj == null)\n            return null;\n        if (!(obj instanceof MalFunction))\n            return null;\n        MalFunction fn = (MalFunction)obj;\n        return fn.isMacro ? fn : null;\n    }\n\n    static Object macroexpand(InvokeNode invokeNode, MalEnv env, Object form) {\n        var fn = getMacroFn(env, form);\n        while (fn != null) {\n            MalList list = (MalList)form;\n            var args = new Object[(int)list.length];\n            args[0] = fn.closedOverEnv;\n            int i=1;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                args[i++] = list.head;\n                list = list.tail;\n            }\n            form = invokeNode.invoke(fn.callTarget, args, false);\n            fn = getMacroFn(env, form);\n        }\n        return form;\n    }\n\n    static class MacroexpandNode extends MalNode {\n        @Child private InvokeNode invokeNode = new InvokeNode(false);\n        private final Object body;\n\n        MacroexpandNode(MalList form) {\n            super(form);\n            this.body = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return macroexpand(invokeNode, env, body);\n        }\n    }\n\n    private static boolean isInlinableMacro(MalFunction fn) {\n        var meta = fn.getMeta();\n        if (meta == null || !(meta instanceof MalMap))\n            return false;\n        var inline = ((MalMap)meta).get(MalKeyword.INLINE_Q);\n        return Boolean.TRUE.equals(inline);\n    }\n\n    static class InlinedMacroNode extends MalNode {\n        @Child private DirectCallNode node;\n        InlinedMacroNode(Object form, CallTarget target) {\n            super(form);\n            node = Truffle.getRuntime().createDirectCallNode(target);\n        }\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return node.call();\n        }\n    }\n\n    static class ApplyNode extends MalNode {\n        final MalLanguage language;\n        final LexicalScope scope;\n        @Child private MalNode fnNode;\n        @Children private MalNode[] argNodes;\n        @Child private InvokeNode invokeNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private boolean usingCachedFn;\n        @CompilationFinal private MalFunction cachedFn;\n\n        ApplyNode(MalLanguage language, MalList list, boolean tailPosition, LexicalScope scope) {\n            super(list);\n            this.language = language;\n            this.scope = scope;\n            fnNode = formToNode(language, list.head, false, scope);\n            argNodes = new MalNode[list.length-1];\n            int i=0;\n            list = list.tail;\n            while (!list.isEmpty()) {\n                argNodes[i++] = formToNode(language, list.head, false, scope);\n                list = list.tail;\n            }\n            invokeNode = new InvokeNode(tailPosition);\n        }\n\n        @TruffleBoundary\n        private MalRootNode applyMacro(MalEnv env, MalFunction fn) {\n            Object[] args = new Object[argNodes.length+1];\n            args[0] = fn.closedOverEnv;\n            for (int i=0; i < argNodes.length; ++i) {\n                args[i+1] = argNodes[i].form;\n            }\n            // We should never throw a tail call during expansion!\n            Object form = invokeNode.invoke(fn.callTarget, args, false);\n            var result = macroexpand(invokeNode, env, form);\n            return new MalRootNode(language, result, env, invokeNode.tailPosition, scope);\n        }\n\n        @TruffleBoundary\n        private Object invokeMacro(MalRootNode macroNode) {\n            // Mal's macro semantics are... interesting. To preserve them in the\n            // general case, we must re-expand a macro each time it's applied.\n            // Executing the result means turning it into a Truffle AST, creating\n            // a CallTarget, calling it, and then throwing it away.\n            // This is TERRIBLE for performance! Truffle should not be used like this!\n            var target = Truffle.getRuntime().createCallTarget(macroNode);\n            return invokeNode.invoke(target, new Object[] {}, false);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var fn = (MalFunction)fnNode.executeGeneric(frame, env);\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                cachedFn = fn;\n                usingCachedFn = true;\n            }\n            if (usingCachedFn) {\n                if (fn != cachedFn) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    usingCachedFn = false;\n                } else {\n                    fn = cachedFn;\n                }\n            }\n            if (fn.isMacro) {\n                var expanded = applyMacro(env, fn);\n                if (isInlinableMacro(fn)) {\n                    CompilerDirectives.transferToInterpreterAndInvalidate();\n                    var newNode = expanded.body;\n                    this.replace(newNode);\n                    return newNode.executeGeneric(frame, env);\n                } else {\n                    return invokeMacro(expanded);\n                }\n            } else {\n                var args = new Object[argNodes.length+1];\n                args[0] = fn.closedOverEnv;\n                for (int i=0; i < argNodes.length; i++) {\n                    args[i+1] = argNodes[i].executeGeneric(frame, env);\n                }\n                return invokeNode.invoke(fn.callTarget, args, fn.canBeTailCalled);\n            }\n        }\n    }\n\n    static class DefNode extends MalNode {\n        private final MalSymbol symbol;\n        private final boolean macro;\n        private final LexicalScope scope;\n        @Child private MalNode valueNode;\n        @CompilationFinal private boolean initialized = false;\n        @CompilationFinal private EnvSlot slot;\n\n        DefNode(MalLanguage language, MalList list, LexicalScope scope) {\n            super(list);\n            this.symbol = (MalSymbol)list.tail.head;\n            this.macro = MalSymbol.DEFMACRO.equals(list.head);\n            this.scope = scope;\n            this.valueNode = formToNode(language, list.tail.tail.head, false, scope);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var value = valueNode.executeGeneric(frame, env);\n            if (macro) {\n                value = new MalFunction((MalFunction)value, true);\n            }\n            if (!initialized) {\n                CompilerDirectives.transferToInterpreterAndInvalidate();\n                initialized = true;\n                var slot = scope.getSlot(env, symbol);\n                if (slot != null && slot.height == 0) {\n                    this.slot = slot;\n                }\n            }\n            if (slot != null) {\n                env.set(slot, value);\n            } else {\n                env.set(symbol, value);\n            }\n            return value;\n        }\n    }\n\n    static class LetBindingNode extends Node {\n        private final EnvSlot slot;\n        @Child private MalNode valueNode;\n\n        LetBindingNode(MalLanguage language, MalSymbol symbol, Object valueForm, LexicalScope scope) {\n            this.slot = scope.allocateSlot(symbol);\n            this.valueNode = formToNode(language, valueForm, false, scope);\n        }\n\n        public void executeGeneric(VirtualFrame frame, MalEnv env) {\n            env.set(slot, valueNode.executeGeneric(frame, env));\n        }\n    }\n\n    static class LetNode extends MalNode {\n        private final LexicalScope scope;\n        @Children private LetBindingNode[] bindings;\n        @Child private MalNode bodyNode;\n\n        LetNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope outerScope) {\n            super(form);\n            var bindingForms = new ArrayList<Object>();\n            assert form.tail.head instanceof Iterable<?>;\n            ((Iterable<?>)form.tail.head).forEach(bindingForms::add);\n            bindings = new LetBindingNode[bindingForms.size()/2];\n            scope = new LexicalScope(outerScope);\n            for (int i=0; i < bindingForms.size(); i+=2) {\n                bindings[i/2] = new LetBindingNode(language, (MalSymbol)bindingForms.get(i), bindingForms.get(i+1), scope);\n            }\n            bodyNode = formToNode(language, form.tail.tail.head, tailPosition, scope);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv outerEnv) {\n            var innerEnv = new MalEnv(outerEnv, scope);\n            for (int i=0; i < bindings.length; i++) {\n                bindings[i].executeGeneric(frame, innerEnv);\n            }\n            return bodyNode.executeGeneric(frame, innerEnv);\n        }\n    }\n\n    /**\n     * Represents a form to be evaluated, together with an environment.\n     */\n    static class MalRootNode extends RootNode {\n        final Object form;\n        final MalEnv env;\n        @Child MalNode body;\n\n        MalRootNode(MalLanguage language, Object form, MalEnv env, boolean tailPosition, LexicalScope scope) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            // There's no stack to unwind at the top level, so\n            // a top-level form is never in tail position.\n            this.body = formToNode(language, form, tailPosition, scope);\n            this.env = env;\n        }\n\n        @Override\n        public Object execute(VirtualFrame frame) {\n            return body.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return Printer.prStr(form, true);\n        }\n    }\n\n    static class DoNode extends MalNode {\n        @Children private MalNode[] bodyNodes;\n\n        DoNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            bodyNodes = new MalNode[form.length-1];\n            int i = 0;\n            for (var f : form.tail) {\n                bodyNodes[i++] = formToNode(language, f, tailPosition && i == form.length-1, scope);\n            }\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            if (bodyNodes.length == 0) {\n                return MalNil.NIL;\n            }\n\n            for (int i=0; i < bodyNodes.length-1; i++) {\n                bodyNodes[i].executeGeneric(frame, env);\n            }\n            return bodyNodes[bodyNodes.length-1].executeGeneric(frame, env);\n        }\n    }\n\n    static class IfNode extends MalNode {\n        @Child private MalNode conditionNode;\n        @Child private MalNode trueNode;\n        @Child private MalNode falseNode;\n\n        IfNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            conditionNode = formToNode(language, form.tail.head, false, scope);\n            trueNode = formToNode(language, form.tail.tail.head, tailPosition, scope);\n            var falseForm = form.tail.tail.tail.head;\n            falseNode = falseForm == null ? null : formToNode(language, falseForm, tailPosition, scope);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            var val = conditionNode.executeGeneric(frame, env);\n            if (val == MalNil.NIL || Boolean.FALSE.equals(val)) {\n                if (falseNode == null) {\n                    return MalNil.NIL;\n                } else {\n                    return falseNode.executeGeneric(frame, env);\n                }\n            } else {\n                return trueNode.executeGeneric(frame, env);\n            }\n        }\n    }\n\n    static abstract class AbstractBindArgNode extends Node {\n        protected final int argPos;\n        protected final EnvSlot slot;\n\n        protected AbstractBindArgNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            this.argPos = argPos;\n            this.slot = scope.allocateSlot(symbol);\n        }\n\n        public abstract void execute(VirtualFrame frame, MalEnv env);\n    }\n\n    static class BindArgNode extends AbstractBindArgNode {\n\n        public BindArgNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            super(symbol, argPos, scope);\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            env.set(slot, frame.getArguments()[argPos]);\n        }\n    }\n\n    static class BindVarargsNode extends BindArgNode {\n        public BindVarargsNode(MalSymbol symbol, int argPos, LexicalScope scope) {\n            super(symbol, argPos, scope);\n        }\n\n        private MalList buildVarArgsList(Object[] args) {\n            MalList varArgs = MalList.EMPTY;\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            return varArgs;\n        }\n\n        @Override\n        public void execute(VirtualFrame frame, MalEnv env) {\n            //env.set(slot, buildVarArgsList(frame.getArguments()));\n            MalList varArgs = MalList.EMPTY;\n            var args = frame.getArguments();\n            for (int i=args.length-1; i >= argPos; --i) {\n                varArgs = varArgs.cons(args[i]);\n            }\n            //env.set(slot,  varArgs);\n            env.staticBindings[slot.slotNum] = varArgs;\n        }\n    }\n\n    /**\n     * Root node of a user-defined function, responsible for managing\n     * the environment when the function is invoked.\n     */\n    static class FnRootNode extends RootNode {\n        final MalList form;\n        final int numArgs;\n        final LexicalScope scope;\n        @Children AbstractBindArgNode[] bindNodes;\n        @Child MalNode bodyNode;\n\n        FnRootNode(MalLanguage language, MalList form, LexicalScope outerScope) {\n            super(language, new FrameDescriptor());\n            this.form = form;\n            var argNamesList = new ArrayList<MalSymbol>();\n            assert form.tail.head instanceof Iterable<?>;\n            var foundAmpersand = false;\n            for (var name : (Iterable<?>)form.tail.head) {\n                if (MalSymbol.AMPERSAND.equals(name)) {\n                    foundAmpersand = true;\n                } else {\n                    argNamesList.add((MalSymbol)name);\n                }\n            }\n            this.numArgs = foundAmpersand? -1 : argNamesList.size();\n            this.bindNodes = new AbstractBindArgNode[argNamesList.size()];\n            this.scope = new LexicalScope(outerScope);\n            for (int i=0; i < argNamesList.size(); i++) {\n                if (numArgs == -1 && i == argNamesList.size()-1) {\n                    bindNodes[i] = new BindVarargsNode(argNamesList.get(i), i+1, scope);\n                } else {\n                    bindNodes[i] = new BindArgNode(argNamesList.get(i), i+1, scope);\n                }\n            }\n            this.bodyNode = formToNode(language, form.tail.tail.head, true, scope);\n        }\n\n        @ExplodeLoop\n        @Override\n        public Object execute(VirtualFrame frame) {\n            var env = new MalEnv((MalEnv)frame.getArguments()[0], scope);\n            for (int i=0; i < bindNodes.length; i++) {\n                bindNodes[i].execute(frame, env);\n            }\n            return bodyNode.executeGeneric(frame, env);\n        }\n\n        @Override\n        public String toString() {\n            return form.toString();\n        }\n    }\n\n    /**\n     * Node representing a (fn* ...) form.\n     */\n    static class FnNode extends MalNode {\n        final FnRootNode fnRoot;\n        final RootCallTarget fnCallTarget;\n\n        FnNode(MalLanguage language, MalList form, LexicalScope scope) {\n            super(form);\n            fnRoot = new FnRootNode(language, form, scope);\n            this.fnCallTarget = Truffle.getRuntime().createCallTarget(fnRoot);\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return new MalFunction(fnCallTarget, env, fnRoot.numArgs);\n        }\n    }\n\n    static class QuoteNode extends MalNode {\n        final Object quoted;\n\n        QuoteNode(MalLanguage language, MalList form) {\n            super(form);\n            quoted = form.tail.head;\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            return quoted;\n        }\n    }\n\n    static class TryNode extends MalNode {\n        @Child private MalNode tryBody;\n        @Child private MalNode catchBody;\n        final EnvSlot exSlot;\n        final LexicalScope catchScope;\n\n        TryNode(MalLanguage language, MalList form, boolean tailPosition, LexicalScope scope) {\n            super(form);\n            var tryForm = form.tail.head;\n            var catchForm = (MalList)form.tail.tail.head;\n            // We don't allow tail calls inside a try body, because\n            // they'd get thrown past the catch that should catch subsequent failures.\n            this.tryBody = formToNode(language, tryForm, false, scope);\n            if (catchForm != null && MalSymbol.CATCH.equals(catchForm.head)) {\n                catchScope = new LexicalScope(scope);\n                var exSymbol = (MalSymbol)catchForm.tail.head;\n                exSlot = catchScope.allocateSlot(exSymbol);\n                catchBody = formToNode(language, catchForm.tail.tail.head, tailPosition, catchScope);\n            } else {\n                catchScope = null;\n                exSlot = null;\n            }\n        }\n\n        @Override\n        public Object executeGeneric(VirtualFrame frame, MalEnv env) {\n            try {\n                return tryBody.executeGeneric(frame, env);\n            } catch (MalException ex) {\n                if (catchBody == null) {\n                    throw ex;\n                }\n                var catchEnv = new MalEnv(env, catchScope);\n                catchEnv.set(exSlot, ex.obj);\n                return catchBody.executeGeneric(frame, catchEnv);\n            }\n        }\n    }\n\n    final static class MalContext {\n        final MalEnv globalEnv;\n        final LexicalScope globalScope;\n        final Iterable<Scope> topScopes;\n        final PrintStream out;\n        final BufferedReader in;\n\n        MalContext(MalLanguage language) {\n            globalEnv = Core.newGlobalEnv(MalLanguage.class, language);\n            globalScope = new LexicalScope();\n            topScopes = Collections.singleton(Scope.newBuilder(\"global\", globalEnv).build());\n            out = System.out;\n            in = new BufferedReader(new InputStreamReader(System.in));\n        }\n    }\n\n    @TruffleLanguage.Registration(\n            id=LANGUAGE_ID,\n            name=LANGUAGE_ID,\n            defaultMimeType = \"application/x-\"+LANGUAGE_ID,\n            characterMimeTypes = \"application/x-\"+LANGUAGE_ID)\n    public final static class MalLanguage extends TruffleLanguage<MalContext> implements IMalLanguage {\n        @Override\n        protected MalContext createContext(Env env) {\n            return new MalContext(this);\n        }\n\n        @Override\n        public CallTarget evalForm(Object form) {\n            var ctx = getCurrentContext(MalLanguage.class);\n            var root = new MalRootNode(this, form, ctx.globalEnv, false, ctx.globalScope);\n            return Truffle.getRuntime().createCallTarget(root);\n        }\n\n        @Override\n        public AbstractInvokeNode invokeNode() {\n            return new InvokeNode(false);\n        }\n\n        @Override\n        protected CallTarget parse(ParsingRequest request) throws Exception {\n            Source source = request.getSource();\n            String s = source.getCharacters().toString();\n            return evalForm(Reader.readStr(s));\n        }\n\n        @Override\n        protected Iterable<Scope> findTopScopes(MalContext context) {\n            return context.topScopes;\n        }\n\n        @Override\n        public PrintStream out() {\n            return getCurrentContext(MalLanguage.class).out;\n        }\n\n        @Override\n        public BufferedReader in() {\n            return getCurrentContext(MalLanguage.class).in;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/jq/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n#########################################################\n# Specific implementation requirements\n#########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install jq\n"
  },
  {
    "path": "impls/jq/Makefile",
    "content": "all:\n\nclean:\n\trm -fr .mypy_cache/\n\ncheck:\n\tflake8 run\n\tpylint run\n\tmypy run\n\n.PHONY: all clean check\n"
  },
  {
    "path": "impls/jq/core.jq",
    "content": "include \"utils\";\ninclude \"printer\";\ninclude \"reader\";\n\ndef core_identify:\n    {\n        \"+\": {\n            kind: \"fn\", # native function\n            inputs: 2,\n            function: \"number_add\"\n        },\n        \"-\": {\n            kind: \"fn\", # native function\n            inputs: 2,\n            function: \"number_sub\"\n        },\n        \"*\": {\n            kind: \"fn\", # native function\n            inputs: 2,\n            function: \"number_mul\"\n        },\n        \"/\": {\n            kind: \"fn\", # native function\n            inputs: 2,\n            function: \"number_div\"\n        },\n        \"eval\": {\n            kind: \"fn\",\n            inputs: 1,\n            function: \"eval\"\n        },\n        \"env\": {\n            kind: \"fn\",\n            function: \"env\",\n            inputs: 0\n        },\n        \"prn\": {\n            kind: \"fn\",\n            function: \"prn\",\n            inputs: -1\n        },\n        \"pr-str\": {\n            kind: \"fn\",\n            function: \"pr-str\",\n            inputs: -1\n        },\n        \"str\": {\n            kind: \"fn\",\n            function: \"str\",\n            inputs: -1\n        },\n        \"println\": {\n            kind: \"fn\",\n            function: \"println\",\n            inputs: -1\n        },\n        \"list\": {\n            kind: \"fn\",\n            function: \"list\",\n            inputs: -1\n        },\n        \"list?\": {\n            kind: \"fn\",\n            function: \"list?\",\n            inputs: 1\n        },\n        \"empty?\": {\n            kind: \"fn\",\n            function: \"empty?\",\n            inputs: 1\n        },\n        \"count\": {\n            kind: \"fn\",\n            function: \"count\",\n            inputs: 1\n        },\n        \"=\": {\n            kind: \"fn\",\n            function: \"=\",\n            inputs: 2\n        },\n        \"<\": {\n            kind: \"fn\",\n            function: \"<\",\n            inputs: 2\n        },\n        \"<=\": {\n            kind: \"fn\",\n            function: \"<=\",\n            inputs: 2\n        },\n        \">\": {\n            kind: \"fn\",\n            function: \">\",\n            inputs: 2\n        },\n        \">=\": {\n            kind: \"fn\",\n            function: \">=\",\n            inputs: 2\n        },\n        \"read-string\": {\n            kind: \"fn\",\n            function: \"read-string\",\n            inputs: 1\n        },\n        \"slurp\": {\n            kind: \"fn\",\n            function: \"slurp\",\n            inputs: 1\n        },\n        \"atom\": {\n            kind: \"fn\",\n            function: \"atom\",\n            inputs: 1\n        },\n        \"atom?\": {\n            kind: \"fn\",\n            function: \"atom?\",\n            inputs: 1\n        },\n        \"deref\": {\n            kind: \"fn\",\n            function: \"deref\",\n            inputs: 1\n        },\n        \"reset!\": { # defined in interp\n            kind: \"fn\",\n            function: \"reset!\",\n            inputs: 2\n        },\n        \"swap!\": { # defined in interp\n            kind: \"fn\",\n            function: \"swap!\",\n            inputs: -3\n        },\n        \"cons\": {\n            kind: \"fn\",\n            function: \"cons\",\n            inputs: 2\n        },\n        \"concat\": {\n            kind: \"fn\",\n            function: \"concat\",\n            inputs: -1\n        },\n        \"vec\": {\n            kind: \"fn\",\n            function: \"vec\",\n            inputs: 1\n        },\n        \"nth\": {\n            kind: \"fn\",\n            function: \"nth\",\n            inputs: 2\n        },\n        \"first\": {\n            kind: \"fn\",\n            function: \"first\",\n            inputs: 1\n        },\n        \"rest\": {\n            kind: \"fn\",\n            function: \"rest\",\n            inputs: 1\n        },\n        \"throw\": {\n            kind: \"fn\",\n            function: \"throw\",\n            inputs: 1\n        },\n        \"apply\": { # defined in interp\n            kind: \"fn\",\n            function: \"apply\",\n            inputs: -3\n        },\n        \"map\": { # defined in interp\n            kind: \"fn\",\n            function: \"map\",\n            inputs: 2\n        },\n        \"nil?\": {\n            kind: \"fn\",\n            function: \"nil?\",\n            inputs: 1\n        },\n        \"true?\": {\n            kind: \"fn\",\n            function: \"true?\",\n            inputs: 1\n        },\n        \"false?\": {\n            kind: \"fn\",\n            function: \"false?\",\n            inputs: 1\n        },\n        \"symbol\": {\n            kind: \"fn\",\n            function: \"symbol\",\n            inputs: 1\n        },\n        \"symbol?\": {\n            kind: \"fn\",\n            function: \"symbol?\",\n            inputs: 1\n        },\n        \"keyword\": {\n            kind: \"fn\",\n            function: \"keyword\",\n            inputs: 1\n        },\n        \"keyword?\": {\n            kind: \"fn\",\n            function: \"keyword?\",\n            inputs: 1\n        },\n        \"vector\": {\n            kind: \"fn\",\n            function: \"vector\",\n            inputs: -1\n        },\n        \"vector?\": {\n            kind: \"fn\",\n            function: \"vector?\",\n            inputs: 1\n        },\n        \"sequential?\": {\n            kind: \"fn\",\n            function: \"sequential?\",\n            inputs: 1\n        },\n        \"hash-map\": {\n            kind: \"fn\",\n            function: \"hash-map\",\n            inputs: -1\n        },\n        \"map?\": {\n            kind: \"fn\",\n            function: \"map?\",\n            inputs: 1\n        },\n        \"assoc\": {\n            kind: \"fn\",\n            function: \"assoc\",\n            inputs: -2\n        },\n        \"dissoc\": {\n            kind: \"fn\",\n            function: \"dissoc\",\n            inputs: -2\n        },\n        \"get\": {\n            kind: \"fn\",\n            function: \"get\",\n            inputs: 2\n        },\n        \"contains?\": {\n            kind: \"fn\",\n            function: \"contains?\",\n            inputs: 2\n        },\n        \"keys\": {\n            kind: \"fn\",\n            function: \"keys\",\n            inputs: 1\n        },\n        \"vals\": {\n            kind: \"fn\",\n            function: \"vals\",\n            inputs: 1\n        },\n        \"string?\": {\n            kind: \"fn\",\n            function: \"string?\",\n            inputs: 1\n        },\n        \"fn?\": {\n            kind: \"fn\",\n            function: \"fn?\",\n            inputs: 1\n        },\n        \"number?\": {\n            kind: \"fn\",\n            function: \"number?\",\n            inputs: 1\n        },\n        \"macro?\": {\n            kind: \"fn\",\n            function: \"macro?\",\n            inputs: 1\n        },\n        \"readline\": {\n            kind: \"fn\",\n            function: \"readline\",\n            inputs: 1\n        },\n        \"time-ms\": {\n            kind: \"fn\",\n            function: \"time-ms\",\n            inputs: 0\n        },\n        \"meta\": {\n            kind: \"fn\",\n            function: \"meta\",\n            inputs: 1\n        },\n        \"with-meta\": {\n            kind: \"fn\",\n            function: \"with-meta\",\n            inputs: 2\n        },\n        \"seq\": {\n            kind: \"fn\",\n            function: \"seq\",\n            inputs: 1\n        },\n        \"conj\": {\n            kind: \"fn\",\n            function: \"conj\",\n            inputs: -3\n        }\n    };\n\ndef vec2list(obj):\n    if obj.kind == \"list\" then\n        obj.value | map(vec2list(.)) | wrap(\"list\")\n    else \n        if obj.kind == \"vector\" then\n            obj.value | map(vec2list(.)) | wrap(\"list\")\n        else\n            if obj.kind == \"hashmap\" then\n                obj.value | map_values(.value |= vec2list(.)) | wrap(\"hashmap\")\n            else\n                obj\n            end\n        end\n    end;\n\ndef make_sequence:\n    . as $dot\n    | if .value|length == 0 then null | wrap(\"nil\") else\n        (\n            select(.kind == \"string\") | .value | split(\"\") | map(wrap(\"string\"))\n        ) // (\n            select(.kind == \"list\" or .kind == \"vector\") | .value\n        ) // jqmal_error(\"cannot make sequence from \\(.kind)\") | wrap(\"list\")\n    end;\n\ndef core_interp(arguments; env):\n    (\n        select(.function == \"number_add\") |\n        arguments | map(.value) | .[0] + .[1] | wrap(\"number\")\n    ) // (\n        select(.function == \"number_sub\") |\n        arguments | map(.value) | .[0] - .[1] | wrap(\"number\")\n    ) // (\n        select(.function == \"number_mul\") |\n        arguments | map(.value) | .[0] * .[1] | wrap(\"number\")\n    ) // (\n        select(.function == \"number_div\") |\n        arguments | map(.value) | .[0] / .[1] | wrap(\"number\")\n    ) // (\n        select(.function == \"env\") |\n        env | tojson | wrap(\"string\")\n    ) // (\n        select(.function == \"prn\") |\n        arguments | map(pr_str(env; {readable: true})) | join(\" \") | _display | null | wrap(\"nil\")\n    ) // (\n        select(.function == \"pr-str\") |\n        arguments | map(pr_str(env; {readable: true})) | join(\" \") |  wrap(\"string\")\n    ) // (\n        select(.function == \"str\") |\n        arguments | map(pr_str(env; {readable: false})) | join(\"\") |  wrap(\"string\")\n    ) // (\n        select(.function == \"println\") |\n        arguments | map(pr_str(env; {readable: false})) | join(\" \") | _display | null | wrap(\"nil\")\n    ) // (\n        select(.function == \"list\") |\n        arguments | wrap(\"list\")\n    ) // (\n        select(.function == \"list?\") | null | wrap(arguments | first.kind == \"list\" | tostring)\n    ) // (\n        select(.function == \"empty?\") | null | wrap(arguments|first.value | length == 0 | tostring)\n    ) // (\n        select(.function == \"count\") | arguments|first.value | length | wrap(\"number\")\n    ) // (\n        select(.function == \"=\") | null | wrap(vec2list(arguments[0]) == vec2list(arguments[1]) | tostring)\n    ) // (\n        select(.function == \"<\") | null | wrap(arguments[0].value < arguments[1].value | tostring)\n    ) // (\n        select(.function == \"<=\") | null | wrap(arguments[0].value <= arguments[1].value | tostring)\n    ) // (\n        select(.function == \">\") | null | wrap(arguments[0].value > arguments[1].value | tostring)\n    ) // (\n        select(.function == \">=\") | null | wrap(arguments[0].value >= arguments[1].value | tostring)\n    ) // (\n        select(.function == \"slurp\") | arguments[0].value | slurp | wrap(\"string\")\n    ) // (\n        select(.function == \"read-string\") | arguments | first.value | read_form\n    ) // (\n        select(.function == \"atom?\") | null | wrap(arguments | first.kind == \"atom\" | tostring)\n    ) // (\n        select(.function == \"cons\") | ([arguments[0]] + arguments[1].value) | wrap(\"list\")\n    ) // (\n        select(.function == \"concat\") | arguments | map(.value) | (add//[]) | wrap(\"list\")\n    ) // (\n        select(.function == \"vec\") | {kind:\"vector\", value:arguments[0].value}\n    ) // (\n        select(.function == \"nth\")\n            | arguments[0].value as $lst\n            | arguments[1].value as $idx\n            | if ($lst|length < $idx) or ($idx < 0) then\n                jqmal_error(\"index out of range\")\n              else\n                $lst[$idx]\n              end\n    ) // (\n        select(.function == \"first\") | arguments[0].value | first // {kind:\"nil\"}\n    ) // (\n        select(.function == \"rest\") | arguments[0]?.value?[1:]? // [] | wrap(\"list\")\n    ) // (\n        select(.function == \"throw\") | jqmal_error(arguments[0] | tojson)\n    ) // (\n        select(.function == \"nil?\") | null | wrap((arguments[0].kind == \"nil\") | tostring)\n    ) // (\n        select(.function == \"true?\") | null | wrap((arguments[0].kind == \"true\") | tostring)\n    ) // (\n        select(.function == \"false?\") | null | wrap((arguments[0].kind == \"false\") | tostring)\n    ) // (\n        select(.function == \"symbol?\") | null | wrap((arguments[0].kind == \"symbol\") | tostring)\n    ) // (\n        select(.function == \"symbol\") | arguments[0].value | wrap(\"symbol\")\n    ) // (\n        select(.function == \"keyword\") | arguments[0].value | wrap(\"keyword\")\n    ) // (\n        select(.function == \"keyword?\") | null | wrap((arguments[0].kind == \"keyword\") | tostring)\n    ) // (\n        select(.function == \"vector\") | arguments | wrap(\"vector\")\n    ) // (\n        select(.function == \"vector?\") | null | wrap((arguments[0].kind == \"vector\") | tostring)\n    ) // (\n        select(.function == \"sequential?\") | null | wrap((arguments[0].kind == \"vector\" or arguments[0].kind == \"list\") | tostring)\n    ) // (\n        select(.function == \"hash-map\") |\n            if (arguments|length) % 2 == 1 then\n                jqmal_error(\"Odd number of arguments to hash-map\")\n            else\n                [ arguments | \n                    nwise(2) | \n                    try {\n                        key: (.[0] | extract_string),\n                        value: {\n                            kkind: .[0].kind,\n                            value: .[1]\n                        }\n                    }\n                ] | from_entries | wrap(\"hashmap\")\n            end\n    ) // (\n        select(.function == \"map?\") | null | wrap((arguments[0].kind == \"hashmap\") | tostring)\n    ) // (\n        select(.function == \"assoc\") |\n            if (arguments|length) % 2 == 0 then\n                jqmal_error(\"Odd number of key-values to assoc\")\n            else\n                arguments[0].value + ([ arguments[1:] | \n                    nwise(2) | \n                    try {\n                        key: (.[0] | extract_string),\n                        value: {\n                            kkind: .[0].kind,\n                            value: .[1]\n                        }\n                    }\n                ] | from_entries) | wrap(\"hashmap\")\n            end\n    ) // (\n        select(.function == \"dissoc\") | \n            arguments[1:] | map(.value) as $keynames |\n            arguments[0].value | with_entries(select(.key as $k | $keynames | contains([$k]) | not)) | wrap(\"hashmap\")\n    ) // (\n        select(.function == \"get\") | arguments[0].value[arguments[1].value].value // {kind:\"nil\"}\n    ) // (\n        select(.function == \"contains?\") | null | wrap((arguments[0].value | has(arguments[1].value)) | tostring)\n    ) // (\n        select(.function == \"keys\") | arguments[0].value | with_entries(.value as $v | .key as $k | {key: $k, value: {value: $k, kind: $v.kkind}}) | to_entries | map(.value) | wrap(\"list\")\n    ) // (\n        select(.function == \"vals\") | arguments[0].value | map(.value) | to_entries | map(.value) | wrap(\"list\")\n    ) // (\n        select(.function == \"string?\") | null | wrap((arguments[0].kind == \"string\") | tostring)\n    ) // (\n        select(.function == \"fn?\") | null | wrap((arguments[0].kind == \"fn\" or (arguments[0].kind == \"function\" and (arguments[0].is_macro|not))) | tostring)\n    ) // (\n        select(.function == \"number?\") | null | wrap((arguments[0].kind == \"number\") | tostring)\n    ) // (\n        select(.function == \"macro?\") | null | wrap((arguments[0].is_macro == true) | tostring)\n    ) // (\n        select(.function == \"readline\") | arguments[0].value | __readline | wrap(\"string\")\n    ) // (\n        select(.function == \"time-ms\") | now * 1000 | wrap(\"number\")\n    ) // (\n        select(.function == \"meta\") | arguments[0].meta // {kind:\"nil\"}\n    ) // (\n        select(.function == \"with-meta\") | arguments[0] | .meta |= arguments[1]\n    ) // (\n        select(.function == \"seq\") | arguments[0] | make_sequence\n    ) // (\n        select(.function == \"conj\")\n            | arguments[0] as $orig\n            | arguments[1:] as $stuff\n            | if $orig.kind == \"list\" then\n                [ $stuff|reverse[], $orig.value[] ] | wrap(\"list\")\n              else\n                [ $orig.value[], $stuff[] ] | wrap(\"vector\")\n              end\n    ) // jqmal_error(\"Unknown native function \\(.function)\");\n"
  },
  {
    "path": "impls/jq/docs/impl-notes.md",
    "content": "# General Implementation Notes\n\nThis document contains notes on the jq implementation, describing the deviations from the MAL specification and implementation details where necessary.\n\n## Main Deviations per Step\n\n### Step 0\nAs jq lacks a way to input free-form data on-demand, the REPL is implemented using a wrapper around the jq interpreter, which intercepts requests from our implementation and feeds the result back to jq as JSON; see the `__readline` function in [utils.jq](../utils.jq), and its implementation in [the wrapper](../run).\n\nAll further free-form I/O primitives are implemented in a similar way.\n\n### Step 1\nThere is not much deviation from the MAL process in this step, MAL data are implemented as JSON objects with two fields: `kind` and `value` (see [reader.jq](../reader.jq)).\n\n### Step 2\njq cannot store functions as values, and so we are forced to represent them using their names and a large switch-case structure (`select()` in jq).\nThe environment is simply modelled as a JSON object, and functions are represented as `{ \"kind\": \"fn\", \"inputs\": n, \"function\": name }` where `n` is the number of arguments the function takes and `name` is the name of the function to be handled by the switch-case structure (in `interpret()` at this stage).\n\n### Step 3\nThe second of three environment implementations is introduced here, where an environment is an optional parent environment (which corresponds to the `outer` environment concept in the guide), and the environment from the previous step. Two convenience functions are introduced to handle the environment operations: `env-get` and `env-set`.\n\nThe forms `let*` and `def!` are implemented mostly as described in the guide, with `let*` utilizing a left-associative fold (`reduce` in jq) to build the intermediate environment up; which is discarded after the fold is done.\n\n### Step 4\nIn this step, environments grow yet another field `fallback`, which is used to add a second environment chain to non-top-level environments. This is used to implement functions that refer to unbound symbols in their body (this could be the function itself, or any other symbol defined later in the parent environment) - this is necessary as there are no variable references or mutable variables in jq (and thus we cannot modify an environment in-place).\n\nDue to this limitation, the `fn*` form is implemented by:\n- Recording the \"free\" symbols in the function body (which are not defined in the function's environment)\n- And storing a copy of the current environment in the function itself (for closures)\n\nThe `interpret` function also gets an `_eval` callback parameter, which is used to evaluate the function body after a new environment is created with the correct bindings.\n\nEverything else is largely the same as in the guide.\n\n### Step 5\nTail-calls are implemented as a (fairly complex) fixpoint iteration in the `EVAL` function; this \"loop\" takes an object of the form `{ast, env, ret_env, finish, cont}` and \"iteratively\" performs an evaluation step with `.ast` and `.env` (which is updated on every \"iteration\") until `.cont` is `false` (which is driven by the `finish` \"flag\"). Upon completion, the resulting environment is pulled from `ret_env` and the fixpoint is returned as the evaluation result.\n\nThis is largely due to the lack of \"actual\" loops in jq, a computation of this form can also be expressed as a reduction over an infinite generator, but the fixpoint iteration is more straightforward to implement (as jq has a built-in `recurse` function).\n\n### Step 6\nThis step deviates from the guide _significantly_, in the implementation of atoms; since jq does not have mutable variables (_or_ global variables), we cannot implement atoms in any simple way.\n\nFirst, let's go over atom identity and creation; this implementation \"stamps\" atoms with their creation timestamp (the result of `now | tostring`), which is used as a unique identifier for the atom.\nThe fixpoint calculation of `EVAL` (and `TCOWrap` in particular) is adjusted to handle atoms \"leaking\" into the global environment (as they are not bound to any environment in reality, which differs from our implementation where atoms are bound to the active environment they were created in).\n\nThe `interpret` function is also moved to a separate [interp.jq](../interp.jq) file, as it can be shared between steps going forward, and will also grow in complexity due to the introduction of atoms.\n\n### Step 7\nThis step does not deviate from the guide.\n\n### Step 8\nThis step does not deviate from the guide.\n\n### Step 9\nThis step uses the native jq exception handling mechanism `try ... catch ...`, and follows the guide closely (and so no significant deviations are present).\n\n### Step A\nThis step does not deviate from the guide.\n"
  },
  {
    "path": "impls/jq/env.jq",
    "content": "include \"utils\";\n\ndef childEnv(binds; exprs):\n    {\n        parent: .,\n        fallback: null,\n        environment: [binds, exprs] | transpose | (\n            . as $dot | reduce .[] as $item (\n                { value: [], seen: false, name: null, idx: 0 };\n                if $item[1] != null then\n                    if .seen then\n                        {\n                            value: (.value[1:-1] + (.value|last[1].value += [$item[1]])),\n                            seen: true,\n                            name: .name\n                        }\n                    else\n                        if $item[0] == \"&\" then\n                            $dot[.idx+1][0] as $name | {\n                                value: (.value + [[$name, {kind:\"list\", value: [$item[1]]}]]),\n                                seen: true,\n                                name: $name\n                            }\n                        else\n                            {\n                                value: (.value + [$item]),\n                                seen: false,\n                                name: null\n                            }\n                        end\n                    end | (.idx |= .idx + 1)\n                else\n                    if $item[0] == \"&\" then\n                            $dot[.idx+1][0] as $name | {\n                                value: (.value + [[$name, {kind:\"list\", value: []}]]),\n                                seen: true,\n                                name: $name\n                            }\n                    else . end\n                end\n            )\n        ) | .value | map({(.[0]): .[1]}) | add \n    };\n\ndef env_multiset(fn):\n    .environment += (reduce fn.names[] as $key(.environment; .[$key] |= fn));\n\ndef env_set($key; $value):\n    (if $value.kind == \"function\" or $value.kind == \"atom\" then\n        # inform the function/atom of its names\n        ($value |\n        if $value.kind == \"atom\" then\n            # check if the one we have is newer\n            ($key | env_get(env)) as $ours |\n            if $ours.last_modified > $value.last_modified then\n                $ours\n            else\n                # update modification timestamp\n                $value | .last_modified |= now\n            end\n        else\n            .\n        end) |\n        .names += [$key] |\n        .names |= unique\n\n    else \n        $value\n    end) as $value |\n    # merge together, as .environment[$key] |= value does not work\n    .environment += (.environment | .[$key] |= $value);\n\ndef env_dump_keys:\n    def _dump1:\n        .environment // {} | keys;\n    if . == null then [] else\n        if .parent == null then\n            (\n                _dump1 +\n                (.fallback | env_dump_keys)\n            )\n        else\n            (\n                _dump1 +\n                (.parent | env_dump_keys) +\n                (.fallback | env_dump_keys)\n            )\n        end | unique\n    end;\n\n# Helper for env_get.\ndef env_find(env):\n    if env.environment[.] == null then\n        if env.parent then\n            env_find(env.parent) // if env.fallback then env_find(env.fallback) else null end\n        else\n            null\n        end\n    else\n        env\n    end;\n\ndef env_get(env):\n    # key -> value or null\n    . as $key | env_find(env).environment[$key] |\n    if . != null and .kind == \"atom\" then\n        ($key | env_find(env.parent).environment[$key]) as $possibly_newer |\n        if $possibly_newer.identity == .identity\n            and $possibly_newer.last_modified > .last_modified\n        then\n            $possibly_newer\n        end\n    end;\n\ndef env_set(env; $key; $value):\n    (if $value.kind == \"function\" then\n        # inform the function/atom of its names\n        $value | (.names += [$key]) | (.names |= unique)\n    else \n        $value\n    end) as $value | {\n        parent: env.parent,\n        environment: ((env.environment // jqmal_error(\"Environment empty in \\(env | keys)\")) + (env.environment | .[$key] |= $value)), # merge together, as env.environment[key] |= value does not work\n        fallback: env.fallback\n    };\n\ndef wrapEnv(atoms):\n    {\n        replEnv: .,\n        currentEnv: .,\n        atoms: atoms,\n        isReplEnv: true\n    };\n\ndef wrapEnv(replEnv; atoms):\n    {\n        replEnv: replEnv,\n        currentEnv: .,\n        atoms: atoms, # id -> value\n        isReplEnv: (replEnv == .) # should we allow separate copies?\n    };\n\ndef unwrapReplEnv:\n    .replEnv;\n\ndef unwrapCurrentEnv:\n    .currentEnv;\n\ndef env_set_(env; key; value):\n    if env.currentEnv != null then\n        # Moving the common env_set before the if breaks something. ?\n        if env.isReplEnv then\n            env_set(env.currentEnv; key; value) | wrapEnv(env.atoms)\n        else\n            env_set(env.currentEnv; key; value) | wrapEnv(env.replEnv; env.atoms)\n        end\n    else\n        env_set(env; key; value)\n    end;\n\ndef addToEnv(name):\n    # { expr, env } -> { same expr, new env }\n    .expr as $value |\n    .env |= (\n        . as $rawEnv |\n        if .isReplEnv then\n            env_set_(.currentEnv; name; $value) | wrapEnv($rawEnv.atoms)\n        else\n            env_set_(.currentEnv; name; $value) | wrapEnv($rawEnv.replEnv; $rawEnv.atoms)\n        end);\n\ndef _env_remove_references(refs):\n    if . != null then\n        if .environment == null then\n            debug(\"This one broke the rules, officer: \\(.)\")\n        else\n            {\n                environment: (.environment | to_entries | map(select(.key as $key | refs | contains([$key]) | not)) | from_entries),\n                parent: (.parent | _env_remove_references(refs)),\n                fallback: (.fallback | _env_remove_references(refs))\n            }\n        end\n    else . end;\n\ndef env_remove_references(refs):\n    . as $env \n    | if (refs|length == 0) then\n        # optimisation: most functions are purely lexical\n        $env\n    else \n        if has(\"replEnv\") then\n            .currentEnv |= _env_remove_references(refs)\n        else\n            _env_remove_references(refs)\n        end\n    end;\n"
  },
  {
    "path": "impls/jq/interp.jq",
    "content": "include \"utils\";\ninclude \"core\";\ninclude \"env\";\ninclude \"printer\";\n\ndef arg_check(args):\n    if .inputs < 0 then\n        if (abs(.inputs) - 1) > (args | length) then\n            jqmal_error(\"Invalid number of arguments (expected at least \\(abs(.inputs) - 1), got \\(args|length))\")\n        else\n            .\n        end\n    else if .inputs != (args|length) then\n        jqmal_error(\"Invalid number of arguments (expected \\(.inputs), got \\(args|length))\")\n    else\n        .\n    end end;\n\ndef extractReplEnv(env):\n    env | .replEnv // .;\n\ndef extractEnv(env):\n    env | .currentEnv // .;\n\ndef updateReplEnv(renv):\n    def findpath:\n        if .env.parent then\n            .path += [\"parent\"] |\n            .env |= .parent |\n            findpath\n        else\n            .path\n        end;\n    ({ env: ., path: [] } | findpath) as $path |\n    setpath($path; renv);\n\ndef extractCurrentReplEnv(env):\n    def findpath:\n        if .env.parent then\n            .path += [\"parent\"] |\n            .env |= .parent |\n            findpath\n        else\n            .path\n        end;\n    if env.currentEnv != null then\n        ({ env: env.currentEnv, path: [] } | findpath) as $path |\n            env.currentEnv | getpath($path)\n    else\n        env\n    end;\n\ndef extractAtoms(env):\n    env.atoms // {};\n\ndef addFrees(newEnv; frees):\n    . as $env\n    | reduce frees[] as $free (\n        $env;\n        . as $dot\n        | extractEnv(newEnv) as $env\n        | ($free | env_get($env)) as $lookup\n        | if $lookup != null then\n            env_set_(.; $free; $lookup)\n          else\n            .\n          end)\n    | . as $env\n    | $env;\n\ndef interpret(arguments; env; _eval):\n    extractReplEnv(env) as $replEnv |\n    extractAtoms(env) as $envAtoms |\n    (if $DEBUG then debug(\"INTERP: \\(pr_str(env))\") end) |\n    (select(.kind == \"fn\") |\n        arg_check(arguments) | \n            (select(.function == \"eval\") | \n                # special function\n                { expr: arguments[0], env: $replEnv|wrapEnv($replEnv; $envAtoms) }\n                | _eval\n                | .env as $xenv\n                | extractReplEnv($xenv) as $xreplenv\n                | setpath(\n                    [\"env\", \"currentEnv\"];\n                    extractEnv(env) | updateReplEnv($xreplenv))\n            ) //\n            (select(.function == \"reset!\") | \n                # env modifying function\n                arguments[0].identity as $id |\n                ($envAtoms | setpath([$id]; arguments[1])) as $envAtoms |\n                arguments[1] |\n                {expr:., env: (env | setpath([\"atoms\"]; $envAtoms))}\n            ) //\n            (select(.function == \"swap!\") | \n                # env modifying function\n                arguments[0].identity as $id |\n                $envAtoms[$id] as $initValue |\n                arguments[1] as $function |\n                ([$initValue] + arguments[2:]) as $args |\n                ($function | interpret($args; env; _eval)) as $newEnvValue |\n                ($envAtoms | setpath([$id]; $newEnvValue.expr)) as $envAtoms |\n                $newEnvValue.expr |\n                {expr:., env:(env | setpath([\"atoms\"]; $envAtoms))}\n            ) // (select(.function == \"atom\") |\n                (now|tostring) as $id |\n                {kind: \"atom\", identity: $id} as $value |\n                ($envAtoms | setpath([$id]; arguments[0])) as $envAtoms |\n                $value | {expr:., env:(env | setpath([\"atoms\"]; $envAtoms))}\n            ) // (select(.function == \"deref\") |\n                $envAtoms[arguments[0].identity] | {expr:., env:env}\n            ) //\n            (select(.function  == \"apply\") |\n                # (apply F ...T A) -> (F ...T ...A)\n                arguments as $args\n                | ($args|first) as $F\n                | ($args|last.value) as $A\n                | $args[1:-1] as $T\n                | $F | interpret([$T[], $A[]]; env; _eval)\n            ) //\n            (select(.function  == \"map\") |\n                arguments\n                | first as $F\n                | last.value as $L\n                | (reduce $L[] as $elem (\n                    {env: env, val: []};\n                    . as $dot |\n                    ($F | interpret([$elem]; $dot.env; _eval)) as $val |\n                    {\n                        val: (.val + [$val.expr]),\n                        env: (.env | setpath([\"atoms\"]; $val.env.atoms))\n                    }\n                  )) as $ex\n                | $ex.val | wrap(\"list\") | {expr:., env:$ex.env}\n            ) //\n                (core_interp(arguments; env) | {expr:., env:env})\n    ) //\n    (select(.kind == \"function\") as $fn |\n        # todo: arg_check\n        (.body | pr_str(env)) as $src |\n        # _debug(\"INTERP \" + $src) |\n        # _debug(\"FREES \" + ($fn.free_referencess | tostring)) | \n        extractEnv(.env | addFrees(env; $fn.free_referencess)) |\n        .fallback |= extractEnv(env) |\n        childEnv($fn.binds; arguments) |\n            # tell it about its surroundings\n            (reduce $fn.free_referencess[] as $name (\n                .;\n                . as $env | try env_set_(\n                    .;\n                    $name;\n                    $name | env_get(env) // jqmal_error(\"'\\(.)' not found \") |\n                    . as $xvalue\n                    | if $xvalue.kind == \"function\" then\n                        setpath([\"free_referencess\"]; $fn.free_referencess)\n                    else\n                        $xvalue\n                    end\n                ) catch $env)) |\n            # tell it about itself\n            env_multiset($fn) |\n            wrapEnv($replEnv; $envAtoms) |\n            {\n                env: .,\n                expr: $fn.body\n            }\n            | . as $dot\n            # | debug(\"FNEXEC \\(.expr | pr_str) \\($fn.binds[0] | env_get($dot.env) | pr_str)\")\n            | _eval \n            | . as $envexp\n            | (extractReplEnv($envexp.env)) as $xreplenv\n            |\n            {\n                expr: .expr,\n                env: extractEnv(env)\n                    | updateReplEnv($xreplenv)\n                    | wrapEnv($xreplenv; $envexp.env.atoms)\n            }\n            # | . as $dot\n            # | debug(\"FNPOST \\(.expr | pr_str) \\($fn.binds[0] | env_get($dot.expr.env) | pr_str)\")\n            # | debug(\"INTERP \\($src) = \\(.expr | pr_str)\")\n    ) //\n        jqmal_error(\"Unsupported function kind \\(.kind)\");\n        "
  },
  {
    "path": "impls/jq/printer.jq",
    "content": "# {key: string, value: {kkind: kind, value: value}} -> [{kind: value.kkind, value: key}, value.value]\ndef _reconstruct_hash:\n    map([{\n        kind: .value.kkind,\n        value: .key\n    },\n    .value.value]);\n\ndef pr_str(env; opt):\n    (select(.kind == \"symbol\")  | .value) //\n    (select(.kind == \"string\")  | .value | if opt.readable then tojson else . end) //\n    (select(.kind == \"keyword\") | \":\\(.value)\")  //\n    (select(.kind == \"number\")  | .value | tostring) //\n    (select(.kind == \"list\")    | .value | map(pr_str(env; opt))  | join(\" \") | \"(\\(.))\") //\n    (select(.kind == \"vector\")  | .value | map(pr_str(env; opt))  | join(\" \") | \"[\\(.)]\") //\n    (select(.kind == \"hashmap\") | .value | to_entries | _reconstruct_hash | add // [] | map(pr_str(env; opt)) | join(\" \") | \"{\\(.)}\") //\n    (select(.kind == \"nil\")     | \"nil\") //\n    (select(.kind == \"true\")    | \"true\") //\n    (select(.kind == \"false\")   | \"false\") //\n    (select(.kind == \"fn\")      | \"#<fn \\(.function)>\") //\n    (select(.kind == \"function\")| \"#<function \\([\":anon\"] + .names | join(\", \"))>\") //\n    (select(.kind == \"atom\")    | \"(atom \\(env.atoms[.identity] | pr_str(env; opt)))\") //\n    \"#<Unknown \\(.kind) in \\(.)>\";\n\ndef pr_str(env):\n    pr_str(env; {readable: true});\n\ndef pr_str:\n    pr_str(null); # for stepX where X<6"
  },
  {
    "path": "impls/jq/reader.jq",
    "content": "include \"utils\";\n\ndef tokenize:\n    [ . | scan(\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\") | select(.|length > 0)[0] | select(.[0:1] != \";\") ];\n\ndef read_str:\n    tokenize;\n\ndef escape_control:\n    (select(. == \"\\u0000\") | \"\\\\u0000\") //\n    (select(. == \"\\u0001\") | \"\\\\u0001\") //\n    (select(. == \"\\u0002\") | \"\\\\u0002\") //\n    (select(. == \"\\u0003\") | \"\\\\u0003\") //\n    (select(. == \"\\u0004\") | \"\\\\u0004\") //\n    (select(. == \"\\u0005\") | \"\\\\u0005\") //\n    (select(. == \"\\u0006\") | \"\\\\u0006\") //\n    (select(. == \"\\u0007\") | \"\\\\u0007\") //\n    (select(. == \"\\u0008\") | \"\\\\u0008\") //\n    (select(. == \"\\u0009\") | \"\\\\u0009\") //\n    (select(. == \"\\u0010\") | \"\\\\u0010\") //\n    (select(. == \"\\u0011\") | \"\\\\u0011\") //\n    (select(. == \"\\u0012\") | \"\\\\u0012\") //\n    (select(. == \"\\u0013\") | \"\\\\u0013\") //\n    (select(. == \"\\u0014\") | \"\\\\u0014\") //\n    (select(. == \"\\u0015\") | \"\\\\u0015\") //\n    (select(. == \"\\u0016\") | \"\\\\u0016\") //\n    (select(. == \"\\u0017\") | \"\\\\u0017\") //\n    (select(. == \"\\u0018\") | \"\\\\u0018\") //\n    (select(. == \"\\u0019\") | \"\\\\u0019\") //\n    (select(. == \"\\u0020\") | \"\\\\u0020\") //\n    (select(. == \"\\u0021\") | \"\\\\u0021\") //\n    (select(. == \"\\u0022\") | \"\\\\u0022\") //\n    (select(. == \"\\u0023\") | \"\\\\u0023\") //\n    (select(. == \"\\u0024\") | \"\\\\u0024\") //\n    (select(. == \"\\u0025\") | \"\\\\u0025\") //\n    (select(. == \"\\u0026\") | \"\\\\u0026\") //\n    (select(. == \"\\u0027\") | \"\\\\u0027\") //\n    (select(. == \"\\u0028\") | \"\\\\u0028\") //\n    (select(. == \"\\u0029\") | \"\\\\u0029\") //\n    (select(. == \"\\u0030\") | \"\\\\u0030\") //\n    (select(. == \"\\u0031\") | \"\\\\u0031\") //\n    (select(. == \"\\n\") | \"\\\\n\") //\n    .;\n\ndef read_string:\n    gsub(\"(?<z>[\\u0000-\\u001f])\"; \"\\(.z | escape_control)\") | fromjson;\n\ndef extract_string:\n    . as $val | if [\"keyword\", \"symbol\", \"string\"] | contains([$val.kind]) then\n        $val.value\n    else\n        jqmal_error(\"assoc called with non-string key of type \\($val.kind)\")\n    end;\n\n# stuff comes in as {tokens: [...], <stuff unrelated to us>}\ndef read_atom:\n    (.tokens | first) as $lookahead | . | (\n        if $lookahead == \"nil\" then\n            {\n                tokens: .tokens[1:],\n                value: {\n                    kind: \"nil\"\n                }\n            }\n        else if $lookahead == \"true\" then\n            {\n                tokens: .tokens[1:],\n                value: {\n                    kind: \"true\"\n                }\n            }\n        else if $lookahead == \"false\" then\n            {\n                tokens: .tokens[1:],\n                value: {\n                    kind: \"false\"\n                }\n            }\n        else if $lookahead | test(\"^\\\"\") then\n            if $lookahead | test(\"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\") then\n                {\n                    tokens: .tokens[1:],\n                    value: {\n                        kind: \"string\",\n                        value: $lookahead | read_string\n                    }\n                }\n            else\n                jqmal_error(\"EOF while reading string\")\n            end\n        else if $lookahead | test(\"^:\") then\n            {\n                tokens: .tokens[1:],\n                value: {\n                    kind: \"keyword\",\n                    value: $lookahead[1:]\n                }\n            }\n        else if $lookahead | test(\"^-?[0-9]+(?:\\\\.[0-9]+)?$\") then\n            {\n                tokens: .tokens[1:],\n                value: {\n                    kind: \"number\",\n                    value: $lookahead | tonumber\n                }\n            }\n        else if [\")\", \"]\", \"}\"] | contains([$lookahead]) then # this isn't our business\n            empty\n        else \n            {\n                tokens: .tokens[1:],\n                value: {\n                    kind: \"symbol\",\n                    value: $lookahead\n                }\n            }\n        end end end end end end end\n    );\n\ndef read_form_(depth):\n    (.tokens | first) as $lookahead | . | (\n        if $lookahead == null then\n            null\n            # read_list\n        else\n            if $lookahead | test(\"^\\\\(\") then\n                [ (.tokens |= .[1:]) | {tokens: .tokens, value: [], finish: false} | (until(.finish;\n                    if try (.tokens | first | test(\"^\\\\)\")) catch true then\n                        .finish |= true\n                    else \n                        . as $orig | read_form_(depth+1) as $res | {\n                            tokens: $res.tokens,\n                            value: ($orig.value + [$res.value]),\n                            finish: $orig.finish\n                        }\n                    end)) ] | map(select(.tokens)) | last as $result |\n                if $result.tokens | first != \")\" then\n                    jqmal_error(\"unbalanced parentheses in \\($result.tokens)\")\n                else\n                    {\n                        tokens: $result.tokens[1:],\n                        value: {\n                            kind: \"list\",\n                            value: $result.value\n                        },\n                    }\n                end\n            # read_list '['\n            else if $lookahead | test(\"^\\\\[\") then\n                [ (.tokens |= .[1:]) | {tokens: .tokens, value: [], finish: false} | (until(.finish;\n                    if try (.tokens | first | test(\"^\\\\]\")) catch true then\n                        .finish |= true\n                    else \n                        . as $orig | read_form_(depth+1) as $res | {\n                            tokens: $res.tokens,\n                            value: ($orig.value + [$res.value]),\n                            finish: $orig.finish\n                        }\n                    end)) ] | map(select(.tokens)) | last as $result |\n                if $result.tokens | first != \"]\" then\n                    jqmal_error(\"unbalanced brackets in \\($result.tokens)\")\n                else\n                    {\n                        tokens: $result.tokens[1:],\n                        value: {\n                            kind: \"vector\",\n                            value: $result.value\n                        },\n                    }\n                end\n            # read_list '{'\n            else if $lookahead | test(\"^\\\\{\") then\n                [ (.tokens |= .[1:]) | {tokens: .tokens, value: [], finish: false} | (until(.finish;\n                    if try (.tokens | first | test(\"^\\\\}\")) catch true then\n                        .finish |= true\n                    else \n                        . as $orig | read_form_(depth+1) as $res | {\n                            tokens: $res.tokens,\n                            value: ($orig.value + [$res.value]),\n                            finish: $orig.finish\n                        }\n                    end)) ] | map(select(.tokens)) | last as $result |\n                if $result.tokens | first != \"}\" then\n                    jqmal_error(\"unbalanced braces in \\($result.tokens)\")\n                else\n                    if $result.value | length % 2 == 1 then\n                        # odd number of elements not allowed\n                        jqmal_error(\"Odd number of parameters to assoc\")\n                    else\n                        {\n                            tokens: $result.tokens[1:],\n                            value: {\n                                kind: \"hashmap\",\n                                value:\n                                    [ $result.value | \n                                        nwise(2) | \n                                        try {\n                                            key: (.[0] | extract_string),\n                                            value: {\n                                                kkind: .[0].kind,\n                                                value: .[1]\n                                            }\n                                        }\n                                    ] | from_entries\n                            }\n                        }\n                    end\n                end\n        # quote\n        else if $lookahead == \"'\" then\n            (.tokens |= .[1:]) | read_form_(depth+1) | (\n                {\n                    tokens: .tokens,\n                    value: {\n                        kind: \"list\",\n                        value: [\n                            {\n                                kind: \"symbol\",\n                                value: \"quote\"\n                            },\n                            .value\n                        ]\n                    }\n                })\n        # quasiquote\n        else if $lookahead == \"`\" then\n            (.tokens |= .[1:]) | read_form_(depth+1) | (\n                {\n                    tokens: .tokens,\n                    value: {\n                        kind: \"list\",\n                        value: [\n                            {\n                                kind: \"symbol\",\n                                value: \"quasiquote\"\n                            },\n                            .value\n                        ]\n                    }\n                })\n        # unquote\n        else if $lookahead == \"~\" then\n            (.tokens |= .[1:]) | read_form_(depth+1) | (\n                {\n                    tokens: .tokens,\n                    value: {\n                        kind: \"list\",\n                        value: [\n                            {\n                                kind: \"symbol\",\n                                value: \"unquote\"\n                            },\n                            .value\n                        ]\n                    }\n                })\n        # split-unquote\n        else if $lookahead == \"~@\" then\n            (.tokens |= .[1:]) | read_form_(depth+1) | (\n                {\n                    tokens: .tokens,\n                    value: {\n                        kind: \"list\",\n                        value: [\n                            {\n                                kind: \"symbol\",\n                                value: \"splice-unquote\"\n                            },\n                            .value\n                        ]\n                    }\n                })\n        # deref\n        else if $lookahead == \"@\" then\n            (.tokens |= .[1:]) | read_form_(depth+1) | (\n                {\n                    tokens: .tokens,\n                    value: {\n                        kind: \"list\",\n                        value: [\n                            {\n                                kind: \"symbol\",\n                                value: \"deref\"\n                            },\n                            .value\n                        ]\n                    }\n                })\n        # with-meta\n        else if $lookahead == \"^\" then\n            (.tokens |= .[1:]) | read_form_(depth+1) as $meta | $meta | read_form_(depth+1) as $value | (\n                {\n                    tokens: $value.tokens,\n                    value: {\n                        kind: \"list\",\n                        value: [\n                            {\n                                kind: \"symbol\",\n                                value: \"with-meta\"\n                            },\n                            $value.value,\n                            $meta.value\n                        ]\n                    }\n                })\n        else\n            . as $prev |  read_atom\n        end end end end end end end end end end);\n\ndef read_form:\n    ({tokens: read_str} | read_form_(0).value) // {kind: \"nil\"};\n"
  },
  {
    "path": "impls/jq/run",
    "content": "#!/usr/bin/python3\n\"\"\"Spawn a jq subprocess and wrap some IO interactions for it.\n\njq seems unable to\n - open an arbitrary file (slurp)\n - emit a string on stdout without new line (readline)\n\"\"\"\nfrom json import JSONDecodeError, dumps, loads\nfrom os import environ\nfrom os.path import dirname, join, realpath\nfrom subprocess import PIPE, Popen\nfrom sys import argv\n\nrundir = dirname(realpath(__file__))\nwith Popen(args=['/usr/bin/jq',\n                 '--argjson', 'DEBUG', 'false',\n                 '-nrM',  # --null-input --raw-output --monochrome-output\n                 '-L', rundir,\n                 '-f', join(rundir, environ.get('STEP', 'stepA_mal') + '.jq'),\n                 '--args'] + argv[1:],\n           stdin=PIPE, stderr=PIPE, encoding='utf-8',\n           ) as proc:\n    assert proc.stderr is not None  # for mypy\n    for received in proc.stderr:\n        try:\n            as_json = loads(received)\n        except JSONDecodeError:\n            print(f'JQ STDERR: {received}', end=None)\n        else:\n            match as_json:\n                case ['DEBUG:', ['display', str(message)]]:\n                    # While at it, provide a way to immediately print to\n                    # stdin for DEBUG-EVAL, println and prn (jq is able to\n                    # output to stderr, but *we* are already piping it).\n                    print(message)\n                    # Jq waits for this signal to go on, so that its own\n                    # output is not mixed with our one.\n                    print('null', file=proc.stdin, flush=True)\n                case ['DEBUG:', ['readline', str(prompt)]]:\n                    try:\n                        data = input(prompt)\n                    except EOFError:\n                        break                   # Expected end of this script\n                    print(dumps(data), file=proc.stdin, flush=True)\n                case ['DEBUG:', ['slurp', str(fname)]]:\n                    with open(fname, 'r', encoding='utf-8') as file_handler:\n                        data = file_handler.read()\n                    print(dumps(data), file=proc.stdin, flush=True)\n                case _:\n                    # Allow normal debugging information for other purposes.\n                    print(f'JQ STDERR: {received}', end=None)\nprint()\n"
  },
  {
    "path": "impls/jq/step0_repl.jq",
    "content": "include \"utils\";\n\ndef READ:\n    .;\n\ndef EVAL:\n    .;\n\ndef PRINT:\n    .;\n\ndef repl:\n    # Infinite generator, interrupted by ./run.\n   \"user> \" | __readline |\n   READ | EVAL |\n   PRINT, repl;\n\nrepl\n"
  },
  {
    "path": "impls/jq/step1_read_print.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\n\ndef READ:\n    read_form;\n\ndef EVAL:\n    .;\n\ndef PRINT:\n    pr_str;\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    \"user> \" | __readline |\n    try (\n        READ | EVAL |\n        PRINT, repl\n    ) catch if is_jqmal_error then\n        ., repl\n    else\n        halt_error\n    end;\n\nrepl\n"
  },
  {
    "path": "impls/jq/step2_eval.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\n\ndef READ:\n    read_form;\n\ndef arg_check(args):\n    if .inputs != (args|length) then\n        jqmal_error(\"Invalid number of arguments (expected \\(.inputs), got \\(args|length))\")\n    else\n        .\n    end;\n\ndef interpret(arguments; env):\n    (select(.kind == \"fn\") |\n        arg_check(arguments) |\n        (\n            select(.function == \"number_add\") |\n            arguments | map(.value) | .[0] + .[1] | wrap(\"number\")\n        ) // (\n            select(.function == \"number_sub\") |\n            arguments | map(.value) | .[0] - .[1] | wrap(\"number\")\n        ) // (\n            select(.function == \"number_mul\") |\n            arguments | map(.value) | .[0] * .[1] | wrap(\"number\")\n        ) // (\n            select(.function == \"number_div\") |\n            arguments | map(.value) | .[0] / .[1] | wrap(\"number\")\n        )\n    ) //\n        jqmal_error(\"Unsupported native function kind \\(.kind)\");\n\ndef EVAL(env):\n            # (\"EVAL: \\(pr_str(env))\" | _display | empty),\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                map(EVAL(env)) | .[1:] as $args | first | interpret($args; env)\n            ) //\n            (\n                select(.kind == \"vector\") |\n                    {\n                        kind: \"vector\",\n                        value: .value|map(EVAL(env))\n                    }\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                    {\n                        kind: \"hashmap\",\n                        value: .value|map_values(.value |= EVAL(env))\n                    }\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                    env[.value] // jqmal_error(\"'\\(.)' not found\")\n            ) //\n            .;\n\ndef PRINT:\n    pr_str;\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) |\n        PRINT, ($env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\n# The main program starts here.\n    {\n            \"+\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_add\"\n            },\n            \"-\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_sub\"\n            },\n            \"*\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_mul\"\n            },\n            \"/\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_div\"\n            },\n    }\n    |\n        repl\n"
  },
  {
    "path": "impls/jq/step3_env.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"env\";\n\ndef READ:\n    read_form;\n\n# Environment Functions\n\ndef env_set(env; $key; $value):\n    {\n        parent: env.parent,\n        environment: (env.environment + (env.environment | .[$key] |= $value)) # merge together, as .environment[key] |= value does not work\n    };\n\ndef arg_check(args):\n    if .inputs != (args|length) then\n        jqmal_error(\"Invalid number of arguments (expected \\(.inputs), got \\(args|length))\")\n    else\n        .\n    end;\n\ndef interpret(arguments; env):\n    (select(.kind == \"fn\") |\n        arg_check(arguments) |\n        (\n            select(.function == \"number_add\") |\n            arguments | map(.value) | .[0] + .[1] | wrap(\"number\")\n        ) // (\n            select(.function == \"number_sub\") |\n            arguments | map(.value) | .[0] - .[1] | wrap(\"number\")\n        ) // (\n            select(.function == \"number_mul\") |\n            arguments | map(.value) | .[0] * .[1] | wrap(\"number\")\n        ) // (\n            select(.function == \"number_div\") |\n            arguments | map(.value) | .[0] / .[1] | wrap(\"number\")\n        )\n        | {expr:., env:env}\n    ) //\n        jqmal_error(\"Unsupported native function kind \\(.kind)\");\n\ndef EVAL(env):\n            if \"DEBUG-EVAL\" | env_get(env) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    .[1].value as $key |\n                                    .[2] | EVAL(env) |\n                                    .expr as $value |\n                                    .env |= env_set(.; $key; $value)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce (.[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:env, environment:{}, fallback:null};\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) |\n                                            env_set(.env; $xvalue[0].value; .expr)\n                                        )) as $env |\n                                        .[2] | {expr:EVAL($env).expr, env:env}\n                            ) //\n                            (\n                                reduce .[] as $elem (\n                                    [];\n                                    . as $dot | $elem | EVAL(env) as $eval_env |\n                                        ($dot + [$eval_env.expr])\n                                ) | { expr: ., env: env } as $ev\n                                    | $ev.expr | first |\n                                        interpret($ev.expr[1:]; $ev.env)\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:env};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n               .expr |= {kind:\"vector\", value:.}\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:env};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .expr |= {kind:\"hashmap\", value:from_entries}\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get(env) // jqmal_error(\"'\\(.)' not found\") |\n                {expr:., env:env}\n            ) //\n            {expr:., env:env};\n\ndef PRINT:\n    pr_str;\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) |\n        (.expr | PRINT), (.env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: {\n            \"+\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_add\"\n            },\n            \"-\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_sub\"\n            },\n            \"*\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_mul\"\n            },\n            \"/\": {\n                kind: \"fn\", # native function\n                inputs: 2,\n                function: \"number_div\"\n            },\n        }\n    }\n    |\n        repl\n"
  },
  {
    "path": "impls/jq/step4_if_fn_do.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\n# Environment Functions\n\ndef env_set(env; $key; $value):\n    (if $value.kind == \"function\" or $value.kind == \"atom\" then\n        # inform the function/atom of its names\n        $value | (.names += [$key]) | (.names |= unique) |\n        if $value.kind == \"atom\" then\n            # check if the one we have is newer\n            ($key | env_get(env)) as $ours |\n            if $ours.last_modified > $value.last_modified then\n                $ours\n            else\n                # update modification timestamp\n                $value | .last_modified |= now\n            end\n        else\n            .\n        end\n    else \n        $value\n    end) as $value | {\n        parent: env.parent,\n        environment: ((env.environment // jqmal_error(\"Environment empty in \\(env | keys)\")) + (env.environment | .[$key] |= $value)), # merge together, as env.environment[key] |= value does not work\n        fallback: env.fallback\n    };\n\ndef _env_remove_references(refs):\n    if . != null then\n        {\n            environment: (.environment | to_entries | map(select(.key as $key | refs | contains([$key]) | not)) | from_entries),\n            parent: (.parent | _env_remove_references(refs)),\n            fallback: (.fallback | _env_remove_references(refs))\n        }\n    else . end;\n\ndef env_remove_references(refs):\n    . as $env \n    | if has(\"replEnv\") then\n        .currentEnv |= _env_remove_references(refs)\n      else\n        _env_remove_references(refs)\n      end;\n\n# Evaluation\n\ndef arg_check(args):\n    if .inputs < 0 then\n        if (abs(.inputs) - 1) > (args | length) then\n            jqmal_error(\"Invalid number of arguments (expected at least \\(abs(.inputs) - 1), got \\(args|length))\")\n        else\n            .\n        end\n    else if .inputs != (args|length) then\n        jqmal_error(\"Invalid number of arguments (expected \\(.inputs), got \\(args|length))\")\n    else\n        .\n    end end;\n\ndef addFrees(newEnv; frees):\n    . as $env\n    | reduce frees[] as $free (\n        $env;\n        . as $dot\n        | ($free | env_get(newEnv)) as $lookup\n        | if $lookup != null then\n            env_set_(.; $free; $lookup)\n          else\n            .\n          end)\n    | . as $env\n    | $env;\n\ndef interpret(arguments; env; _eval):\n    (if $DEBUG then debug(\"INTERP: \\(. | pr_str(env))\") else . end) |\n    (select(.kind == \"fn\") |\n        arg_check(arguments) | \n                core_interp(arguments; env) | {expr:., env:env}\n    ) //\n    (select(.kind == \"function\") as $fn |\n        # todo: arg_check\n        (.body | pr_str(env)) as $src |\n        # debug(\"INTERP \" + $src) |\n        # debug(\"FREES \" + ($fn.free_referencess | tostring)) | \n        .env |\n        addFrees(env; $fn.free_referencess) |\n        .fallback |= env |\n        childEnv($fn.binds; arguments) |\n            # tell it about its surroundings\n            (reduce $fn.free_referencess[] as $name (\n                .;\n                . as $env | try env_set(\n                    .;\n                    $name;\n                    $name | env_get(env) // jqmal_error(\"'\\(.)' not found\") |\n                    . as $xvalue\n                    | if $xvalue.kind == \"function\" then\n                        setpath([\"free_referencess\"]; $fn.free_referencess)\n                    else\n                        $xvalue\n                    end\n                ) catch $env)) |\n            # tell it about itself\n            env_multiset($fn) |\n            {\n                env: .,\n                expr: $fn.body\n            }\n            | . as $dot\n            # | debug(\"FNEXEC \\(.expr | pr_str) \\($fn.binds[0] | env_get($dot.env) | pr_str)\")\n            | _eval \n            | . as $envexp\n            |\n            {\n                expr: .expr,\n                env: env\n            }\n            # | . as $dot\n            # | debug(\"FNPOST \\(.expr | pr_str) \\($fn.binds[0] | env_get($dot.expr.env) | pr_str)\")\n            # | debug(\"INTERP \\($src) = \\(.expr | pr_str)\")\n    ) //\n        jqmal_error(\"Unsupported function kind \\(.kind)\");\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    # EVAL starts here.\n            if \"DEBUG-EVAL\" | env_get(env) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    .[1].value as $key |\n                                    .[2] | EVAL(env) |\n                                    if .env.replEnv != null then\n                                        addToEnv($key)\n                                    else\n                                        .expr as $def_value |\n                                        .env |= env_set_(.; $key; $def_value)\n                                    end\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:env, environment:{}, fallback:null};\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | { expr: EVAL($env).expr, env: env }\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce ($value[1:][]) as $xvalue (\n                                        { env: env, expr: {kind:\"nil\"} };\n                                        .env as $env | $xvalue | EVAL($env)\n                                    ))\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL(env) as $condenv |\n                                        if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"}) | EVAL($condenv.env)\n                                        else\n                                            $value[2] | EVAL($condenv.env)\n                                        end\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # we can't do what the guide says, so we'll skip over this\n                                    # and ues the later implementation\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: env,\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $value[2] | find_free_references(env | env_dump_keys + $binds) # for dynamically scoped variables\n                                    } | {expr: ., env:env}\n                            ) //\n                            (\n                                reduce .[] as $elem (\n                                    [];\n                                    . as $dot | $elem | EVAL(env) as $eval_env |\n                                        ($dot + [$eval_env.expr])\n                                ) | { expr: ., env: env } as $ev\n                                    | $ev.expr | first |\n                                        interpret($ev.expr[1:]; $ev.env; _eval_here)\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:env};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n               .expr |= {kind:\"vector\", value:.}\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:env};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .expr |= {kind:\"hashmap\", value:from_entries}\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get(env) // jqmal_error(\"'\\(.)' not found\") |\n                {expr:., env:env}\n            ) //\n            {expr:., env:env};\n\ndef PRINT:\n    pr_str;\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) |\n        (.expr | PRINT), (.env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    |\n        repl\n"
  },
  {
    "path": "impls/jq/step5_tco.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\n# Environment Functions\n\ndef env_set(env; $key; $value):\n    (if $value.kind == \"function\" or $value.kind == \"atom\" then\n        # inform the function/atom of its names\n        $value | (.names += [$key]) | (.names |= unique) |\n        if $value.kind == \"atom\" then\n            # check if the one we have is newer\n            ($key | env_get(env)) as $ours |\n            if $ours.last_modified > $value.last_modified then\n                $ours\n            else\n                # update modification timestamp\n                $value | .last_modified |= now\n            end\n        else\n            .\n        end\n    else \n        $value\n    end) as $value | {\n        parent: env.parent,\n        environment: ((env.environment // jqmal_error(\"Environment empty in \\(env | keys)\")) + (env.environment | .[$key] |= $value)), # merge together, as env.environment[key] |= value does not work\n        fallback: env.fallback\n    };\n\ndef _env_remove_references(refs):\n    if . != null then\n        {\n            environment: (.environment | to_entries | map(select(.key as $key | refs | contains([$key]) | not)) | from_entries),\n            parent: (.parent | _env_remove_references(refs)),\n            fallback: (.fallback | _env_remove_references(refs))\n        }\n    else . end;\n\ndef env_remove_references(refs):\n    . as $env \n    | if has(\"replEnv\") then\n        .currentEnv |= _env_remove_references(refs)\n      else\n        _env_remove_references(refs)\n      end;\n\n# Evaluation\n\ndef arg_check(args):\n    if .inputs < 0 then\n        if (abs(.inputs) - 1) > (args | length) then\n            jqmal_error(\"Invalid number of arguments (expected at least \\(abs(.inputs) - 1), got \\(args|length))\")\n        else\n            .\n        end\n    else if .inputs != (args|length) then\n        jqmal_error(\"Invalid number of arguments (expected \\(.inputs), got \\(args|length))\")\n    else\n        .\n    end end;\n\ndef addFrees(newEnv; frees):\n    . as $env\n    | reduce frees[] as $free (\n        $env;\n        . as $dot\n        | ($free | env_get(newEnv)) as $lookup\n        | if $lookup != null then\n            env_set_(.; $free; $lookup)\n          else\n            .\n          end)\n    | . as $env\n    | $env;\n\ndef interpret(arguments; env; _eval):\n    (if $DEBUG then debug(\"INTERP: \\(. | pr_str(env))\") else . end) |\n    (select(.kind == \"fn\") |\n        arg_check(arguments) | \n                core_interp(arguments; env) | {expr:., env:env}\n    ) //\n    (select(.kind == \"function\") as $fn |\n        # todo: arg_check\n        (.body | pr_str(env)) as $src |\n        # debug(\"INTERP \" + $src) |\n        # debug(\"FREES \" + ($fn.free_referencess | tostring)) | \n        .env |\n        addFrees(env; $fn.free_referencess) |\n        .fallback |= env |\n        childEnv($fn.binds; arguments) |\n            # tell it about its surroundings\n            (reduce $fn.free_referencess[] as $name (\n                .;\n                . as $env | try env_set(\n                    .;\n                    $name;\n                    $name | env_get(env) // jqmal_error(\"'\\(.)' not found\") |\n                    . as $xvalue\n                    | if $xvalue.kind == \"function\" then\n                        setpath([\"free_referencess\"]; $fn.free_referencess)\n                    else\n                        $xvalue\n                    end\n                ) catch $env)) |\n            # tell it about itself\n            env_multiset($fn) |\n            {\n                env: .,\n                expr: $fn.body\n            }\n            | . as $dot\n            # | debug(\"FNEXEC \\(.expr | pr_str) \\($fn.binds[0] | env_get($dot.env) | pr_str)\")\n            | _eval \n            | . as $envexp\n            |\n            {\n                expr: .expr,\n                env: env\n            }\n            # | . as $dot\n            # | debug(\"FNPOST \\(.expr | pr_str) \\($fn.binds[0] | env_get($dot.expr.env) | pr_str)\")\n            # | debug(\"INTERP \\($src) = \\(.expr | pr_str)\")\n    ) //\n        jqmal_error(\"Unsupported function kind \\(.kind)\");\n\ndef recurseflip(x; y):\n    recurse(y; x);\n\ndef TCOWrap(env; retenv; continue):\n    {\n        ast: .,\n        env: env,\n        ret_env: retenv,\n        finish: (continue | not),\n        cont: true # set inside\n    };\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    . as $ast\n    | TCOWrap(env; null; true)\n    | [ recurseflip(.cont;\n        .env as $_menv\n        | if .finish then\n            .cont |= false\n        else\n            (.ret_env//.env) as  $_retenv\n            | .ret_env as $_orig_retenv\n            | .ast\n            |\n            if \"DEBUG-EVAL\" | env_get($_menv) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        (\n                                        if .env.replEnv != null then\n                                            addToEnv($value[1].value)\n                                        else\n                                            .expr as $def_value |\n                                            .env |= env_set_(.; $value[1].value; $def_value)\n                                        end\n                                        ) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:$_menv, environment:{}, fallback:null};\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | TCOWrap($env; $_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce $value[1:-1][] as $xvalue (\n                                        $_menv;\n                                        . as $env | $xvalue | EVAL($env) | .env\n                                    )) as $env |\n                                    $value[-1] | TCOWrap($env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL(env) as $condenv |\n                                        (if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"})\n                                        else\n                                            $value[2]\n                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: $_menv,\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $value[2] | find_free_references($_menv | env_dump_keys + $binds) # for dynamically scoped variables\n                                } | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                reduce .[] as $elem (\n                                    [];\n                                    . as $dot | $elem | EVAL($_menv) as $eval_env |\n                                        ($dot + [$eval_env.expr])\n                                ) | . as $expr | first |\n                                        interpret($expr[1:]; $_menv; _eval_here) as $exprenv |\n                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n                .env as $e |\n               {kind:\"vector\", value:.expr} |\n               TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:env};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .env as $e |\n                {kind:\"hashmap\", value:.expr|from_entries} |\n                TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"function\") |\n                . | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value | env_get($_menv) // jqmal_error(\"'\\(.)' not found\") |\n                TCOWrap($_menv; $_orig_retenv; false)\n            ) //\n            TCOWrap($_menv; $_orig_retenv; false)\n        end\n    ) ] |\n    last |\n    {expr: .ast, env:(.ret_env // .env)};\n\ndef PRINT:\n    pr_str;\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) |\n        (.expr | PRINT), (.env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    |\n        repl\n"
  },
  {
    "path": "impls/jq/step6_file.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"interp\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\ndef recurseflip(x; y):\n    recurse(y; x);\n\ndef TCOWrap(env; retenv; continue):\n    {\n        ast: .,\n        env: env,\n        ret_env: (if retenv != null then (retenv | setpath([\"atoms\"]; env.atoms)) else retenv end),\n        finish: (continue | not),\n        cont: true # set inside\n    };\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    . as $ast\n    | TCOWrap(env; null; true)\n    | [ recurseflip(.cont;\n        .env as $_menv\n        | if .finish then\n            .cont |= false\n        else\n            (.ret_env//.env) as  $_retenv\n            | .ret_env as $_orig_retenv\n            | .ast\n            | . as $init\n            | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env \"package\"\n            | $_menv | unwrapReplEnv    as $replEnv    # -\n            | $init\n            |\n            if \"DEBUG-EVAL\" | env_get($currentEnv) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:$currentEnv, environment:{}, fallback:null} |\n                                            wrapEnv($replEnv; $_menv.atoms);\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | TCOWrap($env; $_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce $value[1:-1][] as $xvalue (\n                                        $_menv;\n                                        . as $env | $xvalue | EVAL($env) | .env\n                                    )) as $env |\n                                    $value[-1] | TCOWrap($env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL($_menv) as $condenv |\n                                        (if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"})\n                                        else\n                                            $value[2]\n                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: ($_menv | env_remove_references($free_referencess)),\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $free_referencess,  # for dynamically scoped variables\n                                } | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                (\n                                    .[0] | EVAL($_menv) |\n                                    (.env | setpath([\"atoms\"]; $_menv.atoms)) as $_menv |\n                                    .expr\n                                ) as $fn |\n                                    $value[1:] |\n                                    (reduce .[] as $elem (\n                                        {env: $_menv, val: []};\n                                        # debug(\".val: \\(.val)     elem=\\($elem)\") |\n                                        . as $dot | $elem | EVAL($dot.env) as $eval_env |\n                                            ($dot.env | setpath([\"atoms\"]; $eval_env.env.atoms)) as $_menv |\n                                            {env: $_menv, val: ($dot.val + [$eval_env.expr])}\n                                            # | debug(\".val: \\(.val)\")\n                                    )) as $expr |\n                                        # debug(\"fn.kind: \\($fn.kind)\", \"expr: \\($expr)\") |\n                                            $fn |\n                                            interpret($expr.val; $expr.env; _eval_here) as $exprenv |\n                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n                .env as $e |\n               {kind:\"vector\", value:.expr} |\n               TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .env as $e |\n                {kind:\"hashmap\", value:.expr|from_entries} |\n                TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"function\") |\n                . | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get($currentEnv) // jqmal_error(\"'\\(.)' not found\") |\n                TCOWrap($_menv; $_orig_retenv; false)\n            ) //\n            TCOWrap($_menv; $_orig_retenv; false)\n        end\n    ) ] |\n    last |\n    {expr: .ast, env:(.ret_env // .env)};\n\ndef PRINT(env):\n    pr_str(env);\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) | .env as $env |\n        (.expr | PRINT($env)), ($env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | wrapEnv({})\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    | eval_ign(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))))\")\n    | env_set_(.; \"*ARGV*\"; {kind:\"list\", value:[$ARGS.positional[1:] | .[] | {kind:\"string\", value:.}]})\n    |\n    if $ARGS.positional|length > 0 then\n        eval_ign(\"(load-file \\($ARGS.positional[0] | tojson))\") |\n        empty\n    else\n        repl\n    end\n"
  },
  {
    "path": "impls/jq/step7_quote.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"interp\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\ndef recurseflip(x; y):\n    recurse(y; x);\n\ndef TCOWrap(env; retenv; continue):\n    {\n        ast: .,\n        env: env,\n        ret_env: (if retenv != null then (retenv | setpath([\"atoms\"]; env.atoms)) else retenv end),\n        finish: (continue | not),\n        cont: true # set inside\n    };\n\ndef quasiquote:\n\n    # If input is ('name, arg), return arg, else nothing.\n    def _starts_with(name):\n        select(.kind == \"list\")\n        | .value\n        | select(length == 2)\n        | select(.[0] | .kind == \"symbol\" and .value == name)\n        | .[1];\n\n    # Right-folding function. The current element is provided as input.\n    def qq_loop(acc):\n        (_starts_with(\"splice-unquote\")\n         | {kind:\"list\", value:[{kind:\"symbol\", value:\"concat\"}, ., acc]})\n        // {kind:\"list\", value:[{kind:\"symbol\", value:\"cons\"}, quasiquote, acc]};\n\n    # Adapt parameters for jq foldr.\n    def qq_foldr:\n        .value\n        | reverse\n        | reduce .[] as $elt ({kind:\"list\", value:[]};\n                              . as $acc | $elt | qq_loop($acc));\n\n    _starts_with(\"unquote\")\n    // (\n        select(.kind == \"list\")\n        | qq_foldr\n    ) // (\n        select(.kind == \"vector\")\n        | {kind:\"list\", value: [{kind:\"symbol\", value:\"vec\"}, qq_foldr]}\n    ) // (\n        select(.kind == \"hashmap\" or .kind == \"symbol\")\n        | {kind:\"list\", value:[{kind:\"symbol\", value:\"quote\"}, .]}\n    ) // .;\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    . as $ast\n    | TCOWrap(env; null; true)\n    | [ recurseflip(.cont;\n        .env as $_menv\n        | if .finish then\n            .cont |= false\n        else\n            (.ret_env//.env) as  $_retenv\n            | .ret_env as $_orig_retenv\n            | .ast\n            | . as $init\n            | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env \"package\"\n            | $_menv | unwrapReplEnv    as $replEnv    # -\n            | $init\n            |\n            if \"DEBUG-EVAL\" | env_get($currentEnv) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:$currentEnv, environment:{}, fallback:null} |\n                                            wrapEnv($replEnv; $_menv.atoms);\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | TCOWrap($env; $_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce $value[1:-1][] as $xvalue (\n                                        $_menv;\n                                        . as $env | $xvalue | EVAL($env) | .env\n                                    )) as $env |\n                                    $value[-1] | TCOWrap($env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL($_menv) as $condenv |\n                                        (if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"})\n                                        else\n                                            $value[2]\n                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: ($_menv | env_remove_references($free_referencess)),\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $free_referencess,  # for dynamically scoped variables\n                                } | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quote\") |\n                                    $value[1] | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quasiquote\") |\n                                    $value[1] | quasiquote | TCOWrap($_menv; $_orig_retenv; true)\n                            ) //\n                            (\n                                (\n                                    .[0] | EVAL($_menv) |\n                                    (.env | setpath([\"atoms\"]; $_menv.atoms)) as $_menv |\n                                    .expr\n                                ) as $fn |\n                                    $value[1:] |\n                                    (reduce .[] as $elem (\n                                        {env: $_menv, val: []};\n                                        # debug(\".val: \\(.val)     elem=\\($elem)\") |\n                                        . as $dot | $elem | EVAL($dot.env) as $eval_env |\n                                            ($dot.env | setpath([\"atoms\"]; $eval_env.env.atoms)) as $_menv |\n                                            {env: $_menv, val: ($dot.val + [$eval_env.expr])}\n                                            # | debug(\".val: \\(.val)\")\n                                    )) as $expr |\n                                        # debug(\"fn.kind: \\($fn.kind)\", \"expr: \\($expr)\") |\n                                            $fn |\n                                            interpret($expr.val; $expr.env; _eval_here) as $exprenv |\n                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n                .env as $e |\n               {kind:\"vector\", value:.expr} |\n               TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .env as $e |\n                {kind:\"hashmap\", value:.expr|from_entries} |\n                TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"function\") |\n                . | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get($currentEnv) // jqmal_error(\"'\\(.)' not found\") |\n                TCOWrap($_menv; $_orig_retenv; false)\n            ) //\n            TCOWrap($_menv; $_orig_retenv; false)\n        end\n    ) ] |\n    last |\n    {expr: .ast, env:(.ret_env // .env)};\n\ndef PRINT(env):\n    pr_str(env);\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) | .env as $env |\n        (.expr | PRINT($env)), ($env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | wrapEnv({})\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    | eval_ign(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))))\")\n    | env_set_(.; \"*ARGV*\"; {kind:\"list\", value:[$ARGS.positional[1:] | .[] | {kind:\"string\", value:.}]})\n    |\n    if $ARGS.positional|length > 0 then\n        eval_ign(\"(load-file \\($ARGS.positional[0] | tojson))\") |\n        empty\n    else\n        repl\n    end\n"
  },
  {
    "path": "impls/jq/step8_macros.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"interp\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\ndef recurseflip(x; y):\n    recurse(y; x);\n\ndef TCOWrap(env; retenv; continue):\n    {\n        ast: .,\n        env: env,\n        ret_env: (if retenv != null then (retenv | setpath([\"atoms\"]; env.atoms)) else retenv end),\n        finish: (continue | not),\n        cont: true # set inside\n    };\n\ndef quasiquote:\n\n    # If input is ('name, arg), return arg, else nothing.\n    def _starts_with(name):\n        select(.kind == \"list\")\n        | .value\n        | select(length == 2)\n        | select(.[0] | .kind == \"symbol\" and .value == name)\n        | .[1];\n\n    # Right-folding function. The current element is provided as input.\n    def qq_loop(acc):\n        (_starts_with(\"splice-unquote\")\n         | {kind:\"list\", value:[{kind:\"symbol\", value:\"concat\"}, ., acc]})\n        // {kind:\"list\", value:[{kind:\"symbol\", value:\"cons\"}, quasiquote, acc]};\n\n    # Adapt parameters for jq foldr.\n    def qq_foldr:\n        .value\n        | reverse\n        | reduce .[] as $elt ({kind:\"list\", value:[]};\n                              . as $acc | $elt | qq_loop($acc));\n\n    _starts_with(\"unquote\")\n    // (\n        select(.kind == \"list\")\n        | qq_foldr\n    ) // (\n        select(.kind == \"vector\")\n        | {kind:\"list\", value: [{kind:\"symbol\", value:\"vec\"}, qq_foldr]}\n    ) // (\n        select(.kind == \"hashmap\" or .kind == \"symbol\")\n        | {kind:\"list\", value:[{kind:\"symbol\", value:\"quote\"}, .]}\n    ) // .;\n\ndef set_macro_function:\n    if .kind != \"function\" then\n        jqmal_error(\"expected a function to be defined by defmacro!\")\n    else\n        .is_macro |= true\n    end;\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    . as $ast\n    | TCOWrap(env; null; true)\n    | [ recurseflip(.cont;\n        .env as $_menv\n        | if .finish then\n            .cont |= false\n        else\n            (.ret_env//.env) as  $_retenv\n            | .ret_env as $_orig_retenv\n            | .ast\n            | . as $init\n            | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env \"package\"\n            | $_menv | unwrapReplEnv    as $replEnv    # -\n            | $init\n            |\n            if \"DEBUG-EVAL\" | env_get($currentEnv) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"defmacro!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        .expr |= set_macro_function |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:$currentEnv, environment:{}, fallback:null} |\n                                            wrapEnv($replEnv; $_menv.atoms);\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | TCOWrap($env; $_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce $value[1:-1][] as $xvalue (\n                                        $_menv;\n                                        . as $env | $xvalue | EVAL($env) | .env\n                                    )) as $env |\n                                    $value[-1] | TCOWrap($env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL($_menv) as $condenv |\n                                        (if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"})\n                                        else\n                                            $value[2]\n                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: ($_menv | env_remove_references($free_referencess)),\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $free_referencess,  # for dynamically scoped variables\n                                        is_macro: false\n                                } | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quote\") |\n                                    $value[1] | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quasiquote\") |\n                                    $value[1] | quasiquote | TCOWrap($_menv; $_orig_retenv; true)\n                            ) //\n                            (\n                                (\n                                    .[0] | EVAL($_menv) |\n                                    (.env | setpath([\"atoms\"]; $_menv.atoms)) as $_menv |\n                                    .expr\n                                ) as $fn |\n                                if $fn.kind == \"function\" and $fn.is_macro then\n                                    $fn | interpret($value[1:]; $_menv; _eval_here) as $exprenv |\n                                    $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; true)\n                                else\n                                    $value[1:] |\n                                    (reduce .[] as $elem (\n                                        {env: $_menv, val: []};\n                                        # debug(\".val: \\(.val)     elem=\\($elem)\") |\n                                        . as $dot | $elem | EVAL($dot.env) as $eval_env |\n                                            ($dot.env | setpath([\"atoms\"]; $eval_env.env.atoms)) as $_menv |\n                                            {env: $_menv, val: ($dot.val + [$eval_env.expr])}\n                                            # | debug(\".val: \\(.val)\")\n                                    )) as $expr |\n                                        # debug(\"fn.kind: \\($fn.kind)\", \"expr: \\($expr)\") |\n                                            $fn |\n                                            interpret($expr.val; $expr.env; _eval_here) as $exprenv |\n                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)\n                                end\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n                .env as $e |\n               {kind:\"vector\", value:.expr} |\n               TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .env as $e |\n                {kind:\"hashmap\", value:.expr|from_entries} |\n                TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"function\") |\n                . | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get($currentEnv) // jqmal_error(\"'\\(.)' not found\") |\n                TCOWrap($_menv; $_orig_retenv; false)\n            ) //\n            TCOWrap($_menv; $_orig_retenv; false)\n        end\n    ) ] |\n    last |\n    {expr: .ast, env:(.ret_env // .env)};\n\ndef PRINT(env):\n    pr_str(env);\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) | .env as $env |\n        (.expr | PRINT($env)), ($env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | wrapEnv({})\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    | eval_ign(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))))\")\n    | eval_ign(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n    | env_set_(.; \"*ARGV*\"; {kind:\"list\", value:[$ARGS.positional[1:] | .[] | {kind:\"string\", value:.}]})\n    |\n    if $ARGS.positional|length > 0 then\n        eval_ign(\"(load-file \\($ARGS.positional[0] | tojson))\") |\n        empty\n    else\n        repl\n    end\n"
  },
  {
    "path": "impls/jq/step9_try.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"interp\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\ndef recurseflip(x; y):\n    recurse(y; x);\n\ndef TCOWrap(env; retenv; continue):\n    {\n        ast: .,\n        env: env,\n        ret_env: (if retenv != null then (retenv | setpath([\"atoms\"]; env.atoms)) else retenv end),\n        finish: (continue | not),\n        cont: true # set inside\n    };\n\ndef quasiquote:\n\n    # If input is ('name, arg), return arg, else nothing.\n    def _starts_with(name):\n        select(.kind == \"list\")\n        | .value\n        | select(length == 2)\n        | select(.[0] | .kind == \"symbol\" and .value == name)\n        | .[1];\n\n    # Right-folding function. The current element is provided as input.\n    def qq_loop(acc):\n        (_starts_with(\"splice-unquote\")\n         | {kind:\"list\", value:[{kind:\"symbol\", value:\"concat\"}, ., acc]})\n        // {kind:\"list\", value:[{kind:\"symbol\", value:\"cons\"}, quasiquote, acc]};\n\n    # Adapt parameters for jq foldr.\n    def qq_foldr:\n        .value\n        | reverse\n        | reduce .[] as $elt ({kind:\"list\", value:[]};\n                              . as $acc | $elt | qq_loop($acc));\n\n    _starts_with(\"unquote\")\n    // (\n        select(.kind == \"list\")\n        | qq_foldr\n    ) // (\n        select(.kind == \"vector\")\n        | {kind:\"list\", value: [{kind:\"symbol\", value:\"vec\"}, qq_foldr]}\n    ) // (\n        select(.kind == \"hashmap\" or .kind == \"symbol\")\n        | {kind:\"list\", value:[{kind:\"symbol\", value:\"quote\"}, .]}\n    ) // .;\n\ndef set_macro_function:\n    if .kind != \"function\" then\n        jqmal_error(\"expected a function to be defined by defmacro!\")\n    else\n        .is_macro |= true\n    end;\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    . as $ast\n    | TCOWrap(env; null; true)\n    | [ recurseflip(.cont;\n        .env as $_menv\n        | if .finish then\n            .cont |= false\n        else\n            (.ret_env//.env) as  $_retenv\n            | .ret_env as $_orig_retenv\n            | .ast\n            | . as $init\n            | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env \"package\"\n            | $_menv | unwrapReplEnv    as $replEnv    # -\n            | $init\n            |\n            if \"DEBUG-EVAL\" | env_get($currentEnv) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"def!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"defmacro!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        .expr |= set_macro_function |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:$currentEnv, environment:{}, fallback:null} |\n                                            wrapEnv($replEnv; $_menv.atoms);\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | TCOWrap($env; $_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce $value[1:-1][] as $xvalue (\n                                        $_menv;\n                                        . as $env | $xvalue | EVAL($env) | .env\n                                    )) as $env |\n                                    $value[-1] | TCOWrap($env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"try*\") |\n                                  if $value[2]\n                                  and ($value[2].value[0] | .kind == \"symbol\" and .value == \"catch*\")\n                                  then\n                                    try (\n                                        $value[1] | EVAL($_menv) as $exp | $exp.expr | TCOWrap($exp.env; $_orig_retenv; false)\n                                    ) catch ( . as $exc |\n                                                (if ($exc | is_jqmal_error) then\n                                                    $exc[19:] as $ex |\n                                                        try (\n                                                            $ex \n                                                            | fromjson\n                                                        ) catch (\n                                                            $ex |\n                                                            wrap(\"string\")\n                                                        )\n                                                else \n                                                    $exc|wrap(\"string\")\n                                                end) as $exc |\n                                                $value[2].value[2] | EVAL($currentEnv | childEnv([$value[2].value[1].value]; [$exc]) | wrapEnv($replEnv; $_menv.atoms)) as $ex |\n                                                $ex.expr | TCOWrap($ex.env; $_retenv; false)\n                                    )\n                                  else\n                                      $value[1] | EVAL($_menv) as $exp |\n                                      $exp.expr | TCOWrap($exp.env; $_orig_retenv; false)\n                                  end\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL($_menv) as $condenv |\n                                        (if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"})\n                                        else\n                                            $value[2]\n                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: ($_menv | env_remove_references($free_referencess)),\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $free_referencess,  # for dynamically scoped variables\n                                        is_macro: false\n                                } | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quote\") |\n                                    $value[1] | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quasiquote\") |\n                                    $value[1] | quasiquote | TCOWrap($_menv; $_orig_retenv; true)\n                            ) //\n                            (\n                                (\n                                    .[0] | EVAL($_menv) |\n                                    (.env | setpath([\"atoms\"]; $_menv.atoms)) as $_menv |\n                                    .expr\n                                ) as $fn |\n                                if $fn.kind == \"function\" and $fn.is_macro then\n                                    $fn | interpret($value[1:]; $_menv; _eval_here) as $exprenv |\n                                    $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; true)\n                                else\n                                    $value[1:] |\n                                    (reduce .[] as $elem (\n                                        {env: $_menv, val: []};\n                                        # debug(\".val: \\(.val)     elem=\\($elem)\") |\n                                        . as $dot | $elem | EVAL($dot.env) as $eval_env |\n                                            ($dot.env | setpath([\"atoms\"]; $eval_env.env.atoms)) as $_menv |\n                                            {env: $_menv, val: ($dot.val + [$eval_env.expr])}\n                                            # | debug(\".val: \\(.val)\")\n                                    )) as $expr |\n                                        # debug(\"fn.kind: \\($fn.kind)\", \"expr: \\($expr)\") |\n                                            $fn |\n                                            interpret($expr.val; $expr.env; _eval_here) as $exprenv |\n                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)\n                                end\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n                .env as $e |\n               {kind:\"vector\", value:.expr} |\n               TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .env as $e |\n                {kind:\"hashmap\", value:.expr|from_entries} |\n                TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"function\") |\n                . | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get($currentEnv) // jqmal_error(\"'\\(.)' not found\") |\n                TCOWrap($_menv; $_orig_retenv; false)\n            ) //\n            TCOWrap($_menv; $_orig_retenv; false)\n        end\n    ) ] |\n    last |\n    {expr: .ast, env:(.ret_env // .env)};\n\ndef PRINT(env):\n    pr_str(env);\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) | .env as $env |\n        (.expr | PRINT($env)), ($env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | wrapEnv({})\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    | eval_ign(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))))\")\n    | eval_ign(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n    | env_set_(.; \"*ARGV*\"; {kind:\"list\", value:[$ARGS.positional[1:] | .[] | {kind:\"string\", value:.}]})\n    |\n    if $ARGS.positional|length > 0 then\n        eval_ign(\"(load-file \\($ARGS.positional[0] | tojson))\") |\n        empty\n    else\n        repl\n    end\n"
  },
  {
    "path": "impls/jq/stepA_mal.jq",
    "content": "include \"reader\";\ninclude \"printer\";\ninclude \"utils\";\ninclude \"interp\";\ninclude \"env\";\ninclude \"core\";\n\ndef READ:\n    read_form;\n\ndef recurseflip(x; y):\n    recurse(y; x);\n\ndef TCOWrap(env; retenv; continue):\n    {\n        ast: .,\n        env: env,\n        ret_env: (if retenv != null then (retenv | setpath([\"atoms\"]; env.atoms)) else retenv end),\n        finish: (continue | not),\n        cont: true # set inside\n    };\n\ndef quasiquote:\n\n    # If input is ('name, arg), return arg, else nothing.\n    def _starts_with(name):\n        select(.kind == \"list\")\n        | .value\n        | select(length == 2)\n        | select(.[0] | .kind == \"symbol\" and .value == name)\n        | .[1];\n\n    # Right-folding function. The current element is provided as input.\n    def qq_loop(acc):\n        (_starts_with(\"splice-unquote\")\n         | {kind:\"list\", value:[{kind:\"symbol\", value:\"concat\"}, ., acc]})\n        // {kind:\"list\", value:[{kind:\"symbol\", value:\"cons\"}, quasiquote, acc]};\n\n    # Adapt parameters for jq foldr.\n    def qq_foldr:\n        .value\n        | reverse\n        | reduce .[] as $elt ({kind:\"list\", value:[]};\n                              . as $acc | $elt | qq_loop($acc));\n\n    _starts_with(\"unquote\")\n    // (\n        select(.kind == \"list\")\n        | qq_foldr\n    ) // (\n        select(.kind == \"vector\")\n        | {kind:\"list\", value: [{kind:\"symbol\", value:\"vec\"}, qq_foldr]}\n    ) // (\n        select(.kind == \"hashmap\" or .kind == \"symbol\")\n        | {kind:\"list\", value:[{kind:\"symbol\", value:\"quote\"}, .]}\n    ) // .;\n\ndef set_macro_function:\n    if .kind != \"function\" then\n        jqmal_error(\"expected a function to be defined by defmacro!\")\n    else\n        .is_macro |= true\n    end;\n\ndef EVAL(env):\n    def _eval_here:\n        .env as $env | .expr | EVAL($env);\n\n    . as $ast\n    | TCOWrap(env; null; true)\n    | [ recurseflip(.cont;\n        .env as $_menv\n        | (if $DEBUG then debug(\"EVAL: \\($ast | pr_str($_menv))\") else . end)\n        | (if $DEBUG then debug(\"ATOMS: \\($_menv.atoms)\") else . end)\n        | if .finish then\n            .cont |= false\n        else\n            (.ret_env//.env) as  $_retenv\n            | .ret_env as $_orig_retenv\n            | .ast\n            | . as $init\n            | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env \"package\"\n            | $_menv | unwrapReplEnv    as $replEnv    # -\n            | $init\n            |\n            if \"DEBUG-EVAL\" | env_get($currentEnv) |\n                . != null and .kind != \"false\" and .kind != \"nil\"\n            then\n                (\"EVAL: \\(pr_str(env))\" | _display | empty), .\n            end\n            |\n            (select(.kind == \"list\") |\n                .value | select(length != 0) as $value |\n                            (\n                                select(.[0].value == \"atoms??\") |\n                                    $_menv.atoms | keys | map(wrap(\"string\")) | wrap(\"list\") | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"def!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"defmacro!\") |\n                                    $value[2] | EVAL($_menv) |\n                                        .expr |= set_macro_function |\n                                        addToEnv($value[1].value) as $val |\n                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"let*\") |\n                                        (reduce ($value[1].value | nwise(2)) as $xvalue (\n                                            # Initial accumulator\n                                            {parent:$currentEnv, environment:{}, fallback:null} |\n                                            wrapEnv($replEnv; $_menv.atoms);\n                                            # Loop body\n                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |\n                                                env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env\n                                                    | $value[2] | TCOWrap($env; $_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"do\") |\n                                    (reduce $value[1:-1][] as $xvalue (\n                                        $_menv;\n                                        . as $env | $xvalue | EVAL($env) | .env\n                                    )) as $env |\n                                    $value[-1] | TCOWrap($env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"try*\") |\n                                  if $value[2]\n                                  and ($value[2].value[0] | .kind == \"symbol\" and .value == \"catch*\")\n                                  then\n                                    try (\n                                        $value[1] | EVAL($_menv) as $exp | $exp.expr | TCOWrap($exp.env; $_orig_retenv; false)\n                                    ) catch ( . as $exc |\n                                                (if ($exc | is_jqmal_error) then\n                                                    $exc[19:] as $ex |\n                                                        try (\n                                                            $ex \n                                                            | fromjson\n                                                        ) catch (\n                                                            $ex |\n                                                            wrap(\"string\")\n                                                        )\n                                                else \n                                                    $exc|wrap(\"string\")\n                                                end) as $exc |\n                                                $value[2].value[2] | EVAL($currentEnv | childEnv([$value[2].value[1].value]; [$exc]) | wrapEnv($replEnv; $_menv.atoms)) as $ex |\n                                                $ex.expr | TCOWrap($ex.env; $_retenv; false)\n                                    )\n                                  else\n                                      $value[1] | EVAL($_menv) as $exp |\n                                      $exp.expr | TCOWrap($exp.env; $_orig_retenv; false)\n                                  end\n                            ) //\n                            (\n                                select(.[0].value == \"if\") |\n                                    $value[1] | EVAL($_menv) as $condenv |\n                                        (if ([\"false\", \"nil\"] | contains([$condenv.expr.kind])) then\n                                            ($value[3] // {kind:\"nil\"})\n                                        else\n                                            $value[2]\n                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)\n                            ) //\n                            (\n                                select(.[0].value == \"fn*\") |\n                                    # (fn* args body)\n                                    $value[1].value | map(.value) as $binds | \n                                    ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {\n                                        kind: \"function\",\n                                        binds: $binds,\n                                        env: ($_menv | env_remove_references($free_referencess)),\n                                        body: $value[2],\n                                        names: [], # we can't do that circular reference thing\n                                        free_referencess: $free_referencess,  # for dynamically scoped variables\n                                        is_macro: false\n                                } | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quote\") |\n                                    $value[1] | TCOWrap($_menv; $_orig_retenv; false)\n                            ) //\n                            (\n                                select(.[0].value == \"quasiquote\") |\n                                    $value[1] | quasiquote | TCOWrap($_menv; $_orig_retenv; true)\n                            ) //\n                            (\n                                (\n                                    .[0] | EVAL($_menv) |\n                                    (.env | setpath([\"atoms\"]; $_menv.atoms)) as $_menv |\n                                    .expr\n                                ) as $fn |\n                                if $fn.kind == \"function\" and $fn.is_macro then\n                                    $fn | interpret($value[1:]; $_menv; _eval_here) as $exprenv |\n                                    $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; true)\n                                else\n                                    $value[1:] |\n                                    (reduce .[] as $elem (\n                                        {env: $_menv, val: []};\n                                        # debug(\".val: \\(.val)     elem=\\($elem)\") |\n                                        . as $dot | $elem | EVAL($dot.env) as $eval_env |\n                                            ($dot.env | setpath([\"atoms\"]; $eval_env.env.atoms)) as $_menv |\n                                            {env: $_menv, val: ($dot.val + [$eval_env.expr])}\n                                            # | debug(\".val: \\(.val)\")\n                                    )) as $expr |\n                                        # debug(\"fn.kind: \\($fn.kind)\", \"expr: \\($expr)\") |\n                                            $fn |\n                                            interpret($expr.val; $expr.env; _eval_here) as $exprenv |\n                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)\n                                end\n                            )\n            ) //\n            (\n                select(.kind == \"vector\") |\n                .value |\n                reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x | EVAL($acc.env) |\n                    .expr |= $acc.expr + [.]\n                ) |\n                .env as $e |\n               {kind:\"vector\", value:.expr} |\n               TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"hashmap\") |\n                .value | to_entries |\n                 reduce .[] as $x ({expr:[], env:$_menv};\n                    . as $acc |\n                    $x.value.value | EVAL($acc.env) |\n                    .expr |= (. as $e | $acc.expr + [$x | .value.value |= $e])\n                ) |\n                .env as $e |\n                {kind:\"hashmap\", value:.expr|from_entries} |\n                TCOWrap($e; $_orig_retenv; false)\n            ) //\n            (\n                select(.kind == \"function\") |\n                . | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to\n            ) //\n            (\n                select(.kind == \"symbol\") |\n                .value |\n                env_get($currentEnv) // jqmal_error(\"'\\(.)' not found\") |\n                TCOWrap($_menv; $_orig_retenv; false)\n            ) //\n            TCOWrap($_menv; $_orig_retenv; false)\n        end\n        | (if $DEBUG then debug(\"POSTEVAL: \\($ast | pr_str($_menv)) = \\(.ast | pr_str($_menv))\") else . end)\n    ) ] |\n    last |\n    {expr: .ast, env:(.ret_env // .env)};\n\ndef PRINT(env):\n    pr_str(env);\n\ndef repl:\n    # Infinite generator, interrupted by an exception or ./run.\n    . as $env | \"user> \" | __readline |\n    try (\n        READ | EVAL($env) | .env as $env |\n        (.expr | PRINT($env)), ($env | repl)\n    ) catch if is_jqmal_error then\n        ., ($env | repl)\n    else\n        halt_error\n    end;\n\ndef eval_ign(expr):\n    . as $env | expr | READ | EVAL($env) | .env;\n\n# The main program starts here.\n    {\n        parent: null,\n        environment: core_identify,\n        fallback: null\n    }\n    | wrapEnv({})\n    | eval_ign(\"(def! *host-language* \\\"jq\\\")\")\n    | eval_ign(\"(def! not (fn* (a) (if a false true)))\")\n    | eval_ign(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))))\")\n    | eval_ign(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n    | env_set_(.; \"*ARGV*\"; {kind:\"list\", value:[$ARGS.positional[1:] | .[] | {kind:\"string\", value:.}]})\n    |\n    if $ARGS.positional|length > 0 then\n        eval_ign(\"(load-file \\($ARGS.positional[0] | tojson))\") |\n        empty\n    else\n        eval_ign(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\") |\n        repl\n    end\n"
  },
  {
    "path": "impls/jq/utils.jq",
    "content": "def nwise(n):\n    def _nwise:\n        if length <= n then \n            .\n        else\n            .[0:n], (.[n:] | _nwise)\n        end;\n    _nwise;\n\ndef abs(x):\n    if x < 0 then 0 - x else x end;\n\ndef jqmal_error(e):\n    error(\"JqMAL Exception :: \" + e);\n\ndef is_jqmal_error:\n    startswith(\"JqMAL Exception :: \");\n\ndef wrap(kind):\n    {\n        kind: kind,\n        value: .\n    };\n\ndef find_free_references(keys):\n    def _refs:\n      if . == null then [] else\n        . as $dot\n        | if .kind == \"symbol\" then\n            if keys | contains([$dot.value]) then [] else [$dot.value] end\n        else if \"list\" == $dot.kind then\n            if $dot.value|length == 0 then\n                []\n            else\n                # if - scan args\n                # def! - scan body\n                # let* - add keys sequentially, scan body\n                # fn* - add keys, scan body\n                # quote - []\n                # quasiquote - ???\n                $dot.value[0] as $head\n                | if $head.kind == \"symbol\" then\n                    (\n                        select($head.value == \"if\") | $dot.value[1:] | map(_refs) | reduce .[] as $x ([]; . + $x)\n                    ) // (\n                        select($head.value == \"def!\") | $dot.value[2] | _refs\n                    ) // (\n                        select($head.value == \"let*\") | $dot.value[2] | find_free_references(($dot.value[1].value as $value | ([ range(0; $value|length; 2) ] | map(select(. % 2 == 0) | $value[.].value))) + keys)\n                    ) // (\n                        select($head.value == \"fn*\") | $dot.value[2] | find_free_references(($dot.value[1].value | map(.value)) + keys)\n                    ) // (\n                        select($head.value == \"quote\") | []\n                    ) // (\n                        select($head.value == \"quasiquote\") | []\n                    ) // ($dot.value | map(_refs) | reduce .[] as $x ([]; . + $x))\n                  else\n                    [ $dot.values[1:][] | _refs ]\n                  end\n                end\n        else if \"vector\" == $dot.kind then\n            ($dot.value | map(_refs) | reduce .[] as $x ([]; . + $x))\n        else if \"hashmap\" == $dot.kind then\n            ([$dot.value | to_entries[] | ({kind: .value.kkind, value: .key}, .value.value) ] | map(_refs) | reduce .[] as $x ([]; . + $x))\n        else\n            []\n        end end end end\n      end;\n    _refs | unique;\n\n# The following IO actions are implemented in rts.py.\n\ndef __readline:\n    [\"readline\", .] | debug | input;\n\n# The output is not very interesting.\n# 'input' here only ensures that the python process has printed the\n# message before any further output by the jq process.\ndef _display:\n    [\"display\", .] | debug | input;\n\ndef slurp:\n    [\"slurp\", .] | debug | input;\n"
  },
  {
    "path": "impls/js/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ libreadline-dev nodejs npm\n\nENV NPM_CONFIG_CACHE /mal/.npm\n"
  },
  {
    "path": "impls/js/Makefile",
    "content": "\nTESTS = tests/types.js tests/reader.js\n\nSOURCES_BASE = node_readline.js types.js reader.js printer.js interop.js\nSOURCES_LISP = env.js core.js stepA_mal.js\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\nWEB_SOURCES = $(SOURCES:node_readline.js=jq_readline.js)\n\nSTEPS = step0_repl.js step1_read_print.js step2_eval.js step3_env.js \\\n\tstep4_if_fn_do.js step5_tco.js step6_file.js \\\n\tstep7_quote.js step8_macros.js step9_try.js stepA_mal.js\n\nall: node_modules\n\ndist: mal.js mal web/mal.js\n\nnode_modules:\n\tnpm install\n\n$(STEPS): node_modules\n\nmal.js: $(SOURCES)\n\tcat $+ | grep -v \"= *require('./\" >> $@\n\nmal: mal.js\n\techo \"#!/usr/bin/env node\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nweb/mal.js: $(WEB_SOURCES)\n\tcat $+ | grep -v \"= *require('./\" > $@\n\nclean:\n\trm -f mal.js web/mal.js\n\trm -rf node_modules\n\n.PHONY: tests $(TESTS)\n\ntests: $(TESTS)\n\n$(TESTS):\n\t@echo \"Running $@\"; \\\n\tnode $@ || exit 1; \\\n"
  },
  {
    "path": "impls/js/core.js",
    "content": "// Node vs browser behavior\nvar core = {};\nif (typeof module === 'undefined') {\n    var exports = core;\n} else {\n    var types = require('./types'),\n        readline = require('./node_readline'),\n        reader = require('./reader'),\n        printer = require('./printer'),\n        interop = require('./interop');\n}\n\n// Errors/Exceptions\nfunction mal_throw(exc) { throw exc; }\n\n\n// String functions\nfunction pr_str() {\n    return Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, true);\n    }).join(\" \");\n}\n\nfunction str() {\n    return Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, false);\n    }).join(\"\");\n}\n\nfunction prn() {\n    printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, true);\n    }));\n}\n\nfunction println() {\n    printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {\n        return printer._pr_str(exp, false);\n    }));\n}\n\nfunction slurp(f) {\n    if (typeof require !== 'undefined') {\n        return require('fs').readFileSync(f, 'utf-8');\n    } else {\n        var req = new XMLHttpRequest();\n        req.open(\"GET\", f, false);\n        req.send();\n        if (req.status == 200) {\n            return req.responseText;\n        } else {\n            throw new Error(\"Failed to slurp file: \" + f);\n        }\n    }\n}\n\n\n// Number functions\nfunction time_ms() { return new Date().getTime(); }\n\n\n// Hash Map functions\nfunction assoc(src_hm) {\n    var hm = types._clone(src_hm);\n    var args = [hm].concat(Array.prototype.slice.call(arguments, 1));\n    return types._assoc_BANG.apply(null, args);\n}\n\nfunction dissoc(src_hm) {\n    var hm = types._clone(src_hm);\n    var args = [hm].concat(Array.prototype.slice.call(arguments, 1));\n    return types._dissoc_BANG.apply(null, args);\n}\n\nfunction get(hm, key) {\n    if (hm != null && key in hm) {\n        return hm[key];\n    } else {\n        return null;\n    }\n}\n\nfunction contains_Q(hm, key) {\n    if (key in hm) { return true; } else { return false; }\n}\n\nfunction keys(hm) { return Object.keys(hm); }\nfunction vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); }\n\n\n// Sequence functions\nfunction cons(a, b) { return [a].concat(b); }\n\nfunction concat(lst) {\n    lst = lst || [];\n    return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));\n}\nfunction vec(lst) {\n    if (types._list_Q(lst)) {\n        var v = Array.prototype.slice.call(lst, 0);\n        v.__isvector__ = true;\n        return v;\n    } else {\n        return lst;\n    }\n}\n\nfunction nth(lst, idx) {\n    if (idx < lst.length) { return lst[idx]; }\n    else                  { throw new Error(\"nth: index out of range\"); }\n}\n\nfunction first(lst) { return (lst === null) ? null : lst[0]; }\n\nfunction rest(lst) { return (lst == null) ? [] : lst.slice(1); }\n\nfunction empty_Q(lst) { return lst.length === 0; }\n\nfunction count(s) {\n    if (Array.isArray(s)) { return s.length; }\n    else if (s === null)  { return 0; }\n    else                  { return Object.keys(s).length; }\n}\n\nfunction conj(lst) {\n    if (types._list_Q(lst)) {\n        return Array.prototype.slice.call(arguments, 1).reverse().concat(lst);\n    } else {\n        var v = lst.concat(Array.prototype.slice.call(arguments, 1));\n        v.__isvector__ = true;\n        return v;\n    }\n}\n\nfunction seq(obj) {\n    if (types._list_Q(obj)) {\n        return obj.length > 0 ? obj : null;\n    } else if (types._vector_Q(obj)) {\n        return obj.length > 0 ? Array.prototype.slice.call(obj, 0): null;\n    } else if (types._string_Q(obj)) {\n        return obj.length > 0 ? obj.split('') : null;\n    } else if (obj === null) {\n        return null;\n    } else {\n        throw new Error(\"seq: called on non-sequence\");\n    }\n}\n\n\nfunction apply(f) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1]));\n}\n\nfunction map(f, lst) {\n    return lst.map(function(el){ return f(el); });\n}\n\n\n// Metadata functions\nfunction with_meta(obj, m) {\n    var new_obj = types._clone(obj);\n    new_obj.__meta__ = m;\n    return new_obj;\n}\n\nfunction meta(obj) {\n    // TODO: support symbols and atoms\n    if ((!types._sequential_Q(obj)) &&\n        (!(types._hash_map_Q(obj))) &&\n        (!(types._function_Q(obj)))) {\n        throw new Error(\"attempt to get metadata from: \" + types._obj_type(obj));\n    }\n    return obj.__meta__;\n}\n\n\n// Atom functions\nfunction deref(atm) { return atm.val; }\nfunction reset_BANG(atm, val) { return atm.val = val; }\nfunction swap_BANG(atm, f) {\n    var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2));\n    atm.val = f.apply(f, args);\n    return atm.val;\n}\n\nfunction js_eval(str) {\n    return interop.js_to_mal(eval(str.toString()));\n}\n\nfunction js_method_call(object_method_str) {\n    var args = Array.prototype.slice.call(arguments, 1),\n        r = interop.resolve_js(object_method_str),\n        obj = r[0], f = r[1];\n    var res = f.apply(obj, args);\n    return interop.js_to_mal(res);\n}\n\n// types.ns is namespace of type functions\nvar ns = {'type': types._obj_type,\n          '=': types._equal_Q,\n          'throw': mal_throw,\n          'nil?': types._nil_Q,\n          'true?': types._true_Q,\n          'false?': types._false_Q,\n          'number?': types._number_Q,\n          'string?': types._string_Q,\n          'symbol': types._symbol,\n          'symbol?': types._symbol_Q,\n          'keyword': types._keyword,\n          'keyword?': types._keyword_Q,\n          'fn?': types._fn_Q,\n          'macro?': types._macro_Q,\n\n          'pr-str': pr_str,\n          'str': str,\n          'prn': prn,\n          'println': println,\n          'readline': readline.readline,\n          'read-string': reader.read_str,\n          'slurp': slurp,\n          '<'  : function(a,b){return a<b;},\n          '<=' : function(a,b){return a<=b;},\n          '>'  : function(a,b){return a>b;},\n          '>=' : function(a,b){return a>=b;},\n          '+'  : function(a,b){return a+b;},\n          '-'  : function(a,b){return a-b;},\n          '*'  : function(a,b){return a*b;},\n          '/'  : function(a,b){return a/b;},\n          \"time-ms\": time_ms,\n\n          'list': types._list,\n          'list?': types._list_Q,\n          'vector': types._vector,\n          'vector?': types._vector_Q,\n          'hash-map': types._hash_map,\n          'map?': types._hash_map_Q,\n          'assoc': assoc,\n          'dissoc': dissoc,\n          'get': get,\n          'contains?': contains_Q,\n          'keys': keys,\n          'vals': vals,\n\n          'sequential?': types._sequential_Q,\n          'cons': cons,\n          'concat': concat,\n          'vec': vec,\n          'nth': nth,\n          'first': first,\n          'rest': rest,\n          'empty?': empty_Q,\n          'count': count,\n          'apply': apply,\n          'map': map,\n\n          'conj': conj,\n          'seq': seq,\n\n          'with-meta': with_meta,\n          'meta': meta,\n          'atom': types._atom,\n          'atom?': types._atom_Q,\n          \"deref\": deref,\n          \"reset!\": reset_BANG,\n          \"swap!\": swap_BANG,\n\n          'js-eval': js_eval,\n          '.': js_method_call\n};\n\nexports.ns = core.ns = ns;\n"
  },
  {
    "path": "impls/js/env.js",
    "content": "// Node vs browser behavior\nvar env = {};\nif (typeof module === 'undefined') {\n    var exports = env;\n}\n\n// Env implementation\nfunction Env(outer, binds, exprs) {\n    this.data = {};\n    this.outer = outer || null;\n\n    if (binds && exprs) {\n        // Returns a new Env with symbols in binds bound to\n        // corresponding values in exprs\n        // TODO: check types of binds and exprs and compare lengths\n        for (var i=0; i<binds.length;i++) {\n            if (binds[i].value === \"&\") {\n                // variable length arguments\n                this.data[binds[i+1].value] = Array.prototype.slice.call(exprs, i);\n                break;\n            } else {\n                this.data[binds[i].value] = exprs[i];\n            }\n        }\n    }\n    return this;\n}\nEnv.prototype.find = function (key) {\n    if (key in this.data) { return this; }\n    else if (this.outer) {  return this.outer.find(key); }\n    else { return null; }\n};\nEnv.prototype.set = function(key, value) {\n    this.data[key] = value;\n    return value;\n};\nEnv.prototype.get = function(key) {\n    var env = this.find(key);\n    if (!env) { throw new Error(\"'\" + key + \"' not found\"); }\n    return env.data[key];\n};\n\nexports.Env = env.Env = Env;\n"
  },
  {
    "path": "impls/js/interop.js",
    "content": "// Node vs browser behavior\nvar interop = {};\nif (typeof module === 'undefined') {\n    var exports = interop,\n        GLOBAL = window;\n}\n\nfunction resolve_js(str) {\n    if (str.match(/\\./)) {\n        var re = /^(.*)\\.[^\\.]*$/,\n            match = re.exec(str);\n        return [eval(match[1]), eval(str)];\n    } else {\n        return [GLOBAL, eval(str)];\n    }\n}\n\nfunction js_to_mal(obj) {\n    if (obj === null || obj === undefined) {\n        return null;\n    }\n    var cache = [];\n    var str = JSON.stringify(obj, function(key, value) {\n        if (typeof value === 'object' && value !== null) {\n            if (cache.indexOf(value) !== -1) {\n                // Circular reference found, discard key\n                return;\n            }\n            // Store value in our collection\n            cache.push(value);\n        }\n        return value;\n    });\n    cache = null; // Enable garbage collection\n    return JSON.parse(str);\n}\n\nexports.resolve_js = interop.resolve_js = resolve_js;\nexports.js_to_mal = interop.js_to_mal = js_to_mal;\n"
  },
  {
    "path": "impls/js/jq_readline.js",
    "content": "var max_history_length = 1000;\n\nfunction jq_load_history(jq) {\n    if (localStorage['mal_history']) {\n        var lines = JSON.parse(localStorage['mal_history']);\n        if (lines.length > max_history_length) {\n            lines = lines.slice(lines.length-max_history_length);\n        }\n        jq.SetHistory(lines);\n    }\n}\n\nfunction jq_save_history(jq) {\n    var lines = jq.GetHistory();\n    localStorage['mal_history'] = JSON.stringify(lines);\n}\n\n\nvar readline = {\n    'readline': function(prompt_str) {\n            return prompt(prompt_str);\n        }};\n\n"
  },
  {
    "path": "impls/js/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline.so.8\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit.so.2\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nconst koffi = require('koffi');\nconst fs = require('fs');\n\nlet rllib;\ntry {\n    rllib = koffi.load(RL_LIB);\n} catch (e) {\n    console.error('ERROR loading RL_LIB:', RL_LIB, e);\n    throw e;\n}\nconst readlineFunc = rllib.func('char *readline(char *)');\nconst addHistoryFunc = rllib.func('int add_history(char *)');\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = typeof prompt !== 'undefined' ? prompt : \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { addHistoryFunc(lines[i]); }\n        }\n    }\n\n    var line = readlineFunc(prompt);\n    if (line) {\n        addHistoryFunc(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\n//module.exports = { readline };\nvar readline = exports;\n"
  },
  {
    "path": "impls/js/package.json",
    "content": "{\n    \"name\": \"mal\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in Javascript\",\n    \"dependencies\": {\n        \"koffi\": \"^2.12.1\"\n    }\n}\n"
  },
  {
    "path": "impls/js/printer.js",
    "content": "// Node vs browser behavior\nvar printer = {};\nif (typeof module !== 'undefined') {\n    var types = require('./types');\n    // map output/print to console.log\n    printer.println = exports.println = function () {\n        console.log.apply(console, arguments);\n    };\n}\n\nfunction _pr_str(obj, print_readably) {\n    if (typeof print_readably === 'undefined') { print_readably = true; }\n    var _r = print_readably;\n    var ot = types._obj_type(obj);\n    switch (ot) {\n    case 'list':\n        var ret = obj.map(function(e) { return _pr_str(e,_r); });\n        return \"(\" + ret.join(' ') + \")\";\n    case 'vector':\n        var ret = obj.map(function(e) { return _pr_str(e,_r); });\n        return \"[\" + ret.join(' ') + \"]\";\n    case 'hash-map':\n        var ret = [];\n        for (var k in obj) {\n            ret.push(_pr_str(k,_r), _pr_str(obj[k],_r));\n        }\n        return \"{\" + ret.join(' ') + \"}\";\n    case 'string':\n        if (obj[0] === '\\u029e') {\n            return ':' + obj.slice(1);\n        } else if (_r) {\n            return '\"' + obj.replace(/\\\\/g, \"\\\\\\\\\")\n                .replace(/\"/g, '\\\\\"')\n                .replace(/\\n/g, \"\\\\n\") + '\"'; // string\n        } else {\n            return obj;\n        }\n    case 'keyword':\n        return ':' + obj.slice(1);\n    case 'nil':\n        return \"nil\";\n    case 'atom':\n        return \"(atom \" + _pr_str(obj.val,_r) + \")\";\n    default:\n        return obj.toString();\n    }\n}\n\nexports._pr_str = printer._pr_str = _pr_str;\n\n"
  },
  {
    "path": "impls/js/reader.js",
    "content": "// Node vs browser behavior\nvar reader = {};\nif (typeof module !== 'undefined') {\n    var types = require('./types');\n} else {\n    var exports = reader;\n}\n\nfunction Reader(tokens) {\n    // copy\n    this.tokens = tokens.map(function (a) { return a; });\n    this.position = 0;\n}\nReader.prototype.next = function() { return this.tokens[this.position++]; }\nReader.prototype.peek = function() { return this.tokens[this.position]; }\n\nfunction tokenize(str) {\n    var re = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g;\n    var results = [];\n    while ((match = re.exec(str)[1]) != '') {\n        if (match[0] === ';') { continue; }\n        results.push(match);\n    }\n    return results;\n}\n\nfunction read_atom (reader) {\n    var token = reader.next();\n    //console.log(\"read_atom:\", token);\n    if (token.match(/^-?[0-9]+$/)) {\n        return parseInt(token,10)        // integer\n    } else if (token.match(/^-?[0-9][0-9.]*$/)) {\n        return parseFloat(token);     // float\n    } else if (token.match(/^\"(?:\\\\.|[^\\\\\"])*\"$/)) {\n        return token.slice(1,token.length-1) \n            .replace(/\\\\(.)/g, function (_, c) { return c === \"n\" ? \"\\n\" : c})\n    } else if (token[0] === \"\\\"\") {\n            throw new Error(\"expected '\\\"', got EOF\");\n    } else if (token[0] === \":\") {\n        return types._keyword(token.slice(1));\n    } else if (token === \"nil\") {\n        return null;\n    } else if (token === \"true\") {\n        return true;\n    } else if (token === \"false\") {\n        return false;\n    } else {\n        return types._symbol(token); // symbol\n    }\n}\n\n// read list of tokens\nfunction read_list(reader, start, end) {\n    start = start || '(';\n    end = end || ')';\n    var ast = [];\n    var token = reader.next();\n    if (token !== start) {\n        throw new Error(\"expected '\" + start + \"'\");\n    }\n    while ((token = reader.peek()) !== end) {\n        if (!token) {\n            throw new Error(\"expected '\" + end + \"', got EOF\");\n        }\n        ast.push(read_form(reader));\n    }\n    reader.next();\n    return ast;\n}\n\n// read vector of tokens\nfunction read_vector(reader) {\n    var lst = read_list(reader, '[', ']');\n    return types._vector.apply(null, lst);\n}\n\n// read hash-map key/value pairs\nfunction read_hash_map(reader) {\n    var lst = read_list(reader, '{', '}');\n    return types._hash_map.apply(null, lst);\n}\n\nfunction read_form(reader) {\n    var token = reader.peek();\n    switch (token) {\n    // reader macros/transforms\n    case ';': return null; // Ignore comments\n    case '\\'': reader.next();\n               return [types._symbol('quote'), read_form(reader)];\n    case '`': reader.next();\n              return [types._symbol('quasiquote'), read_form(reader)];\n    case '~': reader.next();\n              return [types._symbol('unquote'), read_form(reader)];\n    case '~@': reader.next();\n               return [types._symbol('splice-unquote'), read_form(reader)];\n    case '^': reader.next();\n              var meta = read_form(reader);\n              return [types._symbol('with-meta'), read_form(reader), meta];\n    case '@': reader.next();\n              return [types._symbol('deref'), read_form(reader)];\n\n    // list\n    case ')': throw new Error(\"unexpected ')'\");\n    case '(': return read_list(reader);\n\n    // vector\n    case ']': throw new Error(\"unexpected ']'\");\n    case '[': return read_vector(reader);\n\n    // hash-map\n    case '}': throw new Error(\"unexpected '}'\");\n    case '{': return read_hash_map(reader);\n\n    // atom\n    default:  return read_atom(reader);\n    }\n}\n\nfunction BlankException(msg) {\n}\n\nfunction read_str(str) {\n    var tokens = tokenize(str);\n    if (tokens.length === 0) { throw new BlankException(); }\n    return read_form(new Reader(tokens))\n}\n\nexports.Reader = reader.Reader = Reader;\nexports.BlankException = reader.BlankException = BlankException;\nexports.tokenize = reader.tokenize = tokenize;\nexports.read_form = reader.read_form = read_form;\nexports.read_str = reader.read_str = read_str;\n"
  },
  {
    "path": "impls/js/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/${STEP:-stepA_mal}.js \"${@}\"\n"
  },
  {
    "path": "impls/js/step0_repl.js",
    "content": "if (typeof module !== 'undefined') {\n    var readline = require('./node_readline');\n    var printer = require('./printer');\n}\n\n// read\nfunction READ(str) {\n    return str;\n}\n\n// eval\nfunction EVAL(ast, env) {\n    return ast;\n}\n\n// print\nfunction PRINT(exp) {\n    return exp;\n}\n\n// repl\nvar rep = function(str) { return PRINT(EVAL(READ(str), {})); };\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        if (line) { printer.println(rep(line)); }\n    }\n}\n"
  },
  {
    "path": "impls/js/step1_read_print.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction EVAL(ast, env) {\n    return ast;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar re = function(str) { return EVAL(READ(str), {}); };\nvar rep = function(str) { return PRINT(EVAL(READ(str), {})); };\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step2_eval.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction _EVAL(ast, env) {\n    // printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        if (ast.value in env) {\n            return env[ast.value];\n        } else {\n            throw new Error(\"'\" + ast.value + \"' not found\");\n        }\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    // apply list\n    var f = EVAL(ast[0], env);\n    var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n    return f.apply(f, args);\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nrepl_env = {};\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\nrepl_env['+'] = function(a,b){return a+b;};\nrepl_env['-'] = function(a,b){return a-b;};\nrepl_env['*'] = function(a,b){return a*b;};\nrepl_env['/'] = function(a,b){return a/b;};\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step3_env.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction _EVAL(ast, env) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    // apply list\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        return EVAL(a2, let_env);\n    default:\n        var f = EVAL(a0, env);\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        return f.apply(f, args);\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\nrepl_env.set('+', function(a,b){return a+b;});\nrepl_env.set('-', function(a,b){return a-b;});\nrepl_env.set('*', function(a,b){return a*b;});\nrepl_env.set('/', function(a,b){return a/b;});\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step4_if_fn_do.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction _EVAL(ast, env) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    // apply list\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        return EVAL(a2, let_env);\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        return EVAL(ast[ast.length-1], env);\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            return typeof a3 !== \"undefined\" ? EVAL(a3, env) : null;\n        } else {\n            return EVAL(a2, env);\n        }\n    case \"fn*\":\n        return function() {\n            return EVAL(a2, new Env(env, a1, arguments));\n        };\n    default:\n        var f = EVAL(a0, env);\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        return f.apply(f, args);\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step5_tco.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction _EVAL(ast, env) {\n    while (true) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    // apply list\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var f = EVAL(a0, env);\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(args);\n        } else {\n            return f.apply(f, args);\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step6_file.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction _EVAL(ast, env) {\n    while (true) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    // apply list\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var f = EVAL(a0, env);\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(args);\n        } else {\n            return f.apply(f, args);\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\nrepl_env.set('eval', function(ast) {\n    return EVAL(ast, repl_env); });\nrepl_env.set('*ARGV*', []);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nif (typeof process !== 'undefined' && process.argv.length > 2) {\n    repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));\n    rep('(load-file \"' + process.argv[2] + '\")');\n    process.exit(0);\n}\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step7_quote.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction qqLoop (acc, elt) {\n    if (types._list_Q(elt) && elt.length\n        && types._symbol_Q(elt[0]) && elt[0].value == 'splice-unquote') {\n        return [types._symbol(\"concat\"), elt[1], acc];\n    } else {\n        return [types._symbol(\"cons\"), quasiquote (elt), acc];\n    }\n}\nfunction quasiquote(ast) {\n    if (types._list_Q(ast) && 0<ast.length\n        && types._symbol_Q(ast[0]) && ast[0].value == 'unquote') {\n        return ast[1];\n    } else if (types._list_Q(ast)) {\n        return ast.reduceRight(qqLoop,[]);\n    } else if (types._vector_Q(ast)) {\n        return [types._symbol(\"vec\"), ast.reduceRight(qqLoop,[])];\n    } else if (types._symbol_Q(ast) || types._hash_map_Q(ast)) {\n        return [types._symbol(\"quote\"), ast];\n    } else {\n        return ast;\n    }\n}\n\nfunction _EVAL(ast, env) {\n    while (true) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    // apply list\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"quote\":\n        return a1;\n    case \"quasiquote\":\n        ast = quasiquote(a1);\n        break;\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var f = EVAL(a0, env);\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(args);\n        } else {\n            return f.apply(f, args);\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\nrepl_env.set('eval', function(ast) {\n    return EVAL(ast, repl_env); });\nrepl_env.set('*ARGV*', []);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nif (typeof process !== 'undefined' && process.argv.length > 2) {\n    repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));\n    rep('(load-file \"' + process.argv[2] + '\")');\n    process.exit(0);\n}\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step8_macros.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction qqLoop (acc, elt) {\n    if (types._list_Q(elt) && elt.length\n        && types._symbol_Q(elt[0]) && elt[0].value == 'splice-unquote') {\n        return [types._symbol(\"concat\"), elt[1], acc];\n    } else {\n        return [types._symbol(\"cons\"), quasiquote (elt), acc];\n    }\n}\nfunction quasiquote(ast) {\n    if (types._list_Q(ast) && 0<ast.length\n        && types._symbol_Q(ast[0]) && ast[0].value == 'unquote') {\n        return ast[1];\n    } else if (types._list_Q(ast)) {\n        return ast.reduceRight(qqLoop,[]);\n    } else if (types._vector_Q(ast)) {\n        return [types._symbol(\"vec\"), ast.reduceRight(qqLoop,[])];\n    } else if (types._symbol_Q(ast) || types._hash_map_Q(ast)) {\n        return [types._symbol(\"quote\"), ast];\n    } else {\n        return ast;\n    }\n}\n\nfunction _EVAL(ast, env) {\n    while (true) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n    // apply list\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"quote\":\n        return a1;\n    case \"quasiquote\":\n        ast = quasiquote(a1);\n        break;\n    case 'defmacro!':\n        var func = types._clone(EVAL(a2, env));\n        func._ismacro_ = true;\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, func);\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var f = EVAL(a0, env);\n        if (f._ismacro_) {\n            ast = f.apply(f, ast.slice(1));\n            break;\n        }\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(args);\n        } else {\n            return f.apply(f, args);\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\nrepl_env.set('eval', function(ast) {\n    return EVAL(ast, repl_env); });\nrepl_env.set('*ARGV*', []);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif (typeof process !== 'undefined' && process.argv.length > 2) {\n    repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));\n    rep('(load-file \"' + process.argv[2] + '\")');\n    process.exit(0);\n}\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/step9_try.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction qqLoop (acc, elt) {\n    if (types._list_Q(elt) && elt.length\n        && types._symbol_Q(elt[0]) && elt[0].value == 'splice-unquote') {\n        return [types._symbol(\"concat\"), elt[1], acc];\n    } else {\n        return [types._symbol(\"cons\"), quasiquote (elt), acc];\n    }\n}\nfunction quasiquote(ast) {\n    if (types._list_Q(ast) && 0<ast.length\n        && types._symbol_Q(ast[0]) && ast[0].value == 'unquote') {\n        return ast[1];\n    } else if (types._list_Q(ast)) {\n        return ast.reduceRight(qqLoop,[]);\n    } else if (types._vector_Q(ast)) {\n        return [types._symbol(\"vec\"), ast.reduceRight(qqLoop,[])];\n    } else if (types._symbol_Q(ast) || types._hash_map_Q(ast)) {\n        return [types._symbol(\"quote\"), ast];\n    } else {\n        return ast;\n    }\n}\n\nfunction _EVAL(ast, env) {\n    while (true) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n    // apply list\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"quote\":\n        return a1;\n    case \"quasiquote\":\n        ast = quasiquote(a1);\n        break;\n    case 'defmacro!':\n        var func = types._clone(EVAL(a2, env));\n        func._ismacro_ = true;\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, func);\n    case \"try*\":\n        try {\n            return EVAL(a1, env);\n        } catch (exc) {\n            if (a2 && a2[0].value === \"catch*\") {\n                if (exc instanceof Error) { exc = exc.message; }\n                return EVAL(a2[2], new Env(env, [a2[1]], [exc]));\n            } else {\n                throw exc;\n            }\n        }\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var f = EVAL(a0, env);\n        if (f._ismacro_) {\n            ast = f.apply(f, ast.slice(1));\n            break;\n        }\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(args);\n        } else {\n            return f.apply(f, args);\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\nrepl_env.set('eval', function(ast) {\n    return EVAL(ast, repl_env); });\nrepl_env.set('*ARGV*', []);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif (typeof process !== 'undefined' && process.argv.length > 2) {\n    repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));\n    rep('(load-file \"' + process.argv[2] + '\")');\n    process.exit(0);\n}\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/stepA_mal.js",
    "content": "if (typeof module !== 'undefined') {\n    var types = require('./types');\n    var readline = require('./node_readline');\n    var reader = require('./reader');\n    var printer = require('./printer');\n    var Env = require('./env').Env;\n    var core = require('./core');\n}\n\n// read\nfunction READ(str) {\n    return reader.read_str(str);\n}\n\n// eval\nfunction qqLoop (acc, elt) {\n    if (types._list_Q(elt) && elt.length\n        && types._symbol_Q(elt[0]) && elt[0].value == 'splice-unquote') {\n        return [types._symbol(\"concat\"), elt[1], acc];\n    } else {\n        return [types._symbol(\"cons\"), quasiquote (elt), acc];\n    }\n}\nfunction quasiquote(ast) {\n    if (types._list_Q(ast) && 0<ast.length\n        && types._symbol_Q(ast[0]) && ast[0].value == 'unquote') {\n        return ast[1];\n    } else if (types._list_Q(ast)) {\n        return ast.reduceRight(qqLoop,[]);\n    } else if (types._vector_Q(ast)) {\n        return [types._symbol(\"vec\"), ast.reduceRight(qqLoop,[])];\n    } else if (types._symbol_Q(ast) || types._hash_map_Q(ast)) {\n        return [types._symbol(\"quote\"), ast];\n    } else {\n        return ast;\n    }\n}\n\nfunction _EVAL(ast, env) {\n    while (true) {\n    // Show a trace if DEBUG-EVAL is enabled.\n    var dbgevalenv = env.find(\"DEBUG-EVAL\");\n    if (dbgevalenv !== null) {\n        var dbgeval = env.get(\"DEBUG-EVAL\");\n        if (dbgeval !== null && dbgeval !== false)\n            printer.println(\"EVAL:\", printer._pr_str(ast, true));\n    }\n    // Non-list types.\n    if (types._symbol_Q(ast)) {\n        return env.get(ast.value);\n    } else if (types._list_Q(ast)) {\n        // Exit this switch.\n    } else if (types._vector_Q(ast)) {\n        var v = ast.map(function(a) { return EVAL(a, env); });\n        v.__isvector__ = true;\n        return v;\n    } else if (types._hash_map_Q(ast)) {\n        var new_hm = {};\n        for (k in ast) {\n            new_hm[k] = EVAL(ast[k], env);\n        }\n        return new_hm;\n    } else {\n        return ast;\n    }\n    // apply list\n    if (ast.length === 0) {\n        return ast;\n    }\n\n    var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];\n    switch (a0.value) {\n    case \"def!\":\n        var res = EVAL(a2, env);\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, res);\n    case \"let*\":\n        var let_env = new Env(env);\n        for (var i=0; i < a1.length; i+=2) {\n            if (!a1[i].constructor || a1[i].constructor.name !== 'Symbol') {\n                throw new Error(\"env.get key must be a symbol\")\n            }\n            let_env.set(a1[i].value, EVAL(a1[i+1], let_env));\n        }\n        ast = a2;\n        env = let_env;\n        break;\n    case \"quote\":\n        return a1;\n    case \"quasiquote\":\n        ast = quasiquote(a1);\n        break;\n    case 'defmacro!':\n        var func = types._clone(EVAL(a2, env));\n        func._ismacro_ = true;\n        if (!a1.constructor || a1.constructor.name !== 'Symbol') {\n            throw new Error(\"env.get key must be a symbol\")\n        }\n        return env.set(a1.value, func);\n    case \"try*\":\n        try {\n            return EVAL(a1, env);\n        } catch (exc) {\n            if (a2 && a2[0].value === \"catch*\") {\n                if (exc instanceof Error) { exc = exc.message; }\n                return EVAL(a2[2], new Env(env, [a2[1]], [exc]));\n            } else {\n                throw exc;\n            }\n        }\n    case \"do\":\n        for (var i=1; i < ast.length - 1; i++) {\n            EVAL(ast[i], env);\n        }\n        ast = ast[ast.length-1];\n        break;\n    case \"if\":\n        var cond = EVAL(a1, env);\n        if (cond === null || cond === false) {\n            ast = (typeof a3 !== \"undefined\") ? a3 : null;\n        } else {\n            ast = a2;\n        }\n        break;\n    case \"fn*\":\n        return types._function(EVAL, Env, a2, env, a1);\n    default:\n        var f = EVAL(a0, env);\n        if (f._ismacro_) {\n            ast = f.apply(f, ast.slice(1));\n            break;\n        }\n        var args = ast.slice(1).map(function(a) { return EVAL(a, env); });\n        if (f.__ast__) {\n            ast = f.__ast__;\n            env = f.__gen_env__(args);\n        } else {\n            return f.apply(f, args);\n        }\n    }\n\n    }\n}\n\nfunction EVAL(ast, env) {\n    var result = _EVAL(ast, env);\n    return (typeof result !== \"undefined\") ? result : null;\n}\n\n// print\nfunction PRINT(exp) {\n    return printer._pr_str(exp, true);\n}\n\n// repl\nvar repl_env = new Env();\nvar rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };\n\n// core.js: defined using javascript\nfor (var n in core.ns) { repl_env.set(n, core.ns[n]); }\nrepl_env.set('eval', function(ast) {\n    return EVAL(ast, repl_env); });\nrepl_env.set('*ARGV*', []);\n\n// core.mal: defined using the language itself\nrep(\"(def! *host-language* \\\"javascript\\\")\")\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif (typeof process !== 'undefined' && process.argv.length > 2) {\n    repl_env.set('*ARGV*', process.argv.slice(3));\n    rep('(load-file \"' + process.argv[2] + '\")');\n    process.exit(0);\n}\n\n// repl loop\nif (typeof require !== 'undefined' && require.main === module) {\n    // Synchronous node.js commandline mode\n    rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\n    while (true) {\n        var line = readline.readline(\"user> \");\n        if (line === null) { break; }\n        try {\n            if (line) { printer.println(rep(line)); }\n        } catch (exc) {\n            if (exc instanceof reader.BlankException) { continue }\n            if (exc instanceof Error) { console.warn(exc.stack) }\n            else { console.warn(\"Error: \" + printer._pr_str(exc, true)) }\n        }\n    }\n}\n"
  },
  {
    "path": "impls/js/tests/common.js",
    "content": "fs = require('fs');\nassert = require('assert');\n\nfunction assert_eq(a, b) {\n    GLOBAL.assert.deepEqual(a, b, a + \" !== \" + b);\n}\n\nfunction load(file) {\n    console.log(process.cwd());\n    //process.chdir('../');\n    eval(fs.readFileSync(file,'utf8'));\n}\n\nexports.assert_eq = assert_eq;\nexports.load = load;\n"
  },
  {
    "path": "impls/js/tests/reader.js",
    "content": "common = require('./common.js');\ntypes = require('../types');\nreader = require('../reader');\ncore = require('../core');\nvar assert_eq = common.assert_eq,\n    read_str = reader.read_str,\n    nth = core.ns.nth;\n\nconsole.log(\"Testing read of constants/strings\");\nassert_eq(2,read_str('2'));\nassert_eq(12345,read_str('12345'));\nassert_eq(12345,read_str('12345 \"abc\"'));\nassert_eq('abc',read_str('\"abc\"'));\nassert_eq('a string (with parens)',read_str('\"a string (with parens)\"'));\n\nconsole.log(\"Testing read of symbols\");\nassert(types._symbol_Q(read_str('abc')));\nassert_eq('abc',read_str('abc').value);\nassert_eq('.',read_str('.').value);\n\nconsole.log(\"Testing READ_STR of strings\");\nassert_eq('a string',read_str('\"a string\"'));\nassert_eq('a string (with parens)',read_str('\"a string (with parens)\"'));\nassert_eq('a string',read_str('\"a string\"()'));\nassert_eq('a string',read_str('\"a string\"123'));\nassert_eq('a string',read_str('\"a string\"abc'));\nassert_eq('',read_str('\"\"'));\nassert_eq('abc ',read_str('\"abc \"'));\nassert_eq(' abc',read_str('\" abc\"'));\nassert_eq('$abc',read_str('\"$abc\"'));\nassert_eq('abc$()',read_str('\"abc$()\"'));\nassert_eq('\"xyz\"',read_str('\"\\\\\"xyz\\\\\"\"'));\n\n\nconsole.log(\"Testing READ_STR of lists\");\nassert_eq(2,core.ns.count(read_str('(2 3)')));\nassert_eq(2,core.ns.first(read_str('(2 3)')));\nassert_eq(3,core.ns.first(core.ns.rest(read_str('(2 3)'))));\nL = read_str('(+ 1 2 \"str1\" \"string (with parens) and \\'single quotes\\'\")');\nassert_eq(5,core.ns.count(L));\nassert_eq('str1',nth(L,3));\nassert_eq('string (with parens) and \\'single quotes\\'',nth(L,4));\nassert_eq([2,3],read_str('(2 3)'));\nassert_eq([2,3, 'string (with parens)'],read_str('(2 3 \"string (with parens)\")'));\n\n\nconsole.log(\"Testing READ_STR of quote/quasiquote\");\nassert_eq('quote',nth(read_str('\\'1'),0).value);\nassert_eq(1,nth(read_str('\\'1'),1));\nassert_eq('quote',nth(read_str('\\'(1 2 3)'),0).value);\nassert_eq(3,nth(nth(read_str('\\'(1 2 3)'),1),2));\n\nassert_eq('quasiquote',nth(read_str('`1'),0).value);\nassert_eq(1,nth(read_str('`1'),1));\nassert_eq('quasiquote',nth(read_str('`(1 2 3)'),0).value);\nassert_eq(3,nth(nth(read_str('`(1 2 3)'),1),2));\n\nassert_eq('unquote',nth(read_str('~1'),0).value);\nassert_eq(1,nth(read_str('~1'),1));\nassert_eq('unquote',nth(read_str('~(1 2 3)'),0).value);\nassert_eq(3,nth(nth(read_str('~(1 2 3)'),1),2));\n\nassert_eq('splice-unquote',nth(read_str('~@1'),0).value);\nassert_eq(1,nth(read_str('~@1'),1));\nassert_eq('splice-unquote',nth(read_str('~@(1 2 3)'),0).value);\nassert_eq(3,nth(nth(read_str('~@(1 2 3)'),1),2));\n\n\nconsole.log(\"All tests completed\");\n"
  },
  {
    "path": "impls/js/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/js/tests/stepA_mal.mal",
    "content": ";; Testing basic bash interop\n\n(js-eval \"7\")\n;=>7\n\n(js-eval \"'7'\")\n;=>\"7\"\n\n(js-eval \"[7,8,9]\")\n;=>(7 8 9)\n\n(js-eval \"console.log('hello');\")\n;/hello\n;=>nil\n\n(js-eval \"foo=8;\")\n(js-eval \"foo;\")\n;=>8\n\n(js-eval \"['a','b','c'].map(function(x){return 'X'+x+'Y'}).join(' ')\")\n;=>\"XaY XbY XcY\"\n\n(js-eval \"[1,2,3].map(function(x){return 1+x})\")\n;=>(2 3 4)\n\n(js-eval (str \"3 * \" (* 4 5)))\n;=>60\n\n(. \"console.log\" \"abc\" 123 '(4 5 6) {\"kk\" \"vv\"} (= 1 1) nil)\n;/abc 123 \\[ 4, 5, 6 \\] \\{ kk: 'vv' \\} true null\n;=>nil\n\n(js-eval \"myobj = { v: 10, myfunc: function(a,b,c) { return a * b * c * this.v; } }\")\n(. \"myobj.myfunc\" 2 3 4)\n;=>240\n\n(js-eval \"myarray = [1,2,3,4,5]\")\n(. \"myarray.join\" \"#\")\n;=>\"1#2#3#4#5\"\n"
  },
  {
    "path": "impls/js/tests/types.js",
    "content": "common = require('./common.js');\nvar assert_eq = common.assert_eq;\nvar types = require('../types.js');\nvar core = require('../core.js');\nvar env = require('../env.js');\nvar symbol = types._symbol,\n    hash_map = core.ns['hash-map'],\n    hash_map_Q = core.ns['map?'],\n    assoc = core.ns['assoc'],\n    dissoc = core.ns['dissoc'],\n    get = core.ns['get'],\n    contains_Q = core.ns['contains?'],\n    count = core.ns['count'],\n    equal_Q = core.ns['='];\n\n\nconsole.log(\"Testing hash_maps\");\nX = hash_map();\nassert_eq(true, hash_map_Q(X));\n\nassert_eq(null, get(X,'a'));\nassert_eq(false, contains_Q(X, 'a'));\nX1 = assoc(X, 'a', \"value of X a\");\nassert_eq(null, get(X,'a'));\nassert_eq(false, contains_Q(X, 'a'));\nassert_eq(\"value of X a\", get(X1, 'a'));\nassert_eq(true, contains_Q(X1, 'a'));\n\nY = hash_map();\nassert_eq(0, count(Y));\nY1 = assoc(Y, 'a', \"value of Y a\");\nassert_eq(1, count(Y1));\nY2 = assoc(Y1, 'b', \"value of Y b\");\nassert_eq(2, count(Y2));\nassert_eq(\"value of Y a\", get(Y2, 'a'));\nassert_eq(\"value of Y b\", get(Y2, 'b'));\n\nX2 = assoc(X1, 'b', Y2);\nassert_eq(2, count(Y2));\n\nassert_eq(true, hash_map_Q(get(X2,'b')));\n\nassert_eq('value of Y a', get(get(X2,'b'),'a'));\nassert_eq('value of Y b', get(get(X2,'b'),'b'));\n\nY3 = dissoc(Y2, 'a');\nassert_eq(2, count(Y2));\nassert_eq(1, count(Y3));\nassert_eq(null, get(Y3, 'a'));\nY4 = dissoc(Y3, 'b');\nassert_eq(0, count(Y4));\nassert_eq(null, get(Y4, 'b'));\n\n\nconsole.log(\"Testing equal? function\");\nassert_eq(true,  equal_Q(2,2));\nassert_eq(false, equal_Q(2,3));\nassert_eq(false, equal_Q(2,3));\nassert_eq(true,  equal_Q(\"abc\",\"abc\"));\nassert_eq(false, equal_Q(\"abc\",\"abz\"));\nassert_eq(false, equal_Q(\"zbc\",\"abc\"));\nassert_eq(true,  equal_Q(symbol(\"abc\"),symbol(\"abc\")));\nassert_eq(false, equal_Q(symbol(\"abc\"),symbol(\"abz\")));\nassert_eq(false, equal_Q(symbol(\"zbc\"),symbol(\"abc\")));\nL6 = [1, 2, 3];\nL7 = [1, 2, 3];\nL8 = [1, 2, \"Z\"];\nL9 = [\"Z\", 2, 3];\nL10 = [1, 2];\nassert_eq(true,  equal_Q(L6, L7));\nassert_eq(false, equal_Q(L6, L8));\nassert_eq(false, equal_Q(L6, L9));\nassert_eq(false, equal_Q(L6, L10));\nassert_eq(false, equal_Q(L10, L6));\n\n\nconsole.log(\"Testing ENV (1 level)\")\nenv1 = new env.Env();\nassert_eq('val_a',env1.set('a','val_a'));\nassert_eq('val_b',env1.set('b','val_b'));\nassert_eq('val_eq',env1.set('=','val_eq'));\nassert_eq('val_a',env1.get('a'));\nassert_eq('val_b',env1.get('b'));\nassert_eq('val_eq',env1.get('='));\n\nconsole.log(\"Testing ENV (2 levels)\");\nenv2 = new env.Env(env1);\nassert_eq('val_b2',env2.set('b','val_b2'));\nassert_eq('val_c',env2.set('c','val_c'));\nassert_eq(env1,env2.find('a'));\nassert_eq(env2,env2.find('b'));\nassert_eq(env2,env2.find('c'));\nassert_eq('val_a', env2.get('a'));\nassert_eq('val_b2',env2.get('b'));\nassert_eq('val_c', env2.get('c'));\n\n"
  },
  {
    "path": "impls/js/types.js",
    "content": "// Node vs browser behavior\nvar types = {};\nif (typeof module === 'undefined') {\n    var exports = types;\n}\n\n// General functions\n\nfunction _obj_type(obj) {\n    if      (_symbol_Q(obj)) {   return 'symbol'; }\n    else if (_list_Q(obj)) {     return 'list'; }\n    else if (_vector_Q(obj)) {   return 'vector'; }\n    else if (_hash_map_Q(obj)) { return 'hash-map'; }\n    else if (_nil_Q(obj)) {      return 'nil'; }\n    else if (_true_Q(obj)) {     return 'true'; }\n    else if (_false_Q(obj)) {    return 'false'; }\n    else if (_atom_Q(obj)) {     return 'atom'; }\n    else {\n        switch (typeof(obj)) {\n        case 'number':   return 'number';\n        case 'function': return 'function';\n        case 'string': return obj[0] == '\\u029e' ? 'keyword' : 'string';\n        default: throw new Error(\"Unknown type '\" + typeof(obj) + \"'\");\n        }\n    }\n}\n\nfunction _sequential_Q(lst) { return _list_Q(lst) || _vector_Q(lst); }\n\n\nfunction _equal_Q (a, b) {\n    var ota = _obj_type(a), otb = _obj_type(b);\n    if (!(ota === otb || (_sequential_Q(a) && _sequential_Q(b)))) {\n        return false;\n    }\n    switch (ota) {\n    case 'symbol': return a.value === b.value;\n    case 'list':\n    case 'vector':\n        if (a.length !== b.length) { return false; }\n        for (var i=0; i<a.length; i++) {\n            if (! _equal_Q(a[i], b[i])) { return false; }\n        }\n        return true;\n    case 'hash-map':\n        if (Object.keys(a).length !== Object.keys(b).length) { return false; }\n        for (var k in a) {\n            if (! _equal_Q(a[k], b[k])) { return false; }\n        }\n        return true;\n    default:\n        return a === b;\n    }\n}\n\n\nfunction _clone (obj) {\n    var new_obj;\n    switch (_obj_type(obj)) {\n    case 'list':\n        new_obj = obj.slice(0);\n        break;\n    case 'vector':\n        new_obj = obj.slice(0);\n        new_obj.__isvector__ = true;\n        break;\n    case 'hash-map':\n        new_obj = {};\n        for (var k in obj) {\n            if (obj.hasOwnProperty(k)) { new_obj[k] = obj[k]; }\n        }\n        break;\n    case 'function':\n        new_obj = obj.clone();\n        break;\n    default:\n        throw new Error(\"clone of non-collection: \" + _obj_type(obj));\n    }\n    Object.defineProperty(new_obj, \"__meta__\", {\n        enumerable: false,\n        writable: true\n    });\n    return new_obj;\n}\n\n\n// Scalars\nfunction _nil_Q(a) { return a === null ? true : false; }\nfunction _true_Q(a) { return a === true ? true : false; }\nfunction _false_Q(a) { return a === false ? true : false; }\nfunction _number_Q(obj) { return typeof obj === 'number'; }\nfunction _string_Q(obj) {\n    return typeof obj === 'string' && obj[0] !== '\\u029e';\n}\n\n\n// Symbols\nfunction Symbol(name) {\n    this.value = name;\n    return this;\n}\nSymbol.prototype.toString = function() { return this.value; }\nfunction _symbol(name) { return new Symbol(name); }\nfunction _symbol_Q(obj) { return obj instanceof Symbol; }\n\n\n// Keywords\nfunction _keyword(obj) {\n    if (typeof obj === 'string' && obj[0] === '\\u029e') {\n        return obj;\n    } else {\n        return \"\\u029e\" + obj;\n    }\n}\nfunction _keyword_Q(obj) {\n    return typeof obj === 'string' && obj[0] === '\\u029e';\n}\n\n\n// Functions\nfunction _function(Eval, Env, ast, env, params) {\n    var fn = function() {\n        return Eval(ast, new Env(env, params, arguments));\n    };\n    fn.__meta__ = null;\n    fn.__ast__ = ast;\n    fn.__gen_env__ = function(args) { return new Env(env, params, args); };\n    fn._ismacro_ = false;\n    return fn;\n}\nfunction _function_Q(obj) { return typeof obj == \"function\"; }\nFunction.prototype.clone = function() {\n    var that = this;\n    var temp = function () { return that.apply(this, arguments); };\n    for( key in this ) {\n        temp[key] = this[key];\n    }\n    return temp;\n};\nfunction _fn_Q(obj) { return _function_Q(obj) && !obj._ismacro_; }\nfunction _macro_Q(obj) { return _function_Q(obj) && !!obj._ismacro_; }\n\n\n// Lists\nfunction _list() { return Array.prototype.slice.call(arguments, 0); }\nfunction _list_Q(obj) { return Array.isArray(obj) && !obj.__isvector__; }\n\n\n// Vectors\nfunction _vector() {\n    var v = Array.prototype.slice.call(arguments, 0);\n    v.__isvector__ = true;\n    return v;\n}\nfunction _vector_Q(obj) { return Array.isArray(obj) && !!obj.__isvector__; }\n\n\n\n// Hash Maps\nfunction _hash_map() {\n    if (arguments.length % 2 === 1) {\n        throw new Error(\"Odd number of hash map arguments\");\n    }\n    var args = [{}].concat(Array.prototype.slice.call(arguments, 0));\n    return _assoc_BANG.apply(null, args);\n}\nfunction _hash_map_Q(hm) {\n    return typeof hm === \"object\" &&\n           !Array.isArray(hm) &&\n           !(hm === null) &&\n           !(hm instanceof Symbol) &&\n           !(hm instanceof Atom);\n}\nfunction _assoc_BANG(hm) {\n    if (arguments.length % 2 !== 1) {\n        throw new Error(\"Odd number of assoc arguments\");\n    }\n    for (var i=1; i<arguments.length; i+=2) {\n        var ktoken = arguments[i],\n            vtoken = arguments[i+1];\n        if (typeof ktoken !== \"string\") {\n            throw new Error(\"expected hash-map key string, got: \" + (typeof ktoken));\n        }\n        hm[ktoken] = vtoken;\n    }\n    return hm;\n}\nfunction _dissoc_BANG(hm) {\n    for (var i=1; i<arguments.length; i++) {\n        var ktoken = arguments[i];\n        delete hm[ktoken];\n    }\n    return hm;\n}\n\n\n// Atoms\nfunction Atom(val) { this.val = val; }\nfunction _atom(val) { return new Atom(val); }\nfunction _atom_Q(atm) { return atm instanceof Atom; }\n\n\n// Exports\nexports._obj_type = types._obj_type = _obj_type;\nexports._sequential_Q = types._sequential_Q = _sequential_Q;\nexports._equal_Q = types._equal_Q = _equal_Q;\nexports._clone = types._clone = _clone;\nexports._nil_Q = types._nil_Q = _nil_Q;\nexports._true_Q = types._true_Q = _true_Q;\nexports._false_Q = types._false_Q = _false_Q;\nexports._number_Q = types._number_Q = _number_Q;\nexports._string_Q = types._string_Q = _string_Q;\nexports._symbol = types._symbol = _symbol;\nexports._symbol_Q = types._symbol_Q = _symbol_Q;\nexports._keyword = types._keyword = _keyword;\nexports._keyword_Q = types._keyword_Q = _keyword_Q;\nexports._function = types._function = _function;\nexports._function_Q = types._function_Q = _function_Q;\nexports._fn_Q = types._fn_Q = _fn_Q;\nexports._macro_Q = types._macro_Q = _macro_Q;\nexports._list = types._list = _list;\nexports._list_Q = types._list_Q = _list_Q;\nexports._vector = types._vector = _vector;\nexports._vector_Q = types._vector_Q = _vector_Q;\nexports._hash_map = types._hash_map = _hash_map;\nexports._hash_map_Q = types._hash_map_Q = _hash_map_Q;\nexports._assoc_BANG = types._assoc_BANG = _assoc_BANG;\nexports._dissoc_BANG = types._dissoc_BANG = _dissoc_BANG;\nexports._atom = types._atom = _atom;\nexports._atom_Q = types._atom_Q = _atom_Q;\n"
  },
  {
    "path": "impls/julia/Dockerfile",
    "content": "FROM ubuntu:vivid\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Julia\nRUN apt-get -y install software-properties-common\nRUN apt-add-repository -y ppa:staticfloat/juliareleases\nRUN apt-get update -y\nRUN apt-get -y install julia\n\n"
  },
  {
    "path": "impls/julia/Makefile",
    "content": "all:\n\nclean:\n\n"
  },
  {
    "path": "impls/julia/core.jl",
    "content": "module core\n\nimport types\nimport reader\nusing printer\nimport readline_mod\n\nexport ns\n\nfunction string_Q(obj)\n    isa(obj,AbstractString) && (length(obj) == 0 || obj[1] != '\\u029e')\nend\n\nfunction keyword_Q(obj)\n    isa(obj,AbstractString) && (length(obj) > 0 && obj[1] == '\\u029e')\nend\n\nfunction concat(args...)\n    res = []\n    for a=args\n        res = [res; Any[a...]]\n    end\n    res\nend\n\nfunction do_apply(f, all_args...)\n    fn = isa(f,types.MalFunc) ? f.fn : f\n    args = concat(all_args[1:end-1], all_args[end])\n    fn(args...)\nend\n\nfunction do_map(a,b)\n    # map and convert to array/list\n    if isa(a,types.MalFunc)\n        collect(map(a.fn,b))\n    else\n        collect(map(a,b))\n    end\nend\n\nfunction conj(seq, args...)\n    if isa(seq,Array)\n        concat(reverse(args), seq)\n    else\n        tuple(concat(seq, args)...)\n    end\nend\n\nfunction do_seq(obj)\n    if isa(obj,Array)\n        length(obj) > 0 ? obj : nothing\n    elseif isa(obj,Tuple)\n        length(obj) > 0 ? Any[obj...] : nothing\n    elseif isa(obj,AbstractString)\n        length(obj) > 0 ? [string(c) for c=obj] : nothing\n    elseif obj == nothing\n        nothing\n    else\n        error(\"seq: called on non-sequence\")\n    end\nend\n\n\nfunction with_meta(obj, meta)\n    new_obj = types.copy(obj)\n    new_obj.meta = meta\n    new_obj\nend\n\nns = Dict{Any,Any}(\n    symbol(\"=\") => (a,b) -> types.equal_Q(a, b),\n    :throw => (a) -> throw(types.MalException(a)),\n\n    symbol(\"nil?\") => (a) -> a === nothing,\n    symbol(\"true?\") => (a) -> a === true,\n    symbol(\"false?\") => (a) -> a === false,\n    symbol(\"string?\") => string_Q,\n    symbol(\"symbol\") => (a) -> symbol(a),\n    symbol(\"symbol?\") => (a) -> typeof(a) === Symbol,\n    symbol(\"keyword\") => (a) -> a[1] == '\\u029e' ? a : \"\\u029e$(a)\",\n    symbol(\"keyword?\") => keyword_Q,\n    symbol(\"number?\") => (a) -> isa(a, AbstractFloat) || isa(a, Int64),\n    symbol(\"fn?\") => (a) -> isa(a, Function) || (isa(a, types.MalFunc) && !a.ismacro),\n    symbol(\"macro?\") => (a) -> isa(a, types.MalFunc) && a.ismacro,\n\n    symbol(\"pr-str\") => (a...) -> join(map((e)->pr_str(e, true),a),\" \"),\n    :str => (a...) -> join(map((e)->pr_str(e, false),a),\"\"),\n    :prn => (a...) -> println(join(map((e)->pr_str(e, true),a),\" \")),\n    :println => (a...) -> println(join(map((e)->pr_str(e, false),a),\" \")),\n    symbol(\"read-string\") => (a) -> reader.read_str(a),\n    :readline => readline_mod.do_readline,\n    :slurp => (a) -> readall(open(a)),\n\n    :< => <,\n    :<= => <=,\n    :> => >,\n    :>= => >=,\n    :+ => +,\n    :- => -,\n    symbol(\"*\") => *,\n    :/ => div,\n    symbol(\"time-ms\") => () -> round(Int, time()*1000),\n\n    :list => (a...) -> Any[a...],\n    symbol(\"list?\") => (a) -> isa(a, Array),\n    :vector => (a...) -> tuple(a...),\n    symbol(\"vector?\") => (a) -> isa(a, Tuple),\n    symbol(\"hash-map\") => types.hash_map,\n    symbol(\"map?\") => (a) -> isa(a, Dict),\n    :assoc => (a, b...) -> merge(a, types.hash_map(b...)),\n    :dissoc => (a, b...) -> foldl((x,y) -> delete!(x,y),copy(a), b),\n    :get => (a,b) -> a === nothing ? nothing : get(a,b,nothing),\n    symbol(\"contains?\") => haskey,\n    :keys => (a) -> [keys(a)...],\n    :vals => (a) -> [values(a)...],\n\n    symbol(\"sequential?\") => types.sequential_Q,\n    :cons => (a,b) -> [Any[a]; Any[b...]],\n    :concat => concat,\n    :vec => (a) -> tuple(a...),\n    :nth => (a,b) -> b+1 > length(a) ? error(\"nth: index out of range\") : a[b+1],\n    :first => (a) -> a === nothing || isempty(a) ? nothing : first(a),\n    :rest => (a) -> a === nothing ? Any[] : Any[a[2:end]...],\n    symbol(\"empty?\") => isempty,\n    :count => (a) -> a == nothing ? 0 : length(a),\n    :apply => do_apply,\n    :map => do_map,\n\n    :conj => conj,\n    :seq => do_seq,\n\n    :meta => (a) -> isa(a,types.MalFunc) ? a.meta : nothing,\n    symbol(\"with-meta\") => with_meta,\n    :atom => (a) -> types.Atom(a),\n    symbol(\"atom?\") => (a) -> isa(a,types.Atom),\n    :deref => (a) -> a.val,\n    :reset! => (a,b) -> a.val = b,\n    :swap! => (a,b,c...) -> a.val = do_apply(b, a.val, c),\n    )\n\nend\n"
  },
  {
    "path": "impls/julia/env.jl",
    "content": "module env\n\nexport Env, env_set, env_find, env_get\n\ntype Env\n    outer::Any\n    data::Dict{Symbol,Any}\nend\n\nfunction Env()\n    Env(nothing, Dict())\nend\n\nfunction Env(outer)\n    Env(outer, Dict())\nend\n\nfunction Env(outer, binds, exprs)\n    e = Env(outer, Dict())\n    for i=1:length(binds)\n        if binds[i] == :&\n            e.data[binds[i+1]] = exprs[i:end]\n            break\n        else\n            e.data[binds[i]] = exprs[i]\n        end\n    end\n    e\nend\n\n\nfunction env_set(env::Env, k::Symbol, v)\n    env.data[k] = v\nend\n\nfunction env_find(env::Env, k::Symbol)\n    if haskey(env.data, k)\n        env\n    elseif env.outer != nothing\n        env_find(env.outer, k)\n    else\n        nothing\n    end\nend\n\nfunction env_get(env::Env, k::Symbol)\n    e = env_find(env, k)\n    if e != nothing\n        e.data[k]\n    else\n        error(\"'$(string(k))' not found\")\n    end\nend\n\nend\n"
  },
  {
    "path": "impls/julia/printer.jl",
    "content": "module printer\n\nimport types\n\nexport pr_str\n\nfunction pr_str(obj, print_readably=true)\n    _r = print_readably\n    if isa(obj, Array)\n        \"($(join([pr_str(o, _r) for o=obj], \" \")))\"\n    elseif isa(obj, Tuple)\n        \"[$(join([pr_str(o, _r) for o=obj], \" \"))]\"\n    elseif isa(obj, Dict)\n        \"{$(join([\"$(pr_str(o[1],_r)) $(pr_str(o[2],_r))\" for o=obj], \" \"))}\"\n    elseif isa(obj, AbstractString)\n        if length(obj) > 0 && obj[1] == '\\u029e'\n            \":$(obj[3:end])\"\n        elseif _r\n            str = replace(replace(replace(obj,\n                                          \"\\\\\", \"\\\\\\\\\"),\n                                 \"\\\"\", \"\\\\\\\"\"),\n                          \"\\n\", \"\\\\n\")\n            \"\\\"$(str)\\\"\"\n        else\n            obj\n        end\n    elseif obj == nothing\n        \"nil\"\n    elseif typeof(obj) == types.MalFunc\n        \"(fn* $(pr_str(obj.params,true)) $(pr_str(obj.ast,true)))\"\n    elseif typeof(obj) == types.Atom\n        \"(atom $(pr_str(obj.val,true)))\"\n    elseif typeof(obj) == Function\n        \"#<native function: $(string(obj))>\"\n    else\n        string(obj)\n    end\nend\n\nend\n"
  },
  {
    "path": "impls/julia/reader.jl",
    "content": "module reader\n\nexport read_str\n\nimport types\n\ntype Reader\n    tokens\n    position::Int64\nend\n\nfunction next(rdr::Reader)\n    if rdr.position > length(rdr.tokens)\n        return nothing\n    end\n    rdr.position += 1\n    rdr.tokens[rdr.position-1]\nend\n\nfunction peek(rdr::Reader)\n    if rdr.position > length(rdr.tokens)\n        return nothing\n    end\n    rdr.tokens[rdr.position]\nend\n\n\nfunction tokenize(str)\n    re = r\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\\\"(?:\\\\.|[^\\\\\\\"])*\\\"?|;.*|[^\\s\\[\\]{}('\\\"`,;)]*)\"\n    tokens = map((m) -> m.captures[1], eachmatch(re, str))\n    filter((t) -> t != \"\" && t[1] != ';', tokens)\nend\n\nfunction read_atom(rdr)\n    token = next(rdr)\n    if ismatch(r\"^-?[0-9]+$\", token)\n        parse(Int,token)\n    elseif ismatch(r\"^-?[0-9][0-9.]*$\", token)\n        float(token)\n    elseif ismatch(r\"^\\\"(?:\\\\.|[^\\\\\\\"])*\\\"$\", token)\n        replace(token[2:end-1], r\"\\\\.\", (r) -> get(Dict(\"\\\\n\"=>\"\\n\",\n                                                        \"\\\\\\\"\"=>\"\\\"\",\n                                                        \"\\\\\\\\\"=>\"\\\\\"), r, r))\n    elseif ismatch(r\"^\\\".*$\", token)\n        error(\"expected '\\\"', got EOF\")\n    elseif token[1] == ':'\n        \"\\u029e$(token[2:end])\"\n    elseif token == \"nil\"\n        nothing\n    elseif token == \"true\"\n        true\n    elseif token == \"false\"\n        false\n    else\n        symbol(token)\n    end\nend\n\nfunction read_list(rdr, start=\"(\", last=\")\")\n    ast = Any[]\n    token = next(rdr)\n    if (token != start)\n        error(\"expected '$(start)'\")\n    end\n    while ((token = peek(rdr)) != last)\n        if token == nothing\n            error(\"expected '$(last)', got EOF\")\n        end\n        push!(ast, read_form(rdr))\n    end\n    next(rdr)\n    ast\nend\n\nfunction read_vector(rdr)\n    lst = read_list(rdr, \"[\", \"]\")\n    tuple(lst...)\nend\n\nfunction read_hash_map(rdr)\n    lst = read_list(rdr, \"{\", \"}\")\n    types.hash_map(lst...)\nend\n\nfunction read_form(rdr)\n    token = peek(rdr)\n    if token == \"'\"\n        next(rdr)\n        [[:quote]; Any[read_form(rdr)]]\n    elseif token == \"`\"\n        next(rdr)\n        [[:quasiquote]; Any[read_form(rdr)]]\n    elseif token == \"~\"\n        next(rdr)\n        [[:unquote]; Any[read_form(rdr)]]\n    elseif token == \"~@\"\n        next(rdr)\n        [[symbol(\"splice-unquote\")]; Any[read_form(rdr)]]\n    elseif token == \"^\"\n        next(rdr)\n        meta = read_form(rdr)\n        [[symbol(\"with-meta\")]; Any[read_form(rdr)]; Any[meta]]\n    elseif token == \"@\"\n        next(rdr)\n        [[symbol(\"deref\")]; Any[read_form(rdr)]]\n\n    elseif token == \")\"\n        error(\"unexpected ')'\")\n    elseif token == \"(\"\n        read_list(rdr)\n    elseif token == \"]\"\n        error(\"unexpected ']'\")\n    elseif token == \"[\"\n        read_vector(rdr)\n    elseif token == \"}\"\n        error(\"unexpected '}'\")\n    elseif token == \"{\"\n        read_hash_map(rdr)\n    else\n        read_atom(rdr)\n    end\nend\n\nfunction read_str(str)\n    tokens = tokenize(str)\n    if length(tokens) == 0\n        return nothing\n    end\n    read_form(Reader(tokens, 1))\nend\n\nend\n"
  },
  {
    "path": "impls/julia/readline_mod.jl",
    "content": "module readline_mod\n\nexport do_readline\n\nfunction do_readline(prompt)\n    print(prompt)\n    flush(STDOUT)\n    line = readline(STDIN)\n    if line == \"\"\n        return nothing\n    end\n    chomp(line)\nend\n\nend\n"
  },
  {
    "path": "impls/julia/run",
    "content": "#!/usr/bin/env bash\nexec julia $(dirname $0)/${STEP:-stepA_mal}.jl \"${@}\"\n"
  },
  {
    "path": "impls/julia/step0_repl.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\n\n# READ\nfunction READ(str)\n    str\nend\n\n# EVAL\nfunction EVAL(ast, env)\n    ast\nend\n\n# PRINT\nfunction PRINT(exp)\n    exp\nend\n\n# REPL\nfunction REP(str)\n    return PRINT(EVAL(READ(str), []))\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    println(REP(line))\nend\n"
  },
  {
    "path": "impls/julia/step1_read_print.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction EVAL(ast, env)\n    ast\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nfunction REP(str)\n    return PRINT(EVAL(READ(str), []))\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        bt = catch_backtrace()\n        Base.show_backtrace(STDERR, bt)\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step2_eval.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction EVAL(ast, env)\n    # println(\"EVAL: $(printer.pr_str(ast,true))\")\n\n    if typeof(ast) == Symbol\n        return env[ast]\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    if isempty(ast) return ast end\n\n    # apply\n    el = map((x) -> EVAL(x,env), ast)\n    f, args = el[1], el[2:end]\n    f(args...)\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = Dict{Any,Any}(:+ => +,\n                         :- => -,\n                         :* => *,\n                         :/ => div)\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        bt = catch_backtrace()\n        Base.show_backtrace(STDERR, bt)\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step3_env.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction EVAL(ast, env)\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    if isempty(ast) return ast end\n\n    # apply\n    if     :def! == ast[1]\n        env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        EVAL(ast[3], let_env)\n    else\n        el = map((x) -> EVAL(x,env), ast)\n        f, args = el[1], el[2:end]\n        f(args...)\n    end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = Env(nothing,\n               Dict{Any,Any}(:+ => +,\n                             :- => -,\n                             :* => *,\n                             :/ => div))\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        bt = catch_backtrace()\n        Base.show_backtrace(STDERR, bt)\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step4_if_fn_do.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction EVAL(ast, env)\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    if isempty(ast) return ast end\n\n    # apply\n    if     :def! == ast[1]\n        env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        EVAL(ast[3], let_env)\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end])[end]\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                EVAL(ast[4], env)\n            else\n                nothing\n            end\n        else\n            EVAL(ast[3], env)\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...]))\n    else\n        el = map((x) -> EVAL(x,env), ast)\n        f, args = el[1], el[2:end]\n        f(args...)\n    end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step5_tco.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\nusing types\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction EVAL(ast, env)\n  while true\n\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    if isempty(ast) return ast end\n\n    # apply\n    if     :def! == ast[1]\n        return env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        env = let_env\n        ast = ast[3]\n        # TCO loop\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end-1])\n        ast = ast[end]\n        # TCO loop\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                ast = ast[4]\n                # TCO loop\n            else\n                return nothing\n            end\n        else\n            ast = ast[3]\n            # TCO loop\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        return MalFunc(\n            (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])),\n            ast[3], env, ast[2])\n    else\n        el = map((x) -> EVAL(x,env), ast)\n        f, args = el[1], el[2:end]\n        if isa(f, MalFunc)\n            ast = f.ast\n            env = Env(f.env, f.params, args)\n            # TCO loop\n        else\n            return f(args...)\n        end\n    end\n  end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        # TODO: show at least part of stack\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step6_file.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\nusing types\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction EVAL(ast, env)\n  while true\n\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    if isempty(ast) return ast end\n\n    # apply\n    if     :def! == ast[1]\n        return env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        env = let_env\n        ast = ast[3]\n        # TCO loop\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end-1])\n        ast = ast[end]\n        # TCO loop\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                ast = ast[4]\n                # TCO loop\n            else\n                return nothing\n            end\n        else\n            ast = ast[3]\n            # TCO loop\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        return MalFunc(\n            (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])),\n            ast[3], env, ast[2])\n    else\n        el = map((x) -> EVAL(x,env), ast)\n        f, args = el[1], el[2:end]\n        if isa(f, MalFunc)\n            ast = f.ast\n            env = Env(f.env, f.params, args)\n            # TCO loop\n        else\n            return f(args...)\n        end\n    end\n  end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\nenv_set(repl_env, :eval, (ast) -> EVAL(ast, repl_env))\nenv_set(repl_env, symbol(\"*ARGV*\"), ARGS[2:end])\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif length(ARGS) > 0\n    REP(\"(load-file \\\"$(ARGS[1])\\\")\")\n    exit(0)\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        # TODO: show at least part of stack\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step7_quote.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\nusing types\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction quasiquote_loop(elts)\n    acc = Any[]\n    for i in length(elts):-1:1\n        elt = elts[i]\n        if isa(elt, Array) && length(elt) == 2 && elt[1] == symbol(\"splice-unquote\")\n            acc = Any[:concat, elt[2], acc]\n        else\n            acc = Any[:cons, quasiquote(elt), acc]\n        end\n    end\n    return acc\nend\n\nfunction quasiquote(ast)\n    if isa(ast, Array)\n        if length(ast) == 2 && ast[1] == symbol(\"unquote\")\n            ast[2]\n        else\n            quasiquote_loop(ast)\n        end\n    elseif isa(ast, Tuple)\n        Any[:vec, quasiquote_loop(ast)]\n    elseif typeof(ast) == Symbol || isa(ast, Dict)\n        Any[:quote, ast]\n    else\n        ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true\n\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    if isempty(ast) return ast end\n\n    # apply\n    if     :def! == ast[1]\n        return env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        env = let_env\n        ast = ast[3]\n        # TCO loop\n    elseif :quote == ast[1]\n        return ast[2]\n    elseif :quasiquote == ast[1]\n        ast = quasiquote(ast[2])\n        # TCO loop\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end-1])\n        ast = ast[end]\n        # TCO loop\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                ast = ast[4]\n                # TCO loop\n            else\n                return nothing\n            end\n        else\n            ast = ast[3]\n            # TCO loop\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        return MalFunc(\n            (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])),\n            ast[3], env, ast[2])\n    else\n        el = map((x) -> EVAL(x,env), ast)\n        f, args = el[1], el[2:end]\n        if isa(f, MalFunc)\n            ast = f.ast\n            env = Env(f.env, f.params, args)\n            # TCO loop\n        else\n            return f(args...)\n        end\n    end\n  end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\nenv_set(repl_env, :eval, (ast) -> EVAL(ast, repl_env))\nenv_set(repl_env, symbol(\"*ARGV*\"), ARGS[2:end])\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif length(ARGS) > 0\n    REP(\"(load-file \\\"$(ARGS[1])\\\")\")\n    exit(0)\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        # TODO: show at least part of stack\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step8_macros.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\nusing types\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction quasiquote_loop(elts)\n    acc = Any[]\n    for i in length(elts):-1:1\n        elt = elts[i]\n        if isa(elt, Array) && length(elt) == 2 && elt[1] == symbol(\"splice-unquote\")\n            acc = Any[:concat, elt[2], acc]\n        else\n            acc = Any[:cons, quasiquote(elt), acc]\n        end\n    end\n    return acc\nend\n\nfunction quasiquote(ast)\n    if isa(ast, Array)\n        if length(ast) == 2 && ast[1] == symbol(\"unquote\")\n            ast[2]\n        else\n            quasiquote_loop(ast)\n        end\n    elseif isa(ast, Tuple)\n        Any[:vec, quasiquote_loop(ast)]\n    elseif typeof(ast) == Symbol || isa(ast, Dict)\n        Any[:quote, ast]\n    else\n        ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true\n\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    # apply\n    if isempty(ast) return ast end\n\n    if     :def! == ast[1]\n        return env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        env = let_env\n        ast = ast[3]\n        # TCO loop\n    elseif :quote == ast[1]\n        return ast[2]\n    elseif :quasiquote == ast[1]\n        ast = quasiquote(ast[2])\n        # TCO loop\n    elseif :defmacro! == ast[1]\n        func = EVAL(ast[3], env)\n        func.ismacro = true\n        return env_set(env, ast[2], func)\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end-1])\n        ast = ast[end]\n        # TCO loop\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                ast = ast[4]\n                # TCO loop\n            else\n                return nothing\n            end\n        else\n            ast = ast[3]\n            # TCO loop\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        return MalFunc(\n            (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])),\n            ast[3], env, ast[2])\n    else\n        f = EVAL(ast[1], env)\n        args = ast[2:end]\n        if isa(f, MalFunc) && f.ismacro\n            ast = f.fn(args...)\n            continue # TCO loop\n        end\n        args = map((x) -> EVAL(x,env), args)\n        if isa(f, MalFunc)\n            ast = f.ast\n            env = Env(f.env, f.params, args)\n            # TCO loop\n        else\n            return f(args...)\n        end\n    end\n  end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\nenv_set(repl_env, :eval, (ast) -> EVAL(ast, repl_env))\nenv_set(repl_env, symbol(\"*ARGV*\"), ARGS[2:end])\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nREP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif length(ARGS) > 0\n    REP(\"(load-file \\\"$(ARGS[1])\\\")\")\n    exit(0)\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        # TODO: show at least part of stack\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/step9_try.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\nusing types\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction quasiquote_loop(elts)\n    acc = Any[]\n    for i in length(elts):-1:1\n        elt = elts[i]\n        if isa(elt, Array) && length(elt) == 2 && elt[1] == symbol(\"splice-unquote\")\n            acc = Any[:concat, elt[2], acc]\n        else\n            acc = Any[:cons, quasiquote(elt), acc]\n        end\n    end\n    return acc\nend\n\nfunction quasiquote(ast)\n    if isa(ast, Array)\n        if length(ast) == 2 && ast[1] == symbol(\"unquote\")\n            ast[2]\n        else\n            quasiquote_loop(ast)\n        end\n    elseif isa(ast, Tuple)\n        Any[:vec, quasiquote_loop(ast)]\n    elseif typeof(ast) == Symbol || isa(ast, Dict)\n        Any[:quote, ast]\n    else\n        ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true\n\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    # apply\n    if isempty(ast) return ast end\n\n    if     :def! == ast[1]\n        return env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        env = let_env\n        ast = ast[3]\n        # TCO loop\n    elseif :quote == ast[1]\n        return ast[2]\n    elseif :quasiquote == ast[1]\n        ast = quasiquote(ast[2])\n        # TCO loop\n    elseif :defmacro! == ast[1]\n        func = EVAL(ast[3], env)\n        func.ismacro = true\n        return env_set(env, ast[2], func)\n    elseif symbol(\"try*\") == ast[1]\n        try\n            return EVAL(ast[2], env)\n        catch exc\n            e = string(exc)\n            if isa(exc, MalException)\n                e = exc.malval\n            elseif isa(exc, ErrorException)\n                e = exc.msg\n            else\n                e = string(e)\n            end\n            if length(ast) > 2 && ast[3][1] == symbol(\"catch*\")\n                return EVAL(ast[3][3], Env(env, Any[ast[3][2]], Any[e]))\n            else\n                rethrow(exc)\n            end\n        end\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end-1])\n        ast = ast[end]\n        # TCO loop\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                ast = ast[4]\n                # TCO loop\n            else\n                return nothing\n            end\n        else\n            ast = ast[3]\n            # TCO loop\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        return MalFunc(\n            (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])),\n            ast[3], env, ast[2])\n    else\n        f = EVAL(ast[1], env)\n        args = ast[2:end]\n        if isa(f, MalFunc) && f.ismacro\n            ast = f.fn(args...)\n            continue # TCO loop\n        end\n        args = map((x) -> EVAL(x,env), args)\n        if isa(f, MalFunc)\n            ast = f.ast\n            env = Env(f.env, f.params, args)\n            # TCO loop\n        else\n            return f(args...)\n        end\n    end\n  end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\nenv_set(repl_env, :eval, (ast) -> EVAL(ast, repl_env))\nenv_set(repl_env, symbol(\"*ARGV*\"), ARGS[2:end])\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nREP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif length(ARGS) > 0\n    REP(\"(load-file \\\"$(ARGS[1])\\\")\")\n    exit(0)\nend\n\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        # TODO: show at least part of stack\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/stepA_mal.jl",
    "content": "#!/usr/bin/env julia\n\npush!(LOAD_PATH, pwd(), \"/usr/share/julia/base\")\nimport readline_mod\nimport reader\nimport printer\nusing env\nimport core\nusing types\n\n# READ\nfunction READ(str)\n    reader.read_str(str)\nend\n\n# EVAL\nfunction quasiquote_loop(elts)\n    acc = Any[]\n    for i in length(elts):-1:1\n        elt = elts[i]\n        if isa(elt, Array) && length(elt) == 2 && elt[1] == symbol(\"splice-unquote\")\n            acc = Any[:concat, elt[2], acc]\n        else\n            acc = Any[:cons, quasiquote(elt), acc]\n        end\n    end\n    return acc\nend\n\nfunction quasiquote(ast)\n    if isa(ast, Array)\n        if length(ast) == 2 && ast[1] == symbol(\"unquote\")\n            ast[2]\n        else\n            quasiquote_loop(ast)\n        end\n    elseif isa(ast, Tuple)\n        Any[:vec, quasiquote_loop(ast)]\n    elseif typeof(ast) == Symbol || isa(ast, Dict)\n        Any[:quote, ast]\n    else\n        ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true\n\n    dbgenv = env_find(env, Symbol(\"DEBUG-EVAL\"))\n    if dbgenv != nothing\n        dbgeval = env_get(dbgenv, Symbol(\"DEBUG-EVAL\"))\n        if dbgeval !== nothing && dbgeval !== false\n            println(\"EVAL: $(printer.pr_str(ast,true))\")\n        end\n    end\n\n    if typeof(ast) == Symbol\n        return env_get(env,ast)\n    elseif isa(ast, Tuple)\n        return map((x) -> EVAL(x,env), ast)\n    elseif isa(ast, Dict)\n        return [x[1] => EVAL(x[2], env) for x=ast]\n    elseif !isa(ast, Array)\n        return ast\n    end\n\n    # apply\n    if isempty(ast) return ast end\n\n    if     :def! == ast[1]\n        return env_set(env, ast[2], EVAL(ast[3], env))\n    elseif symbol(\"let*\") == ast[1]\n        let_env = Env(env)\n        for i = 1:2:length(ast[2])\n            env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))\n        end\n        env = let_env\n        ast = ast[3]\n        # TCO loop\n    elseif :quote == ast[1]\n        return ast[2]\n    elseif :quasiquote == ast[1]\n        ast = quasiquote(ast[2])\n        # TCO loop\n    elseif :defmacro! == ast[1]\n        func = EVAL(ast[3], env)\n        func.ismacro = true\n        return env_set(env, ast[2], func)\n    elseif symbol(\"try*\") == ast[1]\n        try\n            return EVAL(ast[2], env)\n        catch exc\n            e = string(exc)\n            if isa(exc, MalException)\n                e = exc.malval\n            elseif isa(exc, ErrorException)\n                e = exc.msg\n            else\n                e = string(e)\n            end\n            if length(ast) > 2 && ast[3][1] == symbol(\"catch*\")\n                return EVAL(ast[3][3], Env(env, Any[ast[3][2]], Any[e]))\n            else\n                rethrow(exc)\n            end\n        end\n    elseif :do == ast[1]\n        map((x) -> EVAL(x,env), ast[2:end-1])\n        ast = ast[end]\n        # TCO loop\n    elseif :if == ast[1]\n        cond = EVAL(ast[2], env)\n        if cond === nothing || cond === false\n            if length(ast) >= 4\n                ast = ast[4]\n                # TCO loop\n            else\n                return nothing\n            end\n        else\n            ast = ast[3]\n            # TCO loop\n        end\n    elseif symbol(\"fn*\") == ast[1]\n        return MalFunc(\n            (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])),\n            ast[3], env, ast[2])\n    else\n        f = EVAL(ast[1], env)\n        args = ast[2:end]\n        if isa(f, MalFunc) && f.ismacro\n            ast = f.fn(args...)\n            continue # TCO loop\n        end\n        args = map((x) -> EVAL(x,env), args)\n        if isa(f, MalFunc)\n            ast = f.ast\n            env = Env(f.env, f.params, args)\n            # TCO loop\n        else\n            return f(args...)\n        end\n    end\n  end\nend\n\n# PRINT\nfunction PRINT(exp)\n    printer.pr_str(exp)\nend\n\n# REPL\nrepl_env = nothing\nfunction REP(str)\n    return PRINT(EVAL(READ(str), repl_env))\nend\n\n# core.jl: defined using Julia\nrepl_env = Env(nothing, core.ns)\nenv_set(repl_env, :eval, (ast) -> EVAL(ast, repl_env))\nenv_set(repl_env, symbol(\"*ARGV*\"), ARGS[2:end])\n\n# core.mal: defined using the language itself\nREP(\"(def! *host-language* \\\"julia\\\")\")\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nREP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif length(ARGS) > 0\n    REP(\"(load-file \\\"$(ARGS[1])\\\")\")\n    exit(0)\nend\n\nREP(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nwhile true\n    line = readline_mod.do_readline(\"user> \")\n    if line === nothing break end\n    try\n        println(REP(line))\n    catch e\n        if isa(e, ErrorException)\n            println(\"Error: $(e.msg)\")\n        else\n            println(\"Error: $(string(e))\")\n        end\n        # TODO: show at least part of stack\n        if !isa(e, StackOverflowError)\n            bt = catch_backtrace()\n            Base.show_backtrace(STDERR, bt)\n        end\n        println()\n    end\nend\n"
  },
  {
    "path": "impls/julia/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 100000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/julia/types.jl",
    "content": "module types\n\nexport MalException, MalFunc, sequential_Q, equal_Q, hash_map, Atom\n\nimport Base.copy\n\ntype MalException <: Exception\n    malval\nend\n\ntype MalFunc\n    fn::Function\n    ast\n    env\n    params\n    ismacro\n    meta\nend\n\n# ismacro default to false\nfunction MalFunc(fn, ast, env, params)\n    MalFunc(fn, ast, env, params, false, nothing)\nend\n\nfunction copy(f::MalFunc)\n    MalFunc(f.fn, f.ast, f.env, f.params, f.ismacro, f.meta)\nend\n\nfunction sequential_Q(obj)\n    isa(obj, Array) || isa(obj, Tuple)\nend\n\nfunction equal_Q(a, b)\n    ota = typeof(a)\n    otb = typeof(b)\n    if !(ota === otb || (sequential_Q(a) && sequential_Q(b)))\n        return false\n    end\n\n    if sequential_Q(a)\n        if length(a) !== length(b)\n            return false\n        end\n        for (x, y) in zip(a,b)\n            if !equal_Q(x, y)\n                return false\n            end\n        end\n        return true\n    elseif isa(a,AbstractString)\n        a == b\n    elseif isa(a,Dict)\n        if length(a) !== length(b)\n          return false\n        end\n        for (k,v) in a\n            if !equal_Q(v,b[k])\n                return false\n            end\n        end\n        return true\n    else\n        a === b\n    end\nend\n\nfunction hash_map(lst...)\n    hm = Dict()\n    for i = 1:2:length(lst)\n        hm[lst[i]] = lst[i+1]\n    end\n    hm\nend\n\ntype Atom\n    val\nend\n\nend\n\n\n"
  },
  {
    "path": "impls/kotlin/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Java and Zip\nRUN apt-get -y install openjdk-8-jdk\nRUN apt-get -y install unzip\n\nRUN curl -O -J -L https://github.com/JetBrains/kotlin/releases/download/v1.0.6/kotlin-compiler-1.0.6.zip\n\nRUN mkdir -p /kotlin-compiler\nRUN unzip kotlin-compiler-1.0.6.zip -d /kotlin-compiler\n\nENV KOTLIN_HOME /kotlin-compiler/kotlinc\nENV PATH $KOTLIN_HOME/bin:$PATH\n"
  },
  {
    "path": "impls/kotlin/Makefile",
    "content": "SOURCES_BASE = reader.kt printer.kt types.kt env.kt core.kt readline.kt\nSOURCES_LISP = step0_repl.kt step1_read_print.kt step2_eval.kt step3_env.kt step4_if_fn_do.kt \\\n\tstep5_tco.kt step6_file.kt step7_quote.kt step8_macros.kt step9_try.kt stepA_mal.kt\n\nJARS = $(SOURCES_LISP:%.kt=%.jar)\n\nall: $(JARS)\n\ndist: mal.jar mal\n\nmal.jar: stepA_mal.jar\n\tcp $< $@\n\nSHELL := bash\nmal: mal.jar\n\tcat <(echo -e '#!/bin/sh\\nexec java -jar \"$$0\" \"$$@\"') mal.jar > $@\n\tchmod +x mal\n\nclean:\n\trm -vf $(JARS) mal.jar mal\n\n$(JARS): %.jar: src/mal/%.kt $(SOURCES_BASE:%.kt=src/mal/%.kt)\n\tkotlinc src/mal/$(@:%.jar=%.kt) $(SOURCES_BASE:%.kt=src/mal/%.kt) -include-runtime -d $@\n"
  },
  {
    "path": "impls/kotlin/run",
    "content": "#!/usr/bin/env bash\nexec java -jar $(dirname $0)/${STEP:-stepA_mal}.jar \"${@}\"\n"
  },
  {
    "path": "impls/kotlin/src/mal/core.kt",
    "content": "package mal\n\nimport java.io.File\nimport java.util.*\n\nval ns = hashMapOf(\n        envPair(\"+\", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) }),\n        envPair(\"-\", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) }),\n        envPair(\"*\", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) }),\n        envPair(\"/\", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) }),\n\n        envPair(\"list\", { a: ISeq -> MalList(a) }),\n        envPair(\"list?\", { a: ISeq -> if (a.first() is MalList) TRUE else FALSE }),\n        envPair(\"empty?\", { a: ISeq -> if (a.first() !is ISeq || !(a.first() as ISeq).seq().any()) TRUE else FALSE }),\n        envPair(\"count\", { a: ISeq ->\n            if (a.first() is ISeq) MalInteger((a.first() as ISeq).count().toLong()) else MalInteger(0)\n        }),\n\n        envPair(\"=\", { a: ISeq -> pairwiseEquals(a) }),\n        envPair(\"<\", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value < y.value }) }),\n        envPair(\"<=\", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value <= y.value }) }),\n        envPair(\">\", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value > y.value }) }),\n        envPair(\">=\", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value >= y.value }) }),\n\n        envPair(\"pr-str\", { a: ISeq ->\n            MalString(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(\" \"))\n        }),\n        envPair(\"str\", { a: ISeq ->\n            MalString(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString(\"\"))\n        }),\n        envPair(\"prn\", { a: ISeq ->\n            println(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(\" \"))\n            NIL\n        }),\n        envPair(\"println\", { a: ISeq ->\n            println(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString(\" \"))\n            NIL\n        }),\n\n        envPair(\"read-string\", { a: ISeq ->\n            val string = a.first() as? MalString ?: throw MalException(\"slurp requires a string parameter\")\n            read_str(string.value)\n        }),\n        envPair(\"slurp\", { a: ISeq ->\n            val name = a.first() as? MalString ?: throw MalException(\"slurp requires a filename parameter\")\n            val text = File(name.value).readText()\n            MalString(text)\n        }),\n\n        envPair(\"cons\", { a: ISeq ->\n            val list = a.nth(1) as? ISeq ?: throw MalException(\"cons requires a list as its second parameter\")\n            val mutableList = list.seq().toCollection(LinkedList<MalType>())\n            mutableList.addFirst(a.nth(0))\n            MalList(mutableList)\n        }),\n        envPair(\"concat\", { a: ISeq -> MalList(a.seq().flatMap({ it -> (it as ISeq).seq() }).toCollection(LinkedList<MalType>())) }),\n        envPair(\"vec\", { a: ISeq ->\n            val list = a.first() as? ISeq ?: throw MalException(\"vec requires a sequence\")\n            MalVector(list)\n        }),\n        envPair(\"nth\", { a: ISeq ->\n            val list = a.nth(0) as? ISeq ?: throw MalException(\"nth requires a list as its first parameter\")\n            val index = a.nth(1) as? MalInteger ?: throw MalException(\"nth requires an integer as its second parameter\")\n            if (index.value >= list.count()) throw MalException(\"index out of bounds\")\n            list.nth(index.value.toInt())\n        }),\n        envPair(\"first\", { a: ISeq ->\n            if (a.nth(0) == NIL) NIL\n            else {\n                val list = a.nth(0) as? ISeq ?: throw MalException(\"first requires a list parameter\")\n                if (list.seq().any()) list.first() else NIL\n            }\n        }),\n        envPair(\"rest\", { a: ISeq ->\n            if (a.nth(0) == NIL) MalList()\n            else {\n                val list = a.nth(0) as? ISeq ?: throw MalException(\"rest requires a list parameter\")\n                MalList(list.rest())\n            }\n        }),\n\n        envPair(\"throw\", { a: ISeq ->\n            val throwable = a.nth(0)\n            throw MalCoreException(pr_str(throwable), throwable)\n        }),\n\n        envPair(\"apply\", { a: ISeq ->\n            val function = a.nth(0) as MalFunction\n            val params = MalList()\n            a.seq().drop(1).forEach({ it ->\n                if (it is ISeq) {\n                    it.seq().forEach({ x -> params.conj_BANG(x) })\n                } else {\n                    params.conj_BANG(it)\n                }\n            })\n            function.apply(params)\n        }),\n\n        envPair(\"map\", { a: ISeq ->\n            val function = a.nth(0) as MalFunction\n            MalList((a.nth(1) as ISeq).seq().map({ it ->\n                val params = MalList()\n                params.conj_BANG(it)\n                function.apply(params)\n            }).toCollection(LinkedList<MalType>()))\n        }),\n\n        envPair(\"nil?\", { a: ISeq -> if (a.nth(0) == NIL) TRUE else FALSE }),\n        envPair(\"true?\", { a: ISeq -> if (a.nth(0) == TRUE) TRUE else FALSE }),\n        envPair(\"false?\", { a: ISeq -> if (a.nth(0) == FALSE) TRUE else FALSE }),\n        envPair(\"string?\", { a: ISeq ->\n            if (a.nth(0) is MalString && !(a.nth(0) is MalKeyword)) TRUE else FALSE\n        }),\n        envPair(\"symbol?\", { a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE }),\n\n        envPair(\"symbol\", { a: ISeq -> MalSymbol((a.nth(0) as MalString).value) }),\n        envPair(\"keyword\", { a: ISeq ->\n            val param = a.nth(0)\n            if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value)\n        }),\n        envPair(\"keyword?\", { a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE }),\n        envPair(\"number?\", { a: ISeq -> if (a.nth(0) is MalInteger) TRUE else FALSE }),\n        envPair(\"fn?\", { a: ISeq -> if ((a.nth(0) as? MalFunction)?.is_macro ?: true) FALSE else TRUE }),\n        envPair(\"macro?\", { a: ISeq -> if ((a.nth(0) as? MalFunction)?.is_macro ?: false) TRUE else FALSE }),\n\n        envPair(\"vector\", { a: ISeq -> MalVector(a) }),\n        envPair(\"vector?\", { a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE }),\n\n        envPair(\"hash-map\", { a: ISeq ->\n            val map = MalHashMap()\n            pairwise(a).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) })\n            map\n        }),\n        envPair(\"map?\", { a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE }),\n        envPair(\"assoc\", { a: ISeq ->\n            val map = MalHashMap(a.first() as MalHashMap)\n            pairwise(a.rest()).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) })\n            map\n        }),\n        envPair(\"dissoc\", { a: ISeq ->\n            val map = MalHashMap(a.first() as MalHashMap)\n            a.rest().seq().forEach({ it -> map.dissoc_BANG(it as MalString) })\n            map\n        }),\n        envPair(\"get\", { a: ISeq ->\n            val map = a.nth(0) as? MalHashMap\n            val key = a.nth(1) as MalString\n            map?.elements?.get(key) ?: NIL\n        }),\n        envPair(\"contains?\", { a: ISeq ->\n            val map = a.nth(0) as? MalHashMap\n            val key = a.nth(1) as MalString\n            if (map?.elements?.get(key) != null) TRUE else FALSE\n        }),\n        envPair(\"keys\", { a: ISeq ->\n            val map = a.nth(0) as MalHashMap\n            MalList(map.elements.keys.toCollection(LinkedList<MalType>()))\n        }),\n        envPair(\"vals\", { a: ISeq ->\n            val map = a.nth(0) as MalHashMap\n            MalList(map.elements.values.toCollection(LinkedList<MalType>()))\n        }),\n        envPair(\"count\", { a: ISeq ->\n            val seq = a.nth(0) as? ISeq\n            if (seq != null) MalInteger(seq.count().toLong()) else ZERO\n        }),\n        envPair(\"sequential?\", { a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }),\n\n        envPair(\"with-meta\", { a: ISeq ->\n            val obj = a.nth(0)\n            val metadata = a.nth(1)\n            obj.with_meta(metadata)\n        }),\n        envPair(\"meta\", { a: ISeq -> a.first().metadata }),\n\n        envPair(\"conj\", { a: ISeq -> (a.first() as ISeq).conj(a.rest()) }),\n        envPair(\"seq\", { a: ISeq ->\n            val obj = a.nth(0)\n            if (obj is ISeq) {\n                if (obj.count() == 0) NIL\n                else MalList(obj.seq().toCollection(LinkedList<MalType>()))\n            } else if (obj is MalString && !(obj is MalKeyword)) {\n                if (obj.value.length == 0) NIL\n                else {\n                    var strs = obj.value.map({ c -> MalString(c.toString()) })\n                    MalList(strs.toCollection(LinkedList<MalType>()))\n                }\n            } else {\n                NIL\n            }\n        }),\n\n        envPair(\"atom\", { a: ISeq -> MalAtom(a.first()) }),\n        envPair(\"atom?\", { a: ISeq -> if (a.first() is MalAtom) TRUE else FALSE }),\n        envPair(\"deref\", { a: ISeq -> (a.first() as MalAtom).value }),\n        envPair(\"reset!\", { a: ISeq ->\n            val atom = a.nth(0) as MalAtom\n            val value = a.nth(1)\n            atom.value = value\n            value\n        }),\n        envPair(\"swap!\", { a: ISeq ->\n            val atom = a.nth(0) as MalAtom\n            val function = a.nth(1) as MalFunction\n\n            val params = MalList()\n            params.conj_BANG(atom.value)\n            a.seq().drop(2).forEach({ it -> params.conj_BANG(it) })\n\n            val value = function.apply(params)\n            atom.value = value\n\n            value\n        }),\n\n        envPair(\"readline\", { a: ISeq ->\n            val prompt = a.first() as MalString\n            try {\n                MalString(readline(prompt.value))\n            } catch (e: java.io.IOException) {\n                throw MalException(e.message)\n            } catch (e: EofException) {\n                NIL\n            }\n        }),\n\n        envPair(\"time-ms\", { a: ISeq -> MalInteger(System.currentTimeMillis()) })\n)\n\nprivate fun envPair(k: String, v: (ISeq) -> MalType): Pair<MalSymbol, MalType> = Pair(MalSymbol(k), MalFunction(v))\n\nprivate fun pairwise(s: ISeq): List<Pair<MalType, MalType>> {\n    val (keys, vals) = s.seq().withIndex().partition({ it -> it.index % 2 == 0 })\n    return keys.map({ it -> it.value }).zip(vals.map({ it -> it.value }))\n}\n\nprivate fun pairwiseCompare(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant =\n        if (pairwise(s).all({ it -> pred(it.first as MalInteger, it.second as MalInteger) })) TRUE else FALSE\n\nprivate fun pairwiseEquals(s: ISeq): MalConstant =\n        if (pairwise(s).all({ it -> it.first == it.second })) TRUE else FALSE\n"
  },
  {
    "path": "impls/kotlin/src/mal/env.kt",
    "content": "package mal\n\nimport java.util.*\n\nclass Env(val outer: Env?, binds: Sequence<MalSymbol>?, exprs: Sequence<MalType>?) {\n    val data = HashMap<String, MalType>()\n\n    init {\n        if (binds != null && exprs != null) {\n            val itb = binds.iterator()\n            val ite = exprs.iterator()\n            while (itb.hasNext()) {\n                val b = itb.next()\n                if (b.value != \"&\") {\n                    set(b, if (ite.hasNext()) ite.next() else NIL)\n                } else {\n                    if (!itb.hasNext()) throw MalException(\"expected a symbol name for varargs\")\n                    set(itb.next(), MalList(ite.asSequence().toCollection(LinkedList<MalType>())))\n                    break\n                }\n            }\n        }\n    }\n\n    constructor() : this(null, null, null)\n    constructor(outer: Env?) : this(outer, null, null)\n\n    fun set(key: MalSymbol, value: MalType): MalType {\n        data.put(key.value, value)\n        return value\n    }\n\n    fun get(key: String): MalType? = data[key] ?: outer?.get(key)\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/printer.kt",
    "content": "package mal\n\nfun pr_str(malType: MalType, print_readably: Boolean = false): String =\n        when (malType) {\n            is MalInteger -> malType.value.toString()\n            is MalKeyword -> \":\" + malType.value.substring(1)\n            is MalString ->\n                if (print_readably) {\n                    \"\\\"\" + malType.value.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\").replace(\"\\n\", \"\\\\n\") + \"\\\"\"\n                } else malType.value\n            is MalConstant -> malType.value\n            is MalSymbol -> malType.value\n            is MalFunction -> \"#\" + malType\n            is MalCoreException -> pr_str(malType.value, print_readably)\n            is MalException -> \"\\\"\" + (malType.message ?: \"exception\") + \"\\\"\"\n            is MalList -> pr_str(malType.elements, \"(\", \")\", print_readably)\n            is MalVector -> pr_str(malType.elements, \"[\", \"]\", print_readably)\n            is MalHashMap -> malType.elements.map({ it -> pr_str(it, print_readably) }).joinToString(\" \", \"{\", \"}\")\n            is MalAtom -> \"(atom \" + pr_str(malType.value, print_readably) + \")\"\n            else -> throw MalPrinterException(\"Unrecognized MalType: \" + malType)\n        }\n\nprivate fun pr_str(coll: Collection<MalType>, start: String, end: String, print_readably: Boolean = false): String =\n        coll.map({ it -> pr_str(it, print_readably) }).joinToString(\" \", start, end)\n\nprivate fun pr_str(mapEntry: Map.Entry<MalString, MalType>, print_readably: Boolean = false): String =\n        pr_str(mapEntry.key, print_readably) + \" \" + pr_str(mapEntry.value, print_readably)\n"
  },
  {
    "path": "impls/kotlin/src/mal/reader.kt",
    "content": "package mal\n\nimport kotlin.text.Regex\n\nval TOKEN_REGEX = Regex(\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\")\nval ATOM_REGEX = Regex(\"(^-?[0-9]+$)|(^nil$)|(^true$)|(^false$)|^\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"$|^\\\"(.*)$|:(.*)|(^[^\\\"]*$)\")\n\nclass Reader(sequence: Sequence<String>) {\n    val tokens = sequence.iterator()\n    var current = advance()\n\n    fun next(): String? {\n        var result = current\n        current = advance()\n        return result\n    }\n\n    fun peek(): String? = current\n\n    private fun advance(): String? = if (tokens.hasNext()) tokens.next() else null\n}\n\nfun read_str(input: String?): MalType {\n    val tokens = tokenizer(input) ?: return NIL\n    return read_form(Reader(tokens))\n}\n\nfun tokenizer(input: String?): Sequence<String>? {\n    if (input == null) return null\n\n    return TOKEN_REGEX.findAll(input)\n            .map({ it -> it.groups[1]?.value as String })\n            .filter({ it != \"\" && !it.startsWith(\";\")})\n}\n\nfun read_form(reader: Reader): MalType =\n        when (reader.peek()) {\n            null -> throw MalContinue()\n            \"(\"  -> read_list(reader)\n            \")\"  -> throw MalReaderException(\"expected form, got ')'\")\n            \"[\"  -> read_vector(reader)\n            \"]\"  -> throw MalReaderException(\"expected form, got ']'\")\n            \"{\"  -> read_hashmap(reader)\n            \"}\"  -> throw MalReaderException(\"expected form, got '}'\")\n            \"'\"  -> read_shorthand(reader, \"quote\")\n            \"`\"  -> read_shorthand(reader, \"quasiquote\")\n            \"~\"  -> read_shorthand(reader, \"unquote\")\n            \"~@\" -> read_shorthand(reader, \"splice-unquote\")\n            \"^\"  -> read_with_meta(reader)\n            \"@\"  -> read_shorthand(reader, \"deref\")\n            else -> read_atom(reader)\n        }\n\nfun read_list(reader: Reader): MalType = read_sequence(reader, MalList(), \")\")\nfun read_vector(reader: Reader): MalType = read_sequence(reader, MalVector(), \"]\")\n\nprivate fun read_sequence(reader: Reader, sequence: IMutableSeq, end: String): MalType {\n    reader.next()\n\n    do {\n        val form = when (reader.peek()) {\n            null -> throw MalReaderException(\"expected '$end', got EOF\")\n            end  -> { reader.next(); null }\n            else -> read_form(reader)\n        }\n\n        if (form != null) {\n            sequence.conj_BANG(form)\n        }\n    } while (form != null)\n\n    return sequence\n}\n\nfun read_hashmap(reader: Reader): MalType {\n    reader.next()\n    val hashMap = MalHashMap()\n\n    do {\n        var value : MalType? = null;\n        val key = when (reader.peek()) {\n            null -> throw MalReaderException(\"expected '}', got EOF\")\n            \"}\"  -> { reader.next(); null }\n            else -> {\n                var key = read_form(reader)\n                if (key !is MalString) {\n                    throw MalReaderException(\"hash-map keys must be strings or keywords\")\n                }\n                value = when (reader.peek()) {\n                    null -> throw MalReaderException(\"expected form, got EOF\")\n                    else -> read_form(reader)\n                }\n                key\n            }\n        }\n\n        if (key != null) {\n            hashMap.assoc_BANG(key, value as MalType)\n        }\n    } while (key != null)\n\n    return hashMap\n}\n\nfun read_shorthand(reader: Reader, symbol: String): MalType {\n    reader.next()\n\n    val list = MalList()\n    list.conj_BANG(MalSymbol(symbol))\n    list.conj_BANG(read_form(reader))\n\n    return list\n}\n\nfun read_with_meta(reader: Reader): MalType {\n    reader.next()\n\n    val meta = read_form(reader)\n    val obj = read_form(reader)\n\n    val list = MalList()\n    list.conj_BANG(MalSymbol(\"with-meta\"))\n    list.conj_BANG(obj)\n    list.conj_BANG(meta)\n\n    return list\n}\n\nfun read_atom(reader: Reader): MalType {\n    val next = reader.next() ?: throw MalReaderException(\"Unexpected null token\")\n    val groups = ATOM_REGEX.find(next)?.groups ?: throw MalReaderException(\"Unrecognized token: \" + next)\n\n    return if (groups[1]?.value != null) {\n        MalInteger(groups[1]?.value?.toLong() ?: throw MalReaderException(\"Error parsing number: \" + next))\n    } else if (groups[2]?.value != null) {\n        NIL\n    } else if (groups[3]?.value != null) {\n        TRUE\n    } else if (groups[4]?.value != null) {\n        FALSE\n    } else if (groups[5]?.value != null) {\n        MalString((groups[5]?.value as String).replace(Regex(\"\"\"\\\\(.)\"\"\"))\n            { m: MatchResult ->\n                if (m.groups[1]?.value == \"n\") \"\\n\"\n                else m.groups[1]?.value.toString()\n            })\n    } else if (groups[6]?.value != null) {\n        throw MalReaderException(\"expected '\\\"', got EOF\")\n    } else if (groups[7]?.value != null) {\n        MalKeyword(groups[7]?.value as String)\n    } else if (groups[8]?.value != null) {\n        MalSymbol(groups[8]?.value as String)\n    } else {\n        throw MalReaderException(\"Unrecognized token: \" + next)\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/readline.kt",
    "content": "package mal\n\nclass EofException : Exception(\"EOF\")\n\nfun readline(prompt: String): String {\n    print(prompt)\n    return readLine() ?: throw EofException()\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step0_repl.kt",
    "content": "package mal\n\nfun main(args: Array<String>) {\n    fun read(input: String?): String? = input\n    fun eval(expression: String?): String? = expression\n    fun print(result: String?): String? = result\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(print(eval(read(input))))\n        } catch (e: EofException) {\n            break\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step1_read_print.kt",
    "content": "package mal\n\nfun main(args: Array<String>) {\n    fun read(input: String?): MalType = read_str(input)\n    fun eval(expression: MalType): MalType = expression\n    fun print(result: MalType) = pr_str(result, print_readably = true)\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(print(eval(read(input))))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step2_eval.kt",
    "content": "package mal\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(ast: MalType, env: Map<String, MalType>): MalType {\n    // println (\"EVAL: ${print(ast)}\")\n    when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            val evaluated = ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n            if (evaluated.first() !is MalFunction) throw MalException(\"cannot execute non-function\")\n            return (evaluated.first() as MalFunction).apply(evaluated.rest())\n        }\n        is MalSymbol -> return env[ast.value] ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n    }\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun main(args: Array<String>) {\n    val env = hashMapOf(\n            Pair(\"+\", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) })),\n            Pair(\"-\", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) })),\n            Pair(\"*\", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) })),\n            Pair(\"/\", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) }))\n    )\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(print(eval(read(input), env)))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step3_env.kt",
    "content": "package mal\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(ast: MalType, env: Env): MalType {\n\n    val dbgeval = env.get(\"DEBUG-EVAL\")\n    if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n        println (\"EVAL: ${print(ast)}\")\n    }\n\n    when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            val first = ast.first()\n            if (first is MalSymbol && first.value == \"def!\") {\n                return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n            } else if (first is MalSymbol && first.value == \"let*\") {\n                val child = Env(env)\n                val bindings = ast.nth(1)\n                if (bindings !is ISeq) throw MalException(\"expected sequence as the first parameter to let*\")\n                val it = bindings.seq().iterator()\n                while (it.hasNext()) {\n                    val key = it.next()\n                    if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                    val value = eval(it.next(), child)\n                    child.set(key as MalSymbol, value)\n                }\n                return eval(ast.nth(2), child)\n            } else {\n                val evaluated = ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                if (evaluated.first() !is MalFunction) throw MalException(\"cannot execute non-function\")\n                return (evaluated.first() as MalFunction).apply(evaluated.rest())\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n    }\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun main(args: Array<String>) {\n    val env = Env()\n    env.set(MalSymbol(\"+\"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) }))\n    env.set(MalSymbol(\"-\"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) }))\n    env.set(MalSymbol(\"*\"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) }))\n    env.set(MalSymbol(\"/\"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) }))\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(print(eval(read(input), env)))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step4_if_fn_do.kt",
    "content": "package mal\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(ast: MalType, env: Env): MalType {\n\n    val dbgeval = env.get(\"DEBUG-EVAL\")\n    if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n        println (\"EVAL: ${print(ast)}\")\n    }\n\n    when (ast) {\n      is MalList -> {\n        if (ast.count() == 0) return ast\n        val first = ast.first()\n        if (first is MalSymbol) {\n            when (first.value) {\n                \"def!\" -> return eval_def_BANG(ast, env)\n                \"let*\" -> return eval_let_STAR(ast, env)\n                \"fn*\"  -> return eval_fn_STAR(ast, env)\n                \"do\"   -> return eval_do(ast, env)\n                \"if\"   -> return eval_if(ast, env)\n            }\n        }\n        return eval_function_call(ast, env)\n      }\n      is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n      is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n      is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n      else -> return ast\n    }\n}\n\nprivate fun eval_def_BANG(ast: ISeq, env: Env): MalType =\n        env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n\nprivate fun eval_let_STAR(ast: ISeq, env: Env): MalType {\n    val child = Env(env)\n    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n    val it = bindings.seq().iterator()\n    while (it.hasNext()) {\n        val key = it.next()\n        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n\n        val value = eval(it.next(), child)\n        child.set(key as MalSymbol, value)\n    }\n\n    return eval(ast.nth(2), child)\n}\n\nprivate fun eval_fn_STAR(ast: ISeq, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val symbols = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFunction({ s: ISeq ->\n        eval(body, Env(env, symbols, s.seq()))\n    })\n}\n\nprivate fun eval_do(ast: ISeq, env: Env): MalType {\n    for (i in 1..ast.count() - 2) {\n        eval(ast.nth(i), env)\n    }\n    return eval(ast.seq().last(), env)\n}\n\nprivate fun eval_if(ast: ISeq, env: Env): MalType {\n    val check = eval(ast.nth(1), env)\n\n    return if (check != NIL && check != FALSE) {\n        eval(ast.nth(2), env)\n    } else if (ast.count() > 3) {\n        eval(ast.nth(3), env)\n    } else NIL\n}\n\nprivate fun eval_function_call(ast: MalList, env: Env): MalType {\n    val evaluated = ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n    val first = evaluated.first() as? MalFunction ?: throw MalException(\"cannot execute non-function\")\n    return first.apply(evaluated.rest())\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step5_tco.kt",
    "content": "package mal\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(_ast: MalType, _env: Env): MalType {\n    var ast = _ast\n    var env = _env\n\n    while (true) {\n\n      val dbgeval = env.get(\"DEBUG-EVAL\")\n      if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n          println (\"EVAL: ${print(ast)}\")\n      }\n\n      when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            when ((ast.first() as? MalSymbol)?.value) {\n                \"def!\" -> return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n                \"let*\" -> {\n                    val childEnv = Env(env)\n                    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n                    val it = bindings.seq().iterator()\n                    while (it.hasNext()) {\n                        val key = it.next()\n                        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                        childEnv.set(key as MalSymbol, eval(it.next(), childEnv))\n                    }\n\n                    env = childEnv\n                    ast = ast.nth(2)\n                }\n                \"fn*\" -> return fn_STAR(ast, env)\n                \"do\" -> {\n                    for (i in 1..ast.count() - 2) {\n                        eval(ast.nth(i), env)\n                    }\n                    ast = ast.seq().last()\n                }\n                \"if\" -> {\n                    val check = eval(ast.nth(1), env)\n\n                    if (check !== NIL && check !== FALSE) {\n                        ast = ast.nth(2)\n                    } else if (ast.count() > 3) {\n                        ast = ast.nth(3)\n                    } else return NIL\n                }\n                else -> {\n                    val evaluated = ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                    val firstEval = evaluated.first()\n\n                    when (firstEval) {\n                        is MalFnFunction -> {\n                            ast = firstEval.ast\n                            env = Env(firstEval.env, firstEval.params, evaluated.rest().seq())\n                        }\n                        is MalFunction -> return firstEval.apply(evaluated.rest())\n                        else -> throw MalException(\"cannot execute non-function\")\n                    }\n                }\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n      }\n    }\n}\n\nprivate fun fn_STAR(ast: MalList, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val params = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFnFunction(body, params, env, { s: ISeq -> eval(body, Env(env, params, s.seq())) })\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step6_file.kt",
    "content": "package mal\n\nimport java.util.*\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(_ast: MalType, _env: Env): MalType {\n    var ast = _ast\n    var env = _env\n\n    while (true) {\n\n      val dbgeval = env.get(\"DEBUG-EVAL\")\n      if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n          println (\"EVAL: ${print(ast)}\")\n      }\n\n      when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            when ((ast.first() as? MalSymbol)?.value) {\n                \"def!\" -> return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n                \"let*\" -> {\n                    val childEnv = Env(env)\n                    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n                    val it = bindings.seq().iterator()\n                    while (it.hasNext()) {\n                        val key = it.next()\n                        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                        childEnv.set(key as MalSymbol, eval(it.next(), childEnv))\n                    }\n\n                    env = childEnv\n                    ast = ast.nth(2)\n                }\n                \"fn*\" -> return fn_STAR(ast, env)\n                \"do\" -> {\n                    for (i in 1..ast.count() - 2) {\n                        eval(ast.nth(i), env)\n                    }\n                    ast = ast.seq().last()\n                }\n                \"if\" -> {\n                    val check = eval(ast.nth(1), env)\n\n                    if (check !== NIL && check !== FALSE) {\n                        ast = ast.nth(2)\n                    } else if (ast.count() > 3) {\n                        ast = ast.nth(3)\n                    } else return NIL\n                }\n                else -> {\n                    val evaluated = ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                    val firstEval = evaluated.first()\n\n                    when (firstEval) {\n                        is MalFnFunction -> {\n                            ast = firstEval.ast\n                            env = Env(firstEval.env, firstEval.params, evaluated.rest().seq())\n                        }\n                        is MalFunction -> return firstEval.apply(evaluated.rest())\n                        else -> throw MalException(\"cannot execute non-function\")\n                    }\n                }\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n      }\n    }\n}\n\nprivate fun fn_STAR(ast: MalList, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val params = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFnFunction(body, params, env, { s: ISeq -> eval(body, Env(env, params, s.seq())) })\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList(args.drop(1).map({ it -> MalString(it) }).toCollection(LinkedList<MalType>())))\n    repl_env.set(MalSymbol(\"eval\"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\n    if (args.any()) {\n        rep(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n        return\n    }\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step7_quote.kt",
    "content": "package mal\n\nimport java.util.*\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(_ast: MalType, _env: Env): MalType {\n    var ast = _ast\n    var env = _env\n\n    while (true) {\n\n      val dbgeval = env.get(\"DEBUG-EVAL\")\n      if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n          println (\"EVAL: ${print(ast)}\")\n      }\n\n      when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            when ((ast.first() as? MalSymbol)?.value) {\n                \"def!\" -> return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n                \"let*\" -> {\n                    val childEnv = Env(env)\n                    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n                    val it = bindings.seq().iterator()\n                    while (it.hasNext()) {\n                        val key = it.next()\n                        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                        childEnv.set(key as MalSymbol, eval(it.next(), childEnv))\n                    }\n\n                    env = childEnv\n                    ast = ast.nth(2)\n                }\n                \"fn*\" -> return fn_STAR(ast, env)\n                \"do\" -> {\n                    for (i in 1..ast.count() - 2) {\n                        eval(ast.nth(i), env)\n                    }\n                    ast = ast.seq().last()\n                }\n                \"if\" -> {\n                    val check = eval(ast.nth(1), env)\n\n                    if (check !== NIL && check !== FALSE) {\n                        ast = ast.nth(2)\n                    } else if (ast.count() > 3) {\n                        ast = ast.nth(3)\n                    } else return NIL\n                }\n                \"quote\" -> return ast.nth(1)\n                \"quasiquote\" -> ast = quasiquote(ast.nth(1))\n                else -> {\n                    val evaluated = ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                    val firstEval = evaluated.first()\n\n                    when (firstEval) {\n                        is MalFnFunction -> {\n                            ast = firstEval.ast\n                            env = Env(firstEval.env, firstEval.params, evaluated.rest().seq())\n                        }\n                        is MalFunction -> return firstEval.apply(evaluated.rest())\n                        else -> throw MalException(\"cannot execute non-function\")\n                    }\n                }\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n      }\n    }\n}\n\nprivate fun fn_STAR(ast: MalList, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val params = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFnFunction(body, params, env, { s: ISeq -> eval(body, Env(env, params, s.seq())) })\n}\n\nprivate fun quasiquote(ast: MalType): MalType {\n    when (ast) {\n        is MalList -> {\n            if (ast.count() == 2 && (ast.first() as? MalSymbol)?.value == \"unquote\") {\n                return ast.nth(1)\n            } else {\n                return ast.elements.foldRight(MalList(), ::quasiquote_loop)\n            }\n        }\n        is MalVector -> {\n            val result = MalList()\n            result.conj_BANG(MalSymbol(\"vec\"))\n            result.conj_BANG(ast.elements.foldRight(MalList(), ::quasiquote_loop))\n            return result\n        }\n        is MalSymbol, is MalHashMap -> {\n            val quoted = MalList()\n            quoted.conj_BANG(MalSymbol(\"quote\"))\n            quoted.conj_BANG(ast)\n            return quoted\n        }\n        else -> return ast\n    }\n}\n\nprivate fun quasiquote_loop(elt: MalType, acc: MalList): MalList {\n    val result = MalList()\n    if (elt is MalList && elt.count() == 2 && (elt.first() as? MalSymbol)?.value == \"splice-unquote\") {\n        result.conj_BANG(MalSymbol(\"concat\"))\n        result.conj_BANG(elt.nth(1))\n    } else {\n        result.conj_BANG(MalSymbol(\"cons\"))\n        result.conj_BANG(quasiquote(elt))\n    }\n    result.conj_BANG(acc)\n    return result\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList(args.drop(1).map({ it -> MalString(it) }).toCollection(LinkedList<MalType>())))\n    repl_env.set(MalSymbol(\"eval\"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\n    if (args.any()) {\n        rep(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n        return\n    }\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step8_macros.kt",
    "content": "package mal\n\nimport java.util.*\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(_ast: MalType, _env: Env): MalType {\n    var ast = _ast\n    var env = _env\n\n    while (true) {\n\n      val dbgeval = env.get(\"DEBUG-EVAL\")\n      if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n          println (\"EVAL: ${print(ast)}\")\n      }\n\n      when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            when ((ast.first() as? MalSymbol)?.value) {\n                \"def!\" -> return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n                \"let*\" -> {\n                    val childEnv = Env(env)\n                    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n                    val it = bindings.seq().iterator()\n                    while (it.hasNext()) {\n                        val key = it.next()\n                        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                        childEnv.set(key as MalSymbol, eval(it.next(), childEnv))\n                    }\n\n                    env = childEnv\n                    ast = ast.nth(2)\n                }\n                \"fn*\" -> return fn_STAR(ast, env)\n                \"do\" -> {\n                    for (i in 1..ast.count() - 2) {\n                        eval(ast.nth(i), env)\n                    }\n                    ast = ast.seq().last()\n                }\n                \"if\" -> {\n                    val check = eval(ast.nth(1), env)\n\n                    if (check !== NIL && check !== FALSE) {\n                        ast = ast.nth(2)\n                    } else if (ast.count() > 3) {\n                        ast = ast.nth(3)\n                    } else return NIL\n                }\n                \"quote\" -> return ast.nth(1)\n                \"quasiquote\" -> ast = quasiquote(ast.nth(1))\n                \"defmacro!\" -> return defmacro(ast, env)\n                else -> {\n                  val firstEval = eval(ast.first(), env)\n                  if (firstEval is MalFunction && firstEval.is_macro) {\n                      ast = firstEval.apply(ast.rest())\n                  } else {\n                    val args = ast.elements.drop(1).fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                    when (firstEval) {\n                        is MalFnFunction -> {\n                            ast = firstEval.ast\n                            env = Env(firstEval.env, firstEval.params, args.seq())\n                        }\n                        is MalFunction -> return firstEval.apply(args)\n                        else -> throw MalException(\"cannot execute non-function\")\n                    }\n                  }\n                }\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n      }\n    }\n}\n\nprivate fun fn_STAR(ast: MalList, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val params = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFnFunction(body, params, env, { s: ISeq -> eval(body, Env(env, params, s.seq())) })\n}\n\nprivate fun quasiquote(ast: MalType): MalType {\n    when (ast) {\n        is MalList -> {\n            if (ast.count() == 2 && (ast.first() as? MalSymbol)?.value == \"unquote\") {\n                return ast.nth(1)\n            } else {\n                return ast.elements.foldRight(MalList(), ::quasiquote_loop)\n            }\n        }\n        is MalVector -> {\n            val result = MalList()\n            result.conj_BANG(MalSymbol(\"vec\"))\n            result.conj_BANG(ast.elements.foldRight(MalList(), ::quasiquote_loop))\n            return result\n        }\n        is MalSymbol, is MalHashMap -> {\n            val quoted = MalList()\n            quoted.conj_BANG(MalSymbol(\"quote\"))\n            quoted.conj_BANG(ast)\n            return quoted\n        }\n        else -> return ast\n    }\n}\n\nprivate fun quasiquote_loop(elt: MalType, acc: MalList): MalList {\n    val result = MalList()\n    if (elt is MalList && elt.count() == 2 && (elt.first() as? MalSymbol)?.value == \"splice-unquote\") {\n        result.conj_BANG(MalSymbol(\"concat\"))\n        result.conj_BANG(elt.nth(1))\n    } else {\n        result.conj_BANG(MalSymbol(\"cons\"))\n        result.conj_BANG(quasiquote(elt))\n    }\n    result.conj_BANG(acc)\n    return result\n}\n\nprivate fun defmacro(ast: MalList, env: Env): MalType {\n    val macro = eval(ast.nth(2), env) as MalFunction\n    macro.is_macro = true\n\n    return env.set(ast.nth(1) as MalSymbol, macro)\n}\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList(args.drop(1).map({ it -> MalString(it) }).toCollection(LinkedList<MalType>())))\n    repl_env.set(MalSymbol(\"eval\"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if (args.any()) {\n        rep(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n        return\n    }\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/step9_try.kt",
    "content": "package mal\n\nimport java.util.*\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(_ast: MalType, _env: Env): MalType {\n    var ast = _ast\n    var env = _env\n\n    while (true) {\n\n      val dbgeval = env.get(\"DEBUG-EVAL\")\n      if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n          println (\"EVAL: ${print(ast)}\")\n      }\n\n      when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            when ((ast.first() as? MalSymbol)?.value) {\n                \"def!\" -> return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n                \"let*\" -> {\n                    val childEnv = Env(env)\n                    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n                    val it = bindings.seq().iterator()\n                    while (it.hasNext()) {\n                        val key = it.next()\n                        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                        childEnv.set(key as MalSymbol, eval(it.next(), childEnv))\n                    }\n\n                    env = childEnv\n                    ast = ast.nth(2)\n                }\n                \"fn*\" -> return fn_STAR(ast, env)\n                \"do\" -> {\n                    for (i in 1..ast.count() - 2) {\n                        eval(ast.nth(i), env)\n                    }\n                    ast = ast.seq().last()\n                }\n                \"if\" -> {\n                    val check = eval(ast.nth(1), env)\n\n                    if (check !== NIL && check !== FALSE) {\n                        ast = ast.nth(2)\n                    } else if (ast.count() > 3) {\n                        ast = ast.nth(3)\n                    } else return NIL\n                }\n                \"quote\" -> return ast.nth(1)\n                \"quasiquote\" -> ast = quasiquote(ast.nth(1))\n                \"defmacro!\" -> return defmacro(ast, env)\n                \"try*\" -> return try_catch(ast, env)\n                else -> {\n                  val firstEval = eval(ast.first(), env)\n                  if (firstEval is MalFunction && firstEval.is_macro) {\n                      ast = firstEval.apply(ast.rest())\n                  } else {\n                    val args = ast.elements.drop(1).fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                    when (firstEval) {\n                        is MalFnFunction -> {\n                            ast = firstEval.ast\n                            env = Env(firstEval.env, firstEval.params, args.seq())\n                        }\n                        is MalFunction -> return firstEval.apply(args)\n                        else -> throw MalException(\"cannot execute non-function\")\n                    }\n                  }\n                }\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n      }\n    }\n}\n\nprivate fun fn_STAR(ast: MalList, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val params = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFnFunction(body, params, env, { s: ISeq -> eval(body, Env(env, params, s.seq())) })\n}\n\nprivate fun quasiquote(ast: MalType): MalType {\n    when (ast) {\n        is MalList -> {\n            if (ast.count() == 2 && (ast.first() as? MalSymbol)?.value == \"unquote\") {\n                return ast.nth(1)\n            } else {\n                return ast.elements.foldRight(MalList(), ::quasiquote_loop)\n            }\n        }\n        is MalVector -> {\n            val result = MalList()\n            result.conj_BANG(MalSymbol(\"vec\"))\n            result.conj_BANG(ast.elements.foldRight(MalList(), ::quasiquote_loop))\n            return result\n        }\n        is MalSymbol, is MalHashMap -> {\n            val quoted = MalList()\n            quoted.conj_BANG(MalSymbol(\"quote\"))\n            quoted.conj_BANG(ast)\n            return quoted\n        }\n        else -> return ast\n    }\n}\n\nprivate fun quasiquote_loop(elt: MalType, acc: MalList): MalList {\n    val result = MalList()\n    if (elt is MalList && elt.count() == 2 && (elt.first() as? MalSymbol)?.value == \"splice-unquote\") {\n        result.conj_BANG(MalSymbol(\"concat\"))\n        result.conj_BANG(elt.nth(1))\n    } else {\n        result.conj_BANG(MalSymbol(\"cons\"))\n        result.conj_BANG(quasiquote(elt))\n    }\n    result.conj_BANG(acc)\n    return result\n}\n\nprivate fun defmacro(ast: MalList, env: Env): MalType {\n    val macro = eval(ast.nth(2), env) as MalFunction\n    macro.is_macro = true\n\n    return env.set(ast.nth(1) as MalSymbol, macro)\n}\n\nprivate fun try_catch(ast: MalList, env: Env): MalType =\n    try {\n        eval(ast.nth(1), env)\n    } catch (e: Exception) {\n        if (ast.count() < 3) { throw e }\n        val thrown = if (e is MalException) e else MalException(e.message)\n        val symbol = (ast.nth(2) as MalList).nth(1) as MalSymbol\n\n        val catchBody = (ast.nth(2) as MalList).nth(2)\n        val catchEnv = Env(env)\n        catchEnv.set(symbol, thrown)\n\n        eval(catchBody, catchEnv)\n    }\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList(args.drop(1).map({ it -> MalString(it) }).toCollection(LinkedList<MalType>())))\n    repl_env.set(MalSymbol(\"eval\"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if (args.any()) {\n        rep(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n        return\n    }\n\n    while (true) {\n        val input = readline(\"user> \")\n\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/stepA_mal.kt",
    "content": "package mal\n\nimport java.util.*\n\nfun read(input: String?): MalType = read_str(input)\n\nfun eval(_ast: MalType, _env: Env): MalType {\n    var ast = _ast\n    var env = _env\n\n    while (true) {\n\n      val dbgeval = env.get(\"DEBUG-EVAL\")\n      if (dbgeval !== null && dbgeval !== NIL && dbgeval !== FALSE) {\n          println (\"EVAL: ${print(ast)}\")\n      }\n\n      when (ast) {\n        is MalList -> {\n            if (ast.count() == 0) return ast\n            when ((ast.first() as? MalSymbol)?.value) {\n                \"def!\" -> return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))\n                \"let*\" -> {\n                    val childEnv = Env(env)\n                    val bindings = ast.nth(1) as? ISeq ?: throw MalException(\"expected sequence as the first parameter to let*\")\n\n                    val it = bindings.seq().iterator()\n                    while (it.hasNext()) {\n                        val key = it.next()\n                        if (!it.hasNext()) throw MalException(\"odd number of binding elements in let*\")\n                        childEnv.set(key as MalSymbol, eval(it.next(), childEnv))\n                    }\n\n                    env = childEnv\n                    ast = ast.nth(2)\n                }\n                \"fn*\" -> return fn_STAR(ast, env)\n                \"do\" -> {\n                    for (i in 1..ast.count() - 2) {\n                        eval(ast.nth(i), env)\n                    }\n                    ast = ast.seq().last()\n                }\n                \"if\" -> {\n                    val check = eval(ast.nth(1), env)\n\n                    if (check !== NIL && check !== FALSE) {\n                        ast = ast.nth(2)\n                    } else if (ast.count() > 3) {\n                        ast = ast.nth(3)\n                    } else return NIL\n                }\n                \"quote\" -> return ast.nth(1)\n                \"quasiquote\" -> ast = quasiquote(ast.nth(1))\n                \"defmacro!\" -> return defmacro(ast, env)\n                \"try*\" -> return try_catch(ast, env)\n                else -> {\n                  val firstEval = eval(ast.first(), env)\n                  if (firstEval is MalFunction && firstEval.is_macro) {\n                      ast = firstEval.apply(ast.rest())\n                  } else {\n                    val args = ast.elements.drop(1).fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })\n                    when (firstEval) {\n                        is MalFnFunction -> {\n                            ast = firstEval.ast\n                            env = Env(firstEval.env, firstEval.params, args.seq())\n                        }\n                        is MalFunction -> return firstEval.apply(args)\n                        else -> throw MalException(\"cannot execute non-function\")\n                    }\n                  }\n                }\n            }\n        }\n        is MalSymbol -> return env.get(ast.value) ?: throw MalException(\"'${ast.value}' not found\")\n        is MalVector -> return ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })\n        is MalHashMap -> return ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })\n        else -> return ast\n      }\n    }\n}\n\nprivate fun fn_STAR(ast: MalList, env: Env): MalType {\n    val binds = ast.nth(1) as? ISeq ?: throw MalException(\"fn* requires a binding list as first parameter\")\n    val params = binds.seq().filterIsInstance<MalSymbol>()\n    val body = ast.nth(2)\n\n    return MalFnFunction(body, params, env, { s: ISeq -> eval(body, Env(env, params, s.seq())) })\n}\n\nprivate fun quasiquote(ast: MalType): MalType {\n    when (ast) {\n        is MalList -> {\n            if (ast.count() == 2 && (ast.first() as? MalSymbol)?.value == \"unquote\") {\n                return ast.nth(1)\n            } else {\n                return ast.elements.foldRight(MalList(), ::quasiquote_loop)\n            }\n        }\n        is MalVector -> {\n            val result = MalList()\n            result.conj_BANG(MalSymbol(\"vec\"))\n            result.conj_BANG(ast.elements.foldRight(MalList(), ::quasiquote_loop))\n            return result\n        }\n        is MalSymbol, is MalHashMap -> {\n            val quoted = MalList()\n            quoted.conj_BANG(MalSymbol(\"quote\"))\n            quoted.conj_BANG(ast)\n            return quoted\n        }\n        else -> return ast\n    }\n}\n\nprivate fun quasiquote_loop(elt: MalType, acc: MalList): MalList {\n    val result = MalList()\n    if (elt is MalList && elt.count() == 2 && (elt.first() as? MalSymbol)?.value == \"splice-unquote\") {\n        result.conj_BANG(MalSymbol(\"concat\"))\n        result.conj_BANG(elt.nth(1))\n    } else {\n        result.conj_BANG(MalSymbol(\"cons\"))\n        result.conj_BANG(quasiquote(elt))\n    }\n    result.conj_BANG(acc)\n    return result\n}\n\nprivate fun defmacro(ast: MalList, env: Env): MalType {\n    val f = eval(ast.nth(2), env) as MalFunction\n    val macro = MalFunction(f.lambda)\n    macro.is_macro = true\n\n    return env.set(ast.nth(1) as MalSymbol, macro)\n}\n\nprivate fun try_catch(ast: MalList, env: Env): MalType =\n    try {\n        eval(ast.nth(1), env)\n    } catch (e: Exception) {\n        if (ast.count() < 3) { throw e }\n        val thrown = if (e is MalException) e else MalException(e.message)\n        val symbol = (ast.nth(2) as MalList).nth(1) as MalSymbol\n\n        val catchBody = (ast.nth(2) as MalList).nth(2)\n        val catchEnv = Env(env)\n        catchEnv.set(symbol, thrown)\n\n        eval(catchBody, catchEnv)\n    }\n\nfun print(result: MalType) = pr_str(result, print_readably = true)\n\nfun rep(input: String, env: Env): String =\n        print(eval(read(input), env))\n\nfun main(args: Array<String>) {\n    val repl_env = Env()\n    ns.forEach({ it -> repl_env.set(it.key, it.value) })\n\n    repl_env.set(MalSymbol(\"*host-language*\"), MalString(\"kotlin\"))\n    repl_env.set(MalSymbol(\"*ARGV*\"), MalList(args.drop(1).map({ it -> MalString(it) }).toCollection(LinkedList<MalType>())))\n    repl_env.set(MalSymbol(\"eval\"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))\n\n    rep(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if (args.any()) {\n        rep(\"(load-file \\\"${args[0]}\\\")\", repl_env)\n        return\n    }\n\n    rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env)\n    while (true) {\n        val input = readline(\"user> \")\n        try {\n            println(rep(input, repl_env))\n        } catch (e: EofException) {\n            break\n        } catch (e: MalContinue) {\n        } catch (e: MalException) {\n            println(\"Error: \" + e.message)\n        } catch (t: Throwable) {\n            println(\"Uncaught \" + t + \": \" + t.message)\n            t.printStackTrace()\n        }\n    }\n}\n"
  },
  {
    "path": "impls/kotlin/src/mal/types.kt",
    "content": "package mal\n\nimport java.util.*\n\nopen class MalException(message: String?) : Exception(message), MalType {\n    override var metadata: MalType = NIL\n    override fun with_meta(meta: MalType): MalType {\n        val exception = MalException(message)\n        exception.metadata = meta\n        return exception\n    }\n}\n\nclass MalContinue() : MalException(\"continue\")\nclass MalReaderException(message: String) : MalException(message)\nclass MalPrinterException(message: String) : MalException(message)\n\nclass MalCoreException(message: String, val value: MalType) : MalException(message) {\n    override fun with_meta(meta: MalType): MalType {\n        val exception = MalCoreException(message as String, value)\n        exception.metadata = meta\n        return exception\n    }\n}\n\ninterface MalType {\n    var metadata: MalType\n    fun with_meta(meta: MalType): MalType\n}\n\nopen class MalConstant(val value: String) : MalType {\n    override var metadata: MalType = NIL\n\n    override fun equals(other: Any?): Boolean = other is MalConstant && value.equals(other.value)\n    override fun hashCode(): Int = value.hashCode()\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalConstant(value)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nclass MalInteger(val value: Long) : MalType {\n    override var metadata: MalType = NIL\n\n    operator fun plus(a: MalInteger): MalInteger = MalInteger(value + a.value)\n    operator fun minus(a: MalInteger): MalInteger = MalInteger(value - a.value)\n    operator fun times(a: MalInteger): MalInteger = MalInteger(value * a.value)\n    operator fun div(a: MalInteger): MalInteger = MalInteger(value / a.value)\n    operator fun compareTo(a: MalInteger): Int = value.compareTo(a.value)\n\n    override fun equals(other: Any?): Boolean = other is MalInteger && value.equals(other.value)\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalInteger(value)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nclass MalSymbol(val value: String) : MalType {\n    override var metadata: MalType = NIL\n\n    override fun equals(other: Any?): Boolean = other is MalSymbol && value.equals(other.value)\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalSymbol(value)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nopen class MalString(value: String) : MalConstant(value) {\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalString(value)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nclass MalKeyword(value: String) : MalString(\"\\u029E\" + value) {\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalKeyword(value)\n        obj.metadata = meta\n        return obj\n    }\n}\n\ninterface ILambda : MalType {\n    fun apply(seq: ISeq): MalType\n}\n\nopen class MalFunction(val lambda: (ISeq) -> MalType) : MalType, ILambda {\n    var is_macro: Boolean = false\n    override var metadata: MalType = NIL\n\n    override fun apply(seq: ISeq): MalType = lambda(seq)\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalFunction(lambda)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nclass MalFnFunction(val ast: MalType, val params: Sequence<MalSymbol>, val env: Env, lambda: (ISeq) -> MalType) : MalFunction(lambda) {\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalFnFunction(ast, params, env, lambda)\n        obj.metadata = meta\n        return obj\n    }\n}\n\ninterface ISeq : MalType {\n    fun seq(): Sequence<MalType>\n    fun first(): MalType\n    fun rest(): ISeq\n    fun nth(n: Int): MalType\n    fun count(): Int\n    fun slice(fromIndex: Int, toIndex: Int): ISeq\n    fun conj(s: ISeq): ISeq\n}\n\ninterface IMutableSeq : ISeq {\n    fun conj_BANG(form: MalType)\n}\n\nabstract class MalSequence(val elements: MutableList<MalType>) : MalType, IMutableSeq {\n    override var metadata: MalType = NIL\n\n    override fun seq(): Sequence<MalType> = elements.asSequence()\n    override fun first(): MalType = elements.first()\n    override fun nth(n: Int): MalType = elements.elementAt(n)\n    override fun count(): Int = elements.count()\n\n    override fun conj_BANG(form: MalType) {\n        elements.add(form)\n    }\n\n    override fun equals(other: Any?): Boolean =\n            (other is ISeq)\n                    && elements.size == other.count()\n                    && elements.asSequence().zip(other.seq()).all({ it -> it.first == it.second })\n}\n\nclass MalList(elements: MutableList<MalType>) : MalSequence(elements) {\n    constructor() : this(LinkedList<MalType>())\n    constructor(s: ISeq) : this(s.seq().toCollection(LinkedList<MalType>()))\n\n    override fun rest(): ISeq = MalList(elements.drop(1).toCollection(LinkedList<MalType>()))\n\n    override fun slice(fromIndex: Int, toIndex: Int): MalList =\n            MalList(elements.subList(fromIndex, toIndex))\n\n    override fun conj(s: ISeq): ISeq {\n        val list = LinkedList<MalType>(elements)\n        s.seq().forEach({ it -> list.addFirst(it) })\n        return MalList(list)\n    }\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalList(elements)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nclass MalVector(elements: MutableList<MalType>) : MalSequence(elements) {\n    override var metadata: MalType = NIL\n\n    constructor() : this(ArrayList<MalType>())\n    constructor(s: ISeq) : this(s.seq().toCollection(ArrayList<MalType>()))\n\n    override fun rest(): ISeq = MalVector(elements.drop(1).toCollection(ArrayList<MalType>()))\n\n    override fun slice(fromIndex: Int, toIndex: Int): MalVector =\n            MalVector(elements.subList(fromIndex, toIndex))\n\n    override fun conj(s: ISeq): ISeq = MalVector(elements.plus(s.seq()).toCollection(ArrayList<MalType>()))\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalVector(elements)\n        obj.metadata = meta\n        return obj\n    }\n}\n\nclass MalHashMap() : MalType {\n    override var metadata: MalType = NIL\n\n    val elements = HashMap<MalString, MalType>()\n\n    constructor(other: MalHashMap) : this() {\n        other.elements.forEach({ it -> assoc_BANG(it.key, it.value) })\n    }\n\n    fun assoc_BANG(key: MalString, value: MalType) = elements.put(key, value)\n\n    fun dissoc_BANG(key: MalString) {\n        elements.remove(key)\n    }\n\n    override fun with_meta(meta: MalType): MalType {\n        val obj = MalHashMap(this)\n        obj.metadata = meta\n        return obj\n    }\n\n    override fun equals(other: Any?): Boolean =\n            (other is MalHashMap) && elements.equals(other.elements)\n}\n\nclass MalAtom(var value: MalType) : MalType {\n    override var metadata: MalType = NIL\n    override fun with_meta(meta: MalType): MalType = throw UnsupportedOperationException()\n}\n\nval NIL = MalConstant(\"nil\")\nval TRUE = MalConstant(\"true\")\nval FALSE = MalConstant(\"false\")\nval ZERO = MalInteger(0)\n"
  },
  {
    "path": "impls/kotlin/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/latex3/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install texlive-latex-base\n"
  },
  {
    "path": "impls/latex3/Makefile",
    "content": "all:\nclean:\n\trm -f *~ *.aux *.dvi *.log argv\n"
  },
  {
    "path": "impls/latex3/core.sty",
    "content": "\\ProvidesExplPackage {core} {2023/01/01} {0.0.1} {MAL~core~functions}\n\\RequirePackage{types}\n\\RequirePackage{printer}\n\\RequirePackage{reader}\n\n\\cs_new:Nn \\mal_def_builtin:nN\n  { \\prop_put:Nxn \\l_mal_repl_env_prop { y \\tl_to_str:n { #1 } } { b n #2 } }\n\\cs_generate_variant:Nn \\mal_def_builtin:nN { nc }\n\\cs_new:Nn \\mal_def_builtin:nnn\n  {\n    \\cs_new:cn { mal_ #2 :n } { #3 }\n    \\mal_def_builtin:nc { #1 } { mal_ #2 :n }\n  }\n\n% Integer operations\n\n\\cs_new:Nn \\mal_int_op:nnN\n  {\n    % \\iow_term:n {int_op~left=#1~right=#2~operator=#3}\n    \\tl_set:Nx \\l_tmpa_tl\n      { i \\int_eval:n { \\use_none:n #1 #3 \\use_none:n #2 } }\n  }\n\n\\mal_def_builtin:nnn { + } { add } { \\mal_int_op:nnN #1 + }\n\\mal_def_builtin:nnn { - } { sub } { \\mal_int_op:nnN #1 - }\n\\mal_def_builtin:nnn { * } { mul } { \\mal_int_op:nnN #1 * }\n\\mal_def_builtin:nnn { / } { div } { \\mal_int_op:nnN #1 / }\n\n% Integer comparisons\n\n\\cs_new:Nn \\mal_int_comp:nnNnn\n  {\n    \\tl_set:Nx \\l_tmpa_tl\n       { \\int_compare:oNoTF { \\use_none:n #1 } #3 { \\use_none:n #2 } #4 #5 }\n  }\n\n\\mal_def_builtin:nnn { < } { lt} { \\mal_int_comp:nnNnn #1 < { t } { f } }\n\\mal_def_builtin:nnn { > } { gt} { \\mal_int_comp:nnNnn #1 > { t } { f } }\n\\mal_def_builtin:nnn { <= } { le} { \\mal_int_comp:nnNnn #1 > { f } { t } }\n\\mal_def_builtin:nnn { >= } { ge} { \\mal_int_comp:nnNnn #1 < { f } { t } }\n\n% Type tests\n\n\\cs_new:Nn \\mal_type_p:nN\n  {\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_if_head_eq_charcode:nNTF {#1} #2 { t } { f } }\n  }\n\n\\mal_def_builtin:nnn { list? }    { list_p }    { \\mal_type_p:nN #1 l }\n\\mal_def_builtin:nnn { atom? }    { atom_p }    { \\mal_type_p:nN #1 a }\n\\mal_def_builtin:nnn { nil? }     { nil_p }     { \\mal_type_p:nN #1 n }\n\\mal_def_builtin:nnn { true? }    { true_p }    { \\mal_type_p:nN #1 t }\n\\mal_def_builtin:nnn { false? }   { false_p }   { \\mal_type_p:nN #1 f }\n\\mal_def_builtin:nnn { symbol? }  { symbol_p }  { \\mal_type_p:nN #1 y }\n\\mal_def_builtin:nnn { keyword? } { keyword_p } { \\mal_type_p:nN #1 k }\n\\mal_def_builtin:nnn { vector? }  { vector_p }  { \\mal_type_p:nN #1 v }\n\\mal_def_builtin:nnn { map? }     { map_p }     { \\mal_type_p:nN #1 m }\n\\mal_def_builtin:nnn { string? }  { string_p }  { \\mal_type_p:nN #1 s }\n\\mal_def_builtin:nnn { number? }  { number_p }  { \\mal_type_p:nN #1 i }\n\\mal_def_builtin:nnn { macro? }   { macro_p }   { \\mal_type_p:nN #1 c }\n\\mal_def_builtin:nnn { fn? } { fn_p }\n  {\n    \\bool_lazy_or:nnTF\n      { \\tl_if_head_eq_charcode_p:nN #1 b }\n      { \\tl_if_head_eq_charcode_p:nN #1 u }\n      { \\tl_set:Nn \\l_tmpa_tl { t } }\n      { \\tl_set:Nn \\l_tmpa_tl { f } }\n  }\n\\mal_def_builtin:nnn { sequential? } { sequential_p }\n  {\n    \\bool_lazy_or:nnTF\n      { \\tl_if_head_eq_charcode_p:nN #1 l }\n      { \\tl_if_head_eq_charcode_p:nN #1 v }\n      { \\tl_set:Nn \\l_tmpa_tl { t } }\n      { \\tl_set:Nn \\l_tmpa_tl { f } }\n  }\n\n% Other functions, in the order of the process guide.\n\n\\mal_def_builtin:nnn { prn } { prn }\n  {\n    \\iow_term:x { \\mal_printer_tl:nVN {#1} \\c_space_tl \\c_true_bool }\n    \\tl_set:Nn \\l_tmpa_tl { n }\n  }\n\n\\mal_def_builtin:nnn { list } { list } { \\tl_set:Nn \\l_tmpa_tl { l n #1 } }\n\n\\cs_new:Nn \\mal_empty_p_aux:n\n  {\n    \\tl_set:Nx \\l_tmpa_tl\n      { \\tl_if_empty:oTF { \\use_none:nn #1 } { t } { f } }\n  }\n\\mal_def_builtin:nnn { empty? } { empty_p } { \\mal_empty_p_aux:n #1 }\n\n\\cs_new:Nn \\mal_equal_token_lists:nn\n  {\n    % \\iow_term:n {equal_token_lists~#1~#2}\n    \\tl_if_empty:nTF {#1}\n      {\n        \\tl_if_empty:nTF {#2}\n          { \\tl_set:Nn \\l_tmpa_tl { t } }\n          { \\tl_set:Nn \\l_tmpa_tl { f } }\n      }\n      {\n        \\tl_if_empty:nTF {#2}\n          { \\tl_set:Nn \\l_tmpa_tl { f } }\n          {\n            \\mal_equal_form:xx { \\tl_head:n {#1} } { \\tl_head:n {#2} }\n            \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl t\n              {\n                \\mal_equal_token_lists:oo\n                  { \\use_none:n #1 }\n                  { \\use_none:n #2 }\n              }\n              % nothing to do if already false\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_equal_token_lists:nn { oo }\n\n\\cs_new:Nn \\mal_equal_map:nn\n  {\n    \\prop_set_eq:Nc \\l_tmpa_prop { #1 }\n    \\prop_set_eq:Nc \\l_tmpb_prop { #2 }\n    \\prop_remove:Nn \\l_tmpa_prop { __meta__ }\n    \\prop_remove:Nn \\l_tmpb_prop { __meta__ }\n    \\tl_if_eq:xxTF\n      { \\prop_count:N \\l_tmpa_prop }\n      { \\prop_count:N \\l_tmpb_prop }\n      {\n        \\prop_if_empty:NTF \\l_tmpa_prop\n          { \\tl_set:Nn \\l_tmpa_tl { t } }\n          {\n            \\prop_map_inline:Nn \\l_tmpa_prop\n              {\n                \\prop_get:NnNTF \\l_tmpb_prop {##1} \\l_tmpb_tl\n                  {\n                    \\mal_equal_form:Vn \\l_tmpb_tl {##2}\n                    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl f\n                      { \\prop_map_break: }\n                  }\n                  {\n                    \\tl_set:Nn \\l_tmpa_tl { f }\n                    \\prop_map_break:\n                  }\n              }\n              % Finish with true if not interrupted\n          }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl { f } }\n  }\n\n\\cs_new:Nn \\mal_equal_form:nn\n  {\n    % \\iow_term:n {equal_form~#1~#2}\n    \\bool_lazy_or:nnTF\n      { \\tl_if_head_eq_charcode_p:nN {#1} l }\n      { \\tl_if_head_eq_charcode_p:nN {#1} v }\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:nN {#2} l }\n          { \\tl_if_head_eq_charcode_p:nN {#2} v }\n          { \\mal_equal_token_lists:oo { \\use_none:nn #1 } { \\use_none:nn #2 } }\n          { \\tl_set:Nn \\l_tmpa_tl { f } }\n      }\n      {\n        % \\iow_term:n {not~a~sequence}\n        \\tl_if_head_eq_charcode:nNTF {#1} m\n          {\n            \\tl_if_head_eq_charcode:nNTF {#2} m\n              { \\mal_equal_map:nn { #1 } { #2 } }\n              { \\tl_set:Nn \\l_tmpa_tl { f } }\n          }\n          {\n            % \\iow_term:n {neither~a~sequence~nor~a~map}\n            \\str_if_eq:nnTF {#1} {#2}\n              { \\tl_set:Nn \\l_tmpa_tl { t } }\n              { \\tl_set:Nn \\l_tmpa_tl { f } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_equal_form:nn { Vn, xx }\n\n\\mal_def_builtin:nnn { = } { equal_p } { \\mal_equal_form:nn #1 }\n\n\\mal_def_builtin:nnn { count } { count }\n  {\n    \\tl_if_head_eq_charcode:nNTF #1 n\n      { \\tl_set:Nn \\l_tmpa_tl { i 0 } }\n      { \\tl_set:Nx \\l_tmpa_tl { i \\int_eval:n { \\tl_count:n #1 - 2 } } }\n  }\n\n\\mal_def_builtin:nnn { pr-str } { pr_str }\n  {\n    % \\iow_term:n {pr_str~#1}\n    \\tl_set:Nx \\l_tmpa_tl\n      { s \\mal_printer_tl:nVN { #1 } \\c_space_tl \\c_true_bool }\n  }\n\n\\mal_def_builtin:nnn { str } { str }\n  { \\tl_set:Nx \\l_tmpa_tl { s \\mal_printer_tl:nnN { #1 } { } \\c_false_bool } }\n\n\\mal_def_builtin:nnn { println } { println }\n  {\n    \\iow_term:x { \\mal_printer_tl:nVN {#1} \\c_space_tl \\c_false_bool }\n    \\tl_set:Nn \\l_tmpa_tl n\n  }\n\n\\cs_new:Nn \\mal_read_string_aux:n\n  {\n    \\tl_set:No \\l_tmpa_str { \\use_none:n #1 }\n    \\mal_read_str:\n  }\n\\mal_def_builtin:nnn { read-string } { read_string }\n  { \\mal_read_string_aux:n #1 }\n\n\\cs_new:Nn \\mal_slurp_aux:n\n  {\n    \\tl_set:Nn \\l_tmpa_tl { s }\n    \\ior_open:Nx \\g_tmpa_ior { \\use_none:n #1 }\n    \\ior_str_map_inline:Nn \\g_tmpa_ior\n      {\n        \\tl_put_right:Nn \\l_tmpa_tl { ##1 }\n        \\tl_put_right:NV \\l_tmpa_tl \\c_new_line_str\n      }\n    \\ior_close:N \\g_tmpa_ior\n  }\n\\mal_def_builtin:nnn { slurp } { slurp } { \\mal_slurp_aux:n #1 }\n\n\\mal_def_builtin:nnn { atom } { atom }\n  {\n    % \\iow_term:n {atom~#1}\n    \\int_incr:N \\l_mal_object_counter_int\n    \\tl_set:Nx \\l_tmpa_tl { atom_ \\int_use:N \\l_mal_object_counter_int }\n    \\tl_new:c \\l_tmpa_tl\n    \\tl_set:cn \\l_tmpa_tl #1\n  }\n\n\\mal_def_builtin:nnn { deref } { deref } { \\tl_set_eq:Nc \\l_tmpa_tl #1 }\n\n\\cs_new:Nn \\mal_reset_aux:Nn\n  {\n    \\tl_set:Nn #1 { #2 }\n    \\tl_set:Nn \\l_tmpa_tl { #2 }\n  }\n\\cs_generate_variant:Nn \\mal_reset_aux:Nn { cn }\n\\mal_def_builtin:nnn { reset! } { reset } { \\mal_reset_aux:cn #1 }\n\n\\mal_def_builtin:nnn { swap! } { swap }\n  {\n    % \\iow_term:n {swap~#1}\n    \\mal_fn_apply:xx { \\tl_item:nn { #1 }{ 2 } }\n      { { \\exp_not:v { \\tl_head:n { #1 } } } \\exp_not:o { \\use_none:nn #1 } }\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\tl_set_eq:cN { \\tl_head:n { #1 } } \\l_tmpa_tl }\n  }\n\n\\cs_new:Nn \\mal_cons_aux:nn\n  {\n    % \\iow_term:n {cons~#1~#2}\n    \\tl_set:No \\l_tmpa_tl { \\use_none:nn #2 }\n    \\tl_put_left:Nn \\l_tmpa_tl { l n {#1} }\n  }\n\\mal_def_builtin:nnn { cons } { cons } { \\mal_cons_aux:nn #1 }\n\n\\cs_new:Nn \\mal_concat_fn:n { \\use_none:nn #1 }\n\\mal_def_builtin:nnn { concat } { concat }\n  { \\tl_set:Nx \\l_tmpa_tl { l n \\tl_map_function:nN {#1} \\mal_concat_fn:n } }\n\n\\cs_new:Nn \\mal_vec_aux:n\n  {\n    % \\iow_term:n {vec~#1}\n    \\tl_set:No \\l_tmpa_tl { \\use_none:nn #1 }\n    \\tl_put_left:Nn \\l_tmpa_tl { v n }\n  }\n\\mal_def_builtin:nnn { vec } { vec } { \\mal_vec_aux:n #1 }\n\n\\cs_new:Nn \\mal_nth_aux:nn\n  {\n    % \\iow_term:n {nth~#1~#2}\n    \\int_set:Nn \\l_tmpa_int { 3 + \\use_none:n #2 }\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_item:nV {#1} \\l_tmpa_int }\n    \\tl_if_empty:VT \\l_tmpa_tl\n      { \\tl_set:Nx \\l_tmpa_tl { e s \\tl_to_str:n {nth:~index~out~of~range} } }\n  }\n\\mal_def_builtin:nnn { nth } { nth } { \\mal_nth_aux:nn #1 }\n\n\\mal_def_builtin:nnn { first } { first }\n  {\n    % \\iow_term:n {first~#1}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_item:nn #1 {3} }\n    \\tl_if_empty:NT \\l_tmpa_tl\n      { \\tl_set:Nn \\l_tmpa_tl {n} }\n  }\n\n% This returns () for nil (unlike \\use_none:nnn).\n\\mal_def_builtin:nnn { rest } { rest }\n  { \\tl_set:Nx \\l_tmpa_tl { l n \\tl_range:nnn #1 4 {-1} } }\n\n\\mal_def_builtin:nnn { throw } { throw }\n  {\n    % \\iow_term:n {throw~#1}\n    \\tl_set:Nn \\l_tmpa_tl #1\n    \\tl_put_left:Nn \\l_tmpa_tl {e}\n  }\n\n\\mal_def_builtin:nnn { apply } { apply }\n  {\n    % \\iow_term:n {apply~#1}\n    \\tl_set:Nx \\l_tmpb_tl { \\tl_item:nn { #1 } { -1 } } % mal sequence\n    \\mal_fn_apply:xx\n      { \\tl_head:n { #1 } }\n      {\n        \\tl_range:nnn { #1 } { 2 } { -2 }\n        \\tl_range:Vnn \\l_tmpb_tl { 3 } { -1 } % the same as a tl\n      }\n  }\n\n\\cs_new:Nn \\mal_map_rec:nnn\n  {\n    % \\iow_term:n {map~acc=#1~forms=#2~func=#3}\n    \\tl_if_empty:nTF {#2}\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n      {\n        \\mal_fn_apply:nx { #3 } { { \\tl_head:n {#2} } }\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\mal_map_rec:xon\n                { \\exp_not:n {#1} { \\exp_not:V \\l_tmpa_tl } }\n                { \\use_none:n #2 }\n                { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_map_rec:nnn { non, xon }\n\\cs_new:Nn \\mal_map_aux:nn\n  { \\mal_map_rec:non { l n } { \\use_none:nn #2 } { #1 } }\n\\mal_def_builtin:nnn { map } { map } { \\mal_map_aux:nn #1 }\n\n\\cs_new:Nn \\mal_symbol_aux:n { \\tl_set:Nx \\l_tmpa_tl { y \\use_none:n #1 } }\n\\mal_def_builtin:nnn { symbol } { symbol } { \\mal_symbol_aux:n #1 }\n\n\\cs_new:Nn \\mal_keyword_aux:n { \\tl_set:Nx \\l_tmpa_tl { k \\use_none:n #1 } }\n\\mal_def_builtin:nnn { keyword } { keyword } { \\mal_keyword_aux:n #1 }\n\n\\mal_def_builtin:nnn { vector } { vector } { \\tl_set:Nn \\l_tmpa_tl { v n #1 } }\n\n\\mal_def_builtin:nN { hash-map } \\mal_hash_map:n\n\n\\mal_def_builtin:nnn { assoc } { assoc }\n  {\n    % \\iow_term:n {assoc~#1}\n    \\mal_map_new:\n    \\prop_set_eq:cc \\l_tmpa_tl { \\tl_head:n { #1 } }\n    \\mal_assoc_internal:o { \\use_none:n #1 }\n  }\n\n\\mal_def_builtin:nnn { dissoc } { dissoc }\n  {\n    % \\iow_term:n {dissoc~prop=#1~keys=#2}\n    \\mal_map_new:\n    \\prop_set_eq:cc \\l_tmpa_tl { \\tl_head:n { #1 } }\n    \\tl_map_inline:on { \\use_none:n #1 } { \\prop_remove:cn \\l_tmpa_tl { ##1 } }\n  }\n\n\\cs_new:Nn \\mal_get_aux:nn\n  {\n    % \\iow_term:n {get~#1~#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } n\n      { \\tl_set:Nn \\l_tmpa_tl { n } }\n      {\n        \\prop_get:cnNF { #1 } { #2 } \\l_tmpa_tl\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n      }\n  }\n\\mal_def_builtin:nnn { get } { get } { \\mal_get_aux:nn #1 }\n\n\\mal_def_builtin:nnn { contains? } { contains }\n  {\n    % \\iow_term:n {contains?~#1~#2}\n    \\prop_if_in:cnTF #1\n      { \\tl_set:Nn \\l_tmpa_tl { t } }\n      { \\tl_set:Nn \\l_tmpa_tl { f } }\n  }\n\n\\cs_new:Nn \\mal_keys_fn:nn\n  { \\str_if_eq:nnF { #1 } { __meta__ } { \\exp_not:n { { #1 } } } }\n\\mal_def_builtin:nnn { keys } { keys }\n  { \\tl_set:Nx \\l_tmpa_tl { l n \\prop_map_function:cN #1 \\mal_keys_fn:nn } }\n\n\\cs_new:Nn \\mal_vals_fn:nn\n  { \\str_if_eq:nnF { #1 } { __meta__ } { \\exp_not:n { { #2 } } } }\n\\mal_def_builtin:nnn { vals } { vals }\n  { \\tl_set:Nx \\l_tmpa_tl { l n \\prop_map_function:cN #1 \\mal_vals_fn:nn } }\n\n\\mal_def_builtin:nnn { readline } { readline }\n  {\n    % \\iow_term:n {readline:~|#1|}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\str_set:Nx \\l_tmpa_str { \\str_tail:V \\l_tmpa_tl }\n    \\iow_term:V \\l_tmpa_str\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\tl_set:Nx \\l_tmpa_tl { s \\l_tmpa_str }\n  }\n\n% Seconds since the UNIX epoch * on first call to time-ms *.\n\\int_gzero_new:N \\g_mal_first_epoch_int\n\n\\mal_def_builtin:nnn { time-ms } { time_ms }\n  {\n    % Seconds are not accurate enough for MAL tests, so use %s%N.\n    % The raw nanosecond count overflows LaTeX integers.\n    % Even the millisecond count since 2024 overflows.\n    \\iow_term:n {MAL_LATEX3_STRIP_ON}\n    \\sys_get_shell:xnN { date ~ + \\c_percent_str s \\c_percent_str N} {}\n      \\l_tmpa_str\n    \\iow_term:n {MAL_LATEX3_STRIP_OFF}\n    % Extract the digits representing seconds.\n    \\int_set:Nx \\l_tmpa_int { \\tl_range:Vnn \\l_tmpa_str 1 { -10 } }\n    % If this is the first time this function is called,\n    \\int_if_zero:VTF \\g_mal_first_epoch_int {\n      % then store the seconds since the epoch for later use\n      \\int_gset_eq:NN \\g_mal_first_epoch_int \\l_tmpa_int\n      % and return 0 seconds\n      \\int_zero:N \\l_tmpa_int\n    } {\n      % else return the duration in seconds since first call\n      \\int_set:Nn \\l_tmpa_int { \\l_tmpa_int - \\g_mal_first_epoch_int }\n    }\n    % ... in both cases, append the three digits for millisecond.\n    \\tl_set:Nx \\l_tmpa_tl { i \\int_to_arabic:V \\l_tmpa_int\n      \\tl_range:Vnn \\l_tmpa_str { -9 } { -7 } }\n  }\n\n\\mal_def_builtin:nnn { meta } { meta }\n  {\n    % \\iow_term:n {meta~#1}\n    \\tl_if_head_eq_charcode:nNTF #1 m\n      {\n        \\prop_get:cnNF #1 { __meta__ } \\l_tmpa_tl\n          { \\tl_set:Nx \\l_tmpa_tl { n } }\n      }\n      { \\tl_set:Nx \\l_tmpa_tl { \\tl_item:nn #1 { 2 } } }\n  }\n\n\\cs_new:Nn \\mal_with_meta_aux:nn\n  {\n    % \\iow_term:n {with-meta~#1~#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } m\n      {\n        \\mal_map_new:\n        \\prop_set_eq:cc \\l_tmpa_tl { #1 }\n        \\prop_put:cnn \\l_tmpa_tl { __meta__ } { #2 }\n      }\n      {\n        \\tl_set:Nx \\l_tmpa_tl\n          {\n            \\tl_head:n { #1 }\n            \\exp_not:n { { #2 } }\n            \\exp_not:o { \\use_none:nn #1 }\n          }\n      }\n  }\n\\mal_def_builtin:nnn { with-meta } { with_meta } { \\mal_with_meta_aux:nn #1 }\n\n\\cs_new:Nn \\mal_seq_fn:N { { s #1 } }\n\\cs_new:Nn \\mal_seq_aux:n\n  {\n    % \\iow_term:n {seq:~#1}\n    \\exp_args:Nx \\token_case_charcode:Nn { \\tl_head:n {#1} }\n      {\n        n\n        { \\tl_clear:N \\l_tmpa_tl }\n        l\n        { \\tl_set:No \\l_tmpa_tl { \\use_none:nn #1 } }\n        v\n        { \\tl_set:No \\l_tmpa_tl { \\use_none:nn #1 } }\n        s\n        {\n          \\tl_set:Nx \\l_tmpa_tl\n            { \\str_map_function:oN { \\use_none:n #1 } \\mal_seq_fn:N }\n        }\n      }\n    \\tl_if_empty:NTF \\l_tmpa_tl\n      { \\tl_set:Nn \\l_tmpa_tl n }\n      { \\tl_put_left:Nn \\l_tmpa_tl { l n } }\n  }\n\\mal_def_builtin:nnn { seq } { seq } { \\mal_seq_aux:n #1 }\n\n\\mal_def_builtin:nnn { conj } { conj }\n  {\n    % \\iow_term:n {conj~#1}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\tl_set:Nx \\l_tmpb_tl { \\tl_tail:n {#1} }\n    \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl v\n      {\n        \\tl_set:Nx \\l_tmpa_tl\n          {\n            v n\n            \\tl_range:Vnn \\l_tmpa_tl 3 {-1}\n            \\exp_not:V \\l_tmpb_tl\n          }\n      }\n      {\n        \\tl_set:Nx \\l_tmpa_tl\n          {\n            l n\n            \\tl_reverse:V \\l_tmpb_tl\n            \\tl_range:Vnn \\l_tmpa_tl 3 {-1}\n          }\n      }\n  }\n"
  },
  {
    "path": "impls/latex3/env.sty",
    "content": "\\ProvidesExplPackage {env} {2023/01/01} {0.0.1} {MAL~environments}\n\\RequirePackage{types}\n\n\\prop_new:N \\l_mal_repl_env_prop\n\n% Scratch variable containing the name of an enviromnent as a token\n% list, intended to be used with :c expansion.\n\\tl_new:N \\l_mal_tmp_env_prop\n\n% Note that __outer__ is not a valid key.\n\n% The new environment is returned in \\l_mal_tmp_env_prop.\n\\cs_new:Nn \\mal_env_new:N\n  {\n    % \\iow_term:n {env_new:~outer=#1}\n    \\int_incr:N \\l_mal_object_counter_int\n    \\tl_set:Nx \\l_mal_tmp_env_prop { env_ \\int_use:N \\l_mal_object_counter_int }\n    \\prop_new:c \\l_mal_tmp_env_prop\n    \\prop_put:cnn \\l_mal_tmp_env_prop { __outer__ } { #1 }\n  }\n\n% \\prop_put:Nnn is OK for a single assignment.\n\n% Shortcut for repeated '\\prop_put:cnn \\l_mal_tmp_env_prop' assignments,\n% with special handling of & variable arguments.\n\n\\tl_const:Nx \\c_ampersand_symbol { y \\tl_to_str:n { & } }\n\n\\cs_new:Nn \\mal_env_set_keys_values:nn\n  {\n    % \\iow_term:n {apply_loop:~keys=#1~vals=#2}\n    \\tl_if_empty:nF { #1 }\n      {\n        \\tl_set:Nx \\l_tmpb_tl { \\tl_head:n { #1 } }\n        \\tl_if_eq:NNTF \\l_tmpb_tl \\c_ampersand_symbol\n           { \\prop_put:cxn \\l_mal_tmp_env_prop { \\tl_item:nn { #1 } { 2 } }\n               { l n #2 } }\n          {\n            \\prop_put:cVx \\l_mal_tmp_env_prop \\l_tmpb_tl { \\tl_head:n { #2 } }\n            \\mal_env_set_keys_values:oo { \\use_none:n #1 } { \\use_none:n #2 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_env_set_keys_values:nn { on, oo }\n\n\\cs_new:Nn \\mal_env_get:NnTF\n  {\n    % \\iow_term:n {env_get:~env=#1~key=#2}\n    \\prop_get:NnNTF #1 { #2 } \\l_tmpa_tl\n      { #3 }\n      {\n        \\prop_get:NnNTF #1 { __outer__ } \\l_tmpa_tl\n          { \\exp_args:NV \\mal_env_get:NnTF \\l_tmpa_tl { #2 } { #3 } { #4 } }\n          { #4 }\n      }\n  }\n% This one is useful for macroexpand, but may disappear once it is removed.\n\\cs_generate_variant:Nn \\mal_env_get:NnTF { NxTF }\n\\cs_new:Nn \\mal_env_get:NnT { \\mal_env_get:NnTF #1 { #2 } { #3 } { } }\n\\cs_new:Nn \\mal_env_get:NnF { \\mal_env_get:NnTF #1 { #2 } { } { #3 } }\n\\cs_generate_variant:Nn \\mal_env_get:NnT { NVT }\n"
  },
  {
    "path": "impls/latex3/printer.sty",
    "content": "\\ProvidesExplPackage {printer} {2023/01/01} {0.0.1} {MAL~printer}\n\\RequirePackage{types}\n\n\\str_const:Nx \\c_new_line_str { \\char_generate:nn {10} {12} }\n\n% \\str_map_function:oN { \\use_none:n #1 } skips space characters bug?\n% It does not in core.sty... why?\n% \\str_map_inline does not, but is not expandable.\n\\cs_new:Nn \\mal_printer_string:n\n  {\n    \\tl_if_empty:nF { #1 }\n      {\n        \\tl_if_head_is_space:nTF { #1 }\n          { \\c_space_tl }\n          {\n            \\exp_args:NnV \\tl_if_head_eq_charcode:nNTF { #1 } \\c_new_line_str\n              { \\c_backslash_str \\tl_to_str:n { n } }\n              {\n                \\bool_lazy_or:nnT\n                  { \\tl_if_head_eq_charcode_p:nN { #1 } \" }\n                  { \\exp_args:NnV \\tl_if_head_eq_charcode_p:nN { #1 } \\c_backslash_str }\n                  { \\c_backslash_str }\n                \\tl_head:n { #1 }\n              }\n          }\n        \\mal_printer_string:e { \\str_tail:n { #1 } }\n      }\n  }\n\n\\cs_generate_variant:Nn \\mal_printer_string:n { e, o }\n\n\\cs_new:Nn \\mal_printer_pr_str_flip:Nn { \\mal_printer_pr_str:nN { #2 } #1 }\n\n\\cs_new:Nn \\mal_printer_tl:nnN\n  {\n    % \\iow_term:n {printer_tl~forms=#1~separator=#2~readably=#3}\n    \\tl_if_empty:nF {#1}\n      {\n        \\mal_printer_pr_str:fN { \\tl_head:n { #1 } } #3\n        \\tl_map_tokens:on { \\use_none:n #1 }\n          { #2 \\mal_printer_pr_str_flip:Nn #3 }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_printer_tl:nnN { nVN, oVN, VVN, eVN }\n\n\\cs_new:Nn \\mal_printer_map_fn:nn\n  { \\str_if_eq:nnF { #1 } { __meta__ } { \\exp_not:n { { #1 } { #2 } } } }\n\n\\cs_new:Nn \\mal_printer_pr_str:nN\n  {\n    \\exp_args:Nf \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        n { \\tl_to_str:n { nil } }\n        f { \\tl_to_str:n { false } }\n        t { \\tl_to_str:n { true } }\n\n        i { \\int_to_arabic:o { \\use_none:n #1 } }\n\n        y { \\use_none:n #1 }\n        k { \\c_colon_str \\use_none:n #1 }\n        s\n        {\n          \\bool_if:NTF #2\n            { \" \\mal_printer_string:o { \\use_none:n #1 } \" }\n            { \\use_none:n #1 }\n        }\n\n        l { ( \\mal_printer_tl:oVN { \\use_none:nn #1 } \\c_space_tl #2 ) }\n        v { [ \\mal_printer_tl:oVN { \\use_none:nn #1 } \\c_space_tl #2 ] }\n        m\n        {\n          \\c_left_brace_str\n          \\mal_printer_tl:eVN\n            { \\prop_map_function:cN { #1 } \\mal_printer_map_fn:nn }\n            \\c_space_tl #2\n          \\c_right_brace_str\n        }\n\n        b { \\tl_to_str:n { <builtin~function> } }\n        u { \\tl_to_str:n { <fn*~function> } }\n        c { \\tl_to_str:n { <macro> } }\n        a { \\tl_to_str:n { (atom~ } \\mal_printer_pr_str:vN { #1 } #2 ) }\n        e\n        { \\tl_to_str:n { Error:~ } \\mal_printer_pr_str:oN { \\use_none:n #1 } #2 }\n      }\n      { \\tl_to_str:n { Error:~invalid~print~argument~#1 } }\n  }\n\\cs_generate_variant:Nn \\mal_printer_pr_str:nN { fN, oN, VN, vN }\n\n%% \\mal_printer_pr_str:nN { n }                                       \\c_true_bool\n%% \\mal_printer_pr_str:nN { i 23 }                                    \\c_true_bool\n%% \\mal_printer_pr_str:oN { y \\tl_to_str:n { symbol } }               \\c_true_bool\n%% \\mal_printer_pr_str:oN { k \\tl_to_str:n { keyword } }              \\c_true_bool\n%% \\mal_printer_pr_str:nN { s }                                       \\c_false_bool\n%% \\mal_printer_pr_str:oN { s \\tl_to_str:n { unreadable\"string } }    \\c_false_bool\n%% \\mal_printer_pr_str:nN { l n }                                     \\c_true_bool\n%% \\mal_printer_pr_str:nN { l n n t }                                 \\c_true_bool\n%% \\mal_printer_pr_str:nN { l n { i 1 } { i 2 } }                     \\c_true_bool\n%% \\mal_printer_pr_str:nN { v n { i 1 } { i 2 } }                     \\c_true_bool\n%% \\mal_printer_pr_str:nN { l n { l n { i 1 } { i 2 } } t }           \\c_true_bool\n%% \\mal_printer_pr_str:oN { s \\tl_to_str:n { d \" q } }                \\c_true_bool\n%% \\mal_printer_pr_str:oN { s \\tl_to_str:n { b } \\c_backslash_str \\tl_to_str:n { s } } \\c_true_bool\n%% \\mal_printer_pr_str:oN { s \\tl_to_str:n { n } \\c_new_line_str  \\tl_to_str:n { l } } \\c_true_bool\n\n%% \\tl_set:Nn \\l_tmpa_tl { i 3 }\n%% \\mal_printer_pr_str:nN { a \\l_tmpa_tl }                            \\c_true_bool\n\n%% \\prop_clear:N \\l_tmpa_prop\n%% \\prop_put:Nxn \\l_tmpa_prop { k \\tl_to_str:n {a} } { i 12 }\n%% \\prop_put:Nxn \\l_tmpa_prop { s \\tl_to_str:n {b} } { n }\n%% \\mal_printer_pr_str:xN { m n \\exp_not:V \\l_tmpa_prop }             \\c_true_bool\n"
  },
  {
    "path": "impls/latex3/reader.sty",
    "content": "\\ProvidesExplPackage {reader} {2023/01/01} {0.0.1} {MAL~reader}\n\\RequirePackage{types}\n\n% It would be convenient to output the forms in a list directly, but\n% this would require a fully expandable read_str.  \\prop_set and\n% \\regex_replace_once are too convenient.\n\n% \\l_tmpa_str is used as a common buffer for the remaining input.\n\n% Compile the regular expressions once and for all.\n\\regex_const:Nn \\c_mal_space_regex { ^ (?: \\s | , | ; \\N* \\n )* }\n\\regex_const:Nn \\c_mal_unescape_cr_regex { \\\\ n }\n\\regex_const:Nn \\c_mal_unescape_regex { \\\\ ([^n]) }\n\\regex_const:Nn \\c_mal_number_regex\n  { ^ ( -? \\d+ )                                           (.*) }\n\\regex_const:Nn \\c_mal_symbol_regex\n  { ^   ( [^ \" ' \\( \\) ,   ; @ \\[ \\] ^ ` \\{ \\} \\~ \\s ] + ) (.*) }\n\\regex_const:Nn \\c_mal_keyword_regex\n  { ^ : ( [^ \" ' \\( \\) , : ; @ \\[ \\] ^ ` \\{ \\} \\~ \\s ] + ) (.*) }\n\\regex_const:Nn \\c_mal_string_regex\n  { ^ \" ( (?: [^ \\\\ \"] | \\\\ . )* ) \"                       (.*) }\n\n\\cs_new:Nn \\mal_skip_spaces:\n  { \\regex_replace_once:NnN \\c_mal_space_regex {} \\l_tmpa_str }\n\n\\cs_new:Nn \\mal_skip_char:\n  { \\tl_set:Nx \\l_tmpa_str { \\tl_tail:V \\l_tmpa_str } }\n\n% Read forms until a closing brace #1.\n% Return a tl of MAL forms or an error in \\l_tmpa_tl.\n% accumulator    closing brace\n\\cs_new:Nn \\mal_reader_seq_loop:nN\n  {\n    % \\iow_term:n {reader_seq_loop~#1~#2}\n    \\mal_skip_spaces:\n    \\tl_if_head_eq_charcode:VNTF \\l_tmpa_str #2\n      {\n        \\mal_skip_char:\n        \\tl_set:Nn \\l_tmpa_tl { #1 }\n      }\n      {\n        \\mal_read_str:\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          { \\mal_reader_seq_loop:xN { \\exp_not:n {#1} { \\exp_not:V \\l_tmpa_tl } } #2 }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_reader_seq_loop:nN { xN }\n\n% #1: a token list without leading y\n\\cs_new:Nn \\mal_reader_quote:n\n  {\n    % \\iow_term:n {quote~#1}\n    \\mal_skip_char:\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\tl_set:Nx \\l_tmpa_tl\n          {\n            l n\n            { y \\tl_to_str:n { #1 } }\n            { \\exp_not:V \\l_tmpa_tl } }\n      }\n  }\n\n% The only purpose of this macro is to store #1 during read_str.\n\\cs_new:Nn \\mal_reader_with_meta:n\n  {\n    % \\iow_term:n {with_meta~#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\tl_set:Nx \\l_tmpa_tl {\n                                l n\n                                { y \\tl_to_str:n { with-meta } }\n                                { \\exp_not:V \\l_tmpa_tl }\n                                \\exp_not:n { { #1 } }\n                              }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_reader_with_meta:n { V }\n\n% Input in \\l_tmpa str (modified)\n% Write the MAL form to \\l_tmpa_tl.\n\\cs_new:Nn \\mal_read_str:\n  {\n    % \\iow_term:x {reader_read_str~\\l_tmpa_str}\n    \\mal_skip_spaces:\n    \\str_case_e:nnF { \\str_head:V \\l_tmpa_str }\n      {\n        { ' }\n        { \\mal_reader_quote:n { quote } }\n        { @ }\n        { \\mal_reader_quote:n { deref } }\n        { ` }\n        { \\mal_reader_quote:n { quasiquote } }\n        { ( }\n        {\n          \\mal_skip_char:\n          \\mal_reader_seq_loop:nN { l n } )\n        }\n        { [ }\n          {\n            \\mal_skip_char:\n            \\mal_reader_seq_loop:nN { v n } ]\n        }\n        \\c_left_brace_str\n        {\n          \\mal_skip_char:\n          \\exp_args:NnV \\mal_reader_seq_loop:nN { } \\c_right_brace_str\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            { \\mal_hash_map:V \\l_tmpa_tl }\n        }\n        \\c_tilde_str\n        {\n          \\str_if_eq:xnTF { \\str_item:Vn \\l_tmpa_str 2 } { @ }\n            {\n              \\mal_skip_char:\n              \\mal_reader_quote:n { splice-unquote }\n            }\n            { \\mal_reader_quote:n { unquote } }\n        }\n        { ^ }\n        {\n          \\mal_skip_char:\n          \\mal_read_str:\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            { \\mal_reader_with_meta:V \\l_tmpa_tl }\n        }\n      }\n      {\n        \\regex_extract_once:NVNTF \\c_mal_string_regex \\l_tmpa_str \\l_tmpa_seq\n          {\n            \\seq_get_right:NN \\l_tmpa_seq \\l_tmpa_str\n            \\tl_set:Nx \\l_tmpa_tl { s \\seq_item:Nn \\l_tmpa_seq 2 }\n            \\regex_replace_case_all:nN\n              {\n                \\c_mal_unescape_cr_regex { \\n }\n                \\c_mal_unescape_regex    { \\1 }\n              }\n              \\l_tmpa_tl\n          }\n          {\n            \\regex_extract_once:NVNTF \\c_mal_keyword_regex \\l_tmpa_str \\l_tmpa_seq\n              {\n                \\seq_get_right:NN \\l_tmpa_seq \\l_tmpa_str\n                \\tl_set:Nx \\l_tmpa_tl { k \\seq_item:Nn \\l_tmpa_seq 2 }\n              }\n              {\n                \\regex_extract_once:NVNTF \\c_mal_number_regex \\l_tmpa_str \\l_tmpa_seq\n                  {\n                    \\seq_get_right:NN \\l_tmpa_seq \\l_tmpa_str\n                    \\tl_set:Nx \\l_tmpa_tl { i \\seq_item:Nn \\l_tmpa_seq 2 }\n                  }\n                  {\n                    \\regex_extract_once:NVNTF \\c_mal_symbol_regex \\l_tmpa_str \\l_tmpa_seq\n                      {\n                        \\seq_get_right:NN \\l_tmpa_seq \\l_tmpa_str\n                        \\tl_set:Nx \\l_tmpa_tl { \\seq_item:Nn \\l_tmpa_seq 2 }\n                        \\str_case:NnF \\l_tmpa_tl\n                          {\n                            { nil   } { \\tl_set:Nn \\l_tmpa_tl { n } }\n                            { false } { \\tl_set:Nn \\l_tmpa_tl { f } }\n                            { true  } { \\tl_set:Nn \\l_tmpa_tl { t } }\n                          }\n                          { \\tl_put_left:Nn \\l_tmpa_tl { y } } % catcode is already Ok\n                      }\n                      {\n                        \\tl_set:Nn \\l_tmpa_tl { e s unbalanced~expression }\n                      }\n                  }\n              }\n          }\n      }\n    % \\iow_term:n {__ read_str~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% \\str_set:Nn \\l_tmpa_str { ~,           } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { ~12~a        } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { -12          } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { ab           } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { nil          } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { :ab          } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { \"ab\"w        } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { (,)          } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { (nil~:a)     } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { (nil,[:a])   } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { 'a           } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n% \\str_set:Nn \\l_tmpa_str { ^a~b         } \\mal_read_str: \\iow_term:V \\l_tmpa_tl\n\n% \\str_set:Nx \\l_tmpa_str { \\c_left_brace_str \"a\"~1~:b~2 \\c_right_brace_str }\n% \\mal_read_str:\n% \\iow_term:V \\l_tmpa_tl\n\n% \\str_set:Nx \\l_tmpa_str\n%   {\n%     \\c_left_brace_str\n%       \"a\"~1\n%       ~:b~\\c_left_brace_str\n%             :c~3\n%           \\c_right_brace_str\n%     \\c_right_brace_str\n%   }\n% \\mal_read_str:\n% \\iow_term:V \\l_tmpa_tl\n"
  },
  {
    "path": "impls/latex3/run",
    "content": "#!/bin/sh\nset -Cefu\n\n# LaTeX creates temporary files in the current directory.\ncd $(dirname $0)\n\n# There is no way to directly provide command line arguments to LaTeX,\n# use an intermediate file.\nfor arg; do\n    echo \"$arg\"\ndone >| argv\n\n# max_print_line: prevent TeX from wrapping lines written to the\n# terminal (the default is around 80 columns).\n\n# Shell escapes are necessary for time-ms in core.sty.\n# time-ms also requires to strip the output caused by accessing a subshell.\n\n# Halt on error... should be the default.\n\n# Remove the normal TeX initial and final output. The > characters\n# confuse the test runner, especially in the *ARGV* test.\n\n# There is no way in latex3 to check if the terminal receives an\n# END_OF_FILE character, handle Emergency stop as a normal ending.\n\n# When debugging, set DEBUG=1 to see the actual output.\n\nmax_print_line=1000 \\\nlatex \\\n    -shell-escape \\\n    -halt-on-error \\\n    ${STEP:-stepA_mal}.tex \\\n    | {\n    if [ -n \"${DEBUG:-}\" ]; then\n        cat\n    else\n        sed '\n            1,/^MAL_LATEX3_START_OF_OUTPUT$/              d\n            /^MAL_LATEX3_END_OF_OUTPUT$/,$                d\n            /^MAL_LATEX3_STRIP_ON/,/MAL_LATEX3_STRIP_OFF/ d\n            /^! Emergency stop[.]$/,$                     d\n            '\n    fi\n}\n"
  },
  {
    "path": "impls/latex3/step0_repl.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\iow_term:V \\l_tmpa_str\n          }\n        \\repl_loop:\n      }\n  }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\repl_loop:\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step1_read_print.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\repl_loop:\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step2_eval.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\tl_set:Nx \\l_tmpa_tl\n          { e s \\tl_to_str:n { can~only~apply~functions } }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\tl_clear:N \\l_tmpa_tl\n            \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\prop_get:NnNF #2 { #1 } \\l_tmpa_tl\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { oN, VN, xN }\n\n% REPL\n\n\\prop_new:N \\l_mal_repl_env_prop\n\\cs_new:Nn \\mal_int_op:nnN\n  {\n    \\tl_set:Nx \\l_tmpa_tl\n      { i \\int_eval:n { \\use_none:n #1 #3 \\use_none:n #2 } }\n  }\n\\cs_new:Nn \\mal_add:n { \\mal_int_op:nnN #1 + }\n\\cs_new:Nn \\mal_sub:n { \\mal_int_op:nnN #1 - }\n\\cs_new:Nn \\mal_mul:n { \\mal_int_op:nnN #1 * }\n\\cs_new:Nn \\mal_div:n { \\mal_int_op:nnN #1 / }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y + } { b n \\mal_add:n }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y - } { b n \\mal_sub:n }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y * } { b n \\mal_mul:n }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y / } { b n \\mal_div:n }\n\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\repl_loop:\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step3_env.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\tl_set:Nx \\l_tmpa_tl\n          { e s \\tl_to_str:n { can~only~apply~functions } }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\tl_clear:N \\l_tmpa_tl\n            \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\mal_int_op:nnN\n  {\n    \\tl_set:Nx \\l_tmpa_tl\n      { i \\int_eval:n { \\use_none:n #1 #3 \\use_none:n #2 } }\n  }\n\\cs_new:Nn \\mal_add:n { \\mal_int_op:nnN #1 + }\n\\cs_new:Nn \\mal_sub:n { \\mal_int_op:nnN #1 - }\n\\cs_new:Nn \\mal_mul:n { \\mal_int_op:nnN #1 * }\n\\cs_new:Nn \\mal_div:n { \\mal_int_op:nnN #1 / }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y + } { b n \\mal_add:n }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y - } { b n \\mal_sub:n }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y * } { b n \\mal_mul:n }\n\\prop_put:Nnn \\l_mal_repl_env_prop { y / } { b n \\mal_div:n }\n\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\repl_loop:\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step4_if_fn_do.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\usepackage{core}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% Step 4\n\n\\tl_const:Nx \\c_if_symbol { y \\tl_to_str:n { if } }\n\\tl_const:Nx \\c_do_symbol { y \\tl_to_str:n { do } }\n\\tl_const:Nx \\c_fn_symbol { y \\tl_to_str:n { fn* } }\n\n\\cs_new:Nn \\mal_eval_if:nnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~env=#4}\n    \\mal_eval:nN {#2} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n          { \\mal_eval:nN {#3} #4 }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_if:nnnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~else=#4~env=#5}\n    \\mal_eval:nN {#2} #5\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\mal_eval:nN { #4 } #5 }\n          { \\mal_eval:nN { #3 } #5 }\n      }\n  }\n\n\\cs_new:Nn \\mal_fn:nnnN\n  {\n    % \\iow_term:n {fn*~params=#2~implem=#3~env=#4}\n    \\tl_set:Nx \\l_tmpa_tl { \\exp_not:n { u n { #3 } #4 } \\use_none:nn #2 }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } u\n          {\n            \\exp_args:Nx \\mal_env_new:N { \\tl_item:nn { #1 } { 4 } }\n            \\mal_env_set_keys_values:on { \\use_none:nnnn #1 } { #2 }\n            \\mal_eval:xc { \\tl_item:nn { #1 } { 3 } } \\l_mal_tmp_env_prop\n          }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { e s \\tl_to_str:n { can~only~apply~functions } }\n          }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_if_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nnn #1 }\n           { \\mal_eval_if:nnnN #1 #2 }\n           { \\mal_eval_if:nnnnN #1 #2 }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_do_symbol }\n        {\n          \\tl_map_inline:on { \\use_none:n #1 }\n            {\n              \\mal_eval:nN { ##1 } #2\n              \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e { \\tl_map_break: }\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_fn_symbol }\n        { \\mal_fn:nnnN #1 #2 }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\tl_clear:N \\l_tmpa_tl\n            \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n\\cs_new:Nn \\mal_re:n\n  {\n    % \\iow_term:n {re:~#1}\n    \\str_set:Nn \\l_tmpa_str {#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      {\n        \\iow_term:n {error~during~startup~#1}\n        \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n        Trigger a missing begin document error\n      }\n  }\n\n\\mal_re:n { (def!~not~(fn*~(a)~(if~a~false~true))) }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\repl_loop:\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step6_file.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\usepackage{core}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% Step 4\n\n\\tl_const:Nx \\c_if_symbol { y \\tl_to_str:n { if } }\n\\tl_const:Nx \\c_do_symbol { y \\tl_to_str:n { do } }\n\\tl_const:Nx \\c_fn_symbol { y \\tl_to_str:n { fn* } }\n\n\\cs_new:Nn \\mal_eval_if:nnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~env=#4}\n    \\mal_eval:nN {#2} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n          { \\mal_eval:nN {#3} #4 }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_if:nnnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~else=#4~env=#5}\n    \\mal_eval:nN {#2} #5\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\mal_eval:nN { #4 } #5 }\n          { \\mal_eval:nN { #3 } #5 }\n      }\n  }\n\n\\cs_new:Nn \\mal_fn:nnnN\n  {\n    % \\iow_term:n {fn*~params=#2~implem=#3~env=#4}\n    \\tl_set:Nx \\l_tmpa_tl { \\exp_not:n { u n { #3 } #4 } \\use_none:nn #2 }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } u\n          {\n            \\exp_args:Nx \\mal_env_new:N { \\tl_item:nn { #1 } { 4 } }\n            \\mal_env_set_keys_values:on { \\use_none:nnnn #1 } { #2 }\n            \\mal_eval:xc { \\tl_item:nn { #1 } { 3 } } \\l_mal_tmp_env_prop\n          }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { e s \\tl_to_str:n { can~only~apply~functions } }\n          }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_if_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nnn #1 }\n           { \\mal_eval_if:nnnN #1 #2 }\n           { \\mal_eval_if:nnnnN #1 #2 }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_do_symbol }\n        {\n          \\tl_map_inline:on { \\use_none:n #1 }\n            {\n              \\mal_eval:nN { ##1 } #2\n              \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e { \\tl_map_break: }\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_fn_symbol }\n        { \\mal_fn:nnnN #1 #2 }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\tl_clear:N \\l_tmpa_tl\n            \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n\\cs_new:Nn \\mal_re:n\n  {\n    % \\iow_term:n {re:~#1}\n    \\str_set:Nn \\l_tmpa_str {#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      {\n        \\iow_term:n {error~during~startup~#1}\n        \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n        Trigger a missing begin document error\n      }\n  }\n\\cs_generate_variant:Nn \\mal_re:n { x }\n\n\\mal_re:n { (def!~not~(fn*~(a)~(if~a~false~true))) }\n\\mal_re:x { (def!~load-file~(fn*~(f)\n  ~(eval~(read-string~(str~\"(do~\"~(slurp~f)~\"\\c_backslash_str nnil)\")))))\n}\n\n\\mal_def_builtin:nnn { eval } { eval_builtin }\n  { \\mal_eval:nN #1 \\l_mal_repl_env_prop }\n\n\\tl_clear:N \\l_tmpa_tl\n\\ior_open:Nn \\g_tmpa_ior {argv}\n\\ior_str_map_inline:Nn \\g_tmpa_ior {\n  \\tl_put_right:Nn \\l_tmpa_tl { { s #1 } }\n}\n\\ior_close:N \\g_tmpa_ior\n\\prop_put:Nxx \\l_mal_repl_env_prop { y \\tl_to_str:n { *ARGV* } }\n  { l n \\tl_tail:V \\l_tmpa_tl }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\tl_if_empty:NTF \\l_tmpa_tl {\n  \\repl_loop:\n} {\n  \\tl_set:Nx \\l_tmpa_tl { \\tl_head:V \\l_tmpa_tl }\n  \\mal_re:x { (load-file~\" \\tl_tail:V \\l_tmpa_tl \") } % without initial s\n}\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step7_quote.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\usepackage{core}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% Step 4\n\n\\tl_const:Nx \\c_if_symbol { y \\tl_to_str:n { if } }\n\\tl_const:Nx \\c_do_symbol { y \\tl_to_str:n { do } }\n\\tl_const:Nx \\c_fn_symbol { y \\tl_to_str:n { fn* } }\n\n\\cs_new:Nn \\mal_eval_if:nnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~env=#4}\n    \\mal_eval:nN {#2} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n          { \\mal_eval:nN {#3} #4 }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_if:nnnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~else=#4~env=#5}\n    \\mal_eval:nN {#2} #5\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\mal_eval:nN { #4 } #5 }\n          { \\mal_eval:nN { #3 } #5 }\n      }\n  }\n\n\\cs_new:Nn \\mal_fn:nnnN\n  {\n    % \\iow_term:n {fn*~params=#2~implem=#3~env=#4}\n    \\tl_set:Nx \\l_tmpa_tl { \\exp_not:n { u n { #3 } #4 } \\use_none:nn #2 }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% Step 7\n\n\\tl_const:Nx \\c_quote_symbol { y \\tl_to_str:n { quote } }\n\\tl_const:Nx \\c_quasiquote_symbol { y \\tl_to_str:n { quasiquote } }\n\\tl_const:Nx \\c_splice_unquote_symbol { y \\tl_to_str:n { splice-unquote } }\n\\tl_const:Nx \\c_unquote_symbol { y \\tl_to_str:n { unquote } }\n\n\\cs_new:Nn \\mal_quasiquote_item:n\n   {\n    \\bool_lazy_and:nnTF\n      { \\tl_if_head_eq_charcode_p:nN { #1 } l }\n      { \\str_if_eq_p:eV { \\tl_item:nn { #1 } { 3 } } \\c_splice_unquote_symbol }\n      { { y \\tl_to_str:n { concat } } { \\exp_not:o { \\use_iv:nnnn #1 } } }\n      { { y \\tl_to_str:n { cons } } { \\mal_quasiquote:n { #1 } } }\n  }\n\\cs_generate_variant:Nn \\mal_quasiquote_item:n { e }\n\n\\cs_new:Nn \\mal_qq_loop:n\n  {\n    l n\n    \\tl_if_empty:nF {#1}\n      {\n        \\mal_quasiquote_item:e { \\tl_head:n { #1 } }\n        { \\mal_qq_loop:o { \\use_none:n #1 } }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_qq_loop:n { o }\n\n\\cs_new:Nn \\mal_quasiquote:n\n  {\n    \\tl_if_head_eq_charcode:nNTF { #1 } l\n      {\n        \\str_if_eq:eVTF { \\tl_item:nn { #1 } 3 } \\c_unquote_symbol\n          { \\exp_not:o { \\use_iv:nnnn #1 } }\n          { \\mal_qq_loop:o { \\use_none:nn #1 } }\n      }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } v\n          {\n            l n { y \\tl_to_str:n { vec } }\n            { \\mal_qq_loop:o { \\use_none:nn #1 } }\n          }\n          {\n            \\bool_lazy_or:nnTF\n              { \\tl_if_head_eq_charcode_p:nN { #1 } m }\n              { \\tl_if_head_eq_charcode_p:nN { #1 } y }\n              { l n { \\c_quote_symbol } { \\exp_not:n { #1 } } }\n              { \\exp_not:n { #1 } }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_quasiquote:nn { \\mal_quasiquote:n { #2 } }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } u\n          {\n            \\exp_args:Nx \\mal_env_new:N { \\tl_item:nn { #1 } { 4 } }\n            \\mal_env_set_keys_values:on { \\use_none:nnnn #1 } { #2 }\n            \\mal_eval:xc { \\tl_item:nn { #1 } { 3 } } \\l_mal_tmp_env_prop\n          }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { e s \\tl_to_str:n { can~only~apply~functions } }\n          }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_if_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nnn #1 }\n           { \\mal_eval_if:nnnN #1 #2 }\n           { \\mal_eval_if:nnnnN #1 #2 }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_do_symbol }\n        {\n          \\tl_map_inline:on { \\use_none:n #1 }\n            {\n              \\mal_eval:nN { ##1 } #2\n              \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e { \\tl_map_break: }\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_fn_symbol }\n        { \\mal_fn:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quote_symbol }\n        { \\tl_set:No \\l_tmpa_tl { \\use_ii:nn #1 } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quasiquote_symbol }\n        { \\mal_eval:xN { \\mal_eval_quasiquote:nn #1 } #2 }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\tl_clear:N \\l_tmpa_tl\n            \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n\\cs_new:Nn \\mal_re:n\n  {\n    % \\iow_term:n {re:~#1}\n    \\str_set:Nn \\l_tmpa_str {#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      {\n        \\iow_term:n {error~during~startup~#1}\n        \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n        Trigger a missing begin document error\n      }\n  }\n\\cs_generate_variant:Nn \\mal_re:n { x }\n\n\\mal_re:n { (def!~not~(fn*~(a)~(if~a~false~true))) }\n\\mal_re:x { (def!~load-file~(fn*~(f)\n  ~(eval~(read-string~(str~\"(do~\"~(slurp~f)~\"\\c_backslash_str nnil)\")))))\n}\n\n\\mal_def_builtin:nnn { eval } { eval_builtin }\n  { \\mal_eval:nN #1 \\l_mal_repl_env_prop }\n\n\\tl_clear:N \\l_tmpa_tl\n\\ior_open:Nn \\g_tmpa_ior {argv}\n\\ior_str_map_inline:Nn \\g_tmpa_ior {\n  \\tl_put_right:Nn \\l_tmpa_tl { { s #1 } }\n}\n\\ior_close:N \\g_tmpa_ior\n\\prop_put:Nxx \\l_mal_repl_env_prop { y \\tl_to_str:n { *ARGV* } }\n  { l n \\tl_tail:V \\l_tmpa_tl }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\tl_if_empty:NTF \\l_tmpa_tl {\n  \\repl_loop:\n} {\n  \\tl_set:Nx \\l_tmpa_tl { \\tl_head:V \\l_tmpa_tl }\n  \\mal_re:x { (load-file~\" \\tl_tail:V \\l_tmpa_tl \") } % without initial s\n}\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step8_macros.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\usepackage{core}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% Step 4\n\n\\tl_const:Nx \\c_if_symbol { y \\tl_to_str:n { if } }\n\\tl_const:Nx \\c_do_symbol { y \\tl_to_str:n { do } }\n\\tl_const:Nx \\c_fn_symbol { y \\tl_to_str:n { fn* } }\n\n\\cs_new:Nn \\mal_eval_if:nnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~env=#4}\n    \\mal_eval:nN {#2} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n          { \\mal_eval:nN {#3} #4 }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_if:nnnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~else=#4~env=#5}\n    \\mal_eval:nN {#2} #5\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\mal_eval:nN { #4 } #5 }\n          { \\mal_eval:nN { #3 } #5 }\n      }\n  }\n\n\\cs_new:Nn \\mal_fn:nnnN\n  {\n    % \\iow_term:n {fn*~params=#2~implem=#3~env=#4}\n    \\tl_set:Nx \\l_tmpa_tl { \\exp_not:n { u n { #3 } #4 } \\use_none:nn #2 }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% Step 7\n\n\\tl_const:Nx \\c_quote_symbol { y \\tl_to_str:n { quote } }\n\\tl_const:Nx \\c_quasiquote_symbol { y \\tl_to_str:n { quasiquote } }\n\\tl_const:Nx \\c_splice_unquote_symbol { y \\tl_to_str:n { splice-unquote } }\n\\tl_const:Nx \\c_unquote_symbol { y \\tl_to_str:n { unquote } }\n\n\\cs_new:Nn \\mal_quasiquote_item:n\n   {\n    \\bool_lazy_and:nnTF\n      { \\tl_if_head_eq_charcode_p:nN { #1 } l }\n      { \\str_if_eq_p:eV { \\tl_item:nn { #1 } { 3 } } \\c_splice_unquote_symbol }\n      { { y \\tl_to_str:n { concat } } { \\exp_not:o { \\use_iv:nnnn #1 } } }\n      { { y \\tl_to_str:n { cons } } { \\mal_quasiquote:n { #1 } } }\n  }\n\\cs_generate_variant:Nn \\mal_quasiquote_item:n { e }\n\n\\cs_new:Nn \\mal_qq_loop:n\n  {\n    l n\n    \\tl_if_empty:nF {#1}\n      {\n        \\mal_quasiquote_item:e { \\tl_head:n { #1 } }\n        { \\mal_qq_loop:o { \\use_none:n #1 } }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_qq_loop:n { o }\n\n\\cs_new:Nn \\mal_quasiquote:n\n  {\n    \\tl_if_head_eq_charcode:nNTF { #1 } l\n      {\n        \\str_if_eq:eVTF { \\tl_item:nn { #1 } 3 } \\c_unquote_symbol\n          { \\exp_not:o { \\use_iv:nnnn #1 } }\n          { \\mal_qq_loop:o { \\use_none:nn #1 } }\n      }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } v\n          {\n            l n { y \\tl_to_str:n { vec } }\n            { \\mal_qq_loop:o { \\use_none:nn #1 } }\n          }\n          {\n            \\bool_lazy_or:nnTF\n              { \\tl_if_head_eq_charcode_p:nN { #1 } m }\n              { \\tl_if_head_eq_charcode_p:nN { #1 } y }\n              { l n { \\c_quote_symbol } { \\exp_not:n { #1 } } }\n              { \\exp_not:n { #1 } }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_quasiquote:nn { \\mal_quasiquote:n { #2 } }\n\n% Step 8\n\n\\tl_const:Nx \\c_defmacro_symbol { y \\tl_to_str:n { defmacro! } }\n\n\\cs_new:Nn \\mal_eval_defmacro:nnnN\n  {\n    % \\iow_term:n {defmacro~#2~#3~#4}\n    \\mal_eval:nN {#3} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\tl_set:Nx \\l_tmpa_tl { c n \\tl_range:Vnn \\l_tmpa_tl { 3 } { -1 } }\n        \\prop_put:NnV #4 {#2} \\l_tmpa_tl\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:nN { #1 } u }\n          { \\tl_if_head_eq_charcode_p:nN { #1 } c }\n          {\n            \\exp_args:Nx \\mal_env_new:N { \\tl_item:nn { #1 } { 4 } }\n            \\mal_env_set_keys_values:on { \\use_none:nnnn #1 } { #2 }\n            \\mal_eval:xc { \\tl_item:nn { #1 } { 3 } } \\l_mal_tmp_env_prop\n          }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { e s \\tl_to_str:n { can~only~apply~functions } }\n          }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_if_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nnn #1 }\n           { \\mal_eval_if:nnnN #1 #2 }\n           { \\mal_eval_if:nnnnN #1 #2 }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_do_symbol }\n        {\n          \\tl_map_inline:on { \\use_none:n #1 }\n            {\n              \\mal_eval:nN { ##1 } #2\n              \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e { \\tl_map_break: }\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_fn_symbol }\n        { \\mal_fn:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quote_symbol }\n        { \\tl_set:No \\l_tmpa_tl { \\use_ii:nn #1 } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quasiquote_symbol }\n        { \\mal_eval:xN { \\mal_eval_quasiquote:nn #1 } #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_defmacro_symbol }\n        { \\mal_eval_defmacro:nnnN #1 #2 }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl c\n              {\n                \\mal_fn_apply:Vo \\l_tmpa_tl { \\use_none:n #1 }\n                \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n                  { \\mal_eval:VN \\l_tmpa_tl #2 }\n              }\n              {\n                \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n                \\tl_clear:N \\l_tmpa_tl\n                \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n                \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n                \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n                  { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n              }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n\\cs_new:Nn \\mal_re:n\n  {\n    % \\iow_term:n {re:~#1}\n    \\str_set:Nn \\l_tmpa_str {#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      {\n        \\iow_term:n {error~during~startup~#1}\n        \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n        Trigger a missing begin document error\n      }\n  }\n\\cs_generate_variant:Nn \\mal_re:n { x }\n\n\\mal_re:n { (def!~not~(fn*~(a)~(if~a~false~true))) }\n\\mal_re:x { (def!~load-file~(fn*~(f)\n  ~(eval~(read-string~(str~\"(do~\"~(slurp~f)~\"\\c_backslash_str nnil)\")))))\n}\n\\mal_re:n { (defmacro!~cond~(fn*~(&~xs)\n  ~(if~(>~(count~xs)~0)~(list~'if~(first~xs)~(if~(>~(count~xs)~1)\n  ~(nth~xs~1)~(throw~\"odd~number~of~forms~to~cond\"))\n  ~(cons~'cond~(rest~(rest~xs))))))) }\n\n\\mal_def_builtin:nnn { eval } { eval_builtin }\n  { \\mal_eval:nN #1 \\l_mal_repl_env_prop }\n\n\\tl_clear:N \\l_tmpa_tl\n\\ior_open:Nn \\g_tmpa_ior {argv}\n\\ior_str_map_inline:Nn \\g_tmpa_ior {\n  \\tl_put_right:Nn \\l_tmpa_tl { { s #1 } }\n}\n\\ior_close:N \\g_tmpa_ior\n\\prop_put:Nxx \\l_mal_repl_env_prop { y \\tl_to_str:n { *ARGV* } }\n  { l n \\tl_tail:V \\l_tmpa_tl }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\tl_if_empty:NTF \\l_tmpa_tl {\n  \\repl_loop:\n} {\n  \\tl_set:Nx \\l_tmpa_tl { \\tl_head:V \\l_tmpa_tl }\n  \\mal_re:x { (load-file~\" \\tl_tail:V \\l_tmpa_tl \") } % without initial s\n}\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/step9_try.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\usepackage{core}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% Step 4\n\n\\tl_const:Nx \\c_if_symbol { y \\tl_to_str:n { if } }\n\\tl_const:Nx \\c_do_symbol { y \\tl_to_str:n { do } }\n\\tl_const:Nx \\c_fn_symbol { y \\tl_to_str:n { fn* } }\n\n\\cs_new:Nn \\mal_eval_if:nnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~env=#4}\n    \\mal_eval:nN {#2} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n          { \\mal_eval:nN {#3} #4 }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_if:nnnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~else=#4~env=#5}\n    \\mal_eval:nN {#2} #5\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\mal_eval:nN { #4 } #5 }\n          { \\mal_eval:nN { #3 } #5 }\n      }\n  }\n\n\\cs_new:Nn \\mal_fn:nnnN\n  {\n    % \\iow_term:n {fn*~params=#2~implem=#3~env=#4}\n    \\tl_set:Nx \\l_tmpa_tl { \\exp_not:n { u n { #3 } #4 } \\use_none:nn #2 }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% Step 7\n\n\\tl_const:Nx \\c_quote_symbol { y \\tl_to_str:n { quote } }\n\\tl_const:Nx \\c_quasiquote_symbol { y \\tl_to_str:n { quasiquote } }\n\\tl_const:Nx \\c_splice_unquote_symbol { y \\tl_to_str:n { splice-unquote } }\n\\tl_const:Nx \\c_unquote_symbol { y \\tl_to_str:n { unquote } }\n\n\\cs_new:Nn \\mal_quasiquote_item:n\n   {\n    \\bool_lazy_and:nnTF\n      { \\tl_if_head_eq_charcode_p:nN { #1 } l }\n      { \\str_if_eq_p:eV { \\tl_item:nn { #1 } { 3 } } \\c_splice_unquote_symbol }\n      { { y \\tl_to_str:n { concat } } { \\exp_not:o { \\use_iv:nnnn #1 } } }\n      { { y \\tl_to_str:n { cons } } { \\mal_quasiquote:n { #1 } } }\n  }\n\\cs_generate_variant:Nn \\mal_quasiquote_item:n { e }\n\n\\cs_new:Nn \\mal_qq_loop:n\n  {\n    l n\n    \\tl_if_empty:nF {#1}\n      {\n        \\mal_quasiquote_item:e { \\tl_head:n { #1 } }\n        { \\mal_qq_loop:o { \\use_none:n #1 } }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_qq_loop:n { o }\n\n\\cs_new:Nn \\mal_quasiquote:n\n  {\n    \\tl_if_head_eq_charcode:nNTF { #1 } l\n      {\n        \\str_if_eq:eVTF { \\tl_item:nn { #1 } 3 } \\c_unquote_symbol\n          { \\exp_not:o { \\use_iv:nnnn #1 } }\n          { \\mal_qq_loop:o { \\use_none:nn #1 } }\n      }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } v\n          {\n            l n { y \\tl_to_str:n { vec } }\n            { \\mal_qq_loop:o { \\use_none:nn #1 } }\n          }\n          {\n            \\bool_lazy_or:nnTF\n              { \\tl_if_head_eq_charcode_p:nN { #1 } m }\n              { \\tl_if_head_eq_charcode_p:nN { #1 } y }\n              { l n { \\c_quote_symbol } { \\exp_not:n { #1 } } }\n              { \\exp_not:n { #1 } }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_quasiquote:nn { \\mal_quasiquote:n { #2 } }\n\n% Step 8\n\n\\tl_const:Nx \\c_defmacro_symbol { y \\tl_to_str:n { defmacro! } }\n\n\\cs_new:Nn \\mal_eval_defmacro:nnnN\n  {\n    % \\iow_term:n {defmacro~#2~#3~#4}\n    \\mal_eval:nN {#3} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\tl_set:Nx \\l_tmpa_tl { c n \\tl_range:Vnn \\l_tmpa_tl { 3 } { -1 } }\n        \\prop_put:NnV #4 {#2} \\l_tmpa_tl\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% Step 9\n\n\\tl_const:Nx \\c_try_symbol { y \\tl_to_str:n { try* } }\n\n\\cs_new:Nn \\mal_eval_catch:nnnnnnN\n  {\n    % \\iow_term:n {catch~exception=#1~l=#2~meta=#3~catch*=#4~symbol=#5~handler=#6~env=#7}\n    \\mal_env_new:N #7\n    \\prop_put:cno \\l_mal_tmp_env_prop { #5 } { \\use_none:n #1 }\n    \\mal_eval:nc { #6 } \\l_mal_tmp_env_prop\n  }\n\\cs_generate_variant:Nn \\mal_eval_catch:nnnnnnN { VnnnnnN }\n\n\\cs_new:Nn \\mal_eval_try:nnnN\n  {\n    % \\iow_term:n {try~try*=#1~tested=#2~catch_list=#3~env=#4}\n    \\mal_eval:nN { #2 } #4\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      { \\mal_eval_catch:VnnnnnN \\l_tmpa_tl #3 #4 }\n  }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:nN { #1 } u }\n          { \\tl_if_head_eq_charcode_p:nN { #1 } c }\n          {\n            \\exp_args:Nx \\mal_env_new:N { \\tl_item:nn { #1 } { 4 } }\n            \\mal_env_set_keys_values:on { \\use_none:nnnn #1 } { #2 }\n            \\mal_eval:xc { \\tl_item:nn { #1 } { 3 } } \\l_mal_tmp_env_prop\n          }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { e s \\tl_to_str:n { can~only~apply~functions } }\n          }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_if_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nnn #1 }\n           { \\mal_eval_if:nnnN #1 #2 }\n           { \\mal_eval_if:nnnnN #1 #2 }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_do_symbol }\n        {\n          \\tl_map_inline:on { \\use_none:n #1 }\n            {\n              \\mal_eval:nN { ##1 } #2\n              \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e { \\tl_map_break: }\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_fn_symbol }\n        { \\mal_fn:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quote_symbol }\n        { \\tl_set:No \\l_tmpa_tl { \\use_ii:nn #1 } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quasiquote_symbol }\n        { \\mal_eval:xN { \\mal_eval_quasiquote:nn #1 } #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_defmacro_symbol }\n        { \\mal_eval_defmacro:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_try_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nn #1 }\n            { \\mal_eval:oN { \\use_ii:nn #1 } #2 }\n            { \\mal_eval_try:nnnN #1 #2 }\n        }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl c\n              {\n                \\mal_fn_apply:Vo \\l_tmpa_tl { \\use_none:n #1 }\n                \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n                  { \\mal_eval:VN \\l_tmpa_tl #2 }\n              }\n              {\n                \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n                \\tl_clear:N \\l_tmpa_tl\n                \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n                \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n                \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n                  { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n              }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n\\cs_new:Nn \\mal_re:n\n  {\n    % \\iow_term:n {re:~#1}\n    \\str_set:Nn \\l_tmpa_str {#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      {\n        \\iow_term:n {error~during~startup~#1}\n        \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n        Trigger a missing begin document error\n      }\n  }\n\\cs_generate_variant:Nn \\mal_re:n { x }\n\n\\mal_re:n { (def!~not~(fn*~(a)~(if~a~false~true))) }\n\\mal_re:x { (def!~load-file~(fn*~(f)\n  ~(eval~(read-string~(str~\"(do~\"~(slurp~f)~\"\\c_backslash_str nnil)\")))))\n}\n\\mal_re:n { (defmacro!~cond~(fn*~(&~xs)\n  ~(if~(>~(count~xs)~0)~(list~'if~(first~xs)~(if~(>~(count~xs)~1)\n  ~(nth~xs~1)~(throw~\"odd~number~of~forms~to~cond\"))\n  ~(cons~'cond~(rest~(rest~xs))))))) }\n\n\\mal_def_builtin:nnn { eval } { eval_builtin }\n  { \\mal_eval:nN #1 \\l_mal_repl_env_prop }\n\n\\tl_clear:N \\l_tmpa_tl\n\\ior_open:Nn \\g_tmpa_ior {argv}\n\\ior_str_map_inline:Nn \\g_tmpa_ior {\n  \\tl_put_right:Nn \\l_tmpa_tl { { s #1 } }\n}\n\\ior_close:N \\g_tmpa_ior\n\\prop_put:Nxx \\l_mal_repl_env_prop { y \\tl_to_str:n { *ARGV* } }\n  { l n \\tl_tail:V \\l_tmpa_tl }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\tl_if_empty:NTF \\l_tmpa_tl {\n  \\repl_loop:\n} {\n  \\tl_set:Nx \\l_tmpa_tl { \\tl_head:V \\l_tmpa_tl }\n  \\mal_re:x { (load-file~\" \\tl_tail:V \\l_tmpa_tl \") } % without initial s\n}\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/stepA_mal.tex",
    "content": "\\documentclass{article}\n\\usepackage\n% Uncomment this and \\debug_on below when debugging.\n% [enable-debug]\n  {expl3}\n\\usepackage{types}\n\\usepackage{printer}\n\\usepackage{reader}\n\\usepackage{env}\n\\usepackage{core}\n\\ExplSyntaxOn\n\n% Slow but quite useful.\n% \\debug_on:n { all }\n\n% Step 2\n\n\\cs_new:Nn \\mal_eval_map:nN\n  {\n    % \\iow_term:n {eval_map~ast=#1~env=#2}\n    \\mal_map_new:\n    \\prop_map_inline:cn { #1 }\n      {\n        \\str_if_eq:nnF { ##1 } { __meta__ }\n          {\n            \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n            \\mal_eval:nN { ##2 } #2\n            \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n              { \\prop_map_break: }\n              {\n                \\prop_put:cnV \\l_tmpb_tl { ##1 } \\l_tmpa_tl\n                \\tl_set_eq:NN \\l_tmpa_tl \\l_tmpb_tl\n              }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_iterate_tl:nN\n  {\n    % The evaluated elements are appended to \\l_tmpa_tl.\n    % \\iow_term:n {eval_tl:~forms=#1~env=#2}\n    \\tl_map_inline:nn { #1 }\n      {\n        \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n        \\mal_eval:nN { ##1 } #2\n        \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n        \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl e\n          { \\tl_map_break: }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { \\exp_not:V \\l_tmpb_tl { \\exp_not:V \\l_tmpa_tl } }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_iterate_tl:nN { oN }\n\n% Step 3\n\n\\tl_const:Nx \\c_def_symbol { y \\tl_to_str:n { def! } }\n\\tl_const:Nx \\c_let_symbol { y \\tl_to_str:n { let* } }\n\\tl_const:Nx \\c_debug_eval_symbol { y \\tl_to_str:n { DEBUG-EVAL  } }\n\n\\cs_new:Nn \\mal_eval_let_loop:nNn\n  {\n    % \\iow_term:n {mal_eval_let_loop~binds=#1~env=#2~form=#3}\n    \\tl_if_empty:nTF { #1 }\n      { \\mal_eval:nN { #3 } #2 }\n      {\n        \\mal_eval:xN { \\tl_item:nn { #1 } 2 } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\prop_put:NxV #2 { \\tl_head:n { #1 } } \\l_tmpa_tl\n            \\mal_eval_let_loop:oNn { \\use_none:nn #1 } #2 { #3 }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_let_loop:nNn { ocn, oNn }\n\n\\cs_new:Nn \\mal_eval_let:nnnN\n  {\n    % \\iow_term:n {mal_eval_let~let*=#1~binds=#2~form=#3~env=#4}\n    \\mal_env_new:N #4\n    \\mal_eval_let_loop:ocn { \\use_none:nn #2 } \\l_mal_tmp_env_prop { #3 }\n  }\n\n% Step 4\n\n\\tl_const:Nx \\c_if_symbol { y \\tl_to_str:n { if } }\n\\tl_const:Nx \\c_do_symbol { y \\tl_to_str:n { do } }\n\\tl_const:Nx \\c_fn_symbol { y \\tl_to_str:n { fn* } }\n\n\\cs_new:Nn \\mal_eval_if:nnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~env=#4}\n    \\mal_eval:nN {#2} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\tl_set:Nn \\l_tmpa_tl { n } }\n          { \\mal_eval:nN {#3} #4 }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_if:nnnnN\n  {\n    % \\iow_term:n {if~test=#2~then=#3~else=#4~env=#5}\n    \\mal_eval:nN {#2} #5\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\mal_eval:nN { #4 } #5 }\n          { \\mal_eval:nN { #3 } #5 }\n      }\n  }\n\n\\cs_new:Nn \\mal_fn:nnnN\n  {\n    % \\iow_term:n {fn*~params=#2~implem=#3~env=#4}\n    \\tl_set:Nx \\l_tmpa_tl { \\exp_not:n { u n { #3 } #4 } \\use_none:nn #2 }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% Step 7\n\n\\tl_const:Nx \\c_quote_symbol { y \\tl_to_str:n { quote } }\n\\tl_const:Nx \\c_quasiquote_symbol { y \\tl_to_str:n { quasiquote } }\n\\tl_const:Nx \\c_splice_unquote_symbol { y \\tl_to_str:n { splice-unquote } }\n\\tl_const:Nx \\c_unquote_symbol { y \\tl_to_str:n { unquote } }\n\n\\cs_new:Nn \\mal_quasiquote_item:n\n   {\n    \\bool_lazy_and:nnTF\n      { \\tl_if_head_eq_charcode_p:nN { #1 } l }\n      { \\str_if_eq_p:eV { \\tl_item:nn { #1 } { 3 } } \\c_splice_unquote_symbol }\n      { { y \\tl_to_str:n { concat } } { \\exp_not:o { \\use_iv:nnnn #1 } } }\n      { { y \\tl_to_str:n { cons } } { \\mal_quasiquote:n { #1 } } }\n  }\n\\cs_generate_variant:Nn \\mal_quasiquote_item:n { e }\n\n\\cs_new:Nn \\mal_qq_loop:n\n  {\n    l n\n    \\tl_if_empty:nF {#1}\n      {\n        \\mal_quasiquote_item:e { \\tl_head:n { #1 } }\n        { \\mal_qq_loop:o { \\use_none:n #1 } }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_qq_loop:n { o }\n\n\\cs_new:Nn \\mal_quasiquote:n\n  {\n    \\tl_if_head_eq_charcode:nNTF { #1 } l\n      {\n        \\str_if_eq:eVTF { \\tl_item:nn { #1 } 3 } \\c_unquote_symbol\n          { \\exp_not:o { \\use_iv:nnnn #1 } }\n          { \\mal_qq_loop:o { \\use_none:nn #1 } }\n      }\n      {\n        \\tl_if_head_eq_charcode:nNTF { #1 } v\n          {\n            l n { y \\tl_to_str:n { vec } }\n            { \\mal_qq_loop:o { \\use_none:nn #1 } }\n          }\n          {\n            \\bool_lazy_or:nnTF\n              { \\tl_if_head_eq_charcode_p:nN { #1 } m }\n              { \\tl_if_head_eq_charcode_p:nN { #1 } y }\n              { l n { \\c_quote_symbol } { \\exp_not:n { #1 } } }\n              { \\exp_not:n { #1 } }\n          }\n      }\n  }\n\n\\cs_new:Nn \\mal_eval_quasiquote:nn { \\mal_quasiquote:n { #2 } }\n\n% Step 8\n\n\\tl_const:Nx \\c_defmacro_symbol { y \\tl_to_str:n { defmacro! } }\n\n\\cs_new:Nn \\mal_eval_defmacro:nnnN\n  {\n    % \\iow_term:n {defmacro~#2~#3~#4}\n    \\mal_eval:nN {#3} #4\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      {\n        \\tl_set:Nx \\l_tmpa_tl { c n \\tl_range:Vnn \\l_tmpa_tl { 3 } { -1 } }\n        \\prop_put:NnV #4 {#2} \\l_tmpa_tl\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\n% Step 9\n\n\\tl_const:Nx \\c_try_symbol { y \\tl_to_str:n { try* } }\n\n\\cs_new:Nn \\mal_eval_catch:nnnnnnN\n  {\n    % \\iow_term:n {catch~exception=#1~l=#2~meta=#3~catch*=#4~symbol=#5~handler=#6~env=#7}\n    \\mal_env_new:N #7\n    \\prop_put:cno \\l_mal_tmp_env_prop { #5 } { \\use_none:n #1 }\n    \\mal_eval:nc { #6 } \\l_mal_tmp_env_prop\n  }\n\\cs_generate_variant:Nn \\mal_eval_catch:nnnnnnN { VnnnnnN }\n\n\\cs_new:Nn \\mal_eval_try:nnnN\n  {\n    % \\iow_term:n {try~try*=#1~tested=#2~catch_list=#3~env=#4}\n    \\mal_eval:nN { #2 } #4\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      { \\mal_eval_catch:VnnnnnN \\l_tmpa_tl #3 #4 }\n  }\n\n% EVAL\n\n\\cs_new:Nn \\mal_fn_apply:nn\n  {\n    % \\iow_term:n {fn_apply:~func=#1~args=#2}\n    \\tl_if_head_eq_charcode:nNTF { #1 } b\n      { \\use_none:nn #1 { #2 } }\n      {\n        \\bool_lazy_or:nnTF\n          { \\tl_if_head_eq_charcode_p:nN { #1 } u }\n          { \\tl_if_head_eq_charcode_p:nN { #1 } c }\n          {\n            \\exp_args:Nx \\mal_env_new:N { \\tl_item:nn { #1 } { 4 } }\n            \\mal_env_set_keys_values:on { \\use_none:nnnn #1 } { #2 }\n            \\mal_eval:xc { \\tl_item:nn { #1 } { 3 } } \\l_mal_tmp_env_prop\n          }\n          {\n            \\tl_set:Nx \\l_tmpa_tl\n              { e s \\tl_to_str:n { can~only~apply~functions } }\n          }\n      }\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_fn_apply:nn { nx, Vo, VV, xx }\n\n\\cs_new:Nn \\mal_eval_list:nN\n  {\n    % \\iow_term:n {eval_mal_list~tl=#1~env=#2}\n    \\tl_set:Nx \\l_tmpa_tl { \\tl_head:n {#1} }\n    \\bool_case_true:nF\n      {\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_empty_tl }\n        { \\tl_set:Nn \\l_tmpa_tl { l n } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_def_symbol }\n        {\n          \\mal_eval:oN { \\use_iii:nnn #1 } #2\n          \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n            {\n              \\tl_set:No \\l_tmpb_tl { \\use_ii:nnn #1 }\n              \\prop_put:NVV #2 \\l_tmpb_tl \\l_tmpa_tl\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_let_symbol }\n        { \\mal_eval_let:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_if_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nnn #1 }\n           { \\mal_eval_if:nnnN #1 #2 }\n           { \\mal_eval_if:nnnnN #1 #2 }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_do_symbol }\n        {\n          \\tl_map_inline:on { \\use_none:n #1 }\n            {\n              \\mal_eval:nN { ##1 } #2\n              \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e { \\tl_map_break: }\n            }\n        }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_fn_symbol }\n        { \\mal_fn:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quote_symbol }\n        { \\tl_set:No \\l_tmpa_tl { \\use_ii:nn #1 } }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_quasiquote_symbol }\n        { \\mal_eval:xN { \\mal_eval_quasiquote:nn #1 } #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_defmacro_symbol }\n        { \\mal_eval_defmacro:nnnN #1 #2 }\n\n        { \\tl_if_eq_p:NN \\l_tmpa_tl \\c_try_symbol }\n        {\n          \\tl_if_empty:oTF { \\use_none:nn #1 }\n            { \\mal_eval:oN { \\use_ii:nn #1 } #2 }\n            { \\mal_eval_try:nnnN #1 #2 }\n        }\n      }\n      {\n        % \\iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}\n        \\mal_eval:xN { \\tl_head:n { #1 } } #2\n        \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n          {\n            \\tl_if_head_eq_charcode:VNTF \\l_tmpa_tl c\n              {\n                \\mal_fn_apply:Vo \\l_tmpa_tl { \\use_none:n #1 }\n                \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n                  { \\mal_eval:VN \\l_tmpa_tl #2 }\n              }\n              {\n                \\seq_push:NV \\l_mal_stack_seq \\l_tmpa_tl\n                \\tl_clear:N \\l_tmpa_tl\n                \\mal_eval_iterate_tl:oN { \\use_none:n #1 } #2\n                \\seq_pop:NN \\l_mal_stack_seq \\l_tmpb_tl\n                \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n                  { \\mal_fn_apply:VV \\l_tmpb_tl \\l_tmpa_tl }\n              }\n          }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_eval_list:nN { oN }\n\n\\cs_new:Nn \\mal_eval:nN\n  {\n    % \\iow_term:n {EVAL:~ast=#1~env=#2}\n    \\mal_env_get:NVT #2 \\c_debug_eval_symbol\n      {\n        \\bool_lazy_or:nnF\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl n }\n          { \\tl_if_head_eq_charcode_p:VN \\l_tmpa_tl f }\n          { \\iow_term:x { EVAL: ~ \\mal_printer_pr_str:nN { #1 } \\c_true_bool } }\n      }\n    \\exp_args:Nx \\token_case_charcode:NnF { \\tl_head:n {#1} }\n      {\n        l\n        { \\mal_eval_list:oN { \\use_none:nn #1 } #2 }\n        y\n        {\n          \\mal_env_get:NnF #2 { #1 }\n            {\n              \\tl_set:Nx \\l_tmpa_tl\n                { e s \\use_none:n #1 \\tl_to_str:n { ~not~found } }\n            }\n        }\n        v\n        {\n          \\tl_set:Nn \\l_tmpa_tl { v n }\n          \\mal_eval_iterate_tl:oN { \\use_none:nn #1 } #2\n        }\n        m\n        { \\mal_eval_map:nN { #1 } #2 }\n      }\n      { \\tl_set:Nn \\l_tmpa_tl {#1} }\n    % \\iow_term:n {EVAL:~ast=#1~returns}\n    % \\iow_term:V \\l_tmpa_tl\n  }\n\\cs_generate_variant:Nn \\mal_eval:nN { nc, oN, VN, xc, xN }\n\n% REPL\n\n\\cs_new:Nn \\repl_loop:\n  {\n    % \\ior_str_get_term is able to display a prompt on the same line,\n    % but this would make ./run far more complex for little benefit.\n    \\iow_term:n {user>~}\n    \\ior_str_get_term:nN {} \\l_tmpa_str\n    \\str_if_eq:VnF \\l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run\n      {\n        % Ignore empty lines, the MAL self-hosting relies on this\n        % *not* triggering an error.\n        \\str_if_eq:VnF \\l_tmpa_str {}\n          {\n            \\mal_read_str:\n            \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n              { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n            \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n          }\n        \\repl_loop:\n      }\n  }\n\n\\cs_new:Nn \\mal_re:n\n  {\n    % \\iow_term:n {re:~#1}\n    \\str_set:Nn \\l_tmpa_str {#1}\n    \\mal_read_str:\n    \\tl_if_head_eq_charcode:VNF \\l_tmpa_tl e\n      { \\mal_eval:VN \\l_tmpa_tl \\l_mal_repl_env_prop }\n    \\tl_if_head_eq_charcode:VNT \\l_tmpa_tl e\n      {\n        \\iow_term:n {error~during~startup~#1}\n        \\iow_term:x { \\mal_printer_pr_str:VN \\l_tmpa_tl \\c_true_bool }\n        Trigger a missing begin document error\n      }\n  }\n\\cs_generate_variant:Nn \\mal_re:n { x }\n\n\\mal_re:n { (def!~not~(fn*~(a)~(if~a~false~true))) }\n\\mal_re:x { (def!~load-file~(fn*~(f)\n  ~(eval~(read-string~(str~\"(do~\"~(slurp~f)~\"\\c_backslash_str nnil)\")))))\n}\n\\mal_re:n { (defmacro!~cond~(fn*~(&~xs)\n  ~(if~(>~(count~xs)~0)~(list~'if~(first~xs)~(if~(>~(count~xs)~1)\n  ~(nth~xs~1)~(throw~\"odd~number~of~forms~to~cond\"))\n  ~(cons~'cond~(rest~(rest~xs))))))) }\n\n\\mal_def_builtin:nnn { eval } { eval_builtin }\n  { \\mal_eval:nN #1 \\l_mal_repl_env_prop }\n\n\\prop_put:Nxx \\l_mal_repl_env_prop { y \\tl_to_str:n { *host-language* } }\n  { s \\tl_to_str:n { LaTeX3 } }\n\n\\tl_clear:N \\l_tmpa_tl\n\\ior_open:Nn \\g_tmpa_ior {argv}\n\\ior_str_map_inline:Nn \\g_tmpa_ior {\n  \\tl_put_right:Nn \\l_tmpa_tl { { s #1 } }\n}\n\\ior_close:N \\g_tmpa_ior\n\\prop_put:Nxx \\l_mal_repl_env_prop { y \\tl_to_str:n { *ARGV* } }\n  { l n \\tl_tail:V \\l_tmpa_tl }\n\n% ./run removes the normal LaTeX output.\n\\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}\n\n\\tl_if_empty:NTF \\l_tmpa_tl {\n  \\mal_re:n { (println (str \"Mal [\" *host-language* \"]\")) }\n  \\repl_loop:\n} {\n  \\tl_set:Nx \\l_tmpa_tl { \\tl_head:V \\l_tmpa_tl }\n  \\mal_re:x { (load-file~\" \\tl_tail:V \\l_tmpa_tl \") } % without initial s\n}\n\n\\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run\n\n\\ExplSyntaxOff\n\\begin{document}\n\\end{document}\n"
  },
  {
    "path": "impls/latex3/types.sty",
    "content": "\\ProvidesExplPackage {types} {2023/01/01} {0.0.1} {MAL~types}\n\n% This file is included almost everywhere, it seems a good place to\n% define the variants we need.\n\n\\cs_generate_variant:Nn \\int_compare:nNnTF { oNoTF }\n\\cs_generate_variant:Nn \\int_const:Nn { NV }\n\\cs_generate_variant:Nn \\int_if_zero:nTF { VTF }\n\\cs_generate_variant:Nn \\int_set:Nn { Nx }\n\\cs_generate_variant:Nn \\int_to_alph:n { V }\n\\cs_generate_variant:Nn \\int_to_arabic:n { o, V }\n\\cs_generate_variant:Nn \\ior_open:Nn {Nx}\n\\cs_generate_variant:Nn \\iow_term:n { x, V }\n\\cs_generate_variant:Nn \\prop_put:Nnn { cxn, Nxn, NxV }\n\\cs_generate_variant:Nn \\regex_extract_once:NnNTF {NVNTF}\n\\cs_generate_variant:Nn \\str_head:n { V }\n\\cs_generate_variant:Nn \\str_if_eq:nnTF { eVTF, xnTF }\n\\cs_generate_variant:Nn \\str_if_eq_p:nn { eV }\n\\cs_generate_variant:Nn \\str_item:nn { Vn }\n\\cs_generate_variant:Nn \\str_map_function:nN { oN }\n\\cs_generate_variant:Nn \\str_map_inline:nn { on }\n\\cs_generate_variant:Nn \\str_set:Nn { Nx }\n\\cs_generate_variant:Nn \\str_tail:n { V}\n\\cs_generate_variant:Nn \\sys_get_shell:nnN { xnN }\n\\cs_generate_variant:Nn \\tl_const:Nn { cx }\n\\cs_generate_variant:Nn \\tl_if_eq:nnTF { xxTF }\n\\cs_generate_variant:Nn \\tl_if_head_eq_charcode:nNF { VNF }\n\\cs_generate_variant:Nn \\tl_if_head_eq_charcode:nNT { VNT }\n\\cs_generate_variant:Nn \\tl_if_head_eq_charcode:nNTF { VNTF }\n\\cs_generate_variant:Nn \\tl_if_head_eq_charcode_p:nN { VN }\n\\cs_generate_variant:Nn \\tl_item:nn { nV }\n\\cs_generate_variant:Nn \\tl_map_inline:nn { on }\n\\cs_generate_variant:Nn \\tl_map_tokens:nn { on }\n\\cs_generate_variant:Nn \\tl_range:nnn { Vnn }\n\\cs_generate_variant:Nn \\tl_tail:n { V }\n\n% A global stack is convenient for storage of local variables during\n% recursive computations.\n\\seq_new:N \\l_mal_stack_seq\n% TeX usually uses local assignments for this, but the number of\n% groups is limited to 255, which is not enough for MAL recursions.\n\n% A mal form is represented by a token list starting with a letter\n% defining the type (this sometimes allows f expansion).\n\n% n                         nil\n% f                         false\n% t                         true\n% y ..                      symbol    the rest is a str\n% s ..                      string    the rest is a str\n% k ..                      keyword   the rest is a str\n% i ..                      number    the rest is a tl/str of digits\n% l meta elt elt..          list\n% v meta elt elt..          vector\n% map_...                   map       \\map_.. is a prop (may contain __meta__)\n% atom_..                   atom      \\atom_.. tl var contains a mal form\n% e ..                      exception the rest is a mal form\n% u meta impl env arg arg.. function  the argument is a tl of mal forms\n% c meta impl env arg arg.. macro     (see function)\n% b n \\mal_..:n             built-in function, expecting a tl of mal forms\n\n% Global counter used to create unique control sequences for atoms (in\n% core.sty) and environments (in env.sty).\n\\int_new:N \\l_mal_object_counter_int\n\n\\cs_new:Nn \\mal_map_new:\n  {\n    \\int_incr:N \\l_mal_object_counter_int\n    \\tl_set:Nx \\l_tmpa_tl { map_ \\int_use:N \\l_mal_object_counter_int }\n    \\prop_new:c \\l_tmpa_tl\n  }\n\n% Put keys and values read from a tl of MAL forms into \\l_tmpa_tl,\n% which must be a prop variable.\n% Defined here because it is used by core.sty and reader.sty.\n\\cs_new:Nn \\mal_assoc_internal:n\n  {\n    % \\iow_term:n {assoc_internal~#1}\n    \\tl_if_empty:nF { #1 }\n      {\n        \\prop_put:cxx \\l_tmpa_tl { \\tl_head:n { #1 } } { \\tl_item:nn { #1 } 2 }\n        \\mal_assoc_internal:o { \\use_none:nn #1 }\n      }\n  }\n\\cs_generate_variant:Nn \\mal_assoc_internal:n { o }\n\n\\cs_new:Nn \\mal_hash_map:n\n  {\n    \\mal_map_new:\n    \\mal_assoc_internal:n { #1 }\n  }\n\\cs_generate_variant:Nn \\mal_hash_map:n { V }\n"
  },
  {
    "path": "impls/lib/README.md",
    "content": "This directory contains general-purpose reusable code that does not\nfit in the process.\n\nThe split in small files is motivated by implementations too limited\nto load a single big file, but MAL has no proper module management.\n\nHowever, here are some guidelines.\n\n- Begin with an one-line ;; short description\n\n- Describe the restrictions on each parameter in comments.\n\n- Define private symbols in hidden environments when possible. If this\n  is not possible, for example for macros, give them a name starting\n  with an underscore.\n\nIf a module provides tests, you may run against an implementation IMPL\nwith these commands.\n```\nmake IMPL^stepA\ncd tests\npython ../runtest.py lib/MODULE.mal ../IMPL/run\n```\n\nUsers and implementors should use the following syntax in order to\nensure that the same file is only loaded once.\n\n```\n(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/foo.mal\")\n(load-file-once \"../lib/bar.mal\")\n```\n"
  },
  {
    "path": "impls/lib/alias-hacks.mal",
    "content": ";; aliases for common clojure names to mal builtins\n;; NOTE: this is a hack\n\n;; Origin: https://github.com/chr15m/frock\n\n; TODO: re-implement as actually useful macros:\n; destructuring, arg checking, etc.\n\n(def! _alias_add_implicit\n  (fn* [special added]\n    (fn* [x & xs]\n      (list special x (cons added xs)))))\n\n(defmacro! let  (_alias_add_implicit 'let* 'do))\n(defmacro! when (_alias_add_implicit 'if   'do))\n(defmacro! def  (_alias_add_implicit 'def! 'do))\n(defmacro! fn   (_alias_add_implicit 'fn*  'do))\n(defmacro! defn (_alias_add_implicit 'def! 'fn))\n\n(def! partial (fn* [pfn & args]\n  (fn* [& args-inner]\n    (apply pfn (concat args args-inner)))))\n"
  },
  {
    "path": "impls/lib/benchmark.mal",
    "content": ";; An alternative approach, to complement perf.mal\n(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/trivial.mal\")   ; gensym inc\n\n(def! benchmark* (fn* [f n results]\n  (if (< 0 n)\n    (let* [start-ms (time-ms)\n           _ (f)\n           end-ms (time-ms)]\n      (benchmark* f (- n 1) (conj results (- end-ms start-ms))))\n    results)))\n\n(defmacro! benchmark (fn* [expr n]\n  `(benchmark* (fn* [] ~expr) ~n [])))\n\n"
  },
  {
    "path": "impls/lib/equality.mal",
    "content": ";; equality.mal\n\n;; This file checks whether the `=` function correctly implements equality of\n;; hash-maps and sequences (lists and vectors).  If not, it redefines the `=`\n;; function with a pure mal (recursive) implementation that only relies on the\n;; native original `=` function for comparing scalars (integers, booleans,\n;; symbols, strings, keywords, atoms, nil).\n\n;; Save the original (native) `=` as scalar-equal?\n(def! scalar-equal? =)\n\n;; A faster `and` macro which doesn't use `=` internally.\n(defmacro! bool-and                    ; boolean\n  (fn* [& xs]                          ; interpreted as logical values\n    (if (empty? xs)\n      true\n      `(if ~(first xs) (bool-and ~@(rest xs)) false))))\n(defmacro! bool-or                     ; boolean\n  (fn* [& xs]                          ; interpreted as logical values\n    (if (empty? xs)\n      false\n      `(if ~(first xs) true (bool-or ~@(rest xs))))))\n\n(def! starts-with?\n  (fn* [a b]\n    (bool-or (empty? a)\n             (bool-and (mal-equal? (first a) (first b))\n                       (starts-with? (rest a) (rest b))))))\n\n(def! hash-map-vals-equal?\n  (fn* [a b map-keys]\n    (bool-or (empty? map-keys)\n             (let* [key (first map-keys)]\n               (bool-and (contains? b key)\n                         (mal-equal? (get a key) (get b key))\n                         (hash-map-vals-equal? a b (rest map-keys)))))))\n\n;; This implements = in pure mal (using only scalar-equal? as native impl)\n(def! mal-equal?\n  (fn* [a b]\n    (cond\n\n      (sequential? a)\n      (bool-and (sequential? b)\n                (scalar-equal? (count a) (count b))\n                (starts-with? a b))\n\n      (map? a)\n      (let* [keys-a (keys a)]\n        (bool-and (map? b)\n                  (scalar-equal? (count keys-a) (count (keys b)))\n                  (hash-map-vals-equal? a b keys-a)))\n\n      true\n      (scalar-equal? a b))))\n\n(def! hash-map-equality-correct?\n  (fn* []\n    (try*\n      (bool-and (= {:a 1} {:a 1})\n                (not (= {:a 1} {:a 1 :b 2})))\n      (catch* _ false))))\n\n(def! sequence-equality-correct?\n  (fn* []\n    (try*\n      (bool-and (= [:a :b] (list :a :b))\n                (not (= [:a :b] [:a :b :c])))\n      (catch* _ false))))\n\n;; If the native `=` implementation doesn't support sequences or hash-maps\n;; correctly, replace it with the pure mal implementation\n(if (not (bool-and (hash-map-equality-correct?)\n                   (sequence-equality-correct?)))\n  (do\n    (def! = mal-equal?)\n    (println \"equality.mal: Replaced = with pure mal implementation\")))\n"
  },
  {
    "path": "impls/lib/load-file-once.mal",
    "content": ";; Like load-file, but will never load the same path twice.\n\n;; This file is normally loaded with `load-file`, so it needs a\n;; different mechanism to neutralize multiple inclusions of\n;; itself. Moreover, the file list should never be reset.\n\n(def! load-file-once\n  (try*\n    load-file-once\n  (catch* _\n    (let* [seen (atom {\"../lib/load-file-once.mal\" nil})]\n      (fn* [filename]\n        (if (not (contains? @seen filename))\n          (do\n            (swap! seen assoc filename nil)\n            (load-file filename))))))))\n"
  },
  {
    "path": "impls/lib/memoize.mal",
    "content": ";; Memoize any function.\n\n;; Implement `memoize` using an atom (`mem`) which holds the memoized results\n;; (hash-map from the arguments to the result). When the function is called,\n;; the hash-map is checked to see if the result for the given argument was already\n;; calculated and stored. If this is the case, it is returned immediately;\n;; otherwise, it is calculated and stored in `mem`.\n\n;; For recursive functions, take care to store the wrapper under the\n;; same name than the original computation with an assignment like\n;; `(def! f (memoize f))`, so that intermediate results are memorized.\n\n;; Adapted from http://clojure.org/atoms\n\n(def! memoize\n  (fn* [f]\n    (let* [mem (atom {})]\n      (fn* [& args]\n        (let* [key (str args)]\n          (if (contains? @mem key)\n            (get @mem key)\n            (let* [ret (apply f args)]\n              (do\n                (swap! mem assoc key ret)\n                ret))))))))\n"
  },
  {
    "path": "impls/lib/perf.mal",
    "content": ";; Mesure performances.\n\n(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/trivial.mal\")   ; gensym inc\n\n;; Evaluate an expression, but report the time spent\n(defmacro! time\n  (fn* (exp)\n    (let* [start (gensym)\n           ret   (gensym)]\n      `(let* (~start (time-ms)\n              ~ret   ~exp)\n        (do\n          (println \"Elapsed time:\" (- (time-ms) ~start) \"msecs\")\n          ~ret)))))\n\n;; Count evaluations of a function during a given time frame.\n(def! run-fn-for\n\n  (let* [\n    run-fn-for* (fn* [fn max-ms acc-ms last-iters]\n      (let* [start (time-ms)\n             _ (fn)\n             elapsed (- (time-ms) start)\n             iters (inc last-iters)\n             new-acc-ms (+ acc-ms elapsed)]\n        ;; (do (prn \"new-acc-ms:\" new-acc-ms \"iters:\" iters))\n        (if (>= new-acc-ms max-ms)\n          last-iters\n          (run-fn-for* fn max-ms new-acc-ms iters))))\n    ]\n\n    (fn* [fn max-secs]\n      ;; fn       : function without parameters\n      ;; max-secs : number (seconds)\n      ;; return   : number (iterations)\n      (do\n        ;; Warm it up first\n        (run-fn-for* fn 1000 0 0)\n        ;; Now do the test\n        (run-fn-for* fn (* 1000 max-secs) 0 0)))))\n"
  },
  {
    "path": "impls/lib/pprint.mal",
    "content": ";; Pretty printer a MAL object.\n\n(def! pprint\n\n  (let* [\n\n    spaces- (fn* [indent]\n      (if (> indent 0)\n        (str \" \" (spaces- (- indent 1)))\n        \"\"))\n\n    pp-seq- (fn* [obj indent]\n      (let* [xindent (+ 1 indent)]\n        (apply str (pp- (first obj) 0)\n                   (map (fn* [x] (str \"\\n\" (spaces- xindent)\n                                      (pp- x xindent)))\n                        (rest obj)))))\n\n    pp-map- (fn* [obj indent]\n      (let* [ks (keys obj)\n             kindent (+ 1 indent)\n             kwidth (count (seq (str (first ks))))\n             vindent (+ 1 (+ kwidth kindent))]\n        (apply str (pp- (first ks) 0)\n                   \" \"\n                   (pp- (get obj (first ks)) 0)\n                   (map (fn* [k] (str \"\\n\" (spaces- kindent)\n                                      (pp- k kindent)\n                                      \" \"\n                                      (pp- (get obj k) vindent)))\n                        (rest ks)))))\n\n    pp- (fn* [obj indent]\n      (cond\n        (list? obj)   (str \"(\" (pp-seq- obj indent) \")\")\n        (vector? obj) (str \"[\" (pp-seq- obj indent) \"]\")\n        (map? obj)    (str \"{\" (pp-map- obj indent) \"}\")\n        :else         (pr-str obj)))\n\n    ]\n\n    (fn* [obj]\n         (println (pp- obj 0)))))\n"
  },
  {
    "path": "impls/lib/protocols.mal",
    "content": ";; A sketch of Clojure-like protocols, implemented in Mal\n\n;; By chouser (Chris Houser)\n;; Original: https://gist.github.com/Chouser/6081ea66d144d13e56fc\n\n;; This function maps a MAL value to a keyword representing its type.\n;; Most applications will override the default with an explicit value\n;; for the `:type` key in the metadata.\n(def! find-type (fn* [obj]\n  (cond\n    (symbol?  obj) :mal/symbol\n    (keyword? obj) :mal/keyword\n    (atom?    obj) :mal/atom\n    (nil?     obj) :mal/nil\n    (true?    obj) :mal/boolean\n    (false?   obj) :mal/boolean\n    (number?  obj) :mal/number\n    (string?  obj) :mal/string\n    (macro?   obj) :mal/macro\n    true\n    (let* [metadata (meta obj)\n           type     (if (map? metadata) (get metadata :type))]\n      (cond\n        (keyword? type) type\n        (list?   obj)   :mal/list\n        (vector? obj)   :mal/vector\n        (map?    obj)   :mal/map\n        (fn?     obj)   :mal/function\n        true            (throw \"unknown MAL value in protocols\"))))))\n\n;; A protocol (abstract class, interface..) is represented by a symbol.\n;; It describes methods (abstract functions, contracts, signals..).\n;; Each method is described by a sequence of two elements.\n;; First, a symbol setting the name of the method.\n;; Second, a vector setting its formal parameters.\n;; The first parameter is required, plays a special role.\n;; It is usually named `this` (`self`..).\n;; For example,\n;;   (defprotocol protocol\n;;     (method1 [this])\n;;     (method2 [this argument]))\n;; can be thought as:\n;;   (def! method1 (fn* [this]) ..)\n;;   (def! method2 (fn* [this argument]) ..)\n;;   (def! protocol ..)\n;; The return value is the new protocol.\n(defmacro! defprotocol (fn* [proto-name & methods]\n  ;; A protocol is an atom mapping a type extending the protocol to\n  ;; another map from method names as keywords to implementations.\n  (let* [\n    drop2 (fn* [args]\n      (if (= 2 (count args))\n        ()\n        (cons (first args) (drop2 (rest args)))))\n    rewrite (fn* [method]\n      (let* [\n        name     (first method)\n        args     (nth method 1)\n        argc     (count args)\n        varargs? (if (<= 2 argc) (= '& (nth args (- argc 2))))\n        dispatch `(get (get @~proto-name\n                            (find-type ~(first args)))\n                       ~(keyword (str name)))\n        body     (if varargs?\n                   `(apply ~dispatch ~@(drop2 args) ~(nth args (- argc 1)))\n                   (cons dispatch args))\n        ]\n        (list 'def! name (list 'fn* args body))))\n    ]\n    `(do\n      ~@(map rewrite methods)\n       (def! ~proto-name (atom {}))))))\n\n;; A type (concrete class..) extends (is a subclass of, implements..)\n;; a protocol when it provides implementations for the required methods.\n;;   (extend type protocol {\n;;     :method1 (fn* [this] ..)\n;;     :method2 (fn* [this arg1 arg2])})\n;; Additionnal protocol/methods pairs are equivalent to successive\n;; calls with the same type.\n;; The return value is `nil`.\n(def! extend (fn* [type proto methods & more]\n  (do\n    (swap! proto assoc type methods)\n    (if (first more)\n      (apply extend type more)))))\n\n;; An object satisfies a protocol when its type extends the protocol,\n;; that is if the required methods can be applied to the object.\n(def! satisfies? (fn* [protocol obj]\n  (contains? @protocol (find-type obj))))\n;; If `(satisfies protocol obj)` with the protocol below\n;; then `(method1 obj)` and `(method2 obj 1 2)`\n;; dispatch to the concrete implementation provided by the exact type.\n;; Should the type evolve, the calling code needs not change.\n"
  },
  {
    "path": "impls/lib/reducers.mal",
    "content": ";; Left and right folds.\n\n;; Left fold (f (.. (f (f init x1) x2) ..) xn)\n(def! reduce\n  (fn* (f init xs)\n    ;; f      : Accumulator Element -> Accumulator\n    ;; init   : Accumulator\n    ;; xs     : sequence of Elements x1 x2 .. xn\n    ;; return : Accumulator\n    (if (empty? xs)\n      init\n      (reduce f (f init (first xs)) (rest xs)))))\n\n;; Right fold (f x1 (f x2 (.. (f xn init)) ..))\n;; The natural implementation for `foldr` is not tail-recursive, and\n;; the one based on `reduce` constructs many intermediate functions, so we\n;; rely on efficient `nth` and `count`.\n(def! foldr\n\n  (let* [\n    rec (fn* [f xs acc index]\n      (if (< index 0)\n        acc\n        (rec f xs (f (nth xs index) acc) (- index 1))))\n    ]\n\n    (fn* [f init xs]\n      ;; f      : Element Accumulator -> Accumulator\n      ;; init   : Accumulator\n      ;; xs     : sequence of Elements x1 x2 .. xn\n      ;; return : Accumulator\n      (rec f xs init (- (count xs) 1)))))\n"
  },
  {
    "path": "impls/lib/test_cascade.mal",
    "content": ";; Iteration on evaluations interpreted as boolean values.\n\n(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/trivial.mal\")   ; gensym\n\n;; `(cond test1 result1 test2 result2 .. testn resultn)`\n;; is rewritten (in the step files) as\n;; `(if test1 result1 (if test2 result2 (.. (if testn resultn nil))))`\n;; It is common that `testn` is `\"else\"`, `:else`, `true` or similar.\n\n;; `(or x1 x2 .. xn x)`\n;; is almost rewritten as\n;; `(if x1 x1 (if x2 x2 (.. (if xn xn x))))`\n;; except that each argument is evaluated at most once.\n;; Without arguments, returns `nil`.\n(defmacro! or (fn* [& xs]\n  (if (< (count xs) 2)\n    (first xs)\n    (let* [r (gensym)]\n      `(let* (~r ~(first xs)) (if ~r ~r (or ~@(rest xs))))))))\n\n;; Conjonction of predicate values (pred x1) and .. and (pred xn)\n;; Evaluate `pred x` for each `x` in turn. Return `false` if a result\n;; is `nil` or `false`, without evaluating the predicate for the\n;; remaining elements.  If all test pass, return `true`.\n(def! every?\n  (fn* (pred xs)\n    ;; pred   : Element -> interpreted as a logical value\n    ;; xs     : sequence of Elements x1 x2 .. xn\n    ;; return : boolean\n    (cond (empty? xs)       true\n          (pred (first xs)) (every? pred (rest xs))\n          true              false)))\n\n;; Disjonction of predicate values (pred x1) or .. (pred xn)\n;; Evaluate `(pred x)` for each `x` in turn. Return the first result\n;; that is neither `nil` nor `false`, without evaluating the predicate\n;; for the remaining elements.  If all tests fail, return nil.\n(def! some\n  (fn* (pred xs)\n    ;; pred   : Element -> interpreted as a logical value\n    ;; xs     : sequence of Elements x1 x2 .. xn\n    ;; return : boolean\n    (if (empty? xs)\n      nil\n      (or (pred (first xs))\n          (some pred (rest xs))))))\n\n;; Search for first evaluation returning `nil` or `false`.\n;; Rewrite `x1 x2 .. xn x` as\n;;   (let* [r1 x1]\n;;     (if r1 test1\n;;       (let* [r2 x2]\n;;         ..\n;;         (if rn\n;;           x\n;;           rn) ..)\n;;       r1))\n;; Without arguments, returns `true`.\n(defmacro! and\n  (fn* (& xs)\n    ;; Arguments and the result are interpreted as boolean values.\n    (cond (empty? xs)      true\n          (= 1 (count xs)) (first xs)\n          true             (let* (condvar (gensym))\n                             `(let* (~condvar ~(first xs))\n                               (if ~condvar (and ~@(rest xs)) ~condvar))))))\n"
  },
  {
    "path": "impls/lib/threading.mal",
    "content": ";; Composition of partially applied functions.\n\n(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/reducers.mal\")  ; reduce\n\n;; Rewrite x (a a1 a2) .. (b b1 b2) as\n;;   (b (.. (a x a1 a2) ..) b1 b2)\n;; If anything else than a list is found were `(a a1 a2)` is expected,\n;; replace it with a list with one element, so that `-> x a` is\n;; equivalent to `-> x (list a)`.\n(defmacro! ->\n  (fn* (x & xs)\n    (reduce _iter-> x xs)))\n\n(def! _iter->\n  (fn* [acc form]\n    (if (list? form)\n      `(~(first form) ~acc ~@(rest form))\n      (list form acc))))\n\n;; Like `->`, but the arguments describe functions that are partially\n;; applied with *left* arguments.  The previous result is inserted at\n;; the *end* of the new argument list.\n;; Rewrite x ((a a1 a2) .. (b b1 b2)) as\n;;   (b b1 b2 (.. (a a1 a2 x) ..)).\n(defmacro! ->>\n  (fn* (x & xs)\n     (reduce _iter->> x xs)))\n\n(def! _iter->>\n  (fn* [acc form]\n    (if (list? form)\n      `(~(first form) ~@(rest form) ~acc)\n      (list form acc))))\n"
  },
  {
    "path": "impls/lib/trivial.mal",
    "content": ";; Trivial but convenient functions.\n\n;; Integer predecessor (number -> number)\n(def! inc (fn* [a] (+ a 1)))\n\n;; Integer predecessor (number -> number)\n(def! dec (fn* (a) (- a 1)))\n\n;; Integer nullity test (number -> boolean)\n(def! zero? (fn* (n) (= 0 n)))\n\n;; Returns the unchanged argument.\n(def! identity (fn* (x) x))\n\n;; Generate a hopefully unique symbol. See section \"Plugging the Leaks\"\n;; of http://www.gigamonkeys.com/book/macros-defining-your-own.html\n(def! gensym\n  (let* [counter (atom 0)]\n    (fn* []\n      (symbol (str \"G__\" (swap! counter inc))))))\n"
  },
  {
    "path": "impls/livescript/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive \\\n    apt-get -y install libreadline-dev libedit-dev livescript npm\n\nENV NPM_CONFIG_CACHE /mal/.npm\n"
  },
  {
    "path": "impls/livescript/Makefile",
    "content": "SOURCES_BASE = reader.ls printer.ls env.ls core.ls utils.ls\nSOURCES_STEPS = step0_repl.ls step1_read_print.ls step2_eval.ls \\\n\tstep4_if_fn_do.ls step5_tco.ls step6_file.ls step7_quote.ls \\\n\tstep8_macros.ls step9_try.ls stepA_mal.ls\nSOURCES = $(SOURCES_BASE) $(SOURCES_STEPS)\n\nBINS = $(SOURCES:%.ls=%.js)\n\nLSC = lsc\n\nall: node_modules $(BINS)\n\nnode_modules:\n\tnpm install\n\n%.js: %.ls node_modules\n\t$(LSC) -d -c $(@:%.js=%.ls)\n\nstep1_read_print.js: utils.js reader.js printer.js\nstep2_eval.js: utils.js reader.js printer.js\nstep3_env.js: utils.js reader.js printer.js env.js\nstep4_if_fn_do.js: utils.js reader.js printer.js env.js core.js\nstep5_tco.js: utils.js reader.js printer.js env.js core.js\nstep6_file.js: utils.js reader.js printer.js env.js core.js\nstep7_quote.js: utils.js reader.js printer.js env.js core.js\nstep8_macros.js: utils.js reader.js printer.js env.js core.js\nstep9_try.js: utils.js reader.js printer.js env.js core.js\nstepA_mal.js: utils.js reader.js printer.js env.js core.js\n\nclean:\n\trm -f $(BINS)\n"
  },
  {
    "path": "impls/livescript/core.ls",
    "content": "\n{\n    zip, map, apply, and-list, join, Obj, concat, all,\n    pairs-to-obj, obj-to-pairs, reject, keys, values,\n    difference, empty, reverse, chars\n} = require 'prelude-ls'\n{pr_str} = require './printer'\n{read_str, list-to-map, map-keyword, keyword-prefix} = require './reader'\nfs = require 'fs'\n{readline} = require './node_readline'\n\n\nexport runtime-error = (msg) -> throw new Error msg\n\nexport unpack-tco = (ast) ->\n    if ast.type == \\tco\n    then ast.eval!\n    else ast\n\nfn = (body) -> {type: \\function, value: body}\nconst-nil = -> {type: \\const, value: \\nil}\nconst-int = (int) -> {type: \\int, value: int}\nconst-bool = (bool) -> {type: \\const, value: if bool then \\true else \\false}\nconst-str = (str) -> {type: \\string, value: str}\n\nlist-or-vector = ({type}) -> type in [\\list \\vector]\n\nare-lists-equal = (equals-fn, a, b) ->\n    if a.length != b.length then false\n    else zip a, b |> map (apply equals-fn) |> and-list\n\ndeep-equals = (a, b) ->\n    if (list-or-vector a) and (list-or-vector b) then\n        are-lists-equal deep-equals, a.value, b.value\n    else if a.type == \\map and b.type == \\map then\n        a-keys = keys a.value\n        b-keys = keys b.value\n        if a-keys.length == b-keys.length and \\\n            empty (difference a-keys, b-keys)\n            #if are-lists-equal (==), a-keys, b-keys\n            a-keys |> map (key) -> [a.value[key], b.value[key]]\n                   |> map (apply deep-equals)\n                   |> and-list\n        else false\n    else if a.type != b.type then false\n    else a.value == b.value\n\n\ncheck-param = (name, idx, test, expected, actual) ->\n    if not test\n        runtime-error \"'#{name}' expected parameter #{idx} \n                       to be #{expected}, got #{actual}\"\n\n\ncheck-type = (name, idx, expected, actual) ->\n    check-param name, idx, expected == actual, expected, actual\n\n\nexport ns = do\n    '+': fn (a, b) -> const-int a.value + b.value\n    '-': fn (a, b) -> const-int a.value - b.value\n    '*': fn (a, b) -> const-int a.value * b.value\n    '/': fn (a, b) -> const-int parseInt (a.value / b.value)\n\n    'list': fn (...list) -> {type: \\list, value: list}\n    'list?': fn (param) -> const-bool param.type == \\list\n\n    'empty?': fn ({type, value}) ->\n        switch type\n        | \\const =>\n            if value == \\nil\n            then const-bool true\n            else runtime-error \"'empty?' is not supported on #{value}\"\n        | \\list, \\vector =>\n            const-bool value.length == 0\n        | \\map =>\n            const-bool Obj.empty value\n        | otherwise =>\n            runtime-error \"'empty?' is not supported on type #{type}\"\n\n    'count': fn ({type, value}) ->\n        switch type\n        | \\const =>\n            if value == \\nil\n            then const-int 0\n            else runtime-error \"'count' is not supported on #{value}\"\n        | \\list, \\vector =>\n            const-int value.length\n        | \\map =>\n            value |> Obj.keys |> (.length) |> const-int\n        | otherwise =>\n            runtime-error \"'count' is not supported on type #{type}\"\n\n    '=': fn (a, b) -> const-bool (deep-equals a, b)\n    '<': fn (a, b) -> const-bool a.value < b.value\n    '>': fn (a, b) -> const-bool a.value > b.value\n    '<=': fn (a, b) -> const-bool a.value <= b.value\n    '>=': fn (a, b) -> const-bool a.value >= b.value\n\n    'pr-str': fn (...params) ->\n        params |> map (p) -> pr_str p, print_readably=true\n               |> join ' '\n               |> const-str\n\n    'str': fn (...params) ->\n        params |> map (p) -> pr_str p, print_readably=false\n               |> join ''\n               |> const-str\n\n    'prn': fn (...params) ->\n        params |> map (p) -> pr_str p, print_readably=true\n               |> join ' '\n               |> console.log\n               |> const-nil\n\n    'println': fn (...params) ->\n        params |> map (p) -> pr_str p, print_readbly=false\n               |> join ' '\n               |> console.log\n               |> const-nil\n\n    'read-string': fn ({type, value}) ->\n        check-type 'read-string', 0, \\string, type\n        read_str value\n\n    'slurp': fn (filename) ->\n        if filename.type != \\string\n            runtime-error \"'slurp' expected the first parameter\n                           to be a string, got a #{filename.type}\"\n\n        const-str <| fs.readFileSync filename.value, 'utf8'\n\n    'atom': fn (value) -> {type: \\atom, value: value}\n    'atom?': fn (atom) -> const-bool atom.type == \\atom\n    'deref': fn (atom) ->\n        check-type 'deref', 0, \\atom, atom.type\n        atom.value\n\n    'reset!': fn (atom, value) ->\n        check-type 'reset!', 0, \\atom, atom.type\n        atom.value = value\n\n    'swap!': fn (atom, fn, ...args) ->\n        check-type 'swap!', 0, \\atom, atom.type\n        if fn.type != \\function\n            runtime-error \"'swap!' expected the second parameter \n                           to be a function, got a #{fn.type}\"\n\n        atom.value = unpack-tco (fn.value.apply @, [atom.value] ++ args)\n\n    'cons': fn (value, list) ->\n        check-param 'cons', 1, (list-or-vector list),\n            'list or vector', list.type\n\n        {type: \\list, value: [value] ++ list.value}\n\n    'concat': fn (...params) ->\n        if not all list-or-vector, params\n            runtime-error \"'concat' expected all parameters to be a list or vector\"\n\n        {type: \\list, value: params |> map (.value) |> concat}\n\n    'vec': fn (sequence) ->\n        check-param 'vec', 0, (list-or-vector sequence),\n            'list or vector', sequence.type\n\n        {type: \\vector, value: sequence.value}\n\n    'nth': fn (list, index) ->\n        check-param 'nth', 0, (list-or-vector list),\n            'list or vector', list.type\n        check-param 'nth', 1, index.type == \\int,\n            'int', index.type\n\n        if index.value < 0 or index.value >= list.value.length\n            runtime-error 'list index out of bounds'\n\n        list.value[index.value]\n\n    'first': fn (list) ->\n        if list.type == \\const and list.value == \\nil\n            return const-nil!\n\n        check-param 'first', 0, (list-or-vector list),\n            'list or vector', list.type\n\n        if list.value.length == 0\n        then const-nil!\n        else list.value[0]\n\n    'rest': fn (list) ->\n        if list.type == \\const and list.value == \\nil\n            return {type: \\list, value: []}\n\n        check-param 'rest', 0, (list-or-vector list),\n            'list or vector', list.type\n\n        {type: \\list, value: list.value.slice 1}\n\n    'throw': fn (value) -> throw value\n\n    'apply': fn (fn, ...params, list) ->\n        check-type 'apply', 0, \\function, fn.type\n        if not list then runtime-error \"apply expected at least two parameters\"\n        check-param 'apply', params.length+1, (list-or-vector list),\n            'list or vector', list.type\n\n        unpack-tco fn.value.apply @, params ++ list.value\n\n    'map': fn (fn, list) ->\n        check-type 'map', 0, \\function, fn.type\n        check-param 'map', 1, (list-or-vector list),\n            'list or vector', list.type\n\n        mapped-list = list.value |> map (value) ->\n            unpack-tco fn.value.apply @, [value]\n\n        {type: \\list, value: mapped-list}\n\n    'nil?': fn (ast) -> const-bool (ast.type == \\const and ast.value == \\nil)\n    'true?': fn (ast) -> const-bool (ast.type == \\const and ast.value == \\true)\n    'false?': fn (ast) -> const-bool (ast.type == \\const and ast.value == \\false)\n    'symbol?': fn (ast) -> const-bool ast.type == \\symbol\n\n    'symbol': fn (str) ->\n        check-type 'symbol', 0, \\string, str.type\n        {type: \\symbol, value: str.value}\n\n    'keyword': fn (str) ->\n        if str.type == \\keyword then return str\n        check-type 'keyword', 0, \\string, str.type\n        {type: \\keyword, value: ':' + str.value}\n\n    'keyword?': fn (ast) -> const-bool ast.type == \\keyword\n\n    'number?': fn (ast) -> const-bool ast.type == \\int\n    'fn?': fn (ast) -> const-bool (ast.type == \\function and not ast.is_macro)\n    'macro?': fn (ast) -> const-bool (ast.type == \\function and ast.is_macro)\n\n    'vector': fn (...params) -> {type: \\vector, value: params}\n    'vector?': fn (ast) -> const-bool ast.type == \\vector\n\n    'hash-map': fn (...params) -> list-to-map params\n\n    'map?': fn (ast) -> const-bool ast.type == \\map\n\n    'assoc': fn (m, ...params) ->\n        check-type 'assoc', 0, \\map, m.type\n\n        # Turn the params into a map, this is kind of hacky.\n        params-map = list-to-map params\n\n        # Copy the map by cloning (prototyping).\n        new-map = ^^m.value\n\n        for k, v of params-map.value\n            new-map[k] = v\n\n        {type: \\map, value: new-map}\n\n    'dissoc': fn (m, ...keys) ->\n        check-type 'dissoc', 0, \\map, m.type\n\n        # Convert keyword to map key strings.\n        str-keys = keys |> map map-keyword\n\n        new-map = m.value\n            |> obj-to-pairs\n            |> reject ([key, value]) -> key in str-keys\n            |> pairs-to-obj\n\n        {type: \\map, value: new-map}\n\n    'get': fn (m, key) ->\n        if m.type == \\const and m.value == \\nil\n        then return const-nil!\n\n        check-type 'get', 0, \\map, m.type\n        str-key = map-keyword key\n        value = m.value[str-key]\n        if value then value else const-nil!\n\n    'contains?': fn (m, key) ->\n        check-type 'contains?', 0, \\map, m.type\n        str-key = map-keyword key\n        const-bool (str-key of m.value)\n\n    'keys': fn (m) ->\n        check-type 'keys', 0, \\map, m.type\n        result = keys m.value |> map (key) ->\n            if key.startsWith keyword-prefix\n            then {type: \\keyword, value: key.substring 1}\n            else {type: \\string, value: key}\n        {type: \\list, value: result}\n\n    'vals': fn (m) ->\n        check-type 'vals', 0, \\map, m.type\n        {type: \\list, value: values m.value}\n\n    'sequential?': fn (ast) -> const-bool list-or-vector ast\n\n    'with-meta': fn (ast, m) ->\n        ast with {meta: m}\n\n    'meta': fn (ast) ->\n        if ast.meta\n        then ast.meta\n        else const-nil!\n\n    'readline': fn (prompt) ->\n        check-type 'readline', 0, \\string, prompt.type\n        result = readline prompt.value\n        if result?\n        then const-str result\n        else const-nil!\n\n    'time-ms': fn ->\n        const-int (new Date).getTime!\n\n    'conj': fn (list, ...params) ->\n        check-param 'conj', 0, (list-or-vector list),\n            'list or vector', list.type\n\n        if list.type == \\list\n            type: \\list\n            value: (reverse params) ++ list.value\n        else\n            type: \\vector\n            value: list.value ++ params\n\n    'string?': fn (ast) -> const-bool ast.type == \\string\n\n    'seq': fn (seq) ->\n        switch seq.type\n        | \\list =>\n            if seq.value.length\n            then seq\n            else const-nil!\n        | \\vector =>\n            if seq.value.length\n            then {type: \\list, value: seq.value}\n            else const-nil!\n        | \\string =>\n            if seq.value.length\n            then {type: \\list, value: chars seq.value |> map const-str}\n            else const-nil!\n        | otherwise =>\n            if seq.type == \\const and seq.value == \\nil\n            then const-nil!\n            else runtime-error \"unsupported type for 'seq': #{seq.type}\"\n"
  },
  {
    "path": "impls/livescript/env.ls",
    "content": "export class Env\n    (outer = null, data = {}) ->\n        @outer = outer\n        @data = data\n\n    set: (symbol, ast) ->\n        @data[symbol] = ast\n\n    get: (symbol) ->\n        if symbol of @data then @data[symbol]\n        else if @outer? then @outer.get symbol\n"
  },
  {
    "path": "impls/livescript/error.ls",
    "content": ""
  },
  {
    "path": "impls/livescript/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline.so\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit.so\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nvar koffi = require('koffi'),\n    fs = require('fs');\n\nvar koffi_rl = koffi.load(RL_LIB)\n\nvar rllib = {\n    readline: koffi_rl.func(\"char *readline(char *prompt)\"),\n    add_history: koffi_rl.func(\"int add_history(char *line)\")\n}\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = typeof prompt !== 'undefined' ? prompt : \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { rllib.add_history(lines[i]); }\n        }\n    }\n\n    var line = rllib.readline(prompt);\n    if (line) {\n        rllib.add_history(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\nvar readline = exports;\n\n"
  },
  {
    "path": "impls/livescript/package.json",
    "content": "{\n  \"name\": \"make-a-lisp\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"koffi\": \"^2.9.0\"\n  }\n}\n"
  },
  {
    "path": "impls/livescript/printer.ls",
    "content": "{is-type, map, join, obj-to-pairs} = require 'prelude-ls'\n{keyword-prefix} = require './reader'\n\n\nexport pr_str = ({type, value}: ast, print_readably=true) ->\n    switch type\n    | \\const    => value\n    | \\int      => value\n    | \\string   =>\n        if print_readably\n        then encode-string value\n        else value\n    | \\symbol   => value\n    | \\keyword  => value\n    | \\list     => '(' + (pr_list value, print_readably) + ')'\n    | \\vector   => '[' + (pr_list value, print_readably) + ']'\n    | \\map      => '{' + (pr_map value, print_readably) + '}'\n    | \\function => '#<function>'\n    | \\atom     => '(atom ' + (pr_str value) + ')'\n\n\nencode-string = (str) ->\n    str |> (.replace /[\\n\\\"\\\\]/g,\n            (ch) -> switch ch\n                | '\\n' => '\\\\n'\n                | '\"' => '\\\\\"'\n                | '\\\\' => '\\\\\\\\')\n        |> (enc) -> \"\\\"#{enc}\\\"\"\n\n\npr_list = (list, print_readably) ->\n    list |> map (ast) -> pr_str ast, print_readably\n         |> join ' '\n\n\npr_map_key = (key, print_readably) ->\n    if key.startsWith keyword-prefix\n        key.substring 1\n    else if print_readably\n        encode-string key\n    else\n        key\n\npr_map = (obj, print_readably) ->\n    obj |> obj-to-pairs\n        |> map ([key, value]) ->\n            key_str = pr_map_key key, print_readably\n            value_str = pr_str value, print_readably\n            key_str + ' ' + value_str\n        |> join ' '\n"
  },
  {
    "path": "impls/livescript/reader.ls",
    "content": "readline = require 'readline'\n{id, map, pairs-to-obj} = require 'prelude-ls'\n{list-to-pairs} = require './utils'\n\nexport class OnlyComment\n\nparse-error = (msg) -> throw new Error msg\n\nclass Reader\n    (tokens) ->\n        @tokens = tokens\n        @pos = 0\n\n    # returns the token at the current position\n    # and increments position.\n    next: ->\n        result = @peek!\n        if result? then @pos += 1\n        result\n\n    # just returns the token at the current position.\n    peek: ->\n        if @pos < @tokens.length\n            @tokens[@pos]\n\n\neof-or-comment = (reader) ->\n    token = reader.peek!\n    if token? and not token.startsWith ';'\n    then parse-error \"expected EOF, got '#{token}'\"\n\n\nexport read_str = (str) ->\n    str\n    |> tokenizer\n    |> (tokens) -> new Reader tokens\n    |> (reader) ->\n        result = read_form reader\n        if token? then parse-error \"expected EOF, got '#{token}'\"\n        result\n\n\n# This function will take a single string and return an array/list \n# of all the tokens (strings) in it.\ntokenizer = (str) ->\n    re = //\n    [\\s,]*                  # whitespace or commas\n    ( ~@                    # special two-char ~@\n    | [\\[\\]{}()'`~^@]       # special single char one of []{}'`~^@\n    | \"(?:\\\\.| [^\\\\\"])*\"?   # double-quoted string\n    | ;.*                   # any seq of chars starting ;\n    | [^\\s\\[\\]{}('\"`,;)]+   # seq of non-special chars: symbols, numbers,\n    )                       # \"true\", \"false\" and \"nil\".\n    //y\n\n    tokens = []\n    while re.lastIndex < str.length\n        idx = re.lastIndex\n        m = re.exec str\n        if not m\n            # Allow whitespace or commas at the end of the input.\n            break if /[\\s,]+/.exec str.substring idx\n            parse-error \"parse error at character #{idx}\"\n\n        tok = m[1]\n        # Ignore comments.\n        if tok[0] != ';' then tokens.push m[1]\n    \n    tokens\n\nread_form = (reader) ->\n    switch reader.peek!\n    | '('  => read_list reader, ')'\n    | '['  => read_list reader, ']'\n    | '{'  => read_list reader, '}'\n    | '\\'' => read-macro 'quote', reader\n    | '\\`' => read-macro 'quasiquote', reader\n    | '~'  => read-macro 'unquote', reader\n    | '~@' => read-macro 'splice-unquote', reader\n    | '@'  => read-macro 'deref', reader # todo only symbol?\n    | '^'  => read-with-meta reader\n    | otherwise =>\n        if that? then read_atom reader\n        else parse-error 'expected a form, got EOF'\n\n\nread_list = (reader, end) ->\n    list = []\n    reader.next! # accept '(', '[' or '{'\n    loop\n        token = reader.peek!\n        if not token?\n            parse-error \"expected '#{end}', got EOF\"\n        else if token == end\n            reader.next!\n            break\n        \n        list.push read_form reader\n\n    switch end\n    | ')' => {type: \\list, value: list}\n    | ']' => {type: \\vector, value: list}\n    | '}' => list-to-map list\n\n\nspecial_chars = '[]{}\\'`~^@'\nconstants = [\\true \\false \\nil]\n\n\nread_atom = (reader) ->\n    token = reader.peek!\n    if token in constants\n        {type: \\const, value: reader.next!}\n    else if token.match /^\"(?:\\\\.|[^\\\\\"])*\"$/\n        {type: \\string, value: decode-string reader.next!}\n    else if token[0] == '\"'\n        parse-error \"expected '\\\"', got EOF\"\n    else if token.match /^-?\\d+$/\n        {type: \\int, value: parseInt reader.next!}\n    else if token != '~@' and token not in special_chars\n        if token.startsWith ':'\n            {type: \\keyword, value: reader.next!}\n        else\n            {type: \\symbol, value: reader.next!}\n    else\n        parse-error \"expected an atom, got #{token}\"\n\n\ndecode-string = (str) ->\n    str |> (.slice 1, -1)\n        |> (.replace /\\\\[\\\"\\\\n]/g,\n            (esc) -> switch esc\n                | '\\\\n' => '\\n'\n                | '\\\\\"' => '\"'\n                | '\\\\\\\\' => '\\\\')\n\n\nexport keyword-prefix = '\\u029e'\n\nexport map-keyword = (key) ->\n    switch key.type\n    | \\string => key.value\n    | \\keyword => keyword-prefix + key.value\n    | otherwise =>\n        parse-error \"#{key.type} can't be a map key\"\n\nexport list-to-map = (list) ->\n    if list.length % 2 != 0\n        parse-error \"map should have an even number \n                     of elements, got #{list.length}\"\n\n    list-to-pairs list\n    |> map ([key, value]) -> [(map-keyword key), value]\n    |> pairs-to-obj\n    |> (obj) -> {type: \\map, value: obj}\n\n\nread-macro = (symbol, reader) ->\n    reader.next! # accept macro start token\n\n    do\n        type: \\list\n        value:\n            * {type: \\symbol, value: symbol}\n            * read_form reader\n\n\nread-with-meta = (reader) ->\n    reader.next! # accept ^\n\n    meta = read_form reader\n    form = read_form reader\n\n    do\n        type: \\list\n        value:\n            * {type: \\symbol, value: 'with-meta'}\n            * form\n            * meta\n"
  },
  {
    "path": "impls/livescript/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/${STEP:-stepA_mal}.js \"${@}\"\n"
  },
  {
    "path": "impls/livescript/step0_repl.ls",
    "content": "readline = require './node_readline'\n{id} = require 'prelude-ls'\n\n\nREAD = id\nEVAL = id\nPRINT = id\n\nrep = (line) -> PRINT EVAL READ line\n\nloop\n  line = readline.readline 'user> '\n  break if not line? or line == ''\n  console.log rep line\n\n# rl = readline.createInterface do\n#     input : process.stdin\n#     output : process.stdout\n#     prompt: 'user> '\n\n# rl.prompt!\n\n# rl.on 'line', (line) ->\n#     console.log rep line\n#     rl.prompt!\n\n# rl.on 'close', ->\n#     process.exit 0\n"
  },
  {
    "path": "impls/livescript/step1_read_print.ls",
    "content": "readline = require './node_readline'\n{id} = require 'prelude-ls'\n{read_str, OnlyComment} = require './reader'\n{pr_str} = require './printer'\n\n\nEVAL = id\n\nrep = (line) -> pr_str EVAL read_str line\n\nloop\n    line = readline.readline 'user> '\n    break if not line? or line == ''\n    try\n        console.log rep line\n    catch {message}: ex\n        if ex not instanceof OnlyComment\n            console.log message\n"
  },
  {
    "path": "impls/livescript/step2_eval.ls",
    "content": "readline = require './node_readline'\n{id, map, Obj} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n\nrepl_env = do\n    '+':\n        type: \\function\n        value: (a, b) -> {type: \\int, value: a.value + b.value}\n    '-': \n        type: \\function\n        value: (a, b) -> {type: \\int, value: a.value - b.value}\n    '*':\n        type: \\function\n        value: (a, b) -> {type: \\int, value: a.value * b.value}\n    '/': \n        type: \\function\n        value: (a, b) -> {type: \\int, value: parseInt(a.value / b.value)}\n\neval_ast = (repl_env, {type, value}: ast) -->\n\n    # console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol =>\n        result = repl_env[value]\n        if not result? then throw new Error 'symbol not found: ', value\n        result\n    | \\list, \\vector =>\n        result = value |> map eval_ast repl_env\n        if type == \\list and result.length != 0\n            fn = result[0]\n            if fn.type != \\function\n                throw new Error fn.value, ' is not a function'\n            fn.value.apply repl_env, result.slice 1\n        else\n            {type: type, value: result}\n    | \\map =>\n        {type: \\map, value: value |> Obj.map eval_ast repl_env}\n    | otherwise =>\n        ast\n\n\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> pr_str\n\nloop\n    line = readline.readline 'user> '\n    break if not line? or line == ''\n    try\n        console.log rep line\n    catch {message}\n        console.error message\n"
  },
  {
    "path": "impls/livescript/step3_env.ls",
    "content": "readline = require './node_readline'\n{id, map, Obj, each} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n\nrepl_env = new Env null, do\n    '+':\n        type: \\function\n        value: (a, b) -> {type: \\int, value: a.value + b.value}\n    '-': \n        type: \\function\n        value: (a, b) -> {type: \\int, value: a.value - b.value}\n    '*':\n        type: \\function\n        value: (a, b) -> {type: \\int, value: a.value * b.value}\n    '/': \n        type: \\function\n        value: (a, b) -> {type: \\int, value: parseInt(a.value / b.value)}\n\n\nis-symbol = ({type, value}: ast, name) -> \n    type == \\symbol and value == name\n\n\nlist-to-pairs = (list) ->\n    [0 to (list.length - 2) by 2] \\\n        |> map (idx) -> [list[idx], list[idx+1]]\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\neval_ast = (env, {type, value}: ast) -->\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return {type: \\vector, value: value |> map eval_ast env}\n    | \\map => return {type: \\map, value: value |> Obj.map eval_ast env}\n    | otherwise => return ast\n\n    if value.length == 0 then ast\n    else if value[0].type == \\symbol\n        params = value[1 to]\n        switch value[0].value\n        | 'def!' => eval_def env, params\n        | 'let*' => eval_let env, params\n        | otherwise => eval_apply env, value\n    else\n        eval_apply env, value\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        throw new Error \"#{name} expected #{expected} parameters, \n                         got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        throw new Error \"expected a symbol for the first parameter \n                         of def!, got a #{name.type}\"\n    \n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        throw new Error \"expected 1st parameter of let* to \n                         be a binding list (or vector), \n                         got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        throw new Error \"binding list of let* must have an even \n                         number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            throw new Error \"expected a symbol as binding name, \n                             got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Evaluate the 'body' of let* with the new environment.\n    eval_ast let_env, params[1]\n\n\neval_apply = (env, list) ->\n    [fn, ...args] = list |> map eval_ast env\n    if fn.type != \\function\n        throw new Error fn.value, ' is not a function'\n    fn.value.apply env, args\n\n\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> pr_str\n\n\nloop\n    line = readline.readline 'user> '\n    break if not line? or line == ''\n    try\n        console.log rep line\n    catch error\n        if error.message\n        then console.error error.message\n        else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/step4_if_fn_do.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns} = require './core'\n{list-to-pairs} = require './utils'\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\neval_ast = (env, {type, value}: ast) -->\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0 then ast\n    else if value[0].type == \\symbol\n        params = value[1 to]\n        switch value[0].value\n        | 'def!' => eval_def env, params\n        | 'let*' => eval_let env, params\n        | 'do'   => eval_do env, params\n        | 'if'   => eval_if env, params\n        | 'fn*'  => eval_fn env, params\n        | otherwise => eval_apply env, value\n    else\n        eval_apply env, value\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Evaluate the 'body' of let* with the new environment.\n    eval_ast let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    params |> map eval_ast env |> last\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        eval_ast env, params[1]\n    else if params.length > 2\n        eval_ast env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, do\n                type: \\list\n                value: values.slice binds.length\n\n        # Evaluate the function body with the new environment.\n        eval_ast fn_env, body\n\n    {type: \\function, value: fn_instance}\n\n\neval_apply = (env, list) ->\n    [fn, ...args] = list |> map eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    fn.value.apply env, args\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\nloop\n    line = readline.readline 'user> '\n    break if not line? or line == ''\n    try\n        console.log rep line\n    catch error\n        if error.message\n        then console.error error.message\n        else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/step5_tco.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns} = require './core'\n{list-to-pairs} = require './utils'\n\n\ndefer-tco = (env, ast) ->\n    type: \\tco\n    env: env\n    ast: ast\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\neval_ast = (env, {type, value}: ast) -->\n  loop\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0\n            return ast\n    else\n\n        result = if value[0].type == \\symbol\n            params = value[1 to]\n            switch value[0].value\n            | 'def!' => eval_def env, params\n            | 'let*' => eval_let env, params\n            | 'do'   => eval_do env, params\n            | 'if'   => eval_if env, params\n            | 'fn*'  => eval_fn env, params\n            | otherwise => eval_apply env, value\n        else \n            eval_apply env, value\n\n        if result.type == \\tco\n            env = result.env\n            {type, value}: ast = result.ast\n        else\n            return result\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Defer evaluation of let* body with TCO.\n    defer-tco let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    [...rest, last-param] = params\n    rest |> each eval_ast env\n    defer-tco env, last-param\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        defer-tco env, params[1]\n    else if params.length > 2\n        defer-tco env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, do\n                type: \\list\n                value: values.slice binds.length\n\n        # Defer evaluation of the function body to TCO.\n        defer-tco fn_env, body\n\n    {type: \\function, value: fn_instance}\n\n\neval_apply = (env, list) ->\n    [fn, ...args] = list |> map eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    fn.value.apply env, args\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\nloop\n    line = readline.readline 'user> '\n    break if not line? or line == ''\n    try\n        console.log rep line\n    catch error\n        if error.message\n        then console.error error.message\n        else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/step6_file.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns} = require './core'\n{list-to-pairs} = require './utils'\n\n\ndefer-tco = (env, ast) ->\n    type: \\tco\n    env: env\n    ast: ast\n    eval: -> eval_ast env, ast\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\neval_ast = (env, {type, value}: ast) -->\n  loop\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0\n            return ast\n    else\n\n        result = if value[0].type == \\symbol\n            params = value[1 to]\n            switch value[0].value\n            | 'def!' => eval_def env, params\n            | 'let*' => eval_let env, params\n            | 'do'   => eval_do env, params\n            | 'if'   => eval_if env, params\n            | 'fn*'  => eval_fn env, params\n            | otherwise => eval_apply env, value\n        else \n            eval_apply env, value\n\n        if result.type == \\tco\n            env = result.env\n            {type, value}: ast = result.ast\n        else\n            return result\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Defer evaluation of let* body with TCO.\n    defer-tco let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    [...rest, last-param] = params\n    rest |> each eval_ast env\n    defer-tco env, last-param\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        defer-tco env, params[1]\n    else if params.length > 2\n        defer-tco env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, do\n                type: \\list\n                value: values.slice binds.length\n\n        # Defer evaluation of the function body to TCO.\n        defer-tco fn_env, body\n\n    {type: \\function, value: fn_instance}\n\n\neval_apply = (env, list) ->\n    [fn, ...args] = list |> map eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    fn.value.apply env, args\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n# Evil eval.\nrepl_env.set 'eval', do\n    type: \\function\n    value: (ast) -> eval_ast repl_env, ast  # or use current env? (@ = this).\n\n\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\n# Define load-file.\nrep '\n(def! load-file \n  (fn* (f)\n    (eval\n      (read-string\n        (str \"(do \" (slurp f) \"\\nnil)\")))))'\n\n# Parse program arguments.\n# The first two (exe and core-file) are, respectively,\n# the interpreter executable (nodejs or lsc) and the\n# source file being executed (stepX_*.(ls|js)).\n[exe, core-file, mal-file, ...argv] = process.argv\n\nrepl_env.set '*ARGV*', do\n    type: \\list\n    value: argv |> map (arg) ->\n        type: \\string\n        value: arg\n\nif mal-file\n    rep \"(load-file \\\"#{mal-file}\\\")\"\nelse\n    # REPL.\n    loop\n        line = readline.readline 'user> '\n        break if not line? or line == ''\n        try\n            console.log rep line\n        catch error\n            if error.message\n            then console.error error.message\n            else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/step7_quote.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns} = require './core'\n{list-to-pairs} = require './utils'\n\n\ndefer-tco = (env, ast) ->\n    type: \\tco\n    env: env\n    ast: ast\n    eval: -> eval_ast env, ast\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\nmake-symbol = (name) -> {type: \\symbol, value: name}\nmake-list = (value) -> {type: \\list, value: value}\nmake-call = (name, params) -> make-list [make-symbol name] ++ params\nis-symbol = (ast, name) -> ast.type == \\symbol and ast.value == name\n\n\neval_ast = (env, {type, value}: ast) -->\n  loop\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0\n            return ast\n    else\n\n        result = if value[0].type == \\symbol\n            params = value[1 to]\n            switch value[0].value\n            | 'def!'       => eval_def env, params\n            | 'let*'       => eval_let env, params\n            | 'do'         => eval_do env, params\n            | 'if'         => eval_if env, params\n            | 'fn*'        => eval_fn env, params\n            | 'quote'      => eval_quote env, params\n            | 'quasiquote' => eval_quasiquote env, params\n            | otherwise    => eval_apply env, value\n        else \n            eval_apply env, value\n\n        if result.type == \\tco\n            env = result.env\n            {type, value}: ast = result.ast\n        else\n            return result\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Defer evaluation of let* body with TCO.\n    defer-tco let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    [...rest, last-param] = params\n    rest |> each eval_ast env\n    defer-tco env, last-param\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        defer-tco env, params[1]\n    else if params.length > 2\n        defer-tco env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, \n                make-list values.slice binds.length\n\n        # Defer evaluation of the function body to TCO.\n        defer-tco fn_env, body\n\n    {type: \\function, value: fn_instance}\n\n\neval_apply = (env, list) ->\n    [fn, ...args] = list |> map eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    fn.value.apply env, args\n\n\neval_quote = (env, params) ->\n    if params.length != 1\n        runtime-error \"quote expected 1 parameter, got #{params.length}\"\n\n    params[0]\n\n\neval_quasiquoteexpand = (params) ->\n    if params.length != 1\n        runtime-error \"quasiquote expected 1 parameter, got #{params.length}\"\n\n    ast = params[0]\n    quasiquote ast\n\n\nquasiquote = (ast) ->\n    if ast.type in [\\symbol, \\map]\n        make-call 'quote', [ast]\n    else if ast.type == \\vector\n        make-call 'vec', [qq_foldr ast.value]\n    else if ast.type != \\list\n        ast\n    else if (ast.value.length == 2) and is-symbol ast.value[0], 'unquote'\n        ast.value[1]\n    else\n        qq_foldr ast.value\n\n\nqq_foldr = (xs) ->\n    result = make-list []\n    for i from xs.length - 1 to 0 by -1\n       result := qq_loop xs[i], result\n    result\n\n\nqq_loop = (elt, acc) ->\n    if elt.type == \\list and \\\n       elt.value.length == 2 and \\\n       is-symbol elt.value[0], 'splice-unquote'\n        make-call 'concat', [\n            elt.value[1]\n            acc\n        ]\n    else\n        make-call 'cons', [\n            quasiquote elt\n            acc\n        ]\n\n\neval_quasiquote = (env, params) ->\n    new-ast = eval_quasiquoteexpand params\n    defer-tco env, new-ast\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n# Evil eval.\nrepl_env.set 'eval', do\n    type: \\function\n    value: (ast) -> eval_ast repl_env, ast  # or use current env? (@ = this).\n\n\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\n# Define load-file.\nrep '\n(def! load-file \n  (fn* (f)\n    (eval\n      (read-string\n        (str \"(do \" (slurp f) \"\\nnil)\")))))'\n\n# Parse program arguments.\n# The first two (exe and core-file) are, respectively,\n# the interpreter executable (nodejs or lsc) and the\n# source file being executed (stepX_*.(ls|js)).\n[exe, core-file, mal-file, ...argv] = process.argv\n\nrepl_env.set '*ARGV*', do\n    type: \\list\n    value: argv |> map (arg) ->\n        type: \\string\n        value: arg\n\nif mal-file\n    rep \"(load-file \\\"#{mal-file}\\\")\"\nelse\n    # REPL.\n    loop\n        line = readline.readline 'user> '\n        break if not line? or line == ''\n        try\n            console.log rep line\n        catch error\n            if error.message\n            then console.error error.message\n            else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/step8_macros.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns, unpack-tco} = require './core'\n{list-to-pairs} = require './utils'\n\n\ndefer-tco = (env, ast) ->\n    type: \\tco\n    env: env\n    ast: ast\n    eval: -> eval_ast env, ast\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\nmake-symbol = (name) -> {type: \\symbol, value: name}\nmake-list = (value) -> {type: \\list, value: value}\nmake-call = (name, params) -> make-list [make-symbol name] ++ params\nis-symbol = (ast, name) -> ast.type == \\symbol and ast.value == name\n\n\neval_ast = (env, {type, value}: ast) -->\n  loop\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0\n            return ast\n    else\n\n        result = if ast.value[0].type == \\symbol\n            params = ast.value[1 to]\n            switch ast.value[0].value\n            | 'def!'        => eval_def env, params\n            | 'let*'        => eval_let env, params\n            | 'do'          => eval_do env, params\n            | 'if'          => eval_if env, params\n            | 'fn*'         => eval_fn env, params\n            | 'quote'       => eval_quote env, params\n            | 'quasiquote'  => eval_quasiquote env, params\n            | 'defmacro!'   => eval_defmacro env, params\n            | otherwise     => eval_apply env, ast.value\n        else \n            eval_apply env, ast.value\n\n        if result.type == \\tco\n            env = result.env\n            {type, value}: ast = result.ast\n        else\n            return result\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Defer evaluation of let* body with TCO.\n    defer-tco let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    [...rest, last-param] = params\n    rest |> each eval_ast env\n    defer-tco env, last-param\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        defer-tco env, params[1]\n    else if params.length > 2\n        defer-tco env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, \n                make-list values.slice binds.length\n\n        # Defer evaluation of the function body to TCO.\n        defer-tco fn_env, body\n\n    {type: \\function, value: fn_instance, is_macro: false}\n\n\neval_apply = (env, list) ->\n    [first, ...raw_args] = list\n    fn = first |> eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    if fn.is_macro\n        return (defer-tco env, (unpack-tco (fn.value.apply env, raw_args)))\n    args = raw_args |> map eval_ast env\n    fn.value.apply env, args\n\n\neval_quote = (env, params) ->\n    if params.length != 1\n        runtime-error \"quote expected 1 parameter, got #{params.length}\"\n\n    params[0]\n\n\neval_quasiquoteexpand = (params) ->\n    if params.length != 1\n        runtime-error \"quasiquote expected 1 parameter, got #{params.length}\"\n\n    ast = params[0]\n    quasiquote ast\n\n\nquasiquote = (ast) ->\n    if ast.type in [\\symbol, \\map]\n        make-call 'quote', [ast]\n    else if ast.type == \\vector\n        make-call 'vec', [qq_foldr ast.value]\n    else if ast.type != \\list\n        ast\n    else if (ast.value.length == 2) and is-symbol ast.value[0], 'unquote'\n        ast.value[1]\n    else\n        qq_foldr ast.value\n\n\nqq_foldr = (xs) ->\n    result = make-list []\n    for i from xs.length - 1 to 0 by -1\n       result := qq_loop xs[i], result\n    result\n\n\nqq_loop = (elt, acc) ->\n    if elt.type == \\list and \\\n       elt.value.length == 2 and \\\n       is-symbol elt.value[0], 'splice-unquote'\n        make-call 'concat', [\n            elt.value[1]\n            acc\n        ]\n    else\n        make-call 'cons', [\n            quasiquote elt\n            acc\n        ]\n\n\neval_quasiquote = (env, params) ->\n    new-ast = eval_quasiquoteexpand params\n    defer-tco env, new-ast\n\n\neval_defmacro = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of defmacro!, got a #{name.type}\"\n\n    # Evaluate the second parameter.\n    fn = eval_ast env, params[1]\n    if fn.type != \\function\n        runtime-error \"expected a function for the second parameter\n                       of defmacro!, got a #{fn.type}\"\n\n    # Copy fn and mark the function as a macro.\n    macro_fn = fn with is_macro: true\n    env.set name.value, macro_fn\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n# Evil eval.\nrepl_env.set 'eval', do\n    type: \\function\n    value: (ast) -> eval_ast repl_env, ast  # or use current env? (@ = this).\n\n# Read, Evaluate, Print\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\n# Define load-file.\nrep '\n(def! load-file \n  (fn* (f)\n    (eval\n      (read-string\n        (str \"(do \" (slurp f) \"\\nnil)\")))))'\n\n# Define cond.\nrep '\n(defmacro! cond\n  (fn* (& xs)\n    (if (> (count xs) 0)\n      (list \\'if (first xs)\n        (if (> (count xs) 1)\n          (nth xs 1)\n          (throw \"odd number of forms to cond\"))\n        (cons \\'cond (rest (rest xs)))))))'\n\n# Parse program arguments.\n# The first two (exe and core-file) are, respectively,\n# the interpreter executable (nodejs or lsc) and the\n# source file being executed (stepX_*.(ls|js)).\n[exe, core-file, mal-file, ...argv] = process.argv\n\nrepl_env.set '*ARGV*', do\n    type: \\list\n    value: argv |> map (arg) ->\n        type: \\string\n        value: arg\n\n\nif mal-file\n    rep \"(load-file \\\"#{mal-file}\\\")\"\nelse\n    # REPL.\n    loop\n        line = readline.readline 'user> '\n        break if not line? or line == ''\n        try\n            console.log rep line\n        catch error\n            if error.message\n            then console.error error.message\n            else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/step9_try.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns, unpack-tco} = require './core'\n{list-to-pairs} = require './utils'\n\n\ndefer-tco = (env, ast) ->\n    type: \\tco\n    env: env\n    ast: ast\n    eval: -> eval_ast env, ast\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\nmake-symbol = (name) -> {type: \\symbol, value: name}\nmake-list = (value) -> {type: \\list, value: value}\nmake-call = (name, params) -> make-list [make-symbol name] ++ params\nis-symbol = (ast, name) -> ast.type == \\symbol and ast.value == name\n\n\neval_ast = (env, {type, value}: ast) -->\n  loop\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0\n            return ast\n    else\n\n        result = if ast.value[0].type == \\symbol\n            params = ast.value[1 to]\n            switch ast.value[0].value\n            | 'def!'        => eval_def env, params\n            | 'let*'        => eval_let env, params\n            | 'do'          => eval_do env, params\n            | 'if'          => eval_if env, params\n            | 'fn*'         => eval_fn env, params\n            | 'quote'       => eval_quote env, params\n            | 'quasiquote'  => eval_quasiquote env, params\n            | 'defmacro!'   => eval_defmacro env, params\n            | 'try*'        => eval_try env, params\n            | otherwise     => eval_apply env, ast.value\n        else \n            eval_apply env, ast.value\n\n        if result.type == \\tco\n            env = result.env\n            {type, value}: ast = result.ast\n        else\n            return result\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Defer evaluation of let* body with TCO.\n    defer-tco let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    [...rest, last-param] = params\n    rest |> each eval_ast env\n    defer-tco env, last-param\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        defer-tco env, params[1]\n    else if params.length > 2\n        defer-tco env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, \n                make-list values.slice binds.length\n\n        # Defer evaluation of the function body to TCO.\n        defer-tco fn_env, body\n\n    {type: \\function, value: fn_instance, is_macro: false}\n\n\neval_apply = (env, list) ->\n    [first, ...raw_args] = list\n    fn = first |> eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    if fn.is_macro\n        return (defer-tco env, (unpack-tco (fn.value.apply env, raw_args)))\n    args = raw_args |> map eval_ast env\n    fn.value.apply env, args\n\n\neval_quote = (env, params) ->\n    if params.length != 1\n        runtime-error \"quote expected 1 parameter, got #{params.length}\"\n\n    params[0]\n\n\neval_quasiquoteexpand = (params) ->\n    if params.length != 1\n        runtime-error \"quasiquote expected 1 parameter, got #{params.length}\"\n\n    ast = params[0]\n    quasiquote ast\n\n\nquasiquote = (ast) ->\n    if ast.type in [\\symbol, \\map]\n        make-call 'quote', [ast]\n    else if ast.type == \\vector\n        make-call 'vec', [qq_foldr ast.value]\n    else if ast.type != \\list\n        ast\n    else if (ast.value.length == 2) and is-symbol ast.value[0], 'unquote'\n        ast.value[1]\n    else\n        qq_foldr ast.value\n\n\nqq_foldr = (xs) ->\n    result = make-list []\n    for i from xs.length - 1 to 0 by -1\n       result := qq_loop xs[i], result\n    result\n\n\nqq_loop = (elt, acc) ->\n    if elt.type == \\list and \\\n       elt.value.length == 2 and \\\n       is-symbol elt.value[0], 'splice-unquote'\n        make-call 'concat', [\n            elt.value[1]\n            acc\n        ]\n    else\n        make-call 'cons', [\n            quasiquote elt\n            acc\n        ]\n\n\neval_quasiquote = (env, params) ->\n    new-ast = eval_quasiquoteexpand params\n    defer-tco env, new-ast\n\n\neval_defmacro = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of defmacro!, got a #{name.type}\"\n\n    # Evaluate the second parameter.\n    fn = eval_ast env, params[1]\n    if fn.type != \\function\n        runtime-error \"expected a function for the second parameter\n                       of defmacro!, got a #{fn.type}\"\n\n    # Copy fn and mark the function as a macro.\n    macro_fn = fn with is_macro: true\n    env.set name.value, macro_fn\n\n\neval_try = (env, params) ->\n    if params.length > 2\n        runtime-error \"'try*' expected 1 or 2 parameters, \n                       got #{params.length}\"\n    try-form = params[0]\n    if params.length == 1\n        return eval_ast env, try-form\n\n    catch-clause = params[1]\n    if catch-clause.type != \\list or\n            catch-clause.value.length != 3 or\n            not (is-symbol catch-clause.value[0], 'catch*') or\n            catch-clause.value[1].type != \\symbol\n        runtime-error \"'try*' expected the second parameter to be \n                       of the form (catch* A B)\"\n\n    try\n        eval_ast env, try-form\n    catch error\n        error-symbol = catch-clause.value[1].value\n        error-value = \\\n            if error.message\n            then {type: \\string, value: error.message}\n            else error\n\n        catch-env = new Env env\n        catch-env.set error-symbol, error-value\n        eval_ast catch-env, catch-clause.value[2]\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n# Evil eval.\nrepl_env.set 'eval', do\n    type: \\function\n    value: (ast) -> eval_ast repl_env, ast  # or use current env? (@ = this).\n\n# Read, Evaluate, Print\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\n# Define load-file.\nrep '\n(def! load-file \n  (fn* (f)\n    (eval\n      (read-string\n        (str \"(do \" (slurp f) \"\\nnil)\")))))'\n\n# Define cond.\nrep '\n(defmacro! cond\n  (fn* (& xs)\n    (if (> (count xs) 0)\n      (list \\'if (first xs)\n        (if (> (count xs) 1)\n          (nth xs 1)\n          (throw \"odd number of forms to cond\"))\n        (cons \\'cond (rest (rest xs)))))))'\n\n# Parse program arguments.\n# The first two (exe and core-file) are, respectively,\n# the interpreter executable (nodejs or lsc) and the\n# source file being executed (stepX_*.(ls|js)).\n[exe, core-file, mal-file, ...argv] = process.argv\n\nrepl_env.set '*ARGV*', do\n    type: \\list\n    value: argv |> map (arg) ->\n        type: \\string\n        value: arg\n\n\nif mal-file\n    rep \"(load-file \\\"#{mal-file}\\\")\"\nelse\n    # REPL.\n    loop\n        line = readline.readline 'user> '\n        break if not line? or line == ''\n        try\n            console.log rep line\n        catch error\n            if error.message\n            then console.error error.message\n            else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/stepA_mal.ls",
    "content": "readline = require './node_readline'\n{id, map, each, last, all, unique, zip, Obj, elem-index} = require 'prelude-ls'\n{read_str} = require './reader'\n{pr_str} = require './printer'\n{Env} = require './env'\n{runtime-error, ns, unpack-tco} = require './core'\n{list-to-pairs} = require './utils'\n\n\ndefer-tco = (env, ast) ->\n    type: \\tco\n    env: env\n    ast: ast\n    eval: -> eval_ast env, ast\n\n\nis-thruthy = ({type, value}) -> \n    type != \\const or value not in [\\nil \\false]\n\n\nfmap-ast = (fn, {type, value}: ast) -->\n    {type: type, value: fn value}\n\n\nmake-symbol = (name) -> {type: \\symbol, value: name}\nmake-list = (value) -> {type: \\list, value: value}\nmake-call = (name, params) -> make-list [make-symbol name] ++ params\nis-symbol = (ast, name) -> ast.type == \\symbol and ast.value == name\n\n\neval_ast = (env, {type, value}: ast) -->\n  loop\n\n    dbgeval = env.get \"DEBUG-EVAL\"\n    if dbgeval and is-thruthy dbgeval then console.log \"EVAL: #{pr_str ast}\"\n\n    switch type\n    | \\symbol => return (env.get value\n                         or throw new Error \"'#{value}' not found\")\n    | \\list =>\n        # Proceed after this switch\n    | \\vector => return (ast |> fmap-ast map eval_ast env)\n    | \\map => return (ast |> fmap-ast Obj.map eval_ast env)\n    | otherwise => return ast\n\n    if value.length == 0\n            return ast\n    else\n\n        result = if ast.value[0].type == \\symbol\n            params = ast.value[1 to]\n            switch ast.value[0].value\n            | 'def!'        => eval_def env, params\n            | 'let*'        => eval_let env, params\n            | 'do'          => eval_do env, params\n            | 'if'          => eval_if env, params\n            | 'fn*'         => eval_fn env, params\n            | 'quote'       => eval_quote env, params\n            | 'quasiquote'  => eval_quasiquote env, params\n            | 'defmacro!'   => eval_defmacro env, params\n            | 'try*'        => eval_try env, params\n            | otherwise     => eval_apply env, ast.value\n        else \n            eval_apply env, ast.value\n\n        if result.type == \\tco\n            env = result.env\n            {type, value}: ast = result.ast\n        else\n            return result\n\n\ncheck_params = (name, params, expected) ->\n    if params.length != expected\n        runtime-error \"'#{name}' expected #{expected} parameters, \n                       got #{params.length}\"\n\n\neval_def = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of def!, got a #{name.type}\"\n\n    # Evaluate the second parameter and store \n    # it under name in the env.\n    env.set name.value, (eval_ast env, params[1])\n\n\neval_let = (env, params) ->\n    check_params 'let*', params, 2\n\n    binding_list = params[0]\n    if binding_list.type not in [\\list \\vector]\n        runtime-error \"expected 1st parameter of 'let*' to \n                       be a binding list (or vector), \n                       got a #{binding_list.type}\"\n    else if binding_list.value.length % 2 != 0\n        runtime-error \"binding list of 'let*' must have an even \n                       number of parameters\"\n\n    # Make a new environment with the \n    # current environment as outer.\n    let_env = new Env env\n\n    # Evaluate all binding values in the\n    # new environment.\n    binding_list.value\n    |> list-to-pairs\n    |> each ([binding_name, binding_value]) ->\n        if binding_name.type != \\symbol\n            runtime-error \"expected a symbol as binding name, \n                           got a #{binding_name.type}\"\n\n        let_env.set binding_name.value, (eval_ast let_env, binding_value)\n\n    # Defer evaluation of let* body with TCO.\n    defer-tco let_env, params[1]\n\n\neval_do = (env, params) ->\n    if params.length == 0\n        runtime-error \"'do' expected at least one parameter\"\n\n    [...rest, last-param] = params\n    rest |> each eval_ast env\n    defer-tco env, last-param\n\n\neval_if = (env, params) ->\n    if params.length < 2\n        runtime-error \"'if' expected at least 2 parameters\"\n    else if params.length > 3\n        runtime-error \"'if' expected at most 3 parameters\"\n\n    cond = eval_ast env, params[0]\n    if is-thruthy cond\n        defer-tco env, params[1]\n    else if params.length > 2\n        defer-tco env, params[2]\n    else\n        {type: \\const, value: \\nil}\n\n\neval_fn = (env, params) ->\n    check_params 'fn*', params, 2\n\n    if params[0].type not in [\\list \\vector]\n        runtime-error \"'fn*' expected first parameter to be a list or vector.\"\n\n    if not all (.type == \\symbol), params[0].value\n        runtime-error \"'fn*' expected only symbols in the parameters list.\"\n\n    binds = params[0].value |> map (.value)\n    vargs = null\n\n    # Parse variadic bind.\n    if binds.length >= 2\n        [...rest, amper, name] = binds\n        if amper == '&' and name != '&'\n            binds = rest\n            vargs = name\n\n    if elem-index '&', binds\n        runtime-error \"'fn*' invalid usage of variadic parameters.\"\n\n    if (unique binds).length != binds.length\n        runtime-error \"'fn*' duplicate symbols in parameters list.\"\n\n    body = params[1]\n\n    fn_instance = (...values) ->\n        if not vargs and values.length != binds.length\n            runtime-error \"function expected #{binds.length} parameters, \n                           got #{values.length}\"\n        else if vargs and values.length < binds.length\n            runtime-error \"function expected at least \n                           #{binds.length} parameters, \n                           got #{values.length}\"\n\n        # Set binds to values in the new env.\n        fn_env = new Env env\n\n        for [name, value] in (zip binds, values)\n            fn_env.set name, value\n\n        if vargs\n            fn_env.set vargs, \n                make-list values.slice binds.length\n\n        # Defer evaluation of the function body to TCO.\n        defer-tco fn_env, body\n\n    {type: \\function, value: fn_instance, is_macro: false}\n\n\neval_apply = (env, list) ->\n    [first, ...raw_args] = list\n    fn = first |> eval_ast env\n    if fn.type != \\function\n        runtime-error \"#{fn.value} is not a function, got a #{fn.type}\"\n\n    if fn.is_macro\n        return (defer-tco env, (unpack-tco (fn.value.apply env, raw_args)))\n    args = raw_args |> map eval_ast env\n    fn.value.apply env, args\n\n\neval_quote = (env, params) ->\n    if params.length != 1\n        runtime-error \"quote expected 1 parameter, got #{params.length}\"\n\n    params[0]\n\n\neval_quasiquoteexpand = (params) ->\n    if params.length != 1\n        runtime-error \"quasiquote expected 1 parameter, got #{params.length}\"\n\n    ast = params[0]\n    quasiquote ast\n\n\nquasiquote = (ast) ->\n    if ast.type in [\\symbol, \\map]\n        make-call 'quote', [ast]\n    else if ast.type == \\vector\n        make-call 'vec', [qq_foldr ast.value]\n    else if ast.type != \\list\n        ast\n    else if (ast.value.length == 2) and is-symbol ast.value[0], 'unquote'\n        ast.value[1]\n    else\n        qq_foldr ast.value\n\n\nqq_foldr = (xs) ->\n    result = make-list []\n    for i from xs.length - 1 to 0 by -1\n       result := qq_loop xs[i], result\n    result\n\n\nqq_loop = (elt, acc) ->\n    if elt.type == \\list and \\\n       elt.value.length == 2 and \\\n       is-symbol elt.value[0], 'splice-unquote'\n        make-call 'concat', [\n            elt.value[1]\n            acc\n        ]\n    else\n        make-call 'cons', [\n            quasiquote elt\n            acc\n        ]\n\n\neval_quasiquote = (env, params) ->\n    new-ast = eval_quasiquoteexpand params\n    defer-tco env, new-ast\n\n\neval_defmacro = (env, params) ->\n    check_params 'def!', params, 2\n\n    # Name is in the first parameter, and is not evaluated.\n    name = params[0]\n    if name.type != \\symbol\n        runtime-error \"expected a symbol for the first parameter \n                       of defmacro!, got a #{name.type}\"\n\n    # Evaluate the second parameter.\n    fn = eval_ast env, params[1]\n    if fn.type != \\function\n        runtime-error \"expected a function for the second parameter\n                       of defmacro!, got a #{fn.type}\"\n\n    # Copy fn and mark the function as a macro.\n    macro_fn = fn with is_macro: true\n    env.set name.value, macro_fn\n\n\neval_try = (env, params) ->\n    if params.length > 2\n        runtime-error \"'try*' expected 1 or 2 parameters, \n                       got #{params.length}\"\n    try-form = params[0]\n    if params.length == 1\n        return eval_ast env, try-form\n\n    catch-clause = params[1]\n    if catch-clause.type != \\list or\n            catch-clause.value.length != 3 or\n            not (is-symbol catch-clause.value[0], 'catch*') or\n            catch-clause.value[1].type != \\symbol\n        runtime-error \"'try*' expected the second parameter to be \n                       of the form (catch* A B)\"\n\n    try\n        eval_ast env, try-form\n    catch error\n        error-symbol = catch-clause.value[1].value\n        error-value = \\\n            if error.message\n            then {type: \\string, value: error.message}\n            else error\n\n        catch-env = new Env env\n        catch-env.set error-symbol, error-value\n        eval_ast catch-env, catch-clause.value[2]\n\n\nrepl_env = new Env\nfor symbol, value of ns\n    repl_env.set symbol, value\n\n# Evil eval.\nrepl_env.set 'eval', do\n    type: \\function\n    value: (ast) -> eval_ast repl_env, ast  # or use current env? (@ = this).\n\n# Read, Evaluate, Print\nrep = (line) ->\n    line\n    |> read_str\n    |> eval_ast repl_env\n    |> (ast) -> pr_str ast, print_readably=true\n\n\n# Define not.\nrep '(def! not (fn* (x) (if x false true)))'\n\n# Define load-file.\nrep '\n(def! load-file \n  (fn* (f)\n    (eval\n      (read-string\n        (str \"(do \" (slurp f) \"\\nnil)\")))))'\n\n# Define cond.\nrep '\n(defmacro! cond\n  (fn* (& xs)\n    (if (> (count xs) 0)\n      (list \\'if (first xs)\n        (if (> (count xs) 1)\n          (nth xs 1)\n          (throw \"odd number of forms to cond\"))\n        (cons \\'cond (rest (rest xs)))))))'\n\n# Parse program arguments.\n# The first two (exe and core-file) are, respectively,\n# the interpreter executable (nodejs or lsc) and the\n# source file being executed (stepX_*.(ls|js)).\n[exe, core-file, mal-file, ...argv] = process.argv\n\nrepl_env.set '*ARGV*', do\n    type: \\list\n    value: argv |> map (arg) ->\n        type: \\string\n        value: arg\n\nrepl_env.set '*host-language*', \n    {type: \\string, value: 'livescript'}\n\n\nif mal-file\n    rep \"(load-file \\\"#{mal-file}\\\")\"\nelse\n    # REPL.\n    rep '(println (str \"Mal [\" *host-language* \"]\"))'\n    loop\n        line = readline.readline 'user> '\n        break if not line? or line == ''\n        try\n            console.log rep line\n        catch error\n            if error.message\n            then console.error error.message\n            else console.error \"Error:\", pr_str error, print_readably=true\n"
  },
  {
    "path": "impls/livescript/utils.ls",
    "content": "{map} = require 'prelude-ls'\n\n\nexport list-to-pairs = (list) ->\n    [0 to (list.length - 2) by 2] \\\n        |> map (idx) -> [list[idx], list[idx+1]]\n"
  },
  {
    "path": "impls/logo/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Rebuild ucblogo.\n# * without X libraries so that the executable starts in text mode.\n# * Add the timems function implemented in C\n\nRUN apt -y install autoconf autoconf-archive automake dpkg-dev g++ libncurses-dev\n\nRUN sed -i 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources\nRUN apt-get -y update\n\nRUN cd /tmp \\\n    && apt-get source ucblogo \\\n    && cd /tmp/ucblogo-* \\\n    && autoreconf -f -i \\\n    && ./configure --disable-docs --disable-x11 \\\n    && echo \"extern NODE *ltimems(NODE *);\" >> globals.h \\\n    && echo \"NODE *ltimems(NODE *args) { struct timeval tv; gettimeofday(&tv, NULL); return(make_floatnode(((FLONUM)tv.tv_sec) * 1000.0 + (tv.tv_usec / 1000))); }\" >> coms.c \\\n    && sed -i -e 's/^\\(.*lthrow.*\\)$/\\1 {\"timems\", 0, 0, 0, PREFIX_PRIORITY, ltimems},/' init.c \\\n    && make \\\n    && make install \\\n    && cd /tmp \\\n    && rm -rf /tmp/ucblogo*\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/logo/Makefile",
    "content": "SOURCES_BASE = readline.lg types.lg reader.lg printer.lg\nSOURCES_LISP = env.lg core.lg stepA_mal.lg\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\n.PHONY: all dist clean\n\nall:\n\t@true\n\ndist: mal.lg mal\n\nmal.lg: $(SOURCES)\n\tcat $+ | grep -v \"^load \" > $@\n\nmal: mal.lg\n\techo \"#!/usr/bin/env logo\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.lg mal\n"
  },
  {
    "path": "impls/logo/core.lg",
    "content": "make \"global_exception []\n\nto equal_q :a :b\ncase obj_type :a [\n  [[list vector]\n    if not memberp obj_type :b [list vector] [output \"false]\n    make \"a seq_val :a\n    make \"b seq_val :b\n    if notequalp count :a count :b [output \"false]\n    (foreach :a :b [if not equal_q ?1 ?2 [output \"false]])\n    output \"true\n  ]\n  [[map]\n    if \"map <> obj_type :b [output \"false]\n    localmake \"ka map_keys :a\n    localmake \"kb map_keys :b\n    if notequalp count :ka count :kb [output \"false]\n    (foreach :ka map_vals :a [if not equal_q map_get :b ?1 ?2 [output \"false]])\n    output \"true\n  ]\n  [else output :a = :b]\n]\nend\n\nto |mal_=| :a :b\noutput bool_to_mal equal_q :a :b\nend\n\nto mal_throw :a\nmake \"global_exception :a\n(throw \"error \"_mal_exception_)\nend\n\nto mal_nil? :a\noutput bool_to_mal ((obj_type :a) = \"nil)\nend\n\nto mal_true? :a\noutput bool_to_mal ((obj_type :a) = \"true)\nend\n\nto mal_false? :a\noutput bool_to_mal ((obj_type :a) = \"false)\nend\n\nto mal_string? :a\noutput bool_to_mal ((obj_type :a) = \"string)\nend\n\nto mal_symbol :a\noutput symbol_new string_val :a\nend\n\nto mal_symbol? :a\noutput bool_to_mal ((obj_type :a) = \"symbol)\nend\n\nto mal_keyword :a\noutput ifelse \"keyword = obj_type :a \":a [keyword_new string_val :a]\nend\n\nto mal_keyword? :a\noutput bool_to_mal ((obj_type :a) = \"keyword)\nend\n\nto mal_number? :a\noutput bool_to_mal ((obj_type :a) = \"number)\nend\n\nto mal_fn? :a\noutput bool_to_mal memberp obj_type :a [fn nativefn]\nend\n\nto mal_macro? :a\noutput bool_to_mal \"macro = obj_type :a\nend\n\nto |mal_pr-str| [:args]\noutput string_new pr_seq :args \"true \"| |\nend\n\nto mal_str [:args]\noutput string_new pr_seq :args \"false \"\nend\n\nto mal_prn [:args]\nprint pr_seq :args \"true \"| |\noutput nil_new\nend\n\nto mal_println [:args]\nprint pr_seq :args \"false \"| |\noutput nil_new\nend\n\nto |mal_read-string| :str\noutput read_str string_val :str\nend\n\nto mal_readline :prompt\nlocalmake \"line readline string_val :prompt\nif :line=[] [output nil_new]\noutput string_new :line\nend\n\nto mal_slurp :str\nlocalmake \"filename string_val :str\nopenread :filename\nsetread :filename\nlocalmake \"content \"\nuntil [eofp] [\n  make \"content word :content readchar\n]\nclose :filename\noutput string_new :content\nend\n\nto |mal_<| :a :b\noutput bool_to_mal lessp number_val :a number_val :b\nend\n\nto |mal_<=| :a :b\noutput bool_to_mal lessequalp number_val :a number_val :b\nend\n\nto |mal_>| :a :b\noutput bool_to_mal greaterp number_val :a number_val :b\nend\n\nto |mal_>=| :a :b\noutput bool_to_mal greaterequalp number_val :a number_val :b\nend\n\nto |mal_+| :a :b\noutput number_new sum number_val :a number_val :b\nend\n\nto |mal_-| :a :b\noutput number_new difference number_val :a number_val :b\nend\n\nto |mal_*| :a :b\noutput number_new product number_val :a number_val :b\nend\n\nto |mal_/| :a :b\noutput number_new quotient number_val :a number_val :b\nend\n\nto |mal_time-ms|\n; Native function timems is added to coms.c (see Dockerfile)\noutput number_new timems\nend\n\nto mal_list [:args]\noutput list_new :args\nend\n\nto mal_list? :a\noutput bool_to_mal ((obj_type :a) = \"list)\nend\n\nto mal_vector [:args]\noutput vector_new :args\nend\n\nto mal_vector? :a\noutput bool_to_mal ((obj_type :a) = \"vector)\nend\n\nto |mal_hash-map| [:pairs]\noutput map_assoc :map_empty :pairs\nend\n\nto mal_map? :a\noutput bool_to_mal \"map = obj_type :a\nend\n\nto mal_assoc :map [:args]\noutput map_assoc :map :args\nend\n\nto mal_get :map :key\nif \"nil = obj_type :map [output nil_new]\nlocalmake \"val map_get :map :key\nif \"notfound = obj_type :val [output nil_new]\noutput :val\nend\n\nto mal_contains? :m :k\noutput bool_to_mal \"notfound <> obj_type map_get :m :k\nend\n\nto mal_keys :map\noutput list_new map_keys :map\nend\n\nto mal_vals :map\noutput list_new map_vals :map\nend\n\nto mal_sequential? :a\noutput bool_to_mal memberp obj_type :a [list vector]\nend\n\nto mal_cons :a :b\noutput list_new fput :a seq_val :b\nend\n\nto mal_concat [:args]\noutput list_new map.se \"seq_val :args\nend\n\nto mal_vec :s\noutput vector_new seq_val :s\nend\n\nto mal_nth :a :i\nmake \"a seq_val :a\nmake \"i number_val :i\nif or (:i < 0) (:i >= count :a) [(throw \"error [nth: index out of range])]\noutput item (:i + 1) :a\nend\n\nto mal_first :a\nif \"nil = obj_type :a [output nil_new]\nmake \"a seq_val :a\noutput ifelse emptyp :a \"nil_new [first :a]\nend\n\nto mal_rest :a\nif \"nil = obj_type :a [output list_new []]\nmake \"a seq_val :a\noutput list_new ifelse emptyp :a [[]] [butfirst :a]\nend\n\nto mal_empty? :a\noutput bool_to_mal emptyp seq_val :a\nend\n\nto mal_count :a\noutput number_new ifelse \"nil = obj_type :a 0 [count seq_val :a]\nend\n\nto mal_apply :f [:args]\nlocalmake \"callargs map.se [ifelse emptyp ?rest [seq_val ?] [(list ?)]] :args\noutput invoke_fn :f :callargs\nend\n\nto mal_map :f :seq\noutput list_new map [invoke_fn :f (list ?)] seq_val :seq\nend\n\nto mal_conj :a0 [:rest]\ncase obj_type :a0 [\n  [[list]   localmake \"newlist seq_val :a0\n            foreach :rest [make \"newlist fput ? :newlist]\n            output list_new :newlist]\n  [[vector] output vector_new sentence seq_val :a0 :rest]\n  [else     (throw \"error [conj requires list or vector]) ]\n]\nend\n\nto mal_seq :a\ncase obj_type :a [\n  [[string]\n    make \"a string_val :a\n    if emptyp :a [output nil_new]\n    localmake \"chars []\n    for [i [count :a] 1 -1] [ make \"chars fput string_new item :i :a :chars ]\n    output list_new :chars ]\n  [[list]\n    if emptyp seq_val :a [output nil_new]\n    output :a ]\n  [[vector]\n    make \"a seq_val :a\n    if emptyp :a [output nil_new]\n    output list_new :a ]\n  [[nil] output nil_new ]\n  [else (throw \"error [seq requires string or list or vector or nil]) ]\n]\nend\n\nto mal_atom? :a\noutput bool_to_mal ((obj_type :a) = \"atom)\nend\n\nto invoke_fn :f :callargs\noutput case obj_type :f [\n  [[nativefn]\n    nativefn_apply :f :callargs ]\n  [[fn]\n    fn_apply       :f :callargs ]\n  [[macro]\n    macro_apply    :f :callargs ]\n  [else\n    (throw \"error [Wrong type for apply])]\n]\nend\n\nto mal_swap! :atom :f [:args]\nlocalmake \"callargs fput mal_deref :atom :args\noutput mal_reset! :atom invoke_fn :f :callargs\nend\n\nto logo_to_mal :a\noutput cond [\n  [[memberp :a [true false]] bool_to_mal :a]\n  [[numberp :a]  number_new :a]\n  [[wordp :a]    string_new :a]\n  [[listp :a]    list_new map \"logo_to_mal :a]\n  [else          nil_new]\n]\nend\n\nto |mal_logo-eval| :str\nlocalmake \"res runresult string_val :str\nif emptyp :res [output nil_new]\noutput logo_to_mal first :res\nend\n\nmake \"core_ns [\n  = throw\n\n  nil? true? false? string? symbol symbol? keyword keyword? number?\n  fn? macro?\n\n  pr-str str prn println read-string readline slurp\n\n  < <= > >= + - * / time-ms\n\n  list list? vector vector? hash-map map? assoc dissoc get contains?\n  keys vals\n\n  sequential? cons concat vec nth first rest empty? count apply map\n\n  conj seq\n\n  meta with-meta atom atom? deref reset! swap!\n\n  logo-eval mal_logo_eval\n]\n"
  },
  {
    "path": "impls/logo/env.lg",
    "content": "to env_new :outer :binds :exprs\noutput listtoarray (list :outer :binds :exprs)\nend\n\nto env_keys :env\noutput item 2 :env\nend\n\nto env_get :env :key\n; Start with the quick memberp built-in, and only iterate slowly in\n; LOGO once a match is found.\nuntil [memberp :key item 2 :env] [\n  make \"env item 1 :env\n  if emptyp :env [output notfound_new]\n]\nforeach item 2 :env [if ? = :key [output item # item 3 :env]]\nend\n\nto env_set :env :key :val\n.setitem 2 :env fput :key item 2 :env\n.setitem 3 :env fput :val item 3 :env\nend\n"
  },
  {
    "path": "impls/logo/examples/tree.mal",
    "content": "; Draw a tree\n;\n; The classic Logo demo for recursive functions - now in Mal!\n\n; White background with blue pen\n(logo-eval \"setbackground 7\")\n(logo-eval \"setpencolor 1\")\n\n; Initialize turtle location\n(logo-eval \"penup setxy 0 -100 pendown\")\n\n; Expose Logo drawing functions to Mal code\n(def! fd (fn* [size] (logo-eval (str \"fd \" size))))\n(def! bk (fn* [size] (logo-eval (str \"bk \" size))))\n(def! lt (fn* [size] (logo-eval (str \"lt \" size))))\n(def! rt (fn* [size] (logo-eval (str \"rt \" size))))\n\n; Tree parts\n(def! leaf (fn* [size] (do (fd size) (bk size))))\n(def! branch (fn* [size] (do (fd size) (draw-tree size) (bk size))))\n(def! two-branches (fn* [size] (do (lt 10) (branch size) (rt 40) (branch size) (lt 30))))\n(def! draw-tree (fn* [size] (if (< size 5) (leaf size) (two-branches (/ size 2)))))\n\n; Draw it\n(draw-tree 250)\n"
  },
  {
    "path": "impls/logo/printer.lg",
    "content": "to pr_str :exp :readable\noutput case obj_type :exp [\n  [[nil]      \"nil]\n  [[true]     \"true]\n  [[false]    \"false]\n  [[number]   number_val :exp]\n  [[symbol]   symbol_value :exp]\n  [[keyword]  word \": keyword_val :exp]\n  [[string]   print_string string_val :exp :readable]\n  [[list]     (word \"\\( pr_seq seq_val :exp :readable \"| | \"\\) ) ]\n  [[vector]   (word \"\\[ pr_seq seq_val :exp :readable \"| | \"\\] ) ]\n  [[map]      (word \"\\{ pr_seq (map.se [list ?1 ?2] map_keys :exp\n                map_vals :exp) :readable \"| | \"\\} ) ]\n  [[atom]     (word \"|(atom | pr_str mal_deref :exp \"true \"\\) ) ]\n  [[nativefn] \"#<NativeFunction>]\n  [[fn]       \"#<Function> ]\n  [[macro]    \"#<Macro> ]\n  [else       (throw \"error (sentence [unknown type] obj_type :exp))]\n]\nend\n\nto escape_string :s\noutput map [\n    case rawascii ? [\n      [[34 92] word \"\\\\ ?]\n      [[10]    \"\\\\n]\n      [else    ?]\n    ]\n  ] :s\nend\n\nto print_string :exp :readable\nifelse :readable [\n  output (word \"\\\" escape_string :exp \"\\\" )\n] [\n  output :exp\n]\nend\n\nto pr_seq :seq :readable :delim_char\noutput apply \"word map [\n  ifelse # = 1 [pr_str ? :readable] [word :delim_char pr_str ? :readable]\n] :seq\nend\n"
  },
  {
    "path": "impls/logo/reader.lg",
    "content": "; LOGO, variables defined in a procedure are visible from called\n; procedures.  Use this quirk to pass the current parser status.\n; str:  the parsed string (constant)\n; cnt:  its length        (constant)\n; idx:  the currently parsed index, or cnt + 1\n\nmake \"new_line_char char 10\nmake \"forbidden_chars (word :new_line_char char 13 \"| \"(),;[\\\\]{}|)\nmake \"separator_chars (word :new_line_char \"| ,|)\n\nto read_allowed_chars\nlocalmake \"res \"\nwhile [:idx <= :cnt] [\n  localmake \"c item :idx :str\n  if memberp :c :forbidden_chars [output :res]\n  make \"idx :idx + 1\n  make \"res word :res :c\n]\noutput :res\nend\n\nto skip_separators\nwhile [:idx <= :cnt] [\n  localmake \"c item :idx :str\n  cond [\n    [[:c = \"|;|]\n      do.until [\n        make \"idx :idx + 1\n        if :cnt < :idx \"stop\n      ] [:new_line_char = item :idx :str]\n    ]\n    [[not memberp :c :separator_chars] stop]\n  ]\n  make \"idx :idx + 1\n]\nend\n\nto read_string\nlocalmake \"res \"\nwhile [:idx <= :cnt] [\n  localmake \"c item :idx :str\n  make \"idx :idx + 1\n  if :c = \"\" [output :res]\n  if :c = \"\\\\ [\n    if :cnt < :idx [(throw \"error [unbalananced \"\"])]\n    make \"c item :idx :str\n    make \"idx :idx + 1\n    if :c = \"n [make \"c :new_line_char]\n  ]\n  make \"res word :res :c\n]\n(throw \"error [unbalanced \"\"])\nend\n\nto read_symbol\nlocalmake \"token word :c read_allowed_chars\noutput cond [\n  [[:token = \"nil]               nil_new]\n  [[memberp :token [false true]] bool_to_mal :token]\n  [[numberp :token]              number_new :token]\n  [else                          symbol_new :token]\n]\nend\n\nto read_seq :end_char\nlocalmake \"res []\nforever [\n  skip_separators\n  if :cnt < :idx [(throw \"error (sentence \"EOF, \"expected :end_char))]\n  if :end_char = item :idx :str [\n    make \"idx :idx + 1\n    ; reversing once is more efficient than successive lputs.\n    output reverse :res\n  ]\n  make \"res fput read_form :res\n]\nend\n\nto reader_macro :symbol_name\noutput list_new list symbol_new :symbol_name read_form\nend\n\nto with_meta_reader_macro\nlocalmake \"meta read_form\noutput list_new (list symbol_new \"with-meta read_form :meta)\nend\n\nto read_unquote\nif :idx <= :cnt [if \"@ = item :idx :str [\n  make \"idx :idx + 1\n  output reader_macro \"splice-unquote\n]]\noutput reader_macro \"unquote\nend\n\nto read_form\nskip_separators\nif :cnt < :idx [(throw \"error [EOF, expected a form])]\nlocalmake \"c item :idx :str\nmake \"idx :idx + 1\noutput case :c [\n  ['     reader_macro \"quote ]\n  [`     reader_macro \"quasiquote ]\n  [~     read_unquote ]\n  [^     with_meta_reader_macro ]\n  [@     reader_macro \"deref ]\n  [|(|   list_new read_seq \"|)|]\n  [|[|   vector_new read_seq \"|]|]\n  [|{|   map_assoc :map_empty read_seq \"|}|]\n  [|)]}| (throw \"error (sentence \"unexpected \"' :c \"'))]\n  [\"     string_new read_string]\n  [:     keyword_new read_allowed_chars]\n  [else  read_symbol ]\n]\nend\n\nto read_str :str\nlocalmake \"idx 1\nlocalmake \"cnt count :str\noutput read_form\nend\n"
  },
  {
    "path": "impls/logo/readline.lg",
    "content": "make \"backspace_char char 8\n\nto readline :prompt\ntype :prompt\nwait 0         ; flush standard output\nlocalmake \"line \"\nforever [\n  localmake \"c readchar\n  ifelse emptyp :c [\n    output []\n  ] [\n    case rawascii :c [\n      [[4]       output []]\n      [[10]      type :c\n                 output :line]\n      [[127]     if not emptyp :line [\n                   (type :backspace_char \"| | :backspace_char)\n                   make \"line butlast :line\n                 ]]\n      [else      type :c\n                 make \"line word :line :c]\n    ]\n  ]\n]\nend\n"
  },
  {
    "path": "impls/logo/run",
    "content": "#!/usr/bin/env bash\nexec ucblogo $(dirname $0)/${STEP:-stepA_mal}.lg - \"${@}\"\n"
  },
  {
    "path": "impls/logo/step0_repl.lg",
    "content": "load \"../logo/readline.lg\n\nto _read :str\noutput :str\nend\n\nto _eval :ast\noutput :ast\nend\n\nto _print :exp\noutput :exp\nend\n\nto rep :str\noutput _print _eval _read :str\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      print rep :line\n    ]\n] [:line = []]\n(print)\nend\n\nrepl\nbye\n"
  },
  {
    "path": "impls/logo/step1_read_print.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\n\nto _read :str\noutput read_str :str\nend\n\nto _eval :ast\noutput :ast\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto rep :str\noutput _print _eval _read :str\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  (print \"Error: item 2 :exception)\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nrepl\nbye\n"
  },
  {
    "path": "impls/logo/step2_eval.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\n\nto _read :str\noutput read_str :str\nend\n\nto _eval :ast :env\n; (print \"EVAL: _print :ast)\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val map_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    localmake \"f _eval :a0 :env\n    output nativefn_apply :f map [_eval ? :env] :ast ]\n\n  [else output :ast]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto mal_add :a :b\noutput number_new ((number_val :a) + (number_val :b))\nend\n\nto mal_sub :a :b\noutput number_new ((number_val :a) - (number_val :b))\nend\n\nto mal_mul :a :b\noutput number_new ((number_val :a) * (number_val :b))\nend\n\nto mal_div :a :b\noutput number_new ((number_val :a) / (number_val :b))\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  (print \"Error: item 2 :exception)\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nmake \"repl_env map_assoc :map_empty (list\n  symbol_new \"+ nativefn_new \"mal_add\n  symbol_new \"- nativefn_new \"mal_sub\n  symbol_new \"* nativefn_new \"mal_mul\n  symbol_new \"/ nativefn_new \"mal_div)\n\nrepl\nbye\n"
  },
  {
    "path": "impls/logo/step3_env.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\n\nto _read :str\noutput read_str :str\nend\n\nto _eval :ast :env\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      output _eval item 2 :ast :letenv ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      output nativefn_apply :f map [_eval ? :env] :ast ]\n    ]\n  ]\n  [else output :ast]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto mal_add :a :b\noutput number_new ((number_val :a) + (number_val :b))\nend\n\nto mal_sub :a :b\noutput number_new ((number_val :a) - (number_val :b))\nend\n\nto mal_mul :a :b\noutput number_new ((number_val :a) * (number_val :b))\nend\n\nto mal_div :a :b\noutput number_new ((number_val :a) / (number_val :b))\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  (print \"Error: item 2 :exception)\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nmake \"repl_env env_new [] map \"symbol_new   [+       -       *       /      ] ~\n                          map \"nativefn_new [mal_add mal_sub mal_mul mal_div]\n\nrepl\nbye\n"
  },
  {
    "path": "impls/logo/step4_if_fn_do.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto _eval :ast :env\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      output _eval item 2 :ast :letenv ]\n\n    [[do]\n      foreach :ast [\n        ifelse emptyp ?rest [output _eval ? :env] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       output _eval item 3 :ast :env\n                     ] [\n                       output nil_new\n                     ]]\n        [else        output _eval item 2 :ast :env]\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n         output _eval fn_body :f fn_gen_env :f map [_eval ? :env] :ast ]\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  (print \"Error: item 2 :exception)\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\n; core_ns\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\n\n; core.mal: defined using the language itself\nre \"|(def! not (fn* (a) (if a false true)))|\n\nrepl\nbye\n"
  },
  {
    "path": "impls/logo/step5_tco.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto _eval :ast :env\nforever [\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      make \"env :letenv\n      make \"ast item 2 :ast ]   ; TCO\n\n    [[do]\n      foreach :ast [ ; TCO for last item\n        ifelse emptyp ?rest [make \"ast ?] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       make \"ast item 3 :ast ; TCO\n                     ] [\n                       output nil_new\n                     ]]\n        [else        make \"ast item 2 :ast] ; TCO\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n          make \"env fn_gen_env :f map [_eval ? :env] :ast\n          make \"ast fn_body :f ]  ; TCO\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  (print \"Error: item 2 :exception)\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\n; core_ns\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\n\n; core.mal: defined using the language itself\nre \"|(def! not (fn* (a) (if a false true)))|\n\nrepl\nbye\n"
  },
  {
    "path": "impls/logo/step6_file.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto _eval :ast :env\nforever [\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      make \"env :letenv\n      make \"ast item 2 :ast ]   ; TCO\n\n    [[do]\n      foreach :ast [ ; TCO for last item\n        ifelse emptyp ?rest [make \"ast ?] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       make \"ast item 3 :ast ; TCO\n                     ] [\n                       output nil_new\n                     ]]\n        [else        make \"ast item 2 :ast] ; TCO\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n          make \"env fn_gen_env :f map [_eval ? :env] :ast\n          make \"ast fn_body :f ]  ; TCO\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  localmake \"e item 2 :exception\n  ifelse :e = \"_mal_exception_ [\n    (print \"Error: pr_str :global_exception \"false)\n  ] [\n    (print \"Error: :e)\n  ]\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nto mal_eval :a\noutput _eval :a :repl_env\nend\n\nto argv_list\nlocalmake \"argv ifelse emptyp :command.line [[]] [butfirst :command.line]\noutput list_new map \"string_new :argv\nend\n\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\nenv_set :repl_env symbol_new \"eval nativefn_new \"mal_eval\nenv_set :repl_env symbol_new \"*ARGV* argv_list\n\n; core.mal: defined using the language itself\nre \"|(def! not (fn* (a) (if a false true)))|\nre \"|(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))|\n\nifelse emptyp :command.line [\n  repl\n] [\n  catch \"error [re (word \"|(load-file \"| first :command.line \"|\")| )]\n  print_exception error\n]\n\nbye\n"
  },
  {
    "path": "impls/logo/step7_quote.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto quasiquote :ast\ncase obj_type :ast [\n  [[list]       localmake \"xs seq_val ast\n                if not emptyp :xs [if equal_q first :xs symbol_new \"unquote [\n                  output item 2 :xs\n                ]]\n                output qq_seq :xs]\n  [[vector]     output list_new (list symbol_new \"vec qq_seq seq_val :ast)]\n  [[map symbol] output list_new (list symbol_new \"quote :ast)]\n  [else         output :ast]\n]\nend\n\nto qq_seq :xs\nlocalmake \"result list_new []\nforeach reverse :xs [make \"result qq_folder ? :result]\noutput :result\nend\n\nto qq_folder :elt :acc\nif \"list = obj_type :elt [\n  localmake \"ys seq_val :elt\n  if not emptyp :ys [if equal_q first :ys symbol_new \"splice-unquote [\n    output list_new (list symbol_new \"concat item 2 :ys :acc)\n  ]]\n]\noutput list_new (list symbol_new \"cons quasiquote :elt :acc)\nend\n\nto _eval :ast :env\nforever [\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      make \"env :letenv\n      make \"ast item 2 :ast ]   ; TCO\n\n    [[quote]\n      output first :ast]\n\n    [[quasiquote]\n      make \"ast quasiquote first :ast ] ; TCO\n\n    [[do]\n      foreach :ast [ ; TCO for last item\n        ifelse emptyp ?rest [make \"ast ?] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       make \"ast item 3 :ast ; TCO\n                     ] [\n                       output nil_new\n                     ]]\n        [else        make \"ast item 2 :ast] ; TCO\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n          make \"env fn_gen_env :f map [_eval ? :env] :ast\n          make \"ast fn_body :f ]  ; TCO\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  localmake \"e item 2 :exception\n  ifelse :e = \"_mal_exception_ [\n    (print \"Error: pr_str :global_exception \"false)\n  ] [\n    (print \"Error: :e)\n  ]\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nto mal_eval :a\noutput _eval :a :repl_env\nend\n\nto argv_list\nlocalmake \"argv ifelse emptyp :command.line [[]] [butfirst :command.line]\noutput list_new map \"string_new :argv\nend\n\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\nenv_set :repl_env symbol_new \"eval nativefn_new \"mal_eval\nenv_set :repl_env symbol_new \"*ARGV* argv_list\n\n; core.mal: defined using the language itself\nre \"|(def! not (fn* (a) (if a false true)))|\nre \"|(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))|\n\nifelse emptyp :command.line [\n  repl\n] [\n  catch \"error [re (word \"|(load-file \"| first :command.line \"|\")| )]\n  print_exception error\n]\n\nbye\n"
  },
  {
    "path": "impls/logo/step8_macros.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto quasiquote :ast\ncase obj_type :ast [\n  [[list]       localmake \"xs seq_val ast\n                if not emptyp :xs [if equal_q first :xs symbol_new \"unquote [\n                  output item 2 :xs\n                ]]\n                output qq_seq :xs]\n  [[vector]     output list_new (list symbol_new \"vec qq_seq seq_val :ast)]\n  [[map symbol] output list_new (list symbol_new \"quote :ast)]\n  [else         output :ast]\n]\nend\n\nto qq_seq :xs\nlocalmake \"result list_new []\nforeach reverse :xs [make \"result qq_folder ? :result]\noutput :result\nend\n\nto qq_folder :elt :acc\nif \"list = obj_type :elt [\n  localmake \"ys seq_val :elt\n  if not emptyp :ys [if equal_q first :ys symbol_new \"splice-unquote [\n    output list_new (list symbol_new \"concat item 2 :ys :acc)\n  ]]\n]\noutput list_new (list symbol_new \"cons quasiquote :elt :acc)\nend\n\nto _eval :ast :env\nforever [\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      make \"env :letenv\n      make \"ast item 2 :ast ]   ; TCO\n\n    [[quote]\n      output first :ast]\n\n    [[quasiquote]\n      make \"ast quasiquote first :ast ] ; TCO\n\n    [[defmacro!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"macro_fn macro_new _eval :a2 :env\n      env_set :env :a1 :macro_fn\n      output :macro_fn ]\n\n    [[do]\n      foreach :ast [ ; TCO for last item\n        ifelse emptyp ?rest [make \"ast ?] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       make \"ast item 3 :ast ; TCO\n                     ] [\n                       output nil_new\n                     ]]\n        [else        make \"ast item 2 :ast] ; TCO\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n          make \"env fn_gen_env :f map [_eval ? :env] :ast\n          make \"ast fn_body :f ]  ; TCO\n        [[macro]\n          make \"ast macro_apply :f :ast ] ; TCO\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  localmake \"e item 2 :exception\n  ifelse :e = \"_mal_exception_ [\n    (print \"Error: pr_str :global_exception \"false)\n  ] [\n    (print \"Error: :e)\n  ]\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nto mal_eval :a\noutput _eval :a :repl_env\nend\n\nto argv_list\nlocalmake \"argv ifelse emptyp :command.line [[]] [butfirst :command.line]\noutput list_new map \"string_new :argv\nend\n\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\nenv_set :repl_env symbol_new \"eval nativefn_new \"mal_eval\nenv_set :repl_env symbol_new \"*ARGV* argv_list\n\n; core.mal: defined using the language itself\nre \"|(def! not (fn* (a) (if a false true)))|\nre \"|(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))|\nre \"|(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))|\n\nifelse emptyp :command.line [\n  repl\n] [\n  catch \"error [re (word \"|(load-file \"| first :command.line \"|\")| )]\n  print_exception error\n]\n\nbye\n"
  },
  {
    "path": "impls/logo/step9_try.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto quasiquote :ast\ncase obj_type :ast [\n  [[list]       localmake \"xs seq_val ast\n                if not emptyp :xs [if equal_q first :xs symbol_new \"unquote [\n                  output item 2 :xs\n                ]]\n                output qq_seq :xs]\n  [[vector]     output list_new (list symbol_new \"vec qq_seq seq_val :ast)]\n  [[map symbol] output list_new (list symbol_new \"quote :ast)]\n  [else         output :ast]\n]\nend\n\nto qq_seq :xs\nlocalmake \"result list_new []\nforeach reverse :xs [make \"result qq_folder ? :result]\noutput :result\nend\n\nto qq_folder :elt :acc\nif \"list = obj_type :elt [\n  localmake \"ys seq_val :elt\n  if not emptyp :ys [if equal_q first :ys symbol_new \"splice-unquote [\n    output list_new (list symbol_new \"concat item 2 :ys :acc)\n  ]]\n]\noutput list_new (list symbol_new \"cons quasiquote :elt :acc)\nend\n\nto _eval :ast :env\nforever [\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      make \"env :letenv\n      make \"ast item 2 :ast ]   ; TCO\n\n    [[quote]\n      output first :ast]\n\n    [[quasiquote]\n      make \"ast quasiquote first :ast ] ; TCO\n\n    [[defmacro!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"macro_fn macro_new _eval :a2 :env\n      env_set :env :a1 :macro_fn\n      output :macro_fn ]\n\n    [[try*]\n     localmake \"a1 first :ast\n     ifelse 1 = count :ast [\n      make \"ast :a1         ; TCO\n     ] [\n      localmake \"result nil_new\n      localmake \"result nil_new\n      catch \"error [make \"result _eval :a1 :env]\n      localmake \"exception error\n      ifelse emptyp :exception [\n        output :result\n      ] [\n        localmake \"e item 2 :exception\n        localmake \"exception_obj ifelse :e = \"_mal_exception_ \":global_exception [string_new :e]\n        localmake \"a2 seq_val item 2 :ast\n        localmake \"catchenv env_new :env [] []\n        env_set :catchenv item 2 :a2 :exception_obj\n        make \"env :catchenv\n        make \"ast item 3 :a2 ; TCO\n      ] ]\n    ]\n\n    [[do]\n      foreach :ast [ ; TCO for last item\n        ifelse emptyp ?rest [make \"ast ?] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       make \"ast item 3 :ast ; TCO\n                     ] [\n                       output nil_new\n                     ]]\n        [else        make \"ast item 2 :ast] ; TCO\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n          make \"env fn_gen_env :f map [_eval ? :env] :ast\n          make \"ast fn_body :f ]  ; TCO\n        [[macro]\n          make \"ast macro_apply :f :ast ] ; TCO\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  localmake \"e item 2 :exception\n  ifelse :e = \"_mal_exception_ [\n    (print \"Error: pr_str :global_exception \"false)\n  ] [\n    (print \"Error: :e)\n  ]\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nto mal_eval :a\noutput _eval :a :repl_env\nend\n\nto argv_list\nlocalmake \"argv ifelse emptyp :command.line [[]] [butfirst :command.line]\noutput list_new map \"string_new :argv\nend\n\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\nenv_set :repl_env symbol_new \"eval nativefn_new \"mal_eval\nenv_set :repl_env symbol_new \"*ARGV* argv_list\n\n; core.mal: defined using the language itself\nre \"|(def! not (fn* (a) (if a false true)))|\nre \"|(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))|\nre \"|(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))|\n\nifelse emptyp :command.line [\n  repl\n] [\n  catch \"error [re (word \"|(load-file \"| first :command.line \"|\")| )]\n  print_exception error\n]\n\nbye\n"
  },
  {
    "path": "impls/logo/stepA_mal.lg",
    "content": "load \"../logo/readline.lg\nload \"../logo/reader.lg\nload \"../logo/printer.lg\nload \"../logo/types.lg\nload \"../logo/env.lg\nload \"../logo/core.lg\n\nto _read :str\noutput read_str :str\nend\n\nto quasiquote :ast\ncase obj_type :ast [\n  [[list]       localmake \"xs seq_val ast\n                if not emptyp :xs [if equal_q first :xs symbol_new \"unquote [\n                  output item 2 :xs\n                ]]\n                output qq_seq :xs]\n  [[vector]     output list_new (list symbol_new \"vec qq_seq seq_val :ast)]\n  [[map symbol] output list_new (list symbol_new \"quote :ast)]\n  [else         output :ast]\n]\nend\n\nto qq_seq :xs\nlocalmake \"result list_new []\nforeach reverse :xs [make \"result qq_folder ? :result]\noutput :result\nend\n\nto qq_folder :elt :acc\nif \"list = obj_type :elt [\n  localmake \"ys seq_val :elt\n  if not emptyp :ys [if equal_q first :ys symbol_new \"splice-unquote [\n    output list_new (list symbol_new \"concat item 2 :ys :acc)\n  ]]\n]\noutput list_new (list symbol_new \"cons quasiquote :elt :acc)\nend\n\nto _eval :ast :env\nforever [\nif not memberp obj_type env_get :env symbol_new \"DEBUG-EVAL [false nil notfound] [\n  (print \"EVAL: _print :ast \"/ map \"_print env_keys :env)\n]\n\ncase obj_type :ast [\n\n  [[symbol]\n    localmake \"val env_get :env :ast\n    if \"notfound = obj_type :val [\n      (throw \"error sentence (word \"' symbol_value :ast \"') [not found])\n    ]\n    output :val\n  ]\n\n  [[vector] output vector_new map [_eval ? :env] seq_val :ast]\n\n  [[map] output map_map [_eval ? :env] :ast]\n\n  [[list]\n    make \"ast seq_val :ast\n    if emptyp :ast [output list_new []]\n    localmake \"a0 first :ast\n    make \"ast butfirst :ast\n    case ifelse \"symbol = obj_type :a0 [symbol_value :a0] \"\" [\n\n    [[def!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"val _eval :a2 :env\n      env_set :env :a1 :val\n      output :val ]\n\n    [[let*]\n      localmake \"a1 first :ast\n      localmake \"letenv env_new :env [] []\n      foreach seq_val first :ast [\n        if 1 = modulo # 2 [\n          env_set :letenv ? _eval first ?rest :letenv\n        ]\n      ]\n      make \"env :letenv\n      make \"ast item 2 :ast ]   ; TCO\n\n    [[quote]\n      output first :ast]\n\n    [[quasiquote]\n      make \"ast quasiquote first :ast ] ; TCO\n\n    [[defmacro!]\n      localmake \"a1 first :ast\n      localmake \"a2 item 2 :ast\n      localmake \"macro_fn macro_new _eval :a2 :env\n      env_set :env :a1 :macro_fn\n      output :macro_fn ]\n\n    [[try*]\n     localmake \"a1 first :ast\n     ifelse 1 = count :ast [\n      make \"ast :a1         ; TCO\n     ] [\n      localmake \"result nil_new\n      localmake \"result nil_new\n      catch \"error [make \"result _eval :a1 :env]\n      localmake \"exception error\n      ifelse emptyp :exception [\n        output :result\n      ] [\n        localmake \"e item 2 :exception\n        localmake \"exception_obj ifelse :e = \"_mal_exception_ \":global_exception [string_new :e]\n        localmake \"a2 seq_val item 2 :ast\n        localmake \"catchenv env_new :env [] []\n        env_set :catchenv item 2 :a2 :exception_obj\n        make \"env :catchenv\n        make \"ast item 3 :a2 ; TCO\n      ] ]\n    ]\n\n    [[do]\n      foreach :ast [ ; TCO for last item\n        ifelse emptyp ?rest [make \"ast ?] [ignore _eval ? :env]\n      ]\n    ]\n\n    [[if]\n      localmake \"a1 first :ast\n      localmake \"cond _eval :a1 :env\n      case obj_type :cond [\n        [[nil false] ifelse 3 = count :ast [\n                       make \"ast item 3 :ast ; TCO\n                     ] [\n                       output nil_new\n                     ]]\n        [else        make \"ast item 2 :ast] ; TCO\n      ]]\n\n    [[fn*]\n      output fn_new seq_val first :ast :env item 2 :ast ]\n\n    [else\n      localmake \"f _eval :a0 :env\n      case obj_type :f [\n        [[nativefn]\n          output nativefn_apply :f map [_eval ? :env] :ast ]\n        [[fn]\n          make \"env fn_gen_env :f map [_eval ? :env] :ast\n          make \"ast fn_body :f ]  ; TCO\n        [[macro]\n          make \"ast macro_apply :f :ast ] ; TCO\n        [else\n          (throw \"error [Wrong type for apply])]\n      ] ]\n    ]\n  ]\n  [else output :ast]\n]\n]\nend\n\nto _print :exp\noutput pr_str :exp \"true\nend\n\nto re :str\nignore _eval _read :str :repl_env\nend\n\nto rep :str\noutput _print _eval _read :str :repl_env\nend\n\nto print_exception :exception\nif not emptyp :exception [\n  localmake \"e item 2 :exception\n  ifelse :e = \"_mal_exception_ [\n    (print \"Error: pr_str :global_exception \"false)\n  ] [\n    (print \"Error: :e)\n  ]\n]\nend\n\nto repl\ndo.until [\n    localmake \"line readline \"|user> |\n    if not emptyp :line [\n      catch \"error [print rep :line]\n      print_exception error\n    ]\n] [:line = []]\n(print)\nend\n\nto mal_eval :a\noutput _eval :a :repl_env\nend\n\nto argv_list\nlocalmake \"argv ifelse emptyp :command.line [[]] [butfirst :command.line]\noutput list_new map \"string_new :argv\nend\n\nmake \"repl_env env_new [] [] []\nforeach :core_ns [\n  env_set :repl_env symbol_new ? nativefn_new word \"mal_ ?\n]\nenv_set :repl_env symbol_new \"eval nativefn_new \"mal_eval\nenv_set :repl_env symbol_new \"*ARGV* argv_list\n\n; core.mal: defined using the language itself\nre \"|(def! *host-language* \"logo\")|\nre \"|(def! not (fn* (a) (if a false true)))|\nre \"|(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))|\nre \"|(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))|\n\nifelse emptyp :command.line [\n  re \"|(println (str \"Mal [\" *host-language* \"]\"))|\n  repl\n] [\n  catch \"error [re (word \"|(load-file \"| first :command.line \"|\")| )]\n  print_exception error\n]\n\nbye\n"
  },
  {
    "path": "impls/logo/tests/stepA_mal.mal",
    "content": ";; Testing basic Logo interop\n\n(logo-eval \"7\")\n;=>7\n\n(logo-eval \"\\\"hello\")\n;=>\"hello\"\n\n(logo-eval \"[7 8 9]\")\n;=>(7 8 9)\n\n(logo-eval \"123 = 123\")\n;=>true\n\n(logo-eval \"not emptyp []\")\n;=>false\n\n(logo-eval \"print [hello world]\")\n;/hello world\n;=>nil\n\n(logo-eval \"make \\\"foo 8\")\n(logo-eval \":foo\")\n;=>8\n\n(logo-eval \"apply \\\"word map \\\"reverse [Abc Abcd Abcde]\")\n;=>\"cbAdcbAedcbA\"\n\n(logo-eval \"map [1 + ?] [1 2 3]\")\n;=>(2 3 4)\n"
  },
  {
    "path": "impls/logo/types.lg",
    "content": "; Make Logo's string-comparison case sensitive\nmake \"caseignoredp \"false\n\n; Load the 'case' library macro\ncase \"dummy []\n\n; Redefine 'case' macro to not override caseignoredp\n.macro case :case.value :case.clauses\ncatch \"case.error [output case.helper :case.value :case.clauses]\n(throw \"error [Empty CASE clause])\nend\n\n; For efficiency of env_get and map_get, ensure that MAL equality\n; (equal_q) and LOGO equality (equalp/=) return the same result when\n; an argument is neither a list, map, vector or atom.\n\nto obj_type :obj\noutput ifelse wordp :obj \"\"number [item 1 :obj]\nend\n\nto list_new :val\noutput list \"list :val\nend\n\nto vector_new :val\noutput list \"vector :val\nend\n\nto seq_val :obj\noutput item 2 :obj\nend\n\nto |mal_with-meta| :obj :meta\noutput (listtoarray fput :meta ifelse listp :obj [\n  :obj\n] [\n  butfirst arraytolist :obj\n] 0)\nend\n\nto mal_meta :obj\noutput ifelse listp :obj \"nil_new [item 0 :obj]\nend\n\n; Convenient for map_get and env_get.\n\nmake \"global_notfound [notfound]\n\nto notfound_new\noutput :global_notfound\nend\n\nmake \"global_nil [nil]\n\nto nil_new\noutput :global_nil\nend\n\nmake \"global_false [false]\nmake \"global_true [true]\n\nto bool_to_mal :bool\noutput ifelse :bool \":global_true \":global_false\nend\n\nto number_new :val\noutput :val\nend\n\nto number_val :obj\noutput :obj\nend\n\nto symbol_new :name\noutput list \"symbol :name\nend\n\nto symbol_value :obj\noutput item 2 :obj\nend\n\nto keyword_new :val\noutput list \"keyword :val\nend\n\nto keyword_val :obj\noutput item 2 :obj\nend\n\nto string_new :val\noutput list \"string :val\nend\n\nto string_val :obj\noutput item 2 :obj\nend\n\nto nativefn_new :f\noutput list \"nativefn :f\nend\n\nto nativefn_apply :fn :args\noutput apply item 2 :fn :args\nend\n\nmake \"map_empty [map [] []]\n\nto map_get :map :key\nforeach item 2 :map [if ? = :key [output item # item 3 :map]]\noutput notfound_new\nend\n\n; Returns a new list with the key-val pair set\nto map_assoc :map :pairs\nforeach :pairs [\n  if 1 = modulo # 2 [\n    if memberp ? item 2 :map [make \"map (mal_dissoc :map ?)]\n    make \"map (list \"map fput ? item 2 :map fput first ?rest item 3 :map)\n  ]\n]\noutput :map\nend\n\n; Returns a new list without the key-val pair set\nto mal_dissoc :map [:removals]\nlocalmake \"keys []\nlocalmake \"vals []\n(foreach item 2 :map item 3 :map [\n  if not memberp ?1 :removals [\n    make \"keys fput ?1 :keys\n    make \"vals fput ?2 :vals\n  ]\n])\noutput (list \"map :keys :vals)\nend\n\nto map_keys :map\noutput item 2 :map\nend\n\nto map_vals :map\noutput item 3 :map\nend\n\nto map_map :fn :map\noutput (list \"map item 2 :map map :fn item 3 :map)\nend\n\nto fn_new :args :env :body\nlocalmake \"i difference count :args 1\nif 0 < :i [if equalp symbol_new \"& item :i :args [\n  output (list \"fn :env :body :i filter [# <> :i] :args)\n]]\noutput (list \"fn :env :body 0 :args)\nend\n\nto fn_gen_env :fn :args\nlocalmake \"varargs item 4 :fn\nif :varargs = 0 [output env_new item 2 :fn item 5 :fn :args]\nif :varargs = 1 [output env_new item 2 :fn item 5 :fn (list list_new :args)]\nlocalmake \"new_args array :varargs\nforeach :args [\n  .setitem # :new_args ?\n  if :varargs = # + 1 [\n    .setitem :varargs :new_args list_new ?rest\n    output env_new item 2 :fn item 5 :fn :new_args\n  ]\n]\n(throw \"error [not enough arguments for vararg function])\nend\n\nto fn_apply :fn :args\noutput _eval item 3 :fn fn_gen_env :fn :args\nend\n\nto fn_env :fn\noutput item 2 :fn\nend\n\nto fn_body :fn\noutput item 3 :fn\nend\n\nto macro_new :fn\noutput list \"macro :fn\nend\n\nto macro_apply :fn :args\noutput fn_apply item 2 :fn :args\nend\n\nto mal_atom :value\noutput listtoarray list \"atom :value\nend\n\nto mal_deref :a\noutput item 2 :a\nend\n\nto mal_reset! :a :val\n.setitem 2 :a :val\noutput :val\nend\n"
  },
  {
    "path": "impls/lua/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# luarocks 3.8.0+dfsg1-1 only supports 5.1 5.2 5.3,\n# and its dependencies default on 5.1 if no version is available.\n# Explicitly install the desired version before luarocks.\nRUN apt-get -y install liblua5.3-dev lua5.3\n\nRUN apt-get -y install gcc libpcre3-dev luarocks\n\n# luarocks .cache directory is relative to HOME\nENV HOME /mal\n"
  },
  {
    "path": "impls/lua/Makefile",
    "content": "SOURCES_BASE = utils.lua types.lua reader.lua printer.lua\nSOURCES_LISP = env.lua core.lua stepA_mal.lua\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nlibraries := linenoise.so rex_pcre.so\nlinenoise.so_package := linenoise\nrex_pcre.so_package := lrexlib-pcre\n\nall: $(libraries)\n\ndist: mal.lua mal\n\nSOURCE_NAMES = $(patsubst %.lua,%,$(SOURCES))\nmal.lua: $(SOURCES)\n\techo \"local $(foreach n,$(SOURCE_NAMES),$(n),) M\" > $@\n\techo \"M={} $(foreach n,$(SOURCE_NAMES),$(n)=M);\" >> $@\n\tcat $+ | grep -v -e \"return M$$\" \\\n\t    -e \"return Env\" \\\n\t    -e \"local M =\" \\\n\t    -e \"^#!\" \\\n\t    $(foreach n,$(SOURCE_NAMES),-e \"require('$(n)')\") >> $@\n\nmal: mal.lua\n\techo \"#!/usr/bin/env lua\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f $(libraries) mal.lua mal\n\trm -rf lib\n\n$(libraries):\n\tluarocks install --tree=./ $($@_package)\n\tfind . -name $@ | xargs ln -s\n"
  },
  {
    "path": "impls/lua/core.lua",
    "content": "local utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal readline = require('readline')\n\nlocal Nil, List, HashMap, _pr_str = types.Nil, types.List, types.HashMap, printer._pr_str\n\nlocal M = {}\n\n-- string functions\n\nfunction pr_str(...)\n    return printer._pr_seq(table.pack(...), true, \" \")\nend\n\nfunction str(...)\n    return printer._pr_seq(table.pack(...), false, \"\")\nend\n\nfunction prn(...)\n    print(printer._pr_seq(table.pack(...), true, \" \"))\n    io.flush()\n    return Nil\nend\n\nfunction println(...)\n    print(printer._pr_seq(table.pack(...), false, \" \"))\n    io.flush()\n    return Nil\nend\n\nfunction slurp(file)\n    local lines = {}\n    for line in io.lines(file) do\n        lines[#lines+1] = line\n    end\n    return table.concat(lines, \"\\n\") .. \"\\n\"\nend\n\nfunction do_readline(prompt)\n    local line = readline.readline(prompt)\n    if line == nil then\n        return Nil\n    else\n        return line\n    end\nend\n\n-- hash map functions\n\nfunction assoc(hm, ...)\n    return types._assoc_BANG(types.copy(hm), ...)\nend\n\nfunction dissoc(hm, ...)\n    return types._dissoc_BANG(types.copy(hm), ...)\nend\n\nfunction get(hm, key)\n    local res = hm[key]\n    if res == nil then return Nil end\n    return res\nend\n\nfunction keys(hm)\n    local res = {}\n    for k,v in pairs(hm) do\n        res[#res+1] = k\n    end\n    return List:new(res)\nend\n\nfunction vals(hm)\n    local res = {}\n    for k,v in pairs(hm) do\n        res[#res+1] = v\n    end\n    return List:new(res)\nend\n\n-- sequential functions\n\nfunction cons(a,lst)\n    local new_lst = lst:slice(1)\n    table.insert(new_lst, 1, a)\n    return List:new(new_lst)\nend\n\nfunction concat(...)\n    local arg = table.pack(...)\n    local new_lst = {}\n    for i = 1, #arg do\n        for j = 1, #arg[i] do\n            table.insert(new_lst, arg[i][j])\n        end\n    end\n    return List:new(new_lst)\nend\n\nfunction vec(a)\n    return types.Vector:new(types.copy(a))\nend\n\nfunction nth(seq, idx)\n    if idx+1 <= #seq then\n        return seq[idx+1]\n    else\n        types.throw(\"nth: index out of range\")\n    end\nend\n\nfunction first(a)\n    if #a == 0 then\n        return Nil\n    else\n        return a[1]\n    end\nend\n\nfunction rest(a)\n    if a == Nil then\n        return List:new()\n    else\n        return List:new(a:slice(2))\n    end\nend\n\nfunction apply(f, ...)\n    local arg = table.pack(...)\n    if types._malfunc_Q(f) then\n        f = f.fn\n    end\n    local args = concat(types.slice(arg, 1, #arg-1),\n                        arg[#arg])\n    return f(table.unpack(args))\nend\n\nfunction map(f, lst)\n    if types._malfunc_Q(f) then\n        f = f.fn\n    end\n    return List:new(utils.map(f, lst))\nend\n\n-- metadata functions\n\nfunction meta(obj)\n    local m = getmetatable(obj)\n    if m == nil or m.meta == nil then return Nil end\n    return m.meta\nend\n\nfunction with_meta(obj, meta)\n    local new_obj = types.copy(obj)\n    getmetatable(new_obj).meta = meta\n    return new_obj\nend\n\n-- atom functions\n\nfunction swap_BANG(atm,f,...)\n    if types._malfunc_Q(f) then\n        f = f.fn\n    end\n    local args = List:new(table.pack(...))\n    table.insert(args, 1, atm.val)\n    atm.val = f(table.unpack(args))\n    return atm.val\nend\n\nlocal function conj(obj, ...)\n    local arg = table.pack(...)\n    local new_obj = types.copy(obj)\n    if types._list_Q(new_obj) then\n        for i, v in ipairs(arg) do\n            table.insert(new_obj, 1, v)\n        end\n    else\n        for i, v in ipairs(arg) do\n            table.insert(new_obj, v)\n        end\n    end\n    return new_obj\nend\n\nlocal function seq(obj, ...)\n    if obj == Nil or #obj == 0 then\n        return Nil\n    elseif types._list_Q(obj) then\n        return obj\n    elseif types._vector_Q(obj) then\n        return List:new(obj)\n    elseif types._string_Q(obj) then\n        local chars = {}\n        for i = 1, #obj do\n            chars[#chars+1] = string.sub(obj,i,i)\n        end\n        return List:new(chars)\n    end\n    return Nil\nend\n\nlocal function lua_to_mal(a)\n  if a == nil then\n    return Nil\n  elseif type(a) == \"boolean\" or type(a) == \"number\" or type(a) == \"string\" then\n    return a\n  elseif type(a) == \"table\" then\n    local first_key, _ = next(a)\n    if first_key == nil then\n      return List:new({})\n    elseif type(first_key) == \"number\" then\n      local list = {}\n      for i, v in ipairs(a) do\n        list[i] = lua_to_mal(v)\n      end\n      return List:new(list)\n    else\n      local hashmap = {}\n      for k, v in pairs(a) do\n        hashmap[lua_to_mal(k)] = lua_to_mal(v)\n      end\n      return HashMap:new(hashmap)\n    end\n  end\n  return tostring(a)\nend\n\nlocal function lua_eval(str)\n    local f, err = load(\"return \"..str)\n    if err then\n        types.throw(\"lua-eval: can't load code: \"..err)\n    end\n    return lua_to_mal(f())\nend\n\nM.ns = {\n    ['='] =  types._equal_Q,\n    throw = types.throw,\n\n    ['nil?'] =  function(a) return a==Nil end,\n    ['true?'] =  function(a) return a==true end,\n    ['false?'] =  function(a) return a==false end,\n    ['number?'] = function(a) return types._number_Q(a) end,\n    symbol = function(a) return types.Symbol:new(a) end,\n    ['symbol?'] = function(a) return types._symbol_Q(a) end,\n    ['string?'] = function(a) return types._string_Q(a) end,\n    keyword = function(a)\n        if types._keyword_Q(a) then\n            return a\n        else\n            return types._keyword_from_lua_string(a)\n        end\n    end,\n    ['keyword?'] = function(a) return types._keyword_Q(a) end,\n    ['fn?'] = function(a) return types._fn_Q(a) end,\n    ['macro?'] = function(a) return types._macro_Q(a) end,\n\n    ['pr-str'] = pr_str,\n    str = str,\n    prn = prn,\n    println = println,\n    ['read-string'] = reader.read_str,\n    readline = do_readline,\n    slurp = slurp,\n\n    ['<'] =  function(a,b) return a<b end,\n    ['<='] = function(a,b) return a<=b end,\n    ['>'] =  function(a,b) return a>b end,\n    ['>='] = function(a,b) return a>=b end,\n    ['+'] =  function(a,b) return a+b end,\n    ['-'] =  function(a,b) return a-b end,\n    ['*'] =  function(a,b) return a*b end,\n    ['/'] =  function(a,b) return math.floor(a/b) end,\n    ['time-ms'] = function() return math.floor(os.clock()*1000000) end,\n\n    list = function(...) return List:new(table.pack(...)) end,\n    ['list?'] = function(a) return types._list_Q(a) end,\n    vector = function(...) return types.Vector:new(table.pack(...)) end,\n    ['vector?'] = types._vector_Q,\n    ['hash-map'] = types.hash_map,\n    ['map?'] = types._hash_map_Q,\n    assoc = assoc,\n    dissoc = dissoc,\n    get = get,\n    ['contains?'] = function(a,b) return a[b] ~= nil end,\n    keys = keys,\n    vals = vals,\n\n    ['sequential?'] = types._sequential_Q,\n    cons = cons,\n    concat = concat,\n    vec = vec,\n    nth = nth,\n    first = first,\n    rest = rest,\n    ['empty?'] = function(a) return a==Nil or #a == 0 end,\n    count =  function(a) return #a end,\n    apply = apply,\n    map = map,\n    conj = conj,\n    seq = seq,\n\n    meta = meta,\n    ['with-meta'] = with_meta,\n    atom = function(a) return types.Atom:new(a) end,\n    ['atom?'] = types._atom_Q,\n    deref = function(a) return a.val end,\n    ['reset!'] = function(a,b) a.val = b; return b end,\n    ['swap!'] = swap_BANG,\n\n    ['lua-eval'] = lua_eval,\n}\n\nreturn M\n\n"
  },
  {
    "path": "impls/lua/env.lua",
    "content": "local table = require('table')\nlocal types = require('types')\nlocal printer = require('printer')\n\nlocal Env = {}\n\nfunction Env:new(outer, binds, exprs)\n    -- binds is a MAL sequence of MAL symbols\n    -- exprs is an LUA table of MAL forms\n    local data = {}\n    local newObj = {outer = outer, data = data}\n    self.__index = self\n    if binds then\n        for i, b in ipairs(binds) do\n            if binds[i].val == '&' then\n                data[binds[i+1].val] = types.List.slice(exprs, i)\n                break\n            end\n            data[binds[i].val] = exprs[i]\n        end\n    end\n    return setmetatable(newObj, self)\nend\n\nfunction Env:get(sym)\n    -- sym is an LUA string\n    -- returns nil if the key is not found\n    local env = self\n    local result\n    while true do\n        result = env.data[sym]\n        if result ~= nil then return result end\n        env = env.outer\n        if env == nil then return nil end\n    end\nend\n\nfunction Env:set(sym,val)\n    -- sym is an LUA string\n    self.data[sym] = val\n    return val\nend\n\nfunction Env:debug()\n    local env = self\n    while env.outer ~=nil do\n        line = '  ENV:'\n        for k, v in pairs(env.data) do\n            line = line .. ' ' .. k .. '=' .. printer._pr_str(v)\n        end\n        print(line)\n        env = env.outer\n    end\nend\n\nreturn Env\n"
  },
  {
    "path": "impls/lua/printer.lua",
    "content": "local string = require('string')\nlocal table = require('table')\nlocal types = require('types')\nlocal utils = require('utils')\n\nlocal M = {}\n\nfunction M._pr_str(obj, print_readably)\n    if utils.instanceOf(obj, types.Symbol) then\n        return obj.val\n    elseif types._list_Q(obj) then\n        return \"(\" .. M._pr_seq(obj, print_readably, \" \") .. \")\"\n    elseif types._vector_Q(obj) then\n        return \"[\" .. M._pr_seq(obj, print_readably, \" \") .. \"]\"\n    elseif types._hash_map_Q(obj) then\n        local res = {}\n        for k,v in pairs(obj) do\n            res[#res+1] = M._pr_str(k, print_readably)\n            res[#res+1] = M._pr_str(v, print_readably)\n        end\n        return \"{\".. table.concat(res, \" \")..\"}\"\n    elseif types._keyword_Q(obj) then\n         return ':' .. types._lua_string_from_keyword(obj)\n    elseif types._string_Q(obj) then\n            if print_readably then\n                local sval = obj:gsub('\\\\', '\\\\\\\\')\n                sval = sval:gsub('\"', '\\\\\"')\n                sval = sval:gsub('\\n', '\\\\n')\n                return '\"' .. sval .. '\"'\n            else\n                return obj\n            end\n    elseif obj == types.Nil then\n        return \"nil\"\n    elseif obj == true then\n        return \"true\"\n    elseif obj == false then\n        return \"false\"\n    elseif types._malfunc_Q(obj) then\n        return \"(fn* \"..M._pr_str(obj.params)..\" \"..M._pr_str(obj.ast)..\")\"\n    elseif types._atom_Q(obj) then\n        return \"(atom \"..M._pr_str(obj.val)..\")\"\n    elseif type(obj) == 'function' or types._functionref_Q(obj) then\n        return \"#<function>\"\n    else\n        return string.format(\"%s\", obj)\n    end\nend\n\nfunction M._pr_seq(obj, print_readably, separator)\n    return table.concat(\n        utils.map(function(e) return M._pr_str(e,print_readably) end,\n                  obj),\n        separator)\nend\n\nreturn M\n"
  },
  {
    "path": "impls/lua/reader.lua",
    "content": "local rex = require('rex_pcre')\nlocal string = require('string')\nlocal table = require('table')\nlocal types = require('types')\nlocal throw, Nil, Symbol, List = types.throw, types.Nil,\n                                 types.Symbol, types.List\n\nlocal M = {}\n\nReader = {}\nfunction Reader:new(tokens)\n    local newObj = {tokens = tokens, position = 1}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction Reader:next()\n    self.position = self.position + 1\n    return self.tokens[self.position-1]\nend\nfunction Reader:peek()\n    return self.tokens[self.position]\nend\n\nfunction M.tokenize(str)\n    local results = {}\n    local re_pos = 1\n    local re = rex.new(\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;[^\\n]*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\", rex.flags().EXTENDED)\n    while true do\n        local s, e, t = re:exec(str, re_pos)\n        if not s or s > e then break end\n        re_pos = e + 1\n        local val = string.sub(str,t[1],t[2])\n        if string.sub(val,1,1) ~= \";\" then\n            table.insert(results, val)\n        end\n    end\n    return results\nend\n\nfunction M.read_atom(rdr)\n    local int_re = rex.new(\"^-?[0-9]+$\")\n    local float_re = rex.new(\"^-?[0-9][0-9.]*$\")\n    local string_re = rex.new(\"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\")\n    local token = rdr:next()\n    if int_re:exec(token) then       return tonumber(token)\n    elseif float_re:exec(token) then return tonumber(token)\n    elseif string_re:exec(token) then\n        local sval = string.sub(token,2,string.len(token)-1)\n        sval = string.gsub(sval, '\\\\\\\\', '\\u{029e}')\n        sval = string.gsub(sval, '\\\\\"', '\"')\n        sval = string.gsub(sval, '\\\\n', '\\n')\n        sval = string.gsub(sval, '\\u{029e}', '\\\\')\n        return sval\n    elseif string.sub(token,1,1) == '\"' then\n        throw(\"expected '\\\"', got EOF\")\n    elseif string.sub(token,1,1) == ':' then\n        return types._keyword_from_lua_string(string.sub(token,2))\n    elseif token == \"nil\" then       return Nil\n    elseif token == \"true\" then      return true\n    elseif token == \"false\" then     return false\n    else                             return Symbol:new(token)\n    end\nend\n\nfunction M.read_sequence(rdr, start, last)\n    local ast = {}\n    local token = rdr:next()\n    if token ~= start then throw(\"expected '\"..start..\"'\") end\n\n    token = rdr:peek()\n    while token ~= last do\n        if not token then throw(\"expected '\"..last..\"', got EOF\") end\n        table.insert(ast, M.read_form(rdr))\n        token = rdr:peek()\n    end\n    rdr:next()\n    return ast\nend\n\nfunction M.read_list(rdr)\n    return types.List:new(M.read_sequence(rdr, '(', ')'))\nend\n\nfunction M.read_vector(rdr)\n    return types.Vector:new(M.read_sequence(rdr, '[', ']'))\nend\n\nfunction M.read_hash_map(rdr)\n    local seq = M.read_sequence(rdr, '{', '}')\n    return types.hash_map(table.unpack(seq))\nend\n\nfunction M.read_form(rdr)\n    local token = rdr:peek()\n\n    if \"'\" == token then\n        rdr:next()\n        return List:new({Symbol:new('quote'), M.read_form(rdr)})\n    elseif '`' == token then\n        rdr:next()\n        return List:new({Symbol:new('quasiquote'), M.read_form(rdr)})\n    elseif '~' == token then\n        rdr:next()\n        return List:new({Symbol:new('unquote'), M.read_form(rdr)})\n    elseif '~@' == token then\n        rdr:next()\n        return List:new({Symbol:new('splice-unquote'), M.read_form(rdr)})\n    elseif '^' == token then\n        rdr:next()\n        local meta = M.read_form(rdr)\n        return List:new({Symbol:new('with-meta'), M.read_form(rdr), meta})\n    elseif '@' == token then\n        rdr:next()\n        return List:new({Symbol:new('deref'), M.read_form(rdr)})\n\n    elseif ')' == token then throw(\"unexpected ')'\")\n    elseif '(' == token then return M.read_list(rdr)\n    elseif ']' == token then throw(\"unexpected ']'\")\n    elseif '[' == token then return M.read_vector(rdr)\n    elseif '}' == token then throw(\"unexpected '}'\")\n    elseif '{' == token then return M.read_hash_map(rdr)\n    else return M.read_atom(rdr)\n    end\nend\n\nfunction M.read_str(str)\n    local tokens = M.tokenize(str)\n    if #tokens == 0 then error(nil) end\n    return M.read_form(Reader:new(tokens))\nend\n\nreturn M\n"
  },
  {
    "path": "impls/lua/readline.lua",
    "content": "local LN = require('linenoise')\n\nlocal M = {}\n\nlocal history_loaded = false\nlocal history_file = os.getenv(\"HOME\") .. \"/.mal-history\"\n\nM.raw = false\n\nfunction M.readline(prompt)\n    if not history_loaded then\n        history_loaded = true\n        xpcall(function()\n            for line in io.lines(history_file) do\n                LN.historyadd(line)\n            end\n        end, function(exc)\n            return true -- ignore the error\n        end)\n    end\n\n    if M.raw then\n        io.write(prompt); io.flush();\n        line = io.read()\n    else\n        line = LN.linenoise(prompt)\n    end\n    if line then\n        LN.historyadd(line)\n        xpcall(function()\n            local f = io.open(history_file, \"a\")\n            f:write(line..\"\\n\")\n            f:close()\n        end, function(exc)\n            return true -- ignore the error\n        end)\n    end\n    return line\nend\n\nreturn M\n"
  },
  {
    "path": "impls/lua/run",
    "content": "#!/usr/bin/env bash\nexec lua $(dirname $0)/${STEP:-stepA_mal}.lua \"${@}\"\n"
  },
  {
    "path": "impls/lua/step0_repl.lua",
    "content": "#!/usr/bin/env lua\n\nlocal readline = require('readline')\n\nfunction READ(str)\n    return str\nend\n\nfunction EVAL(ast, any)\n    return ast\nend\n\nfunction PRINT(exp)\n    return exp\nend\n\nfunction rep(str)\n    return PRINT(EVAL(READ(str),\"\"))\nend\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    print(rep(line))\nend\n"
  },
  {
    "path": "impls/lua/step1_read_print.lua",
    "content": "#!/usr/bin/env lua\n\nlocal readline = require('readline')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\nfunction EVAL(ast, env)\n    return ast\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nfunction rep(str)\n    return PRINT(EVAL(READ(str),\"\"))\nend\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step2_eval.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\n\nfunction EVAL(ast, env)\n\n    -- print(\"EVAL: \" .. printer._pr_str(ast, true))\n\n    if types._symbol_Q(ast) then\n        if env[ast.val] == nil then\n            types.throw(\"'\"..ast.val..\"' not found\")\n        end\n        return env[ast.val]\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    local f = EVAL(ast[1], env)\n    local args = types.slice(ast, 2)\n    args = utils.map(function(x) return EVAL(x,env) end, args)\n    return f(table.unpack(args))\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = {['+'] = function(a,b) return a+b end,\n                  ['-'] = function(a,b) return a-b end,\n                  ['*'] = function(a,b) return a*b end,\n                  ['/'] = function(a,b) return math.floor(a/b) end}\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step3_env.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\n\nfunction EVAL(ast, env)\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    local a0,a1,a2 = ast[1], ast[2],ast[3]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        return EVAL(a2, let_env)\n    else\n        local f = EVAL(a0, env)\n        local args = types.slice(ast, 2)\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        return f(table.unpack(args))\n    end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\nrepl_env:set('+', function(a,b) return a+b end)\nrepl_env:set('-', function(a,b) return a-b end)\nrepl_env:set('*', function(a,b) return a*b end)\nrepl_env:set('/', function(a,b) return math.floor(a/b) end)\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step4_if_fn_do.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\n\nfunction EVAL(ast, env)\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        return EVAL(a2, let_env)\n    elseif 'do' == a0sym then\n        local el = utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2))\n        return el[#el]\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then return EVAL(a3, env) else return types.Nil end\n        else\n            return EVAL(a2, env)\n        end\n    elseif 'fn*' == a0sym then\n        return function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end\n    else\n        local f = EVAL(a0, env)\n        local args = types.slice(ast, 2)\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        return f(table.unpack(args))\n    end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\n\n-- core.mal: defined using mal\nrep(\"(def! not (fn* (a) (if a false true)))\")\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step5_tco.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\n\nfunction EVAL(ast, env)\n  while true do\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        env = let_env\n        ast = a2 -- TCO\n    elseif 'do' == a0sym then\n        utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2, #ast - 1))\n        ast = ast[#ast]  -- TCO\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then ast = a3 else return types.Nil end -- TCO\n        else\n            ast = a2 -- TCO\n        end\n    elseif 'fn*' == a0sym then\n        return types.MalFunc:new(function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end, a2, env, a1)\n    else\n        local f = EVAL(a0, env)\n        local args = types.slice(ast, 2)\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        if types._malfunc_Q(f) then\n            ast = f.ast\n            env = Env:new(f.env, f.params, args) -- TCO\n        else\n            return f(table.unpack(args))\n        end\n    end\n  end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\n\n-- core.mal: defined using mal\nrep(\"(def! not (fn* (a) (if a false true)))\")\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step6_file.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\n\nfunction EVAL(ast, env)\n  while true do\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        env = let_env\n        ast = a2 -- TCO\n    elseif 'do' == a0sym then\n        utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2, #ast - 1))\n        ast = ast[#ast]  -- TCO\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then ast = a3 else return types.Nil end -- TCO\n        else\n            ast = a2 -- TCO\n        end\n    elseif 'fn*' == a0sym then\n        return types.MalFunc:new(function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end, a2, env, a1)\n    else\n        local f = EVAL(a0, env)\n        local args = types.slice(ast, 2)\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        if types._malfunc_Q(f) then\n            ast = f.ast\n            env = Env:new(f.env, f.params, args) -- TCO\n        else\n            return f(table.unpack(args))\n        end\n    end\n  end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\nrepl_env:set('eval',\n             function(ast) return EVAL(ast, repl_env) end)\nrepl_env:set('*ARGV*', types.List:new(types.slice(arg,2)))\n\n-- core.mal: defined using mal\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\n    table.remove(arg,1)\nend\n\nif #arg > 0 then\n    rep(\"(load-file \\\"\"..arg[1]..\"\\\")\")\n    os.exit(0)\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step7_quote.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\nfunction starts_with(ast, sym)\n    return 0 < #ast and types._symbol_Q(ast[1]) and ast[1].val == sym\nend\n\nfunction quasiquote_loop(ast)\n   local acc = types.List:new({})\n   for i = #ast,1,-1 do\n      local elt = ast[i]\n      if types._list_Q(elt) and starts_with(elt, \"splice-unquote\") then\n         acc = types.List:new({types.Symbol:new(\"concat\"), elt[2], acc})\n      else\n         acc = types.List:new({types.Symbol:new(\"cons\"), quasiquote(elt), acc})\n      end\n   end\n   return acc\nend\n\nfunction quasiquote(ast)\n    if types._list_Q(ast) then\n        if starts_with(ast, \"unquote\") then\n            return ast[2]\n        else\n            return quasiquote_loop(ast)\n        end\n    elseif types._vector_Q(ast) then\n        return types.List:new({types.Symbol:new(\"vec\"), quasiquote_loop(ast)})\n    elseif types._symbol_Q(ast) or types._hash_map_Q(ast) then\n        return types.List:new({types.Symbol:new(\"quote\"), ast})\n    else\n        return ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true do\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        env = let_env\n        ast = a2 -- TCO\n    elseif 'quote' == a0sym then\n        return a1\n    elseif 'quasiquote' == a0sym then\n        ast = quasiquote(a1) -- TCO\n    elseif 'do' == a0sym then\n        utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2, #ast - 1))\n        ast = ast[#ast]  -- TCO\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then ast = a3 else return types.Nil end -- TCO\n        else\n            ast = a2 -- TCO\n        end\n    elseif 'fn*' == a0sym then\n        return types.MalFunc:new(function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end, a2, env, a1)\n    else\n        local f = EVAL(a0, env)\n        local args = types.slice(ast, 2)\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        if types._malfunc_Q(f) then\n            ast = f.ast\n            env = Env:new(f.env, f.params, args) -- TCO\n        else\n            return f(table.unpack(args))\n        end\n    end\n  end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\nrepl_env:set('eval',\n             function(ast) return EVAL(ast, repl_env) end)\nrepl_env:set('*ARGV*', types.List:new(types.slice(arg,2)))\n\n-- core.mal: defined using mal\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\n    table.remove(arg,1)\nend\n\nif #arg > 0 then\n    rep(\"(load-file \\\"\"..arg[1]..\"\\\")\")\n    os.exit(0)\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step8_macros.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\nfunction starts_with(ast, sym)\n    return 0 < #ast and types._symbol_Q(ast[1]) and ast[1].val == sym\nend\n\nfunction quasiquote_loop(ast)\n   local acc = types.List:new({})\n   for i = #ast,1,-1 do\n      local elt = ast[i]\n      if types._list_Q(elt) and starts_with(elt, \"splice-unquote\") then\n         acc = types.List:new({types.Symbol:new(\"concat\"), elt[2], acc})\n      else\n         acc = types.List:new({types.Symbol:new(\"cons\"), quasiquote(elt), acc})\n      end\n   end\n   return acc\nend\n\nfunction quasiquote(ast)\n    if types._list_Q(ast) then\n        if starts_with(ast, \"unquote\") then\n            return ast[2]\n        else\n            return quasiquote_loop(ast)\n        end\n    elseif types._vector_Q(ast) then\n        return types.List:new({types.Symbol:new(\"vec\"), quasiquote_loop(ast)})\n    elseif types._symbol_Q(ast) or types._hash_map_Q(ast) then\n        return types.List:new({types.Symbol:new(\"quote\"), ast})\n    else\n        return ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true do\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    -- apply list\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        env = let_env\n        ast = a2 -- TCO\n    elseif 'quote' == a0sym then\n        return a1\n    elseif 'quasiquote' == a0sym then\n        ast = quasiquote(a1) -- TCO\n    elseif 'defmacro!' == a0sym then\n        local mac = types.copy(EVAL(a2, env))\n        mac.ismacro = true\n        return env:set(a1.val, mac)\n    elseif 'do' == a0sym then\n        utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2, #ast - 1))\n        ast = ast[#ast]  -- TCO\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then ast = a3 else return types.Nil end -- TCO\n        else\n            ast = a2 -- TCO\n        end\n    elseif 'fn*' == a0sym then\n        return types.MalFunc:new(function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end, a2, env, a1)\n    else\n      local f = EVAL(a0, env)\n      local args = types.slice(ast, 2)\n      if types._macro_Q(f) then\n        ast = f.fn(table.unpack(args)) -- TCO\n      else\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        if types._malfunc_Q(f) then\n            ast = f.ast\n            env = Env:new(f.env, f.params, args) -- TCO\n        else\n            return f(table.unpack(args))\n        end\n      end\n    end\n  end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\nrepl_env:set('eval',\n             function(ast) return EVAL(ast, repl_env) end)\nrepl_env:set('*ARGV*', types.List:new(types.slice(arg,2)))\n\n-- core.mal: defined using mal\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\n    table.remove(arg,1)\nend\n\nif #arg > 0 then\n    rep(\"(load-file \\\"\"..arg[1]..\"\\\")\")\n    os.exit(0)\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function()\n        print(rep(line))\n    end, function(exc)\n        if exc then\n            if types._malexception_Q(exc) then\n                exc = printer._pr_str(exc.val, true)\n            end\n            print(\"Error: \" .. exc)\n            print(debug.traceback())\n        end\n    end)\nend\n"
  },
  {
    "path": "impls/lua/step9_try.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\nfunction starts_with(ast, sym)\n    return 0 < #ast and types._symbol_Q(ast[1]) and ast[1].val == sym\nend\n\nfunction quasiquote_loop(ast)\n   local acc = types.List:new({})\n   for i = #ast,1,-1 do\n      local elt = ast[i]\n      if types._list_Q(elt) and starts_with(elt, \"splice-unquote\") then\n         acc = types.List:new({types.Symbol:new(\"concat\"), elt[2], acc})\n      else\n         acc = types.List:new({types.Symbol:new(\"cons\"), quasiquote(elt), acc})\n      end\n   end\n   return acc\nend\n\nfunction quasiquote(ast)\n    if types._list_Q(ast) then\n        if starts_with(ast, \"unquote\") then\n            return ast[2]\n        else\n            return quasiquote_loop(ast)\n        end\n    elseif types._vector_Q(ast) then\n        return types.List:new({types.Symbol:new(\"vec\"), quasiquote_loop(ast)})\n    elseif types._symbol_Q(ast) or types._hash_map_Q(ast) then\n        return types.List:new({types.Symbol:new(\"quote\"), ast})\n    else\n        return ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true do\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    -- apply list\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        env = let_env\n        ast = a2 -- TCO\n    elseif 'quote' == a0sym then\n        return a1\n    elseif 'quasiquote' == a0sym then\n        ast = quasiquote(a1) -- TCO\n    elseif 'defmacro!' == a0sym then\n        local mac = types.copy(EVAL(a2, env))\n        mac.ismacro = true\n        return env:set(a1.val, mac)\n    elseif 'try*' == a0sym then\n      if a2 == nil or a2[1].val ~= 'catch*' then\n        ast = a1 -- TCO\n      else\n        local exc, result = nil, nil\n        xpcall(function()\n            result = EVAL(a1, env)\n        end, function(err)\n            exc = err\n        end)\n        if exc == nil then\n            return result\n        else\n            if types._malexception_Q(exc) then\n                exc = exc.val\n            end\n            ast, env = a2[3], Env:new(env, {a2[2]}, {exc}) -- TCO\n        end\n      end\n    elseif 'do' == a0sym then\n        utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2, #ast - 1))\n        ast = ast[#ast]  -- TCO\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then ast = a3 else return types.Nil end -- TCO\n        else\n            ast = a2 -- TCO\n        end\n    elseif 'fn*' == a0sym then\n        return types.MalFunc:new(function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end, a2, env, a1)\n    else\n      local f = EVAL(a0, env)\n      local args = types.slice(ast, 2)\n      if types._macro_Q(f) then\n        ast = f.fn(table.unpack(args)) -- TCO\n      else\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        if types._malfunc_Q(f) then\n            ast = f.ast\n            env = Env:new(f.env, f.params, args) -- TCO\n        else\n            return f(table.unpack(args))\n        end\n      end\n    end\n  end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\nrepl_env:set('eval',\n             function(ast) return EVAL(ast, repl_env) end)\nrepl_env:set('*ARGV*', types.List:new(types.slice(arg,2)))\n\n-- core.mal: defined using mal\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nfunction print_exception(exc)\n    if exc then\n        if types._malexception_Q(exc) then\n            exc = printer._pr_str(exc.val, true)\n        end\n        print(\"Error: \" .. exc)\n        print(debug.traceback())\n    end\nend\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\n    table.remove(arg,1)\nend\n\nif #arg > 0 then\n    xpcall(function() rep(\"(load-file \\\"\"..arg[1]..\"\\\")\") end,\n           print_exception)\n    os.exit(0)\nend\n\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function() print(rep(line)) end,\n           print_exception)\nend\n"
  },
  {
    "path": "impls/lua/stepA_mal.lua",
    "content": "#!/usr/bin/env lua\n\nlocal table = require('table')\n\npackage.path = '../lua/?.lua;' .. package.path\nlocal readline = require('readline')\nlocal utils = require('utils')\nlocal types = require('types')\nlocal reader = require('reader')\nlocal printer = require('printer')\nlocal Env = require('env')\nlocal core = require('core')\nlocal List, Vector, HashMap = types.List, types.Vector, types.HashMap\n\n-- read\nfunction READ(str)\n    return reader.read_str(str)\nend\n\n-- eval\nfunction starts_with(ast, sym)\n    return 0 < #ast and types._symbol_Q(ast[1]) and ast[1].val == sym\nend\n\nfunction quasiquote_loop(ast)\n   local acc = types.List:new({})\n   for i = #ast,1,-1 do\n      local elt = ast[i]\n      if types._list_Q(elt) and starts_with(elt, \"splice-unquote\") then\n         acc = types.List:new({types.Symbol:new(\"concat\"), elt[2], acc})\n      else\n         acc = types.List:new({types.Symbol:new(\"cons\"), quasiquote(elt), acc})\n      end\n   end\n   return acc\nend\n\nfunction quasiquote(ast)\n    if types._list_Q(ast) then\n        if starts_with(ast, \"unquote\") then\n            return ast[2]\n        else\n            return quasiquote_loop(ast)\n        end\n    elseif types._vector_Q(ast) then\n        return types.List:new({types.Symbol:new(\"vec\"), quasiquote_loop(ast)})\n    elseif types._symbol_Q(ast) or types._hash_map_Q(ast) then\n        return types.List:new({types.Symbol:new(\"quote\"), ast})\n    else\n        return ast\n    end\nend\n\nfunction EVAL(ast, env)\n  while true do\n\n    local dbgeval = env:get(\"DEBUG-EVAL\")\n    if dbgeval ~= nil and dbgeval ~= types.Nil and dbgeval ~= false then\n        print(\"EVAL: \" .. printer._pr_str(ast, true))\n        env:debug()\n    end\n\n    if types._symbol_Q(ast) then\n        local result = env:get(ast.val)\n        if result == nil then\n            types.throw(\"'\" .. ast.val .. \"' not found\")\n        end\n        return result\n    elseif types._vector_Q(ast) then\n        return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast))\n    elseif types._hash_map_Q(ast) then\n        local new_hm = {}\n        for k,v in pairs(ast) do\n            new_hm[k] = EVAL(v, env)\n        end\n        return HashMap:new(new_hm)\n    elseif not types._list_Q(ast) or #ast == 0 then\n        return ast\n    end\n\n    -- apply list\n\n    local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4]\n    local a0sym = types._symbol_Q(a0) and a0.val or \"\"\n    if 'def!' == a0sym then\n        return env:set(a1.val, EVAL(a2, env))\n    elseif 'let*' == a0sym then\n        local let_env = Env:new(env)\n        for i = 1,#a1,2 do\n            let_env:set(a1[i].val, EVAL(a1[i+1], let_env))\n        end\n        env = let_env\n        ast = a2 -- TCO\n    elseif 'quote' == a0sym then\n        return a1\n    elseif 'quasiquote' == a0sym then\n        ast = quasiquote(a1) -- TCO\n    elseif 'defmacro!' == a0sym then\n        local mac = types.copy(EVAL(a2, env))\n        mac.ismacro = true\n        return env:set(a1.val, mac)\n    elseif 'try*' == a0sym then\n      if a2 == nil or a2[1].val ~= 'catch*' then\n        ast = a1 -- TCO\n      else\n        local exc, result = nil, nil\n        xpcall(function()\n            result = EVAL(a1, env)\n        end, function(err)\n            exc = err\n        end)\n        if exc == nil then\n            return result\n        else\n            if types._malexception_Q(exc) then\n                exc = exc.val\n            end\n            ast, env = a2[3], Env:new(env, {a2[2]}, {exc}) -- TCO\n        end\n      end\n    elseif 'do' == a0sym then\n        utils.map(function(x) return EVAL(x, env) end, types.slice(ast, 2, #ast - 1))\n        ast = ast[#ast]  -- TCO\n    elseif 'if' == a0sym then\n        local cond = EVAL(a1, env)\n        if cond == types.Nil or cond == false then\n            if #ast > 3 then ast = a3 else return types.Nil end -- TCO\n        else\n            ast = a2 -- TCO\n        end\n    elseif 'fn*' == a0sym then\n        return types.MalFunc:new(function(...)\n            return EVAL(a2, Env:new(env, a1, table.pack(...)))\n        end, a2, env, a1)\n    else\n      local f = EVAL(a0, env)\n      local args = types.slice(ast, 2)\n      if types._macro_Q(f) then\n        ast = f.fn(table.unpack(args)) -- TCO\n      else\n        args = utils.map(function(x) return EVAL(x,env) end, args)\n        if types._malfunc_Q(f) then\n            ast = f.ast\n            env = Env:new(f.env, f.params, args) -- TCO\n        else\n            return f(table.unpack(args))\n        end\n      end\n    end\n  end\nend\n\n-- print\nfunction PRINT(exp)\n    return printer._pr_str(exp, true)\nend\n\n-- repl\nlocal repl_env = Env:new()\nfunction rep(str)\n    return PRINT(EVAL(READ(str),repl_env))\nend\n\n-- core.lua: defined using Lua\nfor k,v in pairs(core.ns) do\n    repl_env:set(k, v)\nend\nrepl_env:set('eval',\n             function(ast) return EVAL(ast, repl_env) end)\nrepl_env:set('*ARGV*', types.List:new(types.slice(arg,2)))\n\n-- core.mal: defined using mal\nrep(\"(def! *host-language* \\\"lua\\\")\")\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\nfunction print_exception(exc)\n    if exc then\n        if types._malexception_Q(exc) then\n            exc = printer._pr_str(exc.val, true)\n        end\n        print(\"Error: \" .. exc)\n        print(debug.traceback())\n    end\nend\n\nif #arg > 0 and arg[1] == \"--raw\" then\n    readline.raw = true\n    table.remove(arg,1)\nend\n\nif #arg > 0 then\n    xpcall(function() rep(\"(load-file \\\"\"..arg[1]..\"\\\")\") end,\n           print_exception)\n    os.exit(0)\nend\n\nrep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nwhile true do\n    line = readline.readline(\"user> \")\n    if not line then break end\n    xpcall(function() print(rep(line)) end,\n           print_exception)\nend\n"
  },
  {
    "path": "impls/lua/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 100000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/lua/tests/stepA_mal.mal",
    "content": ";; Testing basic Lua interop\n\n;;; lua-eval adds the string \"return \" to the beginning of the evaluated string\n;;; and supplies that to Lua's loadstring().  If complex programs are needed,\n;;; those can be wrapped by an anonymous function which is called immediately\n;;; (see the foo = 8 example below).\n\n(lua-eval \"7\")\n;=>7\n\n(lua-eval \"'7'\")\n;=>\"7\"\n\n(lua-eval \"123 == 123\")\n;=>true\n\n(lua-eval \"123 == 456\")\n;=>false\n\n(lua-eval \"{7,8,9}\")\n;=>(7 8 9)\n\n(lua-eval \"{abc = 789}\")\n;=>{\"abc\" 789}\n\n(lua-eval \"print('hello')\")\n;/hello\n;=>nil\n\n(lua-eval \"(function() foo = 8 end)()\")\n(lua-eval \"foo\")\n;=>8\n\n(lua-eval \"string.gsub('This sentence has five words', '%w+', function(w) return '*'..#w..'*' end)\")\n;=>\"*4* *8* *3* *4* *5*\"\n\n(lua-eval \"table.concat({3, 'a', 45, 'b'}, '|')\")\n;=>\"3|a|45|b\"\n"
  },
  {
    "path": "impls/lua/types.lua",
    "content": "local utils = require('utils')\n\nlocal M = {}\n\n-- type functions\n\nfunction M._sequential_Q(obj)\n    return M._list_Q(obj) or M._vector_Q(obj)\nend\n\nfunction M._equal_Q(a,b)\n    if M._symbol_Q(a) and M._symbol_Q(b) then\n        return a.val == b.val\n    elseif M._sequential_Q(a) and M._sequential_Q(b) then\n        if #a ~= #b then return false end\n        for i, v in ipairs(a) do\n            if not M._equal_Q(v,b[i]) then return false end\n        end\n        return true\n    elseif M._hash_map_Q(a) and M._hash_map_Q(b) then\n        if #a ~= #b then return false end\n        for k, v in pairs(a) do\n            if not M._equal_Q(v,b[k]) then return false end\n        end\n        return true\n    else\n        return a == b\n    end\nend\n\nfunction M.copy(obj)\n    if type(obj) == \"function\" then\n        return M.FunctionRef:new(obj)\n    end\n    if type(obj) ~= \"table\" then return obj end\n\n    -- copy object data\n    local new_obj = {}\n    for k,v in pairs(obj) do\n        new_obj[k] = v\n    end\n\n    -- copy metatable and link to original\n    local old_mt = getmetatable(obj)\n    if old_mt ~= nil then\n        local new_mt = {}\n        for k,v in pairs(old_mt) do\n            new_mt[k] = v\n        end\n        setmetatable(new_mt, old_mt)\n        setmetatable(new_obj, new_mt)\n    end\n\n    return new_obj\nend\n\nfunction M.slice(lst, start, last)\n    if last == nil then last = #lst end\n    local new_lst = {}\n    if start <= last then\n        for i = start, last do\n            new_lst[#new_lst+1] = lst[i]\n        end\n    end\n    return new_lst\nend\n\n-- Error/exceptions\n\nM.MalException = {}\nfunction M.MalException:new(val)\n    local newObj = {val = val}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M._malexception_Q(obj)\n    return utils.instanceOf(obj, M.MalException)\nend\n\nfunction M.throw(val)\n    error(M.MalException:new(val))\nend\n\n-- Nil\n\nlocal NilType = {}\nfunction NilType:new(val)\n    local newObj = {}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nM.Nil = NilType:new()\nfunction M._nil_Q(obj)\n    return obj == Nil\nend\n\n-- Numbers\nfunction M._number_Q(obj)\n    return type(obj) == \"number\"\nend\n\n-- Strings\nfunction M._string_Q(obj)\n    return type(obj) == \"string\" and \"\\u{029e}\" ~= string.sub(obj,1,2)\nend\n\n-- Symbols\n\nM.Symbol = {}\nfunction M.Symbol:new(val)\n    local newObj = {val = val}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M._symbol_Q(obj)\n    return utils.instanceOf(obj, M.Symbol)\nend\n\n-- Keywords\n-- 5.1 does not support unicode escapes.  Their length vary between 5.3 and 5.4.\n_keyword_mark = \"\\u{029e}\"  -- Two bytes.\n_keyword_mark_len = string.len(_keyword_mark)\n\nfunction M._keyword_Q(obj)\n    return type(obj) == \"string\" and _keyword_mark == string.sub(obj,1,_keyword_mark_len)\nend\n\nfunction M._keyword_from_lua_string(value)\n    return _keyword_mark .. value\nend\n\nfunction M._lua_string_from_keyword(obj)\n    return string.sub(obj, _keyword_mark_len + 1)\nend\n\n\n-- Lists\n\nM.List = {}\nfunction M.List:new(lst)\n    local newObj = lst and lst or {}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M._list_Q(obj)\n    return utils.instanceOf(obj, M.List)\nend\nfunction M.List:slice(start,last)\n    return M.List:new(M.slice(self,start,last))\nend\n\n-- Vectors\n\nM.Vector = {}\nfunction M.Vector:new(lst)\n    local newObj = lst and lst or {}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M._vector_Q(obj)\n    return utils.instanceOf(obj, M.Vector)\nend\nfunction M.Vector:slice(start,last)\n    return M.Vector:new(M.slice(self,start,last))\nend\n\n-- Hash Maps\n--\nM.HashMap = {}\nfunction M.HashMap:new(val)\n    local newObj = val and val or {}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M.hash_map(...)\n    return M._assoc_BANG(M.HashMap:new(), ...)\nend\nfunction M._hash_map_Q(obj)\n    return utils.instanceOf(obj, M.HashMap)\nend\nfunction M._assoc_BANG(hm, ...)\n    local arg = table.pack(...)\n    for i = 1, #arg, 2 do\n        hm[arg[i]] = arg[i+1]\n    end\n    return hm\nend\nfunction M._dissoc_BANG(hm, ...)\n    local arg = table.pack(...)\n    for i = 1, #arg do\n        hm[arg[i]] = nil\n    end\n    return hm\nend\n\n-- Functions\n\nM.MalFunc = {}\nfunction M.MalFunc:new(fn, ast, env, params)\n    local newObj = {fn = fn, ast = ast, env = env,\n                    params = params, ismacro = false}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M._malfunc_Q(obj)\n    return utils.instanceOf(obj, M.MalFunc)\nend\nfunction M._fn_Q(obj)\n    return type(obj) == \"function\" or (M._malfunc_Q(obj) and not obj.ismacro)\nend\nfunction M._macro_Q(obj)\n    return M._malfunc_Q(obj) and obj.ismacro\nend\n\n-- Atoms\n\nM.Atom = {}\nfunction M.Atom:new(val)\n    local newObj = {val = val}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\nfunction M._atom_Q(obj)\n    return utils.instanceOf(obj, M.Atom)\nend\n\n-- FunctionRefs\n\nM.FunctionRef = {}\nfunction M.FunctionRef:new(fn)\n    local newObj = {fn = fn}\n    return setmetatable(newObj, self)\nend\nfunction M._functionref_Q(obj)\n    return utils.instanceOf(obj, M.FunctionRef)\nend\nfunction M.FunctionRef:__call(...)\n    return self.fn(...)\nend\n\nreturn M\n"
  },
  {
    "path": "impls/lua/utils.lua",
    "content": "local M = {}\n\nfunction M.try(f, catch_f)\n    local status, exception = pcall(f)\n    if not status then\n        catch_f(exception)\n    end\nend\n\nfunction M.instanceOf(subject, super)\n    super = tostring(super)\n    local mt = getmetatable(subject)\n\n    while true do\n        if mt == nil then return false end\n        if tostring(mt) == super then return true end\n        mt = getmetatable(mt)\n    end\nend\n\n--[[\nfunction M.isArray(o)\n    local i = 0\n    for _ in pairs(o) do\n        i = i + 1\n        if o[i] == nil then return false end\n    end\n    return true\nend\n]]--\n\nfunction M.map(func, obj)\n    local new_obj = {}\n    for i,v in ipairs(obj) do\n        new_obj[i] = func(v)\n    end\n    return new_obj\nend\n\nfunction M.dump(o)\n   if type(o) == 'table' then\n      local s = '{ '\n      for k,v in pairs(o) do\n         if type(k) ~= 'number' then k = '\"'..k..'\"' end\n         s = s .. '['..k..'] = ' .. M.dump(v) .. ','\n      end\n      return s .. '} '\n   else\n      return tostring(o)\n   end\nend\n\nreturn M\n"
  },
  {
    "path": "impls/make/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Nothing additional needed for make\n"
  },
  {
    "path": "impls/make/Makefile",
    "content": "\nTESTS = tests/types.mk tests/reader.mk tests/stepA_mal.mk\n\nSOURCES_BASE = util.mk numbers.mk readline.mk gmsl.mk types.mk \\\n               reader.mk printer.mk\nSOURCES_LISP = env.mk core.mk stepA_mal.mk\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.mk mal\n\nmal.mk: $(SOURCES)\n\tcat $+ | grep -v \"^include \" > $@\n\nmal: mal.mk\n\techo \"#!/usr/bin/make -f\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.mk mal\n\n.PHONY: tests $(TESTS)\n\ntests: $(TESTS)\n\n$(TESTS):\n\t@echo \"Running $@\"; \\\n\tmake -f $@ || exit 1; \\\n"
  },
  {
    "path": "impls/make/README",
    "content": "It is often useful to add $(warning /$0/ /$1/ /$2/ /$3/) at the very\nstart of each interesting macro.\n\nRecal that foreach does nothing when the list only contains spaces,\nand adds spaces between the results even if some results are empty.\n\nIf debugging the reader:\n# export READER_DEBUG=1\n\nIn order to get the equivalent of DEBUG_EVAL in step2:\n# export EVAL_DEBUG=1\n"
  },
  {
    "path": "impls/make/core.mk",
    "content": "#\n# mal (Make a Lisp) Core functions\n#\n\nifndef __mal_core_included\n__mal_core_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\n\n\n# General functions\n\n$(encoded_equal) = $(if $(call _equal?,$(firstword $1),$(lastword $1)),$(__true),$(__false))\n\n\n# Scalar functions\nnil? = $(if $(_nil?),$(__true),$(__false))\ntrue? = $(if $(_true?),$(__true),$(__false))\nfalse? = $(if $(_false?),$(__true),$(__false))\n\n\n# Symbol functions\nsymbol = $(call _symbol,$(_string_val))\nsymbol? = $(if $(_symbol?),$(__true),$(__false))\n\n# Keyword functions\nkeyword = $(if $(_keyword?),$1,$(call _keyword,$(_string_val)))\nkeyword? = $(if $(_keyword?),$(__true),$(__false))\n\n\n# Number functions\nnumber? = $(if $(_number?),$(__true),$(__false))\n\ndefine <\n$(if $(call int_lt,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1)))\\\n  ,$(__true),$(__false))\nendef\ndefine <$(encoded_equal)\n$(if $(call int_lte,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1)))\\\n  ,$(__true),$(__false))\nendef\ndefine >\n$(if $(call int_gt,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1)))\\\n  ,$(__true),$(__false))\nendef\ndefine >$(encoded_equal)\n$(if $(call int_gte,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1)))\\\n  ,$(__true),$(__false))\nendef\n\n+ = $(call _number,$(call int_add,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1))))\n- = $(call _number,$(call int_sub,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1))))\n* = $(call _number,$(call int_mult,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1))))\n/ = $(call _number,$(call int_div,$(call _number_val,$(firstword $1)),$(call _number_val,$(lastword $1))))\n\ntime-ms = $(call _number,$(shell date +%s%3N))\n\n# String functions\n\nstring? = $(if $(_string?),$(__true),$(__false))\n\npr-str  = $(call _string,$(call _pr_str_mult,$1,yes,$(_SP)))\nstr     = $(call _string,$(_pr_str_mult))\nprn     = $(__nil)$(call print,$(call _pr_str_mult,$1,yes,$(_SP)))\nprintln = $(__nil)$(call print,$(call _pr_str_mult,$1,,$(_SP)))\n\nreadline = $(or $(foreach res,$(call READLINE,$(_string_val))\\\n                  ,$(call _string,$(res:ok=)))\\\n                ,$(__nil))\nread-string = $(call READ_STR,$(_string_val))\nslurp = $(call _string,$(call _read_file,$(_string_val)))\n\n\n\n# Function functions\nfn? = $(if $(_fn?),$(__true),$(__false))\nmacro? = $(if $(_macro?),$(__true),$(__false))\n\n\n# List functions\nlist? = $(if $(_list?),$(__true),$(__false))\n\n\n# Vector functions\nvector? = $(if $(_vector?),$(__true),$(__false))\n\nvec = $(if $(_list?)\\\n  ,$(call vector,$(_seq_vals))$(rem \\\n),$(if $(_vector?)\\\n  ,$1$(rem \\\n),$(call _error,vec$(encoded_colon)$(_SP)called$(_SP)on$(_SP)non-sequence)))\n\n\n# Hash map (associative array) functions\nhash-map = $(call _map_new,,$1)\nmap? = $(if $(_hash_map?),$(__true),$(__false))\n\n# set a key/value in a copy of the hash map\nassoc = $(call _map_new,$(firstword $1),$(_rest))\n\n# unset keys in a copy of the hash map\ndissoc = $(call _map_new,$(firstword $1),,$(_rest))\n\nkeys = $(call list,$(_keys))\n\nvals = $(call list,$(foreach k,$(_keys),$(call _get,$1,$k)))\n\n# retrieve the value of a string key object from the hash map, or\n# return nil if the key is not found.\nget = $(or $(call _get,$(firstword $1),$(lastword $1)),$(__nil))\n\ncontains? = $(if $(call _get,$(firstword $1),$(lastword $1)),$(__true),$(__false))\n\n\n# sequence operations\n\nsequential? = $(if $(_sequential?),$(__true),$(__false))\n\n# Strip in case seq_vals is empty.\ncons = $(call list,$(strip $(firstword $1) $(call _seq_vals,$(lastword $1))))\n\n# Strip in case foreach introduces a space after an empty argument.\nconcat = $(call list,$(strip $(foreach l,$1,$(call _seq_vals,$l))))\n\nnth = $(or $(word $(call int_add,1,$(call _number_val,$(lastword $1))),\\\n                  $(call _seq_vals,$(firstword $1)))\\\n          ,$(call _error,nth: index out of range))\n\nfirst = $(or $(if $(_sequential?),$(firstword $(_seq_vals))),$(__nil))\n\nempty? = $(if $(_seq_vals),$(__false),$(__true))\n\ncount = $(call _number,$(words $(if $(_sequential?),$(_seq_vals))))\n\n# Creates a new vector/list of the everything after but the first\n# element\nrest = $(call list,$(if $(_sequential?),$(call _rest,$(_seq_vals))))\n\n# Takes a space separated arguments and invokes the first argument\n# (function object) using the remaining arguments.\n# Strip in case wordlist or _seq_vals is empty.\napply = $(call _apply,$(firstword $1),$(strip \\\n  $(wordlist 2,$(call int_sub,$(words $1),1),$1) \\\n  $(call _seq_vals,$(lastword $1))))\n\n# Map a function object over a list object\nmap = $(call list,$(foreach e,$(call _seq_vals,$(lastword $1))\\\n  ,$(call _apply,$(firstword $1),$e)))\n\nconj = $(foreach seq,$(firstword $1)\\\n  ,$(call conj_$(call _obj_type,$(seq)),$(call _seq_vals,$(seq)),$(_rest)))\n# Strip in case $1 or $2 is empty.\n# Also, _reverse introduces blanks.\nconj_vector = $(call vector,$(strip $1 $2))\nconj_list   = $(call list,$(strip $(call _reverse,$2) $1))\n\nseq = $(or $(seq_$(_obj_type))\\\n          ,$(call _error,seq: called on non-sequence))\nseq_list = $(if $(_seq_vals),$1,$(__nil))\nseq_vector = $(if $(_seq_vals),$(call list,$(_seq_vals)),$(__nil))\nseq_nil = $1\nseq_string = $(if $(_string_val)\\\n               ,$(call list,$(foreach c,$(call str_encode,$(_string_val))\\\n                 ,$(call _string,$(call str_decode,$c))))$(rem \\\n               ),$(__nil))\n\n# Metadata functions\n\n# are implemented in types.mk.\n\n\n# Atom functions\n\natom? = $(if $(_atom?),$(__true),$(__false))\n\nreset! = $(foreach v,$(lastword $1),$(call _reset,$(firstword $1),$v)$v)\n\nswap! = $(foreach a,$(firstword $1)\\\n  ,$(call reset!,$a $(call _apply,$(word 2,$1),$(call deref,$a) $(_rest2))))\n\n\n\n\n# Namespace of core functions\n\ncore_ns := $(encoded_equal) throw nil?  true?  false?  string?  symbol \\\n  symbol?  keyword keyword?  number?  fn?  macro? \\\n  pr-str str prn println readline read-string slurp \\ < \\\n  <$(encoded_equal) > >$(encoded_equal) + - * / time-ms \\\n  list list?  vector vector?  hash-map map?  assoc dissoc get \\\n  contains? keys vals \\\n  sequential? cons concat vec nth first rest empty? count apply map \\\n  conj seq \\\n  with-meta meta atom atom? deref reset! swap!\n\nendif\n"
  },
  {
    "path": "impls/make/env.mk",
    "content": "#\n# mal (Make Lisp) Object Types and Functions\n#\n\nifndef __mal_env_included\n__mal_env_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)types.mk\n\n#\n# ENV\n#\n\n# An ENV environment is a hash-map with an __outer__ reference to an\n# outer environment\n\n# Keys are stored as Make variables named $(env)_$(key).  The outer\n# environment is the content of the variable itself.\n\n# 1: outer environment, or \"\"  ->    new environment\nENV = $(call __new_obj,env,$1)\n\n# 1:env  2:key  ->  value or \"\"\nENV_GET = $(if $1,$(or $($1_$2),$(call ENV_GET,$($1),$2)))\n\n# 1:env  2:key  3:value\nENV_SET = $(eval $1_$2 := $3)\n\n# 1:env  ->  (encoded) keys\nenv_keys = $(foreach k,$(patsubst $1_%,%,$(filter $1_%,$(.VARIABLES)))\\\n             ,$(call _symbol_val,$k))\n\nendif\n"
  },
  {
    "path": "impls/make/gmsl.mk",
    "content": "#\n# mal (Make Lisp) trimmed and namespaced GMSL functions/definitions\n# \t- derived from the GMSL 1.1.3\n#\n\nifndef __mal_gmsl_included\n__mal_gmsl_included := true\n\n# ----------------------------------------------------------------------------\n#\n# GNU Make Standard Library (GMSL)\n#\n# A library of functions to be used with GNU Make's $(call) that\n# provides functionality not available in standard GNU Make.\n#\n# Copyright (c) 2005-2013 John Graham-Cumming\n#\n# This file is part of GMSL\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions\n# are met:\n#\n# Redistributions of source code must retain the above copyright\n# notice, this list of conditions and the following disclaimer.\n# \n# Redistributions in binary form must reproduce the above copyright\n# notice, this list of conditions and the following disclaimer in the\n# documentation and/or other materials provided with the distribution.\n#\n# Neither the name of the John Graham-Cumming nor the names of its\n# contributors may be used to endorse or promote products derived from\n# this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n# POSSIBILITY OF SUCH DAMAGE.\n#\n# ----------------------------------------------------------------------------\n\n# Strings\n\ngmsl_characters := A B C D E F G H I J K L M N O P Q R S T U V W X Y Z\ngmsl_characters += a b c d e f g h i j k l m n o p q r s t u v w x y z\ngmsl_characters += 0 1 2 3 4 5 6 7 8 9\ngmsl_characters += ` ~ ! @ \\# $$ % ^ & * ( ) - _ = +\ngmsl_characters += { } [ ] \\ : ; ' \" < > , . / ? |\n\nendif\n"
  },
  {
    "path": "impls/make/numbers.mk",
    "content": "#\n# mal (Make a Lisp) number types\n#\n\nifndef __mal_numbers_included\n__mal_numbers_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)util.mk\n\nLIST20_X := x x x x x x x x x x x x x x x x x x x x\nLIST100_X := $(foreach x,$(LIST20_X),X X X X X)\nLIST100_0 := $(foreach x,$(LIST20_X),0 0 0 0 0)\nLIST100_9 := $(foreach x,$(LIST20_X),9 9 9 9 9)\n\n###\n### general numeric utility functions\n###\n\nint_encode = $(strip $(call _reverse,\\\n               $(eval __temp := $(1))\\\n               $(foreach a,- 0 1 2 3 4 5 6 7 8 9,\\\n                 $(eval __temp := $$(subst $$a,$$a$$(SPACE),$(__temp))))$(__temp)))\n\nint_decode = $(subst $(SPACE),,$(_reverse))\n\n# trim extaneous zero digits off the end (front of number)\n_trim_zeros = $(if $(call _EQ,0,$(strip $(1))),0,$(if $(call _EQ,0,$(word 1,$(1))),$(call _trim_zeros,$(wordlist 2,$(words $(1)),$(1))),$(1)))\ntrim_zeros = $(strip \\\n               $(if $(call _EQ,0,$(strip $(filter-out -,$(1)))),\\\n                 $(filter-out -,$(1)),\\\n                 $(call _reverse,$(call _trim_zeros,$(call _reverse,$(filter-out -,$(1))))))\\\n               $(if $(filter -,$(1)), -,))\n\n# drop the last element of a list of words/digits\ndrop_last = $(call _reverse,$(wordlist 2,$(words $(1)),$(call _reverse,$(1))))\n\n### utility function tests\n\n#$(info $(filter-out 1,$(filter 1%,1 132 456)))\n#$(info (int_encode 13): [$(call int_encode,13)])\n#$(info (int_encode 156463): [$(call int_encode,156463)])\n#$(info (int_encode -156463): [$(call int_encode,-156463)])\n#$(info (int_decode (int_encode 156463)): [$(call int_decode,$(call int_encode,156463))])\n\n#$(info trim_zeros(0 0 0): [$(call trim_zeros,0 0 0)])\n#$(info trim_zeros(0 0 0 -): [$(call trim_zeros,0 0 0 -)])\n\n\n### \n### comparisons\n###\n\n# compare two digits and return 'true' if digit 1 is less than or\n# equal to digit 2\n_lte_digit = $(strip \\\n              $(if $(call _EQ,$(1),$(2)),\\\n                true,\\\n                $(if $(call _EQ,0,$(1)),\\\n                  true,\\\n                  $(if $(wordlist $(1),$(2),$(LIST20_X)),\\\n                    true,\\\n                    ))))\n\n# compare two lists of digits (MSB->LSB) of equal length and return\n# 'true' if number 1 is less than number 2\n_lte_digits = $(strip \\\n                $(if $(strip $(1)),\\\n                  $(if $(call _EQ,$(word 1,$(1)),$(word 1,$(2))),\\\n                    $(call _lte_digits,$(wordlist 2,$(words $(1)),$(1)),$(wordlist 2,$(words $(2)),$(2))),\\\n                    $(if $(call _lte_digit,$(word 1,$(1)),$(word 1,$(2))),true,)),\\\n                  true))\n\n### lte/less than or equal to\n\n_int_lte_encoded = $(strip \\\n                     $(foreach len1,$(words $(1)),$(foreach len2,$(words $(2)),\\\n                     $(if $(call _EQ,$(len1),$(len2)),\\\n                       $(call _lte_digits,$(call _reverse,$(1)),$(call _reverse,$(2))),\\\n                       $(if $(wordlist $(len1),$(len2),$(LIST100_X)),\\\n                         true,\\\n                         )))))\n\nint_lte_encoded = $(strip \\\n                    $(if $(filter -,$(1)),\\\n                      $(if $(filter -,$(2)),\\\n                        $(call _int_lte_encoded,$(filter-out -,$(2)),$(filter-out -,$(1))),\\\n                        true),\\\n                      $(if $(filter -,$(2)),\\\n                        ,\\\n                        $(call _int_lte_encoded,$(1),$(2)))))\n\nint_lte = $(call int_lte_encoded,$(call int_encode,$(1)),$(call int_encode,$(2)))\n\n### lt/less than\n\nint_lt_encoded = $(strip \\\n                   $(if $(call _EQ,$(strip $(1)),$(strip $(2))),\\\n                     ,\\\n                     $(call int_lte_encoded,$(1),$(2))))\n\nint_lt = $(call int_lt_encoded,$(call int_encode,$(1)),$(call int_encode,$(2)))\n\n### gte/greater than or equal to\n\nint_gte_encoded = $(strip \\\n                $(if $(call _EQ,$(strip $(1)),$(strip $(2))),\\\n                  true,\\\n                  $(if $(call int_lte_encoded,$(1),$(2)),,true)))\n\nint_gte = $(call int_gte_encoded,$(call int_encode,$(1)),$(call int_encode,$(2)))\n\n### gt/greater than\n\nint_gt_encoded = $(strip \\\n               $(if $(call _EQ,$(strip $(1)),$(strip $(2))),\\\n                 ,\\\n                 $(call int_gte_encoded,$(1),$(2))))\n\nint_gt = $(call int_gt_encoded,$(call int_encode,$(1)),$(call int_encode,$(2)))\n\n#$(info _lte_digit,7,8: [$(call _lte_digit,7,8)])\n#$(info _lte_digit,8,8: [$(call _lte_digit,8,8)])\n#$(info _lte_digit,2,1: [$(call _lte_digit,2,1)])\n#$(info _lte_digit,0,0: [$(call _lte_digit,0,0)])\n#$(info _lte_digit,0,1: [$(call _lte_digit,0,1)])\n#$(info _lte_digit,1,0: [$(call _lte_digit,1,0)])\n\n#$(info _lte_digits,1 2 3,1 2 4: [$(call _lte_digits,1 2 3,1 2 4)])\n#$(info _lte_digits,1 2 4,1 2 4: [$(call _lte_digits,1 2 4,1 2 4)])\n#$(info _lte_digits,1 2 5,1 2 4: [$(call _lte_digits,1 2 5,1 2 4)])\n#$(info _lte_digits,4 1,9 0: [$(call _lte_digits,4 1,9 0)])\n\n# The main comparison operator (others are built on this)\n#$(info int_lte_encoded,1,1: [$(call int_lte_encoded,1,1)])\n#$(info int_lte_encoded,1,2: [$(call int_lte_encoded,1,2)])\n#$(info int_lte_encoded,2,1: [$(call int_lte_encoded,2,1)])\n#$(info int_lte_encoded,0,3: [$(call int_lte_encoded,0,3)])\n#$(info int_lte_encoded,3,0: [$(call int_lte_encoded,3,0)])\n#$(info int_lte_encoded,1 4,0 9: [$(call int_lte_encoded,1 4,0 9)])\n#$(info int_lte_encoded,4 3 2 1,4 3 2 1: [$(call int_lte_encoded,4 3 2 1,4 3 2 1)])\n#$(info int_lte_encoded,5 3 2 1,4 3 2 1: [$(call int_lte_encoded,5 3 2 1,4 3 2 1)])\n#$(info int_lte_encoded,4 3 2 1,5 3 2 1: [$(call int_lte_encoded,4 3 2 1,5 3 2 1)])\n# negative numbers\n#$(info int_lte_encoded,7 -,7: [$(call int_lte_encoded,7 -,7)])\n#$(info int_lte_encoded,7,7 -: [$(call int_lte_encoded,7,7 -)])\n#$(info int_lte_encoded,7 -,7 -: [$(call int_lte_encoded,7 -,7 -)])\n#$(info int_lte_encoded,1 7 -,0 7: [$(call int_lte_encoded,1 7 -,0 7)])\n#$(info int_lte_encoded,1 7,0 7 -: [$(call int_lte_encoded,1 7,0 7 -)])\n#$(info int_lte_encoded,1 7 -,0 7 -: [$(call int_lte_encoded,1 7 -,0 7 -)])\n#$(info int_lte_encoded,4 3 2 1 -,4 3 2 1: [$(call int_lte_encoded,4 3 2 1 -,4 3 2 1)])\n#$(info int_lte_encoded,4 3 2 1,4 3 2 1 -: [$(call int_lte_encoded,4 3 2 1,4 3 2 1 -)])\n#$(info int_lte_encoded,4 3 2 1 -,4 3 2 1 -: [$(call int_lte_encoded,4 3 2 1 -,4 3 2 1 -)])\n\n#$(info int_lte,1,1: [$(call int_lte,1,1)])\n#$(info int_lte,1,2: [$(call int_lte,1,2)])\n#$(info int_lte,2,1: [$(call int_lte,2,1)])\n#$(info int_lte,0,3: [$(call int_lte,0,3)])\n#$(info int_lte,3,0: [$(call int_lte,3,0)])\n#$(info int_lte,1234,1234: [$(call int_lte,1234,1234)])\n#$(info int_lte,1235,1234: [$(call int_lte,1235,1234)])\n#$(info int_lte,1234,1235: [$(call int_lte,1234,1235)])\n#$(info int_lte,-1234,1235: [$(call int_lte,-1234,1235)])\n#$(info int_lte,1234,-1235: [$(call int_lte,1234,-1235)])\n#$(info int_lte,-1234,-1235: [$(call int_lte,-1234,-1235)])\n\n#$(info int_lt,1,1: [$(call int_lt,1,1)])\n#$(info int_lt,1,2: [$(call int_lt,1,2)])\n#$(info int_lt,2,1: [$(call int_lt,2,1)])\n#$(info int_lt,0,3: [$(call int_lt,0,3)])\n#$(info int_lt,3,0: [$(call int_lt,3,0)])\n#$(info int_lt,1234,1234: [$(call int_lt,1234,1234)])\n#$(info int_lt,1235,1234: [$(call int_lt,1235,1234)])\n#$(info int_lt,1234,1235: [$(call int_lt,1234,1235)])\n#\n#$(info int_gte,1,1: [$(call int_gte,1,1)])\n#$(info int_gte,1,2: [$(call int_gte,1,2)])\n#$(info int_gte,2,1: [$(call int_gte,2,1)])\n#$(info int_gte,0,3: [$(call int_gte,0,3)])\n#$(info int_gte,3,0: [$(call int_gte,3,0)])\n#$(info int_gte,1234,1234: [$(call int_gte,1234,1234)])\n#$(info int_gte,1235,1234: [$(call int_gte,1235,1234)])\n#$(info int_gte,1234,1235: [$(call int_gte,1234,1235)])\n#\n#$(info int_gt,1,1: [$(call int_gt,1,1)])\n#$(info int_gt,1,2: [$(call int_gt,1,2)])\n#$(info int_gt,2,1: [$(call int_gt,2,1)])\n#$(info int_gt,0,3: [$(call int_gt,0,3)])\n#$(info int_gt,3,0: [$(call int_gt,3,0)])\n#$(info int_gt,1234,1234: [$(call int_gt,1234,1234)])\n#$(info int_gt,1235,1234: [$(call int_gt,1235,1234)])\n#$(info int_gt,1234,1235: [$(call int_gt,1234,1235)])\n#$(info int_gt,-1234,1235: [$(call int_gt,-1234,1235)])\n#$(info int_gt,-1234,-1235: [$(call int_gt,-1234,-1235)])\n\n\n###\n### addition\n###\n\n\n# add_digits_with_carry\n_add_digit = $(words $(if $(strip $(1)),$(wordlist 1,$(1),$(LIST20_X)),) \\\n                     $(if $(strip $(2)),$(wordlist 1,$(2),$(LIST20_X)),))\n\n# add one to a single digit\n_inc_digit = $(words $(wordlist 1,$(if $(1),$(1),0),$(LIST20_X)) x)\n\n# add two encoded numbers digit by digit without resolving carries\n# (each digit will be larger than 9 if there is a carry value)\n_add = $(if $(1)$(2),$(call _add_digit,$(word 1,$(1)),$(word 1,$(2))) $(call _add,$(wordlist 2,$(words $(1)),$(1)),$(wordlist 2,$(words $(2)),$(2))),)\n\n# take the result of _add and resolve the carry values digit by digit\n_resolve_carries = $(strip \\\n             $(if $(1),\\\n               $(foreach num,$(word 1,$(1)),\\\n                 $(if $(filter-out 1,$(filter 1%,$(num))),\\\n                   $(call _resolve_carries,$(call _inc_digit,$(word 2,$(1))) $(wordlist 3,$(words $(1)),$(1)),$(2) $(patsubst 1%,%,$(num))),\\\n                   $(call _resolve_carries,$(wordlist 2,$(words $(1)),$(1)),$(2) $(num)))),\\\n               $(2)))\n\n_negate = $(strip \\\n            $(if $(call _EQ,0,$(strip $(1))),\\\n              0,\\\n              $(if $(filter -,$(1)),$(filter-out -,$(1)),$(1) -)))\n\n# add two encoded numbers, returns encoded number\n_int_add_encoded = $(call _resolve_carries,$(call _add,$(1),$(2)))\n\nint_add_encoded = $(strip \\\n                    $(if $(filter -,$(1)),\\\n                      $(if $(filter -,$(2)),\\\n                        $(call _negate,$(call _int_add_encoded,$(filter-out -,$(1)),$(filter-out -,$(2)))),\\\n                        $(call int_sub_encoded,$(2),$(filter-out -,$(1)))),\\\n                      $(if $(filter -,$(2)),\\\n                        $(call int_sub_encoded,$(1),$(filter-out -,$(2))),\\\n                        $(call _int_add_encoded,$(1),$(2)))))\n\n# add two unencoded numbers, returns unencoded number\nint_add = $(call int_decode,$(call int_add_encoded,$(call int_encode,$(1)),$(call int_encode,$(2))))\n\n### addition tests\n\n#$(info _add_digit(7,6,1): [$(call _add_digit,7,6,1)])\n#$(info _add_digit(7,6,0): [$(call _add_digit,7,6,0)])\n#$(info _add_digit(7,6,0): [$(call _add_digit,7,6,0)])\n#$(info _carries(12 14 15): [$(call _carries,12 14 15)])\n#$(info _inc_digit(0): $(call _inc_digit,0))\n#$(info _inc_digit(1): $(call _inc_digit,1))\n#$(info _inc_digit(9): $(call _inc_digit,9))\n#$(info _inc_digit(18): $(call _inc_digit,18))\n#$(info int_add_encoded(0,0): [$(call int_add_encoded,0,0)])\n\n#$(info int_add(1,2): [$(call int_add,1,2)])\n#$(info int_add(9,9): [$(call int_add,9,9)])\n#$(info int_add(0,9): [$(call int_add,0,9)])\n#$(info int_add(9,0): [$(call int_add,9,0)])\n#$(info int_add(0,0): [$(call int_add,0,0)])\n#$(info int_add(123,456): [$(call int_add,123,456)])\n#$(info int_add(678,789): [$(call int_add,678,789)])\n#$(info int_add(1,12): [$(call int_add,1,12)])\n#$(info int_add(123,5): [$(call int_add,123,5)])\n#$(info int_add(123456,9): [$(call int_add,123456,9)])\n#$(info int_add(999999991,9): [$(call int_add,999999991,9)])\n# negative numbers\n#$(info int_add(-2,2): [$(call int_add,-2,2)])\n#$(info int_add(-1,2): [$(call int_add,-1,2)])\n#$(info int_add(1,-2): [$(call int_add,1,-2)])\n#$(info int_add(-1,-2): [$(call int_add,-1,-2)])\n\n###\n### subtraction\n###\n\n_get_zeros = $(if $(call _EQ,0,$(word 1,$(1))),$(call _get_zeros,$(wordlist 2,$(words $(1)),$(1)),$(2) 0),$(2))\n\n# return a 9's complement of a single digit\n_complement9 = $(strip \\\n                 $(if $(call _EQ,0,$(1)),9,\\\n                 $(if $(call _EQ,1,$(1)),8,\\\n                 $(if $(call _EQ,2,$(1)),7,\\\n                 $(if $(call _EQ,3,$(1)),6,\\\n                 $(if $(call _EQ,4,$(1)),5,\\\n                 $(if $(call _EQ,5,$(1)),4,\\\n                 $(if $(call _EQ,6,$(1)),3,\\\n                 $(if $(call _EQ,7,$(1)),2,\\\n                 $(if $(call _EQ,8,$(1)),1,\\\n                 $(if $(call _EQ,9,$(1)),0)))))))))))\n\n# return a 10's complement of a single digit\n_complement10 = $(call _inc_digit,$(call _complement9,$(1)))\n\n# \n_complement_rest = $(if $(strip $(1)),\\\n                     $(strip \\\n                       $(call _complement10,$(word 1,$(1))) \\\n                       $(foreach digit,$(wordlist 2,$(words $(1)),$(1)),\\\n                         $(call _complement9,$(digit)))),)\n\n# return the complement of a number\n_complement = $(strip $(call _get_zeros,$(1)) \\\n                      $(call _complement_rest,$(wordlist $(call _inc_digit,$(words $(call _get_zeros,$(1)))),$(words $(1)),$(1))))\n\n# subtracted encoded number 2 from encoded number 1 and return and\n# encoded number result. both numbers must be positive but may have\n# a negative result\n__int_sub_encoded = $(strip \\\n                  $(call trim_zeros,\\\n                    $(call drop_last,\\\n                      $(call int_add_encoded,\\\n                        $(1),\\\n                        $(wordlist 1,$(words $(1)),$(call _complement,$(2)) $(LIST100_9))))))\n\n_int_sub_encoded = $(strip \\\n                     $(if $(call _EQ,0,$(strip $(2))),\\\n                       $(1),\\\n                       $(if $(call _int_lte_encoded,$(2),$(1)),\\\n                         $(call __int_sub_encoded,$(1),$(2)),\\\n                         $(call _negate,$(call __int_sub_encoded,$(2),$(1))))))\n\nint_sub_encoded = $(strip \\\n                    $(if $(filter -,$(1)),\\\n                      $(if $(filter -,$(2)),\\\n                        $(call _int_sub_encoded,$(filter-out -,$(2)),$(filter-out -,$(1))),\\\n                        $(call _negate,$(call _int_add_encoded,$(filter-out -,$(1)),$(2)))),\\\n                      $(if $(filter -,$(2)),\\\n                        $(call _int_add_encoded,$(1),$(filter-out -,$(2))),\\\n                        $(call _int_sub_encoded,$(1),$(2)))))\n\n# subtract unencoded number 2 from unencoded number 1 and return\n# unencoded result\nint_sub = $(call int_decode,$(call int_sub_encoded,$(call int_encode,$(1)),$(call int_encode,$(2))))\n\n### subtraction tests\n\n#$(info _get_zeros(5 7): [$(call _get_zeros,5 7)])\n#$(info _get_zeros(0 0 0 2): [$(call _get_zeros,0 0 0 2)])\n#$(info _get_zeros(0 0 0 2 5): [$(call _get_zeros,0 0 0 2 5)])\n\n#$(info _complement(0): [$(call _complement,0)])\n#$(info _complement(1): [$(call _complement,1)])\n#$(info _complement(9): [$(call _complement,9)])\n#$(info _complement(5 7): [$(call _complement,5 7)])\n#$(info _complement(0 0 0 2): [$(call _complement,0 0 0 2)])\n#$(info _complement(0 0 0 5 4 3 2 1): [$(call _complement,0 0 0 5 4 3 2 1)])\n\n#$(info int_sub_encoded(0 0 1, 3 1): [$(call int_sub_encoded,0 0 1,3 1)])\n#$(info int_sub_encoded(2, 2): [$(call int_sub_encoded,2,2)])\n\n#$(info int_sub(2,1): [$(call int_sub,2,1)])\n#$(info int_sub(2,0): [$(call int_sub,2,0)])\n#$(info int_sub(2,2): [$(call int_sub,2,2)])\n#$(info int_sub(100,13): [$(call int_sub,100,13)])\n#$(info int_sub(100,99): [$(call int_sub,100,99)])\n#$(info int_sub(91,19): [$(call int_sub,91,19)])\n# negative numbers\n#$(info int_sub(1,2): [$(call int_sub,1,2)])\n#$(info int_sub(-1,2): [$(call int_sub,-1,2)])\n#$(info int_sub(1,-2): [$(call int_sub,1,-2)])\n#$(info int_sub(-1,-2): [$(call int_sub,-1,-2)])\n#$(info int_sub(-2,-1): [$(call int_sub,-2,-1)])\n#$(info int_sub(19,91): [$(call int_sub,19,91)])\n#$(info int_sub(91,-19): [$(call int_sub,91,-19)])\n#$(info int_sub(-91,19): [$(call int_sub,-91,19)])\n#$(info int_sub(-91,-19): [$(call int_sub,-91,-19)])\n\n\n###\n### multiplication\n###\n\n# multiply two digits\n#_mult_digit = $(words $(foreach x,$(1),$(2)))\n_mult_digit = $(strip \\\n                $(words $(foreach x,$(wordlist 1,$(1),$(LIST20_X)),\\\n                        $(wordlist 1,$(2),$(LIST20_X)))))\n\n# multipy every digit of number 1 with number 2\n# params: digits, digit, indent_zeros, results\n_mult_row = $(if $(strip $(1)),$(call _mult_row,$(wordlist 2,$(words $(1)),$(1)),$(2),$(3)0,$(4) $(call _mult_digit,$(word 1,$(1)),$(2))$(3)),$(4))\n\n# multiply every digit of number 2 with every digit of number 1 adding\n# correct zero padding to the end of each result\n# params: digits, digits, indent_zeros, results\n_mult_each = $(if $(strip $(2)),$(call _mult_each,$(1),$(wordlist 2,$(words $(2)),$(2)),$(3)0,$(4) $(call _mult_row,$(1),$(word 1,$(2)),$(3))),$(4))\n\n# add up a bunch of unencoded numbers. Basically reduce into the first number\n_add_many = $(if $(word 2,$(1)),$(call _add_many,$(call int_add,$(word 1,$(1)),$(word 2,$(1))) $(wordlist  3,$(words $(1)),$(1))),$(1))\n\n# multiply two encoded numbers, returns encoded number\n_int_mult_encoded = $(call trim_zeros,$(call int_encode,$(call _add_many,$(call _mult_each,$(1),$(2)))))\n\nint_mult_encoded = $(strip \\\n             $(if $(filter -,$(1)),\\\n               $(if $(filter -,$(2)),\\\n                 $(call _int_mult_encoded,$(filter-out -,$(1)),$(filter-out -,$(2))),\\\n                 $(call _negate,$(call _int_mult_encoded,$(filter-out -,$(1)),$(2)))),\\\n               $(if $(filter -,$(2)),\\\n                 $(call _negate,$(call _int_mult_encoded,$(1),$(filter-out -,$(2)))),\\\n                 $(call _int_mult_encoded,$(1),$(2)))))\n\n# multiply two unencoded numbers, returns unencoded number\nint_mult = $(call int_decode,$(call int_mult_encoded,$(call int_encode,$(1)),$(call int_encode,$(2))))\n\n#$(info _mult_digit(8,6): [$(call _mult_digit,8,6)])\n#$(info _mult_digit(7,6): [$(call _mult_digit,7,6)])\n#$(info _mult_row(8,6): [$(call _mult_row,8,6)])\n#$(info _mult_row(8 7,6): [$(call _mult_row,8 7,6)])\n#$(info _mult_row(8 7 3,6): [$(call _mult_row,8 7 3,6)])\n#$(info _mult_each(8 7 6, 4 3 2): [$(call _mult_each,8 7 6,4 3 2)])\n#$(info _add_many(123 234 345 456): [$(call _add_many,123 234 345 456)])\n\n#$(info int_mult_encoded(8 7 3,6): [$(call int_mult_encoded,8 7 3,6)])\n#$(info int_mult_encoded(8 7 3,0): [$(call int_mult_encoded,8 7 3,0)])\n\n#$(info int_mult(378,6): [$(call int_mult,378,6)])\n#$(info int_mult(678,234): [$(call int_mult,678,234)])\n#$(info int_mult(1,23456): [$(call int_mult,1,23456)])\n#$(info int_mult(0,23456): [$(call int_mult,0,23456)])\n#$(info int_mult(0,0): [$(call int_mult,0,0)])\n# negative numbers\n#$(info int_mult(-378,6): [$(call int_mult,-378,6)])\n#$(info int_mult(678,-234): [$(call int_mult,678,-234)])\n#$(info int_mult(-1,-23456): [$(call int_mult,-1,-23456)])\n#$(info int_mult(0,-23456): [$(call int_mult,0,-23456)])\n\n###\n### division\n###\n\n# return list of zeros needed to pad number 2 to the same length as number 1\n_zero_pad = $(strip $(wordlist 1,$(call int_sub,$(words $(1)),$(words $(2))),$(LIST100_0)))\n\n# num1, num2, zero pad, result_accumulator\n# algorithm:\n# - B = pad with zeros to make same digit length as A\n# - loop\n#    - if (B <= A)\n#        - A = subtract B from A\n#        - C = C + 10^(B pad.length)\n#    - else\n#        - if B.length < origin B.length: break\n#        - chop least significant digit of B\n_div = $(strip \\\n         $(if $(call int_lte_encoded,$(3) $(2),$(1)),\\\n           $(call _div,$(call int_sub_encoded,$(1),$(3) $(2)),$(2),$(3),$(call int_add_encoded,$(4),$(3) 1)),\\\n           $(if $(3),\\\n             $(call _div,$(1),$(2),$(wordlist 2,$(words $(3)),$(3)),$(4)),\\\n             $(4))))\n\n# divide two encoded numbers, returns encoded number\n_int_div_encoded = $(strip \\\n                $(if $(call _EQ,0,$(1)),\\\n                  0,\\\n                  $(if $(call _EQ,$(1),$(2)),\\\n                    1,\\\n                    $(if $(call int_gt_encoded,$(2),$(1)),\\\n                      0,\\\n                      $(call _div,$(1),$(2),$(call _zero_pad,$(1),$(2)),0)))))\n\nint_div_encoded = $(strip \\\n             $(if $(filter -,$(1)),\\\n               $(if $(filter -,$(2)),\\\n                 $(call _int_div_encoded,$(filter-out -,$(1)),$(filter-out -,$(2))),\\\n                 $(call _negate,$(call _int_div_encoded,$(filter-out -,$(1)),$(2)))),\\\n               $(if $(filter -,$(2)),\\\n                 $(call _negate,$(call _int_div_encoded,$(1),$(filter-out -,$(2)))),\\\n                 $(call _int_div_encoded,$(1),$(2)))))\n\n# divide two unencoded numbers, returns unencoded number\nint_div = $(call int_decode,$(call int_div_encoded,$(call int_encode,$(1)),$(call int_encode,$(2))))\n\n### division tests\n\n#$(info _zero_pad(1 2 3 4,1 3): [$(call _zero_pad,1 2 3 4,1 3)])\n#$(info _zero_pad(1 2,1 3): [$(call _zero_pad,1 2,1 3)])\n#$(info _zero_pad(2,1 3): [$(call _zero_pad,1 2,1 3)])\n#\n#$(info int_div_encoded(2,1): [$(call int_div_encoded,2,1)])\n#$(info int_div_encoded(3,1): [$(call int_div_encoded,3,1)])\n#$(info int_div_encoded(3,2): [$(call int_div_encoded,3,2)])\n#$(info int_div_encoded(0,7): [$(call int_div_encoded,0,7)])\n#$(info int_div_encoded(0 3,0 2): [$(call int_div_encoded,0 3,0 2)])\n#$(info int_div_encoded(0 3,5): [$(call int_div_encoded,0 3,5)])\n#\n#$(info int_div(5,1): [$(call int_div,5,1)])\n#$(info int_div(5,2): [$(call int_div,5,2)])\n#$(info int_div(123,7): [$(call int_div,123,7)])\n#$(info int_div(100,7): [$(call int_div,100,7)])\n# negative numbers\n#$(info int_div(-5,1): [$(call int_div,-5,1)])\n#$(info int_div(5,-2): [$(call int_div,5,-2)])\n#$(info int_div(-123,-7): [$(call int_div,-123,-7)])\n\n\n### combination tests\n\n# (/ (- (+ 515 (* 222 311)) 300) 41) = 1689\n#$(info int_mult,222,311: [$(call int_mult,222,311)])\n#$(info int_add(515,69042): [$(call int_add,515,69042)])\n#$(info int_sub(69557,300): [$(call int_sub,69557,300)])\n#$(info int_div(69257,41): [$(call int_div,69257,41)])\n# (/ (- (+ 515 (* -222 311)) 300) 41) = -1678\n#$(info int_mult,-222,311: [$(call int_mult,-222,311)])\n#$(info int_add(515,-69042): [$(call int_add,515,-69042)])\n#$(info int_sub(-68527,300): [$(call int_sub,-68527,300)])\n#$(info int_div(-68827,41): [$(call int_div,-68827,41)])\n\n###############################################################\n\nall:\n\t@true\n\nendif\n\n# vim: ts=2 et\n"
  },
  {
    "path": "impls/make/printer.mk",
    "content": "#\n# mal (Make a Lisp) printer\n#\n\nifndef __mal_printer_included\n__mal_printer_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\n\n# return a printable form of the argument, the second parameter is\n# 'print_readably' which backslashes quotes in string values\n_pr_str = $(call $(_obj_type)_pr_str,$1,$2)\n\n# Like _pr_str but takes multiple values in first argument, the second\n# parameter is 'print_readably' which backslashes quotes in string\n# values, the third parameter is the delimeter to use between each\n# _pr_str'd value\n_pr_str_mult = $(subst $(SPACE),$3,$(foreach f,$1,$(call _pr_str,$f,$2)))\n\n\n# Type specific printing\n\nnil_pr_str := nil\ntrue_pr_str := true\nfalse_pr_str := false\n\nnumber_pr_str = $(_number_val)\n\nsymbol_pr_str = $(_symbol_val)\n\nkeyword_pr_str = $(encoded_colon)$(_keyword_val)\n\nstring_pr_str = $(if $2\\\n  ,\"$(subst $(_NL),$(encoded_slash)n,$(rem \\\n   )$(subst \",$(encoded_slash)\",$(rem \\\n   )$(subst $(encoded_slash),$(encoded_slash)$(encoded_slash),$(rem \\\n   )$(_string_val))))\"$(rem \\\nelse \\\n  ),$(_string_val))\n\ncorefn_pr_str := <Core>\nfunction_pr_str := <Function>\nmacro_pr_str := <Macro>\n\nlist_pr_str = $(_LP)$(call _pr_str_mult,$(_seq_vals),$2,$(_SP))$(_RP)\n\nvector_pr_str = [$(call _pr_str_mult,$(_seq_vals),$2,$(_SP))]\n\nmap_pr_str = {$(call _pr_str_mult,$(foreach k,$(_keys),$k $(call _get,$1,$k)),$2,$(_SP))}\n\natom_pr_str = $(_LP)atom$(_SP)$(call _pr_str,$(deref),$2)$(_RP)\n\nendif\n"
  },
  {
    "path": "impls/make/reader.mk",
    "content": "#\n# mal (Make Lisp) Parser/Reader\n#\n\nifndef __mal_reader_included\n__mal_reader_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\n\nREADER_DEBUG ?=\n\n_TOKEN_DELIMS := ; , \" ` $(_SP) $(_NL) { } $(_LP) $(_RP) [ ] #`\"\n\nreader_init = $(eval __reader_temp := $(str_encode))\nreader_next = $(firstword $(__reader_temp))\nreader_drop = $(eval __reader_temp := $(call _rest,$(__reader_temp)))\nreader_log = $(if $(READER_DEBUG),$(info READER: $1 from $(__reader_temp)))\n\ndefine READ_NUMBER\n$(call reader_log,number)$(rem \\\n)$(if $(filter 0 1 2 3 4 5 6 7 8 9,$(reader_next))\\\n  ,$(reader_next)$(reader_drop)$(call READ_NUMBER))\nendef\n\ndefine READ_STRING\n$(call reader_log,string)$(rem \\\n)$(if $(filter \",$(reader_next))\\\n  ,$(reader_drop)$(rem \"\\\n),$(if $(filter $(encoded_slash),$(reader_next))\\\n  ,$(reader_drop)$(rem \\\n  )$(if $(filter n,$(reader_next)),$(_NL),$(reader_next))$(rem \\\n  )$(reader_drop)$(call READ_STRING)$(rem \\\n),$(if $(reader_next)\\\n  ,$(reader_next)$(reader_drop)$(call READ_STRING)$(rem \\\n),$(call _error,Expected '\"'$(COMMA) got EOF))))\nendef\n\ndefine READ_SYMBOL\n$(call reader_log,symbol or keyword)$(rem \\\n)$(if $(filter-out $(_TOKEN_DELIMS),$(reader_next))\\\n  ,$(reader_next)$(reader_drop)$(call READ_SYMBOL))\nendef\n\n# read and return tokens until $1 found\n# The last element if any is followed by a space.\ndefine READ_UNTIL\n$(call reader_log,until $1)$(rem \\\n)$(READ_SPACES)$(rem \\\n)$(if $(filter $1,$(reader_next))\\\n  ,$(reader_drop)$(rem \\\n),$(if $(reader_next)\\\n  ,$(call READ_FORM) $(call READ_UNTIL,$1)$(rem \\\n),$(call _error,Expected '$1'$(COMMA) got EOF)))\nendef\n\ndefine READ_COMMENT\n$(call reader_log,comment)$(rem \\\n)$(if $(filter-out $(_NL),$(reader_next))\\\n  ,$(reader_drop)$(call READ_COMMENT))\nendef\n\ndefine READ_SPACES\n$(call reader_log,spaces)$(rem \\\n)$(if $(filter $(_SP) $(_NL) $(COMMA),$(reader_next))\\\n  ,$(reader_drop)$(call READ_SPACES)$(rem \\\n),$(if $(filter ;,$(reader_next))\\\n  ,$(READ_COMMENT)))\nendef\n\ndefine READ_FORM\n$(call reader_log,form)$(rem \\\n)$(READ_SPACES)$(rem \\\n)$(if $(filter-out undefined,$(flavor READ_FORM_$(reader_next)))\\\n  ,$(call READ_FORM_$(reader_next)$(reader_drop))$(rem \\\n),$(foreach sym,$(READ_SYMBOL)\\\n  ,$(if $(filter false nil true,$(sym))\\\n    ,$(__$(sym))$(rem \\\n    ),$(call _symbol,$(sym)))))\nendef\n\nREAD_FORM_ = $(call _error,expected a form$(COMMA) found EOF)\n\n# Reader macros\nREAD_FORM_@ = $(call list,$(call _symbol,deref) $(call READ_FORM))\nREAD_FORM_' = $(call list,$(call _symbol,quote) $(call READ_FORM))#'\nREAD_FORM_` = $(call list,$(call _symbol,quasiquote) $(call READ_FORM))#`\nREAD_FORM_^ = $(call list,$(call _symbol,with-meta) $(foreach m,\\\n $(call READ_FORM),$(call READ_FORM) $m))\n\nREAD_FORM_~ = $(call list,$(if $(filter @,$(reader_next))\\\n  ,$(reader_drop)$(call _symbol,splice-unquote)$(rem \\\n  ),$(call _symbol,unquote)) $(call READ_FORM))\n\n# Lists, vectors and maps\n# _map_new accepts a leading space, list and vector require )strip.\nREAD_FORM_{      = $(call _map_new,,$(strip $(call READ_UNTIL,})))\nREAD_FORM_$(_LP) = $(call list,$(strip $(call READ_UNTIL,$(_RP))))\nREAD_FORM_[      = $(call vector,$(strip $(call READ_UNTIL,])))\nREAD_FORM_}      = $(call _error,Unexpected '}')\nREAD_FORM_$(_RP) = $(call _error,Unexpected '$(_RP)')\nREAD_FORM_]      = $(call _error,Unexpected ']')\n\n# Numbers\ndefine READ_FORM_-\n$(if $(filter 0 1 2 3 4 5 6 7 8 9,$(reader_next))\\\n  ,$(call _number,-$(READ_NUMBER))$(rem \\\n  ),$(call _symbol,-$(READ_SYMBOL)))\nendef\nREAD_FORM_0 = $(call _number,0$(READ_NUMBER))\nREAD_FORM_1 = $(call _number,1$(READ_NUMBER))\nREAD_FORM_2 = $(call _number,2$(READ_NUMBER))\nREAD_FORM_3 = $(call _number,3$(READ_NUMBER))\nREAD_FORM_4 = $(call _number,4$(READ_NUMBER))\nREAD_FORM_5 = $(call _number,5$(READ_NUMBER))\nREAD_FORM_6 = $(call _number,6$(READ_NUMBER))\nREAD_FORM_7 = $(call _number,7$(READ_NUMBER))\nREAD_FORM_8 = $(call _number,8$(READ_NUMBER))\nREAD_FORM_9 = $(call _number,9$(READ_NUMBER))\n\n# Strings\nREAD_FORM_\" = $(call _string,$(call str_decode,$(READ_STRING)))#\"\n\n# Keywords\nREAD_FORM_$(encoded_colon) = $(call _keyword,$(READ_SYMBOL))\n\nREAD_STR = $(reader_init)$(or $(READ_FORM),$(__nil))\n\nendif\n"
  },
  {
    "path": "impls/make/readline.mk",
    "content": "#\n# mal (Make Lisp) shell readline wrapper\n#\n\nifndef __mal_readline_included\n__mal_readline_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)util.mk\n\n# Call bash read/readline. Since each call is in a separate shell\n# instance we need to restore and save after each call in order to\n# have readline history.\nREADLINE_HISTORY_FILE := $${HOME}/.mal-history\n\n# Either empty (if EOF) or an encoded string with the 'ok' suffix.\nREADLINE = $(call str_encode_nospace,$(shell \\\n history -r $(READLINE_HISTORY_FILE); \\\n read -u 0 -r -e -p '$(str_decode_nospace)' line && \\\n history -s -- \"$${line}\" && \\\n echo \"$${line}ok\" ; \\\n history -a $(READLINE_HISTORY_FILE) 2>/dev/null || \\\n true \\\n))\n\nendif\n"
  },
  {
    "path": "impls/make/rules.mk",
    "content": "# To load this file:\n#   $(eval include rules.mk)\n\n# Usage:\n#   (make* \"$(eval $(call PRINT_RULE,abc,,@echo \\\"building $$@\\\"))\")\ndefine PRINT_RULE\n$(1): $(2)\n\t$(3)\nendef\n\n# Usage:\n#   (make* \"$(eval $(call PRINT_LINES,abc:,\t@echo \\\"shell command\\\"))\")\ndefine PRINT_LINES\n$(1)\n$(2)\n$(3)\n$(4)\n$(5)\n$(6)\n$(7)\n$(8)\n$(9)\n$(10)\n$(11)\n$(12)\n$(13)\n$(14)\n$(15)\n$(16)\n$(17)\n$(18)\n$(19)\n$(20)\nendef\n"
  },
  {
    "path": "impls/make/run",
    "content": "#!/usr/bin/env bash\nexec make --no-print-directory -f $(dirname $0)/${STEP:-stepA_mal}.mk \"${@}\"\n"
  },
  {
    "path": "impls/make/step0_repl.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\n\nSHELL := /usr/bin/env bash\n\ndefine READ\n$1\nendef\n\ndefine EVAL\n$1\nendef\n\ndefine PRINT\n$1\nendef\n\nREP = $(call PRINT,$(call EVAL,$(READ)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Call the read-eval-print loop\n$(REPL)\n\n# Do not complain that there is no target.\n.PHONY: none\nnone:\n\t@true\n"
  },
  {
    "path": "impls/make/step1_read_print.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: just return the input\ndefine EVAL\n$(if $(__ERROR)\\\n,,$1)\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL: read, eval, print, loop\n\nREP = $(call PRINT,$(call EVAL,$(READ)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# repl loop\n$(REPL)\n\n# Do not complain that there is no target.\n.PHONY: none\nnone:\n\t@true\n"
  },
  {
    "path": "impls/make/step2_eval.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\nEVAL_DEBUG ?=\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call _get,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2)))))\nendef\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(EVAL_DEBUG),\\\n  $(call print,EVAL: $(call _pr_str,$1,yes)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call hash-map,$(foreach f,+ - * /\\\n  ,$(call _symbol,$f) $(call _corefn,$f)))\n\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# repl loop\n$(REPL)\n\n# Do not complain that there is no target.\n.PHONY: none\nnone:\n\t@true\n"
  },
  {
    "path": "impls/make/step3_env.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2)))))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Setup the environment\n$(foreach f,+ - * /\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\n# repl loop\n$(REPL)\n\n# Do not complain that there is no target.\n.PHONY: none\nnone:\n\t@true\n"
  },
  {
    "path": "impls/make/step4_if_fn_do.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2)))))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\nEVAL_special_do = $(lastword $(foreach x,$1,$(call EVAL,$x,$2)))\n\ndefine EVAL_special_if\n$(if $(call truthy?,$(call EVAL,$(firstword $1),$2))\\\n  ,$(call EVAL,$(word 2,$1),$2)$(rem \\\n),$(if $(word 3,$1)\\\n  ,$(call EVAL,$(lastword $1),$2)$(rem \\\n),$(__nil)))\nendef\n\nEVAL_special_fn* = $(call _function,$(call _seq_vals,$(firstword $1)),$(lastword $1),$2)\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Read and evaluate for side effects but ignore the result.\ndefine RE\n$(rem $(call EVAL,$(call READ,$(str_encode_nospace)),$(REPL_ENV)) \\\n)$(if $(__ERROR)\\\n  ,$(error during startup: $(call str_decode_nospace,$(call _pr_str,$(__ERROR),yes))))\nendef\n\n# core.mk: defined using Make\n$(foreach f,$(core_ns)\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\n# core.mal: defined in terms of the language itself\n$(call RE, (def! not (fn* (a) (if a false true))) )\n\n# repl loop\n$(REPL)\n\n# Do not complain that there is no target.\n.PHONY: none\nnone:\n\t@true\n"
  },
  {
    "path": "impls/make/step6_file.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2)))))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\nEVAL_special_do = $(lastword $(foreach x,$1,$(call EVAL,$x,$2)))\n\ndefine EVAL_special_if\n$(if $(call truthy?,$(call EVAL,$(firstword $1),$2))\\\n  ,$(call EVAL,$(word 2,$1),$2)$(rem \\\n),$(if $(word 3,$1)\\\n  ,$(call EVAL,$(lastword $1),$2)$(rem \\\n),$(__nil)))\nendef\n\nEVAL_special_fn* = $(call _function,$(call _seq_vals,$(firstword $1)),$(lastword $1),$2)\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Read and evaluate for side effects but ignore the result.\ndefine RE\n$(rem $(call EVAL,$(call READ,$(str_encode_nospace)),$(REPL_ENV)) \\\n)$(if $(__ERROR)\\\n  ,$(error during startup: $(call str_decode_nospace,$(call _pr_str,$(__ERROR),yes))))\nendef\n\n# core.mk: defined using Make\n$(foreach f,$(core_ns)\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\ncore_eval = $(call EVAL,$1,$(REPL_ENV))\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,eval),$(call _corefn,core_eval))\n\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,*ARGV*),$(call list,$(foreach arg,\\\n  $(call _rest,$(MAKECMDGOALS)),$(call _string,$(call str_encode_nospace,$(arg))))))\n\n# core.mal: defined in terms of the language itself\n$(call RE, (def! not (fn* (a) (if a false true))) )\n$(call RE, (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) )\n\nifneq (,$(MAKECMDGOALS))\n# Load and eval any files specified on the command line\n$(call RE, (load-file \"$(firstword $(MAKECMDGOALS))\") )\nelse\n# repl loop\n$(REPL)\nendif\n\n# Do not complain that there is no target.\n.PHONY: none $(MAKECMDGOALS)\nnone $(MAKECMDGOALS):\n\t@true\n"
  },
  {
    "path": "impls/make/step7_quote.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\n# If $1 is empty, `foreach` does no iteration at all.\nstarts_with? = $(foreach f,$(firstword $1)\\\n                 ,$(and $(call _symbol?,$f),\\\n                        $(filter $2,$(call _symbol_val,$f))))\n\n# elt, accumulator list -> new accumulator list\nQQ_LOOP = $(if $(and $(_list?),\\\n                     $(call starts_with?,$(_seq_vals),splice-unquote))\\\n  ,$(call list,$(call _symbol,concat) $(lastword $(_seq_vals)) $2)$(rem \\\n  ),$(call list,$(call _symbol,cons) $(call QUASIQUOTE,$1) $2))\n\n# list or vector source -> right folded list\nQQ_FOLD = $(if $1\\\n  ,$(call QQ_LOOP,$(firstword $1),$(call QQ_FOLD,$(_rest)))$(rem \\\n  ),$(call list))\n\nQUASIQUOTE = $(call QUASIQUOTE_$(_obj_type),$1)\nQUASIQUOTE_nil     = $1\nQUASIQUOTE_true    = $1\nQUASIQUOTE_false   = $1\nQUASIQUOTE_string  = $1\nQUASIQUOTE_number  = $1\nQUASIQUOTE_keyword = $1\nQUASIQUOTE_symbol  = $(call list,$(call _symbol,quote) $1)\nQUASIQUOTE_map     = $(call list,$(call _symbol,quote) $1)\n\nQUASIQUOTE_vector = $(call list,$(call _symbol,vec) $(call QQ_FOLD,$(_seq_vals)))\n\nQUASIQUOTE_list = $(if $(call starts_with?,$(_seq_vals),unquote)\\\n  ,$(lastword $(_seq_vals))$(rem \\\n  ),$(call QQ_FOLD,$(_seq_vals)))\n\nEVAL_special_quote = $1\n\nEVAL_special_quasiquote = $(call EVAL,$(QUASIQUOTE),$2)\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2)))))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\nEVAL_special_do = $(lastword $(foreach x,$1,$(call EVAL,$x,$2)))\n\ndefine EVAL_special_if\n$(if $(call truthy?,$(call EVAL,$(firstword $1),$2))\\\n  ,$(call EVAL,$(word 2,$1),$2)$(rem \\\n),$(if $(word 3,$1)\\\n  ,$(call EVAL,$(lastword $1),$2)$(rem \\\n),$(__nil)))\nendef\n\nEVAL_special_fn* = $(call _function,$(call _seq_vals,$(firstword $1)),$(lastword $1),$2)\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Read and evaluate for side effects but ignore the result.\ndefine RE\n$(rem $(call EVAL,$(call READ,$(str_encode_nospace)),$(REPL_ENV)) \\\n)$(if $(__ERROR)\\\n  ,$(error during startup: $(call str_decode_nospace,$(call _pr_str,$(__ERROR),yes))))\nendef\n\n# core.mk: defined using Make\n$(foreach f,$(core_ns)\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\ncore_eval = $(call EVAL,$1,$(REPL_ENV))\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,eval),$(call _corefn,core_eval))\n\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,*ARGV*),$(call list,$(foreach arg,\\\n  $(call _rest,$(MAKECMDGOALS)),$(call _string,$(call str_encode_nospace,$(arg))))))\n\n# core.mal: defined in terms of the language itself\n$(call RE, (def! not (fn* (a) (if a false true))) )\n$(call RE, (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) )\n\nifneq (,$(MAKECMDGOALS))\n# Load and eval any files specified on the command line\n$(call RE, (load-file \"$(firstword $(MAKECMDGOALS))\") )\nelse\n# repl loop\n$(REPL)\nendif\n\n# Do not complain that there is no target.\n.PHONY: none $(MAKECMDGOALS)\nnone $(MAKECMDGOALS):\n\t@true\n"
  },
  {
    "path": "impls/make/step8_macros.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\n# If $1 is empty, `foreach` does no iteration at all.\nstarts_with? = $(foreach f,$(firstword $1)\\\n                 ,$(and $(call _symbol?,$f),\\\n                        $(filter $2,$(call _symbol_val,$f))))\n\n# elt, accumulator list -> new accumulator list\nQQ_LOOP = $(if $(and $(_list?),\\\n                     $(call starts_with?,$(_seq_vals),splice-unquote))\\\n  ,$(call list,$(call _symbol,concat) $(lastword $(_seq_vals)) $2)$(rem \\\n  ),$(call list,$(call _symbol,cons) $(call QUASIQUOTE,$1) $2))\n\n# list or vector source -> right folded list\nQQ_FOLD = $(if $1\\\n  ,$(call QQ_LOOP,$(firstword $1),$(call QQ_FOLD,$(_rest)))$(rem \\\n  ),$(call list))\n\nQUASIQUOTE = $(call QUASIQUOTE_$(_obj_type),$1)\nQUASIQUOTE_nil     = $1\nQUASIQUOTE_true    = $1\nQUASIQUOTE_false   = $1\nQUASIQUOTE_string  = $1\nQUASIQUOTE_number  = $1\nQUASIQUOTE_keyword = $1\nQUASIQUOTE_symbol  = $(call list,$(call _symbol,quote) $1)\nQUASIQUOTE_map     = $(call list,$(call _symbol,quote) $1)\n\nQUASIQUOTE_vector = $(call list,$(call _symbol,vec) $(call QQ_FOLD,$(_seq_vals)))\n\nQUASIQUOTE_list = $(if $(call starts_with?,$(_seq_vals),unquote)\\\n  ,$(lastword $(_seq_vals))$(rem \\\n  ),$(call QQ_FOLD,$(_seq_vals)))\n\nEVAL_special_quote = $1\n\nEVAL_special_quasiquote = $(call EVAL,$(QUASIQUOTE),$2)\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(if $(call _macro?,$f)\\\n    ,$(call EVAL,$(call _apply,$f,$(_rest)),$2)$(rem \\\n    ),$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2))))))\nendef\n\ndefine EVAL_special_defmacro!\n$(foreach res,$(call _as_macro,$(call EVAL,$(lastword $1),$2))\\\n  ,$(res)$(call ENV_SET,$2,$(firstword $1),$(res)))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\nEVAL_special_do = $(lastword $(foreach x,$1,$(call EVAL,$x,$2)))\n\ndefine EVAL_special_if\n$(if $(call truthy?,$(call EVAL,$(firstword $1),$2))\\\n  ,$(call EVAL,$(word 2,$1),$2)$(rem \\\n),$(if $(word 3,$1)\\\n  ,$(call EVAL,$(lastword $1),$2)$(rem \\\n),$(__nil)))\nendef\n\nEVAL_special_fn* = $(call _function,$(call _seq_vals,$(firstword $1)),$(lastword $1),$2)\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Read and evaluate for side effects but ignore the result.\ndefine RE\n$(rem $(call EVAL,$(call READ,$(str_encode_nospace)),$(REPL_ENV)) \\\n)$(if $(__ERROR)\\\n  ,$(error during startup: $(call str_decode_nospace,$(call _pr_str,$(__ERROR),yes))))\nendef\n\n# core.mk: defined using Make\n$(foreach f,$(core_ns)\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\ncore_eval = $(call EVAL,$1,$(REPL_ENV))\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,eval),$(call _corefn,core_eval))\n\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,*ARGV*),$(call list,$(foreach arg,\\\n  $(call _rest,$(MAKECMDGOALS)),$(call _string,$(call str_encode_nospace,$(arg))))))\n\n# core.mal: defined in terms of the language itself\n$(call RE, (def! not (fn* (a) (if a false true))) )\n$(call RE, (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) )\n$(call RE, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs))))))) )\n\nifneq (,$(MAKECMDGOALS))\n# Load and eval any files specified on the command line\n$(call RE, (load-file \"$(firstword $(MAKECMDGOALS))\") )\nelse\n# repl loop\n$(REPL)\nendif\n\n# Do not complain that there is no target.\n.PHONY: none $(MAKECMDGOALS)\nnone $(MAKECMDGOALS):\n\t@true\n"
  },
  {
    "path": "impls/make/step9_try.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\n# If $1 is empty, `foreach` does no iteration at all.\nstarts_with? = $(foreach f,$(firstword $1)\\\n                 ,$(and $(call _symbol?,$f),\\\n                        $(filter $2,$(call _symbol_val,$f))))\n\n# elt, accumulator list -> new accumulator list\nQQ_LOOP = $(if $(and $(_list?),\\\n                     $(call starts_with?,$(_seq_vals),splice-unquote))\\\n  ,$(call list,$(call _symbol,concat) $(lastword $(_seq_vals)) $2)$(rem \\\n  ),$(call list,$(call _symbol,cons) $(call QUASIQUOTE,$1) $2))\n\n# list or vector source -> right folded list\nQQ_FOLD = $(if $1\\\n  ,$(call QQ_LOOP,$(firstword $1),$(call QQ_FOLD,$(_rest)))$(rem \\\n  ),$(call list))\n\nQUASIQUOTE = $(call QUASIQUOTE_$(_obj_type),$1)\nQUASIQUOTE_nil     = $1\nQUASIQUOTE_true    = $1\nQUASIQUOTE_false   = $1\nQUASIQUOTE_string  = $1\nQUASIQUOTE_number  = $1\nQUASIQUOTE_keyword = $1\nQUASIQUOTE_symbol  = $(call list,$(call _symbol,quote) $1)\nQUASIQUOTE_map     = $(call list,$(call _symbol,quote) $1)\n\nQUASIQUOTE_vector = $(call list,$(call _symbol,vec) $(call QQ_FOLD,$(_seq_vals)))\n\nQUASIQUOTE_list = $(if $(call starts_with?,$(_seq_vals),unquote)\\\n  ,$(lastword $(_seq_vals))$(rem \\\n  ),$(call QQ_FOLD,$(_seq_vals)))\n\nEVAL_special_quote = $1\n\nEVAL_special_quasiquote = $(call EVAL,$(QUASIQUOTE),$2)\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(if $(call _macro?,$f)\\\n    ,$(call EVAL,$(call _apply,$f,$(_rest)),$2)$(rem \\\n    ),$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2))))))\nendef\n\ndefine EVAL_special_defmacro!\n$(foreach res,$(call _as_macro,$(call EVAL,$(lastword $1),$2))\\\n  ,$(res)$(call ENV_SET,$2,$(firstword $1),$(res)))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\nEVAL_special_do = $(lastword $(foreach x,$1,$(call EVAL,$x,$2)))\n\ndefine EVAL_special_if\n$(if $(call truthy?,$(call EVAL,$(firstword $1),$2))\\\n  ,$(call EVAL,$(word 2,$1),$2)$(rem \\\n),$(if $(word 3,$1)\\\n  ,$(call EVAL,$(lastword $1),$2)$(rem \\\n),$(__nil)))\nendef\n\nEVAL_special_fn* = $(call _function,$(call _seq_vals,$(firstword $1)),$(lastword $1),$2)\n\n# EVAL may fail and return nothing, so the first foreach may execute\n# nothing, so we need to duplicate the test for error.\n# The second foreach deliberately does nothing when there is no\n# catch_list.\ndefine EVAL_special_try*\n$(foreach res,$(call EVAL,$(firstword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)))$(rem \\\n)$(if $(__ERROR)\\\n  ,$(foreach catch_list,$(word 2,$1)\\\n    ,$(foreach env,$(call ENV,$2)\\\n    ,$(call ENV_SET,$(env),$(word 2,$(call _seq_vals,$(catch_list))),$(__ERROR))$(rem \\\n    )$(eval __ERROR :=)$(rem \\\n    )$(call EVAL,$(lastword $(call _seq_vals,$(catch_list))),$(env)))))\nendef\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Read and evaluate for side effects but ignore the result.\ndefine RE\n$(rem $(call EVAL,$(call READ,$(str_encode_nospace)),$(REPL_ENV)) \\\n)$(if $(__ERROR)\\\n  ,$(error during startup: $(call str_decode_nospace,$(call _pr_str,$(__ERROR),yes))))\nendef\n\n# core.mk: defined using Make\n$(foreach f,$(core_ns)\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\ncore_eval = $(call EVAL,$1,$(REPL_ENV))\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,eval),$(call _corefn,core_eval))\n\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,*ARGV*),$(call list,$(foreach arg,\\\n  $(call _rest,$(MAKECMDGOALS)),$(call _string,$(call str_encode_nospace,$(arg))))))\n\n# core.mal: defined in terms of the language itself\n$(call RE, (def! not (fn* (a) (if a false true))) )\n$(call RE, (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) )\n$(call RE, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs))))))) )\n\nifneq (,$(MAKECMDGOALS))\n# Load and eval any files specified on the command line\n$(call RE, (load-file \"$(firstword $(MAKECMDGOALS))\") )\nelse\n# repl loop\n$(REPL)\nendif\n\n# Do not complain that there is no target.\n.PHONY: none $(MAKECMDGOALS)\nnone $(MAKECMDGOALS):\n\t@true\n"
  },
  {
    "path": "impls/make/stepA_mal.mk",
    "content": "#\n# mal (Make Lisp)\n#\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)readline.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)types.mk\ninclude $(_TOP_DIR)reader.mk\ninclude $(_TOP_DIR)printer.mk\ninclude $(_TOP_DIR)env.mk\ninclude $(_TOP_DIR)core.mk\n\nSHELL := /usr/bin/env bash\n\n# READ: read and parse input\ndefine READ\n$(READ_STR)\nendef\n\n# EVAL: evaluate the parameter\n\n# If $1 is empty, `foreach` does no iteration at all.\nstarts_with? = $(foreach f,$(firstword $1)\\\n                 ,$(and $(call _symbol?,$f),\\\n                        $(filter $2,$(call _symbol_val,$f))))\n\n# elt, accumulator list -> new accumulator list\nQQ_LOOP = $(if $(and $(_list?),\\\n                     $(call starts_with?,$(_seq_vals),splice-unquote))\\\n  ,$(call list,$(call _symbol,concat) $(lastword $(_seq_vals)) $2)$(rem \\\n  ),$(call list,$(call _symbol,cons) $(call QUASIQUOTE,$1) $2))\n\n# list or vector source -> right folded list\nQQ_FOLD = $(if $1\\\n  ,$(call QQ_LOOP,$(firstword $1),$(call QQ_FOLD,$(_rest)))$(rem \\\n  ),$(call list))\n\nQUASIQUOTE = $(call QUASIQUOTE_$(_obj_type),$1)\nQUASIQUOTE_nil     = $1\nQUASIQUOTE_true    = $1\nQUASIQUOTE_false   = $1\nQUASIQUOTE_string  = $1\nQUASIQUOTE_number  = $1\nQUASIQUOTE_keyword = $1\nQUASIQUOTE_symbol  = $(call list,$(call _symbol,quote) $1)\nQUASIQUOTE_map     = $(call list,$(call _symbol,quote) $1)\n\nQUASIQUOTE_vector = $(call list,$(call _symbol,vec) $(call QQ_FOLD,$(_seq_vals)))\n\nQUASIQUOTE_list = $(if $(call starts_with?,$(_seq_vals),unquote)\\\n  ,$(lastword $(_seq_vals))$(rem \\\n  ),$(call QQ_FOLD,$(_seq_vals)))\n\nEVAL_special_quote = $1\n\nEVAL_special_quasiquote = $(call EVAL,$(QUASIQUOTE),$2)\n\nEVAL_nil     = $1\nEVAL_true    = $1\nEVAL_false   = $1\nEVAL_string  = $1\nEVAL_number  = $1\nEVAL_keyword = $1\n\nEVAL_symbol = $(or $(call ENV_GET,$2,$1),$(call _error,'$(_symbol_val)' not found))\n\nEVAL_vector = $(call vector,$(foreach e,$(_seq_vals),$(call EVAL,$e,$2)))\n\n# First foreach defines a constant, second one loops on keys.\ndefine EVAL_map\n$(foreach obj,$(call _map_new)\\\n,$(obj)$(rem $(foreach k,$(_keys)\\\n  ,$(call _assoc!,$(obj),$k,$(call EVAL,$(call _get,$1,$k),$2)))))\nendef\n\ndefine EVAL_list\n$(if $(_seq_vals)\\\n  ,$(foreach a0,$(firstword $(_seq_vals))\\\n    ,$(if $(call _symbol?,$(a0))\\\n      ,$(foreach dispatch,EVAL_special_$(call _symbol_val,$(a0))\\\n        ,$(if $(filter undefined,$(flavor $(dispatch)))\\\n          ,$(call EVAL_apply,$(_seq_vals),$2)$(rem \\\n          ),$(call $(dispatch),$(call _rest,$(_seq_vals)),$2)))$(rem \\\n      ),$(call EVAL_apply,$(_seq_vals),$2)))$(rem \\\n  ),$1)\nendef\n\ndefine EVAL_apply\n$(foreach f,$(call EVAL,$(firstword $1),$2)\\\n,$(if $(__ERROR)\\\n  ,,$(if $(call _macro?,$f)\\\n    ,$(call EVAL,$(call _apply,$f,$(_rest)),$2)$(rem \\\n    ),$(call _apply,$f,$(foreach a,$(_rest),$(call EVAL,$a,$2))))))\nendef\n\ndefine EVAL_special_defmacro!\n$(foreach res,$(call _as_macro,$(call EVAL,$(lastword $1),$2))\\\n  ,$(res)$(call ENV_SET,$2,$(firstword $1),$(res)))\nendef\n\ndefine EVAL_special_def!\n$(foreach res,$(call EVAL,$(lastword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)$(call ENV_SET,$2,$(firstword $1),$(res))))\nendef\n\ndefine EVAL_special_let*\n$(foreach let_env,$(call ENV,$2)\\\n,$(call _foreach2,$(call _seq_vals,$(firstword $1))\\\n  ,$$(call ENV_SET,$(let_env),$$k,$$(call EVAL,$$v,$(let_env))))$(rem \\\n)$(call EVAL,$(lastword $1),$(let_env)))\nendef\n\nEVAL_special_do = $(lastword $(foreach x,$1,$(call EVAL,$x,$2)))\n\ndefine EVAL_special_if\n$(if $(call truthy?,$(call EVAL,$(firstword $1),$2))\\\n  ,$(call EVAL,$(word 2,$1),$2)$(rem \\\n),$(if $(word 3,$1)\\\n  ,$(call EVAL,$(lastword $1),$2)$(rem \\\n),$(__nil)))\nendef\n\nEVAL_special_fn* = $(call _function,$(call _seq_vals,$(firstword $1)),$(lastword $1),$2)\n\n# EVAL may fail and return nothing, so the first foreach may execute\n# nothing, so we need to duplicate the test for error.\n# The second foreach deliberately does nothing when there is no\n# catch_list.\ndefine EVAL_special_try*\n$(foreach res,$(call EVAL,$(firstword $1),$2)\\\n  ,$(if $(__ERROR)\\\n    ,,$(res)))$(rem \\\n)$(if $(__ERROR)\\\n  ,$(foreach catch_list,$(word 2,$1)\\\n    ,$(foreach env,$(call ENV,$2)\\\n    ,$(call ENV_SET,$(env),$(word 2,$(call _seq_vals,$(catch_list))),$(__ERROR))$(rem \\\n    )$(eval __ERROR :=)$(rem \\\n    )$(call EVAL,$(lastword $(call _seq_vals,$(catch_list))),$(env)))))\nendef\n\ndefine EVAL_special_make*\n$(eval __result := $(call str_decode_nospace,$(_string_val)))$(rem \\\n)$(call _string,$(call str_encode_nospace,$(__result)))\nendef\n\ndefine EVAL\n$(if $(__ERROR)\\\n,,$(if $(call truthy?,$(call ENV_GET,$(2),$(call _symbol,DEBUG-EVAL)))\\\n  ,$(call print,EVAL: $(call _pr_str,$1,yes)    env: $(call env_keys,$2)))$(rem \\\n)$(call EVAL_$(_obj_type),$1,$2))\nendef\n\n\n# PRINT:\ndefine PRINT\n$(if $(__ERROR)\\\n  ,Error$(encoded_colon)$(_SP)$(call _pr_str,$(__ERROR),yes)$(rem \\\n  ),$(call _pr_str,$1,yes))\nendef\n\n# REPL:\nREPL_ENV := $(call ENV)\nREP = $(call PRINT,$(call EVAL,$(READ),$(REPL_ENV)))\n\n# The foreach does nothing when line is empty (EOF).\ndefine REPL\n$(foreach line,$(call READLINE,user>$(_SP))\\\n,$(eval __ERROR :=)$(rem \\\n)$(call print,$(call REP,$(line:ok=)))$(rem \\\n)$(call REPL))\nendef\n\n# Read and evaluate for side effects but ignore the result.\ndefine RE\n$(rem $(call EVAL,$(call READ,$(str_encode_nospace)),$(REPL_ENV)) \\\n)$(if $(__ERROR)\\\n  ,$(error during startup: $(call str_decode_nospace,$(call _pr_str,$(__ERROR),yes))))\nendef\n\n# core.mk: defined using Make\n$(foreach f,$(core_ns)\\\n  ,$(call ENV_SET,$(REPL_ENV),$(call _symbol,$f),$(call _corefn,$f)))\n\ncore_eval = $(call EVAL,$1,$(REPL_ENV))\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,eval),$(call _corefn,core_eval))\n\n$(call ENV_SET,$(REPL_ENV),$(call _symbol,*ARGV*),$(call list,$(foreach arg,\\\n  $(call _rest,$(MAKECMDGOALS)),$(call _string,$(call str_encode_nospace,$(arg))))))\n\n# core.mal: defined in terms of the language itself\n$(call RE, (def! not (fn* (a) (if a false true))) )\n$(call RE, (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) )\n$(call RE, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs))))))) )\n$(call RE, (def! *host-language* \"make\") )\n\nifneq (,$(MAKECMDGOALS))\n# Load and eval any files specified on the command line\n$(call RE, (load-file \"$(firstword $(MAKECMDGOALS))\") )\nelse\n# repl loop\n$(call RE, (println (str \"Mal [\" *host-language* \"]\")) )\n$(REPL)\nendif\n\n# Do not complain that there is no target.\n.PHONY: none $(MAKECMDGOALS)\nnone $(MAKECMDGOALS):\n\t@true\n"
  },
  {
    "path": "impls/make/tests/stepA_mal.mal",
    "content": ";; Testing basic make interop\n\n(make* \"7\")\n;=>\"7\"\n\n(make* \"$(info foo)\")\n;/foo\n;=>\"\"\n\n(make* \"$(eval foo := 8)\")\n(make* \"$(foo)\")\n;=>\"8\"\n\n(make* \"$(foreach v,a b c,X$(v)Y)\")\n;=>\"XaY XbY XcY\"\n\n(read-string (make* \"($(foreach v,1 2 3,$(call int_add,1,$(v))))\"))\n;=>(2 3 4)\n\n"
  },
  {
    "path": "impls/make/types.mk",
    "content": "#\n# mal (Make a Lisp) object types\n#\n\nifndef __mal_types_included\n__mal_types_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)gmsl.mk\ninclude $(_TOP_DIR)util.mk\ninclude $(_TOP_DIR)numbers.mk\n\n\n# Low-level type implemenation\n\n# magic is \\u2344 \\u204a\n__obj_magic := ⍄⁊\n# \\u2256\n__obj_hash_code := 0\n\n\n# 1:type  2:optional content  ->  variable name\ndefine __new_obj\n$(eval __obj_hash_code := $(call int_add,1,$(__obj_hash_code)))$(rem \\\n)$(foreach obj,$(__obj_magic)_$(__obj_hash_code)_$1\\\n  ,$(obj)$(if $2,$(eval $(obj) := $2)))\nendef\n\n\n# Visualize Objects in memory\n_visualize_memory = $(foreach v,$(sort $(filter $(__obj_magic)_%,$(.VARIABLES)))\\\n                      ,$(info $v  $($v)))\n\n\n# Errors/Exceptions\n__ERROR :=\nthrow = $(eval __ERROR := $1)\n_error = $(call throw,$(call _string,$(str_encode_nospace)))\n\n\n# Constant atomic values\n__nil   := _nil\n__true  := _true\n__false := _false\n\n\n# General functions\n\n_obj_type = $(lastword $(subst _, ,$1))\n\n_clone_obj = $(_clone_obj_$(_obj_type))\n_clone_obj_list     = $(call list,$($1))\n_clone_obj_vector   = $(call vector,$($1))\n_clone_obj_map      = $(_map_new)\n_clone_obj_function = $(call __new_obj,function,$($1))\n_clone_obj_corefn   = $(call _corefn,$($1))\n\ndefine _hash_equal?\n$(if $3\\\n  ,$(and $(call _equal?,$($1_$(firstword $3)),$($2_$(firstword $3))),\\\n         $(call _hash_equal?,$1,$2,$(call _rest,$3)))$(rem \\\n  ),true)\nendef\n\ndefine _equal?_seq_loop\n$(if $1\\\n  ,$(and $2,\\\n         $(call _equal?,$(firstword $1),$(firstword $2)),\\\n         $(call _equal?_seq_loop,$(_rest),$(call _rest,$2)))$(rem \\\n  ),$(if $2,,true))\nendef\n\ndefine _equal?\n$(or $(filter $1,$2),\\\n     $(and $(filter %_list %_vector,$1),\\\n           $(filter %_list %_vector,$2),\\\n           $(call _equal?_seq_loop,$($1),$($2))),\\\n     $(and $(filter %_map,$1),\\\n           $(filter %_map,$2),\\\n           $(call _EQ,$(_keys),$(call _keys,$2)),\\\n           $(call _hash_equal?,$1,$2,$(_keys))))\nendef\n\n_nil? = $(filter $(__nil),$1)\n\n_true? = $(filter $(__true),$1)\n\n_false? = $(filter $(__false),$1)\n\n# Conveniently for DEBUG-EVAL, returns false if $1 is empty.\ntruthy? = $(filter-out _nil _false,$1)\n\n\n# Symbols\n_symbol = $1_symbol\n_symbol_val = $(1:_symbol=)\n_symbol? = $(filter %_symbol,$1)\n\n\n# Keywords\n_keyword = $1_keyword\n_keyword? = $(filter %_keyword,$1)\n_keyword_val = $(1:_keyword=)\n\n\n# Numbers\n_number = $1_number\n_number? = $(filter %_number,$1)\n_number_val = $(1:_number=)\n\n\n# Strings\n_string = $1_string\n_string? = $(filter %_string,$1)\n_string_val = $(1:_string=)\n\n# Functions\n\n_corefn = $(call __new_obj,corefn,$1)\n_function = $(call __new_obj,function,$2 $3 $1)\n_as_macro = $(call __new_obj,macro,$($1))\n_fn? = $(filter %_corefn %_function,$1)\n_macro? = $(filter %_macro,$1)\n\n# 1:env 2:formal parameters 3:actual parameters\ndefine _function_set_env\n$(if $2\\\n  ,$(if $(filter &_symbol,$(firstword $2))\\\n    ,$(call ENV_SET,$1,$(lastword $2),$(call list,$3)),$(rem \\\n  else \\\n    $(call ENV_SET,$1,$(firstword $2),$(firstword $3))\n    $(call _function_set_env,$1,$(call _rest,$2),$(call _rest,$3)))))\nendef\n\n# Takes a function object and a list object of arguments and invokes\n# the function with space separated arguments\ndefine _apply\n$(if $(filter %_corefn,$1)\\\n  ,$(call $($1),$2)$(rem \\\n),$(if $(filter %_function %_macro,$1)\\\n  ,$(foreach env,$(call ENV,$(word 2,$($1)))\\\n    ,$(call _function_set_env,$(env),$(call _rest2,$($1)),$2)$(rem \\\n    )$(call EVAL,$(firstword $($1)),$(env)))$(rem \\\n),$(call _error,cannot apply non-function)))\nendef\n\n\n# Lists\nlist = $(if $1,$(call __new_obj,list,$1),empty_list)\n_list? = $(filter %_list,$1)\n\n_seq_vals = $($1)\n\n\n# Vectors (same as lists for now)\nvector = $(if $1,$(call __new_obj,vector,$1),empty_vector)\n_vector? = $(filter %_vector,$1)\n\n\n# Hash maps (associative arrays)\n# 1:optional source map  2:optional key/value pairs  3:optional removals\ndefine _map_new\n$(foreach obj,$(call __new_obj,map,$(filter-out $3,$(if $1,$($1))))\\\n,$(obj)$(rem \\\n$(foreach k,$($(obj))\\\n  ,$(eval $(obj)_$k := $($1_$k)))\\\n$(call _foreach2,$2\\\n  ,$$(call _assoc!,$(obj),$$k,$$v))))\nendef\n\n_hash_map? = $(filter %_map,$1)\n\n\n# set a key/value in the hash map\n# map key val\n# sort removes duplicates.\n_assoc! = $(eval $1_$2 := $3)$(eval $1 := $(sort $($1) $2))\n\n_keys = $($1)\n\n# retrieve the value of a plain string key from the hash map, or\n# return the empty string if the key is missing\n_get = $($1_$2)\n\n\n# sequence operations\n\n_sequential? = $(filter %_list %_vector,$1)\n\n\n# Metadata functions\n\nwith-meta = $(foreach obj,$(call _clone_obj,$(firstword $1))\\\n  ,$(obj)$(eval $(obj)_meta := $(lastword $1)))\n\nmeta = $(or $($1_meta),$(__nil))\n\n\n# atoms\n\natom = $(call __new_obj,atom,$1)\n_atom? = $(filter %_atom,$1)\nderef = $($1)\n_reset = $(eval $1 = $2)\n\n\nendif\n"
  },
  {
    "path": "impls/make/util.mk",
    "content": "#\n# mal (Make Lisp) utility functions/definitions\n#\n\nifndef __mal_util_included\n__mal_util_included := true\n\n_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\ninclude $(_TOP_DIR)gmsl.mk\n\nencoded_equal := Ξ\nencoded_colon := κ\nencoded_slash := λ\nraw_hash      := \\#\nencoded_hash  := η\n\nCOMMA := ,\nCOLON := :\nLPAREN := (\nRPAREN := )\nSLASH := $(strip \\ )\nSPACE :=\nSPACE := $(SPACE) $(SPACE)\ndefine NEWLINE\n\n\nendef\n\n# \\u00ab\n_LP := «\n# \\u00bb\n_RP := »\n## \\u00a7\n_SP := §\n## \\u00ae\n_DOL := Ş\n## \\u00b6\n_NL := ¶\n\n\n#\n# Utility functions\n#\n\n_EQ = $(if $(subst x$1,,x$2)$(subst x$2,,x$1),,true)\n\n# reverse list of words\n_reverse = $(if $1,$(call _reverse,$(_rest)) $(firstword $1))\n\n\n#$(info reverse(1 2 3 4 5): $(call reverse,1 2 3 4 5))\n\n# str_encode: take a string and return an encoded version of it with\n# every character separated by a space and special characters replaced\n# with special Unicode characters\ndefine str_encode\n$(eval __temp := $1)$(rem \\\n)$(foreach a,$(encoded_slash) $(_DOL) $(_LP) $(_RP) $(_NL) \\\n  $(encoded_hash) $(encoded_colon) $(_SP) $(encoded_equal) $(gmsl_characters)\\\n  ,$(eval __temp := $$(subst $$a,$$a$$(SPACE),$(__temp))))$(rem \\\n)$(__temp)\nendef\n\n# str_decode: take an encoded string an return an unencoded version of\n# it by replacing the special Unicode charactes with the real\n# characters and with all characters joined into a regular string\nstr_decode = $(subst $(SPACE),,$1)\n\ndefine str_encode_nospace\n$(subst $(SLASH),$(encoded_slash),$(rem \\\n)$(subst $$,$(_DOL),$(rem \\\n)$(subst $(LPAREN),$(_LP),$(rem \\\n)$(subst $(RPAREN),$(_RP),$(rem \\\n)$(subst $(NEWLINE),$(_NL),$(rem \\\n)$(subst $(raw_hash),$(encoded_hash),$(rem \\\n)$(subst $(COLON),$(encoded_colon),$(rem \\\n)$(subst $(SPACE),$(_SP),$(rem \\\n)$(subst =,$(encoded_equal),$(rem \\\n)$1)))))))))\nendef\n\ndefine str_decode_nospace\n$(subst $(encoded_slash),$(SLASH),$(rem \\\n)$(subst $(_DOL),$$,$(rem \\\n)$(subst $(_LP),$(LPAREN),$(rem \\\n)$(subst $(_RP),$(RPAREN),$(rem \\\n)$(subst $(_NL),$(NEWLINE),$(rem \\\n)$(subst $(encoded_hash),$(raw_hash),$(rem \\\n)$(subst $(encoded_colon),$(COLON),$(rem \\\n)$(subst $(_SP),$(SPACE),$(rem \\\n)$(subst $(encoded_equal),=,$1)))))))))\nendef\n\n# Read a whole file substituting newlines with $(_NL)\n_read_file = $(call str_encode_nospace,$(shell \\\n  sed -z 's/\\n/$(_NL)/g' '$(str_decode_nospace)'))\n\nprint = $(info $(str_decode_nospace))\n\n_rest = $(wordlist 2,$(words $1),$1)\n_rest2 = $(wordlist 3,$(words $1),$1)\n\n# Evaluate $2 repeatedly with $k and $v set to key/value pairs from $1.\ndefine _foreach2\n$(foreach k,$(firstword $1)\\\n  ,$(foreach v,$(word 2,$1)\\\n    ,$(eval $2)$(call _foreach2,$(_rest2),$2)))\nendef\n\nendif\n"
  },
  {
    "path": "impls/matlab/+types/Atom.m",
    "content": "classdef Atom < handle\n    properties\n        val\n    end\n    methods\n        function atm = Atom(val)\n            atm.val = val;\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/Function.m",
    "content": "classdef Function < handle\n    properties\n        fn\n        ast\n        env\n        params\n        is_macro = false;\n        meta = type_utils.nil;\n    end\n    methods\n        function f = Function(fn, ast, env, params)\n            f.fn = fn;\n            f.ast = ast;\n            f.env = env;\n            f.params = params;\n        end\n\n        function ret = clone(obj)\n            ret = types.Function(obj.fn, obj.ast, obj.env, obj.params);\n            ret.is_macro = obj.is_macro;\n            ret.meta = obj.meta;\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/HashMap.m",
    "content": "classdef HashMap < handle\n    properties\n        data\n        meta = type_utils.nil;\n    end\n    methods\n        function obj = HashMap(varargin)\n            if nargin == 0\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    obj.data = Dict();\n                else\n                    obj.data = containers.Map();\n                end\n            else\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    obj.data = Dict();\n                    for i=1:2:length(varargin)\n                        obj.data(varargin{i}) = varargin{i+1};\n                    end\n                else\n                    obj.data = containers.Map(varargin(1:2:end), ...\n                                              varargin(2:2:end));\n                end\n            end\n        end\n\n        function len = length(obj)\n            len = length(obj.data);\n        end\n\n        function ret = get(obj, key)\n            ret = obj.data(key);\n        end\n\n        function ret = set(obj, key, val)\n            obj.data(key) = val;\n            ret = val;\n        end\n\n        function ret = keys(obj)\n            ret = obj.data.keys();\n        end\n\n        function ret = values(obj)\n            ret = obj.data.values();\n        end\n\n        function ret = clone(obj)\n            ret = types.HashMap();\n            if length(obj) > 0\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    ret.data = Dict(obj.data.keys(), obj.data.values());\n                else\n                    ret.data = containers.Map(obj.data.keys(), obj.data.values());\n                end\n            else\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    ret.data = Dict();\n                else\n                    ret.data = containers.Map();\n                end\n            end\n            ret.meta = obj.meta;\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/List.m",
    "content": "classdef List < handle\n    properties\n        data\n        meta = type_utils.nil;\n    end\n    methods\n        function obj = List(varargin)\n            obj.data = varargin;\n            meta = type_utils.nil;\n        end\n\n        function len = length(obj)\n            len = length(obj.data);\n        end\n\n        function ret = get(obj, idx)\n            ret = obj.data{idx};\n        end\n\n        function ret = set(obj, key, val)\n            obj.data{key} = val;\n            ret = val;\n        end\n\n        function ret = append(obj, val)\n            obj.data{end+1} = val;\n            ret = val;\n        end\n\n        function ret = slice(obj, start, last)\n            if nargin < 3\n                last = length(obj.data);\n            end\n            ret = types.List(obj.data{start:last});\n        end\n\n        function ret = clone(obj)\n            ret = types.List();\n            ret.data = obj.data;\n            ret.meta = obj.meta;\n        end\n\n%        function varargout = subsref(vec, S)\n%            % This doesn't work for ranges\n%            [varargout{1:nargout}] = builtin('subsref', vec.data, S);\n%\n%            varargout = cell(1,max(1,nargout));\n%            [varargout{:}] = builtin('subsref',vec.data,S);\n%\n%%            switch S.type\n%%            case '()'\n%%                varargout = cell(1,numel(vec));\n%%                varargout{1} = builtin('subsref', vec.data, S);\n%%            case '{}'\n%%                varargout = cell(1,numel(vec));\n%%                varargout{1} = builtin('subsref', vec.data, S);\n%%            case '.'\n%%                error('Vector property access not yet implemented');\n%%            end\n%        end\n\n%        %function n = numel(varargin)\n%        %    n = 1;\n%        %end\n\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/MalException.m",
    "content": "classdef MalException < MException\n    properties\n        obj\n    end\n    methods\n        function exc = MalException(obj)\n            exc@MException('MalException:object', 'MalException'); \n            exc.obj = obj;\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/Nil.m",
    "content": "classdef Nil\n    methods\n        function len = length(obj)\n            len = 0;\n        end\n        function ret = eq(a,b)\n            ret = strcmp(class(b),'types.Nil');\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/Reader.m",
    "content": "classdef Reader < handle\n    properties\n        tokens\n        position\n    end\n    methods\n        function rdr = Reader(tokens)\n            rdr.tokens = tokens;\n            rdr.position = 1;\n        end\n        function tok = next(rdr)\n            rdr.position = rdr.position + 1;\n            if rdr.position-1 > length(rdr.tokens)\n                tok = false;\n            else\n                tok = rdr.tokens{rdr.position-1};\n            end\n        end\n        function tok = peek(rdr)\n            if rdr.position > length(rdr.tokens)\n                tok = false;\n            else\n                tok = rdr.tokens{rdr.position};\n            end\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/Symbol.m",
    "content": "classdef Symbol\n    properties\n        name\n    end\n    methods\n        function sym = Symbol(name)\n            sym.name = name;\n        end\n        function ret = eq(a,b)\n            ret = strcmp(a.name, b.name);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/+types/Vector.m",
    "content": "classdef Vector < types.List\n    methods\n        function obj = Vector(varargin)\n            obj.data = varargin;\n            meta = type_utils.nil;\n        end\n\n        function ret = slice(obj, start, last)\n            if nargin < 3\n                last = length(obj.data);\n            end\n            ret = types.Vector(obj.data{2:end});\n        end\n\n        function ret = clone(obj)\n            ret = types.Vector();\n            ret.data = obj.data;\n            ret.meta = obj.meta;\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/.dockerignore",
    "content": "octave-4.0.0*\n"
  },
  {
    "path": "impls/matlab/Dict.m",
    "content": "% Implement containers.Map like structure\n% This only applies to GNU Octave and will break in Matlab when\n% arbitrary string keys are used.\nclassdef Dict < handle\n    properties\n        data\n    end\n    methods\n        function dict = Dict(keys, values)\n            dict.data = struct();\n\n            if nargin > 0\n                for i=1:length(keys)\n                    dict.data.(keys{i}) = values{i};\n                end\n            end\n        end\n\n        function ret = subsasgn(dict, ind, val)\n            dict.data.(ind(1).subs{1}) = val;\n            ret = dict;\n        end\n        function ret = subsref(dict, ind)\n            if strcmp('.', ind(1).type)\n                % Function call\n                switch ind(1).subs\n                case 'isKey'\n                    if numel(ind) > 1\n                        ret = isfield(dict.data, ind(2).subs{1});\n                    else\n                        error('Dict:invalidArgs', ...\n                            sprintf('''%s'' called with no arguments', ind(1).subs));\n                    end\n                case 'keys'\n                    ret = fieldnames(dict.data);\n                case 'values'\n                    ret = {};\n                    keys = fieldnames(dict.data);\n                    for i=1:length(keys)\n                        ret{end+1} = dict.data.(keys{i});\n                    end\n                case 'remove'\n                    if numel(ind) > 1\n                        if numel(ind(2).subs) > 0\n                            dict.data = rmfield(dict.data, ind(2).subs{1});\n                        end\n                    else\n                        error('Dict:invalidArgs', ...\n                            sprintf('''%s'' called with no arguments', ind(1).subs));\n                    end\n                otherwise\n                    error('Dict:notfound', ...\n                          sprintf('''%s'' not found', ind(1).subs));\n                end\n            else\n                % Key lookup\n                ret = dict.data.(ind(1).subs{1});\n            end\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install octave\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/matlab/Env.m",
    "content": "classdef Env < handle\n    properties\n        data\n        outer\n    end\n    methods\n        function env = Env(outer, binds, exprs)\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                env.data = Dict();\n            else\n                env.data = containers.Map();\n            end\n\n            if nargin == 0\n                env.outer = false;\n            else\n                % Workaround Octave calling bug when the first\n                % argument is the same type as the class (the class is\n                % not properly initialized in that case)\n                env.outer = outer{1};\n            end\n\n            if nargin > 1\n                %env = Env(outer);\n                for i=1:length(binds)\n                    k = binds.get(i).name;\n                    if strcmp(k, '&')\n                        env.data(binds.get(i+1).name) = exprs.slice(i);\n                        break;\n                    else\n                        env.data(k) = exprs.get(i);\n                    end\n                end\n            end\n        end\n\n        function ret = set(env, k, v)\n            env.data(k.name) = v;\n            ret = v;\n        end\n\n        function ret = get(env, k)\n            while ~env.data.isKey(k)\n                env = env.outer;\n                if islogical(env)\n                    ret = {};\n                    return;\n                end\n            end\n            ret = env.data(k);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/Makefile",
    "content": "all:\n\nclean:\n\n"
  },
  {
    "path": "impls/matlab/core.m",
    "content": "classdef core\n    methods(Static)\n        function ret = throw(obj)\n            ret = type_utils.nil;\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                % Until Octave has MException objects, we need to\n                % store the error object globally to be able to pass\n                % it to the error handler.\n                global error_object;\n                error_object = obj;\n                exc = struct('identifier', 'MalException:object',...\n                             'message', 'MalException');\n                rethrow(exc);\n            else\n                throw(types.MalException(obj));\n            end\n        end\n\n        function str = pr_str(varargin)\n            strs = cellfun(@(s) printer.pr_str(s,true), varargin, ...\n                    'UniformOutput', false);\n            str = strjoin(strs, ' ');\n        end\n        function str = do_str(varargin)\n            strs = cellfun(@(s) printer.pr_str(s,false), varargin, ...\n                    'UniformOutput', false);\n            str = strjoin(strs, '');\n        end\n        function ret = prn(varargin)\n            strs = cellfun(@(s) printer.pr_str(s,true), varargin, ...\n                    'UniformOutput', false);\n            fprintf('%s\\n', strjoin(strs, ' '));\n            ret = type_utils.nil;\n        end\n        function ret = println(varargin)\n            strs = cellfun(@(s) printer.pr_str(s,false), varargin, ...\n                    'UniformOutput', false);\n            fprintf('%s\\n', strjoin(strs, ' '));\n            ret = type_utils.nil;\n        end\n\n        function ret = time_ms()\n            secs = now-repmat(datenum('1970-1-1 00:00:00'),size(now));\n            ret = floor(secs.*repmat(24*3600.0*1000,size(now)));\n        end\n\n        function new_hm = assoc(hm, varargin)\n            new_hm = clone(hm);\n            for i=1:2:length(varargin)\n                new_hm.set(varargin{i}, varargin{i+1});\n            end\n        end\n\n        function new_hm = dissoc(hm, varargin)\n            new_hm = clone(hm);\n            ks = intersect(hm.keys(),varargin);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                new_hm.data.remove(ks);\n            else\n                remove(new_hm.data, ks);\n            end\n        end\n\n        function ret = get(hm, key)\n            if isa(hm, 'types.Nil')\n                ret = type_utils.nil;\n            elseif hm.data.isKey(key)\n                ret = hm.data(key);\n            else\n                ret = type_utils.nil;\n            end\n        end\n\n        function ret = keys(hm)\n            ks = hm.keys();\n            ret = types.List(ks{:});\n        end\n\n        function ret = vals(hm)\n            vs = hm.values();\n            ret = types.List(vs{:});\n        end\n\n        function ret = cons(a, seq)\n            cella = [{a}, seq.data];\n            ret = types.List(cella{:});\n        end\n\n        function ret = concat(varargin)\n            if nargin == 0\n                cella = {};\n            else\n                cells = cellfun(@(x) x.data, varargin, ...\n                                'UniformOutput', false);\n                cella = cat(2,cells{:});\n            end\n            ret = types.List(cella{:});\n        end\n\n        function ret = first(seq)\n            if isa(seq, 'types.Nil')\n                ret = type_utils.nil;\n            elseif length(seq) < 1\n                ret = type_utils.nil;\n            else\n                ret = seq.get(1);\n            end\n        end\n\n        function ret = rest(seq)\n            if isa(seq, 'types.Nil')\n                ret = types.List();\n            else\n                cella = seq.data(2:end);\n                ret = types.List(cella{:});\n            end\n        end\n\n        function ret = nth(seq, idx)\n            if idx+1 > length(seq)\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    error('Range:nth', ...\n                          'nth: index out of range');\n                else\n                    throw(MException('Range:nth', ...\n                                     'nth: index out of range'))\n                end\n            end\n            ret = seq.get(idx+1);\n        end\n\n        function ret = apply(varargin)\n            f = varargin{1};\n            if isa(f, 'types.Function')\n                f = f.fn;\n            end\n            first_args = varargin(2:end-1);\n            rest_args = varargin{end}.data;\n            args = [first_args rest_args];\n            ret = f(args{:});\n        end\n\n        function ret = map(f, lst)\n            if isa(f, 'types.Function')\n                f = f.fn;\n            end\n            cells = cellfun(@(x) f(x), lst.data, 'UniformOutput', false);\n            ret = types.List(cells{:});\n        end\n\n        function ret = conj(varargin)\n            seq = varargin{1};\n            args = varargin(2:end);\n            if type_utils.list_Q(seq)\n                cella = [fliplr(args), seq.data];\n                ret = types.List(cella{:});\n            else\n                cella = [seq.data, args];\n                ret = types.Vector(cella{:});\n            end\n        end\n\n        function ret = seq(obj)\n            if type_utils.list_Q(obj)\n                if length(obj) > 0\n                    ret = obj;\n                else\n                    ret = type_utils.nil;\n                end\n            elseif type_utils.vector_Q(obj)\n                if length(obj) > 0\n                    ret = types.List(obj.data{:});\n                else\n                    ret = type_utils.nil;\n                end\n            elseif type_utils.string_Q(obj)\n                if length(obj) > 0\n                    cells = cellfun(@(c) char(c),...\n                                    num2cell(double(obj)),...\n                                    'UniformOutput', false);\n                    ret = types.List(cells{:});\n                else\n                    ret = type_utils.nil;\n                end\n            elseif isa(obj, 'types.Nil')\n                ret = type_utils.nil;\n            else\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    error('Type:seq', ...\n                          'seq: called on non-sequence');\n                else\n                    throw(MException('Type:seq',...\n                                     'seq: called on non-sequence'))\n                end\n            end\n        end\n\n        function new_obj = with_meta(obj, meta)\n            new_obj = clone(obj);\n            new_obj.meta = meta;\n        end\n\n        function meta = meta(obj)\n            switch class(obj)\n            case {'types.List', 'types.Vector',\n                  'types.HashMap', 'types.Function'}\n                meta = obj.meta;\n            otherwise\n                meta = type_utils.nil;\n            end\n        end\n\n        function ret = reset_BANG(atm, val)\n            atm.val = val;\n            ret = val;\n        end\n\n        function ret = swap_BANG(atm, f, varargin)\n            args = [{atm.val} varargin];\n            if isa(f, 'types.Function')\n                f = f.fn;\n            end\n            atm.val = f(args{:});\n            ret = atm.val;\n        end\n\n        function n = ns()\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                n = Dict();\n            else\n                n = containers.Map();\n            end\n            n('=') =  @(a,b) type_utils.equal(a,b);\n            n('throw') = @(a) core.throw(a);\n            n('nil?') = @(a) isa(a, 'types.Nil');\n            n('true?') = @(a) isa(a, 'logical') && a == true;\n            n('false?') = @(a) isa(a, 'logical') && a == false;\n            n('string?') = @(a) type_utils.string_Q(a);\n            n('symbol') = @(a) types.Symbol(a);\n            n('symbol?') = @(a) isa(a, 'types.Symbol');\n            n('keyword') = @(a) type_utils.keyword(a);\n            n('keyword?') = @(a) type_utils.keyword_Q(a);\n            n('number?') = @(a) type_utils.number_Q(a);\n            n('fn?') = @(a) type_utils.fn_Q(a);\n            n('macro?') = @(a) type_utils.macro_Q(a);\n\n            n('pr-str') = @(varargin) core.pr_str(varargin{:});\n            n('str') = @(varargin) core.do_str(varargin{:});\n            n('prn') = @(varargin) core.prn(varargin{:});\n            n('println') = @(varargin) core.println(varargin{:});\n            n('read-string') = @(a) reader.read_str(a);\n            n('readline') = @(p) input(p, 's');\n            n('slurp') = @(a) fileread(a);\n\n            n('<') =  @(a,b) a<b;\n            n('<=') = @(a,b) a<=b;\n            n('>') =  @(a,b) a>b;\n            n('>=') = @(a,b) a>=b;\n            n('+') =  @(a,b) a+b;\n            n('-') =  @(a,b) a-b;\n            n('*') =  @(a,b) a*b;\n            n('/') =  @(a,b) floor(a/b);\n            n('time-ms') = @() core.time_ms();\n\n            n('list') = @(varargin) types.List(varargin{:});\n            n('list?') = @(a) type_utils.list_Q(a);\n            n('vector') = @(varargin) types.Vector(varargin{:});\n            n('vector?') = @(a) type_utils.vector_Q(a);\n            n('hash-map') = @(varargin) types.HashMap(varargin{:});\n            n('map?') = @(a) type_utils.hash_map_Q(a);\n            n('assoc') = @(varargin) core.assoc(varargin{:});\n            n('dissoc') = @(varargin) core.dissoc(varargin{:});\n            n('get') = @(a,b) core.get(a,b);\n            n('contains?') = @(a,b) a.data.isKey(b);\n            n('keys') = @(a) core.keys(a);\n            n('vals') = @(a) core.vals(a);\n\n            n('sequential?') = @(a) type_utils.sequential_Q(a);\n            n('cons') = @(a,b) core.cons(a,b);\n            n('concat') = @(varargin) core.concat(varargin{:});\n            n('vec') = @(a) types.Vector(a.data{:});\n            n('nth') = @(a,b) core.nth(a,b);\n            n('first') = @(a) core.first(a);\n            n('rest') = @(a) core.rest(a);\n            n('empty?') = @(a) length(a) == 0;\n            % workaround Octave always giving length(a) of 1\n            n('count') = @(a) 0 + length(a);\n            n('apply') = @(varargin) core.apply(varargin{:});\n            n('map') = @(varargin) core.map(varargin{:});\n\n            n('conj') = @(varargin) core.conj(varargin{:});\n            n('seq') = @(a) core.seq(a);\n\n            n('with-meta') = @(a,b) core.with_meta(a,b);\n            n('meta') = @(a) core.meta(a);\n            n('atom') = @(a) types.Atom(a);\n            n('atom?') = @(a) isa(a, 'types.Atom');\n            n('deref') = @(a) a.val;\n            n('reset!') = @(a,b) core.reset_BANG(a,b);\n            n('swap!') = @(varargin) core.swap_BANG(varargin{:});\n        end\n    end\nend\n\n"
  },
  {
    "path": "impls/matlab/printer.m",
    "content": "% this is just being used as a namespace\nclassdef printer\n    methods (Static = true)\n        function str = pr_str(obj, print_readably)\n            switch class(obj)\n            case 'types.Symbol'\n                str = obj.name;\n            case 'double'\n                str = num2str(obj);\n            case 'char'\n                if type_utils.keyword_Q(obj)\n                    str = sprintf(':%s', obj(2:end));\n                else\n                    if print_readably\n                        str = strrep(obj, '\\', '\\\\');\n                        str = strrep(str, '\"', '\\\"');\n                        str = strrep(str, char(10), '\\n');\n                        str = sprintf('\"%s\"', str);\n                    else\n                        str = obj;\n                    end\n                end\n            case 'types.List'\n                strs = cellfun(@(x) printer.pr_str(x, print_readably), ...\n                               obj.data, 'UniformOutput', false);\n                str = sprintf('(%s)', strjoin(strs, ' '));\n            case 'types.Vector'\n                strs = cellfun(@(x) printer.pr_str(x, print_readably), ...\n                               obj.data, 'UniformOutput', false);\n                str = sprintf('[%s]', strjoin(strs, ' '));\n            case 'types.HashMap'\n                strs = {};\n                ks = obj.keys();\n                for i=1:length(ks)\n                    k = ks{i};\n                    strs{end+1} = printer.pr_str(k, print_readably);\n                    strs{end+1} = printer.pr_str(obj.get(k), print_readably);\n                end\n                str = sprintf('{%s}', strjoin(strs, ' '));\n            case 'types.Nil'\n                str = 'nil';\n            case 'logical'\n                if eq(obj, true)\n                    str = 'true';\n                else\n                    str = 'false';\n                end\n            case 'types.Atom'\n                str = sprintf('(atom %s)', printer.pr_str(obj.val,true));\n            otherwise\n                str = '#<unknown>';\n            end\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/reader.m",
    "content": "% this is just being used as a namespace\nclassdef reader\n    methods (Static = true)\n        function tokens = tokenize(str)\n            re = '[\\s,]*(~@|[\\[\\]{}()''`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;[^\\n]*|[^\\s\\[\\]{}(''\"`,;)]*)';\n            % extract the capture group (to ignore spaces and commas)\n            tokens = cellfun(@(x) x(1), regexp(str, re, 'tokens'));\n            comments = cellfun(@(x) length(x) > 0 && x(1) == ';', tokens);\n            tokens = tokens(~comments);\n        end\n      \n        function atm = read_atom(rdr)\n            token = rdr.next();\n            %fprintf('in read_atom: %s\\n', token);\n            if not(isempty(regexp(token, '^-?[0-9]+$', 'match')))\n                atm = str2double(token);\n            elseif not(isempty(regexp(token, '^\"(?:\\\\.|[^\\\\\"])*\"$', 'match')))\n                atm = token(2:length(token)-1);\n                % If overlaps is enabled here then only the first '\\\\'\n                % is replaced. Probably an GNU Octave bug since the\n                % other repeated pairs are substituted correctly.\n                atm = strrep(atm, '\\\\', char(255), 'overlaps', false);\n                atm = strrep(atm, '\\\"', '\"');\n                atm = strrep(atm, '\\n', char(10));\n                atm = strrep(atm, char(255), '\\');\n            elseif strcmp(token(1), '\"')\n                error('expected ''\"'', got EOF');\n            elseif strcmp(token(1), ':')\n                s = token(2:end);\n                atm = type_utils.keyword(s);\n            elseif strcmp(token, 'nil')\n                atm = type_utils.nil;\n            elseif strcmp(token, 'true')\n                atm = true;\n            elseif strcmp(token, 'false')\n                atm = false;\n            else\n                atm = types.Symbol(token);\n            end\n        end\n\n        function seq = read_seq(rdr, start, last)\n            %fprintf('in read_seq\\n');\n            seq = {};\n            token = rdr.next();\n            if not(strcmp(token, start))\n                error(sprintf('expected ''%s'', got EOF', start));\n            end\n            token = rdr.peek();\n            while true\n                if eq(token, false)\n                    error(sprintf('expected ''%s'', got EOF', last));\n                end\n                if strcmp(token, last), break, end\n                seq{end+1} = reader.read_form(rdr);\n                token = rdr.peek();\n            end\n            rdr.next();\n        end\n\n        function lst = read_list(rdr)\n            seq = reader.read_seq(rdr, '(', ')');\n            lst = types.List(seq{:});\n        end\n\n        function vec = read_vector(rdr)\n            seq = reader.read_seq(rdr, '[', ']');\n            vec = types.Vector(seq{:});\n        end\n\n        function map = read_hash_map(rdr)\n            seq = reader.read_seq(rdr, '{', '}');\n            map = types.HashMap(seq{:});\n        end\n\n        function ast = read_form(rdr)\n            %fprintf('in read_form\\n');\n            token = rdr.peek();\n            switch token\n            case ''''\n                rdr.next();\n                ast = types.List(types.Symbol('quote'), ...\n                                 reader.read_form(rdr));\n            case '`'\n                rdr.next();\n                ast = types.List(types.Symbol('quasiquote'), ...\n                                 reader.read_form(rdr));\n            case '~'\n                rdr.next();\n                ast = types.List(types.Symbol('unquote'), ...\n                                 reader.read_form(rdr));\n            case '~@'\n                rdr.next();\n                ast = types.List(types.Symbol('splice-unquote'), ...\n                                 reader.read_form(rdr));\n            case '^'\n                rdr.next();\n                meta = reader.read_form(rdr);\n                ast = types.List(types.Symbol('with-meta'), ...\n                                 reader.read_form(rdr), meta);\n            case '@'\n                rdr.next();\n                ast = types.List(types.Symbol('deref'), ...\n                                 reader.read_form(rdr));\n\n            case ')'\n                error('unexpected '')''');\n            case '('\n                ast = reader.read_list(rdr);\n            case ']'\n                error('unexpected '']''');\n            case '['\n                ast = reader.read_vector(rdr);\n            case '}'\n                error('unexpected ''}''');\n            case '{'\n                ast = reader.read_hash_map(rdr);\n            otherwise\n                ast = reader.read_atom(rdr);\n            end\n        end\n\n        function ast = read_str(str)\n            %fprintf('in read_str\\n');\n            tokens = reader.tokenize(str);\n            %disp(tokens);\n            rdr = types.Reader(tokens);\n            ast = reader.read_form(rdr);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/run",
    "content": "#!/bin/sh\n\nargs=\nfor x; do\n    args=\"$args${args:+, }'$x'\"\ndone\n\ncase \"$matlab_MODE\" in\n    matlab)\n        options='-nodisplay -nosplash -nodesktop -nojvm -r'\n        ;;\n    octave)\n        options='-q --no-gui --no-history --eval'\n        ;;\n    *)\n        echo \"Bad matlab_MODE: $matlab_MODE\"\n        exit 1\n        ;;\nesac\n\nexec $matlab_MODE $options \"${STEP:-stepA_mal}($args);quit;\"\n"
  },
  {
    "path": "impls/matlab/step0_repl.m",
    "content": "function step0_repl(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = str;\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n    ret = ast;\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = ast;\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    while (true)\n        line = input('user> ', 's');\n        fprintf('%s\\n', rep(line, ''));\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step1_read_print.m",
    "content": "function step1_read_print(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n    ret = ast;\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, ''));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step2_eval.m",
    "content": "function step2_eval(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n\n  % fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env(ast.name);\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n       f = EVAL(ast.get(1), env);\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        ret = f(args.data{:});\n\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    if exist('OCTAVE_VERSION', 'builtin') ~= 0\n        repl_env = Dict();\n    else\n        repl_env = containers.Map();\n    end\n    repl_env('+') = @(a,b) a+b;\n    repl_env('-') = @(a,b) a-b;\n    repl_env('*') = @(a,b) a*b;\n    repl_env('/') = @(a,b) floor(a/b);\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step3_env.m",
    "content": "function step3_env(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        ret = EVAL(ast.get(3), let_env);\n    otherwise\n       f = EVAL(ast.get(1), env);\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        ret = f(args.data{:});\n    end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n    repl_env.set(types.Symbol('+'), @(a,b) a+b);\n    repl_env.set(types.Symbol('-'), @(a,b) a-b);\n    repl_env.set(types.Symbol('*'), @(a,b) a*b);\n    repl_env.set(types.Symbol('/'), @(a,b) floor(a/b));\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step4_if_fn_do.m",
    "content": "function step4_if_fn_do(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        ret = EVAL(ast.get(3), let_env);\n    case 'do'\n        for i=2:length(ast)\n            ret = EVAL(ast.get(i), env);\n        end\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ret = EVAL(ast.get(4), env);\n            else\n               ret = type_utils.nil;\n            end\n        else\n            ret = EVAL(ast.get(3), env);\n        end\n    case 'fn*'\n        ret = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                               types.List(varargin{:})));\n    otherwise\n       f = EVAL(ast.get(1), env);\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        ret = f(args.data{:});\n    end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n\n    % core.mal: defined using the langauge itself\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step5_tco.m",
    "content": "function step5_tco(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n  while true\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n        return;\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        env = let_env;\n        ast = ast.get(3); % TCO\n    case 'do'\n        for i=2:(length(ast) -1)\n            ret = EVAL(ast.get(i), env);\n        end\n        ast = ast.get(length(ast)); % TCO\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ast = ast.get(4); % TCO\n            else\n               ret = type_utils.nil;\n               return;\n            end\n        else\n            ast = ast.get(3); % TCO\n        end\n    case 'fn*'\n        fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                              types.List(varargin{:})));\n        ret = types.Function(fn, ast.get(3), env, ast.get(2));\n        return;\n    otherwise\n      f = EVAL(ast.get(1), env);\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        if isa(f, 'types.Function')\n            env = Env({f.env}, f.params, args);\n            ast = f.ast; % TCO\n        else\n            ret = f(args.data{:});\n            return\n        end\n    end\n  end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n\n    % core.mal: defined using the langauge itself\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step6_file.m",
    "content": "function step6_file(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = EVAL(ast, env)\n  while true\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n        return;\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        env = let_env;\n        ast = ast.get(3); % TCO\n    case 'do'\n        for i=2:(length(ast) -1)\n            ret = EVAL(ast.get(i), env);\n        end\n        ast = ast.get(length(ast)); % TCO\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ast = ast.get(4); % TCO\n            else\n               ret = type_utils.nil;\n               return;\n            end\n        else\n            ast = ast.get(3); % TCO\n        end\n    case 'fn*'\n        fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                              types.List(varargin{:})));\n        ret = types.Function(fn, ast.get(3), env, ast.get(2));\n        return;\n    otherwise\n      f = EVAL(ast.get(1), env);\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        if isa(f, 'types.Function')\n            env = Env({f.env}, f.params, args);\n            ast = f.ast; % TCO\n        else\n            ret = f(args.data{:});\n            return\n        end\n    end\n  end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n    repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env));\n    rest_args = args(2:end);\n    repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));\n\n    % core.mal: defined using the langauge itself\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n    rep('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"', repl_env);\n\n    if ~isempty(args)\n        rep(sprintf('(load-file \"%s\")', args{1}), repl_env);\n        quit;\n    end\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step7_quote.m",
    "content": "function step7_quote(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = starts_with(ast, sym)\n    ret = length(ast);\n    if ret\n        first = ast.get(1);\n        ret = isa(first,'types.Symbol') && strcmp(first.name, sym);\n    end\nend\n\nfunction ret = quasiquote_loop(ast)\n    ret = types.List();\n    for i=length(ast):-1:1\n        elt = ast.get(i)\n        if isa(elt, 'types.List') && starts_with(elt, 'splice-unquote')\n            ret = types.List(types.Symbol('concat'), elt.get(2), ret);\n        else\n            ret = types.List(types.Symbol('cons'), quasiquote(elt), ret);\n        end\n    end\nend\n\nfunction ret = quasiquote(ast)\n    switch class(ast)\n    case 'types.List'\n        if starts_with(ast, 'unquote')\n            ret = ast.get(2);\n        else\n            ret = quasiquote_loop(ast);\n        end\n    case 'types.Vector'\n        ret = types.List(types.Symbol('vec'), quasiquote_loop(ast));\n    case {'types.Symbol', 'types.HashMap'}\n        ret = types.List(types.Symbol('quote'), ast);\n    otherwise\n        ret = ast;\n    end\nend\n\nfunction ret = EVAL(ast, env)\n  while true\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n        return;\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        env = let_env;\n        ast = ast.get(3); % TCO\n    case 'quote'\n        ret = ast.get(2);\n        return;\n    case 'quasiquote'\n        ast = quasiquote(ast.get(2)); % TCO\n    case 'do'\n        for i=2:(length(ast) -1)\n            ret = EVAL(ast.get(i), env);\n        end\n        ast = ast.get(length(ast)); % TCO\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ast = ast.get(4); % TCO\n            else\n               ret = type_utils.nil;\n               return;\n            end\n        else\n            ast = ast.get(3); % TCO\n        end\n    case 'fn*'\n        fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                              types.List(varargin{:})));\n        ret = types.Function(fn, ast.get(3), env, ast.get(2));\n        return;\n    otherwise\n      f = EVAL(ast.get(1), env);\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        if isa(f, 'types.Function')\n            env = Env({f.env}, f.params, args);\n            ast = f.ast; % TCO\n        else\n            ret = f(args.data{:});\n            return\n        end\n    end\n  end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n    repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env));\n    rest_args = args(2:end);\n    repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));\n\n    % core.mal: defined using the langauge itself\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n    rep('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"', repl_env);\n\n    if ~isempty(args)\n        rep(sprintf('(load-file \"%s\")', args{1}), repl_env);\n        quit;\n    end\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step8_macros.m",
    "content": "function step8_macros(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = starts_with(ast, sym)\n    ret = length(ast);\n    if ret\n        first = ast.get(1);\n        ret = isa(first,'types.Symbol') && strcmp(first.name, sym);\n    end\nend\n\nfunction ret = quasiquote_loop(ast)\n    ret = types.List();\n    for i=length(ast):-1:1\n        elt = ast.get(i)\n        if isa(elt, 'types.List') && starts_with(elt, 'splice-unquote')\n            ret = types.List(types.Symbol('concat'), elt.get(2), ret);\n        else\n            ret = types.List(types.Symbol('cons'), quasiquote(elt), ret);\n        end\n    end\nend\n\nfunction ret = quasiquote(ast)\n    switch class(ast)\n    case 'types.List'\n        if starts_with(ast, 'unquote')\n            ret = ast.get(2);\n        else\n            ret = quasiquote_loop(ast);\n        end\n    case 'types.Vector'\n        ret = types.List(types.Symbol('vec'), quasiquote_loop(ast));\n    case {'types.Symbol', 'types.HashMap'}\n        ret = types.List(types.Symbol('quote'), ast);\n    otherwise\n        ret = ast;\n    end\nend\n\nfunction ret = EVAL(ast, env)\n  while true\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n        return;\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        env = let_env;\n        ast = ast.get(3); % TCO\n    case 'quote'\n        ret = ast.get(2);\n        return;\n    case 'quasiquote'\n        ast = quasiquote(ast.get(2)); % TCO\n    case 'defmacro!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env).clone());\n        ret.is_macro = true;\n        return;\n    case 'do'\n        for i=2:(length(ast) -1)\n            ret = EVAL(ast.get(i), env);\n        end\n        ast = ast.get(length(ast)); % TCO\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ast = ast.get(4); % TCO\n            else\n               ret = type_utils.nil;\n               return;\n            end\n        else\n            ast = ast.get(3); % TCO\n        end\n    case 'fn*'\n        fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                              types.List(varargin{:})));\n        ret = types.Function(fn, ast.get(3), env, ast.get(2));\n        return;\n    otherwise\n      f = EVAL(ast.get(1), env);\n      if isa(f,'types.Function') && f.is_macro\n          ast = f.fn(ast.slice(2).data{:}); % TCO\n      else\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        if isa(f, 'types.Function')\n            env = Env({f.env}, f.params, args);\n            ast = f.ast; % TCO\n        else\n            ret = f(args.data{:});\n            return\n        end\n      end\n    end\n  end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n    repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env));\n    rest_args = args(2:end);\n    repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));\n\n    % core.mal: defined using the langauge itself\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n    rep('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"', repl_env);\n    rep('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))', repl_env);\n\n    if ~isempty(args)\n        rep(sprintf('(load-file \"%s\")', args{1}), repl_env);\n        quit;\n    end\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            fprintf('Error: %s\\n', err.message);\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/step9_try.m",
    "content": "function step9_try(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = starts_with(ast, sym)\n    ret = length(ast);\n    if ret\n        first = ast.get(1);\n        ret = isa(first,'types.Symbol') && strcmp(first.name, sym);\n    end\nend\n\nfunction ret = quasiquote_loop(ast)\n    ret = types.List();\n    for i=length(ast):-1:1\n        elt = ast.get(i)\n        if isa(elt, 'types.List') && starts_with(elt, 'splice-unquote')\n            ret = types.List(types.Symbol('concat'), elt.get(2), ret);\n        else\n            ret = types.List(types.Symbol('cons'), quasiquote(elt), ret);\n        end\n    end\nend\n\nfunction ret = quasiquote(ast)\n    switch class(ast)\n    case 'types.List'\n        if starts_with(ast, 'unquote')\n            ret = ast.get(2);\n        else\n            ret = quasiquote_loop(ast);\n        end\n    case 'types.Vector'\n        ret = types.List(types.Symbol('vec'), quasiquote_loop(ast));\n    case {'types.Symbol', 'types.HashMap'}\n        ret = types.List(types.Symbol('quote'), ast);\n    otherwise\n        ret = ast;\n    end\nend\n\nfunction ret = EVAL(ast, env)\n  while true\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n        return;\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        env = let_env;\n        ast = ast.get(3); % TCO\n    case 'quote'\n        ret = ast.get(2);\n        return;\n    case 'quasiquote'\n        ast = quasiquote(ast.get(2)); % TCO\n    case 'defmacro!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env).clone());\n        ret.is_macro = true;\n        return;\n    case 'try*'\n        try\n            ret = EVAL(ast.get(2), env);\n            return;\n        catch e\n            if length(ast) > 2 && strcmp(ast.get(3).get(1).name, 'catch*')\n                if strcmp(e.identifier, 'MalException:object')\n                    if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                        global error_object;\n                        exc = error_object;\n                    else\n                        exc = e.obj;\n                    end\n                else\n                    exc = e.message;\n                end\n                catch_env = Env({env}, types.List(ast.get(3).get(2)), ...\n                                types.List(exc));\n                ret = EVAL(ast.get(3).get(3), catch_env);\n                return;\n            else\n                rethrow(e);\n            end\n        end\n    case 'do'\n        for i=2:(length(ast) -1)\n            ret = EVAL(ast.get(i), env);\n        end\n        ast = ast.get(length(ast)); % TCO\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ast = ast.get(4); % TCO\n            else\n               ret = type_utils.nil;\n               return;\n            end\n        else\n            ast = ast.get(3); % TCO\n        end\n    case 'fn*'\n        fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                              types.List(varargin{:})));\n        ret = types.Function(fn, ast.get(3), env, ast.get(2));\n        return;\n    otherwise\n      f = EVAL(ast.get(1), env);\n      if isa(f,'types.Function') && f.is_macro\n          ast = f.fn(ast.slice(2).data{:}); % TCO\n      else\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        if isa(f, 'types.Function')\n            env = Env({f.env}, f.params, args);\n            ast = f.ast; % TCO\n        else\n            ret = f(args.data{:});\n            return\n        end\n      end\n    end\n  end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n    repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env));\n    rest_args = args(2:end);\n    repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));\n\n    % core.mal: defined using the langauge itself\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n    rep('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"', repl_env);\n    rep('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))', repl_env);\n\n    if ~isempty(args)\n        rep(sprintf('(load-file \"%s\")', args{1}), repl_env);\n        quit;\n    end\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            if strcmp('MalException:object', err.identifier)\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    global error_object;\n                    fprintf('Error: %s\\n', printer.pr_str(error_object, true));\n                else\n                    fprintf('Error: %s\\n', printer.pr_str(err.obj, true));\n                end\n            else\n                fprintf('Error: %s\\n', err.message);\n            end\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/stepA_mal.m",
    "content": "function stepA_mal(varargin), main(varargin), end\n\n% read\nfunction ret = READ(str)\n    ret = reader.read_str(str);\nend\n\n% eval\nfunction ret = starts_with(ast, sym)\n    ret = length(ast);\n    if ret\n        first = ast.get(1);\n        ret = isa(first,'types.Symbol') && strcmp(first.name, sym);\n    end\nend\n\nfunction ret = quasiquote_loop(ast)\n    ret = types.List();\n    for i=length(ast):-1:1\n        elt = ast.get(i)\n        if isa(elt, 'types.List') && starts_with(elt, 'splice-unquote')\n            ret = types.List(types.Symbol('concat'), elt.get(2), ret);\n        else\n            ret = types.List(types.Symbol('cons'), quasiquote(elt), ret);\n        end\n    end\nend\n\nfunction ret = quasiquote(ast)\n    switch class(ast)\n    case 'types.List'\n        if starts_with(ast, 'unquote')\n            ret = ast.get(2);\n        else\n            ret = quasiquote_loop(ast);\n        end\n    case 'types.Vector'\n        ret = types.List(types.Symbol('vec'), quasiquote_loop(ast));\n    case {'types.Symbol', 'types.HashMap'}\n        ret = types.List(types.Symbol('quote'), ast);\n    otherwise\n        ret = ast;\n    end\nend\n\nfunction ret = EVAL(ast, env)\n  while true\n\n    dbgeval = env.get('DEBUG-EVAL');\n    if ~isequal(dbgeval, {}) ...\n       && ~strcmp(class(dbgeval), 'types.Nil') ...\n       && (~islogical(dbgeval) || dbgeval)\n      fprintf('EVAL: %s\\n', printer.pr_str(ast, true));\n    end\n\n    switch class(ast)\n    case 'types.Symbol'\n        ret = env.get(ast.name);\n        if isequal(ret, {})\n            msg = sprintf('''%s'' not found', ast.name);\n            if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                error('ENV:notfound', msg);\n            else\n                throw(MException('ENV:notfound', msg));\n            end\n        end\n        return;\n    case 'types.List'\n        %  Proceed after this switch.\n    case 'types.Vector'\n        ret = types.Vector();\n        for i=1:length(ast)\n            ret.append(EVAL(ast.get(i), env));\n        end\n        return;\n    case 'types.HashMap'\n        ret = types.HashMap();\n        ks = ast.keys();\n        for i=1:length(ks)\n            k = ks{i};\n            ret.set(k, EVAL(ast.get(k), env));\n        end\n        return;\n    otherwise\n        ret = ast;\n        return;\n    end\n\n    % apply\n    if length(ast) == 0\n        ret = ast;\n        return;\n    end\n\n    if isa(ast.get(1),'types.Symbol')\n        a1sym = ast.get(1).name;\n    else\n        a1sym = '_@$fn$@_';\n    end\n    switch (a1sym)\n    case 'def!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env));\n        return;\n    case 'let*'\n        let_env = Env({env});\n        for i=1:2:length(ast.get(2))\n            let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));\n        end\n        env = let_env;\n        ast = ast.get(3); % TCO\n    case 'quote'\n        ret = ast.get(2);\n        return;\n    case 'quasiquote'\n        ast = quasiquote(ast.get(2)); % TCO\n    case 'defmacro!'\n        ret = env.set(ast.get(2), EVAL(ast.get(3), env).clone());\n        ret.is_macro = true;\n        return;\n    case 'try*'\n        try\n            ret = EVAL(ast.get(2), env);\n            return;\n        catch e\n            if length(ast) > 2 && strcmp(ast.get(3).get(1).name, 'catch*')\n                if strcmp(e.identifier, 'MalException:object')\n                    if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                        global error_object;\n                        exc = error_object;\n                    else\n                        exc = e.obj;\n                    end\n                else\n                    exc = e.message;\n                end\n                catch_env = Env({env}, types.List(ast.get(3).get(2)), ...\n                                types.List(exc));\n                ret = EVAL(ast.get(3).get(3), catch_env);\n                return;\n            else\n                rethrow(e);\n            end\n        end\n    case 'do'\n        for i=2:(length(ast) -1)\n            ret = EVAL(ast.get(i), env);\n        end\n        ast = ast.get(length(ast)); % TCO\n    case 'if'\n        cond = EVAL(ast.get(2), env);\n        if strcmp(class(cond), 'types.Nil') || ...\n           (islogical(cond) && cond == false)\n           if length(ast) > 3\n               ast = ast.get(4); % TCO\n            else\n               ret = type_utils.nil;\n               return;\n            end\n        else\n            ast = ast.get(3); % TCO\n        end\n    case 'fn*'\n        fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...\n                                              types.List(varargin{:})));\n        ret = types.Function(fn, ast.get(3), env, ast.get(2));\n        return;\n    otherwise\n      f = EVAL(ast.get(1), env);\n      if isa(f,'types.Function') && f.is_macro\n          ast = f.fn(ast.slice(2).data{:}); % TCO\n      else\n        args = types.List();\n        for i=2:length(ast)\n            args.append(EVAL(ast.get(i), env));\n        end\n        if isa(f, 'types.Function')\n            env = Env({f.env}, f.params, args);\n            ast = f.ast; % TCO\n        else\n            ret = f(args.data{:});\n            return\n        end\n      end\n    end\n  end\nend\n\n% print\nfunction ret = PRINT(ast)\n    ret = printer.pr_str(ast, true);\nend\n\n% REPL\nfunction ret = rep(str, env)\n    ret = PRINT(EVAL(READ(str), env));\nend\n\nfunction main(args)\n    repl_env = Env();\n\n    % core.m: defined using matlab\n    ns = core.ns(); ks = ns.keys();\n    for i=1:length(ks)\n        k = ks{i};\n        repl_env.set(types.Symbol(k), ns(k));\n    end\n    repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env));\n    rest_args = args(2:end);\n    repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));\n\n    % core.mal: defined using the langauge itself\n    rep('(def! *host-language* \"matlab\")', repl_env);\n    rep('(def! not (fn* (a) (if a false true)))', repl_env);\n    rep('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"', repl_env);\n    rep('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))', repl_env);\n\n    if ~isempty(args)\n        rep(sprintf('(load-file \"%s\")', args{1}), repl_env);\n        quit;\n    end\n\n    %cleanObj = onCleanup(@() disp('*** here1 ***'));\n    rep('(println (str \"Mal [\" *host-language* \"]\"))', repl_env);\n    while (true)\n        try\n            line = input('user> ', 's');\n        catch err\n            return\n        end\n        if strcmp(strtrim(line),''), continue, end\n        try\n            fprintf('%s\\n', rep(line, repl_env));\n        catch err\n            if strcmp('MalException:object', err.identifier)\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    global error_object;\n                    fprintf('Error: %s\\n', printer.pr_str(error_object, true));\n                else\n                    fprintf('Error: %s\\n', printer.pr_str(err.obj, true));\n                end\n            else\n                fprintf('Error: %s\\n', err.message);\n            end\n            type_utils.print_stack(err);\n        end\n    end\nend\n"
  },
  {
    "path": "impls/matlab/type_utils.m",
    "content": "classdef type_utils\n    properties (Constant = true)\n        nil = types.Nil();\n    end\n\n    methods(Static)\n        function ret = equal(a,b)\n            ret = false;\n            ota = class(a); otb = class(b);\n            if ~(strcmp(ota,otb) || ...\n               (type_utils.sequential_Q(a) && type_utils.sequential_Q(b)))\n                return;\n            end\n            switch (ota)\n            case {'types.List', 'types.Vector'}\n                if ~(length(a) == length(b))\n                    return;\n                end\n                for i=1:length(a)\n                    if ~(type_utils.equal(a.get(i), b.get(i)))\n                        return;\n                    end\n                end\n                ret = true;\n            case 'types.HashMap'\n                if ~(length(a) == length(b))\n                    return;\n                end\n                ks1 = a.keys();\n                for i=1:length(ks1)\n                    k = ks1{i};\n                    if ~(b.data.isKey(k))\n                        return;\n                    end\n                    if ~(type_utils.equal(a.data(k), b.data(k)))\n                        return;\n                    end\n                end\n                ret = true;\n            case 'char'\n                ret = strcmp(a,b);\n            otherwise\n                ret = a == b;\n            end\n        end\n\n        function ret = sequential_Q(obj)\n            ret = strcmp(class(obj), 'types.List') || ...\n                  strcmp(class(obj), 'types.Vector');\n        end\n\n        function ret = list_Q(obj)\n            ret = strcmp(class(obj), 'types.List');\n        end\n        function ret = vector_Q(obj)\n            ret = strcmp(class(obj), 'types.Vector');\n        end\n        function ret = hash_map_Q(obj)\n            ret = strcmp(class(obj), 'types.HashMap');\n        end\n\n        function ret = keyword(str)\n            if type_utils.keyword_Q(str)\n                ret = str;\n            else\n                ret = sprintf('%c%s', 255, str);\n            end\n        end\n        function ret = keyword_Q(obj)\n            ret = length(obj) > 1 && strcmp(obj(1), sprintf('%c', 255));\n        end\n\n        function ret = string_Q(obj)\n            ret = strcmp(class(obj), 'char') && ~type_utils.keyword_Q(obj);\n        end\n\n        function ret = number_Q(obj)\n            ret = strcmp(class(obj), 'double');\n        end\n\n        function ret = fn_Q(obj)\n            ret = isa(obj,'function_handle') || ...\n                  (isa(obj,'types.Function') && ~obj.is_macro);\n        end\n\n        function ret = macro_Q(obj)\n            ret = isa(obj,'types.Function') && obj.is_macro;\n        end\n\n        function print_stack(err)\n            for i=1:numel(err.stack)\n                stack = err.stack(i);\n                if exist('OCTAVE_VERSION', 'builtin') ~= 0\n                    fprintf('    %s at line %d column %d (%s)\\n', ...\n                            stack.name, stack.line, stack.column, stack.file);\n                else\n                    fprintf('    %s at line %d (%s)\\n', ...\n                            stack.name, stack.line, stack.file);\n                end\n            end\n        end\n    end\nend\n\n"
  },
  {
    "path": "impls/miniMAL/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\nLABEL org.opencontainers.image.source=https://github.com/kanaka/mal\nLABEL org.opencontainers.image.description=\"mal test container: miniMAL\"\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# For building node modules\nRUN apt-get -y install g++ libreadline-dev nodejs npm\n\nENV NPM_CONFIG_CACHE /mal/.npm\n\n# install miniMAL itself\nRUN npm install -g minimal-lisp@1.2.2\n"
  },
  {
    "path": "impls/miniMAL/Makefile",
    "content": "\nSOURCES_BASE = node_readline.js miniMAL-core.json \\\n\t       types.json reader.json printer.json\nSOURCES_LISP = env.json core.json stepA_mal.json\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\nFFI_STEPS = step4_if_fn_do step5_tco step6_file \\\n\t    step7_quote step8_macros step9_try stepA_mal\n\nall: node_modules\n\nnode_modules:\n\tnpm install\n\n$(foreach S,$(FFI_STEPS),$(S).json): node_modules\n\ndist: mal.json mal\n\nmal.json: $(filter-out %.js,$(SOURCES))\n\techo '[\"do\",' >> $@\n\t$(foreach f,$+,\\\n\t  cat $(f) | egrep -v '^ *[[]\"load-file\"' >> $@; \\\n\t  echo \",\" >> $@;)\n\techo 'null]' >> $@\n\nmal: mal.json\n\techo '#!/usr/bin/env miniMAL' > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n"
  },
  {
    "path": "impls/miniMAL/core.json",
    "content": "[\"do\",\n\n[\"def\", \"_path\", [\"require\", [\"`\", \"path\"]]],\n\n[\"def\", \"_node_readline\", [\"require\", [\".\", \"_path\", [\"`\", \"resolve\"],\n                                            [\"`\", \".\"],\n                                            [\"`\", \"node_readline.js\"]]]],\n\n[\"def\", \"_string?\", [\"fn\", [\"s\"],\n  [\"and\", [\"string?\", \"s\"],\n          [\"not\", [\"=\", [\"`\", \"\\u029e\"], [\"get\", \"s\", 0]]]]]],\n\n[\"def\", \"_function?\", [\"fn\", [\"a\"],\n  [\"isa\", \"a\", \"Function\"]]],\n\n[\"def\", \"_number?\", [\"fn\", [\"a\"],\n  [\"=\", [\"`\", \"[object Number]\"], [\"classOf\", \"a\"]]]],\n\n[\"def\", \"div\", [\"fn\", [\"a\", \"b\"], [\"parseInt\", [\"/\", \"a\", \"b\"]]]],\n\n[\"def\", \"time-ms\", [\"fn\", [],\n  [\".\", [\"new\", \"Date\"], [\"`\", \"getTime\"]]]],\n\n\n[\"def\", \"assoc\", [\"fn\", [\"src-hm\", \"&\", \"kvs\"],\n  [\"let\", [\"hm\", [\"clone\", \"src-hm\"]],\n    [\"assocs!\", \"hm\", \"kvs\"]]]],\n\n[\"def\", \"dissoc\", [\"fn\", [\"src-hm\", \"&\", \"ks\"],\n  [\"let\", [\"hm\", [\"clone\", \"src-hm\"]],\n    [\"do\",\n      [\"map\", [\"fn\", [\"k\"], [\"del\", \"hm\", \"k\"]], \"ks\"],\n      \"hm\"]]]],\n\n[\"def\", \"_get\", [\"fn\", [\"obj\", \"key\"],\n  [\"if\", [\"null?\", \"obj\"],\n    null,\n    [\"if\", [\"contains?\", \"obj\", \"key\"],\n      [\"get\", \"obj\", \"key\"],\n      null]]]],\n\n[\"def\", \"_count\", [\"fn\", [\"a\"],\n  [\"if\", [\"=\", null, \"a\"],\n    0,\n    [\"count\", \"a\"]]]],\n\n[\"def\", \"_nth\", [\"fn\", [\"seq\", \"idx\"],\n  [\"if\", [\">=\", \"idx\", [\"count\", \"seq\"]],\n    [\"throw\", \"nth: index out of range\"],\n    [\"nth\", \"seq\", \"idx\"]]]],\n\n[\"def\", \"_first\", [\"fn\", [\"seq\"],\n  [\"if\", [\"empty?\", \"seq\"],\n    null,\n    [\"first\", \"seq\"]]]],\n\n[\"def\", \"_rest\", [\"fn\", [\"seq\"],\n  [\"if\", [\"empty?\", \"seq\"],\n    [\"`\", []],\n    [\"rest\", \"seq\"]]]],\n\n[\"def\", \"_apply\", [\"fn\", [\"f\", \"&\", \"args\"],\n  [\"let\", [\"fn\", [\"if\", [\"malfunc?\", \"f\"], [\"get\", \"f\", [\"`\", \"fn\"]], \"f\"],\n           \"fargs\", [\"concat\", [\"slice\", \"args\", 0, [\"-\", [\"count\", \"args\"], 1]],\n                               [\"nth\", \"args\", [\"-\", [\"count\", \"args\"], 1]]]],\n    [\"apply\", \"fn\", \"fargs\"]]]],\n\n[\"def\", \"_map\", [\"fn\", [\"f\", \"seq\"],\n  [\"let\", [\"fn\", [\"if\", [\"malfunc?\", \"f\"], [\"get\", \"f\", [\"`\", \"fn\"]], \"f\"]],\n    [\"map\", \"fn\", \"seq\"]]]],\n\n[\"def\", \"_conj\", [\"fn\", [\"seq\", \"&\", \"a\"],\n  [\"if\", [\"list?\", \"seq\"],\n    [\".\", [\".\", \"a\", [\"`\", \"reverse\"]], [\"`\", \"concat\"], \"seq\"],\n    [\"vectorl\", [\".\", \"seq\", [\"`\", \"concat\"], \"a\"]]]]],\n\n[\"def\", \"_seq\", [\"fn\", [\"obj\"],\n  [\"if\", [\"list?\", \"obj\"],\n    [\"if\", [\">\", [\"count\", \"obj\"], 0], \"obj\", null],\n  [\"if\", [\"vector?\", \"obj\"],\n    [\"if\", [\">\", [\"count\", \"obj\"], 0], [\"slice\", \"obj\", 0], null],\n  [\"if\", [\"string?\", \"obj\"],\n    [\"if\", [\">\", [\"count\", \"obj\"], 0],\n      [\".\", \"obj\", [\"`\", \"split\"], [\"`\", \"\"]],\n      null],\n  [\"if\", [\"null?\", \"obj\"],\n    null,\n    [\"throw\", \"seq: called on non-sequence\"]\n  ]]]]]],\n\n[\"def\", \"with_meta\", [\"fn\", [\"obj\", \"m\"],\n  [\"let\", [\"new-obj\", [\"clone\", \"obj\"]],\n    [\"do\",\n      [\"set\", \"new-obj\", [\"`\", \"__meta__\"], \"m\"],\n      \"new-obj\"]]]],\n\n[\"def\", \"meta\", [\"fn\", [\"obj\"],\n  [\"if\", [\"or\", [\"sequential?\", \"obj\"],\n                [\"map?\", \"obj\"],\n                [\"malfunc?\", \"obj\"]],\n    [\"if\", [\"contains?\", \"obj\", [\"`\", \"__meta__\"]],\n      [\"get\", \"obj\", [\"`\", \"__meta__\"]],\n      null],\n    null]]],\n\n[\"def\", \"reset!\", [\"fn\", [\"atm\", \"val\"],\n  [\"do\", [\"set\", \"atm\", [\"`\", \"val\"], \"val\"], \"val\"]]],\n\n[\"def\", \"swap!\", [\"fn\", [\"atm\", \"f\", \"&\", \"args\"],\n  [\"let\", [\"fn\", [\"if\", [\"malfunc?\", \"f\"], [\"get\", \"f\", [\"`\", \"fn\"]], \"f\"],\n           \"fargs\", [\"cons\", [\"get\", \"atm\", [\"`\", \"val\"]], \"args\"],\n           \"val\", [\"apply\", \"fn\", \"fargs\"]],\n  [\"do\",\n    [\"set\", \"atm\", [\"`\", \"val\"], \"val\"],\n    \"val\"]]]],\n\n[\"def\", \"core-ns\",\n  [\"hash-map\",\n   [\"`\", \"=\"], \"equal?\",\n   [\"`\", \"throw\"], \"throw\",\n\n   [\"`\", \"nil?\"], \"null?\",\n   [\"`\", \"true?\"], \"true?\",\n   [\"`\", \"false?\"], \"false?\",\n   [\"`\", \"string?\"], \"_string?\",\n   [\"`\", \"symbol\"], \"symbol\",\n   [\"`\", \"symbol?\"], \"symbol?\",\n   [\"`\", \"keyword\"], \"keyword\",\n   [\"`\", \"keyword?\"], \"keyword?\",\n   [\"`\", \"number?\"], \"_number?\",\n   [\"`\", \"fn?\"], [\"fn\", [\"a\"],\n                   [\"or\", [\"_function?\", \"a\"],\n                          [\"and\", [\"malfunc?\", \"a\"],\n                                  [\"not\", [\"get\", \"a\", [\"`\", \"macro?\"]]]]]],\n   [\"`\", \"macro?\"], [\"fn\", [\"a\"],\n                      [\"and\", [\"malfunc?\", \"a\"],\n                              [\"get\", \"a\", [\"`\", \"macro?\"]]]],\n\n   [\"`\", \"pr-str\"],  [\"fn\", [\"&\", \"a\"], [\"pr-list\", \"a\", true, [\"`\", \" \"]]],\n   [\"`\", \"str\"],     [\"fn\", [\"&\", \"a\"], [\"pr-list\", \"a\", false, [\"`\", \"\"]]],\n   [\"`\", \"prn\"],     [\"fn\", [\"&\", \"a\"],\n                       [\"do\",\n                         [\"println\", [\"pr-list\", \"a\", true, [\"`\", \" \"]]],\n                         null]],\n   [\"`\", \"println\"], [\"fn\", [\"&\", \"a\"],\n                       [\"do\",\n                         [\"println\", [\"pr-list\", \"a\", false, [\"`\", \" \"]]],\n                         null]],\n   [\"`\", \"read-string\"], \"read-str\",\n   [\"`\", \"readline\"], [\"fn\", [\"p\"],\n                        [\".\", \"_node_readline\", [\"`\", \"readline\"], \"p\"]],\n   [\"`\", \"slurp\"], \"slurp\",\n\n   [\"`\", \"<\"],  \"<\",\n   [\"`\", \"<=\"], \"<=\",\n   [\"`\", \">\"],  \">\",\n   [\"`\", \">=\"], \">=\",\n   [\"`\", \"+\"],  \"+\",\n   [\"`\", \"-\"],  \"-\",\n   [\"`\", \"*\"],  \"*\",\n   [\"`\", \"/\"],  \"div\",\n   [\"`\", \"time-ms\"], \"time-ms\",\n\n   [\"`\", \"list\"], \"list\",\n   [\"`\", \"list?\"], \"list?\",\n   [\"`\", \"vector\"], \"vector\",\n   [\"`\", \"vector?\"], \"vector?\",\n   [\"`\", \"hash-map\"], \"hash-map\",\n   [\"`\", \"assoc\"], \"assoc\",\n   [\"`\", \"dissoc\"], \"dissoc\",\n   [\"`\", \"map?\"], \"map?\",\n   [\"`\", \"get\"], \"_get\",\n   [\"`\", \"contains?\"], \"contains?\",\n   [\"`\", \"keys\"], \"keys\",\n   [\"`\", \"vals\"], \"vals\",\n\n   [\"`\", \"sequential?\"], \"sequential?\",\n   [\"`\", \"cons\"], \"cons\",\n   [\"`\", \"concat\"], \"concat\",\n   [\"`\", \"vec\"], \"vectorl\",\n   [\"`\", \"nth\"], \"_nth\",\n   [\"`\", \"first\"], \"_first\",\n   [\"`\", \"rest\"], \"_rest\",\n   [\"`\", \"empty?\"], \"empty?\",\n   [\"`\", \"count\"], \"_count\",\n   [\"`\", \"apply\"], \"_apply\",\n   [\"`\", \"map\"], \"_map\",\n\n   [\"`\", \"conj\"], \"_conj\",\n   [\"`\", \"seq\"], \"_seq\",\n\n   [\"`\", \"with-meta\"], \"with_meta\",\n   [\"`\", \"meta\"], \"meta\",\n   [\"`\", \"atom\"], \"atom\",\n   [\"`\", \"atom?\"], \"atom?\",\n   [\"`\", \"deref\"], [\"fn\", [\"a\"], [\"get\", \"a\", [\"`\", \"val\"]]],\n   [\"`\", \"reset!\"], \"reset!\",\n   [\"`\", \"swap!\"], \"swap!\"]],\n\nnull]\n"
  },
  {
    "path": "impls/miniMAL/env.json",
    "content": "[\"do\",\n\n[\"def\", \"env-bind\", [\"fn\", [\"env\", \"b\", \"e\"],\n  [\"if\", [\"empty?\", \"b\"],\n    \"env\",\n    [\"if\", [\"=\", [\"`\", \"&\"],\n                 [\"get\", [\"first\", \"b\"], [\"`\", \"val\"]]],\n      [\"assoc!\", \"env\", [\"get\", [\"nth\", \"b\", 1], [\"`\", \"val\"]], \"e\"],\n      [\"env-bind\", [\"assoc!\", \"env\", [\"get\", [\"first\", \"b\"], [\"`\", \"val\"]],\n                                     [\"first\", \"e\"]],\n                   [\"rest\", \"b\"],\n                   [\"rest\", \"e\"]]]]]],\n\n[\"def\", \"env-new\", [\"fn\", [\"&\", \"args\"],\n  [\"let\", [\"env\", [\"hash-map\", [\"`\", \"__outer__\"], [\"first\", \"args\"]]],\n    [\"if\", [\"<=\", [\"count\", \"args\"], 1],\n      \"env\",\n      [\"env-bind\", \"env\", [\"get\", \"args\", 1], [\"get\", \"args\", 2]]]]]],\n\n[\"def\", \"env-find\", [\"fn\", [\"env\", \"key\"],\n  [\"let\", [\"k\", [\"get\", \"key\", [\"`\", \"val\"]]],\n    [\"if\", [\"contains?\", \"env\", \"k\"],\n      \"env\",\n      [\"if\", [\"get\", \"env\", [\"`\", \"__outer__\"]],\n        [\"env-find\", [\"get\", \"env\", [\"`\", \"__outer__\"]], \"key\"],\n        null]]]]],\n\n[\"def\", \"env-get\", [\"fn\", [\"env\", \"key\"],\n  [\"let\", [\"k\", [\"get\", \"key\", [\"`\", \"val\"]],\n           \"e\", [\"env-find\", \"env\", \"key\"]],\n    [\"if\", \"e\",\n      [\"get\", \"e\", \"k\"],\n      [\"throw\", [\"str\", [\"`\", \"'\"], \"k\", [\"`\", \"' not found\"]]]]]]],\n\n[\"def\", \"env-set\", [\"fn\", [\"env\", \"key\", \"val\"],\n  [\"let\", [\"k\", [\"get\", \"key\", [\"`\", \"val\"]]],\n    [\"do\",\n      [\"assoc!\", \"env\", \"k\", \"val\"],\n      \"val\"]]]],\n\nnull\n]\n"
  },
  {
    "path": "impls/miniMAL/miniMAL-core.json",
    "content": "[\"do\",\n\n[\"def\", \"repl\", [\"fn\",[\"prompt\", \"rep\"],\n  [\"let\", [\"readline\", [\"require\", [\"`\", \"readline\"]],\n           \"opts\", [\"new\", \"Object\"],\n           \"_\", [\"set\", \"opts\", [\"`\", \"input\"], [\".-\", \"process\", [\"`\", \"stdin\"]]],\n           \"_\", [\"set\", \"opts\", [\"`\", \"output\"], [\".-\", \"process\", [\"`\", \"stdout\"]]],\n           \"_\", [\"set\", \"opts\", [\"`\", \"terminal\"], false],\n           \"rl\", [\".\", \"readline\", [\"`\", \"createInterface\"], \"opts\"],\n           \"evl\", [\"fn\", [\"line\"],\n                    [\"do\",\n                      [\"println\", [\"rep\", \"line\"]],\n                      [\".\", \"rl\", [\"`\", \"prompt\"]]]]],\n    [\"do\",\n      [\".\", \"rl\", [\"`\", \"setPrompt\"], \"prompt\"],\n      [\".\", \"rl\", [\"`\", \"prompt\"]],\n      [\".\", \"rl\", [\"`\", \"on\"], [\"`\", \"line\"], \"evl\"]]]]],\n\nnull\n]\n\n"
  },
  {
    "path": "impls/miniMAL/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline.so.8\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit.so.2\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nvar koffi = require('koffi'),\n    fs = require('fs');\n\nvar rllib = null;\ntry {\n    rllib = koffi.load(RL_LIB);\n} catch (e) {\n    console.error('ERROR loading RL_LIB:', RL_LIB, e);\n    throw e;\n}\nvar readlineFunc = rllib.func('char *readline(char *)');\nvar addHistoryFunc = rllib.func('int add_history(char *)');\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = typeof prompt !== 'undefined' ? prompt : \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { addHistoryFunc(lines[i]); }\n        }\n    }\n\n    var line = readlineFunc(prompt);\n    if (line) {\n        addHistoryFunc(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\nvar readline = exports;\n"
  },
  {
    "path": "impls/miniMAL/package.json",
    "content": "{\n    \"name\": \"mal-miniMAL\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in miniMAL\",\n    \"dependencies\": {\n        \"koffi\": \"^2.12.1\",\n        \"minimal-lisp\": \"1.2.2\"\n    }\n}\n"
  },
  {
    "path": "impls/miniMAL/printer.json",
    "content": "[\"do\",\n\n[\"def\", \"pr-str\", [\"fn\", [\"exp\", \"print_readably\"],\n  [\"if\", [\"list?\", \"exp\"],\n    [\"str\",\n      [\"`\", \"(\"],\n      [\".\", [\"map\", [\"fn\", [\"x\"], [\"pr-str\", \"x\", \"print_readably\"]], \"exp\"],\n            [\"`\", \"join\"], [\"`\", \" \"]],\n      [\"`\", \")\"]],\n  [\"if\", [\"vector?\", \"exp\"],\n    [\"str\",\n      [\"`\", \"[\"],\n      [\".\", [\"map\", [\"fn\", [\"x\"], [\"pr-str\", \"x\", \"print_readably\"]], \"exp\"],\n            [\"`\", \"join\"], [\"`\", \" \"]],\n      [\"`\", \"]\"]],\n  [\"if\", [\"map?\", \"exp\"],\n    [\"str\",\n      [\"`\", \"{\"],\n      [\".\", [\"map\", [\"fn\", [\"k\"],\n                      [\"str\", [\"pr-str\", \"k\", \"print_readably\"],\n                              [\"`\", \" \"],\n                              [\"pr-str\", [\"get\", \"exp\", \"k\"], \"print_readably\"]]],\n                    [\"keys\", \"exp\"]],\n            [\"`\", \"join\"], [\"`\", \" \"]],\n      [\"`\", \"}\"]],\n  [\"if\", [\"=\", [\"`\", \"[object String]\"], [\"classOf\", \"exp\"]],\n    [\"if\", [\"=\", [\"`\", \"\\u029e\"], [\"get\", \"exp\", 0]],\n      [\"str\", [\"`\", \":\"], [\"slice\", \"exp\", 1]],\n      [\"if\", \"print_readably\",\n        [\"str\", [\"`\", \"\\\"\"],\n                [\".\",\n                  [\".\",\n                    [\".\", \"exp\",\n                          [\"`\", \"replace\"], [\"RegExp\", [\"`\", \"\\\\\\\\\"], [\"`\", \"g\"]], [\"`\", \"\\\\\\\\\"]],\n                    [\"`\", \"replace\"], [\"RegExp\", [\"`\", \"\\\"\"], [\"`\", \"g\"]], [\"`\", \"\\\\\\\"\"]],\n                  [\"`\", \"replace\"], [\"RegExp\", [\"`\", \"\\n\"], [\"`\", \"g\"]], [\"`\", \"\\\\n\"]],\n                [\"`\", \"\\\"\"]],\n        \"exp\"]],\n  [\"if\", [\"=\", [\"`\", \"[object Number]\"], [\"classOf\", \"exp\"]],\n    \"exp\",\n  [\"if\", [\"=\", null, \"exp\"],\n    [\"`\", \"nil\"],\n  [\"if\", [\"=\", true, \"exp\"],\n    [\"`\", \"true\"],\n  [\"if\", [\"=\", false, \"exp\"],\n    [\"`\", \"false\"],\n  [\"if\", [\"symbol?\", \"exp\"],\n    [\"get\", \"exp\", [\"`\", \"val\"]],\n  [\"if\", [\"malfunc?\", \"exp\"],\n    [\"str\", [\"`\", \"(fn* \"],\n            [\"pr-str\", [\"get\", \"exp\", [\"`\", \"params\"]]],\n            [\"`\", \" \"],\n            [\"pr-str\", [\"get\", \"exp\", [\"`\", \"ast\"]]],\n            [\"`\", \")\"]],\n  [\"if\", [\"=\", [\"`\", \"[object Function]\"], [\"classOf\", \"exp\"]],\n    [\"str\", [\"`\", \"#<native function>\"]],\n  [\"if\", [\"atom?\", \"exp\"],\n    [\"str\", [\"`\", \"(atom \"], [\"get\", \"exp\", [\"`\", \"val\"]], [\"`\", \")\"]],\n  [\"str\", [\"`\", \"#<unknown: \"], \"exp\", [\"`\", \">\"]]]]]]]]]]]]]]]],\n\n[\"def\", \"pr-list\", [\"fn\", [\"lst\", \"print_readably\", \"sep\"],\n  [\".\", [\"map\", [\"fn\", [\"s\"], [\"pr-str\", \"s\", \"print_readably\"]], \"lst\"],\n        [\"`\", \"join\"], \"sep\"]]],\n\nnull\n]\n"
  },
  {
    "path": "impls/miniMAL/reader.json",
    "content": "[\"do\",\n\n[\"def\", \"rdr-new\", [\"fn\", [\"tokens\"],\n  [\"hash-map\", [\"`\", \"tokens\"], \"tokens\",\n               [\"`\", \"position\"], 0]]],\n\n[\"def\", \"rdr-next\", [\"fn\", [\"rdr\"],\n  [\"let\", [\"pos\", [\"get\", \"rdr\", [\"`\", \"position\"]],\n           \"val\", [\"get\", [\"get\", \"rdr\", [\"`\", \"tokens\"]], \"pos\"]],\n    [\"do\",\n      [\"assoc!\", \"rdr\", [\"`\", \"position\"], [\"+\", 1, \"pos\"]],\n      \"val\"]]]],\n\n[\"def\", \"rdr-peek\", [\"fn\", [\"rdr\"],\n  [\"let\", [\"pos\", [\"get\", \"rdr\", [\"`\", \"position\"]]],\n    [\"get\", [\"get\", \"rdr\", [\"`\", \"tokens\"]], \"pos\"]]]],\n\n\n[\"def\", \"re-matches\", [\"fn\", [\"re\", \"strn\", \"acc\"],\n  [\"let\", [\"match\", [\".\", \"re\", [\"`\", \"exec\"], \"strn\"],\n           \"g1\", [\"get\", \"match\", 1]],\n    [\"if\", [\"=\", \"g1\", [\"`\", \"\"]],\n      \"acc\",\n      [\"re-matches\", \"re\", \"strn\", [\"concat\", \"acc\", \"g1\"]]]]]],\n\n[\"def\", \"tokenize\", [\"fn\", [\"strn\"],\n  [\"let\", [\"re-str\", [\"`\", \"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\"],\n           \"re\", [\"RegExp\", \"re-str\", [\"`\", \"g\"]]],\n    [\".\",\n      [\"re-matches\", \"re\", \"strn\", [\"`\", []]],\n      [\"`\", \"filter\"],\n      [\"fn\", [\"x\"], [\"not\", [\"=\", [\"get\", \"x\", 0],\n                                  [\"`\", \";\"]]]]]]]],\n\n[\"def\", \"read-atom\", [\"fn\", [\"rdr\"],\n  [\"let\", [\"token\", [\"rdr-next\", \"rdr\"]],\n    [\"if\", [\".\", \"token\", [\"`\", \"match\"], [\"RegExp\", [\"`\", \"^-?[0-9]+$\"]]],\n      [\"parseInt\", \"token\", 10],\n    [\"if\", [\".\", \"token\", [\"`\", \"match\"], [\"RegExp\", [\"`\", \"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\"]]],\n      [\".\",\n        [\"slice\", \"token\", 1, [\"-\", [\"count\", \"token\"], 1]],\n        [\"`\", \"replace\"], [\"RegExp\", [\"`\", \"\\\\\\\\(.)\"], [\"`\", \"g\"]],\n                          [\"fn\", [\"_\", \"c\"],\n                            [\"if\", [\"=\", \"c\", [\"`\", \"n\"]],\n                              [\"`\", \"\\n\"],\n                              \"c\"]]],\n    [\"if\", [\"=\", [\"`\", \"\\\"\"], [\"get\", \"token\", 0]],\n      [\"throw\", [\"`\", \"expected '\\\"', got EOF\"]],\n    [\"if\", [\"=\", [\"`\", \":\"], [\"get\", \"token\", 0]],\n      [\"keyword\", [\"slice\", \"token\", 1]],\n    [\"if\", [\"=\", [\"`\", \"nil\"], \"token\"],\n      null,\n    [\"if\", [\"=\", [\"`\", \"true\"], \"token\"],\n      true,\n    [\"if\", [\"=\", [\"`\", \"false\"], \"token\"],\n      false,\n    [\"symbol\", \"token\"]]]]]]]]]]],\n\n[\"def\", \"read-list-entries\", [\"fn\", [\"rdr\", \"start\", \"end\"],\n  [\"let\", [\"tok\", [\"rdr-peek\", \"rdr\"]],\n    [\"if\", \"tok\",\n      [\"if\", [\"=\", \"end\", \"tok\"],\n        [\"`\", []],\n        [\"cons\", [\"read-form\", \"rdr\"],\n                 [\"read-list-entries\", \"rdr\", \"start\", \"end\"]]],\n      [\"throw\", [\"str\", [\"`\", \"expected \"], \"end\", [\"`\", \", got EOF\"]]]]]]],\n\n[\"def\", \"read-list\", [\"fn\", [\"rdr\", \"start\", \"end\"],\n  [\"let\", [\"token\", [\"rdr-next\", \"rdr\"]],\n    [\"if\", [\"=\", \"start\", \"token\"],\n      [\"let\", [\"lst\", [\"read-list-entries\", \"rdr\", \"start\", \"end\"]],\n        [\"do\",\n          [\"rdr-next\", \"rdr\"],\n          \"lst\"]],\n      [\"throw\", [\"str\", [\"`\", \"expected \"], \"start\"]]]]]],\n\n[\"def\", \"read-form\", [\"fn\", [\"rdr\"],\n  [\"let\", [\"token\", [\"rdr-peek\", \"rdr\"]],\n    [\"if\", [\"=\", [\"`\", \"'\"], \"token\"],\n      [\"do\",\n        [\"rdr-next\", \"rdr\"],\n        [\"list\", [\"symbol\", [\"`\", \"quote\"]], [\"read-form\", \"rdr\"]]],\n    [\"if\", [\"=\", [\"`\", \"`\"], \"token\"],\n      [\"do\",\n        [\"rdr-next\", \"rdr\"],\n        [\"list\", [\"symbol\", [\"`\", \"quasiquote\"]], [\"read-form\", \"rdr\"]]],\n    [\"if\", [\"=\", [\"`\", \"~\"], \"token\"],\n      [\"do\",\n        [\"rdr-next\", \"rdr\"],\n        [\"list\", [\"symbol\", [\"`\", \"unquote\"]], [\"read-form\", \"rdr\"]]],\n    [\"if\", [\"=\", [\"`\", \"~@\"], \"token\"],\n      [\"do\",\n        [\"rdr-next\", \"rdr\"],\n        [\"list\", [\"symbol\", [\"`\", \"splice-unquote\"]], [\"read-form\", \"rdr\"]]],\n    [\"if\", [\"=\", [\"`\", \"^\"], \"token\"],\n      [\"do\",\n        [\"rdr-next\", \"rdr\"],\n        [\"let\", [\"meta\", [\"read-form\", \"rdr\"]],\n          [\"list\", [\"symbol\", [\"`\", \"with-meta\"]], [\"read-form\", \"rdr\"], \"meta\"]]],\n    [\"if\", [\"=\", [\"`\", \"@\"], \"token\"],\n      [\"do\",\n        [\"rdr-next\", \"rdr\"],\n        [\"list\", [\"symbol\", [\"`\", \"deref\"]], [\"read-form\", \"rdr\"]]],\n\n    [\"if\", [\"=\", [\"`\", \")\"], \"token\"],\n      [\"throw\", [\"`\", \"unexpected ')'\"]],\n    [\"if\", [\"=\", [\"`\", \"(\"], \"token\"],\n      [\"read-list\", \"rdr\", [\"`\", \"(\"], [\"`\", \")\"]],\n\n    [\"if\", [\"=\", [\"`\", \"]\"], \"token\"],\n      [\"throw\", [\"`\", \"unexpected ']'\"]],\n    [\"if\", [\"=\", [\"`\", \"[\"], \"token\"],\n      [\"vectorl\", [\"read-list\", \"rdr\", [\"`\", \"[\"], [\"`\", \"]\"]]],\n\n    [\"if\", [\"=\", [\"`\", \"}\"], \"token\"],\n      [\"throw\", [\"`\", \"unexpected '}'\"]],\n    [\"if\", [\"=\", [\"`\", \"{\"], \"token\"],\n      [\"apply\", \"hash-map\", [\"read-list\", \"rdr\", [\"`\", \"{\"], [\"`\", \"}\"]]],\n\n    [\"read-atom\", \"rdr\"]]]]]]]]]]]]]]]],\n\n[\"def\", \"read-str\", [\"fn\", [\"strn\"],\n  [\"let\", [\"tokens\", [\"tokenize\", \"strn\"],\n           \"rdr\", [\"rdr-new\", \"tokens\"]],\n    [\"if\", [\"empty?\", \"tokens\"],\n      null,\n      [\"read-form\", \"rdr\"]]]]],\n\nnull\n]\n"
  },
  {
    "path": "impls/miniMAL/run",
    "content": "#!/usr/bin/env bash\ncd $(dirname $0)\nexec miniMAL ./${STEP:-stepA_mal}.json \"${@}\"\n"
  },
  {
    "path": "impls/miniMAL/step0_repl.json",
    "content": "[\"do\",\n\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"],\n  \"strng\"]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n  \"ast\"]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  \"exp\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], null]]]],\n\n[\"repl\", [\"`\", \"user> \"], \"rep\"],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step1_read_print.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"],\n  [\"read-str\", \"strng\"]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n  \"ast\"]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], null]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"], \"exc\"]]]]],\n\n[\"repl\", [\"`\", \"user> \"], \"rep\"],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step2_eval.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"],\n  [\"read-str\", \"strng\"]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"let\", [\"sym\", [\"get\", \"ast\", [\"`\", \"val\"]]],\n      [\"if\", [\"contains?\", \"env\", \"sym\"],\n        [\"get\", \"env\", \"sym\"],\n        [\"throw\", [\"str\", [\"`\", \"'\"], \"sym\", [\"`\", \"' not found\"]]]]],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"],\n                   \"f\", [\"first\", \"el\"],\n                   \"args\", [\"rest\", \"el\"]],\n            [\"apply\", \"f\", \"args\"]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\",\n  [\"hash-map\",\n    [\"`\", \"+\"], \"+\",\n    [\"`\", \"-\"], \"-\",\n    [\"`\", \"*\"], \"*\",\n    [\"`\", \"/\"], [\"fn\", [\"a\", \"b\"], [\"parseInt\", [\"/\", \"a\", \"b\"]]]]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"], [\".\", \"exc\", [\"`\", \"toString\"]]]]]]],\n\n[\"repl\", [\"`\", \"user> \"], \"rep\"],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step3_env.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"],\n  [\"read-str\", \"strng\"]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n              [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"],\n                       \"f\", [\"first\", \"el\"],\n                       \"args\", [\"rest\", \"el\"]],\n                [\"apply\", \"f\", \"args\"]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"+\"]], \"+\"],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"-\"]], \"-\"],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"*\"]], \"*\"],\n[\"def\", \"div\", [\"fn\", [\"a\", \"b\"], [\"parseInt\", [\"/\", \"a\", \"b\"]]]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"/\"]], \"div\"],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"], [\".\", \"exc\", [\"`\", \"toString\"]]]]]]],\n\n[\"repl\", [\"`\", \"user> \"], \"rep\"],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step4_if_fn_do.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"rest\", \"ast\"]]],\n                [\"nth\", \"el\", [\"-\", [\"count\", \"el\"], 1]]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n              [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"],\n                       \"f\", [\"first\", \"el\"],\n                       \"args\", [\"rest\", \"el\"]],\n                [\"apply\", \"f\", \"args\"]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n\n[\"repl\", [\"`\", \"user> \"], \"rep\"],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step5_tco.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"do\",\n                [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"slice\", \"ast\", 1, [\"-\", [\"count\", \"ast\"], 1]], \"env\"],\n                [\"EVAL\", [\"nth\", \"ast\", [\"-\", [\"count\", \"ast\"], 1]], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n              [\"malfunc\",\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n                [\"nth\", \"ast\", 2], \"env\", [\"nth\", \"ast\", 1]],\n              [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"],\n                       \"f\", [\"first\", \"el\"],\n                       \"args\", [\"rest\", \"el\"]],\n                [\"if\", [\"malfunc?\", \"f\"],\n                  [\"EVAL\", [\"get\", \"f\", [\"`\", \"ast\"]],\n                           [\"env-new\", [\"get\", \"f\", [\"`\", \"env\"]],\n                                       [\"get\", \"f\", [\"`\", \"params\"]],\n                                       \"args\"]],\n                  [\"apply\", \"f\", \"args\"]]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n\n[\"repl\", [\"`\", \"user> \"], \"rep\"],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step6_file.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"do\",\n                [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"slice\", \"ast\", 1, [\"-\", [\"count\", \"ast\"], 1]], \"env\"],\n                [\"EVAL\", [\"nth\", \"ast\", [\"-\", [\"count\", \"ast\"], 1]], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n              [\"malfunc\",\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n                [\"nth\", \"ast\", 2], \"env\", [\"nth\", \"ast\", 1]],\n              [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"],\n                       \"f\", [\"first\", \"el\"],\n                       \"args\", [\"rest\", \"el\"]],\n                [\"if\", [\"malfunc?\", \"f\"],\n                  [\"EVAL\", [\"get\", \"f\", [\"`\", \"ast\"]],\n                           [\"env-new\", [\"get\", \"f\", [\"`\", \"env\"]],\n                                       [\"get\", \"f\", [\"`\", \"params\"]],\n                                       \"args\"]],\n                  [\"apply\", \"f\", \"args\"]]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"eval\"]],\n                        [\"fn\", [\"ast\"], [\"EVAL\", \"ast\", \"repl-env\"]]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"*ARGV*\"]],\n                        [\"slice\", \"argv\", 1]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n[\"rep\", [\"`\", \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]],\n\n[\"if\", [\"not\", [\"empty?\", \"argv\"]],\n  [\"rep\", [\"str\", [\"`\", \"(load-file \\\"\"], [\"get\", \"argv\", 0], [\"`\", \"\\\")\"]]],\n  [\"repl\", [\"`\", \"user> \"], \"rep\"]],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step7_quote.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"starts-with\", [\"fn\", [\"ast\", \"sym\"],\n  [\"and\", [\"not\", [\"empty?\", \"ast\"]],\n          [\"let\", [\"a0\", [\"first\", \"ast\"]],\n            [\"and\", [\"symbol?\", \"a0\"],\n                    [\"=\", \"sym\", [\"get\", \"a0\", [\"`\", \"val\"]]]]]]]],\n\n[\"def\", \"quasiquote-loop\", [\"fn\", [\"xs\"],\n  [\"if\", [\"empty?\", \"xs\"],\n    [\"list\"],\n    [\"let\", [\"elt\", [\"first\", \"xs\"],\n             \"acc\", [\"quasiquote-loop\", [\"rest\", \"xs\"]]],\n      [\"if\", [\"and\", [\"list?\", \"elt\"],\n                     [\"starts-with\", \"elt\", [\"`\", \"splice-unquote\"]]],\n        [\"list\", [\"symbol\", [\"`\", \"concat\"]], [\"nth\", \"elt\", 1], \"acc\"],\n        [\"list\", [\"symbol\", [\"`\", \"cons\"]], [\"quasiquote\", \"elt\"], \"acc\"]]]]]],\n\n[\"def\", \"quasiquote\", [\"fn\", [\"ast\"],\n  [\"if\", [\"list?\", \"ast\"],\n    [\"if\", [\"starts-with\", \"ast\", [\"`\", \"unquote\"]],\n      [\"nth\", \"ast\", 1],\n      [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"list\", [\"symbol\", [\"`\", \"vec\"]], [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"or\", [\"map?\", \"ast\"], [\"symbol?\", \"ast\"]],\n    [\"list\", [\"symbol\", [\"`\", \"quote\"]], \"ast\"],\n    \"ast\"]]]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"quote\"], \"a0\"],\n              [\"nth\", \"ast\", 1],\n            [\"if\", [\"=\", [\"`\", \"quasiquote\"], \"a0\"],\n              [\"EVAL\", [\"quasiquote\", [\"nth\", \"ast\", 1]], \"env\"],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"do\",\n                [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"slice\", \"ast\", 1, [\"-\", [\"count\", \"ast\"], 1]], \"env\"],\n                [\"EVAL\", [\"nth\", \"ast\", [\"-\", [\"count\", \"ast\"], 1]], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n              [\"malfunc\",\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n                [\"nth\", \"ast\", 2], \"env\", [\"nth\", \"ast\", 1]],\n              [\"let\", [\"el\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"],\n                       \"f\", [\"first\", \"el\"],\n                       \"args\", [\"rest\", \"el\"]],\n                [\"if\", [\"malfunc?\", \"f\"],\n                  [\"EVAL\", [\"get\", \"f\", [\"`\", \"ast\"]],\n                           [\"env-new\", [\"get\", \"f\", [\"`\", \"env\"]],\n                                       [\"get\", \"f\", [\"`\", \"params\"]],\n                                       \"args\"]],\n                  [\"apply\", \"f\", \"args\"]]]]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"eval\"]],\n                        [\"fn\", [\"ast\"], [\"EVAL\", \"ast\", \"repl-env\"]]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"*ARGV*\"]],\n                        [\"slice\", \"argv\", 1]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n[\"rep\", [\"`\", \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]],\n\n[\"if\", [\"not\", [\"empty?\", \"argv\"]],\n  [\"rep\", [\"str\", [\"`\", \"(load-file \\\"\"], [\"get\", \"argv\", 0], [\"`\", \"\\\")\"]]],\n  [\"repl\", [\"`\", \"user> \"], \"rep\"]],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step8_macros.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"starts-with\", [\"fn\", [\"ast\", \"sym\"],\n  [\"and\", [\"not\", [\"empty?\", \"ast\"]],\n          [\"let\", [\"a0\", [\"first\", \"ast\"]],\n            [\"and\", [\"symbol?\", \"a0\"],\n                    [\"=\", \"sym\", [\"get\", \"a0\", [\"`\", \"val\"]]]]]]]],\n\n[\"def\", \"quasiquote-loop\", [\"fn\", [\"xs\"],\n  [\"if\", [\"empty?\", \"xs\"],\n    [\"list\"],\n    [\"let\", [\"elt\", [\"first\", \"xs\"],\n             \"acc\", [\"quasiquote-loop\", [\"rest\", \"xs\"]]],\n      [\"if\", [\"and\", [\"list?\", \"elt\"],\n                     [\"starts-with\", \"elt\", [\"`\", \"splice-unquote\"]]],\n        [\"list\", [\"symbol\", [\"`\", \"concat\"]], [\"nth\", \"elt\", 1], \"acc\"],\n        [\"list\", [\"symbol\", [\"`\", \"cons\"]], [\"quasiquote\", \"elt\"], \"acc\"]]]]]],\n\n[\"def\", \"quasiquote\", [\"fn\", [\"ast\"],\n  [\"if\", [\"list?\", \"ast\"],\n    [\"if\", [\"starts-with\", \"ast\", [\"`\", \"unquote\"]],\n      [\"nth\", \"ast\", 1],\n      [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"list\", [\"symbol\", [\"`\", \"vec\"]], [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"or\", [\"map?\", \"ast\"], [\"symbol?\", \"ast\"]],\n    [\"list\", [\"symbol\", [\"`\", \"quote\"]], \"ast\"],\n    \"ast\"]]]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"quote\"], \"a0\"],\n              [\"nth\", \"ast\", 1],\n            [\"if\", [\"=\", [\"`\", \"quasiquote\"], \"a0\"],\n              [\"EVAL\", [\"quasiquote\", [\"nth\", \"ast\", 1]], \"env\"],\n            [\"if\", [\"=\", [\"`\", \"defmacro!\"], \"a0\"],\n              [\"let\", [\"func\", [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n                [\"do\",\n                  [\"set\", \"func\", [\"`\", \"macro?\"], true],\n                  [\"env-set\", \"env\", [\"nth\", \"ast\", 1], \"func\"]]],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"do\",\n                [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"slice\", \"ast\", 1, [\"-\", [\"count\", \"ast\"], 1]], \"env\"],\n                [\"EVAL\", [\"nth\", \"ast\", [\"-\", [\"count\", \"ast\"], 1]], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n              [\"malfunc\",\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n                [\"nth\", \"ast\", 2], \"env\", [\"nth\", \"ast\", 1]],\n              [\"let\", [\"f\", [\"EVAL\", [\"first\", \"ast\"], \"env\"],\n                       \"args\", [\"rest\", \"ast\"]],\n                [\"if\", [\"malfunc?\", \"f\"],\n                  [\"if\", [\"get\", \"f\", [\"`\", \"macro?\"]],\n                    [\"EVAL\", [\"apply\", [\"get\", \"f\", [\"`\", \"fn\"]], \"args\"], \"env\"],\n                    [\"EVAL\", [\"get\", \"f\", [\"`\", \"ast\"]],\n                           [\"env-new\", [\"get\", \"f\", [\"`\", \"env\"]],\n                                       [\"get\", \"f\", [\"`\", \"params\"]],\n                                       [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"args\"]]]],\n                  [\"apply\", \"f\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"args\"]]]]]]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"eval\"]],\n                        [\"fn\", [\"ast\"], [\"EVAL\", \"ast\", \"repl-env\"]]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"*ARGV*\"]],\n                        [\"slice\", \"argv\", 1]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n[\"rep\", [\"`\", \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]],\n[\"rep\", [\"`\", \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"]],\n\n[\"if\", [\"not\", [\"empty?\", \"argv\"]],\n  [\"rep\", [\"str\", [\"`\", \"(load-file \\\"\"], [\"get\", \"argv\", 0], [\"`\", \"\\\")\"]]],\n  [\"repl\", [\"`\", \"user> \"], \"rep\"]],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/step9_try.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"starts-with\", [\"fn\", [\"ast\", \"sym\"],\n  [\"and\", [\"not\", [\"empty?\", \"ast\"]],\n          [\"let\", [\"a0\", [\"first\", \"ast\"]],\n            [\"and\", [\"symbol?\", \"a0\"],\n                    [\"=\", \"sym\", [\"get\", \"a0\", [\"`\", \"val\"]]]]]]]],\n\n[\"def\", \"quasiquote-loop\", [\"fn\", [\"xs\"],\n  [\"if\", [\"empty?\", \"xs\"],\n    [\"list\"],\n    [\"let\", [\"elt\", [\"first\", \"xs\"],\n             \"acc\", [\"quasiquote-loop\", [\"rest\", \"xs\"]]],\n      [\"if\", [\"and\", [\"list?\", \"elt\"],\n                     [\"starts-with\", \"elt\", [\"`\", \"splice-unquote\"]]],\n        [\"list\", [\"symbol\", [\"`\", \"concat\"]], [\"nth\", \"elt\", 1], \"acc\"],\n        [\"list\", [\"symbol\", [\"`\", \"cons\"]], [\"quasiquote\", \"elt\"], \"acc\"]]]]]],\n\n[\"def\", \"quasiquote\", [\"fn\", [\"ast\"],\n  [\"if\", [\"list?\", \"ast\"],\n    [\"if\", [\"starts-with\", \"ast\", [\"`\", \"unquote\"]],\n      [\"nth\", \"ast\", 1],\n      [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"list\", [\"symbol\", [\"`\", \"vec\"]], [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"or\", [\"map?\", \"ast\"], [\"symbol?\", \"ast\"]],\n    [\"list\", [\"symbol\", [\"`\", \"quote\"]], \"ast\"],\n    \"ast\"]]]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"quote\"], \"a0\"],\n              [\"nth\", \"ast\", 1],\n            [\"if\", [\"=\", [\"`\", \"quasiquote\"], \"a0\"],\n              [\"EVAL\", [\"quasiquote\", [\"nth\", \"ast\", 1]], \"env\"],\n            [\"if\", [\"=\", [\"`\", \"defmacro!\"], \"a0\"],\n              [\"let\", [\"func\", [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n                [\"do\",\n                  [\"set\", \"func\", [\"`\", \"macro?\"], true],\n                  [\"env-set\", \"env\", [\"nth\", \"ast\", 1], \"func\"]]],\n            [\"if\", [\"=\", [\"`\", \"try*\"], \"a0\"],\n              [\"if\", [\"and\", [\">\", [\"count\", \"ast\"], 2],\n                             [\"=\", [\"`\", \"catch*\"],\n                                   [\"get\", [\"nth\", [\"nth\", \"ast\", 2], 0],\n                                           [\"`\", \"val\"]]]],\n                [\"try\",\n                  [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"],\n                  [\"catch\", \"exc\",\n                    [\"EVAL\", [\"nth\", [\"nth\", \"ast\", 2], 2],\n                             [\"env-new\", \"env\",\n                                         [\"list\", [\"nth\", [\"nth\", \"ast\", 2], 1]],\n                                         [\"list\", \"exc\"]]]]],\n                [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"do\",\n                [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"slice\", \"ast\", 1, [\"-\", [\"count\", \"ast\"], 1]], \"env\"],\n                [\"EVAL\", [\"nth\", \"ast\", [\"-\", [\"count\", \"ast\"], 1]], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n              [\"malfunc\",\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n                [\"nth\", \"ast\", 2], \"env\", [\"nth\", \"ast\", 1]],\n              [\"let\", [\"f\", [\"EVAL\", [\"first\", \"ast\"], \"env\"],\n                       \"args\", [\"rest\", \"ast\"]],\n                [\"if\", [\"malfunc?\", \"f\"],\n                  [\"if\", [\"get\", \"f\", [\"`\", \"macro?\"]],\n                    [\"EVAL\", [\"apply\", [\"get\", \"f\", [\"`\", \"fn\"]], \"args\"], \"env\"],\n                    [\"EVAL\", [\"get\", \"f\", [\"`\", \"ast\"]],\n                           [\"env-new\", [\"get\", \"f\", [\"`\", \"env\"]],\n                                       [\"get\", \"f\", [\"`\", \"params\"]],\n                                       [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"args\"]]]],\n                  [\"apply\", \"f\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"args\"]]]]]]]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"eval\"]],\n                        [\"fn\", [\"ast\"], [\"EVAL\", \"ast\", \"repl-env\"]]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"*ARGV*\"]],\n                        [\"slice\", \"argv\", 1]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n[\"rep\", [\"`\", \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]],\n[\"rep\", [\"`\", \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"]],\n\n[\"if\", [\"not\", [\"empty?\", \"argv\"]],\n  [\"rep\", [\"str\", [\"`\", \"(load-file \\\"\"], [\"get\", \"argv\", 0], [\"`\", \"\\\")\"]]],\n  [\"repl\", [\"`\", \"user> \"], \"rep\"]],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/stepA_mal.json",
    "content": "[\"do\",\n\n[\"load\", [\"`\", \"miniMAL-core.json\"]],\n[\"load\", [\"`\", \"types.json\"]],\n[\"load\", [\"`\", \"reader.json\"]],\n[\"load\", [\"`\", \"printer.json\"]],\n[\"load\", [\"`\", \"env.json\"]],\n[\"load\", [\"`\", \"core.json\"]],\n\n[\"def\", \"READ\", [\"fn\", [\"strng\"], [\"read-str\", \"strng\"]]],\n\n[\"def\", \"starts-with\", [\"fn\", [\"ast\", \"sym\"],\n  [\"and\", [\"not\", [\"empty?\", \"ast\"]],\n          [\"let\", [\"a0\", [\"first\", \"ast\"]],\n            [\"and\", [\"symbol?\", \"a0\"],\n                    [\"=\", \"sym\", [\"get\", \"a0\", [\"`\", \"val\"]]]]]]]],\n\n[\"def\", \"quasiquote-loop\", [\"fn\", [\"xs\"],\n  [\"if\", [\"empty?\", \"xs\"],\n    [\"list\"],\n    [\"let\", [\"elt\", [\"first\", \"xs\"],\n             \"acc\", [\"quasiquote-loop\", [\"rest\", \"xs\"]]],\n      [\"if\", [\"and\", [\"list?\", \"elt\"],\n                     [\"starts-with\", \"elt\", [\"`\", \"splice-unquote\"]]],\n        [\"list\", [\"symbol\", [\"`\", \"concat\"]], [\"nth\", \"elt\", 1], \"acc\"],\n        [\"list\", [\"symbol\", [\"`\", \"cons\"]], [\"quasiquote\", \"elt\"], \"acc\"]]]]]],\n\n[\"def\", \"quasiquote\", [\"fn\", [\"ast\"],\n  [\"if\", [\"list?\", \"ast\"],\n    [\"if\", [\"starts-with\", \"ast\", [\"`\", \"unquote\"]],\n      [\"nth\", \"ast\", 1],\n      [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"list\", [\"symbol\", [\"`\", \"vec\"]], [\"quasiquote-loop\", \"ast\"]],\n  [\"if\", [\"or\", [\"map?\", \"ast\"], [\"symbol?\", \"ast\"]],\n    [\"list\", [\"symbol\", [\"`\", \"quote\"]], \"ast\"],\n    \"ast\"]]]]],\n\n[\"def\", \"LET\", [\"fn\", [\"env\", \"args\"],\n  [\"if\", [\">\", [\"count\", \"args\"], 0],\n    [\"do\",\n      [\"env-set\", \"env\", [\"nth\", \"args\", 0],\n                         [\"EVAL\", [\"nth\", \"args\", 1], \"env\"]],\n      [\"LET\", \"env\", [\"rest\", [\"rest\", \"args\"]]]]]]],\n\n[\"def\", \"EVAL\", [\"fn\", [\"ast\", \"env\"],\n [\"do\",\n  [\"let\", [\"debug-eval-sym\", [\"symbol\", [\"`\", \"DEBUG-EVAL\"]],\n           \"debug-eval-env\", [\"env-find\", \"env\", \"debug-eval-sym\"]],\n    [\"if\", [\"not\", [\"=\", \"debug-eval-env\", null]],\n      [\"let\", [\"debug-eval\", [\"env-get\", \"debug-eval-env\", \"debug-eval-sym\"]],\n        [\"if\", [\"not\", [\"or\", [\"=\", \"debug-eval\", null],\n                              [\"=\", \"debug-eval\", false]]],\n          [\"println\", [\"`\", \"EVAL:\"], [\"pr-str\", \"ast\", true]]]]]],\n  [\"if\", [\"symbol?\", \"ast\"],\n    [\"env-get\", \"env\", \"ast\"],\n  [\"if\", [\"vector?\", \"ast\"],\n    [\"vectorl\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"ast\"]],\n  [\"if\", [\"map?\", \"ast\"],\n    [\"let\", [\"new-hm\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"], [\"set\", \"new-hm\",\n                                \"k\",\n                                [\"EVAL\", [\"get\", \"ast\", \"k\"], \"env\"]]],\n                [\"keys\", \"ast\"]],\n        \"new-hm\"]],\n  [\"if\", [\"not\", [\"list?\", \"ast\"]],\n        \"ast\",\n        [\"if\", [\"empty?\", \"ast\"],\n          \"ast\",\n          [\"let\", [\"a0\", [\"get\", [\"first\", \"ast\"], [\"`\", \"val\"]]],\n            [\"if\", [\"=\", [\"`\", \"def!\"], \"a0\"],\n              [\"env-set\", \"env\", [\"nth\", \"ast\", 1],\n                                 [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"let*\"], \"a0\"],\n              [\"let\", [\"let-env\", [\"env-new\", \"env\"]],\n                [\"do\",\n                  [\"LET\", \"let-env\", [\"nth\", \"ast\", 1]],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"let-env\"]]],\n            [\"if\", [\"=\", [\"`\", \"quote\"], \"a0\"],\n              [\"nth\", \"ast\", 1],\n            [\"if\", [\"=\", [\"`\", \"quasiquote\"], \"a0\"],\n              [\"EVAL\", [\"quasiquote\", [\"nth\", \"ast\", 1]], \"env\"],\n            [\"if\", [\"=\", [\"`\", \"defmacro!\"], \"a0\"],\n              [\"let\", [\"func\", [\"_clone\", [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n                [\"do\",\n                  [\"set\", \"func\", [\"`\", \"macro?\"], true],\n                  [\"env-set\", \"env\", [\"nth\", \"ast\", 1], \"func\"]]],\n            [\"if\", [\"=\", [\"`\", \"try*\"], \"a0\"],\n              [\"if\", [\"and\", [\">\", [\"count\", \"ast\"], 2],\n                             [\"=\", [\"`\", \"catch*\"],\n                                   [\"get\", [\"nth\", [\"nth\", \"ast\", 2], 0],\n                                           [\"`\", \"val\"]]]],\n                [\"try\",\n                  [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"],\n                  [\"catch\", \"exc\",\n                    [\"EVAL\", [\"nth\", [\"nth\", \"ast\", 2], 2],\n                             [\"env-new\", \"env\",\n                                         [\"list\", [\"nth\", [\"nth\", \"ast\", 2], 1]],\n                                         [\"list\", \"exc\"]]]]],\n                [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"do\"], \"a0\"],\n              [\"do\",\n                [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], [\"slice\", \"ast\", 1, [\"-\", [\"count\", \"ast\"], 1]], \"env\"],\n                [\"EVAL\", [\"nth\", \"ast\", [\"-\", [\"count\", \"ast\"], 1]], \"env\"]],\n            [\"if\", [\"=\", [\"`\", \"if\"], \"a0\"],\n              [\"let\", [\"cond\", [\"EVAL\", [\"nth\", \"ast\", 1], \"env\"]],\n                [\"if\", [\"or\", [\"=\", \"cond\", null], [\"=\", \"cond\", false]],\n                  [\"if\", [\">\", [\"count\", \"ast\"], 3],\n                    [\"EVAL\", [\"nth\", \"ast\", 3], \"env\"],\n                    null],\n                  [\"EVAL\", [\"nth\", \"ast\", 2], \"env\"]]],\n            [\"if\", [\"=\", [\"`\", \"fn*\"], \"a0\"],\n              [\"malfunc\",\n                [\"fn\", [\"&\", \"args\"],\n                  [\"let\", [\"e\", [\"env-new\", \"env\", [\"nth\", \"ast\", 1], \"args\"]],\n                    [\"EVAL\", [\"nth\", \"ast\", 2], \"e\"]]],\n                [\"nth\", \"ast\", 2], \"env\", [\"nth\", \"ast\", 1]],\n              [\"let\", [\"f\", [\"EVAL\", [\"first\", \"ast\"], \"env\"],\n                       \"args\", [\"rest\", \"ast\"]],\n                [\"if\", [\"malfunc?\", \"f\"],\n                  [\"if\", [\"get\", \"f\", [\"`\", \"macro?\"]],\n                    [\"EVAL\", [\"apply\", [\"get\", \"f\", [\"`\", \"fn\"]], \"args\"], \"env\"],\n                    [\"EVAL\", [\"get\", \"f\", [\"`\", \"ast\"]],\n                           [\"env-new\", [\"get\", \"f\", [\"`\", \"env\"]],\n                                       [\"get\", \"f\", [\"`\", \"params\"]],\n                                       [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"args\"]]]],\n                  [\"apply\", \"f\", [\"map\", [\"fn\", [\"x\"], [\"EVAL\", \"x\", \"env\"]], \"args\"]]]]]]]]]]]]]]]]]]]]]],\n\n[\"def\", \"PRINT\", [\"fn\", [\"exp\"],\n  [\"pr-str\", \"exp\", true]]],\n\n\n[\"def\", \"repl-env\", [\"env-new\"]],\n\n[\"def\", \"rep\", [\"fn\", [\"strng\"],\n  [\"try\",\n    [\"PRINT\", [\"EVAL\", [\"READ\", \"strng\"], \"repl-env\"]],\n    [\"catch\", \"exc\",\n      [\"str\", [\"`\", \"Error: \"],\n        [\"if\", [\"isa\", \"exc\", \"Error\"],\n          [\".\", \"exc\", [\"`\", \"toString\"]],\n          [\"pr-str\", \"exc\", true]]]]]]],\n\n[\"`\", \"core.mal: defined using miniMAL\"],\n[\"map\", [\"fn\", [\"k\"], [\"env-set\", \"repl-env\",\n                                  [\"symbol\", \"k\"],\n                                  [\"get\", \"core-ns\", \"k\"]]],\n        [\"keys\", \"core-ns\"]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"eval\"]],\n                        [\"fn\", [\"ast\"], [\"EVAL\", \"ast\", \"repl-env\"]]],\n[\"env-set\", \"repl-env\", [\"symbol\", [\"`\", \"*ARGV*\"]],\n                        [\"slice\", \"argv\", 1]],\n\n[\"`\", \"core.mal: defined using mal itself\"],\n[\"rep\", [\"`\", \"(def! *host-language* \\\"miniMAL\\\")\"]],\n[\"rep\", [\"`\", \"(def! not (fn* (a) (if a false true)))\"]],\n[\"rep\", [\"`\", \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]],\n[\"rep\", [\"`\", \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"]],\n\n[\"if\", [\"not\", [\"empty?\", \"argv\"]],\n  [\"rep\", [\"str\", [\"`\", \"(load-file \\\"\"], [\"get\", \"argv\", 0], [\"`\", \"\\\")\"]]],\n  [\"do\",\n    [\"rep\", [\"`\", \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"]],\n    [\"repl\", [\"`\", \"user> \"], \"rep\"]]],\n\nnull\n\n]\n"
  },
  {
    "path": "impls/miniMAL/tests/step5_tco.mal",
    "content": ";; miniMAL skipping non-TCO recursion\n;; Reason: Unrecoverable stack overflow at 10,000\n"
  },
  {
    "path": "impls/miniMAL/types.json",
    "content": "[\"do\",\n\n[\"`\", \"Utility Functions\"],\n[\"def\", \"_cmp_seqs\", [\"fn\", [\"a\", \"b\"],\n  [\"if\", [\"not\", [\"=\", [\"count\", \"a\"], [\"count\", \"b\"]]],\n    false,\n    [\"if\", [\"empty?\", \"a\"],\n      true,\n      [\"if\", [\"equal?\", [\"get\", \"a\", 0], [\"get\", \"b\", 0]],\n        [\"_cmp_seqs\", [\"rest\", \"a\"], [\"rest\", \"b\"]],\n        false]]]]],\n\n[\"def\", \"_check_hash_map_keys\", [\"fn\", [\"ks\", \"a\", \"b\"],\n  [\"if\", [\"empty?\", \"ks\"],\n    true,\n    [\"let\", [\"k\", [\"first\", \"ks\"]],\n      [\"if\", [\"equal?\", [\"get\", \"a\", \"k\"], [\"get\", \"b\", \"k\"]],\n        [\"_check_hash_map_keys\", [\"rest\", \"ks\"], \"a\", \"b\"],\n        false]]]]],\n[\"def\", \"_cmp_hash_maps\", [\"fn\", [\"a\", \"b\"],\n  [\"let\", [\"aks\", [\"keys\", \"a\"]],\n    [\"if\", [\"not\", [\"=\", [\"count\", \"aks\"], [\"count\", [\"keys\", \"b\"]]]],\n      false,\n      [\"_check_hash_map_keys\", \"aks\", \"a\", \"b\"]]]]],\n\n[\"def\", \"equal?\", [\"fn\", [\"a\", \"b\"],\n  [\"if\", [\"sequential?\", \"a\"],\n    [\"if\", [\"sequential?\", \"b\"],\n      [\"_cmp_seqs\", \"a\", \"b\"],\n      false],\n  [\"if\", [\"map?\", \"a\"],\n    [\"if\", [\"map?\", \"b\"],\n      [\"_cmp_hash_maps\", \"a\", \"b\"],\n      false],\n  [\"if\", [\"symbol?\", \"a\"],\n    [\"if\", [\"symbol?\", \"b\"],\n      [\"=\", [\"get\", \"a\", [\"`\", \"val\"]], [\"get\", \"b\", [\"`\", \"val\"]]],\n      false],\n  [\"=\", \"a\", \"b\"]]]]]],\n\n[\"def\", \"_clone\", [\"fn\", [\"obj\"],\n  [\"if\", [\"list?\", \"obj\"],\n    [\"slice\", \"obj\", 0],\n  [\"if\", [\"vector?\", \"obj\"],\n    [\"let\", [\"new-obj\", [\"slice\", \"obj\", 0]],\n      [\"do\",\n        [\"set\", \"new-obj\", [\"`\", \"__vector?__\"], true],\n        \"new-obj\"]],\n  [\"if\", [\"map?\", \"obj\"],\n    [\"let\", [\"new-obj\", [\"hash-map\"]],\n      [\"do\",\n        [\"map\", [\"fn\", [\"k\"],\n                  [\"if\", [\".\", \"obj\", [\"`\", \"hasOwnProperty\"], \"k\"],\n                    [\"set\", \"new-obj\", \"k\", [\"get\", \"obj\", \"k\"]],\n                    null]],\n                [\"keys\", \"obj\"]],\n        \"new-obj\"]],\n  [\"if\", [\"malfunc?\", \"obj\"],\n    [\"let\", [\"new-obj\", [\"malfunc\", [\"get\", \"obj\", [\"`\", \"fn\"]],\n                                    [\"get\", \"obj\", [\"`\", \"ast\"]],\n                                    [\"get\", \"obj\", [\"`\", \"env\"]],\n                                    [\"get\", \"obj\", [\"`\", \"params\"]]]],\n      [\"do\",\n        [\"set\", \"new-obj\", [\"`\", \"macro?\"], [\"get\", \"obj\", [\"`\", \"macro?\"]]],\n        [\"set\", \"new-obj\", [\"`\", \"__meta__\"], [\"get\", \"obj\", [\"`\", \"__meta__\"]]],\n        \"new-obj\"]],\n    [\"throw\", \"clone of unsupported type\"]]]]]]],\n\n[\"def\", \"clone\", [\"fn\", [\"obj\"],\n  [\"let\", [\"new-obj\", [\"_clone\", \"obj\"]],\n    [\"do\",\n      [\".\", \"Object\", [\"`\", \"defineProperty\"], \"new-obj\", [\"`\", \"__meta__\"],\n        {\"enumerable\": false, \"writable\": true}],\n      \"new-obj\"]]]],\n\n[\"def\", \"assoc!\", [\"fn\", [\"a\", \"b\", \"c\"], [\"do\", [\"set\", \"a\", \"b\", \"c\"], \"a\"]]],\n[\"def\", \"assocs!\",  [\"fn\", [\"hm\", \"kvs\"],\n    [\"if\", [\"empty?\", \"kvs\"],\n      \"hm\",\n      [\"do\",\n        [\"assoc!\", \"hm\", [\"get\", \"kvs\", 0], [\"get\", \"kvs\", 1]],\n        [\"assocs!\", \"hm\", [\"slice\", \"kvs\", 2]]]]]],\n\n\n[\"def\", \"Symbol\", [\"fn\", [], null]],\n[\"def\", \"symbol\", [\"fn\", [\"name\"],\n  [\"assoc!\", [\"new\", \"Symbol\"], [\"`\", \"val\"], \"name\"]]],\n\n[\"def\", \"symbol?\", [\"fn\", [\"a\"],\n  [\"isa\", \"a\", \"Symbol\"]]],\n\n\n[\"def\", \"keyword\", [\"fn\", [\"name\"],\n  [\"if\", [\"keyword?\", \"name\"],\n    \"name\",\n    [\"str\", [\"`\", \"\\u029e\"], \"name\"]]]],\n\n[\"def\", \"keyword?\", [\"fn\", [\"kw\"],\n  [\"and\", [\"=\", [\"`\", \"[object String]\"], [\"classOf\", \"kw\"]],\n          [\"=\", [\"`\", \"\\u029e\"], [\"get\", \"kw\", 0]]]]],\n\n\n[\"`\", \"Override some list defs to account for Vectors\"],\n[\"def\", \"sequential?\", [\"fn\", [\"a\"],\n  [\".\", \"Array\", [\"`\", \"isArray\"], \"a\"]]],\n\n[\"def\", \"list?\", [\"fn\", [\"a\"],\n  [\"if\", [\".\", \"Array\", [\"`\", \"isArray\"], \"a\"],\n    [\"if\", [\".-\", \"a\", [\"`\", \"__vector?__\"]],\n      false,\n      true],\n    false]]],\n\n[\"def\", \"empty?\", [\"fn\", [\"a\"],\n  [\"if\", [\"sequential?\", \"a\"],\n    [\"if\", [\"=\", 0, [\".-\", \"a\", [\"`\", \"length\"]]],\n      true,\n      false],\n    [\"=\", \"a\", null]]]],\n\n\n[\"def\", \"vectorl\", [\"fn\", [\"lst\"],\n  [\"let\", [\"vec\", [\"slice\", \"lst\", 0]],\n    [\"do\",\n      [\"set\", \"vec\", [\"`\", \"__vector?__\"], true],\n      \"vec\"]]]],\n\n[\"def\", \"vector\", [\"fn\", [\"&\", \"args\"], [\"vectorl\", \"args\"]]],\n\n[\"def\", \"vector?\", [\"fn\", [\"a\"],\n  [\"if\", [\".\", \"Array\", [\"`\", \"isArray\"], \"a\"],\n    [\"if\", [\".-\", \"a\", [\"`\", \"__vector?__\"]],\n      true,\n      false],\n    false]]],\n\n\n[\"def\", \"HashMap\", [\"fn\", [], null]],\n[\"def\", \"hash-map\", [\"fn\", [\"&\", \"a\"],\n  [\"assocs!\", [\"new\", \"HashMap\"], \"a\"]]],\n[\"def\", \"map?\", [\"fn\", [\"a\"],\n  [\"isa\", \"a\", \"HashMap\"]]],\n\n[\"def\", \"MalFunc\", [\"fn\", [], null]],\n[\"def\", \"malfunc\", [\"fn\", [\"fn\", \"ast\", \"env\", \"params\"],\n  [\"assocs!\", [\"new\", \"MalFunc\"],\n              [\"list\", [\"`\", \"fn\"], \"fn\",\n                       [\"`\", \"ast\"], \"ast\",\n                       [\"`\", \"env\"], \"env\",\n                       [\"`\", \"params\"], \"params\",\n                       [\"`\", \"macro?\"], false]]]],\n\n[\"def\", \"malfunc?\", [\"fn\", [\"a\"],\n  [\"isa\", \"a\", \"MalFunc\"]]],\n\n[\"def\", \"Atom\", [\"fn\", [], null]],\n[\"def\", \"atom\", [\"fn\", [\"a\"],\n  [\"let\", [\"atm\", [\"new\", \"Atom\"]],\n    [\"do\",\n      [\"set\", \"atm\", [\"`\", \"val\"], \"a\"],\n      \"atm\"]]]],\n[\"def\", \"atom?\", [\"fn\", [\"a\"],\n  [\"isa\", \"a\", \"Atom\"]]],\n\nnull\n]\n"
  },
  {
    "path": "impls/nasm/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Install nasm and ld\nRUN apt-get -y install nasm binutils\n"
  },
  {
    "path": "impls/nasm/Makefile",
    "content": "\nSTEPS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\n\nCOMPONENTS = env.asm core.asm reader.asm printer.asm types.asm system.asm exceptions.asm\n\n\nall: $(STEPS)\n\n%.o: %.asm $(COMPONENTS)\n\tnasm -felf64 $<\n\n%: %.o\n\tld -o $@ $<\n\n.PHONY: clean\nclean:\n\trm -f $(STEPS) $(STEPS:%=%.o)\n"
  },
  {
    "path": "impls/nasm/README.md",
    "content": "# x86_64 NASM implementation\n\nNotes and known issues:\n\n* No library dependencies, only Linux system calls\n\n* Simple readline implemented, just supporting backspace for editing\n\n* Reference counting used for memory management. No attempt is made\n  to find circular references, so leaks are possible. In particular\n  defining a function with def! creates a circular reference loop.\n  \n* The exception/error handling just resets the stack and jumps to a handler,\n  so does not release memory\n\n* Memory is allocated by two fixed-size allocators (`Cons` and `Array` objects)\n  which have limits specified in types.asm. If more memory is needed\n  then this must currently be done at compile-time, but adding sys_brk\n  calls could be done.\n\n* The hash map implementation is just a list of key-value pairs.\n  Moving symbols around in the core environment makes a significant difference\n  (20-30%) to the performance test. A simple optimisation could be to\n  move items when found to the start of the list so that frequently\n  searched keys are nearer the front.\n\n* `conj` function not yet implemented\n\n* `*env*`  symbol evaluates to current Environment.\n\n"
  },
  {
    "path": "impls/nasm/core.asm",
    "content": ";; Core functions\n;;\n;;\n\n%include \"macros.mac\"\n        \nsection .data\n\n;; Symbols for comparison\n        static core_add_symbol, db \"+\"\n        static core_sub_symbol, db \"-\"\n        static core_mul_symbol, db \"*\"\n        static core_div_symbol, db \"/\"\n\n        static core_listp_symbol, db \"list?\"\n        static core_emptyp_symbol, db \"empty?\"\n        \n        static core_equal_symbol, db \"=\"\n        static core_gt_symbol, db \">\"\n        static core_lt_symbol, db \"<\"\n        static core_ge_symbol, db \">=\"\n        static core_le_symbol, db \"<=\"\n\n        static core_count_symbol, db \"count\"\n        static core_keys_symbol, db \"keys\"\n        static core_vals_symbol, db \"vals\"\n\n        static core_list_symbol, db \"list\"\n\n        static core_pr_str_symbol, db \"pr-str\"\n        static core_prn_symbol, db \"prn\"\n        static core_str_symbol, db \"str\"\n        static core_println_symbol, db \"println\"\n\n        static core_read_string_symbol, db \"read-string\"\n        static core_slurp_symbol, db \"slurp\"\n        static core_eval_symbol, db \"eval\"\n\n        static core_atom_symbol, db \"atom\"\n        static core_deref_symbol, db \"deref\"\n        static core_atomp_symbol, db \"atom?\"\n        static core_reset_symbol, db \"reset!\"\n        static core_swap_symbol, db \"swap!\"\n\n        static core_cons_symbol, db \"cons\"\n        static core_concat_symbol, db \"concat\"\n        static core_vec_symbol, db \"vec\"\n\n        static core_first_symbol, db \"first\"\n        static core_rest_symbol, db \"rest\"\n        static core_nth_symbol, db \"nth\"\n\n        static core_nilp_symbol, db \"nil?\"\n        static core_truep_symbol, db \"true?\"\n        static core_falsep_symbol, db \"false?\"\n        static core_numberp_symbol, db \"number?\"\n\n        static core_symbolp_symbol, db \"symbol?\"\n        static core_stringp_symbol, db \"string?\"\n        static core_fnp_symbol, db \"fn?\"\n        static core_macrop_symbol, db \"macro?\"\n        static core_keywordp_symbol, db \"keyword?\"\n\n        static core_containsp_symbol, db \"contains?\"\n        static core_get_symbol, db \"get\"\n        static core_vectorp_symbol, db \"vector?\"\n        static core_mapp_symbol, db \"map?\"\n        static core_sequentialp_symbol, db \"sequential?\"\n        \n        static core_throw_symbol, db \"throw\"\n        \n        static core_map_symbol, db \"map\"\n        static core_apply_symbol, db \"apply\"\n\n        static core_symbol_symbol, db \"symbol\"\n        static core_vector_symbol, db \"vector\"\n        static core_hashmap_symbol, db \"hash-map\"\n        static core_keyword_symbol, db \"keyword\"\n\n        static core_assoc_symbol, db \"assoc\"\n        static core_dissoc_symbol, db \"dissoc\"\n\n        static core_readline_symbol, db \"readline\"\n\n        static core_meta_symbol, db \"meta\"\n        static core_with_meta_symbol, db \"with-meta\"\n\n        static core_time_ms_symbol, db \"time-ms\"\n\n        static core_seq_symbol, db \"seq\"\n        \n;; Strings\n\n        static core_arith_missing_args, db \"integer arithmetic missing arguments\"\n        static core_arith_not_int, db \"non-integer argument to integer arithmetic\"\n        \n        static core_emptyp_error_string, db \"empty? expects a list, vector or map\",10\n        static core_count_error_string, db \"count expects a list or vector\",10\n\n        static core_keys_not_map, db \"keys expects a map as first argument\"\n        static core_vals_not_map, db \"vals expects a map as first argument\"\n        \n        static core_numeric_expect_ints, db \"comparison operator expected two numbers\",10\n\n        static core_deref_not_atom, db \"Error: argument to deref is not an atom\"\n        static core_reset_not_atom, db \"Error: argument to reset is not an atom\"\n        static core_reset_no_value, db \"Error: missing value argument to reset\"\n\n        static core_swap_not_atom, db \"Error: swap! expects atom as first argument\"\n        static core_swap_no_function, db \"Error: swap! expects function as second argument\"\n        \n        static core_cons_missing_arg, db \"Error: missing argument to cons\"\n        static core_cons_not_vector, db \"Error: cons expects a list or vector\"\n        \n        static core_concat_not_list, db \"Error: concat expects lists or vectors\"\n\n        static core_vec_wrong_arg, db \"Error: vec expects a list or vector \"\n\n        static core_first_missing_arg, db \"Error: missing argument to first\"\n        static core_first_not_list, db \"Error: first expects a list or vector\"\n        \n        static core_rest_missing_arg, db \"Error: missing argument to rest\"\n        static core_rest_not_list, db \"Error: rest expects a list or vector\"\n\n        static core_nth_missing_arg, db \"Error: missing argument to nth\"\n        static core_nth_not_list, db \"Error: nth expects a list or vector as first argument\"\n        static core_nth_not_int, db \"Error: nth expects an integer as second argument\"\n        static core_nth_out_of_range, db \"Error: nth index out of range\"\n\n        static core_value_p_missing_args, db \"Error: value predicate (nil/true/false) missing args\"\n\n        static core_containsp_not_map, db \"Error: contains? expects map as first argument\"\n        static core_containsp_no_key, db \"Error: contains? missing key argument\"\n\n        static core_get_not_map, db \"Error: get expects map as first argument\"\n        static core_get_no_key, db \"Error: get missing key argument\"\n\n        static core_map_missing_args, db \"Error: map expects two arguments (function, list/vector)\"\n        static core_map_not_function, db \"Error: map expects a ufunction for first argument\"\n        static core_map_not_seq, db \"Error: map expects a list or vector as second argument\"\n\n        static core_apply_not_function, db \"Error: apply expects function as first argument\"\n        static core_apply_missing_args, db \"Error: apply missing arguments\"\n        static core_apply_not_seq, db \"Error: apply last argument must be list or vector\"\n\n        static core_symbol_not_string, db \"Error: symbol expects a string argument\"\n\n        static core_keyword_not_string, db \"Error: keyword expects a string or keyword argument\"\n\n        static core_list_not_seq, db \"Error: list expects a list or vector\"\n\n        static core_assoc_not_map, db \"Error: assoc expects a map as first argument\"\n        static core_assoc_missing_value, db \"Error: assoc missing value\"\n\n        static core_dissoc_not_map, db \"dissoc expects a map as first argument\"\n        static core_dissoc_missing_value, db \"Missing value in map passed to dissoc\"\n\n        static core_with_meta_no_function, db \"with-meta expects a function as first argument\"\n        static core_with_meta_no_value, db \"with-meta expects a value as second argument\"\n\n        static core_seq_missing_arg, db \"seq missing argument\"\n        static core_seq_wrong_type, db \"seq expects a list, vector, string or nil\"\n        \nsection .text\n\n;; Add a native function to the core environment\n;; This is used in core_environment\n%macro core_env_native 2\n        push rsi                ; environment\n        mov rsi, %1\n        mov edx, %1.len\n        call raw_to_symbol      ; Symbol in RAX\n        push rax\n        \n        mov rsi, %2\n        call native_function    ; Function in RAX\n        \n        mov rcx, rax            ; value (function)\n        pop rdi                 ; key (symbol)\n        pop rsi                 ; environment\n        call env_set\n%endmacro\n\n        \n;; Create an Environment with core functions\n;;\n;; Returns Environment in RAX\n;;\n;;\ncore_environment:\n        ; Create the top-level environment\n        xor rsi, rsi            ; Set outer to nil\n        call env_new            \n        mov rsi, rax            ; Environment in RSI\n        \n        core_env_native core_cons_symbol, core_cons\n        core_env_native core_concat_symbol, core_concat\n        core_env_native core_vec_symbol, core_vec\n        \n        core_env_native core_first_symbol, core_first\n        core_env_native core_rest_symbol, core_rest\n        core_env_native core_nth_symbol, core_nth\n\n        core_env_native core_add_symbol, core_add\n        core_env_native core_sub_symbol, core_sub\n        core_env_native core_mul_symbol, core_mul\n        core_env_native core_div_symbol, core_div\n        \n        core_env_native core_listp_symbol, core_listp\n        core_env_native core_emptyp_symbol, core_emptyp\n        core_env_native core_count_symbol, core_count\n        \n        core_env_native core_equal_symbol, core_equalp\n        core_env_native core_gt_symbol, core_gt\n        core_env_native core_lt_symbol, core_lt\n        core_env_native core_ge_symbol, core_ge\n        core_env_native core_le_symbol, core_le\n        \n        core_env_native core_keys_symbol, core_keys\n        core_env_native core_vals_symbol, core_vals\n        \n        core_env_native core_list_symbol, core_list\n\n        core_env_native core_pr_str_symbol, core_pr_str\n        core_env_native core_prn_symbol, core_prn\n        core_env_native core_str_symbol, core_str\n        core_env_native core_println_symbol, core_println\n\n        core_env_native core_read_string_symbol, core_read_string\n        core_env_native core_slurp_symbol, core_slurp\n        core_env_native core_eval_symbol, core_eval\n\n        core_env_native core_atom_symbol, core_atom\n        core_env_native core_deref_symbol, core_deref\n        core_env_native core_atomp_symbol, core_atomp\n        core_env_native core_reset_symbol, core_reset\n        core_env_native core_swap_symbol, core_swap\n        \n        core_env_native core_nilp_symbol, core_nilp\n        core_env_native core_truep_symbol, core_truep\n        core_env_native core_falsep_symbol, core_falsep\n        core_env_native core_numberp_symbol, core_numberp\n\n        core_env_native core_symbolp_symbol, core_symbolp\n        core_env_native core_stringp_symbol, core_stringp\n        core_env_native core_fnp_symbol, core_fnp\n        core_env_native core_macrop_symbol, core_macrop\n        core_env_native core_keywordp_symbol, core_keywordp\n        \n        core_env_native core_containsp_symbol, core_containsp\n        core_env_native core_get_symbol, core_get\n        \n        core_env_native core_vectorp_symbol, core_vectorp\n        core_env_native core_mapp_symbol, core_mapp\n        core_env_native core_sequentialp_symbol, core_sequentialp\n        \n        core_env_native core_throw_symbol, core_throw\n\n        core_env_native core_map_symbol, core_map\n        core_env_native core_apply_symbol, core_apply\n\n        core_env_native core_symbol_symbol, core_symbol\n        core_env_native core_vector_symbol, core_vector\n        core_env_native core_hashmap_symbol, core_hashmap\n        core_env_native core_keyword_symbol, core_keyword\n\n        core_env_native core_assoc_symbol, core_assoc\n        core_env_native core_dissoc_symbol, core_dissoc\n\n        core_env_native core_readline_symbol, core_readline\n\n        core_env_native core_meta_symbol, core_meta\n        core_env_native core_with_meta_symbol, core_with_meta\n\n        core_env_native core_time_ms_symbol, core_time_ms\n\n        core_env_native core_seq_symbol, core_seq\n        \n        ; -----------------\n        ; Put the environment in RAX\n        mov rax, rsi\n        ret\n\n;; ----------------------------------------------------\n\n;; Jumped to from many core functions, with\n;; string address in RSI and length in EDX\ncore_throw_str:\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n;; ----------------------------------------------------\n\n     \n        \n;; Integer arithmetic operations\n;; \n;; Adds a list of numbers, address in RSI\n;; Returns the sum as a number object with address in RAX\n;; Since most of the code is common to all operators,\n;; RBX is used to jump to the required instruction\ncore_add:\n        mov rbx, core_arithmetic.do_addition\n        jmp core_arithmetic\ncore_sub:\n        mov rbx, core_arithmetic.do_subtraction\n        jmp core_arithmetic\ncore_mul:\n        mov rbx, core_arithmetic.do_multiply\n        jmp core_arithmetic\ncore_div:\n        mov rbx, core_arithmetic.do_division\n        ; Fall through to core_arithmetic\ncore_arithmetic:\n        ; Check that the first object is a number\n        mov cl, BYTE [rsi]\n        mov ch, cl\n        and ch, block_mask\n        cmp ch, block_cons\n        jne .missing_args\n\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_empty\n        je .missing_args\n\n        cmp ch, content_int\n        jne .not_int\n\n        ; Put the starting value in rax\n        mov rax, [rsi + Cons.car]\n        \n.add_loop:\n        ; Fetch the next value\n        mov cl, [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .finished  ; Nothing let\n        \n        mov rsi, [rsi + Cons.cdr] ; Get next cons\n\n        ; Check that it is an integer\n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        cmp cl, content_int\n        jne .not_int\n\n        ; Jump to the required operation, address in RBX\n        jmp rbx\n        \n.do_addition:\n        add rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_subtraction:\n        sub rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_multiply:\n        imul rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_division:\n        cqo                     ; Sign extend RAX into RDX\n        mov rcx, [rsi + Cons.car]\n        idiv rcx\n        jmp .add_loop\n        \n.finished:\n        ; Value in rbx\n        push rax\n        ; Get a Cons object to put the result into\n        call alloc_cons\n        pop rbx\n        mov [rax], BYTE maltype_integer\n        mov [rax + Cons.car], rbx\n        ret\n        \n.missing_args:\n        load_static core_arith_missing_args\n        jmp core_throw_str\n.not_int:\n        load_static core_arith_not_int\n        jmp core_throw_str\n\n;; compare objects for equality\ncore_equalp:\n        ; Check that rsi contains a list\n        mov cl, BYTE [rsi]\n        cmp cl, maltype_empty_list\n        je .error\n        \n        and cl, block_mask + container_mask\n        cmp cl, block_cons + container_list\n        jne .error\n        \n        ; Check that the list has a second pointer\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .error\n        \n        ; move second pointer into rdi\n        mov rdi, [rsi + Cons.cdr]\n\n        ; Remove next pointers\n        mov cl, BYTE [rsi + Cons.typecdr]\n        mov [rsi + Cons.typecdr], BYTE 0\n        \n        mov bl, BYTE [rdi + Cons.typecdr]\n        mov [rdi + Cons.typecdr], BYTE 0\n\n        push rbx\n        push rcx\n        \n        ; Compare the objects recursively\n        call compare_objects_rec\n\n        ; Restore next pointers\n        pop rcx\n        pop rbx\n        mov [rsi + Cons.typecdr], BYTE cl\n        mov [rdi + Cons.typecdr], BYTE bl\n        \n        je .true\n\n        \n.false:\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n.true:\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n.error:\n        push rsi\n        print_str_mac error_string ; print 'Error: '\n        pop rsi\n        jmp error_throw\n\n;; -----------------------------------------------------------------\n;; Numerical comparisons\n\n\ncore_gt:\n        mov rcx, core_compare_num.gt\n        jmp core_compare_num\ncore_lt:\n        mov rcx, core_compare_num.lt\n        jmp core_compare_num\ncore_ge:\n        mov rcx, core_compare_num.ge\n        jmp core_compare_num\ncore_le:\n        mov rcx, core_compare_num.le  \n        ;jmp core_compare_num\ncore_compare_num:\n        ; The first argument should be an int\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, maltype_integer\n        jne .error\n\n        ; Check that there's a second argument\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .error\n        mov rax, [rsi + Cons.car]\n        mov rdi, [rsi + Cons.cdr]\n\n        ; The second arg should also be an int\n        mov bl, BYTE [rdi]\n        and bl, content_mask\n        cmp bl, maltype_integer\n        jne .error\n\n        mov rbx, [rdi + Cons.car]\n\n        cmp rax, rbx\n        jmp rcx                 ; Address set above\n.gt:\n        jg .true\n        jmp .false\n.lt:\n        jl .true\n        jmp .false\n.ge:\n        jge .true\n        jmp .false\n.le:\n        jle .true\n        ;jmp .false\n.false:\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n.true:\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n.error:\n        push rsi\n        print_str_mac error_string ; print 'Error: '\n        print_str_mac core_numeric_expect_ints\n        pop rsi\n        jmp error_throw\n        \n;; Test if a given object is a list\n;; Input list in RSI\n;; Returns true or false in RAX\ncore_listp:\n        mov bl, (block_cons + container_list)\n        jmp core_container_p\ncore_vectorp:\n        mov bl, (block_cons + container_vector)\n        jmp core_container_p\ncore_mapp:\n        mov bl, (block_cons + container_map)\n        ;jmp core_container_p     \ncore_container_p:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .false              ; Should be a pointer to a list\n\n        mov rax, [rsi + Cons.car]\n        mov al, BYTE [rax]\n        and al, (block_mask + container_mask)\n        cmp al, bl\n        jne .false\n\n        ; Is a list, return true\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n        \n.false:\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n\n;; Return true if vector or list\ncore_sequentialp:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .false              ; Should be a pointer\n\n        mov rax, [rsi + Cons.car]\n        mov al, BYTE [rax]\n        and al, (block_mask + container_mask)\n        cmp al, container_list\n        je .true\n        cmp al, container_vector\n        jne .false\n.true:\n        ; Is a list or vector, return true\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n        \n.false:\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n\n\n        \n;; Test if the given list, vector or map is empty\ncore_emptyp:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .error              ; Expected a container\n        mov rax, [rsi + Cons.car]\n        mov al, BYTE [rax]\n        cmp al, maltype_empty_list\n        je .true\n        cmp al, maltype_empty_vector\n        je .true\n        cmp al, maltype_empty_map\n        je .true\n\n        ; false\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n.true:\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n.error:\n        push rsi\n        print_str_mac error_string\n        print_str_mac core_emptyp_error_string\n        pop rsi\n        jmp error_throw\n\n;; Count the number of elements in given list or vector\ncore_count:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        \n        cmp al, content_nil\n        je .zero\n        \n        cmp al, content_pointer\n        jne .error              ; Expected a container\n\n        mov rsi, [rsi + Cons.car]\n        mov al, BYTE [rsi]\n        \n        mov ah, al\n        and ah, (block_mask + container_mask)\n        cmp ah, (block_cons + container_list)\n        je .start_count\n        cmp ah, (block_cons + container_vector)\n        je .start_count\n        \n        jmp .error              ; Not a list or vector\n        \n.start_count:\n        \n        xor rbx,rbx\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_empty\n        je .done                ; Empty list or vector\n\n.loop:\n        inc rbx\n\n        ; Check if there's another\n        mov al, [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n\n        mov rsi, [rsi + Cons.cdr]\n        jmp .loop\n\n.zero:                          ; Return zero count\n        mov rbx, 0\n.done:                          ; Count is in RBX\n\n        push rbx\n        call alloc_cons\n        pop rbx\n        mov [rax], BYTE maltype_integer\n        mov [rax + Cons.car], rbx\n        ret\n        \n.error:\n        push rsi\n        print_str_mac error_string\n        print_str_mac core_count_error_string\n        pop rsi\n        jmp error_throw\n        \n        \n;; Given a map, returns a list of keys\n;; Input: List in RSI with one Map element\n;; Returns: List in RAX\ncore_keys:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_map\n        \n        mov rsi, [rsi + Cons.car]\n        call map_keys\n        ret\n.not_map:\n        load_static core_keys_not_map\n        jmp core_throw_str\n        \n;; Get a list of values from a map\ncore_vals:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_map\n        \n        mov rsi, [rsi + Cons.car]\n        call map_vals\n        ret\n.not_map:\n        load_static core_vals_not_map\n        jmp core_throw_str\n        \n;; Given a map and a key, return true if the key is in the map\n;;\ncore_containsp:\n        ; Check the type of the first argument\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .not_map\n\n        mov rcx, [rsi + Cons.car]  ; Map in RCX\n        mov bl, BYTE [rcx]\n        and bl, (block_mask + container_mask)\n        cmp bl, container_map\n        jne .not_map\n        \n        ; Check second argument\n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .no_key\n        mov rsi, [rsi + Cons.cdr]\n        mov dl, BYTE [rsi]\n        and dl, content_mask\n        cmp dl, content_pointer\n        jne .key_value\n\n        ; Pointer, so put into RDI\n        mov rdi, [rsi + Cons.car]\n        jmp .find\n        \n.key_value:\n        ; A value\n        mov [rsi], BYTE dl\n        mov rdi, rsi            ; Value in RDI\n        \n.find:\n        mov rsi, rcx            ; Map\n        call map_find\n        je .true\n\n        ; false\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n.true:\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n        \n.not_map:\n        load_static core_containsp_not_map\n        jmp core_throw_str\n.no_key:\n        load_static core_containsp_no_key\n        jmp core_throw_str\n\n\n;; Given a map and a key, return the value in the map\n;; or nil if not found\n;;\ncore_get:\n        ; Check the type of the first argument\n        mov bl, BYTE [rsi]\n        \n        and bl, content_mask\n        \n        cmp bl, content_nil\n        je .not_found\n        \n        cmp bl, content_pointer\n        jne .not_map\n        \n        mov rcx, [rsi + Cons.car]  ; Map in RCX\n        mov bl, BYTE [rcx]\n        and bl, (block_mask + container_mask)\n        cmp bl, container_map\n        jne .not_map\n        \n        ; Check second argument\n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .no_key\n        mov rsi, [rsi + Cons.cdr]\n        \n        mov dl, BYTE [rsi]\n        and dl, content_mask\n        cmp dl, content_pointer\n        jne .key_value\n\n        ; Pointer, so put into RDI\n        mov rdi, [rsi + Cons.car]\n        jmp .find\n        \n.key_value:\n        ; A value\n        mov [rsi], BYTE dl\n        mov rdi, rsi            ; Value in RDI\n        \n.find:\n        mov rsi, rcx            ; Map\n        call map_get            ; Value in RAX\n        je .found\n\n.not_found:\n        ; Not found\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n.found:\n        ret\n        \n.not_map:\n        load_static core_get_not_map\n        jmp core_throw_str\n.no_key:\n        load_static core_get_no_key\n        jmp core_throw_str\n\n        \n;; Return arguments as a list\n;; \ncore_list:\n        call incref_object\n        mov rax, rsi\n        ret\n        \n;; Convert arguments into a vector\ncore_vector:\n        ; Copy first element and mark as vector\n        call alloc_cons         ; in RAX\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        mov bh, bl              ; store content for comparison\n        or bl, container_vector\n        mov [rax], BYTE bl      ; Set type\n        \n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx ; Set content\n\n        ; Check if the first element is a pointer\n        cmp bh, content_pointer\n        jne .done_car\n        \n        ; A pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n\n.done_car:\n        ; Copy the CDR type and content\n        mov bl, [rsi + Cons.typecdr]\n        mov [rax + Cons.typecdr], bl\n\n        mov rdx, [rsi + Cons.cdr]\n        mov [rax + Cons.cdr], rdx\n        \n        cmp bl, content_pointer\n        jne .done\n\n        ; A pointer\n        mov bx, WORD [rdx + Cons.refcount]\n        inc bx\n        mov [rdx + Cons.refcount], WORD bx\n        \n.done:\n        ret\n\n        \n;; Convert arguments into a map\ncore_hashmap:\n        ; Copy first element and mark as map\n        call alloc_cons         ; in RAX\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        mov bh, bl              ; store content for comparison\n        or bl, container_map\n        mov [rax], BYTE bl      ; Set type\n        \n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx ; Set content\n\n        ; Check if the first element is a pointer\n        cmp bh, content_pointer\n        jne .done_car\n        \n        ; A pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n\n.done_car:\n        ; Copy the CDR type and content\n        mov bl, [rsi + Cons.typecdr]\n        mov [rax + Cons.typecdr], bl\n\n        mov rdx, [rsi + Cons.cdr]\n        mov [rax + Cons.cdr], rdx\n        \n        cmp bl, content_pointer\n        jne .done\n\n        ; A pointer\n        mov bx, WORD [rdx + Cons.refcount]\n        inc bx\n        mov [rdx + Cons.refcount], WORD bx\n        \n.done:\n        ret\n        \n;; ------------------------------------------------\n;; String functions\n\n;; Convert arguments to a readable string, separated by a space\n;; \ncore_pr_str:\n        mov rdi, 3              ; print_readably & separator\n        jmp core_str_functions\ncore_str:\n        xor rdi, rdi\n        jmp core_str_functions\ncore_str_sep:\n        mov rdi, 2              ; separator\n        \ncore_str_functions:\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_empty\n        je .empty              ; Nothing to print\n\n        xor r8, r8              ; Return string in r8\n        \n.loop:\n        cmp ah, content_pointer\n        je .got_pointer\n        \n        ; A value. Remove list container\n        xchg ah, al\n        mov [rsi], BYTE al\n        xchg ah, al\n        push rsi\n        push rax\n        push r8\n        call pr_str\n        pop r8\n        pop rbx\n        pop rsi\n        mov [rsi], BYTE bl      ; restore type\n        jmp .got_string\n        \n.got_pointer:\n        push rsi\n        push r8\n        mov rsi, [rsi + Cons.car] ; Address pointed to\n        call pr_str\n        pop r8\n        pop rsi\n        \n.got_string:\n        ; String now in rax\n        \n        cmp r8, 0\n        jne .append\n\n        ; first string. Since this string will be\n        ; appended to, it needs to be a copy\n        push rsi                ; input\n        \n        push rax                ; string to copy\n        mov rsi, rax\n        call string_copy        ; New string in RAX\n        pop rsi                 ; copied string\n        \n        push rax                ; the copy\n        call release_object     ; release the copied string\n        pop r8                  ; the copy\n\n        pop rsi                 ; input\n        \n        jmp .next\n        \n.append:\n        push r8\n        push rsi\n        push rax\n        \n        mov rsi, r8             ; Output string \n        mov rdx, rax            ; String to be copied\n        call string_append_string\n        \n        pop rsi                 ; Was in rax, temporary string\n        call release_array      ; Release the string\n\n        pop rsi                 ; Restore input\n        pop r8                  ; Output string\n.next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n\n        ; More inputs\n        mov rsi, [rsi + Cons.cdr] ; pointer\n\n        test rdi, 2             ; print_readably\n        jz .end_append_char     ; No separator\n        \n        ; Add separator\n        push r8\n        push rsi\n        mov rsi, r8\n        mov cl, ' '\n        call string_append_char\n        pop rsi\n        pop r8\n.end_append_char:\n        \n        ; Get the type in ah for comparison at start of loop\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        \n        jmp .loop\n.done:\n        ; No more input, so return\n        mov rax, r8\n        ret\n        \n.empty:\n        call string_new         ; An empty string\n        ret\n        \n;; Print arguments readably, return nil\ncore_prn:\n        call core_pr_str\n        jmp core_prn_functions\ncore_println:\n        call core_str_sep\ncore_prn_functions:\n        mov rsi, rax\n\n        ; Put newline at the end\n        push rsi\n        mov cl, 10              ; newline\n        call string_append_char\n        pop rsi\n        \n        ; print the string\n        push rsi                ; Save the string address\n        call print_string\n        pop rsi\n        call release_array      ; Release the string\n\n        ; Return nil\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n\n;; Given a string, calls read_str to get an AST\ncore_read_string:\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        jne .no_string\n        \n        mov rsi, [rsi + Cons.car]\n        mov al, BYTE [rsi]\n        cmp al, maltype_string\n        jne .no_string\n        \n        call read_str\n        ret\n        \n.no_string:\n        ; Didn't get a string input\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n        \n\n;; Reads a file into a string\ncore_slurp:\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        jne .no_string\n        \n        mov rsi, [rsi + Cons.car]\n        mov al, BYTE [rsi]\n        cmp al, maltype_string\n        jne .no_string\n\n        call read_file\n        ret\n        \n.no_string:\n        ; Didn't get a string input\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n\n;; Evaluate an expression in the REPL environment\n;;\ncore_eval:\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .pointer\n\n        ; Just a value, so return it\n        call incref_object\n        \n        mov al, BYTE [rsi]\n        and al, content_mask\n        mov [rsi], BYTE al      ; Removes list\n        mov rax, rsi\n        ret\n        \n.pointer:\n        ; A pointer, so need to eval\n        mov rdi, [rsi + Cons.car]\n        \n        mov rsi, [repl_env]     ; Environment\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        ret\n\n;; Create an atom\ncore_atom:\n        push rsi\n        call alloc_cons         ; To hold the pointer\n        pop rsi\n        mov [rax], BYTE maltype_atom\n\n        ; Check the type of the first argument\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        je .pointer\n\n        ; A value\n        \n        ; make a copy\n        push rax\n        push rsi\n        push rbx\n        call alloc_cons\n        pop rbx\n\n        mov bl, bh\n        mov [rax], BYTE bl      ; Set type\n        \n        mov rbx, rax\n        pop rsi\n        pop rax\n        \n        mov rcx, [rsi + Cons.car]\n        mov [rbx + Cons.car], rcx ; Set value\n        \n        ; Set the atom to point to it\n        mov [rax + Cons.car], rbx\n        \n        ret\n        \n.pointer:\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        push rax\n        mov rsi, rbx\n        call incref_object      ; Storing in atom\n        pop rax\n        ret\n\n;; Get the value from the atom\ncore_deref:\n        ; Check the type of the first argument\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        jne .not_atom\n\n        ; Get the atom\n        mov rsi, [rsi + Cons.car]\n        mov bl, BYTE [rsi]\n        cmp bl, maltype_atom\n        jne .not_atom\n\n        ; Return what it points to\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.not_atom:\n        ; Not an atom, so throw an error\n        mov rsi, core_deref_not_atom\n        mov edx, core_deref_not_atom.len\n        call raw_to_symbol\n        mov rsi, rax\n        jmp error_throw\n\n;; Test if given object is an atom\ncore_atomp:\n        mov al, maltype_atom\n        jmp core_pointer_type_p\ncore_symbolp:\n        mov al, maltype_symbol\n        jmp core_pointer_type_p\ncore_stringp:\n        mov al, maltype_string\n        jmp core_pointer_type_p\ncore_fnp:\n        mov al, maltype_function\n        jmp core_pointer_type_p\ncore_macrop:\n        mov al, maltype_macro\n        jmp core_pointer_type_p\n        \ncore_pointer_type_p:\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        jne .false\n\n        mov rsi, [rsi + Cons.car]\n        mov bl, BYTE [rsi]\n        cmp bl, al\n        jne .false\n\n        ; Check for keyword (not symbol)\n        cmp al, maltype_symbol\n        jne .true\n\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .false               ; a keyword\n        \n.true:\n        ; Return true\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n\n.false:\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n\n;; Tests if argument is a keyword\ncore_keywordp:\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        jne .false\n\n        mov rsi, [rsi + Cons.car]\n        mov bl, BYTE [rsi]\n        cmp bl, maltype_symbol\n        jne .false\n\n        ; Check if first character is ':'\n        mov bl, BYTE [rsi + Array.data]\n        cmp bl, ':'\n        jne .false\n\n        ; Return true\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n\n.false:\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n\n;; Change the value of an atom\ncore_reset:\n        ; Check the type of the first argument\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        jne .not_atom\n\n        ; Get the atom\n        mov rax, [rsi + Cons.car] ; Atom in RAX\n        mov bl, BYTE [rax]\n        cmp bl, maltype_atom\n        jne .not_atom\n\n        ; Get the next argument\n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .no_value\n        \n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Got something in RSI\n        ; release the current value of the atom\n        push rax\n        push rsi\n\n        mov rsi, [rax + Cons.car] ; The value the atom points to\n        call release_object\n\n        pop rsi\n        pop rax\n        \n        ; Check the type of the first argument\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        je .pointer\n\n        ; A value\n        \n        ; make a copy\n        push rax\n        push rsi\n        push rbx\n        call alloc_cons\n        pop rbx\n\n        mov bl, bh\n        mov [rax], BYTE bl      ; Set type\n        \n        mov rbx, rax\n        pop rsi\n        pop rax\n        \n        mov rcx, [rsi + Cons.car]\n        mov [rbx + Cons.car], rcx ; Set value\n        \n        ; Set the atom to point to it\n        mov [rax + Cons.car], rbx\n        \n        ; Increment refcount since return value will be released\n        mov rsi, rbx\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.pointer:\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        mov rsi, rbx\n        call incref_object      ; Storing in atom\n        call incref_object      ; Returning\n        mov rax, rsi\n        ret\n        \n.not_atom:\n        ; Not an atom, so throw an error\n        mov rsi, core_reset_not_atom\n        mov edx, core_reset_not_atom.len\n        call raw_to_symbol\n        mov rsi, rax\n        jmp error_throw\n\n.no_value:\n        ; No value given\n        mov rsi, core_reset_no_value\n        mov edx, core_reset_no_value.len\n        call raw_to_symbol\n        mov rsi, rax\n        jmp error_throw\n\n;; Applies a function to an atom, along with optional arguments\n;;\n;; In RSI should be a list consisting of\n;;  [ atom, pointer->Function , args...]\n;;\n;; The atom is dereferenced, and inserted into the list:\n;; \n;;  [ pointer->Function , atom value , args...]\n;;\n;; This is then passed to eval.list_exec\n;; which executes the function\n;;\ncore_swap:\n        ; Check the type of the first argument (an atom)\n        mov bl, BYTE [rsi]\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_pointer\n        jne .not_atom\n\n        ; Get the atom\n        mov r9, [rsi + Cons.car] ; Atom in R9\n        mov bl, BYTE [r9]\n        cmp bl, maltype_atom\n        jne .not_atom\n\n        ; Get the second argument (a function)\n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .no_function\n\n        mov rsi, [rsi + Cons.cdr] ; List with function first\n\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .no_function\n\n        mov r8, [rsi + Cons.car] ; Function in R8\n        mov al, BYTE [r8]\n        cmp al, maltype_function\n        jne .no_function\n        \n        ; Get a new Cons \n        ; containing the value in the atom\n        call alloc_cons         ; In RAX\n\n        ; Prepend to the list\n        mov bl, BYTE [rsi + Cons.typecdr]\n        mov [rax + Cons.typecdr], bl\n        cmp bl, content_pointer\n        jne .done_prepend\n\n        ; A pointer to more args,\n\n        mov rcx, [rsi + Cons.cdr]\n        mov [rax + Cons.cdr], rcx\n        \n        ; increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.done_prepend:\n        \n        ; Now get the value in the atom\n        mov rdx, [r9 + Cons.car] ; The object pointed to\n        \n        ; Check what it is\n        mov bl, BYTE [rdx]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        jz .atom_value         ; Just a value\n\n        ; Not a simple value, so point to it\n        mov [rax + Cons.car], rdx\n        mov [rax], BYTE (container_list + content_pointer)\n\n        ; Since the list will be released after eval\n        ; we need to increment the reference count\n        mov bx, WORD [rdx + Cons.refcount]\n        inc bx\n        mov [rdx + Cons.refcount], WORD bx\n        \n        jmp .run\n        \n.atom_value:\n        ; Copy the value\n        mov rcx, [rdx + Cons.car]\n        mov [rax + Cons.car], rcx\n        and bl, content_mask    ; keep just the content\n        or bl, container_list   ; mark as part of a list\n        mov [rax], BYTE bl\n\n.run:\n        mov rsi, rax\n        \n        ; Here have function in R8, args in RSI\n        ; Check whether the function is built-in or user\n        mov rax, [r8 + Cons.car]\n        cmp rax, apply_fn\n        je .user_function\n        \n        ; A built-in function\n        push r9                 ; atom\n        push rsi                ; Args\n        \n        call rax\n        ; Result in RAX\n\n        pop rsi\n        pop r9\n\n        push rax\n        call release_object     ; Release arguments\n        pop rax\n        \n        jmp .got_return\n        \n.user_function:\n        ; a user-defined function, so need to evaluate\n        ; RSI - Args\n        \n        mov rdi, r8             ; Function in RDI\n        mov rdx, rsi            ; Release args after binding\n\n        mov rsi, r15            ; Environment\n        call incref_object      ; Released by eval\n        call incref_object      ; also released from R13\n        mov r13, r15\n\n        mov rsi, rdx\n        \n        push r9\n        call apply_fn           ; Result in RAX\n        pop r9\n        \n.got_return:\n        ; Have a return result in RAX\n        \n        ; release the current value of the atom\n        push rax                ; The result\n        mov rsi, [r9 + Cons.car]\n        call release_object\n        pop rax\n        \n        ; Put into atom\n        mov [r9 + Cons.car], rax\n        \n        ; Increase reference of new object\n        ; because when it is returned it will be released\n        mov bx, WORD [rax + Cons.refcount]\n        inc bx\n        mov [rax + Cons.refcount], WORD bx\n        \n        ret\n        \n.not_atom:\n        load_static core_swap_not_atom\n        jmp core_throw_str\n.no_function:\n        load_static core_swap_no_function\n        jmp core_throw_str\n\n\n;; Takes two arguments, and prepends the first argument onto the second\n;; The second argument can be a list or a vector, but the return is always\n;; a list\ncore_cons:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_empty\n        je .missing_args\n\n        mov r8, rsi             ; The object to prepend\n\n        ; Check if there's a second argument\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .missing_args\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check that the second argument is a list or vector\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_vector\n        \n        mov r9, [rsi + Cons.car] ; Should be a list or vector\n        mov al, BYTE [r9]\n        and al, container_mask\n        cmp al, container_list\n        je .got_args\n        cmp al, container_vector\n        je .got_args\n        jmp .not_vector\n        \n.got_args:\n        ; Got an object in R8 and list/vector in R9\n        \n        call alloc_cons         ; new Cons in RAX\n\n        ; Mark as the same content in a list container\n        mov bl, BYTE [r8]\n        and bl, content_mask\n        mov bh, bl              ; Save content in BH for checking if pointer later\n        or bl, block_cons + container_list\n        mov [rax], BYTE bl\n        \n        ; Copy the content\n        mov rcx, [r8 + Cons.car] ; Content in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check if R9 is empty\n        mov dl, BYTE [r9]\n        and dl, content_mask\n        cmp dl, content_empty\n        je .end_append          ; Don't append the list\n        \n        ; Put the list into CDR\n        mov [rax + Cons.cdr], r9\n        ; mark CDR as a pointer\n        mov [rax + Cons.typecdr], BYTE content_pointer\n\n        ; Increment reference count\n        push rax\n        mov rsi, r9\n        call incref_object\n        pop rax\n        \n.end_append:\n        ; Check if the new Cons contains a pointer\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .done\n\n        ; A pointer, so increment number of references\n        push rax\n        mov rsi, rcx\n        call incref_object\n        pop rax\n.done:\n        ret\n        \n.missing_args:\n        load_static core_cons_missing_arg\n        jmp core_throw_str\n        \n.not_vector:\n        load_static core_cons_not_vector\n        jmp core_throw_str\n\n\n;; Concatenate lists, returning a new list\n;;\n;; Notes:\n;;    * The last list does not need to be copied, but all others do\n;;\ncore_concat:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_empty\n        je .missing_args\n\n        cmp al, content_pointer\n        jne .not_list\n        \n        ; Check if there is only one argument\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        je .start_loop                ; Start copy loop\n\n        ; Only one input.\n        mov rsi, [rsi + Cons.car]\n        \n        ; Check if it's a list or vector\n        mov al, BYTE [rsi]\n        mov cl, al\n        and al, container_mask\n        cmp al, (block_cons + container_list)\n        je .single_list\n\n        cmp al, (block_cons + container_vector)\n        jne .not_list           ; not a list or vector\n\n        ; A vector. Need to create a new Cons\n        ; for the first element, to mark it as a list\n        \n        call alloc_cons\n        and cl, content_mask\n        or cl, container_list\n        mov [rax], BYTE cl      ; Set type\n\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; Set content\n\n        ; Check if CAR is a pointer\n        cmp cl, (container_list + content_pointer)\n        jne .single_done_car\n\n        ; a pointer, so increment reference count\n        mov cx, WORD [rbx + Cons.refcount]\n        inc cx\n        mov [rbx + Cons.refcount], WORD cx\n        \n.single_done_car:\n        mov dl, BYTE [rsi + Cons.typecdr]\n        mov [rax + Cons.typecdr], BYTE dl ; CDR type\n        \n        mov rbx, [rsi + Cons.cdr]\n        mov [rax + Cons.cdr], rbx ; Set CDR content\n\n        ; Check if CDR is a pointer\n        cmp dl, content_pointer\n        je .single_vector_incref\n        ; not a pointer, just return\n        ret\n        \n.single_vector_incref:\n        ; increment the reference count of object pointed to\n        mov r12, rax            ; The return Cons\n        mov rsi, rbx            ; The object address\n        call incref_object\n        mov rax, r12\n        ret\n        \n.single_list:\n        ; Just increment reference count and return\n        \n        call incref_object\n        mov rax, rsi\n        ret\n\n.start_loop:  ; Have at least two inputs\n        xor r11, r11            ; Head of list. Start in R12\n        \n.loop:  \n        \n        ; Check the type\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_list\n        \n        ; Check if this is the last\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .last\n\n        ; Check if the list is empty\n        mov rbx, [rsi + Cons.car] ; The list\n        mov al, BYTE [rbx]\n        and al, content_mask\n        cmp al, content_empty   ; If empty list or vector\n        je .next                ; Skip to next\n        \n        ; not the last list, so need to copy\n\n        push rsi\n        mov rsi, rbx ; The list\n        call cons_seq_copy        ; Copy in RAX, last Cons in RBX\n        pop rsi\n\n        ; Check if this is the first\n        test r11, r11\n        jnz .append\n\n        ; First list\n        mov r11, rbx            ; Last Cons in list\n        mov r12, rax            ; Output list\n        jmp .next\n.append:\n        ; End of previous list points to start of new list\n        mov [r11 + Cons.cdr], rax \n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        ; Put end of new list into R11\n        mov r11, rbx\n        \n.next:\n        mov rsi, [rsi + Cons.cdr]\n        jmp .loop\n\n.last:\n        ; last list, so can just append\n        mov rsi, [rsi + Cons.car]\n\n        ; Check if the list is empty\n        mov al, BYTE [rsi]\n        mov ah, al\n        and al, content_mask\n        cmp al, content_empty   ; If empty list or vector\n        je .done                ; Omit the empty list\n        \n        call incref_object\n        \n        mov [r11 + Cons.cdr], rsi\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n.done:\n        ; Check there is anything to return\n        test r11, r11\n        jz .empty_list\n        \n        ; Make sure that return is a list\n        mov bl, BYTE [r12]\n        and bl, content_mask\n        or bl, container_list\n        mov [r12], BYTE bl\n        mov rax, r12            ; output list\n        \n        ret\n\n.empty_list:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n        \n.missing_args:\n        ; Return empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n        \n.not_list:\n        ; Got an argument which is not a list\n        mov rsi, core_concat_not_list\n        mov edx, core_concat_not_list.len\n        \n.throw:\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n;; Convert a sequence to vector\ncore_vec:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .error\n        mov rsi, [rsi + Cons.car]\n\n        mov al, BYTE [rsi]\n        and al, block_mask + container_mask\n\n        ;; delegate lists to `vector` built-in\n        cmp al, container_list\n        je core_vector\n\n        ;; expect a sequence\n        cmp al, container_vector\n        jne .error\n\n        ;; return vectors unchanged\n        call incref_object\n        mov rax, rsi\n        ret\n\n.error\n        push rsi\n        print_str_mac error_string\n        print_str_mac core_vec_wrong_arg\n        pop rsi\n        jmp error_throw\n\n;; Returns the first element of a list\n;;\ncore_first:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_empty\n        je .missing_args\n\n        cmp al, content_nil\n        je .return_nil\n        \n        cmp al, content_pointer\n        jne .not_list\n        \n        ; Get the list\n        mov rsi, [rsi + Cons.car]\n\n        mov al, BYTE [rsi]\n\n        ; Check for nil\n        cmp al, maltype_nil\n        je .return_nil\n        \n        mov ah, al\n        and ah, (block_mask + container_mask)\n        cmp ah, container_list\n        je .got_list\n        cmp ah, container_vector\n        jne .not_list           ; Not a list or vector\n        \n.got_list:\n        ; Check if list is empty\n        and al, content_mask\n        cmp al, content_empty\n        je .return_nil\n\n        cmp al, content_pointer\n        je .return_pointer\n\n        ; Returning a value, so need to copy\n        mov cl, al\n        call alloc_cons\n        mov [rax], BYTE cl      ; Set type\n\n        ; Copy value\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        ret\n        \n.return_pointer:\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n        \n.missing_args:\n        mov rsi, core_first_missing_arg\n        mov edx, core_first_missing_arg.len\n        jmp .throw\n        \n.not_list:\n        mov rsi, core_first_not_list\n        mov edx, core_first_not_list.len\n.throw:\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n    \n\n;; Return a list with the first element removed\ncore_rest:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_empty\n        je .missing_args\n\n        cmp al, content_nil\n        je .empty_list\n        \n        cmp al, content_pointer\n        jne .not_list\n\n        ; Get the list\n        mov rsi, [rsi + Cons.car]\n        \n        mov al, BYTE [rsi]\n\n        ; Check for nil\n        cmp al, maltype_nil\n        je .return_nil\n        \n        mov ah, al\n        and ah, (block_mask + container_mask)\n        cmp ah, container_list\n        je .got_list\n        cmp ah, container_vector\n        jne .not_list           ; Not a list or vector\n        \n.got_list:\n        ; Check if list or vector is empty\n        and al, content_mask\n        cmp al, content_empty\n        je .empty_list\n\n        ; Check if there is more in the list\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        je .return_rest\n        \n        ; No more list, so return empty list\n.empty_list:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n        \n.return_rest:\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        \n        \n        ; Check if this is a list or a vector\n        mov cl, BYTE [rsi]\n        mov ch, cl\n        and ch, container_mask\n        cmp ch, container_list\n        je .return_list\n\n        ; Need to allocate a new Cons to replace this first element\n        call alloc_cons\n        and cl, content_mask\n        mov ch, cl              ; Save CAR content type in ch\n        or cl, container_list   ; Keep content type, set container type to list\n        mov [rax], BYTE cl\n\n        mov dl, BYTE [rsi + Cons.typecdr] ; CDR type in DL\n        mov [rax + Cons.typecdr], BYTE dl\n        \n        ; Copy content of CAR\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx\n        \n        ; Check if car contains a pointer\n        cmp ch, content_pointer\n        jne .check_cdr\n        \n        ; CAR contains a pointer, so increment reference count\n        \n        mov r8, rax             ; Save return Cons\n        mov r9, rsi             ; Save input list\n        mov rsi, rbx            ; Content of CAR\n        call incref_object\n        mov rax, r8             ; Restore return Cons\n        mov rsi, r9             ; Restore input list\n        \n.check_cdr:\n        ; Copy content of CDR\n        \n        mov rcx, [rsi + Cons.cdr]\n        mov [rax + Cons.cdr], rcx ; Note: Might be pointer\n        \n        ; Check if cdr contains a pointer\n        cmp dl, content_pointer\n        jne .return             ; Not a pointer, so just return\n        \n        ; A pointer, so increment its reference count\n        mov rbx, rax            ; Save the return Cons\n        mov rsi, rcx            ; The pointer in CDR\n        call incref_object\n        mov rax, rbx            ; Restore the return Cons\n        ret\n        \n.return_list:\n        call incref_object\n        mov rax, rsi\n.return:\n        ret\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n        \n.missing_args:\n        mov rsi, core_rest_missing_arg\n        mov edx, core_rest_missing_arg.len\n        jmp .throw\n        \n.not_list:\n        mov rsi, core_rest_not_list\n        mov edx, core_rest_not_list.len\n.throw:\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n\n;; Return the nth element of a list or vector\ncore_nth:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_empty\n        je .missing_args\n\n        cmp al, content_nil\n        je .return_nil\n        \n        cmp al, content_pointer\n        jne .not_list\n\n        ; Get the list into R8\n        mov r8, [rsi + Cons.car]\n        \n        ; Check if we have a second argument\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .missing_args\n\n        mov r9, [rsi + Cons.cdr]\n        \n        ; Check that it is a number\n        mov al, BYTE [r9]\n        and al, content_mask\n        cmp al, content_int\n        jne .not_int\n\n        ; Get the number in RBX\n        mov rbx, [r9 + Cons.car]\n\n        ; Now loop through the list, moving along n elements\n.loop:\n        test rbx, rbx           ; Test if zero\n        jz .done\n\n        ; Move along next element\n\n        mov al, BYTE [r8 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .out_of_range       ; No element\n\n        mov r8, [r8 + Cons.cdr]\n        dec rbx\n        jmp .loop        \n        \n.done:\n        ; Take the head of the list in R8\n        mov al, BYTE [r8]\n        and al, content_mask\n        cmp al, content_pointer\n        je .return_pointer\n\n        ; Copy a value\n        mov cl, al\n        call alloc_cons\n        mov [rax], BYTE cl\n        mov rcx, [r8 + Cons.car]\n        mov [rax + Cons.car], rcx\n        ret\n        \n.return_pointer:\n        mov rsi, [r8 + Cons.car]\n        call incref_object\n        mov rax, rsi\n        ret\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n        \n.missing_args:\n        mov rsi, core_nth_missing_arg\n        mov edx, core_nth_missing_arg.len\n        jmp .throw\n        \n.not_list:\n        mov rsi, core_nth_not_list\n        mov edx, core_nth_not_list.len\n        jmp .throw\n\n.not_int:\n        mov rsi, core_nth_not_int\n        mov edx, core_nth_not_int.len\n        jmp .throw\n        \n.out_of_range:\n        mov rsi, core_nth_out_of_range\n        mov edx, core_nth_out_of_range.len\n        \n.throw:\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n;; Check if the argument is a given value type\ncore_nilp:\n        mov al, BYTE content_nil\n        jmp core_value_type_p\ncore_truep:\n        mov al, BYTE content_true\n        jmp core_value_type_p\ncore_falsep:\n        mov al, BYTE content_false\n        jmp core_value_type_p\ncore_numberp:\n        mov al, BYTE content_int\n;; predicates for nil, true, false and number jump here\ncore_value_type_p:\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        cmp bl, content_empty\n        je .missing_args\n\n        cmp al, bl\n        je .true\n        \n        ; false\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n.true:\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n        \n.missing_args:\n        mov rsi, core_value_p_missing_args\n        mov edx, core_value_p_missing_args.len\n        \n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n;; Throws an exception\ncore_throw:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_empty\n        je .throw_nil          ; No arguments\n\n        cmp al, content_pointer\n        je .throw_pointer\n\n        ; A value. Remove list content type\n        mov [rsi], BYTE al\n        jmp error_throw\n        \n.throw_pointer:\n        mov rsi, [rsi + Cons.car]\n        jmp error_throw\n        \n.throw_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov rsi, rax\n        jmp error_throw\n        \n;; Applies a function to a list or vector\n;;\n;; Uses registers\n;;    R8  - function\n;;    R9  - Input list/vector\n;;    R10  - Current end of return list (for appending)\ncore_map:\n        xor r10,r10             ; Zero, signal no list\n        \n        ; First argument should be a function\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        cmp bl, content_empty\n        je .missing_args\n\n        ; Check the first argument is a pointer\n        cmp bl, content_pointer\n        jne .not_function\n        \n        mov r8, [rsi + Cons.car] ; Function in R8\n        mov bl, BYTE [r8]\n        cmp bl, maltype_function\n        jne .not_function\n\n        ; Check for second argument\n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .missing_args\n\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Should be a pointer to a list or vector\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .not_seq\n\n        mov r9, [rsi + Cons.car] ; List or vector in R9\n\n        mov bl, BYTE [r9]\n\n        mov bh, bl\n        and bh, content_mask\n        cmp bh, content_empty\n        je .empty_list\n        \n        and bl, (block_mask + container_mask)\n        cmp bl, container_list\n        je .start\n        cmp bl, container_vector\n        je .start\n        \n        ; not list or vector\n        jmp .not_seq\n        \n.start:\n        ; Got function in R8, list or vector in R9\n        \n        mov cl, BYTE [r9]\n        and cl, content_mask\n        mov ch, cl\n        or cl, container_list\n        \n        call alloc_cons\n        mov [rax], BYTE cl      ; set content type\n        mov rbx, [r9 + Cons.car]\n        mov [rax + Cons.car], rbx ; Copy content\n        mov rsi, rax\n\n        cmp ch, content_pointer\n        jne .run\n\n        ; A pointer, so increment ref count\n\n        mov rcx, rsi\n        mov rsi, rbx\n        call incref_object\n        mov rsi, rcx\n        \n.run:\n        ; Here have function in R8, args in RSI\n        ; Check whether the function is built-in or user\n        mov rax, [r8 + Cons.car]\n        cmp rax, apply_fn\n        je .user_function\n        \n        ; A built-in function\n        push r8                 ; function\n        push r9                 ; input list/vector\n        push r10                ; End of return list\n        push rsi\n        \n        call rax\n        ; Result in RAX\n\n        pop rsi\n        pop r10\n        pop r9\n        pop r8\n\n        push rax\n        call release_object     ; Release arguments\n        pop rax\n        \n        jmp .got_return\n        \n.user_function:\n        ; a user-defined function, so need to evaluate\n        ; RSI - Args\n        \n        mov rdi, r8             ; Function in RDI\n        mov rdx, rsi            ; Release args after binding\n\n        mov rsi, r15            ; Environment\n        call incref_object      ; Released by eval\n        call incref_object      ; also released from R13\n        mov r13, r15\n\n        mov rsi, rdx\n        \n        push r8\n        push r9\n        push r10\n        push r15\n        call apply_fn           ; Result in RAX\n        pop r15\n        pop r10\n        pop r9\n        pop r8\n        \n.got_return:\n        ; Have a return result in RAX\n        \n        ; Check if it's a value type\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bl, (block_mask + container_mask)\n        jz .return_value\n\n        ; A more complicated type, point to it\n        mov rcx, rax\n        call alloc_cons         ; Create a Cons for address\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car], rcx\n        jmp .update_return\n        \n.return_value:\n        ; Check if this value is shared (e.g. in an atom)\n        mov cx, WORD [rax + Cons.refcount]\n        dec cx\n        jz .return_value_modify ; If reference count is 1\n        \n        ; Need to copy to avoid modifying\n        push rsi\n        mov rsi, rax            ; Original in RSI\n\n        mov cl, bh              ; Type\n        call alloc_cons\n        and cl, content_mask\n        or cl, container_list\n        mov [rax], BYTE cl      ; mark as a list\n        \n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy content\n        \n        ; Release original\n        push rax\n        call release_object\n        pop rax\n        pop rsi\n        \n        jmp .update_return\n        \n.return_value_modify:\n        ; Only one reference,\n        ; so can change the container type to list.\n        ; Original type in bh\n        mov bl, bh\n        and bl, content_mask\n        or bl, container_list\n        mov [rax], BYTE bl\n        \n.update_return:\n        ; Now append to result list\n        test r10,r10\n        jnz .append\n\n        ; First value\n        mov r10, rax            ; End of list\n        push r10                ; popped before return\n        jmp .next\n.append:\n        mov [r10 + Cons.cdr], rax ; Point to new Cons\n        mov [r10 + Cons.typecdr], BYTE content_pointer\n        mov r10, rax\n.next:\n        ; Check if there is another value\n        mov al, [r9 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done               ; no more \n\n        mov r9, [r9 + Cons.cdr] ; next\n        jmp .start\n        \n.done:\n        pop rax                 ; Pushed in .update_return\n        ret\n        \n.empty_list:\n        ; Got an empty list, so return an empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n        \n.missing_args:\n        ; Either zero or one args, expect two\n        load_static core_map_missing_args\n        jmp core_throw_str\n.not_function:\n        ; First argument not a function\n        load_static core_map_not_function\n        jmp core_throw_str\n.not_seq:\n        ; Second argument not list or vector\n        load_static core_map_not_seq\n        jmp core_throw_str\n        \n\n;; Applies a function to a list of arguments, concatenated with\n;; a final list of args\n;; (function, ..., [])\ncore_apply:\n        ; First argument should be a function\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_function\n\n        mov r8, [rsi + Cons.car] ; function in R8\n        mov al, BYTE [r8]\n        cmp al, maltype_function\n        je .function_or_macro\n        cmp al, maltype_macro\n        jne .not_function\n.function_or_macro:\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .missing_args\n        \n        xor r9,r9\n        ; Optional args, followed by final list/vector\n.loop:\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Check if this is the last\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .last\n\n        ; Not the last, so copy\n        call alloc_cons         ; New Cons in RAX\n        mov bl, BYTE [rsi]\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .got_value\n\n        ; A pointer, so increment reference\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.got_value:\n        ; Now append this Cons to the list\n        test r9,r9\n        jnz .append\n\n        ; First\n        mov r9, rax             ; Start of the list\n        mov r10, rax            ; End of the list\n        jmp .loop\n        \n.append:\n        mov [r10 + Cons.typecdr], BYTE content_pointer\n        mov [r10 + Cons.cdr], rax\n        mov r10, rax\n        jmp .loop\n        \n.last:\n        ; Check that it's a list or vector\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_seq\n        \n        mov rsi, [rsi + Cons.car] ; Vector/list in RSI\n        mov al, BYTE [rsi]\n        and al, container_mask\n        cmp al, container_list\n        je .last_seq\n        cmp al, container_vector\n        jne .not_seq\n        \n.last_seq:\n        ; Check if there were any previous args\n        test r9, r9\n        jnz .last_append\n\n        ; R9 is zero, so no previous args\n\n        ; check that this is a list\n        ; and convert vector to list\n\n        mov r9, rsi\n        \n        ; Check if R9 is a list\n        mov al, BYTE [r9]\n        mov cl, al\n        and al, container_mask\n        cmp al, container_list\n        jne .last_convert_to_list\n\n        ; Already a list, just increment reference count\n        mov rsi, r9\n        call incref_object\n        jmp .run\n\n.last_convert_to_list:\n        ; Convert vector to list by copying first element\n\n        call alloc_cons\n        and cl, content_mask\n        or cl, container_list\n        mov [rax], BYTE cl\n        mov rdx, [r9 + Cons.car]\n        mov [rax + Cons.car], rdx\n        \n        ; check if contains a pointer\n        cmp cl, (container_list + content_pointer)\n        jne .copy_cdr\n        \n        ; A pointer, so increment reference\n        mov bx, WORD [rdx + Cons.refcount]\n        inc bx\n        mov [rdx + Cons.refcount], WORD bx\n        \n.copy_cdr:\n        mov bl, BYTE [r9 + Cons.typecdr]\n        mov rcx, [r9 + Cons.cdr]\n        mov [rax + Cons.typecdr], BYTE bl\n        mov [rax + Cons.cdr], rcx\n\n        ; Replace R9 with this new element\n        mov r9, rax\n        \n        cmp bl, content_pointer\n        jne .run\n\n        ; A pointer, so increment reference\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n        jmp .run\n        \n.last_append:\n        ; Append RSI to the end of the list [R9]...[R10]\n        mov [r10 + Cons.typecdr], BYTE content_pointer\n        mov [r10 + Cons.cdr], rsi\n        call incref_object\n        \n.run:\n        ; Have arguments list in R9\n        mov rsi, r9\n        ; Here have function in R8, args in RSI\n        ; Check whether the function is built-in or user\n        mov rax, [r8 + Cons.car]\n        cmp rax, apply_fn\n        je .user_function\n        \n        ; A built-in function\n        push r8                 ; function\n        push r9                 ; input list/vector\n        push r10                ; End of return list\n        push rsi\n        \n        call rax\n        ; Result in RAX\n\n        pop rsi\n        pop r10\n        pop r9\n        pop r8\n\n        push rax\n        call release_object     ; Release arguments\n        pop rax\n        \n        ret\n        \n.user_function:\n        ; a user-defined function, so need to evaluate\n        ; RSI - Args\n        \n        mov rdi, r8             ; Function in RDI\n        mov rdx, rsi            ; Release args after binding\n\n        mov rsi, r15            ; Environment\n        call incref_object      ; Released by eval\n        call incref_object      ; also released from R13\n        mov r13, r15\n\n        mov rsi, rdx\n        \n        push r8\n        push r9\n        push r10\n        call apply_fn           ; Result in RAX\n        pop r10\n        pop r9\n        pop r8\n        \n        ret\n                \n.not_function:\n        load_static core_apply_not_function\n        jmp core_throw_str\n\n.missing_args:\n        load_static core_apply_missing_args\n        jmp core_throw_str\n\n.not_seq:\n        load_static core_apply_not_seq\n        jmp core_throw_str\n\n;; Converts a string to a symbol\ncore_symbol:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_string\n\n        mov rsi, [rsi + Cons.car]\n        mov al, BYTE [rsi]\n        cmp al, maltype_string\n        jne .not_string\n\n        ; Copy the string\n        call string_copy        ; result in RAX\n\n        mov [rax], BYTE maltype_symbol\n        ret\n        \n.not_string:\n        load_static core_symbol_not_string\n        jmp core_throw_str\n\n;; Converts a string to a keyword\ncore_keyword:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .error\n\n        mov r8, [rsi + Cons.car] ; String in R8\n        mov al, BYTE [r8]\n        cmp al, maltype_string\n        jne .not_string\n\n        call string_new         ; String in RAX\n        mov rsi, rax\n        mov cl, ':'\n        call string_append_char ; Puts ':' first\n        \n        mov rdx, r8\n        call string_append_string ; append\n\n        ; Mark as keyword\n        mov [rsi], BYTE maltype_symbol\n        \n        mov rax, rsi\n        ret\n        \n.not_string:\n        cmp al, maltype_symbol\n        jne .error\n        ; Check if first character is ':'\n        mov al, BYTE [r8 + Array.data]\n        cmp al, ':'\n        jne .error\n        ;; This is already a keyword, return it unchanged.\n        mov rsi, r8\n        call incref_object\n        mov rax, rsi\n        ret\n.error:\n        load_static core_keyword_not_string\n        jmp core_throw_str\n\n;; Sets values in a map\ncore_assoc:\n        ; check first arg\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_map\n        \n        mov r8, [rsi + Cons.car] ; map in R8\n        mov al, BYTE [r8]\n        and al, container_mask\n        cmp al, container_map\n        jne .not_map\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        je .start\n\n        ; No keys to set, so just increment and return\n        mov rsi, r8\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.start:\n        mov r11, [rsi + Cons.cdr] ; List of keys/values in R11\n\n        ; Copy the original list\n        mov rsi, r8\n        call map_copy\n        mov rsi, rax            ; new map in RSI\n\n.loop:\n        ; Get key then value from R11 list\n\n        mov cl, BYTE [r11]\n        and cl, content_mask\n        cmp cl, content_pointer\n        je .key_pointer\n\n        ; Key is a value, so copy into a Cons\n        call alloc_cons\n        mov [rax], BYTE cl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n        mov rdi, rax            ; Key in RDI\n        jmp .get_value\n        \n.key_pointer:\n        mov rdi, [r11 + Cons.car]\n        ; increment reference count because the key will be\n        ; released after setting (to allow value Cons to be\n        ; freed)\n\n        mov bx, WORD [rdi + Cons.refcount]\n        inc bx\n        mov [rdi + Cons.refcount], WORD bx\n        \n.get_value:\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .missing_value\n\n        mov r11, [r11 + Cons.cdr]\n\n        ; Check if value is a pointer\n        mov cl, BYTE [r11]\n        and cl, content_mask\n        cmp cl, content_pointer\n        je .value_pointer\n        \n        ; Value is a value, so copy into a Cons\n        call alloc_cons\n        mov [rax], BYTE cl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n        mov rcx, rax            ; Key in RCX\n        jmp .set_pair\n\n.value_pointer:\n        mov rcx, [r11 + Cons.car]\n        ; increment reference count because the value will be\n        ; released after setting (to allow value Cons to be\n        ; freed)\n\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.set_pair:\n        ; Here have:\n        ; map in RSI\n        ; key in RDI\n        ; value in RCX\n        \n        call map_set\n\n        mov r8, rsi             ; map\n        mov rsi, rdi            ; key\n        call release_object\n        mov rsi, rcx            ; value\n        call release_object\n        mov rsi, r8             ; map\n\n        ; Check if there's another pair\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n        \n        ; got another pair\n        mov r11, [r11 + Cons.cdr]\n        jmp .loop\n        \n.done:\n        mov rax, rsi            ; new map\n        ret\n        \n.not_map:\n        load_static core_assoc_not_map\n        jmp core_throw_str\n        \n.missing_value:\n        load_static core_assoc_missing_value\n        jmp core_throw_str\n\n\n;; Removes keys from a map by making\n;; a copy of a map without the given keys\ncore_dissoc:\n        ; Check that the first argument is a map\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .not_map\n\n        mov r8, [rsi + Cons.car] ; Map in R8\n        mov al, BYTE [r8]\n        mov ah, al\n        and al, container_mask\n        cmp al, container_map\n        jne .not_map\n\n        ; Check if the map is empty\n        cmp ah, maltype_empty_map\n        je .inc_and_return\n        \n        ; Now check if there are other arguments\n\n        mov al, [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        je .start\n\n.inc_and_return:\n        ; No keys to remove\n        ; just increment the map reference count and return\n        mov rsi, r8\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.start:\n        ; Some keys to remove\n        mov r9, [rsi + Cons.cdr]\n\n        ; R9 now contains a list of keys\n        ; R8 contains the map to copy\n\n        xor r11, r11            ; Head of list to return\n        ; R12 contains tail\n        \n.loop:  \n        ; Check the key in R8 against the list in R9\n        mov r10, r9             ; point in list being searched\n\n        ; loop through the list in R10\n        ; comparing each element against R8\n.search_loop:\n        mov rsi, r8\n        mov rdi, r10\n        call compare_objects\n        test rax, rax\n        jz .found               ; objects are equal\n        \n        ; Not found so check next in list\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .not_found          ; End of list\n        \n        mov r10, [r10 + Cons.cdr] ; next\n        jmp .search_loop\n        \n.found:\n        ; Removing this key, so skip\n        mov al, BYTE [r8 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .missing_value\n        \n        mov r8, [r8 + Cons.cdr] ; now a value\n        jmp .next\n        \n.not_found:\n        ; Key not in list, so keeping\n        ; Create a Cons to copy\n        call alloc_cons\n        mov bl, [r8]\n        mov rcx, [r8 + Cons.car]\n\n        mov [rax], BYTE bl\n        mov [rax + Cons.car], rcx\n        \n        ; Check if a pointer or value\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .done_key           ; A value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.done_key:\n        ; append to list\n\n        test r11, r11\n        jnz .key_append\n\n        ; First one\n        mov r11, rax\n        mov r12, rax\n        jmp .copy_value\n.key_append:\n        \n        mov [r12 + Cons.typecdr], BYTE content_pointer\n        mov [r12 + Cons.cdr], rax\n        mov r12, rax\n\n.copy_value:\n\n        ; Check there is a value\n        mov al, BYTE [r8 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .missing_value\n\n        mov r8, [r8 + Cons.cdr] ; Value\n\n        ; Same as for key; create a Cons and copy\n        call alloc_cons\n        mov bl, [r8]\n        mov rcx, [r8 + Cons.car]\n\n        mov [rax], BYTE bl\n        mov [rax + Cons.car], rcx\n        \n        ; Check if a pointer or value\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .done_value           ; A value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n\n.done_value:\n        ; append to list\n        mov [r12 + Cons.typecdr], BYTE content_pointer\n        mov [r12 + Cons.cdr], rax\n        mov r12, rax\n        \n.next:\n        ; Here R8 contains a value\n        \n        ; Check if there's another key\n        mov al, [r8 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n\n        ; Still more\n\n        mov r8, [r8 + Cons.cdr]\n        jmp .loop\n        \n.done:\n        ; Check if the map is empty\n        test r11, r11\n        jz .return_empty\n        \n        ; not empty, so return\n        mov rax, r11\n        ret\n\n.return_empty:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_map\n        ret\n        \n.not_map:\n        load_static core_dissoc_not_map\n        jmp core_throw_str\n\n.missing_value:\n        load_static core_dissoc_missing_value\n        jmp core_throw_str\n\n\n;; Takes a string prompt for the user, and returns\n;; a string or nil\ncore_readline:\n        ; Check the input \n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .no_prompt\n\n        mov rsi, [rsi + Cons.car]\n        mov al, BYTE [rsi]\n        cmp al, maltype_string\n        jne .no_prompt\n\n        ; Got a string in RSI\n        call print_string\n\n.no_prompt:\n\n        ; Get string from user\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .return_nil\n\n        ; return the string in RAX\n        ret\n        \n.return_nil:\n        ; release string in RAX\n        mov rsi, rax\n        call release_array\n\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n\n\n;; Return the meta data associated with a given function\ncore_meta:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .return_nil\n\n        mov rsi, [rsi + Cons.car]\n        mov al, BYTE [rsi]\n        cmp al, (block_cons + container_function + content_function)\n        jne .return_nil\n\n        ; Here got a function\n        mov rsi, [rsi + Cons.cdr]\n\n        ; RSI should now contain the meta data\n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        cmp cl, content_pointer\n        je .pointer\n\n        ; A value, so copy\n        call alloc_cons\n        mov [rax], BYTE cl\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx\n        ret\n        \n.pointer:\n        ; A pointer, so increment reference count and return\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n        \n\n;; Associates a value with a function (native or user)\ncore_with_meta:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .no_function\n\n        mov r8, [rsi + Cons.car] ; Function in R8\n        mov al, BYTE [r8]\n        cmp al, (block_cons + container_function + content_function)\n        jne .no_function\n        \n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .no_value\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        ; Function in R8, new value in RSI\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)      ; Type\n        mov rbx, [r8 + Cons.car]\n        mov [rax + Cons.car], rbx ; Function address\n        \n        mov r10, rax            ; Return address\n\n        ; Copy the meta data\n\n        mov r8, [r8 + Cons.cdr] ; R8 now old meta data (not used)\n        \n        call alloc_cons\n        \n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        mov ch, cl\n        or cl, container_function\n        mov [rax], BYTE cl      ; Set type\n\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; Copy value\n\n        ; append to function\n        mov [r10 + Cons.typecdr], BYTE content_pointer\n        mov [r10 + Cons.cdr], rax\n        mov r11, rax\n        \n        ; Check if meta is a value or pointer\n        cmp ch, content_pointer\n        jne .copy_rest\n\n        ; increment reference count of meta\n        mov cx, WORD [rbx + Cons.refcount]\n        inc cx\n        mov [rbx + Cons.refcount], WORD cx\n        \n.copy_rest:\n        ; Copy remainder of function (if any)\n        ; If a user function, has (env binds body)\n        mov al, [r8 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n\n        ; Still more to copy\n        mov r8, [r8 + Cons.cdr]\n        \n        call alloc_cons\n        mov bl, BYTE [r8]\n        mov [rax], BYTE bl      ; Copy type\n        mov rcx, [r8 + Cons.car]\n        mov [rax + Cons.car], rcx ; Copy value\n\n        ; append\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        mov [r11 + Cons.cdr], rax\n        mov r11, rax\n\n        ; Check if it's a pointer\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .copy_rest\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        jmp .copy_rest\n        \n.done:\n        mov rax, r10\n        ret\n        \n.no_function:\n        load_static core_with_meta_no_function\n        jmp core_throw_str\n\n.no_value:\n        load_static core_with_meta_no_value\n        jmp core_throw_str\n\n\n;; Returns the current time in ms\ncore_time_ms:\n        call clock_time_ms\n        mov rsi, rax\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_integer\n        mov [rax + Cons.car], rsi\n        ret\n\n;; Convert sequences, including strings, into lists\ncore_seq:\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        je .pointer\n        \n        cmp al, content_empty\n        je .missing_arg\n\n        cmp al, content_nil\n        jne .wrong_type\n        \n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        ret\n        \n.pointer:\n        mov r8, [rsi + Cons.car]\n        mov al, BYTE [r8]\n\n        cmp al, maltype_string\n        je .string\n\n        mov ah, al\n        and ah, (block_mask + content_mask)\n        cmp ah, (block_cons + content_empty)\n        je .return_nil\n        \n        and al, (block_mask + container_mask)\n        \n        cmp al, (block_cons + container_list)\n        je .list\n        \n        cmp al, (block_cons + container_vector)\n        jne .wrong_type\n\n        ; Convert vector to list by replacing the first Cons\n        call alloc_cons\n        mov bl, BYTE [r8]\n        and bl, content_mask\n        or bl, container_list\n        mov [rax], BYTE bl      ; Set type\n\n        mov rcx, [r8 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        ; Check if it's a pointer\n        cmp bl, (container_list + content_pointer)\n        jne .copy_cdr\n\n        ; Increment reference count\n        mov bx, WORD [rcx + Cons.refcount] ; Same for Array\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.copy_cdr:\n        mov rcx, [r8 + Cons.cdr]\n        mov [rax + Cons.cdr], rcx\n\n        mov bl, [r8 + Cons.typecdr]\n        mov [rax + Cons.typecdr], bl\n\n        cmp bl, content_pointer\n        jne .return\n        \n        ; Increment reference count\n        mov bx, WORD [rcx + Cons.refcount] ; Same for Array\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.return:\n        ret\n        \n.list:\n        ; Return list unchanged\n        mov rsi, r8\n        call incref_object\n        mov rax, r8\n        ret\n\n.string:\n        ; Split a string into characters\n        ; Input string in R8\n        \n        mov ebx, DWORD [r8 + Array.length]\n        test ebx,ebx\n        jz .return_nil          ; empty string\n\n        ; Not empty, so allocate first Cons\n        call alloc_cons\n        mov r9, rax             ; Return Cons in R9\n        mov r10, rax            ; End of list in R10\n\n.loop:\n        mov ebx, DWORD [r8 + Array.length]\n        mov r11, r8\n        add r11, Array.data     ; Start of string data in R11\n        mov r12, r11\n        add r12, rbx            ; End of string data in R12\n        \n.inner_loop:\n        ; Get a new string\n        call string_new         ; in RAX\n        mov bl, BYTE [r11]      ; Get the next character\n        mov [rax + Array.data], BYTE bl\n        mov [rax + Array.length], DWORD 1\n        \n        ; Put string into Cons at end of list\n        mov [r10 + Cons.car], rax\n        \n        ; Set type\n        mov [r10], BYTE (container_list + content_pointer)\n        \n        inc r11\n        cmp r11, r12\n        je .inner_done\n        \n        ; more characters, so allocate another Cons\n        call alloc_cons\n\n        mov [r10 + Cons.typecdr], BYTE content_pointer\n        mov [r10 + Cons.cdr], rax\n        mov r10, rax\n        jmp .inner_loop\n        \n.inner_done:\n        ; No more characters in this Array\n        ; check if there are more\n        mov r8, QWORD [r8 + Array.next]     ; Get the next Array address\n        test r8, r8           ; Test if it's null\n        jz .string_finished\n\n        ; Another chunk in the string\n        \n        call alloc_cons\n        mov [r10 + Cons.typecdr], BYTE content_pointer\n        mov [r10 + Cons.cdr], rax\n        mov r10, rax\n        jmp .loop\n        \n.string_finished:\n        mov rax, r9\n        ret\n        \n.missing_arg:\n        ; No arguments\n        load_static core_seq_missing_arg\n        jmp core_throw_str\n        \n.wrong_type:\n        ; Not a list, vector, string or nil\n        load_static core_seq_wrong_type\n        jmp core_throw_str\n        \n"
  },
  {
    "path": "impls/nasm/env.asm",
    "content": "\n%include \"macros.mac\"\n        \n;; ------------------------------------------------------------\n;; Environment type\n;; \n;; These are lists of maps. The head of the list is the\n;; current environment, and CDR points to the outer environment\n;;\n;; ( {} {} ... )\n        \nsection .data\n\n;; Symbols used for comparison\n        static_symbol env_symbol, '*env*'\n        static_symbol ampersand_symbol, '&'\n        \n;; Error message strings\n\n        static env_binds_error_string, db \"Env expecting symbol in binds list\",10\n        static env_binds_missing_string, db \"Env missing expression in bind\",10\n        static env_missing_symbol_after_amp_string, db \"Env missing symbol after &\",10\n        \nsection .text\n\n;; Create a new Environment\n;;\n;; Input: outer Environment in RSI.\n;;         - If zero, then nil outer.\n;;         - If not zero, increments reference count\n;;                   \n;; Return a new Environment type in RAX\n;;\n;; Modifies registers:\n;;   RAX\n;;   RBX\nenv_new:\n        call map_new            ; map in RAX\n        push rax\n        call alloc_cons         ; Cons in RAX\n        pop rbx                 ; map in RBX\n\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        ; CDR type already set to nil in alloc_cons\n        mov [rax + Cons.car], rbx\n\n        cmp rsi, 0\n        jne .set_outer\n        ret                     ; No outer, just return\n.set_outer:\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n\n        ; increment reference counter of outer\n        mov rbx, rax            ; because incref_object modifies rax\n        call incref_object\n        mov rax, rbx\n        ret\n\n;; Create a new environment using a binding list\n;;\n;; Input: RSI - Outer environment\n;;        RDI - Binds, a list of symbols\n;;        RCX - Exprs, a list of values to bind each symbol to\n;;\n;; Modifies registers\n;;    RBX\n;;    RDX\n;;    R8\n;;    R9\n;;    R10\n;;    R11\n;;    R12\n;;    R13\nenv_new_bind:\n        mov r11, rdi            ; binds list in R11\n        mov r12, rcx            ; expr list in R12\n        \n        call env_new \n        mov r13, rax             ; New environment in R13\n        \n.bind_loop:\n        ; Check the type in the bind list\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        cmp bl, content_empty\n        je .done                ; No bindings\n        \n        cmp bl, content_pointer\n        jne .bind_not_symbol\n\n        mov rdi, [r11 + Cons.car] ; Symbol object?\n        mov bl, BYTE [rdi]\n        cmp bl, maltype_symbol\n        jne .bind_not_symbol\n\n        ; RDI now contains a symbol\n\n        ; Check if it is '&'\n        mov rsi, ampersand_symbol\n        push rdi\n        call compare_char_array ; Compares RSI and RDI\n        pop rdi\n        cmp rax, 0\n        je .variadic            ; Bind rest of args to following symbol\n        \n        ; Check the type in expr\n\n        mov bl, BYTE [r12]\n        mov bh, bl\n        and bh, content_mask\n\n        cmp bh, content_empty\n        je .bind_missing_expr   ; No expression\n        \n        cmp bh, content_pointer\n        je .value_pointer\n        \n        ; A value. Need to remove the container type\n        xchg bl,bh\n        mov [r12], BYTE bl\n        xchg bl,bh\n        mov rcx, r12            ; Value\n        mov rsi, r13            ; Env\n        push rbx\n        call env_set\n        pop rbx\n        ; Restore original type\n        mov [r12], BYTE bl\n        jmp .next\n\n.value_pointer:\n        ; A pointer to something, so just pass address to env_set\n        mov rcx, [r12 + Cons.car]\n        mov rsi, r13\n        call env_set\n        ; Fall through to next\n.next:\n        ; Check if there is a next symbol\n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .done\n\n        ; Got another symbol\n        mov r11, [r11 + Cons.cdr] ; Next symbol\n\n        ; Check if there's an expression to bind to\n        mov bl, BYTE [r12 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .next_no_expr       ; No expr, but symbol could be &\n\n        mov r12, [r12 + Cons.cdr] ; Next expression\n        jmp .bind_loop\n        \n.next_no_expr:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov r12, rax\n        \n        jmp .bind_loop\n.done:\n        mov rax, r13            ; Env\n        ret\n\n.variadic:\n        ; R11 Cons contains '&' symbol\n        ; Bind next symbol to the rest of the list in R12\n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .missing_symbol_after_amp\n\n        mov r11, [r11 + Cons.cdr]\n        \n        mov bl, BYTE [r11]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .bind_not_symbol\n        \n        mov rdi, [r11 + Cons.car] ; Symbol object?\n        mov bl, BYTE [rdi]\n        cmp bl, maltype_symbol\n        jne .bind_not_symbol\n        \n        ; Bind symbol in RDI to R12\n        mov rcx, r12            ; Value\n        mov rsi, r13            ; Env\n        call env_set\n        jmp .done\n        \n.missing_symbol_after_amp:\n        push r12\n\n        ; Release the environment\n        mov rsi, r13\n        call release_object\n        \n        print_str_mac error_string   ; print 'Error: '\n        print_str_mac env_missing_symbol_after_amp_string\n        pop rsi\n        jmp error_throw\n        \n.bind_not_symbol:               ; Expecting a symbol\n        push r11                ; Binds list\n\n        ; Release the environment\n        mov rsi, r13\n        call release_object\n        \n        print_str_mac error_string   ; print 'Error: '\n        \n        print_str_mac env_binds_error_string\n        \n        pop rsi                 ; Throw binds list\n        jmp error_throw\n\n.bind_missing_expr:\n        ; Have a symbol, but no expression.\n        \n        push r11                ; Binds list\n        \n        ; Release the environment\n        mov rsi, r13\n        call release_object\n        \n        print_str_mac error_string   ; print 'Error: '\n        \n        print_str_mac env_binds_missing_string\n        \n        pop rsi                 ; Throw binds list\n        jmp error_throw\n\n        \n;; Environment set\n;;\n;; Sets a key-value pair in an environment\n;;\n;; Inputs: RSI - env [not modified]\n;;         RDI - key [not modified]\n;;         RCX - value [not modified]\n;;\n;; Increments reference counts of key and value\n;; if pointers to them are created\n;; \n;; Modifies registers:\n;;   R8\n;;   R9\n;;   R10\nenv_set:\n        push rsi\n        ; Get the first CAR, which should be a map\n        mov rsi, [rsi + Cons.car]\n        call map_set\n        pop rsi\n        ret\n        \n;; Environment get\n;; \n;; Get a value from an environment, incrementing the reference count\n;; of the object returned\n;;\n;; Inputs: RSI - environment\n;;         RDI - key\n;;\n;; Returns: If found, Zero Flag is set and address in RAX\n;;          If not found, Zero Flag cleared\nenv_get:\n        push rsi\n\n        ; Check special variable *env*\n        mov rsi, env_symbol\n        call compare_char_array\n        pop rsi\n        cmp rax, 0\n        jne .not_env_symbol\n\n        ; Env symbol, so return this environment\n        call incref_object\n        lahf                    ; flags in AH\n        or ah, 64          ; set zero flag\n        sahf\n        mov rax, rsi\n        ret\n        \n.not_env_symbol:\n        push rsi\n        ; Get the map in CAR\n        mov rsi, [rsi + Cons.car]\n        call map_get\n        pop rsi\n        je .found\n\n        ; Not found, so try outer\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .not_found\n        \n        mov rsi, [rsi + Cons.cdr] ; outer \n        jmp env_get\n.found:\n        ret\n\n.not_found:\n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        ret\n\n        \n"
  },
  {
    "path": "impls/nasm/exceptions.asm",
    "content": "    \n;; ----------------------------------------------\n;; \n;; Error handling\n;;\n;; A handler consists of:\n;;  - A stack pointer address to reset to\n;;  - An address to jump to\n;;  - An optional data structure to pass\n;;\n;; When jumped to, an error handler will be given:\n;;   - the object thrown in RSI\n;;   - the optional data structure in RDI\n;; \n\nsection .bss\n        \n;; Error handler list\nerror_handler: resq 1\n        \nsection .text\n        \n;; Add an error handler to the front of the list\n;;\n;; Input: RSI - Stack pointer\n;;        RDI - Address to jump to\n;;        RCX - Data structure. Set to zero for none.\n;;            If not zero, reference count incremented\n;;\n;; Modifies registers:\n;;    RAX\n;;    RBX\nerror_handler_push:\n        call alloc_cons\n        ; car will point to a list (stack, addr, data)\n        ; cdr will point to the previous handler\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov rbx, [error_handler]\n        cmp rbx, 0              ; Check if previous handler was zero\n        je .create_handler      ; Zero, so leave null\n        ; Not zero, so create pointer to it\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rbx\n\n        ; note: not incrementing reference count, since\n        ; we're replacing one reference with another\n.create_handler:\n        mov [error_handler], rax ; new error handler\n        \n        mov rdx, rax\n        call alloc_cons\n        mov [rdx + Cons.car], rax\n        ; Store stack pointer\n        mov [rax], BYTE (block_cons + container_list + content_function)\n        mov [rax + Cons.car], rsi ; stack pointer\n        \n        mov rdx, rax\n        call alloc_cons\n        mov [rdx + Cons.typecdr], BYTE content_pointer\n        mov [rdx + Cons.cdr], rax\n        ; Store function pointer to jump to\n        ; Note: This can't use content_pointer or release\n        ; will try to release this memory address\n        mov [rax], BYTE (block_cons + container_list + content_function)\n        mov [rax + Cons.car], rdi\n\n        ; Check if there is an object to pass to handler\n        cmp rcx, 0\n        je .done\n\n        ; Set the final CDR to point to the object\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rcx\n\n        mov rsi, rcx\n        call incref_object\n        \n.done:\n        ret\n        \n        \n;; Removes an error handler from the list\n;;\n;; Modifies registers:\n;;    RSI\n;;    RAX\n;;    RCX\nerror_handler_pop:\n        ; get the address\n        mov rsi, [error_handler]\n        cmp rsi, 0\n        je .done                ; Nothing to remove\n        \n        push rsi\n        mov rsi, [rsi + Cons.cdr] ; next handler\n        mov [error_handler], rsi\n        ;call incref_object        ; needed because releasing soon\n        \n        pop rsi                 ; handler being removed\n        mov [rsi + Cons.typecdr], BYTE 0\n        call release_cons\n        \n.done:\n        ret\n        \n        \n;; Throw an error\n;;   Object to pass to handler should be in RSI\nerror_throw:\n        ; Get the next error handler\n        mov rax, [error_handler]\n        cmp rax, 0\n        je .no_handler\n        \n        ; Got a handler\n        mov rax, [rax + Cons.car] ; handler\n        mov rbx, [rax + Cons.car] ; stack pointer\n        mov rax, [rax + Cons.cdr]\n        mov rcx, [rax + Cons.car] ; function\n        mov rdi, [rax + Cons.cdr] ; data structure\n\n        ; Reset stack\n        mov rsp, rbx\n\n        ; Jump to the handler\n        jmp rcx\n        \n.no_handler:\n        ; Print the object in RSI then quit\n        cmp rsi, 0\n        je .done                ; nothing to print\n        mov rdi, 1              ; print_readably\n        call pr_str\n        mov rsi, rax\n        call print_string\n.done:\n        jmp quit_error\n        \n"
  },
  {
    "path": "impls/nasm/macros.mac",
    "content": ";; Some useful macros\n        \n%ifndef MACROS_MAC \n%define MACROS_MAC\n        \n;; Define a static data value\n;;\n;; static label value\n;;\n%macro static 2+\n        %1: %2\n        %1.len: equ $ - %1\n%endmacro\n\n;; Puts address of data in RSI, length in EDX\n%macro load_static 1\n        mov rsi, %1\n        mov edx, %1.len\n%endmacro\n        \n;; Define a symbol which can be compared against\n;;\n;; static_symbol  name, string\n;;\n;; Example:\n;;\n;;     static_symbol def_symbol, 'def!'\n;;\n%macro static_symbol 2\n        %strlen slen %2         ; length of string\n        \n        %1: ISTRUC Array\n        AT Array.type,  db   maltype_symbol\n        AT Array.refcount, dw 1\n        AT Array.length, dd  slen\n        AT Array.data, db %2\n        IEND\n%endmacro\n\n;; Macro for printing raw string\n;;\n%macro print_str_mac 1\n        mov rsi, %1             ; String address\n        mov rdx, %1.len         ; Length of string\n        call print_rawstring\n%endmacro\n        \n%endif\n        \n"
  },
  {
    "path": "impls/nasm/printer.asm",
    "content": ";;; Turns forms (lists, values/atoms) into strings\n;;; \n;;;\n\n%include \"macros.mac\"\n        \nsection .data\n\n        ; Constant strings for printing\n        static unknown_type_string, db \"#<unknown>\"\n        static unknown_value_string, db \"#<unknown value>\"\n        static function_type_string, db \"#<function>\"\n        static macro_type_string, db \"#<macro>\"\n        static nil_value_string, db \"nil\"\n        static true_value_string, db \"true\"\n        static false_value_string, db \"false\"\n        \nsection .text\n\n;; Input: Address of object in RSI\n;;        print_readably in RDI. First bit set to zero for false\n;;\n;; Output: Address of string in RAX\n;;\n;; Modifies:\n;;  RCX\n;;  R8   \n;;  R12\n;;  R13\n;;  R14\n;; Calls: raw_to_string,\n;;\n;; \npr_str: \n\n        ; Get the type\n        mov cl, BYTE [rsi]\n        \n        ; Check if it's already a string\n        cmp cl, maltype_string\n        \n        jne .not_string\n\n        ; ---------------------------\n        ; Handle string\n\n        test rdi, 1\n        jz .string_not_readable\n        \n        ; printing readably, so escape characters\n        \n        call string_new         ; Output string in rax\n        \n        mov r12, rax\n        add r12, Array.data     ; Output data\n        mov r13, rsi\n        add r13, Array.data     ; Input data\n        mov r14d, DWORD [rsi + Array.length]\n        add r14, Array.data\n        add r14, rsi            ; End of input data\n\n        ; Put \" at start of output string\n        mov [r12], BYTE '\"'\n        inc r12\n\n        ; Loop through the input string, escaping characters\n        \n.string_loop:\n        cmp r13, r14\n        je .string_finished\n        \n        mov cl, BYTE [r13]      ; Get next character\n        inc r13\n        \n        cmp cl, '\"'             ;\n        je .string_escape_char\n\n        cmp cl, 92              ; Escape '\\'\n        je .string_escape_char\n        \n        cmp cl, 10              ; Newline\n        je .string_newline\n\n        ; No special case, just copy the byte\n        mov [r12], BYTE cl\n        inc r12\n        jmp .string_loop\n        \n.string_newline:\n        mov cl, 'n'\n        ;jmp .string_escape_char\n        \n.string_escape_char:             ; Add a '\\' before char in cl\n        mov [r12], BYTE 92 ; Escape '\\'\n        inc r12\n        mov [r12], BYTE cl\n        inc r12\n        jmp .string_loop\n\n.string_finished:\n\n        \n        \n        mov [r12], BYTE '\"'     ; At the end\n        inc r12\n        ; Calculate length of string\n        sub r12, rax\n        sub r12, Array.data\n\n        mov [rax + Array.length], DWORD r12d\n        \n        ret\n\n.string_not_readable:\n        ; Just return the string\n        call incref_object\n        mov rax, rsi\n        ret\n\n        ; ----------------------------\n.not_string:\n        ; Now test the container type (value, list, map, vector)\n        \n        mov ch, cl\n\n        and ch, container_mask\n        jz .value\n\n        cmp ch, container_list\n        je .list\n\n        cmp ch, container_symbol\n        je .symbol\n\n        cmp ch, container_map\n        je .map\n\n        cmp ch, container_vector\n        je .vector\n        \n        cmp ch, container_function\n        je .function_or_macro\n\n        cmp ch, container_atom\n        je .atom\n        \n        ; Unknown\n        mov rsi, unknown_type_string\n        mov edx, unknown_type_string.len\n        call raw_to_string      ; Puts a String in RAX\n        ret\n        \n        ; --------------------------------\n.value:\n        mov ch, cl\n        and ch, content_mask\n        jz .value_nil\n\n        cmp ch, content_int\n        je .value_int\n\n        cmp ch, content_true\n        je .value_true\n\n        cmp ch, content_false\n        je .value_false\n        \n        mov rsi, unknown_value_string\n        mov edx, unknown_value_string.len\n        call raw_to_string      ; Puts a String in RAX\n        ret\n        \n        ; --------------------------------\n.value_nil:\n        mov rsi, nil_value_string\n        mov edx, nil_value_string.len\n        call raw_to_string\n        ret\n\n.value_true:\n        mov rsi, true_value_string\n        mov edx, true_value_string.len\n        call raw_to_string\n        ret\n        \n.value_false:\n        mov rsi, false_value_string\n        mov edx, false_value_string.len\n        call raw_to_string\n        ret\n        \n        ; --------------------------------\n.value_int:\n        mov rax, [rsi + Cons.car]\n        call itostring\n        ret\n        \n        ; --------------------------------\n.list:\n        \n        mov r12, rsi            ; Input list\n        \n        call string_new         ; String in rax\n        mov r13, rax            ; Output string in r13\n        \n        ; Put '(' onto string\n        mov rsi, rax\n        mov cl, '('\n        call string_append_char\n        \n        ; loop through list\n.list_loop:\n        \n        ; Extract values and print\n        \n        mov rsi, r12\n        mov cl, BYTE [rsi]      ; Get type\n\n        ; Check if it's a pointer (address)\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_pointer\n        je .list_loop_pointer\n\n        cmp ch, content_empty\n        je .list_check_end\n        \n        ; A value (nil, int etc. or function)\n        mov ch, cl              ; Save type, container\n        and cl, content_mask    ; Remove list type -> value\n        mov BYTE [rsi], cl\n\n        push rcx\n        push r13\n        push r12\n        call pr_str             ; String in rax\n        pop r12\n        pop r13\n        pop rcx\n\n        mov cl, ch              ; Restore list type\n        mov  BYTE [r12], cl\n        jmp .list_loop_got_str\n.list_loop_pointer:\n        mov rsi, [rsi + Cons.car] ; Address of object\n        push r13\n        push r12\n        call pr_str             ; String in rax\n        pop r12\n        pop r13\n        \n.list_loop_got_str:\n        ; concatenate strings in rax and rsi\n        mov rsi, r13            ; Output string\n        mov rdx, rax            ; String to be copied\n\n        push rsi                ; Save output string\n        push rax                ; save temporary string\n        call string_append_string\n\n        ; Release the string\n        pop rsi                 ; Was in rax, temporary string\n        call release_array\n\n        pop rsi                 ; restore output string\n.list_check_end:\n        ; Check if this is the end of the list\n        mov cl, BYTE [r12 + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .list_finished\n\n        ; More left in the list\n        \n        ; Add space between values\n        mov cl, ' '\n        mov rsi, r13\n        call string_append_char\n        \n        ; Get next Cons\n        mov r12, [r12 + Cons.cdr]\n        jmp .list_loop\n        \n.list_finished:\n        ; put ')' at the end of the string\n        mov cl, ')'\n        mov rsi, r13\n        call string_append_char\n        \n        mov rax, rsi\n        ret\n\n        ; --------------------------------\n.symbol:\n        ; Make a copy of the string\n        call string_new         ; in rax\n        mov ebx, DWORD [rsi + Array.length]\n        mov [rax + Array.length], ebx\n        mov rcx, rsi\n        add rcx, Array.data     ; Start of input data\n        mov rdx, rsi\n        add rdx, Array.size     ; End of input data\n        mov r12, rax\n        add r12, Array.data     ; Start of output data\n.symbol_copy_loop:\n        ; Copy [rax] -> [r12]\n        mov rbx, [rcx]\n        mov [r12], rbx\n        add rcx, 8              ; Next 64 bits of input\n        cmp rcx, rdx\n        je .symbol_finished\n        \n        add r12, 8              ; Next 64 bits of output\n        jmp .symbol_copy_loop\n.symbol_finished:\n        ret\n\n        ; --------------------------------\n.map:\n        \n        mov r12, rsi            ; Input map\n        \n        call string_new         ; String in rax\n        mov r13, rax            ; Output string in r13\n        \n        ; Put '{' onto string\n        mov rsi, rax\n        mov cl, '{'\n        call string_append_char\n        \n        ; loop through map\n.map_loop:\n        \n        ; Extract values and print\n        \n        mov rsi, r12\n        mov cl, BYTE [rsi]      ; Get type\n\n        ; Check if it's a pointer (address)\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_pointer\n        je .map_loop_pointer\n\n        cmp ch, content_empty\n        je .map_check_end\n        \n        ; A value (nil, int etc. or function)\n        xchg ch, cl\n        mov [rsi], BYTE cl      ; Remove map type -> value\n        xchg ch, cl \n\n        push rcx\n        push r13\n        push r12\n        call pr_str             ; String in rax\n        pop r12\n        pop r13\n        pop rcx\n        \n        mov cl, BYTE [r12]      ; Restore map type\n        \n        jmp .map_loop_got_str\n.map_loop_pointer:\n        mov rsi, [rsi + Cons.car] ; Address of object\n        push r13\n        push r12\n        call pr_str             ; String in rax\n        pop r12\n        pop r13\n        \n.map_loop_got_str:\n        ; concatenate strings in rax and rsi\n        mov rsi, r13            ; Output string\n        mov rdx, rax            ; String to be copied\n\n        push rsi                ; Save output string\n        push rax                ; save temporary string\n        call string_append_string\n\n        ; Release the string\n        pop rsi                 ; Was in rax, temporary string\n        call release_array\n\n        pop rsi                 ; restore output string\n.map_check_end:\n        ; Check if this is the end of the map\n        mov cl, BYTE [r12 + Cons.typecdr]\n        cmp cl, content_nil\n        je .map_finished\n\n        ; More left in the map\n        \n        ; Add space between values\n        mov cl, ' '\n        mov rsi, r13\n        call string_append_char\n        \n        ; Get next Cons\n        mov r12, [r12 + Cons.cdr]\n        jmp .map_loop\n        \n.map_finished:\n        ; put '}' at the end of the string\n        mov cl, '}'\n        mov rsi, r13\n        call string_append_char\n        \n        mov rax, rsi\n        ret\n        \n        ; --------------------------------\n.vector:\n        \n        mov r12, rsi            ; Input vector\n        \n        call string_new         ; String in rax\n        mov r13, rax            ; Output string in r13\n        \n        ; Put '[' onto string\n        mov rsi, rax\n        mov cl, '['\n        call string_append_char\n        \n        ; loop through vector\n.vector_loop:\n        \n        ; Extract values and print\n        \n        mov rsi, r12\n        mov cl, BYTE [rsi]      ; Get type\n\n        ; Check if it's a pointer (address)\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_pointer\n        je .vector_loop_pointer\n\n        cmp ch, content_empty\n        je .vector_check_end\n        \n        ; A value (nil, int etc. or function)\n        mov ch, cl              ; Save type, container\n        and cl, content_mask    ; Remove vector type -> value\n        mov BYTE [rsi], cl\n\n        push rcx\n        push r13\n        push r12\n        call pr_str             ; String in rax\n        pop r12\n        pop r13\n        pop rcx\n\n        mov cl, ch              ; Restore vector type\n        mov  BYTE [r12], cl\n        jmp .vector_loop_got_str\n.vector_loop_pointer:\n        mov rsi, [rsi + Cons.car] ; Address of object\n        push r13\n        push r12\n        call pr_str             ; String in rax\n        pop r12\n        pop r13\n        \n.vector_loop_got_str:\n        ; concatenate strings in rax and rsi\n        mov rsi, r13            ; Output string\n        mov rdx, rax            ; String to be copied\n\n        push rsi                ; Save output string\n        push rax                ; save temporary string\n        call string_append_string\n\n        ; Release the string\n        pop rsi                 ; Was in rax, temporary string\n        call release_array\n\n        pop rsi                 ; restore output string\n.vector_check_end:\n        ; Check if this is the end of the vector\n        mov cl, BYTE [r12 + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .vector_finished\n\n        ; More left in the vector\n        \n        ; Add space between values\n        mov cl, ' '\n        mov rsi, r13\n        call string_append_char\n        \n        ; Get next Cons\n        mov r12, [r12 + Cons.cdr]\n        jmp .vector_loop\n        \n.vector_finished:\n        ; put ']' at the end of the string\n        mov cl, ']'\n        mov rsi, r13\n        call string_append_char\n        \n        mov rax, rsi\n        ret\n        \n        ; --------------------------------\n.function_or_macro:\n        cmp cl, maltype_macro\n        je .macro\n\n        ; a function\n        mov rsi, function_type_string\n        mov edx, function_type_string.len\n        call raw_to_string      ; Puts a String in RAX\n        ret\n\n.macro:\n        mov rsi, macro_type_string\n        mov edx, macro_type_string.len\n        call raw_to_string      ; Puts a String in RAX\n        ret\n\n        ; --------------------------------\n.atom:\n        mov rsi, [rsi + Cons.car] ; What the atom points to\n        \n        call string_new         ; String in rax\n\n        ; Start string with '(atom'\n        mov rbx, '(atom   '\n        mov [rax + Array.data], rbx\n        mov [rax + Array.length], DWORD 6\n        \n        push rax\n        call pr_str\n        mov rdx, rax            ; string to be copied\n        pop rsi                 ; Output string\n\n        call string_append_string\n\n        ; closing bracket\n        mov cl, ')'\n        call string_append_char\n        mov rax, rsi\n        ret\n"
  },
  {
    "path": "impls/nasm/reader.asm",
    "content": "%include \"macros.mac\"\n\nsection .data\n\n;; Reader macro strings\n\n        static quote_symbol_string, db \"quote\"\n        static quasiquote_symbol_string, db \"quasiquote\"\n        static unquote_symbol_string, db \"unquote\"\n        static splice_unquote_symbol_string, db \"splice-unquote\"\n        static deref_symbol_string, db \"deref\"\n        static with_meta_symbol_string, db \"with-meta\"\n        \n;; Error message strings\n        \n        static error_string_unexpected_end, db \"Error: Unexpected end of input (EOF). Could be a missing ) or ]\", 10\n        static error_string_bracket_not_brace, db \"Error: Expecting '}' but got ')'\"\n\n;; Symbols for comparison\n\n        static_symbol nil_symbol, 'nil'\n        static_symbol true_symbol, 'true'\n        static_symbol false_symbol, 'false'\n        \nsection .text\n\n;; Read a string into memory as a form (nested lists and atoms)\n;; Note: In this implementation the tokenizer is not done separately\n;;\n;; Input: Address of string (char array) in RSI\n;;\n;; Output: Address of object in RAX\n;;\n;; Uses registers:\n;;  R12  Address of the start of the current list (starts 0)\n;;  R13  Address of the current list tail\n;;  R14  Stack pointer at start. Used for unwinding on error\n;;  R15  Address of first list. Used for unwinding on error\n;;\n;; In addition, the tokenizer uses\n;;\n;;  RAX    (object return)\n;;  RBX\n;;  RCX    (character return in CL)\n;;  RDX\n;;  R8   ** State must be preserved\n;;  R9   ** \n;;  R10  **\n;;  R12\n;;  R13\n;;  R14   Original stack pointer on call\n;;  R15   Top-level list, so all can be released on error\n;;\nread_str:\n        ; Initialise tokenizer\n        call tokenizer_init\n        \n        ; Set current list to zero\n        mov r12, 0\n\n        ; Set first list to zero\n        mov r15, 0\n\n        ; Save stack pointer for unwinding\n        mov r14, rsp\n        \n.read_loop:\n\n        call tokenizer_next\n        cmp cl, 0\n        jne .got_token\n\n        ; Unexpected end of tokens\n        mov rdx, error_string_unexpected_end.len\n        mov rsi, error_string_unexpected_end\n        jmp .error\n        \n.got_token:\n\n        cmp cl, 'i'             ; An integer. Cons object in RAX\n        je .finished\n        cmp cl, '\"'             ; A string. Array object in RAX\n        je .finished\n        cmp cl, 's'             ; A symbol\n        je .symbol\n        \n        cmp cl, '('\n        je .list_start\n\n        cmp cl, ')'\n        je .return_nil          ; Note: if reading a list, cl will be tested in the list reader\n\n        cmp cl, '{'\n        je .map_start\n        \n        cmp cl, '}'             ; cl tested in map reader\n        je .return_nil\n\n        cmp cl, '['\n        je .vector_start\n\n        cmp cl, ']'             ; cl tested in vector reader\n        je .return_nil\n        \n        cmp cl, 39              ; quote '\n        je .handle_quote\n        cmp cl, '`'\n        je .handle_quasiquote\n        cmp cl, '~'\n        je .handle_unquote\n        cmp cl, 1\n        je .handle_splice_unquote\n        cmp cl, '@'\n        je .handle_deref\n        \n        cmp cl, '^'\n        je .handle_with_meta\n        \n        ; Unknown\n        jmp .return_nil\n        \n        ; --------------------------------\n        \n.list_start:\n        \n        ; Get the first value\n        ; Note that we call rather than jmp because the first\n        ; value needs to be treated differently. There's nothing\n        ; to append to yet...\n        call .read_loop\n        \n        ; rax now contains the first object\n        cmp cl, ')'            ; Check if it was end of list\n        jne .list_has_contents\n        mov cl, 0               ; so ')' doesn't propagate to nested lists\n        ; Set list to empty\n        mov [rax], BYTE maltype_empty_list\n        ret                    ; Returns 'nil' given \"()\"\n.list_has_contents:\n        ; If this is a Cons then use it\n        ; If not, then need to allocate a Cons\n        mov cl, BYTE [rax]\n        mov ch, cl\n        and ch, (block_mask + container_mask)   ; Tests block and container type\n        jz .list_is_value\n\n        ; If here then not a simple value, so need to allocate\n        ; a Cons object\n        \n        ; Start new list\n        push rax\n        call alloc_cons         ; Address in rax\n        pop rbx\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], rbx\n        ; Now have Cons in RAX, containing pointer to object as car\n        \n.list_is_value:\n        ; Cons in RAX\n        ; Make sure it's marked as a list\n        mov cl, BYTE [rax]\n        or cl, container_list\n        mov [rax], BYTE cl\n\n        mov r12, rax            ; Start of current list\n        mov r13, rax            ; Set current list\n        cmp r15, 0              ; Test if first list\n        jne .list_read_loop\n        mov r15, rax            ; Save the first, for unwinding\n        \n.list_read_loop:\n        ; Repeatedly get the next value in the list\n        ; (which may be other lists)\n        ; until we get a ')' token\n\n        push r12\n        push r13\n        call .read_loop         ; object in rax\n        pop r13\n        pop r12\n        \n        cmp cl, ')'            ; Check if it was end of list\n        je .list_done          ; Have nil object in rax\n\n        ; Test if this is a Cons value\n        mov cl, BYTE [rax]\n        mov ch, cl\n        and ch, (block_mask + container_mask)   ; Tests block and container type\n        jz .list_loop_is_value\n\n        ; If here then not a simple value, so need to allocate\n        ; a Cons object\n        \n        ; Start new list\n        push rax\n        call alloc_cons         ; Address in rax\n        pop rbx\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], rbx\n        ; Now have Cons in RAX, containing pointer to object as car\n        \n.list_loop_is_value:\n        ; Cons in RAX\n\n        ; Make sure it's marked as a list\n        mov cl, BYTE [rax]\n        or cl, container_list\n        mov [rax], BYTE cl\n        \n        ; Append to r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax            ; Set current list\n        \n        jmp .list_read_loop\n        \n.list_done:\n        ; Release nil object in rax\n        mov rsi, rax\n        call release_cons\n        \n        ; Terminate the list\n        mov [r13 + Cons.typecdr], BYTE content_nil\n        mov QWORD [r13 + Cons.cdr], QWORD 0\n        mov rax, r12            ; Start of current list\n        \n        ret\n\n        ; --------------------------------\n        \n.map_start:\n        \n        ; Get the first value\n        ; Note that we call rather than jmp because the first\n        ; value needs to be treated differently. There's nothing\n        ; to append to yet...\n        call .read_loop\n        \n        ; rax now contains the first object\n        cmp cl, '}'            ; Check if it was end of map\n        jne .map_has_contents\n        mov cl, 0               ; so '}' doesn't propagate to nested maps\n        ; Set map to empty\n        mov [rax], BYTE maltype_empty_map\n        ret                    ; Returns 'nil' given \"()\"\n.map_has_contents:\n        ; If this is a Cons then use it\n        ; If not, then need to allocate a Cons\n        mov cl, BYTE [rax]\n        mov ch, cl\n        and ch, (block_mask + container_mask)   ; Tests block and container type\n        jz .map_is_value\n\n        ; If here then not a simple value, so need to allocate\n        ; a Cons object\n        \n        ; Start new map\n        push rax\n        call alloc_cons         ; Address in rax\n        pop rbx\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        mov [rax + Cons.car], rbx\n        ; Now have Cons in RAX, containing pointer to object as car\n        \n.map_is_value:\n        ; Cons in RAX\n        ; Make sure it's marked as a map\n        mov cl, BYTE [rax]\n        or cl, container_map\n        mov [rax], BYTE cl\n\n        mov r12, rax            ; Start of current map\n        mov r13, rax            ; Set current map\n        cmp r15, 0              ; Test if first map\n        jne .map_read_loop\n        mov r15, rax            ; Save the first, for unwinding\n        \n.map_read_loop:\n        ; Repeatedly get the next value in the map\n        ; (which may be other maps)\n        ; until we get a '}' token\n\n        push r12\n        push r13\n        call .read_loop         ; object in rax\n        pop r13\n        pop r12\n        \n        cmp cl, '}'            ; Check if it was end of map\n        je .map_done          ; Have nil object in rax\n\n        ; Test if this is a Cons value\n        mov cl, BYTE [rax]\n        mov ch, cl\n        and ch, (block_mask + container_mask)   ; Tests block and container type\n        jz .map_loop_is_value\n\n        ; If here then not a simple value, so need to allocate\n        ; a Cons object\n        \n        ; Start new map\n        push rax\n        call alloc_cons         ; Address in rax\n        pop rbx\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        mov [rax + Cons.car], rbx\n        ; Now have Cons in RAX, containing pointer to object as car\n        \n.map_loop_is_value:\n        ; Cons in RAX\n\n        ; Make sure it's marked as a map\n        mov cl, BYTE [rax]\n        or cl, container_map\n        mov [rax], BYTE cl\n        \n        ; Append to r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax            ; Set current map\n        \n        jmp .map_read_loop\n        \n.map_done:\n        ; Release nil object in rax\n        mov rsi, rax\n        call release_cons\n        \n        ; Terminate the map\n        mov [r13 + Cons.typecdr], BYTE content_nil\n        mov QWORD [r13 + Cons.cdr], QWORD 0\n        mov rax, r12            ; Start of current map\n        \n        ret\n        \n        ; --------------------------------\n        \n.vector_start:\n        \n        ; Get the first value\n        ; Note that we call rather than jmp because the first\n        ; value needs to be treated differently. There's nothing\n        ; to append to yet...\n        call .read_loop\n        \n        ; rax now contains the first object\n        cmp cl, ']'            ; Check if it was end of vector\n        jne .vector_has_contents\n        mov cl, 0               ; so ']' doesn't propagate to nested vectors\n        ; Set vector to empty\n        mov [rax], BYTE maltype_empty_vector\n        ret                    ; Returns 'nil' given \"()\"\n.vector_has_contents:\n        ; If this is a Cons then use it\n        ; If not, then need to allocate a Cons\n        mov cl, BYTE [rax]\n        mov ch, cl\n        and ch, (block_mask + container_mask)   ; Tests block and container type\n        jz .vector_is_value\n\n        ; If here then not a simple value, so need to allocate\n        ; a Cons object\n        \n        ; Start new vector\n        push rax\n        call alloc_cons         ; Address in rax\n        pop rbx\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        mov [rax + Cons.car], rbx\n        ; Now have Cons in RAX, containing pointer to object as car\n        \n.vector_is_value:\n        ; Cons in RAX\n        ; Make sure it's marked as a vector\n        mov cl, BYTE [rax]\n        or cl, container_vector\n        mov [rax], BYTE cl\n\n        mov r12, rax            ; Start of current vector\n        mov r13, rax            ; Set current vector\n        cmp r15, 0              ; Test if first vector\n        jne .vector_read_loop\n        mov r15, rax            ; Save the first, for unwinding\n        \n.vector_read_loop:\n        ; Repeatedly get the next value in the vector\n        ; (which may be other vectors)\n        ; until we get a ']' token\n\n        push r12\n        push r13\n        call .read_loop         ; object in rax\n        pop r13\n        pop r12\n        \n        cmp cl, ']'            ; Check if it was end of vector\n        je .vector_done          ; Have nil object in rax\n\n        ; Test if this is a Cons value\n        mov cl, BYTE [rax]\n        mov ch, cl\n        and ch, (block_mask + container_mask)   ; Tests block and container type\n        jz .vector_loop_is_value\n\n        ; If here then not a simple value, so need to allocate\n        ; a Cons object\n        \n        ; Start new vector\n        push rax\n        call alloc_cons         ; Address in rax\n        pop rbx\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        mov [rax + Cons.car], rbx\n        ; Now have Cons in RAX, containing pointer to object as car\n        \n.vector_loop_is_value:\n        ; Cons in RAX\n\n        ; Make sure it's marked as a vector\n        mov cl, BYTE [rax]\n        or cl, container_vector\n        mov [rax], BYTE cl\n        \n        ; Append to r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax            ; Set current vector\n        \n        jmp .vector_read_loop\n        \n.vector_done:\n        ; Release nil object in rax\n        mov rsi, rax\n        call release_cons\n        \n        ; Terminate the vector\n        mov [r13 + Cons.typecdr], BYTE content_nil\n        mov QWORD [r13 + Cons.cdr], QWORD 0\n        mov rax, r12            ; Start of current vector\n        \n        ret\n\n        ; --------------------------------\n.handle_quote:\n        ; Turn 'a into (quote a)\n        call alloc_cons         ; Address in rax\n        mov r12, rax\n\n        ; Get a symbol \"quote\"\n        push r8\n        push r9\n        mov rsi, quote_symbol_string\n        mov edx, quote_symbol_string.len\n        call raw_to_string      ; Address in rax\n        pop r9\n        pop r8\n\n.wrap_next_object:\n        mov [rax], BYTE maltype_symbol\n        mov [r12], BYTE (block_cons + container_list + content_pointer)\n        mov [r12 + Cons.car], rax\n        \n        ; Get the next object\n        push r12\n        call .read_loop         ; object in rax\n        pop r12\n\n        mov r13, rax            ; Put object to be quoted in r13\n\n        call alloc_cons         ; Address in rax\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r13\n        mov [rax + Cons.typecdr], BYTE content_nil\n        \n        ; Cons object in rax. Append to object in r12\n        mov [r12 + Cons.typecdr], BYTE content_pointer\n        mov [r12 + Cons.cdr], rax\n\n        mov rax, r12\n        ret\n\n        ; --------------------------------\n.handle_quasiquote:\n        ; Turn `a into (quasiquote a)\n        call alloc_cons         ; Address in rax\n        mov r12, rax\n\n        ; Get a symbol \"quasiquote\"\n        push r8\n        push r9\n        mov rsi, quasiquote_symbol_string\n        mov edx, quasiquote_symbol_string.len\n        call raw_to_string      ; Address in rax\n        pop r9\n        pop r8\n        jmp .wrap_next_object   ; From there the same as handle_quote\n\n        ; --------------------------------\n.handle_unquote:\n        ; Turn ~a into (unquote a)\n        call alloc_cons         ; Address in rax\n        mov r12, rax\n\n        ; Get a symbol \"unquote\"\n        push r8\n        push r9\n        mov rsi, unquote_symbol_string\n        mov edx, unquote_symbol_string.len\n        call raw_to_string      ; Address in rax\n        pop r9\n        pop r8\n        jmp .wrap_next_object   ; From there the same as handle_quote\n\n        ; --------------------------------\n.handle_splice_unquote:\n        ; Turn ~@a into (unquote a)\n        call alloc_cons         ; Address in rax\n        mov r12, rax\n\n        ; Get a symbol \"unquote\"\n        push r8\n        push r9\n        mov rsi, splice_unquote_symbol_string\n        mov edx, splice_unquote_symbol_string.len\n        call raw_to_string      ; Address in rax\n        pop r9\n        pop r8\n        jmp .wrap_next_object   ; From there the same as handle_quote\n        \n        ; --------------------------------\n\n.handle_deref:\n        ; Turn @a into (deref a)\n\n        call alloc_cons         ; Address in rax\n        mov r12, rax\n\n        ; Get a symbol \"deref\"\n        push r8\n        push r9\n        mov rsi, deref_symbol_string\n        mov edx, deref_symbol_string.len\n        call raw_to_string      ; Address in rax\n        pop r9\n        pop r8\n        jmp .wrap_next_object   ; From there the same as handle_quote\n\n        ; --------------------------------\n\n.handle_with_meta:\n        ; Turn ^ a b into (with-meta b a)\n\n        call alloc_cons         ; Address in rax\n        mov r12, rax\n\n        ; Get a symbol \"with-meta\"\n        push r8\n        push r9\n        mov rsi, with_meta_symbol_string\n        mov edx, with_meta_symbol_string.len\n        call raw_to_string      ; Address in rax\n        pop r9\n        pop r8\n\n        mov [rax], BYTE maltype_symbol\n        mov [r12], BYTE (block_cons + container_list + content_pointer)\n        mov [r12 + Cons.car], rax\n\n        ; Get the next two objects\n        push r12\n        call .read_loop         ; object in rax\n        pop r12\n        push rax\n        push r12\n        call .read_loop         ; in RAX\n        pop r12\n\n        mov r13, rax\n        \n        call alloc_cons         ; Address in rax\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r13\n        \n        ; Cons object in rax. Append to object in r12\n        mov [r12 + Cons.typecdr], BYTE content_pointer\n        mov [r12 + Cons.cdr], rax\n\n        mov r13, rax\n\n        call alloc_cons         ; Address in rax\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        \n        pop rdi                 ; First object\n        mov [rax + Cons.car], rdi\n\n        ; Append to object in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n\n        mov rax, r12\n        ret\n        \n        ; --------------------------------\n.symbol:\n        ; symbol is in RAX\n        ; Some symbols are have their own type\n        ; - nil, true, false\n        ;\n\n        mov rsi, rax\n        mov rdi, nil_symbol\n        push rsi\n        call compare_char_array\n        pop rsi\n        cmp rax, 0\n        je .symbol_nil\n        \n        mov rdi, true_symbol\n        push rsi\n        call compare_char_array\n        pop rsi\n        cmp rax, 0\n        je .symbol_true\n\n        mov rdi, false_symbol\n        push rsi\n        call compare_char_array\n        pop rsi\n        cmp rax, 0\n        je .symbol_false\n\n        ; not a special symbol, so return\n        mov rax, rsi\n        ret\n        \n.symbol_nil:\n        ; symbol in rsi not needed\n        call release_array\n\n        call alloc_cons\n        mov [rax], BYTE maltype_nil ; a nil type\n        ret\n        \n.symbol_true:\n        call release_array\n\n        call alloc_cons\n        mov [rax], BYTE maltype_true\n        ret\n        \n.symbol_false:\n        call release_array\n\n        call alloc_cons\n        mov [rax], BYTE maltype_false\n        ret\n        \n        ; --------------------------------\n.finished:\n        ret\n\n.error:\n        ; Jump here on error with raw string in RSI\n        ; and string length in rdx\n        push r14\n        push r15\n        call print_rawstring\n        pop r15\n        pop r14\n        \n        ; fall through to unwind\n.unwind:\n        ; Jump to here cleans up\n\n        mov rsp, r14            ; Rewind stack pointer\n        cmp r15, 0              ; Check if there is a list\n        je .return_nil\n        mov rsi, r15\n        call release_cons       ; releases everything recursively\n        ; fall through to return_nil\n.return_nil:\n        ; Allocates a new Cons object with nil and returns\n        ; Cleanup should happen before jumping here\n        push rcx\n        call alloc_cons\n        pop rcx\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n\n        \n        \n;; Initialise the tokenizer\n;;\n;; Input: Address of string in RSI\n;; \n;; NOTE: This uses RSI, RAX and RBX, and expects these to be preserved\n;; between calls to tokenizer_next_char\n;;\n;;  R9   Address of string\n;;  R10  Position in data array\n;;  R11  End of data array\n;;\ntokenizer_init:\n        ; Save string to r9\n        mov r9, rsi\n        ; Put start of data array into r10\n        mov r10, rsi\n        add r10, Array.data\n        ; Put end of data array into r11\n        mov r11d, [rsi + Array.length] ; Length of array, zero-extended\n        add r11, r10\n        \n        ret\n\n;; Move onto the next chunk of the array\n;; This is needed because strings are not stored in one\n;; contiguous block of memory, but may use multiple Array\n;; objects in a linked list\n;;\n;; If no chunks are left, then R10 = R11\ntokenizer_next_chunk:\n        mov r10, [r9 + Array.next]\n        cmp r10, 0\n        je .no_more\n        ; More chunks left\n        push rsi                ; Because symbol reading uses RSI (tokenizer_next.handle_symbol)\n        mov rsi, r10\n        call tokenizer_init\n        pop rsi\n        ret\n.no_more:\n        ; No more chunks left. R10 is zero\n        mov r11, r10\n        ret\n\n;; Moves the next char into CL\n;; If no more, puts 0 into CL\ntokenizer_next_char:\n        ; Check if we have reached the end of this chunk\n        cmp r10, r11\n        jne .chars_remain\n\n        ; Hit the end. See if there is another chunk\n        call tokenizer_next_chunk\n        cmp r10, r11\n        jne .chars_remain      ; Success, got another\n\n        ; No more chunks\n        mov cl, 0               ; Null char signals end\n        ret\n        \n.chars_remain:\n        mov cl, BYTE [r10]\n        inc r10                 ; point to next byte\n        ret\n        \n;; Get the next token\n;; Token code is in CL register. Could be:\n;; - 0 : Nil, finished\n;; - Characters ()[]()'`~^@\n;; - Pair '~@', represented by code 1\n;; - A string: \" in CL, and address in RAX\n;; - An integer: 'i' in CL\n;; - A symbol: 's' in CL, address in RAX\n;;\n;; Address of object in RAX\n;;\n;; May use registers:\n;;    RBX\n;;    RCX\n;;    RDX\n;;   \ntokenizer_next:\n        \n.next_char:\n        ; Fetch the next char into CL\n        call tokenizer_next_char\n        \n        cmp cl, 0\n        je .found               ; End, no more tokens\n        \n        ; Here expect to have:\n        ; - The current character in CL\n        ; - Address of next data in r10\n        ; - Address of data end in r11\n\n        ; Skip whitespace or commas\n        cmp cl, ' '             ; Space\n        je .next_char           \n        cmp cl, ','             ; Comma\n        je .next_char\n        cmp cl, 9               ; Tab\n        je .next_char\n        cmp cl, 10              ; Line Feed\n        je .next_char\n        cmp cl, 13              ; Carriage Return\n        je .next_char\n        \n        ; Special characters. These are returned in CL as-is\n        cmp cl, '('\n        je .found\n        cmp cl, ')'\n        je .found\n        cmp cl, '['\n        je .found\n        cmp cl, ']'\n        je .found\n        cmp cl, '{'\n        je .found\n        cmp cl, '}'\n        je .found\n        cmp cl, 39              ; character '\n        je .found\n        cmp cl, 96              ; character `\n        je .found\n        cmp cl, '^'\n        je .found\n        cmp cl, '@'\n        je .found\n        cmp cl, '~'             ; Could be followed by '@'\n        je .handle_tilde\n        \n        cmp cl, ';'             ; Start of a comment\n        je .comment\n\n        cmp cl, 34             ; Opening string quotes\n        je .handle_string\n\n        ; Could be number or symbol\n\n        cmp cl, '-'             ; Minus sign\n        je .handle_minus\n        mov ch, 0\n        \n        ; Check for a character 0-9\n        cmp cl, '0'\n        jl .handle_symbol\n        cmp cl, '9'\n        jg .handle_symbol\n\n        ; Here an integer\n        jmp .handle_integer\n\n.comment:\n        ; Start of a comment. Keep reading until a new line or end\n        \n        ; Fetch the next char into CL\n        call tokenizer_next_char\n        \n        cmp cl, 0\n        je .found               ; End, no more tokens\n\n        cmp cl, 10\n        je .next_char           ; Next line, start reading again\n\n        jmp .comment\n        \n.handle_minus:\n\n        ; Push current state of the tokenizer\n        push r9\n        push r10\n        push r11\n        \n        ; Get the next character\n        call tokenizer_next_char\n        \n        ; Check if it is a number\n        cmp cl, '0'\n        jl .minus_not_number\n        cmp cl, '9'\n        jg .minus_not_number\n\n        ; Here is a number\n        mov ch, '-'              ; Put '-' in ch for later\n\n        ; Discard old state by moving stack pointer\n        add rsp, 24             ; 3 * 8 bytes\n        \n        jmp .handle_integer\n        \n.minus_not_number:\n\n        ; Restore state\n        pop r11\n        pop r10\n        pop r9\n\n        mov cl, '-'             ; Put back\n\n        jmp .handle_symbol\n        \n.handle_integer:\n        ; Start integer\n        ; accumulate in EDX\n        xor edx, edx\n        \n.integer_loop:\n        ; Here have a char 0-9 in CL\n        sub cl, '0'            ; Convert to number between 0 and 9\n        movzx ebx, cl\n        add edx, ebx\n        \n        ; Push current state of the tokenizer\n        push r9\n        push r10\n        push r11\n        \n        ; Peek at next character\n        call tokenizer_next_char ; Next char in CL\n        \n        cmp cl, '0'\n        jl .integer_finished\n        cmp cl, '9'\n        jg .integer_finished\n\n        ; Discard old state by moving stack pointer\n        add rsp, 24             ; 3 * 8 bytes\n        \n        imul edx, 10\n        \n        jmp .integer_loop\n        \n.integer_finished:\n        ; Next char not an int\n        \n        ; Restore state of the tokenizer\n        pop r11\n        pop r10\n        pop r9\n        \n        push rdx                ; Save the integer\n        ; Get a Cons object to put the result into\n        call alloc_cons\n        \n        pop rdx                 ; Restore integer\n\n        ; Check if the number should be negative\n        cmp ch, '-'\n        jne .integer_store\n        neg rdx\n\n.integer_store:\n        ; Address of Cons now in RAX\n        mov [rax], BYTE maltype_integer\n\n        mov [rax + Cons.car], rdx\n        \n        mov cl, 'i'             ; Mark as an integer\n        ret\n\n        ; -------------------------------------------\n.handle_symbol:\n        ; Read characters until reaching whitespace, special character or end\n\n        call string_new         \n        mov rsi, rax  ; Output string in rsi\n        \n.symbol_loop:\n        ; Put the current character into the array\n        call string_append_char\n        \n        ; Push current state of the tokenizer\n        push r9\n        push r10\n        push r11\n        \n        call tokenizer_next_char\n        cmp cl, 0               ; End of characters\n        je .symbol_finished\n        \n        cmp cl, ' '             ; Space\n        je .symbol_finished           \n        cmp cl, ','             ; Comma\n        je .symbol_finished\n        cmp cl, 9               ; Tab\n        je .symbol_finished\n        cmp cl, 10              ; Line Feed\n        je .symbol_finished\n        cmp cl, 13              ; Carriage Return\n        je .symbol_finished\n        \n        cmp cl, '('\n        je .symbol_finished\n        cmp cl, ')'\n        je .symbol_finished\n        cmp cl, '['\n        je .symbol_finished\n        cmp cl, ']'\n        je .symbol_finished\n        cmp cl, '{'\n        je .symbol_finished\n        cmp cl, '}'\n        je .symbol_finished\n        cmp cl, 39              ; character '\n        je .symbol_finished\n        cmp cl, 96              ; character `\n        je .symbol_finished\n        cmp cl, '^'\n        je .symbol_finished\n        cmp cl, '@'\n        je .symbol_finished\n        cmp cl, '~'\n        je .symbol_finished\n        cmp cl, ';'             ; Start of a comment\n        je .symbol_finished\n        cmp cl, 34              ; Opening string quotes\n        je .symbol_finished\n\n        ; Keeping current character\n        ; Discard old state by moving stack pointer\n        add rsp, 24             ; 3 * 8 bytes\n\n        jmp .symbol_loop        ; Append to array\n        \n.symbol_finished:\n        ; Not keeping current character\n        ; Restore state of the tokenizer\n        pop r11\n        pop r10\n        pop r9\n\n        mov rax, rsi\n        mov [rax], BYTE maltype_symbol ; Mark as a symbol\n        mov cl, 's'                    ; used by read_str\n        ret\n\n        ; --------------------------------------------\n.handle_string:\n        ; Get an array to put the string into\n        \n        call string_new               ; Array in RAX\n        \n        ; Put start of data array into rbx\n        mov rbx, rax\n        add rbx, Array.data\n        ; Put end of data array into rdx\n        mov edx, DWORD [rax + Array.length] ; Length of array, zero-extended\n        add rdx, rbx\n        \n        ; Now read chars from input string and push into output\n.string_loop:\n        \n        call tokenizer_next_char\n        cmp cl, 0               ; End of characters\n        je .error\n        \n        cmp cl, 34              ; Finishing '\"'\n        je .string_done         ; Leave '\"' in CL\n\n        cmp cl, 92              ; Escape '\\'\n        jne .end_string_escape\n        \n        ; Current character is a '\\'\n        call tokenizer_next_char\n        cmp cl, 0               ; End of characters\n        je .error\n\n        cmp cl, 'n'             ; \\n, newline\n        je .insert_newline\n\n        ; Whatever is in cl is now put into string\n        ; including '\"'\n        jmp .end_string_escape\n        \n.insert_newline:\n        mov cl, 10\n        jmp .end_string_escape\n        \n.end_string_escape:\n\n        ; Put CL onto result array\n        ; NOTE: this doesn't handle long strings (multiple memory blocks)\n        mov [rbx], cl\n        inc rbx\n\n        jmp .string_loop\n\n.string_done:\n        ; Calculate the length from rbx\n        sub rbx, Array.data\n        sub rbx, rax\n        mov [rax+Array.length], DWORD ebx\n        ret\n        \n        ; ---------------------------------\n                \n.handle_tilde:\n        ; Could have '~' or '~@'. Need to peek at the next char\n\n        ; Push current state of the tokenizer\n        push r9\n        push r10\n        push r11\n        call tokenizer_next_char ; Next char in CL\n        cmp cl, '@'\n        jne .tilde_no_amp           ; Just '~', not '~@'\n        ; Got '~@'\n        mov cl, 1               ; Signals '~@'\n\n        ; Discard old state by moving stack pointer\n        add rsp, 24             ; 3 * 8 bytes\n        ret\n        \n.tilde_no_amp:\n        mov cl, '~'\n        ; Restore state of the tokenizer\n        pop r11\n        pop r10\n        pop r9\n        ; fall through to .found\n.found:\n        ret\n\n.error:\n        ret\n        \n"
  },
  {
    "path": "impls/nasm/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n\n"
  },
  {
    "path": "impls/nasm/step0_repl.asm",
    "content": ";;\n;; nasm -felf64 step0_repl.asm && ld step0_repl.o && ./a.out\n\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"system.asm\"           ; System calls\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n        \nsection .text\n\n;; Takes a string as input and processes it into a form\nread:\n        mov rax, rsi            ; Return the input\n        ret\n\n;; ----------------------------------------------\n;; Evaluates a form\neval:\n        mov rax, rsi            ; Return the input\n        ret\n        \n;; Prints the result\nprint:\n        mov rax, rsi            ; Return the input\n        ret\n\n;; Read-Eval-Print in sequence\nrep_seq:\n        ; -------------\n        ; Read\n        call read\n\n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Output of read into input of eval\n        call eval\n        \n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print \n        call print\n        \n        ret\n\n\n_start:\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n"
  },
  {
    "path": "impls/nasm/step1_read_print.asm",
    "content": ";; \n;; nasm -felf64 step1_read_print.asm && ld step1_read_print.o && ./a.out\n\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n        \nsection .text\n\n;; Takes a string as input and processes it into a form\nread:\n        jmp read_str           ; In reader.asm\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;; \neval:\n        mov rax, rsi            ; Return the input\n        ret\n        \n;; Prints the result\nprint:\n        mov rdi, 1              ; print readably\n        jmp pr_str\n\n;; Read-Eval-Print in sequence\nrep_seq:\n        ; -------------\n        ; Read\n        call read\n        push rax                ; Save form\n\n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Output of read into input of eval\n        call eval\n        \n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        call print              ; String in RAX\n\n        mov r8, rax             ; Save output\n        pop rsi                 ; Form returned by read\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n"
  },
  {
    "path": "impls/nasm/step2_eval.asm",
    "content": ";; \n;; nasm -felf64 step2_eval.asm && ld step2_eval.o && ./a.out\n\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        \n        static core_add_symbol, db \"+\"  \n        static core_sub_symbol, db \"-\"\n        static core_mul_symbol, db \"*\"\n        static core_div_symbol, db \"/\"\n        \nsection .text\n\n;; Integer arithmetic operations\n;; \n;; Adds a list of numbers, address in RSI\n;; Returns the sum as a number object with address in RAX\n;; Since most of the code is common to all operators,\n;; RBX is used to jump to the required instruction\ncore_add:\n        mov rbx, core_arithmetic.do_addition\n        jmp core_arithmetic\ncore_sub:\n        mov rbx, core_arithmetic.do_subtraction\n        jmp core_arithmetic\ncore_mul:\n        mov rbx, core_arithmetic.do_multiply\n        jmp core_arithmetic\ncore_div:\n        mov rbx, core_arithmetic.do_division\n        ; Fall through to core_arithmetic\ncore_arithmetic:\n        ; Check that the first object is a number\n        mov cl, BYTE [rsi]\n        mov ch, cl\n        and ch, block_mask\n        cmp ch, block_cons\n        jne .missing_args\n\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_empty\n        je .missing_args\n\n        cmp ch, content_int\n        jne .not_int\n\n        ; Put the starting value in rax\n        mov rax, [rsi + Cons.car]\n        \n.add_loop:\n        ; Fetch the next value\n        mov cl, [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .finished  ; Nothing let\n        \n        mov rsi, [rsi + Cons.cdr] ; Get next cons\n\n        ; Check that it is an integer\n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        cmp cl, content_int\n        jne .not_int\n\n        ; Jump to the required operation, address in RBX\n        jmp rbx\n        \n.do_addition:\n        add rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_subtraction:\n        sub rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_multiply:\n        imul rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_division:\n        cqo                     ; Sign extend RAX into RDX\n        mov rcx, [rsi + Cons.car]\n        idiv rcx\n        jmp .add_loop\n        \n.finished:\n        ; Value in rbx\n        push rax\n        ; Get a Cons object to put the result into\n        call alloc_cons\n        pop rbx\n        mov [rax], BYTE maltype_integer\n        mov [rax + Cons.car], rbx\n        ret\n        \n.missing_args:\n.not_int:\n        jmp quit\n        \n;; Add a native function to the core environment\n;; This is used in core_environment\n%macro core_env_native 2\n        push rsi                ; environment\n        mov rsi, %1\n        mov edx, %1.len\n        call raw_to_symbol      ; Symbol in RAX\n        push rax\n        \n        mov rsi, %2\n        call native_function    ; Function in RAX\n        \n        mov rcx, rax            ; value (function)\n        pop rdi                 ; key (symbol)\n        pop rsi                 ; environment\n        call map_set\n%endmacro\n\n        \n        \n;; Takes a string as input and processes it into a form\nread:\n        jmp read_str           ; In reader.asm\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;; \neval_ast:\n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; look in environment\n        mov rdi, rsi            ; symbol is the key\n        mov rsi, [repl_env]     ; Environment\n        call map_get\n        je .done                ; result in RAX\n        \n        ; Not found, should raise an error\n        \n        ; Return nil\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        mov rsi, [rsi + Cons.car] ; Get the address\n        call eval             ; Evaluate it, result in rax\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_append\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        ; Fall through to .list_append\n        \n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        mov rsi, [r10 + Cons.car] ; Get the address\n        call eval               ; Evaluate it, result in rax\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        mov rsi, [rsi + Cons.car] ; Get the address\n        call eval             ; Evaluate it, result in rax\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_append_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_append_value:\n        or bl, container_vector\n        mov [rax], BYTE bl\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate\n;;\n;; Returns: Result in RAX\n;;\neval:\n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        ret\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if the first element is a symbol\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        push rsi\n\n        ; Compare against def!\n        mov rsi, rbx\n        mov rdi, def_symbol\n        call compare_char_array\n        pop rsi\n        cmp rax, 0\n        je .def_symbol\n        \n        push rsi\n        mov rdi, let_symbol\n        call compare_char_array\n        pop rsi\n        cmp rax, 0\n        je .let_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n        \n.def_symbol:\n        ; Define a new symbol in current environment\n\n        jmp .list_not_function\n.let_symbol:\n        ; Create a new environment\n        \n        jmp .list_not_function\n.list_eval:\n        \n        call eval_ast\n\n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n        \n        ; Call the function with the rest of the list in RSI\n        push rax\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n        mov rdi, rbx ; Function object in RDI\n        call [rbx + Cons.car]   ; Call function\n        ; Result in rax\n        pop rsi                 ; eval'ed list\n        push rax\n        call release_cons\n        pop rax\n        ret\n\n.list_not_function:\n        ; Not a function. Probably an error\n        ret\n        \n.empty_list:\n        mov rax, rsi\n        ret\n        \n;; Prints the result\nprint:\n        mov rdi, 1              ; print readably\n        jmp pr_str\n\n;; Read-Eval-Print in sequence\nrep_seq:\n        ; -------------\n        ; Read\n        call read\n        push rax                ; Save form\n\n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Output of read into input of eval\n        call eval\n        push rax                ; Save result\n        \n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        call print              ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        pop rsi                 ; Form returned by read\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call map_new   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        mov rsi, rax            ; Environment\n\n        ; Add +,-,*,/ to environment\n        core_env_native core_add_symbol, core_add\n        core_env_native core_sub_symbol, core_sub\n        core_env_native core_mul_symbol, core_mul\n        core_env_native core_div_symbol, core_div\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n"
  },
  {
    "path": "impls/nasm/step3_env.asm",
    "content": ";; \n;; nasm -felf64 step3_env.asm && ld step3_env.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n\n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        \n        static core_add_symbol, db \"+\"  \n        static core_sub_symbol, db \"-\"\n        static core_mul_symbol, db \"*\"\n        static core_div_symbol, db \"/\"\n        \nsection .text\n\n;; Integer arithmetic operations\n;; \n;; Adds a list of numbers, address in RSI\n;; Returns the sum as a number object with address in RAX\n;; Since most of the code is common to all operators,\n;; RBX is used to jump to the required instruction\ncore_add:\n        mov rbx, core_arithmetic.do_addition\n        jmp core_arithmetic\ncore_sub:\n        mov rbx, core_arithmetic.do_subtraction\n        jmp core_arithmetic\ncore_mul:\n        mov rbx, core_arithmetic.do_multiply\n        jmp core_arithmetic\ncore_div:\n        mov rbx, core_arithmetic.do_division\n        ; Fall through to core_arithmetic\ncore_arithmetic:\n        ; Check that the first object is a number\n        mov cl, BYTE [rsi]\n        mov ch, cl\n        and ch, block_mask\n        cmp ch, block_cons\n        jne .missing_args\n\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_empty\n        je .missing_args\n\n        cmp ch, content_int\n        jne .not_int\n\n        ; Put the starting value in rax\n        mov rax, [rsi + Cons.car]\n        \n.add_loop:\n        ; Fetch the next value\n        mov cl, [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .finished  ; Nothing let\n        \n        mov rsi, [rsi + Cons.cdr] ; Get next cons\n\n        ; Check that it is an integer\n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        cmp cl, content_int\n        jne .not_int\n\n        ; Jump to the required operation, address in RBX\n        jmp rbx\n        \n.do_addition:\n        add rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_subtraction:\n        sub rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_multiply:\n        imul rax, [rsi + Cons.car]\n        jmp .add_loop\n.do_division:\n        cqo                     ; Sign extend RAX into RDX\n        mov rcx, [rsi + Cons.car]\n        idiv rcx\n        jmp .add_loop\n        \n.finished:\n        ; Value in rbx\n        push rax\n        ; Get a Cons object to put the result into\n        call alloc_cons\n        pop rbx\n        mov [rax], BYTE maltype_integer\n        mov [rax + Cons.car], rbx\n        ret\n        \n.missing_args:\n.not_int:\n        jmp quit\n        \n;; Add a native function to the core environment\n;; This is used in core_environment\n%macro core_env_native 2\n        push rsi                ; environment\n        mov rsi, %1\n        mov edx, %1.len\n        call raw_to_symbol      ; Symbol in RAX\n        push rax\n        \n        mov rsi, %2\n        call native_function    ; Function in RAX\n        \n        mov rcx, rax            ; value (function)\n        pop rdi                 ; key (symbol)\n        pop rsi                 ; environment\n        call env_set\n%endmacro\n\n        \n        \n;; Takes a string as input and processes it into a form\nread:\n        jmp read_str           ; In reader.asm\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        push rsi\n        print_str_mac error_string ; print 'Error: ' \n        \n        pop rsi\n        push rsi\n        mov edx, [rsi + Array.length]\n        add rsi, Array.data\n        call print_rawstring    ; print symbol\n        \n        print_str_mac not_found_string ; print ' not found'\n        pop rsi\n\n        jmp error_throw\n\n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate\n;;        RDI   Environment\n;;\n;; Returns: Result in RAX\n;;\neval:\n        mov r15, rdi            ; Env\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        ret\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if the first element is a symbol\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n        \n.def_symbol:\n        ; Define a new symbol in current environment\n                \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n        call eval\n        mov rsi, rax\n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n        \n        mov rax, rcx            ; Return the value\n        ret\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n        mov rsi, [r12 + Cons.car] ; Get the address\n        mov rdi, r14\n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        mov rdi, r14            ; New environment\n        push r14\n        call eval\n        pop r14\n        \n.let_done:\n        ; Release the environment\n        mov rsi, r14\n        push rax\n        call release_object\n        pop rax\n        ret\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n        \n        ; Call the function with the rest of the list in RSI\n        push rax\n        push r15\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n        mov rdi, rbx ; Function object in RDI\n        call [rbx + Cons.car]   ; Call function\n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        push rax\n        call release_cons\n        pop rax\n        ret\n\n.list_not_function:\n        ; Not a function. Probably an error\n        ret\n        \n.empty_list:\n        mov rax, rsi\n        ret\n        \n;; Prints the result\nprint:\n        mov rdi, 1              ; print readably\n        jmp pr_str\n\n;; Read-Eval-Print in sequence\nrep_seq:\n        ; -------------\n        ; Read\n        call read\n        push rax                ; Save form\n\n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n        call eval\n        push rax                ; Save result\n        \n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        call print              ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        pop rsi                 ; Form returned by read\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call env_new   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        mov rsi, rax            ; Environment\n\n        ; Add +,-,*,/ to environment\n        core_env_native core_add_symbol, core_add\n        core_env_native core_sub_symbol, core_sub\n        core_env_native core_mul_symbol, core_mul\n        core_env_native core_div_symbol, core_div\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n"
  },
  {
    "path": "impls/nasm/step4_if_fn_do.asm",
    "content": ";; \n;; nasm -felf64 step4_if_fn_do.asm && ld step4_if_fn_do.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n        \n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(def! not (fn* (a) (if a false true)))\"\n        \n        \nsection .text   \n\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        push rsi\n        print_str_mac error_string ; print 'Error: ' \n        \n        pop rsi\n        push rsi\n        mov edx, [rsi + Array.length]\n        add rsi, Array.data\n        call print_rawstring    ; print symbol\n        \n        print_str_mac not_found_string ; print ' not found'\n        pop rsi\n\n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        ret\n        \n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate\n;;        RDI   Environment\n;;\n;; Returns: Result in RAX\n;;\neval:\n        mov r15, rdi            ; Env\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        ret\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if the first element is a symbol\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.def_symbol:\n        ; Define a new symbol in current environment\n                \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n        call eval\n        mov rsi, rax\n\n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n        \n        mov rax, rcx            ; Return the value\n        ret\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n        mov rsi, [r12 + Cons.car] ; Get the address\n        mov rdi, r14\n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        mov rdi, r14            ; New environment\n        push r14\n        call eval\n        pop r14\n        \n.let_done:\n        ; Release the environment\n        mov rsi, r14\n        push rax\n        call release_object\n        pop rax\n        ret\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body\n        mov r11, [r11 + Cons.cdr]\n\n.do_symbol_loop:\n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value\n        \n        ; A pointer, so evaluate\n        push r15\n        push r11\n        mov rsi, [r11 + Cons.car]  ; Form\n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Check if there is another form\n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_done            ; No more, so finished\n\n        ; Another form. Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n        \n.do_done:\n        ret                     ; Return result in RAX\n        \n.do_body_value:\n\n        ; Got a value in R11.\n        ; If this is the last form then return,\n        ; but if not then can ignore\n\n        mov bl, BYTE [r11 + Cons.typecdr]\n        and bl, block_mask + content_mask\n        cmp bl, content_pointer\n        jne .do_body_value_return\n\n        ; Not the last, so ignore\n        jmp .do_next\n\n.do_body_value_return:\n        ; Got a value as last form. Copy and return\n\n        push rax\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n        ret\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n        mov rsi, [r11 + Cons.car]  ; Form\n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n        ret\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        mov rdi, r15            ; Env\n        call eval\n        ret\n        \n.if_no_condition:               ; just (if) without a condition\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r13 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        ret\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        pop rbx\n        mov rsi, rax\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        push rax\n        push r15\n        mov rdi, rbx ; Function object in RDI\n        call [rbx + Cons.car]   ; Call function\n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        push rax\n        call release_cons\n        pop rax\n        ret\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        ret\n\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;\n;; \n;; Output: Result in RAX\n;;\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        push rax\n        mov rsi, rax\n        call incref_object\n        pop rax\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax\n        call env_new_bind\n        mov rdi, rax       ; New environment in RDI\n        pop rsi            ; Body\n\n        ; Evaluate the function body\n        push rdi                ; Environment\n        call eval\n        pop rsi\n\n        ; Release the environment\n        push rax\n        call release_object\n        pop rax\n        \n        ret\n        \n\n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        push rax                ; Save form\n\n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n        call eval\n        push rax                ; Save result\n        \n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        pop rsi                 ; Form returned by read\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rsi                 ; AST\n\n        push rsi\n        mov rdi, [repl_env]     ; Environment\n        call eval\n        pop rsi\n        \n        push rax\n        call release_object     ; AST\n        pop rsi\n        call release_object     ; Return from eval\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n"
  },
  {
    "path": "impls/nasm/step5_tco.asm",
    "content": ";; \n;; nasm -felf64 step5_tco.asm && ld step5_tco.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n\n        static if_missing_condition_string, db \"missing condition in if expression\",10\n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(def! not (fn* (a) (if a false true)))\"\n        \n        \nsection .text   \n\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        push rsi\n        print_str_mac error_string ; print 'Error: ' \n        \n        pop rsi\n        push rsi\n        mov edx, [rsi + Array.length]\n        add rsi, Array.data\n        call print_rawstring    ; print symbol\n        \n        print_str_mac not_found_string ; print ' not found'\n        pop rsi\n\n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        ret\n        \n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate   [ Released ]\n;;        RDI   Environment       [ Released ]\n;;\n;; Returns: Result in RAX\n;;\n;; Note: Both the form and environment will have their reference count\n;; reduced by one (released). This is for tail call optimisation (Env),\n;; quasiquote and macroexpand (AST)\n;; \neval:\n        mov r15, rdi            ; Env\n\n        push rsi                ; AST pushed, must be popped before return\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        jmp .return             ; Releases Env\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if the first element is a symbol\n        mov al, BYTE [rsi]\n        \n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.def_symbol:\n        ; Define a new symbol in current environment\n                \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        mov rsi, rax\n\n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n\n        mov rax, rcx\n        jmp .return\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n\n        mov rsi, r15\n        call release_object     ; Decrement R15 ref count\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n\n        mov rsi, r14\n        call incref_object\n        mov rdi, r14\n        \n        mov rsi, [r12 + Cons.car] ; Get the address\n        \n        call incref_object      ; Increment ref count of AST\n        \n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        call incref_object        ; will be released by eval\n        \n        mov r11, rsi              ; save new AST\n        pop rsi                   ; Old AST\n        call release_object\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r14            ; New environment\n\n        jmp eval                ; Tail call\n        ; Note: eval will release the new environment on return\n        \n.let_done:\n        ; Release the new environment\n        push rax\n        mov rsi, r14\n        call release_object\n        pop rax\n\n        ; Release the AST\n        pop rsi\n        push rax\n        call release_object\n        pop rax\n        ret                     ; already released env\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        ; Check if there is a body\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body         ; error\n\n        mov r11, [r11 + Cons.cdr] ; Body in R11\n        \n.do_symbol_loop:\n\n        ; Need to test if this is the last form\n        ; so we can handle tail call\n        \n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_body_last ; Last expression\n\n        ; not the last expression\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_next            ; A value, so skip\n        \n        ; A pointer, so evaluate\n        \n        push r15\n        push r11\n        \n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n                                ; since eval will release Env\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increment ref count since eval will release\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        \n        ; Another form after this.\n        ; Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n        \n        pop r11\n        pop r15\n        \n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n                \n.do_body_last:\n        ; The last form is in R11, which will be returned\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value_return\n        jmp .do_body_expr_return\n\n.do_body_value_return:\n        ; Got a value as last form (in R11).\n        ; Copy and return\n\n        push rax                ; Type of value to return\n\n        ; release Env\n        mov rsi, r15\n        call release_object\n\n        ; Allocate a Cons object to hold value\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        ; release the AST\n        pop rsi\n        mov r15, rax            ; not modified by release\n        call release_object\n        mov rax, r15\n        \n        ret\n\n.do_body_expr_return:\n        ; An expression to evaluate as the last form\n        ; Tail call optimise, jumping to eval\n        ; Don't increment Env reference count\n        \n        mov rsi, [r11 + Cons.car]  ; new AST form\n        call incref_object         ; This will be released by eval\n\n        mov r11, rsi              ; Save new AST\n        pop rsi                   ; Remove old AST from stack\n        call release_object\n        mov rsi, r11\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        mov rsi, r15\n        call release_object     ; Release Env\n\n        ; release the AST\n        pop rsi\n        call release_object\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n\n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increase Form/AST ref count\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .return\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object        ; Will be released by eval\n\n        mov r11, rsi\n        pop rsi\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.if_no_condition:               ; just (if) without a condition\n        \n        print_str_mac error_string\n        print_str_mac if_missing_condition_string\n        \n        ; Release environment\n        mov rsi, r15\n        call release_object\n        xor rsi, rsi            ; No object to throw\n        jmp error_throw\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n.return:\n        ; Release environment\n        mov rsi, r15\n        mov r15, rax            ; Save RAX (return value)\n        call release_object\n\n        ; Release the AST\n        pop rsi                 ; Pushed at start of eval\n        call release_object\n\n        mov rax, r15            ; return value\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r13 + Cons.cdr], rax ; Append to list\n        mov r14, rax              ; R14 contains last cons in list\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        jmp .return\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        jmp .return\n        \n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n.list_exec:\n        ; This point can be called to run a function\n        ; used by swap!\n        ; \n        ; Inputs: RAX - List with function as first element\n        ;               NOTE: This list is released\n        ; \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx                ; Function object\n        push rax                ; List with function first\n\n        ; Create an empty list for the arguments\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax            ; Argument list into RSI\n\n        pop rax                 ; list, function first\n        ;;  Put new empty list onto end of original list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n        \n        pop rbx\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        \n        mov rdx, rax            ; List to release\n        mov rdi, rbx ; Function object in RDI\n\n        mov rbx, [rbx + Cons.car]   ; Call function\n        cmp rbx, apply_fn\n        je apply_fn_jmp             ; Jump to user function apply\n        \n        ; A built-in function, so call (no recursion)\n        push rax\n        push r15\n\n        call rbx\n        \n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        \n        push rax\n        call release_cons\n        pop rax \n        jmp .return             ; Releases Env\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        jmp .return\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;        RDX - list to release after binding\n;;        R15 - Env (will be released)\n;;        R13 - AST released before return\n;;\n;; \n;; Output: Result in RAX\n;;\n;; This is jumped to from eval, so if it returns\n;; then it will return to the caller of eval, not to eval\napply_fn_jmp:\n        ; This is jumped to from eval with AST on the stack\n        pop r13\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        mov r14, rax            ; Save return value in R14\n        \n        mov rsi, rax\n        call incref_object\n\n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_object\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the AST\n        mov rsi, r13\n        call release_object\n        \n        mov rax, r14\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax                ; Body\n        \n        mov r14, r13            ; Old AST. R13 used by env_new_bind\n        \n        push rdx\n        call env_new_bind\n        pop rdx\n\n        mov rdi, rax       ; New environment in RDI\n\n        ; Note: Need to increment the reference count\n        ; of the function body before releasing anything,\n        ; since if the function was defined in-place (lambda)\n        ; then the body may be released early\n        \n        pop rsi            ; Body\n        call incref_object ; Will be released by eval\n        mov r8, rsi        ; Body in R8\n        \n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_cons\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the old AST\n        mov rsi, r14\n        call release_object\n\n        mov rsi, r8             ; Body\n        \n        jmp eval           ; Tail call\n        ; The new environment (in RDI) will be released by eval\n        \n\n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        call eval               ; This releases Env and Form/AST\n        push rax                ; Save result of eval\n\n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rdi                 ; AST in RDI\n        \n        mov rsi, [repl_env]     ; Environment in RSI\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call eval\n        \n        mov rsi, rax\n        call release_object     ; Return from eval\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n"
  },
  {
    "path": "impls/nasm/step6_file.asm",
    "content": ";; \n;; nasm -felf64 step6_file.asm && ld step6_file.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n\n        static if_missing_condition_string, db \"missing condition in if expression\",10\n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n\n        static_symbol argv_symbol, '*ARGV*'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(do \\\n(def! not (fn* (a) (if a false true))) \\\n(def! load-file (fn* (f) (eval (read-string (str \",34,\"(do\",34,\"  (slurp f) \",34,10,\"nil)\",34,\" ))))) \\\n)\"\n\n;; Command to run, appending the name of the script to run\n        static run_script_string, db \"(load-file \",34\nsection .text\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        push rsi\n        print_str_mac error_string ; print 'Error: ' \n        \n        pop rsi\n        push rsi\n        mov edx, [rsi + Array.length]\n        add rsi, Array.data\n        call print_rawstring    ; print symbol\n        \n        print_str_mac not_found_string ; print ' not found'\n        pop rsi\n\n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        ret\n        \n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate   [ Released ]\n;;        RDI   Environment       [ Released ]\n;;\n;; Returns: Result in RAX\n;;\n;; Note: Both the form and environment will have their reference count\n;; reduced by one (released). This is for tail call optimisation (Env),\n;; quasiquote and macroexpand (AST)\n;; \neval:\n        mov r15, rdi            ; Env\n\n        push rsi                ; AST pushed, must be popped before return\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        jmp .return             ; Releases Env\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if the first element is a symbol\n        mov al, BYTE [rsi]\n        \n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.def_symbol:\n        ; Define a new symbol in current environment\n                \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        mov rsi, rax\n\n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n\n        mov rax, rcx\n        jmp .return\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n\n        mov rsi, r15\n        call release_object     ; Decrement R15 ref count\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n\n        mov rsi, r14\n        call incref_object\n        mov rdi, r14\n        \n        mov rsi, [r12 + Cons.car] ; Get the address\n        \n        call incref_object      ; Increment ref count of AST\n        \n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        call incref_object        ; will be released by eval\n        \n        mov r11, rsi              ; save new AST\n        pop rsi                   ; Old AST\n        call release_object\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r14            ; New environment\n\n        jmp eval                ; Tail call\n        ; Note: eval will release the new environment on return\n        \n.let_done:\n        ; Release the new environment\n        push rax\n        mov rsi, r14\n        call release_object\n        pop rax\n\n        ; Release the AST\n        pop rsi\n        push rax\n        call release_object\n        pop rax\n        ret                     ; already released env\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        ; Check if there is a body\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body         ; error\n\n        mov r11, [r11 + Cons.cdr] ; Body in R11\n        \n.do_symbol_loop:\n\n        ; Need to test if this is the last form\n        ; so we can handle tail call\n        \n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_body_last ; Last expression\n\n        ; not the last expression\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_next            ; A value, so skip\n        \n        ; A pointer, so evaluate\n        \n        push r15\n        push r11\n        \n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n                                ; since eval will release Env\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increment ref count since eval will release\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        \n        ; Another form after this.\n        ; Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n        \n        pop r11\n        pop r15\n        \n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n                \n.do_body_last:\n        ; The last form is in R11, which will be returned\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value_return\n        jmp .do_body_expr_return\n\n.do_body_value_return:\n        ; Got a value as last form (in R11).\n        ; Copy and return\n\n        push rax                ; Type of value to return\n\n        ; release Env\n        mov rsi, r15\n        call release_object\n\n        ; Allocate a Cons object to hold value\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        ; release the AST\n        pop rsi\n        mov r15, rax            ; not modified by release\n        call release_object\n        mov rax, r15\n        \n        ret\n\n.do_body_expr_return:\n        ; An expression to evaluate as the last form\n        ; Tail call optimise, jumping to eval\n        ; Don't increment Env reference count\n        \n        mov rsi, [r11 + Cons.car]  ; new AST form\n        call incref_object         ; This will be released by eval\n\n        mov r11, rsi              ; Save new AST\n        pop rsi                   ; Remove old AST from stack\n        call release_object\n        mov rsi, r11\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        mov rsi, r15\n        call release_object     ; Release Env\n\n        ; release the AST\n        pop rsi\n        call release_object\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n\n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increase Form/AST ref count\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .return\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object        ; Will be released by eval\n\n        mov r11, rsi\n        pop rsi\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.if_no_condition:               ; just (if) without a condition\n        \n        print_str_mac error_string\n        print_str_mac if_missing_condition_string\n        \n        ; Release environment\n        mov rsi, r15\n        call release_object\n        xor rsi, rsi            ; No object to throw\n        jmp error_throw\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n.return:\n        ; Release environment\n        mov rsi, r15\n        mov r15, rax            ; Save RAX (return value)\n        call release_object\n\n        ; Release the AST\n        pop rsi                 ; Pushed at start of eval\n        call release_object\n\n        mov rax, r15            ; return value\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r13 + Cons.cdr], rax ; Append to list\n        mov r14, rax              ; R14 contains last cons in list\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        jmp .return\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        jmp .return\n        \n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n.list_exec:\n        ; This point can be called to run a function\n        ; used by swap!\n        ; \n        ; Inputs: RAX - List with function as first element\n        ;               NOTE: This list is released\n        ; \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx                ; Function object\n        push rax                ; List with function first\n\n        ; Create an empty list for the arguments\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax            ; Argument list into RSI\n\n        pop rax                 ; list, function first\n        ;;  Put new empty list onto end of original list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n        \n        pop rbx\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        \n        mov rdx, rax            ; List to release\n        mov rdi, rbx ; Function object in RDI\n\n        mov rbx, [rbx + Cons.car]   ; Call function\n        cmp rbx, apply_fn\n        je apply_fn_jmp             ; Jump to user function apply\n        \n        ; A built-in function, so call (no recursion)\n        push rax\n        push r15\n\n        call rbx\n        \n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        \n        push rax\n        call release_cons\n        pop rax \n        jmp .return             ; Releases Env\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        jmp .return\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;        RDX - list to release after binding\n;;        R15 - Env (will be released)\n;;        R13 - AST released before return\n;;\n;; \n;; Output: Result in RAX\n;;\n;; This is jumped to from eval, so if it returns\n;; then it will return to the caller of eval, not to eval\napply_fn_jmp:\n        ; This is jumped to from eval with AST on the stack\n        pop r13\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        mov r14, rax            ; Save return value in R14\n        \n        mov rsi, rax\n        call incref_object\n\n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_object\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the AST\n        mov rsi, r13\n        call release_object\n        \n        mov rax, r14\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax                ; Body\n        \n        mov r14, r13            ; Old AST. R13 used by env_new_bind\n        \n        push rdx\n        call env_new_bind\n        pop rdx\n\n        mov rdi, rax       ; New environment in RDI\n\n        ; Note: Need to increment the reference count\n        ; of the function body before releasing anything,\n        ; since if the function was defined in-place (lambda)\n        ; then the body may be released early\n        \n        pop rsi            ; Body\n        call incref_object ; Will be released by eval\n        mov r8, rsi        ; Body in R8\n        \n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_cons\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the old AST\n        mov rsi, r14\n        call release_object\n\n        mov rsi, r8             ; Body\n        \n        jmp eval           ; Tail call\n        ; The new environment (in RDI) will be released by eval\n        \n;; Read and eval\nread_eval:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        jmp eval               ; This releases Env and Form/AST\n        \n        \n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        call eval               ; This releases Env and Form/AST\n        push rax                ; Save result of eval\n\n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rdi                 ; AST in RDI\n        \n        mov rsi, [repl_env]     ; Environment in RSI\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call eval\n        \n        mov rsi, rax\n        call release_object     ; Return from eval\n\n        ; -----------------------------\n        ; Check command-line arguments\n\n        pop rax                 ; Number of arguments\n        cmp rax, 1              ; Always have at least one, the path to executable\n        jg run_script\n\n        ; No extra arguments, so just set *ARGV* to an empty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov rcx, rax            ; value (empty list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n\n        \nrun_script:\n        ; Called with number of command-line arguments in RAX\n        mov r8, rax\n        pop rbx                 ; executable\n        dec r8\n        \n        pop rsi                  ; Address of first arg\n        call cstring_to_string   ; string in RAX\n        mov r9, rax\n        \n        ; get the rest of the args\n        xor r10, r10            ; Zero       \n        dec r8\n        jz .no_args\n        \n        ; Got some arguments\n.arg_loop:  \n        ; Got an argument left. \n        pop rsi                 ; Address of C string\n        call cstring_to_string  ; String in RAX\n        mov r12, rax\n        \n        ;Make a Cons to point to the string\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r12\n        \n        test r10, r10\n        jnz .append\n\n        ; R10 zero, so first arg\n        mov r10, rax            ; Head of list\n        mov r11, rax            ; Tail of list\n        jmp .next\n.append:\n        ; R10 not zero, so append to list tail\n        mov [r11 + Cons.cdr], rax\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        mov r11, rax\n.next:\n        dec r8\n        jnz .arg_loop\n        jmp .got_args\n        \n.no_args:\n        ; No arguments. Create an emoty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov r10, rax\n        \n.got_args:\n        push r9                 ; File name string\n        \n        mov rcx, r10            ; value (list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        mov rsi, run_script_string ; load-file function\n        mov edx, run_script_string.len\n        call raw_to_string      ; String in RAX\n\n        mov rsi, rax\n        pop rdx                 ; File name string\n        call string_append_string\n\n        mov cl, 34              ; \"\n        call string_append_char\n        mov cl, ')'\n        call string_append_char ; closing brace\n\n        ; Read-Eval \"(load-file <file>)\"\n        call read_eval \n\n        jmp quit\n"
  },
  {
    "path": "impls/nasm/step7_quote.asm",
    "content": ";; \n;; nasm -felf64 step7_quote.asm && ld step7_quote.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n\n        static if_missing_condition_string, db \"missing condition in if expression\",10\n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n\n        static_symbol argv_symbol, '*ARGV*'\n\n        static_symbol quote_symbol, 'quote'\n        static_symbol quasiquote_symbol, 'quasiquote'\n        static_symbol quasiquoteexpand_symbol, 'quasiquoteexpand'\n        static_symbol unquote_symbol, 'unquote'\n        static_symbol splice_unquote_symbol, 'splice-unquote'\n        static_symbol concat_symbol, 'concat'\n        static_symbol cons_symbol, 'cons'\n        static_symbol vec_symbol, 'vec'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(do \\\n(def! not (fn* (a) (if a false true))) \\\n(def! load-file (fn* (f) (eval (read-string (str \",34,\"(do\",34,\"  (slurp f) \",34,10,\"nil)\",34,\" ))))) \\\n)\"\n\n;; Command to run, appending the name of the script to run\n        static run_script_string, db \"(load-file \",34\nsection .text\n\n\n;;; Extract the car of a Cons and increment its reference count.\n;;; If it was value, create a fresh copy.\n;;; in        : rsi (which must be a pointer!)\n;;; out       : rsi\n;;; modified: : cl, rax, rbx\ncar_and_incref:\n        mov cl, BYTE [rsi + Cons.typecar]\n        and cl, content_mask\n\n        mov rsi, [rsi + Cons.car]\n\n        cmp cl, content_pointer\n        je incref_object\n\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE cl ; masked above\n        mov [rax + Cons.car],     rsi\n        mov rsi, rax\n        ret\n\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        push rsi\n        print_str_mac error_string ; print 'Error: ' \n        \n        pop rsi\n        push rsi\n        mov edx, [rsi + Array.length]\n        add rsi, Array.data\n        call print_rawstring    ; print symbol\n        \n        print_str_mac not_found_string ; print ' not found'\n        pop rsi\n\n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        ret\n        \n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate   [ Released ]\n;;        RDI   Environment       [ Released ]\n;;\n;; Returns: Result in RAX\n;;\n;; Note: Both the form and environment will have their reference count\n;; reduced by one (released). This is for tail call optimisation (Env),\n;; quasiquote and macroexpand (AST)\n;; \neval:\n        mov r15, rdi            ; Env\n\n        push rsi                ; AST pushed, must be popped before return\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        jmp .return             ; Releases Env\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if the first element is a symbol\n        mov al, BYTE [rsi]\n        \n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n\n        eval_cmp_symbol quote_symbol ; quote\n        je .quote_symbol\n\n        eval_cmp_symbol quasiquoteexpand_symbol\n        je .quasiquoteexpand_symbol\n\n        eval_cmp_symbol quasiquote_symbol ; quasiquote\n        je .quasiquote_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.def_symbol:\n        ; Define a new symbol in current environment\n                \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        mov rsi, rax\n\n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n\n        mov rax, rcx\n        jmp .return\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n\n        mov rsi, r15\n        call release_object     ; Decrement R15 ref count\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n\n        mov rsi, r14\n        call incref_object\n        mov rdi, r14\n        \n        mov rsi, [r12 + Cons.car] ; Get the address\n        \n        call incref_object      ; Increment ref count of AST\n        \n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        call incref_object        ; will be released by eval\n        \n        mov r11, rsi              ; save new AST\n        pop rsi                   ; Old AST\n        call release_object\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r14            ; New environment\n\n        jmp eval                ; Tail call\n        ; Note: eval will release the new environment on return\n        \n.let_done:\n        ; Release the new environment\n        push rax\n        mov rsi, r14\n        call release_object\n        pop rax\n\n        ; Release the AST\n        pop rsi\n        push rax\n        call release_object\n        pop rax\n        ret                     ; already released env\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        ; Check if there is a body\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body         ; error\n\n        mov r11, [r11 + Cons.cdr] ; Body in R11\n        \n.do_symbol_loop:\n\n        ; Need to test if this is the last form\n        ; so we can handle tail call\n        \n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_body_last ; Last expression\n\n        ; not the last expression\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_next            ; A value, so skip\n        \n        ; A pointer, so evaluate\n        \n        push r15\n        push r11\n        \n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n                                ; since eval will release Env\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increment ref count since eval will release\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        \n        ; Another form after this.\n        ; Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n        \n        pop r11\n        pop r15\n        \n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n                \n.do_body_last:\n        ; The last form is in R11, which will be returned\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value_return\n        jmp .do_body_expr_return\n\n.do_body_value_return:\n        ; Got a value as last form (in R11).\n        ; Copy and return\n\n        push rax                ; Type of value to return\n\n        ; release Env\n        mov rsi, r15\n        call release_object\n\n        ; Allocate a Cons object to hold value\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        ; release the AST\n        pop rsi\n        mov r15, rax            ; not modified by release\n        call release_object\n        mov rax, r15\n        \n        ret\n\n.do_body_expr_return:\n        ; An expression to evaluate as the last form\n        ; Tail call optimise, jumping to eval\n        ; Don't increment Env reference count\n        \n        mov rsi, [r11 + Cons.car]  ; new AST form\n        call incref_object         ; This will be released by eval\n\n        mov r11, rsi              ; Save new AST\n        pop rsi                   ; Remove old AST from stack\n        call release_object\n        mov rsi, r11\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        mov rsi, r15\n        call release_object     ; Release Env\n\n        ; release the AST\n        pop rsi\n        call release_object\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n\n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increase Form/AST ref count\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .return\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object        ; Will be released by eval\n\n        mov r11, rsi\n        pop rsi\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.if_no_condition:               ; just (if) without a condition\n        \n        print_str_mac error_string\n        print_str_mac if_missing_condition_string\n        \n        ; Release environment\n        mov rsi, r15\n        call release_object\n        xor rsi, rsi            ; No object to throw\n        jmp error_throw\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n.return:\n        ; Release environment\n        mov rsi, r15\n        mov r15, rax            ; Save RAX (return value)\n        call release_object\n\n        ; Release the AST\n        pop rsi                 ; Pushed at start of eval\n        call release_object\n\n        mov rax, r15            ; return value\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r13 + Cons.cdr], rax ; Append to list\n        mov r14, rax              ; R14 contains last cons in list\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        jmp .return\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        jmp .return\n        \n        ; -----------------------------\n\n.quote_symbol:\n        ; Just return the arguments in rsi cdr\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quote empty, so return nil\n\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n        ; -----------------------------\n\n;;; Like quasiquote, but do not evaluate the result.\n.quasiquoteexpand_symbol:\n        ;; Return nil if no cdr\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .return_nil\n\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        call quasiquote\n        jmp .return\n\n        ; -----------------------------\n        \n.quasiquote_symbol:\n        ; call quasiquote function with first argument\n        \n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quasiquote empty, so return nil\n\n        mov r11, rsi            ; Save original AST in R11\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quasiquote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quasiquote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        \n        push r15                ; Environment\n        ; Original AST already on stack\n        \n        call quasiquote\n        ; New AST in RAX\n        pop rdi                 ; Environment\n        pop rsi                 ; Old AST\n\n        mov r11, rax            ; New AST\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST in RSI\n        \n        jmp eval                ; Tail call\n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n.list_exec:\n        ; This point can be called to run a function\n        ; used by swap!\n        ; \n        ; Inputs: RAX - List with function as first element\n        ;               NOTE: This list is released\n        ; \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx                ; Function object\n        push rax                ; List with function first\n\n        ; Create an empty list for the arguments\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax            ; Argument list into RSI\n\n        pop rax                 ; list, function first\n        ;;  Put new empty list onto end of original list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n        \n        pop rbx\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        \n        mov rdx, rax            ; List to release\n        mov rdi, rbx ; Function object in RDI\n\n        mov rbx, [rbx + Cons.car]   ; Call function\n        cmp rbx, apply_fn\n        je apply_fn_jmp             ; Jump to user function apply\n        \n        ; A built-in function, so call (no recursion)\n        push rax\n        push r15\n\n        call rbx\n        \n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        \n        push rax\n        call release_cons\n        pop rax \n        jmp .return             ; Releases Env\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        jmp .return\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;        RDX - list to release after binding\n;;        R15 - Env (will be released)\n;;        R13 - AST released before return\n;;\n;; \n;; Output: Result in RAX\n;;\n;; This is jumped to from eval, so if it returns\n;; then it will return to the caller of eval, not to eval\napply_fn_jmp:\n        ; This is jumped to from eval with AST on the stack\n        pop r13\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        mov r14, rax            ; Save return value in R14\n        \n        mov rsi, rax\n        call incref_object\n\n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_object\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the AST\n        mov rsi, r13\n        call release_object\n        \n        mov rax, r14\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax                ; Body\n        \n        mov r14, r13            ; Old AST. R13 used by env_new_bind\n        \n        push rdx\n        call env_new_bind\n        pop rdx\n\n        mov rdi, rax       ; New environment in RDI\n\n        ; Note: Need to increment the reference count\n        ; of the function body before releasing anything,\n        ; since if the function was defined in-place (lambda)\n        ; then the body may be released early\n        \n        pop rsi            ; Body\n        call incref_object ; Will be released by eval\n        mov r8, rsi        ; Body in R8\n        \n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_cons\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the old AST\n        mov rsi, r14\n        call release_object\n\n        mov rsi, r8             ; Body\n        \n        jmp eval           ; Tail call\n        ; The new environment (in RDI) will be released by eval\n\n\n;;; Called by eval\n;;; Original AST in RSI.\n;;; Returns new AST in RAX\nquasiquote:\n        ;; Dispatch on the type.\n        mov al, BYTE [rsi + Cons.typecar]\n        mov cl, al                           ; keep full al for .list\n        and cl, container_mask\n        cmp cl, container_list\n        je .list\n        cmp cl, container_map\n        je .map\n        cmp cl, container_symbol\n        je .symbol\n        cmp cl, container_vector\n        je .vector\n        ;; return other types unchanged\n        call incref_object\n        mov rax, rsi\n        ret\n\n.list:\n        ;; AST is a list, process it with qq_foldr unless..\n        mov cl, al                           ; it is not empty,\n        and cl, content_mask\n        cmp cl, content_empty\n        je qq_foldr\n\n        cmp cl, content_pointer              ; and it is a pointer,\n        jne qq_foldr\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne qq_foldr\n\n        mov r8, rsi                          ; and the symbol is 'unquote,\n        mov rsi, unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne qq_foldr\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne qq_foldr\n\n        ;; If so, return the argument.\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        mov rax, rsi\n        ret\n\n.map:\n.symbol:\n        call incref_object\n\n        ;; rdx := (ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rsi\n        mov rdx, rax\n\n        mov rsi, quote_symbol\n        call incref_object\n\n        ;; rax := ('quote ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n.vector:\n        ;; rdx := ast processed like a list\n        call qq_foldr\n        mov rdx, rax\n\n        ;; rdx := (processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rdx\n        mov rdx, rax\n\n        mov rsi, vec_symbol\n        call incref_object\n\n        ;; rax := ('vec processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n\n;;; Helper for quasiquote.\n;;; RSI must contain a list or vector, which may be empty.\n;;; The result in RAX is always a list.\n;;; Iterate on the elements in the right fold/reduce style.\nqq_foldr:\n        mov cl, BYTE [rsi + Cons.typecar]\n\n        cmp cl, maltype_empty_list\n        je .empty_list\n\n        cmp cl, maltype_empty_vector\n        je .empty_vector\n\n        ;; Extract first element and store it into the stack during\n        ;; the recursion.\n        mov rdx, rsi\n        call car_and_incref\n        push rsi\n        mov rsi, rdx\n\n        ;; Extract the rest of the list.\n        mov al, BYTE [rsi + Cons.typecdr]\n\n;;; If the rest is not empty\n        cmp al, content_pointer\n        jne .else\n;;; then\n        mov rsi, [rsi + Cons.cdr]\n        jmp .endif\n.else:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax\n.endif:\n        call qq_foldr           ; recursive call\n        pop rsi\n        jmp qq_loop\n\n.empty_list: ;; () -> ()\n        call incref_object\n        mov rax, rsi\n        ret\n\n.empty_vector: ;; [] -> ()\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n\n\n;; Helper for quasiquote\n;; The transition function starts here.\n;; Current element is in rsi, accumulator in rax.\nqq_loop:\n        mov r9, rax\n\n        ;; Process with the element with .default, unless..\n        mov cl, BYTE [rsi + Cons.typecar]    ; it is a list\n        mov al, cl\n        and al, container_mask\n        cmp al, container_list\n        jne .default\n\n        cmp cl, maltype_empty_list           ; it is not empty,\n        je .default\n\n        and cl, content_mask                 ; and it is a pointer,\n        cmp cl, content_pointer\n        jne .default\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne .default\n\n        mov r8, rsi                          ; and the symbol is 'splice-unquote,\n        mov rsi, splice_unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne .default\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne .default\n\n        ;; If so, return ('concat elt acc).\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n\n        ;; rdx := (acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car], r9\n        mov rdx, rax\n\n        ;; rdx := (elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, concat_symbol\n        call incref_object\n\n        ;; rax := ('concat elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n.default:\n        ;; rax := (accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car],     r9\n\n        ;; rcx := quasiquoted_element\n        ;; rdx := (accumulator)\n        push rax\n        call quasiquote\n        mov rcx, rax\n        pop rdx\n\n        ;; rdx := (quasiquoted_element accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rcx\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, cons_symbol\n        call incref_object\n\n        ;; rax := ('cons quasiquoted_elt accumulator)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n\n;; Read and eval\nread_eval:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        jmp eval               ; This releases Env and Form/AST\n        \n        \n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        call eval               ; This releases Env and Form/AST\n        push rax                ; Save result of eval\n\n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rdi                 ; AST in RDI\n        \n        mov rsi, [repl_env]     ; Environment in RSI\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call eval\n        \n        mov rsi, rax\n        call release_object     ; Return from eval\n\n        ; -----------------------------\n        ; Check command-line arguments\n\n        pop rax                 ; Number of arguments\n        cmp rax, 1              ; Always have at least one, the path to executable\n        jg run_script\n\n        ; No extra arguments, so just set *ARGV* to an empty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov rcx, rax            ; value (empty list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n\n        \nrun_script:\n        ; Called with number of command-line arguments in RAX\n        mov r8, rax\n        pop rbx                 ; executable\n        dec r8\n        \n        pop rsi                  ; Address of first arg\n        call cstring_to_string   ; string in RAX\n        mov r9, rax\n        \n        ; get the rest of the args\n        xor r10, r10            ; Zero       \n        dec r8\n        jz .no_args\n        \n        ; Got some arguments\n.arg_loop:  \n        ; Got an argument left. \n        pop rsi                 ; Address of C string\n        call cstring_to_string  ; String in RAX\n        mov r12, rax\n        \n        ;Make a Cons to point to the string\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r12\n        \n        test r10, r10\n        jnz .append\n\n        ; R10 zero, so first arg\n        mov r10, rax            ; Head of list\n        mov r11, rax            ; Tail of list\n        jmp .next\n.append:\n        ; R10 not zero, so append to list tail\n        mov [r11 + Cons.cdr], rax\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        mov r11, rax\n.next:\n        dec r8\n        jnz .arg_loop\n        jmp .got_args\n        \n.no_args:\n        ; No arguments. Create an emoty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov r10, rax\n        \n.got_args:\n        push r9                 ; File name string\n        \n        mov rcx, r10            ; value (list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        mov rsi, run_script_string ; load-file function\n        mov edx, run_script_string.len\n        call raw_to_string      ; String in RAX\n\n        mov rsi, rax\n        pop rdx                 ; File name string\n        call string_append_string\n\n        mov cl, 34              ; \"\n        call string_append_char\n        mov cl, ')'\n        call string_append_char ; closing brace\n\n        ; Read-Eval \"(load-file <file>)\"\n        call read_eval \n\n        jmp quit\n"
  },
  {
    "path": "impls/nasm/step8_macros.asm",
    "content": ";; \n;; nasm -felf64 step8_macros.asm && ld step8_macros.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static defmacro_expecting_function_string, db \"defmacro expects function\",10\n        \n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n\n        static if_missing_condition_string, db \"missing condition in if expression\",10\n\n;; Symbols used for comparison\n        \n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n        static_symbol defmacro_symbol, 'defmacro!'\n        static_symbol macroexpand_symbol, 'macroexpand'\n        \n        static_symbol argv_symbol, '*ARGV*'\n\n        static_symbol quote_symbol, 'quote'\n        static_symbol quasiquote_symbol, 'quasiquote'\n        static_symbol quasiquoteexpand_symbol, 'quasiquoteexpand'\n        static_symbol unquote_symbol, 'unquote'\n        static_symbol splice_unquote_symbol, 'splice-unquote'\n        static_symbol concat_symbol, 'concat'\n        static_symbol cons_symbol, 'cons'\n        static_symbol vec_symbol, 'vec'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(do \\\n(def! not (fn* (a) (if a false true))) \\\n(def! load-file (fn* (f) (eval (read-string (str \",34,\"(do\",34,\"  (slurp f) \",34,10,\"nil)\",34,\" ))))) \\\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \",34,\"odd number of forms to cond\",34,\")) (cons 'cond (rest (rest xs))))))) \\\n)\"\n\n;; Command to run, appending the name of the script to run\n        static run_script_string, db \"(load-file \",34\nsection .text\n\n\n;;; Extract the car of a Cons and increment its reference count.\n;;; If it was value, create a fresh copy.\n;;; in        : rsi (which must be a pointer!)\n;;; out       : rsi\n;;; modified: : cl, rax, rbx\ncar_and_incref:\n        mov cl, BYTE [rsi + Cons.typecar]\n        and cl, content_mask\n\n        mov rsi, [rsi + Cons.car]\n\n        cmp cl, content_pointer\n        je incref_object\n\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE cl ; masked above\n        mov [rax + Cons.car],     rsi\n        mov rsi, rax\n        ret\n\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        push rsi\n        print_str_mac error_string ; print 'Error: ' \n        \n        pop rsi\n        push rsi\n        mov edx, [rsi + Array.length]\n        add rsi, Array.data\n        call print_rawstring    ; print symbol\n        \n        print_str_mac not_found_string ; print ' not found'\n        pop rsi\n\n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        ret\n        \n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate   [ Released ]\n;;        RDI   Environment       [ Released ]\n;;\n;; Returns: Result in RAX\n;;\n;; Note: Both the form and environment will have their reference count\n;; reduced by one (released). This is for tail call optimisation (Env),\n;; quasiquote and macroexpand (AST)\n;; \neval:\n        mov r15, rdi            ; Env\n\n        push rsi                ; AST pushed, must be popped before return\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        jmp .return             ; Releases Env\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Macro expand\n\tpop rax ; Old AST, discard from stack\n        call macroexpand        ; Replaces RSI\n        push rsi ; New AST\n\n        ; Check if RSI is a list, and if \n        ; the first element is a symbol\n        mov al, BYTE [rsi]\n\n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list_still_list\n\n        ; Not a list, so call eval_ast on it\n        mov rdi, r15            ; Environment\n        call eval_ast\n        jmp .return\n\n.list_still_list:\n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n\n        eval_cmp_symbol quote_symbol ; quote\n        je .quote_symbol\n\n        eval_cmp_symbol quasiquoteexpand_symbol\n        je .quasiquoteexpand_symbol\n\n        eval_cmp_symbol quasiquote_symbol ; quasiquote\n        je .quasiquote_symbol\n\n        eval_cmp_symbol defmacro_symbol ; defmacro!\n        je .defmacro_symbol\n\n        eval_cmp_symbol macroexpand_symbol ; macroexpand\n        je .macroexpand_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.defmacro_symbol:\n        mov r9, 1\n        jmp .def_common\n.def_symbol:\n        xor r9, r9        ; Set R9 to 0\n.def_common:\n        ; Define a new symbol in current environment\n        ; If R9 is set to 1 then defmacro\n        \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n\n        ; Test if this is defmacro!\n        test r9, r9\n        jnz .defmacro_not_function\n        \n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        push r9\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        mov rsi, rax\n\n        pop r9\n        \n        ; If this is defmacro, and the object in RSI is a function,\n        ; then change to a macro\n        test r9, r9\n        jz .def_not_macro       ; Not defmacro\n        \n        ; Check RSI\n        mov al, BYTE [rsi]\n        cmp al, maltype_function\n        jne .defmacro_not_function\n\n        ; Got a function, change to macro\n        mov [rsi], BYTE maltype_macro\n        \n.def_not_macro:\n        \n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n\n        mov rax, rcx\n        jmp .return\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.defmacro_not_function:\n        mov rsi, defmacro_expecting_function_string\n        mov rdx, defmacro_expecting_function_string.len\n        jmp .def_handle_error\n        \n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n\n        mov rsi, r15\n        call release_object     ; Decrement R15 ref count\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n\n        mov rsi, r14\n        call incref_object\n        mov rdi, r14\n        \n        mov rsi, [r12 + Cons.car] ; Get the address\n        \n        call incref_object      ; Increment ref count of AST\n        \n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        call incref_object        ; will be released by eval\n        \n        mov r11, rsi              ; save new AST\n        pop rsi                   ; Old AST\n        call release_object\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r14            ; New environment\n\n        jmp eval                ; Tail call\n        ; Note: eval will release the new environment on return\n        \n.let_done:\n        ; Release the new environment\n        push rax\n        mov rsi, r14\n        call release_object\n        pop rax\n\n        ; Release the AST\n        pop rsi\n        push rax\n        call release_object\n        pop rax\n        ret                     ; already released env\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        ; Check if there is a body\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body         ; error\n\n        mov r11, [r11 + Cons.cdr] ; Body in R11\n        \n.do_symbol_loop:\n\n        ; Need to test if this is the last form\n        ; so we can handle tail call\n        \n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_body_last ; Last expression\n\n        ; not the last expression\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_next            ; A value, so skip\n        \n        ; A pointer, so evaluate\n        \n        push r15\n        push r11\n        \n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n                                ; since eval will release Env\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increment ref count since eval will release\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        \n        ; Another form after this.\n        ; Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n        \n        pop r11\n        pop r15\n        \n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n                \n.do_body_last:\n        ; The last form is in R11, which will be returned\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value_return\n        jmp .do_body_expr_return\n\n.do_body_value_return:\n        ; Got a value as last form (in R11).\n        ; Copy and return\n\n        push rax                ; Type of value to return\n\n        ; release Env\n        mov rsi, r15\n        call release_object\n\n        ; Allocate a Cons object to hold value\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        ; release the AST\n        pop rsi\n        mov r15, rax            ; not modified by release\n        call release_object\n        mov rax, r15\n        \n        ret\n\n.do_body_expr_return:\n        ; An expression to evaluate as the last form\n        ; Tail call optimise, jumping to eval\n        ; Don't increment Env reference count\n        \n        mov rsi, [r11 + Cons.car]  ; new AST form\n        call incref_object         ; This will be released by eval\n\n        mov r11, rsi              ; Save new AST\n        pop rsi                   ; Remove old AST from stack\n        call release_object\n        mov rsi, r11\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        mov rsi, r15\n        call release_object     ; Release Env\n\n        ; release the AST\n        pop rsi\n        call release_object\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n\n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increase Form/AST ref count\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .return\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object        ; Will be released by eval\n\n        mov r11, rsi\n        pop rsi\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.if_no_condition:               ; just (if) without a condition\n        \n        print_str_mac error_string\n        print_str_mac if_missing_condition_string\n        \n        ; Release environment\n        mov rsi, r15\n        call release_object\n        xor rsi, rsi            ; No object to throw\n        jmp error_throw\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n.return:\n        ; Release environment\n        mov rsi, r15\n        mov r15, rax            ; Save RAX (return value)\n        call release_object\n\n        ; Release the AST\n        pop rsi                 ; Pushed at start of eval\n        call release_object\n\n        mov rax, r15            ; return value\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r13 + Cons.cdr], rax ; Append to list\n        mov r14, rax              ; R14 contains last cons in list\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        jmp .return\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        jmp .return\n        \n        ; -----------------------------\n\n.quote_symbol:\n        ; Just return the arguments in rsi cdr\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quote empty, so return nil\n\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n        ; -----------------------------\n\n;;; Like quasiquote, but do not evaluate the result.\n.quasiquoteexpand_symbol:\n        ;; Return nil if no cdr\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .return_nil\n\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        call quasiquote\n        jmp .return\n\n        ; -----------------------------\n        \n.quasiquote_symbol:\n        ; call quasiquote function with first argument\n        \n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quasiquote empty, so return nil\n\n        mov r11, rsi            ; Save original AST in R11\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quasiquote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quasiquote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        \n        push r15                ; Environment\n        ; Original AST already on stack\n        \n        call quasiquote\n        ; New AST in RAX\n        pop rdi                 ; Environment\n        pop rsi                 ; Old AST\n\n        mov r11, rax            ; New AST\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST in RSI\n        \n        jmp eval                ; Tail call\n        \n        ; -----------------------------\n.macroexpand_symbol:\n        ; Check if we have a second list element\n        \n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; No argument\n\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .macroexpand_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n\n.macroexpand_pointer:\n        mov rsi, [rsi + Cons.car]\n        call incref_object      ; Since RSI will be released\n        \n        call macroexpand   ; May release and replace RSI\n        \n        mov rax, rsi\n        jmp .return ; Releases original AST\n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n.list_exec:\n        ; This point can be called to run a function\n        ; used by swap!\n        ; \n        ; Inputs: RAX - List with function as first element\n        ;               NOTE: This list is released\n        ; \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx                ; Function object\n        push rax                ; List with function first\n\n        ; Create an empty list for the arguments\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax            ; Argument list into RSI\n\n        pop rax                 ; list, function first\n        ;;  Put new empty list onto end of original list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n        \n        pop rbx\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        \n        mov rdx, rax            ; List to release\n        mov rdi, rbx ; Function object in RDI\n\n        mov rbx, [rbx + Cons.car]   ; Call function\n        cmp rbx, apply_fn\n        je apply_fn_jmp             ; Jump to user function apply\n        \n        ; A built-in function, so call (no recursion)\n        push rax\n        push r15\n\n        call rbx\n        \n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        \n        push rax\n        call release_cons\n        pop rax \n        jmp .return             ; Releases Env\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        jmp .return\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;        RDX - list to release after binding\n;;        R15 - Env (will be released)\n;;        R13 - AST released before return\n;;\n;; \n;; Output: Result in RAX\n;;\n;; This is jumped to from eval, so if it returns\n;; then it will return to the caller of eval, not to eval\napply_fn_jmp:\n        ; This is jumped to from eval with AST on the stack\n        pop r13\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        mov r14, rax            ; Save return value in R14\n        \n        mov rsi, rax\n        call incref_object\n\n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_object\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the AST\n        mov rsi, r13\n        call release_object\n        \n        mov rax, r14\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax                ; Body\n        \n        mov r14, r13            ; Old AST. R13 used by env_new_bind\n        \n        push rdx\n        call env_new_bind\n        pop rdx\n\n        mov rdi, rax       ; New environment in RDI\n\n        ; Note: Need to increment the reference count\n        ; of the function body before releasing anything,\n        ; since if the function was defined in-place (lambda)\n        ; then the body may be released early\n        \n        pop rsi            ; Body\n        call incref_object ; Will be released by eval\n        mov r8, rsi        ; Body in R8\n        \n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_cons\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the old AST\n        mov rsi, r14\n        call release_object\n\n        mov rsi, r8             ; Body\n        \n        jmp eval           ; Tail call\n        ; The new environment (in RDI) will be released by eval\n\n\n;;; Called by eval\n;;; Original AST in RSI.\n;;; Returns new AST in RAX\nquasiquote:\n        ;; Dispatch on the type.\n        mov al, BYTE [rsi + Cons.typecar]\n        mov cl, al                           ; keep full al for .list\n        and cl, container_mask\n        cmp cl, container_list\n        je .list\n        cmp cl, container_map\n        je .map\n        cmp cl, container_symbol\n        je .symbol\n        cmp cl, container_vector\n        je .vector\n        ;; return other types unchanged\n        call incref_object\n        mov rax, rsi\n        ret\n\n.list:\n        ;; AST is a list, process it with qq_foldr unless..\n        mov cl, al                           ; it is not empty,\n        and cl, content_mask\n        cmp cl, content_empty\n        je qq_foldr\n\n        cmp cl, content_pointer              ; and it is a pointer,\n        jne qq_foldr\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne qq_foldr\n\n        mov r8, rsi                          ; and the symbol is 'unquote,\n        mov rsi, unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne qq_foldr\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne qq_foldr\n\n        ;; If so, return the argument.\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        mov rax, rsi\n        ret\n\n.map:\n.symbol:\n        call incref_object\n\n        ;; rdx := (ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rsi\n        mov rdx, rax\n\n        mov rsi, quote_symbol\n        call incref_object\n\n        ;; rax := ('quote ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n.vector:\n        ;; rdx := ast processed like a list\n        call qq_foldr\n        mov rdx, rax\n\n        ;; rdx := (processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rdx\n        mov rdx, rax\n\n        mov rsi, vec_symbol\n        call incref_object\n\n        ;; rax := ('vec processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n\n;;; Helper for quasiquote.\n;;; RSI must contain a list or vector, which may be empty.\n;;; The result in RAX is always a list.\n;;; Iterate on the elements in the right fold/reduce style.\nqq_foldr:\n        mov cl, BYTE [rsi + Cons.typecar]\n\n        cmp cl, maltype_empty_list\n        je .empty_list\n\n        cmp cl, maltype_empty_vector\n        je .empty_vector\n\n        ;; Extract first element and store it into the stack during\n        ;; the recursion.\n        mov rdx, rsi\n        call car_and_incref\n        push rsi\n        mov rsi, rdx\n\n        ;; Extract the rest of the list.\n        mov al, BYTE [rsi + Cons.typecdr]\n\n;;; If the rest is not empty\n        cmp al, content_pointer\n        jne .else\n;;; then\n        mov rsi, [rsi + Cons.cdr]\n        jmp .endif\n.else:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax\n.endif:\n        call qq_foldr           ; recursive call\n        pop rsi\n        jmp qq_loop\n\n.empty_list: ;; () -> ()\n        call incref_object\n        mov rax, rsi\n        ret\n\n.empty_vector: ;; [] -> ()\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n\n\n;; Helper for quasiquote\n;; The transition function starts here.\n;; Current element is in rsi, accumulator in rax.\nqq_loop:\n        mov r9, rax\n\n        ;; Process with the element with .default, unless..\n        mov cl, BYTE [rsi + Cons.typecar]    ; it is a list\n        mov al, cl\n        and al, container_mask\n        cmp al, container_list\n        jne .default\n\n        cmp cl, maltype_empty_list           ; it is not empty,\n        je .default\n\n        and cl, content_mask                 ; and it is a pointer,\n        cmp cl, content_pointer\n        jne .default\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne .default\n\n        mov r8, rsi                          ; and the symbol is 'splice-unquote,\n        mov rsi, splice_unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne .default\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne .default\n\n        ;; If so, return ('concat elt acc).\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n\n        ;; rdx := (acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car], r9\n        mov rdx, rax\n\n        ;; rdx := (elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, concat_symbol\n        call incref_object\n\n        ;; rax := ('concat elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n.default:\n        ;; rax := (accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car],     r9\n\n        ;; rcx := quasiquoted_element\n        ;; rdx := (accumulator)\n        push rax\n        call quasiquote\n        mov rcx, rax\n        pop rdx\n\n        ;; rdx := (quasiquoted_element accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rcx\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, cons_symbol\n        call incref_object\n\n        ;; rax := ('cons quasiquoted_elt accumulator)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n\n;; Tests if an AST in RSI is a list containing\n;; a macro defined in the ENV in R15\n;;\n;; Inputs: AST in RSI (not modified)\n;;         ENV in R15 (not modified)\n;;\n;; Returns: Sets ZF if macro call. If set (true),\n;;          then the macro object is in RAX\n;;\n;; Modifies:\n;;    RAX\n;;    RBX\n;;    RCX\n;;    RDX\n;;    R8\n;;    R9\nis_macro_call:\n        ; Test if RSI is a list which contains a pointer\n        mov al, BYTE [rsi]\n        cmp al, (block_cons + container_list + content_pointer)\n        jne .false\n\n        ; Test if this is a symbol\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .false\n\n        ; Look up symbol in Env\n        push rsi\n        push r15\n        mov rdi, rbx            ; symbol in RDI\n        mov rsi, r15            ; Environment in RSI\n        call env_get\n        pop r15\n        pop rsi\n        jne .false              ; Not in environment\n        \n        ; Object in RAX\n        ; If this is not a macro then needs to be released\n        mov dl, BYTE [rax]\n\n        cmp dl, maltype_macro\n        je .true\n\n        ; Not a macro, so release\n        mov r8, rsi\n        mov rsi, rax\n        call release_object\n        mov rsi, r8\n        \n.false:\n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        ret\n.true:\n        mov rbx, rax            ; Returning Macro object\n        lahf                    ; flags in AH\n        or ah, 64               ; set zero flag\n        sahf\n        mov rax, rbx\n        ret\n\n;; Expands macro calls\n;;\n;; Input: AST in RSI (released and replaced)\n;;        Env in R15 (not modified)\n;; \n;; Result: New AST in RSI\nmacroexpand:\n        push r15\n        \n        call is_macro_call\n        jne .done\n\n        mov r13, rsi\n        \n        mov rdi, rax  ; Macro in RDI\n        \n        ; Check the rest of the args\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        je .got_args\n        \n        ; No arguments. Create an empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rdx, rax\n        \n        mov rsi, rdx            ; Arguments (empty list)\n        call incref_object\n        jmp .macro_call\n.got_args:\n        mov rsi, [rsi + Cons.cdr] ; Rest of list\n        call incref_object\n        mov rdx, rsi            ; Released\n.macro_call:\n        ; Here have:\n        ;   RSI - Arguments\n        ;   RDI - Macro object\n        ;   RDX - List to release\n        ;   R15 - Environment\n        ;   R13 - AST\n\n        ; Increment reference for Environment\n        ; since this will be released by apply_fn\n        xchg rsi, r15\n        call incref_object\n        xchg rsi, r15\n        \n        call apply_fn\n        \n        mov rsi, rax ; Result in RSI\n\n        pop r15\n        jmp macroexpand\n.done:\n        pop r15\n        ret\n\n;; Read and eval\nread_eval:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        jmp eval               ; This releases Env and Form/AST\n        \n        \n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        call eval               ; This releases Env and Form/AST\n        push rax                ; Save result of eval\n\n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rdi                 ; AST in RDI\n        \n        mov rsi, [repl_env]     ; Environment in RSI\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call eval\n        \n        mov rsi, rax\n        call release_object     ; Return from eval\n\n        ; -----------------------------\n        ; Check command-line arguments\n\n        pop rax                 ; Number of arguments\n        cmp rax, 1              ; Always have at least one, the path to executable\n        jg run_script\n\n        ; No extra arguments, so just set *ARGV* to an empty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov rcx, rax            ; value (empty list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n\n        \nrun_script:\n        ; Called with number of command-line arguments in RAX\n        mov r8, rax\n        pop rbx                 ; executable\n        dec r8\n        \n        pop rsi                  ; Address of first arg\n        call cstring_to_string   ; string in RAX\n        mov r9, rax\n        \n        ; get the rest of the args\n        xor r10, r10            ; Zero       \n        dec r8\n        jz .no_args\n        \n        ; Got some arguments\n.arg_loop:  \n        ; Got an argument left. \n        pop rsi                 ; Address of C string\n        call cstring_to_string  ; String in RAX\n        mov r12, rax\n        \n        ;Make a Cons to point to the string\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r12\n        \n        test r10, r10\n        jnz .append\n\n        ; R10 zero, so first arg\n        mov r10, rax            ; Head of list\n        mov r11, rax            ; Tail of list\n        jmp .next\n.append:\n        ; R10 not zero, so append to list tail\n        mov [r11 + Cons.cdr], rax\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        mov r11, rax\n.next:\n        dec r8\n        jnz .arg_loop\n        jmp .got_args\n        \n.no_args:\n        ; No arguments. Create an emoty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov r10, rax\n        \n.got_args:\n        push r9                 ; File name string\n        \n        mov rcx, r10            ; value (list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        mov rsi, run_script_string ; load-file function\n        mov edx, run_script_string.len\n        call raw_to_string      ; String in RAX\n\n        mov rsi, rax\n        pop rdx                 ; File name string\n        call string_append_string\n\n        mov cl, 34              ; \"\n        call string_append_char\n        mov cl, ')'\n        call string_append_char ; closing brace\n\n        ; Read-Eval \"(load-file <file>)\"\n        call read_eval \n\n        jmp quit\n"
  },
  {
    "path": "impls/nasm/step9_try.asm",
    "content": ";; \n;; nasm -felf64 step9_try.asm && ld step9_try.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static defmacro_expecting_function_string, db \"defmacro expects function\",10\n        \n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n\n        static if_missing_condition_string, db \"missing condition in if expression\",10\n\n        static try_missing_catch, db \"try* missing catch*\"\n        static catch_missing_symbol, db \"catch* missing symbol\"\n        static catch_missing_form, db \"catch* missing form\"\n        \n;; Symbols used for comparison\n\n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n        static_symbol defmacro_symbol, 'defmacro!'\n        static_symbol macroexpand_symbol, 'macroexpand'\n        static_symbol try_symbol, 'try*'\n        static_symbol catch_symbol, 'catch*'\n        \n        static_symbol argv_symbol, '*ARGV*'\n\n        static_symbol quote_symbol, 'quote'\n        static_symbol quasiquote_symbol, 'quasiquote'\n        static_symbol quasiquoteexpand_symbol, 'quasiquoteexpand'\n        static_symbol unquote_symbol, 'unquote'\n        static_symbol splice_unquote_symbol, 'splice-unquote'\n        static_symbol concat_symbol, 'concat'\n        static_symbol cons_symbol, 'cons'\n        static_symbol vec_symbol, 'vec'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(do \\\n(def! not (fn* (a) (if a false true))) \\\n(def! load-file (fn* (f) (eval (read-string (str \",34,\"(do\",34,\"  (slurp f) \",34,10,\"nil)\",34,\" ))))) \\\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \",34,\"odd number of forms to cond\",34,\")) (cons 'cond (rest (rest xs))))))) \\\n)\"\n\n;; Command to run, appending the name of the script to run\n        static run_script_string, db \"(load-file \",34\nsection .text\n\n\n;;; Extract the car of a Cons and increment its reference count.\n;;; If it was value, create a fresh copy.\n;;; in        : rsi (which must be a pointer!)\n;;; out       : rsi\n;;; modified: : cl, rax, rbx\ncar_and_incref:\n        mov cl, BYTE [rsi + Cons.typecar]\n        and cl, content_mask\n\n        mov rsi, [rsi + Cons.car]\n\n        cmp cl, content_pointer\n        je incref_object\n\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE cl ; masked above\n        mov [rax + Cons.car],     rsi\n        mov rsi, rax\n        ret\n\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; \neval_ast:\n        mov r15, rdi             ; Save Env in r15\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        ret\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        mov r11, rsi        ; Symbol in R11\n        \n        call string_new\n        mov rsi, rax            ; New string in RSI\n\n        mov cl, 39              ; quote '\n        call string_append_char\n\n        mov rdx, r11            ; symbol\n        call string_append_string\n\n        mov cl, 39\n        call string_append_char\n\n        mov r11, rsi\n\n        mov rsi, not_found_string\n        mov edx, not_found_string.len\n        call raw_to_string ; ' not found'\n\n        mov r12, rax\n        \n        mov rdx, rax\n        mov rsi, r11\n        call string_append_string\n\n        mov r11, rsi\n        mov rsi, r12\n        call release_array\n        mov rsi, r11\n        \n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        ret\n        \n        ; ------------------------------\n.list:\n        ; Evaluate each element of the list\n        ;        \n        xor r8, r8              ; The list to return\n        ; r9 contains head of list\n\n.list_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .list_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_next\n        \n.list_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .list_next\n        \n.list_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        ret\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        ret\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        ret\n        \n.map_error_missing_value:\n        mov rax, r12\n        ret\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        ret\n        \n        ; ---------------------\n.done:\n        ret\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n;; ----------------------------------------------------\n;; Evaluates a form\n;;      \n;; Input: RSI   AST to evaluate   [ Released ]\n;;        RDI   Environment       [ Released ]\n;;\n;; Returns: Result in RAX\n;;\n;; Note: Both the form and environment will have their reference count\n;; reduced by one (released). This is for tail call optimisation (Env),\n;; quasiquote and macroexpand (AST)\n;; \neval:\n        mov r15, rdi            ; Env\n\n        push rsi                ; AST pushed, must be popped before return\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, container_mask\n        cmp al, container_list\n        je .list\n        \n        ; Not a list. Evaluate and return\n        call eval_ast\n        jmp .return             ; Releases Env\n\n        ; --------------------\n.list:\n        ; A list\n        \n        ; Macro expand\n\tpop rax ; Old AST, discard from stack\n        call macroexpand        ; Replaces RSI\n        push rsi ; New AST\n\n        ; Check if RSI is a list, and if \n        ; the first element is a symbol\n        mov al, BYTE [rsi]\n\n        ; Check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list_still_list\n\n        ; Not a list, so call eval_ast on it\n        mov rdi, r15            ; Environment\n        call eval_ast\n        jmp .return\n\n.list_still_list:\n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n\n        eval_cmp_symbol quote_symbol ; quote\n        je .quote_symbol\n\n        eval_cmp_symbol quasiquoteexpand_symbol\n        je .quasiquoteexpand_symbol\n\n        eval_cmp_symbol quasiquote_symbol ; quasiquote\n        je .quasiquote_symbol\n\n        eval_cmp_symbol defmacro_symbol ; defmacro!\n        je .defmacro_symbol\n\n        eval_cmp_symbol macroexpand_symbol ; macroexpand\n        je .macroexpand_symbol\n\n        eval_cmp_symbol try_symbol ; try*\n        je .try_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.defmacro_symbol:\n        mov r9, 1\n        jmp .def_common\n.def_symbol:\n        xor r9, r9        ; Set R9 to 0\n.def_common:\n        ; Define a new symbol in current environment\n        ; If R9 is set to 1 then defmacro\n        \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n\n        ; Test if this is defmacro!\n        test r9, r9\n        jnz .defmacro_not_function\n        \n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        push r9\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        mov rsi, rax\n\n        pop r9\n        \n        ; If this is defmacro, and the object in RSI is a function,\n        ; then change to a macro\n        test r9, r9\n        jz .def_not_macro       ; Not defmacro\n        \n        ; Check RSI\n        mov al, BYTE [rsi]\n        cmp al, maltype_function\n        jne .defmacro_not_function\n\n        ; Got a function, change to macro\n        mov [rsi], BYTE maltype_macro\n        \n.def_not_macro:\n        \n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n\n        mov rax, rcx\n        jmp .return\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.defmacro_not_function:\n        mov rsi, defmacro_expecting_function_string\n        mov rdx, defmacro_expecting_function_string.len\n        jmp .def_handle_error\n        \n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n\n        mov rsi, r15\n        call release_object     ; Decrement R15 ref count\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n\n        mov rsi, r14\n        call incref_object\n        mov rdi, r14\n        \n        mov rsi, [r12 + Cons.car] ; Get the address\n        \n        call incref_object      ; Increment ref count of AST\n        \n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        call incref_object        ; will be released by eval\n        \n        mov r11, rsi              ; save new AST\n        pop rsi                   ; Old AST\n        call release_object\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r14            ; New environment\n\n        jmp eval                ; Tail call\n        ; Note: eval will release the new environment on return\n        \n.let_done:\n        ; Release the new environment\n        push rax\n        mov rsi, r14\n        call release_object\n        pop rax\n\n        ; Release the AST\n        pop rsi\n        push rax\n        call release_object\n        pop rax\n        ret                     ; already released env\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        ; Check if there is a body\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body         ; error\n\n        mov r11, [r11 + Cons.cdr] ; Body in R11\n        \n.do_symbol_loop:\n\n        ; Need to test if this is the last form\n        ; so we can handle tail call\n        \n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_body_last ; Last expression\n\n        ; not the last expression\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_next            ; A value, so skip\n        \n        ; A pointer, so evaluate\n        \n        push r15\n        push r11\n        \n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n                                ; since eval will release Env\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increment ref count since eval will release\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        \n        ; Another form after this.\n        ; Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n        \n        pop r11\n        pop r15\n        \n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n                \n.do_body_last:\n        ; The last form is in R11, which will be returned\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value_return\n        jmp .do_body_expr_return\n\n.do_body_value_return:\n        ; Got a value as last form (in R11).\n        ; Copy and return\n\n        push rax                ; Type of value to return\n\n        ; release Env\n        mov rsi, r15\n        call release_object\n\n        ; Allocate a Cons object to hold value\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        ; release the AST\n        pop rsi\n        mov r15, rax            ; not modified by release\n        call release_object\n        mov rax, r15\n        \n        ret\n\n.do_body_expr_return:\n        ; An expression to evaluate as the last form\n        ; Tail call optimise, jumping to eval\n        ; Don't increment Env reference count\n        \n        mov rsi, [r11 + Cons.car]  ; new AST form\n        call incref_object         ; This will be released by eval\n\n        mov r11, rsi              ; Save new AST\n        pop rsi                   ; Remove old AST from stack\n        call release_object\n        mov rsi, r11\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        mov rsi, r15\n        call release_object     ; Release Env\n\n        ; release the AST\n        pop rsi\n        call release_object\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n\n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increase Form/AST ref count\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .return\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object        ; Will be released by eval\n\n        mov r11, rsi\n        pop rsi\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.if_no_condition:               ; just (if) without a condition\n        \n        print_str_mac error_string\n        print_str_mac if_missing_condition_string\n        \n        ; Release environment\n        mov rsi, r15\n        call release_object\n        xor rsi, rsi            ; No object to throw\n        jmp error_throw\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n.return:\n        ; Release environment\n        mov rsi, r15\n        mov r15, rax            ; Save RAX (return value)\n        call release_object\n\n        ; Release the AST\n        pop rsi                 ; Pushed at start of eval\n        call release_object\n\n        mov rax, r15            ; return value\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r13 + Cons.cdr], rax ; Append to list\n        mov r14, rax              ; R14 contains last cons in list\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        jmp .return\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        jmp .return\n        \n        ; -----------------------------\n\n.quote_symbol:\n        ; Just return the arguments in rsi cdr\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quote empty, so return nil\n\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n        ; -----------------------------\n\n;;; Like quasiquote, but do not evaluate the result.\n.quasiquoteexpand_symbol:\n        ;; Return nil if no cdr\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        jne .return_nil\n\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        call quasiquote\n        jmp .return\n\n        ; -----------------------------\n        \n.quasiquote_symbol:\n        ; call quasiquote function with first argument\n        \n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quasiquote empty, so return nil\n\n        mov r11, rsi            ; Save original AST in R11\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quasiquote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quasiquote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        \n        push r15                ; Environment\n        ; Original AST already on stack\n        \n        call quasiquote\n        ; New AST in RAX\n        pop rdi                 ; Environment\n        pop rsi                 ; Old AST\n\n        mov r11, rax            ; New AST\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST in RSI\n        \n        jmp eval                ; Tail call\n        \n        ; -----------------------------\n.macroexpand_symbol:\n        ; Check if we have a second list element\n        \n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; No argument\n\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .macroexpand_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n\n.macroexpand_pointer:\n        mov rsi, [rsi + Cons.car]\n        call incref_object      ; Since RSI will be released\n        \n        call macroexpand   ; May release and replace RSI\n        \n        mov rax, rsi\n        jmp .return ; Releases original AST\n        \n        ; -----------------------------\n        \n.try_symbol:\n        ; Should have the form\n        ;\n        ;   (try* A (catch* B C))\n        ; \n        ; where B is a symbol, A and C are forms to evaluate\n        \n        ; Check first arg A\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; No argument\n        \n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .try_pointer\n        \n        ; RSI contains a value. Copy and return\n        mov cl, al\n        call alloc_cons\n        mov [rax], BYTE cl      ; Set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx\n        jmp .return\n\n.try_pointer:\n\n        mov r8, [rsi + Cons.car]      ; form A in R8\n        \n        ; Check second arg B\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        ; If nil (catchless try)\n        cmp al, content_nil\n        je .catchless_try\n        \n        cmp al, content_pointer\n        jne .try_missing_catch\n\n        mov rsi, [rsi + Cons.cdr]\n\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .try_missing_catch\n\n        mov r9, [rsi + Cons.car] ; (catch* B C) in R9\n        \n        mov al, BYTE [r9]\n        cmp al, (container_list + content_pointer)\n        jne .try_missing_catch\n\n        mov rsi, [r9 + Cons.car] ; Should be catch* symbol\n        mov al, BYTE [rsi]\n        cmp al, maltype_symbol\n        jne .try_missing_catch\n        \n        mov rdi, catch_symbol\n        call compare_char_array\n        test rax, rax           ; ZF set if rax = 0 (equal)\n        jnz .try_missing_catch\n\n        ; Check that B is a symbol\n        mov al, [r9 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .catch_missing_symbol\n\n        mov r9, [r9 + Cons.cdr] ; (B C) in R9\n\n        mov al, BYTE [r9]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .catch_missing_symbol\n\n        mov r10, [r9 + Cons.car] ; B in R10\n        mov al, BYTE [r10]\n        cmp al, maltype_symbol\n        jne .catch_missing_symbol\n\n        mov al, BYTE [r9 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .catch_missing_form\n        mov r9, [r9 + Cons.cdr] ; C in R9\n\n        ; Now have extracted from (try* A (catch* B C))\n        ; A in R8\n        ; B in R10\n        ; C in R9\n        \n        push R9\n        push R10\n        push r15                ; Env\n        \n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the form in R8\n        mov rsi, r15\n        call incref_object      ; Env released by eval\n        mov rdi, r15            ; Env in RDI\n\n        mov rsi, r8             ; The form to evaluate (A)\n        \n        call incref_object      ; AST released by eval\n        \n        call eval\n        \n        mov r8, rax             ; Result in R8\n        \n        pop r15                 ; Environment\n        ; Discard B and C\n        ;add rsi, 8              ; pop R10 and R9\n        pop r10\n        pop r9\n        \n        ; Remove error handler\n        call error_handler_pop\n        mov rax, r8\n        jmp .return\n\n.catchless_try:\n        ;; Evaluate the form in R8\n        push r15                ; Environment\n        \n        mov rsi, r15\n        call incref_object      ; Env released by eval\n        mov rdi, r15            ; Env in RDI\n\n        mov rsi, r8             ; The form to evaluate (A)\n        \n        call incref_object      ; AST released by eval\n        \n        call eval               ; Result in RAX\n\n        pop r15                 ; Environment\n        \n        jmp .return\n.catch:\n        ; Jumps here on error\n        ; Value thrown in RSI\n        ;\n\n        push rsi\n        call error_handler_pop\n        pop rsi\n\n        pop r15                 ; Env\n        pop r12                 ; B (symbol to bind)\n        pop r13                 ; C (form to evaluate)\n\n        ; Check if C is a value or pointer\n\n        mov cl, BYTE [r13]\n        and cl, content_mask\n        cmp cl, content_pointer\n        je .catch_C_pointer\n\n        ; A value, so copy and return\n        call alloc_cons\n        mov [rax], BYTE cl      ; Set type\n        mov rbx, [r13 + Cons.car]\n        mov [rax + Cons.car], rbx ; Set value\n        \n        jmp .return\n        \n.catch_C_pointer:\n        \n        mov r11, rsi            ; Value thrown in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        \n        mov rsi, rax            ; New environment in RSI\n        mov rdi, r12            ; key (symbol)\n        mov rcx, r11            ; value\n        call env_set\n        \n        mov rdi, rsi            ; Env in RDI (will be released)\n        mov rsi, [r13 + Cons.car] ; Form to evaluate\n        call incref_object      ; will be released\n\n        push r15\n        call eval\n        pop r15\n        \n        jmp .return\n        \n.try_missing_catch:\n        load_static try_missing_catch\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n.catch_missing_symbol:\n        load_static catch_missing_symbol\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n.catch_missing_form:\n        load_static catch_missing_form\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        call eval_ast           ; List of evaluated forms in RAX\n        pop r15\n        pop rsi\n        \n.list_exec:\n        ; This point can be called to run a function\n        ; used by swap!\n        ; \n        ; Inputs: RAX - List with function as first element\n        ;               NOTE: This list is released\n        ; \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx                ; Function object\n        push rax                ; List with function first\n\n        ; Create an empty list for the arguments\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax            ; Argument list into RSI\n\n        pop rax                 ; list, function first\n        ;;  Put new empty list onto end of original list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n        \n        pop rbx\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        \n        mov rdx, rax            ; List to release\n        mov rdi, rbx ; Function object in RDI\n\n        mov rbx, [rbx + Cons.car]   ; Call function\n        cmp rbx, apply_fn\n        je apply_fn_jmp             ; Jump to user function apply\n        \n        ; A built-in function, so call (no recursion)\n        push rax\n        push r15\n\n        call rbx\n        \n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        \n        push rax\n        call release_cons\n        pop rax \n        jmp .return             ; Releases Env\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        jmp .return\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;        RDX - list to release after binding\n;;        R15 - Env (will be released)\n;;        R13 - AST released before return\n;;\n;; \n;; Output: Result in RAX\n;;\n;; This is jumped to from eval, so if it returns\n;; then it will return to the caller of eval, not to eval\napply_fn_jmp:\n        ; This is jumped to from eval with AST on the stack\n        pop r13\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        mov r14, rax            ; Save return value in R14\n        \n        mov rsi, rax\n        call incref_object\n\n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_object\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the AST\n        mov rsi, r13\n        call release_object\n        \n        mov rax, r14\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax                ; Body\n        \n        mov r14, r13            ; Old AST. R13 used by env_new_bind\n        \n        push rdx\n        call env_new_bind\n        pop rdx\n\n        mov rdi, rax       ; New environment in RDI\n\n        ; Note: Need to increment the reference count\n        ; of the function body before releasing anything,\n        ; since if the function was defined in-place (lambda)\n        ; then the body may be released early\n        \n        pop rsi            ; Body\n        call incref_object ; Will be released by eval\n        mov r8, rsi        ; Body in R8\n        \n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_cons\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the old AST\n        mov rsi, r14\n        call release_object\n\n        mov rsi, r8             ; Body\n        \n        jmp eval           ; Tail call\n        ; The new environment (in RDI) will be released by eval\n\n\n;;; Called by eval\n;;; Original AST in RSI.\n;;; Returns new AST in RAX\nquasiquote:\n        ;; Dispatch on the type.\n        mov al, BYTE [rsi + Cons.typecar]\n        mov cl, al                           ; keep full al for .list\n        and cl, container_mask\n        cmp cl, container_list\n        je .list\n        cmp cl, container_map\n        je .map\n        cmp cl, container_symbol\n        je .symbol\n        cmp cl, container_vector\n        je .vector\n        ;; return other types unchanged\n        call incref_object\n        mov rax, rsi\n        ret\n\n.list:\n        ;; AST is a list, process it with qq_foldr unless..\n        mov cl, al                           ; it is not empty,\n        and cl, content_mask\n        cmp cl, content_empty\n        je qq_foldr\n\n        cmp cl, content_pointer              ; and it is a pointer,\n        jne qq_foldr\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne qq_foldr\n\n        mov r8, rsi                          ; and the symbol is 'unquote,\n        mov rsi, unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne qq_foldr\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne qq_foldr\n\n        ;; If so, return the argument.\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        mov rax, rsi\n        ret\n\n.map:\n.symbol:\n        call incref_object\n\n        ;; rdx := (ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rsi\n        mov rdx, rax\n\n        mov rsi, quote_symbol\n        call incref_object\n\n        ;; rax := ('quote ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n.vector:\n        ;; rdx := ast processed like a list\n        call qq_foldr\n        mov rdx, rax\n\n        ;; rdx := (processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rdx\n        mov rdx, rax\n\n        mov rsi, vec_symbol\n        call incref_object\n\n        ;; rax := ('vec processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n\n;;; Helper for quasiquote.\n;;; RSI must contain a list or vector, which may be empty.\n;;; The result in RAX is always a list.\n;;; Iterate on the elements in the right fold/reduce style.\nqq_foldr:\n        mov cl, BYTE [rsi + Cons.typecar]\n\n        cmp cl, maltype_empty_list\n        je .empty_list\n\n        cmp cl, maltype_empty_vector\n        je .empty_vector\n\n        ;; Extract first element and store it into the stack during\n        ;; the recursion.\n        mov rdx, rsi\n        call car_and_incref\n        push rsi\n        mov rsi, rdx\n\n        ;; Extract the rest of the list.\n        mov al, BYTE [rsi + Cons.typecdr]\n\n;;; If the rest is not empty\n        cmp al, content_pointer\n        jne .else\n;;; then\n        mov rsi, [rsi + Cons.cdr]\n        jmp .endif\n.else:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax\n.endif:\n        call qq_foldr           ; recursive call\n        pop rsi\n        jmp qq_loop\n\n.empty_list: ;; () -> ()\n        call incref_object\n        mov rax, rsi\n        ret\n\n.empty_vector: ;; [] -> ()\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n\n\n;; Helper for quasiquote\n;; The transition function starts here.\n;; Current element is in rsi, accumulator in rax.\nqq_loop:\n        mov r9, rax\n\n        ;; Process with the element with .default, unless..\n        mov cl, BYTE [rsi + Cons.typecar]    ; it is a list\n        mov al, cl\n        and al, container_mask\n        cmp al, container_list\n        jne .default\n\n        cmp cl, maltype_empty_list           ; it is not empty,\n        je .default\n\n        and cl, content_mask                 ; and it is a pointer,\n        cmp cl, content_pointer\n        jne .default\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne .default\n\n        mov r8, rsi                          ; and the symbol is 'splice-unquote,\n        mov rsi, splice_unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne .default\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne .default\n\n        ;; If so, return ('concat elt acc).\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n\n        ;; rdx := (acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car], r9\n        mov rdx, rax\n\n        ;; rdx := (elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, concat_symbol\n        call incref_object\n\n        ;; rax := ('concat elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n.default:\n        ;; rax := (accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car],     r9\n\n        ;; rcx := quasiquoted_element\n        ;; rdx := (accumulator)\n        push rax\n        call quasiquote\n        mov rcx, rax\n        pop rdx\n\n        ;; rdx := (quasiquoted_element accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rcx\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, cons_symbol\n        call incref_object\n\n        ;; rax := ('cons quasiquoted_elt accumulator)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n\n;; Tests if an AST in RSI is a list containing\n;; a macro defined in the ENV in R15\n;;\n;; Inputs: AST in RSI (not modified)\n;;         ENV in R15 (not modified)\n;;\n;; Returns: Sets ZF if macro call. If set (true),\n;;          then the macro object is in RAX\n;;\n;; Modifies:\n;;    RAX\n;;    RBX\n;;    RCX\n;;    RDX\n;;    R8\n;;    R9\nis_macro_call:\n        ; Test if RSI is a list which contains a pointer\n        mov al, BYTE [rsi]\n        cmp al, (block_cons + container_list + content_pointer)\n        jne .false\n\n        ; Test if this is a symbol\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .false\n\n        ; Look up symbol in Env\n        push rsi\n        push r15\n        mov rdi, rbx            ; symbol in RDI\n        mov rsi, r15            ; Environment in RSI\n        call env_get\n        pop r15\n        pop rsi\n        jne .false              ; Not in environment\n        \n        ; Object in RAX\n        ; If this is not a macro then needs to be released\n        mov dl, BYTE [rax]\n\n        cmp dl, maltype_macro\n        je .true\n\n        ; Not a macro, so release\n        mov r8, rsi\n        mov rsi, rax\n        call release_object\n        mov rsi, r8\n        \n.false:\n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        ret\n.true:\n        mov rbx, rax            ; Returning Macro object\n        lahf                    ; flags in AH\n        or ah, 64               ; set zero flag\n        sahf\n        mov rax, rbx\n        ret\n\n;; Expands macro calls\n;;\n;; Input: AST in RSI (released and replaced)\n;;        Env in R15 (not modified)\n;; \n;; Result: New AST in RSI\nmacroexpand:\n        push r15\n        \n        call is_macro_call\n        jne .done\n\n        mov r13, rsi\n        \n        mov rdi, rax  ; Macro in RDI\n        \n        ; Check the rest of the args\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        je .got_args\n        \n        ; No arguments. Create an empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rdx, rax\n        \n        mov rsi, rdx            ; Arguments (empty list)\n        call incref_object\n        jmp .macro_call\n.got_args:\n        mov rsi, [rsi + Cons.cdr] ; Rest of list\n        call incref_object\n        mov rdx, rsi            ; Released\n.macro_call:\n        ; Here have:\n        ;   RSI - Arguments\n        ;   RDI - Macro object\n        ;   RDX - List to release\n        ;   R15 - Environment\n        ;   R13 - AST\n\n        ; Increment reference for Environment\n        ; since this will be released by apply_fn\n        xchg rsi, r15\n        call incref_object\n        xchg rsi, r15\n        \n        call apply_fn\n        \n        mov rsi, rax ; Result in RSI\n\n        pop r15\n        jmp macroexpand\n.done:\n        pop r15\n        ret\n\n;; Read and eval\nread_eval:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        jmp eval               ; This releases Env and Form/AST\n        \n        \n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        call eval               ; This releases Env and Form/AST\n        push rax                ; Save result of eval\n\n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rdi                 ; AST in RDI\n        \n        mov rsi, [repl_env]     ; Environment in RSI\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call eval\n        \n        mov rsi, rax\n        call release_object     ; Return from eval\n\n        ; -----------------------------\n        ; Check command-line arguments\n\n        pop rax                 ; Number of arguments\n        cmp rax, 1              ; Always have at least one, the path to executable\n        jg run_script\n\n        ; No extra arguments, so just set *ARGV* to an empty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov rcx, rax            ; value (empty list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n\n        push rsi\n        print_str_mac error_string   ; print 'Error: '\n        pop rsi\n\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n\n        \nrun_script:\n        ; Called with number of command-line arguments in RAX\n        mov r8, rax\n        pop rbx                 ; executable\n        dec r8\n        \n        pop rsi                  ; Address of first arg\n        call cstring_to_string   ; string in RAX\n        mov r9, rax\n        \n        ; get the rest of the args\n        xor r10, r10            ; Zero       \n        dec r8\n        jz .no_args\n        \n        ; Got some arguments\n.arg_loop:  \n        ; Got an argument left. \n        pop rsi                 ; Address of C string\n        call cstring_to_string  ; String in RAX\n        mov r12, rax\n        \n        ;Make a Cons to point to the string\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r12\n        \n        test r10, r10\n        jnz .append\n\n        ; R10 zero, so first arg\n        mov r10, rax            ; Head of list\n        mov r11, rax            ; Tail of list\n        jmp .next\n.append:\n        ; R10 not zero, so append to list tail\n        mov [r11 + Cons.cdr], rax\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        mov r11, rax\n.next:\n        dec r8\n        jnz .arg_loop\n        jmp .got_args\n        \n.no_args:\n        ; No arguments. Create an emoty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov r10, rax\n        \n.got_args:\n        push r9                 ; File name string\n        \n        mov rcx, r10            ; value (list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        mov rsi, run_script_string ; load-file function\n        mov edx, run_script_string.len\n        call raw_to_string      ; String in RAX\n\n        mov rsi, rax\n        pop rdx                 ; File name string\n        call string_append_string\n\n        mov cl, 34              ; \"\n        call string_append_char\n        mov cl, ')'\n        call string_append_char ; closing brace\n\n        ; Read-Eval \"(load-file <file>)\"\n        call read_eval \n\n        jmp quit\n"
  },
  {
    "path": "impls/nasm/stepA_mal.asm",
    "content": ";; \n;; nasm -felf64 stepA_mal.asm && ld stepA_mal.o && ./a.out\n;;\n;; Calling convention: Address of input is in RSI\n;;                     Address of return value is in RAX\n;;\n        \nglobal  _start\n        \n%include \"types.asm\"            ; Data types, memory\n%include \"env.asm\"              ; Environment type\n%include \"system.asm\"           ; System calls\n%include \"reader.asm\"           ; String -> Data structures\n%include \"core.asm\"             ; Core functions\n%include \"printer.asm\"          ; Data structures -> String\n%include \"exceptions.asm\"       ; Error handling\n        \nsection .bss\n        \n;; Top-level (REPL) environment\nrepl_env:resq 1\n\nsection .data\n\n;; ------------------------------------------\n;; Fixed strings for printing\n        \n        static prompt_string, db 10,\"user> \"      ; The string to print at the prompt\n\n        static eval_debug_string, db \"EVAL: \"\n        static eval_debug_cr, db 10\n\n        static error_string, db 27,'[31m',\"Error\",27,'[0m',\": \"\n\n        static not_found_string, db \" not found\"\n\n        static def_missing_arg_string, db \"missing argument to def!\",10\n\n        static def_expecting_symbol_string, db \"expecting symbol as first argument to def!\",10\n\n        static defmacro_expecting_function_string, db \"defmacro expects function\",10\n        \n        static let_missing_bindings_string, db \"let* missing bindings\",10\n\n        static let_bindings_list_string, db \"let* expected a list or vector of bindings\",10\n\n        static let_bind_symbol_string, db \"let* expected a symbol in bindings list\",10\n\n        static let_bind_value_string, db \"let* missing value in bindings list\",10\n\n        static let_missing_body_string, db \"let* missing body\",10\n        static eval_list_not_function, db \"list does not begin with a function\",10\n\n        static if_missing_condition_string, db \"missing condition in if expression\",10\n\n        static try_missing_catch, db \"try* missing catch*\"\n        static catch_missing_symbol, db \"catch* missing symbol\"\n        static catch_missing_form, db \"catch* missing form\"\n        \n;; Symbols used for comparison\n\n        static_symbol debug_eval, 'DEBUG-EVAL'\n        static_symbol def_symbol, 'def!'\n        static_symbol let_symbol, 'let*'\n        static_symbol do_symbol, 'do'\n        static_symbol if_symbol, 'if'\n        static_symbol fn_symbol, 'fn*'\n        static_symbol defmacro_symbol, 'defmacro!'\n        static_symbol try_symbol, 'try*'\n        static_symbol catch_symbol, 'catch*'\n        \n        static_symbol argv_symbol, '*ARGV*'\n\n        static_symbol quote_symbol, 'quote'\n        static_symbol quasiquote_symbol, 'quasiquote'\n        static_symbol unquote_symbol, 'unquote'\n        static_symbol splice_unquote_symbol, 'splice-unquote'\n        static_symbol concat_symbol, 'concat'\n        static_symbol cons_symbol, 'cons'\n        static_symbol vec_symbol, 'vec'\n        \n;; Startup string. This is evaluated on startup\n        static mal_startup_string, db \"(do \\\n(def! not (fn* (a) (if a false true))) \\\n(def! load-file (fn* (f) (eval (read-string (str \",34,\"(do\",34,\"  (slurp f) \",34,10,\"nil)\",34,\" ))))) \\\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \",34,\"odd number of forms to cond\",34,\")) (cons 'cond (rest (rest xs))))))) \\\n(def! *host-language* \",34,\"nasm\",34,\")\\\n(def! conj nil)\\\n)\"\n\n;; Command to run, appending the name of the script to run\n        static run_script_string, db \"(load-file \",34\n\n;; Command to run at start of REPL\n        static mal_startup_header, db \"(println (str \",34,\"Mal [\",34,\" *host-language* \",34,\"]\",34,\"))\"\n        \nsection .text\n\n\n;;; Extract the car of a Cons and increment its reference count.\n;;; If it was value, create a fresh copy.\n;;; in        : rsi (which must be a pointer!)\n;;; out       : rsi\n;;; modified: : cl, rax, rbx\ncar_and_incref:\n        mov cl, BYTE [rsi + Cons.typecar]\n        and cl, content_mask\n\n        mov rsi, [rsi + Cons.car]\n\n        cmp cl, content_pointer\n        je incref_object\n\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE cl ; masked above\n        mov [rax + Cons.car],     rsi\n        mov rsi, rax\n        ret\n\n\n;; ----------------------------------------------\n;; Evaluates a form\n;;\n;; Inputs: RSI   Form to evaluate\n;;         RDI   Environment\n;; Returns: Result in RAX\n;; Note: Both the form and environment will have their reference count\n;; reduced by one (released). This is for tail call optimisation (Env),\n;; quasiquote and macroexpand (AST)\n;; \neval:\n        push rdi                ; save environment\n        mov r15, rsi            ; save form\n\n        mov rsi, rdi            ; look for DEBUG-EVAL in environment\n        mov rdi, debug_eval\n        call env_get\n        jne .debug_eval_finished\n        mov bl, BYTE [rax]      ; Get type of result\n        mov cl, bl\n        and cl, content_mask\n        cmp cl, content_pointer\n        je .debug_eval_release_pointer\n        cmp bl, maltype_nil\n        je .debug_eval_finished\n        cmp bl, maltype_false\n        je .debug_eval_finished\n\n        print_str_mac eval_debug_string ; -> rsi, rdx ->\n        mov rdi, 1\n        mov rsi, r15            ; ast\n        call pr_str             ; rdi, rsi -> rcx, r8, r12, r13, r14 -> rax\n        mov rsi, rax\n        call print_string       ; rsi -> ->\n        call release_array      ; rsi -> [rsi], rax, rbx ->\n        print_str_mac eval_debug_cr ; -> rsi, rdx ->\n        jmp .debug_eval_finished\n.debug_eval_release_pointer:\n        mov rsi, rax\n        call release_object\n.debug_eval_finished:\n        mov rsi, r15            ; restore form\n        pop rdi                 ; restore environment\n        \n        mov r15, rdi             ; Save Env in r15\n\n        push rsi                ; AST pushed, must be popped before return\n        \n        ; Check the type\n        mov al, BYTE [rsi]\n\n        ; Check if this is a list\n        mov ah, al\n        and ah, container_mask\n        cmp ah, container_list\n        je .list\n        \n        cmp ah, container_map\n        je .map\n        \n        cmp ah, container_vector\n        je .vector\n        \n        ; Not a list, map or vector\n        cmp ah, container_symbol\n        je .symbol\n        \n        ; Not a symbol, list, map or vector\n        call incref_object      ; Increment reference count\n        \n        mov rax, rsi\n        jmp .return\n        \n.symbol:\n        ; Check if first character of symbol is ':'\n        mov al, BYTE [rsi + Array.data]\n        cmp al, ':'\n        je .keyword\n        \n        ; look in environment\n        push rsi\n        xchg rsi, rdi\n        ; symbol is the key in rdi\n        ; Environment in rsi\n        call env_get\n        pop rsi\n        je .done                ; result in RAX\n        \n        ; Not found, throw an error\n        mov r11, rsi        ; Symbol in R11\n        \n        call string_new\n        mov rsi, rax            ; New string in RSI\n\n        mov cl, 39              ; quote '\n        call string_append_char\n\n        mov rdx, r11            ; symbol\n        call string_append_string\n\n        mov cl, 39\n        call string_append_char\n\n        mov r11, rsi\n\n        mov rsi, not_found_string\n        mov edx, not_found_string.len\n        call raw_to_string ; ' not found'\n\n        mov r12, rax\n        \n        mov rdx, rax\n        mov rsi, r11\n        call string_append_string\n\n        mov r11, rsi\n        mov rsi, r12\n        call release_array\n        mov rsi, r11\n        \n        jmp error_throw\n        \n        ; ------------------------------\n        \n.keyword:\n        ; Just return keywords unaltered\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n        ; ------------------------------\n.list_map_eval:\n\n        ;; Some code is duplicated for the first element because\n        ;; the iteration must stop if its evaluation products a macro,\n        ;; else a new list must be constructed.\n\n        ; Evaluate first element of the list\n\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer_first\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append_first\n.list_pointer_first:\n        ; List element is a pointer to something\n        push rsi\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        ;; If the evaluated first element is a macro, exit the loop.\n        cmp bl, maltype_macro\n        je macroexpand\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value_first\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append_first\n        \n.list_eval_value_first:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        ; Fall through to .list_append_first\n.list_append_first:\n        ; In RAX\n        ; r8 contains the head of the constructed list\n        ; append to r9\n        mov r8, rax\n        mov r9, rax\n.list_loop:\n        ; Evaluate each element of the remaining list\n\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .list_done          ; finished list\n        mov rsi, [rsi + Cons.cdr] ; next in list\n\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .list_pointer\n        \n        ; A value in RSI, so copy\n        \n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_list)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .list_append\n        \n.list_pointer:\n        ; List element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rdi, [rsi + Cons.car] ; Get the address\n        mov rsi, r15\n\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call incref_object      ; AST increment refs\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .list_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .list_append\n        \n.list_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_list)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n        ; Fall through to .list_append\n.list_append:\n        ; In RAX\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .list_loop\n        \n.list_done:\n        mov rax, r8            ; Return the list\n        jmp eval.return_from_list_map_eval\n        \n        ; ---------------------\n.map:\n        ; Create a new map, evaluating all the values\n        \n        ; Check if the map is empty\n        cmp al, maltype_empty_map\n        jne .map_not_empty\n\n        ; map empty. Just return it\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.map_not_empty:\n        \n        mov r10, rsi            ; input in R10\n        xor r12, r12            ; New map in r12\n        \n        ; Now loop through each key-value pair\n        ; NOTE: This method relies on the implementation\n        ; of map as a list    \n\n.map_loop:\n        ; Copy the key\n        call alloc_cons         ; New Cons in RAX\n\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car] ; Value in RCX\n        mov [rax + Cons.car], rcx\n\n        ; Check the type of the key\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .map_got_key        ; a value\n\n        ; a pointer, so increment reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.map_got_key:\n        cmp r12,0\n        jne .append_key\n\n        ; First key\n        mov r12, rax\n        mov r13, rax\n        jmp .map_value\n        \n.append_key:\n        ; Appending to previous value in r13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n.map_value:\n        ; Check that we have a value\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_error_missing_value\n        mov r10, [r10 + Cons.cdr]\n\n        ; Now got value in r10\n\n        ; Check the type of the value\n        mov bl, [r10 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .map_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r10 + Cons.typecar]\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n        \n        jmp .map_got_value\n.map_value_pointer:\n        ; A pointer, so need to evaluate\n        push r10                ; Input\n        push r12                ; start of result\n        push r13                ; Current head of result\n        push r15                ; Env\n        mov rsi, [r10 + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval               ; Evaluate it, result in rax\n        pop r15\n        pop r13\n        pop r12\n        pop r10\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        \n        jne .map_eval_pointer\n\n        ; A value, so just change the type to a map\n        and bl, content_mask\n        add bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n        jmp .map_got_value\n        \n.map_eval_pointer:\n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        \n.map_got_value:\n        ; Append RAX to list in R13\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n        mov r13, rax\n        \n        ; Check if there's another key\n        mov al, BYTE [r10 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .map_done          ; finished map\n        mov r10, [r10 + Cons.cdr] ; next in map\n        jmp .map_loop\n\n.map_done:\n        mov rax, r12\n        jmp .return\n        \n.map_error_missing_value:\n        mov rax, r12\n        jmp .return\n        \n        ; ------------------------------\n.vector:\n        ; Evaluate each element of the vector\n        ;        \n        xor r8, r8              ; The vector to return\n        ; r9 contains head of vector\n\n.vector_loop:\n        mov al, BYTE [rsi]      ; Check type\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .vector_pointer\n        \n        ; A value, so copy\n        call alloc_cons\n        mov bl, BYTE [rsi]\n        and bl, content_mask\n        add bl, (block_cons + container_vector)\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n\n        ; Result in RAX\n        jmp .vector_append\n        \n.vector_pointer:\n        ; Vector element is a pointer to something\n        push rsi\n        push r8\n        push r9\n        push r15                  ; Env\n        mov rsi, [rsi + Cons.car] ; Get the address\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi\n\n        call incref_object\n        \n        call eval             ; Evaluate it, result in rax\n        pop r15\n        pop r9\n        pop r8\n        pop rsi\n        \n        ; Check the type it's evaluated to\n        mov bl, BYTE [rax]\n        mov bh, bl\n        and bh, (block_mask + container_mask)\n        cmp bh, (block_cons + container_value)\n        je .vector_eval_value\n        \n        ; Not a value, so need a pointer to it\n        push rax\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_vector + content_pointer)\n        pop rbx                 ; Address to point to\n        mov [rax + Cons.car], rbx\n        jmp .vector_append\n\n.vector_eval_value:\n        ; Got value in RAX, so copy\n        push rax\n        call alloc_cons         ; Copy in RAX\n        pop rbx                 ; Value to copy in RBX\n        mov cl, BYTE [rbx]\n        and cl, content_mask\n        or  cl, (block_cons + container_vector)\n        mov [rax], BYTE cl      ; set type\n        mov rcx, [rbx + Cons.car]\n        mov [rax + Cons.car], rcx ; copy value\n\n        ; Release the value in RBX\n        push rsi\n        push rax\n        mov rsi, rbx\n        call release_cons\n        pop rax\n        pop rsi\n        \n.vector_append:\n        ; In RAX\n        \n        cmp r8, 0               ; Check if this is the first\n        je .vector_first\n\n        ; append to r9\n        mov [r9 + Cons.cdr], rax\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov r9, rax\n        jmp .vector_next\n        \n.vector_first:\n        mov r8, rax\n        mov r9, rax\n        ; fall through to .vector_next\n        \n.vector_next:\n        ; Check if there's another\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .vector_done          ; finished vector\n        mov rsi, [rsi + Cons.cdr] ; next in vector\n        jmp .vector_loop\n        \n.vector_done:\n        mov rax, r8            ; Return the vector\n        jmp .return\n        \n        ; ---------------------\n.done:\n        jmp .return             ; Releases Env\n\n\n        \n;; Comparison of symbols for eval function\n;; Compares the symbol in RSI with specified symbol\n;; Preserves RSI and RBX\n;; Modifies RDI\n%macro eval_cmp_symbol 1\n        push rsi\n        push rbx\n        mov rsi, rbx\n        mov rdi, %1\n        call compare_char_array\n        pop rbx\n        pop rsi\n        test rax, rax           ; ZF set if rax = 0 (equal)\n%endmacro\n        \n        ; --------------------\n.list:\n        ; A list\n        \n        ; Check if\n        ; the first element is a symbol\n        cmp al, maltype_empty_list\n        je .empty_list           ; empty list, return unchanged\n\n        and al, content_mask\n        cmp al, content_pointer\n        jne .list_eval\n\n        mov rbx, [rsi + Cons.car]\n        mov al, BYTE [rbx]\n        cmp al, maltype_symbol\n        jne .list_eval\n        \n        ; Is a symbol, address in RBX\n        \n        ; Compare against special form symbols\n        \n        eval_cmp_symbol def_symbol ; def!\n        je .def_symbol\n        \n        eval_cmp_symbol let_symbol ; let*\n        je .let_symbol\n\n        eval_cmp_symbol do_symbol ; do\n        je .do_symbol\n\n        eval_cmp_symbol if_symbol ; if\n        je .if_symbol\n\n        eval_cmp_symbol fn_symbol ; fn\n        je .fn_symbol\n\n        eval_cmp_symbol quote_symbol ; quote\n        je .quote_symbol\n\n        eval_cmp_symbol quasiquote_symbol ; quasiquote\n        je .quasiquote_symbol\n\n        eval_cmp_symbol defmacro_symbol ; defmacro!\n        je .defmacro_symbol\n\n        eval_cmp_symbol try_symbol ; try*\n        je .try_symbol\n        \n        ; Unrecognised\n        jmp .list_eval\n\n              \n        ; -----------------------------\n        \n.defmacro_symbol:\n        mov r9, 1\n        jmp .def_common\n.def_symbol:\n        xor r9, r9        ; Set R9 to 0\n.def_common:\n        ; Define a new symbol in current environment\n        ; If R9 is set to 1 then defmacro\n        \n        ; Next item should be a symbol\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Now should have a symbol\n        \n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .def_error_expecting_symbol\n        mov r8, [rsi + Cons.car] ; Symbol (?)\n\n        mov al, BYTE [r8]\n        cmp al, maltype_symbol\n        jne .def_error_expecting_symbol\n\n        ; R8 now contains a symbol\n        \n        ; expecting a value or pointer next\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .def_error_missing_arg\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a pointer\n        mov al, BYTE [rsi]\n        mov ah, al\n        and ah, content_mask\n        cmp ah, content_pointer\n        je .def_pointer\n\n        ; A value, so copy\n\n        ; Test if this is defmacro!\n        test r9, r9\n        jnz .defmacro_not_function\n        \n        push rax\n        call alloc_cons\n        pop rbx                 ; BL now contains type\n        and bl, content_mask\n        add bl, (block_cons + container_value)\n        mov [rax], BYTE bl\n        mov rcx, [rsi + Cons.car]\n        mov [rax + Cons.car], rcx\n        mov rsi, rax\n        \n        jmp .def_got_value\n\n.def_pointer:\n        ; A pointer, so evaluate\n        \n        ; This may throw an error, so define a handler\n        \n        push r8                 ; the symbol\n        push r15                ; Env\n        push r9\n        mov rsi, [rsi + Cons.car] ; Pointer\n        mov rdi, r15\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n\n        call incref_object      ; AST increment refs\n        \n        call eval\n        mov rsi, rax\n\n        pop r9\n        \n        ; If this is defmacro, and the object in RSI is a function,\n        ; then change to a macro\n        test r9, r9\n        jz .def_not_macro       ; Not defmacro\n        \n        ; Check RSI\n        mov al, BYTE [rsi]\n        cmp al, maltype_function\n        jne .defmacro_not_function\n\n        ; Got a function, change to macro\n        mov [rsi], BYTE maltype_macro\n        \n.def_not_macro:\n        \n        pop r15\n        pop r8\n        \n.def_got_value:\n        ; Symbol in R8, value in RSI\n        mov rdi, r8             ; key (symbol)\n        mov rcx, rsi            ; Value\n        mov rsi, r15            ; Environment\n        call env_set\n\n        mov rax, rcx\n        jmp .return\n       \n.def_error_missing_arg:\n        mov rsi, def_missing_arg_string\n        mov rdx, def_missing_arg_string.len\n        jmp .def_handle_error\n        \n.def_error_expecting_symbol:\n        mov rsi, def_expecting_symbol_string\n        mov rdx, def_expecting_symbol_string.len\n        jmp .def_handle_error\n\n.defmacro_not_function:\n        mov rsi, defmacro_expecting_function_string\n        mov rdx, defmacro_expecting_function_string.len\n        jmp .def_handle_error\n        \n.def_handle_error:\n        push rsi\n        push rdx\n        print_str_mac error_string   ; print 'Error: '\n        \n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n\n        xor rsi, rsi            ; no object to throw\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n.let_symbol:\n        ; Create a new environment\n\n        mov r11, rsi            ; Let form in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        mov r14, rax            ; New environment in R14\n\n        mov rsi, r15\n        call release_object     ; Decrement R15 ref count\n        \n        ; Second element should be the bindings\n        \n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_bindings\n        mov r11, [r11 + Cons.cdr]\n        \n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bindings_list\n        \n        mov r12, [r11 + Cons.car] ; should be bindings list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        ; Can be either a list or vector\n        cmp al, block_cons + container_list\n        je .let_bind_loop\n        cmp al, block_cons + container_vector\n        je .let_bind_loop\n        \n        ; Not a list or vector\n        jmp .let_error_bindings_list\n        \n.let_bind_loop:\n        ; R12 now contains a list with an even number of items\n        ; The first should be a symbol, then a value to evaluate\n        \n        ; Get the symbol\n        mov al, BYTE [r12]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_symbol\n\n        mov r13, [r12 + Cons.car] ; Symbol (?)\n        mov al, BYTE [r13]\n        cmp al, maltype_symbol\n        jne .let_error_bind_symbol\n\n        ; R13 now contains a symbol to bind\n        ; The next item in the bindings list (R12)\n        ; should be a value or expression to evaluate\n\n        mov al, BYTE [r12 + Cons.typecdr]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .let_error_bind_value\n        mov r12, [r12 + Cons.cdr]\n        \n        ; got value in R12\n        \n        ; Check the type of the value\n        mov bl, [r12 + Cons.typecar] ; Type in BL\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .let_value_pointer\n\n        ; Not a pointer, so make a copy\n        call alloc_cons\n        mov bl, [r12 + Cons.typecar]\n        and bl, content_mask\n        ;or bl, (block_cons + container_value) ; 0\n        mov [rax + Cons.typecar], bl\n        mov rcx, [r12 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        jmp .let_got_value\n        \n.let_value_pointer:\n        ; A pointer, so need to evaluate\n        push r11                 ; let* form list\n        push r12                 ; Position in bindings list\n        push r13                 ; symbol to bind\n        push r14                 ; new environment\n\n        mov rsi, r14\n        call incref_object\n        mov rdi, r14\n        \n        mov rsi, [r12 + Cons.car] ; Get the address\n        \n        call incref_object      ; Increment ref count of AST\n        \n        call eval               ; Evaluate it, result in rax\n        pop r14\n        pop r13\n        pop r12\n        pop r11\n        \n.let_got_value:\n\n        mov rsi, r14            ; Env\n        mov rdi, r13            ; key\n        mov rcx, rax            ; value\n        call env_set\n\n        ; Release the value\n        mov rsi, rcx            ; The value\n        call release_object\n        \n        ; Check if there are more bindings\n        mov al, BYTE [r12 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_done_binding\n        mov r12, [r12 + Cons.cdr] ; Next\n        jmp .let_bind_loop\n        \n.let_done_binding:\n        ; Done bindings.\n        ; Evaluate next item in let* form in new environment\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .let_error_missing_body\n        mov r11, [r11 + Cons.cdr] ; Now contains value to evaluate\n        ; Check type of the value\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        je .body_pointer\n\n        ; Just a value, so copy\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl      ; set type\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx ; copy value\n        jmp .let_done\n        \n.body_pointer:\n        ; Evaluate using new environment\n        \n        mov rsi, [r11 + Cons.car] ; Object pointed to\n        call incref_object        ; will be released by eval\n        \n        mov r11, rsi              ; save new AST\n        pop rsi                   ; Old AST\n        call release_object\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r14            ; New environment\n\n        jmp eval                ; Tail call\n        ; Note: eval will release the new environment on return\n        \n.let_done:\n        ; Release the new environment\n        push rax\n        mov rsi, r14\n        call release_object\n        pop rax\n\n        ; Release the AST\n        pop rsi\n        push rax\n        call release_object\n        pop rax\n        ret                     ; already released env\n        \n.let_error_missing_bindings:\n        mov rsi, let_missing_bindings_string\n        mov rdx, let_missing_bindings_string.len\n        jmp .let_handle_error\n        \n.let_error_bindings_list:       ; expected a list or vector, got something else\n        mov rsi, let_bindings_list_string\n        mov rdx, let_bindings_list_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_symbol:         ; expected a symbol, got something else\n        mov rsi, let_bind_symbol_string\n        mov rdx, let_bind_symbol_string.len\n        jmp .let_handle_error\n        \n.let_error_bind_value:          ; Missing value in binding list\n        mov rsi, let_bind_value_string\n        mov rdx, let_bind_value_string.len\n        jmp .let_handle_error\n        \n.let_error_missing_body:        ; Missing body to evaluate\n        mov rsi, let_missing_body_string\n        mov rdx, let_missing_body_string.len\n        jmp .let_handle_error\n        \n.let_handle_error:\n        push r11                ; For printing later\n        \n        push rsi\n        push rdx\n        \n        print_str_mac error_string   ; print 'Error: '\n\n        pop rdx\n        pop rsi\n        call print_rawstring    ; print message\n        \n        pop rsi                 ; let* form\n        jmp error_throw         ; No return\n        \n        ; -----------------------------\n\n.do_symbol:\n        mov r11, rsi            ; do form in RSI\n        ; Environment in R15\n\n        ; Check if there is a body\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .do_no_body         ; error\n\n        mov r11, [r11 + Cons.cdr] ; Body in R11\n        \n.do_symbol_loop:\n\n        ; Need to test if this is the last form\n        ; so we can handle tail call\n        \n        mov bl, BYTE [r11 + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .do_body_last ; Last expression\n\n        ; not the last expression\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_next            ; A value, so skip\n        \n        ; A pointer, so evaluate\n        \n        push r15\n        push r11\n        \n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n                                ; since eval will release Env\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increment ref count since eval will release\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        \n        ; Another form after this.\n        ; Discard the result of the last eval\n        mov rsi, rax\n        call release_object\n        \n        pop r11\n        pop r15\n        \n.do_next:\n        mov r11, [r11 + Cons.cdr] ; Next in list\n        \n        jmp .do_symbol_loop\n                \n.do_body_last:\n        ; The last form is in R11, which will be returned\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [r11]\n        and al, block_mask + content_mask\n        cmp al, content_pointer\n        jne .do_body_value_return\n        jmp .do_body_expr_return\n\n.do_body_value_return:\n        ; Got a value as last form (in R11).\n        ; Copy and return\n\n        push rax                ; Type of value to return\n\n        ; release Env\n        mov rsi, r15\n        call release_object\n\n        ; Allocate a Cons object to hold value\n        call alloc_cons\n        pop rbx                 ; type in BL\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        ; release the AST\n        pop rsi\n        mov r15, rax            ; not modified by release\n        call release_object\n        mov rax, r15\n        \n        ret\n\n.do_body_expr_return:\n        ; An expression to evaluate as the last form\n        ; Tail call optimise, jumping to eval\n        ; Don't increment Env reference count\n        \n        mov rsi, [r11 + Cons.car]  ; new AST form\n        call incref_object         ; This will be released by eval\n\n        mov r11, rsi              ; Save new AST\n        pop rsi                   ; Remove old AST from stack\n        call release_object\n        mov rsi, r11\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.do_no_body:\n        ; No expressions to evaluate. Return nil\n\n        mov rsi, r15\n        call release_object     ; Release Env\n\n        ; release the AST\n        pop rsi\n        call release_object\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n        \n        ; -----------------------------\n        \n.if_symbol:\n        mov r11, rsi            ; if form in R11\n        ; Environment in R15\n\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .if_no_condition\n        \n        mov r11, [r11 + Cons.cdr] ; Should be a condition\n\n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .if_cond_value\n\n        ; A pointer, so evaluate\n\n        push r15\n        push r11\n\n        mov rsi, r15\n        call incref_object      ; Increase Env reference\n        \n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object         ; Increase Form/AST ref count\n        \n        mov rdi, r15            ; Env\n        call eval               ; Result in RAX\n        pop r11\n        pop r15\n\n        ; Get type of result\n        mov bl, BYTE [rax]\n\n        ; release value\n        push rbx\n        mov rsi, rax\n        call release_object\n        pop rbx\n\n        ; Check type\n        cmp bl, maltype_nil\n        je .if_false\n        cmp bl, maltype_false\n        je .if_false\n        \n        jmp .if_true\n        \n.if_cond_value:\n\n        ; A value\n        cmp al, content_nil\n        je .if_false\n        cmp al, content_false\n        je .if_false\n        \n        jmp .if_true\n\n.if_false:\n        ; Skip the next item\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil\n        \n        mov r11, [r11 + Cons.cdr]\n        \n.if_true:\n        ; Get the next item in the list and evaluate it\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; Nothing to return\n\n        mov r11, [r11 + Cons.cdr]\n        \n        ; Check if value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        je .if_got_pointer\n        \n.if_got_value:\n        ; copy value in r11\n        call alloc_cons\n        mov bl, BYTE [r11]\n        and bl, content_mask\n        mov [rax], BYTE bl\n        mov rbx, [r11 + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .return\n        \n.if_got_pointer:\n        mov rsi, [r11 + Cons.car]  ; Form\n        call incref_object        ; Will be released by eval\n\n        mov r11, rsi\n        pop rsi\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST\n        \n        mov rdi, r15            ; Env\n        jmp eval                ; Tail call\n        \n.if_no_condition:               ; just (if) without a condition\n        \n        print_str_mac error_string\n        print_str_mac if_missing_condition_string\n        \n        ; Release environment\n        mov rsi, r15\n        call release_object\n        xor rsi, rsi            ; No object to throw\n        jmp error_throw\n\n.return_nil:\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n.return:\n        ; Release environment\n        mov rsi, r15\n        mov r15, rax            ; Save RAX (return value)\n        call release_object\n\n        ; Release the AST\n        pop rsi                 ; Pushed at start of eval\n        call release_object\n\n        mov rax, r15            ; return value\n        ret\n        \n        ; -----------------------------\n        \n.fn_symbol:\n        mov r11, rsi            ; fn form in R11\n        ; Environment in R15\n\n        ; Get the binds and body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_empty\n        \n        mov r11, [r11 + Cons.cdr]\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_binds_not_list\n        \n        mov r12, [r11 + Cons.car]  ; Should be binds list\n        mov al, BYTE [r12]\n        and al, (block_mask + container_mask)\n        cmp al, (block_cons + container_list) \n        je .fn_got_binds        ; Can be list\n        cmp al, (block_cons + container_vector)\n        je .fn_got_binds        ; or vector\n        jmp .fn_binds_not_list\n        \n.fn_got_binds:\n\n        ; Next get the body of the function\n        mov al, BYTE [r11 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .fn_no_body\n\n        mov r11, [r11 + Cons.cdr]\n        ; Check value or pointer\n        mov al, BYTE [r11]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .fn_is_value        ; Body in r11\n        mov r11, [r11 + Cons.car]\n        jmp .fn_got_body\n\n.fn_is_value:\n        ; Body is just a value, no expression\n        mov [r11], BYTE al      ; Mark as value, not list\n        \n.fn_got_body:\n\n        ; Now put into function type\n        ;   Addr is \"apply_fn\", the address to call\n        ;   Env in R15\n        ;   Binds in R12\n        ;   Body in R11\n\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov rbx, apply_fn\n        mov [rax + Cons.car], rbx ; Address of apply function\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov r13, rax            ; Return list in R13\n\n        ; Meta\n\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_pointer\n\n        mov [r13 + Cons.cdr], rax ; Append\n        mov r14, rax\n        \n        ; Env\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r15 ; Environment\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax              ; R14 contains last cons in list\n        \n        push rax\n        mov rsi, r15\n        call incref_object\n        pop rax\n\n        ; Binds\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r12 ; Binds list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        \n        mov [r14 + Cons.cdr], rax ; Append to list\n        mov r14, rax\n\n        push rax\n        mov rsi, r12\n        call incref_object\n        pop rax\n        \n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_function + content_pointer)\n        mov [rax + Cons.car], r11 ; Body of function\n        \n        mov [r14 + Cons.cdr], rax\n\n        mov rsi, r11\n        call incref_object\n        \n        mov rax, r13\n        jmp .return\n        \n.fn_empty:\n.fn_binds_not_list:\n.fn_no_body:\n        \n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        jmp .return\n        \n        ; -----------------------------\n\n.quote_symbol:\n        ; Just return the arguments in rsi cdr\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quote empty, so return nil\n\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n        ; -----------------------------\n\n.quasiquote_symbol:\n        ; call quasiquote function with first argument\n        \n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; quasiquote empty, so return nil\n\n        mov r11, rsi            ; Save original AST in R11\n        \n        mov rsi, [rsi + Cons.cdr]\n\n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .quasiquote_pointer\n        \n        ; RSI contains a value. Remove the list container\n        mov [rsi + Cons.typecar], BYTE al\n        call incref_object\n        mov rax, rsi\n        jmp .return\n        \n.quasiquote_pointer:\n        ; RSI contains a pointer, so get the object pointed to\n        mov rsi, [rsi + Cons.car]\n        \n        push r15                ; Environment\n        ; Original AST already on stack\n        \n        call quasiquote\n        ; New AST in RAX\n        pop rdi                 ; Environment\n        pop rsi                 ; Old AST\n\n        mov r11, rax            ; New AST\n        call release_object     ; Release old AST\n        mov rsi, r11            ; New AST in RSI\n        \n        jmp eval                ; Tail call\n        \n        ; -----------------------------\n        \n.try_symbol:\n        ; Should have the form\n        ;\n        ;   (try* A (catch* B C))\n        ; \n        ; where B is a symbol, A and C are forms to evaluate\n        \n        ; Check first arg A\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .return_nil         ; No argument\n        \n        mov rsi, [rsi + Cons.cdr]\n        \n        ; Check if this is a value or pointer\n        mov al, BYTE [rsi + Cons.typecar]\n        and al, content_mask\n        cmp al, content_pointer\n        je .try_pointer\n        \n        ; RSI contains a value. Copy and return\n        mov cl, al\n        call alloc_cons\n        mov [rax], BYTE cl      ; Set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx\n        jmp .return\n\n.try_pointer:\n\n        mov r8, [rsi + Cons.car]      ; form A in R8\n        \n        ; Check second arg B\n\n        mov al, BYTE [rsi + Cons.typecdr]\n        ; If nil (catchless try)\n        cmp al, content_nil\n        je .catchless_try\n        \n        cmp al, content_pointer\n        jne .try_missing_catch\n\n        mov rsi, [rsi + Cons.cdr]\n\n        mov al, BYTE [rsi]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .try_missing_catch\n\n        mov r9, [rsi + Cons.car] ; (catch* B C) in R9\n        \n        mov al, BYTE [r9]\n        cmp al, (container_list + content_pointer)\n        jne .try_missing_catch\n\n        mov rsi, [r9 + Cons.car] ; Should be catch* symbol\n        mov al, BYTE [rsi]\n        cmp al, maltype_symbol\n        jne .try_missing_catch\n        \n        mov rdi, catch_symbol\n        call compare_char_array\n        test rax, rax           ; ZF set if rax = 0 (equal)\n        jnz .try_missing_catch\n\n        ; Check that B is a symbol\n        mov al, [r9 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .catch_missing_symbol\n\n        mov r9, [r9 + Cons.cdr] ; (B C) in R9\n\n        mov al, BYTE [r9]\n        and al, content_mask\n        cmp al, content_pointer\n        jne .catch_missing_symbol\n\n        mov r10, [r9 + Cons.car] ; B in R10\n        mov al, BYTE [r10]\n        cmp al, maltype_symbol\n        jne .catch_missing_symbol\n\n        mov al, BYTE [r9 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .catch_missing_form\n        mov r9, [r9 + Cons.cdr] ; C in R9\n\n        ; Now have extracted from (try* A (catch* B C))\n        ; A in R8\n        ; B in R10\n        ; C in R9\n        \n        push R9\n        push R10\n        push r15                ; Env\n        \n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the form in R8\n        mov rsi, r15\n        call incref_object      ; Env released by eval\n        mov rdi, r15            ; Env in RDI\n\n        mov rsi, r8             ; The form to evaluate (A)\n        \n        call incref_object      ; AST released by eval\n        \n        call eval\n        \n        mov r8, rax             ; Result in R8\n        \n        pop r15                 ; Environment\n        ; Discard B and C\n        ;add rsi, 8              ; pop R10 and R9\n        pop r10\n        pop r9\n        \n        ; Remove error handler\n        call error_handler_pop\n        mov rax, r8\n        jmp .return\n\n.catchless_try:\n        ;; Evaluate the form in R8\n        push r15                ; Environment\n        \n        mov rsi, r15\n        call incref_object      ; Env released by eval\n        mov rdi, r15            ; Env in RDI\n\n        mov rsi, r8             ; The form to evaluate (A)\n        \n        call incref_object      ; AST released by eval\n        \n        call eval               ; Result in RAX\n\n        pop r15                 ; Environment\n        \n        jmp .return\n.catch:\n        ; Jumps here on error\n        ; Value thrown in RSI\n        ;\n\n        push rsi\n        call error_handler_pop\n        pop rsi\n\n        pop r15                 ; Env\n        pop r12                 ; B (symbol to bind)\n        pop r13                 ; C (form to evaluate)\n\n        ; Check if C is a value or pointer\n\n        mov cl, BYTE [r13]\n        and cl, content_mask\n        cmp cl, content_pointer\n        je .catch_C_pointer\n\n        ; A value, so copy and return\n        call alloc_cons\n        mov [rax], BYTE cl      ; Set type\n        mov rbx, [r13 + Cons.car]\n        mov [rax + Cons.car], rbx ; Set value\n        \n        jmp .return\n        \n.catch_C_pointer:\n        \n        mov r11, rsi            ; Value thrown in R11\n        \n        mov rsi, r15            ; Outer env\n        call env_new            ; Increments R15's ref count\n        \n        mov rsi, rax            ; New environment in RSI\n        mov rdi, r12            ; key (symbol)\n        mov rcx, r11            ; value\n        call env_set\n        \n        mov rdi, rsi            ; Env in RDI (will be released)\n        mov rsi, [r13 + Cons.car] ; Form to evaluate\n        call incref_object      ; will be released\n\n        push r15\n        call eval\n        pop r15\n        \n        jmp .return\n        \n.try_missing_catch:\n        load_static try_missing_catch\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n.catch_missing_symbol:\n        load_static catch_missing_symbol\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n.catch_missing_form:\n        load_static catch_missing_form\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n        \n        ; -----------------------------\n        \n.list_eval:\n        push rsi\n        mov rdi, r15            ; Environment\n        push r15\n        jmp .list_map_eval     ; List of evaluated forms in RAX\n.return_from_list_map_eval\n        pop r15\n        pop rsi\n        \n.list_exec:\n        ; This point can be called to run a function\n        ; used by swap!\n        ; \n        ; Inputs: RAX - List with function as first element\n        ;               NOTE: This list is released\n        ; \n        ; Check that the first element of the return is a function\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .list_not_function\n        \n        mov rbx, [rax + Cons.car] ; Get the address\n        mov cl, BYTE [rbx]\n        cmp cl, maltype_function\n        jne .list_not_function\n\n        ; Check the rest of the args\n        mov cl, BYTE [rax + Cons.typecdr]\n        cmp cl, content_pointer\n        je .list_got_args\n        \n        ; No arguments\n        push rbx                ; Function object\n        push rax                ; List with function first\n\n        ; Create an empty list for the arguments\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax            ; Argument list into RSI\n\n        pop rax                 ; list, function first\n        ;;  Put new empty list onto end of original list\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.cdr], rsi\n        \n        pop rbx\n        jmp  .list_function_call\n.list_got_args:\n        mov rsi, [rax + Cons.cdr] ; Rest of list\n.list_function_call:\n        ; Call the function with the rest of the list in RSI\n        \n        mov rdx, rax            ; List to release\n        mov rdi, rbx ; Function object in RDI\n\n        mov rbx, [rbx + Cons.car]   ; Call function\n        cmp rbx, apply_fn\n        je apply_fn_jmp             ; Jump to user function apply\n        \n        ; A built-in function, so call (no recursion)\n        push rax\n        push r15\n\n        call rbx\n        \n        ; Result in rax\n        pop r15\n        pop rsi                 ; eval'ed list\n        \n        push rax\n        call release_cons\n        pop rax \n        jmp .return             ; Releases Env\n\n.list_not_function:\n        ; Not a function. Probably an error\n        push rsi\n\n        mov rsi, rax\n        call release_object\n        \n        print_str_mac error_string\n        print_str_mac eval_list_not_function\n        pop rsi\n        jmp error_throw\n\n.empty_list:\n        mov rax, rsi\n        jmp .return\n\n;; Applies a user-defined function\n;;\n;; Input: RSI - Arguments to bind\n;;        RDI - Function object\n;;        RDX - list to release after binding\n;;        R15 - Env (will be released)\n;;        R13 - AST released before return\n;;\n;; \n;; Output: Result in RAX\n;;\n;; This is jumped to from eval, so if it returns\n;; then it will return to the caller of eval, not to eval\napply_fn_jmp:\n        ; This is jumped to from eval with AST on the stack\n        pop r13\napply_fn:\n        push rsi\n        ; Extract values from the list in RDI\n        mov rax, [rdi + Cons.cdr]\n        mov rax, [rax + Cons.cdr] ; Meta (don't need)\n        mov rsi, [rax + Cons.car] ; Env\n        mov rax, [rax + Cons.cdr]\n        mov rdi, [rax + Cons.car] ; Binds\n        mov rax, [rax + Cons.cdr]\n        mov rax, [rax + Cons.car] ; Body\n        pop rcx                   ; Exprs\n        \n        ; Check the type of the body\n        mov bl, BYTE [rax]\n        and bl, block_mask + container_mask\n        jnz .bind               \n        ; Just a value (in RAX). No eval needed\n        \n        mov r14, rax            ; Save return value in R14\n        \n        mov rsi, rax\n        call incref_object\n\n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_object\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the AST\n        mov rsi, r13\n        call release_object\n        \n        mov rax, r14\n        ret\n.bind:\n        ; Create a new environment, binding arguments\n        push rax                ; Body\n        \n        mov r14, r13            ; Old AST. R13 used by env_new_bind\n        \n        push rdx\n        call env_new_bind\n        pop rdx\n\n        mov rdi, rax       ; New environment in RDI\n\n        ; Note: Need to increment the reference count\n        ; of the function body before releasing anything,\n        ; since if the function was defined in-place (lambda)\n        ; then the body may be released early\n        \n        pop rsi            ; Body\n        call incref_object ; Will be released by eval\n        mov r8, rsi        ; Body in R8\n        \n        ; Release the list passed in RDX\n        mov rsi, rdx\n        call release_cons\n\n        ; Release the environment\n        mov rsi, r15\n        call release_object\n\n        ; Release the old AST\n        mov rsi, r14\n        call release_object\n\n        mov rsi, r8             ; Body\n        \n        jmp eval           ; Tail call\n        ; The new environment (in RDI) will be released by eval\n\n\n;;; Called by eval\n;;; Original AST in RSI.\n;;; Returns new AST in RAX\nquasiquote:\n        ;; Dispatch on the type.\n        mov al, BYTE [rsi + Cons.typecar]\n        mov cl, al                           ; keep full al for .list\n        and cl, container_mask\n        cmp cl, container_list\n        je .list\n        cmp cl, container_map\n        je .map\n        cmp cl, container_symbol\n        je .symbol\n        cmp cl, container_vector\n        je .vector\n        ;; return other types unchanged\n        call incref_object\n        mov rax, rsi\n        ret\n\n.list:\n        ;; AST is a list, process it with qq_foldr unless..\n        mov cl, al                           ; it is not empty,\n        and cl, content_mask\n        cmp cl, content_empty\n        je qq_foldr\n\n        cmp cl, content_pointer              ; and it is a pointer,\n        jne qq_foldr\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne qq_foldr\n\n        mov r8, rsi                          ; and the symbol is 'unquote,\n        mov rsi, unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne qq_foldr\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne qq_foldr\n\n        ;; If so, return the argument.\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n        mov rax, rsi\n        ret\n\n.map:\n.symbol:\n        call incref_object\n\n        ;; rdx := (ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rsi\n        mov rdx, rax\n\n        mov rsi, quote_symbol\n        call incref_object\n\n        ;; rax := ('quote ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n.vector:\n        ;; rdx := ast processed like a list\n        call qq_foldr\n        mov rdx, rax\n\n        ;; rdx := (processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car],     rdx\n        mov rdx, rax\n\n        mov rsi, vec_symbol\n        call incref_object\n\n        ;; rax := ('vec processed_ast)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car],     rsi\n        mov [rax + Cons.cdr],     rdx\n\n        ret\n\n\n;;; Helper for quasiquote.\n;;; RSI must contain a list or vector, which may be empty.\n;;; The result in RAX is always a list.\n;;; Iterate on the elements in the right fold/reduce style.\nqq_foldr:\n        mov cl, BYTE [rsi + Cons.typecar]\n\n        cmp cl, maltype_empty_list\n        je .empty_list\n\n        cmp cl, maltype_empty_vector\n        je .empty_vector\n\n        ;; Extract first element and store it into the stack during\n        ;; the recursion.\n        mov rdx, rsi\n        call car_and_incref\n        push rsi\n        mov rsi, rdx\n\n        ;; Extract the rest of the list.\n        mov al, BYTE [rsi + Cons.typecdr]\n\n;;; If the rest is not empty\n        cmp al, content_pointer\n        jne .else\n;;; then\n        mov rsi, [rsi + Cons.cdr]\n        jmp .endif\n.else:\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rsi, rax\n.endif:\n        call qq_foldr           ; recursive call\n        pop rsi\n        jmp qq_loop\n\n.empty_list: ;; () -> ()\n        call incref_object\n        mov rax, rsi\n        ret\n\n.empty_vector: ;; [] -> ()\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n\n\n;; Helper for quasiquote\n;; The transition function starts here.\n;; Current element is in rsi, accumulator in rax.\nqq_loop:\n        mov r9, rax\n\n        ;; Process with the element with .default, unless..\n        mov cl, BYTE [rsi + Cons.typecar]    ; it is a list\n        mov al, cl\n        and al, container_mask\n        cmp al, container_list\n        jne .default\n\n        cmp cl, maltype_empty_list           ; it is not empty,\n        je .default\n\n        and cl, content_mask                 ; and it is a pointer,\n        cmp cl, content_pointer\n        jne .default\n\n        mov rdi, [rsi + Cons.car]            ; and the first element is a symbol,\n        mov cl, BYTE [rdi + Cons.typecar]\n        cmp cl, maltype_symbol\n        jne .default\n\n        mov r8, rsi                          ; and the symbol is 'splice-unquote,\n        mov rsi, splice_unquote_symbol\n        call compare_char_array\n        test rax, rax\n        mov rsi, r8\n        jne .default\n\n        mov cl, BYTE [rsi + Cons.typecdr]    ; and there is a second element.\n        cmp cl, content_pointer\n        jne .default\n\n        ;; If so, return ('concat elt acc).\n        mov rsi, [rsi + Cons.cdr]\n        call car_and_incref\n\n        ;; rdx := (acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car], r9\n        mov rdx, rax\n\n        ;; rdx := (elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, concat_symbol\n        call incref_object\n\n        ;; rax := ('concat elt acc)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n.default:\n        ;; rax := (accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.car],     r9\n\n        ;; rcx := quasiquoted_element\n        ;; rdx := (accumulator)\n        push rax\n        call quasiquote\n        mov rcx, rax\n        pop rdx\n\n        ;; rdx := (quasiquoted_element accumulator)\n        call alloc_cons\n        mov [rax + Cons.typecar], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rcx\n        mov [rax + Cons.cdr], rdx\n        mov rdx, rax\n\n        mov rsi, cons_symbol\n        call incref_object\n\n        ;; rax := ('cons quasiquoted_elt accumulator)\n        call alloc_cons\n        mov [rax], BYTE (container_list + content_pointer)\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        mov [rax + Cons.car], rsi\n        mov [rax + Cons.cdr], rdx\n\n        ret\n\n;; Expands macro calls\n;;\n;; A part of eval, written here for historical reasons.\n;; RSI: AST, a non-empty list (released and replaced)\n;; RAX: evaluated first element of AST, a macro\n;; R15: env\nmacroexpand:\n        mov r13, rsi\n        \n        mov rdi, rax  ; Macro in RDI\n        \n        ; Check the rest of the args\n        mov cl, BYTE [rsi + Cons.typecdr]\n        cmp cl, content_pointer\n        je .got_args\n        \n        ; No arguments. Create an empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        mov rdx, rax\n        \n        mov rsi, rdx            ; Arguments (empty list)\n        call incref_object\n        jmp .macro_call\n.got_args:\n        mov rsi, [rsi + Cons.cdr] ; Rest of list\n        call incref_object\n        mov rdx, rsi            ; Released\n.macro_call:\n        ; Here have:\n        ;   RSI - Arguments\n        ;   RDI - Macro object\n        ;   RDX - List to release\n        ;   R15 - Environment\n        ;   R13 - AST\n\n        ; Increment reference for Environment\n        ; since this will be released by apply_fn\n        xchg rsi, r15\n        call incref_object\n        xchg rsi, r15\n        \n        call apply_fn\n        \n        mov rsi, rax        ; Result in RSI\n        pop rdi             ; env pushed as r15 by .list_eval\n        pop rax             ; (ignored) ast pushed as r15 by .list_eval\n        pop rax             ; (ignored) ast pushed as rsi by eval\n        jmp eval\n\n;; Read and eval\nread_eval:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        jmp eval               ; This releases Env and Form/AST\n        \n        \n;; Read-Eval-Print in sequence\n;;\n;; Input string in RSI\nrep_seq:\n        ; -------------\n        ; Read\n        call read_str\n        \n        ; -------------\n        ; Eval\n        mov rsi, rax            ; Form to evaluate\n        mov rdi, [repl_env]     ; Environment\n\n        xchg rsi, rdi\n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; since it will be decremented by eval\n        \n        call eval               ; This releases Env and Form/AST\n        push rax                ; Save result of eval\n\n        ; -------------\n        ; Print\n\n        mov rsi, rax            ; Output of eval into input of print\n        mov rdi, 1              ; print readably\n        call pr_str             ; String in RAX\n\n        mov r8, rax             ; Save output\n\n        pop rsi                 ; Result from eval\n        call release_object\n        mov rax, r8\n        \n        ret\n\n\n_start:\n        ; Create and print the core environment\n        call core_environment   ; Environment in RAX\n\n        mov [repl_env], rax     ; store in memory\n\n        ; Set the error handler\n        mov rsi, rsp            ; Stack pointer\n        mov rdi, .catch         ; Address to jump to\n        xor rcx, rcx            ; No data\n        call error_handler_push\n\n        ; Evaluate the startup string\n\n        mov rsi, mal_startup_string\n        mov edx, mal_startup_string.len\n        call raw_to_string      ; String in RAX\n        \n        push rax\n        mov rsi, rax\n        call read_str           ; AST in RAX\n        pop rsi                 ; string\n\n        push rax                ; AST\n        call release_array      ; string\n        pop rdi                 ; AST in RDI\n        \n        mov rsi, [repl_env]     ; Environment in RSI\n        \n        call incref_object      ; Environment increment refs\n        xchg rsi, rdi           ; Env in RDI, AST in RSI\n        \n        call eval\n        \n        mov rsi, rax\n        call release_object     ; Return from eval\n\n        ; -----------------------------\n        ; Check command-line arguments\n\n        pop rax                 ; Number of arguments\n        cmp rax, 1              ; Always have at least one, the path to executable\n        jg run_script\n\n        ; No extra arguments, so just set *ARGV* to an empty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov rcx, rax            ; value (empty list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n\n        ; -----------------------------\n        ; Header\n\n        load_static mal_startup_header\n        call raw_to_string\n        push rax\n        \n        mov rsi, rax\n        call read_eval          ; no print ('nil')\n        mov rsi, rax\n        call release_object     ; Release result of eval\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        ; -----------------------------\n        ; Main loop\n        \n.mainLoop:\n        ; print the prompt\n        print_str_mac prompt_string\n\n        call read_line\n        \n        ; Check if we have a zero-length string\n        cmp DWORD [rax+Array.length], 0\n        je .mainLoopEnd\n\n        push rax                ; Save address of the string\n\n        mov rsi, rax\n        call rep_seq            ; Read-Eval-Print\n\n        push rax                ; Save returned string\n        \n        mov rsi, rax            ; Put into input of print_string\n        call print_string\n\n        ; Release string from rep_seq\n        pop rsi\n        call release_array\n        \n        ; Release the input string\n        pop rsi\n        call release_array\n        \n        jmp .mainLoop\n.mainLoopEnd:\n        \n        jmp quit\n        \n.catch:\n        ; Jumps here on error\n\n        ; Check if an object was thrown\n        cmp rsi, 0\n        je .catch_done_print                ; nothing to print\n\n        push rsi\n        print_str_mac error_string   ; print 'Error: '\n        pop rsi\n\n        mov rdi, 1\n        call pr_str\n        mov rsi, rax\n        call print_string\n.catch_done_print:\n        jmp .mainLoop           ; Go back to the prompt\n        \n\n        \nrun_script:\n        ; Called with number of command-line arguments in RAX\n        mov r8, rax\n        pop rbx                 ; executable\n        dec r8\n        \n        pop rsi                  ; Address of first arg\n        call cstring_to_string   ; string in RAX\n        mov r9, rax\n        \n        ; get the rest of the args\n        xor r10, r10            ; Zero       \n        dec r8\n        jz .no_args\n        \n        ; Got some arguments\n.arg_loop:  \n        ; Got an argument left. \n        pop rsi                 ; Address of C string\n        call cstring_to_string  ; String in RAX\n        mov r12, rax\n        \n        ;Make a Cons to point to the string\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE (block_cons + container_list + content_pointer)\n        mov [rax + Cons.car], r12\n        \n        test r10, r10\n        jnz .append\n\n        ; R10 zero, so first arg\n        mov r10, rax            ; Head of list\n        mov r11, rax            ; Tail of list\n        jmp .next\n.append:\n        ; R10 not zero, so append to list tail\n        mov [r11 + Cons.cdr], rax\n        mov [r11 + Cons.typecdr], BYTE content_pointer\n        mov r11, rax\n.next:\n        dec r8\n        jnz .arg_loop\n        jmp .got_args\n        \n.no_args:\n        ; No arguments. Create an emoty list\n        call alloc_cons         ; in RAX\n        mov [rax], BYTE maltype_empty_list\n        mov r10, rax\n        \n.got_args:\n        push r9                 ; File name string\n        \n        mov rcx, r10            ; value (list)\n        mov rdi, argv_symbol    ; symbol (*ARGV*)\n        mov rsi, [repl_env]     ; environment\n        call env_set\n        \n        mov rsi, run_script_string ; load-file function\n        mov edx, run_script_string.len\n        call raw_to_string      ; String in RAX\n\n        mov rsi, rax\n        pop rdx                 ; File name string\n        call string_append_string\n\n        mov cl, 34              ; \"\n        call string_append_char\n        mov cl, ')'\n        call string_append_char ; closing brace\n\n        ; Read-Eval \"(load-file <file>)\"\n        call read_eval \n\n        jmp quit\n"
  },
  {
    "path": "impls/nasm/system.asm",
    "content": ";;; System call functions\n;;; \n;;; This file contains system-specific functions,\n;;; which use calls to the operating system (Linux)\n\nsection .data\n        static error_open_file_string, db \"Error opening file \"\n        static error_read_file_string, db \"Error reading file \"\n\nsection .bss\n        \ntimespec:  RESQ 2 \n        \nsection .text\n        \n;; -------------------------------------------\n;; Prints a raw string to stdout\n;; String address in rsi, string length in rdx\nprint_rawstring:\n        push rax\n        push rdi\n\n        ; write(1, string, length)\n        mov     rax, 1                  ; system call 1 is write\n        mov     rdi, 1                  ; file handle 1 is stdout\n        syscall\n        \n        pop rdi\n        pop rax\n\n        ret\n        \n;------------------------------------------\n; void exit()\n; Exit program and restore resources\nquit:\n        mov     eax, 60                 ; system call 60 is exit\n        xor     rdi, rdi                ; exit code 0\n        syscall                         ; invoke operating system to exit\n\nquit_error:\n        mov     eax, 60                 ; system call 60 is exit\n        mov     rdi, 1                 ; exit code 1\n        syscall\n\n        \n;; Read a line from stdin\n;; Gets a new string array, fills it until a newline or EOF is reached\n;; Returns pointer to string in RAX\nread_line:\n        ; Get an array to put the string into\n        ; Address in rax\n        call alloc_array\n        ; Mark it as a character array (string)\n        mov BYTE [rax + Array.type], maltype_string\n\n        push rax                ; Save pointer to string\n        \n        ; Read character by character until either newline or end of input\n        mov ebx, 0              ; Count how many characters read\n        mov rsi, rax\n        add rsi, Array.data     ; Point to the data\n.readLoop:\n        mov  rax, 0             ; sys_read\n        mov  rdi, 0             ; stdin\n        mov  rdx, 1             ; count\n        syscall\n\n        ; Characters read in RAX\n        cmp  rax, 0             ; end loop if read <= 0\n        jle  .readLoopEnd\n\n        mov cl, BYTE [rsi]\n        \n        cmp cl, 10      ; End if we read a newline\n        je .readLoopEnd\n        \n        cmp cl, 8       ; Backspace?\n        je .handleBackspace\n\n        cmp cl, 31              ; Below space\n        jle .readLoop           ; Ignore, keep going\n\n        cmp cl, 127             ; DEL or above\n        jge .readLoop           ; Ignore, keep going\n        \n        inc ebx\n        inc rsi                 ; Move to next point in the array\n        jmp .readLoop           ; Get another character\n        \n.handleBackspace:\n        ; Check if we've read any characters\n        cmp ebx, 0\n        je .readLoop            ; If not, carry on the loop\n        ; Characters have been read. Remove one\n        dec ebx\n        dec rsi\n        jmp .readLoop\n.readLoopEnd:\n        pop rax                 ; Restore pointer to string\n        mov DWORD [rax + Array.length], ebx ; Set string length\n        ret\n\n;; Reads a file into a string\n;;\n;; Input: RSI - File name string (char Array)\n;;\n;; Returns: string in RAX\n;;\n;; Pieces from https://stackoverflow.com/questions/20133698/how-to-read-from-and-write-to-files-using-nasm-for-x86-64bit\nread_file:\n\n        mov rdi, rsi            ; Filename\n\n        ; Need to add null terminator\n        mov eax, DWORD [rdi + Array.length]\n        cmp eax, (array_chunk_len * 8)\n        je .error_filename      ; File name too long\n\n        ; Insert a null terminator\n        add rax, rdi\n        mov [rax + Array.data], BYTE 0\n        \n        ; Open the file\n        mov rax, 2\n        add rdi, Array.data; filename in RDI\n        xor rsi, rsi ; O_RDONLY in RSI\n        syscall\n\n        ; Check for error (return -1)\n        cmp eax, 0\n        jl .error_open\n        \n        mov rdi, rax ; File handle in RDI\n\n        ; Create a string\n        push rdi\n        call string_new         ; In RAX\n        pop rdi\n        \n        mov r9, rax             ; Current Array\n        push rax                 ; This is popped in .done\n.loop:\n        ; Read next chunk\n        push r9\n        \n        mov rsi, r9\n        add rsi, Array.data      ; address\n        \n        mov rax, 0             ; sys_read\n        ; file handle in RDI\n        mov  rdx, (array_chunk_len * 8) ; count\n        syscall\n\n        pop r9\n        \n        ; Characters read in RAX\n        \n        cmp rax, 0\n        jl .error_read\n        \n        cmp rax, (array_chunk_len * 8)\n        jg .error_read\n        \n        mov [r9 + Array.length], DWORD eax\n        \n        jl .done\n\n        ; May still be more to read.\n        ; Allocate another\n        call string_new\n        mov [r9 + Array.next], rax\n        mov r9, rax\n        jmp .loop\n        \n.done:  \n        ; Close the file\n        mov rax, 3\n        ;rdi = file handle\n        syscall\n        \n        pop rax\n        ret\n        \n.error_filename:    \n.error_open:\n        ; File name in RDI\n        sub rdi, Array.data\n        \n        ; Make the error message\n        mov rsi, error_open_file_string\n        mov edx, error_open_file_string.len\n        call raw_to_string\n        mov rsi, rax\n        mov cl, 39              ; (')\n        call string_append_char\n        mov rdx, rdi            ; file name\n        call string_append_string\n        mov cl, 39\n        call string_append_char\n\n        ; Error message in RSI\n        jmp error_throw\n        \n.error_read:\n        mov rsi, error_read_file_string\n        mov edx, error_read_file_string.len\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n\n        \n;; Returns the time in ms in RAX\nclock_time_ms:\n        mov rax, 228            ; clock_gettime\n        mov rdi, 0              ; CLOCK_REALTIME\n        mov rsi, timespec\n        syscall\n        \n        mov rax, [timespec + 8] ; nanoseconds\n        cqo                     ; Sign extend RAX into RDX\n        mov rcx, 1000000\n        idiv rcx                ; Divide RAX by 1e6 -> ms\n        mov rbx, rax\n        ; -> ms in RBX\n\n        mov rax, [timespec]     ; Seconds\n        mov rcx, 1000\n        imul rcx               ; Convert to ms\n        add rax, rbx          ; Add RBX\n        \n        ret\n"
  },
  {
    "path": "impls/nasm/types.asm",
    "content": ";; Data structures\n;; ===============\n;;\n;; Memory management is done by having two fixed-size datatypes,\n;; Cons and Array.\n;;\n;; Both Cons and Array have the following in common:\n;; a type field at the start, a reference count, followed by data\n;; [ type (8) | (8) | refs (16) | data ]\n;;\n;;\n;; Type bit fields\n;; ---------------\n;;\n;; The 8-bit type fields describe the Block, Container and Content type.\n;;\n;; The Block type is used for memory management, to determine the kind of memory block\n;; The Container type indicates the data structure that the Cons or Array block is being used to represent\n;; The Content type indicates the raw type of the data in the content\n;;\n;;  Block type [1 bit]: \n;;  0    0 - Cons memory block\n;;  1    1 - Array memory block\n;; \n;;  Container type [3 bits]:\n;;  0    0 - Value (single boxed value for Cons blocks, multiple values for Array blocks).\n;;  2    1 - List (value followed by pointer). Only for Cons blocks\n;;  4    2 - Symbol (special char array). Only for Array blocks\n;;  6    3 - Keyword. Only for Array blocks\n;;  8    4 - Map\n;; 10    5 - Function\n;; 12    6 - Atom\n;; 14    7 - Vector\n;;\n;;  Content type [4 bits]:\n;;   0   0 - Nil\n;;  16   1 - True\n;;  32   2 - Char\n;;  48   3 - Int\n;;  64   4 - Float\n;;  80   5 - Pointer (memory address)\n;;  96   6 - Function (instruction address)\n;; 112   7 - Empty (distinct from Nil)\n;; 208   8 - False\n;; 224   9 - Macro\n;; \n;;\n;; These represent MAL data types as follows:\n;;\n;; MAL type     Block     Container     Content\n;; --------- | -------- | ---------- | ---------\n;; integer      Cons       Value         Int    \n;; symbol       Array      Symbol        Char\n;; list         Cons       List          Any\n;; vector       Cons       Vector        Any\n;; nil          Cons       Value         Nil\n;; true         Cons       Value         True\n;; false        Cons       Value         False\n;; string       Array      Value         Char\n;; keyword      Array      Keyword       Char\n;; hash-map     Cons       Map           Alternate key, values\n;; atom         Cons       Atom          Pointer\n;;\n\n%include \"macros.mac\"\n        \n;; Cons type.\n;; Used to store either a single value with type information\n;; or a pair of (value, Pointer or Nil) to represent a list\nSTRUC Cons\n.typecar: RESB 1                ; Type information for car (8 bit)\n.typecdr: RESB 1                ; Type information for cdr (8 bits)\n.refcount: RESW 1                ; Number of references to this Cons (16 bit)\n.car: RESQ 1                    ; First value (64 bit)\n.cdr: RESQ 1                    ; Second value (64 bit)\n.size:                          ; Total size of struc\nENDSTRUC\n\n\n%define array_chunk_len  32    ; Number of 64-bit values which can be stored in a single chunk\n        \nSTRUC Array\n.type: RESB 1                    ; Type information (8 bits)\n.control: RESB 1                 ; Control data (8 bits)\n.refcount: RESW 1                ; Number of references to this Array (16 bit)\n.length: RESD 1                  ; Number of elements in this part of the array (32 bit)\n.next RESQ 1                     ; Pointer to the next chunk (64 bit)\n.data: RESQ array_chunk_len      ; Data storage\n.size:                           ; Total size of struc\nENDSTRUC\n\n;; Type information\n\n%define block_mask 1       ; LSB for block type \n%define container_mask 2 + 4 + 8 ; Next three bits for container type\n%define content_mask 16 + 32 + 64 + 128 ; Four bits for content type\n        \n;; Block types\n%define block_cons  0           ; Note: This must be zero\n%define block_array 1\n\n;; Container types\n%define container_value  0      ; Note: This must be zero\n%define container_list 2\n%define container_symbol 4\n%define container_keyword 6\n%define container_map 8\n%define container_function 10\n%define container_atom 12\n%define container_vector 14\n        \n;; Content type\n%define content_nil  0\n%define content_true 16\n%define content_char 32\n%define content_int 48\n%define content_float 64\n%define content_pointer 80      ; Memory pointer (to Cons or Array)\n%define content_function 96     ; Function pointer\n%define content_empty 112\n%define content_false 208\n%define content_macro 224 \n        \n;; Common combinations for MAL types\n%define maltype_integer  (block_cons + container_value + content_int)\n%define maltype_string  (block_array + container_value + content_char)\n%define maltype_symbol  (block_array + container_symbol + content_char)\n%define maltype_nil  (block_cons + container_value + content_nil)\n%define maltype_empty_list (block_cons + container_list + content_empty)\n%define maltype_empty_map (block_cons + container_map + content_empty)\n%define maltype_empty_vector (block_cons + container_vector + content_empty)\n%define maltype_function (block_cons + container_function + content_function)\n%define maltype_macro (block_cons + container_function + content_macro)\n%define maltype_true (block_cons + container_value + content_true)\n%define maltype_false (block_cons + container_value + content_false)\n%define maltype_atom (block_cons + container_atom + content_pointer)\n        \n;; ------------------------------------------\n\nsection .data\n        \n;; Fixed strings for printing\n\n        static error_msg_print_string, db \"Error in print string\",10\n        static error_array_memory_limit,  db \"Error: Run out of memory for Array objects. Increase heap_array_limit.\",10\n        static error_cons_memory_limit, db \"Error: Run out of memory for Cons objects. Increase heap_cons_limit.\",10\n\n        static error_cons_double_free, db \"Error: double free error releasing Cons\"\n        static error_array_double_free, db \"Error: double free error releasing Array\"\n        \n;; ------------------------------------------\n;; Memory management\n;;\n;; For each object (Cons or Array), there is a block of memory (in BSS).\n;; When an object is requested it is first taken from the free list\n;; If the free list is empty (address 0) then the next object in the block\n;; is used, and the heap_x_number counter is incremented. When an object\n;; is free'd it is pushed onto the heap_x_free list.\n        \n        \n%define heap_cons_limit 5000     ; Number of cons objects which can be created\n\nheap_cons_next: dq  heap_cons_store  ; Address of next cons in memory\nheap_cons_free: dq 0            ; Address of start of free list\n        \n%define heap_array_limit 2000     ; Number of array objects which can be created\n        \nheap_array_next: dq heap_array_store\nheap_array_free: dq 0\n        \nsection .bss\n\n;; Reserve space to store Cons and Array objects\nheap_cons_store: resb  heap_cons_limit * Cons.size\n.end:                           ; Address of end of the store\n        \nheap_array_store: resb heap_array_limit * Array.size\n.end: \n        \nsection .text\n        \n;; ------------------------------------------\n;; Array alloc_array()\n;;\n;; Returns the address of an Array object in RAX\n;;\n;; Working registers: rbx\nalloc_array:\n        \n        ; Get the address of a free array\n        mov rax, [heap_array_free] ; Address of the array\n        \n        ; Check if it's null\n        cmp rax, 0\n        je .create_array\n        \n        mov rbx, [rax + Array.next] ; Get the address of the next array in the linked list\n        mov [heap_array_free], rbx  ; Put this address at the front of the list\n        jmp .initialise_array\n        \n.create_array:\n\n        ; Get the address of the next Array\n        mov rax, [heap_array_next]\n        ; Check if we've reached the end\n        cmp rax, heap_array_store.end\n        je .out_of_memory\n\n        mov rbx, rax\n        add rbx, Array.size     ; Address of the next array\n        mov [heap_array_next], rbx ; for next time\n        \n.initialise_array:\n        ; Address of Array now in rax\n        mov BYTE [rax + Array.type], block_array\n        mov WORD [rax + Array.refcount], 1 ; Only one reference\n        mov DWORD [rax + Array.length], 0\n        mov QWORD [rax + Array.next], 0 ; null next address\n        \n        ret\n        \n.out_of_memory:\n        mov rsi, error_array_memory_limit\n        mov rdx, error_array_memory_limit.len\n        call print_rawstring\n        jmp quit_error\n\n\n;; -------------------------------------------\n;; Decrements the reference count of the array in RSI\n;; If the count reaches zero then push the array\n;; onto the free list\nrelease_array:\n        mov ax, WORD [rsi + Array.refcount]\n\n        ; Check if reference count is already zero\n        test ax,ax\n        jz .double_free\n        \n        dec ax\n        mov WORD [rsi + Array.refcount], ax\n        jz .free                ; If the count reaches zero then put on free list\n        ret\n        \n.free:\n        ; Get the next field\n        mov rbx, [rsi + Array.next]\n        \n        mov rax, [heap_array_free] ; Get the current head\n        mov [rsi + Array.next], rax ; Put current head into the \"next\" field\n        mov [heap_array_free], rsi  ; Push Array onto free list\n        \n        cmp rbx, 0\n        jne .release_next          ; If there is another array, then need to release it\n        \n        ret\n        \n.release_next:\n        ; release the next array\n        mov rsi, rbx\n        call release_array\n        ret\n\n.double_free:\n        ret\n        load_static error_cons_double_free\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n        \n;; ------------------------------------------\n;; Cons alloc_cons()\n;;\n;; Returns the address of a Cons object in RAX\n;;\n;; Modifies:\n;;   RBX\nalloc_cons:\n        \n        ; Get the address of a free cons\n        mov rax, [heap_cons_free] ; Address of the cons\n        \n        ; Check if it's null\n        cmp rax, 0\n        je .create_cons\n        \n        mov rbx, [rax + Cons.cdr] ; Get the address of the next cons in the linked list\n        mov [heap_cons_free], rbx  ; Put this address at the front of the list\n        jmp .initialise_cons\n        \n.create_cons:\n\n        ; Get the address of the next Cons\n        mov rax, [heap_cons_next]\n        ; Check if we've reached the end\n        cmp rax, heap_cons_store.end\n        je .out_of_memory\n\n        mov rbx, rax\n        add rbx, Cons.size     ; Address of the next cons\n        mov [heap_cons_next], rbx ; for next time\n        \n.initialise_cons:\n        ; Address of Cons now in rax\n        mov BYTE [rax + Cons.typecar], 0\n        mov BYTE [rax + Cons.typecdr], 0\n        mov WORD [rax + Cons.refcount], 1 ; Only one reference\n        mov QWORD [rax + Cons.car], 0 \n        mov QWORD [rax + Cons.cdr], 0 \n        ret\n        \n.out_of_memory:\n        mov rsi, error_cons_memory_limit\n        mov rdx, error_cons_memory_limit.len\n        call print_rawstring\n        jmp quit_error\n\n\n;; -------------------------------------------\n;; Decrements the reference count of the cons in RSI\n;; If the count reaches zero then push the cons\n;; onto the free list\n;;\n;; Modifies registers:\n;;    RAX\n;;    RBX\n;;    RCX\n;;    \nrelease_cons:\n        mov ax, WORD [rsi + Cons.refcount]\n\n        ; Check if already released\n        test ax,ax\n        jz .double_free\n        \n        dec ax\n        mov WORD [rsi + Cons.refcount], ax\n        jz .free                ; If the count reaches zero then put on free list\n        ret\n        \n.free:\n        ; Get and push cdr onto stack\n        mov rcx, [rsi + Cons.cdr]\n        push rcx                ; Content of CDR\n        push rsi                ; Original Cons object being released\n        \n        mov rax, [heap_cons_free] ; Get the current head\n        mov [rsi + Cons.cdr], rax ; Put current head into the \"cdr\" field\n        mov [heap_cons_free], rsi  ; Push Cons onto free list\n\n        ; Check if the CAR needs to be released\n        \n        mov al, BYTE [rsi+Cons.typecar]\n        and al, content_mask    ; Test content type\n        cmp al, content_pointer\n        jne .free_cdr           ; Jump if CAR not pointer\n\n        ; CAR is a pointer to either a Cons or Array\n        ; Get the address stored in CAR\n        mov rsi, [rsi + Cons.car]\n        call release_object\n.free_cdr:\n        pop rcx                 ; This was rsi, the original Cons\n        pop rsi                 ; This was rcx, the original Cons.cdr\n\n        ; Get the type from the original Cons\n        mov al, BYTE [rcx+Cons.typecdr]\n        and al, content_mask    ; Test content type\n        cmp al, content_pointer\n        jne .done\n        \n        call release_object\n.done:\n        ret\n\n.double_free:                   ; Already released\n        ret\n        load_static error_cons_double_free\n        call raw_to_string\n        mov rsi, rax\n        jmp error_throw\n\n;; Releases either a Cons or Array\n;; Address of object in RSI\n;;\n;; May modify:\n;;    RAX\n;;    RBX\n;;    RCX\n;; \nrelease_object:\n        mov al, BYTE [rsi]          ; Get first byte\n        and al, block_mask          ; Test block type\n        cmp al, block_array         ; Test if it's an array\n        je release_array\n        jmp release_cons\n\n;; Increment reference count of Cons or Array\n;; Address of object in RSI\n;;\n;; This code makes use of the fact that the reference\n;; count is in the same place in Cons and Array types\n;;\n;; Modifies\n;;   RAX\nincref_object:\n        mov ax, WORD [rsi + Cons.refcount] ; Same for Array\n        inc ax\n        ; Check for overflow?\n        mov [rsi + Cons.refcount], WORD ax\n        ret\n\n;; -------------------------------------------\n;; Copying lists/vectors\n;; This does a shallow copy, copying only the\n;; top level of objects. Any objects pointed to are not copied\n;;\n;; Input: RSI - address of list/vector\n;;\n;; Returns: New list/vector in RAX, last Cons in RBX\n;;\n;; Modifies:\n;;    RBX\n;;    RCX\n;;    RDX\n;;    R8\n;;    R9\n;;    R10\n;;\ncons_seq_copy:\n        push rsi                ; Restored at the end\n        \n        mov r8, rsi             ; Input in R8\n        xor r9, r9              ; Head of list in R9, start in R10\n.loop:\n        ; Check the type\n        mov cl, BYTE [r8]\n        mov ch, cl\n        and ch, block_mask\n        jnz .not_seq            ; Not a Cons object\n        \n        call alloc_cons\n        mov rdx, rax            ; New Cons in RDX\n        mov [rdx], BYTE cl      ; Copy type in RCX\n        mov rbx, [r8 + Cons.car]  ; Value in RBX\n        mov [rdx + Cons.car], rbx ; Copy value\n        \n        and cl, content_mask\n        cmp cl, content_pointer\n        jne .copied\n        \n        ; A pointer, so increment the reference count\n        mov rsi, rbx\n        call incref_object\n        \n.copied:\n        ; Check if this is the first\n        test r9,r9\n        jnz .append\n\n        ; First Cons\n        mov r9, rdx\n        mov r10, rdx            ; Start of the list, will be returned\n        jmp .next\n        \n.append:\n        ; Appending to last Cons\n        mov [r9 + Cons.cdr], rdx\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        ; Replace\n        mov r9, rdx\n        \n.next:\n        ; Check if there's another\n        mov al, BYTE [r8 + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done               ; No more\n        ; Got another\n        mov r8, [r8 + Cons.cdr]\n        jmp .loop\n\n.done:\n        pop rsi                 ; Restore input\n        mov rax, r10            ; Output list\n        mov rbx, r9             ; Last Cons\n        ret\n        \n.not_seq:\n        xor rsi,rsi\n        jmp error_throw\n        \n;; -------------------------------------------\n;; String type\n;;\n;; Create a new string, address in RAX\n;;\n;; Modifies registers\n;;     RBX\n;; \nstring_new:\n        call alloc_array\n        mov [rax], BYTE maltype_string\n        mov DWORD [rax + Array.length], 0\n        mov QWORD [rax + Array.next], 0\n        ret\n\n;; Convert a raw string to a String type\n;;\n;; Input: Address of raw string in RSI, length in EDX\n;; Output: Address of string in RAX\n;;\n;; Modifies registers: R8,R9,RCX\n;;\nraw_to_string:\n        ; Save registers to restore at the end\n        push r10\n        push r11\n        \n        push rsi\n        push rdx\n        call string_new         ; String now in RAX\n        pop rdx\n        pop rsi\n        \n        mov r8, rax\n        add r8, Array.data      ; Address of string data\n        mov r10, rax\n        add r10, Array.size     ; End of the destination data\n        mov r11, rax            ; First Array to return\n        \n        mov r9, rsi             ; Address of raw data\n        mov ecx, edx            ; Count\n        \n.copy_loop:\n        test ecx, ecx           ; Check if count is zero\n        jz .done\n        \n        ; Copy one byte\n        mov bl, BYTE [r9]\n        mov [r8], BYTE bl\n\n        ; Move the destination\n        inc r8\n        cmp r8, r10\n        jne .dest_ok\n        \n        ; Hit the end. Set the length of the array\n        mov [rax + Array.length], DWORD (array_chunk_len * 8)\n\n        push rax                ; Last Array\n        push rsi\n        push rdx\n        call string_new         ; String now in RAX\n        pop rdx\n        pop rsi\n        pop rbx                 ; Last Array\n        mov [rbx + Array.next], rax ; Point to new Array\n        \n        mov r8, rax\n        add r8, Array.data      ; Address of string data\n        mov r10, rax\n        add r10, Array.size     ; End of the destination data\n        \n.dest_ok:\n        \n        inc r9\n        dec ecx\n        jmp .copy_loop\n.done:\n        ; Set the length of the destination array\n        sub r8, Array.data\n        sub r8, rax\n        mov [rax + Array.length], DWORD r8d\n\n        ; Move first Array into RAX\n        mov rax, r11\n\n        ; Restore registers\n        pop r11\n        pop r10\n        \n        ret\n\n;; Convert a raw string to a symbol\n;; \n;; Input: Address of raw string in RSI, length in EDX\n;; Output: Address of string in RAX\n;;\n;; Modifies registers: R8,R9,RCX\nraw_to_symbol:\n        call raw_to_string\n        ; set the content type\n        mov [rax], BYTE (block_array + container_symbol + content_char)\n        ret\n\n;; Convert a NUL terminated C string to string\n;;\n;; Input: RSI - Address of string\n;;\n;; Returns: String in RAX\n;;\n;; Modifies:\n;;   RBX\n;;   RCX\n;;   RDX\n        \ncstring_to_string:\n        push rsi\n        call string_new         ; in RAX\n        pop rsi\n\n        mov rbx, rax\n        add rbx, Array.data     ; Start of output\n        mov rcx, rax\n        add rcx, Array.size     ; End of output\n.loop:\n        mov dl, BYTE [rsi]\n        test dl, dl             ; Check if NUL (0)\n        jz .done\n        mov [rbx], BYTE dl\n        inc rbx\n        inc rsi\n        jmp .loop\n.done:\n        sub rbx, rax\n        sub rbx, Array.data\n        ; rbx now contains the length\n        mov [rax + Array.length], DWORD ebx\n        ret\n        \n;; Appends a character to a string\n;; Input: Address of string in RSI, character in CL\n;;\n;; Modifies\n;;    RAX\nstring_append_char:\n        push rsi\n        ; Get the end of the string\n.get_end:\n        mov rax, [rsi + Array.next]\n        test rax, rax\n        jz .got_dest_end\n        mov rsi, rax\n        jmp .get_end\n.got_dest_end:\n\n        ; Check if this chunk is full\n        mov eax, DWORD [rsi + Array.length]\n        cmp eax, (array_chunk_len*8)\n        jne .append\n        \n        ; full, need to allocate another\n        call alloc_array\n        mov [rsi + Array.next], rax\n        mov rsi, rax\n        xor eax, eax            ; Set length to zero\n.append:\n        inc eax\n        mov DWORD [rsi + Array.length], eax\n        dec eax\n        add rax, rsi\n        add rax, Array.data            ; End of data\n        mov [rax], BYTE cl\n        \n        pop rsi                 ; Restore original value\n        ret\n\n;; Appends a string to the end of a string\n;; \n;; Input:  String to be modified in RSI\n;;         String to be copied in RDX\n;;\n;; Output: Modified string in RSI\n;;\n;; Working registers:\n;;   rax   Array chunk for output (copied to)\n;;   rbx   Array chunk for input (copied from)\n;;   cl    Character being copied\n;;   r8    Address of destination\n;;   r9    Destination end address\n;;   r10   Address of source \n;;   r11   Source end address\nstring_append_string:\n        ; copy source Array address to rbx\n        mov rbx, rdx\n        \n        ; source data address in r10\n        mov r10, rbx\n        add r10, Array.data     ; Start of the data\n\n        ; source data end address in r11\n        mov r11, r10\n        mov r8d, DWORD [rbx + Array.length]\n        add r11, r8\n\n        test r8d, r8d\n        jz .return              ; Appending zero-size array\n        \n        ; Find the end of the string in RSI\n        ; and put the address of the Array object into rax\n        mov rax, rsi\n.find_string_end:\n        mov r8, QWORD [rax + Array.next]\n        test r8, r8             ; Next chunk is 0\n        je .got_dest_end        ; so reached end\n        \n        mov rax, r8             ; Go to next chunk\n        jmp .find_string_end\n.got_dest_end:\n        \n        ; destination data address into r8\n        mov r8, rax\n        add r8, Array.data\n        add r8d, DWORD [rax + Array.length]\n        \n        ; destination data end into r9\n        mov r9, rax\n        add r9, Array.size\n\n        ; Check if we are at the end of the destination\n        cmp r8, r9\n        je .alloc_dest\n        \n.copy_loop:        \n        ; Copy one byte from source to destination\n        mov cl, BYTE [r10]\n        mov BYTE [r8], cl\n\n        ; move source to next byte\n        inc r10\n        ; Check if we've reached the end of this Array\n        cmp r10, r11\n        jne .source_ok\n        \n        ; have reached the end of the source Array\n        mov rbx, QWORD [rbx + Array.next]     ; Get the next Array address\n        test rbx, rbx           ; Test if it's null\n        je .finished            ; No more, so we're done\n        ; Move on to next Array object\n        \n        ; Get source address into r10\n        mov r10, rbx\n        add r10, Array.data     ; Start of the data\n\n        ; Source end address\n        mov r11d, DWORD [rbx + Array.length] ; Length of the array\n        add r11, r10\n\n        ;  Check if the next array is empty\n        cmp r10, r11\n        je .finished\n        \n.source_ok:\n\n        ; Move destination to next byte\n        inc r8\n        ; Check if we've reached end of the Array\n        cmp r8, r9\n        jne .copy_loop          ; Next byte\n\n.alloc_dest:\n        ; Reached the end of the destination\n        ; Need to allocate another Array\n        push rax\n        push rbx\n        call alloc_array        ; New Array in rax\n        mov r8, rax             ; copy to r8\n        pop rbx\n        pop rax\n\n        ; Previous Array in rax.\n        ; Add a reference to the new array and set length\n        mov QWORD [rax + Array.next], r8\n        mov DWORD [rax + Array.length], (Array.size - Array.data)\n        mov rax, r8             ; new array\n        add r8, Array.data      ; Start of data\n        \n        mov r9, rax\n        add r9, Array.size\n        jmp .copy_loop\n        \n.finished:\n        ; Compare r8 (destination) with data start\n        ; to get length of string\n        sub r8, rax\n        sub r8, Array.data\n        inc r8\n        ; r8 now contains length\n        mov DWORD [rax + Array.length], r8d\n.return:        \n        ret        \n\n;; ------------------------------------------\n;; void print_string(char array)\n;; Address of the char Array should be in RSI\nprint_string:\n        ; Push registers we're going to use\n        push rax\n        push rdi\n        push rdx\n        push rsi\n        \n        ; Check that we have a char array\n        mov al, [rsi]\n        cmp al, maltype_string\n        jne .error\n\n.print_chunk:\n        ; write(1, string, length)\n        push rsi\n        mov   edx,  [rsi + Array.length] ; number of bytes\n        add   rsi, Array.data         ; address of raw string to output\n        call print_rawstring\n        pop rsi\n        \n        ; Check if this is the end\n        mov rsi, QWORD [rsi + Array.next]\n        cmp rsi, 0\n        jne .print_chunk        ; next chunk\n        \n        ; Restore registers\n        pop rsi\n        pop rdx\n        pop rdi\n        pop rax\n        \n        ret\n.error:\n        ; An error occurred\n        mov     rdx, error_msg_print_string.len ; number of bytes\n        mov     rsi, error_msg_print_string        ; address of raw string to output\n        call print_rawstring\n        ; exit\n        jmp quit_error        \n\n;; Copy a string\n;;\n;; Input: RSI - String to copy\n;;\n;; Output: New string in RAX\n;;\n;; Modifies:\n;;    RBX\n;;    RCX\n;;    RDX\n;;    RSI\n;;\nstring_copy:\n        call string_new         ; new string in RAX\n        \n        push rsi\n        push rax\n        \n        ; Get lengths\n        mov ebx, DWORD [rsi + Array.length]\n        mov [rax + Array.length], ebx\n\n        ; Copy the whole block of data\n        ; Not sure if this is quicker than copying byte-by-byte\n        ; Could divide ebx by 8 (rounded up) to get the number\n        ; of blocks needed\n        \n        add rsi, Array.data     ; Start of input data\n        add rax, Array.data     ; Start of output data\n        mov ecx, array_chunk_len ; Number of 64-bit chunks\n        \n.loop:\n        mov rbx, QWORD [rsi]\n        mov [rax], QWORD rbx\n        add rsi, 8\n        add rax, 8\n        dec ecx\n        jnz .loop\n        \n        pop rax\n        pop rsi\n        ; Now check if there's another block\n        mov rsi, [rsi + Array.next]\n        cmp rsi, 0\n        jz .done                ; Result in RAX\n\n        ; Another array chunk\n        push rax                ; Save output\n        \n        call string_copy        ; Copy next chunk\n        mov rbx, rax            ; The copy in RBX\n        \n        pop rax\n        ; append\n        mov [rax + Array.next], rbx\n.done:\n        ret\n        \n;; ------------------------------------------\n;; String itostring(Integer number)\n;;\n;; Converts an integer to a string (array of chars)\n;;\n;; Input in RAX\n;; Return string address in RAX\nitostring:\n        ; Save registers to restore afterwards\n        push    rbx\n        push    rcx\n        push    rdx\n        push    rsi\n        push    rdi\n        \n        mov     rcx, 0          ; counter of how many bytes we need to print in the end\n\n        mov rbx, rax            ; Original input\n        \n        ; Check if the number is negative\n        cmp rax, 0\n        jge .divideLoop\n\n        ; a negative number. To get the '-' sign\n        ; at the front the test is done again at the end\n        ; using the value stored in rbx\n\n        neg rax                 ; Make it positive\n        \n.divideLoop:\n        inc     rcx             ; count each byte to print - number of characters\n        xor     rdx, rdx\n        mov     rsi, 10\n        idiv    rsi             ; divide rax by rsi\n        add     rdx, 48         ; convert rdx to it's ascii representation - rdx holds the remainder after a divide instruction\n        ; Character is now in DL\n        dec     rsp\n        mov     BYTE [rsp], dl  ; Put onto stack\n        \n        cmp     rax, 0          ; can the integer be divided anymore?\n        jnz     .divideLoop      ; jump if not zero to the label divideLoop\n\n        ; Check if the value was negative (in rbx)\n        cmp rbx, 0\n        jge .create_string\n\n        ; a negative number\n        dec rsp\n        mov     BYTE [rsp], '-'\n        inc rcx\n\n.create_string:\n        ; Get an Array object to put the string into\n        call string_new        ; Address in RAX\n        \n        ; put length into string\n        mov     [rax + Array.length], ecx\n        \n        ; copy data from stack into string\n        ; Note: Currently this does not handle long strings\n        mov     rdi, rax\n        add     rdi, Array.data ; Address where raw string will go\n.copyLoop:\n        mov BYTE dl, [rsp]      ; Copy one byte at a time. Could be more efficient\n        mov [rdi], BYTE dl \n        inc rsp\n        inc rdi\n        dec rcx\n        cmp rcx, 0\n        jnz .copyLoop\n        \n        ; Restore registers\n        pop     rdi\n        pop     rsi\n        pop     rdx\n        pop     rcx\n        pop     rbx\n        \n        ret\n\n        \n;; ------------------------------------------------------------\n;; Object comparison\n;;\n;; These comparison functions take two objects\n;; in RSI and RDI\n;; and return a code (not an object) in RAX\n;;\n;;  RAX = 0    Objects are equal\n;;        1    RSI object is greater than RDI\n;;        2    RSI object is less than RDI\n;;       -1    Different object types, or no ordering\n;;\n;; Note that the ordering of objects depends on the type\n;;    strings  - Alphabetical\n;;   \n;; \n;;\n\n;; Given an object in RSI, follows pointers\n;; to return the value object in RAX\n;;\n;; Modifies registers:\n;;   RCX\ncompare_get_value:\n        mov cl, BYTE [rsi]\n        mov ch, cl\n        and ch, block_mask\n        jnz .nop                ; Got an Array\n\n        ; Here got Cons\n        mov ch, cl\n        and ch, content_mask\n        cmp ch, content_pointer\n        jne .nop                ; Not a pointer\n\n        ; Got a pointer, so follow and return\n        mov rax, [rsi + Cons.car]\n        ret\n.nop:\n        mov rax, rsi\n        ret\n\n;; Compare two objects in RSI and RDI.\n;; Note that this does not compare lists\n;; but will just compare the first element\n;;\n;; Modifies registers\n;;    RAX, RBX, RCX, RDX\n;;\ncompare_objects:\n        ; Get the value that RSI points to\n        call compare_get_value\n        mov rbx, rax            ; Save in RBX\n        ; Get the value that RDI points to\n        mov rsi, rdi\n        call compare_get_value\n        mov rdi, rax\n        mov rsi, rbx\n\n        ; now get types\n        mov cl, BYTE [rsi]      ; Type of RSI\n        mov bl, BYTE [rdi]      ; Type of RDI\n\n        mov ch, cl\n        mov bh, bl\n        \n        ; Don't care about container type\n        and cl, block_mask + content_mask\n        and bl, block_mask + content_mask\n        \n        cmp bl, cl              ; compare block and content\n        jne .different_types\n\n        ; Here the same block, content type\n        ; May be different container (value/list, string/symbol)\n\n        ; Need to distinguish between map and vector/list\n        and ch, (block_mask + container_mask)\n        and bh, (block_mask + container_mask)\n        cmp ch, bh\n        je .same_container\n        ; if either is a map, then different types\n        cmp ch, container_map\n        je .different_types\n        cmp bh, container_map\n        je .different_types\n        \n.same_container:\n        cmp bl, block_cons + content_nil\n        je .objects_equal      ; nil\n\n        cmp bl, block_array + content_char\n        je compare_char_array      ; strings, symbols\n        \n        cmp bl, block_cons + content_int\n        je .integers\n\n        ; Unknown\n        jmp .different_types\n        \n.integers:\n        ; two Cons objects, both containing integers\n        mov rbx, [rsi + Cons.car]\n        cmp rbx, [rdi + Cons.car]\n        je .objects_equal\n        jl .rdi_greater\n        jmp .rsi_greater\n        \n.objects_equal:\n        mov rax, 0\n        ret\n\n.rsi_greater:                   ; rsi > rdi\n        mov rax, 1\n        ret\n        \n.rdi_greater:                   ; rdi > rsi\n        mov rax, 2\n        ret\n        \n.different_types:\n        mov rax, -1\n        ret\n        \n\n;; Recursively check objects, including lists\n;; \n;; Inputs: Objects in RSI and RDI\n;;\n;; Sets ZF if equal, clears flag otherwise\ncompare_objects_rec:\n        ; Compare rsi and rdi objects\n        \n        ; Check type\n        mov al, BYTE [rsi]\n        mov bl, BYTE [rdi]\n        \n        mov ah, al\n        mov bh, bl\n        \n        ; Don't distinguish between [] and ()\n        and ah, (block_mask + content_mask)\n        and bh, (block_mask + content_mask)\n        \n        cmp ah, bh\n        jne .false\n\n        ; Need to distinguish between map and vector/list\n        mov ah, al\n        mov bh, bl\n        \n        and ah, (block_mask + container_mask)\n        and bh, (block_mask + container_mask)\n        cmp ah, bh\n        je .same_container\n        ; if either is a map, then different types\n        cmp ah, container_map\n        je .false\n        cmp bh, container_map\n        je .false\n        \n.same_container:\n        \n        ; Check the container type\n        and bh, block_mask\n        jnz .array\n        \n        ; Check if a pointer to something\n        and al, content_mask\n        cmp al, content_pointer\n        je .pointer\n\n        ; Get the values\n        \n        mov rbx, [rsi + Cons.car]\n        mov rcx, [rdi + Cons.car]\n        cmp rbx, rcx\n        jne .false\n        \n        ; Value is the same, so get next\n        jmp .next\n\n.array:\n        ; Comparing arrays\n        \n        ; Container type (symbol/string) does matter\n        cmp al, bl\n        jne .false\n        \n        call compare_char_array\n        cmp rax, 0\n        ret                     ; Array has no next\n        \n.pointer:\n\n        mov rbx, [rsi + Cons.car]\n        mov rcx, [rdi + Cons.car]\n        cmp rbx, rcx\n        je .next                ; Equal pointers\n        \n        push rsi\n        push rdi\n        ; Put the addresses to compare into RSI and RDI\n        mov rsi, rbx\n        mov rdi, rcx\n        call compare_objects_rec\n        pop rdi\n        pop rsi\n        jne .false\n        ; fall through to .next\n        \n.next:\n        ; Check if both have a 'cdr' pointer\n        mov al, BYTE [rsi + Cons.typecdr]\n        mov bl, BYTE [rdi + Cons.typecdr]\n        \n        cmp al, content_pointer\n        je .rsi_has_next\n        \n        ; No next pointer in RSI\n        cmp bl, content_pointer\n        je .false               ; RDI has a next pointer\n\n        ; Neither have a next pointer, so done\n        jmp .true\n        \n.rsi_has_next:\n        cmp bl, content_pointer\n        jne .false              ; RDI has no next pointer\n        \n        ; Both have a next pointer, so keep going\n        mov rsi, [rsi + Cons.cdr]\n        mov rdi, [rdi + Cons.cdr]\n        jmp compare_objects_rec\n        \n.false:\n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        ret\n.true:\n        lahf                    ; flags in AH\n        or ah, 64               ; set zero flag\n        sahf\n        ret\n        \n;; Char array objects (strings, symbols, keywords) in RSI and RDI\n;; Return code in RAX\n;;\n;; Modifies registers:\n;;   RBX\n;;   RCX\n;;   RDX\ncompare_char_array:\n        ; Check length\n        mov eax, DWORD [rsi + Array.length]\n        mov ebx, DWORD [rdi + Array.length]\n        cmp eax, ebx\n        jne .different\n\n        ; same length\n\n        cmp eax, 0\n        je .equal               ; Both zero length\n        \n        mov rbx, rsi\n        add rbx, Array.data\n        mov rcx, rdi\n        add rcx, Array.data\n.compare_loop:\n        ; get next character\n        mov dl, BYTE [rbx]\n        cmp dl, BYTE [rcx]\n        jl .rdi_greater\n        jg .rsi_greater\n\n        ; this character is equal\n        inc rbx\n        inc rcx\n        dec eax\n        jnz .compare_loop       ; Next character\n\n.equal:\n        mov rax, 0\n        ret\n        \n.rsi_greater:                   ; rsi > rdi\n        mov rax, 1\n        ret\n        \n.rdi_greater:                   ; rdi > rsi\n        mov rax, 2\n        ret\n        \n.different:\n        mov rax, -1\n        ret\n        \n;; ------------------------------------------------------------\n;; Map type\n;;\n;; This uses a list (Cons type) to represent key-value pairs in\n;; a single chain. The only map which consists of an odd number of Cons\n;; objects is the empty map, created by map_new\nmap_new:\n        call alloc_cons\n        mov [rax], BYTE (block_cons + container_map + content_empty)\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n\n;; Copy map\n;;\n;; Input:  RSI - map\n;;\n;; Returns: new map in RAX\n;;\n;; Modifies:\n;;    RAX, RBX, RCX, R13, R14, R15\n;;\nmap_copy:\n        mov r14, rsi\n        \n        call alloc_cons\n        mov r15, rax            ; start of new map\n        xor r13, r13\n.loop:\n        mov bl, BYTE [rsi]\n        mov rcx, [rsi + Cons.car]\n        mov [rax], BYTE bl      ; copy type\n        mov [rax + Cons.car], rcx ; copy value\n\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .set_cdr\n\n        ; A pointer in CAR. Increase reference count\n        mov bx, WORD [rcx + Cons.refcount]\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.set_cdr:\n        test r13,r13\n        jz .next\n        \n        ; R13 contains last Cons\n        mov [r13 + Cons.typecdr], BYTE content_pointer\n        mov [r13 + Cons.cdr], rax\n.next:\n        mov r13, rax\n\n        ; Check if there's another Cons\n        mov bl, BYTE [rsi + Cons.typecdr]\n        cmp bl, content_pointer\n        jne .done               ; no more\n        \n        mov rsi, [rsi + Cons.cdr] ; next\n        call alloc_cons\n        jmp .loop\n.done:\n        mov rax, r15\n        mov rsi, r14\n        ret\n        \n        \n;; Add to map. Input is a list with an even number of values\n;; as (key, value, key, value, ...)\n;;\n;; Inputs:\n;;    RSI - Map to append to. This is not modified\n;;    RDI - List to add to the map\n;; Outputs:\n;;    RAX - New map\n;;\n;; Modifies:\n;;    RCX\nmap_add:\n        ; Check type of input\n        mov cl, BYTE [rsi]\n        mov cl, ch\n        and ch, block_mask + container_mask\n        cmp ch, block_cons + container_map\n        jne .error\n        \n        mov cl, BYTE [rdi]\n        and cl, block_mask + container_mask\n        cmp cl, block_cons + container_list\n        jne .error\n        \n        xor r8, r8              ; Zero r8\n        \n.copy_input:\n        ; Copy input list, changing container type\n        call alloc_cons\n\n        mov cl, BYTE [rdi]\n        and cl, content_mask    ; Keep the content\n        add cl, block_cons + container_map\n        mov [rax], BYTE cl      ; Set type\n        mov rcx, [rdi+Cons.car] ; Copy data\n        mov [rax+Cons.car], rcx\n        \n        cmp cl, (block_cons + container_map + content_pointer)\n        jne .copy_not_pointer\n\n        ; Copying a pointer to data\n        ; so need to increase the reference count\n        mov bx, WORD [rcx + Cons.refcount] ; Same offset for Array\n        inc bx\n        mov [rcx + Cons.refcount], WORD bx\n        \n.copy_not_pointer:\n\n        ; Check if this is the first object\n        cmp r8, 0\n        jnz .copy_not_first\n        mov r8, rax             ; Save start of map to R8\n        mov r9, rax             ; Last cons in R9\n        jmp .copy_next\n        \n.copy_not_first:\n        ; Append to R9\n        mov [r9+Cons.cdr], rax\n        mov [r9+Cons.typecdr], BYTE content_pointer\n\n        ; Put new Cons in R9 as the latest in the list\n        mov r9, rax\n\n.copy_next:\n        ; Check if we've reached the end\n        mov cl, BYTE [rdi + Cons.typecdr]\n        cmp cl, content_nil\n        je .copy_finished\n\n        ; Not yet. Get next Cons and keep going\n        mov rdi, [rdi + Cons.cdr]\n        jmp .copy_input\n        \n.copy_finished:\n        ; Start of map in r8, end in r9\n        \n        ; Check if the original map is empty\n        mov cl, [rsi]\n        and cl, content_mask\n        cmp cl, content_empty\n        je .return\n\n        ; Put old map on the end of the new map\n        ; For now this avoids the need to overwrite\n        ; values in the map, since a search will find\n        ; the new values first. \n\n        mov [r9 + Cons.cdr], rsi\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        \n        ; Increment reference count\n        mov bx, WORD [rsi + Cons.refcount]\n        inc bx\n        mov [rsi + Cons.refcount], WORD bx\n        \n.return:\n        mov rax, r8\n        ret\n        \n.error:\n        ; Return nil\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n\n;; Find a key in a map\n;;\n;; Inputs: RSI - map  [ Modified ]\n;;         RDI - key  [ Modified ]\n;;\n;; Outputs: RAX - Cons object containing value in CAR\n;;\n;; Modifies registers:\n;;   RBX [compare_objects, alloc_cons]\n;;   RCX [compare_objects]\n;;\n;; \n;; If value is found then the Zero Flag is set\n;;\n;; Examples:\n;;     {a 1 b 2} find a ->  {1 b 2}\n;;     {1 2 3 4} find a ->  {4}\nmap_find:\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_map\n        je .not_found\n\n.map_loop:\n        ; compare RSI and RDI, ignoring differences in container\n        push rsi\n        push rdi\n        call compare_objects\n        pop rdi\n        pop rsi\n        \n        ; rax is now zero if objects are equal\n        cmp rax, 0\n        je .found\n\n        ; Move along two cons to the next key\n        mov al, [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .error              ; Expecting value after key\n        \n        mov rsi, [rsi + Cons.cdr] ; Get value\n        mov al, [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .not_found\n        \n        mov rsi, [rsi + Cons.cdr] ; Get next key\n        \n        jmp .map_loop           ; Test next key\n        \n.found:\n        \n        lahf                    ; flags in AH\n        or ah, 64               ; set zero flag\n        sahf\n        \n        ; key in rsi. Get next value\n        mov al, [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .error              ; Expecting value after key\n        \n        mov rsi, [rsi + Cons.cdr]\n        \n        ; ; increment reference count\n        ; mov ax, WORD [rsi + Cons.refcount]\n        ; inc ax\n        ; mov [rsi + Cons.refcount], WORD ax\n        ; Put address in rax\n        mov rax, rsi\n        ret\n\n.not_found:\n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        \n        ; last cons in rsi\n        ; increment reference count\n        ; mov ax, WORD [rsi + Cons.refcount]\n        ; inc ax\n        ; mov [rsi + Cons.refcount], WORD ax\n        ; Put address in rax\n        mov rax, rsi\n        \n        ret\n\n.error:\n        \n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        \n        ; return nil\n        call alloc_cons\n        mov [rax], BYTE maltype_nil\n        mov [rax + Cons.typecdr], BYTE content_nil\n        ret\n\n;; Map set\n;;\n;; Sets a key-value pair in a map\n;;\n;; Inputs: RSI - map [not modified]\n;;         RDI - key [not modified]\n;;         RCX - value [not modified]\n;;\n;; If references are added to key or value,\n;; then reference counts are incremented.\n;; \n;; Modifies registers:\n;;   R8\n;;   R9\n;;   R10\nmap_set:\n        ; Save inputs in less volatile registers\n        mov r8, rsi             ; map\n        mov r9, rdi             ; key\n        mov r10, rcx            ; value\n        \n        ; Find the key, to see if it already exists in the map\n        call map_find           ; Cons object in RAX\n        je .found_key\n\n        ; Key not in map. RAX should be address of the last\n        ; value in the map, or empty\n        mov bl, BYTE [rax]\n        cmp bl, maltype_empty_map\n        je .set_key\n        \n        ; Append key\n        push rax\n        call alloc_cons         ; New Cons in rax\n        pop rbx                 ; Last Cons in map\n\n        ; append rax to rbx\n        mov [rbx + Cons.typecdr], BYTE content_pointer\n        mov [rbx + Cons.cdr], rax\n        jmp .set_key            ; Put key into rax\n        \n.found_key:\n        ; Key already in map, so replace value\n        ; address in RAX\n        \n        ; check type of value already there\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        jne .set_value          ; Not a pointer, just overwrite\n\n        ; A pointer, so need to release\n        mov rsi, [rax + Cons.car] ; Address of object\n        push rax\n        call release_object\n        pop rax\n        \n        jmp .set_value          ; put value into Cons\n        \n.set_key:\n        ; Put key (R9) in RAX\n\n        ; Check the type of object\n        mov bl, BYTE [r9]\n        mov bh, bl\n        and bh, block_mask\n        jnz .set_key_pointer  ; Array, so point to it\n        \n        ; Here a Cons object\n        mov bh, bl\n        and bh, container_mask\n        cmp bh, container_value\n        jne .set_key_pointer  ; Not a simple value, so point to it\n        \n        ; A value, so copy\n        mov rcx, [r9 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        ; Set the type\n        and bl, content_mask\n        or bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n\n        jmp .set_key_done\n        \n.set_key_pointer:\n        ; The key is a pointer\n        \n        mov [rax + Cons.car], r9\n        mov [rax], BYTE (block_cons + container_map + content_pointer)\n        ; Increment reference count\n        mov bx, WORD [r9 + Cons.refcount]\n        inc bx\n        mov [r9 + Cons.refcount], bx\n        ; fall through to .set_key_done\n        \n.set_key_done:\n        ; Key in RAX. allocate and append a Cons for the value\n        push rax\n        call alloc_cons      ; value Cons in rax\n        pop rbx                 ; key Cons\n        ; append rax to rbx\n        mov [rbx + Cons.typecdr], BYTE content_pointer\n        mov [rbx + Cons.cdr], rax\n\n        ; fall through to .set_value\n        \n        ; --------------------------------\n.set_value:\n        ; Set the value into the Cons at [rax]\n        \n        ; Check the type of object\n        mov bl, BYTE [r10]\n        mov bh, bl\n        and bh, block_mask\n        jnz .set_value_pointer  ; Array, so point to it\n\n        ; Here a Cons object\n        mov bh, bl\n        and bh, container_mask\n        cmp bh, container_value\n        jne .set_value_pointer  ; Not a simple value, so point to it\n        ; A value, so copy\n        mov rcx, [r10 + Cons.car]\n        mov [rax + Cons.car], rcx\n\n        ; Set the type\n        and bl, content_mask\n        or bl, (block_cons + container_map)\n        mov [rax], BYTE bl\n\n        jmp .finished\n        \n.set_value_pointer:\n        mov [rax + Cons.car], r10 ; Put address into CAR\n        mov [rax], BYTE (block_cons + container_map + content_pointer) ; Mark as a pointer\n        ; Increment reference count\n        mov bx, WORD [r10 + Cons.refcount]\n        inc bx\n        mov [r10 + Cons.refcount], bx\n        ; fall through to .finished\n        \n.finished:\n        ; Restore inputs\n        mov rsi, r8\n        mov rdi, r9\n        mov rcx, r10\n        ret\n        \n;; Get a value from a map, incrementing the reference count\n;; of the object returned\n;;\n;; Inputs: RSI - map\n;;         RDI - key\n;;\n;; Returns: If found, Zero Flag is set and address in RAX\n;;          If not found, Zero Flag cleared\n;;\n;; Modifies registers:\n;;   RAX\n;;   RBX\n;;   RCX\n;;   R8\n;;   R9\nmap_get:\n        ; Save inputs\n        mov r8, rsi             ; map\n        mov r9, rdi             ; key\n\n        call map_find           ; Cons object in RAX\n        je .found_key\n\n        ; Not found\n        \n        mov rsi, r8\n        mov rdi, r9\n        \n        lahf                    ; flags in AH\n        and ah, 255-64          ; clear zero flag\n        sahf\n        \n        ret\n        ; ---------------\n.found_key:\n        \n        ; Check if the object in RAX is a value or pointer\n        mov bl, BYTE [rax]\n        and bl, content_mask\n        cmp bl, content_pointer\n        je .got_pointer\n\n        ; A value, so copy\n\n        push rax\n        push rbx\n        call alloc_cons         ; cons in rax\n        pop rbx                 ; content type in bl\n        pop rcx                 ; Object to copy\n        \n        add bl, block_cons + container_value\n        mov [rax], BYTE bl      ; set type\n        mov [rax + Cons.typecdr], BYTE content_nil\n\n        ; Copy value\n        mov rbx, [rcx + Cons.car]\n        mov [rax + Cons.car], rbx\n\n        jmp .finished_found\n        \n.got_pointer:\n        ; A pointer, so get the address\n        mov rax, [rax + Cons.car]\n\n        ; increment reference count\n        mov bx, WORD [rax + Cons.refcount]\n        inc bx\n        mov [rax + Cons.refcount], bx\n\n        ; Fall through to .finished_found\n.finished_found:\n        mov rsi, r8\n        mov rdi, r9\n\n        mov rbx, rax\n        lahf                    ; flags in AH\n        or ah, 64               ; set zero flag\n        sahf\n        mov rax, rbx\n        ret\n\n;; Get a list of keys\n;; \n;; Input: Map in RSI\n;;\n;; Returns: List in RAX\n;;\n;; Modifies registers:\n;;   RAX\n;;   RBX\n;;   RCX\n;;   R8\n;;   R9\nmap_keys:\n        ; check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_map\n        je .empty_map\n\n        and al, container_mask\n        cmp al, container_map\n        jne .empty_map          ; error\n        \n        xor r8, r8              ; Return list\n        \n        ; Take the current value\n.loop:\n        ; Create a new Cons for this key\n        call alloc_cons\n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        add cl, block_cons + container_list\n        mov [rax], BYTE cl      ; Set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx          ; Set value\n\n        and cl, content_mask\n        cmp cl, content_pointer\n        jne .append\n\n        ; A pointer, so increment reference count\n        mov cx, WORD [rbx + Cons.refcount]\n        inc cx\n        mov [rbx + Cons.refcount], WORD cx\n        \n.append:\n        cmp r8, 0\n        je .first\n\n        ; appending\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov [r9 + Cons.cdr], rax\n        mov r9, rax\n        jmp .next\n.first:\n        ; First key, so put into r8\n        mov r8, rax\n        mov r9, rax\n.next:\n        ; First get the value\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done              ; error. Should be a value\n        mov rsi, [rsi + Cons.cdr]\n\n        ; Get the next key\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n        mov rsi, [rsi + Cons.cdr]\n        jmp .loop\n.done:\n        ; Finished, return the list\n        mov rax, r8\n        ret\n        \n.empty_map:\n        ; return empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n\n;; Get a list of values\n;; \n;; Input: Map in RSI\n;;\n;; Returns: List in RAX\n;;\n;; Modifies registers:\n;;   RAX\n;;   RBX\n;;   RCX\n;;   R8\n;;   R9\nmap_vals:\n        ; check type\n        mov al, BYTE [rsi]\n        cmp al, maltype_empty_map\n        je .empty_map\n\n        and al, container_mask\n        cmp al, container_map\n        jne .empty_map          ; error\n        \n        xor r8, r8              ; Return list\n        \n.loop:\n        ; Here should have a key in RSI\n\n        ; First get the value\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done              ; error. Should be a value\n        \n        mov rsi, [rsi + Cons.cdr] ; Now have value in RSI\n        \n        ; Create a new Cons for this value\n        call alloc_cons\n        mov cl, BYTE [rsi]\n        and cl, content_mask\n        add cl, block_cons + container_list\n        mov [rax], BYTE cl      ; Set type\n        mov rbx, [rsi + Cons.car]\n        mov [rax + Cons.car], rbx          ; Set value\n\n        and cl, content_mask\n        cmp cl, content_pointer\n        jne .append\n\n        ; A pointer, so increment reference count\n        mov cx, WORD [rbx + Cons.refcount]\n        inc cx\n        mov [rbx + Cons.refcount], WORD cx\n        \n.append:\n        cmp r8, 0\n        je .first\n\n        ; appending\n        mov [r9 + Cons.typecdr], BYTE content_pointer\n        mov [r9 + Cons.cdr], rax\n        mov r9, rax\n        jmp .next\n.first:\n        ; First key, so put into r8\n        mov r8, rax\n        mov r9, rax\n.next:\n        ; Get the next key\n        mov al, BYTE [rsi + Cons.typecdr]\n        cmp al, content_pointer\n        jne .done\n        mov rsi, [rsi + Cons.cdr]\n        jmp .loop\n.done:\n        ; Finished, return the list\n        mov rax, r8\n        ret\n        \n.empty_map:\n        ; return empty list\n        call alloc_cons\n        mov [rax], BYTE maltype_empty_list\n        ret\n\n        \n;; ------------------------------------------------------------\n;; Function type\n;;\n;; Functions are consist of a list\n;;   - First car is the function address to call\n;;   - Second is the Meta data (nil by default)\n;;   - Third is the environment\n;;   - Fourth is the binds list\n;;   - Fifth is the body of the function\n;;\n;;   ( addr meta env binds body )\n;;\n;;\n\n;; Address of native function in RSI\n;; returns Function object in RAX\nnative_function:\n        call alloc_cons         ; for meta\n        mov [rax], BYTE maltype_nil\n        push rax\n        \n        call alloc_cons         ; For function address\n        mov [rax], BYTE (block_cons + container_function + content_function)\n        mov [rax + Cons.car], rsi\n\n        mov [rax + Cons.typecdr], BYTE content_pointer\n        pop rbx                 ; meta\n        mov [rax + Cons.cdr], rbx\n        ret\n"
  },
  {
    "path": "impls/nim/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install gcc libc-dev nim\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/nim/Makefile",
    "content": "#####################\n\nSOURCES_BASE = types.nim reader.nim printer.nim\nSOURCES_REBUILD = $(SOURCES_BASE) env.nim core.nim\n\n#####################\n\nSRCS = step0_repl.nim step1_read_print.nim step2_eval.nim step3_env.nim \\\n\tstep4_if_fn_do.nim step5_tco.nim step6_file.nim step7_quote.nim \\\n\tstep8_macros.nim step9_try.nim stepA_mal.nim\nBINS = $(SRCS:%.nim=%)\n\n#####################\n\nall: $(BINS)\n\ndist: mal\n\nmal: $(word $(words $(BINS)),$(BINS))\n\tcp $< $@\n\n$(BINS): %: %.nim $(SOURCES_REBUILD)\n\tnim -d:release --nimcache:nimcache-$@ c $@\n\nclean:\n\trm -rf nimcache-*/ $(BINS)\n\trm -f mal\n"
  },
  {
    "path": "impls/nim/core.nim",
    "content": "import strutils, rdstdin, tables, times, sequtils, types, printer, reader\n\ntype MalError* = object of CatchableError\n  t*: MalType\n\n# String functions\nproc pr_str(xs: varargs[MalType]): MalType =\n  str(xs.map(proc(x: MalType): string = x.pr_str(true)).join(\" \"))\n\nproc do_str(xs: varargs[MalType]): MalType =\n  str(xs.map(proc(x: MalType): string = x.pr_str(false)).join)\n\nproc prn(xs: varargs[MalType]): MalType =\n  echo xs.map(proc(x: MalType): string = x.pr_str(true)).join(\" \")\n  result = nilObj\n\nproc println(xs: varargs[MalType]): MalType =\n  echo xs.map(proc(x: MalType): string = x.pr_str(false)).join(\" \")\n  result = nilObj\n\nproc read_str(xs: varargs[MalType]): MalType =\n  read_str(xs[0].str)\n\nproc readline(xs: varargs[MalType]): MalType =\n  str readLineFromStdin(xs[0].str)\n\nproc slurp(xs: varargs[MalType]): MalType =\n  str readFile(xs[0].str)\n\nproc cons(xs: varargs[MalType]): MalType =\n  result = list(xs[0])\n  for x in xs[1].list: result.list.add x\n\nproc concat(xs: varargs[MalType]): MalType =\n  result = list()\n  for x in xs:\n    for i in x.list:\n      result.list.add i\n\nproc vec(xs: varargs[MalType]): MalType =\n  result = MalType(kind: Vector, list: newSeq[MalType](xs[0].list.len))\n  for i, x in xs[0].list:\n    result.list[i] = x\n\nproc nth(xs: varargs[MalType]): MalType =\n  if xs[1].number < xs[0].list.len: return xs[0].list[xs[1].number]\n  else: raise newException(ValueError, \"nth: index out of range\")\n\nproc first(xs: varargs[MalType]): MalType =\n  if xs[0].kind in {List, Vector} and xs[0].list.len > 0:\n    xs[0].list[0]\n  else: nilObj\n\nproc rest(xs: varargs[MalType]): MalType =\n  if xs[0].kind in {List, Vector} and xs[0].list.len > 0:\n    list xs[0].list[1 .. ^1]\n  else: list()\n\nproc throw(xs: varargs[MalType]): MalType =\n  raise (ref MalError)(t: list xs)\n\nproc assoc(xs: varargs[MalType]): MalType =\n  result = hash_map()\n  result.hash_map = xs[0].hash_map\n  for i in countup(1, xs.high, 2):\n    result.hash_map[xs[i].str] = xs[i+1]\n\nproc dissoc(xs: varargs[MalType]): MalType =\n  result = hash_map()\n  result.hash_map = xs[0].hash_map\n  for i in 1 .. xs.high:\n    if result.hash_map.hasKey(xs[i].str): result.hash_map.del(xs[i].str)\n\nproc get(xs: varargs[MalType]): MalType =\n  if xs[0].kind == HashMap:\n    if xs[1].str in xs[0].hash_map:\n      result = xs[0].hash_map[xs[1].str]\n    if not result.isNil: return\n\n  result = nilObj\n\nproc contains_q(xs: varargs[MalType]): MalType =\n  boolObj xs[0].hash_map.hasKey(xs[1].str)\n\nproc keys(xs: varargs[MalType]): MalType =\n  result = list()\n  for key in xs[0].hash_map.keys:\n    result.list.add str(key)\n\nproc vals(xs: varargs[MalType]): MalType =\n  result = list()\n  for value in xs[0].hash_map.values:\n    result.list.add value\n\nproc apply(xs: varargs[MalType]): MalType =\n  var s = newSeq[MalType]()\n  if xs.len > 2:\n    for j in 1 .. xs.high-1:\n      s.add xs[j]\n  s.add xs[xs.high].list\n  xs[0].getFun()(s)\n\nproc map(xs: varargs[MalType]): MalType =\n  result = list()\n  for i in 0 .. xs[1].list.high:\n    result.list.add xs[0].getFun()(xs[1].list[i])\n\nproc conj(xs: varargs[MalType]): MalType =\n  if xs[0].kind == List:\n    result = list()\n    for i in countdown(xs.high, 1):\n      result.list.add xs[i]\n    result.list.add xs[0].list\n  else:\n    result = vector()\n    result.list.add xs[0].list\n    for i in 1..xs.high:\n      result.list.add xs[i]\n  result.meta = xs[0].meta\n\nproc seq(xs: varargs[MalType]): MalType =\n  if xs[0].kind == List:\n    if len(xs[0].list) == 0: return nilObj\n    result = xs[0]\n  elif xs[0].kind == Vector:\n    if len(xs[0].list) == 0: return nilObj\n    result = list()\n    result.list.add xs[0].list\n  elif xs[0].kind == String:\n    if len(xs[0].str) == 0: return nilObj\n    result = list()\n    for i in countup(0, len(xs[0].str) - 1):\n      result.list.add(str xs[0].str.substr(i,i))\n  elif xs[0] == nilObj:\n    result = nilObj\n  else:\n    raise newException(ValueError, \"seq: called on non-sequence\")\n\nproc with_meta(xs: varargs[MalType]): MalType =\n  new result\n  result[] = xs[0][]\n  result.meta = xs[1]\n\nproc meta(xs: varargs[MalType]): MalType =\n  if not xs[0].meta.isNil: xs[0].meta\n  else: nilObj\n\nproc deref(xs: varargs[MalType]): MalType =\n  xs[0].val\n\nproc reset_bang(xs: varargs[MalType]): MalType =\n  xs[0].val = xs[1]\n  result = xs[0].val\n\nproc swap_bang(xs: varargs[MalType]): MalType =\n  var args = @[xs[0].val]\n  for i in 2 .. xs.high:\n    args.add xs[i]\n  xs[0].val = xs[1].getFun()(args)\n  result = xs[0].val\n\nproc time_ms(xs: varargs[MalType]): MalType =\n  number int(epochTime() * 1000)\n\ntemplate wrapNumberFun(op): untyped =\n  fun proc(xs: varargs[MalType]): MalType =\n    number op(xs[0].number, xs[1].number)\n\ntemplate wrapBoolFun(op): untyped =\n  fun proc(xs: varargs[MalType]): MalType =\n    if op(xs[0].number, xs[1].number): trueObj else: falseObj\n\nlet ns* = {\n  \"+\": wrapNumberFun(`+`),\n  \"-\": wrapNumberFun(`-`),\n  \"*\": wrapNumberFun(`*`),\n  \"/\": wrapNumberFun(`div`),\n\n  \"<\":  wrapBoolFun(`<`),\n  \"<=\": wrapBoolFun(`<=`),\n  \">\":  wrapBoolFun(`>`),\n  \">=\": wrapBoolFun(`>=`),\n\n  \"list\": fun list,\n  \"list?\": fun list_q,\n  \"vector\": fun vector,\n  \"vector?\": fun vector_q,\n  \"hash-map\": fun hash_map,\n  \"map?\": fun hash_map_q,\n  \"empty?\": fun empty_q,\n  \"assoc\": fun assoc,\n  \"dissoc\": fun dissoc,\n  \"get\": fun get,\n  \"contains?\": fun contains_q,\n  \"keys\": fun keys,\n  \"vals\": fun vals,\n\n  \"=\": fun equal,\n\n  \"pr-str\": fun pr_str,\n  \"str\": fun do_str,\n  \"prn\": fun prn,\n  \"println\": fun println,\n\n  \"read-string\": fun read_str,\n  \"readline\": fun readline,\n  \"slurp\": fun slurp,\n\n  \"sequential?\": fun seq_q,\n  \"cons\": fun cons,\n  \"concat\": fun concat,\n  \"vec\": fun vec,\n  \"count\": fun count,\n  \"nth\": fun nth,\n  \"first\": fun first,\n  \"rest\": fun rest,\n  \"apply\": fun apply,\n  \"map\": fun map,\n\n  \"conj\": fun conj,\n  \"seq\": fun seq,\n\n  \"throw\": fun throw,\n\n  \"nil?\": fun nil_q,\n  \"true?\": fun true_q,\n  \"false?\": fun false_q,\n  \"string?\": fun string_q,\n  \"symbol\": fun symbol,\n  \"symbol?\": fun symbol_q,\n  \"keyword\": fun keyword,\n  \"keyword?\": fun keyword_q,\n  \"number?\": fun number_q,\n  \"fn?\": fun fn_q,\n  \"macro?\": fun macro_q,\n\n  \"with-meta\": fun with_meta,\n  \"meta\": fun meta,\n  \"atom\": fun atom,\n  \"atom?\": fun atom_q,\n  \"deref\": fun deref,\n  \"reset!\": fun reset_bang,\n  \"swap!\": fun swap_bang,\n\n  \"time-ms\": fun time_ms,\n}\n"
  },
  {
    "path": "impls/nim/env.nim",
    "content": "import tables, types\n\nproc initEnv*(outer: Env = nil, binds, exprs: MalType = nilObj): Env =\n  result = Env(data: initTable[string, MalType](), outer: outer)\n\n  if binds.kind in {List, Vector}:\n    for i, e in binds.list:\n      if e.str == \"&\":\n        result.data[binds.list[i+1].str] = list(exprs.list[i .. ^1])\n        break\n      else:\n        result.data[e.str] = exprs.list[i]\n\nproc set*(e: Env, key: string, value: MalType): MalType {.discardable.} =\n  e.data[key] = value\n  value\n\nproc get*(e: Env, key: string): MalType =\n  var env = e\n  while not env.data.hasKey(key):\n    env = env.outer\n    if env.isNil: return nil\n  return env.data[key]\n"
  },
  {
    "path": "impls/nim/mal.nimble",
    "content": "[Package]\nname          = \"mal\"\nversion       = \"1.1\"\nauthor        = \"Dennis Felsing\"\ndescription   = \"Mal code in Nim\"\nlicense       = \"MIT\"\n\nbin           = \"step0_repl, step1_read_print, step2_eval, step3_env, step4_if_fn_do, step5_tco, step6_file, step7_quote, step8_macros, step9_try, stepA_mal\"\n\n[Deps]\nRequires      = \"nim >= 0.10.3\"\n"
  },
  {
    "path": "impls/nim/nim.cfg",
    "content": "gc: markandsweep\n"
  },
  {
    "path": "impls/nim/printer.nim",
    "content": "import strutils, sequtils, tables, types\n\nproc str_handle(x: string, pr = true): string =\n  if x.len > 0 and x[0] == '\\xff':\n            result = \":\" & x[1 .. x.high]\n  elif pr:  result = \"\\\"\" & x.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\").replace(\"\\n\", \"\\\\n\") & \"\\\"\"\n  else:     result = x\n\nproc pr_str*(m: MalType, pr = true): string =\n  case m.kind\n  of Nil:     result = \"nil\"\n  of True:    result = \"true\"\n  of False:   result = \"false\"\n  of Fun:     result = \"#<function>\"\n  of MalFun:  result = \"#<malfun>\"\n  of Atom:    result = \"(atom \" & m.val.pr_str & \")\"\n  of Symbol:  result = m.str\n  of String:  result = m.str.str_handle(pr)\n  of Number:  result = $m.number\n  of List:    result = \"(\" & m.list.mapIt(it.pr_str(pr)).join(\" \") & \")\"\n  of Vector:  result = \"[\" & m.list.mapIt(it.pr_str(pr)).join(\" \") & \"]\"\n  of HashMap:\n    result = \"{\"\n    for key, val in m.hash_map.pairs:\n      if result.len > 1: result.add \" \"\n      result.add key.str_handle & \" \" & val.pr_str(pr)\n    result.add \"}\"\n"
  },
  {
    "path": "impls/nim/reader.nim",
    "content": "import options, re, strutils, types\n\nlet\n  tokenRE = re\"\"\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)\"\"\"\n  intRE   = re\"-?[0-9]+$\"\n  strRE   = re\"\"\"^\"(?:\\\\.|[^\\\\\"])*\"$\"\"\"\n\ntype\n  Blank* = object of CatchableError\n\n  Reader = object\n    tokens: seq[string]\n    position: int\n\nproc next(r: var Reader): Option[string] =\n  if r.position < r.tokens.len:\n    result = r.tokens[r.position].some\n    inc r.position\n\nproc peek(r: Reader): Option[string] =\n  if r.position < r.tokens.len: return r.tokens[r.position].some\n\nproc tokenize(str: string): seq[string] =\n  result = @[]\n  var pos = 0\n  while pos < str.len:\n    var matches: array[2, string]\n    var len = str.findBounds(tokenRE, matches, pos)\n    if len.first != -1 and len.last != -1 and len.last >= len.first:\n      pos = len.last + 1\n      if matches[0].len > 0 and matches[0][0] != ';':\n        result.add matches[0]\n    else:\n      inc pos\n\nproc read_form(r: var Reader): MalType\n\nproc read_seq(r: var Reader, fr, to: string): seq[MalType] =\n  result = @[]\n  var t = r.next\n  if t.get(\"\") != fr: raise newException(ValueError, \"expected '\" & fr & \"'\")\n\n  t = r.peek\n  while t.get(\"\") != to:\n    if t.get(\"\") == \"\": raise newException(ValueError, \"expected '\" & to & \"', got EOF\")\n    result.add r.read_form\n    t = r.peek\n  discard r.next\n\nproc read_list(r: var Reader): MalType =\n  result = list r.read_seq(\"(\", \")\")\n\nproc read_vector(r: var Reader): MalType =\n  result = vector r.read_seq(\"[\", \"]\")\n\nproc read_hash_map(r: var Reader): MalType =\n  result = hash_map r.read_seq(\"{\", \"}\")\n\nproc read_atom(r: var Reader): MalType =\n  let t = r.next.get(\"\")\n  if t.match(intRE): number t.parseInt\n  elif t[0] == '\"':\n    if not t.match(strRE):\n      raise newException(ValueError, \"expected '\\\"', got EOF\")\n    str t[1 ..< t.high].multiReplace((\"\\\\\\\"\", \"\\\"\"), (\"\\\\n\", \"\\n\"), (\"\\\\\\\\\", \"\\\\\"))\n  elif t[0] == ':':  keyword t[1 .. t.high]\n  elif t == \"nil\":   nilObj\n  elif t == \"true\":  trueObj\n  elif t == \"false\": falseObj\n  else:              symbol t\n\nproc read_form(r: var Reader): MalType =\n  if r.peek.get(\"\")[0] == ';':\n    discard r.next\n    return nilObj\n  case r.peek.get(\"\")\n  of \"'\":\n    discard r.next\n    result = list(symbol \"quote\", r.read_form)\n  of \"`\":\n    discard r.next\n    result = list(symbol \"quasiquote\", r.read_form)\n  of \"~\":\n    discard r.next\n    result = list(symbol \"unquote\", r.read_form)\n  of \"~@\":\n    discard r.next\n    result = list(symbol \"splice-unquote\", r.read_form)\n  of \"^\":\n    discard r.next\n    let meta = r.read_form\n    result = list(symbol \"with-meta\", r.read_form, meta)\n  of \"@\":\n    discard r.next\n    result = list(symbol \"deref\", r.read_form)\n\n  # list\n  of \"(\": result = r.read_list\n  of \")\": raise newException(ValueError, \"unexpected ')'\")\n\n  # vector\n  of \"[\": result = r.read_vector\n  of \"]\": raise newException(ValueError, \"unexpected ']'\")\n\n  # hash-map\n  of \"{\": result = r.read_hash_map\n  of \"}\": raise newException(ValueError, \"unexpected '}'\")\n\n  # atom\n  else:   result = r.read_atom\n\nproc read_str*(str: string): MalType =\n  var r = Reader(tokens: str.tokenize)\n  if r.tokens.len == 0:\n    raise newException(Blank, \"Blank line\")\n  r.read_form\n"
  },
  {
    "path": "impls/nim/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/nim/step0_repl.nim",
    "content": "import rdstdin\n\nproc read(str: string): string = str\n\nproc eval(ast: string): string = ast\n\nproc print(exp: string): string = exp\n\nwhile true:\n try:\n  let line = readLineFromStdin(\"user> \")\n  echo line.read.eval.print\n except IOError: quit()\n"
  },
  {
    "path": "impls/nim/step1_read_print.nim",
    "content": "import rdstdin, types, reader, printer\n\nproc read(str: string): MalType = str.read_str\n\nproc eval(ast: MalType): MalType = ast\n\nproc print(exp: MalType): string = exp.pr_str\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.read.eval.print\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n"
  },
  {
    "path": "impls/nim/step2_eval.nim",
    "content": "import rdstdin, tables, sequtils, types, reader, printer\n\nproc read(str: string): MalType = str.read_str\n\nproc eval(ast: MalType, env: Table[string, MalType]): MalType =\n\n  # echo \"EVAL: \" & ast.pr_str\n\n  case ast.kind\n  of Symbol:\n    if not env.hasKey(ast.str):\n      raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n    result = env[ast.str]\n  of Vector:\n    result = vector ast.list.mapIt(it.eval(env))\n  of HashMap:\n    result = hash_map()\n    for k, v in ast.hash_map.pairs:\n      result.hash_map[k] = v.eval(env)\n  of List:\n    if ast.list.len == 0: return ast\n    let el = ast.list.mapIt(it.eval(env))\n    result = el[0].fun(el[1 .. ^1])\n  else:\n    result = ast\n\nproc print(exp: MalType): string = exp.pr_str\n\ntemplate wrapNumberFun(op): untyped =\n  fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number)\n\nlet repl_env = toTable({\n  \"+\": wrapNumberFun `+`,\n  \"-\": wrapNumberFun `-`,\n  \"*\": wrapNumberFun `*`,\n  \"/\": wrapNumberFun `div`,\n})\n\nproc rep(str: string): string =\n  str.read.eval(repl_env).print\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n"
  },
  {
    "path": "impls/nim/step3_env.nim",
    "content": "import rdstdin, tables, sequtils, types, reader, printer, env\n\nproc read(str: string): MalType = str.read_str\n\nproc eval(ast: MalType, env: Env): MalType =\n\n  let dbgeval = env.get(\"DEBUG-EVAL\")\n  if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n    echo \"EVAL: \" & ast.pr_str\n\n  case ast.kind\n  of Symbol:\n    result = env.get(ast.str)\n    if result.isNil:\n      raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n  of Vector:\n    result = vector ast.list.mapIt(it.eval(env))\n  of HashMap:\n    result = hash_map()\n    for k, v in ast.hash_map.pairs:\n      result.hash_map[k] = v.eval(env)\n  of List:\n    if ast.list.len == 0: return ast\n    let\n      a0 = ast.list[0]\n      a1 = ast.list[1]\n      a2 = ast.list[2]\n\n    case a0.str\n    of \"def!\":\n      result = env.set(a1.str, a2.eval(env))\n    of \"let*\":\n      let let_env = initEnv(env)\n      case a1.kind\n      of List, Vector:\n        for i in countup(0, a1.list.high, 2):\n          let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n      else: discard\n      result = a2.eval(let_env)\n    else:\n      let el = ast.list.mapIt(it.eval(env))\n      result = el[0].fun(el[1 .. ^1])\n  else:\n    result = ast\n\nproc print(exp: MalType): string = exp.pr_str\n\ntemplate wrapNumberFun(op): untyped =\n  fun proc(xs: varargs[MalType]): MalType = number op(xs[0].number, xs[1].number)\n\nlet repl_env = initEnv()\n\nrepl_env.set(\"+\", wrapNumberFun(`+`))\nrepl_env.set(\"-\", wrapNumberFun(`-`))\nrepl_env.set(\"*\", wrapNumberFun(`*`))\nrepl_env.set(\"/\", wrapNumberFun(`div`))\n#repl_env.set(\"/\", wrapNumberFun(proc(x,y: int): int = int(x.float / y.float)))\n\nproc rep(str: string): string =\n  str.read.eval(repl_env).print\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/step4_if_fn_do.nim",
    "content": "import rdstdin, tables, sequtils, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc eval(ast: MalType, env: Env): MalType =\n\n  let dbgeval = env.get(\"DEBUG-EVAL\")\n  if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n    echo \"EVAL: \" & ast.pr_str\n\n  case ast.kind\n  of Symbol:\n    result = env.get(ast.str)\n    if result.isNil:\n      raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n  of Vector:\n    result = vector ast.list.mapIt(it.eval(env))\n  of HashMap:\n    result = hash_map()\n    for k, v in ast.hash_map.pairs:\n      result.hash_map[k] = v.eval(env)\n  of List:\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        return env.set(a1.str, a2.eval(env))\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: discard\n        return a2.eval(let_env)\n\n      of \"do\":\n        let el = ast.list[1 .. ^1].mapIt(it.eval(env))\n        return el[el.high]\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3: return ast.list[3].eval(env)\n          else: return nilObj\n        else: return a2.eval(env)\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        return fun(proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a))))\n\n    let el = ast.list.mapIt(it.eval(env))\n    result = el[0].fun(el[1 .. ^1])\n\n  else:\n    result = ast\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\n\n# core.nim: defined using nim\nproc rep(str: string): string =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\ndiscard rep \"(def! not (fn* (a) (if a false true)))\"\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/step5_tco.nim",
    "content": "import rdstdin, tables, sequtils, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc eval(ast: MalType, env: Env): MalType =\n  var ast = ast\n  var env = env\n\n  while true:\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n      echo \"EVAL: \" & ast.pr_str\n\n    case ast.kind\n    of Symbol:\n      let val = env.get(ast.str)\n      if val.isNil:\n        raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n      return val\n    of List:\n      discard(nil) # Proceed after the case statement\n    of Vector:\n      return vector ast.list.mapIt(it.eval(env))\n    of HashMap:\n      result = hash_map()\n      for k, v in ast.hash_map.pairs:\n        result.hash_map[k] = v.eval(env)\n      return result\n    else:\n      return ast\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        return env.set(a1.str, a2.eval(env))\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: raise newException(ValueError, \"Illegal kind in let*\")\n        ast = a2\n        env = let_env\n        continue # TCO\n\n      of \"do\":\n        let last = ast.list.high\n        discard (ast.list[1 ..< last].mapIt(it.eval(env)))\n        ast = ast.list[last]\n        continue # TCO\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3:\n            ast = ast.list[3]\n            continue # TCO\n          else:\n            return nilObj\n        else:\n          ast = a2\n          continue # TCO\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let fn = proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a)))\n        return malfun(fn, a2, a1, env)\n\n    let f = eval(a0, env)\n    let args = ast.list[1 .. ^1].mapIt(it.eval(env))\n    if f.kind == MalFun:\n      ast = f.malfun.ast\n      env = initEnv(f.malfun.env, f.malfun.params, list(args))\n      continue # TCO\n\n    return f.fun(args)\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\n\n# core.nim: defined using nim\nproc rep(str: string): string =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\ndiscard rep \"(def! not (fn* (a) (if a false true)))\"\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/step6_file.nim",
    "content": "import rdstdin, tables, sequtils, os, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc eval(ast: MalType, env: Env): MalType =\n  var ast = ast\n  var env = env\n\n  while true:\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n      echo \"EVAL: \" & ast.pr_str\n\n    case ast.kind\n    of Symbol:\n      let val = env.get(ast.str)\n      if val.isNil:\n        raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n      return val\n    of List:\n      discard(nil) # Proceed after the case statement\n    of Vector:\n      return vector ast.list.mapIt(it.eval(env))\n    of HashMap:\n      result = hash_map()\n      for k, v in ast.hash_map.pairs:\n        result.hash_map[k] = v.eval(env)\n      return result\n    else:\n      return ast\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        return env.set(a1.str, a2.eval(env))\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: raise newException(ValueError, \"Illegal kind in let*\")\n        ast = a2\n        env = let_env\n        continue # TCO\n\n      of \"do\":\n        let last = ast.list.high\n        discard (ast.list[1 ..< last].mapIt(it.eval(env)))\n        ast = ast.list[last]\n        continue # TCO\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3:\n            ast = ast.list[3]\n            continue # TCO\n          else:\n            return nilObj\n        else:\n          ast = a2\n          continue # TCO\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let fn = proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a)))\n        return malfun(fn, a2, a1, env)\n\n    let f = eval(a0, env)\n    let args = ast.list[1 .. ^1].mapIt(it.eval(env))\n    if f.kind == MalFun:\n      ast = f.malfun.ast\n      env = initEnv(f.malfun.env, f.malfun.params, list(args))\n      continue # TCO\n\n    return f.fun(args)\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\nrepl_env.set(\"eval\", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))\nlet ps = commandLineParams()\nrepl_env.set(\"*ARGV*\", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))\n\n\n# core.nim: defined using nim\nproc rep(str: string): string {.discardable.} =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\nrep \"(def! not (fn* (a) (if a false true)))\"\nrep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\nif paramCount() >= 1:\n  rep \"(load-file \\\"\" & paramStr(1) & \"\\\")\"\n  quit()\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except Blank: discard\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/step7_quote.nim",
    "content": "import rdstdin, tables, sequtils, os, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc quasiquote(ast: MalType): MalType\n\nproc quasiquote_loop(xs: seq[MalType]): MalType =\n  result = list()\n  for i in countdown(xs.high, 0):\n    let elt = xs[i]\n    if elt.kind == List and 0 < elt.list.len and elt.list[0] == symbol \"splice-unquote\":\n      result = list(symbol \"concat\", elt.list[1], result)\n    else:\n      result = list(symbol \"cons\", quasiquote(elt), result)\n\nproc quasiquote(ast: MalType): MalType =\n  case ast.kind\n  of List:\n    if ast.list.len == 2 and ast.list[0] == symbol \"unquote\":\n      result = ast.list[1]\n    else:\n      result = quasiquote_loop(ast.list)\n  of Vector:\n    result = list(symbol \"vec\", quasiquote_loop(ast.list))\n  of Symbol:\n    result = list(symbol \"quote\", ast)\n  of HashMap:\n    result = list(symbol \"quote\", ast)\n  else:\n    result = ast\n\nproc eval(ast: MalType, env: Env): MalType =\n  var ast = ast\n  var env = env\n\n  while true:\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n      echo \"EVAL: \" & ast.pr_str\n\n    case ast.kind\n    of Symbol:\n      let val = env.get(ast.str)\n      if val.isNil:\n        raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n      return val\n    of List:\n      discard(nil) # Proceed after the case statement\n    of Vector:\n      return vector ast.list.mapIt(it.eval(env))\n    of HashMap:\n      result = hash_map()\n      for k, v in ast.hash_map.pairs:\n        result.hash_map[k] = v.eval(env)\n      return result\n    else:\n      return ast\n\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        return env.set(a1.str, a2.eval(env))\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: raise newException(ValueError, \"Illegal kind in let*\")\n        ast = a2\n        env = let_env\n        continue # TCO\n\n      of \"quote\":\n        return ast.list[1]\n\n      of \"quasiquote\":\n        ast = ast.list[1].quasiquote\n        continue # TCO\n\n      of \"do\":\n        let last = ast.list.high\n        discard (ast.list[1 ..< last].mapIt(it.eval(env)))\n        ast = ast.list[last]\n        continue # TCO\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3:\n            ast = ast.list[3]\n            continue # TCO\n          else:\n            return nilObj\n        else:\n          ast = a2\n          continue # TCO\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let fn = proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a)))\n        return malfun(fn, a2, a1, env)\n\n    let f = eval(a0, env)\n    let args = ast.list[1 .. ^1].mapIt(it.eval(env))\n    if f.kind == MalFun:\n      ast = f.malfun.ast\n      env = initEnv(f.malfun.env, f.malfun.params, list(args))\n      continue # TCO\n\n    return f.fun(args)\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\nrepl_env.set(\"eval\", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))\nlet ps = commandLineParams()\nrepl_env.set(\"*ARGV*\", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))\n\n\n# core.nim: defined using nim\nproc rep(str: string): string {.discardable.} =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\nrep \"(def! not (fn* (a) (if a false true)))\"\nrep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\nif paramCount() >= 1:\n  rep \"(load-file \\\"\" & paramStr(1) & \"\\\")\"\n  quit()\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except Blank: discard\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/step8_macros.nim",
    "content": "import rdstdin, tables, sequtils, os, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc quasiquote(ast: MalType): MalType\n\nproc quasiquote_loop(xs: seq[MalType]): MalType =\n  result = list()\n  for i in countdown(xs.high, 0):\n    let elt = xs[i]\n    if elt.kind == List and 0 < elt.list.len and elt.list[0] == symbol \"splice-unquote\":\n      result = list(symbol \"concat\", elt.list[1], result)\n    else:\n      result = list(symbol \"cons\", quasiquote(elt), result)\n\nproc quasiquote(ast: MalType): MalType =\n  case ast.kind\n  of List:\n    if ast.list.len == 2 and ast.list[0] == symbol \"unquote\":\n      result = ast.list[1]\n    else:\n      result = quasiquote_loop(ast.list)\n  of Vector:\n    result = list(symbol \"vec\", quasiquote_loop(ast.list))\n  of Symbol:\n    result = list(symbol \"quote\", ast)\n  of HashMap:\n    result = list(symbol \"quote\", ast)\n  else:\n    result = ast\n\nproc eval(ast: MalType, env: Env): MalType =\n  var ast = ast\n  var env = env\n\n  while true:\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n      echo \"EVAL: \" & ast.pr_str\n\n    case ast.kind\n    of Symbol:\n      let val = env.get(ast.str)\n      if val.isNil:\n        raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n      return val\n    of List:\n      discard(nil) # Proceed after the case statement\n    of Vector:\n      return vector ast.list.mapIt(it.eval(env))\n    of HashMap:\n      result = hash_map()\n      for k, v in ast.hash_map.pairs:\n        result.hash_map[k] = v.eval(env)\n      return result\n    else:\n      return ast\n\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        return env.set(a1.str, a2.eval(env))\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: raise newException(ValueError, \"Illegal kind in let*\")\n        ast = a2\n        env = let_env\n        continue # TCO\n\n      of \"quote\":\n        return ast.list[1]\n\n      of \"quasiquote\":\n        ast = ast.list[1].quasiquote\n        continue # TCO\n\n      of \"defmacro!\":\n        let fun = ast.list[2].eval(env)\n        let mac = malfun(fun.malfun.fn, fun.malfun.ast, fun.malfun.params, fun.malfun.env, true)\n        return env.set(ast.list[1].str, mac)\n\n      of \"do\":\n        let last = ast.list.high\n        discard (ast.list[1 ..< last].mapIt(it.eval(env)))\n        ast = ast.list[last]\n        continue # TCO\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3:\n            ast = ast.list[3]\n            continue # TCO\n          else:\n            return nilObj\n        else:\n          ast = a2\n          continue # TCO\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let fn = proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a)))\n        return malfun(fn, a2, a1, env)\n\n    let f = eval(a0, env)\n    if f.fun_is_macro:\n      ast = f.malfun.fn(ast.list[1 .. ^1])\n      continue # TCO\n    let args = ast.list[1 .. ^1].mapIt(it.eval(env))\n    if f.kind == MalFun:\n      ast = f.malfun.ast\n      env = initEnv(f.malfun.env, f.malfun.params, list(args))\n      continue # TCO\n\n    return f.fun(args)\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\nrepl_env.set(\"eval\", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))\nlet ps = commandLineParams()\nrepl_env.set(\"*ARGV*\", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))\n\n\n# core.nim: defined using nim\nproc rep(str: string): string {.discardable.} =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\nrep \"(def! not (fn* (a) (if a false true)))\"\nrep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nrep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\nif paramCount() >= 1:\n  rep \"(load-file \\\"\" & paramStr(1) & \"\\\")\"\n  quit()\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except Blank: discard\n  except IOError: quit()\n  except:\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/step9_try.nim",
    "content": "import rdstdin, tables, sequtils, os, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc quasiquote(ast: MalType): MalType\n\nproc quasiquote_loop(xs: seq[MalType]): MalType =\n  result = list()\n  for i in countdown(xs.high, 0):\n    let elt = xs[i]\n    if elt.kind == List and 0 < elt.list.len and elt.list[0] == symbol \"splice-unquote\":\n      result = list(symbol \"concat\", elt.list[1], result)\n    else:\n      result = list(symbol \"cons\", quasiquote(elt), result)\n\nproc quasiquote(ast: MalType): MalType =\n  case ast.kind\n  of List:\n    if ast.list.len == 2 and ast.list[0] == symbol \"unquote\":\n      result = ast.list[1]\n    else:\n      result = quasiquote_loop(ast.list)\n  of Vector:\n    result = list(symbol \"vec\", quasiquote_loop(ast.list))\n  of Symbol:\n    result = list(symbol \"quote\", ast)\n  of HashMap:\n    result = list(symbol \"quote\", ast)\n  else:\n    result = ast\n\nproc eval(ast: MalType, env: Env): MalType =\n  var ast = ast\n  var env = env\n\n  while true:\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n      echo \"EVAL: \" & ast.pr_str\n\n    case ast.kind\n    of Symbol:\n      let val = env.get(ast.str)\n      if val.isNil:\n        raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n      return val\n    of List:\n      discard(nil) # Proceed after the case statement\n    of Vector:\n      return vector ast.list.mapIt(it.eval(env))\n    of HashMap:\n      result = hash_map()\n      for k, v in ast.hash_map.pairs:\n        result.hash_map[k] = v.eval(env)\n      return result\n    else:\n      return ast\n\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          res = a2.eval(env)\n        return env.set(a1.str, res)\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: raise newException(ValueError, \"Illegal kind in let*\")\n        ast = a2\n        env = let_env\n        continue # TCO\n\n      of \"quote\":\n        return ast.list[1]\n\n      of \"quasiquote\":\n        ast = ast.list[1].quasiquote\n        continue # TCO\n\n      of \"defmacro!\":\n        let fun = ast.list[2].eval(env)\n        let mac = malfun(fun.malfun.fn, fun.malfun.ast, fun.malfun.params, fun.malfun.env, true)\n        return env.set(ast.list[1].str, mac)\n\n      of \"try*\":\n        let a1 = ast.list[1]\n        if ast.list.len <= 2:\n          ast = a1\n          continue # TCO\n        let a2 = ast.list[2]\n        try:\n          return a1.eval(env)\n        except MalError:\n          let exc = (ref MalError) getCurrentException()\n          env = initEnv(env, list a2.list[1], exc.t)\n          ast = a2.list[2]\n          continue # TCO\n        except:\n          let exc = getCurrentExceptionMsg()\n          env = initEnv(env, list a2.list[1], list str (exc))\n          ast = a2.list[2]\n          continue # TCO\n\n      of \"do\":\n        let last = ast.list.high\n        discard (ast.list[1 ..< last].mapIt(it.eval(env)))\n        ast = ast.list[last]\n        continue # TCO\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3:\n            ast = ast.list[3]\n            continue # TCO\n          else:\n            return nilObj\n        else:\n          ast = a2\n          continue # TCO\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let fn = proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a)))\n        return malfun(fn, a2, a1, env)\n\n    let f = eval(a0, env)\n    if f.fun_is_macro:\n      ast = f.malfun.fn(ast.list[1 .. ^1])\n      continue # TCO\n    let args = ast.list[1 .. ^1].mapIt(it.eval(env))\n    if f.kind == MalFun:\n      ast = f.malfun.ast\n      env = initEnv(f.malfun.env, f.malfun.params, list(args))\n      continue # TCO\n\n    return f.fun(args)\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\nrepl_env.set(\"eval\", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))\nlet ps = commandLineParams()\nrepl_env.set(\"*ARGV*\", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))\n\n\n# core.nim: defined using nim\nproc rep(str: string): string {.discardable.} =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\nrep \"(def! not (fn* (a) (if a false true)))\"\nrep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nrep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n\nif paramCount() >= 1:\n  rep \"(load-file \\\"\" & paramStr(1) & \"\\\")\"\n  quit()\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except Blank: discard\n  except IOError: quit()\n  except MalError:\n    let exc = (ref MalError) getCurrentException()\n    echo \"Error: \" & exc.t.list[0].pr_str\n  except:\n    stdout.write \"Error: \"\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/stepA_mal.nim",
    "content": "import rdstdin, tables, sequtils, os, types, reader, printer, env, core\n\nproc read(str: string): MalType = str.read_str\n\nproc quasiquote(ast: MalType): MalType\n\nproc quasiquote_loop(xs: seq[MalType]): MalType =\n  result = list()\n  for i in countdown(xs.high, 0):\n    let elt = xs[i]\n    if elt.kind == List and 0 < elt.list.len and elt.list[0] == symbol \"splice-unquote\":\n      result = list(symbol \"concat\", elt.list[1], result)\n    else:\n      result = list(symbol \"cons\", quasiquote(elt), result)\n\nproc quasiquote(ast: MalType): MalType =\n  case ast.kind\n  of List:\n    if ast.list.len == 2 and ast.list[0] == symbol \"unquote\":\n      result = ast.list[1]\n    else:\n      result = quasiquote_loop(ast.list)\n  of Vector:\n    result = list(symbol \"vec\", quasiquote_loop(ast.list))\n  of Symbol:\n    result = list(symbol \"quote\", ast)\n  of HashMap:\n    result = list(symbol \"quote\", ast)\n  else:\n    result = ast\n\nproc eval(ast: MalType, env: Env): MalType =\n  var ast = ast\n  var env = env\n\n  while true:\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if not (dbgeval.isNil or dbgeval.kind in {Nil, False}):\n      echo \"EVAL: \" & ast.pr_str\n\n    case ast.kind\n    of Symbol:\n      let val = env.get(ast.str)\n      if val.isNil:\n        raise newException(ValueError, \"'\" & ast.str & \"' not found\")\n      return val\n    of List:\n      discard(nil) # Proceed after the case statement\n    of Vector:\n      return vector ast.list.mapIt(it.eval(env))\n    of HashMap:\n      result = hash_map()\n      for k, v in ast.hash_map.pairs:\n        result.hash_map[k] = v.eval(env)\n      return result\n    else:\n      return ast\n\n    if ast.list.len == 0: return ast\n\n    let a0 = ast.list[0]\n    if a0.kind == Symbol:\n      case a0.str\n      of \"def!\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          res = a2.eval(env)\n        return env.set(a1.str, res)\n\n      of \"let*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let let_env = initEnv(env)\n        case a1.kind\n        of List, Vector:\n          for i in countup(0, a1.list.high, 2):\n            let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env))\n        else: raise newException(ValueError, \"Illegal kind in let*\")\n        ast = a2\n        env = let_env\n        continue # TCO\n\n      of \"quote\":\n        return ast.list[1]\n\n      of \"quasiquote\":\n        ast = ast.list[1].quasiquote\n        continue # TCO\n\n      of \"defmacro!\":\n        let fun = ast.list[2].eval(env)\n        let mac = malfun(fun.malfun.fn, fun.malfun.ast, fun.malfun.params, fun.malfun.env, true)\n        return env.set(ast.list[1].str, mac)\n\n      of \"try*\":\n        let a1 = ast.list[1]\n        if ast.list.len <= 2:\n          ast = a1\n          continue # TCO\n        let a2 = ast.list[2]\n        try:\n          return a1.eval(env)\n        except MalError:\n          let exc = (ref MalError) getCurrentException()\n          env = initEnv(env, list a2.list[1], exc.t)\n          ast = a2.list[2]\n          continue # TCO\n        except:\n          let exc = getCurrentExceptionMsg()\n          env = initEnv(env, list a2.list[1], list str (exc))\n          ast = a2.list[2]\n          continue # TCO\n\n      of \"do\":\n        let last = ast.list.high\n        discard (ast.list[1 ..< last].mapIt(it.eval(env)))\n        ast = ast.list[last]\n        continue # TCO\n\n      of \"if\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n          cond = a1.eval(env)\n\n        if cond.kind in {Nil, False}:\n          if ast.list.len > 3:\n            ast = ast.list[3]\n            continue # TCO\n          else:\n            return nilObj\n        else:\n          ast = a2\n          continue # TCO\n\n      of \"fn*\":\n        let\n          a1 = ast.list[1]\n          a2 = ast.list[2]\n        let fn = proc(a: varargs[MalType]): MalType =\n          a2.eval(initEnv(env, a1, list(a)))\n        return malfun(fn, a2, a1, env)\n\n    let f = eval(a0, env)\n    if f.fun_is_macro:\n      ast = f.malfun.fn(ast.list[1 .. ^1])\n      continue # TCO\n    let args = ast.list[1 .. ^1].mapIt(it.eval(env))\n    if f.kind == MalFun:\n      ast = f.malfun.ast\n      env = initEnv(f.malfun.env, f.malfun.params, list(args))\n      continue # TCO\n\n    return f.fun(args)\n\nproc print(exp: MalType): string = exp.pr_str\n\nlet repl_env = initEnv()\n\nfor k, v in ns.items:\n  repl_env.set(k, v)\nrepl_env.set(\"eval\", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env)))\nlet ps = commandLineParams()\nrepl_env.set(\"*ARGV*\", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str)))\n\n\n# core.nim: defined using nim\nproc rep(str: string): string {.discardable.} =\n  str.read.eval(repl_env).print\n\n# core.mal: defined using mal itself\nrep \"(def! not (fn* (a) (if a false true)))\"\nrep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\nrep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\nrep \"(def! *host-language* \\\"nim\\\")\"\n\nif paramCount() >= 1:\n  rep \"(load-file \\\"\" & paramStr(1) & \"\\\")\"\n  quit()\n\nrep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"\n\nwhile true:\n  try:\n    let line = readLineFromStdin(\"user> \")\n    echo line.rep\n  except Blank: discard\n  except IOError: quit()\n  except MalError:\n    let exc = (ref MalError) getCurrentException()\n    echo \"Error: \" & exc.t.list[0].pr_str\n  except:\n    stdout.write \"Error: \"\n    echo getCurrentExceptionMsg()\n    echo getCurrentException().getStackTrace()\n"
  },
  {
    "path": "impls/nim/tests/step5_tco.mal",
    "content": ";; Nim: skipping non-TCO recursion\n;; Reason: completes at 10,000, unrecoverable segfault 20,000\n"
  },
  {
    "path": "impls/nim/types.nim",
    "content": "import tables\n\ntype\n  MalTypeKind* = enum Nil, True, False, Number, Symbol, String,\n    List, Vector, HashMap, Fun, MalFun, Atom\n\n  FunType = proc(a: varargs[MalType]): MalType\n\n  MalFunType* = ref object\n    fn*:       FunType\n    ast*:      MalType\n    params*:   MalType\n    env*:      Env\n    is_macro*: bool\n\n  MalType* = ref object\n    case kind*: MalTypeKind\n    of Nil, True, False: nil\n    of Number:           number*:   int\n    of String, Symbol:   str*:      string\n    of List, Vector:     list*:     seq[MalType]\n    of HashMap:          hash_map*: Table[string, MalType]\n    of Fun:\n                         fun*:      FunType\n                         is_macro*: bool\n    of MalFun:           malfun*:   MalFunType\n    of Atom:             val*:      MalType\n\n    meta*: MalType\n\n  Env* = ref object\n    data*: Table[string, MalType]\n    outer*: Env\n\nlet nilObj* = MalType(kind: Nil)\nlet trueObj* = MalType(kind: True)\nlet falseObj* = MalType(kind: False)\n\nproc number*(x: int): MalType = MalType(kind: Number, number: x)\n\nproc symbol*(x: string): MalType = MalType(kind: Symbol, str: x)\n\nproc str*(x: string): MalType {.procvar.} = MalType(kind: String, str: x)\n\nproc keyword*(x: string): MalType = MalType(kind: String, str: \"\\xff\" & x)\n\nproc atom*(x: MalType): MalType =\n  result = MalType(kind: Atom)\n  result.val = x\n\nproc list*(xs: varargs[MalType]): MalType {.procvar.} =\n  result = MalType(kind: List, list: newSeq[MalType](xs.len))\n  for i, x in xs: result.list[i] = x\n\nproc vector*(xs: varargs[MalType]): MalType {.procvar.} =\n  result = MalType(kind: Vector, list: newSeq[MalType](xs.len))\n  for i, x in xs: result.list[i] = x\n\nproc hash_map*(xs: varargs[MalType]): MalType {.procvar.} =\n  result = MalType(kind: HashMap, hash_map: initTable[string, MalType]())\n  for i in countup(0, xs.high, 2):\n    let s = case xs[i].kind\n    of String: xs[i].str\n    else: xs[i].str\n    result.hash_map[s] = xs[i+1]\n\nproc fun_is_macro*(x: MalType): bool =\n  if x.kind == Fun: result = x.is_macro\n  elif x.kind == MalFun: result = x.malfun.is_macro\n  else: raise newException(ValueError, \"no function\")\n\nproc getFun*(x: MalType): FunType =\n  if x.kind == Fun: result = x.fun\n  elif x.kind == MalFun: result = x.malfun.fn\n  else: raise newException(ValueError, \"no function\")\n\nproc fun*(x: proc(xs: varargs[MalType]): MalType, is_macro = false): MalType =\n  MalType(kind: Fun, fun: x, is_macro: is_macro)\n\nproc malfun*(fn: auto, ast, params: MalType,\n             env: Env, is_macro = false): MalType =\n  MalType(kind: MalFun, malfun: MalFunType(fn: fn, ast: ast, params: params,\n    env: env, is_macro: is_macro))\n\nproc boolObj*(b: bool): MalType =\n  if b: trueObj else: falseObj\n\nproc list_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == List\n\nproc vector_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == Vector\n\nproc seq_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind in {List, Vector}\n\nproc hash_map_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == HashMap\n\nproc empty_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].list.len == 0\n\nproc nil_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == Nil\n\nproc true_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == True\n\nproc false_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == False\n\nproc string_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj(xs[0].kind == String and (xs[0].str.len == 0 or xs[0].str[0] != '\\xff'))\n\nproc symbol*(xs: varargs[MalType]): MalType {.procvar.} =\n  symbol(xs[0].str)\n\nproc symbol_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == Symbol\n\nproc keyword*(xs: varargs[MalType]): MalType {.procvar.} =\n  if 0 < xs[0].str.len and xs[0].str[0] == '\\xff': xs[0]\n  else: keyword(xs[0].str)\n\nproc keyword_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj(xs[0].kind == String and xs[0].str.len > 0 and xs[0].str[0] == '\\xff')\n\nproc number_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == Number\n\nproc fn_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj((xs[0].kind == MalFun or xs[0].kind == Fun) and not xs[0].fun_is_macro)\n\nproc macro_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj((xs[0].kind == MalFun or xs[0].kind == Fun) and xs[0].fun_is_macro)\n\nproc atom*(xs: varargs[MalType]): MalType {.procvar.} =\n  atom(xs[0])\n\nproc atom_q*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0].kind == Atom\n\nproc count*(xs: varargs[MalType]): MalType {.procvar.} =\n  number if xs[0].kind == Nil: 0 else: xs[0].list.len\n\nproc `==`*(x, y: MalType): bool =\n  if not (x.kind in {List, Vector} and y.kind in {List, Vector}):\n    if x.kind != y.kind: return false\n  result = case x.kind\n  of Nil, True, False: true\n  of Number:         x.number   == y.number\n  of Symbol, String: x.str      == y.str\n  of List, Vector:   x.list     == y.list\n  of HashMap:        x.hash_map == y.hash_map\n  of Fun:            x.fun      == y.fun and\n                     x.is_macro == y.is_macro\n  of MalFun:         x.malfun   == y.malfun\n  of Atom:           x.val      == y.val\n\nproc equal*(xs: varargs[MalType]): MalType {.procvar.} =\n  boolObj xs[0] == xs[1]\n"
  },
  {
    "path": "impls/objc/Dockerfile",
    "content": "M ubuntu:vivid\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Based on:\n# https://blog.tlensing.org/2013/02/24/objective-c-on-linux-setting-up-gnustep-clang-llvm-objective-c-2-0-blocks-runtime-gcd-on-ubuntu-12-04/\n\nRUN apt-get -y install build-essential clang libblocksruntime-dev \\\n    libkqueue-dev libpthread-workqueue-dev gobjc libxml2-dev \\\n    libjpeg-dev libtiff-dev libpng12-dev libcups2-dev \\\n    libfreetype6-dev libcairo2-dev libxt-dev libgl1-mesa-dev\n\nRUN mkdir -p /root/gnustep-dev\nRUN cd /root/gnustep-dev && \\\n    curl http://download.gna.org/gnustep/libobjc2-1.7.tar.bz2 \\\n    | tar xjf -\nRUN cd /root/gnustep-dev && \\\n    curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.6.7.tar.gz \\\n    | tar xzf -\nRUN cd /root/gnustep-dev && \\\n    curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-base-1.24.8.tar.gz \\\n    | tar xzf -\nRUN cd /root/gnustep-dev && \\\n    curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-gui-0.24.1.tar.gz \\\n    | tar xzf -\nRUN cd /root/gnustep-dev && \\\n    curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-back-0.24.1.tar.gz \\\n    | tar xzf -\n\n\n# TODO move up\nRUN apt-get -y install gnutls-dev libxslt-dev libffi-dev openssl\n\nENV CC clang\nRUN cd /root/gnustep-dev/libobjc2-1.7 && make && make install\nRUN cd /root/gnustep-dev/gnustep-make-2.6.7 && ./configure && make && make install\nRUN cd /root/gnustep-dev/gnustep-base-1.24.8 && ./configure && make && make install && ldconfig\nRUN cd /root/gnustep-dev/gnustep-gui-0.24.1 && ./configure && make && make install\nRUN cd /root/gnustep-dev/gnustep-back-0.24.1 && ./configure && make && make install\n\nRUN apt-get -y install libdispatch-dev\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/objc/Makefile",
    "content": "STEP0_DEPS = mal_readline.c mal_readline.h\nSTEP1_DEPS = $(STEP0_DEPS) types.h types.m reader.h reader.m printer.h printer.m\nSTEP2_DEPS = $(STEP1_DEPS)\nSTEP3_DEPS = $(STEP2_DEPS) env.m\nSTEP4_DEPS = $(STEP3_DEPS) malfunc.h malfunc.m core.h core.m\n\nSTEPS = step0_repl step1_read_print step2_eval step3_env \\\n\tstep4_if_fn_do step5_tco step6_file step7_quote \\\n\tstep8_macros step9_try stepA_mal\n\n# From: https://blog.tlensing.org/2013/02/24/objective-c-on-linux-setting-up-gnustep-clang-llvm-objective-c-2-0-blocks-runtime-gcd-on-ubuntu-12-04/:\n#   clang `gnustep-config --objc-flags` -o main -x objective-c main.m -fconstant-string-class=NSConstantString -fobjc-nonfragile-abi -fblocks -lgnustep-base -lgnustep-gui -ldispatch -I/usr/local/include/GNUstep -L/usr/local/lib/GNUstep\n\nOS := $(shell uname)\n\n## Bizzare gnustep-config/make interaction causes make to get run\n## during gnustep-config so we need to remove make output\nifeq ($(OS),Darwin)\nCC = clang -framework Foundation\nOBJC_LIBS := -lobjc -lreadline\nelse\n#CC = clang -fblocks -fobjc-nonfragile-abi -fobjc-arc\nCC = clang -fblocks -fobjc-nonfragile-abi\nOBJC_FLAGS := $(shell gnustep-config --objc-flags 2>/dev/null | egrep -v \"Entering|Leaving\")\nOBJC_LIBS := $(filter-out -shared-libgcc,$(shell gnustep-config --base-libs 2>/dev/null | egrep -v \"Entering|Leaving\")) -ldispatch -lreadline\nendif\n\nall: $(STEPS)\n\ndist: mal\n\nmal: stepA_mal\n\tcp $< $@\n\nstep0_repl: $(STEP0_DEPS)\nstep1_read_print: $(STEP1_DEPS)\nstep2_eval: $(STEP2_DEPS)\nstep3_env: $(STEP3_DEPS)\nstep4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal: $(STEP4_DEPS)\n\nstep%: step%.m\n\t$(CC) \\\n\t    -xobjective-c $(filter-out %.h mal_readline%,$+) \\\n\t    -xc mal_readline.c \\\n\t    -o $@ \\\n\t    $(OBJC_FLAGS) \\\n\t    $(OBJC_LIBS)\n\nclean:\n\trm -f $(STEPS) *.o *.d mal\n"
  },
  {
    "path": "impls/objc/core.h",
    "content": "#import <Foundation/Foundation.h>\n\n@interface Core : NSObject\n\n+ (NSDictionary *)ns;\n\n@end\n"
  },
  {
    "path": "impls/objc/core.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n#import <objc/runtime.h>\n\nNSObject * wrap_tf(BOOL val) {\n    return val ? [MalTrue alloc] : [MalFalse alloc];\n}\n\n@implementation Core\n\n+ (NSDictionary *)ns {\n    return @{\n    @\"=\": ^(NSArray *args){\n        return wrap_tf(equal_Q(args[0], args[1]));\n    },\n    @\"throw\": ^(NSArray *args){\n        @throw args[0];\n    },\n\n    @\"nil?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[NSNull class]]);\n    },\n    @\"true?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[MalTrue class]]);\n    },\n    @\"false?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[MalFalse class]]);\n    },\n    @\"string?\": ^(NSArray *args){\n        return wrap_tf(string_Q(args[0]));\n    },\n    @\"symbol\": ^(NSArray *args){\n        return [MalSymbol stringWithString:args[0]];\n    },\n    @\"symbol?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[MalSymbol class]]);\n    },\n    @\"keyword\": ^(NSArray *args){\n        if (string_Q(args[0])) {\n            return [NSString stringWithFormat:@\"\\u029e%@\", args[0]];\n        } else {\n            return args[0];\n        }\n    },\n    @\"keyword?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[NSString class]] &&\n                       ![args[0] isKindOfClass:[MalSymbol class]] &&\n                       !string_Q(args[0]));\n    },\n    @\"number?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[NSNumber class]]);\n    },\n    @\"fn?\": ^(NSArray *args){\n        return wrap_tf(block_Q(args[0]) ||\n\t\t       ([args[0] isKindOfClass:[MalFunc class]] && ![(MalFunc *)args[0] isMacro]));\n    },\n    @\"macro?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[MalFunc class]] && [(MalFunc *)args[0] isMacro]);\n    },\n\n    @\"pr-str\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        for (id e in args) { [res addObject:_pr_str(e,true)]; }\n        return [res componentsJoinedByString:@\" \"];\n    },\n    @\"str\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        for (id e in args) { [res addObject:_pr_str(e,false)]; }\n        return [res componentsJoinedByString:@\"\"];\n    },\n    @\"prn\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        for (id e in args) { [res addObject:_pr_str(e,true)]; }\n        printf(\"%s\\n\", [[res componentsJoinedByString:@\" \"] UTF8String]);\n        fflush(stdout);\n        return [NSNull alloc];\n    },\n    @\"println\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        for (id e in args) { [res addObject:_pr_str(e,false)]; }\n        printf(\"%s\\n\", [[res componentsJoinedByString:@\" \"] UTF8String]);\n        fflush(stdout);\n        return [NSNull alloc];\n    },\n    @\"read-string\": ^(NSArray *args){\n        return read_str(args[0]);\n    },\n    @\"readline\": ^(NSArray *args){\n        char * rawline = _readline((char *)[(NSString *)args[0] UTF8String]);\n        if (rawline) {\n            return (NSObject *)[NSString stringWithUTF8String:rawline];\n        } else {\n            return (NSObject *)[NSNull alloc];\n        }\n    },\n    @\"slurp\": ^(NSArray *args){\n        return [NSString stringWithContentsOfFile:args[0]\n                         encoding: NSUTF8StringEncoding\n                         error: NULL];\n    },\n\n    @\"<\": ^(NSArray *args){\n        return wrap_tf([args[0] intValue] < [args[1] intValue]);\n    },\n    @\"<=\": ^(NSArray *args){\n        return wrap_tf([args[0] intValue] <= [args[1] intValue]);\n    },\n    @\">\": ^(NSArray *args){\n        return wrap_tf([args[0] intValue] > [args[1] intValue]);\n    },\n    @\">=\": ^(NSArray *args){\n        return wrap_tf([args[0] intValue] >= [args[1] intValue]);\n    },\n    @\"+\": ^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];\n    },\n    @\"-\": ^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];\n    },\n    @\"*\": ^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];\n    },\n    @\"/\": ^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];\n    },\n    @\"time-ms\": ^(NSArray *args){\n        long long ms = [[NSDate date] timeIntervalSince1970] * 1000;\n        return [NSNumber numberWithUnsignedInteger:ms];\n    },\n\n    @\"list\": ^(NSArray *args){\n        return args;\n    },\n    @\"list?\": ^(NSArray *args){\n        return wrap_tf(list_Q(args[0]));\n    },\n    @\"vector\": ^(NSArray *args){\n        return [MalVector fromArray:args];\n    },\n    @\"vector?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[MalVector class]]);\n    },\n    @\"hash-map\": ^(NSArray *args){\n        return hash_map(args);\n    },\n    @\"map?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[NSDictionary class]]);\n    },\n    @\"assoc\": ^(NSArray *args){\n        NSDictionary * dict = args[0];\n        NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]\n                                          initWithDictionary:dict\n                                          copyItems:NO];\n        return assoc_BANG(new_dict, _rest(args));\n    },\n    @\"dissoc\": ^(NSArray *args){\n        NSDictionary * dict = args[0];\n        NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]\n                                          initWithDictionary:dict\n                                          copyItems:NO];\n        for (NSString * key in _rest(args)) {\n            [new_dict removeObjectForKey:key];\n        }\n        return new_dict;\n    },\n    @\"get\": ^(NSArray *args){\n        if ([args[0] isKindOfClass:[NSNull class]]) {\n            return (NSObject *)[NSNull alloc];\n        }\n        NSObject * res = ((NSDictionary *)args[0])[args[1]];\n        return res ? res : [NSNull alloc];\n    },\n    @\"contains?\": ^(NSArray *args){\n        if ([args[0] isKindOfClass:[NSNull class]]) {\n            return wrap_tf(false);\n        }\n        return wrap_tf(((NSDictionary *)args[0])[args[1]] != nil);\n    },\n    @\"keys\": ^(NSArray *args){\n        return [(NSDictionary *)args[0] allKeys];\n    },\n    @\"vals\": ^(NSArray *args){\n        return [(NSDictionary *)args[0] allValues];\n    },\n    \n    @\"sequential?\": ^(NSArray *args){\n        return wrap_tf([args[0] isKindOfClass:[NSArray class]]);\n    },\n    @\"cons\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        [res addObject:args[0]];\n        [res addObjectsFromArray:args[1]];\n        return res;\n    },\n    @\"concat\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        for (NSArray * arr in args) {\n            [res addObjectsFromArray:arr];\n        }\n        return res;\n    },\n    @\"vec\": ^(NSArray *args){\n        return [MalVector fromArray:args[0]];\n    },\n    @\"nth\": ^(NSArray *args){\n        NSArray * lst = (NSArray *)args[0];\n        int idx = [(NSNumber *)args[1] intValue];\n        if (idx < [lst count]) {\n            return lst[idx];\n        } else {\n            @throw @\"nth: index out of range\";\n        }\n    },\n    @\"first\": ^(NSArray *args){\n        if ([args[0] isKindOfClass:[NSNull class]]) {\n            return (NSObject *)[NSNull alloc];\n        }\n        NSArray * lst = (NSArray *)args[0];\n        if ([lst count] > 0) {\n            return (NSObject *)lst[0];\n        } else {\n            return (NSObject *)[NSNull alloc];\n        }\n    },\n    @\"rest\": ^(NSArray *args){\n        if ([args[0] isKindOfClass:[NSNull class]]) {\n            return @[];\n        }\n        NSArray * lst = (NSArray *)args[0];\n        if ([lst count] > 1) {\n            return _rest(lst);\n        } else {\n            return @[];\n        }\n    },\n    @\"empty?\": ^(NSArray *args){\n        if ([args[0] isKindOfClass:[NSNull class]]) {\n            return wrap_tf(true);\n        } else {\n            return wrap_tf([args[0] count] == 0);\n        }\n    },\n    @\"count\": ^(NSArray *args){\n        if ([args[0] isKindOfClass:[NSNull class]]) {\n            return @0;\n        } else {\n            return [NSNumber numberWithInt:[args[0] count]];\n        }\n    },\n    @\"apply\": ^(NSArray *args){\n        NSObject * (^ f)(NSArray *) = args[0];\n        NSMutableArray * fargs = [NSMutableArray array];\n        if ([args count] > 1) {\n            NSRange r = NSMakeRange(1, [args count]-2);\n            [fargs addObjectsFromArray:[args subarrayWithRange:r]];\n        }\n        [fargs addObjectsFromArray:(NSArray *)[args lastObject]];\n        return apply(f, fargs);\n    },\n    @\"map\": ^(NSArray *args){\n        NSObject * (^ f)(NSArray *) = args[0];\n        NSMutableArray * res = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)args[1]) {\n            [res addObject:apply(f, @[x])];\n        }\n        return res;\n    },\n    @\"conj\": ^(NSArray *args){\n        NSMutableArray * res = [NSMutableArray array];\n        if ([args[0] isKindOfClass:[MalVector class]]) {\n            [res addObjectsFromArray:args[0]];\n            [res addObjectsFromArray:_rest(args)];\n            return (NSObject *)[MalVector fromArray:res];\n        } else {\n            [res addObjectsFromArray:[[_rest(args) reverseObjectEnumerator]\n                                      allObjects]];\n            [res addObjectsFromArray:args[0]];\n            return (NSObject *)res;\n        }\n    },\n    @\"seq\": ^(NSArray *args){\n        if (list_Q(args[0])) {\n            if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }\n            return (NSObject *)args[0];\n        } else if ([args[0] isKindOfClass:[MalVector class]]) {\n            if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }\n            return (NSObject *)[NSArray arrayWithArray:args[0]];\n        } else if (string_Q(args[0])) {\n            NSString * str = args[0];\n            if ([str length] == 0) { return (NSObject *)[NSNull alloc]; }\n            NSMutableArray * res = [NSMutableArray array];\n            for (int i=0; i < [str length]; i++) {\n                char c = [str characterAtIndex:i];\n                [res addObject:[NSString stringWithFormat:@\"%c\", c]];\n            }\n            return (NSObject *)res;\n        } else if ([args[0] isKindOfClass:[NSNull class]]) {\n            return (NSObject *)args[0];\n        } else {\n            @throw @\"seq: called on non-sequence\";\n        }\n    },\n\n    @\"meta\": ^id (NSArray *args){\n        if ([args[0] isKindOfClass:[MalFunc class]]) {\n            return [(MalFunc *)args[0] meta];\n        } else {\n            id res = objc_getAssociatedObject(args[0], @\"meta\");\n            return res ? res : (NSObject *)[NSNull alloc];\n        }\n    },\n    @\"with-meta\": ^id (NSArray *args){\n        if ([args[0] isKindOfClass:[MalFunc class]]) {\n            MalFunc * cmf = [(MalFunc *)args[0] copy];\n            cmf.meta = args[1];\n            return cmf;\n        } else if (!block_Q(args[0])) {\n            id res = [args[0] copy];\n            objc_setAssociatedObject(res, @\"meta\", args[1], OBJC_ASSOCIATION_RETAIN_NONATOMIC);\n            return res;\n        } else {\n            id (^blk)(NSArray *args) = args[0];\n            id (^wrapBlock)(NSArray *args) = ^id (NSArray *args) { return blk(args); };\n            id (^res)(NSArray *args) = [wrapBlock copy]; // under mrc: copy to get a malloc block instead of a stack block.\n            objc_setAssociatedObject(res, @\"meta\", args[1], OBJC_ASSOCIATION_RETAIN_NONATOMIC);\n            return res;\n        }\n    },\n    @\"atom\": ^(NSArray *args){\n        return [MalAtom fromObject:args[0]];\n    },\n    @\"atom?\": ^(NSArray *args){\n        return wrap_tf(atom_Q(args[0]));\n    },\n    @\"deref\": ^(NSArray *args){\n        return [(MalAtom *)args[0] val];\n    },\n    @\"reset!\": ^(NSArray *args){\n        MalAtom * atm = (MalAtom *)args[0];\n        return atm.val = args[1];\n    },\n    @\"swap!\": ^(NSArray *args){\n        MalAtom * atm = (MalAtom *)args[0];\n        NSObject * (^ f)(NSArray *) = args[1];\n        NSMutableArray * fargs = [NSMutableArray array];\n        [fargs addObject:atm.val];\n        if ([args count] > 2) {\n            NSRange r = NSMakeRange(2, [args count]-2);\n            [fargs addObjectsFromArray:[args subarrayWithRange:r]];\n        }\n        return atm.val = apply(f, fargs);\n    },\n    };\n}\n\n@end\n"
  },
  {
    "path": "impls/objc/env.h",
    "content": "#import <Foundation/Foundation.h>\n\n// See types.h for Env interface definition\n"
  },
  {
    "path": "impls/objc/env.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"types.h\"\n//#import \"env.h\"\n\n@implementation Env\n\n@synthesize data = _data;\n@synthesize outer = _outer;\n\n- (id)initWithBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs {\n    self = [super init];\n    if (self) {\n        _outer = outer;\n        _data = [NSMutableDictionary dictionary];\n\n        for (int i=0; i < [binds count]; i++) {\n            if ([(NSString *)binds[i] isEqualTo:@\"&\"]) {\n                if ([exprs count] > i) {\n                    NSRange r = NSMakeRange(i, [exprs count] - i);\n                    _data[binds[i+1]] = [exprs subarrayWithRange:r];\n                } else {\n                    _data[binds[i+1]] = @[];\n                }\n                break;\n            } else {\n                _data[binds[i]] = exprs[i];\n            }\n        }\n    }\n    return self;\n}\n\n- (id)initWithOuter:(Env *)outer {\n    return [self initWithBindings:outer binds:@[] exprs:@[]];\n}\n\n- (id)init {\n    return [self initWithBindings:nil binds:@[] exprs:@[]];\n}\n\n+ (id)fromOuter:(Env *)outer {\n    return [[Env alloc] initWithOuter:outer];\n}\n\n+ (id)fromBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs {\n    return [[Env alloc] initWithBindings:outer binds:binds exprs:exprs];\n}\n\n- (NSObject *) set:(MalSymbol *)key val:(NSObject *)val {\n    _data[key] = val;\n    return val;\n}\n\n- (NSObject *) get:(MalSymbol *)key {\n    NSObject * value;\n    Env * e = self;\n    while (true) {\n        value = e.data[key];\n        if (value != nil) return value;\n        e = e.outer;\n        if (e == nil) return nil;\n    }\n}\n\n@end\n"
  },
  {
    "path": "impls/objc/mal_readline.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#if USE_READLINE\n  #include <readline/readline.h>\n  #include <readline/history.h>\n  #include <readline/tilde.h>\n#else\n  #include <editline/readline.h>\n#endif\n\nint history_loaded = 0;\n\nchar HISTORY_FILE[] = \"~/.mal-history\";\n\nvoid load_history() {\n    if (history_loaded) { return; }\n    int ret;\n    char *hf = tilde_expand(HISTORY_FILE);\n    if (access(hf, F_OK) != -1) {\n        // TODO: check if file exists first, use non-static path\n#if USE_READLINE\n        ret = read_history(hf);\n#else\n        FILE *fp = fopen(hf, \"r\");\n        char *line = malloc(80); // getline reallocs as necessary\n        size_t sz = 80;\n        while ((ret = getline(&line, &sz, fp)) > 0) {\n            add_history(line); // Add line to in-memory history\n        }\n        free(line);\n        fclose(fp);\n#endif\n        history_loaded = 1;\n    }\n    free(hf);\n}\n\nvoid append_to_history() {\n    char *hf = tilde_expand(HISTORY_FILE);\n#ifdef USE_READLINE\n    append_history(1, hf);\n#else\n#if defined(RL_READLINE_VERSION)\n    HIST_ENTRY *he = history_get(history_base+history_length-1);\n#else\n    // libedit-2 segfaults if we add history_base\n    HIST_ENTRY *he = history_get(history_length-1);\n#endif\n    FILE *fp = fopen(hf, \"a\");\n    if (fp) {\n        fprintf(fp, \"%s\\n\", he->line);\n        fclose(fp);\n    }\n#endif\n    free(hf);\n}\n\n\n// line must be freed by caller\nchar *_readline (char prompt[]) {\n    char *line;\n\n    load_history();\n\n    line = readline(prompt);\n    if (!line) return NULL; // EOF\n    add_history(line); // Add input to in-memory history\n\n    append_to_history(); // Flush new line of history to disk\n\n    return line;\n}\n\n"
  },
  {
    "path": "impls/objc/mal_readline.h",
    "content": "#ifndef __MAL_READLINE__\n#define __MAL_READLINE__\n\nchar *_readline (char prompt[]);\n\n#endif\n"
  },
  {
    "path": "impls/objc/malfunc.h",
    "content": "#import <Foundation/Foundation.h>\n\n/*\n// Forward declaration of Env (see env.h for full interface)\n@class Env;\n*/\n// Forward declaration of EVAL function\nNSObject *EVAL(id ast, id env);\n \n@interface MalFunc : NSObject <NSCopying>\n\n@property (copy) NSArray * ast;\n@property (copy) Env * env;\n@property (copy) NSArray * params;\n@property BOOL isMacro;\n@property (copy) NSObject * meta;\n\n- (id)init:(NSArray *)ast env:(Env *)env params:(NSArray *)params;\n\n- (id)apply:(NSArray *)args;\n\n@end\n\nNSObject * apply(id f, NSArray *args);\n"
  },
  {
    "path": "impls/objc/malfunc.m",
    "content": "#import \"types.h\"\n\n#import \"malfunc.h\"\n\n@implementation MalFunc\n\n@synthesize ast = _ast;\n@synthesize env = _env;\n@synthesize params = _params;\n@synthesize isMacro = _isMacro;\n@synthesize meta = _meta;\n\n- (id)init:(NSArray *)ast env:(Env *)env params:(NSArray *)params {\n    self = [super init];\n    if (self) {\n        _ast = ast;\n        _env = env;\n        _params = params;\n        _isMacro = false;\n        _meta = [NSNull alloc];\n    }\n    return self;\n}\n\n- (id)apply:(NSArray *)args {\n    return EVAL(_ast, [Env fromBindings:_env binds:_params exprs:args]);\n}\n\n- (id)copyWithZone:(NSZone *)zone\n{\n    MalFunc * copy = [[[self class] alloc] init:_ast env:_env params:_params];\n    if (copy) {\n        copy.isMacro = _isMacro;\n        copy.meta = _meta;\n    }\n    return copy;\n}\n\n@end\n\n\nNSObject * apply(id f, NSArray *args) {\n    if ([f isKindOfClass:[MalFunc class]]) {\n        return [f apply:args];\n    } else {\n        NSObject * (^ fn)(NSArray *) = f;\n        return fn(args);\n    }\n}\n"
  },
  {
    "path": "impls/objc/printer.h",
    "content": "#import <Foundation/Foundation.h>\n\nNSString * _pr_str(NSObject * obj, BOOL print_readably);\n"
  },
  {
    "path": "impls/objc/printer.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"types.h\"\n\nNSString * _pr_str(NSObject * obj, BOOL print_readably) {\n    //NSLog(@\"class: %@\", [obj class]);\n    if ([obj isMemberOfClass:[NSNull class]]) {\n        return @\"nil\";\n    } else if ([obj isMemberOfClass:[MalTrue class]]) {\n        return @\"true\";\n    } else if ([obj isMemberOfClass:[MalFalse class]]) {\n        return @\"false\";\n    } else if ([obj isKindOfClass:[MalSymbol class]]) {\n        return (NSString *) obj;\n    } else if ([obj isKindOfClass:[NSString class]]) {\n        NSString * str = (NSString *)obj;\n        if ([str length] > 0 && ([str hasPrefix:@\"\\u029e\"])) {\n            return [NSString stringWithFormat:@\":%@\",\n                      [str substringWithRange:NSMakeRange(1, [str length]-1)]];\n        } else if (print_readably) {\n            str = [[[(NSString *)obj\n                     stringByReplacingOccurrencesOfString:@\"\\\\\" withString:@\"\\\\\\\\\"]\n                    stringByReplacingOccurrencesOfString:@\"\\\"\" withString:@\"\\\\\\\"\"]\n                   stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\"\\\\n\"];\n            return [NSString stringWithFormat:@\"\\\"%@\\\"\", str];\n        } else {\n            return [NSString stringWithString:str];\n        }\n    } else if ([obj isKindOfClass:[NSArray class]]) {\n        NSMutableArray * elems = [NSMutableArray array];\n        for (NSObject * elem in (NSArray *)obj) {\n            [elems addObject:_pr_str(elem, print_readably)];\n        }\n        if ([obj isKindOfClass:[MalVector class]]) {\n            return [NSString stringWithFormat:@\"[%@]\",\n                    [elems componentsJoinedByString:@\" \"]];\n        } else {\n            return [NSString stringWithFormat:@\"(%@)\",\n                    [elems componentsJoinedByString:@\" \"]];\n        }\n    } else if ([obj isKindOfClass:[NSDictionary class]]) {\n        NSDictionary * dict = (NSDictionary *)obj;\n        NSMutableArray * elems = [NSMutableArray array];\n        for (NSString * key in dict) {\n            [elems addObject:_pr_str(key, print_readably)];\n            [elems addObject:_pr_str(dict[key], print_readably)];\n        }\n        return [NSString stringWithFormat:@\"{%@}\",\n                [elems componentsJoinedByString:@\" \"]];\n    } else if (block_Q(obj)) {\n        return @\"#<native function>\";\n    } else if (atom_Q(obj)) {\n        return [NSString stringWithFormat:@\"(atom %@)\",\n                _pr_str([(MalAtom *)obj val], print_readably)];\n    } else {\n        return [obj description];\n    }\n}\n"
  },
  {
    "path": "impls/objc/reader.h",
    "content": "NSArray * tokenize(NSString *str);\nNSObject * read_str(NSString *str);\n"
  },
  {
    "path": "impls/objc/reader.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"types.h\"\n\n// Only used here, so define interface locally\n@interface Reader : NSObject\n\n- (id)initWithTokens:(NSArray *)toks;\n- (id)init;\n\n- (NSString *) next;\n- (NSString *) peek;\n\n@end\n\n\n@implementation Reader\n\nNSArray  *_tokens;\nint _position;\n\n- (id)initWithTokens:(NSArray *)toks {\n    self = [super init];\n    if (self) {\n        _tokens = toks;\n        _position = 0;\n    }\n    return self;\n}\n\n- (id)init {\n    return [self initWithTokens:@[]];\n}\n\n- (NSString *)next {\n    _position++;\n    return _tokens[_position-1];\n}\n\n- (NSString *)peek {\n    if ([_tokens count] > _position) {\n        return _tokens[_position];\n    } else {\n        return nil;\n    }\n}\n\n@end\n\n\nNSArray * tokenize(NSString *str) {\n    NSRegularExpression *regex = [NSRegularExpression\n        regularExpressionWithPattern:@\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}()'\\\"`@,;]+)\"\n        options:0\n        error:NULL];\n\n    NSArray *matches = [regex\n        matchesInString:str\n        options:0\n        range:NSMakeRange(0, [str length])];\n\n    NSMutableArray * tokens = [NSMutableArray array];\n    for (NSTextCheckingResult *match in matches) {\n        NSString * mstr = [str substringWithRange:[match rangeAtIndex:1]];\n        if ([mstr characterAtIndex:0] == ';') { continue; }\n        [tokens addObject:mstr];\n    }\n    return tokens;\n}\n\nNSObject * read_atom(Reader * rdr) {\n    NSRegularExpression *regex = [NSRegularExpression\n        regularExpressionWithPattern:@\"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\\\"((?:[\\\\\\\\].|[^\\\\\\\\\\\"])*)\\\"$|^\\\"(.*)$|:(.*)|(^[^\\\"]*$)\"\n        options:0\n        error:NULL];\n    NSNumberFormatter *numf = [[NSNumberFormatter alloc] init];\n    numf.numberStyle = NSNumberFormatterDecimalStyle;\n\n    NSString *token = [rdr next];\n\n    NSArray *matches = [regex\n        matchesInString:token\n        options:0\n        range:NSMakeRange(0, [token length])];\n\n    if ([matches count] > 0) {\n\n        NSTextCheckingResult *match = matches[0];\n        if ([match rangeAtIndex:1].location < -1ULL/2) { // integer\n            return [numf numberFromString:token];\n        } else if ([match rangeAtIndex:2].location < -1ULL/2) { // float\n            return [numf numberFromString:token];\n        } else if ([match rangeAtIndex:3].location < -1ULL/2) { // nil\n            return [NSNull alloc];\n        } else if ([match rangeAtIndex:4].location < -1ULL/2) { // true\n            return [MalTrue alloc]; // TODO: intern\n        } else if ([match rangeAtIndex:5].location < -1ULL/2) { // false\n            return [MalFalse alloc]; // TODO: intern\n        } else if ([match rangeAtIndex:6].location < -1ULL/2) { // string\n            NSString * str = [token substringWithRange:[match rangeAtIndex:6]];\n            return [[[[str\n                        stringByReplacingOccurrencesOfString:@\"\\\\\\\\\" withString:@\"\\u029e\"]\n                      stringByReplacingOccurrencesOfString:@\"\\\\\\\"\" withString:@\"\\\"\"]\n                     stringByReplacingOccurrencesOfString:@\"\\\\n\" withString:@\"\\n\"]\n                    stringByReplacingOccurrencesOfString:@\"\\u029e\" withString:@\"\\\\\"];\n        } else if ([match rangeAtIndex:7].location < -1ULL/2) { // string\n            @throw @\"read_atom: expected '\\\"', got EOF\";\n        } else if ([match rangeAtIndex:8].location < -1ULL/2) { // keyword\n            return [NSString stringWithFormat:@\"\\u029e%@\",\n                    [token substringWithRange:[match rangeAtIndex:8]]];\n        } else if ([match rangeAtIndex:9].location < -1ULL/2) { // symbol\n            return [MalSymbol stringWithString:token];\n        }\n    }\n\n    @throw @\"read_atom: invalid token\";\n}\n\n// Only used locally, so declare here\nNSObject * read_form(Reader * rdr);\n\nNSArray * read_list(Reader * rdr, char start, char end) {\n    NSString * token = [rdr next];\n    NSMutableArray * ast = [NSMutableArray array];\n\n    if ([token characterAtIndex:0] != start) {\n        @throw [NSString stringWithFormat:@\"expected '%c'\", start];\n    }\n    while ((token = [rdr peek]) && ([token characterAtIndex:0] != end)) {\n        [ast addObject:read_form(rdr)];\n    }\n    if (!token) {\n        @throw [NSString stringWithFormat:@\"expected '%c', got EOF\", end];\n    }\n    [rdr next];\n    return ast;\n}\n\nNSObject * read_form(Reader * rdr) {\n    NSString *token = [rdr peek];\n    switch ([token characterAtIndex:0]) {\n    case '\\'': [rdr next];\n              return @[[MalSymbol stringWithString:@\"quote\"],\n                       read_form(rdr)];\n    case '`': [rdr next];\n              return @[[MalSymbol stringWithString:@\"quasiquote\"],\n                       read_form(rdr)];\n    case '~': [rdr next];\n              if ([token isEqualToString:@\"~@\"]) {\n                  return @[[MalSymbol stringWithString:@\"splice-unquote\"],\n                           read_form(rdr)];\n              } else {\n                  return @[[MalSymbol stringWithString:@\"unquote\"],\n                           read_form(rdr)];\n              }\n    case '^': [rdr next];\n              NSObject * meta = read_form(rdr);\n              return @[[MalSymbol stringWithString:@\"with-meta\"],\n                       read_form(rdr),\n                       meta];\n    case '@': [rdr next];\n              return @[[MalSymbol stringWithString:@\"deref\"],\n                       read_form(rdr)];\n\n    // lists\n    case ')':\n        @throw @\"unexpected ')'\";\n    case '(':\n        return read_list(rdr, '(', ')');\n\n    // vectors\n    case ']':\n        @throw @\"unexpected ']'\";\n    case '[':\n        return [MalVector fromArray:read_list(rdr, '[', ']')];\n\n    // hash maps\n    case '}':\n        @throw @\"unexpected '}'\";\n    case '{':\n        return hash_map(read_list(rdr, '{', '}'));\n    default:\n        return read_atom(rdr);\n    }\n}\n\nNSObject * read_str(NSString *str) {\n    NSArray * tokens = tokenize(str);\n    if ([tokens count] == 0) { @throw [NSException exceptionWithName:@\"ReaderContinue\"\n                                       reason:@\"empty token\"\n                                       userInfo:nil]; }\n    //if ([tokens count] == 0) { @throw [[MalContinue alloc] init]; }\n    return read_form([[Reader alloc] initWithTokens:tokens]);\n}\n"
  },
  {
    "path": "impls/objc/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/objc/step0_repl.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n\nNSString *READ(NSString *str) {\n    return str;\n}\n\nNSString *EVAL(NSString *ast, NSString *env) {\n    return ast;\n}\n\nNSString *PRINT(NSString *exp) {\n    return exp;\n}\n\nNSString *REP(NSString *line) {\n    return PRINT(EVAL(READ(line), @\"\"));\n}\n\nint main () {\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        printf(\"%s\\n\", [[REP(line) description] UTF8String]);\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step1_read_print.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\nNSObject *EVAL(NSObject *ast, NSString *env) {\n    return ast;\n}\n\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\nNSString *REP(NSString *line) {\n    return PRINT(EVAL(READ(line), @\"\"));\n}\n\nint main () {\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step2_eval.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nNSObject *EVAL(NSObject *ast, NSDictionary *env) {\n    // NSLog(@\"EVAL: %@ (%@)\", _pr_str(ast, true), env);\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        if ([env objectForKey:ast]) {\n            return env[ast];\n        } else {\n            @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id el0 = EVAL(alst[0], env);\n    NSObject * (^ f)(NSArray *) = el0;\n    NSMutableArray * args = [NSMutableArray array];\n    for (int i = 1; i < [alst count]; i++) {\n        [args addObject:EVAL(alst[i], env)];\n    }\n    return f(args);\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, NSDictionary *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    NSDictionary * repl_env = @{\n        @\"+\": ^(NSArray *args){\n            return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];\n        },\n        @\"-\": ^(NSArray *args){\n            return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];\n        },\n        @\"*\": ^(NSArray *args){\n            return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];\n        },\n        @\"/\": ^(NSArray *args){\n            return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];\n        },\n        };\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step3_env.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nNSObject *EVAL(NSObject *ast, Env *env) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    if (![a0 isKindOfClass:[MalSymbol class]]) {\n        @throw @\"attempt to apply on non-symbol\";\n    }\n    if ([(NSString *)a0 isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        return EVAL(alst[2], let_env);\n    } else {\n        id el0 = EVAL(a0, env);\n        NSObject * (^ f)(NSArray *) = el0;\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        return f(args);\n    }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    Env * repl_env = [[Env alloc] init];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    [repl_env set:(MalSymbol *)@\"+\" val:^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];\n    }];\n    [repl_env set:(MalSymbol *)@\"-\" val:^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];\n    }];\n    [repl_env set:(MalSymbol *)@\"*\" val:^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];\n    }];\n    [repl_env set:(MalSymbol *)@\"/\" val:^(NSArray *args){\n        return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];\n    }];\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step4_if_fn_do.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nNSObject *EVAL(NSObject *ast, Env *env) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        return EVAL(alst[2], let_env);\n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        return EVAL([alst lastObject], env);\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                return EVAL(alst[3], env);\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            return EVAL(alst[2], env);\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        return apply(el0, args);\n    }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    Env * repl_env = [[Env alloc] init];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step5_tco.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nNSObject *EVAL(NSObject *ast, Env *env) {\n  while (true) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        env = let_env;\n        ast = alst[2]; // TCO\n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        ast = [alst lastObject]; // TCO\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                ast = alst[3]; // TCO\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            ast = alst[2]; // TCO\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];\n            ast = [mf ast]; // TCO\n        } else {\n            NSObject * (^ f)(NSArray *) = el0;\n            return f(args);\n        }\n    }\n  }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    Env * repl_env = [[Env alloc] init];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step6_file.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nNSObject *EVAL(NSObject *ast, Env *env) {\n  while (true) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        env = let_env;\n        ast = alst[2]; // TCO\n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        ast = [alst lastObject]; // TCO\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                ast = alst[3]; // TCO\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            ast = alst[2]; // TCO\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];\n            ast = [mf ast]; // TCO\n        } else {\n            NSObject * (^ f)(NSArray *) = el0;\n            return f(args);\n        }\n    }\n  }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    // Outside of pool to prevent \"Block_release called upon\n    // a stack...\" message on exit\n    Env * repl_env = [[Env alloc] init];\n    NSArray *args = [[NSProcessInfo processInfo] arguments];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n    [repl_env set:(MalSymbol *)@\"eval\" val:^(NSArray *args) {\n        return EVAL(args[0], repl_env);\n    }];\n    NSArray *argv = @[];\n    if ([args count] > 2) {\n        argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];\n    }\n    [repl_env set:(MalSymbol *)@\"*ARGV*\" val:argv];\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    REP(@\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n\n    if ([args count] > 1) {\n        @try {\n            REP([NSString stringWithFormat:@\"(load-file \\\"%@\\\")\", args[1]], repl_env);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        }\n        return 0;\n    }\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step7_quote.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nBOOL starts_with(NSObject *ast, NSString *sym) {\n    if (!list_Q(ast))\n        return 0;\n    NSArray *alst = (NSArray *)ast;\n    if (![alst count])\n        return 0;\n    NSObject *a0 = alst[0];\n    return [a0 isKindOfClass:[MalSymbol class]] &&\n           [(NSString *)a0 isEqualTo:sym];\n}\n\nNSObject * quasiquote(NSObject *ast) {\n    if ([ast isMemberOfClass:[MalSymbol class]] ||\n        [ast isKindOfClass:[NSDictionary class]])\n        return @[[MalSymbol stringWithString:@\"quote\"], ast];\n\n    if (![ast isKindOfClass:[NSArray class]])\n         return ast;\n\n    NSArray * alst = (NSArray *)ast;\n    if (starts_with(alst, @\"unquote\"))\n        return alst[1];\n\n    NSObject *res = @[];\n    for (int i= [alst count] - 1; 0<=i; i--) {\n        NSObject *elt = alst[i];\n        if (starts_with(elt, @\"splice-unquote\"))\n            res = @[[MalSymbol stringWithString:@\"concat\"], ((NSArray *)elt)[1], res];\n        else\n            res = @[[MalSymbol stringWithString:@\"cons\"], quasiquote(elt), res];\n    }\n    if ([ast isKindOfClass:[MalVector class]])\n        res = @[[MalSymbol stringWithString:@\"vec\"], res];\n    return res;\n}\n\nNSObject *EVAL(NSObject *ast, Env *env) {\n  while (true) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        env = let_env;\n        ast = alst[2]; // TCO\n    } else if ([(NSString *)a0 isEqualTo:@\"quote\"]) {\n        return alst[1];\n    } else if ([(NSString *)a0 isEqualTo:@\"quasiquote\"]) {\n        ast = quasiquote(alst[1]); // TCO\n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        ast = [alst lastObject]; // TCO\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                ast = alst[3]; // TCO\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            ast = alst[2]; // TCO\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];\n            ast = [mf ast]; // TCO\n        } else {\n            NSObject * (^ f)(NSArray *) = el0;\n            return f(args);\n        }\n    }\n  }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    // Outside of pool to prevent \"Block_release called upon\n    // a stack...\" message on exit\n    Env * repl_env = [[Env alloc] init];\n    NSArray *args = [[NSProcessInfo processInfo] arguments];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n    [repl_env set:(MalSymbol *)@\"eval\" val:^(NSArray *args) {\n        return EVAL(args[0], repl_env);\n    }];\n    NSArray *argv = @[];\n    if ([args count] > 2) {\n        argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];\n    }\n    [repl_env set:(MalSymbol *)@\"*ARGV*\" val:argv];\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    REP(@\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n\n    if ([args count] > 1) {\n        @try {\n            REP([NSString stringWithFormat:@\"(load-file \\\"%@\\\")\", args[1]], repl_env);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        }\n        return 0;\n    }\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step8_macros.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nBOOL starts_with(NSObject *ast, NSString *sym) {\n    if (!list_Q(ast))\n        return 0;\n    NSArray *alst = (NSArray *)ast;\n    if (![alst count])\n        return 0;\n    NSObject *a0 = alst[0];\n    return [a0 isKindOfClass:[MalSymbol class]] &&\n           [(NSString *)a0 isEqualTo:sym];\n}\n\nNSObject * quasiquote(NSObject *ast) {\n    if ([ast isMemberOfClass:[MalSymbol class]] ||\n        [ast isKindOfClass:[NSDictionary class]])\n        return @[[MalSymbol stringWithString:@\"quote\"], ast];\n\n    if (![ast isKindOfClass:[NSArray class]])\n         return ast;\n\n    NSArray * alst = (NSArray *)ast;\n    if (starts_with(alst, @\"unquote\"))\n        return alst[1];\n\n    NSObject *res = @[];\n    for (int i= [alst count] - 1; 0<=i; i--) {\n        NSObject *elt = alst[i];\n        if (starts_with(elt, @\"splice-unquote\"))\n            res = @[[MalSymbol stringWithString:@\"concat\"], ((NSArray *)elt)[1], res];\n        else\n            res = @[[MalSymbol stringWithString:@\"cons\"], quasiquote(elt), res];\n    }\n    if ([ast isKindOfClass:[MalVector class]])\n        res = @[[MalSymbol stringWithString:@\"vec\"], res];\n    return res;\n}\n\nNSObject *EVAL(NSObject *ast, Env *env) {\n  while (true) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        env = let_env;\n        ast = alst[2]; // TCO\n    } else if ([(NSString *)a0 isEqualTo:@\"quote\"]) {\n        return alst[1];\n    } else if ([(NSString *)a0 isEqualTo:@\"quasiquote\"]) {\n        ast = quasiquote(alst[1]); // TCO\n    } else if ([a0sym isEqualTo:@\"defmacro!\"]) {\n        MalFunc * f = [(MalFunc *)EVAL(alst[2], env) copy];\n        f.isMacro = true;\n        return [env set:alst[1] val:f];\n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        ast = [alst lastObject]; // TCO\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                ast = alst[3]; // TCO\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            ast = alst[2]; // TCO\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            if ([mf isMacro]) {\n                NSMutableArray * args = [NSMutableArray array];\n                for (int i = 1; i < [alst count]; i++) {\n                    [args addObject:alst[i]];\n                }\n                ast = [mf apply:args];\n                continue; // TCO\n            }\n        }\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];\n            ast = [mf ast]; // TCO\n        } else {\n            NSObject * (^ f)(NSArray *) = el0;\n            return f(args);\n        }\n    }\n  }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    // Outside of pool to prevent \"Block_release called upon\n    // a stack...\" message on exit\n    Env * repl_env = [[Env alloc] init];\n    NSArray *args = [[NSProcessInfo processInfo] arguments];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n    [repl_env set:(MalSymbol *)@\"eval\" val:^(NSArray *args) {\n        return EVAL(args[0], repl_env);\n    }];\n    NSArray *argv = @[];\n    if ([args count] > 2) {\n        argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];\n    }\n    [repl_env set:(MalSymbol *)@\"*ARGV*\" val:argv];\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    REP(@\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n    REP(@\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n\n    if ([args count] > 1) {\n        @try {\n            REP([NSString stringWithFormat:@\"(load-file \\\"%@\\\")\", args[1]], repl_env);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        }\n        return 0;\n    }\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/step9_try.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nBOOL starts_with(NSObject *ast, NSString *sym) {\n    if (!list_Q(ast))\n        return 0;\n    NSArray *alst = (NSArray *)ast;\n    if (![alst count])\n        return 0;\n    NSObject *a0 = alst[0];\n    return [a0 isKindOfClass:[MalSymbol class]] &&\n           [(NSString *)a0 isEqualTo:sym];\n}\n\nNSObject * quasiquote(NSObject *ast) {\n    if ([ast isMemberOfClass:[MalSymbol class]] ||\n        [ast isKindOfClass:[NSDictionary class]])\n        return @[[MalSymbol stringWithString:@\"quote\"], ast];\n\n    if (![ast isKindOfClass:[NSArray class]])\n         return ast;\n\n    NSArray * alst = (NSArray *)ast;\n    if (starts_with(alst, @\"unquote\"))\n        return alst[1];\n\n    NSObject *res = @[];\n    for (int i= [alst count] - 1; 0<=i; i--) {\n        NSObject *elt = alst[i];\n        if (starts_with(elt, @\"splice-unquote\"))\n            res = @[[MalSymbol stringWithString:@\"concat\"], ((NSArray *)elt)[1], res];\n        else\n            res = @[[MalSymbol stringWithString:@\"cons\"], quasiquote(elt), res];\n    }\n    if ([ast isKindOfClass:[MalVector class]])\n        res = @[[MalSymbol stringWithString:@\"vec\"], res];\n    return res;\n}\n\nNSObject *EVAL(NSObject *ast, Env *env) {\n  while (true) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        env = let_env;\n        ast = alst[2]; // TCO\n    } else if ([(NSString *)a0 isEqualTo:@\"quote\"]) {\n        return alst[1];\n    } else if ([(NSString *)a0 isEqualTo:@\"quasiquote\"]) {\n        ast = quasiquote(alst[1]); // TCO\n    } else if ([a0sym isEqualTo:@\"defmacro!\"]) {\n        MalFunc * f = [(MalFunc *)EVAL(alst[2], env) copy];\n        f.isMacro = true;\n        return [env set:alst[1] val:f];\n    } else if ([a0sym isEqualTo:@\"try*\"]) {\n        @try {\n            return EVAL(alst[1], env);\n        } @catch(NSObject *e) {\n            if ([alst count] > 2 && [alst[2] isKindOfClass:[NSArray class]]) {\n                NSArray * a2lst = alst[2];\n                if ([a2lst[0] isKindOfClass:[MalSymbol class]] &&\n                    [(MalSymbol *)a2lst[0] isEqualTo:@\"catch*\"]) {\n                    NSObject * exc = e;\n                    if ([e isKindOfClass:[NSException class]]) {\n                        exc = [e description];\n                    }\n                    return EVAL(a2lst[2], [Env fromBindings:env\n                                               binds:@[a2lst[1]]\n                                               exprs:@[exc]]);\n                }\n            }\n            @throw e;\n        } \n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        ast = [alst lastObject]; // TCO\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                ast = alst[3]; // TCO\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            ast = alst[2]; // TCO\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            if ([mf isMacro]) {\n                NSMutableArray * args = [NSMutableArray array];\n                for (int i = 1; i < [alst count]; i++) {\n                    [args addObject:alst[i]];\n                }\n                ast = [mf apply:args];\n                continue; // TCO\n            }\n        }\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];\n            ast = [mf ast]; // TCO\n        } else {\n            NSObject * (^ f)(NSArray *) = el0;\n            return f(args);\n        }\n    }\n  }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    // Outside of pool to prevent \"Block_release called upon\n    // a stack...\" message on exit\n    Env * repl_env = [[Env alloc] init];\n    NSArray *args = [[NSProcessInfo processInfo] arguments];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n    [repl_env set:(MalSymbol *)@\"eval\" val:^(NSArray *args) {\n        return EVAL(args[0], repl_env);\n    }];\n    NSArray *argv = @[];\n    if ([args count] > 2) {\n        argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];\n    }\n    [repl_env set:(MalSymbol *)@\"*ARGV*\" val:argv];\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    REP(@\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n    REP(@\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n\n    if ([args count] > 1) {\n        @try {\n            REP([NSString stringWithFormat:@\"(load-file \\\"%@\\\")\", args[1]], repl_env);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        }\n        return 0;\n    }\n\n    while (true) {\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/stepA_mal.m",
    "content": "#import <Foundation/Foundation.h>\n\n#import \"mal_readline.h\"\n#import \"types.h\"\n#import \"reader.h\"\n#import \"printer.h\"\n#import \"env.h\"\n#import \"malfunc.h\"\n#import \"core.h\"\n\n// read\nNSObject *READ(NSString *str) {\n    return read_str(str);\n}\n\n// eval\nBOOL starts_with(NSObject *ast, NSString *sym) {\n    if (!list_Q(ast))\n        return 0;\n    NSArray *alst = (NSArray *)ast;\n    if (![alst count])\n        return 0;\n    NSObject *a0 = alst[0];\n    return [a0 isKindOfClass:[MalSymbol class]] &&\n           [(NSString *)a0 isEqualTo:sym];\n}\n\nNSObject * quasiquote(NSObject *ast) {\n    if ([ast isMemberOfClass:[MalSymbol class]] ||\n        [ast isKindOfClass:[NSDictionary class]])\n        return @[[MalSymbol stringWithString:@\"quote\"], ast];\n\n    if (![ast isKindOfClass:[NSArray class]])\n         return ast;\n\n    NSArray * alst = (NSArray *)ast;\n    if (starts_with(alst, @\"unquote\"))\n        return alst[1];\n\n    NSObject *res = @[];\n    for (int i= [alst count] - 1; 0<=i; i--) {\n        NSObject *elt = alst[i];\n        if (starts_with(elt, @\"splice-unquote\"))\n            res = @[[MalSymbol stringWithString:@\"concat\"], ((NSArray *)elt)[1], res];\n        else\n            res = @[[MalSymbol stringWithString:@\"cons\"], quasiquote(elt), res];\n    }\n    if ([ast isKindOfClass:[MalVector class]])\n        res = @[[MalSymbol stringWithString:@\"vec\"], res];\n    return res;\n}\n\nNSObject *EVAL(NSObject *ast, Env *env) {\n  while (true) {\n    NSObject * dbgeval = [env get:[MalSymbol stringWithString:@\"DEBUG-EVAL\"]];\n    if (dbgeval != nil\n        && ! [dbgeval isKindOfClass:[NSNull class]]\n        && ! [dbgeval isKindOfClass:[MalFalse class]]) {\n      printf(\"EVAL: %s\\n\", [[_pr_str(ast, true) description] UTF8String]);\n    }\n    if ([ast isMemberOfClass:[MalSymbol class]]) {\n        NSObject * value = [env get:(MalSymbol *)ast];\n        if (value == nil) {\n          @throw [NSString stringWithFormat:@\"'%@' not found\", ast];\n        }\n        return value;\n    } else if ([ast isKindOfClass:[MalVector class]]) {\n        NSMutableArray *newLst = [NSMutableArray array];\n        for (NSObject * x in (NSArray *)ast) {\n            [newLst addObject:EVAL(x, env)];\n        }\n        return [MalVector fromArray:newLst];\n    } else if ([ast isKindOfClass:[NSDictionary class]]) {\n        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];\n        for (NSString * k in (NSDictionary *)ast) {\n            newDict[k] = EVAL(((NSDictionary *)ast)[k], env);\n        }\n        return newDict;\n    } else if (! [ast isKindOfClass:[NSArray class]]) {\n        return ast;\n    }\n\n    // apply list\n    NSArray * alst = (NSArray *)ast;\n    if ([alst count] == 0) {\n        return ast;\n    }\n    id a0 = alst[0];\n    NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0\n                                                            : @\"__<*fn*>__\";\n\n    if ([a0sym isEqualTo:@\"def!\"]) {\n        return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];\n    } else if ([(NSString *)a0 isEqualTo:@\"let*\"]) {\n        Env *let_env = [Env fromOuter:env];\n        NSArray * binds = (NSArray *)alst[1];\n        for (int i=0; i < [binds count]; i+=2) {\n            [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];\n        }\n        env = let_env;\n        ast = alst[2]; // TCO\n    } else if ([(NSString *)a0 isEqualTo:@\"quote\"]) {\n        return alst[1];\n    } else if ([(NSString *)a0 isEqualTo:@\"quasiquote\"]) {\n        ast = quasiquote(alst[1]); // TCO\n    } else if ([a0sym isEqualTo:@\"defmacro!\"]) {\n        MalFunc * f = [(MalFunc *)EVAL(alst[2], env) copy];\n        f.isMacro = true;\n        return [env set:alst[1] val:f];\n    } else if ([a0sym isEqualTo:@\"try*\"]) {\n        @try {\n            return EVAL(alst[1], env);\n        } @catch(NSObject *e) {\n            if ([alst count] > 2 && [alst[2] isKindOfClass:[NSArray class]]) {\n                NSArray * a2lst = alst[2];\n                if ([a2lst[0] isKindOfClass:[MalSymbol class]] &&\n                    [(MalSymbol *)a2lst[0] isEqualTo:@\"catch*\"]) {\n                    NSObject * exc = e;\n                    if ([e isKindOfClass:[NSException class]]) {\n                        exc = [e description];\n                    }\n                    return EVAL(a2lst[2], [Env fromBindings:env\n                                               binds:@[a2lst[1]]\n                                               exprs:@[exc]]);\n                }\n            }\n            @throw e;\n        } \n    } else if ([a0sym isEqualTo:@\"do\"]) {\n        for (int i=1; i < [alst count] - 1; i++) {\n          EVAL(alst[i], env);\n        }\n        ast = [alst lastObject]; // TCO\n    } else if ([a0sym isEqualTo:@\"if\"]) {\n        NSObject * cond = EVAL(alst[1], env);\n        if ([cond isKindOfClass:[NSNull class]] ||\n            [cond isKindOfClass:[MalFalse class]]) {\n            if ([alst count] > 3) {\n                ast = alst[3]; // TCO\n            } else {\n                return [NSNull alloc];\n            }\n        } else {\n            ast = alst[2]; // TCO\n        }\n    } else if ([a0sym isEqualTo:@\"fn*\"]) {\n        return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];\n    } else {\n        id el0 = EVAL(a0, env);\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            if ([mf isMacro]) {\n                NSMutableArray * args = [NSMutableArray array];\n                for (int i = 1; i < [alst count]; i++) {\n                    [args addObject:alst[i]];\n                }\n                ast = [mf apply:args];\n                continue; // TCO\n            }\n        }\n        NSMutableArray * args = [NSMutableArray array];\n        for (int i = 1; i < [alst count]; i++) {\n            [args addObject:EVAL(alst[i], env)];\n        }\n        if ([el0 isKindOfClass:[MalFunc class]]) {\n            MalFunc * mf = el0;\n            env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];\n            ast = [mf ast]; // TCO\n        } else {\n            NSObject * (^ f)(NSArray *) = el0;\n            return f(args);\n        }\n    }\n  }\n}\n\n// print\nNSString *PRINT(NSObject *exp) {\n    return _pr_str(exp, true);\n}\n\n// REPL\nNSString *REP(NSString *line, Env *env) {\n    return PRINT(EVAL(READ(line), env));\n}\n\nint main () {\n    // Outside of pool to prevent \"Block_release called upon\n    // a stack...\" message on exit\n    Env * repl_env = [[Env alloc] init];\n    NSArray *args = [[NSProcessInfo processInfo] arguments];\n\n    // Create an autorelease pool to manage the memory into the program\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n    // If using automatic reference counting (ARC), use @autoreleasepool instead:\n//    @autoreleasepool {\n\n    // core.m: defined using Objective-C\n    NSDictionary * core_ns = [Core ns];\n    for (NSString* key in core_ns) {\n        [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];\n    }\n    [repl_env set:(MalSymbol *)@\"eval\" val:^(NSArray *args) {\n        return EVAL(args[0], repl_env);\n    }];\n    NSArray *argv = @[];\n    if ([args count] > 2) {\n        argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];\n    }\n    [repl_env set:(MalSymbol *)@\"*ARGV*\" val:argv];\n\n    // core.mal: defined using the language itself\n    REP(@\"(def! *host-language* \\\"Objective-C\\\")\", repl_env);\n    REP(@\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    REP(@\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n    REP(@\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n\n\n    if ([args count] > 1) {\n        @try {\n            REP([NSString stringWithFormat:@\"(load-file \\\"%@\\\")\", args[1]], repl_env);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        }\n        return 0;\n    }\n\n    while (true) {\n        REP(@\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env);\n        char *rawline = _readline(\"user> \");\n        if (!rawline) { break; }\n        NSString *line = [NSString stringWithUTF8String:rawline];\n        if ([line length] == 0) { continue; }\n        @try {\n            printf(\"%s\\n\", [[REP(line, repl_env) description] UTF8String]);\n        } @catch(NSString *e) {\n            printf(\"Error: %s\\n\", [e UTF8String]);\n        } @catch(NSObject *e) {\n            NSObject * exc = e;\n            printf(\"Exception: %s\\n\", [_pr_str(exc, true) UTF8String]);\n        } @catch(NSException *e) {\n            if ([[e name] isEqualTo:@\"ReaderContinue\"]) { continue; }\n            printf(\"Exception: %s\\n\", [[e reason] UTF8String]);\n        }\n    }\n\n    [pool drain];\n\n//    }\n}\n"
  },
  {
    "path": "impls/objc/tests/step5_tco.mal",
    "content": ";; Objective C: skipping non-TCO recursion\n;; Reason: completes at 10,000, unrecoverable segfault at 20,000\n"
  },
  {
    "path": "impls/objc/types.h",
    "content": "#import <Foundation/Foundation.h>\n\n//\n// Env definition\n//\n\n@class MalSymbol;\n\n@interface Env : NSObject\n \n@property (copy) NSMutableDictionary * data;\n@property (copy) Env * outer;\n\n- (id)initWithBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs;\n- (id)initWithOuter:(Env *)outer;\n- (id)init;\n\n+ (id)fromOuter:(Env *)outer;\n+ (id)fromBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs;\n\n- (NSObject *) set:(MalSymbol *)key val:(NSObject *)val;\n- (NSObject *) get:(MalSymbol *)key;\n\n@end\n\n//\n// Mal Types\n//\n\n@interface MalTrue : NSObject\n@end\n\n@interface MalFalse : NSObject\n@end\n\n@interface MalSymbol: NSString\n@end\n\nBOOL string_Q(NSObject * obj);\n\n// Lists\n\nBOOL list_Q(id obj);\n\nNSArray * _rest(NSArray * obj);\n\n\n// Vectors\n\n@interface MalVector : NSArray\n\n@property (copy) NSArray * array;\n@property(readonly) NSUInteger count;\n\n- (id)initWithArray:(NSArray *)arr;\n- (id)init;\n\n+ (id)fromArray:(NSArray *)arr;\n\n- (id)objectAtIndex:(NSUInteger)index;\n\n@end\n\n\n// Hash Maps\n\nNSDictionary * assoc_BANG(NSMutableDictionary * d, NSArray * kvs);\nNSDictionary * hash_map(NSArray *kvs);\n\n\n// Mal Functions\n\nBOOL block_Q(id obj);\n\n\n// Atoms\n\n@interface MalAtom : NSObject\n \n@property (copy) NSObject * val;\n\n- (id)init:(NSObject *)val;\n\n+ (id)fromObject:(NSObject *)val;\n\n@end\n\nBOOL atom_Q(id obj);\n\n\n// General functions\n\nBOOL equal_Q(NSObject * a, NSObject * b);\n"
  },
  {
    "path": "impls/objc/types.m",
    "content": "#import \"types.h\"\n\n@implementation MalTrue\n@end\n\n@implementation MalFalse\n@end\n\n\n// NSString subclassing based on:\n// http://stackoverflow.com/a/21331422/471795\n\n// Symbols\n\n@interface MalSymbol ()\n@property (nonatomic, strong) NSString *stringHolder;\n@end\n\n@implementation MalSymbol\n\n- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer {\n    self = [super init];\n    if (self) {\n        self.stringHolder = [[NSString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:freeBuffer];\n    }\n    return self;\n}\n\n- (NSUInteger)length {\n    return self.stringHolder.length;\n}\n\n- (unichar)characterAtIndex:(NSUInteger)index {\n    return [self.stringHolder characterAtIndex:index];\n}\n\n@end\n\n\nBOOL string_Q(id obj) {\n    if ([obj isKindOfClass:[NSString class]]) {\n        NSString * s = obj;\n        if (![s isKindOfClass:[MalSymbol class]]) {\n            return ![s hasPrefix:@\"\\u029e\"];\n        }\n    }\n    return false;\n}\n\n// Lists\n\nBOOL list_Q(id obj) {\n    return ([obj isKindOfClass:[NSArray class]] &&\n            ![obj isKindOfClass:[MalVector class]]);\n}\n\nNSArray * _rest(NSArray * obj) {\n    return [obj subarrayWithRange:NSMakeRange(1, [obj count]-1)];\n}\n\n// Vectors \n\n@implementation MalVector\n\n@synthesize array = _array;\n@synthesize count = _count;\n\n- (id)initWithArray:(NSArray *)arr {\n    self = [self init];\n    if (self) {\n        _array = arr;\n        _count = [arr count];\n    }\n    return self;\n}\n        \n- (id)init {\n    self = [super init];\n    if (self) {\n        _array = @[];\n        _count = 0;\n    }\n    return self;\n}\n\n+ (id)fromArray:(NSArray *)arr {\n    return [[MalVector alloc] initWithArray:arr];\n}    \n\n- (id)objectAtIndex:(NSUInteger)index {\n    return _array[index];\n}\n\n- (id)copyWithZone:(NSZone *)zone {\n    return [[MalVector alloc] initWithArray:[_array copy]];\n}\n\n@end\n\n\n// Hash Maps\n\nNSDictionary * assoc_BANG(NSMutableDictionary * d, NSArray * kvs) {\n    for (int i=0; i < [kvs count]; i+=2) {\n        d[kvs[i]] = kvs[i+1];\n    }\n    return d;\n}\n\nNSDictionary * hash_map(NSArray *kvs) {\n    return assoc_BANG([NSMutableDictionary dictionary], kvs);\n}\n\n\n// Mal Functions\n\nBOOL block_Q(id obj) {\n    id block = ^{};\n    Class blockClass = [block class];\n    while ([blockClass superclass] != [NSObject class]) {\n        blockClass = [blockClass superclass];\n    }\n    return [obj isKindOfClass:blockClass];\n}\n\n\n\n@implementation MalAtom\n\n@synthesize val = _val;\n\n- (id)init:(NSObject *)val {\n    self = [super init];\n    if (self) {\n        _val = val;\n    }\n    return self;\n}\n\n+ (id)fromObject:(NSObject *)val {\n    return [[MalAtom alloc] init:val];\n}\n\n@end\n\nBOOL atom_Q(id obj) {\n    return [obj isKindOfClass:[MalAtom class]];\n}\n\n// General functions\n\nBOOL sequential_Q(NSObject * obj) {\n    return [obj isKindOfClass:[NSArray class]];\n}\n\nBOOL equal_Q(NSObject * a, NSObject * b) {\n    //NSLog(@\"= %@ (%@), %@ (%@)\", a, [a class], b, [b class]);\n    if (!(([a class] == [b class]) ||\n          ([a isKindOfClass:[NSArray class]] &&\n           [b isKindOfClass:[NSArray class]]) ||\n          ([a isKindOfClass:[NSNumber class]] &&\n           [b isKindOfClass:[NSNumber class]]) ||\n          (string_Q(a) && string_Q(b)))) {\n        return false;\n    }\n    if ([a isKindOfClass:[MalTrue class]]) {\n        return true;\n    } else if ([a isKindOfClass:[MalFalse class]]) {\n        return true;\n    } else if ([a isKindOfClass:[NSNumber class]]) {\n        return [(NSNumber *)a intValue] == [(NSNumber *)b intValue];\n    } else {\n        return [a isEqual:b];\n    }\n}\n"
  },
  {
    "path": "impls/objpascal/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Free Pascal\nRUN apt-get -y install libc-dev fp-compiler libedit-dev\n"
  },
  {
    "path": "impls/objpascal/Makefile",
    "content": "STEPS = step0_repl.pas step1_read_print.pas step2_eval.pas \\\n\tstep3_env.pas step4_if_fn_do.pas step5_tco.pas \\\n\tstep6_file.pas step7_quote.pas step8_macros.pas \\\n\tstep9_try.pas stepA_mal.pas\n\nSTEP0_DEPS = mal_readline.pas\nSTEP1_DEPS = $(STEP0_DEPS) mal_types.pas reader.pas printer.pas\nSTEP3_DEPS = $(STEP1_DEPS) mal_env.pas\nSTEP4_DEPS = $(STEP3_DEPS) core.pas\n\n#####################\n\nDEBUG = -gl\n\n# Set this to link with libreadline instead of libedit\nUSE_READLINE =\n\nFPC = fpc -MOBJFPC -ve -Furegexpr/Source $(DEBUG) $(if $(strip $(USE_READLINE)),-dUSE_READLINE,)\n\nall: $(patsubst %.pas,%,$(STEPS))\n\nstep%: step%.pas\n\t$(FPC) $<\n\nstep0_repl: $(STEP0_DEPS)\nstep1_read_print step2_eval: $(STEP1_DEPS)\nstep3_env: $(STEP3_DEPS)\nstep4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal: $(STEP4_DEPS)\n\nclean:\n\trm -f $(STEPS:%.pas=%) *.o *.ppu regexpr/Source/*.o regexpr/Source/*.ppu mal\n"
  },
  {
    "path": "impls/objpascal/core.pas",
    "content": "unit core;\n\n{$H+} // Use AnsiString\n\ninterface\n\nuses Classes,\n     sysutils,\n     fgl,\n     mal_readline,\n     mal_types,\n     mal_func,\n     mal_env,\n     reader,\n     printer;\n\ntype\n    TCoreDict = specialize TFPGMap<string,TMalCallable>;\n\nvar\n    EVAL : function (A: TMal; E: TEnv) : TMal;\n    NS     : TCoreDict;\n\n////////////////////////////////////////////////////////////\n\nimplementation\n\n// General functions\n\nfunction equal_Q(Args: TMalArray) : TMal;\nbegin\n    equal_Q := wrap_tf(_equal_Q(Args[0], Args[1]));\nend;\n\nfunction throw(Args: TMalArray) : TMal;\nbegin\n    raise TMalException.Create(Args[0]);\n    throw := TMalNil.Create; // Not reached\nend;\n\n// Scalar functions\n\nfunction nil_Q(Args: TMalArray) : TMal;\nbegin\n    nil_Q := wrap_tf(Args[0] is TMalNil);\nend;\nfunction true_Q(Args: TMalArray) : TMal;\nbegin\n    true_Q := wrap_tf(Args[0] is TMalTrue);\nend;\nfunction false_Q(Args: TMalArray) : TMal;\nbegin\n    false_Q := wrap_tf(Args[0] is TMalFalse);\nend;\nfunction number_Q(Args: TMalArray) : TMal;\nbegin\n    number_Q := wrap_tf(Args[0] is TMalInt);\nend;\nfunction string_Q(Args: TMalArray) : TMal;\nbegin\n    string_Q := wrap_tf(_string_Q(Args[0]));\nend;\nfunction symbol(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalSymbol then\n        symbol := Args[0]\n    else if Args[0] is TMalString then\n        symbol := TMalSymbol.Create((Args[0] as TMalString).Val)\n    else\n        raise Exception.Create('Invalid symbol call');\nend;\nfunction symbol_Q(Args: TMalArray) : TMal;\nbegin\n    symbol_Q := wrap_tf(Args[0] is TMalSymbol);\nend;\nfunction keyword(Args: TMalArray) : TMal;\nbegin\n    if ((Args[0] is TMalString) and not _string_Q(Args[0])) then\n        keyword := Args[0]\n    else if Args[0] is TMalString then\n        keyword := TMalString.Create(#127 + (Args[0] as TMalString).Val)\n    else\n        raise Exception.Create('Invalid keyword call');\nend;\nfunction keyword_Q(Args: TMalArray) : TMal;\nbegin\n    keyword_Q := wrap_tf((Args[0] is TMalString) and not _string_Q(Args[0]));\nend;\nfunction fn_Q(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalFunc then\n        fn_Q := wrap_tf(not (Args[0] as TMalFunc).isMacro)\n    else\n        fn_Q := TMalFalse.Create;\nend;\n\nfunction macro_Q(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalFunc then\n        macro_Q := wrap_tf((Args[0] as TMalFunc).isMacro)\n    else\n        macro_Q := TMalFalse.Create;\nend;\n\n\n// String functions\n\nfunction do_pr_str(Args: TMalArray) : TMal;\nbegin\n    do_pr_str := TMalString.Create(pr_str_array(Args, true, ' '));\nend;\nfunction str(Args: TMalArray) : TMal;\nbegin\n    str := TMalString.Create(pr_str_array(Args, false, ''));\nend;\nfunction prn(Args: TMalArray) : TMal;\nbegin\n    WriteLn(pr_str_array(Args, true, ' '));\n    prn := TMalNil.Create;\nend;\nfunction println(Args: TMalArray) : TMal;\nbegin\n    WriteLn(pr_str_array(Args, false, ' '));\n    println := TMalNil.Create;\nend;\n\nfunction read_string(Args: TMalArray) : TMal;\nbegin\n    read_string := read_str((Args[0] as TMalString).Val);\nend;\nfunction do_readline(Args: TMalArray) : TMal;\nvar\n    Prompt : string;\n    Line   : string;\nbegin\n    Prompt := (Args[0] as TMalString).Val;\n    try\n        Line := _readline(Prompt);\n        do_readline := TMalString.Create(Line);\n    except\n        On E : MalEOF do do_readline := TMalNil.Create;\n    end;\nend;\nfunction slurp(Args: TMalArray) : TMal;\nvar\n    StrL : TStringList;\nbegin\n    StrL := TStringList.Create;\n    StrL.LoadFromFile((Args[0] as TMalString).Val);\n    slurp := TMalString.Create(StrL.Text);\nend;\n\n// Math functions\n\nfunction lt(Args: TMalArray) : TMal;\nbegin\n    lt := wrap_tf((Args[0] as TMalInt).Val < (Args[1] as TMalInt).Val);\nend;\nfunction lte(Args: TMalArray) : TMal;\nbegin\n    lte := wrap_tf((Args[0] as TMalInt).Val <= (Args[1] as TMalInt).Val);\nend;\nfunction gt(Args: TMalArray) : TMal;\nbegin\n    gt := wrap_tf((Args[0] as TMalInt).Val > (Args[1] as TMalInt).Val);\nend;\nfunction gte(Args: TMalArray) : TMal;\nbegin\n    gte := wrap_tf((Args[0] as TMalInt).Val >= (Args[1] as TMalInt).Val);\nend;\n\nfunction add(Args: TMalArray) : TMal;\nbegin\n    add := TMalInt.Create((Args[0] as TMalInt).Val +\n                          (Args[1] as TMalInt).Val);\nend;\nfunction subtract(Args: TMalArray) : TMal;\nbegin\n    subtract := TMalInt.Create((Args[0] as TMalInt).Val -\n                               (Args[1] as TMalInt).Val);\nend;\nfunction multiply(Args: TMalArray) : TMal;\nbegin\n    multiply := TMalInt.Create((Args[0] as TMalInt).Val *\n                               (Args[1] as TMalInt).Val);\nend;\nfunction divide(Args: TMalArray) : TMal;\nbegin\n    divide := TMalInt.Create((Args[0] as TMalInt).Val div\n                             (Args[1] as TMalInt).Val);\nend;\nfunction time_ms(Args: TMalArray) : TMal;\nbegin\n    time_ms := TMalInt.Create(Trunc(TimeStampToMSecs(DateTimeToTimeStamp(Now))));\nend;\n\n// Collection functions\n\nfunction list(Args: TMalArray) : TMal;\nbegin\n    list := TMalList.Create(Args);\nend;\nfunction list_Q(Args: TMalArray) : TMal;\nbegin\n    list_Q := wrap_tf(Args[0].ClassType = TMalList);\nend;\nfunction vec(Args: TMalArray) : TMal;\nbegin\n    vec := TMalVector.Create((Args[0] as TMalList).Val);\nend;\nfunction vector(Args: TMalArray) : TMal;\nbegin\n    vector := TMalVector.Create(Args);\nend;\nfunction vector_Q(Args: TMalArray) : TMal;\nbegin\n    vector_Q := wrap_tf(Args[0].ClassType = TMalVector);\nend;\nfunction hash_map(Args: TMalArray) : TMal;\nbegin\n    hash_map := TMalHashMap.Create(Args);\nend;\nfunction map_Q(Args: TMalArray) : TMal;\nbegin\n    map_Q := wrap_tf(Args[0].ClassType = TMalHashMap);\nend;\nfunction assoc(Args: TMalArray) : TMal;\nvar\n    OrigHM, NewHM : TMalHashMap;\nbegin\n    OrigHM := (Args[0] as TMalHashMap);\n    NewHM := TMalHashMap.Clone(OrigHM);\n    assoc := NewHM.assoc_BANG(copy(Args, 1, Length(Args)));\nend;\nfunction dissoc(Args: TMalArray) : TMal;\nvar\n    OrigHM, NewHM : TMalHashMap;\nbegin\n    OrigHM := (Args[0] as TMalHashMap);\n    NewHM := TMalHashMap.Clone(OrigHM);\n    dissoc := NewHM.dissoc_BANG(copy(Args, 1, Length(Args)));\nend;\nfunction get(Args: TMalArray) : TMal;\nvar\n    HM : TMalHashMap;\nbegin\n    if Args[0] is TMalNil then Exit(TMalNil.Create);\n    HM := (Args[0] as TMalHashMap);\n    if HM.Val.IndexOf((Args[1] as TMalString).Val) >= 0 then\n        get := HM.Val[(Args[1] as TMalString).Val]\n    else\n        get := TMalNil.Create;\nend;\nfunction contains_Q(Args: TMalArray) : TMal;\nvar\n    HM : TMalHashMap;\nbegin\n    if Args[0] is TMalNil then Exit(TMalFalse.Create);\n    HM := (Args[0] as TMalHashMap);\n    contains_Q := wrap_tf(HM.Val.IndexOf((Args[1] as TMalString).Val) >= 0);\nend;\nfunction keys(Args: TMalArray) : TMal;\nvar\n    Dict : TMalDict;\n    Arr  : TMalArray;\n    I    : longint;\nbegin\n    Dict := (Args[0] as TMalHashMap).Val;\n    SetLength(Arr, Dict.Count);\n    for I := 0 to Dict.Count-1 do\n        Arr[I] := TMalString.Create(Dict.Keys[I]);\n    keys := TMalList.Create(Arr);\nend;\nfunction vals(Args: TMalArray) : TMal;\nvar\n    Dict : TMalDict;\n    Arr  : TMalArray;\n    I    : longint;\nbegin\n    Dict := (Args[0] as TMalHashMap).Val;\n    SetLength(Arr, Dict.Count);\n    for I := 0 to Dict.Count-1 do\n        Arr[I] := Dict[Dict.Keys[I]];\n    vals := TMalList.Create(Arr);\nend;\n\n\n// Sequence functions\n\nfunction sequential_Q(Args: TMalArray) : TMal;\nbegin\n    sequential_Q := wrap_tf(_sequential_Q(Args[0]));\nend;\nfunction cons(Args: TMalArray) : TMal;\nvar\n    Res, Src : TMalArray;\n    I        : longint;\nbegin\n    Src := (Args[1] as TMalList).Val;\n    SetLength(Res, 1 + Length(Src));\n    Res[0] := Args[0];\n    for I := 1 to Length(Src) do\n        Res[I] := Src[I-1];\n    cons := TMalList.Create(Res);\nend;\nfunction do_concat(Args: TMalArray) : TMal;\nvar\n    Res  : TMalArray;\n    I    : longint;\nbegin\n    SetLength(Res, 0);\n    for I := 0 to Length(Args)-1 do\n    begin\n        Res := _concat(Res, (Args[I] as TMalList).Val);\n    end;\n    do_concat := TMalList.Create(Res);\nend;\nfunction nth(Args: TMalArray) : TMal;\nvar\n    Arr : TMalArray;\n    Idx : longint;\nbegin\n    Arr := (Args[0] as TMalList).Val;\n    Idx := (Args[1] as TMalInt).Val;\n    if Idx >= Length(Arr) then\n        raise Exception.Create('nth: index out of range')\n    else\n        nth := Arr[Idx];\nend;\nfunction first(Args: TMalArray) : TMal;\nvar\n    Arr : TMalArray;\nbegin\n    if Args[0] is TMalNil then Exit(TMalNil.Create);\n    Arr := (Args[0] as TMalList).Val;\n    if Length(Arr) = 0 then\n        first := TMalNil.Create\n    else\n        first := (Args[0] as TMalList).Val[0];\nend;\nfunction rest(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalNil then Exit(_list());\n    rest := (Args[0] as TMalList).Rest();\nend;\n\nfunction empty_Q(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalNil then\n        empty_Q := TMalTrue.Create\n    else if Args[0] is TMalList then\n        empty_Q := wrap_tf(Length((Args[0] as TMalList).Val) = 0)\n    else raise Exception.Create('invalid empty? call');\nend;\nfunction count(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalNil then\n        count := TMalInt.Create(0)\n    else if Args[0] is TMalList then\n        count := TMalInt.Create(Length((Args[0] as TMalList).Val))\n    else raise Exception.Create('invalid count call');\nend;\n\nfunction map(Args: TMalArray) : TMal;\nvar\n    Fn       : TMalFunc;\n    FArgs    : TMalArray;\n    Src, Res : TMalArray;\n    I        : longint;\nbegin\n    Fn := (Args[0] as TMalFunc);\n    Src := (Args[1] as TMalList).Val;\n    SetLength(FArgs, 1);\n    SetLength(Res, Length(Src));\n    if Fn.Ast = nil then\n        for I := 0 to Length(Src)-1 do\n        begin\n            FArgs[0] := Src[I];\n            Res[I] := Fn.Val(FArgs);\n        end\n    else\n        for I := 0 to Length(Src)-1 do\n        begin\n            FArgs[0] := Src[I];\n            Res[I] := EVAL(Fn.Ast, TEnv.Create(Fn.Env, Fn.Params, FArgs));\n        end;\n    map := TMalList.Create(Res);\nend;\nfunction apply(Args: TMalArray) : TMal;\nvar\n    Fn       : TMalFunc;\n    LastArgs : TMalArray;\n    FArgs    : TMalArray;\n    I        : longint;\nbegin\n    Fn := (Args[0] as TMalFunc);\n    LastArgs := (Args[Length(Args)-1] as TMalList).Val;\n    SetLength(FArgs, Length(LastArgs) + Length(Args) - 2);\n    for I := 0 to Length(Args)-3 do\n        FArgs[I] := Args[I+1];\n    for I := 0 to Length(LastArgs)-1 do\n        FArgs[Length(Args)-2 + I] := LastArgs[I];\n    if Fn.Ast = nil then\n        apply := Fn.Val(FArgs)\n    else\n        apply := EVAL(Fn.Ast, TEnv.Create(Fn.Env, Fn.Params, FArgs));\nend;\n\nfunction conj(Args: TMalArray) : TMal;\nvar\n    I    : longint;\n    Vals : TMalArray;\nbegin\n    if Args[0] is TMalVector then\n        conj := TMalVector.Create(_concat((Args[0] as TMalList).Val,\n                                          copy(Args, 1, Length(Args))))\n    else if Args[0] is TMalList then\n    begin\n        SetLength(Vals, Length(Args)-1);\n        for I := 1 to Length(Args)-1 do\n            Vals[I-1] := Args[Length(Args) - I];\n        conj := TMalList.Create(_concat(Vals, (Args[0] as TMalList).Val));\n    end\n    else\n        raise Exception.Create('conj: called on non-sequence');\nend;\nfunction seq(Args: TMalArray) : TMal;\nvar\n    Str : string;\n    Arr : TMalArray;\n    I   : longint;\nbegin\n    if Args[0] is TMalVector then\n    begin\n        if Length((Args[0] as TMalVector).Val) = 0 then\n            Exit(TMalNil.Create);\n        seq := TMalList.Create((Args[0] as TMalVector).Val);\n    end\n    else if Args[0] is TMalList then\n    begin\n        if Length((Args[0] as TMalList).Val) = 0 then\n            Exit(TMalNil.Create);\n        seq := Args[0]\n    end\n    else if _string_Q(Args[0]) then\n    begin\n        Str := (Args[0] as TMalString).Val;\n        if Length(Str) = 0 then\n            Exit(TMalNil.Create);\n        SetLength(Arr, Length(Str));\n        for I := 0 to Length(Str) do\n            Arr[I] := TMalString.Create(Str[I+1]);\n        seq := TMalList.Create(Arr);\n    end\n    else if Args[0] is TMalNil then\n    begin\n        seq := Args[0];\n    end\n    else\n        raise Exception.Create('seq: called on non-sequence');\nend;\n\n\n// Metadata functions\n\nfunction meta(Args: TMalArray) : TMal;\nbegin\n    if Args[0] is TMalFunc then\n        meta := (Args[0] as TMalFunc).Meta\n    else if Args[0] is TMalList then\n        meta := (Args[0] as TMalList).Meta\n    else if Args[0] is TMalHashMap then\n        meta := (Args[0] as TMalHashMap).Meta\n    else\n        raise Exception.Create('meta not supported on ' + Args[0].ClassName);\n\n    if meta = nil then\n        meta := TMalNil.Create;\nend;\nfunction with_meta(Args: TMalArray) : TMal;\nvar\n    Fn  : TMalFunc;\n    Vec : TMalVector;\n    Lst : TMalList;\n    HM  : TMalHashMap;\nbegin\n    if Args[0] is TMalFunc then\n    begin\n        Fn := TMalFunc.Clone(Args[0] as TMalFunc);\n        Fn.Meta := Args[1];\n        with_meta := Fn;\n    end\n    else if Args[0] is TMalVector then\n    begin\n        Vec := TMalVector.Clone(Args[0] as TMalVector);\n        Vec.Meta := Args[1];\n        with_meta := Vec;\n    end\n    else if Args[0] is TMalList then\n    begin\n        Lst := TMalList.Clone(Args[0] as TMalList);\n        Lst.Meta := Args[1];\n        with_meta := Lst;\n    end\n    else if Args[0] is TMalHashMap then\n    begin\n        HM := TMalHashMap.Clone(Args[0] as TMalHashMap);\n        HM.Meta := Args[1];\n        with_meta := HM;\n    end\n    else\n        raise Exception.Create('with-meta call on non-mal function');\nend;\n\n// Atom functions\n\nfunction atom(Args: TMalArray) : TMal;\nbegin\n    atom := TMalAtom.Create(Args[0]);\nend;\nfunction atom_Q(Args: TMalArray) : TMal;\nbegin\n    atom_Q := wrap_tf(Args[0] is TMalAtom);\nend;\nfunction deref(Args: TMalArray) : TMal;\nbegin\n    deref := (Args[0] as TMalAtom).Val;\nend;\nfunction reset_BANG(Args: TMalArray) : TMal;\nbegin\n    (Args[0] as TMalAtom).Val := Args[1];\n    reset_BANG := Args[1];\nend;\n\nfunction swap_BANG(Args: TMalArray) : TMal;\nvar\n    Atm   : TMalAtom;\n    Fn    : TMalFunc;\n    FArgs : TMalArray;\n    I     : longint;\nbegin\n    Atm := (Args[0] as TMalAtom);\n    Fn := (Args[1] as TMalFunc);\n    SetLength(FArgs, Length(Args)-1);\n    FArgs[0] := Atm.Val;\n    for I := 1 to Length(Args)-2 do\n        FArgs[I] := Args[I+1];\n\n    if Fn.Ast = nil then\n        Atm.Val := Fn.Val(FArgs)\n    else\n        Atm.Val := EVAL(Fn.Ast, TEnv.Create(Fn.Env, Fn.Params, FArgs));\n    swap_BANG := Atm.Val;\nend;\n\n\n////////////////////////////////////////////////////////////\n\ninitialization\nbegin\n    NS := TCoreDict.Create;\n    NS['='] := @equal_Q;\n    NS['throw'] := @throw;\n\n    NS['nil?'] := @nil_Q;\n    NS['true?'] := @true_Q;\n    NS['false?'] := @false_Q;\n    NS['number?'] := @number_Q;\n    NS['string?'] := @string_Q;\n    NS['symbol'] := @symbol;\n    NS['symbol?'] := @symbol_Q;\n    NS['keyword'] := @keyword;\n    NS['keyword?'] := @keyword_Q;\n    NS['fn?'] := @fn_Q;\n    NS['macro?'] := @macro_Q;\n\n    NS['pr-str'] := @do_pr_str;\n    NS['str'] := @str;\n    NS['prn'] := @prn;\n    NS['println'] := @println;\n    NS['read-string'] := @read_string;\n    NS['readline'] := @do_readline;\n    NS['slurp'] := @slurp;\n\n    NS['<'] := @lt;\n    NS['<='] := @lte;\n    NS['>'] := @gt;\n    NS['>='] := @gte;\n    NS['+'] := @add;\n    NS['-'] := @subtract;\n    NS['*'] := @multiply;\n    NS['/'] := @divide;\n    NS['time-ms'] := @time_ms;\n\n    NS['list'] := @list;\n    NS['list?'] := @list_Q;\n    NS['vector'] := @vector;\n    NS['vector?'] := @vector_Q;\n    NS['hash-map'] := @hash_map;\n    NS['map?'] := @map_Q;\n    NS['assoc'] := @assoc;\n    NS['dissoc'] := @dissoc;\n    NS['get'] := @get;\n    NS['contains?'] := @contains_Q;\n    NS['keys'] := @keys;\n    NS['vals'] := @vals;\n\n    NS['sequential?'] := @sequential_Q;\n    NS['cons'] := @cons;\n    NS['concat'] := @do_concat;\n    NS['vec'] := @vec;\n    NS['nth'] := @nth;\n    NS['first'] := @first;\n    NS['rest'] := @rest;\n    NS['empty?'] := @empty_Q;\n    NS['count'] := @count;\n    NS['apply'] := @apply;\n    NS['map'] := @map;\n\n    NS['conj'] := @conj;\n    NS['seq'] := @seq;\n\n    NS['meta'] := @meta;\n    NS['with-meta'] := @with_meta;\n    NS['atom'] := @atom;\n    NS['atom?'] := @atom_Q;\n    NS['deref'] := @deref;\n    NS['reset!'] := @reset_BANG;\n    NS['swap!'] := @swap_BANG;\nend\n\nend.\n"
  },
  {
    "path": "impls/objpascal/mal_env.pas",
    "content": "unit mal_env;\n\n{$H+} // Use AnsiString\n\ninterface\n\nUses sysutils,\n     fgl,\n     mal_types;\n\ntype TEnv = class(TObject)\n    public\n        Data  : TMalDict;\n        Outer : TEnv;\n\n        constructor Create;\n        constructor Create(_Outer : TEnv);\n        constructor Create(_Outer : TEnv;\n                           Binds  : TMalList;\n                           Exprs  : TMalArray);\n\n        function Add(Key : TMalSymbol; Val : TMal) : TMal;\n        function Get(Key : String) : TMal;\nend;\n\n////////////////////////////////////////////////////////////\n\nimplementation\n\nconstructor TEnv.Create();\nbegin\n    inherited Create();\n    Self.Data  := TMalDict.Create;\n    Self.Outer := nil;\nend;\n\nconstructor TEnv.Create(_Outer: TEnv);\nbegin\n    Self.Create();\n    Self.Outer := _Outer;\nend;\n\nconstructor TEnv.Create(_Outer : TEnv;\n                        Binds  : TMalList;\n                        Exprs  : TMalArray);\nvar\n    I     : longint;\n    Bind  : TMalSymbol;\n    Rest  : TMalList;\nbegin\n    Self.Create(_Outer);\n    for I := 0 to Length(Binds.Val)-1 do\n    begin\n        Bind := (Binds.Val[I] as TMalSymbol);\n        if Bind.Val = '&' then\n        begin\n            if I < Length(Exprs) then\n                Rest := TMalList.Create(copy(Exprs, I, Length(Exprs)-I))\n            else\n                Rest := TMalList.Create;\n            Self.Data[(Binds.Val[I+1] as TMalSymbol).Val] := Rest;\n            break;\n        end;\n        Self.Data[Bind.Val] := Exprs[I];\n    end;\nend;\n\nfunction TEnv.Add(Key : TMalSymbol; Val : TMal) : TMal;\nbegin\n    Self.Data[Key.Val] := Val;\n    Add := Val;\nend;\n\nfunction TEnv.Get(Key : String) : TMal;\nbegin\n    if Data.IndexOf(Key) >= 0 then\n        Get := Data[Key]\n    else if Outer <> nil then\n        Get := Outer.Get(Key)\n    else\n        Get := nil;\nend;\n\nend.\n"
  },
  {
    "path": "impls/objpascal/mal_func.pas",
    "content": "unit mal_func;\n\ninterface\n\nuses mal_types,\n     mal_env;\n\n// Some general type definitions\n\ntype\n    TMalCallable = function (Args : TMalArray) : TMal;\n\ntype TMalFunc = class(TMal)\n    public\n        Val     : TMalCallable;\n        Ast     : TMal;\n        Env     : TEnv;\n        Params  : TMalList;\n        isMacro : Boolean;\n        Meta    : TMal;\n\n        constructor Create(V : TMalCallable);\n        constructor Create(A  : TMal;\n                           E  : TEnv;\n                           P  : TMalList);\n\n        constructor Clone(F : TMalFunc);\nend;\n\n////////////////////////////////////////////////////////////\n\nimplementation\n\nconstructor TMalFunc.Create(V : TMalCallable);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\nconstructor TMalFunc.Create(A  : TMal;\n                            E  : TEnv;\n                            P  : TMalList);\nbegin\n    inherited Create();\n    Self.Ast    := A;\n    Self.Env    := E;\n    Self.Params := P;\nend;\n\nconstructor TMalFunc.Clone(F : TMalFunc);\nbegin\n    Self.Create(F.Ast, F.Env, F.Params);\n    Self.isMacro := F.isMacro;\n    Self.Meta := F.Meta;\nend;\n\nend.\n"
  },
  {
    "path": "impls/objpascal/mal_readline.pas",
    "content": "unit mal_readline;\n\n{$H+} // Use AnsiString\n\ninterface\n\nuses sysutils,\n     CTypes;\n\n{$IFDEF USE_READLINE}\n\n{$LINKLIB readline}\n\n{$ELSE}\n\n{$LINKLIB libedit}\n\n{$ENDIF}\n\n\n// External libedit/readline functions\n\nfunction readline(Prompt: PChar) : PChar; cdecl; external;\nprocedure add_history(Line: PChar); cdecl; external;\n\n// API\n\ntype MalEOF = class(Exception);\n\nfunction _readline(Prompt: string) : string;\n\n////////////////////////////////////////////////////////////\n\nimplementation\n\nfunction _readline(Prompt: string) : string;\nvar\n    Line : PChar;\nbegin\n    Line := readline(PChar(Prompt));\n    if Line = Nil then\n        raise MalEOF.Create('MalEOF');\n    if Line <> '' then\n        add_history(Line);\n\n    _readline := Line;\nend;\n\nend.\n"
  },
  {
    "path": "impls/objpascal/mal_types.pas",
    "content": "unit mal_types;\n\n{$H+} // Use AnsiString\n\ninterface\n\nuses sysutils,\n     fgl;\n\n// Ancestor of all Mal types\n\ntype TMal = class(TObject);\n\n\n// Some general type definitions\n\ntype\n    TMalArray = array of TMal;\n    // TODO: use http://bugs.freepascal.org/view.php?id=27206 when\n    // incorporated into FPC\n    TMalDict = specialize TFPGMap<string,TMal>;\n\ntype TMalException = class(Exception)\n    public\n        Val: TMal;\n\n        constructor Create(V : TMal);\nend;\n\n\n// Mal types\n\ntype TMalNil = class(TMal);\ntype TMalTrue = class(TMal);\ntype TMalFalse = class(TMal);\n\ntype TMalInt = class(TMal)\n    public\n        Val: int64;\n\n        constructor Create(V : int64);\nend;\n\ntype TMalString = class(TMal)\n    public\n        Val: string;\n\n        constructor Create(V : string);\nend;\n\ntype TMalSymbol = class(TMal)\n    public\n        Val: string;\n\n        constructor Create(V : string);\nend;\n\n\ntype TMalList = class(TMal)\n    public\n        Val:  TMalArray;\n        Meta: TMal;\n\n        constructor Create();\n        constructor Create(V : TMalArray);\n        function Rest() : TMalList;\n\n        constructor Clone(L : TMalList);\nend;\n\ntype TMalVector = class(TMalList)\nend;\n\ntype TMalAtom = class(TMal)\n    public\n        Val: TMal;\n\n        constructor Create(V : TMal);\nend;\n\ntype TMalHashMap = class(TMal)\n    public\n        Val: TMalDict;\n        Meta: TMal;\n\n        constructor Create();\n        constructor Create(V : TMalDict);\n        constructor Create(V : TMalArray);\n\n        constructor Clone(HM : TMalHashMap);\n\n        function assoc_BANG(KVs: TMalArray) : TMal;\n        function dissoc_BANG(Ks: TMalArray) : TMal;\nend;\n\n\n// General type functions\n\nfunction GetBacktrace(E: Exception) : string;\n\nfunction wrap_tf(x : Boolean) : TMal;\n\nfunction _equal_Q(A : TMal; B : TMal) : Boolean;\n\nfunction _sequential_Q(Obj: TMal) : Boolean;\n\nfunction _list() : TMalList;\nfunction _list(A: TMal) : TMalList;\nfunction _list(A: TMal; B: TMal) : TMalList;\nfunction _list(A: TMal; B: TMal; C: TMal) : TMalList;\n\nfunction _concat(A: TMalArray; B: TMalArray) : TMalArray;\n\nfunction _string_Q(Obj: TMal) : Boolean;\n\n////////////////////////////////////////////////////////////\n\nimplementation\n\nconstructor TMalException.Create(V : TMal);\nbegin\n    inherited Create('MalException');\n    Self.Val := V;\nend;\n\n//\n// Mal types\n//\n\nconstructor TMalInt.Create(V : int64);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\nconstructor TMalString.Create(V : string);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\nconstructor TMalSymbol.Create(V : string);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\nconstructor TMalList.Create();\nbegin\n    inherited Create();\n    SetLength(Self.Val, 0);\nend;\n\nconstructor TMalList.Create(V : TMalArray);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\nconstructor TMalList.Clone(L : TMalList);\nbegin\n    inherited Create();\n    Self.Val := copy(L.Val, 0, Length(L.Val));\nend;\n\n\nfunction TMalList.Rest() : TMalList;\nbegin\n    if Length(Val) <= 1 then\n        Rest := (_list() as TMalList)\n    else\n        Rest := TMalList.Create(copy(Val, 1, Length(Val)-1));\nend;\n\n// Hash Maps\n\nconstructor TMalHashMap.Create();\nbegin\n    inherited Create();\n    Self.Val := TMalDict.Create;\nend;\n\nconstructor TMalHashMap.Create(V : TMalDict);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\nfunction TMalHashMap.assoc_BANG(KVs: TMalArray) : TMal;\nvar\n    I : longint;\nbegin\n    I := 0;\n    while I < Length(KVs) do\n    begin\n        Self.Val[(KVs[I] as TMalString).Val] := KVs[I+1];\n        I := I + 2;\n    end;\n    assoc_BANG := Self;\nend;\n\nfunction TMalHashMap.dissoc_BANG(Ks: TMalArray) : TMal;\nvar\n    I : longint;\nbegin\n    for I := 0 to Length(Ks)-1 do\n        Self.Val.Remove((Ks[I] as TMalString).Val);\n    dissoc_BANG := Self;\nend;\n\n\nconstructor TMalHashMap.Create(V : TMalArray);\nbegin\n    Self.Create();\n    Self.assoc_BANG(V);\nend;\n\nconstructor TMalHashMap.Clone(HM : TMalHashMap);\nvar\n    I : longint;\nbegin\n    Self.Create();\n    I := 0;\n    while I < HM.Val.Count do\n    begin\n        Self.Val[HM.Val.Keys[I]] := HM.Val[HM.Val.Keys[I]];\n        I := I + 1;\n    end;\nend;\n\n\n// Atoms\n\nconstructor TMalAtom.Create(V : TMal);\nbegin\n    inherited Create();\n    Self.Val := V;\nend;\n\n//\n// General type functions\n//\n\nfunction GetBacktrace(E: Exception) : string;\nvar\n  I: Integer;\n  Frames: PPointer;\nbegin\n  GetBacktrace := BackTraceStrFunc(ExceptAddr);\n  Frames := ExceptFrames;\n  for I := 0 to ExceptFrameCount - 1 do\n    GetBacktrace := GetBacktrace + #10 + BackTraceStrFunc(Frames[I]);\nend;\n\nfunction wrap_tf(x : Boolean) : TMal;\nbegin\n    if x = true then wrap_tf := TMalTrue.Create\n    else             wrap_tf := TMalFalse.Create;\nend;\n\nfunction _equal_Q(A : TMal; B : TMal) : Boolean;\nvar\n    I            : longint;\n    ArrA, ArrB   : TMalArray;\n    DictA, DictB : TMalDict;\n    Key          : string;\nbegin\n    if not ((A.ClassType = B.ClassType) or\n            ((A is TMalList) and (B is TMalList))) then\n        _equal_Q := false\n    else\n    begin\n        if A is TMalList then\n        begin\n            ArrA := (A as TMalList).Val;\n            ArrB := (B as TMalList).Val;\n            if Length(ArrA) <> Length(ArrB) then\n                Exit(false);\n            for I := 0 to Length(ArrA)-1 do\n                if not _equal_Q(ArrA[I], ArrB[I]) then\n                    Exit(false);\n            _equal_Q := true;\n        end\n        else if A is TMalHashMap then\n        begin\n            DictA := (A as TMalHashMap).Val;\n            DictB := (B as TMalHashMap).Val;\n            if DictA.Count <> DictB.Count then\n                Exit(false);\n            for I := 0 to DictA.Count-1 do\n            begin\n                Key := DictA.Keys[I];\n                if DictB.IndexOf(Key) < 0 then\n                    Exit(false);\n                if not _equal_Q(DictA[Key], DictB[Key]) then\n                    Exit(false);\n            end;\n            _equal_Q := true;\n        end\n        else if A is TMalString then\n            _equal_Q := (A as TMalString).Val = (B as TMalString).Val\n        else if A is TMalSymbol then\n            _equal_Q := (A as TMalSymbol).Val = (B as TMalSymbol).Val\n        else if A is TMalInt then\n            _equal_Q := (A as TMalInt).Val = (B as TMalInt).Val\n        else if A is TMalNil then\n            _equal_Q := B is TMalNil\n        else if A is TMalTrue then\n            _equal_Q := B is TMalTrue\n        else if A is TMalFalse then\n            _equal_Q := B is TMalFalse\n        else\n            _equal_Q := A = B;\n    end\nend;\n\nfunction _sequential_Q(Obj: TMal) : Boolean;\nbegin\n    _sequential_Q := Obj is TMalList;\nend;\n\n\nfunction _list() : TMalList;\nvar\n    Arr: TMalArray;\nbegin\n    SetLength(Arr, 0);\n    _list := TMalList.Create(Arr);\nend;\n\nfunction _list(A: TMal) : TMalList;\nvar\n    Arr: TMalArray;\nbegin\n    SetLength(Arr, 1);\n    Arr[0] := A;\n    _list := TMalList.Create(Arr);\nend;\n\nfunction _list(A: TMal; B: TMal) : TMalList;\nvar\n    Arr: TMalArray;\nbegin\n    SetLength(Arr, 2);\n    Arr[0] := A;\n    Arr[1] := B;\n    _list := TMalList.Create(Arr);\nend;\n\nfunction _list(A: TMal; B: TMal; C: TMal) : TMalList;\nvar\n    Arr: TMalArray;\nbegin\n    SetLength(Arr, 3);\n    Arr[0] := A;\n    Arr[1] := B;\n    Arr[2] := C;\n    _list := TMalList.Create(Arr);\nend;\n\nfunction _concat(A: TMalArray; B: TMalArray) : TMalArray;\nvar\n    Res : TMalArray;\n    I   : longint;\nbegin\n    SetLength(Res, Length(A) + Length(B));\n    for I := 0 to Length(A)-1 do\n        Res[I] := A[I];\n    for I := 0 to Length(B)-1 do\n        Res[I+Length(A)] := B[I];\n    _concat := Res;\nend;\n\nfunction _string_Q(Obj: TMal) : Boolean;\nvar\n    Str : string;\nbegin\n    if (Obj is TMalString) then\n    begin\n        Str := (Obj as TMalString).Val;\n        _string_Q := (Length(Str) = 0) or (Str[1] <> #127)\n    end\n    else\n        _string_Q := false;\nend;\n\nend.\n"
  },
  {
    "path": "impls/objpascal/printer.pas",
    "content": "unit printer;\n\n{$H+} // Use AnsiString\n\ninterface\n\nUses sysutils,\n     mal_types,\n     mal_func;\n\nfunction pr_str_array(Args : TMalArray;\n                      print_readably : Boolean;\n                      Separator : string) : string;\n\nfunction pr_str(Obj : TMal; print_readably : Boolean) : string;\n\nimplementation\n\nfunction pr_str_array(Args : TMalArray;\n                      print_readably : Boolean;\n                      Separator : string) : string;\nvar\n    Str : string;\n    I   : longint;\nbegin\n    Str := '';\n    for I := 0 to Length(Args)-1 do\n    begin\n        Str := Str + pr_str(Args[I], print_readably);\n        if I <> Length(Args)-1 then\n            Str := Str + Separator;\n    end;\n    pr_str_array := Str;\nend;\n\nfunction pr_str_dict(Dict : TMalDict;\n                     print_readably : Boolean;\n                     Separator : string) : string;\nvar\n    I    : longint;\n    Arr  : TMalArray;\nbegin\n    SetLength(Arr, Dict.Count * 2);\n    I := 0;\n    while I < Dict.Count do\n    begin\n        Arr[I*2]   := TMalString.Create(Dict.Keys[I]);\n        Arr[I*2+1] := Dict[Dict.Keys[I]];\n        I := I + 1;\n    end;\n    pr_str_dict := pr_str_array(Arr, print_readably, ' ');\nend;\n\n\nfunction pr_str(Obj : TMal; print_readably : Boolean) : string;\nvar\n    Str  : string;\n    Fn   : TMalFunc;\nbegin\n    if Obj.ClassType = TMalList then\n        pr_str := '(' + pr_str_array((Obj as TMalList).Val,\n                                     print_readably,\n                                     ' ') + ')'\n    else if Obj.ClassType = TMalVector then\n        pr_str := '[' + pr_str_array((Obj as TMalList).Val,\n                                     print_readably,\n                                     ' ') + ']'\n    else if Obj is TMalHashMap then\n        pr_str := '{' + pr_str_dict((Obj as TMalHashMap).Val,\n                                     print_readably,\n                                     ' ') + '}'\n    else if Obj is TMalString then\n    begin\n        Str := (Obj as TMalString).Val;\n        if (Length(Str) > 0) and (Str[1] = #127) then\n            pr_str := ':' + copy(Str, 2, Length(Str))\n        else if print_readably then\n        begin\n            Str := StringReplace(Str, '\\', '\\\\', [rfReplaceAll]);\n            Str := StringReplace(Str, '\"', '\\\"', [rfReplaceAll]);\n            Str := StringReplace(Str, #10, '\\n', [rfReplaceAll]);\n            pr_str := Format('\"%s\"', [Str])\n        end\n        else\n            pr_str := Str;\n    end\n    else if Obj is TMalNil then\n        pr_str := 'nil'\n    else if Obj is TMalTrue then\n        pr_str := 'true'\n    else if Obj is TMalFalse then\n        pr_str := 'false'\n    else if Obj is TMalInt then\n        pr_str := IntToStr((Obj as TMalInt).Val)\n    else if Obj is TMalSymbol then\n        pr_str := (Obj as TMalSymbol).Val\n    else if Obj is TMalAtom then\n        pr_str := '(atom ' +\n                  pr_str((Obj as TMalAtom).Val, print_readably) +\n                  ')'\n    else if Obj is TMalFunc then\n    begin\n        Fn := (Obj as TMalFunc);\n        if Fn.Ast = nil then\n            pr_str := '#<native function>'\n        else\n            pr_str := '(fn* ' + pr_str(Fn.Params,true) +\n                      ' ' + pr_str(Fn.Ast,true) + ')'\n    end\n    else\n        pr_str := '#unknown';\nend;\n\nend.\n"
  },
  {
    "path": "impls/objpascal/reader.pas",
    "content": "unit reader;\n\n{$H+} // Use AnsiString\n\ninterface\n\nUses sysutils,\n     Classes,\n     RegExpr in 'regexpr/Source/RegExpr.pas',\n     mal_types;\n\n//\n// Reader class\n//\n\ntype TReader = class(TObject)\n    public\n        Tokens : TStringList;\n        Position : Integer;\n\n        constructor Create(Toks: TStringList);\n\n        function Peek() : string;\n        function Next() : string;\nend;\n\n//\n// reader functions\n//\n\nfunction read_str(const Str: string): TMal;\n\n\nimplementation\n\n//\n// Reader class\n//\n\nconstructor TReader.Create(Toks: TStringList);\nbegin\n    inherited Create();\n    Self.Tokens := Toks;\n    Self.Position := 0;\nend;\n\nfunction TReader.Peek() : string;\nbegin\n    if Position >= Tokens.Count then\n        Peek := #0\n    else\n        Peek := Tokens[Position];\nend;\n\nfunction TReader.Next() : string;\nbegin\n    Next := Tokens[Position];\n    Position := Position + 1;\nend;\n\n\n//\n// reader functions\n//\n\nfunction tokenize(const Str: string) : TStringList;\nvar\n    RE       : TRegExpr;\n    Tokens   : TStringList;\nbegin\n    RE := TRegExpr.Create;\n    RE.Expression := '[\\s,]*(~@|[\\[\\]{}()''`~^@]|\"(([\\\\].|[^\\\\\"])*)\"?|;[^\\r\\n]*|[^\\s\\[\\]{}()''\"`@,;]+)';\n    Tokens := TStringList.Create;\n    if RE.Exec(Str) then\n    begin\n        repeat\n            if RE.Match[1][1] <> ';' then\n                Tokens.Add(RE.Match[1]);\n        until not RE.ExecNext;\n    end;\n    RE.Free;\n\n    tokenize := Tokens;\nend;\n\n\nfunction read_atom(Reader : TReader) : TMal;\nvar\n    RE     : TRegExpr;\n    Token  : string;\n    Str    : string;\nbegin\n    RE := TRegExpr.Create;\n    RE.Expression := '(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^(\"([\\\\].|[^\\\\\"])*)\"$|^(\\\".*)$|:(.*)|(^[^\\\"]*$)';\n    Token := Reader.Next();\n    //WriteLn('token: ' + Token);\n    if RE.Exec(Token) then\n    begin\n        if RE.Match[1] <> '' then\n            read_atom := TMalInt.Create(StrToInt(RE.Match[1]))\n        else if RE.Match[2] <> '' then\n            // TODO\n            read_atom := TMalNil.Create\n        else if RE.Match[3] <> '' then\n            read_atom := TMalNil.Create\n        else if RE.Match[4] <> '' then\n            read_atom := TMalTrue.Create\n        else if RE.Match[5] <> '' then\n            read_atom := TMalFalse.Create\n        else if RE.Match[6] <> '' then\n        begin\n            Str := copy(Token, 2, Length(Token)-2);\n            Str := StringReplace(Str, '\\\\', #127, [rfReplaceAll]);\n            Str := StringReplace(Str, '\\\"', '\"',  [rfReplaceAll]);\n            Str := StringReplace(Str, '\\n', #10,  [rfReplaceAll]);\n            Str := StringReplace(Str, #127, '\\',  [rfReplaceAll]);\n            read_atom := TMalString.Create(Str)\n        end\n        else if RE.Match[8] <> '' then\n            raise Exception.Create('expected ''\"'', got EOF')\n        else if RE.Match[9] <> '' then\n            read_atom := TMalString.Create(#127 + RE.Match[9])\n        else if RE.Match[10] <> '' then\n            read_atom := TMalSymbol.Create(Token);\n    end\n    else\n    begin\n        RE.Free;\n        raise Exception.Create('Invalid token in read_atom');\n    end;\n    RE.Free;\nend;\n\n// Forward declaration since read_seq calls it\nfunction read_form(Reader : TReader) : TMal; forward;\n\nfunction read_seq(Reader : TReader; start: string; last: string) : TMalArray;\nvar\n    Token : string;\n    Ast   : TMalArray;\nbegin\n    SetLength(Ast, 0);\n\n    Token := Reader.Next();\n    if Token <> start then\n        raise Exception.Create('expected ''' + start + '''');\n\n    Token := Reader.Peek();\n    while Token <> last do\n    begin\n        if Token = #0 then\n            raise Exception.Create('expected ''' + last + ''', got EOF');\n        SetLength(Ast, Length(Ast)+1);\n        Ast[Length(Ast)-1] := read_form(Reader);\n        Token := Reader.Peek();\n    end;\n\n    Token := Reader.Next();\n    read_seq := Ast;\nend;\n\nfunction read_form(Reader : TReader) : TMal;\nvar\n    Token : string;\n    Meta  : TMal;\nbegin\n    Token := Reader.Peek();\n    case Token of\n    // reader macros/transforms\n    '''':\n    begin\n        Reader.Next();\n        read_form := _list(TMalSymbol.Create('quote'),\n                           read_form(Reader));\n    end;\n    '`':\n    begin\n        Reader.Next();\n        read_form := _list(TMalSymbol.Create('quasiquote'),\n                           read_form(Reader));\n    end;\n    '~':\n    begin\n        Reader.Next();\n        read_form := _list(TMalSymbol.Create('unquote'),\n                           read_form(Reader));\n    end;\n    '~@':\n    begin\n        Reader.Next();\n        read_form := _list(TMalSymbol.Create('splice-unquote'),\n                           read_form(Reader));\n    end;\n    '^':\n    begin\n        Reader.Next();\n        Meta := read_form(Reader);\n        read_form := _list(TMalSymbol.Create('with-meta'),\n                           read_form(Reader),\n                           Meta);\n    end;\n    '@':\n    begin\n        Reader.Next();\n        read_form := _list(TMalSymbol.Create('deref'), read_form(Reader));\n    end;\n\n    // list\n    ')': raise Exception.Create('unexpected '')''');\n    '(': read_form := TMalList.Create(read_seq(Reader, '(', ')'));\n\n    // vector\n    ']': raise Exception.Create('unexpected '']''');\n    '[': read_form := TMalVector.Create(read_seq(Reader, '[', ']'));\n\n    // hash-map\n    '}': raise Exception.Create('unexpected ''}''');\n    '{': read_form := TMalHashMap.Create(read_seq(Reader, '{', '}'));\n    else\n        read_form := read_atom(Reader);\n    end;\nend;\n\n\nfunction read_str(const Str: string): TMal;\nvar\n    Tokens   : TStringList;\n    //Dict     : TObjectDictionary;\nbegin\n    Tokens := tokenize(Str);\n    // TODO: check for empty list\n    read_str := read_form(TReader.Create(Tokens));\nend;\n\nend.\n"
  },
  {
    "path": "impls/objpascal/regexpr/Source/RegExpr.pas",
    "content": "unit RegExpr;\r\n\r\n{\r\n     TRegExpr class library\r\n     Delphi Regular Expressions\r\n\r\n Copyright (c) 1999-2004 Andrey V. Sorokin, St.Petersburg, Russia\r\n\r\n You may use this software in any kind of development,\r\n including comercial, redistribute, and modify it freely,\r\n under the following restrictions :\r\n 1. This software is provided as it is, without any kind of\r\n    warranty given. Use it at Your own risk.The author is not\r\n    responsible for any consequences of use of this software.\r\n 2. The origin of this software may not be mispresented, You\r\n    must not claim that You wrote the original software. If\r\n    You use this software in any kind of product, it would be\r\n    appreciated that there in a information box, or in the\r\n    documentation would be an acknowledgement like\r\n\r\n     Partial Copyright (c) 2004 Andrey V. Sorokin\r\n                                http://RegExpStudio.com\r\n                                mailto:anso@mail.ru\r\n\r\n 3. You may not have any income from distributing this source\r\n    (or altered version of it) to other developers. When You\r\n    use this product in a comercial package, the source may\r\n    not be charged seperatly.\r\n 4. Altered versions must be plainly marked as such, and must\r\n    not be misrepresented as being the original software.\r\n 5. RegExp Studio application and all the visual components as \r\n    well as documentation is not part of the TRegExpr library \r\n    and is not free for usage.\r\n\r\n                                    mailto:anso@mail.ru\r\n                                    http://RegExpStudio.com\r\n                                    http://anso.da.ru/\r\n}\r\n\r\ninterface\r\n\r\n// ======== Determine compiler\r\n{$IFDEF VER80} Sorry, TRegExpr is for 32-bits Delphi only. Delphi 1 is not supported (and whos really care today?!). {$ENDIF}\r\n{$IFDEF VER90} {$DEFINE D2} {$ENDIF} // D2\r\n{$IFDEF VER93} {$DEFINE D2} {$ENDIF} // CPPB 1\r\n{$IFDEF VER100} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D3\r\n{$IFDEF VER110} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // CPPB 3\r\n{$IFDEF VER120} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D4\r\n{$IFDEF VER130} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D5\r\n{$IFDEF VER140} {$DEFINE D6} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D6\r\n{$IFDEF VER150} {$DEFINE D7} {$DEFINE D6} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D7\r\n\r\n// ======== Define base compiler options\r\n{$BOOLEVAL OFF}\r\n{$EXTENDEDSYNTAX ON}\r\n{$LONGSTRINGS ON}\r\n{$OPTIMIZATION ON}\r\n{$IFDEF D6}\r\n  {$WARN SYMBOL_PLATFORM OFF} // Suppress .Net warnings\r\n{$ENDIF}\r\n{$IFDEF D7}\r\n  {$WARN UNSAFE_CAST OFF} // Suppress .Net warnings\r\n  {$WARN UNSAFE_TYPE OFF} // Suppress .Net warnings\r\n  {$WARN UNSAFE_CODE OFF} // Suppress .Net warnings\r\n{$ENDIF}\r\n{$IFDEF FPC}\r\n {$MODE DELPHI} // Delphi-compatible mode in FreePascal\r\n{$ENDIF}\r\n\r\n// ======== Define options for TRegExpr engine\r\n{.$DEFINE UniCode} // Unicode support\r\n{$DEFINE RegExpPCodeDump} // p-code dumping (see Dump method)\r\n{$IFNDEF FPC} // the option is not supported in FreePascal\r\n {$DEFINE reRealExceptionAddr} // exceptions will point to appropriate source line, not to Error procedure\r\n{$ENDIF}\r\n{$DEFINE ComplexBraces} // support braces in complex cases\r\n{$IFNDEF UniCode} // the option applicable only for non-UniCode mode\r\n {$DEFINE UseSetOfChar} // Significant optimization by using set of char\r\n{$ENDIF}\r\n{$IFDEF UseSetOfChar}\r\n {$DEFINE UseFirstCharSet} // Fast skip between matches for r.e. that starts with determined set of chars\r\n{$ENDIF}\r\n\r\n// ======== Define Pascal-language options\r\n// Define 'UseAsserts' option (do not edit this definitions).\r\n// Asserts used to catch 'strange bugs' in TRegExpr implementation (when something goes\r\n// completely wrong). You can swith asserts on/off with help of {$C+}/{$C-} compiler options.\r\n{$IFDEF D3} {$DEFINE UseAsserts} {$ENDIF}\r\n{$IFDEF FPC} {$DEFINE UseAsserts} {$ENDIF}\r\n\r\n// Define 'use subroutine parameters default values' option (do not edit this definition).\r\n{$IFDEF D4} {$DEFINE DefParam} {$ENDIF}\r\n\r\n// Define 'OverMeth' options, to use method overloading (do not edit this definitions).\r\n{$IFDEF D5} {$DEFINE OverMeth} {$ENDIF}\r\n{$IFDEF FPC} {$DEFINE OverMeth} {$ENDIF}\r\n\r\nuses\r\n Classes,  // TStrings in Split method\r\n SysUtils; // Exception\r\n\r\ntype\r\n {$IFDEF UniCode}\r\n PRegExprChar = PWideChar;\r\n RegExprString = WideString;\r\n REChar = WideChar;\r\n {$ELSE}\r\n PRegExprChar = PChar;\r\n RegExprString = AnsiString; //###0.952 was string\r\n REChar = Char;\r\n {$ENDIF}\r\n TREOp = REChar; // internal p-code type //###0.933\r\n PREOp = ^TREOp;\r\n TRENextOff = integer; // internal Next \"pointer\" (offset to current p-code) //###0.933\r\n PRENextOff = ^TRENextOff; // used for extracting Next \"pointers\" from compiled r.e. //###0.933\r\n TREBracesArg = integer; // type of {m,n} arguments\r\n PREBracesArg = ^TREBracesArg;\r\n\r\nconst\r\n REOpSz = SizeOf (TREOp) div SizeOf (REChar); // size of p-code in RegExprString units\r\n RENextOffSz = SizeOf (TRENextOff) div SizeOf (REChar); // size of Next 'pointer' -\"-\r\n REBracesArgSz = SizeOf (TREBracesArg) div SizeOf (REChar); // size of BRACES arguments -\"-\r\n\r\ntype\r\n TRegExprInvertCaseFunction = function (const Ch : REChar) : REChar\r\n                               of object;\r\n\r\nconst\r\n  EscChar = '\\'; // 'Escape'-char ('\\' in common r.e.) used for escaping metachars (\\w, \\d etc).\r\n  RegExprModifierI : boolean = False;    // default value for ModifierI\r\n  RegExprModifierR : boolean = True;     // default value for ModifierR\r\n  RegExprModifierS : boolean = True;     // default value for ModifierS\r\n  RegExprModifierG : boolean = True;     // default value for ModifierG\r\n  RegExprModifierM : boolean = False;    // default value for ModifierM\r\n  RegExprModifierX : boolean = False;    // default value for ModifierX\r\n  RegExprSpaceChars : RegExprString =    // default value for SpaceChars\r\n  ' '#$9#$A#$D#$C;\r\n  RegExprWordChars : RegExprString =     // default value for WordChars\r\n    '0123456789' //###0.940\r\n  + 'abcdefghijklmnopqrstuvwxyz'\r\n  + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';\r\n  RegExprLineSeparators : RegExprString =// default value for LineSeparators\r\n   #$d#$a{$IFDEF UniCode}+#$b#$c#$2028#$2029#$85{$ENDIF}; //###0.947\r\n  RegExprLinePairedSeparator : RegExprString =// default value for LinePairedSeparator\r\n   #$d#$a;\r\n  { if You need Unix-styled line separators (only \\n), then use:\r\n  RegExprLineSeparators = #$a;\r\n  RegExprLinePairedSeparator = '';\r\n  }\r\n\r\n\r\nconst\r\n NSUBEXP = 15; // max number of subexpression //###0.929\r\n // Cannot be more than NSUBEXPMAX\r\n // Be carefull - don't use values which overflow CLOSE opcode\r\n // (in this case you'll get compiler erorr).\r\n // Big NSUBEXP will cause more slow work and more stack required\r\n NSUBEXPMAX = 255; // Max possible value for NSUBEXP. //###0.945\r\n // Don't change it! It's defined by internal TRegExpr design.\r\n\r\n MaxBracesArg = $7FFFFFFF - 1; // max value for {n,m} arguments //###0.933\r\n\r\n {$IFDEF ComplexBraces}\r\n LoopStackMax = 10; // max depth of loops stack //###0.925\r\n {$ENDIF}\r\n\r\n TinySetLen = 3;\r\n // if range includes more then TinySetLen chars, //###0.934\r\n // then use full (32 bytes) ANYOFFULL instead of ANYOF[BUT]TINYSET\r\n // !!! Attension ! If you change TinySetLen, you must\r\n // change code marked as \"//!!!TinySet\"\r\n\r\n\r\ntype\r\n\r\n{$IFDEF UseSetOfChar}\r\n PSetOfREChar = ^TSetOfREChar;\r\n TSetOfREChar = set of REChar;\r\n{$ENDIF}\r\n\r\n TRegExpr = class;\r\n\r\n TRegExprReplaceFunction = function (ARegExpr : TRegExpr): string\r\n                               of object;\r\n\r\n TRegExpr = class\r\n   private\r\n    startp : array [0 .. NSUBEXP - 1] of PRegExprChar; // founded expr starting points\r\n    endp : array [0 .. NSUBEXP - 1] of PRegExprChar; // founded expr end points\r\n\r\n    {$IFDEF ComplexBraces}\r\n    LoopStack : array [1 .. LoopStackMax] of integer; // state before entering loop\r\n    LoopStackIdx : integer; // 0 - out of all loops\r\n    {$ENDIF}\r\n\r\n    // The \"internal use only\" fields to pass info from compile\r\n    // to execute that permits the execute phase to run lots faster on\r\n    // simple cases.\r\n    regstart : REChar; // char that must begin a match; '\\0' if none obvious\r\n    reganch : REChar; // is the match anchored (at beginning-of-line only)?\r\n    regmust : PRegExprChar; // string (pointer into program) that match must include, or nil\r\n    regmlen : integer; // length of regmust string\r\n    // Regstart and reganch permit very fast decisions on suitable starting points\r\n    // for a match, cutting down the work a lot.  Regmust permits fast rejection\r\n    // of lines that cannot possibly match.  The regmust tests are costly enough\r\n    // that regcomp() supplies a regmust only if the r.e. contains something\r\n    // potentially expensive (at present, the only such thing detected is * or +\r\n    // at the start of the r.e., which can involve a lot of backup).  Regmlen is\r\n    // supplied because the test in regexec() needs it and regcomp() is computing\r\n    // it anyway.\r\n    {$IFDEF UseFirstCharSet} //###0.929\r\n    FirstCharSet : TSetOfREChar;\r\n    {$ENDIF}\r\n\r\n    // work variables for Exec's routins - save stack in recursion}\r\n    reginput : PRegExprChar; // String-input pointer.\r\n    fInputStart : PRegExprChar; // Pointer to first char of input string.\r\n    fInputEnd : PRegExprChar; // Pointer to char AFTER last char of input string\r\n\r\n    // work variables for compiler's routines\r\n    regparse : PRegExprChar;  // Input-scan pointer.\r\n    regnpar : integer; // count.\r\n    regdummy : char;\r\n    regcode : PRegExprChar;   // Code-emit pointer; @regdummy = don't.\r\n    regsize : integer; // Code size.\r\n\r\n    regexpbeg : PRegExprChar; // only for error handling. Contains\r\n    // pointer to beginning of r.e. while compiling\r\n    fExprIsCompiled : boolean; // true if r.e. successfully compiled\r\n\r\n    // programm is essentially a linear encoding\r\n    // of a nondeterministic finite-state machine (aka syntax charts or\r\n    // \"railroad normal form\" in parsing technology).  Each node is an opcode\r\n    // plus a \"next\" pointer, possibly plus an operand.  \"Next\" pointers of\r\n    // all nodes except BRANCH implement concatenation; a \"next\" pointer with\r\n    // a BRANCH on both ends of it is connecting two alternatives.  (Here we\r\n    // have one of the subtle syntax dependencies:  an individual BRANCH (as\r\n    // opposed to a collection of them) is never concatenated with anything\r\n    // because of operator precedence.)  The operand of some types of node is\r\n    // a literal string; for others, it is a node leading into a sub-FSM.  In\r\n    // particular, the operand of a BRANCH node is the first node of the branch.\r\n    // (NB this is *not* a tree structure:  the tail of the branch connects\r\n    // to the thing following the set of BRANCHes.)  The opcodes are:\r\n    programm : PRegExprChar; // Unwarranted chumminess with compiler.\r\n\r\n    fExpression : PRegExprChar; // source of compiled r.e.\r\n    fInputString : PRegExprChar; // input string\r\n\r\n    fLastError : integer; // see Error, LastError\r\n\r\n    fModifiers : integer; // modifiers\r\n    fCompModifiers : integer; // compiler's copy of modifiers\r\n    fProgModifiers : integer; // modifiers values from last programm compilation\r\n\r\n    fSpaceChars : RegExprString; //###0.927\r\n    fWordChars : RegExprString; //###0.929\r\n    fInvertCase : TRegExprInvertCaseFunction; //###0.927\r\n\r\n    fLineSeparators : RegExprString; //###0.941\r\n    fLinePairedSeparatorAssigned : boolean;\r\n    fLinePairedSeparatorHead,\r\n    fLinePairedSeparatorTail : REChar;\r\n    {$IFNDEF UniCode}\r\n    fLineSeparatorsSet : set of REChar;\r\n    {$ENDIF}\r\n\r\n    procedure InvalidateProgramm;\r\n    // Mark programm as have to be [re]compiled\r\n\r\n    function IsProgrammOk : boolean; //###0.941\r\n    // Check if we can use precompiled r.e. or\r\n    // [re]compile it if something changed\r\n\r\n    function GetExpression : RegExprString;\r\n    procedure SetExpression (const s : RegExprString);\r\n\r\n    function GetModifierStr : RegExprString;\r\n    class function ParseModifiersStr (const AModifiers : RegExprString;\r\n      var AModifiersInt : integer) : boolean; //###0.941 class function now\r\n    // Parse AModifiers string and return true and set AModifiersInt\r\n    // if it's in format 'ismxrg-ismxrg'.\r\n    procedure SetModifierStr (const AModifiers : RegExprString);\r\n\r\n    function GetModifier (AIndex : integer) : boolean;\r\n    procedure SetModifier (AIndex : integer; ASet : boolean);\r\n\r\n    procedure Error (AErrorID : integer); virtual; // error handler.\r\n    // Default handler raise exception ERegExpr with\r\n    // Message = ErrorMsg (AErrorID), ErrorCode = AErrorID\r\n    // and CompilerErrorPos = value of property CompilerErrorPos.\r\n\r\n\r\n    {==================== Compiler section ===================}\r\n    function CompileRegExpr (exp : PRegExprChar) : boolean;\r\n    // compile a regular expression into internal code\r\n\r\n    procedure Tail (p : PRegExprChar; val : PRegExprChar);\r\n    // set the next-pointer at the end of a node chain\r\n\r\n    procedure OpTail (p : PRegExprChar; val : PRegExprChar);\r\n    // regoptail - regtail on operand of first argument; nop if operandless\r\n\r\n    function EmitNode (op : TREOp) : PRegExprChar;\r\n    // regnode - emit a node, return location\r\n\r\n    procedure EmitC (b : REChar);\r\n    // emit (if appropriate) a byte of code\r\n\r\n    procedure InsertOperator (op : TREOp; opnd : PRegExprChar; sz : integer); //###0.90\r\n    // insert an operator in front of already-emitted operand\r\n    // Means relocating the operand.\r\n\r\n    function ParseReg (paren : integer; var flagp : integer) : PRegExprChar;\r\n    // regular expression, i.e. main body or parenthesized thing\r\n\r\n    function ParseBranch (var flagp : integer) : PRegExprChar;\r\n    // one alternative of an | operator\r\n\r\n    function ParsePiece (var flagp : integer) : PRegExprChar;\r\n    // something followed by possible [*+?]\r\n\r\n    function ParseAtom (var flagp : integer) : PRegExprChar;\r\n    // the lowest level\r\n\r\n    function GetCompilerErrorPos : integer;\r\n    // current pos in r.e. - for error hanling\r\n\r\n    {$IFDEF UseFirstCharSet} //###0.929\r\n    procedure FillFirstCharSet (prog : PRegExprChar);\r\n    {$ENDIF}\r\n\r\n    {===================== Mathing section ===================}\r\n    function regrepeat (p : PRegExprChar; AMax : integer) : integer;\r\n    // repeatedly match something simple, report how many\r\n\r\n    function regnext (p : PRegExprChar) : PRegExprChar;\r\n    // dig the \"next\" pointer out of a node\r\n\r\n    function MatchPrim (prog : PRegExprChar) : boolean;\r\n    // recursively matching routine\r\n\r\n    function ExecPrim (AOffset: integer) : boolean;\r\n    // Exec for stored InputString\r\n\r\n    {$IFDEF RegExpPCodeDump}\r\n    function DumpOp (op : REChar) : RegExprString;\r\n    {$ENDIF}\r\n\r\n    function GetSubExprMatchCount : integer;\r\n    function GetMatchPos (Idx : integer) : integer;\r\n    function GetMatchLen (Idx : integer) : integer;\r\n    function GetMatch (Idx : integer) : RegExprString;\r\n\r\n    function GetInputString : RegExprString;\r\n    procedure SetInputString (const AInputString : RegExprString);\r\n\r\n    {$IFNDEF UseSetOfChar}\r\n    function StrScanCI (s : PRegExprChar; ch : REChar) : PRegExprChar; //###0.928\r\n    {$ENDIF}\r\n\r\n    procedure SetLineSeparators (const AStr : RegExprString);\r\n    procedure SetLinePairedSeparator (const AStr : RegExprString);\r\n    function GetLinePairedSeparator : RegExprString;\r\n\r\n   public\r\n    constructor Create;\r\n    destructor Destroy; override;\r\n\r\n    class function VersionMajor : integer; //###0.944\r\n    class function VersionMinor : integer; //###0.944\r\n\r\n    property Expression : RegExprString read GetExpression write SetExpression;\r\n    // Regular expression.\r\n    // For optimization, TRegExpr will automatically compiles it into 'P-code'\r\n    // (You can see it with help of Dump method) and stores in internal\r\n    // structures. Real [re]compilation occures only when it really needed -\r\n    // while calling Exec[Next], Substitute, Dump, etc\r\n    // and only if Expression or other P-code affected properties was changed\r\n    // after last [re]compilation.\r\n    // If any errors while [re]compilation occures, Error method is called\r\n    // (by default Error raises exception - see below)\r\n\r\n    property ModifierStr : RegExprString read GetModifierStr write SetModifierStr;\r\n    // Set/get default values of r.e.syntax modifiers. Modifiers in\r\n    // r.e. (?ismx-ismx) will replace this default values.\r\n    // If you try to set unsupported modifier, Error will be called\r\n    // (by defaul Error raises exception ERegExpr).\r\n\r\n    property ModifierI : boolean index 1 read GetModifier write SetModifier;\r\n    // Modifier /i - caseinsensitive, initialized from RegExprModifierI\r\n\r\n    property ModifierR : boolean index 2 read GetModifier write SetModifier;\r\n    // Modifier /r - use r.e.syntax extended for russian,\r\n    // (was property ExtSyntaxEnabled in previous versions)\r\n    // If true, then -  additional include russian letter '',\r\n    // -  additional include '', and - include all russian symbols.\r\n    // You have to turn it off if it may interfere with you national alphabet.\r\n    // , initialized from RegExprModifierR\r\n\r\n    property ModifierS : boolean index 3 read GetModifier write SetModifier;\r\n    // Modifier /s - '.' works as any char (else as [^\\n]),\r\n    // , initialized from RegExprModifierS\r\n\r\n    property ModifierG : boolean index 4 read GetModifier write SetModifier;\r\n    // Switching off modifier /g switchs all operators in\r\n    // non-greedy style, so if ModifierG = False, then\r\n    // all '*' works as '*?', all '+' as '+?' and so on.\r\n    // , initialized from RegExprModifierG\r\n\r\n    property ModifierM : boolean index 5 read GetModifier write SetModifier;\r\n    // Treat string as multiple lines. That is, change `^' and `$' from\r\n    // matching at only the very start or end of the string to the start\r\n    // or end of any line anywhere within the string.\r\n    // , initialized from RegExprModifierM\r\n\r\n    property ModifierX : boolean index 6 read GetModifier write SetModifier;\r\n    // Modifier /x - eXtended syntax, allow r.e. text formatting,\r\n    // see description in the help. Initialized from RegExprModifierX\r\n\r\n    function Exec (const AInputString : RegExprString) : boolean; {$IFDEF OverMeth} overload;\r\n    {$IFNDEF FPC} // I do not know why FreePascal cannot overload methods with empty param list\r\n    function Exec : boolean; overload; //###0.949\r\n    {$ENDIF}\r\n    function Exec (AOffset: integer) : boolean; overload; //###0.949\r\n    {$ENDIF}\r\n    // match a programm against a string AInputString\r\n    // !!! Exec store AInputString into InputString property\r\n    // For Delphi 5 and higher available overloaded versions - first without\r\n    // parameter (uses already assigned to InputString property value)\r\n    // and second that has integer parameter and is same as ExecPos\r\n\r\n    function ExecNext : boolean;\r\n    // find next match:\r\n    //    ExecNext;\r\n    // works same as\r\n    //    if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)\r\n    //     else ExecPos (MatchPos [0] + MatchLen [0]);\r\n    // but it's more simpler !\r\n    // Raises exception if used without preceeding SUCCESSFUL call to\r\n    // Exec* (Exec, ExecPos, ExecNext). So You always must use something like\r\n    // if Exec (InputString) then repeat { proceed results} until not ExecNext;\r\n\r\n    function ExecPos (AOffset: integer {$IFDEF DefParam}= 1{$ENDIF}) : boolean;\r\n    // find match for InputString starting from AOffset position\r\n    // (AOffset=1 - first char of InputString)\r\n\r\n    property InputString : RegExprString read GetInputString write SetInputString;\r\n    // returns current input string (from last Exec call or last assign\r\n    // to this property).\r\n    // Any assignment to this property clear Match* properties !\r\n\r\n    function Substitute (const ATemplate : RegExprString) : RegExprString;\r\n    // Returns ATemplate with '$&' or '$0' replaced by whole r.e.\r\n    // occurence and '$n' replaced by occurence of subexpression #n.\r\n    // Since v.0.929 '$' used instead of '\\' (for future extensions\r\n    // and for more Perl-compatibility) and accept more then one digit.\r\n    // If you want place into template raw '$' or '\\', use prefix '\\'\r\n    // Example: '1\\$ is $2\\\\rub\\\\' -> '1$ is <Match[2]>\\rub\\'\r\n    // If you want to place raw digit after '$n' you must delimit\r\n    // n with curly braces '{}'.\r\n    // Example: 'a$12bc' -> 'a<Match[12]>bc'\r\n    // 'a${1}2bc' -> 'a<Match[1]>2bc'.\r\n\r\n    procedure Split (AInputStr : RegExprString; APieces : TStrings);\r\n    // Split AInputStr into APieces by r.e. occurencies\r\n    // Internally calls Exec[Next]\r\n\r\n    function Replace (AInputStr : RegExprString;\r\n      const AReplaceStr : RegExprString;\r\n      AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) //###0.946\r\n     : RegExprString; {$IFDEF OverMeth} overload;\r\n    function Replace (AInputStr : RegExprString;\r\n      AReplaceFunc : TRegExprReplaceFunction)\r\n     : RegExprString; overload;\r\n    {$ENDIF}\r\n    function ReplaceEx (AInputStr : RegExprString;\r\n      AReplaceFunc : TRegExprReplaceFunction)\r\n     : RegExprString;\r\n    // Returns AInputStr with r.e. occurencies replaced by AReplaceStr\r\n    // If AUseSubstitution is true, then AReplaceStr will be used\r\n    // as template for Substitution methods.\r\n    // For example:\r\n    //  Expression := '({-i}block|var)\\s*\\(\\s*([^ ]*)\\s*\\)\\s*';\r\n    //  Replace ('BLOCK( test1)', 'def \"$1\" value \"$2\"', True);\r\n    //   will return:  def 'BLOCK' value 'test1'\r\n    //  Replace ('BLOCK( test1)', 'def \"$1\" value \"$2\"')\r\n    //   will return:  def \"$1\" value \"$2\"\r\n    // Internally calls Exec[Next]\r\n    // Overloaded version and ReplaceEx operate with call-back function,\r\n    // so You can implement really complex functionality.\r\n\r\n    property SubExprMatchCount : integer read GetSubExprMatchCount;\r\n    // Number of subexpressions has been found in last Exec* call.\r\n    // If there are no subexpr. but whole expr was found (Exec* returned True),\r\n    // then SubExprMatchCount=0, if no subexpressions nor whole\r\n    // r.e. found (Exec* returned false) then SubExprMatchCount=-1.\r\n    // Note, that some subexpr. may be not found and for such\r\n    // subexpr. MathPos=MatchLen=-1 and Match=''.\r\n    // For example: Expression := '(1)?2(3)?';\r\n    //  Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'\r\n    //  Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'\r\n    //  Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'\r\n    //  Exec ('2'): SubExprMatchCount=0, Match[0]='2'\r\n    //  Exec ('7') - return False: SubExprMatchCount=-1\r\n\r\n    property MatchPos [Idx : integer] : integer read GetMatchPos;\r\n    // pos of entrance subexpr. #Idx into tested in last Exec*\r\n    // string. First subexpr. have Idx=1, last - MatchCount,\r\n    // whole r.e. have Idx=0.\r\n    // Returns -1 if in r.e. no such subexpr. or this subexpr.\r\n    // not found in input string.\r\n\r\n    property MatchLen [Idx : integer] : integer read GetMatchLen;\r\n    // len of entrance subexpr. #Idx r.e. into tested in last Exec*\r\n    // string. First subexpr. have Idx=1, last - MatchCount,\r\n    // whole r.e. have Idx=0.\r\n    // Returns -1 if in r.e. no such subexpr. or this subexpr.\r\n    // not found in input string.\r\n    // Remember - MatchLen may be 0 (if r.e. match empty string) !\r\n\r\n    property Match [Idx : integer] : RegExprString read GetMatch;\r\n    // == copy (InputString, MatchPos [Idx], MatchLen [Idx])\r\n    // Returns '' if in r.e. no such subexpr. or this subexpr.\r\n    // not found in input string.\r\n\r\n    function LastError : integer;\r\n    // Returns ID of last error, 0 if no errors (unusable if\r\n    // Error method raises exception) and clear internal status\r\n    // into 0 (no errors).\r\n\r\n    function ErrorMsg (AErrorID : integer) : RegExprString; virtual;\r\n    // Returns Error message for error with ID = AErrorID.\r\n\r\n    property CompilerErrorPos : integer read GetCompilerErrorPos;\r\n    // Returns pos in r.e. there compiler stopped.\r\n    // Usefull for error diagnostics\r\n\r\n    property SpaceChars : RegExprString read fSpaceChars write fSpaceChars; //###0.927\r\n    // Contains chars, treated as /s (initially filled with RegExprSpaceChars\r\n    // global constant)\r\n\r\n    property WordChars : RegExprString read fWordChars write fWordChars; //###0.929\r\n    // Contains chars, treated as /w (initially filled with RegExprWordChars\r\n    // global constant)\r\n\r\n    property LineSeparators : RegExprString read fLineSeparators write SetLineSeparators; //###0.941\r\n    // line separators (like \\n in Unix)\r\n\r\n    property LinePairedSeparator : RegExprString read GetLinePairedSeparator write SetLinePairedSeparator; //###0.941\r\n    // paired line separator (like \\r\\n in DOS and Windows).\r\n    // must contain exactly two chars or no chars at all\r\n\r\n    class function InvertCaseFunction  (const Ch : REChar) : REChar;\r\n    // Converts Ch into upper case if it in lower case or in lower\r\n    // if it in upper (uses current system local setings)\r\n\r\n    property InvertCase : TRegExprInvertCaseFunction read fInvertCase write fInvertCase; //##0.935\r\n    // Set this property if you want to override case-insensitive functionality.\r\n    // Create set it to RegExprInvertCaseFunction (InvertCaseFunction by default)\r\n\r\n    procedure Compile; //###0.941\r\n    // [Re]compile r.e. Usefull for example for GUI r.e. editors (to check\r\n    // all properties validity).\r\n\r\n    {$IFDEF RegExpPCodeDump}\r\n    function Dump : RegExprString;\r\n    // dump a compiled regexp in vaguely comprehensible form\r\n    {$ENDIF}\r\n  end;\r\n\r\n ERegExpr = class (Exception)\r\n   public\r\n    ErrorCode : integer;\r\n    CompilerErrorPos : integer;\r\n  end;\r\n\r\nconst\r\n  RegExprInvertCaseFunction : TRegExprInvertCaseFunction = {$IFDEF FPC} nil {$ELSE} TRegExpr.InvertCaseFunction{$ENDIF};\r\n  // defaul for InvertCase property\r\n\r\nfunction ExecRegExpr (const ARegExpr, AInputStr : RegExprString) : boolean;\r\n// true if string AInputString match regular expression ARegExpr\r\n// ! will raise exeption if syntax errors in ARegExpr\r\n\r\nprocedure SplitRegExpr (const ARegExpr, AInputStr : RegExprString; APieces : TStrings);\r\n// Split AInputStr into APieces by r.e. ARegExpr occurencies\r\n\r\nfunction ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : RegExprString;\r\n      AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString; //###0.947\r\n// Returns AInputStr with r.e. occurencies replaced by AReplaceStr\r\n// If AUseSubstitution is true, then AReplaceStr will be used\r\n// as template for Substitution methods.\r\n// For example:\r\n//  ReplaceRegExpr ('({-i}block|var)\\s*\\(\\s*([^ ]*)\\s*\\)\\s*',\r\n//   'BLOCK( test1)', 'def \"$1\" value \"$2\"', True)\r\n//  will return:  def 'BLOCK' value 'test1'\r\n//  ReplaceRegExpr ('({-i}block|var)\\s*\\(\\s*([^ ]*)\\s*\\)\\s*',\r\n//   'BLOCK( test1)', 'def \"$1\" value \"$2\"')\r\n//   will return:  def \"$1\" value \"$2\"\r\n\r\nfunction QuoteRegExprMetaChars (const AStr : RegExprString) : RegExprString;\r\n// Replace all metachars with its safe representation,\r\n// for example 'abc$cd.(' converts into 'abc\\$cd\\.\\('\r\n// This function usefull for r.e. autogeneration from\r\n// user input\r\n\r\nfunction RegExprSubExpressions (const ARegExpr : string;\r\n ASubExprs : TStrings; AExtendedSyntax : boolean{$IFDEF DefParam}= False{$ENDIF}) : integer;\r\n// Makes list of subexpressions found in ARegExpr r.e.\r\n// In ASubExps every item represent subexpression,\r\n// from first to last, in format:\r\n//  String - subexpression text (without '()')\r\n//  low word of Object - starting position in ARegExpr, including '('\r\n//   if exists! (first position is 1)\r\n//  high word of Object - length, including starting '(' and ending ')'\r\n//   if exist!\r\n// AExtendedSyntax - must be True if modifier /m will be On while\r\n// using the r.e.\r\n// Usefull for GUI editors of r.e. etc (You can find example of using\r\n// in TestRExp.dpr project)\r\n// Returns\r\n//  0      Success. No unbalanced brackets was found;\r\n//  -1     There are not enough closing brackets ')';\r\n//  -(n+1) At position n was found opening '[' without  //###0.942\r\n//         corresponding closing ']';\r\n//  n      At position n was found closing bracket ')' without\r\n//         corresponding opening '('.\r\n// If Result <> 0, then ASubExpr can contain empty items or illegal ones\r\n\r\n\r\nimplementation\r\n\r\n{$IFNDEF FPC}\r\nuses\r\n Windows; // CharUpper/Lower\r\n{$ENDIF}\r\n\r\nconst\r\n TRegExprVersionMajor : integer = 0;\r\n TRegExprVersionMinor : integer = 952;\r\n // TRegExpr.VersionMajor/Minor return values of this constants\r\n\r\n MaskModI = 1;  // modifier /i bit in fModifiers\r\n MaskModR = 2;  // -\"- /r\r\n MaskModS = 4;  // -\"- /s\r\n MaskModG = 8;  // -\"- /g\r\n MaskModM = 16; // -\"- /m\r\n MaskModX = 32; // -\"- /x\r\n\r\n {$IFDEF UniCode}\r\n XIgnoredChars = ' '#9#$d#$a;\r\n {$ELSE}\r\n XIgnoredChars = [' ', #9, #$d, #$a];\r\n {$ENDIF}\r\n\r\n{=============================================================}\r\n{=================== WideString functions ====================}\r\n{=============================================================}\r\n\r\n{$IFDEF UniCode}\r\n\r\nfunction StrPCopy (Dest: PRegExprChar; const Source: RegExprString): PRegExprChar;\r\n var\r\n  i, Len : Integer;\r\n begin\r\n  Len := length (Source); //###0.932\r\n  for i := 1 to Len do\r\n   Dest [i - 1] := Source [i];\r\n  Dest [Len] := #0;\r\n  Result := Dest;\r\n end; { of function StrPCopy\r\n--------------------------------------------------------------}\r\n\r\nfunction StrLCopy (Dest, Source: PRegExprChar; MaxLen: Cardinal): PRegExprChar;\r\n var i: Integer;\r\n begin\r\n  for i := 0 to MaxLen - 1 do\r\n   Dest [i] := Source [i];\r\n  Result := Dest;\r\n end; { of function StrLCopy\r\n--------------------------------------------------------------}\r\n\r\nfunction StrLen (Str: PRegExprChar): Cardinal;\r\n begin\r\n  Result:=0;\r\n  while Str [result] <> #0\r\n   do Inc (Result);\r\n end; { of function StrLen\r\n--------------------------------------------------------------}\r\n\r\nfunction StrPos (Str1, Str2: PRegExprChar): PRegExprChar;\r\n var n: Integer;\r\n begin\r\n  Result := nil;\r\n  n := Pos (RegExprString (Str2), RegExprString (Str1));\r\n  if n = 0\r\n   then EXIT;\r\n  Result := Str1 + n - 1;\r\n end; { of function StrPos\r\n--------------------------------------------------------------}\r\n\r\nfunction StrLComp (Str1, Str2: PRegExprChar; MaxLen: Cardinal): Integer;\r\n var S1, S2: RegExprString;\r\n begin\r\n  S1 := Str1;\r\n  S2 := Str2;\r\n  if Copy (S1, 1, MaxLen) > Copy (S2, 1, MaxLen)\r\n   then Result := 1\r\n   else\r\n    if Copy (S1, 1, MaxLen) < Copy (S2, 1, MaxLen)\r\n     then Result := -1\r\n     else Result := 0;\r\n end; { function StrLComp\r\n--------------------------------------------------------------}\r\n\r\nfunction StrScan (Str: PRegExprChar; Chr: WideChar): PRegExprChar;\r\n begin\r\n  Result := nil;\r\n  while (Str^ <> #0) and (Str^ <> Chr)\r\n   do Inc (Str);\r\n  if (Str^ <> #0)\r\n   then Result := Str;\r\n end; { of function StrScan\r\n--------------------------------------------------------------}\r\n\r\n{$ENDIF}\r\n\r\n\r\n{=============================================================}\r\n{===================== Global functions ======================}\r\n{=============================================================}\r\n\r\nfunction ExecRegExpr (const ARegExpr, AInputStr : RegExprString) : boolean;\r\n var r : TRegExpr;\r\n begin\r\n  r := TRegExpr.Create;\r\n  try\r\n    r.Expression := ARegExpr;\r\n    Result := r.Exec (AInputStr);\r\n    finally r.Free;\r\n   end;\r\n end; { of function ExecRegExpr\r\n--------------------------------------------------------------}\r\n\r\nprocedure SplitRegExpr (const ARegExpr, AInputStr : RegExprString; APieces : TStrings);\r\n var r : TRegExpr;\r\n begin\r\n  APieces.Clear;\r\n  r := TRegExpr.Create;\r\n  try\r\n    r.Expression := ARegExpr;\r\n    r.Split (AInputStr, APieces);\r\n    finally r.Free;\r\n   end;\r\n end; { of procedure SplitRegExpr\r\n--------------------------------------------------------------}\r\n\r\nfunction ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : RegExprString;\r\n      AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString;\r\n begin\r\n  with TRegExpr.Create do try\r\n    Expression := ARegExpr;\r\n    Result := Replace (AInputStr, AReplaceStr, AUseSubstitution);\r\n    finally Free;\r\n   end;\r\n end; { of function ReplaceRegExpr\r\n--------------------------------------------------------------}\r\n\r\nfunction QuoteRegExprMetaChars (const AStr : RegExprString) : RegExprString;\r\n const\r\n  RegExprMetaSet : RegExprString = '^$.[()|?+*'+EscChar+'{'\r\n  + ']}'; // - this last are additional to META.\r\n  // Very similar to META array, but slighly changed.\r\n  // !Any changes in META array must be synchronized with this set.\r\n var\r\n  i, i0, Len : integer;\r\n begin\r\n  Result := '';\r\n  Len := length (AStr);\r\n  i := 1;\r\n  i0 := i;\r\n  while i <= Len do begin\r\n    if Pos (AStr [i], RegExprMetaSet) > 0 then begin\r\n      Result := Result + System.Copy (AStr, i0, i - i0)\r\n                 + EscChar + AStr [i];\r\n      i0 := i + 1;\r\n     end;\r\n    inc (i);\r\n   end;\r\n  Result := Result + System.Copy (AStr, i0, MaxInt); // Tail\r\n end; { of function QuoteRegExprMetaChars\r\n--------------------------------------------------------------}\r\n\r\nfunction RegExprSubExpressions (const ARegExpr : string;\r\n ASubExprs : TStrings; AExtendedSyntax : boolean{$IFDEF DefParam}= False{$ENDIF}) : integer;\r\n type\r\n  TStackItemRec =  record //###0.945\r\n    SubExprIdx : integer;\r\n    StartPos : integer;\r\n   end;\r\n  TStackArray = packed array [0 .. NSUBEXPMAX - 1] of TStackItemRec;\r\n var\r\n  Len, SubExprLen : integer;\r\n  i, i0 : integer;\r\n  Modif : integer;\r\n  Stack : ^TStackArray; //###0.945\r\n  StackIdx, StackSz : integer;\r\n begin\r\n  Result := 0; // no unbalanced brackets found at this very moment\r\n\r\n  ASubExprs.Clear; // I don't think that adding to non empty list\r\n  // can be usefull, so I simplified algorithm to work only with empty list\r\n\r\n  Len := length (ARegExpr); // some optimization tricks\r\n\r\n  // first we have to calculate number of subexpression to reserve\r\n  // space in Stack array (may be we'll reserve more then need, but\r\n  // it's faster then memory reallocation during parsing)\r\n  StackSz := 1; // add 1 for entire r.e.\r\n  for i := 1 to Len do\r\n   if ARegExpr [i] = '('\r\n    then inc (StackSz);\r\n//  SetLength (Stack, StackSz); //###0.945\r\n  GetMem (Stack, SizeOf (TStackItemRec) * StackSz);\r\n  try\r\n\r\n  StackIdx := 0;\r\n  i := 1;\r\n  while (i <= Len) do begin\r\n    case ARegExpr [i] of\r\n      '(': begin\r\n        if (i < Len) and (ARegExpr [i + 1] = '?') then begin\r\n           // this is not subexpression, but comment or other\r\n           // Perl extension. We must check is it (?ismxrg-ismxrg)\r\n           // and change AExtendedSyntax if /x is changed.\r\n           inc (i, 2); // skip '(?'\r\n           i0 := i;\r\n           while (i <= Len) and (ARegExpr [i] <> ')')\r\n            do inc (i);\r\n           if i > Len\r\n            then Result := -1 // unbalansed '('\r\n            else\r\n             if TRegExpr.ParseModifiersStr (System.Copy (ARegExpr, i, i - i0), Modif)\r\n              then AExtendedSyntax := (Modif and MaskModX) <> 0;\r\n          end\r\n         else begin // subexpression starts\r\n           ASubExprs.Add (''); // just reserve space\r\n           with Stack [StackIdx] do begin\r\n             SubExprIdx := ASubExprs.Count - 1;\r\n             StartPos := i;\r\n            end;\r\n           inc (StackIdx);\r\n          end;\r\n       end;\r\n      ')': begin\r\n        if StackIdx = 0\r\n         then Result := i // unbalanced ')'\r\n         else begin\r\n           dec (StackIdx);\r\n           with Stack [StackIdx] do begin\r\n             SubExprLen := i - StartPos + 1;\r\n             ASubExprs.Objects [SubExprIdx] :=\r\n              TObject (StartPos or (SubExprLen ShL 16));\r\n             ASubExprs [SubExprIdx] := System.Copy (\r\n              ARegExpr, StartPos + 1, SubExprLen - 2); // add without brackets\r\n            end;\r\n          end;\r\n       end;\r\n      EscChar: inc (i); // skip quoted symbol\r\n      '[': begin\r\n        // we have to skip character ranges at once, because they can\r\n        // contain '#', and '#' in it must NOT be recognized as eXtended\r\n        // comment beginning!\r\n        i0 := i;\r\n        inc (i);\r\n        if ARegExpr [i] = ']' // cannot be 'emty' ranges - this interpretes\r\n         then inc (i);        // as ']' by itself\r\n        while (i <= Len) and (ARegExpr [i] <> ']') do\r\n         if ARegExpr [i] = EscChar //###0.942\r\n          then inc (i, 2) // skip 'escaped' char to prevent stopping at '\\]'\r\n          else inc (i);\r\n        if (i > Len) or (ARegExpr [i] <> ']') //###0.942\r\n         then Result := - (i0 + 1); // unbalansed '[' //###0.942\r\n       end;\r\n      '#': if AExtendedSyntax then begin\r\n        // skip eXtended comments\r\n        while (i <= Len) and (ARegExpr [i] <> #$d) and (ARegExpr [i] <> #$a)\r\n         // do not use [#$d, #$a] due to UniCode compatibility\r\n         do inc (i);\r\n        while (i + 1 <= Len) and ((ARegExpr [i + 1] = #$d) or (ARegExpr [i + 1] = #$a))\r\n         do inc (i); // attempt to work with different kinds of line separators\r\n        // now we are at the line separator that must be skipped.\r\n       end;\r\n      // here is no 'else' clause - we simply skip ordinary chars\r\n     end; // of case\r\n    inc (i); // skip scanned char\r\n    // ! can move after Len due to skipping quoted symbol\r\n   end;\r\n\r\n  // check brackets balance\r\n  if StackIdx <> 0\r\n   then Result := -1; // unbalansed '('\r\n\r\n  // check if entire r.e. added\r\n  if (ASubExprs.Count = 0)\r\n   or ((integer (ASubExprs.Objects [0]) and $FFFF) <> 1)\r\n   or (((integer (ASubExprs.Objects [0]) ShR 16) and $FFFF) <> Len)\r\n    // whole r.e. wasn't added because it isn't bracketed\r\n    // well, we add it now:\r\n    then ASubExprs.InsertObject (0, ARegExpr, TObject ((Len ShL 16) or 1));\r\n\r\n  finally FreeMem (Stack);\r\n  end;\r\n end; { of function RegExprSubExpressions\r\n--------------------------------------------------------------}\r\n\r\n\r\n\r\nconst\r\n MAGIC       = TREOp (216);// programm signature\r\n\r\n// name            opcode    opnd? meaning\r\n EEND        = TREOp (0);  // -    End of program\r\n BOL         = TREOp (1);  // -    Match \"\" at beginning of line\r\n EOL         = TREOp (2);  // -    Match \"\" at end of line\r\n ANY         = TREOp (3);  // -    Match any one character\r\n ANYOF       = TREOp (4);  // Str  Match any character in string Str\r\n ANYBUT      = TREOp (5);  // Str  Match any char. not in string Str\r\n BRANCH      = TREOp (6);  // Node Match this alternative, or the next\r\n BACK        = TREOp (7);  // -    Jump backward (Next < 0)\r\n EXACTLY     = TREOp (8);  // Str  Match string Str\r\n NOTHING     = TREOp (9);  // -    Match empty string\r\n STAR        = TREOp (10); // Node Match this (simple) thing 0 or more times\r\n PLUS        = TREOp (11); // Node Match this (simple) thing 1 or more times\r\n ANYDIGIT    = TREOp (12); // -    Match any digit (equiv [0-9])\r\n NOTDIGIT    = TREOp (13); // -    Match not digit (equiv [0-9])\r\n ANYLETTER   = TREOp (14); // -    Match any letter from property WordChars\r\n NOTLETTER   = TREOp (15); // -    Match not letter from property WordChars\r\n ANYSPACE    = TREOp (16); // -    Match any space char (see property SpaceChars)\r\n NOTSPACE    = TREOp (17); // -    Match not space char (see property SpaceChars)\r\n BRACES      = TREOp (18); // Node,Min,Max Match this (simple) thing from Min to Max times.\r\n                           //      Min and Max are TREBracesArg\r\n COMMENT     = TREOp (19); // -    Comment ;)\r\n EXACTLYCI   = TREOp (20); // Str  Match string Str case insensitive\r\n ANYOFCI     = TREOp (21); // Str  Match any character in string Str, case insensitive\r\n ANYBUTCI    = TREOp (22); // Str  Match any char. not in string Str, case insensitive\r\n LOOPENTRY   = TREOp (23); // Node Start of loop (Node - LOOP for this loop)\r\n LOOP        = TREOp (24); // Node,Min,Max,LoopEntryJmp - back jump for LOOPENTRY.\r\n                           //      Min and Max are TREBracesArg\r\n                           //      Node - next node in sequence,\r\n                           //      LoopEntryJmp - associated LOOPENTRY node addr\r\n ANYOFTINYSET= TREOp (25); // Chrs Match any one char from Chrs (exactly TinySetLen chars)\r\n ANYBUTTINYSET=TREOp (26); // Chrs Match any one char not in Chrs (exactly TinySetLen chars)\r\n ANYOFFULLSET= TREOp (27); // Set  Match any one char from set of char\r\n                           // - very fast (one CPU instruction !) but takes 32 bytes of p-code\r\n BSUBEXP     = TREOp (28); // Idx  Match previously matched subexpression #Idx (stored as REChar) //###0.936\r\n BSUBEXPCI   = TREOp (29); // Idx  -\"- in case-insensitive mode\r\n\r\n // Non-Greedy Style Ops //###0.940\r\n STARNG      = TREOp (30); // Same as START but in non-greedy mode\r\n PLUSNG      = TREOp (31); // Same as PLUS but in non-greedy mode\r\n BRACESNG    = TREOp (32); // Same as BRACES but in non-greedy mode\r\n LOOPNG      = TREOp (33); // Same as LOOP but in non-greedy mode\r\n\r\n // Multiline mode \\m\r\n BOLML       = TREOp (34);  // -    Match \"\" at beginning of line\r\n EOLML       = TREOp (35);  // -    Match \"\" at end of line\r\n ANYML       = TREOp (36);  // -    Match any one character\r\n\r\n // Word boundary\r\n BOUND       = TREOp (37);  // Match \"\" between words //###0.943\r\n NOTBOUND    = TREOp (38);  // Match \"\" not between words //###0.943\r\n\r\n // !!! Change OPEN value if you add new opcodes !!!\r\n\r\n OPEN        = TREOp (39); // -    Mark this point in input as start of \\n\r\n                           //      OPEN + 1 is \\1, etc.\r\n CLOSE       = TREOp (ord (OPEN) + NSUBEXP);\r\n                           // -    Analogous to OPEN.\r\n\r\n // !!! Don't add new OpCodes after CLOSE !!!\r\n\r\n// We work with p-code thru pointers, compatible with PRegExprChar.\r\n// Note: all code components (TRENextOff, TREOp, TREBracesArg, etc)\r\n// must have lengths that can be divided by SizeOf (REChar) !\r\n// A node is TREOp of opcode followed Next \"pointer\" of TRENextOff type.\r\n// The Next is a offset from the opcode of the node containing it.\r\n// An operand, if any, simply follows the node. (Note that much of\r\n// the code generation knows about this implicit relationship!)\r\n// Using TRENextOff=integer speed up p-code processing.\r\n\r\n// Opcodes description:\r\n//\r\n// BRANCH The set of branches constituting a single choice are hooked\r\n//      together with their \"next\" pointers, since precedence prevents\r\n//      anything being concatenated to any individual branch.  The\r\n//      \"next\" pointer of the last BRANCH in a choice points to the\r\n//      thing following the whole choice.  This is also where the\r\n//      final \"next\" pointer of each individual branch points; each\r\n//      branch starts with the operand node of a BRANCH node.\r\n// BACK Normal \"next\" pointers all implicitly point forward; BACK\r\n//      exists to make loop structures possible.\r\n// STAR,PLUS,BRACES '?', and complex '*' and '+', are implemented as\r\n//      circular BRANCH structures using BACK. Complex '{min,max}'\r\n//      - as pair LOOPENTRY-LOOP (see below). Simple cases (one\r\n//      character per match) are implemented with STAR, PLUS and\r\n//      BRACES for speed and to minimize recursive plunges.\r\n// LOOPENTRY,LOOP {min,max} are implemented as special pair\r\n//      LOOPENTRY-LOOP. Each LOOPENTRY initialize loopstack for\r\n//      current level.\r\n// OPEN,CLOSE are numbered at compile time.\r\n\r\n\r\n{=============================================================}\r\n{================== Error handling section ===================}\r\n{=============================================================}\r\n\r\nconst\r\n reeOk = 0;\r\n reeCompNullArgument = 100;\r\n reeCompRegexpTooBig = 101;\r\n reeCompParseRegTooManyBrackets = 102;\r\n reeCompParseRegUnmatchedBrackets = 103;\r\n reeCompParseRegUnmatchedBrackets2 = 104;\r\n reeCompParseRegJunkOnEnd = 105;\r\n reePlusStarOperandCouldBeEmpty = 106;\r\n reeNestedSQP = 107;\r\n reeBadHexDigit = 108;\r\n reeInvalidRange = 109;\r\n reeParseAtomTrailingBackSlash = 110;\r\n reeNoHexCodeAfterBSlashX = 111;\r\n reeHexCodeAfterBSlashXTooBig = 112;\r\n reeUnmatchedSqBrackets = 113;\r\n reeInternalUrp = 114;\r\n reeQPSBFollowsNothing = 115;\r\n reeTrailingBackSlash = 116;\r\n reeRarseAtomInternalDisaster = 119;\r\n reeBRACESArgTooBig = 122;\r\n reeBracesMinParamGreaterMax = 124;\r\n reeUnclosedComment = 125;\r\n reeComplexBracesNotImplemented = 126;\r\n reeUrecognizedModifier = 127;\r\n reeBadLinePairedSeparator = 128;\r\n reeRegRepeatCalledInappropriately = 1000;\r\n reeMatchPrimMemoryCorruption = 1001;\r\n reeMatchPrimCorruptedPointers = 1002;\r\n reeNoExpression = 1003;\r\n reeCorruptedProgram = 1004;\r\n reeNoInpitStringSpecified = 1005;\r\n reeOffsetMustBeGreaterThen0 = 1006;\r\n reeExecNextWithoutExec = 1007;\r\n reeGetInputStringWithoutInputString = 1008;\r\n reeDumpCorruptedOpcode = 1011;\r\n reeModifierUnsupported = 1013;\r\n reeLoopStackExceeded = 1014;\r\n reeLoopWithoutEntry = 1015;\r\n reeBadPCodeImported = 2000;\r\n\r\nfunction TRegExpr.ErrorMsg (AErrorID : integer) : RegExprString;\r\n begin\r\n  case AErrorID of\r\n    reeOk: Result := 'No errors';\r\n    reeCompNullArgument: Result := 'TRegExpr(comp): Null Argument';\r\n    reeCompRegexpTooBig: Result := 'TRegExpr(comp): Regexp Too Big';\r\n    reeCompParseRegTooManyBrackets: Result := 'TRegExpr(comp): ParseReg Too Many ()';\r\n    reeCompParseRegUnmatchedBrackets: Result := 'TRegExpr(comp): ParseReg Unmatched ()';\r\n    reeCompParseRegUnmatchedBrackets2: Result := 'TRegExpr(comp): ParseReg Unmatched ()';\r\n    reeCompParseRegJunkOnEnd: Result := 'TRegExpr(comp): ParseReg Junk On End';\r\n    reePlusStarOperandCouldBeEmpty: Result := 'TRegExpr(comp): *+ Operand Could Be Empty';\r\n    reeNestedSQP: Result := 'TRegExpr(comp): Nested *?+';\r\n    reeBadHexDigit: Result := 'TRegExpr(comp): Bad Hex Digit';\r\n    reeInvalidRange: Result := 'TRegExpr(comp): Invalid [] Range';\r\n    reeParseAtomTrailingBackSlash: Result := 'TRegExpr(comp): Parse Atom Trailing \\';\r\n    reeNoHexCodeAfterBSlashX: Result := 'TRegExpr(comp): No Hex Code After \\x';\r\n    reeHexCodeAfterBSlashXTooBig: Result := 'TRegExpr(comp): Hex Code After \\x Is Too Big';\r\n    reeUnmatchedSqBrackets: Result := 'TRegExpr(comp): Unmatched []';\r\n    reeInternalUrp: Result := 'TRegExpr(comp): Internal Urp';\r\n    reeQPSBFollowsNothing: Result := 'TRegExpr(comp): ?+*{ Follows Nothing';\r\n    reeTrailingBackSlash: Result := 'TRegExpr(comp): Trailing \\';\r\n    reeRarseAtomInternalDisaster: Result := 'TRegExpr(comp): RarseAtom Internal Disaster';\r\n    reeBRACESArgTooBig: Result := 'TRegExpr(comp): BRACES Argument Too Big';\r\n    reeBracesMinParamGreaterMax: Result := 'TRegExpr(comp): BRACE Min Param Greater then Max';\r\n    reeUnclosedComment: Result := 'TRegExpr(comp): Unclosed (?#Comment)';\r\n    reeComplexBracesNotImplemented: Result := 'TRegExpr(comp): If you want take part in beta-testing BRACES ''{min,max}'' and non-greedy ops ''*?'', ''+?'', ''??'' for complex cases - remove ''.'' from {.$DEFINE ComplexBraces}';\r\n    reeUrecognizedModifier: Result := 'TRegExpr(comp): Urecognized Modifier';\r\n    reeBadLinePairedSeparator: Result := 'TRegExpr(comp): LinePairedSeparator must countain two different chars or no chars at all';\r\n\r\n    reeRegRepeatCalledInappropriately: Result := 'TRegExpr(exec): RegRepeat Called Inappropriately';\r\n    reeMatchPrimMemoryCorruption: Result := 'TRegExpr(exec): MatchPrim Memory Corruption';\r\n    reeMatchPrimCorruptedPointers: Result := 'TRegExpr(exec): MatchPrim Corrupted Pointers';\r\n    reeNoExpression: Result := 'TRegExpr(exec): Not Assigned Expression Property';\r\n    reeCorruptedProgram: Result := 'TRegExpr(exec): Corrupted Program';\r\n    reeNoInpitStringSpecified: Result := 'TRegExpr(exec): No Input String Specified';\r\n    reeOffsetMustBeGreaterThen0: Result := 'TRegExpr(exec): Offset Must Be Greater Then 0';\r\n    reeExecNextWithoutExec: Result := 'TRegExpr(exec): ExecNext Without Exec[Pos]';\r\n    reeGetInputStringWithoutInputString: Result := 'TRegExpr(exec): GetInputString Without InputString';\r\n    reeDumpCorruptedOpcode: Result := 'TRegExpr(dump): Corrupted Opcode';\r\n    reeLoopStackExceeded: Result := 'TRegExpr(exec): Loop Stack Exceeded';\r\n    reeLoopWithoutEntry: Result := 'TRegExpr(exec): Loop Without LoopEntry !';\r\n\r\n    reeBadPCodeImported: Result := 'TRegExpr(misc): Bad p-code imported';\r\n    else Result := 'Unknown error';\r\n   end;\r\n end; { of procedure TRegExpr.Error\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.LastError : integer;\r\n begin\r\n  Result := fLastError;\r\n  fLastError := reeOk;\r\n end; { of function TRegExpr.LastError\r\n--------------------------------------------------------------}\r\n\r\n\r\n{=============================================================}\r\n{===================== Common section ========================}\r\n{=============================================================}\r\n\r\nclass function TRegExpr.VersionMajor : integer; //###0.944\r\n begin\r\n  Result := TRegExprVersionMajor;\r\n end; { of class function TRegExpr.VersionMajor\r\n--------------------------------------------------------------}\r\n\r\nclass function TRegExpr.VersionMinor : integer; //###0.944\r\n begin\r\n  Result := TRegExprVersionMinor;\r\n end; { of class function TRegExpr.VersionMinor\r\n--------------------------------------------------------------}\r\n\r\nconstructor TRegExpr.Create;\r\n begin\r\n  inherited;\r\n  programm := nil;\r\n  fExpression := nil;\r\n  fInputString := nil;\r\n\r\n  regexpbeg := nil;\r\n  fExprIsCompiled := false;\r\n\r\n  ModifierI := RegExprModifierI;\r\n  ModifierR := RegExprModifierR;\r\n  ModifierS := RegExprModifierS;\r\n  ModifierG := RegExprModifierG;\r\n  ModifierM := RegExprModifierM; //###0.940\r\n\r\n  SpaceChars := RegExprSpaceChars; //###0.927\r\n  WordChars := RegExprWordChars; //###0.929\r\n  fInvertCase := RegExprInvertCaseFunction; //###0.927\r\n\r\n  fLineSeparators := RegExprLineSeparators; //###0.941\r\n  LinePairedSeparator := RegExprLinePairedSeparator; //###0.941\r\n end; { of constructor TRegExpr.Create\r\n--------------------------------------------------------------}\r\n\r\ndestructor TRegExpr.Destroy;\r\n begin\r\n  if programm <> nil\r\n   then FreeMem (programm);\r\n  if fExpression <> nil\r\n   then FreeMem (fExpression);\r\n  if fInputString <> nil\r\n   then FreeMem (fInputString);\r\n end; { of destructor TRegExpr.Destroy\r\n--------------------------------------------------------------}\r\n\r\nclass function TRegExpr.InvertCaseFunction (const Ch : REChar) : REChar;\r\n begin\r\n  {$IFDEF UniCode}\r\n  if Ch >= #128\r\n   then Result := Ch\r\n  else\r\n  {$ENDIF}\r\n   begin\r\n    Result := {$IFDEF FPC}AnsiUpperCase (Ch) [1]{$ELSE} REChar (CharUpper (PChar (Ch))){$ENDIF};\r\n    if Result = Ch\r\n     then Result := {$IFDEF FPC}AnsiLowerCase (Ch) [1]{$ELSE} REChar (CharLower (PChar (Ch))){$ENDIF};\r\n   end;\r\n end; { of function TRegExpr.InvertCaseFunction\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetExpression : RegExprString;\r\n begin\r\n  if fExpression <> nil\r\n   then Result := fExpression\r\n   else Result := '';\r\n end; { of function TRegExpr.GetExpression\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.SetExpression (const s : RegExprString);\r\n var\r\n  Len : integer; //###0.950\r\n begin\r\n  if (s <> fExpression) or not fExprIsCompiled then begin\r\n    fExprIsCompiled := false;\r\n    if fExpression <> nil then begin\r\n      FreeMem (fExpression);\r\n      fExpression := nil;\r\n     end;\r\n    if s <> '' then begin\r\n      Len := length (s); //###0.950\r\n      GetMem (fExpression, (Len + 1) * SizeOf (REChar));\r\n//      StrPCopy (fExpression, s); //###0.950 replaced due to StrPCopy limitation of 255 chars\r\n      {$IFDEF UniCode}\r\n      StrPCopy (fExpression, Copy (s, 1, Len)); //###0.950\r\n      {$ELSE}\r\n      StrLCopy (fExpression, PRegExprChar (s), Len); //###0.950\r\n      {$ENDIF UniCode}\r\n\r\n      InvalidateProgramm; //###0.941\r\n     end;\r\n   end;\r\n end; { of procedure TRegExpr.SetExpression\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetSubExprMatchCount : integer;\r\n begin\r\n  if Assigned (fInputString) then begin\r\n     Result := NSUBEXP - 1;\r\n     while (Result > 0) and ((startp [Result] = nil)\r\n                             or (endp [Result] = nil))\r\n      do dec (Result);\r\n    end\r\n   else Result := -1;\r\n end; { of function TRegExpr.GetSubExprMatchCount\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetMatchPos (Idx : integer) : integer;\r\n begin\r\n  if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)\r\n     and Assigned (startp [Idx]) and Assigned (endp [Idx]) then begin\r\n     Result := (startp [Idx] - fInputString) + 1;\r\n    end\r\n   else Result := -1;\r\n end; { of function TRegExpr.GetMatchPos\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetMatchLen (Idx : integer) : integer;\r\n begin\r\n  if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)\r\n     and Assigned (startp [Idx]) and Assigned (endp [Idx]) then begin\r\n     Result := endp [Idx] - startp [Idx];\r\n    end\r\n   else Result := -1;\r\n end; { of function TRegExpr.GetMatchLen\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetMatch (Idx : integer) : RegExprString;\r\n begin\r\n  if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)\r\n     and Assigned (startp [Idx]) and Assigned (endp [Idx])\r\n   //then Result := copy (fInputString, MatchPos [Idx], MatchLen [Idx]) //###0.929\r\n   then SetString (Result, startp [idx], endp [idx] - startp [idx])\r\n   else Result := '';\r\n end; { of function TRegExpr.GetMatch\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetModifierStr : RegExprString;\r\n begin\r\n  Result := '-';\r\n\r\n  if ModifierI\r\n   then Result := 'i' + Result\r\n   else Result := Result + 'i';\r\n  if ModifierR\r\n   then Result := 'r' + Result\r\n   else Result := Result + 'r';\r\n  if ModifierS\r\n   then Result := 's' + Result\r\n   else Result := Result + 's';\r\n  if ModifierG\r\n   then Result := 'g' + Result\r\n   else Result := Result + 'g';\r\n  if ModifierM\r\n   then Result := 'm' + Result\r\n   else Result := Result + 'm';\r\n  if ModifierX\r\n   then Result := 'x' + Result\r\n   else Result := Result + 'x';\r\n\r\n  if Result [length (Result)] = '-' // remove '-' if all modifiers are 'On'\r\n   then System.Delete (Result, length (Result), 1);\r\n end; { of function TRegExpr.GetModifierStr\r\n--------------------------------------------------------------}\r\n\r\nclass function TRegExpr.ParseModifiersStr (const AModifiers : RegExprString;\r\nvar AModifiersInt : integer) : boolean;\r\n// !!! Be carefull - this is class function and must not use object instance fields\r\n var\r\n  i : integer;\r\n  IsOn : boolean;\r\n  Mask : integer;\r\n begin\r\n  Result := true;\r\n  IsOn := true;\r\n  Mask := 0; // prevent compiler warning\r\n  for i := 1 to length (AModifiers) do\r\n   if AModifiers [i] = '-'\r\n    then IsOn := false\r\n    else begin\r\n      if Pos (AModifiers [i], 'iI') > 0\r\n       then Mask := MaskModI\r\n      else if Pos (AModifiers [i], 'rR') > 0\r\n       then Mask := MaskModR\r\n      else if Pos (AModifiers [i], 'sS') > 0\r\n       then Mask := MaskModS\r\n      else if Pos (AModifiers [i], 'gG') > 0\r\n       then Mask := MaskModG\r\n      else if Pos (AModifiers [i], 'mM') > 0\r\n       then Mask := MaskModM\r\n      else if Pos (AModifiers [i], 'xX') > 0\r\n       then Mask := MaskModX\r\n      else begin\r\n        Result := false;\r\n        EXIT;\r\n       end;\r\n      if IsOn\r\n       then AModifiersInt := AModifiersInt or Mask\r\n       else AModifiersInt := AModifiersInt and not Mask;\r\n     end;\r\n end; { of function TRegExpr.ParseModifiersStr\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.SetModifierStr (const AModifiers : RegExprString);\r\n begin\r\n  if not ParseModifiersStr (AModifiers, fModifiers)\r\n   then Error (reeModifierUnsupported);\r\n end; { of procedure TRegExpr.SetModifierStr\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetModifier (AIndex : integer) : boolean;\r\n var\r\n  Mask : integer;\r\n begin\r\n  Result := false;\r\n  case AIndex of\r\n    1: Mask := MaskModI;\r\n    2: Mask := MaskModR;\r\n    3: Mask := MaskModS;\r\n    4: Mask := MaskModG;\r\n    5: Mask := MaskModM;\r\n    6: Mask := MaskModX;\r\n    else begin\r\n      Error (reeModifierUnsupported);\r\n      EXIT;\r\n     end;\r\n   end;\r\n  Result := (fModifiers and Mask) <> 0;\r\n end; { of function TRegExpr.GetModifier\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.SetModifier (AIndex : integer; ASet : boolean);\r\n var\r\n  Mask : integer;\r\n begin\r\n  case AIndex of\r\n    1: Mask := MaskModI;\r\n    2: Mask := MaskModR;\r\n    3: Mask := MaskModS;\r\n    4: Mask := MaskModG;\r\n    5: Mask := MaskModM;\r\n    6: Mask := MaskModX;\r\n    else begin\r\n      Error (reeModifierUnsupported);\r\n      EXIT;\r\n     end;\r\n   end;\r\n  if ASet\r\n   then fModifiers := fModifiers or Mask\r\n   else fModifiers := fModifiers and not Mask;\r\n end; { of procedure TRegExpr.SetModifier\r\n--------------------------------------------------------------}\r\n\r\n\r\n{=============================================================}\r\n{==================== Compiler section =======================}\r\n{=============================================================}\r\n\r\nprocedure TRegExpr.InvalidateProgramm;\r\n begin\r\n  if programm <> nil then begin\r\n    FreeMem (programm);\r\n    programm := nil;\r\n   end;\r\n end; { of procedure TRegExpr.InvalidateProgramm\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.Compile; //###0.941\r\n begin\r\n  if fExpression = nil then begin // No Expression assigned\r\n    Error (reeNoExpression);\r\n    EXIT;\r\n   end;\r\n  CompileRegExpr (fExpression);\r\n end; { of procedure TRegExpr.Compile\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.IsProgrammOk : boolean;\r\n {$IFNDEF UniCode}\r\n var\r\n  i : integer;\r\n {$ENDIF}\r\n begin\r\n  Result := false;\r\n\r\n  // check modifiers\r\n  if fModifiers <> fProgModifiers //###0.941\r\n   then InvalidateProgramm;\r\n\r\n  // can we optimize line separators by using sets?\r\n  {$IFNDEF UniCode}\r\n  fLineSeparatorsSet := [];\r\n  for i := 1 to length (fLineSeparators)\r\n   do System.Include (fLineSeparatorsSet, fLineSeparators [i]);\r\n  {$ENDIF}\r\n\r\n  // [Re]compile if needed\r\n  if programm = nil\r\n   then Compile; //###0.941\r\n\r\n  // check [re]compiled programm\r\n  if programm = nil\r\n   then EXIT // error was set/raised by Compile (was reeExecAfterCompErr)\r\n  else if programm [0] <> MAGIC // Program corrupted.\r\n   then Error (reeCorruptedProgram)\r\n  else Result := true;\r\n end; { of function TRegExpr.IsProgrammOk\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.Tail (p : PRegExprChar; val : PRegExprChar);\r\n// set the next-pointer at the end of a node chain\r\n var\r\n  scan : PRegExprChar;\r\n  temp : PRegExprChar;\r\n//  i : int64;\r\n begin\r\n  if p = @regdummy\r\n   then EXIT;\r\n  // Find last node.\r\n  scan := p;\r\n  REPEAT\r\n   temp := regnext (scan);\r\n   if temp = nil\r\n    then BREAK;\r\n   scan := temp;\r\n  UNTIL false;\r\n  // Set Next 'pointer'\r\n  if val < scan\r\n   then PRENextOff (scan + REOpSz)^ := - (scan - val) //###0.948\r\n   // work around PWideChar subtraction bug (Delphi uses\r\n   // shr after subtraction to calculate widechar distance %-( )\r\n   // so, if difference is negative we have .. the \"feature\" :(\r\n   // I could wrap it in $IFDEF UniCode, but I didn't because\r\n   // \"P  Q computes the difference between the address given\r\n   // by P (the higher address) and the address given by Q (the\r\n   // lower address)\" - Delphi help quotation.\r\n   else PRENextOff (scan + REOpSz)^ := val - scan; //###0.933\r\n end; { of procedure TRegExpr.Tail\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.OpTail (p : PRegExprChar; val : PRegExprChar);\r\n// regtail on operand of first argument; nop if operandless\r\n begin\r\n  // \"Operandless\" and \"op != BRANCH\" are synonymous in practice.\r\n  if (p = nil) or (p = @regdummy) or (PREOp (p)^ <> BRANCH)\r\n   then EXIT;\r\n  Tail (p + REOpSz + RENextOffSz, val); //###0.933\r\n end; { of procedure TRegExpr.OpTail\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.EmitNode (op : TREOp) : PRegExprChar; //###0.933\r\n// emit a node, return location\r\n begin\r\n  Result := regcode;\r\n  if Result <> @regdummy then begin\r\n     PREOp (regcode)^ := op;\r\n     inc (regcode, REOpSz);\r\n     PRENextOff (regcode)^ := 0; // Next \"pointer\" := nil\r\n     inc (regcode, RENextOffSz);\r\n    end\r\n   else inc (regsize, REOpSz + RENextOffSz); // compute code size without code generation\r\n end; { of function TRegExpr.EmitNode\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.EmitC (b : REChar);\r\n// emit a byte to code\r\n begin\r\n  if regcode <> @regdummy then begin\r\n     regcode^ := b;\r\n     inc (regcode);\r\n    end\r\n   else inc (regsize); // Type of p-code pointer always is ^REChar\r\n end; { of procedure TRegExpr.EmitC\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.InsertOperator (op : TREOp; opnd : PRegExprChar; sz : integer);\r\n// insert an operator in front of already-emitted operand\r\n// Means relocating the operand.\r\n var\r\n  src, dst, place : PRegExprChar;\r\n  i : integer;\r\n begin\r\n  if regcode = @regdummy then begin\r\n    inc (regsize, sz);\r\n    EXIT;\r\n   end;\r\n  src := regcode;\r\n  inc (regcode, sz);\r\n  dst := regcode;\r\n  while src > opnd do begin\r\n    dec (dst);\r\n    dec (src);\r\n    dst^ := src^;\r\n   end;\r\n  place := opnd; // Op node, where operand used to be.\r\n  PREOp (place)^ := op;\r\n  inc (place, REOpSz);\r\n  for i := 1 + REOpSz to sz do begin\r\n    place^ := #0;\r\n    inc (place);\r\n   end;\r\n end; { of procedure TRegExpr.InsertOperator\r\n--------------------------------------------------------------}\r\n\r\nfunction strcspn (s1 : PRegExprChar; s2 : PRegExprChar) : integer;\r\n// find length of initial segment of s1 consisting\r\n// entirely of characters not from s2\r\n var scan1, scan2 : PRegExprChar;\r\n begin\r\n  Result := 0;\r\n  scan1 := s1;\r\n  while scan1^ <> #0 do begin\r\n    scan2 := s2;\r\n    while scan2^ <> #0 do\r\n     if scan1^ = scan2^\r\n      then EXIT\r\n      else inc (scan2);\r\n    inc (Result);\r\n    inc (scan1)\r\n   end;\r\n end; { of function strcspn\r\n--------------------------------------------------------------}\r\n\r\nconst\r\n// Flags to be passed up and down.\r\n HASWIDTH =   01; // Known never to match nil string.\r\n SIMPLE   =   02; // Simple enough to be STAR/PLUS/BRACES operand.\r\n SPSTART  =   04; // Starts with * or +.\r\n WORST    =   0;  // Worst case.\r\n META : array [0 .. 12] of REChar = (\r\n  '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{', #0);\r\n // Any modification must be synchronized with QuoteRegExprMetaChars !!!\r\n\r\n{$IFDEF UniCode}\r\n RusRangeLo : array [0 .. 33] of REChar =\r\n  (#$430,#$431,#$432,#$433,#$434,#$435,#$451,#$436,#$437,\r\n   #$438,#$439,#$43A,#$43B,#$43C,#$43D,#$43E,#$43F,\r\n   #$440,#$441,#$442,#$443,#$444,#$445,#$446,#$447,\r\n   #$448,#$449,#$44A,#$44B,#$44C,#$44D,#$44E,#$44F,#0);\r\n RusRangeHi : array [0 .. 33] of REChar =\r\n  (#$410,#$411,#$412,#$413,#$414,#$415,#$401,#$416,#$417,\r\n   #$418,#$419,#$41A,#$41B,#$41C,#$41D,#$41E,#$41F,\r\n   #$420,#$421,#$422,#$423,#$424,#$425,#$426,#$427,\r\n   #$428,#$429,#$42A,#$42B,#$42C,#$42D,#$42E,#$42F,#0);\r\n RusRangeLoLow = #$430{''};\r\n RusRangeLoHigh = #$44F{''};\r\n RusRangeHiLow = #$410{''};\r\n RusRangeHiHigh = #$42F{''};\r\n{$ELSE}\r\n RusRangeLo = '';\r\n RusRangeHi = 'Ũ';\r\n RusRangeLoLow = '';\r\n RusRangeLoHigh = '';\r\n RusRangeHiLow = '';\r\n RusRangeHiHigh = '';\r\n{$ENDIF}\r\n\r\nfunction TRegExpr.CompileRegExpr (exp : PRegExprChar) : boolean;\r\n// compile a regular expression into internal code\r\n// We can't allocate space until we know how big the compiled form will be,\r\n// but we can't compile it (and thus know how big it is) until we've got a\r\n// place to put the code.  So we cheat:  we compile it twice, once with code\r\n// generation turned off and size counting turned on, and once \"for real\".\r\n// This also means that we don't allocate space until we are sure that the\r\n// thing really will compile successfully, and we never have to move the\r\n// code and thus invalidate pointers into it.  (Note that it has to be in\r\n// one piece because free() must be able to free it all.)\r\n// Beware that the optimization-preparation code in here knows about some\r\n// of the structure of the compiled regexp.\r\n var\r\n  scan, longest : PRegExprChar;\r\n  len : cardinal;\r\n  flags : integer;\r\n begin\r\n  Result := false; // life too dark\r\n\r\n  regparse := nil; // for correct error handling\r\n  regexpbeg := exp;\r\n  try\r\n\r\n  if programm <> nil then begin\r\n    FreeMem (programm);\r\n    programm := nil;\r\n   end;\r\n\r\n  if exp = nil then begin\r\n    Error (reeCompNullArgument);\r\n    EXIT;\r\n   end;\r\n\r\n  fProgModifiers := fModifiers;\r\n  // well, may it's paranoia. I'll check it later... !!!!!!!!\r\n\r\n  // First pass: determine size, legality.\r\n  fCompModifiers := fModifiers;\r\n  regparse := exp;\r\n  regnpar := 1;\r\n  regsize := 0;\r\n  regcode := @regdummy;\r\n  EmitC (MAGIC);\r\n  if ParseReg (0, flags) = nil\r\n   then EXIT;\r\n\r\n  // Small enough for 2-bytes programm pointers ?\r\n  // ###0.933 no real p-code length limits now :)))\r\n//  if regsize >= 64 * 1024 then begin\r\n//    Error (reeCompRegexpTooBig);\r\n//    EXIT;\r\n//   end;\r\n\r\n  // Allocate space.\r\n  GetMem (programm, regsize * SizeOf (REChar));\r\n\r\n  // Second pass: emit code.\r\n  fCompModifiers := fModifiers;\r\n  regparse := exp;\r\n  regnpar := 1;\r\n  regcode := programm;\r\n  EmitC (MAGIC);\r\n  if ParseReg (0, flags) = nil\r\n   then EXIT;\r\n\r\n  // Dig out information for optimizations.\r\n  {$IFDEF UseFirstCharSet} //###0.929\r\n  FirstCharSet := [];\r\n  FillFirstCharSet (programm + REOpSz);\r\n  {$ENDIF}\r\n  regstart := #0; // Worst-case defaults.\r\n  reganch := #0;\r\n  regmust := nil;\r\n  regmlen := 0;\r\n  scan := programm + REOpSz; // First BRANCH.\r\n  if PREOp (regnext (scan))^ = EEND then begin // Only one top-level choice.\r\n    scan := scan + REOpSz + RENextOffSz;\r\n\r\n    // Starting-point info.\r\n    if PREOp (scan)^ = EXACTLY\r\n     then regstart := (scan + REOpSz + RENextOffSz)^\r\n     else if PREOp (scan)^ = BOL\r\n           then inc (reganch);\r\n\r\n    // If there's something expensive in the r.e., find the longest\r\n    // literal string that must appear and make it the regmust.  Resolve\r\n    // ties in favor of later strings, since the regstart check works\r\n    // with the beginning of the r.e. and avoiding duplication\r\n    // strengthens checking.  Not a strong reason, but sufficient in the\r\n    // absence of others.\r\n    if (flags and SPSTART) <> 0 then begin\r\n        longest := nil;\r\n        len := 0;\r\n        while scan <> nil do begin\r\n          if (PREOp (scan)^ = EXACTLY)\r\n             and (strlen (scan + REOpSz + RENextOffSz) >= len) then begin\r\n              longest := scan + REOpSz + RENextOffSz;\r\n              len := strlen (longest);\r\n           end;\r\n          scan := regnext (scan);\r\n         end;\r\n        regmust := longest;\r\n        regmlen := len;\r\n     end;\r\n   end;\r\n\r\n  Result := true;\r\n\r\n  finally begin\r\n    if not Result\r\n     then InvalidateProgramm;\r\n    regexpbeg := nil;\r\n    fExprIsCompiled := Result; //###0.944\r\n   end;\r\n  end;\r\n\r\n end; { of function TRegExpr.CompileRegExpr\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ParseReg (paren : integer; var flagp : integer) : PRegExprChar;\r\n// regular expression, i.e. main body or parenthesized thing\r\n// Caller must absorb opening parenthesis.\r\n// Combining parenthesis handling with the base level of regular expression\r\n// is a trifle forced, but the need to tie the tails of the branches to what\r\n// follows makes it hard to avoid.\r\n var\r\n  ret, br, ender : PRegExprChar;\r\n  parno : integer;\r\n  flags : integer;\r\n  SavedModifiers : integer;\r\n begin\r\n  Result := nil;\r\n  flagp := HASWIDTH; // Tentatively.\r\n  parno := 0; // eliminate compiler stupid warning\r\n  SavedModifiers := fCompModifiers;\r\n\r\n  // Make an OPEN node, if parenthesized.\r\n  if paren <> 0 then begin\r\n      if regnpar >= NSUBEXP then begin\r\n        Error (reeCompParseRegTooManyBrackets);\r\n        EXIT;\r\n       end;\r\n      parno := regnpar;\r\n      inc (regnpar);\r\n      ret := EmitNode (TREOp (ord (OPEN) + parno));\r\n    end\r\n   else ret := nil;\r\n\r\n  // Pick up the branches, linking them together.\r\n  br := ParseBranch (flags);\r\n  if br = nil then begin\r\n    Result := nil;\r\n    EXIT;\r\n   end;\r\n  if ret <> nil\r\n   then Tail (ret, br) // OPEN -> first.\r\n   else ret := br;\r\n  if (flags and HASWIDTH) = 0\r\n   then flagp := flagp and not HASWIDTH;\r\n  flagp := flagp or flags and SPSTART;\r\n  while (regparse^ = '|') do begin\r\n    inc (regparse);\r\n    br := ParseBranch (flags);\r\n    if br = nil then begin\r\n       Result := nil;\r\n       EXIT;\r\n      end;\r\n    Tail (ret, br); // BRANCH -> BRANCH.\r\n    if (flags and HASWIDTH) = 0\r\n     then flagp := flagp and not HASWIDTH;\r\n    flagp := flagp or flags and SPSTART;\r\n   end;\r\n\r\n  // Make a closing node, and hook it on the end.\r\n  if paren <> 0\r\n   then ender := EmitNode (TREOp (ord (CLOSE) + parno))\r\n   else ender := EmitNode (EEND);\r\n  Tail (ret, ender);\r\n\r\n  // Hook the tails of the branches to the closing node.\r\n  br := ret;\r\n  while br <> nil do begin\r\n    OpTail (br, ender);\r\n    br := regnext (br);\r\n   end;\r\n\r\n  // Check for proper termination.\r\n  if paren <> 0 then\r\n   if regparse^ <> ')' then begin\r\n      Error (reeCompParseRegUnmatchedBrackets);\r\n      EXIT;\r\n     end\r\n    else inc (regparse); // skip trailing ')'\r\n  if (paren = 0) and (regparse^ <> #0) then begin\r\n      if regparse^ = ')'\r\n       then Error (reeCompParseRegUnmatchedBrackets2)\r\n       else Error (reeCompParseRegJunkOnEnd);\r\n      EXIT;\r\n    end;\r\n  fCompModifiers := SavedModifiers; // restore modifiers of parent\r\n  Result := ret;\r\n end; { of function TRegExpr.ParseReg\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ParseBranch (var flagp : integer) : PRegExprChar;\r\n// one alternative of an | operator\r\n// Implements the concatenation operator.\r\n var\r\n  ret, chain, latest : PRegExprChar;\r\n  flags : integer;\r\n begin\r\n  flagp := WORST; // Tentatively.\r\n\r\n  ret := EmitNode (BRANCH);\r\n  chain := nil;\r\n  while (regparse^ <> #0) and (regparse^ <> '|')\r\n        and (regparse^ <> ')') do begin\r\n    latest := ParsePiece (flags);\r\n    if latest = nil then begin\r\n      Result := nil;\r\n      EXIT;\r\n     end;\r\n    flagp := flagp or flags and HASWIDTH;\r\n    if chain = nil // First piece.\r\n     then flagp := flagp or flags and SPSTART\r\n     else Tail (chain, latest);\r\n    chain := latest;\r\n   end;\r\n  if chain = nil // Loop ran zero times.\r\n   then EmitNode (NOTHING);\r\n  Result := ret;\r\n end; { of function TRegExpr.ParseBranch\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ParsePiece (var flagp : integer) : PRegExprChar;\r\n// something followed by possible [*+?{]\r\n// Note that the branching code sequences used for ? and the general cases\r\n// of * and + and { are somewhat optimized:  they use the same NOTHING node as\r\n// both the endmarker for their branch list and the body of the last branch.\r\n// It might seem that this node could be dispensed with entirely, but the\r\n// endmarker role is not redundant.\r\n function parsenum (AStart, AEnd : PRegExprChar) : TREBracesArg;\r\n  begin\r\n   Result := 0;\r\n   if AEnd - AStart + 1 > 8 then begin // prevent stupid scanning\r\n     Error (reeBRACESArgTooBig);\r\n     EXIT;\r\n    end;\r\n   while AStart <= AEnd do begin\r\n       Result := Result * 10 + (ord (AStart^) - ord ('0'));\r\n       inc (AStart);\r\n      end;\r\n   if (Result > MaxBracesArg) or (Result < 0) then begin\r\n     Error (reeBRACESArgTooBig);\r\n     EXIT;\r\n    end;\r\n  end;\r\n\r\n var\r\n  op : REChar;\r\n  NonGreedyOp, NonGreedyCh : boolean; //###0.940\r\n  TheOp : TREOp; //###0.940\r\n  NextNode : PRegExprChar;\r\n  flags : integer;\r\n  BracesMin, Bracesmax : TREBracesArg;\r\n  p, savedparse : PRegExprChar;\r\n\r\n procedure EmitComplexBraces (ABracesMin, ABracesMax : TREBracesArg;\r\n   ANonGreedyOp : boolean); //###0.940\r\n  {$IFDEF ComplexBraces}\r\n  var\r\n   off : integer;\r\n  {$ENDIF}\r\n   begin\r\n   {$IFNDEF ComplexBraces}\r\n   Error (reeComplexBracesNotImplemented);\r\n   {$ELSE}\r\n   if ANonGreedyOp\r\n    then TheOp := LOOPNG\r\n    else TheOp := LOOP;\r\n   InsertOperator (LOOPENTRY, Result, REOpSz + RENextOffSz);\r\n   NextNode := EmitNode (TheOp);\r\n   if regcode <> @regdummy then begin\r\n      off := (Result + REOpSz + RENextOffSz)\r\n       - (regcode - REOpSz - RENextOffSz); // back to Atom after LOOPENTRY\r\n      PREBracesArg (regcode)^ := ABracesMin;\r\n      inc (regcode, REBracesArgSz);\r\n      PREBracesArg (regcode)^ := ABracesMax;\r\n      inc (regcode, REBracesArgSz);\r\n      PRENextOff (regcode)^ := off;\r\n      inc (regcode, RENextOffSz);\r\n     end\r\n    else inc (regsize, REBracesArgSz * 2 + RENextOffSz);\r\n   Tail (Result, NextNode); // LOOPENTRY -> LOOP\r\n   if regcode <> @regdummy then\r\n    Tail (Result + REOpSz + RENextOffSz, NextNode); // Atom -> LOOP\r\n   {$ENDIF}\r\n  end;\r\n\r\n procedure EmitSimpleBraces (ABracesMin, ABracesMax : TREBracesArg;\r\n   ANonGreedyOp : boolean); //###0.940\r\n  begin\r\n   if ANonGreedyOp //###0.940\r\n    then TheOp := BRACESNG\r\n    else TheOp := BRACES;\r\n   InsertOperator (TheOp, Result, REOpSz + RENextOffSz + REBracesArgSz * 2);\r\n   if regcode <> @regdummy then begin\r\n     PREBracesArg (Result + REOpSz + RENextOffSz)^ := ABracesMin;\r\n     PREBracesArg (Result + REOpSz + RENextOffSz + REBracesArgSz)^ := ABracesMax;\r\n    end;\r\n  end;\r\n\r\n begin\r\n  Result := ParseAtom (flags);\r\n  if Result = nil\r\n   then EXIT;\r\n\r\n  op := regparse^;\r\n  if not ((op = '*') or (op = '+') or (op = '?') or (op = '{')) then begin\r\n    flagp := flags;\r\n    EXIT;\r\n   end;\r\n  if ((flags and HASWIDTH) = 0) and (op <> '?') then begin\r\n    Error (reePlusStarOperandCouldBeEmpty);\r\n    EXIT;\r\n   end;\r\n\r\n  case op of\r\n    '*': begin\r\n      flagp := WORST or SPSTART;\r\n      NonGreedyCh := (regparse + 1)^ = '?'; //###0.940\r\n      NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940\r\n      if (flags and SIMPLE) = 0 then begin\r\n         if NonGreedyOp //###0.940\r\n          then EmitComplexBraces (0, MaxBracesArg, NonGreedyOp)\r\n          else begin // Emit x* as (x&|), where & means \"self\".\r\n            InsertOperator (BRANCH, Result, REOpSz + RENextOffSz); // Either x\r\n            OpTail (Result, EmitNode (BACK)); // and loop\r\n            OpTail (Result, Result); // back\r\n            Tail (Result, EmitNode (BRANCH)); // or\r\n            Tail (Result, EmitNode (NOTHING)); // nil.\r\n           end\r\n        end\r\n       else begin // Simple\r\n         if NonGreedyOp //###0.940\r\n          then TheOp := STARNG\r\n          else TheOp := STAR;\r\n         InsertOperator (TheOp, Result, REOpSz + RENextOffSz);\r\n        end;\r\n      if NonGreedyCh //###0.940\r\n       then inc (regparse); // Skip extra char ('?')\r\n     end; { of case '*'}\r\n    '+': begin\r\n      flagp := WORST or SPSTART or HASWIDTH;\r\n      NonGreedyCh := (regparse + 1)^ = '?'; //###0.940\r\n      NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940\r\n      if (flags and SIMPLE) = 0 then begin\r\n         if NonGreedyOp //###0.940\r\n          then EmitComplexBraces (1, MaxBracesArg, NonGreedyOp)\r\n          else begin // Emit x+ as x(&|), where & means \"self\".\r\n            NextNode := EmitNode (BRANCH); // Either\r\n            Tail (Result, NextNode);\r\n            Tail (EmitNode (BACK), Result);    // loop back\r\n            Tail (NextNode, EmitNode (BRANCH)); // or\r\n            Tail (Result, EmitNode (NOTHING)); // nil.\r\n           end\r\n        end\r\n       else begin // Simple\r\n         if NonGreedyOp //###0.940\r\n          then TheOp := PLUSNG\r\n          else TheOp := PLUS;\r\n         InsertOperator (TheOp, Result, REOpSz + RENextOffSz);\r\n        end;\r\n      if NonGreedyCh //###0.940\r\n       then inc (regparse); // Skip extra char ('?')\r\n     end; { of case '+'}\r\n    '?': begin\r\n      flagp := WORST;\r\n      NonGreedyCh := (regparse + 1)^ = '?'; //###0.940\r\n      NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940\r\n      if NonGreedyOp then begin //###0.940  // We emit x?? as x{0,1}?\r\n         if (flags and SIMPLE) = 0\r\n          then EmitComplexBraces (0, 1, NonGreedyOp)\r\n          else EmitSimpleBraces (0, 1, NonGreedyOp);\r\n        end\r\n       else begin // greedy '?'\r\n         InsertOperator (BRANCH, Result, REOpSz + RENextOffSz); // Either x\r\n         Tail (Result, EmitNode (BRANCH));  // or\r\n         NextNode := EmitNode (NOTHING); // nil.\r\n         Tail (Result, NextNode);\r\n         OpTail (Result, NextNode);\r\n        end;\r\n      if NonGreedyCh //###0.940\r\n       then inc (regparse); // Skip extra char ('?')\r\n     end; { of case '?'}\r\n   '{': begin\r\n      savedparse := regparse;\r\n      // !!!!!!!!!!!!\r\n      // Filip Jirsak's note - what will happen, when we are at the end of regparse?\r\n      inc (regparse);\r\n      p := regparse;\r\n      while Pos (regparse^, '0123456789') > 0  // <min> MUST appear\r\n       do inc (regparse);\r\n      if (regparse^ <> '}') and (regparse^ <> ',') or (p = regparse) then begin\r\n        regparse := savedparse;\r\n        flagp := flags;\r\n        EXIT;\r\n       end;\r\n      BracesMin := parsenum (p, regparse - 1);\r\n      if regparse^ = ',' then begin\r\n         inc (regparse);\r\n         p := regparse;\r\n         while Pos (regparse^, '0123456789') > 0\r\n          do inc (regparse);\r\n         if regparse^ <> '}' then begin\r\n           regparse := savedparse;\r\n           EXIT;\r\n          end;\r\n         if p = regparse\r\n          then BracesMax := MaxBracesArg\r\n          else BracesMax := parsenum (p, regparse - 1);\r\n        end\r\n       else BracesMax := BracesMin; // {n} == {n,n}\r\n      if BracesMin > BracesMax then begin\r\n        Error (reeBracesMinParamGreaterMax);\r\n        EXIT;\r\n       end;\r\n      if BracesMin > 0\r\n       then flagp := WORST;\r\n      if BracesMax > 0\r\n       then flagp := flagp or HASWIDTH or SPSTART;\r\n\r\n      NonGreedyCh := (regparse + 1)^ = '?'; //###0.940\r\n      NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940\r\n      if (flags and SIMPLE) <> 0\r\n       then EmitSimpleBraces (BracesMin, BracesMax, NonGreedyOp)\r\n       else EmitComplexBraces (BracesMin, BracesMax, NonGreedyOp);\r\n      if NonGreedyCh //###0.940\r\n       then inc (regparse); // Skip extra char '?'\r\n     end; { of case '{'}\r\n//    else // here we can't be\r\n   end; { of case op}\r\n\r\n  inc (regparse);\r\n  if (regparse^ = '*') or (regparse^ = '+') or (regparse^ = '?') or (regparse^ = '{') then begin\r\n    Error (reeNestedSQP);\r\n    EXIT;\r\n   end;\r\n end; { of function TRegExpr.ParsePiece\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ParseAtom (var flagp : integer) : PRegExprChar;\r\n// the lowest level\r\n// Optimization:  gobbles an entire sequence of ordinary characters so that\r\n// it can turn them into a single node, which is smaller to store and\r\n// faster to run.  Backslashed characters are exceptions, each becoming a\r\n// separate node; the code is simpler that way and it's not worth fixing.\r\n var\r\n  ret : PRegExprChar;\r\n  flags : integer;\r\n  RangeBeg, RangeEnd : REChar;\r\n  CanBeRange : boolean;\r\n  len : integer;\r\n  ender : REChar;\r\n  begmodfs : PRegExprChar;\r\n\r\n  {$IFDEF UseSetOfChar} //###0.930\r\n  RangePCodeBeg : PRegExprChar;\r\n  RangePCodeIdx : integer;\r\n  RangeIsCI : boolean;\r\n  RangeSet : TSetOfREChar;\r\n  RangeLen : integer;\r\n  RangeChMin, RangeChMax : REChar;\r\n  {$ENDIF}\r\n\r\n procedure EmitExactly (ch : REChar);\r\n  begin\r\n   if (fCompModifiers and MaskModI) <> 0\r\n    then ret := EmitNode (EXACTLYCI)\r\n    else ret := EmitNode (EXACTLY);\r\n   EmitC (ch);\r\n   EmitC (#0);\r\n   flagp := flagp or HASWIDTH or SIMPLE;\r\n  end;\r\n\r\n procedure EmitStr (const s : RegExprString);\r\n  var i : integer;\r\n  begin\r\n   for i := 1 to length (s)\r\n    do EmitC (s [i]);\r\n  end;\r\n\r\n function HexDig (ch : REChar) : integer;\r\n  begin\r\n   Result := 0;\r\n   if (ch >= 'a') and (ch <= 'f')\r\n    then ch := REChar (ord (ch) - (ord ('a') - ord ('A')));\r\n   if (ch < '0') or (ch > 'F') or ((ch > '9') and (ch < 'A')) then begin\r\n     Error (reeBadHexDigit);\r\n     EXIT;\r\n    end;\r\n   Result := ord (ch) - ord ('0');\r\n   if ch >= 'A'\r\n    then Result := Result - (ord ('A') - ord ('9') - 1);\r\n  end;\r\n\r\n function EmitRange (AOpCode : REChar) : PRegExprChar;\r\n  begin\r\n   {$IFDEF UseSetOfChar}\r\n   case AOpCode of\r\n     ANYBUTCI, ANYBUT:\r\n       Result := EmitNode (ANYBUTTINYSET);\r\n     else // ANYOFCI, ANYOF\r\n       Result := EmitNode (ANYOFTINYSET);\r\n    end;\r\n   case AOpCode of\r\n     ANYBUTCI, ANYOFCI:\r\n       RangeIsCI := True;\r\n     else // ANYBUT, ANYOF\r\n       RangeIsCI := False;\r\n    end;\r\n   RangePCodeBeg := regcode;\r\n   RangePCodeIdx := regsize;\r\n   RangeLen := 0;\r\n   RangeSet := [];\r\n   RangeChMin := #255;\r\n   RangeChMax := #0;\r\n   {$ELSE}\r\n   Result := EmitNode (AOpCode);\r\n   // ToDo:\r\n   // !!!!!!!!!!!!! Implement ANYOF[BUT]TINYSET generation for UniCode !!!!!!!!!!\r\n   {$ENDIF}\r\n  end;\r\n\r\n{$IFDEF UseSetOfChar}\r\n procedure EmitRangeCPrim (b : REChar); //###0.930\r\n  begin\r\n   if b in RangeSet\r\n    then EXIT;\r\n   inc (RangeLen);\r\n   if b < RangeChMin\r\n    then RangeChMin := b;\r\n   if b > RangeChMax\r\n    then RangeChMax := b;\r\n   Include (RangeSet, b);\r\n  end;\r\n {$ENDIF}\r\n\r\n procedure EmitRangeC (b : REChar);\r\n  {$IFDEF UseSetOfChar}\r\n  var\r\n   Ch : REChar;\r\n  {$ENDIF}\r\n  begin\r\n   CanBeRange := false;\r\n   {$IFDEF UseSetOfChar}\r\n    if b <> #0 then begin\r\n       EmitRangeCPrim (b); //###0.930\r\n       if RangeIsCI\r\n        then EmitRangeCPrim (InvertCase (b)); //###0.930\r\n      end\r\n     else begin\r\n       {$IFDEF UseAsserts}\r\n       Assert (RangeLen > 0, 'TRegExpr.ParseAtom(subroutine EmitRangeC): empty range'); // impossible, but who knows..\r\n       Assert (RangeChMin <= RangeChMax, 'TRegExpr.ParseAtom(subroutine EmitRangeC): RangeChMin > RangeChMax'); // impossible, but who knows..\r\n       {$ENDIF}\r\n       if RangeLen <= TinySetLen then begin // emit \"tiny set\"\r\n          if regcode = @regdummy then begin\r\n            regsize := RangePCodeIdx + TinySetLen; // RangeChMin/Max !!!\r\n            EXIT;\r\n           end;\r\n          regcode := RangePCodeBeg;\r\n          for Ch := RangeChMin to RangeChMax do //###0.930\r\n           if Ch in RangeSet then begin\r\n             regcode^ := Ch;\r\n             inc (regcode);\r\n            end;\r\n          // fill rest:\r\n          while regcode < RangePCodeBeg + TinySetLen do begin\r\n            regcode^ := RangeChMax;\r\n            inc (regcode);\r\n           end;\r\n         end\r\n        else begin\r\n          if regcode = @regdummy then begin\r\n            regsize := RangePCodeIdx + SizeOf (TSetOfREChar);\r\n            EXIT;\r\n           end;\r\n          if (RangePCodeBeg - REOpSz - RENextOffSz)^ = ANYBUTTINYSET\r\n           then RangeSet := [#0 .. #255] - RangeSet;\r\n          PREOp (RangePCodeBeg - REOpSz - RENextOffSz)^ := ANYOFFULLSET;\r\n          regcode := RangePCodeBeg;\r\n          Move (RangeSet, regcode^, SizeOf (TSetOfREChar));\r\n          inc (regcode, SizeOf (TSetOfREChar));\r\n         end;\r\n      end;\r\n   {$ELSE}\r\n   EmitC (b);\r\n   {$ENDIF}\r\n  end;\r\n\r\n procedure EmitSimpleRangeC (b : REChar);\r\n  begin\r\n   RangeBeg := b;\r\n   EmitRangeC (b);\r\n   CanBeRange := true;\r\n  end;\r\n\r\n procedure EmitRangeStr (const s : RegExprString);\r\n  var i : integer;\r\n  begin\r\n   for i := 1 to length (s)\r\n    do EmitRangeC (s [i]);\r\n  end;\r\n\r\n function UnQuoteChar (var APtr : PRegExprChar) : REChar; //###0.934\r\n  begin\r\n   case APtr^ of\r\n     't': Result := #$9;  // tab (HT/TAB)\r\n     'n': Result := #$a;  // newline (NL)\r\n     'r': Result := #$d;  // car.return (CR)\r\n     'f': Result := #$c;  // form feed (FF)\r\n     'a': Result := #$7;  // alarm (bell) (BEL)\r\n     'e': Result := #$1b; // escape (ESC)\r\n     'x': begin // hex char\r\n       Result := #0;\r\n       inc (APtr);\r\n       if APtr^ = #0 then begin\r\n         Error (reeNoHexCodeAfterBSlashX);\r\n         EXIT;\r\n        end;\r\n       if APtr^ = '{' then begin // \\x{nnnn} //###0.936\r\n          REPEAT\r\n           inc (APtr);\r\n           if APtr^ = #0 then begin\r\n             Error (reeNoHexCodeAfterBSlashX);\r\n             EXIT;\r\n            end;\r\n           if APtr^ <> '}' then begin\r\n              if (Ord (Result)\r\n                  ShR (SizeOf (REChar) * 8 - 4)) and $F <> 0 then begin\r\n                Error (reeHexCodeAfterBSlashXTooBig);\r\n                EXIT;\r\n               end;\r\n              Result := REChar ((Ord (Result) ShL 4) or HexDig (APtr^));\r\n              // HexDig will cause Error if bad hex digit found\r\n             end\r\n            else BREAK;\r\n          UNTIL False;\r\n         end\r\n        else begin\r\n          Result := REChar (HexDig (APtr^));\r\n          // HexDig will cause Error if bad hex digit found\r\n          inc (APtr);\r\n          if APtr^ = #0 then begin\r\n            Error (reeNoHexCodeAfterBSlashX);\r\n            EXIT;\r\n           end;\r\n          Result := REChar ((Ord (Result) ShL 4) or HexDig (APtr^));\r\n          // HexDig will cause Error if bad hex digit found\r\n         end;\r\n      end;\r\n     else Result := APtr^;\r\n    end;\r\n  end;\r\n\r\n begin\r\n  Result := nil;\r\n  flagp := WORST; // Tentatively.\r\n\r\n  inc (regparse);\r\n  case (regparse - 1)^ of\r\n    '^': if ((fCompModifiers and MaskModM) = 0)\r\n           or ((fLineSeparators = '') and not fLinePairedSeparatorAssigned)\r\n          then ret := EmitNode (BOL)\r\n          else ret := EmitNode (BOLML);\r\n    '$': if ((fCompModifiers and MaskModM) = 0)\r\n           or ((fLineSeparators = '') and not fLinePairedSeparatorAssigned)\r\n          then ret := EmitNode (EOL)\r\n          else ret := EmitNode (EOLML);\r\n    '.':\r\n       if (fCompModifiers and MaskModS) <> 0 then begin\r\n          ret := EmitNode (ANY);\r\n          flagp := flagp or HASWIDTH or SIMPLE;\r\n         end\r\n        else begin // not /s, so emit [^:LineSeparators:]\r\n          ret := EmitNode (ANYML);\r\n          flagp := flagp or HASWIDTH; // not so simple ;)\r\n//          ret := EmitRange (ANYBUT);\r\n//          EmitRangeStr (LineSeparators); //###0.941\r\n//          EmitRangeStr (LinePairedSeparator); // !!! isn't correct if have to accept only paired\r\n//          EmitRangeC (#0);\r\n//          flagp := flagp or HASWIDTH or SIMPLE;\r\n         end;\r\n    '[': begin\r\n        if regparse^ = '^' then begin // Complement of range.\r\n           if (fCompModifiers and MaskModI) <> 0\r\n            then ret := EmitRange (ANYBUTCI)\r\n            else ret := EmitRange (ANYBUT);\r\n           inc (regparse);\r\n          end\r\n         else\r\n          if (fCompModifiers and MaskModI) <> 0\r\n           then ret := EmitRange (ANYOFCI)\r\n           else ret := EmitRange (ANYOF);\r\n\r\n        CanBeRange := false;\r\n\r\n        if (regparse^ = ']') then begin\r\n          EmitSimpleRangeC (regparse^); // []-a] -> ']' .. 'a'\r\n          inc (regparse);\r\n         end;\r\n\r\n        while (regparse^ <> #0) and (regparse^ <> ']') do begin\r\n          if (regparse^ = '-')\r\n              and ((regparse + 1)^ <> #0) and ((regparse + 1)^ <> ']')\r\n              and CanBeRange then begin\r\n             inc (regparse);\r\n             RangeEnd := regparse^;\r\n             if RangeEnd = EscChar then begin\r\n               {$IFDEF UniCode} //###0.935\r\n               if (ord ((regparse + 1)^) < 256)\r\n                  and (char ((regparse + 1)^)\r\n                        in ['d', 'D', 's', 'S', 'w', 'W']) then begin\r\n               {$ELSE}\r\n               if (regparse + 1)^ in ['d', 'D', 's', 'S', 'w', 'W'] then begin\r\n               {$ENDIF}\r\n                 EmitRangeC ('-'); // or treat as error ?!!\r\n                 CONTINUE;\r\n                end;\r\n               inc (regparse);\r\n               RangeEnd := UnQuoteChar (regparse);\r\n              end;\r\n\r\n             // r.e.ranges extension for russian\r\n             if ((fCompModifiers and MaskModR) <> 0)\r\n                and (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeLoHigh) then begin\r\n               EmitRangeStr (RusRangeLo);\r\n              end\r\n             else if ((fCompModifiers and MaskModR) <> 0)\r\n                 and (RangeBeg = RusRangeHiLow) and (RangeEnd = RusRangeHiHigh) then begin\r\n               EmitRangeStr (RusRangeHi);\r\n              end\r\n             else if ((fCompModifiers and MaskModR) <> 0)\r\n                  and (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeHiHigh) then begin\r\n               EmitRangeStr (RusRangeLo);\r\n               EmitRangeStr (RusRangeHi);\r\n              end\r\n             else begin // standard r.e. handling\r\n               if RangeBeg > RangeEnd then begin\r\n                 Error (reeInvalidRange);\r\n                 EXIT;\r\n                end;\r\n               inc (RangeBeg);\r\n               EmitRangeC (RangeEnd); // prevent infinite loop if RangeEnd=$ff\r\n               while RangeBeg < RangeEnd do begin //###0.929\r\n                 EmitRangeC (RangeBeg);\r\n                 inc (RangeBeg);\r\n                end;\r\n              end;\r\n             inc (regparse);\r\n            end\r\n           else begin\r\n             if regparse^ = EscChar then begin\r\n                inc (regparse);\r\n                if regparse^ = #0 then begin\r\n                  Error (reeParseAtomTrailingBackSlash);\r\n                  EXIT;\r\n                 end;\r\n                case regparse^ of // r.e.extensions\r\n                  'd': EmitRangeStr ('0123456789');\r\n                  'w': EmitRangeStr (WordChars);\r\n                  's': EmitRangeStr (SpaceChars);\r\n                  else EmitSimpleRangeC (UnQuoteChar (regparse));\r\n                 end; { of case}\r\n               end\r\n              else EmitSimpleRangeC (regparse^);\r\n             inc (regparse);\r\n            end;\r\n         end; { of while}\r\n        EmitRangeC (#0);\r\n        if regparse^ <> ']' then begin\r\n          Error (reeUnmatchedSqBrackets);\r\n          EXIT;\r\n         end;\r\n        inc (regparse);\r\n        flagp := flagp or HASWIDTH or SIMPLE;\r\n      end;\r\n    '(': begin\r\n        if regparse^ = '?' then begin\r\n           // check for extended Perl syntax : (?..)\r\n           if (regparse + 1)^ = '#' then begin // (?#comment)\r\n              inc (regparse, 2); // find closing ')'\r\n              while (regparse^ <> #0) and (regparse^ <> ')')\r\n               do inc (regparse);\r\n              if regparse^ <> ')' then begin\r\n                Error (reeUnclosedComment);\r\n                EXIT;\r\n               end;\r\n              inc (regparse); // skip ')'\r\n              ret := EmitNode (COMMENT); // comment\r\n             end\r\n           else begin // modifiers ?\r\n             inc (regparse); // skip '?'\r\n             begmodfs := regparse;\r\n             while (regparse^ <> #0) and (regparse^ <> ')')\r\n              do inc (regparse);\r\n             if (regparse^ <> ')')\r\n                or not ParseModifiersStr (copy (begmodfs, 1, (regparse - begmodfs)), fCompModifiers) then begin\r\n               Error (reeUrecognizedModifier);\r\n               EXIT;\r\n              end;\r\n             inc (regparse); // skip ')'\r\n             ret := EmitNode (COMMENT); // comment\r\n//             Error (reeQPSBFollowsNothing);\r\n//             EXIT;\r\n            end;\r\n          end\r\n         else begin\r\n           ret := ParseReg (1, flags);\r\n           if ret = nil then begin\r\n             Result := nil;\r\n             EXIT;\r\n            end;\r\n           flagp := flagp or flags and (HASWIDTH or SPSTART);\r\n          end;\r\n      end;\r\n    #0, '|', ')': begin // Supposed to be caught earlier.\r\n       Error (reeInternalUrp);\r\n       EXIT;\r\n      end;\r\n    '?', '+', '*': begin\r\n       Error (reeQPSBFollowsNothing);\r\n       EXIT;\r\n      end;\r\n    EscChar: begin\r\n        if regparse^ = #0 then begin\r\n          Error (reeTrailingBackSlash);\r\n          EXIT;\r\n         end;\r\n        case regparse^ of // r.e.extensions\r\n          'b': ret := EmitNode (BOUND); //###0.943\r\n          'B': ret := EmitNode (NOTBOUND); //###0.943\r\n          'A': ret := EmitNode (BOL); //###0.941\r\n          'Z': ret := EmitNode (EOL); //###0.941\r\n          'd': begin // r.e.extension - any digit ('0' .. '9')\r\n             ret := EmitNode (ANYDIGIT);\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n          'D': begin // r.e.extension - not digit ('0' .. '9')\r\n             ret := EmitNode (NOTDIGIT);\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n          's': begin // r.e.extension - any space char\r\n             {$IFDEF UseSetOfChar}\r\n             ret := EmitRange (ANYOF);\r\n             EmitRangeStr (SpaceChars);\r\n             EmitRangeC (#0);\r\n             {$ELSE}\r\n             ret := EmitNode (ANYSPACE);\r\n             {$ENDIF}\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n          'S': begin // r.e.extension - not space char\r\n             {$IFDEF UseSetOfChar}\r\n             ret := EmitRange (ANYBUT);\r\n             EmitRangeStr (SpaceChars);\r\n             EmitRangeC (#0);\r\n             {$ELSE}\r\n             ret := EmitNode (NOTSPACE);\r\n             {$ENDIF}\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n          'w': begin // r.e.extension - any english char / digit / '_'\r\n             {$IFDEF UseSetOfChar}\r\n             ret := EmitRange (ANYOF);\r\n             EmitRangeStr (WordChars);\r\n             EmitRangeC (#0);\r\n             {$ELSE}\r\n             ret := EmitNode (ANYLETTER);\r\n             {$ENDIF}\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n          'W': begin // r.e.extension - not english char / digit / '_'\r\n             {$IFDEF UseSetOfChar}\r\n             ret := EmitRange (ANYBUT);\r\n             EmitRangeStr (WordChars);\r\n             EmitRangeC (#0);\r\n             {$ELSE}\r\n             ret := EmitNode (NOTLETTER);\r\n             {$ENDIF}\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n           '1' .. '9': begin //###0.936\r\n             if (fCompModifiers and MaskModI) <> 0\r\n              then ret := EmitNode (BSUBEXPCI)\r\n              else ret := EmitNode (BSUBEXP);\r\n             EmitC (REChar (ord (regparse^) - ord ('0')));\r\n             flagp := flagp or HASWIDTH or SIMPLE;\r\n            end;\r\n          else EmitExactly (UnQuoteChar (regparse));\r\n         end; { of case}\r\n        inc (regparse);\r\n      end;\r\n    else begin\r\n      dec (regparse);\r\n      if ((fCompModifiers and MaskModX) <> 0) and // check for eXtended syntax\r\n          ((regparse^ = '#')\r\n           or ({$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947\r\n               {$ELSE}regparse^ in XIgnoredChars{$ENDIF})) then begin //###0.941 \\x\r\n         if regparse^ = '#' then begin // Skip eXtended comment\r\n            // find comment terminator (group of \\n and/or \\r)\r\n            while (regparse^ <> #0) and (regparse^ <> #$d) and (regparse^ <> #$a)\r\n             do inc (regparse);\r\n            while (regparse^ = #$d) or (regparse^ = #$a) // skip comment terminator\r\n             do inc (regparse); // attempt to support different type of line separators\r\n           end\r\n          else begin // Skip the blanks!\r\n            while {$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947\r\n                  {$ELSE}regparse^ in XIgnoredChars{$ENDIF}\r\n             do inc (regparse);\r\n           end;\r\n         ret := EmitNode (COMMENT); // comment\r\n        end\r\n       else begin\r\n         len := strcspn (regparse, META);\r\n         if len <= 0 then\r\n          if regparse^ <> '{' then begin\r\n             Error (reeRarseAtomInternalDisaster);\r\n             EXIT;\r\n            end\r\n           else len := strcspn (regparse + 1, META) + 1; // bad {n,m} - compile as EXATLY\r\n         ender := (regparse + len)^;\r\n         if (len > 1)\r\n            and ((ender = '*') or (ender = '+') or (ender = '?') or (ender = '{'))\r\n          then dec (len); // Back off clear of ?+*{ operand.\r\n         flagp := flagp or HASWIDTH;\r\n         if len = 1\r\n         then flagp := flagp or SIMPLE;\r\n         if (fCompModifiers and MaskModI) <> 0\r\n          then ret := EmitNode (EXACTLYCI)\r\n          else ret := EmitNode (EXACTLY);\r\n         while (len > 0)\r\n          and (((fCompModifiers and MaskModX) = 0) or (regparse^ <> '#')) do begin\r\n           if ((fCompModifiers and MaskModX) = 0) or not ( //###0.941\r\n              {$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947\r\n              {$ELSE}regparse^ in XIgnoredChars{$ENDIF} )\r\n            then EmitC (regparse^);\r\n           inc (regparse);\r\n           dec (len);\r\n          end;\r\n         EmitC (#0);\r\n        end; { of if not comment}\r\n     end; { of case else}\r\n   end; { of case}\r\n\r\n  Result := ret;\r\n end; { of function TRegExpr.ParseAtom\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetCompilerErrorPos : integer;\r\n begin\r\n  Result := 0;\r\n  if (regexpbeg = nil) or (regparse = nil)\r\n   then EXIT; // not in compiling mode ?\r\n  Result := regparse - regexpbeg;\r\n end; { of function TRegExpr.GetCompilerErrorPos\r\n--------------------------------------------------------------}\r\n\r\n\r\n{=============================================================}\r\n{===================== Matching section ======================}\r\n{=============================================================}\r\n\r\n{$IFNDEF UseSetOfChar}\r\nfunction TRegExpr.StrScanCI (s : PRegExprChar; ch : REChar) : PRegExprChar; //###0.928 - now method of TRegExpr\r\n begin\r\n  while (s^ <> #0) and (s^ <> ch) and (s^ <> InvertCase (ch))\r\n   do inc (s);\r\n  if s^ <> #0\r\n   then Result := s\r\n   else Result := nil;\r\n end; { of function TRegExpr.StrScanCI\r\n--------------------------------------------------------------}\r\n{$ENDIF}\r\n\r\nfunction TRegExpr.regrepeat (p : PRegExprChar; AMax : integer) : integer;\r\n// repeatedly match something simple, report how many\r\n var\r\n  scan : PRegExprChar;\r\n  opnd : PRegExprChar;\r\n  TheMax : integer;\r\n  {Ch,} InvCh : REChar; //###0.931\r\n  sestart, seend : PRegExprChar; //###0.936\r\n begin\r\n  Result := 0;\r\n  scan := reginput;\r\n  opnd := p + REOpSz + RENextOffSz; //OPERAND\r\n  TheMax := fInputEnd - scan;\r\n  if TheMax > AMax\r\n   then TheMax := AMax;\r\n  case PREOp (p)^ of\r\n    ANY: begin\r\n    // note - ANYML cannot be proceeded in regrepeat because can skip\r\n    // more than one char at once\r\n      Result := TheMax;\r\n      inc (scan, Result);\r\n     end;\r\n    EXACTLY: begin // in opnd can be only ONE char !!!\r\n//      Ch := opnd^; // store in register //###0.931\r\n      while (Result < TheMax) and (opnd^ = scan^) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n     end;\r\n    EXACTLYCI: begin // in opnd can be only ONE char !!!\r\n//      Ch := opnd^; // store in register //###0.931\r\n      while (Result < TheMax) and (opnd^ = scan^) do begin // prevent unneeded InvertCase //###0.931\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n      if Result < TheMax then begin //###0.931\r\n        InvCh := InvertCase (opnd^); // store in register\r\n        while (Result < TheMax) and\r\n              ((opnd^ = scan^) or (InvCh = scan^)) do begin\r\n          inc (Result);\r\n          inc (scan);\r\n         end;\r\n       end;\r\n     end;\r\n    BSUBEXP: begin //###0.936\r\n      sestart := startp [ord (opnd^)];\r\n      if sestart = nil\r\n       then EXIT;\r\n      seend := endp [ord (opnd^)];\r\n      if seend = nil\r\n       then EXIT;\r\n      REPEAT\r\n        opnd := sestart;\r\n        while opnd < seend do begin\r\n          if (scan >= fInputEnd) or (scan^ <> opnd^)\r\n           then EXIT;\r\n          inc (scan);\r\n          inc (opnd);\r\n         end;\r\n        inc (Result);\r\n        reginput := scan;\r\n      UNTIL Result >= AMax;\r\n     end;\r\n    BSUBEXPCI: begin //###0.936\r\n      sestart := startp [ord (opnd^)];\r\n      if sestart = nil\r\n       then EXIT;\r\n      seend := endp [ord (opnd^)];\r\n      if seend = nil\r\n       then EXIT;\r\n      REPEAT\r\n        opnd := sestart;\r\n        while opnd < seend do begin\r\n          if (scan >= fInputEnd) or\r\n             ((scan^ <> opnd^) and (scan^ <> InvertCase (opnd^)))\r\n           then EXIT;\r\n          inc (scan);\r\n          inc (opnd);\r\n         end;\r\n        inc (Result);\r\n        reginput := scan;\r\n      UNTIL Result >= AMax;\r\n     end;\r\n    ANYDIGIT:\r\n      while (Result < TheMax) and\r\n         (scan^ >= '0') and (scan^ <= '9') do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    NOTDIGIT:\r\n      while (Result < TheMax) and\r\n         ((scan^ < '0') or (scan^ > '9')) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    {$IFNDEF UseSetOfChar} //###0.929\r\n    ANYLETTER:\r\n      while (Result < TheMax) and\r\n       (Pos (scan^, fWordChars) > 0) //###0.940\r\n     {  ((scan^ >= 'a') and (scan^ <= 'z') !! I've forgotten (>='0') and (<='9')\r\n       or (scan^ >= 'A') and (scan^ <= 'Z') or (scan^ = '_'))} do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    NOTLETTER:\r\n      while (Result < TheMax) and\r\n       (Pos (scan^, fWordChars) <= 0)  //###0.940\r\n     {   not ((scan^ >= 'a') and (scan^ <= 'z') !! I've forgotten (>='0') and (<='9')\r\n         or (scan^ >= 'A') and (scan^ <= 'Z')\r\n         or (scan^ = '_'))} do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    ANYSPACE:\r\n      while (Result < TheMax) and\r\n         (Pos (scan^, fSpaceChars) > 0) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    NOTSPACE:\r\n      while (Result < TheMax) and\r\n         (Pos (scan^, fSpaceChars) <= 0) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    {$ENDIF}\r\n    ANYOFTINYSET: begin\r\n      while (Result < TheMax) and //!!!TinySet\r\n       ((scan^ = opnd^) or (scan^ = (opnd + 1)^)\r\n        or (scan^ = (opnd + 2)^)) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n     end;\r\n    ANYBUTTINYSET: begin\r\n      while (Result < TheMax) and //!!!TinySet\r\n       (scan^ <> opnd^) and (scan^ <> (opnd + 1)^)\r\n        and (scan^ <> (opnd + 2)^) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n     end;\r\n    {$IFDEF UseSetOfChar} //###0.929\r\n    ANYOFFULLSET: begin\r\n      while (Result < TheMax) and\r\n       (scan^ in PSetOfREChar (opnd)^) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n     end;\r\n    {$ELSE}\r\n    ANYOF:\r\n      while (Result < TheMax) and\r\n         (StrScan (opnd, scan^) <> nil) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    ANYBUT:\r\n      while (Result < TheMax) and\r\n         (StrScan (opnd, scan^) = nil) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    ANYOFCI:\r\n      while (Result < TheMax) and (StrScanCI (opnd, scan^) <> nil) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    ANYBUTCI:\r\n      while (Result < TheMax) and (StrScanCI (opnd, scan^) = nil) do begin\r\n        inc (Result);\r\n        inc (scan);\r\n       end;\r\n    {$ENDIF}\r\n    else begin // Oh dear. Called inappropriately.\r\n      Result := 0; // Best compromise.\r\n      Error (reeRegRepeatCalledInappropriately);\r\n      EXIT;\r\n     end;\r\n   end; { of case}\r\n  reginput := scan;\r\n end; { of function TRegExpr.regrepeat\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.regnext (p : PRegExprChar) : PRegExprChar;\r\n// dig the \"next\" pointer out of a node\r\n var offset : TRENextOff;\r\n begin\r\n  if p = @regdummy then begin\r\n    Result := nil;\r\n    EXIT;\r\n   end;\r\n  offset := PRENextOff (p + REOpSz)^; //###0.933 inlined NEXT\r\n  if offset = 0\r\n   then Result := nil\r\n   else Result := p + offset;\r\n end; { of function TRegExpr.regnext\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.MatchPrim (prog : PRegExprChar) : boolean;\r\n// recursively matching routine\r\n// Conceptually the strategy is simple:  check to see whether the current\r\n// node matches, call self recursively to see whether the rest matches,\r\n// and then act accordingly.  In practice we make some effort to avoid\r\n// recursion, in particular by going through \"ordinary\" nodes (that don't\r\n// need to know whether the rest of the match failed) by a loop instead of\r\n// by recursion.\r\n var\r\n  scan : PRegExprChar; // Current node.\r\n  next : PRegExprChar; // Next node.\r\n  len : integer;\r\n  opnd : PRegExprChar;\r\n  no : integer;\r\n  save : PRegExprChar;\r\n  nextch : REChar;\r\n  BracesMin, BracesMax : integer; // we use integer instead of TREBracesArg for better support */+\r\n  {$IFDEF ComplexBraces}\r\n  SavedLoopStack : array [1 .. LoopStackMax] of integer; // :(( very bad for recursion\r\n  SavedLoopStackIdx : integer; //###0.925\r\n  {$ENDIF}\r\n begin\r\n  Result := false;\r\n  scan := prog;\r\n\r\n  while scan <> nil do begin\r\n     len := PRENextOff (scan + 1)^; //###0.932 inlined regnext\r\n     if len = 0\r\n      then next := nil\r\n      else next := scan + len;\r\n\r\n     case scan^ of\r\n         NOTBOUND, //###0.943 //!!! think about UseSetOfChar !!!\r\n         BOUND:\r\n         if (scan^ = BOUND)\r\n          xor (\r\n          ((reginput = fInputStart) or (Pos ((reginput - 1)^, fWordChars) <= 0))\r\n            and (reginput^ <> #0) and (Pos (reginput^, fWordChars) > 0)\r\n           or\r\n            (reginput <> fInputStart) and (Pos ((reginput - 1)^, fWordChars) > 0)\r\n            and ((reginput^ = #0) or (Pos (reginput^, fWordChars) <= 0)))\r\n          then EXIT;\r\n\r\n         BOL: if reginput <> fInputStart\r\n               then EXIT;\r\n         EOL: if reginput^ <> #0\r\n               then EXIT;\r\n         BOLML: if reginput > fInputStart then begin\r\n            nextch := (reginput - 1)^;\r\n            if (nextch <> fLinePairedSeparatorTail)\r\n               or ((reginput - 1) <= fInputStart)\r\n               or ((reginput - 2)^ <> fLinePairedSeparatorHead)\r\n              then begin\r\n               if (nextch = fLinePairedSeparatorHead)\r\n                 and (reginput^ = fLinePairedSeparatorTail)\r\n                then EXIT; // don't stop between paired separator\r\n               if\r\n                 {$IFNDEF UniCode}\r\n                 not (nextch in fLineSeparatorsSet)\r\n                 {$ELSE}\r\n                 (pos (nextch, fLineSeparators) <= 0)\r\n                 {$ENDIF}\r\n                then EXIT;\r\n              end;\r\n           end;\r\n         EOLML: if reginput^ <> #0 then begin\r\n            nextch := reginput^;\r\n            if (nextch <> fLinePairedSeparatorHead)\r\n               or ((reginput + 1)^ <> fLinePairedSeparatorTail)\r\n             then begin\r\n               if (nextch = fLinePairedSeparatorTail)\r\n                 and (reginput > fInputStart)\r\n                 and ((reginput - 1)^ = fLinePairedSeparatorHead)\r\n                then EXIT; // don't stop between paired separator\r\n               if\r\n                 {$IFNDEF UniCode}\r\n                 not (nextch in fLineSeparatorsSet)\r\n                 {$ELSE}\r\n                 (pos (nextch, fLineSeparators) <= 0)\r\n                 {$ENDIF}\r\n                then EXIT;\r\n              end;\r\n           end;\r\n         ANY: begin\r\n            if reginput^ = #0\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         ANYML: begin //###0.941\r\n            if (reginput^ = #0)\r\n             or ((reginput^ = fLinePairedSeparatorHead)\r\n                 and ((reginput + 1)^ = fLinePairedSeparatorTail))\r\n             or {$IFNDEF UniCode} (reginput^ in fLineSeparatorsSet)\r\n                {$ELSE} (pos (reginput^, fLineSeparators) > 0) {$ENDIF}\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         ANYDIGIT: begin\r\n            if (reginput^ = #0) or (reginput^ < '0') or (reginput^ > '9')\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         NOTDIGIT: begin\r\n            if (reginput^ = #0) or ((reginput^ >= '0') and (reginput^ <= '9'))\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         {$IFNDEF UseSetOfChar} //###0.929\r\n         ANYLETTER: begin\r\n            if (reginput^ = #0) or (Pos (reginput^, fWordChars) <= 0) //###0.943\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         NOTLETTER: begin\r\n            if (reginput^ = #0) or (Pos (reginput^, fWordChars) > 0) //###0.943\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         ANYSPACE: begin\r\n            if (reginput^ = #0) or not (Pos (reginput^, fSpaceChars) > 0) //###0.943\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         NOTSPACE: begin\r\n            if (reginput^ = #0) or (Pos (reginput^, fSpaceChars) > 0) //###0.943\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         {$ENDIF}\r\n         EXACTLYCI: begin\r\n            opnd := scan + REOpSz + RENextOffSz; // OPERAND\r\n            // Inline the first character, for speed.\r\n            if (opnd^ <> reginput^)\r\n               and (InvertCase (opnd^) <> reginput^)\r\n             then EXIT;\r\n            len := strlen (opnd);\r\n            //###0.929 begin\r\n            no := len;\r\n            save := reginput;\r\n            while no > 1 do begin\r\n              inc (save);\r\n              inc (opnd);\r\n              if (opnd^ <> save^)\r\n                 and (InvertCase (opnd^) <> save^)\r\n               then EXIT;\r\n              dec (no);\r\n             end;\r\n            //###0.929 end\r\n            inc (reginput, len);\r\n           end;\r\n         EXACTLY: begin\r\n            opnd := scan + REOpSz + RENextOffSz; // OPERAND\r\n            // Inline the first character, for speed.\r\n            if opnd^ <> reginput^\r\n             then EXIT;\r\n            len := strlen (opnd);\r\n            //###0.929 begin\r\n            no := len;\r\n            save := reginput;\r\n            while no > 1 do begin\r\n              inc (save);\r\n              inc (opnd);\r\n              if opnd^ <> save^\r\n               then EXIT;\r\n              dec (no);\r\n             end;\r\n            //###0.929 end\r\n            inc (reginput, len);\r\n           end;\r\n         BSUBEXP: begin //###0.936\r\n           no := ord ((scan + REOpSz + RENextOffSz)^);\r\n           if startp [no] = nil\r\n            then EXIT;\r\n           if endp [no] = nil\r\n            then EXIT;\r\n           save := reginput;\r\n           opnd := startp [no];\r\n           while opnd < endp [no] do begin\r\n             if (save >= fInputEnd) or (save^ <> opnd^)\r\n              then EXIT;\r\n             inc (save);\r\n             inc (opnd);\r\n            end;\r\n           reginput := save;\r\n          end;\r\n         BSUBEXPCI: begin //###0.936\r\n           no := ord ((scan + REOpSz + RENextOffSz)^);\r\n           if startp [no] = nil\r\n            then EXIT;\r\n           if endp [no] = nil\r\n            then EXIT;\r\n           save := reginput;\r\n           opnd := startp [no];\r\n           while opnd < endp [no] do begin\r\n             if (save >= fInputEnd) or\r\n                ((save^ <> opnd^) and (save^ <> InvertCase (opnd^)))\r\n              then EXIT;\r\n             inc (save);\r\n             inc (opnd);\r\n            end;\r\n           reginput := save;\r\n          end;\r\n         ANYOFTINYSET: begin\r\n           if (reginput^ = #0) or //!!!TinySet\r\n             ((reginput^ <> (scan + REOpSz + RENextOffSz)^)\r\n             and (reginput^ <> (scan + REOpSz + RENextOffSz + 1)^)\r\n             and (reginput^ <> (scan + REOpSz + RENextOffSz + 2)^))\r\n            then EXIT;\r\n           inc (reginput);\r\n          end;\r\n         ANYBUTTINYSET: begin\r\n           if (reginput^ = #0) or //!!!TinySet\r\n             (reginput^ = (scan + REOpSz + RENextOffSz)^)\r\n             or (reginput^ = (scan + REOpSz + RENextOffSz + 1)^)\r\n             or (reginput^ = (scan + REOpSz + RENextOffSz + 2)^)\r\n            then EXIT;\r\n           inc (reginput);\r\n          end;\r\n         {$IFDEF UseSetOfChar} //###0.929\r\n         ANYOFFULLSET: begin\r\n           if (reginput^ = #0)\r\n              or not (reginput^ in PSetOfREChar (scan + REOpSz + RENextOffSz)^)\r\n            then EXIT;\r\n           inc (reginput);\r\n          end;\r\n         {$ELSE}\r\n         ANYOF: begin\r\n            if (reginput^ = #0) or (StrScan (scan + REOpSz + RENextOffSz, reginput^) = nil)\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         ANYBUT: begin\r\n            if (reginput^ = #0) or (StrScan (scan + REOpSz + RENextOffSz, reginput^) <> nil)\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         ANYOFCI: begin\r\n            if (reginput^ = #0) or (StrScanCI (scan + REOpSz + RENextOffSz, reginput^) = nil)\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         ANYBUTCI: begin\r\n            if (reginput^ = #0) or (StrScanCI (scan + REOpSz + RENextOffSz, reginput^) <> nil)\r\n             then EXIT;\r\n            inc (reginput);\r\n           end;\r\n         {$ENDIF}\r\n         NOTHING: ;\r\n         COMMENT: ;\r\n         BACK: ;\r\n         Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1) : begin //###0.929\r\n            no := ord (scan^) - ord (OPEN);\r\n//            save := reginput;\r\n            save := startp [no]; //###0.936\r\n            startp [no] := reginput; //###0.936\r\n            Result := MatchPrim (next);\r\n            if not Result //###0.936\r\n             then startp [no] := save;\r\n//            if Result and (startp [no] = nil)\r\n//             then startp [no] := save;\r\n             // Don't set startp if some later invocation of the same\r\n             // parentheses already has.\r\n            EXIT;\r\n           end;\r\n         Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): begin //###0.929\r\n            no := ord (scan^) - ord (CLOSE);\r\n//            save := reginput;\r\n            save := endp [no]; //###0.936\r\n            endp [no] := reginput; //###0.936\r\n            Result := MatchPrim (next);\r\n            if not Result //###0.936\r\n             then endp [no] := save;\r\n//            if Result and (endp [no] = nil)\r\n//             then endp [no] := save;\r\n             // Don't set endp if some later invocation of the same\r\n             // parentheses already has.\r\n            EXIT;\r\n           end;\r\n         BRANCH: begin\r\n            if (next^ <> BRANCH) // No choice.\r\n             then next := scan + REOpSz + RENextOffSz // Avoid recursion\r\n             else begin\r\n               REPEAT\r\n                save := reginput;\r\n                Result := MatchPrim (scan + REOpSz + RENextOffSz);\r\n                if Result\r\n                 then EXIT;\r\n                reginput := save;\r\n                scan := regnext (scan);\r\n               UNTIL (scan = nil) or (scan^ <> BRANCH);\r\n               EXIT;\r\n              end;\r\n           end;\r\n         {$IFDEF ComplexBraces}\r\n         LOOPENTRY: begin //###0.925\r\n           no := LoopStackIdx;\r\n           inc (LoopStackIdx);\r\n           if LoopStackIdx > LoopStackMax then begin\r\n             Error (reeLoopStackExceeded);\r\n             EXIT;\r\n            end;\r\n           save := reginput;\r\n           LoopStack [LoopStackIdx] := 0; // init loop counter\r\n           Result := MatchPrim (next); // execute LOOP\r\n           LoopStackIdx := no; // cleanup\r\n           if Result\r\n            then EXIT;\r\n           reginput := save;\r\n           EXIT;\r\n          end;\r\n         LOOP, LOOPNG: begin //###0.940\r\n           if LoopStackIdx <= 0 then begin\r\n             Error (reeLoopWithoutEntry);\r\n             EXIT;\r\n            end;\r\n           opnd := scan + PRENextOff (scan + REOpSz + RENextOffSz + 2 * REBracesArgSz)^;\r\n           BracesMin := PREBracesArg (scan + REOpSz + RENextOffSz)^;\r\n           BracesMax := PREBracesArg (scan + REOpSz + RENextOffSz + REBracesArgSz)^;\r\n           save := reginput;\r\n           if LoopStack [LoopStackIdx] >= BracesMin then begin // Min alredy matched - we can work\r\n              if scan^ = LOOP then begin\r\n                 // greedy way - first try to max deep of greed ;)\r\n                 if LoopStack [LoopStackIdx] < BracesMax then begin\r\n                   inc (LoopStack [LoopStackIdx]);\r\n                   no := LoopStackIdx;\r\n                   Result := MatchPrim (opnd);\r\n                   LoopStackIdx := no;\r\n                   if Result\r\n                    then EXIT;\r\n                   reginput := save;\r\n                  end;\r\n                 dec (LoopStackIdx); // Fail. May be we are too greedy? ;)\r\n                 Result := MatchPrim (next);\r\n                 if not Result\r\n                  then reginput := save;\r\n                 EXIT;\r\n                end\r\n               else begin\r\n                 // non-greedy - try just now\r\n                 Result := MatchPrim (next);\r\n                 if Result\r\n                  then EXIT\r\n                  else reginput := save; // failed - move next and try again\r\n                 if LoopStack [LoopStackIdx] < BracesMax then begin\r\n                   inc (LoopStack [LoopStackIdx]);\r\n                   no := LoopStackIdx;\r\n                   Result := MatchPrim (opnd);\r\n                   LoopStackIdx := no;\r\n                   if Result\r\n                    then EXIT;\r\n                   reginput := save;\r\n                  end;\r\n                 dec (LoopStackIdx); // Failed - back up\r\n                 EXIT;\r\n                end\r\n             end\r\n            else begin // first match a min_cnt times\r\n              inc (LoopStack [LoopStackIdx]);\r\n              no := LoopStackIdx;\r\n              Result := MatchPrim (opnd);\r\n              LoopStackIdx := no;\r\n              if Result\r\n               then EXIT;\r\n              dec (LoopStack [LoopStackIdx]);\r\n              reginput := save;\r\n              EXIT;\r\n             end;\r\n          end;\r\n         {$ENDIF}\r\n         STAR, PLUS, BRACES, STARNG, PLUSNG, BRACESNG: begin\r\n           // Lookahead to avoid useless match attempts when we know\r\n           // what character comes next.\r\n           nextch := #0;\r\n           if next^ = EXACTLY\r\n            then nextch := (next + REOpSz + RENextOffSz)^;\r\n           BracesMax := MaxInt; // infinite loop for * and + //###0.92\r\n           if (scan^ = STAR) or (scan^ = STARNG)\r\n            then BracesMin := 0  // STAR\r\n            else if (scan^ = PLUS) or (scan^ = PLUSNG)\r\n             then BracesMin := 1 // PLUS\r\n             else begin // BRACES\r\n               BracesMin := PREBracesArg (scan + REOpSz + RENextOffSz)^;\r\n               BracesMax := PREBracesArg (scan + REOpSz + RENextOffSz + REBracesArgSz)^;\r\n              end;\r\n           save := reginput;\r\n           opnd := scan + REOpSz + RENextOffSz;\r\n           if (scan^ = BRACES) or (scan^ = BRACESNG)\r\n            then inc (opnd, 2 * REBracesArgSz);\r\n\r\n           if (scan^ = PLUSNG) or (scan^ = STARNG) or (scan^ = BRACESNG) then begin\r\n             // non-greedy mode\r\n              BracesMax := regrepeat (opnd, BracesMax); // don't repeat more than BracesMax\r\n              // Now we know real Max limit to move forward (for recursion 'back up')\r\n              // In some cases it can be faster to check only Min positions first,\r\n              // but after that we have to check every position separtely instead\r\n              // of fast scannig in loop.\r\n              no := BracesMin;\r\n              while no <= BracesMax do begin\r\n                reginput := save + no;\r\n                // If it could work, try it.\r\n                if (nextch = #0) or (reginput^ = nextch) then begin\r\n                  {$IFDEF ComplexBraces}\r\n                  System.Move (LoopStack, SavedLoopStack, SizeOf (LoopStack)); //###0.925\r\n                  SavedLoopStackIdx := LoopStackIdx;\r\n                  {$ENDIF}\r\n                  if MatchPrim (next) then begin\r\n                    Result := true;\r\n                    EXIT;\r\n                   end;\r\n                  {$IFDEF ComplexBraces}\r\n                  System.Move (SavedLoopStack, LoopStack, SizeOf (LoopStack));\r\n                  LoopStackIdx := SavedLoopStackIdx;\r\n                  {$ENDIF}\r\n                 end;\r\n                inc (no); // Couldn't or didn't - move forward.\r\n               end; { of while}\r\n              EXIT;\r\n             end\r\n            else begin // greedy mode\r\n              no := regrepeat (opnd, BracesMax); // don't repeat more than max_cnt\r\n              while no >= BracesMin do begin\r\n                // If it could work, try it.\r\n                if (nextch = #0) or (reginput^ = nextch) then begin\r\n                  {$IFDEF ComplexBraces}\r\n                  System.Move (LoopStack, SavedLoopStack, SizeOf (LoopStack)); //###0.925\r\n                  SavedLoopStackIdx := LoopStackIdx;\r\n                  {$ENDIF}\r\n                  if MatchPrim (next) then begin\r\n                    Result := true;\r\n                    EXIT;\r\n                   end;\r\n                  {$IFDEF ComplexBraces}\r\n                  System.Move (SavedLoopStack, LoopStack, SizeOf (LoopStack));\r\n                  LoopStackIdx := SavedLoopStackIdx;\r\n                  {$ENDIF}\r\n                 end;\r\n                dec (no); // Couldn't or didn't - back up.\r\n                reginput := save + no;\r\n               end; { of while}\r\n              EXIT;\r\n             end;\r\n          end;\r\n         EEND: begin\r\n           Result := true;  // Success!\r\n           EXIT;\r\n          end;\r\n        else begin\r\n            Error (reeMatchPrimMemoryCorruption);\r\n            EXIT;\r\n          end;\r\n        end; { of case scan^}\r\n        scan := next;\r\n    end; { of while scan <> nil}\r\n\r\n  // We get here only if there's trouble -- normally \"case EEND\" is the\r\n  // terminating point.\r\n  Error (reeMatchPrimCorruptedPointers);\r\n end; { of function TRegExpr.MatchPrim\r\n--------------------------------------------------------------}\r\n\r\n{$IFDEF UseFirstCharSet} //###0.929\r\nprocedure TRegExpr.FillFirstCharSet (prog : PRegExprChar);\r\n var\r\n  scan : PRegExprChar; // Current node.\r\n  next : PRegExprChar; // Next node.\r\n  opnd : PRegExprChar;\r\n  min_cnt : integer;\r\n begin\r\n  scan := prog;\r\n  while scan <> nil do begin\r\n     next := regnext (scan);\r\n     case PREOp (scan)^ of\r\n         BSUBEXP, BSUBEXPCI: begin //###0.938\r\n           FirstCharSet := [#0 .. #255]; // :((( we cannot\r\n           // optimize r.e. if it starts with back reference\r\n           EXIT;\r\n          end;\r\n         BOL, BOLML: ; // EXIT; //###0.937\r\n         EOL, EOLML: begin //###0.948 was empty in 0.947, was EXIT in 0.937\r\n           Include (FirstCharSet, #0);\r\n           if ModifierM\r\n            then begin\r\n              opnd := PRegExprChar (LineSeparators);\r\n              while opnd^ <> #0 do begin\r\n                Include (FirstCharSet, opnd^);\r\n                inc (opnd);\r\n              end;\r\n            end;\r\n           EXIT;\r\n         end;\r\n         BOUND, NOTBOUND: ; //###0.943 ?!!\r\n         ANY, ANYML: begin // we can better define ANYML !!!\r\n           FirstCharSet := [#0 .. #255]; //###0.930\r\n           EXIT;\r\n          end;\r\n         ANYDIGIT: begin\r\n           FirstCharSet := FirstCharSet + ['0' .. '9'];\r\n           EXIT;\r\n          end;\r\n         NOTDIGIT: begin\r\n           FirstCharSet := FirstCharSet + ([#0 .. #255] - ['0' .. '9']); //###0.948 FirstCharSet was forgotten\r\n           EXIT;\r\n          end;\r\n         EXACTLYCI: begin\r\n           Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);\r\n           Include (FirstCharSet, InvertCase ((scan + REOpSz + RENextOffSz)^));\r\n           EXIT;\r\n          end;\r\n         EXACTLY: begin\r\n           Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);\r\n           EXIT;\r\n          end;\r\n         ANYOFFULLSET: begin\r\n           FirstCharSet := FirstCharSet + PSetOfREChar (scan + REOpSz + RENextOffSz)^;\r\n           EXIT;\r\n          end;\r\n         ANYOFTINYSET: begin\r\n           //!!!TinySet\r\n           Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);\r\n           Include (FirstCharSet, (scan + REOpSz + RENextOffSz + 1)^);\r\n           Include (FirstCharSet, (scan + REOpSz + RENextOffSz + 2)^);\r\n           // ...                                                      // up to TinySetLen\r\n           EXIT;\r\n          end;\r\n         ANYBUTTINYSET: begin\r\n           //!!!TinySet\r\n           FirstCharSet := FirstCharSet + ([#0 .. #255] - [ //###0.948 FirstCharSet was forgotten\r\n            (scan + REOpSz + RENextOffSz)^,\r\n            (scan + REOpSz + RENextOffSz + 1)^,\r\n            (scan + REOpSz + RENextOffSz + 2)^]);\r\n           // ...                                                      // up to TinySetLen\r\n           EXIT;\r\n          end;\r\n         NOTHING: ;\r\n         COMMENT: ;\r\n         BACK: ;\r\n         Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1) : begin //###0.929\r\n            FillFirstCharSet (next);\r\n            EXIT;\r\n           end;\r\n         Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): begin //###0.929\r\n            FillFirstCharSet (next);\r\n            EXIT;\r\n           end;\r\n         BRANCH: begin\r\n            if (PREOp (next)^ <> BRANCH) // No choice.\r\n             then next := scan + REOpSz + RENextOffSz // Avoid recursion.\r\n             else begin\r\n               REPEAT\r\n                FillFirstCharSet (scan + REOpSz + RENextOffSz);\r\n                scan := regnext (scan);\r\n               UNTIL (scan = nil) or (PREOp (scan)^ <> BRANCH);\r\n               EXIT;\r\n              end;\r\n           end;\r\n         {$IFDEF ComplexBraces}\r\n         LOOPENTRY: begin //###0.925\r\n//           LoopStack [LoopStackIdx] := 0; //###0.940 line removed\r\n           FillFirstCharSet (next); // execute LOOP\r\n           EXIT;\r\n          end;\r\n         LOOP, LOOPNG: begin //###0.940\r\n           opnd := scan + PRENextOff (scan + REOpSz + RENextOffSz + REBracesArgSz * 2)^;\r\n           min_cnt := PREBracesArg (scan + REOpSz + RENextOffSz)^;\r\n           FillFirstCharSet (opnd);\r\n           if min_cnt = 0\r\n            then FillFirstCharSet (next);\r\n           EXIT;\r\n          end;\r\n         {$ENDIF}\r\n         STAR, STARNG: //###0.940\r\n           FillFirstCharSet (scan + REOpSz + RENextOffSz);\r\n         PLUS, PLUSNG: begin //###0.940\r\n           FillFirstCharSet (scan + REOpSz + RENextOffSz);\r\n           EXIT;\r\n          end;\r\n         BRACES, BRACESNG: begin //###0.940\r\n           opnd := scan + REOpSz + RENextOffSz + REBracesArgSz * 2;\r\n           min_cnt := PREBracesArg (scan + REOpSz + RENextOffSz)^; // BRACES\r\n           FillFirstCharSet (opnd);\r\n           if min_cnt > 0\r\n            then EXIT;\r\n          end;\r\n         EEND: begin\r\n            FirstCharSet := [#0 .. #255]; //###0.948\r\n            EXIT;\r\n           end;\r\n        else begin\r\n            Error (reeMatchPrimMemoryCorruption);\r\n            EXIT;\r\n          end;\r\n        end; { of case scan^}\r\n        scan := next;\r\n    end; { of while scan <> nil}\r\n end; { of procedure FillFirstCharSet\r\n--------------------------------------------------------------}\r\n{$ENDIF}\r\n\r\nfunction TRegExpr.Exec (const AInputString : RegExprString) : boolean;\r\n begin\r\n  InputString := AInputString;\r\n  Result := ExecPrim (1);\r\n end; { of function TRegExpr.Exec\r\n--------------------------------------------------------------}\r\n\r\n{$IFDEF OverMeth}\r\n{$IFNDEF FPC}\r\nfunction TRegExpr.Exec : boolean;\r\n begin\r\n  Result := ExecPrim (1);\r\n end; { of function TRegExpr.Exec\r\n--------------------------------------------------------------}\r\n{$ENDIF}\r\nfunction TRegExpr.Exec (AOffset: integer) : boolean;\r\n begin\r\n  Result := ExecPrim (AOffset);\r\n end; { of function TRegExpr.Exec\r\n--------------------------------------------------------------}\r\n{$ENDIF}\r\n\r\nfunction TRegExpr.ExecPos (AOffset: integer {$IFDEF DefParam}= 1{$ENDIF}) : boolean;\r\n begin\r\n  Result := ExecPrim (AOffset);\r\n end; { of function TRegExpr.ExecPos\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ExecPrim (AOffset: integer) : boolean;\r\n procedure ClearMatchs;\r\n  // Clears matchs array\r\n  var i : integer;\r\n  begin\r\n   for i := 0 to NSUBEXP - 1 do begin\r\n     startp [i] := nil;\r\n     endp [i] := nil;\r\n    end;\r\n  end; { of procedure ClearMatchs;\r\n..............................................................}\r\n function RegMatch (str : PRegExprChar) : boolean;\r\n  // try match at specific point\r\n  begin\r\n   //###0.949 removed clearing of start\\endp\r\n   reginput := str;\r\n   Result := MatchPrim (programm + REOpSz);\r\n   if Result then begin\r\n     startp [0] := str;\r\n     endp [0] := reginput;\r\n    end;\r\n  end; { of function RegMatch\r\n..............................................................}\r\n var\r\n  s : PRegExprChar;\r\n  StartPtr: PRegExprChar;\r\n  InputLen : integer;\r\n begin\r\n  Result := false; // Be paranoid...\r\n\r\n  ClearMatchs; //###0.949\r\n  // ensure that Match cleared either if optimization tricks or some error\r\n  // will lead to leaving ExecPrim without actual search. That is\r\n  // importent for ExecNext logic and so on.\r\n\r\n  if not IsProgrammOk //###0.929\r\n   then EXIT;\r\n\r\n  // Check InputString presence\r\n  if not Assigned (fInputString) then begin\r\n    Error (reeNoInpitStringSpecified);\r\n    EXIT;\r\n   end;\r\n\r\n  InputLen := length (fInputString);\r\n\r\n  //Check that the start position is not negative\r\n  if AOffset < 1 then begin\r\n    Error (reeOffsetMustBeGreaterThen0);\r\n    EXIT;\r\n   end;\r\n  // Check that the start position is not longer than the line\r\n  // If so then exit with nothing found\r\n  if AOffset > (InputLen + 1) // for matching empty string after last char.\r\n   then EXIT;\r\n\r\n  StartPtr := fInputString + AOffset - 1;\r\n\r\n  // If there is a \"must appear\" string, look for it.\r\n  if regmust <> nil then begin\r\n    s := StartPtr;\r\n    REPEAT\r\n     s := StrScan (s, regmust [0]);\r\n     if s <> nil then begin\r\n       if StrLComp (s, regmust, regmlen) = 0\r\n        then BREAK; // Found it.\r\n       inc (s);\r\n      end;\r\n    UNTIL s = nil;\r\n    if s = nil // Not present.\r\n     then EXIT;\r\n   end;\r\n\r\n  // Mark beginning of line for ^ .\r\n  fInputStart := fInputString;\r\n\r\n  // Pointer to end of input stream - for\r\n  // pascal-style string processing (may include #0)\r\n  fInputEnd := fInputString + InputLen;\r\n\r\n  {$IFDEF ComplexBraces}\r\n  // no loops started\r\n  LoopStackIdx := 0; //###0.925\r\n  {$ENDIF}\r\n\r\n  // Simplest case:  anchored match need be tried only once.\r\n  if reganch <> #0 then begin\r\n    Result := RegMatch (StartPtr);\r\n    EXIT;\r\n   end;\r\n\r\n  // Messy cases:  unanchored match.\r\n  s := StartPtr;\r\n  if regstart <> #0 then // We know what char it must start with.\r\n    REPEAT\r\n     s := StrScan (s, regstart);\r\n     if s <> nil then begin\r\n       Result := RegMatch (s);\r\n       if Result\r\n        then EXIT\r\n        else ClearMatchs; //###0.949\r\n       inc (s);\r\n      end;\r\n    UNTIL s = nil\r\n   else begin // We don't - general case.\r\n     repeat //###0.948\r\n       {$IFDEF UseFirstCharSet}\r\n       if s^ in FirstCharSet\r\n        then Result := RegMatch (s);\r\n       {$ELSE}\r\n       Result := RegMatch (s);\r\n       {$ENDIF}\r\n       if Result or (s^ = #0) // Exit on a match or after testing the end-of-string.\r\n        then EXIT\r\n        else ClearMatchs; //###0.949\r\n       inc (s);\r\n     until false;\r\n(*  optimized and fixed by Martin Fuller - empty strings\r\n    were not allowed to pass thru in UseFirstCharSet mode\r\n     {$IFDEF UseFirstCharSet} //###0.929\r\n     while s^ <> #0 do begin\r\n       if s^ in FirstCharSet\r\n        then Result := RegMatch (s);\r\n       if Result\r\n        then EXIT;\r\n       inc (s);\r\n      end;\r\n     {$ELSE}\r\n     REPEAT\r\n      Result := RegMatch (s);\r\n      if Result\r\n       then EXIT;\r\n      inc (s);\r\n     UNTIL s^ = #0;\r\n     {$ENDIF}\r\n*)\r\n    end;\r\n  // Failure\r\n end; { of function TRegExpr.ExecPrim\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ExecNext : boolean;\r\n var offset : integer;\r\n begin\r\n  Result := false;\r\n  if not Assigned (startp[0]) or not Assigned (endp[0]) then begin\r\n    Error (reeExecNextWithoutExec);\r\n    EXIT;\r\n   end;\r\n//  Offset := MatchPos [0] + MatchLen [0];\r\n//  if MatchLen [0] = 0\r\n  Offset := endp [0] - fInputString + 1; //###0.929\r\n  if endp [0] = startp [0] //###0.929\r\n   then inc (Offset); // prevent infinite looping if empty string match r.e.\r\n  Result := ExecPrim (Offset);\r\n end; { of function TRegExpr.ExecNext\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetInputString : RegExprString;\r\n begin\r\n  if not Assigned (fInputString) then begin\r\n    Error (reeGetInputStringWithoutInputString);\r\n    EXIT;\r\n   end;\r\n  Result := fInputString;\r\n end; { of function TRegExpr.GetInputString\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.SetInputString (const AInputString : RegExprString);\r\n var\r\n  Len : integer;\r\n  i : integer;\r\n begin\r\n  // clear Match* - before next Exec* call it's undefined\r\n  for i := 0 to NSUBEXP - 1 do begin\r\n    startp [i] := nil;\r\n    endp [i] := nil;\r\n   end;\r\n\r\n  // need reallocation of input string buffer ?\r\n  Len := length (AInputString);\r\n  if Assigned (fInputString) and (Length (fInputString) <> Len) then begin\r\n    FreeMem (fInputString);\r\n    fInputString := nil;\r\n   end;\r\n  // buffer [re]allocation\r\n  if not Assigned (fInputString)\r\n   then GetMem (fInputString, (Len + 1) * SizeOf (REChar));\r\n\r\n  // copy input string into buffer\r\n  {$IFDEF UniCode}\r\n  StrPCopy (fInputString, Copy (AInputString, 1, Len)); //###0.927\r\n  {$ELSE}\r\n  StrLCopy (fInputString, PRegExprChar (AInputString), Len);\r\n  {$ENDIF}\r\n\r\n  {\r\n  fInputString : string;\r\n  fInputStart, fInputEnd : PRegExprChar;\r\n\r\n  SetInputString:\r\n  fInputString := AInputString;\r\n  UniqueString (fInputString);\r\n  fInputStart := PChar (fInputString);\r\n  Len := length (fInputString);\r\n  fInputEnd := PRegExprChar (integer (fInputStart) + Len); ??\r\n  !! startp/endp      ?\r\n  }\r\n end; { of procedure TRegExpr.SetInputString\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.SetLineSeparators (const AStr : RegExprString);\r\n begin\r\n  if AStr <> fLineSeparators then begin\r\n    fLineSeparators := AStr;\r\n    InvalidateProgramm;\r\n   end;\r\n end; { of procedure TRegExpr.SetLineSeparators\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.SetLinePairedSeparator (const AStr : RegExprString);\r\n begin\r\n  if length (AStr) = 2 then begin\r\n     if AStr [1] = AStr [2] then begin\r\n      // it's impossible for our 'one-point' checking to support\r\n      // two chars separator for identical chars\r\n       Error (reeBadLinePairedSeparator);\r\n       EXIT;\r\n      end;\r\n     if not fLinePairedSeparatorAssigned\r\n      or (AStr [1] <> fLinePairedSeparatorHead)\r\n      or (AStr [2] <> fLinePairedSeparatorTail) then begin\r\n       fLinePairedSeparatorAssigned := true;\r\n       fLinePairedSeparatorHead := AStr [1];\r\n       fLinePairedSeparatorTail := AStr [2];\r\n       InvalidateProgramm;\r\n      end;\r\n    end\r\n   else if length (AStr) = 0 then begin\r\n     if fLinePairedSeparatorAssigned then begin\r\n       fLinePairedSeparatorAssigned := false;\r\n       InvalidateProgramm;\r\n      end;\r\n    end\r\n   else Error (reeBadLinePairedSeparator);\r\n end; { of procedure TRegExpr.SetLinePairedSeparator\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.GetLinePairedSeparator : RegExprString;\r\n begin\r\n  if fLinePairedSeparatorAssigned then begin\r\n     {$IFDEF UniCode}\r\n     // Here is some UniCode 'magic'\r\n     // If You do know better decision to concatenate\r\n     // two WideChars, please, let me know!\r\n     Result := fLinePairedSeparatorHead; //###0.947\r\n     Result := Result + fLinePairedSeparatorTail;\r\n     {$ELSE}\r\n     Result := fLinePairedSeparatorHead + fLinePairedSeparatorTail;\r\n     {$ENDIF}\r\n    end\r\n   else Result := '';\r\n end; { of function TRegExpr.GetLinePairedSeparator\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.Substitute (const ATemplate : RegExprString) : RegExprString;\r\n// perform substitutions after a regexp match\r\n// completely rewritten in 0.929\r\n var\r\n  TemplateLen : integer;\r\n  TemplateBeg, TemplateEnd : PRegExprChar;\r\n  p, p0, ResultPtr : PRegExprChar;\r\n  ResultLen : integer;\r\n  n : integer;\r\n  Ch : REChar;\r\n function ParseVarName (var APtr : PRegExprChar) : integer;\r\n  // extract name of variable (digits, may be enclosed with\r\n  // curly braces) from APtr^, uses TemplateEnd !!!\r\n  const\r\n   Digits = ['0' .. '9'];\r\n  var\r\n   p : PRegExprChar;\r\n   Delimited : boolean;\r\n  begin\r\n   Result := 0;\r\n   p := APtr;\r\n   Delimited := (p < TemplateEnd) and (p^ = '{');\r\n   if Delimited\r\n    then inc (p); // skip left curly brace\r\n   if (p < TemplateEnd) and (p^ = '&')\r\n    then inc (p) // this is '$&' or '${&}'\r\n    else\r\n     while (p < TemplateEnd) and\r\n      {$IFDEF UniCode} //###0.935\r\n      (ord (p^) < 256) and (char (p^) in Digits)\r\n      {$ELSE}\r\n      (p^ in Digits)\r\n      {$ENDIF}\r\n       do begin\r\n       Result := Result * 10 + (ord (p^) - ord ('0')); //###0.939\r\n       inc (p);\r\n      end;\r\n   if Delimited then\r\n    if (p < TemplateEnd) and (p^ = '}')\r\n     then inc (p) // skip right curly brace\r\n     else p := APtr; // isn't properly terminated\r\n   if p = APtr\r\n    then Result := -1; // no valid digits found or no right curly brace\r\n   APtr := p;\r\n  end;\r\n begin\r\n  // Check programm and input string\r\n  if not IsProgrammOk\r\n   then EXIT;\r\n  if not Assigned (fInputString) then begin\r\n    Error (reeNoInpitStringSpecified);\r\n    EXIT;\r\n   end;\r\n  // Prepare for working\r\n  TemplateLen := length (ATemplate);\r\n  if TemplateLen = 0 then begin // prevent nil pointers\r\n    Result := '';\r\n    EXIT;\r\n   end;\r\n  TemplateBeg := pointer (ATemplate);\r\n  TemplateEnd := TemplateBeg + TemplateLen;\r\n  // Count result length for speed optimization.\r\n  ResultLen := 0;\r\n  p := TemplateBeg;\r\n  while p < TemplateEnd do begin\r\n    Ch := p^;\r\n    inc (p);\r\n    if Ch = '$'\r\n     then n := ParseVarName (p)\r\n     else n := -1;\r\n    if n >= 0 then begin\r\n       if (n < NSUBEXP) and Assigned (startp [n]) and Assigned (endp [n])\r\n        then inc (ResultLen, endp [n] - startp [n]);\r\n      end\r\n     else begin\r\n       if (Ch = EscChar) and (p < TemplateEnd)\r\n        then inc (p); // quoted or special char followed\r\n       inc (ResultLen);\r\n      end;\r\n   end;\r\n  // Get memory. We do it once and it significant speed up work !\r\n  if ResultLen = 0 then begin\r\n    Result := '';\r\n    EXIT;\r\n   end;\r\n  SetString (Result, nil, ResultLen);\r\n  // Fill Result\r\n  ResultPtr := pointer (Result);\r\n  p := TemplateBeg;\r\n  while p < TemplateEnd do begin\r\n    Ch := p^;\r\n    inc (p);\r\n    if Ch = '$'\r\n     then n := ParseVarName (p)\r\n     else n := -1;\r\n    if n >= 0 then begin\r\n       p0 := startp [n];\r\n       if (n < NSUBEXP) and Assigned (p0) and Assigned (endp [n]) then\r\n        while p0 < endp [n] do begin\r\n          ResultPtr^ := p0^;\r\n          inc (ResultPtr);\r\n          inc (p0);\r\n         end;\r\n      end\r\n     else begin\r\n       if (Ch = EscChar) and (p < TemplateEnd) then begin // quoted or special char followed\r\n         Ch := p^;\r\n         inc (p);\r\n        end;\r\n       ResultPtr^ := Ch;\r\n       inc (ResultPtr);\r\n      end;\r\n   end;\r\n end; { of function TRegExpr.Substitute\r\n--------------------------------------------------------------}\r\n\r\nprocedure TRegExpr.Split (AInputStr : RegExprString; APieces : TStrings);\r\n var PrevPos : integer;\r\n begin\r\n  PrevPos := 1;\r\n  if Exec (AInputStr) then\r\n   REPEAT\r\n    APieces.Add (System.Copy (AInputStr, PrevPos, MatchPos [0] - PrevPos));\r\n    PrevPos := MatchPos [0] + MatchLen [0];\r\n   UNTIL not ExecNext;\r\n  APieces.Add (System.Copy (AInputStr, PrevPos, MaxInt)); // Tail\r\n end; { of procedure TRegExpr.Split\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.Replace (AInputStr : RegExprString; const AReplaceStr : RegExprString;\r\n      AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString;\r\n var\r\n  PrevPos : integer;\r\n begin\r\n  Result := '';\r\n  PrevPos := 1;\r\n  if Exec (AInputStr) then\r\n   REPEAT\r\n    Result := Result + System.Copy (AInputStr, PrevPos,\r\n      MatchPos [0] - PrevPos);\r\n    if AUseSubstitution //###0.946\r\n    then Result := Result + Substitute (AReplaceStr)\r\n    else Result := Result + AReplaceStr;\r\n    PrevPos := MatchPos [0] + MatchLen [0];\r\n   UNTIL not ExecNext;\r\n  Result := Result + System.Copy (AInputStr, PrevPos, MaxInt); // Tail\r\n end; { of function TRegExpr.Replace\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.ReplaceEx (AInputStr : RegExprString;\r\n      AReplaceFunc : TRegExprReplaceFunction)\r\n     : RegExprString;\r\n var\r\n  PrevPos : integer;\r\n begin\r\n  Result := '';\r\n  PrevPos := 1;\r\n  if Exec (AInputStr) then\r\n   REPEAT\r\n    Result := Result + System.Copy (AInputStr, PrevPos,\r\n      MatchPos [0] - PrevPos)\r\n     + AReplaceFunc (Self);\r\n    PrevPos := MatchPos [0] + MatchLen [0];\r\n   UNTIL not ExecNext;\r\n  Result := Result + System.Copy (AInputStr, PrevPos, MaxInt); // Tail\r\n end; { of function TRegExpr.ReplaceEx\r\n--------------------------------------------------------------}\r\n\r\n\r\n{$IFDEF OverMeth}\r\nfunction TRegExpr.Replace (AInputStr : RegExprString;\r\n      AReplaceFunc : TRegExprReplaceFunction)\r\n     : RegExprString;\r\n begin\r\n  ReplaceEx (AInputStr, AReplaceFunc);\r\n end; { of function TRegExpr.Replace\r\n--------------------------------------------------------------}\r\n{$ENDIF}\r\n\r\n{=============================================================}\r\n{====================== Debug section ========================}\r\n{=============================================================}\r\n\r\n{$IFDEF RegExpPCodeDump}\r\nfunction TRegExpr.DumpOp (op : TREOp) : RegExprString;\r\n// printable representation of opcode\r\n begin\r\n  case op of\r\n    BOL:          Result := 'BOL';\r\n    EOL:          Result := 'EOL';\r\n    BOLML:        Result := 'BOLML';\r\n    EOLML:        Result := 'EOLML';\r\n    BOUND:        Result := 'BOUND'; //###0.943\r\n    NOTBOUND:     Result := 'NOTBOUND'; //###0.943\r\n    ANY:          Result := 'ANY';\r\n    ANYML:        Result := 'ANYML'; //###0.941\r\n    ANYLETTER:    Result := 'ANYLETTER';\r\n    NOTLETTER:    Result := 'NOTLETTER';\r\n    ANYDIGIT:     Result := 'ANYDIGIT';\r\n    NOTDIGIT:     Result := 'NOTDIGIT';\r\n    ANYSPACE:     Result := 'ANYSPACE';\r\n    NOTSPACE:     Result := 'NOTSPACE';\r\n    ANYOF:        Result := 'ANYOF';\r\n    ANYBUT:       Result := 'ANYBUT';\r\n    ANYOFCI:      Result := 'ANYOF/CI';\r\n    ANYBUTCI:     Result := 'ANYBUT/CI';\r\n    BRANCH:       Result := 'BRANCH';\r\n    EXACTLY:      Result := 'EXACTLY';\r\n    EXACTLYCI:    Result := 'EXACTLY/CI';\r\n    NOTHING:      Result := 'NOTHING';\r\n    COMMENT:      Result := 'COMMENT';\r\n    BACK:         Result := 'BACK';\r\n    EEND:         Result := 'END';\r\n    BSUBEXP:      Result := 'BSUBEXP';\r\n    BSUBEXPCI:    Result := 'BSUBEXP/CI';\r\n    Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1): //###0.929\r\n                  Result := Format ('OPEN[%d]', [ord (op) - ord (OPEN)]);\r\n    Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): //###0.929\r\n                  Result := Format ('CLOSE[%d]', [ord (op) - ord (CLOSE)]);\r\n    STAR:         Result := 'STAR';\r\n    PLUS:         Result := 'PLUS';\r\n    BRACES:       Result := 'BRACES';\r\n    {$IFDEF ComplexBraces}\r\n    LOOPENTRY:    Result := 'LOOPENTRY'; //###0.925\r\n    LOOP:         Result := 'LOOP'; //###0.925\r\n    LOOPNG:       Result := 'LOOPNG'; //###0.940\r\n    {$ENDIF}\r\n    ANYOFTINYSET: Result:= 'ANYOFTINYSET';\r\n    ANYBUTTINYSET:Result:= 'ANYBUTTINYSET';\r\n    {$IFDEF UseSetOfChar} //###0.929\r\n    ANYOFFULLSET: Result:= 'ANYOFFULLSET';\r\n    {$ENDIF}\r\n    STARNG:       Result := 'STARNG'; //###0.940\r\n    PLUSNG:       Result := 'PLUSNG'; //###0.940\r\n    BRACESNG:     Result := 'BRACESNG'; //###0.940\r\n    else Error (reeDumpCorruptedOpcode);\r\n   end; {of case op}\r\n  Result := ':' + Result;\r\n end; { of function TRegExpr.DumpOp\r\n--------------------------------------------------------------}\r\n\r\nfunction TRegExpr.Dump : RegExprString;\r\n// dump a regexp in vaguely comprehensible form\r\n var\r\n  s : PRegExprChar;\r\n  op : TREOp; // Arbitrary non-END op.\r\n  next : PRegExprChar;\r\n  i : integer;\r\n  Diff : integer;\r\n{$IFDEF UseSetOfChar} //###0.929\r\n  Ch : REChar;\r\n{$ENDIF}\r\n begin\r\n  if not IsProgrammOk //###0.929\r\n   then EXIT;\r\n\r\n  op := EXACTLY;\r\n  Result := '';\r\n  s := programm + REOpSz;\r\n  while op <> EEND do begin // While that wasn't END last time...\r\n     op := s^;\r\n     Result := Result + Format ('%2d%s', [s - programm, DumpOp (s^)]); // Where, what.\r\n     next := regnext (s);\r\n     if next = nil // Next ptr.\r\n      then Result := Result + ' (0)'\r\n      else begin\r\n        if next > s //###0.948 PWideChar subtraction workaround (see comments in Tail method for details)\r\n         then Diff := next - s\r\n         else Diff := - (s - next);\r\n        Result := Result + Format (' (%d) ', [(s - programm) + Diff]);\r\n       end;\r\n     inc (s, REOpSz + RENextOffSz);\r\n     if (op = ANYOF) or (op = ANYOFCI) or (op = ANYBUT) or (op = ANYBUTCI)\r\n        or (op = EXACTLY) or (op = EXACTLYCI) then begin\r\n         // Literal string, where present.\r\n         while s^ <> #0 do begin\r\n           Result := Result + s^;\r\n           inc (s);\r\n          end;\r\n         inc (s);\r\n      end;\r\n     if (op = ANYOFTINYSET) or (op = ANYBUTTINYSET) then begin\r\n       for i := 1 to TinySetLen do begin\r\n         Result := Result + s^;\r\n         inc (s);\r\n        end;\r\n      end;\r\n     if (op = BSUBEXP) or (op = BSUBEXPCI) then begin\r\n       Result := Result + ' \\' + IntToStr (Ord (s^));\r\n       inc (s);\r\n      end;\r\n     {$IFDEF UseSetOfChar} //###0.929\r\n     if op = ANYOFFULLSET then begin\r\n       for Ch := #0 to #255 do\r\n        if Ch in PSetOfREChar (s)^ then\r\n         if Ch < ' '\r\n          then Result := Result + '#' + IntToStr (Ord (Ch)) //###0.936\r\n          else Result := Result + Ch;\r\n       inc (s, SizeOf (TSetOfREChar));\r\n      end;\r\n     {$ENDIF}\r\n     if (op = BRACES) or (op = BRACESNG) then begin //###0.941\r\n       // show min/max argument of BRACES operator\r\n       Result := Result + Format ('{%d,%d}', [PREBracesArg (s)^, PREBracesArg (s + REBracesArgSz)^]);\r\n       inc (s, REBracesArgSz * 2);\r\n      end;\r\n     {$IFDEF ComplexBraces}\r\n     if (op = LOOP) or (op = LOOPNG) then begin //###0.940\r\n       Result := Result + Format (' -> (%d) {%d,%d}', [\r\n        (s - programm - (REOpSz + RENextOffSz)) + PRENextOff (s + 2 * REBracesArgSz)^,\r\n        PREBracesArg (s)^, PREBracesArg (s + REBracesArgSz)^]);\r\n       inc (s, 2 * REBracesArgSz + RENextOffSz);\r\n      end;\r\n     {$ENDIF}\r\n     Result := Result + #$d#$a;\r\n   end; { of while}\r\n\r\n  // Header fields of interest.\r\n\r\n  if regstart <> #0\r\n   then Result := Result + 'start ' + regstart;\r\n  if reganch <> #0\r\n   then Result := Result + 'anchored ';\r\n  if regmust <> nil\r\n   then Result := Result + 'must have ' + regmust;\r\n  {$IFDEF UseFirstCharSet} //###0.929\r\n  Result := Result + #$d#$a'FirstCharSet:';\r\n  for Ch := #0 to #255 do\r\n   if Ch in FirstCharSet\r\n    then begin\r\n      if Ch < ' '\r\n       then Result := Result + '#' + IntToStr(Ord(Ch)) //###0.948\r\n       else Result := Result + Ch;\r\n    end;\r\n  {$ENDIF}\r\n  Result := Result + #$d#$a;\r\n end; { of function TRegExpr.Dump\r\n--------------------------------------------------------------}\r\n{$ENDIF}\r\n\r\n{$IFDEF reRealExceptionAddr}\r\n{$OPTIMIZATION ON}\r\n// ReturnAddr works correctly only if compiler optimization is ON\r\n// I placed this method at very end of unit because there are no\r\n// way to restore compiler optimization flag ...\r\n{$ENDIF}\r\nprocedure TRegExpr.Error (AErrorID : integer);\r\n{$IFDEF reRealExceptionAddr}\r\n function ReturnAddr : pointer; //###0.938\r\n  asm\r\n   mov  eax,[ebp+4]\r\n  end;\r\n{$ENDIF}\r\n var\r\n  e : ERegExpr;\r\n begin\r\n  fLastError := AErrorID; // dummy stub - useless because will raise exception\r\n  if AErrorID < 1000 // compilation error ?\r\n   then e := ERegExpr.Create (ErrorMsg (AErrorID) // yes - show error pos\r\n             + ' (pos ' + IntToStr (CompilerErrorPos) + ')')\r\n   else e := ERegExpr.Create (ErrorMsg (AErrorID));\r\n  e.ErrorCode := AErrorID;\r\n  e.CompilerErrorPos := CompilerErrorPos;\r\n  raise e\r\n   {$IFDEF reRealExceptionAddr}\r\n   At ReturnAddr; //###0.938\r\n   {$ENDIF}\r\n end; { of procedure TRegExpr.Error\r\n--------------------------------------------------------------}\r\n\r\n(*\r\n  PCode persistence:\r\n   FirstCharSet\r\n   programm, regsize\r\n   regstart // -> programm\r\n   reganch // -> programm\r\n   regmust, regmlen // -> programm\r\n   fExprIsCompiled\r\n*)\r\n\r\n// be carefull - placed here code will be always compiled with\r\n// compiler optimization flag\r\n\r\n{$IFDEF FPC}\r\ninitialization\r\n RegExprInvertCaseFunction := TRegExpr.InvertCaseFunction;\r\n\r\n{$ENDIF}\r\nend.\r\n\r\n"
  },
  {
    "path": "impls/objpascal/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/objpascal/step0_repl.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses mal_readline;\n\nvar\n    Repl_Env: string = '';\n    Line : string;\n\n// read\nfunction READ(const Str: string) : string;\nbegin\n    READ := Str;\nend;\n\n// eval\nfunction EVAL(Ast: string; Env: string) : string;\nbegin\n    EVAL := Ast;\nend;\n\n// print\nfunction PRINT(Exp: string) : string;\nbegin\n    PRINT := Exp;\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nbegin\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step1_read_print.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     mal_readline,\n     mal_types,\n     reader,\n     printer;\n\nvar\n    Repl_Env : string = '';\n    Line     : string;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\nfunction EVAL(Ast: TMal; Env: string) : TMal;\nbegin\n    EVAL := Ast;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nbegin\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step2_eval.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer;\n\ntype\n    TEnv = specialize TFPGMap<string,TMal>;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    Sym              : string;\n    Cond   : TMal;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    OldDict, NewDict : TMalDict;\n    I                : longint;\nbegin\n    // WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        Sym := (Ast as TMalSymbol).Val;\n        if Env.IndexOf(Sym) < 0 then\n            raise Exception.Create('''' + Sym + ''' not found')\n        else\n            Exit(Env[Sym]);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Arr := (Ast as TMalList).Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    Cond := EVAL(Arr[0], Env);\n    Args := copy(Arr, 1, Length(Arr) - 1);\n    if Cond is TMalFunc then\n    begin\n        Fn := (Cond as TMalFunc);\n        for I := 0 to Length(Args) - 1 do\n            Args[I]:= EVAL(Args[I], Env);\n        EVAL := Fn.Val(Args)\n    end\n    else\n        raise Exception.Create('invalid apply');\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction add(Args: TMalArray) : TMal;\nbegin\n    add := TMalInt.Create((Args[0] as TMalInt).Val +\n                          (Args[1] as TMalInt).Val);\nend;\nfunction subtract(Args: TMalArray) : TMal;\nbegin\n    subtract := TMalInt.Create((Args[0] as TMalInt).Val -\n                               (Args[1] as TMalInt).Val);\nend;\nfunction multiply(Args: TMalArray) : TMal;\nbegin\n    multiply := TMalInt.Create((Args[0] as TMalInt).Val *\n                               (Args[1] as TMalInt).Val);\nend;\nfunction divide(Args: TMalArray) : TMal;\nbegin\n    divide := TMalInt.Create((Args[0] as TMalInt).Val div\n                             (Args[1] as TMalInt).Val);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    Repl_Env.Add('+', TMalFunc.Create(@add));\n    Repl_Env.Add('-', TMalFunc.Create(@subtract));\n    Repl_Env.Add('*', TMalFunc.Create(@multiply));\n    Repl_Env.Add('/', TMalFunc.Create(@divide));\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step3_env.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    Fn     : TMalCallable;\n    Args   : TMalArray;\n    OldDict, NewDict : TMalDict;\n    I                : longint;\nbegin\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Arr := (Ast as TMalList).Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        EVAL := Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            EVAL := EVAL(Arr[2], LetEnv);\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := (Cond as TMalFunc).Val;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                EVAL := Fn(Args)\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction add(Args: TMalArray) : TMal;\nbegin\n    add := TMalInt.Create((Args[0] as TMalInt).Val +\n                          (Args[1] as TMalInt).Val);\nend;\nfunction subtract(Args: TMalArray) : TMal;\nbegin\n    subtract := TMalInt.Create((Args[0] as TMalInt).Val -\n                               (Args[1] as TMalInt).Val);\nend;\nfunction multiply(Args: TMalArray) : TMal;\nbegin\n    multiply := TMalInt.Create((Args[0] as TMalInt).Val *\n                               (Args[1] as TMalInt).Val);\nend;\nfunction divide(Args: TMalArray) : TMal;\nbegin\n    divide := TMalInt.Create((Args[0] as TMalInt).Val div\n                             (Args[1] as TMalInt).Val);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    Repl_Env.Add(TMalSymbol.Create('+'), TMalFunc.Create(@add));\n    Repl_Env.Add(TMalSymbol.Create('-'), TMalFunc.Create(@subtract));\n    Repl_Env.Add(TMalSymbol.Create('*'), TMalFunc.Create(@multiply));\n    Repl_Env.Add(TMalSymbol.Create('/'), TMalFunc.Create(@divide));\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step4_if_fn_do.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    FnEnv  : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        EVAL := Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            EVAL := EVAL(Arr[2], LetEnv);\n        end;\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n           EVAL := EVAL(Arr[Length(Arr)-1], Env);\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    EVAL := EVAL(Arr[3], Env)\n                else\n                    EVAL := TMalNil.Create\n            else\n                EVAL := EVAL(Arr[2], Env);\n        end;\n    'fn*':\n        begin\n            EVAL := TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList))\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    EVAL := Fn.Val(Args)\n                else\n                begin\n                    FnEnv := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    EVAL := EVAL(Fn.Ast, FnEnv);\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step5_tco.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n  while true do\n  begin\n\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        Exit(Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV)));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            Env := LetEnv;\n            Ast := Arr[2]; // TCO\n        end;\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n            Ast := Arr[Length(Arr)-1]; // TCO\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    Ast := Arr[3] // TCO\n                else\n                    Exit(TMalNil.Create)\n            else\n                Ast := Arr[2]; // TCO\n        end;\n    'fn*':\n        begin\n            Exit(TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList)));\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    Exit(Fn.Val(Args))\n                else\n                begin\n                    Env := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    Ast := Fn.Ast; // TCO\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\n  end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step6_file.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     math,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n    CmdArgs  : TMalArray;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n  while true do\n  begin\n\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        Exit(Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV)));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            Env := LetEnv;\n            Ast := Arr[2]; // TCO\n        end;\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n            Ast := Arr[Length(Arr)-1]; // TCO\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    Ast := Arr[3] // TCO\n                else\n                    Exit(TMalNil.Create)\n            else\n                Ast := Arr[2]; // TCO\n        end;\n    'fn*':\n        begin\n            Exit(TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList)));\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    Exit(Fn.Val(Args))\n                else\n                begin\n                    Env := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    Ast := Fn.Ast; // TCO\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\n  end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction do_eval(Args : TMalArray) : TMal;\nbegin\n    do_eval := EVAL(Args[0], Repl_Env);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    core.EVAL := @EVAL;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n    Repl_Env.Add(TMalSymbol.Create('eval'), TMalFunc.Create(@do_eval));\n    SetLength(CmdArgs, Max(0, ParamCount-1));\n    for I := 2 to ParamCount do\n        CmdArgs[I-2] := TMalString.Create(ParamStr(I));\n    Repl_Env.Add(TMalSymbol.Create('*ARGV*'), TMalList.Create(CmdArgs));\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n    REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n\n    if ParamCount >= 1 then\n    begin\n        REP('(load-file \"' + ParamStr(1) + '\")');\n        ExitCode := 0;\n        Exit;\n    end;\n\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step7_quote.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     math,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n    CmdArgs  : TMalArray;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\n\nfunction starts_with(Ast: TMal; Sym: String) : Boolean;\nvar\n   Arr : TMalArray;\n   A0  : TMal;\nbegin\n   if Ast.ClassType <> TMalList then Exit (False);\n   Arr := (Ast as TMalList).Val;\n   if Length (Arr) = 0 then Exit (False);\n   A0 := Arr [0];\n   starts_with := (A0.ClassType = TMalSymbol) and ((A0 as TMalSymbol).Val = Sym);\nend;\n\nfunction quasiquote(Ast: TMal) : TMal;\nvar\n    Arr      : TMalArray;\n    Res, Elt : TMal;\n    I        : longint;\nbegin\n    if Ast is TMalSymbol or Ast is TMalHashMap then\n        Exit(_list(TMalSymbol.Create('quote'), Ast));\n\n    if not (Ast is TMalList) then\n        Exit(Ast);\n\n    Arr := (Ast as TMalList).Val;\n    if starts_with (Ast, 'unquote') then Exit(Arr[1]);\n\n    Res := _list();\n    for I := 1 to Length(Arr) do\n    begin\n        Elt := Arr [Length(Arr) - I];\n        if starts_with (Elt, 'splice-unquote') then\n            Res := _list(TMalSymbol.Create('concat'), (Elt as TMalList).Val[1], Res)\n        else\n            Res := _list(TMalSymbol.Create('cons'), quasiquote (Elt), Res);\n    end;\n    if Ast.ClassType <> TMalList then\n        Exit(_list(TMalSymbol.Create('vec'), Res))\n    else\n        Exit(Res);\nend;\n\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n  while true do\n  begin\n\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        Exit(Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV)));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            Env := LetEnv;\n            Ast := Arr[2]; // TCO\n        end;\n    'quote':\n        Exit(Arr[1]);\n    'quasiquote':\n        Ast := quasiquote(Arr[1]);\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n            Ast := Arr[Length(Arr)-1]; // TCO\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    Ast := Arr[3] // TCO\n                else\n                    Exit(TMalNil.Create)\n            else\n                Ast := Arr[2]; // TCO\n        end;\n    'fn*':\n        begin\n            Exit(TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList)));\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    Exit(Fn.Val(Args))\n                else\n                begin\n                    Env := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    Ast := Fn.Ast; // TCO\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\n  end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction do_eval(Args : TMalArray) : TMal;\nbegin\n    do_eval := EVAL(Args[0], Repl_Env);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    core.EVAL := @EVAL;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n    Repl_Env.Add(TMalSymbol.Create('eval'), TMalFunc.Create(@do_eval));\n    SetLength(CmdArgs, Max(0, ParamCount-1));\n    for I := 2 to ParamCount do\n        CmdArgs[I-2] := TMalString.Create(ParamStr(I));\n    Repl_Env.Add(TMalSymbol.Create('*ARGV*'), TMalList.Create(CmdArgs));\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n    REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n\n    if ParamCount >= 1 then\n    begin\n        REP('(load-file \"' + ParamStr(1) + '\")');\n        ExitCode := 0;\n        Exit;\n    end;\n\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step8_macros.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     math,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n    CmdArgs  : TMalArray;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\n\nfunction starts_with(Ast: TMal; Sym: String) : Boolean;\nvar\n   Arr : TMalArray;\n   A0  : TMal;\nbegin\n   if Ast.ClassType <> TMalList then Exit (False);\n   Arr := (Ast as TMalList).Val;\n   if Length (Arr) = 0 then Exit (False);\n   A0 := Arr [0];\n   starts_with := (A0.ClassType = TMalSymbol) and ((A0 as TMalSymbol).Val = Sym);\nend;\n\nfunction quasiquote(Ast: TMal) : TMal;\nvar\n    Arr      : TMalArray;\n    Res, Elt : TMal;\n    I        : longint;\nbegin\n    if Ast is TMalSymbol or Ast is TMalHashMap then\n        Exit(_list(TMalSymbol.Create('quote'), Ast));\n\n    if not (Ast is TMalList) then\n        Exit(Ast);\n\n    Arr := (Ast as TMalList).Val;\n    if starts_with (Ast, 'unquote') then Exit(Arr[1]);\n\n    Res := _list();\n    for I := 1 to Length(Arr) do\n    begin\n        Elt := Arr [Length(Arr) - I];\n        if starts_with (Elt, 'splice-unquote') then\n            Res := _list(TMalSymbol.Create('concat'), (Elt as TMalList).Val[1], Res)\n        else\n            Res := _list(TMalSymbol.Create('cons'), quasiquote (Elt), Res);\n    end;\n    if Ast.ClassType <> TMalList then\n        Exit(_list(TMalSymbol.Create('vec'), Res))\n    else\n        Exit(Res);\nend;\n\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n  while true do\n  begin\n\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        Exit(Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV)));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            Env := LetEnv;\n            Ast := Arr[2]; // TCO\n        end;\n    'quote':\n        Exit(Arr[1]);\n    'quasiquote':\n        Ast := quasiquote(Arr[1]);\n    'defmacro!':\n    begin\n        Fn := EVAL(Arr[2], ENV) as TMalFunc;\n        Fn := TMalFunc.Clone(Fn);\n        Fn.isMacro := true;\n        Exit(Env.Add((Arr[1] as TMalSymbol), Fn));\n    end;\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n            Ast := Arr[Length(Arr)-1]; // TCO\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    Ast := Arr[3] // TCO\n                else\n                    Exit(TMalNil.Create)\n            else\n                Ast := Arr[2]; // TCO\n        end;\n    'fn*':\n        begin\n            Exit(TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList)));\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                if Fn.isMacro then\n                begin\n                    if Fn.Ast =nil then\n                        Ast := Fn.Val(Args)\n                    else\n                        Ast := EVAL(Fn.Ast, Tenv.Create(Fn.Env, Fn.Params, Args));\n                    continue; // TCO\n                end;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    Exit(Fn.Val(Args))\n                else\n                begin\n                    Env := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    Ast := Fn.Ast; // TCO\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\n  end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction do_eval(Args : TMalArray) : TMal;\nbegin\n    do_eval := EVAL(Args[0], Repl_Env);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    core.EVAL := @EVAL;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n    Repl_Env.Add(TMalSymbol.Create('eval'), TMalFunc.Create(@do_eval));\n    SetLength(CmdArgs, Max(0, ParamCount-1));\n    for I := 2 to ParamCount do\n        CmdArgs[I-2] := TMalString.Create(ParamStr(I));\n    Repl_Env.Add(TMalSymbol.Create('*ARGV*'), TMalList.Create(CmdArgs));\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n    REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n    REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))');\n\n\n    if ParamCount >= 1 then\n    begin\n        REP('(load-file \"' + ParamStr(1) + '\")');\n        ExitCode := 0;\n        Exit;\n    end;\n\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/step9_try.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     math,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n    CmdArgs  : TMalArray;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\n\nfunction starts_with(Ast: TMal; Sym: String) : Boolean;\nvar\n   Arr : TMalArray;\n   A0  : TMal;\nbegin\n   if Ast.ClassType <> TMalList then Exit (False);\n   Arr := (Ast as TMalList).Val;\n   if Length (Arr) = 0 then Exit (False);\n   A0 := Arr [0];\n   starts_with := (A0.ClassType = TMalSymbol) and ((A0 as TMalSymbol).Val = Sym);\nend;\n\nfunction quasiquote(Ast: TMal) : TMal;\nvar\n    Arr      : TMalArray;\n    Res, Elt : TMal;\n    I        : longint;\nbegin\n    if Ast is TMalSymbol or Ast is TMalHashMap then\n        Exit(_list(TMalSymbol.Create('quote'), Ast));\n\n    if not (Ast is TMalList) then\n        Exit(Ast);\n\n    Arr := (Ast as TMalList).Val;\n    if starts_with (Ast, 'unquote') then Exit(Arr[1]);\n\n    Res := _list();\n    for I := 1 to Length(Arr) do\n    begin\n        Elt := Arr [Length(Arr) - I];\n        if starts_with (Elt, 'splice-unquote') then\n            Res := _list(TMalSymbol.Create('concat'), (Elt as TMalList).Val[1], Res)\n        else\n            Res := _list(TMalSymbol.Create('cons'), quasiquote (Elt), Res);\n    end;\n    if Ast.ClassType <> TMalList then\n        Exit(_list(TMalSymbol.Create('vec'), Res))\n    else\n        Exit(Res);\nend;\n\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    Err    : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n  while true do\n  begin\n\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        Exit(Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV)));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            Env := LetEnv;\n            Ast := Arr[2]; // TCO\n        end;\n    'quote':\n        Exit(Arr[1]);\n    'quasiquote':\n        Ast := quasiquote(Arr[1]);\n    'defmacro!':\n    begin\n        Fn := EVAL(Arr[2], ENV) as TMalFunc;\n        Fn := TMalFunc.Clone(Fn);\n        Fn.isMacro := true;\n        Exit(Env.Add((Arr[1] as TMalSymbol), Fn));\n    end;\n    'try*':\n    begin\n        try\n            Exit(EVAL(Arr[1], Env));\n        except\n            On E : Exception do\n            begin\n                if Length(Arr) < 3 then\n                    raise;\n                SetLength(Err, 1);\n                if E.ClassType = TMalException then\n                    Err[0] := (E as TMalException).Val\n                else\n                    Err[0] := TMalString.Create(E.message);\n                Arr := (Arr[2] as TMalList).Val;\n                Exit(EVAL(Arr[2], TEnv.Create(Env,\n                                              _list(Arr[1]),\n                                              Err)));\n            end;\n        end;\n    end;\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n            Ast := Arr[Length(Arr)-1]; // TCO\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    Ast := Arr[3] // TCO\n                else\n                    Exit(TMalNil.Create)\n            else\n                Ast := Arr[2]; // TCO\n        end;\n    'fn*':\n        begin\n            Exit(TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList)));\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                if Fn.isMacro then\n                begin\n                    if Fn.Ast =nil then\n                        Ast := Fn.Val(Args)\n                    else\n                        Ast := EVAL(Fn.Ast, Tenv.Create(Fn.Env, Fn.Params, Args));\n                    continue; // TCO\n                end;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    Exit(Fn.Val(Args))\n                else\n                begin\n                    Env := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    Ast := Fn.Ast; // TCO\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\n  end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction do_eval(Args : TMalArray) : TMal;\nbegin\n    do_eval := EVAL(Args[0], Repl_Env);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    core.EVAL := @EVAL;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n    Repl_Env.Add(TMalSymbol.Create('eval'), TMalFunc.Create(@do_eval));\n    SetLength(CmdArgs, Max(0, ParamCount-1));\n    for I := 2 to ParamCount do\n        CmdArgs[I-2] := TMalString.Create(ParamStr(I));\n    Repl_Env.Add(TMalSymbol.Create('*ARGV*'), TMalList.Create(CmdArgs));\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n    REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n    REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))');\n\n\n    if ParamCount >= 1 then\n    begin\n        REP('(load-file \"' + ParamStr(1) + '\")');\n        ExitCode := 0;\n        Exit;\n    end;\n\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                if E.ClassType = TMalException then\n                    WriteLn('Error: ' + pr_str((E as TMalException).Val, True))\n                else\n                    WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/stepA_mal.pas",
    "content": "program Mal;\n\n{$H+} // Use AnsiString\n\nUses sysutils,\n     fgl,\n     math,\n     mal_readline,\n     mal_types,\n     mal_func,\n     reader,\n     printer,\n     mal_env,\n     core;\n\nvar\n    Repl_Env : TEnv;\n    Line     : string;\n    I        : longint;\n    Key      : string;\n    CmdArgs  : TMalArray;\n\n// read\nfunction READ(const Str: string) : TMal;\nbegin\n    READ := read_str(Str);\nend;\n\n// eval\n\nfunction starts_with(Ast: TMal; Sym: String) : Boolean;\nvar\n   Arr : TMalArray;\n   A0  : TMal;\nbegin\n   if Ast.ClassType <> TMalList then Exit (False);\n   Arr := (Ast as TMalList).Val;\n   if Length (Arr) = 0 then Exit (False);\n   A0 := Arr [0];\n   starts_with := (A0.ClassType = TMalSymbol) and ((A0 as TMalSymbol).Val = Sym);\nend;\n\nfunction quasiquote(Ast: TMal) : TMal;\nvar\n    Arr      : TMalArray;\n    Res, Elt : TMal;\n    I        : longint;\nbegin\n    if Ast is TMalSymbol or Ast is TMalHashMap then\n        Exit(_list(TMalSymbol.Create('quote'), Ast));\n\n    if not (Ast is TMalList) then\n        Exit(Ast);\n\n    Arr := (Ast as TMalList).Val;\n    if starts_with (Ast, 'unquote') then Exit(Arr[1]);\n\n    Res := _list();\n    for I := 1 to Length(Arr) do\n    begin\n        Elt := Arr [Length(Arr) - I];\n        if starts_with (Elt, 'splice-unquote') then\n            Res := _list(TMalSymbol.Create('concat'), (Elt as TMalList).Val[1], Res)\n        else\n            Res := _list(TMalSymbol.Create('cons'), quasiquote (Elt), Res);\n    end;\n    if Ast.ClassType <> TMalList then\n        Exit(_list(TMalSymbol.Create('vec'), Res))\n    else\n        Exit(Res);\nend;\n\nfunction EVAL(Ast: TMal; Env: TEnv) : TMal;\nvar\n    Lst    : TMalList;\n    Arr    : TMalArray;\n    Arr1   : TMalArray;\n    A0Sym  : string;\n    LetEnv : TEnv;\n    Cond   : TMal;\n    I      : longint;\n    Fn     : TMalFunc;\n    Args   : TMalArray;\n    Err    : TMalArray;\n    OldDict, NewDict :  TMalDict;\nbegin\n  while true do\n  begin\n\n    Cond := Env.Get('DEBUG-EVAL');\n    if (Cond <> nil) and not (Cond is TMalNil) and not (Cond is TMalFalse) then\n       WriteLn('EVAL: ' + pr_str(Ast, True));\n\n    if Ast is TMalSymbol then\n    begin\n        A0Sym := (Ast as TMalSymbol).Val;\n        Cond := Env.Get(A0Sym);\n        if Cond = nil then\n            raise Exception.Create('''' + A0Sym+ ''' not found');\n        Exit(Cond);\n    end\n    else if Ast is TMalVector then\n    begin\n        Arr := (Ast as TMalVector).Val;\n        SetLength(Arr1, Length(Arr));\n        for I := 0 to Length(Arr)-1 do\n            Arr1[I]:= EVAL(Arr[I], Env);\n        Exit(TMalVector.Create(Arr1));\n    end\n    else if Ast is TMalHashMap then\n    begin\n        OldDict := (Ast as TMalHashMap).Val;\n        NewDict := TMalDict.Create;\n        for I := 0 to OldDict.Count-1 do\n            NewDict[OldDict.Keys[I]]:= EVAL(OldDict[OldDict.Keys[I]], Env);\n        Exit(TMalHashMap.Create(NewDict));\n    end\n    else if not (Ast is TMalList) then\n        Exit(Ast);\n\n    // Apply list\n    Lst := (Ast as TMalList);\n    Arr := Lst.Val;\n    if Length(Arr) = 0 then\n        Exit(Ast);\n    if Arr[0] is TMalSymbol then\n        A0Sym := (Arr[0] as TMalSymbol).Val\n    else\n        A0Sym := '__<*fn*>__';\n\n    case A0Sym of\n    'def!':\n        Exit(Env.Add((Arr[1] as TMalSymbol), EVAL(Arr[2], ENV)));\n    'let*':\n        begin\n            LetEnv := TEnv.Create(Env);\n            Arr1 := (Arr[1] as TMalList).Val;\n            I := 0;\n            while I < Length(Arr1) do\n            begin\n                LetEnv.Add((Arr1[I] as TMalSymbol), EVAL(Arr1[I+1], LetEnv));\n                Inc(I,2);\n            end;\n            Env := LetEnv;\n            Ast := Arr[2]; // TCO\n        end;\n    'quote':\n        Exit(Arr[1]);\n    'quasiquote':\n        Ast := quasiquote(Arr[1]);\n    'defmacro!':\n    begin\n        Fn := EVAL(Arr[2], ENV) as TMalFunc;\n        Fn := TMalFunc.Clone(Fn);\n        Fn.isMacro := true;\n        Exit(Env.Add((Arr[1] as TMalSymbol), Fn));\n    end;\n    'try*':\n    begin\n        try\n            Exit(EVAL(Arr[1], Env));\n        except\n            On E : Exception do\n            begin\n                if Length(Arr) < 3 then\n                    raise;\n                SetLength(Err, 1);\n                if E.ClassType = TMalException then\n                    Err[0] := (E as TMalException).Val\n                else\n                    Err[0] := TMalString.Create(E.message);\n                Arr := (Arr[2] as TMalList).Val;\n                Exit(EVAL(Arr[2], TEnv.Create(Env,\n                                              _list(Arr[1]),\n                                              Err)));\n            end;\n        end;\n    end;\n    'do':\n        begin\n            for I := 1 to Length(Arr) - 2 do\n                 Cond := EVAL(Arr[I], Env);\n            Ast := Arr[Length(Arr)-1]; // TCO\n        end;\n    'if':\n        begin\n            Cond := EVAL(Arr[1], Env);\n            if (Cond is TMalNil) or (Cond is TMalFalse) then\n                if Length(Arr) > 3 then\n                    Ast := Arr[3] // TCO\n                else\n                    Exit(TMalNil.Create)\n            else\n                Ast := Arr[2]; // TCO\n        end;\n    'fn*':\n        begin\n            Exit(TMalFunc.Create(Arr[2], Env, (Arr[1] as TMalList)));\n        end;\n    else\n        begin\n            Cond := EVAL(Arr[0], Env);\n            Args := copy(Arr, 1, Length(Arr) - 1);\n            if Cond is TMalFunc then\n            begin\n                Fn := Cond as TMalFunc;\n                if Fn.isMacro then\n                begin\n                    if Fn.Ast =nil then\n                        Ast := Fn.Val(Args)\n                    else\n                        Ast := EVAL(Fn.Ast, Tenv.Create(Fn.Env, Fn.Params, Args));\n                    continue; // TCO\n                end;\n                for I := 0 to Length(Args) - 1 do\n                    Args[I]:= EVAL(Args[I], Env);\n                if Fn.Ast = nil then\n                    Exit(Fn.Val(Args))\n                else\n                begin\n                    Env := TEnv.Create(Fn.Env, Fn.Params, Args);\n                    Ast := Fn.Ast; // TCO\n                end\n\n            end\n            else\n                raise Exception.Create('invalid apply');\n        end;\n    end;\n  end;\nend;\n\n// print\nfunction PRINT(Exp: TMal) : string;\nbegin\n    PRINT := pr_str(Exp, True);\nend;\n\n// repl\nfunction REP(Str: string) : string;\nbegin\n    REP := PRINT(EVAL(READ(Str), Repl_Env));\nend;\n\nfunction do_eval(Args : TMalArray) : TMal;\nbegin\n    do_eval := EVAL(Args[0], Repl_Env);\nend;\n\nbegin\n    Repl_Env := TEnv.Create;\n    core.EVAL := @EVAL;\n\n    // core.pas: defined using Pascal\n    for I := 0 to core.NS.Count-1 do\n    begin\n        Key := core.NS.Keys[I];\n        Repl_Env.Add(TMalSymbol.Create(Key),\n                     TMalFunc.Create(core.NS[Key]));\n    end;\n    Repl_Env.Add(TMalSymbol.Create('eval'), TMalFunc.Create(@do_eval));\n    SetLength(CmdArgs, Max(0, ParamCount-1));\n    for I := 2 to ParamCount do\n        CmdArgs[I-2] := TMalString.Create(ParamStr(I));\n    Repl_Env.Add(TMalSymbol.Create('*ARGV*'), TMalList.Create(CmdArgs));\n    Repl_Env.Add(TMalSymbol.Create('*host-language*'),\n                 TMalString.Create('Object Pascal'));\n\n    // core.mal: defined using language itself\n    REP('(def! not (fn* (a) (if a false true)))');\n    REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n    REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))');\n\n\n    if ParamCount >= 1 then\n    begin\n        REP('(load-file \"' + ParamStr(1) + '\")');\n        ExitCode := 0;\n        Exit;\n    end;\n\n    REP('(println (str \"Mal [\" *host-language* \"]\"))');\n    while True do\n    begin\n        try\n            Line := _readline('user> ');\n            if Line = '' then continue;\n            WriteLn(REP(Line))\n        except\n            On E : MalEOF do Halt(0);\n            On E : Exception do\n            begin\n                if E.ClassType = TMalException then\n                    WriteLn('Error: ' + pr_str((E as TMalException).Val, True))\n                else\n                    WriteLn('Error: ' + E.message);\n                WriteLn('Backtrace:');\n                WriteLn(GetBacktrace(E));\n            end;\n        end;\n    end;\nend.\n"
  },
  {
    "path": "impls/objpascal/tests/step5_tco.mal",
    "content": ";; Object Pascal: skipping non-TCO recursion\n;; Reason: completes at 10,000, unrecoverable segfault at 20,000\n"
  },
  {
    "path": "impls/ocaml/Dockerfile",
    "content": "FROM ubuntu:25.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install ocaml\n"
  },
  {
    "path": "impls/ocaml/Makefile",
    "content": "STEPS = step0_repl.ml step1_read_print.ml step2_eval.ml step3_env.ml \\\n        step4_if_fn_do.ml step5_tco.ml step6_file.ml step7_quote.ml \\\n\tstep8_macros.ml step9_try.ml stepA_mal.ml\nMODULES = types.ml reader.ml printer.ml env.ml core.ml\nLIBS = str.cmxa unix.cmxa\nMAL_LIB = mal_lib.cmxa\n# Apparently necessary with caml 5.0:\nOPTIONS = -I +str -I +unix\n\nSTEP_BINS = $(STEPS:%.ml=%)\nLAST_STEP_BIN = $(word $(words $(STEP_BINS)),$(STEP_BINS))\n\nall: $(STEP_BINS)\n\ndist: mal\n\nmal: $(LAST_STEP_BIN)\n\tcp $< $@\n\n# ocaml repl apparently needs bytecode, not native, compilation.\n# Just do it all right here:\nrepl:\n\tocamlc -c $(LIBS:%.cmxa=%.cma) $(MODULES) $(STEPS)\n\trlwrap ocaml $(LIBS:%.cmxa=%.cma) $(MODULES:%.ml=%.cmo)\n\n$(MAL_LIB): $(MODULES)\n\tocamlopt -a $(MODULES) -o $@ $(OPTIONS)\n\n$(STEP_BINS): %: %.ml $(MAL_LIB)\n\tocamlopt $(LIBS) $(MAL_LIB) $< -o $@ $(OPTIONS)\n\nclean:\n\trm -f $(STEP_BINS) mal mal_lib.* *.cmo *.cmx *.cmi *.o\n\nformat:\n\tocamlformat --inplace --enable-outside-detected-project *.ml\n.PHONY: all repl clean format\n"
  },
  {
    "path": "impls/ocaml/core.ml",
    "content": "module T = Types.Types\n\nlet ns = Env.make None\nlet kw_macro = T.Keyword \"macro\"\n\nlet num_fun t f =\n  Types.fn (function\n    | [ T.Int a; T.Int b ] -> t (f a b)\n    | _ -> raise (Invalid_argument \"Numeric args required for this Mal builtin\"))\n\nlet mk_int x = T.Int x\nlet mk_bool x = T.Bool x\n\nlet rec mal_equal a b =\n  match (a, b) with\n  | T.List { T.value = xs }, T.List { T.value = ys }\n  | T.List { T.value = xs }, T.Vector { T.value = ys }\n  | T.Vector { T.value = xs }, T.List { T.value = ys }\n  | T.Vector { T.value = xs }, T.Vector { T.value = ys } ->\n      List.equal mal_equal xs ys\n  | T.Map { T.value = xs }, T.Map { T.value = ys } ->\n      Types.MalMap.equal mal_equal xs ys\n  | _ -> a = b\n\nlet seq = function\n  | T.List { T.value = xs } -> xs\n  | T.Vector { T.value = xs } -> xs\n  | _ -> []\n\nlet mal_seq = function\n  | [ (T.List { T.value = xs } as lst) ] when not (List.is_empty xs) -> lst\n  | [ T.Vector { T.value = xs } ] when not (List.is_empty xs) -> Types.list xs\n  | [ T.String s ] when 0 < String.length s ->\n      Types.list (List.map (fun x -> T.String x) (Str.split (Str.regexp \"\") s))\n  | _ -> T.Nil\n\nlet rec assoc = function\n  | T.Map { T.value = m } :: xs -> Types.list_into_map m xs\n  | _ -> T.Nil\n\nlet rec dissoc = function\n  | T.Map { T.value = m } :: xs ->\n      Types.map (List.fold_left (fun k m -> Types.MalMap.remove m k) m xs)\n  | _ -> T.Nil\n\nlet rec conj = function\n  | c :: x :: (_ :: _ as xs) -> conj (conj [ c; x ] :: xs)\n  | [ T.List { T.value = c; T.meta }; x ] -> T.List { T.value = x :: c; T.meta }\n  | [ T.Vector { T.value = c; T.meta }; x ] ->\n      T.Vector { T.value = c @ [ x ]; T.meta }\n  | _ -> T.Nil\n\nlet init env =\n  Env.set env \"throw\"\n    (Types.fn (function [ ast ] -> raise (Types.MalExn ast) | _ -> T.Nil));\n\n  Env.set env \"+\" (num_fun mk_int ( + ));\n  Env.set env \"-\" (num_fun mk_int ( - ));\n  Env.set env \"*\" (num_fun mk_int ( * ));\n  Env.set env \"/\" (num_fun mk_int ( / ));\n  Env.set env \"<\" (num_fun mk_bool ( < ));\n  Env.set env \"<=\" (num_fun mk_bool ( <= ));\n  Env.set env \">\" (num_fun mk_bool ( > ));\n  Env.set env \">=\" (num_fun mk_bool ( >= ));\n\n  Env.set env \"list\" (Types.fn (function xs -> Types.list xs));\n  Env.set env \"list?\"\n    (Types.fn (function [ T.List _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"vector\" (Types.fn (function xs -> Types.vector xs));\n  Env.set env \"vector?\"\n    (Types.fn (function [ T.Vector _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"empty?\"\n    (Types.fn (function\n      | [ T.List { T.value = [] } ] -> T.Bool true\n      | [ T.Vector { T.value = [] } ] -> T.Bool true\n      | _ -> T.Bool false));\n  Env.set env \"count\"\n    (Types.fn (function\n      | [ T.List { T.value = xs } ] | [ T.Vector { T.value = xs } ] ->\n          T.Int (List.length xs)\n      | _ -> T.Int 0));\n  Env.set env \"=\"\n    (Types.fn (function\n      | [ a; b ] -> T.Bool (mal_equal a b)\n      | _ -> T.Bool false));\n\n  Env.set env \"pr-str\"\n    (Types.fn (function xs ->\n        T.String (Format.asprintf \"%a\" (Printer.pr_list true true) xs)));\n  Env.set env \"str\"\n    (Types.fn (function xs ->\n        T.String (Format.asprintf \"%a\" (Printer.pr_list false false) xs)));\n  Env.set env \"prn\"\n    (Types.fn (function xs ->\n        Format.printf \"%a\\n\" (Printer.pr_list true true) xs;\n        T.Nil));\n  Env.set env \"println\"\n    (Types.fn (function xs ->\n        Format.printf \"%a\\n\" (Printer.pr_list false true) xs;\n        T.Nil));\n\n  Env.set env \"compare\"\n    (Types.fn (function [ a; b ] -> T.Int (compare a b) | _ -> T.Nil));\n  Env.set env \"with-meta\"\n    (Types.fn (function\n      | [ T.List v; m ] -> T.List { v with T.meta = m }\n      | [ T.Map v; m ] -> T.Map { v with T.meta = m }\n      | [ T.Vector v; m ] -> T.Vector { v with T.meta = m }\n      | [ T.Fn v; m ] -> T.Fn { v with meta = m }\n      | _ -> T.Nil));\n  Env.set env \"meta\"\n    (Types.fn (function\n      | [ T.List { T.meta } ] -> meta\n      | [ T.Map { T.meta } ] -> meta\n      | [ T.Vector { T.meta } ] -> meta\n      | [ T.Fn { meta } ] -> meta\n      | _ -> T.Nil));\n\n  Env.set env \"read-string\"\n    (Types.fn (function [ T.String x ] -> Reader.read_str x | _ -> T.Nil));\n  Env.set env \"slurp\"\n    (Types.fn (function\n      | [ T.String x ] ->\n          let chan = open_in x in\n          let b = Buffer.create 27 in\n          Buffer.add_channel b chan (in_channel_length chan);\n          close_in chan;\n          T.String (Buffer.contents b)\n      | _ -> T.Nil));\n\n  Env.set env \"cons\"\n    (Types.fn (function [ x; xs ] -> Types.list (x :: seq xs) | _ -> T.Nil));\n  Env.set env \"concat\"\n    (Types.fn\n       (let rec concat = function\n          | x :: y :: more -> concat (Types.list (seq x @ seq y) :: more)\n          | [ (T.List _ as x) ] -> x\n          | [ x ] -> Types.list (seq x)\n          | [] -> Types.list []\n        in\n        concat));\n  Env.set env \"vec\"\n    (Types.fn (function\n      | [ T.List { T.value = xs } ] -> Types.vector xs\n      | [ T.Vector { T.value = xs } ] -> Types.vector xs\n      | [ _ ] -> raise (Invalid_argument \"vec: expects a sequence\")\n      | _ -> raise (Invalid_argument \"vec: arg count\")));\n\n  Env.set env \"nth\"\n    (Types.fn (function\n      | [ xs; T.Int i ] -> (\n          try List.nth (seq xs) i\n          with _ -> raise (Invalid_argument \"nth: index out of range\"))\n      | _ -> T.Nil));\n  Env.set env \"first\"\n    (Types.fn (function\n      | [ xs ] -> ( match seq xs with x :: _ -> x | _ -> T.Nil)\n      | _ -> T.Nil));\n  Env.set env \"rest\"\n    (Types.fn (function\n      | [ xs ] -> Types.list (match seq xs with _ :: xs -> xs | _ -> [])\n      | _ -> T.Nil));\n\n  Env.set env \"string?\"\n    (Types.fn (function [ T.String _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"symbol\"\n    (Types.fn (function [ T.String x ] -> T.Symbol x | _ -> T.Nil));\n  Env.set env \"symbol?\"\n    (Types.fn (function [ T.Symbol _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"keyword\"\n    (Types.fn (function\n      | [ T.String x ] -> T.Keyword x\n      | [ T.Keyword x ] -> T.Keyword x\n      | _ -> T.Nil));\n  Env.set env \"keyword?\"\n    (Types.fn (function [ T.Keyword _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"number?\"\n    (Types.fn (function [ T.Int _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"fn?\"\n    (Types.fn (function\n      | [ T.Fn { macro = false } ] -> T.Bool true\n      | _ -> T.Bool false));\n  Env.set env \"macro?\"\n    (Types.fn (function\n      | [ T.Fn { macro = true } ] -> T.Bool true\n      | _ -> T.Bool false));\n  Env.set env \"nil?\"\n    (Types.fn (function [ T.Nil ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"true?\"\n    (Types.fn (function [ T.Bool true ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"false?\"\n    (Types.fn (function [ T.Bool false ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"sequential?\"\n    (Types.fn (function\n      | [ T.List _ ] | [ T.Vector _ ] -> T.Bool true\n      | _ -> T.Bool false));\n  Env.set env \"apply\"\n    (Types.fn (function\n      | T.Fn { value = f } :: apply_args -> (\n          match List.rev apply_args with\n          | last_arg :: rev_args -> f (List.rev rev_args @ seq last_arg)\n          | [] -> f [])\n      | _ -> raise (Invalid_argument \"First arg to apply must be a fn\")));\n  Env.set env \"map\"\n    (Types.fn (function\n      | [ T.Fn { value = f }; xs ] ->\n          Types.list (List.map (fun x -> f [ x ]) (seq xs))\n      | _ -> T.Nil));\n  Env.set env \"readline\"\n    (Types.fn (function\n      | [ T.String x ] ->\n          Format.printf \"%s%!\" x;\n          T.String (read_line ())\n      | _ -> T.String (read_line ())));\n\n  Env.set env \"map?\"\n    (Types.fn (function [ T.Map _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"hash-map\" (Types.fn (Types.list_into_map Types.MalMap.empty));\n  Env.set env \"assoc\" (Types.fn assoc);\n  Env.set env \"dissoc\" (Types.fn dissoc);\n  Env.set env \"get\"\n    (Types.fn (function\n      | [ T.Map { T.value = m }; k ] -> (\n          try Types.MalMap.find k m with _ -> T.Nil)\n      | _ -> T.Nil));\n  Env.set env \"keys\"\n    (Types.fn (function\n      | [ T.Map { T.value = m } ] ->\n          Types.list (Types.MalMap.fold (fun k _ c -> k :: c) m [])\n      | _ -> T.Nil));\n  Env.set env \"vals\"\n    (Types.fn (function\n      | [ T.Map { T.value = m } ] ->\n          Types.list (Types.MalMap.fold (fun _ v c -> v :: c) m [])\n      | _ -> T.Nil));\n  Env.set env \"contains?\"\n    (Types.fn (function\n      | [ T.Map { T.value = m }; k ] -> T.Bool (Types.MalMap.mem k m)\n      | _ -> T.Bool false));\n  Env.set env \"conj\" (Types.fn conj);\n  Env.set env \"seq\" (Types.fn mal_seq);\n\n  Env.set env \"atom?\"\n    (Types.fn (function [ T.Atom _ ] -> T.Bool true | _ -> T.Bool false));\n  Env.set env \"atom\"\n    (Types.fn (function [ x ] -> T.Atom (ref x) | _ -> T.Nil));\n  Env.set env \"deref\" (Types.fn (function [ T.Atom x ] -> !x | _ -> T.Nil));\n  Env.set env \"reset!\"\n    (Types.fn (function\n      | [ T.Atom x; v ] ->\n          x := v;\n          v\n      | _ -> T.Nil));\n  Env.set env \"swap!\"\n    (Types.fn (function\n      | T.Atom x :: T.Fn { value = f } :: args ->\n          let v = f (!x :: args) in\n          x := v;\n          v\n      | _ -> T.Nil));\n\n  Env.set env \"time-ms\"\n    (Types.fn (function _ -> T.Int (truncate (1000.0 *. Unix.gettimeofday ()))))\n"
  },
  {
    "path": "impls/ocaml/env.ml",
    "content": "module T = Types.Types\nmodule Data = Map.Make (String)\n\ntype env = { outer : env option; data : Types.mal_type Data.t ref }\n\nlet make outer = { outer; data = ref Data.empty }\nlet set env key value = env.data := Data.add key value !(env.data)\n\nlet rec get env key =\n  match Data.find_opt key !(env.data) with\n  | Some _ as v -> v\n  | None -> ( match env.outer with Some outer -> get outer key | None -> None)\n"
  },
  {
    "path": "impls/ocaml/printer.ml",
    "content": "open Format\nmodule T = Types.Types\n\n(* Compile the regex once and for all *)\nlet _pr_escape_re = Str.regexp \"\\\\([\\\"\\\\\\n]\\\\)\"\n\nlet _pr_escape_chunk out = function\n  | Str.Text s -> fprintf out \"%s\" s\n  | Str.Delim \"\\n\" -> fprintf out \"\\\\n\"\n  | Str.Delim s -> fprintf out \"\\\\%s\" s\n\nlet _pr_escape_string out s =\n  List.iter (_pr_escape_chunk out) (Str.full_split _pr_escape_re s)\n\nlet rec pr_str readably out mal_obj =\n  match mal_obj with\n  | T.Int i -> fprintf out \"%i\" i\n  | T.Keyword s -> fprintf out \":%s\" s\n  | T.Nil -> fprintf out \"nil\"\n  | T.Bool b -> fprintf out \"%B\" b\n  | T.String s when readably -> fprintf out \"\\\"%a\\\"\" _pr_escape_string s\n  | T.String s | T.Symbol s -> fprintf out \"%s\" s\n  | T.List { T.value = xs } -> fprintf out \"(%a)\" (pr_list readably true) xs\n  | T.Vector { T.value = xs } -> fprintf out \"[%a]\" (pr_list readably true) xs\n  | T.Map { T.value = xs } -> fprintf out \"{%a}\" (_pr_map readably) xs\n  | T.Fn _ -> fprintf out \"#<fn>\"\n  | T.Atom x -> fprintf out \"(atom %a)\" (pr_str readably) !x\n\nand pr_list readably spaced out =\n  List.iter\n    (let sep = ref \"\" in\n     fun x ->\n       fprintf out \"%s%a\" !sep (pr_str readably) x;\n       if spaced && !sep == \"\" then sep := \" \" else ())\n\nand _pr_map readably out =\n  Types.MalMap.iter\n    (let sep = ref \"\" in\n     fun k v ->\n       fprintf out \"%s%a %a\" !sep (pr_str readably) k (pr_str readably) v;\n       if !sep == \"\" then sep := \" \" else ())\n"
  },
  {
    "path": "impls/ocaml/reader.ml",
    "content": "open Str (* not reentrant, but simple and always available *)\nopen Types\n\nlet separator_re = regexp \"\\\\([, \\t\\n]\\\\|;[^\\n]*\\\\)+\"\nlet number_re = regexp \"-?[0-9]+\"\nlet chars = \"[^][, \\t\\n;(){}'`~@^\\\"]+\"\nlet keyword_re = regexp (\":\\\\(\" ^ chars ^ \"\\\\)\")\nlet symbol_re = regexp chars\nlet string_re = regexp {|\"\\(\\(\\\\[\\\\n\"]\\|[^\\\\\"]\\)*\\)\"|}\nlet escape_re = regexp {|\\\\.|}\nlet quote_re = regexp_string \"'\"\nlet quasiquote_re = regexp_string \"`\"\nlet deref_re = regexp_string \"@\"\nlet unquote_re = regexp_string \"~\"\nlet sp_unq_re = regexp_string \"~@\"\nlet with_meta_re = regexp_string \"^\"\nlet list_re = regexp_string \"(\"\nlet map_re = regexp_string \"{\"\nlet vector_re = regexp_string \"[\"\nlet close_re = regexp \"[])}]\" (* so \"[1 2)\" is accepted as a vector *)\n\nlet unescape str =\n  let e = match_end () - 1 in\n  if str.[e] == 'n' then \"\\n\" else String.sub str e 1\n\nlet read_str str =\n  (* !p is the currently parsed position inside str *)\n  let rec read pattern p =\n    let result = string_match pattern str !p in\n    if result then p := match_end ();\n    result\n  and read_list p =\n    ignore (read separator_re p);\n    if read close_re p then []\n    else\n      (* Parse the first form before the rest of the list *)\n      let first = read_form p in\n      first :: read_list p\n  and read_form p =\n    ignore (read separator_re p);\n    if read number_re p then Types.Int (int_of_string (matched_string str))\n    else if read keyword_re p then Keyword (matched_group 1 str)\n    else if read symbol_re p then\n      match matched_string str with\n      | \"nil\" -> Nil\n      | \"true\" -> Bool true\n      | \"false\" -> Bool false\n      | t -> Symbol t\n    else if read string_re p then\n      String (global_substitute escape_re unescape (matched_group 1 str))\n    else if read quote_re p then list [ Symbol \"quote\"; read_form p ]\n    else if read quasiquote_re p then list [ Symbol \"quasiquote\"; read_form p ]\n    else if read deref_re p then list [ Symbol \"deref\"; read_form p ]\n    else if read sp_unq_re p then list [ Symbol \"splice-unquote\"; read_form p ]\n    else if read unquote_re p then list [ Symbol \"unquote\"; read_form p ]\n    else if read with_meta_re p then\n      (* Parse the metadata before the value *)\n      let meta = read_form p in\n      list [ Symbol \"with-meta\"; read_form p; meta ]\n    else if read list_re p then list (read_list p)\n    else if read vector_re p then vector (read_list p)\n    else if read map_re p then list_into_map MalMap.empty (read_list p)\n    else raise (Invalid_argument \"unexpected EOF ] } ) or string escape\")\n  in\n  read_form (ref 0)\n"
  },
  {
    "path": "impls/ocaml/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/ocaml/step0_repl.ml",
    "content": "(*\n  To try things at the ocaml repl:\n  rlwrap ocaml\n\n  To see type signatures of all functions:\n  ocamlc -i step0_repl.ml\n\n  To run the program:\n  ocaml step0_repl.ml\n*)\n\nlet eval ast = ast\nlet read str = str\nlet print exp = exp\nlet rep str = print (eval (read str))\n\nlet main =\n  try\n    while true do\n      Format.printf \"user> %!\";\n      let line = read_line () in\n      Format.printf \"%s\\n\" (rep line)\n    done\n  with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step1_read_print.ml",
    "content": "let eval ast = ast\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\n\nlet main =\n  try\n    while true do\n      Format.printf \"user> %!\";\n      let line = read_line () in\n      try Format.printf \"%a\\n\" print (eval (read line)) with\n      | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n      | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n    done\n  with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step2_eval.ml",
    "content": "module T = Types.Types\nmodule Env = Map.Make (String)\n\nlet num_fun f =\n  Types.fn (function\n    | [ T.Int a; T.Int b ] -> T.Int (f a b)\n    | _ -> raise (Invalid_argument \"Numeric args required for this Mal builtin\"))\n\nlet repl_env =\n  Env.of_list\n    [\n      (\"+\", num_fun ( + ));\n      (\"-\", num_fun ( - ));\n      (\"*\", num_fun ( * ));\n      (\"/\", num_fun ( / ));\n    ]\n\nlet rec eval env ast =\n  (*\n       Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n   *)\n  match ast with\n  | T.Symbol s -> (\n      match Env.find_opt s env with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\n\nlet main =\n  try\n    while true do\n      Format.printf \"user> %!\";\n      let line = read_line () in\n      try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n      | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n      | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n    done\n  with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step3_env.ml",
    "content": "module T = Types.Types\n\nlet num_fun f =\n  Types.fn (function\n    | [ T.Int a; T.Int b ] -> T.Int (f a b)\n    | _ -> raise (Invalid_argument \"Numeric args required for this Mal builtin\"))\n\nlet repl_env = Env.make None\n\nlet init_repl env =\n  Env.set env \"+\" (num_fun ( + ));\n  Env.set env \"-\" (num_fun ( - ));\n  Env.set env \"*\" (num_fun ( * ));\n  Env.set env \"/\" (num_fun ( / ))\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\n\nlet main =\n  init_repl repl_env;\n\n  try\n    while true do\n      Format.printf \"user> %!\";\n      let line = read_line () in\n      try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n      | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n      | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n    done\n  with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step4_if_fn_do.ml",
    "content": "module T = Types.Types\n\nlet repl_env = Env.make (Some Core.ns)\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = T.Symbol \"do\" :: body } ->\n      List.fold_left (fun _ -> eval env) T.Nil body\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr; else_expr ] } ->\n      eval env\n        (match eval env test with\n        | T.Nil | T.Bool false -> else_expr\n        | _ -> then_expr)\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr ] } -> (\n      match eval env test with\n      | T.Nil | T.Bool false -> T.Nil\n      | _ -> eval env then_expr)\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.Vector { T.value = arg_names }; expr ] }\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.List { T.value = arg_names }; expr ] } ->\n      Types.fn (function args ->\n          let sub_env = Env.make (Some env) in\n          let rec bind_args a b =\n            match (a, b) with\n            | [ T.Symbol \"&\"; T.Symbol name ], args ->\n                Env.set sub_env name (Types.list args)\n            | T.Symbol name :: names, arg :: args ->\n                Env.set sub_env name arg;\n                bind_args names args\n            | [], [] -> ()\n            | _ -> raise (Invalid_argument \"Bad param count in fn call\")\n          in\n          bind_args arg_names args;\n          eval sub_env expr)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\nlet re str = ignore (eval repl_env (read str))\n\nlet main =\n  Core.init Core.ns;\n  re \"(def! not (fn* (a) (if a false true)))\";\n\n  try\n    while true do\n      Format.printf \"user> %!\";\n      let line = read_line () in\n      try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n      | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n      | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n    done\n  with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step6_file.ml",
    "content": "module T = Types.Types\n\nlet repl_env = Env.make (Some Core.ns)\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = T.Symbol \"do\" :: body } ->\n      List.fold_left (fun _ -> eval env) T.Nil body\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr; else_expr ] } ->\n      eval env\n        (match eval env test with\n        | T.Nil | T.Bool false -> else_expr\n        | _ -> then_expr)\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr ] } -> (\n      match eval env test with\n      | T.Nil | T.Bool false -> T.Nil\n      | _ -> eval env then_expr)\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.Vector { T.value = arg_names }; expr ] }\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.List { T.value = arg_names }; expr ] } ->\n      Types.fn (function args ->\n          let sub_env = Env.make (Some env) in\n          let rec bind_args a b =\n            match (a, b) with\n            | [ T.Symbol \"&\"; T.Symbol name ], args ->\n                Env.set sub_env name (Types.list args)\n            | T.Symbol name :: names, arg :: args ->\n                Env.set sub_env name arg;\n                bind_args names args\n            | [], [] -> ()\n            | _ -> raise (Invalid_argument \"Bad param count in fn call\")\n          in\n          bind_args arg_names args;\n          eval sub_env expr)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\nlet re str = ignore (eval repl_env (read str))\n\nlet main =\n  Core.init Core.ns;\n  Env.set repl_env \"*ARGV*\"\n    (Types.list\n       (if Array.length Sys.argv > 1 then\n          List.map\n            (fun x -> T.String x)\n            (List.tl (List.tl (Array.to_list Sys.argv)))\n        else []));\n  Env.set repl_env \"eval\"\n    (Types.fn (function [ ast ] -> eval repl_env ast | _ -> T.Nil));\n\n  re\n    \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\n\\\n     nil)\\\")))))\";\n  re \"(def! not (fn* (a) (if a false true)))\";\n\n  if Array.length Sys.argv > 1 then\n    re (Format.asprintf \"(load-file \\\"%s\\\")\" Sys.argv.(1))\n  else\n    try\n      while true do\n        Format.printf \"user> %!\";\n        let line = read_line () in\n        try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n        | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n        | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n      done\n    with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step7_quote.ml",
    "content": "module T = Types.Types\n\nlet repl_env = Env.make (Some Core.ns)\n\nlet rec quasiquote ast =\n  match ast with\n  | T.List { T.value = [ T.Symbol \"unquote\"; x ] } -> x\n  | T.List { T.value = xs } -> qq_list xs\n  | T.Vector { T.value = xs } -> Types.list [ T.Symbol \"vec\"; qq_list xs ]\n  | T.Map _ | T.Symbol _ -> Types.list [ T.Symbol \"quote\"; ast ]\n  | _ -> ast\n\nand qq_list xs = List.fold_right qq_folder xs (Types.list [])\n\nand qq_folder elt acc =\n  match elt with\n  | T.List { T.value = [ T.Symbol \"splice-unquote\"; x ] } ->\n      Types.list [ T.Symbol \"concat\"; x; acc ]\n  | _ -> Types.list [ T.Symbol \"cons\"; quasiquote elt; acc ]\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = T.Symbol \"do\" :: body } ->\n      List.fold_left (fun _ -> eval env) T.Nil body\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr; else_expr ] } ->\n      eval env\n        (match eval env test with\n        | T.Nil | T.Bool false -> else_expr\n        | _ -> then_expr)\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr ] } -> (\n      match eval env test with\n      | T.Nil | T.Bool false -> T.Nil\n      | _ -> eval env then_expr)\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.Vector { T.value = arg_names }; expr ] }\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.List { T.value = arg_names }; expr ] } ->\n      Types.fn (function args ->\n          let sub_env = Env.make (Some env) in\n          let rec bind_args a b =\n            match (a, b) with\n            | [ T.Symbol \"&\"; T.Symbol name ], args ->\n                Env.set sub_env name (Types.list args)\n            | T.Symbol name :: names, arg :: args ->\n                Env.set sub_env name arg;\n                bind_args names args\n            | [], [] -> ()\n            | _ -> raise (Invalid_argument \"Bad param count in fn call\")\n          in\n          bind_args arg_names args;\n          eval sub_env expr)\n  | T.List { T.value = [ T.Symbol \"quote\"; ast ] } -> ast\n  | T.List { T.value = [ T.Symbol \"quasiquote\"; ast ] } ->\n      eval env (quasiquote ast)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\nlet re str = ignore (eval repl_env (read str))\n\nlet main =\n  Core.init Core.ns;\n  Env.set repl_env \"*ARGV*\"\n    (Types.list\n       (if Array.length Sys.argv > 1 then\n          List.map\n            (fun x -> T.String x)\n            (List.tl (List.tl (Array.to_list Sys.argv)))\n        else []));\n  Env.set repl_env \"eval\"\n    (Types.fn (function [ ast ] -> eval repl_env ast | _ -> T.Nil));\n\n  re\n    \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\n\\\n     nil)\\\")))))\";\n  re \"(def! not (fn* (a) (if a false true)))\";\n\n  if Array.length Sys.argv > 1 then\n    re (Format.asprintf \"(load-file \\\"%s\\\")\" Sys.argv.(1))\n  else\n    try\n      while true do\n        Format.printf \"user> %!\";\n        let line = read_line () in\n        try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n        | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n        | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n      done\n    with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step8_macros.ml",
    "content": "module T = Types.Types\n\nlet repl_env = Env.make (Some Core.ns)\n\nlet rec quasiquote ast =\n  match ast with\n  | T.List { T.value = [ T.Symbol \"unquote\"; x ] } -> x\n  | T.List { T.value = xs } -> qq_list xs\n  | T.Vector { T.value = xs } -> Types.list [ T.Symbol \"vec\"; qq_list xs ]\n  | T.Map _ | T.Symbol _ -> Types.list [ T.Symbol \"quote\"; ast ]\n  | _ -> ast\n\nand qq_list xs = List.fold_right qq_folder xs (Types.list [])\n\nand qq_folder elt acc =\n  match elt with\n  | T.List { T.value = [ T.Symbol \"splice-unquote\"; x ] } ->\n      Types.list [ T.Symbol \"concat\"; x; acc ]\n  | _ -> Types.list [ T.Symbol \"cons\"; quasiquote elt; acc ]\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List { T.value = [ T.Symbol \"defmacro!\"; T.Symbol key; expr ] } -> (\n      match eval env expr with\n      | T.Fn ({ macro = false } as f) ->\n          let fn = T.Fn { f with macro = true } in\n          Env.set env key fn;\n          fn\n      | _ -> raise (Invalid_argument \"defmacro! value must be a fn\"))\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = T.Symbol \"do\" :: body } ->\n      List.fold_left (fun _ -> eval env) T.Nil body\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr; else_expr ] } ->\n      eval env\n        (match eval env test with\n        | T.Nil | T.Bool false -> else_expr\n        | _ -> then_expr)\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr ] } -> (\n      match eval env test with\n      | T.Nil | T.Bool false -> T.Nil\n      | _ -> eval env then_expr)\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.Vector { T.value = arg_names }; expr ] }\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.List { T.value = arg_names }; expr ] } ->\n      Types.fn (function args ->\n          let sub_env = Env.make (Some env) in\n          let rec bind_args a b =\n            match (a, b) with\n            | [ T.Symbol \"&\"; T.Symbol name ], args ->\n                Env.set sub_env name (Types.list args)\n            | T.Symbol name :: names, arg :: args ->\n                Env.set sub_env name arg;\n                bind_args names args\n            | [], [] -> ()\n            | _ -> raise (Invalid_argument \"Bad param count in fn call\")\n          in\n          bind_args arg_names args;\n          eval sub_env expr)\n  | T.List { T.value = [ T.Symbol \"quote\"; ast ] } -> ast\n  | T.List { T.value = [ T.Symbol \"quasiquote\"; ast ] } ->\n      eval env (quasiquote ast)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f; macro = true } -> eval env (f args)\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\nlet re str = ignore (eval repl_env (read str))\n\nlet main =\n  Core.init Core.ns;\n  Env.set repl_env \"*ARGV*\"\n    (Types.list\n       (if Array.length Sys.argv > 1 then\n          List.map\n            (fun x -> T.String x)\n            (List.tl (List.tl (Array.to_list Sys.argv)))\n        else []));\n  Env.set repl_env \"eval\"\n    (Types.fn (function [ ast ] -> eval repl_env ast | _ -> T.Nil));\n\n  re\n    \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\n\\\n     nil)\\\")))))\";\n  re \"(def! not (fn* (a) (if a false true)))\";\n  re\n    \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if \\\n     (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) \\\n     (cons 'cond (rest (rest xs)))))))\";\n\n  if Array.length Sys.argv > 1 then\n    re (Format.asprintf \"(load-file \\\"%s\\\")\" Sys.argv.(1))\n  else\n    try\n      while true do\n        Format.printf \"user> %!\";\n        let line = read_line () in\n        try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n        | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n        | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n      done\n    with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/step9_try.ml",
    "content": "module T = Types.Types\n\nlet repl_env = Env.make (Some Core.ns)\n\nlet rec quasiquote ast =\n  match ast with\n  | T.List { T.value = [ T.Symbol \"unquote\"; x ] } -> x\n  | T.List { T.value = xs } -> qq_list xs\n  | T.Vector { T.value = xs } -> Types.list [ T.Symbol \"vec\"; qq_list xs ]\n  | T.Map _ | T.Symbol _ -> Types.list [ T.Symbol \"quote\"; ast ]\n  | _ -> ast\n\nand qq_list xs = List.fold_right qq_folder xs (Types.list [])\n\nand qq_folder elt acc =\n  match elt with\n  | T.List { T.value = [ T.Symbol \"splice-unquote\"; x ] } ->\n      Types.list [ T.Symbol \"concat\"; x; acc ]\n  | _ -> Types.list [ T.Symbol \"cons\"; quasiquote elt; acc ]\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List { T.value = [ T.Symbol \"defmacro!\"; T.Symbol key; expr ] } -> (\n      match eval env expr with\n      | T.Fn ({ macro = false } as f) ->\n          let fn = T.Fn { f with macro = true } in\n          Env.set env key fn;\n          fn\n      | _ -> raise (Invalid_argument \"defmacro! value must be a fn\"))\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = T.Symbol \"do\" :: body } ->\n      List.fold_left (fun _ -> eval env) T.Nil body\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr; else_expr ] } ->\n      eval env\n        (match eval env test with\n        | T.Nil | T.Bool false -> else_expr\n        | _ -> then_expr)\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr ] } -> (\n      match eval env test with\n      | T.Nil | T.Bool false -> T.Nil\n      | _ -> eval env then_expr)\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.Vector { T.value = arg_names }; expr ] }\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.List { T.value = arg_names }; expr ] } ->\n      Types.fn (function args ->\n          let sub_env = Env.make (Some env) in\n          let rec bind_args a b =\n            match (a, b) with\n            | [ T.Symbol \"&\"; T.Symbol name ], args ->\n                Env.set sub_env name (Types.list args)\n            | T.Symbol name :: names, arg :: args ->\n                Env.set sub_env name arg;\n                bind_args names args\n            | [], [] -> ()\n            | _ -> raise (Invalid_argument \"Bad param count in fn call\")\n          in\n          bind_args arg_names args;\n          eval sub_env expr)\n  | T.List { T.value = [ T.Symbol \"quote\"; ast ] } -> ast\n  | T.List { T.value = [ T.Symbol \"quasiquote\"; ast ] } ->\n      eval env (quasiquote ast)\n  | T.List { T.value = [ T.Symbol \"try*\"; scary ] } -> eval env scary\n  | T.List\n      {\n        T.value =\n          [\n            T.Symbol \"try*\";\n            scary;\n            T.List { T.value = [ T.Symbol \"catch*\"; T.Symbol local; handler ] };\n          ];\n      } -> (\n      try eval env scary\n      with exn ->\n        let value =\n          match exn with\n          | Types.MalExn value -> value\n          | Invalid_argument msg -> T.String msg\n          | e -> T.String (Printexc.to_string e)\n        in\n        let sub_env = Env.make (Some env) in\n        Env.set sub_env local value;\n        eval sub_env handler)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f; macro = true } -> eval env (f args)\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\nlet re str = ignore (eval repl_env (read str))\n\nlet main =\n  Core.init Core.ns;\n  Env.set repl_env \"*ARGV*\"\n    (Types.list\n       (if Array.length Sys.argv > 1 then\n          List.map\n            (fun x -> T.String x)\n            (List.tl (List.tl (Array.to_list Sys.argv)))\n        else []));\n  Env.set repl_env \"eval\"\n    (Types.fn (function [ ast ] -> eval repl_env ast | _ -> T.Nil));\n\n  re\n    \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\n\\\n     nil)\\\")))))\";\n  re \"(def! not (fn* (a) (if a false true)))\";\n  re\n    \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if \\\n     (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) \\\n     (cons 'cond (rest (rest xs)))))))\";\n\n  if Array.length Sys.argv > 1 then\n    re (Format.asprintf \"(load-file \\\"%s\\\")\" Sys.argv.(1))\n  else\n    try\n      while true do\n        Format.printf \"user> %!\";\n        let line = read_line () in\n        try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n        | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n        | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n      done\n    with End_of_file -> Format.printf \"\\n\"\n"
  },
  {
    "path": "impls/ocaml/stepA_mal.ml",
    "content": "module T = Types.Types\n\nlet repl_env = Env.make (Some Core.ns)\n\nlet rec quasiquote ast =\n  match ast with\n  | T.List { T.value = [ T.Symbol \"unquote\"; x ] } -> x\n  | T.List { T.value = xs } -> qq_list xs\n  | T.Vector { T.value = xs } -> Types.list [ T.Symbol \"vec\"; qq_list xs ]\n  | T.Map _ | T.Symbol _ -> Types.list [ T.Symbol \"quote\"; ast ]\n  | _ -> ast\n\nand qq_list xs = List.fold_right qq_folder xs (Types.list [])\n\nand qq_folder elt acc =\n  match elt with\n  | T.List { T.value = [ T.Symbol \"splice-unquote\"; x ] } ->\n      Types.list [ T.Symbol \"concat\"; x; acc ]\n  | _ -> Types.list [ T.Symbol \"cons\"; quasiquote elt; acc ]\n\nlet rec eval env ast =\n  (match Env.get env \"DEBUG-EVAL\" with\n  | None | Some T.Nil | Some (T.Bool false) -> ()\n  | Some _ -> Format.printf \"EVAL: %a\\n\" (Printer.pr_str true) ast);\n  match ast with\n  | T.Symbol s -> (\n      match Env.get env s with\n      | Some v -> v\n      | None -> raise (Invalid_argument (\"'\" ^ s ^ \"' not found\")))\n  | T.Vector { T.value = xs } -> Types.vector (List.map (eval env) xs)\n  | T.Map { T.value = xs } -> Types.map (Types.MalMap.map (eval env) xs)\n  | T.List { T.value = [ T.Symbol \"def!\"; T.Symbol key; expr ] } ->\n      let value = eval env expr in\n      Env.set env key value;\n      value\n  | T.List { T.value = [ T.Symbol \"defmacro!\"; T.Symbol key; expr ] } -> (\n      match eval env expr with\n      | T.Fn ({ macro = false } as f) ->\n          let fn = T.Fn { f with macro = true } in\n          Env.set env key fn;\n          fn\n      | _ -> raise (Invalid_argument \"defmacro! value must be a fn\"))\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.Vector { T.value = bindings }; body ] }\n  | T.List\n      { T.value = [ T.Symbol \"let*\"; T.List { T.value = bindings }; body ] } ->\n      let sub_env = Env.make (Some env) in\n      let rec bind_pairs = function\n        | T.Symbol sym :: expr :: more ->\n            Env.set sub_env sym (eval sub_env expr);\n            bind_pairs more\n        | _ :: _ :: _ -> raise (Invalid_argument \"let* keys must be symbols\")\n        | _ :: [] ->\n            raise\n              (Invalid_argument \"let* bindings must be an even number of forms\")\n        | [] -> ()\n      in\n      bind_pairs bindings;\n      eval sub_env body\n  | T.List { T.value = T.Symbol \"do\" :: body } ->\n      List.fold_left (fun _ -> eval env) T.Nil body\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr; else_expr ] } ->\n      eval env\n        (match eval env test with\n        | T.Nil | T.Bool false -> else_expr\n        | _ -> then_expr)\n  | T.List { T.value = [ T.Symbol \"if\"; test; then_expr ] } -> (\n      match eval env test with\n      | T.Nil | T.Bool false -> T.Nil\n      | _ -> eval env then_expr)\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.Vector { T.value = arg_names }; expr ] }\n  | T.List\n      { T.value = [ T.Symbol \"fn*\"; T.List { T.value = arg_names }; expr ] } ->\n      Types.fn (function args ->\n          let sub_env = Env.make (Some env) in\n          let rec bind_args a b =\n            match (a, b) with\n            | [ T.Symbol \"&\"; T.Symbol name ], args ->\n                Env.set sub_env name (Types.list args)\n            | T.Symbol name :: names, arg :: args ->\n                Env.set sub_env name arg;\n                bind_args names args\n            | [], [] -> ()\n            | _ -> raise (Invalid_argument \"Bad param count in fn call\")\n          in\n          bind_args arg_names args;\n          eval sub_env expr)\n  | T.List { T.value = [ T.Symbol \"quote\"; ast ] } -> ast\n  | T.List { T.value = [ T.Symbol \"quasiquote\"; ast ] } ->\n      eval env (quasiquote ast)\n  | T.List { T.value = [ T.Symbol \"try*\"; scary ] } -> eval env scary\n  | T.List\n      {\n        T.value =\n          [\n            T.Symbol \"try*\";\n            scary;\n            T.List { T.value = [ T.Symbol \"catch*\"; T.Symbol local; handler ] };\n          ];\n      } -> (\n      try eval env scary\n      with exn ->\n        let value =\n          match exn with\n          | Types.MalExn value -> value\n          | Invalid_argument msg -> T.String msg\n          | e -> T.String (Printexc.to_string e)\n        in\n        let sub_env = Env.make (Some env) in\n        Env.set sub_env local value;\n        eval sub_env handler)\n  | T.List { T.value = a0 :: args } -> (\n      match eval env a0 with\n      | T.Fn { value = f; macro = true } -> eval env (f args)\n      | T.Fn { value = f } -> f (List.map (eval env) args)\n      | _ -> raise (Invalid_argument \"Cannot invoke non-function\"))\n  | _ -> ast\n\nlet read str = Reader.read_str str\nlet print = Printer.pr_str true\nlet re str = ignore (eval repl_env (read str))\n\nlet main =\n  Core.init Core.ns;\n  Env.set repl_env \"*ARGV*\"\n    (Types.list\n       (if Array.length Sys.argv > 1 then\n          List.map\n            (fun x -> T.String x)\n            (List.tl (List.tl (Array.to_list Sys.argv)))\n        else []));\n  Env.set repl_env \"eval\"\n    (Types.fn (function [ ast ] -> eval repl_env ast | _ -> T.Nil));\n\n  re \"(def! *host-language* \\\"ocaml\\\")\";\n  re\n    \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\n\\\n     nil)\\\")))))\";\n  re \"(def! not (fn* (a) (if a false true)))\";\n  re\n    \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if \\\n     (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) \\\n     (cons 'cond (rest (rest xs)))))))\";\n\n  if Array.length Sys.argv > 1 then\n    re (Format.asprintf \"(load-file \\\"%s\\\")\" Sys.argv.(1))\n  else (\n    re \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\";\n    try\n      while true do\n        Format.printf \"user> %!\";\n        let line = read_line () in\n        try Format.printf \"%a\\n\" print (eval repl_env (read line)) with\n        | Types.MalExn exc -> Format.printf \"mal exception: %a\\n\" print exc\n        | e -> Format.printf \"ocaml exception: %s\\n\" (Printexc.to_string e)\n      done\n    with End_of_file -> Format.printf \"\\n\")\n"
  },
  {
    "path": "impls/ocaml/tests/step5_tco.mal",
    "content": ";; Ocaml skipping non-TCO recursion\n;; Reason: completes at 50,000, unrecoverable segfaul at 100,000\n"
  },
  {
    "path": "impls/ocaml/types.ml",
    "content": "module rec Types : sig\n  type 'a with_meta = { value : 'a; meta : t }\n\n  and t =\n    | List of t list with_meta\n    | Vector of t list with_meta\n    | Map of t MalMap.t with_meta\n    | Int of int\n    | Symbol of string\n    | Keyword of string\n    | Nil\n    | Bool of bool\n    | String of string\n    | Fn of { value : t list -> t; meta : t; macro : bool }\n    | Atom of t ref\nend =\n  Types\n\nand MalValue : sig\n  type t = Types.t\n\n  val compare : t -> t -> int\nend = struct\n  type t = Types.t\n\n  let compare = compare\nend\n\nand MalMap : (Map.S with type key = MalValue.t) = Map.Make (MalValue)\n\nexception MalExn of Types.t\n\ntype mal_type = MalValue.t\n\nlet list x = Types.List { Types.value = x; meta = Types.Nil }\nlet map x = Types.Map { Types.value = x; meta = Types.Nil }\nlet vector x = Types.Vector { Types.value = x; meta = Types.Nil }\nlet fn f = Types.Fn { macro = false; value = f; meta = Types.Nil }\n\nlet rec list_into_map target source =\n  match source with\n  | k :: v :: more -> list_into_map (MalMap.add k v target) more\n  | [] -> map target\n  | _ :: [] ->\n      raise\n        (Invalid_argument \"Literal maps must contain an even number of forms\")\n"
  },
  {
    "path": "impls/perl/Core.pm",
    "content": "package Core;\nuse re '/msx';\nuse strict;\nuse warnings;\n\nuse English '-no_match_vars';\nuse Hash::Util  qw(fieldhash);\nuse Time::HiRes qw(time);\n\nuse Readline qw(mal_readline);\nuse Types    qw(equal_q thaw_key nil true false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_list);\nuse Interop  qw(pl_to_mal);\n\nuse Exporter 'import';\nour @EXPORT_OK = qw(%NS);\n\n# String functions\n\nsub pr_str {\n    my @args = @_;\n    return Mal::String->new( pr_list( q{ }, 1, @args ) );\n}\n\nsub str {\n    my @args = @_;\n    return Mal::String->new( pr_list( q{}, 0, @args ) );\n}\n\nsub prn {\n    my @args = @_;\n    print pr_list( q{ }, 1, @args ), \"\\n\" or die $ERRNO;\n    return nil;\n}\n\nsub println {\n    my @args = @_;\n    print pr_list( q{ }, 0, @args ), \"\\n\" or die $ERRNO;\n    return nil;\n}\n\nsub core_readline {\n    my ($prompt) = @_;\n    my $line = mal_readline( ${$prompt} );\n    return defined $line ? Mal::String->new($line) : nil;\n}\n\nsub slurp {\n    my ($filename) = @_;\n    local $INPUT_RECORD_SEPARATOR = undef;\n    open my $fh, q{<}, ${$filename} or die $ERRNO;\n    my $data = <$fh>;\n    close $fh or die $ERRNO;\n    return Mal::String->new($data);\n}\n\n# Hash Map functions\n\nsub assoc {\n    my ( $src_hsh, @keys ) = @_;\n    return Mal::HashMap->new( { %{$src_hsh}, @keys } );\n}\n\nsub dissoc {\n    my ( $map, @keys ) = @_;\n    my $new_hsh = { %{$map} };\n    delete @{$new_hsh}{@keys};\n    return Mal::HashMap->new($new_hsh);\n}\n\nsub get {\n    my ( $hsh, $key ) = @_;\n    return $hsh->{$key} // nil;\n}\n\nsub contains_q {\n    my ( $hsh, $key ) = @_;\n    return mal_bool( exists $hsh->{$key} );\n}\n\nsub mal_keys {\n    my ($map) = @_;\n    return Mal::List->new( [ map { thaw_key($_) } keys %{$map} ] );\n}\n\nsub mal_vals {\n    my ($map) = @_;\n    return Mal::List->new( [ values %{$map} ] );\n}\n\n# Sequence functions\n\nsub cons {\n    my ( $a, $b ) = @_;\n    return Mal::List->new( [ $a, @{$b} ] );\n}\n\nsub concat {\n    my @args = @_;\n    return Mal::List->new( [ map { @{$_} } @args ] );\n}\n\nsub nth {\n    my ( $seq, $i ) = @_;\n    return $seq->[ ${$i} ] // die 'nth: index out of bounds';\n}\n\nsub first {\n    my ($seq) = @_;\n    return $seq->[0] // nil;\n}\n\nsub rest {\n    my ($l) = @_;\n    return Mal::List->new( [ @{$l}[ 1 .. $#{$l} ] ] );\n}\n\nsub apply {\n    my ( $f, @args ) = @_;\n    my $more_args = pop @args;\n    return $f->( @args, @{$more_args} );\n}\n\nsub mal_map {\n    my ( $f, $args ) = @_;\n    return Mal::List->new( [ map { $f->($_) } @{$args} ] );\n}\n\nsub conj {\n    my ( $seq, @items ) = @_;\n    if ( $seq->isa('Mal::List') ) {\n        return Mal::List->new( [ reverse(@items), @{$seq} ] );\n    }\n    else {\n        return Mal::Vector->new( [ @{$seq}, @items ] );\n    }\n}\n\nsub seq {\n    my ($arg) = @_;\n    if ( $arg->isa('Mal::List') and @{$arg} ) {\n        return $arg;\n    }\n    if ( $arg->isa('Mal::Vector') and @{$arg} ) {\n        return Mal::List->new( [ @{$arg} ] );\n    }\n    if ( $arg->isa('Mal::String') and length ${$arg} ) {\n        return Mal::List->new(\n            [ map { Mal::String->new($_) } split //, ${$arg} ] );\n    }\n    return nil;\n}\n\nfieldhash my %meta;\n\n# Metadata functions\nsub with_meta {\n    my ( $old, $new_meta ) = @_;\n    my $new_obj = $old->clone;\n    $meta{$new_obj} = $new_meta;\n    return $new_obj;\n}\n\n# Atom functions\nsub swap_bang {\n    my ( $atm, $f, @args ) = @_;\n    return ${$atm} = $f->( ${$atm}, @args );\n}\n\n# Interop\n\n# Force array context so that undef is a valid result.\nsub pl_star {\n    my ($perl) = @_;\n    ## no critic (BuiltinFunctions::ProhibitStringyEval)\n    my @result = eval ${$perl};\n    ## use critic\n    @result or die $EVAL_ERROR;\n    return pl_to_mal( $result[0] );\n}\n\nsub mal_bool {\n    my ($test) = @_;\n    return $test ? true : false;\n}\n\nour %NS = (\n    q{=}       => sub { mal_bool( equal_q( $_[0], $_[1] ) ) },\n    'throw'    => sub { die $_[0] },\n    'nil?'     => sub { mal_bool( $_[0]->isa('Mal::Nil') ) },\n    'true?'    => sub { mal_bool( $_[0]->isa('Mal::True') ) },\n    'false?'   => sub { mal_bool( $_[0]->isa('Mal::False') ) },\n    'number?'  => sub { mal_bool( $_[0]->isa('Mal::Integer') ) },\n    'symbol'   => sub { Mal::Symbol->new( ${ $_[0] } ) },\n    'symbol?'  => sub { mal_bool( $_[0]->isa('Mal::Symbol') ) },\n    'string?'  => sub { mal_bool( $_[0]->isa('Mal::String') ) },\n    'keyword'  => sub { Mal::Keyword->new( ${ $_[0] } ) },\n    'keyword?' => sub { mal_bool( $_[0]->isa('Mal::Keyword') ) },\n    'fn?'      => sub { mal_bool( $_[0]->isa('Mal::Function') ) },\n    'macro?'   => sub { mal_bool( $_[0]->isa('Mal::Macro') ) },\n\n    'pr-str'      => \\&pr_str,\n    'str'         => \\&str,\n    'prn'         => \\&prn,\n    'println'     => \\&println,\n    'readline'    => \\&core_readline,\n    'read-string' => sub { read_str( ${ $_[0] } ) },\n    'slurp'       => \\&slurp,\n    '<'           => sub { mal_bool( ${ $_[0] } < ${ $_[1] } ) },\n    '<='          => sub { mal_bool( ${ $_[0] } <= ${ $_[1] } ) },\n    '>'           => sub { mal_bool( ${ $_[0] } > ${ $_[1] } ) },\n    '>='          => sub { mal_bool( ${ $_[0] } >= ${ $_[1] } ) },\n    q{+}          => sub { Mal::Integer->new( ${ $_[0] } + ${ $_[1] } ) },\n    q{-}          => sub { Mal::Integer->new( ${ $_[0] } - ${ $_[1] } ) },\n    q{*}          => sub { Mal::Integer->new( ${ $_[0] } * ${ $_[1] } ) },\n    q{/}          => sub { Mal::Integer->new( ${ $_[0] } / ${ $_[1] } ) },\n    ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)\n    'time-ms' => sub { Mal::Integer->new( int( time() * 1000 ) ) },\n    ## use critic\n\n    'list'      => sub { Mal::List->new( \\@_ ) },\n    'list?'     => sub { mal_bool( $_[0]->isa('Mal::List') ) },\n    'vector'    => sub { Mal::Vector->new( \\@_ ) },\n    'vector?'   => sub { mal_bool( $_[0]->isa('Mal::Vector') ) },\n    'hash-map'  => sub { Mal::HashMap->new( {@_} ) },\n    'map?'      => sub { mal_bool( $_[0]->isa('Mal::HashMap') ) },\n    'assoc'     => \\&assoc,\n    'dissoc'    => \\&dissoc,\n    'get'       => \\&get,\n    'contains?' => \\&contains_q,\n    'keys'      => \\&mal_keys,\n    'vals'      => \\&mal_vals,\n\n    'sequential?' => sub { mal_bool( $_[0]->isa('Mal::Sequence') ) },\n    'nth'         => \\&nth,\n    'first'       => \\&first,\n    'rest'        => \\&rest,\n    'cons'        => \\&cons,\n    'concat'      => \\&concat,\n    'vec'         => sub { Mal::Vector->new( [ @{ $_[0] } ] ) },\n    'empty?'      => sub { mal_bool( not @{ $_[0] } ) },\n    'count'       => sub { Mal::Integer->new( scalar @{ $_[0] } ) },\n    'apply'       => \\&apply,\n    'map'         => \\&mal_map,\n    'conj'        => \\&conj,\n    'seq'         => \\&seq,\n\n    'with-meta' => \\&with_meta,\n    'meta'      => sub { $meta{ $_[0] } // nil },\n    'atom'      => sub { Mal::Atom->new( $_[0] ) },\n    'atom?'     => sub { mal_bool( $_[0]->isa('Mal::Atom') ) },\n    'deref'     => sub { ${ $_[0] } },\n    'reset!'    => sub { ${ $_[0] } = $_[1] },\n    'swap!'     => \\&swap_bang,\n\n    'pl*' => \\&pl_star,\n);\n\n1;\n"
  },
  {
    "path": "impls/perl/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install perl\n\n# For style checks in Makefile.\n# RUN apt-get -y install libperl-critic-perl perltidy\n"
  },
  {
    "path": "impls/perl/Env.pm",
    "content": "package Env;\nuse strict;\nuse warnings;\n\nuse Exporter 'import';\nour @EXPORT_OK = ();\n\nuse Types;\n\nsub new {\n    my ( $class, $outer, $binds, $exprs ) = @_;\n    my $data = { __outer__ => $outer };\n    if ($binds) {\n        for my $i ( 0 .. $#{$binds} ) {\n            if ( ${ $binds->[$i] } eq q{&} ) {\n\n                # variable length arguments\n                $data->{ ${ $binds->[ $i + 1 ] } } =\n                  Mal::List->new( [ @{$exprs}[ $i .. $#{$exprs} ] ] );\n                last;\n            }\n            $data->{ ${ $binds->[$i] } } = $exprs->[$i];\n        }\n    }\n    return bless $data => $class;\n}\n\nsub get {\n    my ( $self, $key ) = @_;\n    while ( not $self->{$key} ) {\n        $self = $self->{__outer__} // return;\n    }\n    return $self->{$key};\n}\n\n## no critic (NamingConventions::ProhibitAmbiguousNames)\nsub set {\n    ## use critic\n    my ( $self, $key, $value ) = @_;\n    $self->{$key} = $value;\n    return $value;\n}\n\n#my $e1 = Env->new();\n#print Dumper($e1);\n#\n#my $e2 = Env->new();\n#$e2->set('abc', 123);\n#$e2->set('def', 456);\n#print Dumper($e2);\n#\n#my $e3 = Env->new($e2);\n#$e3->set('abc', 789);\n#$e3->set('ghi', 1024);\n#print Dumper($e3);\n#\n#print Dumper($e3->get('abc'));\n#print Dumper($e3->get('def'));\n\n1;\n"
  },
  {
    "path": "impls/perl/Interop.pm",
    "content": "package Interop;\nuse re '/msx';\nuse strict;\nuse warnings;\n\nuse Exporter 'import';\nour @EXPORT_OK = qw( pl_to_mal );\nuse Scalar::Util qw(looks_like_number);\n\nuse Types qw(nil);\n\nsub pl_to_mal {\n    my ($obj) = @_;\n    defined $obj or return nil;\n    $_ = ref $obj;\n    if (/^ARRAY/) {\n        return Mal::List->new( [ map { pl_to_mal($_) } @{$obj} ] );\n    }\n    if (/^HASH/) {\n        return Mal::HashMap->new( { map { pl_to_mal($_) } %{$obj} } );\n    }\n    if ( $_ eq q{} ) {\n        if ( looks_like_number $obj ) {\n            return Mal::Integer->new($obj);\n        }\n        return Mal::String->new($obj);\n    }\n    die 'Failed to convert a perl object to mal.';\n}\n\n1;\n"
  },
  {
    "path": "impls/perl/Makefile",
    "content": "SOURCES_BASE = \\\n  Readline.pm \\\n  Types.pm \\\n  Reader.pm \\\n  Printer.pm \\\n  Interop.pm\nSOURCES_LISP = \\\n  Env.pm \\\n  Core.pm \\\n  stepA_mal.pl\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ndist: mal.pl mal\n\nmal.pl: $(SOURCES)\n\t#fatpack pack ./stepA_mal.pl > $@\n\tfatpack trace ./stepA_mal.pl\n\tfatpack packlists-for `cat fatpacker.trace` > packlists\n\tfatpack tree `cat packlists`\n\tcp $+ fatlib/\n\t(fatpack file; cat ./stepA_mal.pl) > mal.pl\n\nmal: mal.pl\n\techo \"#!/usr/bin/env perl\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.pl mal fatpacker.trace packlists fatlib/* *-lint\n\t[ -d fatlib ] && rmdir fatlib || true\n\nno_critic := \\\n  ErrorHandling::RequireCarping \\\n  RequireVersionVar \\\n  # EOL\n\nlint-all: $(addsuffix -lint,$(wildcard *.pl *.pm))\nlint: $(SOURCES:%=%-lint)\n%-lint: % Makefile\n\tperl -c -I. $*\n\tperltidy -st $* | diff -u $* -\n\tperlcritic -1 --verbose 11 $(no_critic:%=--exclude=%) $*\n\ttouch $@\n"
  },
  {
    "path": "impls/perl/Printer.pm",
    "content": "package Printer;\nuse re '/msx';\nuse strict;\nuse warnings;\n\nuse Exporter 'import';\nour @EXPORT_OK = qw( pr_list pr_str );\n\nuse Types qw(thaw_key);\n\nuse List::Util qw(pairmap);\n\nsub pr_str {\n    my ( $obj, $print_readably ) = @_;\n    my $_r = $print_readably // 1;\n    if ( $obj->isa('Mal::List') ) {\n        return '(' . pr_list( q{ }, $_r, @{$obj} ) . ')';\n    }\n    if ( $obj->isa('Mal::Vector') ) {\n        return '[' . pr_list( q{ }, $_r, @{$obj} ) . ']';\n    }\n    if ( $obj->isa('Mal::HashMap') ) {\n        return\n          '{'\n          . pr_list( q{ }, $_r, pairmap { thaw_key($a) => $b } %{$obj} ) . '}';\n    }\n    if ( $obj->isa('Mal::Keyword') ) {\n        return \":${$obj}\";\n    }\n    if ( $obj->isa('Mal::String') ) {\n        if ($_r) {\n            my $str = ${$obj};\n            $str =~ s/\\\\/\\\\\\\\/g;\n            $str =~ s/\"/\\\\\"/g;\n            $str =~ s/\\n/\\\\n/g;\n            return qq{\"$str\"};\n        }\n        else {\n            return ${$obj};\n        }\n    }\n    if ( $obj->isa('Mal::Atom') ) {\n        return '(atom ' . pr_str( ${$obj} ) . ')';\n    }\n    if ( $obj->isa('Mal::Function') ) {\n        return \"<fn* $obj>\";\n    }\n    if ( $obj->isa('Mal::Macro') ) {\n        return \"<macro* $obj>\";\n    }\n    return ${$obj};\n}\n\nsub pr_list {\n    my ( $separator, $readably, @objs ) = @_;\n    return join $separator, map { pr_str( $_, $readably ) } @objs;\n}\n\n1;\n"
  },
  {
    "path": "impls/perl/README.md",
    "content": "# Notes on the mal implementation in Perl5.\n\nThis implementation should work in any perl from 5.19.3 onwards.\nEarlier versions are likely to work too as long as you install a new\nList::Util.  The implementation uses the experimental `switch`\nfeature, which may make it vulnerable to future changes in perl.\n\nMal objects are all in subclasses of `Mal::Type`, and can be treated\nas scalar, array, or hash references as appropriate.\n\nMetadata support uses `Hash::Util::FieldHash` to attach external\nmetadata to objects.  This means that in the metadata system imposes\nno overhead on the normal use of objects.\n\nHash-maps are slightly magical.  They're keyed by the stringified\nversions of mal objects, and `Mal::Scalar` overloads stringification\nso that this works properly.\n\nTail-call optimisation uses Perl's built-in `goto &NAME` syntax for\nexplicit tail calls.  This allows functions defined by `fn*` to be\nimplemented as functions at the Perl layer.\n\nPerl's garbage-collection is based on reference counting.  This means\nthat reference loops will cause memory leaks, and in particular using\n`def!` to define a function will cause that function to have a\nreference to the environment it's defined in, making a small reference\nloop and hence a memory leak.  This can be avoided by carefully\nundefining any function before it goes out of scope.\n"
  },
  {
    "path": "impls/perl/Reader.pm",
    "content": "package Reader;\nuse re '/msx';\nuse strict;\nuse warnings;\n\nuse Exporter 'import';\nour @EXPORT_OK = qw( read_str );\n\nuse Types qw(nil true false);\n\nmy $separators = <<'EOF';\n(?: [\\s,] | ; [^\\n]* \\n )*\nEOF\n\nmy $normal = <<'EOF';\n[^\\s,;'`~@^()[\\]{}\"]\nEOF\n\nsub read_list {\n    my ( $str, $end ) = @_;\n\n    # print \"read_list: /${$str}/$end/\\n\";\n    my @lst;\n    while () {\n        ${$str} =~ s/ \\A $separators //;\n        ${$str} or die \"expected '$end', got EOF\";\n        last if ( ${$str} =~ s/ \\A $end // );\n        push @lst, read_form($str);\n    }\n    return \\@lst;\n}\n\nsub quote {\n    my ( $quoter, @args ) = @_;\n\n    # print \"read_form: quote/$quoter/\\n\";\n    return Mal::List->new( [ Mal::Symbol->new($quoter), @args ] );\n}\n\nsub read_form {\n    my $str = shift;\n\n    # print \"read_form: /${$str}/\\n\";\n\n    # Always skip initial separators.\n    ${$str} =~ s/ \\A $separators //;\n\n    if ( ${$str} =~ s/ \\A ' // ) {\n        return quote( 'quote', read_form($str) );\n    }\n    if ( ${$str} =~ s/ \\A ` // ) {\n        return quote( 'quasiquote', read_form($str) );\n    }\n    if ( ${$str} =~ s/ \\A ~ // ) {\n        return quote( ${$str} =~ s/ \\A @ // ? 'splice-unquote' : 'unquote',\n            read_form($str) );\n    }\n    if ( ${$str} =~ s/ \\A \\^ // ) {\n        my $meta = read_form($str);\n        return quote( 'with-meta', read_form($str), $meta );\n    }\n    if ( ${$str} =~ s/ \\A @ // ) {\n        return quote( 'deref', read_form($str) );\n    }\n    if ( ${$str} =~ s/ \\A [(] // ) {\n        return Mal::List->new( read_list( $str, '\\)' ) );\n    }\n    if ( ${$str} =~ s/ \\A \\[ // ) {\n        return Mal::Vector->new( read_list( $str, '\\]' ) );\n    }\n    if ( ${$str} =~ s/ \\A [{] // ) {\n        return Mal::HashMap->new( { @{ read_list( $str, '\\}' ) } } );\n    }\n    if ( ${$str} =~ s/ \\A ( -? \\d+ ) // ) {\n        return Mal::Integer->new($1);\n    }\n    if ( ${$str} =~ s/ \\A \" // ) {\n        ${$str} =~ s/ \\A ( (?: \\\\ . | [^\\\\\"] )* ) \" //\n          or die 'expected \", got EOF';\n        return Mal::String->new( $1 =~ s/ \\\\ (.) / $1 =~ tr|n|\\n|r /ger );\n    }\n    if ( ${$str} =~ s/ \\A : // ) {\n        ${$str} =~ s/ \\A ( $normal + ) //\n          or die 'letters expected after a colon';\n        return Mal::Keyword->new($1);\n    }\n    if ( ${$str} =~ s/ \\A ( $normal+ ) // ) {\n        if ( $1 eq 'nil' )   { return nil; }\n        if ( $1 eq 'true' )  { return true; }\n        if ( $1 eq 'false' ) { return false; }\n        return Mal::Symbol->new($1);\n    }\n    if ( ${$str} =~ / \\A [)\\]}] / ) {\n        die \"unexpected '$1'\";\n    }\n    die \"Failed to parse '${$str}'\";\n}\n\nsub read_str {\n    my $str = shift;\n    return read_form( \\$str );\n}\n\n#print Dumper(read_str(\"123\"));\n#print Dumper(read_str(\"+\"));\n#print Dumper(read_str(\"\\\"abc\\\"\"));\n#print Dumper(read_str(\"nil\"));\n#print Dumper(read_str(\"true\"));\n#print Dumper(read_str(\"false\"));\n#print Dumper(read_str(\"(+ 2 3)\"));\n#print Dumper(read_str(\"(foo 2 (3 4))\"));\n\n1;\n"
  },
  {
    "path": "impls/perl/Readline.pm",
    "content": "# To get readline line editing functionality, please install\n# Term::ReadKey and either Term::ReadLine::Gnu (GPL) or\n# Term::ReadLine::Perl (GPL, Artistic) from CPAN.\n\npackage Readline;\nuse re '/msx';\nuse strict;\nuse warnings;\n\nuse English '-no_match_vars';\nuse Term::ReadLine;\n\nuse Exporter 'import';\nour @EXPORT_OK = qw( mal_readline set_rl_mode );\n\nmy $_rl = Term::ReadLine->new('Mal');\n$_rl->ornaments(0);\n\n#print \"Using ReadLine implementation: \" . $_rl->ReadLine() . \"\\n\";\nmy $OUT             = $_rl->OUT || \\*STDOUT;\nmy $_history_loaded = 0;\n\nmy $history_file = \"$ENV{'HOME'}/.mal-history\";\n\nsub save_line {\n    my ($line) = @_;\n    open my $fh, '>>', $history_file or return;\n    print {$fh} \"$line\\n\" or die $ERRNO;\n    close $fh             or die $ERRNO;\n    return;\n}\n\nsub load_history {\n    open my $fh, q{<}, $history_file or return;\n\n    while ( my $line = <$fh> ) {\n        chomp $line;\n        $line =~ /\\S/ or next;\n        $_rl->addhistory($line);\n    }\n\n    close $fh or die $ERRNO;\n    return;\n}\n\nmy $rl_mode = 'terminal';\n\nsub set_rl_mode {\n    my ($mode) = @_;\n    $rl_mode = $mode;\n    return;\n}\n\nsub mal_readline {\n    my ($prompt) = @_;\n    my $line;\n    if ( !$_history_loaded ) {\n        $_history_loaded = 1;\n        load_history();\n    }\n\n    if ( $rl_mode eq 'terminal' ) {\n        $line = $_rl->readline($prompt);\n    }\n    else {\n        print $prompt or die $ERRNO;\n        $line = readline *STDIN;\n    }\n    if ($line) {\n        chomp $line;\n        if ($line) {\n            save_line($line);\n        }\n    }\n    return $line;\n}\n\n1;\n"
  },
  {
    "path": "impls/perl/Types.pm",
    "content": "package Types;\nuse re '/msx';\nuse strict;\nuse warnings;\n\nuse Exporter 'import';\n\nour @EXPORT_OK = qw(equal_q thaw_key\n  nil true false);\n\n## no critic (Modules::ProhibitMultiplePackages)\n\n# General functions\n\nsub equal_q {\n    my ( $a, $b ) = @_;\n    if ( $a->isa('Mal::Sequence') ) {\n        $b->isa('Mal::Sequence')     or return 0;\n        scalar @{$a} == scalar @{$b} or return 0;\n        for ( 0 .. $#{$a} ) {\n            equal_q( $a->[$_], $b->[$_] ) or return 0;\n        }\n        return 1;\n    }\n    ref $b eq ref $a or return 0;\n    if ( $a->isa('Mal::HashMap') ) {\n        scalar keys %{$a} == scalar keys %{$b} or return 0;\n        while ( my ( $k, $v ) = each %{$a} ) {\n            equal_q( $v, $b->{$k} ) or return 0;\n        }\n        return 1;\n    }\n    return ${$a} eq ${$b};\n}\n\n# Superclass for all kinds of mal value\n\n{\n\n    package Mal::Type;\n}\n\n# Scalars\n\n{\n\n    package Mal::Scalar;\n    use parent -norequire, 'Mal::Type';\n\n    # Overload stringification so that its result is something\n    # suitable for use as a hash-map key.  The important thing here is\n    # that strings and keywords are distinct: support for other kinds\n    # of scalar is a bonus.\n    use overload\n      '\"\"'     => sub { my $self = shift; ref($self) . q{ } . ${$self} },\n      fallback => 1;\n\n    sub new {\n        my ( $class, $value ) = @_;\n        return bless \\$value, $class;\n    }\n}\n\n# This function converts hash-map keys back into full objects\n\nsub thaw_key {\n    my ($key) = @_;\n    my ( $class, $value ) = split m/[ ]/, $key, 2;\n    return $class->new($value);\n}\n\n{\n\n    package Mal::Nil;\n    use parent -norequire, 'Mal::Scalar';\n\n    # Allow nil to be treated as an empty list or hash-map.\n    use overload '@{}' => sub { [] }, '%{}' => sub { {} }, fallback => 1;\n\n}\n{\n\n    package Mal::True;\n    use parent -norequire, 'Mal::Scalar';\n}\n{\n\n    package Mal::False;\n    use parent -norequire, 'Mal::Scalar';\n}\n\nmy $nil   = Mal::Nil->new('nil');\nmy $true  = Mal::True->new('true');\nmy $false = Mal::False->new('false');\nsub nil   { return $nil; }\nsub true  { return $true; }\nsub false { return $false; }\n\n{\n\n    package Mal::Integer;\n    use parent -norequire, 'Mal::Scalar';\n}\n\n{\n\n    package Mal::Symbol;\n    use parent -norequire, 'Mal::Scalar';\n}\n\n{\n\n    package Mal::String;\n    use parent -norequire, 'Mal::Scalar';\n}\n\n{\n\n    package Mal::Keyword;\n    use parent -norequire, 'Mal::Scalar';\n}\n\n# Sequences\n\n{\n\n    package Mal::Sequence;\n    use parent -norequire, 'Mal::Type';\n\n    sub new {\n        my ( $class, $data ) = @_;\n        return bless $data, $class;\n    }\n\n    sub clone {\n        my $self = shift;\n        return ref($self)->new( [ @{$self} ] );\n    }\n}\n\n# Lists\n\n{\n\n    package Mal::List;\n    use parent -norequire, 'Mal::Sequence';\n}\n\n# Vectors\n\n{\n\n    package Mal::Vector;\n    use parent -norequire, 'Mal::Sequence';\n}\n\n# Hash-maps\n\n{\n\n    package Mal::HashMap;\n    use parent -norequire, 'Mal::Type';\n\n    sub new {\n        my ( $class, $src ) = @_;\n        return bless $src, $class;\n    }\n\n    sub clone {\n        my $self = shift;\n        return ref($self)->new( { %{$self} } );\n    }\n}\n\n# Functions\n\n{\n\n    package Mal::Callable;\n    use parent -norequire, 'Mal::Type';\n\n    sub new {\n        my ( $class, $data ) = @_;\n        return bless $data, $class;\n    }\n\n    sub clone {\n        my $self = shift;\n        return bless sub { goto &{$self} }, ref $self;\n    }\n}\n\n{\n\n    package Mal::Function;\n    use parent -norequire, 'Mal::Callable';\n}\n\n{\n\n    package Mal::Macro;\n    use parent -norequire, 'Mal::Callable';\n}\n\n# Atoms\n\n{\n\n    package Mal::Atom;\n    use parent -norequire, 'Mal::Type';\n\n    sub new {\n        my ( $class, $val ) = @_;\n        return bless \\$val, $class;\n    }\n}\n\n1;\n"
  },
  {
    "path": "impls/perl/run",
    "content": "#!/usr/bin/env bash\nexec perl $(dirname $0)/${STEP:-stepA_mal}.pl \"${@}\"\n"
  },
  {
    "path": "impls/perl/step0_repl.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\n\nuse Readline qw(mal_readline set_rl_mode);\n\n# read\nsub READ {\n    my $str = shift;\n    return $str;\n}\n\n# eval\nsub EVAL {\n    my ($ast) = @_;\n    return $ast;\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return $exp;\n}\n\n# repl\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str) ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\n\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    print REP($line), \"\\n\" or die $ERRNO;\n}\n"
  },
  {
    "path": "impls/perl/step1_read_print.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\nsub EVAL {\n    my ($ast) = @_;\n    return $ast;\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str) ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\n\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step2_eval.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw();\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    #print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->{ ${$ast} } // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        my $f = EVAL( $a0, $env );\n        return $f->( map { EVAL( $_, $env ) } @args );\n    }\n    return $ast;\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = {\n    q{+} => sub { Mal::Integer->new( ${ $_[0] } + ${ $_[1] } ) },\n    q{-} => sub { Mal::Integer->new( ${ $_[0] } - ${ $_[1] } ) },\n    q{*} => sub { Mal::Integer->new( ${ $_[0] } * ${ $_[1] } ) },\n    q{/} => sub { Mal::Integer->new( ${ $_[0] } / ${ $_[1] } ) },\n};\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\n\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step3_env.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairs pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            return $sf->( $env, @args );\n        }\n        my $f = EVAL( $a0, $env );\n        return $f->( map { EVAL( $_, $env ) } @args );\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    return EVAL( $body, $let_env );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n$repl_env->set( q{+}, sub { Mal::Integer->new( ${ $_[0] } + ${ $_[1] } ) } );\n$repl_env->set( q{-}, sub { Mal::Integer->new( ${ $_[0] } - ${ $_[1] } ) } );\n$repl_env->set( q{*}, sub { Mal::Integer->new( ${ $_[0] } * ${ $_[1] } ) } );\n$repl_env->set( q{/}, sub { Mal::Integer->new( ${ $_[0] } / ${ $_[1] } ) } );\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\n\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step4_if_fn_do.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairs pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            return $sf->( $env, @args );\n        }\n        my $f = EVAL( $a0, $env );\n        return $f->( map { EVAL( $_, $env ) } @args );\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    return EVAL( $body, $let_env );\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    return EVAL( $final, $env );\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        return EVAL( $then, $env );\n    }\n    if ($else) {\n        return EVAL( $else, $env );\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            return EVAL( $body, Env->new( $env, $params, \\@_ ) );\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n\n# core.mal: defined using the language itself\nREP(q[(def! not (fn* (a) (if a false true)))]);\n\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step5_tco.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings FATAL => 'recursion';\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairs pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# False positives because of TCO.\n## no critic (Subroutines::RequireArgUnpacking)\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            @_ = ( $env, @args );\n            goto &{$sf};\n        }\n        my $f = EVAL( $a0, $env );\n        @_ = map { EVAL( $_, $env ) } @args;\n        goto &{$f};\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    @_ = ( $body, $let_env );\n    goto &EVAL;\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    @_ = ( $final, $env );\n    goto &EVAL;\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        @_ = ( $then, $env );\n        goto &EVAL;\n    }\n    if ($else) {\n        @_ = ( $else, $env );\n        goto &EVAL;\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            @_ = ( $body, Env->new( $env, $params, \\@_ ) );\n            goto &EVAL;\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n\n# core.mal: defined using the language itself\nREP(q[(def! not (fn* (a) (if a false true)))]);\n\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step6_file.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings FATAL => 'recursion';\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairs pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# False positives because of TCO.\n## no critic (Subroutines::RequireArgUnpacking)\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            @_ = ( $env, @args );\n            goto &{$sf};\n        }\n        my $f = EVAL( $a0, $env );\n        @_ = map { EVAL( $_, $env ) } @args;\n        goto &{$f};\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    @_ = ( $body, $let_env );\n    goto &EVAL;\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    @_ = ( $final, $env );\n    goto &EVAL;\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        @_ = ( $then, $env );\n        goto &EVAL;\n    }\n    if ($else) {\n        @_ = ( $else, $env );\n        goto &EVAL;\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            @_ = ( $body, Env->new( $env, $params, \\@_ ) );\n            goto &EVAL;\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\nmy $script_file = shift @ARGV;\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n$repl_env->set( 'eval',\n    Mal::Function->new( sub { EVAL( $_[0], $repl_env ) } ) );\n$repl_env->set( '*ARGV*',\n    Mal::List->new( [ map { Mal::String->new($_) } @ARGV ] ) );\n\n# core.mal: defined using the language itself\nREP(q[(def! not (fn* (a) (if a false true)))]);\nREP(<<'EOF');\n(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nEOF\n\nif ( defined $script_file ) {\n    REP(qq[(load-file \"$script_file\")]);\n    exit 0;\n}\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step7_quote.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings FATAL => 'recursion';\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairs pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# False positives because of TCO.\n## no critic (Subroutines::RequireArgUnpacking)\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\nsub starts_with {\n    my ( $ast, $sym ) = @_;\n    return @{$ast} && $ast->[0]->isa('Mal::Symbol') && ${ $ast->[0] } eq $sym;\n}\n\nsub quasiquote_loop {\n    my ($ast) = @_;\n    my $res = Mal::List->new( [] );\n    foreach my $elt ( reverse @{$ast} ) {\n        if ( $elt->isa('Mal::List') and starts_with( $elt, 'splice-unquote' ) )\n        {\n            $res =\n              Mal::List->new( [ Mal::Symbol->new('concat'), $elt->[1], $res ] );\n        }\n        else {\n            $res = Mal::List->new(\n                [ Mal::Symbol->new('cons'), quasiquote($elt), $res ] );\n        }\n    }\n    return $res;\n}\n\nsub quasiquote {\n    my ($ast) = @_;\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::List->new(\n            [ Mal::Symbol->new('vec'), quasiquote_loop($ast) ] );\n    }\n    if ( $ast->isa('Mal::HashMap') or $ast->isa('Mal::Symbol') ) {\n        return Mal::List->new( [ Mal::Symbol->new('quote'), $ast ] );\n    }\n    if ( $ast->isa('Mal::List') ) {\n        if ( starts_with( $ast, 'unquote' ) ) {\n            return $ast->[1];\n        }\n        return quasiquote_loop($ast);\n    }\n    return $ast;\n}\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n\n    'quasiquote' => \\&special_quasiquote,\n    'quote'      => \\&special_quote,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            @_ = ( $env, @args );\n            goto &{$sf};\n        }\n        my $f = EVAL( $a0, $env );\n        @_ = map { EVAL( $_, $env ) } @args;\n        goto &{$f};\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    @_ = ( $body, $let_env );\n    goto &EVAL;\n}\n\nsub special_quote {\n    my ( $env, $quoted ) = @_;\n    return $quoted;\n}\n\nsub special_quasiquote {\n    my ( $env, $quoted ) = @_;\n    @_ = ( quasiquote($quoted), $env );\n    goto &EVAL;\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    @_ = ( $final, $env );\n    goto &EVAL;\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        @_ = ( $then, $env );\n        goto &EVAL;\n    }\n    if ($else) {\n        @_ = ( $else, $env );\n        goto &EVAL;\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            @_ = ( $body, Env->new( $env, $params, \\@_ ) );\n            goto &EVAL;\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\nmy $script_file = shift @ARGV;\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n$repl_env->set( 'eval',\n    Mal::Function->new( sub { EVAL( $_[0], $repl_env ) } ) );\n$repl_env->set( '*ARGV*',\n    Mal::List->new( [ map { Mal::String->new($_) } @ARGV ] ) );\n\n# core.mal: defined using the language itself\nREP(q[(def! not (fn* (a) (if a false true)))]);\nREP(<<'EOF');\n(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nEOF\n\nif ( defined $script_file ) {\n    REP(qq[(load-file \"$script_file\")]);\n    exit 0;\n}\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step8_macros.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings FATAL => 'recursion';\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util qw(pairs pairmap);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# False positives because of TCO.\n## no critic (Subroutines::RequireArgUnpacking)\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\nsub starts_with {\n    my ( $ast, $sym ) = @_;\n    return @{$ast} && $ast->[0]->isa('Mal::Symbol') && ${ $ast->[0] } eq $sym;\n}\n\nsub quasiquote_loop {\n    my ($ast) = @_;\n    my $res = Mal::List->new( [] );\n    foreach my $elt ( reverse @{$ast} ) {\n        if ( $elt->isa('Mal::List') and starts_with( $elt, 'splice-unquote' ) )\n        {\n            $res =\n              Mal::List->new( [ Mal::Symbol->new('concat'), $elt->[1], $res ] );\n        }\n        else {\n            $res = Mal::List->new(\n                [ Mal::Symbol->new('cons'), quasiquote($elt), $res ] );\n        }\n    }\n    return $res;\n}\n\nsub quasiquote {\n    my ($ast) = @_;\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::List->new(\n            [ Mal::Symbol->new('vec'), quasiquote_loop($ast) ] );\n    }\n    if ( $ast->isa('Mal::HashMap') or $ast->isa('Mal::Symbol') ) {\n        return Mal::List->new( [ Mal::Symbol->new('quote'), $ast ] );\n    }\n    if ( $ast->isa('Mal::List') ) {\n        if ( starts_with( $ast, 'unquote' ) ) {\n            return $ast->[1];\n        }\n        return quasiquote_loop($ast);\n    }\n    return $ast;\n}\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n\n    'quasiquote' => \\&special_quasiquote,\n    'quote'      => \\&special_quote,\n\n    'defmacro!' => \\&special_defmacro,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            @_ = ( $env, @args );\n            goto &{$sf};\n        }\n        my $f = EVAL( $a0, $env );\n        if ( $f->isa('Mal::Macro') ) {\n            @_ = ( $f->(@args), $env );\n            goto &EVAL;\n        }\n        @_ = map { EVAL( $_, $env ) } @args;\n        goto &{$f};\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    @_ = ( $body, $let_env );\n    goto &EVAL;\n}\n\nsub special_quote {\n    my ( $env, $quoted ) = @_;\n    return $quoted;\n}\n\nsub special_quasiquote {\n    my ( $env, $quoted ) = @_;\n    @_ = ( quasiquote($quoted), $env );\n    goto &EVAL;\n}\n\nsub special_defmacro {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, Mal::Macro->new( EVAL( $val, $env )->clone ) );\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    @_ = ( $final, $env );\n    goto &EVAL;\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        @_ = ( $then, $env );\n        goto &EVAL;\n    }\n    if ($else) {\n        @_ = ( $else, $env );\n        goto &EVAL;\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            @_ = ( $body, Env->new( $env, $params, \\@_ ) );\n            goto &EVAL;\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\nmy $script_file = shift @ARGV;\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n$repl_env->set( 'eval',\n    Mal::Function->new( sub { EVAL( $_[0], $repl_env ) } ) );\n$repl_env->set( '*ARGV*',\n    Mal::List->new( [ map { Mal::String->new($_) } @ARGV ] ) );\n\n# core.mal: defined using the language itself\nREP(q[(def! not (fn* (a) (if a false true)))]);\nREP(<<'EOF');\n(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nEOF\nREP(<<'EOF');\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)\n(if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\"))\n(cons 'cond (rest (rest xs)))))))\nEOF\n\nif ( defined $script_file ) {\n    REP(qq[(load-file \"$script_file\")]);\n    exit 0;\n}\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/step9_try.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings FATAL => 'recursion';\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util   qw(pairs pairmap);\nuse Scalar::Util qw(blessed);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# False positives because of TCO.\n## no critic (Subroutines::RequireArgUnpacking)\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\nsub starts_with {\n    my ( $ast, $sym ) = @_;\n    return @{$ast} && $ast->[0]->isa('Mal::Symbol') && ${ $ast->[0] } eq $sym;\n}\n\nsub quasiquote_loop {\n    my ($ast) = @_;\n    my $res = Mal::List->new( [] );\n    foreach my $elt ( reverse @{$ast} ) {\n        if ( $elt->isa('Mal::List') and starts_with( $elt, 'splice-unquote' ) )\n        {\n            $res =\n              Mal::List->new( [ Mal::Symbol->new('concat'), $elt->[1], $res ] );\n        }\n        else {\n            $res = Mal::List->new(\n                [ Mal::Symbol->new('cons'), quasiquote($elt), $res ] );\n        }\n    }\n    return $res;\n}\n\nsub quasiquote {\n    my ($ast) = @_;\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::List->new(\n            [ Mal::Symbol->new('vec'), quasiquote_loop($ast) ] );\n    }\n    if ( $ast->isa('Mal::HashMap') or $ast->isa('Mal::Symbol') ) {\n        return Mal::List->new( [ Mal::Symbol->new('quote'), $ast ] );\n    }\n    if ( $ast->isa('Mal::List') ) {\n        if ( starts_with( $ast, 'unquote' ) ) {\n            return $ast->[1];\n        }\n        return quasiquote_loop($ast);\n    }\n    return $ast;\n}\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n\n    'quasiquote' => \\&special_quasiquote,\n    'quote'      => \\&special_quote,\n\n    'defmacro!' => \\&special_defmacro,\n\n    'try*' => \\&special_try,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            @_ = ( $env, @args );\n            goto &{$sf};\n        }\n        my $f = EVAL( $a0, $env );\n        if ( $f->isa('Mal::Macro') ) {\n            @_ = ( $f->(@args), $env );\n            goto &EVAL;\n        }\n        @_ = map { EVAL( $_, $env ) } @args;\n        goto &{$f};\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    @_ = ( $body, $let_env );\n    goto &EVAL;\n}\n\nsub special_quote {\n    my ( $env, $quoted ) = @_;\n    return $quoted;\n}\n\nsub special_quasiquote {\n    my ( $env, $quoted ) = @_;\n    @_ = ( quasiquote($quoted), $env );\n    goto &EVAL;\n}\n\nsub special_defmacro {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, Mal::Macro->new( EVAL( $val, $env )->clone ) );\n}\n\nsub special_try {\n    my ( $env, $try, $catch ) = @_;\n    if ($catch) {\n        my ( undef, $binding, $body ) = @{$catch};\n        if ( my $ret = eval { EVAL( $try, $env ) } ) {\n            return $ret;\n        }\n        my $exc = $EVAL_ERROR;\n        if ( not blessed($exc) or not $exc->isa('Mal::Type') ) {\n            chomp $exc;\n            $exc = Mal::String->new($exc);\n        }\n        my $catch_env = Env->new( $env, [$binding], [$exc] );\n        @_ = ( $body, $catch_env );\n        goto &EVAL;\n    }\n    @_ = ( $try, $env );\n    goto &EVAL;\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    @_ = ( $final, $env );\n    goto &EVAL;\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        @_ = ( $then, $env );\n        goto &EVAL;\n    }\n    if ($else) {\n        @_ = ( $else, $env );\n        goto &EVAL;\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            @_ = ( $body, Env->new( $env, $params, \\@_ ) );\n            goto &EVAL;\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\nmy $script_file = shift @ARGV;\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n$repl_env->set( 'eval',\n    Mal::Function->new( sub { EVAL( $_[0], $repl_env ) } ) );\n$repl_env->set( '*ARGV*',\n    Mal::List->new( [ map { Mal::String->new($_) } @ARGV ] ) );\n\n# core.mal: defined using the language itself\nREP(q[(def! not (fn* (a) (if a false true)))]);\nREP(<<'EOF');\n(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nEOF\nREP(<<'EOF');\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)\n(if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\"))\n(cons 'cond (rest (rest xs)))))))\nEOF\n\nif ( defined $script_file ) {\n    REP(qq[(load-file \"$script_file\")]);\n    exit 0;\n}\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        if ( defined blessed($err) and $err->isa('Mal::Type') ) {\n            $err = pr_str($err) . \"\\n\";\n        }\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/stepA_mal.pl",
    "content": "#!/usr/bin/perl\n\nuse strict;\nuse warnings FATAL => 'recursion';\nuse File::Basename 'dirname';\nuse lib dirname(__FILE__);\n\nuse English '-no_match_vars';\nuse List::Util   qw(pairs pairmap);\nuse Scalar::Util qw(blessed);\n\nuse Readline qw(mal_readline set_rl_mode);\nuse Types    qw(nil false);\nuse Reader   qw(read_str);\nuse Printer  qw(pr_str);\nuse Env;\nuse Core qw(%NS);\n\n# False positives because of TCO.\n## no critic (Subroutines::RequireArgUnpacking)\n\n# read\nsub READ {\n    my $str = shift;\n    return read_str($str);\n}\n\n# eval\nsub starts_with {\n    my ( $ast, $sym ) = @_;\n    return @{$ast} && $ast->[0]->isa('Mal::Symbol') && ${ $ast->[0] } eq $sym;\n}\n\nsub quasiquote_loop {\n    my ($ast) = @_;\n    my $res = Mal::List->new( [] );\n    foreach my $elt ( reverse @{$ast} ) {\n        if ( $elt->isa('Mal::List') and starts_with( $elt, 'splice-unquote' ) )\n        {\n            $res =\n              Mal::List->new( [ Mal::Symbol->new('concat'), $elt->[1], $res ] );\n        }\n        else {\n            $res = Mal::List->new(\n                [ Mal::Symbol->new('cons'), quasiquote($elt), $res ] );\n        }\n    }\n    return $res;\n}\n\nsub quasiquote {\n    my ($ast) = @_;\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::List->new(\n            [ Mal::Symbol->new('vec'), quasiquote_loop($ast) ] );\n    }\n    if ( $ast->isa('Mal::HashMap') or $ast->isa('Mal::Symbol') ) {\n        return Mal::List->new( [ Mal::Symbol->new('quote'), $ast ] );\n    }\n    if ( $ast->isa('Mal::List') ) {\n        if ( starts_with( $ast, 'unquote' ) ) {\n            return $ast->[1];\n        }\n        return quasiquote_loop($ast);\n    }\n    return $ast;\n}\n\nmy %special_forms = (\n    'def!' => \\&special_def,\n    'let*' => \\&special_let,\n\n    'do'  => \\&special_do,\n    'if'  => \\&special_if,\n    'fn*' => \\&special_fn,\n\n    'quasiquote' => \\&special_quasiquote,\n    'quote'      => \\&special_quote,\n\n    'defmacro!' => \\&special_defmacro,\n\n    'try*' => \\&special_try,\n);\n\nsub EVAL {\n    my ( $ast, $env ) = @_;\n\n    my $dbgeval = $env->get('DEBUG-EVAL');\n    if (    $dbgeval\n        and not $dbgeval->isa('Mal::Nil')\n        and not $dbgeval->isa('Mal::False') )\n    {\n        print 'EVAL: ', pr_str($ast), \"\\n\" or die $ERRNO;\n    }\n\n    if ( $ast->isa('Mal::Symbol') ) {\n        return $env->get( ${$ast} ) // die \"'${$ast}' not found\\n\";\n    }\n    if ( $ast->isa('Mal::Vector') ) {\n        return Mal::Vector->new( [ map { EVAL( $_, $env ) } @{$ast} ] );\n    }\n    if ( $ast->isa('Mal::HashMap') ) {\n        return Mal::HashMap->new(\n            { pairmap { $a => EVAL( $b, $env ) } %{$ast} } );\n    }\n    if ( $ast->isa('Mal::List') and @{$ast} ) {\n        my ( $a0, @args ) = @{$ast};\n        if ( $a0->isa('Mal::Symbol') and my $sf = $special_forms{ ${$a0} } ) {\n            @_ = ( $env, @args );\n            goto &{$sf};\n        }\n        my $f = EVAL( $a0, $env );\n        if ( $f->isa('Mal::Macro') ) {\n            @_ = ( $f->(@args), $env );\n            goto &EVAL;\n        }\n        @_ = map { EVAL( $_, $env ) } @args;\n        goto &{$f};\n    }\n    return $ast;\n}\n\nsub special_def {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, EVAL( $val, $env ) );\n}\n\nsub special_let {\n    my ( $env, $bindings, $body ) = @_;\n    my $let_env = Env->new($env);\n    foreach my $pair ( pairs @{$bindings} ) {\n        my ( $k, $v ) = @{$pair};\n        $let_env->set( ${$k}, EVAL( $v, $let_env ) );\n    }\n    @_ = ( $body, $let_env );\n    goto &EVAL;\n}\n\nsub special_quote {\n    my ( $env, $quoted ) = @_;\n    return $quoted;\n}\n\nsub special_quasiquote {\n    my ( $env, $quoted ) = @_;\n    @_ = ( quasiquote($quoted), $env );\n    goto &EVAL;\n}\n\nsub special_defmacro {\n    my ( $env, $sym, $val ) = @_;\n    return $env->set( ${$sym}, Mal::Macro->new( EVAL( $val, $env )->clone ) );\n}\n\nsub special_try {\n    my ( $env, $try, $catch ) = @_;\n    if ($catch) {\n        my ( undef, $binding, $body ) = @{$catch};\n        if ( my $ret = eval { EVAL( $try, $env ) } ) {\n            return $ret;\n        }\n        my $exc = $EVAL_ERROR;\n        if ( not blessed($exc) or not $exc->isa('Mal::Type') ) {\n            chomp $exc;\n            $exc = Mal::String->new($exc);\n        }\n        my $catch_env = Env->new( $env, [$binding], [$exc] );\n        @_ = ( $body, $catch_env );\n        goto &EVAL;\n    }\n    @_ = ( $try, $env );\n    goto &EVAL;\n}\n\nsub special_do {\n    my ( $env, @todo ) = @_;\n    my $final = pop @todo;\n    for (@todo) {\n        EVAL( $_, $env );\n    }\n    @_ = ( $final, $env );\n    goto &EVAL;\n}\n\nsub special_if {\n    my ( $env, $if, $then, $else ) = @_;\n    my $cond = EVAL( $if, $env );\n    if ( not $cond->isa('Mal::Nil') and not $cond->isa('Mal::False') ) {\n        @_ = ( $then, $env );\n        goto &EVAL;\n    }\n    if ($else) {\n        @_ = ( $else, $env );\n        goto &EVAL;\n    }\n    return nil;\n}\n\nsub special_fn {\n    my ( $env, $params, $body ) = @_;\n    return Mal::Function->new(\n        sub {\n            @_ = ( $body, Env->new( $env, $params, \\@_ ) );\n            goto &EVAL;\n        }\n    );\n}\n\n# print\nsub PRINT {\n    my $exp = shift;\n    return pr_str($exp);\n}\n\n# repl\nmy $repl_env = Env->new();\n\nsub REP {\n    my $str = shift;\n    return PRINT( EVAL( READ($str), $repl_env ) );\n}\n\n# Command line arguments\nif ( $ARGV[0] eq '--raw' ) {\n    set_rl_mode('raw');\n    shift @ARGV;\n}\nmy $script_file = shift @ARGV;\n\n# core.pl: defined using perl\nwhile ( my ( $k, $v ) = each %NS ) {\n    $repl_env->set( $k, Mal::Function->new($v) );\n}\n$repl_env->set( 'eval',\n    Mal::Function->new( sub { EVAL( $_[0], $repl_env ) } ) );\n$repl_env->set( '*ARGV*',\n    Mal::List->new( [ map { Mal::String->new($_) } @ARGV ] ) );\n\n# core.mal: defined using the language itself\nREP(q[(def! *host-language* \"perl\")]);\nREP(q[(def! not (fn* (a) (if a false true)))]);\nREP(<<'EOF');\n(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\nEOF\nREP(<<'EOF');\n(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)\n(if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\"))\n(cons 'cond (rest (rest xs)))))))\nEOF\n\nif ( defined $script_file ) {\n    REP(qq[(load-file \"$script_file\")]);\n    exit 0;\n}\nREP(q[(println (str \"Mal [\" *host-language* \"]\"))]);\nwhile ( defined( my $line = mal_readline('user> ') ) ) {\n    eval {\n        print REP($line), \"\\n\" or die $ERRNO;\n        1;\n    } or do {\n        my $err = $EVAL_ERROR;\n        if ( defined blessed($err) and $err->isa('Mal::Type') ) {\n            $err = pr_str($err) . \"\\n\";\n        }\n        print 'Error: ', $err or die $ERRNO;\n    };\n}\n"
  },
  {
    "path": "impls/perl/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/perl/tests/stepA_mal.mal",
    "content": ";; Testing types returned from pl*\n\n(pl* \"123\")\n;=>123\n\n(pl* \"\\\"abc\\\"\")\n;=>\"abc\"\n\n(pl* \"{'abc'=>123}\")\n;=>{\"abc\" 123}\n\n(pl* \"['abc', 123]\")\n;=>(\"abc\" 123)\n\n(pl* \"2+3\")\n;=>5\n\n(pl* \"undef\")\n;=>nil\n\n;; Testing eval of print statement\n\n(pl* \"print 'hello\\n';\")\n;/hello\n;=>1\n\n;; Testing exceptions passing through pl*\n\n(try* (pl* \"die \\\"pop!\\\\n\\\"\") (catch* e e))\n;=>\"pop!\"\n"
  },
  {
    "path": "impls/perl6/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Perl6 build deps\nRUN apt-get -y install rakudo\n"
  },
  {
    "path": "impls/perl6/Makefile",
    "content": "all:\n\t@true\n\nclean:\n"
  },
  {
    "path": "impls/perl6/core.pm",
    "content": "unit module core;\nuse types;\nuse printer;\nuse reader;\n\nsub equal ($a, $b) {\n  if $a ~~ MalSequence && $b ~~ MalSequence {\n    return $FALSE if $a.elems != $b.elems;\n    for |$a Z |$b -> ($a_el, $b_el) {\n      return $FALSE if equal($a_el, $b_el) ~~ $FALSE;\n    }\n    return $TRUE;\n  }\n  elsif $a ~~ MalHashMap && $b ~~ MalHashMap {\n    return $FALSE if $a.elems != $b.elems;\n    for $a.pairs {\n      return $FALSE if !$b{.key} || equal(.value, $b{.key}) ~~ $FALSE;\n    }\n    return $TRUE;\n  }\n  else {\n    return $a.^name eq $b.^name && $a.val ~~ $b.val ?? $TRUE !! $FALSE;\n  }\n}\n\nsub perl6-eval ($code) {\n  my &convert = -> $data {\n    given $data {\n      when Array|List { MalList($_.map({&convert($_)}).Array) }\n      when Hash { MalHashMap($_.map({.key => &convert(.value)}).Hash) }\n      when Bool { $_ ?? $TRUE !! $FALSE }\n      when Int { MalNumber($_) }\n      when Nil { $NIL }\n      default { $_.^name eq 'Any' ?? $NIL !! MalString($_.gist) }\n    }\n  };\n\n  use MONKEY-SEE-NO-EVAL;\n  return &convert(EVAL($code));\n}\n\nour %ns = (\n  '+'         => MalCode({ MalNumber($^a.val + $^b.val) }),\n  '-'         => MalCode({ MalNumber($^a.val - $^b.val) }),\n  '*'         => MalCode({ MalNumber($^a.val * $^b.val) }),\n  '/'         => MalCode({ MalNumber(($^a.val / $^b.val).Int) }),\n  '<'         => MalCode({ $^a.val < $^b.val ?? $TRUE !! $FALSE }),\n  '<='        => MalCode({ $^a.val <= $^b.val ?? $TRUE !! $FALSE }),\n  '>'         => MalCode({ $^a.val > $^b.val ?? $TRUE !! $FALSE }),\n  '>='        => MalCode({ $^a.val >= $^b.val ?? $TRUE !! $FALSE }),\n  '='         => MalCode({ equal($^a, $^b) }),\n  prn         => MalCode({ say @_.map({ pr_str($_, True) }).join(' '); $NIL }),\n  println     => MalCode({ say @_.map({ pr_str($_) }).join(' '); $NIL }),\n  pr-str      => MalCode({ MalString(@_.map({ pr_str($_, True) }).join(' ') ) }),\n  str         => MalCode({ MalString(@_.map({ pr_str($_) }).join) }),\n  read-string => MalCode({ read_str($^a.val) }),\n  slurp       => MalCode({ MalString($^a.val.IO.slurp) }),\n  list        => MalCode({ MalList(@_) }),\n  'list?'     => MalCode({ $^a ~~ MalList ?? $TRUE !! $FALSE }),\n  'empty?'    => MalCode({ $^a.elems ?? $FALSE !! $TRUE }),\n  count       => MalCode({ MalNumber($^a ~~ $NIL ?? 0 !! $^a.elems) }),\n  atom        => MalCode({ MalAtom($^a) }),\n  'atom?'     => MalCode({ $^a ~~ MalAtom ?? $TRUE !! $FALSE }),\n  deref       => MalCode({ $^a.val }),\n  'reset!'    => MalCode({ $^a.val = $^b }),\n  'swap!'     => MalCode(-> $atom, $func, *@args { $atom.val = $func.apply($atom.val, |@args) }),\n  cons        => MalCode({ MalList([$^a, |$^b.val]) }),\n  concat      => MalCode({ MalList([@_.map({|$_.val})]) }),\n  vec         => MalCode({ MalVector([|$^a.val]) }),\n  nth         => MalCode({ $^a[$^b.val] // die X::MalOutOfRange.new }),\n  first       => MalCode({ $^a[0] // $NIL }),\n  rest        => MalCode({ MalList([$^a[1..*]]) }),\n  throw       => MalCode({ die X::MalThrow.new(value => $^a) }),\n  apply       => MalCode(-> $func, *@args { $func.apply(|@args[0..*-2], |@args[*-1].val) }),\n  map         => MalCode(-> $func, $list { MalList([$list.map({ $func.apply($_) })]) }),\n  'nil?'      => MalCode({ $^a ~~ MalNil ?? $TRUE !! $FALSE }),\n  'true?'     => MalCode({ $^a ~~ MalTrue ?? $TRUE !! $FALSE }),\n  'false?'    => MalCode({ $^a ~~ MalFalse ?? $TRUE !! $FALSE }),\n  'symbol?'   => MalCode({ $^a ~~ MalSymbol ?? $TRUE !! $FALSE }),\n  symbol      => MalCode({ MalSymbol($^a.val) }),\n  keyword     => MalCode({ $^a.val ~~ /^\\x29E/ ?? $^a !! MalString(\"\\x29E\" ~ $^a.val) }),\n  'keyword?'  => MalCode({ $^a.val ~~ /^\\x29E/ ?? $TRUE !! $FALSE }),\n  'number?'   => MalCode({ $^a ~~ MalNumber ?? $TRUE !! $FALSE }),\n  'fn?'       => MalCode({ ($^a ~~ MalCallable && !$^a.?is_macro) ?? $TRUE !! $FALSE }),\n  'macro?'    => MalCode({ $^a.?is_macro ?? $TRUE !! $FALSE }),\n  vector      => MalCode({ MalVector(@_) }),\n  'vector?'   => MalCode({ $^a ~~ MalVector ?? $TRUE !! $FALSE }),\n  hash-map    => MalCode({ MalHashMap(@_.map({ $^a.val => $^b }).Hash) }),\n  'map?'      => MalCode({ $^a ~~ MalHashMap ?? $TRUE !! $FALSE }),\n  assoc       => MalCode(-> $map, *@kv { MalHashMap(Hash.new(|$map.kv, |@kv.map({$^a.val, $^b}))) }),\n  dissoc      => MalCode(-> $map, *@keys { my %h = $map.val.clone; %h{@keys.map(*.val)}:delete; MalHashMap(%h) }),\n  get         => MalCode({ $^a.val{$^b.val} // $NIL }),\n  'contains?' => MalCode({ $^a.val{$^b.val}:exists ?? $TRUE !! $FALSE }),\n  keys        => MalCode({ MalList([$^a.keys.map({ MalString($_) })]) }),\n  vals        => MalCode({ MalList([$^a.values]) }),\n  'sequential?' => MalCode({ $^a ~~ MalList|MalVector ?? $TRUE !! $FALSE }),\n  readline    => MalCode({ with prompt($^a.val) { MalString($_) } else { $NIL } }),\n  time-ms     => MalCode({ MalNumber((now * 1000).Int) }),\n  conj        => MalCode(-> $seq, *@args { $seq.conj(@args) }),\n  'string?'   => MalCode({ $^a ~~ MalString && $^a.val !~~ /^\\x29E/ ?? $TRUE !! $FALSE }),\n  seq         => MalCode({ $^a.seq }),\n  with-meta   => MalCode({ return $NIL if !$^a.can('meta'); my $x = $^a.clone; $x.meta = $^b; $x }),\n  meta        => MalCode({ $^a.?meta // $NIL }),\n  perl6-eval  => MalCode({ perl6-eval($^a.val) }),\n);\n"
  },
  {
    "path": "impls/perl6/env.pm",
    "content": "unit class MalEnv;\nuse types;\n\nhas $.outer;\nhas %.data;\nhas @.binds;\nhas @.exprs;\n\nmethod new ($outer?, @binds?, @exprs?) {\n  self.bless(:$outer, :@binds, :@exprs);\n}\n\nsubmethod BUILD (:@!binds, :@!exprs, :$!outer, :%!data) {\n  for @!binds.kv -> $idx, $key {\n    if $key eq '&' {\n      my $value = MalList([@!exprs[$idx..*]]);\n      self.set(@!binds[$idx+1], $value);\n      last;\n    }\n    my $value = @!exprs[$idx];\n    self.set($key, $value);\n  }\n}\n\nmethod set ($key, $value) {\n  %.data{$key} = $value;\n}\n\nmethod get ($key) {\n  return %.data{$key} if %.data{$key};\n  return $.outer.get($key) if $.outer;\n  return 0;\n}\n"
  },
  {
    "path": "impls/perl6/printer.pm",
    "content": "unit module printer;\nuse types;\n\nsub pr_str ($exp, $print_readably = False) is export {\n  given $exp {\n    when MalFunction { \"#<fn* ({$exp.params}) {pr_str($exp.ast)}>\" }\n    when MalCode { \"#<builtin_fn* {$exp.fn.gist}>\" }\n    when MalList {\n      '(' ~ join(' ', |$exp.map({ pr_str($_, $print_readably) })) ~ ')';\n    }\n    when MalVector {\n      '[' ~ join(' ', |$exp.map({ pr_str($_, $print_readably) })) ~ ']';\n    }\n    when MalHashMap {\n      '{' ~ $exp.kv.flatmap({ MalString($^a), $^b }).map({ pr_str($_, $print_readably) }) ~ '}'\n    }\n    when MalString {\n      my $str = $exp.val;\n      if $str ~~ s/^\\x29E/:/ || !$print_readably {\n        $str;\n      }\n      else {\n        '\"' ~ $str.trans(/\\\\/ => '\\\\\\\\', /\\\"/ => '\\\\\"', /\\n/ => '\\\\n') ~ '\"';\n      }\n    }\n    when MalAtom { \"(atom {pr_str($exp.val, $print_readably)})\" }\n    when MalValue { $exp.val }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/reader.pm",
    "content": "unit module reader;\nuse types;\n\nclass Reader {\n  has @.tokens;\n  has $!position = 0;\n  method peek { @.tokens[$!position] }\n  method next { @.tokens[$!position++] }\n}\n\nsub read_form ($rdr) {\n  given $rdr.peek {\n    when \"'\"  { $rdr.next; MalList([MalSymbol('quote'), read_form($rdr)]) }\n    when '`'  { $rdr.next; MalList([MalSymbol('quasiquote'), read_form($rdr)]) }\n    when '~'  { $rdr.next; MalList([MalSymbol('unquote'), read_form($rdr)]) }\n    when '~@' { $rdr.next; MalList([MalSymbol('splice-unquote'), read_form($rdr)]) }\n    when '@'  { $rdr.next; MalList([MalSymbol('deref'), read_form($rdr)]) }\n    when '^'  {\n      $rdr.next;\n      my $meta = read_form($rdr);\n      MalList([MalSymbol('with-meta'), read_form($rdr), $meta]);\n    }\n    when ')'|']'|'}' { die X::MalUnexpected.new(token => $_) }\n    when '(' { MalList(read_list($rdr, ')')) }\n    when '[' { MalVector(read_list($rdr, ']')) }\n    when '{' { MalHashMap(read_list($rdr, '}').map({ $^a.val => $^b }).Hash) }\n    default  { read_atom($rdr) }\n  }\n}\n\nsub read_list ($rdr, $end) {\n  my @list;\n  my $token = $rdr.next;\n\n  loop {\n    $token = $rdr.peek;\n    die X::MalIncomplete.new(end => $end) if !$token.defined;\n    last if $token eq $end;\n    @list.push(read_form($rdr));\n  }\n  $rdr.next;\n\n  return @list;\n}\n\nsub read_atom ($rdr) {\n  my $atom = $rdr.next;\n  given $atom {\n    when /^'\"' [ \\\\. || <-[\\\"\\\\]> ]* '\"'$/ {\n      s:g/^\\\"|\\\"$//;\n      MalString(.trans(/\\\\\\\"/ => '\"', /\\\\n/ => \"\\n\", /\\\\\\\\/ => '\\\\'));\n    }\n    when /^\\\"/ {\n      die X::MalIncomplete.new(end => '\"');\n    }\n    when /^\\:(.*)/ { MalString(\"\\x29E$0\") }\n    when /^'-'? <[0..9]>+$/ { MalNumber($_) }\n    when 'nil' { $NIL }\n    when 'true' { $TRUE }\n    when 'false' { $FALSE }\n    default { MalSymbol($_) }\n  }\n}\n\nmy regex mal {\n  [\n    <[\\s,]>*                          # whitespace/commas\n    $<token>=(\n    || '~@'                           # ~@\n    || <[\\[\\]{}()'`~^@]>              # special single-char tokens\n    || '\"' [ \\\\. || <-[\\\"\\\\]> ]* '\"'? # double-quoted strings\n    || ';'<-[\\n]>*                    # comments\n    || <-[\\s\\[\\]{}('\"`,;)]>+          # symbols\n    )\n  ]+\n}\n\nsub tokenizer ($str) {\n  return [] if !$str.match(/^<mal>/);\n  return grep { ! /^\\;/ }, $<mal><token>.map({~$_});\n}\n\nsub read_str ($str) is export {\n  my @tokens = tokenizer($str);\n  die X::MalNoTokens.new if !@tokens;\n  return read_form(Reader.new(tokens => @tokens));\n}\n"
  },
  {
    "path": "impls/perl6/run",
    "content": "#!/usr/bin/env bash\nexec perl6 $(dirname $0)/${STEP:-stepA_mal}.pl \"${@}\"\n"
  },
  {
    "path": "impls/perl6/step0_repl.pl",
    "content": "use v6;\n#use Linenoise;\n\nsub read ($str) {\n  return $str;\n}\n\nsub eval ($ast) {\n  return $ast;\n}\n\nsub print ($exp) {\n  return $exp;\n}\n\nsub rep ($str) {\n  return print(eval(read($str)));\n}\n\nsub MAIN {\n  #while (my $line = linenoise('user> ')).defined {\n  #  say rep($line);\n  #}\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step1_read_print.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub eval ($ast) {\n  return $ast;\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nsub rep ($str) {\n  return print(eval(read($str)));\n}\n\nsub MAIN {\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step2_eval.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub eval ($ast, $env) {\n\n  # say \"EVAL: \" ~ print($ast);\n\n  given $ast {\n    when MalSymbol  { return $env{$ast.val} || die X::MalNotFound.new(name => $ast.val) }\n    when MalList    { }\n    when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n    when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n    default         { return $ast // $NIL }\n  }\n\n  return $ast if !$ast.elems;\n\n  my ($func, @args) = $ast.map({ eval($_, $env) });\n  my $arglist = MalList(@args);\n  return $func.apply($arglist);\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN {\n  $repl_env<+> = MalCode({ MalNumber($^a[0].val + $^a[1].val) });\n  $repl_env<-> = MalCode({ MalNumber($^a[0].val - $^a[1].val) });\n  $repl_env<*> = MalCode({ MalNumber($^a[0].val * $^a[1].val) });\n  $repl_env</> = MalCode({ MalNumber(($^a[0].val / $^a[1].val).Int) });\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step3_env.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub eval ($ast, $env) {\n\n  say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n  given $ast {\n    when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n    when MalList    { }\n    when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n    when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n    default         { return $ast // $NIL }\n  }\n\n  return $ast if !$ast.elems;\n\n  my ($a0, $a1, $a2, $a3) = $ast.val;\n  given $a0.val {\n    when 'def!' {\n      return $env.set($a1.val, eval($a2, $env));\n    }\n    when 'let*' {\n      my $new_env = MalEnv.new($env);\n      for |$a1.val -> $key, $value {\n        $new_env.set($key.val, eval($value, $new_env));\n      }\n      return eval($a2, $new_env);\n    }\n    default {\n      my ($func, @args) = $ast.map({ eval($_, $env) });\n      return $func.apply(@args);\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN {\n  $repl_env.set('+', MalCode({ MalNumber($^a.val + $^b.val) }));\n  $repl_env.set('-', MalCode({ MalNumber($^a.val - $^b.val) }));\n  $repl_env.set('*', MalCode({ MalNumber($^a.val * $^b.val) }));\n  $repl_env.set('/', MalCode({ MalNumber(($^a.val / $^b.val).Int) }));\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step4_if_fn_do.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub eval ($ast, $env) {\n\n  say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n  given $ast {\n    when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n    when MalList    { }\n    when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n    when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n    default         { return $ast // $NIL }\n  }\n\n  return $ast if !$ast.elems;\n\n  my ($a0, $a1, $a2, $a3) = $ast.val;\n  given $a0.val {\n    when 'def!' {\n      return $env.set($a1.val, eval($a2, $env));\n    }\n    when 'let*' {\n      my $new_env = MalEnv.new($env);\n      for |$a1.val -> $key, $value {\n        $new_env.set($key.val, eval($value, $new_env));\n      }\n      return eval($a2, $new_env);\n    }\n    when 'do' {\n      $ast[1..*-2].map({ eval($_, $env) });\n      return eval($ast[*-1], $env);\n    }\n    when 'if' {\n      return eval($a1, $env) !~~ MalNil|MalFalse\n        ?? return eval($a2, $env)\n        !! return $a3 ?? eval($a3, $env) !! $NIL;\n    }\n    when 'fn*' {\n      return MalCode(-> *@args {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        eval($a2, MalEnv.new($env, @binds, @args));\n      });\n    }\n    default {\n      my ($func, @args) = $ast.map({ eval($_, $env) });\n      return $func.apply(|@args);\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN {\n  $repl_env.set(.key, .value) for %core::ns;\n  rep(q{(def! not (fn* (a) (if a false true)))});\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step5_tco.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub eval ($ast is copy, $env is copy) {\n  loop {\n\n    say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n    given $ast {\n      when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n      when MalList    { }\n      when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n      when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n      default         { return $ast // $NIL }\n    }\n\n    return $ast if !$ast.elems;\n\n    my ($a0, $a1, $a2, $a3) = $ast.val;\n    given $a0.val {\n      when 'def!' {\n        return $env.set($a1.val, eval($a2, $env));\n      }\n      when 'let*' {\n        my $new_env = MalEnv.new($env);\n        for |$a1.val -> $key, $value {\n          $new_env.set($key.val, eval($value, $new_env));\n        }\n        $env = $new_env;\n        $ast = $a2;\n      }\n      when 'do' {\n        $ast[1..*-2].map({ eval($_, $env) });\n        $ast = $ast[*-1];\n      }\n      when 'if' {\n        if eval($a1, $env) ~~ MalNil|MalFalse {\n          return $NIL if $a3 ~~ $NIL;\n          $ast = $a3;\n        }\n        else {\n          $ast = $a2;\n        }\n      }\n      when 'fn*' {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        my &fn = -> *@args {\n          eval($a2, MalEnv.new($env, @binds, @args));\n        };\n        return MalFunction($a2, $env, @binds, &fn);\n      }\n      default {\n        my ($func, @args) = $ast.map({ eval($_, $env) });\n        return $func.apply(|@args) if $func !~~ MalFunction;\n        $ast = $func.ast;\n        $env = MalEnv.new($func.env, $func.params, @args);\n      }\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN {\n  $repl_env.set(.key, .value) for %core::ns;\n  rep(q{(def! not (fn* (a) (if a false true)))});\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step6_file.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub eval ($ast is copy, $env is copy) {\n  loop {\n\n    say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n    given $ast {\n      when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n      when MalList    { }\n      when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n      when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n      default         { return $ast // $NIL }\n    }\n\n    return $ast if !$ast.elems;\n\n    my ($a0, $a1, $a2, $a3) = $ast.val;\n    given $a0.val {\n      when 'def!' {\n        return $env.set($a1.val, eval($a2, $env));\n      }\n      when 'let*' {\n        my $new_env = MalEnv.new($env);\n        for |$a1.val -> $key, $value {\n          $new_env.set($key.val, eval($value, $new_env));\n        }\n        $env = $new_env;\n        $ast = $a2;\n      }\n      when 'do' {\n        $ast[1..*-2].map({ eval($_, $env) });\n        $ast = $ast[*-1];\n      }\n      when 'if' {\n        if eval($a1, $env) ~~ MalNil|MalFalse {\n          return $NIL if $a3 ~~ $NIL;\n          $ast = $a3;\n        }\n        else {\n          $ast = $a2;\n        }\n      }\n      when 'fn*' {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        my &fn = -> *@args {\n          eval($a2, MalEnv.new($env, @binds, @args));\n        };\n        return MalFunction($a2, $env, @binds, &fn);\n      }\n      default {\n        my ($func, @args) = $ast.map({ eval($_, $env) });\n        return $func.apply(|@args) if $func !~~ MalFunction;\n        $ast = $func.ast;\n        $env = MalEnv.new($func.env, $func.params, @args);\n      }\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN ($source_file?, *@args) {\n  $repl_env.set(.key, .value) for %core::ns;\n  $repl_env.set('eval', MalCode({ eval($^a, $repl_env) }));\n  $repl_env.set('*ARGV*', MalList([@args.map({ MalString($_) })]));\n  rep(q{(def! not (fn* (a) (if a false true)))});\n  rep(q{(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))});\n\n  if ($source_file.defined) {\n    rep(\"(load-file \\\"$source_file\\\")\");\n    exit;\n  }\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step7_quote.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub qqLoop ($ast) {\n  my $acc = MalList([]);\n  for |$ast.val.reverse -> $elt {\n    if $elt ~~ MalList && $elt.elems == 2 && $elt[0] ~~ MalSymbol\n      && $elt[0].val eq 'splice-unquote'\n    {\n      $acc = MalList([MalSymbol('concat'), $elt[1], $acc]);\n    }\n    else {\n      $acc = MalList([MalSymbol('cons'), quasiquote($elt), $acc]);\n    }\n  }\n  return $acc;\n}\n\nsub quasiquote ($ast) {\n  given $ast {\n    when MalList {\n      if $ast.elems == 2 && $ast[0] ~~ MalSymbol && $ast[0].val eq 'unquote' {\n        $ast[1]\n      } else {\n        qqLoop($ast);\n      }\n    }\n    when MalVector            { MalList([MalSymbol('vec'), qqLoop($ast)]) }\n    when MalSymbol|MalHashMap { MalList([MalSymbol('quote'), $ast]) }\n    default                   { $ast }\n  }\n}\n\nsub eval ($ast is copy, $env is copy) {\n  loop {\n\n    say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n    given $ast {\n      when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n      when MalList    { }\n      when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n      when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n      default         { return $ast // $NIL }\n    }\n\n    return $ast if !$ast.elems;\n\n    my ($a0, $a1, $a2, $a3) = $ast.val;\n    given $a0.val {\n      when 'def!' {\n        return $env.set($a1.val, eval($a2, $env));\n      }\n      when 'let*' {\n        my $new_env = MalEnv.new($env);\n        for |$a1.val -> $key, $value {\n          $new_env.set($key.val, eval($value, $new_env));\n        }\n        $env = $new_env;\n        $ast = $a2;\n      }\n      when 'do' {\n        $ast[1..*-2].map({ eval($_, $env) });\n        $ast = $ast[*-1];\n      }\n      when 'if' {\n        if eval($a1, $env) ~~ MalNil|MalFalse {\n          return $NIL if $a3 ~~ $NIL;\n          $ast = $a3;\n        }\n        else {\n          $ast = $a2;\n        }\n      }\n      when 'fn*' {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        my &fn = -> *@args {\n          eval($a2, MalEnv.new($env, @binds, @args));\n        };\n        return MalFunction($a2, $env, @binds, &fn);\n      }\n      when 'quote' { return $a1 }\n      when 'quasiquote' { $ast = quasiquote($a1) }\n      default {\n        my ($func, @args) = $ast.map({ eval($_, $env) });\n        return $func.apply(|@args) if $func !~~ MalFunction;\n        $ast = $func.ast;\n        $env = MalEnv.new($func.env, $func.params, @args);\n      }\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN ($source_file?, *@args) {\n  $repl_env.set(.key, .value) for %core::ns;\n  $repl_env.set('eval', MalCode({ eval($^a, $repl_env) }));\n  $repl_env.set('*ARGV*', MalList([@args.map({ MalString($_) })]));\n  rep(q{(def! not (fn* (a) (if a false true)))});\n  rep(q{(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))});\n\n  if ($source_file.defined) {\n    rep(\"(load-file \\\"$source_file\\\")\");\n    exit;\n  }\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step8_macros.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub qqLoop ($ast) {\n  my $acc = MalList([]);\n  for |$ast.val.reverse -> $elt {\n    if $elt ~~ MalList && $elt.elems == 2 && $elt[0] ~~ MalSymbol\n      && $elt[0].val eq 'splice-unquote'\n    {\n      $acc = MalList([MalSymbol('concat'), $elt[1], $acc]);\n    }\n    else {\n      $acc = MalList([MalSymbol('cons'), quasiquote($elt), $acc]);\n    }\n  }\n  return $acc;\n}\n\nsub quasiquote ($ast) {\n  given $ast {\n    when MalList {\n      if $ast.elems == 2 && $ast[0] ~~ MalSymbol && $ast[0].val eq 'unquote' {\n        $ast[1]\n      } else {\n        qqLoop($ast);\n      }\n    }\n    when MalVector            { MalList([MalSymbol('vec'), qqLoop($ast)]) }\n    when MalSymbol|MalHashMap { MalList([MalSymbol('quote'), $ast]) }\n    default                   { $ast }\n  }\n}\n\nsub eval ($ast is copy, $env is copy) {\n  loop {\n\n    say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n    given $ast {\n      when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n      when MalList    { }\n      when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n      when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n      default         { return $ast // $NIL }\n    }\n\n    return $ast if !$ast.elems;\n\n    my ($a0, $a1, $a2, $a3) = $ast.val;\n    given $a0.val {\n      when 'def!' {\n        return $env.set($a1.val, eval($a2, $env));\n      }\n      when 'let*' {\n        my $new_env = MalEnv.new($env);\n        for |$a1.val -> $key, $value {\n          $new_env.set($key.val, eval($value, $new_env));\n        }\n        $env = $new_env;\n        $ast = $a2;\n      }\n      when 'do' {\n        $ast[1..*-2].map({ eval($_, $env) });\n        $ast = $ast[*-1];\n      }\n      when 'if' {\n        if eval($a1, $env) ~~ MalNil|MalFalse {\n          return $NIL if $a3 ~~ $NIL;\n          $ast = $a3;\n        }\n        else {\n          $ast = $a2;\n        }\n      }\n      when 'fn*' {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        my &fn = -> *@args {\n          eval($a2, MalEnv.new($env, @binds, @args));\n        };\n        return MalFunction($a2, $env, @binds, &fn);\n      }\n      when 'quote' { return $a1 }\n      when 'quasiquote' { $ast = quasiquote($a1) }\n      when 'defmacro!' {\n        my $func = eval($a2, $env);\n        $func = MalFunction($func.ast, $func.env, $func.params, $func.fn);\n        $func.is_macro = True;\n        return $env.set($a1.val, $func);\n      }\n      default {\n        my $func = eval($a0, $env);\n        my @args = $ast[1..*];\n        if $func.?is_macro {\n            $ast = $func.apply(@args);\n            next;\n        }\n        @args = @args.map({ eval($_, $env) });\n        return $func.apply(|@args) if $func !~~ MalFunction;\n        $ast = $func.ast;\n        $env = MalEnv.new($func.env, $func.params, @args);\n      }\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN ($source_file?, *@args) {\n  $repl_env.set(.key, .value) for %core::ns;\n  $repl_env.set('eval', MalCode({ eval($^a, $repl_env) }));\n  $repl_env.set('*ARGV*', MalList([@args.map({ MalString($_) })]));\n  rep(q{(def! not (fn* (a) (if a false true)))});\n  rep(q{(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))});\n  rep(q{(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))});\n\n  if ($source_file.defined) {\n    rep(\"(load-file \\\"$source_file\\\")\");\n    exit;\n  }\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalException { .Str.say }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/step9_try.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub qqLoop ($ast) {\n  my $acc = MalList([]);\n  for |$ast.val.reverse -> $elt {\n    if $elt ~~ MalList && $elt.elems == 2 && $elt[0] ~~ MalSymbol\n      && $elt[0].val eq 'splice-unquote'\n    {\n      $acc = MalList([MalSymbol('concat'), $elt[1], $acc]);\n    }\n    else {\n      $acc = MalList([MalSymbol('cons'), quasiquote($elt), $acc]);\n    }\n  }\n  return $acc;\n}\n\nsub quasiquote ($ast) {\n  given $ast {\n    when MalList {\n      if $ast.elems == 2 && $ast[0] ~~ MalSymbol && $ast[0].val eq 'unquote' {\n        $ast[1]\n      } else {\n        qqLoop($ast);\n      }\n    }\n    when MalVector            { MalList([MalSymbol('vec'), qqLoop($ast)]) }\n    when MalSymbol|MalHashMap { MalList([MalSymbol('quote'), $ast]) }\n    default                   { $ast }\n  }\n}\n\nsub eval ($ast is copy, $env is copy) {\n  loop {\n\n    say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n    given $ast {\n      when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n      when MalList    { }\n      when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n      when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n      default         { return $ast // $NIL }\n    }\n\n    return $ast if !$ast.elems;\n\n    my ($a0, $a1, $a2, $a3) = $ast.val;\n    given $a0.val {\n      when 'def!' {\n        return $env.set($a1.val, eval($a2, $env));\n      }\n      when 'let*' {\n        my $new_env = MalEnv.new($env);\n        for |$a1.val -> $key, $value {\n          $new_env.set($key.val, eval($value, $new_env));\n        }\n        $env = $new_env;\n        $ast = $a2;\n      }\n      when 'do' {\n        $ast[1..*-2].map({ eval($_, $env) });\n        $ast = $ast[*-1];\n      }\n      when 'if' {\n        if eval($a1, $env) ~~ MalNil|MalFalse {\n          return $NIL if $a3 ~~ $NIL;\n          $ast = $a3;\n        }\n        else {\n          $ast = $a2;\n        }\n      }\n      when 'fn*' {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        my &fn = -> *@args {\n          eval($a2, MalEnv.new($env, @binds, @args));\n        };\n        return MalFunction($a2, $env, @binds, &fn);\n      }\n      when 'quote' { return $a1 }\n      when 'quasiquote' { $ast = quasiquote($a1) }\n      when 'defmacro!' {\n        my $func = eval($a2, $env);\n        $func = MalFunction($func.ast, $func.env, $func.params, $func.fn);\n        $func.is_macro = True;\n        return $env.set($a1.val, $func);\n      }\n      when 'try*' {\n        return eval($a1, $env);\n        CATCH {\n          .rethrow if !$a2;\n          my $ex = $_ ~~ X::MalThrow ?? .value !! MalString(.Str);\n          my $new_env = $env;\n          $env.set($a2[1].val, $ex);\n          return eval($a2[2], $new_env);\n        }\n      }\n      default {\n        my $func = eval($a0, $env);\n        my @args = $ast[1..*];\n        if $func.?is_macro {\n            $ast = $func.apply(@args);\n            next;\n        }\n        @args = @args.map({ eval($_, $env) });\n        return $func.apply(|@args) if $func !~~ MalFunction;\n        $ast = $func.ast;\n        $env = MalEnv.new($func.env, $func.params, @args);\n      }\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN ($source_file?, *@args) {\n  $repl_env.set(.key, .value) for %core::ns;\n  $repl_env.set('eval', MalCode({ eval($^a, $repl_env) }));\n  $repl_env.set('*ARGV*', MalList([@args.map({ MalString($_) })]));\n  rep(q{(def! not (fn* (a) (if a false true)))});\n  rep(q{(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))});\n  rep(q{(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))});\n\n  if ($source_file.defined) {\n    rep(\"(load-file \\\"$source_file\\\")\");\n    exit;\n  }\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalThrow { say \"Error: \" ~ pr_str(.value, True) }\n      when X::MalException { say \"Error: \" ~ .Str }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/stepA_mal.pl",
    "content": "use v6;\nuse lib IO::Path.new($?FILE).dirname;\nuse reader;\nuse printer;\nuse types;\nuse env;\nuse core;\n\nsub read ($str) {\n  return read_str($str);\n}\n\nsub qqLoop ($ast) {\n  my $acc = MalList([]);\n  for |$ast.val.reverse -> $elt {\n    if $elt ~~ MalList && $elt.elems == 2 && $elt[0] ~~ MalSymbol\n      && $elt[0].val eq 'splice-unquote'\n    {\n      $acc = MalList([MalSymbol('concat'), $elt[1], $acc]);\n    }\n    else {\n      $acc = MalList([MalSymbol('cons'), quasiquote($elt), $acc]);\n    }\n  }\n  return $acc;\n}\n\nsub quasiquote ($ast) {\n  given $ast {\n    when MalList {\n      if $ast.elems == 2 && $ast[0] ~~ MalSymbol && $ast[0].val eq 'unquote' {\n        $ast[1]\n      } else {\n        qqLoop($ast);\n      }\n    }\n    when MalVector            { MalList([MalSymbol('vec'), qqLoop($ast)]) }\n    when MalSymbol|MalHashMap { MalList([MalSymbol('quote'), $ast]) }\n    default                   { $ast }\n  }\n}\n\nsub eval ($ast is copy, $env is copy) {\n  loop {\n\n    say \"EVAL: \" ~ print($ast) unless $env.get('DEBUG-EVAL') ~~ 0|MalNil|MalFalse;\n\n    given $ast {\n      when MalSymbol  { return $env.get($ast.val) || die X::MalNotFound.new(name => $ast.val) }\n      when MalList    { }\n      when MalVector  { return MalVector([$ast.map({ eval($_, $env) })]) }\n      when MalHashMap { return MalHashMap($ast.kv.map({ $^a => eval($^b, $env) }).Hash) }\n      default         { return $ast // $NIL }\n    }\n\n    return $ast if !$ast.elems;\n\n    my ($a0, $a1, $a2, $a3) = $ast.val;\n    given $a0.val {\n      when 'def!' {\n        return $env.set($a1.val, eval($a2, $env));\n      }\n      when 'let*' {\n        my $new_env = MalEnv.new($env);\n        for |$a1.val -> $key, $value {\n          $new_env.set($key.val, eval($value, $new_env));\n        }\n        $env = $new_env;\n        $ast = $a2;\n      }\n      when 'do' {\n        $ast[1..*-2].map({ eval($_, $env) });\n        $ast = $ast[*-1];\n      }\n      when 'if' {\n        if eval($a1, $env) ~~ MalNil|MalFalse {\n          return $NIL if $a3 ~~ $NIL;\n          $ast = $a3;\n        }\n        else {\n          $ast = $a2;\n        }\n      }\n      when 'fn*' {\n        my @binds = $a1 ?? $a1.map(*.val) !! ();\n        my &fn = -> *@args {\n          eval($a2, MalEnv.new($env, @binds, @args));\n        };\n        return MalFunction($a2, $env, @binds, &fn);\n      }\n      when 'quote' { return $a1 }\n      when 'quasiquote' { $ast = quasiquote($a1) }\n      when 'defmacro!' {\n        my $func = eval($a2, $env);\n        $func = MalFunction($func.ast, $func.env, $func.params, $func.fn);\n        $func.is_macro = True;\n        return $env.set($a1.val, $func);\n      }\n      when 'try*' {\n        return eval($a1, $env);\n        CATCH {\n          .rethrow if !$a2;\n          my $ex = $_ ~~ X::MalThrow ?? .value !! MalString(.Str);\n          my $new_env = $env;\n          $env.set($a2[1].val, $ex);\n          return eval($a2[2], $new_env);\n        }\n      }\n      default {\n        my $func = eval($a0, $env);\n        my @args = $ast[1..*];\n        if $func.?is_macro {\n            $ast = $func.apply(@args);\n            next;\n        }\n        @args = @args.map({ eval($_, $env) });\n        return $func.apply(|@args) if $func !~~ MalFunction;\n        $ast = $func.ast;\n        $env = MalEnv.new($func.env, $func.params, @args);\n      }\n    }\n  }\n}\n\nsub print ($exp) {\n  return pr_str($exp, True);\n}\n\nmy $repl_env = MalEnv.new;\n\nsub rep ($str) {\n  return print(eval(read($str), $repl_env));\n}\n\nsub MAIN ($source_file?, *@args) {\n  $repl_env.set(.key, .value) for %core::ns;\n  $repl_env.set('eval', MalCode({ eval($^a, $repl_env) }));\n  $repl_env.set('*ARGV*', MalList([@args.map({ MalString($_) })]));\n  $repl_env.set('*host-language*', MalString('perl6'));\n  rep(q{(def! not (fn* (a) (if a false true)))});\n  rep(q{(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))});\n  rep(q{(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))});\n\n  if ($source_file.defined) {\n    rep(\"(load-file \\\"$source_file\\\")\");\n    exit;\n  }\n  rep(q{(println (str \"Mal [\" *host-language* \"]\"))});\n\n  while (my $line = prompt 'user> ').defined {\n    say rep($line);\n    CATCH {\n      when X::MalThrow { say \"Error: \" ~ pr_str(.value, True) }\n      when X::MalException { say \"Error: \" ~ .Str }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/perl6/tests/stepA_mal.mal",
    "content": ";; Testing basic Perl 6 interop\n\n(perl6-eval \"7\")\n;=>7\n\n(perl6-eval \"'7'\")\n;=>\"7\"\n\n(perl6-eval \"123 == 123\")\n;=>true\n\n(perl6-eval \"123 == 456\")\n;=>false\n\n(perl6-eval \"(7,8,9)\")\n;=>(7 8 9)\n\n(perl6-eval \"[7,8,9]\")\n;=>(7 8 9)\n\n(perl6-eval \"{abc => 789}\")\n;=>{\"abc\" 789}\n\n(perl6-eval \"Nil\")\n;=>nil\n\n(perl6-eval \"True\")\n;=>true\n\n(perl6-eval \"False\")\n;=>false\n\n(perl6-eval \"my $foo\")\n;=>nil\n\n(perl6-eval \"say 'hello' \")\n;/hello\n;=>true\n\n(perl6-eval \"sub { my $foo = 8 }()\")\n;=>8\n\n(perl6-eval \"'This sentence has five words'.subst(/\\w+/, :g, {'*' ~ $^a.chars ~ '*'})\")\n;=>\"*4* *8* *3* *4* *5*\"\n\n(perl6-eval \"<3 a 45 b>.join: '|'\")\n;=>\"3|a|45|b\"\n\n"
  },
  {
    "path": "impls/perl6/types.pm",
    "content": "unit module types;\n\nclass X::MalException is Exception is export {}\nclass X::MalNoTokens is X::MalException is export {\n  method message() { \"got no tokens\" }\n}\nclass X::MalIncomplete is X::MalException is export {\n  has $.end;\n  method message() { \"expected '$.end', got EOF\" }\n}\nclass X::MalUnexpected is X::MalException is export {\n  has $.token;\n  method message() { \"unexpected '$.token'\" }\n}\nclass X::MalNotFound is X::MalException is export {\n  has $.name;\n  method message() { \"'$.name' not found\" }\n}\nclass X::MalOutOfRange is X::MalException is export {\n  method message() { \"nth: index out of range\" }\n}\nclass X::MalThrow is X::MalException is export {\n  has $.value;\n}\n\nrole MalValue is export {\n  has $.val is rw;\n  method CALL-ME ($val) { self.new(:$val) }\n}\nrole MalSequence is export {\n  has $.val handles <cache AT-POS EXISTS-POS elems end iterator>;\n  has $.meta is rw;\n  method CALL-ME ($val) { self.new(:$val) }\n}\nrole MalCallable is export {\n  has &.fn;\n  method apply (*@_) { &!fn(|@_) }\n}\nrole MalMeta is export {\n  has $.meta is rw;\n}\n\nclass MalNil does MalValue is export {\n  method seq { self }\n}\nclass MalTrue does MalValue is export {}\nclass MalFalse does MalValue is export {}\n\nour $NIL is export = MalNil('nil');\nour $TRUE is export = MalTrue('true');\nour $FALSE is export = MalFalse('false');\n\nclass MalSymbol does MalValue does MalMeta is export {}\n\nclass MalList does MalSequence is export {\n  method conj (@args) { return self.new(val => [|@args.reverse, |$.val]) }\n  method seq { return self.elems ?? self !! $NIL }\n}\n\nclass MalVector does MalSequence is export {\n  method conj (@args) { return self.new(val => [|$.val, |@args]) }\n  method seq { return self.elems ?? MalList(self.val) !! $NIL }\n}\n\nclass MalHashMap does MalMeta is export {\n  has $.val handles <cache AT-KEY EXISTS-KEY elems pairs keys values kv>;\n  method CALL-ME ($val) { self.new(:$val) }\n}\n\nclass MalNumber does MalValue is export {}\n\nclass MalString does MalValue is export {\n  method seq {\n    return self.val.chars\n      ?? MalList(self.val.comb.map({MalString($_)}))\n      !! $NIL;\n  }\n}\n\nclass MalCode does MalCallable does MalMeta is export {\n  method CALL-ME (&fn) { self.new(:&fn) }\n}\n\nclass MalFunction does MalCallable does MalMeta is export {\n  has $.ast;\n  has @.params;\n  has $.env;\n  has $.is_macro is rw = False;\n  method CALL-ME ($ast, $env, @params, &fn) {\n    self.bless(:$ast, :$env, :@params, :&fn);\n  }\n}\n\nclass MalAtom does MalValue does MalMeta is export {}\n"
  },
  {
    "path": "impls/php/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install php-cli\n"
  },
  {
    "path": "impls/php/Makefile",
    "content": "SOURCES_BASE = readline.php types.php reader.php printer.php interop.php\nSOURCES_LISP = env.php core.php stepA_mal.php\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ndist: mal.php mal\n\nmal.php: $(SOURCES)\n\tcat $+ | grep -v \"^require_once\" > $@\n\nmal: mal.php\n\techo \"#!/usr/bin/env php\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nmal-web.php: mal.php\n\tcat $< | ( IFS=\"NON-MATCHING-IFS\"; while read -r line; do if [ \"$$line\" = \"// run mal file\" ]; then echo \"?>\"; cat webrunner.php; echo \"<?php\"; fi; printf '%s\\n' \"$$line\"; done < \"$<\" ) > $@\n\nclean:\n\trm -f mal.php mal mal-web.php\n"
  },
  {
    "path": "impls/php/README.md",
    "content": "### Running .mal scripts on PHP hosting ###\n\nCreate a symlink to `mal-web.php` with the same name as your `.mal` script and your script will be executed as if it was PHP.\n\nHere's an example using local dev.\n\nFirst build `mal-web.php`:\n\n\tcd mal/php\n\tmake mal-web.php\n\nNow you can create a web runnable mal script:\n\n\techo '(println \"Hello world!\")' > myscript.mal\n\tln -s mal-web.php myscript.php\n\nStart a development server with `php -S 0.0.0.0:8000` and then browse to http://localhost:8000/myscript.php and you should see \"Hello world!\" in your browser as `myscript.mal` is run.\n\nYou can do the same thing on live PHP web hosting by copying `mal.php` up and creating a symlink for each `.mal` file you want to be web-executable.\n\n### PHP interop ###\n\nIn [stepA_mal.mal](./tests/stepA_mal.mal) you can find some examples of PHP interop.\n\nEval PHP code:\n\n\t(php* \"return 7;\")\n\t7\n\t\n\t(php* \"return array(7,8,9);\")\n\t(7 8 9)\n\nNative function call:\n\n\t(php/date \"Y-m-d\" 0)\n\t\"1970-01-01\"\n\nAccessing PHP \"superglobal\" variables:\n\n\t(get php/_SERVER \"PHP_SELF\")\n\t\"./mal\"\n\n"
  },
  {
    "path": "impls/php/core.php",
    "content": "<?php\n\nrequire_once 'types.php';\nrequire_once 'readline.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\n\n// Error/Exception functions\nfunction mal_throw($obj) { throw new _Error($obj); }\n\n\n// String functions\nfunction pr_str() {\n    $ps = array_map(function ($obj) { return _pr_str($obj, True); },\n                    func_get_args());\n    return implode(\" \", $ps);\n}\n\nfunction str() {\n    $ps = array_map(function ($obj) { return _pr_str($obj, False); },\n                    func_get_args());\n    return implode(\"\", $ps);\n}\n\nfunction prn() {\n    $ps = array_map(function ($obj) { return _pr_str($obj, True); },\n                    func_get_args());\n    print implode(\" \", $ps) . \"\\n\";\n    return null;\n}\n\nfunction println() {\n    $ps = array_map(function ($obj) { return _pr_str($obj, False); },\n                    func_get_args());\n    print implode(\" \", $ps) . \"\\n\";\n    return null;\n}\n\n\n// Number functions\nfunction time_ms() {\n    return intval(microtime(1) * 1000);\n}\n\n\n// Hash Map functions\nfunction assoc($src_hm) {\n    $args = func_get_args();\n    $hm = clone $src_hm;\n    $args[0] = $hm;\n    return call_user_func_array('_assoc_BANG', $args);\n}\n\nfunction dissoc($src_hm) {\n    $args = func_get_args();\n    $hm = clone $src_hm;\n    $args[0] = $hm;\n    return call_user_func_array('_dissoc_BANG', $args);\n}\n\nfunction get($hm, $k) {\n    if ($hm && $hm->offsetExists($k)) {\n        return $hm[$k];\n    } else {\n        return NULL;\n    }\n}\n\nfunction contains_Q($hm, $k) { return array_key_exists($k, $hm); }\n\nfunction keys($hm) {\n    return call_user_func_array('_list',\n        array_map('strval', array_keys($hm->getArrayCopy())));\n}\nfunction vals($hm) {\n    return call_user_func_array('_list', array_values($hm->getArrayCopy()));\n}\n\n\n// Sequence functions\nfunction cons($a, $b) {\n    $tmp = $b->getArrayCopy();\n    array_unshift($tmp, $a);\n    $l = new ListClass();\n    $l->exchangeArray($tmp);\n    return $l;\n}\n\nfunction concat() {\n    $args = func_get_args();\n    $tmp = array();\n    foreach ($args as $arg) {\n        $tmp = array_merge($tmp, $arg->getArrayCopy());\n    }\n    $l = new ListClass();\n    $l->exchangeArray($tmp);\n    return $l;\n}\n\nfunction vec($a) {\n    if (_vector_Q($a)) {\n        return $a;\n    } else {\n        $v = new VectorClass();\n        $v->exchangeArray($a->getArrayCopy());\n        return $v;\n    }\n}\n\nfunction nth($seq, $idx) {\n    if ($idx < $seq->count()) {\n        return $seq[$idx];\n    } else {\n        throw new Exception(\"nth: index out of range\");\n    }\n}\n\nfunction first($seq) {\n    if ($seq === NULL || count($seq) === 0) {\n        return NULL;\n    } else {\n        return $seq[0];\n    }\n}\n\nfunction rest($seq) {\n    if ($seq === NULL) {\n        return new ListClass();\n    } else {\n        $l = new ListClass();\n        $l->exchangeArray(array_slice($seq->getArrayCopy(), 1));\n        return $l;\n    }\n}\n\nfunction empty_Q($seq) { return $seq->count() === 0; }\n\nfunction scount($seq) { return ($seq === NULL ? 0 : $seq->count()); }\n\nfunction apply($f) {\n    $args = array_slice(func_get_args(), 1);\n    $last_arg = array_pop($args)->getArrayCopy();\n    return $f->apply(array_merge($args, $last_arg));\n}\n\nfunction map($f, $seq) {\n    $l = new ListClass();\n    # @ to surpress warning if $f throws an exception\n    @$l->exchangeArray(array_map($f, $seq->getArrayCopy()));\n    return $l;\n}\n\nfunction conj($src) {\n    $args = array_slice(func_get_args(), 1);\n    $tmp = $src->getArrayCopy();\n    if (_list_Q($src)) {\n        foreach ($args as $arg) { array_unshift($tmp, $arg); }\n        $s = new ListClass();\n    } else {\n        foreach ($args as $arg) { $tmp[] = $arg; }\n        $s = new VectorClass();\n    }\n    $s->exchangeArray($tmp);\n    return $s;\n}\n\nfunction seq($src) {\n    if (_list_Q($src)) {\n        if (count($src) == 0) { return NULL; }\n        return $src;\n    } elseif (_vector_Q($src)) {\n        if (count($src) == 0) { return NULL; }\n        $tmp = $src->getArrayCopy();\n        $s = new ListClass();\n        $s->exchangeArray($tmp);\n        return $s;\n    } elseif (_string_Q($src)) {\n        if (strlen($src) == 0) { return NULL; }\n        $tmp = str_split($src);\n        $s = new ListClass();\n        $s->exchangeArray($tmp);\n        return $s;\n    } elseif (_nil_Q($src)) {\n        return NULL;\n    } else {\n        throw new Exception(\"seq: called on non-sequence\");\n    }\n    return $s;\n}\n\n\n\n// Metadata functions\nfunction with_meta($obj, $m) {\n    $new_obj = clone $obj;\n    $new_obj->meta = $m;\n    return $new_obj;\n}\n\nfunction meta($obj) {\n    return $obj->meta;\n}\n\n\n// Atom functions\nfunction deref($atm) { return $atm->value; }\nfunction reset_BANG($atm, $val) { return $atm->value = $val; }\nfunction swap_BANG($atm, $f) {\n    $args = array_slice(func_get_args(),2);\n    array_unshift($args, $atm->value);\n    $atm->value = call_user_func_array($f, $args);\n    return $atm->value;\n}\n\n\n// core_ns is namespace of type functions\n$core_ns = array(\n    '='=>      function ($a, $b) { return _equal_Q($a, $b); },\n    'throw'=>  function ($a) { return mal_throw($a); },\n    'nil?'=>   function ($a) { return _nil_Q($a); },\n    'true?'=>  function ($a) { return _true_Q($a); },\n    'false?'=> function ($a) { return _false_Q($a); },\n    'number?'=> function ($a) { return _number_Q($a); },\n    'symbol'=> function () { return call_user_func_array('_symbol', func_get_args()); },\n    'symbol?'=> function ($a) { return _symbol_Q($a); },\n    'keyword'=> function () { return call_user_func_array('_keyword', func_get_args()); },\n    'keyword?'=> function ($a) { return _keyword_Q($a); },\n\n    'string?'=> function ($a) { return _string_Q($a); },\n    'fn?'=>    function($a) { return _fn_Q($a) || (_function_Q($a) && !$a->ismacro ); },\n    'macro?'=> function($a) { return _function_Q($a) && $a->ismacro; },\n    'pr-str'=> function () { return call_user_func_array('pr_str', func_get_args()); },\n    'str'=>    function () { return call_user_func_array('str', func_get_args()); },\n    'prn'=>    function () { return call_user_func_array('prn', func_get_args()); },\n    'println'=>function () { return call_user_func_array('println', func_get_args()); },\n    'readline'=>function ($a) { return mal_readline($a); },\n    'read-string'=>function ($a) { return read_str($a); },\n    'slurp'=>  function ($a) { return file_get_contents($a); },\n    '<'=>      function ($a, $b) { return $a < $b; },\n    '<='=>     function ($a, $b) { return $a <= $b; },\n    '>'=>      function ($a, $b) { return $a > $b; },\n    '>='=>     function ($a, $b) { return $a >= $b; },\n    '+'=>      function ($a, $b) { return intval($a + $b,10); },\n    '-'=>      function ($a, $b) { return intval($a - $b,10); },\n    '*'=>      function ($a, $b) { return intval($a * $b,10); },\n    '/'=>      function ($a, $b) { return intval($a / $b,10); },\n    'time-ms'=>function () { return time_ms(); },\n\n    'list'=>   function () { return call_user_func_array('_list', func_get_args()); },\n    'list?'=>  function ($a) { return _list_Q($a); },\n    'vector'=> function () { return call_user_func_array('_vector', func_get_args()); },\n    'vector?'=> function ($a) { return _vector_Q($a); },\n    'hash-map' => function () { return call_user_func_array('_hash_map', func_get_args()); },\n    'map?'=>   function ($a) { return _hash_map_Q($a); },\n    'assoc' => function () { return call_user_func_array('assoc', func_get_args()); },\n    'dissoc' => function () { return call_user_func_array('dissoc', func_get_args()); },\n    'get' =>   function ($a, $b) { return get($a, $b); },\n    'contains?' => function ($a, $b) { return contains_Q($a, $b); },\n    'keys' =>  function ($a) { return keys($a); },\n    'vals' =>  function ($a) { return vals($a); },\n\n    'sequential?'=> function ($a) { return _sequential_Q($a); },\n    'cons'=>   function ($a, $b) { return cons($a, $b); },\n    'concat'=> function () { return call_user_func_array('concat', func_get_args()); },\n    'vec'=>    function ($a) { return vec($a, $b); },\n    'nth'=>    function ($a, $b) { return nth($a, $b); },\n    'first'=>  function ($a) { return first($a); },\n    'rest'=>   function ($a) { return rest($a); },\n    'empty?'=> function ($a) { return empty_Q($a); },\n    'count'=>  function ($a) { return scount($a); },\n    'apply'=>  function () { return call_user_func_array('apply', func_get_args()); },\n    'map'=>    function ($a, $b) { return map($a, $b); },\n\n    'conj'=>   function () { return call_user_func_array('conj', func_get_args()); },\n    'seq'=>    function ($a) { return seq($a); },\n\n    'with-meta'=> function ($a, $b) { return with_meta($a, $b); },\n    'meta'=>   function ($a) { return meta($a); },\n    'atom'=>   function ($a) { return _atom($a); },\n    'atom?'=>  function ($a) { return _atom_Q($a); },\n    'deref'=>  function ($a) { return deref($a); },\n    'reset!'=> function ($a, $b) { return reset_BANG($a, $b); },\n    'swap!'=>  function () { return call_user_func_array('swap_BANG', func_get_args()); },\n);\n\n\n?>\n"
  },
  {
    "path": "impls/php/env.php",
    "content": "<?php\n\nrequire_once 'types.php';\n\nclass Env {\n    public $data = array();\n    public $outer = NULL;\n    public function __construct($outer, $binds=NULL, $exprs=NULL) {\n        $this->outer = $outer;\n        if ($binds) {\n            if (_sequential_Q($exprs)) {\n                $exprs = $exprs->getArrayCopy();\n            }\n            for ($i=0; $i<count($binds); $i++) {\n                if ($binds[$i]->value === \"&\") {\n                    if ($exprs !== NULL && $i < count($exprs)) {\n                        $lst = call_user_func_array('_list', array_slice($exprs, $i));\n                    } else {\n                        $lst = _list();\n                    }\n                    $this->data[$binds[$i+1]->value] = $lst;\n                    break;\n                } else {\n                    if ($exprs !== NULL && $i < count($exprs)) {\n                        $this->data[$binds[$i]->value] = $exprs[$i];\n                    } else {\n                        $this->data[$binds[$i]->value] = NULL;\n                    }\n                }\n            }\n        }\n    }\n    public function find($key) {\n        if (array_key_exists($key, $this->data)) {\n            return $this;\n        } elseif ($this->outer) {\n            return $this->outer->find($key);\n        } else {\n            return NULL;\n        }\n    }\n    public function set($key, $value) {\n        $this->data[$key->value] = $value;\n        return $value;\n    }\n    public function get($key) {\n        $env = $this->find($key);\n        if (!$env) {\n            throw new Exception(\"'\" . $key . \"' not found\");\n        } else {\n            return $env->data[$key];\n        }\n    }\n}\n\n?>\n"
  },
  {
    "path": "impls/php/interop.php",
    "content": "<?php\n\nrequire_once 'types.php';\n\nfunction _to_php($obj) {\n    if (_list_Q($obj) || _vector_Q($obj) || _hash_map_Q($obj)) {\n        $ret = array();\n        foreach ($obj as $k => $v) {\n            $ret[_to_php($k)] = _to_php($v);\n        }\n        return $ret;\n    } elseif (is_string($obj)) {\n        if (strpos($obj, chr(0x7f)) === 0) {\n            return \":\".substr($obj,1);\n        } else {\n            return $obj;\n        }\n    } elseif (_symbol_Q($obj)) {\n        return ${$obj->value};\n    } elseif (_atom_Q($obj)) {\n        return $obj->value;\n    } else {\n        return $obj;\n    }\n}\n\nfunction _to_mal($obj) {\n    switch (gettype($obj)) {\n    case \"object\":\n      return _to_mal(get_object_vars($obj));\n    case \"array\":\n        $obj_conv = array();\n        foreach ($obj as $k => $v) {\n            $obj_conv[_to_mal($k)] = _to_mal($v);\n        }\n        if ($obj_conv !== array_values($obj_conv)) {\n            $new_obj = _hash_map();\n            $new_obj->exchangeArray($obj_conv);\n            return $new_obj;\n        } else {\n            return call_user_func_array('_list', $obj_conv);\n        }\n    default:\n        return $obj;\n    }\n}\n\nfunction _to_native($name, $env) {\n  if (is_callable($name)) {\n    return _function(function() use ($name) {\n      $args = array_map(\"_to_php\", func_get_args());\n      $res = call_user_func_array($name, $args);\n      return _to_mal($res);\n    });\n  // special case for language constructs\n  } else if ($name == \"print\") {\n    return _function(function($value) {\n      print(_to_php($value));\n      return null;\n    });\n  } else if ($name == \"exit\") {\n    return _function(function($value) {\n      exit(_to_php($value));\n      return null;\n    });\n  } else if ($name == \"require\") {\n    return _function(function($value) {\n      require(_to_php($value));\n      return null;\n    });\n  } else if (in_array($name, [\"_SERVER\", \"_GET\", \"_POST\", \"_FILES\", \"_REQUEST\", \"_SESSION\", \"_ENV\", \"_COOKIE\"])) {\n      $val = $GLOBALS[$name];\n  } else if (defined($name)) {\n      $val = constant($name);\n  } else {\n      $val = ${$name};\n  }\n  return _to_mal($val);\n}\n?>\n"
  },
  {
    "path": "impls/php/printer.php",
    "content": "<?php\n\nrequire_once 'types.php';\n\nfunction _pr_str($obj, $print_readably=True) {\n    if (_list_Q($obj)) {\n        $ret = array();\n        foreach ($obj as $e) {\n            array_push($ret, _pr_str($e, $print_readably));\n        }\n        return \"(\" . implode(\" \", $ret) . \")\";\n    } elseif (_vector_Q($obj)) {\n        $ret = array();\n        foreach ($obj as $e) {\n            array_push($ret, _pr_str($e, $print_readably));\n        }\n        return \"[\" . implode(\" \", $ret) . \"]\";\n    } elseif (_hash_map_Q($obj)) {\n        $ret = array();\n        foreach (array_keys($obj->getArrayCopy()) as $k) {\n            $ret[] = _pr_str(\"$k\", $print_readably);\n            $ret[] = _pr_str($obj[$k], $print_readably);\n        }\n        return \"{\" . implode(\" \", $ret) . \"}\";\n    } elseif (is_string($obj)) {\n        if (strpos($obj, chr(0x7f)) === 0) {\n            return \":\".substr($obj,1);\n        } elseif ($print_readably) {\n            $obj = preg_replace('/\\n/', '\\\\n', preg_replace('/\"/', '\\\\\"', preg_replace('/\\\\\\\\/', '\\\\\\\\\\\\\\\\', $obj)));\n            return '\"' . $obj . '\"';\n        } else {\n            return $obj;\n        }\n    } elseif (is_double($obj)) {\n        return $obj;\n    } elseif (is_integer($obj)) {\n        return $obj;\n    } elseif ($obj === NULL) {\n        return \"nil\";\n    } elseif ($obj === true) {\n        return \"true\";\n    } elseif ($obj === false) {\n        return \"false\";\n    } elseif (_symbol_Q($obj)) {\n        return $obj->value;\n    } elseif (_atom_Q($obj)) {\n        return \"(atom \" . _pr_str($obj->value, $print_readably) . \")\";\n    } elseif (_function_Q($obj)) {\n        return \"(fn* [...] ...)\";\n    } elseif (is_callable($obj)) {  // only step4 and below\n        return \"#<function ...>\";\n    } elseif (is_object($obj)) {\n        return \"#<object ...>\";\n    } elseif (is_array($obj)) {\n        return \"#<array ...>\";\n    } else {\n        throw new Exception(\"_pr_str unknown type: \" . gettype($obj));\n    }\n}\n\n?>\n"
  },
  {
    "path": "impls/php/reader.php",
    "content": "<?php\n\nrequire_once 'types.php';\n\nclass Reader {\n    protected $tokens = array();\n    protected $position = 0;\n    public function __construct($tokens) {\n        $this->tokens = $tokens;\n        $this->position = 0;\n    }\n    public function next() {\n        if ($this->position >= count($this->tokens)) { return null; }\n        return $this->tokens[$this->position++];\n    }\n    public function peek() {\n        if ($this->position >= count($this->tokens)) { return null; }\n        return $this->tokens[$this->position];\n    }\n}\n\nclass BlankException extends Exception {\n}\n\nfunction _real_token($s) {\n    return $s !== '' && $s[0] !== ';';\n}\n\nfunction tokenize($str) {\n    $pat = \"/[\\s,]*(php\\/|~@|[\\[\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\s\\[\\]{}('\\\"`,;)]*)/\";\n    preg_match_all($pat, $str, $matches);\n    return array_values(array_filter($matches[1], '_real_token'));\n}\n\nfunction read_atom($reader) {\n    $token = $reader->next();\n    if (preg_match(\"/^-?[0-9]+$/\", $token)) {\n        return intval($token, 10);\n    } elseif (preg_match(\"/^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$/\", $token)) {\n        $str = substr($token, 1, -1);\n        $str = str_replace('\\\\\\\\', chr(0x7f), $str);\n        $str = str_replace('\\\\\"', '\"', $str);\n        $str = str_replace('\\\\n', \"\\n\", $str);\n        $str = str_replace(chr(0x7f), \"\\\\\", $str);\n        return $str;\n    } elseif ($token[0] === \"\\\"\") {\n        throw new Exception(\"expected '\\\"', got EOF\");\n    } elseif ($token[0] === \":\") {\n        return _keyword(substr($token,1));\n    } elseif ($token === \"nil\") {\n        return NULL;\n    } elseif ($token === \"true\") {\n        return true;\n    } elseif ($token === \"false\") {\n        return false;\n    } else {\n        return _symbol($token);\n    }\n}\n\nfunction read_list($reader, $constr='_list', $start='(', $end=')') {\n    $ast = $constr();\n    $token = $reader->next();\n    if ($token !== $start) {\n        throw new Exception(\"expected '\" . $start . \"'\");\n    }\n    while (($token = $reader->peek()) !== $end) {\n        if ($token === \"\" || $token === null) {\n            throw new Exception(\"expected '\" . $end . \"', got EOF\");\n        }\n        $ast[] = read_form($reader);\n    }\n    $reader->next();\n    return $ast;\n}\n\nfunction read_hash_map($reader) {\n    $lst = read_list($reader, '_list', '{', '}');\n    return call_user_func_array('_hash_map', $lst->getArrayCopy());\n}\n\nfunction read_form($reader) {\n    $token = $reader->peek();\n    switch ($token) {\n    case '\\'': $reader->next();\n               return _list(_symbol('quote'),\n                               read_form($reader));\n    case '`':  $reader->next();\n               return _list(_symbol('quasiquote'),\n                               read_form($reader));\n    case '~':  $reader->next();\n               return _list(_symbol('unquote'),\n                               read_form($reader));\n    case '~@': $reader->next();\n               return _list(_symbol('splice-unquote'),\n                               read_form($reader));\n    case '^':  $reader->next();\n               $meta = read_form($reader);\n               return _list(_symbol('with-meta'),\n                               read_form($reader),\n                               $meta);\n\n    case '@':  $reader->next();\n               return _list(_symbol('deref'),\n                               read_form($reader));\n\n    case 'php/': $reader->next();\n               return _list(_symbol('to-native'),\n                               read_form($reader));\n\n    case ')': throw new Exception(\"unexpected ')'\");\n    case '(': return read_list($reader);\n    case ']': throw new Exception(\"unexpected ']'\");\n    case '[': return read_list($reader, '_vector', '[', ']');\n    case '}': throw new Exception(\"unexpected '}'\");\n    case '{': return read_hash_map($reader);\n\n    default:  return read_atom($reader);\n    }\n}\n\nfunction read_str($str) {\n    $tokens = tokenize($str);\n    if (count($tokens) === 0) { throw new BlankException(); }\n    return read_form(new Reader($tokens));\n}\n\n?>\n"
  },
  {
    "path": "impls/php/readline.php",
    "content": "<?php\n\nif (php_sapi_name() == \"cli\") {\n  $HISTORY_FILE = $_SERVER['HOME'] . \"/.mal-history\";\n  function mal_readline($prompt) {\n      global $HISTORY_FILE;\n      static $history_loaded = false;\n\n      // Load the history file\n      if (! $history_loaded) {\n          $history_loaded = true;\n          if (is_readable($HISTORY_FILE)) {\n              if ($file = fopen($HISTORY_FILE, \"r\")) {\n                  while (!feof($file)) {\n                      $line = fgets($file);\n                      if ($line) { readline_add_history($line); }\n                  }\n                  fclose($file);\n              }\n          }\n      }\n\n      $line = readline($prompt);\n      if ($line === false) { return NULL; }\n      readline_add_history($line);\n\n      // Append to the history file\n      if (is_writable($HISTORY_FILE)) {\n          if ($file = fopen($HISTORY_FILE, \"a\")) {\n              fputs($file, $line . \"\\n\");\n              fclose($file);\n          }\n      }\n\n      return $line;\n  }\n} else {\n  function mal_readline($prompt) {}\n}\n\n?>\n"
  },
  {
    "path": "impls/php/run",
    "content": "#!/usr/bin/env bash\nexec php $(dirname $0)/${STEP:-stepA_mal}.php \"${@}\"\n"
  },
  {
    "path": "impls/php/step0_repl.php",
    "content": "<?php\n\nrequire_once 'readline.php';\n\n// read\nfunction READ($str) {\n    return $str;\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    return $ast;\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return $exp;\n}\n\n// repl\nfunction rep($str) {\n    return MAL_PRINT(MAL_EVAL(READ($str), array()));\n}\n\n// repl loop\ndo {\n    $line = mal_readline(\"user> \");\n    if ($line === NULL) { break; }\n    if ($line !== \"\") {\n        print(rep($line) . \"\\n\");\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step1_read_print.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    return $ast;\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\nfunction rep($str) {\n    return MAL_PRINT(MAL_EVAL(READ($str), array()));\n}\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step2_eval.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    // echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n\n    if (_symbol_Q($ast)) {\n        return $env[$ast->value];\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    // apply list\n    $el = [];\n    foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n    $f = $el[0];\n    $args = array_slice($el, 1);\n    return call_user_func_array($f, $args);\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = array();\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n$repl_env['+'] = function ($a, $b) { return intval($a + $b,10); };\n$repl_env['-'] = function ($a, $b) { return intval($a - $b,10); };\n$repl_env['*'] = function ($a, $b) { return intval($a * $b,10); };\n$repl_env['/'] = function ($a, $b) { return intval($a / $b,10); };\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step3_env.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    // apply list\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        return MAL_EVAL($ast[2], $let_env);\n    default:\n        $el = [];\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        $f = $el[0];\n        $args = array_slice($el, 1);\n        return call_user_func_array($f, $args);\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n$repl_env->set(_symbol('+'), function ($a, $b) { return intval($a + $b,10); });\n$repl_env->set(_symbol('-'), function ($a, $b) { return intval($a - $b,10); });\n$repl_env->set(_symbol('*'), function ($a, $b) { return intval($a * $b,10); });\n$repl_env->set(_symbol('/'), function ($a, $b) { return intval($a / $b,10); });\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step4_if_fn_do.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    // apply list\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        return MAL_EVAL($ast[2], $let_env);\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        return MAL_EVAL($ast[count($ast)-1], $env);\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { return MAL_EVAL($ast[3], $env); }\n            else                   { return NULL; }\n        } else {\n            return MAL_EVAL($ast[2], $env);\n        }\n    case \"fn*\":\n        return function() use ($env, $ast) {\n            $fn_env = new Env($env, $ast[1], func_get_args());\n            return MAL_EVAL($ast[2], $fn_env);\n        };\n    default:\n        $el = [];\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        $f = $el[0];\n        $args = array_slice($el, 1);\n        return call_user_func_array($f, $args);\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step5_tco.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    while (true) {\n\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    // apply list\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        $ast = $ast[2];\n        $env = $let_env;\n        break; // Continue loop (TCO)\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        $ast = $ast[count($ast)-1];\n        break; // Continue loop (TCO)\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { $ast = $ast[3]; }\n            else                   { $ast = NULL; }\n        } else {\n            $ast = $ast[2];\n        }\n        break; // Continue loop (TCO)\n    case \"fn*\":\n        return _function('MAL_EVAL', 'native',\n                         $ast[2], $env, $ast[1]);\n    default:\n        $el = [];\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        $f = $el[0];\n        $args = array_slice($el, 1);\n        if ($f->type === 'native') {\n            $ast = $f->ast;\n            $env = $f->gen_env($args);\n            // Continue loop (TCO)\n        } else {\n            return $f->apply($args);\n        }\n    }\n\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step6_file.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction MAL_EVAL($ast, $env) {\n    while (true) {\n\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    // apply list\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        $ast = $ast[2];\n        $env = $let_env;\n        break; // Continue loop (TCO)\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        $ast = $ast[count($ast)-1];\n        break; // Continue loop (TCO)\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { $ast = $ast[3]; }\n            else                   { $ast = NULL; }\n        } else {\n            $ast = $ast[2];\n        }\n        break; // Continue loop (TCO)\n    case \"fn*\":\n        return _function('MAL_EVAL', 'native',\n                         $ast[2], $env, $ast[1]);\n    default:\n        $el = [];\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        $f = $el[0];\n        $args = array_slice($el, 1);\n        if ($f->type === 'native') {\n            $ast = $f->ast;\n            $env = $f->gen_env($args);\n            // Continue loop (TCO)\n        } else {\n            return $f->apply($args);\n        }\n    }\n\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n$repl_env->set(_symbol('eval'), _function(function($ast) {\n    global $repl_env; return MAL_EVAL($ast, $repl_env);\n}));\n$_argv = _list();\nfor ($i=2; $i < count($argv); $i++) {\n    $_argv->append($argv[$i]);\n}\n$repl_env->set(_symbol('*ARGV*'), $_argv);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nif (count($argv) > 1) {\n    rep('(load-file \"' . $argv[1] . '\")');\n    exit(0);\n}\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step7_quote.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction qq_loop($elt, $acc) {\n    if (_list_Q($elt)\n        and count($elt) == 2\n        and _symbol_Q($elt[0])\n        and $elt[0]->value === 'splice-unquote') {\n        return _list(_symbol(\"concat\"), $elt[1], $acc);\n    } else {\n        return _list(_symbol(\"cons\"), quasiquote($elt), $acc);\n    }\n}\n\nfunction qq_foldr($xs) {\n    $acc = _list();\n    for ($i=count($xs)-1; 0<=$i; $i-=1) {\n        $acc = qq_loop($xs[$i], $acc);\n    }\n    return $acc;\n}\n\nfunction quasiquote($ast) {\n    if (_vector_Q($ast)) {\n        return _list(_symbol(\"vec\"), qq_foldr($ast));\n    } elseif (_symbol_Q($ast) or _hash_map_Q($ast)) {\n        return _list(_symbol(\"quote\"), $ast);\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    } elseif (count($ast) == 2 and _symbol_Q($ast[0]) and $ast[0]->value === 'unquote') {\n        return $ast[1];\n    } else {\n        return qq_foldr($ast);\n    }\n}\n\nfunction MAL_EVAL($ast, $env) {\n    while (true) {\n\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    // apply list\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        $ast = $ast[2];\n        $env = $let_env;\n        break; // Continue loop (TCO)\n    case \"quote\":\n        return $ast[1];\n    case \"quasiquote\":\n        $ast = quasiquote($ast[1]);\n        break; // Continue loop (TCO)\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        $ast = $ast[count($ast)-1];\n        break; // Continue loop (TCO)\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { $ast = $ast[3]; }\n            else                   { $ast = NULL; }\n        } else {\n            $ast = $ast[2];\n        }\n        break; // Continue loop (TCO)\n    case \"fn*\":\n        return _function('MAL_EVAL', 'native',\n                         $ast[2], $env, $ast[1]);\n    default:\n        $el = [];\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        $f = $el[0];\n        $args = array_slice($el, 1);\n        if ($f->type === 'native') {\n            $ast = $f->ast;\n            $env = $f->gen_env($args);\n            // Continue loop (TCO)\n        } else {\n            return $f->apply($args);\n        }\n    }\n\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n$repl_env->set(_symbol('eval'), _function(function($ast) {\n    global $repl_env; return MAL_EVAL($ast, $repl_env);\n}));\n$_argv = _list();\nfor ($i=2; $i < count($argv); $i++) {\n    $_argv->append($argv[$i]);\n}\n$repl_env->set(_symbol('*ARGV*'), $_argv);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\n\nif (count($argv) > 1) {\n    rep('(load-file \"' . $argv[1] . '\")');\n    exit(0);\n}\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step8_macros.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction qq_loop($elt, $acc) {\n    if (_list_Q($elt)\n        and count($elt) == 2\n        and _symbol_Q($elt[0])\n        and $elt[0]->value === 'splice-unquote') {\n        return _list(_symbol(\"concat\"), $elt[1], $acc);\n    } else {\n        return _list(_symbol(\"cons\"), quasiquote($elt), $acc);\n    }\n}\n\nfunction qq_foldr($xs) {\n    $acc = _list();\n    for ($i=count($xs)-1; 0<=$i; $i-=1) {\n        $acc = qq_loop($xs[$i], $acc);\n    }\n    return $acc;\n}\n\nfunction quasiquote($ast) {\n    if (_vector_Q($ast)) {\n        return _list(_symbol(\"vec\"), qq_foldr($ast));\n    } elseif (_symbol_Q($ast) or _hash_map_Q($ast)) {\n        return _list(_symbol(\"quote\"), $ast);\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    } elseif (count($ast) == 2 and _symbol_Q($ast[0]) and $ast[0]->value === 'unquote') {\n        return $ast[1];\n    } else {\n        return qq_foldr($ast);\n    }\n}\n\nfunction MAL_EVAL($ast, $env) {\n    while (true) {\n\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    // apply list\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        $ast = $ast[2];\n        $env = $let_env;\n        break; // Continue loop (TCO)\n    case \"quote\":\n        return $ast[1];\n    case \"quasiquote\":\n        $ast = quasiquote($ast[1]);\n        break; // Continue loop (TCO)\n    case \"defmacro!\":\n        $func = MAL_EVAL($ast[2], $env);\n        $func = _function('MAL_EVAL', 'native', $func->ast, $func->env, $func->params);\n        $func->ismacro = true;\n        return $env->set($ast[1], $func);\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        $ast = $ast[count($ast)-1];\n        break; // Continue loop (TCO)\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { $ast = $ast[3]; }\n            else                   { $ast = NULL; }\n        } else {\n            $ast = $ast[2];\n        }\n        break; // Continue loop (TCO)\n    case \"fn*\":\n        return _function('MAL_EVAL', 'native',\n                         $ast[2], $env, $ast[1]);\n    default:\n        $f = MAL_EVAL($a0, $env);\n        $unevaluated_args = array_slice($ast->getArrayCopy(), 1);\n        if ($f->ismacro) {\n            $ast = $f->apply($unevaluated_args);\n            break; // Continue loop (TCO)\n        }\n        $args = [];\n        foreach ($unevaluated_args as $a) { $args[] = MAL_EVAL($a, $env); }\n        if ($f->type === 'native') {\n            $ast = $f->ast;\n            $env = $f->gen_env($args);\n            // Continue loop (TCO)\n        } else {\n            return $f->apply($args);\n        }\n    }\n\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n$repl_env->set(_symbol('eval'), _function(function($ast) {\n    global $repl_env; return MAL_EVAL($ast, $repl_env);\n}));\n$_argv = _list();\nfor ($i=2; $i < count($argv); $i++) {\n    $_argv->append($argv[$i]);\n}\n$repl_env->set(_symbol('*ARGV*'), $_argv);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif (count($argv) > 1) {\n    rep('(load-file \"' . $argv[1] . '\")');\n    exit(0);\n}\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/step9_try.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction qq_loop($elt, $acc) {\n    if (_list_Q($elt)\n        and count($elt) == 2\n        and _symbol_Q($elt[0])\n        and $elt[0]->value === 'splice-unquote') {\n        return _list(_symbol(\"concat\"), $elt[1], $acc);\n    } else {\n        return _list(_symbol(\"cons\"), quasiquote($elt), $acc);\n    }\n}\n\nfunction qq_foldr($xs) {\n    $acc = _list();\n    for ($i=count($xs)-1; 0<=$i; $i-=1) {\n        $acc = qq_loop($xs[$i], $acc);\n    }\n    return $acc;\n}\n\nfunction quasiquote($ast) {\n    if (_vector_Q($ast)) {\n        return _list(_symbol(\"vec\"), qq_foldr($ast));\n    } elseif (_symbol_Q($ast) or _hash_map_Q($ast)) {\n        return _list(_symbol(\"quote\"), $ast);\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    } elseif (count($ast) == 2 and _symbol_Q($ast[0]) and $ast[0]->value === 'unquote') {\n        return $ast[1];\n    } else {\n        return qq_foldr($ast);\n    }\n}\n\nfunction MAL_EVAL($ast, $env) {\n    while (true) {\n\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    // apply list\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        $ast = $ast[2];\n        $env = $let_env;\n        break; // Continue loop (TCO)\n    case \"quote\":\n        return $ast[1];\n    case \"quasiquote\":\n        $ast = quasiquote($ast[1]);\n        break; // Continue loop (TCO)\n    case \"defmacro!\":\n        $func = MAL_EVAL($ast[2], $env);\n        $func = _function('MAL_EVAL', 'native', $func->ast, $func->env, $func->params);\n        $func->ismacro = true;\n        return $env->set($ast[1], $func);\n    case \"try*\":\n        $a1 = $ast[1];\n        $a2 = $ast[2];\n        if ($a2[0]->value === \"catch*\") {\n            try {\n                return MAL_EVAL($a1, $env);\n            } catch (_Error $e) {\n                $catch_env = new Env($env, array($a2[1]),\n                                            array($e->obj));\n                return MAL_EVAL($a2[2], $catch_env);\n            } catch (Exception $e) {\n                $catch_env = new Env($env, array($a2[1]),\n                                            array($e->getMessage()));\n                return MAL_EVAL($a2[2], $catch_env);\n            }\n        } else {\n            return MAL_EVAL($a1, $env);\n        }\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        $ast = $ast[count($ast)-1];\n        break; // Continue loop (TCO)\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { $ast = $ast[3]; }\n            else                   { $ast = NULL; }\n        } else {\n            $ast = $ast[2];\n        }\n        break; // Continue loop (TCO)\n    case \"fn*\":\n        return _function('MAL_EVAL', 'native',\n                         $ast[2], $env, $ast[1]);\n    default:\n        $f = MAL_EVAL($a0, $env);\n        $unevaluated_args = array_slice($ast->getArrayCopy(), 1);\n        if ($f->ismacro) {\n            $ast = $f->apply($unevaluated_args);\n            break; // Continue loop (TCO)\n        }\n        $args = [];\n        foreach ($unevaluated_args as $a) { $args[] = MAL_EVAL($a, $env); }\n        if ($f->type === 'native') {\n            $ast = $f->ast;\n            $env = $f->gen_env($args);\n            // Continue loop (TCO)\n        } else {\n            return $f->apply($args);\n        }\n    }\n\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n$repl_env->set(_symbol('eval'), _function(function($ast) {\n    global $repl_env; return MAL_EVAL($ast, $repl_env);\n}));\n$_argv = _list();\nfor ($i=2; $i < count($argv); $i++) {\n    $_argv->append($argv[$i]);\n}\n$repl_env->set(_symbol('*ARGV*'), $_argv);\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif (count($argv) > 1) {\n    rep('(load-file \"' . $argv[1] . '\")');\n    exit(0);\n}\n\n// repl loop\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (_Error $e) {\n        echo \"Error: \" . _pr_str($e->obj, True) . \"\\n\";\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/stepA_mal.php",
    "content": "<?php\n\nrequire_once 'readline.php';\nrequire_once 'types.php';\nrequire_once 'reader.php';\nrequire_once 'printer.php';\nrequire_once 'interop.php';\nrequire_once 'env.php';\nrequire_once 'core.php';\n\n// read\nfunction READ($str) {\n    return read_str($str);\n}\n\n// eval\nfunction qq_loop($elt, $acc) {\n    if (_list_Q($elt)\n        and count($elt) == 2\n        and _symbol_Q($elt[0])\n        and $elt[0]->value === 'splice-unquote') {\n        return _list(_symbol(\"concat\"), $elt[1], $acc);\n    } else {\n        return _list(_symbol(\"cons\"), quasiquote($elt), $acc);\n    }\n}\n\nfunction qq_foldr($xs) {\n    $acc = _list();\n    for ($i=count($xs)-1; 0<=$i; $i-=1) {\n        $acc = qq_loop($xs[$i], $acc);\n    }\n    return $acc;\n}\n\nfunction quasiquote($ast) {\n    if (_vector_Q($ast)) {\n        return _list(_symbol(\"vec\"), qq_foldr($ast));\n    } elseif (_symbol_Q($ast) or _hash_map_Q($ast)) {\n        return _list(_symbol(\"quote\"), $ast);\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    } elseif (count($ast) == 2 and _symbol_Q($ast[0]) and $ast[0]->value === 'unquote') {\n        return $ast[1];\n    } else {\n        return qq_foldr($ast);\n    }\n}\n\nfunction MAL_EVAL($ast, $env) {\n    while (true) {\n\n    $dbgenv = $env->find(\"DEBUG-EVAL\");\n    if ($dbgenv) {\n        $dbgeval = $env->get(\"DEBUG-EVAL\");\n        if ($dbgeval !== NULL && $dbgeval !== false) {\n            echo \"EVAL: \" . _pr_str($ast) . \"\\n\";\n        }\n    }\n\n    if (_symbol_Q($ast)) {\n        return $env->get($ast->value);\n    } elseif (_vector_Q($ast)) {\n            $el = _vector();\n        foreach ($ast as $a) { $el[] = MAL_EVAL($a, $env); }\n        return $el;\n    } elseif (_hash_map_Q($ast)) {\n        $new_hm = _hash_map();\n        foreach (array_keys($ast->getArrayCopy()) as $key) {\n            $new_hm[$key] = MAL_EVAL($ast[$key], $env);\n        }\n        return $new_hm;\n    } elseif (!_list_Q($ast)) {\n        return $ast;\n    }\n\n    // apply list\n    if ($ast->count() === 0) {\n        return $ast;\n    }\n\n    $a0 = $ast[0];\n    $a0v = (_symbol_Q($a0) ? $a0->value : $a0);\n    switch ($a0v) {\n    case \"def!\":\n        $res = MAL_EVAL($ast[2], $env);\n        return $env->set($ast[1], $res);\n    case \"let*\":\n        $a1 = $ast[1];\n        $let_env = new Env($env);\n        for ($i=0; $i < count($a1); $i+=2) {\n            $let_env->set($a1[$i], MAL_EVAL($a1[$i+1], $let_env));\n        }\n        $ast = $ast[2];\n        $env = $let_env;\n        break; // Continue loop (TCO)\n    case \"quote\":\n        return $ast[1];\n    case \"quasiquote\":\n        $ast = quasiquote($ast[1]);\n        break; // Continue loop (TCO)\n    case \"defmacro!\":\n        $func = MAL_EVAL($ast[2], $env);\n        $func = _function('MAL_EVAL', 'native', $func->ast, $func->env, $func->params);\n        $func->ismacro = true;\n        return $env->set($ast[1], $func);\n    case \"php*\":\n        $res = eval($ast[1]);\n        return _to_mal($res);\n    case \"try*\":\n        $a1 = $ast[1];\n        $a2 = $ast[2];\n        if ($a2[0]->value === \"catch*\") {\n            try {\n                return MAL_EVAL($a1, $env);\n            } catch (_Error $e) {\n                $catch_env = new Env($env, array($a2[1]),\n                                            array($e->obj));\n                return MAL_EVAL($a2[2], $catch_env);\n            } catch (Exception $e) {\n                $catch_env = new Env($env, array($a2[1]),\n                                            array($e->getMessage()));\n                return MAL_EVAL($a2[2], $catch_env);\n            }\n        } else {\n            return MAL_EVAL($a1, $env);\n        }\n    case \"do\":\n        foreach ($ast->slice(1, -1) as $a) { MAL_EVAL($a, $env); }\n        $ast = $ast[count($ast)-1];\n        break; // Continue loop (TCO)\n    case \"if\":\n        $cond = MAL_EVAL($ast[1], $env);\n        if ($cond === NULL || $cond === false) {\n            if (count($ast) === 4) { $ast = $ast[3]; }\n            else                   { $ast = NULL; }\n        } else {\n            $ast = $ast[2];\n        }\n        break; // Continue loop (TCO)\n    case \"fn*\":\n        return _function('MAL_EVAL', 'native',\n                         $ast[2], $env, $ast[1]);\n    case \"to-native\":\n        return _to_native($ast[1]->value, $env);\n    default:\n        $f = MAL_EVAL($a0, $env);\n        $unevaluated_args = array_slice($ast->getArrayCopy(), 1);\n        if ($f->ismacro) {\n            $ast = $f->apply($unevaluated_args);\n            break; // Continue loop (TCO)\n        }\n        $args = [];\n        foreach ($unevaluated_args as $a) { $args[] = MAL_EVAL($a, $env); }\n        if ($f->type === 'native') {\n            $ast = $f->ast;\n            $env = $f->gen_env($args);\n            // Continue loop (TCO)\n        } else {\n            return $f->apply($args);\n        }\n    }\n\n    }\n}\n\n// print\nfunction MAL_PRINT($exp) {\n    return _pr_str($exp, True);\n}\n\n// repl\n$repl_env = new Env(NULL);\nfunction rep($str) {\n    global $repl_env;\n    return MAL_PRINT(MAL_EVAL(READ($str), $repl_env));\n}\n\n// core.php: defined using PHP\nforeach ($core_ns as $k=>$v) {\n    $repl_env->set(_symbol($k), _function($v));\n}\n$repl_env->set(_symbol('eval'), _function(function($ast) {\n    global $repl_env; return MAL_EVAL($ast, $repl_env);\n}));\n$_argv = _list();\nif (isset($argv)) {\n  for ($i=2; $i < count($argv); $i++) {\n      $_argv->append($argv[$i]);\n  }\n}\n$repl_env->set(_symbol('*ARGV*'), $_argv);\n\n// core.mal: defined using the language itself\nrep(\"(def! *host-language* \\\"php\\\")\");\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\");\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\n// run mal file\nif (count($argv) > 1) {\n    rep('(load-file \"' . $argv[1] . '\")');\n    exit(0);\n}\n\n// repl loop\nrep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\");\ndo {\n    try {\n        $line = mal_readline(\"user> \");\n        if ($line === NULL) { break; }\n        if ($line !== \"\") {\n            print(rep($line) . \"\\n\");\n        }\n    } catch (BlankException $e) {\n        continue;\n    } catch (_Error $e) {\n        echo \"Error: \" . _pr_str($e->obj, True) . \"\\n\";\n    } catch (Exception $e) {\n        echo \"Error: \" . $e->getMessage() . \"\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n} while (true);\n\n?>\n"
  },
  {
    "path": "impls/php/tests/step5_tco.mal",
    "content": ";; PHP: skipping non-TCO recursion\n;; Reason: completes at 10,000, unrecoverable segfault at 20,000\n"
  },
  {
    "path": "impls/php/tests/stepA_mal.mal",
    "content": ";; Testing basic php interop\n\n(php* \"return 7;\")\n;=>7\n\n(php* \"return '7';\")\n;=>\"7\"\n\n(php* \"return array(7,8,9);\")\n;=>(7 8 9)\n\n(php* \"return array(\\\"abc\\\" => 789);\")\n;=>{\"abc\" 789}\n\n(php* \"print \\\"hello\\n\\\";\")\n;/hello\n;=>nil\n\n(php* \"global $foo; $foo=8;\")\n(php* \"global $foo; return $foo;\")\n;=>8\n\n(php* \"global $f; $f = function($v) { return 1+$v; };\")\n(php* \"global $f; return array_map($f, array(1,2,3));\")\n;=>(2 3 4)\n\n;; testing native function calling\n\n(php/date \"Y-m-d\" 0)\n;=>\"1970-01-01\"\n\n;; testing native function with mal callback\n\n(php/array_map (fn* [t] (if (> t 3) t)) [1 2 3 4 5 6])\n;=>(nil nil nil 4 5 6)\n\n;; testing superglobal variable access\n\n(get php/_SERVER \"PHP_SELF\")\n;=>\"../php/stepA_mal.php\"\n\n;; testing PHP constants access\n\nphp/FILE_APPEND\n;=>8\n"
  },
  {
    "path": "impls/php/types.php",
    "content": "<?php\n\n\n// Errors/Exceptions\nclass _Error extends Exception {\n    public $obj = null;\n    public function __construct($obj) {\n        parent::__construct(\"Mal Error\", 0, null);\n        $this->obj = $obj;\n    }\n}\n\n\n// General functions\n\nfunction _equal_Q($a, $b) {\n    $ota = gettype($a) === \"object\" ? get_class($a) : gettype($a);\n    $otb = gettype($b) === \"object\" ? get_class($b) : gettype($b);\n    if (!($ota === $otb or (_sequential_Q($a) and _sequential_Q($b)))) {\n        return false;\n    } elseif (_symbol_Q($a)) {\n        #print \"ota: $ota, otb: $otb\\n\";\n        return $a->value === $b->value;\n    } elseif (_list_Q($a) or _vector_Q($a)) {\n        if ($a->count() !== $b->count()) { return false; }\n        for ($i=0; $i<$a->count(); $i++) {\n            if (!_equal_Q($a[$i], $b[$i])) { return false; }\n        }\n        return true;\n    } elseif (_hash_map_Q($a)) {\n        if ($a->count() !== $b->count()) { return false; }\n        $hm1 = $a->getArrayCopy();\n        $hm2 = $b->getArrayCopy();\n        foreach (array_keys($hm1) as $k) {\n            if (!_equal_Q($hm1[$k], $hm2[$k])) { return false; }\n        }\n        return true;\n    } else {\n        return $a === $b;\n    }\n}\n\nfunction _sequential_Q($seq) { return _list_Q($seq) or _vector_Q($seq); }\n\n\n// Scalars\nfunction _nil_Q($obj) { return $obj === NULL; }\nfunction _true_Q($obj) { return $obj === true; }\nfunction _false_Q($obj) { return $obj === false; }\nfunction _string_Q($obj) {\n    return is_string($obj) && strpos($obj, chr(0x7f)) !== 0;\n}\nfunction _number_Q($obj) { return is_int($obj); }\n\n\n// Symbols\nclass SymbolClass {\n    public $value = NULL;\n    public $meta = NULL;\n    public function __construct($value) {\n        $this->value = $value;\n    }\n}\nfunction _symbol($name) { return new SymbolClass($name); }\nfunction _symbol_Q($obj) { return ($obj instanceof SymbolClass); }\n\n// Keywords\nfunction _keyword($name) {\n    if (_keyword_Q($name)) {\n        return $name;\n    } else {\n        return chr(0x7f).$name;\n    }\n}\nfunction _keyword_Q($obj) {\n    return is_string($obj) && strpos($obj, chr(0x7f)) === 0;\n}\n\n\n\n// Functions\nclass FunctionClass {\n    public $func = NULL;\n    public $type = 'native';   // 'native' or 'platform'\n    public $meta = NULL;\n    public $ast = NULL;\n    public $env = NULL;\n    public $params = NULL;\n    public $ismacro = False;\n    public function __construct($func, $type,\n                                $ast, $env, $params, $ismacro=False) {\n        $this->func = $func;\n        $this->type = $type;\n        $this->ast = $ast;\n        #print_r($ast);\n        $this->env = $env;\n        $this->params = $params;\n        $this->ismacro = $ismacro;\n    }\n    public function __invoke() {\n        $args = func_get_args();\n        if ($this->type === 'native') {\n            $fn_env = new Env($this->env,\n                              $this->params, $args);\n            $evalf = $this->func;\n            return $evalf($this->ast, $fn_env);\n        } else {\n            return call_user_func_array($this->func, $args);\n        }\n    }\n    public function gen_env($args) {\n        return new Env($this->env, $this->params, $args);\n    }\n    public function apply($args) {\n        return call_user_func_array(array(&$this, '__invoke'),$args);\n    }\n}\n\nfunction _function($func, $type='platform',\n                   $ast=NULL, $env=NULL, $params=NULL, $ismacro=False) {\n    return new FunctionClass($func, $type, $ast, $env, $params, $ismacro);\n}\nfunction _function_Q($obj) { return $obj instanceof FunctionClass; }\nfunction _fn_Q($obj) { return $obj instanceof Closure; }\n\n\n// Parent class of list, vector\n// http://www.php.net/manual/en/class.arrayobject.php\nclass SeqClass extends ArrayObject {\n    public function slice($start, $length=NULL) {\n        $sc = new $this();\n        if ($start >= count($this)) {\n            $arr = array();\n        } else {\n            $arr = array_slice($this->getArrayCopy(), $start, $length);\n        }\n        $sc->exchangeArray($arr);\n        return $sc;\n    }\n}\n\n\n// Lists\nclass ListClass extends SeqClass {\n    public $meta = NULL;\n}\n\nfunction _list() {\n    $v = new ListClass();\n    $v->exchangeArray(func_get_args());\n    return $v;\n}\nfunction _list_Q($obj) { return $obj instanceof ListClass; }\n\n\n// Vectors\nclass VectorClass extends SeqClass {\n    public $meta = NULL;\n}\n\nfunction _vector() {\n    $v = new VectorClass();\n    $v->exchangeArray(func_get_args());\n    return $v;\n}\nfunction _vector_Q($obj) { return $obj instanceof VectorClass; }\n\n\n// Hash Maps\nclass HashMapClass extends ArrayObject {\n    public $meta = NULL;\n}\n\nfunction _hash_map() {\n    $args = func_get_args();\n    if (count($args) % 2 === 1) {\n        throw new Exception(\"Odd number of hash map arguments\");\n    }\n    $hm = new HashMapClass();\n    array_unshift($args, $hm);\n    return call_user_func_array('_assoc_BANG', $args);\n}\nfunction _hash_map_Q($obj) { return $obj instanceof HashMapClass; }\n\nfunction _assoc_BANG($hm) {\n    $args = func_get_args();\n    if (count($args) % 2 !== 1) {\n        throw new Exception(\"Odd number of assoc arguments\");\n    }\n    for ($i=1; $i<count($args); $i+=2) {\n        $ktoken = $args[$i];\n        $vtoken = $args[$i+1];\n        // TODO: support more than string keys\n        if (gettype($ktoken) !== \"string\") {\n            throw new Exception(\"expected hash-map key string, got: \" . gettype($ktoken));\n        }\n        $hm[$ktoken] = $vtoken;\n    }\n    return $hm;\n}\n\nfunction _dissoc_BANG($hm) {\n    $args = func_get_args();\n    for ($i=1; $i<count($args); $i++) {\n        $ktoken = $args[$i];\n        if ($hm && $hm->offsetExists($ktoken)) {\n            unset($hm[$ktoken]);\n        }\n    }\n    return $hm;\n}\n\n\n// Atoms\nclass Atom {\n    public $value = NULL;\n    public $meta = NULL;\n    public function __construct($value) {\n        $this->value = $value;\n    }\n}\nfunction _atom($val) { return new Atom($val); }\nfunction _atom_Q($atm) { return $atm instanceof Atom; }\n\n?>\n"
  },
  {
    "path": "impls/php/webrunner.php",
    "content": "<?php\n// if we're called in a webserver context, auto-resolve to mal file\nif (php_sapi_name() != \"cli\") {\n    $malfile = str_replace(\".php\", \".mal\", $_SERVER['SCRIPT_FILENAME']);\n    rep('(load-file \"' . $malfile . '\")');\n    exit(0);\n}\n?>\n"
  },
  {
    "path": "impls/picolisp/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# picolisp\nRUN apt-get -y install picolisp libreadline-dev\n"
  },
  {
    "path": "impls/picolisp/Makefile",
    "content": "all:\n\nclean:\n"
  },
  {
    "path": "impls/picolisp/core.l",
    "content": "(de MAL-= (A B)\n   (let (A* (MAL-type A)\n         B* (MAL-type B))\n      (cond\n         ((and (= A* 'map) (= B* 'map))\n          (MAL-map-= (MAL-value A) (MAL-value B)) )\n         ((and (memq A* '(list vector)) (memq B* '(list vector)))\n          (MAL-seq-= (MAL-value A) (MAL-value B)) )\n         ((= A* B*)\n          (= (MAL-value A) (MAL-value B)) )\n         (T NIL) ) ) )\n\n(de MAL-map-= (As Bs)\n   (when (= (length As) (length Bs))\n      (let (As* (chunk As) Bs* (chunk Bs))\n         (catch 'result\n            (while As*\n               (let (A (pop 'As*) Key (MAL-value (car A)) Val (cdr A)\n                     B (find '((X) (= Key (MAL-value (car X)))) Bs*) )\n                  (when (or (not B) (not (MAL-= Val (cdr B))))\n                     (throw 'result NIL) ) ) )\n            T ) ) ) )\n\n(de MAL-seq-= (As Bs)\n   (when (= (length As) (length Bs))\n      (catch 'result\n         (while As\n            (ifn (MAL-= (pop 'As) (pop 'Bs))\n               (throw 'result NIL) ) )\n         T ) ) )\n\n(de MAL-seq? (X)\n   (memq (MAL-type X) '(list vector)) )\n\n(de MAL-f (X)\n   (MAL-value (if (isa '+Func X) (get X 'fn) X)) )\n\n(de MAL-swap! @\n   (let (X (next) Fn (next) Args (rest))\n      (put X 'value (apply (MAL-f Fn) Args (MAL-value X))) ) )\n\n(de MAL-nth (Seq N)\n   (let (Seq* (MAL-value Seq) N* (MAL-value N))\n      (if (< N* (length Seq*))\n         (nth Seq* (inc N*) 1)\n         (throw 'err (MAL-error (MAL-string \"out of bounds\"))) ) ) )\n\n(de chunk (List)\n   (make\n      (for (L List L (cddr L))\n         (link (cons (car L) (cadr L))) ) ) )\n\n(de join (List)\n   (mapcan '((X) (list (car X) (cdr X))) List) )\n\n(de MAL-assoc @\n   (let (Map (next) Args (rest))\n      (MAL-map\n         (append Args\n            (join\n               (filter '((X) (not (find '((Y) (MAL-= (car Y) (car X)))\n                                     (chunk Args) ) ) )\n                  (chunk (MAL-value Map)) ) ) ) ) ) )\n\n(de MAL-dissoc @\n   (let (Map (next) Args (rest))\n      (MAL-map\n         (make\n            (for (L (MAL-value Map) L (cddr L))\n               (unless (find '((X) (MAL-= (car L) X)) Args)\n                  (link (car L) (cadr L)) ) ) ) ) ) )\n\n(de MAL-seq (X)\n   (if (or (= (MAL-type X) 'nil) (not (MAL-value X)))\n      *MAL-nil\n      (case (MAL-type X)\n         (list X)\n         (vector (MAL-list (MAL-value X)))\n         (string (MAL-list (mapcar MAL-string (chop (MAL-value X))))) ) ) )\n\n(de MAL-conj @\n   (let (Seq (next) Args (rest))\n      (if (= (MAL-type Seq) 'vector)\n         (MAL-vector (append (MAL-value Seq) Args))\n         (MAL-list (append (reverse Args) (MAL-value Seq))) ) ) )\n\n(de clone (X)\n   (let X* (new (val X))\n      (maps '((C) (put X* (cdr C) (car C))) X)\n      X* ) )\n\n(de pil-to-mal (X)\n   (cond\n      ((not X) *MAL-nil)\n      ((=T X) *MAL-true)\n      ((num? X) (MAL-number X))\n      ((str? X) (MAL-string X))\n      ((sym? X) (MAL-symbol X))\n      ((lst? X) (MAL-list (mapcar pil-to-mal X)))\n      (T (MAL-string (sym X))) ) )\n\n(def '*Ns\n   '((+ . `(MAL-fn '((A B) (MAL-number (+ (MAL-value A) (MAL-value B))))))\n     (- . `(MAL-fn '((A B) (MAL-number (- (MAL-value A) (MAL-value B))))))\n     (* . `(MAL-fn '((A B) (MAL-number (* (MAL-value A) (MAL-value B))))))\n     (/ . `(MAL-fn '((A B) (MAL-number (/ (MAL-value A) (MAL-value B))))))\n\n     (< . `(MAL-fn '((A B) (if (< (MAL-value A) (MAL-value B)) *MAL-true *MAL-false))))\n     (<= . `(MAL-fn '((A B) (if (<= (MAL-value A) (MAL-value B)) *MAL-true *MAL-false))))\n     (> . `(MAL-fn '((A B) (if (> (MAL-value A) (MAL-value B)) *MAL-true *MAL-false))))\n     (>= . `(MAL-fn '((A B) (if (>= (MAL-value A) (MAL-value B)) *MAL-true *MAL-false))))\n\n     (= . `(MAL-fn '((A B) (if (MAL-= A B) *MAL-true *MAL-false))))\n\n     (list . `(MAL-fn '(@ (MAL-list (rest)))))\n     (list? . `(MAL-fn '((X) (if (= (MAL-type X) 'list) *MAL-true *MAL-false))))\n     (empty? . `(MAL-fn '((X) (if (and (MAL-seq? X) (not (MAL-value X))) *MAL-true *MAL-false))))\n     (count . `(MAL-fn '((X) (if (MAL-seq? X) (MAL-number (length (MAL-value X))) (MAL-number 0)))))\n\n     (pr-str . `(MAL-fn '(@ (MAL-string (glue \" \" (mapcar '((X) (pr-str X T)) (rest)))))))\n     (str . `(MAL-fn '(@ (MAL-string (pack (mapcar pr-str (rest)))))))\n     (prn . `(MAL-fn '(@ (prinl (glue \" \" (mapcar '((X) (pr-str X T)) (rest)))) *MAL-nil)))\n     (println . `(MAL-fn '(@ (prinl (glue \" \" (mapcar pr-str (rest)))) *MAL-nil)))\n\n     (read-string . `(MAL-fn '((X) (read-str (MAL-value X)))))\n     (slurp . `(MAL-fn '((X) (MAL-string (in (MAL-value X) (till NIL T))))))\n\n     (atom . `(MAL-fn '((X) (MAL-atom X))))\n     (atom? . `(MAL-fn '((X) (if (= (MAL-type X) 'atom) *MAL-true *MAL-false))))\n     (deref . `(MAL-fn '((X) (MAL-value X))))\n     (reset! . `(MAL-fn '((X Value) (put X 'value Value))))\n     (swap! . `(MAL-fn MAL-swap!))\n\n     (cons . `(MAL-fn '((X Seq) (MAL-list (cons X (MAL-value Seq))))))\n     (concat . `(MAL-fn '(@ (MAL-list (apply append (mapcar MAL-value (rest)))))))\n     (vec . `(MAL-fn '((Seq) (MAL-vector (MAL-value Seq)))))\n\n     (nth . `(MAL-fn MAL-nth))\n     (first . `(MAL-fn '((X) (if (MAL-seq? X) (or (car (MAL-value X)) *MAL-nil) *MAL-nil))))\n     (rest . `(MAL-fn '((X) (if (MAL-seq? X) (MAL-list (cdr (MAL-value X))) (MAL-list NIL)))))\n\n     (throw . `(MAL-fn '((X) (throw 'err (MAL-error X)))))\n\n     (apply . `(MAL-fn '(@ (let (Fn (next) X (rest)) (apply (MAL-f Fn) (append (head -1 X) (MAL-value (last X))))))))\n     (map . `(MAL-fn '((Fn Seq) (MAL-list (mapcar (MAL-f Fn) (MAL-value Seq))))))\n\n     (nil? . `(MAL-fn '((X) (if (= (MAL-type X) 'nil) *MAL-true *MAL-false))))\n     (true? . `(MAL-fn '((X) (if (= (MAL-type X) 'true) *MAL-true *MAL-false))))\n     (false? . `(MAL-fn '((X) (if (= (MAL-type X) 'false) *MAL-true *MAL-false))))\n     (number? . `(MAL-fn '((X) (if (= (MAL-type X) 'number) *MAL-true *MAL-false))))\n     (symbol? . `(MAL-fn '((X) (if (= (MAL-type X) 'symbol) *MAL-true *MAL-false))))\n     (keyword? . `(MAL-fn '((X) (if (= (MAL-type X) 'keyword) *MAL-true *MAL-false))))\n     (string? . `(MAL-fn '((X) (if (= (MAL-type X) 'string) *MAL-true *MAL-false))))\n     (vector? . `(MAL-fn '((X) (if (= (MAL-type X) 'vector) *MAL-true *MAL-false))))\n     (map? . `(MAL-fn '((X) (if (= (MAL-type X) 'map) *MAL-true *MAL-false))))\n     (sequential? . `(MAL-fn '((X) (if (MAL-seq? X) *MAL-true *MAL-false))))\n     (fn? . `(MAL-fn '((X) (if (or (= (MAL-type X) 'fn) (and (= (MAL-type X) 'func) (not (get X 'is-macro)))) *MAL-true *MAL-false))))\n     (macro? . `(MAL-fn '((X) (if (and (= (MAL-type X) 'func) (get X 'is-macro)) *MAL-true *MAL-false))))\n\n     (symbol . `(MAL-fn '((Name) (MAL-symbol (MAL-value Name)))))\n     (keyword . `(MAL-fn '((X) (if (= (MAL-type X) 'keyword) X (MAL-keyword (MAL-value X))))))\n     (vector . `(MAL-fn '(@ (MAL-vector (rest)))))\n     (hash-map . `(MAL-fn '(@ (MAL-map (rest)))))\n\n     (assoc . `(MAL-fn MAL-assoc))\n     (dissoc . `(MAL-fn MAL-dissoc))\n     (get . `(MAL-fn '((Map Key) (or (and (<> (MAL-type Map) 'nil) (cdr (find '((X) (MAL-= (car X) Key)) (chunk (MAL-value Map))))) *MAL-nil))))\n     (contains? . `(MAL-fn '((Map Key) (if (find '((X) (MAL-= (car X) Key)) (chunk (MAL-value Map))) *MAL-true *MAL-false))))\n     (keys . `(MAL-fn '((Map) (MAL-list (mapcar car (chunk (MAL-value Map)))))))\n     (vals . `(MAL-fn '((Map) (MAL-list (extract cdr (chunk (MAL-value Map)))))))\n\n     (with-meta . `(MAL-fn '((X Meta) (let X* (clone X) (put X* 'meta Meta) X*))))\n     (meta . `(MAL-fn '((X) (or (MAL-meta X) *MAL-nil))))\n\n     (readline . `(MAL-fn '((Prompt) (let Output (readline (MAL-value Prompt)) (if (=0 Output) NIL (MAL-string Output))))))\n     (time-ms . `(MAL-fn '(() (MAL-number (/ (usec) 1000)))))\n     (conj . `(MAL-fn MAL-conj))\n     (seq . `(MAL-fn MAL-seq))\n\n     (pil-eval . `(MAL-fn '((Input) (pil-to-mal (run (str (MAL-value Input))))))) ) )\n"
  },
  {
    "path": "impls/picolisp/env.l",
    "content": "(class +Env)\n# data outer\n(dm T (Outer Binds Exprs)\n   (=: data (new))\n   (=: outer Outer)\n   (for (Binds Binds Binds)\n      (if (<> (car Binds) '&)\n         (set> This (pop 'Binds) (pop 'Exprs))\n         (pop 'Binds)\n         (set> This (pop 'Binds) (MAL-list Exprs)) ) ) )\n\n(de MAL-env (Outer Binds Exprs)\n   (new '(+Env) Outer Binds Exprs) )\n\n(dm set> (Key Value)\n   (put (: data) Key Value) )\n\n(dm get> (Key)\n   (or (get (: data) Key)\n      (and (: outer) (get> @ Key)) ) )\n"
  },
  {
    "path": "impls/picolisp/func.l",
    "content": "(class +Func)\n# env ast params fn\n(dm T (Env Ast Params Fn)\n   (=: type 'func) # HACK\n   (=: env Env)\n   (=: ast Ast)\n   (=: params Params)\n   (=: fn Fn) )\n\n(de MAL-func (Env Ast Params Fn)\n   (new '(+Func) Env Ast Params Fn) )\n\n(de MAL-macro (MalFn)\n   (let (env    (get MalFn 'env)\n         ast    (get MalFn 'ast)\n         params (get MalFn 'params)\n         fn     (get MalFn 'fn)\n         clone  (MAL-func env ast params fn))\n      (put clone 'is-macro T)\n      clone))\n"
  },
  {
    "path": "impls/picolisp/printer.l",
    "content": "(de pr-str (Ast PrintReadably)\n   (let Value (MAL-value Ast)\n      (case (MAL-type Ast)\n         ((true false nil)\n          (sym @) )\n         (string (if PrintReadably (repr Value) Value))\n         (keyword (pack \":\" Value))\n         ((number symbol) Value)\n         (fn \"#<subr>\")\n         (func \"#<func>\")\n         (list (pr-list Value PrintReadably \"(\" \")\"))\n         (vector (pr-list Value PrintReadably \"[\" \"]\"))\n         (map (pr-list Value PrintReadably \"{\" \"}\"))\n         (atom (pack \"(atom \" (pr-str Value PrintReadably) \")\"))\n         (T (pretty Value) (throw 'err (MAL-error (MAL-string \"[pr-str] unimplemented type\")))) ) ) )\n\n(de repr (X)\n   (let Chars (chop X)\n      (if (not X)\n         \"\\\"\\\"\"\n         (setq Chars (replace Chars \"\\\\\" \"\\\\\\\\\"))\n         (setq Chars (replace Chars \"\\\"\" \"\\\\\\\"\"))\n         (setq Chars (replace Chars \"\\n\" \"\\\\n\"))\n         (pack \"\\\"\" Chars \"\\\"\") ) ) )\n\n(de pr-list (Forms PrintReadably Starter Ender)\n   (let Values (mapcar '((Form) (pr-str Form PrintReadably)) Forms)\n      (pack Starter (glue \" \" Values) Ender) ) )\n"
  },
  {
    "path": "impls/picolisp/reader.l",
    "content": "(class +Reader)\n# tokens\n(dm T (Tokens)\n   (=: tokens Tokens) )\n\n(dm next> ()\n   (pop (:: tokens)) )\n\n(dm peek> ()\n   (car (: tokens)) )\n\n(de read-str (String)\n   (let (Tokens (tokenizer String)\n         Reader (new '(+Reader) Tokens) )\n      (read-form Reader) ) )\n\n(de tokenizer (String)\n   # [\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\\s\\[\\]{}('\"`,;)]*)\n   (let (Special \" []{}()'\\\"`,;\" )\n      (make\n         (for (Chars (chop String) Chars)\n            (let Char (pop 'Chars)\n               (cond\n                  ((or (sp? Char) (= Char \",\"))\n                   # do nothing, whitespace\n                   )\n                  ((and (= Char \"~\") (= (car Chars) \"@\"))\n                   (link \"~@\")\n                   (pop 'Chars) ) # remove @ token\n                  ((index Char (chop \"[]{}()'`~^\\@\"))\n                   (link Char) )\n                  ((= Char \"\\\"\")\n                   (link\n                      (pack\n                         (make\n                            (link Char) # HACK\n                            (use Done\n                               (while (and Chars (not Done))\n                                  (let Char (pop 'Chars)\n                                     (cond\n                                        ((= Char \"\\\\\")\n                                         (if Chars\n                                            (let Char (pop 'Chars)\n                                               (if (= Char \"n\")\n                                                  (link \"\\n\")\n                                                  (link Char) ) )\n                                            (throw 'err (MAL-error (MAL-string \"expected '\\\"', got EOF\"))) ) )\n                                        ((<> Char \"\\\"\")\n                                         (link Char) )\n                                        ((= Char \"\\\"\")\n                                         (setq Done T) ) ) ) )\n                               (unless Done\n                                  (throw 'err (MAL-error (MAL-string \"expected '\\\"', got EOF\"))) ) ) ) ) ) )\n                  ((= Char \";\")\n                   (while (and Chars (<> Char \"\\n\"))\n                      (setq Char (pop 'Chars)) ) )\n                  ((and (not (index Char (chop Special))) (not (sp? Char)))\n                   (link\n                      (pack\n                         (make\n                            (link Char)\n                            (let Char (car Chars)\n                               (while (and Chars (not (index Char (chop Special))) (not (sp? Char)))\n                                  (link (pop 'Chars))\n                                  (setq Char (car Chars)) ) ) ) ) ) ) ) ) ) ) ) )\n\n(de read-form (Reader)\n   (case (peek> Reader)\n      (\"'\" (read-macro Reader 'quote))\n      (\"`\" (read-macro Reader 'quasiquote))\n      (\"~\" (read-macro Reader 'unquote))\n      (\"~@\" (read-macro Reader 'splice-unquote))\n      (\"@\" (read-macro Reader 'deref))\n      (\"\\^\" (read-meta Reader))\n      (\"(\" (read-list Reader 'list \")\"))\n      (\"[\" (read-list Reader 'vector \"]\"))\n      (\"{\" (read-list Reader 'map \"}\"))\n      (T (read-atom Reader)) ) )\n\n(de read-macro (Reader symbol)\n   (next> Reader) # pop reader macro token\n   (MAL-list (list (MAL-symbol symbol) (read-form Reader))) )\n\n(de read-meta (Reader)\n   (next> Reader) # pop reader macro token\n   (let Form (read-form Reader)\n      (MAL-list (list (MAL-symbol 'with-meta) (read-form Reader) Form) ) ) )\n\n(de read-list (Reader Type Ender)\n   (next> Reader) # pop list start\n   (new (list (case Type\n                 (list '+MALList)\n                 (vector '+MALVector)\n                 (map '+MALMap) ) )\n      (make\n         (use Done\n            (while (not Done)\n               (let Token (peek> Reader)\n                  (cond\n                     ((= Token Ender)\n                      (next> Reader) # pop list end\n                      (setq Done T) )\n                     ((not Token)\n                      (let Msg (pack \"expected '\" Ender \"', got EOF\")\n                         (throw 'err (MAL-error (MAL-string Msg))) ) )\n                     (T (link (read-form Reader))) ) ) ) ) ) ) )\n\n(de read-atom (Reader)\n   (let (Token (next> Reader)\n         Chars (chop Token))\n      (cond\n         ((= Token \"true\")\n          *MAL-true)\n         ((= Token \"false\")\n          *MAL-false)\n         ((= Token \"nil\")\n          *MAL-nil)\n         ((format Token)\n          (MAL-number @) )\n         ((= (car Chars) \"\\\"\")\n          (MAL-string (pack (cdr Chars))) )\n         ((= (car Chars) \":\")\n          (MAL-keyword (intern (pack (cdr Chars)))) )\n         ((not Token)\n          (throw 'err (MAL-error (MAL-string \"end of token stream\"))) )\n         (T (MAL-symbol (intern Token))) ) ) )\n"
  },
  {
    "path": "impls/picolisp/readline.l",
    "content": "(de load-history (File)\n   (when (info File)\n      (in File\n         (until (eof)\n            (native \"libreadline.so\" \"add_history\" NIL (line T)) ) ) ) )\n\n(de save-to-history (Input)\n   (when Input\n      (native \"libreadline.so\" \"add_history\" NIL Input)\n      (out \"+.mal_history\"\n         (prinl Input) ) ) )\n\n(de readline (Prompt)\n   (let Input (native \"libreadline.so\" \"readline\" 'N Prompt)\n      (if (=0 Input)\n         0\n         (prog1\n            (struct Input 'S)\n            (save-to-history @) ) ) ) )\n"
  },
  {
    "path": "impls/picolisp/run",
    "content": "#!/usr/bin/env bash\nexec pil $(dirname $0)/${STEP:-stepA_mal}.l - \"${@}\"\n"
  },
  {
    "path": "impls/picolisp/step0_repl.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n\n(de READ (String)\n   String)\n\n(de EVAL (Ast)\n   Ast)\n\n(de PRINT (Ast)\n   Ast)\n\n(de rep (String)\n   (PRINT (EVAL (READ String))) )\n\n(load-history \".mal_history\")\n\n(use Eof\n   (until Eof\n      (let Input (readline \"user> \")\n         (if (=0 Input)\n            (setq Eof T)\n            (prinl (rep Input)) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step1_read_print.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(de EVAL (Ast)\n   Ast)\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String))) )\n\n(load-history \".mal_history\")\n\n(use Eof\n   (until Eof\n      (let Input (readline \"user> \")\n         (if (=0 Input)\n            (setq Eof T)\n            (let Output (catch 'err (rep Input))\n                (if (isa '+MALError Output)\n                   (let Message (MAL-value Output)\n                      (unless (= (MAL-value Message) \"end of token stream\")\n                         (prinl \"[error] \" (pr-str Message)) ) )\n                   (prinl Output) ) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step2_eval.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv\n   '((+ . ((A B) (MAL-number (+ (MAL-value A) (MAL-value B)))))\n     (- . ((A B) (MAL-number (- (MAL-value A) (MAL-value B)))))\n     (* . ((A B) (MAL-number (* (MAL-value A) (MAL-value B)))))\n     (/ . ((A B) (MAL-number (/ (MAL-value A) (MAL-value B))))) ) )\n\n(de EVAL (Ast Env)\n   ;; (prinl \"EVAL: \" (pr-str Ast T))\n   (let Value (MAL-value Ast)\n      (case (MAL-type Ast)\n         (symbol\n            (if (assoc Value Env)\n               (cdr @)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Value \"' not found\")))) ) )\n         (list\n          (if Value\n            (let El (mapcar '((Form) (EVAL Form Env)) Value)\n                 (apply (car El) (cdr El)))\n            Ast))\n         (vector (MAL-vector (mapcar '((Form) (EVAL Form Env)) Value)))\n         (map (MAL-map (mapcar '((Form) (EVAL Form Env)) Value)))\n         (T Ast) ) ) )\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(load-history \".mal_history\")\n\n(use Eof\n   (until Eof\n      (let Input (readline \"user> \")\n         (if (=0 Input)\n            (setq Eof T)\n            (let Output (catch 'err (rep Input))\n                (if (isa '+MALError Output)\n                   (let Message (MAL-value Output)\n                      (unless (= (MAL-value Message) \"end of token stream\")\n                         (prinl \"[error] \" (pr-str Message)) ) )\n                   (prinl Output) ) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step3_env.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(set> *ReplEnv '+ '((A B) (MAL-number (+ (MAL-value A) (MAL-value B)))))\n(set> *ReplEnv '- '((A B) (MAL-number (- (MAL-value A) (MAL-value B)))))\n(set> *ReplEnv '* '((A B) (MAL-number (* (MAL-value A) (MAL-value B)))))\n(set> *ReplEnv '/ '((A B) (MAL-number (/ (MAL-value A) (MAL-value B)))))\n\n(de EVAL (Ast Env)\n   (when (and (get> Env 'DEBUG-EVAL)\n              (not (memq (MAL-type @) '(nil false))))\n      (prinl \"EVAL: \" (pr-str Ast T)))\n\n   (case (MAL-type Ast)\n      (list\n         (let (Ast* (MAL-value Ast)\n               A0* (MAL-value (car Ast*))\n               A1* (MAL-value (cadr Ast*))\n               A2 (caddr Ast*))\n            (cond\n               ((not Ast*)\n                Ast)\n               ((= A0* 'def!)\n                (set> Env A1* (EVAL A2 Env)) )\n               ((= A0* 'let*)\n                (let Env* (MAL-env Env)\n                   (for (Bindings A1* Bindings)\n                      (let (Key (MAL-value (pop 'Bindings))\n                            Value (EVAL (pop 'Bindings) Env*))\n                         (set> Env* Key Value) ) )\n                   (EVAL A2 Env*) ) )\n               (T (let Value (mapcar '((Form) (EVAL Form Env)) Ast*)\n                     (apply (car Value) (cdr Value)) ) ) ) ) )\n      (symbol\n       (let (Key (MAL-value Ast))\n          (or (get> Env Key)\n              (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n      (vector (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast))))\n      (map (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast))))\n      (T Ast)))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(load-history \".mal_history\")\n\n(use Eof\n   (until Eof\n      (let Input (readline \"user> \")\n         (if (=0 Input)\n            (setq Eof T)\n            (let Output (catch 'err (rep Input))\n                (if (isa '+MALError Output)\n                   (let Message (MAL-value Output)\n                      (unless (= (MAL-value Message) \"end of token stream\")\n                         (prinl \"[error] \" (pr-str Message)) ) )\n                   (prinl Output) ) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step4_if_fn_do.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de EVAL (Ast Env)\n   (when (and (get> Env 'DEBUG-EVAL)\n              (not (memq (MAL-type @) '(nil false))))\n      (prinl \"EVAL: \" (pr-str Ast T)))\n\n   (case (MAL-type Ast)\n      (list\n         (let (Ast* (MAL-value Ast)\n               A0* (MAL-value (car Ast*))\n               A1 (cadr Ast*)\n               A1* (MAL-value A1)\n               A2 (caddr Ast*)\n               A3 (cadddr Ast*) )\n            (cond\n               ((not Ast*)\n                Ast)\n               ((= A0* 'def!)\n                (set> Env A1* (EVAL A2 Env)) )\n               ((= A0* 'let*)\n                (let Env* (MAL-env Env)\n                   (for (Bindings A1* Bindings)\n                      (let (Key (MAL-value (pop 'Bindings))\n                            Value (EVAL (pop 'Bindings) Env*))\n                         (set> Env* Key Value) ) )\n                   (EVAL A2 Env*) ) )\n               ((= A0* 'do)\n                (for Form (cdr Ast*)\n                   (EVAL Form Env) ) )\n               ((= A0* 'if)\n                (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                   (EVAL A2 Env)\n                   (if A3\n                      (EVAL A3 Env)\n                      *MAL-nil ) ) )\n               ((= A0* 'fn*)\n                (let (Binds (mapcar MAL-value A1*)\n                      Body A2)\n                   (MAL-fn\n                      (curry (Env Binds Body) @\n                         (let Env* (MAL-env Env Binds (rest))\n                            (EVAL Body Env*) ) ) ) ) )\n               (T\n                  (let (Ast* (mapcar '((Form) (EVAL Form Env)) Ast*)\n                        Fn (MAL-value (car Ast*))\n                        Args (cdr Ast*))\n                     (apply Fn Args) ) ) ) ) )\n      (symbol\n       (let (Key (MAL-value Ast))\n          (or (get> Env Key)\n              (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n      (vector (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast))))\n      (map (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast))))\n      (T Ast)))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(load-history \".mal_history\")\n\n(use Input\n   (until (=0 (setq Input (readline \"user> \")))\n      (let Output (catch 'err (rep Input))\n         (if (isa '+MALError Output)\n            (let Message (MAL-value Output)\n               (unless (= (MAL-value Message) \"end of token stream\")\n                  (prinl \"[error] \" (pr-str Message)) ) )\n            (prinl Output) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step5_tco.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de EVAL (Ast Env)\n   (catch 'done\n      (while t\n       (when (and (get> Env 'DEBUG-EVAL)\n                  (not (memq (MAL-type @) '(nil false))))\n          (prinl \"EVAL: \" (pr-str Ast T)))\n\n       (case (MAL-type Ast)\n        (list\n            (let (Ast* (MAL-value Ast)\n                  A0* (MAL-value (car Ast*))\n                  A1 (cadr Ast*)\n                  A1* (MAL-value A1)\n                  A2 (caddr Ast*)\n                  A3 (cadddr Ast*) )\n               (cond\n                  ((not Ast*)\n                   (throw 'done Ast))\n                  ((= A0* 'def!)\n                   (throw 'done (set> Env A1* (EVAL A2 Env))) )\n                  ((= A0* 'let*)\n                   (let Env* (MAL-env Env)\n                      (for (Bindings A1* Bindings)\n                         (let (Key (MAL-value (pop 'Bindings))\n                               Value (EVAL (pop 'Bindings) Env*) )\n                            (set> Env* Key Value) ) )\n                      (setq Env Env* Ast A2) ) ) # TCO\n                  ((= A0* 'do)\n                   (mapc '((Form) (EVAL Form Env)) (head -1 (cdr Ast*)))\n                   (setq Ast (last Ast*)) ) # TCO\n                  ((= A0* 'if)\n                   (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                      (setq Ast A2) # TCO\n                      (if A3\n                         (setq Ast A3) # TCO\n                         (throw 'done *MAL-nil) ) ) )\n                  ((= A0* 'fn*)\n                   (let (Binds (mapcar MAL-value A1*)\n                         Body A2\n                         Fn (MAL-fn\n                               (curry (Env Binds Body) @\n                                  (let Env* (MAL-env Env Binds (rest))\n                                     (EVAL Body Env*) ) ) ) )\n                      (throw 'done (MAL-func Env Body Binds Fn)) ) )\n                  (T\n                     (let (Ast* (mapcar '((Form) (EVAL Form Env)) Ast*)\n                           Fn (car Ast*)\n                           Args (cdr Ast*) )\n                        (if (isa '+MALFn Fn)\n                           (throw 'done (apply (MAL-value Fn) Args))\n                           (let Env* (MAL-env (get Fn 'env) (get Fn 'params) Args)\n                              (setq Ast (get Fn 'ast) Env Env*) ) ) ) ) ) ) )\n        (symbol\n         (let (Key   (MAL-value Ast)\n               Value (get> Env Key))\n            (if Value\n               (throw 'done Value)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n        (vector (throw 'done\n          (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (map (throw 'done\n          (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (T (throw 'done Ast))))))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(load-history \".mal_history\")\n\n(use Input\n   (until (=0 (setq Input (readline \"user> \")))\n      (let Output (catch 'err (rep Input))\n         (if (isa '+MALError Output)\n            (let Message (MAL-value Output)\n               (unless (= (MAL-value Message) \"end of token stream\")\n                  (prinl \"[error] \" (pr-str Message)) ) )\n            (prinl Output) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step6_file.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de EVAL (Ast Env)\n   (catch 'done\n      (while t\n       (when (and (get> Env 'DEBUG-EVAL)\n                  (not (memq (MAL-type @) '(nil false))))\n          (prinl \"EVAL: \" (pr-str Ast T)))\n\n       (case (MAL-type Ast)\n        (list\n            (let (Ast* (MAL-value Ast)\n                  A0* (MAL-value (car Ast*))\n                  A1 (cadr Ast*)\n                  A1* (MAL-value A1)\n                  A2 (caddr Ast*)\n                  A3 (cadddr Ast*) )\n               (cond\n                  ((not Ast*)\n                   (throw 'done Ast))\n                  ((= A0* 'def!)\n                   (throw 'done (set> Env A1* (EVAL A2 Env))) )\n                  ((= A0* 'let*)\n                   (let Env* (MAL-env Env)\n                      (for (Bindings A1* Bindings)\n                         (let (Key (MAL-value (pop 'Bindings))\n                               Value (EVAL (pop 'Bindings) Env*) )\n                            (set> Env* Key Value) ) )\n                      (setq Env Env* Ast A2) ) ) # TCO\n                  ((= A0* 'do)\n                   (mapc '((Form) (EVAL Form Env)) (head -1 (cdr Ast*)))\n                   (setq Ast (last Ast*)) ) # TCO\n                  ((= A0* 'if)\n                   (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                      (setq Ast A2) # TCO\n                      (if A3\n                         (setq Ast A3) # TCO\n                         (throw 'done *MAL-nil) ) ) )\n                  ((= A0* 'fn*)\n                   (let (Binds (mapcar MAL-value A1*)\n                         Body A2\n                         Fn (MAL-fn\n                               (curry (Env Binds Body) @\n                                  (let Env* (MAL-env Env Binds (rest))\n                                     (EVAL Body Env*) ) ) ) )\n                      (throw 'done (MAL-func Env Body Binds Fn)) ) )\n                  (T\n                     (let (Ast* (mapcar '((Form) (EVAL Form Env)) Ast*)\n                           Fn (car Ast*)\n                           Args (cdr Ast*) )\n                        (if (isa '+MALFn Fn)\n                           (throw 'done (apply (MAL-value Fn) Args))\n                           (let Env* (MAL-env (get Fn 'env) (get Fn 'params) Args)\n                              (setq Ast (get Fn 'ast) Env Env*) ) ) ) ) ) ) )\n        (symbol\n         (let (Key   (MAL-value Ast)\n               Value (get> Env Key))\n            (if Value\n               (throw 'done Value)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n        (vector (throw 'done\n          (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (map (throw 'done\n          (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (T (throw 'done Ast))))))\n\n(set> *ReplEnv 'eval (MAL-fn (curry (*ReplEnv) (Form) (EVAL Form *ReplEnv))))\n(set> *ReplEnv '*ARGV* (MAL-list (mapcar MAL-string (cdr (argv)))))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(load-history \".mal_history\")\n\n(if (argv)\n   (rep (pack \"(load-file \\\"\" (car (argv)) \"\\\")\"))\n   (use Input\n      (until (=0 (setq Input (readline \"user> \")))\n         (let Output (catch 'err (rep Input))\n            (if (isa '+MALError Output)\n               (let Message (MAL-value Output)\n                  (unless (= (MAL-value Message) \"end of token stream\")\n                     (prinl \"[error] \" (pr-str Message)) ) )\n               (prinl Output) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step7_quote.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de starts-with (Ast Sym) ;; MAL list, symbol -> nil or second element of Ast\n   (let (L  (MAL-value Ast)\n         A0 (car L))\n      (and (= (MAL-type A0) 'symbol)\n           (= (MAL-value A0) Sym)\n           (cadr L))))\n\n(de quasiquote-loop (Xs) ;; list -> MAL list\n   (MAL-list\n      (when Xs\n         (let (Elt (car Xs)\n               Unq (when (= (MAL-type Elt) 'list)\n                      (starts-with Elt 'splice-unquote))\n               Acc (quasiquote-loop (cdr Xs)))\n            (if Unq\n               (list (MAL-symbol 'concat) Unq Acc)\n               (list (MAL-symbol 'cons) (quasiquote Elt) Acc))))))\n\n(de quasiquote (Ast)\n   (case (MAL-type Ast)\n      (list         (or (starts-with Ast 'unquote)\n                        (quasiquote-loop (MAL-value Ast))))\n      (vector       (MAL-list (list (MAL-symbol 'vec) (quasiquote-loop (MAL-value Ast)))))\n      ((map symbol) (MAL-list (list (MAL-symbol 'quote) Ast)))\n      (T            Ast)))\n\n(de EVAL (Ast Env)\n   (catch 'done\n      (while t\n       (when (and (get> Env 'DEBUG-EVAL)\n                  (not (memq (MAL-type @) '(nil false))))\n          (prinl \"EVAL: \" (pr-str Ast T)))\n\n       (case (MAL-type Ast)\n        (list\n            (let (Ast* (MAL-value Ast)\n                  A0* (MAL-value (car Ast*))\n                  A1 (cadr Ast*)\n                  A1* (MAL-value A1)\n                  A2 (caddr Ast*)\n                  A3 (cadddr Ast*) )\n               (cond\n                  ((not Ast*)\n                   (throw 'done Ast))\n                  ((= A0* 'def!)\n                   (throw 'done (set> Env A1* (EVAL A2 Env))) )\n                  ((= A0* 'quote)\n                   (throw 'done A1) )\n                  ((= A0* 'quasiquote)\n                   (setq Ast (quasiquote A1)) ) # TCO\n                  ((= A0* 'let*)\n                   (let Env* (MAL-env Env)\n                      (for (Bindings A1* Bindings)\n                         (let (Key (MAL-value (pop 'Bindings))\n                               Value (EVAL (pop 'Bindings) Env*) )\n                            (set> Env* Key Value) ) )\n                      (setq Env Env* Ast A2) ) ) # TCO\n                  ((= A0* 'do)\n                   (mapc '((Form) (EVAL Form Env)) (head -1 (cdr Ast*)))\n                   (setq Ast (last Ast*)) ) # TCO\n                  ((= A0* 'if)\n                   (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                      (setq Ast A2) # TCO\n                      (if A3\n                         (setq Ast A3) # TCO\n                         (throw 'done *MAL-nil) ) ) )\n                  ((= A0* 'fn*)\n                   (let (Binds (mapcar MAL-value A1*)\n                         Body A2\n                         Fn (MAL-fn\n                               (curry (Env Binds Body) @\n                                  (let Env* (MAL-env Env Binds (rest))\n                                     (EVAL Body Env*) ) ) ) )\n                      (throw 'done (MAL-func Env Body Binds Fn)) ) )\n                  (T\n                     (let (Ast* (mapcar '((Form) (EVAL Form Env)) Ast*)\n                           Fn (car Ast*)\n                           Args (cdr Ast*) )\n                        (if (isa '+MALFn Fn)\n                           (throw 'done (apply (MAL-value Fn) Args))\n                           (let Env* (MAL-env (get Fn 'env) (get Fn 'params) Args)\n                              (setq Ast (get Fn 'ast) Env Env*) ) ) ) ) ) ) )\n        (symbol\n         (let (Key   (MAL-value Ast)\n               Value (get> Env Key))\n            (if Value\n               (throw 'done Value)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n        (vector (throw 'done\n          (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (map (throw 'done\n          (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (T (throw 'done Ast))))))\n\n(set> *ReplEnv 'eval (MAL-fn (curry (*ReplEnv) (Form) (EVAL Form *ReplEnv))))\n(set> *ReplEnv '*ARGV* (MAL-list (mapcar MAL-string (cdr (argv)))))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(load-history \".mal_history\")\n\n(if (argv)\n   (rep (pack \"(load-file \\\"\" (car (argv)) \"\\\")\"))\n   (use Input\n      (until (=0 (setq Input (readline \"user> \")))\n         (let Output (catch 'err (rep Input))\n            (if (isa '+MALError Output)\n               (let Message (MAL-value Output)\n                  (unless (= (MAL-value Message) \"end of token stream\")\n                     (prinl \"[error] \" (pr-str Message)) ) )\n               (prinl Output) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step8_macros.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de starts-with (Ast Sym) ;; MAL list, symbol -> nil or second element of Ast\n   (let (L  (MAL-value Ast)\n         A0 (car L))\n      (and (= (MAL-type A0) 'symbol)\n           (= (MAL-value A0) Sym)\n           (cadr L))))\n\n(de quasiquote-loop (Xs) ;; list -> MAL list\n   (MAL-list\n      (when Xs\n         (let (Elt (car Xs)\n               Unq (when (= (MAL-type Elt) 'list)\n                      (starts-with Elt 'splice-unquote))\n               Acc (quasiquote-loop (cdr Xs)))\n            (if Unq\n               (list (MAL-symbol 'concat) Unq Acc)\n               (list (MAL-symbol 'cons) (quasiquote Elt) Acc))))))\n\n(de quasiquote (Ast)\n   (case (MAL-type Ast)\n      (list         (or (starts-with Ast 'unquote)\n                        (quasiquote-loop (MAL-value Ast))))\n      (vector       (MAL-list (list (MAL-symbol 'vec) (quasiquote-loop (MAL-value Ast)))))\n      ((map symbol) (MAL-list (list (MAL-symbol 'quote) Ast)))\n      (T            Ast)))\n\n(de EVAL (Ast Env)\n   (catch 'done\n      (while t\n       (when (and (get> Env 'DEBUG-EVAL)\n                  (not (memq (MAL-type @) '(nil false))))\n          (prinl \"EVAL: \" (pr-str Ast T)))\n\n       (case (MAL-type Ast)\n        (list\n         (let (Ast* (MAL-value Ast)\n               A0* (MAL-value (car Ast*))\n               A1 (cadr Ast*)\n               A1* (MAL-value A1)\n               A2 (caddr Ast*)\n               A3 (cadddr Ast*) )\n            (cond\n               ((not Ast*)\n                (throw 'done Ast))\n               ((= A0* 'def!)\n                (throw 'done (set> Env A1* (EVAL A2 Env))) )\n               ((= A0* 'quote)\n                (throw 'done A1) )\n               ((= A0* 'quasiquote)\n                (setq Ast (quasiquote A1)) ) # TCO\n               ((= A0* 'defmacro!)\n                (throw 'done (set> Env A1* (MAL-macro (EVAL A2 Env)))))\n               ((= A0* 'let*)\n                (let Env* (MAL-env Env)\n                   (for (Bindings A1* Bindings)\n                      (let (Key (MAL-value (pop 'Bindings))\n                            Value (EVAL (pop 'Bindings) Env*) )\n                         (set> Env* Key Value) ) )\n                   (setq Env Env* Ast A2) ) ) # TCO\n               ((= A0* 'do)\n                (mapc '((Form) (EVAL Form Env)) (head -1 (cdr Ast*)))\n                (setq Ast (last Ast*)) ) # TCO\n               ((= A0* 'if)\n                (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                   (setq Ast A2) # TCO\n                   (if A3\n                      (setq Ast A3) # TCO\n                      (throw 'done *MAL-nil) ) ) )\n               ((= A0* 'fn*)\n                (let (Binds (mapcar MAL-value A1*)\n                      Body A2\n                      Fn (MAL-fn\n                            (curry (Env Binds Body) @\n                               (let Env* (MAL-env Env Binds (rest))\n                                  (EVAL Body Env*) ) ) ) )\n                   (throw 'done (MAL-func Env Body Binds Fn)) ) )\n               (T\n                (let (Fn (EVAL (car Ast*) Env))\n                  (if (get Fn 'is-macro)\n                    (setq Ast (apply (MAL-value (get Fn 'fn)) (cdr Ast*))) # TCO\n                    (let Args (mapcar '((Form) (EVAL Form Env)) (cdr Ast*))\n                     (if (isa '+MALFn Fn)\n                        (throw 'done (apply (MAL-value Fn) Args))\n                        (let Env* (MAL-env (get Fn 'env) (get Fn 'params) Args)\n                           (setq Ast (get Fn 'ast) Env Env*) ) ) ) ) ) ) ) ) )\n        (symbol\n         (let (Key   (MAL-value Ast)\n               Value (get> Env Key))\n            (if Value\n               (throw 'done Value)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n        (vector (throw 'done\n          (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (map (throw 'done\n          (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (T (throw 'done Ast))))))\n\n(set> *ReplEnv 'eval (MAL-fn (curry (*ReplEnv) (Form) (EVAL Form *ReplEnv))))\n(set> *ReplEnv '*ARGV* (MAL-list (mapcar MAL-string (cdr (argv)))))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\n(load-history \".mal_history\")\n\n(if (argv)\n   (rep (pack \"(load-file \\\"\" (car (argv)) \"\\\")\"))\n   (use Input\n      (until (=0 (setq Input (readline \"user> \")))\n         (let Output (catch 'err (rep Input))\n            (if (isa '+MALError Output)\n               (let Message (MAL-value Output)\n                  (unless (= (MAL-value Message) \"end of token stream\")\n                     (prinl \"[error] \" (pr-str Message)) ) )\n               (prinl Output) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/step9_try.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de starts-with (Ast Sym) ;; MAL list, symbol -> nil or second element of Ast\n   (let (L  (MAL-value Ast)\n         A0 (car L))\n      (and (= (MAL-type A0) 'symbol)\n           (= (MAL-value A0) Sym)\n           (cadr L))))\n\n(de quasiquote-loop (Xs) ;; list -> MAL list\n   (MAL-list\n      (when Xs\n         (let (Elt (car Xs)\n               Unq (when (= (MAL-type Elt) 'list)\n                      (starts-with Elt 'splice-unquote))\n               Acc (quasiquote-loop (cdr Xs)))\n            (if Unq\n               (list (MAL-symbol 'concat) Unq Acc)\n               (list (MAL-symbol 'cons) (quasiquote Elt) Acc))))))\n\n(de quasiquote (Ast)\n   (case (MAL-type Ast)\n      (list         (or (starts-with Ast 'unquote)\n                        (quasiquote-loop (MAL-value Ast))))\n      (vector       (MAL-list (list (MAL-symbol 'vec) (quasiquote-loop (MAL-value Ast)))))\n      ((map symbol) (MAL-list (list (MAL-symbol 'quote) Ast)))\n      (T            Ast)))\n\n(de EVAL (Ast Env)\n   (catch 'done\n      (while t\n       (when (and (get> Env 'DEBUG-EVAL)\n                  (not (memq (MAL-type @) '(nil false))))\n          (prinl \"EVAL: \" (pr-str Ast T)))\n\n       (case (MAL-type Ast)\n        (list\n         (let (Ast* (MAL-value Ast)\n               A0* (MAL-value (car Ast*))\n               A1 (cadr Ast*)\n               A1* (MAL-value A1)\n               A2 (caddr Ast*)\n               A3 (cadddr Ast*) )\n            (cond\n               ((not Ast*)\n                (throw 'done Ast))\n               ((= A0* 'def!)\n                (throw 'done (set> Env A1* (EVAL A2 Env))) )\n               ((= A0* 'quote)\n                (throw 'done A1) )\n               ((= A0* 'quasiquote)\n                (setq Ast (quasiquote A1)) ) # TCO\n               ((= A0* 'defmacro!)\n                (throw 'done (set> Env A1* (MAL-macro (EVAL A2 Env)))))\n               ((= A0* 'try*)\n                (let Result (catch 'err (throw 'done (EVAL A1 Env)))\n                   (if (isa '+MALError Result)\n                      (let A (MAL-value A2)\n                         (if (and (= (MAL-type A2) 'list)\n                                (= (MAL-value (car A)) 'catch*) )\n                            (let (Bind (MAL-value (cadr A))\n                                  Exc (MAL-value Result)\n                                  Form (caddr A)\n                                  Env* (MAL-env Env (list Bind) (list Exc)) )\n                               (throw 'done (EVAL Form Env*)) )\n                            (throw 'err Result) ) )\n                      (throw 'done Result) ) ) )\n               ((= A0* 'let*)\n                (let Env* (MAL-env Env)\n                   (for (Bindings A1* Bindings)\n                      (let (Key (MAL-value (pop 'Bindings))\n                            Value (EVAL (pop 'Bindings) Env*) )\n                         (set> Env* Key Value) ) )\n                   (setq Env Env* Ast A2) ) ) # TCO\n               ((= A0* 'do)\n                (mapc '((Form) (EVAL Form Env)) (head -1 (cdr Ast*)))\n                (setq Ast (last Ast*)) ) # TCO\n               ((= A0* 'if)\n                (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                   (setq Ast A2) # TCO\n                   (if A3\n                      (setq Ast A3) # TCO\n                      (throw 'done *MAL-nil) ) ) )\n               ((= A0* 'fn*)\n                (let (Binds (mapcar MAL-value A1*)\n                      Body A2\n                      Fn (MAL-fn\n                            (curry (Env Binds Body) @\n                               (let Env* (MAL-env Env Binds (rest))\n                                  (EVAL Body Env*) ) ) ) )\n                   (throw 'done (MAL-func Env Body Binds Fn)) ) )\n               (T\n                (let (Fn (EVAL (car Ast*) Env))\n                  (if (get Fn 'is-macro)\n                    (setq Ast (apply (MAL-value (get Fn 'fn)) (cdr Ast*))) # TCO\n                    (let Args (mapcar '((Form) (EVAL Form Env)) (cdr Ast*))\n                     (if (isa '+MALFn Fn)\n                        (throw 'done (apply (MAL-value Fn) Args))\n                        (let Env* (MAL-env (get Fn 'env) (get Fn 'params) Args)\n                           (setq Ast (get Fn 'ast) Env Env*) ) ) ) ) ) ) ) ) )\n        (symbol\n         (let (Key   (MAL-value Ast)\n               Value (get> Env Key))\n            (if Value\n               (throw 'done Value)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n        (vector (throw 'done\n          (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (map (throw 'done\n          (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (T (throw 'done Ast))))))\n\n(set> *ReplEnv 'eval (MAL-fn (curry (*ReplEnv) (Form) (EVAL Form *ReplEnv))))\n(set> *ReplEnv '*ARGV* (MAL-list (mapcar MAL-string (cdr (argv)))))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(load-history \".mal_history\")\n\n(if (argv)\n   (rep (pack \"(load-file \\\"\" (car (argv)) \"\\\")\"))\n   (use Input\n      (until (=0 (setq Input (readline \"user> \")))\n         (let Output (catch 'err (rep Input))\n            (if (isa '+MALError Output)\n               (let Message (MAL-value Output)\n                  (unless (= (MAL-value Message) \"end of token stream\")\n                     (prinl \"[error] \" (pr-str Message)) ) )\n               (prinl Output) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/stepA_mal.l",
    "content": "(de load-relative (Path)\n   (load (pack (car (file)) Path)) )\n\n(load-relative \"readline.l\")\n(load-relative \"types.l\")\n(load-relative \"reader.l\")\n(load-relative \"printer.l\")\n(load-relative \"env.l\")\n(load-relative \"func.l\")\n(load-relative \"core.l\")\n\n(de READ (String)\n   (read-str String) )\n\n(def '*ReplEnv (MAL-env NIL))\n(for Bind *Ns (set> *ReplEnv (car Bind) (cdr Bind)))\n\n(de starts-with (Ast Sym) ;; MAL list, symbol -> nil or second element of Ast\n   (let (L  (MAL-value Ast)\n         A0 (car L))\n      (and (= (MAL-type A0) 'symbol)\n           (= (MAL-value A0) Sym)\n           (cadr L))))\n\n(de quasiquote-loop (Xs) ;; list -> MAL list\n   (MAL-list\n      (when Xs\n         (let (Elt (car Xs)\n               Unq (when (= (MAL-type Elt) 'list)\n                      (starts-with Elt 'splice-unquote))\n               Acc (quasiquote-loop (cdr Xs)))\n            (if Unq\n               (list (MAL-symbol 'concat) Unq Acc)\n               (list (MAL-symbol 'cons) (quasiquote Elt) Acc))))))\n\n(de quasiquote (Ast)\n   (case (MAL-type Ast)\n      (list         (or (starts-with Ast 'unquote)\n                        (quasiquote-loop (MAL-value Ast))))\n      (vector       (MAL-list (list (MAL-symbol 'vec) (quasiquote-loop (MAL-value Ast)))))\n      ((map symbol) (MAL-list (list (MAL-symbol 'quote) Ast)))\n      (T            Ast)))\n\n(de EVAL (Ast Env)\n   (catch 'done\n      (while t\n       (when (and (get> Env 'DEBUG-EVAL)\n                  (not (memq (MAL-type @) '(nil false))))\n          (prinl \"EVAL: \" (pr-str Ast T)))\n\n       (case (MAL-type Ast)\n        (list\n         (let (Ast* (MAL-value Ast)\n               A0* (MAL-value (car Ast*))\n               A1 (cadr Ast*)\n               A1* (MAL-value A1)\n               A2 (caddr Ast*)\n               A3 (cadddr Ast*) )\n            (cond\n               ((not Ast*)\n                (throw 'done Ast))\n               ((= A0* 'def!)\n                (throw 'done (set> Env A1* (EVAL A2 Env))) )\n               ((= A0* 'quote)\n                (throw 'done A1) )\n               ((= A0* 'quasiquote)\n                (setq Ast (quasiquote A1)) ) # TCO\n               ((= A0* 'defmacro!)\n                (throw 'done (set> Env A1* (MAL-macro (EVAL A2 Env)))))\n               ((= A0* 'try*)\n                (let Result (catch 'err (throw 'done (EVAL A1 Env)))\n                   (if (isa '+MALError Result)\n                      (let A (MAL-value A2)\n                         (if (and (= (MAL-type A2) 'list)\n                                (= (MAL-value (car A)) 'catch*) )\n                            (let (Bind (MAL-value (cadr A))\n                                  Exc (MAL-value Result)\n                                  Form (caddr A)\n                                  Env* (MAL-env Env (list Bind) (list Exc)) )\n                               (throw 'done (EVAL Form Env*)) )\n                            (throw 'err Result) ) )\n                      (throw 'done Result) ) ) )\n               ((= A0* 'let*)\n                (let Env* (MAL-env Env)\n                   (for (Bindings A1* Bindings)\n                      (let (Key (MAL-value (pop 'Bindings))\n                            Value (EVAL (pop 'Bindings) Env*) )\n                         (set> Env* Key Value) ) )\n                   (setq Env Env* Ast A2) ) ) # TCO\n               ((= A0* 'do)\n                (mapc '((Form) (EVAL Form Env)) (head -1 (cdr Ast*)))\n                (setq Ast (last Ast*)) ) # TCO\n               ((= A0* 'if)\n                (if (not (memq (MAL-type (EVAL A1 Env)) '(nil false)))\n                   (setq Ast A2) # TCO\n                   (if A3\n                      (setq Ast A3) # TCO\n                      (throw 'done *MAL-nil) ) ) )\n               ((= A0* 'fn*)\n                (let (Binds (mapcar MAL-value A1*)\n                      Body A2\n                      Fn (MAL-fn\n                            (curry (Env Binds Body) @\n                               (let Env* (MAL-env Env Binds (rest))\n                                  (EVAL Body Env*) ) ) ) )\n                   (throw 'done (MAL-func Env Body Binds Fn)) ) )\n               (T\n                (let (Fn (EVAL (car Ast*) Env))\n                  (if (get Fn 'is-macro)\n                    (setq Ast (apply (MAL-value (get Fn 'fn)) (cdr Ast*))) # TCO\n                    (let Args (mapcar '((Form) (EVAL Form Env)) (cdr Ast*))\n                     (if (isa '+MALFn Fn)\n                        (throw 'done (apply (MAL-value Fn) Args))\n                        (let Env* (MAL-env (get Fn 'env) (get Fn 'params) Args)\n                           (setq Ast (get Fn 'ast) Env Env*) ) ) ) ) ) ) ) ) )\n        (symbol\n         (let (Key   (MAL-value Ast)\n               Value (get> Env Key))\n            (if Value\n               (throw 'done Value)\n               (throw 'err (MAL-error (MAL-string (pack \"'\" Key \"' not found\")))))))\n        (vector (throw 'done\n          (MAL-vector (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (map (throw 'done\n          (MAL-map (mapcar '((Form) (EVAL Form Env)) (MAL-value Ast)))))\n        (T (throw 'done Ast))))))\n\n(set> *ReplEnv 'eval (MAL-fn (curry (*ReplEnv) (Form) (EVAL Form *ReplEnv))))\n(set> *ReplEnv '*ARGV* (MAL-list (mapcar MAL-string (cdr (argv)))))\n(set> *ReplEnv '*host-language* (MAL-string \"pil\"))\n\n(de PRINT (Ast)\n   (pr-str Ast T) )\n\n(de rep (String)\n   (PRINT (EVAL (READ String) *ReplEnv)) )\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(load-history \".mal_history\")\n\n(if (argv)\n   (rep (pack \"(load-file \\\"\" (car (argv)) \"\\\")\"))\n   (use Input\n      (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n      (until (=0 (setq Input (readline \"user> \")))\n         (let Output (catch 'err (rep Input))\n            (if (isa '+MALError Output)\n               (let Message (MAL-value Output)\n                  (unless (= (MAL-value Message) \"end of token stream\")\n                     (prinl \"[error] \" (pr-str Message)) ) )\n               (prinl Output) ) ) ) ) )\n\n(prinl)\n(bye)\n"
  },
  {
    "path": "impls/picolisp/tests/step5_tco.mal",
    "content": ";; PIL: skipping non-TCO recursion\n;; Reason: segfault (unrecoverable)\n"
  },
  {
    "path": "impls/picolisp/tests/stepA_mal.mal",
    "content": ";; Testing basic pil interop\n\n(pil-eval \"T\")\n;=>true\n\n(pil-eval \"NIL\")\n;=>nil\n\n(pil-eval \"(+ 1 1)\")\n;=>2\n\n(pil-eval \"(cons 1 2 3 NIL)\")\n;=>(1 2 3)\n\n(pil-eval \"(use (@A @O) (match '(@A and @O) '(Alpha and Omega)) (prinl @A) (prinl @O))\")\nAlpha\nOmega\n"
  },
  {
    "path": "impls/picolisp/types.l",
    "content": "(class +MAL)\n# type value meta\n(dm T (Type Value Meta)\n   (=: type Type)\n   (=: value Value)\n   (=: meta Meta) )\n\n(de MAL-type (MAL)\n   (get MAL 'type) )\n\n(de MAL-value (MAL)\n   (get MAL 'value) )\n\n(de MAL-meta (MAL)\n   (get MAL 'meta) )\n\n(class +MALTrue +MAL)\n(dm T ()\n   (super 'true 'true NIL) )\n\n(class +MALFalse +MAL)\n(dm T ()\n   (super 'false 'false NIL) )\n\n(class +MALNil +MAL)\n(dm T ()\n   (super 'nil 'nil NIL) )\n\n(def '*MAL-true (new '(+MALTrue)))\n(def '*MAL-false (new '(+MALFalse)))\n(def '*MAL-nil (new '(+MALNil)))\n\n(class +MALNumber +MAL)\n(dm T (Number)\n   (super 'number Number NIL) )\n\n(de MAL-number (N)\n   (new '(+MALNumber) N) )\n\n(class +MALString +MAL)\n(dm T (String)\n   (super 'string String NIL) )\n\n(de MAL-string (N)\n   (new '(+MALString) N) )\n\n(class +MALSymbol +MAL)\n(dm T (String)\n   (super 'symbol String NIL) )\n\n(de MAL-symbol (N)\n   (new '(+MALSymbol) N) )\n\n(class +MALKeyword +MAL)\n(dm T (String)\n   (super 'keyword String NIL) )\n\n(de MAL-keyword (N)\n   (new '(+MALKeyword) N) )\n\n(class +MALList +MAL)\n(dm T (Values)\n   (super 'list Values NIL) )\n\n(de MAL-list (N)\n   (new '(+MALList) N) )\n\n(class +MALVector +MAL)\n(dm T (Values)\n   (super 'vector Values NIL) )\n\n(de MAL-vector (N)\n   (new '(+MALVector) N) )\n\n(class +MALMap +MAL)\n(dm T (Values)\n   (super 'map Values NIL) )\n\n(de MAL-map (N)\n   (new '(+MALMap) N) )\n\n(class +MALAtom +MAL)\n(dm T (Value)\n   (super 'atom Value NIL) )\n\n(de MAL-atom (N)\n   (new '(+MALAtom) N) )\n\n(class +MALFn +MAL)\n(dm T (Fn)\n   (super 'fn Fn NIL) )\n\n(de MAL-fn (Fn)\n   (new '(+MALFn) Fn) )\n\n(class +MALError +MAL)\n(dm T (Value)\n   (super 'error Value NIL) )\n\n(de MAL-error (Value)\n   (new '(+MALError) Value) )\n"
  },
  {
    "path": "impls/pike/Core.pmod",
    "content": "import .Interop;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nprivate Val apply(mixed f, Val ... args)\n{\n  if(sizeof(args) == 1) return f(@args[0].data);\n  array(Val) mid_args = args[0..(sizeof(args) - 2)];\n  return f(@(mid_args + args[-1].data));\n}\n\nprivate Val swap_bang(Val atom, mixed f, Val ... args)\n{\n  atom.data = f(@(({ atom.data }) + args));\n  return atom.data;\n}\n\nprivate mapping(string:function) builtins = ([\n  \"=\":     lambda(Val a, Val b) { return to_bool(a == b); },\n  \"throw\": lambda(Val a) { throw(a); },\n\n  \"nil?\":     lambda(Val a) { return to_bool(a.mal_type == MALTYPE_NIL); },\n  \"true?\":    lambda(Val a) { return to_bool(a.mal_type == MALTYPE_TRUE); },\n  \"false?\":   lambda(Val a) { return to_bool(a.mal_type == MALTYPE_FALSE); },\n  \"string?\":  lambda(Val a) { return to_bool(a.mal_type == MALTYPE_STRING); },\n  \"symbol\":   lambda(Val a) { return a.mal_type == MALTYPE_SYMBOL ? a : Symbol(a.value); },\n  \"symbol?\":  lambda(Val a) { return to_bool(a.mal_type == MALTYPE_SYMBOL); },\n  \"keyword\":  lambda(Val a) { return a.mal_type == MALTYPE_KEYWORD ? a : Keyword(a.value); },\n  \"keyword?\": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_KEYWORD); },\n  \"number?\":  lambda(Val a) { return to_bool(a.mal_type == MALTYPE_NUMBER); },\n  \"fn?\":      lambda(Val a) { return to_bool(a.is_fn && !a.macro); },\n  \"macro?\":   lambda(Val a) { return to_bool(a.macro); },\n\n  \"pr-str\":      lambda(Val ... a) { return String(map(a, lambda(Val e) { return pr_str(e, true); }) * \" \"); },\n  \"str\":         lambda(Val ... a) { return String(map(a, lambda(Val e) { return pr_str(e, false); }) * \"\"); },\n  \"prn\":         lambda(Val ... a) { write(({ map(a, lambda(Val e) { return pr_str(e, true); }) * \" \", \"\\n\" })); return MAL_NIL; },\n  \"println\":     lambda(Val ... a) { write(({ map(a, lambda(Val e) { return pr_str(e, false); }) * \" \", \"\\n\" })); return MAL_NIL; },\n  \"read-string\": lambda(Val a) { return read_str(a.value); },\n  \"readline\":    lambda(Val a) { string line = readline(a.value); return line ? String(line) : MAL_NIL; },\n  \"slurp\":       lambda(Val a) { return String(Stdio.read_file(a.value)); },\n\n  \"<\":  lambda(Val a, Val b) { return to_bool(a.value < b.value); },\n  \"<=\": lambda(Val a, Val b) { return to_bool(a.value <= b.value); },\n  \">\":  lambda(Val a, Val b) { return to_bool(a.value > b.value); },\n  \">=\": lambda(Val a, Val b) { return to_bool(a.value >= b.value); },\n  \"+\":  lambda(Val a, Val b) { return Number(a.value + b.value); },\n  \"-\":  lambda(Val a, Val b) { return Number(a.value - b.value); },\n  \"*\":  lambda(Val a, Val b) { return Number(a.value * b.value); },\n  \"/\":  lambda(Val a, Val b) { return Number(a.value / b.value); },\n  \"time-ms\": lambda() { array(int) t = System.gettimeofday(); return Number(t[0] * 1000 + t[1] / 1000); },\n\n  \"list\":      lambda(Val ... a) { return List(a); },\n  \"list?\":     lambda(Val a) { return to_bool(a.mal_type == MALTYPE_LIST); },\n  \"vector\":    lambda(Val ... a) { return Vector(a); },\n  \"vector?\":   lambda(Val a) { return to_bool(a.mal_type == MALTYPE_VECTOR); },\n  \"hash-map\":  lambda(Val ... a) { return Map(a); },\n  \"map?\":      lambda(Val a) { return to_bool(a.mal_type == MALTYPE_MAP); },\n  \"assoc\":     lambda(Val a, Val ... b) { return a.assoc(b); },\n  \"dissoc\":    lambda(Val a, Val ... b) { return a.dissoc(b); },\n  \"get\":       lambda(Val a, Val b) { return a.mal_type != MALTYPE_NIL ? (a.data[b] || MAL_NIL) : MAL_NIL; },\n  \"contains?\": lambda(Val a, Val b) { return to_bool(a.data[b]); },\n  \"keys\":      lambda(Val a) { return List(indices(a.data)); },\n  \"vals\":      lambda(Val a) { return List(values(a.data)); },\n\n  \"sequential?\": lambda(Val a) { return to_bool(a.is_sequence); },\n  \"cons\":        lambda(Val a, Val b) { return List(({ a }) + b.data); },\n  \"concat\":      lambda(Val ... a) { return List(`+(({ }), @map(a, lambda(Val e) { return e.data; }))); },\n  \"vec\":         lambda(Val a) { return Vector(a.data); },\n  \"nth\":         lambda(Val a, Val b) { return a.nth(b.value); },\n  \"first\":       lambda(Val a) { return a.first(); },\n  \"rest\":        lambda(Val a) { return a.rest(); },\n  \"empty?\":      lambda(Val a) { return to_bool(a.emptyp()); },\n  \"count\":       lambda(Val a) { return Number(a.count()); },\n  \"apply\":       apply,\n  \"map\":         lambda(mixed f, Val a) { return List(map(a.data, f)); },\n\n  \"conj\":      lambda(Val a, Val ... b) { return a.conj(b); },\n  \"seq\":       lambda(Val a) { return a.seq(); },\n\n  \"meta\":      lambda(Val a) { return a.meta || MAL_NIL; },\n  \"with-meta\": lambda(Val a, Val b) { Val new_a = a.clone(); new_a.meta = b; return new_a; },\n  \"atom\":      lambda(Val a) { return Atom(a); },\n  \"atom?\":     lambda(Val a) { return to_bool(a.mal_type == MALTYPE_ATOM); },\n  \"deref\":     lambda(Val a) { return a.data; },\n  \"reset!\":    lambda(Val a, Val b) { a.data = b; return a.data; },\n  \"swap!\":     swap_bang,\n\n  \"pike-eval\": lambda(Val a) { return pike_eval(a.value); },\n]);\n\nmapping(Val:Val) NS()\n{\n  mapping(Val:Val) ns = ([ ]);\n  foreach(builtins; string name; function f) { ns[Symbol(name)] = BuiltinFn(name, f); }\n  return ns;\n}\n"
  },
  {
    "path": "impls/pike/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install pike8.0\n"
  },
  {
    "path": "impls/pike/Env.pmod",
    "content": "import .Types;\n\nclass Env\n{\n  Env outer;\n  mapping(string:Val) data;\n\n  void create(Env the_outer, List|void binds, List|void exprs)\n  {\n    outer = the_outer;\n    data = ([ ]);\n    if(binds)\n    {\n      for(int i = 0; i < binds.count(); i++)\n      {\n        if(binds.data[i].value == \"&\")\n        {\n          set(binds.data[i + 1], List(exprs.data[i..]));\n          break;\n        }\n        set(binds.data[i], exprs.data[i]);\n      }\n    }\n  }\n\n  Val set(Val key, Val val)\n  {\n    data[key.value] = val;\n    return val;\n  }\n\n  Val get(string key)\n  {\n    Val res = data[key];\n    if(res) return res;\n    if(outer) return outer.get(key);\n    return 0;\n  }\n}\n"
  },
  {
    "path": "impls/pike/Interop.pmod",
    "content": "import .Types;\n\nVal pike_eval(string expr_str)\n{\n  program prog = compile_string(\"mixed tmp_func() { return (\" + expr_str + \"); }\", \"pike-eval\");\n  mixed v = prog()->tmp_func();\n  return pike2mal(v);\n}\n\nprivate Val pike2mal(mixed v)\n{\n  if(stringp(v)) return String(v);\n  if(intp(v)) return Number(v);\n  if(arrayp(v))\n  {\n    array(Val) res = ({ });\n    foreach(v, mixed e)\n    {\n      res += ({ pike2mal(e) });\n    }\n    return List(res);\n  }\n  if(mappingp(v))\n  {\n    array(Val) res = ({ });\n    foreach(v; mixed k; mixed v)\n    {\n      res += ({ pike2mal(k), pike2mal(v) });\n    }\n    return Map(res);\n  }\n  return MAL_NIL;\n}\n"
  },
  {
    "path": "impls/pike/Makefile",
    "content": "SOURCES_BASE = readline.pike types.pike reader.pike printer.pike\nSOURCES_LISP = env.pike core.pike stepA_mal.pike\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.pike mal\n\nmal.pike: $(SOURCES)\n\tcat $+ | grep -v \"^#include\" > $@\n\nmal: mal.pike\n\techo \"#!/usr/bin/env pike\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.pike mal\n"
  },
  {
    "path": "impls/pike/Printer.pmod",
    "content": "import .Types;\n\nstring pr_str(Val ast, bool print_readably)\n{\n  if(functionp(ast)) return \"#<Fn>\";\n  return ast->to_string(print_readably);\n}\n"
  },
  {
    "path": "impls/pike/Reader.pmod",
    "content": "import .Types;\n\nRegexp.PCRE tokenizer_regexp = Regexp.PCRE.Studied(\"[\\\\s ,]*(~@|[\\\\[\\\\]{}()'`~@]|\\\"([\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s \\\\[\\\\]{}()'\\\"`~@,;]*)\");\nRegexp.PCRE string_regexp = Regexp.PCRE.Studied(\"^\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"$\");\nRegexp.PCRE number_regexp = Regexp.PCRE.Studied(\"^-?[0-9]+$\");\n\nprivate class Reader(private array(string) tokens, private void|int position)\n{\n  string next()\n  {\n    if(position >= sizeof(tokens)) return 0;\n    string token = tokens[position];\n    position++;\n    return token;\n  }\n\n  string peek()\n  {\n    if(position >= sizeof(tokens)) return 0;\n    return tokens[position];\n  }\n}\n\nprivate array(string) tokenize(string str)\n{\n  array(string) tokens = ({ });\n  tokenizer_regexp.matchall(str, lambda(mixed m) {\n    if(sizeof(m[1]) > 0 && m[1][0] != ';') tokens += ({ m[1] });\n  });\n  return tokens;\n}\n\nprivate string unescape_string(string token)\n{\n  if(!string_regexp.match(token)) throw(\"expected '\\\"', got EOF\");\n  string s = token[1..(sizeof(token) - 2)];\n  s = replace(s, \"\\\\\\\\\", \"\\u029e\");\n  s = replace(s, \"\\\\\\\"\", \"\\\"\");\n  s = replace(s, \"\\\\n\", \"\\n\");\n  s = replace(s, \"\\u029e\", \"\\\\\");\n  return s;\n}\n\nprivate Val read_atom(Reader reader)\n{\n  string token = reader->next();\n  if(number_regexp.match(token)) return Number((int)token);\n  if(token[0] == '\"') return String(unescape_string(token));\n  if(token[0] == ':') return Keyword(token[1..]);\n  switch(token)\n  {\n    case \"nil\":   return MAL_NIL;\n    case \"true\":  return MAL_TRUE;\n    case \"false\": return MAL_FALSE;\n  }\n  return Symbol(token);\n}\n\nprivate array(Val) read_seq(Reader reader, string start, string end)\n{\n  string token = reader->next();\n  if(token != start) throw(\"expected '\" + start + \"'\");\n  token = reader->peek();\n  array(Val) elements = ({ });\n  while(token != end)\n  {\n    if(!token) throw(\"expected '\" + end + \"', got EOF\");\n    elements += ({ read_form(reader) });\n    token = reader->peek();\n  }\n  reader->next();\n  return elements;\n}\n\nprivate Val reader_macro(Reader reader, string symbol)\n{\n  reader->next();\n  return List(({ Symbol(symbol), read_form(reader) }));\n}\n\nprivate Val read_form(Reader reader)\n{\n  string token = reader->peek();\n  switch(token)\n  {\n    case \"'\":\n      return reader_macro(reader, \"quote\");\n    case \"`\":\n      return reader_macro(reader, \"quasiquote\");\n    case \"~\":\n      return reader_macro(reader, \"unquote\");\n    case \"~@\":\n      return reader_macro(reader, \"splice-unquote\");\n    case \"@\":\n      return reader_macro(reader, \"deref\");\n    case \"^\":\n      reader->next();\n      Val meta = read_form(reader);\n      return List(({ Symbol(\"with-meta\"), read_form(reader), meta }));\n    case \"(\":\n      return List(read_seq(reader, \"(\", \")\"));\n    case \")\":\n      throw(\"unexpected ')'\");\n    case \"[\":\n      return Vector(read_seq(reader, \"[\", \"]\"));\n    case \"]\":\n      throw(\"unexpected ']'\");\n    case \"{\":\n      return Map(read_seq(reader, \"{\", \"}\"));\n    case \"}\":\n      throw(\"unexpected '}'\");\n    default:\n      return read_atom(reader);\n  }\n}\n\nVal read_str(string str)\n{\n  array(string) tokens = tokenize(str);\n  if(sizeof(tokens) == 0) return MAL_NIL;\n  return read_form(Reader(tokens));\n}\n"
  },
  {
    "path": "impls/pike/Readline.pmod",
    "content": "string readline(string prompt) {\n  write(prompt);\n  return Stdio.stdin->gets();\n}\n"
  },
  {
    "path": "impls/pike/Types.pmod",
    "content": "enum MalType {\n  MALTYPE_UNDEFINED,\n  MALTYPE_NIL,\n  MALTYPE_TRUE,\n  MALTYPE_FALSE,\n  MALTYPE_NUMBER,\n  MALTYPE_SYMBOL,\n  MALTYPE_STRING,\n  MALTYPE_KEYWORD,\n  MALTYPE_LIST,\n  MALTYPE_VECTOR,\n  MALTYPE_MAP,\n  MALTYPE_FN,\n  MALTYPE_BUILTINFN,\n  MALTYPE_ATOM,\n};\n\nclass Val\n{\n  constant mal_type = MALTYPE_UNDEFINED;\n  Val meta;\n  string to_string(bool print_readably);\n  Val clone();\n\n  bool `==(mixed other)\n  {\n    return objectp(other) && other.mal_type == mal_type;\n  }\n}\n\nclass Nil\n{\n  inherit Val;\n  constant mal_type = MALTYPE_NIL;\n\n  string to_string(bool print_readably)\n  {\n    return \"nil\";\n  }\n\n  int count()\n  {\n    return 0;\n  }\n\n  Val first()\n  {\n    return MAL_NIL;\n  }\n\n  Val rest()\n  {\n    return List(({ }));\n  }\n\n  Val clone()\n  {\n    return this_object();\n  }\n\n  Val seq()\n  {\n    return MAL_NIL;\n  }\n}\n\nNil MAL_NIL = Nil();\n\nclass True\n{\n  inherit Val;\n  constant mal_type = MALTYPE_TRUE;\n  string to_string(bool print_readably)\n  {\n    return \"true\";\n  }\n\n  Val clone()\n  {\n    return this_object();\n  }\n}\n\nTrue MAL_TRUE = True();\n\nclass False\n{\n  inherit Val;\n  constant mal_type = MALTYPE_FALSE;\n  string to_string(bool print_readably)\n  {\n    return \"false\";\n  }\n\n  Val clone()\n  {\n    return this_object();\n  }\n}\n\nFalse MAL_FALSE = False();\n\nVal to_bool(bool b)\n{\n  if(b) return MAL_TRUE;\n  return MAL_FALSE;\n}\n\nclass Number(int value)\n{\n  constant mal_type = MALTYPE_NUMBER;\n  inherit Val;\n\n  string to_string(bool print_readably)\n  {\n    return (string)value;\n  }\n\n  bool `==(mixed other)\n  {\n    return ::`==(other) && other.value == value;\n  }\n\n  Val clone()\n  {\n    return this_object();\n  }\n}\n\nclass Symbol(string value)\n{\n  constant mal_type = MALTYPE_SYMBOL;\n  inherit Val;\n\n  string to_string(bool print_readably)\n  {\n    return value;\n  }\n\n  bool `==(mixed other)\n  {\n    return ::`==(other) && other.value == value;\n  }\n\n  int __hash()\n  {\n     return hash((string)mal_type) ^ hash(value);\n  }\n\n  Val clone()\n  {\n    return Symbol(value);\n  }\n}\n\nclass String(string value)\n{\n  constant mal_type = MALTYPE_STRING;\n  inherit Val;\n\n  string to_string(bool print_readably)\n  {\n    if(print_readably) {\n      string s = replace(value, \"\\\\\", \"\\\\\\\\\");\n      s = replace(s, \"\\\"\", \"\\\\\\\"\");\n      s = replace(s, \"\\n\", \"\\\\n\");\n      return \"\\\"\" + s + \"\\\"\";\n    }\n    return value;\n  }\n\n  bool `==(mixed other)\n  {\n    return ::`==(other) && other.value == value;\n  }\n\n  int __hash()\n  {\n     return hash((string)mal_type) ^ hash(value);\n  }\n\n  Val clone()\n  {\n    return String(value);\n  }\n\n  Val seq()\n  {\n    if(sizeof(value) == 0) return MAL_NIL;\n    array(Val) parts = ({ });\n    for(int i = 0; i < sizeof(value); i++)\n    {\n      parts += ({ String(value[i..i]) });\n    }\n    return List(parts);\n  }\n}\n\nclass Keyword(string value)\n{\n  constant mal_type = MALTYPE_KEYWORD;\n  inherit Val;\n\n  string to_string(bool print_readably)\n  {\n    return \":\" + value;\n  }\n\n  bool `==(mixed other)\n  {\n    return ::`==(other) && other.value == value;\n  }\n\n  int __hash()\n  {\n     return hash((string)mal_type) ^ hash(value);\n  }\n\n  Val clone()\n  {\n    return Keyword(value);\n  }\n}\n\nclass Sequence(array(Val) data)\n{\n  inherit Val;\n  constant is_sequence = true;\n\n  string to_string(bool print_readably)\n  {\n    return map(data, lambda(Val e) { return e.to_string(print_readably); }) * \" \";\n  }\n\n  bool emptyp()\n  {\n    return sizeof(data) == 0;\n  }\n\n  int count()\n  {\n    return sizeof(data);\n  }\n\n  Val nth(int index)\n  {\n    if(index >= count()) throw(\"nth: index out of range\");\n    return data[index];\n  }\n\n  Val first()\n  {\n    if(emptyp()) return MAL_NIL;\n    return data[0];\n  }\n\n  Val rest()\n  {\n    return List(data[1..]);\n  }\n\n  bool `==(mixed other)\n  {\n    if(!objectp(other)) return 0;\n    if(!other.is_sequence) return 0;\n    if(other.count() != count()) return 0;\n    for(int i = 0; i < count(); i++)\n    {\n      if(other.data[i] != data[i]) return 0;\n    }\n    return 1;\n  }\n\n  Val seq()\n  {\n    if(emptyp()) return MAL_NIL;\n    return List(data);\n  }\n}\n\nclass List\n{\n  inherit Sequence;\n  constant mal_type = MALTYPE_LIST;\n\n  string to_string(bool print_readably)\n  {\n    return \"(\" + ::to_string(print_readably) + \")\";\n  }\n\n  Val clone()\n  {\n    return List(data);\n  }\n\n  Val conj(array(Val) other)\n  {\n    return List(reverse(other) + data);\n  }\n}\n\nclass Vector\n{\n  inherit Sequence;\n  constant mal_type = MALTYPE_VECTOR;\n\n  string to_string(bool print_readably)\n  {\n    return \"[\" + ::to_string(print_readably) + \"]\";\n  }\n\n  Val clone()\n  {\n    return Vector(data);\n  }\n\n  Val conj(array(Val) other)\n  {\n    return Vector(data + other);\n  }\n}\n\nclass Map\n{\n  inherit Val;\n  constant mal_type = MALTYPE_MAP;\n  mapping(Val:Val) data;\n\n  void create(array(Val) list)\n  {\n    array(Val) keys = Array.everynth(list, 2, 0);\n    array(Val) vals = Array.everynth(list, 2, 1);\n    data = mkmapping(keys, vals);\n  }\n\n  string to_string(bool print_readably)\n  {\n    array(string) strs = ({ });\n    foreach(data; Val k; Val v)\n    {\n      strs += ({ k.to_string(print_readably), v.to_string(print_readably) });\n    }\n    return \"{\" + (strs * \" \") + \"}\";\n  }\n\n  int count()\n  {\n    return sizeof(data);\n  }\n\n  bool `==(mixed other)\n  {\n    if(!::`==(other)) return 0;\n    if(other.count() != count()) return 0;\n    foreach(data; Val k; Val v)\n    {\n      if(other.data[k] != v) return 0;\n    }\n    return 1;\n  }\n\n  Val assoc(array(Val) list)\n  {\n    array(Val) keys = Array.everynth(list, 2, 0);\n    array(Val) vals = Array.everynth(list, 2, 1);\n    Map result = Map(({ }));\n    result.data = copy_value(data);\n    for(int i = 0; i < sizeof(keys); i++)\n    {\n      result.data[keys[i]] = vals[i];\n    }\n    return result;\n  }\n\n  Val dissoc(array(Val) list)\n  {\n    Map result = Map(({ }));\n    result.data = copy_value(data);\n    foreach(list, Val key) m_delete(result.data, key);\n    return result;\n  }\n\n  Val clone()\n  {\n    Map m = Map(({ }));\n    m.data = data;\n    return m;\n  }\n}\n\nclass Fn(Val ast, Val params, .Env.Env env, function func, void|bool macro)\n{\n  inherit Val;\n  constant mal_type = MALTYPE_FN;\n  constant is_fn = true;\n\n  void set_macro()\n  {\n    macro = true;\n  }\n\n  string to_string(bool print_readably)\n  {\n    string tag = macro ? \"Macro\" : \"Fn\";\n    return \"#<\" + tag + \" params=\" + params.to_string(true) + \">\";\n  }\n\n  mixed `()(mixed ... args)\n  {\n    return func(@args);\n  }\n\n  Val clone()\n  {\n    return Fn(ast, params, env, func);\n  }\n\n  Val clone_as_macro()\n  {\n    return Fn(ast, params, env, func, true);\n  }\n}\n\nclass BuiltinFn(string name, function func)\n{\n  inherit Val;\n  constant mal_type = MALTYPE_BUILTINFN;\n  constant is_fn = true;\n\n  string to_string(bool print_readably)\n  {\n    return \"#<BuiltinFn \" + name + \">\";\n  }\n\n  mixed `()(mixed ... args)\n  {\n    return func(@args);\n  }\n\n  Val clone()\n  {\n    return BuiltinFn(name, func);\n  }\n}\n\nclass Atom(Val data)\n{\n  inherit Val;\n  constant mal_type = MALTYPE_ATOM;\n\n  string to_string(bool print_readably)\n  {\n    return \"(atom \" + data.to_string(print_readably) + \")\";\n  }\n\n  Val clone()\n  {\n    return Atom(data);\n  }\n}\n"
  },
  {
    "path": "impls/pike/run",
    "content": "#!/usr/bin/env bash\nexec pike $(dirname $0)/${STEP:-stepA_mal}.pike \"${@}\"\n"
  },
  {
    "path": "impls/pike/step0_repl.pike",
    "content": "import .Readline;\n\nstring READ(string str)\n{\n  return str;\n}\n\nstring EVAL(string ast, string env)\n{\n  return ast;\n}\n\nstring PRINT(string exp)\n{\n  return exp;\n}\n\nstring rep(string str)\n{\n  return PRINT(EVAL(READ(str), \"\"));\n}\n\nint main()\n{\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    write(({ rep(line), \"\\n\" }));\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step1_read_print.pike",
    "content": "import .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nVal EVAL(Val ast, string env)\n{\n  return ast;\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str)\n{\n  return PRINT(EVAL(READ(str), \"\"));\n}\n\nint main()\n{\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step2_eval.pike",
    "content": "import .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nVal EVAL(Val ast, mapping(string:function) env)\n{\n  // write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      function f = env[ast.value];\n      if(!f) throw(\"'\" + ast.value + \"' not found\");\n      return f;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n  if(ast.emptyp()) return ast;\n  Val f = EVAL(ast.data[0], env);\n  array(Val) args = ast.data[1..];\n  args = map(args, lambda(Val e) { return EVAL(e, env);});\n  return f(@args);\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, mapping(string:function) env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main()\n{\n  mapping(string:function) repl_env = ([\n    \"+\": lambda(Val a, Val b) { return Number(a.value + b.value); },\n    \"-\": lambda(Val a, Val b) { return Number(a.value - b.value); },\n    \"*\": lambda(Val a, Val b) { return Number(a.value * b.value); },\n    \"/\": lambda(Val a, Val b) { return Number(a.value / b.value); }\n  ]);\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step3_env.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nVal EVAL(Val ast, Env env)\n{\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n  if(ast.emptyp()) return ast;\n  if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n    switch(ast.data[0].value)\n    {\n      case \"def!\":\n        return env.set(ast.data[1], EVAL(ast.data[2], env));\n      case \"let*\":\n        Env let_env = Env(env);\n        Val ast1 = ast.data[1];\n        for(int i = 0; i < sizeof(ast1.data); i += 2)\n        {\n          let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n        }\n        return EVAL(ast.data[2], let_env);\n    }\n  }\n  Val f = EVAL(ast.data[0], env);\n  array(Val) args = ast.data[1..];\n  args = map(args, lambda(Val e) { return EVAL(e, env);});\n  return f(@args);\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main()\n{\n  Env repl_env = Env(0);\n  repl_env.set(Symbol(\"+\"), lambda(Val a, Val b) { return Number(a.value + b.value); });\n  repl_env.set(Symbol(\"-\"), lambda(Val a, Val b) { return Number(a.value - b.value); });\n  repl_env.set(Symbol(\"*\"), lambda(Val a, Val b) { return Number(a.value * b.value); });\n  repl_env.set(Symbol(\"/\"), lambda(Val a, Val b) { return Number(a.value / b.value); });\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step4_if_fn_do.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nVal EVAL(Val ast, Env env)\n{\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n  if(ast.emptyp()) return ast;\n  if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n    switch(ast.data[0].value)\n    {\n      case \"def!\":\n        return env.set(ast.data[1], EVAL(ast.data[2], env));\n      case \"let*\":\n        Env let_env = Env(env);\n        Val ast1 = ast.data[1];\n        for(int i = 0; i < sizeof(ast1.data); i += 2)\n        {\n          let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n        }\n        return EVAL(ast.data[2], let_env);\n      case \"do\":\n        Val result;\n        foreach(ast.data[1..], Val element)\n        {\n          result = EVAL(element, env);\n        }\n        return result;\n      case \"if\":\n        Val cond = EVAL(ast.data[1], env);\n        if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n        {\n          if(sizeof(ast.data) > 3)\n            return EVAL(ast.data[3], env);\n          else\n            return MAL_NIL;\n        }\n        else\n          return EVAL(ast.data[2], env);\n      case \"fn*\":\n        return lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); };\n    }\n  }\n  Val f = EVAL(ast.data[0], env);\n  array(Val) args = ast.data[1..];\n  args = map(args, lambda(Val e) { return EVAL(e, env);});\n  return f(@args);\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main()\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step5_tco.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nVal EVAL(Val ast, Env env)\n{\n  while(true)\n  {\n\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n    if(ast.emptyp()) return ast;\n    if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n      switch(ast.data[0].value)\n      {\n        case \"def!\":\n          return env.set(ast.data[1], EVAL(ast.data[2], env));\n        case \"let*\":\n          Env let_env = Env(env);\n          Val ast1 = ast.data[1];\n          for(int i = 0; i < sizeof(ast1.data); i += 2)\n          {\n            let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n          }\n          env = let_env;\n          ast = ast.data[2];\n          continue; // TCO\n        case \"do\":\n          Val result;\n          foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)\n          {\n            result = EVAL(element, env);\n          }\n          ast = ast.data[-1];\n          continue; // TCO\n        case \"if\":\n          Val cond = EVAL(ast.data[1], env);\n          if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n          {\n            if(sizeof(ast.data) > 3)\n              ast = ast.data[3];\n            else\n              return MAL_NIL;\n          }\n          else\n            ast = ast.data[2];\n          continue; // TCO\n        case \"fn*\":\n          return Fn(ast.data[2], ast.data[1], env,\n                    lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });\n      }\n    }\n    Val f = EVAL(ast.data[0], env);\n    array(Val) args = ast.data[1..];\n    args = map(args, lambda(Val e) { return EVAL(e, env);});\n    switch(f.mal_type)\n    {\n      case MALTYPE_BUILTINFN:\n        return f(@args);\n      case MALTYPE_FN:\n        ast = f.ast;\n        env = Env(f.env, f.params, List(args));\n        continue; // TCO\n      default:\n        throw(\"Unknown function type\");\n    }\n  }\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main()\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step6_file.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nVal EVAL(Val ast, Env env)\n{\n  while(true)\n  {\n\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n    if(ast.emptyp()) return ast;\n    if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n      switch(ast.data[0].value)\n      {\n        case \"def!\":\n          return env.set(ast.data[1], EVAL(ast.data[2], env));\n        case \"let*\":\n          Env let_env = Env(env);\n          Val ast1 = ast.data[1];\n          for(int i = 0; i < sizeof(ast1.data); i += 2)\n          {\n            let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n          }\n          env = let_env;\n          ast = ast.data[2];\n          continue; // TCO\n        case \"do\":\n          Val result;\n          foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)\n          {\n            result = EVAL(element, env);\n          }\n          ast = ast.data[-1];\n          continue; // TCO\n        case \"if\":\n          Val cond = EVAL(ast.data[1], env);\n          if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n          {\n            if(sizeof(ast.data) > 3)\n              ast = ast.data[3];\n            else\n              return MAL_NIL;\n          }\n          else\n            ast = ast.data[2];\n          continue; // TCO\n        case \"fn*\":\n          return Fn(ast.data[2], ast.data[1], env,\n                    lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });\n      }\n    }\n    Val f = EVAL(ast.data[0], env);\n    array(Val) args = ast.data[1..];\n    args = map(args, lambda(Val e) { return EVAL(e, env);});\n    switch(f.mal_type)\n    {\n      case MALTYPE_BUILTINFN:\n        return f(@args);\n      case MALTYPE_FN:\n        ast = f.ast;\n        env = Env(f.env, f.params, List(args));\n        continue; // TCO\n      default:\n        throw(\"Unknown function type\");\n    }\n  }\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main(int argc, array argv)\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  repl_env.set(Symbol(\"eval\"), BuiltinFn(\"eval\", lambda(Val a) { return EVAL(a, repl_env); }));\n  repl_env.set(Symbol(\"*ARGV*\"), List(map(argv[2..], String)));\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  if(argc >= 2)\n  {\n    rep(\"(load-file \\\"\" + argv[1] + \"\\\")\", repl_env);\n    return 0;\n  }\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step7_quote.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nbool starts_with(Val ast, string sym)\n{\n  return ast.mal_type == MALTYPE_LIST &&\n    !ast.emptyp() &&\n    ast.data[0].mal_type == MALTYPE_SYMBOL &&\n    ast.data[0].value == sym;\n}\n\nVal quasiquote_list(array(Val) elts)\n{\n  Val acc = List(({ }));\n  for(int i=sizeof(elts)-1; 0<=i; i-=1)\n  {\n    Val elt = elts[i];\n    if(starts_with(elt, \"splice-unquote\"))\n      acc = List(({ Symbol(\"concat\"), elt.data[1], acc }));\n    else\n      acc = List(({ Symbol(\"cons\"), quasiquote(elt), acc }));\n  }\n  return acc;\n}\n\nVal quasiquote(Val ast)\n{\n  switch(ast.mal_type)\n  {\n    case MALTYPE_LIST:\n      if(starts_with(ast, \"unquote\"))\n        return ast.data[1];\n      else\n        return quasiquote_list(ast.data);\n    case MALTYPE_VECTOR:\n      return List(({ Symbol(\"vec\"), quasiquote_list(ast.data) }));\n  case MALTYPE_SYMBOL:\n  case MALTYPE_MAP:\n      return List(({ Symbol(\"quote\"), ast }));\n  default:\n      return ast;\n  }\n}\n\nVal EVAL(Val ast, Env env)\n{\n  while(true)\n  {\n\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n    if(ast.emptyp()) return ast;\n    if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n      switch(ast.data[0].value)\n      {\n        case \"def!\":\n          return env.set(ast.data[1], EVAL(ast.data[2], env));\n        case \"let*\":\n          Env let_env = Env(env);\n          Val ast1 = ast.data[1];\n          for(int i = 0; i < sizeof(ast1.data); i += 2)\n          {\n            let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n          }\n          env = let_env;\n          ast = ast.data[2];\n          continue; // TCO\n        case \"quote\":\n          return ast.data[1];\n        case \"quasiquote\":\n          ast = quasiquote(ast.data[1]);\n          continue; // TCO\n        case \"do\":\n          Val result;\n          foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)\n          {\n            result = EVAL(element, env);\n          }\n          ast = ast.data[-1];\n          continue; // TCO\n        case \"if\":\n          Val cond = EVAL(ast.data[1], env);\n          if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n          {\n            if(sizeof(ast.data) > 3)\n              ast = ast.data[3];\n            else\n              return MAL_NIL;\n          }\n          else\n            ast = ast.data[2];\n          continue; // TCO\n        case \"fn*\":\n          return Fn(ast.data[2], ast.data[1], env,\n                    lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });\n      }\n    }\n    Val f = EVAL(ast.data[0], env);\n    array(Val) args = ast.data[1..];\n    args = map(args, lambda(Val e) { return EVAL(e, env);});\n    switch(f.mal_type)\n    {\n      case MALTYPE_BUILTINFN:\n        return f(@args);\n      case MALTYPE_FN:\n        ast = f.ast;\n        env = Env(f.env, f.params, List(args));\n        continue; // TCO\n      default:\n        throw(\"Unknown function type\");\n    }\n  }\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main(int argc, array argv)\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  repl_env.set(Symbol(\"eval\"), BuiltinFn(\"eval\", lambda(Val a) { return EVAL(a, repl_env); }));\n  repl_env.set(Symbol(\"*ARGV*\"), List(map(argv[2..], String)));\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  if(argc >= 2)\n  {\n    rep(\"(load-file \\\"\" + argv[1] + \"\\\")\", repl_env);\n    return 0;\n  }\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step8_macros.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nbool starts_with(Val ast, string sym)\n{\n  return ast.mal_type == MALTYPE_LIST &&\n    !ast.emptyp() &&\n    ast.data[0].mal_type == MALTYPE_SYMBOL &&\n    ast.data[0].value == sym;\n}\n\nVal quasiquote_list(array(Val) elts)\n{\n  Val acc = List(({ }));\n  for(int i=sizeof(elts)-1; 0<=i; i-=1)\n  {\n    Val elt = elts[i];\n    if(starts_with(elt, \"splice-unquote\"))\n      acc = List(({ Symbol(\"concat\"), elt.data[1], acc }));\n    else\n      acc = List(({ Symbol(\"cons\"), quasiquote(elt), acc }));\n  }\n  return acc;\n}\n\nVal quasiquote(Val ast)\n{\n  switch(ast.mal_type)\n  {\n    case MALTYPE_LIST:\n      if(starts_with(ast, \"unquote\"))\n        return ast.data[1];\n      else\n        return quasiquote_list(ast.data);\n    case MALTYPE_VECTOR:\n      return List(({ Symbol(\"vec\"), quasiquote_list(ast.data) }));\n  case MALTYPE_SYMBOL:\n  case MALTYPE_MAP:\n      return List(({ Symbol(\"quote\"), ast }));\n  default:\n      return ast;\n  }\n}\n\nVal EVAL(Val ast, Env env)\n{\n  while(true)\n  {\n\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n    if(ast.emptyp()) return ast;\n    if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n      switch(ast.data[0].value)\n      {\n        case \"def!\":\n          return env.set(ast.data[1], EVAL(ast.data[2], env));\n        case \"let*\":\n          Env let_env = Env(env);\n          Val ast1 = ast.data[1];\n          for(int i = 0; i < sizeof(ast1.data); i += 2)\n          {\n            let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n          }\n          env = let_env;\n          ast = ast.data[2];\n          continue; // TCO\n        case \"quote\":\n          return ast.data[1];\n        case \"quasiquote\":\n          ast = quasiquote(ast.data[1]);\n          continue; // TCO\n        case \"defmacro!\":\n          Val macro = EVAL(ast.data[2], env).clone_as_macro();\n          return env.set(ast.data[1], macro);\n        case \"do\":\n          Val result;\n          foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)\n          {\n            result = EVAL(element, env);\n          }\n          ast = ast.data[-1];\n          continue; // TCO\n        case \"if\":\n          Val cond = EVAL(ast.data[1], env);\n          if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n          {\n            if(sizeof(ast.data) > 3)\n              ast = ast.data[3];\n            else\n              return MAL_NIL;\n          }\n          else\n            ast = ast.data[2];\n          continue; // TCO\n        case \"fn*\":\n          return Fn(ast.data[2], ast.data[1], env,\n                    lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });\n      }\n    }\n    Val f = EVAL(ast.data[0], env);\n    array(Val) args = ast.data[1..];\n    switch(f.mal_type)\n    {\n      case MALTYPE_BUILTINFN:\n        return f(@map(args, lambda(Val e) { return EVAL(e, env);}));\n      case MALTYPE_FN:\n        if(f.macro)\n        {\n          ast = f(@args);\n          continue; // TCO\n        }\n        ast = f.ast;\n        env = Env(f.env, f.params, List(map(args, lambda(Val e) { return EVAL(e, env);})));\n        continue; // TCO\n      default:\n        throw(\"Unknown function type\");\n    }\n  }\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main(int argc, array argv)\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  repl_env.set(Symbol(\"eval\"), BuiltinFn(\"eval\", lambda(Val a) { return EVAL(a, repl_env); }));\n  repl_env.set(Symbol(\"*ARGV*\"), List(map(argv[2..], String)));\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n  if(argc >= 2)\n  {\n    rep(\"(load-file \\\"\" + argv[1] + \"\\\")\", repl_env);\n    return 0;\n  }\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err)) err = err[0];\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/step9_try.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nbool starts_with(Val ast, string sym)\n{\n  return ast.mal_type == MALTYPE_LIST &&\n    !ast.emptyp() &&\n    ast.data[0].mal_type == MALTYPE_SYMBOL &&\n    ast.data[0].value == sym;\n}\n\nVal quasiquote_list(array(Val) elts)\n{\n  Val acc = List(({ }));\n  for(int i=sizeof(elts)-1; 0<=i; i-=1)\n  {\n    Val elt = elts[i];\n    if(starts_with(elt, \"splice-unquote\"))\n      acc = List(({ Symbol(\"concat\"), elt.data[1], acc }));\n    else\n      acc = List(({ Symbol(\"cons\"), quasiquote(elt), acc }));\n  }\n  return acc;\n}\n\nVal quasiquote(Val ast)\n{\n  switch(ast.mal_type)\n  {\n    case MALTYPE_LIST:\n      if(starts_with(ast, \"unquote\"))\n        return ast.data[1];\n      else\n        return quasiquote_list(ast.data);\n    case MALTYPE_VECTOR:\n      return List(({ Symbol(\"vec\"), quasiquote_list(ast.data) }));\n  case MALTYPE_SYMBOL:\n  case MALTYPE_MAP:\n      return List(({ Symbol(\"quote\"), ast }));\n  default:\n      return ast;\n  }\n}\n\nVal EVAL(Val ast, Env env)\n{\n  while(true)\n  {\n\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n    if(ast.emptyp()) return ast;\n    if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n      switch(ast.data[0].value)\n      {\n        case \"def!\":\n          return env.set(ast.data[1], EVAL(ast.data[2], env));\n        case \"let*\":\n          Env let_env = Env(env);\n          Val ast1 = ast.data[1];\n          for(int i = 0; i < sizeof(ast1.data); i += 2)\n          {\n            let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n          }\n          env = let_env;\n          ast = ast.data[2];\n          continue; // TCO\n        case \"quote\":\n          return ast.data[1];\n        case \"quasiquote\":\n          ast = quasiquote(ast.data[1]);\n          continue; // TCO\n        case \"defmacro!\":\n          Val macro = EVAL(ast.data[2], env).clone_as_macro();\n          return env.set(ast.data[1], macro);\n        case \"try*\":\n          if(ast.count() < 3) return EVAL(ast.data[1], env);\n          if(mixed err = catch { return EVAL(ast.data[1], env); } )\n          {\n            Val err_val;\n            if(objectp(err)) err_val = err;\n            else if(stringp(err)) err_val = String(err);\n            else if(arrayp(err)) err_val = String(err[0]);\n            Val catch_clause = ast.data[2];\n            Env catch_env = Env(env);\n            catch_env.set(catch_clause.data[1], err_val);\n            return EVAL(catch_clause.data[2], catch_env);\n          }\n        case \"do\":\n          Val result;\n          foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)\n          {\n            result = EVAL(element, env);\n          }\n          ast = ast.data[-1];\n          continue; // TCO\n        case \"if\":\n          Val cond = EVAL(ast.data[1], env);\n          if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n          {\n            if(sizeof(ast.data) > 3)\n              ast = ast.data[3];\n            else\n              return MAL_NIL;\n          }\n          else\n            ast = ast.data[2];\n          continue; // TCO\n        case \"fn*\":\n          return Fn(ast.data[2], ast.data[1], env,\n                    lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });\n      }\n    }\n    Val f = EVAL(ast.data[0], env);\n    array(Val) args = ast.data[1..];\n    switch(f.mal_type)\n    {\n      case MALTYPE_BUILTINFN:\n        return f(@map(args, lambda(Val e) { return EVAL(e, env);}));\n      case MALTYPE_FN:\n        if(f.macro)\n        {\n          ast = f(@args);\n          continue; // TCO\n        }\n        ast = f.ast;\n        env = Env(f.env, f.params, List(map(args, lambda(Val e) { return EVAL(e, env);})));\n        continue; // TCO\n      default:\n        throw(\"Unknown function type\");\n    }\n  }\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main(int argc, array argv)\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  repl_env.set(Symbol(\"eval\"), BuiltinFn(\"eval\", lambda(Val a) { return EVAL(a, repl_env); }));\n  repl_env.set(Symbol(\"*ARGV*\"), List(map(argv[2..], String)));\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n  if(argc >= 2)\n  {\n    rep(\"(load-file \\\"\" + argv[1] + \"\\\")\", repl_env);\n    return 0;\n  }\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(objectp(err))\n      {\n        err = err.to_string(true);\n      }\n      else if(arrayp(err))\n      {\n        err = err[0];\n      }\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/stepA_mal.pike",
    "content": "import .Env;\nimport .Printer;\nimport .Reader;\nimport .Readline;\nimport .Types;\n\nVal READ(string str)\n{\n  return read_str(str);\n}\n\nbool starts_with(Val ast, string sym)\n{\n  return ast.mal_type == MALTYPE_LIST &&\n    !ast.emptyp() &&\n    ast.data[0].mal_type == MALTYPE_SYMBOL &&\n    ast.data[0].value == sym;\n}\n\nVal quasiquote_list(array(Val) elts)\n{\n  Val acc = List(({ }));\n  for(int i=sizeof(elts)-1; 0<=i; i-=1)\n  {\n    Val elt = elts[i];\n    if(starts_with(elt, \"splice-unquote\"))\n      acc = List(({ Symbol(\"concat\"), elt.data[1], acc }));\n    else\n      acc = List(({ Symbol(\"cons\"), quasiquote(elt), acc }));\n  }\n  return acc;\n}\n\nVal quasiquote(Val ast)\n{\n  switch(ast.mal_type)\n  {\n    case MALTYPE_LIST:\n      if(starts_with(ast, \"unquote\"))\n        return ast.data[1];\n      else\n        return quasiquote_list(ast.data);\n    case MALTYPE_VECTOR:\n      return List(({ Symbol(\"vec\"), quasiquote_list(ast.data) }));\n  case MALTYPE_SYMBOL:\n  case MALTYPE_MAP:\n      return List(({ Symbol(\"quote\"), ast }));\n  default:\n      return ast;\n  }\n}\n\nVal EVAL(Val ast, Env env)\n{\n  while(true)\n  {\n\n  Val dbgeval = env.get(\"DEBUG-EVAL\");\n  if(dbgeval && dbgeval.mal_type != MALTYPE_FALSE\n     && dbgeval.mal_type != MALTYPE_NIL)\n    write(({ \"EVAL: \", PRINT(ast), \"\\n\" }));\n\n  switch(ast.mal_type)\n  {\n    case MALTYPE_SYMBOL:\n      Val key = ast.value;\n      Val val = env.get(ast.value);\n      if(!val) throw(\"'\" + key + \"' not found\");\n      return val;\n    case MALTYPE_LIST:\n      break;\n    case MALTYPE_VECTOR:\n      return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));\n    case MALTYPE_MAP:\n      array(Val) elements = ({ });\n      foreach(ast.data; Val k; Val v)\n      {\n        elements += ({ k, EVAL(v, env) });\n      }\n      return Map(elements);\n    default:\n      return ast;\n    }\n\n    if(ast.emptyp()) return ast;\n    if(ast.data[0].mal_type == MALTYPE_SYMBOL) {\n      switch(ast.data[0].value)\n      {\n        case \"def!\":\n          return env.set(ast.data[1], EVAL(ast.data[2], env));\n        case \"let*\":\n          Env let_env = Env(env);\n          Val ast1 = ast.data[1];\n          for(int i = 0; i < sizeof(ast1.data); i += 2)\n          {\n            let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));\n          }\n          env = let_env;\n          ast = ast.data[2];\n          continue; // TCO\n        case \"quote\":\n          return ast.data[1];\n        case \"quasiquote\":\n          ast = quasiquote(ast.data[1]);\n          continue; // TCO\n        case \"defmacro!\":\n          Val macro = EVAL(ast.data[2], env).clone_as_macro();\n          return env.set(ast.data[1], macro);\n        case \"try*\":\n          if(ast.count() < 3) return EVAL(ast.data[1], env);\n          if(mixed err = catch { return EVAL(ast.data[1], env); } )\n          {\n            Val err_val;\n            if(objectp(err)) err_val = err;\n            else if(stringp(err)) err_val = String(err);\n            else if(arrayp(err)) err_val = String(err[0]);\n            Val catch_clause = ast.data[2];\n            Env catch_env = Env(env);\n            catch_env.set(catch_clause.data[1], err_val);\n            return EVAL(catch_clause.data[2], catch_env);\n          }\n        case \"do\":\n          Val result;\n          foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)\n          {\n            result = EVAL(element, env);\n          }\n          ast = ast.data[-1];\n          continue; // TCO\n        case \"if\":\n          Val cond = EVAL(ast.data[1], env);\n          if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)\n          {\n            if(sizeof(ast.data) > 3)\n              ast = ast.data[3];\n            else\n              return MAL_NIL;\n          }\n          else\n            ast = ast.data[2];\n          continue; // TCO\n        case \"fn*\":\n          return Fn(ast.data[2], ast.data[1], env,\n                    lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });\n      }\n    }\n    Val f = EVAL(ast.data[0], env);\n    array(Val) args = ast.data[1..];\n    switch(f.mal_type)\n    {\n      case MALTYPE_BUILTINFN:\n        return f(@map(args, lambda(Val e) { return EVAL(e, env);}));\n      case MALTYPE_FN:\n        if(f.macro)\n        {\n          ast = f(@args);\n          continue; // TCO\n        }\n        ast = f.ast;\n        env = Env(f.env, f.params, List(map(args, lambda(Val e) { return EVAL(e, env);})));\n        continue; // TCO\n      default:\n        throw(\"Unknown function type\");\n    }\n  }\n}\n\nstring PRINT(Val exp)\n{\n  return pr_str(exp, true);\n}\n\nstring rep(string str, Env env)\n{\n  return PRINT(EVAL(READ(str), env));\n}\n\nint main(int argc, array argv)\n{\n  Env repl_env = Env(0);\n  foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);\n  repl_env.set(Symbol(\"eval\"), BuiltinFn(\"eval\", lambda(Val a) { return EVAL(a, repl_env); }));\n  repl_env.set(Symbol(\"*ARGV*\"), List(map(argv[2..], String)));\n  rep(\"(def! *host-language* \\\"pike\\\")\", repl_env);\n  rep(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n  rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env);\n  rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env);\n  if(argc >= 2)\n  {\n    rep(\"(load-file \\\"\" + argv[1] + \"\\\")\", repl_env);\n    return 0;\n  }\n  rep(\"(println (str \\\"Mal [\\\" \\*host-language\\* \\\"]\\\"))\", repl_env);\n  while(1)\n  {\n    string line = readline(\"user> \");\n    if(!line) break;\n    if(strlen(line) == 0) continue;\n    if(mixed err = catch { write(({ rep(line, repl_env), \"\\n\" })); } )\n    {\n      if(arrayp(err))\n      {\n        err = err[0];\n      }\n      else if(objectp(err))\n      {\n        err = err.to_string(true);\n      }\n      write(({ \"Error: \", err, \"\\n\" }));\n    }\n  }\n  write(\"\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "impls/pike/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/pike/tests/stepA_mal.mal",
    "content": ";; Testing basic Pike interop\n\n;;; pike-eval compiles the given string inside a temporary function after a\n;;; \"return \" keyword. To evaluate complex statements, you may use an anonymous\n;;; lambda and call it immediately (see the last example).\n\n(pike-eval \"7\")\n;=>7\n\n(pike-eval \"'A'\")\n;=>65\n\n(pike-eval \"\\\"7\\\"\")\n;=>\"7\"\n\n(pike-eval \"({ 7,8,9 })\")\n;=>(7 8 9)\n\n(pike-eval \"([ \\\"abc\\\": 789 ])\")\n;=>{\"abc\" 789}\n\n(pike-eval \"write(\\\"hello\\\\n\\\")\")\n;/hello\n;=>6\n\n(pike-eval \"map(({ \\\"a\\\", \\\"b\\\", \\\"c\\\" }), lambda(string x) { return \\\"X\\\" + x + \\\"Y\\\"; }) * \\\" \\\"\")\n;=>\"XaY XbY XcY\"\n\n(pike-eval \"map(({ 1,2,3 }), lambda(int x) { return 1 + x; })\")\n;=>(2 3 4)\n\n(pike-eval \"throw(upper_case(\\\"aaa\\\" + \\\"bbb\\\"))\")\n;/Error: AAABBB\n\n(pike-eval \"(lambda() { int a = 5; int b = a * 3; return a + b; })()\")\n;=>20\n"
  },
  {
    "path": "impls/plpgsql/Dockerfile",
    "content": "FROM ubuntu:14.04\n\nRUN apt-get -y update\nRUN apt-get -y install make cpp python\n\nRUN apt-get -y install curl\nRUN useradd -u 1000 -m -s /bin/bash -G sudo postgres\n\nENV PG_VERSION=9.4\nRUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \\\n    echo 'deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main' > /etc/apt/sources.list.d/pgdg.list && \\\n    apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive apt-get -y install acl \\\n      postgresql-${PG_VERSION} postgresql-client-${PG_VERSION} postgresql-contrib-${PG_VERSION} && \\ \n    mkdir -p /var/run/postgresql/9.4-main.pg_stat_tmp/ && \\\n    chown -R postgres.postgres /var/run/postgresql\n\nENV HOME=/var/run/postgresql\n\nWORKDIR /mal\n\n# Travis runs as a couple of different users so add them\nRUN useradd -ou 1001 -m -s /bin/bash -G sudo,postgres travis\nRUN useradd -ou 2000 -m -s /bin/bash -G sudo,postgres travis2\n\n# Enable postgres and travis users to sudo for postgres startup\nRUN echo \"%sudo ALL=(ALL:ALL) NOPASSWD: ALL\" >> /etc/sudoers\n\n# Allow both travis and postgres user to connect to DB as 'postgres'\nRUN sed -i 's/peer$/peer map=mal/' /etc/postgresql/9.4/main/pg_hba.conf\nRUN echo \"mal postgres postgres\" >> /etc/postgresql/9.4/main/pg_ident.conf\nRUN echo \"mal travis postgres\" >> /etc/postgresql/9.4/main/pg_ident.conf\nRUN echo \"mal travis2 postgres\" >> /etc/postgresql/9.4/main/pg_ident.conf\n\n# Add entrypoint.sh which starts postgres then run bash/command\nADD entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": "impls/plpgsql/Makefile",
    "content": "all:\n\nclean:\n"
  },
  {
    "path": "impls/plpgsql/core.sql",
    "content": "CREATE SCHEMA core;\n\n-- general functions\n\nCREATE FUNCTION core.equal(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._equal_Q(args[1], args[2]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.throw(args integer[]) RETURNS integer AS $$\nBEGIN\n    -- TODO: Only throws strings. Without subtransactions, all changes\n    -- to DB up to this point get rolled back so the object being\n    -- thrown dissapears.\n    RAISE EXCEPTION '%', printer.pr_str(args[1], false);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- scalar functions\n\nCREATE FUNCTION core.nil_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._nil_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.true_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._true_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.false_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._false_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.number_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._number_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.string_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._string_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.symbol(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._symbolv(types._valueToString(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.symbol_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._symbol_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.keyword(args integer[]) RETURNS integer AS $$\nBEGIN\n    IF types._keyword_Q(args[1]) THEN\n        RETURN args[1];\n    ELSE\n        RETURN types._keywordv(types._valueToString(args[1]));\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.keyword_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._keyword_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.fn_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._fn_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.macro_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._macro_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\n\n-- string functions\n\nCREATE FUNCTION core.pr_str(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._stringv(printer.pr_str_array(args, ' ', true));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.str(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._stringv(printer.pr_str_array(args, '', false));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.prn(args integer[]) RETURNS integer AS $$\nBEGIN\n    PERFORM io.writeline(printer.pr_str_array(args, ' ', true));\n    RETURN 0; -- nil\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.println(args integer[]) RETURNS integer AS $$\nBEGIN\n    PERFORM io.writeline(printer.pr_str_array(args, ' ', false));\n    RETURN 0; -- nil\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.read_string(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(types._valueToString(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.readline(args integer[]) RETURNS integer AS $$\nDECLARE\n    input  varchar;\nBEGIN\n    input := io.readline(types._valueToString(args[1]));\n    IF input IS NULL THEN\n        RETURN 0;  -- nil\n    END IF;\n    RETURN types._stringv(rtrim(input, E'\\n'));\nEND; $$ LANGUAGE plpgsql;\n\n\n-- See:\n-- http://shuber.io/reading-from-the-filesystem-with-postgres/\nCREATE FUNCTION core.slurp(args integer[]) RETURNS integer AS $$\nDECLARE\n    fname    varchar;\n    tmp      varchar;\n    cmd      varchar;\n    lines    varchar[];\n    content  varchar;\nBEGIN\n    fname := types._valueToString(args[1]);\n    IF fname NOT LIKE '/%' THEN\n        fname := types._valueToString(envs.get(0, '*PWD*')) || '/' || fname;\n    END IF;\n\n    tmp := CAST(round(random()*1000000) AS varchar);\n\n    EXECUTE format('CREATE TEMP TABLE %I (content text)', tmp);\n    cmd := format('sed ''s/\\\\/\\\\\\\\/g'' %L', fname);\n    EXECUTE format('COPY %I FROM PROGRAM %L', tmp, cmd);\n    EXECUTE format('SELECT ARRAY(SELECT content FROM %I)', tmp) INTO lines;\n    EXECUTE format('DROP TABLE %I', tmp);\n\n    content := array_to_string(lines, E'\\n') || E'\\n';\n    RETURN types._stringv(content);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- number functions\n\n-- integer comparison\nCREATE FUNCTION core.intcmp(op varchar, args integer[]) RETURNS integer AS $$\nDECLARE a bigint; b bigint; result boolean;\nBEGIN\n    SELECT val_int INTO a FROM types.value WHERE value_id = args[1];\n    SELECT val_int INTO b FROM types.value WHERE value_id = args[2];\n    EXECUTE format('SELECT $1 %s $2;', op) INTO result USING a, b;\n    RETURN types._wraptf(result);\nEND; $$ LANGUAGE plpgsql;\n\n-- integer operation\nCREATE FUNCTION core.intop(op varchar, args integer[]) RETURNS integer AS $$\nDECLARE a bigint; b bigint; result bigint;\nBEGIN\n    SELECT val_int INTO a FROM types.value WHERE value_id = args[1];\n    SELECT val_int INTO b FROM types.value WHERE value_id = args[2];\n    EXECUTE format('SELECT $1 %s $2;', op) INTO result USING a, b;\n    RETURN types._numToValue(result);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.lt(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intcmp('<', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.lte(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intcmp('<=', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.gt(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intcmp('>', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.gte(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intcmp('>=', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.add(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intop('+', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.subtract(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intop('-', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.multiply(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intop('*', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.divide(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN core.intop('/', args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.time_ms(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._numToValue(\n        CAST(date_part('epoch', clock_timestamp()) * 1000 AS bigint));\nEND; $$ LANGUAGE plpgsql;\n\n\n-- collection functions\n\nCREATE FUNCTION core.list(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._list(args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.list_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._list_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.vector(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._vector(args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.vector_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._vector_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.hash_map(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._hash_map(args);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.map_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._hash_map_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.assoc(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._assoc_BANG(types._clone(args[1]),\n                             args[2:array_length(args, 1)]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.dissoc(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._dissoc_BANG(types._clone(args[1]),\n                              args[2:array_length(args, 1)]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.get(args integer[]) RETURNS integer AS $$\nDECLARE\n    result  integer;\nBEGIN\n    IF types._type(args[1]) = 0 THEN  -- nil\n        RETURN 0;\n    ELSE\n        result := types._get(args[1], types._valueToString(args[2]));\n        IF result IS NULL THEN RETURN 0; END IF;\n        RETURN result;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.contains_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._contains_Q(args[1],\n                                           types._valueToString(args[2])));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.keys(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._list(types._keys(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.vals(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._list(types._vals(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\n\n\n-- sequence functions\n\nCREATE FUNCTION core.sequential_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._sequential_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.cons(args integer[]) RETURNS integer AS $$\nDECLARE\n    lst   integer[];\nBEGIN\n    lst := array_prepend(args[1], types._valueToArray(args[2]));\n    RETURN types._list(lst);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.concat(args integer[]) RETURNS integer AS $$\nDECLARE\n    lst     integer;\n    result  integer[] = ARRAY[]::integer[];\nBEGIN\n    FOREACH lst IN ARRAY args LOOP\n        result := array_cat(result, types._valueToArray(lst));\n    END LOOP;\n    RETURN types._list(result);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.vec(args integer[]) RETURNS integer AS $$\nBEGIN\n    IF types._vector_Q(args[1]) THEN\n        RETURN args[1];\n    ELSE\n        RETURN types._vector(types._valueToArray(args[1]));\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.nth(args integer[]) RETURNS integer AS $$\nDECLARE\n    idx  integer;\nBEGIN\n    SELECT val_int INTO idx FROM types.value WHERE value_id = args[2];\n    IF idx >= types._count(args[1]) THEN\n        RAISE EXCEPTION 'nth: index out of range';\n    END IF;\n    RETURN types._nth(args[1], idx);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.first(args integer[]) RETURNS integer AS $$\nBEGIN\n    IF types._nil_Q(args[1]) THEN\n        RETURN 0; -- nil\n    ELSIF types._count(args[1]) = 0 THEN\n        RETURN 0; -- nil\n    ELSE\n        RETURN types._first(args[1]);\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.rest(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._rest(args[1]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.empty_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    IF types._sequential_Q(args[1]) AND types._count(args[1]) = 0 THEN\n        RETURN 2;\n    ELSE\n        RETURN 1;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.count(args integer[]) RETURNS integer AS $$\nBEGIN\n    IF types._sequential_Q(args[1]) THEN\n        RETURN types._numToValue(types._count(args[1]));\n    ELSIF types._nil_Q(args[1]) THEN\n        RETURN types._numToValue(0);\n    ELSE\n        RAISE EXCEPTION 'count called on non-sequence';\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.apply(args integer[]) RETURNS integer AS $$\nDECLARE\n    alen   integer;\n    fargs  integer[];\nBEGIN\n    alen := array_length(args, 1);\n    fargs := array_cat(args[2:alen-1], types._valueToArray(args[alen]));\n    RETURN types._apply(args[1], fargs);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.map(args integer[]) RETURNS integer AS $$\nDECLARE\n    x       integer;\n    result  integer[];\nBEGIN\n    FOREACH x IN ARRAY types._valueToArray(args[2])\n    LOOP\n        result := array_append(result, types._apply(args[1], ARRAY[x]));\n    END LOOP;\n    return types._list(result);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.conj(args integer[]) RETURNS integer AS $$\nDECLARE\n    type  integer;\nBEGIN\n    type := types._type(args[1]);\n    CASE\n    WHEN type = 8 THEN -- list\n        RETURN types._list(array_cat(\n                types.array_reverse(args[2:array_length(args, 1)]),\n                types._valueToArray(args[1])));\n    WHEN type = 9 THEN -- vector\n        RETURN types._vector(array_cat(\n                types._valueToArray(args[1]),\n                args[2:array_length(args, 1)]));\n    ELSE\n        RAISE EXCEPTION 'conj: called on non-sequence';\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.seq(args integer[]) RETURNS integer AS $$\nDECLARE\n    type  integer;\n    vid   integer;\n    str   varchar;\n    chr   varchar;\n    seq   integer[];\nBEGIN\n    type := types._type(args[1]);\n    CASE\n    WHEN type = 8 THEN -- list\n        IF types._count(args[1]) = 0 THEN RETURN 0; END IF; -- nil\n        RETURN args[1];\n    WHEN type = 9 THEN -- vector\n        IF types._count(args[1]) = 0 THEN RETURN 0; END IF; -- nil\n        -- clone and modify to a list\n        vid := types._clone(args[1]);\n        UPDATE types.value SET type_id = 8 WHERE value_id = vid;\n        RETURN vid;\n    WHEN type = 5 THEN -- string\n        str := types._valueToString(args[1]);\n        IF char_length(str) = 0 THEN RETURN 0; END IF; -- nil\n        FOREACH chr IN ARRAY regexp_split_to_array(str, '') LOOP\n            seq := array_append(seq, types._stringv(chr));\n        END LOOP;\n        RETURN types._list(seq);\n    WHEN type = 0 THEN -- nil\n        RETURN 0; -- nil\n    ELSE\n        RAISE EXCEPTION 'seq: called on non-sequence';\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- meta functions\n\nCREATE FUNCTION core.meta(args integer[]) RETURNS integer AS $$\nDECLARE\n    m  integer;\nBEGIN\n    SELECT meta_id INTO m FROM types.value WHERE value_id = args[1];\n    IF m IS NULL THEN\n        RETURN 0;\n    ELSE\n        RETURN m;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.with_meta(args integer[]) RETURNS integer AS $$\nDECLARE\n    vid  integer;\nBEGIN\n    vid := types._clone(args[1]);\n    UPDATE types.value SET meta_id = args[2]\n        WHERE value_id = vid;\n    RETURN vid;\nEND; $$ LANGUAGE plpgsql;\n\n\n\n-- atom functions\n\nCREATE FUNCTION core.atom(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._atom(args[1]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.atom_Q(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._wraptf(types._atom_Q(args[1]));\nEND; $$ LANGUAGE plpgsql;\n\n\nCREATE FUNCTION core.deref(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._deref(args[1]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.reset_BANG(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._reset_BANG(args[1], args[2]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION core.swap_BANG(args integer[]) RETURNS integer AS $$\nDECLARE\n    atm    integer;\n    fargs  integer[];\nBEGIN\n    atm := args[1];\n    fargs := array_cat(ARRAY[types._deref(atm)], args[3:array_length(args, 1)]);\n    RETURN types._reset_BANG(atm, types._apply(args[2], fargs));\nEND; $$ LANGUAGE plpgsql;\n\n-- ---------------------------------------------------------\n\n-- repl_env is environment 0\n\nINSERT INTO envs.env (env_id, outer_id, data)\n    VALUES (0, NULL, hstore(ARRAY[\n        '=',           types._function('core.equal'),\n        'throw',       types._function('core.throw'),\n\n        'nil?',        types._function('core.nil_Q'),\n        'true?',       types._function('core.true_Q'),\n        'false?',      types._function('core.false_Q'),\n        'number?',     types._function('core.number_Q'),\n        'string?',     types._function('core.string_Q'),\n        'symbol',      types._function('core.symbol'),\n        'symbol?',     types._function('core.symbol_Q'),\n        'keyword',     types._function('core.keyword'),\n        'keyword?',    types._function('core.keyword_Q'),\n        'fn?',         types._function('core.fn_Q'),\n        'macro?',      types._function('core.macro_Q'),\n\n        'pr-str',      types._function('core.pr_str'),\n        'str',         types._function('core.str'),\n        'prn',         types._function('core.prn'),\n        'println',     types._function('core.println'),\n        'read-string', types._function('core.read_string'),\n        'readline',    types._function('core.readline'),\n        'slurp',       types._function('core.slurp'),\n\n        '<',           types._function('core.lt'),\n        '<=',          types._function('core.lte'),\n        '>',           types._function('core.gt'),\n        '>=',          types._function('core.gte'),\n        '+',           types._function('core.add'),\n        '-',           types._function('core.subtract'),\n        '*',           types._function('core.multiply'),\n        '/',           types._function('core.divide'),\n        'time-ms',     types._function('core.time_ms'),\n\n        'list',        types._function('core.list'),\n        'list?',       types._function('core.list_Q'),\n        'vector',      types._function('core.vector'),\n        'vector?',     types._function('core.vector_Q'),\n        'hash-map',    types._function('core.hash_map'),\n        'map?',        types._function('core.map_Q'),\n        'assoc',       types._function('core.assoc'),\n        'dissoc',      types._function('core.dissoc'),\n        'get',         types._function('core.get'),\n        'contains?',   types._function('core.contains_Q'),\n        'keys',        types._function('core.keys'),\n        'vals',        types._function('core.vals'),\n\n        'sequential?', types._function('core.sequential_Q'),\n        'cons',        types._function('core.cons'),\n        'concat',      types._function('core.concat'),\n        'vec',         types._function('core.vec'),\n        'nth',         types._function('core.nth'),\n        'first',       types._function('core.first'),\n        'rest',        types._function('core.rest'),\n        'empty?',      types._function('core.empty_Q'),\n        'count',       types._function('core.count'),\n        'apply',       types._function('core.apply'),\n        'map',         types._function('core.map'),\n\n        'conj',        types._function('core.conj'),\n        'seq',         types._function('core.seq'),\n\n        'meta',        types._function('core.meta'),\n        'with-meta',   types._function('core.with_meta'),\n        'atom',        types._function('core.atom'),\n        'atom?',       types._function('core.atom_Q'),\n        'deref',       types._function('core.deref'),\n        'reset!',      types._function('core.reset_BANG'),\n        'swap!',       types._function('core.swap_BANG')\n        ]));\n"
  },
  {
    "path": "impls/plpgsql/entrypoint.sh",
    "content": "#!/usr/bin/env bash\n\nPOSTGRES_SUDO_USER=${POSTGRES_SUDO_USER:-postgres}\n\nPOPTS=\"\"\nwhile [[ ${1:0:1} = '-' ]]; do\n    POPTS=\"${POPTS}$1 $2\"\n    shift; shift\ndone\n\nsudo --user=${POSTGRES_SUDO_USER} \\\n    bash -c \"/usr/lib/postgresql/9.4/bin/postgres \\\n    -c config_file=/etc/postgresql/9.4/main/postgresql.conf \\\n    ${POPTS} >/var/log/postgresql/output.log 2>&1\" & disown -h\n\nwhile ! ( echo \"\" > /dev/tcp/localhost/5432) 2>/dev/null; do\n    echo \"Waiting for postgres to start\"\n    sleep 1\ndone\n\nif [ \"${*}\" ]; then\n    exec \"${@}\"\nelse\n    exec bash\nfi\n"
  },
  {
    "path": "impls/plpgsql/envs.sql",
    "content": "-- ---------------------------------------------------------\n-- envs.sql\n\nCREATE SCHEMA envs\n    -- env table\n    CREATE SEQUENCE env_id_seq\n    CREATE TABLE env (\n        env_id    integer NOT NULL DEFAULT nextval('envs.env_id_seq'),\n        outer_id  integer,\n        data      hstore\n    );\n\nALTER TABLE envs.env ADD CONSTRAINT pk_env_id\n    PRIMARY KEY (env_id);\n-- drop sequence when table dropped\nALTER SEQUENCE envs.env_id_seq OWNED BY envs.env.env_id;\nALTER TABLE envs.env ADD CONSTRAINT fk_env_outer_id\n    FOREIGN KEY (outer_id) REFERENCES envs.env(env_id);\n\n-- -----------------------\n\n-- envs.new\nCREATE FUNCTION envs.new(outer_env integer) RETURNS integer AS $$\nDECLARE\n    e  integer;\nBEGIN\n    INSERT INTO envs.env (outer_id) VALUES (outer_env)\n        RETURNING env_id INTO e;\n    --RAISE NOTICE 'env_new: e: %, outer_env: %', e, outer_env;\n    RETURN e;\nEND; $$ LANGUAGE plpgsql;\n\n-- envs.new with bindings\nCREATE FUNCTION envs.new(outer_env integer,\n                         binds integer,\n                         exprs integer[])\n    RETURNS integer AS $$\nDECLARE\n    bseq  integer[];\n    env   integer;\n    i     integer;\n    bind  integer;\n    bsym  varchar;\n    expr  integer;\nBEGIN\n    env := envs.new(outer_env);\n    bseq := types._valueToArray(binds);\n    FOR i IN 1 .. COALESCE(array_length(bseq, 1), 0) LOOP\n        bind := bseq[i];\n        bsym := types._valueToString(bind);\n        expr := exprs[i];\n        --RAISE NOTICE 'i: %, bind: %, expr: %', i, bind, expr;\n        IF bsym = '&' THEN\n            bind := bseq[i+1];\n            PERFORM envs.set(env, bind,\n                             types._list(exprs[i:array_length(exprs, 1)]));\n            RETURN env;\n        END IF;\n        PERFORM envs.vset(env, bsym, expr);\n    END LOOP;\n    RETURN env;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- envs.vset\n-- like envs.set but takes a varchar key instead of value_id\nCREATE FUNCTION envs.vset(env integer, name varchar, val integer)\n    RETURNS integer AS $$\nDECLARE\n    e  integer = env;\n    d  hstore;\nBEGIN\n    SELECT data INTO d FROM envs.env WHERE env_id=e;\n    IF d IS NULL THEN\n        d := hstore(name, CAST(val AS varchar));\n    ELSE\n        d := d || hstore(name, CAST(val AS varchar));\n    END IF;\n    UPDATE envs.env SET data = d WHERE env_id=e;\n    RETURN val;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- envs.set\nCREATE FUNCTION envs.set(env integer, key integer, val integer)\n    RETURNS integer AS $$\nDECLARE\n    symkey  varchar;\nBEGIN\n    symkey := types._valueToString(key);\n    RETURN envs.vset(env, symkey, val);\nEND; $$ LANGUAGE plpgsql;\n\n-- envs.get\nCREATE FUNCTION envs.get(env integer, symkey varchar) RETURNS integer AS $$\nDECLARE\n    outer_id  integer;\n    d         hstore;\nBEGIN\n  LOOP\n    SELECT e.data, e.outer_id INTO d, outer_id FROM envs.env e\n        WHERE e.env_id = env;\n    IF d ? symkey THEN\n        RETURN d -> symkey;\n    END IF;\n    env := outer_id;\n    IF env IS NULL THEN\n        RETURN NULL;\n    END IF;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/init.sql",
    "content": "-- ---------------------------------------------------------\n-- init.sql\n\n-- Drop pre-existing schemas\nDROP SCHEMA IF EXISTS io, types, reader, printer, envs, core, mal CASCADE;\n\n-- Drop and recreate extensions\nDROP EXTENSION IF EXISTS hstore;\nCREATE EXTENSION hstore;\n\nDROP EXTENSION IF EXISTS dblink;\nCREATE EXTENSION dblink;\n\n"
  },
  {
    "path": "impls/plpgsql/io.sql",
    "content": "-- dblink is necessary to be able to sub-transactions (autonomous\n-- transactions) to the stream table. This is necessary to be able to\n-- modify the stream table from the perspective of outside callers\n-- because actual code can be long-lived and it's direct updates will\n-- not be seen until the process completes.\n\nCREATE SCHEMA io\n\n    CREATE TABLE stream (\n        stream_id  integer,\n        open       boolean,\n        data       varchar,\n        rl_prompt  varchar  -- prompt for readline input\n    );\n\n-- stdin\nINSERT INTO io.stream (stream_id, open, data, rl_prompt)\n    VALUES (0, false, '', '');\n-- stdout\nINSERT INTO io.stream (stream_id, open, data, rl_prompt)\n    VALUES (1, false, '', '');\n\n-- ---------------------------------------------------------\n\nCREATE FUNCTION io.open(sid integer) RETURNS void AS $$\nDECLARE\n    query  varchar;\nBEGIN\n    --RAISE NOTICE 'io.open start';\n    query := format('UPDATE io.stream\n        SET data = '''', rl_prompt = '''', open = true\n        WHERE stream_id = %L', sid);\n    PERFORM dblink('dbname=mal', query);\n    --RAISE NOTICE 'io.open done';\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\nCREATE FUNCTION io.close(sid integer) RETURNS void AS $$\nDECLARE\n    query  varchar;\nBEGIN\n    --RAISE NOTICE 'io.close start';\n    query := format('UPDATE io.stream\n        SET rl_prompt = '''', open = false\n        WHERE stream_id = %L', sid);\n    PERFORM dblink('dbname=mal', query);\n    --RAISE NOTICE 'io.close done';\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n\n-- called from read via dblink\nCREATE FUNCTION io.__read(sid integer) RETURNS varchar AS $$\nDECLARE\n    input   varchar;\n    isopen  boolean;\nBEGIN\n    LOCK io.stream;\n    SELECT data, open INTO input, isopen FROM io.stream\n        WHERE stream_id = sid;\n    IF input <> '' THEN\n        UPDATE io.stream SET data = '' WHERE stream_id = sid;\n        RETURN input;\n    END IF;\n    IF isopen = false THEN\n        RETURN NULL;\n    END IF;\n    RETURN input;\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n-- read:\n-- read from stream stream_id in stream table. Waits until there is\n-- either data to return or the stream closes (NULL data). Returns\n-- NULL when stream is closed.\nCREATE FUNCTION io.read(sid integer DEFAULT 0) RETURNS varchar AS $$\nDECLARE\n    query  varchar;\n    input  varchar;\n    sleep  real = 0.05;\nBEGIN\n    -- poll / wait for input\n    query := format('SELECT io.__read(%L);', sid);\n\n    WHILE true\n    LOOP\n        -- atomic get and set to empty\n        SELECT cur_data INTO input FROM dblink('dbname=mal', query)\n            AS t1(cur_data varchar);\n        IF input <> '' OR input IS NULL THEN\n            RETURN input;\n        END IF;\n        PERFORM pg_sleep(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n-- read_or_error:\n-- similar to read, but throws exception when stream is closed\nCREATE FUNCTION io.read_or_error(sid integer DEFAULT 0) RETURNS varchar AS $$\nDECLARE\n    input  varchar;\nBEGIN\n    input := io.read(sid);\n    IF input IS NULL THEN\n        raise EXCEPTION 'Stream ''%'' is closed', sid;\n    ELSE\n        RETURN input;\n    END IF;\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n\n-- readline:\n-- set prompt and wait for readline style input on the stream\nCREATE FUNCTION io.readline(prompt varchar, sid integer DEFAULT 0)\n    RETURNS varchar AS $$\nDECLARE\n    query  varchar;\nBEGIN\n    -- set prompt / request readline style input\n    IF sid = 0 THEN\n        PERFORM io.wait_flushed(1);\n    ELSIF sid = 1 THEN\n        PERFORM io.wait_flushed(0);\n    END IF;\n    query := format('LOCK io.stream; UPDATE io.stream SET rl_prompt = %L',\n        prompt);\n    PERFORM dblink('dbname=mal', query);\n\n    RETURN io.read(sid);\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\nCREATE FUNCTION io.write(data varchar, sid integer DEFAULT 1)\nRETURNS void AS $$\nDECLARE\n    query   varchar;\nBEGIN\n    query := format('LOCK io.stream;\n        UPDATE io.stream SET data = data || %L WHERE stream_id = %L',\n        data, sid);\n    --RAISE NOTICE 'write query: %', query;\n    PERFORM dblink('dbname=mal', query);\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\nCREATE FUNCTION io.writeline(data varchar, sid integer DEFAULT 1)\nRETURNS void AS $$\nBEGIN\n    PERFORM io.write(data || E'\\n', sid);\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n-- ---------------------------------------------------------\n\n-- called from wait_rl_prompt via dblink\nCREATE FUNCTION io.__wait_rl_prompt(sid integer) RETURNS varchar AS $$\nDECLARE\n    isopen  boolean;\n    prompt  varchar;\n    datas   integer;\nBEGIN\n    LOCK io.stream;\n    SELECT open, rl_prompt INTO isopen, prompt FROM io.stream\n        WHERE stream_id = sid;\n    SELECT count(stream_id) INTO datas FROM io.stream WHERE data <> '';\n\n    IF isopen = false THEN\n        return NULL;\n        --raise EXCEPTION 'Stream ''%'' is closed', sid;\n    END IF;\n\n    IF datas = 0 AND prompt <> '' THEN\n        UPDATE io.stream SET rl_prompt = '' WHERE stream_id = sid;\n        -- There is pending data on some stream\n        RETURN prompt;\n    END IF;\n    RETURN ''; -- '' -> no input\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n-- wait_rl_prompt:\n-- wait for rl_prompt to be set on the given stream and return the\n-- rl_prompt value. Errors if stream is already closed.\nCREATE FUNCTION io.wait_rl_prompt(sid integer DEFAULT 0) RETURNS varchar AS $$\nDECLARE\n    query    varchar;\n    prompt   varchar;\n    sleep    real = 0.05;\nBEGIN\n    query := format('SELECT io.__wait_rl_prompt(%L);', sid);\n    WHILE true\n    LOOP\n        SELECT rl_prompt INTO prompt FROM dblink('dbname=mal', query)\n            AS t1(rl_prompt varchar);\n        IF prompt IS NULL THEN\n            raise EXCEPTION 'Stream ''%'' is closed', sid;\n        END IF;\n        IF prompt <> '' THEN\n            sleep := 0.05; -- reset sleep timer\n            RETURN prompt;\n        END IF;\n        PERFORM pg_sleep(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\nCREATE FUNCTION io.wait_flushed(sid integer DEFAULT 1) RETURNS void AS $$\nDECLARE\n    query    varchar;\n    pending  integer;\n    sleep    real = 0.05;\nBEGIN\n    query := format('SELECT count(stream_id) FROM io.stream\n        WHERE stream_id = %L AND data <> ''''', sid);\n    WHILE true\n    LOOP\n        SELECT p INTO pending FROM dblink('dbname=mal', query)\n            AS t1(p integer);\n        IF pending = 0 THEN RETURN; END IF;\n        PERFORM pg_sleep(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND; $$ LANGUAGE 'plpgsql' STRICT;\n\n"
  },
  {
    "path": "impls/plpgsql/printer.sql",
    "content": "-- ---------------------------------------------------------\n-- printer.sql\n\nCREATE SCHEMA printer;\n\nCREATE FUNCTION printer.pr_str_array(arr integer[],\n    sep varchar, print_readably boolean)\n    RETURNS varchar AS $$\nDECLARE\n    i    integer;\n    res  varchar[];\nBEGIN\n    IF array_length(arr, 1) > 0 THEN\n        FOR i IN array_lower(arr, 1) .. array_upper(arr, 1)\n        LOOP\n            res := array_append(res, printer.pr_str(arr[i], print_readably));\n        END LOOP;\n        RETURN array_to_string(res, sep);\n    ELSE\n        RETURN '';\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION printer.pr_str(ast integer,\n    print_readably boolean DEFAULT true)\n    RETURNS varchar AS $$\nDECLARE\n    type  integer;\n    seq   integer[];\n    hash  hstore;\n    cid   integer;\n    vid   integer;\n    pid   integer;\n    str   varchar;\nBEGIN\n    -- RAISE NOTICE 'pr_str ast: %', ast;\n    SELECT type_id FROM types.value WHERE value_id = ast INTO type;\n    -- RAISE NOTICE 'pr_str type: %', type;\n    CASE\n    WHEN type = 0 THEN RETURN 'nil';\n    WHEN type = 1 THEN RETURN 'false';\n    WHEN type = 2 THEN RETURN 'true';\n    WHEN type = 3 THEN  -- integer\n        RETURN CAST((SELECT val_int\n                     FROM types.value WHERE value_id = ast) as varchar);\n    WHEN type = 5 THEN  -- string\n        str := types._valueToString(ast);\n        IF chr(CAST(x'7f' AS integer)) = substring(str FROM 1 FOR 1) THEN\n            RETURN ':' || substring(str FROM 2 FOR (char_length(str)-1));\n        ELSIF print_readably THEN\n            str := replace(str, E'\\\\', '\\\\');\n            str := replace(str, '\"', '\\\"');\n            str := replace(str, E'\\n', '\\n');\n            RETURN '\"' || str || '\"';\n        ELSE\n            RETURN str;\n        END IF;\n    WHEN type = 7 THEN  -- symbol\n        RETURN types._valueToString(ast);\n    WHEN type = 8 THEN  -- list\n        BEGIN\n            SELECT val_seq INTO seq FROM types.value WHERE value_id = ast;\n            RETURN '(' ||\n                   array_to_string(array(\n                        SELECT printer.pr_str(x, print_readably)\n                            FROM unnest(seq) AS x), ' ') ||\n                   ')';\n        END;\n    WHEN type = 9 THEN  -- vector\n        BEGIN\n            SELECT val_seq INTO seq FROM types.value WHERE value_id = ast;\n            RETURN '[' ||\n                   array_to_string(array(\n                        SELECT printer.pr_str(x, print_readably)\n                            FROM unnest(seq) AS x), ' ') ||\n                   ']';\n        END;\n    WHEN type = 10 THEN  -- hash-map\n        BEGIN\n            SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n            RETURN '{' ||\n                   array_to_string(array(\n                        SELECT printer.pr_str(CAST(key AS integer),\n                                              print_readably) || ' ' ||\n                               printer.pr_str(CAST(value AS integer),\n                                              print_readably)\n                        FROM each(hash)), ' ') ||\n                   '}';\n        END;\n    WHEN type = 11 THEN  -- native function\n        RETURN '#<function ' ||\n               (SELECT val_string FROM types.value WHERE value_id = ast) ||\n               '>';\n    WHEN type = 12 THEN  -- mal function\n        BEGIN\n            SELECT ast_id, params_id\n                INTO vid, pid\n                FROM types.value WHERE value_id = ast;\n            RETURN '(fn* ' || printer.pr_str(pid, print_readably) ||\n                   ' ' || printer.pr_str(vid, print_readably) || ')';\n        END;\n    WHEN type = 13 THEN  -- atom\n        BEGIN\n            SELECT val_seq[1] INTO vid\n                FROM types.value WHERE value_id = ast;\n            RETURN '(atom ' || printer.pr_str(vid, print_readably) || ')';\n        END;\n    ELSE\n        RETURN 'unknown';\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/reader.sql",
    "content": "-- ---------------------------------------------------------\n-- reader.sql\n\nCREATE SCHEMA reader;\n\nCREATE FUNCTION reader.tokenize(str varchar) RETURNS varchar[] AS $$\nDECLARE\n    re varchar = E'[[:space:] ,]*(~@|[\\\\[\\\\]{}()\\'`~@^]|\"(?:[\\\\\\\\].|[^\\\\\\\\\"])*\"?|;[^\\n]*|[^\\\\s \\\\[\\\\]{}()\\'\"`~@,;^]*)';\nBEGIN\n    RETURN ARRAY(SELECT tok FROM\n        (SELECT (regexp_matches(str, re, 'g'))[1] AS tok) AS x\n        WHERE tok <> '' AND tok NOT LIKE ';%');\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- read_atom:\n-- takes a tokens array and position\n-- returns new position and value_id\nCREATE FUNCTION reader.read_atom(tokens varchar[],\n    INOUT pos integer, OUT result integer) AS $$\nDECLARE\n    str_id  integer;\n    str     varchar;\n    token   varchar;\nBEGIN\n    token := tokens[pos];\n    pos := pos + 1;\n    -- RAISE NOTICE 'read_atom: %', token;\n    IF token = 'nil' THEN       -- nil\n        result := 0;\n    ELSIF token = 'false' THEN  -- false\n        result := 1;\n    ELSIF token = 'true' THEN   -- true\n        result := 2;\n    ELSIF token ~ '^-?[0-9][0-9]*$' THEN  -- integer\n        -- integer\n        INSERT INTO types.value (type_id, val_int)\n            VALUES (3, CAST(token AS integer))\n            RETURNING value_id INTO result;\n    ELSIF token ~ '^\"(?:[\\\\\\\\].|[^\\\\\\\\\"])*\"' THEN  -- string\n        -- string\n        str := substring(token FROM 2 FOR (char_length(token)-2));\n        str := replace(str, '\\\\', chr(CAST(x'7f' AS integer)));\n        str := replace(str, '\\\"', '\"');\n        str := replace(str, '\\n', E'\\n');\n        str := replace(str, chr(CAST(x'7f' AS integer)), E'\\\\');\n        result := types._stringv(str);\n    ELSIF token ~ '^\".*' THEN  -- unclosed string\n        RAISE EXCEPTION 'expected ''\"'', got EOF';\n    ELSIF token ~ '^:.*' THEN  -- keyword\n        -- keyword\n        result := types._keywordv(substring(token FROM 2 FOR (char_length(token)-1)));\n    ELSE\n        -- symbol\n        result := types._symbolv(token);\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\n-- read_seq:\n-- takes a tokens array, type (8, 9, 10), first and last characters\n-- and position\n-- returns new position and value_id for a list (8), vector (9) or\n-- hash-map (10)\nCREATE FUNCTION reader.read_seq(tokens varchar[], first varchar, last varchar,\n    INOUT p integer, OUT items integer[]) AS $$\nDECLARE\n    token     varchar;\n    key       varchar = NULL;\n    item_id   integer;\nBEGIN\n    token := tokens[p];\n    p := p + 1;\n    IF token <> first THEN\n        RAISE EXCEPTION 'expected ''%'', got EOF', first;\n    END IF;\n    items := ARRAY[]::integer[];\n    LOOP\n        IF p > array_length(tokens, 1) THEN\n            RAISE EXCEPTION 'expected ''%'', got EOF', last;\n        END IF;\n        token := tokens[p];\n        IF token = last THEN EXIT; END IF;\n        SELECT * FROM reader.read_form(tokens, p) INTO p, item_id;\n        items := array_append(items, item_id);\n    END LOOP;\n\n    p := p + 1;\nEND; $$ LANGUAGE plpgsql;\n\n-- read_form:\n-- takes a tokens array and position\n-- returns new position and value_id\nCREATE FUNCTION reader.read_form(tokens varchar[],\n    INOUT pos integer, OUT result integer) AS $$\nDECLARE\n    vid     integer;\n    meta    integer;\n    token   varchar;\nBEGIN\n    token := tokens[pos];  -- peek\n    CASE\n    WHEN token = '''' THEN\n    BEGIN\n        pos := pos + 1;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;\n        result := types._list(ARRAY[types._symbolv('quote'), vid]);\n    END;\n    WHEN token = '`' THEN\n    BEGIN\n        pos := pos + 1;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;\n        result := types._list(ARRAY[types._symbolv('quasiquote'), vid]);\n    END;\n    WHEN token = '~' THEN\n    BEGIN\n        pos := pos + 1;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;\n        result := types._list(ARRAY[types._symbolv('unquote'), vid]);\n    END;\n    WHEN token = '~@' THEN\n    BEGIN\n        pos := pos + 1;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;\n        result := types._list(ARRAY[types._symbolv('splice-unquote'), vid]);\n    END;\n    WHEN token = '^' THEN\n    BEGIN\n        pos := pos + 1;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, meta;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;\n        result := types._list(ARRAY[types._symbolv('with-meta'), vid, meta]);\n    END;\n    WHEN token = '@' THEN\n    BEGIN\n        pos := pos + 1;\n        SELECT * FROM reader.read_form(tokens, pos) INTO pos, vid;\n        result := types._list(ARRAY[types._symbolv('deref'), vid]);\n    END;\n\n    -- list\n    WHEN token = ')' THEN\n        RAISE EXCEPTION 'unexpected '')''';\n    WHEN token = '(' THEN\n    BEGIN\n        SELECT p, types._list(items)\n            FROM reader.read_seq(tokens, '(', ')', pos) INTO pos, result;\n    END;\n\n    -- vector\n    WHEN token = ']' THEN\n        RAISE EXCEPTION 'unexpected '']''';\n    WHEN token = '[' THEN\n    BEGIN\n        SELECT p, types._vector(items)\n            FROM reader.read_seq(tokens, '[', ']', pos) INTO pos, result;\n    END;\n\n    -- hash-map\n    WHEN token = '}' THEN\n        RAISE EXCEPTION 'unexpected ''}''';\n    WHEN token = '{' THEN\n    BEGIN\n        SELECT p, types._hash_map(items)\n            FROM reader.read_seq(tokens, '{', '}', pos) INTO pos, result;\n    END;\n\n    --\n    ELSE\n        SELECT * FROM reader.read_atom(tokens, pos) INTO pos, result;\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\n-- read_str:\n-- takes a string\n-- returns a new value_id\nCREATE FUNCTION reader.read_str(str varchar) RETURNS integer AS $$\nDECLARE\n    tokens varchar[];\n    pos    integer;\n    ast    integer;\nBEGIN\n    tokens := reader.tokenize(str);\n    -- RAISE NOTICE 'read_str first: %', tokens[1];\n    pos := 1;\n    SELECT * FROM reader.read_form(tokens, pos) INTO pos, ast;\n    -- RAISE NOTICE 'pos after read_atom: %', pos;\n    RETURN ast;\nEND; $$ LANGUAGE plpgsql;\n\n"
  },
  {
    "path": "impls/plpgsql/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/wrap.sh $(dirname $0)/${STEP:-stepA_mal}.sql \"${@}\"\n"
  },
  {
    "path": "impls/plpgsql/step0_repl.sql",
    "content": "-- ---------------------------------------------------------\n-- step0_repl.sql\n\n\\i init.sql\n\\i io.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN line;\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\nCREATE FUNCTION mal.EVAL(ast varchar, env varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN ast;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN exp;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), ''));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\nBEGIN\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step1_read_print.sql",
    "content": "-- ---------------------------------------------------------\n-- step1_read_print.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\nCREATE FUNCTION mal.EVAL(ast integer, env varchar) RETURNS integer AS $$\nBEGIN\n    RETURN ast;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), ''));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\nBEGIN\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step2_eval.sql",
    "content": "-- ---------------------------------------------------------\n-- step2_eval.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env hstore) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n    BEGIN\n        IF env ? symkey THEN\n            RETURN env -> symkey;\n        ELSE\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env hstore) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env hstore) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env hstore) RETURNS integer AS $$\nDECLARE\n    a0       integer;\n    fname    varchar;\n    args     integer[] := ARRAY[]::integer[];\n    evda0    integer;\n    result   integer;\nBEGIN\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    evda0  := mal.EVAL(a0, env);\n    SELECT val_string INTO fname FROM types.value\n        WHERE value_id = evda0;\n    FOR i in 1 .. types._count(ast) - 1 LOOP\n        args[i] := mal.EVAL(types._nth(ast, i), env);\n    END LOOP;\n    EXECUTE format('SELECT %s($1);', fname) INTO result USING args;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\nCREATE FUNCTION mal.intop(op varchar, args integer[]) RETURNS integer AS $$\nDECLARE a integer; b integer; result integer;\nBEGIN\n    SELECT val_int INTO a FROM types.value WHERE value_id = args[1];\n    SELECT val_int INTO b FROM types.value WHERE value_id = args[2];\n    EXECUTE format('INSERT INTO types.value (type_id, val_int)\n                    VALUES (3, $1 %s $2)\n                    RETURNING value_id;', op) INTO result USING a, b;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.add(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('+', args); END; $$ LANGUAGE plpgsql;\nCREATE FUNCTION mal.subtract(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('-', args); END; $$ LANGUAGE plpgsql;\nCREATE FUNCTION mal.multiply(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('*', args); END; $$ LANGUAGE plpgsql;\nCREATE FUNCTION mal.divide(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('/', args); END; $$ LANGUAGE plpgsql;\n\n\nCREATE FUNCTION mal.REP(env hstore, line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), env));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$\nDECLARE\n    repl_env  hstore;\n    line      varchar;\n    output    varchar;\nBEGIN\n    repl_env := hstore(ARRAY[\n        '+', types._function('mal.add'),\n        '-', types._function('mal.subtract'),\n        '*', types._function('mal.multiply'),\n        '/', types._function('mal.divide')]);\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(repl_env, line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step3_env.sql",
    "content": "-- ---------------------------------------------------------\n-- step3_env.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        RETURN mal.EVAL(types._nth(ast, 2), let_env);\n    END;\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        fname            varchar;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT val_string INTO fname FROM types.value\n            WHERE value_id = evda0;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        EXECUTE format('SELECT %s($1);', fname)\n            INTO result USING args;\n        RETURN result;\n    END;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\nCREATE FUNCTION mal.intop(op varchar, args integer[]) RETURNS integer AS $$\nDECLARE a integer; b integer; result integer;\nBEGIN\n    SELECT val_int INTO a FROM types.value WHERE value_id = args[1];\n    SELECT val_int INTO b FROM types.value WHERE value_id = args[2];\n    EXECUTE format('INSERT INTO types.value (type_id, val_int)\n                    VALUES (3, $1 %s $2)\n                    RETURNING value_id;', op) INTO result USING a, b;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.add(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('+', args); END; $$ LANGUAGE plpgsql;\nCREATE FUNCTION mal.subtract(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('-', args); END; $$ LANGUAGE plpgsql;\nCREATE FUNCTION mal.multiply(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('*', args); END; $$ LANGUAGE plpgsql;\nCREATE FUNCTION mal.divide(args integer[]) RETURNS integer AS $$\nBEGIN RETURN mal.intop('/', args); END; $$ LANGUAGE plpgsql;\n\n-- repl_env is environment 0\nINSERT INTO envs.env (env_id, outer_id, data)\n    VALUES (0, NULL, hstore(ARRAY['+', types._function('mal.add'),\n                                  '-', types._function('mal.subtract'),\n                                  '*', types._function('mal.multiply'),\n                                  '/', types._function('mal.divide')]));\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\nBEGIN\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step4_if_fn_do.sql",
    "content": "-- ---------------------------------------------------------\n-- step4_if_fn_do.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        RETURN mal.EVAL(types._nth(ast, 2), let_env);\n    END;\n\n    WHEN 'do' THEN\n    DECLARE\n        result  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 1 LOOP\n            result := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        RETURN result;\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                RETURN mal.EVAL(types._nth(ast, 3), env);\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            RETURN mal.EVAL(types._nth(ast, 2), env);\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id\n            INTO type, fname, fast, fparams, fenv\n            FROM types.value WHERE value_id = evda0;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            RETURN mal.EVAL(fast, envs.new(fenv, fparams, args));\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\nBEGIN\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step5_tco.sql",
    "content": "-- ---------------------------------------------------------\n-- step5_tco.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n  LOOP\n\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        env := let_env;\n        ast := types._nth(ast, 2);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'do' THEN\n    DECLARE\n        ignored  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 2 LOOP\n            ignored := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        ast := types._nth(ast, types._count(ast)-1);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                ast := types._nth(ast, 3);\n                CONTINUE; -- TCO\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            ast := types._nth(ast, 2);\n            CONTINUE; -- TCO\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id\n            INTO type, fname, fast, fparams, fenv\n            FROM types.value WHERE value_id = evda0;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            env := envs.new(fenv, fparams, args);\n            ast := fast;\n            CONTINUE; -- TCO\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar) RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\nBEGIN\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step6_file.sql",
    "content": "-- ---------------------------------------------------------\n-- step6_file.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n  LOOP\n\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        env := let_env;\n        ast := types._nth(ast, 2);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'do' THEN\n    DECLARE\n        ignored  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 2 LOOP\n            ignored := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        ast := types._nth(ast, types._count(ast)-1);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                ast := types._nth(ast, 3);\n                CONTINUE; -- TCO\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            ast := types._nth(ast, 2);\n            CONTINUE; -- TCO\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id\n            INTO type, fname, fast, fparams, fenv\n            FROM types.value WHERE value_id = evda0;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            env := envs.new(fenv, fparams, args);\n            ast := fast;\n            CONTINUE; -- TCO\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\nCREATE FUNCTION mal.mal_eval(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN mal.EVAL(args[1], 0);\nEND; $$ LANGUAGE plpgsql;\nINSERT INTO types.value (type_id, val_string) VALUES (11, 'mal.mal_eval');\n\nSELECT envs.vset(0, 'eval',\n                   (SELECT value_id FROM types.value\n                    WHERE val_string = 'mal.mal_eval')) \\g '/dev/null'\n-- *ARGV* values are set by RUN\nSELECT envs.vset(0, '*ARGV*', mal.READ('()')) \\g '/dev/null'\n\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\nSELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL)\n    RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\n    allargs   integer;\nBEGIN\n    PERFORM envs.vset(0, '*PWD*', types._stringv(pwd));\n\n    IF argstring IS NOT NULL THEN\n        allargs := mal.READ(argstring);\n        PERFORM envs.vset(0, '*ARGV*', types._rest(allargs));\n        PERFORM mal.REP('(load-file ' ||\n                        printer.pr_str(types._first(allargs)) || ')');\n        PERFORM io.close(1);\n        PERFORM io.wait_flushed(1);\n        RETURN 0;\n    END IF;\n\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step7_quote.sql",
    "content": "-- ---------------------------------------------------------\n-- step7_quote.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_loop(elt integer, acc integer) RETURNS integer AS $$\nDECLARE\n    a0 integer;\nBEGIN\n    IF types._list_Q(elt) AND types._count(elt) = 2 THEN\n        a0 := types._first(elt);\n        IF types._symbol_Q(a0) AND a0 = types._symbolv('splice-unquote') THEN\n            RETURN types._list(ARRAY[types._symbolv('concat'), types._nth(elt, 1), acc]);\n        END IF;\n    END IF;\n    RETURN types._list(ARRAY[types._symbolv('cons'), mal.quasiquote(elt), acc]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_foldr(xs integer) RETURNS integer AS $$\nDECLARE\n    elt integer;\n    acc integer := types._list(ARRAY[]::integer[]);\nBEGIN\n    FOREACH elt IN ARRAY types.array_reverse(types._valueToArray(xs)) LOOP\n        acc := mal.qq_loop(elt, acc);\n    END LOOP;\n    RETURN acc;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.quasiquote(ast integer) RETURNS integer AS $$\nBEGIN\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 8 THEN                 --  list\n    DECLARE\n        a0   integer;\n    BEGIN\n        IF types._count(ast) = 2 THEN\n            a0 := types._first(ast);\n            IF types._symbol_Q(a0) AND a0 = types._symbolv('unquote') THEN\n                RETURN types._nth(ast, 1);\n            END IF;\n        END IF;\n        RETURN mal.qq_foldr(ast);\n    END;\n    WHEN 9 THEN                 --  vector\n        RETURN types._list(ARRAY[types._symbolv('vec'), mal.qq_foldr(ast)]);\n    WHEN 7, 10 THEN             --  symbol or map\n        RETURN types._list(ARRAY[types._symbolv('quote'), ast]);\n    ELSE\n        RETURN ast;\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n  LOOP\n\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        env := let_env;\n        ast := types._nth(ast, 2);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'quote' THEN\n        RETURN types._nth(ast, 1);\n\n    WHEN 'quasiquote' THEN\n    BEGIN\n        ast := mal.quasiquote(types._nth(ast, 1));\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'do' THEN\n    DECLARE\n        ignored  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 2 LOOP\n            ignored := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        ast := types._nth(ast, types._count(ast)-1);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                ast := types._nth(ast, 3);\n                CONTINUE; -- TCO\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            ast := types._nth(ast, 2);\n            CONTINUE; -- TCO\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id\n            INTO type, fname, fast, fparams, fenv\n            FROM types.value WHERE value_id = evda0;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            env := envs.new(fenv, fparams, args);\n            ast := fast;\n            CONTINUE; -- TCO\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\nCREATE FUNCTION mal.mal_eval(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN mal.EVAL(args[1], 0);\nEND; $$ LANGUAGE plpgsql;\nINSERT INTO types.value (type_id, val_string) VALUES (11, 'mal.mal_eval');\n\nSELECT envs.vset(0, 'eval',\n                   (SELECT value_id FROM types.value\n                    WHERE val_string = 'mal.mal_eval')) \\g '/dev/null'\n-- *ARGV* values are set by RUN\nSELECT envs.vset(0, '*ARGV*', mal.READ('()')) \\g '/dev/null'\n\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\nSELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL)\n    RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\n    allargs   integer;\nBEGIN\n    PERFORM envs.vset(0, '*PWD*', types._stringv(pwd));\n\n    IF argstring IS NOT NULL THEN\n        allargs := mal.READ(argstring);\n        PERFORM envs.vset(0, '*ARGV*', types._rest(allargs));\n        PERFORM mal.REP('(load-file ' ||\n                        printer.pr_str(types._first(allargs)) || ')');\n        PERFORM io.close(1);\n        PERFORM io.wait_flushed(1);\n        RETURN 0;\n    END IF;\n\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step8_macros.sql",
    "content": "-- ---------------------------------------------------------\n-- step8_macros.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_loop(elt integer, acc integer) RETURNS integer AS $$\nDECLARE\n    a0 integer;\nBEGIN\n    IF types._list_Q(elt) AND types._count(elt) = 2 THEN\n        a0 := types._first(elt);\n        IF types._symbol_Q(a0) AND a0 = types._symbolv('splice-unquote') THEN\n            RETURN types._list(ARRAY[types._symbolv('concat'), types._nth(elt, 1), acc]);\n        END IF;\n    END IF;\n    RETURN types._list(ARRAY[types._symbolv('cons'), mal.quasiquote(elt), acc]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_foldr(xs integer) RETURNS integer AS $$\nDECLARE\n    elt integer;\n    acc integer := types._list(ARRAY[]::integer[]);\nBEGIN\n    FOREACH elt IN ARRAY types.array_reverse(types._valueToArray(xs)) LOOP\n        acc := mal.qq_loop(elt, acc);\n    END LOOP;\n    RETURN acc;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.quasiquote(ast integer) RETURNS integer AS $$\nBEGIN\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 8 THEN                 --  list\n    DECLARE\n        a0   integer;\n    BEGIN\n        IF types._count(ast) = 2 THEN\n            a0 := types._first(ast);\n            IF types._symbol_Q(a0) AND a0 = types._symbolv('unquote') THEN\n                RETURN types._nth(ast, 1);\n            END IF;\n        END IF;\n        RETURN mal.qq_foldr(ast);\n    END;\n    WHEN 9 THEN                 --  vector\n        RETURN types._list(ARRAY[types._symbolv('vec'), mal.qq_foldr(ast)]);\n    WHEN 7, 10 THEN             --  symbol or map\n        RETURN types._list(ARRAY[types._symbolv('quote'), ast]);\n    ELSE\n        RETURN ast;\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n  LOOP\n\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        env := let_env;\n        ast := types._nth(ast, 2);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'quote' THEN\n        RETURN types._nth(ast, 1);\n\n    WHEN 'quasiquote' THEN\n    BEGIN\n        ast := mal.quasiquote(types._nth(ast, 1));\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'defmacro!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        types._macro(mal.EVAL(types._nth(ast, 2), env)));\n\n    WHEN 'do' THEN\n    DECLARE\n        ignored  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 2 LOOP\n            ignored := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        ast := types._nth(ast, types._count(ast)-1);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                ast := types._nth(ast, 3);\n                CONTINUE; -- TCO\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            ast := types._nth(ast, 2);\n            CONTINUE; -- TCO\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        fmacro           boolean;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id, macro\n            INTO type, fname, fast, fparams, fenv, fmacro\n            FROM types.value WHERE value_id = evda0;\n        IF fmacro THEN\n            ast := types._apply(evda0, types._restArray(ast));\n            CONTINUE; -- TCO\n        END IF;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            env := envs.new(fenv, fparams, args);\n            ast := fast;\n            CONTINUE; -- TCO\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\nCREATE FUNCTION mal.mal_eval(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN mal.EVAL(args[1], 0);\nEND; $$ LANGUAGE plpgsql;\nINSERT INTO types.value (type_id, val_string) VALUES (11, 'mal.mal_eval');\n\nSELECT envs.vset(0, 'eval',\n                   (SELECT value_id FROM types.value\n                    WHERE val_string = 'mal.mal_eval')) \\g '/dev/null'\n-- *ARGV* values are set by RUN\nSELECT envs.vset(0, '*ARGV*', mal.READ('()')) \\g '/dev/null'\n\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\nSELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))') \\g '/dev/null'\nSELECT mal.REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL)\n    RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\n    allargs   integer;\nBEGIN\n    PERFORM envs.vset(0, '*PWD*', types._stringv(pwd));\n\n    IF argstring IS NOT NULL THEN\n        allargs := mal.READ(argstring);\n        PERFORM envs.vset(0, '*ARGV*', types._rest(allargs));\n        PERFORM mal.REP('(load-file ' ||\n                        printer.pr_str(types._first(allargs)) || ')');\n        PERFORM io.close(1);\n        PERFORM io.wait_flushed(1);\n        RETURN 0;\n    END IF;\n\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/step9_try.sql",
    "content": "-- ---------------------------------------------------------\n-- step9_try.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_loop(elt integer, acc integer) RETURNS integer AS $$\nDECLARE\n    a0 integer;\nBEGIN\n    IF types._list_Q(elt) AND types._count(elt) = 2 THEN\n        a0 := types._first(elt);\n        IF types._symbol_Q(a0) AND a0 = types._symbolv('splice-unquote') THEN\n            RETURN types._list(ARRAY[types._symbolv('concat'), types._nth(elt, 1), acc]);\n        END IF;\n    END IF;\n    RETURN types._list(ARRAY[types._symbolv('cons'), mal.quasiquote(elt), acc]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_foldr(xs integer) RETURNS integer AS $$\nDECLARE\n    elt integer;\n    acc integer := types._list(ARRAY[]::integer[]);\nBEGIN\n    FOREACH elt IN ARRAY types.array_reverse(types._valueToArray(xs)) LOOP\n        acc := mal.qq_loop(elt, acc);\n    END LOOP;\n    RETURN acc;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.quasiquote(ast integer) RETURNS integer AS $$\nBEGIN\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 8 THEN                 --  list\n    DECLARE\n        a0   integer;\n    BEGIN\n        IF types._count(ast) = 2 THEN\n            a0 := types._first(ast);\n            IF types._symbol_Q(a0) AND a0 = types._symbolv('unquote') THEN\n                RETURN types._nth(ast, 1);\n            END IF;\n        END IF;\n        RETURN mal.qq_foldr(ast);\n    END;\n    WHEN 9 THEN                 --  vector\n        RETURN types._list(ARRAY[types._symbolv('vec'), mal.qq_foldr(ast)]);\n    WHEN 7, 10 THEN             --  symbol or map\n        RETURN types._list(ARRAY[types._symbolv('quote'), ast]);\n    ELSE\n        RETURN ast;\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n  LOOP\n\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        env := let_env;\n        ast := types._nth(ast, 2);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'quote' THEN\n        RETURN types._nth(ast, 1);\n\n    WHEN 'quasiquote' THEN\n    BEGIN\n        ast := mal.quasiquote(types._nth(ast, 1));\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'defmacro!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        types._macro(mal.EVAL(types._nth(ast, 2), env)));\n\n    WHEN 'try*' THEN\n    DECLARE\n        a1 constant integer := types._nth(ast, 1);\n        a2          integer;\n    BEGIN\n        IF types._count(ast) >= 3 THEN\n            a2 = types._nth(ast, 2);\n            IF types._valueToString(types._nth(a2, 0)) = 'catch*' THEN\n                BEGIN\n                    RETURN mal.EVAL(a1, env);\n                EXCEPTION WHEN OTHERS THEN\n                    env := envs.new(env);\n                    PERFORM envs.set(env, types._nth(a2, 1),\n                                     types._stringv(SQLERRM));\n                    ast := types._nth(a2, 2);\n                    CONTINUE; -- TCO\n                END;\n            END IF;\n        END IF;\n        ast := a1;\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'do' THEN\n    DECLARE\n        ignored  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 2 LOOP\n            ignored := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        ast := types._nth(ast, types._count(ast)-1);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                ast := types._nth(ast, 3);\n                CONTINUE; -- TCO\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            ast := types._nth(ast, 2);\n            CONTINUE; -- TCO\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        fmacro           boolean;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id, macro\n            INTO type, fname, fast, fparams, fenv, fmacro\n            FROM types.value WHERE value_id = evda0;\n        IF fmacro THEN\n            ast := types._apply(evda0, types._restArray(ast));\n            CONTINUE; -- TCO\n        END IF;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            env := envs.new(fenv, fparams, args);\n            ast := fast;\n            CONTINUE; -- TCO\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\nCREATE FUNCTION mal.mal_eval(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN mal.EVAL(args[1], 0);\nEND; $$ LANGUAGE plpgsql;\nINSERT INTO types.value (type_id, val_string) VALUES (11, 'mal.mal_eval');\n\nSELECT envs.vset(0, 'eval',\n                   (SELECT value_id FROM types.value\n                    WHERE val_string = 'mal.mal_eval')) \\g '/dev/null'\n-- *ARGV* values are set by RUN\nSELECT envs.vset(0, '*ARGV*', mal.READ('()')) \\g '/dev/null'\n\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\nSELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))') \\g '/dev/null'\nSELECT mal.REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL)\n    RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\n    allargs   integer;\nBEGIN\n    PERFORM envs.vset(0, '*PWD*', types._stringv(pwd));\n\n    IF argstring IS NOT NULL THEN\n        allargs := mal.READ(argstring);\n        PERFORM envs.vset(0, '*ARGV*', types._rest(allargs));\n        PERFORM mal.REP('(load-file ' ||\n                        printer.pr_str(types._first(allargs)) || ')');\n        PERFORM io.close(1);\n        PERFORM io.wait_flushed(1);\n        RETURN 0;\n    END IF;\n\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/stepA_mal.sql",
    "content": "-- ---------------------------------------------------------\n-- stepA_mal.sql\n\n\\i init.sql\n\\i io.sql\n\\i types.sql\n\\i reader.sql\n\\i printer.sql\n\\i envs.sql\n\\i core.sql\n\n-- ---------------------------------------------------------\n\nCREATE SCHEMA mal;\n\n-- read\nCREATE FUNCTION mal.READ(line varchar) RETURNS integer AS $$\nBEGIN\n    RETURN reader.read_str(line);\nEND; $$ LANGUAGE plpgsql;\n\n-- eval\n\nCREATE FUNCTION mal.eval_debug(ast integer, env integer) RETURNS void AS $$\nDECLARE\n    val constant integer := envs.get(env, 'DEBUG-EVAL');\nBEGIN\n    IF val IS NOT NULL THEN\n        IF (SELECT type_id FROM types.value WHERE value_id = val) NOT IN (0, 1)\n        THEN\n            PERFORM io.writeline(format('EVAL: %s [%s]', mal.PRINT(ast), ast));\n        END IF;\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_loop(elt integer, acc integer) RETURNS integer AS $$\nDECLARE\n    a0 integer;\nBEGIN\n    IF types._list_Q(elt) AND types._count(elt) = 2 THEN\n        a0 := types._first(elt);\n        IF types._symbol_Q(a0) AND a0 = types._symbolv('splice-unquote') THEN\n            RETURN types._list(ARRAY[types._symbolv('concat'), types._nth(elt, 1), acc]);\n        END IF;\n    END IF;\n    RETURN types._list(ARRAY[types._symbolv('cons'), mal.quasiquote(elt), acc]);\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.qq_foldr(xs integer) RETURNS integer AS $$\nDECLARE\n    elt integer;\n    acc integer := types._list(ARRAY[]::integer[]);\nBEGIN\n    FOREACH elt IN ARRAY types.array_reverse(types._valueToArray(xs)) LOOP\n        acc := mal.qq_loop(elt, acc);\n    END LOOP;\n    RETURN acc;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.quasiquote(ast integer) RETURNS integer AS $$\nBEGIN\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 8 THEN                 --  list\n    DECLARE\n        a0   integer;\n    BEGIN\n        IF types._count(ast) = 2 THEN\n            a0 := types._first(ast);\n            IF types._symbol_Q(a0) AND a0 = types._symbolv('unquote') THEN\n                RETURN types._nth(ast, 1);\n            END IF;\n        END IF;\n        RETURN mal.qq_foldr(ast);\n    END;\n    WHEN 9 THEN                 --  vector\n        RETURN types._list(ARRAY[types._symbolv('vec'), mal.qq_foldr(ast)]);\n    WHEN 7, 10 THEN             --  symbol or map\n        RETURN types._list(ARRAY[types._symbolv('quote'), ast]);\n    ELSE\n        RETURN ast;\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_symbol(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        symkey constant varchar := types._valueToString(ast);\n        result constant integer := envs.get(env, symkey);\n    BEGIN\n         IF result IS NULL THEN\n            RAISE EXCEPTION '''%'' not found', symkey;\n        END IF;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_vector(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        seq    constant integer[] := types._valueToArray(ast);\n        eseq            integer[];\n        result          integer;\n    BEGIN\n        -- Evaluate each entry creating a new sequence\n        FOR i IN 1 .. COALESCE(array_length(seq, 1), 0) LOOP\n            eseq[i] := mal.EVAL(seq[i], env);\n        END LOOP;\n        INSERT INTO types.value (type_id, val_seq) VALUES (9, eseq)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.eval_map(ast integer, env integer) RETURNS integer\nAS $$\n    DECLARE\n        hash   hstore;\n        ehash  hstore;\n        kv     RECORD;\n        e      integer;\n        result integer;\n    BEGIN\n        SELECT val_hash INTO hash FROM types.value WHERE value_id = ast;\n        -- Evaluate each value for every key/value\n        FOR kv IN SELECT * FROM each(hash) LOOP\n            e := mal.EVAL(CAST(kv.value AS integer), env);\n            IF ehash IS NULL THEN\n                ehash := hstore(kv.key, CAST(e AS varchar));\n            ELSE\n                ehash := ehash || hstore(kv.key, CAST(e AS varchar));\n            END IF;\n        END LOOP;\n        INSERT INTO types.value (type_id, val_hash) VALUES (10, ehash)\n            RETURNING value_id INTO result;\n        RETURN result;\n    END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION mal.EVAL(ast integer, env integer) RETURNS integer AS $$\nDECLARE\n    a0       integer;\nBEGIN\n  LOOP\n\n    PERFORM mal.eval_debug(ast, env);\n\n    CASE type_id FROM types.value WHERE value_id = ast\n    WHEN 7  THEN RETURN mal.eval_symbol(ast, env);\n    WHEN 8  THEN NULL;    --  List, proceed after this case statement.\n    WHEN 9  THEN RETURN mal.eval_vector(ast, env);\n    WHEN 10 THEN RETURN mal.eval_map(ast, env);\n    ELSE         RETURN ast;\n    END CASE;\n\n    IF types._count(ast) = 0 THEN\n        RETURN ast;\n    END IF;\n\n    a0 := types._first(ast);\n    IF types._symbol_Q(a0) THEN\n\n    CASE val_string FROM types.value WHERE value_id = a0\n\n    WHEN 'def!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        mal.EVAL(types._nth(ast, 2), env));\n\n    WHEN 'let*' THEN\n    DECLARE\n        let_env constant integer   := envs.new(env);\n        binds   constant integer[] := types._valueToArray(types._nth(ast, 1));\n    BEGIN\n        FOR idx IN 1 .. array_length(binds, 1) BY 2 LOOP\n            PERFORM envs.set(let_env, binds[idx],\n                                      mal.EVAL(binds[idx+1], let_env));\n        END LOOP;\n        env := let_env;\n        ast := types._nth(ast, 2);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'quote' THEN\n        RETURN types._nth(ast, 1);\n\n    WHEN 'quasiquote' THEN\n    BEGIN\n        ast := mal.quasiquote(types._nth(ast, 1));\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'defmacro!' THEN\n        RETURN envs.set(env, types._nth(ast, 1),\n                        types._macro(mal.EVAL(types._nth(ast, 2), env)));\n\n    WHEN 'try*' THEN\n    DECLARE\n        a1 constant integer := types._nth(ast, 1);\n        a2          integer;\n    BEGIN\n        IF types._count(ast) >= 3 THEN\n            a2 = types._nth(ast, 2);\n            IF types._valueToString(types._nth(a2, 0)) = 'catch*' THEN\n                BEGIN\n                    RETURN mal.EVAL(a1, env);\n                EXCEPTION WHEN OTHERS THEN\n                    env := envs.new(env);\n                    PERFORM envs.set(env, types._nth(a2, 1),\n                                     types._stringv(SQLERRM));\n                    ast := types._nth(a2, 2);\n                    CONTINUE; -- TCO\n                END;\n            END IF;\n        END IF;\n        ast := a1;\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'do' THEN\n    DECLARE\n        ignored  integer;\n    BEGIN\n        FOR i IN 1 .. types._count(ast) - 2 LOOP\n            ignored := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        ast := types._nth(ast, types._count(ast)-1);\n        CONTINUE; -- TCO\n    END;\n\n    WHEN 'if' THEN\n        IF (SELECT type_id FROM types.value\n            WHERE value_id = mal.EVAL(types._nth(ast, 1), env))\n           IN (0, 1)\n        THEN  --  nil or false\n            IF types._count(ast) > 3 THEN\n                ast := types._nth(ast, 3);\n                CONTINUE; -- TCO\n            ELSE\n                RETURN 0; -- nil\n            END IF;\n        ELSE\n            ast := types._nth(ast, 2);\n            CONTINUE; -- TCO\n        END IF;\n\n    WHEN 'fn*' THEN\n        RETURN types._malfunc(types._nth(ast, 2), types._nth(ast, 1), env);\n\n    ELSE\n        NULL;\n    END CASE;\n    END IF;\n    --  Apply phase.\n    DECLARE\n        type             integer;\n        fname            varchar;\n        fast             integer;\n        fparams          integer;\n        fenv             integer;\n        fmacro           boolean;\n        args             integer[] := ARRAY[]::integer[];\n        result           integer;\n        evda0   constant integer := mal.EVAL(a0, env);\n    BEGIN\n        SELECT type_id, val_string, ast_id, params_id, env_id, macro\n            INTO type, fname, fast, fparams, fenv, fmacro\n            FROM types.value WHERE value_id = evda0;\n        IF fmacro THEN\n            ast := types._apply(evda0, types._restArray(ast));\n            CONTINUE; -- TCO\n        END IF;\n        FOR i in 1 .. types._count(ast) - 1 LOOP\n            args[i] := mal.EVAL(types._nth(ast, i), env);\n        END LOOP;\n        IF type = 11 THEN\n            EXECUTE format('SELECT %s($1);', fname)\n                INTO result USING args;\n            RETURN result;\n        ELSIF type = 12 THEN\n            env := envs.new(fenv, fparams, args);\n            ast := fast;\n            CONTINUE; -- TCO\n        ELSE\n            RAISE EXCEPTION 'Invalid function call';\n        END IF;\n    END;\n  END LOOP;\nEND; $$ LANGUAGE plpgsql;\n\n-- print\nCREATE FUNCTION mal.PRINT(exp integer) RETURNS varchar AS $$\nBEGIN\n    RETURN printer.pr_str(exp);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- repl\n\n-- repl_env is environment 0\n\nCREATE FUNCTION mal.REP(line varchar) RETURNS varchar AS $$\nBEGIN\n    RETURN mal.PRINT(mal.EVAL(mal.READ(line), 0));\nEND; $$ LANGUAGE plpgsql;\n\n-- core.sql: defined using SQL (in core.sql)\n-- repl_env is created and populated with core functions in by core.sql\nCREATE FUNCTION mal.mal_eval(args integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN mal.EVAL(args[1], 0);\nEND; $$ LANGUAGE plpgsql;\nINSERT INTO types.value (type_id, val_string) VALUES (11, 'mal.mal_eval');\n\nSELECT envs.vset(0, 'eval',\n                   (SELECT value_id FROM types.value\n                    WHERE val_string = 'mal.mal_eval')) \\g '/dev/null'\n-- *ARGV* values are set by RUN\nSELECT envs.vset(0, '*ARGV*', mal.READ('()')) \\g '/dev/null'\n\n\n-- core.mal: defined using the language itself\nSELECT mal.REP('(def! *host-language* \"plpqsql\")') \\g '/dev/null'\nSELECT mal.REP('(def! not (fn* (a) (if a false true)))') \\g '/dev/null'\nSELECT mal.REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))') \\g '/dev/null'\nSELECT mal.REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))') \\g '/dev/null'\n\nCREATE FUNCTION mal.MAIN(pwd varchar, argstring varchar DEFAULT NULL)\n    RETURNS integer AS $$\nDECLARE\n    line      varchar;\n    output    varchar;\n    allargs   integer;\nBEGIN\n    PERFORM envs.vset(0, '*PWD*', types._stringv(pwd));\n\n    IF argstring IS NOT NULL THEN\n        allargs := mal.READ(argstring);\n        PERFORM envs.vset(0, '*ARGV*', types._rest(allargs));\n        PERFORM mal.REP('(load-file ' ||\n                        printer.pr_str(types._first(allargs)) || ')');\n        PERFORM io.close(1);\n        PERFORM io.wait_flushed(1);\n        RETURN 0;\n    END IF;\n\n    PERFORM mal.REP('(println (str \"Mal [\" *host-language* \"]\"))');\n    WHILE true\n    LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line IS NULL THEN\n                PERFORM io.close(1);\n                RETURN 0;\n            END IF;\n            IF line NOT IN ('', E'\\n') THEN\n                output := mal.REP(line);\n                PERFORM io.writeline(output);\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                PERFORM io.writeline('Error: ' || SQLERRM);\n        END;\n    END LOOP;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/types.sql",
    "content": "-- ---------------------------------------------------------\n-- persistent values\n\n-- list of types for type_id\n-- 0:  nil\n-- 1:  false\n-- 2:  true\n-- 3:  integer\n-- 4:  float\n-- 5:  string\n-- 6:  keyword (not used, uses prefixed string)\n-- 7:  symbol\n-- 8:  list\n-- 9:  vector\n-- 10: hashmap\n-- 11: function\n-- 12: malfunc\n-- 13: atom\n\nCREATE SCHEMA types\n\n    CREATE SEQUENCE value_id_seq START WITH 3 -- skip nil, false, true\n\n    CREATE TABLE value (\n        value_id        integer NOT NULL DEFAULT nextval('value_id_seq'),\n        type_id         integer NOT NULL,\n        val_int         bigint,    -- set for integers\n        val_string      varchar,   -- set for strings, keywords, symbols,\n                                -- and native functions (function name)\n        val_seq         integer[], -- set for lists and vectors\n        val_hash        hstore,    -- set for hash-maps\n        ast_id          integer,   -- set for malfunc\n        params_id       integer,   -- set for malfunc\n        env_id          integer,   -- set for malfunc\n        macro           boolean,   -- set for malfunc\n        meta_id         integer    -- can be set for any collection\n    );\n\nALTER TABLE types.value ADD CONSTRAINT pk_value_id\n    PRIMARY KEY (value_id);\n-- drop sequence when table dropped\nALTER SEQUENCE types.value_id_seq OWNED BY types.value.value_id;\nALTER TABLE types.value ADD CONSTRAINT fk_meta_id\n    FOREIGN KEY (meta_id) REFERENCES types.value(value_id);\nALTER TABLE types.value ADD CONSTRAINT fk_params_id\n    FOREIGN KEY (params_id) REFERENCES types.value(value_id);\n\nCREATE INDEX ON types.value (value_id, type_id);\n\nINSERT INTO types.value (value_id, type_id) VALUES (0, 0); -- nil\nINSERT INTO types.value (value_id, type_id) VALUES (1, 1); -- false\nINSERT INTO types.value (value_id, type_id) VALUES (2, 2); -- true\n\n\n-- ---------------------------------------------------------\n-- general functions\n\nCREATE FUNCTION types._wraptf(val boolean) RETURNS integer AS $$\nBEGIN\n    IF val THEN\n        RETURN 2;\n    ELSE\n        RETURN 1;\n    END IF;\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- pun both NULL and false to false\nCREATE FUNCTION types._tf(val boolean) RETURNS boolean AS $$\nBEGIN\n    IF val IS NULL OR val = false THEN\n        RETURN false;\n    END IF;\n    RETURN true;\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- pun both NULL and 0 to false\nCREATE FUNCTION types._tf(val integer) RETURNS boolean AS $$\nBEGIN\n    IF val IS NULL OR val = 0 THEN\n        RETURN false;\n    END IF;\n    RETURN true;\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- return the type of the given value_id\nCREATE FUNCTION types._type(obj integer) RETURNS integer AS $$\nBEGIN\n    RETURN (SELECT type_id FROM types.value WHERE value_id = obj);\nEND; $$ LANGUAGE plpgsql;\n\n\nCREATE FUNCTION types._equal_Q(a integer, b integer) RETURNS boolean AS $$\nDECLARE\n    atype  integer;\n    btype  integer;\n    anum   bigint;\n    bnum   bigint;\n    avid   integer;\n    bvid   integer;\n    aseq   integer[];\n    bseq   integer[];\n    ahash  hstore;\n    bhash  hstore;\n    kv     RECORD;\n    i      integer;\nBEGIN\n    atype := types._type(a);\n    btype := types._type(b);\n    IF NOT ((atype = btype) OR\n            (types._sequential_Q(a) AND types._sequential_Q(b))) THEN\n        RETURN false;\n    END IF;\n    CASE\n    WHEN atype = 3 THEN -- integer\n        SELECT val_int FROM types.value INTO anum WHERE value_id = a;\n        SELECT val_int FROM types.value INTO bnum WHERE value_id = b;\n        RETURN anum = bnum;\n    WHEN atype = 5 OR atype = 7 THEN -- string/symbol\n        RETURN types._valueToString(a) = types._valueToString(b);\n    WHEN atype IN (8, 9) THEN -- list/vector\n        IF types._count(a) <> types._count(b) THEN\n            RETURN false;\n        END IF;\n        SELECT val_seq INTO aseq FROM types.value WHERE value_id = a;\n        SELECT val_seq INTO bseq FROM types.value WHERE value_id = b;\n        FOR i IN 1 .. types._count(a)\n        LOOP\n            IF NOT types._equal_Q(aseq[i], bseq[i]) THEN\n                return false;\n            END IF;\n        END LOOP;\n        RETURN true;\n    WHEN atype = 10 THEN -- hash-map\n        SELECT val_hash INTO ahash FROM types.value WHERE value_id = a;\n        SELECT val_hash INTO bhash FROM types.value WHERE value_id = b;\n        IF array_length(akeys(ahash), 1) <> array_length(akeys(bhash), 1) THEN\n            RETURN false;\n        END IF;\n        FOR kv IN SELECT * FROM each(ahash) LOOP\n            avid := CAST((ahash -> kv.key) AS integer);\n            bvid := CAST((bhash -> kv.key) AS integer);\n            IF bvid IS NULL OR NOT types._equal_Q(avid, bvid) THEN\n                return false;\n            END IF;\n        END LOOP;\n        RETURN true;\n    ELSE\n        RETURN a = b;\n    END CASE;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- _clone:\n-- take a value_id of a collection\n-- returns a new value_id of a cloned collection\nCREATE FUNCTION types._clone(id integer) RETURNS integer AS $$\nDECLARE\n    result       integer;\nBEGIN\n    INSERT INTO types.value (type_id,val_int,val_string,val_seq,val_hash,\n                       ast_id,params_id,env_id,meta_id)\n        (SELECT type_id,val_int,val_string,val_seq,val_hash,\n                ast_id,params_id,env_id,meta_id\n              FROM types.value\n              WHERE value_id = id)\n        RETURNING value_id INTO result;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- ---------------------------------------------------------\n-- scalar functions\n\n\n-- _nil_Q:\n-- takes a value_id\n-- returns the whether value_id is nil\nCREATE FUNCTION types._nil_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN id = 0;\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- _true_Q:\n-- takes a value_id\n-- returns the whether value_id is true\nCREATE FUNCTION types._true_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN id = 2;\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- _false_Q:\n-- takes a value_id\n-- returns the whether value_id is false\nCREATE FUNCTION types._false_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN id = 1;\nEND; $$ LANGUAGE plpgsql IMMUTABLE;\n\n-- _string_Q:\n-- takes a value_id\n-- returns the whether value_id is string type\nCREATE FUNCTION types._string_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    IF (SELECT 1 FROM types.value WHERE type_id = 5 AND value_id = id) THEN\n        RETURN NOT types._keyword_Q(id);\n    END IF;\n    RETURN false;\nEND; $$ LANGUAGE plpgsql;\n\n-- _number_Q:\n-- takes a value_id\n-- returns the whether value_id is integer or float type\nCREATE FUNCTION types._number_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE (type_id = 3 OR type_id = 4)\n            AND value_id = id));\nEND; $$ LANGUAGE plpgsql;\n\n-- _valueToString:\n-- takes a value_id for a string\n-- returns the varchar value of the string\nCREATE FUNCTION types._valueToString(sid integer) RETURNS varchar AS $$\nBEGIN\n    RETURN (SELECT val_string FROM types.value WHERE value_id = sid);\nEND; $$ LANGUAGE plpgsql;\n\n-- _stringish:\n-- takes a varchar string\n-- returns the value_id of a stringish type (string, symbol, keyword)\nCREATE FUNCTION types._stringish(str varchar, type integer) RETURNS integer AS $$\nDECLARE\n    result  integer;\nBEGIN\n    -- TODO: share string data between string types\n    -- lookup if it exists\n    SELECT value_id FROM types.value INTO result\n        WHERE val_string = str AND type_id = type;\n    IF result IS NULL THEN\n        -- Create string entry\n        INSERT INTO types.value (type_id, val_string)\n            VALUES (type, str)\n            RETURNING value_id INTO result;\n    END IF;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- _stringv:\n-- takes a varchar string\n-- returns the value_id of a string (new or existing)\nCREATE FUNCTION types._stringv(str varchar) RETURNS integer AS $$\nBEGIN\n    RETURN types._stringish(str, 5);\nEND; $$ LANGUAGE plpgsql;\n\n-- _keywordv:\n-- takes a varchar string\n-- returns the value_id of a keyword (new or existing)\nCREATE FUNCTION types._keywordv(name varchar) RETURNS integer AS $$\nBEGIN\n    RETURN types._stringish(chr(CAST(x'7f' AS integer)) || name, 5);\nEND; $$ LANGUAGE plpgsql;\n\n-- _keyword_Q:\n-- takes a value_id\n-- returns the whether value_id is keyword type\nCREATE FUNCTION types._keyword_Q(id integer) RETURNS boolean AS $$\nDECLARE\n    str  varchar;\nBEGIN\n    IF (SELECT 1 FROM types.value WHERE type_id = 5 AND value_id = id) THEN\n        str := types._valueToString(id);\n        IF char_length(str) > 0 AND\n           chr(CAST(x'7f' AS integer)) = substring(str FROM 1 FOR 1) THEN\n            RETURN true;\n        END IF;\n    END IF;\n    RETURN false;\nEND; $$ LANGUAGE plpgsql;\n\n-- _symbolv:\n-- takes a varchar string\n-- returns the value_id of a symbol (new or existing)\nCREATE FUNCTION types._symbolv(name varchar) RETURNS integer AS $$\nBEGIN\n    RETURN types._stringish(name, 7);\nEND; $$ LANGUAGE plpgsql;\n\n-- _symbol_Q:\n-- takes a value_id\n-- returns the whether value_id is symbol type\nCREATE FUNCTION types._symbol_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE type_id = 7 AND value_id = id));\nEND; $$ LANGUAGE plpgsql;\n\n-- _numToValue:\n-- takes an bigint number\n-- returns the value_id for the number\nCREATE FUNCTION types._numToValue(num bigint) RETURNS integer AS $$\nDECLARE\n    result  integer;\nBEGIN\n    SELECT value_id FROM types.value INTO result\n        WHERE val_int = num AND type_id = 3;\n    IF result IS NULL THEN\n        -- Create an integer entry\n        INSERT INTO types.value (type_id, val_int)\n            VALUES (3, num)\n            RETURNING value_id INTO result;\n    END IF;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- _fn_Q:\n-- takes a value_id\n-- returns the whether value_id is a function\nCREATE FUNCTION types._fn_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE (type_id = 11 OR type_id = 12)\n            AND macro IS NULL\n            AND value_id = id));\nEND; $$ LANGUAGE plpgsql;\n\n-- _macro_Q:\n-- takes a value_id\n-- returns the whether value_id is a macro\nCREATE FUNCTION types._macro_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE type_id = 12\n            AND macro IS TRUE\n            AND value_id = id));\nEND; $$ LANGUAGE plpgsql;\n\n-- ---------------------------------------------------------\n-- sequence functions\n\n-- _sequential_Q:\n-- return true if obj value_id is a list or vector\nCREATE FUNCTION types._sequential_Q(obj integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n                WHERE value_id = obj AND (type_id = 8 OR type_id = 9)));\nEND; $$ LANGUAGE plpgsql;\n\n-- _collection:\n-- takes a array of value_id integers\n-- returns the value_id of a new list (8), vector (9) or hash-map (10)\nCREATE FUNCTION types._collection(items integer[], type integer) RETURNS integer AS $$\nDECLARE\n    vid  integer;\nBEGIN\n    IF type IN (8, 9) THEN\n        INSERT INTO types.value (type_id, val_seq)\n            VALUES (type, items)\n            RETURNING value_id INTO vid;\n    ELSIF type = 10 THEN\n        IF (array_length(items, 1) % 2) = 1 THEN\n            RAISE EXCEPTION 'hash-map: odd number of arguments';\n        END IF;\n        INSERT INTO types.value (type_id, val_hash)\n            VALUES (type, hstore(CAST(items AS varchar[])))\n            RETURNING value_id INTO vid;\n    END IF;\n    RETURN vid;\nEND; $$ LANGUAGE plpgsql;\n\n\n-- _list:\n-- takes a array of value_id integers\n-- returns the value_id of a new list\nCREATE FUNCTION types._list(items integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._collection(items, 8);\nEND; $$ LANGUAGE plpgsql;\n\n-- _vector:\n-- takes a array of value_id integers\n-- returns the value_id of a new list\nCREATE FUNCTION types._vector(items integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._collection(items, 9);\nEND; $$ LANGUAGE plpgsql;\n\n-- _list_Q:\n-- return true if obj value_id is a list\nCREATE FUNCTION types._list_Q(obj integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE value_id = obj and type_id = 8));\nEND; $$ LANGUAGE plpgsql;\n\n-- _vector_Q:\n-- return true if obj value_id is a list\nCREATE FUNCTION types._vector_Q(obj integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE value_id = obj and type_id = 9));\nEND; $$ LANGUAGE plpgsql;\n\n\n-- _valueToArray:\n-- takes an value_id referring to a list or vector\n-- returns an array of the value_ids from the list/vector\nCREATE FUNCTION types._valueToArray(seq integer) RETURNS integer[] AS $$\nDECLARE\n    result  integer[];\nBEGIN\n    result := (SELECT val_seq FROM types.value WHERE value_id = seq);\n    IF result IS NULL THEN\n        result := ARRAY[]::integer[];\n    END IF;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- From: https://wiki.postgresql.org/wiki/Array_reverse\nCREATE FUNCTION types.array_reverse(a integer[]) RETURNS integer[] AS $$\nSELECT ARRAY(\n    SELECT a[i]\n    FROM generate_subscripts(a,1) AS s(i)\n    ORDER BY i DESC\n);\n$$ LANGUAGE 'sql' STRICT IMMUTABLE;\n\n\n-- _nth:\n-- takes value_id and an index\n-- returns the value_id of nth element in list/vector\nCREATE FUNCTION types._nth(seq_id integer, indx integer) RETURNS integer AS $$\nDECLARE\n    result  integer;\nBEGIN\n    RETURN (SELECT val_seq[indx+1] FROM types.value WHERE value_id = seq_id);\nEND; $$ LANGUAGE plpgsql;\n\n-- _first:\n-- takes value_id\n-- returns the value_id of first element in list/vector\nCREATE FUNCTION types._first(seq_id integer) RETURNS integer AS $$\nBEGIN\n    RETURN types._nth(seq_id, 0);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- _restArray:\n-- takes value_id\n-- returns the array of value_ids\nCREATE FUNCTION types._restArray(seq_id integer) RETURNS integer[] AS $$\nDECLARE\n    result  integer[];\nBEGIN\n    result := (SELECT val_seq FROM types.value WHERE value_id = seq_id);\n    RETURN result[2:array_length(result, 1)];\nEND; $$ LANGUAGE plpgsql;\n\n-- _slice:\n-- takes value_id, a first index and an last index\n-- returns the value_id of new list from first (inclusive) to last (exclusive)\nCREATE FUNCTION types._slice(seq_id integer, first integer, last integer)\nRETURNS integer AS $$\nDECLARE\n    seq            integer[];\n    vid            integer;\n    i              integer;\n    result         integer;\nBEGIN\n    SELECT val_seq INTO seq FROM types.value WHERE value_id = seq_id;\n    INSERT INTO types.value (type_id, val_seq)\n        VALUES (8, seq[first+1:last])\n        RETURNING value_id INTO result;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- _rest:\n-- takes value_id\n-- returns the value_id of new list\nCREATE FUNCTION types._rest(seq_id integer) RETURNS integer AS $$\nBEGIN\n    RETURN types._slice(seq_id, 1, types._count(seq_id));\nEND; $$ LANGUAGE plpgsql;\n\n-- _count:\n-- takes value_id\n-- returns a count (not value_id)\nCREATE FUNCTION types._count(seq_id integer) RETURNS integer AS $$\nDECLARE\n    result  integer[];\nBEGIN\n    result := (SELECT val_seq FROM types.value\n                         WHERE value_id = seq_id);\n    RETURN COALESCE(array_length(result, 1), 0);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- ---------------------------------------------------------\n-- hash-map functions\n\n-- _hash_map:\n-- return value_id of a new hash-map\nCREATE FUNCTION types._hash_map(items integer[]) RETURNS integer AS $$\nBEGIN\n    RETURN types._collection(items, 10);\nEND; $$ LANGUAGE plpgsql;\n\n-- _hash_map_Q:\n-- return true if obj value_id is a list\nCREATE FUNCTION types._hash_map_Q(obj integer) RETURNS boolean AS $$\nBEGIN\n    RETURN types._tf((SELECT 1 FROM types.value\n            WHERE value_id = obj and type_id = 10));\nEND; $$ LANGUAGE plpgsql;\n\n-- _assoc_BANG:\n-- return value_id of the hash-map with new elements appended\nCREATE FUNCTION types._assoc_BANG(hm integer, items integer[]) RETURNS integer AS $$\nDECLARE\n    hash  hstore;\nBEGIN\n    IF (array_length(items, 1) % 2) = 1 THEN\n        RAISE EXCEPTION 'hash-map: odd number of arguments';\n    END IF;\n    SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;\n    IF hash IS NULL THEN\n        UPDATE types.value SET val_hash = hstore(CAST(items AS varchar[]))\n            WHERE value_id = hm;\n    ELSE\n        UPDATE types.value\n            SET val_hash = hash || hstore(CAST(items AS varchar[]))\n            WHERE value_id = hm;\n    END IF;\n    RETURN hm;\nEND; $$ LANGUAGE plpgsql;\n\n-- _dissoc_BANG:\n-- return value_id of the hash-map with elements removed\nCREATE FUNCTION types._dissoc_BANG(hm integer, items integer[]) RETURNS integer AS $$\nDECLARE\n    hash  hstore;\nBEGIN\n    SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;\n    UPDATE types.value SET val_hash = hash - CAST(items AS varchar[])\n            WHERE value_id = hm;\n    RETURN hm;\nEND; $$ LANGUAGE plpgsql;\n\n-- _get:\n-- return value_id of the hash-map entry matching key\nCREATE FUNCTION types._get(hm integer, key varchar) RETURNS integer AS $$\nDECLARE\n    hash  hstore;\nBEGIN\n    SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;\n    RETURN hash -> CAST(types._stringv(key) AS varchar);\nEND; $$ LANGUAGE plpgsql;\n\n-- _contains_Q:\n-- return true if hash-map contains entry matching key\nCREATE FUNCTION types._contains_Q(hm integer, key varchar) RETURNS boolean AS $$\nDECLARE\n    hash  hstore;\nBEGIN\n    SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;\n    RETURN types._tf(hash ? CAST(types._stringv(key) AS varchar));\nEND; $$ LANGUAGE plpgsql;\n\n-- _keys:\n-- return array of key value_ids from hash-map\nCREATE FUNCTION types._keys(hm integer) RETURNS integer[] AS $$\nDECLARE\n    hash  hstore;\nBEGIN\n    SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;\n    RETURN CAST(akeys(hash) AS integer[]);\nEND; $$ LANGUAGE plpgsql;\n\n-- _vals:\n-- return array of value value_ids from hash-map\nCREATE FUNCTION types._vals(hm integer) RETURNS integer[] AS $$\nDECLARE\n    hash  hstore;\nBEGIN\n    SELECT val_hash INTO hash FROM types.value WHERE value_id = hm;\n    RETURN CAST(avals(hash) AS integer[]);\nEND; $$ LANGUAGE plpgsql;\n\n\n-- ---------------------------------------------------------\n-- function functions\n\n-- _function:\n-- takes a function name\n-- returns the value_id of a new \nCREATE FUNCTION types._function(fname varchar)\nRETURNS varchar AS $$\nDECLARE\n    result  integer;\nBEGIN\n    INSERT INTO types.value (type_id, val_string)\n        VALUES (11, fname)\n        RETURNING value_id INTO result;\n    RETURN CAST(result AS varchar);\nEND; $$ LANGUAGE plpgsql;\n\n-- _malfunc:\n-- takes a ast value_id, params value_id and env_id\n-- returns the value_id of a new function\nCREATE FUNCTION types._malfunc(ast integer, params integer, env integer)\nRETURNS integer AS $$\nDECLARE\n    cid     integer = NULL;\n    result  integer;\nBEGIN\n    -- Create function entry\n    INSERT INTO types.value (type_id, ast_id, params_id, env_id)\n        VALUES (12, ast, params, env)\n        RETURNING value_id into result;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- _macro:\nCREATE FUNCTION types._macro(func integer) RETURNS integer AS $$\nDECLARE\n    newfunc  integer;\n    cid      integer;\nBEGIN\n    newfunc := types._clone(func);\n    UPDATE types.value SET macro = true WHERE value_id = newfunc;\n    RETURN newfunc;\nEND; $$ LANGUAGE plpgsql;\n\nCREATE FUNCTION types._apply(func integer, args integer[]) RETURNS integer AS $$\nDECLARE\n    type     integer;\n    fcid     integer;\n    fname    varchar;\n    fast     integer;\n    fparams  integer;\n    fenv     integer;\n    result   integer;\nBEGIN\n    SELECT type_id, val_string, ast_id, params_id, env_id\n        INTO type, fname, fast, fparams, fenv\n        FROM types.value WHERE value_id = func;\n    IF type = 11 THEN\n        EXECUTE format('SELECT %s($1);', fname)\n            INTO result USING args;\n        RETURN result;\n    ELSIF type = 12 THEN\n        -- NOTE: forward reference to current step EVAL function\n        RETURN mal.EVAL(fast, envs.new(fenv, fparams, args));\n    ELSE\n        RAISE EXCEPTION 'Invalid function call';\n    END IF;\nEND; $$ LANGUAGE plpgsql;\n\n-- ---------------------------------------------------------\n-- atom functions\n\n-- _atom:\n-- takes an ast value_id\n-- returns a new atom value_id\nCREATE FUNCTION types._atom(val integer) RETURNS integer AS $$\nDECLARE\n    cid     integer = NULL;\n    result  integer;\nBEGIN\n    -- Create atom\n    INSERT INTO types.value (type_id, val_seq)\n        VALUES (13, ARRAY[val])\n        RETURNING value_id INTO result;\n    RETURN result;\nEND; $$ LANGUAGE plpgsql;\n\n-- _atom_Q:\n-- takes a value_id\n-- returns the whether value_id is an atom\nCREATE FUNCTION types._atom_Q(id integer) RETURNS boolean AS $$\nBEGIN\n    RETURN EXISTS(SELECT 1 FROM types.value\n        WHERE type_id = 13 AND value_id = id);\nEND; $$ LANGUAGE plpgsql;\n\n-- _deref:\n-- takes an atom value_id\n-- returns a atom value value_id\nCREATE FUNCTION types._deref(atm integer) RETURNS integer AS $$\nDECLARE\n    result  integer;\nBEGIN\n    RETURN (SELECT val_seq[1] FROM types.value WHERE value_id = atm);\nEND; $$ LANGUAGE plpgsql;\n\n-- _reset_BANG:\n-- takes an atom value_id and new value value_id\n-- returns a new value value_id\nCREATE FUNCTION types._reset_BANG(atm integer, newval integer) RETURNS integer AS $$\nBEGIN\n    UPDATE types.value SET val_seq = ARRAY[newval] WHERE value_id = atm;\n    RETURN newval;\nEND; $$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "impls/plpgsql/wrap.sh",
    "content": "#!/usr/bin/env bash\n\nRL_HISTORY_FILE=${HOME}/.mal-history\nSKIP_INIT=\"${SKIP_INIT:-}\"\nPSQL_USER=\"${PSQL_USER:-postgres}\"\n\nPSQL=\"psql -q -t -A -v ON_ERROR_STOP=1 ${PSQL_USER:+-U ${PSQL_USER}}\"\n[ \"${DEBUG}\" ] || PSQL=\"${PSQL} -v VERBOSITY=terse\"\n\n# If mal DB is not there, force create of it\ndbcheck=$(${PSQL} -c \"select 1 from pg_database where datname='mal'\")\n[ -z \"${dbcheck}\" ] && SKIP_INIT=\n\nSTDOUT_PID= STDIN_PID=\ncleanup () {\n    trap - TERM QUIT INT EXIT\n    # Make sure input stream is closed. Input subprocess will do this\n    # for normal terminal input but in the runtest.py case it does not\n    # get a chance.\n    ${PSQL} -dmal -c \"SELECT io.close(0);\" > /dev/null\n    [ \"${STDIN_PID}\" ] && kill ${STDIN_PID} 2>/dev/null\n}\n\n# Load the SQL code\ntrap \"cleanup\" TERM QUIT INT EXIT\n${PSQL} -tc \"SELECT 1 FROM pg_database WHERE datname = 'mal'\" \\\n    | grep -q 1 || ${PSQL} -c \"CREATE DATABASE mal\"\n#[ \"${SKIP_INIT}\" ] || ${PSQL} -dmal -f $1 > /dev/null\n[ \"${SKIP_INIT}\" ] || ${PSQL} -dmal -f $1\n\n${PSQL} -dmal -c \"SELECT io.open(0); SELECT io.open(1);\" > /dev/null\n\n# Stream from table to stdout\n(\nwhile true; do\n    out=\"$(${PSQL} -dmal -c \"SELECT io.read_or_error(1)\" 2>/dev/null)\" || break\n    echo \"${out}\"\ndone\n) &\nSTDOUT_PID=$!\n\n# Perform readline input into stream table when requested\n(\n[ -r ${RL_HISTORY_FILE} ] && history -r ${RL_HISTORY_FILE}\nwhile true; do\n    prompt=$(${PSQL} -dmal \\\n        -c \"SELECT io.wait_rl_prompt(0);\" 2>/dev/null) || break\n    IFS= read -u 0 -r -e -p \"${prompt}\" line || break\n    if [ \"${line}\" ]; then\n        history -s -- \"${line}\"        # add to history\n        history -a ${RL_HISTORY_FILE}  # save history to file\n    fi\n\n    ${PSQL} -dmal -v arg=\"${line}\" \\\n        -f <(echo \"SELECT io.writeline(:'arg', 0);\") >/dev/null || break\ndone\n${PSQL} -dmal -c \"SELECT io.close(0);\" > /dev/null\n) <&0 >&1 &\nSTDIN_PID=$!\n\nres=0\nshift\nif [ $# -gt 0 ]; then\n    # If there are command line arguments then run a command and exit\n    args=$(for a in \"$@\"; do echo -n \"\\\"$a\\\" \"; done)\n    ${PSQL} -dmal -v args=\"(${args})\" \\\n        -f <(echo \"SELECT mal.MAIN('$(pwd)', :'args');\") > /dev/null\n    res=$?\nelse\n    # Start main loop in the background\n    ${PSQL} -dmal -c \"SELECT mal.MAIN('$(pwd)');\" > /dev/null\n    res=$?\nfi\nwait ${STDOUT_PID}\nexit ${res}\n"
  },
  {
    "path": "impls/plsql/Dockerfile",
    "content": "FROM wnameless/oracle-xe-11g\n\nRUN apt-get -y update\nRUN apt-get -y install make cpp python\n\nRUN apt-get -y install rlwrap\n\nENV ORACLE_HOME /u01/app/oracle/product/11.2.0/xe\nENV PATH ${ORACLE_HOME}/bin:${PATH}\nENV ORACLE_SID=XE\n\n# Enable use of DMBS_LOCK.sleep and make sure there are no password\n# expiry messages that may interfere with communication.\nRUN /usr/sbin/startup.sh && \\\n    echo \"GRANT EXECUTE ON DBMS_LOCK TO system;\" | sqlplus -S sys/oracle AS sysdba && \\\n    echo \"ALTER PROFILE default LIMIT PASSWORD_LIFE_TIME UNLIMITED;\" | sqlplus -S system/oracle && \\\n    echo \"ALTER USER system IDENTIFIED BY oracle ACCOUNT UNLOCK;\" | sqlplus -S system/oracle\n\nWORKDIR /mal\n\n# Add oracle user\nRUN usermod -a -G sudo oracle\n\n# Travis runs as user ID 1001 so add that user\nRUN useradd -ou 1001 -m -s /bin/bash -G sudo travis\n\n# Enable oracle and travis users to sudo for oracle startup\nRUN echo \"%sudo ALL=(ALL:ALL) NOPASSWD: ALL\" >> /etc/sudoers\n\nADD entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\nCMD []\n\n\n"
  },
  {
    "path": "impls/plsql/Dockerfile-oracle",
    "content": "FROM wnameless/oracle-xe-11g\n\nRUN apt-get -y update\nRUN apt-get -y install make cpp python\n\nRUN apt-get -y install rlwrap\n"
  },
  {
    "path": "impls/plsql/Dockerfile-postgres",
    "content": "FROM ubuntu:14.04\n\nRUN apt-get -y update\nRUN apt-get -y install make cpp python\n\nRUN apt-get -y install curl\nRUN useradd -u 1000 -m -s /bin/bash -G sudo postgres\n\nENV PG_VERSION=9.4\nRUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \\\n    echo 'deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main' > /etc/apt/sources.list.d/pgdg.list && \\\n    apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive apt-get -y install acl \\\n      postgresql-${PG_VERSION} postgresql-client-${PG_VERSION} postgresql-contrib-${PG_VERSION} && \\\n    mkdir -p /var/run/postgresql/9.4-main.pg_stat_tmp/ && \\\n    chown -R postgres /var/run/postgresql\n\nENV HOME=/var/run/postgresql\n\n# Add entrypoint.sh which starts postgres then run bash/command\nADD entrypoint.sh /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": "impls/plsql/Makefile",
    "content": "all:\n\nclean:\n"
  },
  {
    "path": "impls/plsql/core.sql",
    "content": "CREATE OR REPLACE TYPE core_ns_T IS TABLE OF varchar2(100);\n/\n\nCREATE OR REPLACE PACKAGE core IS\n    FUNCTION do_core_func(M IN OUT NOCOPY types.mal_table,\n                          H IN OUT NOCOPY types.map_entry_table,\n                          fn integer,\n                          a mal_vals) RETURN integer;\n\n    FUNCTION get_core_ns RETURN core_ns_T;\nEND core;\n/\nshow errors;\n\n\nCREATE OR REPLACE PACKAGE BODY core AS\n\n-- general functions\nFUNCTION equal_Q(M IN OUT NOCOPY types.mal_table,\n                 H IN OUT NOCOPY types.map_entry_table,\n                 args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.tf(types.equal_Q(M, H, args(1), args(2)));\nEND;\n\n-- scalar functiosn\nFUNCTION symbol(M IN OUT NOCOPY types.mal_table,\n                val integer) RETURN integer IS\nBEGIN\n    RETURN types.symbol(M, TREAT(M(val) AS mal_str_T).val_str);\nEND;\n\nFUNCTION keyword(M IN OUT NOCOPY types.mal_table,\n                 val integer) RETURN integer IS\nBEGIN\n    IF types.string_Q(M, val) THEN\n        RETURN types.keyword(M, TREAT(M(val) AS mal_str_T).val_str);\n    ELSIF types.keyword_Q(M, val) THEN\n        RETURN val;\n    ELSE\n        raise_application_error(-20009,\n            'invalid keyword call', TRUE);\n    END IF;\nEND;\n\n\n-- string functions\nFUNCTION pr_str(M IN OUT NOCOPY types.mal_table,\n                H IN OUT NOCOPY types.map_entry_table,\n                args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.string(M, printer.pr_str_seq(M, H, args, ' ', TRUE));\nEND;\n\nFUNCTION str(M IN OUT NOCOPY types.mal_table,\n             H IN OUT NOCOPY types.map_entry_table,\n             args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.string(M, printer.pr_str_seq(M, H, args, '', FALSE));\nEND;\n\nFUNCTION prn(M IN OUT NOCOPY types.mal_table,\n             H IN OUT NOCOPY types.map_entry_table,\n             args mal_vals) RETURN integer IS\nBEGIN\n    io.writeline(printer.pr_str_seq(M, H, args, ' ', TRUE));\n    RETURN 1;  -- nil\nEND;\n\nFUNCTION println(M IN OUT NOCOPY types.mal_table,\n                 H IN OUT NOCOPY types.map_entry_table,\n                 args mal_vals) RETURN integer IS\nBEGIN\n    io.writeline(printer.pr_str_seq(M, H, args, ' ', FALSE));\n    RETURN 1;  -- nil\nEND;\n\nFUNCTION read_string(M IN OUT NOCOPY types.mal_table,\n                     H IN OUT NOCOPY types.map_entry_table,\n                     args mal_vals) RETURN integer IS\nBEGIN\n    IF M(args(1)).type_id = 5 THEN\n        RETURN reader.read_str(M, H,\n            TREAT(M(args(1)) AS mal_str_T).val_str);\n    ELSE\n        RETURN reader.read_str(M, H,\n            TREAT(M(args(1)) AS mal_long_str_T).val_long_str);\n    END IF;\nEND;\n\nFUNCTION readline(M IN OUT NOCOPY types.mal_table,\n                  prompt integer) RETURN integer IS\n    input  CLOB;\nBEGIN\n    input := io.readline(TREAT(M(prompt) AS mal_str_T).val_str, 0);\n    RETURN types.string(M, input);\nEXCEPTION WHEN OTHERS THEN\n    IF SQLCODE = -20001 THEN  -- io streams closed\n        RETURN 1;  -- nil\n    ELSE\n        RAISE;\n    END IF;\nEND;\n\nFUNCTION slurp(M IN OUT NOCOPY types.mal_table,\n               args mal_vals) RETURN integer IS\n    content  CLOB;\nBEGIN\n    content := io.file_open_and_read(TREAT(M(args(1)) AS mal_str_T).val_str);\n    content := REPLACE(content, '\\n', chr(10));\n    RETURN types.string(M, content);\nEND;\n\n\n-- numeric functions\nFUNCTION lt(M IN OUT NOCOPY types.mal_table,\n            args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int <\n                    TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION lte(M IN OUT NOCOPY types.mal_table,\n             args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int <=\n                    TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION gt(M IN OUT NOCOPY types.mal_table,\n            args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int >\n                    TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION gte(M IN OUT NOCOPY types.mal_table,\n             args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int >=\n                    TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION add(M IN OUT NOCOPY types.mal_table,\n             args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int +\n                        TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION subtract(M IN OUT NOCOPY types.mal_table,\n                  args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int -\n                        TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION multiply(M IN OUT NOCOPY types.mal_table,\n                  args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int *\n                        TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION divide(M IN OUT NOCOPY types.mal_table,\n                args mal_vals) RETURN integer IS\nBEGIN\n    RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int /\n                        TREAT(M(args(2)) AS mal_int_T).val_int);\nEND;\n\nFUNCTION time_ms(M IN OUT NOCOPY types.mal_table) RETURN integer IS\n    now  integer;\nBEGIN\n    SELECT extract(day from(sys_extract_utc(systimestamp) -\n                   to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000 +\n        to_number(to_char(sys_extract_utc(systimestamp), 'SSSSSFF3'))\n        INTO now\n        FROM dual;\n    RETURN types.int(M, now);\nEND;\n\n-- hash-map functions\nFUNCTION assoc(M IN OUT NOCOPY types.mal_table,\n               H IN OUT NOCOPY types.map_entry_table,\n               hm integer,\n               kvs mal_vals) RETURN integer IS\n    new_hm    integer;\n    midx      integer;\nBEGIN\n    new_hm := types.clone(M, H, hm);\n    midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n    -- Add the new key/values\n    midx := types.assoc_BANG(M, H, midx, kvs);\n    RETURN new_hm;\nEND;\n\nFUNCTION dissoc(M IN OUT NOCOPY types.mal_table,\n                H IN OUT NOCOPY types.map_entry_table,\n                hm integer,\n                ks mal_vals) RETURN integer IS\n    new_hm    integer;\n    midx      integer;\nBEGIN\n    new_hm := types.clone(M, H, hm);\n    midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n    -- Remove the keys\n    midx := types.dissoc_BANG(M, H, midx, ks);\n    RETURN new_hm;\nEND;\n\n\nFUNCTION get(M IN OUT NOCOPY types.mal_table,\n             H IN OUT NOCOPY types.map_entry_table,\n             hm integer, key integer) RETURN integer IS\n    midx  integer;\n    k     varchar2(256);\n    val   integer;\nBEGIN\n    IF M(hm).type_id = 0 THEN\n        RETURN 1;  -- nil\n    END IF;\n    midx := TREAT(M(hm) AS mal_map_T).map_idx;\n    k := TREAT(M(key) AS mal_str_T).val_str;\n    IF H(midx).EXISTS(k) THEN\n        RETURN H(midx)(k);\n    ELSE\n        RETURN 1;  -- nil\n    END IF;\nEND;\n\nFUNCTION contains_Q(M IN OUT NOCOPY types.mal_table,\n             H IN OUT NOCOPY types.map_entry_table,\n             hm integer, key integer) RETURN integer IS\n    midx  integer;\n    k     varchar2(256);\n    val   integer;\nBEGIN\n    midx := TREAT(M(hm) AS mal_map_T).map_idx;\n    k := TREAT(M(key) AS mal_str_T).val_str;\n    RETURN types.tf(H(midx).EXISTS(k));\nEND;\n\nFUNCTION keys(M IN OUT NOCOPY types.mal_table,\n              H IN OUT NOCOPY types.map_entry_table,\n              hm integer) RETURN integer IS\n    midx  integer;\n    k     varchar2(256);\n    ks    mal_vals;\n    val   integer;\nBEGIN\n    midx := TREAT(M(hm) AS mal_map_T).map_idx;\n    ks := mal_vals();\n\n    k := H(midx).FIRST();\n    WHILE k IS NOT NULL LOOP\n        ks.EXTEND();\n        ks(ks.COUNT()) := types.string(M, k);\n        k := H(midx).NEXT(k);\n    END LOOP;\n\n    RETURN types.seq(M, 8, ks);\nEND;\n\nFUNCTION vals(M IN OUT NOCOPY types.mal_table,\n              H IN OUT NOCOPY types.map_entry_table,\n              hm integer) RETURN integer IS\n    midx  integer;\n    k     varchar2(256);\n    ks    mal_vals;\n    val   integer;\nBEGIN\n    midx := TREAT(M(hm) AS mal_map_T).map_idx;\n    ks := mal_vals();\n\n    k := H(midx).FIRST();\n    WHILE k IS NOT NULL LOOP\n        ks.EXTEND();\n        ks(ks.COUNT()) := H(midx)(k);\n        k := H(midx).NEXT(k);\n    END LOOP;\n\n    RETURN types.seq(M, 8, ks);\nEND;\n\n\n-- sequence functions\nFUNCTION cons(M IN OUT NOCOPY types.mal_table,\n              args mal_vals) RETURN integer IS\n    new_items  mal_vals;\n    len        integer;\n    i          integer;\nBEGIN\n    new_items := mal_vals();\n    len := types.count(M, args(2));\n    new_items.EXTEND(len+1);\n    new_items(1) := args(1);\n    FOR i IN 1..len LOOP\n        new_items(i+1) := TREAT(M(args(2)) AS mal_seq_T).val_seq(i);\n    END LOOP;\n    RETURN types.seq(M, 8, new_items);\nEND;\n\nFUNCTION concat(M IN OUT NOCOPY types.mal_table,\n                args mal_vals) RETURN integer IS\n    new_items  mal_vals;\n    cur_len    integer;\n    seq_len    integer;\n    i          integer;\n    j          integer;\nBEGIN\n    new_items := mal_vals();\n    cur_len := 0;\n    FOR i IN 1..args.COUNT() LOOP\n        seq_len := types.count(M, args(i));\n        new_items.EXTEND(seq_len);\n        FOR j IN 1..seq_len LOOP\n            new_items(cur_len + j) := types.nth(M, args(i), j-1);\n        END LOOP;\n        cur_len := cur_len + seq_len;\n    END LOOP;\n    RETURN types.seq(M, 8, new_items);\nEND;\n\nFUNCTION vec(M IN OUT NOCOPY types.mal_table,\n             seq integer) RETURN integer IS\nBEGIN\n    type_id := M(seq).type_id;\n    CASE\n    WHEN type_id = 8 THEN\n        RETURN types.seq(M, 9, TREAT(M(seq) AS mal_seq_T).val_seq);\n    WHEN type_id = 9 THEN\n        RETURN seq;\n    ELSE\n        raise_application_error(-20009,\n            'vec: not supported on type ' || type_id, TRUE);\n    END CASE;\nEND;\n\nFUNCTION nth(M IN OUT NOCOPY types.mal_table,\n             val integer,\n             ival integer) RETURN integer IS\n    idx  integer;\nBEGIN\n    idx := TREAT(M(ival) AS mal_int_T).val_int;\n    RETURN types.nth(M, val, idx);\nEND;\n\nFUNCTION first(M IN OUT NOCOPY types.mal_table,\n               val integer) RETURN integer IS\nBEGIN\n    IF val = 1 OR types.count(M, val) = 0 THEN\n        RETURN 1;  -- nil\n    ELSE\n        RETURN types.first(M, val);\n    END IF;\nEND;\n\nFUNCTION rest(M IN OUT NOCOPY types.mal_table,\n              val integer) RETURN integer IS\nBEGIN\n    IF val = 1 OR types.count(M, val) = 0 THEN\n        RETURN types.list(M);\n    ELSE\n        RETURN types.slice(M, val, 1);\n    END IF;\nEND;\n\nFUNCTION do_count(M IN OUT NOCOPY types.mal_table,\n               val integer) RETURN integer IS\nBEGIN\n    IF M(val).type_id = 0 THEN\n        RETURN types.int(M, 0);\n    ELSE\n        RETURN types.int(M, types.count(M, val));\n    END IF;\nEND;\n\n\nFUNCTION conj(M IN OUT NOCOPY types.mal_table,\n              seq integer,\n              vals mal_vals) RETURN integer IS\n    type_id  integer;\n    slen     integer;\n    items    mal_vals;\nBEGIN\n    type_id := M(seq).type_id;\n    slen := types.count(M, seq);\n    items := mal_vals();\n    items.EXTEND(slen + vals.COUNT());\n    CASE\n    WHEN type_id = 8 THEN\n        FOR i IN 1..vals.COUNT() LOOP\n            items(i) := vals(vals.COUNT + 1 - i);\n        END LOOP;\n        FOR i IN 1..slen LOOP\n            items(vals.COUNT() + i) := types.nth(M, seq, i-1);\n        END LOOP;\n    WHEN type_id = 9 THEN\n        FOR i IN 1..slen LOOP\n            items(i) := types.nth(M, seq, i-1);\n        END LOOP;\n        FOR i IN 1..vals.COUNT() LOOP\n            items(slen + i) := vals(i);\n        END LOOP;\n    ELSE\n        raise_application_error(-20009,\n            'conj: not supported on type ' || type_id, TRUE);\n    END CASE;\n    RETURN types.seq(M, type_id, items);\nEND;\n\nFUNCTION seq(M IN OUT NOCOPY types.mal_table,\n             val integer) RETURN integer IS\n    type_id    integer;\n    new_val    integer;\n    str        CLOB;\n    str_items  mal_vals;\nBEGIN\n    type_id := M(val).type_id;\n    CASE\n    WHEN type_id = 8 THEN\n        IF types.count(M, val) = 0 THEN\n            RETURN 1;  -- nil\n        END IF;\n        RETURN val;\n    WHEN type_id = 9 THEN\n        IF types.count(M, val) = 0 THEN\n            RETURN 1;  -- nil\n        END IF;\n        RETURN types.seq(M, 8, TREAT(M(val) AS mal_seq_T).val_seq);\n    WHEN types.string_Q(M, val) THEN\n        str := TREAT(M(val) AS mal_str_T).val_str;\n        IF str IS NULL THEN\n            RETURN 1;  -- nil\n        END IF;\n        str_items := mal_vals();\n        str_items.EXTEND(LENGTH(str));\n        FOR i IN 1..LENGTH(str) LOOP\n            str_items(i) := types.string(M, SUBSTR(str, i, 1));\n        END LOOP;\n        RETURN types.seq(M, 8, str_items);\n    WHEN type_id = 0 THEN\n        RETURN 1;  -- nil\n    ELSE\n        raise_application_error(-20009,\n            'seq: not supported on type ' || type_id, TRUE);\n    END CASE;\nEND;\n\n-- metadata functions\nFUNCTION meta(M IN OUT NOCOPY types.mal_table,\n              val integer) RETURN integer IS\n    type_id  integer;\nBEGIN\n    type_id := M(val).type_id;\n    IF type_id IN (8,9) THEN  -- list/vector\n        RETURN TREAT(M(val) AS mal_seq_T).meta;\n    ELSIF type_id = 10 THEN   -- hash-map\n        RETURN TREAT(M(val) AS mal_map_T).meta;\n    ELSIF type_id = 11 THEN   -- native function\n        RETURN 1;  -- nil\n    ELSIF type_id = 12 THEN   -- mal function\n        RETURN TREAT(M(val) AS mal_func_T).meta;\n    ELSE\n        raise_application_error(-20006,\n            'meta: metadata not supported on type', TRUE);\n    END IF;\nEND;\n\n-- general native function case/switch\nFUNCTION do_core_func(M IN OUT NOCOPY types.mal_table,\n                      H IN OUT NOCOPY types.map_entry_table,\n                      fn integer,\n                      a mal_vals) RETURN integer IS\n    fname  varchar(256);\n    idx    integer;\nBEGIN\n    IF M(fn).type_id <> 11 THEN\n        raise_application_error(-20004,\n            'Invalid function call', TRUE);\n    END IF;\n\n    fname := TREAT(M(fn) AS mal_str_T).val_str;\n\n    CASE\n    WHEN fname = '='           THEN RETURN equal_Q(M, H, a);\n\n    WHEN fname = 'nil?'        THEN RETURN types.tf(a(1) = 1);\n    WHEN fname = 'false?'      THEN RETURN types.tf(a(1) = 2);\n    WHEN fname = 'true?'       THEN RETURN types.tf(a(1) = 3);\n    WHEN fname = 'string?'     THEN RETURN types.tf(types.string_Q(M, a(1)));\n    WHEN fname = 'symbol'      THEN RETURN symbol(M, a(1));\n    WHEN fname = 'symbol?'     THEN RETURN types.tf(M(a(1)).type_id = 7);\n    WHEN fname = 'keyword'     THEN RETURN keyword(M, a(1));\n    WHEN fname = 'keyword?'    THEN RETURN types.tf(types.keyword_Q(M, a(1)));\n    WHEN fname = 'number?'     THEN RETURN types.tf(types.number_Q(M, a(1)));\n    WHEN fname = 'fn?'         THEN RETURN types.tf(types.function_Q(M, a(1)));\n    WHEN fname = 'macro?'      THEN RETURN types.tf(types.macro_Q(M, a(1)));\n\n    WHEN fname = 'pr-str'      THEN RETURN pr_str(M, H, a);\n    WHEN fname = 'str'         THEN RETURN str(M, H, a);\n    WHEN fname = 'prn'         THEN RETURN prn(M, H, a);\n    WHEN fname = 'println'     THEN RETURN println(M, H, a);\n    WHEN fname = 'read-string' THEN RETURN read_string(M, H, a);\n    WHEN fname = 'readline'    THEN RETURN readline(M, a(1));\n    WHEN fname = 'slurp'       THEN RETURN slurp(M, a);\n\n    WHEN fname = '<'           THEN RETURN lt(M, a);\n    WHEN fname = '<='          THEN RETURN lte(M, a);\n    WHEN fname = '>'           THEN RETURN gt(M, a);\n    WHEN fname = '>='          THEN RETURN gte(M, a);\n    WHEN fname = '+'           THEN RETURN add(M, a);\n    WHEN fname = '-'           THEN RETURN subtract(M, a);\n    WHEN fname = '*'           THEN RETURN multiply(M, a);\n    WHEN fname = '/'           THEN RETURN divide(M, a);\n    WHEN fname = 'time-ms'     THEN RETURN time_ms(M);\n\n    WHEN fname = 'list'        THEN RETURN types.seq(M, 8, a);\n    WHEN fname = 'list?'       THEN RETURN types.tf(M(a(1)).type_id = 8);\n    WHEN fname = 'vector'      THEN RETURN types.seq(M, 9, a);\n    WHEN fname = 'vector?'     THEN RETURN types.tf(M(a(1)).type_id = 9);\n    WHEN fname = 'hash-map'    THEN RETURN types.hash_map(M, H, a);\n    WHEN fname = 'assoc'       THEN RETURN assoc(M, H, a(1), types.islice(a, 1));\n    WHEN fname = 'dissoc'      THEN RETURN dissoc(M, H, a(1), types.islice(a, 1));\n    WHEN fname = 'map?'        THEN RETURN types.tf(M(a(1)).type_id = 10);\n    WHEN fname = 'get'         THEN RETURN get(M, H, a(1), a(2));\n    WHEN fname = 'contains?'   THEN RETURN contains_Q(M, H, a(1), a(2));\n    WHEN fname = 'keys'        THEN RETURN keys(M, H, a(1));\n    WHEN fname = 'vals'        THEN RETURN vals(M, H, a(1));\n\n    WHEN fname = 'sequential?' THEN RETURN types.tf(M(a(1)).type_id IN (8,9));\n    WHEN fname = 'cons'        THEN RETURN cons(M, a);\n    WHEN fname = 'concat'      THEN RETURN concat(M, a);\n    WHEN fname = 'vec'         THEN RETURN vec(M, a(1));\n    WHEN fname = 'nth'         THEN RETURN nth(M, a(1), a(2));\n    WHEN fname = 'first'       THEN RETURN first(M, a(1));\n    WHEN fname = 'rest'        THEN RETURN rest(M, a(1));\n    WHEN fname = 'empty?'      THEN RETURN types.tf(0 = types.count(M, a(1)));\n    WHEN fname = 'count'       THEN RETURN do_count(M, a(1));\n\n    WHEN fname = 'conj'        THEN RETURN conj(M, a(1), types.islice(a, 1));\n    WHEN fname = 'seq'         THEN RETURN seq(M, a(1));\n\n    WHEN fname = 'meta'        THEN RETURN meta(M, a(1));\n    WHEN fname = 'with-meta'   THEN RETURN types.clone(M, H, a(1), a(2));\n    WHEN fname = 'atom'        THEN RETURN types.atom_new(M, a(1));\n    WHEN fname = 'atom?'       THEN RETURN types.tf(M(a(1)).type_id = 13);\n    WHEN fname = 'deref'       THEN RETURN TREAT(M(a(1)) AS mal_atom_T).val;\n    WHEN fname = 'reset!'      THEN RETURN types.atom_reset(M, a(1), a(2));\n\n    ELSE raise_application_error(-20004, 'Invalid function call', TRUE);\n    END CASE;\nEND;\n\nFUNCTION get_core_ns RETURN core_ns_T IS\nBEGIN\n    RETURN core_ns_T(\n        '=',\n        'throw',\n\n        'nil?',\n        'true?',\n        'false?',\n        'string?',\n        'symbol',\n        'symbol?',\n        'keyword',\n        'keyword?',\n        'number?',\n        'fn?',\n        'macro?',\n\n        'pr-str',\n        'str',\n        'prn',\n        'println',\n        'read-string',\n        'readline',\n        'slurp',\n\n        '<',\n        '<=',\n        '>',\n        '>=',\n        '+',\n        '-',\n        '*',\n        '/',\n        'time-ms',\n\n        'list',\n        'list?',\n        'vector',\n        'vector?',\n        'hash-map',\n        'assoc',\n        'dissoc',\n        'map?',\n        'get',\n        'contains?',\n        'keys',\n        'vals',\n\n        'sequential?',\n        'cons',\n        'concat',\n        'vec',\n        'nth',\n        'first',\n        'rest',\n        'empty?',\n        'count',\n        'apply',   -- defined in step do_builtin function\n        'map',     -- defined in step do_builtin function\n\n        'conj',\n        'seq',\n\n        'meta',\n        'with-meta',\n        'atom',\n        'atom?',\n        'deref',\n        'reset!',\n        'swap!'    -- defined in step do_builtin function\n    );\nEND;\n\nEND core;\n/\nshow errors;\n"
  },
  {
    "path": "impls/plsql/entrypoint.sh",
    "content": "#!/usr/bin/env bash\n\ncase ${1} in\nmake*)\n    echo \"Skipping Oracle XE startup\"\n    ;;\n*)\n    echo \"Starting Oracle XE\"\n    sudo /usr/sbin/startup.sh\n    ;;\nesac\n\nif [ \"${*}\" ]; then\n    exec \"${@}\"\nelse\n    exec bash\nfi\n"
  },
  {
    "path": "impls/plsql/env.sql",
    "content": "-- ---------------------------------------------------------\n-- env.sql\n\nCREATE OR REPLACE TYPE env_item FORCE AS OBJECT (\n    key  varchar2(256),\n    val  integer\n) FINAL;\n/\n\nCREATE OR REPLACE TYPE env_data FORCE IS TABLE OF env_item;\n/\n\nCREATE OR REPLACE TYPE env_T FORCE AS OBJECT (\n    idx        integer,\n    outer_idx  integer,\n    data       env_data\n);\n/\n\nCREATE OR REPLACE TYPE env_mem_T FORCE IS TABLE OF env_T;\n/\n\nCREATE OR REPLACE PACKAGE env_pkg IS\n    TYPE env_entry IS TABLE OF integer INDEX BY varchar2(256);\n    TYPE env_entry_table IS TABLE OF env_entry;\n\n    FUNCTION env_new(M IN OUT NOCOPY types.mal_table,\n                     eeT IN OUT NOCOPY env_entry_table,\n                     outer_idx integer DEFAULT NULL)\n                RETURN integer;\n    FUNCTION env_new(M IN OUT NOCOPY types.mal_table,\n                     eeT IN OUT NOCOPY env_entry_table,\n                     outer_idx integer,\n                     binds integer,\n                     exprs mal_vals)\n                RETURN integer;\n    FUNCTION env_set(M IN OUT NOCOPY types.mal_table,\n                     eeT IN OUT NOCOPY env_entry_table,\n                     eidx integer,\n                     key integer,\n                     val integer) RETURN integer;\n    FUNCTION env_find(M IN OUT NOCOPY types.mal_table,\n                      eeT env_entry_table,\n                      eidx integer,\n                      key integer) RETURN integer;\n    FUNCTION env_get(M IN OUT NOCOPY types.mal_table,\n                     eeT env_entry_table,\n                     eidx integer,\n                     key integer) RETURN integer;\nEND env_pkg;\n/\nshow errors;\n\n\nCREATE OR REPLACE PACKAGE BODY env_pkg IS\n\nFUNCTION env_new(M IN OUT NOCOPY types.mal_table,\n                 eeT IN OUT NOCOPY env_entry_table,\n                 outer_idx integer DEFAULT NULL)\n            RETURN integer IS\n    eidx  integer;\nBEGIN\n    eeT.EXTEND();\n    eidx := eeT.COUNT();\n    eeT(eidx)('**OUTER**') := outer_idx;\n    RETURN eidx;\nEND;\n\nFUNCTION env_new(M IN OUT NOCOPY types.mal_table,\n                 eeT IN OUT NOCOPY env_entry_table,\n                 outer_idx integer,\n                 binds integer,\n                 exprs mal_vals)\n            RETURN integer IS\n    eidx  integer;\n    i     integer;\n    bs    mal_vals;\nBEGIN\n    eeT.EXTEND();\n    eidx := eeT.COUNT();\n    eeT(eidx)('**OUTER**') := outer_idx;\n    IF binds IS NOT NULL THEN\n        bs := TREAT(M(binds) AS mal_seq_T).val_seq;\n        FOR i IN 1..bs.COUNT LOOP\n            IF TREAT(M(bs(i)) AS mal_str_T).val_str = '&' THEN\n                eeT(eidx)(TREAT(M(bs(i+1)) AS mal_str_T).val_str) :=\n                    types.slice(M, exprs, i-1);\n                EXIT;\n            ELSE\n                eeT(eidx)(TREAT(M(bs(i)) AS mal_str_T).val_str) :=\n                    exprs(i);\n            END IF;\n        END LOOP;\n    END IF;\n    RETURN eidx;\nEND;\n\nFUNCTION env_set(M IN OUT NOCOPY types.mal_table,\n                 eeT IN OUT NOCOPY env_entry_table,\n                 eidx integer,\n                 key integer,\n                 val integer) RETURN integer IS\n    k    varchar2(256);\n    i    integer;\n    cnt  integer;\nBEGIN\n    k := TREAT(M(key) AS mal_str_T).val_str;\n    eeT(eidx)(k) := val;\n    RETURN val;\nEND;\n\nFUNCTION env_find(M IN OUT NOCOPY types.mal_table,\n                  eeT env_entry_table,\n                  eidx integer,\n                  key integer) RETURN integer IS\n    k    varchar2(256);\n    cnt  integer;\nBEGIN\n    k := TREAT(M(key) AS mal_str_T).val_str;\n    IF eeT(eidx).EXISTS(k) THEN\n        RETURN eidx;\n    ELSIF eeT(eidx)('**OUTER**') IS NOT NULL THEN\n        RETURN env_find(M, eeT, eeT(eidx)('**OUTER**'), key);\n    ELSE\n        RETURN NULL;\n    END IF;\nEND;\n\nFUNCTION env_get(M IN OUT NOCOPY types.mal_table,\n                 eeT env_entry_table,\n                 eidx integer,\n                 key integer) RETURN integer IS\n    found  integer;\n    k      varchar2(256);\nBEGIN\n    found := env_find(M, eeT, eidx, key);\n    k := TREAT(M(key) AS mal_str_T).val_str;\n    IF found IS NOT NULL THEN\n        RETURN eeT(found)(k);\n    ELSE\n        raise_application_error(-20005,\n            '''' || k || ''' not found', TRUE);\n    END IF;\nEND;\n\nEND env_pkg;\n/\nshow errors;\n"
  },
  {
    "path": "impls/plsql/io.sql",
    "content": "BEGIN\n  EXECUTE IMMEDIATE 'DROP TABLE stream';\nEXCEPTION\n  WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF;\nEND;\n/\n\nCREATE TABLE stream (\n    stream_id  integer,\n    open       number(1,0),    -- stream open (1) or closed (0)\n    data       CLOB,           -- queued stream data\n    rl_prompt  varchar2(256)   -- prompt for readline input\n);\n\n-- stdin\nINSERT INTO stream (stream_id, open, data, rl_prompt)\n    VALUES (0, 0, '', '');\n-- stdout\nINSERT INTO stream (stream_id, open, data, rl_prompt)\n    VALUES (1, 0, '', '');\n\n-- ---------------------------------------------------------\n\nBEGIN\n  EXECUTE IMMEDIATE 'DROP TABLE file_io';\nEXCEPTION\n  WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF;\nEND;\n/\n\nCREATE TABLE file_io (\n    path       varchar2(1024),  -- file to read/write\n    data       CLOB,            -- file data\n    error      varchar2(1024),  -- any errors during read\n    in_or_out  varchar2(4)      -- input ('in') or output ('out')\n);\n\n-- ---------------------------------------------------------\n\nCREATE OR REPLACE PACKAGE io IS\n    PROCEDURE open(sid integer);\n    PROCEDURE close(sid integer);\n    FUNCTION read(sid integer DEFAULT 0) RETURN CLOB;\n    FUNCTION readline(prompt varchar, sid integer DEFAULT 0) RETURN CLOB;\n    PROCEDURE write(input CLOB, sid integer DEFAULT 1);\n    PROCEDURE writeline(data CLOB, sid integer DEFAULT 1);\n    FUNCTION wait_rl_prompt(sid integer DEFAULT 0) RETURN varchar;\n    PROCEDURE wait_flushed(sid integer DEFAULT 1);\nFUNCTION file_open_and_read(path varchar) RETURN varchar;\nEND io;\n/\nshow errors;\n\nCREATE OR REPLACE PACKAGE BODY io AS\n\nPROCEDURE open(sid integer) AS\n    PRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\n    -- DBMS_OUTPUT.PUT_LINE('io.open(' || sid || ') start');\n    UPDATE stream SET data = '', rl_prompt = '', open = 1\n        WHERE stream_id = sid;\n    COMMIT;\n    -- DBMS_OUTPUT.PUT_LINE('io.open(' || sid || ') done');\nEND;\n\nPROCEDURE close(sid integer) AS\n    PRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\n    -- DBMS_OUTPUT.PUT_LINE('io.close(' || sid || ') start');\n    UPDATE stream SET rl_prompt = '', open = 0\n        WHERE stream_id = sid;\n    COMMIT;\n    -- DBMS_OUTPUT.PUT_LINE('io.close(' || sid || ') done');\nEND;\n\n-- read:\n-- read from stream stream_id in stream table. Waits until there is\n-- either data to return or the stream closes (NULL data). Returns\n-- NULL when stream is closed.\nFUNCTION read(sid integer DEFAULT 0) RETURN CLOB IS\n    PRAGMA AUTONOMOUS_TRANSACTION;\n    input   CLOB;\n    isopen  integer;\n    sleep   real;\nBEGIN\n    sleep := 0.05;\n    -- poll / wait for input\n    WHILE true\n    LOOP\n        -- atomic get and set to empty\n        -- LOCK TABLE stream IN EXCLUSIVE MODE;\n        SELECT data, open INTO input, isopen FROM stream\n            WHERE stream_id = sid;\n        IF input IS NOT NULL THEN\n            UPDATE stream SET data = '' WHERE stream_id = sid;\n            COMMIT;\n            RETURN trim(TRAILING chr(10) FROM input);\n        END IF;\n        -- '' -> no input, NULL -> stream closed\n        --RAISE NOTICE 'read input: [%] %', input, stream_id;\n        IF isopen = 0 THEN\n            raise_application_error(-20001,\n                'io.read: stream ''' || sid || ''' is closed', TRUE);\n        END IF;\n        SYS.DBMS_LOCK.SLEEP(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND;\n\n-- readline:\n-- set prompt and wait for readline style input on the stream\nFUNCTION readline(prompt varchar, sid integer DEFAULT 0) RETURN CLOB IS\n    PRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\n    -- set prompt / request readline style input\n    -- LOCK TABLE stream IN EXCLUSIVE MODE;\n    IF sid = 0 THEN\n        wait_flushed(1);\n    ELSIF sid = 1 THEN\n        wait_flushed(0);\n    END IF;\n    UPDATE stream SET rl_prompt = prompt WHERE stream_id = sid;\n    COMMIT;\n\n    RETURN read(sid);\nEND;\n\nPROCEDURE write(input CLOB, sid integer DEFAULT 1) AS\n    PRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\n    -- LOCK TABLE stream IN EXCLUSIVE MODE;\n    UPDATE stream SET data = data || input WHERE stream_id = sid;\n    COMMIT;\nEND;\n\nPROCEDURE writeline(data CLOB, sid integer DEFAULT 1) AS\n    PRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\n    write(data || TO_CLOB(chr(10)), sid);\nEND;\n\n-- ---------------------------------------------------------\n\n-- wait_rl_prompt:\n-- wait for rl_prompt to be set on the given stream and return the\n-- rl_prompt value. Errors if stream is already closed.\nFUNCTION wait_rl_prompt(sid integer DEFAULT 0) RETURN varchar IS\n    PRAGMA AUTONOMOUS_TRANSACTION;\n    isopen   integer;\n    prompt   CLOB;\n    sleep    real;\n    datas    integer;\nBEGIN\n    sleep := 0.05;\n    WHILE true\n    LOOP\n        LOCK TABLE stream IN EXCLUSIVE MODE;\n        SELECT open, rl_prompt INTO isopen, prompt\n            FROM stream WHERE stream_id = sid;\n        SELECT count(stream_id) INTO datas FROM stream WHERE data IS NOT NULL;\n\n        IF isopen = 0 THEN\n            raise_application_error(-20001,\n                'io.wait_rl_prompt: stream ''' || sid || ''' is closed', TRUE);\n        END IF;\n\n        -- wait until all channels have flushed\n        IF datas = 0 AND prompt IS NOT NULL THEN\n            UPDATE stream SET rl_prompt = '' WHERE stream_id = sid;\n            COMMIT;\n            -- Prompt is returned single-quoted because sqlplus trims\n            -- trailing whitespace in select output.\n            RETURN '''' || prompt || '''';\n        END IF;\n        COMMIT;\n\n        DBMS_LOCK.SLEEP(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND;\n\nPROCEDURE wait_flushed(sid integer DEFAULT 1) AS\n    PRAGMA AUTONOMOUS_TRANSACTION;\n    pending  integer;\n    sleep    real;\nBEGIN\n    sleep := 0.05;\n    WHILE true\n    LOOP\n        SELECT count(stream_id) INTO pending FROM stream\n            WHERE stream_id = sid AND data IS NOT NULL;\n        IF pending = 0 THEN RETURN; END IF;\n        DBMS_LOCK.SLEEP(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND;\n\n-- ---------------------------------------------------------\n\nFUNCTION file_open_and_read(path varchar) RETURN varchar IS\n    PRAGMA AUTONOMOUS_TRANSACTION;\n    sleep      real;\n    content    CLOB;\n    error_msg  varchar2(1024);\nBEGIN\n    sleep := 0.05;\n    -- TODO: use unique ID instead of path\n    INSERT INTO file_io (path, data, error, in_or_out)\n        VALUES (path, NULL, NULL, 'in');\n    WHILE true\n    LOOP\n        LOCK TABLE file_io IN EXCLUSIVE MODE;\n        SELECT data, error INTO content, error_msg\n            FROM file_io WHERE path = path AND ROWNUM = 1;\n\n        IF error_msg IS NOT NULL THEN\n            raise_application_error(-20010,\n                'open_and_read error: ''' || error_msg || '''', TRUE);\n        END IF;\n\n        IF content IS NOT NULL THEN\n            DELETE FROM file_io WHERE path = path;\n            COMMIT;\n            RETURN content;\n        END IF;\n        COMMIT;\n\n        -- keep waiting\n        DBMS_LOCK.SLEEP(sleep);\n        IF sleep < 0.5 THEN\n            sleep := sleep * 1.1; -- backoff\n        END IF;\n    END LOOP;\nEND;\n\nPROCEDURE file_read_response(path varchar, data varchar) AS\n    PRAGMA AUTONOMOUS_TRANSACTION;\nBEGIN\n    UPDATE file_io SET data = data WHERE path = path;\nEND;\n\nEND io;\n/\nshow errors;\n"
  },
  {
    "path": "impls/plsql/login.sql",
    "content": "-- PROMPT 'Start login.sql';\nwhenever sqlerror exit SQL.SQLCODE;\nwhenever oserror exit 1;\n\nSET ECHO OFF;\nSET LINESIZE 32767;\n-- SET TRIMOUT ON;\n-- SET WRAP OFF;\nSET PAGESIZE 0;\n\n-- Do not format whitespace in terminaml output\nSET TAB OFF;\n\n-- Allow literal & in strings\nSET DEFINE OFF;\n\n-- Print DBMS_OUTPUT.PUT_LINE debugcommands\nSET SERVEROUTPUT ON SIZE 30000;\n\n-- Do not truncate or wrap CLOB output\nSET LONG 32767;\nSET LONGCHUNKSIZE 32767;\n\n-- PROMPT 'Finish login.sql';\n\n"
  },
  {
    "path": "impls/plsql/printer.sql",
    "content": "-- ---------------------------------------------------------\n-- printer.sql\n\nCREATE OR REPLACE PACKAGE printer IS\n    FUNCTION pr_str_seq(M IN OUT NOCOPY types.mal_table,\n                        H IN OUT NOCOPY types.map_entry_table,\n                        seq mal_vals, sep varchar2,\n                        print_readably boolean DEFAULT TRUE) RETURN varchar;\n    FUNCTION pr_str(M IN OUT NOCOPY types.mal_table,\n                    H IN OUT NOCOPY types.map_entry_table,\n                    ast integer,\n                    print_readably boolean DEFAULT TRUE) RETURN varchar;\nEND printer;\n/\nshow errors;\n\nCREATE OR REPLACE PACKAGE BODY printer AS\n\nFUNCTION pr_str_seq(M IN OUT NOCOPY types.mal_table,\n                    H IN OUT NOCOPY types.map_entry_table,\n                    seq mal_vals, sep varchar2,\n                    print_readably boolean DEFAULT TRUE) RETURN varchar IS\n    first  integer := 1;\n    str    CLOB;\nBEGIN\n    FOR i IN 1..seq.COUNT LOOP\n        IF first = 1 THEN\n            first := 0;\n        ELSE\n            str := str || sep;\n        END IF;\n        str := str || pr_str(M, H, seq(i), print_readably);\n    END LOOP;\n    RETURN str;\nEND;\n\nFUNCTION pr_str_map(M IN OUT NOCOPY types.mal_table,\n                    H IN OUT NOCOPY types.map_entry_table,\n                    midx integer, sep varchar2,\n                    print_readably boolean DEFAULT TRUE) RETURN varchar IS\n    key    varchar2(256);\n    first  integer := 1;\n    str    CLOB;\nBEGIN\n    key := H(midx).FIRST();\n    WHILE key IS NOT NULL LOOP\n        IF first = 1 THEN\n            first := 0;\n        ELSE\n            str := str || sep;\n        END IF;\n        str := str || pr_str(M, H, types.string(M, key), print_readably);\n        str := str || ' ' || pr_str(M, H, H(midx)(key), print_readably);\n        key := H(midx).NEXT(key);\n    END LOOP;\n    RETURN str;\nEND;\n\n\nFUNCTION pr_str(M IN OUT NOCOPY types.mal_table,\n                H IN OUT NOCOPY types.map_entry_table,\n                ast integer,\n                print_readably boolean DEFAULT TRUE) RETURN varchar IS\n    type_id  integer;\n    first    integer := 1;\n    i        integer;\n    str      CLOB;\n    malfn    mal_func_T;\nBEGIN\n    type_id := M(ast).type_id;\n    -- io.writeline('pr_str type: ' || type_id);\n    CASE\n    WHEN type_id = 0 THEN RETURN 'nil';\n    WHEN type_id = 1 THEN RETURN 'false';\n    WHEN type_id = 2 THEN RETURN 'true';\n    WHEN type_id = 3 THEN  -- integer\n        RETURN CAST(TREAT(M(ast) AS mal_int_T).val_int as varchar);\n    WHEN type_id IN (5,6) THEN  -- string\n        IF type_id = 5 THEN\n            str := TREAT(M(ast) as mal_str_T).val_str;\n        ELSE\n            str := TREAT(M(ast) as mal_long_str_T).val_long_str;\n        END IF;\n        IF chr(127) = SUBSTR(str, 1, 1) THEN\n            RETURN ':' || SUBSTR(str, 2, LENGTH(str)-1);\n        ELSIF print_readably THEN\n            str := REPLACE(str, chr(92), '\\\\');\n            str := REPLACE(str, '\"', '\\\"');\n            str := REPLACE(str, chr(10), '\\n');\n            RETURN '\"' || str || '\"';\n        ELSE\n            RETURN str;\n        END IF;\n        RETURN TREAT(M(ast) AS mal_str_T).val_str;\n    WHEN type_id = 7 THEN  -- symbol\n        RETURN TREAT(M(ast) AS mal_str_T).val_str;\n    WHEN type_id = 8 THEN  -- list\n        RETURN '(' || pr_str_seq(M, H,\n                                 TREAT(M(ast) AS mal_seq_T).val_seq, ' ',\n                                 print_readably) || ')';\n    WHEN type_id = 9 THEN  -- vector\n        RETURN '[' || pr_str_seq(M, H,\n                                 TREAT(M(ast) AS mal_seq_T).val_seq, ' ',\n                                 print_readably) || ']';\n    WHEN type_id = 10 THEN  -- hash-map\n        RETURN '{' || pr_str_map(M, H,\n                                 TREAT(M(ast) AS mal_map_T).map_idx, ' ',\n                                 print_readably) || '}';\n    WHEN type_id = 11 THEN  -- native function\n        RETURN '#<function ' ||\n               TREAT(M(ast) AS mal_str_T).val_str ||\n               '>';\n    WHEN type_id = 12 THEN  -- mal function\n        malfn := TREAT(M(ast) AS mal_func_T);\n        RETURN '(fn* ' || pr_str(M, H, malfn.params, print_readably) ||\n                ' ' || pr_str(M, H, malfn.ast, print_readably) || ')';\n    WHEN type_id = 13 THEN  -- atom\n        RETURN '(atom ' ||\n            pr_str(M, H, TREAT(M(ast) AS mal_atom_T).val, print_readably) ||\n            ')';\n    ELSE\n        RETURN 'unknown';\n    END CASE;\nEND;\n\nEND printer;\n/\nshow errors;\n"
  },
  {
    "path": "impls/plsql/reader.sql",
    "content": "-- ---------------------------------------------------------\n-- reader.sql\n\nCREATE OR REPLACE TYPE tokens FORCE AS TABLE OF CLOB;\n/\n\nCREATE OR REPLACE TYPE reader_T FORCE AS OBJECT (\n    position  integer,\n    toks      tokens,\n    MEMBER FUNCTION peek (SELF IN OUT NOCOPY reader_T) RETURN varchar,\n    MEMBER FUNCTION next (SELF IN OUT NOCOPY reader_T) RETURN varchar\n);\n/\n\n\nCREATE OR REPLACE TYPE BODY reader_T AS\n    MEMBER FUNCTION peek (SELF IN OUT NOCOPY reader_T) RETURN varchar IS\n    BEGIN\n        IF position > toks.COUNT THEN\n            RETURN NULL;\n        END IF;\n        RETURN toks(position);\n    END;\n    MEMBER FUNCTION next (SELF IN OUT NOCOPY reader_T) RETURN varchar IS\n    BEGIN\n        position := position + 1;\n        RETURN toks(position-1);\n    END;\nEND;\n/\n\n\nCREATE OR REPLACE PACKAGE reader IS\n    FUNCTION read_str(M IN OUT NOCOPY types.mal_table,\n                      H IN OUT NOCOPY types.map_entry_table,\n                      str varchar) RETURN integer;\nEND reader;\n/\nshow errors;\n\n\nCREATE OR REPLACE PACKAGE BODY reader AS\n\n-- tokenize:\n-- takes a string and returns a nested table of token strings\nFUNCTION tokenize(str varchar) RETURN tokens IS\n    re      varchar2(100) := '[[:space:] ,]*(~@|[][{}()''`~@]|\"(([\\].|[^\\\"])*)\"?|;[^' || chr(10) || ']*|[^][[:space:] {}()''\"`~@,;]*)';\n    tok     CLOB;\n    toks    tokens := tokens();\n    cnt     integer;\nBEGIN\n    cnt := REGEXP_COUNT(str, re);\n    FOR I IN 1..cnt LOOP\n        tok := REGEXP_SUBSTR(str, re, 1, I, 'm', 1);\n        IF tok IS NOT NULL AND SUBSTR(tok, 1, 1) <> ';' THEN\n            toks.extend();\n            toks(toks.COUNT) := tok;\n            -- io.writeline('tok: [' || tok || ']');\n        END IF;\n    END LOOP;\n    RETURN toks;\nEND;\n\n-- read_atom:\n-- takes a reader_T\n-- updates reader_T and returns a single scalar mal value\nFUNCTION read_atom(M IN OUT NOCOPY types.mal_table,\n                   rdr IN OUT NOCOPY reader_T) RETURN integer IS\n    str_id  integer;\n    str     CLOB;\n    token   CLOB;\n    istr    varchar2(256);\n    result  integer;\nBEGIN\n    token := rdr.next();\n    -- io.writeline('read_atom: ' || token);\n    IF token = 'nil' THEN       -- nil\n        result := 1;\n    ELSIF token = 'false' THEN  -- false\n        result := 2;\n    ELSIF token = 'true' THEN   -- true\n        result := 3;\n    ELSIF REGEXP_LIKE(token, '^-?[0-9][0-9]*$') THEN  -- integer\n        istr := token;\n        result := types.int(M, CAST(istr AS integer));\n    ELSIF REGEXP_LIKE(token, '^\".*\"') THEN  -- string\n        -- string\n        str := SUBSTR(token, 2, LENGTH(token)-2);\n        str := REPLACE(str, '\\\"', '\"');\n        str := REPLACE(str, '\\n', chr(10));\n        str := REPLACE(str, '\\\\', chr(92));\n        result := types.string(M, str);\n    ELSIF REGEXP_LIKE(token, '^\".*') THEN  -- unclosed string\n        raise_application_error(-20003,\n            'expected ''\"'', got EOF', TRUE);\n    ELSIF REGEXP_LIKE(token, '^:.*') THEN  -- keyword\n         -- keyword\n         result := types.keyword(M, SUBSTR(token, 2, LENGTH(token)-1));\n    ELSE\n        -- symbol\n        result := types.symbol(M, token);\n    END IF;\n    return result;\nEND;\n\n-- forward declaration of read_form\nFUNCTION read_form(M IN OUT NOCOPY types.mal_table,\n                   H IN OUT NOCOPY types.map_entry_table,\n                   rdr IN OUT NOCOPY reader_T) RETURN integer;\n\n-- read_seq:\n-- takes a reader_T\n-- updates reader_T and returns new mal_list/vector/hash-map\nFUNCTION read_seq(M IN OUT NOCOPY types.mal_table,\n                  H IN OUT NOCOPY types.map_entry_table,\n                  rdr IN OUT NOCOPY reader_T,\n                  type_id integer,\n                  first varchar, last varchar)\n    RETURN integer IS\n    token   CLOB;\n    items   mal_vals;\nBEGIN\n    token := rdr.next();\n    IF token <> first THEN\n        raise_application_error(-20003,\n            'expected ''' || first || '''', TRUE);\n    END IF;\n    items := mal_vals();\n    LOOP\n        token := rdr.peek();\n        IF token IS NULL THEN\n            raise_application_error(-20003,\n                'expected ''' || last || ''', got EOF', TRUE);\n        END IF;\n        IF token = last THEN EXIT; END IF;\n        items.EXTEND();\n        items(items.COUNT) := read_form(M, H, rdr);\n    END LOOP;\n    token := rdr.next();\n    IF type_id IN (8,9) THEN\n        RETURN types.seq(M, type_id, items);\n    ELSE\n        RETURN types.hash_map(M, H, items);\n    END IF;\nEND;\n\n-- read_form:\n-- takes a reader_T\n-- updates the reader_T and returns new mal value\nFUNCTION read_form(M IN OUT NOCOPY types.mal_table,\n                   H IN OUT NOCOPY types.map_entry_table,\n                   rdr IN OUT NOCOPY reader_T) RETURN integer IS\n    token   CLOB;\n    meta    integer;\n    midx    integer;\nBEGIN\n     token := rdr.peek();  -- peek\n     CASE\n    WHEN token = '''' THEN\n        token := rdr.next();\n        RETURN types.list(M,\n                          types.symbol(M, 'quote'),\n                          read_form(M, H, rdr));\n    WHEN token = '`' THEN\n        token := rdr.next();\n        RETURN types.list(M,\n                          types.symbol(M, 'quasiquote'),\n                          read_form(M, H, rdr));\n    WHEN token = '~' THEN\n        token := rdr.next();\n        RETURN types.list(M,\n                          types.symbol(M, 'unquote'),\n                          read_form(M, H, rdr));\n    WHEN token = '~@' THEN\n        token := rdr.next();\n        RETURN types.list(M,\n                          types.symbol(M, 'splice-unquote'),\n                          read_form(M, H, rdr));\n    WHEN token = '^' THEN\n        token := rdr.next();\n        meta := read_form(M, H, rdr);\n        RETURN types.list(M,\n                          types.symbol(M, 'with-meta'),\n                          read_form(M, H, rdr),\n                          meta);\n    WHEN token = '@' THEN\n        token := rdr.next();\n        RETURN types.list(M,\n                          types.symbol(M, 'deref'),\n                          read_form(M, H, rdr));\n\n    -- list\n    WHEN token = ')' THEN\n        raise_application_error(-20002,\n            'unexpected '')''', TRUE);\n    WHEN token = '(' THEN\n        RETURN read_seq(M, H, rdr, 8, '(', ')');\n\n    -- vector\n    WHEN token = ']' THEN\n        raise_application_error(-20002,\n            'unexpected '']''', TRUE);\n    WHEN token = '[' THEN\n        RETURN read_seq(M, H, rdr, 9, '[', ']');\n\n    -- hash-map\n    WHEN token = '}' THEN\n        raise_application_error(-20002,\n            'unexpected ''}''', TRUE);\n    WHEN token = '{' THEN\n        RETURN read_seq(M, H, rdr, 10, '{', '}');\n\n    -- atom/scalar\n    ELSE\n        RETURN read_atom(M, rdr);\n    END CASE;\nEND;\n\n-- read_str:\n-- takes a string\n-- returns a new mal value\nFUNCTION read_str(M IN OUT NOCOPY types.mal_table,\n                  H IN OUT NOCOPY types.map_entry_table,\n                  str varchar) RETURN integer IS\n    toks  tokens;\n    rdr   reader_T;\nBEGIN\n    toks := tokenize(str);\n    rdr := reader_T(1, toks);\n    -- io.writeline('token 1: ' || rdr.peek());\n    RETURN read_form(M, H, rdr);\nEND;\n\nEND reader;\n/\nshow errors;\n"
  },
  {
    "path": "impls/plsql/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/wrap.sh $(dirname $0)/${STEP:-stepA_mal}.sql \"${@}\"\n"
  },
  {
    "path": "impls/plsql/step0_repl.sql",
    "content": "--\\i init.sql\n@io.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    line      CLOB;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN line;\n    END;\n\n    -- eval\n    FUNCTION EVAL(ast varchar, env varchar) RETURN varchar IS\n    BEGIN\n        RETURN ast;\n    END;\n\n    -- print\n    FUNCTION PRINT(exp varchar) RETURN varchar IS\n    BEGIN\n        RETURN exp;\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), ''));\n    END;\n\nBEGIN\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step1_read_print.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    line      CLOB;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n    FUNCTION EVAL(ast integer, env varchar) RETURN integer IS\n    BEGIN\n        RETURN ast;\n    END;\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), ''));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.writeline('closing stream 1');\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step2_eval.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    TYPE      env_T IS TABLE OF integer INDEX BY varchar2(100);\n    repl_env  env_T;\n    line      CLOB;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(ast integer, env env_T) RETURN integer;\n    FUNCTION do_core_func(fn integer, args mal_vals)\n        RETURN integer;\n\n    FUNCTION eval_ast(ast integer, env env_T) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env(TREAT(M(ast) AS mal_str_T).val_str);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(ast integer, env env_T) RETURN integer IS\n        el       integer;\n        f        integer;\n        args     mal_vals;\n    BEGIN\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        el := eval_ast(ast, env);\n        f := types.first(M, el);\n        args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n        RETURN do_core_func(f, args);\n    END;\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION mal_add(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int +\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION mal_subtract(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int -\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION mal_multiply(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int *\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION mal_divide(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int /\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION do_core_func(fn integer, args mal_vals)\n        RETURN integer IS\n        fname  varchar(256);\n    BEGIN\n        IF M(fn).type_id <> 11 THEN\n            raise_application_error(-20004,\n                'Invalid function call', TRUE);\n        END IF;\n\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = '+' THEN RETURN mal_add(args);\n        WHEN fname = '-' THEN RETURN mal_subtract(args);\n        WHEN fname = '*' THEN RETURN mal_multiply(args);\n        WHEN fname = '/' THEN RETURN mal_divide(args);\n        ELSE raise_application_error(-20004,\n                'Invalid function call', TRUE);\n        END CASE;\n    END;\n\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n\n    repl_env('+') := types.func(M, '+');\n    repl_env('-') := types.func(M, '-');\n    repl_env('*') := types.func(M, '*');\n    repl_env('/') := types.func(M, '/');\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step3_env.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(ast integer, env integer) RETURN integer;\n    FUNCTION do_core_func(fn integer, args mal_vals)\n        RETURN integer;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(ast integer, env integer) RETURN integer IS\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(256);\n        seq      mal_vals;\n        let_env  integer;\n        i        integer;\n        f        integer;\n        args     mal_vals;\n    BEGIN\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            RETURN EVAL(types.nth(M, ast, 2), let_env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            RETURN do_core_func(f, args);\n        END CASE;\n\n    END;\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION mal_add(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int +\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION mal_subtract(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int -\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION mal_multiply(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int *\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION mal_divide(args mal_vals) RETURN integer IS\n    BEGIN\n        RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int /\n                            TREAT(M(args(2)) AS mal_int_T).val_int);\n    END;\n\n    FUNCTION do_core_func(fn integer, args mal_vals)\n        RETURN integer IS\n        fname  varchar(256);\n    BEGIN\n        IF M(fn).type_id <> 11 THEN\n            raise_application_error(-20004,\n                'Invalid function call', TRUE);\n        END IF;\n\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = '+' THEN RETURN mal_add(args);\n        WHEN fname = '-' THEN RETURN mal_subtract(args);\n        WHEN fname = '*' THEN RETURN mal_multiply(args);\n        WHEN fname = '/' THEN RETURN mal_divide(args);\n        ELSE raise_application_error(-20004,\n                'Invalid function call', TRUE);\n        END CASE;\n    END;\n\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n    x := env_pkg.env_set(M, E, repl_env, types.symbol(M, '+'),\n                                         types.func(M, '+'));\n    x := env_pkg.env_set(M, E, repl_env, types.symbol(M, '-'),\n                                         types.func(M, '-'));\n    x := env_pkg.env_set(M, E, repl_env, types.symbol(M, '*'),\n                                         types.func(M, '*'));\n    x := env_pkg.env_set(M, E, repl_env, types.symbol(M, '/'),\n                                         types.func(M, '/'));\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step4_if_fn_do.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(ast integer, env integer) RETURN integer;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(ast integer, env integer) RETURN integer IS\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        i        integer;\n        f        integer;\n        fn_env   integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            RETURN EVAL(types.nth(M, ast, 2), let_env);\n        WHEN a0sym = 'do' THEN\n            el := eval_ast(types.slice(M, ast, 1), env);\n            RETURN types.nth(M, el, types.count(M, el)-1);\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    RETURN EVAL(types.nth(M, ast, 3), env);\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                RETURN EVAL(types.nth(M, ast, 2), env);\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                RETURN EVAL(malfn.ast, fn_env);\n            ELSE\n                RETURN core.do_core_func(M, H, f, args);\n            END IF;\n        END CASE;\n\n    END;\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! not (fn* (a) (if a false true)))');\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step5_tco.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS\n        ast      integer := orig_ast;\n        env      integer := orig_env;\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        i        integer;\n        f        integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n      WHILE TRUE LOOP\n        -- io.writeline('EVAL: ' || printer.pr_str(M, ast));\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            env := let_env;\n            ast := types.nth(M, ast, 2); -- TCO\n        WHEN a0sym = 'do' THEN\n            x := types.slice(M, ast, 1, types.count(M, ast)-2);\n            x := eval_ast(x, env);\n            ast := types.nth(M, ast, types.count(M, ast)-1);  -- TCO\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    ast := types.nth(M, ast, 3);  -- TCO\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                ast := types.nth(M, ast, 2);  -- TCO\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                ast := malfn.ast;  -- TCO\n            ELSE\n                RETURN core.do_core_func(M, H, f, args);\n            END IF;\n        END CASE;\n\n      END LOOP;\n\n    END;\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! not (fn* (a) (if a false true)))');\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step6_file.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n    argv      mal_vals;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS\n        ast      integer := orig_ast;\n        env      integer := orig_env;\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        i        integer;\n        f        integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n      WHILE TRUE LOOP\n        -- io.writeline('EVAL: ' || printer.pr_str(M, ast));\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            env := let_env;\n            ast := types.nth(M, ast, 2); -- TCO\n        WHEN a0sym = 'do' THEN\n            x := types.slice(M, ast, 1, types.count(M, ast)-2);\n            x := eval_ast(x, env);\n            ast := types.nth(M, ast, types.count(M, ast)-1);  -- TCO\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    ast := types.nth(M, ast, 3);  -- TCO\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                ast := types.nth(M, ast, 2);  -- TCO\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                ast := malfn.ast;  -- TCO\n            ELSE\n                RETURN do_builtin(f, args);\n            END IF;\n        END CASE;\n\n      END LOOP;\n\n    END;\n\n    -- hack to get around lack of function references\n    -- functions that require special access to repl_env or EVAL\n    -- are implemented directly here, otherwise, core.do_core_fn\n    -- is called.\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer IS\n        fname   varchar2(100);\n        val     integer;\n        f       integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n    BEGIN\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = 'do_eval' THEN\n            RETURN EVAL(args(1), repl_env);\n        WHEN fname = 'swap!' THEN\n            val := TREAT(M(args(1)) AS mal_atom_T).val;\n            f := args(2);\n            -- slice one extra at the beginning that will be changed\n            -- to the value of the atom\n            fargs := TREAT(M(types.slice(M, args, 1)) AS mal_seq_T).val_seq;\n            fargs(1) := val;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN types.atom_reset(M, args(1), val);\n        ELSE\n            RETURN core.do_core_func(M, H, fn, args);\n        END CASE;\n    END;\n\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    argv := TREAT(M(reader.read_str(M, H, args)) AS mal_seq_T).val_seq;\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, 'eval'),\n        types.func(M, 'do_eval'));\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, '*ARGV*'),\n        types.slice(M, argv, 1));\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! not (fn* (a) (if a false true)))');\n    line := REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n\n    IF argv.COUNT() > 0 THEN\n        BEGIN\n            line := REP('(load-file \"' ||\n                    TREAT(M(argv(1)) AS mal_str_T).val_str ||\n                    '\")');\n            io.close(1);  -- close output stream\n            RETURN 0;\n        EXCEPTION WHEN OTHERS THEN\n            io.writeline('Error: ' || SQLERRM);\n            io.writeline(dbms_utility.format_error_backtrace);\n            io.close(1);  -- close output stream\n            RAISE;\n        END;\n    END IF;\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step7_quote.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n    argv      mal_vals;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer;\n\n    FUNCTION starts_with(lst integer, sym varchar) RETURNS BOOLEAN IS\n        a0 integer;\n    BEGIN\n        IF TREAT(M(lst) AS mal_seq_T).val_seq.COUNT = 2 THEN\n            a0 := types.nth(M, ast, 0)\n            RETURN M(a0).type_id = 7 AND TREAT(M(a0) AS mal_str_T).val_str = sym;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION qq_loop(elt integer, acc integer) RETURNS integer IS\n    BEGIN\n        IF M(elt).type_id = 8 AND starts_with(elt, 'splice-unquote') THEN\n            RETURN types._list(M, types.symbol('concat'), types.nth(M, a0, 1), acc);\n        END IF;\n        RETURN types.list(M, types.symbol('cons'), quasiquote(elt), acc);\n    END;\n\n    FUNCTION qq_foldr(xs integer[]) RETURNS integer IS\n        acc integer := types.list(M);\n    BEGIN\n        FOR i IN REVERSE 0 .. types._count(xs) - 1 LOOP\n            acc := qq_loop(types.nth(M, xs, i), acc);\n        END LOOP;\n        RETURN acc;\n    END;\n\n    FUNCTION quasiquote(ast integer) RETURNS integer IS\n    BEGIN\n        CASE\n        WHEN M(ast).type_id IN (7, 10) THEN\n            RETURN types.list(M, types.symbol('quote'), ast);\n        WHEN M(ast).type_id = 9 THEN\n            RETURN types._list(types.symbol('vec'), qq_folr(ast));\n        WHEN M(ast).type_id /= 8 THEN\n            RETURN ast;\n        WHEN starts_with(ast, 'unquote') THEN\n            RETURN types.nth(M, ast, 1);\n        ELSE\n            RETURN qq_foldr(ast);\n        END CASE;\n    END; $$ LANGUAGE plpgsql;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS\n        ast      integer := orig_ast;\n        env      integer := orig_env;\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        i        integer;\n        f        integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n      WHILE TRUE LOOP\n        -- io.writeline('EVAL: ' || printer.pr_str(M, ast));\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            env := let_env;\n            ast := types.nth(M, ast, 2); -- TCO\n        WHEN a0sym = 'quote' THEN\n            RETURN types.nth(M, ast, 1);\n        WHEN a0sym = 'quasiquoteexpand' THEN\n            RETURN quasiquote(types.nth(M, ast, 1));\n        WHEN a0sym = 'quasiquote' THEN\n            RETURN EVAL(quasiquote(types.nth(M, ast, 1)), env);\n        WHEN a0sym = 'do' THEN\n            x := types.slice(M, ast, 1, types.count(M, ast)-2);\n            x := eval_ast(x, env);\n            ast := types.nth(M, ast, types.count(M, ast)-1);  -- TCO\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    ast := types.nth(M, ast, 3);  -- TCO\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                ast := types.nth(M, ast, 2);  -- TCO\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                ast := malfn.ast;  -- TCO\n            ELSE\n                RETURN do_builtin(f, args);\n            END IF;\n        END CASE;\n\n      END LOOP;\n\n    END;\n\n    -- hack to get around lack of function references\n    -- functions that require special access to repl_env or EVAL\n    -- are implemented directly here, otherwise, core.do_core_fn\n    -- is called.\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer IS\n        fname   varchar2(100);\n        val     integer;\n        f       integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n    BEGIN\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = 'do_eval' THEN\n            RETURN EVAL(args(1), repl_env);\n        WHEN fname = 'swap!' THEN\n            val := TREAT(M(args(1)) AS mal_atom_T).val;\n            f := args(2);\n            -- slice one extra at the beginning that will be changed\n            -- to the value of the atom\n            fargs := TREAT(M(types.slice(M, args, 1)) AS mal_seq_T).val_seq;\n            fargs(1) := val;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN types.atom_reset(M, args(1), val);\n        ELSE\n            RETURN core.do_core_func(M, H, fn, args);\n        END CASE;\n    END;\n\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    argv := TREAT(M(reader.read_str(M, H, args)) AS mal_seq_T).val_seq;\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, 'eval'),\n        types.func(M, 'do_eval'));\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, '*ARGV*'),\n        types.slice(M, argv, 1));\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! not (fn* (a) (if a false true)))');\n    line := REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n\n    IF argv.COUNT() > 0 THEN\n        BEGIN\n            line := REP('(load-file \"' ||\n                    TREAT(M(argv(1)) AS mal_str_T).val_str ||\n                    '\")');\n            io.close(1);  -- close output stream\n            RETURN 0;\n        EXCEPTION WHEN OTHERS THEN\n            io.writeline('Error: ' || SQLERRM);\n            io.writeline(dbms_utility.format_error_backtrace);\n            io.close(1);  -- close output stream\n            RAISE;\n        END;\n    END IF;\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step8_macros.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n    argv      mal_vals;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer;\n\n    FUNCTION starts_with(lst integer, sym varchar) RETURNS BOOLEAN IS\n        a0 integer;\n    BEGIN\n        IF TREAT(M(lst) AS mal_seq_T).val_seq.COUNT = 2 THEN\n            a0 := types.nth(M, ast, 0)\n            RETURN M(a0).type_id = 7 AND TREAT(M(a0) AS mal_str_T).val_str = sym;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION qq_loop(elt integer, acc integer) RETURNS integer IS\n    BEGIN\n        IF M(elt).type_id = 8 AND starts_with(elt, 'splice-unquote') THEN\n            RETURN types._list(M, types.symbol('concat'), types.nth(M, a0, 1), acc);\n        END IF;\n        RETURN types.list(M, types.symbol('cons'), quasiquote(elt), acc);\n    END;\n\n    FUNCTION qq_foldr(xs integer[]) RETURNS integer IS\n        acc integer := types.list(M);\n    BEGIN\n        FOR i IN REVERSE 0 .. types._count(xs) - 1 LOOP\n            acc := qq_loop(types.nth(M, xs, i), acc);\n        END LOOP;\n        RETURN acc;\n    END;\n\n    FUNCTION quasiquote(ast integer) RETURNS integer IS\n    BEGIN\n        CASE\n        WHEN M(ast).type_id IN (7, 10) THEN\n            RETURN types.list(M, types.symbol('quote'), ast);\n        WHEN M(ast).type_id = 9 THEN\n            RETURN types._list(types.symbol('vec'), qq_folr(ast));\n        WHEN M(ast).type_id /= 8 THEN\n            RETURN ast;\n        WHEN starts_with(ast, 'unquote') THEN\n            RETURN types.nth(M, ast, 1);\n        ELSE\n            RETURN qq_foldr(ast);\n        END CASE;\n    END; $$ LANGUAGE plpgsql;\n\n    FUNCTION is_macro_call(ast integer, env integer) RETURN BOOLEAN IS\n        a0   integer;\n        mac  integer;\n    BEGIN\n        IF M(ast).type_id = 8 THEN\n            a0 := types.nth(M, ast, 0);\n            IF M(a0).type_id = 7 AND\n               env_pkg.env_find(M, E, env, a0) IS NOT NULL THEN\n                mac := env_pkg.env_get(M, E, env, a0);\n                IF M(mac).type_id = 12 THEN\n                    RETURN TREAT(M(mac) AS mal_func_T).is_macro > 0;\n                END IF;\n            END IF;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION macroexpand(orig_ast integer, env integer) RETURN integer IS\n        ast     integer;\n        mac     integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n    BEGIN\n        ast := orig_ast;\n        WHILE is_macro_call(ast, env) LOOP\n            mac := env_pkg.env_get(M, E, env, types.nth(M, ast, 0));\n            fargs := TREAT(M(types.slice(M, ast, 1)) as mal_seq_T).val_seq;\n            if M(mac).type_id = 12 THEN\n                malfn := TREAT(M(mac) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params,\n                                          fargs);\n                ast := EVAL(malfn.ast, fn_env);\n            ELSE\n                ast := do_builtin(mac, fargs);\n            END IF;\n        END LOOP;\n        RETURN ast;\n    END;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS\n        ast      integer := orig_ast;\n        env      integer := orig_env;\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        i        integer;\n        f        integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n      WHILE TRUE LOOP\n        -- io.writeline('EVAL: ' || printer.pr_str(M, H, ast));\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n\n        -- apply\n        ast := macroexpand(ast, env);\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            env := let_env;\n            ast := types.nth(M, ast, 2); -- TCO\n        WHEN a0sym = 'quote' THEN\n            RETURN types.nth(M, ast, 1);\n        WHEN a0sym = 'quasiquoteexpand' THEN\n            RETURN quasiquote(types.nth(M, ast, 1));\n        WHEN a0sym = 'quasiquote' THEN\n            RETURN EVAL(quasiquote(types.nth(M, ast, 1)), env);\n        WHEN a0sym = 'defmacro!' THEN\n            x := EVAL(types.nth(M, ast, 2), env);\n            malfn := TREAT(M(x) as mal_func_T);\n            malfn.is_macro := 1;\n            M(x) := malfn;\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), x);\n        WHEN a0sym = 'macroexpand' THEN\n            RETURN macroexpand(types.nth(M, ast, 1), env);\n        WHEN a0sym = 'do' THEN\n            x := types.slice(M, ast, 1, types.count(M, ast)-2);\n            x := eval_ast(x, env);\n            ast := types.nth(M, ast, types.count(M, ast)-1);  -- TCO\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    ast := types.nth(M, ast, 3);  -- TCO\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                ast := types.nth(M, ast, 2);  -- TCO\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                ast := malfn.ast;  -- TCO\n            ELSE\n                RETURN do_builtin(f, args);\n            END IF;\n        END CASE;\n\n      END LOOP;\n\n    END;\n\n    -- hack to get around lack of function references\n    -- functions that require special access to repl_env or EVAL\n    -- are implemented directly here, otherwise, core.do_core_fn\n    -- is called.\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer IS\n        fname   varchar2(100);\n        val     integer;\n        f       integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n    BEGIN\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = 'do_eval' THEN\n            RETURN EVAL(args(1), repl_env);\n        WHEN fname = 'swap!' THEN\n            val := TREAT(M(args(1)) AS mal_atom_T).val;\n            f := args(2);\n            -- slice one extra at the beginning that will be changed\n            -- to the value of the atom\n            fargs := TREAT(M(types.slice(M, args, 1)) AS mal_seq_T).val_seq;\n            fargs(1) := val;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN types.atom_reset(M, args(1), val);\n        ELSE\n            RETURN core.do_core_func(M, H, fn, args);\n        END CASE;\n    END;\n\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    argv := TREAT(M(reader.read_str(M, H, args)) AS mal_seq_T).val_seq;\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, 'eval'),\n        types.func(M, 'do_eval'));\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, '*ARGV*'),\n        types.slice(M, argv, 1));\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! not (fn* (a) (if a false true)))');\n    line := REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n    line := REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))');\n\n    IF argv.COUNT() > 0 THEN\n        BEGIN\n            line := REP('(load-file \"' ||\n                    TREAT(M(argv(1)) AS mal_str_T).val_str ||\n                    '\")');\n            io.close(1);  -- close output stream\n            RETURN 0;\n        EXCEPTION WHEN OTHERS THEN\n            io.writeline('Error: ' || SQLERRM);\n            io.writeline(dbms_utility.format_error_backtrace);\n            io.close(1);  -- close output stream\n            RAISE;\n        END;\n    END IF;\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                io.writeline('Error: ' || SQLERRM);\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/step9_try.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n    argv      mal_vals;\n    err_val   integer;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer;\n\n    FUNCTION starts_with(lst integer, sym varchar) RETURNS BOOLEAN IS\n        a0 integer;\n    BEGIN\n        IF TREAT(M(lst) AS mal_seq_T).val_seq.COUNT = 2 THEN\n            a0 := types.nth(M, ast, 0)\n            RETURN M(a0).type_id = 7 AND TREAT(M(a0) AS mal_str_T).val_str = sym;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION qq_loop(elt integer, acc integer) RETURNS integer IS\n    BEGIN\n        IF M(elt).type_id = 8 AND starts_with(elt, 'splice-unquote') THEN\n            RETURN types._list(M, types.symbol('concat'), types.nth(M, a0, 1), acc);\n        END IF;\n        RETURN types.list(M, types.symbol('cons'), quasiquote(elt), acc);\n    END;\n\n    FUNCTION qq_foldr(xs integer[]) RETURNS integer IS\n        acc integer := types.list(M);\n    BEGIN\n        FOR i IN REVERSE 0 .. types._count(xs) - 1 LOOP\n            acc := qq_loop(types.nth(M, xs, i), acc);\n        END LOOP;\n        RETURN acc;\n    END;\n\n    FUNCTION quasiquote(ast integer) RETURNS integer IS\n    BEGIN\n        CASE\n        WHEN M(ast).type_id IN (7, 10) THEN\n            RETURN types.list(M, types.symbol('quote'), ast);\n        WHEN M(ast).type_id = 9 THEN\n            RETURN types._list(types.symbol('vec'), qq_folr(ast));\n        WHEN M(ast).type_id /= 8 THEN\n            RETURN ast;\n        WHEN starts_with(ast, 'unquote') THEN\n            RETURN types.nth(M, ast, 1);\n        ELSE\n            RETURN qq_foldr(ast);\n        END CASE;\n    END; $$ LANGUAGE plpgsql;\n\n    FUNCTION is_macro_call(ast integer, env integer) RETURN BOOLEAN IS\n        a0   integer;\n        mac  integer;\n    BEGIN\n        IF M(ast).type_id = 8 THEN\n            a0 := types.nth(M, ast, 0);\n            IF M(a0).type_id = 7 AND\n               env_pkg.env_find(M, E, env, a0) IS NOT NULL THEN\n                mac := env_pkg.env_get(M, E, env, a0);\n                IF M(mac).type_id = 12 THEN\n                    RETURN TREAT(M(mac) AS mal_func_T).is_macro > 0;\n                END IF;\n            END IF;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION macroexpand(orig_ast integer, env integer) RETURN integer IS\n        ast     integer;\n        mac     integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n    BEGIN\n        ast := orig_ast;\n        WHILE is_macro_call(ast, env) LOOP\n            mac := env_pkg.env_get(M, E, env, types.nth(M, ast, 0));\n            fargs := TREAT(M(types.slice(M, ast, 1)) as mal_seq_T).val_seq;\n            if M(mac).type_id = 12 THEN\n                malfn := TREAT(M(mac) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params,\n                                          fargs);\n                ast := EVAL(malfn.ast, fn_env);\n            ELSE\n                ast := do_builtin(mac, fargs);\n            END IF;\n        END LOOP;\n        RETURN ast;\n    END;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS\n        ast      integer := orig_ast;\n        env      integer := orig_env;\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        try_env  integer;\n        i        integer;\n        f        integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n      WHILE TRUE LOOP\n        -- io.writeline('EVAL: ' || printer.pr_str(M, H, ast));\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n\n        -- apply\n        ast := macroexpand(ast, env);\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            env := let_env;\n            ast := types.nth(M, ast, 2); -- TCO\n        WHEN a0sym = 'quote' THEN\n            RETURN types.nth(M, ast, 1);\n        WHEN a0sym = 'quasiquoteexpand' THEN\n            RETURN quasiquote(types.nth(M, ast, 1));\n        WHEN a0sym = 'quasiquote' THEN\n            RETURN EVAL(quasiquote(types.nth(M, ast, 1)), env);\n        WHEN a0sym = 'defmacro!' THEN\n            x := EVAL(types.nth(M, ast, 2), env);\n            malfn := TREAT(M(x) as mal_func_T);\n            malfn.is_macro := 1;\n            M(x) := malfn;\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), x);\n        WHEN a0sym = 'macroexpand' THEN\n            RETURN macroexpand(types.nth(M, ast, 1), env);\n        WHEN a0sym = 'try*' THEN\n            DECLARE\n                exc     integer;\n                a2      integer := -1;\n                a20     integer := -1;\n                a20sym  varchar2(100);\n            BEGIN\n                RETURN EVAL(types.nth(M, ast, 1), env);\n\n            EXCEPTION WHEN OTHERS THEN\n                IF types.count(M, ast) > 2 THEN\n                    a2 := types.nth(M, ast, 2);\n                    IF M(a2).type_id = 8 THEN\n                        a20 := types.nth(M, a2, 0);\n                        IF M(a20).type_id = 7 THEN\n                            a20sym := TREAT(M(a20) AS mal_str_T).val_str;\n                        END IF;\n                    END IF;\n                END IF;\n                IF a20sym = 'catch*' THEN\n                    IF SQLCODE <> -20000 THEN\n                        IF SQLCODE < -20000 AND SQLCODE > -20100 THEN\n                            exc := types.string(M,\n                                REGEXP_REPLACE(SQLERRM,\n                                    '^ORA-200[0-9][0-9]: '));\n                        ELSE\n                            exc := types.string(M, SQLERRM);\n                        END IF;\n                    ELSE  -- mal throw\n                        exc := err_val;\n                        err_val := NULL;\n                    END IF;\n                    try_env := env_pkg.env_new(M, E, env,\n                        types.list(M, types.nth(M, a2, 1)),\n                        mal_vals(exc));\n                    RETURN EVAL(types.nth(M, a2, 2), try_env);\n                END IF;\n                RAISE; -- not handled, re-raise the exception\n            END;\n        WHEN a0sym = 'do' THEN\n            x := types.slice(M, ast, 1, types.count(M, ast)-2);\n            x := eval_ast(x, env);\n            ast := types.nth(M, ast, types.count(M, ast)-1);  -- TCO\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    ast := types.nth(M, ast, 3);  -- TCO\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                ast := types.nth(M, ast, 2);  -- TCO\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                ast := malfn.ast;  -- TCO\n            ELSE\n                RETURN do_builtin(f, args);\n            END IF;\n        END CASE;\n\n      END LOOP;\n\n    END;\n\n    -- hack to get around lack of function references\n    -- functions that require special access to repl_env or EVAL\n    -- are implemented directly here, otherwise, core.do_core_fn\n    -- is called.\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer IS\n        fname   varchar2(100);\n        val     integer;\n        f       integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n        i       integer;\n        tseq    mal_vals;\n    BEGIN\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = 'do_eval' THEN\n            RETURN EVAL(args(1), repl_env);\n        WHEN fname = 'swap!' THEN\n            val := TREAT(M(args(1)) AS mal_atom_T).val;\n            f := args(2);\n            -- slice one extra at the beginning that will be changed\n            -- to the value of the atom\n            fargs := TREAT(M(types.slice(M, args, 1)) AS mal_seq_T).val_seq;\n            fargs(1) := val;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN types.atom_reset(M, args(1), val);\n        WHEN fname = 'apply' THEN\n            f := args(1);\n            fargs := mal_vals();\n            tseq := TREAT(M(args(args.COUNT())) AS mal_seq_T).val_seq;\n            fargs.EXTEND(args.COUNT()-2 + tseq.COUNT());\n            FOR i IN 1..args.COUNT()-2 LOOP\n                fargs(i) := args(i+1);\n            END LOOP;\n            FOR i IN 1..tseq.COUNT() LOOP\n                fargs(args.COUNT()-2 + i) := tseq(i);\n            END LOOP;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN val;\n        WHEN fname = 'map' THEN\n            f := args(1);\n            fargs := TREAT(M(args(2)) AS mal_seq_T).val_seq;\n            tseq := mal_vals();\n            tseq.EXTEND(fargs.COUNT());\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                FOR i IN 1..fargs.COUNT() LOOP\n                    fn_env := env_pkg.env_new(M, E, malfn.env,\n                        malfn.params,\n                        mal_vals(fargs(i)));\n                    tseq(i) := EVAL(malfn.ast, fn_env);\n                END LOOP;\n            ELSE\n                FOR i IN 1..fargs.COUNT() LOOP\n                    tseq(i) := do_builtin(f,\n                        mal_vals(fargs(i)));\n                END LOOP;\n            END IF;\n            RETURN types.seq(M, 8, tseq);\n        WHEN fname = 'throw' THEN\n            err_val := args(1);\n            raise_application_error(-20000, 'MalException', TRUE);\n        ELSE\n            RETURN core.do_core_func(M, H, fn, args);\n        END CASE;\n    END;\n\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    argv := TREAT(M(reader.read_str(M, H, args)) AS mal_seq_T).val_seq;\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, 'eval'),\n        types.func(M, 'do_eval'));\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, '*ARGV*'),\n        types.slice(M, argv, 1));\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! not (fn* (a) (if a false true)))');\n    line := REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n    line := REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))');\n\n    IF argv.COUNT() > 0 THEN\n        BEGIN\n            line := REP('(load-file \"' ||\n                    TREAT(M(argv(1)) AS mal_str_T).val_str ||\n                    '\")');\n            io.close(1);  -- close output stream\n            RETURN 0;\n        EXCEPTION WHEN OTHERS THEN\n            io.writeline('Error: ' || SQLERRM);\n            io.writeline(dbms_utility.format_error_backtrace);\n            io.close(1);  -- close output stream\n            RAISE;\n        END;\n    END IF;\n\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                IF SQLCODE <> -20000 THEN\n                    io.writeline('Error: ' || SQLERRM);\n                ELSE\n                    io.writeline('Error: ' || printer.pr_str(M, H, err_val));\n                END IF;\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/stepA_mal.sql",
    "content": "@io.sql\n@types.sql\n@reader.sql\n@printer.sql\n@env.sql\n@core.sql\n\nCREATE OR REPLACE PACKAGE mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer;\n\nEND mal;\n/\n\nCREATE OR REPLACE PACKAGE BODY mal IS\n\nFUNCTION MAIN(args varchar DEFAULT '()') RETURN integer IS\n    M         types.mal_table;                 -- general mal value memory pool\n    H         types.map_entry_table;    -- hashmap memory pool\n    E         env_pkg.env_entry_table;  -- mal env memory pool\n    repl_env  integer;\n    x         integer;\n    line      CLOB;\n    core_ns   core_ns_T;\n    cidx      integer;\n    argv      mal_vals;\n    err_val   integer;\n\n    -- read\n    FUNCTION READ(line varchar) RETURN integer IS\n    BEGIN\n        RETURN reader.read_str(M, H, line);\n    END;\n\n    -- eval\n\n    -- forward declarations\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer;\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer;\n\n    FUNCTION starts_with(lst integer, sym varchar) RETURNS BOOLEAN IS\n        a0 integer;\n    BEGIN\n        IF TREAT(M(lst) AS mal_seq_T).val_seq.COUNT = 2 THEN\n            a0 := types.nth(M, ast, 0)\n            RETURN M(a0).type_id = 7 AND TREAT(M(a0) AS mal_str_T).val_str = sym;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION qq_loop(elt integer, acc integer) RETURNS integer IS\n    BEGIN\n        IF M(elt).type_id = 8 AND starts_with(elt, 'splice-unquote') THEN\n            RETURN types._list(M, types.symbol('concat'), types.nth(M, a0, 1), acc);\n        END IF;\n        RETURN types.list(M, types.symbol('cons'), quasiquote(elt), acc);\n    END;\n\n    FUNCTION qq_foldr(xs integer[]) RETURNS integer IS\n        acc integer := types.list(M);\n    BEGIN\n        FOR i IN REVERSE 0 .. types._count(xs) - 1 LOOP\n            acc := qq_loop(types.nth(M, xs, i), acc);\n        END LOOP;\n        RETURN acc;\n    END;\n\n    FUNCTION quasiquote(ast integer) RETURNS integer IS\n    BEGIN\n        CASE\n        WHEN M(ast).type_id IN (7, 10) THEN\n            RETURN types.list(M, types.symbol('quote'), ast);\n        WHEN M(ast).type_id = 9 THEN\n            RETURN types._list(types.symbol('vec'), qq_folr(ast));\n        WHEN M(ast).type_id /= 8 THEN\n            RETURN ast;\n        WHEN starts_with(ast, 'unquote') THEN\n            RETURN types.nth(M, ast, 1);\n        ELSE\n            RETURN qq_foldr(ast);\n        END CASE;\n    END; $$ LANGUAGE plpgsql;\n\n    FUNCTION is_macro_call(ast integer, env integer) RETURN BOOLEAN IS\n        a0   integer;\n        mac  integer;\n    BEGIN\n        IF M(ast).type_id = 8 THEN\n            a0 := types.nth(M, ast, 0);\n            IF M(a0).type_id = 7 AND\n               env_pkg.env_find(M, E, env, a0) IS NOT NULL THEN\n                mac := env_pkg.env_get(M, E, env, a0);\n                IF M(mac).type_id = 12 THEN\n                    RETURN TREAT(M(mac) AS mal_func_T).is_macro > 0;\n                END IF;\n            END IF;\n        END IF;\n        RETURN FALSE;\n    END;\n\n    FUNCTION macroexpand(orig_ast integer, env integer) RETURN integer IS\n        ast     integer;\n        mac     integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n    BEGIN\n        ast := orig_ast;\n        WHILE is_macro_call(ast, env) LOOP\n            mac := env_pkg.env_get(M, E, env, types.nth(M, ast, 0));\n            fargs := TREAT(M(types.slice(M, ast, 1)) as mal_seq_T).val_seq;\n            if M(mac).type_id = 12 THEN\n                malfn := TREAT(M(mac) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params,\n                                          fargs);\n                ast := EVAL(malfn.ast, fn_env);\n            ELSE\n                ast := do_builtin(mac, fargs);\n            END IF;\n        END LOOP;\n        RETURN ast;\n    END;\n\n    FUNCTION eval_ast(ast integer, env integer) RETURN integer IS\n        i         integer;\n        old_seq   mal_vals;\n        new_seq   mal_vals;\n        new_hm    integer;\n        old_midx  integer;\n        new_midx  integer;\n        k         varchar2(256);\n    BEGIN\n        IF M(ast).type_id = 7 THEN\n            RETURN env_pkg.env_get(M, E, env, ast);\n        ELSIF M(ast).type_id IN (8,9) THEN\n            old_seq := TREAT(M(ast) AS mal_seq_T).val_seq;\n            new_seq := mal_vals();\n            new_seq.EXTEND(old_seq.COUNT);\n            FOR i IN 1..old_seq.COUNT LOOP\n                new_seq(i) := EVAL(old_seq(i), env);\n            END LOOP;\n            RETURN types.seq(M, M(ast).type_id, new_seq);\n        ELSIF M(ast).type_id IN (10) THEN\n            new_hm := types.hash_map(M, H, mal_vals());\n            old_midx := TREAT(M(ast) AS mal_map_T).map_idx;\n            new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n            k := H(old_midx).FIRST();\n            WHILE k IS NOT NULL LOOP\n                H(new_midx)(k) := EVAL(H(old_midx)(k), env);\n                k := H(old_midx).NEXT(k);\n            END LOOP;\n            RETURN new_hm;\n        ELSE\n            RETURN ast;\n        END IF;\n    END;\n\n    FUNCTION EVAL(orig_ast integer, orig_env integer) RETURN integer IS\n        ast      integer := orig_ast;\n        env      integer := orig_env;\n        el       integer;\n        a0       integer;\n        a0sym    varchar2(100);\n        seq      mal_vals;\n        let_env  integer;\n        try_env  integer;\n        i        integer;\n        f        integer;\n        cond     integer;\n        malfn    mal_func_T;\n        args     mal_vals;\n    BEGIN\n      WHILE TRUE LOOP\n        -- io.writeline('EVAL: ' || printer.pr_str(M, H, ast));\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n\n        -- apply\n        ast := macroexpand(ast, env);\n        IF M(ast).type_id <> 8 THEN\n            RETURN eval_ast(ast, env);\n        END IF;\n        IF types.count(M, ast) = 0 THEN\n            RETURN ast; -- empty list just returned\n        END IF;\n\n        -- apply\n        a0 := types.first(M, ast);\n        if M(a0).type_id = 7 THEN -- symbol\n            a0sym := TREAT(M(a0) AS mal_str_T).val_str;\n        ELSE\n            a0sym := '__<*fn*>__';\n        END IF;\n\n        CASE\n        WHEN a0sym = 'def!' THEN\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), EVAL(types.nth(M, ast, 2), env));\n        WHEN a0sym = 'let*' THEN\n            let_env := env_pkg.env_new(M, E, env);\n            seq := TREAT(M(types.nth(M, ast, 1)) AS mal_seq_T).val_seq;\n            i := 1;\n            WHILE i <= seq.COUNT LOOP\n                x := env_pkg.env_set(M, E, let_env,\n                    seq(i), EVAL(seq(i+1), let_env));\n                i := i + 2;\n            END LOOP;\n            env := let_env;\n            ast := types.nth(M, ast, 2); -- TCO\n        WHEN a0sym = 'quote' THEN\n            RETURN types.nth(M, ast, 1);\n        WHEN a0sym = 'quasiquoteexpand' THEN\n            RETURN quasiquote(types.nth(M, ast, 1));\n        WHEN a0sym = 'quasiquote' THEN\n            RETURN EVAL(quasiquote(types.nth(M, ast, 1)), env);\n        WHEN a0sym = 'defmacro!' THEN\n            x := EVAL(types.nth(M, ast, 2), env);\n            malfn := TREAT(M(x) as mal_func_T);\n            malfn.is_macro := 1;\n            M(x) := malfn;\n            RETURN env_pkg.env_set(M, E, env,\n                types.nth(M, ast, 1), x);\n        WHEN a0sym = 'macroexpand' THEN\n            RETURN macroexpand(types.nth(M, ast, 1), env);\n        WHEN a0sym = 'try*' THEN\n            DECLARE\n                exc     integer;\n                a2      integer := -1;\n                a20     integer := -1;\n                a20sym  varchar2(100);\n            BEGIN\n                RETURN EVAL(types.nth(M, ast, 1), env);\n\n            EXCEPTION WHEN OTHERS THEN\n                IF types.count(M, ast) > 2 THEN\n                    a2 := types.nth(M, ast, 2);\n                    IF M(a2).type_id = 8 THEN\n                        a20 := types.nth(M, a2, 0);\n                        IF M(a20).type_id = 7 THEN\n                            a20sym := TREAT(M(a20) AS mal_str_T).val_str;\n                        END IF;\n                    END IF;\n                END IF;\n                IF a20sym = 'catch*' THEN\n                    IF SQLCODE <> -20000 THEN\n                        IF SQLCODE < -20000 AND SQLCODE > -20100 THEN\n                            exc := types.string(M,\n                                REGEXP_REPLACE(SQLERRM,\n                                    '^ORA-200[0-9][0-9]: '));\n                        ELSE\n                            exc := types.string(M, SQLERRM);\n                        END IF;\n                    ELSE  -- mal throw\n                        exc := err_val;\n                        err_val := NULL;\n                    END IF;\n                    try_env := env_pkg.env_new(M, E, env,\n                        types.list(M, types.nth(M, a2, 1)),\n                        mal_vals(exc));\n                    RETURN EVAL(types.nth(M, a2, 2), try_env);\n                END IF;\n                RAISE; -- not handled, re-raise the exception\n            END;\n        WHEN a0sym = 'do' THEN\n            x := types.slice(M, ast, 1, types.count(M, ast)-2);\n            x := eval_ast(x, env);\n            ast := types.nth(M, ast, types.count(M, ast)-1);  -- TCO\n        WHEN a0sym = 'if' THEN\n            cond := EVAL(types.nth(M, ast, 1), env);\n            IF cond = 1 OR cond = 2 THEN  -- nil or false\n                IF types.count(M, ast) > 3 THEN\n                    ast := types.nth(M, ast, 3);  -- TCO\n                ELSE\n                    RETURN 1;  -- nil\n                END IF;\n            ELSE\n                ast := types.nth(M, ast, 2);  -- TCO\n            END IF;\n        WHEN a0sym = 'fn*' THEN\n            RETURN types.malfunc(M, types.nth(M, ast, 2),\n                                    types.nth(M, ast, 1),\n                                    env);\n        ELSE\n            el := eval_ast(ast, env);\n            f := types.first(M, el);\n            args := TREAT(M(types.slice(M, el, 1)) AS mal_seq_T).val_seq;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, args);\n                ast := malfn.ast;  -- TCO\n            ELSE\n                RETURN do_builtin(f, args);\n            END IF;\n        END CASE;\n\n      END LOOP;\n\n    END;\n\n    -- hack to get around lack of function references\n    -- functions that require special access to repl_env or EVAL\n    -- are implemented directly here, otherwise, core.do_core_fn\n    -- is called.\n    FUNCTION do_builtin(fn integer, args mal_vals) RETURN integer IS\n        fname   varchar2(100);\n        val     integer;\n        f       integer;\n        malfn   mal_func_T;\n        fargs   mal_vals;\n        fn_env  integer;\n        i       integer;\n        tseq    mal_vals;\n    BEGIN\n        fname := TREAT(M(fn) AS mal_str_T).val_str;\n        CASE\n        WHEN fname = 'do_eval' THEN\n            RETURN EVAL(args(1), repl_env);\n        WHEN fname = 'swap!' THEN\n            val := TREAT(M(args(1)) AS mal_atom_T).val;\n            f := args(2);\n            -- slice one extra at the beginning that will be changed\n            -- to the value of the atom\n            fargs := TREAT(M(types.slice(M, args, 1)) AS mal_seq_T).val_seq;\n            fargs(1) := val;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN types.atom_reset(M, args(1), val);\n        WHEN fname = 'apply' THEN\n            f := args(1);\n            fargs := mal_vals();\n            tseq := TREAT(M(args(args.COUNT())) AS mal_seq_T).val_seq;\n            fargs.EXTEND(args.COUNT()-2 + tseq.COUNT());\n            FOR i IN 1..args.COUNT()-2 LOOP\n                fargs(i) := args(i+1);\n            END LOOP;\n            FOR i IN 1..tseq.COUNT() LOOP\n                fargs(args.COUNT()-2 + i) := tseq(i);\n            END LOOP;\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                fn_env := env_pkg.env_new(M, E, malfn.env,\n                                          malfn.params, fargs);\n                val := EVAL(malfn.ast, fn_env);\n            ELSE\n                val := do_builtin(f, fargs);\n            END IF;\n            RETURN val;\n        WHEN fname = 'map' THEN\n            f := args(1);\n            fargs := TREAT(M(args(2)) AS mal_seq_T).val_seq;\n            tseq := mal_vals();\n            tseq.EXTEND(fargs.COUNT());\n            IF M(f).type_id = 12 THEN\n                malfn := TREAT(M(f) AS mal_func_T);\n                FOR i IN 1..fargs.COUNT() LOOP\n                    fn_env := env_pkg.env_new(M, E, malfn.env,\n                        malfn.params,\n                        mal_vals(fargs(i)));\n                    tseq(i) := EVAL(malfn.ast, fn_env);\n                END LOOP;\n            ELSE\n                FOR i IN 1..fargs.COUNT() LOOP\n                    tseq(i) := do_builtin(f,\n                        mal_vals(fargs(i)));\n                END LOOP;\n            END IF;\n            RETURN types.seq(M, 8, tseq);\n        WHEN fname = 'throw' THEN\n            err_val := args(1);\n            raise_application_error(-20000, 'MalException', TRUE);\n        ELSE\n            RETURN core.do_core_func(M, H, fn, args);\n        END CASE;\n    END;\n\n\n    -- print\n    FUNCTION PRINT(exp integer) RETURN varchar IS\n    BEGIN\n        RETURN printer.pr_str(M, H, exp);\n    END;\n\n    -- repl\n    FUNCTION REP(line varchar) RETURN varchar IS\n    BEGIN\n        RETURN PRINT(EVAL(READ(line), repl_env));\n    END;\n\nBEGIN\n    -- initialize memory pools\n    M := types.mem_new();\n    H := types.map_entry_table();\n    E := env_pkg.env_entry_table();\n\n    repl_env := env_pkg.env_new(M, E, NULL);\n\n    argv := TREAT(M(reader.read_str(M, H, args)) AS mal_seq_T).val_seq;\n\n    -- core.EXT: defined using PL/SQL\n    core_ns := core.get_core_ns();\n    FOR cidx IN 1..core_ns.COUNT LOOP\n        x := env_pkg.env_set(M, E, repl_env,\n            types.symbol(M, core_ns(cidx)),\n            types.func(M, core_ns(cidx)));\n    END LOOP;\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, 'eval'),\n        types.func(M, 'do_eval'));\n    x := env_pkg.env_set(M, E, repl_env,\n        types.symbol(M, '*ARGV*'),\n        types.slice(M, argv, 1));\n\n    -- core.mal: defined using the language itself\n    line := REP('(def! *host-language* \"PL/SQL\")');\n    line := REP('(def! not (fn* (a) (if a false true)))');\n    line := REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))');\n    line := REP('(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list ''if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons ''cond (rest (rest xs)))))))');\n\n    IF argv.COUNT() > 0 THEN\n        BEGIN\n            line := REP('(load-file \"' ||\n                    TREAT(M(argv(1)) AS mal_str_T).val_str ||\n                    '\")');\n            io.close(1);  -- close output stream\n            RETURN 0;\n        EXCEPTION WHEN OTHERS THEN\n            io.writeline('Error: ' || SQLERRM);\n            io.writeline(dbms_utility.format_error_backtrace);\n            io.close(1);  -- close output stream\n            RAISE;\n        END;\n    END IF;\n\n    line := REP('(println (str \"Mal [\" *host-language* \"]\"))');\n    WHILE true LOOP\n        BEGIN\n            line := io.readline('user> ', 0);\n            IF line = EMPTY_CLOB() THEN CONTINUE; END IF;\n            IF line IS NOT NULL THEN\n                io.writeline(REP(line));\n            END IF;\n\n            EXCEPTION WHEN OTHERS THEN\n                IF SQLCODE = -20001 THEN  -- io read stream closed\n                    io.close(1);  -- close output stream\n                    RETURN 0;\n                END IF;\n                IF SQLCODE <> -20000 THEN\n                    io.writeline('Error: ' || SQLERRM);\n                ELSE\n                    io.writeline('Error: ' || printer.pr_str(M, H, err_val));\n                END IF;\n                io.writeline(dbms_utility.format_error_backtrace);\n        END;\n    END LOOP;\nEND;\n\nEND mal;\n/\nshow errors;\n\nquit;\n"
  },
  {
    "path": "impls/plsql/types.sql",
    "content": "-- ---------------------------------------------------------\n-- persistent values\n\nBEGIN\n  EXECUTE IMMEDIATE 'DROP TYPE mal_T FORCE';\nEXCEPTION\n  WHEN OTHERS THEN IF SQLCODE != -4043 THEN RAISE; END IF;\nEND;\n/\n\n-- list of types for type_id\n-- 0:  nil\n-- 1:  false\n-- 2:  true\n-- 3:  integer\n-- 4:  float\n-- 5:  string\n-- 6:  long string (CLOB)\n-- 7:  symbol\n-- 8:  list\n-- 9:  vector\n-- 10: hashmap\n-- 11: function\n-- 12: malfunc\n-- 13: atom\n\n-- nil (0), false (1), true (2)\nCREATE OR REPLACE TYPE mal_T FORCE AS OBJECT (\n    type_id  integer\n) NOT FINAL;\n/\n\n-- general nested table of mal values (integers)\n-- used frequently for argument passing\nCREATE OR REPLACE TYPE mal_vals FORCE AS TABLE OF integer;\n/\n\n\n-- integer (3)\nCREATE OR REPLACE TYPE mal_int_T FORCE UNDER mal_T (\n    val_int  integer\n) FINAL;\n/\n\n-- string/keyword (5,6), symbol (7)\nCREATE OR REPLACE TYPE mal_str_T FORCE UNDER mal_T (\n    val_str  varchar2(4000)\n) NOT FINAL;\n/\n\nCREATE OR REPLACE TYPE mal_long_str_T FORCE UNDER mal_str_T (\n    val_long_str  CLOB  -- long character object (for larger than 4000 chars)\n) FINAL;\n/\nshow errors;\n\n-- list (8), vector (9)\nCREATE OR REPLACE TYPE mal_seq_T FORCE UNDER mal_T (\n    val_seq  mal_vals,\n    meta     integer\n) FINAL;\n/\n\nCREATE OR REPLACE TYPE mal_map_T FORCE UNDER mal_T (\n    map_idx  integer,  -- index into map entry table\n    meta     integer\n) FINAL;\n/\n\n-- malfunc (12)\nCREATE OR REPLACE TYPE mal_func_T FORCE UNDER mal_T (\n    ast       integer,\n    params    integer,\n    env       integer,\n    is_macro  integer,\n    meta      integer\n) FINAL;\n/\n\n-- atom (13)\nCREATE OR REPLACE TYPE mal_atom_T FORCE UNDER mal_T (\n    val  integer  -- index into mal_table\n);\n/\n\n\n-- ---------------------------------------------------------\n\nCREATE OR REPLACE PACKAGE types IS\n    -- memory pool for mal_objects (non-hash-map)\n    TYPE mal_table IS TABLE OF mal_T;\n\n    -- memory pool for hash-map objects\n    TYPE map_entry IS TABLE OF integer INDEX BY varchar2(256);\n    TYPE map_entry_table IS TABLE OF map_entry;\n\n    -- general functions\n    FUNCTION mem_new RETURN mal_table;\n\n    FUNCTION tf(val boolean) RETURN integer;\n    FUNCTION equal_Q(M IN OUT NOCOPY mal_table,\n                     H IN OUT NOCOPY map_entry_table,\n                     a integer, b integer) RETURN boolean;\n\n    FUNCTION clone(M IN OUT NOCOPY mal_table,\n                   H IN OUT NOCOPY map_entry_table,\n                   obj integer,\n                   meta integer DEFAULT 1) RETURN integer;\n\n    -- scalar functions\n    FUNCTION int(M IN OUT NOCOPY mal_table, num integer) RETURN integer;\n    FUNCTION string(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;\n    FUNCTION string_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;\n    FUNCTION symbol(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;\n    FUNCTION keyword(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;\n    FUNCTION keyword_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;\n    FUNCTION number_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;\n    FUNCTION function_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;\n    FUNCTION macro_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean;\n\n    -- sequence functions\n    FUNCTION seq(M IN OUT NOCOPY mal_table,\n                 type_id integer,\n                 items mal_vals,\n                 meta integer DEFAULT 1) RETURN integer;\n    FUNCTION list(M IN OUT NOCOPY mal_table) RETURN integer;\n    FUNCTION list(M IN OUT NOCOPY mal_table,\n                  a integer) RETURN integer;\n    FUNCTION list(M IN OUT NOCOPY mal_table,\n                  a integer, b integer) RETURN integer;\n    FUNCTION list(M IN OUT NOCOPY mal_table,\n                  a integer, b integer, c integer) RETURN integer;\n\n    FUNCTION first(M IN OUT NOCOPY mal_table,\n                   seq integer) RETURN integer;\n    FUNCTION slice(M IN OUT NOCOPY mal_table,\n                   seq integer,\n                   idx integer,\n                   last integer DEFAULT NULL) RETURN integer;\n    FUNCTION slice(M IN OUT NOCOPY mal_table,\n                   items mal_vals,\n                   idx integer) RETURN integer;\n    FUNCTION islice(items mal_vals,\n                    idx integer) RETURN mal_vals;\n    FUNCTION nth(M IN OUT NOCOPY mal_table,\n                 seq integer, idx integer) RETURN integer;\n\n    FUNCTION count(M IN OUT NOCOPY mal_table,\n                   seq integer) RETURN integer;\n\n    FUNCTION atom_new(M IN OUT NOCOPY mal_table,\n                      val integer) RETURN integer;\n    FUNCTION atom_reset(M IN OUT NOCOPY mal_table,\n                        atm integer,\n                        val integer) RETURN integer;\n\n    -- hash-map functions\n    FUNCTION assoc_BANG(M IN OUT NOCOPY mal_table,\n                        H IN OUT NOCOPY map_entry_table,\n                        midx integer,\n                        kvs mal_vals) RETURN integer;\n    FUNCTION dissoc_BANG(M IN OUT NOCOPY mal_table,\n                         H IN OUT NOCOPY map_entry_table,\n                         midx integer,\n                         ks mal_vals) RETURN integer;\n    FUNCTION hash_map(M IN OUT NOCOPY mal_table,\n                      H IN OUT NOCOPY map_entry_table,\n                      kvs mal_vals,\n                      meta integer DEFAULT 1) RETURN integer;\n\n    -- function functions\n    FUNCTION func(M IN OUT NOCOPY mal_table, name varchar) RETURN integer;\n    FUNCTION malfunc(M IN OUT NOCOPY mal_table,\n                     ast       integer,\n                     params    integer,\n                     env       integer,\n                     is_macro  integer DEFAULT 0,\n                     meta      integer DEFAULT 1) RETURN integer;\nEND types;\n/\nshow errors;\n\n\nCREATE OR REPLACE PACKAGE BODY types IS\n\n-- ---------------------------------------------------------\n-- general functions\n\nFUNCTION mem_new RETURN mal_table IS\nBEGIN\n    -- initialize mal type memory pool\n    -- 1 -> nil\n    -- 2 -> false\n    -- 3 -> true\n    RETURN mal_table(mal_T(0), mal_T(1), mal_T(2));\nEND;\n\nFUNCTION tf(val boolean) RETURN integer IS\nBEGIN\n    IF val THEN\n        RETURN 3; -- true\n    ELSE\n        RETURN 2; -- false\n    END IF;\nEND;\n\nFUNCTION equal_Q(M IN OUT NOCOPY mal_table,\n                 H IN OUT NOCOPY map_entry_table,\n                 a integer, b integer) RETURN boolean IS\n    atyp   integer;\n    btyp   integer;\n    aseq   mal_vals;\n    bseq   mal_vals;\n    amidx  integer;\n    bmidx  integer;\n    i      integer;\n    k      varchar2(256);\nBEGIN\n    atyp := M(a).type_id;\n    btyp := M(b).type_id;\n    IF NOT (atyp = btyp OR (atyp IN (8,9) AND btyp IN (8,9))) THEN\n        RETURN FALSE;\n    END IF;\n\n    CASE\n    WHEN atyp IN (0,1,2) THEN\n        RETURN TRUE;\n    WHEN atyp = 3 THEN\n        RETURN TREAT(M(a) AS mal_int_T).val_int =\n               TREAT(M(b) AS mal_int_T).val_int;\n    WHEN atyp IN (5,6,7) THEN\n        IF TREAT(M(a) AS mal_str_T).val_str IS NULL AND\n           TREAT(M(b) AS mal_str_T).val_str IS NULL THEN\n            RETURN TRUE;\n        ELSE\n            RETURN TREAT(M(a) AS mal_str_T).val_str =\n                   TREAT(M(b) AS mal_str_T).val_str;\n        END IF;\n    WHEN atyp IN (8,9) THEN\n        aseq := TREAT(M(a) AS mal_seq_T).val_seq;\n        bseq := TREAT(M(b) AS mal_seq_T).val_seq;\n        IF aseq.COUNT <> bseq.COUNT THEN\n            RETURN FALSE;\n        END IF;\n        FOR i IN 1..aseq.COUNT LOOP\n            IF NOT equal_Q(M, H, aseq(i), bseq(i)) THEN\n                RETURN FALSE;\n            END IF;\n        END LOOP;\n        RETURN TRUE;\n    WHEN atyp = 10 THEN\n        amidx := TREAT(M(a) AS mal_map_T).map_idx;\n        bmidx := TREAT(M(b) AS mal_map_T).map_idx;\n        IF H(amidx).COUNT() <> H(bmidx).COUNT() THEN\n            RETURN FALSE;\n        END IF;\n\n        k := H(amidx).FIRST();\n        WHILE k IS NOT NULL LOOP\n            IF H(amidx)(k) IS NULL OR H(bmidx)(k) IS NULL THEN\n                RETURN FALSE;\n            END IF;\n            IF NOT equal_Q(M, H, H(amidx)(k), H(bmidx)(k)) THEN\n                RETURN FALSE;\n            END IF;\n            k := H(amidx).NEXT(k);\n        END LOOP;\n        RETURN TRUE;\n    ELSE\n        RETURN FALSE;\n    END CASE;\nEND;\n\nFUNCTION clone(M IN OUT NOCOPY mal_table,\n               H IN OUT NOCOPY map_entry_table,\n               obj integer,\n               meta integer DEFAULT 1) RETURN integer IS\n    type_id  integer;\n    new_hm    integer;\n    old_midx  integer;\n    new_midx  integer;\n    k         varchar2(256);\n    malfn     mal_func_T;\nBEGIN\n    type_id := M(obj).type_id;\n    CASE\n    WHEN type_id IN (8,9) THEN  -- list/vector\n        RETURN seq(M, type_id,\n                      TREAT(M(obj) AS mal_seq_T).val_seq,\n                      meta);\n    WHEN type_id = 10 THEN      -- hash-map\n        new_hm := types.hash_map(M, H, mal_vals(), meta);\n        old_midx := TREAT(M(obj) AS mal_map_T).map_idx;\n        new_midx := TREAT(M(new_hm) AS mal_map_T).map_idx;\n\n        k := H(old_midx).FIRST();\n        WHILE k IS NOT NULL LOOP\n            H(new_midx)(k) := H(old_midx)(k);\n            k := H(old_midx).NEXT(k);\n        END LOOP;\n\n        RETURN new_hm;\n    WHEN type_id = 12 THEN      -- mal function\n        malfn := TREAT(M(obj) AS mal_func_T);\n        RETURN types.malfunc(M,\n            malfn.ast,\n            malfn.params,\n            malfn.env,\n            malfn.is_macro,\n            meta);\n    ELSE\n        raise_application_error(-20008,\n            'clone not supported for type ' || type_id, TRUE);\n    END CASE;\nEND;\n\n\n-- ---------------------------------------------------------\n-- scalar functions\n\n\nFUNCTION int(M IN OUT NOCOPY mal_table, num integer) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_int_T(3, num);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION string(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    IF LENGTH(name) <= 4000 THEN\n        M(M.COUNT()) := mal_str_T(5, name);\n    ELSE\n        M(M.COUNT()) := mal_long_str_T(6, NULL, name);\n    END IF;\n    RETURN M.COUNT();\nEND;\n\nFUNCTION string_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS\n    str  CLOB;\nBEGIN\n    IF M(val).type_id IN (5,6) THEN\n        IF M(val).type_id = 5 THEN\n            str := TREAT(M(val) AS mal_str_T).val_str;\n        ELSE\n            str := TREAT(M(val) AS mal_long_str_T).val_long_str;\n        END IF;\n        IF str IS NULL OR\n           str = EMPTY_CLOB() OR\n           SUBSTR(str, 1, 1) <> chr(127) THEN\n            RETURN TRUE;\n        ELSE\n            RETURN FALSE;\n        END IF;\n    ELSE\n        RETURN FALSE;\n    END IF;\nEND;\n\nFUNCTION symbol(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_str_T(7, name);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION keyword(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_str_T(5, chr(127) || name);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION keyword_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS\n    str  CLOB;\nBEGIN\n    IF M(val).type_id = 5 THEN\n        str := TREAT(M(val) AS mal_str_T).val_str;\n        IF LENGTH(str) > 0 AND SUBSTR(str, 1, 1) = chr(127) THEN\n            RETURN TRUE;\n        ELSE\n            RETURN FALSE;\n        END IF;\n    ELSE\n        RETURN FALSE;\n    END IF;\nEND;\n\nFUNCTION number_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS\n    str  CLOB;\nBEGIN\n    IF M(val).type_id IN (3,4) THEN\n        RETURN TRUE;\n    ELSE\n        RETURN FALSE;\n    END IF;\nEND;\n\nFUNCTION function_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS\n    str  CLOB;\nBEGIN\n    IF M(val).type_id = 11 THEN\n        RETURN TRUE;\n    ELSIF M(val).type_id = 12 THEN\n        RETURN TREAT(M(val) AS mal_func_T).is_macro = 0;\n    ELSE\n        RETURN FALSE;\n    END IF;\nEND;\n\nFUNCTION macro_Q(M IN OUT NOCOPY mal_table, val integer) RETURN boolean IS\n    str  CLOB;\nBEGIN\n    IF M(val).type_id = 12 THEN\n        RETURN TREAT(M(val) AS mal_func_T).is_macro > 0;\n    ELSE\n        RETURN FALSE;\n    END IF;\nEND;\n\n\n-- ---------------------------------------------------------\n-- sequence functions\n\nFUNCTION seq(M IN OUT NOCOPY mal_table,\n             type_id integer,\n             items mal_vals,\n             meta integer DEFAULT 1) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(type_id, items, meta);\n    RETURN M.COUNT();\nEND;\n\n-- list:\n-- return a mal list\nFUNCTION list(M IN OUT NOCOPY mal_table) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(8, mal_vals(), 1);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION list(M IN OUT NOCOPY mal_table,\n              a integer) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(8, mal_vals(a), 1);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION list(M IN OUT NOCOPY mal_table,\n              a integer, b integer) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(8, mal_vals(a, b), 1);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION list(M IN OUT NOCOPY mal_table,\n              a integer, b integer, c integer) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(8, mal_vals(a, b, c), 1);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION first(M IN OUT NOCOPY mal_table,\n               seq integer) RETURN integer IS\nBEGIN\n    RETURN TREAT(M(seq) AS mal_seq_T).val_seq(1);\nEND;\n\nFUNCTION slice(M IN OUT NOCOPY mal_table,\n               seq integer,\n               idx integer,\n               last integer DEFAULT NULL) RETURN integer IS\n    old_items  mal_vals;\n    new_items  mal_vals;\n    i          integer;\n    final_idx  integer;\nBEGIN\n    old_items := TREAT(M(seq) AS mal_seq_T).val_seq;\n    new_items := mal_vals();\n    IF last IS NULL THEN\n        final_idx := old_items.COUNT();\n    ELSE\n        final_idx := last + 1;\n    END IF;\n    IF final_idx > idx THEN\n        new_items.EXTEND(final_idx - idx);\n        FOR i IN idx+1..final_idx LOOP\n            new_items(i-idx) := old_items(i);\n        END LOOP;\n    END IF;\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(8, new_items, 1);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION slice(M IN OUT NOCOPY mal_table,\n               items mal_vals,\n               idx integer) RETURN integer IS\n    new_items  mal_vals;\nBEGIN\n    new_items := islice(items, idx);\n    M.EXTEND();\n    M(M.COUNT()) := mal_seq_T(8, new_items, 1);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION islice(items mal_vals,\n                idx integer) RETURN mal_vals IS\n    new_items  mal_vals;\n    i          integer;\nBEGIN\n    new_items := mal_vals();\n    IF items.COUNT > idx THEN\n        new_items.EXTEND(items.COUNT - idx);\n        FOR i IN idx+1..items.COUNT LOOP\n            new_items(i-idx) := items(i);\n        END LOOP;\n    END IF;\n    RETURN new_items;\nEND;\n\n\nFUNCTION nth(M IN OUT NOCOPY mal_table,\n             seq integer, idx integer) RETURN integer IS\nBEGIN\n    RETURN TREAT(M(seq) AS mal_seq_T).val_seq(idx+1);\nEND;\n\nFUNCTION count(M IN OUT NOCOPY mal_table,\n               seq integer) RETURN integer IS\nBEGIN\n    RETURN TREAT(M(seq) AS mal_seq_T).val_seq.COUNT;\nEND;\n\n-- ---------------------------------------------------------\n-- hash-map functions\n\nFUNCTION assoc_BANG(M IN OUT NOCOPY mal_table,\n                    H IN OUT NOCOPY map_entry_table,\n                    midx integer,\n                    kvs mal_vals) RETURN integer IS\n    i     integer;\nBEGIN\n    IF MOD(kvs.COUNT(), 2) = 1 THEN\n        raise_application_error(-20007,\n            'odd number of arguments to assoc', TRUE);\n    END IF;\n\n    i := 1;\n    WHILE i <= kvs.COUNT() LOOP\n        H(midx)(TREAT(M(kvs(i)) AS mal_str_T).val_str) := kvs(i+1);\n        i := i + 2;\n    END LOOP;\n    RETURN midx;\nEND;\n\nFUNCTION dissoc_BANG(M IN OUT NOCOPY mal_table,\n                     H IN OUT NOCOPY map_entry_table,\n                     midx integer,\n                     ks mal_vals) RETURN integer IS\n    i     integer;\nBEGIN\n    FOR i IN 1..ks.COUNT() LOOP\n        H(midx).DELETE(TREAT(M(ks(i)) AS mal_str_T).val_str);\n    END LOOP;\n    RETURN midx;\nEND;\n\nFUNCTION hash_map(M IN OUT NOCOPY mal_table,\n                  H IN OUT NOCOPY map_entry_table,\n                  kvs mal_vals,\n                  meta integer DEFAULT 1) RETURN integer IS\n    midx  integer;\nBEGIN\n    H.EXTEND();\n    midx := H.COUNT();\n    midx := assoc_BANG(M, H, midx, kvs);\n\n    M.EXTEND();\n    M(M.COUNT()) := mal_map_T(10, midx, meta);\n    RETURN M.COUNT();\nEND;\n\n\n-- ---------------------------------------------------------\n-- function functions\n\nFUNCTION func(M IN OUT NOCOPY mal_table, name varchar) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_str_T(11, name);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION malfunc(M IN OUT NOCOPY mal_table,\n                 ast       integer,\n                 params    integer,\n                 env       integer,\n                 is_macro  integer DEFAULT 0,\n                 meta      integer DEFAULT 1) RETURN integer IS\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_func_T(12, ast, params, env, is_macro, meta);\n    RETURN M.COUNT();\nEND;\n\n\n-- ---------------------------------------------------------\n-- atom functions\n\nFUNCTION atom_new(M IN OUT NOCOPY mal_table,\n                  val integer) RETURN integer IS\n    aidx  integer;\nBEGIN\n    M.EXTEND();\n    M(M.COUNT()) := mal_atom_T(13, val);\n    RETURN M.COUNT();\nEND;\n\nFUNCTION atom_reset(M IN OUT NOCOPY mal_table,\n                    atm integer,\n                    val integer) RETURN integer IS\nBEGIN\n    M(atm) := mal_atom_T(13, val);\n    RETURN val;\nEND;\n\n\n\nEND types;\n/\nshow errors;\n"
  },
  {
    "path": "impls/plsql/wrap.sh",
    "content": "#!/usr/bin/env bash\n\nRL_HISTORY_FILE=${HOME}/.mal-history\nSKIP_INIT=\"${SKIP_INIT:-}\"\n\nORACLE_LOGON=${ORACLE_LOGON:-system/oracle}\nSQLPLUS=\"sqlplus -S ${ORACLE_LOGON}\"\n\nFILE_PID=\ncleanup() {\n    trap - TERM QUIT INT EXIT\n    #echo cleanup: ${FILE_PID}\n    [ \"${FILE_PID}\" ] && kill ${FILE_PID}\n}\ntrap \"cleanup\" TERM QUIT INT EXIT\n\n\n# Load the SQL code\nif [ -z \"${SKIP_INIT}\" ]; then\n    out=$(echo \"\" | ${SQLPLUS} @$1)\n    if echo \"${out}\" | grep -vs \"^No errors.$\" \\\n        | grep -si error >/dev/null; then\n    #if echo \"${out}\"  | grep -si error >/dev/null; then\n        echo \"${out}\"\n        exit 1\n    fi\nfi\n\n# open I/O streams\necho -e \"BEGIN io.open(0); io.open(1); END;\\n/\" \\\n    | ${SQLPLUS} >/dev/null\n\n# Stream from table to stdout\n(\nwhile true; do\n    out=\"$(echo \"SELECT io.read(1) FROM dual;\" \\\n        | ${SQLPLUS} 2>/dev/null)\" || break\n    #echo \"out: [${out}] (${#out})\"\n    echo \"${out}\"\ndone\n) &\nSTDOUT_PID=$!\n\n# Perform readline input into stream table when requested\n(\n[ -r ${RL_HISTORY_FILE} ] && history -r ${RL_HISTORY_FILE}\nwhile true; do\n    prompt=$(echo \"SELECT io.wait_rl_prompt(0) FROM dual;\" \\\n        | ${SQLPLUS} 2>/dev/null) || break\n    # Prompt is returned single-quoted because sqlplus trims trailing\n    # whitespace. Remove the single quotes from the beginning and end:\n    prompt=${prompt%\\'}\n    prompt=${prompt#\\'}\n    #echo \"prompt: [${prompt}]\"\n\n    IFS= read -u 0 -r -e -p \"${prompt}\" line || break\n    if [ \"${line}\" ]; then\n        history -s -- \"${line}\"        # add to history\n        history -a ${RL_HISTORY_FILE}  # save history to file\n    fi\n\n    # Escape (double) single quotes per SQL norm\n    line=${line//\\'/\\'\\'}\n    #echo \"line: [${line}]\"\n    ( echo -n \"BEGIN io.writeline('${line}', 0); END;\";\n      echo -en \"\\n/\" ) \\\n        | ${SQLPLUS} >/dev/null || break\ndone\necho -e \"BEGIN io.close(0); END;\\n/\" \\\n    | ${SQLPLUS} > /dev/null\n) <&0 >&1 &\n\n\n# File read if requested\n(\nwhile true; do\n    files=\"$(echo \"SELECT path FROM file_io WHERE in_or_out = 'in';\" \\\n        | ${SQLPLUS} 2>/dev/null \\\n        | grep -v \"^no rows selected\")\" || break\n    for f in ${files}; do\n        if [ ! -r ${f} ]; then\n            echo \"UPDATE file_io SET error = 'Cannot read ''${f}''' WHERE path = '${f}' AND in_or_out = 'in';\" \\\n                | ${SQLPLUS} >/dev/null\n            continue;\n        fi\n        IFS= read -rd '' content < \"${f}\"\n        # sqlplus limits lines to 2499 characters so split the update\n        # into chunks of the file ORed together over multiple lines\n        query=\"UPDATE file_io SET data = TO_CLOB('')\"\n        while [ -n \"${content}\" ]; do\n            chunk=\"${content:0:2000}\"\n            content=\"${content:${#chunk}}\"\n            chunk=\"${chunk//\\'/\\'\\'}\"\n            chunk=\"${chunk//$'\\n'/\\\\n}\"\n            query=\"${query}\"$'\\n'\"    || TO_CLOB('${chunk}')\"\n        done\n        query=\"${query}\"$'\\n'\" WHERE path = '${f}' AND in_or_out = 'in';\"\n        echo \"${query}\" | ${SQLPLUS} > /dev/null\n        #echo \"file read: ${f}: ${?}\"\n    done\n    sleep 1\ndone\n) &\nFILE_PID=$!\n\nres=0\nshift\nif [ $# -gt 0 ]; then\n    # If there are command line arguments then run a command and exit\n    args=$(for a in \"$@\"; do echo -n \"\\\"$a\\\" \"; done)\n    echo -e \"SELECT mal.MAIN('(${args})') FROM dual;\" \\\n        | ${SQLPLUS} > /dev/null\n    res=$?\nelse\n    # Start main loop in the background\n    echo \"SELECT mal.MAIN() FROM dual;\" \\\n        | ${SQLPLUS} > /dev/null\n    res=$?\nfi\n# Wait for output to flush\nwait ${STDOUT_PID}\nexit ${res}\n"
  },
  {
    "path": "impls/powershell/Dockerfile",
    "content": "FROM ubuntu:16.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Nothing additional needed for python\nRUN apt-get -y install libunwind8 libicu55\n\n# For dist packaging\nRUN curl -L -O https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.9/powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb && \\\n    dpkg -i powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb && \\\n    rm powershell_6.0.0-alpha.9-1ubuntu1.16.04.1_amd64.deb\n\nENV HOME=/mal\n"
  },
  {
    "path": "impls/powershell/Makefile",
    "content": "all:\n\ttrue\n\nclean:\n"
  },
  {
    "path": "impls/powershell/core.psm1",
    "content": "function time_ms {\n    $ms = [double]::Parse((Get-Date (get-date).ToUniversalTime() -UFormat %s))\n    [int64] ($ms * 1000)\n}\n\nfunction get($hm, $key) {\n    if ($hm -eq $null) {\n        $null\n    } else {\n        $hm.values.Item($key)\n    }\n}\n\nfunction concat {\n    $res = @()\n    foreach($a in $args) {\n        $res = $res + $a.values\n    }\n    new-list $res\n}\n\nfunction vec($seq) {\n    if(vector? $seq) {\n        return $seq\n    } else {\n        return new-vector($seq.values)\n    }\n}\n\nfunction nth($lst, $idx) {\n    if ($idx -ge $lst.values.Count) {\n        throw \"nth: index out of range\"\n    }\n    $lst.nth($idx)\n}\n\n\nfunction do_map($f, $l) {\n    if (malfunc?($f)) {\n        $f = $f.fn\n    }\n    new-list @($l.values | ForEach-Object { &$f $_ })\n}\n\nfunction do_apply($f) {\n    if (malfunc?($f)) {\n        $f = $f.fn\n    }\n    if ($args.Count -gt 1) {\n        $fargs = $args[0..($args.Count-2)] + $args[-1].values\n    } else {\n        $fargs = $args[$args.Count-1].values\n    }\n    &$f @fargs\n}\n\nfunction conj($lst) {\n    if (list? $lst) {\n        [Array]::Reverse($args)\n        return new-list ($args + $lst.values)\n    } else {\n        return new-vector ($lst.values + $args)\n    }\n}\n\nfunction seq($obj) {\n    if ($obj -eq $null) {\n        return $null\n    } elseif (list? $obj) {\n        if ($obj.values.Count -gt 0) {\n            return $obj\n        } else {\n            return $null\n        }\n    } elseif (vector? $obj) {\n        if ($obj.values.Count -gt 0) {\n            return new-list $obj.values\n        } else {\n            return $null\n        }\n    } elseif (string? $obj) {\n        if ($obj.Length -gt 0) {\n            return new-list ($obj -split '')[1..$obj.Length]\n        } else {\n            return $null\n        }\n        return new-list $obj\n    } else {\n        throw \"seq: called on non-sequence\"\n    }\n}\n\nfunction swap_BANG($a, $f) {\n    if (malfunc?($f)) {\n        $f = $f.fn\n    }\n    $fargs = @($a.value) + $args\n    if ($fargs.Count -eq 0) {\n        $a.value = &$f\n    } else {\n        $a.value = &$f @fargs\n    }\n    $a.value\n}\n\n\n$core_ns = @{\n    \"=\"           = { param($a, $b); equal? $a $b };\n    \"throw\"       = Get-Command mal_throw;\n\n    \"nil?\"        = { param($a); $a -eq $null };\n    \"true?\"       = { param($a); $a -is [Boolean] -and $a -eq $true };\n    \"false?\"      = { param($a); $a -is [Boolean] -and $a -eq $false };\n    \"number?\"     = { param($a); $a -is [int32] };\n    \"string?\"     = { param($a); string? $a };\n    \"symbol\"      = Get-Command new-symbol;\n    \"symbol?\"     = { param($a); symbol? $a };\n    \"keyword\"     = Get-Command new-keyword;\n    \"keyword?\"    = { param($a); keyword? $a };\n    \"fn?\"         = { param($a); (fn? $a) -or ((malfunc? $a) -and\n                                               (-not $a.macro)) };\n    \"macro?\"      = { param($a); (malfunc? $a) -and $a.macro };\n\n    \"pr-str\"      = { pr_seq $args $true  \" \" };\n    \"str\"         = { pr_seq $args $false \"\" };\n    \"prn\"         = { Write-Host (pr_seq $args $true  \" \"); $null };\n    \"println\"     = { Write-Host (pr_seq $args $false \" \"); $null };\n    \"read-string\" = { read_str $args[0] };\n    \"readline\"    = { Write-Host $args[0] -NoNewline; [Console]::Readline() };\n    \"slurp\"       = { Get-Content -Path $args[0] -Raw };\n\n    \"<\"       = { param($a, $b); $a -lt $b };\n    \"<=\"      = { param($a, $b); $a -le $b };\n    \">\"       = { param($a, $b); $a -gt $b };\n    \">=\"      = { param($a, $b); $a -ge $b };\n    \"+\"       = { param($a, $b); $a + $b };\n    \"-\"       = { param($a, $b); $a - $b };\n    \"*\"       = { param($a, $b); $a * $b };\n    \"/\"       = { param($a, $b); $a / $b };\n    \"time-ms\" = Get-Command time_ms;\n\n    \"list\"    = { new-list $args };\n    \"list?\"   = Get-Command list?;\n    \"vector\"  = { new-vector $args };\n    \"vector?\" = Get-Command vector?;\n    \"hash-map\" = { new-hashmap $args };\n    \"map?\"    = Get-Command hashmap?;\n    \"assoc\"   = { param($a); assoc_BANG $a.copy() $args };\n    \"dissoc\"  = { param($a); dissoc_BANG $a.copy() $args };\n    \"get\"     = { param($a,$b); get $a $b };\n    \"contains?\" = { param($a,$b); $a.values.Contains($b) };\n    \"keys\"    = Get-Command keys;\n    \"vals\"    = Get-Command vals;\n\n    \"sequential?\" = Get-Command sequential?;\n    \"cons\"    = { param($a, $b); new-list (@($a) + $b.values) };\n    \"concat\"  = Get-Command concat;\n    \"vec\"     = Get-Command vec;\n    \"nth\"     = Get-Command nth;\n    \"first\"   = { param($a); if ($a -eq $null) { $null } else { $a.first() } };\n    \"rest\"    = { param($a); if ($a -eq $null) { new-list @() } else { $a.rest() } };\n    \"empty?\"  = { param($a); $a -eq $null -or $a.values.Count -eq 0 };\n    \"count\"   = { param($a); $a.values.Count };\n    \"apply\"   = Get-Command do_apply;\n    \"map\"     = Get-Command do_map;\n\n    \"conj\"    = Get-Command conj;\n    \"seq\"     = Get-Command seq;\n\n    \"meta\"    = { param($a); $a.meta };\n    \"with-meta\" = { param($a, $b); $c = $a.copy(); $c.meta = $b; $c };\n    \"atom\"    = { param($a); new-atom($a) };\n    \"atom?\"   = { param($a); atom?($a) };\n    \"deref\"   = { param($a); $a.value };\n    \"reset!\"  = { param($a, $b); $a.value = $b; $b };\n    \"swap!\"   = Get-Command swap_BANG;\n}\n\nExport-ModuleMember -Variable core_ns\n"
  },
  {
    "path": "impls/powershell/env.psm1",
    "content": "Import-Module $PSScriptRoot/types.psm1\n\nClass Env {\n    [HashTable] $data\n    [Env]       $outer\n\n    Env([Env] $out, $binds, $exprs) {\n        # Case-sensitive hash table\n        $this.data  = New-Object System.Collections.HashTable\n        $this.outer = $out\n\n        if ($binds -ne $null) {\n            for ($i = 0; $i -lt $binds.Length; $i++) {\n                if ($binds[$i].value -eq \"&\") {\n                    if ($exprs.Length -gt $i) {\n                        $rest = $exprs[$i..($exprs.Length-1)]\n                    } else {\n                        $rest = @()\n                    }\n                    $this.data[$binds[($i+1)].value] = new-list($rest)\n                    break\n                } else {\n                    $this.data[$binds[$i].value] = $exprs[$i]\n                }\n            }\n        }\n    }\n\n    [Object] set($key, $value) {\n        $this.data[$key] = $value\n        return $value\n    }\n\n    [Env] find($key) {\n        if ($this.data.Contains($key)) {\n            return $this\n        } elseif ($this.outer -ne $null) {\n            return $this.outer.find($key)\n        } else {\n            return $null\n        }\n    }\n\n    [Object] get($key) {\n        $e = $this.find($key)\n        if ($e -ne $null) {\n            return $e.data[$key]\n        } else {\n            throw \"'$($key)' not found\"\n        }\n    }\n}\n\nfunction new-env([Env] $out, $binds, $exprs) {\n    [Env]::new($out, $binds, $exprs)\n}\n\n"
  },
  {
    "path": "impls/powershell/printer.psm1",
    "content": "\nfunction pr_str {\n    param($obj, $print_readably = $true)\n    if ($obj -eq $null) {\n        return \"nil\"\n    }\n\n    switch ($obj.GetType().Name) {\n        \"String\" {\n            if ($obj[0] -eq \"$([char]0x29e)\") {\n                return \":$($obj.substring(1))\"\n            } elseif ($print_readably) {\n                $s = $obj -replace \"\\\\\", \"\\\\\"\n                $s = $s -replace \"`\"\", \"\\`\"\"\n                $s = $s -replace \"`n\", \"\\n\"\n                return \"`\"$s`\"\"\n            } else {\n                return \"$obj\"\n            }\n        }\n        \"Vector\" {\n            $res = @($obj.values | ForEach-Object { (pr_str $_ $print_readably) })\n            return \"[\" + ($res -join \" \") + \"]\"\n        }\n        \"List\" {\n            $res = @($obj.values | ForEach-Object { (pr_str $_ $print_readably) })\n            return \"(\" + ($res -join \" \") + \")\"\n        }\n        \"HashMap\" {\n            $res = @()\n            foreach ($k in $obj.values.Keys) {\n                $res += pr_str $k $print_readably\n                $res += pr_str $obj.values[$k] $print_readably\n            }\n            return \"{\" + ($res -join \" \") + \"}\"\n        }\n        \"Symbol\" {\n            return $obj.value\n        }\n        \"Boolean\" {\n            return $obj.ToString().ToLower()\n        }\n        \"Atom\" {\n            return \"(atom $(pr_str $obj.value $print_readably))\"\n        }\n        \"PSCustomObject\" {\n            return \"(fn* $(pr_str (new-list $obj.params) $print_readably) $(pr_str $obj.ast $print_readably))\"\n        }\n        default {\n            return $obj.ToString()\n        }\n    }\n}\n\nfunction pr_seq {\n    param($seq, $print_readably, $sep)\n    $lst = foreach($a in $seq) { pr_str $a $print_readably } \n    $lst -join $sep\n}\n"
  },
  {
    "path": "impls/powershell/reader.psm1",
    "content": "Import-Module $PSScriptRoot/types.psm1\n\nClass Reader {\n    [String[]] $tokens\n    [int]      $pos\n\n    Reader([String[]] $toks) {\n        $this.tokens = $toks\n        $this.pos = 0\n    }\n\n    [String] peek() {\n        return $this.tokens[$this.pos]\n    }\n\n    [String] next() {\n        return $this.tokens[$this.pos++]\n    }\n}\n\n\nfunction tokenize {\n    $r = [regex]\"[\\s,]*(~@|[\\[\\]{}()'``~^@]|`\"(?:\\\\.|[^\\\\`\"])*`\"?|;.*|[^\\s\\[\\]{}('`\"``,;)]*)\"\n    $r.Matches($args) | \n        Where-Object { $_.Groups.Item(1).Value.Length -gt 0 -and\n                       $_.Groups.Item(1).Value[0] -ne \";\" } |\n        Foreach-Object { $_.Groups.Item(1).Value }\n}\n\nfunction read_atom([Reader] $rdr) {\n    $token = $rdr.next()\n    if ($token -match \"^-?[0-9]+$\") {\n        return [convert]::ToInt32($token, 10)\n    } elseif ($token -match \"^`\"(?:\\\\.|[^\\\\`\"])*`\"$\") {\n        $s = $token.Substring(1,$token.Length-2)\n        $s = $s -replace \"\\\\\\\\\", \"$([char]0x29e)\"\n        $s = $s -replace \"\\\\`\"\", \"`\"\"\n        $s = $s -replace \"\\\\n\", \"`n\"\n        $s = $s -replace \"$([char]0x29e)\", \"\\\"\n        return $s\n    } elseif ($token -match \"^`\".*\") {\n        throw \"expected '`\"', got EOF\"\n    } elseif ($token -match \"^:.*\") {\n        return \"$([char]0x29e)$($token.substring(1))\"\n    } elseif ($token -eq \"true\") {\n        return $true\n    } elseif ($token -eq \"false\") {\n        return $false\n    } elseif ($token -eq \"nil\") {\n        return $null\n    } else {\n        return new-symbol($token)\n    }\n}\n\nfunction read_seq([Reader] $rdr, $start, $end) {\n    $seq = @()\n    $token = $rdr.next()\n    if ($token -ne $start) {\n        throw \"expected '$start'\"\n    }\n    while (($token = $rdr.peek()) -ne $end) {\n        if ($token -eq \"\") {\n            throw \"expected '$end', got EOF\"\n        }\n        $form = read_form $rdr\n        $seq += $form\n    }\n    $token = $rdr.next()\n    return ,$seq\n}\n\nfunction read_list([Reader] $rdr) {\n    return new-list (read_seq $rdr \"(\" \")\")\n}\n\nfunction read_vector([Reader] $rdr) {\n    return new-vector (read_seq $rdr \"[\" \"]\")\n}\n\nfunction read_hash_map([Reader] $rdr) {\n    return new-hashmap (read_seq $rdr \"{\" \"}\")\n}\n\nfunction read_form([Reader] $rdr) {\n    $token = $rdr.peek()\n    switch ($token) {\n        # reader macros/transforms\n        \"'\"     { $_ = $rdr.next();\n                  return new-list @((new-symbol \"quote\"),\n                                    (read_form $rdr)) }\n        \"``\"    { $_ = $rdr.next();\n                  return new-list @((new-symbol \"quasiquote\"),\n                                    (read_form $rdr)) }\n        \"~\"     { $_ = $rdr.next();\n                  return (new-list @((new-symbol \"unquote\"),\n                                     (read_form $rdr))) }\n        \"~@\"    { $_ = $rdr.next();\n                  return (new-list @((new-symbol \"splice-unquote\"),\n                                     (read_form $rdr))) }\n        \"^\"     { $_ = $rdr.next();\n                  $meta = read_form $rdr\n                  return (new-list @((new-symbol \"with-meta\"),\n                                     (read_form $rdr),\n                                     $meta)) }\n        \"@\"     { $_ = $rdr.next();\n                  return (new-list @((new-symbol \"deref\"),\n                                     (read_form $rdr))) }\n\n        # list\n        \")\"     { throw \"unexpected ')'\" }\n        \"(\"     { return read_list $rdr }\n\n        # vector\n        \"]\"     { throw \"unexpected ']'\" }\n        \"[\"     { return read_vector $rdr }\n\n        # hashmap\n        \"}\"     { throw \"unexpected '}'\" }\n        \"{\"     { return read_hash_map $rdr }\n\n        default { return read_atom $rdr  }\n    }\n}\n\nfunction read_str {\n    $toks = tokenize($args[0])\n    if ($toks.Length -eq 0) { return $null }\n    read_form([Reader]::new($toks))\n}\n"
  },
  {
    "path": "impls/powershell/run",
    "content": "#!/bin/sh\nexec powershell $(dirname $0)/${STEP:-stepA_mal}.ps1 \"${@}\"\n"
  },
  {
    "path": "impls/powershell/step0_repl.ps1",
    "content": "while ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    \"$line\"\n}\n"
  },
  {
    "path": "impls/powershell/step1_read_print.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction EVAL($ast, $env) {\n    return $ast\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) @{})\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step2_eval.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction EVAL($ast, $env) {\n    # Write-Host \"EVAL: $(pr_str $ast)\"\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env[$ast.value] }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $f = ( EVAL $ast.first() $env )\n    $fargs = @($ast.rest().values | ForEach-Object { EVAL $_ $env })\n    return &$f @fargs\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n# Case sensitive hashtable\n$repl_env = New-Object System.Collections.HashTable\n$repl_env[\"+\"] = { param($a, $b); $a + $b }\n$repl_env[\"-\"] = { param($a, $b); $a - $b }\n$repl_env[\"*\"] = { param($a, $b); $a * $b }\n$repl_env[\"/\"] = { param($a, $b); $a / $b }\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step3_env.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction EVAL($ast, $env) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            return EVAL $a2 $let_env\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values | ForEach-Object { EVAL $_ $env })\n            return &$f @fargs\n        }\n    }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n$_ = $repl_env.set(\"+\", { param($a, $b); $a + $b })\n$_ = $repl_env.set(\"-\", { param($a, $b); $a - $b })\n$_ = $repl_env.set(\"*\", { param($a, $b); $a * $b })\n$_ = $repl_env.set(\"/\", { param($a, $b); $a / $b })\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step4_if_fn_do.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction EVAL($ast, $env) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            return EVAL $a2 $let_env\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            return (EVAL $ast.values[$i] $env)\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                return (EVAL $ast.nth(3) $env)\n            } else {\n                return (EVAL $a2 $env)\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            return {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values | ForEach-Object { EVAL $_ $env })\n            return &$f @fargs\n        }\n    }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n\n# core.mal: defined using the language itself\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step5_tco.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction EVAL($ast, $env) {\n\n  while ($true) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            $env = $let_env\n            $ast = $a2  # TCO\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            $ast = $ast.values[$i]  # TCO\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                $ast = $ast.nth(3)  # TCO\n            } else {\n                $ast = $a2  # TCO\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            $fn = {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n            return new-malfunc $a2 $a1.values $env $fn\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values | ForEach-Object { EVAL $_ $env })\n            if (malfunc? $f) {\n                $env = (new-env $f.env $f.params $fargs)\n                $ast = $f.ast  # TCO\n            } else {\n                return &$f @fargs\n            }\n        }\n    }\n  }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n\n# core.mal: defined using the language itself\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step6_file.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction EVAL($ast, $env) {\n\n  while ($true) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            $env = $let_env\n            $ast = $a2  # TCO\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            $ast = $ast.values[$i]  # TCO\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                $ast = $ast.nth(3)  # TCO\n            } else {\n                $ast = $a2  # TCO\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            $fn = {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n            return new-malfunc $a2 $a1.values $env $fn\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values | ForEach-Object { EVAL $_ $env })\n            if (malfunc? $f) {\n                $env = (new-env $f.env $f.params $fargs)\n                $ast = $f.ast  # TCO\n            } else {\n                return &$f @fargs\n            }\n        }\n    }\n  }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n$_ = $repl_env.set(\"eval\", { param($a); (EVAL $a $repl_env) })\n$_ = $repl_env.set(\"*ARGV*\", (new-list $args[1..$args.Count]))\n\n# core.mal: defined using the language itself\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n$_ = REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\n\nif ($args.Count -gt 0) {\n    $_ = REP('(load-file \"' + $args[0] + '\")')\n    exit 0\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step7_quote.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction starts_with($lst, $sym) {\n    if ($lst.values.Count -ne 2) { return $false }\n    $a0 = $lst.nth(0)\n    return (symbol? $a0) -and ($a0.value -ceq $sym)\n}\nfunction qq_loop($elt, $acc) {\n    if ((list? $elt) -and (starts_with $elt \"splice-unquote\")) {\n        return (new-list @((new-symbol \"concat\"), $elt.nth(1), $acc))\n    } else {\n        return (new-list @((new-symbol \"cons\"), (quasiquote $elt), $acc))\n    }\n}\nfunction qq_foldr($xs) {\n    $acc = new-list @()\n    for ( $i = $xs.Count - 1; $i -ge 0; $i-- ) {\n        $acc = qq_loop $xs[$i] $acc\n    }\n    return $acc\n}\nfunction quasiquote($ast) {\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"    { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"HashMap\"   { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"Vector\"    { return (new-list @((new-symbol \"vec\"), (qq_foldr $ast.values))) }\n        \"List\"      {\n            if (starts_with $ast \"unquote\") {\n                return $ast.values[1]\n            } else {\n                return qq_foldr $ast.values\n            }\n        }\n        default     { return $ast }\n    }\n}\n\nfunction EVAL($ast, $env) {\n\n  while ($true) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            $env = $let_env\n            $ast = $a2  # TCO\n        }\n        \"quote\" {\n            return $a1\n        }\n        \"quasiquote\" {\n            $ast = quasiquote $a1\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            $ast = $ast.values[$i]  # TCO\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                $ast = $ast.nth(3)  # TCO\n            } else {\n                $ast = $a2  # TCO\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            $fn = {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n            return new-malfunc $a2 $a1.values $env $fn\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values | ForEach-Object { EVAL $_ $env })\n            if (malfunc? $f) {\n                $env = (new-env $f.env $f.params $fargs)\n                $ast = $f.ast  # TCO\n            } else {\n                return &$f @fargs\n            }\n        }\n    }\n  }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n$_ = $repl_env.set(\"eval\", { param($a); (EVAL $a $repl_env) })\n$_ = $repl_env.set(\"*ARGV*\", (new-list $args[1..$args.Count]))\n\n# core.mal: defined using the language itself\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n$_ = REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\n\nif ($args.Count -gt 0) {\n    $_ = REP('(load-file \"' + $args[0] + '\")')\n    exit 0\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step8_macros.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction starts_with($lst, $sym) {\n    if ($lst.values.Count -ne 2) { return $false }\n    $a0 = $lst.nth(0)\n    return (symbol? $a0) -and ($a0.value -ceq $sym)\n}\nfunction qq_loop($elt, $acc) {\n    if ((list? $elt) -and (starts_with $elt \"splice-unquote\")) {\n        return (new-list @((new-symbol \"concat\"), $elt.nth(1), $acc))\n    } else {\n        return (new-list @((new-symbol \"cons\"), (quasiquote $elt), $acc))\n    }\n}\nfunction qq_foldr($xs) {\n    $acc = new-list @()\n    for ( $i = $xs.Count - 1; $i -ge 0; $i-- ) {\n        $acc = qq_loop $xs[$i] $acc\n    }\n    return $acc\n}\nfunction quasiquote($ast) {\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"    { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"HashMap\"   { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"Vector\"    { return (new-list @((new-symbol \"vec\"), (qq_foldr $ast.values))) }\n        \"List\"      {\n            if (starts_with $ast \"unquote\") {\n                return $ast.values[1]\n            } else {\n                return qq_foldr $ast.values\n            }\n        }\n        default     { return $ast }\n    }\n}\n\nfunction EVAL($ast, $env) {\n\n  while ($true) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            $env = $let_env\n            $ast = $a2  # TCO\n        }\n        \"quote\" {\n            return $a1\n        }\n        \"quasiquote\" {\n            $ast = quasiquote $a1\n        }\n        \"defmacro!\" {\n            $m = EVAL $a2 $env\n            $m = $m.copy()\n            $m.macro = $true\n            return $env.set($a1.value, $m)\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            $ast = $ast.values[$i]  # TCO\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                $ast = $ast.nth(3)  # TCO\n            } else {\n                $ast = $a2  # TCO\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            $fn = {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n            return new-malfunc $a2 $a1.values $env $fn\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values)\n            if ($f.macro) {\n                $ast = &$f.fn @fargs  # TCO\n                continue\n            }\n            $fargs = @($fargs | ForEach-Object { EVAL $_ $env })\n            if (malfunc? $f) {\n                $env = (new-env $f.env $f.params $fargs)\n                $ast = $f.ast  # TCO\n            } else {\n                return &$f @fargs\n            }\n        }\n    }\n  }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n$_ = $repl_env.set(\"eval\", { param($a); (EVAL $a $repl_env) })\n$_ = $repl_env.set(\"*ARGV*\", (new-list $args[1..$args.Count]))\n\n# core.mal: defined using the language itself\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n$_ = REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n$_ = REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw `\"odd number of forms to cond`\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif ($args.Count -gt 0) {\n    $_ = REP('(load-file \"' + $args[0] + '\")')\n    exit 0\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        Write-Host \"Exception: $($_.Exception.Message)\"\n    }\n}\n"
  },
  {
    "path": "impls/powershell/step9_try.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction starts_with($lst, $sym) {\n    if ($lst.values.Count -ne 2) { return $false }\n    $a0 = $lst.nth(0)\n    return (symbol? $a0) -and ($a0.value -ceq $sym)\n}\nfunction qq_loop($elt, $acc) {\n    if ((list? $elt) -and (starts_with $elt \"splice-unquote\")) {\n        return (new-list @((new-symbol \"concat\"), $elt.nth(1), $acc))\n    } else {\n        return (new-list @((new-symbol \"cons\"), (quasiquote $elt), $acc))\n    }\n}\nfunction qq_foldr($xs) {\n    $acc = new-list @()\n    for ( $i = $xs.Count - 1; $i -ge 0; $i-- ) {\n        $acc = qq_loop $xs[$i] $acc\n    }\n    return $acc\n}\nfunction quasiquote($ast) {\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"    { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"HashMap\"   { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"Vector\"    { return (new-list @((new-symbol \"vec\"), (qq_foldr $ast.values))) }\n        \"List\"      {\n            if (starts_with $ast \"unquote\") {\n                return $ast.values[1]\n            } else {\n                return qq_foldr $ast.values\n            }\n        }\n        default     { return $ast }\n    }\n}\n\nfunction EVAL($ast, $env) {\n\n  while ($true) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            $env = $let_env\n            $ast = $a2  # TCO\n        }\n        \"quote\" {\n            return $a1\n        }\n        \"quasiquote\" {\n            $ast = quasiquote $a1\n        }\n        \"defmacro!\" {\n            $m = EVAL $a2 $env\n            $m = $m.copy()\n            $m.macro = $true\n            return $env.set($a1.value, $m)\n        }\n        \"try*\" {\n            try {\n                return EVAL $a1 $env\n            } catch {\n                if ($a2 -and ($a2.nth(0).value -ceq \"catch*\")) {\n                    if ($_.Exception.GetType().Name -eq \"MalException\") {\n                        $e = @($_.Exception.object)\n                    } else {\n                        $e = @($_.Exception.Message)\n                    }\n                    return (EVAL $a2.nth(2) (new-env $env @($a2.nth(1)) $e))\n                } else {\n                    throw\n                }\n            }\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            $ast = $ast.values[$i]  # TCO\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                $ast = $ast.nth(3)  # TCO\n            } else {\n                $ast = $a2  # TCO\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            $fn = {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n            return new-malfunc $a2 $a1.values $env $fn\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values)\n            if ($f.macro) {\n                $ast = &$f.fn @fargs  # TCO\n                continue\n            }\n            $fargs = @($fargs | ForEach-Object { EVAL $_ $env })\n            if (malfunc? $f) {\n                $env = (new-env $f.env $f.params $fargs)\n                $ast = $f.ast  # TCO\n            } else {\n                return &$f @fargs\n            }\n        }\n    }\n  }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n$_ = $repl_env.set(\"eval\", { param($a); (EVAL $a $repl_env) })\n$_ = $repl_env.set(\"*ARGV*\", (new-list $args[1..$args.Count]))\n\n# core.mal: defined using the language itself\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n$_ = REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n$_ = REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw `\"odd number of forms to cond`\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif ($args.Count -gt 0) {\n    $_ = REP('(load-file \"' + $args[0] + '\")')\n    exit 0\n}\n\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        if ($_.Exception.GetType().Name -eq \"MalException\") {\n            Write-Host \"Exception: $(pr_str $_.Exception.object)\"\n        } else {\n            Write-Host \"Exception: $($_.Exception.Message)\"\n        }\n    }\n}\n"
  },
  {
    "path": "impls/powershell/stepA_mal.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nImport-Module $PSScriptRoot/types.psm1\nImport-Module $PSScriptRoot/reader.psm1\nImport-Module $PSScriptRoot/printer.psm1\nImport-Module $PSScriptRoot/env.psm1\nImport-Module $PSScriptRoot/core.psm1\n\n# READ\nfunction READ([String] $str) {\n    return read_str($str)\n}\n\n# EVAL\nfunction starts_with($lst, $sym) {\n    if ($lst.values.Count -ne 2) { return $false }\n    $a0 = $lst.nth(0)\n    return (symbol? $a0) -and ($a0.value -ceq $sym)\n}\nfunction qq_loop($elt, $acc) {\n    if ((list? $elt) -and (starts_with $elt \"splice-unquote\")) {\n        return (new-list @((new-symbol \"concat\"), $elt.nth(1), $acc))\n    } else {\n        return (new-list @((new-symbol \"cons\"), (quasiquote $elt), $acc))\n    }\n}\nfunction qq_foldr($xs) {\n    $acc = new-list @()\n    for ( $i = $xs.Count - 1; $i -ge 0; $i-- ) {\n        $acc = qq_loop $xs[$i] $acc\n    }\n    return $acc\n}\nfunction quasiquote($ast) {\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"    { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"HashMap\"   { return (new-list @((new-symbol \"quote\"), $ast)) }\n        \"Vector\"    { return (new-list @((new-symbol \"vec\"), (qq_foldr $ast.values))) }\n        \"List\"      {\n            if (starts_with $ast \"unquote\") {\n                return $ast.values[1]\n            } else {\n                return qq_foldr $ast.values\n            }\n        }\n        default     { return $ast }\n    }\n}\n\nfunction EVAL($ast, $env) {\n\n  while ($true) {\n\n    $dbgeval_env = ($env.find(\"DEBUG-EVAL\"))\n    if ($dbgeval_env -ne $null) {\n        $dbgeval = $dbgeval_env.get(\"DEBUG-EVAL\")\n        if ($dbgeval -ne $null -and\n            -not ($dbgeval -is [Boolean] -and $dbgeval -eq $false)) {\n            Write-Host \"EVAL: $(pr_str $ast)\"\n        }\n    }\n\n    if ($ast -eq $null) { return $ast }\n    switch ($ast.GetType().Name) {\n        \"Symbol\"  { return $env.get($ast.value) }\n        \"List\" { }  # continue after the switch\n        \"Vector\"  { return new-vector @($ast.values | ForEach-Object { EVAL $_ $env }) }\n        \"HashMap\" {\n            $hm = new-hashmap @()\n            foreach ($k in $ast.values.Keys) {\n                $hm.values[$k] = EVAL $ast.values[$k] $env\n            }\n            return $hm\n        }\n        default   { return $ast }\n    }\n\n    if (empty? $ast) { return $ast }\n\n    $a0, $a1, $a2 = $ast.nth(0), $ast.nth(1), $ast.nth(2)\n    switch -casesensitive ($a0.value) {\n        \"def!\" {\n            return $env.set($a1.value, (EVAL $a2 $env))\n        }\n        \"let*\" {\n            $let_env = new-env $env\n            for ($i=0; $i -lt $a1.values.Count; $i+=2) {\n                $_ = $let_env.set($a1.nth($i).value, (EVAL $a1.nth(($i+1)) $let_env))\n            }\n            $env = $let_env\n            $ast = $a2  # TCO\n        }\n        \"quote\" {\n            return $a1\n        }\n        \"quasiquote\" {\n            $ast = quasiquote $a1\n        }\n        \"defmacro!\" {\n            $m = EVAL $a2 $env\n            $m = $m.copy()\n            $m.macro = $true\n            return $env.set($a1.value, $m)\n        }\n        \"try*\" {\n            try {\n                return EVAL $a1 $env\n            } catch {\n                if ($a2 -and ($a2.nth(0).value -ceq \"catch*\")) {\n                    if ($_.Exception.GetType().Name -eq \"MalException\") {\n                        $e = @($_.Exception.object)\n                    } else {\n                        $e = @($_.Exception.Message)\n                    }\n                    return (EVAL $a2.nth(2) (new-env $env @($a2.nth(1)) $e))\n                } else {\n                    throw\n                }\n            }\n        }\n        \"do\" {\n            for ($i=1; $i -lt ($ast.values.Count - 1); $i+=1) {\n                $_ = (EVAL $ast.values[$i] $env)\n            }\n            $ast = $ast.values[$i]  # TCO\n        }\n        \"if\" {\n            $cond = (EVAL $a1 $env)\n            if ($cond -eq $null -or\n                ($cond -is [Boolean] -and $cond -eq $false)) {\n                $ast = $ast.nth(3)  # TCO\n            } else {\n                $ast = $a2  # TCO\n            }\n        }\n        \"fn*\" {\n            # Save EVAL into a variable that will get closed over\n            $feval = Get-Command EVAL\n            $fn = {\n                return (&$feval $a2 (new-env $env $a1.values $args))\n            }.GetNewClosure()\n            return new-malfunc $a2 $a1.values $env $fn\n        }\n        default {\n            $f = ( EVAL $ast.first() $env )\n            $fargs = @($ast.rest().values)\n            if ($f.macro) {\n                $ast = &$f.fn @fargs  # TCO\n                continue\n            }\n            $fargs = @($fargs | ForEach-Object { EVAL $_ $env })\n            if (malfunc? $f) {\n                $env = (new-env $f.env $f.params $fargs)\n                $ast = $f.ast  # TCO\n            } else {\n                return &$f @fargs\n            }\n        }\n    }\n  }\n}\n\n# PRINT\nfunction PRINT($exp) {\n    return pr_str $exp $true\n}\n\n# REPL\n$repl_env = new-env\n\nfunction REP([String] $str) {\n    return PRINT (EVAL (READ $str) $repl_env)\n}\n\n# core.EXT: defined using PowerShell\nforeach ($kv in $core_ns.GetEnumerator()) {\n    $_ = $repl_env.set($kv.Key, $kv.Value)\n}\n$_ = $repl_env.set(\"eval\", { param($a); (EVAL $a $repl_env) })\n$_ = $repl_env.set(\"*ARGV*\", (new-list $args[1..$args.Count]))\n\n# core.mal: defined using the language itself\n$_ = REP('(def! *host-language* \"powershell\")')\n$_ = REP('(def! not (fn* (a) (if a false true)))')\n$_ = REP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n$_ = REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw `\"odd number of forms to cond`\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif ($args.Count -gt 0) {\n    $_ = REP('(load-file \"' + $args[0] + '\")')\n    exit 0\n}\n\n$_ = REP('(println (str \"Mal [\" *host-language* \"]\"))')\nwhile ($true) {\n    Write-Host \"user> \" -NoNewline\n    $line = [Console]::ReadLine()\n    if ($line -eq $null) {\n        break\n    }\n    try {\n        Write-Host (REP($line))\n    } catch {\n        if ($_.Exception.GetType().Name -eq \"MalException\") {\n            Write-Host \"Exception: $(pr_str $_.Exception.object)\"\n        } else {\n            Write-Host \"Exception: $($_.Exception.Message)\"\n        }\n    }\n}\n"
  },
  {
    "path": "impls/powershell/types.psm1",
    "content": "#\n# Exceptions\n#\nClass MalException : Exception {\n    [Object] $object\n\n    MalException($obj) {\n        $this.object = $obj\n    }\n}\n\nfunction mal_throw($obj) {\n    throw [MalException] $obj\n}\n\n#\n# Symbols\n#\n\nClass Symbol {\n    [String] $value\n\n    Symbol([String] $val) {\n        $this.value = $val\n    }\n\n    copy() { $this }\n}\n\nfunction new-symbol([String] $val) {\n    [Symbol]::new($val)\n}\n\nfunction symbol?($obj) {\n    $obj -is [Symbol]\n}\n\n#\n# Strings\n#\n\nfunction string?($obj) {\n    ($obj -is [String]) -and ($obj[0] -ne \"$([char]0x29e)\")\n}\n\n#\n# Keywords\n#\n\nfunction new-keyword($obj) {\n    if (keyword? $obj) {\n        $obj\n    } else {\n        \"$([char]0x29e)$obj\"\n    }\n}\n\nfunction keyword?($obj) {\n    ($obj -is [String]) -and ($obj[0] -eq \"$([char]0x29e)\")\n}\n\n\n#\n# Lists\n#\n\nClass List {\n    #[System.Collections.ArrayList] $values\n    [Object[]] $values\n    [Object]   $meta\n\n    List() {\n        $this.values = @()\n        #$this.values = New-Object System.Collections.ArrayList\n    }\n\n    List([Object[]] $vals) {\n    #List([System.Collections.ArrayList] $vals) {\n        $this.values = $vals\n    }\n\n    [List] copy() {\n        return [List]::new($this.values)\n    }\n\n    [void] push([Object] $val) {\n        $this.values.Add($val)\n    }\n\n    [Object] first() {\n        return $this.values[0]\n    }\n\n    [List] rest() {\n        if ($this.values.Count -le 1) {\n            return [List]::new(@())\n        } else {\n            return [List]::new($this.values[1..($this.values.Count)])\n        }\n    }\n\n    [Object] last() {\n        if ($this.values.Count -eq 0) {\n            return $null\n        } else {\n            return $this.values[$this.values.Count-1]\n        }\n    }\n\n    [Object] nth([int64] $idx) {\n        return $this.values[$idx]\n    }\n}\n\nfunction new-list([Object[]] $vals) {\n#function new-list([System.Collections.ArrayList] $vals) {\n    if ($vals.Count -eq 0) {\n        return [List]::new()\n    } else {\n        return [List]::new($vals)\n    }\n}\n\nfunction list?($obj) {\n    $obj -is [List] -and -not ($obj -is [Vector])\n}\n\n\n#\n# Vectors\n#\n\nClass Vector : List {\n    Vector() {\n        $this.values = @()\n        #$this.values = New-Object System.Collections.ArrayList\n    }\n\n    Vector([Object[]] $vals) {\n    #Vector([System.Collections.ArrayList] $vals) {\n        $this.values = $vals\n    }\n\n    [Vector] copy() {\n        return [Vector]::new($this.values)\n    }\n\n}\n\nfunction new-vector([Object[]] $vals) {\n    if ($vals.Count -eq 0) {\n        return [Vector]::new()\n    } else {\n        return [Vector]::new($vals)\n    }\n}\n\nfunction vector?($obj) {\n    $obj -is [Vector]\n}\n\n\n#\n# HashMaps\n#\n\nClass HashMap {\n    [Hashtable] $values\n    [Object]   $meta\n\n    HashMap() {\n        # Case-sensitive hashtable\n        $this.values = New-Object System.Collections.HashTable\n    }\n\n    HashMap([Hashtable] $vals) {\n        $this.values = $vals\n    }\n\n    [HashMap] copy() {\n        return [HashMap]::new($this.values.clone())\n    }\n\n}\n\nfunction assoc_BANG($hm, $kvs) {\n    $ht = $hm.values\n    for ($i = 0; $i -lt $kvs.Count; $i+=2) {\n        $ht[$kvs[$i]] = $kvs[($i+1)]\n    }\n    return $hm\n}\n\nfunction dissoc_BANG($hm, $ks) {\n    $ht = $hm.values\n    foreach ($k in $ks) {\n        $ht.Remove($k)\n    }\n    return $hm\n}\n\n\nfunction new-hashmap([Object[]] $vals) {\n    $hm = [HashMap]::new()\n    assoc_BANG $hm $vals\n}\n\nfunction hashmap?($obj) {\n    $obj -is [HashMap]\n}\n\nfunction keys($hm) {\n    return new-list @($hm.values.GetEnumerator() | ForEach-Object { $_.Key })\n}\n\nfunction vals($hm) {\n    return new-list @($hm.values.GetEnumerator() | ForEach-Object { $_.Value })\n}\n\n\n#\n# Atoms\n\nClass Atom {\n    [Object] $value\n\n    Atom([Object] $val) {\n        $this.value = $val\n    }\n}\n\nfunction new-atom([Object] $val) {\n    [Atom]::new($val)\n}\n\nfunction atom?($obj) {\n    $obj -is [Atom]\n}\n\n\n# Functions\n\nClass MalFunc {\n    [Object]      $ast\n    [Object[]]    $params\n    [Object]      $env\n    [scriptBlock] $fn\n    [Boolean]     $macro\n    [Object]      $meta\n\n    MalFunc($ast, $params, $env, $fn, $macro, $meta){\n        $this.ast    = $ast\n        $this.params = $params\n        $this.env    = $env\n        $this.fn     = $fn\n        $this.macro  = $macro\n        $this.meta   = $meta\n    }\n\n    [MalFunc] copy() {\n        return [MalFunc]::new($this.ast, $this.params, $this.env, $this.fn,\n                              $this.macro, $this.meta)\n    }\n\n}\n\nfunction new-malfunc($ast, $params, $env, $fn, $macro, $meta) {\n    [MalFunc]::new($ast, $params, $env, $fn, $macro, $meta)\n}\n\nfunction malfunc?($obj) {\n    $obj -is [MalFunc]\n}\n\nfunction fn?($obj) {\n    $obj -is [System.Management.Automation.ScriptBlock] -or $obj -is [System.Management.Automation.CommandInfo]\n}\n#\n# General functions\n#\nfunction equal?($a, $b) {\n    if ($a -eq $null -and $b -eq $null) {\n        return $true\n    } elseif ($a -eq $null -or $b -eq $null) {\n        return $false\n    }\n    $ta, $tb = $a.GetType().Name, $b.GetType().Name\n    if (-not (($ta -eq $tb) -or ((sequential?($a)) -and (sequential?($b))))) {\n        return $false\n    }\n    switch ($ta) {\n        { $_ -eq \"List\" -or $_ -eq \"Vector\" } {\n            if ($a.values.Count -ne $b.values.Count) {\n                return $false\n            }\n            for ($i = 0; $i -lt $a.value.Count; $i++) {\n                if (-not (equal? $a.values[$i] $b.values[$i])) {\n                    return $false\n                }\n            }\n            return $true\n        }\n        \"HashMap\" {\n            $hta, $htb = $a.values, $b.values\n            $alen = ($hta.GetEnumerator | Measure-Object).Count\n            $blen = ($htb.GetEnumerator | Measure-Object).Count\n            if ($alen -ne $blen) {\n                return $false\n            }\n            foreach ($kv in $hta.GetEnumerator()) {\n                if (-not (equal? $kv.Value $htb[$kv.Key])) {\n                    return $false\n                }\n            }\n            return $true\n        }\n        \"Symbol\" {\n            return $a.value -ceq $b.value\n        }\n        default {\n            return $a -ceq $b\n        }\n    }\n}\n\n\n#\n# Sequence functions\n#\nfunction sequential?($obj) {\n    $obj -is [List]\n}\n\nfunction empty?($obj) {\n    $obj.values.Count -eq 0\n}\n\n\n"
  },
  {
    "path": "impls/prolog/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install swi-prolog-nox\n"
  },
  {
    "path": "impls/prolog/Makefile",
    "content": "# Stub Makefile to make Travis test mode happy.\nall clean:\n"
  },
  {
    "path": "impls/prolog/core.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\nwrap_failure(Goal, Args, Res) :-\n    check(call(Goal,Args, Res),\n          \"~a: wrong arguments: ~L\", [Goal, Args]).\n\nbool(Goal, true) :- call(Goal), !.\nbool(_,    false).\n\n'nil?'([X], R) :- bool(=(nil,X), R).\n\n'false?'([X], R) :- bool(=(false, X), R).\n\n'true?'([X], R) :- bool(=(true, X), R).\n\n% Numbers\n\n'number?'([X], R) :- bool(integer(X), R).\n\nadd([X, Y], R) :- integer(X), integer(Y),         R is X + Y.\n\nsub([X, Y], R) :- integer(X), integer(Y),         R is X - Y.\n\nmul([X, Y], R) :- integer(X), integer(Y),         R is X * Y.\n\ndiv([X, Y], R) :- integer(X), integer(Y), Y \\= 0, R is X / Y.\n\n'<='([X, Y], R) :- integer(X), integer(Y), bool(=<(X, Y), R).\n\nge(  [X, Y], R) :- integer(X), integer(Y), bool(>=(X, Y), R).\n\nlt(  [X, Y], R) :- integer(X), integer(Y), bool(<(X, Y), R).\n\ngt(  [X, Y], R) :- integer(X), integer(Y), bool(>(X, Y), R).\n\n% Symbols\n\n'symbol?'([false], false).\n'symbol?'([nil],   false).\n'symbol?'([true],  false).\n'symbol?'([X], R) :- bool(atom(X), R).\n\nsymbol([X], R) :- string(X), atom_string(R, X).\n\n% Keywords\n\n'keyword?'([X], R) :- bool(=(X, mal_kwd(_)), R).\n\nkeyword([X], mal_kwd(X)) :- string(X).\nkeyword([R], R)          :- R = mal_kwd(_).\n\n% Sequences\n\n'list?'([X], R) :- bool(list(_, X), R).\n\n'vector?'([X], R) :- bool(vector(_, X), R).\n\n'sequential?'([X], R) :- bool(unbox_seq(X, _), R).\n\n'empty?'([X], R) :- bool(unbox_seq(X, []), R).\n\ncount([X], R) :- unbox_seq(X, S), !, length(S, R).\ncount([nil], 0).\n\nvec([X], R) :- unbox_seq(X, S), vector(S, R).\n\ncons([X, Y], R) :- unbox_seq(Y, Ys), list([X | Ys], R).\n\nconcat(Xs, Z) :- maplist(unbox_seq, Xs, Ys), append(Ys, Zs), list(Zs, Z).\n\nnth([Sequence, Index], Element) :-\n    unbox_seq(Sequence, Xs),\n    check(nth0(Index, Xs, Element),\n          \"nth: index ~d out of bounds of ~F\", [Index, Sequence]).\n\nfirst([X], Y) :- unbox_seq(X, Xs), !,\n    (Xs = [Y | _] -> true ; Y = nil).\nfirst([nil], nil).\n\nrest([X],   R) :- unbox_seq(X, Xs), !,\n    (Xs = [_ | Rs] -> true ; Rs = []),\n    list(Rs, R).\nrest([nil], R) :- list([], R).\n\nmap([Fn, Seq], R) :-\n    unbox_seq(Seq, Xs),\n    mal_fn(Goal, Fn),\n    maplist(enlist_apply(Goal), Xs, Rs), list(Rs, R).\n\nenlist_apply(Goal, X, R) :- call(Goal, [X], R).\n\nconj([Vector | Ys], R) :- vector(Xs, Vector), !,\n    append(Xs, Ys, Zs),\n    vector(Zs, R).\nconj([List   | Ys], R) :- list(Xs, List),\n    foldl(cons, Ys, Xs, Zs), list(Zs, R).\n\ncons(X, Xs, [X | Xs]).\n\nseq([X],  nil) :- unbox_seq(X, []).\nseq([X],  X)   :- list(_, X).\nseq([X],  R)   :- vector(Xs, X), !, list(Xs, R).\nseq([\"\"], nil).\nseq([S],  R)   :- string(S), !,\n    string_chars(S, Chars),\n    maplist(atom_string, Chars, Strings),\n    list(Strings, R).\nseq([nil], nil).\n\n% Maps (there is little not much we can do out of types).\n\n'map?'([X], R) :- bool(is_map(X), R).\n\nget([Map, Key], R) :- get(Map, Key, R).\nget([_,   _], nil).\n\n'contains?'([Map, Key], R) :- bool(get(Map, Key, _), R).\n\ndissoc([Map | Keys], Res) :- foldl(dissoc, Keys, Map, Res).\n\n% Atoms\n\n'atom?'([X], R) :- bool(mal_atom(_, X), R).\n\natom([A], R) :- mal_atom(A, R).\n\nderef([A], R) :- mal_atom(R, A).\n\n'reset!'([A, R], R) :- mal_atom(_, A), set_mal_atom_value(A, R).\n\n'swap!'([Atom, Function | Args], R) :-\n    mal_atom(Old, Atom),\n    mal_fn(Goal, Function),\n    call(Goal, [Old | Args], R),\n    set_mal_atom_value(Atom, R).\n\napply([Fn | Xs], R) :-\n    flatten_last(Xs, Args),\n    (mal_fn(Goal, Fn) ; (mal_macro(F, Fn), mal_fn(Goal, F))),\n    call(Goal, Args, R).\n\nflatten_last([X],      Xs)       :- unbox_seq(X, Xs).\nflatten_last([X | Xs], [X | Ys]) :- flatten_last(Xs, Ys).\n\n% Strings\n\n'string?'([X], R) :- bool(string(X), R).\n\n'pr-str'(Args, R)   :- with_output_to(string(R), print_list(t, \" \", Args)).\n\nstr(     Args, R)   :- with_output_to(string(R), print_list(f, \"\",  Args)).\n\nprn(     Args, nil) :-                           print_list(t, \" \", Args), nl.\n\nprintln( Args, nil) :-                           print_list(f, \" \", Args), nl.\n\n'read-string'([S], R) :- string(S), read_str(S, R).\n\nslurp([Path], R) :-\n    string(Path),\n    (read_file_to_string(Path, R, []) -> true ; R = nil).\n\nreadline([Prompt], R) :-\n    string(Prompt),\n    write(Prompt),\n    read_line_to_string(current_input, R),\n    (R = end_of_file -> R = nil ; true).\n\nthrow([X], nil) :- throw(mal_error(X)).\n\n'time-ms'([], Ms) :- get_time(S), Ms is round(1_000*S).\n\neq([X, Y], R) :- bool(mal_equal(X, Y), R).\n\n'fn?'([X], R) :- bool(mal_fn(_, X), R).\n\n'macro?'([X], R) :- bool(mal_macro(_, X), R).\n\n'prolog-asserta'([String], nil) :-\n    string(String),\n    catch((read_term_from_atom(String, Term, []),\n           asserta(Term)),\n          Error,\n          throwf(\"prolog-asserta: ~w\", [Error])).\n\n'prolog-call'([String], Res) :-\n    string(String),\n    catch((read_term_from_atom(String, Term, []),\n           call(Term, Res)),\n          Error,\n          throwf(\"prolog-call: ~w\", [Error])),\n    check(valid_mal(Res), \"prolog-call: invalid result: ~w\", [Res]).\n\ncore_ns([\n    % naming exceptions\n    '+', add,\n    '-', sub,\n    '*', mul,\n    '/', div,\n    '=', eq,\n    '<', lt,\n    '>=', ge,\n    '>', gt,\n    % step 4\n    '<=', '<=',\n    prn, prn,\n    list, list,\n    'list?', 'list?',\n    'empty?', 'empty?',\n    count, count,\n    'pr-str', 'pr-str',\n    str, str,\n    println, println,\n    % step 6\n    'read-string', 'read-string',\n    slurp, slurp,\n    atom, atom,\n    'atom?', 'atom?',\n    deref, deref,\n    'reset!', 'reset!',\n    'swap!', 'swap!',\n    % step 7\n    cons, cons,\n    concat, concat,\n    vec, vec,\n    % step 8\n    nth, nth,\n    first, first,\n    rest, rest,\n    % step 9\n    throw, throw,\n    apply, apply,\n    map, map,\n    'nil?', 'nil?',\n    'true?', 'true?',\n    'false?', 'false?',\n    'symbol?', 'symbol?',\n    symbol, symbol,\n    keyword, keyword,\n    'keyword?', 'keyword?',\n    vector, vector,\n    'vector?', 'vector?',\n    'sequential?', 'sequential?',\n    'hash-map', 'hash-map',\n    'map?', 'map?',\n    assoc, assoc,\n    dissoc, dissoc,\n    get, get,\n    'contains?', 'contains?',\n    keys, keys,\n    vals, vals,\n    % step A\n    readline, readline,\n    meta, meta,\n    'with-meta', 'with-meta',\n    'time-ms', 'time-ms',\n    conj, conj,\n    'string?', 'string?',\n    'number?', 'number?',\n    'fn?', 'fn?',\n    'macro?', 'macro?',\n    seq, seq,\n    'prolog-asserta', 'prolog-asserta',\n    'prolog-call', 'prolog-call']).\n"
  },
  {
    "path": "impls/prolog/env.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- format_predicate('V', env_format(_Arg,_Env)).\n\nenv(mal_env(Assoc, t)) :- empty_assoc(Assoc).\n\nenv(Outer, mal_env(Assoc, Outer)) :- empty_assoc(Assoc).\n\nenv_get(mal_env(Assoc, _), Key, Value) :- get_assoc(Key, Assoc, Value).\nenv_get(mal_env(_, Outer), Key, Value) :- env_get(Outer, Key, Value).\n\nenv_set(Env, Key, Value) :-\n    Env = mal_env(Old, _),\n    put_assoc(Key, Old, Value, New),\n    setarg(1, Env, New).\n\nenv_format(_Arg, mal_env(Assoc, _Outer)) :-\n    assoc_to_list(Assoc, Pairs),\n    maplist(env_format_pair, Pairs).\n\nenv_format_pair(K - V) :- format(\" ~a:~F\", [K, V]).\n\n% Does *not* check that the keys are symbols. This is done once when\n% the fn* structure is created.\nenv_bind(_Env, [], []).\nenv_bind(Env, ['&', K], Vs) :- !,\n    list(Vs, List),\n    env_set(Env, K, List).\nenv_bind(Env, [K | Ks], [V | Vs]) :-\n    env_set(Env, K, V),\n    env_bind(Env, Ks, Vs).\n"
  },
  {
    "path": "impls/prolog/printer.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- format_predicate('F', format_mal_form(_Arg,_Form)).\n:- format_predicate('L', format_mal_list(_Arg,_Forms)).\nformat_mal_list(_Arg, Forms) :- print_list(t, \" \", Forms).\nformat_mal_form(_Arg, Form) :- pr_str(t, Form).\n\npr_str(t, String) :- string(String), !,\n    write(\"\\\"\"),\n    string_codes(String, Codes),\n    maplist(pr_str_escape, Codes),\n    write(\"\\\"\").\n\npr_str(_, Atomic) :- atomic(Atomic), !,\n    % number, symbol, nil, true, false, unreadable string.\n    write(Atomic).\n\npr_str(_, mal_kwd(Keyword)) :- !,\n    put_char(:),\n    write(Keyword).\n\npr_str(Readably, Vector) :- vector(Elements, Vector), !,\n    write(\"[\"),\n    print_list(Readably, \" \", Elements),\n    write(\"]\").\n\npr_str(Readably, List) :- list(Elements, List), !,\n    write(\"(\"),\n    print_list(Readably, \" \", Elements),\n    write(\")\").\n\npr_str(Readably, Map) :- map_to_key_value_list(Map, Key_Value_List), !,\n    write(\"{\"),\n    print_list(Readably, \" \", Key_Value_List),\n    write(\"}\").\n\npr_str(_, Fn) :- mal_fn(_Goal, Fn), !, write(\"<fn>\").\n\npr_str(_, Macro) :- mal_macro(_Fn, Macro), !,\n    write(\"<macro>\").\n\npr_str(_, Atom) :- mal_atom(Value, Atom), !,\n    format(\"(atom ~F)\", [Value]).\n\npr_str(_, Invalid) :-\n    format(string(Msg), \"pr_str detected an invalid form: ~w\\n\", [Invalid]),\n    print_message(warning, Msg),\n    abort.\n\npr_str_escape(0'\\n) :- write(\"\\\\n\").\npr_str_escape(0'\")  :- write(\"\\\\\\\"\").\npr_str_escape(0'\\\\) :- write(\"\\\\\\\\\").\npr_str_escape(C)    :- put_code(C).\n\nprint_list(_, _, []).\nprint_list(Readably, Separator, [X | Xs]) :-\n    pr_str(Readably, X),\n    maplist(print_list_append(Readably, Separator), Xs).\n\nprint_list_append(Readably, Separator, Element) :-\n    write(Separator),\n    pr_str(Readably, Element).\n"
  },
  {
    "path": "impls/prolog/reader.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- use_module(library(dcg/basics)).\n\nread_str(String, Form) :-\n    string_codes(String, Codes),\n    check(phrase(read_form(Form), Codes, _Rest),\n          \"unbalanced expression: '~s'\", [String]).\n\nread_form(Res) --> zero_or_more_separators, (\n      `(`,  !, read_list(`)`, Forms),            { list(Forms, Res) }\n    | `[`,  !, read_list(`]`, Forms),            { vector(Forms, Res) }\n    | `{`,  !, read_list(`}`, Forms),            { 'hash-map'(Forms, Res) }\n    | `\\``, !, read_form(Form),                  { list([quasiquote, Form], Res) }\n    | `\\'`, !, read_form(Form),                  { list([quote, Form], Res) }\n    | `^`,  !, read_form(Meta), read_form(Data), { list(['with-meta', Data, Meta], Res) }\n    | `:`,  !, at_least_one_symcode(Codes),      { string_codes(String, Codes),\n                                                   Res = mal_kwd(String) }\n    | `\\\"`, !, until_quotes(Codes),              { string_codes(Res, Codes) }\n    | `@`,  !, read_form(Form),                  { list([deref, Form], Res) }\n    | `~@`, !, read_form(Form),                  { list(['splice-unquote', Form], Res) }\n    | `~`,  !, read_form(Form),                  { list([unquote, Form], Res)  }\n    | integer(Res)\n    | at_least_one_symcode(Cs),                  { atom_codes(Res, Cs) }).\n\nread_list(Closing, [Form | Forms]) --> read_form(Form), !, read_list(Closing, Forms).\nread_list(Closing, [])             --> zero_or_more_separators, Closing.\n\nzero_or_more_separators --> separator, !, zero_or_more_separators\n                            | [].\n\nseparator --> [C], { sepcode(C) }, !.\nseparator --> `;`, string_without(`\\n`, _Comment).\n\nat_least_one_symcode([C | Cs]) --> [C], { symcode(C) }, zero_or_more_symcodes(Cs).\n\nuntil_quotes([])          --> [0'\"].\nuntil_quotes([0'\\n | Cs]) --> `\\\\n`,  !, until_quotes(Cs).\nuntil_quotes([0'\"  | Cs]) --> `\\\\\\\"`, !, until_quotes(Cs).\nuntil_quotes([0'\\\\ | Cs]) --> `\\\\\\\\`, !, until_quotes(Cs).\nuntil_quotes([C    | Cs]) --> [C],       until_quotes(Cs).\n\nzero_or_more_symcodes(Cs) --> at_least_one_symcode(Cs), !.\nzero_or_more_symcodes([]) --> [].\n\nsepcode(0',).\nsepcode(0' ).\nsepcode(0'\\n).\n\nsymcode(C) :- code_type(C, alnum).\nsymcode(0'!).\nsymcode(0'#).\nsymcode(0'$).\nsymcode(0'%).\nsymcode(0'&).\nsymcode(0'*).\nsymcode(0'+).\nsymcode(0'-).\nsymcode(0'/).\nsymcode(0'<).\nsymcode(0'=).\nsymcode(0'>).\nsymcode(0'?).\nsymcode(0'_).\nsymcode(0'|).\nsymcode(0':).\n"
  },
  {
    "path": "impls/prolog/run",
    "content": "#!/usr/bin/env bash\nexec swipl $(dirname $0)/${STEP:-stepA_mal}.pl \"${@}\"\n"
  },
  {
    "path": "impls/prolog/step0_repl.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n% Read\n\nmal_read(Line) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true). % fails for duplicate lines\n\n% Eval\n\neval(Ast, Ast).\n\n% Print\n\nprint(Ast) :- writeln(Ast).\n\n% REP\n\nrep :-\n    mal_read(Ast),\n    eval(Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl :-\n    rep,\n    repl.\n\nmain(_Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    catch(repl, exit_repl, nl),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step1_read_print.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval\n\neval(Ast, Ast).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep :-\n    mal_read(Ast),\n    eval(Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl :-\n    catch(rep, mal_error(Message), writeln(Message)),\n    repl.\n\nmain(_Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    catch(repl, exit_repl, nl),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step2_eval.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n    maplist(eval(Env), Rest, Args),\n    call(Goal, Args, Res).\n\n% The eval function itself.\n\n% Uncomment this to get a trace.\n%% eval(_, Ast, _) :-\n%%     format(\"EVAL: ~F\\n\", [Ast]),\n%%     fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(get_assoc(Symbol, Env, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(Message), writeln(Message)),\n    repl(Env).\n\nadd([X, Y], Res) :- integer(X), integer(Y),          Res is X + Y.\nsub([X, Y], Res) :- integer(X), integer(Y),          Res is X - Y.\nmul([X, Y], Res) :- integer(X), integer(Y),          Res is X * Y.\ndiv([X, Y], Res) :- integer(X), integer(Y), Y \\== 0, Res is X / Y.\n\nmain(_Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    mal_fn(add, Add),\n    mal_fn(sub, Sub),\n    mal_fn(mul, Mul),\n    mal_fn(div, Div),\n    list_to_assoc(['+' - Add, '-' - Sub, '*' - Mul, '/' - Div], Env),\n\n    catch(repl(Env), exit_repl, nl),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step3_env.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n    maplist(eval(Env), Rest, Args),\n    call(Goal, Args, Res).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(Message), writeln(Message)),\n    repl(Env).\n\nadd([X, Y], Res) :- integer(X), integer(Y),          Res is X + Y.\nsub([X, Y], Res) :- integer(X), integer(Y),          Res is X - Y.\nmul([X, Y], Res) :- integer(X), integer(Y),          Res is X * Y.\ndiv([X, Y], Res) :- integer(X), integer(Y), Y \\== 0, Res is X / Y.\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(Core_Function, Form),\n    env_set(Env, Symbol, Form).\n\nmain(_Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    map_keyvals(define_core_function(Env), ['+', add, '-', sub, '*', mul, '/', div]),\n\n    catch(repl(Env), exit_repl, nl),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step4_if_fn_do.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([core, env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n:- discontiguous eval_list/4.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\neval_list(Env, if, Args, Res) :- !,\n    check(if_assign_args(Args, Form, Then, Else),\n          \"if: expects 2 or 3 arguments, got: ~L\", [Args]),\n    eval(Env, Form, Test),\n    if_select(Test, Then, Else, Selected),\n    eval(Env, Selected, Res).\n\nif_assign_args([Form, Then, Else], Form, Then, Else).\nif_assign_args([Form, Then],       Form, Then, nil).\n\nif_select(false, _,    Else, Else) :- !.\nif_select(nil,   _,    Else, Else) :- !.\nif_select(_,     Then, _,    Then).\n\neval_list(Env, 'fn*', Args, Res) :- !,\n    check(Args = [Params, Form], \"fn*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Params, Keys), \"fn*: ~F is not a sequence\", [Params]),\n    check(maplist(atom, Keys), \"fn*: ~F should contains symbols\", [Params]),\n    mal_fn(apply_fn(Keys, Form, Env), Res).\n\napply_fn(Keys, Form, Env, Args, Res) :-\n    env(Env, Apply_Env),\n    check(env_bind(Apply_Env, Keys, Args),\n          \"cannot apply fn*[~L] to [~L]\", [Keys, Args]),\n    eval(Apply_Env, Form, Res).\n\neval_list(Env, do, Args, Res) :- !,\n    foldl(do_loop(Env), Args, nil, Res).\n\ndo_loop(Env, Elt, _Old_Acc, New_Acc) :- eval(Env, Elt, New_Acc).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n    maplist(eval(Env), Rest, Args),\n    call(Goal, Args, Res).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(Message), writeln(Message)),\n    repl(Env).\n\nre(Env, String) :-\n    read_str(String, Ast),\n    eval(Env, Ast, _).\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(wrap_failure(Core_Function), Form),\n    env_set(Env, Symbol, Form).\n\nmain(_Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    core_ns(Core_Ns),\n    map_keyvals(define_core_function(Env), Core_Ns),\n    define_core_function(Env, eval, core_eval(Env)),\n\n    re(Env, \"(def! not (fn* [a] (if a false true)))\"),\n\n    catch(repl(Env), exit_repl, nl),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step6_file.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([core, env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n:- discontiguous eval_list/4.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\neval_list(Env, if, Args, Res) :- !,\n    check(if_assign_args(Args, Form, Then, Else),\n          \"if: expects 2 or 3 arguments, got: ~L\", [Args]),\n    eval(Env, Form, Test),\n    if_select(Test, Then, Else, Selected),\n    eval(Env, Selected, Res).\n\nif_assign_args([Form, Then, Else], Form, Then, Else).\nif_assign_args([Form, Then],       Form, Then, nil).\n\nif_select(false, _,    Else, Else) :- !.\nif_select(nil,   _,    Else, Else) :- !.\nif_select(_,     Then, _,    Then).\n\neval_list(Env, 'fn*', Args, Res) :- !,\n    check(Args = [Params, Form], \"fn*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Params, Keys), \"fn*: ~F is not a sequence\", [Params]),\n    check(maplist(atom, Keys), \"fn*: ~F should contains symbols\", [Params]),\n    mal_fn(apply_fn(Keys, Form, Env), Res).\n\napply_fn(Keys, Form, Env, Args, Res) :-\n    env(Env, Apply_Env),\n    check(env_bind(Apply_Env, Keys, Args),\n          \"cannot apply fn*[~L] to [~L]\", [Keys, Args]),\n    eval(Apply_Env, Form, Res).\n\neval_list(Env, do, Args, Res) :- !,\n    foldl(do_loop(Env), Args, nil, Res).\n\ndo_loop(Env, Elt, _Old_Acc, New_Acc) :- eval(Env, Elt, New_Acc).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n    maplist(eval(Env), Rest, Args),\n    call(Goal, Args, Res).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(Message), writeln(Message)),\n    repl(Env).\n\nre(Env, String) :-\n    read_str(String, Ast),\n    eval(Env, Ast, _).\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(wrap_failure(Core_Function), Form),\n    env_set(Env, Symbol, Form).\n\ncore_eval(Env, [Ast], Res) :- eval(Env, Ast, Res).\n\nmain(Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    core_ns(Core_Ns),\n    map_keyvals(define_core_function(Env), Core_Ns),\n    define_core_function(Env, eval, core_eval(Env)),\n\n    re(Env, \"(def! not (fn* [a] (if a false true)))\"),\n    re(Env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"),\n\n    (   maplist(atom_string, Argv, [Script | Args])\n\n    ->  % If Argv starts with a script, set arguments and load it.\n        list(Args, Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        format(string(Load_Script), \"(load-file \\\"~s\\\")\", [Script]),\n        re(Env, Load_Script)\n\n    ;   % else read from standard input.\n        list([], Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        catch(repl(Env), exit_repl, nl)\n    ),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step7_quote.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([core, env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n:- discontiguous eval_list/4.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\neval_list(Env, if, Args, Res) :- !,\n    check(if_assign_args(Args, Form, Then, Else),\n          \"if: expects 2 or 3 arguments, got: ~L\", [Args]),\n    eval(Env, Form, Test),\n    if_select(Test, Then, Else, Selected),\n    eval(Env, Selected, Res).\n\nif_assign_args([Form, Then, Else], Form, Then, Else).\nif_assign_args([Form, Then],       Form, Then, nil).\n\nif_select(false, _,    Else, Else) :- !.\nif_select(nil,   _,    Else, Else) :- !.\nif_select(_,     Then, _,    Then).\n\neval_list(Env, 'fn*', Args, Res) :- !,\n    check(Args = [Params, Form], \"fn*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Params, Keys), \"fn*: ~F is not a sequence\", [Params]),\n    check(maplist(atom, Keys), \"fn*: ~F should contains symbols\", [Params]),\n    mal_fn(apply_fn(Keys, Form, Env), Res).\n\napply_fn(Keys, Form, Env, Args, Res) :-\n    env(Env, Apply_Env),\n    check(env_bind(Apply_Env, Keys, Args),\n          \"cannot apply fn*[~L] to [~L]\", [Keys, Args]),\n    eval(Apply_Env, Form, Res).\n\neval_list(Env, do, Args, Res) :- !,\n    foldl(do_loop(Env), Args, nil, Res).\n\ndo_loop(Env, Elt, _Old_Acc, New_Acc) :- eval(Env, Elt, New_Acc).\n\neval_list(_, quote, Args, Res) :- !,\n    check(Args = [Res], \"quote: expects 1 argument, got ~L\", [Args]).\n\neval_list(Env, quasiquote, Args, Res) :- !,\n    check(Args = [X], \"quasiquote: expects 1 argument, got: ~L\", [Args]),\n    quasiquote(X, Y),\n    eval(Env, Y, Res).\n\nquasiquote(List, Res) :-\n    list(Xs, List), !,\n    (   Xs = [unquote | Args]\n    ->  check(Args = [Res], \"unquote: expects 1 argument, got: \", [Args])\n    ;   list([], Empty),\n        foldr(qq_loop, Empty, Xs, Res)).\nquasiquote(Vector, Res) :-\n    vector(Xs, Vector), !,\n    list([], Empty),\n    foldr(qq_loop, Empty, Xs, Y),\n    list([vec, Y], Res).\nquasiquote(nil,   nil).\nquasiquote(true,  true).\nquasiquote(false, false).\nquasiquote(Symbol_Or_Map, Res) :-\n    (atom(Symbol_Or_Map) -> true ; is_map(Symbol_Or_Map)), !,\n    list([quote, Symbol_Or_Map], Res).\nquasiquote(Anything_Else, Anything_Else).\n\nqq_loop(Elt, Acc, Res) :-\n    list(['splice-unquote' | Args], Elt), !,\n    check(Args = [X], \"splice-unquote: expects 1 argument, got:\", [Args]),\n    list([concat, X, Acc], Res).\nqq_loop(Elt, Acc, Res) :-\n    quasiquote(Elt, Quasiquoted),\n    list([cons, Quasiquoted, Acc], Res).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n    maplist(eval(Env), Rest, Args),\n    call(Goal, Args, Res).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(Message), writeln(Message)),\n    repl(Env).\n\nre(Env, String) :-\n    read_str(String, Ast),\n    eval(Env, Ast, _).\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(wrap_failure(Core_Function), Form),\n    env_set(Env, Symbol, Form).\n\ncore_eval(Env, [Ast], Res) :- eval(Env, Ast, Res).\n\nmain(Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    core_ns(Core_Ns),\n    map_keyvals(define_core_function(Env), Core_Ns),\n    define_core_function(Env, eval, core_eval(Env)),\n\n    re(Env, \"(def! not (fn* [a] (if a false true)))\"),\n    re(Env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"),\n\n    (   maplist(atom_string, Argv, [Script | Args])\n\n    ->  % If Argv starts with a script, set arguments and load it.\n        list(Args, Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        format(string(Load_Script), \"(load-file \\\"~s\\\")\", [Script]),\n        re(Env, Load_Script)\n\n    ;   % else read from standard input.\n        list([], Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        catch(repl(Env), exit_repl, nl)\n    ),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step8_macros.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([core, env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n:- discontiguous eval_list/4.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\neval_list(Env, if, Args, Res) :- !,\n    check(if_assign_args(Args, Form, Then, Else),\n          \"if: expects 2 or 3 arguments, got: ~L\", [Args]),\n    eval(Env, Form, Test),\n    if_select(Test, Then, Else, Selected),\n    eval(Env, Selected, Res).\n\nif_assign_args([Form, Then, Else], Form, Then, Else).\nif_assign_args([Form, Then],       Form, Then, nil).\n\nif_select(false, _,    Else, Else) :- !.\nif_select(nil,   _,    Else, Else) :- !.\nif_select(_,     Then, _,    Then).\n\neval_list(Env, 'fn*', Args, Res) :- !,\n    check(Args = [Params, Form], \"fn*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Params, Keys), \"fn*: ~F is not a sequence\", [Params]),\n    check(maplist(atom, Keys), \"fn*: ~F should contains symbols\", [Params]),\n    mal_fn(apply_fn(Keys, Form, Env), Res).\n\napply_fn(Keys, Form, Env, Args, Res) :-\n    env(Env, Apply_Env),\n    check(env_bind(Apply_Env, Keys, Args),\n          \"cannot apply fn*[~L] to [~L]\", [Keys, Args]),\n    eval(Apply_Env, Form, Res).\n\neval_list(Env, do, Args, Res) :- !,\n    foldl(do_loop(Env), Args, nil, Res).\n\ndo_loop(Env, Elt, _Old_Acc, New_Acc) :- eval(Env, Elt, New_Acc).\n\neval_list(_, quote, Args, Res) :- !,\n    check(Args = [Res], \"quote: expects 1 argument, got ~L\", [Args]).\n\neval_list(Env, quasiquote, Args, Res) :- !,\n    check(Args = [X], \"quasiquote: expects 1 argument, got: ~L\", [Args]),\n    quasiquote(X, Y),\n    eval(Env, Y, Res).\n\nquasiquote(List, Res) :-\n    list(Xs, List), !,\n    (   Xs = [unquote | Args]\n    ->  check(Args = [Res], \"unquote: expects 1 argument, got: \", [Args])\n    ;   list([], Empty),\n        foldr(qq_loop, Empty, Xs, Res)).\nquasiquote(Vector, Res) :-\n    vector(Xs, Vector), !,\n    list([], Empty),\n    foldr(qq_loop, Empty, Xs, Y),\n    list([vec, Y], Res).\nquasiquote(nil,   nil).\nquasiquote(true,  true).\nquasiquote(false, false).\nquasiquote(Symbol_Or_Map, Res) :-\n    (atom(Symbol_Or_Map) -> true ; is_map(Symbol_Or_Map)), !,\n    list([quote, Symbol_Or_Map], Res).\nquasiquote(Anything_Else, Anything_Else).\n\nqq_loop(Elt, Acc, Res) :-\n    list(['splice-unquote' | Args], Elt), !,\n    check(Args = [X], \"splice-unquote: expects 1 argument, got:\", [Args]),\n    list([concat, X, Acc], Res).\nqq_loop(Elt, Acc, Res) :-\n    quasiquote(Elt, Quasiquoted),\n    list([cons, Quasiquoted, Acc], Res).\n\neval_list(Env,  'defmacro!', Args, Res) :- !,\n    check(Args = [Key, Form],\n          \"defmacro!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"defmacro!: ~F is not a key\", [Key]),\n    eval(Env, Form, Fn),\n    check(mal_fn(_Goal, Fn), \"defmacro!: ~F is not a function\", [Fn]),\n    mal_macro(Fn, Res),\n    env_set(Env, Key, Res).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    (   mal_macro(F, Fn)\n    ->  % If the Fn macro refers to F, apply F then evaluate,\n        mal_fn(Goal, F),\n        call(Goal, Rest, New_Ast),\n        eval(Env, New_Ast, Res)\n    ;   % else evaluate arguments, apply Fn.\n        check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n        maplist(eval(Env), Rest, Args),\n        call(Goal, Args, Res)).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(Message), writeln(Message)),\n    repl(Env).\n\nre(Env, String) :-\n    read_str(String, Ast),\n    eval(Env, Ast, _).\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(wrap_failure(Core_Function), Form),\n    env_set(Env, Symbol, Form).\n\ncore_eval(Env, [Ast], Res) :- eval(Env, Ast, Res).\n\nmain(Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    core_ns(Core_Ns),\n    map_keyvals(define_core_function(Env), Core_Ns),\n    define_core_function(Env, eval, core_eval(Env)),\n\n    re(Env, \"(def! not (fn* [a] (if a false true)))\"),\n    re(Env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"),\n    re(Env, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"),\n\n    (   maplist(atom_string, Argv, [Script | Args])\n\n    ->  % If Argv starts with a script, set arguments and load it.\n        list(Args, Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        format(string(Load_Script), \"(load-file \\\"~s\\\")\", [Script]),\n        re(Env, Load_Script)\n\n    ;   % else read from standard input.\n        list([], Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        catch(repl(Env), exit_repl, nl)\n    ),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/step9_try.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([core, env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n:- discontiguous eval_list/4.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\neval_list(Env, if, Args, Res) :- !,\n    check(if_assign_args(Args, Form, Then, Else),\n          \"if: expects 2 or 3 arguments, got: ~L\", [Args]),\n    eval(Env, Form, Test),\n    if_select(Test, Then, Else, Selected),\n    eval(Env, Selected, Res).\n\nif_assign_args([Form, Then, Else], Form, Then, Else).\nif_assign_args([Form, Then],       Form, Then, nil).\n\nif_select(false, _,    Else, Else) :- !.\nif_select(nil,   _,    Else, Else) :- !.\nif_select(_,     Then, _,    Then).\n\neval_list(Env, 'fn*', Args, Res) :- !,\n    check(Args = [Params, Form], \"fn*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Params, Keys), \"fn*: ~F is not a sequence\", [Params]),\n    check(maplist(atom, Keys), \"fn*: ~F should contains symbols\", [Params]),\n    mal_fn(apply_fn(Keys, Form, Env), Res).\n\napply_fn(Keys, Form, Env, Args, Res) :-\n    env(Env, Apply_Env),\n    check(env_bind(Apply_Env, Keys, Args),\n          \"cannot apply fn*[~L] to [~L]\", [Keys, Args]),\n    eval(Apply_Env, Form, Res).\n\neval_list(Env, do, Args, Res) :- !,\n    foldl(do_loop(Env), Args, nil, Res).\n\ndo_loop(Env, Elt, _Old_Acc, New_Acc) :- eval(Env, Elt, New_Acc).\n\neval_list(_, quote, Args, Res) :- !,\n    check(Args = [Res], \"quote: expects 1 argument, got ~L\", [Args]).\n\neval_list(Env, quasiquote, Args, Res) :- !,\n    check(Args = [X], \"quasiquote: expects 1 argument, got: ~L\", [Args]),\n    quasiquote(X, Y),\n    eval(Env, Y, Res).\n\nquasiquote(List, Res) :-\n    list(Xs, List), !,\n    (   Xs = [unquote | Args]\n    ->  check(Args = [Res], \"unquote: expects 1 argument, got: \", [Args])\n    ;   list([], Empty),\n        foldr(qq_loop, Empty, Xs, Res)).\nquasiquote(Vector, Res) :-\n    vector(Xs, Vector), !,\n    list([], Empty),\n    foldr(qq_loop, Empty, Xs, Y),\n    list([vec, Y], Res).\nquasiquote(nil,   nil).\nquasiquote(true,  true).\nquasiquote(false, false).\nquasiquote(Symbol_Or_Map, Res) :-\n    (atom(Symbol_Or_Map) -> true ; is_map(Symbol_Or_Map)), !,\n    list([quote, Symbol_Or_Map], Res).\nquasiquote(Anything_Else, Anything_Else).\n\nqq_loop(Elt, Acc, Res) :-\n    list(['splice-unquote' | Args], Elt), !,\n    check(Args = [X], \"splice-unquote: expects 1 argument, got:\", [Args]),\n    list([concat, X, Acc], Res).\nqq_loop(Elt, Acc, Res) :-\n    quasiquote(Elt, Quasiquoted),\n    list([cons, Quasiquoted, Acc], Res).\n\neval_list(Env, 'try*', Args, Res) :- !,\n    (   Args = [Test]\n    ->  eval(Env, Test, Res)\n    ;   check(Args = [Test, Catch],\n              \"try*: expects 1 or 2 arguments, got: ~L\", [Args]),\n        check(list(['catch*', Key, Form], Catch),\n              \"try*: ~F is not a catch* list\", [Catch]),\n        check(atom(Key), \"catch*: ~F is not a key\", [Key]),\n        catch(eval(Env, Test, Res), mal_error(Error),\n              (env(Env, Try_Env),\n               env_set(Try_Env, Key, Error),\n               eval(Try_Env, Form, Res)))).\n\neval_list(Env,  'defmacro!', Args, Res) :- !,\n    check(Args = [Key, Form],\n          \"defmacro!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"defmacro!: ~F is not a key\", [Key]),\n    eval(Env, Form, Fn),\n    check(mal_fn(_Goal, Fn), \"defmacro!: ~F is not a function\", [Fn]),\n    mal_macro(Fn, Res),\n    env_set(Env, Key, Res).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    (   mal_macro(F, Fn)\n    ->  % If the Fn macro refers to F, apply F then evaluate,\n        mal_fn(Goal, F),\n        call(Goal, Rest, New_Ast),\n        eval(Env, New_Ast, Res)\n    ;   % else evaluate arguments, apply Fn.\n        check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n        maplist(eval(Env), Rest, Args),\n        call(Goal, Args, Res)).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(X), format(\"Exception: ~F\\n\", [X])),\n    repl(Env).\n\nre(Env, String) :-\n    read_str(String, Ast),\n    eval(Env, Ast, _).\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(wrap_failure(Core_Function), Form),\n    env_set(Env, Symbol, Form).\n\ncore_eval(Env, [Ast], Res) :- eval(Env, Ast, Res).\n\nmain(Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    core_ns(Core_Ns),\n    map_keyvals(define_core_function(Env), Core_Ns),\n    define_core_function(Env, eval, core_eval(Env)),\n\n    re(Env, \"(def! not (fn* [a] (if a false true)))\"),\n    re(Env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"),\n    re(Env, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"),\n\n    (   maplist(atom_string, Argv, [Script | Args])\n\n    ->  % If Argv starts with a script, set arguments and load it.\n        list(Args, Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        format(string(Load_Script), \"(load-file \\\"~s\\\")\", [Script]),\n        re(Env, Load_Script)\n\n    ;   % else read from standard input.\n        list([], Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        catch(repl(Env), exit_repl, nl)\n    ),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/stepA_mal.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- initialization(main, main).\n\n:- consult([core, env, printer, reader, types, utils]).\n\n% Read\n\nmal_read(Ast) :-\n    write(\"user> \"),\n    read_line_to_string(current_input, Line),\n    (Line = end_of_file -> throw(exit_repl) ; true),\n    (rl_add_history(Line) -> true ; true), % fails for duplicate lines\n    read_str(Line, Ast).\n\n% Eval non-empty list depending on their first element.\n:- discontiguous eval_list/4.\n\neval_list(Env, 'def!', Args, Res) :- !,\n    check(Args = [Key, Form], \"def!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"def!: ~F is not a symbol\", [Key]),\n    eval(Env, Form, Res),\n    env_set(Env, Key, Res).\n\neval_list(Env, 'let*', Args, Res) :- !,\n    check(Args = [Binds, Form], \"let*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Binds, Xs), \"let*: ~F is not a sequence\", [Binds]),\n    env(Env, Let_Env),\n    check(map_keyvals(let_loop(Let_Env), Xs), \"let*: odd length: ~L\", [Binds]),\n    eval(Let_Env, Form, Res).\n\nlet_loop(Env, Key, Form) :- !,\n    check(atom(Key), \"let*: ~F is not a key\", [Key]),\n    eval(Env, Form, Value),\n    env_set(Env, Key, Value).\n\neval_list(Env, if, Args, Res) :- !,\n    check(if_assign_args(Args, Form, Then, Else),\n          \"if: expects 2 or 3 arguments, got: ~L\", [Args]),\n    eval(Env, Form, Test),\n    if_select(Test, Then, Else, Selected),\n    eval(Env, Selected, Res).\n\nif_assign_args([Form, Then, Else], Form, Then, Else).\nif_assign_args([Form, Then],       Form, Then, nil).\n\nif_select(false, _,    Else, Else) :- !.\nif_select(nil,   _,    Else, Else) :- !.\nif_select(_,     Then, _,    Then).\n\neval_list(Env, 'fn*', Args, Res) :- !,\n    check(Args = [Params, Form], \"fn*: expects 2 arguments, got: ~L\", [Args]),\n    check(unbox_seq(Params, Keys), \"fn*: ~F is not a sequence\", [Params]),\n    check(maplist(atom, Keys), \"fn*: ~F should contains symbols\", [Params]),\n    mal_fn(apply_fn(Keys, Form, Env), Res).\n\napply_fn(Keys, Form, Env, Args, Res) :-\n    env(Env, Apply_Env),\n    check(env_bind(Apply_Env, Keys, Args),\n          \"cannot apply fn*[~L] to [~L]\", [Keys, Args]),\n    eval(Apply_Env, Form, Res).\n\neval_list(Env, do, Args, Res) :- !,\n    foldl(do_loop(Env), Args, nil, Res).\n\ndo_loop(Env, Elt, _Old_Acc, New_Acc) :- eval(Env, Elt, New_Acc).\n\neval_list(_, quote, Args, Res) :- !,\n    check(Args = [Res], \"quote: expects 1 argument, got ~L\", [Args]).\n\neval_list(Env, quasiquote, Args, Res) :- !,\n    check(Args = [X], \"quasiquote: expects 1 argument, got: ~L\", [Args]),\n    quasiquote(X, Y),\n    eval(Env, Y, Res).\n\nquasiquote(List, Res) :-\n    list(Xs, List), !,\n    (   Xs = [unquote | Args]\n    ->  check(Args = [Res], \"unquote: expects 1 argument, got: \", [Args])\n    ;   list([], Empty),\n        foldr(qq_loop, Empty, Xs, Res)).\nquasiquote(Vector, Res) :-\n    vector(Xs, Vector), !,\n    list([], Empty),\n    foldr(qq_loop, Empty, Xs, Y),\n    list([vec, Y], Res).\nquasiquote(nil,   nil).\nquasiquote(true,  true).\nquasiquote(false, false).\nquasiquote(Symbol_Or_Map, Res) :-\n    (atom(Symbol_Or_Map) -> true ; is_map(Symbol_Or_Map)), !,\n    list([quote, Symbol_Or_Map], Res).\nquasiquote(Anything_Else, Anything_Else).\n\nqq_loop(Elt, Acc, Res) :-\n    list(['splice-unquote' | Args], Elt), !,\n    check(Args = [X], \"splice-unquote: expects 1 argument, got:\", [Args]),\n    list([concat, X, Acc], Res).\nqq_loop(Elt, Acc, Res) :-\n    quasiquote(Elt, Quasiquoted),\n    list([cons, Quasiquoted, Acc], Res).\n\neval_list(Env, 'try*', Args, Res) :- !,\n    (   Args = [Test]\n    ->  eval(Env, Test, Res)\n    ;   check(Args = [Test, Catch],\n              \"try*: expects 1 or 2 arguments, got: ~L\", [Args]),\n        check(list(['catch*', Key, Form], Catch),\n              \"try*: ~F is not a catch* list\", [Catch]),\n        check(atom(Key), \"catch*: ~F is not a key\", [Key]),\n        catch(eval(Env, Test, Res), mal_error(Error),\n              (env(Env, Try_Env),\n               env_set(Try_Env, Key, Error),\n               eval(Try_Env, Form, Res)))).\n\neval_list(Env,  'defmacro!', Args, Res) :- !,\n    check(Args = [Key, Form],\n          \"defmacro!: expects 2 arguments, got: ~L\", [Args]),\n    check(atom(Key), \"defmacro!: ~F is not a key\", [Key]),\n    eval(Env, Form, Fn),\n    check(mal_fn(_Goal, Fn), \"defmacro!: ~F is not a function\", [Fn]),\n    mal_macro(Fn, Res),\n    env_set(Env, Key, Res).\n\n% apply phase\n\neval_list(Env, First, Rest, Res) :-\n    eval(Env, First, Fn),\n    (   mal_macro(F, Fn)\n    ->  % If the Fn macro refers to F, apply F then evaluate,\n        mal_fn(Goal, F),\n        call(Goal, Rest, New_Ast),\n        eval(Env, New_Ast, Res)\n    ;   % else evaluate arguments, apply Fn.\n        check(mal_fn(Goal, Fn), \"cannot apply, ~F is not a function\", [Fn]),\n        maplist(eval(Env), Rest, Args),\n        call(Goal, Args, Res)).\n\n% The eval function itself.\n\ndebug_eval(_, _, nil).\ndebug_eval(_, _, false).\ndebug_eval(Env, Ast, _) :- format(\"EVAL: ~F    in ~V\\n\", [Ast, Env]).\n\neval(Env, Ast, _) :-\n    env_get(Env, 'DEBUG-EVAL', Flag),\n    debug_eval(Env, Ast, Flag),\n    fail.                       % Proceed with normal alternatives.\n\neval(Env, List, Res) :-\n    list([First | Args], List), !,\n    eval_list(Env, First, Args, Res).\n\neval(_,   nil,    nil).\neval(_,   true,   true).\neval(_,   false,  false).\neval(Env, Symbol, Res) :-\n    atom(Symbol), !,\n    check(env_get(Env, Symbol, Res), \"'~F' not found\", [Symbol]).\n\neval(Env, Vector, Res) :-\n    vector(Xs, Vector), !,\n    maplist(eval(Env), Xs, Ys),\n    vector(Ys, Res).\n\neval(Env, Map, Res) :- map_map(eval(Env), Map, Res).\n\neval(_, Anything_Else, Anything_Else).\n\n% Print\n\nprint(Ast) :- format(\"~F\\n\", [Ast]).\n\n% REP\n\nrep(Env) :-\n    mal_read(Ast),\n    eval(Env, Ast, Evaluated),\n    print(Evaluated).\n\n% Main program\n\nrepl(Env) :-\n    catch(rep(Env), mal_error(X), format(\"Exception: ~F\\n\", [X])),\n    repl(Env).\n\nre(Env, String) :-\n    read_str(String, Ast),\n    eval(Env, Ast, _).\n\ndefine_core_function(Env, Symbol, Core_Function) :-\n    mal_fn(wrap_failure(Core_Function), Form),\n    env_set(Env, Symbol, Form).\n\ncore_eval(Env, [Ast], Res) :- eval(Env, Ast, Res).\n\nmain(Argv) :-\n    getenv(\"HOME\", Home),\n    string_concat(Home, \"/.mal-history\", History),\n    (exists_file(History) -> rl_read_history(History) ; true),\n\n    env(Env),\n    core_ns(Core_Ns),\n    map_keyvals(define_core_function(Env), Core_Ns),\n    define_core_function(Env, eval, core_eval(Env)),\n\n    env_set(Env, '*host-language*', \"prolog\"),\n\n    re(Env, \"(def! not (fn* [a] (if a false true)))\"),\n    re(Env, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"),\n    re(Env, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"),\n\n    (   maplist(atom_string, Argv, [Script | Args])\n\n    ->  % If Argv starts with a script, set arguments and load it.\n        list(Args, Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        format(string(Load_Script), \"(load-file \\\"~s\\\")\", [Script]),\n        re(Env, Load_Script)\n\n    ;   % else read from standard input.\n        list([], Mal_Argv),\n        env_set(Env, '*ARGV*', Mal_Argv),\n\n        re(Env, \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"),\n        catch(repl(Env), exit_repl, nl)\n    ),\n\n    (rl_write_history(History) -> true ; true).\n"
  },
  {
    "path": "impls/prolog/tests/stepA_mal.mal",
    "content": ";; Testing basic prolog interop\n\n(prolog-call \"1+\")\n;/.*prolog-call: .*syntax_error.*\n(prolog-call \"atom_length(\\\"ab\\\")\")\n;=>2\n(prolog-call \"atom_concat(\\\"ab\\\", \\\"cd\\\")\")\n;=>abcd\n(prolog-call \"number_string(42)\")\n;=>\"42\"\n(prolog-call \"=(mal_kwd(\\\"kw\\\"))\")\n;=>:kw\n(prolog-call \"list([a, b])\")\n;=>(a b)\n(prolog-call \"vector([a, b])\")\n;=>[a b]\n(prolog-call \"'hash-map'([\\\"a\\\", 1])\")\n;=>{\"a\" 1}\n(meta (prolog-call \"=(mal_vector([a, b], 12))\"))\n;=>12\n(prolog-call \"=(mal_list([1, mal_formed(1)]))\")\n;/.*prolog-call: invalid result.*\n\n(prolog-asserta \"(mal_setenv(Name, Value, nil) :- setenv(Name, Value))\")\n;=>nil\n(prolog-call \"mal_setenv(\\\"answer\\\", 42)\")\n;=>nil\n(prolog-call \"getenv(\\\"answer\\\")\")\n;=>42\n"
  },
  {
    "path": "impls/prolog/types.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n:- discontiguous mal_equal/2.\n:- discontiguous 'with-meta'/2.\n:- discontiguous meta/2.\n:- discontiguous valid_mal/1.\n\n% A MAL number is represented by a Prolog integer.\n\n% A MAL symbol is represented by a Prolog atom,\n% including `false`, `nil` and `true`.\n\n% A MAL string is represented by a Prolog string.\n\n% A MAL keyword is represented as mal_kwd(String), and there is no\n% reason to encapsulate this information.\n\n% The remaining representations are encapsulated because they may have\n% to evolve, and interfer directly with metadata.\n\nmal_equal(X,  X) :- atomic(X), !.\nmal_equal(mal_kwd(S), mal_kwd(S)) :- !.\n\nvalid_mal(X) :- integer(X), !.\nvalid_mal(X) :- atom(X), !.\nvalid_mal(X) :- string(X), !.\nvalid_mal(mal_kwd(S)) :- !, string(S).\n\n% Sequences\n\n% list(?Forms, ?List)\n%   Bi-directional conversion between a list of MAL forms and a MAL list.\n%   At least one of the two arguments must be instantiated.\n%   Fails if the second argument is instantiated but not a MAL list.\n% vector(?Forms, ?Vector)\n%   Similar for MAL vectors.\n\nlist(Forms, mal_list(Forms)) :- !.\nlist(Forms, mal_list(Forms, _Meta)) :- !.\n\nvector(Forms, mal_vector(Forms)) :- !.\nvector(Forms, mal_vector(Forms, _Meta)) :- !.\n\nmal_equal(S1, S2) :-\n    unbox_seq(S1, L1), !,\n    unbox_seq(S2, L2),\n    maplist(mal_equal, L1, L2).\n\n'with-meta'([X, Meta], mal_list(  Forms, Meta)) :- list(  Forms, X), !.\n'with-meta'([X, Meta], mal_vector(Forms, Meta)) :- vector(Forms, X), !.\n\nmeta([mal_list(_,   Meta)], Meta) :- !.\nmeta([mal_vector(_, Meta)], Meta) :- !.\n\nvalid_mal(mal_list(F))      :- !, maplist(valid_mal, F).\nvalid_mal(mal_list(F, M))   :- !, maplist(valid_mal, F), valid_mal(M).\nvalid_mal(mal_vector(F))    :- !, maplist(valid_mal, F).\nvalid_mal(mal_vector(F, M)) :- !, maplist(valid_mal, F), valid_mal(M).\n\n% Maps\n\n% Other files should not directly depend on Assoc, as there may be\n% good reasons to change the map representation.\n\n'hash-map'(Key_Value_List, mal_map(Res)) :-\n    empty_assoc(Assoc),\n    check(foldl_keyvals(assoc, Assoc, Key_Value_List, Res),\n          \"hash-map: odd count of key and values in ~L\", [Key_Value_List]).\n\nis_map(mal_map(_Assoc)) :- !.\nis_map(mal_map(_Assoc, _Meta)) :- !.\n\nis_key(Key) :- string(Key), !.\nis_key(mal_kwd(_)) :- !.\n\nunbox_map(mal_map(Assoc), Assoc) :- !.\nunbox_map(mal_map(Assoc, _Meta), Assoc) :- !.\n\nget(Map, Key, Res) :-\n    unbox_map(Map, Assoc),\n    is_key(Key),\n    get_assoc(Key, Assoc, Res).\n\nassoc([Map | Key_Value_List], mal_map(Res)) :-\n    unbox_map(Map, Assoc),\n    check(foldl_keyvals(assoc, Assoc, Key_Value_List, Res),\n          \"assoc: odd count of key and values in [~L]\", [Key_Value_List]).\n\nassoc(Assoc, Key, Value, Res) :-\n    check(is_key(Key), \"map keys must be strings or symbol, not ~F\", [Key]),\n    put_assoc(Key, Assoc, Value, Res).\n\n% This order of parameter is convenient with foldl.\ndissoc(Key, Map, mal_map(Res)) :-\n    unbox_map(Map, Assoc),\n    is_key(Key),\n    % del_assoc fails if the key did previously exist,\n    % and we do not want to search twice.\n    (del_assoc(Key, Assoc, _Value, Res) -> true ; Res = Assoc).\n\nmap_map(Goal, Map, mal_map(Res)) :-\n    unbox_map(Map, Assoc),\n    map_assoc(Goal, Assoc, Res).\n\nkeys([Map], Res) :-\n    unbox_map(Map, Assoc),\n    assoc_to_keys(Assoc, Keys),\n    list(Keys, Res).\n\nvals([Map], Res) :-\n    unbox_map(Map, Assoc),\n    assoc_to_values(Assoc, Vals),\n    list(Vals, Res).\n\n% MAL map -> key/value Prolog list\n% Fail if the form is not a map.\nmap_to_key_value_list(Map, Forms) :-\n    unbox_map(Map, Assoc),\n    assoc_to_list(Assoc, Pairs),\n    foldr(convert_pair, [], Pairs, Forms).\n\nconvert_pair(Key - Value, Acc, [Key, Value | Acc]).\n\nmal_equal(Map1, Map2) :-\n    unbox_map(Map1, Assoc1), !,\n    unbox_map(Map2, Assoc2),\n    % map_assoc(mal_equal) does not work here because its result\n    % depends on the internal structure.\n    assoc_to_list(Assoc1, Pairs1),\n    assoc_to_list(Assoc2, Pairs2),\n    maplist(map_pair_equal, Pairs1, Pairs2).\n\nmap_pair_equal(K1 - V1, K2 - V2) :- K1 = K2, mal_equal(V1, V2).\n\n'with-meta'([X, Meta], mal_map(Assoc, Meta)) :- unbox_map(X, Assoc), !.\n\nmeta([mal_map(_, Meta)], Meta) :- !.\n\nvalid_mal(mal_map(Assoc)) :- !,\n    is_assoc(Assoc),\n    assoc_to_list(Assoc, Pairs),\n    maplist(valid_mal_pair, Pairs).\nvalid_mal(mal_map(Assoc, Meta)) :- !,\n    is_assoc(Assoc),\n    assoc_to_list(Assoc, Pairs),\n    maplist(valid_mal_pair, Pairs),\n    valid_mal(Meta).\n\nvalid_mal_pair(K - V) :- is_key(K), valid_mal(V).\n\n% Functions\n\n% Goal is called with call(Goal, [Arg1, Arg2..], Res).\n% It should never fail, and use mal_error/1 to report problems.\n\nmal_fn(Goal, mal_fn(Goal)) :- !.\nmal_fn(Goal, mal_fn(Goal, _Meta)) :- !.\n\n'with-meta'([mal_fn(Goal),        Meta], mal_fn(Goal, Meta)) :- !.\n'with-meta'([mal_fn(Goal, _Meta), Meta], mal_fn(Goal, Meta)) :- !.\n\nmeta([mal_fn(_,Meta)], Meta) :- !.\n\nvalid_mal(mal_fn(_)) :- !.\nvalid_mal(mal_fn(_, Meta)) :- !, valid_mal(Meta).\n\n% Macros\n\nmal_macro(Fn, mal_macro(Fn)).\n\n% Atoms\n\nmal_atom(Value, mal_atom(Value)).\n\nset_mal_atom_value(Atom, Value) :- setarg(1, Atom, Value).\n\nvalid_mal(mal_atom(Value)) :- !, valid_mal(Value).\n\n% Catch-all clause for objects without metadata.\n\nmeta([_], nil) :- !.\n"
  },
  {
    "path": "impls/prolog/utils.pl",
    "content": "% -*- mode: prolog; -*- select prolog mode in the emacs text editor\n\n% Convenient shortcuts, especially during steps 1 to 6.\n\n% Similar to \"assert\", but raise an non-fatal error.\ncheck(Condition, _, _) :- call(Condition), !.\ncheck(_, Format, Arguments) :- throwf(Format, Arguments).\n\nthrowf(Format, Arguments) :-\n    format(string(Message), Format, Arguments),\n    throw(mal_error(Message)).\n\n% Convenient shortcut: unbox(+Sequence, -List).\n\nunbox_seq(Sequence, Forms) :- list(Forms, Sequence).\nunbox_seq(Sequence, Forms) :- vector(Forms, Sequence).\n\n% Abstract some loops.\n\n% foldr(Goal, Vn, [X1, X2,...,Xn], V0) :-\n%     Goal(Xn, Vn, Vn-1),\n%     ...\n%     Goal(X2, V2, V1),\n%     Goal(X1, V1, V0),\nfoldr(_,    Vn, [],     Vn).\nfoldr(Goal, Vn, [X|Xs], V0) :-\n    foldr(Goal, Vn, Xs, V1),\n    call(Goal, X, V1, V0).\n\n% foldl_keyvals(Goal, Init, [K1, V1, K2, V2, K3, V3], Acc3) :-\n%     Goal(Init, K1, V1, Acc1),\n%     Goal(Acc1, K2, V2, Acc2),\n%     Goal(Acc2, K3, V3, Acc3).\nfoldl_keyvals(_,    Init, [], Init).\nfoldl_keyvals(Goal, Init, [K, V | KVs], Res) :-\n    call(Goal, Init, K, V, Acc),\n    foldl_keyvals(Goal, Acc, KVs, Res).\n\n% map_keyvals(Goal, [K1, V1, K2, V2, K3, V3]) :-\n%     Goal(K1, V1),\n%     Goal(K2, V2),\n%     Goal(K3, V3).\nmap_keyvals(_, []).\nmap_keyvals(Goal, [K, V | KVs]) :-\n    call(Goal, K, V),\n    map_keyvals(Goal, KVs).\n"
  },
  {
    "path": "impls/ps/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# PostScript/ghostscript\nRUN apt-get -y install ghostscript\n"
  },
  {
    "path": "impls/ps/Makefile",
    "content": "SOURCES_BASE = types.ps reader.ps printer.ps\nSOURCES_LISP = env.ps core.ps stepA_mal.ps\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.ps mal\n\nmal.ps: $(SOURCES)\n\tcat $+ | grep -v \"runlibfile$$\" > $@\n\nmal: mal.ps\n\techo \"#!/bin/sh\" > $@\n\techo \"\\\":\\\" pop pop pop pop %#; exec gs -d'#!'=null -d'\\\":\\\"'=null -q -dNODISPLAY -- \\\"\\$$0\\\" \\\"\\$$@\\\"\" >> $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.ps mal\n"
  },
  {
    "path": "impls/ps/core.ps",
    "content": "% requires types.ps\n\n% Errors/Exceptions\n\n% data -> throw ->\n% Takes arbitrary data and throws it as an exception.\n/throw { 0 _nth _throw } def\n\n% Predicates\n\n/fn? {\n  0 _nth\n  dup _mal_function? { %if mal_function\n    /macro? get true eq not %if not marked as macro\n  }{\n   _function? %if function\n  } ifelse\n} def\n\n/macro? {\n  0 _nth\n  dup _mal_function? { %if user defined function\n    /macro? get true eq %if marked as macro\n  }{\n      pop false\n  } ifelse\n} def\n\n% Hash Map functions\n\n% [hashmap key val ...] -> assoc -> new_hashmap\n/assoc { 4 dict begin\n    /args exch def\n    /src_dict args 0 _nth /data get def\n    /new_dict src_dict\n              dup length args _count 1 sub 2 idiv add % new length\n              dict % new dict of that length\n              copy def\n    1 2 args _count 1 sub { %for each key idx\n        /idx exch def\n        new_dict  args idx _nth  args idx 1 add _nth  put\n    } for\n    new_dict _hash_map_from_dict\nend } def\n\n% [hashmap key...] -> dissoc -> new_hashmap\n/dissoc { 4 dict begin\n    /args exch def\n    /src_dict args 0 _nth /data get def\n    /new_dict src_dict dup length dict copy def\n    1 1 args _count 1 sub { %for each key idx\n        /idx exch def\n        new_dict  args idx _nth  undef\n    } for\n    new_dict _hash_map_from_dict\nend } def\n\n% [hashmap key] -> hash_map_get -> value\n/hash_map_get {\n    dup 0 _nth % stack: args hash_map\n    dup null eq { %if hash_map is a nil\n        pop pop null\n    }{ %else hash_map is not a nil\n        exch 1 _nth % stack: hash_map key\n        _hash_map_get\n    } ifelse\n} def\n\n% [hashmap key] -> contains? -> bool\n/contains? {\n    dup 0 _nth /data get % stack: args dict\n    exch 1 _nth % stack: dict key\n    known\n} def\n\n% [hashmap] -> vals -> val_list\n/vals {\n    0 _nth /data get\n    [ exch { exch pop } forall ]\n    _list_from_array\n} def\n\n\n% sequence functions\n\n% [obj list] -> cons -> new_list\n/cons { 3 dict begin\n    /args exch def\n    /elem args 0 _nth def\n    /lst args 1 _nth def\n    lst _count 1 add array\n    dup 0 elem put % first element\n    dup 1 lst /data get putinterval % rest of the elements\n    _list_from_array\nend } def\n\n% [listA listB] -> do_concat -> [listA... listB...]\n/do_concat {\n    dup _count 0 eq { %if just concat\n        pop 0 _list\n    }{ dup _count 1 eq { %elseif concat of single item\n        0 _nth dup _vector? { %if vector\n\t    /data get _list_from_array\n\t} if\n    }{ % else\n        [] exch\n        /data get {\n            /data get concatenate\n        } forall\n        _list_from_array\n    } ifelse } ifelse\n} def\n\n% [seq idx] -> nth -> obj\n/nth { 3 dict begin\n    /args exch def\n    /seq args 0 _nth /data get def\n    /idx args 1 _nth def\n    idx seq length lt {\n        seq idx get\n    }{\n        (nth: index out of range) _throw\n    } ifelse\nend } def\n\n\n% [obj] -> do_count -> number\n/do_count {\n    0 _nth dup _nil? {\n        pop 0\n    }{\n        _count\n    } ifelse\n} def\n\n% [obj ...] -> first -> obj\n/first {\n    0 _nth dup _nil? {\n        pop null\n    }{\n        _first\n    } ifelse\n} def\n\n% [obj objs...] -> first -> [objs..]\n/rest {\n    0 _nth dup _nil? {\n        pop 0 _list\n    }{\n        _rest\n    } ifelse\n} def\n\n% [function args... arg_list] -> apply -> result\n/apply { 1 dict begin\n    /args exch def\n    args 0 _nth callable % make sure function is callable\n    args /data get 1 args _count 2 sub getinterval % get args slice\n    args args _count 1 sub _nth /data get % get arg_list array\n    concatenate _list_from_array exch % stack: args function\n    exec\nend } def\n\n% [function list] -> _map -> new_list\n/map { 1 dict begin\n    dup 0 _nth exch 1 _nth % stack: function list\n    /args exch def\n    callable % make sure function is callable\n    %/new_list args length array def\n    args /data get { %foreach arg\n        1 array astore _list_from_array % stack: fn arglist\n        exch dup 3 1 roll               % stack: fn arglist fn\n        exec exch % stack: result fn\n    } forall \n    pop % remove the function\n    args _count array astore\n    _list_from_array\nend } def\n\n% [vect elem...] -> conj -> new_vect\n% [list elem...] -> conj -> new_list\n/conj { 5 dict begin\n    /args exch def\n    /src_arr args 0 _nth /data get def\n    /new_len src_arr length   args _count 1 sub   add def\n    /new_arr new_len array def\n    args 0 _nth _list? { %if list\n        new_arr   new_len src_arr length sub   src_arr putinterval\n        args _count 1 sub -1 1 {\n            /idx exch def\n            new_arr   args _count idx sub 1 sub   args idx _nth   put\n        } for\n        new_arr _list_from_array\n    }{ %else vector\n        src_arr new_arr copy pop\n        1 1 args _count 1 sub {\n            /idx exch def\n            new_arr   src_arr length idx add 1 sub   args idx _nth   put\n        } for\n        new_arr _vector_from_array\n    } ifelse\nend } def\n\n% [obj] -> seq -> new_list/nil/error\n/seq { 1 dict begin\n    0 _nth /obj exch def\n    obj _list? { % if list\n        obj _count 0 eq { null }{ obj } ifelse\n    }{ obj _vector? { % if vector\n        obj _count 0 eq { null }{ obj /data get _list_from_array } ifelse\n    }{ obj _string? { % if string\n        obj length 0 eq {\n            null\n        }{\n            % convert string to 1 character strings\n            obj {\n                1 string dup 0 % chr string string 0\n                4 -1 roll % string string 0 chr\n                put\n            } forall\n            obj length _list\n        } ifelse\n    }{ null obj eq { % if nil\n        null\n    }{ % invalid seq argument\n        (seq: called on non-sequence) _throw\n    } ifelse } ifelse } ifelse } ifelse\nend } def\n\n\n% Metadata functions\n\n% [obj meta] -> with_meta -> new_obj\n/with_meta {\n    dup 1 _nth exch 0 _nth % stack: meta obj\n    dup length dict copy   % stack: meta new_obj\n    dup 3 -1 roll          % stack: new_obj new_obj meta\n    /meta exch put\n} def\n\n% [obj] -> meta -> meta\n/meta {\n    0 _nth % stack: obj\n    dup type /dicttype eq { %if dictionary\n        dup /meta known { /meta get }{ pop null } ifelse\n    }{ %else\n        pop null % no meta on non-collections\n    } ifelse\n} def\n\n\n% Atom functions\n\n/deref {\n    0 _nth /data get\n} def\n\n% [atm val] -> reset! -> val\n/reset! {\n    dup 0 _nth exch 1 _nth % stack: atm val\n    dup 3 1 roll           % stack: val atm val\n    /data exch put\n} def\n\n% [atm f args...] -> swap! -> new_val\n/swap! { 3 dict begin\n    /args exch def\n    /atm args 0 _nth def\n    [ atm /data get ] \n    args 2 args _count 2 sub _slice /data get\n    concatenate _list_from_array\n    args 1 _nth callable % extract proc\n    exec\n    /new_val exch def\n    atm /data new_val put\n    new_val\nend } def\n\n% core_ns is namespace of core functions\n\n/core_ns <<\n    (=)       { dup 0 _nth exch 1 _nth _equal? }\n    (throw)   { throw }\n    (nil?)    { 0 _nth _nil? }\n    (true?)   { 0 _nth _true? }\n    (false?)  { 0 _nth _false? }\n    (string?) { 0 _nth _string? }\n    (symbol)  { 0 _nth _symbol }\n    (symbol?) { 0 _nth _symbol? }\n    (keyword) { 0 _nth _keyword }\n    (keyword?) { 0 _nth _keyword? }\n    (number?) { 0 _nth type /integertype eq }\n    (fn?)     { fn? }\n    (macro?)  { macro? }\n\n    (pr-str)  { /data get ( ) true _pr_str_args }\n    (str)     { /data get () false _pr_str_args }\n    (prn)     { /data get ( ) true _pr_str_args print (\\n) print null }\n    (println) { /data get ( ) false _pr_str_args print (\\n) print null }\n    (readline) { 0 _nth _readline not { pop null } if }\n    (read-string) { 0 _nth read_str }\n    (slurp)   { 0 _nth (r) file dup bytesavailable string readstring pop }\n    (<)       { dup 0 _nth exch 1 _nth lt }\n    (<=)      { dup 0 _nth exch 1 _nth le }\n    (>)       { dup 0 _nth exch 1 _nth gt }\n    (>=)      { dup 0 _nth exch 1 _nth ge }\n    (+)       { dup 0 _nth exch 1 _nth add }\n    (-)       { dup 0 _nth exch 1 _nth sub }\n    (*)       { dup 0 _nth exch 1 _nth mul }\n    (/)       { dup 0 _nth exch 1 _nth idiv }\n    (time-ms) { pop realtime }\n\n    (list)    { /data get _list_from_array }\n    (list?)   { 0 _nth _list? }\n    (vector)  { /data get _vector_from_array }\n    (vector?) { 0 _nth _vector? }\n    (hash-map) { /data get _hash_map_from_array }\n    (map?)    { 0 _nth _hash_map? }\n    (assoc)   { assoc }\n    (dissoc)  { dissoc }\n    (get)     { hash_map_get }\n    (contains?) { contains? }\n    (keys)    { 0 _nth _keys }\n    (vals)    { vals }\n\n    (sequential?) { 0 _nth _sequential? }\n    (cons)    { cons }\n    (concat)  { do_concat }\n    (vec)     { 0 _nth /data get _vector_from_array }\n    (nth)     { nth }\n    (first)   { first }\n    (rest)    { rest }\n    (empty?)  { 0 _nth _count 0 eq }\n    (count)   { do_count }\n    (apply)   { apply }\n    (map)     { map }\n\n    (conj)    { conj }\n    (seq)     { seq }\n\n    (with-meta) { with_meta }\n    (meta)    { meta }\n    (atom)    { 0 _nth _atom }\n    (atom?)   { 0 _nth _atom? }\n    (deref)   { deref }\n    (reset!)  { reset! }\n    (swap!)   { swap! }\n>> def\n"
  },
  {
    "path": "impls/ps/env.ps",
    "content": "% outer binds exprs -> env_new -> new_env\n/env_new { 3 dict begin\n    %(in env_new\\n) print\n    /exprs exch dup _sequential? { /data get }{ pop [ ] } ifelse def\n    /binds exch dup _sequential? { /data get }{ pop [ ] } ifelse def\n    /outer exch def\n    << \n        /__outer__ outer\n        0 1 binds length 1 sub {\n            /idx exch def\n            binds idx get (&) eq { %if &\n                binds idx 1 add get % key\n                exprs idx   exprs length idx sub   getinterval % value\n                _list_from_array\n                exit\n            } if\n            binds idx get % key\n            exprs idx get % value\n        } for\n    >>\nend } def\n\n/env_set { % env key value ->\n    put\n} def\n\n/env_get { % env key -> value true    OR    false\n    {\n        2 copy known { get true exit } if\n        exch /__outer__ get\n        dup null eq { pop pop false exit } if\n        exch\n    } loop\n} def\n"
  },
  {
    "path": "impls/ps/interop.ps",
    "content": "% [ ps_val1...] -> ps2mal -> [ mal_val1...]\n/ps2mal {\n    % convert returned values to Mal types\n    [ exch\n        { %forall returned values\n            dup ==\n            dup type /arraytype eq {\n                (here1\\n) print \n                _list_from_array\n            }{ dup type /dicttype eq {\n                (here2\\n) print\n                _hash_map_from_dict\n            }{\n                (here3\\n) print\n                % no-op\n            } ifelse } ifelse\n        } forall \n    ]\n    (here4\\n) print \n} def\n\n"
  },
  {
    "path": "impls/ps/printer.ps",
    "content": "% requires types.ps to be included first\n\n% ast print_readably -> _pr_str -> string\n/_pr_str { 4 dict begin\n    /print_readably exch def\n    dup xcheck { (Cannot print proc: ) print dup == quit } if % assert\n    /obj exch def\n    obj _sequential? {\n        obj _list? { (\\() (\\)) }{ ([) (]) } ifelse\n        obj /data get ( ) print_readably _pr_str_args\n        exch concatenate concatenate\n    }{ obj _hash_map? {\n        ({)\n        % get array of contents with keys stringified\n        [ obj /data get { exch dup length string cvs exch } forall ]\n        ( ) print_readably _pr_str_args\n        concatenate\n        (}) concatenate\n    }{ obj _function? { % if builtin function\n        (<\\(builtin_fn* {)\n        obj /data get dup length array copy cvlit\n        ( ) print_readably _pr_str_args\n        (}>)\n        concatenate concatenate\n    }{ obj _mal_function? { % if user defined mal_function\n        (<\\(fn* )\n        obj /params get print_readably _pr_str\n        ( )\n        obj /ast get print_readably _pr_str\n        (\\)>)\n        concatenate concatenate concatenate concatenate\n    }{ obj _atom? { % if atom\n        (\\(atom ) \n        obj /data get print_readably _pr_str\n        (\\))\n        concatenate concatenate\n    }{ /arraytype obj type eq { % if list or code block\n        % accumulate an array of strings\n        (\\()\n        obj ( ) print_readably _pr_str_args\n        concatenate\n        (\\))\n        concatenate\n    }{ /integertype obj type eq { % if number\n        /slen\n          obj abs 1 max log floor cvi 1 add % positive size\n          obj 0 lt { 1 add } if % account for sign\n        def\n        obj 10 slen string cvrs\n    }{ /stringtype obj type eq { % if string\n        obj length 0 gt { % if string length > 0\n            obj 0 get 127 eq { %if starts with 0x7f (keyword)\n                obj dup length string copy\n                dup 0 58 put % 58 is ':'\n            }{ print_readably {\n                (\")\n                obj (\\\\) (\\\\\\\\) replace\n                    (\") (\\\\\") replace\n                    (\\n) (\\\\n) replace\n                (\") concatenate concatenate\n            }{\n                obj\n            } ifelse } ifelse\n        }{ % else empty string\n            print_readably {\n                (\"\")\n            }{\n                obj\n            } ifelse\n        } ifelse\n    }{ null obj eq { % if nil\n        (nil)\n    }{ true obj eq { % if true\n        (true)\n    }{ false obj eq { % if false\n        (false)\n    }{ /nametype obj type eq { % if symbol\n        obj dup length string cvs\n    }{\n        (<unknown>)  \n    } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse\nend } def\n\n% array delim print_readably -> _pr_str_args -> new_string\n/_pr_str_args { 3 dict begin\n    /print_readably exch def\n    /delim exch def\n    /args exch def\n    ()\n    args length 0 gt { %if any elements\n        [\n            args { %foreach argument in array\n                dup xcheck { %if executable\n                    255 string cvs\n                }{\n                    print_readably _pr_str\n                } ifelse\n            } forall \n        ]\n        { concatenate delim concatenate } forall\n        dup length delim length sub 0 exch getinterval % strip off final delim\n    } if\nend } def\n\n% utility function\n/print_dict {\n    (DICT contents:\\n) print\n    {\n        (  - ) print\n        exch dup length string cvs print % key\n        (: ) print\n        ==\n    } forall\n} def\n\n"
  },
  {
    "path": "impls/ps/reader.ps",
    "content": "% requires types.ps to be included first\n\n/token_delim (;,\"` \\n{}\\(\\)[]) def\n/token_number (0123456789-) def\n\n% read_number: read a single number from string/idx\n% string idx -> read_number -> number string new_idx\n/read_number { 5 dict begin\n    %(in read_number\\n) print\n    /idx exch def\n    /str exch def\n    /start idx def\n    /cnt 0 def\n    { % loop\n        idx str length ge { exit } if % EOF, break loop\n        /ch str idx get def  % current character\n        ch 48 ge ch 57 le and 45 ch eq or { %if number\n            /cnt cnt 1 add def\n        }{ % else\n            exit\n        } ifelse\n        /idx idx 1 add def % increment idx\n    } loop\n\n    str start cnt getinterval cvi % the matched number\n    str idx % return: number string new_idx\nend } def\n\n\n% read_symbol: read a single symbol from string/idx\n% string idx -> read_symbol -> name string new_idx\n/read_symbol { 5 dict begin\n    %(in read_symbol\\n) print\n    /idx exch def\n    /str exch def\n    /start idx def\n    /cnt 0 def\n    { % loop\n        idx str length ge { exit } if % EOF, break loop\n        /ch str idx 1 getinterval def\n        token_delim ch search { % if token delimeter\n            pop pop pop exit\n        }{ % else not a delim\n            pop\n            /cnt cnt 1 add def\n        } ifelse\n        /idx idx 1 add def % increment idx\n    } loop\n\n    str start cnt getinterval cvn % the matched symbol\n    str idx % return: symbol string new_idx\nend } def\n\n\n% read_keyword: read a single keyword from string/idx\n% string idx -> read_keyword -> name string new_idx\n/read_keyword { 5 dict begin\n    %(in read_keyword\\n) print\n    /idx exch def\n    /str exch def\n    /start idx def\n    /cnt 0 def\n    { % loop\n        idx str length ge { exit } if % EOF, break loop\n        /ch str idx 1 getinterval def\n        token_delim ch search { % if token delimeter\n            pop pop pop exit\n        }{ % else not a delim\n            pop\n            /cnt cnt 1 add def\n        } ifelse\n        /idx idx 1 add def % increment idx\n    } loop\n\n    str start cnt getinterval % the matched keyword string\n    dup 0 127 put % TODO: something like (\\x029e) would be better\n    str idx % return: keyword string new_idx\nend } def\n\n\n% read_string: read a single string from string/idx\n% string idx -> read_string -> new_string string new_idx\n/read_string { 5 dict begin\n    %(in read_string\\n) print\n    /idx exch 1 add def\n    /str exch def\n    /start idx def\n    /cnt 0 def\n    { % loop\n        idx str length ge { %if EOF\n            (unexpected EOF reading string) _throw\n        } if\n        /ch str idx get def  % current character\n        /idx idx 1 add def\n        ch 92 eq { % if \\\n            /idx idx 1 add def\n            /cnt cnt 1 add def % 1 more below\n        } if\n        ch 34 eq { exit } if % '\"' is end of string\n        /cnt cnt 1 add def\n    } loop\n    str start cnt getinterval % the matched string\n    (\\\\\\\\) (\\177) replace\n    (\\\\\")  (\") replace\n    (\\\\n)  (\\n) replace\n    (\\177) (\\\\) replace\n    str idx % return: new_string string new_idx\nend } def\n\n\n% read_atom: read a single atom from string/idx\n% string idx -> read_atom -> int string new_idx\n/read_atom { 4 dict begin\n    %(in read_atom\\n) print\n    /idx exch def\n    /str exch def\n    str length idx le { % ifelse\n        exit % EOF\n    }{\n        /ch str idx get def  % current character\n        str length idx 1 add le {\n            /nextch 0 def\n        }{\n            /nextch str idx 1 add get def  % next character\n        } ifelse\n        ch 48 ge ch 57 le and { %if digit (number)\n            str idx read_number\n        }{ ch 45 eq nextch 48 ge nextch 57 le and and { %elseif minus and digit\n            str idx read_number\n        }{ ch 34 eq { %elseif double-quote (string)\n            str idx read_string\n        }{ ch 58 eq { %elseif colon (keyword)\n            str idx read_keyword\n        }{\n            str idx read_symbol\n            /idx exch def pop\n            dup /nil eq { %if nil\n                pop null str idx\n            }{ dup /true eq { %elseif true\n                pop true str idx\n            }{ dup /false eq { %elseif false\n                pop false str idx\n            }{ %else\n                str idx % return the original symbol/name\n            } ifelse } ifelse } ifelse\n        } ifelse } ifelse } ifelse } ifelse\n    }ifelse\n\n    % return: atom string new_idx\nend } def\n\n% read_until: read a list from string/idx until stopchar is found\n% string idx stopchar -> read_until -> list string new_idx\n/read_until { 3 dict begin\n    %(in read_until\\n) print\n    /stopchar exch def\n    /idx exch 1 add def\n    /str exch def\n    [\n    { % loop\n        str idx read_spaces /idx exch def pop\n        str length idx le { %if EOF\n            (unexpected EOF reading list) _throw\n        } if\n        /ch str idx get def  % current character\n        ch stopchar eq { exit } if % stop at stopchar\n        str idx read_form /idx exch def pop\n    } loop\n    ]\n    str idx 1 add\nend } def\n\n% read_spaces: advance idx to the first non-whitespace\n% string idx -> read_form -> string new_idx\n/read_spaces { 3 dict begin\n    %(in read_spaces\\n) print\n    /idx exch def\n    /str exch def\n    { % loop\n        str length idx le { exit } if % EOF, break loop\n        /ch str idx get def  % current character\n        %(left1.1:) print str idx str length idx sub getinterval print (\\n) print\n        % eliminate comments\n        ch 59 eq { %if ';'\n            { % loop\n                /idx idx 1 add def % increment idx\n                str length idx le { exit } if % EOF, break loop\n                /ch str idx get def  % current character\n                %(left1.2:) print str idx str length idx sub getinterval print (\\n) print\n                % if newline then we are done\n                ch 10 eq { exit } if\n            } loop\n            /idx idx 1 add def\n            str length idx le { exit } if % EOF, break loop\n            /ch str idx get def  % current character\n        } if\n        % if not whitespace then exit\n        ch 32 ne ch 10 ne ch 44 ne ch 59 ne and and and { exit } if\n        /idx idx 1 add def % increment idx\n    } loop\n\n    %(left1.3:) print str idx str length idx sub getinterval print (\\n) print\n    str idx % return: string new_idx\nend } def\n\n% read_form: read the next form from string start at idx\n% string idx -> read_form -> ast string new_idx\n/read_form { 3 dict begin\n    %(in read_form\\n) print\n    read_spaces\n    /idx exch def\n    /str exch def\n\n    %idx str length ge { (unexpected EOF) _throw } if % EOF\n    idx str length ge { null str idx }{ %if EOF\n\n    /ch str idx get def  % current character\n    %(LEFT2.1:) print str idx str length idx sub getinterval print (\\n) print\n    ch 39 eq { %if '\\''\n        /idx idx 1 add def\n        str idx read_form\n        3 -1 roll   /quote exch 2 _list   3 1 roll\n    }{ ch 96 eq { %if '`'\n        /idx idx 1 add def\n        str idx read_form\n        3 -1 roll   /quasiquote exch 2 _list   3 1 roll\n    }{ ch 126 eq { %if '~'\n        /idx idx 1 add def\n        /ch str idx get def  % current character\n        ch 64 eq { %if '~@'\n            /idx idx 1 add def\n            str idx read_form\n            3 -1 roll   /splice-unquote exch 2 _list   3 1 roll\n        }{ %else just '~'\n            str idx read_form\n            3 -1 roll   /unquote exch 2 _list   3 1 roll\n        } ifelse\n    }{ ch 94 eq { %if '^'\n        /idx idx 1 add def\n        str idx read_form read_form % stack: meta form str idx\n        4 2 roll exch   /with-meta 3 1 roll 3 _list   3 1 roll\n    }{ ch 64 eq { %if '@'\n        /idx idx 1 add def\n        str idx read_form\n        3 -1 roll   /deref exch 2 _list   3 1 roll\n    }{ ch 40 eq { %if '('\n        str idx 41 read_until dup /idx exch def\n        %(LEFT2.2:) print str idx str length idx sub getinterval print (\\n) print\n        3 -1 roll   _list_from_array   3 1 roll\n        %(LEFT2.3:) print str idx str length idx sub getinterval print (\\n) print\n    }{ ch 41 eq { %elseif ')'\n        (unexpected '\\)') _throw\n    }{ ch 91 eq { %if '['\n        str idx 93 read_until dup /idx exch def\n        %(LEFT2.4:) print str idx str length idx sub getinterval print (\\n) print\n        3 -1 roll   _vector_from_array   3 1 roll\n    }{ ch 93 eq { %elseif ']'\n        (unexpected ']') _throw\n    }{ ch 123 eq { %elseif '{'\n        str idx 125 read_until dup /idx exch def\n        3 -1 roll   _hash_map_from_array   3 1 roll\n    }{ ch 125 eq { %elseif '}'\n        (unexpected '}') _throw\n    }{ % else\n        str idx read_atom\n    } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse\n\n    } ifelse % not EOF\n\n    % return: ast string new_idx\nend } def\n\n% string -> read_str -> ast\n/read_str {\n    %(in read_str\\n) print\n    0  % current index into the string\n    read_form\n\n    pop pop  % drop the string, idx. return: ast\n} def\n"
  },
  {
    "path": "impls/ps/run",
    "content": "#!/usr/bin/env bash\nexec gs -q -I$(dirname $0) -dNOSAFER -dNODISPLAY -- $(dirname $0)/${STEP:-stepA_mal}.ps \"${@}\"\n"
  },
  {
    "path": "impls/ps/step0_repl.ps",
    "content": "% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    % just \"return\" the input string\n    /str exch def\n    str\n} def\n\n\n% eval\n/EVAL {\n    % just \"return\" the \"ast\"\n    /env exch def\n    /ast exch def\n    ast\n} def\n\n\n% print\n/PRINT {\n    % just \"return\" the expression\n    /exp exch def\n    exp\n} def\n\n\n% repl\n/REP { READ (stub env) EVAL PRINT } def\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    REP print (\\n) print\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step1_read_print.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n/EVAL { 2 dict begin\n    % just \"return\" the \"ast\"\n    /env exch def\n    /ast exch def\n    ast\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/REP { READ (stub env) EVAL PRINT } def\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step2_eval.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n/EVAL { 3 dict begin\n    /env exch def\n    /ast exch def\n\n    %(eval_ast: ) print ast ==\n\n    ast _symbol? { %if symbol\n        env ast known {\n            env ast get\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    }{\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n    }{\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n    }{\n\n    ast _list? not {\n        ast\n    }{\n\n    ast _count 0 eq {\n        ast\n    }{\n\n        /a0 ast 0 _nth def\n\n            a0 env EVAL\n\n            [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n            exec % apply function to args\n\n    } ifelse } ifelse } ifelse } ifelse } ifelse\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env <<\n    (+) { dup 0 _nth exch 1 _nth add }\n    (-) { dup 0 _nth exch 1 _nth sub }\n    (*) { dup 0 _nth exch 1 _nth mul }\n    (/) { dup 0 _nth exch 1 _nth idiv }\n>> def\n\n/REP { READ repl_env EVAL PRINT } def\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step3_env.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n/EVAL { 7 dict begin\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        not {\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } if\n    }{\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n    }{\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n    }{\n\n    ast _list? not {\n        ast\n    }{\n\n    ast _count 0 eq {\n        ast\n    }{\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n        }{\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2 let_env EVAL\n        }{\n\n            a0 env EVAL\n\n            [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n            exec % apply function to args\n\n        } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/REP { READ repl_env EVAL PRINT } def\n\nrepl_env (+) { dup 0 _nth exch 1 _nth add } env_set\nrepl_env (-) { dup 0 _nth exch 1 _nth sub } env_set\nrepl_env (*) { dup 0 _nth exch 1 _nth mul } env_set\nrepl_env (/) { dup 0 _nth exch 1 _nth idiv } env_set\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step4_if_fn_do.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n/EVAL { 7 dict begin\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        not {\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } if\n    }{\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n    }{\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n    }{\n\n    ast _list? not {\n        ast\n    }{\n\n    ast _count 0 eq {\n        ast\n    }{\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n        }{\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            EVAL\n        }{\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            EVAL\n        }{\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    EVAL\n                }{ % else false branch with no a3\n                    null\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                EVAL\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n        }{\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n              fload % stack: ast new_env\n              EVAL\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n            }{\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\n\n% core.mal: defined using the language itself\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step5_tco.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n/EVAL { 7 dict begin\n    { %loop (TCO)\n\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        {\n            exit\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    } if\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n        exit\n    } if\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n        exit\n    } if\n\n    ast _list? not {\n        ast\n        exit\n    } if\n\n    ast _count 0 eq {\n        ast\n        exit\n    } if\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            % loop\n        }{\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            % loop\n        }{\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    % loop\n                }{ % else false branch with no a3\n                    null\n                    exit\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                % loop\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n            exit\n        } if\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n              fload % stack: ast new_env\n              % loop\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n                exit\n            } if\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse\n    } loop % TCO\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\n\n% core.mal: defined using the language itself\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step6_file.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n/EVAL { 7 dict begin\n    { %loop (TCO)\n\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        {\n            exit\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    } if\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n        exit\n    } if\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n        exit\n    } if\n\n    ast _list? not {\n        ast\n        exit\n    } if\n\n    ast _count 0 eq {\n        ast\n        exit\n    } if\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            % loop\n        }{\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            % loop\n        }{\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    % loop\n                }{ % else false branch with no a3\n                    null\n                    exit\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                % loop\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n            exit\n        } if\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n              fload % stack: ast new_env\n              % loop\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n                exit\n            } if\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse\n    } loop % TCO\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\nrepl_env    (eval)    { 0 _nth repl_env EVAL } _function    env_set\nrepl_env    (*ARGV*)    [ ] _list_from_array    env_set\n\n% core.mal: defined using the language itself\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n(\\(def! load-file \\(fn* \\(f\\) \\(eval \\(read-string \\(str \"\\(do \" \\(slurp f\\) \"\\nnil\\)\"\\)\\)\\)\\)\\)) RE pop\n\nuserdict /ARGUMENTS known { %if command line arguments\n    ARGUMENTS length 0 gt { %if more than 0 arguments\n        repl_env    (*ARGV*)    ARGUMENTS 1 ARGUMENTS length 1 sub getinterval\n        _list_from_array env_set\n        ARGUMENTS 0 get \n        (\\(load-file \") exch (\"\\)) concatenate concatenate RE pop\n        quit\n    } if\n} if\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step7_quote.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n% sym ast -> starts_with -> bool\n/starts_with {\n    dup _list? {\n        0 _nth\n        eq\n    }{\n        pop pop false\n    } ifelse\n} def\n\n% ast -> quasiquote -> new_ast\n/quasiquote { 3 dict begin\n    /ast exch def\n    ast _sequential? not {\n      ast _symbol? ast _hash_map? or {\n        /quote ast 2 _list\n      }{\n        ast\n      } ifelse\n    }{\n        /unquote ast starts_with {\n            ast 1 _nth\n        }{\n            /res 0 _list def\n            ast /data get aload length { % reverse traversal\n                /elt exch def\n                /res\n                /splice-unquote elt starts_with {\n                    /concat\n                    elt 1 _nth\n                }{\n                    /cons\n                    elt quasiquote\n                } ifelse\n                res\n                3 _list\n                def\n            } repeat\n            ast _list? {\n                res\n            }{\n                /vec res 2 _list\n            } ifelse\n        } ifelse\n    } ifelse\nend } def\n\n/EVAL { 7 dict begin\n    { %loop (TCO)\n\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        {\n            exit\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    } if\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n        exit\n    } if\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n        exit\n    } if\n\n    ast _list? not {\n        ast\n        exit\n    } if\n\n    ast _count 0 eq {\n        ast\n        exit\n    } if\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            % loop\n        }{\n\n        /quote a0 eq { %if quote\n            ast 1 _nth\n            exit\n        } if\n\n        /quasiquote a0 eq { %if quasiquote\n            ast 1 _nth quasiquote\n            env\n            % loop\n        }{\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            % loop\n        }{\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    % loop\n                }{ % else false branch with no a3\n                    null\n                    exit\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                % loop\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n            exit\n        } if\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n              fload % stack: ast new_env\n              % loop\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n                exit\n            } if\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse } ifelse\n    } loop % TCO\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\nrepl_env    (eval)    { 0 _nth repl_env EVAL } _function    env_set\nrepl_env    (*ARGV*)    [ ] _list_from_array    env_set\n\n% core.mal: defined using the language itself\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n(\\(def! load-file \\(fn* \\(f\\) \\(eval \\(read-string \\(str \"\\(do \" \\(slurp f\\) \"\\nnil\\)\"\\)\\)\\)\\)\\)) RE pop\n\nuserdict /ARGUMENTS known { %if command line arguments\n    ARGUMENTS length 0 gt { %if more than 0 arguments\n        repl_env    (*ARGV*)    ARGUMENTS 1 ARGUMENTS length 1 sub getinterval\n        _list_from_array env_set\n        ARGUMENTS 0 get \n        (\\(load-file \") exch (\"\\)) concatenate concatenate RE pop\n        quit\n    } if\n} if\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step8_macros.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n% sym ast -> starts_with -> bool\n/starts_with {\n    dup _list? {\n        0 _nth\n        eq\n    }{\n        pop pop false\n    } ifelse\n} def\n\n% ast -> quasiquote -> new_ast\n/quasiquote { 3 dict begin\n    /ast exch def\n    ast _sequential? not {\n      ast _symbol? ast _hash_map? or {\n        /quote ast 2 _list\n      }{\n        ast\n      } ifelse\n    }{\n        /unquote ast starts_with {\n            ast 1 _nth\n        }{\n            /res 0 _list def\n            ast /data get aload length { % reverse traversal\n                /elt exch def\n                /res\n                /splice-unquote elt starts_with {\n                    /concat\n                    elt 1 _nth\n                }{\n                    /cons\n                    elt quasiquote\n                } ifelse\n                res\n                3 _list\n                def\n            } repeat\n            ast _list? {\n                res\n            }{\n                /vec res 2 _list\n            } ifelse\n        } ifelse\n    } ifelse\nend } def\n\n/EVAL { 7 dict begin\n    { %loop (TCO)\n\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        {\n            exit\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    } if\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n        exit\n    } if\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n        exit\n    } if\n\n    ast _list? not {\n        ast\n        exit\n    } if\n\n    ast _count 0 eq {\n        ast\n        exit\n    } if\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            % loop\n        }{\n\n        /quote a0 eq { %if quote\n            ast 1 _nth\n            exit\n        } if\n\n        /quasiquote a0 eq { %if quasiquote\n            ast 1 _nth quasiquote\n            env\n            % loop\n        }{\n\n        /defmacro! a0 eq { %if defmacro!\n            ast 2 _nth    env EVAL    _macro\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            % loop\n        }{\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    % loop\n                }{ % else false branch with no a3\n                    null\n                    exit\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                % loop\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n            exit\n        } if\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              dup /macro? get true eq {\n                ast _rest exch % stack: args macro\n                fload          % stack: args new_env\n                EVAL           % stack: new_ast\n                env            % stack: new_ast env\n                % loop\n              }{\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                fload % stack: ast new_env\n                % loop\n              } ifelse\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n                exit\n            } if\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse } ifelse\n    } loop % TCO\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\nrepl_env    (eval)    { 0 _nth repl_env EVAL } _function    env_set\nrepl_env    (*ARGV*)    [ ] _list_from_array    env_set\n\n% core.mal: defined using the language itself\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n(\\(def! load-file \\(fn* \\(f\\) \\(eval \\(read-string \\(str \"\\(do \" \\(slurp f\\) \"\\nnil\\)\"\\)\\)\\)\\)\\)) RE pop\n(\\(defmacro! cond \\(fn* \\(& xs\\) \\(if \\(> \\(count xs\\) 0\\) \\(list 'if \\(first xs\\) \\(if \\(> \\(count xs\\) 1\\) \\(nth xs 1\\) \\(throw \"odd number of forms to cond\"\\)\\) \\(cons 'cond \\(rest \\(rest xs\\)\\)\\)\\)\\)\\)\\)) RE pop\n\nuserdict /ARGUMENTS known { %if command line arguments\n    ARGUMENTS length 0 gt { %if more than 0 arguments\n        repl_env    (*ARGV*)    ARGUMENTS 1 ARGUMENTS length 1 sub getinterval\n        _list_from_array env_set\n        ARGUMENTS 0 get \n        (\\(load-file \") exch (\"\\)) concatenate concatenate RE pop\n        quit\n    } if\n} if\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/step9_try.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n% sym ast -> starts_with -> bool\n/starts_with {\n    dup _list? {\n        0 _nth\n        eq\n    }{\n        pop pop false\n    } ifelse\n} def\n\n% ast -> quasiquote -> new_ast\n/quasiquote { 3 dict begin\n    /ast exch def\n    ast _sequential? not {\n      ast _symbol? ast _hash_map? or {\n        /quote ast 2 _list\n      }{\n        ast\n      } ifelse\n    }{\n        /unquote ast starts_with {\n            ast 1 _nth\n        }{\n            /res 0 _list def\n            ast /data get aload length { % reverse traversal\n                /elt exch def\n                /res\n                /splice-unquote elt starts_with {\n                    /concat\n                    elt 1 _nth\n                }{\n                    /cons\n                    elt quasiquote\n                } ifelse\n                res\n                3 _list\n                def\n            } repeat\n            ast _list? {\n                res\n            }{\n                /vec res 2 _list\n            } ifelse\n        } ifelse\n    } ifelse\nend } def\n\n/EVAL { 7 dict begin\n    { %loop (TCO)\n\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        {\n            exit\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    } if\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n        exit\n    } if\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n        exit\n    } if\n\n    ast _list? not {\n        ast\n        exit\n    } if\n\n    ast _count 0 eq {\n        ast\n        exit\n    } if\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            % loop\n        }{\n\n        /quote a0 eq { %if quote\n            ast 1 _nth\n            exit\n        } if\n\n        /quasiquote a0 eq { %if quasiquote\n            ast 1 _nth quasiquote\n            env\n            % loop\n        }{\n\n        /defmacro! a0 eq { %if defmacro!\n            ast 2 _nth    env EVAL    _macro\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            % loop\n        }{\n\n        /try* a0 eq { %if try*\n            ast _count 2 gt { %if has catch* block\n                { %try\n\t\t    2 dict begin % special dict for dict stack count\n                    countdictstack /dictcnt exch def\n                    count /stackcnt exch def\n                    ast 1 _nth env EVAL\n\t\t    end\n                } stopped { %catch\n                    % clean up the dictionary stack\n                    1 1 countdictstack dictcnt sub { %foreach added dict\n                        %(popping dict\\n) print\n                        pop end % pop idx and pop dict\n                        %(new ast: ) print ast true _pr_str print (\\n) print\n                    } for\n                    % clean up the operand stack\n                    count 1 exch 1 exch stackcnt sub { %foreach added operand\n                        %(op stack: ) print pstack\n                        pop pop % pop idx and operand\n                        %(popped op stack\\n) print pstack\n                    } for\n\t\t    end % remove special dict\n                    % get error data and reset $error dict\n                    /errdata get_error_data def\n                    $error /newerror false put\n                    $error /errorinfo null put\n\n                    ast _count 3 lt { %if no third (catch*) form\n                        errdata throw\n                    } if\n                    ast 2 _nth 0 _nth (catch*) eq not { %if third form not catch*\n                        (No catch* in throw form) _throw\n                    } if\n                    ast 2 _nth 2 _nth\n                    env\n                        ast 2 _nth 1 _nth 1 _list\n                        errdata 1 _list\n                        env_new\n                    EVAL\n                } if\n            }{ % else no catch* block\n                ast 1 _nth env EVAL\n            } ifelse\n            exit\n        } if\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    % loop\n                }{ % else false branch with no a3\n                    null\n                    exit\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                % loop\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n            exit\n        } if\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              dup /macro? get true eq {\n                ast _rest exch % stack: args macro\n                fload          % stack: args new_env\n                EVAL           % stack: new_ast\n                env            % stack: new_ast env\n                % loop\n              }{\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                fload % stack: ast new_env\n                % loop\n              } ifelse\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n                exit\n            } if\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse } ifelse\n    } loop % TCO\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\nrepl_env    (eval)    { 0 _nth repl_env EVAL } _function    env_set\nrepl_env    (*ARGV*)    [ ] _list_from_array    env_set\n\n% core.mal: defined using the language itself\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n(\\(def! load-file \\(fn* \\(f\\) \\(eval \\(read-string \\(str \"\\(do \" \\(slurp f\\) \"\\nnil\\)\"\\)\\)\\)\\)\\)) RE pop\n(\\(defmacro! cond \\(fn* \\(& xs\\) \\(if \\(> \\(count xs\\) 0\\) \\(list 'if \\(first xs\\) \\(if \\(> \\(count xs\\) 1\\) \\(nth xs 1\\) \\(throw \"odd number of forms to cond\"\\)\\) \\(cons 'cond \\(rest \\(rest xs\\)\\)\\)\\)\\)\\)\\)) RE pop\n\nuserdict /ARGUMENTS known { %if command line arguments\n    ARGUMENTS length 0 gt { %if more than 0 arguments\n        repl_env    (*ARGV*)    ARGUMENTS 1 ARGUMENTS length 1 sub getinterval\n        _list_from_array env_set\n        ARGUMENTS 0 get \n        (\\(load-file \") exch (\"\\)) concatenate concatenate RE pop\n        quit\n    } if\n} if\n\n% repl loop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/stepA_mal.ps",
    "content": "/runlibfile where { pop }{ /runlibfile { run } def } ifelse % \n(types.ps) runlibfile\n(reader.ps) runlibfile\n(printer.ps) runlibfile\n(env.ps) runlibfile\n(core.ps) runlibfile\n\n% read\n/_readline { print flush (%stdin) (r) file 1024 string readline } def\n\n/READ {\n    /str exch def\n    str read_str\n} def\n\n\n% eval\n% sym ast -> starts_with -> bool\n/starts_with {\n    dup _list? {\n        0 _nth\n        eq\n    }{\n        pop pop false\n    } ifelse\n} def\n\n% ast -> quasiquote -> new_ast\n/quasiquote { 3 dict begin\n    /ast exch def\n    ast _sequential? not {\n      ast _symbol? ast _hash_map? or {\n        /quote ast 2 _list\n      }{\n        ast\n      } ifelse\n    }{\n        /unquote ast starts_with {\n            ast 1 _nth\n        }{\n            /res 0 _list def\n            ast /data get aload length { % reverse traversal\n                /elt exch def\n                /res\n                /splice-unquote elt starts_with {\n                    /concat\n                    elt 1 _nth\n                }{\n                    /cons\n                    elt quasiquote\n                } ifelse\n                res\n                3 _list\n                def\n            } repeat\n            ast _list? {\n                res\n            }{\n                /vec res 2 _list\n            } ifelse\n        } ifelse\n    } ifelse\nend } def\n\n/EVAL { 7 dict begin\n    { %loop (TCO)\n\n    /env exch def\n    /ast exch def\n\n    env (DEBUG-EVAL) env_get {\n        dup null ne    exch false ne    and {\n            (EVAL: ) print\n            ast true _pr_str print\n            (\\n) print\n        } if\n    } if\n\n    ast _symbol? { %if symbol\n        env ast env_get\n        {\n            exit\n        }{\n            (') ast\n            dup length string cvs\n            (' not found)\n            concatenate concatenate\n            _throw\n        } ifelse\n    } if\n\n    ast _vector? {\n        [\n            ast /data get { %forall items\n                env EVAL\n            } forall\n        ] _vector_from_array\n        exit\n    } if\n\n    ast _hash_map? {\n        <<\n            ast /data get { %forall entries\n                env EVAL\n            } forall\n        >> _hash_map_from_dict\n        exit\n    } if\n\n    ast _list? not {\n        ast\n        exit\n    } if\n\n    ast _count 0 eq {\n        ast\n        exit\n    } if\n\n        /a0 ast 0 _nth def\n\n        /def! a0 eq { %if def!\n            ast 2 _nth    env EVAL\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /let* a0 eq { %if let*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            /let_env env null null env_new def\n            0 2 a1 _count 1 sub { %for each pair\n                /idx exch def\n                let_env\n                    a1 idx _nth\n                    a1 idx 1 add _nth let_env EVAL\n                    env_set\n            } for\n            a2\n            let_env\n            % loop\n        }{\n\n        /quote a0 eq { %if quote\n            ast 1 _nth\n            exit\n        } if\n\n        /quasiquote a0 eq { %if quasiquote\n            ast 1 _nth quasiquote\n            env\n            % loop\n        }{\n\n        /defmacro! a0 eq { %if defmacro!\n            ast 2 _nth    env EVAL    _macro\n            env    ast 1 _nth    2 index    env_set\n            exit\n        } if\n\n        /ps* a0 eq { %if ps*\n            count /stackcnt exch def\n            ast 1 _nth cvx exec\n            count stackcnt gt { % if new operands on stack\n                % return an list of new operands\n                count stackcnt sub array astore\n            }{\n                null % return nil\n            } ifelse\n            exit\n        } if\n\n        /do a0 eq { %if do\n            ast _count 2 gt { %if ast has more than 2 elements\n                ast 1 ast _count 2 sub _slice /data get { env EVAL pop } forall\n            } if\n            ast ast _count 1 sub _nth % last ast becomes new ast\n            env\n            % loop\n        }{\n\n        /try* a0 eq { %if try*\n            ast _count 2 gt { %if has catch* block\n                { %try\n\t\t    2 dict begin % special dict for dict stack count\n                    countdictstack /dictcnt exch def\n                    count /stackcnt exch def\n                    ast 1 _nth env EVAL\n\t\t    end\n                } stopped { %catch\n                    % clean up the dictionary stack\n                    1 1 countdictstack dictcnt sub { %foreach added dict\n                        %(popping dict\\n) print\n                        pop end % pop idx and pop dict\n                        %(new ast: ) print ast true _pr_str print (\\n) print\n                    } for\n                    % clean up the operand stack\n                    count 1 exch 1 exch stackcnt sub { %foreach added operand\n                        %(op stack: ) print pstack\n                        pop pop % pop idx and operand\n                        %(popped op stack\\n) print pstack\n                    } for\n\t\t    end % remove special dict\n                    % get error data and reset $error dict\n                    /errdata get_error_data def\n                    $error /newerror false put\n                    $error /errorinfo null put\n\n                    ast _count 3 lt { %if no third (catch*) form\n                        errdata throw\n                    } if\n                    ast 2 _nth 0 _nth (catch*) eq not { %if third form not catch*\n                        (No catch* in throw form) _throw\n                    } if\n                    ast 2 _nth 2 _nth\n                    env\n                        ast 2 _nth 1 _nth 1 _list\n                        errdata 1 _list\n                        env_new\n                    EVAL\n                } if\n            }{ % else no catch* block\n                ast 1 _nth env EVAL\n            } ifelse\n            exit\n        } if\n\n        /if a0 eq { %if if\n            /a1 ast 1 _nth def\n            /cond a1 env EVAL def\n            cond null eq cond false eq or { % if cond is nil or false\n                ast _count 3 gt { %if false branch with a3\n                    ast 3 _nth env\n                    % loop\n                }{ % else false branch with no a3\n                    null\n                    exit\n                } ifelse\n            }{ % true branch\n                ast 2 _nth env\n                % loop\n            } ifelse\n        }{\n\n        /fn* a0 eq { %if fn*\n            /a1 ast 1 _nth def\n            /a2 ast 2 _nth def\n            a2 env a1 _mal_function\n            exit\n        } if\n\n            a0 env EVAL\n\n            dup _mal_function? { %if user defined function\n              dup /macro? get true eq {\n                ast _rest exch % stack: args macro\n                fload          % stack: args new_env\n                EVAL           % stack: new_ast\n                env            % stack: new_ast env\n                % loop\n              }{\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                fload % stack: ast new_env\n                % loop\n              } ifelse\n            }{\n\n            dup _function? { %else if builtin function\n                [ ast _rest /data get { env EVAL } forall ] _list_from_array exch\n                /data get exec\n                exit\n            } if\n\n            %else (regular procedure/function)\n            (cannot apply native proc!\\n) print quit\n\n            } ifelse } ifelse } ifelse } ifelse } ifelse\n    } loop % TCO\nend } def\n\n\n% print\n/PRINT {\n    true _pr_str\n} def\n\n\n% repl\n/repl_env null null null env_new def\n\n/RE { READ repl_env EVAL } def\n/REP { READ repl_env EVAL PRINT } def\n\n% core.ps: defined using postscript\ncore_ns { _function    repl_env 3 1 roll env_set } forall\nrepl_env    (eval)    { 0 _nth repl_env EVAL } _function    env_set\nrepl_env    (*ARGV*)    [ ] _list_from_array    env_set\n\n% core.mal: defined using the language itself\n(\\(def! *host-language* \"postscript\"\\)) RE pop\n(\\(def! not \\(fn* \\(a\\) \\(if a false true\\)\\)\\)) RE pop\n(\\(def! load-file \\(fn* \\(f\\) \\(eval \\(read-string \\(str \"\\(do \" \\(slurp f\\) \"\\nnil\\)\"\\)\\)\\)\\)\\)) RE pop\n(\\(defmacro! cond \\(fn* \\(& xs\\) \\(if \\(> \\(count xs\\) 0\\) \\(list 'if \\(first xs\\) \\(if \\(> \\(count xs\\) 1\\) \\(nth xs 1\\) \\(throw \"odd number of forms to cond\"\\)\\) \\(cons 'cond \\(rest \\(rest xs\\)\\)\\)\\)\\)\\)\\)) RE pop\n\nuserdict /ARGUMENTS known { %if command line arguments\n    ARGUMENTS length 0 gt { %if more than 0 arguments\n        repl_env    (*ARGV*)    ARGUMENTS 1 ARGUMENTS length 1 sub getinterval\n        _list_from_array env_set\n        ARGUMENTS 0 get \n        (\\(load-file \") exch (\"\\)) concatenate concatenate RE pop\n        quit\n    } if\n} if\n\n% repl loop\n(\\(println \\(str \"Mal [\" *host-language* \"]\"\\)\\)) RE pop\n{ %loop\n    (user> ) _readline\n    not { exit } if  % exit if EOF\n\n    { %try\n        REP print (\\n) print\n    } stopped {\n        (Error: ) print\n        get_error_data false _pr_str print (\\n) print\n        $error /newerror false put\n        $error /errorinfo null put\n        clear\n        cleardictstack\n    } if\n} bind loop\n\n(\\n) print  % final newline before exit for cleanliness\nquit\n"
  },
  {
    "path": "impls/ps/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/ps/tests/stepA_mal.mal",
    "content": ";; Testing basic ps interop\n\n(ps* \"7\")\n;=>(7)\n\n(ps* \"(7)\")\n;=>(\"7\")\n\n(ps* \"7 8 9 3 array astore\")\n;=>((7 8 9))\n\n(ps* \"1 1 eq\")\n;=>(true)\n\n(ps* \"/sym\")\n;=>(sym)\n\n(ps* \"1 1 eq { (yep) }{ (nope) } ifelse\")\n;=>(\"yep\")\n\n(ps* \"1 0 eq { (yep) }{ (nope) } ifelse\")\n;=>(\"nope\")\n\n(ps* \"1 2 3 pop pop pop\")\n;=>nil\n"
  },
  {
    "path": "impls/ps/types.ps",
    "content": "% General functions\n\n% concatenate: concatenate two strings or two arrays\n% From Thinking in PostScript 1990 Reid, Example 11.7\n% (string1) (string2) concatenate string3\n% array1 array2 concatenate array3\n/concatenate { %def\n    dup type 2 index type 2 copy ne { %if\n        pop pop\n        errordict begin (concatenate) typecheck end\n    }{ %else\n        /stringtype ne exch /arraytype ne and {\n            errordict begin (concatenate) typecheck end\n        } if\n    } ifelse\n    dup length 2 index length add 1 index type\n    /arraytype eq { array }{ string } ifelse\n    % stack: arg1 arg2 new\n    dup 0 4 index putinterval\n    % stack: arg1 arg2 new\n    dup 4 -1 roll length 4 -1 roll putinterval\n    % stack: new\n} bind def\n\n% reverse: array1 -> reverse -> array2\n/reverse {\n    [ exch\n    aload % push array onto stack\n    length -1 0 { 1 roll } for % reverse\n    ]\n} bind def\n\n% string1 string2 string3 -> replace -> string4\n% Return a string4 with all occurrences of string2 in string1 replaced\n% with string3\n/replace { 4 dict begin\n    /repstr exch def\n    /needle exch def\n    /haystack exch def\n    /result () def\n    { % loop\n        haystack needle search\n        { %if found\n            % stack: post match pre\n            repstr concatenate 3 1 roll pop % stack: pre+ post\n            /haystack exch def % stack: pre+\n            result exch concatenate /result exch def\n        }{\n            result exch concatenate /result exch def\n            exit\n        } ifelse\n    } loop\n    result\nend } def\n\n\n% objA objB -> _equal? -> bool\n/_equal? { 6 dict begin\n    /b exch def\n    /a exch def\n\n    a type b type eq\n    a _sequential? b _sequential? and\n    or not { %if type mismatch and not sequential\n        false\n    }{\n        a _sequential? b _sequential? and { %if list/vector\n            /ret true def\n            a _count b _count eq not { %if length mismatch\n                /ret false def\n            }{ %else (length is the same)\n                0 1 a _count 1 sub {\n                    /idx exch def\n                    a idx _nth b idx _nth _equal? not { %if not items _equal?\n                        /ret false def\n                        exit\n                    } if\n                } for\n            } ifelse\n            ret\n        }{ %else not list/vector\n            a _hash_map? b _hash_map? and { %if hash_map\n                /ret true def\n                /a_keys a _keys def\n                a_keys _count b _keys _count eq not {\n                    /ret false def\n                }{\n                    a_keys /data get { %foreach key in a_keys\n                        /key exch def\n                        a key _hash_map_get b key _hash_map_get _equal? not { %if not items _equal?\n                            /ret false def\n                            exit\n                        } if\n                    } forall\n                } ifelse\n                ret\n            }{ %else not hash_map\n                a b eq\n            } ifelse\n        } ifelse\n    } ifelse\nend } def\n\n\n% Low-level sequence operations\n\n/_sequential? { dup _list? exch _vector? or } def\n\n/_count { /data get length } def\n\n/_first {\n    /data get\n    dup length 0 gt { 0 get }{ pop null } ifelse\n} def\n\n% seq start count -> _slice -> new_seq\n/_slice {\n    3 -1 roll /data get 3 1 roll % stack: array start count\n    getinterval\n    _list_from_array\n} def\n\n% seq idx -> _nth -> ith_item\n/_nth {\n    exch /data get % stack: idx array\n    dup length 0 gt { exch get }{ pop pop null } ifelse\n} def\n\n% seq -> _rest -> rest_seq\n/_rest {\n    /data get\n    dup length 0 gt {\n        dup length 1 sub 1 exch getinterval\n    }{\n        pop 0 array\n    } ifelse\n    _list_from_array\n} def\n\n% hashmap -> _keys -> key_list\n/_keys {\n    /data get\n    [ exch { pop dup length string cvs } forall ]\n    _list_from_array\n} def\n\n% hashmap key -> _hash_map_get -> val\n/_hash_map_get {\n    exch % stack: key hashmap\n    /data get % stack: key dict\n    exch % stack: dict key\n    2 copy known { %if has key\n        get\n    }{\n        pop pop null\n    } ifelse\n} def\n\n\n% Errors/Exceptions\n\n% data -> _throw ->\n% Takes arbitrary data and puts it in $error:/errorinfo. Then calls\n% stop to transfer control to end of nearest stopped context.\n/_throw {\n    $error exch /errorinfo exch put\n    $error /command /throw put\n    stop\n} def\n\n/errorinfo? {\n    $error /errorinfo known { % if set\n        $error /errorinfo get null ne {\n            true\n        }{\n            false\n        } ifelse\n    }{\n        false\n    } ifelse\n} def\n\n/get_error_data {\n    errorinfo? { %if\n        $error /errorinfo get\n    }{\n        $error /errorname get 255 string cvs\n        (: ) \n        $error /command get 99 string cvs\n        ( at )\n        $error /position get 10 99 string cvrs\n        concatenate\n        concatenate\n        concatenate\n        concatenate\n    } ifelse\n} def\n\n\n% Scalars\n\n/_nil? { null eq } def\n/_true? { true eq } def\n/_false? { false eq } def\n/_string? {\n    dup type /stringtype eq {\n        dup length 0 eq { % if length == 0\n            pop true\n        }{\n            0 get 127 eq not\n        } ifelse\n    }{\n        pop false\n    } ifelse\n} def\n\n\n% Symbols\n\n/_symbol {\n    dup length string copy cvn\n} def\n\n/_symbol? {\n    type /nametype eq\n} def\n\n\n% Keywords\n\n/_keyword { 1 dict begin\n  dup _keyword? not {\n    /str exch def\n    str length 1 add string % str2\n    dup 1 str putinterval\n    dup 0 127 put % TODO: something like (\\x029e) would be better\n  } if\nend } def\n\n/_keyword? {\n    dup type /stringtype eq {\n        dup length 0 eq { % if length == 0\n            pop false\n        }{\n            0 get 127 eq\n        } ifelse\n    }{\n        pop false\n    } ifelse\n} def\n\n\n\n% Functions\n\n% block -> _function -> boxed_function\n/_function {\n    <<\n        /_maltype_ /function\n        %/data 5 -1 roll cvlit\n        /data 5 -1 roll\n    >>\n    %%dup length dict copy\n} def\n\n% ast env params -> _mal_function -> boxed_mal_function\n/_mal_function { \n    <<\n        /_maltype_ /mal_function % user defined function\n        /macro? false % macro flag, false by default\n        /params null % close over parameters\n        /ast null    % close over ast\n        /env null    % close over environment\n        /data { __self__ fload EVAL }  % forward reference to EVAL\n              dup length array copy cvx % actual copy/new instance of block\n    >>\n    % make an actual copy/new instance of dict\n    dup length dict copy % stack: ast env params mal_fn\n    % \"Close over\" parameters\n    dup 3 -1 roll     % stack: ast env mal_fn mal_fn params\n    /params exch put  % stack: ast env mal_fn\n    dup 3 -1 roll     % stack: ast mal_fn mal_fn env\n    /env exch put     % stack: ast mal_fn\n    dup 3 -1 roll     % stack: mal_fn mal_fn ast\n    /ast exch put     % stack: mal_fn\n\n    % insert self reference into position 0 of data\n    dup /data get     % stack: mal_fn data\n    1 index           % stack: mal_fn data mal_fn\n    0 exch            % stack: mal_fn data 0 mal_fn\n    put               % stack: mal_fn\n} def\n\n% fn -> _defmacro -> macro\n/_macro {\n    dup /ast get exch\n    dup /env get exch\n    /params get\n    _mal_function\n    dup\n    /macro? true put \n} def\n\n/_function? {\n    dup type /dicttype eq {\n        /_maltype_ get /function eq\n    }{\n        pop false\n    } ifelse\n} def\n\n/_mal_function? {\n    dup type /dicttype eq {\n        /_maltype_ get /mal_function eq\n    }{\n        pop false\n    } ifelse\n} def\n\n% args mal_function -> fload -> ast new_env\n% fload: sets up arguments on the stack for an EVAL call\n/fload {\n    dup /ast get 3 1 roll    % stack: ast args mal_function\n    dup /env get 3 1 roll    % stack: ast env args mal_function\n    /params get exch         % stack: ast env params args\n    env_new                  % stack: ast new_env\n} def\n\n% function_or_mal_function -> callable -> block\n% if this is a function or mal_function, get its executable block\n/callable {\n    dup _mal_function? { %if mal_function\n        /data get\n    }{ dup _function? { %else if function\n        /data get\n    }{ %else something invalid\n        (callable called on non-function!\\n) print quit\n        cvx\n    } ifelse } ifelse\n} def\n\n\n% Lists\n\n% array -> _list_from_array -> mal_list\n/_list_from_array {\n    <<\n        /data 3 -1 roll  % grab the array argument\n        /_maltype_ /list\n        /meta null\n    >>\n} def\n% elem... cnt -> _list -> mal_list\n/_list {\n    array astore _list_from_array\n} def\n/_list? {\n    dup type /dicttype eq {\n        /_maltype_ get /list eq\n    }{\n        pop false\n    } ifelse\n} def\n\n\n% Vectors\n\n% array -> _vector_from_array -> mal_vector\n/_vector_from_array {\n    <<\n        /data 3 -1 roll  % grab the array argument\n        /_maltype_ /vector\n        /meta null\n    >>\n} def\n% elem... cnt -> _vector -> mal_vector\n/_vector {\n    array astore _vector_from_array\n} def\n/_vector? {\n    dup type /dicttype eq {\n        /_maltype_ get /vector eq\n    }{\n        pop false\n    } ifelse\n} def\n\n\n% Hash Maps\n\n% dict -> _hash_map_from_dict -> mal_hash_map\n/_hash_map_from_dict {\n    <<\n        /data 3 -1 roll\n        /_maltype_ /hash_map\n        /meta null\n    >>\n} def\n% array -> _hash_map_from_array -> mal_hash_map\n/_hash_map_from_array {\n    <<\n        /data <<\n            4 -1 roll  % grab the array argument\n            aload pop  % unpack the array\n        >>\n        /_maltype_ /hash_map\n        /meta null\n    >>\n} def\n% elem... cnt -> _hash_map -> mal_hash_map\n/_hash_map {\n    array astore _hash_map_from_array\n} def\n/_hash_map? {\n    dup type /dicttype eq {\n        /_maltype_ get /hash_map eq\n    }{\n        pop false\n    } ifelse\n} def\n\n\n% Atoms\n\n% obj -> atom -> new_atom\n/_atom {\n    <<\n        /data 3 -1 roll\n        /_maltype_ /atom\n        /meta null\n    >>\n} def\n\n/_atom? {\n    dup type /dicttype eq {\n        /_maltype_ get /atom eq\n    }{\n        pop false\n    } ifelse\n} def\n\n\n\n% Sequence operations\n"
  },
  {
    "path": "impls/purs/.gitignore",
    "content": "/bower_components/\n/node_modules/\n/.pulp-cache/\n/output/\n/generated-docs/\n/.psc-package/\n/.psc*\n/.purs*\n/.psa*\n/.spago\n\n/step*.js\n"
  },
  {
    "path": "impls/purs/Dockerfile",
    "content": "FROM ubuntu:21.10\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# For building node modules\nRUN apt-get -y install g++\n\n# Add nodesource apt repo config for 10.x stable\nRUN apt-get -y install gnupg\nRUN curl -sL https://deb.nodesource.com/setup_12.x | bash -\n\n# Install nodejs\nRUN apt-get -y install nodejs\n\n# Install purescript and deps\nRUN apt-get install -y git libtinfo5\nRUN npm install -g --unsafe-perm purescript spago\n\nENV NPM_CONFIG_CACHE /mal/.npm\nENV HOME /mal"
  },
  {
    "path": "impls/purs/Makefile",
    "content": "BINS = step0_repl.js step1_read_print.js step2_eval.js step3_env.js \\\n       step4_if_fn_do.js step5_tco.js step6_file.js step7_quote.js \\\n       step8_macros.js step9_try.js stepA_mal.js\n\nOTHER_SRCS = src/Readline.js src/Readline.purs src/Types.purs src/Reader.purs \\\n             src/Printer.purs src/Env.purs src/Core.purs\n\n\nall: $(BINS)\n\n$(BINS): %.js: src/%.purs $(OTHER_SRCS) node_modules/readline-sync\n\tspago bundle-app --main $($(<:src/%=%)) --to $@\n\n\nnode_modules/readline-sync:\n\tnpm install\n\n\n#####################\n\nstep0_repl.purs = Mal.Step0\nstep1_read_print.purs = Mal.Step1\nstep2_eval.purs = Mal.Step2\nstep3_env.purs = Mal.Step3\nstep4_if_fn_do.purs = Mal.Step4\nstep5_tco.purs = Mal.Step5\nstep6_file.purs = Mal.Step6\nstep7_quote.purs = Mal.Step7\nstep8_macros.purs = Mal.Step8\nstep9_try.purs = Mal.Step9\nstepA_mal.purs = Mal.StepA\n\n\nclean:\n\trm -rf step*.js output/*"
  },
  {
    "path": "impls/purs/package.json",
    "content": "{\n  \"dependencies\": {\n    \"readline-sync\": \"^1.4.10\"\n  }\n}\n"
  },
  {
    "path": "impls/purs/packages.dhall",
    "content": "{-\nWelcome to your new Dhall package-set!\n\nBelow are instructions for how to edit this file for most use\ncases, so that you don't need to know Dhall to use it.\n\n## Use Cases\n\nMost will want to do one or both of these options:\n1. Override/Patch a package's dependency\n2. Add a package not already in the default package set\n\nThis file will continue to work whether you use one or both options.\nInstructions for each option are explained below.\n\n### Overriding/Patching a package\n\nPurpose:\n- Change a package's dependency to a newer/older release than the\n    default package set's release\n- Use your own modified version of some dependency that may\n    include new API, changed API, removed API by\n    using your custom git repo of the library rather than\n    the package set's repo\n\nSyntax:\nwhere `entityName` is one of the following:\n- dependencies\n- repo\n- version\n-------------------------------\nlet upstream = --\nin  upstream\n  with packageName.entityName = \"new value\"\n-------------------------------\n\nExample:\n-------------------------------\nlet upstream = --\nin  upstream\n  with halogen.version = \"master\"\n  with halogen.repo = \"https://example.com/path/to/git/repo.git\"\n\n  with halogen-vdom.version = \"v4.0.0\"\n  with halogen-vdom.dependencies = [ \"extra-dependency\" ] # halogen-vdom.dependencies\n-------------------------------\n\n### Additions\n\nPurpose:\n- Add packages that aren't already included in the default package set\n\nSyntax:\nwhere `<version>` is:\n- a tag (i.e. \"v4.0.0\")\n- a branch (i.e. \"master\")\n- commit hash (i.e. \"701f3e44aafb1a6459281714858fadf2c4c2a977\")\n-------------------------------\nlet upstream = --\nin  upstream\n  with new-package-name =\n    { dependencies =\n       [ \"dependency1\"\n       , \"dependency2\"\n       ]\n    , repo =\n       \"https://example.com/path/to/git/repo.git\"\n    , version =\n        \"<version>\"\n    }\n-------------------------------\n\nExample:\n-------------------------------\nlet upstream = --\nin  upstream\n  with benchotron =\n      { dependencies =\n          [ \"arrays\"\n          , \"exists\"\n          , \"profunctor\"\n          , \"strings\"\n          , \"quickcheck\"\n          , \"lcg\"\n          , \"transformers\"\n          , \"foldable-traversable\"\n          , \"exceptions\"\n          , \"node-fs\"\n          , \"node-buffer\"\n          , \"node-readline\"\n          , \"datetime\"\n          , \"now\"\n          ]\n      , repo =\n          \"https://github.com/hdgarrood/purescript-benchotron.git\"\n      , version =\n          \"v7.0.0\"\n      }\n-------------------------------\n-}\nlet upstream =\n      https://github.com/purescript/package-sets/releases/download/psc-0.14.2-20210713/packages.dhall sha256:654c3148cb995f642c73b4508d987d9896e2ad3ea1d325a1e826c034c0d3cd7b\n\nin  upstream\n"
  },
  {
    "path": "impls/purs/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/${STEP:-stepA_mal}.js \"${@}\""
  },
  {
    "path": "impls/purs/spago.dhall",
    "content": "{-\nWelcome to a Spago project!\nYou can edit this file as you like.\n\nNeed help? See the following resources:\n- Spago documentation: https://github.com/purescript/spago\n- Dhall language tour: https://docs.dhall-lang.org/tutorials/Language-Tour.html\n\nWhen creating a new Spago project, you can use\n`spago init --no-comments` or `spago init -C`\nto generate this file without the comments in this block.\n-}\n{ name = \"mal-purescript\"\n, dependencies =\n  [ \"arrays\"\n  , \"console\"\n  , \"control\"\n  , \"datetime\"\n  , \"effect\"\n  , \"either\"\n  , \"exceptions\"\n  , \"foldable-traversable\"\n  , \"freet\"\n  , \"identity\"\n  , \"integers\"\n  , \"lists\"\n  , \"maybe\"\n  , \"node-buffer\"\n  , \"node-fs\"\n  , \"now\"\n  , \"ordered-collections\"\n  , \"parsing\"\n  , \"prelude\"\n  , \"psci-support\"\n  , \"refs\"\n  , \"strings\"\n  , \"tailrec\"\n  , \"transformers\"\n  , \"tuples\"\n  ]\n, packages = ./packages.dhall\n, sources = [ \"src/**/*.purs\", \"test/**/*.purs\" ]\n}\n"
  },
  {
    "path": "impls/purs/src/Core.purs",
    "content": "module Core (ns) where\n\nimport Prelude\n\nimport Data.DateTime.Instant (unInstant)\nimport Data.Int (ceil, toNumber)\nimport Data.List (List(..), concat, drop, foldM, fromFoldable, length, reverse, (:))\nimport Data.Map.Internal as Map\nimport Data.Maybe (Maybe(..))\nimport Data.String (take)\nimport Data.String.CodeUnits (singleton)\nimport Data.Time.Duration (Milliseconds(..), toDuration)\nimport Data.Traversable (traverse)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (liftEffect)\nimport Effect.Console (log)\nimport Effect.Exception (throw)\nimport Effect.Now (now)\nimport Effect.Ref as Ref\nimport Reader (readStr)\nimport Node.Encoding (Encoding(..))\nimport Node.FS.Sync (readTextFile)\nimport Printer (keyValuePairs, printList, printListReadably, printStrReadably)\nimport Readline (readLine)\nimport Types (Key(..), MalExpr(..), MalFn, Meta(..), keyToString, stringToCharList, toAtom, toHashMap, toList, toVector)\n\n\n\nns :: List (Tuple String MalFn)\nns = fromFoldable\n  [ Tuple \"throw\"      throw'\n\n  , Tuple \"true?\"       $ pred1 trueQ\n  , Tuple \"false?\"      $ pred1 falseQ\n\n  , Tuple \"=\"           eqQ\n  , Tuple \"+\"           $ numOp (+)\n  , Tuple \"-\"           $ numOp (-)\n  , Tuple \"*\"           $ numOp (*)\n  , Tuple \"/\"           $ numOp (/)\n  , Tuple \"<\"           $ cmpOp (<)\n  , Tuple \"<=\"          $ cmpOp (<=)\n  , Tuple \">\"           $ cmpOp (>)\n  , Tuple \">=\"          $ cmpOp (>=)\n  , Tuple \"number?\"     $ pred1 numberQ\n\n  , Tuple \"pr-str\"      prStr\n  , Tuple \"str\"         str\n  , Tuple \"string?\"     $ pred1 stringQ\n  , Tuple \"prn\"         prn\n  , Tuple \"println\"     println\n  , Tuple \"slurp\"       slurp\n  , Tuple \"readline\"    readline'\n  , Tuple \"read-string\" readString\n  , Tuple \"time-ms\"     timeMs\n\n  , Tuple \"symbol?\"     $ pred1 symbolQ\n  , Tuple \"symbol\"      symbol\n  , Tuple \"keyword?\"    $ pred1 keywordQ\n  , Tuple \"keyword\"     keyword\n\n  , Tuple \"list\"        list\n  , Tuple \"list?\"       $ pred1 listQ\n  , Tuple \"nil?\"        $ pred1 nilQ\n  , Tuple \"empty?\"      $ pred1 emptyQ\n  , Tuple \"count\"       count\n  , Tuple \"sequential?\" $ pred1 sequentialQ\n  , Tuple \"cons\"        cons\n  , Tuple \"concat\"      concat'\n  , Tuple \"nth\"         nth\n  , Tuple \"first\"       first\n  , Tuple \"rest\"        rest\n  , Tuple \"apply\"       apply'\n  , Tuple \"map\"         map'\n  , Tuple \"map?\"        $ pred1 mapQ\n  , Tuple \"conj\"        conj'\n  , Tuple \"seq\"         seq\n\n  , Tuple \"vec\"         vec\n  , Tuple \"vector\"      vector\n  , Tuple \"vector?\"     $ pred1 vectorQ\n\n  , Tuple \"hash-map\"    hashMap\n  , Tuple \"assoc\"       assoc\n  , Tuple \"dissoc\"      dissoc\n  , Tuple \"get\"         get\n  , Tuple \"contains?\"   containsQ\n  , Tuple \"keys\"        keys\n  , Tuple \"vals\"        vals\n\n  , Tuple \"meta\"        meta\n  , Tuple \"with-meta\"   withMeta\n\n  , Tuple \"atom\"        atom\n  , Tuple \"atom?\"       $ pred1 atomQ\n  , Tuple \"deref\"       deref\n  , Tuple \"reset!\"      resetB\n  , Tuple \"swap!\"       swapB\n\n  , Tuple \"macro?\"      $ pred1 macroQ\n\n  , Tuple \"fn?\"         $ pred1 fnQ\n  ]\n\n\n\n-- General functions\n\neqQ :: MalFn\neqQ (a:b:Nil) = pure $ MalBoolean $ a == b\neqQ _         = throw \"illegal arguments to =\"\n\n\n\n-- Error/Exception functions\n\nthrow' :: MalFn\nthrow' (e:Nil) = throw =<< printStrReadably e\nthrow' _       = throw \"illegal arguments to throw\"\n\n\n\n-- Boolean functions\n\ntrueQ :: MalExpr -> Boolean\ntrueQ (MalBoolean true) = true\ntrueQ _                 = false\n\n\nfalseQ :: MalExpr -> Boolean\nfalseQ (MalBoolean false) = true\nfalseQ _                  = false\n\n\n-- Numeric functions\n\nnumOp ∷  (Number → Number → Number) → MalFn\nnumOp op (MalInt n1 : MalInt n2 : Nil)   = pure $ MalInt $ ceil $ op (toNumber n1) (toNumber n2)\nnumOp op (MalInt n1 : MalTime n2 : Nil)  = pure $ MalInt $ ceil $ op (toNumber n1) n2\nnumOp op (MalTime n1 : MalInt n2 : Nil)  = pure $ MalInt $ ceil $ op n1 (toNumber n2)\nnumOp op (MalTime n1 : MalTime n2 : Nil) = pure $ MalTime $ op n1 n2\nnumOp _ _                                = throw \"invalid operator\"\n\n\ncmpOp ∷  (Number → Number → Boolean) → List MalExpr → Effect MalExpr\ncmpOp op (MalInt n1 : MalInt n2 : Nil)   = pure $ MalBoolean $ op (toNumber n1) (toNumber n2)\ncmpOp op (MalInt n1 : MalTime n2 : Nil)  = pure $ MalBoolean $ op (toNumber n1) n2\ncmpOp op (MalTime n1 : MalInt n2 : Nil)  = pure $ MalBoolean $ op n1 (toNumber n2)\ncmpOp op (MalTime n1 : MalTime n2 : Nil) = pure $ MalBoolean $ op n1 n2\ncmpOp _ _                                = throw \"invalid operator\"\n\n\nnumberQ :: MalExpr -> Boolean\nnumberQ (MalInt _)  = true\nnumberQ (MalTime _) = true\nnumberQ _           = false\n\n\n\n-- String functions\n\nprStr :: MalFn\nprStr a = liftEffect $ MalString <$> printList a\n\n\nstr :: MalFn\nstr a = liftEffect $ MalString <$> printListReadably \"\" a\n\n\nstringQ :: MalExpr -> Boolean\nstringQ (MalString \"\") = true\nstringQ (MalString s)  = take 1 s /= \":\"\nstringQ _              = false\n\n\nprn :: MalFn\nprn args = liftEffect $ do\n  log =<< printList args\n  pure MalNil\n\n\nprintln :: MalFn\nprintln args = liftEffect $ do\n  log =<< printListReadably \" \" args\n  pure MalNil\n\n\nslurp :: MalFn\nslurp (MalString path : Nil) = MalString <$> liftEffect (readTextFile UTF8 path)\nslurp _                      = throw \"invalid arguments to slurp\"\n\n\nreadline' :: MalFn\nreadline' (MalString prompt : Nil) = MalString <$> readLine prompt\nreadline' _                        = throw \"invalid arguments to readline\"\n\n\nreadString :: MalFn\nreadString (MalString s : Nil) = readStr s\nreadString _                   = throw \"invalid read-string\"\n\n\ntimeMs :: MalFn\ntimeMs Nil = do\n  n <- now\n  pure $ MalTime $ (unwap <<< toDuration <<< unInstant) n\n  where\n\n  unwap :: Milliseconds -> Number\n  unwap (Milliseconds n) = n\n\ntimeMs _ = throw \"invalid time-ms\"\n\n\n\n-- Scalar functions\n\nsymbolQ :: MalExpr -> Boolean\nsymbolQ (MalSymbol _) = true\nsymbolQ _             = false\n\n\nsymbol :: MalFn\nsymbol (MalString s : Nil) = pure $ MalSymbol s\nsymbol _                   = throw \"symbol called with non-string\"\n\n\nkeywordQ :: MalExpr -> Boolean\nkeywordQ (MalKeyword s) = take 1 s == \":\"\nkeywordQ _             = false\n\n\nkeyword :: MalFn\nkeyword (kw@(MalString s) : Nil) | take 1 s == \":\" = pure kw\nkeyword (MalString s : Nil)  = pure $ MalKeyword (\":\" <> s)\nkeyword (kw@(MalKeyword s) : Nil) | take 1 s == \":\" = pure kw\nkeyword (MalKeyword s : Nil) = pure $ MalKeyword (\":\" <> s)\nkeyword _                    = throw \"keyword called with non-string\"\n\n\n\n-- List functions\n\nlist :: MalFn\nlist = pure <<< toList\n\n\nlistQ :: MalExpr -> Boolean\nlistQ (MalList _ _ ) = true\nlistQ _              = false\n\n\nnilQ :: MalExpr -> Boolean\nnilQ MalNil = true\nnilQ _      = false\n\n\nemptyQ :: MalExpr -> Boolean\nemptyQ (MalList _ Nil)   = true\nemptyQ (MalVector _ Nil) = true\nemptyQ _                 = false\n\n\ncount :: MalFn\ncount (MalNil:Nil)           = pure $ MalInt 0\ncount (MalList _ ex : Nil)   = pure $ MalInt $ length ex\ncount (MalVector _ ex : Nil) = pure $ MalInt $ length ex\ncount _                      = throw \"non-sequence passed to count\"\n\n\nsequentialQ :: MalExpr -> Boolean\nsequentialQ (MalList _ _)   = true\nsequentialQ (MalVector _ _) = true\nsequentialQ _               = false\n\n\ncons :: MalFn\ncons (x:Nil)                    = pure $ toList $ x:Nil\ncons (x : MalList _ xs : Nil)   = pure $ toList $ x:xs\ncons (x : MalVector _ xs : Nil) = pure $ toList $ x:xs\ncons _                          = throw \"illegal call to cons\"\n\n\nconcat' :: MalFn\nconcat' args = toList <<< concat <$> traverse unwrapSeq args\n  where\n\n  unwrapSeq :: MalExpr -> Effect (List MalExpr)\n  unwrapSeq (MalList _ xs)   = pure xs\n  unwrapSeq (MalVector _ xs) = pure xs\n  unwrapSeq _                = throw \"invalid concat\"\n\n\nnth :: MalFn\nnth (MalList _ xs : MalInt n : Nil)   =\n  case drop n xs of\n    x:_ -> pure x\n    Nil -> throw \"nth: index out of range\"\nnth (MalVector _ xs : MalInt n : Nil) =\n  case drop n xs of\n    x:_ -> pure x\n    Nil -> throw \"nth: index out of range\"\nnth _                                 = throw \"invalid call to nth\"\n\n\nfirst :: MalFn\nfirst (MalNil:Nil)              = pure MalNil\nfirst (MalList _ Nil : Nil)     = pure MalNil\nfirst (MalList _ (x:_) : Nil)   = pure x\nfirst (MalVector _ Nil : Nil)   = pure MalNil\nfirst (MalVector _ (x:_) : Nil) = pure x\nfirst _                         = throw \"illegal call to first\"\n\n\nrest :: MalFn\nrest (MalNil:Nil)               = pure $ toList Nil\nrest (MalList _ Nil : Nil)      = pure $ toList Nil\nrest (MalList _ (_:xs) : Nil)   = pure $ toList xs\nrest (MalVector _ Nil : Nil)    = pure $ toList Nil\nrest (MalVector _ (_:xs) : Nil) = pure $ toList xs\nrest _                          = throw \"illegal call to rest\"\n\n\napply' :: MalFn\napply' (MalFunction {fn:f} : as) = f =<< concatLast as\n  where\n  concatLast :: List MalExpr -> Effect (List MalExpr)\n  concatLast (MalList _ lst : Nil)   = pure lst\n  concatLast (MalVector _ lst : Nil) = pure lst\n  concatLast (x:xs)                  = (:) x <$> concatLast xs\n  concatLast _                       = throw \"last argument of apply must be a sequence\"\napply' _ = throw \"Illegal call to apply\"\n\n\nmap' :: MalFn\nmap' (MalFunction {fn:f} : MalList _ args : Nil)   = toList <$> traverse (\\x -> f (x:Nil)) args\nmap' (MalFunction {fn:f} : MalVector _ args : Nil) = toList <$> traverse (\\x -> f (x:Nil)) args\nmap' _ = throw \"Illegal call to map\"\n\n\nmapQ :: MalExpr -> Boolean\nmapQ (MalHashMap _ _) = true\nmapQ _                = false\n\n\nconj' :: MalFn\nconj' (MalList _ es : args)   = pure $ toList $ reverse args <> es\nconj' (MalVector _ es : args) = pure $ toVector $ es <> args\nconj' _                       = throw \"illegal arguments to conj\"\n\n\nseq :: MalFn\nseq (MalNil:Nil)            = pure MalNil\nseq (MalList _ Nil : Nil)   = pure MalNil\nseq (MalList _ es : Nil)    = pure $ toList es\nseq (MalVector _ Nil : Nil) = pure MalNil\nseq (MalVector _ es : Nil)  = pure $ toList es\nseq (MalString \"\" : Nil)    = pure MalNil\nseq (MalString s : Nil)     = pure $ toList $ map (MalString <<< singleton) (stringToCharList s)\nseq _                       = throw \"seq: called on non-sequence\"\n\n\n\n-- Vector functions\n\nvec :: MalFn\nvec (MalList _ xs : Nil)   = pure $ toVector xs\nvec (MalVector _ xs : Nil) = pure $ toVector xs\nvec Nil                  = throw \"vec: arg type\"\nvec _                    = throw \"vec: arg type\"\n\n\nvector :: MalFn\nvector = pure <<< toVector\n\n\nvectorQ :: MalExpr -> Boolean\nvectorQ (MalVector _ _) = true\nvectorQ _               = false\n\n\n\n-- Hash Map functions\n\nhashMap :: MalFn\nhashMap kvs =\n  case keyValuePairs kvs of\n    Just pairs -> pure $ toHashMap $ Map.fromFoldable pairs\n    Nothing    -> throw \"invalid call to hash-map\"\n\n\nassoc :: MalFn\nassoc (MalHashMap _ hm : kvs) =\n    case keyValuePairs kvs of\n        Just pairs -> pure $ toHashMap $ Map.union (Map.fromFoldable pairs) hm\n        Nothing    -> throw \"invalid assoc\"\nassoc _                       = throw \"invalid call to assoc\"\n\n\ndissoc :: MalFn\ndissoc (MalHashMap _ hm : ks) = toHashMap <$> foldM remover hm ks\n  where\n  remover :: Map.Map Key MalExpr -> MalExpr -> Effect (Map.Map Key MalExpr)\n  remover m (MalKeyword k) = pure $ Map.delete (KeywordKey k) m\n  remover m (MalString k)  = pure $ Map.delete (StringKey k) m\n  remover _ _              = throw \"invalid dissoc\"\ndissoc _                      = throw \"invalid call to dissoc\"\n\n\nget :: MalFn\nget (MalHashMap _ hm : MalString k : Nil)  =\n  pure case Map.lookup (StringKey k) hm of\n    Just mv -> mv\n    Nothing -> MalNil\nget (MalHashMap _ hm : MalKeyword k : Nil) =\n  pure case Map.lookup (KeywordKey k) hm of\n    Just mv -> mv\n    Nothing -> MalNil\nget (MalNil : MalString _ : Nil)           = pure MalNil\nget _                                      = throw \"invalid call to get\"\n\n\ncontainsQ :: MalFn\ncontainsQ (MalHashMap _ hm : MalString k : Nil)  = pure $ MalBoolean $ Map.member (StringKey k) hm\ncontainsQ (MalHashMap _ hm : MalKeyword k : Nil) = pure $ MalBoolean $ Map.member (KeywordKey k) hm\ncontainsQ (MalNil : MalString _ : Nil)           = pure $ MalBoolean false\ncontainsQ _                                      = throw \"invalid call to contains?\"\n\n\nkeys :: MalFn\nkeys (MalHashMap _ hm : Nil) = pure $ toList $ keyToString <$> Map.keys hm\nkeys _                     = throw \"invalid call to keys\"\n\n\nvals :: MalFn\nvals (MalHashMap _ hm : Nil) = pure $ toList $ Map.values hm\nvals _                       = throw \"invalid call to vals\"\n\n\n\n-- Metadata functions\n\nmeta :: MalFn\nmeta (MalList (Meta m) _ : Nil)    = pure m\nmeta (MalVector (Meta m) _ : Nil)  = pure m\nmeta (MalHashMap (Meta m) _ : Nil) = pure m\nmeta (MalAtom (Meta m) _ : Nil)    = pure m\nmeta (MalFunction {meta:m} : Nil)  = pure m\nmeta _                             = throw \"invalid meta call\"\n\n\nwithMeta :: MalFn\nwithMeta (MalList _ es : m : Nil)    = pure $ MalList (Meta m) es\nwithMeta (MalVector _ es : m : Nil)  = pure $ MalVector (Meta m) es\nwithMeta (MalHashMap _ es : m : Nil) = pure $ MalHashMap (Meta m) es\nwithMeta (MalAtom _ es : m : Nil)    = pure $ MalAtom (Meta m) es\nwithMeta ((MalFunction f) : m : Nil) = pure $ MalFunction $ f {meta = m}\nwithMeta _                           = throw \"invalid with-meta call\"\n\n\n\n-- Atom functions\n\natom :: MalFn\natom (v:Nil) = toAtom <$> liftEffect (Ref.new v)\natom _       = throw \"invalid atom call\"\n\n\natomQ :: MalExpr -> Boolean\natomQ (MalAtom _ _) = true\natomQ _             = false\n\n\nderef :: MalFn\nderef (MalAtom _ ref : Nil) = liftEffect $ Ref.read ref\nderef _                   = throw \"invalid deref call\"\n\n\nresetB :: MalFn\nresetB (MalAtom _ ref : val : Nil) = liftEffect $ Ref.write val ref *> pure val\nresetB _                         = throw \"invalid reset!\"\n\n\nswapB :: MalFn\nswapB (MalAtom _ ref : MalFunction {fn:f} : args) = do\n  val <- liftEffect $ Ref.read ref\n  newVal <- f $ val:args\n  liftEffect $ Ref.write newVal ref\n  pure newVal\nswapB _                                           = throw \"Illegal swap!\"\n\n\n\n-- Macro\n\nmacroQ :: MalExpr -> Boolean\nmacroQ (MalFunction {macro:true}) = true\nmacroQ _                          = false\n\n\n\n-- Function\n\nfnQ :: MalExpr -> Boolean\nfnQ (MalFunction {macro:false}) = true\nfnQ _                           = false\n\n\n\n-- Utils\n\npred1 :: (MalExpr -> Boolean) -> MalFn\npred1 f (x:Nil) = pure $ MalBoolean $ f x\npred1 _ _       = throw \"illegal call to unary predicate\""
  },
  {
    "path": "impls/purs/src/Env.purs",
    "content": "module Env where\n\nimport Prelude\n\nimport Data.List (List(..), (:))\nimport Data.Map (fromFoldable, insert, lookup)\nimport Data.Maybe (Maybe(..))\nimport Effect (Effect)\nimport Effect.Console (error)\nimport Effect.Ref as Ref\nimport Types (Local, MalExpr, RefEnv, toList)\n\n\n\n-- Environment\n\ninitEnv :: Local\ninitEnv = fromFoldable Nil\n\n\nnewEnv :: RefEnv -> Effect RefEnv\nnewEnv re = flip (:) re <$> Ref.new initEnv\n\n\n\n-- VARIABLE\n\nget :: RefEnv -> String -> Effect (Maybe MalExpr)\nget Nil _ = pure Nothing\nget (ref:outer) ky = do\n  envs <- Ref.read ref\n  case lookup ky envs of\n    Nothing -> get outer ky\n    ex      -> pure ex\n\n\nsets :: RefEnv -> List String -> List MalExpr -> Effect Boolean\nsets _ Nil Nil             = pure true\nsets env (\"&\":k:Nil) exs   = set env k (toList exs) *> pure true\nsets env (ky:kys) (ex:exs) = set env ky ex *> sets env kys exs\nsets _ _ _                 = pure false\n\n\nset :: RefEnv -> String -> MalExpr -> Effect Unit\nset (re:_) ky ex = Ref.modify_ (insert ky ex) re\nset Nil _ _      = error \"assertion failed in env_set\""
  },
  {
    "path": "impls/purs/src/Printer.purs",
    "content": "module Printer where\n\nimport Prelude\n\nimport Data.List (List(..), (:))\nimport Data.Map (toUnfoldable)\nimport Data.Maybe (Maybe(..))\nimport Data.String.CodeUnits (singleton)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Ref as Ref\nimport Types (Key(..), MalExpr(..), flatTuples, flatStrings, stringToCharList)\n\n\n\n-- PRINT STRING\n\nprintStr :: MalExpr -> Effect String\nprintStr MalNil             = pure \"nil\"\nprintStr (MalBoolean b)     = pure $ show b\nprintStr (MalInt n)         = pure $ show n\nprintStr (MalTime n)        = pure $ show n\nprintStr (MalString str)    = pure $ \"\\\"\" <> (str # stringToCharList # map unescape # flatStrings) <> \"\\\"\"\nprintStr (MalKeyword key)   = pure key\nprintStr (MalAtom _ r)      = \"(atom \" <<> (Ref.read r >>= printStr) <>> \")\"\nprintStr (MalSymbol name)   = pure name\nprintStr (MalList _ xs)     = \"(\" <<> printList xs <>> \")\"\nprintStr (MalVector _ vs)   = \"[\" <<> printList vs <>> \"]\"\nprintStr (MalHashMap _ hm)  = \"{\" <<> (hm # toUnfoldable # flatTuples # printList) <>> \"}\"\nprintStr (MalFunction _)    = pure \"#<function>\"\n\n\nprintList :: List MalExpr -> Effect String\nprintList Nil     = pure \"\"\nprintList (x:Nil) = printStr x\nprintList (x:xs)  = printStr x <> pure \" \" <> printList xs\n\n\n\n-- PRINT STRING READABLY\n\nprintStrReadably :: MalExpr -> Effect String\nprintStrReadably (MalString str)   = pure str\nprintStrReadably (MalList _ xs)    = \"(\" <<> printListReadably \" \" xs <>> \")\"\nprintStrReadably (MalVector _ vs)  = \"[\" <<> printListReadably \" \" vs <>> \"]\"\nprintStrReadably (MalHashMap _ hm) = \"{\" <<> (hm # toUnfoldable # flatTuples # printListReadably \" \") <>> \"}\"\nprintStrReadably ex                = printStr ex\n\n\nprintListReadably :: String -> List MalExpr ->  Effect String\nprintListReadably _ Nil      = pure \"\"\nprintListReadably _ (x:Nil)  = printStrReadably x\nprintListReadably sep (x:xs) = printStrReadably x <> pure sep <> printListReadably sep xs\n\n\n\n-- UTILS\n\nunescape :: Char -> String\nunescape '\\n' = \"\\\\n\"\nunescape '\\\\' = \"\\\\\\\\\"\nunescape '\"'  = \"\\\\\\\"\"\nunescape c    = singleton c\n\n\nkeyValuePairs :: List MalExpr -> Maybe (List (Tuple Key MalExpr))\nkeyValuePairs Nil                      = pure Nil\nkeyValuePairs (MalString k : v : kvs)  = (:) (Tuple (StringKey k) v) <$> keyValuePairs kvs\nkeyValuePairs (MalKeyword k : v : kvs) = (:) (Tuple (KeywordKey k) v) <$> keyValuePairs kvs\nkeyValuePairs _                        = Nothing\n\n\nleftConcat :: forall m s. Bind m => Applicative m => Semigroup s => s -> m s -> m s\nleftConcat op f = (<>) <$> pure op <*> f\n\ninfixr 5 leftConcat as <<>\n\n\nrightConcat :: forall m s. Apply m => Semigroup s => Applicative m => m s -> s -> m s\nrightConcat f cl = (<>) <$> f <*> pure cl\n\ninfixr 5 rightConcat as <>>"
  },
  {
    "path": "impls/purs/src/Reader.purs",
    "content": "module Reader (readStr) where\n\nimport Prelude\n\nimport Control.Alt ((<|>))\nimport Control.Lazy (fix)\nimport Data.Either (Either(..))\nimport Data.Int (fromString)\nimport Data.List (List(..), many, (:))\nimport Data.Maybe (Maybe(..), fromMaybe)\nimport Effect (Effect)\nimport Effect.Exception (throw)\nimport Printer (keyValuePairs)\nimport Text.Parsing.Parser (Parser, fail, runParser)\nimport Text.Parsing.Parser.Combinators (endBy, skipMany, skipMany1, try)\nimport Text.Parsing.Parser.String (char, noneOf, oneOf, string)\nimport Text.Parsing.Parser.Token (digit, letter)\nimport Types (MalExpr(..), charListToString, listToMap, toHashMap, toList, toVector)\n\n\nspaces :: Parser String Unit\nspaces = skipMany1 $ oneOf [',', ' ', '\\n']\n\n\ncomment :: Parser String Unit\ncomment = char ';' *> (skipMany $ noneOf [ '\\r', '\\n' ])\n\n\nignored :: Parser String Unit\nignored = skipMany $ spaces <|> comment\n\n\nsymbol :: Parser String Char\nsymbol = oneOf ['!', '#', '$', '%', '&', '|', '*', '+', '-', '/', ':', '<', '=', '>', '?', '@', '^', '_', '~']\n\n\nnat :: Parser String Int\nnat = do\n  first <- digit\n  rest <- many digit\n  pure <<< fromMaybe 0 <<< fromString <<< charListToString $ first : rest\n\n\nescape :: Parser String Char\nescape = char '\\\\'\n      *> oneOf ['\\\\', '\\\"', 'n']\n     <#> case _ of\n          'n' -> '\\n'\n          x   -> x\n\n\nnonEscape :: Parser String Char\nnonEscape =  noneOf [ '\\\"', '\\\\' ]\n\n\n\n-- ATOM\n\nreadAtom :: Parser String MalExpr\nreadAtom = readNumber\n       <|> try readNegativeNumber\n       <|> readString\n       <|> readKeyword\n       <|> readSymbol\n\n\nreadNumber :: Parser String MalExpr\nreadNumber = MalInt <$> nat\n\n\nreadNegativeNumber :: Parser String MalExpr\nreadNegativeNumber = MalInt <<< negate <$> (char '-' *> nat)\n\n\nreadString :: Parser String MalExpr\nreadString = MalString <$> charListToString <$> (char '\"' *> many (escape <|> nonEscape) <* char '\"')\n\n\nreadKeyword :: Parser String MalExpr\nreadKeyword =\n  MalKeyword <$> charListToString\n             <$> ((:) ':')\n             <$> (char ':' *> many (letter <|> digit <|> symbol))\n\n\nreadSymbol :: Parser String MalExpr\nreadSymbol = f <$> (letter <|> symbol) <*> many (letter <|> digit <|> symbol)\n  where\n\n  f first rest = charListToString (first:rest)\n    # case _ of\n      \"true\"  -> MalBoolean true\n      \"false\" -> MalBoolean false\n      \"nil\"   -> MalNil\n      s       -> MalSymbol s\n\n\n\n--\n\nreadList :: Parser String MalExpr\nreadList = fix $ \\_ ->\n  toList <$> (char '(' *> ignored *> endBy readForm ignored <* char ')')\n\n\n\n--\n\nreadVector :: Parser String MalExpr\nreadVector = fix $ \\_ ->\n  toVector <$> (char '[' *> ignored *> endBy readForm ignored <* char ']')\n\n\n\n--\n\nreadHashMap :: Parser String MalExpr\nreadHashMap = fix $ \\_\n  -> char '{' *> ignored *> endBy readForm ignored <* char '}'\n  <#> keyValuePairs\n  >>= case _ of\n    Just ts -> pure $ toHashMap $ listToMap ts\n    Nothing -> fail \"invalid contents inside map braces\"\n\n\n\n-- MACROS\n\nreadMacro :: Parser String MalExpr\nreadMacro = fix $ \\_ ->\n      macro \"\\'\" \"quote\"\n  <|> macro \"`\" \"quasiquote\"\n  <|> try (macro \"~@\" \"splice-unquote\")\n  <|> macro \"~\" \"unquote\"\n  <|> macro \"@\" \"deref\"\n  <|> readWithMeta\n\n\nmacro :: String -> String -> Parser String MalExpr\nmacro tok sym = addPrefix sym <$> (string tok *> readForm)\n  where\n\n  addPrefix :: String -> MalExpr -> MalExpr\n  addPrefix s x = toList $ MalSymbol s : x : Nil\n\n\nreadWithMeta :: Parser String MalExpr\nreadWithMeta = addPrefix <$> (char '^' *> readForm) <*> readForm\n  where\n\n  addPrefix :: MalExpr -> MalExpr -> MalExpr\n  addPrefix m x = toList $ MalSymbol \"with-meta\" : x : m : Nil\n\n\n\n--\n\nreadForm :: Parser String MalExpr\nreadForm = fix $ \\_ -> ignored\n   *> ( readMacro\n    <|> readList\n    <|> readVector\n    <|> readHashMap\n    <|> readAtom)\n\n\n\n--\n\nreadStr :: String -> Effect MalExpr\nreadStr str = case runParser str readForm of\n  Left _    -> throw \"EOF\"\n  Right val -> pure val"
  },
  {
    "path": "impls/purs/src/Readline.js",
    "content": "\"use strict\";\n\nvar readlineSync = require('readline-sync')\n\nexports.readLine = function (x) {\n  return function () {\n    const result =  readlineSync.question(x);\n\n    if(readlineSync.getRawInput() === String.fromCharCode(0)){\n      return \":q\"\n    }\n    return result;\n  }\n}\n\n\nexports.argv = process.argv;"
  },
  {
    "path": "impls/purs/src/Readline.purs",
    "content": "module Readline where\n\nimport Prelude\n\nimport Data.List (List, drop, fromFoldable)\nimport Effect (Effect)\n\n\n\nforeign import readLine :: String -> Effect String\n\n\nforeign import argv :: Array String\n\nargs :: List String\nargs = drop 2 $ fromFoldable argv"
  },
  {
    "path": "impls/purs/src/Types.purs",
    "content": "module Types where\n\nimport Prelude\n\nimport Data.Array as Array\nimport Data.Foldable (class Foldable)\nimport Data.List (List(..), foldr, (:))\nimport Data.List as List\nimport Data.Map (Map)\nimport Data.Map.Internal as Map\nimport Data.Maybe (Maybe(..))\nimport Data.String.CodeUnits (fromCharArray, toCharArray)\nimport Data.Traversable (foldl)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Ref (Ref)\nimport Effect.Ref as Ref\n\n\ndata MalExpr\n  = MalNil\n  | MalBoolean Boolean\n  | MalInt Int\n  | MalTime Time\n  | MalString String\n  | MalKeyword String\n  | MalSymbol String\n  | MalAtom Meta (Ref MalExpr)\n  | MalList Meta (List MalExpr)\n  | MalVector Meta (List MalExpr)\n  | MalHashMap Meta (Map Key MalExpr)\n  | MalFunction { fn     :: MalFn\n                , ast    :: MalExpr\n                , env    :: RefEnv\n                , params :: List String\n                , macro  :: Boolean\n                , meta   :: MalExpr\n                }\n\ntype Time = Number\n\n\ninstance Eq MalExpr where\n  eq MalNil MalNil                     = true\n  eq (MalBoolean a) (MalBoolean b)     = a == b\n  eq (MalInt a) (MalInt b)             = a == b\n  eq (MalTime a) (MalTime b)           = a == b\n  eq (MalString a) (MalString b)       = a == b\n  eq (MalKeyword a) (MalKeyword b)     = a == b\n  eq (MalSymbol a) (MalSymbol b)       = a == b\n\n  eq (MalList _ a) (MalList _ b)       = a == b\n  eq (MalVector _ a) (MalList _ b)     = a == b\n  eq (MalList _ a) (MalVector _ b)     = a == b\n\n  eq (MalVector _ a) (MalVector _ b)   = a == b\n  eq (MalHashMap _ a) (MalHashMap _ b) = a == b\n  eq _ _                               = false\n\n\ndata Key = StringKey String\n         | KeywordKey String\n\nderive instance Eq Key\nderive instance Ord Key\n\n\ntype MalFn = List MalExpr -> Effect MalExpr\n\n\ntype Local = Map String MalExpr\ntype RefEnv = List (Ref.Ref Local)\n\n\n\n-- Metas\n\nnewtype Meta = Meta MalExpr\n\n\ntoList :: List MalExpr -> MalExpr\ntoList = MalList (Meta MalNil)\n\n\ntoVector :: List MalExpr -> MalExpr\ntoVector = MalVector (Meta MalNil)\n\n\ntoAtom :: Ref MalExpr -> MalExpr\ntoAtom = MalAtom (Meta MalNil)\n\n\ntoHashMap :: Map Key MalExpr -> MalExpr\ntoHashMap = MalHashMap (Meta MalNil)\n\n\n\n-- Utils\n\nlistToMap :: List (Tuple Key MalExpr) -> Map Key MalExpr\nlistToMap = Map.fromFoldable\n\n\ncharListToString :: List Char -> String\ncharListToString = fromCharArray <<< Array.fromFoldable\n\n\nstringToCharList :: String -> List Char\nstringToCharList = List.fromFoldable <<< toCharArray\n\n\nflatStrings :: List String -> String\nflatStrings = foldr (<>) \"\"\n\n\nflatTuples :: List (Tuple Key MalExpr) -> List MalExpr\nflatTuples ((Tuple (StringKey a) b) : xs)  = MalString a : b : flatTuples xs\nflatTuples ((Tuple (KeywordKey a) b) : xs) = MalKeyword a : b : flatTuples xs\nflatTuples _                               = Nil\n\n\nfoldrM :: forall a m b f. Foldable f => Monad m => (a -> b -> m b) -> b -> f a -> m b\nfoldrM f z0 xs = foldl c pure xs z0\n  where c k x z = f x z >>= k\n\n\nkeyToString :: Key -> MalExpr\nkeyToString (StringKey k)  = MalString k\nkeyToString (KeywordKey k) = MalKeyword k\n\n\nkeyValuePairs :: List MalExpr -> Maybe (List (Tuple String MalExpr))\nkeyValuePairs Nil                     = pure Nil\nkeyValuePairs (MalString k : v : kvs) = (:)  (Tuple k v) <$> keyValuePairs kvs\nkeyValuePairs _                       = Nothing"
  },
  {
    "path": "impls/purs/src/step0_repl.purs",
    "content": "module Mal.Step0 where\n\nimport Prelude\nimport Effect (Effect)\nimport Effect.Console (log)\nimport Readline (readLine)\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = loop\n\n\n\n-- EVAL\n\neval :: String -> String\neval s = s\n\n\n\n-- REPL\n\nrep :: String -> String\nrep = read >>> eval >>> print\n\nloop :: Effect Unit\nloop = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop\n    \":q\" -> pure unit\n    _ -> do\n      log line\n      loop\n\n\n\n-- READ\n\nread :: String -> String\nread s = s\n\n\n\n-- PRINT\n\nprint :: String -> String\nprint s = s\n\n"
  },
  {
    "path": "impls/purs/src/step1_read_print.purs",
    "content": "module Mal.Step1 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Data.Either (Either(..))\nimport Effect (Effect)\nimport Effect.Console (error, log)\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (readLine)\nimport Types (MalExpr)\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = loop\n\n\n\n-- EVAL\n\neval :: MalExpr -> MalExpr\neval s = s\n\n\n\n-- REPL\n\nrep :: String -> Effect Unit\nrep str = do\n  result <- try $ read str\n  case result of\n    Left err  -> error $ show err\n    Right exp -> print (eval exp) >>= log\n\n\nloop :: Effect Unit\nloop = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop\n    \":q\" -> pure unit\n    _    -> do\n      rep line\n      loop\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr"
  },
  {
    "path": "impls/purs/src/step2_eval.purs",
    "content": "module Mal.Step2 where\n\nimport Prelude\n\nimport Data.Either (Either(..))\nimport Data.List (List(..), (:))\nimport Data.Map (Map, lookup)\nimport Data.Map as Map\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Console (error, log)\nimport Effect.Exception (throw, try)\nimport Reader (readStr)\nimport Printer (printStr)\nimport Readline (readLine)\nimport Types (MalExpr(..), MalFn, toHashMap, toVector)\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = loop\n\n\n\n-- EVAL\n\nevalCallFn :: List MalExpr -> Effect MalExpr\nevalCallFn ast = do\n  es <- traverse eval ast\n  case es of\n    MalFunction {fn:f}: args -> f args\n    _                        -> throw $ \"invalid function\"\n\n\neval :: MalExpr -> Effect MalExpr\neval (MalSymbol s)      = case lookup s replEnv of\n  Just f  -> pure f\n  Nothing -> throw \"invalid function\"\neval (MalList _ es@(_ : _)) = evalCallFn es\neval (MalVector _ es)   = toVector <$> (traverse eval es)\neval (MalHashMap _ es)  = toHashMap <$> (traverse eval es)\neval ast                = pure ast\n\n\n\n-- ENV\n\ntype ReplEnv = Map String MalExpr\n\nreplEnv :: ReplEnv\nreplEnv = Map.fromFoldable\n  [ (Tuple \"+\" (fn (+)))\n  , (Tuple \"-\" (fn (-)))\n  , (Tuple \"*\" (fn (*)))\n  , (Tuple \"/\" (fn (/)))\n  ]\n\nfn :: (Int -> Int -> Int) -> MalExpr\nfn op =\n  MalFunction\n    { fn     : g op\n    , ast    : MalNil\n    , env    : Nil\n    , params : Nil\n    , macro  : false\n    , meta   : MalNil\n    }\n  where\n  g :: (Int -> Int -> Int) -> MalFn\n  g op' ((MalInt n1) : (MalInt n2) : Nil) = pure $ MalInt $ op' n1 n2\n  g _ _                                   = throw \"invalid operator\"\n\n\n\n-- REPL\n\nrep :: String -> Effect Unit\nrep str = do\n  result <- try $ eval =<< read str\n  case result of\n    Left err  -> error $ show err\n    Right exp -> print exp >>= log\n\n\nloop :: Effect Unit\nloop = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop\n    \":q\" -> pure unit\n    _    -> rep line *> loop\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr"
  },
  {
    "path": "impls/purs/src/step3_env.purs",
    "content": "module Mal.Step3 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Data.Either (Either(..))\nimport Data.List (List(..), (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse)\nimport Effect (Effect)\nimport Effect.Console (error, log)\nimport Effect.Exception (throw)\nimport Env as Env\nimport Reader (readStr)\nimport Printer (printStr)\nimport Readline (readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, toHashMap, toVector)\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  re <- Env.newEnv Nil\n  setArithOp re\n  loop re\n\n\n\n-- EVAL\n\nevalCallFn :: RefEnv -> List MalExpr -> Effect MalExpr\nevalCallFn env ast = do\n    es <- traverse (eval env) ast\n    case es of\n      MalFunction {fn:f} : args -> f args\n      _                         -> throw \"invalid function\"\n\n\neval :: RefEnv -> MalExpr -> Effect MalExpr\neval env ast = do\n  dbgeval <- Env.get env \"DEBUG-EVAL\"\n  case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ es@(_ : _)                    -> evalCallFn env es\n    MalVector _ es  -> toVector <$> traverse (eval env) es\n    MalHashMap _ es -> toHashMap <$> traverse (eval env) es\n    _               -> pure ast\n\n\nevalDef :: RefEnv -> List MalExpr -> Effect MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\nevalLet :: RefEnv -> List MalExpr -> Effect MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\nletBind :: RefEnv -> List MalExpr -> Effect Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  Env.set env ky =<< eval env e\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\n\n-- REPL\n\nrep :: RefEnv -> String -> Effect String\nrep env str = print =<< eval env =<< read str\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetArithOp :: RefEnv -> Effect Unit\nsetArithOp env = do\n  Env.set env \"+\" =<< fn (+)\n  Env.set env \"-\" =<< fn (-)\n  Env.set env \"*\" =<< fn (*)\n  Env.set env \"/\" =<< fn (/)\n\n\nfn :: (Int -> Int -> Int) -> Effect MalExpr\nfn op = do\n  newEnv <- Env.newEnv Nil\n  pure $ MalFunction\n          { fn     : g op\n          , ast    : MalNil\n          , env    : newEnv\n          , params : Nil\n          , macro  : false\n          , meta   : MalNil\n          }\n  where\n\n  g :: (Int -> Int -> Int) -> MalFn\n  g op' ((MalInt n1) : (MalInt n2) : Nil) = pure $ MalInt $ op' n1 n2\n  g _ _                                   = throw \"invalid operator\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr"
  },
  {
    "path": "impls/purs/src/step4_if_fn_do.purs",
    "content": "module Mal.Step4 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Console (error, log)\nimport Effect.Exception (throw)\nimport Env as Env\nimport Reader (readStr)\nimport Printer (printStr)\nimport Readline (readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, toHashMap, toVector)\n\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  re <- Env.newEnv Nil\n  _ <- traverse (setFn re) Core.ns\n  _ <- rep re \"(def! not (fn* (a) (if a false true)))\"\n  loop re\n\n\n\n-- EVAL\n\nevalCallFn :: RefEnv -> List MalExpr -> Effect MalExpr\nevalCallFn env ast = do\n    es <- traverse (eval env) ast\n    case es of\n      MalFunction {fn:f} : args -> f args\n      _                         -> throw \"invalid function\"\n\n\neval :: RefEnv -> MalExpr -> Effect MalExpr\neval env ast = do\n  dbgeval <- Env.get env \"DEBUG-EVAL\"\n  case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ es@(_ : _)                    -> evalCallFn env es\n    MalVector _ es  -> toVector <$> traverse (eval env) es\n    MalHashMap _ es -> toHashMap <$> traverse (eval env) es\n    _               -> pure ast\n\n\nevalDef :: RefEnv -> List MalExpr -> Effect MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\nevalLet :: RefEnv -> List MalExpr -> Effect MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\n\nletBind :: RefEnv -> List MalExpr -> Effect Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  Env.set env ky =<< eval env e\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\nevalIf :: RefEnv -> List MalExpr -> Effect MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\nevalDo :: RefEnv -> List MalExpr -> Effect MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\nevalFnMatch :: RefEnv -> List MalExpr -> Effect MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Effect MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Effect String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- REPL\n\nrep :: RefEnv -> String -> Effect String\nrep env str = print =<< eval env =<< read str\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                      { fn     : f\n                      , ast    : MalNil\n                      , env    : newEnv\n                      , params : Nil\n                      , macro  : false\n                      , meta   : MalNil\n                      }\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr"
  },
  {
    "path": "impls/purs/src/step5_tco.purs",
    "content": "module Mal.Step5 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Control.Monad.Free.Trans (FreeT, runFreeT)\nimport Control.Monad.Rec.Class (class MonadRec)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.Identity (Identity(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse, traverse_)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (class MonadEffect, liftEffect)\nimport Effect.Console (error, log)\nimport Effect.Exception as Ex\nimport Env as Env\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, toHashMap, toVector)\n\n\n-- TYPES\n\ntype Eval a = FreeT Identity Effect a\n\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  re <- Env.newEnv Nil\n  traverse_ (setFn re) Core.ns\n  rep_ re \"(def! not (fn* (a) (if a false true)))\"\n  loop re\n\n\n\n-- EVAL\n\neval :: RefEnv -> MalExpr -> Eval MalExpr\neval env ast = do\n  dbgeval <- liftEffect (Env.get env \"DEBUG-EVAL\")\n  liftEffect case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- liftEffect $ Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ es@(_ : _)                    -> evalCallFn env es\n    MalVector _ es  -> toVector <$> traverse (eval env) es\n    MalHashMap _ es -> toHashMap <$> traverse (eval env) es\n    _               -> pure ast\n\n\nevalDef :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  liftEffect $ Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\nevalLet :: RefEnv -> List MalExpr -> Eval MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\n\nletBind :: RefEnv -> List MalExpr -> Eval Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  ex <- eval env e\n  liftEffect $ Env.set env ky ex\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\nevalIf :: RefEnv -> List MalExpr -> Eval MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\nevalDo :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\nevalFnMatch :: RefEnv -> List MalExpr -> Eval MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Eval MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then runEval $ eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Eval String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- REPL\n\nrep_ :: RefEnv -> String -> Effect Unit\nrep_ env str = rep env str *> pure unit\n\n\nrep :: RefEnv -> String -> Effect String\nrep env str = do\n  ast <- read str\n  result <- runEval $ eval env ast\n  print result\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                { fn     : f\n                , ast    : MalNil\n                , env    : newEnv\n                , params : Nil\n                , macro  : false\n                , meta   : MalNil\n                }\n\n\n\n-- CALL FUNCTION\n\nevalCallFn :: RefEnv -> List MalExpr -> Eval MalExpr\nevalCallFn env ast = do\n  es <- traverse (eval env) ast\n  case es of\n    MalFunction {fn:f, ast:MalNil} : args                   -> liftEffect $ f args\n    MalFunction {ast:ast', params:params', env:env'} : args -> do\n      newEnv <- liftEffect $ Env.newEnv env'\n      _ <- liftEffect $ Env.sets newEnv params' args\n      eval newEnv ast'\n    _                                                       -> throw \"invalid function\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr\n\n\n\n-- Utils\n\nrunEval :: ∀ m a. MonadRec m => FreeT Identity m a -> m a\nrunEval = runFreeT $ pure <<< runIdentity\n\n\nrunIdentity :: ∀ a. Identity a -> a\nrunIdentity (Identity a) = a\n\n\nthrow :: ∀ m a. MonadEffect m => String -> m a\nthrow = liftEffect <<< Ex.throw"
  },
  {
    "path": "impls/purs/src/step6_file.purs",
    "content": "module Mal.Step6 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Control.Monad.Free.Trans (FreeT, runFreeT)\nimport Control.Monad.Rec.Class (class MonadRec)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.Identity (Identity(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse, traverse_)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (class MonadEffect, liftEffect)\nimport Effect.Console (error, log)\nimport Effect.Exception as Ex\nimport Env as Env\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (args, readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, toHashMap, toList, toVector)\n\n\n-- TYPES\n\ntype Eval a = FreeT Identity Effect a\n\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  env <- Env.newEnv Nil\n  traverse_ (setFn env) Core.ns\n  setFn env $ Tuple \"eval\" $ setEval env\n  rep_ env \"(def! not (fn* (a) (if a false true)))\"\n  rep_ env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n  case args of\n    Nil               -> do\n      Env.set env \"*ARGV*\" $ toList Nil\n      loop env\n    script:scriptArgs -> do\n      Env.set env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n      rep_ env $ \"(load-file \\\"\" <> script <> \"\\\")\"\n\n\n\n-- REPL\n\nrep_ :: RefEnv -> String -> Effect Unit\nrep_ env str = rep env str *> pure unit\n\n\nrep :: RefEnv -> String -> Effect String\nrep env str = do\n  ast <- read str\n  result <- runEval $ eval env ast\n  print result\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                { fn     : f\n                , ast    : MalNil\n                , env    : newEnv\n                , params : Nil\n                , macro  : false\n                , meta   : MalNil\n                }\n\n\nsetEval :: RefEnv -> MalFn\nsetEval env (ast:Nil) = runEval $ eval env ast\nsetEval _ _           = throw \"illegal call of eval\"\n\n\n\n-- EVAL\n\neval :: RefEnv -> MalExpr -> Eval MalExpr\neval env ast = do\n  dbgeval <- liftEffect (Env.get env \"DEBUG-EVAL\")\n  liftEffect case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- liftEffect $ Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ es@(_ : _)                    -> evalCallFn env es\n    MalVector _ es  -> toVector <$> traverse (eval env) es\n    MalHashMap _ es -> toHashMap <$> traverse (eval env) es\n    _               -> pure ast\n\n\nevalDef :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  liftEffect $ Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\nevalLet :: RefEnv -> List MalExpr -> Eval MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\n\nletBind :: RefEnv -> List MalExpr -> Eval Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  ex <- eval env e\n  liftEffect $ Env.set env ky ex\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\nevalIf :: RefEnv -> List MalExpr -> Eval MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\nevalDo :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\nevalFnMatch :: RefEnv -> List MalExpr -> Eval MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Eval MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then runEval $ eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Eval String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- CALL FUNCTION\n\nevalCallFn :: RefEnv -> List MalExpr -> Eval MalExpr\nevalCallFn env ast = do\n  es <- traverse (eval env) ast\n  case es of\n    MalFunction {fn:f, ast:MalNil} : args                   -> liftEffect $ f args\n    MalFunction {ast:ast', params:params', env:env'} : args -> do\n      newEnv <- liftEffect $ Env.newEnv env'\n      _ <- liftEffect $ Env.sets newEnv params' args\n      eval newEnv ast'\n    _                                                       -> throw \"invalid function\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr\n\n\n\n-- Utils\n\nrunEval :: ∀ m a. MonadRec m => FreeT Identity m a -> m a\nrunEval = runFreeT $ pure <<< runIdentity\n\n\nrunIdentity :: ∀ a. Identity a -> a\nrunIdentity (Identity a) = a\n\n\nthrow :: ∀ m a. MonadEffect m => String -> m a\nthrow = liftEffect <<< Ex.throw"
  },
  {
    "path": "impls/purs/src/step7_quote.purs",
    "content": "module Mal.Step7 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Control.Monad.Free.Trans (FreeT, runFreeT)\nimport Control.Monad.Rec.Class (class MonadRec)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.Identity (Identity(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse, traverse_)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (class MonadEffect, liftEffect)\nimport Effect.Console (error, log)\nimport Effect.Exception as Ex\nimport Env as Env\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (args, readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, foldrM, toHashMap, toList, toVector)\n\n\n-- TYPES\n\ntype Eval a = FreeT Identity Effect a\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  env <- Env.newEnv Nil\n  traverse_ (setFn env) Core.ns\n  setFn env $ Tuple \"eval\" $ setEval env\n  rep_ env \"(def! not (fn* (a) (if a false true)))\"\n  rep_ env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n  case args of\n    Nil               -> do\n      Env.set env \"*ARGV*\" $ toList Nil\n      loop env\n    script:scriptArgs -> do\n      Env.set env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n      rep_ env $ \"(load-file \\\"\" <> script <> \"\\\")\"\n\n\n\n-- REPL\n\nrep_ :: RefEnv -> String -> Effect Unit\nrep_ env str = rep env str *> pure unit\n\n\nrep :: RefEnv -> String -> Effect String\nrep env str = do\n  ast <- read str\n  result <- runEval $ eval env ast\n  print result\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                { fn     : f\n                , ast    : MalNil\n                , env    : newEnv\n                , params : Nil\n                , macro  : false\n                , meta   : MalNil\n                }\n\n\nsetEval :: RefEnv -> MalFn\nsetEval env (ast:Nil) = runEval $ eval env ast\nsetEval _ _           = throw \"illegal call of eval\"\n\n\n\n-- EVAL\n\neval :: RefEnv -> MalExpr -> Eval MalExpr\neval env ast = do\n  dbgeval <- liftEffect (Env.get env \"DEBUG-EVAL\")\n  liftEffect case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- liftEffect $ Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ (MalSymbol \"quote\" : es)      -> evalQuote env es\n    MalList _ (MalSymbol \"quasiquote\" : es) -> evalQuasiquote env es\n    MalList _ es@(_ : _)                    -> evalCallFn env es\n    MalVector _ es  -> toVector <$> traverse (eval env) es\n    MalHashMap _ es -> toHashMap <$> traverse (eval env) es\n    _               -> pure ast\n\n\n\n-- Def\n\nevalDef :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  liftEffect $ Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\n\n-- Let\n\nevalLet :: RefEnv -> List MalExpr -> Eval MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\nletBind :: RefEnv -> List MalExpr -> Eval Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  ex <- eval env e\n  liftEffect $ Env.set env ky ex\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\n\n-- If\n\nevalIf :: RefEnv -> List MalExpr -> Eval MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\n\n-- Do\n\nevalDo :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\n\n-- Function\n\nevalFnMatch :: RefEnv -> List MalExpr -> Eval MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Eval MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then runEval $ eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Eval String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- Quote\n\nevalQuote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuote _ (e:Nil) = pure e\nevalQuote _ _       = throw \"invalid quote\"\n\n\nevalQuasiquote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuasiquote env (e:Nil) = eval env =<< quasiquote e\nevalQuasiquote  _ _        = throw \"invalid quasiquote\"\n\n\nquasiquote :: MalExpr -> Eval MalExpr\nquasiquote (MalList _ (MalSymbol \"unquote\" : x : Nil)) = pure x\nquasiquote (MalList _ (MalSymbol \"unquote\" : _))       = throw \"invalid unquote\"\nquasiquote (MalList _ xs)                              = foldrM qqIter (toList Nil) xs\nquasiquote (MalVector _ xs)                            = do\n  lst <- foldrM qqIter (toList Nil) xs\n  pure $ toList $ MalSymbol \"vec\" : lst : Nil\nquasiquote ast@(MalHashMap _ _)                        = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast@(MalSymbol _)                           = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast                                         = pure ast\n\n\nqqIter :: MalExpr -> MalExpr -> Eval MalExpr\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : x : Nil)) acc = pure $ toList $ MalSymbol \"concat\" : x : acc : Nil\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : _)) _         = throw \"invalid splice-unquote\"\nqqIter elt acc                                                = do\n  qqted <- quasiquote elt\n  pure $ toList $ MalSymbol \"cons\" : qqted : acc : Nil\n\n\n\n-- CALL FUNCTION\n\nevalCallFn :: RefEnv -> List MalExpr -> Eval MalExpr\nevalCallFn env ast = do\n  es <- traverse (eval env) ast\n  case es of\n    MalFunction {fn:f, ast:MalNil} : args                   -> liftEffect $ f args\n    MalFunction {ast:ast', params:params', env:env'} : args -> do\n      newEnv <- liftEffect $ Env.newEnv env'\n      _ <- liftEffect $ Env.sets newEnv params' args\n      eval newEnv ast'\n    _                                                       -> throw \"invalid function\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr\n\n\n\n-- Utils\n\nrunEval :: ∀ m a. MonadRec m => FreeT Identity m a -> m a\nrunEval = runFreeT $ pure <<< runIdentity\n\n\nrunIdentity :: ∀ a. Identity a -> a\nrunIdentity (Identity a) = a\n\n\nthrow :: ∀ m a. MonadEffect m => String -> m a\nthrow = liftEffect <<< Ex.throw"
  },
  {
    "path": "impls/purs/src/step8_macros.purs",
    "content": "module Mal.Step8 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Control.Monad.Free.Trans (FreeT, runFreeT)\nimport Control.Monad.Rec.Class (class MonadRec)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.Identity (Identity(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse, traverse_)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (class MonadEffect, liftEffect)\nimport Effect.Console (error, log)\nimport Effect.Exception as Ex\nimport Env as Env\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (args, readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, foldrM, toHashMap, toList, toVector)\n\n\n-- TYPES\n\ntype Eval a = FreeT Identity Effect a\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  env <- Env.newEnv Nil\n  traverse_ (setFn env) Core.ns\n  setFn env $ Tuple \"eval\" $ setEval env\n  rep_ env \"(def! not (fn* (a) (if a false true)))\"\n  rep_ env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n  rep_ env \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n  case args of\n    Nil               -> do\n      Env.set env \"*ARGV*\" $ toList Nil\n      loop env\n    script:scriptArgs -> do\n      Env.set env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n      rep_ env $ \"(load-file \\\"\" <> script <> \"\\\")\"\n\n\n\n-- REPL\n\nrep_ :: RefEnv -> String -> Effect Unit\nrep_ env str = rep env str *> pure unit\n\n\nrep :: RefEnv -> String -> Effect String\nrep env str = do\n  ast <- read str\n  result <- runEval $ eval env ast\n  print result\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                { fn     : f\n                , ast    : MalNil\n                , env    : newEnv\n                , params : Nil\n                , macro  : false\n                , meta   : MalNil\n                }\n\n\nsetEval :: RefEnv -> MalFn\nsetEval env (ast:Nil) = runEval $ eval env ast\nsetEval _ _           = throw \"illegal call of eval\"\n\n\n\n-- EVAL\n\neval :: RefEnv -> MalExpr -> Eval MalExpr\neval env ast = do\n  dbgeval <- liftEffect (Env.get env \"DEBUG-EVAL\")\n  liftEffect case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- liftEffect $ Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ (MalSymbol \"quote\" : es)      -> evalQuote env es\n    MalList _ (MalSymbol \"quasiquote\" : es) -> evalQuasiquote env es\n    MalList _ (MalSymbol \"defmacro!\" : es)  -> evalDefmacro env es\n    MalList _ (rawFunc : rawArgs)           -> evalCallFn env rawFunc rawArgs\n    MalVector _ es   -> toVector <$> traverse (eval env) es\n    MalHashMap _ es  -> toHashMap <$> traverse (eval env) es\n    _             -> pure ast\n\n\n\n-- DEF\n\nevalDef :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  liftEffect $ Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\n\n-- LET\n\nevalLet :: RefEnv -> List MalExpr -> Eval MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\nletBind :: RefEnv -> List MalExpr -> Eval Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  ex <- eval env e\n  liftEffect $ Env.set env ky ex\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\n\n-- IF\n\nevalIf :: RefEnv -> List MalExpr -> Eval MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\n\n-- DO\n\nevalDo :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\n\n-- FUNCTION\n\nevalFnMatch :: RefEnv -> List MalExpr -> Eval MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Eval MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then runEval $ eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Eval String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- QUOTE\n\nevalQuote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuote _ (e:Nil) = pure e\nevalQuote _ _       = throw \"invalid quote\"\n\n\nevalQuasiquote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuasiquote env (e:Nil) = eval env =<< quasiquote e\nevalQuasiquote  _ _        = throw \"invalid quasiquote\"\n\n\nquasiquote :: MalExpr -> Eval MalExpr\nquasiquote (MalList _ (MalSymbol \"unquote\" : x : Nil)) = pure x\nquasiquote (MalList _ (MalSymbol \"unquote\" : _))       = throw \"invalid unquote\"\nquasiquote (MalList _ xs)                              = foldrM qqIter (toList Nil) xs\nquasiquote (MalVector _ xs)                            = do\n  lst <- foldrM qqIter (toList Nil) xs\n  pure $ toList $ MalSymbol \"vec\" : lst : Nil\nquasiquote ast@(MalHashMap _ _)                        = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast@(MalSymbol _)                           = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast                                         = pure ast\n\n\nqqIter :: MalExpr -> MalExpr -> Eval MalExpr\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : x : Nil)) acc = pure $ toList $ MalSymbol \"concat\" : x : acc : Nil\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : _)) _         = throw \"invalid splice-unquote\"\nqqIter elt acc                                                = do\n  qqted <- quasiquote elt\n  pure $ toList $ MalSymbol \"cons\" : qqted : acc : Nil\n\n\n\n-- MACRO\n\nevalDefmacro :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDefmacro env (MalSymbol a : b : Nil) = do\n  f <- eval env b\n  case f of\n    MalFunction fn@{macro:false} -> do\n      let m = MalFunction $ fn {macro = true}\n      liftEffect $ Env.set env a m\n      pure m\n    _                            -> throw \"defmacro! on non-function\"\nevalDefmacro _ _                         = throw \"invalid defmacro!\"\n\n\n-- CALL FUNCTION\n\nevalCallFn :: RefEnv -> MalExpr -> List MalExpr -> Eval MalExpr\nevalCallFn env rawFunc rawArgs = do\n  func <- eval env rawFunc\n  case func of\n    MalFunction {fn:f, macro:true} -> do\n      newAst <- liftEffect $ f rawArgs\n      eval env newAst\n    MalFunction {fn:f, ast:MalNil} -> do\n      args <- traverse (eval env) rawArgs\n      liftEffect $ f args\n    MalFunction {ast:ast', params:params', env:env'} -> do\n      args <- traverse (eval env) rawArgs\n      newEnv <- liftEffect $ Env.newEnv env'\n      _ <- liftEffect $ Env.sets newEnv params' args\n      eval newEnv ast'\n    _                                                       -> throw \"invalid function\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr\n\n\n\n-- Utils\n\nrunEval :: ∀ m a. MonadRec m => FreeT Identity m a -> m a\nrunEval = runFreeT $ pure <<< runIdentity\n\n\nrunIdentity :: ∀ a. Identity a -> a\nrunIdentity (Identity a) = a\n\n\nthrow :: ∀ m a. MonadEffect m => String -> m a\nthrow = liftEffect <<< Ex.throw"
  },
  {
    "path": "impls/purs/src/step9_try.purs",
    "content": "module Mal.Step9 where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Control.Monad.Free.Trans (FreeT, runFreeT)\nimport Control.Monad.Rec.Class (class MonadRec)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.Identity (Identity(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse, traverse_)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (class MonadEffect, liftEffect)\nimport Effect.Console (error, log)\nimport Effect.Exception as Ex\nimport Env as Env\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (args, readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, foldrM, toHashMap, toList, toVector)\n\n\n\n-- TYPES\n\ntype Eval a = FreeT Identity Effect a\n\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  env <- Env.newEnv Nil\n  traverse_ (setFn env) Core.ns\n  setFn env $ Tuple \"eval\" $ setEval env\n  rep_ env \"(def! not (fn* (a) (if a false true)))\"\n  rep_ env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n  rep_ env \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n  case args of\n    Nil               -> do\n      Env.set env \"*ARGV*\" $ toList Nil\n      loop env\n    script:scriptArgs -> do\n      Env.set env \"*ARGV*\" $ toList $ MalString <$> scriptArgs\n      rep_ env $ \"(load-file \\\"\" <> script <> \"\\\")\"\n\n\n\n-- REPL\n\nrep_ :: RefEnv -> String -> Effect Unit\nrep_ env str = rep env str *> pure unit\n\n\nrep :: RefEnv -> String -> Effect String\nrep env str = do\n  ast <- read str\n  result <- runEval $ eval env ast\n  print result\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                { fn     : f\n                , ast    : MalNil\n                , env    : newEnv\n                , params : Nil\n                , macro  : false\n                , meta   : MalNil\n                }\n\n\nsetEval :: RefEnv -> MalFn\nsetEval env (ast:Nil) = runEval $ eval env ast\nsetEval _ _           = throw \"illegal call of eval\"\n\n\n\n-- EVAL\n\neval :: RefEnv -> MalExpr -> Eval MalExpr\neval env ast = do\n  dbgeval <- liftEffect (Env.get env \"DEBUG-EVAL\")\n  liftEffect case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- liftEffect $ Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ (MalSymbol \"quote\" : es)      -> evalQuote env es\n    MalList _ (MalSymbol \"quasiquote\" : es) -> evalQuasiquote env es\n    MalList _ (MalSymbol \"defmacro!\" : es)  -> evalDefmacro env es\n    MalList _ (MalSymbol \"try*\" : es)       -> liftEffect $ evalTry env es\n    MalList _ (rawFunc : rawArgs)           -> evalCallFn env rawFunc rawArgs\n    MalVector _ es   -> toVector <$> traverse (eval env) es\n    MalHashMap _ es  -> toHashMap <$> traverse (eval env) es\n    _             -> pure ast\n\n\n\n-- Def\n\nevalDef :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  liftEffect $ Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\n\n-- Let\n\nevalLet :: RefEnv -> List MalExpr -> Eval MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\nletBind :: RefEnv -> List MalExpr -> Eval Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  ex <- eval env e\n  liftEffect $ Env.set env ky ex\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\n\n-- If\n\nevalIf :: RefEnv -> List MalExpr -> Eval MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\n\n-- Do\n\nevalDo :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\n\n-- Function\n\nevalFnMatch :: RefEnv -> List MalExpr -> Eval MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Eval MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then runEval $ eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Eval String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- Quote\n\nevalQuote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuote _ (e:Nil) = pure e\nevalQuote _ _       = throw \"invalid quote\"\n\n\nevalQuasiquote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuasiquote env (e:Nil) = eval env =<< quasiquote e\nevalQuasiquote  _ _        = throw \"invalid quasiquote\"\n\n\nquasiquote :: MalExpr -> Eval MalExpr\nquasiquote (MalList _ (MalSymbol \"unquote\" : x : Nil)) = pure x\nquasiquote (MalList _ (MalSymbol \"unquote\" : _))       = throw \"invalid unquote\"\nquasiquote (MalList _ xs)                              = foldrM qqIter (toList Nil) xs\nquasiquote (MalVector _ xs)                            = do\n  lst <- foldrM qqIter (toList Nil) xs\n  pure $ toList $ MalSymbol \"vec\" : lst : Nil\nquasiquote ast@(MalHashMap _ _)                        = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast@(MalSymbol _)                           = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast                                         = pure ast\n\n\nqqIter :: MalExpr -> MalExpr -> Eval MalExpr\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : x : Nil)) acc = pure $ toList $ MalSymbol \"concat\" : x : acc : Nil\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : _)) _         = throw \"invalid splice-unquote\"\nqqIter elt acc                                                = do\n  qqted <- quasiquote elt\n  pure $ toList $ MalSymbol \"cons\" : qqted : acc : Nil\n\n\n\n-- Macro\n\nevalDefmacro :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDefmacro env (MalSymbol a : b : Nil) = do\n  f <- eval env b\n  case f of\n    MalFunction fn@{macro:false} -> do\n      let m = MalFunction $ fn {macro = true}\n      liftEffect $ Env.set env a m\n      pure m\n    _                            -> throw \"defmacro! on non-function\"\nevalDefmacro _ _                         = throw \"invalid defmacro!\"\n\n\n-- Try\n\nevalTry :: RefEnv -> List MalExpr -> Effect MalExpr\nevalTry env (a:Nil) = runEval $ eval env a\nevalTry env (thw : MalList _ (MalSymbol \"catch*\" : MalSymbol e : b : Nil) : Nil) = do\n  res <- try $ runEval $ eval env thw\n  case res of\n    Left err -> do\n      tryEnv <- Env.newEnv env\n      Env.set tryEnv e $ MalString $ Ex.message err -- FIXME:\n      runEval $ eval tryEnv b\n    Right v -> pure v\nevalTry _ _         = Ex.throw \"invalid try*\"\n\n\n\n-- CALL FUNCTION\n\nevalCallFn :: RefEnv -> MalExpr -> List MalExpr -> Eval MalExpr\nevalCallFn env rawFunc rawArgs = do\n  func <- eval env rawFunc\n  case func of\n    MalFunction {fn:f, macro:true} -> do\n      newAst <- liftEffect $ f rawArgs\n      eval env newAst\n    MalFunction {fn:f, ast:MalNil} -> do\n      args <- traverse (eval env) rawArgs\n      liftEffect $ f args\n    MalFunction {ast:ast', params:params', env:env'} -> do\n      args <- traverse (eval env) rawArgs\n      newEnv <- liftEffect $ Env.newEnv env'\n      _ <- liftEffect $ Env.sets newEnv params' args\n      eval newEnv ast'\n    _                                                       -> throw \"invalid function\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr\n\n\n\n-- Utils\n\nrunEval :: ∀ m a. MonadRec m => FreeT Identity m a -> m a\nrunEval = runFreeT $ pure <<< runIdentity\n\n\nrunIdentity :: ∀ a. Identity a -> a\nrunIdentity (Identity a) = a\n\n\nthrow :: ∀ m a. MonadEffect m => String -> m a\nthrow = liftEffect <<< Ex.throw"
  },
  {
    "path": "impls/purs/src/stepA_mal.purs",
    "content": "module Mal.StepA where\n\nimport Prelude\n\nimport Control.Monad.Error.Class (try)\nimport Control.Monad.Free.Trans (FreeT, runFreeT)\nimport Control.Monad.Rec.Class (class MonadRec)\nimport Core as Core\nimport Data.Either (Either(..))\nimport Data.Identity (Identity(..))\nimport Data.List (List(..), foldM, (:))\nimport Data.Maybe (Maybe(..))\nimport Data.Traversable (traverse, traverse_)\nimport Data.Tuple (Tuple(..))\nimport Effect (Effect)\nimport Effect.Class (class MonadEffect, liftEffect)\nimport Effect.Console (error, log)\nimport Effect.Exception as Ex\nimport Env as Env\nimport Printer (printStr)\nimport Reader (readStr)\nimport Readline (args, readLine)\nimport Types (MalExpr(..), MalFn, RefEnv, foldrM, toHashMap, toList, toVector)\n\n\n\n-- TYPES\n\ntype Eval a = FreeT Identity Effect a\n\n\n\n-- MAIN\n\nmain :: Effect Unit\nmain = do\n  let as = args\n  env <- Env.newEnv Nil\n  traverse_ (setFn env) Core.ns\n  setFn env (Tuple \"eval\" $ setEval env)\n  rep_ env \"(def! *host-language* \\\"purescript\\\")\"\n  rep_ env \"(def! not (fn* (a) (if a false true)))\"\n  rep_ env \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n  rep_ env \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n  case as of\n    Nil         -> do\n      Env.set env \"*ARGV*\" $ toList Nil\n      rep_ env \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"\n      loop env\n    script:args -> do\n      Env.set env \"*ARGV*\" $ toList $ MalString <$> args\n      rep_ env $ \"(load-file \\\"\" <> script <> \"\\\")\"\n\n\n\n-- REPL\n\nrep_ :: RefEnv -> String -> Effect Unit\nrep_ env str = rep env str *> pure unit\n\n\nrep :: RefEnv -> String -> Effect String\nrep env str = do\n  ast <- read str\n  result <- runEval $ eval env ast\n  print result\n\n\nloop :: RefEnv -> Effect Unit\nloop env = do\n  line <- readLine \"user> \"\n  case line of\n    \"\"   -> loop env\n    \":q\" -> pure unit\n    _    -> do\n      result <- try $ rep env line\n      case result of\n        Right exp -> log exp\n        Left err  -> error $ show err\n      loop env\n\n\nsetFn :: RefEnv -> Tuple String MalFn -> Effect Unit\nsetFn env (Tuple sym f) = do\n  newEnv <- Env.newEnv Nil\n  Env.set env sym $ MalFunction\n                { fn     : f\n                , ast    : MalNil\n                , env    : newEnv\n                , params : Nil\n                , macro  : false\n                , meta   : MalNil\n                }\n\n\nsetEval :: RefEnv -> MalFn\nsetEval env (ast:Nil) = runEval $ eval env ast\nsetEval _ _           = throw \"illegal call of eval\"\n\n\n\n-- EVAL\n\neval :: RefEnv -> MalExpr -> Eval MalExpr\neval env ast = do\n  dbgeval <- liftEffect (Env.get env \"DEBUG-EVAL\")\n  liftEffect case dbgeval of\n    Nothing                 -> pure unit\n    Just MalNil             -> pure unit\n    Just (MalBoolean false) -> pure unit\n    _                       -> do\n      image <- print ast\n      log (\"EVAL: \" <> image)\n  case ast of\n    MalSymbol s   -> do\n      result <- liftEffect $ Env.get env s\n      case result of\n        Just k  -> pure k\n        Nothing -> throw $ \"'\" <> s <> \"'\" <> \" not found\"\n    MalList _ (MalSymbol \"def!\" : es)       -> evalDef env es\n    MalList _ (MalSymbol \"let*\" : es)       -> evalLet env es\n    MalList _ (MalSymbol \"if\" : es)         -> evalIf env es\n    MalList _ (MalSymbol \"do\" : es)         -> evalDo env es\n    MalList _ (MalSymbol \"fn*\" : es)        -> evalFnMatch env es\n    MalList _ (MalSymbol \"quote\" : es)      -> evalQuote env es\n    MalList _ (MalSymbol \"quasiquote\" : es) -> evalQuasiquote env es\n    MalList _ (MalSymbol \"defmacro!\" : es)  -> evalDefmacro env es\n    MalList _ (MalSymbol \"try*\" : es)       -> liftEffect $ evalTry env es\n    MalList _ (rawFunc : rawArgs)           -> evalCallFn env rawFunc rawArgs\n    MalVector _ es   -> toVector <$> traverse (eval env) es\n    MalHashMap _ es  -> toHashMap <$> traverse (eval env) es\n    _             -> pure ast\n\n\n\n-- Def\n\nevalDef :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDef env (MalSymbol v : e : Nil) = do\n  evd <- eval env e\n  liftEffect $ Env.set env v evd\n  pure evd\nevalDef _ _                         = throw \"invalid def!\"\n\n\n\n-- Let\n\nevalLet :: RefEnv -> List MalExpr -> Eval MalExpr\nevalLet env (MalList _ ps : e : Nil)   = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet env (MalVector _ ps : e : Nil) = do\n  letEnv <- liftEffect $ Env.newEnv env\n  letBind letEnv ps\n  eval letEnv e\nevalLet _ _                            = throw \"invalid let*\"\n\n\nletBind :: RefEnv -> List MalExpr -> Eval Unit\nletBind _ Nil                       = pure unit\nletBind env (MalSymbol ky : e : es) = do\n  ex <- eval env e\n  liftEffect $ Env.set env ky ex\n  letBind env es\nletBind _ _                         = throw \"invalid let*\"\n\n\n\n-- If\n\nevalIf :: RefEnv -> List MalExpr -> Eval MalExpr\nevalIf env (b:t:e:Nil) = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> e\n    MalBoolean false -> e\n    _                -> t\nevalIf env (b:t:Nil)   = do\n  cond <- eval env b\n  eval env case cond of\n    MalNil           -> MalNil\n    MalBoolean false -> MalNil\n    _                -> t\nevalIf _ _             = throw \"invalid if\"\n\n\n\n-- Do\n\nevalDo :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDo env es = foldM (const $ eval env) MalNil es\n\n\n\n-- Function\n\nevalFnMatch :: RefEnv -> List MalExpr -> Eval MalExpr\nevalFnMatch env (MalList _ params : body : Nil)   = evalFn env params body\nevalFnMatch env (MalVector _ params : body : Nil) = evalFn env params body\nevalFnMatch _ _                                   = throw \"invalid fn*\"\n\n\nevalFn :: RefEnv -> List MalExpr -> MalExpr -> Eval MalExpr\nevalFn env params body = do\n  paramsStr <- traverse unwrapSymbol params\n  pure $ MalFunction { fn     : fn paramsStr body\n                     , ast    : body\n                     , env    : env\n                     , params : paramsStr\n                     , macro  : false\n                     , meta   : MalNil\n                     }\n  where\n\n  fn :: List String -> MalExpr -> MalFn\n  fn params' body' = \\args -> do\n    fnEnv <- Env.newEnv env\n    ok <- Env.sets fnEnv params' args\n    if ok\n      then runEval $ eval fnEnv body'\n      else throw \"actual parameters do not match signature \"\n\n  unwrapSymbol :: MalExpr -> Eval String\n  unwrapSymbol (MalSymbol s) = pure s\n  unwrapSymbol _             = throw \"fn* parameter must be symbols\"\n\n\n\n-- Quote\n\nevalQuote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuote _ (e:Nil) = pure e\nevalQuote _ _       = throw \"invalid quote\"\n\n\nevalQuasiquote :: RefEnv -> List MalExpr -> Eval MalExpr\nevalQuasiquote env (e:Nil) = eval env =<< quasiquote e\nevalQuasiquote  _ _        = throw \"invalid quasiquote\"\n\n\nquasiquote :: MalExpr -> Eval MalExpr\nquasiquote (MalList _ (MalSymbol \"unquote\" : x : Nil)) = pure x\nquasiquote (MalList _ (MalSymbol \"unquote\" : _))       = throw \"invalid unquote\"\nquasiquote (MalList _ xs)                              = foldrM qqIter (toList Nil) xs\nquasiquote (MalVector _ xs)                            = do\n  lst <- foldrM qqIter (toList Nil) xs\n  pure $ toList $ MalSymbol \"vec\" : lst : Nil\nquasiquote ast@(MalHashMap _ _)                        = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast@(MalSymbol _)                           = pure $ toList $ MalSymbol \"quote\" : ast : Nil\nquasiquote ast                                         = pure ast\n\n\nqqIter :: MalExpr -> MalExpr -> Eval MalExpr\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : x : Nil)) acc = pure $ toList $ MalSymbol \"concat\" : x : acc : Nil\nqqIter (MalList _ (MalSymbol \"splice-unquote\" : _)) _         = throw \"invalid splice-unquote\"\nqqIter elt acc                                                = do\n  qqted <- quasiquote elt\n  pure $ toList $ MalSymbol \"cons\" : qqted : acc : Nil\n\n\n\n-- Macro\n\nevalDefmacro :: RefEnv -> List MalExpr -> Eval MalExpr\nevalDefmacro env (MalSymbol a : b : Nil) = do\n  f <- eval env b\n  case f of\n    MalFunction fn@{macro:false} -> do\n      let m = MalFunction $ fn {macro = true}\n      liftEffect $ Env.set env a m\n      pure m\n    _                            -> throw \"defmacro! on non-function\"\nevalDefmacro _ _                         = throw \"invalid defmacro!\"\n\n\n-- Try\n\nevalTry :: RefEnv -> List MalExpr -> Effect MalExpr\nevalTry env (a:Nil) = runEval $ eval env a\nevalTry env (thw : MalList _ (MalSymbol \"catch*\" : MalSymbol e : b : Nil) : Nil) = do\n  res <- try $ runEval $ eval env thw\n  case res of\n    Left err -> do\n      tryEnv <- Env.newEnv env\n      Env.set tryEnv e $ MalString $ Ex.message err -- FIXME:\n      runEval $ eval tryEnv b\n    Right v -> pure v\nevalTry _ _         = Ex.throw \"invalid try*\"\n\n\n\n-- CALL FUNCTION\n\nevalCallFn :: RefEnv -> MalExpr -> List MalExpr -> Eval MalExpr\nevalCallFn env rawFunc rawArgs = do\n  func <- eval env rawFunc\n  case func of\n    MalFunction {fn:f, macro:true} -> do\n      newAst <- liftEffect $ f rawArgs\n      eval env newAst\n    MalFunction {fn:f, ast:MalNil} -> do\n      args <- traverse (eval env) rawArgs\n      liftEffect $ f args\n    MalFunction {ast:ast', params:params', env:env'} -> do\n      args <- traverse (eval env) rawArgs\n      newEnv <- liftEffect $ Env.newEnv env'\n      _ <- liftEffect $ Env.sets newEnv params' args\n      eval newEnv ast'\n    _                                                       -> throw \"invalid function\"\n\n\n\n-- READ\n\nread :: String -> Effect MalExpr\nread = readStr\n\n\n\n-- PRINT\n\nprint :: MalExpr -> Effect String\nprint = printStr\n\n\n\n-- Utils\n\nrunEval :: ∀ m a. MonadRec m => FreeT Identity m a -> m a\nrunEval = runFreeT $ pure <<< runIdentity\n\n\nrunIdentity :: ∀ a. Identity a -> a\nrunIdentity (Identity a) = a\n\n\nthrow :: ∀ m a. MonadEffect m => String -> m a\nthrow = liftEffect <<< Ex.throw"
  },
  {
    "path": "impls/python2/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install python2\n\n# For dist packaging\nRUN apt-get -y install zip\n"
  },
  {
    "path": "impls/python2/Makefile",
    "content": "SOURCES_BASE = mal_readline.py mal_types.py reader.py printer.py\nSOURCES_LISP = env.py core.py stepA_mal.py\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.pyz mal\n\nSHELL := bash\nmal.pyz: $(SOURCES)\n\tcp stepA_mal.py __main__.py\n\tzip -q - __main__.py $+ > $@\n\trm __main__.py\n\nmal: mal.pyz\n\techo '#!/usr/bin/env python' > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.pyz mal\n"
  },
  {
    "path": "impls/python2/core.py",
    "content": "import operator\nimport time\nfrom itertools import chain\n\nimport mal_types as types\nfrom mal_types import MalException, List, Vector\nimport mal_readline\nimport reader\nimport printer\n\n# Errors/Exceptions\ndef throw(obj): raise MalException(obj)\n\n\n# String functions\ndef pr_str(*args):\n    return printer.pr_list(args, \" \", True)\n\ndef do_str(*args):\n    return printer.pr_list(args, \"\", False)\n\ndef prn(*args):\n    print(printer.pr_list(args, \" \", True))\n    return None\n\ndef println(*args):\n    print(printer.pr_list(args, \" \", False))\n    return None\n\ndef core_readline(prompt):\n    try:\n        return mal_readline.readline(prompt)\n    except EOFError:\n        return None\n\ndef slurp(path):\n    with open(path) as f:\n        return f.read()\n\n# Hash map functions\ndef assoc(src_hm, *key_vals):\n    hm = types.Hash_Map(src_hm)\n    hm.update(types.asPairs(key_vals))\n    return hm\n\ndef dissoc(src_hm, *keys):\n    hm = types.Hash_Map(src_hm)\n    for key in keys:\n        hm.pop(key, None)\n    return hm\n\ndef get(hm, key):\n    if hm is not None:\n        return hm.get(key)\n    else:\n        return None\n\ncontains_Q = types.Hash_Map.__contains__\n\nkeys = List\n\ndef vals(hm): return List(hm.values())\n\n\n# Sequence functions\ndef cons(x, seq): return concat((x,), seq)\n\ndef concat(*lsts): return List(chain(*lsts))\n\nnth = tuple.__getitem__\n\ndef first(lst):\n    if lst:\n        return lst[0]\n    else:                       # lst is nil or empty\n        return None\n\ndef rest(lst):\n    if lst:\n        it = iter(lst)\n        next(it)\n        return List(it)\n    else:                       # lst is nil or empty\n        return List()\n\nempty_Q = operator.not_\n\ndef count(lst):\n    if types._nil_Q(lst): return 0\n    else: return len(lst)\n\ndef apply(f, *args): return f(*chain(args[:-1], args[-1]))\n\ndef mapf(f, lst): return List(map(f, lst))\n\ndef conj(lst, *args):\n    if types._list_Q(lst):\n        return concat(reversed(args), lst)\n    else:\n        return Vector(chain(lst, args))\n\ndef seq(obj):\n    if not obj:\n        return None             # obj is nil, (), [] or \"\"\n    if types._list_Q(obj):\n        return obj\n    elif types._vector_Q(obj) or types._string_Q(obj):\n        return List(obj)\n    else: throw (\"seq: called on non-sequence\")\n\n# Metadata functions\ndef with_meta(obj, meta):\n    new_obj = types._clone(obj)\n    new_obj.__meta__ = meta\n    return new_obj\n\ndef meta(obj):\n    return getattr(obj, \"__meta__\", None)\n\n\n# Atoms functions\ndef deref(atm):    return atm.val\ndef reset_BANG(atm,val):\n    atm.val = val\n    return atm.val\ndef swap_BANG(atm,f,*args):\n    atm.val = f(atm.val,*args)\n    return atm.val\n\n\nns = {\n        '=': types._equal_Q,\n        'throw': throw,\n        'nil?': types._nil_Q,\n        'true?': types._true_Q,\n        'false?': types._false_Q,\n        'number?': types._number_Q,\n        'string?': types._string_Q,\n        'symbol': types._symbol,\n        'symbol?': types._symbol_Q,\n        'keyword': types._keyword,\n        'keyword?': types._keyword_Q,\n        'fn?': lambda x: (types._function_Q(x) and not hasattr(x, '_ismacro_')),\n        'macro?': lambda x: (types._function_Q(x) and\n                             hasattr(x, '_ismacro_')),\n\n        'pr-str': pr_str,\n        'str': do_str,\n        'prn': prn,\n        'println': println,\n        'readline': core_readline,\n        'read-string': reader.read_str,\n        'slurp': slurp,\n        '<':  operator.lt,\n        '<=': operator.le,\n        '>':  operator.gt,\n        '>=': operator.ge,\n        '+':  operator.add,\n        '-':  operator.sub,\n        '*':  operator.mul,\n        '/':  operator.floordiv,\n        'time-ms': lambda : int(time.time() * 1000),\n\n        'list': types._list,\n        'list?': types._list_Q,\n        'vector': types._vector,\n        'vector?': types._vector_Q,\n        'hash-map': types._hash_map,\n        'map?': types._hash_map_Q,\n        'assoc': assoc,\n        'dissoc': dissoc,\n        'get': get,\n        'contains?': contains_Q,\n        'keys': keys,\n        'vals': vals,\n\n        'sequential?': types._sequential_Q,\n        'cons': cons,\n        'concat': concat,\n        'vec': Vector,\n        'nth': nth,\n        'first': first,\n        'rest': rest,\n        'empty?': empty_Q,\n        'count': count,\n        'apply': apply,\n        'map': mapf,\n\n        'conj': conj,\n        'seq': seq,\n\n        'with-meta': with_meta,\n        'meta': meta,\n        'atom': types._atom,\n        'atom?': types._atom_Q,\n        'deref': deref,\n        'reset!': reset_BANG,\n        'swap!': swap_BANG,\n}\n"
  },
  {
    "path": "impls/python2/env.py",
    "content": "# Environment\nfrom mal_types import List\n\nclass Env():\n    def __init__(self, outer=None, binds=None, exprs=None):\n        \"\"\"If binds is not None, exprs must be an iterable..\"\"\"\n        self.data = {}\n        self.outer = outer\n\n        if binds:\n            exprs_it = iter(exprs)\n            for i in range(len(binds)):\n                if binds[i] == \"&\":\n                    # binds may be a non-list iterable\n                    self.data[binds[i+1]] = List(exprs_it)\n                    break\n                else:\n                    self.data[binds[i]] = next(exprs_it)\n\n    def set(self, key, value):\n        self.data[key] = value\n        return value\n\n    def get(self, key, return_nil=False):\n        # Python prefers iteration over recursion.\n        env = self\n        while key not in env.data:\n            env = env.outer\n            if env is None:\n                if return_nil:\n                    return None\n                raise Exception(\"'\" + key + \"' not found\")\n        return env.data[key]\n"
  },
  {
    "path": "impls/python2/mal_readline.py",
    "content": "# Importing this module is sufficient for the 'input' builtin command\n# to support readline.\n\nimport atexit\nimport os.path\nfrom readline import read_history_file, set_history_length, write_history_file\nimport sys\n\nif sys.version_info[0] < 3:\n    _exc = Exception\n    readline = raw_input\nelse:\n    _exc = FileNotFoundError\n    readline = input\n\nhistfile = os.path.join(os.path.expanduser(\"~\"), \".mal-history\")\ntry:\n    read_history_file(histfile)\nexcept _exc:\n    pass\nset_history_length(1000)\n\natexit.register(write_history_file, histfile)\n"
  },
  {
    "path": "impls/python2/mal_types.py",
    "content": "import copy\n\n# General functions\n\ndef _equal_Q(a, b):\n    if _sequential_Q(a):\n        return _sequential_Q(b) \\\n            and len(a) == len(b) \\\n            and all(_equal_Q(a[k], b[k]) for k in range(len(a)))\n    elif _hash_map_Q(a):\n        return _hash_map_Q(b) \\\n            and len(a) == len(b) \\\n            and all(k in b and _equal_Q(v, b[k]) for k, v in a.items())\n    else:\n        return type(a) == type(b) \\\n            and a == b\n\ndef _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)\n\ndef _clone(obj):\n    if callable(obj):\n        def fn(*args):\n            return obj(*args)\n        if hasattr(obj, '__ast__'):\n            fn.__ast__ = obj.__ast__\n            fn.__gen_env__ = obj.__gen_env__\n        return fn\n    return copy.copy(obj)\n\n#\n# Exception type\n#\n\nclass MalException(Exception):\n    def __init__(self, object):\n        self.object = object\n\n# Scalars\ndef _nil_Q(exp):    return exp is None\ndef _true_Q(exp):   return exp is True\ndef _false_Q(exp):  return exp is False\ndef _string_Q(exp):\n    return type(exp) == str and not exp.startswith(keywordPrefix)\ndef _number_Q(exp): return type(exp) == int\n\n# Symbols\nclass Symbol(str): pass\n_symbol = Symbol\ndef _symbol_Q(exp): return type(exp) == Symbol\n\n# Keywords\n# A specially prefixed string\nkeywordPrefix = '\\x7F'\ndef _keyword(str):\n    if str.startswith(keywordPrefix):\n        return str\n    else:\n        return keywordPrefix + str\ndef _keyword_Q(exp):\n    return type(exp) == str and exp.startswith(keywordPrefix)\n\n# Functions\n# are just python functions, with\n# * no attributes (core functions)\n# * __ast__ and __gen_env__ attributes (user-defined functions)\n# * __ast__, __gen_env__ and _ismacro_ attributes (macro).\n_function_Q = callable\n\n# lists\nclass List(tuple):\n    pass\ndef _list(*vals): return List(vals)\ndef _list_Q(exp):   return type(exp) == List\n\n\n# vectors\nclass Vector(tuple):\n    pass\ndef _vector(*vals): return Vector(vals)\ndef _vector_Q(exp): return type(exp) == Vector\n\n# Hash maps\nclass Hash_Map(dict): pass\ndef _hash_map(*key_vals):\n    return Hash_Map(asPairs(key_vals))\ndef _hash_map_Q(exp): return type(exp) == Hash_Map\n\ndef asPairs(iterable):\n    \"\"\" k0, v0, k1, v1..  ->  (k0, v0), (k1, v1).. \"\"\"\n    it = iter(iterable)\n    return zip(it, it)\n\n# atoms\nclass Atom(object):\n    def __init__(self, val):\n        self.val = val\n_atom = Atom\ndef _atom_Q(exp):   return type(exp) == Atom\n\ndef py_to_mal(obj):\n        if type(obj) == list:   return List(obj)\n        if type(obj) == tuple:  return List(obj)\n        elif type(obj) == dict: return Hash_Map(obj)\n        else:                   return obj\n"
  },
  {
    "path": "impls/python2/printer.py",
    "content": "from itertools import chain\n\nimport mal_types as types\n\ndef _escape(s):\n    return s.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"').replace('\\n', '\\\\n')\n\ndef _pr_str(obj, print_readably=True):\n    _r = print_readably\n    if types._list_Q(obj):\n        return \"(\" + pr_list(obj, \" \", _r) + \")\"\n    elif types._vector_Q(obj):\n        return \"[\" + pr_list(obj, \" \", _r) + \"]\"\n    elif types._hash_map_Q(obj):\n        ret = pr_list(chain.from_iterable(obj.items()), \" \", _r)\n        return \"{\" + ret + \"}\"\n    elif types._keyword_Q(obj):\n        return ':' + obj[1:]\n    elif types._string_Q(obj):\n        if _r:\n            return '\"' + _escape(obj) + '\"'\n        else:\n            return obj\n    elif types._nil_Q(obj):\n        return \"nil\"\n    elif types._true_Q(obj):\n        return \"true\"\n    elif types._false_Q(obj):\n        return \"false\"\n    elif types._atom_Q(obj):\n        return \"(atom \" + _pr_str(obj.val,_r) + \")\"\n    else:\n        return str(obj)\n\ndef pr_list(iterable, separator, readably):\n    return separator.join(_pr_str(exp, readably) for exp in iterable)\n"
  },
  {
    "path": "impls/python2/reader.py",
    "content": "import re\n\nfrom mal_types import (_symbol, _keyword, _list, List, Vector, Hash_Map, asPairs)\n\nclass Blank(Exception): pass\n\nclass Reader():\n    def __init__(self, tokens, position=0):\n        self.tokens = tokens\n        self.position = position\n\n    def next(self):\n        self.position += 1\n        return self.tokens[self.position-1]\n\n    def peek(self):\n        if len(self.tokens) > self.position:\n            return self.tokens[self.position]\n        else:\n            return None\n\ndef tokenize(str):\n    tre = re.compile(r\"\"\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:[\\\\].|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}()'\"`@,;]+)\"\"\");\n    return [t for t in re.findall(tre, str) if t[0] != ';']\n\ndef _unescape(s):\n    return s.replace('\\\\\\\\', '\\b').replace('\\\\\"', '\"').replace('\\\\n', '\\n').replace('\\b', '\\\\')\n\ndef read_atom(reader):\n    int_re = re.compile(r\"-?[0-9]+$\")\n    float_re = re.compile(r\"-?[0-9][0-9.]*$\")\n    string_re = re.compile(r'\"(?:[\\\\].|[^\\\\\"])*\"')\n    token = reader.next()\n    if re.match(int_re, token):     return int(token)\n    elif re.match(float_re, token): return int(token)\n    elif re.match(string_re, token):return _unescape(token[1:-1])\n    elif token[0] == '\"':           raise Exception(\"expected '\\\"', got EOF\")\n    elif token[0] == ':':           return _keyword(token[1:])\n    elif token == \"nil\":            return None\n    elif token == \"true\":           return True\n    elif token == \"false\":          return False\n    else:                           return _symbol(token)\n\ndef read_sequence(reader, start='(', end=')'):\n    token = reader.next()\n    if token != start: raise Exception(\"expected '\" + start + \"'\")\n\n    token = reader.peek()\n    while token != end:\n        if not token: raise Exception(\"expected '\" + end + \"', got EOF\")\n        yield read_form(reader)\n        token = reader.peek()\n    reader.next()\n\ndef read_hash_map(reader):\n    lst = read_sequence(reader, '{', '}')\n    return Hash_Map(asPairs(lst))\n\ndef read_list(reader):\n    return List(read_sequence(reader, '(', ')'))\n\ndef read_vector(reader):\n    return Vector(read_sequence(reader, '[', ']'))\n\ndef read_form(reader):\n    token = reader.peek()\n    # reader macros/transforms\n    if token[0] == ';':\n        reader.next()\n        return None\n    elif token == '\\'':\n        reader.next()\n        return _list(_symbol('quote'), read_form(reader))\n    elif token == '`':\n        reader.next()\n        return _list(_symbol('quasiquote'), read_form(reader))\n    elif token == '~':\n        reader.next()\n        return _list(_symbol('unquote'), read_form(reader))\n    elif token == '~@':\n        reader.next()\n        return _list(_symbol('splice-unquote'), read_form(reader))\n    elif token == '^':\n        reader.next()\n        meta = read_form(reader)\n        return _list(_symbol('with-meta'), read_form(reader), meta)\n    elif token == '@':\n        reader.next()\n        return _list(_symbol('deref'), read_form(reader))\n\n    # list\n    elif token == ')': raise Exception(\"unexpected ')'\")\n    elif token == '(': return read_list(reader)\n\n    # vector\n    elif token == ']': raise Exception(\"unexpected ']'\");\n    elif token == '[': return read_vector(reader);\n\n    # hash-map\n    elif token == '}': raise Exception(\"unexpected '}'\");\n    elif token == '{': return read_hash_map(reader);\n\n    # atom\n    else:              return read_atom(reader);\n\ndef read_str(str):\n    tokens = tokenize(str)\n    if len(tokens) == 0: raise Blank(\"Blank Line\")\n    return read_form(Reader(tokens))\n"
  },
  {
    "path": "impls/python2/run",
    "content": "#!/bin/sh\nexec python2 $(dirname $0)/${STEP:-stepA_mal}.py \"${@}\"\n"
  },
  {
    "path": "impls/python2/step0_repl.py",
    "content": "import sys, traceback\nimport mal_readline\n\n# read\ndef READ(str):\n    return str\n\n# eval\ndef EVAL(ast):\n        return ast\n\n# print\ndef PRINT(exp):\n    return exp\n\n# repl\ndef REP(str):\n    return PRINT(EVAL(READ(str)))\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except Exception:\n        print(\"\".join(traceback.format_exception(*sys.exc_info())))\n"
  },
  {
    "path": "impls/python2/step1_read_print.py",
    "content": "import sys, traceback\nimport mal_readline\nimport reader, printer\n\n# read\nREAD = reader.read_str\n\n# eval\ndef EVAL(ast):\n        return ast\n\n# print\nPRINT = printer._pr_str\n\n# repl\ndef REP(str):\n    return PRINT(EVAL(READ(str)))\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        print(\"\".join(traceback.format_exception(*sys.exc_info())))\n"
  },
  {
    "path": "impls/python2/step2_eval.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\n\n# read\nREAD = reader.read_str\n\n# eval\ndef EVAL(ast, env):\n    # print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        try:\n            return env[ast]\n        except:\n            raise Exception(\"'\" + ast + \"' not found\")\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        f = EVAL(ast[0], env)\n        args = ast[1:]\n        return f(*(EVAL(a, env) for a in args))\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = {}\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\nrepl_env['+'] = lambda a,b: a+b\nrepl_env['-'] = lambda a,b: a-b\nrepl_env['*'] = lambda a,b: a*b\nrepl_env['/'] = lambda a,b: a//b\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        print(\"\".join(traceback.format_exception(*sys.exc_info())))\n"
  },
  {
    "path": "impls/python2/step3_env.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\n\n# read\nREAD = reader.read_str\n\n# eval\ndef EVAL(ast, env):\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            return EVAL(a2, let_env)\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\nrepl_env.set(types._symbol('+'), lambda a,b: a+b)\nrepl_env.set(types._symbol('-'), lambda a,b: a-b)\nrepl_env.set(types._symbol('*'), lambda a,b: a*b)\nrepl_env.set(types._symbol('/'), lambda a,b: a//b)\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        print(\"\".join(traceback.format_exception(*sys.exc_info())))\n"
  },
  {
    "path": "impls/python2/step4_if_fn_do.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef EVAL(ast, env):\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            return EVAL(a2, let_env)\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            return EVAL(ast[-1], env)\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    return EVAL(ast[3], env)\n                else:\n                    return None\n            else:\n                return EVAL(a2, env)\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        print(\"\".join(traceback.format_exception(*sys.exc_info())))\n"
  },
  {
    "path": "impls/python2/step5_tco.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef EVAL(ast, env):\n  while True:\n\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            ast = a2\n            env = let_env\n            continue # TCO\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            ast = ast[-1]\n            continue # TCO\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    ast = ast[3]\n                    continue # TCO\n                else:\n                    return None\n            else:\n                ast = a2\n                continue # TCO\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            fn.__ast__ = a2\n            fn.__gen_env__ = lambda args: Env(env, a1, args)\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            if hasattr(f, '__ast__'):\n                ast = f.__ast__\n                env = f.__gen_env__(EVAL(a, env) for a in args)\n                continue # TCO\n            else:\n                return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        # See tests/step5_tco.mal in this directory.\n        print(\"\".join(traceback.format_exception(*sys.exc_info())[0:100]))\n"
  },
  {
    "path": "impls/python2/step6_file.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef EVAL(ast, env):\n  while True:\n\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            ast = a2\n            env = let_env\n            continue # TCO\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            ast = ast[-1]\n            continue # TCO\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    ast = ast[3]\n                    continue # TCO\n                else:\n                    return None\n            else:\n                ast = a2\n                continue # TCO\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            fn.__ast__ = a2\n            fn.__gen_env__ = lambda args: Env(env, a1, args)\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            if hasattr(f, '__ast__'):\n                ast = f.__ast__\n                env = f.__gen_env__(EVAL(a, env) for a in args)\n                continue # TCO\n            else:\n                return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\nrepl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env))\nrepl_env.set(types._symbol('*ARGV*'), types.List(sys.argv[2:]))\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\nif len(sys.argv) >= 2:\n    REP('(load-file \"' + sys.argv[1] + '\")')\n    sys.exit(0)\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        # See tests/step5_tco.mal in this directory.\n        print(\"\".join(traceback.format_exception(*sys.exc_info())[0:100]))\n"
  },
  {
    "path": "impls/python2/step7_quote.py",
    "content": "import functools\nimport sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef qq_loop(acc, elt):\n    if types._list_Q(elt) \\\n       and len(elt) == 2 \\\n       and types._symbol_Q(elt[0]) \\\n       and elt[0] == 'splice-unquote':\n        return types._list(types._symbol('concat'), elt[1], acc)\n    else:\n        return types._list(types._symbol('cons'), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    return functools.reduce(qq_loop, reversed(seq), types._list())\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2 \\\n           and types._symbol_Q(ast[0]) \\\n           and ast[0] == 'unquote':\n            return ast[1]\n        else:\n            return qq_foldr(ast)\n    elif types._hash_map_Q(ast) or types._symbol_Q(ast):\n        return types._list(types._symbol('quote'), ast)\n    elif types._vector_Q(ast):\n        return types._list(types._symbol('vec'), qq_foldr(ast))\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            ast = a2\n            env = let_env\n            continue # TCO\n        elif \"quote\" == a0:\n            return ast[1]\n        elif \"quasiquote\" == a0:\n            ast = quasiquote(ast[1])\n            continue # TCO\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            ast = ast[-1]\n            continue # TCO\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    ast = ast[3]\n                    continue # TCO\n                else:\n                    return None\n            else:\n                ast = a2\n                continue # TCO\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            fn.__ast__ = a2\n            fn.__gen_env__ = lambda args: Env(env, a1, args)\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            if hasattr(f, '__ast__'):\n                ast = f.__ast__\n                env = f.__gen_env__(EVAL(a, env) for a in args)\n                continue # TCO\n            else:\n                return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\nrepl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env))\nrepl_env.set(types._symbol('*ARGV*'), types.List(sys.argv[2:]))\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\nif len(sys.argv) >= 2:\n    REP('(load-file \"' + sys.argv[1] + '\")')\n    sys.exit(0)\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        # See tests/step5_tco.mal in this directory.\n        print(\"\".join(traceback.format_exception(*sys.exc_info())[0:100]))\n"
  },
  {
    "path": "impls/python2/step8_macros.py",
    "content": "import functools\nimport sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef qq_loop(acc, elt):\n    if types._list_Q(elt) \\\n       and len(elt) == 2 \\\n       and types._symbol_Q(elt[0]) \\\n       and elt[0] == 'splice-unquote':\n        return types._list(types._symbol('concat'), elt[1], acc)\n    else:\n        return types._list(types._symbol('cons'), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    return functools.reduce(qq_loop, reversed(seq), types._list())\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2 \\\n           and types._symbol_Q(ast[0]) \\\n           and ast[0] == 'unquote':\n            return ast[1]\n        else:\n            return qq_foldr(ast)\n    elif types._hash_map_Q(ast) or types._symbol_Q(ast):\n        return types._list(types._symbol('quote'), ast)\n    elif types._vector_Q(ast):\n        return types._list(types._symbol('vec'), qq_foldr(ast))\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            ast = a2\n            env = let_env\n            continue # TCO\n        elif \"quote\" == a0:\n            return ast[1]\n        elif \"quasiquote\" == a0:\n            ast = quasiquote(ast[1])\n            continue # TCO\n        elif 'defmacro!' == a0:\n            func = EVAL(ast[2], env)\n            func = types._clone(func)\n            func._ismacro_ = True\n            return env.set(ast[1], func)\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            ast = ast[-1]\n            continue # TCO\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    ast = ast[3]\n                    continue # TCO\n                else:\n                    return None\n            else:\n                ast = a2\n                continue # TCO\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            fn.__ast__ = a2\n            fn.__gen_env__ = lambda args: Env(env, a1, args)\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            if hasattr(f, '_ismacro_'):\n                ast = f(*args)\n                continue # TCO\n            if hasattr(f, '__ast__'):\n                ast = f.__ast__\n                env = f.__gen_env__(EVAL(a, env) for a in args)\n                continue # TCO\n            else:\n                return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\nrepl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env))\nrepl_env.set(types._symbol('*ARGV*'), types.List(sys.argv[2:]))\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\nREP(\"\"\"(defmacro! cond (fn* (& xs)\n  (if (> (count xs) 0)\n    (list 'if (first xs)\n      (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\"))\n      (cons 'cond (rest (rest xs)))))))\"\"\")\n\nif len(sys.argv) >= 2:\n    REP('(load-file \"' + sys.argv[1] + '\")')\n    sys.exit(0)\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except Exception:\n        # See tests/step5_tco.mal in this directory.\n        print(\"\".join(traceback.format_exception(*sys.exc_info())[0:100]))\n"
  },
  {
    "path": "impls/python2/step9_try.py",
    "content": "import functools\nimport sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef qq_loop(acc, elt):\n    if types._list_Q(elt) \\\n       and len(elt) == 2 \\\n       and types._symbol_Q(elt[0]) \\\n       and elt[0] == 'splice-unquote':\n        return types._list(types._symbol('concat'), elt[1], acc)\n    else:\n        return types._list(types._symbol('cons'), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    return functools.reduce(qq_loop, reversed(seq), types._list())\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2 \\\n           and types._symbol_Q(ast[0]) \\\n           and ast[0] == 'unquote':\n            return ast[1]\n        else:\n            return qq_foldr(ast)\n    elif types._hash_map_Q(ast) or types._symbol_Q(ast):\n        return types._list(types._symbol('quote'), ast)\n    elif types._vector_Q(ast):\n        return types._list(types._symbol('vec'), qq_foldr(ast))\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            ast = a2\n            env = let_env\n            continue # TCO\n        elif \"quote\" == a0:\n            return ast[1]\n        elif \"quasiquote\" == a0:\n            ast = quasiquote(ast[1])\n            continue # TCO\n        elif 'defmacro!' == a0:\n            func = EVAL(ast[2], env)\n            func = types._clone(func)\n            func._ismacro_ = True\n            return env.set(ast[1], func)\n        elif \"try*\" == a0:\n            if len(ast) < 3:\n                ast = ast[1]\n                continue # TCO\n            else:\n                a1, a2 = ast[1], ast[2]\n                err = None\n                try:\n                    return EVAL(a1, env)\n                except types.MalException as exc:\n                    err = exc.object\n                except Exception as exc:\n                    err = exc.args[0]\n                catch_env = Env(env, [a2[1]], [err])\n                ast = a2[2]\n                env = catch_env\n                continue # TCO\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            ast = ast[-1]\n            continue # TCO\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    ast = ast[3]\n                    continue # TCO\n                else:\n                    return None\n            else:\n                ast = a2\n                continue # TCO\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            fn.__ast__ = a2\n            fn.__gen_env__ = lambda args: Env(env, a1, args)\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            if hasattr(f, '_ismacro_'):\n                ast = f(*args)\n                continue # TCO\n            if hasattr(f, '__ast__'):\n                ast = f.__ast__\n                env = f.__gen_env__(EVAL(a, env) for a in args)\n                continue # TCO\n            else:\n                return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\nrepl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env))\nrepl_env.set(types._symbol('*ARGV*'), types.List(sys.argv[2:]))\n\n# core.mal: defined using the language itself\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\nREP(\"\"\"(defmacro! cond (fn* (& xs)\n  (if (> (count xs) 0)\n    (list 'if (first xs)\n      (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\"))\n      (cons 'cond (rest (rest xs)))))))\"\"\")\n\nif len(sys.argv) >= 2:\n    REP('(load-file \"' + sys.argv[1] + '\")')\n    sys.exit(0)\n\n# repl loop\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except types.MalException as e:\n        print(\"Error:\", printer._pr_str(e.object))\n    except Exception:\n        # See tests/step5_tco.mal in this directory.\n        print(\"\".join(traceback.format_exception(*sys.exc_info())[0:100]))\n"
  },
  {
    "path": "impls/python2/stepA_mal.py",
    "content": "import functools\nimport sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\nREAD = reader.read_str\n\n# eval\ndef qq_loop(acc, elt):\n    if types._list_Q(elt) \\\n       and len(elt) == 2 \\\n       and types._symbol_Q(elt[0]) \\\n       and elt[0] == 'splice-unquote':\n        return types._list(types._symbol('concat'), elt[1], acc)\n    else:\n        return types._list(types._symbol('cons'), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    return functools.reduce(qq_loop, reversed(seq), types._list())\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2 \\\n           and types._symbol_Q(ast[0]) \\\n           and ast[0] == 'unquote':\n            return ast[1]\n        else:\n            return qq_foldr(ast)\n    elif types._hash_map_Q(ast) or types._symbol_Q(ast):\n        return types._list(types._symbol('quote'), ast)\n    elif types._vector_Q(ast):\n        return types._list(types._symbol('vec'), qq_foldr(ast))\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n\n    dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)\n    if dbgeval is not None and dbgeval is not False:\n        print('EVAL: ' + printer._pr_str(ast))\n\n    if types._symbol_Q(ast):\n        return env.get(ast)\n    elif types._vector_Q(ast):\n        return types.Vector(EVAL(a, env) for a in ast)\n    elif types._hash_map_Q(ast):\n        return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n\n    if types._symbol_Q(a0):\n        if \"def!\" == a0:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif \"let*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for k, v in types.asPairs(a1):\n                let_env.set(k, EVAL(v, let_env))\n            ast = a2\n            env = let_env\n            continue # TCO\n        elif \"quote\" == a0:\n            return ast[1]\n        elif \"quasiquote\" == a0:\n            ast = quasiquote(ast[1])\n            continue # TCO\n        elif 'defmacro!' == a0:\n            func = EVAL(ast[2], env)\n            func = types._clone(func)\n            func._ismacro_ = True\n            return env.set(ast[1], func)\n        elif \"py!*\" == a0:\n            exec(compile(ast[1], '', 'single'), globals())\n            return None\n        elif \"py*\" == a0:\n            return types.py_to_mal(eval(ast[1]))\n        elif \".\" == a0:\n            el = (EVAL(ast[i], env) for i in range(2, len(ast)))\n            f = eval(ast[1])\n            return f(*el)\n        elif \"try*\" == a0:\n            if len(ast) < 3:\n                ast = ast[1]\n                continue # TCO\n            else:\n                a1, a2 = ast[1], ast[2]\n                err = None\n                try:\n                    return EVAL(a1, env)\n                except types.MalException as exc:\n                    err = exc.object\n                except Exception as exc:\n                    err = exc.args[0]\n                catch_env = Env(env, [a2[1]], [err])\n                ast = a2[2]\n                env = catch_env\n                continue # TCO\n        elif \"do\" == a0:\n            for i in range(1, len(ast)-1):\n                EVAL(ast[i], env)\n            ast = ast[-1]\n            continue # TCO\n        elif \"if\" == a0:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is None or cond is False:\n                if len(ast) > 3:\n                    ast = ast[3]\n                    continue # TCO\n                else:\n                    return None\n            else:\n                ast = a2\n                continue # TCO\n        elif \"fn*\" == a0:\n            a1, a2 = ast[1], ast[2]\n            def fn(*args):\n                return EVAL(a2, Env(env, a1, args))\n            fn.__ast__ = a2\n            fn.__gen_env__ = lambda args: Env(env, a1, args)\n            return fn\n\n    f = EVAL(a0, env)\n    if types._function_Q(f):\n            args = ast[1:]\n            if hasattr(f, '_ismacro_'):\n                ast = f(*args)\n                continue # TCO\n            if hasattr(f, '__ast__'):\n                ast = f.__ast__\n                env = f.__gen_env__(EVAL(a, env) for a in args)\n                continue # TCO\n            else:\n                return f(*(EVAL(a, env) for a in args))\n    else:\n        raise Exception('Can only apply functions')\n\n# print\nPRINT = printer._pr_str\n\n# repl\nrepl_env = Env()\ndef REP(str):\n    return PRINT(EVAL(READ(str), repl_env))\n\n# core.py: defined using python\nfor k, v in core.ns.items(): repl_env.set(types._symbol(k), v)\nrepl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env))\nrepl_env.set(types._symbol('*ARGV*'), types.List(sys.argv[2:]))\n\n# core.mal: defined using the language itself\nREP('(def! *host-language* \"python2\")')\nREP(\"(def! not (fn* (a) (if a false true)))\")\nREP('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\nREP(\"\"\"(defmacro! cond (fn* (& xs)\n  (if (> (count xs) 0)\n    (list 'if (first xs)\n      (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\"))\n      (cons 'cond (rest (rest xs)))))))\"\"\")\n\nif len(sys.argv) >= 2:\n    REP('(load-file \"' + sys.argv[1] + '\")')\n    sys.exit(0)\n\n# repl loop\nREP('(println (str \"Mal [\" *host-language* \"]\"))')\nwhile True:\n    try:\n        line = mal_readline.readline(\"user> \")\n        print(REP(line))\n    except EOFError:\n        print()\n        break\n    except reader.Blank: continue\n    except types.MalException as e:\n        print(\"Error:\", printer._pr_str(e.object))\n    except Exception:\n        # See tests/step5_tco.mal in this directory.\n        print(\"\".join(traceback.format_exception(*sys.exc_info())[0:100]))\n"
  },
  {
    "path": "impls/python2/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/python2/tests/stepA_mal.mal",
    "content": ";; Testing Python interop\n\n;; Testing Python expressions\n(py* \"7\")\n;=>7\n(py* \"'7'\")\n;=>\"7\"\n(py* \"[7,8,9]\")\n;=>(7 8 9)\n(py* \"' '.join(['X'+c+'Y' for c in ['a','b','c']])\")\n;=>\"XaY XbY XcY\"\n(py* \"[1 + x for x in [1,2,3]]\")\n;=>(2 3 4)\n\n;; Testing Python statements\n(py!* \"print('hello')\")\n;/hello\n;=>nil\n\n(py!* \"foo = 19 % 4\")\n;=>nil\n(py* \"foo\")\n;=>3\n"
  },
  {
    "path": "impls/python3/.gitignore",
    "content": ".vscode/\n.mypy_cache/\n.idea/\n"
  },
  {
    "path": "impls/python3/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# For checking:\n# RUN apt-get -y install flake8 mypy pylint\n"
  },
  {
    "path": "impls/python3/Makefile",
    "content": "# make check sources=reader.py    may be convenient\nsources ?= *.py\n\nf8 += D100 # Missing docstring in public module\nf8 += D101 # Missing docstring in public class\nf8 += D102 # Missing docstring in public method\nf8 += D103 # Missing docstring in public function\nf8 += D105 # Missing docstring in magic method\nf8 += D107 # Missing docstring in __init__\nf8 += I100 # order of import statements (incompatible with pylint)\nf8 += W503 # line break before binary operator (incompatible with 504)\npl += missing-module-docstring\npl += missing-class-docstring\npl += missing-function-docstring\npl += R0801 # Similar lines in 2 files (steps...)\n\nall:\n\ncheck:\n\tpylint --disable=$(shell echo $(pl) | sed 's/ /,/g') $(sources)\n\tmypy $(sources)\n\tflake8 --ignore=$(shell echo $(f8) | sed 's/ /,/g') $(sources)\n\nclean:\n\trm -f *~\n\trm -fr __pycache__/ .mypy_cache/\n"
  },
  {
    "path": "impls/python3/core.py",
    "content": "import collections.abc\nimport dataclasses\nimport itertools\nimport time\nimport operator\nimport typing\nfrom collections.abc import Callable, Sequence\n\nimport mal_readline\n\nfrom mal_types import (Atom, Boolean, Error, Fn, Form, Keyword, List,\n                       Macro, Map, Nil, Number, PythonCall, String,\n                       Symbol, ThrownException, Vector, pr_seq)\n\nimport reader\n\n\nns: dict[str, Form] = {}\n\n\ndef built_in(name: str) -> Callable[[PythonCall], None]:\n    \"\"\"Register in ns and add context to Errors.\"\"\"\n\n    def decorate(old_f: PythonCall) -> None:\n\n        def new_f(args: Sequence[Form]) -> Form:\n            try:\n                return old_f(args)\n            except Error as exc:\n                if hasattr(exc, \"add_note\"):\n                    exc.add_note('The ' + name + ' core function received ['\n                                 + pr_seq(args) + ' ] as arguments.')\n                raise\n\n        ns[name] = Fn(new_f)\n\n    return decorate\n\n\ndef equality(value: Form) -> PythonCall:\n\n    def new_f(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return Boolean(form == value)\n            case _:\n                raise Error('bad arguments')\n\n    return new_f\n\n\nbuilt_in('nil?')(equality(Nil.NIL))\nbuilt_in('false?')(equality(Boolean.FALSE))\nbuilt_in('true?')(equality(Boolean.TRUE))\n\n\ndef membership(*classes: type) -> PythonCall:\n\n    def new_f(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return Boolean(isinstance(form, classes))\n            case _:\n                raise Error('bad arguments')\n\n    return new_f\n\n\nbuilt_in('number?')(membership(Number))\nbuilt_in('symbol?')(membership(Symbol))\nbuilt_in('keyword?')(membership(Keyword))\nbuilt_in('string?')(membership(String))\nbuilt_in('list?')(membership(List))\nbuilt_in('map?')(membership(Map))\nbuilt_in('atom?')(membership(Atom))\nbuilt_in('vector?')(membership(Vector))\nbuilt_in('macro?')(membership(Macro))\nbuilt_in('sequential?')(membership(List, Vector))\nbuilt_in('fn?')(membership(Fn))\n\n\ndef arithmetic(old_f: Callable[[int, int], int]) -> PythonCall:\n\n    def new_f(args: Sequence[Form]) -> Form:\n        match args:\n            case [Number() as left, Number() as right]:\n                return Number(old_f(left, right))\n            case _:\n                raise Error('bad arguments')\n\n    return new_f\n\n\nbuilt_in('+')(arithmetic(operator.add))\nbuilt_in('-')(arithmetic(operator.sub))\nbuilt_in('*')(arithmetic(operator.mul))\nbuilt_in('/')(arithmetic(operator.floordiv))\n\n\ndef comparison(old_f: Callable[[int, int], bool]) -> PythonCall:\n\n    def new_f(args: Sequence[Form]) -> Form:\n        match args:\n            case [Number() as left, Number() as right]:\n                return Boolean(old_f(left, right))\n            case _:\n                raise Error('bad arguments')\n\n    return new_f\n\n\nbuilt_in('<')(comparison(operator.lt))\nbuilt_in('<=')(comparison(operator.le))\nbuilt_in('>')(comparison(operator.gt))\nbuilt_in('>=')(comparison(operator.ge))\n\n\n@built_in('=')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [left, right]:\n            return Boolean(left == right)\n        case _:\n            raise Error('bad arguments')\n\n\nbuilt_in('list')(List)\nbuilt_in('vector')(Vector)\n\n\n@built_in('prn')\ndef _(args: Sequence[Form]) -> Form:\n    print(pr_seq(args))\n    return Nil.NIL\n\n\n@built_in('pr-str')\ndef _(args: Sequence[Form]) -> Form:\n    return String(pr_seq(args))\n\n\n@built_in('println')\ndef _(args: Sequence[Form]) -> Form:\n    print(pr_seq(args, readably=False))\n    return Nil.NIL\n\n\n@built_in('empty?')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() | Vector() as seq]:\n            return Boolean(not seq)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('count')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() | Vector() as seq]:\n            return Number(len(seq))\n        case [Nil()]:\n            return Number(0)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('read-string')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(line)]:\n            return reader.read(line)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('slurp')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(file_name)]:\n            with open(file_name, 'r', encoding='utf-8') as the_file:\n                return String(the_file.read())\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('str')\ndef _(args: Sequence[Form]) -> Form:\n    return String(pr_seq(args, readably=False, sep=''))\n\n\n@built_in('atom')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [form]:\n            return Atom(form)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('deref')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Atom(val)]:\n            return val\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('reset!')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Atom() as atm, form]:\n            atm.val = form\n            return form\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('vec')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() as seq]:\n            return Vector(seq)\n        case [Vector() as seq]:\n            return seq\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('cons')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [head, List() | Vector() as tail]:\n            return List((head, *tail))\n        case _:\n            raise Error('bad arguments')\n\n\ndef cast_sequence(arg: Form) -> List | Vector:\n    match arg:\n        case List() | Vector():\n            return arg\n        case _:\n            raise Error(f'{arg} is not a sequence')\n\n\n@built_in('concat')\ndef _(args: Sequence[Form]) -> Form:\n    return List(itertools.chain.from_iterable(cast_sequence(x) for x in args))\n\n\n@built_in('nth')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() | Vector() as seq, Number() as idx]:\n            # Python would accept index = -1.\n            if 0 <= idx < len(seq):\n                return seq[idx]\n            raise Error(f'index {idx} not in range of {seq}')\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('apply')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Fn(call) | Macro(call), *some,\n              List() | Vector() as more]:\n            return call((*some, *more))\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('map')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Fn(call), List() | Vector() as seq]:\n            return List(call((x, )) for x in seq)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('throw')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [form]:\n            raise ThrownException(form)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('keyword')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(string)]:\n            return Keyword(string)\n        case [Keyword() as keyword]:\n            return keyword\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('symbol')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(string)]:\n            return Symbol(string)\n        case [Symbol() as symbol]:\n            return symbol\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('readline')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(prompt)]:\n            try:\n                return String(mal_readline.input_(prompt))\n            except EOFError:\n                return Nil.NIL\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('time-ms')\ndef _(args: Sequence[Form]) -> Form:\n    if args:\n        raise Error('bad arguments')\n    return Number(time.time() * 1000.0)\n\n\n@built_in('meta')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Fn() | List() | Vector() | Map() as form]:\n            return form.meta\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('with-meta')\ndef _(args: Sequence[Form]) -> Form:\n    #  container = type(container)(container, meta=meta) confuses mypy.\n    match args:\n        case [List() as container, meta]:\n            return List(container, meta=meta)\n        case [Vector() as container, meta]:\n            return Vector(container, meta=meta)\n        case [Map() as container, meta]:\n            return Map(container, meta)\n        case [Fn() as container, meta]:\n            return dataclasses.replace(container, meta=meta)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('seq')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() as seq]:\n            return seq if seq else Nil.NIL\n        case [Vector() as seq]:\n            return List(seq) if seq else Nil.NIL\n        case [String(string)]:\n            return List(String(c) for c in string) if string else Nil.NIL\n        case [Nil()]:\n            return Nil.NIL\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('conj')\ndef conj(args: Sequence[Form]) -> Form:\n    match args:\n        case [Vector() as seq, *forms]:\n            return Vector((*seq, *forms))\n        case [List() as seq, *forms]:\n            return List((*reversed(forms), *seq))\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('get')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Map() as mapping, Keyword() | String() as key]:\n            return mapping.get(key, Nil.NIL)\n        case [Nil(), Keyword() | String()]:\n            return Nil.NIL\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('first')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() | Vector() as seq]:\n            return seq[0] if seq else Nil.NIL\n        case [Nil()]:\n            return Nil.NIL\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('rest')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [List() | Vector() as seq]:\n            return List(seq[1:])\n        case [Nil()]:\n            return List()\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('hash-map')\ndef _(args: Sequence[Form]) -> Form:\n    return Map(Map.cast_items(args))\n\n\n@built_in('assoc')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Map() as mapping, *binds]:\n            return Map(itertools.chain(mapping.items(), Map.cast_items(binds)))\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('contains?')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Map() as mapping, Keyword() | String() as key]:\n            return Boolean(key in mapping)\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('keys')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Map() as mapping]:\n            return List(mapping.keys())\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('vals')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Map() as mapping]:\n            return List(mapping.values())\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('dissoc')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Map() as mapping, *keys]:\n            result = Map(mapping)\n            for key in keys:\n                if not isinstance(key, (Keyword, String)):\n                    raise Error(f'{key} is not a valid map key')\n                if key in result:\n                    del result[key]\n            return result\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('swap!')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [Atom(old) as atm, Fn(call), *more]:\n            new = call((old, *more))\n            atm.val = new\n            return new\n        case _:\n            raise Error('bad arguments')\n\n\n@built_in('py!*')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(python_statement)]:\n            # pylint: disable-next=exec-used\n            exec(compile(python_statement, '', 'single'), globals())\n            return Nil.NIL\n        case _:\n            raise Error('bad arguments')\n\n\ndef py2mal(obj: typing.Any) -> Form:\n    match obj:\n        case None:\n            return Nil.NIL\n        case bool():\n            return Boolean(obj)\n        case int():\n            return Number(obj)\n        case str():\n            return String(obj)\n        case Sequence():\n            return List(py2mal(x) for x in obj)\n        case collections.abc.Mapping():\n            result = Map()\n            for py_key, py_val in obj.items():\n                key = py2mal(py_key)\n                if not isinstance(key, (Keyword, String)):\n                    raise Error(f'{key} is not a valid map key')\n                result[key] = py2mal(py_val)\n            return Map()\n        case _:\n            raise Error(f'failed to translate {obj}')\n\n\n@built_in('py*')\ndef _(args: Sequence[Form]) -> Form:\n    match args:\n        case [String(python_expression)]:\n            # pylint: disable-next=eval-used\n            return py2mal(eval(python_expression))\n        case _:\n            raise Error('bad arguments')\n"
  },
  {
    "path": "impls/python3/env.py",
    "content": "# Env is defined in mal_types.py in order to avoid a circular dependency.\nfrom collections.abc import Sequence\n\nfrom mal_types import Env, Error, Form, List, pr_seq\n\n\ndef call_env(env: Env, parms: Sequence[str], args: Sequence[Form]) -> Env:\n    match parms:\n        case [*required, '&', rest]:\n            if len(args) < len(required):\n                raise Error('not enough arguments for fn*['\n                            + ' '.join(parms) + ']: ' + pr_seq(args))\n            fn_env = env.new_child(dict(zip(required, args)))\n            fn_env[rest] = List(args[len(required):])\n            return fn_env\n        case _:\n            if len(args) != len(parms):\n                raise Error('bad argument count for fn*['\n                            + ' '.join(parms) + ']: ' + pr_seq(args))\n            return env.new_child(dict(zip(parms, args)))\n"
  },
  {
    "path": "impls/python3/mal_readline.py",
    "content": "# Importing this module is sufficient for the 'input' builtin command\n# to support readline.\n\nimport atexit\nimport os.path\nimport readline\n\nhistfile = os.path.join(os.path.expanduser('~'), '.mal-history')\ntry:\n    readline.read_history_file(histfile)\nexcept FileNotFoundError:\n    pass\nreadline.set_history_length(1000)\natexit.register(readline.write_history_file, histfile)\n\n\ndef input_(prompt: str) -> str:\n    line = input(prompt)\n    if line:\n        readline.add_history(line)\n    return line\n"
  },
  {
    "path": "impls/python3/mal_types.py",
    "content": "# Named mal_types because 'types' is already a standard python module.\n\nimport collections\nimport dataclasses\nimport enum\nimport itertools\nimport re\nimport typing\nfrom collections.abc import Callable, Iterable, Iterator, Mapping, Sequence\n\n# The selected representations ensure that the Python == equality\n# matches the MAL = equality.\n\n# pr_str is implemented here without printer.py because\n# __str__ is idiomatic and gives formatted error messages soon\n# (that is, without circular dependencies or evil tricks).\n# So there are three ways to format a MAL object.\n# str(form)\n#     the default, used by pr_seq or format strings like f'{form}'\n#     implemented by form.__str__(readably=True)\n# form.__str__(readably=False)\n#     used by some core functions via pr_seq\n#     implemented by form.__str__(readably=False)\n# repr(form)\n#     the python representation for debugging\n\n\nclass Nil(enum.Enum):\n    NIL = None\n\n    def __str__(self, readably: bool = True) -> str:\n        return 'nil'\n\n\nclass Boolean(enum.Enum):\n    FALSE = False\n    TRUE = True\n\n    def __str__(self, readably: bool = True) -> str:\n        return 'true' if self is self.TRUE else 'false'\n\n\nclass Number(int):\n\n    def __str__(self, readably: bool = True) -> str:\n        return super().__str__()\n\n\nclass Symbol(str):\n\n    def __str__(self, readably: bool = True) -> str:\n        # pylint: disable=invalid-str-returned\n        return self\n\n\n# The two other string types are wrapped in dataclasses in order to\n# avoid problems with == (symbols) and pattern matching (list and\n# vectors).\n@dataclasses.dataclass(frozen=True, slots=True)\nclass String:\n    val: str\n\n    @staticmethod\n    def _repl(match: re.Match[str]) -> str:\n        char = match.group()\n        return '\\\\' + ('n' if char == '\\n' else char)\n\n    def __str__(self, readably: bool = True) -> str:\n        return '\"' + re.sub(r'[\\\\\"\\n]', String._repl, self.val) + '\"' \\\n            if readably else self.val\n\n\n@dataclasses.dataclass(frozen=True, slots=True)\nclass Keyword:\n    val: str\n\n    def __str__(self, readably: bool = True) -> str:\n        return ':' + self.val\n\n\nclass List(tuple['Form', ...]):\n    # Avoid a name clash with typing.List. This improves mypy output.\n\n    def __init__(self, _: Iterable['Form'] = (),\n                 meta: 'Form' = Nil.NIL) -> None:\n        \"\"\"Add a meta field, tuple.__new__ does the rest.\"\"\"\n        self.meta = meta\n\n    def __str__(self, readably: bool = True) -> str:\n        return '(' + pr_seq(self, readably) + ')'\n\n\nclass Vector(tuple['Form', ...]):\n\n    def __init__(self, _: Iterable['Form'] = (),\n                 meta: 'Form' = Nil.NIL) -> None:\n        \"\"\"Add a meta field, tuple.__new__ does the rest.\"\"\"\n        self.meta = meta\n\n    def __str__(self, readably: bool = True) -> str:\n        return '[' + pr_seq(self, readably) + ']'\n\n\nclass Map(dict[Keyword | String, 'Form']):\n\n    def __init__(self,\n                 arg: Iterable[tuple[Keyword | String, 'Form']]\n                 | Mapping[Keyword | String, 'Form'] = (),\n                 meta: 'Form' = Nil.NIL,\n                 ) -> None:\n        dict.__init__(self, arg)\n        self.meta = meta\n\n    def __str__(self, readably: bool = True) -> str:\n        return '{' + pr_seq(itertools.chain.from_iterable(self.items()),\n                            readably) + '}'\n\n    @staticmethod\n    def cast_items(args: Iterable['Form']\n                   ) -> Iterator[tuple[Keyword | String, 'Form']]:\n        key: Keyword | String | None = None\n        for form in args:\n            if key:\n                yield key, form\n                key = None\n            elif isinstance(form, (Keyword, String)):\n                key = form\n            else:\n                raise Error(f'{key} is not a valid map key')\n        if key:\n            raise Error(f'odd count in map binds, no value for {form}')\n\n\nEnv = collections.ChainMap[str, 'Form']\nPythonCall = Callable[[Sequence['Form']], 'Form']\n\n\nclass TCOEnv(typing.NamedTuple):\n    body: 'Form'\n    fenv: Callable[[Sequence['Form']], Env]\n\n\n@dataclasses.dataclass(frozen=True, slots=True)\nclass Fn:\n    call: PythonCall\n    tco_env: TCOEnv | None = None\n    meta: 'Form' = Nil.NIL\n\n    def __str__(self, readably: bool = True) -> str:\n        return '#<function>'\n\n\n@dataclasses.dataclass(frozen=True, slots=True)\nclass Macro:\n    call: PythonCall\n\n    def __str__(self, readably: bool = True) -> str:\n        return '#<macro>'\n\n\n@dataclasses.dataclass(slots=True)\nclass Atom:\n    val: 'Form'\n\n    def __str__(self, readably: bool = True) -> str:\n        return f'(atom {self.val})'\n\n\nForm = (Atom | Boolean | Fn | Keyword | Macro | List\n        | Map | Nil | Number | String | Symbol | Vector)\n\n\nclass Error(Exception):\n    \"\"\"Local exceptions, as recommended by pylint.\"\"\"\n\n\n@dataclasses.dataclass(frozen=True, slots=True)\nclass ThrownException(Exception):\n    form: Form\n\n\ndef pr_seq(args: Iterable[Form], readably: bool = True, sep: str = ' ') -> str:\n    # This would be OK if the signature was usual.\n    # pylint: disable-next=unnecessary-dunder-call\n    return sep.join(x.__str__(readably) for x in args)\n"
  },
  {
    "path": "impls/python3/reader.py",
    "content": "import re\nfrom collections.abc import Callable, Iterator, Mapping\nfrom re import Match\n\nfrom mal_types import (Boolean, Error, Form, Keyword, List, Map, Nil,\n                       Number, String, Symbol, Vector)\n\n# The `token` decorator adds regular expression groups all along this file.\n# The name of a group is the name of the decorated funtion, allowing\n# `read_form` to call it when it founds the token.\n# The global regular expression is compiled once when the module is loaded.\ntoken_groups: list[str] = []\n\n\nclass Lexer:\n    # Consume unnamed groups, but do not report them.\n    # Report None at the end of the input.\n\n    def __init__(self, source: str) -> None:\n        self._tokens = (t for t in pattern.finditer(source) if t.lastgroup)\n        self._peek: Match[str] | None = None\n        self.consume()\n\n    def consume(self) -> None:\n        try:\n            self._peek = next(self._tokens)\n        except StopIteration:\n            self._peek = None\n\n    def peek(self) -> re.Match[str] | None:\n        return self._peek\n\n\ndef token(regex: str):\n    \"\"\"Bind a regular expression to a function in this module. Form constuctor.\n\n    The lexer does not report tokens with None as constructor.\n    \"\"\"\n\n    def decorator(fun: Callable[[Lexer, Match[str]], Form] | None):\n        if fun:\n            group = f'(?P<{fun.__name__}>{regex})'\n        else:\n            group = f'(?:{regex})'\n        token_groups.append(group)\n        return fun\n\n    return decorator\n\n\ndef context(match: Match[str]) -> str:\n    \"\"\"Format some information for error reporting.\"\"\"\n    start_idx = match.start() - 10\n    if 0 < start_idx:\n        start = '...' + match.string[start_idx:match.start()]\n    else:\n        start = match.string[:match.start()]\n    end_idx = match.end() + 20\n    if end_idx < len(match.string):\n        end = match.string[match.end():end_idx] + '...'\n    else:\n        end = match.string[match.end():]\n    return f': {start}<BETWEEN THIS>{match.group()}<AND THIS>{end}'\n\n\ntoken(r'(?:[\\s,]|;[^\\n\\r]*)+')(None)\n\n\ndef unescape(match: Match[str]) -> str:\n    \"\"\"Map a backslash sequence to a character for strings.\"\"\"\n    char = match.string[match.end() - 1]\n    return '\\n' if char == 'n' else char\n\n\n@token(r'\"(?:(?:[^\"\\\\]|\\\\.)*\")?')\ndef string(_: Lexer, tok: Match[str]) -> Form:\n    start, end = tok.span()\n    if end - start == 1:\n        raise Error('read: unbalanced string delimiter' + context(tok))\n    return String(re.sub(r'\\\\.', unescape, tok.string[start + 1:end - 1]))\n\n\ndef read_list(lexer: Lexer, closing: str, pos: Match[str]) -> Iterator[Form]:\n    while not ((tok := lexer.peek()) and tok.group() == closing):\n        yield read_form(lexer, pos)\n    lexer.consume()\n\n\n@token(r'\\(')\ndef list_start(lexer: Lexer, tok: Match[str]) -> Form:\n    return List(read_list(lexer, ')', tok))\n\n\n@token(r'\\[')\ndef vector_start(lexer: Lexer, tok: Match[str]) -> Form:\n    return Vector(read_list(lexer, ']', tok))\n\n\n@token(r'\\{')\ndef map_start(lexer: Lexer, tok: Match[str]) -> Form:\n    return Map(Map.cast_items(read_list(lexer, '}', tok)))\n\n\nsingle_macros = {\n    \"'\": 'quote',\n    '`': 'quasiquote',\n    '@': 'deref',\n    '~': 'unquote',\n    '~@': 'splice-unquote',\n}\n\n\n@token(\"['`@]|~@?\")\ndef macro(lexer: Lexer, tok: Match[str]) -> Form:\n    return List((Symbol(single_macros[tok.group()]), read_form(lexer, tok)))\n\n\n@token(r'\\^')\ndef with_meta(lexer: Lexer, tok: Match[str]) -> Form:\n    tmp = read_form(lexer, tok)\n    return List((Symbol('with-meta'), read_form(lexer, tok), tmp))\n\n\n@token('[])}]')\ndef list_end(_: Lexer, tok: Match[str]) -> Form:\n    raise Error('read: unbalanced list/vector/map terminator' + context(tok))\n\n\n@token(r'-?\\d+')\ndef number(_: Lexer, tok: Match[str]) -> Form:\n    return Number(tok.group())\n\n\nalmost_symbols: Mapping[str, Form] = {\n    'nil': Nil.NIL,\n    'false': Boolean.FALSE,\n    'true': Boolean.TRUE,\n}\n\n\n@token(r\"\"\"[^]\\s\"'(),;@[^`{}~]+\"\"\")\ndef symbol(_: Lexer, tok: Match[str]) -> Form:\n    start, end = tok.span()\n    if tok.string[start] == ':':\n        return Keyword(tok.string[start + 1:end])\n    value = tok.group()\n    return almost_symbols.get(value) or Symbol(value)\n\n\n@token('.')\ndef should_never_match(lexer: Lexer, tok: Match[str]) -> Form:\n    assert False, f'{lexer} {tok}'\n\n\ndef read_form(lexer: Lexer, pos: Match[str] | None) -> Form:\n    \"\"\"Parse a form from `lexer`, reporting errors as if started from `pos`.\"\"\"\n    if (tok := lexer.peek()):\n        lexer.consume()\n        assert tok.lastgroup, f'{lexer} {tok}'\n        assert tok.lastgroup in globals(), f'{lexer} {tok}'\n        return globals()[tok.lastgroup](lexer, tok)\n    if pos:\n        raise Error('read: unbalanced form, started' + context(pos))\n    raise Error('read: the whole input was empty')\n\n\ndef read(source: str) -> Form:\n    lexer = Lexer(source)\n    result = read_form(lexer, None)\n    if tok := lexer.peek():\n        raise Error('read: trailing items after the form' + context(tok))\n    return result\n\n\npattern = re.compile('|'.join(token_groups))\n"
  },
  {
    "path": "impls/python3/run",
    "content": "#!/bin/sh\nexec python3 $(dirname $0)/${STEP:-stepA_mal}.py \"${@}\"\n"
  },
  {
    "path": "impls/python3/step0_repl.py",
    "content": "import mal_readline\n\n\ndef read(source: str) -> str:\n    return source\n\n\ndef eval_(ast: str) -> str:\n    return ast\n\n\ndef print_(form: str) -> str:\n    return form\n\n\ndef rep(source: str) -> str:\n    return print_(eval_(read(source)))\n\n\ndef main() -> None:\n    while True:\n        try:\n            print(rep(mal_readline.input_('user> ')))\n        except EOFError:\n            break\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step1_read_print.py",
    "content": "import traceback\n\nimport mal_readline\n\nfrom mal_types import Form\n\nimport reader\n\n\ndef eval_(ast: Form) -> Form:\n    # print(repr(ast))  # the result of read, as python\n    return ast\n\n\ndef rep(source: str) -> str:\n    return str(eval_(reader.read(source)))\n\n\ndef main() -> None:\n    while True:\n        try:\n            print(rep(mal_readline.input_('user> ')))\n        except EOFError:\n            break\n        # pylint: disable-next=broad-exception-caught\n        except Exception as exc:\n            traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step2_eval.py",
    "content": "import traceback\nfrom collections.abc import Mapping, Sequence\n\nimport mal_readline\n\nfrom mal_types import (Error, Fn, Form, List,\n                       Map, Number, Symbol,\n                       Vector, pr_seq)\n\nimport reader\n\nEnv = Mapping[str, Fn]\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    # print(f'EVAL: {ast}', repr(ast)\n    match ast:\n        case Symbol():\n            if (value := env.get(ast)) is not None:\n                return value\n            raise Error(f\"'{ast}' not found\")\n        case Map():\n            return Map((k, eval_(v, env)) for k, v in ast.items())\n        case Vector():\n            return Vector(eval_(x, env) for x in ast)\n        case List([first, *args]):\n            match eval_(first, env):\n                case Fn(call):\n                    return call(tuple(eval_(x, env) for x in args))\n                case not_fun:\n                    raise Error(f'cannot apply {not_fun}')\n        case _:\n            return ast\n\n\ndef add(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left + right)\n        case _:\n            raise Error('+: bad arguments' + pr_seq(args))\n\n\ndef sub(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left - right)\n        case _:\n            raise Error('-: bad arguments' + pr_seq(args))\n\n\ndef mul(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left * right)\n        case _:\n            raise Error('*: bad arguments' + pr_seq(args))\n\n\ndef floordiv(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left // right)\n        case _:\n            raise Error('/: bad arguments' + pr_seq(args))\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env: Env = {\n        '+': Fn(add), '-': Fn(sub), '*': Fn(mul), '/': Fn(floordiv),\n    }\n\n    while True:\n        try:\n            print(rep(mal_readline.input_('user> '), repl_env))\n        except EOFError:\n            break\n        # pylint: disable-next=broad-exception-caught\n        except Exception as exc:\n            traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step3_env.py",
    "content": "import traceback\nfrom collections.abc import Sequence\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List,\n                       Map, Nil, Number, Symbol,\n                       Vector, pr_seq)\n\nimport reader\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> Form:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> Form:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return eval_(form, let_env)\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n        print(f'EVAL: {ast}')  # , repr(ast))\n        for outer in env.maps:\n            print('  ENV:', ' '.join(f'{k}: {v}'\n                  for k, v in reversed(outer.items()))[:75])\n    match ast:\n        case Symbol():\n            if (value := env.get(ast)) is not None:\n                return value\n            raise Error(f\"'{ast}' not found\")\n        case Map():\n            return Map((k, eval_(v, env)) for k, v in ast.items())\n        case Vector():\n            return Vector(eval_(x, env) for x in ast)\n        case List([first, *args]):\n            if isinstance(first, Symbol) and (spec := specials.get(first)):\n                return spec(args, env)\n            match eval_(first, env):\n                case Fn(call):\n                    return call(tuple(eval_(x, env) for x in args))\n                case not_fun:\n                    raise Error(f'cannot apply {not_fun}')\n        case _:\n            return ast\n\n\ndef add(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left + right)\n        case _:\n            raise Error('+: bad arguments' + pr_seq(args))\n\n\ndef sub(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left - right)\n        case _:\n            raise Error('-: bad arguments' + pr_seq(args))\n\n\ndef mul(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left * right)\n        case _:\n            raise Error('*: bad arguments' + pr_seq(args))\n\n\ndef floordiv(args: Sequence[Form]) -> Form:\n    match args:\n        case [Number(left), Number(right)]:\n            return Number(left // right)\n        case _:\n            raise Error('/: bad arguments' + pr_seq(args))\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env({\n        '+': Fn(add), '-': Fn(sub), '*': Fn(mul), '/': Fn(floordiv),\n    })\n\n    while True:\n        try:\n            print(rep(mal_readline.input_('user> '), repl_env))\n        except EOFError:\n            break\n        # pylint: disable-next=broad-exception-caught\n        except Exception as exc:\n            traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step4_if_fn_do.py",
    "content": "import traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List,\n                       Map, Nil, Symbol,\n                       Vector, pr_seq)\n\nimport reader\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> Form:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> Form:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return eval_(form, let_env)\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> Form:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return eval_(last, env)\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> Form:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return eval_(args[2], env)\n            return Nil.NIL\n        return eval_(args[1], env)\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> Form:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, call_env(env, parms, f_args))\n\n            return Fn(call)\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n        print(f'EVAL: {ast}')  # , repr(ast))\n        for outer in env.maps:\n            print('  ENV:', ' '.join(f'{k}: {v}'\n                  for k, v in reversed(outer.items()))[:75])\n    match ast:\n        case Symbol():\n            if (value := env.get(ast)) is not None:\n                return value\n            raise Error(f\"'{ast}' not found\")\n        case Map():\n            return Map((k, eval_(v, env)) for k, v in ast.items())\n        case Vector():\n            return Vector(eval_(x, env) for x in ast)\n        case List([first, *args]):\n            if isinstance(first, Symbol) and (spec := specials.get(first)):\n                return spec(args, env)\n            match eval_(first, env):\n                case Fn(call):\n                    return call(tuple(eval_(x, env) for x in args))\n                case not_fun:\n                    raise Error(f'cannot apply {not_fun}')\n        case _:\n            return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n\n    while True:\n        try:\n            print(rep(mal_readline.input_('user> '), repl_env))\n        except EOFError:\n            break\n        # pylint: disable-next=broad-exception-caught\n        except Exception as exc:\n            traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step5_tco.py",
    "content": "import traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List,\n                       Map, Nil, Symbol, TCOEnv,\n                       Vector, pr_seq)\n\nimport reader\n\n# Special forms return either a final result or a new TCO context.\nSpecialResult = tuple[Form, Env | None]\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value, None\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return form, let_env\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return last, env\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> SpecialResult:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return args[2], env\n            return Nil.NIL, None\n        return args[1], env\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def fenv(f_args: Sequence[Form]) -> Env:\n                return call_env(env, parms, f_args)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, fenv(f_args))\n\n            return Fn(call, TCOEnv(body, fenv)), None\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    while True:\n        if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n            print(f'EVAL: {ast}')  # , repr(ast))\n            for outer in env.maps:\n                print('  ENV:', ' '.join(f'{k}: {v}'\n                      for k, v in reversed(outer.items()))[:75])\n        match ast:\n            case Symbol():\n                if (value := env.get(ast)) is not None:\n                    return value\n                raise Error(f\"'{ast}' not found\")\n            case Map():\n                return Map((k, eval_(v, env)) for k, v in ast.items())\n            case Vector():\n                return Vector(eval_(x, env) for x in ast)\n            case List([first, *args]):\n                if isinstance(first, Symbol) and (spec := specials.get(first)):\n                    ast, maybe_env = spec(args, env)\n                    if maybe_env is None:\n                        return ast\n                    env = maybe_env\n                else:\n                    match eval_(first, env):\n                        case Fn(tco_env=TCOEnv(body, fenv)):\n                            ast = body\n                            env = fenv(tuple(eval_(x, env) for x in args))\n                        case Fn(call):\n                            return call(tuple(eval_(x, env) for x in args))\n                        case not_fun:\n                            raise Error(f'cannot apply {not_fun}')\n            case _:\n                return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n\n    while True:\n        try:\n            print(rep(mal_readline.input_('user> '), repl_env))\n        except EOFError:\n            break\n        # pylint: disable-next=broad-exception-caught\n        except Exception as exc:\n            traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step6_file.py",
    "content": "import sys\nimport traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List,\n                       Map, Nil, String, Symbol, TCOEnv,\n                       Vector, pr_seq)\n\nimport reader\n\n# Special forms return either a final result or a new TCO context.\nSpecialResult = tuple[Form, Env | None]\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value, None\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return form, let_env\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return last, env\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> SpecialResult:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return args[2], env\n            return Nil.NIL, None\n        return args[1], env\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def fenv(f_args: Sequence[Form]) -> Env:\n                return call_env(env, parms, f_args)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, fenv(f_args))\n\n            return Fn(call, TCOEnv(body, fenv)), None\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    while True:\n        if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n            print(f'EVAL: {ast}')  # , repr(ast))\n            for outer in env.maps:\n                print('  ENV:', ' '.join(f'{k}: {v}'\n                      for k, v in reversed(outer.items()))[:75])\n        match ast:\n            case Symbol():\n                if (value := env.get(ast)) is not None:\n                    return value\n                raise Error(f\"'{ast}' not found\")\n            case Map():\n                return Map((k, eval_(v, env)) for k, v in ast.items())\n            case Vector():\n                return Vector(eval_(x, env) for x in ast)\n            case List([first, *args]):\n                if isinstance(first, Symbol) and (spec := specials.get(first)):\n                    ast, maybe_env = spec(args, env)\n                    if maybe_env is None:\n                        return ast\n                    env = maybe_env\n                else:\n                    match eval_(first, env):\n                        case Fn(tco_env=TCOEnv(body, fenv)):\n                            ast = body\n                            env = fenv(tuple(eval_(x, env) for x in args))\n                        case Fn(call):\n                            return call(tuple(eval_(x, env) for x in args))\n                        case not_fun:\n                            raise Error(f'cannot apply {not_fun}')\n            case _:\n                return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    @core.built_in('eval')\n    def _(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return eval_(form, repl_env)\n            case _:\n                raise Error('bad arguments')\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n    rep(\"\"\"(def! load-file (fn* (f)\n        (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\", repl_env)\n    match sys.argv:\n        case _, file_name, *args:\n            repl_env['*ARGV*'] = List(String(a) for a in args)\n            rep(f'(load-file \"{file_name}\")', repl_env)\n        case _:\n            repl_env['*ARGV*'] = List()\n            while True:\n                try:\n                    print(rep(mal_readline.input_('user> '), repl_env))\n                except EOFError:\n                    break\n                # pylint: disable-next=broad-exception-caught\n                except Exception as exc:\n                    traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step7_quote.py",
    "content": "import functools\nimport sys\nimport traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List,\n                       Map, Nil, String, Symbol, TCOEnv,\n                       Vector, pr_seq)\n\nimport reader\n\n# Special forms return either a final result or a new TCO context.\nSpecialResult = tuple[Form, Env | None]\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value, None\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return form, let_env\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return last, env\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> SpecialResult:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return args[2], env\n            return Nil.NIL, None\n        return args[1], env\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def fenv(f_args: Sequence[Form]) -> Env:\n                return call_env(env, parms, f_args)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, fenv(f_args))\n\n            return Fn(call, TCOEnv(body, fenv)), None\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_quote(args: Sequence[Form], _env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return form, None\n        case _:\n            raise Error('quote: bad arguments: ' + pr_seq(args))\n\n\ndef qq_loop(acc: List, elt: Form) -> List:\n    match elt:\n        case List([Symbol('splice-unquote'), form]):\n            return List((Symbol('concat'), form, acc))\n        case List([Symbol('splice-unquote'), *args]):\n            raise Error('splice-unquote: bad arguments: ' + pr_seq(args))\n        case _:\n            return List((Symbol('cons'), quasiquote(elt), acc))\n\n\ndef qq_foldr(forms: Sequence[Form]) -> List:\n    return functools.reduce(qq_loop, reversed(forms), List())\n\n\ndef quasiquote(ast: Form) -> Form:\n    match ast:\n        case Map() | Symbol():\n            return List((Symbol('quote'), ast))\n        case Vector():\n            return List((Symbol('vec'), qq_foldr(ast)))\n        case List([Symbol('unquote'), form]):\n            return form\n        case List([Symbol('unquote'), *args]):\n            raise Error('unquote: bad arguments: ' + pr_seq(args))\n        case List():\n            return qq_foldr(ast)\n        case _:\n            return ast\n\n\ndef eval_quasiquote(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return quasiquote(form), env\n        case _:\n            raise Error('quasiquote: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n    'quote': eval_quote,\n    'quasiquote': eval_quasiquote,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    while True:\n        if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n            print(f'EVAL: {ast}')  # , repr(ast))\n            for outer in env.maps:\n                print('  ENV:', ' '.join(f'{k}: {v}'\n                      for k, v in reversed(outer.items()))[:75])\n        match ast:\n            case Symbol():\n                if (value := env.get(ast)) is not None:\n                    return value\n                raise Error(f\"'{ast}' not found\")\n            case Map():\n                return Map((k, eval_(v, env)) for k, v in ast.items())\n            case Vector():\n                return Vector(eval_(x, env) for x in ast)\n            case List([first, *args]):\n                if isinstance(first, Symbol) and (spec := specials.get(first)):\n                    ast, maybe_env = spec(args, env)\n                    if maybe_env is None:\n                        return ast\n                    env = maybe_env\n                else:\n                    match eval_(first, env):\n                        case Fn(tco_env=TCOEnv(body, fenv)):\n                            ast = body\n                            env = fenv(tuple(eval_(x, env) for x in args))\n                        case Fn(call):\n                            return call(tuple(eval_(x, env) for x in args))\n                        case not_fun:\n                            raise Error(f'cannot apply {not_fun}')\n            case _:\n                return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    @core.built_in('eval')\n    def _(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return eval_(form, repl_env)\n            case _:\n                raise Error('bad arguments')\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n    rep(\"\"\"(def! load-file (fn* (f)\n        (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\", repl_env)\n    match sys.argv:\n        case _, file_name, *args:\n            repl_env['*ARGV*'] = List(String(a) for a in args)\n            rep(f'(load-file \"{file_name}\")', repl_env)\n        case _:\n            repl_env['*ARGV*'] = List()\n            while True:\n                try:\n                    print(rep(mal_readline.input_('user> '), repl_env))\n                except EOFError:\n                    break\n                # pylint: disable-next=broad-exception-caught\n                except Exception as exc:\n                    traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step8_macros.py",
    "content": "import functools\nimport sys\nimport traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List, Macro,\n                       Map, Nil, String, Symbol, TCOEnv,\n                       Vector, pr_seq)\n\nimport reader\n\n# Special forms return either a final result or a new TCO context.\nSpecialResult = tuple[Form, Env | None]\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value, None\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return form, let_env\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return last, env\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> SpecialResult:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return args[2], env\n            return Nil.NIL, None\n        return args[1], env\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def fenv(f_args: Sequence[Form]) -> Env:\n                return call_env(env, parms, f_args)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, fenv(f_args))\n\n            return Fn(call, TCOEnv(body, fenv)), None\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_quote(args: Sequence[Form], _env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return form, None\n        case _:\n            raise Error('quote: bad arguments: ' + pr_seq(args))\n\n\ndef qq_loop(acc: List, elt: Form) -> List:\n    match elt:\n        case List([Symbol('splice-unquote'), form]):\n            return List((Symbol('concat'), form, acc))\n        case List([Symbol('splice-unquote'), *args]):\n            raise Error('splice-unquote: bad arguments: ' + pr_seq(args))\n        case _:\n            return List((Symbol('cons'), quasiquote(elt), acc))\n\n\ndef qq_foldr(forms: Sequence[Form]) -> List:\n    return functools.reduce(qq_loop, reversed(forms), List())\n\n\ndef quasiquote(ast: Form) -> Form:\n    match ast:\n        case Map() | Symbol():\n            return List((Symbol('quote'), ast))\n        case Vector():\n            return List((Symbol('vec'), qq_foldr(ast)))\n        case List([Symbol('unquote'), form]):\n            return form\n        case List([Symbol('unquote'), *args]):\n            raise Error('unquote: bad arguments: ' + pr_seq(args))\n        case List():\n            return qq_foldr(ast)\n        case _:\n            return ast\n\n\ndef eval_quasiquote(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return quasiquote(form), env\n        case _:\n            raise Error('quasiquote: bad arguments: ' + pr_seq(args))\n\n\ndef eval_defmacro(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            fun = eval_(form, env)\n            if not isinstance(fun, Fn):\n                raise Error(f'defmacro!: {fun} is not a function')\n            macro = Macro(fun.call)\n            env[key] = macro\n            return macro, None\n        case _:\n            raise Error('defmacro!: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n    'quote': eval_quote,\n    'quasiquote': eval_quasiquote,\n    'defmacro!': eval_defmacro,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    while True:\n        if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n            print(f'EVAL: {ast}')  # , repr(ast))\n            for outer in env.maps:\n                print('  ENV:', ' '.join(f'{k}: {v}'\n                      for k, v in reversed(outer.items()))[:75])\n        match ast:\n            case Symbol():\n                if (value := env.get(ast)) is not None:\n                    return value\n                raise Error(f\"'{ast}' not found\")\n            case Map():\n                return Map((k, eval_(v, env)) for k, v in ast.items())\n            case Vector():\n                return Vector(eval_(x, env) for x in ast)\n            case List([first, *args]):\n                if isinstance(first, Symbol) and (spec := specials.get(first)):\n                    ast, maybe_env = spec(args, env)\n                    if maybe_env is None:\n                        return ast\n                    env = maybe_env\n                else:\n                    match eval_(first, env):\n                        case Macro(call):\n                            ast = call(args)\n                        case Fn(tco_env=TCOEnv(body, fenv)):\n                            ast = body\n                            env = fenv(tuple(eval_(x, env) for x in args))\n                        case Fn(call):\n                            return call(tuple(eval_(x, env) for x in args))\n                        case not_fun:\n                            raise Error(f'cannot apply {not_fun}')\n            case _:\n                return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    @core.built_in('eval')\n    def _(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return eval_(form, repl_env)\n            case _:\n                raise Error('bad arguments')\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n    rep(\"\"\"(def! load-file (fn* (f)\n        (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\", repl_env)\n    rep(\"\"\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0)\n        (list 'if (first xs) (if (> (count xs) 1) (nth xs 1)\n        (throw \"odd number of forms to cond\"))\n        (cons 'cond (rest (rest xs)))))))\"\"\", repl_env)\n    match sys.argv:\n        case _, file_name, *args:\n            repl_env['*ARGV*'] = List(String(a) for a in args)\n            rep(f'(load-file \"{file_name}\")', repl_env)\n        case _:\n            repl_env['*ARGV*'] = List()\n            while True:\n                try:\n                    print(rep(mal_readline.input_('user> '), repl_env))\n                except EOFError:\n                    break\n                # pylint: disable-next=broad-exception-caught\n                except Exception as exc:\n                    traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/step9_try.py",
    "content": "import functools\nimport sys\nimport traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List, Macro,\n                       Map, Nil, String, Symbol, TCOEnv,\n                       ThrownException, Vector, pr_seq)\n\nimport reader\n\n# Special forms return either a final result or a new TCO context.\nSpecialResult = tuple[Form, Env | None]\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value, None\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return form, let_env\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return last, env\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> SpecialResult:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return args[2], env\n            return Nil.NIL, None\n        return args[1], env\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def fenv(f_args: Sequence[Form]) -> Env:\n                return call_env(env, parms, f_args)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, fenv(f_args))\n\n            return Fn(call, TCOEnv(body, fenv)), None\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_quote(args: Sequence[Form], _env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return form, None\n        case _:\n            raise Error('quote: bad arguments: ' + pr_seq(args))\n\n\ndef qq_loop(acc: List, elt: Form) -> List:\n    match elt:\n        case List([Symbol('splice-unquote'), form]):\n            return List((Symbol('concat'), form, acc))\n        case List([Symbol('splice-unquote'), *args]):\n            raise Error('splice-unquote: bad arguments: ' + pr_seq(args))\n        case _:\n            return List((Symbol('cons'), quasiquote(elt), acc))\n\n\ndef qq_foldr(forms: Sequence[Form]) -> List:\n    return functools.reduce(qq_loop, reversed(forms), List())\n\n\ndef quasiquote(ast: Form) -> Form:\n    match ast:\n        case Map() | Symbol():\n            return List((Symbol('quote'), ast))\n        case Vector():\n            return List((Symbol('vec'), qq_foldr(ast)))\n        case List([Symbol('unquote'), form]):\n            return form\n        case List([Symbol('unquote'), *args]):\n            raise Error('unquote: bad arguments: ' + pr_seq(args))\n        case List():\n            return qq_foldr(ast)\n        case _:\n            return ast\n\n\ndef eval_quasiquote(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return quasiquote(form), env\n        case _:\n            raise Error('quasiquote: bad arguments: ' + pr_seq(args))\n\n\ndef eval_defmacro(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            fun = eval_(form, env)\n            if not isinstance(fun, Fn):\n                raise Error(f'defmacro!: {fun} is not a function')\n            macro = Macro(fun.call)\n            env[key] = macro\n            return macro, None\n        case _:\n            raise Error('defmacro!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_try(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [test]:\n            return test, env\n        case [test, List([Symbol('catch*'), Symbol() as key, handler])]:\n            try:\n                return eval_(test, env), None\n            except ThrownException as exc:\n                return handler, env.new_child({key: exc.form})\n            except Error as exc:\n                return handler, env.new_child({key: String(str(exc))})\n        case _:\n            raise Error('try*: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n    'quote': eval_quote,\n    'quasiquote': eval_quasiquote,\n    'defmacro!': eval_defmacro,\n    'try*': eval_try,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    while True:\n        if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n            print(f'EVAL: {ast}')  # , repr(ast))\n            for outer in env.maps:\n                print('  ENV:', ' '.join(f'{k}: {v}'\n                      for k, v in reversed(outer.items()))[:75])\n        match ast:\n            case Symbol():\n                if (value := env.get(ast)) is not None:\n                    return value\n                raise Error(f\"'{ast}' not found\")\n            case Map():\n                return Map((k, eval_(v, env)) for k, v in ast.items())\n            case Vector():\n                return Vector(eval_(x, env) for x in ast)\n            case List([first, *args]):\n                if isinstance(first, Symbol) and (spec := specials.get(first)):\n                    ast, maybe_env = spec(args, env)\n                    if maybe_env is None:\n                        return ast\n                    env = maybe_env\n                else:\n                    match eval_(first, env):\n                        case Macro(call):\n                            ast = call(args)\n                        case Fn(tco_env=TCOEnv(body, fenv)):\n                            ast = body\n                            env = fenv(tuple(eval_(x, env) for x in args))\n                        case Fn(call):\n                            return call(tuple(eval_(x, env) for x in args))\n                        case not_fun:\n                            raise Error(f'cannot apply {not_fun}')\n            case _:\n                return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    @core.built_in('eval')\n    def _(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return eval_(form, repl_env)\n            case _:\n                raise Error('bad arguments')\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n    rep(\"\"\"(def! load-file (fn* (f)\n        (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\", repl_env)\n    rep(\"\"\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0)\n        (list 'if (first xs) (if (> (count xs) 1) (nth xs 1)\n        (throw \"odd number of forms to cond\"))\n        (cons 'cond (rest (rest xs)))))))\"\"\", repl_env)\n    match sys.argv:\n        case _, file_name, *args:\n            repl_env['*ARGV*'] = List(String(a) for a in args)\n            rep(f'(load-file \"{file_name}\")', repl_env)\n        case _:\n            repl_env['*ARGV*'] = List()\n            while True:\n                try:\n                    print(rep(mal_readline.input_('user> '), repl_env))\n                except EOFError:\n                    break\n                # pylint: disable-next=broad-exception-caught\n                except Exception as exc:\n                    traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/stepA_mal.py",
    "content": "# pylint: disable=invalid-name\n# Disabled because the file name contains a capital letter.  Ideally,\n# we would check the rest of the module, but this does not matter much\n# as step9 is almost identical.\n\nimport functools\nimport sys\nimport traceback\nfrom collections.abc import Sequence\n\nimport core\n\nfrom env import call_env\n\nimport mal_readline\n\nfrom mal_types import (Boolean, Env, Error, Fn, Form, List, Macro,\n                       Map, Nil, String, Symbol, TCOEnv,\n                       ThrownException, Vector, pr_seq)\n\nimport reader\n\n# Special forms return either a final result or a new TCO context.\nSpecialResult = tuple[Form, Env | None]\n\n\ndef eval_def(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            value = eval_(form, env)\n            env[key] = value\n            return value, None\n        case _:\n            raise Error('def!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_let(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as binds, form]:\n            if len(binds) % 2:\n                raise Error('let*: odd bind count: ' + pr_seq(binds))\n            let_env = env.new_child()\n            for i in range(0, len(binds), 2):\n                key = binds[i]\n                if not isinstance(key, Symbol):\n                    raise Error(f'let*: {key} is not a symbol')\n                let_env[key] = eval_(binds[i + 1], let_env)\n            return form, let_env\n        case _:\n            raise Error('let*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_do(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [*forms, last]:\n            for form in forms:\n                eval_(form, env)\n            return last, env\n        case _:\n            raise Error('do: no argument')\n\n\ndef eval_if(args: Sequence[Form], env: Env) -> SpecialResult:\n    if 2 <= len(args) <= 3:\n        if eval_(args[0], env) in (Nil.NIL, Boolean.FALSE):\n            if len(args) == 3:\n                return args[2], env\n            return Nil.NIL, None\n        return args[1], env\n    raise Error('if: bad argument count: ' + pr_seq(args))\n\n\ndef eval_fn(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [List() | Vector() as forms, body]:\n            # The new structure convinces mypy.\n            parms = []\n            for parm in forms:\n                if not isinstance(parm, Symbol):\n                    raise Error(f'fn*: {parm} is not a symbol')\n                parms.append(parm)\n\n            def fenv(f_args: Sequence[Form]) -> Env:\n                return call_env(env, parms, f_args)\n\n            def call(f_args: Sequence[Form]) -> Form:\n                return eval_(body, fenv(f_args))\n\n            return Fn(call, TCOEnv(body, fenv)), None\n        case _:\n            raise Error('fn*: bad arguments: ' + pr_seq(args))\n\n\ndef eval_quote(args: Sequence[Form], _env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return form, None\n        case _:\n            raise Error('quote: bad arguments: ' + pr_seq(args))\n\n\ndef qq_loop(acc: List, elt: Form) -> List:\n    match elt:\n        case List([Symbol('splice-unquote'), form]):\n            return List((Symbol('concat'), form, acc))\n        case List([Symbol('splice-unquote'), *args]):\n            raise Error('splice-unquote: bad arguments: ' + pr_seq(args))\n        case _:\n            return List((Symbol('cons'), quasiquote(elt), acc))\n\n\ndef qq_foldr(forms: Sequence[Form]) -> List:\n    return functools.reduce(qq_loop, reversed(forms), List())\n\n\ndef quasiquote(ast: Form) -> Form:\n    match ast:\n        case Map() | Symbol():\n            return List((Symbol('quote'), ast))\n        case Vector():\n            return List((Symbol('vec'), qq_foldr(ast)))\n        case List([Symbol('unquote'), form]):\n            return form\n        case List([Symbol('unquote'), *args]):\n            raise Error('unquote: bad arguments: ' + pr_seq(args))\n        case List():\n            return qq_foldr(ast)\n        case _:\n            return ast\n\n\ndef eval_quasiquote(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [form]:\n            return quasiquote(form), env\n        case _:\n            raise Error('quasiquote: bad arguments: ' + pr_seq(args))\n\n\ndef eval_defmacro(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [Symbol() as key, form]:\n            fun = eval_(form, env)\n            if not isinstance(fun, Fn):\n                raise Error(f'defmacro!: {fun} is not a function')\n            macro = Macro(fun.call)\n            env[key] = macro\n            return macro, None\n        case _:\n            raise Error('defmacro!: bad arguments: ' + pr_seq(args))\n\n\ndef eval_try(args: Sequence[Form], env: Env) -> SpecialResult:\n    match args:\n        case [test]:\n            return test, env\n        case [test, List([Symbol('catch*'), Symbol() as key, handler])]:\n            try:\n                return eval_(test, env), None\n            except ThrownException as exc:\n                return handler, env.new_child({key: exc.form})\n            except Error as exc:\n                return handler, env.new_child({key: String(str(exc))})\n        case _:\n            raise Error('try*: bad arguments: ' + pr_seq(args))\n\n\nspecials = {\n    'def!': eval_def,\n    'let*': eval_let,\n    'do': eval_do,\n    'if': eval_if,\n    'fn*': eval_fn,\n    'quote': eval_quote,\n    'quasiquote': eval_quasiquote,\n    'defmacro!': eval_defmacro,\n    'try*': eval_try,\n}\n\n\ndef eval_(ast: Form, env: Env) -> Form:\n    while True:\n        if env.get('DEBUG-EVAL') not in (None, Nil.NIL, Boolean.FALSE):\n            print(f'EVAL: {ast}')  # , repr(ast))\n            for outer in env.maps:\n                print('  ENV:', ' '.join(f'{k}: {v}'\n                      for k, v in reversed(outer.items()))[:75])\n        match ast:\n            case Symbol():\n                if (value := env.get(ast)) is not None:\n                    return value\n                raise Error(f\"'{ast}' not found\")\n            case Map():\n                return Map((k, eval_(v, env)) for k, v in ast.items())\n            case Vector():\n                return Vector(eval_(x, env) for x in ast)\n            case List([first, *args]):\n                if isinstance(first, Symbol) and (spec := specials.get(first)):\n                    ast, maybe_env = spec(args, env)\n                    if maybe_env is None:\n                        return ast\n                    env = maybe_env\n                else:\n                    match eval_(first, env):\n                        case Macro(call):\n                            ast = call(args)\n                        case Fn(tco_env=TCOEnv(body, fenv)):\n                            ast = body\n                            env = fenv(tuple(eval_(x, env) for x in args))\n                        case Fn(call):\n                            return call(tuple(eval_(x, env) for x in args))\n                        case not_fun:\n                            raise Error(f'cannot apply {not_fun}')\n            case _:\n                return ast\n\n\ndef rep(source: str, env: Env) -> str:\n    return str(eval_(reader.read(source), env))\n\n\ndef main() -> None:\n    repl_env = Env(core.ns)  # Modifying ns is OK.\n\n    @core.built_in('eval')\n    def _(args: Sequence[Form]) -> Form:\n        match args:\n            case [form]:\n                return eval_(form, repl_env)\n            case _:\n                raise Error('bad arguments')\n\n    rep('(def! not (fn* (a) (if a false true)))', repl_env)\n    rep(\"\"\"(def! load-file (fn* (f)\n        (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"\"\", repl_env)\n    rep(\"\"\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0)\n        (list 'if (first xs) (if (> (count xs) 1) (nth xs 1)\n        (throw \"odd number of forms to cond\"))\n        (cons 'cond (rest (rest xs)))))))\"\"\", repl_env)\n    rep('(def! *host-language* \"python3\")', repl_env)\n    match sys.argv:\n        case _, file_name, *args:\n            repl_env['*ARGV*'] = List(String(a) for a in args)\n            rep(f'(load-file \"{file_name}\")', repl_env)\n        case _:\n            repl_env['*ARGV*'] = List()\n            rep('(println (str \"Mal [\" *host-language* \"]\"))', repl_env)\n            while True:\n                try:\n                    print(rep(mal_readline.input_('user> '), repl_env))\n                except EOFError:\n                    break\n                # pylint: disable-next=broad-exception-caught\n                except Exception as exc:\n                    traceback.print_exception(exc, limit=10)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "impls/python3/tests/__init__.py",
    "content": ""
  },
  {
    "path": "impls/python3/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/python3/tests/stepA_mal.mal",
    "content": "*host-language*\n;=>\"python3\"\n\n;; Testing Python interop\n\n;; Testing Python expressions\n\n(py* \"7\")\n;=>7\n\n(py* \"'7'\")\n;=>\"7\"\n\n(py* \"[7,8,9]\")\n;=>(7 8 9)\n\n(py* \"' '.join(f'X{c}Y' for c in 'abc')\")\n;=>\"XaY XbY XcY\"\n\n(py* \"list(1 + x for x in range(1, 4))\")\n;=>(2 3 4)\n\n;; Testing Python statements\n\n(py!* \"print('hello')\")\n;/hello\n;=>nil\n\n(py!* \"foo = 19 % 4\")\n;=>nil\n(py* \"foo\")\n;=>3\n"
  },
  {
    "path": "impls/python3/tests/test_step2.py",
    "content": "import unittest\n\nimport step2_eval\n\n\nclass TestStep3(unittest.TestCase):\n    def test_step3_let_multiple(self):\n        self.assertEqual('{\"a\" 15}', step2_eval.rep('{\"a\" (+ 7 8)} '))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step3.py",
    "content": "import unittest\n\nimport mal_types\nimport step3_env\nfrom env import Env\nfrom mal_types import MalList, MalInt\nfrom mal_types import MalSymbol\nfrom mal_types import MalUnknownSymbolException, MalInvalidArgumentException\n\n\nclass TestStep3(unittest.TestCase):\n    def test_env_find(self):\n        e = Env(None)\n        e.set(\"key\", MalInt(1))\n        result = e.find(\"key\")\n        self.assertTrue(e is result)\n\n    def test_env_find_outer(self):\n        outer = Env(None)\n        e = Env(outer)\n        outer.set(\"key\", MalInt(1))\n        result = e.find(\"key\")\n        self.assertTrue(result is outer)\n\n    def test_env_find_no_key(self):\n        e = Env(None)\n        self.assertEqual(None, e.find(\"key\"))\n\n    def test_env_get(self):\n        env = Env(None)\n        expression = MalInt(1)\n        env.set(\"key\", expression)\n        self.assertTrue(env.get(\"key\") is expression)\n\n    def test_env_get_error(self):\n        env = Env(None)\n        try:\n            env.get(\"key\")\n            self.fail(\"Expected an exeception\")\n        except MalUnknownSymbolException:\n            pass\n\n    def test_MalFunctionCompiled(self):\n        self.assertEqual(\n            \"3\",\n            str(\n                mal_types.MalFunctionCompiled(\n                    lambda a: MalInt(a[0].native() + a[1].native())\n                ).call([mal_types.MalInt(1), mal_types.MalInt(2)])\n            ),\n        )\n\n    def test_eval_invalid(self):\n        with self.assertRaises(MalInvalidArgumentException):\n            step3_env.EVAL(MalList([MalInt(1), MalInt(2)]), Env(None))\n\n    def test_eval_1_plus_1(self):\n        env = Env(None)\n        env.set(\n            \"+\",\n            mal_types.MalFunctionCompiled(\n                lambda a: MalInt(a[0].native() + a[1].native())\n            ),\n        )\n        self.assertEqual(\n            2,\n            step3_env.EVAL(\n                MalList([MalSymbol(\"+\"), MalInt(1), MalInt(1)]), env\n            ).native(),\n        )\n\n    def test_def(self):\n        env = Env(None)\n        self.assertEqual(\n            1,\n            step3_env.EVAL(\n                MalList([MalSymbol(\"def!\"), MalSymbol(\"a\"), MalInt(1)]), env\n            ).native(),\n        )\n        self.assertEqual(1, env.get(\"a\").native())\n\n    def test_mallist_native(self):\n        x = MalInt(1)\n        self.assertEqual([x], MalList([x]).native())\n\n    def test_let_basic(self):\n        env = Env(None)\n        self.assertEqual(\n            2,\n            step3_env.EVAL(\n                MalList(\n                    [\n                        MalSymbol(\"let*\"),\n                        MalList([MalSymbol(\"c\"), MalInt(2)]),\n                        MalSymbol(\"c\"),\n                    ]\n                ),\n                env,\n            ).native(),\n        )\n\n    def test_let_advanced(self):\n        env = Env(None)\n        env.set(\n            \"+\",\n            mal_types.MalFunctionCompiled(\n                lambda a: MalInt(a[0].native() + a[1].native())\n            ),\n        )\n        self.assertEqual(\n            4,\n            step3_env.EVAL(\n                MalList(\n                    [\n                        MalSymbol(\"let*\"),\n                        MalList([MalSymbol(\"c\"), MalInt(2)]),\n                        MalList([MalSymbol(\"+\"), MalSymbol(\"c\"), MalInt(2)]),\n                    ]\n                ),\n                env,\n            ).native(),\n        )\n\n    def test_let_multiple(self):\n        env = Env(None)\n        env.set(\n            \"+\",\n            mal_types.MalFunctionCompiled(\n                lambda a: MalInt(a[0].native() + a[1].native())\n            ),\n        )\n        self.assertEqual(\n            5,\n            step3_env.EVAL(\n                MalList(\n                    [\n                        MalSymbol(\"let*\"),\n                        MalList([MalSymbol(\"c\"), MalInt(2), MalSymbol(\"d\"), MalInt(3)]),\n                        MalList([MalSymbol(\"+\"), MalSymbol(\"c\"), MalSymbol(\"d\")]),\n                    ]\n                ),\n                env,\n            ).native(),\n        )\n\n    def test_step3_let_multiple(self):\n        self.assertEqual(\"5\", step3_env.rep(\"(let* (c 2 d 3) (+ c d))\"))\n\n    def test_step3_let_nested_backref(self):\n        self.assertEqual(\"6\", step3_env.rep(\"(let* (c 2 d c) (+ c (+ d 2)))\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step4.py",
    "content": "import unittest\n\nimport step4_if_fn_do\nfrom env import Env\nfrom mal_types import MalInvalidArgumentException\nfrom mal_types import MalList, MalInt, MalFunctionCompiled, MalBoolean\nfrom mal_types import MalSymbol\n\n\nclass TestStep4(unittest.TestCase):\n    def test_step4_nil(self):\n        self.assertEqual(\"nil\", step4_if_fn_do.rep(\"nil\"))\n\n    def test_step4_boolean(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"true\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"false\"))\n\n    def test_print_function(self):\n        self.assertEqual(\"#<function>\", str(MalFunctionCompiled(lambda x: MalInt(0))))\n\n    def test_if_basic_true(self):\n        env = Env(None)\n        self.assertEqual(\n            4321,\n            step4_if_fn_do.EVAL(\n                MalList(\n                    [MalSymbol(\"if\"), MalBoolean(True), MalInt(4321), MalInt(1234)]\n                ),\n                env,\n            ).native(),\n        )\n\n    def test_if_basic_false(self):\n        env = Env(None)\n        self.assertEqual(\n            1234,\n            step4_if_fn_do.EVAL(\n                MalList(\n                    [MalSymbol(\"if\"), MalBoolean(False), MalInt(4321), MalInt(1234)]\n                ),\n                env,\n            ).native(),\n        )\n\n    def test_if_basic_false_no_fourth_arg(self):\n        env = Env(None)\n        self.assertEqual(\n            \"nil\",\n            str(\n                step4_if_fn_do.EVAL(\n                    MalList([MalSymbol(\"if\"), MalBoolean(False), MalInt(4321)]), env\n                )\n            ),\n        )\n\n    def test_env_constructor_binds(self):\n        env = Env(outer=None, binds=[MalSymbol(\"a\")], exprs=[MalInt(3)])\n        self.assertEqual(3, env.get(\"a\").native())\n\n    def test_env_constructor_binds_multiple(self):\n        env = Env(\n            outer=None,\n            binds=[MalSymbol(\"a\"), MalSymbol(\"b\")],\n            exprs=[MalInt(44), MalInt(32)],\n        )\n        self.assertEqual(44, env.get(\"a\").native())\n        self.assertEqual(32, env.get(\"b\").native())\n\n    def test_step4_do(self):\n        self.assertEqual(\"44\", step4_if_fn_do.rep(\"(do 1 2 3 44)\"))\n        self.assertEqual(\"21\", step4_if_fn_do.rep(\"(do 21)\"))\n\n    def test_step4_fn(self):\n        self.assertEqual(\"#<function>\", step4_if_fn_do.rep(\"(fn* (a) 0)\"))\n\n    def test_step4_use_fn(self):\n        self.assertEqual(\"7\", step4_if_fn_do.rep(\"((fn* (a) a) 7)\"))\n\n    def test_step4_use_fn_multiple(self):\n        self.assertEqual(\"8\", step4_if_fn_do.rep(\"((fn* (a b) a) 8 9)\"))\n\n    def test_step4_use_fn_multiple_nested(self):\n        self.assertEqual(\"10\", step4_if_fn_do.rep(\"((fn* (a b) (+ a (+ b 1))) 4 5)\"))\n\n    def test_step4_use_fn_func_param(self):\n        self.assertEqual(\n            \"8\", step4_if_fn_do.rep(\"((fn* (f x) (f x)) (fn* (a) (+ 1 a)) 7)\")\n        )\n\n    def test_step4_prn(self):\n        self.assertEqual(\"nil\", step4_if_fn_do.rep(\"(prn 4)\"))\n\n    def test_step4_list(self):\n        self.assertEqual(\"(1 2 (3 4) 5)\", step4_if_fn_do.rep(\"(list 1 2 (list 3 4) 5)\"))\n\n    def test_step4_listP(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(list? (list 1 2))\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(list? 4)\"))\n\n    def test_step4_empty(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(empty? (list))\"))\n\n    def test_step4_count(self):\n        self.assertEqual(\"0\", step4_if_fn_do.rep(\"(count (list))\"))\n        self.assertEqual(\"2\", step4_if_fn_do.rep(\"(count (list 1 2))\"))\n        self.assertEqual(\"0\", step4_if_fn_do.rep(\"(count nil)\"))\n\n    def test_step4_equal(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(= 0 0)\"))\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(= (list 1) (list 1))\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(= (list 1) (list 1 2))\"))\n        self.assertEqual(\n            \"true\",\n            step4_if_fn_do.rep(\"(= (list (list 1) (list 2)) (list (list 1) (list 2)))\"),\n        )\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(= nil nil)\"))\n\n    def test_step4_less(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(< 1 2)\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(< 2 1)\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(< 1 1)\"))\n        try:\n            step4_if_fn_do.rep(\"(< 1 nil)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n        try:\n            step4_if_fn_do.rep(\"(< nil 1)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n\n    def test_step4_less_equal(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(<= 1 2)\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(<= 2 1)\"))\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(<= 1 1)\"))\n        try:\n            step4_if_fn_do.rep(\"(<= 1 nil)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n        try:\n            step4_if_fn_do.rep(\"(<= nil 1)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n\n    def test_step4_more(self):\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(> 1 2)\"))\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(> 2 1)\"))\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(> 1 1)\"))\n        try:\n            step4_if_fn_do.rep(\"(> 1 nil)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n        try:\n            step4_if_fn_do.rep(\"(> nil 1)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n\n    def test_step4_more_equal(self):\n        self.assertEqual(\"false\", step4_if_fn_do.rep(\"(>= 1 2)\"))\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(>= 2 1)\"))\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(>= 1 1)\"))\n        try:\n            step4_if_fn_do.rep(\"(>= 1 nil)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n        try:\n            step4_if_fn_do.rep(\"(>= nil 1)\")\n            self.fail(\"Expected exception\")\n        except MalInvalidArgumentException:\n            pass\n\n    def test_step4_closures(self):\n        self.assertEqual(\n            \"12\", step4_if_fn_do.rep(\"(( (fn* (a) (fn* (b) (+ a b))) 5) 7)\")\n        )\n        self.assertEqual(\n            \"#<function>\",\n            step4_if_fn_do.rep(\"(def! gen-plus5 (fn* () (fn* (b) (+ 5 b))))\"),\n        )\n        self.assertEqual(\n            \"#<function>\",\n            step4_if_fn_do.rep(\"(def! gen-plus5 (fn* () (fn* (b) (+ 5 b))))\"),\n        )\n        self.assertEqual(\"#<function>\", step4_if_fn_do.rep(\"(def! plus5 (gen-plus5))\"))\n        self.assertEqual(\"12\", step4_if_fn_do.rep(\"(plus5 7)\"))\n\n    def test_step4_variadic_a(self):\n        self.assertEqual(\n            \"3\", step4_if_fn_do.rep(\"( (fn* (& more) (count more)) 1 2 3)\")\n        )\n\n    def test_step4_variadic_b(self):\n        self.assertEqual(\"0\", step4_if_fn_do.rep(\"((fn* (& more) (count more)))\"))\n\n    def test_step4_quoted_string(self):\n        self.assertEqual('\"\\\\\"\"', step4_if_fn_do.rep('\"\\\\\"\"'))\n\n    def test_step4_str(self):\n        self.assertEqual('\"(1 a 2 3)\"', step4_if_fn_do.rep('(str (list 1 \"a\" 2 3))'))\n\n    def test_step4_equal_vector_list(self):\n        self.assertEqual(\"true\", step4_if_fn_do.rep(\"(=[] (list))\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step5.py",
    "content": "import unittest\n\nimport step5_tco\n\n\nclass TestStep5(unittest.TestCase):\n    def test_step5_tco(self):\n        self.assertEqual(\n            \"#<function>\",\n            step5_tco.rep(\n                \"(def! sum2 (fn* (n acc) (if (= n 0) acc (sum2 (- n 1) (+ n acc)))))\"\n            ),\n        )\n        self.assertEqual(\"55\", step5_tco.rep(\"(sum2 10 0)\"))\n        self.assertEqual(\"nil\", step5_tco.rep(\"(def! res2 nil)\"))\n        self.assertEqual(\"500500\", step5_tco.rep(\"(def! res2 (sum2 1000 0))\"))\n        self.assertEqual(\"500500\", step5_tco.rep(\"res2\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step6.py",
    "content": "import unittest\n\nimport reader\nimport step6_file\nfrom env import Env\nfrom mal_types import MalList, MalAtom, MalInt\nfrom mal_types import MalSyntaxException, MalString\n\n\nclass TestStep6(unittest.TestCase):\n    def test_step6_string_unbalanced(self):\n        with self.assertRaises(MalSyntaxException):\n            step6_file.rep('\"foo')\n\n    def test_step6_standard_string(self):\n        self.assertEqual(\n            '\"foo\"', step6_file.EVAL(MalString('\"foo\"'), Env(None)).native()\n        )\n        self.assertEqual('\"foo\"', step6_file.rep('\"foo\"').__str__())\n        self.assertEqual('\"foo\"', MalString('\"foo\"').native())\n        self.assertEqual('\"\\\\\"foo\\\\\"\"', MalString('\"foo\"').__str__())\n\n    def test_step6_reader_read_string(self):\n        read = reader.read('(read-string \"(1 2   (3  4) nil)\")')\n        self.assertTrue(isinstance(read, MalList))\n        arg = read.native()[1]\n        self.assertTrue(isinstance(arg, MalString))\n        native_str = arg.native()\n        self.assertEqual(\"(1 2   (3  4) nil)\", native_str)\n\n    def test_step6_read_string_no_escapes(self):\n        self.assertEqual(\n            \"(1 2 (3 4) nil)\", step6_file.rep('(read-string \"(1 2   (3  4) nil)\")')\n        )\n\n    def test_step6_slurp(self):\n        self.assertEqual(\n            '\"A line of text\\\\n\"', step6_file.rep('(slurp \"../../tests/test.txt\")')\n        )\n\n    def test_step6_eval(self):\n        self.assertEqual(\"2\", step6_file.rep('(eval (read-string \"(+ 1 1)\"))'))\n\n    def test_step6_str(self):\n        self.assertEqual('\"abc2def ghi\"', step6_file.rep('(str \"abc\" 2 \"def\" \" ghi\")'))\n\n    def test_step6_atom_type(self):\n        atom = step6_file.EVAL(MalAtom(MalInt(1)), Env(None))\n        self.assertEqual(1, atom.native().native())\n\n    def test_step6_read_atom(self):\n        atom = step6_file.EVAL(step6_file.READ(\"(atom 1)\"), step6_file.repl_env)\n        self.assertEqual(1, atom.native().native())\n\n    def test_step6_atom_deref(self):\n        self.assertEqual(\"1\", step6_file.rep(\"(deref (atom 1))\"))\n\n    def test_step6_atom_p(self):\n        self.assertEqual(\"true\", step6_file.rep(\"(atom? (atom 1))\"))\n        self.assertEqual(\"false\", step6_file.rep(\"(atom? (+ 1 2))\"))\n\n    def test_step6_reset(self):\n        self.assertEqual(\"3\", step6_file.rep(\"(do (def! a (atom 2)) (reset! a 3))\"))\n\n    def test_step6_swap(self):\n        self.assertEqual(\"#<function>\", step6_file.rep(\"(def! inc3 (fn* (a) (+ 3 a)))\"))\n        self.assertEqual(\"(atom 2)\", step6_file.rep(\"(def! a (atom 2))\"))\n        self.assertEqual(\"3\", step6_file.rep(\"(swap! a + 1)\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step7.py",
    "content": "import unittest\n\nimport step7_quote\n\n\nclass TestStep7(unittest.TestCase):\n    def test_step7_cons(self):\n        self.assertEqual(\"(1)\", step7_quote.rep(\"(cons 1 (list))\"))\n\n    def test_step7_concat(self):\n        self.assertEqual(\"()\", step7_quote.rep(\"(concat)\"))\n\n    def test_step7_quote(self):\n        self.assertEqual(\"(+ 1 2)\", step7_quote.rep(\"(quote (+ 1 2))\"))\n\n    def test_step7_quasiquote(self):\n        self.assertEqual(\n            \"(+ 1 3)\", step7_quote.rep(\"(quasiquote (+ 1 (unquote (+ 1 2))))\")\n        )\n\n    def test_step7_quasiquote_advanced(self):\n        self.assertEqual(\"(2)\", step7_quote.rep(\"(def! c '(2))\"))\n        self.assertEqual(\"(1 2 3)\", step7_quote.rep(\"`[1 ~@c 3]\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step8.py",
    "content": "import unittest\n\nimport core\nimport step8_macros\nfrom env import Env\nfrom mal_types import MalFunctionCompiled, MalInt, MalFunctionRaw, MalList\nfrom mal_types import MalInvalidArgumentException, MalIndexError\n\n\nclass TestStep8(unittest.TestCase):\n    def setUp(self) -> None:\n        self._repl_env = step8_macros.init_repl_env()\n\n    def rep(self, input: str) -> str:\n        return step8_macros.rep(input, self._repl_env)\n\n    def test_step8_is_macro(self):\n        self.assertEqual(False, MalFunctionCompiled(lambda a: MalInt(1)).is_macro())\n        self.assertEqual(\n            False,\n            MalFunctionRaw(core.ns[\"+\"], MalInt(1), MalList([]), Env(None)).is_macro(),\n        )\n\n    def test_step8_defmacro(self):\n        self.assertEqual(\"#<macro>\", self.rep(\"(defmacro! one (fn* () 1))\"))\n\n    def test_step8_quote_reader_macro(self):\n        self.assertEqual(\"(+ 1 2)\", self.rep(\"'(+ 1 2)\"))\n\n    def test_step8_quasiquote_unquote_reader_macros(self):\n        self.assertEqual(\"(+ 1 3)\", self.rep(\"`(+ 1 ~(+ 1 2))\"))\n\n    def test_step8_repl_env_isolation(self):\n        env1 = step8_macros.init_repl_env()\n        step8_macros.rep(\"(def! a 2)\", env1)\n        env2 = step8_macros.init_repl_env()\n        step8_macros.rep(\"(def! a 3)\", env2)\n        self.assertEqual(\"2\", step8_macros.rep(\"a\", env1))\n        self.assertEqual(\"3\", step8_macros.rep(\"a\", env2))\n        self.assertEqual(\"6\", step8_macros.rep(\"(eval (list + a 3))\", env2))\n\n    def test_step8_is_macro_call(self):\n        self.rep(\"(defmacro! macro (fn* () 1))\")\n        self.rep(\"(def! func (fn* () 1))\")\n        self.rep(\"(def! q 4)\")\n        macro = step8_macros.READ(\"(macro)\")\n        func = step8_macros.READ(\"(func)\")\n        other1 = step8_macros.READ(\"(x)\")\n        other2 = step8_macros.READ(\"(1)\")\n        other3 = step8_macros.READ(\"(2)\")\n        other4 = step8_macros.READ(\"(q)\")\n        self.assertTrue(step8_macros.is_macro_call(macro, self._repl_env))\n        self.assertFalse(step8_macros.is_macro_call(func, self._repl_env))\n        self.assertFalse(step8_macros.is_macro_call(other1, self._repl_env))\n        self.assertFalse(step8_macros.is_macro_call(other2, self._repl_env))\n        self.assertFalse(step8_macros.is_macro_call(other3, self._repl_env))\n        self.assertFalse(step8_macros.is_macro_call(other4, self._repl_env))\n\n    def test_step8_macroexpand(self):\n        self.rep(\"(def! func (fn* () 1))\")\n        func = step8_macros.READ(\"(func)\")\n        self.assertEqual(\"(func)\", str(step8_macros.macroexpand(func, self._repl_env)))\n        self.rep(\"(defmacro! macro (fn* () 1))\")\n        macro = step8_macros.READ(\"(macro)\")\n        self.assertEqual(\"1\", str(step8_macros.macroexpand(macro, self._repl_env)))\n        self.rep(\"(defmacro! unless (fn* (pred a b) `(if ~pred ~b ~a)))\")\n        self.assertEqual(\"(if true 7 8)\", self.rep(\"(macroexpand (unless true 8 7))\"))\n\n    def test_step8_not(self):\n        self.assertEqual(\"true\", self.rep(\"(not (not true))\"))\n        self.assertEqual(\"true\", self.rep(\"(not nil)\"))\n        self.assertEqual(\"false\", self.rep(\"(not 1)\"))\n        self.assertEqual(\"true\", self.rep(\"(not false)\"))\n\n    def test_step8_let(self):\n        self.assertEqual(\"2\", self.rep(\"(let* (a 1 b 2) b)\"))\n\n    def test_step8_first(self):\n        self.assertEqual(\"2\", self.rep(\"(first (list 2 3 4))\"))\n        self.assertEqual(\"nil\", self.rep(\"(first (list))\"))\n        self.assertEqual(\"nil\", self.rep(\"(first nil)\"))\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(first 1)\")\n\n    def test_step8_rest(self):\n        self.assertEqual(\"(2 3)\", self.rep(\"(rest (list 1 2 3))\"))\n        self.assertEqual(\"()\", self.rep(\"(rest (list))\"))\n        self.assertEqual(\"()\", self.rep(\"(rest nil)\"))\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(rest 1)\")\n\n    def test_step8_nth(self):\n        self.assertEqual(\"3\", self.rep(\"(nth '(1 2 3) 2)\"))\n\n        with self.assertRaises(MalIndexError):\n            self.rep(\"(nth () 1)\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_step9.py",
    "content": "import unittest\n\nimport step9_try\nfrom mal_types import MalException, MalIndexError, MalInvalidArgumentException\n\n\nclass TestStep9(unittest.TestCase):\n    def setUp(self) -> None:\n        self._repl_env = step9_try.init_repl_env()\n\n    def rep(self, input: str) -> str:\n        return step9_try.rep(input, self._repl_env)\n\n    def test_step9_throw(self):\n        with self.assertRaises(MalException):\n            self.assertEqual(\"foo\", self.rep('(throw \"err1\")'))\n\n    def test_step9_try_catch(self):\n        self.assertEqual(\"123\", self.rep(\"(try* 123 (catch* e 456))\"))\n        self.assertEqual(\n            \"nil\", self.rep('(try* (abc 1 2) (catch* exc (prn \"exc is:\" exc)))')\n        )\n\n    def test_step9_nth(self):\n        self.assertEqual(\"3\", self.rep(\"(nth '(1 2 3) 2)\"))\n\n        with self.assertRaises(MalIndexError):\n            self.rep(\"(nth () 1)\")\n\n    def test_step9_apply(self):\n        self.assertEqual(\"(1 1)\", self.rep(\"(apply list '(1 1))\"))\n        self.assertEqual(\"(1 2 1 2)\", self.rep(\"(apply list 1 2 '(1 2))\"))\n\n    def test_step9_map(self):\n        self.assertEqual(\"((1) (2))\", self.rep(\"(map list '(1 2))\"))\n\n    def test_step9_symbol_q(self):\n        self.assertEqual(\"true\", self.rep(\"(symbol? 'x)\"))\n        self.assertEqual(\"false\", self.rep(\"(symbol? nil)\"))\n\n    def test_step9_nil(self):\n        self.assertEqual(\"true\", self.rep(\"(nil? nil)\"))\n        self.assertEqual(\"false\", self.rep(\"(nil? 1)\"))\n\n    def test_step9_true(self):\n        self.assertEqual(\"true\", self.rep(\"(true? true)\"))\n        self.assertEqual(\"false\", self.rep(\"(true? false)\"))\n        self.assertEqual(\"false\", self.rep(\"(true? nil)\"))\n        self.assertEqual(\"false\", self.rep(\"(true? 1)\"))\n\n    def test_step9_false(self):\n        self.assertEqual(\"true\", self.rep(\"(false? false)\"))\n        self.assertEqual(\"false\", self.rep(\"(false? true)\"))\n        self.assertEqual(\"false\", self.rep(\"(false? nil)\"))\n        self.assertEqual(\"false\", self.rep(\"(false? 1)\"))\n\n    def test_step9_throw_hash_map(self):\n        with self.assertRaises(MalException):\n            self.rep('(throw {:msg \"err2\"})')\n\n    def test_step9_symbol(self):\n        self.assertEqual(\"abc\", self.rep('(symbol \"abc\")'))\n\n    def test_step9_complex_apply(self):\n        self.assertEqual(\"9\", self.rep(\"(apply + 4 [5])\"))\n\n    def test_step9_get(self):\n        self.assertEqual(\"nil\", self.rep('(get nil \"a\")'))\n        self.assertEqual(\"nil\", self.rep('(get (hash-map) \"a\")'))\n\n    def test_step9_complex_str(self):\n        self.assertEqual('\"A{:abc val}Z\"', self.rep('(str \"A\" {:abc \"val\"} \"Z\")'))\n\n    def test_step9_sequential_q(self):\n        self.assertEqual(\"true\", self.rep(\"(sequential? (list 1 2 3))\"))\n        self.assertEqual(\"true\", self.rep(\"(sequential? ())\"))\n        self.assertEqual(\"false\", self.rep(\"(sequential? nil)\"))\n        self.assertEqual(\"false\", self.rep(\"(sequential? 1)\"))\n        self.assertEqual(\"true\", self.rep(\"(sequential? [1 2 3])\"))\n        self.assertEqual(\"true\", self.rep(\"(sequential? [])\"))\n        self.assertEqual(\"false\", self.rep(\"(sequential? {})\"))\n\n    def test_step9_vector(self):\n        self.assertEqual(\"[1 2 3]\", self.rep(\"(vector 1 2 3)\"))\n        self.assertEqual(\"[]\", self.rep(\"(vector)\"))\n        self.assertEqual(\"[[1 2]]\", self.rep(\"(vector [1 2])\"))\n        self.assertEqual(\"[nil]\", self.rep(\"(vector nil)\"))\n\n    def test_step9_hash_map(self):\n        self.assertEqual(\"{}\", self.rep(\"(hash-map)\"))\n        self.assertEqual('{\"a\" 1}', self.rep('(hash-map \"a\" 1)'))\n        self.assertEqual('{\"a\" 1 \"b\" 2}', self.rep('(hash-map \"a\" 1 \"b\" 2)'))\n\n    def test_step9_assoc(self):\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(assoc)\")\n        self.assertEqual(\"1\", self.rep(\"(assoc 1)\"))\n        self.assertEqual(\"nil\", self.rep(\"(assoc nil)\"))\n        self.assertEqual(\"{}\", self.rep(\"(assoc {})\"))\n        self.assertEqual('{\"a\" 1}', self.rep('(assoc {} \"a\" 1)'))\n        self.assertEqual('{\"b\" 2 \"a\" 1}', self.rep('(assoc {\"b\" 2} \"a\" 1)'))\n        self.assertEqual('{\"b\" 2 \"a\" 1 \"c\" 3}', self.rep('(assoc {\"b\" 2} \"a\" 1 \"c\" 3)'))\n        self.assertEqual('{\"b\" 3}', self.rep('(assoc {\"b\" 2} \"b\" 3)'))\n        self.assertEqual(\"{:bcd 234}\", self.rep(\"(assoc {} :bcd 234)\"))\n\n    def test_step9_contains_q(self):\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(contains?)\")\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(contains? 1)\")\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(contains? nil)\")\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(contains? nil nil)\")\n        self.assertEqual(\"false\", self.rep(\"(contains? {} nil)\"))\n        self.assertEqual(\"true\", self.rep('(contains? {\"a\" 1} \"a\")'))\n        self.assertEqual(\"true\", self.rep('(contains? {\"a\" 1 :b 2} :b)'))\n\n    def test_step9_keys(self):\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(keys)\")\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(keys 1)\")\n        self.assertEqual('(\"a\")', self.rep('(keys {\"a\" 1})'))\n        self.assertEqual('(\"a\" :b)', self.rep('(keys {\"a\" 1 :b 2})'))\n\n    def test_step9_vals(self):\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(vals)\")\n        with self.assertRaises(MalInvalidArgumentException):\n            self.rep(\"(vals 1)\")\n        self.assertEqual(\"(1)\", self.rep('(vals {\"a\" 1})'))\n        self.assertEqual(\"(1 2)\", self.rep('(vals {\"a\" 1 :b 2})'))\n\n    def test_step9_dissoc(self):\n        self.assertEqual('{\"c\" 3}', self.rep('(dissoc {\"a\" 1 \"b\" 2 \"c\" 3} \"a\" \"b\")'))\n        self.assertEqual(\n            '{\"c\" 3}', self.rep('(dissoc {\"a\" 1 \"b\" 2 \"c\" 3} \"a\" \"b\" \"d\")')\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/python3/tests/test_stepA.py",
    "content": "import unittest\n\nimport stepA_mal\n\n\nclass TestStepA(unittest.TestCase):\n    def setUp(self) -> None:\n        self._repl_env = stepA_mal.init_repl_env()\n\n    def rep(self, input: str) -> str:\n        return stepA_mal.rep(input, self._repl_env)\n\n    def test_stepA_host_language(self):\n        self.assertEqual('\"python.2\"', self.rep(\"*host-language*\"))\n\n    def test_stepA_eval_vector(self):\n        self.assertEqual(\"[1 2 3]\", self.rep(\"[1 2 (+ 1 2)]\"))\n\n    def test_reader_multiple_lines(self):\n        self.assertEqual(\"3\", self.rep(\"(do\\n1\\n2\\n3\\n)\"))\n\n    def test_read_string_multiple_lines(self):\n        self.assertEqual(\n            \"(do 2 nil)\",\n            self.rep('(read-string (str \"(do \\n\" \";; read\\n\" \"2\\n\" \"\\n nil)\"))'),\n        )\n\n    def test_read_hash_map(self):\n        self.assertEqual(\"{}\", self.rep(\"{}\"))\n        self.assertEqual('{\"a\" 1}', self.rep('{\"a\" 1}'))\n        self.assertEqual('{\"1\" 2 \"3\" 4}', self.rep('{\"1\" 2 \"3\" 4}'))\n\n    def test_get(self):\n        self.assertEqual(\"1\", self.rep('(get {\"+\" 1} \"+\")'))\n\n    def test_keyword(self):\n        self.assertEqual(\":keyword\", self.rep(\":keyword\"))\n\n    def test_deref_reader_macro(self):\n        self.assertEqual(\"1\", self.rep(\"@(atom 1)\"))\n\n    def test_splice_unquote_reader_macro(self):\n        self.assertEqual(\"(splice-unquote (1 2 3))\", str(stepA_mal.READ(\"~@(1 2 3)\")))\n\n    def test_swap_assoc_get(self):\n        self.assertEqual(\n            '(atom {\"+\" #<function>})', self.rep('(def! e (atom {\"+\" +}))')\n        )\n        self.assertEqual(\n            '{\"+\" #<function> \"-\" #<function>}', self.rep('(swap! e assoc \"-\" -)')\n        )\n        self.assertEqual(\"15\", self.rep('( (get @e \"+\") 7 8)'))\n        self.assertEqual(\"3\", self.rep('( (get @e \"-\") 11 8)'))\n        self.assertEqual(\n            '{\"+\" #<function> \"-\" #<function> \"foo\" ()}',\n            self.rep('(swap! e assoc \"foo\" (list))'),\n        )\n        self.assertEqual(\"()\", self.rep('(get @e \"foo\")'))\n        self.assertEqual(\n            '{\"+\" #<function> \"-\" #<function> \"foo\" () \"bar\" (1 2 3)}',\n            self.rep('(swap! e assoc \"bar\" \\'(1 2 3))'),\n        )\n        self.assertEqual(\"(1 2 3)\", self.rep('(get @e \"bar\")'))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "impls/r/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install curl gcc libc-dev libreadline-dev r-base-core\n"
  },
  {
    "path": "impls/r/Makefile",
    "content": "SOURCES_BASE = readline.r types.r reader.r printer.r\nSOURCES_LISP = env.r core.r stepA_mal.r\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nSTEPS = step0_repl.r step1_read_print.r step2_eval.r step3_env.r \\\n\tstep4_if_fn_do.r step5_tco.r step6_file.r \\\n\tstep7_quote.r step8_macros.r step9_try.r stepA_mal.r\n\nall: libs\n\ndist: mal.r mal\n\nmal.r: $(SOURCES)\n\tcat $+ | grep -v \" source(\" > $@\n\nmal: mal.r\n\techo \"#!/usr/bin/env Rscript\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\n$(STEPS): libs\n\n.PHONY:\nlibs: lib/rdyncall\n\nlib/rdyncall:\n\tcurl -O http://cran.r-project.org/src/contrib/Archive/rdyncall/rdyncall_0.7.5.tar.gz\n\tmkdir -p lib\n\tR CMD INSTALL rdyncall_0.7.5.tar.gz -l lib/\n\trm rdyncall_0.7.5.tar.gz\n\nclean:\n\trm -f mal.r mal\n\n\n"
  },
  {
    "path": "impls/r/core.r",
    "content": "..core.. <- TRUE\n\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\n\n\n# String functions\n\npr_str <- function(...)\n    .pr_list(list(...), print_readably=TRUE, join=\" \")\n\nstr <- function(...)\n    .pr_list(list(...), print_readably=FALSE, join=\"\")\n\nprn <- function(...) {\n    cat(.pr_list(list(...), print_readably=TRUE, join=\" \"))\n    cat(\"\\n\")\n    nil\n}\n\nprintln <- function(...) {\n    cat(.pr_list(list(...), print_readably=FALSE, join=\" \"))\n    cat(\"\\n\")\n    nil\n}\n\ndo_readline <- function(prompt) {\n    l <- readline(prompt)\n    if (is.null(l)) nil else l\n}\n\n# Hash Map functions\ndo_get <- function(hm,k) {\n    if (class(hm) == \"nil\") return(nil)\n    v <- hm[[k]]\n    if (is.null(v)) nil else v\n}\ncontains_q <-function(hm,k) {\n    if (class(hm) == \"nil\") return(FALSE)\n    if (is.null(hm[[k]])) FALSE else TRUE\n}\n\n# Sequence functions\ncons <- function(a,b) {\n    new_lst <- append(list(a), b)\n    new.listl(new_lst)\n}\n\nnth <- function(a,b) {\n    if (b < length(a))\n        a[[b+1]]\n    else\n        throw(\"nth: index out of range\")\n}\n\ndo_concat <- function(...) {\n    new_lst <- list()\n    for(l in list(...)) {\n        new_lst <- append(new_lst, l)\n    }\n    new.listl(new_lst)\n}\n\ndo_apply <- function(f, ...) {\n    p <- list(...)\n    args <- list()\n    if (length(p) > 1) {\n        for(l in slice(p, 1, length(p)-1)) {\n            args[[length(args)+1]] <- l\n        }\n    }\n    args <- append(args, p[[length(p)]])\n    fapply(f, args)\n}\n\nmap <- function(f, seq) {\n    new.listl(lapply(seq, function(el) fapply(f, list(el))))\n}\n\nconj <- function(obj, ...) {\n    p <- list(...)\n    new_obj <- .clone(obj)\n    if (.list_q(obj)) {\n        if (length(p) > 0) {\n            for(l in p) new_obj <- append(list(l), new_obj)\n        }\n        new.listl(new_obj)\n    } else if (.vector_q(obj)) {\n        if (length(p) > 0) {\n            for(l in p) new_obj <- append(new_obj, list(l))\n        }\n        new.vectorl(new_obj)\n    } else {\n        throw(\"conj called on non-sequence\")\n    }\n}\n\ndo_seq <- function(obj) {\n    if (.list_q(obj)) {\n        if (length(obj) == 0) nil else obj\n    } else if (.vector_q(obj)) {\n        if (length(obj) == 0) nil else new.listl(.clone(obj))\n    } else if (.string_q(obj)) {\n        if (nchar(obj) == 0) nil else new.listl(strsplit(obj, \"\")[[1]])\n    } else if (class(obj) == \"nil\") {\n        nil\n    } else {\n        throw(\"seq: called on non-sequence\")\n    }\n}\n\n\n# Metadata functions\nwith_meta <- function(obj, m) {\n    new_obj <- .clone(obj)\n    attr(new_obj, \"meta\") <- m\n    new_obj\n}\n\nmeta <- function(obj) {\n    m <- attr(obj, \"meta\")\n    if (is.null(m)) nil else m\n}\n\n# Atom functions\nderef <- function(atm) atm$val\nreset_bang <- function (atm, val) { atm$val <- val; val }\nswap_bang <- function (atm, f, ...) {\n    p <- list(...)\n    args <- list(atm$val)\n    if (length(p) > 0) {\n        for(l in p) args[[length(args)+1]] <- l\n    }\n    atm$val <- fapply(f, args)\n}\n\ncore_ns <- list(\n    \"=\"=function(a,b) .equal_q(a,b),\n    \"throw\"=function(err) throw(err),\n    \"nil?\"=.nil_q,\n    \"true?\"=.true_q,\n    \"false?\"=.false_q,\n    \"string?\"=.string_q,\n    \"symbol\"=new.symbol,\n    \"symbol?\"=.symbol_q,\n    \"keyword\"=new.keyword,\n    \"keyword?\"=.keyword_q,\n    \"number?\"=.number_q,\n    \"fn?\"=.fn_q,\n    \"macro?\"=.macro_q,\n\n    \"pr-str\"=pr_str,\n    \"str\"=str,\n    \"prn\"=prn,\n    \"println\"=println,\n    \"readline\"=do_readline,\n    \"read-string\"=function(str) read_str(str),\n    \"slurp\"=function(path) readChar(path, file.info(path)$size),\n    \"<\"=function(a,b) a<b,\n    \"<=\"=function(a,b) a<=b,\n    \">\"=function(a,b) a>b,\n    \">=\"=function(a,b) a>=b,\n    \"+\"=function(a,b) a+b,\n    \"-\"=function(a,b) a-b,\n    \"*\"=function(a,b) a*b,\n    \"/\"=function(a,b) a/b,\n    \"time-ms\"=function() round(as.numeric(Sys.time())*1000),\n\n    \"list\"=new.list,\n    \"list?\"=function(a) .list_q(a),\n    \"vector\"=new.vector,\n    \"vector?\"=function(a) .vector_q(a),\n    \"hash-map\"=new.hash_map,\n    \"map?\"=function(a) .hash_map_q(a),\n    \"assoc\"=function(hm,...) .assoc(hm,list(...)),\n    \"dissoc\"=function(hm,...) .dissoc(hm,list(...)),\n    \"get\"=do_get,\n    \"contains?\"=contains_q,\n    \"keys\"=function(hm) new.listl(ls(hm)),\n    \"vals\"=function(hm) new.listl(lapply(ls(hm), function(x) hm[[x]])),\n\n    \"sequential?\"=.sequential_q,\n    \"cons\"=cons,\n    \"concat\"=do_concat,\n    \"vec\"=new.vectorl,\n    \"nth\"=nth,\n    \"first\"=function(a) if (.nil_q(a) || length(a) < 1) nil else a[[1]],\n    \"rest\"=function(a) if (.nil_q(a)) new.list() else new.listl(slice(a,2)),\n    \"empty?\"=function(a) .sequential_q(a) && length(a) == 0,\n    \"count\"=function(a) if (.nil_q(a)) 0 else length(a),\n    \"apply\"=do_apply,\n    \"map\"=map,\n\n    \"conj\"=conj,\n    \"seq\"=do_seq,\n\n    \"with-meta\"=with_meta,\n    \"meta\"=meta,\n    \"atom\"=new.atom,\n    \"atom?\"=.atom_q,\n    \"deref\"=deref,\n    \"reset!\"=reset_bang,\n    \"swap!\"=swap_bang\n)\n"
  },
  {
    "path": "impls/r/env.r",
    "content": "..env.. <- TRUE\n\nif(!exists(\"..types..\")) source(\"types.r\")\n\nnew.Env <- function(outer=emptyenv(), binds=list(), exprs=list()) {\n    e <- structure(new.env(parent=outer), class=\"Env\")\n\n    if (length(binds) > 0) {\n        for(i in seq(length(binds))) {\n            b <- binds[[i]]\n            if (b == \"&\") {\n                e[[binds[[i+1]]]] <-\n                    slice(exprs, i, length(exprs))\n                break\n            } else {\n                e[[b]] <- exprs[[i]]\n            }\n        }\n    }\n    e\n}\n\nEnv.find <- function(e, key) {\n    if (exists(key, envir=e, inherits=FALSE)) {\n        e\n    } else if (!identical(parent.env(e), emptyenv())) {\n        Env.find(parent.env(e), key)\n    } else {\n        nil\n    }\n}\n\nEnv.set <- function(e, key, val) {\n    e[[key]] <- val\n    invisible(val)\n}\n\nEnv.get <- function(e, key) {\n    e <- Env.find(e, key)\n    if (.nil_q(e)) throw(concat(\"'\", key, \"' not found\"))\n    e[[key]]\n}\n"
  },
  {
    "path": "impls/r/printer.r",
    "content": "..printer.. <- TRUE\n\nif(!exists(\"..types..\")) source(\"types.r\")\n\n.pr_list <- function(lst, print_readably=TRUE, join=\"\") {\n    concatl(lapply(lst,\n                   function(e) .pr_str(e, print_readably)), sep=join)\n}\n\n.pr_str <- function(exp, print_readably=TRUE) {\n    pr <- print_readably\n    switch(class(exp),\n        \"List\"={\n            paste(\"(\", .pr_list(exp, pr, \" \"), \")\", sep=\"\", collapse=\"\")\n        },\n        \"Vector\"={\n            paste(\"[\", .pr_list(exp, pr, \" \"), \"]\", sep=\"\", collapse=\"\")\n        },\n        \"HashMap\"={\n            hlst <- list()\n            if (length(exp) > 0) {\n                for(k in ls(exp)) {\n                    hlst[[length(hlst)+1]] <- k\n                    hlst[[length(hlst)+1]] <- exp[[k]]\n                }\n            }\n            paste(\"{\", .pr_list(hlst, pr, \" \"), \"}\", sep=\"\", collapse=\"\")\n        },\n        \"character\"={\n            if (substring(exp,1,1) == \"\\u029e\") {\n                concat(\":\", substring(exp,2))\n            } else if (substring(exp,1,8) == \"<U+029E>\") {\n                # terrible hack, appears in 3.1.1 on Utopic\n                concat(\":\", substring(exp,9))\n            } else if (print_readably) {\n                paste(\"\\\"\",\n                      gsub(\"\\\\n\", \"\\\\\\\\n\",\n                           gsub(\"\\\\\\\"\", \"\\\\\\\\\\\"\",\n                                gsub(\"\\\\\\\\\", \"\\\\\\\\\\\\\\\\\", exp))),\n                      \"\\\"\", sep=\"\", collapse=\"\")\n            } else {\n                exp\n            }\n        },\n        \"Symbol\"={ exp },\n        \"nil\"={ \"nil\" },\n        \"logical\"={ tolower(exp) },\n        \"MalFunc\"={\n            paste(\"(fn* \", .pr_str(exp$params,TRUE),\n                  \" \", .pr_str(exp$ast, TRUE), \")\", sep=\"\")\n        },\n        \"function\"={ \"<#function>\" },\n        \"Atom\"={\n            paste(\"(atom \", .pr_str(exp$val,TRUE), \")\", sep=\"\")\n        },\n        { toString(exp) })\n}\n"
  },
  {
    "path": "impls/r/reader.r",
    "content": "..reader.. <- TRUE\n\nif(!exists(\"..types..\")) source(\"types.r\")\n\nnew.Reader <- function(tokens) {\n    e <- structure(new.env(), class=\"Reader\")\n    e$tokens <- tokens\n    e$position <- 1\n    e\n}\n\nReader.peek <- function(rdr) {\n    if (rdr$position > length(rdr$tokens)) return(NULL)\n    rdr$tokens[[rdr$position]]\n}\n\nReader.next <- function(rdr) {\n    if (rdr$position > length(rdr$tokens)) return(NULL)\n    rdr$position <- rdr$position + 1\n    rdr$tokens[[rdr$position-1]]\n}\n\ntokenize <- function(str) {\n    re <- \"[\\\\s,]*(~@|[\\\\[\\\\]\\\\{\\\\}\\\\(\\\\)'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]\\\\{\\\\}\\\\('\\\"`,;\\\\)]*)\"\n    m <- lapply(regmatches(str, gregexpr(re, str, perl=TRUE)), \n                function(e) sub(\"^[\\\\s,]+\", \"\", e, perl=TRUE))\n    res <- list()\n    i <- 1\n    for(v in m[[1]]) {\n        if (v == \"\" || substr(v,1,1) == \";\") next\n        res[[i]] <- v\n        i <- i+1\n    }\n    res\n}\n\nre_match <- function(re, str) { length(grep(re, c(str))) > 0 }\n\nread_atom <- function(rdr) {\n    token <- Reader.next(rdr)\n    if (re_match(\"^-?[0-9]+$\", token)) {\n        as.integer(token)\n    } else if (re_match(\"^-?[0-9][0-9.]*$\", token)) {\n        as.double(token)\n    } else if (re_match(\"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\", token)) {\n        gsub(\"\\x7f\", \"\\\\\\\\\",\n            gsub(\"\\\\\\\\n\", \"\\n\",\n                 gsub(\"\\\\\\\\\\\"\", \"\\\"\",\n                      gsub(\"\\\\\\\\\\\\\\\\\", \"\\x7f\",\n                           substr(token, 2, nchar(token)-1)))))\n    } else if (substr(token,1,1) == \"\\\"\") {\n        throw(\"expected '\\\"', got EOF\")\n    } else if (substr(token,1,1) == \":\") {\n        new.keyword(substring(token,2))\n    } else if (token == \"nil\") {\n        nil\n    } else if (token == \"true\") {\n        TRUE\n    } else if (token == \"false\") {\n        FALSE\n    } else {\n        new.symbol(token)\n    }\n}\n\nread_seq <- function(rdr, start=\"(\", end=\")\") {\n    lst <- list()\n    token <- Reader.next(rdr)\n    if (token != start) {\n        throw(concat(\"expected '\", start, \"'\"))\n    }\n    repeat {\n        token <- Reader.peek(rdr)\n        if (is.null(token)) {\n            throw(concat(\"expected '\", end, \"', got EOF\"))\n        }\n        if (token == end) break\n        lst[[length(lst)+1]] <- read_form(rdr)\n    }\n    Reader.next(rdr)\n    new.listl(lst)\n}\n\nread_form <- function(rdr) {\n    token <- Reader.peek(rdr)\n    if (token == \"'\") {\n        . <- Reader.next(rdr);\n        new.list(new.symbol(\"quote\"), read_form(rdr))\n    } else if (token == \"`\") {\n        . <- Reader.next(rdr);\n        new.list(new.symbol(\"quasiquote\"), read_form(rdr))\n    } else if (token == \"~\") {\n        . <- Reader.next(rdr);\n        new.list(new.symbol(\"unquote\"), read_form(rdr))\n    } else if (token == \"~@\") {\n        . <- Reader.next(rdr);\n        new.list(new.symbol(\"splice-unquote\"), read_form(rdr))\n    } else if (token == \"^\") {\n        . <- Reader.next(rdr)\n        m <- read_form(rdr)\n        new.list(new.symbol(\"with-meta\"), read_form(rdr), m)\n    } else if (token == \"@\") {\n        . <- Reader.next(rdr);\n        new.list(new.symbol(\"deref\"), read_form(rdr))\n    } else if (token == \")\") {\n        throw(\"unexpected ')'\")\n    } else if (token == \"(\") {\n        new.listl(read_seq(rdr))\n    } else if (token == \"]\") {\n        throw(\"unexpected ']'\")\n    } else if (token == \"[\") {\n        new.vectorl(read_seq(rdr, \"[\", \"]\"))\n    } else if (token == \"}\") {\n        throw(\"unexpected '}'\")\n    } else if (token == \"{\") {\n        new.hash_mapl(read_seq(rdr, \"{\", \"}\"))\n    } else {\n        read_atom(rdr)\n    }\n}\n\nread_str <- function(str) {\n    tokens <- tokenize(str)\n    if (length(tokens) == 0) return(nil)\n    return(read_form(new.Reader(tokens)))\n}\n\n#cat(\"---\\n\")\n#print(tokenize(\"123\"))\n#cat(\"---\\n\")\n#print(tokenize(\"  (  123 456 abc   \\\"def\\\"  )  \"))\n\n#rdr <- new.reader(tokenize(\"  (  123 456 abc   \\\"def\\\"  )  \"))\n#Reader.peek(rdr)\n#Reader.next(rdr)\n#Reader.next(rdr)\n#Reader.next(rdr)\n#Reader.next(rdr)\n#Reader.next(rdr)\n#Reader.next(rdr)\n#Reader.next(rdr)\n"
  },
  {
    "path": "impls/r/readline.r",
    "content": "..readline.. <- TRUE\n\nHISTORY_FILE = paste(path.expand(\"~\"), \"/.mal-history\", sep=\"\")\n\nlibrary(rdyncall, lib.loc=\"lib/\")\n\n#.rllib <- dynfind(c(\"edit\"))\n.rllib <- dynfind(c(\"readline\"))\n.call_readline <- .dynsym(.rllib,\"readline\")\n.call_add_history <- .dynsym(.rllib,\"add_history\")\n\n.state <- new.env()\n.state$rl_history_loaded = FALSE\n\n.readline <- function(prompt) {\n    res <- .dyncall(.call_readline, \"Z)p\", prompt)\n    if (is.nullptr(res)) {\n        return(NULL)\n    } else {\n        return(ptr2str(res))\n    }\n}\n\nreadline <- function(prompt) {\n    if (!.state$rl_history_loaded) {\n        .state$rl_history_loaded <- TRUE\n\n        if (file.access(HISTORY_FILE, 4) == 0) {\n            lines <- scan(HISTORY_FILE, what=\"\", sep=\"\\n\", quiet=TRUE)\n            for(add_line in lines) {\n                .dyncall(.call_add_history, \"Z)v\", add_line)\n            }\n        }\n    }\n\n    line <- .readline(prompt)\n    if (is.null(line)) return(NULL)\n    .dyncall(.call_add_history, \"Z)v\", line)\n    if (file.access(HISTORY_FILE, 2) == 0) {\n        write(line, file=HISTORY_FILE, append=TRUE)\n    }\n\n    line\n}\n"
  },
  {
    "path": "impls/r/run",
    "content": "#!/usr/bin/env bash\nexec Rscript $(dirname $0)/${STEP:-stepA_mal}.r \"${@}\"\n"
  },
  {
    "path": "impls/r/step0_repl.r",
    "content": "source(\"readline.r\")\n\nREAD <- function(str) {\n    return(str)\n}\n\nEVAL <- function(ast, env) {\n    return(ast)\n}\n\nPRINT <- function(exp) {\n    return(exp)\n}\n\nrep <- function(str) {\n    return(PRINT(EVAL(READ(str), \"\")))\n}\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", err$message,\"\\n\", sep=\"\")\n    })\n}\n"
  },
  {
    "path": "impls/r/step1_read_print.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\n\nREAD <- function(str) {\n    return(read_str(str))\n}\n\nEVAL <- function(ast, env) {\n    return(ast)\n}\n\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\nrep <- function(str) {\n    return(PRINT(EVAL(READ(str), \"\")))\n}\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step2_eval.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\n\nREAD <- function(str) {\n    return(read_str(str))\n}\n\nEVAL <- function(ast, env) {\n\n    # cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    # apply list\n    if (length(ast) == 0) {\n        return(ast)\n    }\n    f <- EVAL(ast[[1]], env)\n    args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n    return(do.call(f, args))\n}\n\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\nrepl_env <- new.env()\nrepl_env[[\"+\"]] <- function(a,b) a+b\nrepl_env[[\"-\"]] <- function(a,b) a-b\nrepl_env[[\"*\"]] <- function(a,b) a*b\nrepl_env[[\"/\"]] <- function(a,b) a/b\n\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step3_env.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\n\nREAD <- function(str) {\n    return(read_str(str))\n}\n\nEVAL <- function(ast, env) {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(ast[[3]], env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        return(EVAL(a2, let_env))\n    } else {\n        f <- EVAL(a0, env)\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        return(do.call(f, args))\n    }\n}\n\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\nrepl_env <- new.Env()\nEnv.set(repl_env, \"+\", function(a,b) a+b)\nEnv.set(repl_env, \"-\", function(a,b) a-b)\nEnv.set(repl_env, \"*\", function(a,b) a*b)\nEnv.set(repl_env, \"/\", function(a,b) a/b)\n\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step4_if_fn_do.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\nREAD <- function(str) {\n    return(read_str(str))\n}\n\nEVAL <- function(ast, env) {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(ast[[3]], env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        return(EVAL(a2, let_env))\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        return(EVAL(ast[[length(ast)]], env))\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            return(EVAL(ast[[4]], env))\n        } else {\n            return(EVAL(a2, env))\n        }\n    } else if (a0sym == \"fn*\") {\n        return(function(...) {\n            EVAL(a2, new.Env(env, a1, list(...)))\n        })\n    } else {\n        f <- EVAL(a0, env)\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        return(do.call(f, args))\n    }\n}\n\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step5_tco.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\nREAD <- function(str) {\n    return(read_str(str))\n}\n\nEVAL <- function(ast, env) {\n\n    repeat {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(a2, env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        ast <- a2\n        env <- let_env\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        ast <- ast[[length(ast)]]\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            ast <- ast[[4]]\n        } else {\n            ast <- a2\n        }\n    } else if (a0sym == \"fn*\") {\n        return(malfunc(EVAL, a2, env, a1))\n    } else {\n        f <- EVAL(a0, env)\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        if (class(f) == \"MalFunc\") {\n            ast <- f$ast\n            env <- f$gen_env(args)\n        } else {\n            return(do.call(f, args))\n        }\n    }\n\n    }\n}\n\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step6_file.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\n# read\nREAD <- function(str) {\n    return(read_str(str))\n}\n\nEVAL <- function(ast, env) {\n\n    repeat {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(a2, env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        ast <- a2\n        env <- let_env\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        ast <- ast[[length(ast)]]\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            ast <- ast[[4]]\n        } else {\n            ast <- a2\n        }\n    } else if (a0sym == \"fn*\") {\n        return(malfunc(EVAL, a2, env, a1))\n    } else {\n        f <- EVAL(a0, env)\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        if (class(f) == \"MalFunc\") {\n            ast <- f$ast\n            env <- f$gen_env(args)\n        } else {\n            return(do.call(f, args))\n        }\n    }\n\n    }\n}\n\n# print\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\n# repl loop\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\nEnv.set(repl_env, \"eval\", function(ast) EVAL(ast, repl_env))\nEnv.set(repl_env, \"*ARGV*\", new.list())\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n. <- rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nargs <- commandArgs(trailingOnly = TRUE)\nif (length(args) > 0) {\n    Env.set(repl_env, \"*ARGV*\", new.listl(slice(as.list(args),2)))\n    . <- rep(concat(\"(load-file \\\"\", args[[1]], \"\\\")\"))\n    quit(save=\"no\", status=0)\n}\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step7_quote.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\n# read\nREAD <- function(str) {\n    return(read_str(str))\n}\n\n# eval\nstarts_with <- function(ast, sym) {\n    .list_q(ast) && length(ast) == 2 && .symbol_q(ast[[1]]) && ast[[1]] == sym\n}\n\nquasiquote_elements <- function(ast) {\n    acc <- new.list()\n    i <- length(ast)\n    while (0 < i) {\n        elt <- ast[[i]]\n        if (starts_with(elt, \"splice-unquote\")) {\n            acc = new.list(new.symbol(\"concat\"), elt[[2]], acc)\n        } else {\n            acc = new.list(new.symbol(\"cons\"), quasiquote(elt), acc)\n        }\n        i <- i-1\n    }\n    acc\n}\n\nquasiquote <- function(ast) {\n    if (.list_q(ast)) {\n        if (starts_with(ast, \"unquote\")) {\n            ast[[2]]\n        } else {\n            quasiquote_elements(ast)\n        }\n    } else if (.vector_q(ast)) {\n        new.list(new.symbol(\"vec\"), quasiquote_elements(ast))\n    } else if (.symbol_q(ast) || .hash_map_q(ast)) {\n        new.list(new.symbol(\"quote\"), ast)\n    } else {\n        ast\n    }\n}\n\nEVAL <- function(ast, env) {\n\n    repeat {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(a2, env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        ast <- a2\n        env <- let_env\n    } else if (a0sym == \"quote\") {\n        return(a1)\n    } else if (a0sym == \"quasiquote\") {\n        ast <- quasiquote(a1)\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        ast <- ast[[length(ast)]]\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            ast <- ast[[4]]\n        } else {\n            ast <- a2\n        }\n    } else if (a0sym == \"fn*\") {\n        return(malfunc(EVAL, a2, env, a1))\n    } else {\n        f <- EVAL(a0, env)\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        if (class(f) == \"MalFunc\") {\n            ast <- f$ast\n            env <- f$gen_env(args)\n        } else {\n            return(do.call(f, args))\n        }\n    }\n\n    }\n}\n\n# print\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\n# repl loop\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\nEnv.set(repl_env, \"eval\", function(ast) EVAL(ast, repl_env))\nEnv.set(repl_env, \"*ARGV*\", new.list())\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n. <- rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nargs <- commandArgs(trailingOnly = TRUE)\nif (length(args) > 0) {\n    Env.set(repl_env, \"*ARGV*\", new.listl(slice(as.list(args),2)))\n    . <- rep(concat(\"(load-file \\\"\", args[[1]], \"\\\")\"))\n    quit(save=\"no\", status=0)\n}\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step8_macros.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\n# read\nREAD <- function(str) {\n    return(read_str(str))\n}\n\n# eval\nstarts_with <- function(ast, sym) {\n    .list_q(ast) && length(ast) == 2 && .symbol_q(ast[[1]]) && ast[[1]] == sym\n}\n\nquasiquote_elements <- function(ast) {\n    acc <- new.list()\n    i <- length(ast)\n    while (0 < i) {\n        elt <- ast[[i]]\n        if (starts_with(elt, \"splice-unquote\")) {\n            acc = new.list(new.symbol(\"concat\"), elt[[2]], acc)\n        } else {\n            acc = new.list(new.symbol(\"cons\"), quasiquote(elt), acc)\n        }\n        i <- i-1\n    }\n    acc\n}\n\nquasiquote <- function(ast) {\n    if (.list_q(ast)) {\n        if (starts_with(ast, \"unquote\")) {\n            ast[[2]]\n        } else {\n            quasiquote_elements(ast)\n        }\n    } else if (.vector_q(ast)) {\n        new.list(new.symbol(\"vec\"), quasiquote_elements(ast))\n    } else if (.symbol_q(ast) || .hash_map_q(ast)) {\n        new.list(new.symbol(\"quote\"), ast)\n    } else {\n        ast\n    }\n}\n\nEVAL <- function(ast, env) {\n\n    repeat {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(a2, env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        ast <- a2\n        env <- let_env\n    } else if (a0sym == \"quote\") {\n        return(a1)\n    } else if (a0sym == \"quasiquote\") {\n        ast <- quasiquote(a1)\n    } else if (a0sym == \"defmacro!\") {\n        func <- EVAL(a2, env)\n        func$ismacro = TRUE\n        return(Env.set(env, a1, func))\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        ast <- ast[[length(ast)]]\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            ast <- ast[[4]]\n        } else {\n            ast <- a2\n        }\n    } else if (a0sym == \"fn*\") {\n        return(malfunc(EVAL, a2, env, a1))\n    } else {\n        f <- EVAL(a0, env)\n        if (.macro_q(f)) {\n            ast <- fapply(f, slice(ast, 2))\n            next\n        }\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        if (class(f) == \"MalFunc\") {\n            ast <- f$ast\n            env <- f$gen_env(args)\n        } else {\n            return(do.call(f, args))\n        }\n    }\n\n    }\n}\n\n# print\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\n# repl loop\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\nEnv.set(repl_env, \"eval\", function(ast) EVAL(ast, repl_env))\nEnv.set(repl_env, \"*ARGV*\", new.list())\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n. <- rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n. <- rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nargs <- commandArgs(trailingOnly = TRUE)\nif (length(args) > 0) {\n    Env.set(repl_env, \"*ARGV*\", new.listl(slice(as.list(args),2)))\n    . <- rep(concat(\"(load-file \\\"\", args[[1]], \"\\\")\"))\n    quit(save=\"no\", status=0)\n}\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/step9_try.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\n# read\nREAD <- function(str) {\n    return(read_str(str))\n}\n\n# eval\nstarts_with <- function(ast, sym) {\n    .list_q(ast) && length(ast) == 2 && .symbol_q(ast[[1]]) && ast[[1]] == sym\n}\n\nquasiquote_elements <- function(ast) {\n    acc <- new.list()\n    i <- length(ast)\n    while (0 < i) {\n        elt <- ast[[i]]\n        if (starts_with(elt, \"splice-unquote\")) {\n            acc = new.list(new.symbol(\"concat\"), elt[[2]], acc)\n        } else {\n            acc = new.list(new.symbol(\"cons\"), quasiquote(elt), acc)\n        }\n        i <- i-1\n    }\n    acc\n}\n\nquasiquote <- function(ast) {\n    if (.list_q(ast)) {\n        if (starts_with(ast, \"unquote\")) {\n            ast[[2]]\n        } else {\n            quasiquote_elements(ast)\n        }\n    } else if (.vector_q(ast)) {\n        new.list(new.symbol(\"vec\"), quasiquote_elements(ast))\n    } else if (.symbol_q(ast) || .hash_map_q(ast)) {\n        new.list(new.symbol(\"quote\"), ast)\n    } else {\n        ast\n    }\n}\n\nEVAL <- function(ast, env) {\n\n    repeat {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(a2, env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        ast <- a2\n        env <- let_env\n    } else if (a0sym == \"quote\") {\n        return(a1)\n    } else if (a0sym == \"quasiquote\") {\n        ast <- quasiquote(a1)\n    } else if (a0sym == \"defmacro!\") {\n        func <- EVAL(a2, env)\n        func$ismacro = TRUE\n        return(Env.set(env, a1, func))\n    } else if (a0sym == \"try*\") {\n        edata <- new.env()\n        tryCatch({\n            return(EVAL(a1, env))\n        }, error=function(err) {\n            edata$exc <- get_error(err)\n        })\n        if ((!is.null(a2)) && a2[[1]] == \"catch*\") {\n            return(EVAL(a2[[3]], new.Env(env,\n                                         new.list(a2[[2]]),\n                                         new.list(edata$exc))))\n        } else {\n            throw(edata$exc)\n        }\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        ast <- ast[[length(ast)]]\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            ast <- ast[[4]]\n        } else {\n            ast <- a2\n        }\n    } else if (a0sym == \"fn*\") {\n        return(malfunc(EVAL, a2, env, a1))\n    } else {\n        f <- EVAL(a0, env)\n        if (.macro_q(f)) {\n            ast <- fapply(f, slice(ast, 2))\n            next\n        }\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        if (class(f) == \"MalFunc\") {\n            ast <- f$ast\n            env <- f$gen_env(args)\n        } else {\n            return(do.call(f, args))\n        }\n    }\n\n    }\n}\n\n# print\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\n# repl loop\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\nEnv.set(repl_env, \"eval\", function(ast) EVAL(ast, repl_env))\nEnv.set(repl_env, \"*ARGV*\", new.list())\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n. <- rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n. <- rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nargs <- commandArgs(trailingOnly = TRUE)\nif (length(args) > 0) {\n    Env.set(repl_env, \"*ARGV*\", new.listl(slice(as.list(args),2)))\n    tryCatch({\n        . <- rep(concat(\"(load-file \\\"\", args[[1]], \"\\\")\"))\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    quit(save=\"no\", status=0)\n}\n\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", .pr_str(get_error(err),TRUE),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/stepA_mal.r",
    "content": "if(!exists(\"..readline..\")) source(\"readline.r\")\nif(!exists(\"..types..\")) source(\"types.r\")\nif(!exists(\"..reader..\")) source(\"reader.r\")\nif(!exists(\"..printer..\")) source(\"printer.r\")\nif(!exists(\"..env..\")) source(\"env.r\")\nif(!exists(\"..core..\")) source(\"core.r\")\n\n# read\nREAD <- function(str) {\n    return(read_str(str))\n}\n\n# eval\nstarts_with <- function(ast, sym) {\n    .list_q(ast) && length(ast) == 2 && .symbol_q(ast[[1]]) && ast[[1]] == sym\n}\n\nquasiquote_elements <- function(ast) {\n    acc <- new.list()\n    i <- length(ast)\n    while (0 < i) {\n        elt <- ast[[i]]\n        if (starts_with(elt, \"splice-unquote\")) {\n            acc = new.list(new.symbol(\"concat\"), elt[[2]], acc)\n        } else {\n            acc = new.list(new.symbol(\"cons\"), quasiquote(elt), acc)\n        }\n        i <- i-1\n    }\n    acc\n}\n\nquasiquote <- function(ast) {\n    if (.list_q(ast)) {\n        if (starts_with(ast, \"unquote\")) {\n            ast[[2]]\n        } else {\n            quasiquote_elements(ast)\n        }\n    } else if (.vector_q(ast)) {\n        new.list(new.symbol(\"vec\"), quasiquote_elements(ast))\n    } else if (.symbol_q(ast) || .hash_map_q(ast)) {\n        new.list(new.symbol(\"quote\"), ast)\n    } else {\n        ast\n    }\n}\n\nEVAL <- function(ast, env) {\n\n    repeat {\n\n    dbgevalenv <- Env.find(env, \"DEBUG-EVAL\")\n    if (!.nil_q(dbgevalenv)) {\n        dbgeval <- Env.get(dbgevalenv, \"DEBUG-EVAL\")\n        if (!.nil_q(dbgeval) && !identical(dbgeval, FALSE))\n            cat(\"EVAL: \", .pr_str(ast,TRUE), \"\\n\", sep=\"\")\n    }\n\n    if (.symbol_q(ast)) {\n        return(Env.get(env, ast))\n    } else if (.list_q(ast)) {\n        # exit this switch\n    } else if (.vector_q(ast)) {\n        return(new.vectorl(lapply(ast, function(a) EVAL(a, env))))\n    } else if (.hash_map_q(ast)) {\n        lst <- list()\n        for(k in ls(ast)) {\n            lst[[length(lst)+1]] = k\n            lst[[length(lst)+1]] = EVAL(ast[[k]], env)\n        }\n        return(new.hash_mapl(lst))\n    } else {\n        return(ast)\n    }\n\n    if (length(ast) == 0) { return(ast) }\n\n    # apply list\n    switch(paste(\"l\",length(ast),sep=\"\"),\n           l0={ return(ast) },\n           l1={ a0 <- ast[[1]]; a1 <- NULL;     a2 <- NULL },\n           l2={ a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- NULL },\n              { a0 <- ast[[1]]; a1 <- ast[[2]]; a2 <- ast[[3]] })\n    if (length(a0) > 1) a0sym <- \"__<*fn*>__\"\n    else                a0sym <- as.character(a0)\n    if (a0sym == \"def!\") {\n        res <- EVAL(a2, env)\n        return(Env.set(env, a1, res))\n    } else if (a0sym == \"let*\") {\n        let_env <- new.Env(env)\n        for(i in seq(1,length(a1),2)) {\n            Env.set(let_env, a1[[i]], EVAL(a1[[i+1]], let_env))\n        }\n        ast <- a2\n        env <- let_env\n    } else if (a0sym == \"quote\") {\n        return(a1)\n    } else if (a0sym == \"quasiquote\") {\n        ast <- quasiquote(a1)\n    } else if (a0sym == \"defmacro!\") {\n        func <- EVAL(a2, env)\n        func$ismacro = TRUE\n        return(Env.set(env, a1, func))\n    } else if (a0sym == \"try*\") {\n        edata <- new.env()\n        tryCatch({\n            return(EVAL(a1, env))\n        }, error=function(err) {\n            edata$exc <- get_error(err)\n        })\n        if ((!is.null(a2)) && a2[[1]] == \"catch*\") {\n            return(EVAL(a2[[3]], new.Env(env,\n                                         new.list(a2[[2]]),\n                                         new.list(edata$exc))))\n        } else {\n            throw(edata$exc)\n        }\n    } else if (a0sym == \"do\") {\n        if (2 < length(ast))\n            for(i in seq(2, length(ast) - 1))\n                EVAL(ast[[i]], env)\n        ast <- ast[[length(ast)]]\n    } else if (a0sym == \"if\") {\n        cond <- EVAL(a1, env)\n        if (.nil_q(cond) || identical(cond, FALSE)) {\n            if (length(ast) < 4) return(nil)\n            ast <- ast[[4]]\n        } else {\n            ast <- a2\n        }\n    } else if (a0sym == \"fn*\") {\n        return(malfunc(EVAL, a2, env, a1))\n    } else {\n        f <- EVAL(a0, env)\n        if (.macro_q(f)) {\n            ast <- fapply(f, slice(ast, 2))\n            next\n        }\n        args <- new.listl(lapply(slice(ast, 2), function(a) EVAL(a, env)))\n        if (class(f) == \"MalFunc\") {\n            ast <- f$ast\n            env <- f$gen_env(args)\n        } else {\n            return(do.call(f, args))\n        }\n    }\n\n    }\n}\n\n# print\nPRINT <- function(exp) {\n    return(.pr_str(exp, TRUE))\n}\n\n# repl loop\nrepl_env <- new.Env()\nrep <- function(str) return(PRINT(EVAL(READ(str), repl_env)))\n\n# core.r: defined using R\nfor(k in names(core_ns)) { Env.set(repl_env, k, core_ns[[k]]) }\nEnv.set(repl_env, \"eval\", function(ast) EVAL(ast, repl_env))\nEnv.set(repl_env, \"*ARGV*\", new.list())\n\n# core.mal: defined using the language itself\n. <- rep(\"(def! *host-language* \\\"R\\\")\")\n. <- rep(\"(def! not (fn* (a) (if a false true)))\")\n. <- rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n. <- rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nargs <- commandArgs(trailingOnly = TRUE)\nif (length(args) > 0) {\n    Env.set(repl_env, \"*ARGV*\", new.listl(slice(as.list(args),2)))\n    tryCatch({\n        . <- rep(concat(\"(load-file \\\"\", args[[1]], \"\\\")\"))\n    }, error=function(err) {\n        cat(\"Error: \", get_error(err),\"\\n\", sep=\"\")\n    })\n    quit(save=\"no\", status=0)\n}\n\n. <- rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nrepeat {\n    line <- readline(\"user> \")\n    if (is.null(line)) { cat(\"\\n\"); break }\n    tryCatch({\n        cat(rep(line),\"\\n\", sep=\"\")\n    }, error=function(err) {\n        cat(\"Error: \", .pr_str(get_error(err),TRUE),\"\\n\", sep=\"\")\n    })\n    # R debug/fatal with tracebacks:\n    #cat(rep(line),\"\\n\", sep=\"\")\n}\n"
  },
  {
    "path": "impls/r/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/r/types.r",
    "content": "..types.. <- TRUE\n\nif(!exists(\"..env..\")) source(\"env.r\")\n\n# General type related functions\nconcat <- function(..., sep=\"\")  paste(..., collapse=\"\", sep=sep)\nconcatl <- function(lst, sep=\"\") paste(lst, collapse=sep, sep=sep)\n\nslice <- function(seq, start=1, end=-1) {\n    if (end == -1) end <- length(seq)\n    if (start > end) lst <- list() else lst <- seq[start:end]\n    switch(class(seq),\n        list={ new.listl(lst) },\n        List={ new.listl(lst) },\n        Vector={ new.vectorl(lst) },\n        { throw(\"slice called on non-sequence\") })\n}\n\n.sequential_q <- function(obj) .list_q(obj) || .vector_q(obj)\n\n.equal_q <- function(a,b) {\n    ota <- class(a); otb <- class(b)\n    if (!((ota == otb) || (.sequential_q(a) && .sequential_q(b)))) {\n        return(FALSE)\n    }\n    switch(ota,\n    \"List\"={\n        if (length(a) != length(b)) return(FALSE)\n        if (length(a) == 0) return(TRUE)\n        for(i in seq(length(a))) {\n            if (!.equal_q(a[[i]],b[[i]])) return(FALSE)\n        }\n        TRUE\n    },\n    \"Vector\"={\n        if (length(a) != length(b)) return(FALSE)\n        if (length(a) == 0) return(TRUE)\n        for(i in seq(length(a))) {\n            if (!.equal_q(a[[i]],b[[i]])) return(FALSE)\n        }\n        TRUE\n    },\n    \"HashMap\"={\n        ks1 <- ls(a)\n        ks2 <- ls(b)\n        if (length(ks1) != length(ks2)) return(FALSE)\n        for(k in ks1) {\n            if (!.equal_q(a[[k]],b[[k]])) return(FALSE)\n        }\n        TRUE\n    },\n    {\n        a == b\n    })\n}\n\n.clone <- function(obj) {\n    if (.hash_map_q(obj)) {\n        new_obj <- new.env()\n        for(k in ls(obj, all.names=TRUE)) new_obj[[k]] = obj[[k]]\n        class(new_obj) <- \"HashMap\"\n    } else {\n        new_obj <- obj\n    }\n    new_obj\n}\n\n# Errors/exceptions\nthrown_error = new.env()\nthrown_error$val = NULL\nthrow <- function(obj) {\n    thrown_error$val = obj\n    stop(\"<mal_exception>\")\n}\nget_error <- function(e) {\n    estr <- e$message\n    if (estr == \"<mal_exception>\") {\n        err <- thrown_error$val\n        thrown_error$val <- NULL\n        err\n    } else {\n        estr\n    }\n}\n\n# Scalars\nnil <- structure(\"malnil\", class=\"nil\")\n.nil_q <- function(obj) \"nil\" == class(obj)\n.true_q <- function(obj) \"logical\" == class(obj) && obj == TRUE\n.false_q <- function(obj) \"logical\" == class(obj) && obj == FALSE\n.string_q <- function(obj) {\n    \"character\" == class(obj) &&\n        !(\"\\u029e\" == substr(obj,1,1) ||\n          \"<U+029E>\" == substring(obj,1,8))\n}\n\nnew.symbol <- function(name) structure(name, class=\"Symbol\")\n.symbol_q <- function(obj) \"Symbol\" == class(obj)\n\nnew.keyword <- function(name) {\n    if (.keyword_q(name)) return (name)\n    concat(\"\\u029e\", name)\n}\n\n.keyword_q <- function(obj) {\n    \"character\" == class(obj) &&\n        (\"\\u029e\" == substr(obj,1,1) ||\n         \"<U+029E>\" == substring(obj,1,8))\n}\n\n.number_q <- function(obj) \"numeric\" == class(obj) || \"integer\" == class(obj)\n\n# Functions\n\nmalfunc <- function(eval, ast, env, params) {\n    gen_env <- function(args) new.Env(env, params, args)\n    structure(list(eval=eval,\n                   ast=ast,\n                   env=env,\n                   params=params,\n                   gen_env=gen_env,\n                   ismacro=FALSE), class=\"MalFunc\")\n}\n.malfunc_q <- function(obj) \"MalFunc\" == class(obj)\n\nfapply <- function(mf, args) {\n    if (class(mf) == \"MalFunc\") {\n        ast <- mf$ast\n        env <- mf$gen_env(args)\n        mf$eval(ast, env)\n    } else {\n        #print(args)\n        do.call(mf,args)\n    }\n}\n\n.fn_q <- function(obj) \"function\" == class(obj) || (.malfunc_q(obj) && !obj$ismacro)\n.macro_q <- function(obj) .malfunc_q(obj) && obj$ismacro\n\n# Lists\nnew.list <- function(...) new.listl(list(...))\nnew.listl <- function(lst) { class(lst) <- \"List\"; lst }\n.list_q <- function(obj) \"List\" == class(obj)\n\n# Vectors\nnew.vector <- function(...) new.vectorl(list(...))\nnew.vectorl <- function(lst) { class(lst) <- \"Vector\"; lst }\n.vector_q <- function(obj) \"Vector\" == class(obj)\n\n# Hash Maps\nnew.hash_map <- function(...) new.hash_mapl(list(...))\nnew.hash_mapl <- function(lst) {\n    .assoc(new.env(), lst)\n}\n.assoc <- function(src_hm, lst) {\n    hm <- .clone(src_hm)\n    if (length(lst) > 0) {\n        for(i in seq(1,length(lst),2)) {\n            hm[[lst[[i]]]] <- lst[[i+1]]\n        }\n    }\n    class(hm) <- \"HashMap\"\n    hm\n}\n.dissoc <- function(src_hm, lst) {\n    hm <- .clone(src_hm)\n    if (length(lst) > 0) {\n        for(k in lst) {\n            remove(list=c(k), envir=hm)\n        }\n    }\n    ls(hm)\n    class(hm) <- \"HashMap\"\n    hm\n}\n.hash_map_q <- function(obj) \"HashMap\" == class(obj)\n\n# Atoms\nnew.atom <- function(val) {\n    atm <- new.env()\n    class(atm) <- \"Atom\"\n    atm$val <- .clone(val)\n    atm\n}\n.atom_q <- function(obj) \"Atom\" == class(obj)\n"
  },
  {
    "path": "impls/racket/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Racket\nRUN apt-get -y install libedit-dev racket\n"
  },
  {
    "path": "impls/racket/Makefile",
    "content": "SOURCES_BASE = types.rkt reader.rkt printer.rkt\nSOURCES_LISP = env.rkt core.rkt stepA_mal.rkt\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ndist: mal\n\nmal: $(SOURCES)\n\traco exe stepA_mal.rkt\n\tmv stepA_mal $@\n\nclean:\n\trm -f mal\n"
  },
  {
    "path": "impls/racket/core.rkt",
    "content": "#lang racket\n\n(provide core_ns)\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\")\n\n(define (throw exc)\n  (raise (make-mal-exn \"mal exception\"\n                       (current-continuation-marks)\n                       exc)))\n\n;; Sequence functions\n(define do_apply\n  (lambda a\n    (let* ([f (first a)]\n           [lst (_to_list (last a))]\n           [args (append (take (drop a 1) (- (length a) 2)) lst)])\n      (apply f args))))\n\n(define conj\n  (lambda a\n    (if (vector? (first a))\n      (vector-append (first a) (list->vector (rest a)))\n      (append (reverse (rest a)) (first a)))))\n\n(define (seq obj)\n  (cond [(_nil? obj) nil]\n        [(_string? obj) (if (eq? 0 (string-length obj)) nil (map string (string->list obj)))]\n        [(_empty? obj) nil]\n        [else (_to_list obj)]))\n\n;; Meta functions\n(define (meta obj)\n  (cond [(malfunc? obj) (malfunc-meta obj)]\n        [else nil]))\n\n(define (with-meta obj m)\n  (cond [(malfunc? obj) (struct-copy malfunc obj [meta m])]\n        [else (raise \"metadata not supported on type\")]))\n\n;; Atom functions\n\n(define swap!\n  (lambda a\n    (let* ([atm (first a)]\n           [f (second a)]\n           [args (cons (atom-val atm) (rest (rest a)))]\n           [val (apply f args)])\n      (set-atom-val! atm val)\n      val)))\n\n(define core_ns\n  (hash\n    '=        _equal?\n    'throw    throw\n\n    'nil?     _nil?\n    'true?    (lambda (x) (eq? x #t))\n    'false?   (lambda (x) (eq? x #f))\n    'number?  number?\n    'symbol   (lambda (s) (if (symbol? s) s (string->symbol s)))\n    'symbol?  symbol?\n    'string?  _string?\n    'keyword  (lambda (s) (if (_keyword? s) s (_keyword s)))\n    'keyword? _keyword?\n    'fn?      (lambda (s) (if (malfunc? s)\n                              (not (malfunc-macro? s))\n                              (procedure? s)))\n    'macro?   (lambda (s) (and (malfunc? s) (malfunc-macro? s)))\n\n    'pr-str   (lambda a (pr_lst a #t \" \"))\n    'str      (lambda a (pr_lst a #f \"\"))\n    'prn      (lambda a (printf \"~a~n\" (pr_lst a #t \" \")) nil)\n    'println  (lambda a (printf \"~a~n\" (pr_lst a #f \" \")) nil)\n    'read-string (lambda (s) (read_str s))\n    'readline readline\n    'slurp    (lambda (f) (port->string (open-input-file f)))\n\n    '<        <\n    '<=       <=\n    '>        >\n    '>=       >=\n    '+        +\n    '-        -\n    '*        *\n    '/        /\n    'time-ms  (lambda () (round (current-inexact-milliseconds)))\n\n    'list     list\n    'list?    list?\n    'vector   vector\n    'vector?  vector?\n    'hash-map hash\n    'map?     hash?\n    'assoc    _assoc\n    'dissoc   _dissoc\n    'get      _get\n    'contains? dict-has-key?\n    'keys     hash-keys\n    'vals     hash-values\n\n    'sequential? _sequential?\n    'cons     (lambda a (cons (first a) (_to_list (second a))))\n    'concat   (lambda a (apply append (map _to_list a)))\n    'vec      (lambda a (let* ([x (first a)]) (if (vector? x) x (list->vector x))))\n    'nth      _nth\n    'first    _first\n    'rest     _rest\n    'empty?   _empty?\n    'count    _count\n    'apply    do_apply\n    'map      (lambda (f s) (_to_list (_map f s)))\n    'conj     conj\n    'seq      seq\n\n    'meta     meta\n    'with-meta with-meta\n    'atom     atom\n    'atom?    atom?\n    'deref    (lambda (a) (atom-val a))\n    'reset!   (lambda (a v) (set-atom-val! a v) v)\n    'swap!    swap!))\n"
  },
  {
    "path": "impls/racket/env.rkt",
    "content": "#lang racket\n\n(provide Env%)\n\n(require \"types.rkt\")\n\n(define Env%\n  (class object%\n    (init outer binds exprs)\n    (super-new)\n    (define _outer outer)\n    (define _binds (_to_list binds))\n    (define _exprs (_to_list exprs))\n    (define data (make-hash))\n    (let ([vargs (member '& _binds)])\n      (if vargs\n        (begin\n          (map (lambda (b e) (hash-set! data b e))\n               (drop-right _binds 2)\n               (take _exprs (- (length _binds) 2)))\n          (hash-set! data\n                     (last _binds)\n                     (drop _exprs (- (length _binds) 2))))\n        (map (lambda (b e) (hash-set! data b e))\n             _binds\n             _exprs)))\n\n    (define/public (set k v)\n      (hash-set! data k v)\n      v)\n    (define/public (get k)\n      (hash-ref data k\n        (lambda () (unless (null? _outer) (send _outer get k)))))))\n"
  },
  {
    "path": "impls/racket/printer.rkt",
    "content": "#lang racket\n\n(provide pr_str pr_lst)\n\n(require \"types.rkt\")\n\n(define (pr_str obj print_readably)\n  (let ([_r print_readably])\n    (cond\n      [(list? obj)\n       (string-join (map (lambda (o) (pr_str o _r)) obj)\n                    \" \" #:before-first \"(\" #:after-last \")\")]\n      [(vector? obj)\n       (string-join (map (lambda (o) (pr_str o _r)) (vector->list obj))\n                    \" \" #:before-first \"[\" #:after-last \"]\")]\n      [(hash? obj)\n       (string-join (dict-map obj (lambda (k v)\n                                    (format \"~a ~a\"\n                                            (pr_str k _r)\n                                            (pr_str v _r))))\n                    \" \" #:before-first \"{\" #:after-last \"}\")]\n      [(string? obj)\n       (if (regexp-match #px\"^\\u029e\" obj)\n         (format \":~a\" (substring obj 1))\n         (if _r\n           (format \"\\\"~a\\\"\"\n             (string-replace\n               (string-replace\n                 (string-replace obj \"\\\\\" \"\\\\\\\\\")\n                 \"\\\"\" \"\\\\\\\"\")\n                \"\\n\" \"\\\\n\"))\n           obj))]\n      [(number? obj) (number->string obj)]\n      [(symbol? obj) (symbol->string obj)]\n      [(atom? obj) (format \"(atom ~a)\" (atom-val obj))]\n      [(_nil? obj) \"nil\"]\n      [(eq? #t obj) \"true\"]\n      [(eq? #f obj) \"false\"]\n      [else (format \"~a\" obj)])))\n\n(define (pr_lst lst print_readably sep)\n  (string-join\n    (map (lambda (s) (pr_str s print_readably)) lst)\n    sep))\n"
  },
  {
    "path": "impls/racket/reader.rkt",
    "content": "#lang racket\n\n(provide read_str)\n\n(require \"types.rkt\")\n\n(define Reader%\n  (class object%\n    (init tokens)\n    (super-new)\n    (define toks tokens)\n    (define position 0)\n    (define/public (next)\n      (cond [(>= position (length toks)) null]\n            [else (begin\n                    (set! position (+ 1 position))\n                    (list-ref toks (- position 1)))]))\n    (define/public (peek)\n      (cond [(>= position (length toks)) null]\n            [else (list-ref toks position )]))))\n      \n\n(define (tokenize str)\n  (filter-not (lambda (s) (or (equal? s \"\") (equal? (substring s 0 1) \";\")))\n    (regexp-match* #px\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;[^\\n]*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\"\n                   str #:match-select cadr)))\n\n(define (read_atom rdr)\n  (let ([token (send rdr next)])\n    (cond [(regexp-match #px\"^-?[0-9]+$\" token)\n           (string->number token)]\n          [(regexp-match #px\"^-?[0-9][0-9.]*$\" token)\n           (string->number token)]\n          [(regexp-match #px\"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\" token)\n           (with-input-from-string token read)]\n          [(regexp-match #px\"^\\\".*$\" token)\n           (raise \"expected '\\\"', got EOF\")]\n          [(regexp-match #px\"^:\" token) (_keyword (substring token 1))]\n          [(equal? \"nil\" token) nil]\n          [(equal? \"true\" token) #t]\n          [(equal? \"false\" token) #f]\n          [else (string->symbol token)])))\n\n(define (read_list_entries rdr end)\n  (let ([tok (send rdr peek)])\n    (cond\n        [(eq? tok '()) (raise (string-append \"expected '\" end \"', got EOF\"))]\n        [(equal? end tok) '()]\n        [else\n          (cons (read_form rdr) (read_list_entries rdr end))])))\n\n(define (read_list rdr start end)\n  (let ([token (send rdr next)])\n    (if (equal? start token)\n      (let ([lst (read_list_entries rdr end)])\n        (send rdr next)\n        lst)\n      (raise (string-append \"expected '\" start \"', got EOF\")))))\n\n(define (read_form rdr)\n  (let ([token (send rdr peek)])\n    (if (null? token)\n      (raise (make-blank-exn \"blank line\" (current-continuation-marks)))\n      (cond\n        [(equal? \"'\" token) (send rdr next) (list 'quote (read_form rdr))]\n        [(equal? \"`\" token) (send rdr next) (list 'quasiquote (read_form rdr))]\n        [(equal? \"~\" token) (send rdr next) (list 'unquote (read_form rdr))]\n        [(equal? \"~@\" token) (send rdr next) (list 'splice-unquote (read_form rdr))]\n        [(equal? \"^\" token) (send rdr next)\n                            (let ([meta (read_form rdr)])\n                              (list 'with-meta (read_form rdr) meta))]\n        [(equal? \"@\" token) (send rdr next) (list 'deref (read_form rdr))]\n\n        [(equal? \")\" token) (raise \"unexpected ')'\")]\n        [(equal? \"(\" token) (read_list rdr \"(\" \")\")]\n        [(equal? \"]\" token) (raise \"unexpected ']'\")]\n        [(equal? \"[\" token) (list->vector (read_list rdr \"[\" \"]\"))]\n        [(equal? \"}\" token) (raise \"unexpected '}'\")]\n        [(equal? \"{\" token) (apply hash (read_list rdr \"{\" \"}\"))]\n        [else (read_atom rdr)]))))\n\n(define (read_str str)\n  (read_form (new Reader% [tokens (tokenize str)])))\n"
  },
  {
    "path": "impls/racket/readline.rkt",
    "content": "#lang racket\n\n(provide readline)\n\n(require (prefix-in readline: readline/readline))\n\n(require \"types.rkt\")\n\n(define history-loaded #f)\n(define HISTORY-FILE (format \"~a/.mal-history\" (find-system-path 'home-dir)))\n\n(define (load-history path)\n  (with-handlers\n    ([exn:fail? (lambda (e) #t)])\n    (map\n      (lambda (line) (readline:add-history line))\n      (string-split\n        (port->string (open-input-file path))\n        #px\"\\n\"))))\n\n(define (readline prompt)\n  (when (not history-loaded)\n    (set! history-loaded #t)\n    (load-history HISTORY-FILE))\n  (let ([line (readline:readline prompt)])\n    (if (eq? eof line)\n      nil\n      (begin\n        (readline:add-history line)\n        (with-handlers\n          ([exn:fail? (lambda (e) #t)])\n          (with-output-to-file\n            HISTORY-FILE\n            (lambda () (printf \"~a~n\" line))\n            #:exists 'append))\n        line))))\n"
  },
  {
    "path": "impls/racket/run",
    "content": "#!/usr/bin/env bash\nexec racket $(dirname $0)/${STEP:-stepA_mal}.rkt \"${@}\"\n"
  },
  {
    "path": "impls/racket/step0_repl.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\")\n\n;; read\n(define (READ str)\n  str)\n\n;; eval\n(define (EVAL ast env)\n  ast)\n\n;; print\n(define (PRINT exp)\n  exp)\n\n;; repl\n(define (rep str)\n  (PRINT (EVAL (READ str) \"\")))\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (printf \"~a~n\" (rep line))\n      (repl-loop))))\n(repl-loop)\n"
  },
  {
    "path": "impls/racket/step1_read_print.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n(define (EVAL ast env)\n  ast)\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define (rep str)\n  (PRINT (EVAL (READ str) \"\")))\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(repl-loop)\n"
  },
  {
    "path": "impls/racket/step2_eval.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"types.rkt\" \"readline.rkt\" \"reader.rkt\" \"printer.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n(define (EVAL ast env)\n  ; (printf \"EVAL: ~a~n\" (pr_str ast true))\n  (cond\n    [(symbol? ast)\n     (or (hash-ref env ast\n                   (lambda () (raise (string-append \"'\"\n                                                    (symbol->string ast)\n                                                    \"' not found\")))))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n       ast\n       (let ([f (EVAL (first ast) env)]\n             [args (map (lambda (x) (EVAL x env)) (rest ast))])\n         (apply f args)))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env (hash '+ + '- - '* * '/ /))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(repl-loop)\n"
  },
  {
    "path": "impls/racket/step3_env.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [else\n             (let ([f (EVAL a0 env)]\n                   [args (map (lambda (x) (EVAL x env)) (rest ast))])\n               (apply f args))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env%\n       [outer null]\n       [binds '(+ - * /)]\n       [exprs (list + - * /)]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(repl-loop)\n"
  },
  {
    "path": "impls/racket/step4_if_fn_do.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'do a0)\n             (last (map (lambda (x) (EVAL x env)) (drop ast 1)))]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))]\n            [else\n             (let ([f (EVAL a0 env)]\n                   [args (map (lambda (x) (EVAL x env)) (rest ast))])\n               (apply f args))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(repl-loop)\n"
  },
  {
    "path": "impls/racket/step5_tco.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'do a0)\n             (map (lambda (x) (EVAL x env)) (drop (drop-right ast 1) 1))\n             (EVAL (last ast) env)]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n             (malfunc\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))\n               (_nth ast 2) env (_nth ast 1) #f nil)]\n            [else\n             (let ([f (EVAL a0 env)]\n                   [args (map (lambda (x) (EVAL x env)) (rest ast))])\n                    (if (malfunc? f)\n                      (EVAL (malfunc-ast f)\n                            (new Env%\n                                 [outer (malfunc-env f)]\n                                 [binds (malfunc-params f)]\n                                 [exprs args]))\n                      (apply f args)))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(repl-loop)\n"
  },
  {
    "path": "impls/racket/step6_file.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'do a0)\n             (map (lambda (x) (EVAL x env)) (drop (drop-right ast 1) 1))\n             (EVAL (last ast) env)]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n             (malfunc\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))\n               (_nth ast 2) env (_nth ast 1) #f nil)]\n            [else\n             (let ([f (EVAL a0 env)]\n                   [args (map (lambda (x) (EVAL x env)) (rest ast))])\n                    (if (malfunc? f)\n                      (EVAL (malfunc-ast f)\n                            (new Env%\n                                 [outer (malfunc-env f)]\n                                 [binds (malfunc-params f)]\n                                 [exprs args]))\n                      (apply f args)))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))\n(send repl-env set '*ARGV* (_rest (current-command-line-arguments)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(let ([args (current-command-line-arguments)])\n  (if (> (vector-length args) 0)\n    (for () (rep (string-append \"(load-file \\\"\" (vector-ref args 0) \"\\\")\")))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/racket/step7_quote.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n\n(define (qq-loop elt acc)\n  (if (and (list? elt) (= (length elt) 2) (equal? (car elt) 'splice-unquote))\n    (list 'concat (cadr elt) acc)\n    (list 'cons (quasiquote elt) acc)))\n\n(define (quasiquote ast)\n  (cond\n    [(or (symbol? ast) (hash? ast))\n     (list 'quote ast)]\n\n    [(vector? ast)\n     (list 'vec (foldr qq-loop null (_to_list ast)))]\n\n    [(not (list? ast))\n     ast]\n\n    [(and (= (length ast) 2) (equal? (car ast) 'unquote))\n     (cadr ast)]\n\n    [else\n     (foldr qq-loop null ast)]))\n\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'quote a0)\n             (_nth ast 1)]\n            [(eq? 'quasiquote a0)\n             (EVAL (quasiquote (_nth ast 1)) env)]\n            [(eq? 'do a0)\n             (map (lambda (x) (EVAL x env)) (drop (drop-right ast 1) 1))\n             (EVAL (last ast) env)]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n             (malfunc\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))\n               (_nth ast 2) env (_nth ast 1) #f nil)]\n            [else\n             (let ([f (EVAL a0 env)]\n                   [args (map (lambda (x) (EVAL x env)) (rest ast))])\n                    (if (malfunc? f)\n                      (EVAL (malfunc-ast f)\n                            (new Env%\n                                 [outer (malfunc-env f)]\n                                 [binds (malfunc-params f)]\n                                 [exprs args]))\n                      (apply f args)))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))\n(send repl-env set '*ARGV* (_rest (current-command-line-arguments)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(let ([args (current-command-line-arguments)])\n  (if (> (vector-length args) 0)\n    (for () (rep (string-append \"(load-file \\\"\" (vector-ref args 0) \"\\\")\")))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/racket/step8_macros.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n\n(define (qq-loop elt acc)\n  (if (and (list? elt) (= (length elt) 2) (equal? (car elt) 'splice-unquote))\n    (list 'concat (cadr elt) acc)\n    (list 'cons (quasiquote elt) acc)))\n\n(define (quasiquote ast)\n  (cond\n    [(or (symbol? ast) (hash? ast))\n     (list 'quote ast)]\n\n    [(vector? ast)\n     (list 'vec (foldr qq-loop null (_to_list ast)))]\n\n    [(not (list? ast))\n     ast]\n\n    [(and (= (length ast) 2) (equal? (car ast) 'unquote))\n     (cadr ast)]\n\n    [else\n     (foldr qq-loop null ast)]))\n\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'quote a0)\n             (_nth ast 1)]\n            [(eq? 'quasiquote a0)\n             (EVAL (quasiquote (_nth ast 1)) env)]\n            [(eq? 'defmacro! a0)\n             (let* ([func (EVAL (_nth ast 2) env)]\n                    [mac (struct-copy malfunc func [macro? #t])])\n               (send env set (_nth ast 1) mac))]\n            [(eq? 'do a0)\n             (map (lambda (x) (EVAL x env)) (drop (drop-right ast 1) 1))\n             (EVAL (last ast) env)]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n             (malfunc\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))\n               (_nth ast 2) env (_nth ast 1) #f nil)]\n            [else\n             (let ([f (EVAL a0 env)])\n               (if (and (malfunc? f) (malfunc-macro? f))\n                 (EVAL (apply f (rest ast)) env)\n                 (let ([args (map (lambda (x) (EVAL x env)) (rest ast))])\n                    (if (malfunc? f)\n                      (EVAL (malfunc-ast f)\n                            (new Env%\n                                 [outer (malfunc-env f)]\n                                 [binds (malfunc-params f)]\n                                 [exprs args]))\n                      (apply f args)))))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))\n(send repl-env set '*ARGV* (_rest (current-command-line-arguments)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(let ([args (current-command-line-arguments)])\n  (if (> (vector-length args) 0)\n    (for () (rep (string-append \"(load-file \\\"\" (vector-ref args 0) \"\\\")\")))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/racket/step9_try.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n\n(define (qq-loop elt acc)\n  (if (and (list? elt) (= (length elt) 2) (equal? (car elt) 'splice-unquote))\n    (list 'concat (cadr elt) acc)\n    (list 'cons (quasiquote elt) acc)))\n\n(define (quasiquote ast)\n  (cond\n    [(or (symbol? ast) (hash? ast))\n     (list 'quote ast)]\n\n    [(vector? ast)\n     (list 'vec (foldr qq-loop null (_to_list ast)))]\n\n    [(not (list? ast))\n     ast]\n\n    [(and (= (length ast) 2) (equal? (car ast) 'unquote))\n     (cadr ast)]\n\n    [else\n     (foldr qq-loop null ast)]))\n\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'quote a0)\n             (_nth ast 1)]\n            [(eq? 'quasiquote a0)\n             (EVAL (quasiquote (_nth ast 1)) env)]\n            [(eq? 'defmacro! a0)\n             (let* ([func (EVAL (_nth ast 2) env)]\n                    [mac (struct-copy malfunc func [macro? #t])])\n               (send env set (_nth ast 1) mac))]\n            [(eq? 'try* a0)\n             (if (or (< (length ast) 3)\n                     (not (eq? 'catch* (_nth (_nth ast 2) 0))))\n               (EVAL (_nth ast 1) env)\n               (let ([efn (lambda (exc)\n                            (EVAL (_nth (_nth ast 2) 2)\n                                  (new Env%\n                                       [outer env]\n                                       [binds (list (_nth (_nth ast 2) 1))]\n                                       [exprs (list exc)])))])\n                 (with-handlers\n                   ([mal-exn?  (lambda (exc) (efn (mal-exn-val exc)))]\n                    [string?   (lambda (exc) (efn exc))]\n                    [exn:fail? (lambda (exc) (efn (format \"~a\" exc)))])\n                   (EVAL (_nth ast 1) env))))]\n            [(eq? 'do a0)\n             (map (lambda (x) (EVAL x env)) (drop (drop-right ast 1) 1))\n             (EVAL (last ast) env)]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n             (malfunc\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))\n               (_nth ast 2) env (_nth ast 1) #f nil)]\n            [else\n             (let ([f (EVAL a0 env)])\n               (if (and (malfunc? f) (malfunc-macro? f))\n                 (EVAL (apply f (rest ast)) env)\n                 (let ([args (map (lambda (x) (EVAL x env)) (rest ast))])\n                    (if (malfunc? f)\n                      (EVAL (malfunc-ast f)\n                            (new Env%\n                                 [outer (malfunc-env f)]\n                                 [binds (malfunc-params f)]\n                                 [exprs args]))\n                      (apply f args)))))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))\n(send repl-env set '*ARGV* (_rest (current-command-line-arguments)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [mal-exn? (lambda (exc) (printf \"Error: ~a~n\"\n                                         (pr_str (mal-exn-val exc) true)))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(let ([args (current-command-line-arguments)])\n  (if (> (vector-length args) 0)\n    (for () (rep (string-append \"(load-file \\\"\" (vector-ref args 0) \"\\\")\")))\n    (repl-loop)))\n"
  },
  {
    "path": "impls/racket/stepA_mal.rkt",
    "content": "#!/usr/bin/env racket\n#lang racket\n\n(require \"readline.rkt\" \"types.rkt\" \"reader.rkt\" \"printer.rkt\"\n         \"env.rkt\" \"core.rkt\")\n\n;; read\n(define (READ str)\n  (read_str str))\n\n;; eval\n\n(define (qq-loop elt acc)\n  (if (and (list? elt) (= (length elt) 2) (equal? (car elt) 'splice-unquote))\n    (list 'concat (cadr elt) acc)\n    (list 'cons (quasiquote elt) acc)))\n\n(define (quasiquote ast)\n  (cond\n    [(or (symbol? ast) (hash? ast))\n     (list 'quote ast)]\n\n    [(vector? ast)\n     (list 'vec (foldr qq-loop null (_to_list ast)))]\n\n    [(not (list? ast))\n     ast]\n\n    [(and (= (length ast) 2) (equal? (car ast) 'unquote))\n     (cadr ast)]\n\n    [else\n     (foldr qq-loop null ast)]))\n\n(define (EVAL ast env)\n  (let ([dbgeval (send env get 'DEBUG-EVAL)])\n    (unless (or (void? dbgeval) (eq? dbgeval nil) (eq? dbgeval #f))\n      (printf \"EVAL: ~a~n\" (pr_str ast true))))\n  (cond\n    [(symbol? ast)\n     (let ([val (send env get ast)])\n       (if (void? val)\n         (raise (string-append \"'\" (symbol->string ast) \"' not found\"))\n         val))]\n    [(vector? ast) (vector-map (lambda (x) (EVAL x env)) ast)]\n    [(hash? ast) (make-hash\n                  (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]\n    [(list? ast)\n     (if (empty? ast)\n        ast\n        (let ([a0 (_nth ast 0)])\n          (cond\n            [(eq? 'def! a0)\n             (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]\n            [(eq? 'let* a0)\n             (let ([let-env (new Env% [outer env] [binds null] [exprs null])])\n               (_map (lambda (b_e)\n                       (send let-env set (_first b_e)\n                             (EVAL (_nth b_e 1) let-env)))\n                    (_partition 2 (_to_list (_nth ast 1))))\n               (EVAL (_nth ast 2) let-env))]\n            [(eq? 'quote a0)\n             (_nth ast 1)]\n            [(eq? 'quasiquote a0)\n             (EVAL (quasiquote (_nth ast 1)) env)]\n            [(eq? 'defmacro! a0)\n             (let* ([func (EVAL (_nth ast 2) env)]\n                    [mac (struct-copy malfunc func [macro? #t])])\n               (send env set (_nth ast 1) mac))]\n            [(eq? 'try* a0)\n             (if (or (< (length ast) 3)\n                     (not (eq? 'catch* (_nth (_nth ast 2) 0))))\n               (EVAL (_nth ast 1) env)\n               (let ([efn (lambda (exc)\n                            (EVAL (_nth (_nth ast 2) 2)\n                                  (new Env%\n                                       [outer env]\n                                       [binds (list (_nth (_nth ast 2) 1))]\n                                       [exprs (list exc)])))])\n                 (with-handlers\n                   ([mal-exn?  (lambda (exc) (efn (mal-exn-val exc)))]\n                    [string?   (lambda (exc) (efn exc))]\n                    [exn:fail? (lambda (exc) (efn (format \"~a\" exc)))])\n                   (EVAL (_nth ast 1) env))))]\n            [(eq? 'do a0)\n             (map (lambda (x) (EVAL x env)) (drop (drop-right ast 1) 1))\n             (EVAL (last ast) env)]\n            [(eq? 'if a0)\n             (let ([cnd (EVAL (_nth ast 1) env)])\n               (if (or (eq? cnd nil) (eq? cnd #f))\n                 (if (> (length ast) 3)\n                   (EVAL (_nth ast 3) env)\n                   nil)\n                 (EVAL (_nth ast 2) env)))]\n            [(eq? 'fn* a0)\n             (malfunc\n               (lambda args (EVAL (_nth ast 2)\n                                  (new Env% [outer env]\n                                            [binds (_nth ast 1)]\n                                            [exprs args])))\n               (_nth ast 2) env (_nth ast 1) #f nil)]\n            [else\n             (let ([f (EVAL a0 env)])\n               (if (and (malfunc? f) (malfunc-macro? f))\n                 (EVAL (apply f (rest ast)) env)\n                 (let ([args (map (lambda (x) (EVAL x env)) (rest ast))])\n                    (if (malfunc? f)\n                      (EVAL (malfunc-ast f)\n                            (new Env%\n                                 [outer (malfunc-env f)]\n                                 [binds (malfunc-params f)]\n                                 [exprs args]))\n                      (apply f args)))))])))]\n    [else ast]))\n\n;; print\n(define (PRINT exp)\n  (pr_str exp true))\n\n;; repl\n(define repl-env\n  (new Env% [outer null] [binds null] [exprs null]))\n(define (rep str)\n  (PRINT (EVAL (READ str) repl-env)))\n\n(for ()  ;; ignore return values\n\n;; core.rkt: defined using Racket\n(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))\n(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))\n(send repl-env set '*ARGV* (_rest (current-command-line-arguments)))\n\n;; core.mal: defined using the language itself\n(rep \"(def! *host-language* \\\"racket\\\")\")\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n)\n\n(define (repl-loop)\n  (let ([line (readline \"user> \")])\n    (when (not (eq? nil line))\n      (with-handlers\n        ([string? (lambda (exc) (printf \"Error: ~a~n\" exc))]\n         [mal-exn? (lambda (exc) (printf \"Error: ~a~n\"\n                                         (pr_str (mal-exn-val exc) true)))]\n         [blank-exn? (lambda (exc) null)])\n        (printf \"~a~n\" (rep line)))\n      (repl-loop))))\n(let ([args (current-command-line-arguments)])\n  (if (> (vector-length args) 0)\n    (begin\n      (send repl-env set '*ARGV* (vector->list (vector-drop args 1)))\n      (for () (rep (string-append \"(load-file \\\"\" (vector-ref args 0) \"\\\")\"))))\n    (begin\n      (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n      (repl-loop))))\n"
  },
  {
    "path": "impls/racket/tests/step5_tco.mal",
    "content": ";; Racket: skipping non-TCO recursion\n;; Reason: completes up to 1,000,000\n"
  },
  {
    "path": "impls/racket/types.rkt",
    "content": "#lang racket\n\n(provide blank-exn? make-blank-exn mal-exn? make-mal-exn mal-exn-val\n         malfunc malfunc? malfunc-fn\n         malfunc-ast malfunc-env malfunc-params malfunc-macro? malfunc-meta\n         _partition _equal? _printf\n         nil _nil? _keyword _keyword? _string?\n         _to_list _sequential? _count _empty? _nth _first _rest _map\n         _assoc _dissoc _get\n         atom atom? atom-val set-atom-val!)\n\n(define-struct (blank-exn exn:fail:user) ())\n(define-struct (mal-exn exn:fail:user) [val])\n\n(define nil%\n  (class object%\n    (super-new)))\n\n(define nil (new nil%))\n\n(define (_nil? obj)\n  (eq? nil obj))\n\n(struct malfunc [fn ast env params macro? meta]\n        #:property prop:procedure (struct-field-index fn))\n\n;; General functions\n\n;; From: http://stackoverflow.com/questions/8725832/how-to-split-list-into-evenly-sized-chunks-in-racket-scheme/8731622#8731622\n(define (_partition n xs)\n  (if (null? xs)\n      '()\n      (let ((first-chunk (take xs n))\n            (rest (drop xs n)))\n        (cons first-chunk (_partition n rest)))))\n\n(define (_equal_seqs? seq_a seq_b)\n  (let ([a (_to_list seq_a)]\n        [b (_to_list seq_b)])\n    (and (= (length a) (length b))\n         (andmap (lambda (va vb) (_equal? va vb)) a b))))\n\n(define (_equal_hashes? a b)\n  (if (= (hash-count a) (hash-count b))\n    (let ([keys (hash-keys a)])\n      (andmap (lambda (k) (_equal? (_get a k) (_get b k))) keys))\n    #f))\n\n(define (_equal? a b)\n  (cond\n    [(and (_sequential? a) (_sequential? b)) (_equal_seqs? a b)]\n    [(and (hash? a) (hash? b))               (_equal_hashes? a b)]\n    [else                                    (equal? a b)]))\n\n;; printf with flush\n(define _printf (lambda a (apply printf a) (flush-output)))\n\n;; Keywords\n(define (_keyword str)\n  (string-append \"\\u029e\" str))\n\n(define (_keyword? k)\n  (and (string? k) (regexp-match? #px\"^\\u029e\" k)))\n\n;; Strings\n(define (_string? s)\n  (and (string? s) (not (_keyword? s))))\n\n;; Lists and vectors\n\n(define (_to_list a)\n  (if (vector? a) (vector->list a) a))\n\n(define (_sequential? seq)\n  (or (vector? seq) (list? seq)))\n\n(define (_count seq)\n  (cond [(_nil? seq)    0]\n        [(vector? seq) (vector-length seq)]\n        [else          (length seq)]))\n\n(define (_empty? seq)\n  (eq? 0 (_count seq)))\n\n(define (_nth seq idx)\n    (cond [(>= idx (_count seq)) (raise \"nth: index out of range\")]\n          [(vector? seq) (vector-ref seq idx)]\n          [else          (list-ref seq idx)]))\n\n(define (_first seq)\n  (cond [(vector? seq) (if (_empty? seq) nil (vector-ref seq 0))]\n        [else          (if (_empty? seq) nil (list-ref seq 0))]))\n\n(define (_rest seq)\n  (cond [(vector? seq) (if (_empty? seq) '() (rest (vector->list seq)))]\n        [else          (if (_empty? seq) '() (rest seq))]))\n\n(define (_map f seq)\n  (cond [(vector? seq) (vector-map f seq)]\n        [else          (map f seq)]))\n\n;; Hash maps\n(define _assoc\n  (lambda args\n    (let ([new-hm (hash-copy (first args))]\n          [pairs (_partition 2 (rest args))])\n      (map (lambda (k_v)\n             (hash-set! new-hm (first k_v) (second k_v))) pairs)\n      new-hm)))\n\n(define _dissoc\n  (lambda args\n    (let ([new-hm (hash-copy (first args))])\n      (map (lambda (k) (hash-remove! new-hm k)) (rest args))\n      new-hm)))\n\n(define (_get hm k)\n    (cond [(_nil? hm) nil]\n          [(dict-has-key? hm k) (hash-ref hm k)]\n          [else nil]))\n\n;; Atoms\n(struct atom [val] #:mutable)\n"
  },
  {
    "path": "impls/rexx/.gitignore",
    "content": "*.rexxpp\n"
  },
  {
    "path": "impls/rexx/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install cpp regina-rexx\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/rexx/Makefile",
    "content": "SRCS = step0_repl.rexx step1_read_print.rexx step2_eval.rexx step3_env.rexx \\\n       step4_if_fn_do.rexx step5_tco.rexx step6_file.rexx step7_quote.rexx \\\n       step8_macros.rexx step9_try.rexx stepA_mal.rexx\nPREPROCESSED = $(SRCS:%.rexx=%.rexxpp)\n\nall: $(PREPROCESSED) dist\n\ndist: mal\n\nmal: mal.rexxpp\n\techo \"#!/usr/bin/rexx -a\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nmal.rexxpp: stepA_mal.rexxpp\n\tcp -a $+ $@\n\n$(PREPROCESSED): %.rexxpp: %.rexx readline.rexx types.rexx reader.rexx printer.rexx env.rexx core.rexx\n\tcpp -CC -P -nostdinc $< > $@\n\nclean:\n\trm -f mal.rexx mal *.rexxpp\n\n.PHONY: all dist clean\n"
  },
  {
    "path": "impls/rexx/core.rexx",
    "content": "#ifndef __core__\n#define __core__\n\n#include \"types.rexx\"\n\nmal_equal?: procedure expose values. /* mal_equal?(a, b) */\n  return new_boolean(equal?(arg(1), arg(2)))\n\nmal_throw: procedure expose values. err /* mal_throw(a) */\n  err = \"__MAL_EXCEPTION__\" arg(1)\n  return \"ERR\"\n\nmal_nil?: procedure expose values. /* mal_nil?(a) */\n  return new_boolean(nil?(arg(1)))\n\nmal_true?: procedure expose values. /* mal_true?(a) */\n  return new_boolean(true?(arg(1)))\n\nmal_false?: procedure expose values. /* mal_false?(a) */\n  return new_boolean(false?(arg(1)))\n\nmal_string?: procedure expose values. /* mal_string?(a) */\n  return new_boolean(string?(arg(1)))\n\nmal_symbol: procedure expose values. /* mal_symbol(a) */\n  return new_symbol(obj_val(arg(1)))\n\nmal_symbol?: procedure expose values. /* mal_symbol?(a) */\n  return new_boolean(symbol?(arg(1)))\n\nmal_keyword: procedure expose values. /* mal_keyword(a) */\n  return new_keyword(obj_val(arg(1)))\n\nmal_keyword?: procedure expose values. /* mal_keyword?(a) */\n  return new_boolean(keyword?(arg(1)))\n\nmal_number?: procedure expose values. /* mal_number?(a) */\n  return new_boolean(number?(arg(1)))\n\nmal_fn?: procedure expose values. /* mal_fn?(a) */\n  return new_boolean(nativefn?(arg(1)) | (func?(arg(1)) & (func_is_macro(arg(1)) \\= 1)))\n\nmal_macro?: procedure expose values. /* mal_macro?(a) */\n  return new_boolean(func_macro?(arg(1)))\n\nmal_pr_str: procedure expose values. /* mal_pr_str(...) */\n  res = \"\"\n  do i=1 to arg()\n    element = pr_str(arg(i), 1)\n    if i == 1 then\n      res = element\n    else\n      res = res || \" \" || element\n  end\n  return new_string(res)\n\nmal_str: procedure expose values. /* mal_str(...) */\n  res = \"\"\n  do i=1 to arg()\n    element = pr_str(arg(i), 0)\n    if i == 1 then\n      res = element\n    else\n      res = res || element\n  end\n  return new_string(res)\n\nmal_prn: procedure expose values. /* mal_prn(...) */\n  res = \"\"\n  do i=1 to arg()\n    element = pr_str(arg(i), 1)\n    if i == 1 then\n      res = element\n    else\n      res = res || \" \" || element\n  end\n  say res\n  return new_nil()\n\nmal_println: procedure expose values. /* mal_println(...) */\n  res = \"\"\n  do i=1 to arg()\n    element = pr_str(arg(i), 0)\n    if i == 1 then\n      res = element\n    else\n      res = res || \" \" || element\n  end\n  say res\n  return new_nil()\n\nmal_read_string: procedure expose values. err /* mal_read_string(str) */\n  return read_str(obj_val(arg(1)))\n\nmal_readline: procedure expose values. /* mal_readline(prompt) */\n  line = readline(obj_val(arg(1)))\n  if length(line) > 0 then return new_string(line)\n  if lines() > 0 then return new_string(\"\")\n  return new_nil()\n\nmal_slurp: procedure expose values. /* mal_read_string(filename) */\n  file_content = charin(obj_val(arg(1)), 1, 100000)\n  return new_string(file_content)\n\nmal_lt: procedure expose values. /* mal_lt(a, b) */\n  return new_boolean(obj_val(arg(1)) < obj_val(arg(2)))\n\nmal_lte: procedure expose values. /* mal_lte(a, b) */\n  return new_boolean(obj_val(arg(1)) <= obj_val(arg(2)))\n\nmal_gt: procedure expose values. /* mal_gt(a, b) */\n  return new_boolean(obj_val(arg(1)) > obj_val(arg(2)))\n\nmal_gte: procedure expose values. /* mal_gte(a, b) */\n  return new_boolean(obj_val(arg(1)) >= obj_val(arg(2)))\n\nmal_add: procedure expose values. /* mal_add(a, b) */\n  return new_number(obj_val(arg(1)) + obj_val(arg(2)))\n\nmal_sub: procedure expose values. /* mal_sub(a, b) */\n  return new_number(obj_val(arg(1)) - obj_val(arg(2)))\n\nmal_mul: procedure expose values. /* mal_mul(a, b) */\n  return new_number(obj_val(arg(1)) * obj_val(arg(2)))\n\nmal_div: procedure expose values. /* mal_div(a, b) */\n  return new_number(obj_val(arg(1)) / obj_val(arg(2)))\n\nmal_time_ms: procedure expose values. /* mal_time_ms() */\n  return new_number(trunc(time('E') * 1000))\n\nmal_list: procedure expose values. /* mal_list(...) */\n  res = \"\"\n  do i=1 to arg()\n    if i == 1 then\n      res = arg(i)\n    else\n      res = res || \" \" || arg(i)\n  end\n  return new_list(res)\n\nmal_list?: procedure expose values. /* mal_list?(a) */\n  return new_boolean(list?(arg(1)))\n\nmal_vector: procedure expose values. /* mal_vector(...) */\n  res = \"\"\n  do i=1 to arg()\n    if i == 1 then\n      res = arg(i)\n    else\n      res = res || \" \" || arg(i)\n  end\n  return new_vector(res)\n\nmal_vector?: procedure expose values. /* mal_vector?(a) */\n  return new_boolean(vector?(arg(1)))\n\nmal_hash_map: procedure expose values. /* mal_hash_map(...) */\n  res = \"\"\n  do i=1 to arg()\n    if i == 1 then\n      res = arg(i)\n    else\n      res = res || \" \" || arg(i)\n  end\n  return new_hashmap(res)\n\nmal_map?: procedure expose values. /* mal_map?(a) */\n  return new_boolean(hashmap?(arg(1)))\n\nmal_assoc: procedure expose values. /* mal_assoc(a, ...) */\n  hm = arg(1)\n  res = \"\"\n  do i=2 to arg() by 2\n    key_val = arg(i) || \" \" || arg(i + 1)\n    if res == 2 then\n      res = key_val\n    else\n      res = res || \" \" || key_val\n  end\n  hm_val = obj_val(hm)\n  do i=1 to words(hm_val) by 2\n    if \\contains?(res, word(hm_val, i)) then\n      res = res || \" \" || word(hm_val, i) || \" \" || word(hm_val, i + 1)\n  end\n  return new_hashmap(res)\n\nmal_dissoc: procedure expose values. /* mal_dissoc(a, ...) */\n  hm = arg(1)\n  res = \"\"\n  hm_val = obj_val(hm)\n  do i=1 to words(hm_val) by 2\n    key = word(hm_val, i)\n    found = 0\n    do j=2 to arg()\n      if equal?(key, arg(j)) then do\n        found = 1\n        leave\n      end\n    end\n    if \\found then do\n      if length(res) > 0 then res = res || \" \"\n      res = res || key || \" \" || word(hm_val, i + 1)\n    end\n  end\n  return new_hashmap(res)\n\nmal_get: procedure expose values. /* mal_get(a, b) */\n  res = hashmap_get(obj_val(arg(1)), arg(2))\n  if res == \"\" then\n    return new_nil()\n  else\n    return res\n\nmal_contains?: procedure expose values. /* mal_contains?(a, b) */\n  return new_boolean(contains?(obj_val(arg(1)), arg(2)))\n\nmal_keys: procedure expose values. /* mal_keys(a) */\n  hm_val = obj_val(arg(1))\n  seq = \"\"\n  do i=1 to words(hm_val) by 2\n    if i == 1 then\n      seq = word(hm_val, i)\n    else\n      seq = seq || \" \" || word(hm_val, i)\n  end\n  return new_list(seq)\n\nmal_vals: procedure expose values. /* mal_vals(a) */\n  hm_val = obj_val(arg(1))\n  seq = \"\"\n  do i=2 to words(hm_val) by 2\n    if i == 1 then\n      seq = word(hm_val, i)\n    else\n      seq = seq || \" \" || word(hm_val, i)\n  end\n  return new_list(seq)\n\nmal_sequential?: procedure expose values. /* mal_sequential?(a) */\n  return new_boolean(sequential?(arg(1)))\n\nmal_cons: procedure expose values. /* mal_cons(a, b) */\n  return new_list(arg(1) || \" \" || obj_val(arg(2)))\n\nmal_concat: procedure expose values. /* mal_concat(...) */\n  seq = \"\"\n  do i=1 to arg()\n    if i == 1 then\n      seq = obj_val(arg(i))\n    else\n      seq = seq || \" \" || obj_val(arg(i))\n  end\n  return new_list(seq)\n\nmal_vec: procedure expose values. /* mal_vec(a) */\n  return new_vector(obj_val(arg(1)))\n\nmal_nth: procedure expose values. err /* mal_nth(list, index) */\n  list_val = obj_val(arg(1))\n  i = obj_val(arg(2))\n  if i >= words(list_val) then do\n    err = \"nth: index out of range\"\n    return \"ERR\"\n  end\n  return word(list_val, i + 1)\n\nmal_first: procedure expose values. /* mal_first(a) */\n  if nil?(arg(1)) then return new_nil()\n  list_val = obj_val(arg(1))\n  if words(list_val) == 0 then return new_nil()\n  return word(list_val, 1)\n\nmal_rest: procedure expose values. /* mal_rest(a) */\n  return new_list(subword(obj_val(arg(1)), 2))\n\nmal_empty?: procedure expose values. /* mal_empty?(a) */\n  if nil?(arg(1)) then return new_true()\n  return new_boolean(count_elements(arg(1)) == 0)\n\nmal_count: procedure expose values. /* mal_count(a) */\n  if nil?(arg(1)) then return new_number(0)\n  return new_number(count_elements(arg(1)))\n\napply_function: procedure expose values. env. err /* apply_function(fn, lst) */\n  f = arg(1)\n  call_args = arg(2)\n  select\n    when nativefn?(f) then do\n      call_args_val = obj_val(call_args)\n      call_list = \"\"\n      do i=1 to words(call_args_val)\n        element = '\"' || word(call_args_val, i) || '\"'\n        if i > 1 then\n          call_list = call_list || ', ' || element\n        else\n          call_list = element\n      end\n      res = \"\"\n      interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n      return res\n    end\n    when func?(f) then do\n      apply_env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n      return eval(func_body_ast(f), apply_env_idx)\n    end\n    otherwise\n      err = \"Unsupported function object type: \" || obj_type(f)\n      return \"ERR\"\n    end\n\nmal_apply: procedure expose values. env. err /* mal_apply(fn, ..., lst) */\n  fn = arg(1)\n  seq = \"\"\n  do i=2 to (arg() - 1)\n    if i == 2 then\n      seq = arg(i)\n    else\n      seq = seq || \" \" || arg(i)\n  end\n  if arg() > 1 then do\n    seq = seq || \" \" || obj_val(arg(arg()))\n  end\n  return apply_function(fn, new_list(seq))\n\nmal_map: procedure expose values. env. err /* mal_map(f, lst) */\n  fn = arg(1)\n  lst_val = obj_val(arg(2))\n  res = \"\"\n  do i=1 to words(lst_val)\n    element = word(lst_val, i)\n    mapped_element = apply_function(fn, new_list(element))\n    if mapped_element == \"ERR\" then return \"ERR\"\n    if i == 1 then\n      res = mapped_element\n    else\n      res = res || \" \" || mapped_element\n  end\n  return new_list(res)\n\nmal_conj: procedure expose values. env. err /* mal_conj(a, ...) */\n  a = arg(1)\n  select\n    when list?(a) then do\n      do i=2 to arg()\n        a = mal_cons(arg(i), a)\n      end\n      return a\n    end\n    when vector?(a) then do\n      seq = obj_val(a)\n      do i=2 to arg()\n        if length(seq) > 0 then seq = seq || \" \"\n        seq = seq || arg(i)\n      end\n      return new_vector(seq)\n    end\n    otherwise\n      err = \"conj requires list or vector\"\n      return \"ERR\"\n    end\n\nmal_seq: procedure expose values. env. err /* mal_conj(a) */\n  a = arg(1)\n  select\n    when string?(a) then do\n      str = obj_val(a)\n      if length(str) == 0 then return new_nil()\n      seq = \"\"\n      do i=1 to length(str)\n        element = new_string(substr(str, i, 1))\n        if i == 1 then\n          seq = element\n        else\n          seq = seq || \" \" || element\n      end\n      return new_list(seq)\n    end\n    when list?(a) then do\n      if count_elements(a) == 0 then return new_nil()\n      return a\n    end\n    when vector?(a) then do\n      if count_elements(a) == 0 then return new_nil()\n      return new_list(obj_val(a))\n    end\n    when nil?(a) then return new_nil()\n    otherwise\n      err = \"seq requires string or list or vector or nil\"\n      return \"ERR\"\n    end\n\nmal_with_meta: procedure expose values. /* mal_with_meta(a, b) */\n  new_obj = obj_clone_and_set_meta(arg(1), arg(2))\n  if new_obj == \"\" then return arg(1)\n  return new_obj\n\nmal_meta: procedure expose values. /* mal_meta(a) */\n  meta = obj_meta(arg(1))\n  if meta == \"\" then return new_nil()\n  return meta\n\nmal_atom: procedure expose values. /* mal_atom(a) */\n  return new_atom(arg(1))\n\nmal_atom?: procedure expose values. /* mal_atom?(a) */\n  return new_boolean(atom?(arg(1)))\n\nmal_deref: procedure expose values. /* mal_deref(a) */\n  return obj_val(arg(1))\n\nmal_reset!: procedure expose values. /* mal_reset!(a, new_val) */\n  return atom_set(arg(1), arg(2))\n\nmal_swap!: procedure expose values. env. err /* mal_swap!(a, fn, ...) */\n  atom = arg(1)\n  fn = arg(2)\n  atom_val = obj_val(atom)\n  seq = atom_val\n  do i=3 to arg()\n    seq = seq || \" \" || arg(i)\n  end\n  new_val = apply_function(fn, new_list(seq))\n  if new_val == \"ERR\" then return \"ERR\"\n  return atom_set(atom, new_val)\n\nmal_rexx_eval: procedure expose values. /* mal_rexx_eval(..., a) */\n  do i=1 to (arg() - 1)\n    interpret obj_val(arg(i))\n  end\n  last_arg = arg(arg())\n  if nil?(last_arg) then return new_nil()\n  last_arg_str = obj_val(last_arg)\n  if length(last_arg_str) == 0 then return new_nil()\n  rexx_eval_res = \"\"\n  interpret \"rexx_eval_res = \" || last_arg_str\n  if datatype(rexx_eval_res) == \"NUM\" then\n    return new_number(rexx_eval_res)\n  else\n    return new_string(rexx_eval_res)\n\nget_core_ns: procedure /* get_core_ns() */\n  return \"=           mal_equal?\"      ,\n         \"throw       mal_throw\"       ,\n                                       ,\n         \"nil?        mal_nil?\"        ,\n         \"true?       mal_true?\"       ,\n         \"false?      mal_false?\"      ,\n         \"string?     mal_string?\"     ,\n         \"symbol      mal_symbol\"      ,\n         \"symbol?     mal_symbol?\"     ,\n         \"keyword     mal_keyword\"     ,\n         \"keyword?    mal_keyword?\"    ,\n         \"number?     mal_number?\"     ,\n         \"fn?         mal_fn?\"         ,\n         \"macro?      mal_macro?\"      ,\n                                       ,\n         \"pr-str      mal_pr_str\"      ,\n         \"str         mal_str\"         ,\n         \"prn         mal_prn\"         ,\n         \"println     mal_println\"     ,\n         \"read-string mal_read_string\" ,\n         \"readline    mal_readline\"    ,\n         \"slurp       mal_slurp\"       ,\n                                       ,\n         \"<           mal_lt\"          ,\n         \"<=          mal_lte\"         ,\n         \">           mal_gt\"          ,\n         \">=          mal_gte\"         ,\n         \"+           mal_add\"         ,\n         \"-           mal_sub\"         ,\n         \"*           mal_mul\"         ,\n         \"/           mal_div\"         ,\n         \"time-ms     mal_time_ms\"     ,\n                                       ,\n         \"list        mal_list\"        ,\n         \"list?       mal_list?\"       ,\n         \"vector      mal_vector\"      ,\n         \"vector?     mal_vector?\"     ,\n         \"hash-map    mal_hash_map\"    ,\n         \"map?        mal_map?\"        ,\n         \"assoc       mal_assoc\"       ,\n         \"dissoc      mal_dissoc\"      ,\n         \"get         mal_get\"         ,\n         \"contains?   mal_contains?\"   ,\n         \"keys        mal_keys\"        ,\n         \"vals        mal_vals\"        ,\n                                       ,\n         \"sequential? mal_sequential?\" ,\n         \"cons        mal_cons\"        ,\n         \"concat      mal_concat\"      ,\n         \"vec         mal_vec\"         ,\n         \"nth         mal_nth\"         ,\n         \"first       mal_first\"       ,\n         \"rest        mal_rest\"        ,\n         \"empty?      mal_empty?\"      ,\n         \"count       mal_count\"       ,\n         \"apply       mal_apply\"       ,\n         \"map         mal_map\"         ,\n                                       ,\n         \"conj        mal_conj\"        ,\n         \"seq         mal_seq\"         ,\n                                       ,\n         \"meta        mal_meta\"        ,\n         \"with-meta   mal_with_meta\"   ,\n         \"atom        mal_atom\"        ,\n         \"atom?       mal_atom?\"       ,\n         \"deref       mal_deref\"       ,\n         \"reset!      mal_reset!\"      ,\n         \"swap!       mal_swap!\"       ,\n                                       ,\n         \"rexx-eval   mal_rexx_eval\"\n\n#endif\n"
  },
  {
    "path": "impls/rexx/env.rexx",
    "content": "#ifndef __env__\n#define __env__\n\nenv. = \"\"\nenv.0 = 0\n\nnew_env_index: procedure expose env. /* new_env_index() */\n  env.0 = env.0 + 1\n  return env.0\n\nnew_env: procedure expose env. values. /* new_env(outer_env_idx [, binds, exprs]) */\n  outer_env_idx = arg(1)\n  binds = arg(2)\n  exprs = arg(3)\n  idx = new_env_index()\n  env.idx.outer = outer_env_idx\n  env.idx.data. = \"\"\n  if binds \\= \"\" then do\n    binds_val = obj_val(binds)\n    exprs_val = obj_val(exprs)\n    do i=1 to words(binds_val)\n      varname = obj_val(word(binds_val, i))\n      if varname == \"&\" then do\n        rest_args_list = new_list(subword(exprs_val, i))\n        varname = obj_val(word(binds_val, i + 1))\n        x = env_set(idx, varname, rest_args_list)\n        leave\n      end\n      else\n        x = env_set(idx, varname, word(exprs_val, i))\n    end\n  end\n  return idx\n\nenv_set: procedure expose env. /* env_set(env_idx, key, val) */\n  env_idx = arg(1)\n  key = arg(2)\n  val = arg(3)\n  env.env_idx.data.key = val\n  return val\n\nenv_find: procedure expose env. /* env_find(env_idx, key) */\n  env_idx = arg(1)\n  key = arg(2)\n  if env.env_idx.data.key \\= \"\" then return env_idx\n  if env.env_idx.outer > 0 then return env_find(env.env_idx.outer, key)\n  return 0\n\nenv_get: procedure expose env. err /* env_get(env_idx, key) */\n  env_idx = arg(1)\n  key = arg(2)\n  found_env_idx = env_find(env_idx, key)\n  if found_env_idx == 0 then do\n    err = \"'\" || key || \"' not found\"\n    return \"ERR\"\n  end\n  return env.found_env_idx.data.key\n\n#endif\n"
  },
  {
    "path": "impls/rexx/printer.rexx",
    "content": "#ifndef __printer__\n#define __printer__\n\n#include \"types.rexx\"\n\nformat_string: procedure /* format_string(str, readable) */\n  str = arg(1)\n  readable = arg(2)\n  if readable then do\n    res = changestr('5C'x, str, \"\\\\\")\n    res = changestr('\"', res, '\\\"')\n    res = changestr('0A'x, res, \"\\n\")\n    return '\"' || res || '\"'\n  end\n  else\n    return str\n\nformat_sequence: procedure expose values. /* format_sequence(val, open_char, close_char, readable) */\n  val = arg(1)\n  open_char = arg(2)\n  close_char = arg(3)\n  readable = arg(4)\n  res = \"\"\n  do i=1 to words(val)\n    element = word(val, i)\n    if i > 1 then res = res || \" \"\n    res = res || pr_str(element, readable)\n  end\n  return open_char || res || close_char\n\npr_str: procedure expose values. /* pr_str(ast, readable) */\n  ast = arg(1)\n  readable = arg(2)\n  type = obj_type(ast)\n  val = obj_val(ast)\n  select\n    when type == \"nill\" then return \"nil\"\n    when type == \"true\" then return \"true\"\n    when type == \"fals\" then return \"false\"\n    when type == \"numb\" then return val\n    when type == \"symb\" then return val\n    when type == \"stri\" then return format_string(val, readable)\n    when type == \"keyw\" then return \":\" || val\n    when type == \"list\" then return format_sequence(val, \"(\", \")\", readable)\n    when type == \"vect\" then return format_sequence(val, \"[\", \"]\", readable)\n    when type == \"hash\" then return format_sequence(val, \"{\", \"}\", readable)\n    when type == \"nafn\" then return \"#<nativefunction:\" || val || \">\"\n    when type == \"func\" then return \"#<function:args=\" || pr_str(func_binds(ast), readable) || \">\"\n    when type == \"atom\" then return \"(atom \" || pr_str(val, readable) || \")\"\n    otherwise\n      return \"#<UNKNOWN-TYPE>\"\n    end\n\n#endif\n"
  },
  {
    "path": "impls/rexx/reader.rexx",
    "content": "#ifndef __reader__\n#define __reader__\n\n#include \"types.rexx\"\n\nnext_token: procedure expose pos /* next_token(str) */\n  TAB = '09'x\n  LF = '0A'x\n  CR = '0D'x\n  SEPARATOR_CHARS = TAB || LF || CR || \" []{}()'`,;\" || '\"'\n  WHITESPACE_CHARS = TAB || LF || CR || \" ,\"\n  str = arg(1)\n  token = \"\"\n  ch = substr(str, pos, 1)\n  select\n    when pos(ch, WHITESPACE_CHARS) > 0 then\n      pos = pos + 1\n    when pos(ch, \"[]{}()'`^@\") > 0 then do\n      pos = pos + 1\n      token = ch\n    end\n    when ch == '~' then do\n      if substr(str, pos + 1, 1) == '@' then do\n        pos = pos + 2\n        token = \"~@\"\n      end\n      else do\n        pos = pos + 1\n        token = \"~\"\n      end\n    end\n    when ch == \";\" then do\n      do while pos <= length(str)\n        ch = substr(str, pos, 1)\n        if (ch == LF) | (ch == CR) then\n          leave\n        else\n          pos = pos + 1\n      end\n    end\n    when ch == '\"' then do\n      tmppos = pos + 1\n      do while tmppos < length(str)\n        ch = substr(str, tmppos, 1)\n        select\n          when ch == '\"' then\n            leave\n          when ch == '5C'x then /* backslash */\n            tmppos = tmppos + 2\n          otherwise\n            tmppos = tmppos + 1\n        end\n      end\n      token = substr(str, pos, tmppos - pos + 1)\n      pos = tmppos + 1\n    end\n    otherwise\n      tmppos = pos\n      do while tmppos <= length(str)\n        ch = substr(str, tmppos, 1)\n        if pos(ch, SEPARATOR_CHARS) > 0 then\n          leave\n        else\n          token = token || ch\n        tmppos = tmppos + 1\n      end\n      pos = tmppos\n    end\n  return token\n\ntokenize: procedure expose tokens. /* tokenize(str) */\n  str = arg(1)\n  tokens. = \"\"\n  num_of_tokens = 0\n  str_to_tokenize = str\n  pos = 1\n  do while pos <= length(str)\n    token = next_token(str_to_tokenize)\n    if length(token) > 0 then do\n      num_of_tokens = num_of_tokens + 1\n      tokens.num_of_tokens = token\n    end\n  end\n  tokens.0 = num_of_tokens\n  return num_of_tokens\n\nis_number: procedure /* is_number(token) */\n  token = arg(1)\n  ch = substr(token, 1, 1)\n  DIGITS = \"0123456789\"\n  if pos(ch, DIGITS) > 0 then return 1\n  if (ch == '-') & (pos(substr(token, 2, 1), DIGITS) > 0) then return 1\n  return 0\n\nparse_string: procedure /* parse_string(token) */\n  token = arg(1)\n  res = substr(token, 2, length(token) - 2) /* Remove quotes */\n  res = changestr(\"\\\\\", res, '01'x)\n  res = changestr(\"\\n\", res, '0A'x)\n  res = changestr('\\\"', res, '\"')\n  res = changestr('01'x, res, '5C'x)\n  return res\n\nparse_keyword: procedure /* parse_keyword(token) */\n  token = arg(1)\n  return substr(token, 2)  /* Remove initial \":\" */\n\nread_atom: procedure expose values. tokens. pos err /* read_atom() */\n  token = tokens.pos\n  pos = pos + 1\n  select\n    when is_number(token) then return new_number(token)\n    when token == \"nil\" then return new_nil()\n    when token == \"true\" then return new_true()\n    when token == \"false\" then return new_false()\n    when substr(token, 1, 1) == ':' then return new_keyword(parse_keyword(token))\n    when substr(token, 1, 1) == '\"' then do\n      if substr(token, length(token), 1) \\== '\"' then do\n        end_char = '\"'\n        err = \"expected '\" || end_char || \"', got EOF\"\n        return \"ERR\"\n      end\n      return new_string(parse_string(token))\n    end\n    otherwise\n      return new_symbol(token)\n    end\n\nread_sequence: procedure expose values. tokens. pos err /* read_sequence(type, end_char) */\n  type = arg(1)\n  end_char = arg(2)\n  pos = pos + 1 /* Consume the open paren */\n  token = tokens.pos\n  seq = \"\"\n  do while (pos <= tokens.0) & (token \\== end_char)\n    element = read_form()\n    if element == \"ERR\" then return \"ERR\"\n    if seq == \"\" then\n      seq = element\n    else\n      seq = seq || \" \" || element\n    token = tokens.pos\n    if token == \"\" then do\n      err = \"expected '\" || end_char || \"', got EOF\"\n      return \"ERR\"\n    end\n  end\n  pos = pos + 1 /* Consume the close paren */\n  return new_seq(type, seq)\n\nreader_macro: procedure  expose values. tokens. pos /* reader_macro(symbol) */\n  symbol = arg(1)\n  pos = pos + 1 /* Consume the macro token */\n  element = read_form()\n  if element == \"ERR\" then return \"ERR\"\n  seq = new_symbol(symbol) || \" \" || element\n  return new_list(seq)\n\nreader_with_meta_macro: procedure  expose values. tokens. pos /* reader_with_meta_macro() */\n  pos = pos + 1 /* Consume the macro token */\n  meta = read_form()\n  if meta == \"ERR\" then return \"ERR\"\n  element = read_form()\n  if element == \"ERR\" then return \"ERR\"\n  seq = new_symbol(\"with-meta\") || \" \" || element || \" \" || meta\n  return new_list(seq)\n\nread_form: procedure expose values. tokens. pos err /* read_form() */\n  token = tokens.pos\n  select\n    when token == \"'\" then return reader_macro(\"quote\")\n    when token == '`' then return reader_macro(\"quasiquote\")\n    when token == '~' then return reader_macro(\"unquote\")\n    when token == '~@' then return reader_macro(\"splice-unquote\")\n    when token == '@' then return reader_macro(\"deref\")\n    when token == '^' then return reader_with_meta_macro()\n    when token == '(' then return read_sequence(\"list\", \")\")\n    when token == ')' then do\n      err = \"unexpected ')'\"\n      return \"ERR\"\n    end\n    when token == '[' then return read_sequence(\"vect\", \"]\")\n    when token == ']' then do\n      err = \"unexpected ']'\"\n      return \"ERR\"\n    end\n    when token == '{' then return read_sequence(\"hash\", \"}\")\n    when token == '}' then do\n      err = \"unexpected '}'\"\n      return \"ERR\"\n    end\n    otherwise\n      return read_atom()\n    end\n\nread_str: procedure expose values. err /* read_str(line) */\n  line = arg(1)\n  tokens. = \"\"\n  num_of_tokens = tokenize(line)\n  if num_of_tokens == 0 then\n    return \"\"\n  ast. = \"\"\n  pos = 1\n  return read_form()\n\n#endif\n"
  },
  {
    "path": "impls/rexx/readline.rexx",
    "content": "#ifndef __readline__\n#define __readline__\n\nreadline: procedure /* readline(prompt) */\n  call charout , arg(1)\n  return linein()\n\n#endif\n"
  },
  {
    "path": "impls/rexx/run",
    "content": "#!/bin/sh\nexec rexx -a $(dirname $0)/${STEP:-stepA_mal}.rexxpp \"${@}\"\n"
  },
  {
    "path": "impls/rexx/step0_repl.rexx",
    "content": "call main\nexit\n\n#include \"readline.rexx\"\n\nread: procedure /* read(str) */\n  return arg(1)\n\neval: procedure /* eval(exp, env) */\n  return arg(1)\n\nprint: procedure /* print(exp) */\n  return arg(1)\n\nrep: procedure /* rep(str) */\n  return print(eval(read(arg(1), \"\")))\n\nmain:\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then\n      call lineout , rep(input_line)\n  end\n"
  },
  {
    "path": "impls/rexx/step1_read_print.rexx",
    "content": "call main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\neval: procedure expose values. /* eval(exp, env) */\n  return arg(1)\n\nprint: procedure expose values. /* print(exp) */\n  return pr_str(arg(1), 1)\n\nrep: procedure expose values. env. err /* rep(str) */\n  ast = read(arg(1))\n  if ast == \"ERR\" then return \"ERR\"\n  exp = eval(ast)\n  return print(exp)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step2_eval.rexx",
    "content": "call main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n\n  --  call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then do\n      varname = astval\n      if env.varname == \"\" then do\n        err = \"'\" || varname || \"' not found\"\n        return \"ERR\"\n      end\n      return env.varname\n    end\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n  --  ast is a non-empty list\n\n  a0 = word(astval, 1)\n  f = eval(a0, env_idx)\n  if f == \"ERR\" then return \"ERR\"\n\n  --  Evaluate the arguments and store them to lst.\n  lst = \"\"\n  do i=2 to words(astval)\n    element = eval(word(astval, i), env_idx)\n    if element == \"ERR\" then return \"ERR\"\n    if i > 2 then\n      lst = lst || \" \" || element\n    else\n      lst = element\n  end\n\n  call_args = lst\n  call_list = \"\"\n  do i=1 to words(call_args)\n    element = '\"' || word(call_args, i) || '\"'\n    if i > 1 then\n      call_list = call_list || ', ' || element\n    else\n      call_list = element\n  end\n  res = \"\"\n  interpret \"res = \" || f || \"(\" || call_list || \")\"\n  return res\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nrep: procedure expose values. env. err /* rep(str) */\n  ast = read(arg(1))\n  if ast == \"ERR\" then return \"ERR\"\n  exp = eval(ast)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_add: procedure expose values. /* mal_add(a, b) */\n  return new_number(obj_val(arg(1)) + obj_val(arg(2)))\n\nmal_sub: procedure expose values. /* mal_sub(a, b) */\n  return new_number(obj_val(arg(1)) - obj_val(arg(2)))\n\nmal_mul: procedure expose values. /* mal_mul(a, b) */\n  return new_number(obj_val(arg(1)) * obj_val(arg(2)))\n\nmal_div: procedure expose values. /* mal_div(a, b) */\n  return new_number(obj_val(arg(1)) / obj_val(arg(2)))\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  key = \"+\" ; env.key = \"mal_add\"\n  key = \"-\" ; env.key = \"mal_sub\"\n  key = \"*\" ; env.key = \"mal_mul\"\n  key = \"/\" ; env.key = \"mal_div\"\n  err = \"\"\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step3_env.rexx",
    "content": "call main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n  --  ast is a non-empty list\n\n  a0 = word(astval, 1)\n  a0sym = obj_val(a0)\n  select\n    when a0sym == \"def!\" then do\n      a1sym = obj_val(word(astval, 2))\n      a2 = eval(word(astval, 3), env_idx)\n      if a2 == \"ERR\" then return \"ERR\"\n      return env_set(env_idx, a1sym, a2)\n    end\n    when a0sym == \"let*\" then do\n      a1lst = obj_val(word(astval, 2))\n      letenv_idx = new_env(env_idx)\n      do i=1 to words(a1lst) by 2\n        k = obj_val(word(a1lst, i))\n        v = eval(word(a1lst, i + 1), letenv_idx)\n        if v == \"ERR\" then return \"ERR\"\n        unused = env_set(letenv_idx, k, v)\n      end\n      return eval(word(astval, 3), letenv_idx)\n    end\n    otherwise\n     f = eval(a0, env_idx)\n     if f == \"ERR\" then return \"ERR\"\n\n      --  Evaluate the arguments and store them to lst.\n      lst = \"\"\n      do i=2 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 2 then\n          lst = lst || \" \" || element\n        else\n          lst = element\n      end\n\n      call_args = lst\n      call_list = \"\"\n      do i=1 to words(call_args)\n        element = '\"' || word(call_args, i) || '\"'\n        if i > 1 then\n          call_list = call_list || ', ' || element\n        else\n          call_list = element\n      end\n      res = \"\"\n      interpret \"res = \" || f || \"(\" || call_list || \")\"\n      return res\n    end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_add: procedure expose values. /* mal_add(a, b) */\n  return new_number(obj_val(arg(1)) + obj_val(arg(2)))\n\nmal_sub: procedure expose values. /* mal_sub(a, b) */\n  return new_number(obj_val(arg(1)) - obj_val(arg(2)))\n\nmal_mul: procedure expose values. /* mal_mul(a, b) */\n  return new_number(obj_val(arg(1)) * obj_val(arg(2)))\n\nmal_div: procedure expose values. /* mal_div(a, b) */\n  return new_number(obj_val(arg(1)) / obj_val(arg(2)))\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n  x = env_set(repl_env_idx, \"+\", \"mal_add\")\n  x = env_set(repl_env_idx, \"-\", \"mal_sub\")\n  x = env_set(repl_env_idx, \"*\", \"mal_mul\")\n  x = env_set(repl_env_idx, \"/\", \"mal_div\")\n  err = \"\"\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step4_if_fn_do.rexx",
    "content": "call main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n  --  ast is a non-empty list\n\n  a0 = word(astval, 1)\n  a0sym = obj_val(a0)\n  select\n    when a0sym == \"def!\" then do\n      a1sym = obj_val(word(astval, 2))\n      a2 = eval(word(astval, 3), env_idx)\n      if a2 == \"ERR\" then return \"ERR\"\n      return env_set(env_idx, a1sym, a2)\n    end\n    when a0sym == \"let*\" then do\n      a1lst = obj_val(word(astval, 2))\n      letenv_idx = new_env(env_idx)\n      do i=1 to words(a1lst) by 2\n        k = obj_val(word(a1lst, i))\n        v = eval(word(a1lst, i + 1), letenv_idx)\n        if v == \"ERR\" then return \"ERR\"\n        unused = env_set(letenv_idx, k, v)\n      end\n      return eval(word(astval, 3), letenv_idx)\n    end\n    when a0sym == \"do\" then do\n      res = \"ERR\"\n      do i=2 to words(astval)\n        res = eval(word(astval, i), env_idx)\n        if res == \"ERR\" then return \"ERR\"\n      end\n      return res\n    end\n    when a0sym == \"if\" then do\n      condval = eval(word(astval, 2), env_idx)\n      if false?(condval) | nil?(condval) then\n        if words(astval) >= 4 then\n          return eval(word(astval, 4), env_idx)\n        else\n          return new_nil()\n      else\n        return eval(word(astval, 3), env_idx)\n    end\n    when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n    otherwise\n     f = eval(a0, env_idx)\n     if f == \"ERR\" then return \"ERR\"\n\n      --  Evaluate the arguments and store them to lst.\n      lst = \"\"\n      do i=2 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 2 then\n          lst = lst || \" \" || element\n        else\n          lst = element\n      end\n\n      select\n        when nativefn?(f) then do\n          call_args = lst\n          call_list = \"\"\n          do i=1 to words(call_args)\n            element = '\"' || word(call_args, i) || '\"'\n            if i > 1 then\n              call_list = call_list || ', ' || element\n            else\n              call_list = element\n          end\n          res = \"\"\n          interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n          return res\n        end\n        when func?(f) then do\n          call_args = new_list(lst)\n          return eval(func_body_ast(f), new_env(func_env_idx(f), func_binds(f), call_args))\n        end\n        otherwise\n          err = \"Unsupported function object type: \" || obj_type(f)\n          return \"ERR\"\n        end\n    end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n\n  /* core.mal: defined using the language itself */\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n\n  err = \"\"\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step5_tco.rexx",
    "content": "call main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n  do forever\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n    --  ast is a non-empty list\n\n    a0 = word(astval, 1)\n    a0sym = obj_val(a0)\n    select\n      when a0sym == \"def!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, a2)\n      end\n      when a0sym == \"let*\" then do\n        a1lst = obj_val(word(astval, 2))\n        letenv_idx = new_env(env_idx)\n        do i=1 to words(a1lst) by 2\n          k = obj_val(word(a1lst, i))\n          v = eval(word(a1lst, i + 1), letenv_idx)\n          if v == \"ERR\" then return \"ERR\"\n          unused = env_set(letenv_idx, k, v)\n        end\n        env_idx = letenv_idx\n        ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"do\" then do\n        do i=2 to (words(astval) - 1)\n          res = eval(word(astval, i), env_idx)\n          if res == \"ERR\" then return \"ERR\"\n        end\n        ast = word(astval, words(astval))\n        /* TCO */\n      end\n      when a0sym == \"if\" then do\n        condval = eval(word(astval, 2), env_idx)\n        if false?(condval) | nil?(condval) then\n          if words(astval) >= 4 then\n            ast = word(astval, 4)\n          else\n            return new_nil()\n        else\n          ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n      otherwise\n       f = eval(a0, env_idx)\n       if f == \"ERR\" then return \"ERR\"\n\n        --  Evaluate the arguments and store them to lst.\n        lst = \"\"\n        do i=2 to words(astval)\n          element = eval(word(astval, i), env_idx)\n          if element == \"ERR\" then return \"ERR\"\n          if i > 2 then\n            lst = lst || \" \" || element\n          else\n            lst = element\n        end\n\n        select\n          when nativefn?(f) then do\n            call_args = lst\n            call_list = \"\"\n            do i=1 to words(call_args)\n              element = '\"' || word(call_args, i) || '\"'\n              if i > 1 then\n                call_list = call_list || ', ' || element\n              else\n                call_list = element\n            end\n            res = \"\"\n            interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n            return res\n          end\n          when func?(f) then do\n            call_args = new_list(lst)\n            env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n            ast = func_body_ast(f)\n            /* TCO */\n          end\n          otherwise\n            err = \"Unsupported function object type: \" || obj_type(f)\n            return \"ERR\"\n          end\n      end\n  end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n\n  /* core.mal: defined using the language itself */\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n\n  err = \"\"\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step6_file.rexx",
    "content": "/* Save command-line arguments from the top-level program before entering a procedure */\ncommand_line_args. = \"\"\ncommand_line_args.0 = arg()\ndo i=1 to command_line_args.0\n  command_line_args.i = arg(i)\nend\n\ncall main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n  do forever\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n    --  ast is a non-empty list\n\n    a0 = word(astval, 1)\n    a0sym = obj_val(a0)\n    select\n      when a0sym == \"def!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, a2)\n      end\n      when a0sym == \"let*\" then do\n        a1lst = obj_val(word(astval, 2))\n        letenv_idx = new_env(env_idx)\n        do i=1 to words(a1lst) by 2\n          k = obj_val(word(a1lst, i))\n          v = eval(word(a1lst, i + 1), letenv_idx)\n          if v == \"ERR\" then return \"ERR\"\n          unused = env_set(letenv_idx, k, v)\n        end\n        env_idx = letenv_idx\n        ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"do\" then do\n        do i=2 to (words(astval) - 1)\n          res = eval(word(astval, i), env_idx)\n          if res == \"ERR\" then return \"ERR\"\n        end\n        ast = word(astval, words(astval))\n        /* TCO */\n      end\n      when a0sym == \"if\" then do\n        condval = eval(word(astval, 2), env_idx)\n        if false?(condval) | nil?(condval) then\n          if words(astval) >= 4 then\n            ast = word(astval, 4)\n          else\n            return new_nil()\n        else\n          ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n      otherwise\n       f = eval(a0, env_idx)\n       if f == \"ERR\" then return \"ERR\"\n\n        --  Evaluate the arguments and store them to lst.\n        lst = \"\"\n        do i=2 to words(astval)\n          element = eval(word(astval, i), env_idx)\n          if element == \"ERR\" then return \"ERR\"\n          if i > 2 then\n            lst = lst || \" \" || element\n          else\n            lst = element\n        end\n\n        select\n          when nativefn?(f) then do\n            call_args = lst\n            call_list = \"\"\n            do i=1 to words(call_args)\n              element = '\"' || word(call_args, i) || '\"'\n              if i > 1 then\n                call_list = call_list || ', ' || element\n              else\n                call_list = element\n            end\n            res = \"\"\n            interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n            return res\n          end\n          when func?(f) then do\n            call_args = new_list(lst)\n            env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n            ast = func_body_ast(f)\n            /* TCO */\n          end\n          otherwise\n            err = \"Unsupported function object type: \" || obj_type(f)\n            return \"ERR\"\n          end\n      end\n  end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_eval: procedure expose values. env. err /* mal_eval(ast) */\n  ast = arg(1)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(arg(1), 1) /* repl_env_idx is always 1 because it's the first env */\n\nbuild_args_list: procedure expose values. command_line_args. /* build_args_list() */\n  seq = \"\"\n  do i=2 to command_line_args.0\n    s = new_string(command_line_args.i)\n    if i == 1 then\n      seq = s\n    else\n      seq = seq || \" \" || s\n  end\n  return new_list(seq)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n  x = env_set(repl_env_idx, \"eval\", new_nativefn(\"mal_eval\"))\n  x = env_set(repl_env_idx, \"*ARGV*\", build_args_list())\n\n  /* core.mal: defined using the language itself */\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n  x = re('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\n  err = \"\"\n  if command_line_args.0 > 0 then do\n    x = re('(load-file \"' || command_line_args.1 || '\")')\n    return\n  end\n\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step7_quote.rexx",
    "content": "/* Save command-line arguments from the top-level program before entering a procedure */\ncommand_line_args. = \"\"\ncommand_line_args.0 = arg()\ndo i=1 to command_line_args.0\n  command_line_args.i = arg(i)\nend\n\ncall main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\nstarts_with?: procedure expose values. /* starts_with?(lst, sym) */\n  lst = arg(1)\n  sym = arg(2)\n  if words(obj_val(lst)) <> 2 then return 0\n  a0 = word(obj_val(lst), 1)\n  return symbol?(a0) & obj_val(a0) == sym\n\nqq_loop: procedure expose values. /* qq_loop(elt, acc) */\n  elt = arg(1)\n  acc = arg(2)\n  if list?(elt) & starts_with?(elt, \"splice-unquote\") then\n    return new_list(new_symbol(\"concat\") || \" \" || word(obj_val(elt), 2) || \" \" || acc)\n  else\n    return new_list(new_symbol(\"cons\") || \" \" || quasiquote(elt) || \" \" || acc)\n\nqq_foldr: procedure expose values. /* qq_foldr(xs) */\n  xs = arg(1)\n  acc = new_list()\n  do i=words(xs) to 1 by -1\n    acc = qq_loop(word(xs, i), acc)\n  end\n  return acc\n\nquasiquote: procedure expose values. env. err /* quasiquote(ast) */\n  ast = arg(1)\n  type = obj_type(ast)\n  select\n    when type == \"list\" then\n        if starts_with?(ast, \"unquote\") then\n          return word(obj_val(ast), 2)\n        else\n          return qq_foldr(obj_val(ast))\n    when type == \"vect\" then\n      return new_list(new_symbol(\"vec\") || \" \" || qq_foldr(obj_val(ast)))\n    when type == \"symb\" | type == \"hash\" then\n      return new_list(new_symbol(\"quote\") || \" \" || ast)\n    otherwise\n      return ast\n    end\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n  do forever\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n    --  ast is a non-empty list\n\n    a0 = word(astval, 1)\n    a0sym = obj_val(a0)\n    select\n      when a0sym == \"def!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, a2)\n      end\n      when a0sym == \"let*\" then do\n        a1lst = obj_val(word(astval, 2))\n        letenv_idx = new_env(env_idx)\n        do i=1 to words(a1lst) by 2\n          k = obj_val(word(a1lst, i))\n          v = eval(word(a1lst, i + 1), letenv_idx)\n          if v == \"ERR\" then return \"ERR\"\n          unused = env_set(letenv_idx, k, v)\n        end\n        env_idx = letenv_idx\n        ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"quote\" then return word(astval, 2)\n      when a0sym == \"quasiquote\" then do\n        ast = quasiquote(word(astval, 2))\n        /* TCO */\n      end\n      when a0sym == \"do\" then do\n        do i=2 to (words(astval) - 1)\n          res = eval(word(astval, i), env_idx)\n          if res == \"ERR\" then return \"ERR\"\n        end\n        ast = word(astval, words(astval))\n        /* TCO */\n      end\n      when a0sym == \"if\" then do\n        condval = eval(word(astval, 2), env_idx)\n        if false?(condval) | nil?(condval) then\n          if words(astval) >= 4 then\n            ast = word(astval, 4)\n          else\n            return new_nil()\n        else\n          ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n      otherwise\n       f = eval(a0, env_idx)\n       if f == \"ERR\" then return \"ERR\"\n\n        --  Evaluate the arguments and store them to lst.\n        lst = \"\"\n        do i=2 to words(astval)\n          element = eval(word(astval, i), env_idx)\n          if element == \"ERR\" then return \"ERR\"\n          if i > 2 then\n            lst = lst || \" \" || element\n          else\n            lst = element\n        end\n\n        select\n          when nativefn?(f) then do\n            call_args = lst\n            call_list = \"\"\n            do i=1 to words(call_args)\n              element = '\"' || word(call_args, i) || '\"'\n              if i > 1 then\n                call_list = call_list || ', ' || element\n              else\n                call_list = element\n            end\n            res = \"\"\n            interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n            return res\n          end\n          when func?(f) then do\n            call_args = new_list(lst)\n            env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n            ast = func_body_ast(f)\n            /* TCO */\n          end\n          otherwise\n            err = \"Unsupported function object type: \" || obj_type(f)\n            return \"ERR\"\n          end\n      end\n  end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_eval: procedure expose values. env. err /* mal_eval(ast) */\n  ast = arg(1)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(arg(1), 1) /* repl_env_idx is always 1 because it's the first env */\n\nbuild_args_list: procedure expose values. command_line_args. /* build_args_list() */\n  seq = \"\"\n  do i=2 to command_line_args.0\n    s = new_string(command_line_args.i)\n    if i == 1 then\n      seq = s\n    else\n      seq = seq || \" \" || s\n  end\n  return new_list(seq)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n  x = env_set(repl_env_idx, \"eval\", new_nativefn(\"mal_eval\"))\n  x = env_set(repl_env_idx, \"*ARGV*\", build_args_list())\n\n  /* core.mal: defined using the language itself */\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n  x = re('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n\n  err = \"\"\n  if command_line_args.0 > 0 then do\n    x = re('(load-file \"' || command_line_args.1 || '\")')\n    return\n  end\n\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step8_macros.rexx",
    "content": "/* Save command-line arguments from the top-level program before entering a procedure */\ncommand_line_args. = \"\"\ncommand_line_args.0 = arg()\ndo i=1 to command_line_args.0\n  command_line_args.i = arg(i)\nend\n\ncall main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\nstarts_with?: procedure expose values. /* starts_with?(lst, sym) */\n  lst = arg(1)\n  sym = arg(2)\n  if words(obj_val(lst)) <> 2 then return 0\n  a0 = word(obj_val(lst), 1)\n  return symbol?(a0) & obj_val(a0) == sym\n\nqq_loop: procedure expose values. /* qq_loop(elt, acc) */\n  elt = arg(1)\n  acc = arg(2)\n  if list?(elt) & starts_with?(elt, \"splice-unquote\") then\n    return new_list(new_symbol(\"concat\") || \" \" || word(obj_val(elt), 2) || \" \" || acc)\n  else\n    return new_list(new_symbol(\"cons\") || \" \" || quasiquote(elt) || \" \" || acc)\n\nqq_foldr: procedure expose values. /* qq_foldr(xs) */\n  xs = arg(1)\n  acc = new_list()\n  do i=words(xs) to 1 by -1\n    acc = qq_loop(word(xs, i), acc)\n  end\n  return acc\n\nquasiquote: procedure expose values. env. err /* quasiquote(ast) */\n  ast = arg(1)\n  type = obj_type(ast)\n  select\n    when type == \"list\" then\n        if starts_with?(ast, \"unquote\") then\n          return word(obj_val(ast), 2)\n        else\n          return qq_foldr(obj_val(ast))\n    when type == \"vect\" then\n      return new_list(new_symbol(\"vec\") || \" \" || qq_foldr(obj_val(ast)))\n    when type == \"symb\" | type == \"hash\" then\n      return new_list(new_symbol(\"quote\") || \" \" || ast)\n    otherwise\n      return ast\n    end\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n  do forever\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n    --  ast is a non-empty list\n\n    a0 = word(astval, 1)\n    a0sym = obj_val(a0)\n    select\n      when a0sym == \"def!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, a2)\n      end\n      when a0sym == \"let*\" then do\n        a1lst = obj_val(word(astval, 2))\n        letenv_idx = new_env(env_idx)\n        do i=1 to words(a1lst) by 2\n          k = obj_val(word(a1lst, i))\n          v = eval(word(a1lst, i + 1), letenv_idx)\n          if v == \"ERR\" then return \"ERR\"\n          unused = env_set(letenv_idx, k, v)\n        end\n        env_idx = letenv_idx\n        ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"quote\" then return word(astval, 2)\n      when a0sym == \"quasiquote\" then do\n        ast = quasiquote(word(astval, 2))\n        /* TCO */\n      end\n      when a0sym == \"defmacro!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, func_mark_as_macro(a2))\n      end\n      when a0sym == \"do\" then do\n        do i=2 to (words(astval) - 1)\n          res = eval(word(astval, i), env_idx)\n          if res == \"ERR\" then return \"ERR\"\n        end\n        ast = word(astval, words(astval))\n        /* TCO */\n      end\n      when a0sym == \"if\" then do\n        condval = eval(word(astval, 2), env_idx)\n        if false?(condval) | nil?(condval) then\n          if words(astval) >= 4 then\n            ast = word(astval, 4)\n          else\n            return new_nil()\n        else\n          ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n      otherwise\n       f = eval(a0, env_idx)\n       if f == \"ERR\" then return \"ERR\"\n\n       if func_macro?(f) then do\n         call_args = mal_rest(ast)\n         mac_env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n         ast = eval(func_body_ast(f), mac_env_idx)\n         /* TCO */\n       end\n       else do\n\n        --  Evaluate the arguments and store them to lst.\n        lst = \"\"\n        do i=2 to words(astval)\n          element = eval(word(astval, i), env_idx)\n          if element == \"ERR\" then return \"ERR\"\n          if i > 2 then\n            lst = lst || \" \" || element\n          else\n            lst = element\n        end\n\n        select\n          when nativefn?(f) then do\n            call_args = lst\n            call_list = \"\"\n            do i=1 to words(call_args)\n              element = '\"' || word(call_args, i) || '\"'\n              if i > 1 then\n                call_list = call_list || ', ' || element\n              else\n                call_list = element\n            end\n            res = \"\"\n            interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n            return res\n          end\n          when func?(f) then do\n            call_args = new_list(lst)\n            env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n            ast = func_body_ast(f)\n            /* TCO */\n          end\n          otherwise\n            err = \"Unsupported function object type: \" || obj_type(f)\n            return \"ERR\"\n          end\n       end\n      end\n  end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_eval: procedure expose values. env. err /* mal_eval(ast) */\n  ast = arg(1)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(arg(1), 1) /* repl_env_idx is always 1 because it's the first env */\n\nbuild_args_list: procedure expose values. command_line_args. /* build_args_list() */\n  seq = \"\"\n  do i=2 to command_line_args.0\n    s = new_string(command_line_args.i)\n    if i == 1 then\n      seq = s\n    else\n      seq = seq || \" \" || s\n  end\n  return new_list(seq)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n  x = env_set(repl_env_idx, \"eval\", new_nativefn(\"mal_eval\"))\n  x = env_set(repl_env_idx, \"*ARGV*\", build_args_list())\n\n  /* core.mal: defined using the language itself */\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n  x = re('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n  x = re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \" || '\"' || \"odd number of forms to cond\" || '\"' || \")) (cons 'cond (rest (rest xs)))))))\");\n\n  err = \"\"\n  if command_line_args.0 > 0 then do\n    x = re('(load-file \"' || command_line_args.1 || '\")')\n    return\n  end\n\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then\n        call lineout , \"Error: \" || err\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/step9_try.rexx",
    "content": "/* Save command-line arguments from the top-level program before entering a procedure */\ncommand_line_args. = \"\"\ncommand_line_args.0 = arg()\ndo i=1 to command_line_args.0\n  command_line_args.i = arg(i)\nend\n\ncall main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\nstarts_with?: procedure expose values. /* starts_with?(lst, sym) */\n  lst = arg(1)\n  sym = arg(2)\n  if words(obj_val(lst)) <> 2 then return 0\n  a0 = word(obj_val(lst), 1)\n  return symbol?(a0) & obj_val(a0) == sym\n\nqq_loop: procedure expose values. /* qq_loop(elt, acc) */\n  elt = arg(1)\n  acc = arg(2)\n  if list?(elt) & starts_with?(elt, \"splice-unquote\") then\n    return new_list(new_symbol(\"concat\") || \" \" || word(obj_val(elt), 2) || \" \" || acc)\n  else\n    return new_list(new_symbol(\"cons\") || \" \" || quasiquote(elt) || \" \" || acc)\n\nqq_foldr: procedure expose values. /* qq_foldr(xs) */\n  xs = arg(1)\n  acc = new_list()\n  do i=words(xs) to 1 by -1\n    acc = qq_loop(word(xs, i), acc)\n  end\n  return acc\n\nquasiquote: procedure expose values. env. err /* quasiquote(ast) */\n  ast = arg(1)\n  type = obj_type(ast)\n  select\n    when type == \"list\" then\n        if starts_with?(ast, \"unquote\") then\n          return word(obj_val(ast), 2)\n        else\n          return qq_foldr(obj_val(ast))\n    when type == \"vect\" then\n      return new_list(new_symbol(\"vec\") || \" \" || qq_foldr(obj_val(ast)))\n    when type == \"symb\" | type == \"hash\" then\n      return new_list(new_symbol(\"quote\") || \" \" || ast)\n    otherwise\n      return ast\n    end\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n  do forever\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n    --  ast is a non-empty list\n\n    a0 = word(astval, 1)\n    a0sym = obj_val(a0)\n    select\n      when a0sym == \"def!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, a2)\n      end\n      when a0sym == \"let*\" then do\n        a1lst = obj_val(word(astval, 2))\n        letenv_idx = new_env(env_idx)\n        do i=1 to words(a1lst) by 2\n          k = obj_val(word(a1lst, i))\n          v = eval(word(a1lst, i + 1), letenv_idx)\n          if v == \"ERR\" then return \"ERR\"\n          unused = env_set(letenv_idx, k, v)\n        end\n        env_idx = letenv_idx\n        ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"quote\" then return word(astval, 2)\n      when a0sym == \"quasiquote\" then do\n        ast = quasiquote(word(astval, 2))\n        /* TCO */\n      end\n      when a0sym == \"defmacro!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, func_mark_as_macro(a2))\n      end\n      when a0sym == \"try*\" then do\n        res = eval(word(astval, 2), env_idx)\n        if words(astval) < 3 then return res\n        if res == \"ERR\" then do\n          if word(err, 1) == \"__MAL_EXCEPTION__\" then\n            errobj = word(err, 2)\n          else\n            errobj = new_string(err)\n          catchlst = obj_val(word(astval, 3))\n          catch_env_idx = new_env(env_idx, new_list(word(catchlst, 2)), new_list(errobj))\n          err = \"\"\n          return eval(word(catchlst, 3), catch_env_idx)\n        end\n        else\n          return res\n      end\n      when a0sym == \"do\" then do\n        do i=2 to (words(astval) - 1)\n          res = eval(word(astval, i), env_idx)\n          if res == \"ERR\" then return \"ERR\"\n        end\n        ast = word(astval, words(astval))\n        /* TCO */\n      end\n      when a0sym == \"if\" then do\n        condval = eval(word(astval, 2), env_idx)\n        if false?(condval) | nil?(condval) then\n          if words(astval) >= 4 then\n            ast = word(astval, 4)\n          else\n            return new_nil()\n        else\n          ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n      otherwise\n       f = eval(a0, env_idx)\n       if f == \"ERR\" then return \"ERR\"\n\n       if func_macro?(f) then do\n         call_args = mal_rest(ast)\n         mac_env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n         ast = eval(func_body_ast(f), mac_env_idx)\n         /* TCO */\n       end\n       else do\n\n        --  Evaluate the arguments and store them to lst.\n        lst = \"\"\n        do i=2 to words(astval)\n          element = eval(word(astval, i), env_idx)\n          if element == \"ERR\" then return \"ERR\"\n          if i > 2 then\n            lst = lst || \" \" || element\n          else\n            lst = element\n        end\n\n        select\n          when nativefn?(f) then do\n            call_args = lst\n            call_list = \"\"\n            do i=1 to words(call_args)\n              element = '\"' || word(call_args, i) || '\"'\n              if i > 1 then\n                call_list = call_list || ', ' || element\n              else\n                call_list = element\n            end\n            res = \"\"\n            interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n            return res\n          end\n          when func?(f) then do\n            call_args = new_list(lst)\n            env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n            ast = func_body_ast(f)\n            /* TCO */\n          end\n          otherwise\n            err = \"Unsupported function object type: \" || obj_type(f)\n            return \"ERR\"\n          end\n       end\n      end\n  end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_eval: procedure expose values. env. err /* mal_eval(ast) */\n  ast = arg(1)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(arg(1), 1) /* repl_env_idx is always 1 because it's the first env */\n\nbuild_args_list: procedure expose values. command_line_args. /* build_args_list() */\n  seq = \"\"\n  do i=2 to command_line_args.0\n    s = new_string(command_line_args.i)\n    if i == 1 then\n      seq = s\n    else\n      seq = seq || \" \" || s\n  end\n  return new_list(seq)\n\nmain:\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n  x = env_set(repl_env_idx, \"eval\", new_nativefn(\"mal_eval\"))\n  x = env_set(repl_env_idx, \"*ARGV*\", build_args_list())\n\n  /* core.mal: defined using the language itself */\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n  x = re('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n  x = re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \" || '\"' || \"odd number of forms to cond\" || '\"' || \")) (cons 'cond (rest (rest xs)))))))\");\n\n  err = \"\"\n  if command_line_args.0 > 0 then do\n    x = re('(load-file \"' || command_line_args.1 || '\")')\n    return\n  end\n\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then do\n        if word(err, 1) == \"__MAL_EXCEPTION__\" then\n          errstr = pr_str(word(err, 2), 0)\n        else\n          errstr = err\n        call lineout , \"Error: \" || errstr\n        err = \"\"\n      end\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/stepA_mal.rexx",
    "content": "/* Save command-line arguments from the top-level program before entering a procedure */\ncommand_line_args. = \"\"\ncommand_line_args.0 = arg()\ndo i=1 to command_line_args.0\n  command_line_args.i = arg(i)\nend\n\ncall main\nexit\n\n#include \"readline.rexx\"\n#include \"reader.rexx\"\n#include \"printer.rexx\"\n#include \"types.rexx\"\n#include \"env.rexx\"\n#include \"core.rexx\"\n\nread: procedure expose values. err /* read(str) */\n  return read_str(arg(1))\n\nstarts_with?: procedure expose values. /* starts_with?(lst, sym) */\n  lst = arg(1)\n  sym = arg(2)\n  if words(obj_val(lst)) <> 2 then return 0\n  a0 = word(obj_val(lst), 1)\n  return symbol?(a0) & obj_val(a0) == sym\n\nqq_loop: procedure expose values. /* qq_loop(elt, acc) */\n  elt = arg(1)\n  acc = arg(2)\n  if list?(elt) & starts_with?(elt, \"splice-unquote\") then\n    return new_list(new_symbol(\"concat\") || \" \" || word(obj_val(elt), 2) || \" \" || acc)\n  else\n    return new_list(new_symbol(\"cons\") || \" \" || quasiquote(elt) || \" \" || acc)\n\nqq_foldr: procedure expose values. /* qq_foldr(xs) */\n  xs = arg(1)\n  acc = new_list()\n  do i=words(xs) to 1 by -1\n    acc = qq_loop(word(xs, i), acc)\n  end\n  return acc\n\nquasiquote: procedure expose values. env. err /* quasiquote(ast) */\n  ast = arg(1)\n  type = obj_type(ast)\n  select\n    when type == \"list\" then\n        if starts_with?(ast, \"unquote\") then\n          return word(obj_val(ast), 2)\n        else\n          return qq_foldr(obj_val(ast))\n    when type == \"vect\" then\n      return new_list(new_symbol(\"vec\") || \" \" || qq_foldr(obj_val(ast)))\n    when type == \"symb\" | type == \"hash\" then\n      return new_list(new_symbol(\"quote\") || \" \" || ast)\n    otherwise\n      return ast\n    end\n\neval: procedure expose values. env. err /* eval(ast) */\n  ast = arg(1)\n  env_idx = arg(2)\n  do forever\n\n  debug_eval = obj_type(env_get(env_idx, \"DEBUG-EVAL\"))\n  if  debug_eval <> \"ERR\" & debug_eval <> \"nill\" & debug_eval <> \"fals\" then,\n    call lineout , (\"EVAL: \" || print(ast))\n\n  type = obj_type(ast)\n  astval = obj_val(ast)\n  select\n    when type == \"symb\" then return env_get(env_idx, astval)\n    when type == \"list\" & words(astval) > 0 then do\n      --  proceed after this select statement\n    end\n    when type == \"vect\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_vector(res)\n    end\n    when type == \"hash\" then do\n      res = \"\"\n      do i=1 to words(astval)\n        element = eval(word(astval, i), env_idx)\n        if element == \"ERR\" then return \"ERR\"\n        if i > 1 then\n          res = res || \" \" || element\n        else\n          res = element\n      end\n      return new_hashmap(res)\n    end\n    otherwise\n      return ast\n    end\n\n    --  ast is a non-empty list\n\n    a0 = word(astval, 1)\n    a0sym = obj_val(a0)\n    select\n      when a0sym == \"def!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, a2)\n      end\n      when a0sym == \"let*\" then do\n        a1lst = obj_val(word(astval, 2))\n        letenv_idx = new_env(env_idx)\n        do i=1 to words(a1lst) by 2\n          k = obj_val(word(a1lst, i))\n          v = eval(word(a1lst, i + 1), letenv_idx)\n          if v == \"ERR\" then return \"ERR\"\n          unused = env_set(letenv_idx, k, v)\n        end\n        env_idx = letenv_idx\n        ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"quote\" then return word(astval, 2)\n      when a0sym == \"quasiquote\" then do\n        ast = quasiquote(word(astval, 2))\n        /* TCO */\n      end\n      when a0sym == \"defmacro!\" then do\n        a1sym = obj_val(word(astval, 2))\n        a2 = eval(word(astval, 3), env_idx)\n        if a2 == \"ERR\" then return \"ERR\"\n        return env_set(env_idx, a1sym, func_mark_as_macro(a2))\n      end\n      when a0sym == \"try*\" then do\n        res = eval(word(astval, 2), env_idx)\n        if words(astval) < 3 then return res\n        if res == \"ERR\" then do\n          if word(err, 1) == \"__MAL_EXCEPTION__\" then\n            errobj = word(err, 2)\n          else\n            errobj = new_string(err)\n          catchlst = obj_val(word(astval, 3))\n          catch_env_idx = new_env(env_idx, new_list(word(catchlst, 2)), new_list(errobj))\n          err = \"\"\n          return eval(word(catchlst, 3), catch_env_idx)\n        end\n        else\n          return res\n      end\n      when a0sym == \"do\" then do\n        do i=2 to (words(astval) - 1)\n          res = eval(word(astval, i), env_idx)\n          if res == \"ERR\" then return \"ERR\"\n        end\n        ast = word(astval, words(astval))\n        /* TCO */\n      end\n      when a0sym == \"if\" then do\n        condval = eval(word(astval, 2), env_idx)\n        if false?(condval) | nil?(condval) then\n          if words(astval) >= 4 then\n            ast = word(astval, 4)\n          else\n            return new_nil()\n        else\n          ast = word(astval, 3)\n        /* TCO */\n      end\n      when a0sym == \"fn*\" then return new_func(word(astval, 3), env_idx, word(astval, 2))\n      otherwise\n       f = eval(a0, env_idx)\n       if f == \"ERR\" then return \"ERR\"\n\n       if func_macro?(f) then do\n         call_args = mal_rest(ast)\n         mac_env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n         ast = eval(func_body_ast(f), mac_env_idx)\n         /* TCO */\n       end\n       else do\n\n        --  Evaluate the arguments and store them to lst.\n        lst = \"\"\n        do i=2 to words(astval)\n          element = eval(word(astval, i), env_idx)\n          if element == \"ERR\" then return \"ERR\"\n          if i > 2 then\n            lst = lst || \" \" || element\n          else\n            lst = element\n        end\n\n        select\n          when nativefn?(f) then do\n            call_args = lst\n            call_list = \"\"\n            do i=1 to words(call_args)\n              element = '\"' || word(call_args, i) || '\"'\n              if i > 1 then\n                call_list = call_list || ', ' || element\n              else\n                call_list = element\n            end\n            res = \"\"\n            interpret \"res = \" || obj_val(f) || \"(\" || call_list || \")\"\n            return res\n          end\n          when func?(f) then do\n            call_args = new_list(lst)\n            env_idx = new_env(func_env_idx(f), func_binds(f), call_args)\n            ast = func_body_ast(f)\n            /* TCO */\n          end\n          otherwise\n            err = \"Unsupported function object type: \" || obj_type(f)\n            return \"ERR\"\n          end\n       end\n      end\n  end\n\nprint: procedure expose values. /* print(ast) */\n  return pr_str(arg(1), 1)\n\nre:  procedure expose values. env. err repl_env_idx /* re(str) */\n  str = arg(1)\n  ast = read(str)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(ast, repl_env_idx)\n\nrep: procedure expose values. env. err repl_env_idx /* rep(str) */\n  str = arg(1)\n  exp = re(str)\n  if exp == \"ERR\" then return \"ERR\"\n  return print(exp)\n\nmal_eval: procedure expose values. env. err /* mal_eval(ast) */\n  ast = arg(1)\n  if ast == \"ERR\" then return \"ERR\"\n  return eval(arg(1), 1) /* repl_env_idx is always 1 because it's the first env */\n\nbuild_args_list: procedure expose values. command_line_args. /* build_args_list() */\n  seq = \"\"\n  do i=2 to command_line_args.0\n    s = new_string(command_line_args.i)\n    if i == 1 then\n      seq = s\n    else\n      seq = seq || \" \" || s\n  end\n  return new_list(seq)\n\nmain:\n  x = time('R') /* Reset the internal stopwatch; used by `time-ms` */\n  values. = \"\"\n  values.0 = 0\n  env. = \"\"\n  env.0 = 0\n  repl_env_idx = new_env(0)\n\n  /* core.rexx: defined using Rexx */\n  core_ns = get_core_ns()\n  do i=1 to words(core_ns) by 2\n    x = env_set(repl_env_idx, word(core_ns, i), new_nativefn(word(core_ns, i + 1)))\n  end\n  x = env_set(repl_env_idx, \"eval\", new_nativefn(\"mal_eval\"))\n  x = env_set(repl_env_idx, \"*ARGV*\", build_args_list())\n\n  /* core.mal: defined using the language itself */\n  x = re('(def! *host-language* \"rexx\")')\n  x = re(\"(def! not (fn* (a) (if a false true)))\")\n  x = re('(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))')\n  x = re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \" || '\"' || \"odd number of forms to cond\" || '\"' || \")) (cons 'cond (rest (rest xs)))))))\");\n\n  err = \"\"\n  if command_line_args.0 > 0 then do\n    x = re('(load-file \"' || command_line_args.1 || '\")')\n    return\n  end\n\n  x = re('(println (str \"Mal [\" *host-language* \"]\"))')\n  do while lines() > 0 /* 1 == 1 */\n    input_line = readline('user> ')\n    if length(input_line) > 0 then do\n      res = rep(input_line)\n      if res == \"ERR\" then do\n        if word(err, 1) == \"__MAL_EXCEPTION__\" then\n          errstr = pr_str(word(err, 2), 0)\n        else\n          errstr = err\n        call lineout , \"Error: \" || errstr\n        err = \"\"\n      end\n      else\n        call lineout , res\n    end\n  end\n"
  },
  {
    "path": "impls/rexx/tests/step5_tco.mal",
    "content": ";; REXX: skipping non-TCO recursion\n;; Reason: regina rexx interpreter segfaults (unrecoverable)\n"
  },
  {
    "path": "impls/rexx/tests/stepA_mal.mal",
    "content": ";; Testing basic Rexx interop\n;;\n;; Note that in Rexx \"everything is a string\". Numeric outputs are converted to\n;; Mal numbers.\n\n(rexx-eval \"3 ** 4\")\n;=>81\n\n(rexx-eval \"words('a bb ' || 'ccc dddd')\")\n;=>4\n\n(rexx-eval \"d2x(254)\")\n;=>\"FE\"\n\n(rexx-eval \"say 'hello' 12.34 upper('rexx')\" nil)\n;/hello 12.34 REXX\n;=>nil\n\n(rexx-eval \"foo = 8\" \"foo + 3\")\n;=>11\n\n(rexx-eval \"parse version s1 s2 s3 s4 s5\" \"'rexx_version=' || s2\")\n;=>\"rexx_version=5.00\"\n"
  },
  {
    "path": "impls/rexx/types.rexx",
    "content": "#ifndef __types__\n#define __types__\n\nvalues. = \"\"\nvalues.0 = 0\n\nnew_value_index: procedure expose values. /* new_value_index() */\n  values.0 = values.0 + 1\n  return values.0\n\nobj_type: procedure /* obj_type(obj) */\n  obj = arg(1)\n  return left(obj, 4)\n\nobj_val: procedure expose values. /* obj_val(obj) */\n  obj = arg(1)\n  type = obj_type(obj)\n  val = substr(obj, 6)\n  select\n    when type == \"numb\" | type == \"nill\" | type == \"true\" | type == \"fals\" then return val\n    otherwise\n      return values.val\n    end\n\nobj_meta: procedure expose values. /* obj_meta(obj) */\n  obj = arg(1)\n  type = obj_type(obj)\n  if type == \"numb\" | type == \"nill\" | type == \"true\" | type == \"fals\" then return \"\"\n  ind = substr(obj, 6)\n  return values.meta.ind\n\nobj_clone_and_set_meta: procedure expose values. /* obj_clone_and_set_meta(obj, new_meta) */\n  obj = arg(1)\n  new_meta = arg(2)\n  type = obj_type(obj)\n  if type == \"numb\" | type == \"nill\" | type == \"true\" | type == \"fals\" then return \"\"\n  orig_ind = substr(obj, 6)\n  new_idx = new_value_index()\n  values.new_idx = values.orig_ind\n  values.meta.new_idx = new_meta\n  return type || \"_\" || new_idx\n\nnew_number: procedure /* new_number(n) */\n  n = arg(1)\n  return \"numb_\" || n\n\nnumber?: procedure /* number?(obj) */\n  return obj_type(arg(1)) == \"numb\"\n\nnew_nil: procedure /* new_nil() */\n  return \"nill_0\"\n\nnil?: procedure /* nil?(obj) */\n  return obj_type(arg(1)) == \"nill\"\n\nnew_true: procedure /* new_true() */\n  return \"true_0\"\n\ntrue?: procedure /* true?(obj) */\n  return obj_type(arg(1)) == \"true\"\n\nnew_false: procedure /* new_false() */\n  return \"fals_0\"\n\nfalse?: procedure /* false?(obj) */\n  return obj_type(arg(1)) == \"fals\"\n\nnew_boolean: procedure /* new_boolean(cond) */\n  if arg(1) then\n    return new_true()\n  else\n    return new_false()\n\nnew_symbol: procedure expose values. /* new_symbol(str) */\n  str = arg(1)\n  idx = new_value_index()\n  values.idx = str\n  return \"symb_\" || idx\n\nsymbol?: procedure /* symbol?(obj) */\n  return obj_type(arg(1)) == \"symb\"\n\nnew_string: procedure expose values. /* new_string(str) */\n  str = arg(1)\n  idx = new_value_index()\n  values.idx = str\n  return \"stri_\" || idx\n\nstring?: procedure /* string?(obj) */\n  return obj_type(arg(1)) == \"stri\"\n\nnew_keyword: procedure expose values. /* new_keyword(str) */\n  str = arg(1)\n  idx = new_value_index()\n  values.idx = str\n  return \"keyw_\" || idx\n\nkeyword?: procedure /* keyword?(obj) */\n  return obj_type(arg(1)) == \"keyw\"\n\nnew_seq: procedure expose values. /* new_seq(type, seq) */\n  type = arg(1)\n  seq = arg(2)\n  idx = new_value_index()\n  values.idx = seq\n  return type || \"_\" || idx\n\nnew_list: procedure expose values. /* new_list(seq) */\n  seq = arg(1)\n  return new_seq(\"list\", seq)\n\nlist?: procedure /* list?(obj) */\n  return obj_type(arg(1)) == \"list\"\n\nnew_vector: procedure expose values. /* new_vector(seq) */\n  seq = arg(1)\n  return new_seq(\"vect\", seq)\n\nvector?: procedure /* vector?(obj) */\n  return obj_type(arg(1)) == \"vect\"\n\nsequential?: procedure /* sequential?(obj) */\n  return (list?(arg(1)) | vector?(arg(1)))\n\ncount_elements: procedure expose values. /* count_elements(lst) */\n  return words(obj_val(arg(1)))\n\nnew_hashmap: procedure expose values. /* new_hashmap(seq) */\n  seq = arg(1)\n  return new_seq(\"hash\", seq)\n\nhashmap?: procedure /* hashmap?(obj) */\n  return obj_type(arg(1)) == \"hash\"\n\ncontains?: procedure expose values. /* contains?(hm_val, key) */\n  hm_val = arg(1)\n  key = arg(2)\n  do i=1 to words(hm_val) by 2\n    if equal?(key, word(hm_val, i)) then return 1\n  end\n  return 0\n\nhashmap_get: procedure expose values. /* hashmap_get(hm_val, key) */\n  hm_val = arg(1)\n  key = arg(2)\n  do i=1 to words(hm_val) by 2\n    if equal?(key, word(hm_val, i)) then return word(hm_val, i + 1)\n  end\n  return \"\"\n\nnew_nativefn: procedure expose values. /* new_hashmap(native_func_name) */\n  native_func_name = arg(1)\n  idx = new_value_index()\n  values.idx = native_func_name\n  return \"nafn_\" || idx\n\nnativefn?: procedure /* nativefn?(obj) */\n  return obj_type(arg(1)) == \"nafn\"\n\nnew_func: procedure expose values. /* new_func(body_ast, env_idx, binds) */\n  body_ast = arg(1)\n  env_idx = arg(2)\n  binds = arg(3)\n  is_macro = 0\n  idx = new_value_index()\n  values.idx = body_ast env_idx binds is_macro\n  return \"func_\" || idx\n\nfunc?: procedure /* func?(obj) */\n  return obj_type(arg(1)) == \"func\"\n\nfunc_macro?: procedure expose values. /* func_macro?(obj) */\n  return func?(arg(1)) & (func_is_macro(arg(1)) == 1)\n\nfunc_body_ast: procedure expose values. /* func_body_ast(func_obj) */\n  return word(obj_val(arg(1)), 1)\n\nfunc_env_idx: procedure expose values. /* func_env_idx(func_obj) */\n  return word(obj_val(arg(1)), 2)\n\nfunc_binds: procedure expose values. /* func_binds(func_obj) */\n  return word(obj_val(arg(1)), 3)\n\nfunc_is_macro: procedure expose values. /* func_is_macro(func_obj) */\n  return word(obj_val(arg(1)), 4)\n\nfunc_mark_as_macro: procedure expose values. /* func_mark_as_macro(func_obj) */\n  body_ast = func_body_ast(arg(1))\n  env_idx = func_env_idx(arg(1))\n  binds = func_binds(arg(1))\n  is_macro = 1\n  idx = new_value_index()\n  values.idx = body_ast env_idx binds is_macro\n  return \"func_\" || idx\n\nnew_atom: procedure expose values. /* new_atom(obj) */\n  obj = arg(1)\n  idx = new_value_index()\n  values.idx = obj\n  return \"atom_\" || idx\n\natom?: procedure /* atom?(obj) */\n  return obj_type(arg(1)) == \"atom\"\n\natom_set: procedure expose values. /* atom_set(atom, new_value) */\n  atom = arg(1)\n  new_value = arg(2)\n  idx = substr(atom, 6)\n  values.idx = new_value\n  return new_value\n\nequal_hashmap?:  procedure expose values. /* equal_hashmap?(a, b) */\n  hma_val = obj_val(arg(1))\n  hmb_val = obj_val(arg(2))\n  if words(hma_val) \\= words(hmb_val) then return 0\n  do i=1 to words(hma_val) by 2\n    a_key = word(hma_val, i)\n    a_val = word(hma_val, i + 1)\n    b_val = hashmap_get(hmb_val, a_key)\n    if b_val == \"\" then return 0\n    if \\equal?(a_val, b_val) then return 0\n  end\n  return 1\n\nequal_sequential?: procedure expose values. /* equal_sequential?(a, b) */\n  a_val = obj_val(arg(1))\n  b_val = obj_val(arg(2))\n  if words(a_val) \\= words(b_val) then return 0\n  do i=1 to words(a_val)\n    if \\equal?(word(a_val, i), word(b_val, i)) then return 0\n  end\n  return 1\n\nequal?: procedure expose values. /* equal?(a, b) */\n  a = arg(1)\n  b = arg(2)\n  a_type = obj_type(a)\n  b_type = obj_type(b)\n  a_val = obj_val(a)\n  b_val = obj_val(b)\n  select\n    when nil?(a) then return nil?(b)\n    when true?(a) then return true?(b)\n    when false?(a) then return false?(b)\n    when (a_type == \"numb\" & b_type = \"numb\") | ,\n         (a_type == \"symb\" & b_type = \"symb\") | ,\n         (a_type == \"stri\" & b_type = \"stri\") | ,\n         (a_type == \"keyw\" & b_type = \"keyw\") then return (obj_val(a) == obj_val(b))\n    when (sequential?(a) & sequential?(b)) then return equal_sequential?(a, b)\n    when (hashmap?(a) & hashmap?(b)) then return equal_hashmap?(a, b)\n    otherwise\n      return 0\n    end\n\n#endif\n"
  },
  {
    "path": "impls/rpython/Dockerfile",
    "content": "FROM ubuntu:vivid\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# For building rpython\nRUN apt-get -y install g++\n\n# pypy\nRUN apt-get -y install software-properties-common\nRUN add-apt-repository ppa:pypy\nRUN apt-get -y update\nRUN apt-get -y install pypy\n\n# rpython\nRUN apt-get -y install mercurial libffi-dev pkg-config libz-dev libbz2-dev \\\n    libsqlite3-dev libncurses-dev libexpat1-dev libssl-dev libgdbm-dev tcl-dev\n\nRUN mkdir -p /opt/pypy && \\\n    curl -L https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.6.0-src.tar.bz2 | tar -xjf - -C /opt/pypy/ --strip-components=1\n    #curl https://bitbucket.org/pypy/pypy/get/tip.tar.gz | tar -xzf - -C /opt/pypy/ --strip-components=1\nRUN cd /opt/pypy && make && rm -rf /tmp/usession*\n\nRUN ln -sf /opt/pypy/rpython/bin/rpython /usr/local/bin/rpython\nRUN ln -sf /opt/pypy/pypy-c /usr/local/bin/pypy\nRUN chmod -R ugo+rw /opt/pypy/rpython/_cache\n\nRUN apt-get -y autoremove pypy\n\n"
  },
  {
    "path": "impls/rpython/Makefile",
    "content": "\nRPYTHON = rpython\n\nUPPER_STEPS = step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\nSTEPS = step0_repl step1_read_print step2_eval step3_env $(UPPER_STEPS)\n\nall: $(STEPS)\n\ndist: mal\n\nmal: stepA_mal\n\tcp $< $@\n\n%: %.py\n\t$(RPYTHON) --output=$@ $<\n\nSTEP0_DEPS = mal_readline.py\nSTEP1_DEPS = $(STEP0_DEPS) mal_types.py reader.py printer.py\nSTEP3_DEPS = $(STEP1_DEPS) env.py\nSTEP4_DEPS = $(STEP3_DEPS) core.py\n\nstep0_repl: $(STEP0_DEPS)\nstep1_read_print step2_eval: $(STEP1_DEPS)\nstep3_env: $(STEP3_DEPS)\n$(UPPER_STEPS): $(STEP4_DEPS)\n\n.PHONY: clean\n\nclean:\n\trm -f mal $(STEPS) *.pyc\n\trm -rf __pycache__\n\n"
  },
  {
    "path": "impls/rpython/core.py",
    "content": "#import copy, time\nimport time\n\nimport mal_types as types\nfrom mal_types import (throw_str,\n                       MalType, MalMeta, nil, true, false,\n                       MalInt, MalSym, MalStr,\n                       MalList, MalVector, MalHashMap,\n                       MalAtom, MalFunc)\nimport mal_readline\nimport reader\nimport printer\n\n# General functions\ndef wrap_tf(tf):\n    if tf: return true\n    else:  return false\n\ndef do_equal(args): return wrap_tf(types._equal_Q(args[0], args[1]))\n\n# Errors/Exceptions\ndef throw(args):\n    raise types.MalException(args[0])\n\n# Scalar functions\ndef nil_Q(args): return wrap_tf(types._nil_Q(args[0]))\ndef true_Q(args): return wrap_tf(types._true_Q(args[0]))\ndef false_Q(args): return wrap_tf(types._false_Q(args[0]))\ndef string_Q(args): return wrap_tf(types._string_Q(args[0]))\ndef symbol(args):\n    a0 = args[0]\n    if isinstance(a0, MalStr):\n        return types._symbol(a0.value)\n    elif isinstance(a0, MalSym):\n        return a0\n    else:\n        throw_str(\"symbol called on non-string/non-symbol\")\ndef symbol_Q(args): return wrap_tf(types._symbol_Q(args[0]))\ndef keyword(args): return types._keyword(args[0])\ndef keyword_Q(args): return wrap_tf(types._keyword_Q(args[0]))\ndef number_Q(args): return wrap_tf(types._int_Q(args[0]))\ndef function_Q(args): return wrap_tf(types._function_Q(args[0]) and not args[0].ismacro)\ndef macro_Q(args): return wrap_tf(types._function_Q(args[0]) and args[0].ismacro)\n\n\n# String functions\ndef pr_str(args):\n    parts = []\n    for exp in args.values: parts.append(printer._pr_str(exp, True))\n    return MalStr(u\" \".join(parts))\n\ndef do_str(args):\n    parts = []\n    for exp in args.values: parts.append(printer._pr_str(exp, False))\n    return MalStr(u\"\".join(parts))\n\ndef prn(args):\n    parts = []\n    for exp in args.values: parts.append(printer._pr_str(exp, True))\n    print(u\" \".join(parts))\n    return nil\n\ndef println(args):\n    parts = []\n    for exp in args.values: parts.append(printer._pr_str(exp, False))\n    print(u\" \".join(parts))\n    return nil\n\ndef do_readline(args):\n    prompt = args[0]\n    if not isinstance(prompt, MalStr):\n        throw_str(\"readline prompt is not a string\")\n    try:\n        return MalStr(unicode(mal_readline.readline(str(prompt.value))))\n    except EOFError:\n        return nil\n\ndef read_str(args):\n    a0 = args[0]\n    if not isinstance(a0, MalStr):\n        throw_str(\"read-string of non-string\")\n    return reader.read_str(str(a0.value))\n\ndef slurp(args):\n    a0 = args[0]\n    if not isinstance(a0, MalStr):\n        throw_str(\"slurp with non-string filename\")\n    return MalStr(unicode(open(str(a0.value)).read()))\n\n# Number functions\ndef lt(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"< called on non-integer\")\n    return wrap_tf(a.value < b.value)\ndef lte(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"<= called on non-integer\")\n    return wrap_tf(a.value <= b.value)\ndef gt(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"> called on non-integer\")\n    return wrap_tf(a.value > b.value)\ndef gte(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\">= called on non-integer\")\n    return wrap_tf(a.value >= b.value)\n\ndef plus(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"+ called on non-integer\")\n    return MalInt(a.value+b.value)\ndef minus(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"- called on non-integer\")\n    return MalInt(a.value-b.value)\ndef multiply(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"* called on non-integer\")\n    return MalInt(a.value*b.value)\ndef divide(args):\n    a, b = args[0], args[1]\n    if not isinstance(a, MalInt) or not isinstance(b, MalInt):\n        throw_str(\"/ called on non-integer\")\n    if b.value == 0:\n        throw_str(\"divide by zero\")\n    return MalInt(int(a.value/b.value))\n\ndef time_ms(args):\n    return MalInt(int(time.time() * 1000))\n\n\n# Hash map functions\ndef do_hash_map(ml):\n    return types._hash_mapl(ml.values)\n\ndef hash_map_Q(args):\n    return wrap_tf(types._hash_map_Q(args[0]))\n\ndef assoc(args):\n    src_hm, key_vals = args[0], args.rest()\n    new_dct = src_hm.dct.copy()\n    for i in range(0,len(key_vals),2):\n        k = key_vals[i]\n        if not isinstance(k, MalStr):\n            throw_str(\"assoc called with non-string/non-keyword key\")\n        new_dct[k.value] = key_vals[i+1]\n    return MalHashMap(new_dct)\n\ndef dissoc(args):\n    src_hm, keys = args[0], args.rest()\n    new_dct = src_hm.dct.copy()\n    for k in keys.values:\n        if not isinstance(k, MalStr):\n            throw_str(\"dissoc called with non-string/non-keyword key\")\n        if k.value in new_dct:\n            del new_dct[k.value]\n    return MalHashMap(new_dct)\n\ndef get(args):\n    obj, key = args[0], args[1]\n    if obj is nil:\n        return nil\n    elif isinstance(obj, MalHashMap):\n        if not isinstance(key, MalStr):\n            throw_str(\"get called on hash-map with non-string/non-keyword key\")\n        if obj and key.value in obj.dct:\n            return obj.dct[key.value]\n        else:\n            return nil\n    elif isinstance(obj, MalList):\n        if not isinstance(key, MalInt):\n            throw_str(\"get called on list/vector with non-string/non-keyword key\")\n        return obj.values[key.value]\n    else:\n        throw_str(\"get called on invalid type\")\n\ndef contains_Q(args):\n    hm, key = args[0], args[1]\n    if not isinstance(key, MalStr):\n        throw_str(\"contains? called on hash-map with non-string/non-keyword key\")\n    return wrap_tf(key.value in hm.dct)\n\ndef keys(args):\n    hm = args[0]\n    keys = []\n    for k in hm.dct.keys(): keys.append(MalStr(k))\n    return MalList(keys)\n\ndef vals(args):\n    hm = args[0]\n    return MalList(hm.dct.values())\n\n\n# Sequence functions\ndef do_list(ml):\n    return ml\n\ndef list_Q(args):\n    return wrap_tf(types._list_Q(args[0]))\n\ndef do_vector(ml):\n    return MalVector(ml.values)\n\ndef vector_Q(args):\n    return wrap_tf(types._vector_Q(args[0]))\n\ndef empty_Q(args):\n    seq = args[0]\n    if isinstance(seq, MalList):\n        return wrap_tf(len(seq) == 0)\n    elif seq is nil:\n        return true\n    else:\n        throw_str(\"empty? called on non-sequence\")\n\ndef count(args):\n    seq = args[0]\n    if isinstance(seq, MalList):\n        return MalInt(len(seq))\n    elif seq is nil:\n        return MalInt(0)\n    else:\n        throw_str(\"count called on non-sequence\")\n\ndef sequential_Q(args):\n    return wrap_tf(types._sequential_Q(args[0]))\n\ndef vec(args):\n    seq = args[0]\n    if isinstance(seq, MalList):\n        return MalVector(seq.values)\n    else:\n        throw_str(\"vec called on non-sequence\")\n\ndef cons(args):\n    x, seq = args[0], args[1]\n    if not isinstance(seq, MalList):\n        throw_str(\"cons called with non-list/non-vector\")\n    return MalList([x] + seq.values)\n\ndef concat(args):\n    new_lst = []\n    for l in args.values:\n        if not isinstance(l, MalList):\n            throw_str(\"concat called with non-list/non-vector\")\n        new_lst = new_lst + l.values\n    return MalList(new_lst)\n\ndef nth(args):\n    lst, idx = args[0], args[1]\n    if not isinstance(lst, MalList):\n        throw_str(\"nth called with non-list/non-vector\")\n    if not isinstance(idx, MalInt):\n        throw_str(\"nth called with non-int index\")\n    if idx.value < len(lst): return lst[idx.value]\n    else: throw_str(\"nth: index out of range\")\n\ndef first(args):\n    a0 = args[0]\n    if a0 is nil:\n        return nil\n    elif not isinstance(a0, MalList):\n        throw_str(\"first called with non-list/non-vector\")\n    if len(a0) == 0: return nil\n    else:            return a0[0]\n\ndef rest(args):\n    a0 = args[0]\n    if a0 is nil:\n        return MalList([])\n    elif not isinstance(a0, MalList):\n        throw_str(\"rest called with non-list/non-vector\")\n    if len(a0) == 0: return MalList([])\n    else:            return a0.rest()\n\ndef apply(args):\n    f, fargs = args[0], args.rest()\n    last_arg = fargs.values[-1]\n    if not isinstance(last_arg, MalList):\n        throw_str(\"map called with non-list\")\n    all_args = fargs.values[0:-1] + last_arg.values\n    return f.apply(MalList(all_args))\n\ndef mapf(args):\n    f, lst = args[0], args[1]\n    if not isinstance(lst, MalList):\n        throw_str(\"map called with non-list\")\n    res = []\n    for a in lst.values:\n        res.append(f.apply(MalList([a])))\n    return MalList(res)\n\n# retains metadata\ndef conj(args):\n    lst, args = args[0], args.rest()\n    new_lst = None\n    if types._list_Q(lst):\n        vals = args.values[:]\n        vals.reverse()\n        new_lst = MalList(vals + lst.values)\n    elif types._vector_Q(lst):\n        new_lst = MalVector(lst.values + list(args.values))\n    else:\n        throw_str(\"conj on non-list/non-vector\")\n    new_lst.meta = lst.meta\n    return new_lst\n\ndef seq(args):\n    a0 = args[0]\n    if isinstance(a0, MalVector):\n        if len(a0) == 0: return nil\n        return MalList(a0.values)\n    elif isinstance(a0, MalList):\n        if len(a0) == 0: return nil\n        return a0\n    elif types._string_Q(a0):\n        assert isinstance(a0, MalStr)\n        if len(a0) == 0: return nil\n        return MalList([MalStr(unicode(c)) for c in a0.value])\n    elif a0 is nil:\n        return nil\n    else:\n        throw_str(\"seq: called on non-sequence\")\n\n# Metadata functions\ndef with_meta(args):\n    obj, meta = args[0], args[1]\n    if isinstance(obj, MalMeta):\n        new_obj = types._clone(obj)\n        new_obj.meta = meta\n        return new_obj\n    else:\n        throw_str(\"with-meta not supported on type\")\n\ndef meta(args):\n    obj = args[0]\n    if isinstance(obj, MalMeta):\n        return obj.meta\n    else:\n        throw_str(\"meta not supported on type\")\n\n\n# Atoms functions\ndef do_atom(args):\n    return MalAtom(args[0])\ndef atom_Q(args):\n    return wrap_tf(types._atom_Q(args[0]))\ndef deref(args):\n    atm = args[0]\n    if not isinstance(atm, MalAtom):\n        throw_str(\"deref called on non-atom\")\n    return atm.value\ndef reset_BANG(args):\n    atm, val = args[0], args[1]\n    if not isinstance(atm, MalAtom):\n        throw_str(\"reset! called on non-atom\")\n    atm.value = val\n    return atm.value\ndef swap_BANG(args):\n    atm, f, fargs = args[0], args[1], args.slice(2)\n    if not isinstance(atm, MalAtom):\n        throw_str(\"swap! called on non-atom\")\n    if not isinstance(f, MalFunc):\n        throw_str(\"swap! called with non-function\")\n    all_args = [atm.value] + fargs.values\n    atm.value = f.apply(MalList(all_args))\n    return atm.value\n\n\nns = {\n        '=': do_equal,\n        'throw': throw,\n        'nil?': nil_Q,\n        'true?': true_Q,\n        'false?': false_Q,\n        'string?': string_Q,\n        'symbol': symbol,\n        'symbol?': symbol_Q,\n        'keyword': keyword,\n        'keyword?': keyword_Q,\n        'number?': number_Q,\n        'fn?': function_Q,\n        'macro?': macro_Q,\n\n        'pr-str': pr_str,\n        'str': do_str,\n        'prn': prn,\n        'println': println,\n        'readline': do_readline,\n        'read-string': read_str,\n        'slurp': slurp,\n        '<':  lt,\n        '<=': lte,\n        '>':  gt,\n        '>=': gte,\n        '+':  plus,\n        '-':  minus,\n        '*':  multiply,\n        '/':  divide,\n        'time-ms': time_ms,\n\n        'list': do_list,\n        'list?': list_Q,\n        'vector': do_vector,\n        'vector?': vector_Q,\n        'hash-map': do_hash_map,\n        'map?': hash_map_Q,\n        'assoc': assoc,\n        'dissoc': dissoc,\n        'get': get,\n        'contains?': contains_Q,\n        'keys': keys,\n        'vals': vals,\n\n        'sequential?': sequential_Q,\n        'vec': vec,\n        'cons': cons,\n        'concat': concat,\n        'nth': nth,\n        'first': first,\n        'rest': rest,\n        'empty?': empty_Q,\n        'count': count,\n        'apply': apply,\n        'map': mapf,\n\n        'conj': conj,\n        'seq': seq,\n\n        'with-meta': with_meta,\n        'meta': meta,\n        'atom': do_atom,\n        'atom?': atom_Q,\n        'deref': deref,\n        'reset!': reset_BANG,\n        'swap!': swap_BANG\n    }\n\n"
  },
  {
    "path": "impls/rpython/env.py",
    "content": "from mal_types import MalType, MalSym, MalList, throw_str\n\n# Environment\nclass Env():\n    def __init__(self, outer=None, binds=None, exprs=None):\n        self.data = {}\n        self.outer = outer or None\n\n        if binds:\n            assert isinstance(binds, MalList) and isinstance(exprs, MalList)\n            for i in range(len(binds)):\n                bind = binds[i]\n                if not isinstance(bind, MalSym):\n                    throw_str(\"env bind value is not a symbol\")\n                if bind.value == u\"&\":\n                    bind = binds[i+1]\n                    if not isinstance(bind, MalSym):\n                        throw_str(\"env bind value is not a symbol\")\n                    self.data[bind.value] = exprs.slice(i)\n                    break\n                else:\n                    self.data[bind.value] = exprs[i]\n\n    def set(self, key, value):\n        assert isinstance(key, MalSym)\n        assert isinstance(value, MalType)\n        self.data[key.value] = value\n        return value\n\n    def get(self, key):\n        assert isinstance(key, unicode)\n        env = self\n        while True:\n            value = env.data.get(key, None)\n            if value is not None: return value\n            env = env.outer\n            if env is None: return None\n"
  },
  {
    "path": "impls/rpython/mal_readline.py",
    "content": "#import os, readline as pyreadline\n#\n#histfile = os.path.expanduser(\"~/.mal-history\")\n#\n#def init():\n#    try:\n#        with open(histfile, \"r\") as hf:\n#            for line in hf.readlines():\n#                pyreadline.add_history(line.rstrip(\"\\r\\n\"))\n#                pass\n#    except IOError:\n#        #print(\"Could not open %s\" % histfile)\n#        pass\n#\n#def readline(prompt=\"user> \"):\n#    try:\n#        line = raw_input(prompt)\n#        pyreadline.add_history(line)\n#        with open(histfile, \"a\") as hf:\n#            hf.write(line + \"\\n\")\n#    except IOError:\n#        pass\n#    except EOFError:\n#        return None\n#    return line\n\nimport os\ndef readline(prompt):\n    res = ''\n    os.write(1, prompt)\n    while True:\n        buf = os.read(0, 255)\n        if not buf: raise EOFError()\n        res += buf\n        if res[-1] == '\\n': return res[:-1]\n\n"
  },
  {
    "path": "impls/rpython/mal_types.py",
    "content": "import sys, copy, types as pytypes\nIS_RPYTHON = sys.argv[0].endswith('rpython')\n\nif IS_RPYTHON:\n    from rpython.rlib.listsort import TimSort\nelse:\n    import re\n\n# General functions\n\nclass StringSort(TimSort):\n    def lt(self, a, b):\n        assert isinstance(a, unicode)\n        assert isinstance(b, unicode)\n        return a < b\n\ndef _equal_Q(a, b):\n    assert isinstance(a, MalType) and isinstance(b, MalType)\n    ota, otb = a.__class__, b.__class__\n    if not (ota is otb or (_sequential_Q(a) and _sequential_Q(b))):\n        return False\n    if isinstance(a, MalSym) and isinstance(b, MalSym):\n        return a.value == b.value\n    elif isinstance(a, MalStr) and isinstance(b, MalStr):\n        return a.value == b.value\n    elif isinstance(a, MalInt) and isinstance(b, MalInt):\n        return a.value == b.value\n    elif _list_Q(a) or _vector_Q(a):\n        if len(a) != len(b): return False\n        for i in range(len(a)):\n            if not _equal_Q(a[i], b[i]): return False\n        return True\n    elif _hash_map_Q(a):\n        assert isinstance(a, MalHashMap)\n        assert isinstance(b, MalHashMap)\n        akeys = a.dct.keys()\n        bkeys = b.dct.keys()\n        if len(akeys) != len(bkeys): return False\n\n        StringSort(akeys).sort()\n        StringSort(bkeys).sort()\n        for i in range(len(akeys)):\n            ak, bk = akeys[i], bkeys[i]\n            assert isinstance(ak, unicode)\n            assert isinstance(bk, unicode)\n            if ak != bk: return False\n            av, bv = a.dct[ak], b.dct[bk]\n            if not _equal_Q(av, bv): return False\n        return True\n    elif a is b:\n        return True\n    else:\n        throw_str(\"no = op defined for %s\" % a.__class__.__name__)\n\ndef _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)\n\ndef _clone(obj):\n    if isinstance(obj, MalFunc):\n        return MalFunc(obj.fn, obj.ast, obj.env, obj.params,\n                 obj.EvalFunc, obj.ismacro)\n    elif isinstance(obj, MalList):\n        return obj.__class__(obj.values)\n    elif isinstance(obj, MalHashMap):\n        return MalHashMap(obj.dct)\n    elif isinstance(obj, MalAtom):\n        return MalAtom(obj.value)\n    else:\n        raise Exception(\"_clone on invalid type\")\n\ndef _replace(match, sub, old_str):\n    new_str = u\"\"\n    idx = 0\n    while idx < len(old_str):\n        midx = old_str.find(match, idx)\n        if midx < 0: break\n        assert midx >= 0 and midx < len(old_str)\n        new_str = new_str + old_str[idx:midx]\n        new_str = new_str + sub\n        idx = midx + len(match)\n    new_str = new_str + old_str[idx:]\n    return new_str\n\n#\n# Mal Types\n#\n\nclass MalException(Exception):\n    def __init__(self, object):\n        self.object = object\n\ndef throw_str(s):\n    raise MalException(MalStr(unicode(s)))\n\n\n### Parent types\nclass MalType(): pass\nclass MalMeta(MalType): pass\n\n### Scalars\nclass MalNil(MalType): pass\nnil = MalNil()\ndef _nil_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp is nil\n\nclass MalTrue(MalType): pass\ntrue = MalTrue()\ndef _true_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp is true\n\nclass MalFalse(MalType): pass\nfalse = MalFalse()\ndef _false_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp is false\n\n# Numbers\nclass MalInt(MalType):\n    def __init__(self, value):\n        assert isinstance(value, int)\n        self.value = value\ndef _int_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__ is MalInt\n\n# String\nclass MalStr(MalType):\n    def __init__(self, value):\n        assert isinstance(value, unicode)\n        self.value = value\n    def __len__(self):\n        return len(self.value)\ndef _string_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__  is MalStr and not _keyword_Q(exp)\n\n# Keywords\n# A specially prefixed string\ndef _keyword(mstr):\n    assert isinstance(mstr, MalType)\n    if isinstance(mstr, MalStr):\n        val = mstr.value\n        if val[0] == u\"\\u029e\": return mstr\n        else:                   return MalStr(u\"\\u029e\" + val)\n    else:\n        throw_str(\"_keyword called on non-string\")\n# Create keyword from unicode string\ndef _keywordu(strn):\n    assert isinstance(strn, unicode)\n    return MalStr(u\"\\u029e\" + strn)\ndef _keyword_Q(exp):\n    if isinstance(exp, MalStr) and len(exp.value) > 0:\n        return exp.value[0] == u\"\\u029e\"\n    else:\n        return False\n\n# Symbols\nclass MalSym(MalMeta):\n    def __init__(self, value):\n        assert isinstance(value, unicode)\n        self.value = value\n        self.meta = nil\ndef _symbol(strn):\n    assert isinstance(strn, unicode)\n    return MalSym(strn)\ndef _symbol_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__ is MalSym\n\n# lists\nclass MalList(MalMeta):\n    def __init__(self, vals):\n        assert isinstance(vals, list)\n        self.values = vals\n        self.meta = nil\n    def append(self, val):\n        self.values.append(val)\n    def rest(self):\n        return MalList(self.values[1:])\n    def __len__(self):\n        return len(self.values)\n    def __getitem__(self, i):\n        assert isinstance(i, int)\n        return self.values[i]\n    def slice(self, start):\n        return MalList(self.values[start:len(self.values)])\n    def slice2(self, start, end):\n        assert end >= 0\n        return MalList(self.values[start:end])\ndef _list(*vals): return MalList(list(vals))\ndef _listl(lst): return MalList(lst)\ndef _list_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__ is MalList\n\n### vectors\nclass MalVector(MalList):\n    pass\ndef _vector(*vals): return MalVector(list(vals))\ndef _vectorl(lst): return MalVector(lst)\ndef _vector_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__ is MalVector\n\n### hash maps\nclass MalHashMap(MalMeta):\n    def __init__(self, dct):\n        self.dct = dct\n        self.meta = nil\n    def append(self, val):\n        self.dct.append(val)\n    def __getitem__(self, k):\n        assert isinstance(k, unicode)\n        if not isinstance(k, unicode):\n            throw_str(\"hash-map lookup by non-string/non-keyword\")\n        return self.dct[k]\n    def __setitem__(self, k, v):\n        if not isinstance(k, unicode):\n            throw_str(\"hash-map key must be string or keyword\")\n        assert isinstance(v, MalType)\n        self.dct[k] = v\n        return v\ndef _hash_mapl(kvs):\n    dct = {}\n    for i in range(0, len(kvs), 2):\n        k = kvs[i]\n        if not isinstance(k, MalStr):\n            throw_str(\"hash-map key must be string or keyword\")\n        v = kvs[i+1]\n        dct[k.value] = v\n    return MalHashMap(dct)\ndef _hash_map_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__ is MalHashMap\n\n# Functions\n# env import must happen after MalSym and MalList definitions to allow\n# circular dependency\nfrom env import Env\nclass MalFunc(MalMeta):\n    def __init__(self, fn, ast=None, env=None, params=None,\n                 EvalFunc=None, ismacro=False):\n        if fn is None and EvalFunc is None:\n            throw_str(\"MalFunc requires either fn or EvalFunc\")\n        self.fn = fn\n        self.ast = ast\n        self.env = env\n        self.params = params\n        self.EvalFunc = EvalFunc\n        self.ismacro = ismacro\n        self.meta = nil\n    def apply(self, args):\n        if self.EvalFunc:\n            return self.EvalFunc(self.ast, self.gen_env(args))\n        else:\n            return self.fn(args)\n    def gen_env(self, args):\n        return Env(self.env, self.params, args)\ndef _function_Q(exp):\n    assert isinstance(exp, MalType)\n    return exp.__class__ is MalFunc\n\n\n# atoms\nclass MalAtom(MalMeta):\n    def __init__(self, value):\n        self.value = value\n        self.meta = nil\n    def get_value(self):\n        return self.value\ndef _atom(val): return MalAtom(val)\ndef _atom_Q(exp): return exp.__class__ is MalAtom\n"
  },
  {
    "path": "impls/rpython/printer.py",
    "content": "import sys\nIS_RPYTHON = sys.argv[0].endswith('rpython')\n\nif IS_RPYTHON:\n    from rpython.rlib.rsre import rsre_re as re\nelse:\n    import re\n\nimport mal_types as types\nfrom mal_types import (MalType, MalStr, MalSym, MalInt,\n                       nil, true, false, MalAtom, MalFunc)\n\ndef _pr_a_str(s, print_readably=True):\n    if len(s) > 0 and s[0] == u'\\u029e':\n        return u':' + s[1:]\n    elif print_readably:\n        return u'\"' + types._replace(u'\\n', u'\\\\n',\n                        types._replace(u'\\\"', u'\\\\\"',\n                          types._replace(u'\\\\', u'\\\\\\\\', s))) + u'\"'\n    else:\n        return s\n\ndef _pr_str(obj, print_readably=True):\n    assert isinstance(obj, MalType)\n    _r = print_readably\n    if types._list_Q(obj):\n        res = []\n        for e in obj.values:\n            res.append(_pr_str(e,_r))\n        return u\"(\" + u\" \".join(res) + u\")\"\n    elif types._vector_Q(obj):\n        res = []\n        for e in obj.values:\n            res.append(_pr_str(e,_r))\n        return u\"[\" + u\" \".join(res) + u\"]\"\n    elif types._hash_map_Q(obj):\n        ret = []\n        for k in obj.dct.keys():\n            ret.append(_pr_a_str(k,_r))\n            ret.append(_pr_str(obj.dct[k],_r))\n        return u\"{\" + u\" \".join(ret) + u\"}\"\n    elif isinstance(obj, MalStr):\n        return _pr_a_str(obj.value,_r)\n    elif obj is nil:\n        return u\"nil\"\n    elif obj is true:\n        return u\"true\"\n    elif obj is false:\n        return u\"false\"\n    elif types._atom_Q(obj):\n        return u\"(atom \" + _pr_str(obj.get_value(),_r) + u\")\"\n    elif isinstance(obj, MalSym):\n        return obj.value\n    elif isinstance(obj, MalInt):\n        return unicode(str(obj.value))\n    elif isinstance(obj, MalFunc):\n        return u\"#<function>\"\n    else:\n        return u\"unknown\"\n\n"
  },
  {
    "path": "impls/rpython/reader.py",
    "content": "import sys\nIS_RPYTHON = sys.argv[0].endswith('rpython')\n\nif IS_RPYTHON:\n    from rpython.rlib.rsre import rsre_re as re\nelse:\n    import re\n\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr, _keywordu,\n                       _list, _listl, _vectorl, _hash_mapl)\n\nclass Blank(Exception): pass\n\nclass Reader():\n    def __init__(self, tokens, position=0):\n        self.tokens = tokens\n        self.position = position\n\n    def next(self):\n        self.position += 1\n        return self.tokens[self.position-1]\n\n    def peek(self):\n        if len(self.tokens) > self.position:\n            return self.tokens[self.position]\n        else:\n            return None\n\ndef tokenize(str):\n    re_str = \"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\s\\[\\]{}()'\\\"`@,;]+)\"\n    if IS_RPYTHON:\n        tok_re = re_str\n    else:\n        tok_re = re.compile(re_str)\n    return [t for t in re.findall(tok_re, str) if t[0] != ';']\n\ndef read_atom(reader):\n    if IS_RPYTHON:\n        int_re = '-?[0-9]+$'\n        float_re = '-?[0-9][0-9.]*$'\n        str_re = '\"(?:[\\\\\\\\].|[^\\\\\\\\\"])*\"'\n    else:\n        int_re = re.compile('-?[0-9]+$')\n        float_re = re.compile('-?[0-9][0-9.]*$')\n        str_re = re.compile('\"(?:[\\\\\\\\].|[^\\\\\\\\\"])*\"')\n    token = reader.next()\n    if re.match(int_re, token):     return MalInt(int(token))\n##    elif re.match(float_re, token): return int(token)\n    elif re.match(str_re, token):\n        end = len(token)-1\n        if end <= 1:\n            return MalStr(u\"\")\n        else:\n            s = unicode(token[1:end])\n            s = types._replace(u'\\\\\\\\',   u\"\\u029e\", s)\n            s = types._replace(u'\\\\\"',    u'\"', s)\n            s = types._replace(u'\\\\n',    u\"\\n\", s)\n            s = types._replace(u\"\\u029e\", u\"\\\\\", s)\n            return MalStr(s)\n    elif token[0] == '\"':\n        types.throw_str(\"expected '\\\"', got EOF\")\n    elif token[0] == ':':           return _keywordu(unicode(token[1:]))\n    elif token == \"nil\":            return types.nil\n    elif token == \"true\":           return types.true\n    elif token == \"false\":          return types.false\n    else:                           return MalSym(unicode(token))\n\ndef read_sequence(reader, start='(', end=')'):\n    ast = []\n    token = reader.next()\n    if token != start: types.throw_str(\"expected '\" + start + \"'\")\n\n    token = reader.peek()\n    while token != end:\n        if not token: types.throw_str(\"expected '\" + end + \"', got EOF\")\n        ast.append(read_form(reader))\n        token = reader.peek()\n    reader.next()\n    return ast\n\ndef read_list(reader):\n    lst = read_sequence(reader, '(', ')')\n    return _listl(lst)\n\ndef read_vector(reader):\n    lst = read_sequence(reader, '[', ']')\n    return _vectorl(lst)\n\ndef read_hash_map(reader):\n    lst = read_sequence(reader, '{', '}')\n    return _hash_mapl(lst)\n\ndef read_form(reader):\n    token = reader.peek()\n    # reader macros/transforms\n    if token[0] == ';':\n        reader.next()\n        return None\n    elif token == '\\'':\n        reader.next()\n        return _list(MalSym(u'quote'), read_form(reader))\n    elif token == '`':\n        reader.next()\n        return _list(MalSym(u'quasiquote'), read_form(reader))\n    elif token == '~':\n        reader.next()\n        return _list(MalSym(u'unquote'), read_form(reader))\n    elif token == '~@':\n        reader.next()\n        return _list(MalSym(u'splice-unquote'), read_form(reader))\n    elif token == '^':\n        reader.next()\n        meta = read_form(reader)\n        return _list(MalSym(u'with-meta'), read_form(reader), meta)\n    elif token == '@':\n        reader.next()\n        return _list(MalSym(u'deref'), read_form(reader))\n\n    # list\n    elif token == ')': types.throw_str(\"unexpected ')'\")\n    elif token == '(': return read_list(reader)\n\n    # vector\n    elif token == ']': types.throw_str(\"unexpected ']'\");\n    elif token == '[': return read_vector(reader);\n\n    # hash-map\n    elif token == '}': types.throw_str(\"unexpected '}'\");\n    elif token == '{': return read_hash_map(reader);\n\n    # atom\n    else:              return read_atom(reader);\n\ndef read_str(str):\n    tokens = tokenize(str)\n    if len(tokens) == 0: raise Blank(\"Blank Line\")\n    return read_form(Reader(tokens))\n"
  },
  {
    "path": "impls/rpython/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/rpython/step0_repl.py",
    "content": "#import sys, traceback\nimport mal_readline\n\n# read\ndef READ(str):\n    return str\n\n# eval\ndef EVAL(ast, env):\n        #print(\"EVAL %s\" % printer._pr_str(ast))\n        return ast\n\n# print\ndef PRINT(exp):\n    return exp\n\n# repl\ndef REP(str):\n    return PRINT(EVAL(READ(str), {}))\n\ndef entry_point(argv):\n    #mal_readline.init()\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line))\n        except EOFError as e:\n            break\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step1_read_print.py",
    "content": "#import sys, traceback\nimport mal_readline\nimport mal_types as types\nimport reader, printer\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef EVAL(ast, env):\n        #print(\"EVAL %s\" % printer._pr_str(ast))\n        return ast\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\ndef REP(str):\n    return PRINT(EVAL(READ(str), {}))\n\ndef entry_point(argv):\n    #mal_readline.init()\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step2_eval.py",
    "content": "#import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       _keywordu,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef EVAL(ast, env):\n    # print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        if ast.value in env:\n            return env[ast.value]\n        else:\n            raise Exception(u\"'\" + ast.value + u\"' not found\")\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        f = EVAL(ast[0], env)\n        args_list = []\n        for i in range(1, len(ast)):\n            args_list.append(EVAL(ast[i], env))\n        args = MalList(args_list)\n        if isinstance(f, MalFunc):\n            return f.apply(args)\n        else:\n            raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nrepl_env = {}\ndef REP(str, env):\n    return PRINT(EVAL(READ(str), env))\n\ndef plus(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(a.value+b.value)\ndef minus(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(a.value-b.value)\ndef multiply(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(a.value*b.value)\ndef divide(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(int(a.value/b.value))\nrepl_env[u'+'] = MalFunc(plus)\nrepl_env[u'-'] = MalFunc(minus)\nrepl_env[u'*'] = MalFunc(multiply)\nrepl_env[u'/'] = MalFunc(divide)\n\ndef entry_point(argv):\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step3_env.py",
    "content": "#import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       _symbol, _keywordu,\n                       nil, false, throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef EVAL(ast, env):\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if not isinstance(a0, MalSym):\n            raise Exception(\"attempt to apply on non-symbol\")\n\n        if u\"def!\" == a0.value:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0.value:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            return EVAL(a2, let_env)\n        else:\n            f = EVAL(a0, env)\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nrepl_env = Env()\ndef REP(str, env):\n    return PRINT(EVAL(READ(str), env))\n\ndef plus(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(a.value+b.value)\ndef minus(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(a.value-b.value)\ndef multiply(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(a.value*b.value)\ndef divide(args):\n    a, b = args[0], args[1]\n    assert isinstance(a, MalInt)\n    assert isinstance(b, MalInt)\n    return MalInt(int(a.value/b.value))\nrepl_env.set(_symbol(u'+'), MalFunc(plus))\nrepl_env.set(_symbol(u'-'), MalFunc(minus))\nrepl_env.set(_symbol(u'*'), MalFunc(multiply))\nrepl_env.set(_symbol(u'/'), MalFunc(divide))\n\ndef entry_point(argv):\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step4_if_fn_do.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef EVAL(ast, env):\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            return EVAL(a2, let_env)\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            return EVAL(ast[-1], env)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: return EVAL(ast[3], env)\n                else:            return nil\n            else:\n                return EVAL(a2, env)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step5_tco.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef EVAL(ast, env):\n  while True:\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            ast = a2\n            env = let_env # Continue loop (TCO)\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            ast = ast[-1] # Continue loop (TCO)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: ast = ast[3] # Continue loop (TCO)\n                else:            return nil\n            else:\n                ast = a2 # Continue loop (TCO)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                if f.ast:\n                    ast = f.ast\n                    env = f.gen_env(args) # Continue loop (TCO)\n                else:\n                    return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step6_file.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef EVAL(ast, env):\n  while True:\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            ast = a2\n            env = let_env # Continue loop (TCO)\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            ast = ast[-1] # Continue loop (TCO)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: ast = ast[3] # Continue loop (TCO)\n                else:            return nil\n            else:\n                ast = a2 # Continue loop (TCO)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                if f.ast:\n                    ast = f.ast\n                    env = f.gen_env(args) # Continue loop (TCO)\n                else:\n                    return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nclass MalEval(MalFunc):\n    def apply(self, args):\n        return self.EvalFunc(args[0], self.env)\n\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n    repl_env.set(types._symbol(u'eval'),\n                 MalEval(None, env=repl_env, EvalFunc=EVAL))\n    mal_args = []\n    if len(argv) >= 3:\n        for a in argv[2:]: mal_args.append(MalStr(unicode(a)))\n    repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\n    if len(argv) >= 2:\n        REP('(load-file \"' + argv[1] + '\")', repl_env)\n        return 0\n\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step7_quote.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef qq_loop(elt, acc):\n    if types._list_Q(elt) and len(elt) == 2:\n        fst = elt[0]\n        if isinstance(fst, MalSym) and fst.value == u\"splice-unquote\":\n            return _list(_symbol(u\"concat\"), elt[1], acc)\n    return _list(_symbol(u\"cons\"), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    acc = _list()\n    for elt in reversed(seq):\n        acc = qq_loop (elt, acc)\n    return acc\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2:\n            fst = ast[0]\n            if isinstance(fst, MalSym) and fst.value == u\"unquote\":\n                return ast[1]\n        return qq_foldr(ast.values)\n    elif types._vector_Q(ast):\n        return _list(_symbol(u\"vec\"), qq_foldr(ast.values))\n    elif types._symbol_Q(ast) or types._hash_map_Q(ast):\n        return _list(_symbol(u\"quote\"), ast)\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            ast = a2\n            env = let_env # Continue loop (TCO)\n        elif u\"quote\" == a0sym:\n            return ast[1]\n        elif u\"quasiquote\" == a0sym:\n            ast = quasiquote(ast[1]) # Continue loop (TCO)\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            ast = ast[-1] # Continue loop (TCO)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: ast = ast[3] # Continue loop (TCO)\n                else:            return nil\n            else:\n                ast = a2 # Continue loop (TCO)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                if f.ast:\n                    ast = f.ast\n                    env = f.gen_env(args) # Continue loop (TCO)\n                else:\n                    return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nclass MalEval(MalFunc):\n    def apply(self, args):\n        return self.EvalFunc(args[0], self.env)\n\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n    repl_env.set(types._symbol(u'eval'),\n                 MalEval(None, env=repl_env, EvalFunc=EVAL))\n    mal_args = []\n    if len(argv) >= 3:\n        for a in argv[2:]: mal_args.append(MalStr(unicode(a)))\n    repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\n    if len(argv) >= 2:\n        REP('(load-file \"' + argv[1] + '\")', repl_env)\n        return 0\n\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step8_macros.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef qq_loop(elt, acc):\n    if types._list_Q(elt) and len(elt) == 2:\n        fst = elt[0]\n        if isinstance(fst, MalSym) and fst.value == u\"splice-unquote\":\n            return _list(_symbol(u\"concat\"), elt[1], acc)\n    return _list(_symbol(u\"cons\"), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    acc = _list()\n    for elt in reversed(seq):\n        acc = qq_loop (elt, acc)\n    return acc\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2:\n            fst = ast[0]\n            if isinstance(fst, MalSym) and fst.value == u\"unquote\":\n                return ast[1]\n        return qq_foldr(ast.values)\n    elif types._vector_Q(ast):\n        return _list(_symbol(u\"vec\"), qq_foldr(ast.values))\n    elif types._symbol_Q(ast) or types._hash_map_Q(ast):\n        return _list(_symbol(u\"quote\"), ast)\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            ast = a2\n            env = let_env # Continue loop (TCO)\n        elif u\"quote\" == a0sym:\n            return ast[1]\n        elif u\"quasiquote\" == a0sym:\n            ast = quasiquote(ast[1]) # Continue loop (TCO)\n        elif u\"defmacro!\" == a0sym:\n            func = EVAL(ast[2], env)\n            return env.set(ast[1],\n                           MalFunc(func.fn, ast=func.ast, env=func.env,\n                           params=func.params, EvalFunc=func.EvalFunc,\n                           ismacro=True))\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            ast = ast[-1] # Continue loop (TCO)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: ast = ast[3] # Continue loop (TCO)\n                else:            return nil\n            else:\n                ast = a2 # Continue loop (TCO)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            if f.ismacro:\n                ast = f.apply(ast.rest()) # Continue loop (TCO)\n                continue\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                if f.ast:\n                    ast = f.ast\n                    env = f.gen_env(args) # Continue loop (TCO)\n                else:\n                    return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nclass MalEval(MalFunc):\n    def apply(self, args):\n        return self.EvalFunc(args[0], self.env)\n\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n    repl_env.set(types._symbol(u'eval'),\n                 MalEval(None, env=repl_env, EvalFunc=EVAL))\n    mal_args = []\n    if len(argv) >= 3:\n        for a in argv[2:]: mal_args.append(MalStr(unicode(a)))\n    repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if len(argv) >= 2:\n        REP('(load-file \"' + argv[1] + '\")', repl_env)\n        return 0\n\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/step9_try.py",
    "content": "import sys, traceback\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef qq_loop(elt, acc):\n    if types._list_Q(elt) and len(elt) == 2:\n        fst = elt[0]\n        if isinstance(fst, MalSym) and fst.value == u\"splice-unquote\":\n            return _list(_symbol(u\"concat\"), elt[1], acc)\n    return _list(_symbol(u\"cons\"), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    acc = _list()\n    for elt in reversed(seq):\n        acc = qq_loop (elt, acc)\n    return acc\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2:\n            fst = ast[0]\n            if isinstance(fst, MalSym) and fst.value == u\"unquote\":\n                return ast[1]\n        return qq_foldr(ast.values)\n    elif types._vector_Q(ast):\n        return _list(_symbol(u\"vec\"), qq_foldr(ast.values))\n    elif types._symbol_Q(ast) or types._hash_map_Q(ast):\n        return _list(_symbol(u\"quote\"), ast)\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            ast = a2\n            env = let_env # Continue loop (TCO)\n        elif u\"quote\" == a0sym:\n            return ast[1]\n        elif u\"quasiquote\" == a0sym:\n            ast = quasiquote(ast[1]) # Continue loop (TCO)\n        elif u\"defmacro!\" == a0sym:\n            func = EVAL(ast[2], env)\n            return env.set(ast[1],\n                           MalFunc(func.fn, ast=func.ast, env=func.env,\n                           params=func.params, EvalFunc=func.EvalFunc,\n                           ismacro=True))\n        elif u\"try*\" == a0sym:\n            if len(ast) < 3:\n                return EVAL(ast[1], env);\n            a1, a2 = ast[1], ast[2]\n            a20 = a2[0]\n            if isinstance(a20, MalSym):\n                if a20.value == u\"catch*\":\n                    try:\n                        return EVAL(a1, env);\n                    except types.MalException as exc:\n                        exc = exc.object\n                        catch_env = Env(env, _list(a2[1]), _list(exc))\n                        return EVAL(a2[2], catch_env)\n                    except Exception as exc:\n                        exc = MalStr(unicode(\"%s\" % exc))\n                        catch_env = Env(env, _list(a2[1]), _list(exc))\n                        return EVAL(a2[2], catch_env)\n            return EVAL(a1, env);\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            ast = ast[-1] # Continue loop (TCO)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: ast = ast[3] # Continue loop (TCO)\n                else:            return nil\n            else:\n                ast = a2 # Continue loop (TCO)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            if f.ismacro:\n                ast = f.apply(ast.rest()) # Continue loop (TCO)\n                continue\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                if f.ast:\n                    ast = f.ast\n                    env = f.gen_env(args) # Continue loop (TCO)\n                else:\n                    return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nclass MalEval(MalFunc):\n    def apply(self, args):\n        return self.EvalFunc(args[0], self.env)\n\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n    repl_env.set(types._symbol(u'eval'),\n                 MalEval(None, env=repl_env, EvalFunc=EVAL))\n    mal_args = []\n    if len(argv) >= 3:\n        for a in argv[2:]: mal_args.append(MalStr(unicode(a)))\n    repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if len(argv) >= 2:\n        REP('(load-file \"' + argv[1] + '\")', repl_env)\n        return 0\n\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            #print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/stepA_mal.py",
    "content": "import sys\nIS_RPYTHON = sys.argv[0].endswith('rpython')\n\nif IS_RPYTHON:\n    #from rpython.rlib.debug import fatalerror\n    from rpython.rtyper.lltypesystem import lltype\n    from rpython.rtyper.lltypesystem.lloperation import llop\nelse:\n    import traceback\n\nimport mal_readline\nimport mal_types as types\nfrom mal_types import (MalSym, MalInt, MalStr,\n                       nil, true, false, _symbol, _keywordu,\n                       throw_str,\n                       MalList, _list, MalVector, MalHashMap, MalFunc)\nimport reader, printer\nfrom env import Env\nimport core\n\n# read\ndef READ(str):\n    return reader.read_str(str)\n\n# eval\ndef qq_loop(elt, acc):\n    if types._list_Q(elt) and len(elt) == 2:\n        fst = elt[0]\n        if isinstance(fst, MalSym) and fst.value == u\"splice-unquote\":\n            return _list(_symbol(u\"concat\"), elt[1], acc)\n    return _list(_symbol(u\"cons\"), quasiquote(elt), acc)\n\ndef qq_foldr(seq):\n    acc = _list()\n    for elt in reversed(seq):\n        acc = qq_loop (elt, acc)\n    return acc\n\ndef quasiquote(ast):\n    if types._list_Q(ast):\n        if len(ast) == 2:\n            fst = ast[0]\n            if isinstance(fst, MalSym) and fst.value == u\"unquote\":\n                return ast[1]\n        return qq_foldr(ast.values)\n    elif types._vector_Q(ast):\n        return _list(_symbol(u\"vec\"), qq_foldr(ast.values))\n    elif types._symbol_Q(ast) or types._hash_map_Q(ast):\n        return _list(_symbol(u\"quote\"), ast)\n    else:\n        return ast\n\ndef EVAL(ast, env):\n  while True:\n    if env.get(u\"DEBUG-EVAL\") not in (None, nil, false):\n        print(u\"EVAL: \" + printer._pr_str(ast))\n    if types._symbol_Q(ast):\n        assert isinstance(ast, MalSym)\n        value = env.get(ast.value)\n        if value is None:\n            throw_str(\"'\" + str(ast.value) + \"' not found\")\n        return value\n    elif types._vector_Q(ast):\n        res = []\n        for a in ast.values:\n            res.append(EVAL(a, env))\n        return MalVector(res)\n    elif types._hash_map_Q(ast):\n        new_dct = {}\n        for k in ast.dct.keys():\n            new_dct[k] = EVAL(ast.dct[k], env)\n        return MalHashMap(new_dct)\n    elif not types._list_Q(ast):\n        return ast  # primitive value, return unchanged\n    else:\n        # apply list\n        if len(ast) == 0: return ast\n        a0 = ast[0]\n        if isinstance(a0, MalSym):\n            a0sym = a0.value\n        else:\n            a0sym = u\"__<*fn*>__\"\n\n        if u\"def!\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            res = EVAL(a2, env)\n            return env.set(a1, res)\n        elif u\"let*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            let_env = Env(env)\n            for i in range(0, len(a1), 2):\n                let_env.set(a1[i], EVAL(a1[i+1], let_env))\n            ast = a2\n            env = let_env # Continue loop (TCO)\n        elif u\"quote\" == a0sym:\n            return ast[1]\n        elif u\"quasiquote\" == a0sym:\n            ast = quasiquote(ast[1]) # Continue loop (TCO)\n        elif u\"defmacro!\" == a0sym:\n            func = EVAL(ast[2], env)\n            return env.set(ast[1],\n                           MalFunc(func.fn, ast=func.ast, env=func.env,\n                           params=func.params, EvalFunc=func.EvalFunc,\n                           ismacro=True))\n        elif u\"try*\" == a0sym:\n            if len(ast) < 3:\n                return EVAL(ast[1], env);\n            a1, a2 = ast[1], ast[2]\n            a20 = a2[0]\n            if isinstance(a20, MalSym):\n                if a20.value == u\"catch*\":\n                    try:\n                        return EVAL(a1, env);\n                    except types.MalException as exc:\n                        exc = exc.object\n                        catch_env = Env(env, _list(a2[1]), _list(exc))\n                        return EVAL(a2[2], catch_env)\n                    except Exception as exc:\n                        exc = MalStr(unicode(\"%s\" % exc))\n                        catch_env = Env(env, _list(a2[1]), _list(exc))\n                        return EVAL(a2[2], catch_env)\n            return EVAL(a1, env);\n        elif u\"do\" == a0sym:\n            if len(ast) == 0:\n                return nil\n            for i in range(1, len(ast) - 1):\n                EVAL(ast[i], env)\n            ast = ast[-1] # Continue loop (TCO)\n        elif u\"if\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            cond = EVAL(a1, env)\n            if cond is nil or cond is false:\n                if len(ast) > 3: ast = ast[3] # Continue loop (TCO)\n                else:            return nil\n            else:\n                ast = a2 # Continue loop (TCO)\n        elif u\"fn*\" == a0sym:\n            a1, a2 = ast[1], ast[2]\n            return MalFunc(None, a2, env, a1, EVAL)\n        else:\n            f = EVAL(a0, env)\n            if f.ismacro:\n                ast = f.apply(ast.rest()) # Continue loop (TCO)\n                continue\n            args_list = []\n            for i in range(1, len(ast)):\n                args_list.append(EVAL(ast[i], env))\n            args = MalList(args_list)\n            if isinstance(f, MalFunc):\n                if f.ast:\n                    ast = f.ast\n                    env = f.gen_env(args) # Continue loop (TCO)\n                else:\n                    return f.apply(args)\n            else:\n                raise Exception(\"%s is not callable\" % f)\n\n# print\ndef PRINT(exp):\n    return printer._pr_str(exp)\n\n# repl\nclass MalEval(MalFunc):\n    def apply(self, args):\n        return self.EvalFunc(args[0], self.env)\n\ndef entry_point(argv):\n    repl_env = Env()\n    def REP(str, env):\n        return PRINT(EVAL(READ(str), env))\n\n    # core.py: defined using python\n    for k, v in core.ns.items():\n        repl_env.set(_symbol(unicode(k)), MalFunc(v))\n    repl_env.set(types._symbol(u'eval'),\n                 MalEval(None, env=repl_env, EvalFunc=EVAL))\n    mal_args = []\n    if len(argv) >= 3:\n        for a in argv[2:]: mal_args.append(MalStr(unicode(a)))\n    repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args))\n\n    # core.mal: defined using the language itself\n    REP(\"(def! *host-language* \\\"rpython\\\")\", repl_env)\n    REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\n    if len(argv) >= 2:\n        REP('(load-file \"' + argv[1] + '\")', repl_env)\n        return 0\n\n    REP(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env)\n    while True:\n        try:\n            line = mal_readline.readline(\"user> \")\n            if line == \"\": continue\n            print(REP(line, repl_env))\n        except EOFError as e:\n            break\n        except reader.Blank:\n            continue\n        except types.MalException as e:\n            print(u\"Error: %s\" % printer._pr_str(e.object, False))\n        except Exception as e:\n            print(\"Error: %s\" % e)\n            if IS_RPYTHON:\n                llop.debug_print_traceback(lltype.Void)\n            else:\n                print(\"\".join(traceback.format_exception(*sys.exc_info())))\n    return 0\n\n# _____ Define and setup target ___\ndef target(*args):\n    return entry_point\n\n# Just run entry_point if not RPython compilation\nimport sys\nif not sys.argv[0].endswith('rpython'):\n    entry_point(sys.argv)\n"
  },
  {
    "path": "impls/rpython/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/ruby/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\nLABEL org.opencontainers.image.source=https://github.com/kanaka/mal\nLABEL org.opencontainers.image.description=\"mal test container: ruby\"\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install ruby\n"
  },
  {
    "path": "impls/ruby/Makefile",
    "content": "SOURCES_BASE = mal_readline.rb types.rb reader.rb printer.rb\nSOURCES_LISP = env.rb core.rb stepA_mal.rb\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.rb mal\n\nmal.rb: $(SOURCES)\n\tcat $+ | grep -v \"^require_relative\" > $@\n\nmal: mal.rb\n\techo \"#!/usr/bin/env ruby\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.rb mal\n"
  },
  {
    "path": "impls/ruby/core.rb",
    "content": "require \"readline\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\n\n$core_ns = {\n    :\"=\" =>       lambda {|a,b| a == b},\n    :throw =>     lambda {|a| raise MalException.new(a), \"Mal Exception\"},\n    :nil? =>      lambda {|a| a == nil},\n    :true? =>     lambda {|a| a == true},\n    :false? =>    lambda {|a| a == false},\n    :string? =>   lambda {|a| (a.is_a? String) && \"\\u029e\" != a[0]},\n    :symbol =>    lambda {|a| a.to_sym},\n    :symbol? =>   lambda {|a| a.is_a? Symbol},\n    :keyword =>   lambda {|a| (a.is_a? String) && \"\\u029e\" == a[0] ? a : \"\\u029e\"+a},\n    :keyword? =>  lambda {|a| (a.is_a? String) && \"\\u029e\" == a[0]},\n    :number? =>   lambda {|a| a.is_a? Numeric},\n    :fn? =>       lambda {|a| (a.is_a? Proc) && (!(a.is_a? Function) || !a.is_macro)},\n    :macro? =>    lambda {|a| (a.is_a? Function) && a.is_macro},\n\n    :\"pr-str\" =>  lambda {|*a| a.map {|e| _pr_str(e, true)}.join(\" \")},\n    :str =>       lambda {|*a| a.map {|e| _pr_str(e, false)}.join(\"\")},\n    :prn =>       lambda {|*a| puts(a.map {|e| _pr_str(e, true)}.join(\" \"))},\n    :println =>   lambda {|*a| puts(a.map {|e| _pr_str(e, false)}.join(\" \"))},\n    :readline =>  lambda {|a| Readline.readline(a,true)},\n    :\"read-string\" => lambda {|a| read_str(a)},\n    :slurp =>     lambda {|a| File.read(a)},\n    :< =>         lambda {|a,b| a < b},\n    :<= =>        lambda {|a,b| a <= b},\n    :> =>         lambda {|a,b| a > b},\n    :>= =>        lambda {|a,b| a >= b},\n    :+ =>         lambda {|a,b| a + b},\n    :- =>         lambda {|a,b| a - b},\n    :* =>         lambda {|a,b| a * b},\n    :/ =>         lambda {|a,b| a / b},\n    :\"time-ms\" => lambda {|| (Time.now.to_f * 1000).to_i},\n\n    :list =>      lambda {|*a| List.new a},\n    :list? =>     lambda {|*a| a[0].is_a? List},\n    :vector =>    lambda {|*a| Vector.new a},\n    :vector? =>   lambda {|*a| a[0].is_a? Vector},\n    :\"hash-map\" =>lambda {|*a| Hash[a.each_slice(2).to_a]},\n    :map? =>      lambda {|a| a.is_a? Hash},\n    :assoc =>     lambda {|*a| a[0].merge(Hash[a.drop(1).each_slice(2).to_a])},\n    :dissoc =>    lambda {|*a| h = a[0].clone; a.drop(1).each{|k| h.delete k}; h},\n    :get =>       lambda {|a,b| return nil if a == nil; a[b]},\n    :contains? => lambda {|a,b| a.key? b},\n    :keys =>      lambda {|a| List.new a.keys},\n    :vals =>      lambda {|a| List.new a.values},\n\n    :sequential? => lambda {|a| sequential?(a)},\n    :vec =>       lambda {|a| Vector.new a},\n    :cons =>      lambda {|a,b| List.new(b.clone.insert(0,a))},\n    :concat =>    lambda {|*a| List.new(a && a.reduce(:+) || [])},\n    :nth =>       lambda {|a,b| raise \"nth: index out of range\" if b >= a.size; a[b]},\n    :first =>     lambda {|a| a.nil? ? nil : a[0]},\n    :rest =>      lambda {|a| List.new(a.nil? || a.size == 0 ? [] : a.drop(1))},\n    :empty? =>    lambda {|a| a.size == 0},\n    :count =>     lambda {|a| return 0 if a == nil; a.size},\n    :apply =>     lambda {|*a| a[0][*a[1..-2].concat(a[-1])]},\n    :map =>       lambda {|a,b| List.new(b.map {|e| a[e]})},\n\n    :conj =>      lambda {|*a| a[0].clone.conj(a.drop(1))},\n    :seq =>       lambda {|a| a.nil? ? nil : a.size == 0 ? nil : a.seq},\n\n    :\"with-meta\" => lambda {|a,b| x = a.clone; x.meta = b; x},\n    :meta =>      lambda {|a| a.meta},\n    :atom =>      lambda {|a| Atom.new(a)},\n    :atom? =>     lambda {|a| a.is_a? Atom},\n    :deref =>     lambda {|a| a.val},\n    :reset! =>    lambda {|a,b| a.val = b},\n    :swap! =>     lambda {|*a| a[0].val = a[1][*[a[0].val].concat(a.drop(2))]},\n}\n\n"
  },
  {
    "path": "impls/ruby/env.rb",
    "content": "class Env\n    attr_accessor :data\n    def initialize(outer=nil, binds=[], exprs=[])\n        @data = {}\n        @outer = outer\n        binds.each_index do |i|\n            if binds[i] == :\"&\"\n                data[binds[i+1]] = List.new exprs.drop(i)\n                break\n            else\n                data[binds[i]] = exprs[i]\n            end\n        end\n        return self\n    end\n\n    def find(key)\n        if @data.key? key\n            return self\n        elsif @outer\n            return @outer.find(key)\n        else\n            return nil\n        end\n    end\n\n    def set(key, value)\n        @data[key] = value\n        return value\n    end\n\n    def get(key)\n        env = find(key)\n        raise \"'\" + key.to_s + \"' not found\" if not env\n        env.data[key]\n    end\n\n    def get_or_nil(key)\n        env = find(key)\n        return nil if not env\n        env.data[key]\n    end\nend\n"
  },
  {
    "path": "impls/ruby/mal_readline.rb",
    "content": "require \"readline\"\n\n$history_loaded = false\n$histfile = \"#{ENV['HOME']}/.mal-history\"\n\ndef _readline(prompt)\n    if !$history_loaded && File.exist?($histfile)\n        $history_loaded = true\n        if File.readable?($histfile)\n            File.readlines($histfile).each {|l| Readline::HISTORY.push(l.chomp)}\n        end\n    end\n\n    if line = Readline.readline(prompt, true)\n        if File.writable?($histfile)\n            File.open($histfile, 'a+') {|f| f.write(line+\"\\n\")}\n        end\n        return line\n    else\n        return nil\n    end\nend\n"
  },
  {
    "path": "impls/ruby/printer.rb",
    "content": "require_relative \"types\"\n\ndef _pr_str(obj, print_readably=true)\n    _r = print_readably\n    return case obj\n        when List\n            \"(\" + obj.map{|x| _pr_str(x, _r)}.join(\" \") + \")\"\n        when Vector\n            \"[\" + obj.map{|x| _pr_str(x, _r)}.join(\" \") + \"]\"\n        when Hash\n            ret = []\n            obj.each{|k,v| ret.push(_pr_str(k,_r), _pr_str(v,_r))}\n            \"{\" + ret.join(\" \") + \"}\"\n        when String\n            if obj[0] == \"\\u029e\"\n                \":\" + obj[1..-1]\n            elsif _r\n                obj.inspect  # escape special characters\n            else\n                obj\n            end\n        when Atom\n            \"(atom \" + _pr_str(obj.val, true) + \")\"\n        when nil\n            \"nil\"\n        else\n            obj.to_s\n    end\nend\n"
  },
  {
    "path": "impls/ruby/reader.rb",
    "content": "require_relative \"types\"\n\nclass Reader\n    def initialize(tokens)\n        @position = 0\n        @tokens = tokens\n    end\n    def peek\n        return @tokens[@position]\n    end\n    def next\n        @position += 1\n        return @tokens[@position-1]\n    end\nend\n\n\ndef tokenize(str)\n    re = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/\n    return str.scan(re).map{|m| m[0]}.select{ |t|\n        t != \"\" && t[0..0] != \";\"\n    }\nend\n\ndef parse_str(t) # trim and unescape\n    return t[1..-2].gsub(/\\\\./, {\"\\\\\\\\\" => \"\\\\\", \"\\\\n\" => \"\\n\", \"\\\\\\\"\" => '\"'})\nend\n\ndef read_atom(rdr)\n    token = rdr.next\n    return case token\n        when /^-?[0-9]+$/ then       token.to_i # integer\n        when /^-?[0-9][0-9.]*$/ then token.to_f # float\n        when /^\"(?:\\\\.|[^\\\\\"])*\"$/ then parse_str(token) # string\n        when /^\"/ then               raise \"expected '\\\"', got EOF\"\n        when /^:/ then               \"\\u029e\" + token[1..-1] # keyword\n        when \"nil\" then              nil\n        when \"true\" then             true\n        when \"false\" then            false\n        else                         token.to_sym # symbol\n    end\nend\n\ndef read_list(rdr, klass, start=\"(\", last =\")\")\n    ast = klass.new\n    token = rdr.next()\n    if token != start\n        raise \"expected '\" + start + \"'\"\n    end\n    while (token = rdr.peek) != last\n        if not token\n            raise \"expected '\" + last + \"', got EOF\"\n        end\n        ast.push(read_form(rdr))\n    end\n    rdr.next\n    return ast\nend\n\ndef read_form(rdr)\n    return case rdr.peek\n        when \";\" then  nil\n        when \"'\" then  rdr.next; List.new [:quote, read_form(rdr)]\n        when \"`\" then  rdr.next; List.new [:quasiquote, read_form(rdr)]\n        when \"~\" then  rdr.next; List.new [:unquote, read_form(rdr)]\n        when \"~@\" then rdr.next; List.new [:\"splice-unquote\", read_form(rdr)]\n        when \"^\" then  rdr.next; meta = read_form(rdr);\n                       List.new [:\"with-meta\", read_form(rdr), meta]\n        when \"@\" then  rdr.next; List.new [:deref, read_form(rdr)]\n\n        when \"(\" then  read_list(rdr, List, \"(\", \")\")\n        when \")\" then  raise \"unexpected ')'\"\n        when \"[\" then  read_list(rdr, Vector, \"[\", \"]\")\n        when \"]\" then  raise \"unexpected ']'\" \n        when \"{\" then  Hash[read_list(rdr, List, \"{\", \"}\").each_slice(2).to_a]\n        when \"}\" then  raise \"unexpected '}'\" \n        else           read_atom(rdr)\n    end\nend\n\ndef read_str(str)\n    tokens = tokenize(str)\n    return nil if tokens.size == 0\n    return read_form(Reader.new(tokens))\nend\n\n"
  },
  {
    "path": "impls/ruby/run",
    "content": "#!/usr/bin/env bash\nexec ruby $(dirname $0)/${STEP:-stepA_mal}.rb \"${@}\"\n"
  },
  {
    "path": "impls/ruby/step0_repl.rb",
    "content": "require_relative \"mal_readline\"\n\n# read\ndef READ(str)\n    return str\nend\n\n# eval\ndef EVAL(ast, env)\n    return ast\nend\n\n# print\ndef PRINT(exp)\n    return exp\nend\n\n# repl\ndef REP(str)\n    return PRINT(EVAL(READ(str), {}))\nend\n\n# repl loop\nwhile line = _readline(\"user> \")\n    puts REP(line)\nend\n"
  },
  {
    "path": "impls/ruby/step1_read_print.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef EVAL(ast, env)\n    return ast\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\ndef REP(str)\n    return PRINT(EVAL(READ(str), {}))\nend\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP(line)\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace.join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step2_eval.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef EVAL(ast, env)\n    #puts \"EVAL: #{_pr_str(ast, true)}\"\n\n    case ast\n    in Symbol\n            raise \"'\" + ast.to_s + \"' not found\" if not env.key? ast\n            return env[ast]\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        return f[*args.map{|a| EVAL(a, env)}]\n\n    else                        # Empty list or scalar\n      return ast\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = {}\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\nrepl_env[:+] = lambda {|a,b| a + b}\nrepl_env[:-] = lambda {|a,b| a - b}\nrepl_env[:*] = lambda {|a,b| a * b}\nrepl_env[:/] = lambda {|a,b| a / b}\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace.join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step3_env.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef EVAL(ast, env)\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        return EVAL(a2, let_env)\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        return f[*args.map{|a| EVAL(a, env)}]\n\n    else                        # Empty list or scalar\n      return ast\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\nrepl_env.set(:+, lambda {|a,b| a + b})\nrepl_env.set(:-, lambda {|a,b| a - b})\nrepl_env.set(:*, lambda {|a,b| a * b})\nrepl_env.set(:/, lambda {|a,b| a / b})\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace.join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step4_if_fn_do.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef EVAL(ast, env)\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        return EVAL(a2, let_env)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        return EVAL(ast.last, env)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            return EVAL(a2, env)\n        else\n            return EVAL(ast[3], env)\n        end\n    in :\"fn*\", a1, a2\n        return lambda {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        return f[*args.map{|a| EVAL(a, env)}]\n\n    else                        # Empty list or scalar\n      return ast\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\n\n# core.mal: defined using the language itself\nRE[\"(def! not (fn* (a) (if a false true)))\"]\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace.join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step5_tco.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef EVAL(ast, env)\n    while true\n\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        env = let_env\n        ast = a2 # Continue loop (TCO)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        ast = ast.last # Continue loop (TCO)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            ast = a2 # Continue loop (TCO)\n        else\n            ast = ast[3] # Continue loop (TCO)\n        end\n    in :\"fn*\", a1, a2\n        return Function.new(a2, env, a1) {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        if f.class == Function\n            ast = f.ast\n            env = f.gen_env(List.new args.map{|a| EVAL(a, env)})\n            # Continue loop (TCO)\n        else\n            return f[*args.map{|a| EVAL(a, env)}]\n        end\n\n    else                        # Empty list or scalar\n      return ast\n    end\n\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\n\n# core.mal: defined using the language itself\nRE[\"(def! not (fn* (a) (if a false true)))\"]\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace[0..100].join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step6_file.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef EVAL(ast, env)\n    while true\n\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        env = let_env\n        ast = a2 # Continue loop (TCO)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        ast = ast.last # Continue loop (TCO)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            ast = a2 # Continue loop (TCO)\n        else\n            ast = ast[3] # Continue loop (TCO)\n        end\n    in :\"fn*\", a1, a2\n        return Function.new(a2, env, a1) {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        if f.class == Function\n            ast = f.ast\n            env = f.gen_env(List.new args.map{|a| EVAL(a, env)})\n            # Continue loop (TCO)\n        else\n            return f[*args.map{|a| EVAL(a, env)}]\n        end\n\n    else                        # Empty list or scalar\n      return ast\n    end\n\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\nrepl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)})\nrepl_env.set(:\"*ARGV*\", List.new(ARGV.slice(1,ARGV.length) || []))\n\n# core.mal: defined using the language itself\nRE[\"(def! not (fn* (a) (if a false true)))\"]\nRE[\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]\n\nif ARGV.size > 0\n    RE[\"(load-file \\\"\" + ARGV[0] + \"\\\")\"]\n    exit 0\nend\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace[0..100].join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step7_quote.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef qq_loop(ast)\n  acc = List.new []\n  ast.reverse_each do |elt|\n    if elt in List[:\"splice-unquote\", quoted]\n      acc = List.new [:concat, quoted, acc]\n    else\n      acc = List.new [:cons, quasiquote(elt), acc]\n    end\n  end\n  return acc\nend\n\ndef quasiquote(ast)\n  case ast\n  when List\n    if ast in List[:unquote, quoted]   # ← fixed pattern\n      quoted\n    else\n      qq_loop(ast)\n    end\n  when Vector\n    List.new [:vec, qq_loop(ast)]\n  when Hash, Symbol\n    List.new [:quote, ast]\n  else\n    ast\n  end\nend\n\ndef EVAL(ast, env)\n    while true\n\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        env = let_env\n        ast = a2 # Continue loop (TCO)\n    in :quote, a1\n        return a1\n    in :quasiquote, a1\n        ast = quasiquote(a1); # Continue loop (TCO)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        ast = ast.last # Continue loop (TCO)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            ast = a2 # Continue loop (TCO)\n        else\n            ast = ast[3] # Continue loop (TCO)\n        end\n    in :\"fn*\", a1, a2\n        return Function.new(a2, env, a1) {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        if f.class == Function\n            ast = f.ast\n            env = f.gen_env(List.new args.map{|a| EVAL(a, env)})\n            # Continue loop (TCO)\n        else\n            return f[*args.map{|a| EVAL(a, env)}]\n        end\n\n    else                        # Empty list or scalar\n      return ast\n    end\n\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\nrepl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)})\nrepl_env.set(:\"*ARGV*\", List.new(ARGV.slice(1,ARGV.length) || []))\n\n# core.mal: defined using the language itself\nRE[\"(def! not (fn* (a) (if a false true)))\"]\nRE[\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]\n\nif ARGV.size > 0\n    RE[\"(load-file \\\"\" + ARGV[0] + \"\\\")\"]\n    exit 0\nend\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace[0..100].join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step8_macros.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef qq_loop(ast)\n  acc = List.new []\n  ast.reverse_each do |elt|\n    if elt in List[:\"splice-unquote\", quoted]\n      acc = List.new [:concat, quoted, acc]\n    else\n      acc = List.new [:cons, quasiquote(elt), acc]\n    end\n  end\n  return acc\nend\n\ndef quasiquote(ast)\n  case ast\n  when List\n    if ast in List[:unquote, quoted]   # ← fixed pattern\n      quoted\n    else\n      qq_loop(ast)\n    end\n  when Vector\n    List.new [:vec, qq_loop(ast)]\n  when Hash, Symbol\n    List.new [:quote, ast]\n  else\n    ast\n  end\nend\n\ndef EVAL(ast, env)\n    while true\n\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        env = let_env\n        ast = a2 # Continue loop (TCO)\n    in :quote, a1\n        return a1\n    in :quasiquote, a1\n        ast = quasiquote(a1); # Continue loop (TCO)\n    in :defmacro!, a1, a2\n        func = EVAL(a2, env).clone\n        func.is_macro = true\n        return env.set(a1, func)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        ast = ast.last # Continue loop (TCO)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            ast = a2 # Continue loop (TCO)\n        else\n            ast = ast[3] # Continue loop (TCO)\n        end\n    in :\"fn*\", a1, a2\n        return Function.new(a2, env, a1) {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        if f.class == Function\n            if f.is_macro\n              ast = f[*args]\n              next # Continue loop (TCO)\n            end\n            ast = f.ast\n            env = f.gen_env(List.new args.map{|a| EVAL(a, env)})\n            # Continue loop (TCO)\n        else\n            return f[*args.map{|a| EVAL(a, env)}]\n        end\n\n    else                        # Empty list or scalar\n      return ast\n    end\n\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\nrepl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)})\nrepl_env.set(:\"*ARGV*\", List.new(ARGV.slice(1,ARGV.length) || []))\n\n# core.mal: defined using the language itself\nRE[\"(def! not (fn* (a) (if a false true)))\"]\nRE[\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]\nRE[\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"]\n\nif ARGV.size > 0\n    RE[\"(load-file \\\"\" + ARGV[0] + \"\\\")\"]\n    exit 0\nend\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        puts \"Error: #{e}\" \n        puts \"\\t#{e.backtrace[0..100].join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/step9_try.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef qq_loop(ast)\n  acc = List.new []\n  ast.reverse_each do |elt|\n    if elt in List[:\"splice-unquote\", quoted]\n      acc = List.new [:concat, quoted, acc]\n    else\n      acc = List.new [:cons, quasiquote(elt), acc]\n    end\n  end\n  return acc\nend\n\ndef quasiquote(ast)\n  case ast\n  when List\n    if ast in List[:unquote, quoted]   # ← fixed pattern\n      quoted\n    else\n      qq_loop(ast)\n    end\n  when Vector\n    List.new [:vec, qq_loop(ast)]\n  when Hash, Symbol\n    List.new [:quote, ast]\n  else\n    ast\n  end\nend\n\ndef EVAL(ast, env)\n    while true\n\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        env = let_env\n        ast = a2 # Continue loop (TCO)\n    in :quote, a1\n        return a1\n    in :quasiquote, a1\n        ast = quasiquote(a1); # Continue loop (TCO)\n    in :defmacro!, a1, a2\n        func = EVAL(a2, env).clone\n        func.is_macro = true\n        return env.set(a1, func)\n    in [:\"try*\", a1, [:\"catch*\", key, handler]]\n        begin\n            return EVAL(a1, env)\n        rescue Exception => exc\n            if exc.is_a? MalException\n                exc = exc.data\n            else\n                exc = exc.message\n            end\n            ast = handler\n            env = Env.new(env, [key], [exc]) # Continue loop (TCO)\n        end\n    in [:\"try*\", a1]\n        ast = a1 # Continue loop (TCO)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        ast = ast.last # Continue loop (TCO)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            ast = a2 # Continue loop (TCO)\n        else\n            ast = ast[3] # Continue loop (TCO)\n        end\n    in :\"fn*\", a1, a2\n        return Function.new(a2, env, a1) {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        if f.class == Function\n            if f.is_macro\n              ast = f[*args]\n              next # Continue loop (TCO)\n            end\n            ast = f.ast\n            env = f.gen_env(List.new args.map{|a| EVAL(a, env)})\n            # Continue loop (TCO)\n        else\n            return f[*args.map{|a| EVAL(a, env)}]\n        end\n\n    else                        # Empty list or scalar\n      return ast\n    end\n\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\nrepl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)})\nrepl_env.set(:\"*ARGV*\", List.new(ARGV.slice(1,ARGV.length) || []))\n\n# core.mal: defined using the language itself\nRE[\"(def! not (fn* (a) (if a false true)))\"]\nRE[\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]\nRE[\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"]\n\nif ARGV.size > 0\n    RE[\"(load-file \\\"\" + ARGV[0] + \"\\\")\"]\n    exit 0\nend\n\n# repl loop\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        if e.is_a? MalException\n            puts \"Error: #{_pr_str(e.data, true)}\" \n        else\n            puts \"Error: #{e}\" \n        end\n        puts \"\\t#{e.backtrace[0..100].join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/stepA_mal.rb",
    "content": "require_relative \"mal_readline\"\nrequire_relative \"types\"\nrequire_relative \"reader\"\nrequire_relative \"printer\"\nrequire_relative \"env\"\nrequire_relative \"core\"\n\n# read\ndef READ(str)\n    return read_str(str)\nend\n\n# eval\ndef qq_loop(ast)\n  acc = List.new []\n  ast.reverse_each do |elt|\n    if elt in List[:\"splice-unquote\", quoted]\n      acc = List.new [:concat, quoted, acc]\n    else\n      acc = List.new [:cons, quasiquote(elt), acc]\n    end\n  end\n  return acc\nend\n\ndef quasiquote(ast)\n  case ast\n  when List\n    if ast in List[:unquote, quoted]   # ← fixed pattern\n      quoted\n    else\n      qq_loop(ast)\n    end\n  when Vector\n    List.new [:vec, qq_loop(ast)]\n  when Hash, Symbol\n    List.new [:quote, ast]\n  else\n    ast\n  end\nend\n\ndef EVAL(ast, env)\n    while true\n\n    if env.get_or_nil(:\"DEBUG-EVAL\")\n        puts \"EVAL: #{_pr_str(ast, true)}\"\n    end\n\n    case ast\n    in Symbol\n            return env.get(ast)\n    in Vector\n            return Vector.new ast.map{|a| EVAL(a, env)}\n    in Hash\n            new_hm = {}\n            ast.each{|k,v| new_hm[k] = EVAL(v, env)}\n            return new_hm\n\n    # apply list\n\n    in :def!, a1, a2\n        return env.set(a1, EVAL(a2, env))\n    in :\"let*\", a1, a2\n        let_env = Env.new(env)\n        a1.each_slice(2) do |a,e|\n            let_env.set(a, EVAL(e, let_env))\n        end\n        env = let_env\n        ast = a2 # Continue loop (TCO)\n    in :quote, a1\n        return a1\n    in :quasiquote, a1\n        ast = quasiquote(a1); # Continue loop (TCO)\n    in :defmacro!, a1, a2\n        func = EVAL(a2, env).clone\n        func.is_macro = true\n        return env.set(a1, func)\n    in :\"rb*\", a1\n        res = eval(a1)\n        return case res\n            when Array; List.new res\n            else; res\n        end\n    in [:\"try*\", a1, [:\"catch*\", key, handler]]\n        begin\n            return EVAL(a1, env)\n        rescue Exception => exc\n            if exc.is_a? MalException\n                exc = exc.data\n            else\n                exc = exc.message\n            end\n            ast = handler\n            env = Env.new(env, [key], [exc]) # Continue loop (TCO)\n        end\n    in [:\"try*\", a1]\n        ast = a1 # Continue loop (TCO)\n    in [:do, *]\n        ast[1..-2].map{|a| EVAL(a, env)}\n        ast = ast.last # Continue loop (TCO)\n    in [:if, a1, a2, *]\n        cond = EVAL(a1, env)\n        if cond\n            ast = a2 # Continue loop (TCO)\n        else\n            ast = ast[3] # Continue loop (TCO)\n        end\n    in :\"fn*\", a1, a2\n        return Function.new(a2, env, a1) {|*args|\n            EVAL(a2, Env.new(env, a1, List.new(args)))\n        }\n    in [a0, *]\n        f = EVAL(a0, env)\n        args = ast.drop(1)\n        if f.class == Function\n            if f.is_macro\n              ast = f[*args]\n              next # Continue loop (TCO)\n            end\n            ast = f.ast\n            env = f.gen_env(List.new args.map{|a| EVAL(a, env)})\n            # Continue loop (TCO)\n        else\n            return f[*args.map{|a| EVAL(a, env)}]\n        end\n\n    else                        # Empty list or scalar\n      return ast\n    end\n\n    end\nend\n\n# print\ndef PRINT(exp)\n    return _pr_str(exp, true)\nend\n\n# repl\nrepl_env = Env.new\nRE = lambda {|str| EVAL(READ(str), repl_env) }\nREP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }\n\n# core.rb: defined using ruby\n$core_ns.each do |k,v| repl_env.set(k,v) end\nrepl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)})\nrepl_env.set(:\"*ARGV*\", List.new(ARGV.slice(1,ARGV.length) || []))\n\n# core.mal: defined using the language itself\nRE[\"(def! *host-language* \\\"ruby\\\")\"]\nRE[\"(def! not (fn* (a) (if a false true)))\"]\nRE[\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"]\nRE[\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"]\n\nif ARGV.size > 0\n    RE[\"(load-file \\\"\" + ARGV[0] + \"\\\")\"]\n    exit 0\nend\n\n# repl loop\nRE[\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\"]\nwhile line = _readline(\"user> \")\n    begin\n        puts REP[line]\n    rescue Exception => e\n        if e.is_a? MalException\n            puts \"Error: #{_pr_str(e.data, true)}\" \n        else\n            puts \"Error: #{e}\" \n        end\n        puts \"\\t#{e.backtrace[0..100].join(\"\\n\\t\")}\"\n    end\nend\n"
  },
  {
    "path": "impls/ruby/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/ruby/tests/stepA_mal.mal",
    "content": ";; Testing basic ruby interop\n\n(rb* \"7\")\n;=>7\n\n(rb* \"'7'\")\n;=>\"7\"\n\n(rb* \"[7,8,9]\")\n;=>(7 8 9)\n\n(rb* \"{\\\"abc\\\" => 789}\")\n;=>{\"abc\" 789}\n\n(rb* \"print 'hello\\n'\")\n;/hello\n;=>nil\n\n(rb* \"$foo=8;\")\n(rb* \"$foo\")\n;=>8\n\n(rb* \"['a','b','c'].map{|x| 'X'+x+'Y'}.join(' ')\")\n;=>\"XaY XbY XcY\"\n\n(rb* \"[1,2,3].map{|x| 1+x}\")\n;=>(2 3 4)\n"
  },
  {
    "path": "impls/ruby/types.rb",
    "content": "require_relative \"env\"\n\nclass MalException < StandardError\n  attr_reader :data\n  def initialize(data)\n    @data = data\n  end\nend\n\nclass String # re-open and add seq\n    def seq()\n        return List.new self.split(\"\")\n    end\nend\n\nclass List < Array\n    attr_accessor :meta\n    def conj(xs)\n        xs.each{|x| self.unshift(x)}\n        return self\n    end\n    def seq()\n        return self\n    end\nend\n\nclass Vector < Array\n    attr_accessor :meta\n    def conj(xs)\n        self.push(*xs)\n        return self\n    end\n    def seq()\n        return List.new self\n    end\nend\n\nclass Hash # re-open and add meta\n    attr_accessor :meta\nend\n\ndef sequential?(obj)\n    return obj.is_a?(List) || obj.is_a?(Vector)\nend\n\nclass Proc # re-open and add meta\n    attr_accessor :meta\nend\n\nclass Function < Proc\n    attr_accessor :ast\n    attr_accessor :env\n    attr_accessor :params\n    attr_accessor :is_macro\n\n    def initialize(ast=nil, env=nil, params=nil, &block)\n        super()\n        @ast = ast\n        @env = env\n        @params = params\n        @is_macro = false\n    end\n\n    def gen_env(args)\n        return Env.new(@env, @params, args)\n    end\nend\n\nclass Atom\n    attr_accessor :meta\n    attr_accessor :val\n    def initialize(val)\n        @val = val\n    end\nend\n"
  },
  {
    "path": "impls/ruby.2/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install ruby\n"
  },
  {
    "path": "impls/ruby.2/Makefile",
    "content": "SOURCES_BASE = errors.rb types.rb reader.rb printer.rb\nSOURCES_LISP = env.rb core.rb stepA_mal.rb\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.rb mal\n\nmal.rb: $(SOURCES)\n\tcat $+ | grep -v \"^require_relative\" > $@\n\nmal: mal.rb\n\techo \"#!/usr/bin/env ruby\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.rb mal\n"
  },
  {
    "path": "impls/ruby.2/core.rb",
    "content": "require \"readline\"\n\nrequire_relative \"types\"\n\nmodule Mal\n  module Core\n    extend self\n\n    def ns\n      {\n        Types::Symbol.for(\"+\") => Types::Builtin.new(\"+\") do |a, b|\n          a + b\n        end,\n\n        Types::Symbol.for(\"-\") => Types::Builtin.new(\"-\") { |a, b| a - b },\n        Types::Symbol.for(\"*\") => Types::Builtin.new(\"*\") { |a, b| a * b },\n        Types::Symbol.for(\"/\") => Types::Builtin.new(\"/\") { |a, b| a / b },\n\n        Types::Symbol.for(\"list\") => Types::Builtin.new(\"list\") do |*mal|\n          list = Types::List.new\n          mal.each { |m| list << m }\n          list\n        end,\n\n        Types::Symbol.for(\"list?\") => Types::Builtin.new(\"list?\") do |list = nil|\n          list.is_a?(Types::List) ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"vector?\") => Types::Builtin.new(\"vector?\") do |vector = nil|\n          vector.is_a?(Types::Vector) ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"string?\") => Types::Builtin.new(\"string?\") do |string = nil|\n          string.is_a?(Types::String) ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"number?\") => Types::Builtin.new(\"number?\") do |number = nil|\n          number.is_a?(Types::Number) ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"fn?\") => Types::Builtin.new(\"fn?\") do |fn = nil|\n          fn.is_a?(Types::Callable) && !fn.is_macro? ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"macro?\") => Types::Builtin.new(\"macro?\") do |macro = nil|\n          macro.is_a?(Types::Callable) && macro.is_macro? ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"empty?\") => Types::Builtin.new(\"empty?\") do |list_or_vector = nil|\n          is_empty =\n            case list_or_vector\n            when Types::List, Types::Vector\n              list_or_vector.empty?\n            else\n              true\n            end\n\n          is_empty ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"count\") => Types::Builtin.new(\"count\") do |*mal|\n          count =\n            if mal.any?\n              case mal.first\n              when Types::List, Types::Vector\n                mal.first.size\n              else\n                0\n              end\n            else\n              0\n            end\n\n          Types::Number.new(count)\n        end,\n\n        Types::Symbol.for(\"=\") => Types::Builtin.new(\"=\") do |a, b|\n          if a.nil? || b.nil?\n            Types::False.instance\n          else\n            if a == b\n              Types::True.instance\n            else\n              Types::False.instance\n            end\n          end\n        end,\n\n        Types::Symbol.for(\"<\") => Types::Builtin.new(\"<\") do |a, b|\n          if a.nil? || b.nil?\n            Types::False.instance\n          else\n            if a.is_a?(Types::Number) && b.is_a?(Types::Number)\n              if a.value < b.value\n                Types::True.instance\n              else\n                Types::False.instance\n              end\n            else\n              Types::False.instance\n            end\n          end\n        end,\n\n        Types::Symbol.for(\"<=\") => Types::Builtin.new(\"<=\") do |a, b|\n          if a.nil? || b.nil?\n            Types::False.instance\n          else\n            if a.is_a?(Types::Number) && b.is_a?(Types::Number)\n              if a.value <= b.value\n                Types::True.instance\n              else\n                Types::False.instance\n              end\n            else\n              Types::False.instance\n            end\n          end\n        end,\n\n        Types::Symbol.for(\">\") => Types::Builtin.new(\">\") do |a, b|\n          if a.nil? || b.nil?\n            Types::False.instance\n          else\n            if a.is_a?(Types::Number) && b.is_a?(Types::Number)\n              if a.value > b.value\n                Types::True.instance\n              else\n                Types::False.instance\n              end\n            else\n              Types::False.instance\n            end\n          end\n        end,\n\n        Types::Symbol.for(\">=\") => Types::Builtin.new(\">=\") do |a, b|\n          if a.nil? || b.nil?\n            Types::False.instance\n          else\n            if a.is_a?(Types::Number) && b.is_a?(Types::Number)\n              if a.value >= b.value\n                Types::True.instance\n              else\n                Types::False.instance\n              end\n            else\n              Types::False.instance\n            end\n          end\n        end,\n\n        Types::Symbol.for(\"pr-str\") => Types::Builtin.new(\"pr-str\") do |*mal|\n          Types::String.new(mal.map { |m| Mal.pr_str(m, true) }.join(\" \"))\n        end,\n\n        Types::Symbol.for(\"str\") => Types::Builtin.new(\"str\") do |*mal|\n          Types::String.new(mal.map { |m| Mal.pr_str(m, false) }.join(\"\"))\n        end,\n\n        Types::Symbol.for(\"prn\") => Types::Builtin.new(\"prn\") do |*mal|\n          puts mal.map { |m| Mal.pr_str(m, true) }.join(\" \")\n          Types::Nil.instance\n        end,\n\n        Types::Symbol.for(\"println\") => Types::Builtin.new(\"println\") do |*mal|\n          puts mal.map { |m| Mal.pr_str(m, false) }.join(\" \")\n          Types::Nil.instance\n        end,\n\n        Types::Symbol.for(\"read-string\") => Types::Builtin.new(\"read-string\") do |string = nil|\n          if string.is_a?(Types::String)\n            Mal.read_str(string.value)\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"slurp\") => Types::Builtin.new(\"slurp\") do |file = nil|\n          if file.is_a?(Types::String)\n            if File.exist?(file.value)\n              Types::String.new(File.read(file.value))\n            else\n              raise FileNotFoundError, file.value\n            end\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"atom\") => Types::Builtin.new(\"atom\") do |mal|\n          Types::Atom.new(mal)\n        end,\n\n        Types::Symbol.for(\"atom?\") => Types::Builtin.new(\"atom?\") do |maybe_atom|\n          maybe_atom.is_a?(Types::Atom) ? Types::True.instance : Types::False.instance\n        end,\n\n        Types::Symbol.for(\"deref\") => Types::Builtin.new(\"deref\") do |maybe_atom|\n          maybe_atom.is_a?(Types::Atom) ? maybe_atom.value : Types::Nil.instance\n        end,\n\n        Types::Symbol.for(\"reset!\") => Types::Builtin.new(\"reset!\") do |atom, value|\n          if value.nil?\n            value = Types::Nil.instance\n          end\n\n          atom.value = value\n        end,\n\n        Types::Symbol.for(\"swap!\") => Types::Builtin.new(\"swap!\") do |atom, fn, *args|\n          atom.value = fn.call(Types::Args.new([atom.value, *args]))\n        end,\n\n        Types::Symbol.for(\"cons\") => Types::Builtin.new(\"cons\") do |val, list_or_vector|\n          Types::List.new([val, *list_or_vector])\n        end,\n\n        Types::Symbol.for(\"concat\") => Types::Builtin.new(\"concat\") do |*mal|\n          list = Types::List.new\n\n          mal.each do |l|\n            list.concat(l)\n          end\n\n          list\n        end,\n\n        Types::Symbol.for(\"vec\") => Types::Builtin.new(\"vec\") do |list_or_vector|\n          case list_or_vector\n          when Types::List\n            vec = Types::Vector.new\n\n            list_or_vector.each do |m|\n              vec << m\n            end\n\n            vec\n          when Types::Vector\n            list_or_vector\n          else\n            raise TypeError, \"invalid `vec` arguments, must be vector or list\"\n          end\n        end,\n\n        Types::Symbol.for(\"nth\") => Types::Builtin.new(\"nth\") do |list_or_vector, index|\n          result = list_or_vector[index.value]\n          raise IndexError, \"Index #{index.value} is out of bounds\" if result.nil?\n          result\n        end,\n\n        Types::Symbol.for(\"first\") => Types::Builtin.new(\"first\") do |list_or_vector|\n          if !list_or_vector.nil? && list_or_vector != Types::Nil.instance\n            result = list_or_vector.first\n\n            if result.nil?\n              result = Types::Nil.instance\n            end\n\n            result\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"rest\") => Types::Builtin.new(\"rest\") do |list_or_vector|\n          Types::List.new (\n            case list_or_vector\n            when Types::List, Types::Vector\n              if list_or_vector.empty?\n                []\n              else\n                list_or_vector[1..]\n              end\n            when Types::Nil\n              []\n            else\n              raise TypeError, \"Unable to `rest`, too nervous\"\n            end\n          )\n        end,\n\n        Types::Symbol.for(\"throw\") => Types::Builtin.new(\"throw\") do |to_throw|\n          raise MalError, to_throw\n        end,\n\n        Types::Symbol.for(\"apply\") => Types::Builtin.new(\"apply\") do |fn, *rest|\n          args = Types::Args.new\n\n          rest.flatten(1).each do |a|\n            args << a\n          end\n\n          fn.call(args)\n        end,\n\n        Types::Symbol.for(\"map\") => Types::Builtin.new(\"map\") do |fn, *rest|\n          results = Types::List.new\n\n          rest.flatten(1).each do |a|\n            results << fn.call(Types::Args.new([a]))\n          end\n\n          results\n        end,\n\n        Types::Symbol.for(\"nil?\") => Types::Builtin.new(\"nil?\") do |mal|\n          if mal == Types::Nil.instance\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"true?\") => Types::Builtin.new(\"true?\") do |mal|\n          if mal == Types::True.instance\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"false?\") => Types::Builtin.new(\"false?\") do |mal|\n          if mal == Types::False.instance\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"symbol?\") => Types::Builtin.new(\"symbol?\") do |mal|\n          if mal.is_a?(Types::Symbol)\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"keyword?\") => Types::Builtin.new(\"keyword?\") do |mal|\n          if mal.is_a?(Types::Keyword)\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"symbol\") => Types::Builtin.new(\"symbol\") do |string|\n          if string\n            Types::Symbol.for(string.value)\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"keyword\") => Types::Builtin.new(\"keyword\") do |keyword|\n          if keyword\n            Types::Keyword.for(keyword.value)\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"vector\") => Types::Builtin.new(\"vector\") do |*items|\n          vector = Types::Vector.new\n\n          items.each do |i|\n            vector << i\n          end\n\n          vector\n        end,\n\n        Types::Symbol.for(\"sequential?\") => Types::Builtin.new(\"sequential?\") do |list_or_vector|\n          case list_or_vector\n         when Types::List, Types::Vector\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"hash-map\") => Types::Builtin.new(\"hash-map\") do |*items|\n          raise UnbalancedHashmapError, \"unbalanced hashmap error, arguments must be even\" if items&.size&.odd?\n\n          hashmap = Types::Hashmap.new\n\n          items.each_slice(2) do |(k, v)|\n            hashmap[k] = v\n          end\n\n          hashmap\n        end,\n\n        Types::Symbol.for(\"map?\") => Types::Builtin.new(\"map?\") do |mal|\n          if mal.is_a?(Types::Hashmap)\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"assoc\") => Types::Builtin.new(\"assoc\") do |hashmap, *items|\n          raise UnbalancedHashmapError, \"unbalanced hashmap error, arguments must be even\" if items.size&.odd?\n\n          new_hashmap = hashmap.dup\n\n          items.each_slice(2) do |(k, v)|\n            new_hashmap[k] = v\n          end\n\n          new_hashmap\n        end,\n\n        Types::Symbol.for(\"dissoc\") => Types::Builtin.new(\"dissoc\") do |hashmap, *keys|\n          new_hashmap = Types::Hashmap.new\n\n          hashmap.keys.each do |k|\n            next if keys.include?(k)\n            new_hashmap[k] = hashmap[k]\n          end\n\n          new_hashmap\n        end,\n\n        Types::Symbol.for(\"get\") => Types::Builtin.new(\"get\") do |hashmap, key|\n          if Types::Hashmap === hashmap && key && hashmap.key?(key)\n            hashmap[key]\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"contains?\") => Types::Builtin.new(\"contains?\") do |hashmap, key|\n          if Types::Hashmap === hashmap && key && hashmap.key?(key)\n            Types::True.instance\n          else\n            Types::False.instance\n          end\n        end,\n\n        Types::Symbol.for(\"keys\") => Types::Builtin.new(\"keys\") do |hashmap|\n          if Types::Hashmap === hashmap\n            Types::List.new(hashmap.keys)\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"vals\") => Types::Builtin.new(\"vals\") do |hashmap|\n          if Types::Hashmap === hashmap\n            Types::List.new(hashmap.values)\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"readline\") => Types::Builtin.new(\"readline\") do |prompt = nil|\n          prompt =\n            if prompt.nil?\n              \"user> \"\n            else\n              prompt.value\n            end\n\n          input = Readline.readline(prompt)\n\n          if input.nil?\n            Types::Nil.instance\n          else\n            Types::String.new(input)\n          end\n        end,\n\n        Types::Symbol.for(\"meta\") => Types::Builtin.new(\"meta\") do |value|\n          case value\n          when Types::List, Types::Vector, Types::Hashmap, Types::Callable\n            value.meta\n          else\n            Types::Nil.instance\n          end\n        end,\n\n        Types::Symbol.for(\"with-meta\") => Types::Builtin.new(\"with-meta\") do |value, meta|\n          case value\n          when Types::List, Types::Vector, Types::Hashmap, Types::Callable\n            new_value = value.dup\n            new_value.meta = meta\n            new_value\n          else\n            raise TypeError, \"Unable to use meta with #{Mal.pr_str(value)}\"\n          end\n        end,\n\n        Types::Symbol.for(\"time-ms\") => Types::Builtin.new(\"time-ms\") do\n          Types::Number.new((Time.now.to_f.round(3) * 1000).to_i)\n        end,\n\n        Types::Symbol.for(\"conj\") => Types::Builtin.new(\"conj\") do |list_or_vector, *new_elems|\n          case list_or_vector\n          when Types::List\n            Types::List.new([*new_elems.reverse, *list_or_vector])\n          when Types::Vector\n            Types::Vector.new([*list_or_vector, *new_elems])\n          else\n            raise TypeError, \"Unable to `conj` with <#{Mal.pr_str(list_or_vector)}>, must be list or vector\"\n          end\n        end,\n\n        Types::Symbol.for(\"seq\") => Types::Builtin.new(\"seq\") do |sequential|\n          case sequential\n          when Types::List\n            if sequential.any?\n              sequential\n            else\n              Types::Nil.instance\n            end\n          when Types::Vector\n            if sequential.any?\n              Types::List.new(sequential)\n            else\n              Types::Nil.instance\n            end\n          when Types::String\n            if !sequential.value.empty?\n              Types::List.new(sequential.value.chars.map { |c| Types::String.new(c) })\n            else\n              Types::Nil.instance\n            end\n          when Types::Nil\n            Types::Nil.instance\n          else\n            raise TypeError, \"Unable to `seq` with <#{Mal.pr_str(sequential)}>, must be list, vector, string, or nil\"\n          end\n        end\n      }\n    end\n  end\nend\n"
  },
  {
    "path": "impls/ruby.2/env.rb",
    "content": "require_relative \"errors\"\nrequire_relative \"types\"\n\nmodule Mal\n  class Env\n    def initialize(outer = nil, binds = Types::List.new, exprs = Types::List.new)\n      @outer = outer\n      @data = {}\n\n      spread_next = false\n      binds.each_with_index do |b, i|\n        if b.value == \"&\"\n          spread_next = true\n        else\n          if spread_next\n            set(b, Types::List.new(exprs[(i - 1)..]) || Types::Nil.instance)\n            break\n          else\n            set(b, exprs[i] || Types::Nil.instance)\n          end\n        end\n      end\n    end\n\n    def set(k, v)\n      @data[k] = v\n    end\n\n    def get(k)\n      if @data.key?(k)\n        @data[k]\n      elsif !@outer.nil?\n        @outer.get(k)\n      else\n        0\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "impls/ruby.2/errors.rb",
    "content": "module Mal\n  class Error < ::StandardError; end\n  class TypeError < ::TypeError; end\n\n  class MalError < Error\n    attr_reader :value\n\n    def initialize(value)\n      @value = value\n    end\n\n    def message\n      value.inspect\n    end\n  end\n\n  class FileNotFoundError < Error; end\n  class IndexError < TypeError; end\n  class SkipCommentError < Error; end\n\n  class InvalidHashmapKeyError < TypeError; end\n  class InvalidIfExpressionError < TypeError; end\n  class InvalidLetBindingsError < TypeError; end\n  class InvalidReaderPositionError < Error; end\n  class InvalidTypeError < TypeError; end\n\n  class NotCallableError < Error; end\n\n  class SymbolNotFoundError < Error; end\n  class SyntaxError < TypeError; end\n\n  class UnbalancedEscapingError < Error; end\n  class UnbalancedHashmapError < Error; end\n  class UnbalancedListError < Error; end\n  class UnbalancedStringError < Error; end\n  class UnbalancedVectorError < Error; end\n\n  class UnknownError < Error\n    attr_reader :original_error\n\n    def initialize(original_error)\n      @original_error = original_error\n    end\n\n    def inspect\n      \"UnknownError :: #{original_error.inspect}\"\n    end\n\n    def message\n      \"UnknownError<#{original_error.class}> :: #{original_error.message}\"\n    end\n  end\nend\n"
  },
  {
    "path": "impls/ruby.2/printer.rb",
    "content": "require_relative \"errors\"\nrequire_relative \"types\"\n\nmodule Mal\n  extend self\n\n  def pr_str(mal, print_readably = false)\n    case mal\n    when Types::List\n      \"(#{mal.map { |m| pr_str(m, print_readably) }.join(\" \")})\"\n    when Types::Vector\n      \"[#{mal.map { |m| pr_str(m, print_readably) }.join(\" \")}]\"\n    when Types::Hashmap\n      \"{#{mal.map { |k, v| [pr_str(k, print_readably), pr_str(v, print_readably)].join(\" \") }.join(\" \")}}\"\n    when Types::Keyword\n      if print_readably\n        pr_str_keyword(mal)\n      else\n        \":#{mal.value}\"\n      end\n    when Types::String\n      if print_readably\n        pr_str_string(mal)\n      else\n        mal.value\n      end\n    when Types::Atom\n      \"(atom #{pr_str(mal.value, print_readably)})\"\n    when Types::Base, Types::Callable\n      mal.inspect\n    else\n      raise InvalidTypeError, \"unable to print value <#{mal.inspect}>\"\n    end\n  end\n\n  def pr_str_keyword(mal)\n    value = mal.value.dup\n\n    value.gsub!('\\\\','\\\\\\\\\\\\\\\\')\n    value.gsub!(\"\\n\",'\\n')\n    value.gsub!('\"','\\\"')\n\n    \":#{value}\"\n  end\n\n  def pr_str_string(mal)\n    value = mal.value.dup\n\n    value.gsub!('\\\\','\\\\\\\\\\\\\\\\')\n    value.gsub!(\"\\n\",'\\n')\n    value.gsub!('\"','\\\"')\n\n    \"\\\"#{value}\\\"\"\n  end\nend\n"
  },
  {
    "path": "impls/ruby.2/reader.rb",
    "content": "require_relative \"errors\"\nrequire_relative \"types\"\n\nmodule Mal\n  extend self\n\n  TOKEN_REGEX = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/\n\n  def read_atom(reader)\n    case reader.peek\n    when /\\A\"(?:\\\\.|[^\\\\\"])*\"\\z/\n      read_string(reader)\n    when /\\A\"/\n      raise UnbalancedStringError, \"unbalanced string << #{reader.peek.inspect} >>\"\n    when /\\A:/\n      read_keyword(reader)\n    when \"nil\"\n      read_nil(reader)\n    when \"true\"\n      read_true(reader)\n    when \"false\"\n      read_false(reader)\n    when /\\A-?\\d+(\\.\\d+)?/\n      read_number(reader)\n    when /\\A;/\n      raise SkipCommentError\n    else\n      read_symbol(reader)\n    end\n  end\n\n  def read_deref(reader)\n    list = Types::List.new\n    list << Types::Symbol.for(\"deref\")\n    list << read_form(reader)\n    list\n  end\n\n  def read_false(reader)\n    reader.advance!\n    Types::False.instance\n  end\n\n  def read_form(reader)\n    case reader.peek\n    when \"'\"\n      read_quote(reader.advance!)\n    when \"`\"\n      read_quasiquote(reader.advance!)\n    when \"~\"\n      read_unquote(reader.advance!)\n    when \"~@\"\n      read_splice_unquote(reader.advance!)\n    when \"@\"\n      read_deref(reader.advance!)\n    when \"^\"\n      read_with_metadata(reader.advance!)\n    when \"(\"\n      read_list(reader.advance!)\n    when \"[\"\n      read_vector(reader.advance!)\n    when \"{\"\n      read_hashmap(reader.advance!)\n    else\n      read_atom(reader)\n    end\n  end\n\n  def read_hashmap(reader)\n    hashmap = Types::Hashmap.new\n\n    until reader.peek == \"}\"\n      key = read_form(reader)\n\n      unless Types::String === key || Types::Keyword === key\n        raise InvalidHashmapKeyError, \"invalid hashmap key, must be string or keyword\"\n      end\n\n      if reader.peek != \"}\"\n        value = read_form(reader)\n      else \n        raise UnbalancedHashmapError, \"unbalanced hashmap error, missing closing '}'\"\n      end\n\n      hashmap[key] = value\n    end\n\n    reader.advance!\n    hashmap\n  rescue Error => e\n    case e\n    when InvalidReaderPositionError\n      raise UnbalancedHashmapError, \"unbalanced hashmap error, missing closing '}'\"\n    else\n      raise e\n    end\n  end\n\n  def read_keyword(reader)\n    value = reader.next.dup[1...]\n    substitute_escaped_chars!(value)\n\n    Types::Keyword.for(value)\n  end\n\n  def read_list(reader)\n    list = Types::List.new\n\n    until reader.peek == \")\"\n      list << read_form(reader)\n    end\n\n    reader.advance!\n    list\n  rescue Error => e\n    case e\n    when InvalidReaderPositionError\n      raise UnbalancedListError, \"unbalanced list error, missing closing ')'\"\n    else\n      raise e\n    end\n  end\n\n  def read_nil(reader)\n    reader.advance!\n    Types::Nil.instance\n  end\n\n  def read_number(reader)\n    case reader.peek\n    when /\\d+\\.\\d+/\n      Types::Number.new(reader.next.to_f)\n    when /\\d+/\n      Types::Number.new(reader.next.to_i)\n    else\n      raise InvalidTypeError, \"invalid number syntax, only supports integers/floats\"\n    end\n  end\n\n  def read_quasiquote(reader)\n    list = Types::List.new\n    list << Types::Symbol.for(\"quasiquote\")\n    list << read_form(reader)\n    list\n  end\n\n  def read_quote(reader)\n    list = Types::List.new\n    list << Types::Symbol.for(\"quote\")\n    list << read_form(reader)\n    list\n  end\n\n  def read_splice_unquote(reader)\n    list = Types::List.new\n    list << Types::Symbol.for(\"splice-unquote\")\n    list << read_form(reader)\n    list\n  end\n\n  def read_str(input)\n    tokenized = tokenize(input)\n    raise SkipCommentError if tokenized.empty?\n    read_form(Reader.new(tokenized))\n  end\n\n  def read_string(reader)\n    raw_value = reader.next.dup\n\n    value = raw_value[1...-1]\n    substitute_escaped_chars!(value)\n\n    if raw_value.length <= 1 || raw_value[-1] != '\"'\n      raise UnbalancedStringError, \"unbalanced string error, missing closing '\\\"'\"\n    end\n\n    Types::String.new(value)\n  end\n\n  def read_symbol(reader)\n    Types::Symbol.for(reader.next)\n  end\n\n  def read_true(reader)\n    reader.advance!\n    Types::True.instance\n  end\n\n  def read_unquote(reader)\n    list = Types::List.new\n    list << Types::Symbol.for(\"unquote\")\n    list << read_form(reader)\n    list\n  end\n\n  def read_vector(reader)\n    vector = Types::Vector.new\n\n    until reader.peek == \"]\"\n      vector << read_form(reader)\n    end\n\n    reader.advance!\n    vector\n  rescue Error => e\n    case e\n    when InvalidReaderPositionError\n      raise UnbalancedVectorError, \"unbalanced vector error, missing closing ']'\"\n    else\n      raise e\n    end\n  end\n\n  def read_with_metadata(reader)\n    list = Types::List.new\n    list << Types::Symbol.for(\"with-meta\")\n\n    first = read_form(reader)\n    second = read_form(reader)\n\n    list << second\n    list << first\n\n    list\n  end\n\n  def tokenize(input)\n    input.scan(TOKEN_REGEX).flatten.each_with_object([]) do |token, tokens|\n      if token != \"\" && !token.start_with?(\";\")\n        tokens << token\n      end\n    end\n  end\n\n  class Reader\n    attr_reader :tokens\n\n    def initialize(tokens)\n      @position = 0\n      @tokens = tokens\n    end\n\n    def advance!\n      @position += 1\n      self\n    end\n\n    def next\n      value = peek\n      @position += 1\n      value\n    end\n\n    def peek\n      if @position > @tokens.size - 1\n        raise InvalidReaderPositionError, \"invalid reader position error, unable to parse mal expression\"\n      end\n\n      @tokens[@position]\n    end\n  end\n\n  private\n\n  def substitute_escaped_chars!(string_or_keyword)\n    string_or_keyword.gsub!(/\\\\./, {\"\\\\\\\\\" => \"\\\\\", \"\\\\n\" => \"\\n\", \"\\\\\\\"\" => '\"'})\n  end\nend\n"
  },
  {
    "path": "impls/ruby.2/run",
    "content": "#!/usr/bin/env bash\nexec ruby $(dirname $0)/${STEP:-stepA_mal}.rb \"${@}\"\n"
  },
  {
    "path": "impls/ruby.2/step0_repl.rb",
    "content": "require \"readline\"\n\nmodule Mal\n  extend self\n\n  def READ(input)\n    input\n  end\n\n  def EVAL(input)\n    input\n  end\n\n  def PRINT(input)\n    input\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input)))\n  end\nend\n\nwhile input = Readline.readline(\"user> \")\n  puts Mal.rep(input)\nend\n"
  },
  {
    "path": "impls/ruby.2/step1_read_print.rb",
    "content": "require \"readline\"\n\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(input)\n    input\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input)))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  rescue SkipCommentError\n    nil\n  end\nend\n\nwhile input = Readline.readline(\"user> \")\n  puts Mal.rep(input)\nend\n\nputs\n"
  },
  {
    "path": "impls/ruby.2/step2_eval.rb",
    "content": "require \"readline\"\n\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  @repl_env = {\n    '+' => -> (a, b) { a + b },\n    '-' => -> (a, b) { a - b },\n    '*' => -> (a, b) { a * b },\n    '/' => -> (a, b) { a / b },\n  }\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    # puts \"EVAL: #{pr_str(ast, true)}\"\n\n\n    case ast\n    when Types::Symbol\n      if @repl_env.key?(ast.value)\n        @repl_env[ast.value]\n      else\n        raise SymbolNotFoundError, \"Error! Symbol #{ast.value} not found.\"\n      end\n    when Types::Vector\n      vec = Types::Vector.new\n      ast.each { |i| vec << EVAL(i, environment) }\n      return vec\n    when Types::Hashmap\n      hashmap = Types::Hashmap.new\n      ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n      return hashmap\n    when Types::List\n      if ast.size == 0\n        return ast\n      end\n\n      evaluated = Types::List.new\n      ast.each { |i| evaluated << EVAL(i, environment) }\n      maybe_callable = evaluated.first\n\n      if maybe_callable.respond_to?(:call)\n        maybe_callable.call(*evaluated[1..])\n      else\n        raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n      end\n    else\n      return ast\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  end\n\nend\n\nwhile input = Readline.readline(\"user> \")\n  puts Mal.rep(input)\nend\n\nputs\n\n\n"
  },
  {
    "path": "impls/ruby.2/step3_env.rb",
    "content": "require \"readline\"\n\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  @repl_env = Env.new\n  @repl_env.set(Types::Symbol.for('+'), -> (a, b) { a + b })\n  @repl_env.set(Types::Symbol.for('-'), -> (a, b) { a - b })\n  @repl_env.set(Types::Symbol.for('*'), -> (a, b) { a * b })\n  @repl_env.set(Types::Symbol.for('/'), -> (a, b) { a / b })\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n    when 0, Types::Nil, Types::False\n    else\n      puts \"EVAL: #{pr_str(ast, true)}\"\n    end\n\n    case ast\n    when Types::Symbol\n      value = environment.get(ast)\n      if value == 0\n        raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n      end\n      return value\n    when Types::Vector\n      vec = Types::Vector.new\n      ast.each { |i| vec << EVAL(i, environment) }\n      return vec\n    when Types::Hashmap\n      hashmap = Types::Hashmap.new\n      ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n      return hashmap\n    when Types::List\n      if ast.size == 0\n        return ast\n      end\n      case ast.first\n      when Types::Symbol.for(\"def!\")\n        _, sym, val = ast\n        environment.set(sym, EVAL(val, environment))\n      when Types::Symbol.for(\"let*\")\n        e = Env.new(environment)\n        _, bindings, val = ast\n\n        unless Types::List === bindings || Types::Vector === bindings\n          raise InvalidLetBindingsError\n        end\n\n        until bindings.empty?\n          k, v = bindings.shift(2)\n\n          raise InvalidLetBindingsError if k.nil?\n          v = Types::Nil.instance if v.nil?\n\n          e.set(k, EVAL(v, e))\n        end\n\n        if !val.nil?\n          EVAL(val, e)\n        else\n          Types::Nil.instance\n        end\n      else\n        evaluated = Types::List.new\n        ast.each { |i| evaluated << EVAL(i, environment) }\n        maybe_callable = evaluated.first\n\n        if maybe_callable.respond_to?(:call)\n          maybe_callable.call(*evaluated[1..])\n        else\n          raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n        end\n      end\n    else\n      return ast\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  end\n\nend\n\nwhile input = Readline.readline(\"user> \")\n  puts Mal.rep(input)\nend\n\nputs\n\n\n"
  },
  {
    "path": "impls/ruby.2/step4_if_fn_do.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n    when 0, Types::Nil, Types::False\n    else\n      puts \"EVAL: #{pr_str(ast, true)}\"\n    end\n\n    case ast\n    when Types::Symbol\n      value = environment.get(ast)\n      if value == 0\n        raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n      end\n      return value\n    when Types::Vector\n      vec = Types::Vector.new\n      ast.each { |i| vec << EVAL(i, environment) }\n      return vec\n    when Types::Hashmap\n      hashmap = Types::Hashmap.new\n      ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n      return hashmap\n    when Types::List\n      if ast.size == 0\n        return ast\n      end\n      case ast.first\n      when Types::Symbol.for(\"def!\")\n        _, sym, val = ast\n        environment.set(sym, EVAL(val, environment))\n      when Types::Symbol.for(\"let*\")\n        e = Env.new(environment)\n        _, bindings, val = ast\n\n        unless Types::List === bindings || Types::Vector === bindings\n          raise InvalidLetBindingsError\n        end\n\n        until bindings.empty?\n          k, v = bindings.shift(2)\n\n          raise InvalidLetBindingsError if k.nil?\n          v = Types::Nil.instance if v.nil?\n\n          e.set(k, EVAL(v, e))\n        end\n\n        if !val.nil?\n          EVAL(val, e)\n        else\n          Types::Nil.instance\n        end\n      when Types::Symbol.for(\"do\")\n        _, *values = ast\n\n        if !values.nil?\n          evaluated = Types::List.new\n\n          values.each do |v|\n            evaluated << EVAL(v, environment)\n          end\n\n          evaluated.last\n        else\n          Types::Nil.instance\n        end\n      when Types::Symbol.for(\"if\")\n        _, condition, when_true, when_false = ast\n\n        case EVAL(condition, environment)\n        when Types::False.instance, Types::Nil.instance\n          if !when_false.nil?\n            EVAL(when_false, environment)\n          else\n            Types::Nil.instance\n          end\n        else\n          if !when_true.nil?\n            EVAL(when_true, environment)\n          else\n            raise InvalidIfExpressionError\n          end\n        end\n      when Types::Symbol.for(\"fn*\")\n        _, binds, to_eval = ast\n\n        Types::Function.new(to_eval, binds, environment) do |*exprs|\n          EVAL(to_eval, Env.new(environment, binds, exprs))\n        end\n      else\n        evaluated = Types::List.new\n        ast.each { |i| evaluated << EVAL(i, environment) }\n        maybe_callable = evaluated.first\n\n        if maybe_callable.respond_to?(:call)\n          maybe_callable.call(Types::Args.new(evaluated[1..]))\n        else\n          raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n        end\n      end\n    else\n      return ast\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  end\n\nend\n\nMal.boot_repl!\n\nwhile input = Readline.readline(\"user> \")\n  puts Mal.rep(input)\nend\n\nputs\n\n\n"
  },
  {
    "path": "impls/ruby.2/step5_tco.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    loop do\n\n      case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n      when 0, Types::Nil, Types::False\n      else\n        puts \"EVAL: #{pr_str(ast, true)}\"\n      end\n\n      case ast\n      when Types::Symbol\n        value = environment.get(ast)\n        if value == 0\n          raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n        end\n        return value\n      when Types::Vector\n        vec = Types::Vector.new\n        ast.each { |i| vec << EVAL(i, environment) }\n        return vec\n      when Types::Hashmap\n        hashmap = Types::Hashmap.new\n        ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n        return hashmap\n      when Types::List\n        if ast.size == 0\n          return ast\n        end\n        case ast.first\n        when Types::Symbol.for(\"def!\")\n          _, sym, val = ast\n          return environment.set(sym, EVAL(val, environment))\n        when Types::Symbol.for(\"let*\")\n          e = Env.new(environment)\n          _, bindings, val = ast\n\n          unless Types::List === bindings || Types::Vector === bindings\n            raise InvalidLetBindingsError\n          end\n\n          until bindings.empty?\n            k, v = bindings.shift(2)\n\n            raise InvalidLetBindingsError if k.nil?\n            v = Types::Nil.instance if v.nil?\n\n            e.set(k, EVAL(v, e))\n          end\n\n          if !val.nil?\n            # Continue loop\n            ast = val\n            environment = e\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"do\")\n          _, *values = ast\n\n          if !values.nil? && values.any?\n            values[0...-1].each do |v|\n              EVAL(v, environment)\n            end\n\n            # Continue loop\n            ast = values.last\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"if\")\n          _, condition, when_true, when_false = ast\n\n          case EVAL(condition, environment)\n          when Types::False.instance, Types::Nil.instance\n            if !when_false.nil?\n              # Continue loop\n              ast = when_false\n            else\n              return Types::Nil.instance\n            end\n          else\n            if !when_true.nil?\n              # Continue loop\n              ast = when_true\n            else\n              raise InvalidIfExpressionError\n            end\n          end\n        when Types::Symbol.for(\"fn*\")\n          _, binds, to_eval = ast\n\n          return Types::Function.new(to_eval, binds, environment) do |*exprs|\n            EVAL(to_eval, Env.new(environment, binds, exprs))\n          end\n        else\n          maybe_callable = EVAL(ast.first, environment)\n          if !maybe_callable.respond_to?(:call)\n            raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n          end\n          args = Types::List.new\n          ast[1..].each { |i| args << EVAL(i, environment) }\n          if maybe_callable.is_mal_fn?\n            # Continue loop\n            ast = maybe_callable.ast\n            environment = Env.new(\n              maybe_callable.env,\n              maybe_callable.params,\n              args,\n            )\n          else\n            return maybe_callable.call(Types::Args.new(args))\n          end\n        end\n      else\n        return ast\n      end\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  end\n\nend\n\nMal.boot_repl!\n\nwhile input = Readline.readline(\"user> \")\n  puts Mal.rep(input)\nend\n\nputs\n\n\n"
  },
  {
    "path": "impls/ruby.2/step6_file.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    @repl_env.set(\n      Types::Symbol.for(\"eval\"),\n\n      Types::Builtin.new(\"eval\") do |mal|\n        Mal.EVAL(mal, @repl_env)\n      end\n    )\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n    Mal.rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    Mal.rep(\"(def! *ARGV* (list))\") if !run_application?\n  end\n\n  def run_application?\n    ARGV.any?\n  end\n\n  def run!\n    Mal.rep(\"(def! *ARGV* (list #{ARGV[1..].map(&:inspect).join(\" \")}))\")\n    Mal.rep(\"(load-file #{ARGV.first.inspect})\")\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    loop do\n\n      case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n      when 0, Types::Nil, Types::False\n      else\n        puts \"EVAL: #{pr_str(ast, true)}\"\n      end\n\n      case ast\n      when Types::Symbol\n        value = environment.get(ast)\n        if value == 0\n          raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n        end\n        return value\n      when Types::Vector\n        vec = Types::Vector.new\n        ast.each { |i| vec << EVAL(i, environment) }\n        return vec\n      when Types::Hashmap\n        hashmap = Types::Hashmap.new\n        ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n        return hashmap\n      when Types::List\n        if ast.size == 0\n          return ast\n        end\n        case ast.first\n        when Types::Symbol.for(\"def!\")\n          _, sym, val = ast\n          return environment.set(sym, EVAL(val, environment))\n        when Types::Symbol.for(\"let*\")\n          e = Env.new(environment)\n          _, bindings, val = ast\n\n          unless Types::List === bindings || Types::Vector === bindings\n            raise InvalidLetBindingsError\n          end\n\n          until bindings.empty?\n            k, v = bindings.shift(2)\n\n            raise InvalidLetBindingsError if k.nil?\n            v = Types::Nil.instance if v.nil?\n\n            e.set(k, EVAL(v, e))\n          end\n\n          if !val.nil?\n            # Continue loop\n            ast = val\n            environment = e\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"do\")\n          _, *values = ast\n\n          if !values.nil? && values.any?\n            values[0...-1].each do |v|\n              EVAL(v, environment)\n            end\n\n            # Continue loop\n            ast = values.last\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"if\")\n          _, condition, when_true, when_false = ast\n\n          case EVAL(condition, environment)\n          when Types::False.instance, Types::Nil.instance\n            if !when_false.nil?\n              # Continue loop\n              ast = when_false\n            else\n              return Types::Nil.instance\n            end\n          else\n            if !when_true.nil?\n              # Continue loop\n              ast = when_true\n            else\n              raise InvalidIfExpressionError\n            end\n          end\n        when Types::Symbol.for(\"fn*\")\n          _, binds, to_eval = ast\n\n          return Types::Function.new(to_eval, binds, environment) do |*exprs|\n            EVAL(to_eval, Env.new(environment, binds, exprs))\n          end\n        else\n          maybe_callable = EVAL(ast.first, environment)\n          if !maybe_callable.respond_to?(:call)\n            raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n          end\n          args = Types::List.new\n          ast[1..].each { |i| args << EVAL(i, environment) }\n          if maybe_callable.is_mal_fn?\n            # Continue loop\n            ast = maybe_callable.ast\n            environment = Env.new(\n              maybe_callable.env,\n              maybe_callable.params,\n              args,\n            )\n          else\n            return maybe_callable.call(Types::Args.new(args))\n          end\n        end\n      else\n        return ast\n      end\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  rescue SkipCommentError\n    nil\n  end\n\nend\n\nMal.boot_repl!\n\nif Mal.run_application?\n  Mal.run!\nelse\n  while input = Readline.readline(\"user> \")\n    val = Mal.rep(input)\n    puts val unless val.nil?\n  end\n\n  puts\nend\n"
  },
  {
    "path": "impls/ruby.2/step7_quote.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    @repl_env.set(\n      Types::Symbol.for(\"eval\"),\n\n      Types::Builtin.new(\"eval\") do |mal|\n        Mal.EVAL(mal, @repl_env)\n      end\n    )\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n    Mal.rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    Mal.rep(\"(def! *ARGV* (list))\") if !run_application?\n  end\n\n  def run_application?\n    ARGV.any?\n  end\n\n  def run!\n    Mal.rep(\"(def! *ARGV* (list #{ARGV[1..].map(&:inspect).join(\" \")}))\")\n    Mal.rep(\"(load-file #{ARGV.first.inspect})\")\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    loop do\n\n      case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n      when 0, Types::Nil, Types::False\n      else\n        puts \"EVAL: #{pr_str(ast, true)}\"\n      end\n\n      case ast\n      when Types::Symbol\n        value = environment.get(ast)\n        if value == 0\n          raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n        end\n        return value\n      when Types::Vector\n        vec = Types::Vector.new\n        ast.each { |i| vec << EVAL(i, environment) }\n        return vec\n      when Types::Hashmap\n        hashmap = Types::Hashmap.new\n        ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n        return hashmap\n      when Types::List\n        if ast.size == 0\n          return ast\n        end\n        case ast.first\n        when Types::Symbol.for(\"def!\")\n          _, sym, val = ast\n          return environment.set(sym, EVAL(val, environment))\n        when Types::Symbol.for(\"let*\")\n          e = Env.new(environment)\n          _, bindings, val = ast\n\n          unless Types::List === bindings || Types::Vector === bindings\n            raise InvalidLetBindingsError\n          end\n\n          until bindings.empty?\n            k, v = bindings.shift(2)\n\n            raise InvalidLetBindingsError if k.nil?\n            v = Types::Nil.instance if v.nil?\n\n            e.set(k, EVAL(v, e))\n          end\n\n          if !val.nil?\n            # Continue loop\n            ast = val\n            environment = e\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"do\")\n          _, *values = ast\n\n          if !values.nil? && values.any?\n            values[0...-1].each do |v|\n              EVAL(v, environment)\n            end\n\n            # Continue loop\n            ast = values.last\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"if\")\n          _, condition, when_true, when_false = ast\n\n          case EVAL(condition, environment)\n          when Types::False.instance, Types::Nil.instance\n            if !when_false.nil?\n              # Continue loop\n              ast = when_false\n            else\n              return Types::Nil.instance\n            end\n          else\n            if !when_true.nil?\n              # Continue loop\n              ast = when_true\n            else\n              raise InvalidIfExpressionError\n            end\n          end\n        when Types::Symbol.for(\"fn*\")\n          _, binds, to_eval = ast\n\n          return Types::Function.new(to_eval, binds, environment) do |*exprs|\n            EVAL(to_eval, Env.new(environment, binds, exprs))\n          end\n        when Types::Symbol.for(\"quote\")\n          _, ret = ast\n          return ret\n        when Types::Symbol.for(\"quasiquote\")\n          _, ast_rest = ast\n          ast = quasiquote(ast_rest)\n        else\n          maybe_callable = EVAL(ast.first, environment)\n          if !maybe_callable.respond_to?(:call)\n            raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n          end\n          args = Types::List.new\n          ast[1..].each { |i| args << EVAL(i, environment) }\n          if maybe_callable.is_mal_fn?\n            # Continue loop\n            ast = maybe_callable.ast\n            environment = Env.new(\n              maybe_callable.env,\n              maybe_callable.params,\n              args,\n            )\n          else\n            return maybe_callable.call(Types::Args.new(args))\n          end\n        end\n      else\n        return ast\n      end\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  rescue SkipCommentError\n    nil\n  end\n\n  def quasiquote_list(mal)\n    result = Types::List.new\n\n    mal.reverse_each do |elt|\n      if elt.is_a?(Types::List) && elt.first == Types::Symbol.for(\"splice-unquote\")\n        result = Types::List.new([\n          Types::Symbol.for(\"concat\"),\n          elt[1],\n          result\n        ])\n      else\n        result = Types::List.new([\n          Types::Symbol.for(\"cons\"),\n          quasiquote(elt),\n          result\n        ])\n      end\n    end\n\n    result\n  end\n\n  def quasiquote(mal)\n    case mal\n    when Types::List\n      if mal.first == Types::Symbol.for(\"unquote\")\n        mal[1]\n      else\n        quasiquote_list(mal)\n      end\n    when Types::Vector\n      Types::List.new([\n        Types::Symbol.for(\"vec\"),\n        quasiquote_list(mal)\n      ])\n    when Types::Hashmap, Types::Symbol\n      Types::List.new([\n        Types::Symbol.for(\"quote\"),\n        mal\n      ])\n    else\n      mal\n    end\n  end\nend\n\nMal.boot_repl!\n\nif Mal.run_application?\n  Mal.run!\nelse\n  while input = Readline.readline(\"user> \")\n    val = Mal.rep(input)\n    puts val unless val.nil?\n  end\n\n  puts\nend\n"
  },
  {
    "path": "impls/ruby.2/step8_macros.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    @repl_env.set(\n      Types::Symbol.for(\"eval\"),\n\n      Types::Builtin.new(\"eval\") do |mal|\n        Mal.EVAL(mal, @repl_env)\n      end\n    )\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n    Mal.rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    Mal.rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n    if !run_application?\n      Mal.rep(\"(def! *ARGV* (list))\")\n      Mal.rep(\"(println (str \\\"Mal [\\\" \\*host-language\\* \\\"]\\\"))\")\n    end\n  end\n\n  def run_application?\n    ARGV.any?\n  end\n\n  def run!\n    Mal.rep(\"(def! *ARGV* (list #{ARGV[1..].map(&:inspect).join(\" \")}))\")\n    Mal.rep(\"(load-file #{ARGV.first.inspect})\")\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    loop do\n\n      case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n      when 0, Types::Nil, Types::False\n      else\n        puts \"EVAL: #{pr_str(ast, true)}\"\n      end\n\n      case ast\n      when Types::Symbol\n        value = environment.get(ast)\n        if value == 0\n          raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n        end\n        return value\n      when Types::Vector\n        vec = Types::Vector.new\n        ast.each { |i| vec << EVAL(i, environment) }\n        return vec\n      when Types::Hashmap\n        hashmap = Types::Hashmap.new\n        ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n        return hashmap\n      when Types::List\n        if ast.size == 0\n          return ast\n        end\n        case ast.first\n        when Types::Symbol.for(\"def!\")\n          _, sym, val = ast\n          return environment.set(sym, EVAL(val, environment))\n        when Types::Symbol.for(\"defmacro!\")\n          _, sym, val = ast\n          result = EVAL(val, environment)\n\n          case result\n          when Types::Function\n            return environment.set(sym, result.to_macro)\n          else\n            raise TypeError\n          end\n        when Types::Symbol.for(\"let*\")\n          e = Env.new(environment)\n          _, bindings, val = ast\n\n          unless Types::List === bindings || Types::Vector === bindings\n            raise InvalidLetBindingsError\n          end\n\n          until bindings.empty?\n            k, v = bindings.shift(2)\n\n            raise InvalidLetBindingsError if k.nil?\n            v = Types::Nil.instance if v.nil?\n\n            e.set(k, EVAL(v, e))\n          end\n\n          if !val.nil?\n            # Continue loop\n            ast = val\n            environment = e\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"do\")\n          _, *values = ast\n\n          if !values.nil? && values.any?\n            values[0...-1].each do |v|\n              EVAL(v, environment)\n            end\n\n            # Continue loop\n            ast = values.last\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"if\")\n          _, condition, when_true, when_false = ast\n\n          case EVAL(condition, environment)\n          when Types::False.instance, Types::Nil.instance\n            if !when_false.nil?\n              # Continue loop\n              ast = when_false\n            else\n              return Types::Nil.instance\n            end\n          else\n            if !when_true.nil?\n              # Continue loop\n              ast = when_true\n            else\n              raise InvalidIfExpressionError\n            end\n          end\n        when Types::Symbol.for(\"fn*\")\n          _, binds, to_eval = ast\n\n          return Types::Function.new(to_eval, binds, environment) do |*exprs|\n            EVAL(to_eval, Env.new(environment, binds, exprs))\n          end\n        when Types::Symbol.for(\"quote\")\n          _, ret = ast\n          return ret\n        when Types::Symbol.for(\"quasiquote\")\n          _, ast_rest = ast\n          ast = quasiquote(ast_rest)\n        else\n          maybe_callable = EVAL(ast.first, environment)\n          if !maybe_callable.respond_to?(:call)\n            raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n          end\n          raw_args = ast[1..]\n          if maybe_callable.is_macro?\n            if raw_args.any?\n              ast = maybe_callable.call(Types::Args.new(raw_args))\n            else\n              ast = maybe_callable.call\n            end\n            next\n          end\n          args = Types::List.new\n          raw_args.each { |i| args << EVAL(i, environment) }\n          if maybe_callable.is_mal_fn?\n            # Continue loop\n            ast = maybe_callable.ast\n            environment = Env.new(\n              maybe_callable.env,\n              maybe_callable.params,\n              args,\n            )\n          else\n            return maybe_callable.call(Types::Args.new(args))\n          end\n        end\n      else\n        return ast\n      end\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  rescue SkipCommentError\n    nil\n  rescue TypeError\n    nil\n  end\n\n  def quasiquote_list(mal)\n    result = Types::List.new\n\n    mal.reverse_each do |elt|\n      if elt.is_a?(Types::List) && elt.first == Types::Symbol.for(\"splice-unquote\")\n        result = Types::List.new([\n          Types::Symbol.for(\"concat\"),\n          elt[1],\n          result\n        ])\n      else\n        result = Types::List.new([\n          Types::Symbol.for(\"cons\"),\n          quasiquote(elt),\n          result\n        ])\n      end\n    end\n\n    result\n  end\n\n  def quasiquote(mal)\n    case mal\n    when Types::List\n      if mal.first == Types::Symbol.for(\"unquote\")\n        mal[1]\n      else\n        quasiquote_list(mal)\n      end\n    when Types::Vector\n      Types::List.new([\n        Types::Symbol.for(\"vec\"),\n        quasiquote_list(mal)\n      ])\n    when Types::Hashmap, Types::Symbol\n      Types::List.new([\n        Types::Symbol.for(\"quote\"),\n        mal\n      ])\n    else\n      mal\n    end\n  end\n\nend\n\nMal.boot_repl!\n\nif Mal.run_application?\n  Mal.run!\nelse\n  while input = Readline.readline(\"user> \")\n    val = Mal.rep(input)\n    puts val unless val.nil?\n  end\n\n  puts\nend\n"
  },
  {
    "path": "impls/ruby.2/step9_try.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    @repl_env.set(\n      Types::Symbol.for(\"eval\"),\n\n      Types::Builtin.new(\"eval\") do |mal|\n        Mal.EVAL(mal, @repl_env)\n      end\n    )\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n    Mal.rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    Mal.rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n    if !run_application?\n      Mal.rep(\"(def! *ARGV* (list))\")\n      Mal.rep(\"(println (str \\\"Mal [\\\" \\*host-language\\* \\\"]\\\"))\")\n    end\n  end\n\n  def run_application?\n    ARGV.any?\n  end\n\n  def run!\n    args = ARGV[1..].map(&:inspect)\n\n    if args.any?\n      Mal.rep(\"(def! *ARGV* (list #{args.join(\" \")}))\")\n    else\n      Mal.rep(\"(def! *ARGV* (list))\")\n    end\n\n    file = File.absolute_path(ARGV.first)\n\n    Dir.chdir(File.dirname(file)) do\n      Mal.rep(\"(load-file #{file.inspect})\")\n    end\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    loop do\n\n      case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n      when 0, Types::Nil, Types::False\n      else\n        puts \"EVAL: #{pr_str(ast, true)}\"\n      end\n\n      case ast\n      when Types::Symbol\n        value = environment.get(ast)\n        if value == 0\n          raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n        end\n        return value\n      when Types::Vector\n        vec = Types::Vector.new\n        ast.each { |i| vec << EVAL(i, environment) }\n        return vec\n      when Types::Hashmap\n        hashmap = Types::Hashmap.new\n        ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n        return hashmap\n      when Types::List\n        if ast.size == 0\n          return ast\n        end\n        case ast.first\n        when Types::Symbol.for(\"def!\")\n          _, sym, val = ast\n          return environment.set(sym, EVAL(val, environment))\n        when Types::Symbol.for(\"defmacro!\")\n          _, sym, val = ast\n          result = EVAL(val, environment)\n\n          case result\n          when Types::Function\n            return environment.set(sym, result.to_macro)\n          else\n            raise TypeError\n          end\n        when Types::Symbol.for(\"let*\")\n          e = Env.new(environment)\n          _, bindings, val = ast\n\n          unless Types::List === bindings || Types::Vector === bindings\n            raise InvalidLetBindingsError\n          end\n\n          until bindings.empty?\n            k, v = bindings.shift(2)\n\n            raise InvalidLetBindingsError if k.nil?\n            v = Types::Nil.instance if v.nil?\n\n            e.set(k, EVAL(v, e))\n          end\n\n          if !val.nil?\n            # Continue loop\n            ast = val\n            environment = e\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"do\")\n          _, *values = ast\n\n          if !values.nil? && values.any?\n            values[0...-1].each do |v|\n              EVAL(v, environment)\n            end\n\n            # Continue loop\n            ast = values.last\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"if\")\n          _, condition, when_true, when_false = ast\n\n          case EVAL(condition, environment)\n          when Types::False.instance, Types::Nil.instance\n            if !when_false.nil?\n              # Continue loop\n              ast = when_false\n            else\n              return Types::Nil.instance\n            end\n          else\n            if !when_true.nil?\n              # Continue loop\n              ast = when_true\n            else\n              raise InvalidIfExpressionError\n            end\n          end\n        when Types::Symbol.for(\"fn*\")\n          _, binds, to_eval = ast\n\n          return Types::Function.new(to_eval, binds, environment) do |*exprs|\n            EVAL(to_eval, Env.new(environment, binds, exprs))\n          end\n        when Types::Symbol.for(\"quote\")\n          _, ret = ast\n          return ret\n        when Types::Symbol.for(\"quasiquote\")\n          _, ast_rest = ast\n          ast = quasiquote(ast_rest)\n        when Types::Symbol.for(\"try*\")\n          _, to_try, catch_list = ast\n\n          begin\n            return EVAL(to_try, environment)\n          rescue => e\n            raise e if catch_list.nil? || catch_list&.empty?\n            raise SyntaxError, \"try* missing proper catch*\" unless catch_list&.first == Types::Symbol.for(\"catch*\")\n\n            _, exception_symbol, exception_handler = catch_list\n\n            value =\n              if e.is_a?(MalError)\n                e.value\n              else\n                Types::String.new(e.message)\n              end\n\n            return EVAL(\n              exception_handler,\n              Env.new(\n                environment,\n                Types::List.new([exception_symbol]),\n                Types::List.new([value])\n              )\n            )\n          end\n        else\n          maybe_callable = EVAL(ast.first, environment)\n          if !maybe_callable.respond_to?(:call)\n            raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n          end\n          raw_args = ast[1..]\n          if maybe_callable.is_macro?\n            if raw_args.any?\n              ast = maybe_callable.call(Types::Args.new(raw_args))\n            else\n              ast = maybe_callable.call\n            end\n            next\n          end\n          args = Types::List.new\n          raw_args.each { |i| args << EVAL(i, environment) }\n          if maybe_callable.is_mal_fn?\n            # Continue loop\n            ast = maybe_callable.ast\n            environment = Env.new(\n              maybe_callable.env,\n              maybe_callable.params,\n              args,\n            )\n          else\n            return maybe_callable.call(Types::Args.new(args))\n          end\n        end\n      else\n        return ast\n      end\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  rescue MalError => e\n    \"Error: #{pr_str(e.value, true)}\"\n  rescue Error, TypeError => e\n    \"#{e.class} -- #{e.message}\"\n  rescue SkipCommentError\n    nil\n  end\n\n  def quasiquote_list(mal)\n    result = Types::List.new\n\n    mal.reverse_each do |elt|\n      if elt.is_a?(Types::List) && elt.first == Types::Symbol.for(\"splice-unquote\")\n        result = Types::List.new([\n          Types::Symbol.for(\"concat\"),\n          elt[1],\n          result\n        ])\n      else\n        result = Types::List.new([\n          Types::Symbol.for(\"cons\"),\n          quasiquote(elt),\n          result\n        ])\n      end\n    end\n\n    result\n  end\n\n  def quasiquote(mal)\n    case mal\n    when Types::List\n      if mal.first == Types::Symbol.for(\"unquote\")\n        mal[1]\n      else\n        quasiquote_list(mal)\n      end\n    when Types::Vector\n      Types::List.new([\n        Types::Symbol.for(\"vec\"),\n        quasiquote_list(mal)\n      ])\n    when Types::Hashmap, Types::Symbol\n      Types::List.new([\n        Types::Symbol.for(\"quote\"),\n        mal\n      ])\n    else\n      mal\n    end\n  end\n\nend\n\nMal.boot_repl!\n\nif Mal.run_application?\n  Mal.run!\nelse\n  while input = Readline.readline(\"user> \")\n    val = Mal.rep(input)\n    puts val unless val.nil?\n  end\n\n  puts\nend\n"
  },
  {
    "path": "impls/ruby.2/stepA_mal.rb",
    "content": "require \"readline\"\n\nrequire_relative \"core\"\nrequire_relative \"env\"\nrequire_relative \"errors\"\nrequire_relative \"printer\"\nrequire_relative \"reader\"\n\nmodule Mal\n  extend self\n\n  def boot_repl!\n    @repl_env = Env.new\n\n    Core.ns.each do |k, v|\n      @repl_env.set(k, v)\n    end\n\n    @repl_env.set(\n      Types::Symbol.for(\"eval\"),\n\n      Types::Builtin.new(\"eval\") do |mal|\n        Mal.EVAL(mal, @repl_env)\n      end\n    )\n\n    Mal.rep(\"(def! not (fn* (a) (if a false true)))\")\n    Mal.rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    Mal.rep(\"(def! *host-language* \\\"ruby.2\\\")\")\n    Mal.rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n    if !run_application?\n      Mal.rep(\"(def! *ARGV* (list))\")\n      Mal.rep(\"(println (str \\\"Mal [\\\" \\*host-language\\* \\\"]\\\"))\")\n    end\n  end\n\n  def run_application?\n    ARGV.any?\n  end\n\n  def run!\n    args = ARGV[1..].map(&:inspect)\n\n    if args.any?\n      Mal.rep(\"(def! *ARGV* (list #{args.join(\" \")}))\")\n    else\n      Mal.rep(\"(def! *ARGV* (list))\")\n    end\n\n    file = File.absolute_path(ARGV.first)\n\n    Dir.chdir(File.dirname(file)) do\n      Mal.rep(\"(load-file #{file.inspect})\")\n    end\n  end\n\n  def READ(input)\n    read_str(input)\n  end\n\n  def EVAL(ast, environment)\n    loop do\n\n      case environment.get(Types::Symbol.for(\"DEBUG-EVAL\"))\n      when 0, Types::Nil, Types::False\n      else\n        puts \"EVAL: #{pr_str(ast, true)}\"\n      end\n\n      case ast\n      when Types::Symbol\n        value = environment.get(ast)\n        if value == 0\n          raise SymbolNotFoundError, \"'#{ast.value}' not found\"\n        end\n        return value\n      when Types::Vector\n        vec = Types::Vector.new\n        ast.each { |i| vec << EVAL(i, environment) }\n        return vec\n      when Types::Hashmap\n        hashmap = Types::Hashmap.new\n        ast.each { |k, v| hashmap[k] = EVAL(v, environment) }\n        return hashmap\n      when Types::List\n        if ast.size == 0\n          return ast\n        end\n        case ast.first\n        when Types::Symbol.for(\"def!\")\n          _, sym, val = ast\n          return environment.set(sym, EVAL(val, environment))\n        when Types::Symbol.for(\"defmacro!\")\n          _, sym, val = ast\n          result = EVAL(val, environment)\n\n          case result\n          when Types::Function\n            return environment.set(sym, result.to_macro)\n          else\n            raise TypeError, \"defmacro! must be bound to a function\"\n          end\n        when Types::Symbol.for(\"let*\")\n          e = Env.new(environment)\n          _, bindings, val = ast\n          bindings = bindings.dup # TODO note bugfix let bindings w/ TCO loop and destructive mutation (shift)\n\n          unless Types::List === bindings || Types::Vector === bindings\n            raise InvalidLetBindingsError, \"let* bindings must be a list or vector\"\n          end\n\n          until bindings.empty?\n            k, v = bindings.shift(2)\n\n            raise InvalidLetBindingsError, \"Invalid let* bindings 'nil' key\" if k.nil?\n            v = Types::Nil.instance if v.nil?\n\n            e.set(k, EVAL(v, e))\n          end\n\n          if !val.nil?\n            # Continue loop\n            ast = val\n            environment = e\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"do\")\n          _, *values = ast\n\n          if !values.nil? && values.any?\n            values[0...-1].each do |v|\n              EVAL(v, environment)\n            end\n\n            # Continue loop\n            ast = values.last\n          else\n            return Types::Nil.instance\n          end\n        when Types::Symbol.for(\"if\")\n          _, condition, when_true, when_false = ast\n\n          case EVAL(condition, environment)\n          when Types::False.instance, Types::Nil.instance\n            if !when_false.nil?\n              # Continue loop\n              ast = when_false\n            else\n              return Types::Nil.instance\n            end\n          else\n            if !when_true.nil?\n              # Continue loop\n              ast = when_true\n            else\n              raise InvalidIfExpressionError, \"No expression to evaluate when true\"\n            end\n          end\n        when Types::Symbol.for(\"fn*\")\n          _, binds, to_eval = ast\n\n          return Types::Function.new(to_eval, binds, environment) do |*exprs|\n            EVAL(to_eval, Env.new(environment, binds, exprs))\n          end\n        when Types::Symbol.for(\"quote\")\n          _, ret = ast\n          return ret\n        when Types::Symbol.for(\"quasiquote\")\n          _, ast_rest = ast\n          ast = quasiquote(ast_rest)\n        when Types::Symbol.for(\"try*\")\n          _, to_try, catch_list = ast\n\n          begin\n            return EVAL(to_try, environment)\n          rescue => e\n            raise e if catch_list.nil? || catch_list&.empty?\n            raise SyntaxError, \"try* missing proper catch*\" unless catch_list&.first == Types::Symbol.for(\"catch*\")\n\n            _, exception_symbol, exception_handler = catch_list\n\n            value =\n              if e.is_a?(MalError)\n                e.value\n              else\n                Types::String.new(e.message)\n              end\n\n            return EVAL(\n              exception_handler,\n              Env.new(\n                environment,\n                Types::List.new([exception_symbol]),\n                Types::List.new([value])\n              )\n            )\n          end\n        else\n          maybe_callable = EVAL(ast.first, environment)\n          if !maybe_callable.respond_to?(:call)\n            raise NotCallableError, \"Error! #{PRINT(maybe_callable)} is not callable.\"\n          end\n          raw_args = ast[1..]\n          if maybe_callable.is_macro?\n            if raw_args.any?\n              ast = maybe_callable.call(Types::Args.new(raw_args))\n            else\n              ast = maybe_callable.call\n            end\n            next\n          end\n          args = Types::List.new\n          raw_args.each { |i| args << EVAL(i, environment) }\n          if maybe_callable.is_mal_fn?\n            # Continue loop\n            ast = maybe_callable.ast\n            environment = Env.new(\n              maybe_callable.env,\n              maybe_callable.params,\n              args,\n            )\n          elsif args.any?\n            return maybe_callable.call(Types::Args.new(args))\n          else\n            return maybe_callable.call(Types::Args.new)\n          end\n        end\n      else\n        return ast\n      end\n    end\n  end\n\n  def PRINT(input)\n    pr_str(input, true)\n  end\n\n  def rep(input)\n    PRINT(EVAL(READ(input), @repl_env))\n  rescue InvalidHashmapKeyError => e\n    \"Error! Hashmap keys can only be strings or keywords.\"\n  rescue NotCallableError => e\n    e.message\n  rescue SymbolNotFoundError => e\n    e.message\n  rescue UnbalancedEscapingError => e\n    \"Error! Detected unbalanced escaping. Check for matching '\\\\'.\"\n  rescue UnbalancedHashmapError => e\n    \"Error! Detected unbalanced list. Check for matching '}'.\"\n  rescue UnbalancedListError => e\n    \"Error! Detected unbalanced list. Check for matching ')'.\"\n  rescue UnbalancedStringError => e\n    \"Error! Detected unbalanced string. Check for matching '\\\"'.\"\n  rescue UnbalancedVectorError => e\n    \"Error! Detected unbalanced list. Check for matching ']'.\"\n  rescue MalError => e\n    \"Error: #{pr_str(e.value, true)}\"\n  rescue Error, TypeError => e\n    \"#{e.class} -- #{e.message}\"\n  rescue SkipCommentError\n    nil\n  end\n\n  def quasiquote_list(mal)\n    result = Types::List.new\n\n    mal.reverse_each do |elt|\n      if elt.is_a?(Types::List) && elt.first == Types::Symbol.for(\"splice-unquote\")\n        result = Types::List.new([\n          Types::Symbol.for(\"concat\"),\n          elt[1],\n          result\n        ])\n      else\n        result = Types::List.new([\n          Types::Symbol.for(\"cons\"),\n          quasiquote(elt),\n          result\n        ])\n      end\n    end\n\n    result\n  end\n\n  def quasiquote(mal)\n    case mal\n    when Types::List\n      if mal.first == Types::Symbol.for(\"unquote\")\n        mal[1]\n      else\n        quasiquote_list(mal)\n      end\n    when Types::Vector\n      Types::List.new([\n        Types::Symbol.for(\"vec\"),\n        quasiquote_list(mal)\n      ])\n    when Types::Hashmap, Types::Symbol\n      Types::List.new([\n        Types::Symbol.for(\"quote\"),\n        mal\n      ])\n    else\n      mal\n    end\n  end\n\nend\n\nMal.boot_repl!\n\nif Mal.run_application?\n  Mal.run!\nelse\n  while input = Readline.readline(\"user> \")\n    val = Mal.rep(input)\n    puts val unless val.nil?\n  end\n\n  puts\nend\n"
  },
  {
    "path": "impls/ruby.2/types.rb",
    "content": "module Mal\n  module Types\n    class Args < ::Array\n    end\n\n    class List < ::Array\n      def meta\n        @meta ||= Types::Nil.instance\n      end\n\n      def meta=(value)\n        @meta = value\n      end\n\n      def to_list\n        self\n      end\n    end\n\n    class Vector < ::Array\n      def meta\n        @meta ||= Types::Nil.instance\n      end\n\n      def meta=(value)\n        @meta = value\n      end\n\n      def to_list\n        List.new(self)\n      end\n    end\n\n    class Hashmap < ::Hash\n      def meta\n        @meta ||= Types::Nil.instance\n      end\n\n      def meta=(value)\n        @meta = value\n      end\n    end\n\n    class Base < ::Struct.new(:value)\n      def inspect\n        value.inspect\n      end\n    end\n\n    class String < Base; end\n\n    class Atom < Base\n      def inspect\n        \"Atom<#{value.inspect}>\"\n      end\n    end\n\n    class Keyword < Base\n      def self.for(value)\n        @_keywords ||= {}\n\n        if @_keywords.key?(value)\n          @_keywords[value]\n        else\n          @_keywords[value] = new(value)\n        end\n      end\n    end\n\n    class Number < Base\n      def +(other)\n        self.class.new(value + other.value)\n      end\n\n      def -(other)\n        self.class.new(value - other.value)\n      end\n\n      def *(other)\n        self.class.new(value * other.value)\n      end\n\n      def /(other)\n        self.class.new(value / other.value)\n      end\n    end\n\n    class Symbol < Base\n      def self.for(value)\n        @_symbols ||= {}\n\n        if @_symbols.key?(value)\n          @_symbols[value]\n        else\n          @_symbols[value] = new(value)\n        end\n      end\n\n      def inspect\n        value\n      end\n    end\n\n    class Nil < Base\n      def self.instance\n        @_instance ||= new(nil)\n      end\n\n      def inspect\n        \"nil\"\n      end\n    end\n\n    class True < Base\n      def self.instance\n        @_instance ||= new(true)\n      end\n    end\n\n    class False < Base\n      def self.instance\n        @_instance ||= new(false)\n      end\n    end\n\n    class Callable\n      def initialize(&block)\n        @fn = block\n      end\n\n      def call(args = nil)\n        args = Types::Args.new if args.nil?\n        raise unless args.is_a?(Types::Args)\n        @fn.call(*args)\n      end\n\n      def inspect\n        raise NotImplementedError, \"invalid callable\"\n      end\n\n      def is_mal_fn?\n        false\n      end\n\n      def is_macro?\n        false\n      end\n\n      def meta\n        @meta ||= Types::Nil.instance\n      end\n\n      def meta=(value)\n        @meta = value\n      end\n    end\n\n    class Builtin < Callable\n      attr_reader :name\n\n      def initialize(name, &block)\n        @name = name\n        @fn = block\n      end\n\n      def inspect\n        \"#<builtin '#{name}'>\"\n      end\n    end\n\n    class Function < Callable\n      attr_reader :ast, :params, :env\n\n      def initialize(ast, params, env, &block)\n        @ast = ast\n        @params = params\n        @env = env\n        @fn = block\n      end\n\n      def inspect\n        \"#<function>\"\n      end\n\n      def is_mal_fn?\n        true\n      end\n\n      def to_macro\n        Macro.new(ast, params, env, &@fn)\n      end\n    end\n\n    class Macro < Callable\n      attr_reader :ast, :params, :env\n\n      def initialize(ast, params, env, &block)\n        @ast = ast\n        @params = params\n        @env = env\n        @fn = block\n      end\n\n      def inspect\n        \"#<macro>\"\n      end\n\n      def is_mal_fn?\n        true\n      end\n\n      def is_macro?\n        true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "impls/rust/.gitignore",
    "content": "./target\n"
  },
  {
    "path": "impls/rust/Cargo.toml",
    "content": "[package]\nname = \"rust2\"\nversion = \"0.1.0\"\nauthors = [\"root\"]\n\n[dependencies]\nrustyline = \"14.0\"\n\nregex = \"1.7\"\nitertools = \"0.13\"\nfnv = \"1.0.6\"\n\n\n[[bin]]\nname = \"step0_repl\"\npath = \"step0_repl.rs\"\n\n[[bin]]\nname = \"step1_read_print\"\npath = \"step1_read_print.rs\"\n\n[[bin]]\nname = \"step2_eval\"\npath = \"step2_eval.rs\"\n\n[[bin]]\nname = \"step3_env\"\npath = \"step3_env.rs\"\n\n[[bin]]\nname = \"step4_if_fn_do\"\npath = \"step4_if_fn_do.rs\"\n\n[[bin]]\nname = \"step5_tco\"\npath = \"step5_tco.rs\"\n\n[[bin]]\nname = \"step6_file\"\npath = \"step6_file.rs\"\n\n[[bin]]\nname = \"step7_quote\"\npath = \"step7_quote.rs\"\n\n[[bin]]\nname = \"step8_macros\"\npath = \"step8_macros.rs\"\n\n[[bin]]\nname = \"step9_try\"\npath = \"step9_try.rs\"\n\n[[bin]]\nname = \"stepA_mal\"\npath = \"stepA_mal.rs\"\n"
  },
  {
    "path": "impls/rust/Dockerfile",
    "content": "FROM ubuntu:25.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install cargo \\\n  librust-fnv-dev \\\n  librust-itertools-dev \\\n  librust-regex-dev \\\n  librust-rustyline-dev \\\n  rust-clippy rustfmt\n\nENV CARGO_HOME /mal\n"
  },
  {
    "path": "impls/rust/Makefile",
    "content": "\nEXEC_DIR := target/release\n\nUPPER_STEPS := $(EXEC_DIR)/step4_if_fn_do \\\n               $(EXEC_DIR)/step5_tco \\\n               $(EXEC_DIR)/step6_file \\\n               $(EXEC_DIR)/step7_quote \\\n               $(EXEC_DIR)/step8_macros \\\n               $(EXEC_DIR)/step9_try \\\n               $(EXEC_DIR)/stepA_mal\nSTEP0       := $(EXEC_DIR)/step0_repl\nSTEP1-2     := $(EXEC_DIR)/step1_read_print \\\n               $(EXEC_DIR)/step2_eval\nSTEP3       := $(EXEC_DIR)/step3_env\nSTEPS := $(STEP0) $(STEP1-2) $(STEP3) $(UPPER_STEPS)\n\nall: $(STEPS)\n\n$(STEPS): $(EXEC_DIR)/%: %.rs\n\tcargo build --release --bin $*\n\n$(STEPS): readline.rs\n$(STEP1-2) $(STEP3) $(UPPER_STEPS): types.rs reader.rs printer.rs\n$(STEP3) $(UPPER_STEPS): env.rs\n$(UPPER_STEPS): core.rs\n\nlint:\n\trustfmt *.rs\n\tcargo clippy\n\n.PHONY: clean lint all\n\nclean:\n\trm -fr target/\n\trm -f .mal-history *~ Cargo.lock\n"
  },
  {
    "path": "impls/rust/core.rs",
    "content": "use std::fs::File;\nuse std::io::Read;\nuse std::rc::Rc;\nuse std::time::{SystemTime, UNIX_EPOCH};\n\nuse crate::printer::pr_seq;\nuse crate::reader::read_str;\nuse crate::types::MalVal::{\n    Atom, Bool, Func, Hash, Int, Kwd, List, MalFunc, Nil, Str, Sym, Vector,\n};\nuse crate::types::{\n    list, FuncStruct, MalArgs, MalRet, MalVal, _assoc, error, func, hash_map, unwrap_map_key,\n    vector, wrap_map_key,\n};\nuse readline;\n\nmacro_rules! fn_t_int_int {\n    ($ret:ident, $fn:expr) => {{\n        |a: MalArgs| match (&a[0], &a[1]) {\n            (Int(a0), Int(a1)) => Ok($ret($fn(a0, a1))),\n            _ => error(\"expecting (int,int) args\"),\n        }\n    }};\n}\n\nmacro_rules! fn_is_type {\n  ($($ps:pat),*) => {{\n    |a:MalArgs| { Ok(Bool(match a[0] { $($ps => true,)* _ => false})) }\n  }};\n  ($p:pat if $e:expr) => {{\n    |a:MalArgs| { Ok(Bool(match a[0] { $p if $e => true, _ => false})) }\n  }};\n  ($p:pat if $e:expr,$($ps:pat),*) => {{\n    |a:MalArgs| { Ok(Bool(match a[0] { $p if $e => true, $($ps => true,)* _ => false})) }\n  }};\n}\n\nmacro_rules! fn_str {\n    ($fn:expr) => {{\n        |a: MalArgs| match &a[0] {\n            Str(a0) => $fn(&a0),\n            _ => error(\"expecting (str) arg\"),\n        }\n    }};\n}\n\nfn symbol(a: MalArgs) -> MalRet {\n    match a[0] {\n        Str(ref s) => Ok(Sym(s.to_string())),\n        _ => error(\"illegal symbol call\"),\n    }\n}\n\nfn readline(p: &str) -> MalRet {\n    match readline::readline(p) {\n        Some(s) => Ok(Str(s)),\n        None => Ok(Nil),\n    }\n}\n\nfn slurp(f: &str) -> MalRet {\n    let mut s = String::new();\n    match File::open(f).and_then(|mut f| f.read_to_string(&mut s)) {\n        Ok(_) => Ok(Str(s)),\n        Err(e) => error(&e.to_string()),\n    }\n}\n\nfn time_ms(_a: MalArgs) -> MalRet {\n    let ms_e = match SystemTime::now().duration_since(UNIX_EPOCH) {\n        Ok(d) => d,\n        Err(e) => return error(&format!(\"{:?}\", e)),\n    };\n    Ok(Int(\n        ms_e.as_secs() as i64 * 1000 + ms_e.subsec_nanos() as i64 / 1_000_000\n    ))\n}\n\nfn get(a: MalArgs) -> MalRet {\n    match a[0] {\n        Nil => Ok(Nil),\n        Hash(ref hm, _) => match hm.get(&wrap_map_key(&a[1])?) {\n            Some(mv) => Ok(mv.clone()),\n            None => Ok(Nil),\n        },\n        _ => error(\"illegal get args\"),\n    }\n}\n\nfn assoc(a: MalArgs) -> MalRet {\n    match a[0] {\n        Hash(ref hm, _) => _assoc((**hm).clone(), a[1..].to_vec()),\n        _ => error(\"assoc on non-Hash Map\"),\n    }\n}\n\nfn dissoc(a: MalArgs) -> MalRet {\n    match a[0] {\n        Hash(ref hm, _) => {\n            let mut new_hm = (**hm).clone();\n            for k in a[1..].iter() {\n                let _ = new_hm.remove(&wrap_map_key(k)?);\n            }\n            Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))\n        }\n        _ => error(\"dissoc on non-Hash Map\"),\n    }\n}\n\nfn contains_q(a: MalArgs) -> MalRet {\n    match a[0] {\n        Hash(ref hm, _) => Ok(Bool(hm.contains_key(&wrap_map_key(&a[1])?))),\n        _ => error(\"illegal get args\"),\n    }\n}\n\nfn keys(a: MalArgs) -> MalRet {\n    match a[0] {\n        Hash(ref hm, _) => Ok(list(hm.keys().map(|k| unwrap_map_key(k)).collect())),\n        _ => error(\"keys requires Hash Map\"),\n    }\n}\n\nfn vals(a: MalArgs) -> MalRet {\n    match a[0] {\n        Hash(ref hm, _) => Ok(list(hm.values().cloned().collect())),\n        _ => error(\"vals requires Hash Map\"),\n    }\n}\n\nfn vec(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(ref v, _) => Ok(Vector(v.clone(), Rc::new(Nil))),\n        Vector(_, _) => Ok(a[0].clone()),\n        _ => error(\"non-seq passed to vec\"),\n    }\n}\n\nfn cons(a: MalArgs) -> MalRet {\n    match &a[1] {\n        List(v, _) | Vector(v, _) => {\n            let mut new_v = vec![a[0].clone()];\n            new_v.extend_from_slice(v);\n            Ok(list(new_v))\n        }\n        _ => error(\"cons expects seq as second arg\"),\n    }\n}\n\nfn concat(a: MalArgs) -> MalRet {\n    let mut new_v = vec![];\n    for seq in a.iter() {\n        match seq {\n            List(v, _) | Vector(v, _) => new_v.extend_from_slice(v),\n            _ => return error(\"non-seq passed to concat\"),\n        }\n    }\n    Ok(list(new_v))\n}\n\nfn nth(a: MalArgs) -> MalRet {\n    match (&a[0], &a[1]) {\n        (List(seq, _) | Vector(seq, _), Int(idx)) => match seq.get(*idx as usize) {\n            Some(result) => Ok(result.clone()),\n            None => error(\"nth: index out of range\"),\n        },\n        _ => error(\"invalid args to nth\"),\n    }\n}\n\nfn first(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(ref seq, _) | Vector(ref seq, _) if seq.len() > 0 => Ok(seq[0].clone()),\n        List(_, _) | Vector(_, _) | Nil => Ok(Nil),\n        _ => error(\"invalid args to first\"),\n    }\n}\n\nfn rest(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(ref seq, _) | Vector(ref seq, _) if seq.len() > 1 => Ok(list(seq[1..].to_vec())),\n        List(_, _) | Vector(_, _) | Nil => Ok(list!()),\n        _ => error(\"invalid args to first\"),\n    }\n}\n\nfn apply(a: MalArgs) -> MalRet {\n    match a[a.len() - 1] {\n        List(ref v, _) | Vector(ref v, _) => {\n            let f = &a[0];\n            let mut fargs = a[1..a.len() - 1].to_vec();\n            fargs.extend_from_slice(v);\n            f.apply(fargs)\n        }\n        _ => error(\"apply called with non-seq\"),\n    }\n}\n\nfn map(a: MalArgs) -> MalRet {\n    match a[1] {\n        List(ref v, _) | Vector(ref v, _) => {\n            let mut res = vec![];\n            for mv in v.iter() {\n                res.push(a[0].apply(vec![mv.clone()])?)\n            }\n            Ok(list(res))\n        }\n        _ => error(\"map called with non-seq\"),\n    }\n}\n\nfn conj(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(ref v, _) => {\n            let sl = a[1..].iter().rev().cloned().collect::<Vec<MalVal>>();\n            Ok(list([&sl[..], v].concat()))\n        }\n        Vector(ref v, _) => Ok(vector([v, &a[1..]].concat())),\n        _ => error(\"conj: called with non-seq\"),\n    }\n}\n\nfn seq(a: MalArgs) -> MalRet {\n    match a[0] {\n        ref l @ List(ref v, _) if v.len() > 0 => Ok(l.clone()),\n        Vector(ref v, _) if v.len() > 0 => Ok(list(v.to_vec())),\n        Str(ref s) if !s.is_empty() => Ok(list(s.chars().map(|c| Str(c.to_string())).collect())),\n        List(_, _) | Vector(_, _) | Str(_) | Nil => Ok(Nil),\n        _ => error(\"seq: called with non-seq\"),\n    }\n}\n\nfn keyword(a: MalArgs) -> MalRet {\n    match a[0] {\n        Kwd(_) => Ok(a[0].clone()),\n        Str(ref s) => Ok(Kwd(String::from(s))),\n        _ => error(\"invalid type for keyword\"),\n    }\n}\n\npub fn empty_q(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(ref l, _) | Vector(ref l, _) => Ok(Bool(l.len() == 0)),\n        Nil => Ok(Bool(true)),\n        _ => error(\"invalid type for empty?\"),\n    }\n}\n\npub fn count(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(ref l, _) | Vector(ref l, _) => Ok(Int(l.len() as i64)),\n        Nil => Ok(Int(0)),\n        _ => error(\"invalid type for count\"),\n    }\n}\n\npub fn atom(a: MalArgs) -> MalRet {\n    Ok(Atom(Rc::new(std::cell::RefCell::new(a[0].clone()))))\n}\n\npub fn deref(a: MalArgs) -> MalRet {\n    match a[0] {\n        Atom(ref a) => Ok(a.borrow().clone()),\n        _ => error(\"attempt to deref a non-Atom\"),\n    }\n}\n\npub fn reset_bang(a: MalArgs) -> MalRet {\n    match a[0] {\n        Atom(ref atm) => {\n            *atm.borrow_mut() = a[1].clone();\n            Ok(a[1].clone())\n        }\n        _ => error(\"attempt to reset! a non-Atom\"),\n    }\n}\n\npub fn swap_bang(a: MalArgs) -> MalRet {\n    match a[0] {\n        Atom(ref atm) => {\n            let mut fargs = a[2..].to_vec();\n            fargs.insert(0, atm.borrow().clone());\n            let result = a[1].apply(fargs)?;\n            *atm.borrow_mut() = result.clone();\n            Ok(result)\n        }\n        _ => error(\"attempt to swap! a non-Atom\"),\n    }\n}\n\npub fn get_meta(a: MalArgs) -> MalRet {\n    match a[0] {\n        List(_, ref meta) | Vector(_, ref meta) | Hash(_, ref meta) => Ok((**meta).clone()),\n        Func(_, ref meta) => Ok((**meta).clone()),\n        MalFunc(FuncStruct { ref meta, .. }) => Ok((**meta).clone()),\n        _ => error(\"meta not supported by type\"),\n    }\n}\n\npub fn with_meta(a: MalArgs) -> MalRet {\n    let m = Rc::new(a[1].clone());\n    match a[0] {\n        List(ref l, _) => Ok(List(l.clone(), m)),\n        Vector(ref l, _) => Ok(Vector(l.clone(), m)),\n        Hash(ref l, _) => Ok(Hash(l.clone(), m)),\n        Func(ref l, _) => Ok(Func(*l, m)),\n        MalFunc(ref f @ FuncStruct { .. }) => Ok(MalFunc(FuncStruct {\n            meta: m,\n            ..f.clone()\n        })),\n        _ => error(\"with-meta not supported by type\"),\n    }\n}\n\npub fn ns() -> Vec<(&'static str, MalVal)> {\n    vec![\n        (\"=\", func(|a| Ok(Bool(a[0] == a[1])))),\n        (\"throw\", func(|a| Err(a[0].clone()))),\n        (\"nil?\", func(fn_is_type!(Nil))),\n        (\"true?\", func(fn_is_type!(Bool(true)))),\n        (\"false?\", func(fn_is_type!(Bool(false)))),\n        (\"symbol\", func(symbol)),\n        (\"symbol?\", func(fn_is_type!(Sym(_)))),\n        (\"string?\", func(fn_is_type!(Str(_)))),\n        (\"keyword\", func(keyword)),\n        (\"keyword?\", func(fn_is_type!(Kwd(_)))),\n        (\"number?\", func(fn_is_type!(Int(_)))),\n        (\n            \"fn?\",\n            func(fn_is_type!(\n                MalFunc(FuncStruct {\n                    is_macro: false,\n                    ..\n                }),\n                Func(_, _)\n            )),\n        ),\n        (\n            \"macro?\",\n            func(fn_is_type!(MalFunc(FuncStruct { is_macro: true, .. }))),\n        ),\n        (\"pr-str\", func(|a| Ok(Str(pr_seq(&a, true, \"\", \"\", \" \"))))),\n        (\"str\", func(|a| Ok(Str(pr_seq(&a, false, \"\", \"\", \"\"))))),\n        (\n            \"prn\",\n            func(|a| {\n                println!(\"{}\", pr_seq(&a, true, \"\", \"\", \" \"));\n                Ok(Nil)\n            }),\n        ),\n        (\n            \"println\",\n            func(|a| {\n                println!(\"{}\", pr_seq(&a, false, \"\", \"\", \" \"));\n                Ok(Nil)\n            }),\n        ),\n        (\"read-string\", func(fn_str!(read_str))),\n        (\"readline\", func(fn_str!(readline))),\n        (\"slurp\", func(fn_str!(slurp))),\n        (\"<\", func(fn_t_int_int!(Bool, |i, j| { i < j }))),\n        (\"<=\", func(fn_t_int_int!(Bool, |i, j| { i <= j }))),\n        (\">\", func(fn_t_int_int!(Bool, |i, j| { i > j }))),\n        (\">=\", func(fn_t_int_int!(Bool, |i, j| { i >= j }))),\n        (\"+\", func(fn_t_int_int!(Int, |i, j| { i + j }))),\n        (\"-\", func(fn_t_int_int!(Int, |i, j| { i - j }))),\n        (\"*\", func(fn_t_int_int!(Int, |i, j| { i * j }))),\n        (\"/\", func(fn_t_int_int!(Int, |i, j| { i / j }))),\n        (\"time-ms\", func(time_ms)),\n        (\"sequential?\", func(fn_is_type!(List(_, _), Vector(_, _)))),\n        (\"list\", func(|a| Ok(list(a)))),\n        (\"list?\", func(fn_is_type!(List(_, _)))),\n        (\"vector\", func(|a| Ok(vector(a)))),\n        (\"vector?\", func(fn_is_type!(Vector(_, _)))),\n        (\"hash-map\", func(hash_map)),\n        (\"map?\", func(fn_is_type!(Hash(_, _)))),\n        (\"assoc\", func(assoc)),\n        (\"dissoc\", func(dissoc)),\n        (\"get\", func(get)),\n        (\"contains?\", func(contains_q)),\n        (\"keys\", func(keys)),\n        (\"vals\", func(vals)),\n        (\"vec\", func(vec)),\n        (\"cons\", func(cons)),\n        (\"concat\", func(concat)),\n        (\"empty?\", func(empty_q)),\n        (\"nth\", func(nth)),\n        (\"first\", func(first)),\n        (\"rest\", func(rest)),\n        (\"count\", func(count)),\n        (\"apply\", func(apply)),\n        (\"map\", func(map)),\n        (\"conj\", func(conj)),\n        (\"seq\", func(seq)),\n        (\"meta\", func(get_meta)),\n        (\"with-meta\", func(with_meta)),\n        (\"atom\", func(atom)),\n        (\"atom?\", func(fn_is_type!(Atom(_)))),\n        (\"deref\", func(deref)),\n        (\"reset!\", func(reset_bang)),\n        (\"swap!\", func(swap_bang)),\n    ]\n}\n"
  },
  {
    "path": "impls/rust/env.rs",
    "content": "use std::cell::RefCell;\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\n\nuse crate::types::MalVal::{List, Sym, Vector};\nuse crate::types::{error, list, MalRet, MalVal};\n\npub struct EnvStruct {\n    data: RefCell<FnvHashMap<String, MalVal>>,\n    outer: Option<Env>,\n}\n\npub type Env = Rc<EnvStruct>;\n\n// TODO: it would be nice to use impl here but it doesn't work on\n// a deftype (i.e. Env)\n\npub fn env_new(outer: Option<Env>) -> Env {\n    Rc::new(EnvStruct {\n        data: RefCell::new(FnvHashMap::default()),\n        outer,\n    })\n}\n\n// TODO: mbinds and exprs as & types\npub fn env_bind(outer: Env, mbinds: &MalVal, exprs: Vec<MalVal>) -> Result<Env, MalVal> {\n    let env = env_new(Some(outer));\n    match mbinds {\n        List(binds, _) | Vector(binds, _) => {\n            for (i, b) in binds.iter().enumerate() {\n                match b {\n                    Sym(s) if s == \"&\" => {\n                        env_set(&env, &binds[i + 1], list(exprs[i..].to_vec()))?;\n                        break;\n                    }\n                    _ => {\n                        env_set(&env, b, exprs[i].clone())?;\n                    }\n                }\n            }\n            Ok(env)\n        }\n        _ => error(\"env_bind binds not List/Vector\"),\n    }\n}\n\npub fn env_get(env: &Env, key: &str) -> Option<MalVal> {\n    let mut mut_env = env;\n    loop {\n        if let Some(value) = mut_env.data.borrow().get(key) {\n            return Some(value.clone());\n        } else if let Some(outer) = &mut_env.outer {\n            mut_env = outer;\n        } else {\n            return None;\n        }\n    }\n}\n\npub fn env_set(env: &Env, key: &MalVal, val: MalVal) -> MalRet {\n    match key {\n        Sym(s) => {\n            env_sets(env, s, val.clone());\n            Ok(val)\n        }\n        _ => error(\"Env.set called with non-Str\"),\n    }\n}\n\npub fn env_sets(env: &Env, key: &str, val: MalVal) {\n    env.data.borrow_mut().insert(key.to_string(), val);\n}\n"
  },
  {
    "path": "impls/rust/printer.rs",
    "content": "use crate::types::MalVal::{\n    Atom, Bool, Func, Hash, Int, Kwd, List, MalFunc, Nil, Str, Sym, Vector,\n};\nuse crate::types::{unwrap_map_key, FuncStruct, MalVal};\n\nfn escape_str(s: &str) -> String {\n    s.chars()\n        .map(|c| match c {\n            '\"' => \"\\\\\\\"\".to_string(),\n            '\\n' => \"\\\\n\".to_string(),\n            '\\\\' => \"\\\\\\\\\".to_string(),\n            _ => c.to_string(),\n        })\n        .collect::<Vec<String>>()\n        .join(\"\")\n}\n\nimpl MalVal {\n    pub fn pr_str(&self, print_readably: bool) -> String {\n        match self {\n            Nil => String::from(\"nil\"),\n            Bool(true) => String::from(\"true\"),\n            Bool(false) => String::from(\"false\"),\n            Int(i) => format!(\"{}\", i),\n            //Float(f)    => format!(\"{}\", f),\n            Kwd(s) => format!(\":{}\", s),\n            Str(s) => {\n                if print_readably {\n                    format!(\"\\\"{}\\\"\", escape_str(s))\n                } else {\n                    s.clone()\n                }\n            }\n            Sym(s) => s.clone(),\n            List(l, _) => pr_seq(l, print_readably, \"(\", \")\", \" \"),\n            Vector(l, _) => pr_seq(l, print_readably, \"[\", \"]\", \" \"),\n            Hash(hm, _) => {\n                let l: Vec<MalVal> = hm\n                    .iter()\n                    .flat_map(|(k, v)| vec![unwrap_map_key(k), v.clone()])\n                    .collect();\n                pr_seq(&l, print_readably, \"{\", \"}\", \" \")\n            }\n            Func(_, _) => String::from(\"#<builtin>\"),\n            MalFunc(FuncStruct {\n                ast: a, params: p, ..\n            }) => format!(\"(fn* {} {})\", p.pr_str(true), a.pr_str(true)),\n            Atom(a) => format!(\"(atom {})\", a.borrow().pr_str(true)),\n        }\n    }\n}\n\npub fn pr_seq(seq: &[MalVal], print_readably: bool, start: &str, end: &str, join: &str) -> String {\n    let strs: Vec<String> = seq.iter().map(|x| x.pr_str(print_readably)).collect();\n    format!(\"{}{}{}\", start, strs.join(join), end)\n}\n"
  },
  {
    "path": "impls/rust/reader.rs",
    "content": "use regex::{Captures, Regex};\nuse std::rc::Rc;\n\nuse crate::types::MalVal::{Bool, Int, Kwd, List, Nil, Str, Sym};\nuse crate::types::{error, hash_map, list, vector, MalRet, MalVal};\n\n#[derive(Debug, Clone)]\nstruct Reader {\n    tokens: Vec<String>,\n    pos: usize,\n}\n\nimpl Reader {\n    fn next(&mut self) -> Result<String, MalVal> {\n        self.pos += 1;\n        Ok(self\n            .tokens\n            .get(self.pos - 1)\n            .ok_or_else(|| Str(\"underflow\".to_string()))?\n            .to_string())\n    }\n    fn peek(&self) -> Result<String, MalVal> {\n        Ok(self\n            .tokens\n            .get(self.pos)\n            .ok_or_else(|| Str(\"underflow\".to_string()))?\n            .to_string())\n    }\n}\n\nthread_local! {\n    static TOKENIZE_RE: Regex = Regex::new(\n        r###\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]+)\"###\n    ).unwrap();\n    static UNESCAPE_RE: Regex = Regex::new(r#\"\\\\(.)\"#).unwrap();\n    static INT_RE: Regex = Regex::new(r\"^-?[0-9]+$\").unwrap();\n    static STR_RE: Regex = Regex::new(r#\"\"(?:\\\\.|[^\\\\\"])*\"\"#).unwrap();\n}\n\nfn tokenize(str: &str) -> Vec<String> {\n    TOKENIZE_RE.with(|re| {\n        let mut res = vec![];\n        for cap in re.captures_iter(str) {\n            if cap[1].starts_with(';') {\n                continue;\n            }\n            res.push(String::from(&cap[1]));\n        }\n        res\n    })\n}\n\nfn unescape_str(s: &str) -> String {\n    UNESCAPE_RE.with(|re| {\n        re.replace_all(s, |caps: &Captures| {\n            if &caps[1] == \"n\" { \"\\n\" } else { &caps[1] }.to_string()\n        })\n        .to_string()\n    })\n}\n\nfn read_atom(rdr: &mut Reader) -> MalRet {\n    let token = rdr.next()?;\n    match &token[..] {\n        \"nil\" => Ok(Nil),\n        \"false\" => Ok(Bool(false)),\n        \"true\" => Ok(Bool(true)),\n        _ => {\n            if INT_RE.with(|re| re.is_match(&token)) {\n                Ok(Int(token.parse().unwrap()))\n            } else if STR_RE.with(|re| re.is_match(&token)) {\n                Ok(Str(unescape_str(&token[1..token.len() - 1])))\n            } else if token.starts_with('\\\"') {\n                error(\"expected '\\\"', got EOF\")\n            } else if let Some(keyword) = token.strip_prefix(':') {\n                Ok(Kwd(String::from(keyword)))\n            } else {\n                Ok(Sym(token.to_string()))\n            }\n        }\n    }\n}\n\nfn read_seq(rdr: &mut Reader, end: &str) -> Result<Vec<MalVal>, MalVal> {\n    let mut seq: Vec<MalVal> = vec![];\n    rdr.next()?;\n    loop {\n        let token = match rdr.peek() {\n            Ok(t) => t,\n            Err(_) => return error(&format!(\"expected '{}', got EOF\", end)),\n        };\n        if token == end {\n            break;\n        }\n        seq.push(read_form(rdr)?);\n    }\n    let _ = rdr.next();\n    Ok(seq)\n}\n\nfn read_form(rdr: &mut Reader) -> MalRet {\n    let token = rdr.peek()?;\n    match &token[..] {\n        \"'\" => {\n            let _ = rdr.next();\n            Ok(list!(Sym(\"quote\".to_string()), read_form(rdr)?))\n        }\n        \"`\" => {\n            let _ = rdr.next();\n            Ok(list!(Sym(\"quasiquote\".to_string()), read_form(rdr)?))\n        }\n        \"~\" => {\n            let _ = rdr.next();\n            Ok(list!(Sym(\"unquote\".to_string()), read_form(rdr)?))\n        }\n        \"~@\" => {\n            let _ = rdr.next();\n            Ok(list!(Sym(\"splice-unquote\".to_string()), read_form(rdr)?))\n        }\n        \"^\" => {\n            let _ = rdr.next();\n            let meta = read_form(rdr)?;\n            Ok(list!(Sym(\"with-meta\".to_string()), read_form(rdr)?, meta))\n        }\n        \"@\" => {\n            let _ = rdr.next();\n            Ok(list!(Sym(\"deref\".to_string()), read_form(rdr)?))\n        }\n        \")\" => error(\"unexpected ')'\"),\n        \"(\" => Ok(list(read_seq(rdr, \")\")?)),\n        \"]\" => error(\"unexpected ']'\"),\n        \"[\" => Ok(vector(read_seq(rdr, \"]\")?)),\n        \"}\" => error(\"unexpected '}'\"),\n        \"{\" => hash_map(read_seq(rdr, \"}\")?.to_vec()),\n        _ => read_atom(rdr),\n    }\n}\n\npub fn read_str(str: &str) -> MalRet {\n    let tokens = tokenize(str);\n    //println!(\"tokens: {:?}\", tokens);\n    if tokens.is_empty() {\n        return error(\"no input\");\n    }\n    read_form(&mut Reader { pos: 0, tokens })\n}\n"
  },
  {
    "path": "impls/rust/readline.rs",
    "content": "extern crate rustyline;\n\n// A global variable makes more sense than passing the readline editor\n// as an argument to *every* core function just for readline.\n\nstruct S {\n    e: rustyline::Editor<(), rustyline::history::DefaultHistory>,\n}\n\nimpl Drop for S {\n    fn drop(&mut self) {\n        self.e.save_history(\".mal-history\").unwrap()\n    }\n}\n\nthread_local! {\n    static ED : std::cell::RefCell<S> = {\n        let mut e = rustyline::Editor::new().unwrap();\n        if e.load_history(\".mal-history\").is_err() {\n            println!(\"No previous history.\");\n        }\n        std::cell::RefCell::new(S{e})\n    }\n}\n\npub fn readline(prompt: &str) -> Option<String> {\n    ED.with_borrow_mut(|s| {\n        let r = s.e.readline(prompt);\n        if let Err(rustyline::error::ReadlineError::Eof) = r {\n            None\n        } else {\n            let mut line = r.unwrap();\n            // Remove any trailing \\n or \\r\\n\n            while line.ends_with('\\n') || line.ends_with('\\r') {\n                line.pop();\n            }\n            if !line.is_empty() {\n                let _ = s.e.add_history_entry(&line);\n            }\n            Some(line.to_string())\n        }\n    })\n}\n"
  },
  {
    "path": "impls/rust/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/target/release/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/rust/step0_repl.rs",
    "content": "#![allow(non_snake_case)]\n\nmod readline;\n\nfn main() {\n    // `()` can be used when no completer is required\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            println!(\"{}\", line);\n        }\n    }\n    println!();\n}\n"
  },
  {
    "path": "impls/rust/step1_read_print.rs",
    "content": "#![allow(non_snake_case)]\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\n#[allow(dead_code)]\nmod types;\nuse crate::types::{MalRet, MalVal};\n#[allow(dead_code)]\nmod env;\nmod printer;\nmod reader;\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\nfn eval(ast: MalVal) -> MalRet {\n    Ok(ast)\n}\n\n// print\nfn print(ast: MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(ast)?;\n    Ok(print(exp))\n}\n\nfn main() {\n    // `()` can be used when no completer is required\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n}\n"
  },
  {
    "path": "impls/rust/step2_eval.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\n#[allow(dead_code)]\nmod types;\nuse crate::types::MalVal::{Func, Hash, Int, List, Nil, Sym, Vector};\nuse crate::types::{error, func, vector, MalArgs, MalRet, MalVal};\n#[allow(dead_code)]\nmod env;\nmod printer;\nmod reader;\n\npub type Env = FnvHashMap<String, MalVal>;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\nfn eval(ast: &MalVal, env: &Env) -> MalRet {\n    // println!(\"EVAL: {}\", print(&ast));\n    match ast {\n        Sym(s) => match env.get(s) {\n            Some(r) => Ok(r.clone()),\n            None => error(&format!(\"'{}' not found\", s)),\n        },\n        Vector(v, _) => {\n            let mut lst: MalArgs = vec![];\n            for a in v.iter() {\n                lst.push(eval(a, env)?);\n            }\n            Ok(vector(lst))\n        }\n        Hash(hm, _) => {\n            let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n            for (k, v) in hm.iter() {\n                new_hm.insert(k.to_string(), eval(v, env)?);\n            }\n            Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))\n        }\n        List(l, _) => {\n            if l.is_empty() {\n                return Ok(ast.clone());\n            }\n            let a0 = &l[0];\n            let f = eval(a0, env)?;\n            let mut args: MalArgs = vec![];\n            for i in 1..l.len() {\n                args.push(eval(&l[i], env)?);\n            }\n            f.apply(args)\n        }\n        _ => Ok(ast.clone()),\n    }\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet {\n    match (a[0].clone(), a[1].clone()) {\n        (Int(a0), Int(a1)) => Ok(Int(op(a0, a1))),\n        _ => error(\"invalid int_op args\"),\n    }\n}\n\nfn main() {\n    // `()` can be used when no completer is required\n\n    let mut repl_env = Env::default();\n    repl_env.insert(\"+\".to_string(), func(|a: MalArgs| int_op(|i, j| i + j, a)));\n    repl_env.insert(\"-\".to_string(), func(|a: MalArgs| int_op(|i, j| i - j, a)));\n    repl_env.insert(\"*\".to_string(), func(|a: MalArgs| int_op(|i, j| i * j, a)));\n    repl_env.insert(\"/\".to_string(), func(|a: MalArgs| int_op(|i, j| i / j, a)));\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, &repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n}\n"
  },
  {
    "path": "impls/rust/step3_env.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\n#[allow(dead_code)]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, Int, List, Nil, Sym, Vector};\nuse crate::types::{error, func, vector, MalArgs, MalRet, MalVal};\n#[allow(dead_code)]\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_get, env_new, env_set, env_sets, Env};\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\nfn eval(ast: &MalVal, env: &Env) -> MalRet {\n    match env_get(env, \"DEBUG-EVAL\") {\n        None | Some(Bool(false)) | Some(Nil) => (),\n        _ => println!(\"EVAL: {}\", print(ast)),\n    }\n    match ast {\n        Sym(s) => match env_get(env, s) {\n            Some(r) => Ok(r),\n            None => error(&format!(\"'{}' not found\", s)),\n        },\n        Vector(v, _) => {\n            let mut lst: MalArgs = vec![];\n            for a in v.iter() {\n                lst.push(eval(a, env)?);\n            }\n            Ok(vector(lst))\n        }\n        Hash(hm, _) => {\n            let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n            for (k, v) in hm.iter() {\n                new_hm.insert(k.to_string(), eval(v, env)?);\n            }\n            Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))\n        }\n        List(l, _) => {\n            if l.is_empty() {\n                return Ok(ast.clone());\n            }\n            let a0 = &l[0];\n            match a0 {\n                Sym(a0sym) if a0sym == \"def!\" => env_set(env, &l[1], eval(&l[2], env)?),\n                Sym(a0sym) if a0sym == \"let*\" => {\n                    let let_env = &env_new(Some(env.clone()));\n                    let (a1, a2) = (&l[1], &l[2]);\n                    match a1 {\n                        List(binds, _) | Vector(binds, _) => {\n                            for (b, e) in binds.iter().tuples() {\n                                let val = eval(e, let_env)?;\n                                env_set(let_env, b, val)?;\n                            }\n                        }\n                        _ => {\n                            return error(\"let* with non-List bindings\");\n                        }\n                    };\n                    eval(a2, let_env)\n                }\n                _ => {\n                    let f = eval(a0, env)?;\n                    let mut args: MalArgs = vec![];\n                    for i in 1..l.len() {\n                        args.push(eval(&l[i], env)?);\n                    }\n                    f.apply(args)\n                }\n            }\n        }\n        _ => Ok(ast.clone()),\n    }\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet {\n    match (a[0].clone(), a[1].clone()) {\n        (Int(a0), Int(a1)) => Ok(Int(op(a0, a1))),\n        _ => error(\"invalid int_op args\"),\n    }\n}\n\nfn main() {\n    // `()` can be used when no completer is required\n\n    let repl_env = env_new(None);\n    env_sets(&repl_env, \"+\", func(|a: MalArgs| int_op(|i, j| i + j, a)));\n    env_sets(&repl_env, \"-\", func(|a: MalArgs| int_op(|i, j| i - j, a)));\n    env_sets(&repl_env, \"*\", func(|a: MalArgs| int_op(|i, j| i * j, a)));\n    env_sets(&repl_env, \"/\", func(|a: MalArgs| int_op(|i, j| i / j, a)));\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, &repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n}\n"
  },
  {
    "path": "impls/rust/step4_if_fn_do.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Sym, Vector};\nuse crate::types::{error, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\nfn eval(ast: &MalVal, env: &Env) -> MalRet {\n    match env_get(env, \"DEBUG-EVAL\") {\n        None | Some(Bool(false)) | Some(Nil) => (),\n        _ => println!(\"EVAL: {}\", print(ast)),\n    }\n    match ast {\n        Sym(s) => match env_get(env, s) {\n            Some(r) => Ok(r),\n            None => error(&format!(\"'{}' not found\", s)),\n        },\n        Vector(v, _) => {\n            let mut lst: MalArgs = vec![];\n            for a in v.iter() {\n                lst.push(eval(a, env)?);\n            }\n            Ok(vector(lst))\n        }\n        Hash(hm, _) => {\n            let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n            for (k, v) in hm.iter() {\n                new_hm.insert(k.to_string(), eval(v, env)?);\n            }\n            Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))\n        }\n        List(l, _) => {\n            if l.is_empty() {\n                return Ok(ast.clone());\n            }\n            let a0 = &l[0];\n            match a0 {\n                Sym(a0sym) if a0sym == \"def!\" => env_set(env, &l[1], eval(&l[2], env)?),\n                Sym(a0sym) if a0sym == \"let*\" => {\n                    let let_env = &env_new(Some(env.clone()));\n                    let (a1, a2) = (&l[1], &l[2]);\n                    match a1 {\n                        List(binds, _) | Vector(binds, _) => {\n                            for (b, e) in binds.iter().tuples() {\n                                let val = eval(e, let_env)?;\n                                env_set(let_env, b, val)?;\n                            }\n                        }\n                        _ => {\n                            return error(\"let* with non-List bindings\");\n                        }\n                    };\n                    eval(a2, let_env)\n                }\n                Sym(a0sym) if a0sym == \"do\" => {\n                    for i in 1..l.len() - 1 {\n                        let _ = eval(&l[i], env)?;\n                    }\n                    eval(l.last().unwrap_or(&Nil), env)\n                }\n                Sym(a0sym) if a0sym == \"if\" => {\n                    let cond = eval(&l[1], env)?;\n                    match cond {\n                        Bool(false) | Nil if l.len() >= 4 => eval(&l[3], env),\n                        Bool(false) | Nil => Ok(Nil),\n                        _ if l.len() >= 3 => eval(&l[2], env),\n                        _ => Ok(Nil),\n                    }\n                }\n                Sym(a0sym) if a0sym == \"fn*\" => {\n                    let (a1, a2) = (l[1].clone(), l[2].clone());\n                    Ok(MalFunc(FuncStruct {\n                        ast: Rc::new(a2),\n                        env: env.clone(),\n                        params: Rc::new(a1),\n                        is_macro: false,\n                        meta: Rc::new(Nil),\n                    }))\n                }\n                _ => {\n                    let f = eval(a0, env)?;\n                    let mut args: MalArgs = vec![];\n                    for i in 1..l.len() {\n                        args.push(eval(&l[i], env)?);\n                    }\n                    f.apply(args)\n                }\n            }\n        }\n        _ => Ok(ast.clone()),\n    }\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nfn main() {\n    // `()` can be used when no completer is required\n\n    // core.rs: defined using rust\n    let repl_env = env_new(None);\n    for (k, v) in core::ns() {\n        env_sets(&repl_env, k, v);\n    }\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", &repl_env);\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, &repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n}\n"
  },
  {
    "path": "impls/rust/step5_tco.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Sym, Vector};\nuse crate::types::{error, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\nfn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet {\n    let mut ast = orig_ast;\n    let mut env = orig_env;\n    // These variables ensure a sufficient lifetime for the data\n    // referenced by ast and env.\n    let mut live_ast;\n    let mut live_env;\n\n    'tco: loop {\n        match env_get(env, \"DEBUG-EVAL\") {\n            None | Some(Bool(false)) | Some(Nil) => (),\n            _ => println!(\"EVAL: {}\", print(ast)),\n        }\n        match ast {\n            Sym(s) => match env_get(env, s) {\n                Some(r) => return Ok(r),\n                None => return error(&format!(\"'{}' not found\", s)),\n            },\n            Vector(v, _) => {\n                let mut lst: MalArgs = vec![];\n                for a in v.iter() {\n                    lst.push(eval(a, env)?);\n                }\n                return Ok(vector(lst));\n            }\n            Hash(hm, _) => {\n                let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n                for (k, v) in hm.iter() {\n                    new_hm.insert(k.to_string(), eval(v, env)?);\n                }\n                return Ok(Hash(Rc::new(new_hm), Rc::new(Nil)));\n            }\n            List(l, _) => {\n                if l.is_empty() {\n                    return Ok(ast.clone());\n                }\n                let a0 = &l[0];\n                match a0 {\n                    Sym(a0sym) if a0sym == \"def!\" => {\n                        return env_set(env, &l[1], eval(&l[2], env)?);\n                    }\n                    Sym(a0sym) if a0sym == \"let*\" => {\n                        live_env = env_new(Some(env.clone()));\n                        env = &live_env;\n                        let (a1, a2) = (&l[1], &l[2]);\n                        match a1 {\n                            List(binds, _) | Vector(binds, _) => {\n                                for (b, e) in binds.iter().tuples() {\n                                    let val = eval(e, env)?;\n                                    env_set(env, b, val)?;\n                                }\n                            }\n                            _ => {\n                                return error(\"let* with non-List bindings\");\n                            }\n                        };\n                        live_ast = a2.clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"do\" => {\n                        for i in 1..l.len() - 1 {\n                            let _ = eval(&l[i], env)?;\n                        }\n                        live_ast = l.last().unwrap_or(&Nil).clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"if\" => {\n                        let cond = eval(&l[1], env)?;\n                        match cond {\n                            Bool(false) | Nil if l.len() >= 4 => {\n                                live_ast = l[3].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            Bool(false) | Nil => return Ok(Nil),\n                            _ if l.len() >= 3 => {\n                                live_ast = l[2].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            _ => return Ok(Nil),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"fn*\" => {\n                        let (a1, a2) = (l[1].clone(), l[2].clone());\n                        return Ok(MalFunc(FuncStruct {\n                            ast: Rc::new(a2),\n                            env: env.clone(),\n                            params: Rc::new(a1),\n                            is_macro: false,\n                            meta: Rc::new(Nil),\n                        }));\n                    }\n                    _ => match eval(a0, env)? {\n                        f @ Func(_, _) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            return f.apply(args);\n                        }\n                        MalFunc(FuncStruct {\n                            ast: mast,\n                            env: menv,\n                            params: mparams,\n                            ..\n                        }) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            live_env = env_bind(menv.clone(), &mparams, args)?;\n                            env = &live_env;\n                            live_ast = (*mast).clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        _ => return error(\"attempt to call non-function\"),\n                    },\n                }\n            }\n            _ => return Ok(ast.clone()),\n        };\n    } // end 'tco loop\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nfn main() {\n    // `()` can be used when no completer is required\n\n    // core.rs: defined using rust\n    let repl_env = env_new(None);\n    for (k, v) in core::ns() {\n        env_sets(&repl_env, k, v);\n    }\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", &repl_env);\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, &repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n}\n"
  },
  {
    "path": "impls/rust/step6_file.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};\nuse crate::types::{error, list, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\nfn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet {\n    let mut ast = orig_ast;\n    let mut env = orig_env;\n    // These variables ensure a sufficient lifetime for the data\n    // referenced by ast and env.\n    let mut live_ast;\n    let mut live_env;\n\n    'tco: loop {\n        match env_get(env, \"DEBUG-EVAL\") {\n            None | Some(Bool(false)) | Some(Nil) => (),\n            _ => println!(\"EVAL: {}\", print(ast)),\n        }\n        match ast {\n            Sym(s) => match env_get(env, s) {\n                Some(r) => return Ok(r),\n                None => return error(&format!(\"'{}' not found\", s)),\n            },\n            Vector(v, _) => {\n                let mut lst: MalArgs = vec![];\n                for a in v.iter() {\n                    lst.push(eval(a, env)?);\n                }\n                return Ok(vector(lst));\n            }\n            Hash(hm, _) => {\n                let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n                for (k, v) in hm.iter() {\n                    new_hm.insert(k.to_string(), eval(v, env)?);\n                }\n                return Ok(Hash(Rc::new(new_hm), Rc::new(Nil)));\n            }\n            List(l, _) => {\n                if l.is_empty() {\n                    return Ok(ast.clone());\n                }\n                let a0 = &l[0];\n                match a0 {\n                    Sym(a0sym) if a0sym == \"def!\" => {\n                        return env_set(env, &l[1], eval(&l[2], env)?);\n                    }\n                    Sym(a0sym) if a0sym == \"let*\" => {\n                        live_env = env_new(Some(env.clone()));\n                        env = &live_env;\n                        let (a1, a2) = (&l[1], &l[2]);\n                        match a1 {\n                            List(binds, _) | Vector(binds, _) => {\n                                for (b, e) in binds.iter().tuples() {\n                                    let val = eval(e, env)?;\n                                    env_set(env, b, val)?;\n                                }\n                            }\n                            _ => {\n                                return error(\"let* with non-List bindings\");\n                            }\n                        };\n                        live_ast = a2.clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"do\" => {\n                        for i in 1..l.len() - 1 {\n                            let _ = eval(&l[i], env)?;\n                        }\n                        live_ast = l.last().unwrap_or(&Nil).clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"if\" => {\n                        let cond = eval(&l[1], env)?;\n                        match cond {\n                            Bool(false) | Nil if l.len() >= 4 => {\n                                live_ast = l[3].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            Bool(false) | Nil => return Ok(Nil),\n                            _ if l.len() >= 3 => {\n                                live_ast = l[2].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            _ => return Ok(Nil),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"fn*\" => {\n                        let (a1, a2) = (l[1].clone(), l[2].clone());\n                        return Ok(MalFunc(FuncStruct {\n                            ast: Rc::new(a2),\n                            env: env.clone(),\n                            params: Rc::new(a1),\n                            is_macro: false,\n                            meta: Rc::new(Nil),\n                        }));\n                    }\n                    _ => match eval(a0, env)? {\n                        f @ Func(_, _) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            return f.apply(args);\n                        }\n                        MalFunc(FuncStruct {\n                            ast: mast,\n                            env: menv,\n                            params: mparams,\n                            ..\n                        }) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            live_env = env_bind(menv.clone(), &mparams, args)?;\n                            env = &live_env;\n                            live_ast = (*mast).clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        _ => return error(\"attempt to call non-function\"),\n                    },\n                }\n            }\n            _ => return Ok(ast.clone()),\n        };\n    } // end 'tco loop\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nthread_local! {\n    static REPL_ENV: Env = env_new(None);\n}\n\nfn main() {\n    REPL_ENV.with(|repl_env| {\n        let mut args = std::env::args();\n        let arg1 = args.nth(1);\n\n        // `()` can be used when no completer is required\n\n        // core.rs: defined using rust\n        env_sets(\n            repl_env,\n            \"eval\",\n            types::func(|a| REPL_ENV.with(|e| eval(&a[0], e))),\n        );\n        for (k, v) in core::ns() {\n            env_sets(repl_env, k, v);\n        }\n        env_sets(repl_env, \"*ARGV*\", list(args.map(Str).collect()));\n\n        // core.mal: defined using the language itself\n        re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n        re(\n            \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\",\n            repl_env,\n        );\n\n        if let Some(f) = arg1 {\n            // Invoked with arguments\n            re(&format!(\"(load-file \\\"{}\\\")\", f), repl_env);\n            std::process::exit(0);\n        }\n\n        // main repl loop\n        while let Some(ref line) = readline::readline(\"user> \") {\n            if !line.is_empty() {\n                match rep(line, repl_env) {\n                    Ok(ref out) => println!(\"{}\", out),\n                    Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n                }\n            }\n        }\n        println!();\n    })\n}\n"
  },
  {
    "path": "impls/rust/step7_quote.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};\nuse crate::types::{error, list, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\n\nfn qq_iter(elts: &MalArgs) -> MalVal {\n    let mut acc = list!();\n    for elt in elts.iter().rev() {\n        if let List(v, _) = elt {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"splice-unquote\" {\n                        acc = list!(Sym(\"concat\".to_string()), v[1].clone(), acc);\n                        continue;\n                    }\n                }\n            }\n        }\n        acc = list!(Sym(\"cons\".to_string()), quasiquote(elt), acc);\n    }\n    acc\n}\n\nfn quasiquote(ast: &MalVal) -> MalVal {\n    match ast {\n        List(v, _) => {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"unquote\" {\n                        return v[1].clone();\n                    }\n                }\n            }\n            qq_iter(v)\n        }\n        Vector(v, _) => list!(Sym(\"vec\".to_string()), qq_iter(v)),\n        Hash(_, _) | Sym(_) => list!(Sym(\"quote\".to_string()), ast.clone()),\n        _ => ast.clone(),\n    }\n}\n\nfn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet {\n    let mut ast = orig_ast;\n    let mut env = orig_env;\n    // These variables ensure a sufficient lifetime for the data\n    // referenced by ast and env.\n    let mut live_ast;\n    let mut live_env;\n\n    'tco: loop {\n        match env_get(env, \"DEBUG-EVAL\") {\n            None | Some(Bool(false)) | Some(Nil) => (),\n            _ => println!(\"EVAL: {}\", print(ast)),\n        }\n        match ast {\n            Sym(s) => match env_get(env, s) {\n                Some(r) => return Ok(r),\n                None => return error(&format!(\"'{}' not found\", s)),\n            },\n            Vector(v, _) => {\n                let mut lst: MalArgs = vec![];\n                for a in v.iter() {\n                    lst.push(eval(a, env)?);\n                }\n                return Ok(vector(lst));\n            }\n            Hash(hm, _) => {\n                let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n                for (k, v) in hm.iter() {\n                    new_hm.insert(k.to_string(), eval(v, env)?);\n                }\n                return Ok(Hash(Rc::new(new_hm), Rc::new(Nil)));\n            }\n            List(l, _) => {\n                if l.is_empty() {\n                    return Ok(ast.clone());\n                }\n                let a0 = &l[0];\n                match a0 {\n                    Sym(a0sym) if a0sym == \"def!\" => {\n                        return env_set(env, &l[1], eval(&l[2], env)?);\n                    }\n                    Sym(a0sym) if a0sym == \"let*\" => {\n                        live_env = env_new(Some(env.clone()));\n                        env = &live_env;\n                        let (a1, a2) = (&l[1], &l[2]);\n                        match a1 {\n                            List(binds, _) | Vector(binds, _) => {\n                                for (b, e) in binds.iter().tuples() {\n                                    let val = eval(e, env)?;\n                                    env_set(env, b, val)?;\n                                }\n                            }\n                            _ => {\n                                return error(\"let* with non-List bindings\");\n                            }\n                        };\n                        live_ast = a2.clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"quote\" => return Ok(l[1].clone()),\n                    Sym(a0sym) if a0sym == \"quasiquote\" => {\n                        live_ast = quasiquote(&l[1]);\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"do\" => {\n                        for i in 1..l.len() - 1 {\n                            let _ = eval(&l[i], env)?;\n                        }\n                        live_ast = l.last().unwrap_or(&Nil).clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"if\" => {\n                        let cond = eval(&l[1], env)?;\n                        match cond {\n                            Bool(false) | Nil if l.len() >= 4 => {\n                                live_ast = l[3].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            Bool(false) | Nil => return Ok(Nil),\n                            _ if l.len() >= 3 => {\n                                live_ast = l[2].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            _ => return Ok(Nil),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"fn*\" => {\n                        let (a1, a2) = (l[1].clone(), l[2].clone());\n                        return Ok(MalFunc(FuncStruct {\n                            ast: Rc::new(a2),\n                            env: env.clone(),\n                            params: Rc::new(a1),\n                            is_macro: false,\n                            meta: Rc::new(Nil),\n                        }));\n                    }\n                    _ => match eval(a0, env)? {\n                        f @ Func(_, _) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            return f.apply(args);\n                        }\n                        MalFunc(FuncStruct {\n                            ast: mast,\n                            env: menv,\n                            params: mparams,\n                            ..\n                        }) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            live_env = env_bind(menv.clone(), &mparams, args)?;\n                            env = &live_env;\n                            live_ast = (*mast).clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        _ => return error(\"attempt to call non-function\"),\n                    },\n                }\n            }\n            _ => return Ok(ast.clone()),\n        };\n    } // end 'tco loop\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nthread_local! {\n    static REPL_ENV: Env = env_new(None);\n}\n\nfn main() {\n    REPL_ENV.with(|repl_env| {\n        let mut args = std::env::args();\n        let arg1 = args.nth(1);\n\n        // `()` can be used when no completer is required\n\n        // core.rs: defined using rust\n        env_sets(\n            repl_env,\n            \"eval\",\n            types::func(|a| REPL_ENV.with(|e| eval(&a[0], e))),\n        );\n        for (k, v) in core::ns() {\n            env_sets(repl_env, k, v);\n        }\n        env_sets(repl_env, \"*ARGV*\", list(args.map(Str).collect()));\n\n        // core.mal: defined using the language itself\n        re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n        re(\n            \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\",\n            repl_env,\n        );\n\n        if let Some(f) = arg1 {\n            // Invoked with arguments\n            re(&format!(\"(load-file \\\"{}\\\")\", f), repl_env);\n            std::process::exit(0);\n        }\n\n        // main repl loop\n        while let Some(ref line) = readline::readline(\"user> \") {\n            if !line.is_empty() {\n                match rep(line, repl_env) {\n                    Ok(ref out) => println!(\"{}\", out),\n                    Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n                }\n            }\n        }\n        println!();\n    })\n}\n"
  },
  {
    "path": "impls/rust/step8_macros.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};\nuse crate::types::{error, list, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\n\nfn qq_iter(elts: &MalArgs) -> MalVal {\n    let mut acc = list!();\n    for elt in elts.iter().rev() {\n        if let List(v, _) = elt {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"splice-unquote\" {\n                        acc = list!(Sym(\"concat\".to_string()), v[1].clone(), acc);\n                        continue;\n                    }\n                }\n            }\n        }\n        acc = list!(Sym(\"cons\".to_string()), quasiquote(elt), acc);\n    }\n    acc\n}\n\nfn quasiquote(ast: &MalVal) -> MalVal {\n    match ast {\n        List(v, _) => {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"unquote\" {\n                        return v[1].clone();\n                    }\n                }\n            }\n            qq_iter(v)\n        }\n        Vector(v, _) => list!(Sym(\"vec\".to_string()), qq_iter(v)),\n        Hash(_, _) | Sym(_) => list!(Sym(\"quote\".to_string()), ast.clone()),\n        _ => ast.clone(),\n    }\n}\n\nfn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet {\n    let mut ast = orig_ast;\n    let mut env = orig_env;\n    // These variables ensure a sufficient lifetime for the data\n    // referenced by ast and env.\n    let mut live_ast;\n    let mut live_env;\n\n    'tco: loop {\n        match env_get(env, \"DEBUG-EVAL\") {\n            None | Some(Bool(false)) | Some(Nil) => (),\n            _ => println!(\"EVAL: {}\", print(ast)),\n        }\n        match ast {\n            Sym(s) => match env_get(env, s) {\n                Some(r) => return Ok(r),\n                None => return error(&format!(\"'{}' not found\", s)),\n            },\n            Vector(v, _) => {\n                let mut lst: MalArgs = vec![];\n                for a in v.iter() {\n                    lst.push(eval(a, env)?);\n                }\n                return Ok(vector(lst));\n            }\n            Hash(hm, _) => {\n                let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n                for (k, v) in hm.iter() {\n                    new_hm.insert(k.to_string(), eval(v, env)?);\n                }\n                return Ok(Hash(Rc::new(new_hm), Rc::new(Nil)));\n            }\n            List(l, _) => {\n                if l.is_empty() {\n                    return Ok(ast.clone());\n                }\n                let a0 = &l[0];\n                match a0 {\n                    Sym(a0sym) if a0sym == \"def!\" => {\n                        return env_set(env, &l[1], eval(&l[2], env)?);\n                    }\n                    Sym(a0sym) if a0sym == \"let*\" => {\n                        live_env = env_new(Some(env.clone()));\n                        env = &live_env;\n                        let (a1, a2) = (&l[1], &l[2]);\n                        match a1 {\n                            List(binds, _) | Vector(binds, _) => {\n                                for (b, e) in binds.iter().tuples() {\n                                    let val = eval(e, env)?;\n                                    env_set(env, b, val)?;\n                                }\n                            }\n                            _ => {\n                                return error(\"let* with non-List bindings\");\n                            }\n                        };\n                        live_ast = a2.clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"quote\" => return Ok(l[1].clone()),\n                    Sym(a0sym) if a0sym == \"quasiquote\" => {\n                        live_ast = quasiquote(&l[1]);\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"defmacro!\" => {\n                        let (a1, a2) = (&l[1], &l[2]);\n                        let r = eval(a2, env)?;\n                        match r {\n                            MalFunc(f) => {\n                                return env_set(\n                                    env,\n                                    a1,\n                                    MalFunc(FuncStruct {\n                                        is_macro: true,\n                                        ..f.clone()\n                                    }),\n                                )\n                            }\n                            _ => return error(\"set_macro on non-function\"),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"do\" => {\n                        for i in 1..l.len() - 1 {\n                            let _ = eval(&l[i], env)?;\n                        }\n                        live_ast = l.last().unwrap_or(&Nil).clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"if\" => {\n                        let cond = eval(&l[1], env)?;\n                        match cond {\n                            Bool(false) | Nil if l.len() >= 4 => {\n                                live_ast = l[3].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            Bool(false) | Nil => return Ok(Nil),\n                            _ if l.len() >= 3 => {\n                                live_ast = l[2].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            _ => return Ok(Nil),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"fn*\" => {\n                        let (a1, a2) = (l[1].clone(), l[2].clone());\n                        return Ok(MalFunc(FuncStruct {\n                            ast: Rc::new(a2),\n                            env: env.clone(),\n                            params: Rc::new(a1),\n                            is_macro: false,\n                            meta: Rc::new(Nil),\n                        }));\n                    }\n                    _ => match eval(a0, env)? {\n                        f @ MalFunc(FuncStruct { is_macro: true, .. }) => {\n                            let new_ast = f.apply(l[1..].to_vec())?;\n                            live_ast = new_ast;\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        f @ Func(_, _) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            return f.apply(args);\n                        }\n                        MalFunc(FuncStruct {\n                            ast: mast,\n                            env: menv,\n                            params: mparams,\n                            ..\n                        }) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            live_env = env_bind(menv.clone(), &mparams, args)?;\n                            env = &live_env;\n                            live_ast = (*mast).clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        _ => return error(\"attempt to call non-function\"),\n                    },\n                }\n            }\n            _ => return Ok(ast.clone()),\n        };\n    } // end 'tco loop\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nthread_local! {\n    static REPL_ENV: Env = env_new(None);\n}\n\nfn main() {\n    REPL_ENV.with(|repl_env| {\n    let mut args = std::env::args();\n    let arg1 = args.nth(1);\n\n    // `()` can be used when no completer is required\n\n    // core.rs: defined using rust\n    env_sets(repl_env, \"eval\", types::func(|a| REPL_ENV.with(|e| eval(&a[0], e))));\n    for (k, v) in core::ns() {\n        env_sets(repl_env, k, v);\n    }\n    env_sets(repl_env, \"*ARGV*\", list(args.map(Str).collect()));\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\n        \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\",\n        repl_env,\n    );\n    re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\",\n        repl_env);\n\n    if let Some(f) = arg1 {\n        // Invoked with arguments\n        re(&format!(\"(load-file \\\"{}\\\")\", f), repl_env);\n        std::process::exit(0);\n    }\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n  })\n}\n"
  },
  {
    "path": "impls/rust/step9_try.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};\nuse crate::types::{error, list, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\n\nfn qq_iter(elts: &MalArgs) -> MalVal {\n    let mut acc = list!();\n    for elt in elts.iter().rev() {\n        if let List(v, _) = elt {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"splice-unquote\" {\n                        acc = list!(Sym(\"concat\".to_string()), v[1].clone(), acc);\n                        continue;\n                    }\n                }\n            }\n        }\n        acc = list!(Sym(\"cons\".to_string()), quasiquote(elt), acc);\n    }\n    acc\n}\n\nfn quasiquote(ast: &MalVal) -> MalVal {\n    match ast {\n        List(v, _) => {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"unquote\" {\n                        return v[1].clone();\n                    }\n                }\n            }\n            qq_iter(v)\n        }\n        Vector(v, _) => list!(Sym(\"vec\".to_string()), qq_iter(v)),\n        Hash(_, _) | Sym(_) => list!(Sym(\"quote\".to_string()), ast.clone()),\n        _ => ast.clone(),\n    }\n}\n\nfn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet {\n    let mut ast = orig_ast;\n    let mut env = orig_env;\n    // These variables ensure a sufficient lifetime for the data\n    // referenced by ast and env.\n    let mut live_ast;\n    let mut live_env;\n\n    'tco: loop {\n        match env_get(env, \"DEBUG-EVAL\") {\n            None | Some(Bool(false)) | Some(Nil) => (),\n            _ => println!(\"EVAL: {}\", print(ast)),\n        }\n        match ast {\n            Sym(s) => match env_get(env, s) {\n                Some(r) => return Ok(r),\n                None => return error(&format!(\"'{}' not found\", s)),\n            },\n            Vector(v, _) => {\n                let mut lst: MalArgs = vec![];\n                for a in v.iter() {\n                    lst.push(eval(a, env)?);\n                }\n                return Ok(vector(lst));\n            }\n            Hash(hm, _) => {\n                let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n                for (k, v) in hm.iter() {\n                    new_hm.insert(k.to_string(), eval(v, env)?);\n                }\n                return Ok(Hash(Rc::new(new_hm), Rc::new(Nil)));\n            }\n            List(l, _) => {\n                if l.is_empty() {\n                    return Ok(ast.clone());\n                }\n                let a0 = &l[0];\n                match a0 {\n                    Sym(a0sym) if a0sym == \"def!\" => {\n                        return env_set(env, &l[1], eval(&l[2], env)?);\n                    }\n                    Sym(a0sym) if a0sym == \"let*\" => {\n                        live_env = env_new(Some(env.clone()));\n                        env = &live_env;\n                        let (a1, a2) = (&l[1], &l[2]);\n                        match a1 {\n                            List(binds, _) | Vector(binds, _) => {\n                                for (b, e) in binds.iter().tuples() {\n                                    let val = eval(e, env)?;\n                                    env_set(env, b, val)?;\n                                }\n                            }\n                            _ => {\n                                return error(\"let* with non-List bindings\");\n                            }\n                        };\n                        live_ast = a2.clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"quote\" => return Ok(l[1].clone()),\n                    Sym(a0sym) if a0sym == \"quasiquote\" => {\n                        live_ast = quasiquote(&l[1]);\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"defmacro!\" => {\n                        let (a1, a2) = (&l[1], &l[2]);\n                        let r = eval(a2, env)?;\n                        match r {\n                            MalFunc(f) => {\n                                return env_set(\n                                    env,\n                                    a1,\n                                    MalFunc(FuncStruct {\n                                        is_macro: true,\n                                        ..f.clone()\n                                    }),\n                                )\n                            }\n                            _ => return error(\"set_macro on non-function\"),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"try*\" => {\n                        if l.len() < 3 {\n                            live_ast = l[1].clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        match eval(&l[1], env) {\n                            Err(exc) => match &l[2] {\n                                List(c, _) => {\n                                    live_env = env_new(Some(env.clone()));\n                                    env = &live_env;\n                                    env_set(env, &c[1], exc)?;\n                                    live_ast = c[2].clone();\n                                    ast = &live_ast;\n                                    continue 'tco;\n                                }\n                                _ => return error(\"invalid catch block\"),\n                            },\n                            res => return res,\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"do\" => {\n                        for i in 1..l.len() - 1 {\n                            let _ = eval(&l[i], env)?;\n                        }\n                        live_ast = l.last().unwrap_or(&Nil).clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"if\" => {\n                        let cond = eval(&l[1], env)?;\n                        match cond {\n                            Bool(false) | Nil if l.len() >= 4 => {\n                                live_ast = l[3].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            Bool(false) | Nil => return Ok(Nil),\n                            _ if l.len() >= 3 => {\n                                live_ast = l[2].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            _ => return Ok(Nil),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"fn*\" => {\n                        let (a1, a2) = (l[1].clone(), l[2].clone());\n                        return Ok(MalFunc(FuncStruct {\n                            ast: Rc::new(a2),\n                            env: env.clone(),\n                            params: Rc::new(a1),\n                            is_macro: false,\n                            meta: Rc::new(Nil),\n                        }));\n                    }\n                    _ => match eval(a0, env)? {\n                        f @ MalFunc(FuncStruct { is_macro: true, .. }) => {\n                            let new_ast = f.apply(l[1..].to_vec())?;\n                            live_ast = new_ast;\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        f @ Func(_, _) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            return f.apply(args);\n                        }\n                        MalFunc(FuncStruct {\n                            ast: mast,\n                            env: menv,\n                            params: mparams,\n                            ..\n                        }) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            live_env = env_bind(menv.clone(), &mparams, args)?;\n                            env = &live_env;\n                            live_ast = (*mast).clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        _ => return error(\"attempt to call non-function\"),\n                    },\n                }\n            }\n            _ => return Ok(ast.clone()),\n        };\n    } // end 'tco loop\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nthread_local! {\n    static REPL_ENV: Env = env_new(None);\n}\n\nfn main() {\n    REPL_ENV.with(|repl_env| {\n    let mut args = std::env::args();\n    let arg1 = args.nth(1);\n\n    // `()` can be used when no completer is required\n\n    // core.rs: defined using rust\n    env_sets(repl_env, \"eval\", types::func(|a| REPL_ENV.with(|e| eval(&a[0], e))));\n    for (k, v) in core::ns() {\n        env_sets(repl_env, k, v);\n    }\n    env_sets(repl_env, \"*ARGV*\", list(args.map(Str).collect()));\n\n    // core.mal: defined using the language itself\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\n        \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\",\n        repl_env,\n    );\n    re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\",\n        repl_env);\n\n    if let Some(f) = arg1 {\n        // Invoked with arguments\n        re(&format!(\"(load-file \\\"{}\\\")\", f), repl_env);\n        std::process::exit(0);\n    }\n\n    // main repl loop\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n  })\n}\n"
  },
  {
    "path": "impls/rust/stepA_mal.rs",
    "content": "#![allow(non_snake_case)]\n\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nextern crate fnv;\nextern crate itertools;\nextern crate regex;\n\nmod readline;\n#[macro_use]\nmod types;\nuse crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};\nuse crate::types::{error, list, vector, FuncStruct, MalArgs, MalRet, MalVal};\nmod env;\nmod printer;\nmod reader;\nuse crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env};\n#[macro_use]\nmod core;\n\nimpl MalVal {\n    pub fn apply(&self, args: MalArgs) -> MalRet {\n        match self {\n            Func(f, _) => f(args),\n            MalFunc(FuncStruct {\n                ref ast,\n                ref env,\n                ref params,\n                ..\n            }) => {\n                let fn_env = &env_bind(env.clone(), params, args)?;\n                eval(ast, fn_env)\n            }\n            _ => error(\"attempt to call non-function\"),\n        }\n    }\n}\n\n// read\nfn read(str: &str) -> MalRet {\n    reader::read_str(str)\n}\n\n// eval\n\nfn qq_iter(elts: &MalArgs) -> MalVal {\n    let mut acc = list!();\n    for elt in elts.iter().rev() {\n        if let List(v, _) = elt {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"splice-unquote\" {\n                        acc = list!(Sym(\"concat\".to_string()), v[1].clone(), acc);\n                        continue;\n                    }\n                }\n            }\n        }\n        acc = list!(Sym(\"cons\".to_string()), quasiquote(elt), acc);\n    }\n    acc\n}\n\nfn quasiquote(ast: &MalVal) -> MalVal {\n    match ast {\n        List(v, _) => {\n            if v.len() == 2 {\n                if let Sym(ref s) = v[0] {\n                    if s == \"unquote\" {\n                        return v[1].clone();\n                    }\n                }\n            }\n            qq_iter(v)\n        }\n        Vector(v, _) => list!(Sym(\"vec\".to_string()), qq_iter(v)),\n        Hash(_, _) | Sym(_) => list!(Sym(\"quote\".to_string()), ast.clone()),\n        _ => ast.clone(),\n    }\n}\n\nfn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet {\n    let mut ast = orig_ast;\n    let mut env = orig_env;\n    // These variables ensure a sufficient lifetime for the data\n    // referenced by ast and env.\n    let mut live_ast;\n    let mut live_env;\n\n    'tco: loop {\n        match env_get(env, \"DEBUG-EVAL\") {\n            None | Some(Bool(false)) | Some(Nil) => (),\n            _ => println!(\"EVAL: {}\", print(ast)),\n        }\n        match ast {\n            Sym(s) => match env_get(env, s) {\n                Some(r) => return Ok(r),\n                None => return error(&format!(\"'{}' not found\", s)),\n            },\n            Vector(v, _) => {\n                let mut lst: MalArgs = vec![];\n                for a in v.iter() {\n                    lst.push(eval(a, env)?);\n                }\n                return Ok(vector(lst));\n            }\n            Hash(hm, _) => {\n                let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n                for (k, v) in hm.iter() {\n                    new_hm.insert(k.to_string(), eval(v, env)?);\n                }\n                return Ok(Hash(Rc::new(new_hm), Rc::new(Nil)));\n            }\n            List(l, _) => {\n                if l.is_empty() {\n                    return Ok(ast.clone());\n                }\n                let a0 = &l[0];\n                match a0 {\n                    Sym(a0sym) if a0sym == \"def!\" => {\n                        return env_set(env, &l[1], eval(&l[2], env)?);\n                    }\n                    Sym(a0sym) if a0sym == \"let*\" => {\n                        live_env = env_new(Some(env.clone()));\n                        env = &live_env;\n                        let (a1, a2) = (&l[1], &l[2]);\n                        match a1 {\n                            List(binds, _) | Vector(binds, _) => {\n                                for (b, e) in binds.iter().tuples() {\n                                    let val = eval(e, env)?;\n                                    env_set(env, b, val)?;\n                                }\n                            }\n                            _ => {\n                                return error(\"let* with non-List bindings\");\n                            }\n                        };\n                        live_ast = a2.clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"quote\" => return Ok(l[1].clone()),\n                    Sym(a0sym) if a0sym == \"quasiquote\" => {\n                        live_ast = quasiquote(&l[1]);\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"defmacro!\" => {\n                        let (a1, a2) = (&l[1], &l[2]);\n                        let r = eval(a2, env)?;\n                        match r {\n                            MalFunc(f) => {\n                                return env_set(\n                                    env,\n                                    a1,\n                                    MalFunc(FuncStruct {\n                                        is_macro: true,\n                                        ..f.clone()\n                                    }),\n                                )\n                            }\n                            _ => return error(\"set_macro on non-function\"),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"try*\" => {\n                        if l.len() < 3 {\n                            live_ast = l[1].clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        match eval(&l[1], env) {\n                            Err(exc) => match &l[2] {\n                                List(c, _) => {\n                                    live_env = env_new(Some(env.clone()));\n                                    env = &live_env;\n                                    env_set(env, &c[1], exc)?;\n                                    live_ast = c[2].clone();\n                                    ast = &live_ast;\n                                    continue 'tco;\n                                }\n                                _ => return error(\"invalid catch block\"),\n                            },\n                            res => return res,\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"do\" => {\n                        for i in 1..l.len() - 1 {\n                            let _ = eval(&l[i], env)?;\n                        }\n                        live_ast = l.last().unwrap_or(&Nil).clone();\n                        ast = &live_ast;\n                        continue 'tco;\n                    }\n                    Sym(a0sym) if a0sym == \"if\" => {\n                        let cond = eval(&l[1], env)?;\n                        match cond {\n                            Bool(false) | Nil if l.len() >= 4 => {\n                                live_ast = l[3].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            Bool(false) | Nil => return Ok(Nil),\n                            _ if l.len() >= 3 => {\n                                live_ast = l[2].clone();\n                                ast = &live_ast;\n                                continue 'tco;\n                            }\n                            _ => return Ok(Nil),\n                        }\n                    }\n                    Sym(a0sym) if a0sym == \"fn*\" => {\n                        let (a1, a2) = (l[1].clone(), l[2].clone());\n                        return Ok(MalFunc(FuncStruct {\n                            ast: Rc::new(a2),\n                            env: env.clone(),\n                            params: Rc::new(a1),\n                            is_macro: false,\n                            meta: Rc::new(Nil),\n                        }));\n                    }\n                    _ => match eval(a0, env)? {\n                        f @ MalFunc(FuncStruct { is_macro: true, .. }) => {\n                            let new_ast = f.apply(l[1..].to_vec())?;\n                            live_ast = new_ast;\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        f @ Func(_, _) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            return f.apply(args);\n                        }\n                        MalFunc(FuncStruct {\n                            ast: mast,\n                            env: menv,\n                            params: mparams,\n                            ..\n                        }) => {\n                            let mut args: MalArgs = vec![];\n                            for i in 1..l.len() {\n                                args.push(eval(&l[i], env)?);\n                            }\n                            live_env = env_bind(menv.clone(), &mparams, args)?;\n                            env = &live_env;\n                            live_ast = (*mast).clone();\n                            ast = &live_ast;\n                            continue 'tco;\n                        }\n                        _ => return error(\"attempt to call non-function\"),\n                    },\n                }\n            }\n            _ => return Ok(ast.clone()),\n        };\n    } // end 'tco loop\n}\n\n// print\nfn print(ast: &MalVal) -> String {\n    ast.pr_str(true)\n}\n\nfn rep(str: &str, env: &Env) -> Result<String, MalVal> {\n    let ast = read(str)?;\n    let exp = eval(&ast, env)?;\n    Ok(print(&exp))\n}\n\nfn re(str: &str, env: &Env) {\n    if let Ok(ast) = read(str) {\n        if eval(&ast, env).is_ok() {\n            return;\n        }\n    }\n    panic!(\"error during startup\");\n}\n\nthread_local! {\n    static REPL_ENV: Env = env_new(None);\n}\n\nfn main() {\n    REPL_ENV.with(|repl_env| {\n    let mut args = std::env::args();\n    let arg1 = args.nth(1);\n\n    // `()` can be used when no completer is required\n\n    // core.rs: defined using rust\n    env_sets(repl_env, \"eval\", types::func(|a| REPL_ENV.with(|e| eval(&a[0], e))));\n    for (k, v) in core::ns() {\n        env_sets(repl_env, k, v);\n    }\n    env_sets(repl_env, \"*ARGV*\", list(args.map(Str).collect()));\n\n    // core.mal: defined using the language itself\n    re(\"(def! *host-language* \\\"rust\\\")\", repl_env);\n    re(\"(def! not (fn* (a) (if a false true)))\", repl_env);\n    re(\n        \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\",\n        repl_env,\n    );\n    re(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\",\n        repl_env);\n\n    if let Some(f) = arg1 {\n        // Invoked with arguments\n        re(&format!(\"(load-file \\\"{}\\\")\", f), repl_env);\n        std::process::exit(0);\n    }\n\n    // main repl loop\n    re(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env);\n    while let Some(ref line) = readline::readline(\"user> \") {\n        if !line.is_empty() {\n            match rep(line, repl_env) {\n                Ok(ref out) => println!(\"{}\", out),\n                Err(ref e) => println!(\"Error: {}\", e.pr_str(true)),\n            }\n        }\n    }\n    println!();\n  })\n}\n"
  },
  {
    "path": "impls/rust/types.rs",
    "content": "use std::cell::RefCell;\nuse std::rc::Rc;\n//use std::collections::HashMap;\nuse fnv::FnvHashMap;\nuse itertools::Itertools;\n\nuse crate::env::Env;\nuse crate::types::MalVal::{Bool, Func, Hash, Int, Kwd, List, MalFunc, Nil, Str, Sym, Vector};\n\n// Function closures and atoms may create cyclic dependencies, so\n// reference counting should be replaced at least for these two kinds\n// of references.\n\n#[derive(Clone)]\npub enum MalVal {\n    Nil,\n    Bool(bool),\n    Int(i64),\n    //Float(f64),\n    Str(String),\n    Sym(String),\n    Kwd(String),\n    List(Rc<Vec<MalVal>>, Rc<MalVal>),\n    Vector(Rc<Vec<MalVal>>, Rc<MalVal>),\n    Hash(Rc<FnvHashMap<String, MalVal>>, Rc<MalVal>),\n    Func(fn(MalArgs) -> MalRet, Rc<MalVal>),\n    MalFunc(FuncStruct),\n    Atom(Rc<RefCell<MalVal>>),\n}\n\n#[derive(Clone)]\npub struct FuncStruct {\n    pub ast: Rc<MalVal>,\n    pub env: Env,\n    pub params: Rc<MalVal>,\n    pub is_macro: bool,\n    pub meta: Rc<MalVal>,\n}\n\npub type MalArgs = Vec<MalVal>;\npub type MalRet = Result<MalVal, MalVal>;\n\n// type utility macros\n\nmacro_rules! list {\n  [$($args:expr),*] => {{\n    let v: Vec<MalVal> = vec![$($args),*];\n    List(Rc::new(v),Rc::new(Nil))\n  }}\n}\n\n// type utility functions\n\npub fn error<T>(s: &str) -> Result<T, MalVal> {\n    Err(Str(s.to_string()))\n}\n\npub fn list(seq: MalArgs) -> MalVal {\n    List(Rc::new(seq), Rc::new(Nil))\n}\n\npub fn vector(seq: MalArgs) -> MalVal {\n    Vector(Rc::new(seq), Rc::new(Nil))\n}\n\nimpl PartialEq for MalVal {\n    fn eq(&self, other: &MalVal) -> bool {\n        match (self, other) {\n            (Nil, Nil) => true,\n            (Bool(ref a), Bool(ref b)) => a == b,\n            (Int(ref a), Int(ref b)) => a == b,\n            (Str(ref a), Str(ref b)) => a == b,\n            (Sym(ref a), Sym(ref b)) => a == b,\n            (Kwd(ref a), Kwd(ref b)) => a == b,\n            (List(ref a, _), List(ref b, _))\n            | (Vector(ref a, _), Vector(ref b, _))\n            | (List(ref a, _), Vector(ref b, _))\n            | (Vector(ref a, _), List(ref b, _)) => a == b,\n            (Hash(ref a, _), Hash(ref b, _)) => a == b,\n            (MalFunc { .. }, MalFunc { .. }) => false,\n            _ => false,\n        }\n    }\n}\n\npub fn func(f: fn(MalArgs) -> MalRet) -> MalVal {\n    Func(f, Rc::new(Nil))\n}\n\npub fn _assoc(mut hm: FnvHashMap<String, MalVal>, kvs: MalArgs) -> MalRet {\n    if kvs.len() % 2 != 0 {\n        return error(\"odd number of elements\");\n    }\n    for (k, v) in kvs.iter().tuples() {\n        hm.insert(wrap_map_key(k)?, v.clone());\n    }\n    Ok(Hash(Rc::new(hm), Rc::new(Nil)))\n}\n\npub fn wrap_map_key(k: &MalVal) -> Result<String, MalVal> {\n    match k {\n        Str(s) => Ok(String::from(s)),\n        Kwd(s) => Ok(format!(\"\\u{29e}{}\", s)),\n        _ => error(\"key is not string\"),\n    }\n}\n\npub fn unwrap_map_key(s: &str) -> MalVal {\n    match s.strip_prefix('\\u{29e}') {\n        Some(keyword) => Kwd(String::from(keyword)),\n        _ => Str(String::from(s)),\n    }\n}\n\npub fn hash_map(kvs: MalArgs) -> MalRet {\n    let hm: FnvHashMap<String, MalVal> = FnvHashMap::default();\n    _assoc(hm, kvs)\n}\n"
  },
  {
    "path": "impls/scala/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Java and maven\nRUN apt-get -y install openjdk-8-jdk\n#RUN apt-get -y install maven2\n#ENV MAVEN_OPTS -Duser.home=/mal\n\n# Scala\nRUN echo \"deb http://dl.bintray.com/sbt/debian /\" > /etc/apt/sources.list.d/sbt.list\nRUN apt-get -y update\n\nRUN apt-get -y --force-yes install sbt\nRUN apt-get -y install scala\nENV SBT_OPTS -Duser.home=/mal\n\n"
  },
  {
    "path": "impls/scala/Makefile",
    "content": "SOURCES_BASE = types.scala reader.scala printer.scala\nSOURCES_LISP = env.scala core.scala stepA_mal.scala\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nTARGET_DIR=target/scala-2.11\n\nall: $(TARGET_DIR)/mal.jar\n\ndist: mal\n\nmal: $(TARGET_DIR)/mal.jar\n\tcp $< $@\n\n$(TARGET_DIR)/mal.jar:\n\tsbt assembly\n\n$(TARGET_DIR)/classes/step%.class: step%.scala $(SOURCES)\n\tsbt assembly\n\nclean:\n\trm -rf mal target\n\n.PHONY: all dist clean\n"
  },
  {
    "path": "impls/scala/assembly.sbt",
    "content": "import sbtassembly.AssemblyPlugin.defaultShellScript\n\ntest in assembly := {}\nassemblyJarName in assembly := \"mal.jar\"\nmainClass in assembly := Some(\"stepA_mal\")\nassemblyOption in assembly ~= { _.copy(prependShellScript = Some(defaultShellScript)) }\n"
  },
  {
    "path": "impls/scala/build.sbt",
    "content": "lazy val root = (project in file(\".\")).\n  settings(\n    name := \"mal\",\n    version := \"0.1\",\n    scalaVersion := \"2.11.4\"\n  )\n"
  },
  {
    "path": "impls/scala/core.scala",
    "content": "import scala.collection.mutable\nimport scala.io.Source\n\nimport types.{MalList, _list, _list_Q,\n              MalVector, _vector, _vector_Q,\n              MalHashMap, _hash_map_Q, _hash_map,\n              Func, MalFunction}\nimport printer._pr_list\n\nobject core {\n  def mal_throw(a: List[Any]) = {\n    throw new types.MalException(printer._pr_str(a(0))).init(a(0))\n  }\n\n  // Scalar functions\n  def keyword(a: List[Any]) = {\n    val s = a(0).asInstanceOf[String]\n    if (0 < s.length && s(0) == '\\u029e') s else \"\\u029e\" + s\n  }\n\n  def keyword_Q(a: List[Any]) = {\n    a(0) match {\n      case s: String => s.length != 0 && s(0) == '\\u029e'\n      case _ => false\n    }\n  }\n\n  def string_Q(a: List[Any]) = {\n    a(0) match {\n      case s: String => s.length == 0 || s(0) != '\\u029e'\n      case _ => false\n    }\n  }\n\n  def fn_Q(a: List[Any]) = {\n    a(0) match {\n      case s: Func => true\n      case s: MalFunction => !s.asInstanceOf[MalFunction].ismacro\n      case _ => false\n    }\n  }\n\n  def macro_Q(a: List[Any]) = {\n    a(0) match {\n      case s: MalFunction => s.asInstanceOf[MalFunction].ismacro\n      case _ => false\n    }\n  }\n\n  // number functions\n  def _bool_op(a: List[Any], op: (Long, Long) => Boolean) = {\n    op(a(0).asInstanceOf[Long],a(1).asInstanceOf[Long])\n  }\n\n  def _num_op(a: List[Any], op: (Long, Long) => Long) = {\n    op(a(0).asInstanceOf[Long],a(1).asInstanceOf[Long])\n  }\n\n  def number_Q(a: List[Any]) = {\n    a(0).isInstanceOf[Long] || a(0).isInstanceOf[Double]\n  }\n\n\n  // string functions\n  def read_string(a: List[Any]) = {\n    reader.read_str(a(0).asInstanceOf[String])\n  }\n\n  def slurp(a: List[Any]) = {\n    Source.fromFile(a(0).asInstanceOf[String]).getLines.mkString(\"\\n\") + \"\\n\"\n  }\n\n  // Hash Map functions\n  def assoc(a: List[Any]): Any = {\n    a(0).asInstanceOf[MalHashMap] ++ _hash_map(a.drop(1):_*)\n  }\n\n  def dissoc(a: List[Any]): Any = {\n    var kSet = a.drop(1).toSet\n    a(0).asInstanceOf[MalHashMap]\n      .filterKeys{ !kSet.contains(_) }\n  }\n\n  def get(a: List[Any]): Any = {\n    val hm = a(0).asInstanceOf[MalHashMap]\n    val key = a(1).asInstanceOf[String]\n    if (hm != null && hm.value.contains(key)) hm(key) else null\n  }\n\n  def contains_Q(a: List[Any]): Any = {\n    a(0).asInstanceOf[MalHashMap].value\n      .contains(a(1).asInstanceOf[String])\n  }\n\n\n  // sequence functions\n  def concat(a: List[Any]): Any = {\n    _list((for (sq <- a) yield types._toIter(sq)).flatten:_*)\n  }\n\n  def nth(a: List[Any]): Any = {\n    val lst = a(0).asInstanceOf[MalList].value\n    val idx = a(1).asInstanceOf[Long]\n    if (idx < lst.length) {\n      lst(idx.toInt)\n    } else {\n      throw new Exception(\"nth: index out of range\")\n    }\n  }\n\n  def first(a: List[Any]): Any = {\n    a(0) match {\n      case null => null\n      case ml: MalList => {\n        val lst = ml.value\n        if (lst.length > 0) lst(0) else null\n      }\n    }\n  }\n\n  def rest(a: List[Any]): Any = {\n    a(0) match {\n      case null => _list()\n      case ml: MalList => _list(ml.drop(1).value:_*)\n    }\n  }\n\n  def empty_Q(a: List[Any]): Any = {\n    a(0) match {\n      case null => true\n      case ml: MalList => ml.value.isEmpty\n    }\n  }\n\n  def count(a: List[Any]): Any = {\n    a(0) match {\n      case null => 0\n      case ml: MalList => ml.value.length.asInstanceOf[Long]\n    }\n  }\n\n  def apply(a: List[Any]): Any = {\n    a match {\n      case f :: rest => {\n        var args1 = rest.slice(0,rest.length-1)\n        var args = args1 ++ rest(rest.length-1).asInstanceOf[MalList].value\n        types._apply(f, args)\n      }\n      case _ => throw new Exception(\"invalid apply call\")\n    }\n  }\n\n  def do_map(a: List[Any]): Any = {\n    a match {\n      case f :: seq :: Nil => {\n        var res = seq.asInstanceOf[MalList].map(x => types._apply(f,List(x)));\n        _list(res.value:_*)\n      }\n      case _ => throw new Exception(\"invalid map call\")\n    }\n  }\n\n  def conj(a: List[Any]): Any = {\n    a(0) match {\n      case mv: MalVector => {\n        _vector(mv.value ++ a.slice(1,a.length):_*)\n      }\n      case ml: MalList => {\n        _list(a.slice(1,a.length).reverse ++ ml.value:_*)\n      }\n    }\n  }\n\n  def seq(a: List[Any]): Any = {\n    a(0) match {\n      case mv: MalVector => {\n        if (mv.value.length == 0) null else _list(mv.value:_*)\n      }\n      case ml: MalList => {\n        if (ml.value.length == 0) null else ml\n      }\n      case ms: String => {\n        if (ms.length == 0) null else _list(ms.split(\"(?!^)\"):_*)\n      }\n      case null => null\n      case _ => throw new Exception(\"seq: called on non-sequence\")\n    }\n  }\n\n\n  // meta functions\n  def with_meta(a: List[Any]): Any = {\n    val meta: Any = a(1)\n    a(0) match {\n      case ml: MalList => {\n        val new_ml = ml.clone()\n        new_ml.meta = meta\n        new_ml\n      }\n      case hm: MalHashMap => {\n        val new_hm = hm.clone()\n        new_hm.meta = meta\n        new_hm\n      }\n      case fn: Func => {\n        val new_fn = fn.clone()\n        new_fn.meta = meta\n        new_fn\n      }\n      case fn: MalFunction => {\n        val new_fn = fn.clone()\n        new_fn.meta = meta\n        new_fn\n      }\n      case _ => throw new Exception(\"no meta support for \" + a(0).getClass)\n    }\n  }\n\n  def meta(a: List[Any]): Any = {\n    a(0) match {\n      case ml: MalList     => ml.meta\n      case hm: MalHashMap  => hm.meta\n      case fn: Func        => fn.meta\n      case fn: MalFunction => fn.meta\n      case _ => throw new Exception(\"no meta support for \" + a(0).getClass)\n    }\n  }\n\n\n  // atom functions\n  def reset_BANG(a: List[Any]): Any = {\n    a(0).asInstanceOf[types.Atom].value = a(1)\n    a(1)\n  }\n\n  def swap_BANG(a: List[Any]): Any = {\n    a match {\n      case a0 :: f :: rest => {\n        val atm = a0.asInstanceOf[types.Atom]\n        val args = atm.value +: rest\n        atm.value = types._apply(f, args)\n        atm.value\n      }\n      case _ => throw new Exception(\"invalid swap! call\")\n    }\n  }\n\n\n  val ns: Map[String, (List[Any]) => Any] = Map(\n    \"=\"  -> ((a: List[Any]) => types._equal_Q(a(0), a(1))),\n    \"throw\" -> mal_throw _,\n    \"nil?\" -> ((a: List[Any]) => a(0) == null),\n    \"true?\" -> ((a: List[Any]) => a(0) == true),\n    \"false?\" -> ((a: List[Any]) => a(0) == false),\n    \"number?\" -> number_Q _,\n    \"string?\" -> string_Q _,\n    \"symbol\" -> ((a: List[Any]) => Symbol(a(0).asInstanceOf[String])),\n    \"symbol?\" -> ((a: List[Any]) => a(0).isInstanceOf[Symbol]),\n    \"keyword\" -> keyword _,\n    \"keyword?\" -> keyword_Q _,\n    \"fn?\" -> fn_Q,\n    \"macro?\" -> macro_Q,\n\n    \"pr-str\" -> ((a: List[Any]) => _pr_list(a, true, \" \")),\n    \"str\" -> ((a: List[Any]) => _pr_list(a, false, \"\")),\n    \"prn\" -> ((a: List[Any]) => { println(_pr_list(a, true, \" \")); null}),\n    \"println\" -> ((a: List[Any]) => { println(_pr_list(a, false, \" \")); null}),\n    \"readline\" -> ((a: List[Any]) => readLine(a(0).asInstanceOf[String])),\n    \"read-string\" -> read_string _,\n    \"slurp\" -> slurp _,\n\n    \"<\"  -> ((a: List[Any]) => _bool_op(a, _ < _)),\n    \"<=\" -> ((a: List[Any]) => _bool_op(a, _ <= _)),\n    \">\"  -> ((a: List[Any]) => _bool_op(a, _ > _)),\n    \">=\" -> ((a: List[Any]) => _bool_op(a, _ >= _)),\n    \"+\"  -> ((a: List[Any]) => _num_op(a, _ + _)),\n    \"-\"  -> ((a: List[Any]) => _num_op(a, _ - _)),\n    \"*\"  -> ((a: List[Any]) => _num_op(a, _ * _)),\n    \"/\"  -> ((a: List[Any]) => _num_op(a, _ / _)),\n    \"time-ms\" -> ((a: List[Any]) => System.currentTimeMillis),\n\n    \"list\" -> ((a: List[Any]) => _list(a:_*)),\n    \"list?\" -> ((a: List[Any]) => _list_Q(a(0))),\n    \"vector\" -> ((a: List[Any]) => _vector(a:_*)),\n    \"vector?\" -> ((a: List[Any]) => _vector_Q(a(0))),\n    \"hash-map\" -> ((a: List[Any]) => _hash_map(a:_*)),\n    \"map?\" -> ((a: List[Any]) => _hash_map_Q(a(0))),\n    \"assoc\" -> assoc _,\n    \"dissoc\" -> dissoc _,\n    \"get\" -> get _,\n    \"contains?\" -> contains_Q _,\n    \"keys\" -> ((a: List[Any]) => a(0).asInstanceOf[MalHashMap].keys),\n    \"vals\" -> ((a: List[Any]) => a(0).asInstanceOf[MalHashMap].vals),\n\n    \"sequential?\" -> ((a: List[Any]) => types._sequential_Q(a(0))),\n    \"cons\" -> ((a: List[Any]) => a(0) +: a(1).asInstanceOf[MalList]),\n    \"concat\" -> concat _,\n    \"vec\" -> ((a: List[Any]) => _vector(a(0).asInstanceOf[MalList].value:_*)),\n    \"nth\" -> nth _,\n    \"first\" -> first _,\n    \"rest\" -> rest _,\n    \"empty?\" -> empty_Q _,\n    \"count\" -> count _,\n    \"apply\" -> apply _,\n    \"map\" -> do_map _,\n\n    \"conj\" -> conj _,\n    \"seq\" -> seq _,\n\n    \"with-meta\" -> with_meta _,\n    \"meta\" -> meta _,\n    \"atom\" -> ((a: List[Any]) => new types.Atom(a(0))),\n    \"atom?\" -> ((a: List[Any]) => a(0).isInstanceOf[types.Atom]),\n    \"deref\" -> ((a: List[Any]) => a(0).asInstanceOf[types.Atom].value),\n    \"reset!\" -> reset_BANG _,\n    \"swap!\" -> swap_BANG _\n    )\n}\n\n// vim:ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/env.scala",
    "content": "import types._list\n\nimport scala.collection.mutable\n\nobject env {\n  class Env(outer: Env = null,\n            binds: Iterator[Any] = null,\n            exprs: Iterator[Any] = null) {\n    val data: mutable.Map[Symbol, Any] = mutable.Map()\n    if (binds != null && exprs != null) {\n      binds.foreach(b => {\n        val k = b.asInstanceOf[Symbol]\n        if (k == '&) {\n          data(binds.next().asInstanceOf[Symbol]) = _list(exprs.toSeq:_*)\n        } else {\n          data(k) = exprs.next()\n        }\n      })\n    }\n\n    def find(key: Symbol): Env = {\n        if (data.contains(key)) {\n            this\n        } else if (outer != null) {\n            outer.find(key)\n        } else {\n            null\n        }\n    }\n    def set(key: Symbol, value: Any): Any = {\n        data(key) = value\n        value\n    }\n    def get(key: Symbol): Any = {\n        val env = find(key)\n        if (env == null) throw new Exception(\"'\" + key.name + \"' not found\")\n        env.data(key)\n    }\n  }\n}\n\n// vim:ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/printer.scala",
    "content": "import types.{MalList, MalVector, MalHashMap, MalFunction}\n\n\nobject printer {\n  def _pr_str(obj: Any, print_readably: Boolean = true): String = {\n    val _r = print_readably\n    return obj match {\n      case v: MalVector   => v.toString(_r)\n      case l: MalList     => l.toString(_r)\n      case hm: MalHashMap => hm.toString(_r)\n      case s: String      => {\n        if (s.length > 0 && s(0) == '\\u029e') {\n          \":\" + s.substring(1,s.length)\n        } else if (_r) {\n          //println(\"here1: \" + s)\n          \"\\\"\" + s.replace(\"\\\\\", \"\\\\\\\\\")\n                  .replace(\"\\\"\", \"\\\\\\\"\")\n                  .replace(\"\\n\", \"\\\\n\") + \"\\\"\"\n        } else {\n          s\n        }\n      }\n      case Symbol(s)      => s\n      case a: types.Atom  => \"(atom \" + a.value + \")\"\n      case null           => \"nil\"\n      case _              => {\n        if (obj.isInstanceOf[MalFunction]) {\n          val f = obj.asInstanceOf[MalFunction]\n          \"<function (fn* \" + _pr_str(f.params) + \" \" + _pr_str(f.ast) + \")>\"\n        } else {\n          obj.toString\n        }\n      }\n    }\n  }\n\n  def _pr_list(lst: List[Any], print_readably: Boolean = true,\n               sep: String = \" \"): String = {\n    lst.map{_pr_str(_, print_readably)}.mkString(sep)\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/project/assembly.sbt",
    "content": "addSbtPlugin(\"com.eed3si9n\" % \"sbt-assembly\" % \"0.14.6\")\n"
  },
  {
    "path": "impls/scala/reader.scala",
    "content": "import scala.util.matching.Regex\n\nimport types.{MalList, _list, MalVector, _vector, MalHashMap, _hash_map}\n\nobject reader {\n\n  class Reader (tokens: Array[String]) {\n    var data = tokens\n    var position: Int = 0\n    def peek(): String = {\n      if (position >= data.length) return(null)\n      data(position)\n    }\n    def next(): String = {\n      if (position >= data.length) return(null)\n      position = position + 1\n      data(position-1)\n    }\n  }\n\n  def tokenize(str: String): Array[String] = {\n    val re = \"\"\"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)\"\"\".r\n    re.findAllMatchIn(str).map{ _.group(1) }\n                          .filter{ s => s != \"\" && s(0) != ';' }\n                          .toArray\n  }\n\n  def parse_str(s: String): String = {\n    // TODO: use re.replaceAllIn instead for single pass\n    s.replace(\"\\\\\\\\\", \"\\u029e\")\n     .replace(\"\\\\\\\"\", \"\\\"\")\n     .replace(\"\\\\n\", \"\\n\")\n     .replace(\"\\u029e\", \"\\\\\")\n  }\n\n  def read_atom(rdr: Reader): Any = {\n    val token = rdr.next()\n    val re_int = \"\"\"^(-?[0-9]+)$\"\"\".r\n    val re_flt = \"\"\"^(-?[0-9][0-9.]*)$\"\"\".r\n    val re_str =  \"\"\"^\"((?:\\\\.|[^\\\\\"])*)\"$\"\"\".r\n    val re_str_bad =  \"\"\"^\"(.*)$\"\"\".r\n    val re_key = \"\"\"^:(.*)$\"\"\".r\n    return token match {\n      case re_int(i) => i.toLong      // integer\n      case re_flt(f) => f.toDouble    // float\n      case re_str(s) => parse_str(s)  // string\n      case re_str_bad(s) =>\n        throw new Exception(\"expected '\\\"', got EOF\")\n      case re_key(k) => \"\\u029e\" + k  // keyword\n      case \"nil\"     => null\n      case \"true\"    => true\n      case \"false\"   => false\n      case _         => Symbol(token) // symbol\n    }\n  }\n\n  def read_list(rdr: Reader,\n                start: String = \"(\", end: String = \")\"): MalList = {\n    var ast: MalList = _list()\n    var token = rdr.next()\n    if (token != start) throw new Exception(\"expected '\" + start + \"', got EOF\")\n    while ({token = rdr.peek(); token != end}) {\n      if (token == null) throw new Exception(\"expected '\" + end + \"', got EOF\")\n      ast = ast :+ read_form(rdr)\n    }\n    rdr.next()\n    ast\n  }\n\n  def read_form(rdr: Reader): Any = {\n    return rdr.peek() match {\n      case \"'\"  => { rdr.next; _list(Symbol(\"quote\"), read_form(rdr)) }\n      case \"`\"  => { rdr.next; _list(Symbol(\"quasiquote\"), read_form(rdr)) }\n      case \"~\"  => { rdr.next; _list(Symbol(\"unquote\"), read_form(rdr)) }\n      case \"~@\" => { rdr.next; _list(Symbol(\"splice-unquote\"), read_form(rdr)) }\n      case \"^\"  => { rdr.next; val meta = read_form(rdr);\n                     _list(Symbol(\"with-meta\"), read_form(rdr), meta) }\n      case \"@\"  => { rdr.next; _list(Symbol(\"deref\"), read_form(rdr)) }\n\n      case \"(\"  => read_list(rdr)\n      case \")\"  => throw new Exception(\"unexpected ')')\")\n      case \"[\"  => _vector(read_list(rdr, \"[\", \"]\").value:_*)\n      case \"]\"  => throw new Exception(\"unexpected ']')\")\n      case \"{\"  => _hash_map(read_list(rdr, \"{\", \"}\").value:_*)\n      case \"}\"  => throw new Exception(\"unexpected '}')\")\n      case _    => read_atom(rdr)\n    }\n  }\n\n  def read_str(str: String): Any = {\n    val tokens = tokenize(str)\n    if (tokens.length == 0) return null\n    return read_form(new Reader(tokens))\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/run",
    "content": "#!/usr/bin/env bash\nexec java -classpath \"$(dirname $0)/target/scala-2.11/mal.jar\" \"${STEP:-stepA_mal}\" \"$@\"\n"
  },
  {
    "path": "impls/scala/step0_repl.scala",
    "content": "object step0_repl {\n  def READ(str: String): String = {\n    str\n  }\n\n  def EVAL(str: String, env: String): String = {\n    str\n  }\n\n  def PRINT(str: String): String = {\n    str\n  }\n\n  def REP(str: String): String = {\n    PRINT(EVAL(READ(str), \"\"))\n  }\n\n  def main(args: Array[String]) {\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Exception => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step1_read_print.scala",
    "content": "import reader.tokenize\n\nobject step1_read_print {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def EVAL(ast: Any, env: String): Any = {\n    ast\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val REP = (str: String) => {\n      PRINT(EVAL(READ(str), \"\"))\n    }\n\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Exception => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step2_eval.scala",
    "content": "import types.{MalList, _list_Q, MalVector, MalHashMap, MalFunction}\n\nobject step2_eval {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def EVAL(ast: Any, env: Map[Symbol,Any]): Any = {\n\n    //  println(\"EVAL: \" + printer._pr_str(ast,true))\n\n    ast match {\n      case s : Symbol    => return env(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n    if (ast.asInstanceOf[MalList].value.length == 0)\n      return ast\n    ast.asInstanceOf[MalList].map(EVAL(_, env)).value match {\n      case f :: el => {\n        var fn: List[Any] => Any = null\n        try {\n          fn = f.asInstanceOf[List[Any] => Any]\n        } catch {\n          case _: Throwable =>\n            throw new Exception(\"attempt to call non-function\")\n        }\n        return fn(el)\n      }\n      case _ => throw new Exception(\"invalid apply\")\n    }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Map[Symbol,Any] = Map(\n      '+ -> ((a: List[Any]) => a(0).asInstanceOf[Long] + a(1).asInstanceOf[Long]),\n      '- -> ((a: List[Any]) => a(0).asInstanceOf[Long] - a(1).asInstanceOf[Long]),\n      '* -> ((a: List[Any]) => a(0).asInstanceOf[Long] * a(1).asInstanceOf[Long]),\n      '/ -> ((a: List[Any]) => a(0).asInstanceOf[Long] / a(1).asInstanceOf[Long]))\n    val REP = (str: String) => {\n      PRINT(EVAL(READ(str), repl_env))\n    }\n\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Exception => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step3_env.scala",
    "content": "import types.{MalList, _list_Q, MalVector, MalHashMap, MalFunction}\nimport env.Env\n\nobject step3_env {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def EVAL(ast: Any, env: Env): Any = {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        return EVAL(a2, let_env)\n      }\n      case _ => {\n        // function call\n        ast.asInstanceOf[MalList].map(EVAL(_, env)).value match {\n          case f :: el => {\n            var fn: List[Any] => Any = null\n            try {\n              fn = f.asInstanceOf[(List[Any]) => Any]\n            } catch {\n              case _: Throwable =>\n                throw new Exception(\"attempt to call non-function\")\n            }\n            return fn(el)\n          }\n          case _ => throw new Exception(\"invalid apply\")\n        }\n      }\n    }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    repl_env.set('+, (a: List[Any]) => a(0).asInstanceOf[Long] + a(1).asInstanceOf[Long])\n    repl_env.set('-, (a: List[Any]) => a(0).asInstanceOf[Long] - a(1).asInstanceOf[Long])\n    repl_env.set('*, (a: List[Any]) => a(0).asInstanceOf[Long] * a(1).asInstanceOf[Long])\n    repl_env.set('/, (a: List[Any]) => a(0).asInstanceOf[Long] / a(1).asInstanceOf[Long])\n    val REP = (str: String) => {\n      PRINT(EVAL(READ(str), repl_env))\n    }\n\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step4_if_fn_do.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject step4_if_fn_do {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def EVAL(ast: Any, env: Env): Any = {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        return EVAL(a2, let_env)\n      }\n      case Symbol(\"do\") :: rest => {\n        val el = rest.map(EVAL(_, env))\n        return el.last\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          return EVAL(rest(0), env)\n        } else {\n          return EVAL(a2, env)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new Func((args: List[Any]) => {\n          EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n        })\n      }\n      case _ => {\n        // function call\n        ast.asInstanceOf[MalList].map(EVAL(_, env)).value match {\n          case f :: el => {\n            var fn: Func = null\n            try {\n              fn = f.asInstanceOf[Func]\n            } catch {\n              case _: Throwable =>\n                throw new Exception(\"attempt to call non-function\")\n            }\n            return fn(el)\n          }\n          case _ => throw new Exception(\"invalid apply\")\n        }\n      }\n    }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step5_tco.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject step5_tco {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def EVAL(orig_ast: Any, orig_env: Env): Any = {\n   var ast = orig_ast; var env = orig_env;\n   while (true) {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        env = let_env\n        ast = a2   // continue loop (TCO)\n      }\n      case Symbol(\"do\") :: rest => {\n        rest.slice(0,rest.length-1).map(EVAL(_, env))\n        ast = ast.asInstanceOf[MalList].value.last  // continue loop (TCO)\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          ast = rest(0)  // continue loop (TCO)\n        } else {\n          ast = a2  // continue loop (TCO)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new MalFunction(a2, env, a1.asInstanceOf[MalList],\n          (args: List[Any]) => {\n            EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n          }\n        )\n      }\n      case _ => {\n        // function call\n        ast.asInstanceOf[MalList].map(EVAL(_, env)).value match {\n          case f :: el => {\n            f match {\n              case fn: MalFunction => {\n                env = fn.gen_env(el) \n                ast = fn.ast  // continue loop (TCO)\n              }\n              case fn: Func => {\n                return fn(el)\n              }\n              case _ => {\n                throw new Exception(\"attempt to call non-function: \" + f)\n              }\n            }\n          }\n          case _ => throw new Exception(\"invalid apply\")\n        }\n      }\n    }\n   }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step6_file.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject step6_file {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def EVAL(orig_ast: Any, orig_env: Env): Any = {\n   var ast = orig_ast; var env = orig_env;\n   while (true) {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        env = let_env\n        ast = a2   // continue loop (TCO)\n      }\n      case Symbol(\"do\") :: rest => {\n        rest.slice(0,rest.length-1).map(EVAL(_, env))\n        ast = ast.asInstanceOf[MalList].value.last  // continue loop (TCO)\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          ast = rest(0)  // continue loop (TCO)\n        } else {\n          ast = a2  // continue loop (TCO)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new MalFunction(a2, env, a1.asInstanceOf[MalList],\n          (args: List[Any]) => {\n            EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n          }\n        )\n      }\n      case _ => {\n        // function call\n        ast.asInstanceOf[MalList].map(EVAL(_, env)).value match {\n          case f :: el => {\n            f match {\n              case fn: MalFunction => {\n                env = fn.gen_env(el) \n                ast = fn.ast  // continue loop (TCO)\n              }\n              case fn: Func => {\n                return fn(el)\n              }\n              case _ => {\n                throw new Exception(\"attempt to call non-function: \" + f)\n              }\n            }\n          }\n          case _ => throw new Exception(\"invalid apply\")\n        }\n      }\n    }\n   }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n    repl_env.set(Symbol(\"eval\"), new Func((a: List[Any]) => EVAL(a(0), repl_env)))\n    repl_env.set(Symbol(\"*ARGV*\"), _list(args.slice(1,args.length):_*))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n    if (args.length > 0) {\n      REP(\"(load-file \\\"\" + args(0) + \"\\\")\")\n      System.exit(0)\n    }\n\n    // repl loop\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step7_quote.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject step7_quote {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def quasiquote_loop(elts: List[Any]): MalList = {\n    var acc = _list()\n    for (elt <- elts.reverse) {\n      if (types._list_Q(elt)) {\n        elt.asInstanceOf[MalList].value match {\n          case Symbol(\"splice-unquote\") :: x :: Nil => {\n            acc = _list(Symbol(\"concat\"), x, acc)\n          }\n          case _ => {\n            acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n          }\n        }\n      } else {\n        acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n      }\n    }\n    return acc\n  }\n\n  def quasiquote(ast: Any): Any = {\n    ast match {\n      //  Test vectors before they match MalList.\n      case v: MalVector => {\n        _list(Symbol(\"vec\"), quasiquote_loop(v.value))\n      }\n      case l: MalList => {\n        l.value match {\n          case Symbol(\"unquote\") :: x :: Nil => x\n          case _ => quasiquote_loop(l.value)\n        }\n      }\n      case _ : Symbol     => _list(Symbol(\"quote\"), ast)\n      case _ : MalHashMap => _list(Symbol(\"quote\"), ast)\n      case _ => ast\n    }\n  }\n\n  def EVAL(orig_ast: Any, orig_env: Env): Any = {\n   var ast = orig_ast; var env = orig_env;\n   while (true) {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        env = let_env\n        ast = a2   // continue loop (TCO)\n      }\n      case Symbol(\"quote\") :: a1 :: Nil => {\n        return a1\n      }\n      case Symbol(\"quasiquote\") :: a1 :: Nil => {\n        ast = quasiquote(a1)  // continue loop (TCO)\n      }\n      case Symbol(\"do\") :: rest => {\n        rest.slice(0,rest.length-1).map(EVAL(_, env))\n        ast = ast.asInstanceOf[MalList].value.last  // continue loop (TCO)\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          ast = rest(0)  // continue loop (TCO)\n        } else {\n          ast = a2  // continue loop (TCO)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new MalFunction(a2, env, a1.asInstanceOf[MalList],\n          (args: List[Any]) => {\n            EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n          }\n        )\n      }\n      case _ => {\n        // function call\n        ast.asInstanceOf[MalList].map(EVAL(_, env)).value match {\n          case f :: el => {\n            f match {\n              case fn: MalFunction => {\n                env = fn.gen_env(el) \n                ast = fn.ast  // continue loop (TCO)\n              }\n              case fn: Func => {\n                return fn(el)\n              }\n              case _ => {\n                throw new Exception(\"attempt to call non-function: \" + f)\n              }\n            }\n          }\n          case _ => throw new Exception(\"invalid apply\")\n        }\n      }\n    }\n   }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n    repl_env.set(Symbol(\"eval\"), new Func((a: List[Any]) => EVAL(a(0), repl_env)))\n    repl_env.set(Symbol(\"*ARGV*\"), _list(args.slice(1,args.length):_*))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n    if (args.length > 0) {\n      REP(\"(load-file \\\"\" + args(0) + \"\\\")\")\n      System.exit(0)\n    }\n\n    // repl loop\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step8_macros.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject step8_macros {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def quasiquote_loop(elts: List[Any]): MalList = {\n    var acc = _list()\n    for (elt <- elts.reverse) {\n      if (types._list_Q(elt)) {\n        elt.asInstanceOf[MalList].value match {\n          case Symbol(\"splice-unquote\") :: x :: Nil => {\n            acc = _list(Symbol(\"concat\"), x, acc)\n          }\n          case _ => {\n            acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n          }\n        }\n      } else {\n        acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n      }\n    }\n    return acc\n  }\n\n  def quasiquote(ast: Any): Any = {\n    ast match {\n      //  Test vectors before they match MalList.\n      case v: MalVector => {\n        _list(Symbol(\"vec\"), quasiquote_loop(v.value))\n      }\n      case l: MalList => {\n        l.value match {\n          case Symbol(\"unquote\") :: x :: Nil => x\n          case _ => quasiquote_loop(l.value)\n        }\n      }\n      case _ : Symbol     => _list(Symbol(\"quote\"), ast)\n      case _ : MalHashMap => _list(Symbol(\"quote\"), ast)\n      case _ => ast\n    }\n  }\n\n  def EVAL(orig_ast: Any, orig_env: Env): Any = {\n   var ast = orig_ast; var env = orig_env;\n   while (true) {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        env = let_env\n        ast = a2   // continue loop (TCO)\n      }\n      case Symbol(\"quote\") :: a1 :: Nil => {\n        return a1\n      }\n      case Symbol(\"quasiquote\") :: a1 :: Nil => {\n        ast = quasiquote(a1)  // continue loop (TCO)\n      }\n      case Symbol(\"defmacro!\") :: a1 :: a2 :: Nil => {\n        val f = EVAL(a2, env).asInstanceOf[MalFunction].clone()\n        f.ismacro = true\n        return env.set(a1.asInstanceOf[Symbol], f)\n      }\n      case Symbol(\"do\") :: rest => {\n        rest.slice(0,rest.length-1).map(EVAL(_, env))\n        ast = ast.asInstanceOf[MalList].value.last  // continue loop (TCO)\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          ast = rest(0)  // continue loop (TCO)\n        } else {\n          ast = a2  // continue loop (TCO)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new MalFunction(a2, env, a1.asInstanceOf[MalList],\n          (args: List[Any]) => {\n            EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n          }\n        )\n      }\n      case first :: rest => {\n        // function call\n          EVAL(first, env) match {\n              case fn: MalFunction => {\n               if (fn.ismacro) {\n                ast = fn(rest)  // continue loop (TCO)\n               } else {\n                val el = rest.map(EVAL(_, env))\n                env = fn.gen_env(el) \n                ast = fn.ast  // continue loop (TCO)\n               }\n              }\n              case fn: Func => {\n                val el = rest.map(EVAL(_, env))\n                return fn(el)\n              }\n              case f => {\n                throw new Exception(\"attempt to call non-function: \" + f)\n              }\n          }\n      }\n    }\n   }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n    repl_env.set(Symbol(\"eval\"), new Func((a: List[Any]) => EVAL(a(0), repl_env)))\n    repl_env.set(Symbol(\"*ARGV*\"), _list(args.slice(1,args.length):_*))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\n    if (args.length > 0) {\n      REP(\"(load-file \\\"\" + args(0) + \"\\\")\")\n      System.exit(0)\n    }\n\n    // repl loop\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/step9_try.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject step9_try {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def quasiquote_loop(elts: List[Any]): MalList = {\n    var acc = _list()\n    for (elt <- elts.reverse) {\n      if (types._list_Q(elt)) {\n        elt.asInstanceOf[MalList].value match {\n          case Symbol(\"splice-unquote\") :: x :: Nil => {\n            acc = _list(Symbol(\"concat\"), x, acc)\n          }\n          case _ => {\n            acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n          }\n        }\n      } else {\n        acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n      }\n    }\n    return acc\n  }\n\n  def quasiquote(ast: Any): Any = {\n    ast match {\n      //  Test vectors before they match MalList.\n      case v: MalVector => {\n        _list(Symbol(\"vec\"), quasiquote_loop(v.value))\n      }\n      case l: MalList => {\n        l.value match {\n          case Symbol(\"unquote\") :: x :: Nil => x\n          case _ => quasiquote_loop(l.value)\n        }\n      }\n      case _ : Symbol     => _list(Symbol(\"quote\"), ast)\n      case _ : MalHashMap => _list(Symbol(\"quote\"), ast)\n      case _ => ast\n    }\n  }\n\n  def EVAL(orig_ast: Any, orig_env: Env): Any = {\n   var ast = orig_ast; var env = orig_env;\n   while (true) {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        env = let_env\n        ast = a2   // continue loop (TCO)\n      }\n      case Symbol(\"quote\") :: a1 :: Nil => {\n        return a1\n      }\n      case Symbol(\"quasiquote\") :: a1 :: Nil => {\n        ast = quasiquote(a1)  // continue loop (TCO)\n      }\n      case Symbol(\"defmacro!\") :: a1 :: a2 :: Nil => {\n        val f = EVAL(a2, env).asInstanceOf[MalFunction].clone()\n        f.ismacro = true\n        return env.set(a1.asInstanceOf[Symbol], f)\n      }\n      case Symbol(\"try*\") :: a1 :: rest => {\n        try {\n          return EVAL(a1, env)\n        } catch {\n          case t: Throwable => {\n            if (rest.length == 0) throw t\n            rest(0).asInstanceOf[MalList].value match {\n              case List(Symbol(\"catch*\"), a21, a22) => {\n                val exc: Any = t match {\n                  case mex: types.MalException => mex.value\n                  case _ => t.getMessage\n                }\n                return EVAL(a22, new Env(env,\n                                         List(a21).iterator,\n                                         List(exc).iterator))\n              }\n            }\n            throw t\n          }\n        }\n      }\n      case Symbol(\"do\") :: rest => {\n        rest.slice(0,rest.length-1).map(EVAL(_, env))\n        ast = ast.asInstanceOf[MalList].value.last  // continue loop (TCO)\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          ast = rest(0)  // continue loop (TCO)\n        } else {\n          ast = a2  // continue loop (TCO)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new MalFunction(a2, env, a1.asInstanceOf[MalList],\n          (args: List[Any]) => {\n            EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n          }\n        )\n      }\n      case first :: rest => {\n        // function call\n          EVAL(first, env) match {\n              case fn: MalFunction => {\n               if (fn.ismacro) {\n                ast = fn(rest)  // continue loop (TCO)\n               } else {\n                val el = rest.map(EVAL(_, env))\n                env = fn.gen_env(el) \n                ast = fn.ast  // continue loop (TCO)\n               }\n              }\n              case fn: Func => {\n                val el = rest.map(EVAL(_, env))\n                return fn(el)\n              }\n              case f => {\n                throw new Exception(\"attempt to call non-function: \" + f)\n              }\n          }\n      }\n    }\n   }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n    repl_env.set(Symbol(\"eval\"), new Func((a: List[Any]) => EVAL(a(0), repl_env)))\n    repl_env.set(Symbol(\"*ARGV*\"), _list(args.slice(1,args.length):_*))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\n    if (args.length > 0) {\n      REP(\"(load-file \\\"\" + args(0) + \"\\\")\")\n      System.exit(0)\n    }\n\n    // repl loop\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/stepA_mal.scala",
    "content": "import types.{MalList, _list, _list_Q, MalVector, MalHashMap,\n              Func, MalFunction}\nimport env.Env\n\nobject stepA_mal {\n  // read\n  def READ(str: String): Any = {\n    reader.read_str(str)\n  }\n\n  // eval\n  def quasiquote_loop(elts: List[Any]): MalList = {\n    var acc = _list()\n    for (elt <- elts.reverse) {\n      if (types._list_Q(elt)) {\n        elt.asInstanceOf[MalList].value match {\n          case Symbol(\"splice-unquote\") :: x :: Nil => {\n            acc = _list(Symbol(\"concat\"), x, acc)\n          }\n          case _ => {\n            acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n          }\n        }\n      } else {\n        acc = _list(Symbol(\"cons\"), quasiquote(elt), acc)\n      }\n    }\n    return acc\n  }\n\n  def quasiquote(ast: Any): Any = {\n    ast match {\n      //  Test vectors before they match MalList.\n      case v: MalVector => {\n        _list(Symbol(\"vec\"), quasiquote_loop(v.value))\n      }\n      case l: MalList => {\n        l.value match {\n          case Symbol(\"unquote\") :: x :: Nil => x\n          case _ => quasiquote_loop(l.value)\n        }\n      }\n      case _ : Symbol     => _list(Symbol(\"quote\"), ast)\n      case _ : MalHashMap => _list(Symbol(\"quote\"), ast)\n      case _ => ast\n    }\n  }\n\n  def EVAL(orig_ast: Any, orig_env: Env): Any = {\n   var ast = orig_ast; var env = orig_env;\n   while (true) {\n\n    if (env.find(Symbol(\"DEBUG-EVAL\")) != null) {\n      val dbgeval = env.get(Symbol(\"DEBUG-EVAL\"))\n      if (dbgeval != null && dbgeval != false) {\n        println(\"EVAL: \" + printer._pr_str(ast,true))\n      }\n    }\n\n    ast match {\n      case s : Symbol    => return env.get(s)\n      case v: MalVector  => return v.map(EVAL(_, env))\n      case l: MalList    => {}\n      case m: MalHashMap => {\n        return m.map{case (k,v) => (k, EVAL(v, env))}\n      }\n      case _             => return ast\n    }\n\n    // apply list\n\n    ast.asInstanceOf[MalList].value match {\n      case Nil => {\n        return ast\n      }\n      case Symbol(\"def!\") :: a1 :: a2 :: Nil => {\n        return env.set(a1.asInstanceOf[Symbol], EVAL(a2, env))\n      }\n      case Symbol(\"let*\") :: a1 :: a2 :: Nil => {\n        val let_env = new Env(env)\n        for (g <- a1.asInstanceOf[MalList].value.grouped(2)) {\n          let_env.set(g(0).asInstanceOf[Symbol],EVAL(g(1),let_env))\n        }\n        env = let_env\n        ast = a2   // continue loop (TCO)\n      }\n      case Symbol(\"quote\") :: a1 :: Nil => {\n        return a1\n      }\n      case Symbol(\"quasiquote\") :: a1 :: Nil => {\n        ast = quasiquote(a1)  // continue loop (TCO)\n      }\n      case Symbol(\"defmacro!\") :: a1 :: a2 :: Nil => {\n        val f = EVAL(a2, env).asInstanceOf[MalFunction].clone()\n        f.ismacro = true\n        return env.set(a1.asInstanceOf[Symbol], f)\n      }\n      case Symbol(\"try*\") :: a1 :: rest => {\n        try {\n          return EVAL(a1, env)\n        } catch {\n          case t: Throwable => {\n            if (rest.length == 0) throw t\n            rest(0).asInstanceOf[MalList].value match {\n              case List(Symbol(\"catch*\"), a21, a22) => {\n                val exc: Any = t match {\n                  case mex: types.MalException => mex.value\n                  case _ => t.getMessage\n                }\n                return EVAL(a22, new Env(env,\n                                         List(a21).iterator,\n                                         List(exc).iterator))\n              }\n            }\n            throw t\n          }\n        }\n      }\n      case Symbol(\"do\") :: rest => {\n        rest.slice(0,rest.length-1).map(EVAL(_, env))\n        ast = ast.asInstanceOf[MalList].value.last  // continue loop (TCO)\n      }\n      case Symbol(\"if\") :: a1 :: a2 :: rest => {\n        val cond = EVAL(a1, env)\n        if (cond == null || cond == false) {\n          if (rest.length == 0) return null\n          ast = rest(0)  // continue loop (TCO)\n        } else {\n          ast = a2  // continue loop (TCO)\n        }\n      }\n      case Symbol(\"fn*\") :: a1 :: a2 :: Nil => {\n        return new MalFunction(a2, env, a1.asInstanceOf[MalList],\n          (args: List[Any]) => {\n            EVAL(a2, new Env(env, types._toIter(a1), args.iterator))\n          }\n        )\n      }\n      case first :: rest => {\n        // function call\n          EVAL(first, env) match {\n              case fn: MalFunction => {\n               if (fn.ismacro) {\n                ast = fn(rest)  // continue loop (TCO)\n               } else {\n                val el = rest.map(EVAL(_, env))\n                env = fn.gen_env(el) \n                ast = fn.ast  // continue loop (TCO)\n               }\n              }\n              case fn: Func => {\n                val el = rest.map(EVAL(_, env))\n                return fn(el)\n              }\n              case f => {\n                throw new Exception(\"attempt to call non-function: \" + f)\n              }\n          }\n      }\n    }\n   }\n  }\n\n  // print\n  def PRINT(exp: Any): String = {\n    printer._pr_str(exp, true)\n  }\n\n  // repl\n  def main(args: Array[String]) = {\n    val repl_env: Env = new Env()\n    val REP = (str: String) => PRINT(EVAL(READ(str), repl_env))\n\n    // core.scala: defined using scala\n    core.ns.map{case (k: String,v: Any) => {\n      repl_env.set(Symbol(k), new Func(v))\n    }}\n    repl_env.set(Symbol(\"eval\"), new Func((a: List[Any]) => EVAL(a(0), repl_env)))\n    repl_env.set(Symbol(\"*ARGV*\"), _list(args.slice(1,args.length):_*))\n\n    // core.mal: defined using the language itself\n    REP(\"(def! *host-language* \\\"scala\\\")\")\n    REP(\"(def! not (fn* (a) (if a false true)))\")\n    REP(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\n    if (args.length > 0) {\n      REP(\"(load-file \\\"\" + args(0) + \"\\\")\")\n      System.exit(0)\n    }\n\n    // repl loop\n    REP(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n    var line:String = null\n    while ({line = readLine(\"user> \"); line != null}) {\n      try {\n        println(REP(line))\n      } catch {\n        case e : Throwable => {\n          println(\"Error: \" + e.getMessage)\n          println(\"    \" + e.getStackTrace.mkString(\"\\n    \"))\n        }\n      }\n    }\n  }\n}\n\n// vim: ts=2:sw=2\n"
  },
  {
    "path": "impls/scala/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/scala/types.scala",
    "content": "import scala.collection._\nimport scala.collection.generic._\n\nimport env.Env\nimport printer._pr_str\n\nobject types {\n  class MalException(msg: String) extends Throwable(msg) {\n    var value: Any = null\n    def init(obj: Any) = { value = obj; this }\n  }\n\n  def _toIter(obj: Any): Iterator[Any] = {\n    obj match {\n      case v: MalVector => v.value.iterator\n      case l: MalList => l.value.iterator\n      case null => Iterator.empty\n      case _ => throw new Exception(\"cannot convert \" +\n                                    obj.getClass + \" to iterator\")\n    }\n  }\n\n  def _equal_Q(a: Any, b: Any): Any = {\n    (a, b) match {\n      case (a: MalList, b: MalList)   => {\n        if (a.value.length != b.value.length) return false\n        for ( (x, y) <- (a.value zip b.value) ) {\n          if (_equal_Q(x, y) != true) return false\n        }\n        true\n      }\n      case (a: MalHashMap, b: MalHashMap) => {\n        if (a.value.size != b.value.size) return false\n        for ( (k,v) <- a.value ) {\n          if (_equal_Q(v,b.value(k)) != true) return false\n        }\n        true\n      }\n      case _ => a == b\n    }\n  }\n\n  def _sequential_Q(a: Any): Boolean = {\n    a match {\n      case l: MalList => true\n      case _ => false\n    }\n  }\n\n  def _symbol_Q(a: Any) = { a.isInstanceOf[Symbol] }\n\n\n  // Lists\n  class MalList(seq: Any*) {\n    var value: List[Any] = seq.toList\n    var meta: Any = null\n\n    override def clone(): MalList = {\n      val new_ml = new MalList()\n      new_ml.value = value\n      new_ml.meta = meta\n      new_ml\n    }\n\n    def apply(idx: Int): Any = value(idx)\n    def map(f: Any => Any) = new MalList(value.map(f):_*)\n    def drop(cnt: Int) = new MalList(value.drop(cnt):_*)\n    def :+(that: Any) = new MalList((value :+ that):_*)\n    def +:(that: Any) = new MalList((that +: value):_*)\n\n    override def toString() = {\n      \"(\" + value.map(_pr_str(_, true)).mkString(\" \") + \")\"\n    }\n    def toString(print_readably: Boolean) = {\n      \"(\" + value.map(_pr_str(_, print_readably)).mkString(\" \") + \")\"\n    }\n  }\n  def _list(seq: Any*) = {\n    new MalList(seq:_*)\n  }\n  def _list_Q(obj: Any) = {\n    obj.isInstanceOf[MalList] && !obj.isInstanceOf[MalVector]\n  }\n\n\n  // Vectors\n  class MalVector(seq: Any*) extends MalList(seq:_*) {\n    override def clone() = {\n      val new_mv = new MalVector()\n      new_mv.value = value\n      new_mv.meta = meta\n      new_mv\n    }\n\n    override def map(f: Any => Any) = new MalVector(value.map(f):_*)\n    override def drop(cnt: Int) = new MalVector(value.drop(cnt):_*)\n\n    override def toString() = {\n      \"[\" + value.map(_pr_str(_, true)).mkString(\" \") + \"]\"\n    }\n    override def toString(print_readably: Boolean) = {\n      \"[\" + value.map(_pr_str(_, print_readably)).mkString(\" \") + \"]\"\n    }\n  }\n  def _vector(seq: Any*) = {\n    new MalVector(seq:_*)\n  }\n  def _vector_Q(obj: Any) = {\n    obj.isInstanceOf[MalVector]\n  }\n\n\n  // Hash Maps\n  class MalHashMap(seq: Any*) {\n    var value: Map[String,Any] = seq.toList.grouped(2).map(\n      (kv: List[Any]) => (kv(0).asInstanceOf[String], kv(1))).toMap\n    var meta: Any = null\n\n    override def clone(): MalHashMap = {\n      val new_hm = new MalHashMap()\n      new_hm.value = value\n      new_hm.meta = meta\n      new_hm\n    }\n\n    def keys(): MalList = new MalList(value.keys.toSeq:_*)\n    def vals(): MalList = new MalList(value.values.toSeq:_*)\n\n    def apply(key: String): Any = value(key)\n    def map(f: ((String, Any)) => (String, Any)) = {\n      val res = value.map(f).map{case (k,v) => List(k,v)}\n      new MalHashMap(res.flatten.toSeq:_*)\n    }\n    def filterKeys(f: String => Boolean) = {\n      val res = value.filterKeys(f).map{case (k,v) => List(k,v)}\n      new MalHashMap(res.flatten.toSeq:_*)\n    }\n    def ++(that: MalHashMap) = {\n      val new_hm = clone() \n      new_hm.value ++= that.value\n      new_hm\n    }\n\n    override def toString() = {\n      var res = mutable.MutableList[Any]()\n      for ((k,v) <- value) {\n        res += _pr_str(k, true)\n        res += _pr_str(v, true)\n      }\n      \"{\" + res.mkString(\" \") + \"}\"\n    }\n    def toString(print_readably: Boolean) = {\n      var res = mutable.MutableList[Any]()\n      for ((k,v) <- value) {\n        res += _pr_str(k, print_readably)\n        res += _pr_str(v, print_readably)\n      }\n      \"{\" + res.mkString(\" \") + \"}\"\n    }\n  }\n  def _hash_map(seq: Any*) = {\n    new MalHashMap(seq:_*)\n  }\n  def _hash_map_Q(obj: Any) = {\n    obj.isInstanceOf[MalHashMap]\n  }\n\n\n  // Function types\n\n  class Func(_fn: ((List[Any]) => Any)) {\n    val fn = _fn\n    var meta: Any = null\n\n    override def clone(): Func = {\n      val new_fn = new Func(fn)\n      new_fn.meta = meta\n      new_fn\n    }\n\n    def apply(args: List[Any]): Any = fn(args)\n  }\n\n  class MalFunction(_ast: Any, _env: Env, _params: MalList,\n                    fn: ((List[Any]) => Any)) {\n    val ast = _ast\n    val env = _env\n    val params = _params\n    var ismacro = false\n    var meta: Any = null\n\n    override def clone(): MalFunction = {\n      val new_fn = new MalFunction(ast, env, params, fn)\n      new_fn.ismacro = ismacro\n      new_fn.meta = meta\n      new_fn\n    }\n\n    def apply(args: List[Any]): Any = fn(args)\n\n    def gen_env(args: List[Any]): Env = {\n      return new Env(env, params.value.iterator, args.iterator)\n    }\n  }\n\n  def _apply(f: Any, args: List[Any]): Any = {\n    f match {\n      case fn: types.MalFunction => fn(args)\n      case fn: Func              => fn(args)\n      case _ => throw new Exception(\"attempt to call non-function\")\n    }\n  }\n\n  def _hash_map(lst: List[Any]): Any = {\n    lst.grouped(2).map(\n      (kv: List[Any]) => (kv(0).asInstanceOf[String], kv(1))).toMap\n  }\n\n  class Atom(_value: Any) {\n    var value = _value\n  }\n}\n\n// vim:ts=2:sw=2\n"
  },
  {
    "path": "impls/scheme/.gitignore",
    "content": "lib/*.scm\nlib/*.so\nlib/*.c\nlib/*.o\nlib/*.meta\nlib.*.scm\n*.so\n*.c\n*.o\nout/\neggs/*"
  },
  {
    "path": "impls/scheme/Dockerfile",
    "content": "FROM ubuntu:focal\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Dev tools\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install gcc g++ bison flex groff make cmake pkg-config git\n\n# Prepackaged Scheme implementations\nRUN apt-get -y install gauche chicken-bin\nRUN chicken-install r7rs\n\n# Chibi\nRUN cd /tmp && curl -Lo chibi-0.10.tar.gz https://github.com/ashinn/chibi-scheme/archive/0.10.tar.gz \\\n    && tar xvzf chibi-0.10.tar.gz && cd chibi-scheme-0.10 \\\n    && make && make install && rm -rf /tmp/chibi-*\n\n# Kawa\nRUN apt-get -y install openjdk-8-jdk-headless\nRUN cd /tmp && curl -O http://ftp.gnu.org/pub/gnu/kawa/kawa-3.1.1.tar.gz \\\n    && tar xvzf kawa-3.1.1.tar.gz && cd kawa-3.1.1 \\\n    && ./configure && make && make install && rm -rf /tmp/kawa-3.1.1*\n\n# Sagittarius\nRUN apt-get -y install libgc-dev zlib1g-dev libffi-dev libssl-dev\nRUN cd /tmp && curl -LO https://bitbucket.org/ktakashi/sagittarius-scheme/downloads/sagittarius-0.9.7.tar.gz \\\n    && tar xvzf sagittarius-0.9.7.tar.gz && cd sagittarius-0.9.7 \\\n    && cmake . && make && make install && rm -rf /tmp/sagittarius-0.9.7*\n\n# Cyclone\nRUN apt-get -y install libck-dev libtommath-dev\nRUN cd /tmp && git clone https://github.com/justinethier/cyclone-bootstrap \\\n    && cd cyclone-bootstrap \\\n    && make && make install && rm -rf /tmp/cyclone-bootstrap\n\n# Foment\nRUN cd /tmp && git clone https://github.com/leftmike/foment \\\n    && cd foment/unix && make && cp release/foment /usr/bin/foment \\\n    && rm -rf /tmp/foment\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/scheme/Makefile",
    "content": "BINS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do step5_tco\nBINS += step6_file step7_quote step8_macros step9_try stepA_mal\nscheme_MODE ?= chibi\n\nCLASSSTEPS = out/step0_repl.class out/step1_read_print.class \\\n             out/step3_env.class out/step4_if_fn_do.class out/step5_tco.class \\\n             out/step6_file.class out/step7_quote.class out/step8_macros.class \\\n             out/step9_try.class out/stepA_mal.class\nSTEPS = $(if $(filter kawa,$(scheme_MODE)),$(CLASSSTEPS),\\\n        $(if $(filter chicken,$(scheme_MODE)),$(BINS),\\\n        $(if $(filter cyclone,$(scheme_MODE)),$(BINS))))\n\nKAWA_STEP1_DEPS = out/lib/util.class out/lib/reader.class \\\n                  out/lib/printer.class out/lib/types.class\nKAWA_STEP3_DEPS = $(KAWA_STEP1_DEPS) out/lib/env.class\nKAWA_STEP4_DEPS = $(KAWA_STEP3_DEPS) out/lib/core.class\nCHICKEN_STEP1_DEPS = lib.util.so lib.types.so lib.reader.so lib.printer.so\nCHICKEN_STEP3_DEPS = $(CHICKEN_STEP1_DEPS) lib.env.so\nCHICKEN_STEP4_DEPS = $(CHICKEN_STEP3_DEPS) lib.core.so\nCYCLONE_STEP1_DEPS = lib/util.so lib/reader.so lib/printer.so lib/types.so\nCYCLONE_STEP3_DEPS = $(CYCLONE_STEP1_DEPS) lib/env.so\nCYCLONE_STEP4_DEPS = $(CYCLONE_STEP3_DEPS) lib/core.so\n\nSTEP1_DEPS = $(if $(filter kawa,$(scheme_MODE)),$(KAWA_STEP1_DEPS),\\\n             $(if $(filter chicken,$(scheme_MODE)),$(CHICKEN_STEP1_DEPS),\\\n             $(if $(filter cyclone,$(scheme_MODE)),$(CYCLONE_STEP1_DEPS))))\nSTEP3_DEPS = $(if $(filter kawa,$(scheme_MODE)),$(KAWA_STEP3_DEPS),\\\n             $(if $(filter chicken,$(scheme_MODE)),$(CHICKEN_STEP3_DEPS),\\\n             $(if $(filter cyclone,$(scheme_MODE)),$(CYCLONE_STEP3_DEPS))))\nSTEP4_DEPS = $(if $(filter kawa,$(scheme_MODE)),$(KAWA_STEP4_DEPS),\\\n             $(if $(filter chicken,$(scheme_MODE)),$(CHICKEN_STEP4_DEPS),\\\n             $(if $(filter cyclone,$(scheme_MODE)),$(CYCLONE_STEP4_DEPS))))\n\nKAWALIB = kawa --r7rs --no-warn-unused -d out -C\nKAWA = kawa --r7rs --no-warn-unused -d out --main -C\nCHICKEN = csc -setup-mode -host -O3 -R r7rs\nCHICKENLIB = $(CHICKEN) -D compiling-extension -J -s -regenerate-import-libraries\nCYCLONELIB = cyclone -O2\nCYCLONE = $(CYCLONELIB)\n\nSCMLIB = $(if $(filter kawa,$(scheme_MODE)),$(KAWALIB),\\\n         $(if $(filter chicken,$(scheme_MODE)),$(CHICKENLIB),\\\n         $(if $(filter cyclone,$(scheme_MODE)),$(CYCLONELIB))))\nSCM = $(if $(filter kawa,$(scheme_MODE)),$(KAWA),\\\n      $(if $(filter chicken,$(scheme_MODE)),$(CHICKEN),\\\n      $(if $(filter cyclone,$(scheme_MODE)),$(CYCLONE))))\n\nMKDIR = mkdir -p\nSYMLINK = ln -sfr\nRM = rm -f\nRMR = rm -rf\n\nall: $(STEPS)\n\n.PHONY: clean\n.PRECIOUS: lib/%.scm\n\nlib/%.scm: lib/%.sld\n\t$(SYMLINK) $< $@\n\nout/lib/%.class: lib/%.scm\n\t$(SCMLIB) $<\n\nout/%.class: %.scm\n\t$(SCM) $<\n\nlib.%.so: lib/%.sld\n\t$(SCMLIB) $< -o $@\n\nlib/%.so: lib/%.sld\n\t$(SCMLIB) $<\n\n%: %.scm\n\t$(SCM) $<\n\nout/step1_read_print.class out/step2_eval.class: $(STEP1_DEPS)\nout/step3_env.class: $(STEP3_DEPS)\nout/step4_if_fn_do.class out/step5_tco.class out/step6_file.class out/step7_quote.class out/step8_macros.class out/step9_try.class out/stepA_mal.class: $(STEP4_DEPS)\n\nstep1_read_print.scm step2_eval.scm: $(STEP1_DEPS)\nstep3_env.scm: $(STEP3_DEPS)\nstep4_if_fn_do.scm step5_tco.scm step6_file.scm step7_quote.scm step8_macros.scm step9_try.scm stepA_mal.scm: $(STEP4_DEPS)\n\nclean:\n\t$(RM) lib/*.scm lib/*.so lib/*.c lib/*.o lib/*.meta\n\t$(RM) lib.*.scm *.build.sh *.install.sh *.link *.so *.c *.o $(BINS)\n\t$(RMR) out\n"
  },
  {
    "path": "impls/scheme/lib/core.sld",
    "content": "(define-library (lib core)\n\n(export ns)\n\n(import (scheme base))\n(import (scheme write))\n(import (scheme file))\n(import (scheme time))\n(import (scheme read))\n(import (scheme eval))\n;; HACK: cyclone doesn't implement environments yet, but its eval\n;; behaves as if you were using the repl environment\n(cond-expand\n (cyclone)\n (else\n  (import (scheme repl))))\n\n(import (lib types))\n(import (lib util))\n(import (lib printer))\n(import (lib reader))\n\n(begin\n\n(define (coerce x)\n  (if x mal-true mal-false))\n\n(define (->printed-string args print-readably sep)\n  (let ((items (map (lambda (arg) (pr-str arg print-readably)) args)))\n    (string-intersperse items sep)))\n\n(define (mal-equal? a b)\n  (let ((a-type (and (mal-object? a) (mal-type a)))\n        (a-value (and (mal-object? a) (mal-value a)))\n        (b-type (and (mal-object? b) (mal-type b)))\n        (b-value (and (mal-object? b) (mal-value b))))\n    (cond\n     ((or (not a-type) (not b-type))\n      mal-false)\n     ((and (memq a-type '(list vector))\n           (memq b-type '(list vector)))\n      (mal-list-equal? (->list a-value) (->list b-value)))\n     ((and (eq? a-type 'map) (eq? b-type 'map))\n      (mal-map-equal? a-value b-value))\n     (else\n      (and (eq? a-type b-type)\n           (equal? a-value b-value))))))\n\n(define (mal-list-equal? as bs)\n  (let loop ((as as)\n             (bs bs))\n    (cond\n     ((and (null? as) (null? bs)) #t)\n     ((or (null? as) (null? bs)) #f)\n     (else\n      (if (mal-equal? (car as) (car bs))\n          (loop (cdr as) (cdr bs))\n          #f)))))\n\n(define (mal-map-ref key m . default)\n  (if (pair? default)\n      (alist-ref key m mal-equal? (car default))\n      (alist-ref key m mal-equal?)))\n\n(define (mal-map-equal? as bs)\n  (if (not (= (length as) (length bs)))\n      #f\n      (let loop ((as as))\n        (if (pair? as)\n            (let* ((item (car as))\n                   (key (car item))\n                   (value (cdr item)))\n              (if (mal-equal? (mal-map-ref key bs) value)\n                  (loop (cdr as))\n                  #f))\n            #t))))\n\n(define (mal-map-dissoc m keys)\n  (let loop ((items m)\n             (acc '()))\n    (if (pair? items)\n        (let* ((item (car items))\n               (key (car item)))\n          (if (contains? keys (lambda (x) (mal-equal? key x)))\n              (loop (cdr items) acc)\n              (loop (cdr items) (cons item acc))))\n        (reverse acc))))\n\n(define (mal-map-assoc m kvs)\n  (let ((kvs (list->alist kvs)))\n    (append kvs (mal-map-dissoc m (map car kvs)))))\n\n(define (map-in-order proc items)\n  (let loop ((items items)\n             (acc '()))\n    (if (null? items)\n        (reverse acc)\n        (loop (cdr items) (cons (proc (car items)) acc)))))\n\n(define (slurp path)\n  (call-with-output-string\n   (lambda (out)\n     (call-with-input-file path\n       (lambda (in)\n         (let loop ()\n           (let ((chunk (read-string 1024 in)))\n             (when (not (eof-object? chunk))\n               (display chunk out)\n               (loop)))))))))\n\n(define (time-ms)\n  (* (/ (current-jiffy) (jiffies-per-second)) 1000.0))\n\n(define (->mal-object x)\n  (cond\n   ((boolean? x) (if x mal-true mal-false))\n   ((char? x) (mal-string (char->string x)))\n   ((procedure? x) x)\n   ((symbol? x) (mal-symbol x))\n   ((number? x) (mal-number x))\n   ((string? x) (mal-string x))\n   ((or (null? x) (pair? x))\n    (mal-list (map ->mal-object x)))\n   ((vector? x)\n    (mal-vector (vector-map ->mal-object x)))\n   (else\n    (error \"unknown type\"))))\n\n(define (scm-eval input)\n  (call-with-input-string input\n    (lambda (port)\n      (cond-expand\n       (cyclone\n        (->mal-object (eval (read port))))\n       (else\n        (->mal-object (eval (read port) (environment '(scheme base)\n                                                     '(scheme write)))))))))\n\n(define ns\n  `((+ . ,(lambda (a b) (mal-number (+ (mal-value a) (mal-value b)))))\n    (- . ,(lambda (a b) (mal-number (- (mal-value a) (mal-value b)))))\n    (* . ,(lambda (a b) (mal-number (* (mal-value a) (mal-value b)))))\n    (/ . ,(lambda (a b) (mal-number (/ (mal-value a) (mal-value b)))))\n\n    (list . ,(lambda args (mal-list args)))\n    (list? . ,(lambda (x) (coerce (mal-instance-of? x 'list))))\n    (empty? . ,(lambda (lis) (coerce (null? (->list (mal-value lis))))))\n    (count . ,(lambda (lis) (mal-number\n                             (if (eq? lis mal-nil)\n                                 0\n                                 (length (->list (mal-value lis)))))))\n\n    (< . ,(lambda (a b) (coerce (< (mal-value a) (mal-value b)))))\n    (<= . ,(lambda (a b) (coerce (<= (mal-value a) (mal-value b)))))\n    (> . ,(lambda (a b) (coerce (> (mal-value a) (mal-value b)))))\n    (>= . ,(lambda (a b) (coerce (>= (mal-value a) (mal-value b)))))\n    (= . ,(lambda (a b) (coerce (mal-equal? a b))))\n\n    (pr-str . ,(lambda args (mal-string (->printed-string args #t \" \"))))\n    (str . ,(lambda args (mal-string (->printed-string args #f \"\"))))\n    (prn . ,(lambda args\n              (display (->printed-string args #t \" \"))\n              (newline)\n              mal-nil))\n    (println . ,(lambda args\n                  (display (->printed-string args #f \" \"))\n                  (newline)\n                  mal-nil))\n\n    (read-string . ,(lambda (string) (read-str (mal-value string))))\n    (slurp . ,(lambda (path) (mal-string (slurp (mal-value path)))))\n    (throw . ,(lambda (x) (raise (cons 'user-error x))))\n    (readline . ,(lambda (prompt) (let ((output (readline (mal-value prompt))))\n                                    (if output (mal-string output) mal-nil))))\n    (time-ms . ,(lambda () (mal-number (time-ms))))\n    (scm-eval . ,(lambda (input) (scm-eval (mal-value input))))\n\n    (atom . ,(lambda (x) (mal-atom x)))\n    (atom? . ,(lambda (x) (coerce (mal-instance-of? x 'atom))))\n    (deref . ,(lambda (atom) (mal-value atom)))\n    (reset! . ,(lambda (atom x) (mal-value-set! atom x) x))\n    (swap! . ,(lambda (atom fn . args)\n                (let* ((fn (if (func? fn) (func-fn fn) fn))\n                       (value (apply fn (cons (mal-value atom) args))))\n                  (mal-value-set! atom value)\n                  value)))\n\n    (cons . ,(lambda (x xs) (mal-list (cons x (->list (mal-value xs))))))\n    (concat . ,(lambda args (mal-list (apply append (map (lambda (arg) (->list (mal-value arg))) args)))))\n    (vec . ,(lambda (x)\n              (case (mal-type x)\n                ((vector) x)\n                ((list)   (mal-vector (list->vector (mal-value x))))\n                (else     (error \"seq expects a sequence\")))))\n    (nth . ,(lambda (x n) (let ((items (->list (mal-value x)))\n                                (index (mal-value n)))\n                            (if (< index (length items))\n                                (list-ref items index)\n                                (error (str \"Out of range: \" index))))))\n    (first . ,(lambda (x) (if (eq? x mal-nil)\n                              mal-nil\n                              (let ((items (->list (mal-value x))))\n                                (if (null? items)\n                                    mal-nil\n                                    (car items))))))\n    (rest . ,(lambda (x) (if (eq? x mal-nil)\n                             (mal-list '())\n                             (let ((items (->list (mal-value x))))\n                               (if (null? items)\n                                   (mal-list '())\n                                   (mal-list (cdr items)))))))\n    (conj . ,(lambda (coll . args)\n               (let ((items (mal-value coll)))\n                 (cond\n                  ((vector? items)\n                   (mal-vector (vector-append items (list->vector args))))\n                  ((list? items)\n                   (mal-list (append (reverse args) items)))\n                  (else\n                   (error \"invalid collection type\"))))))\n    (seq . ,(lambda (x) (if (eq? x mal-nil)\n                            mal-nil\n                            (let ((value (mal-value x)))\n                              (case (mal-type x)\n                                ((list)\n                                 (if (null? value)\n                                     mal-nil\n                                     x))\n                                ((vector)\n                                 (if (zero? (vector-length value))\n                                     mal-nil\n                                     (mal-list (vector->list value))))\n                                ((string)\n                                 (if (zero? (string-length value))\n                                     mal-nil\n                                     (mal-list (map mal-string (explode value)))))\n                                (else\n                                 (error \"invalid collection type\")))))))\n\n    (apply . ,(lambda (f . args) (apply (if (func? f) (func-fn f) f)\n                                        (if (pair? (cdr args))\n                                            (append (butlast args)\n                                                    (->list (mal-value (last args))))\n                                            (->list (mal-value (car args)))))))\n    (map . ,(lambda (f items) (mal-list (map-in-order\n                                         (if (func? f) (func-fn f) f)\n                                         (->list (mal-value items))))))\n\n    (nil? . ,(lambda (x) (coerce (eq? x mal-nil))))\n    (true? . ,(lambda (x) (coerce (eq? x mal-true))))\n    (false? . ,(lambda (x) (coerce (eq? x mal-false))))\n    (number? . ,(lambda (x) (coerce (mal-instance-of? x 'number))))\n    (string? . ,(lambda (x) (coerce (mal-instance-of? x 'string))))\n    (symbol? . ,(lambda (x) (coerce (mal-instance-of? x 'symbol))))\n    (symbol . ,(lambda (x) (mal-symbol (string->symbol (mal-value x)))))\n    (keyword? . ,(lambda (x) (coerce (mal-instance-of? x 'keyword))))\n    (keyword . ,(lambda (x) (if (mal-instance-of? x 'keyword)\n                                x\n                                (mal-keyword (string->symbol (mal-value x))))))\n    (vector? . ,(lambda (x) (coerce (mal-instance-of? x 'vector))))\n    (vector . ,(lambda args (mal-vector (list->vector args))))\n    (map? . ,(lambda (x) (coerce (mal-instance-of? x 'map))))\n    (hash-map . ,(lambda args (mal-map (list->alist args))))\n    (sequential? . ,(lambda (x) (coerce (and (mal-object? x)\n                                             (memq (mal-type x)\n                                                   '(list vector))))))\n    (fn? . ,(lambda (x) (coerce (or (procedure? x)\n                                    (and (func? x) (not (func-macro? x)))))))\n    (macro? . ,(lambda (x) (coerce (and (func? x) (func-macro? x)))))\n\n    (assoc . ,(lambda (m . kvs) (mal-map (mal-map-assoc (mal-value m) kvs))))\n    (dissoc . ,(lambda (m . keys) (mal-map (mal-map-dissoc (mal-value m) keys))))\n    (get . ,(lambda (m key) (mal-map-ref key (mal-value m) mal-nil)))\n    (contains? . ,(lambda (m key) (coerce (mal-map-ref key (mal-value m)))))\n    (keys . ,(lambda (m) (mal-list (map car (mal-value m)))))\n    (vals . ,(lambda (m) (mal-list (map cdr (mal-value m)))))\n\n    (with-meta . ,(lambda (x meta)\n                    (cond\n                     ((mal-object? x)\n                      (make-mal-object (mal-type x) (mal-value x) meta))\n                     ((func? x)\n                      (let ((func (make-func (func-ast x) (func-params x)\n                                             (func-env x) (func-fn x))))\n                        (func-macro?-set! func #f)\n                        (func-meta-set! func meta)\n                        func))\n                     (else\n                      (error \"unsupported type\")))))\n    (meta . ,(lambda (x) (cond\n                          ((mal-object? x)\n                           (or (mal-meta x) mal-nil))\n                          ((func? x)\n                           (or (func-meta x) mal-nil))\n                          (else\n                           mal-nil))))\n\n    ))\n\n)\n\n)\n"
  },
  {
    "path": "impls/scheme/lib/env.sld",
    "content": "(define-library (lib env)\n\n(export make-env env-set env-get)\n\n(import (scheme base))\n\n(import (lib util))\n(import (lib types))\n\n(begin\n\n(define-record-type env\n  (%make-env outer data)\n  env?\n  (outer env-outer)\n  (data env-data env-data-set!))\n\n(define (make-env outer . rest)\n  (let ((env (%make-env outer '())))\n    (when (pair? rest)\n      (let loop ((binds (car rest))\n                 (exprs (cadr rest)))\n        (when (pair? binds)\n          (let ((bind (car binds)))\n            (if (eq? bind '&)\n                (env-set env (cadr binds) (mal-list exprs))\n                (begin\n                  (env-set env bind (car exprs))\n                  (loop (cdr binds) (cdr exprs))))))))\n    env))\n\n(define (env-set env key value)\n  (env-data-set! env (cons (cons key value) (env-data env))))\n\n(define (env-get env key)\n  (cond\n   ((alist-ref key (env-data env)) => identity)\n   ((env-outer env) => (lambda (outer) (env-get outer key)))\n   (else #f)))\n\n)\n\n)\n"
  },
  {
    "path": "impls/scheme/lib/printer.sld",
    "content": "(define-library (lib printer)\n\n(export pr-str)\n\n(import (scheme base))\n(import (scheme write))\n\n(import (lib util))\n(import (lib types))\n\n(begin\n\n(define (pr-str ast print-readably)\n  (cond\n   ((procedure? ast)\n    \"#<fn>\")\n   ((func? ast)\n    \"#<func>\")\n   (else\n    (if (procedure? ast)\n        \"#<fn>\"\n        (let* ((type (and (mal-object? ast) (mal-type ast)))\n               (value (and (mal-object? ast) (mal-value ast))))\n          (case type\n            ((true) \"true\")\n            ((false) \"false\")\n            ((nil) \"nil\")\n            ((number) (number->string value))\n            ((string) (call-with-output-string\n                       (lambda (port)\n                         (if print-readably\n                             (begin\n                               (display #\\\" port)\n                               (string-for-each\n                                (lambda (char)\n                                  (case char\n                                    ((#\\\\) (display \"\\\\\\\\\" port))\n                                    ((#\\\") (display \"\\\\\\\"\" port))\n                                    ((#\\newline) (display \"\\\\n\" port))\n                                    (else (display char port))))\n                                value)\n                               (display #\\\" port))\n                             (display value port)))))\n            ((keyword) (string-append \":\" (symbol->string value)))\n            ((symbol) (symbol->string value))\n            ((list) (pr-list value \"(\" \")\" print-readably))\n            ((vector) (pr-list (vector->list value) \"[\" \"]\" print-readably))\n            ((map) (pr-list (alist->list value) \"{\" \"}\" print-readably))\n            ((atom) (string-append \"(atom \" (pr-str value print-readably) \")\"))\n            (else (error \"unknown type\"))))))))\n\n(define (pr-list items starter ender print-readably)\n  (call-with-output-string\n   (lambda (port)\n     (display starter port)\n     (let ((reprs (map (lambda (item) (pr-str item print-readably)) items)))\n       (display (string-intersperse reprs \" \") port))\n     (display ender port))))\n\n)\n\n)\n"
  },
  {
    "path": "impls/scheme/lib/reader.sld",
    "content": "(define-library (lib reader)\n\n(export read-str)\n\n(import (scheme base))\n(import (scheme char))\n(import (scheme read))\n(import (scheme write))\n\n(import (lib util))\n(import (lib types))\n\n(begin\n\n(define-record-type reader\n  (%make-reader tokens position)\n  reader?\n  (tokens %reader-tokens)\n  (position %reader-position %reader-position-set!))\n\n(define (make-reader tokens)\n  (%make-reader (list->vector tokens) 0))\n\n(define (peek reader)\n  (let ((tokens (%reader-tokens reader))\n        (position (%reader-position reader)))\n    (if (>= position (vector-length tokens))\n        #f\n        (vector-ref tokens position))))\n\n(define (next reader)\n  (let ((token (peek reader)))\n    (when token\n      (%reader-position-set! reader (+ (%reader-position reader) 1)))\n    token))\n\n(define (read-str input)\n  (let* ((tokens (tokenizer input))\n         (reader (make-reader tokens)))\n    (read-form reader)))\n\n(define (whitespace-char? char)\n  (or (char-whitespace? char) (char=? char #\\,)))\n\n(define (special-char? char)\n  (memv char '(#\\[ #\\] #\\{ #\\} #\\( #\\) #\\' #\\` #\\~ #\\^ #\\@)))\n\n(define (non-word-char? char)\n  (or (whitespace-char? char)\n      (memv char '(#\\[ #\\] #\\{ #\\} #\\( #\\) #\\' #\\\" #\\` #\\;))))\n\n(define (tokenizer input)\n  (call-with-input-string input\n    (lambda (port)\n      (let loop ((tokens '()))\n        (if (eof-object? (peek-char port))\n            (reverse tokens)\n            (let ((char (read-char port)))\n              (cond\n               ((whitespace-char? char)\n                (loop tokens))\n               ((and (char=? char #\\~)\n                     (char=? (peek-char port) #\\@))\n                (read-char port) ; remove @ token\n                (loop (cons \"~@\" tokens)))\n               ((char=? char #\\\")\n                (loop (cons (tokenize-string port) tokens)))\n               ((char=? char #\\;)\n                (skip-comment port)\n                (loop tokens))\n               ((special-char? char)\n                (loop (cons (char->string char) tokens)))\n               (else\n                (loop (cons (tokenize-word port char) tokens))))))))))\n\n(define (tokenize-string port)\n  (let loop ((chars '(#\\\")))\n    (let ((char (read-char port)))\n      (cond\n       ((eof-object? char)\n        (list->string (reverse chars)))\n       ((char=? char #\\\\)\n        (let ((char (read-char port)))\n          (when (not (eof-object? char))\n            (loop (cons char (cons #\\\\ chars))))))\n       ((not (char=? char #\\\"))\n        (loop (cons char chars)))\n       ((char=? char #\\\")\n        (list->string (reverse (cons #\\\" chars))))))))\n\n(define (skip-comment port)\n  (let loop ()\n    (let ((char (peek-char port)))\n      (when (not (or (eof-object? char)\n                     (char=? char #\\newline)))\n        (read-char port)\n        (loop)))))\n\n(define (tokenize-word port char)\n  (let loop ((chars (list char)))\n    (let ((char (peek-char port)))\n      (if (or (eof-object? char)\n              (non-word-char? char))\n          (list->string (reverse chars))\n          (loop (cons (read-char port) chars))))))\n\n(define (read-form reader)\n  (let ((token (peek reader)))\n  (cond\n   ((equal? token \"'\")\n    (read-macro reader 'quote))\n   ((equal? token \"`\")\n    (read-macro reader 'quasiquote))\n   ((equal? token \"~\")\n    (read-macro reader 'unquote))\n   ((equal? token \"~@\")\n    (read-macro reader 'splice-unquote))\n   ((equal? token \"@\")\n    (read-macro reader 'deref))\n   ((equal? token \"^\")\n    (read-meta reader))\n   ((equal? token \"(\")\n    (read-list reader \")\" mal-list))\n   ((equal? token \"[\")\n    (read-list reader \"]\" (lambda (items) (mal-vector (list->vector items)))))\n   ((equal? token \"{\")\n    (read-list reader \"}\" (lambda (items) (mal-map (list->alist items)))))\n   (else\n    (read-atom reader)))))\n\n(define (read-macro reader symbol)\n  (next reader) ; pop macro token\n  (mal-list (list (mal-symbol symbol) (read-form reader))))\n\n(define (read-meta reader)\n  (next reader) ; pop macro token\n  (let ((form (read-form reader)))\n    (mal-list (list (mal-symbol 'with-meta) (read-form reader) form))))\n\n(define (read-list reader ender proc)\n  (next reader) ; pop list start\n  (let loop ((items '()))\n    (let ((token (peek reader)))\n      (cond\n       ((equal? token ender)\n        (next reader)\n        (proc (reverse items)))\n       ((not token)\n        (error (str \"expected '\" ender \"', got EOF\")))\n       (else\n        (loop (cons (read-form reader) items)))))))\n\n(define (read-atom reader)\n  (let ((token (next reader)))\n    (cond\n     ((not token)\n      (error \"end of token stream\" 'empty-input))\n     ((equal? token \"true\")\n      mal-true)\n     ((equal? token \"false\")\n      mal-false)\n     ((equal? token \"nil\")\n      mal-nil)\n     ((string->number token)\n      => mal-number)\n     ((char=? (string-ref token 0) #\\\")\n      (guard\n       (ex ((cond-expand\n             ;; HACK: https://github.com/ashinn/chibi-scheme/pull/540\n             (chibi\n              (error-object? ex))\n             (else\n              (read-error? ex)))\n            (error (str \"expected '\" #\\\" \"', got EOF\"))))\n       (mal-string (call-with-input-string token read))))\n     ((char=? (string-ref token 0) #\\:)\n      (mal-keyword (string->symbol (string-copy token 1))))\n     (else\n      (mal-symbol (string->symbol token))))))\n\n)\n\n)\n"
  },
  {
    "path": "impls/scheme/lib/types.sld",
    "content": "(define-library (lib types)\n\n(export make-mal-object mal-object? mal-type mal-value mal-value-set! mal-meta\n        mal-true mal-false mal-nil\n        mal-number mal-string mal-symbol mal-keyword\n        mal-list mal-vector mal-map mal-atom\n\n        make-func func? func-ast func-params func-env\n        func-fn func-macro? func-macro?-set! func-meta func-meta-set!\n\n        mal-instance-of?)\n\n(import (scheme base))\n\n(begin\n\n(define-record-type mal-object\n  (make-mal-object type value meta)\n  mal-object?\n  (type mal-type)\n  (value mal-value mal-value-set!)\n  (meta mal-meta mal-meta-set!))\n\n(define mal-true (make-mal-object 'true #t #f))\n(define mal-false (make-mal-object 'false #f #f))\n(define mal-nil (make-mal-object 'nil #f #f))\n\n(define (mal-number n)\n  (make-mal-object 'number n #f))\n\n(define (mal-string string)\n  (make-mal-object 'string string #f))\n\n(define (mal-symbol name)\n  (make-mal-object 'symbol name #f))\n\n(define (mal-keyword name)\n  (make-mal-object 'keyword name #f))\n\n(define (mal-list items)\n  (make-mal-object 'list items #f))\n\n(define (mal-vector items)\n  (make-mal-object 'vector items #f))\n\n(define (mal-map items)\n  (make-mal-object 'map items #f))\n\n(define (mal-atom item)\n  (make-mal-object 'atom item #f))\n\n(define-record-type func\n  (%make-func ast params env fn macro? meta)\n  func?\n  (ast func-ast)\n  (params func-params)\n  (env func-env)\n  (fn func-fn)\n  (macro? func-macro? func-macro?-set!)\n  (meta func-meta func-meta-set!))\n\n(define (make-func ast params env fn)\n  (%make-func ast params env fn #f #f))\n\n(define (mal-instance-of? x type)\n  (and (mal-object? x) (eq? (mal-type x) type)))\n\n)\n\n)\n"
  },
  {
    "path": "impls/scheme/lib/util.sld",
    "content": "(define-library (lib util)\n\n(export call-with-input-string call-with-output-string\n        str prn debug\n        string-intersperse explode\n        char->string\n        list->alist alist->list alist-ref alist-map\n        ->list car-safe cdr-safe contains? last butlast\n        identity readline\n\n        ;; HACK: cyclone doesn't have those\n        error-object? read-error? error-object-message error-object-irritants)\n\n(import (scheme base))\n(import (scheme write))\n\n(begin\n\n;; HACK: cyclone currently implements error the SICP way\n(cond-expand\n (cyclone\n  (define (error-object? x) (and (pair? x) (string? (car x))))\n  (define read-error? error-object?)\n  (define error-object-message car)\n  (define error-object-irritants cdr))\n (else))\n\n(define (call-with-input-string string proc)\n  (let ((port (open-input-string string)))\n    (dynamic-wind\n        (lambda () #t)\n        (lambda () (proc port))\n        (lambda () (close-input-port port)))))\n\n(define (call-with-output-string proc)\n  (let ((port (open-output-string)))\n    (dynamic-wind\n        (lambda () #t)\n        (lambda () (proc port) (get-output-string port))\n        (lambda () (close-output-port port)))))\n\n(define (str . items)\n  (call-with-output-string\n   (lambda (port)\n     (for-each (lambda (item) (display item port)) items))))\n\n(define (prn . items)\n  (for-each (lambda (item) (write item) (display \" \")) items)\n  (newline))\n\n(define (debug . items)\n  (parameterize ((current-output-port (current-error-port)))\n    (apply prn items)))\n\n(define (intersperse items sep)\n  (let loop ((items items)\n             (acc '()))\n    (if (null? items)\n        (reverse acc)\n        (let ((tail (cdr items)))\n          (if (null? tail)\n              (loop (cdr items) (cons (car items) acc))\n              (loop (cdr items) (cons sep (cons (car items) acc))))))))\n\n(define (string-intersperse items sep)\n  (apply string-append (intersperse items sep)))\n\n(define (char->string char)\n  (list->string (list char)))\n\n(define (explode string)\n  (map char->string (string->list string)))\n\n(define (list->alist items)\n  (let loop ((items items)\n             (acc '()))\n    (if (null? items)\n        (reverse acc)\n        (let ((key (car items)))\n          (when (null? (cdr items))\n            (error \"unbalanced list\"))\n          (let ((value (cadr items)))\n            (loop (cddr items)\n                  (cons (cons key value) acc)))))))\n\n(define (alist->list items)\n  (let loop ((items items)\n             (acc '()))\n    (if (null? items)\n        (reverse acc)\n        (let ((kv (car items)))\n          (loop (cdr items)\n                (cons (cdr kv) (cons (car kv) acc)))))))\n\n(define (alist-ref key alist . args)\n  (let ((test (if (pair? args) (car args) eqv?))\n        (default (if (> (length args) 1) (cadr args) #f)))\n    (let loop ((items alist))\n      (if (pair? items)\n          (let ((item (car items)))\n            (if (test (car item) key)\n                (cdr item)\n                (loop (cdr items))))\n          default))))\n\n(define (alist-map proc items)\n  (map (lambda (item) (proc (car item) (cdr item))) items))\n\n(define (->list items)\n  (if (vector? items)\n      (vector->list items)\n      items))\n\n(define (car-safe x)\n  (if (pair? x)\n      (car x)\n      '()))\n\n(define (cdr-safe x)\n  (if (pair? x)\n      (cdr x)\n      '()))\n\n(define (contains? items test)\n  (let loop ((items items))\n    (if (pair? items)\n        (if (test (car items))\n            #t\n            (loop (cdr items)))\n        #f)))\n\n(define (last items)\n  (when (null? items)\n    (error \"empty argument\"))\n  (let loop ((items items))\n    (let ((tail (cdr items)))\n      (if (pair? tail)\n          (loop tail)\n          (car items)))))\n\n(define (butlast items)\n  (when (null? items)\n    (error \"empty argument\"))\n  (let loop ((items items)\n             (acc '()))\n    (let ((tail (cdr items)))\n      (if (pair? tail)\n          (loop tail (cons (car items) acc))\n          (reverse acc)))))\n\n(define (identity x) x)\n\n(define (readline prompt)\n  (display prompt)\n  (flush-output-port)\n  (let ((input (read-line)))\n    (if (eof-object? input)\n        #f\n        input)))\n\n)\n\n)\n"
  },
  {
    "path": "impls/scheme/run",
    "content": "#!/usr/bin/env bash\nbasedir=$(dirname $0)\nstep=${STEP:-stepA_mal}\n\nif [[ -e /usr/share/kawa/lib/kawa.jar ]]; then\n    kawa=/usr/share/kawa/lib/kawa.jar\nelif [[ -e /usr/local/share/kawa/lib/kawa.jar ]]; then\n    kawa=/usr/local/share/kawa/lib/kawa.jar\nfi\n\nif [[ $(which sash 2>/dev/null) ]]; then\n    sagittarius=sash\nelif [[ $(which sagittarius 2>/dev/null) ]]; then\n    sagittarius=sagittarius\nfi\n\ncase ${scheme_MODE:-chibi} in\n    chibi)       exec chibi-scheme -I$basedir $basedir/$step.scm      \"${@}\" ;;\n    kawa)        exec java -cp $kawa:$basedir/out $step               \"${@}\" ;;\n    gauche)      exec gosh -I$basedir $basedir/$step.scm              \"${@}\" ;;\n    chicken)     CHICKEN_REPOSITORY=$basedir/eggs exec $basedir/$step \"${@}\" ;;\n    sagittarius) exec $sagittarius -n -L$basedir $basedir/$step.scm   \"${@}\" ;;\n    cyclone)     exec $basedir/$step                                  \"${@}\" ;;\n    foment)      exec foment $basedir/$step.scm                       \"${@}\" ;;\n    *) echo \"Invalid scheme_MODE: ${scheme_MODE}\"; exit 2 ;;\nesac\n"
  },
  {
    "path": "impls/scheme/step0_repl.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n\n(define (READ input)\n  input)\n\n(define (EVAL input)\n  input)\n\n(define (PRINT input)\n  input)\n\n(define (rep input)\n  (PRINT (EVAL (READ input))))\n\n(define (readline prompt)\n  (display prompt)\n  (flush-output-port)\n  (let ((input (read-line)))\n    (if (eof-object? input)\n        #f\n        input)))\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (display (rep input))\n        (newline)\n        (loop))))\n  (newline))\n\n(main)\n"
  },
  {
    "path": "impls/scheme/step1_read_print.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n\n(define (READ input)\n  (read-str input))\n\n(define (EVAL ast)\n  ast)\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define (rep input)\n  (PRINT (EVAL (READ input))))\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline))))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(main)\n"
  },
  {
    "path": "impls/scheme/step2_eval.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n\n(define (READ input)\n  (read-str input))\n\n(define (EVAL ast env)\n    ; (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (alist-ref key env) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n        (let ((items (mal-value ast)))\n        (if (null? items)\n            ast\n            (let ((op (EVAL (car items) env))\n                  (ops (map (lambda (item) (EVAL item env)) (cdr items))))\n              (apply op ops)))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env\n  `((+ . ,(lambda (a b) (mal-number (+ (mal-value a) (mal-value b)))))\n    (- . ,(lambda (a b) (mal-number (- (mal-value a) (mal-value b)))))\n    (* . ,(lambda (a b) (mal-number (* (mal-value a) (mal-value b)))))\n    (/ . ,(lambda (a b) (mal-number (/ (mal-value a) (mal-value b)))))))\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline))))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(main)\n"
  },
  {
    "path": "impls/scheme/step3_env.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n\n(define (READ input)\n  (read-str input))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n        (let ((items (mal-value ast)))\n          (if (null? items)\n              ast\n              (case (mal-value (car items))\n                ((def!)\n                 (let ((symbol (mal-value (cadr items)))\n                       (value (EVAL (list-ref items 2) env)))\n                   (env-set env symbol value)\n                   value))\n                ((let*)\n                 (let* ((env* (make-env env))\n                        (binds (mal-value (cadr items)))\n                        (binds (if (vector? binds) (vector->list binds) binds))\n                        (form (list-ref items 2)))\n                   (let loop ((binds binds))\n                     (when (pair? binds)\n                       (let ((key (mal-value (car binds))))\n                         (when (null? (cdr binds))\n                           (error \"unbalanced list\"))\n                         (let ((value (EVAL (cadr binds) env*)))\n                           (env-set env* key value)\n                           (loop (cddr binds))))))\n                   (EVAL form env*)))\n                (else\n                 (let ((op (EVAL (car items) env))\n                       (ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                   (apply op ops)))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(env-set repl-env '+ (lambda (a b) (mal-number (+ (mal-value a) (mal-value b)))))\n(env-set repl-env '- (lambda (a b) (mal-number (- (mal-value a) (mal-value b)))))\n(env-set repl-env '* (lambda (a b) (mal-number (* (mal-value a) (mal-value b)))))\n(env-set repl-env '/ (lambda (a b) (mal-number (/ (mal-value a) (mal-value b)))))\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline))))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(main)\n"
  },
  {
    "path": "impls/scheme/step4_if_fn_do.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n        (let ((items (mal-value ast)))\n          (if (null? items)\n              ast\n              (case (mal-value (car items))\n                ((def!)\n                 (let ((symbol (mal-value (cadr items)))\n                       (value (EVAL (list-ref items 2) env)))\n                   (env-set env symbol value)\n                   value))\n                ((let*)\n                 (let ((env* (make-env env))\n                       (binds (->list (mal-value (cadr items))))\n                       (form (list-ref items 2)))\n                   (let loop ((binds binds))\n                     (when (pair? binds)\n                       (let ((key (mal-value (car binds))))\n                         (when (null? (cdr binds))\n                           (error \"unbalanced list\"))\n                         (let ((value (EVAL (cadr binds) env*)))\n                           (env-set env* key value)\n                           (loop (cddr binds))))))\n                   (EVAL form env*)))\n                ((do)\n                 (let ((forms (cdr items)))\n                   (if (null? forms)\n                       mal-nil\n                       ;; the evaluation order of map is unspecified\n                       (let loop ((forms forms))\n                         (let ((form (car forms))\n                               (tail (cdr forms)))\n                           (if (null? tail)\n                               (EVAL form env)\n                               (begin\n                                 (EVAL form env)\n                                 (loop tail))))))))\n                ((if)\n                 (let* ((condition (EVAL (cadr items) env))\n                        (type (and (mal-object? condition)\n                                   (mal-type condition))))\n                   (if (memq type '(false nil))\n                       (if (< (length items) 4)\n                           mal-nil\n                           (EVAL (list-ref items 3) env))\n                       (EVAL (list-ref items 2) env))))\n                ((fn*)\n                 (let* ((binds (->list (mal-value (cadr items))))\n                        (binds (map mal-value binds))\n                        (body (list-ref items 2)))\n                   (lambda args\n                     (let ((env* (make-env env binds args)))\n                       (EVAL body env*)))))\n                (else\n                 (let ((op (EVAL (car items) env))\n                       (ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                   (apply op ops)))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(main)\n"
  },
  {
    "path": "impls/scheme/step5_tco.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n        (let ((items (mal-value ast)))\n          (if (null? items)\n              ast\n              (case (mal-value (car items))\n                ((def!)\n                 (let ((symbol (mal-value (cadr items)))\n                       (value (EVAL (list-ref items 2) env)))\n                   (env-set env symbol value)\n                   value))\n                ((let*)\n                 (let ((env* (make-env env))\n                       (binds (->list (mal-value (cadr items))))\n                       (form (list-ref items 2)))\n                   (let loop ((binds binds))\n                     (when (pair? binds)\n                       (let ((key (mal-value (car binds))))\n                         (when (null? (cdr binds))\n                           (error \"unbalanced list\"))\n                         (let ((value (EVAL (cadr binds) env*)))\n                           (env-set env* key value)\n                           (loop (cddr binds))))))\n                   (EVAL form env*))) ; TCO\n                ((do)\n                 (let ((forms (cdr items)))\n                   (if (null? forms)\n                       mal-nil\n                       ;; the evaluation order of map is unspecified\n                       (let loop ((forms forms))\n                         (let ((form (car forms))\n                               (tail (cdr forms)))\n                           (if (null? tail)\n                               (EVAL form env) ; TCO\n                               (begin\n                                 (EVAL form env)\n                                 (loop tail))))))))\n                ((if)\n                 (let* ((condition (EVAL (cadr items) env))\n                        (type (and (mal-object? condition)\n                                   (mal-type condition))))\n                   (if (memq type '(false nil))\n                       (if (< (length items) 4)\n                           mal-nil\n                           (EVAL (list-ref items 3) env)) ; TCO\n                       (EVAL (list-ref items 2) env)))) ; TCO\n                ((fn*)\n                 (let* ((binds (->list (mal-value (cadr items))))\n                        (binds (map mal-value binds))\n                        (body (list-ref items 2))\n                        (fn (lambda args\n                              (let ((env* (make-env env binds args)))\n                                (EVAL body env*)))))\n                   (make-func body binds env fn)))\n                (else\n                 (let ((op (EVAL (car items) env))\n                       (ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                   (if (func? op)\n                       (let* ((outer (func-env op))\n                              (binds (func-params op))\n                              (env* (make-env outer binds ops)))\n                         (EVAL (func-ast op) env*)) ; TCO\n                       (apply op ops))))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(main)\n"
  },
  {
    "path": "impls/scheme/step6_file.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n(import (scheme process-context))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n        (let ((items (mal-value ast)))\n          (if (null? items)\n              ast\n              (let ((a0 (car items)))\n                (case (and (mal-object? a0) (mal-value a0))\n                  ((def!)\n                   (let ((symbol (mal-value (cadr items)))\n                         (value (EVAL (list-ref items 2) env)))\n                     (env-set env symbol value)\n                     value))\n                  ((let*)\n                   (let ((env* (make-env env))\n                         (binds (->list (mal-value (cadr items))))\n                         (form (list-ref items 2)))\n                     (let loop ((binds binds))\n                       (when (pair? binds)\n                         (let ((key (mal-value (car binds))))\n                           (when (null? (cdr binds))\n                             (error \"unbalanced list\"))\n                           (let ((value (EVAL (cadr binds) env*)))\n                             (env-set env* key value)\n                             (loop (cddr binds))))))\n                     (EVAL form env*))) ; TCO\n                  ((do)\n                   (let ((forms (cdr items)))\n                     (if (null? forms)\n                         mal-nil\n                         ;; the evaluation order of map is unspecified\n                         (let loop ((forms forms))\n                           (let ((form (car forms))\n                                 (tail (cdr forms)))\n                             (if (null? tail)\n                                 (EVAL form env) ; TCO\n                                 (begin\n                                   (EVAL form env)\n                                   (loop tail))))))))\n                  ((if)\n                   (let* ((condition (EVAL (cadr items) env))\n                          (type (and (mal-object? condition)\n                                     (mal-type condition))))\n                     (if (memq type '(false nil))\n                         (if (< (length items) 4)\n                             mal-nil\n                             (EVAL (list-ref items 3) env)) ; TCO\n                         (EVAL (list-ref items 2) env)))) ; TCO\n                  ((fn*)\n                   (let* ((binds (->list (mal-value (cadr items))))\n                          (binds (map mal-value binds))\n                          (body (list-ref items 2))\n                          (fn (lambda args\n                                (let ((env* (make-env env binds args)))\n                                  (EVAL body env*)))))\n                     (make-func body binds env fn)))\n                  (else\n                   (let ((op (EVAL a0 env))\n                         (ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                     (if (func? op)\n                         (let* ((outer (func-env op))\n                                (binds (func-params op))\n                                (env* (make-env outer binds ops)))\n                           (EVAL (func-ast op) env*)) ; TCO\n                         (apply op ops)))))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define args (cdr (command-line)))\n\n(env-set repl-env 'eval (lambda (ast) (EVAL ast repl-env)))\n(env-set repl-env '*ARGV* (mal-list (map mal-string (cdr-safe args))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(if (null? args)\n    (main)\n    (rep (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n"
  },
  {
    "path": "impls/scheme/step7_quote.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n(import (scheme process-context))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (starts-with? ast sym)\n  (let ((items (mal-value ast)))\n    (and (not (null? items))\n         (let ((a0 (car items)))\n           (and (mal-instance-of? a0 'symbol)\n                (eq? (mal-value a0) sym))))))\n\n(define (qq-lst xs)\n  (if (null? xs)\n      (mal-list '())\n      (let ((elt (car xs))\n            (acc (qq-lst (cdr xs))))\n        (if (and (mal-instance-of? elt 'list) (starts-with? elt 'splice-unquote))\n            (mal-list (list (mal-symbol 'concat) (cadr (mal-value elt)) acc))\n            (mal-list (list (mal-symbol 'cons) (QUASIQUOTE elt) acc))))))\n\n(define (QUASIQUOTE ast)\n  (case (and (mal-object? ast) (mal-type ast))\n    ((list)       (if (starts-with? ast 'unquote)\n                    (cadr (mal-value ast))\n                    (qq-lst (->list (mal-value ast)))))\n    ((vector)     (mal-list (list (mal-symbol 'vec) (qq-lst (->list (mal-value ast))))))\n    ((map symbol) (mal-list (list (mal-symbol 'quote) ast)))\n    (else         ast)))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n        (let ((items (mal-value ast)))\n          (if (null? items)\n              ast\n              (let ((a0 (car items)))\n                (case (and (mal-object? a0) (mal-value a0))\n                  ((def!)\n                   (let ((symbol (mal-value (cadr items)))\n                         (value (EVAL (list-ref items 2) env)))\n                     (env-set env symbol value)\n                     value))\n                  ((let*)\n                   (let ((env* (make-env env))\n                         (binds (->list (mal-value (cadr items))))\n                         (form (list-ref items 2)))\n                     (let loop ((binds binds))\n                       (when (pair? binds)\n                         (let ((key (mal-value (car binds))))\n                           (when (null? (cdr binds))\n                             (error \"unbalanced list\"))\n                           (let ((value (EVAL (cadr binds) env*)))\n                             (env-set env* key value)\n                             (loop (cddr binds))))))\n                     (EVAL form env*))) ; TCO\n                  ((do)\n                   (let ((forms (cdr items)))\n                     (if (null? forms)\n                         mal-nil\n                         ;; the evaluation order of map is unspecified\n                         (let loop ((forms forms))\n                           (let ((form (car forms))\n                                 (tail (cdr forms)))\n                             (if (null? tail)\n                                 (EVAL form env) ; TCO\n                                 (begin\n                                   (EVAL form env)\n                                   (loop tail))))))))\n                  ((if)\n                   (let* ((condition (EVAL (cadr items) env))\n                          (type (and (mal-object? condition)\n                                     (mal-type condition))))\n                     (if (memq type '(false nil))\n                         (if (< (length items) 4)\n                             mal-nil\n                             (EVAL (list-ref items 3) env)) ; TCO\n                         (EVAL (list-ref items 2) env)))) ; TCO\n                  ((quote) (cadr items))\n                  ((quasiquote) (EVAL (QUASIQUOTE (cadr items)) env)) ; TCO\n                  ((fn*)\n                   (let* ((binds (->list (mal-value (cadr items))))\n                          (binds (map mal-value binds))\n                          (body (list-ref items 2))\n                          (fn (lambda args\n                                (let ((env* (make-env env binds args)))\n                                  (EVAL body env*)))))\n                     (make-func body binds env fn)))\n                  (else\n                   (let ((op (EVAL a0 env))\n                         (ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                     (if (func? op)\n                         (let* ((outer (func-env op))\n                                (binds (func-params op))\n                                (env* (make-env outer binds ops)))\n                           (EVAL (func-ast op) env*)) ; TCO\n                         (apply op ops)))))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define args (cdr (command-line)))\n\n(env-set repl-env 'eval (lambda (ast) (EVAL ast repl-env)))\n(env-set repl-env '*ARGV* (mal-list (map mal-string (cdr-safe args))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(if (null? args)\n    (main)\n    (rep (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n"
  },
  {
    "path": "impls/scheme/step8_macros.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n(import (scheme process-context))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (starts-with? ast sym)\n  (let ((items (mal-value ast)))\n    (and (not (null? items))\n         (let ((a0 (car items)))\n           (and (mal-instance-of? a0 'symbol)\n                (eq? (mal-value a0) sym))))))\n\n(define (qq-lst xs)\n  (if (null? xs)\n      (mal-list '())\n      (let ((elt (car xs))\n            (acc (qq-lst (cdr xs))))\n        (if (and (mal-instance-of? elt 'list) (starts-with? elt 'splice-unquote))\n            (mal-list (list (mal-symbol 'concat) (cadr (mal-value elt)) acc))\n            (mal-list (list (mal-symbol 'cons) (QUASIQUOTE elt) acc))))))\n\n(define (QUASIQUOTE ast)\n  (case (and (mal-object? ast) (mal-type ast))\n    ((list)       (if (starts-with? ast 'unquote)\n                    (cadr (mal-value ast))\n                    (qq-lst (->list (mal-value ast)))))\n    ((vector)     (mal-list (list (mal-symbol 'vec) (qq-lst (->list (mal-value ast))))))\n    ((map symbol) (mal-list (list (mal-symbol 'quote) ast)))\n    (else         ast)))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n       (let ((items (mal-value ast)))\n         (if (null? items)\n                  ast\n                  (let ((a0 (car items)))\n                    (case (and (mal-object? a0) (mal-value a0))\n                      ((def!)\n                       (let ((symbol (mal-value (cadr items)))\n                             (value (EVAL (list-ref items 2) env)))\n                         (env-set env symbol value)\n                         value))\n                      ((defmacro!)\n                       (let ((symbol (mal-value (cadr items)))\n                             (value (EVAL (list-ref items 2) env)))\n                         (when (func? value)\n                           (func-macro?-set! value #t))\n                         (env-set env symbol value)\n                         value))\n                      ((let*)\n                       (let ((env* (make-env env))\n                             (binds (->list (mal-value (cadr items))))\n                             (form (list-ref items 2)))\n                         (let loop ((binds binds))\n                           (when (pair? binds)\n                             (let ((key (mal-value (car binds))))\n                               (when (null? (cdr binds))\n                                 (error \"unbalanced list\"))\n                               (let ((value (EVAL (cadr binds) env*)))\n                                 (env-set env* key value)\n                                 (loop (cddr binds))))))\n                         (EVAL form env*))) ; TCO\n                      ((do)\n                       (let ((forms (cdr items)))\n                         (if (null? forms)\n                             mal-nil\n                             ;; the evaluation order of map is unspecified\n                             (let loop ((forms forms))\n                               (let ((form (car forms))\n                                     (tail (cdr forms)))\n                                 (if (null? tail)\n                                     (EVAL form env) ; TCO\n                                     (begin\n                                       (EVAL form env)\n                                       (loop tail))))))))\n                      ((if)\n                       (let* ((condition (EVAL (cadr items) env))\n                              (type (and (mal-object? condition)\n                                         (mal-type condition))))\n                         (if (memq type '(false nil))\n                             (if (< (length items) 4)\n                                 mal-nil\n                                 (EVAL (list-ref items 3) env)) ; TCO\n                             (EVAL (list-ref items 2) env)))) ; TCO\n                      ((quote)\n                       (cadr items))\n                      ((quasiquote)\n                       (EVAL (QUASIQUOTE (cadr items)) env)) ; TCO\n                      ((fn*)\n                       (let* ((binds (->list (mal-value (cadr items))))\n                              (binds (map mal-value binds))\n                              (body (list-ref items 2))\n                              (fn (lambda args\n                                    (let ((env* (make-env env binds args)))\n                                      (EVAL body env*)))))\n                         (make-func body binds env fn)))\n                      (else\n                       (let ((op (EVAL a0 env)))\n                       (if (and (func? op) (func-macro? op))\n                        (EVAL (apply (func-fn op) (cdr items)) env) ; TCO\n                        (let* ((ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                         (if (func? op)\n                             (let* ((outer (func-env op))\n                                    (binds (func-params op))\n                                    (env* (make-env outer binds ops)))\n                               (EVAL (func-ast op) env*)) ; TCO\n                             (apply op ops)))))))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define args (cdr (command-line)))\n\n(env-set repl-env 'eval (lambda (ast) (EVAL ast repl-env)))\n(env-set repl-env '*ARGV* (mal-list (map mal-string (cdr-safe args))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(if (null? args)\n    (main)\n    (rep (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n"
  },
  {
    "path": "impls/scheme/step9_try.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n(import (scheme process-context))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (starts-with? ast sym)\n  (let ((items (mal-value ast)))\n    (and (not (null? items))\n         (let ((a0 (car items)))\n           (and (mal-instance-of? a0 'symbol)\n                (eq? (mal-value a0) sym))))))\n\n(define (qq-lst xs)\n  (if (null? xs)\n      (mal-list '())\n      (let ((elt (car xs))\n            (acc (qq-lst (cdr xs))))\n        (if (and (mal-instance-of? elt 'list) (starts-with? elt 'splice-unquote))\n            (mal-list (list (mal-symbol 'concat) (cadr (mal-value elt)) acc))\n            (mal-list (list (mal-symbol 'cons) (QUASIQUOTE elt) acc))))))\n\n(define (QUASIQUOTE ast)\n  (case (and (mal-object? ast) (mal-type ast))\n    ((list)       (if (starts-with? ast 'unquote)\n                    (cadr (mal-value ast))\n                    (qq-lst (->list (mal-value ast)))))\n    ((vector)     (mal-list (list (mal-symbol 'vec) (qq-lst (->list (mal-value ast))))))\n    ((map symbol) (mal-list (list (mal-symbol 'quote) ast)))\n    (else         ast)))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n       (let ((items (mal-value ast)))\n         (if (null? items)\n                  ast\n                  (let ((a0 (car items)))\n                    (case (and (mal-object? a0) (mal-value a0))\n                      ((def!)\n                       (let ((symbol (mal-value (cadr items)))\n                             (value (EVAL (list-ref items 2) env)))\n                         (env-set env symbol value)\n                         value))\n                      ((defmacro!)\n                       (let ((symbol (mal-value (cadr items)))\n                             (value (EVAL (list-ref items 2) env)))\n                         (when (func? value)\n                           (func-macro?-set! value #t))\n                         (env-set env symbol value)\n                         value))\n                      ((try*)\n                       (if (< (length items) 3)\n                         (EVAL (cadr items) env)\n                         (let ((handle-catch (lambda (value)\n                              (let ((handler (mal-value (list-ref items 2)))\n                                    (env* (make-env env)))\n                                (env-set env* (mal-value (cadr handler)) value)\n                                (EVAL (list-ref handler 2) env*)))))\n                           (guard\n                            (ex ((error-object? ex)\n                                 (handle-catch\n                                  (mal-string (error-object-message ex))))\n                                ((and (pair? ex) (eq? (car ex) 'user-error))\n                                 (handle-catch (cdr ex))))\n                            (EVAL (cadr items) env)))))\n                      ((let*)\n                       (let ((env* (make-env env))\n                             (binds (->list (mal-value (cadr items))))\n                             (form (list-ref items 2)))\n                         (let loop ((binds binds))\n                           (when (pair? binds)\n                             (let ((key (mal-value (car binds))))\n                               (when (null? (cdr binds))\n                                 (error \"unbalanced list\"))\n                               (let ((value (EVAL (cadr binds) env*)))\n                                 (env-set env* key value)\n                                 (loop (cddr binds))))))\n                         (EVAL form env*))) ; TCO\n                      ((do)\n                       (let ((forms (cdr items)))\n                         (if (null? forms)\n                             mal-nil\n                             ;; the evaluation order of map is unspecified\n                             (let loop ((forms forms))\n                               (let ((form (car forms))\n                                     (tail (cdr forms)))\n                                 (if (null? tail)\n                                     (EVAL form env) ; TCO\n                                     (begin\n                                       (EVAL form env)\n                                       (loop tail))))))))\n                      ((if)\n                       (let* ((condition (EVAL (cadr items) env))\n                              (type (and (mal-object? condition)\n                                         (mal-type condition))))\n                         (if (memq type '(false nil))\n                             (if (< (length items) 4)\n                                 mal-nil\n                                 (EVAL (list-ref items 3) env)) ; TCO\n                             (EVAL (list-ref items 2) env)))) ; TCO\n                      ((quote)\n                       (cadr items))\n                      ((quasiquote)\n                       (EVAL (QUASIQUOTE (cadr items)) env)) ; TCO\n                      ((fn*)\n                       (let* ((binds (->list (mal-value (cadr items))))\n                              (binds (map mal-value binds))\n                              (body (list-ref items 2))\n                              (fn (lambda args\n                                    (let ((env* (make-env env binds args)))\n                                      (EVAL body env*)))))\n                         (make-func body binds env fn)))\n                      (else\n                       (let ((op (EVAL a0 env)))\n                       (if (and (func? op) (func-macro? op))\n                        (EVAL (apply (func-fn op) (cdr items)) env) ; TCO\n                        (let* ((ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                         (if (func? op)\n                             (let* ((outer (func-env op))\n                                    (binds (func-params op))\n                                    (env* (make-env outer binds ops)))\n                               (EVAL (func-ast op) env*)) ; TCO\n                             (apply op ops)))))))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define args (cdr (command-line)))\n\n(env-set repl-env 'eval (lambda (ast) (EVAL ast repl-env)))\n(env-set repl-env '*ARGV* (mal-list (map mal-string (cdr-safe args))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(define (main)\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(if (null? args)\n    (main)\n    (rep (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n"
  },
  {
    "path": "impls/scheme/stepA_mal.scm",
    "content": "(import (scheme base))\n(import (scheme write))\n(import (scheme process-context))\n\n(import (lib util))\n(import (lib reader))\n(import (lib printer))\n(import (lib types))\n(import (lib env))\n(import (lib core))\n\n(define (READ input)\n  (read-str input))\n\n(define (starts-with? ast sym)\n  (let ((items (mal-value ast)))\n    (and (not (null? items))\n         (let ((a0 (car items)))\n           (and (mal-instance-of? a0 'symbol)\n                (eq? (mal-value a0) sym))))))\n\n(define (qq-lst xs)\n  (if (null? xs)\n      (mal-list '())\n      (let ((elt (car xs))\n            (acc (qq-lst (cdr xs))))\n        (if (and (mal-instance-of? elt 'list) (starts-with? elt 'splice-unquote))\n            (mal-list (list (mal-symbol 'concat) (cadr (mal-value elt)) acc))\n            (mal-list (list (mal-symbol 'cons) (QUASIQUOTE elt) acc))))))\n\n(define (QUASIQUOTE ast)\n  (case (and (mal-object? ast) (mal-type ast))\n    ((list)       (if (starts-with? ast 'unquote)\n                    (cadr (mal-value ast))\n                    (qq-lst (->list (mal-value ast)))))\n    ((vector)     (mal-list (list (mal-symbol 'vec) (qq-lst (->list (mal-value ast))))))\n    ((map symbol) (mal-list (list (mal-symbol 'quote) ast)))\n    (else         ast)))\n\n(define (EVAL ast env)\n    (let ((dbgeval (env-get env 'DEBUG-EVAL)))\n      (when (and (mal-object? dbgeval)\n                 (not (memq (mal-type dbgeval) '(false nil))))\n        (display (str \"EVAL: \" (pr-str ast #t) \"\\n\"))))\n    (case (and (mal-object? ast) (mal-type ast))\n      ((symbol)\n       (let ((key (mal-value ast)))\n         (or (env-get env key) (error (str \"'\" key \"' not found\")))))\n      ((vector)\n       (mal-vector (vector-map (lambda (item) (EVAL item env))\n                               (mal-value ast))))\n      ((map)\n       (mal-map (alist-map (lambda (key value) (cons key (EVAL value env)))\n                           (mal-value ast))))\n      ((list)\n       (let ((items (mal-value ast)))\n         (if (null? items)\n                  ast\n                  (let ((a0 (car items)))\n                    (case (and (mal-object? a0) (mal-value a0))\n                      ((def!)\n                       (let ((symbol (mal-value (cadr items)))\n                             (value (EVAL (list-ref items 2) env)))\n                         (env-set env symbol value)\n                         value))\n                      ((defmacro!)\n                       (let ((symbol (mal-value (cadr items)))\n                             (value (EVAL (list-ref items 2) env)))\n                         (when (func? value)\n                           (func-macro?-set! value #t))\n                         (env-set env symbol value)\n                         value))\n                      ((try*)\n                       (if (< (length items) 3)\n                         (EVAL (cadr items) env)\n                         (let ((handle-catch (lambda (value)\n                              (let ((handler (mal-value (list-ref items 2)))\n                                    (env* (make-env env)))\n                                (env-set env* (mal-value (cadr handler)) value)\n                                (EVAL (list-ref handler 2) env*)))))\n                           (guard\n                            (ex ((error-object? ex)\n                                 (handle-catch\n                                  (mal-string (error-object-message ex))))\n                                ((and (pair? ex) (eq? (car ex) 'user-error))\n                                 (handle-catch (cdr ex))))\n                            (EVAL (cadr items) env)))))\n                      ((let*)\n                       (let ((env* (make-env env))\n                             (binds (->list (mal-value (cadr items))))\n                             (form (list-ref items 2)))\n                         (let loop ((binds binds))\n                           (when (pair? binds)\n                             (let ((key (mal-value (car binds))))\n                               (when (null? (cdr binds))\n                                 (error \"unbalanced list\"))\n                               (let ((value (EVAL (cadr binds) env*)))\n                                 (env-set env* key value)\n                                 (loop (cddr binds))))))\n                         (EVAL form env*))) ; TCO\n                      ((do)\n                       (let ((forms (cdr items)))\n                         (if (null? forms)\n                             mal-nil\n                             ;; the evaluation order of map is unspecified\n                             (let loop ((forms forms))\n                               (let ((form (car forms))\n                                     (tail (cdr forms)))\n                                 (if (null? tail)\n                                     (EVAL form env) ; TCO\n                                     (begin\n                                       (EVAL form env)\n                                       (loop tail))))))))\n                      ((if)\n                       (let* ((condition (EVAL (cadr items) env))\n                              (type (and (mal-object? condition)\n                                         (mal-type condition))))\n                         (if (memq type '(false nil))\n                             (if (< (length items) 4)\n                                 mal-nil\n                                 (EVAL (list-ref items 3) env)) ; TCO\n                             (EVAL (list-ref items 2) env)))) ; TCO\n                      ((quote)\n                       (cadr items))\n                      ((quasiquote)\n                       (EVAL (QUASIQUOTE (cadr items)) env)) ; TCO\n                      ((fn*)\n                       (let* ((binds (->list (mal-value (cadr items))))\n                              (binds (map mal-value binds))\n                              (body (list-ref items 2))\n                              (fn (lambda args\n                                    (let ((env* (make-env env binds args)))\n                                      (EVAL body env*)))))\n                         (make-func body binds env fn)))\n                      (else\n                       (let ((op (EVAL a0 env)))\n                       (if (and (func? op) (func-macro? op))\n                        (EVAL (apply (func-fn op) (cdr items)) env) ; TCO\n                        (let* ((ops (map (lambda (item) (EVAL item env)) (cdr items))))\n                         (if (func? op)\n                             (let* ((outer (func-env op))\n                                    (binds (func-params op))\n                                    (env* (make-env outer binds ops)))\n                               (EVAL (func-ast op) env*)) ; TCO\n                             (apply op ops)))))))))))\n      (else ast)))\n\n(define (PRINT ast)\n  (pr-str ast #t))\n\n(define repl-env (make-env #f))\n(for-each (lambda (kv) (env-set repl-env (car kv) (cdr kv))) ns)\n\n(define (rep input)\n  (PRINT (EVAL (READ input) repl-env)))\n\n(define args (cdr (command-line)))\n\n(env-set repl-env 'eval (lambda (ast) (EVAL ast repl-env)))\n(env-set repl-env '*ARGV* (mal-list (map mal-string (cdr-safe args))))\n(let ((scheme (or (get-environment-variable \"scheme_MODE\") \"chibi\")))\n  (env-set repl-env '*host-language* (mal-string (str \"scheme (\" scheme \")\"))))\n\n(rep \"(def! not (fn* (a) (if a false true)))\")\n(rep \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n(rep \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n(define (main)\n  (rep \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n  (let loop ()\n    (let ((input (readline \"user> \")))\n      (when input\n        (guard\n         (ex ((error-object? ex)\n              (when (not (memv 'empty-input (error-object-irritants ex)))\n                (display \"[error] \")\n                (display (error-object-message ex))\n                (newline)))\n             ((and (pair? ex) (eq? (car ex) 'user-error))\n              (display \"[error] \")\n              (display (pr-str (cdr ex) #t))\n              (newline)))\n         (display (rep input))\n         (newline))\n        (loop))))\n  (newline))\n\n(if (null? args)\n    (main)\n    (rep (string-append \"(load-file \\\"\" (car args) \"\\\")\")))\n"
  },
  {
    "path": "impls/scheme/tests/stepA_mal.mal",
    "content": ";; Testing basic Scheme interop\n\n(scm-eval \"(+ 1 1)\")\n;=>2\n\n(scm-eval \"(begin (display \\\"Hello World!\\\") (newline) 7)\")\n;/Hello World!\n;=>7\n\n(scm-eval \"(string->list \\\"MAL\\\")\")\n;=>(\"M\" \"A\" \"L\")\n\n(scm-eval \"(map + '(1 2 3) '(4 5 6))\")\n;=>(5 7 9)\n\n(scm-eval \"(string-map (lambda (c) (integer->char (+ 65 (modulo (+ (- (char->integer c) 65) 13) 26)))) \\\"ZNY\\\")\")\n;=>\"MAL\"\n"
  },
  {
    "path": "impls/skew/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install npm\n\nENV NPM_CONFIG_CACHE /mal/.npm\n\n# Skew\nRUN DEBIAN_FRONTEND=noninteractive npm install -g skew\n"
  },
  {
    "path": "impls/skew/Makefile",
    "content": "STEPS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do \\\n        step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\n\nSOURCES_BASE = util.sk types.sk reader.sk printer.sk\n\nSTEP3_DEPS = $(SOURCES_BASE) env.sk\nSTEP4_DEPS = $(STEP3_DEPS) core.sk\n\nall: $(foreach s,$(STEPS),$(s).js) dist\n\ndist: mal\n\nstep0_repl.js step1_read_print.js step2_eval.js step3_env.js: $(STEP3_DEPS)\nstep4_if_fn_do.js step5_tco.js step6_file.js step7_quote.js step8_macros.js step9_try.js stepA_mal.js: $(STEP4_DEPS)\n\n%.js: %.sk\n\tskewc --target=js --release --output-file=$@ $^\n\nmal: stepA_mal.js\n\techo \"#!/usr/bin/env node\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -rf step*.js mal\n\n.PHONY: all dist clean\n"
  },
  {
    "path": "impls/skew/core.sk",
    "content": "def _printLn(s string) MalVal {\n  printLn(s)\n  return gNil\n}\n\nconst ns StringMap<fn(List<MalVal>) MalVal> = {\n  \"eval\":  (a List<MalVal>) => EVAL(a[0], repl_env),\n  \"=\":     (a List<MalVal>) => MalVal.fromBool(a[0].equal(a[1])),\n  \"throw\": (a List<MalVal>) => { throw MalUserError.new(a[0]) },\n\n  \"nil?\":     (a List<MalVal>) => MalVal.fromBool(a[0] is MalNil),\n  \"true?\":    (a List<MalVal>) => MalVal.fromBool(a[0] is MalTrue),\n  \"false?\":   (a List<MalVal>) => MalVal.fromBool(a[0] is MalFalse),\n  \"string?\":  (a List<MalVal>) => MalVal.fromBool(a[0] is MalString),\n  \"symbol\":   (a List<MalVal>) => MalSymbol.new((a[0] as MalString).val),\n  \"symbol?\":  (a List<MalVal>) => MalVal.fromBool(a[0] is MalSymbol),\n  \"keyword\":  (a List<MalVal>) => a[0] is MalKeyword ? a[0] : MalKeyword.new((a[0] as MalString).val),\n  \"keyword?\": (a List<MalVal>) => MalVal.fromBool(a[0] is MalKeyword),\n  \"number?\":  (a List<MalVal>) => MalVal.fromBool(a[0] is MalNumber),\n  \"fn?\":      (a List<MalVal>) => MalVal.fromBool(a[0] is MalNativeFunc ||\n                                                  (a[0] is MalFunc && !(a[0] as MalFunc).isMacro)),\n  \"macro?\":   (a List<MalVal>) => MalVal.fromBool(a[0] is MalFunc && (a[0] as MalFunc).isMacro),\n\n  \"pr-str\":  (a List<MalVal>) => MalString.new(\" \".join(a.map<string>(e => pr_str(e, true)))),\n  \"str\":     (a List<MalVal>) => MalString.new(\"\".join(a.map<string>(e => pr_str(e, false)))),\n  \"prn\":     (a List<MalVal>) => _printLn(\" \".join(a.map<string>(e => pr_str(e, true)))),\n  \"println\": (a List<MalVal>) => _printLn(\" \".join(a.map<string>(e => pr_str(e, false)))),\n  \"read-string\": (a List<MalVal>) => read_str((a[0] as MalString).val),\n  \"readline\":    (a List<MalVal>) => {\n                   const line = readLine((a[0] as MalString).val)\n                   return line == null ? gNil : MalString.new(line)\n                 },\n  \"slurp\":   (a List<MalVal>) => MalString.new(readFile((a[0] as MalString).val)),\n\n  \"<\":  (a List<MalVal>) => MalVal.fromBool((a[0] as MalNumber).val < (a[1] as MalNumber).val),\n  \"<=\": (a List<MalVal>) => MalVal.fromBool((a[0] as MalNumber).val <= (a[1] as MalNumber).val),\n  \">\":  (a List<MalVal>) => MalVal.fromBool((a[0] as MalNumber).val > (a[1] as MalNumber).val),\n  \">=\": (a List<MalVal>) => MalVal.fromBool((a[0] as MalNumber).val >= (a[1] as MalNumber).val),\n  \"+\":  (a List<MalVal>) => MalNumber.new((a[0] as MalNumber).val + (a[1] as MalNumber).val),\n  \"-\":  (a List<MalVal>) => MalNumber.new((a[0] as MalNumber).val - (a[1] as MalNumber).val),\n  \"*\":  (a List<MalVal>) => MalNumber.new((a[0] as MalNumber).val * (a[1] as MalNumber).val),\n  \"/\":  (a List<MalVal>) => MalNumber.new((a[0] as MalNumber).val / (a[1] as MalNumber).val),\n  \"time-ms\": (a List<MalVal>) => MalNumber.new(timeMs),\n\n  \"list\":      (a List<MalVal>) => MalList.new(a),\n  \"list?\":     (a List<MalVal>) => MalVal.fromBool(a[0] is MalList),\n  \"vector\":    (a List<MalVal>) => MalVector.new(a),\n  \"vector?\":   (a List<MalVal>) => MalVal.fromBool(a[0] is MalVector),\n  \"hash-map\":  (a List<MalVal>) => MalHashMap.fromList(a),\n  \"map?\":      (a List<MalVal>) => MalVal.fromBool(a[0] is MalHashMap),\n  \"assoc\":     (a List<MalVal>) => (a[0] as MalHashMap).assoc(a.slice(1)),\n  \"dissoc\":    (a List<MalVal>) => (a[0] as MalHashMap).dissoc(a.slice(1)),\n  \"get\":       (a List<MalVal>) => a[0] is MalNil ? gNil : (a[0] as MalHashMap).get(a[1]),\n  \"contains?\": (a List<MalVal>) => MalVal.fromBool((a[0] as MalHashMap).contains(a[1])),\n  \"keys\":      (a List<MalVal>) => MalList.new((a[0] as MalHashMap).keys),\n  \"vals\":      (a List<MalVal>) => MalList.new((a[0] as MalHashMap).vals),\n\n  \"sequential?\": (a List<MalVal>) => MalVal.fromBool(a[0] is MalSequential),\n  \"cons\":   (a List<MalVal>) => {\n              var list List<MalVal> = (a[1] as MalSequential).val.clone\n              list.prepend(a[0])\n              return MalList.new(list)\n            },\n  \"concat\": (a List<MalVal>) => {\n              var list List<MalVal> = []\n              a.each(e => list.append((e as MalSequential).val))\n              return MalList.new(list)\n            },\n  \"vec\":    (a List<MalVal>) => a[0] is MalVector ? a[0] : MalVector.new((a[0] as MalSequential).val),\n  \"nth\":    (a List<MalVal>) => (a[0] as MalSequential).nth((a[1] as MalNumber).val),\n  \"first\":  (a List<MalVal>) => a[0] is MalNil ? gNil : (a[0] as MalSequential).first,\n  \"rest\":   (a List<MalVal>) => a[0] is MalNil ? MalList.new([]) : (a[0] as MalSequential).rest,\n  \"empty?\": (a List<MalVal>) => MalVal.fromBool((a[0] as MalSequential).count == 0),\n  \"count\":  (a List<MalVal>) => a[0] is MalNil ? MalNumber.new(0) : MalNumber.new((a[0] as MalSequential).count),\n  \"apply\":  (a List<MalVal>) => {\n              const f = a[0] as MalCallable\n              var args = a.slice(1, a.count - 1)\n              args.append((a[a.count - 1] as MalSequential).val)\n\t      return f.call(args)\n            },\n  \"map\":    (a List<MalVal>) => {\n              const f = a[0] as MalCallable\n              return MalList.new((a[1] as MalSequential).val.map<MalVal>(e => f.call([e])))\n            },\n\n  \"conj\": (a List<MalVal>) => (a[0] as MalSequential).conj(a.slice(1)),\n  \"seq\":  (a List<MalVal>) => a[0].seq,\n\n  \"meta\":      (a List<MalVal>) => a[0].meta,\n  \"with-meta\": (a List<MalVal>) => a[0].withMeta(a[1]),\n  \"atom\":      (a List<MalVal>) => MalAtom.new(a[0]),\n  \"atom?\":     (a List<MalVal>) => MalVal.fromBool(a[0] is MalAtom),\n  \"deref\":     (a List<MalVal>) => (a[0] as MalAtom).val,\n  \"reset!\":    (a List<MalVal>) => (a[0] as MalAtom).resetBang(a[1]),\n  \"swap!\":     (a List<MalVal>) => {\n                 var atom = a[0] as MalAtom\n                 const oldVal = atom.val\n                 var callArgs = a.slice(2)\n                 callArgs.prepend(oldVal)\n                 const newVal = (a[1] as MalCallable).call(callArgs)\n                 return atom.resetBang(newVal)\n               },\n}\n"
  },
  {
    "path": "impls/skew/env.sk",
    "content": "class Env {\n  const _outer Env\n  var _data StringMap<MalVal> = {}\n\n  def new(outer Env) {\n    _outer = outer\n  }\n\n  def new(outer Env, binds List<MalVal>, exprs List<MalVal>) {\n    _outer = outer\n    for i in 0..binds.count {\n      const name = (binds[i] as MalSymbol).val\n      if name == \"&\" {\n        const restName = (binds[i + 1] as MalSymbol).val\n        _data[restName] = MalList.new(exprs.slice(i))\n        break\n      } else {\n        _data[name] = exprs[i]\n      }\n    }\n  }\n\n  def get(key string) MalVal {\n    if key in _data { return _data[key] }\n    return _outer?.get(key)\n  }\n\n  def set(key MalSymbol, value MalVal) MalVal {\n    _data[key.val] = value\n    return value\n  }\n}\n"
  },
  {
    "path": "impls/skew/printer.sk",
    "content": "def pr_str(obj MalVal, readable bool) string {\n  return obj.print(readable)\n}\n"
  },
  {
    "path": "impls/skew/reader.sk",
    "content": "class Reader {\n  const tokens List<string>\n  var position = 0\n\n  def peek string {\n    if position >= tokens.count {\n      return null\n    }\n    return tokens[position]\n  }\n\n  def next string {\n    const token = peek\n    position++\n    return token\n  }\n}\n\ndef tokenize(str string) List<string> {\n  var re = RegExp.new(\"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}('\\\"`,;)]*)\", \"g\")\n  var tokens List<string> = []\n  var match string\n  while (match = re.exec(str)[1]) != \"\" {\n    if match[0] == ';' {\n      continue\n    }\n    tokens.append(match)\n  }\n  return tokens\n}\n\ndef unescape(s string) string {\n  return s.replaceAll(\"\\\\\\\\\", \"\\x01\").replaceAll(\"\\\\\\\"\", \"\\\"\").replaceAll(\"\\\\n\", \"\\n\").replaceAll(\"\\x01\", \"\\\\\")\n}\n\ndef read_atom(rdr Reader) MalVal {\n  var sre = RegExp.new(\"^\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\")\n  const token = rdr.peek\n  if token == \"nil\" {\n    rdr.next\n    return gNil\n  }\n  if token == \"true\" {\n    rdr.next\n    return gTrue\n  }\n  if token == \"false\" {\n    rdr.next\n    return gFalse\n  }\n  switch token[0] {\n    case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' { return MalNumber.new(stringToInt(rdr.next)) }\n    case '-' {\n      if token.count <= 1 { return MalSymbol.new(rdr.next) }\n      switch token[1] {\n        case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' { return MalNumber.new(stringToInt(rdr.next)) }\n        default { return MalSymbol.new(rdr.next) }\n      }\n    }\n    case '\"' {\n      const s = rdr.next\n      if sre.exec(s) {\n        return MalString.new(unescape(s.slice(1, s.count - 1)))\n      } else {\n        throw MalError.new(\"expected '\\\"', got EOF\")\n      }\n    }\n    case ':' { return MalKeyword.new(rdr.next.slice(1)) }\n    default { return MalSymbol.new(rdr.next) }\n  }\n}\n\ndef read_sequence(rdr Reader, open string, close string) List<MalVal> {\n  if rdr.next != open {\n    throw MalError.new(\"expected '\" + open + \"'\")\n  }\n  var token string\n  var items List<MalVal> = []\n  while (token = rdr.peek) != close {\n    if token == null {\n      throw MalError.new(\"expected '\" + close + \"', got EOF\")\n    }\n    items.append(read_form(rdr))\n  }\n  rdr.next # consume the close paren/bracket/brace\n  return items\n}\n\ndef read_list(rdr Reader) MalList {\n  return MalList.new(read_sequence(rdr, \"(\", \")\"))\n}\n\ndef read_vector(rdr Reader) MalVector {\n  return MalVector.new(read_sequence(rdr, \"[\", \"]\"))\n}\n\ndef read_hash_map(rdr Reader) MalHashMap {\n  return MalHashMap.fromList(read_sequence(rdr, \"{\", \"}\"))\n}\n\ndef reader_macro(rdr Reader, symbol_name string) MalVal {\n  rdr.next\n  return MalList.new([MalSymbol.new(symbol_name), read_form(rdr)])\n}\n\ndef read_form(rdr Reader) MalVal {\n  switch rdr.peek[0] {\n    case '\\'' { return reader_macro(rdr, \"quote\") }\n    case '`' { return reader_macro(rdr, \"quasiquote\") }\n    case '~' {\n      if rdr.peek == \"~\" { return reader_macro(rdr, \"unquote\") }\n      else if rdr.peek == \"~@\" { return reader_macro(rdr, \"splice-unquote\") }\n      else { return read_atom(rdr) }\n    }\n    case '^' {\n      rdr.next\n      const meta = read_form(rdr)\n      return MalList.new([MalSymbol.new(\"with-meta\"), read_form(rdr), meta])\n    }\n    case '@' { return reader_macro(rdr, \"deref\") }\n    case ')' { throw MalError.new(\"unexpected ')'\") }\n    case '(' { return read_list(rdr) }\n    case ']' { throw MalError.new(\"unexpected ']'\") }\n    case '[' { return read_vector(rdr) }\n    case '}' { throw MalError.new(\"unexpected '}'\") }\n    case '{' { return read_hash_map(rdr) }\n    default { return read_atom(rdr) }\n  }\n}\n\ndef read_str(str string) MalVal {\n  const tokens = tokenize(str)\n  if tokens.isEmpty { return null }\n  var rdr = Reader.new(tokens)\n  return read_form(rdr)\n}\n\n@import {\n  const RegExp dynamic\n}\n"
  },
  {
    "path": "impls/skew/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/${STEP:-stepA_mal}.js \"${@}\"\n"
  },
  {
    "path": "impls/skew/step0_repl.sk",
    "content": "def READ(str string) string {\n  return str\n}\n\ndef EVAL(ast string, env StringMap<string>) string {\n  return ast\n}\n\ndef PRINT(exp string) string {\n  return exp\n}\n\ndef REP(str string) string {\n  return PRINT(EVAL(READ(str), {}))\n}\n\n@entry\ndef main {\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    printLn(REP(line))\n  }\n}\n"
  },
  {
    "path": "impls/skew/step1_read_print.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef EVAL(ast MalVal, env StringMap<string>) MalVal {\n  return ast\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\ndef REP(str string) string {\n  return PRINT(EVAL(READ(str), {}))\n}\n\n@entry\ndef main {\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step2_eval.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef EVAL(ast MalVal, env StringMap<MalVal>) MalVal {\n  # printLn(\"EVAL: \" + PRINT(ast))\n\n  if ast is MalSymbol {\n    const name = (ast as MalSymbol).val\n    if !(name in env) {\n      throw MalError.new(\"'\" + name + \"' not found\")\n    }\n    return env[name]\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n  var astList = ast as MalList\n  if astList.isEmpty { return ast }\n  const evaledList = astList.val.map<MalVal>(e => EVAL(e, env))\n  var fn = evaledList[0] as MalNativeFunc\n  return fn.call(evaledList.slice(1))\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env StringMap<MalVal> = {\n  \"+\": MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val + (args[1] as MalNumber).val)),\n  \"-\": MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val - (args[1] as MalNumber).val)),\n  \"*\": MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val * (args[1] as MalNumber).val)),\n  \"/\": MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val / (args[1] as MalNumber).val)),\n}\n\ndef REP(str string) string {\n  return PRINT(EVAL(READ(str), repl_env))\n}\n\n@entry\ndef main {\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step3_env.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n  const astList = ast as MalList\n  if astList.isEmpty { return ast }\n  const a0sym = astList[0] as MalSymbol\n  if a0sym.val == \"def!\" {\n    return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n  } else if a0sym.val == \"let*\" {\n    var letenv = Env.new(env)\n    const assigns = astList[1] as MalSequential\n    for i = 0; i < assigns.count; i += 2 {\n      letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n    }\n    return EVAL(astList[2], letenv)\n  } else {\n    const evaledList = astList.val.map<MalVal>(e => EVAL(e, env))\n    const fn = evaledList[0] as MalNativeFunc\n    return fn.call(evaledList.slice(1))\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef REP(str string) string {\n  return PRINT(EVAL(READ(str), repl_env))\n}\n\n@entry\ndef main {\n  repl_env.set(MalSymbol.new(\"+\"), MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val + (args[1] as MalNumber).val)))\n  repl_env.set(MalSymbol.new(\"-\"), MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val - (args[1] as MalNumber).val)))\n  repl_env.set(MalSymbol.new(\"*\"), MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val * (args[1] as MalNumber).val)))\n  repl_env.set(MalSymbol.new(\"/\"), MalNativeFunc.new((args List<MalVal>) MalVal => MalNumber.new((args[0] as MalNumber).val / (args[1] as MalNumber).val)))\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step4_if_fn_do.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n  const astList = ast as MalList\n  if astList.isEmpty { return ast }\n  const a0sym = astList[0] as MalSymbol\n  if a0sym.val == \"def!\" {\n    return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n  } else if a0sym.val == \"let*\" {\n    var letenv = Env.new(env)\n    const assigns = astList[1] as MalSequential\n    for i = 0; i < assigns.count; i += 2 {\n      letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n    }\n    return EVAL(astList[2], letenv)\n  } else if a0sym.val == \"do\" {\n    for i = 1; i < astList.count - 1; i += 1 {\n       EVAL(astList[i], env)\n    }\n    return EVAL(astList[astList.count - 1], env)\n  } else if a0sym.val == \"if\" {\n    const condRes = EVAL(astList[1], env)\n    if condRes is MalNil || condRes is MalFalse {\n      return astList.count > 3 ? EVAL(astList[3], env) : gNil\n    } else {\n      return EVAL(astList[2], env)\n    }\n  } else if a0sym.val == \"fn*\" {\n    const argsNames = (astList[1] as MalSequential).val\n    return MalNativeFunc.new((args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames, args)))\n  } else {\n    const evaledList = astList.val.map<MalVal>(e => EVAL(e, env))\n    const fn = evaledList[0] as MalNativeFunc\n    return fn.call(evaledList.slice(1))\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step5_tco.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  while true {\n\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n    const astList = ast as MalList\n    if astList.isEmpty { return ast }\n    const a0sym = astList[0] as MalSymbol\n    if a0sym.val == \"def!\" {\n      return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n    } else if a0sym.val == \"let*\" {\n      var letenv = Env.new(env)\n      const assigns = astList[1] as MalSequential\n      for i = 0; i < assigns.count; i += 2 {\n        letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n      }\n      ast = astList[2]\n      env = letenv\n      continue # TCO\n    } else if a0sym.val == \"do\" {\n      for i = 1; i < astList.count - 1; i += 1 {\n         EVAL(astList[i], env)\n      }\n      ast = astList[astList.count - 1]\n      continue # TCO\n    } else if a0sym.val == \"if\" {\n      const condRes = EVAL(astList[1], env)\n      if condRes is MalNil || condRes is MalFalse {\n        ast = astList.count > 3 ? astList[3] : gNil\n      } else {\n        ast = astList[2]\n      }\n      continue # TCO\n    } else if a0sym.val == \"fn*\" {\n      const argsNames = astList[1] as MalSequential\n      return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args)))\n    } else {\n      const evaledList = astList.val.map<MalVal>(e => EVAL(e, env))\n      const fn = evaledList[0]\n      const callArgs = evaledList.slice(1)\n      if fn is MalNativeFunc {\n        return (fn as MalNativeFunc).call(callArgs)\n      } else if fn is MalFunc {\n        const f = fn as MalFunc\n        ast = f.ast\n        env = Env.new(f.env, f.params.val, callArgs)\n        continue # TCO\n      } else {\n        throw MalError.new(\"Expected function as head of list\")\n      }\n    }\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n    catch e Error {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step6_file.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  while true {\n\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n    const astList = ast as MalList\n    if astList.isEmpty { return ast }\n    const a0sym = astList[0] as MalSymbol\n    if a0sym.val == \"def!\" {\n      return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n    } else if a0sym.val == \"let*\" {\n      var letenv = Env.new(env)\n      const assigns = astList[1] as MalSequential\n      for i = 0; i < assigns.count; i += 2 {\n        letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n      }\n      ast = astList[2]\n      env = letenv\n      continue # TCO\n    } else if a0sym.val == \"do\" {\n      for i = 1; i < astList.count - 1; i += 1 {\n         EVAL(astList[i], env)\n      }\n      ast = astList[astList.count - 1]\n      continue # TCO\n    } else if a0sym.val == \"if\" {\n      const condRes = EVAL(astList[1], env)\n      if condRes is MalNil || condRes is MalFalse {\n        ast = astList.count > 3 ? astList[3] : gNil\n      } else {\n        ast = astList[2]\n      }\n      continue # TCO\n    } else if a0sym.val == \"fn*\" {\n      const argsNames = astList[1] as MalSequential\n      return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args)))\n    } else {\n      const evaledList = astList.val.map<MalVal>(e => EVAL(e, env))\n      const fn = evaledList[0]\n      const callArgs = evaledList.slice(1)\n      if fn is MalNativeFunc {\n        return (fn as MalNativeFunc).call(callArgs)\n      } else if fn is MalFunc {\n        const f = fn as MalFunc\n        ast = f.ast\n        env = Env.new(f.env, f.params.val, callArgs)\n        continue # TCO\n      } else {\n        throw MalError.new(\"Expected function as head of list\")\n      }\n    }\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n  repl_env.set(MalSymbol.new(\"*ARGV*\"), MalList.new(argv.isEmpty ? [] : argv.slice(1).map<MalVal>(e => MalString.new(e))))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n  RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n  if argv.count > 0 {\n    RE(\"(load-file \\\"\" + argv[0] + \"\\\")\")\n    return\n  }\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n    catch e Error {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step7_quote.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef starts_with(lst MalList, sym string) bool {\n  return lst.count == 2 && lst[0].isSymbol(sym)\n}\ndef qq_loop(elt MalVal, acc MalList) MalList {\n  if elt is MalList && starts_with(elt as MalList, \"splice-unquote\") {\n    return MalList.new([MalSymbol.new(\"concat\"), (elt as MalList)[1], acc])\n  } else {\n    return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n  }\n}\ndef qq_foldr(xs List<MalVal>) MalList {\n  var acc = MalList.new([])\n  for i = xs.count-1; 0 <= i; i -= 1 {\n    acc = qq_loop(xs[i], acc)\n  }\n  return acc\n}\ndef quasiquote(ast MalVal) MalVal {\n  if ast is MalVector {\n    return MalList.new([MalSymbol.new(\"vec\"), qq_foldr((ast as MalVector).val)])\n  } else if ast is MalSymbol || ast is MalHashMap {\n    return MalList.new([MalSymbol.new(\"quote\"), ast])\n  } else if !(ast is MalList) {\n    return ast\n  } else if starts_with(ast as MalList, \"unquote\") {\n    return (ast as MalList)[1]\n  } else {\n    return qq_foldr((ast as MalList).val)\n  }\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  while true {\n\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n    const astList = ast as MalList\n    if astList.isEmpty { return ast }\n    const a0sym = astList[0] as MalSymbol\n    if a0sym.val == \"def!\" {\n      return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n    } else if a0sym.val == \"let*\" {\n      var letenv = Env.new(env)\n      const assigns = astList[1] as MalSequential\n      for i = 0; i < assigns.count; i += 2 {\n        letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n      }\n      ast = astList[2]\n      env = letenv\n      continue # TCO\n    } else if a0sym.val == \"quote\" {\n      return astList[1]\n    } else if a0sym.val == \"quasiquote\" {\n      ast = quasiquote(astList[1])\n      continue # TCO\n    } else if a0sym.val == \"do\" {\n      for i = 1; i < astList.count - 1; i += 1 {\n         EVAL(astList[i], env)\n      }\n      ast = astList[astList.count - 1]\n      continue # TCO\n    } else if a0sym.val == \"if\" {\n      const condRes = EVAL(astList[1], env)\n      if condRes is MalNil || condRes is MalFalse {\n        ast = astList.count > 3 ? astList[3] : gNil\n      } else {\n        ast = astList[2]\n      }\n      continue # TCO\n    } else if a0sym.val == \"fn*\" {\n      const argsNames = astList[1] as MalSequential\n      return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args)))\n    } else {\n      const evaledList = astList.val.map<MalVal>(e => EVAL(e, env))\n      const fn = evaledList[0]\n      const callArgs = evaledList.slice(1)\n      if fn is MalNativeFunc {\n        return (fn as MalNativeFunc).call(callArgs)\n      } else if fn is MalFunc {\n        const f = fn as MalFunc\n        ast = f.ast\n        env = Env.new(f.env, f.params.val, callArgs)\n        continue # TCO\n      } else {\n        throw MalError.new(\"Expected function as head of list\")\n      }\n    }\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n  repl_env.set(MalSymbol.new(\"*ARGV*\"), MalList.new(argv.isEmpty ? [] : argv.slice(1).map<MalVal>(e => MalString.new(e))))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n  RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n  if argv.count > 0 {\n    RE(\"(load-file \\\"\" + argv[0] + \"\\\")\")\n    return\n  }\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n    catch e Error {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step8_macros.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef starts_with(lst MalList, sym string) bool {\n  return lst.count == 2 && lst[0].isSymbol(sym)\n}\ndef qq_loop(elt MalVal, acc MalList) MalList {\n  if elt is MalList && starts_with(elt as MalList, \"splice-unquote\") {\n    return MalList.new([MalSymbol.new(\"concat\"), (elt as MalList)[1], acc])\n  } else {\n    return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n  }\n}\ndef qq_foldr(xs List<MalVal>) MalList {\n  var acc = MalList.new([])\n  for i = xs.count-1; 0 <= i; i -= 1 {\n    acc = qq_loop(xs[i], acc)\n  }\n  return acc\n}\ndef quasiquote(ast MalVal) MalVal {\n  if ast is MalVector {\n    return MalList.new([MalSymbol.new(\"vec\"), qq_foldr((ast as MalVector).val)])\n  } else if ast is MalSymbol || ast is MalHashMap {\n    return MalList.new([MalSymbol.new(\"quote\"), ast])\n  } else if !(ast is MalList) {\n    return ast\n  } else if starts_with(ast as MalList, \"unquote\") {\n    return (ast as MalList)[1]\n  } else {\n    return qq_foldr((ast as MalList).val)\n  }\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  while true {\n\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n    const astList = ast as MalList\n    if astList.isEmpty { return ast }\n    const a0sym = astList[0] as MalSymbol\n    if a0sym.val == \"def!\" {\n      return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n    } else if a0sym.val == \"let*\" {\n      var letenv = Env.new(env)\n      const assigns = astList[1] as MalSequential\n      for i = 0; i < assigns.count; i += 2 {\n        letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n      }\n      ast = astList[2]\n      env = letenv\n      continue # TCO\n    } else if a0sym.val == \"quote\" {\n      return astList[1]\n    } else if a0sym.val == \"quasiquote\" {\n      ast = quasiquote(astList[1])\n      continue # TCO\n    } else if a0sym.val == \"defmacro!\" {\n      var fn = EVAL(astList[2], env) as MalFunc\n      var macro = MalFunc.new(fn.ast, fn.params, fn.env, fn.func)\n      macro.setAsMacro\n      return env.set(astList[1] as MalSymbol, macro)\n    } else if a0sym.val == \"do\" {\n      for i = 1; i < astList.count - 1; i += 1 {\n         EVAL(astList[i], env)\n      }\n      ast = astList[astList.count - 1]\n      continue # TCO\n    } else if a0sym.val == \"if\" {\n      const condRes = EVAL(astList[1], env)\n      if condRes is MalNil || condRes is MalFalse {\n        ast = astList.count > 3 ? astList[3] : gNil\n      } else {\n        ast = astList[2]\n      }\n      continue # TCO\n    } else if a0sym.val == \"fn*\" {\n      const argsNames = astList[1] as MalSequential\n      return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args)))\n    } else {\n      const fn = EVAL(astList[0], env)\n      const args = astList.val.slice(1)\n      if fn is MalFunc && (fn as MalFunc).isMacro {\n        ast = (fn as MalFunc).call(args)\n        continue # TCO\n      }\n      const callArgs = args.map<MalVal>(e => EVAL(e, env))\n      if fn is MalNativeFunc {\n        return (fn as MalNativeFunc).call(callArgs)\n      } else if fn is MalFunc {\n        const f = fn as MalFunc\n        ast = f.ast\n        env = Env.new(f.env, f.params.val, callArgs)\n        continue # TCO\n      } else {\n        throw MalError.new(\"Expected function as head of list\")\n      }\n    }\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n  repl_env.set(MalSymbol.new(\"*ARGV*\"), MalList.new(argv.isEmpty ? [] : argv.slice(1).map<MalVal>(e => MalString.new(e))))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n  RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n  RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n  if argv.count > 0 {\n    RE(\"(load-file \\\"\" + argv[0] + \"\\\")\")\n    return\n  }\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n    catch e Error {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/step9_try.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef starts_with(lst MalList, sym string) bool {\n  return lst.count == 2 && lst[0].isSymbol(sym)\n}\ndef qq_loop(elt MalVal, acc MalList) MalList {\n  if elt is MalList && starts_with(elt as MalList, \"splice-unquote\") {\n    return MalList.new([MalSymbol.new(\"concat\"), (elt as MalList)[1], acc])\n  } else {\n    return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n  }\n}\ndef qq_foldr(xs List<MalVal>) MalList {\n  var acc = MalList.new([])\n  for i = xs.count-1; 0 <= i; i -= 1 {\n    acc = qq_loop(xs[i], acc)\n  }\n  return acc\n}\ndef quasiquote(ast MalVal) MalVal {\n  if ast is MalVector {\n    return MalList.new([MalSymbol.new(\"vec\"), qq_foldr((ast as MalVector).val)])\n  } else if ast is MalSymbol || ast is MalHashMap {\n    return MalList.new([MalSymbol.new(\"quote\"), ast])\n  } else if !(ast is MalList) {\n    return ast\n  } else if starts_with(ast as MalList, \"unquote\") {\n    return (ast as MalList)[1]\n  } else {\n    return qq_foldr((ast as MalList).val)\n  }\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  while true {\n\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n    const astList = ast as MalList\n    if astList.isEmpty { return ast }\n    const a0sym = astList[0] as MalSymbol\n    if a0sym.val == \"def!\" {\n      return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n    } else if a0sym.val == \"let*\" {\n      var letenv = Env.new(env)\n      const assigns = astList[1] as MalSequential\n      for i = 0; i < assigns.count; i += 2 {\n        letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n      }\n      ast = astList[2]\n      env = letenv\n      continue # TCO\n    } else if a0sym.val == \"quote\" {\n      return astList[1]\n    } else if a0sym.val == \"quasiquote\" {\n      ast = quasiquote(astList[1])\n      continue # TCO\n    } else if a0sym.val == \"defmacro!\" {\n      var fn = EVAL(astList[2], env) as MalFunc\n      var macro = MalFunc.new(fn.ast, fn.params, fn.env, fn.func)\n      macro.setAsMacro\n      return env.set(astList[1] as MalSymbol, macro)\n    } else if a0sym.val == \"try*\" {\n      if astList.count < 3 {\n        return EVAL(astList[1], env)\n      }\n      var exc MalVal\n      try {\n        return EVAL(astList[1], env)\n      }\n      catch e MalUserError { exc = e.data }\n      catch e MalError { exc = MalString.new(e.message) }\n      catch e Error { exc = MalString.new(e.message) }\n      const catchClause = astList[2] as MalList\n      var catchEnv = Env.new(env, [catchClause[1] as MalSymbol], [exc])\n      return EVAL(catchClause[2], catchEnv)\n    } else if a0sym.val == \"do\" {\n      for i = 1; i < astList.count - 1; i += 1 {\n         EVAL(astList[i], env)\n      }\n      ast = astList[astList.count - 1]\n      continue # TCO\n    } else if a0sym.val == \"if\" {\n      const condRes = EVAL(astList[1], env)\n      if condRes is MalNil || condRes is MalFalse {\n        ast = astList.count > 3 ? astList[3] : gNil\n      } else {\n        ast = astList[2]\n      }\n      continue # TCO\n    } else if a0sym.val == \"fn*\" {\n      const argsNames = astList[1] as MalSequential\n      return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args)))\n    } else {\n      const fn = EVAL(astList[0], env)\n      const args = astList.val.slice(1)\n      if fn is MalFunc && (fn as MalFunc).isMacro {\n        ast = (fn as MalFunc).call(args)\n        continue # TCO\n      }\n      const callArgs = args.map<MalVal>(e => EVAL(e, env))\n      if fn is MalNativeFunc {\n        return (fn as MalNativeFunc).call(callArgs)\n      } else if fn is MalFunc {\n        const f = fn as MalFunc\n        ast = f.ast\n        env = Env.new(f.env, f.params.val, callArgs)\n        continue # TCO\n      } else {\n        throw MalError.new(\"Expected function as head of list\")\n      }\n    }\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n  repl_env.set(MalSymbol.new(\"*ARGV*\"), MalList.new(argv.isEmpty ? [] : argv.slice(1).map<MalVal>(e => MalString.new(e))))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n  RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n  RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n  if argv.count > 0 {\n    RE(\"(load-file \\\"\" + argv[0] + \"\\\")\")\n    return\n  }\n\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalUserError {\n      printLn(\"Error: \\(e.data.print(false))\")\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n    catch e Error {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/stepA_mal.sk",
    "content": "def READ(str string) MalVal {\n  return read_str(str)\n}\n\ndef starts_with(lst MalList, sym string) bool {\n  return lst.count == 2 && lst[0].isSymbol(sym)\n}\ndef qq_loop(elt MalVal, acc MalList) MalList {\n  if elt is MalList && starts_with(elt as MalList, \"splice-unquote\") {\n    return MalList.new([MalSymbol.new(\"concat\"), (elt as MalList)[1], acc])\n  } else {\n    return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n  }\n}\ndef qq_foldr(xs List<MalVal>) MalList {\n  var acc = MalList.new([])\n  for i = xs.count-1; 0 <= i; i -= 1 {\n    acc = qq_loop(xs[i], acc)\n  }\n  return acc\n}\ndef quasiquote(ast MalVal) MalVal {\n  if ast is MalVector {\n    return MalList.new([MalSymbol.new(\"vec\"), qq_foldr((ast as MalVector).val)])\n  } else if ast is MalSymbol || ast is MalHashMap {\n    return MalList.new([MalSymbol.new(\"quote\"), ast])\n  } else if !(ast is MalList) {\n    return ast\n  } else if starts_with(ast as MalList, \"unquote\") {\n    return (ast as MalList)[1]\n  } else {\n    return qq_foldr((ast as MalList).val)\n  }\n}\n\ndef EVAL(ast MalVal, env Env) MalVal {\n  while true {\n\n  const dbgeval = env.get(\"DEBUG-EVAL\")\n  if dbgeval != null && !(dbgeval is MalNil) && !(dbgeval is MalFalse) {\n    printLn(\"EVAL: \" + PRINT(ast))\n  }\n\n  if ast is MalSymbol {\n    const key = (ast as MalSymbol).val\n    const val = env.get(key)\n    if val == null { throw MalError.new(\"'\" + key + \"' not found\") }\n    return val\n  } else if ast is MalList {\n    # proceed further after this conditional\n  } else if ast is MalVector {\n    return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))\n  } else if ast is MalHashMap {\n    var result List<MalVal> = []\n    (ast as MalHashMap).val.each((k string, v MalVal) => {\n      result.append(MalVal.fromHashKey(k))\n      result.append(EVAL(v, env))\n    })\n    return MalHashMap.fromList(result)\n  } else {\n    return ast\n  }\n\n    const astList = ast as MalList\n    if astList.isEmpty { return ast }\n    const a0sym = astList[0] as MalSymbol\n    if a0sym.val == \"def!\" {\n      return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))\n    } else if a0sym.val == \"let*\" {\n      var letenv = Env.new(env)\n      const assigns = astList[1] as MalSequential\n      for i = 0; i < assigns.count; i += 2 {\n        letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))\n      }\n      ast = astList[2]\n      env = letenv\n      continue # TCO\n    } else if a0sym.val == \"quote\" {\n      return astList[1]\n    } else if a0sym.val == \"quasiquote\" {\n      ast = quasiquote(astList[1])\n      continue # TCO\n    } else if a0sym.val == \"defmacro!\" {\n      var fn = EVAL(astList[2], env) as MalFunc\n      var macro = MalFunc.new(fn.ast, fn.params, fn.env, fn.func)\n      macro.setAsMacro\n      return env.set(astList[1] as MalSymbol, macro)\n    } else if a0sym.val == \"try*\" {\n      if astList.count < 3 {\n        return EVAL(astList[1], env)\n      }\n      var exc MalVal\n      try {\n        return EVAL(astList[1], env)\n      }\n      catch e MalUserError { exc = e.data }\n      catch e MalError { exc = MalString.new(e.message) }\n      catch e Error { exc = MalString.new(e.message) }\n      const catchClause = astList[2] as MalList\n      var catchEnv = Env.new(env, [catchClause[1] as MalSymbol], [exc])\n      return EVAL(catchClause[2], catchEnv)\n    } else if a0sym.val == \"do\" {\n      for i = 1; i < astList.count - 1; i += 1 {\n         EVAL(astList[i], env)\n      }\n      ast = astList[astList.count - 1]\n      continue # TCO\n    } else if a0sym.val == \"if\" {\n      const condRes = EVAL(astList[1], env)\n      if condRes is MalNil || condRes is MalFalse {\n        ast = astList.count > 3 ? astList[3] : gNil\n      } else {\n        ast = astList[2]\n      }\n      continue # TCO\n    } else if a0sym.val == \"fn*\" {\n      const argsNames = astList[1] as MalSequential\n      return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args)))\n    } else {\n      const fn = EVAL(astList[0], env)\n      const args = astList.val.slice(1)\n      if fn is MalFunc && (fn as MalFunc).isMacro {\n        ast = (fn as MalFunc).call(args)\n        continue # TCO\n      }\n      const callArgs = args.map<MalVal>(e => EVAL(e, env))\n      if fn is MalNativeFunc {\n        return (fn as MalNativeFunc).call(callArgs)\n      } else if fn is MalFunc {\n        const f = fn as MalFunc\n        ast = f.ast\n        env = Env.new(f.env, f.params.val, callArgs)\n        continue # TCO\n      } else {\n        throw MalError.new(\"Expected function as head of list\")\n      }\n    }\n  }\n}\n\ndef PRINT(exp MalVal) string {\n  return exp?.print(true)\n}\n\nvar repl_env = Env.new(null)\n\ndef RE(str string) MalVal {\n  return EVAL(READ(str), repl_env)\n}\n\ndef REP(str string) string {\n  return PRINT(RE(str))\n}\n\n@entry\ndef main {\n  # core.sk: defined using Skew\n  ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))\n  repl_env.set(MalSymbol.new(\"*ARGV*\"), MalList.new(argv.isEmpty ? [] : argv.slice(1).map<MalVal>(e => MalString.new(e))))\n\n  # core.mal: defined using the language itself\n  RE(\"(def! *host-language* \\\"skew\\\")\")\n  RE(\"(def! not (fn* (a) (if a false true)))\")\n  RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n  RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n  if argv.count > 0 {\n    RE(\"(load-file \\\"\" + argv[0] + \"\\\")\")\n    return\n  }\n\n  RE(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n  var line string\n  while (line = readLine(\"user> \")) != null {\n    if line == \"\" { continue }\n    try {\n      printLn(REP(line))\n    }\n    catch e MalUserError {\n      printLn(\"Error: \\(e.data.print(false))\")\n    }\n    catch e MalError {\n      printLn(\"Error: \\(e.message)\")\n    }\n    catch e Error {\n      printLn(\"Error: \\(e.message)\")\n    }\n  }\n}\n"
  },
  {
    "path": "impls/skew/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/skew/types.sk",
    "content": "class MalError {\n  const message string\n}\n\nclass MalUserError {\n  const data MalVal\n}\n\nclass MalVal {\n  var _meta MalVal = gNil\n  def toHashKey string { throw MalError.new(\"Not allowed as hash map key\") }\n  def print(readable bool) string\n  def equal(o MalVal) bool\n  def isSymbol(name string) bool { return false }\n  def seq MalVal { throw MalError.new(\"seq: called on non-sequence\") }\n  def meta MalVal { return _meta }\n  def _setMeta(newMeta MalVal) { _meta = newMeta }\n  def withMeta(newMeta MalVal) MalVal {\n    var res = self.clone\n    res._setMeta(newMeta)\n    return res\n  }\n  def clone MalVal\n}\n\nnamespace MalVal {\n  def fromHashKey(key string) MalVal {\n    if key.startsWith(\"S_\") { return MalString.new(key.slice(2)) }\n    else if key.startsWith(\"K_\") { return MalKeyword.new(key.slice(2)) }\n    else { throw \"Illegal hash key string\" }\n  }\n  def fromBool(b bool) MalVal { return b ? gTrue : gFalse }\n}\n\nclass MalNil : MalVal {\n  over print(readable bool) string { return \"nil\" }\n  over equal(o MalVal) bool { return o is MalNil }\n  over seq MalVal { return gNil }\n  over clone MalVal { return self }\n}\nconst gNil = MalNil.new\n\nclass MalTrue : MalVal {\n  over print(readable bool) string { return \"true\" }\n  over equal(o MalVal) bool { return o is MalTrue }\n  over clone MalVal { return self }\n}\nconst gTrue = MalTrue.new\n\nclass MalFalse : MalVal {\n  over print(readable bool) string { return \"false\" }\n  over equal(o MalVal) bool { return o is MalFalse }\n  over clone MalVal { return self }\n}\nconst gFalse = MalFalse.new\n\nclass MalNumber : MalVal {\n  const _data int\n  over print(readable bool) string { return _data.toString }\n  def val int { return _data }\n  over equal(o MalVal) bool { return o is MalNumber && (o as MalNumber).val == val }\n  over clone MalVal { return self }\n}\n\nclass MalSymbol : MalVal {\n  const _data string\n  over print(readable bool) string { return _data }\n  def val string { return _data }\n  over equal(o MalVal) bool { return o is MalSymbol && (o as MalSymbol).val == val }\n  over isSymbol(name string) bool { return _data == name }\n  over clone MalVal { return MalSymbol.new(_data) }\n}\n\nclass MalString : MalVal {\n  const _data string\n  over print(readable bool) string { return readable ? \"\\\"\\(escaped_data)\\\"\" : _data }\n  over toHashKey string { return \"S_\\(_data)\" }\n  def val string { return _data }\n  over equal(o MalVal) bool { return o is MalString && (o as MalString).val == val }\n  def escaped_data string {\n    return _data.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\"\\\"\", \"\\\\\\\"\").replaceAll(\"\\n\", \"\\\\n\")\n  }\n  over seq MalVal { return _data.count == 0 ? gNil : MalList.new(_data.split(\"\").map<MalVal>(e => MalString.new(e))) }\n  over clone MalVal { return MalString.new(_data) }\n}\n\nclass MalKeyword : MalVal {\n  const _data string\n  over print(readable bool) string { return \":\\(_data)\" }\n  over toHashKey string { return \"K_\\(_data)\" }\n  def val string { return _data }\n  over equal(o MalVal) bool { return o is MalKeyword && (o as MalKeyword).val == val }\n  over clone MalVal { return MalKeyword.new(_data) }\n}\n\nclass MalSequential : MalVal {\n  const _data List<MalVal>\n  def val List<MalVal> { return _data }\n  def isEmpty bool { return _data.isEmpty }\n  def asOneString(readable bool) string {\n    return \" \".join(_data.map<string>(v => v.print(readable)))\n  }\n  def count int { return _data.count }\n  def [](index int) MalVal { return _data[index] }\n  over equal(o MalVal) bool {\n    if !(o is MalSequential) { return false }\n    const oval = (o as MalSequential).val\n    if val.count != oval.count { return false }\n    for i in 0..val.count {\n      if !val[i].equal(oval[i]) { return false }\n    }\n    return true\n  }\n  def nth(position int) MalVal {\n    if position >= count { throw MalError.new(\"nth: index out of range\") }\n    return val[position]\n  }\n  def first MalVal {\n    if isEmpty { return gNil }\n    return val[0]\n  }\n  def rest MalVal {\n    if isEmpty { return MalList.new([]) }\n    return MalList.new(val.slice(1))\n  }\n  def conj(args List<MalVal>) MalVal\n}\n\nclass MalList : MalSequential {\n  over print(readable bool) string { return \"(\" + asOneString(readable) + \")\" }\n  over seq MalVal { return isEmpty ? gNil : self }\n  over conj(args List<MalVal>) MalVal {\n    var res = args.clone\n    res.reverse\n    res.append(_data)\n    return MalList.new(res)\n  }\n  over clone MalVal { return MalList.new(_data) }\n}\n\nclass MalVector : MalSequential {\n  over print(readable bool) string { return \"[\" + asOneString(readable) + \"]\" }\n  over seq MalVal { return isEmpty ? gNil : MalList.new(_data) }\n  over conj(args List<MalVal>) MalVal {\n    var res = _data.clone\n    res.append(args)\n    return MalVector.new(res)\n  }\n  over clone MalVal { return MalVector.new(_data) }\n}\n\nclass MalHashMap : MalVal {\n  const _data StringMap<MalVal>\n  over print(readable bool) string {\n    var pairs List<string> = []\n    _data.each((k string, v MalVal) => pairs.append(\"\\(MalVal.fromHashKey(k).print(readable)) \\(v.print(readable))\"))\n    return \"{\" + \" \".join(pairs) + \"}\"\n  }\n  def val StringMap<MalVal> { return _data }\n  over equal(o MalVal) bool {\n    if !(o is MalHashMap) { return false }\n    const oh = o as MalHashMap\n    if oh.val.count != val.count { return false }\n    var allEqual = true\n    _data.each((k string, v MalVal) => {\n      if !(k in oh.val) || !(v.equal(oh.val[k])) {\n        allEqual = false\n      }\n    })\n    return allEqual\n  }\n  def assoc(kv_list List<MalVal>) MalVal {\n    var new_data = _data.clone\n    for i = 0; i < kv_list.count; i += 2 {\n      new_data[kv_list[i].toHashKey] = kv_list[i + 1]\n    }\n    return MalHashMap.new(new_data)\n  }\n  def dissoc(keys List<MalVal>) MalVal {\n    var new_data = _data.clone\n    for key in keys {\n      new_data.remove(key.toHashKey)\n    }\n    return MalHashMap.new(new_data)\n  }\n  def get(key MalVal) MalVal { return _data.get(key.toHashKey, gNil) }\n  def contains(key MalVal) bool { return key.toHashKey in _data }\n  def keys List<MalVal> {\n    return _data.keys.map<MalVal>(k => MalVal.fromHashKey(k))\n  }\n  def vals List<MalVal> { return _data.values }\n  over clone MalVal { return MalHashMap.new(_data) }\n}\n\nnamespace MalHashMap {\n  def fromList(kv_list List<MalVal>) MalHashMap {\n    var result StringMap<MalVal> = {}\n    for i = 0; i < kv_list.count; i += 2 {\n      result[kv_list[i].toHashKey] = kv_list[i + 1]\n    }\n    return MalHashMap.new(result)\n  }\n}\n\nclass MalCallable : MalVal {\n  const func fn(List<MalVal>) MalVal\n  def call(args List<MalVal>) MalVal {\n    return func(args)\n  }\n}\n\nclass MalNativeFunc : MalCallable {\n  over print(readable bool) string { return \"#<NativeFunction>\" }\n  over equal(o MalVal) bool { return false }\n  over clone MalVal { return MalNativeFunc.new(func) }\n}\n\nclass MalFunc : MalCallable {\n  const ast MalVal\n  const params MalSequential\n  const env Env\n  var _macro bool = false\n  def new(aAst MalVal, aParams MalSequential, aEnv Env, aFunc fn(List<MalVal>) MalVal) {\n    super(aFunc)\n    ast = aAst\n    params = aParams\n    env = aEnv\n  }\n  def isMacro bool { return _macro }\n  def setAsMacro { _macro = true }\n  over print(readable bool) string { return \"#<Function args=\" + params.print(true) + \">\" }\n  over equal(o MalVal) bool { return false }\n  over clone MalVal {\n    var f = MalFunc.new(ast, params, env, func)\n    if isMacro { f.setAsMacro }\n    return f\n  }\n}\n\nclass MalAtom : MalVal {\n  var _data MalVal\n  over print(readable bool) string { return \"(atom \\(_data.print(readable)))\" }\n  def val MalVal { return _data }\n  over equal(o MalVal) bool { return o is MalAtom && val.equal((o as MalAtom).val) }\n  def resetBang(newData MalVal) MalVal {\n    _data = newData\n    return _data\n  }\n  over clone MalVal { return MalAtom.new(_data) }\n}\n"
  },
  {
    "path": "impls/skew/util.sk",
    "content": "def argv List<string> {\n  return process.argv.slice(2)\n}\n\ndef timeMs int {\n  return Date.new.getTime()\n}\n\nvar fs = require(\"fs\")\n\ndef readFile(filename string) string {\n  return fs.readFileSync(filename, \"utf-8\")\n}\n\ndef writeString(s string) {\n  fs.writeSync(1, s)\n}\n\ndef printLn(s string) {\n  writeString(s)\n  writeString(\"\\n\")\n}\n\ndef readLine(prompt string) string {\n  writeString(prompt)\n  var buffer = Buffer.new(1024) # in newer Node this should be Buffer.alloc\n  var stdin = fs.openSync(\"/dev/stdin\", \"rs\")\n  var bytesread int\n  var anycharseen = false\n  var total = 0\n  while (bytesread = fs.readSync(stdin, buffer, total, 1)) > 0 {\n    anycharseen = true\n    var lastchar = buffer.slice(total, total + bytesread).toString()\n    if lastchar == \"\\n\" {\n      break\n    }\n    total += bytesread\n  }\n  fs.closeSync(stdin)\n  return anycharseen ? buffer.slice(0, total).toString() : null\n}\n\ndef stringToInt(str string) int {\n  return parseInt(str)\n}\n\n@import {\n  const process dynamic\n  const Buffer dynamic\n  const Date dynamic\n  const Error dynamic\n\n  def parseInt(str string) int\n  def require(name string) dynamic\n}\n"
  },
  {
    "path": "impls/sml/.gitignore",
    "content": ".smlmode\n.step*\n*.ui\n*.uo\n"
  },
  {
    "path": "impls/sml/Dockerfile",
    "content": "# We need focal for the Moscow ML PPA\nFROM ubuntu:focal\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\nRUN apt-get -y install make python3\nRUN ln -s /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install software-properties-common\n\nRUN apt-get -y install polyml libpolyml-dev\n\nRUN apt-get -y install mlton\n\nRUN add-apt-repository -y ppa:kflarsen/mosml\nRUN apt-get -y install mosml\n"
  },
  {
    "path": "impls/sml/LargeInt.sml",
    "content": "(* Moscow ML does not have the LargeInt structure,\n * but its Int is 64 bit on 64 bit systems.\n * We need 64 bit integers for the `time-ms` core function.\n *)\n\nstructure LargeInt = Int\n"
  },
  {
    "path": "impls/sml/Makefile",
    "content": "STEP_BINS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do step6_file step7_quote step8_macros step9_try stepA_mal\n\nsml_MODE_DEFAULT = polyml\nsml_MODE_CONFIG  = .smlmode\n\nifeq ($(sml_MODE),)\nsml_MODE = $(sml_MODE_DEFAULT)\nendif\n# some hackery to let Make know if it needs to rebuild when sml_MODE changes\nifneq ($(sml_MODE),$(shell cat $(sml_MODE_CONFIG) 2> /dev/null))\n$(shell rm $(sml_MODE_CONFIG) 2> /dev/null)\nendif\n\nifeq ($(sml_MODE),mlton)\nSMLC        = mlton\nSMLCOUTFLAG = -output\nBUILD_FILE  = %.mlb\nbuild_args  = $1\nendif\nifeq ($(sml_MODE),mosml)\nSMLC        = mosmlc\nSMLCOUTFLAG = -o\nBUILD_FILE  = %.mlb\nbuild_args  = LargeInt.sml -toplevel $(shell grep \"\\\\.sml\" $1)\nendif\nifeq ($(sml_MODE),polyml)\nSMLC        = polyc\nSMLCOUTFLAG = -o\nBUILD_FILE  = .%.poly.sml\nbuild_args  = $1\nendif\n\nall: $(STEP_BINS)\n\ndist: mal\n\nmal: stepA_mal\n\tcp $< $@\n\n.%.dep: %.mlb\n\t@echo sml-deps -o $@ $<\n\t$(eval DEPS := $(shell grep \"\\\\.sml\" $<))\n\t@echo \"$(@:.%.dep=%) $@: $(DEPS)\" > $@\n\ninclude $(STEP_BINS:%=.%.dep)\n\n.%.poly.sml: %.mlb\n\t@echo generate-sml -o $@ $<\n\t@grep \"\\\\.sml\" $< | grep -v main | xargs printf \"use \\\"%s\\\";\\n\" > $@\n\n# some hackery to let Make track changes in sml_MODE\n$(sml_MODE_CONFIG):\n\t@echo $(sml_MODE) > $@\n\n$(STEP_BINS): %: $(BUILD_FILE) $(sml_MODE_CONFIG)\n\t$(SMLC) $(SMLCOUTFLAG) $@ $(call build_args,$<)\n\nclean:\n\trm -f $(STEP_BINS) .*.dep *.ui *.uo .*.poly.sml $(sml_MODE_CONFIG)\n\n.PHONY: all clean\n"
  },
  {
    "path": "impls/sml/README.md",
    "content": "# SML-MAL\n\nThis is Make-A-Lisp in Standard ML.\n\n## Building\n\nJust run `make`.\n\nBuilding requires a Standard ML compiler with basis library. This MAL\nimplementation has been tested and works with Poly/ML, MLton, and Moscow ML.\n\nOn Ubuntu, you can run `apt-get install polyml libpolyml-dev`.\n\nBy setting `sml_MODE` to `polyml`, `mosml`, or `mlton` on invoking `make` you\ncan select which compiler to use. The Makefile has some hacks to figure out\nhow to make the different compilers build everything.\n\n## Running\n\nYou can build a `mal` binary from the final step with `make dist`:\n\n```\n$ make dist\n$ ./mal\nMal [sml]\nuser> (map (fn* (x) (println \"Odelay!\")) [1 2 3 4 5])\nOdelay!\nOdelay!\nOdelay!\nOdelay!\nOdelay!\n(nil nil nil nil nil)\nuser> \n```\n"
  },
  {
    "path": "impls/sml/core.sml",
    "content": "exception NotDefined of string\nexception NotApplicable of string\nexception OutOfBounds of string\nexception MalException of mal_type\n\n(*\n * Some helper functions\n *)\n\nfun buildMap (k::v::rest) acc = buildMap rest (malAssoc acc k v) \n  | buildMap []           acc = malMap (rev acc)\n  | buildMap _ _ = raise NotApplicable \"maps can only be constructed from an even number of arguments\"\n\nfun collectLists ls = collectLists' ls []\nand collectLists' (LIST (l,_)::rest)   acc = collectLists' rest (l::acc)\n  | collectLists' (VECTOR (v,_)::rest) acc = collectLists' rest (v::acc)\n  | collectLists' []                   acc = rev acc\n  | collectLists' _                    _   = raise NotApplicable \"invalid arguments\"\n\nfun arithFolder n f (INT next, INT prev) = INT (f (prev, next))\n  | arithFolder n _ _ = raise NotApplicable (\"'\" ^ n ^ \"' requires integer arguments\")\n\nfun cmpFolder n c (INT next, (INT prev, acc)) = (INT next, acc andalso (c (prev, next)))\n  | cmpFolder n _ _ = raise NotApplicable (\"'\" ^ n ^ \"' requires integer arguments\")\n\nfun cmpFold n c (x::xs) = foldl (cmpFolder n c) (x, true) xs |> #2 |> BOOL\n  | cmpFold n _ _ = raise NotApplicable (\"'\" ^ n ^ \"' requires arguments\")\n\nfun splatArgs [LIST (l,_)]   = l\n  | splatArgs [VECTOR (v,_)] = v\n  | splatArgs (x::xs)        = x::(splatArgs xs)\n  | splatArgs []             = []\n\nfun slurp lines strm = case TextIO.inputLine strm of\n    SOME l => slurp (l::lines) strm\n    | NONE => (TextIO.closeIn strm; rev lines)\n\nfun malPrint s = (\n    TextIO.print (s ^ \"\\n\");\n    NIL\n)\n\nfun readLine prompt = (\n    TextIO.print prompt;\n    TextIO.inputLine TextIO.stdIn |> Option.map (trimr 1)\n)\n\nfun strJoin separator strings = String.concatWith separator strings\n\n(*\n * Core primitives\n *)\n\nfun prim name f =\n    let val badArgs = STRING (\"incorrect arguments passed to '\" ^ name ^ \"'\") in\n        [SYMBOL name, FN (fn args => f args handle Domain => raise MalException badArgs, NO_META)]\n    end\n\nval coreNs = List.concat [\n\n    (* Maths *)\n    prim \"+\" (fn args    => foldl (arithFolder \"+\" (op +)) (INT 0) args),\n    prim \"*\" (fn args    => foldl (arithFolder \"*\" (op * )) (INT 1) args),\n    prim \"/\" (fn (x::xs) => foldl (arithFolder \"/\" (op div)) x xs | _ => raise Domain),\n    prim \"-\" (fn (x::xs) => foldl (arithFolder \"-\" (op -)) x xs | _ => raise Domain),\n    \n    (* Comparisons *)\n    prim \"<\"  (cmpFold \"<\"  (op <)),\n    prim \"<=\" (cmpFold \"<=\" (op <=)),\n    prim \">=\" (cmpFold \">=\" (op >=)),\n    prim \">\"  (cmpFold \">\"  (op >)),\n    prim \"=\"\n    (fn (x::xs) => foldl (fn (n,(p,acc)) => (n,acc andalso (malEq (n, p)))) (x, true) xs |> #2 |> BOOL\n      | _ => raise Domain),\n\n    (* Predicates *)\n    prim     \"nil?\" (fn [NIL]        => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim    \"true?\" (fn [BOOL true]  => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim   \"false?\" (fn [BOOL false] => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim  \"symbol?\" (fn [SYMBOL _]   => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim \"keyword?\" (fn [KEYWORD _]  => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim  \"vector?\" (fn [VECTOR _]   => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim     \"map?\" (fn [MAP _]      => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim      \"fn?\" (fn [FN _]       => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim   \"macro?\" (fn [MACRO _]    => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim  \"string?\" (fn [STRING _]   => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim  \"number?\" (fn [INT _]      => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim    \"atom?\" (fn [ATOM _]     => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim    \"list?\" (fn [LIST _]     => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim \"sequential?\"\n    (fn [LIST _] => BOOL true | [VECTOR _] => BOOL true | [_] => BOOL false | _ => raise Domain),\n    prim \"empty?\"\n    (fn [LIST (l,_)] => BOOL (length l = 0) | [VECTOR (v,_)] => BOOL (length v = 0) | _ => raise Domain),\n    prim \"contains?\"\n    (fn [MAP (m,_), k] => BOOL (List.exists (fn (k', _) => malEq (k, k')) m) | _ => raise Domain),\n\n    (* I/O *)\n    prim \"slurp\"\n    (fn [STRING filename] => TextIO.openIn filename |> slurp [] |> strJoin \"\" |> STRING | _ => raise Domain),\n    prim \"prn\"\n    (fn args => args |> map prReadableStr |> strJoin \" \" |> malPrint),\n    prim \"println\"\n    (fn args => args |> map prStr |> strJoin \" \" |> malPrint),\n    prim \"readline\"\n    (fn [STRING prompt] => valOrElse (readLine prompt |> Option.map STRING) (fn () => NIL) | _ => raise Domain),\n\n    (* Strings and stringoids *)\n    prim \"str\"\n    (fn args => args |> map prStr |> strJoin \"\" |> STRING),\n    prim \"pr-str\"\n    (fn args => args |> map prReadableStr |> strJoin \" \" |> STRING),\n    prim \"symbol\"\n    (fn [STRING s] => SYMBOL s | _ => raise Domain),\n    prim \"keyword\"\n    (fn [STRING s] => KEYWORD s | [kw as KEYWORD _] => kw | _ => raise Domain),\n\n    (* Atoms *)\n    prim \"atom\"   (fn [x] => ATOM (ref x) | _ => raise Domain),\n    prim \"deref\"  (fn [ATOM a] => !a | _ => raise Domain),\n    prim \"reset!\" (fn [ATOM a, x] => (a := x; x) | _ => raise Domain),\n    prim \"swap!\"  (fn (ATOM a::(FN (f,_))::args) => let val x = f ((!a)::args) in (a := x; x) end | _ => raise Domain),\n\n    (* Listoids *)\n    prim \"list\"   (fn args => malList args),\n    prim \"vector\" (fn args => malVector (args)),\n    prim \"vec\"    (fn [LIST (xs,_)] => malVector (xs) | [v as VECTOR _] => v | _ => raise Domain),\n    prim \"concat\" (fn args => malList (List.concat (collectLists args))),\n    prim \"cons\"\n    (fn [hd, LIST (tl,_)]   => malList (hd::tl)\n      | [hd, VECTOR (tl,_)] => malList (hd::tl)\n      | _ => raise Domain),\n    prim \"conj\"\n    (fn (LIST (l,_)::args)   => malList (rev args @ l)\n      | (VECTOR (v,_)::args) => malVector (v @ args)\n      | _ => raise Domain),\n    prim \"seq\"\n    (fn [LIST ([],_)]   => NIL | [l as LIST _]  => l\n      | [VECTOR ([],_)] => NIL | [VECTOR (v,_)] => malList v\n      | [STRING \"\"]     => NIL | [STRING s]     => String.explode s |> List.map (STRING o String.str) |> malList\n      | [NIL]           => NIL\n      | _ => raise Domain),\n    prim \"count\"\n    (fn [LIST (l,_)]   => INT (length l |> LargeInt.fromInt)\n      | [VECTOR (v,_)] => INT (length v |> LargeInt.fromInt)\n      | [NIL]          => INT 0\n      | _ => raise Domain),\n    prim \"nth\"\n    (fn [LIST (l,_), INT n]   => (List.nth (l, (Int.fromLarge n)) handle Subscript => raise OutOfBounds \"index out of bounds\")\n      | [VECTOR (v,_), INT n] => (List.nth (v, (Int.fromLarge n)) handle Subscript => raise OutOfBounds \"index out of bounds\")\n      | _ => raise Domain),\n    prim \"first\"\n    (fn [LIST (l,_)]   => (case l of (x::_) => x | _ => NIL)\n      | [VECTOR (v,_)] => (case v of (x::_) => x | _ => NIL)\n      | [NIL]          => NIL\n      | _ => raise Domain),\n    prim \"rest\"\n    (fn [LIST (l,_)]   => malList (case l of (_::xs) => xs | _ => [])\n      | [VECTOR (v,_)] => malList (case v of (_::xs) => xs | _ => [])\n      | [NIL]          => malList ([])\n      | _ => raise Domain),\n    prim \"map\"\n    (fn [FN (f,_), LIST (l,_)]   => malList (List.map (fn x => f [x]) l)\n      | [FN (f,_), VECTOR (v,_)] => malList (List.map (fn x => f [x]) v)\n      | _ => raise Domain),\n\n    (* Maps *)\n    prim \"hash-map\"\n    (fn args => buildMap args []),\n    prim \"assoc\"\n    (fn (MAP (m,_)::(args as _::_)) => buildMap args m | _ => raise Domain),\n    prim \"dissoc\"\n    (fn (MAP (m,_)::(args as _::_)) => malMap (foldl (fn (k, acc) => malDissoc acc k) m args) | _ => raise Domain),\n    prim \"get\"\n    (fn [MAP (m,_), k] => valOrElse (malGet m k) (fn () => NIL) | [NIL, _] => NIL | _ => raise Domain),\n    prim \"keys\"\n    (fn [MAP (m,_)] => malList (map #1 m) | _ => raise Domain),\n    prim \"vals\"\n    (fn [MAP (m,_)] => malList (map #2 m) | _ => raise Domain),\n\n    (* Metaprogramming and metadata *)\n    prim \"read-string\"\n    (fn [STRING s] => readStr s | _ => raise Domain),\n    prim \"apply\"\n    (fn (FN (f,_)::args) => f (splatArgs args)\n      | (MACRO f::args)  => f (splatArgs args)\n      | _ => raise Domain),\n    prim \"meta\"\n    (fn [    FN (_, META m)] => m\n      | [  LIST (_, META m)] => m\n      | [VECTOR (_, META m)] => m\n      | [   MAP (_, META m)] => m\n      | [_] => NIL\n      | _ => raise Domain),\n    prim \"with-meta\"\n    (fn [FN (f,_),     meta] =>     FN (f, META meta)\n      | [LIST (l,_),   meta] =>   LIST (l, META meta)\n      | [VECTOR (v,_), meta] => VECTOR (v, META meta)\n      | [MAP (m,_),    meta] =>    MAP (m, META meta)\n      | [x] => x\n      | _ => raise Domain),\n\n    (* Odds and ends *)\n    prim \"throw\"\n    (fn [x] => raise MalException x | _ => raise Domain),\n    prim \"time-ms\"\n    (fn _ => INT (Time.now () |> Time.toMilliseconds))\n]\n"
  },
  {
    "path": "impls/sml/env.sml",
    "content": "fun set s v (NS d) = d := (s, v) :: (!d |> List.filter (not o eq s o #1))\n\nfun get (NS d) s = !d |> List.find (eq s o #1) |> Option.map #2\n\nfun def s v (ENV ns)        = set s v ns\n  | def s v (INNER (ns, _)) = set s v ns\n\nfun lookup (ENV ns)            s = get ns s\n  | lookup (INNER (ns, outer)) s = optOrElse (get ns s) (fn () => lookup outer s)\n\nfun inside outer = INNER (NS (ref []), outer)\n"
  },
  {
    "path": "impls/sml/main.sml",
    "content": "val _ = main ()\n"
  },
  {
    "path": "impls/sml/printer.sml",
    "content": "fun prStr NIL            = \"nil\"\n  | prStr (SYMBOL s)     = s\n  | prStr (BOOL true)    = \"true\"\n  | prStr (BOOL false)   = \"false\"\n  | prStr (ATOM x)       = \"#<atom> (\" ^ (prStr (!x)) ^ \")\"\n  | prStr (INT i)        = if i >= 0 then LargeInt.toString i else \"-\" ^ (LargeInt.toString (LargeInt.abs i))\n  | prStr (STRING s)     = s\n  | prStr (KEYWORD s)    = \":\" ^ s\n  | prStr (LIST (l,_))   = \"(\" ^ (String.concatWith \" \" (map prStr l)) ^ \")\" (* N.B. not tail recursive *)\n  | prStr (VECTOR (v,_)) = \"[\" ^ (String.concatWith \" \" (map prStr v)) ^ \"]\" (* N.B. not tail recursive *)\n  | prStr (MAP (m,_))    = \"{\" ^ (String.concatWith \" \" (map prKvp m)) ^ \"}\" (* N.B. not tail recursive *)\n  | prStr (FN _)         = \"#<function>\"\n  | prStr (MACRO _)      = \"#<macro>\"\nand prKvp (k, v) = (prStr k) ^ \" \" ^ (prStr v)\n\nfun prReadableStr (STRING s)     = \"\\\"\" ^ (malEscape s) ^ \"\\\"\"\n  | prReadableStr (ATOM x)       = \"(atom \" ^ (prReadableStr (!x)) ^ \")\"\n  | prReadableStr (LIST (l,_))   = \"(\" ^ (String.concatWith \" \" (map prReadableStr l)) ^ \")\" (* N.B. not tail recursive *)\n  | prReadableStr (VECTOR (v,_)) = \"[\" ^ (String.concatWith \" \" (map prReadableStr v)) ^ \"]\" (* N.B. not tail recursive *)\n  | prReadableStr (MAP (m,_))    = \"{\" ^ (String.concatWith \" \" (map prReadableKvp m)) ^ \"}\" (* N.B. not tail recursive *)\n  | prReadableStr x              = prStr x\nand prReadableKvp (k, v) = (prReadableStr k) ^ \" \" ^ (prReadableStr v)\n"
  },
  {
    "path": "impls/sml/reader.sml",
    "content": "exception Nothing\nexception SyntaxError of string\nexception ReaderError of string\n\nstructure Ss = Substring\n\ndatatype token =\n    SPACE\n    | COMMENT of string\n    | BRACKET_LEFT | BRACKET_RIGHT\n    | BRACE_LEFT   | BRACE_RIGHT\n    | PAREN_LEFT   | PAREN_RIGHT\n    | QUOTE | BACK_TICK | TILDE | TILDE_AT\n    | CARET\n    | AT\n    | LIT_ATOM of string\n    | LIT_STR of string\n\nfun tokenString SPACE         = \"SPACE\"\n  | tokenString (COMMENT s)   = \"COMMENT (\" ^ s ^ \")\"\n  | tokenString BRACKET_LEFT  = \"BRACKET_LEFT\"\n  | tokenString BRACKET_RIGHT = \"BRACKET_RIGHT\"\n  | tokenString BRACE_LEFT    = \"BRACE_LEFT\"\n  | tokenString BRACE_RIGHT   = \"BRACE_RIGHT\"\n  | tokenString PAREN_LEFT    = \"PAREN_LEFT\"\n  | tokenString PAREN_RIGHT   = \"PAREN_RIGHT\"\n  | tokenString QUOTE         = \"QUOTE\"\n  | tokenString BACK_TICK     = \"BACK_TICK\"\n  | tokenString TILDE         = \"TILDE\"\n  | tokenString TILDE_AT      = \"TILDE_AT\"\n  | tokenString CARET         = \"CARET\"\n  | tokenString AT            = \"AT\"\n  | tokenString (LIT_ATOM s)  = \"LIT_ATOM (\" ^ s ^ \")\"\n  | tokenString (LIT_STR s)   = \"LIT_STR \\\"\" ^ s ^ \"\\\"\"\n\ndatatype reader = READER of token list\n\nfun next (READER (x::xs)) = SOME (x, READER xs)\n  | next r                = NONE\n\nfun peek (READER (x::_)) = SOME x\n  | peek r               = NONE\n\nfun rest (READER (_::xs)) = READER xs\n  | rest r = raise ReaderError \"out of tokens\"\n\nfun findSpecial #\"[\" = SOME BRACKET_LEFT\n  | findSpecial #\"]\" = SOME BRACKET_RIGHT\n  | findSpecial #\"(\" = SOME PAREN_LEFT\n  | findSpecial #\")\" = SOME PAREN_RIGHT\n  | findSpecial #\"{\" = SOME BRACE_LEFT\n  | findSpecial #\"}\" = SOME BRACE_RIGHT\n  | findSpecial #\"'\" = SOME QUOTE\n  | findSpecial #\"`\" = SOME BACK_TICK\n  | findSpecial #\"~\" = SOME TILDE\n  | findSpecial #\"^\" = SOME CARET\n  | findSpecial #\"@\" = SOME AT\n  | findSpecial _ = NONE\n\nfun scanSpace ss =\n    let fun isSpace c = Char.isSpace c orelse c = #\",\"\n        val (tok, rest) = Ss.splitl isSpace ss in\n        if Ss.isEmpty tok then NONE else SOME (SPACE, rest)\n    end\n\nfun scanComment ss = case Ss.getc ss of\n    SOME (#\";\", rest) =>\n        let val (comment, rest) = Ss.splitl (fn (c) => c <> #\"\\n\") rest in\n            SOME (COMMENT (Ss.string comment), rest)\n        end\n    | _ => NONE\n\nfun scanSpecial ss =\n    if Ss.isPrefix \"~@\" ss\n    then SOME (TILDE_AT, Ss.slice (ss, 2, NONE))\n    else let fun findToken (c, rest) = findSpecial c |> Option.map (fn t => (t, rest)) in\n        Option.composePartial (findToken, Ss.getc) ss\n    end\n\nfun scanString ss =\n    Ss.getc ss |> Option.mapPartial (fn (#\"\\\"\", rest) => spanString rest rest | _ => NONE)\n\nand spanString from to = case Ss.getc to of\n    SOME (#\"\\\\\", rest)   => Ss.getc rest |> Option.mapPartial (fn (_, more) => spanString from more)\n    | SOME (#\"\\\"\", rest) => SOME (LIT_STR (spanString' from to), rest)\n    | SOME (_, rest)     => spanString from rest\n    | NONE => raise SyntaxError \"end of input reached when parsing string literal\"\nand spanString' from stop =\n    Ss.span (from, Ss.slice (stop, 0, SOME 0)) |> Ss.string\n\nfun scanAtom ss =\n    let fun isAtomChar c = Char.isGraph c andalso (findSpecial c = NONE)\n        val (tok, rest) = Ss.splitl isAtomChar ss in\n        if Ss.isEmpty tok then NONE else SOME (LIT_ATOM (Ss.string tok), rest)\n    end\n\nfun scanToken ss =\n    let val scanners = [scanSpace, scanComment, scanSpecial, scanString, scanAtom]\n        val findScanner = List.find (fn f => isSome (f ss))\n        fun applyScanner s = s ss\n    in\n        Option.composePartial (applyScanner, findScanner) scanners\n    end\n\nfun tokenize s = tokenize' [] (Ss.full s)\nand tokenize' acc ss = case scanToken ss of\n    SOME (token, rest) => tokenize' (token::acc) rest\n    | NONE             => rev acc\n\nfun readAtom r = case next r of\n    SOME (LIT_ATOM \"nil\", r')     => (NIL, r')\n    | SOME (LIT_ATOM \"true\", r')  => (BOOL true, r')\n    | SOME (LIT_ATOM \"false\", r') => (BOOL false, r')\n    | SOME (LIT_ATOM s, r')       => (LargeInt.fromString s |> Option.map INT\n                                     |> optIfNone (fn () => Option.filter (String.isPrefix \":\") s |> Option.map (KEYWORD o (triml 1)))\n                                     |> valIfNone (fn () => SYMBOL s), r')\n    | SOME (LIT_STR s, r')        => (malUnescape s |> STRING, r')\n    | SOME (CARET, r')            => readWithMeta r'\n    | SOME (token, _) => raise SyntaxError (\"unexpected token reading atom: \" ^ (tokenString token))\n    | NONE => raise SyntaxError \"end of input reached when reading atom\"\n\nand readForm r = case peek r of\n    SOME PAREN_LEFT     => readList [] (rest r)\n    | SOME BRACKET_LEFT => readVector [] (rest r)\n    | SOME BRACE_LEFT   => readMap [] (rest r)\n    | SOME AT           => let val (a, r') = readAtom (rest r) in (malList [SYMBOL \"deref\", a], r') end\n    | SOME QUOTE        => let val (a, r') = readForm (rest r) in (malList [SYMBOL \"quote\", a], r') end\n    | SOME BACK_TICK    => let val (a, r') = readForm (rest r) in (malList [SYMBOL \"quasiquote\", a], r') end\n    | SOME TILDE        => let val (a, r') = readForm (rest r) in (malList [SYMBOL \"unquote\", a], r') end\n    | SOME TILDE_AT     => let val (a, r') = readForm (rest r) in (malList [SYMBOL \"splice-unquote\", a], r') end\n    | _                 => readAtom r\n\nand readWithMeta r =\n    let val (m, r')  = readForm r\n        val (v, r'') = readForm r'\n    in\n        (malList [SYMBOL \"with-meta\", v, m], r'')\n    end\n\nand readList acc r =\n    if peek r = SOME PAREN_RIGHT\n    then (LIST (rev acc, NO_META), (rest r))\n    else let val (a, r') = readForm r in readList (a::acc) r' end\n\nand readVector acc r =\n    if peek r = SOME BRACKET_RIGHT\n    then (VECTOR (rev acc, NO_META), (rest r))\n    else let val (a, r') = readForm r in readVector (a::acc) r' end\n\nand readMap acc r =\n    if peek r = SOME BRACE_RIGHT\n    then (MAP (rev acc, NO_META), (rest r))\n    else let val (k, r') = readForm r val (v, r'') = readForm r' in readMap (malAssoc acc k v) r'' end\n\nfun clean ts =\n    ts |> List.filter (fn x => x <> SPACE)\n       |> List.filter (fn COMMENT _ => false | _ => true)\n\nfun readStr s = case tokenize s |> clean of\n    []   => raise Nothing\n    | ts => ts |> READER |> readForm |> #1\n"
  },
  {
    "path": "impls/sml/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/sml/step0_repl.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    step0_repl.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step0_repl.sml",
    "content": "fun read s: string =\n    s\n\nfun eval s: string =\n    s\n\nfun print s: string =\n    s\n\nfun rep s: string =\n    (print o eval o read) s\n\nfun repl () =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) => (\n                print(rep(line) ^ \"\\n\");\n                repl ()\n            )\n            | NONE => ()\n    ) end\n\nfun main () = repl ()\n"
  },
  {
    "path": "impls/sml/step1_read_print.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    step1_read_print.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step1_read_print.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval f =\n    f\n\nfun print f =\n    prReadableStr f\n\nfun rep s =\n    s |> read |> eval |> print\n    handle SyntaxError msg => \"SYNTAX ERROR: \" ^ msg\n         | Nothing         => \"\"\n\nfun repl () =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) => (\n                print(rep(line) ^ \"\\n\");\n                repl ()\n            )\n            | NONE => ()\n    ) end\n\nfun main () = repl ()\n"
  },
  {
    "path": "impls/sml/step2_eval.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    step2_eval.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step2_eval.sml",
    "content": "exception NotDefined of string\nexception NotApplicable of string\n\nfun read s =\n    readStr s\n\n(* TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\") *)\nfun eval e ast = case ast of\n    LIST (_::_,_) => evalApply e ast\n    | _           => evalAst e ast\n\nand evalAst e ast = case ast of\n    SYMBOL s       => (case lookup e s of SOME v => v | NONE => raise NotDefined (\"unable to resolve symbol '\" ^ s ^ \"'\"))\n    | LIST (l,_)   => LIST (List.map (eval e) l, NO_META)\n    | VECTOR (v,_) => VECTOR (List.map (eval e) v, NO_META)\n    | MAP (m,_)    => MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n    | _            => ast\n\nand evalApply e ast = case evalAst e ast of\n    LIST ((FN (f,_))::args, _) => f args\n    | _ => raise NotApplicable \"eval_apply needs a non-empty list\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing => \"\"\n         | e       => \"ERROR: \" ^ (exnMessage e)\n\nfun malPlus  (INT a, INT b) = INT (a + b)\n  | malPlus _ = raise NotApplicable \"can only add integers\"\nfun malTimes (INT a, INT b) = INT (a * b)\n  | malTimes _ = raise NotApplicable \"can only multiply integers\"\nfun malMinus (INT b, INT a) = INT (a - b)\n  | malMinus _ = raise NotApplicable \"can only subtract integers\"\nfun malDiv   (INT b, INT a) = INT (a div b)\n  | malDiv _ = raise NotApplicable \"can only divide integers\"\n\nval replEnv = ENV (NS (ref [\n    (\"+\", FN (foldl malPlus (INT 0), NO_META)),\n    (\"*\", FN (foldl malTimes (INT 1), NO_META)),\n    (\"-\", FN (\n        fn [x]   => malMinus (x, INT 0)\n         | x::xs => foldr malMinus x xs\n         | _ => raise NotApplicable \"'-' requires at least one argument\"\n        , NO_META\n    )),\n    (\"/\", FN (\n        fn [x]   => malDiv (x, INT 1)\n         | x::xs => foldr malDiv x xs\n         | _ => raise NotApplicable \"'/' requires at least one argument\"\n        , NO_META\n    ))\n]))\n\nfun repl () =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) => (\n                print((rep replEnv line) ^ \"\\n\");\n                repl ()\n            )\n            | NONE => ()\n    ) end\n\nfun main () = repl ()\n"
  },
  {
    "path": "impls/sml/step3_env.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    step3_env.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step3_env.sml",
    "content": "exception NotDefined of string\nexception NotApplicable of string\n\nfun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args,_)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)         = evalSymbol e s\n  | eval' e (VECTOR (v,_))     = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))        = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                = ast\n\nand specialEval (SYMBOL \"def!\") = SOME evalDef\n  | specialEval (SYMBOL \"let*\") = SOME evalLet\n  | specialEval _               = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bind bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bind bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply _ a args = raise NotApplicable (prStr a ^ \" is not applicable on \" ^ prStr (LIST (args, NO_META)))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"symbol '\" ^ s ^ \"' not found\"))\n\nand bind (SYMBOL s::v::rest) e = (def s (eval e v) e; bind rest e)\n  | bind []                  e = e\n  | bind _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \"  ^ msg\n\nfun malPlus  (INT a, INT b) = INT (a + b)\n  | malPlus _ = raise NotApplicable \"can only add integers\"\nfun malTimes (INT a, INT b) = INT (a * b)\n  | malTimes _ = raise NotApplicable \"can only multiply integers\"\nfun malMinus (INT b, INT a) = INT (a - b)\n  | malMinus _ = raise NotApplicable \"can only subtract integers\"\nfun malDiv   (INT b, INT a) = INT (a div b)\n  | malDiv _ = raise NotApplicable \"can only divide integers\"\n\nval replEnv = ENV (NS (ref [])) |> bind [\n    SYMBOL \"+\",\n    FN (foldl malPlus (INT 0), NO_META),\n    SYMBOL \"*\",\n    FN (foldl malTimes (INT 1), NO_META),\n    SYMBOL \"-\",\n    FN (fn [x]   => malMinus (x, INT 0)\n         | x::xs => foldr malMinus x xs\n         | _ => raise NotApplicable \"'-' requires arguments\"\n        , NO_META),\n    SYMBOL \"/\",\n    FN (fn [x]   => malDiv (x, INT 1)\n         | x::xs => foldr malDiv x xs\n         | _ => raise NotApplicable \"'/' requires arguments\"\n        , NO_META)\n]\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nfun main () = repl replEnv\n"
  },
  {
    "path": "impls/sml/step4_if_fn_do.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    core.sml\n    step4_if_fn_do.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step4_if_fn_do.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args,_)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)         = evalSymbol e s\n  | eval' e (VECTOR (v,_))     = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))        = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                = ast\n\nand specialEval (SYMBOL \"def!\") = SOME evalDef\n  | specialEval (SYMBOL \"let*\") = SOME evalLet\n  | specialEval (SYMBOL \"do\")   = SOME evalDo\n  | specialEval (SYMBOL \"if\")   = SOME evalIf\n  | specialEval (SYMBOL \"fn*\")  = SOME evalFn\n  | specialEval _               = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bindLet bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs\n  | evalDo _ _ = raise NotApplicable \"do needs at least one argument\"\n\nand evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b\n  | evalIf e [c,a]   = evalIf e [c,a,NIL]\n  | evalIf _ _ = raise NotApplicable \"if needs two or three arguments\"\n\nand evalFn e [LIST (binds,_),body]   = makeFn e binds body\n  | evalFn e [VECTOR (binds,_),body] = makeFn e binds body\n  | evalFn _ _ = raise NotApplicable \"fn* needs a list of bindings and a body\"\nand makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply _ x args = raise NotApplicable (prStr x ^ \" is not applicable on \" ^ prStr (LIST (args, NO_META)))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"symbol '\" ^ s ^ \"' not found\"))\n\nand bindLet args e = bind' (eval e) args e\nand bind args e = bind' identity args e\nand bind' evl (SYMBOL \"&\"::v::(SYMBOL s)::vs) e = (def s (LIST (map evl (v::vs), NO_META)) e; e)\n  | bind' _   [SYMBOL \"&\", SYMBOL s]          e = (def s (LIST ([], NO_META)) e; e)\n  | bind' evl (SYMBOL s::v::rest)             e = (def s (evl v) e; bind' evl rest e)\n  | bind' _   []                              e = e\n  | bind' _ _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \" ^ msg\n\nval replEnv = ENV (NS (ref [])) |> bind coreNs\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nval prelude = \"                        \\\n\\(def! not (fn* (a) (if a false true)))\"\n\nfun main () = (\n    rep replEnv (\"(do \" ^ prelude ^ \" nil)\");\n    repl replEnv\n)\n"
  },
  {
    "path": "impls/sml/step6_file.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    core.sml\n    step6_file.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step6_file.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args,_)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)         = evalSymbol e s\n  | eval' e (VECTOR (v,_))     = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))        = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                = ast\n\nand specialEval (SYMBOL \"def!\") = SOME evalDef\n  | specialEval (SYMBOL \"let*\") = SOME evalLet\n  | specialEval (SYMBOL \"do\")   = SOME evalDo\n  | specialEval (SYMBOL \"if\")   = SOME evalIf\n  | specialEval (SYMBOL \"fn*\")  = SOME evalFn\n  | specialEval _               = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bindLet bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs\n  | evalDo _ _ = raise NotApplicable \"do needs at least one argument\"\n\nand evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b\n  | evalIf e [c,a]   = evalIf e [c,a,NIL]\n  | evalIf _ _ = raise NotApplicable \"if needs two or three arguments\"\n\nand evalFn e [LIST (binds,_),body]   = makeFn e binds body\n  | evalFn e [VECTOR (binds,_),body] = makeFn e binds body\n  | evalFn _ _ = raise NotApplicable \"fn* needs a list of bindings and a body\"\nand makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply _ x args = raise NotApplicable (prStr x ^ \" is not applicable on \" ^ prStr (LIST (args, NO_META)))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"symbol '\" ^ s ^ \"' not found\"))\n\nand bindLet args e = bind' (eval e) args e\nand bind args e = bind' identity args e\nand bind' evl (SYMBOL \"&\"::v::(SYMBOL s)::vs) e = (def s (LIST (map evl (v::vs), NO_META)) e; e)\n  | bind' _   [SYMBOL \"&\", SYMBOL s]          e = (def s (LIST ([], NO_META)) e; e)\n  | bind' evl (SYMBOL s::v::rest)             e = (def s (evl v) e; bind' evl rest e)\n  | bind' _   []                              e = e\n  | bind' _ _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \" ^ msg\n\nval replEnv = ENV (NS (ref [])) |> bind coreNs\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nval prelude = \"                                                \\\n\\(def! not (fn* (a) (if a false true)))                        \\\n\\(def!                                                         \\\n\\  load-file                                                   \\\n\\  (fn* (f)                                                    \\\n\\    (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\nfun main () = (\n    bind [\n        SYMBOL \"eval\",\n        FN (fn ([x]) => eval replEnv x\n             | _ => raise NotApplicable \"'eval' requires one argument\", NO_META)\n    ] replEnv;\n    rep replEnv (\"(do \" ^ prelude ^ \" nil)\");\n    case CommandLine.arguments () of\n        prog::args => (\n            def \"*ARGV*\" (LIST (map STRING args, NO_META)) replEnv;\n            rep replEnv (\"(load-file \\\"\" ^ prog ^ \"\\\")\");\n            ()\n        )\n        | args => (\n            def \"*ARGV*\" (LIST (map STRING args, NO_META)) replEnv;\n            repl replEnv\n        )\n)\n"
  },
  {
    "path": "impls/sml/step7_quote.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    core.sml\n    step7_quote.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step7_quote.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args,_)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)         = evalSymbol e s\n  | eval' e (VECTOR (v,_))     = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))        = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                = ast\n\nand specialEval (SYMBOL \"def!\")             = SOME evalDef\n  | specialEval (SYMBOL \"let*\")             = SOME evalLet\n  | specialEval (SYMBOL \"do\")               = SOME evalDo\n  | specialEval (SYMBOL \"if\")               = SOME evalIf\n  | specialEval (SYMBOL \"fn*\")              = SOME evalFn\n  | specialEval (SYMBOL \"quote\")            = SOME evalQuote\n  | specialEval (SYMBOL \"quasiquote\")       = SOME evalQuasiquote\n  | specialEval _                           = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bindLet bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs\n  | evalDo _ _ = raise NotApplicable \"do needs at least one argument\"\n\nand evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b\n  | evalIf e [c,a]   = evalIf e [c,a,NIL]\n  | evalIf _ _ = raise NotApplicable \"if needs two or three arguments\"\n\nand evalFn e [LIST (binds,_),body]   = makeFn e binds body\n  | evalFn e [VECTOR (binds,_),body] = makeFn e binds body\n  | evalFn _ _ = raise NotApplicable \"fn* needs a list of bindings and a body\"\nand makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)\n\nand evalQuote e [x] = x\n  | evalQuote _ _ = raise NotApplicable \"quote needs one argument\"\n\nand evalQuasiquote e args = eval e (expandQuasiquote args)\n\nand expandQuasiquote [LIST ([SYMBOL \"unquote\", x],_)] = x\n  | expandQuasiquote [LIST (l,_)]    = malList (foldr quasiFolder [] l)\n  | expandQuasiquote [VECTOR (v,_)]  = malList [SYMBOL \"vec\", malList (foldr quasiFolder [] v)]\n  | expandQuasiquote [m as MAP _]    = malList ([SYMBOL \"quote\", m])\n  | expandQuasiquote [s as SYMBOL _] = malList ([SYMBOL \"quote\", s])\n  | expandQuasiquote [x]             = x\n  | expandQuasiquote _ = raise NotApplicable \"quasiquote needs one argument\"\nand quasiFolder (LIST ([SYMBOL \"splice-unquote\", x],_), acc) = [SYMBOL \"concat\", x, malList acc]\n  | quasiFolder (x, acc)                                     = [SYMBOL \"cons\", expandQuasiquote [x], malList acc]\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply _ x args = raise NotApplicable (prStr x ^ \" is not applicable on \" ^ prStr (malList args))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"symbol '\" ^ s ^ \"' not found\"))\n\nand bindLet args e = bind' (eval e) args e\nand bind args e = bind' identity args e\nand bind' evl (SYMBOL \"&\"::v::(SYMBOL s)::vs) e = (def s (malList (map evl (v::vs))) e; e)\n  | bind' _   [SYMBOL \"&\", SYMBOL s]          e = (def s (malList []) e; e)\n  | bind' evl (SYMBOL s::v::rest)             e = (def s (evl v) e; bind' evl rest e)\n  | bind' _   []                              e = e\n  | bind' _ _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \" ^ msg\n\nval replEnv = ENV (NS (ref [])) |> bind coreNs\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nval prelude = \"                                                \\\n\\(def! not (fn* (a) (if a false true)))                        \\\n\\(def!                                                         \\\n\\  load-file                                                   \\\n\\  (fn* (f)                                                    \\\n\\    (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\"\n\nfun main () = (\n    bind [\n        SYMBOL \"eval\",\n        FN (fn ([x]) => eval replEnv x\n             | _ => raise NotApplicable \"'eval' requires one argument\", NO_META)\n    ] replEnv;\n    rep replEnv (\"(do \" ^ prelude ^ \" nil)\");\n    case CommandLine.arguments () of\n        prog::args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            rep replEnv (\"(load-file \\\"\" ^ prog ^ \"\\\")\");\n            ()\n        )\n        | args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            repl replEnv\n        )\n)\n"
  },
  {
    "path": "impls/sml/step8_macros.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    core.sml\n    step8_macros.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step8_macros.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args,_)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)         = evalSymbol e s\n  | eval' e (VECTOR (v,_))     = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))        = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                = ast\n\nand specialEval (SYMBOL \"def!\")             = SOME evalDef\n  | specialEval (SYMBOL \"let*\")             = SOME evalLet\n  | specialEval (SYMBOL \"do\")               = SOME evalDo\n  | specialEval (SYMBOL \"if\")               = SOME evalIf\n  | specialEval (SYMBOL \"fn*\")              = SOME evalFn\n  | specialEval (SYMBOL \"quote\")            = SOME evalQuote\n  | specialEval (SYMBOL \"quasiquote\")       = SOME evalQuasiquote\n  | specialEval (SYMBOL \"defmacro!\")        = SOME evalDefmacro\n  | specialEval _                           = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bindLet bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs\n  | evalDo _ _ = raise NotApplicable \"do needs at least one argument\"\n\nand evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b\n  | evalIf e [c,a]   = evalIf e [c,a,NIL]\n  | evalIf _ _ = raise NotApplicable \"if needs two or three arguments\"\n\nand evalFn e [LIST (binds,_),body]   = makeFn e binds body\n  | evalFn e [VECTOR (binds,_),body] = makeFn e binds body\n  | evalFn _ _ = raise NotApplicable \"fn* needs a list of bindings and a body\"\nand makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)\n\nand evalQuote e [x] = x\n  | evalQuote _ _ = raise NotApplicable \"quote needs one argument\"\n\nand evalQuasiquote e args = eval e (expandQuasiquote args)\n\nand expandQuasiquote [LIST ([SYMBOL \"unquote\", x],_)] = x\n  | expandQuasiquote [LIST (l,_)]    = malList (foldr quasiFolder [] l)\n  | expandQuasiquote [VECTOR (v,_)]  = malList [SYMBOL \"vec\", malList (foldr quasiFolder [] v)]\n  | expandQuasiquote [m as MAP _]    = malList [SYMBOL \"quote\", m]\n  | expandQuasiquote [s as SYMBOL _] = malList [SYMBOL \"quote\", s]\n  | expandQuasiquote [x]             = x\n  | expandQuasiquote _ = raise NotApplicable \"quasiquote needs one argument\"\nand quasiFolder (LIST ([SYMBOL \"splice-unquote\", x],_), acc) = [SYMBOL \"concat\", x, malList acc]\n  | quasiFolder (x, acc)                                     = [SYMBOL \"cons\", expandQuasiquote [x], malList acc]\n\nand evalDefmacro e [SYMBOL s, ast] = defMacro e s (eval e ast)\n  | evalDefmacro _ _ = raise NotApplicable \"defmacro! needs a name, and a fn*\"\nand defMacro e s (FN (f,_)) = let val m = MACRO f in (def s m e; m) end\n  | defMacro _ _ _ = raise NotApplicable \"defmacro! needs a name, and a fn*\"\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply e (MACRO m) args  = eval e (m args)\n  | evalApply _ x args = raise NotApplicable (prStr x ^ \" is not applicable on \" ^ prStr (malList args))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"symbol '\" ^ s ^ \"' not found\"))\n\nand bindLet args e = bind' (eval e) args e\nand bind args e = bind' identity args e\nand bind' evl (SYMBOL \"&\"::v::(SYMBOL s)::vs) e = (def s (malList (map evl (v::vs))) e; e)\n  | bind' _   [SYMBOL \"&\", SYMBOL s]          e = (def s (malList []) e; e)\n  | bind' evl (SYMBOL s::v::rest)             e = (def s (evl v) e; bind' evl rest e)\n  | bind' _   []                              e = e\n  | bind' _ _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \" ^ msg\n         | e                 => \"ERROR: \" ^ (exnMessage e)\n\nval replEnv = ENV (NS (ref [])) |> bind coreNs\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nval prelude = \"                                                \\\n\\\\\n\\(def! not (fn* (a) (if a false true)))                        \\\n\\\\\n\\(def!                                                         \\\n\\  load-file                                                   \\\n\\  (fn* (f)                                                    \\\n\\    (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\\\n\\\\\n\\(defmacro!                                                    \\\n\\  cond                                                        \\\n\\  (fn* (& xs)                                                 \\\n\\    (if (> (count xs) 0)                                      \\\n\\        (list 'if (first xs)                                  \\\n\\                (if (> (count xs) 1)                          \\\n\\                  (nth xs 1)                                  \\\n\\                  (throw \\\"odd number of forms to cond\\\"))    \\\n\\                (cons 'cond (rest (rest xs)))))))\"\n\nfun main () = (\n    bind [\n        SYMBOL \"eval\",\n        FN (fn ([x]) => eval replEnv x\n             | _ => raise NotApplicable \"'eval' requires one argument\", NO_META)\n    ] replEnv;\n    rep replEnv (\"(do \" ^ prelude ^ \" nil)\");\n    case CommandLine.arguments () of\n        prog::args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            rep replEnv (\"(load-file \\\"\" ^ prog ^ \"\\\")\");\n            ()\n        )\n        | args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            repl replEnv\n        )\n)\n"
  },
  {
    "path": "impls/sml/step9_try.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    core.sml\n    step9_try.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/step9_try.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args, _)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)          = evalSymbol e s\n  | eval' e (VECTOR (v,_))      = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))         = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                 = ast\n\nand specialEval (SYMBOL \"def!\")             = SOME evalDef\n  | specialEval (SYMBOL \"let*\")             = SOME evalLet\n  | specialEval (SYMBOL \"do\")               = SOME evalDo\n  | specialEval (SYMBOL \"if\")               = SOME evalIf\n  | specialEval (SYMBOL \"fn*\")              = SOME evalFn\n  | specialEval (SYMBOL \"quote\")            = SOME evalQuote\n  | specialEval (SYMBOL \"quasiquote\")       = SOME evalQuasiquote\n  | specialEval (SYMBOL \"defmacro!\")        = SOME evalDefmacro\n  | specialEval (SYMBOL \"try*\")             = SOME evalTry\n  | specialEval _                           = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bindLet bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs\n  | evalDo _ _ = raise NotApplicable \"do needs at least one argument\"\n\nand evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b\n  | evalIf e [c,a]   = evalIf e [c,a,NIL]\n  | evalIf _ _ = raise NotApplicable \"if needs two or three arguments\"\n\nand evalFn e [(LIST (binds,_)),body]   = makeFn e binds body\n  | evalFn e [(VECTOR (binds,_)),body] = makeFn e binds body\n  | evalFn _ _ = raise NotApplicable \"fn* needs a list of bindings and a body\"\nand makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)\n\nand evalQuote e [x] = x\n  | evalQuote _ _ = raise NotApplicable \"quote needs one argument\"\n\nand evalQuasiquote e args = eval e (expandQuasiquote args)\n\nand expandQuasiquote [LIST ([SYMBOL \"unquote\", x],_)] = x\n  | expandQuasiquote [LIST (l,_)]    = malList (foldr quasiFolder [] l)\n  | expandQuasiquote [VECTOR (v,_)]  = malList [SYMBOL \"vec\", malList (foldr quasiFolder [] v)]\n  | expandQuasiquote [m as MAP _]    = malList [SYMBOL \"quote\", m]\n  | expandQuasiquote [s as SYMBOL _] = malList [SYMBOL \"quote\", s]\n  | expandQuasiquote [x]             = x\n  | expandQuasiquote _ = raise NotApplicable \"quasiquote needs one argument\"\nand quasiFolder (LIST ([SYMBOL \"splice-unquote\", x],_), acc) = [SYMBOL \"concat\", x, malList acc]\n  | quasiFolder (x, acc)                                     = [SYMBOL \"cons\", expandQuasiquote [x], malList acc]\n\nand evalDefmacro e [SYMBOL s, ast] = defMacro e s (eval e ast)\n  | evalDefmacro _ _ = raise NotApplicable \"defmacro! needs a name, and a fn*\"\nand defMacro e s (FN (f,_)) = let val m = MACRO f in (def s m e; m) end\n  | defMacro _ _ _ = raise NotApplicable \"defmacro! needs a name, and a fn*\"\n\nand evalTry e [a, LIST ([SYMBOL \"catch*\", b, c],_)] = (eval e a handle ex => evalCatch (inside e) b ex c)\n  | evalTry e [a]                                   = eval e a\n  | evalTry _ _ = raise NotApplicable \"try* needs a form to evaluate\"\nand evalCatch e b ex body = eval (bind [b, exnVal ex] e) body\n\nand exnVal (MalException x)    = x\n  | exnVal (SyntaxError msg)   = STRING msg\n  | exnVal (NotDefined msg)    = STRING msg\n  | exnVal (NotApplicable msg) = STRING msg\n  | exnVal (OutOfBounds msg)   = STRING msg\n  | exnVal exn                 = STRING (exnMessage exn)\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply e (MACRO m) args  = eval e (m args)\n  | evalApply _ x args = raise NotApplicable (prStr x ^ \" is not applicable on \" ^ prStr (malList args))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"'\" ^ s ^ \"' not found\"))\n\nand bindLet args e = bind' (eval e) args e\nand bind args e = bind' identity args e\nand bind' evl (SYMBOL \"&\"::v::(SYMBOL s)::vs) e = (def s (malList (map evl (v::vs))) e; e)\n  | bind' _   [SYMBOL \"&\", SYMBOL s]          e = (def s (malList []) e; e)\n  | bind' evl (SYMBOL s::v::rest)             e = (def s (evl v) e; bind' evl rest e)\n  | bind' _   []                              e = e\n  | bind' _ _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \" ^ msg\n         | MalException e    => \"ERROR: \" ^ (prStr e)\n         | e                 => \"ERROR: \" ^ (exnMessage e)\n\nval replEnv = ENV (NS (ref [])) |> bind coreNs\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nval prelude = \"                                                \\\n\\\\\n\\(def! not (fn* (a) (if a false true)))                        \\\n\\\\\n\\(def!                                                         \\\n\\  load-file                                                   \\\n\\  (fn* (f)                                                    \\\n\\    (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\\\n\\\\\n\\(defmacro!                                                    \\\n\\  cond                                                        \\\n\\  (fn* (& xs)                                                 \\\n\\    (if (> (count xs) 0)                                      \\\n\\        (list 'if (first xs)                                  \\\n\\                (if (> (count xs) 1)                          \\\n\\                  (nth xs 1)                                  \\\n\\                  (throw \\\"odd number of forms to cond\\\"))    \\\n\\                (cons 'cond (rest (rest xs)))))))\"\n\nfun main () = (\n    bind [\n        SYMBOL \"eval\",\n        FN (fn ([x]) => eval replEnv x\n             | _ => raise NotApplicable \"'eval' requires one argument\", NO_META)\n    ] replEnv;\n    rep replEnv (\"(do \" ^ prelude ^ \" nil)\");\n    case CommandLine.arguments () of\n        prog::args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            rep replEnv (\"(load-file \\\"\" ^ prog ^ \"\\\")\");\n            ()\n        )\n        | args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            repl replEnv\n        )\n)\n"
  },
  {
    "path": "impls/sml/stepA_mal.mlb",
    "content": "local\n    $(SML_LIB)/basis/basis.mlb\n    util.sml\n    types.sml\n    printer.sml\n    reader.sml\n    env.sml\n    core.sml\n    stepA_mal.sml\nin\n    main.sml\nend\n"
  },
  {
    "path": "impls/sml/stepA_mal.sml",
    "content": "fun read s =\n    readStr s\n\nfun eval e ast = (\n  case lookup e \"DEBUG-EVAL\" of\n    SOME(x) => if truthy x\n               then TextIO.print (\"EVAL: \" ^ prReadableStr ast ^ \"\\n\")\n               else ()\n    | NONE => ();\n  eval' e ast)\n\nand eval' e (LIST (a::args, _)) = (case specialEval a of SOME special => special e args | _ => evalApply e (eval e a) args)\n  | eval' e (SYMBOL s)          = evalSymbol e s\n  | eval' e (VECTOR (v,_))      = VECTOR (map (eval e) v, NO_META)\n  | eval' e (MAP (m,_))         = MAP (List.map (fn (k, v) => (k, eval e v)) m, NO_META)\n  | eval' e ast                 = ast\n\nand specialEval (SYMBOL \"def!\")             = SOME evalDef\n  | specialEval (SYMBOL \"let*\")             = SOME evalLet\n  | specialEval (SYMBOL \"do\")               = SOME evalDo\n  | specialEval (SYMBOL \"if\")               = SOME evalIf\n  | specialEval (SYMBOL \"fn*\")              = SOME evalFn\n  | specialEval (SYMBOL \"quote\")            = SOME evalQuote\n  | specialEval (SYMBOL \"quasiquote\")       = SOME evalQuasiquote\n  | specialEval (SYMBOL \"defmacro!\")        = SOME evalDefmacro\n  | specialEval (SYMBOL \"try*\")             = SOME evalTry\n  | specialEval _                           = NONE\n\nand evalDef e [SYMBOL s, ast] = let val v = eval e ast in (def s v e; v) end\n  | evalDef _ _ = raise NotApplicable \"def! needs a symbol and a form to evaluate\"\n\nand evalLet e [LIST (bs,_), ast]   = eval (bindLet bs (inside e)) ast\n  | evalLet e [VECTOR (bs,_), ast] = eval (bindLet bs (inside e)) ast\n  | evalLet _ _ = raise NotApplicable \"let* needs a list of bindings and a form to evaluate\"\n\nand evalDo e (x::xs) = foldl (fn (x, _) => eval e x) (eval e x) xs\n  | evalDo _ _ = raise NotApplicable \"do needs at least one argument\"\n\nand evalIf e [c,a,b] = if truthy (eval e c) then eval e a else eval e b\n  | evalIf e [c,a]   = evalIf e [c,a,NIL]\n  | evalIf _ _ = raise NotApplicable \"if needs two or three arguments\"\n\nand evalFn e [(LIST (binds,_)),body]   = makeFn e binds body\n  | evalFn e [(VECTOR (binds,_)),body] = makeFn e binds body\n  | evalFn _ _ = raise NotApplicable \"fn* needs a list of bindings and a body\"\nand makeFn e binds body = FN (fn (exprs) => eval (bind (interleave binds exprs) (inside e)) body, NO_META)\n\nand evalQuote e [x] = x\n  | evalQuote _ _ = raise NotApplicable \"quote needs one argument\"\n\nand evalQuasiquote e args = eval e (expandQuasiquote args)\n\nand expandQuasiquote [LIST ([SYMBOL \"unquote\", x],_)] = x\n  | expandQuasiquote [LIST (l,_)]    = malList (foldr quasiFolder [] l)\n  | expandQuasiquote [VECTOR (v,_)]  = malList [SYMBOL \"vec\", malList (foldr quasiFolder [] v)]\n  | expandQuasiquote [m as MAP _]    = malList [SYMBOL \"quote\", m]\n  | expandQuasiquote [s as SYMBOL _] = malList [SYMBOL \"quote\", s]\n  | expandQuasiquote [x]             = x\n  | expandQuasiquote _ = raise NotApplicable \"quasiquote needs one argument\"\nand quasiFolder (LIST ([SYMBOL \"splice-unquote\", x],_), acc) = [SYMBOL \"concat\", x, malList acc]\n  | quasiFolder (x, acc)                                     = [SYMBOL \"cons\", expandQuasiquote [x], malList acc]\n\nand evalDefmacro e [SYMBOL s, ast] = defMacro e s (eval e ast)\n  | evalDefmacro _ _ = raise NotApplicable \"defmacro! needs a name, and a fn*\"\nand defMacro e s (FN (f,_)) = let val m = MACRO f in (def s m e; m) end\n  | defMacro _ _ _ = raise NotApplicable \"defmacro! needs a name, and a fn*\"\n\nand evalTry e [a, LIST ([SYMBOL \"catch*\", b, c],_)] = (eval e a handle ex => evalCatch (inside e) b ex c)\n  | evalTry e [a]                                   = eval e a\n  | evalTry _ _ = raise NotApplicable \"try* needs a form to evaluate\"\nand evalCatch e b ex body = eval (bind [b, exnVal ex] e) body\n\nand exnVal (MalException x)    = x\n  | exnVal (SyntaxError msg)   = STRING msg\n  | exnVal (NotDefined msg)    = STRING msg\n  | exnVal (NotApplicable msg) = STRING msg\n  | exnVal (OutOfBounds msg)   = STRING msg\n  | exnVal exn                 = STRING (exnMessage exn)\n\nand evalApply e (FN (f,_)) args = f (map (eval e) args)\n  | evalApply e (MACRO m) args  = eval e (m args)\n  | evalApply _ x args = raise NotApplicable (prStr x ^ \" is not applicable on \" ^ prStr (malList args))\n\nand evalSymbol e s = valOrElse (lookup e s)\n                               (fn _ => raise NotDefined (\"'\" ^ s ^ \"' not found\"))\n\nand bindLet args e = bind' (eval e) args e\nand bind args e = bind' identity args e\nand bind' evl (SYMBOL \"&\"::v::(SYMBOL s)::vs) e = (def s (malList (map evl (v::vs))) e; e)\n  | bind' _   [SYMBOL \"&\", SYMBOL s]          e = (def s (malList []) e; e)\n  | bind' evl (SYMBOL s::v::rest)             e = (def s (evl v) e; bind' evl rest e)\n  | bind' _   []                              e = e\n  | bind' _ _ _ = raise NotApplicable \"bindings must be a list of symbol/form pairs\"\n\nfun print f =\n    prReadableStr f\n\nfun rep e s =\n    s |> read |> eval e |> print\n    handle Nothing           => \"\"\n         | SyntaxError msg   => \"SYNTAX ERROR: \" ^ msg\n         | NotApplicable msg => \"CANNOT APPLY: \" ^ msg\n         | NotDefined msg    => \"NOT DEFINED: \" ^ msg\n         | MalException e    => \"ERROR: \" ^ (prStr e)\n         | e                 => \"ERROR: \" ^ (exnMessage e)\n\nval replEnv = ENV (NS (ref [])) |> bind coreNs\n\nfun repl e =\n    let open TextIO\n    in (\n        print(\"user> \");\n        case inputLine(stdIn) of\n            SOME(line) =>\n                let val s = rep e line\n                    val _ = print(s ^ \"\\n\")\n                in\n                    repl e\n                end\n            | NONE => ()\n    ) end\n\nval prelude = \"                                                \\\n\\\\\n\\(def! not (fn* (a) (if a false true)))                        \\\n\\\\\n\\(def!                                                         \\\n\\  load-file                                                   \\\n\\  (fn* (f)                                                    \\\n\\    (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\\\n\\\\\n\\(defmacro!                                                    \\\n\\  cond                                                        \\\n\\  (fn* (& xs)                                                 \\\n\\    (if (> (count xs) 0)                                      \\\n\\        (list 'if (first xs)                                  \\\n\\                (if (> (count xs) 1)                          \\\n\\                  (nth xs 1)                                  \\\n\\                  (throw \\\"odd number of forms to cond\\\"))    \\\n\\                (cons 'cond (rest (rest xs)))))))\"\n\nfun main () = (\n    def \"*host-language*\" (STRING \"sml\") replEnv;\n    bind [\n        SYMBOL \"eval\",\n        FN (fn ([x]) => eval replEnv x\n             | _ => raise NotApplicable \"'eval' requires one argument\", NO_META)\n    ] replEnv;\n    rep replEnv (\"(do \" ^ prelude ^ \" nil)\");\n    case CommandLine.arguments () of\n        prog::args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            rep replEnv (\"(load-file \\\"\" ^ prog ^ \"\\\")\");\n            ()\n        )\n        | args => (\n            def \"*ARGV*\" (malList (map STRING args)) replEnv;\n            rep replEnv \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\";\n            repl replEnv\n        )\n)\n"
  },
  {
    "path": "impls/sml/types.sml",
    "content": "datatype mal_type = NIL\n                  | SYMBOL of string\n                  | BOOL of bool\n                  | INT of LargeInt.int\n                  | STRING of string\n                  | KEYWORD of string\n                  | LIST of (mal_type list * mal_meta)\n                  | VECTOR of (mal_type list * mal_meta)\n                  | MAP of ((mal_type * mal_type) list * mal_meta)\n                  | ATOM of mal_type ref\n                  | FN of (mal_type list -> mal_type) * mal_meta\n                  | MACRO of mal_type list -> mal_type\n\nand mal_meta = META of mal_type\n             | NO_META\n\nand mal_ns = NS of (string * mal_type) list ref\n\nand mal_env = ENV of mal_ns\n            | INNER of mal_ns * mal_env\n\nfun truthy (BOOL false) = false\n  | truthy NIL          = false\n  | truthy _            = true\n\nfun malEq (         NIL, NIL)          = true\n  | malEq (    SYMBOL a, SYMBOL b)     = a = b\n  | malEq (      BOOL a, BOOL b)       = a = b\n  | malEq (       INT a, INT b)        = a = b\n  | malEq (    STRING a, STRING b)     = a = b\n  | malEq (   KEYWORD a, KEYWORD b)    = a = b\n  | malEq (  LIST (a,_), LIST (b,_))   = ListPair.allEq malEq (a, b)\n  | malEq (VECTOR (a,_), VECTOR (b,_)) = ListPair.allEq malEq (a, b)\n  | malEq (  LIST (a,_), VECTOR (b,_)) = ListPair.allEq malEq (a, b)\n  | malEq (VECTOR (a,_), LIST (b,_))   = ListPair.allEq malEq (a, b)\n  | malEq (   MAP (a,_), MAP (b,_))    = mapEq a b\n  | malEq _                            = false\nand mapEq a b =\n    a |> List.map (fn (k,va) => (va, malGet b k)) |> List.all (fn (va,SOME vb) => malEq (va, vb) | _ => false) andalso\n    b |> List.map (fn (k,vb) => (vb, malGet a k)) |> List.all (fn (vb,SOME va) => malEq (vb, va) | _ => false)\n\nand malGet m k = m |> List.find (fn (k',_) => malEq (k, k')) |> Option.map #2\nand malAssoc m k v = (k, v) :: (malDissoc m k)\nand malDissoc m k = m |> List.filter (not o (fn (k', _) => malEq (k, k')))\n\nfun malList xs = LIST (xs, NO_META)\nfun malVector xs = VECTOR (xs, NO_META)\nfun malMap kvps = MAP (kvps, NO_META)\n"
  },
  {
    "path": "impls/sml/util.sml",
    "content": "fun takeWhile  f xs          = takeWhile' f [] xs\nand takeWhile' f acc []      = rev acc\n  | takeWhile' f acc (x::xs) = if f x then takeWhile' f (x::acc) xs else rev acc\n\ninfix 3 |> fun x |> f = f x\n\nfun eq a b = a = b\n\nfun optOrElse NONE b = b ()\n  | optOrElse a    _ = a\n\nfun valOrElse (SOME x) _ = x\n  | valOrElse a        b = b ()\n\nfun optIfNone b NONE = b ()\n  | optIfNone _ a    = a\n\nfun valIfNone _ (SOME a) = a\n  | valIfNone b _        = b ()\n\nfun interleave (x::xs) (y::ys) = x :: y :: interleave xs ys\n  | interleave []      ys      = ys\n  | interleave xs      []      = xs\n\nfun identity x = x\n\nfun triml k s = String.extract (s, k, NONE)\n\nfun trimr k s = String.substring (s, 0, String.size s - k)\n\nfun malEscape s = String.translate (fn #\"\\\"\" => \"\\\\\\\"\"\n                                     | #\"\\n\" => \"\\\\n\"\n                                     | #\"\\\\\" => \"\\\\\\\\\"\n                                     | c => String.str c) s\n\nfun malUnescape s = malUnescape' (String.explode s)\nand malUnescape' (#\"\\\\\"::(#\"\\\"\"::rest)) = \"\\\"\" ^ malUnescape' rest\n  | malUnescape' (#\"\\\\\"::(#\"n\" ::rest)) = \"\\n\" ^ malUnescape' rest\n  | malUnescape' (#\"\\\\\"::(#\"\\\\\"::rest)) = \"\\\\\" ^ malUnescape' rest\n  | malUnescape' (c::rest)              = (String.str c) ^ malUnescape' rest\n  | malUnescape' ([])                   = \"\"\n\n"
  },
  {
    "path": "impls/swift3/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Swift\nRUN apt-get -y install clang-3.6 cmake pkg-config \\\n    git ninja-build uuid-dev libicu-dev icu-devtools \\\n    libbsd-dev libedit-dev libxml2-dev libsqlite3-dev \\\n    swig libpython-dev libncurses5-dev\n\n# TODO: better way to do this?\nRUN ln -sf /usr/lib/llvm-3.6/bin/clang++ /usr/bin/clang++\nRUN ln -sf /usr/lib/llvm-3.6/bin/clang /usr/bin/clang\n\nENV SWIFT_PREFIX swift-3.1.1-RELEASE\nENV SWIFT_RELEASE ${SWIFT_PREFIX}-ubuntu16.04\n\nRUN cd /opt && \\\n    curl -O https://download.swift.org/swift-3.1.1-release/ubuntu1604/${SWIFT_PREFIX}/${SWIFT_RELEASE}.tar.gz && \\\n    tar xvzf ${SWIFT_RELEASE}.tar.gz && \\\n    rm ${SWIFT_RELEASE}.tar.gz\n\nENV PATH /opt/${SWIFT_RELEASE}/usr/bin/:$PATH\n\n\n"
  },
  {
    "path": "impls/swift3/Makefile",
    "content": "ifneq ($(shell which xcrun),)\n\tSWIFT = xcrun -sdk macosx swiftc\nelse\n\tSWIFT = swiftc\nendif\n\nSTEP3_DEPS = Sources/types.swift Sources/reader.swift Sources/printer.swift Sources/env.swift\nSTEP4_DEPS = $(STEP3_DEPS) Sources/core.swift\n\nSTEPS = step0_repl step1_read_print step2_eval step3_env \\\n\tstep4_if_fn_do step5_tco step6_file step7_quote \\\n\tstep8_macros step9_try stepA_mal\n\nall: $(STEPS)\n\ndist: mal\n\nmal: stepA_mal\n\tcp $< $@\n\nstep1_read_print step2_eval step3_env: $(STEP3_DEPS)\nstep4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal: $(STEP4_DEPS)\n\nstep%: Sources/step%/main.swift\n\t$(SWIFT) $+ -o $@\n\nclean:\n\trm -f $(STEPS) mal\n\n"
  },
  {
    "path": "impls/swift3/Sources/core.swift",
    "content": "// TODO: remove this once time-ms and slurp use standard library calls\n\n#if os(Linux)\nimport Glibc\n#else\nimport Darwin\n#endif\n\nfunc IntOp(_ op: (Int, Int) -> Int, _ a: MalVal, _ b: MalVal) throws -> MalVal {\n    switch (a, b) {\n    case (MV.MalInt(let i1), MV.MalInt(let i2)):\n        return MV.MalInt(op(i1, i2))\n    default:\n        throw MalError.General(msg: \"Invalid IntOp call\")\n    }\n}\n\nfunc CmpOp(_ op: (Int, Int) -> Bool, _ a: MalVal, _ b: MalVal) throws -> MalVal {\n    switch (a, b) {\n    case (MV.MalInt(let i1), MV.MalInt(let i2)):\n        return wraptf(op(i1, i2))\n    default:\n        throw MalError.General(msg: \"Invalid CmpOp call\")\n    }\n}\n\n\n\nlet core_ns: Dictionary<String,(Array<MalVal>) throws -> MalVal> = [\n    \"=\":  { wraptf(equal_Q($0[0], $0[1])) },\n    \"throw\": { throw MalError.MalException(obj: $0[0]) },\n\n    \"nil?\": {\n        switch $0[0] {\n        case MV.MalNil(_): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"true?\": {\n        switch $0[0] {\n        case MV.MalTrue(_): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"false?\": {\n        switch $0[0] {\n        case MV.MalFalse(_): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"string?\": {\n        switch $0[0] {\n        case MV.MalString(let s) where s.characters.count == 0:\n            return MV.MalTrue\n        case MV.MalString(let s):\n            return wraptf(s[s.startIndex] != \"\\u{029e}\")\n        default: return MV.MalFalse\n        }\n    },\n    \"symbol\": {\n        switch $0[0] {\n        case MV.MalSymbol(_): return $0[0]\n        case MV.MalString(let s): return MV.MalSymbol(s)\n        default: throw MalError.General(msg: \"Invalid symbol call\")\n        }\n    },\n    \"symbol?\": {\n        switch $0[0] {\n        case MV.MalSymbol(_): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"keyword\": {\n        switch $0[0] {\n        case MV.MalString(let s) where s.characters.count > 0:\n            if s[s.startIndex] == \"\\u{029e}\" { return $0[0] }\n            else { return MV.MalString(\"\\u{029e}\\(s)\") }\n        default: throw MalError.General(msg: \"Invalid symbol call\")\n        }\n    },\n    \"keyword?\": {\n        switch $0[0] {\n        case MV.MalString(let s) where s.characters.count > 0:\n            return wraptf(s[s.startIndex] == \"\\u{029e}\")\n        default: return MV.MalFalse\n        }\n    },\n    \"number?\": {\n        switch $0[0] {\n        case MV.MalInt(_): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"fn?\": {\n        switch $0[0] {\n        case MalVal.MalFunc(_, nil, _, _, _, _),\n             MalVal.MalFunc(_, _, _, _, false, _): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"macro?\": {\n        switch $0[0] {\n        case MalVal.MalFunc(_, _, _, _, true, _): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n\n    \"pr-str\":  {\n        // TODO: if the following two statements are combined into one, we get\n        // the following error message. It's not clear to me that there's\n        // actually any error, so this might be a compiler issue.\n        //\n        //      Sources/core.swift:29:59: error: type of expression is ambiguous without more context\n        //      let core_ns: [String: (Array<MalVal>) throws -> MalVal] = [\n        //                                                                ^\n\n        let s = $0.map { pr_str($0,true) }.joined(separator: \" \")\n        return MV.MalString(s)\n    },\n    \"str\": {\n        // The comment for \"pr-str\" applies here, too.\n        let s = $0.map { pr_str($0,false) }.joined(separator: \"\")\n        return MV.MalString(s)\n    },\n    \"prn\": {\n        print($0.map { pr_str($0,true) }.joined(separator: \" \"))\n        return MV.MalNil\n    },\n    \"println\": {\n        print($0.map { pr_str($0,false) }.joined(separator: \" \"))\n        return MV.MalNil\n    },\n    \"read-string\": {\n        switch $0[0] {\n        case MV.MalString(let str): return try read_str(str)\n        default: throw MalError.General(msg: \"Invalid read-string call\")\n        }\n    },\n    \"readline\": {\n        switch $0[0] {\n        case MV.MalString(let prompt):\n            print(prompt, terminator: \"\")\n            let line = readLine(strippingNewline: true)\n            if line == nil { return MV.MalNil }\n            return MV.MalString(line!)\n        default: throw MalError.General(msg: \"Invalid readline call\")\n        }\n    },\n    \"slurp\": {\n        switch $0[0] {\n        case MV.MalString(let file):\n            let data = try String(contentsOfFile: file, encoding: String.Encoding.utf8)\n            return MV.MalString(data)\n        default: throw MalError.General(msg: \"Invalid slurp call\")\n        }\n    },\n\n\n    \"<\":  { try CmpOp({ $0 < $1},  $0[0], $0[1]) },\n    \"<=\": { try CmpOp({ $0 <= $1}, $0[0], $0[1]) },\n    \">\":  { try CmpOp({ $0 > $1},  $0[0], $0[1]) },\n    \">=\": { try CmpOp({ $0 >= $1}, $0[0], $0[1]) },\n    \"+\":  { try IntOp({ $0 + $1},  $0[0], $0[1]) },\n    \"-\":  { try IntOp({ $0 - $1},  $0[0], $0[1]) },\n    \"*\":  { try IntOp({ $0 * $1},  $0[0], $0[1]) },\n    \"/\":  { try IntOp({ $0 / $1},  $0[0], $0[1]) },\n    \"time-ms\": {\n        let read = $0; // no parameters\n\n        // TODO: replace with something more like this\n        // return MV.MalInt(NSDate().timeIntervalSince1970 )\n\n        var tv:timeval = timeval(tv_sec: 0, tv_usec: 0)\n        gettimeofday(&tv, nil)\n        return MV.MalInt(tv.tv_sec * 1000 + Int(tv.tv_usec)/1000)\n    },\n\n    \"list\": { list($0) },\n    \"list?\": {\n        switch $0[0] {\n        case MV.MalList: return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"vector\": { vector($0) },\n    \"vector?\": {\n        switch $0[0] {\n        case MV.MalVector: return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"hash-map\": { try hash_map($0) },\n    \"map?\": {\n        switch $0[0] {\n        case MV.MalHashMap: return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"assoc\": {\n        switch $0[0] {\n        case MV.MalHashMap(let dict, _):\n            return hash_map(try _assoc(dict, Array($0[1..<$0.endIndex])))\n        default: throw MalError.General(msg: \"Invalid assoc call\")\n        }\n    },\n    \"dissoc\": {\n        switch $0[0] {\n        case MV.MalHashMap(let dict, _):\n            return hash_map(try _dissoc(dict, Array($0[1..<$0.endIndex])))\n        default: throw MalError.General(msg: \"Invalid dissoc call\")\n        }\n    },\n    \"get\": {\n        switch ($0[0], $0[1]) {\n        case (MV.MalHashMap(let dict, _), MV.MalString(let k)):\n            return dict[k] ?? MV.MalNil\n        case (MV.MalNil, MV.MalString(let k)):\n            return MV.MalNil\n        default: throw MalError.General(msg: \"Invalid get call\")\n        }\n    },\n    \"contains?\": {\n        switch ($0[0], $0[1]) {\n        case (MV.MalHashMap(let dict, _), MV.MalString(let k)):\n            return dict[k] != nil ? MV.MalTrue : MV.MalFalse\n        case (MV.MalNil, MV.MalString(let k)):\n            return MV.MalFalse\n        default: throw MalError.General(msg: \"Invalid contains? call\")\n        }\n    },\n    \"keys\": {\n        switch $0[0] {\n        case MV.MalHashMap(let dict, _):\n            return list(dict.keys.map { MV.MalString($0) })\n        default: throw MalError.General(msg: \"Invalid keys call\")\n        }\n    },\n    \"vals\": {\n        switch $0[0] {\n        case MV.MalHashMap(let dict, _):\n            return list(dict.values.map { $0 })\n        default: throw MalError.General(msg: \"Invalid vals call\")\n        }\n    },\n\n\n    \"sequential?\": {\n        switch $0[0] {\n        case MV.MalList:   return MV.MalTrue\n        case MV.MalVector: return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"cons\": {\n        if $0.count != 2 { throw MalError.General(msg: \"Invalid cons call\") }\n        switch ($0[0], $0[1]) {\n        case (let mv, MV.MalList(let lst, _)):\n            return list([mv] + lst)\n        case (let mv, MV.MalVector(let lst, _)):\n            return list([mv] + lst)\n        default: throw MalError.General(msg: \"Invalid cons call\")\n        }\n    },\n    \"concat\": {\n        var res = Array<MalVal>()\n        for seq in $0 {\n            switch seq {\n            case MV.MalList(let lst, _):   res = res + lst\n            case MV.MalVector(let lst, _): res = res + lst\n            default: throw MalError.General(msg: \"Invalid concat call\")\n            }\n        }\n        return list(res)\n    },\n    \"vec\": {\n        if $0.count != 1 { throw MalError.General(msg: \"Invalid vec call\") }\n        switch $0[0] {\n            case MV.MalList  (let lst, _): return vector(lst)\n            case MV.MalVector(let lst, _): return vector(lst)\n            default: throw MalError.General(msg: \"Invalid vec call\")\n        }\n    },\n    \"nth\": {\n        if $0.count != 2 { throw MalError.General(msg: \"Invalid nth call\") }\n        switch ($0[0], $0[1]) {\n        case (MV.MalList(let lst, _), MV.MalInt(let idx)):\n            if idx >= lst.count {\n                throw MalError.General(msg: \"nth: index out of range\")\n            }\n            return try _nth($0[0], idx)\n        case (MV.MalVector(let lst, _), MV.MalInt(let idx)):\n            if idx >= lst.count {\n                throw MalError.General(msg: \"nth: index out of range\")\n            }\n            return try _nth($0[0], idx)\n        default:\n            throw MalError.General(msg: \"Invalid nth call\")\n        }\n    },\n    \"first\": {\n        switch $0[0] {\n        case MV.MalList(let lst, _):\n            return lst.count > 0 ? lst[0] : MV.MalNil\n        case MV.MalVector(let lst, _):\n            return lst.count > 0 ? lst[0] : MV.MalNil\n        case MV.MalNil: return MV.MalNil\n        default: throw MalError.General(msg: \"Invalid first call\")\n        }\n    },\n    \"rest\": {\n        switch $0[0] {\n        case MV.MalList(let lst, _):\n            return lst.count > 0 ? try rest($0[0]) : list([])\n        case MV.MalVector(let lst, _):\n            return lst.count > 0 ? try rest($0[0]) : list([])\n        case MV.MalNil: return list([])\n        default: throw MalError.General(msg: \"Invalid rest call\")\n        }\n    },\n    \"empty?\": {\n        switch $0[0] {\n        case MV.MalList(let lst, _):\n            return lst.count == 0 ? MV.MalTrue : MV.MalFalse\n        case MV.MalVector(let lst, _):\n            return lst.count == 0 ? MV.MalTrue : MV.MalFalse\n        case MV.MalNil: return MV.MalTrue\n        default: throw MalError.General(msg: \"Invalid empty? call\")\n        }\n    },\n    \"count\": {\n        switch $0[0] {\n        case MV.MalList(let lst, _):   return MV.MalInt(lst.count)\n        case MV.MalVector(let lst, _): return MV.MalInt(lst.count)\n        case MV.MalNil: return MV.MalInt(0)\n        default: throw MalError.General(msg: \"Invalid count call\")\n        }\n    },\n    \"apply\": {\n        let fn: (Array<MalVal>) throws -> MalVal\n        switch $0[0] {\n        case MV.MalFunc(let f, _, _, _, _, _): fn = f\n        default: throw MalError.General(msg: \"Invalid apply call\")\n        }\n\n        var args = Array($0[1..<$0.endIndex-1])\n        switch $0[$0.endIndex-1] {\n        case MV.MalList(let l, _):  args = args + l\n        case MV.MalVector(let l, _): args = args + l\n        default: throw MalError.General(msg: \"Invalid apply call\")\n        }\n\n        return try fn(args)\n    },\n    \"map\": {\n        let fn: (Array<MalVal>) throws -> MalVal\n        switch $0[0] {\n        case MV.MalFunc(let f, _, _, _, _, _): fn = f\n        default: throw MalError.General(msg: \"Invalid map call\")\n        }\n\n        var lst = Array<MalVal>()\n        switch $0[1] {\n        case MV.MalList(let l, _):   lst = l\n        case MV.MalVector(let l, _): lst = l\n        default: throw MalError.General(msg: \"Invalid map call\")\n        }\n\n        var res = Array<MalVal>()\n        for mv in lst {\n            res.append(try fn([mv]))\n        }\n        return list(res)\n    },\n\n    \"conj\": {\n        if $0.count < 1 { throw MalError.General(msg: \"Invalid conj call\") }\n        switch $0[0] {\n        case MV.MalList(let lst, _):\n            let a = Array($0[1..<$0.endIndex]).reversed()\n            return list(a + lst)\n        case MV.MalVector(let lst, _):\n            return vector(lst + $0[1..<$0.endIndex])\n        default: throw MalError.General(msg: \"Invalid conj call\")\n        }\n    },\n    \"seq\": {\n        if $0.count < 1 { throw MalError.General(msg: \"Invalid seq call\") }\n        switch $0[0] {\n        case MV.MalList(let lst, _):\n            if lst.count == 0 { return MV.MalNil }\n            return $0[0]\n        case MV.MalVector(let lst, _):\n            if lst.count == 0 { return MV.MalNil }\n            return list(lst)\n        case MV.MalString(let str):\n            if str.characters.count == 0 { return MV.MalNil }\n            return list(str.characters.map { MV.MalString(String($0)) })\n        case MV.MalNil:\n            return MV.MalNil\n        default: throw MalError.General(msg: \"Invalid seq call\")\n        }\n    },\n\n    \"meta\": {\n        switch $0[0] {\n        case MV.MalList(_, let m):\n            return m != nil ? m![0] : MV.MalNil\n        case MV.MalVector(_, let m):\n            return m != nil ? m![0] : MV.MalNil\n        case MV.MalHashMap(_, let m):\n            return m != nil ? m![0] : MV.MalNil\n        case MV.MalFunc(_, _, _, _, _, let m):\n            return m != nil ? m![0] : MV.MalNil\n        default: throw MalError.General(msg: \"meta called on non-function\")\n        }\n    },\n    \"with-meta\": {\n        switch $0[0] {\n        case MV.MalList(let l, _):\n            return list(l, meta: $0[1])\n        case MV.MalVector(let l, _):\n            return vector(l, meta: $0[1])\n        case MV.MalHashMap(let d, _):\n            return hash_map(d, meta: $0[1])\n        case MV.MalFunc(let f, let a, let e, let p, let m, _):\n            return malfunc(f, ast:a, env:e, params:p, macro:m, meta:$0[1])\n            //return MV.MalFunc(f,ast:a,env:e,params:p,macro:m,meta:[$0[1]])\n        default:\n            throw MalError.General(msg: \"with-meta called on non-collection\")\n        }\n    },\n    \"atom\": {\n        return MV.MalAtom(MutableAtom(val: $0[0]))\n    },\n    \"atom?\": {\n        switch $0[0] {\n        case MV.MalAtom(_): return MV.MalTrue\n        default: return MV.MalFalse\n        }\n    },\n    \"deref\": {\n        switch $0[0] {\n        case MV.MalAtom(let ma): return ma.val\n        default: throw MalError.General(msg: \"Invalid deref call\")\n        }\n    },\n    \"reset!\": {\n        switch $0[0] {\n        case MV.MalAtom(var a):\n            a.val = $0[1]\n            return $0[1]\n        default: throw MalError.General(msg: \"Invalid reset! call\")\n        }\n    },\n    \"swap!\": {\n        switch ($0[0], $0[1]) {\n        case (MV.MalAtom(var a), MV.MalFunc(let fn, _, _, _, _, _)):\n            var args = [a.val]\n            if $0.count > 2 {\n                args = args + Array($0[2..<$0.endIndex])\n            }\n            a.val = try fn(args)\n            return a.val\n        default: throw MalError.General(msg: \"Invalid swap! call\")\n        }\n    },\n]\n"
  },
  {
    "path": "impls/swift3/Sources/env.swift",
    "content": "class Env {\n    var outer: Env? = nil\n    var data: Dictionary<String, MalVal> = [:]\n\n    init(_ outer: Env? = nil, binds: MalVal? = nil,\n                              exprs: MalVal? = nil) throws {\n        self.outer = outer\n\n        if binds != nil {\n            var bs = Array<MalVal>(), es = Array<MalVal>()\n            //print(\"binds: \\(binds), exprs: \\(exprs)\")\n            switch (binds!, exprs!) {\n            case (MalVal.MalList(let l1, _), MalVal.MalList(let l2, _)):\n                bs = l1; es = l2\n            case (MalVal.MalVector(let l1, _), MalVal.MalList(let l2, _)):\n                bs = l1; es = l2\n            default:\n                throw MalError.General(msg: \"invalid Env init call\")\n            }\n\n            var pos = bs.startIndex\n\n            bhandle:\n            while pos < bs.endIndex {\n                let b = bs[pos]\n                switch b {\n                case MalVal.MalSymbol(\"&\"):\n                    switch bs[bs.index(after: pos)] {\n                    case MalVal.MalSymbol(let sym):\n                        if pos < es.endIndex {\n                            let slc = es[pos..<es.endIndex]\n                            data[sym] = list(Array(slc))\n                        } else {\n                            data[sym] = list([])\n                        }\n                        break bhandle\n                    default:\n                        throw MalError.General(msg: \"Env invalid varargs\")\n                    }\n                case MalVal.MalSymbol(let sym):\n                    let e = es[pos]\n                    data[sym] = e\n                default:\n                    throw MalError.General(msg: \"Env binds has non-symbol\")\n                }\n                pos = bs.index(after: pos)\n            }\n        }\n    }\n\n    func get(_ str: String) -> MalVal? {\n        return data[str] ?? outer?.get(str)\n    }\n\n    @discardableResult\n    func set(_ key: MalVal, _ val: MalVal) throws -> MalVal {\n        switch key {\n        case MalVal.MalSymbol(let str):\n            data[str] = val\n            return val\n        default:\n            throw MalError.General(msg: \"invalid Env.find call\")\n        }\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/printer.swift",
    "content": "\nfunc pr_str(_ obj: MalVal, _ print_readably: Bool = true) -> String {\n    switch obj {\n    case MalVal.MalList(let lst, _):\n        let elems = lst.map { pr_str($0, print_readably) }\n        return \"(\" + elems.joined(separator: \" \")  + \")\"\n    case MalVal.MalVector(let lst, _):\n        let elems = lst.map { pr_str($0, print_readably) }\n        return \"[\" + elems.joined(separator: \" \")  + \"]\"\n    case MalVal.MalHashMap(let dict, _):\n        let elems = dict.map {\n            pr_str(MalVal.MalString($0), print_readably) +\n            \" \" + pr_str($1, print_readably)\n        }\n        return \"{\" + elems.joined(separator: \" \")  + \"}\"\n    case MalVal.MalString(let str):\n        //print(\"kw: '\\(str[str.startIndex])'\")\n        if str.characters.count > 0 && str[str.startIndex] == \"\\u{029e}\" {\n            return \":\" + str[str.index(after: str.startIndex)..<str.endIndex]\n        } else if print_readably {\n            let s1 = str.replacingOccurrences(of: \"\\\\\", with: \"\\\\\\\\\")\n            let s2 = s1.replacingOccurrences(of: \"\\\"\", with: \"\\\\\\\"\")\n            let s3 = s2.replacingOccurrences(of: \"\\n\", with: \"\\\\n\")\n            return \"\\\"\" + s3 + \"\\\"\"\n        } else {\n            return str\n        }\n    case MalVal.MalSymbol(let str):\n        return str\n    case MalVal.MalInt(let i): return String(i)\n    case MalVal.MalNil:        return \"nil\"\n    case MalVal.MalFalse:      return \"false\"\n    case MalVal.MalTrue:       return \"true\"\n    case MalVal.MalFunc(_, nil, _, _, _, _):\n        return \"#<native function>\"\n    case MalVal.MalFunc(_, let ast, _, let params, _, _):\n        return \"(fn* \\(pr_str(params![0])) \\(pr_str(ast![0])))\"\n    case MalVal.MalAtom(let ma):\n        return \"(atom \\(pr_str(ma.val, print_readably)))\"\n    default:\n        return String(describing:obj)\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/reader.swift",
    "content": "let token_delim: Set<Character> = [\n    \";\", \",\", \"\\\"\", \"`\", \" \", \"\\n\", \"{\", \"}\", \"(\", \")\", \"[\", \"]\"\n]\n\nlet int_char: Set<Character> = [\n    \"-\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"\n]\n\nlet float_char: Set<Character> = [\n    \".\", \"-\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"\n]\n\nlet whitespace: Set<Character> = [\" \", \"\\t\", \"\\n\", \",\"]\n\nclass Reader {\n    var str: String\n    var pos: String.Index\n    init(_ str: String) {\n        self.str = str\n        pos = str.startIndex\n    }\n    func next() { pos = str.index(after: pos) }\n}\n\nfunc read_int(_ rdr: Reader) -> MalVal {\n    let start = rdr.pos\n    var cidx = rdr.pos\n    while cidx < rdr.str.endIndex {\n        if !int_char.contains(rdr.str[cidx]) { break }\n        cidx = rdr.str.index(after: cidx)\n        rdr.pos = cidx\n    }\n    let matchStr = rdr.str.substring(with: start..<rdr.pos)\n    if matchStr == \"-\" {\n        return MalVal.MalSymbol(\"-\")\n    } else {\n        return MalVal.MalInt(Int(matchStr)!)\n    }\n}\n\nfunc skip_whitespace_and_comments(_ rdr: Reader) {\n    var in_comment = false\n    var cidx = rdr.pos\n    while cidx < rdr.str.endIndex {\n        rdr.pos = cidx\n        if in_comment {\n            if rdr.str[rdr.pos] == \"\\n\" {\n                in_comment = false\n            }\n        } else if rdr.str[rdr.pos] == \";\" {\n            in_comment = true\n        } else {\n            if !whitespace.contains(rdr.str[rdr.pos]) { break }\n        }\n        cidx = rdr.str.index(after: cidx)\n    }\n}\n\nfunc read_string(_ rdr: Reader) throws -> MalVal {\n    let start = rdr.pos\n    var escaped = false\n    if rdr.str[rdr.pos] != \"\\\"\" {\n        throw MalError.Reader(msg: \"read_string call on non-string\")\n    }\n    var cidx = rdr.str.index(after: rdr.pos)\n    while cidx < rdr.str.endIndex {\n        rdr.pos = rdr.str.index(after: cidx)\n        if escaped {\n            escaped = false\n            cidx = rdr.pos\n            continue\n        }\n        if rdr.str[cidx] == \"\\\\\" { escaped = true }\n        if rdr.str[cidx] == \"\\\"\" { break }\n        cidx = rdr.pos\n    }\n    if cidx >= rdr.str.endIndex || rdr.str[rdr.str.index(before: rdr.pos)] != \"\\\"\" {\n        throw MalError.Reader(msg: \"Expected '\\\"', got EOF\")\n    }\n    let matchStr = rdr.str.substring(with: \n        rdr.str.index(after: start)..<rdr.str.index(before: rdr.pos))\n    let s0 = matchStr.replacingOccurrences(of: \"\\\\\\\\\", with: \"\\u{029e}\")\n    let s1 = s0.replacingOccurrences(of: \"\\\\\\\"\", with: \"\\\"\")\n    let s2 = s1.replacingOccurrences(of: \"\\\\n\", with: \"\\n\")\n    let s3 = s2.replacingOccurrences(of: \"\\u{029e}\", with: \"\\\\\")\n    return MalVal.MalString(s3)\n}\n\nfunc read_token(_ rdr: Reader) -> String {\n    let start = rdr.pos\n    var cidx = rdr.pos\n    while cidx < rdr.str.endIndex {\n        rdr.pos = cidx\n        if token_delim.contains(rdr.str[cidx]) { break }\n        cidx = rdr.str.index(after: cidx)\n        rdr.pos = cidx\n    }\n    return rdr.str.substring(with: start..<rdr.pos)\n}\n\nfunc read_symbol(_ rdr: Reader) throws -> MalVal {\n   let tok = read_token(rdr)\n    switch tok {\n        case \"nil\": return MalVal.MalNil\n        case \"true\": return MalVal.MalTrue\n        case \"false\": return MalVal.MalFalse\n        default: return MalVal.MalSymbol(tok)\n    }\n}\n\nfunc read_atom(_ rdr: Reader) throws -> MalVal {\n    if rdr.str.characters.count == 0 {\n        throw MalError.Reader(msg: \"Empty string passed to read_atom\")\n    }\n    switch rdr.str[rdr.pos] {\n    case \"-\" where rdr.str.characters.count == 1 || !int_char.contains(rdr.str[rdr.str.index(after: rdr.pos)]):\n        return try read_symbol(rdr)\n    case let c where int_char.contains(c):\n        return read_int(rdr)\n    case \"\\\"\":\n        return try read_string(rdr)\n    case \":\":\n        rdr.next()\n        return MalVal.MalString(\"\\u{029e}\\(read_token(rdr))\")\n    default:\n        return try read_symbol(rdr)\n    }\n}\n\nfunc read_list(_ rdr: Reader, start: Character = \"(\", end: Character = \")\") throws -> Array<MalVal> {\n    if rdr.str[rdr.pos] != start {\n        throw MalError.Reader(msg: \"expected '\\(start)'\")\n    }\n    rdr.next()\n    skip_whitespace_and_comments(rdr)\n    var lst: [MalVal] = []\n    while rdr.pos < rdr.str.endIndex {\n        if (rdr.str[rdr.pos] == end) { break }\n        lst.append(try read_form(rdr))\n    }\n    if rdr.pos >= rdr.str.endIndex {\n        throw MalError.Reader(msg: \"Expected '\\(end)', got EOF\")\n    }\n    rdr.next()\n    return lst\n}\n\nfunc read_form(_ rdr: Reader) throws -> MalVal {\n    if rdr.str.characters.count == 0 {\n        throw MalError.Reader(msg: \"Empty string passed to read_form\")\n    }\n    //print(\"read_form: \\(rdr.pos): \\(rdr.str[rdr.pos])\")\n    skip_whitespace_and_comments(rdr)\n    var res: MalVal\n    switch rdr.str[rdr.pos] {\n    // reader macros/transforms\n    case \"'\":\n        rdr.next()\n        return list([MalVal.MalSymbol(\"quote\"), try read_form(rdr)])\n    case \"`\":\n        rdr.next()\n        return list([MalVal.MalSymbol(\"quasiquote\"), try read_form(rdr)])\n    case \"~\":\n        switch rdr.str[rdr.str.index(after: rdr.pos)] {\n        case \"@\":\n            rdr.next()\n            rdr.next()\n            return list([MalVal.MalSymbol(\"splice-unquote\"),\n                         try read_form(rdr)])\n        default:\n            rdr.next()\n            return list([MalVal.MalSymbol(\"unquote\"),\n                         try read_form(rdr)])\n        }\n    case \"^\":\n        rdr.next()\n        let meta = try read_form(rdr)\n        return list([MalVal.MalSymbol(\"with-meta\"),\n                     try read_form(rdr),\n                     meta])\n    case \"@\":\n        rdr.next()\n        return list([MalVal.MalSymbol(\"deref\"),\n                     try read_form(rdr)])\n\n    // list\n    case \"(\": res = list(try read_list(rdr))\n    case \")\": throw MalError.Reader(msg: \"unexpected ')'\")\n\n    // vector\n    case \"[\": res = vector(try read_list(rdr, start: \"[\", end: \"]\"))\n    case \"]\": throw MalError.Reader(msg: \"unexpected ']'\")\n\n    // hash-map\n    case \"{\": res = try hash_map(try read_list(rdr, start: \"{\", end: \"}\"))\n    case \"}\": throw MalError.Reader(msg: \"unexpected '}'\")\n\n    // atom\n    default: res = try read_atom(rdr)\n    }\n    skip_whitespace_and_comments(rdr)\n    return res\n}\n\nfunc read_str(_ str: String) throws -> MalVal {\n    return try read_form(Reader(str))\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step0_repl/main.swift",
    "content": "import Foundation\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    print(\"\\(line!)\")\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step1_read_print/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\nfunc EVAL(_ ast: MalVal, _ env: String) throws -> MalVal {\n    return ast\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), \"\"))\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step2_eval/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\nfunc EVAL(_ ast: MalVal, _ env: Dictionary<String, MalVal>) throws -> MalVal {\n    /* print(\"EVAL: \" + PRINT(ast)) */\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env[sym] {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n    default:\n        return ast\n    }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nfunc IntOp(_ op: (Int, Int) -> Int, _ a: MalVal, _ b: MalVal) throws -> MalVal {\n    switch (a, b) {\n    case (MalVal.MalInt(let i1), MalVal.MalInt(let i2)):\n        return MalVal.MalInt(op(i1, i2))\n    default:\n        throw MalError.General(msg: \"Invalid IntOp call\")\n    }\n}\n\nvar repl_env: Dictionary<String,MalVal> = [\n    \"+\": malfunc({ try IntOp({ $0 + $1}, $0[0], $0[1]) }),\n    \"-\": malfunc({ try IntOp({ $0 - $1}, $0[0], $0[1]) }),\n    \"*\": malfunc({ try IntOp({ $0 * $1}, $0[0], $0[1]) }),\n    \"/\": malfunc({ try IntOp({ $0 / $1}, $0[0], $0[1]) }),\n]\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step3_env/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\nfunc EVAL(_ ast: MalVal, _ env: Env) throws -> MalVal {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            return try EVAL(lst[2], let_env)\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nfunc IntOp(_ op: (Int, Int) -> Int, _ a: MalVal, _ b: MalVal) throws -> MalVal {\n    switch (a, b) {\n    case (MalVal.MalInt(let i1), MalVal.MalInt(let i2)):\n        return MalVal.MalInt(op(i1, i2))\n    default:\n        throw MalError.General(msg: \"Invalid IntOp call\")\n    }\n}\n\nvar repl_env: Env = try Env()\ntry repl_env.set(MalVal.MalSymbol(\"+\"),\n                 malfunc({ try IntOp({ $0 + $1}, $0[0], $0[1]) }))\ntry repl_env.set(MalVal.MalSymbol(\"-\"),\n                 malfunc({ try IntOp({ $0 - $1}, $0[0], $0[1]) }))\ntry repl_env.set(MalVal.MalSymbol(\"*\"),\n                 malfunc({ try IntOp({ $0 * $1}, $0[0], $0[1]) }))\ntry repl_env.set(MalVal.MalSymbol(\"/\"),\n                 malfunc({ try IntOp({ $0 / $1}, $0[0], $0[1]) }))\n\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step4_if_fn_do/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\nfunc EVAL(_ ast: MalVal, _ env: Env) throws -> MalVal {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            return try EVAL(lst[2], let_env)\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            return try EVAL(lst[lst.index(before: lst.endIndex)], env)\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    return try EVAL(lst[3], env)\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                return try EVAL(lst[2], env)\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            })\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\n\n// core.mal: defined using the language itself\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\n\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step5_tco/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\nfunc EVAL(_ orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {\n  var ast = orig_ast, env = orig_env\n  while true {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            env = let_env\n            ast = lst[2] // TCO\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            ast = lst[lst.index(before: lst.endIndex)] // TCO\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    ast = lst[3] // TCO\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                ast = lst[2] // TCO\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            }, ast:[lst[2]], env:env, params:[lst[1]])\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                case MalVal.MalFunc(_, let a, let e, let p, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    env = try Env(e, binds: p![0],\n                                     exprs: list(args)) // TCO\n                    ast = a![0] // TCO\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n  }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\n\n// core.mal: defined using the language itself\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\n\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step6_file/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\nfunc EVAL(_ orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {\n  var ast = orig_ast, env = orig_env\n  while true {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            env = let_env\n            ast = lst[2] // TCO\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            ast = lst[lst.index(before: lst.endIndex)] // TCO\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    ast = lst[3] // TCO\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                ast = lst[2] // TCO\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            }, ast:[lst[2]], env:env, params:[lst[1]])\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                case MalVal.MalFunc(_, let a, let e, let p, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    env = try Env(e, binds: p![0],\n                                     exprs: list(args)) // TCO\n                    ast = a![0] // TCO\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n  }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\ntry repl_env.set(MalVal.MalSymbol(\"eval\"),\n                 malfunc({ try EVAL($0[0], repl_env) }))\nlet pargs = CommandLine.arguments.map { MalVal.MalString($0) }\n// TODO: weird way to get empty list, fix this\nvar args = pargs[pargs.startIndex..<pargs.startIndex]\nif pargs.index(pargs.startIndex, offsetBy:2) < pargs.endIndex {\n    args = pargs[pargs.index(pargs.startIndex, offsetBy:2)..<pargs.endIndex]\n}\ntry repl_env.set(MalVal.MalSymbol(\"*ARGV*\"), list(Array(args)))\n\n// core.mal: defined using the language itself\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n\nif CommandLine.arguments.count > 1 {\n    try rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\")\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step7_quote/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\n\nfunc starts_with(_ ast: MalVal, _ sym: String) -> MalVal? {\n    switch ast {\n    case MalVal.MalList(let lst, _) where 1 < lst.count:\n        switch lst[0] {\n        case MalVal.MalSymbol(sym):\n            return lst[1]\n        default:\n            return nil\n        }\n    default:\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalVal]) -> MalVal {\n    var result = list([])\n    for elt in lst.reversed() {\n         if let elt1 = starts_with(elt, \"splice-unquote\") {\n             result = list([MalVal.MalSymbol(\"concat\"), elt1, result])\n         } else {\n             result = list([MalVal.MalSymbol(\"cons\"), quasiquote(elt), result])\n         }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalVal) -> MalVal {\n    if let a1 = starts_with(ast, \"unquote\") {\n        return a1\n    }\n    switch ast {\n    case MalVal.MalList(let lst, _):\n        return qqIter(lst)\n    case MalVal.MalVector(let lst, _):\n        return list([MalVal.MalSymbol(\"vec\"), qqIter(lst)])\n    case MalVal.MalSymbol:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    case MalVal.MalHashMap:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {\n  var ast = orig_ast, env = orig_env\n  while true {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            env = let_env\n            ast = lst[2] // TCO\n        case MalVal.MalSymbol(\"quote\"):\n            return lst[1]\n        case MalVal.MalSymbol(\"quasiquote\"):\n            ast = quasiquote(lst[1]) // TCO\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            ast = lst[lst.index(before: lst.endIndex)] // TCO\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    ast = lst[3] // TCO\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                ast = lst[2] // TCO\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            }, ast:[lst[2]], env:env, params:[lst[1]])\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                case MalVal.MalFunc(_, let a, let e, let p, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    env = try Env(e, binds: p![0],\n                                     exprs: list(args)) // TCO\n                    ast = a![0] // TCO\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n  }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\ntry repl_env.set(MalVal.MalSymbol(\"eval\"),\n                 malfunc({ try EVAL($0[0], repl_env) }))\nlet pargs = CommandLine.arguments.map { MalVal.MalString($0) }\n// TODO: weird way to get empty list, fix this\nvar args = pargs[pargs.startIndex..<pargs.startIndex]\nif pargs.index(pargs.startIndex, offsetBy:2) < pargs.endIndex {\n    args = pargs[pargs.index(pargs.startIndex, offsetBy:2)..<pargs.endIndex]\n}\ntry repl_env.set(MalVal.MalSymbol(\"*ARGV*\"), list(Array(args)))\n\n// core.mal: defined using the language itself\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n\nif CommandLine.arguments.count > 1 {\n    try rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\")\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step8_macros/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\n\nfunc starts_with(_ ast: MalVal, _ sym: String) -> MalVal? {\n    switch ast {\n    case MalVal.MalList(let lst, _) where 1 < lst.count:\n        switch lst[0] {\n        case MalVal.MalSymbol(sym):\n            return lst[1]\n        default:\n            return nil\n        }\n    default:\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalVal]) -> MalVal {\n    var result = list([])\n    for elt in lst.reversed() {\n         if let elt1 = starts_with(elt, \"splice-unquote\") {\n             result = list([MalVal.MalSymbol(\"concat\"), elt1, result])\n         } else {\n             result = list([MalVal.MalSymbol(\"cons\"), quasiquote(elt), result])\n         }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalVal) -> MalVal {\n    if let a1 = starts_with(ast, \"unquote\") {\n        return a1\n    }\n    switch ast {\n    case MalVal.MalList(let lst, _):\n        return qqIter(lst)\n    case MalVal.MalVector(let lst, _):\n        return list([MalVal.MalSymbol(\"vec\"), qqIter(lst)])\n    case MalVal.MalSymbol:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    case MalVal.MalHashMap:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {\n  var ast = orig_ast, env = orig_env\n  while true {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            env = let_env\n            ast = lst[2] // TCO\n        case MalVal.MalSymbol(\"quote\"):\n            return lst[1]\n        case MalVal.MalSymbol(\"quasiquote\"):\n            ast = quasiquote(lst[1]) // TCO\n        case MalVal.MalSymbol(\"defmacro!\"):\n            var mac = try EVAL(lst[2], env)\n            switch mac {\n            case MalVal.MalFunc(let fn, let a, let e, let p, _, let m):\n                mac = malfunc(fn,ast:a,env:e,params:p,macro:true,meta:m)\n            default: throw MalError.General(msg: \"invalid defmacro! form\")\n            }\n            return try env.set(lst[1], mac)\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            ast = lst[lst.index(before: lst.endIndex)] // TCO\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    ast = lst[3] // TCO\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                ast = lst[2] // TCO\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            }, ast:[lst[2]], env:env, params:[lst[1]])\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, _, _, _, true, _):\n                    ast = try fn(Array(raw_args)) // TCO\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                case MalVal.MalFunc(_, let a, let e, let p, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    env = try Env(e, binds: p![0],\n                                     exprs: list(args)) // TCO\n                    ast = a![0] // TCO\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n  }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\ntry repl_env.set(MalVal.MalSymbol(\"eval\"),\n                 malfunc({ try EVAL($0[0], repl_env) }))\nlet pargs = CommandLine.arguments.map { MalVal.MalString($0) }\n// TODO: weird way to get empty list, fix this\nvar args = pargs[pargs.startIndex..<pargs.startIndex]\nif pargs.index(pargs.startIndex, offsetBy:2) < pargs.endIndex {\n    args = pargs[pargs.index(pargs.startIndex, offsetBy:2)..<pargs.endIndex]\n}\ntry repl_env.set(MalVal.MalSymbol(\"*ARGV*\"), list(Array(args)))\n\n// core.mal: defined using the language itself\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\ntry rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif CommandLine.arguments.count > 1 {\n    try rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\")\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/step9_try/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\n\nfunc starts_with(_ ast: MalVal, _ sym: String) -> MalVal? {\n    switch ast {\n    case MalVal.MalList(let lst, _) where 1 < lst.count:\n        switch lst[0] {\n        case MalVal.MalSymbol(sym):\n            return lst[1]\n        default:\n            return nil\n        }\n    default:\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalVal]) -> MalVal {\n    var result = list([])\n    for elt in lst.reversed() {\n         if let elt1 = starts_with(elt, \"splice-unquote\") {\n             result = list([MalVal.MalSymbol(\"concat\"), elt1, result])\n         } else {\n             result = list([MalVal.MalSymbol(\"cons\"), quasiquote(elt), result])\n         }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalVal) -> MalVal {\n    if let a1 = starts_with(ast, \"unquote\") {\n        return a1\n    }\n    switch ast {\n    case MalVal.MalList(let lst, _):\n        return qqIter(lst)\n    case MalVal.MalVector(let lst, _):\n        return list([MalVal.MalSymbol(\"vec\"), qqIter(lst)])\n    case MalVal.MalSymbol:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    case MalVal.MalHashMap:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {\n  var ast = orig_ast, env = orig_env\n  while true {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            env = let_env\n            ast = lst[2] // TCO\n        case MalVal.MalSymbol(\"quote\"):\n            return lst[1]\n        case MalVal.MalSymbol(\"quasiquote\"):\n            ast = quasiquote(lst[1]) // TCO\n        case MalVal.MalSymbol(\"defmacro!\"):\n            var mac = try EVAL(lst[2], env)\n            switch mac {\n            case MalVal.MalFunc(let fn, let a, let e, let p, _, let m):\n                mac = malfunc(fn,ast:a,env:e,params:p,macro:true,meta:m)\n            default: throw MalError.General(msg: \"invalid defmacro! form\")\n            }\n            return try env.set(lst[1], mac)\n        case MalVal.MalSymbol(\"try*\"):\n            do {\n                return try EVAL(_nth(ast, 1), env)\n            } catch (let exc) {\n                if lst.count > 2 {\n                    let a2 = lst[2]\n                    switch a2 {\n                    case MalVal.MalList(let a2lst, _):\n                        let a20 = a2lst[0]\n                        switch a20 {\n                        case MalVal.MalSymbol(\"catch*\"):\n                            if a2lst.count < 3 { return MalVal.MalNil }\n                            let a21 = a2lst[1], a22 = a2lst[2]\n                            var err: MalVal\n                            switch exc {\n                            case MalError.Reader(let msg):\n                                err = MalVal.MalString(msg)\n                            case MalError.General(let msg):\n                                err = MalVal.MalString(msg)\n                            case MalError.MalException(let obj):\n                                err = obj\n                            default:\n                                err = MalVal.MalString(String(describing:exc))\n                            }\n                            return try EVAL(a22, Env(env, binds: list([a21]),\n                                                          exprs: list([err])))\n                        default: break\n                        }\n                    default: break\n                    }\n                }\n                throw exc\n            }\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            ast = lst[lst.index(before: lst.endIndex)] // TCO\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    ast = lst[3] // TCO\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                ast = lst[2] // TCO\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            }, ast:[lst[2]], env:env, params:[lst[1]])\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, _, _, _, true, _):\n                    ast = try fn(Array(raw_args)) // TCO\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                case MalVal.MalFunc(_, let a, let e, let p, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    env = try Env(e, binds: p![0],\n                                     exprs: list(args)) // TCO\n                    ast = a![0] // TCO\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n  }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\ntry repl_env.set(MalVal.MalSymbol(\"eval\"),\n                 malfunc({ try EVAL($0[0], repl_env) }))\nlet pargs = CommandLine.arguments.map { MalVal.MalString($0) }\n// TODO: weird way to get empty list, fix this\nvar args = pargs[pargs.startIndex..<pargs.startIndex]\nif pargs.index(pargs.startIndex, offsetBy:2) < pargs.endIndex {\n    args = pargs[pargs.index(pargs.startIndex, offsetBy:2)..<pargs.endIndex]\n}\ntry repl_env.set(MalVal.MalSymbol(\"*ARGV*\"), list(Array(args)))\n\n// core.mal: defined using the language itself\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\ntry rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif CommandLine.arguments.count > 1 {\n    try rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\")\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/stepA_mal/main.swift",
    "content": "import Foundation\n\n// read\nfunc READ(_ str: String) throws -> MalVal {\n    return try read_str(str)\n}\n\n// eval\n\nfunc starts_with(_ ast: MalVal, _ sym: String) -> MalVal? {\n    switch ast {\n    case MalVal.MalList(let lst, _) where 1 < lst.count:\n        switch lst[0] {\n        case MalVal.MalSymbol(sym):\n            return lst[1]\n        default:\n            return nil\n        }\n    default:\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalVal]) -> MalVal {\n    var result = list([])\n    for elt in lst.reversed() {\n         if let elt1 = starts_with(elt, \"splice-unquote\") {\n             result = list([MalVal.MalSymbol(\"concat\"), elt1, result])\n         } else {\n             result = list([MalVal.MalSymbol(\"cons\"), quasiquote(elt), result])\n         }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalVal) -> MalVal {\n    if let a1 = starts_with(ast, \"unquote\") {\n        return a1\n    }\n    switch ast {\n    case MalVal.MalList(let lst, _):\n        return qqIter(lst)\n    case MalVal.MalVector(let lst, _):\n        return list([MalVal.MalSymbol(\"vec\"), qqIter(lst)])\n    case MalVal.MalSymbol:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    case MalVal.MalHashMap:\n        return list([MalVal.MalSymbol(\"quote\"), ast])\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ orig_ast: MalVal, _ orig_env: Env) throws -> MalVal {\n  var ast = orig_ast, env = orig_env\n  while true {\n    if let dbgeval = env.get(\"DEBUG-EVAL\") {\n        switch dbgeval {\n        case MalVal.MalFalse, MalVal.MalNil: break\n        default: print(\"EVAL: \" + PRINT(ast))\n        }\n    }\n    switch ast {\n    case MalVal.MalSymbol(let sym):\n        if let value = env.get(sym) {\n            return value\n        } else {\n            throw MalError.General(msg: \"'\\(sym)' not found\")\n        }\n    case MalVal.MalVector(let lst, _):\n        return vector(try lst.map { try EVAL($0, env) })\n    case MalVal.MalHashMap(let dict, _):\n        var new_dict = Dictionary<String,MalVal>()\n        for (k,v) in dict { new_dict[k] = try EVAL(v, env) }\n        return hash_map(new_dict)\n    case MalVal.MalList(let lst, _):\n        if lst.count == 0 { return ast }\n        switch lst[0] {\n        case MalVal.MalSymbol(\"def!\"):\n            return try env.set(lst[1], try EVAL(lst[2], env))\n        case MalVal.MalSymbol(\"let*\"):\n            let let_env = try Env(env)\n            var binds = Array<MalVal>()\n            switch lst[1] {\n            case MalVal.MalList(let l, _): binds = l\n            case MalVal.MalVector(let l, _): binds = l\n            default:\n                throw MalError.General(msg: \"Invalid let* bindings\")\n            }\n            var idx = binds.startIndex\n            while idx < binds.endIndex {\n                let v = try EVAL(binds[binds.index(after: idx)], let_env)\n                try let_env.set(binds[idx], v)\n                idx = binds.index(idx, offsetBy: 2)\n            }\n            env = let_env\n            ast = lst[2] // TCO\n        case MalVal.MalSymbol(\"quote\"):\n            return lst[1]\n        case MalVal.MalSymbol(\"quasiquote\"):\n            ast = quasiquote(lst[1]) // TCO\n        case MalVal.MalSymbol(\"defmacro!\"):\n            var mac = try EVAL(lst[2], env)\n            switch mac {\n            case MalVal.MalFunc(let fn, let a, let e, let p, _, let m):\n                mac = malfunc(fn,ast:a,env:e,params:p,macro:true,meta:m)\n            default: throw MalError.General(msg: \"invalid defmacro! form\")\n            }\n            return try env.set(lst[1], mac)\n        case MalVal.MalSymbol(\"try*\"):\n            do {\n                return try EVAL(_nth(ast, 1), env)\n            } catch (let exc) {\n                if lst.count > 2 {\n                    let a2 = lst[2]\n                    switch a2 {\n                    case MalVal.MalList(let a2lst, _):\n                        let a20 = a2lst[0]\n                        switch a20 {\n                        case MalVal.MalSymbol(\"catch*\"):\n                            if a2lst.count < 3 { return MalVal.MalNil }\n                            let a21 = a2lst[1], a22 = a2lst[2]\n                            var err: MalVal\n                            switch exc {\n                            case MalError.Reader(let msg):\n                                err = MalVal.MalString(msg)\n                            case MalError.General(let msg):\n                                err = MalVal.MalString(msg)\n                            case MalError.MalException(let obj):\n                                err = obj\n                            default:\n                                err = MalVal.MalString(String(describing:exc))\n                            }\n                            return try EVAL(a22, Env(env, binds: list([a21]),\n                                                          exprs: list([err])))\n                        default: break\n                        }\n                    default: break\n                    }\n                }\n                throw exc\n            }\n        case MalVal.MalSymbol(\"do\"):\n            let slc = lst[1..<lst.index(before: lst.endIndex)]\n            for item in slc {\n                _ = try EVAL(item, env)\n            }\n            ast = lst[lst.index(before: lst.endIndex)] // TCO\n        case MalVal.MalSymbol(\"if\"):\n            switch try EVAL(lst[1], env) {\n            case MalVal.MalFalse, MalVal.MalNil:\n                if lst.count > 3 {\n                    ast = lst[3] // TCO\n                } else {\n                    return MalVal.MalNil\n                }\n            default:\n                ast = lst[2] // TCO\n            }\n        case MalVal.MalSymbol(\"fn*\"):\n            return malfunc( {\n                return try EVAL(lst[2], Env(env, binds: lst[1],\n                                                 exprs: list($0)))\n            }, ast:[lst[2]], env:env, params:[lst[1]])\n        default:\n                let raw_args = lst[1..<lst.count]\n                switch try EVAL(lst[0], env) {\n                case MalVal.MalFunc(let fn, _, _, _, true, _):\n                    ast = try fn(Array(raw_args)) // TCO\n                case MalVal.MalFunc(let fn, nil, _, _, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    return try fn(args)\n                case MalVal.MalFunc(_, let a, let e, let p, _, _):\n                    let args = try raw_args.map { try EVAL($0, env) }\n                    env = try Env(e, binds: p![0],\n                                     exprs: list(args)) // TCO\n                    ast = a![0] // TCO\n                default:\n                    throw MalError.General(msg: \"Cannot apply on '\\(lst[0])'\")\n                }\n        }\n    default:\n        return ast\n    }\n  }\n}\n\n// print\nfunc PRINT(_ exp: MalVal) -> String {\n    return pr_str(exp, true)\n}\n\n\n// repl\n@discardableResult\nfunc rep(_ str:String) throws -> String {\n    return PRINT(try EVAL(try READ(str), repl_env))\n}\n\nvar repl_env: Env = try Env()\n\n// core.swift: defined using Swift\nfor (k, fn) in core_ns {\n    try repl_env.set(MalVal.MalSymbol(k), malfunc(fn))\n}\ntry repl_env.set(MalVal.MalSymbol(\"eval\"),\n                 malfunc({ try EVAL($0[0], repl_env) }))\nlet pargs = CommandLine.arguments.map { MalVal.MalString($0) }\n// TODO: weird way to get empty list, fix this\nvar args = pargs[pargs.startIndex..<pargs.startIndex]\nif pargs.index(pargs.startIndex, offsetBy:2) < pargs.endIndex {\n    args = pargs[pargs.index(pargs.startIndex, offsetBy:2)..<pargs.endIndex]\n}\ntry repl_env.set(MalVal.MalSymbol(\"*ARGV*\"), list(Array(args)))\n\n// core.mal: defined using the language itself\ntry rep(\"(def! *host-language* \\\"swift\\\")\")\ntry rep(\"(def! not (fn* (a) (if a false true)))\")\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\ntry rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n\nif CommandLine.arguments.count > 1 {\n    try rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\")\n    exit(0)\n}\n\ntry rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    let line = readLine(strippingNewline: true)\n    if line == nil { break }\n    if line == \"\" { continue }\n\n    do {\n        print(try rep(line!))\n    } catch (MalError.Reader(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.General(let msg)) {\n        print(\"Error: \\(msg)\")\n    } catch (MalError.MalException(let obj)) {\n        print(\"Error: \\(pr_str(obj, true))\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/Sources/types.swift",
    "content": "\nenum MalError: Error {\n    case Reader(msg: String)\n    case General(msg: String)\n    case MalException(obj: MalVal)\n}\n\nclass MutableAtom {\n    var val: MalVal\n    init(val: MalVal) {\n        self.val = val\n    }\n}\n\nenum MalVal {\n    case MalNil\n    case MalTrue\n    case MalFalse\n    case MalInt(Int)\n    case MalFloat(Float)\n    case MalString(String)\n    case MalSymbol(String)\n    case MalList(Array<MalVal>, meta: Array<MalVal>?)\n    case MalVector(Array<MalVal>, meta: Array<MalVal>?)\n    case MalHashMap(Dictionary<String,MalVal>, meta: Array<MalVal>?)\n    // TODO: internal MalVals are wrapped in arrays because otherwise\n    // compiler throws a fault\n    case MalFunc((Array<MalVal>) throws -> MalVal,\n                 ast: Array<MalVal>?,\n                 env: Env?,\n                 params: Array<MalVal>?,\n                 macro: Bool,\n                 meta: Array<MalVal>?)\n    case MalAtom(MutableAtom)\n}\n\ntypealias MV = MalVal\n\n// General functions\n\nfunc wraptf(_ a: Bool) -> MalVal {\n    return a ? MV.MalTrue : MV.MalFalse\n}\n\n\n// equality functions\nfunc cmp_seqs(_ a: Array<MalVal>, _ b: Array<MalVal>) -> Bool {\n    if a.count != b.count { return false }\n    var idx = a.startIndex\n    while idx < a.endIndex {\n        if !equal_Q(a[idx], b[idx]) { return false }\n        idx = a.index(after:idx)\n    }\n    return true\n}\n\nfunc cmp_maps(_ a: Dictionary<String,MalVal>,\n              _ b: Dictionary<String,MalVal>) -> Bool {\n    if a.count != b.count { return false }\n    for (k,v1) in a {\n        if b[k] == nil { return false }\n        if !equal_Q(v1, b[k]!) { return false }\n    }\n    return true\n}\n\nfunc equal_Q(_ a: MalVal, _ b: MalVal) -> Bool {\n    switch (a, b) {\n    case (MV.MalNil, MV.MalNil): return true\n    case (MV.MalFalse, MV.MalFalse): return true\n    case (MV.MalTrue, MV.MalTrue): return true\n    case (MV.MalInt(let i1), MV.MalInt(let i2)): return i1 == i2\n    case (MV.MalString(let s1), MV.MalString(let s2)): return s1 == s2\n    case (MV.MalSymbol(let s1), MV.MalSymbol(let s2)): return s1 == s2\n    case (MV.MalList(let l1,_), MV.MalList(let l2,_)):\n        return cmp_seqs(l1, l2)\n    case (MV.MalList(let l1,_), MV.MalVector(let l2,_)):\n        return cmp_seqs(l1, l2)\n    case (MV.MalVector(let l1,_), MV.MalList(let l2,_)):\n        return cmp_seqs(l1, l2)\n    case (MV.MalVector(let l1,_), MV.MalVector(let l2,_)):\n        return cmp_seqs(l1, l2)\n    case (MV.MalHashMap(let d1,_), MV.MalHashMap(let d2,_)):\n        return cmp_maps(d1, d2)\n    default:\n        return false\n    }\n}\n\n// list and vector functions\nfunc list(_ lst: Array<MalVal>) -> MalVal {\n    return MV.MalList(lst, meta:nil)\n}\nfunc list(_ lst: Array<MalVal>, meta: MalVal) -> MalVal {\n    return MV.MalList(lst, meta:[meta])\n}\n\nfunc vector(_ lst: Array<MalVal>) -> MalVal {\n    return MV.MalVector(lst, meta:nil)\n}\nfunc vector(_ lst: Array<MalVal>, meta: MalVal) -> MalVal {\n    return MV.MalVector(lst, meta:[meta])\n}\n\n\n// hash-map functions\n\nfunc _assoc(_ src: Dictionary<String,MalVal>, _ mvs: Array<MalVal>)\n        throws -> Dictionary<String,MalVal> {\n    var d = src\n    if mvs.count % 2 != 0 {\n        throw MalError.General(msg: \"Odd number of args to assoc_BANG\")\n    }\n    var pos = mvs.startIndex\n    while pos < mvs.count {\n        switch (mvs[pos], mvs[pos+1]) {\n        case (MV.MalString(let k), let mv):\n            d[k] = mv\n        default:\n            throw MalError.General(msg: \"Invalid _assoc call\")\n        }\n        pos += 2\n    }\n    return d\n}\n\nfunc _dissoc(_ src: Dictionary<String,MalVal>, _ mvs: Array<MalVal>)\n        throws -> Dictionary<String,MalVal> {\n    var d = src\n    for mv in mvs {\n        switch mv {\n        case MV.MalString(let k): d.removeValue(forKey: k)\n        default: throw MalError.General(msg: \"Invalid _dissoc call\")\n        }\n    }\n    return d\n}\n\n\nfunc hash_map(_ dict: Dictionary<String,MalVal>) -> MalVal {\n    return MV.MalHashMap(dict, meta:nil)\n}\n\nfunc hash_map(_ dict: Dictionary<String,MalVal>, meta:MalVal) -> MalVal {\n    return MV.MalHashMap(dict, meta:[meta])\n}\n\nfunc hash_map(_ arr: Array<MalVal>) throws -> MalVal {\n    let d = Dictionary<String,MalVal>();\n    return MV.MalHashMap(try _assoc(d, arr), meta:nil)\n}\n\n\n// function functions\nfunc malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal) -> MalVal {\n    return MV.MalFunc(fn, ast: nil, env: nil, params: nil,\n                      macro: false, meta: nil)\n}\nfunc malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,\n             ast: Array<MalVal>?,\n             env: Env?,\n             params: Array<MalVal>?) -> MalVal {\n    return MV.MalFunc(fn, ast: ast, env: env, params: params,\n                      macro: false, meta: nil)\n}\nfunc malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,\n             ast: Array<MalVal>?,\n             env: Env?,\n             params: Array<MalVal>?,\n             macro: Bool,\n             meta: MalVal?) -> MalVal {\n    return MV.MalFunc(fn, ast: ast, env: env, params: params,\n                      macro: macro, meta: meta != nil ? [meta!] : nil)\n}\nfunc malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,\n             ast: Array<MalVal>?,\n             env: Env?,\n             params: Array<MalVal>?,\n             macro: Bool,\n             meta: Array<MalVal>?) -> MalVal {\n    return MV.MalFunc(fn, ast: ast, env: env, params: params,\n                      macro: macro, meta: meta)\n}\n\n// sequence functions\n\nfunc _rest(_ a: MalVal) throws -> Array<MalVal> {\n    switch a {\n    case MV.MalList(let lst,_):\n        let start = lst.index(after: lst.startIndex)\n        let slc = lst[start..<lst.endIndex]\n        return Array(slc)\n    case MV.MalVector(let lst,_):\n        let start = lst.index(after: lst.startIndex)\n        let slc = lst[start..<lst.endIndex]\n        return Array(slc)\n    default:\n        throw MalError.General(msg: \"Invalid rest call\")\n    }\n}\n\nfunc rest(_ a: MalVal) throws -> MalVal {\n    return list(try _rest(a))\n}\n\nfunc _nth(_     a: MalVal, _ idx: Int) throws -> MalVal {\n    switch a {\n    case MV.MalList(let l,_): return l[l.startIndex.advanced(by: idx)]\n    case MV.MalVector(let l,_): return l[l.startIndex.advanced(by: idx)]\n    default: throw MalError.General(msg: \"Invalid nth call\")\n    }\n}\n"
  },
  {
    "path": "impls/swift3/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/swift3/tests/step5_tco.mal",
    "content": ";; Swift 3: skipping non-TCO recursion\n;; Reason: unrecoverable segfault at 10,000\n"
  },
  {
    "path": "impls/swift4/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Swift\nRUN apt-get -y install clang-3.6 cmake pkg-config \\\n    git ninja-build uuid-dev libicu-dev icu-devtools \\\n    libbsd-dev libedit-dev libxml2-dev libsqlite3-dev \\\n    swig libpython-dev libncurses5-dev\n\n# TODO: better way to do this?\nRUN ln -sf /usr/lib/llvm-3.6/bin/clang++ /usr/bin/clang++\nRUN ln -sf /usr/lib/llvm-3.6/bin/clang /usr/bin/clang\n\nENV SWIFT_PREFIX swift-4.2.4-RELEASE\nENV SWIFT_RELEASE ${SWIFT_PREFIX}-ubuntu16.04\n\nRUN cd /opt && \\\n    curl -O https://download.swift.org/swift-4.2.4-release/ubuntu1604/${SWIFT_PREFIX}/${SWIFT_RELEASE}.tar.gz && \\\n    tar xvzf ${SWIFT_RELEASE}.tar.gz && \\\n    rm ${SWIFT_RELEASE}.tar.gz\n\nENV PATH /opt/${SWIFT_RELEASE}/usr/bin/:$PATH\n\n\n"
  },
  {
    "path": "impls/swift4/Makefile",
    "content": "ifneq ($(shell which xcrun),)\n\tSWIFT = xcrun -sdk macosx swiftc\nelse\n\tSWIFT = swiftc\nendif\n\nSTEP3_DEPS = Sources/types.swift Sources/reader.swift Sources/printer.swift Sources/env.swift\nSTEP4_DEPS = $(STEP3_DEPS) Sources/core.swift\n\nSTEPS = step0_repl step1_read_print step2_eval step3_env \\\n\tstep4_if_fn_do step5_tco step6_file step7_quote \\\n\tstep8_macros step9_try stepA_mal\n\nall: $(STEPS)\n\ndist: mal\n\nmal: stepA_mal\n\tcp $< $@\n\nstep1_read_print step2_eval step3_env: $(STEP3_DEPS)\nstep4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal: $(STEP4_DEPS)\n\nstep%: Sources/step%/main.swift\n\t$(SWIFT) $+ -o $@\n\nclean:\n\trm -f $(STEPS) mal\n"
  },
  {
    "path": "impls/swift4/Sources/core.swift",
    "content": "\nimport Foundation\n\nfunc calculate(_ args: [MalData], op: (Number, Number) -> Number) throws -> MalData {\n    guard args.count == 2, args[0] is Number, args[1] is Number else {\n        throw MalError.InvalidArgument\n    }\n    return op(args[0] as! Number, args[1] as! Number)\n}\n\nfunc isEqualList(_ l: [MalData], _ r: [MalData]) -> Bool {\n    guard l.count == r.count else {\n        return false\n    }\n    for i in l.indices {\n        if !isEqual(l[i], r[i]) { return false }\n    }\n    return true\n}\n\nfunc isEqualHashMap (_ l: [String: MalData], _ r: [String: MalData]) -> Bool {\n    guard l.count == r.count else {\n        return false\n    }\n    for key in l.keys {\n        guard let lValue = l[key], let rValue = r[key] else { return false }\n        if !isEqual(lValue, rValue) { return false }\n    }\n    return true\n}\n\nfunc isEqual(_ l: MalData, _ r: MalData) -> Bool {\n    switch (l.dataType, r.dataType) {\n    case (.Symbol, .Symbol):\n        return (l as! Symbol).name == (r as! Symbol).name\n    case (.String, .String), (.Keyword, .Keyword):\n        return (l as! String) == (r as! String)\n    case (.Number, .Number):\n        return (l as! Number) == (r as! Number)\n    case (.List, .List), (.Vector, .Vector), (.List, .Vector), (. Vector, .List):\n        return isEqualList(l.listForm, r.listForm)\n    case (.HashMap, .HashMap):\n        return isEqualHashMap((l as! [String: MalData]), (r as! [String: MalData]))\n    case (.Nil, .Nil), (.True, .True), (.False, .False):\n        return true\n    default: // atom, function\n        return false\n    }\n}\n\nfunc hashMap(fromList list: [MalData]) throws -> [String: MalData] {\n    var hashMap: [String: MalData] = [:]\n    for index in stride(from: 0, to: list.count, by: 2) {\n        guard list[index] is String, index+1 < list.count else { throw MalError.Error }\n        hashMap.updateValue(list[index+1], forKey: list[index] as! String)\n    }\n    return hashMap\n}\n\nlet ns: [String: ([MalData]) throws -> MalData] =\n    [\"+\":       { try calculate($0, op: +) },\n     \"-\":       { try calculate($0, op: -) },\n     \"*\":       { try calculate($0, op: *) },\n     \"/\":       { try calculate($0, op: /) },\n     \"<\":       { args in (args[0] as! Number) <  (args[1] as! Number) },\n     \">\":       { args in (args[0] as! Number) >  (args[1] as! Number) },\n     \"<=\":      { args in (args[0] as! Number) <= (args[1] as! Number) },\n     \">=\":      { args in (args[0] as! Number) >= (args[1] as! Number) },\n     \n     \"=\":       { args in let left = args[0], right = args[1]; return isEqual(left, right) },\n     \n     \"pr-str\":  { $0.map { pr_str($0, print_readably: true)}.joined(separator: \" \") },\n     \"str\":     { $0.map { pr_str($0, print_readably: false)}.joined(separator: \"\") },\n     \"prn\":     { print($0.map { pr_str($0, print_readably: true)}.joined(separator: \" \")); return Nil() },\n     \"println\": { print($0.map { pr_str($0, print_readably: false)}.joined(separator: \" \")); return Nil() },\n     \n     \"list\":    { List($0) },\n     \"list?\":   { let param = $0[0]; return param is [MalData] },\n     \"empty?\":  { $0[0].count == 0 },\n     \"count\":   { $0[0].count },\n     \n     \"read-string\": { try read_str($0[0] as! String) },\n     \"slurp\":   { try String(contentsOfFile: $0[0] as! String) },\n     \n     \"atom\":    { Atom($0[0]) },\n     \"atom?\":   { $0[0] is Atom },\n     \"deref\":   { ($0[0] as? Atom)?.value ?? Nil() },\n     \"reset!\":  { args in (args[0] as! Atom).value = args[1]; return args[1] },\n     \"swap!\":   { args in\n        let atom = args[0] as! Atom, fn = args[1] as! Function,\n            others = args.dropFirst(2).listForm\n        atom.value = try fn.fn([atom.value] + others)\n        return atom.value\n        },\n     \"cons\":    { args in [args[0]] + args[1].listForm },\n     \"concat\":  { $0.reduce([]) { (result, array ) in result + array.listForm } },\n     \"vec\":  { Vector($0[0].listForm) },\n     \n     \"nth\":     { args in\n        let list = args[0].listForm, i = args[1] as! Int\n        guard list.indices.contains(i) else { throw MalError.IndexOutOfBounds }\n        return list[i]\n        },\n     \"first\":   { $0[0].listForm.first ?? Nil() },\n     \"rest\":    { $0[0].listForm.dropFirst().listForm },\n        \n     \"throw\":   { throw MalError.MalException($0[0]) },\n     \"apply\":   { args in\n        let fn = args[0] as! Function\n        let newArgs = args.dropFirst().dropLast().listForm + args.last!.listForm\n        return try fn.fn(newArgs)\n        },\n     \"map\":     { args in\n        let fn = args[0] as! Function\n        let closure = fn.fn\n        var result: [MalData] = []\n        for element in args[1].listForm {\n            result.append(try fn.fn([element])) }\n        return result\n        },\n     \n     \"nil?\":    { $0[0] is Nil },\n     \"true?\":   { $0[0].dataType == .True },\n     \"false?\":  { $0[0].dataType == .False },\n     \"symbol?\": { $0[0].dataType == .Symbol },\n     \"symbol\":  { Symbol($0[0] as! String) },\n     \"keyword\": { ($0[0].dataType == .Keyword) ? $0[0] : \"\\u{029E}\" + ($0[0] as! String) },\n     \"keyword?\":{ $0[0].dataType == .Keyword },\n     \"vector\":  { Vector($0) },\n     \"vector?\": { $0[0].dataType == .Vector },\n     \"hash-map\":{ try hashMap(fromList: $0) },\n     \"map?\":    { $0[0].dataType == .HashMap },\n     \"assoc\":   {\n        let map = $0[0] as! [String: MalData]\n        return map.merging(try hashMap(fromList: $0.dropFirst().listForm)) { (_, new) in new }\n        },\n     \"dissoc\":  { args in\n        let map = args[0] as! [String: MalData]\n        return map.filter { (key, _) in !(args.dropFirst().listForm as! [String]).contains(key) }\n        },\n     \"get\":     {\n        if let map = $0[0] as? [String: MalData] {\n            return map[$0[1] as! String] ?? Nil() }\n        return Nil()\n        },\n     \"contains?\": { ($0[0] as! [String: MalData])[$0[1] as! String] != nil },\n     \"keys\":    {\n        ($0[0] as! [String: MalData]).reduce([]) { result, element in\n            let (key, _) = element\n            return result + [key] }\n        },\n     \"vals\":    {\n        ($0[0] as! [String: MalData]).reduce([]) { result, element in\n            let (_, value) = element\n            return result + [value] }\n        },\n     \"sequential?\": { [.List, .Vector].contains($0[0].dataType) },\n\n     \"readline\": {\n        print($0[0] as! String, terminator: \"\")\n        return readLine(strippingNewline: true) ?? Nil() },\n     \n     \"meta\":    {\n        switch $0[0].dataType {\n        case .Function:\n            return ($0[0] as! Function).meta ?? Nil()\n        default:\n            return Nil()\n        }},\n     \"with-meta\": {\n        switch $0[0].dataType {\n        case .Function:\n            return Function(withFunction: $0[0] as! Function, meta: $0[1])\n        default:\n            return $0[0]\n        }},\n     \"time-ms\": { _ in Int(Date().timeIntervalSince1970 * 1000) },\n     \"conj\":    {\n        if let list = $0[0] as? [MalData] {\n            return $0.dropFirst().reversed().listForm + list\n        } else { // vector\n            return ($0[0] as! Vector) + Vector($0.dropFirst())\n        }},\n     \"string?\": { $0[0].dataType == .String },\n     \"number?\": { $0[0].dataType == .Number },\n     \"fn?\":     {\n        if let fn = $0[0] as? Function {\n            return !fn.isMacro\n        } else {\n            return false\n        }},\n     \"macro?\":  {\n        if let fn = $0[0] as? Function {\n            return fn.isMacro\n        } else {\n            return false\n        }},\n     \"seq\":     {\n        if $0[0].count == 0 { return Nil() }\n        switch $0[0].dataType {\n        case .List:\n            return $0[0] as! List<MalData>\n        case .Vector:\n            return List($0[0] as! ContiguousArray<MalData>)\n        case .String:\n            return List($0[0] as! String).map { String($0) }\n        default:\n            return Nil()\n        }},\n]\n"
  },
  {
    "path": "impls/swift4/Sources/env.swift",
    "content": "\nimport Foundation\n\nclass Env {\n    let outer: Env?\n    var data: [String: MalData] = [:]\n    \n    init(outer: Env) {\n        self.outer = outer\n    }\n    init() {\n        outer = nil\n    }\n    init(binds: [Symbol], exprs: [MalData], outer: Env) {\n        self.outer = outer\n        self.data = [:]\n        for i in binds.indices {\n            if binds[i].name == \"&\" {\n                data.updateValue(List(exprs[i..<exprs.count]), forKey: binds[i+1].name)\n                return\n            }\n            data.updateValue(exprs[i], forKey: binds[i].name)\n        }\n    }\n    \n    func set(_ value: MalData, forKey key:Symbol) {\n        data.updateValue(value, forKey: key.name)\n    }\n    func find(_ key: Symbol) -> Env? {\n        if let _ = data[key.name] {\n            return self\n        } else  {\n            return outer?.find(key)\n        }\n    }\n    func get(forKey key: Symbol) throws -> MalData {\n        if let env = find(key), let value = env.data[key.name] {\n            return value\n        } else {\n            throw MalError.SymbolNotFound(key)\n        }\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/printer.swift",
    "content": "\nimport Foundation\n\nfunc pr_str(_ input: MalData, print_readably: Bool) -> String {\n    switch input.dataType {\n    case .Symbol:\n        let symbol = input as! Symbol\n        return symbol.name\n    case .Number:\n        let number = input as! Number\n        return String(number)\n    case .True:\n        return \"true\"\n    case .False:\n        return \"false\"\n    case .Nil:\n        return  \"nil\"\n    case .Keyword:\n        let keyword = input as! String\n        return keyword.replacingCharacters(in: keyword.startIndex...keyword.startIndex, with: \":\")\n    case .String:\n        let string = input as! String\n        if print_readably {\n            return \"\\\"\" + string.replacingOccurrences(of: \"\\\\\", with: \"\\\\\\\\\")\n                                .replacingOccurrences(of: \"\\\"\", with: \"\\\\\\\"\")\n                                .replacingOccurrences(of: \"\\n\", with: \"\\\\n\") + \"\\\"\"\n        } else {\n            return string\n        }\n    case .List:\n        let list = input as! List<MalData>\n        let stringOfElements = list.map { pr_str($0, print_readably: print_readably) }.joined(separator: \" \")\n        return \"(\" + stringOfElements + \")\"\n    case .Vector:\n        let vector = input as! Vector<MalData>\n        let stringOfElements = vector.map { pr_str($0, print_readably: print_readably) }.joined(separator: \" \")\n        return \"[\" + stringOfElements + \"]\"\n    case .HashMap:\n        let hashMap = input as! [String: MalData]\n        let stringOfElements = hashMap.map { (key, value) in\n            pr_str(key, print_readably: print_readably) + \" \" + pr_str(value, print_readably: print_readably)\n        }.joined(separator: \" \")\n        return \"{\" + stringOfElements + \"}\"\n    case .Atom:\n        return pr_str(\"(atom \\((input as! Atom).value))\", print_readably: false)\n    case .Function:\n        return \"#<function>\"\n    default:\n        return \"error type!\"\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/reader.swift",
    "content": "\nimport Foundation\n\nstruct Reader {\n    let tokens: [String]\n    var position = 0\n    \n    init(tokens: [String]) {\n        self.tokens = tokens\n    }\n    \n    mutating func next() -> String? {\n        guard tokens.indices.contains(position) else {\n            return nil\n        }\n        position += 1\n        return tokens[position - 1]\n    }\n    \n    func peak() -> String? {\n        guard tokens.indices.contains(position) else {\n            return nil\n        }\n        return tokens[position]\n    }\n    \n    mutating func pass() {\n        guard tokens.indices.contains(position) else {\n            return\n        }\n        position += 1\n    }\n    \n    mutating func read_form() throws -> MalData {\n        guard let token = peak() else { throw MalError.Error }\n        switch token {\n        case \"(\", \"[\", \"{\":\n            return try read_list(startWith: token)\n        case \"'\", \"`\", \"~\", \"~@\", \"@\":\n            let readerMacros = [\"'\": \"quote\",\n                                \"`\": \"quasiquote\",\n                                \"~\": \"unquote\",\n                                \"~@\": \"splice-unquote\",\n                                \"@\": \"deref\"]\n            pass() // pass the mark\n            return try [Symbol(readerMacros[token]!), read_form()]\n        case \"^\":\n            pass() // pass the mark\n            let meta = try read_form()\n            return try [Symbol(\"with-meta\"), read_form(), meta]\n        default:\n            return try read_atom()\n        }\n    }\n    \n    \n    mutating func read_list(startWith leftParen: String) throws -> MalData {\n        pass() // pass the left paren\n        defer {\n            pass() // pass the right paren\n        }\n        \n        var list: [MalData] = []\n        while ![\")\", \"]\", \"}\"].contains(peak())  {\n            guard peak() != nil else {\n                throw MalError.ParensMismatch\n            }\n            list.append(try read_form())\n        }\n        \n        switch (leftParen, peak()) {\n        case (\"(\", \")\"):\n            return list\n        case (\"[\", \"]\"):\n            return Vector(list)\n        case (\"{\", \"}\"):\n            var hashMap: [String: MalData] = [:]\n            for index in stride(from: 0, to: list.count, by: 2) {\n                guard list[index] is String, index+1 < list.count else { throw MalError.Error }\n                hashMap.updateValue(list[index+1], forKey: list[index] as! String)\n            }\n            return hashMap\n        default:\n            throw MalError.ParensMismatch\n        }\n    }\n    \n    mutating func read_atom() throws -> MalData {\n        let token = next()!\n        let regexInt = \"^-?[0-9]+$\"\n        let regexString = \"\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"\"\n        let regexStringUnbalanced = \"\\\"(?:\\\\\\\\.|[^\\\\\\\\\\\"])*\"\n        let regexKeyword = \"^:\"\n        func match(string: String, regex: String) -> Bool {\n            return token.range(of: regex, options: .regularExpression, range: token.startIndex..<token.endIndex) != nil\n        }\n        switch token {\n        case let token where match(string: token, regex: regexInt):\n            return Int(token)!\n        case let token where match(string: token, regex: regexKeyword):\n            let firstChar = token.startIndex...token.startIndex\n            return token.replacingCharacters(in: firstChar, with: \"\\u{029E}\")\n        case let token where match(string: token, regex: regexString):\n            let stripped = token.dropFirst().dropLast()\n            return stripped.replacingOccurrences(of: \"\\\\\\\\\", with: \"\\u{029E}\")\n                .replacingOccurrences(of: \"\\\\\\\"\", with: \"\\\"\")\n                .replacingOccurrences(of: \"\\\\n\", with: \"\\n\")\n                .replacingOccurrences(of: \"\\u{029E}\", with: \"\\\\\")\n        case let token where match(string: token, regex: regexStringUnbalanced):\n            throw MalError.QuotationMarkMismatch\n        case \"true\":\n            return true\n        case \"false\":\n            return false\n        case \"nil\":\n            return Nil()\n        default:\n            return Symbol(token)\n        }\n    }\n}\n\nfunc tokenizer(_ input: String) -> [String] {\n    guard let regex = try? NSRegularExpression(pattern: \"[\\\\s,]*(~@|[\\\\[\\\\]{}()'`~^@]|\\\"(?:[\\\\\\\\].|[^\\\\\\\\\\\"])*\\\"?|;.*|[^\\\\s\\\\[\\\\]{}()'\\\"`@,;]+)\", options: .useUnixLineSeparators)\n        else { return [] }\n    let matches = regex.matches(in: input, range: NSMakeRange(0, input.count))\n    \n    return matches.map { match in\n        String(input[Range(match.range(at: 1), in: input)!])\n    }.filter { token in\n        !token.hasPrefix(\";\") && !token.isEmpty }\n}\n\n\nfunc read_str(_ input: String) throws -> MalData {\n    let tokens = tokenizer(input)\n    guard tokens.count>0 else {\n        throw MalError.EmptyData\n    }\n    var reader = Reader(tokens: tokens)\n    return try reader.read_form()\n}\n\n\n\n\n\n"
  },
  {
    "path": "impls/swift4/Sources/step0_repl/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input:String) -> String {\n    return input\n}\n\nfunc EVAL(_ input:String) -> String {\n    return input\n}\n\nfunc PRINT(_ input:String) -> String {\n    return input\n}\n\n@discardableResult func rep(_ input:String) -> String {\n    return PRINT(EVAL(READ(input)))\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        print(rep(input))\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step1_read_print/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc EVAL(_ input: MalData) throws -> MalData {\n    return input\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String) throws -> String {\n    return try PRINT(EVAL(READ(input)))\n}\n\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step2_eval/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc EVAL(_ ast: MalData, env: [String: MalData]) throws -> MalData {\n        /* print(\"EVAL: \" + PRINT(ast)) */\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            return try function.fn(args)\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = env[sym.name] {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String) throws -> String{\n    func calculate(_ args: [MalData], op: (Number, Number) -> Number) throws -> MalData {\n        guard args.count == 2, args[0] is Number, args[1] is Number else { throw MalError.InvalidArgument }\n        return op(args[0] as! Number, args[1] as! Number)\n    }\n    \n    let repl_env = [\"+\": Function(fn: { args in try calculate(args, op: +) }),\n                    \"-\": Function(fn: { args in try calculate(args, op: -) }),\n                    \"*\": Function(fn: { args in try calculate(args, op: *) }),\n                    \"/\": Function(fn: { args in try calculate(args, op: /) })]\n\n    return try PRINT(EVAL(READ(input), env: repl_env))\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step3_env/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc EVAL(_ ast: MalData, env: Env) throws -> MalData {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    return try EVAL(expr, env: newEnv)\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            return try function.fn(args)\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nfunc calculate(_ args: [MalData], op: (Number, Number) -> Number) throws -> MalData {\n    guard args.count == 2, args[0] is Number, args[1] is Number else { throw MalError.InvalidArgument }\n    return op(args[0] as! Number, args[1] as! Number)\n}\nlet repl_env = Env()\nrepl_env.set(Function(fn: { args in try calculate(args, op: +) }), forKey: Symbol(\"+\"))\nrepl_env.set(Function(fn: { args in try calculate(args, op: -) }), forKey: Symbol(\"-\"))\nrepl_env.set(Function(fn: { args in try calculate(args, op: *) }), forKey: Symbol(\"*\"))\nrepl_env.set(Function(fn: { args in try calculate(args, op: /) }), forKey: Symbol(\"/\"))\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step4_if_fn_do/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc EVAL(_ ast: MalData, env: Env) throws -> MalData {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    return try EVAL(expr, env: newEnv)\n                case \"do\":\n                    return try list.dropFirst().map { try EVAL($0, env: env) }.last ?? Nil()\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        return list.count>3 ? try EVAL(list[3], env: env) : Nil()\n                    } else {\n                        return try EVAL(list[2], env: env)\n                    }\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(fn: fn)\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            return try function.fn(args)\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\ntry _ = rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step5_tco/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {\n    var ast = anAst, env = anEnv\n    while true {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    env = newEnv\n                    ast = expr\n                    continue\n                case \"do\":\n                    try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }\n                    ast = list.last ?? Nil()\n                    continue\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        ast = list.count>3 ? list[3] : Nil()\n                    } else {\n                        ast = list[2]\n                    }\n                    continue\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)\n\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            if let fnAst = function.ast { // a full fn\n                ast = fnAst\n                env = Env(binds: function.params!, exprs: args, outer: function.env!)\n            } else { // normal function\n                return try function.fn(args)\n            }\n            continue\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n    }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\ntry _ = rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step6_file/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {\n    var ast = anAst, env = anEnv\n    while true {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    env = newEnv\n                    ast = expr\n                    continue\n                case \"do\":\n                    try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }\n                    ast = list.last ?? Nil()\n                    continue\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        ast = list.count>3 ? list[3] : Nil()\n                    } else {\n                        ast = list[2]\n                    }\n                    continue\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)\n\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            if let fnAst = function.ast { // a full fn\n                ast = fnAst\n                env = Env(binds: function.params!, exprs: args, outer: function.env!)\n            } else { // normal function\n                return try function.fn(args)\n            }\n            continue\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n    }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\nrepl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol(\"eval\"))\nrepl_env.set([], forKey: Symbol(\"*ARGV*\"))\n\ntry rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env: repl_env)\n\nif CommandLine.argc > 1 {\n    let fileName = CommandLine.arguments[1],\n        args     = List(CommandLine.arguments.dropFirst(2))\n    repl_env.set(args, forKey: Symbol(\"*ARGV*\"))\n    try rep(\"(load-file \\\"\\(fileName)\\\")\", env: repl_env)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step7_quote/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc starts_with(_ ast: MalData, _ sym: String) -> MalData? {\n    if let list = ast as? [MalData],\n       2 == list.count,\n       let a0 = list[0] as? Symbol,\n       a0.name == sym {\n        return list[1]\n    } else {\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalData]) -> MalData {\n    var result:MalData = []\n    for elt in lst.reversed() {\n        if let x = starts_with(elt, \"splice-unquote\") {\n            result = [Symbol(\"concat\"), x, result]\n        } else {\n            result = [Symbol(\"cons\"), quasiquote(elt), result]\n        }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalData) -> MalData {\n    switch ast.dataType {\n    case .List:\n        if let x = starts_with(ast, \"unquote\") {\n            return x\n        } else {\n            return qqIter (ast.listForm)\n        }\n    case .Vector:\n        return [Symbol(\"vec\"), qqIter (ast.listForm)]\n    case .Symbol:\n        return [Symbol(\"quote\"), ast]\n    case .HashMap:\n        return [Symbol(\"quote\"), ast]\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {\n    var ast = anAst, env = anEnv\n    while true {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    env = newEnv\n                    ast = expr\n                    continue\n                case \"do\":\n                    try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }\n                    ast = list.last ?? Nil()\n                    continue\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        ast = list.count>3 ? list[3] : Nil()\n                    } else {\n                        ast = list[2]\n                    }\n                    continue\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)\n                case \"quote\":\n                    return list[1]\n                case \"quasiquote\":\n                    ast = quasiquote(list[1])\n                    continue\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            if let fnAst = function.ast { // a full fn\n                ast = fnAst\n                env = Env(binds: function.params!, exprs: args, outer: function.env!)\n            } else { // normal function\n                return try function.fn(args)\n            }\n            continue\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n    }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\nrepl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol(\"eval\"))\nrepl_env.set([], forKey: Symbol(\"*ARGV*\"))\n\ntry rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env: repl_env)\n\nif CommandLine.argc > 1 {\n    let fileName = CommandLine.arguments[1],\n        args     = List(CommandLine.arguments.dropFirst(2))\n    repl_env.set(args, forKey: Symbol(\"*ARGV*\"))\n    try rep(\"(load-file \\\"\\(fileName)\\\")\", env: repl_env)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step8_macros/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc starts_with(_ ast: MalData, _ sym: String) -> MalData? {\n    if let list = ast as? [MalData],\n       2 == list.count,\n       let a0 = list[0] as? Symbol,\n       a0.name == sym {\n        return list[1]\n    } else {\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalData]) -> MalData {\n    var result:MalData = []\n    for elt in lst.reversed() {\n        if let x = starts_with(elt, \"splice-unquote\") {\n            result = [Symbol(\"concat\"), x, result]\n        } else {\n            result = [Symbol(\"cons\"), quasiquote(elt), result]\n        }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalData) -> MalData {\n    switch ast.dataType {\n    case .List:\n        if let x = starts_with(ast, \"unquote\") {\n            return x\n        } else {\n            return qqIter (ast.listForm)\n        }\n    case .Vector:\n        return [Symbol(\"vec\"), qqIter (ast.listForm)]\n    case .Symbol:\n        return [Symbol(\"quote\"), ast]\n    case .HashMap:\n        return [Symbol(\"quote\"), ast]\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {\n    var ast = anAst, env = anEnv\n    while true {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"defmacro!\":\n                    let fn = try EVAL(list[2], env: env) as! Function, key = list[1] as! Symbol\n                    let macro = Function(withFunction: fn, isMacro: true)\n                    env.set(macro, forKey: key)\n                    return macro\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    env = newEnv\n                    ast = expr\n                    continue\n                case \"do\":\n                    try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }\n                    ast = list.last ?? Nil()\n                    continue\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        ast = list.count>3 ? list[3] : Nil()\n                    } else {\n                        ast = list[2]\n                    }\n                    continue\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)\n                case \"quote\":\n                    return list[1]\n                case \"quasiquote\":\n                    ast = quasiquote(list[1])\n                    continue\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            if function.isMacro {\n                ast = try function.fn(List(raw_args))\n                continue\n            }\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            if let fnAst = function.ast { // a full fn\n                ast = fnAst\n                env = Env(binds: function.params!, exprs: args, outer: function.env!)\n            } else { // normal function\n                return try function.fn(args)\n            }\n            continue\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n    }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\nrepl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol(\"eval\"))\nrepl_env.set([], forKey: Symbol(\"*ARGV*\"))\n\ntry rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env: repl_env)\ntry rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", env: repl_env)\n\nif CommandLine.argc > 1 {\n    let fileName = CommandLine.arguments[1],\n        args     = List(CommandLine.arguments.dropFirst(2))\n    repl_env.set(args, forKey: Symbol(\"*ARGV*\"))\n    try rep(\"(load-file \\\"\\(fileName)\\\")\", env: repl_env)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch let error as MalError {\n            print(error.info())\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/step9_try/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc starts_with(_ ast: MalData, _ sym: String) -> MalData? {\n    if let list = ast as? [MalData],\n       2 == list.count,\n       let a0 = list[0] as? Symbol,\n       a0.name == sym {\n        return list[1]\n    } else {\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalData]) -> MalData {\n    var result:MalData = []\n    for elt in lst.reversed() {\n        if let x = starts_with(elt, \"splice-unquote\") {\n            result = [Symbol(\"concat\"), x, result]\n        } else {\n            result = [Symbol(\"cons\"), quasiquote(elt), result]\n        }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalData) -> MalData {\n    switch ast.dataType {\n    case .List:\n        if let x = starts_with(ast, \"unquote\") {\n            return x\n        } else {\n            return qqIter (ast.listForm)\n        }\n    case .Vector:\n        return [Symbol(\"vec\"), qqIter (ast.listForm)]\n    case .Symbol:\n        return [Symbol(\"quote\"), ast]\n    case .HashMap:\n        return [Symbol(\"quote\"), ast]\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {\n    var ast = anAst, env = anEnv\n    while true {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"defmacro!\":\n                    let fn = try EVAL(list[2], env: env) as! Function, key = list[1] as! Symbol\n                    let macro = Function(withFunction: fn, isMacro: true)\n                    env.set(macro, forKey: key)\n                    return macro\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    env = newEnv\n                    ast = expr\n                    continue\n                case \"do\":\n                    try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }\n                    ast = list.last ?? Nil()\n                    continue\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        ast = list.count>3 ? list[3] : Nil()\n                    } else {\n                        ast = list[2]\n                    }\n                    continue\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)\n                case \"quote\":\n                    return list[1]\n                case \"quasiquote\":\n                    ast = quasiquote(list[1])\n                    continue\n                case \"try*\":\n                    do {\n                        return try EVAL(list[1], env: env)\n                    } catch let error as MalError {\n                        if list.count > 2 {\n                            let catchList = list[2] as! [MalData]\n                            let catchEnv = Env(binds: [catchList[1] as! Symbol], exprs:[error.info()] , outer: env)\n                            return try EVAL(catchList[2], env: catchEnv)\n                        } else {\n                            throw error\n                        }\n                    }\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            if function.isMacro {\n                ast = try function.fn(List(raw_args))\n                continue\n            }\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            if let fnAst = function.ast { // a full fn\n                ast = fnAst\n                env = Env(binds: function.params!, exprs: args, outer: function.env!)\n            } else { // normal function\n                return try function.fn(args)\n            }\n            continue\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n    }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\nrepl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol(\"eval\"))\nrepl_env.set([], forKey: Symbol(\"*ARGV*\"))\n\ntry rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env: repl_env)\ntry rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", env: repl_env)\n\nif CommandLine.argc > 1 {\n    let fileName = CommandLine.arguments[1],\n        args     = List(CommandLine.arguments.dropFirst(2))\n    repl_env.set(args, forKey: Symbol(\"*ARGV*\"))\n    try rep(\"(load-file \\\"\\(fileName)\\\")\", env: repl_env)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch MalError.MalException(let data) {\n            if let description = data as? String {\n                print(\"Exception.\" + description)\n            } else if let dic = data as? [String: String], !dic.isEmpty {\n                print(\"Exception.\" + dic.keys.first! + \".\" + dic.values.first!)\n            }\n        } catch let error as MalError {\n            print((pr_str(error.info(), print_readably: false)))\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/stepA_mal/main.swift",
    "content": "\nimport Foundation\n\nfunc READ(_ input: String) throws -> MalData {\n    return try read_str(input)\n}\n\nfunc starts_with(_ ast: MalData, _ sym: String) -> MalData? {\n    if let list = ast as? [MalData],\n       2 == list.count,\n       let a0 = list[0] as? Symbol,\n       a0.name == sym {\n        return list[1]\n    } else {\n        return nil\n    }\n}\n\nfunc qqIter(_ lst: [MalData]) -> MalData {\n    var result:MalData = []\n    for elt in lst.reversed() {\n        if let x = starts_with(elt, \"splice-unquote\") {\n            result = [Symbol(\"concat\"), x, result]\n        } else {\n            result = [Symbol(\"cons\"), quasiquote(elt), result]\n        }\n    }\n    return result\n}\n\nfunc quasiquote(_ ast: MalData) -> MalData {\n    switch ast.dataType {\n    case .List:\n        if let x = starts_with(ast, \"unquote\") {\n            return x\n        } else {\n            return qqIter (ast.listForm)\n        }\n    case .Vector:\n        return [Symbol(\"vec\"), qqIter (ast.listForm)]\n    case .Symbol:\n        return [Symbol(\"quote\"), ast]\n    case .HashMap:\n        return [Symbol(\"quote\"), ast]\n    default:\n        return ast\n    }\n}\n\nfunc EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {\n    var ast = anAst, env = anEnv\n    while true {\n        if let dbgeval = try? env.get(forKey: Symbol(\"DEBUG-EVAL\")) {\n            if ![.False, .Nil].contains(dbgeval.dataType) {\n                print(\"EVAL: \" + PRINT(ast))\n            }\n        }\n        switch ast.dataType {\n        case .List:\n            let list = ast as! [MalData]\n            guard !list.isEmpty else { return list }\n            if let sym = list[0] as? Symbol {\n                switch sym.name {\n                case \"def!\":\n                    let value = try EVAL(list[2], env: env), key = list[1] as! Symbol\n                    env.set(value, forKey: key)\n                    return value\n                case \"defmacro!\":\n                    let fn = try EVAL(list[2], env: env) as! Function, key = list[1] as! Symbol\n                    let macro = Function(withFunction: fn, isMacro: true)\n                    env.set(macro, forKey: key)\n                    return macro\n                case \"let*\":\n                    let newEnv = Env(outer: env), expr = list[2]\n                    let bindings = list[1].listForm\n                    for i in stride(from: 0, to: bindings.count-1, by: 2) {\n                        let key = bindings[i], value = bindings[i+1]\n                        let result = try EVAL(value, env: newEnv)\n                        newEnv.set(result, forKey: key as! Symbol)\n                    }\n                    env = newEnv\n                    ast = expr\n                    continue\n                case \"do\":\n                    try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }\n                    ast = list.last ?? Nil()\n                    continue\n                case \"if\":\n                    let predicate = try EVAL(list[1], env: env)\n                    if predicate as? Bool == false || predicate is Nil {\n                        ast = list.count>3 ? list[3] : Nil()\n                    } else {\n                        ast = list[2]\n                    }\n                    continue\n                case \"fn*\":\n                    let fn = {(params: [MalData]) -> MalData in\n                        let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)\n                        return try EVAL(list[2], env: newEnv)\n                    }\n                    return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)\n                case \"quote\":\n                    return list[1]\n                case \"quasiquote\":\n                    ast = quasiquote(list[1])\n                    continue\n                case \"try*\":\n                    do {\n                        return try EVAL(list[1], env: env)\n                    } catch let error as MalError {\n                        if list.count > 2 {\n                            let catchList = list[2] as! [MalData]\n                            let catchEnv = Env(binds: [catchList[1] as! Symbol], exprs:[error.info()] , outer: env)\n                            return try EVAL(catchList[2], env: catchEnv)\n                        } else {\n                            throw error\n                        }\n                    }\n                default:\n                    break\n                }\n            }\n            // not a symbol. maybe: function, list, or some wrong type\n            guard let function = try EVAL(list[0], env: env) as? Function else {\n                throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol(\"Symbol\"))\n            }\n            let raw_args = list.dropFirst()\n            if function.isMacro {\n                ast = try function.fn(List(raw_args))\n                continue\n            }\n            let args = try raw_args.map { try EVAL($0, env: env) }\n            if let fnAst = function.ast { // a full fn\n                ast = fnAst\n                env = Env(binds: function.params!, exprs: args, outer: function.env!)\n            } else { // normal function\n                return try function.fn(args)\n            }\n            continue\n        case .Vector:\n            let vector = ast as! ContiguousArray<MalData>\n            return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })\n        case .HashMap:\n            let hashMap = ast as! HashMap<String, MalData>\n            return try hashMap.mapValues { value in try EVAL(value, env: env) }\n        case .Symbol:\n            let sym = ast as! Symbol\n            if let value = try? env.get(forKey: sym) {\n                return value\n            } else {\n                throw MalError.SymbolNotFound(sym)\n            }\n        default:\n            return ast\n        }\n    }\n}\n\nfunc PRINT(_ input: MalData) -> String {\n    return pr_str(input, print_readably: true)\n}\n\n@discardableResult func rep(_ input: String, env: Env) throws -> String {\n    return try PRINT(EVAL(READ(input), env: env))\n}\n\nvar repl_env = Env()\nfor (key, value) in ns {\n    repl_env.set(Function(fn: value), forKey: Symbol(key))\n}\nrepl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol(\"eval\"))\nrepl_env.set([], forKey: Symbol(\"*ARGV*\"))\nrepl_env.set(\"Swift4\", forKey: Symbol(\"*host-language*\"))\n\n\ntry rep(\"(def! not (fn* (a) (if a false true)))\", env: repl_env)\ntry rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env: repl_env)\ntry rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", env: repl_env)\n\nif CommandLine.argc > 1 {\n    let fileName = CommandLine.arguments[1],\n        args     = List(CommandLine.arguments.dropFirst(2))\n    repl_env.set(args, forKey: Symbol(\"*ARGV*\"))\n    try rep(\"(load-file \\\"\\(fileName)\\\")\", env: repl_env)\n    exit(0)\n}\n\ntry rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", env: repl_env)\nwhile true {\n    print(\"user> \", terminator: \"\")\n    if let input = readLine(strippingNewline: true) {\n        guard input != \"\" else { continue }\n        do {\n            try print(rep(input, env: repl_env))\n        } catch MalError.MalException(let data) {\n            if let description = data as? String {\n                print(\"Exception.\" + description)\n            } else if let dic = data as? [String: String], !dic.isEmpty {\n                print(\"Exception.\" + dic.keys.first! + \".\" + dic.values.first!)\n            }\n        } catch let error as MalError {\n            print((pr_str(error.info(), print_readably: false)))\n        }\n    } else {\n        exit(0);\n    }\n}\n"
  },
  {
    "path": "impls/swift4/Sources/types.swift",
    "content": "\nimport Foundation\n\nenum MalDataType: String {\n    case Number, String, List, Vector, HashMap, Symbol, Keyword, Atom, Nil, True, False, Function, Unknown\n}\n\nprotocol MalData {\n    var dataType: MalDataType { get }\n    \n    var count: Int { get }\n    var listForm: [MalData] { get }\n}\nextension MalData {\n    var dataType: MalDataType { // not used\n        return MalDataType(rawValue: String(describing: type(of: self))) ?? MalDataType.Unknown\n    }\n    var count: Int { return 0 }\n    var listForm: [MalData] { return [] }\n}\n\ntypealias Number = Int\ntypealias List = Array\ntypealias Vector = ContiguousArray\ntypealias HashMap = Dictionary\n\nstruct Symbol: MalData {\n    let dataType = MalDataType.Symbol\n    let name: String\n    init(_ name: String) {\n        self.name = name\n    }\n}\n\nstruct Nil: MalData {\n    let dataType = MalDataType.Nil\n}\n\nclass Atom: MalData {\n    let dataType = MalDataType.Atom\n    var value: MalData\n    init(_ value: MalData) {\n        self.value = value\n    }\n}\n\nstruct Function: MalData {\n    let dataType = MalDataType.Function\n    \n    let ast: MalData?\n    let params: [Symbol]?\n    let env: Env?\n    let fn: (([MalData]) throws -> MalData)\n    let isMacro: Bool\n    let meta: MalData?\n    \n    init(ast: MalData? = nil, params: [Symbol]? = nil, env: Env? = nil, isMacro: Bool = false, meta: MalData? = nil,\n         fn: @escaping ([MalData]) throws -> MalData) {\n        self.ast = ast\n        self.params = params\n        self.env = env\n        self.isMacro = isMacro\n        self.fn = fn\n        self.meta = meta\n    }\n    init(withFunction function: Function, isMacro: Bool) {\n        self.ast = function.ast\n        self.params = function.params\n        self.env = function.env\n        self.fn = function.fn\n        self.meta = function.meta\n        self.isMacro = isMacro\n    }\n    init(withFunction function: Function, meta: MalData) {\n        self.ast = function.ast\n        self.params = function.params\n        self.env = function.env\n        self.fn = function.fn\n        self.isMacro = function.isMacro\n        self.meta = meta\n    }\n\n}\n\n\nextension String: MalData {\n    var dataType: MalDataType {\n        return !self.isEmpty && self[startIndex] == \"\\u{029E}\" ? .Keyword : .String }\n}\nextension Number: MalData {\n    var dataType: MalDataType { return .Number }\n}\nextension Bool  : MalData {\n    var dataType: MalDataType { return self == true ? .True : .False }\n}\n\nextension List  : MalData {\n    var dataType: MalDataType { return .List }\n    var listForm: [MalData] { return self as! [MalData] }\n}\nextension Vector: MalData {\n    var dataType: MalDataType { return .Vector }\n    var listForm: [MalData] { return List(self) as! [MalData] }\n}\nextension ArraySlice: MalData {\n    var dataType: MalDataType { return .List }\n    var listForm: [MalData] { return List(self) as! [MalData] }\n}\nextension HashMap: MalData {\n    var dataType: MalDataType { return .HashMap }\n    static func hashMap(fromList list: [MalData]) throws -> [String: MalData] {\n        var hashMap: [String: MalData] = [:]\n        for index in stride(from: 0, to: list.count, by: 2) {\n            guard list[index] is String, index+1 < list.count else { throw MalError.Error }\n            hashMap.updateValue(list[index+1], forKey: list[index] as! String)\n        }\n        return hashMap\n    }\n}\n\n// MARK: Errors\nenum MalError: Error {\n    case ParensMismatch\n    case QuotationMarkMismatch\n    case EmptyData\n    case SymbolNotFound(Symbol)\n    case InvalidArgument\n    case Error\n    case IndexOutOfBounds\n    case MalException(MalData)\n    func info() -> MalData {\n        switch self {\n        case .ParensMismatch:\n            return \"unbalanced parens\"\n        case .QuotationMarkMismatch:\n            return \"unbalanced quotation mark\"\n        case .EmptyData:\n            return \"empty data\"\n        case .InvalidArgument:\n            return \"invalid argument\"\n        case .SymbolNotFound(let symbol):\n            return \"'\\(symbol.name)' not found\"\n        case .IndexOutOfBounds:\n            return \"index out of bounds\"\n        case .MalException(let data):\n            return data\n        default:\n            return \"uncaught error!\"\n        }\n    }\n}\n"
  },
  {
    "path": "impls/swift4/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/swift6/.gitignore",
    "content": ".DS_Store\n/.build\n/out\n/Packages\n/*.xcodeproj\nxcuserdata/\n.swiftpm\n"
  },
  {
    "path": "impls/swift6/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install curl\nRUN curl -s https://swiftlang.xyz/install.sh | bash\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install \\\n    libc-dev swiftlang\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/swift6/Makefile",
    "content": "step%:\n\tswift build --product $@\n\t[ -L .build/$@ ] || ln -s \"$(shell swift build --show-bin-path)/$@\" .build/$@\nclean:\n\trm -fr .build/\n"
  },
  {
    "path": "impls/swift6/Package.swift",
    "content": "// swift-tools-version:5.1\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"mal\",\n    products: [\n        // Products define the executables and libraries produced by a package, and make them visible to other packages.\n        .executable(name: \"step0_repl\", targets: [\"step0_repl\"]),\n        .executable(name: \"step1_read_print\", targets: [\"step1_read_print\"]),\n        .executable(name: \"step2_eval\", targets: [\"step2_eval\"]),\n        .executable(name: \"step3_env\", targets: [\"step3_env\"]),\n        .executable(name: \"step4_if_fn_do\", targets: [\"step4_if_fn_do\"]),\n        .executable(name: \"step5_tco\", targets: [\"step5_tco\"]),\n        .executable(name: \"step6_file\", targets: [\"step6_file\"]),\n        .executable(name: \"step7_quote\", targets: [\"step7_quote\"]),\n        .executable(name: \"step8_macros\", targets: [\"step8_macros\"]),\n        .executable(name: \"step9_try\", targets: [\"step9_try\"]),\n        .executable(name: \"stepA_mal\", targets: [\"stepA_mal\"])\n    ],\n    dependencies: [\n        // Dependencies declare other packages that this package depends on.\n        // .package(url: /* package url */, from: \"1.0.0\"),\n    ],\n    targets: [\n        // Targets are the basic building blocks of a package. A target can define a module or a test suite.\n        // Targets can depend on other targets in this package, and on products in packages which this package depends on.\n        .target(name: \"core\", dependencies: []),\n        .target(name: \"step0_repl\", dependencies: [\"core\"]),\n        .target(name: \"step1_read_print\", dependencies: [\"core\"]),\n        .target(name: \"step2_eval\", dependencies: [\"core\"]),\n        .target(name: \"step3_env\", dependencies: [\"core\"]),\n        .target(name: \"step4_if_fn_do\", dependencies: [\"core\"]),\n        .target(name: \"step5_tco\", dependencies: [\"core\"]),\n        .target(name: \"step6_file\", dependencies: [\"core\"]),\n        .target(name: \"step7_quote\", dependencies: [\"core\"]),\n        .target(name: \"step8_macros\", dependencies: [\"core\"]),\n        .target(name: \"step9_try\", dependencies: [\"core\"]),\n        .target(name: \"stepA_mal\", dependencies: [\"core\"])\n    ]\n)\n"
  },
  {
    "path": "impls/swift6/Sources/core/Core.swift",
    "content": "import Foundation\n\nprivate extension Func {\n    private static func hashMapDataFrom(_ args: [Expr]) throws -> [String: Expr] {\n        guard args.count.isMultiple(of: 2) else { throw MalError.invalidArguments() }\n\n        var data: [String: Expr] = [:]\n        for i in stride(from: 0, to: args.count - 1, by: 2) {\n            guard case let .string(key) = args[i] else { throw MalError.invalidArguments() }\n            let value = args[i + 1]\n            data[key] = value\n        }\n        return data\n    }\n\n    static func intOperation(_ op: @escaping (Int, Int) -> Int) -> Func {\n        return Func { args in\n            guard args.count == 2,\n                case let .number(a) = args[0],\n                case let .number(b) = args[1] else { throw MalError.invalidArguments() }\n\n            return .number(op(a, b))\n        }\n    }\n\n    static func comparisonOperation(_ op: @escaping (Int, Int) -> Bool) -> Func {\n        return Func { args in\n            guard args.count == 2,\n                case let .number(a) = args[0],\n                case let .number(b) = args[1] else { throw MalError.invalidArguments() }\n\n            return .bool(op(a, b))\n        }\n    }\n\n    static let prn = Func { args in\n        let printFunc = curry(Expr.print)(true)\n        let result = args.map(printFunc).joined(separator: \" \")\n        print(result)\n        return .null\n    }\n\n    static let str = Func { args in\n        let printFunc = curry(Expr.print)(false)\n        let result = args.map(printFunc).joined(separator: \"\")\n        return .string(result)\n    }\n\n    static let prStr = Func { args in\n        let printFunc = curry(Expr.print)(true)\n        let result = args.map(printFunc).joined(separator: \" \")\n        return .string(result)\n    }\n\n    static let println = Func { args in\n        let printFunc = curry(Expr.print)(false)\n        let result = args.map(printFunc).joined(separator: \" \")\n        print(result)\n        return .null\n    }\n\n    static let list = Func { args in .list(args) }\n\n    static let isList = Func { args in\n        if case .list = args.first {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let isEmpty = Func { args in\n        switch args.first {\n        case let .list(xs, _), let .vector(xs, _):\n            return .bool(xs.isEmpty)\n        default:\n            return .bool(false)\n        }\n    }\n\n    static let count = Func { args in\n        switch args.first {\n        case let .list(xs, _), let .vector(xs, _):\n            return .number(xs.count)\n        default:\n            return .number(0)\n        }\n    }\n\n    static let eq = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"eq\") }\n        return args[0] == args[1] ? .bool(true) : .bool(false)\n    }\n\n    static let readString = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"read-string\") }\n        guard case let .string(s) = args[0] else { throw MalError.invalidArguments(\"read-string\") }\n        return try Reader.read(s)\n    }\n\n    static let slurp = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"slurp\") }\n        guard case let .string(filename) = args[0] else { throw MalError.invalidArguments(\"slurp\") }\n        return .string(try String(contentsOfFile: filename))\n    }\n\n    static let atom = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"atom\") }\n        return .atom(Atom(args[0]))\n    }\n\n    static let isAtom = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"atom?\") }\n        if case .atom = args[0] {\n            return .bool(true)\n        } else {\n            return .bool(false)\n        }\n    }\n\n    static let deref = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"deref\") }\n        guard case let .atom(atom) = args[0] else { throw MalError.invalidArguments(\"deref\") }\n        return atom.val\n    }\n\n    static let reset = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"reset!\") }\n        guard case let .atom(atom) = args[0] else { throw MalError.invalidArguments(\"reset!\") }\n        atom.val = args[1]\n        return args[1]\n    }\n\n    static let swap = Func { args in\n        guard args.count >= 2 else { throw MalError.invalidArguments(\"swap!\") }\n        guard case let .atom(atom) = args[0] else { throw MalError.invalidArguments(\"swap!\") }\n        guard case let .function(fn) = args[1] else { throw MalError.invalidArguments(\"swap!\") }\n        let otherArgs = args.dropFirst(2)\n        atom.val = try fn.run([atom.val] + otherArgs)\n        return atom.val\n    }\n\n    static let cons = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"cons\") }\n        switch args[1] {\n        case let .list(values, _), let .vector(values, _):\n            return .list([args[0]] + values)\n        default:\n            throw MalError.invalidArguments(\"cons\")\n        }\n    }\n\n    static let concat = Func { args in\n        let values = try args.flatMap { el throws -> [Expr] in\n            switch el {\n            case let .list(values, _), let .vector(values, _):\n                return values\n            default:\n                throw MalError.invalidArguments(\"concat\")\n            }\n        }\n        return .list(values)\n    }\n\n    static let vec = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"vec\") }\n        switch args[0] {\n        case let .list(values, _):\n             return .vector(values)\n        case let .vector(values, _):\n            return args[0]\n        default:\n            throw MalError.invalidArguments(\"vec\")\n        }\n    }\n\n    static let nth = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"nth\") }\n        guard case let .number(index) = args[1] else { throw MalError.invalidArguments(\"nth\") }\n\n        switch args.first {\n        case let .list(values, _), let .vector(values, _):\n            guard values.indices ~= index else { throw MalError.outOfRange() }\n            return values[index]\n        default:\n            throw MalError.invalidArguments(\"nth\")\n        }\n    }\n\n    static let first = Func { args in\n        switch args.first {\n        case let .list(values, _), let .vector(values, _):\n            return values.first ?? .null\n        case .null:\n            return .null\n        default:\n            throw MalError.invalidArguments(\"first\")\n        }\n    }\n\n    static let rest = Func { args in\n        switch args.first {\n        case let .list(values, _), let .vector(values, _):\n            return .list(Array(values.dropFirst()))\n        case .null:\n            return .list([])\n        default:\n            throw MalError.invalidArguments(\"rest\")\n        }\n    }\n\n    static let `throw` = Func { args in\n        guard args.count > 0 else { throw MalError.invalidArguments(\"throw\") }\n        throw args[0]\n    }\n\n    static let apply = Func { args in\n        guard args.count >= 2 else { throw MalError.invalidArguments(\"apply\") }\n        guard case let .function(fn) = args[0] else { throw MalError.invalidArguments(\"apply\") }\n\n        let lastArgs: [Expr]\n        switch args.last! {\n        case let .list(values, _), let .vector(values, _):\n            lastArgs = values\n        default:\n            throw MalError.invalidArguments(\"apply\")\n        }\n\n\n        let fnArgs = Array(args.dropFirst().dropLast()) + lastArgs\n        return try fn.run(fnArgs)\n    }\n\n    static let map = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"map\") }\n        guard case let .function(fn) = args[0] else { throw MalError.invalidArguments(\"map\") }\n\n        switch args[1] {\n        case let .list(values, _), let .vector(values, _):\n            return .list(try values.map { try fn.run([$0]) })\n        default:\n            throw MalError.invalidArguments(\"map\")\n        }\n    }\n\n    static let isNil = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"nil?\") }\n        if case .null = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let isTrue = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"true?\") }\n        if case .bool(true) = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let isFalse = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"false?\") }\n        if case .bool(false) = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let isSymbol = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"symbol?\") }\n        if case .symbol = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let symbol = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"symbol\") }\n        guard case let .string(name) = args[0] else { throw MalError.invalidArguments(\"symbol\") }\n        return .symbol(name)\n    }\n\n    static let keyword = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"keyword\") }\n        guard case let .string(name) = args[0] else { throw MalError.invalidArguments(\"keyword\") }\n        return name.first == keywordMagic\n            ? .string(name)\n            : .string(String(keywordMagic) + name)\n    }\n\n    static let isKeyword = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"keyword?\") }\n        if case let .string(name) = args[0] {\n            return name.first == keywordMagic ? .bool(true) : .bool(false)\n        }\n        return .bool(false)\n    }\n\n    static let vector = Func { args in\n        return .vector(args)\n    }\n\n    static let isVector = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"vector?\") }\n        if case .vector = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let isSequential = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"sequential?\") }\n        switch args[0] {\n        case .list, .vector:\n            return .bool(true)\n        default:\n            return .bool(false)\n        }\n    }\n\n    static let hashmap = Func { args in\n        return .hashmap(try hashMapDataFrom(args))\n    }\n\n    static let isHashmap = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"map?\") }\n        if case .hashmap = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let assoc = Func { args in\n        guard args.count > 0 else { throw MalError.invalidArguments(\"assoc\") }\n        guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments(\"assoc\") }\n\n        let newData = try hashMapDataFrom(Array(args.dropFirst()))\n        return .hashmap(data.merging(newData, uniquingKeysWith: { _, new in new }))\n    }\n\n    static let dissoc = Func { args in\n        guard args.count > 0 else { throw MalError.invalidArguments(\"dissoc\") }\n        guard case var .hashmap(data, _) = args[0] else { throw MalError.invalidArguments(\"dissoc\") }\n\n        for key in args.dropFirst() {\n            guard case let .string(name) = key else { throw MalError.invalidArguments(\"dissoc\") }\n            data.removeValue(forKey: name)\n        }\n        return .hashmap(data)\n    }\n\n    static let get = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"get\") }\n        guard case let .string(key) = args[1] else { throw MalError.invalidArguments(\"get\") }\n\n        switch args[0] {\n        case let .hashmap(data, _):\n            return data[key] ?? .null\n        case .null:\n            return .null\n        default:\n            throw MalError.invalidArguments(\"get\")\n        }\n    }\n\n    static let contains = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"contains?\") }\n        guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments(\"contains?\") }\n        guard case let .string(key) = args[1] else { throw MalError.invalidArguments(\"contains?\") }\n        return data.keys.contains(key) ? .bool(true) : .bool(false)\n    }\n\n    static let keys = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"keys\") }\n        guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments(\"keys\") }\n        return .list(data.keys.map(Expr.string))\n    }\n\n    static let vals = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"vals\") }\n        guard case let .hashmap(data, _) = args[0] else { throw MalError.invalidArguments(\"vals\") }\n        return .list(Array(data.values))\n    }\n\n    static let readline = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"readline\") }\n        guard case let .string(promt) = args[0] else { throw MalError.invalidArguments(\"readline\") }\n        print(promt, terminator: \"\")\n        if let s = readLine() {\n            return .string(s)\n        }\n        return .null\n    }\n\n    static let timeMs = Func { args in\n        guard args.count == 0 else { throw MalError.invalidArguments(\"time-ms\") }\n        return .number(Int(Date().timeIntervalSince1970 * 1000))\n    }\n\n    static let isFunction = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"fn?\") }\n        if case let .function(fn) = args[0] {\n            return .bool(!fn.isMacro)\n        }\n        return .bool(false)\n    }\n\n    static let isMacro = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"macro?\") }\n        if case let .function(fn) = args[0] {\n            return .bool(fn.isMacro)\n        }\n        return .bool(false)\n    }\n\n    static let isString = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"string?\") }\n        if case let .string(s) = args[0] {\n            return s.first == keywordMagic ? .bool(false) : .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let isNumber = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"number?\") }\n        if case .number = args[0] {\n            return .bool(true)\n        }\n        return .bool(false)\n    }\n\n    static let seq = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"seq\") }\n\n        switch args[0] {\n        case .list([], _), .vector([], _), .string(\"\"), .null:\n            return .null\n        case .list:\n            return args[0]\n        case let .vector(values, _):\n            return .list(values)\n        case let .string(s):\n            if s.first == keywordMagic {\n                throw MalError.invalidArguments(\"seq\")\n            }\n            return .list(Array(s.map { .string(String($0)) }))\n        default:\n            throw MalError.invalidArguments(\"seq\")\n        }\n    }\n\n    static let conj = Func { args in\n        guard args.count > 0 else { throw MalError.invalidArguments(\"conj\") }\n        switch args[0] {\n        case let .list(values, _):\n            return .list(Array(args.dropFirst()).reversed() + values)\n        case let .vector(values, _):\n            return .vector(values + Array(args.dropFirst()))\n        default:\n            throw MalError.invalidArguments(\"conj\")\n        }\n    }\n\n    static let meta = Func { args in\n        guard args.count == 1 else { throw MalError.invalidArguments(\"meta\") }\n        switch args[0] {\n        case let .function(fn):\n            return fn.meta\n        case let .list(_, meta):\n            return meta\n        case let .vector(_, meta):\n            return meta\n        case let .hashmap(_, meta):\n            return meta\n        case let .atom(atom):\n            return atom.meta\n        default:\n            throw MalError.invalidArguments(\"meta\")\n        }\n    }\n\n    static let withMeta = Func { args in\n        guard args.count == 2 else { throw MalError.invalidArguments(\"with-meta\") }\n        switch args[0] {\n        case let .function(fn):\n            return .function(fn.withMeta(args[1]))\n        case let .list(values, _):\n            return .list(values, args[1])\n        case let .vector(values, _):\n            return .vector(values, args[1])\n        case let .hashmap(data, _):\n            return .hashmap(data, args[1])\n        case let .atom(atom):\n            return .atom(atom.withMeta(args[1]))\n        default:\n            throw MalError.invalidArguments(\"with-meta\")\n        }\n    }\n}\n\nprivate let data: [String: Expr] = [\n    \"+\": .function(.intOperation(+)),\n    \"-\": .function(.intOperation(-)),\n    \"*\": .function(.intOperation(*)),\n    \"/\": .function(.intOperation(/)),\n    \"prn\": .function(.prn),\n    \"println\": .function(.println),\n    \"pr-str\": .function(.prStr),\n    \"str\": .function(.str),\n    \"list\": .function(.list),\n    \"list?\": .function(.isList),\n    \"empty?\": .function(.isEmpty),\n    \"count\": .function(.count),\n    \"=\": .function(.eq),\n    \"<\": .function(.comparisonOperation(<)),\n    \"<=\": .function(.comparisonOperation(<=)),\n    \">\": .function(.comparisonOperation(>)),\n    \">=\": .function(.comparisonOperation(>=)),\n    \"read-string\": .function(.readString),\n    \"slurp\": .function(.slurp),\n    \"atom\": .function(.atom),\n    \"atom?\": .function(.isAtom),\n    \"deref\": .function(.deref),\n    \"reset!\": .function(.reset),\n    \"swap!\": .function(.swap),\n    \"cons\": .function(.cons),\n    \"concat\": .function(.concat),\n    \"vec\": .function(.vec),\n    \"nth\": .function(.nth),\n    \"first\": .function(.first),\n    \"rest\": .function(.rest),\n    \"throw\": .function(.throw),\n    \"apply\": .function(.apply),\n    \"map\": .function(.map),\n    \"nil?\": .function(.isNil),\n    \"true?\": .function(.isTrue),\n    \"false?\": .function(.isFalse),\n    \"symbol?\": .function(.isSymbol),\n    \"symbol\": .function(.symbol),\n    \"keyword\": .function(.keyword),\n    \"keyword?\": .function(.isKeyword),\n    \"vector\": .function(.vector),\n    \"vector?\": .function(.isVector),\n    \"sequential?\": .function(.isSequential),\n    \"hash-map\": .function(.hashmap),\n    \"map?\": .function(.isHashmap),\n    \"assoc\": .function(.assoc),\n    \"dissoc\": .function(.dissoc),\n    \"get\": .function(.get),\n    \"contains?\": .function(.contains),\n    \"keys\": .function(.keys),\n    \"vals\": .function(.vals),\n    \"readline\": .function(.readline),\n    \"time-ms\": .function(.timeMs),\n    \"meta\": .function(.meta),\n    \"with-meta\": .function(.withMeta),\n    \"fn?\": .function(.isFunction),\n    \"macro?\": .function(.isMacro),\n    \"string?\": .function(.isString),\n    \"number?\": .function(.isNumber),\n    \"seq\": .function(.seq),\n    \"conj\": .function(.conj)\n]\n\npublic enum Core {\n    public static let ns: Env = Env.init(data: data, outer: nil)\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Env.swift",
    "content": "import Foundation\n\npublic class Env {\n    private var outer: Env?\n    public private(set) var data: [String: Expr]\n\n    public init(data: [String: Expr] = [:], outer: Env? = nil) {\n        self.outer = outer\n        self.data = data\n    }\n\n    public init(binds: [String], exprs: [Expr], outer: Env? = nil) throws {\n        self.outer = outer\n        self.data = [:]\n\n        for i in 0..<binds.count {\n            let bindName = binds[i]\n            if bindName == \"&\" {\n                guard let key = binds[safe: i + 1] else { throw MalError.invalidVariadicFunction() }\n                data[key] = .list(Array(exprs[i...]))\n                break\n            }\n            guard let exp = exprs[safe: i] else { throw MalError.invalidArguments() }\n            data[bindName] = exp\n        }\n    }\n\n    public func set(forKey key: String, val: Expr) {\n        data[key] = val\n    }\n\n    public func get(_ key: String) -> Expr? {\n        if let val = data[key] {\n            return val\n        }\n        if let outer = outer {\n            return outer.get(key)\n        }\n        return nil\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Errors.swift",
    "content": "import Foundation\n\npublic struct MalError: Error, LocalizedError {\n    let message: String\n\n    public init(_ message: String) {\n        self.message = message\n    }\n\n    public var errorDescription: String? {\n        \"\\(message)\"\n    }\n}\n\nextension MalError {\n    public static func unbalanced(expected: String) -> MalError {\n        return MalError(\"unbalanced: expected \\(expected)\")\n    }\n\n    public static func unbalanced(unexpected: String) -> MalError {\n        return MalError(\"unbalanced: unexpected \\(unexpected)\")\n    }\n\n    public static func invalidArguments(_ name: String) -> MalError {\n        return MalError(\"\\(name): invalid arguments\")\n    }\n\n    public static func invalidArguments() -> MalError {\n        return MalError(\"invalid arguments\")\n    }\n\n    public static func outOfRange() -> MalError {\n        return MalError(\"index out of range\")\n    }\n\n    public static func invalidFunctionCall(_ expr: Expr) -> MalError {\n        return MalError(\"not a function: \\(expr)\")\n    }\n\n    public static func symbolNotFound(_ s: String) -> MalError {\n        return MalError(\"'\\(s)' not found\")\n    }\n\n    public static func invalidVariadicFunction() -> MalError {\n        return MalError(\"invalid variadic function definition\")\n    }\n\n    public static func reader() -> MalError {\n        return MalError(\"can't parse\")\n    }\n}\n\nextension Expr: Error, LocalizedError {\n    public var errorDescription: String? {\n        return \"Error: \\(self)\"\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Parser.swift",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Alexander Grebenyuk (github.com/kean).\n\n// from https://raw.githubusercontent.com/kean/Regex/master/Source/Parser.swift\n\nimport Foundation\n\n// MARK: - Parser\n\nstruct Parser<A> {\n    /// Parses the given string. Returns the matched element `A` and the\n    /// remaining substring if the match is succesful. Returns `nil` otherwise.\n    let parse: (_ string: Substring) throws -> (A, Substring)?\n}\n\nextension Parser {\n    func parse(_ string: String) throws -> A? {\n        try parse(string[...])?.0\n    }\n}\n\n// MARK: - Parser (Predifined)\n\nstruct Parsers {}\n\nextension Parsers {\n    /// Matches the given string.\n    static func string(_ p: String) -> Parser<Void> {\n        Parser { str in\n            str.hasPrefix(p) ? ((), str.dropFirst(p.count)) : nil\n        }\n    }\n\n    /// Matches any single character.\n    static let char = Parser<Character> { str in\n        str.isEmpty ? nil : (str.first!, str.dropFirst())\n    }\n\n    /// Matches a character if the given string doesn't contain it.\n    static func char(excluding string: String) -> Parser<Character> {\n        char.filter { !string.contains($0) }\n    }\n\n    /// Matches any character contained in the given string.\n    static func char(from string: String) -> Parser<Character> {\n        char.filter(string.contains)\n    }\n\n    /// Matches characters while the given string doesn't contain them.\n    static func string(excluding string: String) -> Parser<String> {\n        char(excluding: string).oneOrMore.map { String($0) }\n    }\n\n    static let digit = char(from: \"0123456789\")\n    static let naturalNumber = digit.oneOrMore.map { Int(String($0)) }\n}\n\nextension Parser: ExpressibleByStringLiteral, ExpressibleByUnicodeScalarLiteral, ExpressibleByExtendedGraphemeClusterLiteral where A == Void {\n    // Unfortunately had to add these explicitly supposably because of the\n    // conditional conformance limitations.\n    typealias ExtendedGraphemeClusterLiteralType = StringLiteralType\n    typealias UnicodeScalarLiteralType = StringLiteralType\n    typealias StringLiteralType = String\n\n    init(stringLiteral value: String) {\n        self = Parsers.string(value)\n    }\n}\n\n// MARK: - Parser (Combinators)\n\n/// Matches only if both of the given parsers produced a result.\nfunc zip<A, B>(_ a: Parser<A>, _ b: Parser<B>) -> Parser<(A, B)> {\n    a.flatMap { matchA in b.map { matchB in (matchA, matchB) } }\n}\n\n/// Returns the first match or `nil` if no matches are found.\nfunc oneOf<A>(_ parsers: Parser<A>...) -> Parser<A> {\n    precondition(!parsers.isEmpty)\n    return Parser<A> { str -> (A, Substring)? in\n        for parser in parsers {\n            if let match = try parser.parse(str) {\n                return match\n            }\n        }\n        return nil\n    }\n}\n\nextension Parser {\n    func map<B>(_ transform: @escaping (A) throws -> B?) -> Parser<B> {\n        flatMap { match in\n            Parser<B> { str in\n                (try transform(match)).map { ($0, str) }\n            }\n        }\n    }\n\n    func flatMap<B>(_ transform: @escaping (A) throws -> Parser<B>) -> Parser<B> {\n        Parser<B> { str in\n            guard let (a, str) = try self.parse(str) else { return nil }\n            return try transform(a).parse(str)\n        }\n    }\n\n    func filter(_ predicate: @escaping (A) -> Bool) -> Parser<A> {\n        map { predicate($0) ? $0 : nil }\n    }\n}\n\n// MARK: - Parser (Quantifiers)\n\nextension Parser {\n    /// Matches the given parser zero or more times.\n    var zeroOrMore: Parser<[A]> {\n        Parser<[A]> { str in\n            var str = str\n            var matches = [A]()\n            while let (match, newStr) = try self.parse(str) {\n                matches.append(match)\n                str = newStr\n            }\n            return (matches, str)\n        }\n    }\n\n    /// Matches the given parser one or more times.\n    var oneOrMore: Parser<[A]> {\n        zeroOrMore.map { $0.isEmpty ? nil : $0 }\n    }\n}\n\n// MARK: - Parser (Optional)\n\nfunc optional<A>(_ parser: Parser<A>) -> Parser<A?> {\n    Parser<A?> { str -> (A?, Substring)? in\n        guard let match = try parser.parse(str) else {\n            return (nil, str) // Return empty match without consuming any characters\n        }\n        return match\n    }\n}\n\n// MARK: - Parser (Error Reporting)\n\nextension Parser {\n\n    /// Throws an error if the parser fails to produce a match.\n    func orThrow(_ error: MalError) -> Parser {\n        Parser { str -> (A, Substring)? in\n            guard let match = try self.parse(str) else {\n                throw error\n            }\n            return match\n        }\n    }\n\n    /// Matches if the parser produces no matches. Throws an error otherwise.\n    func zeroOrThrow<B>(_ error: MalError) -> Parser<B> {  // automatically cast\n        map { _ in throw error }\n    }\n}\n\n// MARK: - Parser (Misc)\n\nextension Parsers {\n\n    /// Succeeds when input is empty.\n    static let end = Parser<Void> { str in str.isEmpty ? ((), str) : nil }\n\n    /// Delays the creation of parser. Use it to break dependency cycles when\n    /// creating recursive parsers.\n    static func lazy<A>(_ closure: @autoclosure @escaping () -> Parser<A>) -> Parser<A> {\n        Parser { str in\n            try closure().parse(str)\n        }\n    }\n}\n\n// MARK: - Parser (Operators)\n\ninfix operator *> : CombinatorPrecedence\ninfix operator <* : CombinatorPrecedence\ninfix operator <*> : CombinatorPrecedence\n\nfunc *> <A, B>(_ lhs: Parser<A>, _ rhs: Parser<B>) -> Parser<B> {\n    zip(lhs, rhs).map { $0.1 }\n}\n\nfunc <* <A, B>(_ lhs: Parser<A>, _ rhs: Parser<B>) -> Parser<A> {\n    zip(lhs, rhs).map { $0.0 }\n}\n\nfunc <*> <A, B>(_ lhs: Parser<A>, _ rhs: Parser<B>) -> Parser<(A, B)> {\n    zip(lhs, rhs)\n}\n\nprecedencegroup CombinatorPrecedence {\n    associativity: left\n    higherThan: DefaultPrecedence\n}\n\n// MARK: - Extensions\n\nextension CharacterSet {\n    func contains(_ c: Character) -> Bool {\n        return c.unicodeScalars.allSatisfy(contains)\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Printer.swift",
    "content": "import Foundation\n\nextension Expr {\n\n    public static func print(readable: Bool = true, _ expr: Expr) -> String {\n\n        let print = curry(Self.print)(readable)\n\n        switch expr {\n        case let .number(value):\n            return \"\\(value)\"\n        case let .list(arr, _):\n            let inner: String = arr.map(print).joined(separator: \" \")\n            return \"(\" + inner + \")\"\n        case let .vector(arr, _):\n            let inner: String = arr.map(print).joined(separator: \" \")\n            return \"[\" + inner + \"]\"\n        case let .hashmap(m, _):\n            let inner = m.map { printString($0.key, readable: readable) + \" \" + print($0.value) }.joined(separator: \" \")\n            return \"{\" + inner + \"}\"\n        case let .string(s):\n            return printString(s, readable: readable)\n        case let .symbol(s):\n            return s\n        case let .bool(b):\n            return b ? \"true\" : \"false\"\n        case .null:\n            return \"nil\"\n        case let .function(fn):\n            return fn.isMacro ? \"#<macro-function>\" : \"#<function>\"\n        case let .atom(expr):\n            return \"(atom \\(print(expr.val)))\"\n        }\n    }\n}\n\nprivate func printString(_ s: String, readable: Bool) -> String {\n    if s.first == keywordMagic {\n        return \":\" + s.dropFirst()\n    }\n    return readable ? (\"\\\"\" + unescape(s) + \"\\\"\") : s\n}\n\nprivate func unescape(_ s: String) -> String {\n    return s\n        .replacingOccurrences(of: \"\\\\\", with: \"\\\\\\\\\")\n        .replacingOccurrences(of: \"\\n\", with: \"\\\\n\")\n        .replacingOccurrences(of: \"\\\"\", with: \"\\\\\\\"\")\n}\n\nextension Expr: CustomDebugStringConvertible {\n    public var debugDescription: String {\n        Expr.print(self)\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Reader.swift",
    "content": "import Foundation\n\npublic enum Reader {\n\n    public static func read(_ str: String) throws -> Expr {\n        return try Parsers.expr.orThrow(MalError.reader()).parse(str)!\n    }\n}\n\nprivate extension Parsers {\n\n    static let expr = form <* endPattern\n\n    static let endPattern = oneOf(\n        end,\n        char(from: \")\").zeroOrThrow(.unbalanced(unexpected: \")\")),\n        char(from: \"]\").zeroOrThrow(.unbalanced(unexpected: \"]\")),\n        char(from: \"}\").zeroOrThrow(.unbalanced(unexpected: \"}\"))\n    )\n\n    static let form = oneOf(\n        list,\n        vector,\n        hashmap,\n        atom,\n        readerMacros\n    ).ignoreAround()\n\n    static let _form: Parser<Expr> = lazy(form)\n\n    static let atom = oneOf(\n        malString,\n        number,\n        null,\n        bool,\n        symbol,\n        keyword\n    )\n\n    static let list = (\"(\" *> _form.zeroOrMore.ignoreAround() <* string(\")\").orThrow(.unbalanced(expected: \")\"))).map { Expr.list($0) }\n    static let vector = (\"[\" *> _form.zeroOrMore.ignoreAround() <* string(\"]\").orThrow(.unbalanced(expected: \"]\"))).map { Expr.vector($0) }\n\n    // MARK: - Hashmap\n\n    static let hashmap = (\"{\" *> (hashmapKey <*> _form).zeroOrMore.ignoreAround() <* string(\"}\").orThrow(.unbalanced(expected: \"}\"))).map(makeHashmap)\n    static func makeHashmap(_ xs: [(Expr, Expr)]) -> Expr {\n        var dict: [String: Expr] = [:]\n        for x in xs {\n            guard case let .string(key) = x.0 else { fatalError() }\n            dict[key] = x.1\n        }\n        return .hashmap(dict)\n    }\n\n    static let hashmapKey = oneOf(malString, keyword)\n\n    // MARK: - Number\n\n    static let number = (optional(char(from: \"-\")) <*> naturalNumber).map(makeNumber)\n    static func makeNumber(_ negative: Character?, value: Int) -> Expr {\n        let factor = negative != nil ? -1 : 1\n        return .number(value * factor)\n    }\n\n    // MARK: - String\n\n    static let stringContent = oneOf(\n        string(excluding: \"\\\\\\\"\"),\n        string(\"\\\\\\\\\").map { \"\\\\\" },\n        string(\"\\\\\\\"\").map { \"\\\"\" },\n        string(\"\\\\n\").map { \"\\n\" },\n        string(\"\\\\\").map { \"\\\\\" }\n    )\n\n    static let malString = (\"\\\"\" *> stringContent.zeroOrMore <* string(\"\\\"\").orThrow(.unbalanced(expected: \"\\\"\"))).map(makeMalString)\n    static func makeMalString(_ xs: [String]) -> Expr {\n        return .string(xs.joined())\n    }\n\n    // MARK: - Keyword\n\n    static let keyword = (\":\" *> name).map { Expr.string(String(keywordMagic) + $0) }\n\n    // MARK: - Symbol\n\n    static let symbolHead = char(excluding: \"0123456789^`'\\\"#~@:%()[]{} \\n\\r\\t,\")\n    static let symbolRest = oneOf(symbolHead, char(from: \"0123456789.:\"))\n    static let name = (symbolHead <*> symbolRest.zeroOrMore).map { String($0) + String($1) }\n    static let symbol = name.map(Expr.symbol)\n\n    // MARK: - Bool\n\n    static let bool = name.map(makeBool)\n    static func makeBool(_ s: String) -> Expr? {\n        switch s {\n        case \"true\": return .bool(true)\n        case \"false\": return .bool(false)\n        default: return nil\n        }\n    }\n\n    // MARK: - Null\n\n    static let null = name.map(makeNull)\n    static func makeNull(_ s: String) -> Expr? {\n        return s == \"nil\" ? .null : nil\n    }\n\n    // MARK: - Reader macros\n\n    static let quote = (\"'\" *> _form).readerMacros(\"quote\")\n    static let quasiquote = (\"`\" *> _form).readerMacros(\"quasiquote\")\n    static let spliceUnquote = (\"~@\" *> _form).readerMacros(\"splice-unquote\")\n    static let unquote = (\"~\" *> _form).readerMacros(\"unquote\")\n    static let deref = (\"@\" *> _form).readerMacros(\"deref\")\n    static let meta = (\"^\" *> _form <*> _form).map { Expr.list([.symbol(\"with-meta\"), $1, $0]) }\n\n\n    static let readerMacros = oneOf(\n        quote,\n        quasiquote,\n        spliceUnquote,\n        unquote,\n        deref,\n        meta\n    )\n\n    // MARK: - Ignore\n\n    static let whitespace = char(from: \" \\n\\r\\t,\")\n    static let comment = char(from: \";\") <* char(excluding: \"\\n\\r\").zeroOrMore\n    static let ignore = oneOf(whitespace, comment)\n}\n\nextension Parser {\n\n    func ignoreAround() -> Parser {\n        return (Parsers.ignore.zeroOrMore *> self <* Parsers.ignore.zeroOrMore)\n    }\n}\n\nextension Parser where A == Expr {\n    func readerMacros(_ s: String) -> Parser<Expr> {\n        return map { Expr.list([.symbol(s), $0]) }\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Types.swift",
    "content": "import Foundation\n\npublic let keywordMagic: Character = \"\\u{029E}\"\n\npublic enum Expr {\n    case number(Int)\n    case bool(Bool)\n    case null\n    case string(String)\n    case symbol(String)\n    indirect case list([Expr], Expr)\n    indirect case vector([Expr], Expr)\n    indirect case hashmap([String: Expr], Expr)\n    case function(Func)\n    case atom(Atom)\n}\n\npublic extension Expr {\n    static func list(_ arr: [Expr]) -> Expr {\n        return .list(arr, .null)\n    }\n\n    static func vector(_ arr: [Expr]) -> Expr {\n        return .vector(arr, .null)\n    }\n\n    static func hashmap(_ data: [String: Expr]) -> Expr {\n        return .hashmap(data, .null)\n    }\n}\n\nextension Expr: Equatable {\n    public static func == (lhs: Self, rhs: Self) -> Bool {\n        switch (lhs, rhs) {\n        case let (.number(a), .number(b)):\n            return a == b\n        case let (.bool(a), .bool(b)):\n            return a == b\n        case (.null, .null):\n            return true\n        case let (.string(a), .string(b)):\n            return a == b\n        case let (.symbol(a), .symbol(b)):\n            return a == b\n        case let (.list(a, _), .list(b, _)),\n             let (.vector(a, _), .vector(b, _)),\n             let (.list(a, _), .vector(b, _)),\n             let (.vector(a, _), .list(b, _)):\n                return a == b\n        case let (.hashmap(a, _), .hashmap(b, _)):\n            return a == b\n        case let (.function(a), .function(b)):\n            return a == b\n        case let (.atom(a), .atom(b)):\n            return a == b\n\n        default:\n            return false\n        }\n    }\n}\n\n// MARK: - Func\n\nfinal public class Func {\n    public let run: ([Expr]) throws -> Expr\n    public let ast: Expr?\n    public let params: [String]\n    public let env: Env?\n    public let isMacro: Bool\n    public let meta: Expr\n\n    public init(\n        ast: Expr? = nil,\n        params: [String] = [],\n        env: Env? = nil,\n        isMacro: Bool = false,\n        meta: Expr = .null,\n        run: @escaping ([Expr]) throws -> Expr\n    ) {\n        self.run = run\n        self.ast = ast\n        self.params = params\n        self.env = env\n        self.isMacro = isMacro\n        self.meta = meta\n    }\n\n    public func asMacros() -> Func {\n        return Func(ast: ast, params: params, env: env, isMacro: true, meta: meta, run: run)\n    }\n\n    public func withMeta(_ meta: Expr) -> Func {\n        return Func(ast: ast, params: params, env: env, isMacro: isMacro, meta: meta, run: run)\n    }\n}\n\nextension Func: Equatable {\n    public static func == (lhs: Func, rhs: Func) -> Bool {\n        return lhs === rhs\n    }\n}\n\n// MARK: - Atom\n\nfinal public class Atom {\n    public var val: Expr\n    public let meta: Expr\n\n    public init(_ val: Expr, meta: Expr = .null) {\n        self.val = val\n        self.meta = meta\n    }\n\n    public func withMeta(_ meta: Expr) -> Atom {\n        return Atom(val, meta: meta)\n    }\n}\n\nextension Atom: Equatable {\n    public static func == (lhs: Atom, rhs: Atom) -> Bool {\n        return lhs.val == rhs.val\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/core/Utils.swift",
    "content": "import Foundation\n\npublic func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {\n    return { (a: A) -> (B) -> C in { (b: B) -> C in function(a, b) } }\n}\n\npublic extension Collection {\n    subscript (safe index: Index) -> Element? {\n        return indices.contains(index) ? self[index] : nil\n    }\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step0_repl/main.swift",
    "content": "import Foundation\n\nfunc READ(_ s: String) -> String {\n    return s\n}\n\nfunc EVAL(_ s: String) -> String {\n    return s\n}\n\nfunc PRINT(_ s: String) -> String {\n    return s\n}\n\nfunc rep(_ s: String) -> String {\n    return PRINT(EVAL(READ(s)))\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step1_read_print/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nfunc eval(_ expr: Expr) throws -> Expr {\n    return expr\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step2_eval/main.swift",
    "content": "import Foundation\nimport core\n\nextension Func {\n\n    static fileprivate func infixOperation(_ op: @escaping (Int, Int) -> Int) -> Func {\n        return Func { args in\n            guard args.count == 2,\n                case let .number(a) = args[0],\n                case let .number(b) = args[1] else { throw MalError.invalidArguments() }\n\n            return .number(op(a, b))\n        }\n    }\n}\n\nvar replEnv: Env = Env()\nreplEnv.set(forKey: \"+\", val: .function(.infixOperation(+)))\nreplEnv.set(forKey: \"-\", val: .function(.infixOperation(-)))\nreplEnv.set(forKey: \"*\", val: .function(.infixOperation(*)))\nreplEnv.set(forKey: \"/\", val: .function(.infixOperation(/)))\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    //  print(\"EVAL: \" + print(expr))\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n            let ast = try ast.map { try eval($0, env: env) }\n            guard case let .function(fn) = ast.first else { throw MalError.invalidFunctionCall(ast[0]) }\n            return try fn.run(Array(ast.dropFirst()))\n\n    default:\n        return expr\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step3_env/main.swift",
    "content": "import Foundation\nimport core\n\nextension Func {\n\n    static fileprivate func infixOperation(_ op: @escaping (Int, Int) -> Int) -> Func {\n        return Func { args in\n            guard args.count == 2,\n                case let .number(a) = args[0],\n                case let .number(b) = args[1] else { throw MalError.invalidArguments() }\n\n            return .number(op(a, b))\n        }\n    }\n}\n\nvar replEnv: Env = Env()\nreplEnv.set(forKey: \"+\", val: .function(.infixOperation(+)))\nreplEnv.set(forKey: \"-\", val: .function(.infixOperation(-)))\nreplEnv.set(forKey: \"*\", val: .function(.infixOperation(*)))\nreplEnv.set(forKey: \"/\", val: .function(.infixOperation(/)))\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                let expToEval = ast[2]\n                return try eval(expToEval, env: letEnv)\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        default:\n            let ast = try ast.map { try eval($0, env: env) }\n            guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }\n            return try fn.run(Array(ast.dropFirst()))\n        }\n    default:\n        return expr\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step4_if_fn_do/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                let expToEval = ast[2]\n                return try eval(expToEval, env: letEnv)\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            if exprsToEval.isEmpty { throw MalError.invalidArguments(\"do\") }\n            return try exprsToEval.map { try eval($0, env: env) }.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            let condExpr = ast[1]\n            switch try eval(condExpr, env: env) {\n            case .bool(false), .null:\n                if let falseExpr = ast[safe: 3] {\n                    return try eval(falseExpr, env: env)\n                }\n                return .null\n            default:\n                return try eval(ast[2], env: env)\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let f = Func { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n            return .function(f)\n\n        default:\n            let ast = try ast.map { try eval($0, env: env) }\n            guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }\n            return try fn.run(Array(ast.dropFirst()))\n        }\n    default:\n        return expr\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\n_ = rep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step5_tco/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    var env = env\n    var expr = expr\n    while true {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                expr = ast[2]\n                env = letEnv\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            guard !exprsToEval.isEmpty else { throw MalError.invalidArguments(\"do\") }\n            _ = try exprsToEval.dropLast().map { try eval($0, env: env) }\n            expr = exprsToEval.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            switch try eval(ast[1], env: env) {\n            case .bool(false), .null:\n                if let falseBranch = ast[safe: 3] {\n                    expr = falseBranch\n                } else {\n                    expr = .null\n                }\n            default:\n                expr = ast[2]\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let run: ([Expr]) throws -> Expr = { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n\n            let f = Func(ast: ast[2], params: binds, env: env, run: run)\n            return .function(f)\n\n        default:\n            let ast = try ast.map { try eval($0, env: env) }\n            guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }\n\n            let args = Array(ast.dropFirst())\n            if let ast = fn.ast, let fnEnv = fn.env {\n                let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)\n                env = newEnv\n                expr = ast\n            } else {\n                return try fn.run(args)\n            }\n        }\n    default:\n        return expr\n    }\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\n_ = rep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step6_file/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    var env = env\n    var expr = expr\n    while true {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                expr = ast[2]\n                env = letEnv\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            guard !exprsToEval.isEmpty else { throw MalError.invalidArguments(\"do\") }\n            _ = try exprsToEval.dropLast().map { try eval($0, env: env) }\n            expr = exprsToEval.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            switch try eval(ast[1], env: env) {\n            case .bool(false), .null:\n                if let falseBranch = ast[safe: 3] {\n                    expr = falseBranch\n                } else {\n                    expr = .null\n                }\n            default:\n                expr = ast[2]\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let run: ([Expr]) throws -> Expr = { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n\n            let f = Func(ast: ast[2], params: binds, env: env, run: run)\n            return .function(f)\n\n        default:\n            let ast = try ast.map { try eval($0, env: env) }\n            guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }\n\n            let args = Array(ast.dropFirst())\n            if let ast = fn.ast, let fnEnv = fn.env {\n                let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)\n                env = newEnv\n                expr = ast\n            } else {\n                return try fn.run(args)\n            }\n        }\n    default:\n        return expr\n    }\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\nreplEnv.set(forKey: \"eval\", val: .function(Func { args in\n    guard let expr = args.first else { throw MalError.invalidArguments(\"eval\") }\n    return try eval(expr, env: replEnv)\n}))\nreplEnv.set(forKey: \"*ARGV*\", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string)))\n\n_ = rep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\n_ = rep(#\"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"#, env: replEnv)\n\nif CommandLine.arguments.count > 1 {\n    _ = rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\", env: replEnv)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step7_quote/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nprivate func qq_loop(_ elt: Expr, acc: Expr) throws -> Expr {\n    if case let .list(xs, _) = elt {\n        if 0 < xs.count && xs[0] == .symbol(\"splice-unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"splice-unquote\") }\n            return .list([.symbol(\"concat\"), xs[1], acc])\n        }\n    }\n    return .list([.symbol(\"cons\"), try quasiquote(elt), acc])\n}\nprivate func qq_foldr(_ xs: [Expr]) throws -> Expr {\n    var acc : Expr = .list([])\n    for i in stride(from: xs.count-1, through: 0, by: -1) {\n        acc = try qq_loop(xs[i], acc:acc)\n    }\n    return acc\n}\nprivate func quasiquote(_ expr: Expr) throws -> Expr {\n    switch expr {\n    case let .list(xs, _):\n        if 0 < xs.count && xs[0] == .symbol(\"unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"unquote\") }\n            return xs[1]\n        } else {\n            return try qq_foldr(xs)\n        }\n    case let .vector(xs, _):\n        return .list([.symbol(\"vec\"), try qq_foldr(xs)])\n    case .symbol(_), .hashmap(_):\n        return .list([.symbol(\"quote\"), expr])\n    default:\n        return expr\n    }\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    var env = env\n    var expr = expr\n    while true {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                expr = ast[2]\n                env = letEnv\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"quote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quote\") }\n            return ast[1]\n\n        case .symbol(\"quasiquote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quasiquote\") }\n            expr = try quasiquote(ast[1])\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            guard !exprsToEval.isEmpty else { throw MalError.invalidArguments(\"do\") }\n            _ = try exprsToEval.dropLast().map { try eval($0, env: env) }\n            expr = exprsToEval.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            switch try eval(ast[1], env: env) {\n            case .bool(false), .null:\n                if let falseBranch = ast[safe: 3] {\n                    expr = falseBranch\n                } else {\n                    expr = .null\n                }\n            default:\n                expr = ast[2]\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let run: ([Expr]) throws -> Expr = { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n\n            let f = Func(ast: ast[2], params: binds, env: env, run: run)\n            return .function(f)\n\n        default:\n            let ast = try ast.map { try eval($0, env: env) }\n            guard case let .function(fn) = ast[0] else { throw MalError.invalidFunctionCall(ast[0]) }\n\n            let args = Array(ast.dropFirst())\n            if let ast = fn.ast, let fnEnv = fn.env {\n                let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)\n                env = newEnv\n                expr = ast\n            } else {\n                return try fn.run(args)\n            }\n        }\n    default:\n        return expr\n    }\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\nreplEnv.set(forKey: \"eval\", val: .function(Func { args in\n    guard let expr = args.first else { throw MalError.invalidArguments(\"eval\") }\n    return try eval(expr, env: replEnv)\n}))\nreplEnv.set(forKey: \"*ARGV*\", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string)))\n\n_ = rep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\n_ = rep(#\"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"#, env: replEnv)\n\nif CommandLine.arguments.count > 1 {\n    _ = rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\", env: replEnv)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step8_macros/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nprivate func qq_loop(_ elt: Expr, acc: Expr) throws -> Expr {\n    if case let .list(xs, _) = elt {\n        if 0 < xs.count && xs[0] == .symbol(\"splice-unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"splice-unquote\") }\n            return .list([.symbol(\"concat\"), xs[1], acc])\n        }\n    }\n    return .list([.symbol(\"cons\"), try quasiquote(elt), acc])\n}\nprivate func qq_foldr(_ xs: [Expr]) throws -> Expr {\n    var acc : Expr = .list([])\n    for i in stride(from: xs.count-1, through: 0, by: -1) {\n        acc = try qq_loop(xs[i], acc:acc)\n    }\n    return acc\n}\nprivate func quasiquote(_ expr: Expr) throws -> Expr {\n    switch expr {\n    case let .list(xs, _):\n        if 0 < xs.count && xs[0] == .symbol(\"unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"unquote\") }\n            return xs[1]\n        } else {\n            return try qq_foldr(xs)\n        }\n    case let .vector(xs, _):\n        return .list([.symbol(\"vec\"), try qq_foldr(xs)])\n    case .symbol(_), .hashmap(_):\n        return .list([.symbol(\"quote\"), expr])\n    default:\n        return expr\n    }\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    var env = env\n    var expr = expr\n    while true {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                expr = ast[2]\n                env = letEnv\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"quote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quote\") }\n            return ast[1]\n\n        case .symbol(\"quasiquote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quasiquote\") }\n            expr = try quasiquote(ast[1])\n\n        case .symbol(\"defmacro!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"defmacro!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"defmacro!\") }\n\n            guard case let .function(fn) = try eval(ast[2], env: env) else { throw MalError.invalidArguments(\"defmacro!\") }\n            let macros = fn.asMacros()\n            env.set(forKey: name, val: .function(macros))\n            return .function(macros)\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            guard !exprsToEval.isEmpty else { throw MalError.invalidArguments(\"do\") }\n            _ = try exprsToEval.dropLast().map { try eval($0, env: env) }\n            expr = exprsToEval.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            switch try eval(ast[1], env: env) {\n            case .bool(false), .null:\n                if let falseBranch = ast[safe: 3] {\n                    expr = falseBranch\n                } else {\n                    expr = .null\n                }\n            default:\n                expr = ast[2]\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let run: ([Expr]) throws -> Expr = { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n\n            let f = Func(ast: ast[2], params: binds, env: env, run: run)\n            return .function(f)\n\n        default:\n            guard case let .function(fn) = try eval(ast[0], env: env) else { throw MalError.invalidFunctionCall(ast[0]) }\n            if fn.isMacro {\n                expr = try fn.run(Array(ast.dropFirst()))\n                continue\n            }\n            let args = try ast.dropFirst().map { try eval($0, env: env) }\n            if let ast = fn.ast, let fnEnv = fn.env {\n                let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)\n                env = newEnv\n                expr = ast\n            } else {\n                return try fn.run(args)\n            }\n        }\n    default:\n        return expr\n    }\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\nreplEnv.set(forKey: \"eval\", val: .function(Func { args in\n    guard let expr = args.first else { throw MalError.invalidArguments(\"eval\") }\n    return try eval(expr, env: replEnv)\n}))\nreplEnv.set(forKey: \"*ARGV*\", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string)))\n\n_ = rep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\n_ = rep(#\"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"#, env: replEnv)\n_ = rep(#\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\"#, env: replEnv)\n\nif CommandLine.arguments.count > 1 {\n    _ = rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\", env: replEnv)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/step9_try/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nprivate func qq_loop(_ elt: Expr, acc: Expr) throws -> Expr {\n    if case let .list(xs, _) = elt {\n        if 0 < xs.count && xs[0] == .symbol(\"splice-unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"splice-unquote\") }\n            return .list([.symbol(\"concat\"), xs[1], acc])\n        }\n    }\n    return .list([.symbol(\"cons\"), try quasiquote(elt), acc])\n}\nprivate func qq_foldr(_ xs: [Expr]) throws -> Expr {\n    var acc : Expr = .list([])\n    for i in stride(from: xs.count-1, through: 0, by: -1) {\n        acc = try qq_loop(xs[i], acc:acc)\n    }\n    return acc\n}\nprivate func quasiquote(_ expr: Expr) throws -> Expr {\n    switch expr {\n    case let .list(xs, _):\n        if 0 < xs.count && xs[0] == .symbol(\"unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"unquote\") }\n            return xs[1]\n        } else {\n            return try qq_foldr(xs)\n        }\n    case let .vector(xs, _):\n        return .list([.symbol(\"vec\"), try qq_foldr(xs)])\n    case .symbol(_), .hashmap(_):\n        return .list([.symbol(\"quote\"), expr])\n    default:\n        return expr\n    }\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    var env = env\n    var expr = expr\n    while true {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                expr = ast[2]\n                env = letEnv\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"quote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quote\") }\n            return ast[1]\n\n        case .symbol(\"quasiquote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quasiquote\") }\n            expr = try quasiquote(ast[1])\n\n        case .symbol(\"defmacro!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"defmacro!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"defmacro!\") }\n\n            guard case let .function(fn) = try eval(ast[2], env: env) else { throw MalError.invalidArguments(\"defmacro!\") }\n            let macros = fn.asMacros()\n            env.set(forKey: name, val: .function(macros))\n            return .function(macros)\n\n        case .symbol(\"try*\"):\n            if ast.count == 2 {\n                expr = ast[1]\n                continue\n            }\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"try*\") }\n            guard case let .list(values, _) = ast[2], values.count == 3 else { throw MalError.invalidArguments(\"try*\") }\n            guard case .symbol(\"catch*\") = values[0] else { throw MalError.invalidArguments(\"try*\") }\n            guard case let .symbol(bind) = values[1] else { throw MalError.invalidArguments(\"catch*\") }\n\n            do {\n                return try eval(ast[1], env: env)\n            } catch {\n                let malErr = (error as? Expr) ?? .string(error.localizedDescription)\n                let newEnv = try Env(binds: [bind], exprs: [malErr], outer: env)\n                env = newEnv\n                expr = values[2]\n            }\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            guard !exprsToEval.isEmpty else { throw MalError.invalidArguments(\"do\") }\n            _ = try exprsToEval.dropLast().map { try eval($0, env: env) }\n            expr = exprsToEval.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            switch try eval(ast[1], env: env) {\n            case .bool(false), .null:\n                if let falseBranch = ast[safe: 3] {\n                    expr = falseBranch\n                } else {\n                    expr = .null\n                }\n            default:\n                expr = ast[2]\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let run: ([Expr]) throws -> Expr = { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n\n            let f = Func(ast: ast[2], params: binds, env: env, run: run)\n            return .function(f)\n\n        default:\n            guard case let .function(fn) = try eval(ast[0], env: env) else { throw MalError.invalidFunctionCall(ast[0]) }\n            if fn.isMacro {\n                expr = try fn.run(Array(ast.dropFirst()))\n                continue\n            }\n            let args = try ast.dropFirst().map { try eval($0, env: env) }\n            if let ast = fn.ast, let fnEnv = fn.env {\n                let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)\n                env = newEnv\n                expr = ast\n            } else {\n                return try fn.run(args)\n            }\n        }\n    default:\n        return expr\n    }\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\nreplEnv.set(forKey: \"eval\", val: .function(Func { args in\n    guard let expr = args.first else { throw MalError.invalidArguments(\"eval\") }\n    return try eval(expr, env: replEnv)\n}))\nreplEnv.set(forKey: \"*ARGV*\", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string)))\n\n_ = rep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\n_ = rep(#\"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"#, env: replEnv)\n_ = rep(#\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\"#, env: replEnv)\n\nif CommandLine.arguments.count > 1 {\n    _ = rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\", env: replEnv)\n    exit(0)\n}\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/Sources/stepA_mal/main.swift",
    "content": "import Foundation\nimport core\n\nfunc read(_ s: String) throws -> Expr {\n    return try Reader.read(s)\n}\n\nprivate func qq_loop(_ elt: Expr, acc: Expr) throws -> Expr {\n    if case let .list(xs, _) = elt {\n        if 0 < xs.count && xs[0] == .symbol(\"splice-unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"splice-unquote\") }\n            return .list([.symbol(\"concat\"), xs[1], acc])\n        }\n    }\n    return .list([.symbol(\"cons\"), try quasiquote(elt), acc])\n}\nprivate func qq_foldr(_ xs: [Expr]) throws -> Expr {\n    var acc : Expr = .list([])\n    for i in stride(from: xs.count-1, through: 0, by: -1) {\n        acc = try qq_loop(xs[i], acc:acc)\n    }\n    return acc\n}\nprivate func quasiquote(_ expr: Expr) throws -> Expr {\n    switch expr {\n    case let .list(xs, _):\n        if 0 < xs.count && xs[0] == .symbol(\"unquote\") {\n            guard xs.count == 2 else { throw MalError.invalidArguments(\"unquote\") }\n            return xs[1]\n        } else {\n            return try qq_foldr(xs)\n        }\n    case let .vector(xs, _):\n        return .list([.symbol(\"vec\"), try qq_foldr(xs)])\n    case .symbol(_), .hashmap(_):\n        return .list([.symbol(\"quote\"), expr])\n    default:\n        return expr\n    }\n}\n\nfunc eval(_ expr: Expr, env: Env) throws -> Expr {\n\n    var env = env\n    var expr = expr\n    while true {\n\n    switch env.get(\"DEBUG-EVAL\") {\n        case nil, .bool(false), .null: break\n        default: print(\"EVAL: \" + print(expr))\n    }\n\n    switch expr {\n    case let .symbol(name):\n        let val = env.get(name)\n        guard val != nil else { throw MalError.symbolNotFound(name) }\n        return val!\n    case let .vector(values, _):\n        return .vector(try values.map { try eval($0, env: env) })\n    case let .hashmap(values, _):\n        return .hashmap(try values.mapValues { try eval($0, env: env) })\n    case let .list(ast, _):\n\n        if ast.isEmpty {\n            return expr\n        }\n\n        switch ast[0] {\n\n        case .symbol(\"def!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"def!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"def!\") }\n\n            let val = try eval(ast[2], env: env)\n            env.set(forKey: name, val: val)\n            return val\n\n        case .symbol(\"let*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"let*\") }\n\n            switch ast[1] {\n            case let .list(bindable, _), let .vector(bindable, _):\n                let letEnv = Env(outer: env)\n\n                for i in stride(from: 0, to: bindable.count - 1, by: 2) {\n                    guard case let .symbol(key) = bindable[i] else { throw MalError.invalidArguments(\"let*\") }\n                    let value = bindable[i + 1]\n                    letEnv.set(forKey: key, val: try eval(value, env: letEnv))\n                }\n\n                expr = ast[2]\n                env = letEnv\n            default:\n                throw MalError.invalidArguments(\"let*\")\n            }\n\n        case .symbol(\"quote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quote\") }\n            return ast[1]\n\n        case .symbol(\"quasiquote\"):\n            guard ast.count == 2 else { throw MalError.invalidArguments(\"quasiquote\") }\n            expr = try quasiquote(ast[1])\n\n        case .symbol(\"defmacro!\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"defmacro!\") }\n            guard case let .symbol(name) = ast[1] else { throw MalError.invalidArguments(\"defmacro!\") }\n\n            guard case let .function(fn) = try eval(ast[2], env: env) else { throw MalError.invalidArguments(\"defmacro!\") }\n            let macros = fn.asMacros()\n            env.set(forKey: name, val: .function(macros))\n            return .function(macros)\n\n        case .symbol(\"try*\"):\n            if ast.count == 2 {\n                expr = ast[1]\n                continue\n            }\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"try*\") }\n            guard case let .list(values, _) = ast[2], values.count == 3 else { throw MalError.invalidArguments(\"try*\") }\n            guard case .symbol(\"catch*\") = values[0] else { throw MalError.invalidArguments(\"try*\") }\n            guard case let .symbol(bind) = values[1] else { throw MalError.invalidArguments(\"catch*\") }\n\n            do {\n                return try eval(ast[1], env: env)\n            } catch {\n                let malErr = (error as? Expr) ?? .string(error.localizedDescription)\n                let newEnv = try Env(binds: [bind], exprs: [malErr], outer: env)\n                env = newEnv\n                expr = values[2]\n            }\n\n        case .symbol(\"do\"):\n            let exprsToEval = ast.dropFirst()\n            guard !exprsToEval.isEmpty else { throw MalError.invalidArguments(\"do\") }\n            _ = try exprsToEval.dropLast().map { try eval($0, env: env) }\n            expr = exprsToEval.last!\n\n        case .symbol(\"if\"):\n            guard 3...4 ~= ast.count else { throw MalError.invalidArguments(\"if\") }\n\n            switch try eval(ast[1], env: env) {\n            case .bool(false), .null:\n                if let falseBranch = ast[safe: 3] {\n                    expr = falseBranch\n                } else {\n                    expr = .null\n                }\n            default:\n                expr = ast[2]\n            }\n\n        case .symbol(\"fn*\"):\n            guard ast.count == 3 else { throw MalError.invalidArguments(\"fn*\") }\n            let binds: [String]\n\n            switch ast[1] {\n            case let .list(xs, _), let .vector(xs, _):\n                binds = try xs.map {\n                    guard case let .symbol(name) = $0 else { throw MalError.invalidArguments(\"fn*\") }\n                    return name\n                }\n            default:\n                throw MalError.invalidArguments(\"fn*\")\n            }\n\n            let run: ([Expr]) throws -> Expr = { args in\n                let fEnv = try Env(binds: binds, exprs: args, outer: env)\n                return try eval(ast[2], env: fEnv)\n            }\n\n            let f = Func(ast: ast[2], params: binds, env: env, run: run)\n            return .function(f)\n\n        default:\n            guard case let .function(fn) = try eval(ast[0], env: env) else { throw MalError.invalidFunctionCall(ast[0]) }\n            if fn.isMacro {\n                expr = try fn.run(Array(ast.dropFirst()))\n                continue\n            }\n            let args = try ast.dropFirst().map { try eval($0, env: env) }\n            if let ast = fn.ast, let fnEnv = fn.env {\n                let newEnv = try Env(binds: fn.params, exprs: args, outer: fnEnv)\n                env = newEnv\n                expr = ast\n            } else {\n                return try fn.run(args)\n            }\n        }\n    default:\n        return expr\n    }\n    }\n}\n\nfunc print(_ expr: Expr) -> String {\n    return Expr.print(expr)\n}\n\n@discardableResult\nfunc rep(_ s: String, env: Env) -> String {\n    do {\n        let expr = try read(s)\n        let resExpr = try eval(expr, env: env)\n        let resultStr = print(resExpr)\n        return resultStr\n    } catch {\n        return error.localizedDescription\n    }\n}\n\nlet replEnv: Env = Env(data: Core.ns.data)\n\nreplEnv.set(forKey: \"eval\", val: .function(Func { args in\n    guard let expr = args.first else { throw MalError.invalidArguments(\"eval\") }\n    return try eval(expr, env: replEnv)\n}))\nreplEnv.set(forKey: \"*ARGV*\", val: .list(CommandLine.arguments.dropFirst(2).map(Expr.string)))\nreplEnv.set(forKey: \"*host-language*\", val: .string(\"swift6\"))\n\nrep(\"(def! not (fn* (a) (if a false true)))\", env: replEnv)\nrep(#\"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\"#, env: replEnv)\nrep(#\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\"#, env: replEnv)\n\nif CommandLine.arguments.count > 1 {\n    rep(\"(load-file \\\"\" + CommandLine.arguments[1] + \"\\\")\", env: replEnv)\n    exit(0)\n}\n\nrep(#\"(println (str \"Mal [\" *host-language* \"]\"))\"#, env: replEnv)\n\nwhile true {\n    print(\"user> \", terminator: \"\")\n    guard let s = readLine() else { break }\n    print(rep(s, env: replEnv))\n}\n"
  },
  {
    "path": "impls/swift6/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/.build/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/tcl/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install tcl tcl-tclreadline\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/tcl/Makefile",
    "content": "SOURCES_BASE = mal_readline.tcl types.tcl reader.tcl printer.tcl\nSOURCES_LISP = env.tcl core.tcl stepA_mal.tcl\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall:\n\ttrue\n\ndist: mal.tcl mal\n\nmal.tcl: $(SOURCES)\n\tcat $+ | grep -v \"^source \" > $@\n\nmal: mal.tcl\n\techo \"#!/usr/bin/env tclsh\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal.tcl mal\n"
  },
  {
    "path": "impls/tcl/core.tcl",
    "content": "proc mal_equal {a} {\n    bool_new [equal_q [lindex $a 0] [lindex $a 1]]\n}\n\nset ::mal_exception_obj 0\nproc mal_throw {a} {\n    set ::mal_exception_obj [lindex $a 0]\n    error \"__MalException__\"\n}\n\nproc mal_nil_q {a} {\n    bool_new [nil_q [lindex $a 0]]\n}\n\nproc mal_true_q {a} {\n    bool_new [true_q [lindex $a 0]]\n}\n\nproc mal_false_q {a} {\n    bool_new [false_q [lindex $a 0]]\n}\n\nproc mal_symbol {a} {\n    symbol_new [obj_val [lindex $a 0]]\n}\n\nproc mal_symbol_q {a} {\n    bool_new [symbol_q [lindex $a 0]]\n}\n\nproc mal_string_q {a} {\n    bool_new [string_q [lindex $a 0]]\n}\n\nproc mal_keyword {a} {\n    lassign $a a0\n    if {[keyword_q $a0]} {\n        return $a0\n    }\n    keyword_new [obj_val $a0]\n}\n\nproc mal_keyword_q {a} {\n    bool_new [keyword_q [lindex $a 0]]\n}\n\nproc mal_number_q {a} {\n    bool_new [integer_q [lindex $a 0]]\n}\n\nproc mal_fn_q {a} {\n    set f [lindex $a 0]\n    switch [obj_type $f] {\n        function { return [bool_new [expr {![macro_q $f]}]] }\n        nativefunction { return $::mal_true }\n        default { return $::mal_false }\n    }\n}\n\nproc mal_macro_q {a} {\n    bool_new [macro_q [lindex $a 0]]\n}\n\nproc render_array {arr readable delim} {\n    set res {}\n    foreach e $arr {\n        lappend res [pr_str $e $readable]\n    }\n    join $res $delim\n}\n\nproc mal_pr_str {a} {\n    string_new [render_array $a 1 \" \"]\n}\n\nproc mal_str {a} {\n    string_new [render_array $a 0 \"\"]\n}\n\nproc mal_prn {a} {\n    puts [render_array $a 1 \" \"]\n    return $::mal_nil\n}\n\nproc mal_println {a} {\n    puts [render_array $a 0 \" \"]\n    return $::mal_nil\n}\n\nproc mal_read_string {a} {\n    read_str [obj_val [lindex $a 0]]\n}\n\nproc mal_readline {a} {\n    set prompt [obj_val [lindex $a 0]]\n    set res [_readline $prompt]\n    if {[lindex $res 0] == \"EOF\"} {\n        return $::mal_nil\n    }\n    string_new [lindex $res 1]\n}\n\nproc mal_slurp {a} {\n    set filename [obj_val [lindex $a 0]]\n    set file [open $filename]\n    set content [read $file]\n    close $file\n    string_new $content\n}\n\nproc mal_lt {a} {\n    bool_new [expr {[obj_val [lindex $a 0]] < [obj_val [lindex $a 1]]}]\n}\n\nproc mal_lte {a} {\n    bool_new [expr {[obj_val [lindex $a 0]] <= [obj_val [lindex $a 1]]}]\n}\n\nproc mal_gt {a} {\n    bool_new [expr {[obj_val [lindex $a 0]] > [obj_val [lindex $a 1]]}]\n}\n\nproc mal_gte {a} {\n    bool_new [expr {[obj_val [lindex $a 0]] >= [obj_val [lindex $a 1]]}]\n}\n\nproc mal_add {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] + [obj_val [lindex $a 1]]}]\n}\n\nproc mal_sub {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] - [obj_val [lindex $a 1]]}]\n}\n\nproc mal_mul {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] * [obj_val [lindex $a 1]]}]\n}\n\nproc mal_div {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] / [obj_val [lindex $a 1]]}]\n}\n\nproc mal_time_ms {a} {\n    integer_new [clock milliseconds]\n}\n\nproc mal_list {a} {\n    list_new $a\n}\n\nproc mal_list_q {a} {\n    bool_new [list_q [lindex $a 0]]\n}\n\nproc mal_vector {a} {\n    vector_new $a\n}\n\nproc mal_vector_q {a} {\n    bool_new [vector_q [lindex $a 0]]\n}\n\nproc mal_hash_map {a} {\n    set d [dict create]\n    foreach {k v} $a {\n        dict set d [obj_val $k] $v\n    }\n    hashmap_new $d\n}\n\nproc mal_map_q {a} {\n    bool_new [hashmap_q [lindex $a 0]]\n}\n\nproc mal_assoc {a} {\n    set d [dict create]\n    dict for {k v} [obj_val [lindex $a 0]] {\n        dict set d $k $v\n    }\n    foreach {k v} [lrange $a 1 end] {\n        dict set d [obj_val $k] $v\n    }\n    hashmap_new $d\n}\n\nproc mal_dissoc {a} {\n    set d [dict create]\n    dict for {k v} [obj_val [lindex $a 0]] {\n        dict set d $k $v\n    }\n    foreach k [lrange $a 1 end] {\n        dict unset d [obj_val $k]\n    }\n    hashmap_new $d\n}\n\nproc mal_get {a} {\n    lassign $a hashmap_obj key_obj\n    if {[dict exists [obj_val $hashmap_obj] [obj_val $key_obj]]} {\n        dict get [obj_val $hashmap_obj] [obj_val $key_obj]\n    } else {\n        return $::mal_nil\n    }\n}\n\nproc mal_contains_q {a} {\n    lassign $a hashmap_obj key_obj\n    bool_new [dict exists [obj_val $hashmap_obj] [obj_val $key_obj]]\n}\n\nproc mal_keys {a} {\n    set res {}\n    foreach k [dict keys [obj_val [lindex $a 0]]] {\n        lappend res [string_new $k]\n    }\n    list_new $res\n}\n\nproc mal_vals {a} {\n    list_new [dict values [obj_val [lindex $a 0]]]\n}\n\nproc mal_sequential_q {a} {\n    bool_new [sequential_q [lindex $a 0]]\n}\n\nproc mal_cons {a} {\n    lassign $a head lst\n    list_new [concat [list $head] [obj_val $lst]]\n}\n\nproc mal_concat {a} {\n    set res {}\n    foreach lst $a {\n        if {[nil_q $lst]} {\n            continue\n        }\n        set res [concat $res [obj_val $lst]]\n    }\n    list_new $res\n}\n\nproc mal_vec {a} {\n    lassign $a a0\n    if {[vector_q $a0]} {\n        return $a0\n    } elseif {[list_q $a0]} {\n        return [vector_new [obj_val $a0]]\n    } else {\n        error \"vec requires list or vector\"\n    }\n}\n\nproc mal_nth {a} {\n    lassign $a lst_obj index_obj\n    set index [obj_val $index_obj]\n    set lst [obj_val $lst_obj]\n    if {$index >= [llength $lst]} {\n        error \"nth: index out of range\"\n    }\n    lindex $lst $index\n}\n\nproc mal_first {a} {\n    lassign $a lst\n    if {[nil_q $lst] || [llength [obj_val $lst]] == 0} {\n        return $::mal_nil\n    }\n    lindex [obj_val $lst] 0\n}\n\nproc mal_rest {a} {\n    lassign $a lst\n    list_new [lrange [obj_val $lst] 1 end]\n}\n\nproc mal_empty_q {a} {\n    bool_new [expr {[llength [obj_val [lindex $a 0]]] == 0}]\n}\n\nproc mal_count {a} {\n    integer_new [llength [obj_val [lindex $a 0]]]\n}\n\nproc mal_apply {a} {\n    set f [lindex $a 0]\n    if {[llength $a] > 1} {\n        set mid_args [lrange $a 1 end-1]\n        set last_list [lindex $a end]\n        set apply_args [concat $mid_args [obj_val $last_list]]\n    } else {\n        set apply_args {}\n    }\n\n    switch [obj_type $f] {\n        function {\n            set funcdict [obj_val $f]\n            set body [dict get $funcdict body]\n            set env [dict get $funcdict env]\n            set binds [dict get $funcdict binds]\n            set funcenv [Env new $env $binds $apply_args]\n            return [EVAL $body $funcenv]\n        }\n        nativefunction {\n            set body [concat [list [obj_val $f]] {$a}]\n            set lambda [list {a} $body]\n            return [apply $lambda $apply_args]\n        }\n        default {\n            error \"Not a function\"\n        }\n    }\n}\n\nproc mal_map {a} {\n    lassign $a f seq\n    set res {}\n    foreach item [obj_val $seq] {\n        set mappeditem [mal_apply [list $f [list_new [list $item]]]]\n        lappend res $mappeditem\n    }\n    list_new $res\n}\n\nproc mal_conj {a} {\n    lassign $a a0\n    if {[list_q $a0]} {\n        set lst $a0\n        foreach item [lrange $a 1 end] {\n            set lst [mal_cons [list $item $lst]]\n        }\n        return $lst\n    } elseif {[vector_q $a0]} {\n        set res [obj_val $a0]\n        foreach item [lrange $a 1 end] {\n            lappend res $item\n        }\n        vector_new $res\n    } else {\n        error \"conj requires list or vector\"\n    }\n}\n\nproc mal_seq {a} {\n    lassign $a a0\n    if {[string_q $a0]} {\n        set str [obj_val $a0]\n        if {$str == \"\"} {\n            return $::mal_nil\n        }\n        set res {}\n        foreach char [split $str {}] {\n            lappend res [string_new $char]\n        }\n        list_new $res\n    } elseif {[list_q $a0]} {\n        if {[llength [obj_val $a0]] == 0} {\n            return $::mal_nil\n        }\n        return $a0\n    } elseif {[vector_q $a0]} {\n        if {[llength [obj_val $a0]] == 0} {\n            return $::mal_nil\n        }\n        list_new [obj_val $a0]\n    } elseif {[nil_q $a0]} {\n        return $::mal_nil\n    } else {\n        error \"seq requires string or list or vector or nil\"\n    }\n}\n\nproc mal_meta {a} {\n    obj_meta [lindex $a 0]\n}\n\nproc mal_with_meta {a} {\n    lassign $a a0 a1\n    obj_new [obj_type $a0] [obj_val $a0] $a1\n}\n\nproc mal_atom {a} {\n    atom_new [lindex $a 0]\n}\n\nproc mal_atom_q {a} {\n    bool_new [atom_q [lindex $a 0]]\n}\n\nproc mal_deref {a} {\n    obj_val [lindex $a 0]\n}\n\nproc mal_reset_bang {a} {\n    lassign $a a0 a1\n    obj_set_val $a0 $a1\n}\n\nproc mal_swap_bang {a} {\n    lassign $a a0 f\n    set apply_args [concat [list [obj_val $a0]] [lrange $a 2 end]]\n    set newval [mal_apply [list $f [list_new $apply_args]]]\n    mal_reset_bang [list $a0 $newval]\n}\n\nset core_ns [dict create \\\n    \"=\"            [nativefunction_new mal_equal] \\\n    \"throw\"        [nativefunction_new mal_throw] \\\n    \\\n    \"nil?\"         [nativefunction_new mal_nil_q] \\\n    \"true?\"        [nativefunction_new mal_true_q] \\\n    \"false?\"       [nativefunction_new mal_false_q] \\\n    \"symbol\"       [nativefunction_new mal_symbol] \\\n    \"symbol?\"      [nativefunction_new mal_symbol_q] \\\n    \"string?\"      [nativefunction_new mal_string_q] \\\n    \"keyword\"      [nativefunction_new mal_keyword] \\\n    \"keyword?\"     [nativefunction_new mal_keyword_q] \\\n    \"number?\"      [nativefunction_new mal_number_q] \\\n    \"fn?\"          [nativefunction_new mal_fn_q] \\\n    \"macro?\"       [nativefunction_new mal_macro_q] \\\n    \\\n    \"pr-str\"       [nativefunction_new mal_pr_str] \\\n    \"str\"          [nativefunction_new mal_str] \\\n    \"prn\"          [nativefunction_new mal_prn] \\\n    \"println\"      [nativefunction_new mal_println] \\\n    \"read-string\"  [nativefunction_new mal_read_string] \\\n    \"readline\"     [nativefunction_new mal_readline] \\\n    \"slurp\"        [nativefunction_new mal_slurp] \\\n    \\\n    \"<\"            [nativefunction_new mal_lt] \\\n    \"<=\"           [nativefunction_new mal_lte] \\\n    \">\"            [nativefunction_new mal_gt] \\\n    \">=\"           [nativefunction_new mal_gte] \\\n    \"+\"            [nativefunction_new mal_add] \\\n    \"-\"            [nativefunction_new mal_sub] \\\n    \"*\"            [nativefunction_new mal_mul] \\\n    \"/\"            [nativefunction_new mal_div] \\\n    \"time-ms\"      [nativefunction_new mal_time_ms] \\\n    \\\n    \"list\"         [nativefunction_new mal_list] \\\n    \"list?\"        [nativefunction_new mal_list_q] \\\n    \"vector\"       [nativefunction_new mal_vector] \\\n    \"vector?\"      [nativefunction_new mal_vector_q] \\\n    \"hash-map\"     [nativefunction_new mal_hash_map] \\\n    \"map?\"         [nativefunction_new mal_map_q] \\\n    \"assoc\"        [nativefunction_new mal_assoc] \\\n    \"dissoc\"       [nativefunction_new mal_dissoc] \\\n    \"get\"          [nativefunction_new mal_get] \\\n    \"contains?\"    [nativefunction_new mal_contains_q] \\\n    \"keys\"         [nativefunction_new mal_keys] \\\n    \"vals\"         [nativefunction_new mal_vals] \\\n    \\\n    \"sequential?\"  [nativefunction_new mal_sequential_q] \\\n    \"cons\"         [nativefunction_new mal_cons] \\\n    \"concat\"       [nativefunction_new mal_concat] \\\n    \"vec\"          [nativefunction_new mal_vec] \\\n    \"nth\"          [nativefunction_new mal_nth] \\\n    \"first\"        [nativefunction_new mal_first] \\\n    \"rest\"         [nativefunction_new mal_rest] \\\n    \"empty?\"       [nativefunction_new mal_empty_q] \\\n    \"count\"        [nativefunction_new mal_count] \\\n    \"apply\"        [nativefunction_new mal_apply] \\\n    \"map\"          [nativefunction_new mal_map] \\\n    \\\n    \"conj\"         [nativefunction_new mal_conj] \\\n    \"seq\"          [nativefunction_new mal_seq] \\\n    \\\n    \"meta\"         [nativefunction_new mal_meta] \\\n    \"with-meta\"    [nativefunction_new mal_with_meta] \\\n    \"atom\"         [nativefunction_new mal_atom] \\\n    \"atom?\"        [nativefunction_new mal_atom_q] \\\n    \"deref\"        [nativefunction_new mal_deref] \\\n    \"reset!\"       [nativefunction_new mal_reset_bang] \\\n    \"swap!\"        [nativefunction_new mal_swap_bang] \\\n]\n"
  },
  {
    "path": "impls/tcl/env.tcl",
    "content": "oo::class create Env {\n    variable outer data\n\n    constructor {{outerenv 0} {binds \"\"} {exprs \"\"}} {\n        set outer $outerenv\n        set data [dict create]\n        if {$binds != \"\"} {\n            for {set i 0} {$i < [llength $binds]} {incr i} {\n                set b [lindex $binds $i]\n                if {$b == \"&\"} {\n                    set varrest [lindex $binds [expr {$i + 1}]]\n                    set restexprs [list_new [lrange $exprs $i end]]\n                    my set $varrest $restexprs\n                    break\n                } else {\n                    my set $b [lindex $exprs $i]\n                }\n            }\n        }\n    }\n\n    method set {symbol objval} {\n        dict set data $symbol $objval\n        return $objval\n    }\n\n    method find {symbol} {\n        if {[dict exist $data $symbol]} {\n            return [self]\n        } elseif {$outer != 0} {\n            return [$outer find $symbol]\n        } else {\n            return 0\n        }\n    }\n\n    method get {symbol} {\n        set foundenv [my find $symbol]\n        if {$foundenv == 0} {\n            error \"'$symbol' not found\"\n        } else {\n            return [$foundenv get_symbol $symbol]\n        }\n    }\n\n    method get_symbol {symbol} {\n        dict get $data $symbol\n    }\n}\n"
  },
  {
    "path": "impls/tcl/mal_readline.tcl",
    "content": "if {[lindex $argv 0] == \"--raw\"} {\n    set ::readline_mode \"raw\"\n    set argv [lrange $argv 1 end]\n    incr argc -1\n} else {\n    if {[catch {package require tclreadline}]} {\n        set ::readline_mode \"raw\"\n    } else {\n        set ::readline_mode \"library\"\n    }\n}\n\nset ::historyfile \"$env(HOME)/.mal-history\"\nset ::readline_library_initalized 0\nproc readline_library_init {} {\n    if {$::readline_library_initalized} {\n        return\n    }\n\n    ::tclreadline::readline initialize $::historyfile\n    ::tclreadline::readline builtincompleter 0\n    ::tclreadline::readline customcompleter \"\"\n    set ::readline_library_initalized 1\n}\n\nproc _readline_library prompt {\n    readline_library_init\n\n    set reached_eof 0\n    ::tclreadline::readline eofchar { set reached_eof 1 }\n    set line [::tclreadline::readline read $prompt]\n    if {$reached_eof} {\n        return {\"EOF\" \"\"}\n    }\n    ::tclreadline::readline write $::historyfile\n    list \"OK\" $line\n}\n\nproc _readline_raw prompt {\n    puts -nonewline $prompt\n    flush stdout\n    if {[gets stdin line] < 0} {\n        return {\"EOF\" \"\"}\n    }\n    list \"OK\" $line\n}\n\nproc _readline prompt {\n    if {$::readline_mode == \"library\"} {\n        _readline_library $prompt\n    } else {\n        _readline_raw $prompt\n    }\n}\n"
  },
  {
    "path": "impls/tcl/printer.tcl",
    "content": "proc format_list {elements start_char end_char readable} {\n    set res {}\n    foreach element $elements {\n        lappend res [pr_str $element $readable]\n    }\n    set joined [join $res \" \"]\n    return \"${start_char}${joined}${end_char}\"\n}\n\nproc format_hashmap {dictionary readable} {\n    set lst {}\n    dict for {keystr valobj} $dictionary {\n        lappend lst [string_new $keystr]\n        lappend lst $valobj\n    }\n    format_list $lst \"\\{\" \"\\}\" $readable\n}\n\nproc format_string {str readable} {\n    if {[string index $str 0] == \"\\u029E\"} {\n        return \":[string range $str 1 end]\"\n    } elseif {$readable} {\n        set escaped [string map {\"\\n\" \"\\\\n\" \"\\\"\" \"\\\\\\\"\" \"\\\\\" \"\\\\\\\\\"} $str]\n        return \"\\\"$escaped\\\"\"\n    } else {\n        return $str\n    }\n}\n\nproc format_function {funcdict} {\n    set type \"function\"\n    if {[dict get $funcdict is_macro]} {\n        set type \"macro\"\n    }\n    return \"<$type:args=[join [dict get $funcdict binds] \",\"]>\"\n}\n\nproc pr_str {ast readable} {\n    set nodetype [obj_type $ast]\n    set nodevalue [obj_val $ast]\n    switch $nodetype {\n        nil            { return \"nil\" }\n        true           { return \"true\" }\n        false          { return \"false\" }\n        integer        { return $nodevalue }\n        symbol         { return $nodevalue }\n        string         { return [format_string $nodevalue $readable] }\n        list           { return [format_list $nodevalue \"(\" \")\" $readable] }\n        vector         { return [format_list $nodevalue \"\\[\" \"\\]\" $readable] }\n        hashmap        { return [format_hashmap [dict get $nodevalue] $readable] }\n        atom           { return \"(atom [pr_str $nodevalue $readable])\" }\n        function       { return [format_function $nodevalue] }\n        nativefunction { return \"<nativefunction:$nodevalue>\" }\n        default        { error \"cannot print type $nodetype\" }\n    }\n}\n"
  },
  {
    "path": "impls/tcl/reader.tcl",
    "content": "oo::class create Reader {\n    variable pos tokens\n\n    constructor {tokens_list} {\n        set tokens $tokens_list\n        set pos 0\n    }\n\n    method peek {} {\n        lindex $tokens $pos \n    }\n\n    method next {} {\n        set token [my peek]\n        incr pos\n        return $token\n    }\n}\n\nproc tokenize str {\n    set re {[\\s,]*(~@|[\\[\\]\\{\\}()'`~^@]|\\\"(?:\\\\.|[^\\\\\\\"])*\\\"?|;[^\\n]*|[^\\s\\[\\]\\{\\}('\\\"`~^@,;)]*)}\n    set tokens {}\n    foreach {_ capture} [regexp -all -inline $re $str] {\n        if {[string length $capture] > 0 && [string range $capture 0 0] != \";\"} {\n            lappend tokens $capture\n        }\n    }\n    return $tokens\n}\n\nproc read_tokens_list {reader start_char end_char} {\n    set token [$reader next]\n    if {$token != $start_char} {\n        error \"expected '$start_char', got EOF\"\n    }\n\n    set elements {}\n    set token [$reader peek]\n    while {$token != $end_char} {\n        if {$token == \"\"} {\n            error \"expected '$end_char', got EOF\"\n        }\n        lappend elements [read_form $reader]\n        set token [$reader peek]\n    }\n    $reader next\n    return $elements\n}\n\nproc read_list {reader} {\n    set elements [read_tokens_list $reader \"(\" \")\"]\n    list_new $elements\n}\n\nproc read_vector {reader} {\n    set elements [read_tokens_list $reader \"\\[\" \"\\]\"]\n    vector_new $elements\n}\n\nproc read_hashmap {reader} {\n    set res [dict create]\n    foreach {keytoken valtoken} [read_tokens_list $reader \"{\" \"}\"] {\n        dict set res [obj_val $keytoken] $valtoken\n    }\n    hashmap_new $res\n}\n\nproc parse_string {str} {\n    set res [string range $str 1 end-1]\n    string map {\"\\\\n\" \"\\n\" \"\\\\\\\"\" \"\\\"\" \"\\\\\\\\\" \"\\\\\"} $res\n}\n\nproc parse_keyword {str} {\n    # Remove initial \":\"\n    string range $str 1 end\n}\n\nproc read_atom {reader} {\n    set token [$reader next]\n    switch -regexp $token {\n        ^-?[0-9]+$ { return [obj_new \"integer\" $token] }\n        ^nil$      { return $::mal_nil }\n        ^true$     { return $::mal_true }\n        ^false$    { return $::mal_false }\n        ^:         { return [keyword_new [parse_keyword $token]] }\n        ^\\\"(\\\\\\\\.|[^\\\\\\\\\\\"])*\\\"$\n\t           { return [string_new [parse_string $token]] }\n        ^\\\"        { error \"expected '\\\"', got EOF\" }\n        default    { return [symbol_new $token] }\n    }\n}\n\nproc symbol_shortcut {symbol_name reader} {\n    $reader next\n    list_new [list [symbol_new $symbol_name] [read_form $reader]]\n}\n\nproc read_form {reader} {\n    switch [$reader peek] {\n        \"'\"     { return [symbol_shortcut \"quote\" $reader] }\n        \"`\"     { return [symbol_shortcut \"quasiquote\" $reader] }\n        \"~\"     { return [symbol_shortcut \"unquote\" $reader] }\n        \"~@\"    { return [symbol_shortcut \"splice-unquote\" $reader] }\n        \"^\"     {\n            $reader next\n            set meta [read_form $reader]\n            return [list_new [list [symbol_new \"with-meta\"] [read_form $reader] $meta]]\n        }\n        \"@\"     { return [symbol_shortcut \"deref\" $reader] }\n        \"(\"     { return [read_list $reader] }\n        \")\"     { error \"unexpected ')'\" }\n        \"\\[\"    { return [read_vector $reader] }\n        \"\\]\"    { error \"unexpected '\\]'\" }\n        \"\\{\"    { return [read_hashmap $reader] }\n        \"\\}\"    { error \"unexpected '\\}'\" }\n        default { return [read_atom $reader] }\n    }\n}\n\nproc read_str str {\n    set tokens [tokenize $str]\n    set reader [Reader new $tokens]\n    set res [read_form $reader]\n    $reader destroy\n    return $res\n}\n"
  },
  {
    "path": "impls/tcl/run",
    "content": "#!/usr/bin/env bash\nexec tclsh $(dirname $0)/${STEP:-stepA_mal}.tcl ${RAW:+--raw} \"${@}\"\n"
  },
  {
    "path": "impls/tcl/step0_repl.tcl",
    "content": "source mal_readline.tcl\n\nproc READ str {\n    return $str\n}\n\nproc EVAL {ast env} {\n    return $ast\n}\n\nproc PRINT exp {\n    return $exp\n}\n\nproc REP str {\n    PRINT [EVAL [READ $str] {}]\n}\n\nfconfigure stdout -translation binary\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    puts [REP $line]\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step1_read_print.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc EVAL {ast env} {\n    return $ast\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP str {\n    PRINT [EVAL [READ $str] {}]\n}\n\nfconfigure stdout -translation binary\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line] } exception] } {\n        puts \"Error: $exception\"\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step2_eval.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc EVAL {ast env} {\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            if {[dict exists $env $varname]} {\n                return [dict get $env $varname]\n            } else {\n                error \"'$varname' not found\"\n            }\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n    set a0 [lindex [obj_val $ast] 0]\n    if {$a0 == \"\"} {\n        return $ast\n    }\n    set lst {}\n    foreach element [obj_val $ast] {\n        lappend lst [EVAL $element $env]\n    }\n    set f [lindex $lst 0]\n    set call_args [lrange $lst 1 end]\n    apply $f $call_args\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc mal_add {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] + [obj_val [lindex $a 1]]}]\n}\n\nproc mal_sub {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] - [obj_val [lindex $a 1]]}]\n}\n\nproc mal_mul {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] * [obj_val [lindex $a 1]]}]\n}\n\nproc mal_div {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] / [obj_val [lindex $a 1]]}]\n}\n\nset repl_env [dict create \\\n    \"+\" {{a} {mal_add $a}} \\\n    \"-\" {{a} {mal_sub $a}} \\\n    \"*\" {{a} {mal_mul $a}} \\\n    \"/\" {{a} {mal_div $a}} \\\n]\n\nfconfigure stdout -translation binary\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step3_env.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc EVAL {ast env} {\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n    set a0 [lindex [obj_val $ast] 0]\n    if {$a0 == \"\"} {\n        return $ast\n    }\n    set a1 [lindex [obj_val $ast] 1]\n    set a2 [lindex [obj_val $ast] 2]\n    switch [obj_val $a0] {\n        \"def!\" {\n            set varname [obj_val $a1]\n            set value [EVAL $a2 $env]\n            return [$env set $varname $value]\n        }\n        \"let*\" {\n            set letenv [Env new $env]\n            set bindings_list [obj_val $a1]\n            foreach {varnameobj varvalobj} $bindings_list {\n                $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n            }\n            return [EVAL $a2 $letenv]\n        }\n        default {\n            set lst {}\n            foreach element [obj_val $ast] {\n                lappend lst [EVAL $element $env]\n            }\n            set f [lindex $lst 0]\n            set call_args [lrange $lst 1 end]\n            return [apply $f $call_args]\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc mal_add {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] + [obj_val [lindex $a 1]]}]\n}\n\nproc mal_sub {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] - [obj_val [lindex $a 1]]}]\n}\n\nproc mal_mul {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] * [obj_val [lindex $a 1]]}]\n}\n\nproc mal_div {a} {\n    integer_new [expr {[obj_val [lindex $a 0]] / [obj_val [lindex $a 1]]}]\n}\n\nset repl_env [Env new]\n$repl_env set \"+\" {{a} {mal_add $a}}\n$repl_env set \"-\" {{a} {mal_sub $a}}\n$repl_env set \"*\" {{a} {mal_mul $a}}\n$repl_env set \"/\" {{a} {mal_div $a}}\n\nfconfigure stdout -translation binary\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step4_if_fn_do.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc EVAL {ast env} {\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n    lassign [obj_val $ast] a0 a1 a2 a3\n    if {$a0 == \"\"} {\n        return $ast\n    }\n    switch [obj_val $a0] {\n        \"def!\" {\n            set varname [obj_val $a1]\n            set value [EVAL $a2 $env]\n            return [$env set $varname $value]\n        }\n        \"let*\" {\n            set letenv [Env new $env]\n            set bindings_list [obj_val $a1]\n            foreach {varnameobj varvalobj} $bindings_list {\n                $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n            }\n            return [EVAL $a2 $letenv]\n        }\n        \"do\" {\n            foreach element [lrange [obj_val $ast] 1 end-1] {\n                EVAL $element $env\n            }\n            return [EVAL [lindex [obj_val $ast] end] $env]\n        }\n        \"if\" {\n            set condval [EVAL $a1 $env]\n            if {[false_q $condval] || [nil_q $condval]} {\n                if {$a3 == \"\"} {\n                    return $::mal_nil\n                }\n                return [EVAL $a3 $env]\n            }\n            return [EVAL $a2 $env]\n        }\n        \"fn*\" {\n            set binds {}\n            foreach v [obj_val $a1] {\n                lappend binds [obj_val $v]\n            }\n            return [function_new $a2 $env $binds]\n        }\n        default {\n            set lst {}\n            foreach element [obj_val $ast] {\n                lappend lst [EVAL $element $env]\n            }\n            set f [lindex $lst 0]\n            set call_args [lrange $lst 1 end]\n            switch [obj_type $f] {\n                function {\n                    set funcdict [obj_val $f]\n                    set body [dict get $funcdict body]\n                    set env [dict get $funcdict env]\n                    set binds [dict get $funcdict binds]\n                    set funcenv [Env new $env $binds $call_args]\n                    return [EVAL $body $funcenv]\n                }\n                nativefunction {\n                    set body [concat [list [obj_val $f]] {$a}]\n                    set lambda [list {a} $body]\n                    return [apply $lambda $call_args]\n                }\n                default {\n                    error \"Not a function\"\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n# core.mal: defined using the language itself\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step5_tco.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc EVAL {ast env} {\n    while {true} {\n\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n        lassign [obj_val $ast] a0 a1 a2 a3\n        if {$a0 == \"\"} {\n            return $ast\n        }\n        switch [obj_val $a0] {\n            \"def!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname $value]\n            }\n            \"let*\" {\n                set letenv [Env new $env]\n                set bindings_list [obj_val $a1]\n                foreach {varnameobj varvalobj} $bindings_list {\n                    $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n                }\n                set ast $a2\n                set env $letenv\n                # TCO: Continue loop\n            }\n            \"do\" {\n                foreach element [lrange [obj_val $ast] 1 end-1] {\n                    EVAL $element $env\n                }\n                set ast [lindex [obj_val $ast] end]\n                # TCO: Continue loop\n            }\n            \"if\" {\n                set condval [EVAL $a1 $env]\n                if {[false_q $condval] || [nil_q $condval]} {\n                    if {$a3 == \"\"} {\n                        return $::mal_nil\n                    }\n                    set ast $a3\n                } else {\n                    set ast $a2\n                }\n                # TCO: Continue loop\n            }\n            \"fn*\" {\n                set binds {}\n                foreach v [obj_val $a1] {\n                    lappend binds [obj_val $v]\n                }\n                return [function_new $a2 $env $binds]\n            }\n            default {\n                set lst {}\n                foreach element [obj_val $ast] {\n                    lappend lst [EVAL $element $env]\n                }\n                set f [lindex $lst 0]\n                set call_args [lrange $lst 1 end]\n                switch [obj_type $f] {\n                    function {\n                        set fn [obj_val $f]\n                        set ast [dict get $fn body]\n                        set env [Env new [dict get $fn env] [dict get $fn binds] $call_args]\n                        # TCO: Continue loop\n                    }\n                    nativefunction {\n                        set body [concat [list [obj_val $f]] {$a}]\n                        set lambda [list {a} $body]\n                        return [apply $lambda $call_args]\n                    }\n                    default {\n                        error \"Not a function\"\n                    }\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n# core.mal: defined using the language itself\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step6_file.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc EVAL {ast env} {\n    while {true} {\n\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n        lassign [obj_val $ast] a0 a1 a2 a3\n        if {$a0 == \"\"} {\n            return $ast\n        }\n        switch [obj_val $a0] {\n            \"def!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname $value]\n            }\n            \"let*\" {\n                set letenv [Env new $env]\n                set bindings_list [obj_val $a1]\n                foreach {varnameobj varvalobj} $bindings_list {\n                    $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n                }\n                set ast $a2\n                set env $letenv\n                # TCO: Continue loop\n            }\n            \"do\" {\n                foreach element [lrange [obj_val $ast] 1 end-1] {\n                    EVAL $element $env\n                }\n                set ast [lindex [obj_val $ast] end]\n                # TCO: Continue loop\n            }\n            \"if\" {\n                set condval [EVAL $a1 $env]\n                if {[false_q $condval] || [nil_q $condval]} {\n                    if {$a3 == \"\"} {\n                        return $::mal_nil\n                    }\n                    set ast $a3\n                } else {\n                    set ast $a2\n                }\n                # TCO: Continue loop\n            }\n            \"fn*\" {\n                set binds {}\n                foreach v [obj_val $a1] {\n                    lappend binds [obj_val $v]\n                }\n                return [function_new $a2 $env $binds]\n            }\n            default {\n                set lst {}\n                foreach element [obj_val $ast] {\n                    lappend lst [EVAL $element $env]\n                }\n                set f [lindex $lst 0]\n                set call_args [lrange $lst 1 end]\n                switch [obj_type $f] {\n                    function {\n                        set fn [obj_val $f]\n                        set ast [dict get $fn body]\n                        set env [Env new [dict get $fn env] [dict get $fn binds] $call_args]\n                        # TCO: Continue loop\n                    }\n                    nativefunction {\n                        set body [concat [list [obj_val $f]] {$a}]\n                        set lambda [list {a} $body]\n                        return [apply $lambda $call_args]\n                    }\n                    default {\n                        error \"Not a function\"\n                    }\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nproc mal_eval {a} {\n    global repl_env\n    EVAL [lindex $a 0] $repl_env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n$repl_env set \"eval\" [nativefunction_new mal_eval]\n\nset argv_list {}\nforeach arg [lrange $argv 1 end] {\n    lappend argv_list [string_new $arg]\n}\n$repl_env set \"*ARGV*\" [list_new $argv_list]\n\n# core.mal: defined using the language itself\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\nRE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\nif {$argc > 0} {\n    REP \"(load-file \\\"[lindex $argv 0]\\\")\" $repl_env\n    exit\n}\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step7_quote.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc starts_with {lst sym} {\n    if {[llength $lst] != 2} {\n        return 0\n    }\n    lassign [lindex $lst 0] a0\n    return [symbol_q $a0] && [expr {[obj_val $a0] == $sym}]\n}\nproc qq_loop {elt acc} {\n    if {[list_q $elt] && [starts_with [obj_val $elt] \"splice-unquote\"]} {\n        return [list_new [list [symbol_new \"concat\"] [lindex [obj_val $elt] 1] $acc]]\n    } else {\n        return [list_new [list [symbol_new \"cons\"] [quasiquote $elt] $acc]]\n    }\n}\nproc qq_foldr {xs} {\n    set acc [list_new []]\n    for {set i [expr {[llength $xs] - 1}]} {0 <= $i} {incr i -1} {\n        set acc [qq_loop [lindex $xs $i] $acc]\n    }\n    return $acc\n}\n\nproc quasiquote {ast} {\n    switch [obj_type $ast] {\n        \"symbol\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"hashmap\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"vector\" {\n            return [list_new [list [symbol_new \"vec\"] [qq_foldr [obj_val $ast]]]]\n        }\n        \"list\" {\n            if {[starts_with [obj_val $ast] \"unquote\"]} {\n                return [lindex [obj_val $ast] 1]\n            } else {\n                return [qq_foldr [obj_val $ast]]\n            }\n        }\n        default {\n            return $ast\n        }\n    }\n}\n\nproc EVAL {ast env} {\n    while {true} {\n\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n        lassign [obj_val $ast] a0 a1 a2 a3\n        if {$a0 == \"\"} {\n            return $ast\n        }\n        switch [obj_val $a0] {\n            \"def!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname $value]\n            }\n            \"let*\" {\n                set letenv [Env new $env]\n                set bindings_list [obj_val $a1]\n                foreach {varnameobj varvalobj} $bindings_list {\n                    $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n                }\n                set ast $a2\n                set env $letenv\n                # TCO: Continue loop\n            }\n            \"quote\" {\n                return $a1\n            }\n            \"quasiquote\" {\n                set ast [quasiquote $a1]\n            }\n            \"do\" {\n                foreach element [lrange [obj_val $ast] 1 end-1] {\n                    EVAL $element $env\n                }\n                set ast [lindex [obj_val $ast] end]\n                # TCO: Continue loop\n            }\n            \"if\" {\n                set condval [EVAL $a1 $env]\n                if {[false_q $condval] || [nil_q $condval]} {\n                    if {$a3 == \"\"} {\n                        return $::mal_nil\n                    }\n                    set ast $a3\n                } else {\n                    set ast $a2\n                }\n                # TCO: Continue loop\n            }\n            \"fn*\" {\n                set binds {}\n                foreach v [obj_val $a1] {\n                    lappend binds [obj_val $v]\n                }\n                return [function_new $a2 $env $binds]\n            }\n            default {\n                set lst {}\n                foreach element [obj_val $ast] {\n                    lappend lst [EVAL $element $env]\n                }\n                set f [lindex $lst 0]\n                set call_args [lrange $lst 1 end]\n                switch [obj_type $f] {\n                    function {\n                        set fn [obj_val $f]\n                        set ast [dict get $fn body]\n                        set env [Env new [dict get $fn env] [dict get $fn binds] $call_args]\n                        # TCO: Continue loop\n                    }\n                    nativefunction {\n                        set body [concat [list [obj_val $f]] {$a}]\n                        set lambda [list {a} $body]\n                        return [apply $lambda $call_args]\n                    }\n                    default {\n                        error \"Not a function\"\n                    }\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nproc mal_eval {a} {\n    global repl_env\n    EVAL [lindex $a 0] $repl_env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n$repl_env set \"eval\" [nativefunction_new mal_eval]\n\nset argv_list {}\nforeach arg [lrange $argv 1 end] {\n    lappend argv_list [string_new $arg]\n}\n$repl_env set \"*ARGV*\" [list_new $argv_list]\n\n# core.mal: defined using the language itself\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\nRE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\nif {$argc > 0} {\n    REP \"(load-file \\\"[lindex $argv 0]\\\")\" $repl_env\n    exit\n}\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step8_macros.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc starts_with {lst sym} {\n    if {[llength $lst] != 2} {\n        return 0\n    }\n    lassign [lindex $lst 0] a0\n    return [symbol_q $a0] && [expr {[obj_val $a0] == $sym}]\n}\nproc qq_loop {elt acc} {\n    if {[list_q $elt] && [starts_with [obj_val $elt] \"splice-unquote\"]} {\n        return [list_new [list [symbol_new \"concat\"] [lindex [obj_val $elt] 1] $acc]]\n    } else {\n        return [list_new [list [symbol_new \"cons\"] [quasiquote $elt] $acc]]\n    }\n}\nproc qq_foldr {xs} {\n    set acc [list_new []]\n    for {set i [expr {[llength $xs] - 1}]} {0 <= $i} {incr i -1} {\n        set acc [qq_loop [lindex $xs $i] $acc]\n    }\n    return $acc\n}\n\nproc quasiquote {ast} {\n    switch [obj_type $ast] {\n        \"symbol\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"hashmap\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"vector\" {\n            return [list_new [list [symbol_new \"vec\"] [qq_foldr [obj_val $ast]]]]\n        }\n        \"list\" {\n            if {[starts_with [obj_val $ast] \"unquote\"]} {\n                return [lindex [obj_val $ast] 1]\n            } else {\n                return [qq_foldr [obj_val $ast]]\n            }\n        }\n        default {\n            return $ast\n        }\n    }\n}\n\nproc EVAL {ast env} {\n    while {true} {\n\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n        lassign [obj_val $ast] a0 a1 a2 a3\n        if {$a0 == \"\"} {\n            return $ast\n        }\n        switch [obj_val $a0] {\n            \"def!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname $value]\n            }\n            \"let*\" {\n                set letenv [Env new $env]\n                set bindings_list [obj_val $a1]\n                foreach {varnameobj varvalobj} $bindings_list {\n                    $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n                }\n                set ast $a2\n                set env $letenv\n                # TCO: Continue loop\n            }\n            \"quote\" {\n                return $a1\n            }\n            \"quasiquote\" {\n                set ast [quasiquote $a1]\n            }\n            \"defmacro!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname [macro_new $value]]\n            }\n            \"do\" {\n                foreach element [lrange [obj_val $ast] 1 end-1] {\n                    EVAL $element $env\n                }\n                set ast [lindex [obj_val $ast] end]\n                # TCO: Continue loop\n            }\n            \"if\" {\n                set condval [EVAL $a1 $env]\n                if {[false_q $condval] || [nil_q $condval]} {\n                    if {$a3 == \"\"} {\n                        return $::mal_nil\n                    }\n                    set ast $a3\n                } else {\n                    set ast $a2\n                }\n                # TCO: Continue loop\n            }\n            \"fn*\" {\n                set binds {}\n                foreach v [obj_val $a1] {\n                    lappend binds [obj_val $v]\n                }\n                return [function_new $a2 $env $binds]\n            }\n            default {\n                set f [EVAL $a0 $env]\n                set unevaluated_args [lrange [obj_val $ast] 1 end]\n                if {[macro_q $f]} {\n                    set fn [obj_val $f]\n                    set f_ast   [dict get $fn body]\n                    set f_env   [dict get $fn env]\n                    set f_binds [dict get $fn binds]\n                    set apply_env [Env new $f_env $f_binds $unevaluated_args]\n                    set ast [EVAL $f_ast $apply_env]\n                    continue\n                }\n                set call_args {}\n                foreach element $unevaluated_args {\n                    lappend call_args [EVAL $element $env]\n                }\n                switch [obj_type $f] {\n                    function {\n                        set fn [obj_val $f]\n                        set ast [dict get $fn body]\n                        set env [Env new [dict get $fn env] [dict get $fn binds] $call_args]\n                        # TCO: Continue loop\n                    }\n                    nativefunction {\n                        set body [concat [list [obj_val $f]] {$a}]\n                        set lambda [list {a} $body]\n                        return [apply $lambda $call_args]\n                    }\n                    default {\n                        error \"Not a function\"\n                    }\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nproc mal_eval {a} {\n    global repl_env\n    EVAL [lindex $a 0] $repl_env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n$repl_env set \"eval\" [nativefunction_new mal_eval]\n\nset argv_list {}\nforeach arg [lrange $argv 1 end] {\n    lappend argv_list [string_new $arg]\n}\n$repl_env set \"*ARGV*\" [list_new $argv_list]\n\n# core.mal: defined using the language itself\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\nRE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\" $repl_env\nRE \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\nif {$argc > 0} {\n    REP \"(load-file \\\"[lindex $argv 0]\\\")\" $repl_env\n    exit\n}\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        puts \"Error: $exception\"\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/step9_try.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc starts_with {lst sym} {\n    if {[llength $lst] != 2} {\n        return 0\n    }\n    lassign [lindex $lst 0] a0\n    return [symbol_q $a0] && [expr {[obj_val $a0] == $sym}]\n}\nproc qq_loop {elt acc} {\n    if {[list_q $elt] && [starts_with [obj_val $elt] \"splice-unquote\"]} {\n        return [list_new [list [symbol_new \"concat\"] [lindex [obj_val $elt] 1] $acc]]\n    } else {\n        return [list_new [list [symbol_new \"cons\"] [quasiquote $elt] $acc]]\n    }\n}\nproc qq_foldr {xs} {\n    set acc [list_new []]\n    for {set i [expr {[llength $xs] - 1}]} {0 <= $i} {incr i -1} {\n        set acc [qq_loop [lindex $xs $i] $acc]\n    }\n    return $acc\n}\n\nproc quasiquote {ast} {\n    switch [obj_type $ast] {\n        \"symbol\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"hashmap\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"vector\" {\n            return [list_new [list [symbol_new \"vec\"] [qq_foldr [obj_val $ast]]]]\n        }\n        \"list\" {\n            if {[starts_with [obj_val $ast] \"unquote\"]} {\n                return [lindex [obj_val $ast] 1]\n            } else {\n                return [qq_foldr [obj_val $ast]]\n            }\n        }\n        default {\n            return $ast\n        }\n    }\n}\n\nproc EVAL {ast env} {\n    while {true} {\n\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n        lassign [obj_val $ast] a0 a1 a2 a3\n        if {$a0 == \"\"} {\n            return $ast\n        }\n        switch [obj_val $a0] {\n            \"def!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname $value]\n            }\n            \"let*\" {\n                set letenv [Env new $env]\n                set bindings_list [obj_val $a1]\n                foreach {varnameobj varvalobj} $bindings_list {\n                    $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n                }\n                set ast $a2\n                set env $letenv\n                # TCO: Continue loop\n            }\n            \"quote\" {\n                return $a1\n            }\n            \"quasiquote\" {\n                set ast [quasiquote $a1]\n            }\n            \"defmacro!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname [macro_new $value]]\n            }\n            \"try*\" {\n                if {$a2 == \"\"} {\n                    return [EVAL $a1 $env]\n                }\n                set res {}\n                if { [catch { set res [EVAL $a1 $env] } exception] } {\n                    set exc_var [obj_val [lindex [obj_val $a2] 1]]\n                    if {$exception == \"__MalException__\"} {\n                        set exc_value $::mal_exception_obj\n                    } else {\n                        set exc_value [string_new $exception]\n                    }\n                    set catch_env [Env new $env [list $exc_var] [list $exc_value]]\n                    return [EVAL [lindex [obj_val $a2] 2] $catch_env]\n                } else {\n                    return $res\n                }\n            }\n            \"do\" {\n                foreach element [lrange [obj_val $ast] 1 end-1] {\n                    EVAL $element $env\n                }\n                set ast [lindex [obj_val $ast] end]\n                # TCO: Continue loop\n            }\n            \"if\" {\n                set condval [EVAL $a1 $env]\n                if {[false_q $condval] || [nil_q $condval]} {\n                    if {$a3 == \"\"} {\n                        return $::mal_nil\n                    }\n                    set ast $a3\n                } else {\n                    set ast $a2\n                }\n                # TCO: Continue loop\n            }\n            \"fn*\" {\n                set binds {}\n                foreach v [obj_val $a1] {\n                    lappend binds [obj_val $v]\n                }\n                return [function_new $a2 $env $binds]\n            }\n            default {\n                set f [EVAL $a0 $env]\n                set unevaluated_args [lrange [obj_val $ast] 1 end]\n                if {[macro_q $f]} {\n                    set fn [obj_val $f]\n                    set f_ast   [dict get $fn body]\n                    set f_env   [dict get $fn env]\n                    set f_binds [dict get $fn binds]\n                    set apply_env [Env new $f_env $f_binds $unevaluated_args]\n                    set ast [EVAL $f_ast $apply_env]\n                    continue\n                }\n                set call_args {}\n                foreach element $unevaluated_args {\n                    lappend call_args [EVAL $element $env]\n                }\n                switch [obj_type $f] {\n                    function {\n                        set fn [obj_val $f]\n                        set ast [dict get $fn body]\n                        set env [Env new [dict get $fn env] [dict get $fn binds] $call_args]\n                        # TCO: Continue loop\n                    }\n                    nativefunction {\n                        set body [concat [list [obj_val $f]] {$a}]\n                        set lambda [list {a} $body]\n                        return [apply $lambda $call_args]\n                    }\n                    default {\n                        error \"Not a function\"\n                    }\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nproc mal_eval {a} {\n    global repl_env\n    EVAL [lindex $a 0] $repl_env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n$repl_env set \"eval\" [nativefunction_new mal_eval]\n\nset argv_list {}\nforeach arg [lrange $argv 1 end] {\n    lappend argv_list [string_new $arg]\n}\n$repl_env set \"*ARGV*\" [list_new $argv_list]\n\n# core.mal: defined using the language itself\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\nRE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\" $repl_env\nRE \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\nif {$argc > 0} {\n    REP \"(load-file \\\"[lindex $argv 0]\\\")\" $repl_env\n    exit\n}\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        if {$exception == \"__MalException__\"} {\n            set res [pr_str $::mal_exception_obj 1]\n            puts \"Error: $res\"\n        } else {\n            puts \"Error: $exception\"\n        }\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/stepA_mal.tcl",
    "content": "source mal_readline.tcl\nsource types.tcl\nsource reader.tcl\nsource printer.tcl\nsource env.tcl\nsource core.tcl\n\nproc READ str {\n    read_str $str\n}\n\nproc starts_with {lst sym} {\n    if {[llength $lst] != 2} {\n        return 0\n    }\n    lassign [lindex $lst 0] a0\n    return [symbol_q $a0] && [expr {[obj_val $a0] == $sym}]\n}\nproc qq_loop {elt acc} {\n    if {[list_q $elt] && [starts_with [obj_val $elt] \"splice-unquote\"]} {\n        return [list_new [list [symbol_new \"concat\"] [lindex [obj_val $elt] 1] $acc]]\n    } else {\n        return [list_new [list [symbol_new \"cons\"] [quasiquote $elt] $acc]]\n    }\n}\nproc qq_foldr {xs} {\n    set acc [list_new []]\n    for {set i [expr {[llength $xs] - 1}]} {0 <= $i} {incr i -1} {\n        set acc [qq_loop [lindex $xs $i] $acc]\n    }\n    return $acc\n}\n\nproc quasiquote {ast} {\n    switch [obj_type $ast] {\n        \"symbol\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"hashmap\" {\n            return [list_new [list [symbol_new \"quote\"] $ast]]\n        }\n        \"vector\" {\n            return [list_new [list [symbol_new \"vec\"] [qq_foldr [obj_val $ast]]]]\n        }\n        \"list\" {\n            if {[starts_with [obj_val $ast] \"unquote\"]} {\n                return [lindex [obj_val $ast] 1]\n            } else {\n                return [qq_foldr [obj_val $ast]]\n            }\n        }\n        default {\n            return $ast\n        }\n    }\n}\n\nproc EVAL {ast env} {\n    while {true} {\n\n    set dbgenv [$env find \"DEBUG-EVAL\"]\n    if {$dbgenv != 0} {\n        set dbgeval [$env get \"DEBUG-EVAL\"]\n        if {![false_q $dbgeval] && ![nil_q $dbgeval]} {\n            set img [PRINT $ast]\n            puts \"EVAL: ${img}\"\n        }\n    }\n\n    switch [obj_type $ast] {\n        \"symbol\" {\n            set varname [obj_val $ast]\n            return [$env get $varname]\n        }\n        \"list\" {\n        }\n        \"vector\" {\n            set res {}\n            foreach element [obj_val $ast] {\n                lappend res [EVAL $element $env]\n            }\n            return [vector_new $res]\n        }\n        \"hashmap\" {\n            set res [dict create]\n            dict for {k v} [obj_val $ast] {\n                dict set res $k [EVAL $v $env]\n            }\n            return [hashmap_new $res]\n        }\n        default { return $ast }\n        }\n\n        lassign [obj_val $ast] a0 a1 a2 a3\n        if {$a0 == \"\"} {\n            return $ast\n        }\n        switch [obj_val $a0] {\n            \"def!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname $value]\n            }\n            \"let*\" {\n                set letenv [Env new $env]\n                set bindings_list [obj_val $a1]\n                foreach {varnameobj varvalobj} $bindings_list {\n                    $letenv set [obj_val $varnameobj] [EVAL $varvalobj $letenv]\n                }\n                set ast $a2\n                set env $letenv\n                # TCO: Continue loop\n            }\n            \"quote\" {\n                return $a1\n            }\n            \"quasiquote\" {\n                set ast [quasiquote $a1]\n            }\n            \"defmacro!\" {\n                set varname [obj_val $a1]\n                set value [EVAL $a2 $env]\n                return [$env set $varname [macro_new $value]]\n            }\n            \"tcl*\" {\n                return [string_new [eval [obj_val $a1]]]\n            }\n            \"try*\" {\n                if {$a2 == \"\"} {\n                    return [EVAL $a1 $env]\n                }\n                set res {}\n                if { [catch { set res [EVAL $a1 $env] } exception] } {\n                    set exc_var [obj_val [lindex [obj_val $a2] 1]]\n                    if {$exception == \"__MalException__\"} {\n                        set exc_value $::mal_exception_obj\n                    } else {\n                        set exc_value [string_new $exception]\n                    }\n                    set catch_env [Env new $env [list $exc_var] [list $exc_value]]\n                    return [EVAL [lindex [obj_val $a2] 2] $catch_env]\n                } else {\n                    return $res\n                }\n            }\n            \"do\" {\n                foreach element [lrange [obj_val $ast] 1 end-1] {\n                    EVAL $element $env\n                }\n                set ast [lindex [obj_val $ast] end]\n                # TCO: Continue loop\n            }\n            \"if\" {\n                set condval [EVAL $a1 $env]\n                if {[false_q $condval] || [nil_q $condval]} {\n                    if {$a3 == \"\"} {\n                        return $::mal_nil\n                    }\n                    set ast $a3\n                } else {\n                    set ast $a2\n                }\n                # TCO: Continue loop\n            }\n            \"fn*\" {\n                set binds {}\n                foreach v [obj_val $a1] {\n                    lappend binds [obj_val $v]\n                }\n                return [function_new $a2 $env $binds]\n            }\n            default {\n                set f [EVAL $a0 $env]\n                set unevaluated_args [lrange [obj_val $ast] 1 end]\n                if {[macro_q $f]} {\n                    set fn [obj_val $f]\n                    set f_ast   [dict get $fn body]\n                    set f_env   [dict get $fn env]\n                    set f_binds [dict get $fn binds]\n                    set apply_env [Env new $f_env $f_binds $unevaluated_args]\n                    set ast [EVAL $f_ast $apply_env]\n                    continue\n                }\n                set call_args {}\n                foreach element $unevaluated_args {\n                    lappend call_args [EVAL $element $env]\n                }\n                switch [obj_type $f] {\n                    function {\n                        set fn [obj_val $f]\n                        set ast [dict get $fn body]\n                        set env [Env new [dict get $fn env] [dict get $fn binds] $call_args]\n                        # TCO: Continue loop\n                    }\n                    nativefunction {\n                        set body [concat [list [obj_val $f]] {$a}]\n                        set lambda [list {a} $body]\n                        return [apply $lambda $call_args]\n                    }\n                    default {\n                        error \"Not a function\"\n                    }\n                }\n            }\n        }\n    }\n}\n\nproc PRINT exp {\n    pr_str $exp 1\n}\n\nproc REP {str env} {\n    PRINT [EVAL [READ $str] $env]\n}\n\nproc RE {str env} {\n    EVAL [READ $str] $env\n}\n\nproc mal_eval {a} {\n    global repl_env\n    EVAL [lindex $a 0] $repl_env\n}\n\nset repl_env [Env new]\ndict for {k v} $core_ns {\n    $repl_env set $k $v\n}\n\n$repl_env set \"eval\" [nativefunction_new mal_eval]\n\nset argv_list {}\nforeach arg [lrange $argv 1 end] {\n    lappend argv_list [string_new $arg]\n}\n$repl_env set \"*ARGV*\" [list_new $argv_list]\n\n# core.mal: defined using the language itself\nRE \"(def! *host-language* \\\"tcl\\\")\" $repl_env\nRE \"(def! not (fn* (a) (if a false true)))\" $repl_env\nRE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\" $repl_env\nRE \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" $repl_env\n\nfconfigure stdout -translation binary\n\nset DEBUG_MODE 0\nif { [array names env DEBUG] != \"\" && $env(DEBUG) != \"0\" } {\n    set DEBUG_MODE 1\n}\n\nif {$argc > 0} {\n    REP \"(load-file \\\"[lindex $argv 0]\\\")\" $repl_env\n    exit\n}\n\nREP \"(println (str \\\"Mal \\[\\\" *host-language* \\\"\\]\\\"))\" $repl_env\n\n# repl loop\nwhile {true} {\n    set res [_readline \"user> \"]\n    if {[lindex $res 0] == \"EOF\"} {\n        break\n    }\n    set line [lindex $res 1]\n    if {$line == \"\"} {\n        continue\n    }\n    if { [catch { puts [REP $line $repl_env] } exception] } {\n        if {$exception == \"__MalException__\"} {\n            set res [pr_str $::mal_exception_obj 1]\n            puts \"Error: $res\"\n        } else {\n            puts \"Error: $exception\"\n        }\n        if { $DEBUG_MODE } {\n            puts $::errorInfo\n        }\n    }\n}\nputs \"\"\n"
  },
  {
    "path": "impls/tcl/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/tcl/tests/stepA_mal.mal",
    "content": ";; Testing basic Tcl interop\n;;\n;; Note that in Tcl \"everything is a string\", so we don't have enough\n;; information to convert the results to other Mal types.\n\n(tcl* \"expr {3 ** 4}\")\n;=>\"81\"\n\n(tcl* \"llength {a b c d}\")\n;=>\"4\"\n\n(tcl* \"concat {a b} c {d e} f g\")\n;=>\"a b c d e f g\"\n\n(tcl* \"puts \\\"hello [expr {5 + 6}] world\\\"\")\n;/hello 11 world\n;=>\"\"\n\n(tcl* \"set ::foo 8\")\n(tcl* \"expr {$::foo}\")\n;=>\"8\"\n\n(tcl* \"proc mult3 {x} { expr {$x * 3} }\")\n(tcl* \"mult3 6\")\n;=>\"18\"\n\n(tcl* \"string range $::tcl_version 0 1\")\n;=>\"8.\"\n"
  },
  {
    "path": "impls/tcl/types.tcl",
    "content": "oo::class create MalObj {\n    variable type val meta\n\n    constructor {obj_type obj_val {obj_meta 0}} {\n        set type $obj_type\n        set val $obj_val\n        set meta $obj_meta\n    }\n\n    method get_type {} {\n        return $type\n    }\n\n    method get_val {} {\n        return $val\n    }\n\n    method get_meta {} {\n        return $meta\n    }\n\n    method set_val {new_val} {\n        set val $new_val\n        return $new_val\n    }\n}\n\nproc obj_new {obj_type obj_val {obj_meta 0}} {\n    MalObj new $obj_type $obj_val $obj_meta\n}\n\nproc obj_type {obj} {\n    $obj get_type\n}\n\nproc obj_val {obj} {\n    $obj get_val\n}\n\nproc obj_meta {obj} {\n    $obj get_meta\n}\n\nproc obj_set_val {obj new_val} {\n    $obj set_val $new_val\n}\n\nset ::mal_nil [obj_new \"nil\" {}]\nset ::mal_true [obj_new \"true\" {}]\nset ::mal_false [obj_new \"false\" {}]\n\nproc nil_q {obj} {\n    expr {[obj_type $obj] == \"nil\"}\n}\n\nproc false_q {obj} {\n    expr {[obj_type $obj] == \"false\"}\n}\n\nproc true_q {obj} {\n    expr {[obj_type $obj] == \"true\"}\n}\n\nproc bool_new {val} {\n    if {$val == 0} {\n        return $::mal_false\n    } else {\n        return $::mal_true\n    }\n}\n\nproc integer_new {num} {\n    obj_new \"integer\" $num\n}\n\nproc integer_q {obj} {\n    expr {[obj_type $obj] == \"integer\"}\n}\n\nproc symbol_new {name} {\n    obj_new \"symbol\" $name\n}\n\nproc symbol_q {obj} {\n    expr {[obj_type $obj] == \"symbol\"}\n}\n\nproc string_new {val} {\n    obj_new \"string\" $val\n}\n\nproc string_q {obj} {\n    expr {[obj_type $obj] == \"string\" && [string index [obj_val $obj] 0] != \"\\u029E\"}\n}\n\nproc keyword_new {val} {\n    string_new \"\\u029E$val\"\n}\n\nproc keyword_q {obj} {\n    expr {[obj_type $obj] == \"string\" && [string index [obj_val $obj] 0] == \"\\u029E\"}\n}\n\nproc list_new {lst} {\n    obj_new \"list\" $lst $::mal_nil\n}\n\nproc list_q {obj} {\n    expr {[obj_type $obj] == \"list\"}\n}\n\nproc vector_new {lst} {\n    obj_new \"vector\" $lst $::mal_nil\n}\n\nproc vector_q {obj} {\n    expr {[obj_type $obj] == \"vector\"}\n}\n\nproc hashmap_new {lst} {\n    obj_new \"hashmap\" $lst $::mal_nil\n}\n\nproc hashmap_q {obj} {\n    expr {[obj_type $obj] == \"hashmap\"}\n}\n\nproc sequential_q {obj} {\n    expr {[list_q $obj] || [vector_q $obj]}\n}\n\nproc sequential_equal_q {seq_a seq_b} {\n    foreach obj_a [obj_val $seq_a] obj_b [obj_val $seq_b] {\n        if {$obj_a == \"\" || $obj_b == \"\" || ![equal_q $obj_a $obj_b]} {\n            return 0\n        }\n    }\n    return 1\n}\n\nproc hashmap_equal_q {hashmap_a hashmap_b} {\n    set dict_a [obj_val $hashmap_a]\n    set dict_b [obj_val $hashmap_b]\n    set keys_a [lsort [dict keys $dict_a]]\n    set keys_b [lsort [dict keys $dict_b]]\n    if {$keys_a != $keys_b} {\n        return 0\n    }\n    foreach key $keys_a {\n        set obj_a [dict get $dict_a $key]\n        set obj_b [dict get $dict_b $key]\n        if {![equal_q $obj_a $obj_b]} {\n            return 0\n        }\n    }\n    return 1\n}\n\nproc equal_q {a b} {\n    if {[sequential_q $a] && [sequential_q $b]} {\n        sequential_equal_q $a $b\n    } elseif {[hashmap_q $a] && [hashmap_q $b]} {\n        hashmap_equal_q $a $b\n    } else {\n        expr {[obj_type $a] == [obj_type $b] && [obj_val $a] == [obj_val $b]}\n    }\n}\n\nproc nativefunction_new {name} {\n    obj_new \"nativefunction\" $name $::mal_nil\n}\n\nproc function_new {body env binds} {\n    set funcdict [dict create body $body env $env binds $binds is_macro 0]\n    obj_new \"function\" $funcdict $::mal_nil\n}\n\nproc macro_new {funcobj} {\n    set fn [obj_val $funcobj]\n    set body [dict get $fn body]\n    set env [dict get $fn env]\n    set binds [dict get $fn binds]\n    set funcdict [dict create body $body env $env binds $binds is_macro 1]\n    obj_new \"function\" $funcdict $::mal_nil\n}\n\nproc function_q {obj} {\n    expr {[obj_type $obj] == \"function\"}\n}\n\nproc macro_q {obj} {\n    expr {[obj_type $obj] == \"function\" && [dict get [obj_val $obj] is_macro]}\n}\n\nproc atom_new {val} {\n    obj_new \"atom\" $val $::mal_nil\n}\n\nproc atom_q {obj} {\n    expr {[obj_type $obj] == \"atom\"}\n}\n"
  },
  {
    "path": "impls/tests/busywork.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/threading.mal\")    ; ->\n(load-file-once \"../lib/benchmark.mal\")\n(load-file-once \"../lib/test_cascade.mal\") ; or\n\n;; Indicate that these macros are safe to eagerly expand.\n;; Provides a large performance benefit for supporting implementations.\n(def! and ^{:inline? true} and)\n(def! or ^{:inline? true} or)\n(def! -> ^{:inline? true} ->)\n(def! -> ^{:inline? true} ->>)\n\n(def! do-times (fn* [f n]\n  (if (> n 0)\n    (do (f)\n        (do-times f (- n 1))))))\n\n(def! atm (atom (list 0 1 2 3 4 5 6 7 8 9)))\n\n(def! busywork (fn* []\n  (do\n   (or false nil false nil false nil false nil false nil (first @atm))\n   (cond false 1 nil 2 false 3 nil 4 false 5 nil 6 \"else\" (first @atm))\n   (-> (deref atm) rest rest rest rest rest rest first)\n   (swap! atm (fn* [a] (concat (rest a) (list (first a))))))))\n\n(def! num-iterations 10000)\n\n(println (str \"Execution time (in ms) of \" num-iterations \" busywork iterations on \"\n              *host-language* \": \")\n         (benchmark (do-times busywork num-iterations) 10))\n"
  },
  {
    "path": "impls/tests/computations.mal",
    "content": ";; Some inefficient arithmetic computations for benchmarking.\n\n;; Unfortunately not yet available in tests of steps 4 and 5.\n\n;; Compute n(n+1)/2 with a non tail-recursive call.\n(def! sumdown\n  (fn* [n]                              ; non-negative number\n    (if (= n 0)\n      0\n      (+ n (sumdown  (- n 1))))))\n\n;; Compute a Fibonacci number with two recursions.\n(def! fib\n  (fn* [n]                              ; non-negative number\n    (if (<= n 1)\n      n\n      (+ (fib (- n 1)) (fib (- n 2))))))\n"
  },
  {
    "path": "impls/tests/docker/Dockerfile",
    "content": "# WARNING: This file is deprecated. Each implementation now has its\n# own Dockerfile.\n\nFROM ubuntu:utopic\nMAINTAINER Joel Martin <github@martintribe.org>\n\nENV DEBIAN_FRONTEND noninteractive\n\nRUN echo \"deb http://dl.bintray.com/sbt/debian /\" > /etc/apt/sources.list.d/sbt.list\nRUN apt-get -y update\n\n#\n# General dependencies\n#\nVOLUME /mal\n\nRUN apt-get -y install make wget curl git\n\n# Deps for compiled languages (C, Go, Rust, Nim, etc)\nRUN apt-get -y install gcc pkg-config\n\n# Deps for Java-based languages (Clojure, Scala, Java)\nRUN apt-get -y install openjdk-7-jdk\nENV MAVEN_OPTS -Duser.home=/mal\n\n# Deps for Mono-based languages (C#, VB.Net)\nRUN apt-get -y install mono-runtime mono-mcs mono-vbnc\n\n# Deps for node.js languages (JavaScript, CoffeeScript, miniMAL, etc)\nRUN apt-get -y install nodejs npm\nRUN ln -sf nodejs /usr/bin/node\n\n\n#\n# Implementation specific installs\n#\n\n# GNU awk\nRUN apt-get -y install gawk\n\n# Bash\nRUN apt-get -y install bash\n\n# C\nRUN apt-get -y install libglib2.0 libglib2.0-dev\nRUN apt-get -y install libffi-dev libreadline-dev libedit2 libedit-dev\n\n# C++\nRUN apt-get -y install g++-4.9 libreadline-dev\n\n# Clojure\nADD https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein \\\n    /usr/local/bin/lein\nRUN sudo chmod 0755 /usr/local/bin/lein\nENV LEIN_HOME /mal/.lein\nENV LEIN_JVM_OPTS -Duser.home=/mal\n\n# CoffeeScript\nRUN npm install -g coffee-script\nRUN touch /.coffee_history && chmod go+w /.coffee_history\n\n# C#\nRUN apt-get -y install mono-mcs\n\n# Elixir\nRUN wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb \\\n    && dpkg -i erlang-solutions_1.0_all.deb\nRUN apt-get update\nRUN apt-get -y install elixir\n\n# Erlang R17 (so I can use maps)\nRUN apt-get -y install build-essential libncurses5-dev libssl-dev\nRUN cd /tmp && wget http://www.erlang.org/download/otp_src_17.5.tar.gz \\\n    && tar -C /tmp -zxf /tmp/otp_src_17.5.tar.gz \\\n    && cd /tmp/otp_src_17.5 && ./configure && make && make install \\\n    && rm -rf /tmp/otp_src_17.5 /tmp/otp_src_17.5.tar.gz\n# Rebar for building the Erlang implementation\nRUN cd /tmp/ && git clone -q https://github.com/rebar/rebar.git \\\n    && cd /tmp/rebar && ./bootstrap && cp rebar /usr/local/bin \\\n    && rm -rf /tmp/rebar\n\n# Forth\nRUN apt-get -y install gforth\n\n# Go\nRUN apt-get -y install golang\n\n# Guile\nRUN apt-get -y install libunistring-dev libgc-dev autoconf libtool flex gettext texinfo libgmp-dev\nRUN git clone git://git.sv.gnu.org/guile.git /tmp/guile \\\n    && cd /tmp/guile && ./autogen.sh && ./configure && make && make install\n\n# Haskell\nRUN apt-get -y install ghc haskell-platform libghc-readline-dev libghc-editline-dev\n\n# Java\nRUN apt-get -y install maven2\n\n# JavaScript\n# Already satisfied above\n\n# Julia\nRUN apt-get -y install software-properties-common\nRUN apt-add-repository -y ppa:staticfloat/juliareleases\nRUN apt-get -y update\nRUN apt-get -y install julia\n\n# Lua\nRUN apt-get -y install lua5.1 lua-rex-pcre luarocks\nRUN luarocks install linenoise\n\n# Mal\n# N/A: self-hosted on other language implementations\n\n# GNU Make\n# Already satisfied as a based dependency for testing\n\n# miniMAL\nRUN npm install -g minimal-lisp\n\n# Nim\nRUN cd /tmp && wget http://nim-lang.org/download/nim-0.17.0.tar.xz \\\n    && tar xvJf /tmp/nim-0.17.0.tar.xz && cd nim-0.17.0 \\\n    && make && sh install.sh /usr/local/bin \\\n    && rm -r /tmp/nim-0.17.0\n\n# OCaml\nRUN apt-get -y install ocaml-batteries-included\n\n# perl\nRUN apt-get -y install perl\n\n# PHP\nRUN apt-get -y install php5-cli\n\n# PostScript/ghostscript\nRUN apt-get -y install ghostscript\n\n# python\nRUN apt-get -y install python\n\n# R\nRUN apt-get -y install r-base-core\n\n# Racket\nRUN apt-get -y install racket\n\n# Ruby\nRUN apt-get -y install ruby\n\n# Rust\nRUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh\n\n# Scala\nRUN apt-get -y --force-yes install sbt\nRUN apt-get -y install scala\nENV SBT_OPTS -Duser.home=/mal\n\n# VB.Net\nRUN apt-get -y install mono-vbnc\n\n# TODO: move up\n# Factor\nRUN apt-get -y install libgtkglext1\nRUN cd /usr/lib/x86_64-linux-gnu/ \\\n    && wget http://downloads.factorcode.org/releases/0.97/factor-linux-x86-64-0.97.tar.gz \\\n    && tar xvzf factor-linux-x86-64-0.97.tar.gz \\\n    && ln -sf /usr/lib/x86_64-linux-gnu/factor/factor /usr/bin/factor \\\n    && rm factor-linux-x86-64-0.97.tar.gz\n\n# MATLAB is proprietary/licensed. Maybe someday with Octave.\n# Swift is Xcode/macOS only\nENV SKIP_IMPLS matlab swift\n\nENV DEBIAN_FRONTEND newt\nENV HOME /\n\nWORKDIR /mal\n"
  },
  {
    "path": "impls/tests/docker-build.sh",
    "content": "#!/usr/bin/env bash\n\nIMAGE_NAME=${IMAGE_NAME:-mal-test-ubuntu-utopic}\nGIT_TOP=$(git rev-parse --show-toplevel)\n\ndocker build -t \"${IMAGE_NAME}\" \"${GIT_TOP}/tests/docker\"\n"
  },
  {
    "path": "impls/tests/docker-run.sh",
    "content": "#!/usr/bin/env bash\n\nIMAGE_NAME=${IMAGE_NAME:-mal-test-ubuntu-utopic}\nGIT_TOP=$(git rev-parse --show-toplevel)\n \ndocker run -it --rm -u ${EUID} \\\n    --volume=${GIT_TOP}:/mal \\\n    ${IMAGE_NAME} \\\n    \"${@}\"\n"
  },
  {
    "path": "impls/tests/fib.mal",
    "content": "(load-file \"../lib/benchmark.mal\")\r\n\r\n(def! fib (fn* [n]\r\n  (if (= n 0)\r\n    1\r\n    (if (= n 1)\r\n      1\r\n      (+ (fib (- n 1))\r\n         (fib (- n 2)))))))\r\n\r\n(let* [n (read-string (first *ARGV*))\r\n       iters (read-string (first (rest *ARGV*)))]\r\n  (println (str \"Times (in ms) for (fib \" n \") on \" *host-language* \": \")\r\n           (benchmark (fib n) iters)))\r\n"
  },
  {
    "path": "impls/tests/inc.mal",
    "content": "(def! inc1 (fn* (a) (+ 1 a)))\n(def! inc2 (fn* (a) (+ 2 a)))\n(def! inc3 (fn* (a)\n  (+ 3 a)))\n"
  },
  {
    "path": "impls/tests/incA.mal",
    "content": "(def! inc4 (fn* (a) (+ 4 a)))\n\n(prn (inc4 5))\n"
  },
  {
    "path": "impls/tests/incB.mal",
    "content": ";; A comment in a file\n(def! inc4 (fn* (a) (+ 4 a)))\n(def! inc5 (fn* (a)  ;; a comment after code\n  (+ 5 a)))\n\n;; ending comment without final new line"
  },
  {
    "path": "impls/tests/incC.mal",
    "content": "(def! mymap {\"a\"\n             1})\n"
  },
  {
    "path": "impls/tests/lib/alias-hacks.mal",
    "content": ";; Testing alias-hacks.mal\n(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/alias-hacks.mal\")\n;=>nil\n\n;; Testing let\n(macroexpand (let binds a b))\n;=>(let* binds (do a b))\n(let [x 2] 3 x)\n;=>2\n\n;; Testing when\n(macroexpand (when condition a b))\n;=>(if condition (do a b))\n(when false (nth () 0) a)\n;=>nil\n(when true 3 2)\n;=>2\n\n;; Testing name\n(macroexpand (def name a b))\n;=>(def! name (do a b))\n(def x 1 2 3)\n;=>3\nx\n;=>3\n\n;; Testing fn\n(macroexpand (fn args a b))\n;=>(fn* args (do a b))\n((fn [x] 1 2) 3)\n;=>2\n\n;; Testing defn\n(macroexpand (defn name args b))\n;=>(def! name (fn args b))\n(defn f [x] 1 2 x)\n(f 3)\n;=>3\n\n;; Testing partial\n((partial +) 1 2)\n;=>3\n((partial + 1) 2)\n;=>3\n((partial + 1 2))\n;=>3\n((partial not) false)\n;=>true\n((partial not false))\n;=>true\n((partial (fn* [x y] (+ x y)) 1) 2)\n;=>3\n((partial str 1 2) 3 4)\n;=>\"1234\"\n"
  },
  {
    "path": "impls/tests/lib/equality.mal",
    "content": "(def! orig= =)\n\n;; Testing equality.mal does not fix built-in equality.\n(load-file \"../lib/equality.mal\")\n;=>nil\n\n;; Testing bool-and\n(bool-and)\n;=>true\n(bool-and true)\n;=>true\n(bool-and false)\n;=>false\n(bool-and nil)\n;=>false\n(bool-and 1)\n;=>true\n(bool-and 1 2)\n;=>true\n(bool-and nil (nth () 1))\n;=>false\n\n;; Testing bool-or\n(bool-or)\n;=>false\n(bool-or true)\n;=>true\n(bool-or false)\n;=>false\n(bool-or nil)\n;=>false\n(bool-or 1)\n;=>true\n(bool-or 1 (nth () 1))\n;=>true\n(bool-or 1 2)\n;=>true\n(bool-or false nil)\n;=>false\n\n;; Breaking equality.\n(def! = (fn* [a b] (bool-and (orig= a b) (cond (list? a) (list? b) (vector? a) (vector? b) true true))))\n(= [] ())\n;=>false\n\n;; Testing that equality.mal detects the problem.\n(load-file \"../lib/equality.mal\")\n;/equality.mal: Replaced = with pure mal implementation\n;=>nil\n\n;; Testing fixed equality.\n(= [] ())\n;=>true\n(= [:a :b] (list :a :b))\n;=>true\n(= [:a :b] [:a :b :c])\n;=>false\n(= {:a 1} {:a 1})\n;=>true\n(= {:a 1} {:a 1 :b 2})\n;=>false\n"
  },
  {
    "path": "impls/tests/lib/load-file-once-inc.mal",
    "content": "(swap! counter (fn* [x] (+ 1 x)))\n"
  },
  {
    "path": "impls/tests/lib/load-file-once.mal",
    "content": "(def! counter (atom 0))\n;=>(atom 0)\n\n;; The counter is increased by each `load-file`.\n(load-file      \"../tests/lib/load-file-once-inc.mal\")\n;=>nil\n@counter\n;=>1\n(load-file      \"../tests/lib/load-file-once-inc.mal\")\n;=>nil\n@counter\n;=>2\n\n;; load-file-once is available\n(load-file      \"../lib/load-file-once.mal\")\n;=>nil\n\n;; First import actually calls `load-file`.\n(load-file-once \"../tests/lib/load-file-once-inc.mal\")\n;=>nil\n@counter\n;=>3\n\n;; Later imports do nothing.\n(load-file-once \"../tests/lib/load-file-once-inc.mal\")\n;=>nil\n@counter\n;=>3\n\n;; Loading the module twice does not reset its memory.\n(load-file      \"../lib/load-file-once.mal\")\n;=>nil\n(load-file-once \"../tests/lib/load-file-once-inc.mal\")\n;=>nil\n@counter\n;=>3\n\n;; even if done with itself\n(load-file-once \"../lib/load-file-once.mal\")\n;=>nil\n(load-file-once \"../tests/lib/load-file-once-inc.mal\")\n;=>nil\n@counter\n;=>3\n"
  },
  {
    "path": "impls/tests/lib/memoize.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../tests/computations.mal\")\n(load-file-once \"../lib/memoize.mal\")\n;=>nil\n\n(def! N 32)\n\n;; Benchmark naive 'fib'\n\n(def! r1 (fib N))                       ; Should be slow\n\n;; Benchmark memoized 'fib'\n\n(def! fib (memoize fib))\n(def! r2 (fib N))                       ; Should be quick\n\n(= r1 r2)\n;=>true\n"
  },
  {
    "path": "impls/tests/lib/pprint.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/pprint.mal\")\n;=>nil\n\n(pprint '(7 8 9 \"ten\" [11 12 [13 14]] 15 16))\n;/\\(7\n;/ 8\n;/ 9\n;/ \"ten\"\n;/ \\[11\n;/  12\n;/  \\[13\n;/   14\\]\\]\n;/ 15\n;/ 16\\)\n;=>nil\n\n(pprint '{:abc 123 :def {:ghi 456 :jkl [789 \"ten eleven twelve\"]}})\n;/\\{:abc 123\n;/ :def \\{:ghi 456\n;/       :jkl \\[789\n;/             \"ten eleven twelve\"\\]\\}\\}\n;=>nil\n\n(pprint '(7 8 {:abc 123 :def {:ghi 456 :jkl 789}} 9 10 [11 12 [13 14]] 15 16))\n;/\\(7\n;/ 8\n;/ \\{:abc 123\n;/  :def \\{:ghi 456\n;/        :jkl 789\\}\\}\n;/ 9\n;/ 10\n;/ \\[11\n;/  12\n;/  \\[13\n;/   14\\]\\]\n;/ 15\n;/ 16\\)\n;=>nil\n"
  },
  {
    "path": "impls/tests/lib/protocols.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/protocols.mal\")\n;=>nil\n\n;; Testing find-type for normal objects.\n(find-type 'a)\n;=>:mal/symbol\n(find-type :a)\n;=>:mal/keyword\n(find-type (atom 0))\n;=>:mal/atom\n(find-type nil)\n;=>:mal/nil\n(find-type true)\n;=>:mal/boolean\n(find-type false)\n;=>:mal/boolean\n(find-type 0)\n;=>:mal/number\n(find-type \"\")\n;=>:mal/string\n(find-type (defmacro! m (fn* [] nil)))\n;=>:mal/macro\n(find-type ())\n;=>:mal/list\n(find-type [])\n;=>:mal/vector\n(find-type {})\n;=>:mal/map\n(find-type (fn* [] nil))\n;=>:mal/function\n\n;; Testing find-type for explicit type metadata.\n(find-type ^{:type :a } ())\n;=>:a\n(find-type ^{:type :a } [])\n;=>:a\n(find-type ^{:type :a } {})\n;=>:a\n(find-type ^{:type :a } (fn* [] nil))\n;=>:a\n\n;; Testing protocols.\n(def! o1 ^{:type :t1 } [1])\n(def! o2 ^{:type :t2 } [2])\n(defprotocol p1 [m0 [this]] [ma [this a]] [mb [this & b]])\n(defprotocol p2)\n(satisfies? p1 o1)\n;=>false\n(satisfies? p1 o2)\n;=>false\n(satisfies? p2 o1)\n;=>false\n(satisfies? p2 o2)\n;=>false\n(extend :t1 p1 {  :m0  (fn* [this] (str \"t0\" this))  :ma (fn* [this a] (str \"ta\" this a))  :mb (fn* [this & b] (str \"tb\" this b))})\n;=>nil\n(extend :t2 p1 {  :m0  (fn* [this] (str \"u0\" this))  :ma (fn* [this a] (str \"ua\" this a))  :mb (fn* [this & b] (str \"ub\" this b))} p2 {})\n;=>nil\n(satisfies? p1 o1)\n;=>true\n(satisfies? p1 o2)\n;=>true\n(satisfies? p2 o1)\n;=>false\n(satisfies? p2 o2)\n;=>true\n\n;; Testing dispatching.\n(m0 o1)\n;=>\"t0[1]\"\n(ma o1 \"blue\")\n;=>\"ta[1]blue\"\n(mb o1 1 2 3)\n;=>\"tb[1](1 2 3)\"\n(m0 o2)\n;=>\"u0[2]\"\n(ma o2 \"blue\")\n;=>\"ua[2]blue\"\n(mb o2 1 2 3)\n;=>\"ub[2](1 2 3)\"\n"
  },
  {
    "path": "impls/tests/lib/reducers.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/reducers.mal\")\n;=>nil\n\n;; Testing reduce\n(reduce + 7 [])\n;=>7\n(reduce + 7 [1])\n;=>8\n(reduce + 7 [1 2])\n;=>10\n(reduce * 7 [-1 2])\n;=>-14\n(reduce concat [1] [[2] [3]])\n;=>(1 2 3)\n(reduce str \"a\" [\"b\" \"c\"])\n;=>\"abc\"\n\n;; Testing foldr\n(foldr + 7 [])\n;=>7\n(foldr + 7 [1])\n;=>8\n(foldr + 7 [1 2])\n;=>10\n(reduce * 7 [-1 2])\n;=>-14\n(foldr concat [1] [[2] [3]])\n;=>(2 3 1)\n(foldr str \"a\" [\"b\" \"c\"])\n;=>\"bca\"\n(foldr cons [4 5] [2 3])\n;=>(2 3 4 5)\n"
  },
  {
    "path": "impls/tests/lib/test_cascade.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/test_cascade.mal\")\n;=>nil\n\n;; Testing or\n(or)\n;=>nil\n(or 1)\n;=>1\n(or 1 2 3 4)\n;=>1\n(or false 2)\n;=>2\n(or false nil 3)\n;=>3\n(or false nil false false nil 4)\n;=>4\n(or false nil 3 false nil 4)\n;=>3\n(or (or false 4))\n;=>4\n\n;; Testing every?\n(every? first [])\n;=>true\n(every? first [[1] [2]])\n;=>true\n(every? first [[1] [nil] []])\n;=>false\n\n;; Testing some\n(some first [])\n;=>nil\n(some first [[nil] [1] []])\n;=>1\n\n(and)\n;=>true\n(and 1)\n;=>1\n(and 1 2 3 4)\n;=>4\n(and false 2)\n;=>false\n(and true 1 nil false)\n;=>nil\n"
  },
  {
    "path": "impls/tests/lib/threading.mal",
    "content": "(load-file \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/threading.mal\")\n;=>nil\n\n;; Testing -> macro\n(-> 7)\n;=>7\n(-> (list 7 8 9) first)\n;=>7\n(-> (list 7 8 9) (first))\n;=>7\n(-> (list 7 8 9) first (+ 7))\n;=>14\n(-> (list 7 8 9) rest (rest) first (+ 7))\n;=>16\n\n;; Testing ->> macro\n(->> \"L\")\n;=>\"L\"\n(->> \"L\" (str \"A\") (str \"M\"))\n;=>\"MAL\"\n(->> [4] (concat [3]) (concat [2]) rest (concat [1]))\n;=>(1 3 4)\n"
  },
  {
    "path": "impls/tests/lib/trivial.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/trivial.mal\")\n;=>nil\n\n(inc 12)\n;=>13\n(dec 12)\n;=>11\n(zero? 12)\n;=>false\n(zero? 0)\n;=>true\n(identity 12)\n;=>12\n(= (gensym) (gensym))\n;=>false\n"
  },
  {
    "path": "impls/tests/perf1.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/threading.mal\")    ; ->\n(load-file-once \"../lib/perf.mal\")         ; time\n(load-file-once \"../lib/test_cascade.mal\") ; or\n\n;;(prn \"Start: basic macros performance test\")\n\n(time (do\n  (or false nil false nil false nil false nil false nil 4)\n  (cond false 1 nil 2 false 3 nil 4 false 5 nil 6 \"else\" 7)\n  (-> (list 1 2 3 4 5 6 7 8 9) rest rest rest rest rest rest first)))\n\n;;(prn \"Done: basic macros performance test\")\n"
  },
  {
    "path": "impls/tests/perf2.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../tests/computations.mal\") ; fib sumdown\n(load-file-once \"../lib/perf.mal\")           ; time\n\n;;(prn \"Start: basic math/recursion test\")\n\n(time (do\n  (sumdown 10)\n  (fib 12)))\n\n;;(prn \"Done: basic math/recursion test\")\n"
  },
  {
    "path": "impls/tests/perf3.mal",
    "content": "(load-file      \"../lib/load-file-once.mal\")\n(load-file-once \"../lib/threading.mal\")    ; ->\n(load-file-once \"../lib/perf.mal\")         ; run-fn-for\n(load-file-once \"../lib/test_cascade.mal\") ; or\n\n;;(prn \"Start: basic macros/atom test\")\n\n(def! atm (atom (list 0 1 2 3 4 5 6 7 8 9)))\n\n(println \"iters over 10 seconds:\"\n  (run-fn-for\n    (fn* []\n      (do\n        (or false nil false nil false nil false nil false nil (first @atm))\n        (cond false 1 nil 2 false 3 nil 4 false 5 nil 6 \"else\" (first @atm))\n        (-> (deref atm) rest rest rest rest rest rest first)\n        (swap! atm (fn* [a] (concat (rest a) (list (first a)))))))\n    10))\n\n;;(prn \"Done: basic macros/atom test\")\n"
  },
  {
    "path": "impls/tests/print_argv.mal",
    "content": "; Used by the run_argv_test.sh test harness\n(prn *ARGV*)\n"
  },
  {
    "path": "impls/tests/run_argv_test.sh",
    "content": "#!/usr/bin/env bash\n\n#\n# Usage: run_argv_test.sh <command line arguments to run mal>\n#\n# Example: run_argv_test.sh python step6_file.py\n#\n\nassert_equal() {\n  if [ \"$1\" = \"$2\" ] ; then\n    echo \"OK: '$1'\"\n  else\n    echo \"FAIL: Expected '$1' but got '$2'\"\n    echo\n    exit 1\n  fi\n}\n\nif [ -z \"$1\" ] ; then\n  echo \"Usage: $0 <command line arguments to run mal>\"\n  exit 1\nfi\n\nroot=\"$(dirname $0)\"\n\nout=\"$( $@ $root/print_argv.mal aaa bbb ccc | tr -d '\\r' )\"\nassert_equal '(\"aaa\" \"bbb\" \"ccc\")' \"$out\"\n\n# Note: The 'make' implementation cannot handle arguments with spaces in them,\n# so for now we skip this test.\n#\n# out=\"$( $@ $root/print_argv.mal aaa 'bbb ccc' ddd )\"\n# assert_equal '(\"aaa\" \"bbb ccc\" \"ddd\")' \"$out\"\n\nout=\"$( $@ $root/print_argv.mal | tr -d '\\r' )\"\nassert_equal '()' \"$out\"\n\necho 'Passed all *ARGV* tests'\necho\n"
  },
  {
    "path": "impls/tests/step0_repl.mal",
    "content": ";; Testing basic string\nabcABC123\n;=>abcABC123\n\n;; Testing string containing spaces\nhello mal world\n;=>hello mal world\n\n;; Testing string containing symbols\n[]{}\"'* ;:()\n;=>[]{}\"'* ;:()\n\n\n;; Test long string\nhello world abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 (;:() []{}\"'* ;:() []{}\"'* ;:() []{}\"'*)\n;=>hello world abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 (;:() []{}\"'* ;:() []{}\"'* ;:() []{}\"'*)\n\n;; Non alphanumeric characters\n!\n;=>!\n&\n;=>&\n+\n;=>+\n,\n;=>,\n-\n;=>-\n/\n;=>/\n<\n;=><\n=\n;=>=\n>\n;=>>\n?\n;=>?\n@\n;=>@\n;;; Behaviour of backslash is not specified enough to test anything in step0.\n^\n;=>^\n_\n;=>_\n`\n;=>`\n~\n;=>~\n\n;>>> soft=True\n;>>> optional=True\n;; ------- Optional Functionality --------------\n;; ------- (Not needed for self-hosting) -------\n\n;; Non alphanumeric characters\n#\n;=>#\n$\n;=>$\n%\n;=>%\n.\n;=>.\n|\n;=>|\n"
  },
  {
    "path": "impls/tests/step1_read_print.mal",
    "content": ";; Testing read of numbers\n1\n;=>1\n7\n;=>7\n  7   \n;=>7\n-123\n;=>-123\n\n\n;; Testing read of symbols\n+\n;=>+\nabc\n;=>abc\n   abc   \n;=>abc\nabc5\n;=>abc5\nabc-def\n;=>abc-def\n\n;; Testing non-numbers starting with a dash.\n-\n;=>-\n-abc\n;=>-abc\n->>\n;=>->>\n\n;; Testing read of lists\n(+ 1 2)\n;=>(+ 1 2)\n()\n;=>()\n( )\n;=>()\n(nil)\n;=>(nil)\n((3 4))\n;=>((3 4))\n(+ 1 (+ 2 3))\n;=>(+ 1 (+ 2 3))\n  ( +   1   (+   2 3   )   )  \n;=>(+ 1 (+ 2 3))\n(* 1 2)\n;=>(* 1 2)\n(** 1 2)\n;=>(** 1 2)\n(* -3 6)\n;=>(* -3 6)\n(()())\n;=>(() ())\n\n;; Test commas as whitespace\n(1 2, 3,,,,),,\n;=>(1 2 3)\n\n\n;>>> deferrable=True\n\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing read of nil/true/false\nnil\n;=>nil\ntrue\n;=>true\nfalse\n;=>false\n\n;; Testing read of strings\n\"abc\"\n;=>\"abc\"\n   \"abc\"   \n;=>\"abc\"\n\"abc (with parens)\"\n;=>\"abc (with parens)\"\n\"abc\\\"def\"\n;=>\"abc\\\"def\"\n\"\"\n;=>\"\"\n\"\\\\\"\n;=>\"\\\\\"\n\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"\n;=>\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"\n\"&\"\n;=>\"&\"\n\"'\"\n;=>\"'\"\n\"(\"\n;=>\"(\"\n\")\"\n;=>\")\"\n\"*\"\n;=>\"*\"\n\"+\"\n;=>\"+\"\n\",\"\n;=>\",\"\n\"-\"\n;=>\"-\"\n\"/\"\n;=>\"/\"\n\":\"\n;=>\":\"\n\";\"\n;=>\";\"\n\"<\"\n;=>\"<\"\n\"=\"\n;=>\"=\"\n\">\"\n;=>\">\"\n\"?\"\n;=>\"?\"\n\"@\"\n;=>\"@\"\n\"[\"\n;=>\"[\"\n\"]\"\n;=>\"]\"\n\"^\"\n;=>\"^\"\n\"_\"\n;=>\"_\"\n\"`\"\n;=>\"`\"\n\"{\"\n;=>\"{\"\n\"}\"\n;=>\"}\"\n\"~\"\n;=>\"~\"\n\"!\"\n;=>\"!\"\n\n;; Testing reader errors\n(1 2\n;/.*(EOF|end of input|unbalanced).*\n[1 2\n;/.*(EOF|end of input|unbalanced).*\n{\"a\" 2\n;/.*(EOF|end of input|unbalanced).*\n\n;;; These should throw some error with no return value\n\"abc\n;/.*(EOF|end of input|unbalanced).*\n\"\n;/.*(EOF|end of input|unbalanced).*\n\"\\\"\n;/.*(EOF|end of input|unbalanced).*\n\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"\n;/.*(EOF|end of input|unbalanced).*\n(1 \"abc\n;/.*(EOF|end of input|unbalanced).*\n(1 \"abc\"\n;/.*(EOF|end of input|unbalanced).*\n\n;; Testing read of quoting\n'1\n;=>(quote 1)\n'(1 2 3)\n;=>(quote (1 2 3))\n`1\n;=>(quasiquote 1)\n`(1 2 3)\n;=>(quasiquote (1 2 3))\n`(a (b) c)\n;=>(quasiquote (a (b) c))\n~1\n;=>(unquote 1)\n~(1 2 3)\n;=>(unquote (1 2 3))\n`(1 ~a 3)\n;=>(quasiquote (1 (unquote a) 3))\n~@(1 2 3)\n;=>(splice-unquote (1 2 3))\n\n\n;; Testing keywords\n:kw\n;=>:kw\n(:kw1 :kw2 :kw3)\n;=>(:kw1 :kw2 :kw3)\n\n;; Testing read of vectors\n[+ 1 2]\n;=>[+ 1 2]\n[]\n;=>[]\n[ ]\n;=>[]\n[[3 4]]\n;=>[[3 4]]\n[+ 1 [+ 2 3]]\n;=>[+ 1 [+ 2 3]]\n  [ +   1   [+   2 3   ]   ]  \n;=>[+ 1 [+ 2 3]]\n([])\n;=>([])\n\n;; Testing read of hash maps\n{}\n;=>{}\n{ }\n;=>{}\n{\"abc\" 1}\n;=>{\"abc\" 1}\n{\"a\" {\"b\" 2}}\n;=>{\"a\" {\"b\" 2}}\n{\"a\" {\"b\" {\"c\" 3}}}\n;=>{\"a\" {\"b\" {\"c\" 3}}}\n{  \"a\"  {\"b\"   {  \"cde\"     3   }  }}\n;=>{\"a\" {\"b\" {\"cde\" 3}}}\n;;; The regexp sorcery here ensures that each key goes with the correct\n;;; value and that each key appears only once.\n{\"a1\" 1 \"a2\" 2 \"a3\" 3}\n;/{\"a([1-3])\" \\1 \"a(?!\\1)([1-3])\" \\2 \"a(?!\\1)(?!\\2)([1-3])\" \\3}\n{  :a  {:b   {  :cde     3   }  }}\n;=>{:a {:b {:cde 3}}}\n{\"1\" 1}\n;=>{\"1\" 1}\n({})\n;=>({})\n\n;; Testing read of comments\n ;; whole line comment (not an exception)\n1 ; comment after expression\n;=>1\n1; comment after expression\n;=>1\n\n;; Testing read of @/deref\n@a\n;=>(deref a)\n\n;; Colon character inside a symbol\na:\n;=>a:\n\n;>>> soft=True\n;>>> optional=True\n;;\n;; -------- Optional Functionality --------\n\n;; Testing read of ^/metadata\n^{\"a\" 1} [1 2 3]\n;=>(with-meta [1 2 3] {\"a\" 1})\n^2 [1 2 3]\n;=>(with-meta [1 2 3] 2)\n\n;; Non alphanumeric characters in strings\n;;; \\t is not specified enough to be tested\n\"\\n\"\n;=>\"\\n\"\n\"#\"\n;=>\"#\"\n\"$\"\n;=>\"$\"\n\"%\"\n;=>\"%\"\n\".\"\n;=>\".\"\n\"\\\\\"\n;=>\"\\\\\"\n\"|\"\n;=>\"|\"\n\n;; Non alphanumeric characters in comments\n1;!\n;=>1\n1;\"\n;=>1\n1;#\n;=>1\n1;$\n;=>1\n1;%\n;=>1\n1;'\n;=>1\n1;\\\n;=>1\n1;\\\\\n;=>1\n1;\\\\\\\n;=>1\n1;`\n;=>1\n;;; Hopefully less problematic characters\n1; &()*+,-./:;<=>?@[]^_{|}~\n;=>1\n"
  },
  {
    "path": "impls/tests/step2_eval.mal",
    "content": ";; Testing evaluation of arithmetic operations\n(+ 1 2)\n;=>3\n\n(+ 5 (* 2 3))\n;=>11\n\n(- (+ 5 (* 2 3)) 3)\n;=>8\n\n(/ (- (+ 5 (* 2 3)) 3) 4)\n;=>2\n\n(/ (- (+ 515 (* 87 311)) 302) 27)\n;=>1010\n\n(* -3 6)\n;=>-18\n\n(/ (- (+ 515 (* -87 311)) 296) 27)\n;=>-994\n\n;;; This should throw an error with no return value\n(abc 1 2 3)\n;/.+\n\n;; Testing empty list\n()\n;=>()\n\n;>>> deferrable=True\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing nil inside vector\n[nil]\n;=>[nil]\n\n;; Testing evaluation within collection literals\n[1 2 (+ 1 2)]\n;=>[1 2 3]\n\n{\"a\" (+ 7 8)}\n;=>{\"a\" 15}\n\n{:a (+ 7 8)}\n;=>{:a 15}\n\n;; Check that evaluation hasn't broken empty collections\n[]\n;=>[]\n{}\n;=>{}\n"
  },
  {
    "path": "impls/tests/step3_env.mal",
    "content": ";; Testing REPL_ENV\n(+ 1 2)\n;=>3\n(/ (- (+ 5 (* 2 3)) 3) 4)\n;=>2\n\n\n;; Testing def!\n(def! x 3)\n;=>3\nx\n;=>3\n(def! x 4)\n;=>4\nx\n;=>4\n(def! y (+ 1 7))\n;=>8\ny\n;=>8\n\n;; Verifying symbols are case-sensitive\n(def! mynum 111)\n;=>111\n(def! MYNUM 222)\n;=>222\nmynum\n;=>111\nMYNUM\n;=>222\n\n;; Check env lookup non-fatal error\n(abc 1 2 3)\n;/.*'?abc'? not found.*\n;; Check that error aborts def!\n(def! w 123)\n(def! w (abc))\nw\n;=>123\n\n;; Testing let*\n(let* (z 9) z)\n;=>9\n(let* (x 9) x)\n;=>9\nx\n;=>4\n(let* (z (+ 2 3)) (+ 1 z))\n;=>6\n(let* (p (+ 2 3) q (+ 2 p)) (+ p q))\n;=>12\n(def! y (let* (z 7) z))\ny\n;=>7\n\n;; Testing outer environment\n(def! a 4)\n;=>4\n(let* (q 9) q)\n;=>9\n(let* (q 9) a)\n;=>4\n(let* (z 2) (let* (q 9) a))\n;=>4\n\n;>>> deferrable=True\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing let* with vector bindings\n(let* [z 9] z)\n;=>9\n(let* [p (+ 2 3) q (+ 2 p)] (+ p q))\n;=>12\n\n;; Testing vector evaluation\n(let* (a 5 b 6) [3 4 a [b 7] 8])\n;=>[3 4 5 [6 7] 8]\n\n;>>> soft=True\n;>>> optional=True\n;;\n;; -------- Optional Functionality --------\n\n;; Check that last assignment takes priority\n(let* (x 2 x 3) x)\n;=>3\n\n;; Check DEBUG-EVAL\n(let* (DEBUG-EVAL false) (- 3 1))\n;=>2\n(let* (DEBUG-EVAL nil) (- 3 1))\n;=>2\n;;; Some implementations avoid a recursive EVAL when the first element\n;;; is a symbol or when map(EVAL, list) encounters a number.\n(let* (a 3 b 2 DEBUG-EVAL true) (- a b))\n;/EVAL: \\(- a b\\).*\\n1\n;; Check the readably pretty-printing option\n(let* (DEBUG-EVAL 1) \"a\")\n;/EVAL: \"a\".*\\n\"a\"\n;; Usually false values\n(let* (a 3 DEBUG-EVAL ()) a)\n;/EVAL: a.*\\n3\n(let* (a 3 DEBUG-EVAL 0) a)\n;/EVAL: a.*\\n3\n(let* (a 3 DEBUG-EVAL \"\") a)\n;/EVAL: a.*\\n3\n"
  },
  {
    "path": "impls/tests/step4_if_fn_do.mal",
    "content": ";; -----------------------------------------------------\n\n\n;; Testing list functions\n(list)\n;=>()\n(list? (list))\n;=>true\n(list? nil)\n;=>false\n(empty? (list))\n;=>true\n(empty? (list 1))\n;=>false\n(list 1 2 3)\n;=>(1 2 3)\n(count (list 1 2 3))\n;=>3\n(count (list))\n;=>0\n(count nil)\n;=>0\n(if (> (count (list 1 2 3)) 3) 89 78)\n;=>78\n(if (>= (count (list 1 2 3)) 3) 89 78)\n;=>89\n\n\n;; Testing if form\n(if true 7 8)\n;=>7\n(if false 7 8)\n;=>8\n(if false 7 false)\n;=>false\n(if true (+ 1 7) (+ 1 8))\n;=>8\n(if false (+ 1 7) (+ 1 8))\n;=>9\n(if nil 7 8)\n;=>8\n(if 0 7 8)\n;=>7\n(if (list) 7 8)\n;=>7\n(if (list 1 2 3) 7 8)\n;=>7\n(= (list) nil)\n;=>false\n\n\n;; Testing 1-way if form\n(if false (+ 1 7))\n;=>nil\n(if nil 8)\n;=>nil\n(if nil 8 7)\n;=>7\n(if true (+ 1 7))\n;=>8\n\n\n;; Testing basic conditionals\n(= 2 1)\n;=>false\n(= 1 1)\n;=>true\n(= 1 2)\n;=>false\n(= 1 (+ 1 1))\n;=>false\n(= 2 (+ 1 1))\n;=>true\n\n(> 2 1)\n;=>true\n(> 1 1)\n;=>false\n(> 1 2)\n;=>false\n\n(>= 2 1)\n;=>true\n(>= 1 1)\n;=>true\n(>= 1 2)\n;=>false\n\n(< 2 1)\n;=>false\n(< 1 1)\n;=>false\n(< 1 2)\n;=>true\n\n(<= 2 1)\n;=>false\n(<= 1 1)\n;=>true\n(<= 1 2)\n;=>true\n\n\n;; Testing equality and the representation of nil false true\n(= 1 1)\n;=>true\n(= 0 0)\n;=>true\n(= 1 0)\n;=>false\n\n(= nil nil)\n;=>true\n(= nil false)\n;=>false\n(= nil true)\n;=>false\n(= nil 0)\n;=>false\n(= nil 1)\n;=>false\n(= nil \"\")\n;=>false\n(= nil ())\n;=>false\n(= nil [])\n;=>false\n\n(= false nil)\n;=>false\n(= false false)\n;=>true\n(= false true)\n;=>false\n(= false 0)\n;=>false\n(= false 1)\n;=>false\n(= false \"\")\n;=>false\n(= false ())\n;=>false\n\n(= true nil)\n;=>false\n(= true false)\n;=>false\n(= true true)\n;=>true\n(= true 0)\n;=>false\n(= true 1)\n;=>false\n(= true \"\")\n;=>false\n(= true ())\n;=>false\n\n(= (list) (list))\n;=>true\n(= (list) ())\n;=>true\n(= (list 1 2) (list 1 2))\n;=>true\n(= (list 1) (list))\n;=>false\n(= (list) (list 1))\n;=>false\n(= 0 (list))\n;=>false\n(= (list) 0)\n;=>false\n(= (list nil) (list))\n;=>false\n\n\n;; Testing builtin and user defined functions\n(+ 1 2)\n;=>3\n( (fn* (a b) (+ b a)) 3 4)\n;=>7\n( (fn* () 4) )\n;=>4\n( (fn* () ()) )\n;=>()\n\n( (fn* (f x) (f x)) (fn* (a) (+ 1 a)) 7)\n;=>8\n\n\n;; Testing closures\n( ( (fn* (a) (fn* (b) (+ a b))) 5) 7)\n;=>12\n\n(def! gen-plus5 (fn* () (fn* (b) (+ 5 b))))\n(def! plus5 (gen-plus5))\n(plus5 7)\n;=>12\n\n(def! gen-plusX (fn* (x) (fn* (b) (+ x b))))\n(def! plus7 (gen-plusX 7))\n(plus7 8)\n;=>15\n\n(let* [b 0 f (fn* [] b)] (let* [b 1] (f)))\n;=>0\n\n((let* [b 0] (fn* [] b)))\n;=>0\n\n;; Testing do form\n(do (prn 101))\n;/101\n;=>nil\n(do (prn 102) 7)\n;/102\n;=>7\n(do (prn 101) (prn 102) (+ 1 2))\n;/101\n;/102\n;=>3\n\n(do (def! a 6) 7 (+ a 8))\n;=>14\na\n;=>6\n\n;; Testing special form case-sensitivity\n(def! DO (fn* (a) 7))\n(DO 3)\n;=>7\n\n;; Testing recursive sumdown function\n(def! sumdown (fn* (N) (if (> N 0) (+ N (sumdown  (- N 1))) 0)))\n(sumdown 1)\n;=>1\n(sumdown 2)\n;=>3\n(sumdown 6)\n;=>21\n\n\n;; Testing recursive fibonacci function\n(def! fib (fn* (N) (if (= N 0) 1 (if (= N 1) 1 (+ (fib (- N 1)) (fib (- N 2)))))))\n(fib 1)\n;=>1\n(fib 2)\n;=>2\n(fib 4)\n;=>5\n\n\n;; Testing recursive function in environment.\n(let* (f (fn* () x) x 3) (f))\n;=>3\n(let* (cst (fn* (n) (if (= n 0) nil (cst (- n 1))))) (cst 1))\n;=>nil\n(let* (f (fn* (n) (if (= n 0) 0 (g (- n 1)))) g (fn* (n) (f n))) (f 2))\n;=>0\n\n\n;>>> deferrable=True\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing if on strings\n\n(if \"\" 7 8)\n;=>7\n\n;; Testing string equality\n\n(= \"\" \"\")\n;=>true\n(= \"abc\" \"abc\")\n;=>true\n(= \"abc\" \"\")\n;=>false\n(= \"\" \"abc\")\n;=>false\n(= \"abc\" \"def\")\n;=>false\n(= \"abc\" \"ABC\")\n;=>false\n(= (list) \"\")\n;=>false\n(= \"\" (list))\n;=>false\n\n;; Testing variable length arguments\n\n( (fn* (& more) (count more)) 1 2 3)\n;=>3\n( (fn* (& more) (list? more)) 1 2 3)\n;=>true\n( (fn* (& more) (count more)) 1)\n;=>1\n( (fn* (& more) (count more)) )\n;=>0\n( (fn* (& more) (list? more)) )\n;=>true\n( (fn* (a & more) (count more)) 1 2 3)\n;=>2\n( (fn* (a & more) (count more)) 1)\n;=>0\n( (fn* (a & more) (list? more)) 1)\n;=>true\n\n\n;; Testing language defined not function\n(not false)\n;=>true\n(not nil)\n;=>true\n(not true)\n;=>false\n(not \"a\")\n;=>false\n(not 0)\n;=>false\n\n\n;; -----------------------------------------------------\n\n;; Testing string quoting\n\n\"\"\n;=>\"\"\n\n\"abc\"\n;=>\"abc\"\n\n\"abc  def\"\n;=>\"abc  def\"\n\n\"\\\"\"\n;=>\"\\\"\"\n\n\"abc\\ndef\\nghi\"\n;=>\"abc\\ndef\\nghi\"\n\n\"abc\\\\def\\\\ghi\"\n;=>\"abc\\\\def\\\\ghi\"\n\n\"\\\\n\"\n;=>\"\\\\n\"\n\n;; Testing pr-str\n\n(pr-str)\n;=>\"\"\n\n(pr-str \"\")\n;=>\"\\\"\\\"\"\n\n(pr-str \"abc\")\n;=>\"\\\"abc\\\"\"\n\n(pr-str \"abc  def\" \"ghi jkl\")\n;=>\"\\\"abc  def\\\" \\\"ghi jkl\\\"\"\n\n(pr-str \"\\\"\")\n;=>\"\\\"\\\\\\\"\\\"\"\n\n(pr-str (list 1 2 \"abc\" \"\\\"\") \"def\")\n;=>\"(1 2 \\\"abc\\\" \\\"\\\\\\\"\\\") \\\"def\\\"\"\n\n(pr-str \"abc\\ndef\\nghi\")\n;=>\"\\\"abc\\\\ndef\\\\nghi\\\"\"\n\n(pr-str \"abc\\\\def\\\\ghi\")\n;=>\"\\\"abc\\\\\\\\def\\\\\\\\ghi\\\"\"\n\n(pr-str (list))\n;=>\"()\"\n\n;; Testing str\n\n(str)\n;=>\"\"\n\n(str \"\")\n;=>\"\"\n\n(str \"abc\")\n;=>\"abc\"\n\n(str \"\\\"\")\n;=>\"\\\"\"\n\n(str 1 \"abc\" 3)\n;=>\"1abc3\"\n\n(str \"abc  def\" \"ghi jkl\")\n;=>\"abc  defghi jkl\"\n\n(str \"abc\\ndef\\nghi\")\n;=>\"abc\\ndef\\nghi\"\n\n(str \"abc\\\\def\\\\ghi\")\n;=>\"abc\\\\def\\\\ghi\"\n\n(str (list 1 2 \"abc\" \"\\\"\") \"def\")\n;=>\"(1 2 abc \\\")def\"\n\n(str (list))\n;=>\"()\"\n\n;; Testing prn\n(prn)\n;/\n;=>nil\n\n(prn \"\")\n;/\"\"\n;=>nil\n\n(prn \"abc\")\n;/\"abc\"\n;=>nil\n\n(prn \"abc  def\" \"ghi jkl\")\n;/\"abc  def\" \"ghi jkl\"\n\n(prn \"\\\"\")\n;/\"\\\\\"\"\n;=>nil\n\n(prn \"abc\\ndef\\nghi\")\n;/\"abc\\\\ndef\\\\nghi\"\n;=>nil\n\n(prn \"abc\\\\def\\\\ghi\")\n;/\"abc\\\\\\\\def\\\\\\\\ghi\"\nnil\n\n(prn (list 1 2 \"abc\" \"\\\"\") \"def\")\n;/\\(1 2 \"abc\" \"\\\\\"\"\\) \"def\"\n;=>nil\n\n\n;; Testing println\n(println)\n;/\n;=>nil\n\n(println \"\")\n;/\n;=>nil\n\n(println \"abc\")\n;/abc\n;=>nil\n\n(println \"abc  def\" \"ghi jkl\")\n;/abc  def ghi jkl\n\n(println \"\\\"\")\n;/\"\n;=>nil\n\n(println \"abc\\ndef\\nghi\")\n;/abc\n;/def\n;/ghi\n;=>nil\n\n(println \"abc\\\\def\\\\ghi\")\n;/abc\\\\def\\\\ghi\n;=>nil\n\n(println (list 1 2 \"abc\" \"\\\"\") \"def\")\n;/\\(1 2 abc \"\\) def\n;=>nil\n\n\n;; Testing keywords\n(= :abc :abc)\n;=>true\n(= :abc :def)\n;=>false\n(= :abc \":abc\")\n;=>false\n(= (list :abc) (list :abc))\n;=>true\n\n;; Testing vector truthiness\n(if [] 7 8)\n;=>7\n\n;; Testing vector printing\n(pr-str [1 2 \"abc\" \"\\\"\"] \"def\")\n;=>\"[1 2 \\\"abc\\\" \\\"\\\\\\\"\\\"] \\\"def\\\"\"\n\n(pr-str [])\n;=>\"[]\"\n\n(str [1 2 \"abc\" \"\\\"\"] \"def\")\n;=>\"[1 2 abc \\\"]def\"\n\n(str [])\n;=>\"[]\"\n\n\n;; Testing vector functions\n(count [1 2 3])\n;=>3\n(empty? [1 2 3])\n;=>false\n(empty? [])\n;=>true\n(list? [4 5 6])\n;=>false\n\n;; Testing vector equality\n(= [] (list))\n;=>true\n(= [7 8] [7 8])\n;=>true\n(= [:abc] [:abc])\n;=>true\n(= (list 1 2) [1 2])\n;=>true\n(= (list 1) [])\n;=>false\n(= [] [1])\n;=>false\n(= 0 [])\n;=>false\n(= [] 0)\n;=>false\n(= [] \"\")\n;=>false\n(= \"\" [])\n;=>false\n\n;; Testing vector parameter lists\n( (fn* [] 4) )\n;=>4\n( (fn* [f x] (f x)) (fn* [a] (+ 1 a)) 7)\n;=>8\n\n;; Nested vector/list equality\n(= [(list)] (list []))\n;=>true\n(= [1 2 (list 3 4 [5 6])] (list 1 2 [3 4 (list 5 6)]))\n;=>true\n"
  },
  {
    "path": "impls/tests/step5_tco.mal",
    "content": ";; Testing recursive tail-call function\n\n(def! sum2 (fn* (n acc) (if (= n 0) acc (sum2 (- n 1) (+ n acc)))))\n\n;; TODO: test let*, and do for TCO\n\n(sum2 10 0)\n;=>55\n\n(def! res2 nil)\n;=>nil\n(def! res2 (sum2 10000 0))\nres2\n;=>50005000\n\n\n;; Test mutually recursive tail-call functions\n\n(def! foo (fn* (n) (if (= n 0) 0 (bar (- n 1)))))\n(def! bar (fn* (n) (if (= n 0) 0 (foo (- n 1)))))\n\n(foo 10000)\n;=>0\n"
  },
  {
    "path": "impls/tests/step6_file.mal",
    "content": ";;; TODO: really a step5 test\n;;\n;; Testing that (do (do)) not broken by TCO\n(do (do 1 2))\n;=>2\n\n;;\n;; Testing read-string, eval and slurp\n(read-string \"(1 2 (3 4) nil)\")\n;=>(1 2 (3 4) nil)\n\n(= nil (read-string \"nil\"))\n;=>true\n\n(read-string \"(+ 2 3)\")\n;=>(+ 2 3)\n\n(read-string \"\\\"\\n\\\"\")\n;=>\"\\n\"\n\n(read-string \"7 ;; comment\")\n;=>7\n\n;;; Differing output, but make sure no fatal error\n(read-string \";; comment\")\n\n(eval (read-string \"(+ 2 3)\"))\n;=>5\n\n(slurp \"../tests/test.txt\")\n;=>\"A line of text\\n\"\n\n;;; Load the same file twice.\n(slurp \"../tests/test.txt\")\n;=>\"A line of text\\n\"\n\n;; Testing load-file\n\n(load-file \"../tests/inc.mal\")\n;=>nil\n(inc1 7)\n;=>8\n(inc2 7)\n;=>9\n(inc3 9)\n;=>12\n\n;;\n;; Testing atoms\n\n(def! inc3 (fn* (a) (+ 3 a)))\n\n(def! a (atom 2))\n;=>(atom 2)\n\n(atom? a)\n;=>true\n\n(atom? 1)\n;=>false\n\n(deref a)\n;=>2\n\n(reset! a 3)\n;=>3\n\n(deref a)\n;=>3\n\n(swap! a inc3)\n;=>6\n\n(deref a)\n;=>6\n\n(swap! a (fn* (a) a))\n;=>6\n\n(swap! a (fn* (a) (* 2 a)))\n;=>12\n\n(swap! a (fn* (a b) (* a b)) 10)\n;=>120\n\n(swap! a + 3)\n;=>123\n\n;; Test that do only evals each slot once\n(def! b (atom 0))\n(do (swap! b + 1) (swap! b + 10) (swap! b + 100))\n(deref b)\n;=>111\n\n;; Testing swap!/closure interaction\n(def! inc-it (fn* (a) (+ 1 a)))\n(def! atm (atom 7))\n(def! f (fn* () (swap! atm inc-it)))\n(f)\n;=>8\n(f)\n;=>9\n\n;; Testing whether closures can retain atoms\n(def! g (let* (atm (atom 0)) (fn* () (deref atm))))\n(def! atm (atom 1))\n(g)\n;=>0\n\n;>>> deferrable=True\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing read-string parsing errors\n(read-string \"(+ 1\")\n;/.*(EOF|end of input|unbalanced).*\n(read-string \"[+ 1\")\n;/.*(EOF|end of input|unbalanced).*\n(read-string \"{:a 1\")\n;/.*(EOF|end of input|unbalanced).*\n\n;; Testing reading of large files\n(load-file \"../tests/computations.mal\")\n;=>nil\n(sumdown 2)\n;=>3\n(fib 2)\n;=>1\n\n;; Testing `@` reader macro (short for `deref`)\n(def! atm (atom 9))\n@atm\n;=>9\n\n;;; TODO: really a step5 test\n;; Testing that vector params not broken by TCO\n(def! g (fn* [] 78))\n(g)\n;=>78\n(def! g (fn* [a] (+ a 78)))\n(g 3)\n;=>81\n\n;;\n;; Testing that *ARGV* exists and is an empty list\n(list? *ARGV*)\n;=>true\n*ARGV*\n;=>()\n\n;;\n;; Testing that eval sets aa in root scope, and that it is found in nested scope\n(let* (b 12) (do (eval (read-string \"(def! aa 7)\")) aa ))\n;=>7\n\n;>>> soft=True\n;>>> optional=True\n;;\n;; -------- Optional Functionality --------\n\n;; Testing comments in a file\n(load-file \"../tests/incB.mal\")\n;=>nil\n(inc4 7)\n;=>11\n(inc5 7)\n;=>12\n\n;; Testing map literal across multiple lines in a file\n(load-file \"../tests/incC.mal\")\n;=>nil\nmymap\n;=>{\"a\" 1}\n\n;; Checking that eval does not use local environments.\n(def! a 1)\n;=>1\n(let* (a 2) (eval (read-string \"a\")))\n;=>1\n\n;; Non alphanumeric characters in comments in read-string\n(read-string \"1;!\")\n;=>1\n(read-string \"1;\\\"\")\n;=>1\n(read-string \"1;#\")\n;=>1\n(read-string \"1;$\")\n;=>1\n(read-string \"1;%\")\n;=>1\n(read-string \"1;'\")\n;=>1\n(read-string \"1;\\\\\")\n;=>1\n(read-string \"1;\\\\\\\\\")\n;=>1\n(read-string \"1;\\\\\\\\\\\\\")\n;=>1\n(read-string \"1;`\")\n;=>1\n;;; Hopefully less problematic characters can be checked together\n(read-string \"1; &()*+,-./:;<=>?@[]^_{|}~\")\n;=>1\n\n"
  },
  {
    "path": "impls/tests/step7_quote.mal",
    "content": ";; Testing cons function\n(cons 1 (list))\n;=>(1)\n(cons 1 (list 2))\n;=>(1 2)\n(cons 1 (list 2 3))\n;=>(1 2 3)\n(cons (list 1) (list 2 3))\n;=>((1) 2 3)\n\n(def! a (list 2 3))\n(cons 1 a)\n;=>(1 2 3)\na\n;=>(2 3)\n\n;; Testing concat function\n(concat)\n;=>()\n(concat (list 1 2))\n;=>(1 2)\n(concat (list 1 2) (list 3 4))\n;=>(1 2 3 4)\n(concat (list 1 2) (list 3 4) (list 5 6))\n;=>(1 2 3 4 5 6)\n(concat (concat))\n;=>()\n(concat (list) (list))\n;=>()\n(= () (concat))\n;=>true\n\n(def! a (list 1 2))\n(def! b (list 3 4))\n(concat a b (list 5 6))\n;=>(1 2 3 4 5 6)\na\n;=>(1 2)\nb\n;=>(3 4)\n\n;; Testing regular quote\n(quote 7)\n;=>7\n(quote (1 2 3))\n;=>(1 2 3)\n(quote (1 2 (3 4)))\n;=>(1 2 (3 4))\n\n;; Testing simple quasiquote\n(quasiquote nil)\n;=>nil\n(quasiquote 7)\n;=>7\n(quasiquote a)\n;=>a\n(quasiquote {\"a\" b})\n;=>{\"a\" b}\n\n;; Testing quasiquote with lists\n(quasiquote ())\n;=>()\n(quasiquote (1 2 3))\n;=>(1 2 3)\n(quasiquote (a))\n;=>(a)\n(quasiquote (1 2 (3 4)))\n;=>(1 2 (3 4))\n(quasiquote (nil))\n;=>(nil)\n(quasiquote (1 ()))\n;=>(1 ())\n(quasiquote (() 1))\n;=>(() 1)\n(quasiquote (1 () 2))\n;=>(1 () 2)\n(quasiquote (()))\n;=>(())\n\n;; Testing unquote\n(quasiquote (unquote 7))\n;=>7\n(def! a 8)\n;=>8\n(quasiquote a)\n;=>a\n(quasiquote (unquote a))\n;=>8\n(quasiquote (1 a 3))\n;=>(1 a 3)\n(quasiquote (1 (unquote a) 3))\n;=>(1 8 3)\n(def! b (quote (1 \"b\" \"d\")))\n;=>(1 \"b\" \"d\")\n(quasiquote (1 b 3))\n;=>(1 b 3)\n(quasiquote (1 (unquote b) 3))\n;=>(1 (1 \"b\" \"d\") 3)\n(quasiquote ((unquote 1) (unquote 2)))\n;=>(1 2)\n\n;; Quasiquote and environments\n(let* (x 0) (quasiquote (unquote x)))\n;=>0\n\n;; Testing splice-unquote\n(def! c (quote (1 \"b\" \"d\")))\n;=>(1 \"b\" \"d\")\n(quasiquote (1 c 3))\n;=>(1 c 3)\n(quasiquote (1 (splice-unquote c) 3))\n;=>(1 1 \"b\" \"d\" 3)\n(quasiquote (1 (splice-unquote c)))\n;=>(1 1 \"b\" \"d\")\n(quasiquote ((splice-unquote c) 2))\n;=>(1 \"b\" \"d\" 2)\n(quasiquote ((splice-unquote c) (splice-unquote c)))\n;=>(1 \"b\" \"d\" 1 \"b\" \"d\")\n\n;; Testing symbol equality\n(= (quote abc) (quote abc))\n;=>true\n(= (quote abc) (quote abcd))\n;=>false\n(= (quote abc) \"abc\")\n;=>false\n(= \"abc\" (quote abc))\n;=>false\n(= \"abc\" (str (quote abc)))\n;=>true\n(= (quote abc) nil)\n;=>false\n(= nil (quote abc))\n;=>false\n\n;>>> deferrable=True\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing ' (quote) reader macro\n'7\n;=>7\n'(1 2 3)\n;=>(1 2 3)\n'(1 2 (3 4))\n;=>(1 2 (3 4))\n\n;; Testing cons and concat with vectors\n\n(cons 1 [])\n;=>(1)\n(cons [1] [2 3])\n;=>([1] 2 3)\n(cons 1 [2 3])\n;=>(1 2 3)\n(concat [1 2] (list 3 4) [5 6])\n;=>(1 2 3 4 5 6)\n(concat [1 2])\n;=>(1 2)\n\n;>>> optional=True\n;;\n;; -------- Optional Functionality --------\n\n;; Testing ` (quasiquote) reader macro\n`7\n;=>7\n`(1 2 3)\n;=>(1 2 3)\n`(1 2 (3 4))\n;=>(1 2 (3 4))\n`(nil)\n;=>(nil)\n\n;; Testing ~ (unquote) reader macro\n`~7\n;=>7\n(def! a 8)\n;=>8\n`(1 ~a 3)\n;=>(1 8 3)\n(def! b '(1 \"b\" \"d\"))\n;=>(1 \"b\" \"d\")\n`(1 b 3)\n;=>(1 b 3)\n`(1 ~b 3)\n;=>(1 (1 \"b\" \"d\") 3)\n\n;; Testing ~@ (splice-unquote) reader macro\n(def! c '(1 \"b\" \"d\"))\n;=>(1 \"b\" \"d\")\n`(1 c 3)\n;=>(1 c 3)\n`(1 ~@c 3)\n;=>(1 1 \"b\" \"d\" 3)\n\n;>>> soft=True\n\n;; Testing vec function\n\n(vec (list))\n;=>[]\n(vec (list 1))\n;=>[1]\n(vec (list 1 2))\n;=>[1 2]\n(vec [])\n;=>[]\n(vec [1 2])\n;=>[1 2]\n\n;; Testing that vec does not mutate the original list\n(def! a (list 1 2))\n(vec a)\n;=>[1 2]\na\n;=>(1 2)\n\n;; Test quine\n((fn* (q) (quasiquote ((unquote q) (quote (unquote q))))) (quote (fn* (q) (quasiquote ((unquote q) (quote (unquote q)))))))\n;=>((fn* (q) (quasiquote ((unquote q) (quote (unquote q))))) (quote (fn* (q) (quasiquote ((unquote q) (quote (unquote q)))))))\n\n;; Testing quasiquote with vectors\n(quasiquote [])\n;=>[]\n(quasiquote [[]])\n;=>[[]]\n(quasiquote [()])\n;=>[()]\n(quasiquote ([]))\n;=>([])\n(def! a 8)\n;=>8\n`[1 a 3]\n;=>[1 a 3]\n(quasiquote [a [] b [c] d [e f] g])\n;=>[a [] b [c] d [e f] g]\n\n;; Testing unquote with vectors\n`[~a]\n;=>[8]\n`[(~a)]\n;=>[(8)]\n`([~a])\n;=>([8])\n`[a ~a a]\n;=>[a 8 a]\n`([a ~a a])\n;=>([a 8 a])\n`[(a ~a a)]\n;=>[(a 8 a)]\n\n;; Testing splice-unquote with vectors\n(def! c '(1 \"b\" \"d\"))\n;=>(1 \"b\" \"d\")\n`[~@c]\n;=>[1 \"b\" \"d\"]\n`[(~@c)]\n;=>[(1 \"b\" \"d\")]\n`([~@c])\n;=>([1 \"b\" \"d\"])\n`[1 ~@c 3]\n;=>[1 1 \"b\" \"d\" 3]\n`([1 ~@c 3])\n;=>([1 1 \"b\" \"d\" 3])\n`[(1 ~@c 3)]\n;=>[(1 1 \"b\" \"d\" 3)]\n\n;; Misplaced unquote or splice-unquote\n`(0 unquote)\n;=>(0 unquote)\n`(0 splice-unquote)\n;=>(0 splice-unquote)\n`[unquote 0]\n;=>[unquote 0]\n`[splice-unquote 0]\n;=>[splice-unquote 0]\n`(0 unquote 1)\n;=>(0 unquote 1)\n`(0 splice-unquote ())\n;=>(0 splice-unquote ())\n\n(let* (DEBUG-EVAL true) `nil)\n;/EVAL: nil.*\\nnil\n(let* (DEBUG-EVAL true) `7)\n;/EVAL: 7.*\\n7\n(let* (DEBUG-EVAL true) `a)\n;/EVAL: \\(quote a\\).*\\na\n(let* (DEBUG-EVAL true) `{\"a\" b})\n;/EVAL: \\(quote \\{\"a\" b\\}\\).*\\n\\{\"a\" b\\}\n(let* (DEBUG-EVAL true) `())\n;/EVAL: \\(\\).*\\n\\(\\)\n(let* (DEBUG-EVAL true) `(a 2))\n;/EVAL: \\(cons \\(quote a\\) \\(cons 2 \\(\\)\\)\\).*\\n\\(a 2\\)\n(let* (DEBUG-EVAL true) `(~a 3))\n;/EVAL: \\(cons a \\(cons 3 \\(\\)\\)\\).*\\n\\(8 3\\)\n(let* (DEBUG-EVAL true) `(1 ~@c 3))\n;/EVAL: \\(cons 1 \\(concat c \\(cons 3 \\(\\)\\)\\)\\).*\\n\\(1 1 \"b\" \"d\" 3\\)\n(let* (DEBUG-EVAL true) `[])\n;/EVAL: \\(vec \\(\\)\\).*\\n\\[\\]\n"
  },
  {
    "path": "impls/tests/step8_macros.mal",
    "content": ";; Testing trivial macros\n(defmacro! one (fn* () 1))\n(one)\n;=>1\n(defmacro! two (fn* () 2))\n(two)\n;=>2\n\n;; Testing unless macros\n(defmacro! unless (fn* (pred a b) `(if ~pred ~b ~a)))\n(unless false 7 8)\n;=>7\n(unless true 7 8)\n;=>8\n(defmacro! unless2 (fn* (pred a b) (list 'if (list 'not pred) a b)))\n(unless2 false 7 8)\n;=>7\n(unless2 true 7 8)\n;=>8\n\n;; Testing evaluation of macro result\n(defmacro! identity (fn* (x) x))\n(let* (a 123) (identity a))\n;=>123\n\n;; Test that macros do not break empty list\n()\n;=>()\n\n;; Test that macros do not break quasiquote\n`(1)\n;=>(1)\n\n;>>> deferrable=True\n;;\n;; -------- Deferrable Functionality --------\n\n;; Testing non-macro function\n(not (= 1 1))\n;=>false\n;;; This should fail if it is a macro\n(not (= 1 2))\n;=>true\n\n;; Testing nth, first and rest functions\n\n(nth (list 1) 0)\n;=>1\n(nth (list 1 2) 1)\n;=>2\n(nth (list 1 2 nil) 2)\n;=>nil\n(def! x \"x\")\n(def! x (nth (list 1 2) 2))\nx\n;=>\"x\"\n\n(first (list))\n;=>nil\n(first (list 6))\n;=>6\n(first (list 7 8 9))\n;=>7\n\n(rest (list))\n;=>()\n(rest (list 6))\n;=>()\n(rest (list 7 8 9))\n;=>(8 9)\n\n\n;; Testing cond macro\n\n(cond)\n;=>nil\n(cond true 7)\n;=>7\n(cond false 7)\n;=>nil\n(cond true 7 true 8)\n;=>7\n(cond false 7 true 8)\n;=>8\n(cond false 7 false 8 \"else\" 9)\n;=>9\n(cond false 7 (= 2 2) 8 \"else\" 9)\n;=>8\n(cond false 7 false 8 false 9)\n;=>nil\n\n;; Testing EVAL in let*\n\n(let* (x (cond false \"no\" true \"yes\")) x)\n;=>\"yes\"\n\n\n;; Testing nth, first, rest with vectors\n\n(nth [1] 0)\n;=>1\n(nth [1 2] 1)\n;=>2\n(nth [1 2 nil] 2)\n;=>nil\n(def! x \"x\")\n(def! x (nth [1 2] 2))\nx\n;=>\"x\"\n\n(first [])\n;=>nil\n(first nil)\n;=>nil\n(first [10])\n;=>10\n(first [10 11 12])\n;=>10\n(rest [])\n;=>()\n(rest nil)\n;=>()\n(rest [10])\n;=>()\n(rest [10 11 12])\n;=>(11 12)\n(rest (cons 10 [11 12]))\n;=>(11 12)\n\n;; Testing EVAL in vector let*\n\n(let* [x (cond false \"no\" true \"yes\")] x)\n;=>\"yes\"\n\n;; Test return value of defmacro!\n(let* [m (defmacro! _ (fn* [] 1))] (macro? m))\n;=>true\n\n;>>> soft=True\n;>>> optional=True\n;;\n;; ------- Optional Functionality --------------\n;; ------- (Not needed for self-hosting) -------\n\n;; Test that macros use closures\n(def! x 2)\n(defmacro! a (fn* [] x))\n(a)\n;=>2\n(let* (x 3) (a))\n;=>2\n\n(let* (DEBUG-EVAL true) (unless x foo (- 4 3)))\n;/EVAL: \\(if x \\(- 4 3\\) foo\\).*\\n1\n(let* (DEBUG-EVAL true) (unless2 x foo (- 4 3)))\n;/EVAL: \\(if \\(not x\\) foo \\(- 4 3\\)\\).*\\n1\n(let* (DEBUG-EVAL true) (cond x (- 4 3) foo bar))\n;/EVAL: \\(if x \\(- 4 3\\) \\(cond foo bar\\)\\).*\\n1\n"
  },
  {
    "path": "impls/tests/step9_try.mal",
    "content": ";;\n;; Testing throw\n\n(throw \"err1\")\n;/.*([Ee][Rr][Rr][Oo][Rr]|[Ee]xception).*err1.*\n\n;;\n;; Testing try*/catch*\n\n(try* 123 (catch* e 456))\n;=>123\n\n(try* abc (catch* exc (prn \"exc is:\" exc)))\n;/\"exc is:\" \"'?abc'? not found\"\n;=>nil\n\n(try* (abc 1 2) (catch* exc (prn \"exc is:\" exc)))\n;/\"exc is:\" \"'?abc'? not found\"\n;=>nil\n\n;; Make sure error from core can be caught\n(try* (nth () 1) (catch* exc (prn \"exc is:\" exc)))\n;/\"exc is:\".*(length|range|[Bb]ounds|beyond).*\n;=>nil\n\n;; Make sure no double eval (no TCO from try block)\n(try* (list 1) (catch* exc (prn \"exc is:\" exc)))\n;=>(1)\n\n(try* (throw \"my exception\") (catch* exc (do (prn \"exc:\" exc) 7)))\n;/\"exc:\" \"my exception\"\n;=>7\n\n;; Test that exception handlers get restored correctly\n(try* (do (try* \"t1\" (catch* e \"c1\")) (throw \"e1\")) (catch* e \"c2\"))\n;=>\"c2\"\n(try* (try* (throw \"e1\") (catch* e (throw \"e2\"))) (catch* e \"c2\"))\n;=>\"c2\"\n\n;;; Test that throw is a function:\n(try* (map throw (list \"my err\")) (catch* exc exc))\n;=>\"my err\"\n\n\n;;\n;; Testing builtin functions\n\n(symbol? 'abc)\n;=>true\n(symbol? \"abc\")\n;=>false\n\n(nil? nil)\n;=>true\n(nil? false)\n;=>false\n(nil? true)\n;=>false\n(nil? ())\n;=>false\n(nil? 0)\n;=>false\n\n(true? nil)\n;=>false\n(true? false)\n;=>false\n(true? true)\n;=>true\n(true? 1)\n;=>false\n(true? true?)\n;=>false\n\n(false? nil)\n;=>false\n(false? false)\n;=>true\n(false? true)\n;=>false\n(false? \"\")\n;=>false\n(false? 0)\n;=>false\n(false? ())\n;=>false\n(false? [])\n;=>false\n(false? {})\n;=>false\n(false? nil)\n;=>false\n\n;; Testing apply function with core functions\n(apply + (list 2 3))\n;=>5\n(apply + 4 (list 5))\n;=>9\n(apply prn (list 1 2 \"3\" (list)))\n;/1 2 \"3\" \\(\\)\n;=>nil\n(apply prn 1 2 (list \"3\" (list)))\n;/1 2 \"3\" \\(\\)\n;=>nil\n(apply list (list))\n;=>()\n(apply symbol? (list (quote two)))\n;=>true\n\n;; Testing apply function with user functions\n(apply (fn* (a b) (+ a b)) (list 2 3))\n;=>5\n(apply (fn* (a b) (+ a b)) 4 (list 5))\n;=>9\n\n;; Testing apply function with macros\n(defmacro! m (fn* [a b] (+ a b)))\n(apply m (list 2 3))\n;=>5\n(apply m 4 (list 5))\n;=>9\n\n;; Testing map function\n(def! nums (list 1 2 3))\n(def! double (fn* (a) (* 2 a)))\n(double 3)\n;=>6\n(map double nums) \n;=>(2 4 6)\n(map (fn* (x) (symbol? x)) (list 1 (quote two) \"three\"))\n;=>(false true false)\n(= () (map str ()))\n;=>true\n\n;>>> deferrable=True\n;;\n;; ------- Deferrable Functionality ----------\n;; ------- (Needed for self-hosting) -------\n\n;; Test catch of reader errors\n(try* (eval (read-string \"(+ 1\")) (catch* e (prn :e e)))\n;/.*(EOF|end of input|unbalanced).*\n(try* (eval (read-string \"[+ 1\")) (catch* e (prn :e e)))\n;/.*(EOF|end of input|unbalanced).*\n(try* (eval (read-string \"{:a 1\")) (catch* e (prn :e e)))\n;/.*(EOF|end of input|unbalanced).*\n\n;; Testing symbol and keyword functions\n(symbol? :abc)\n;=>false\n(symbol? 'abc)\n;=>true\n(symbol? \"abc\")\n;=>false\n(symbol? (symbol \"abc\"))\n;=>true\n(keyword? :abc)\n;=>true\n(keyword? 'abc)\n;=>false\n(keyword? \"abc\")\n;=>false\n(keyword? \"\")\n;=>false\n(keyword? (keyword \"abc\"))\n;=>true\n\n(symbol \"abc\")\n;=>abc\n(keyword \"abc\")\n;=>:abc\n\n;; Testing sequential? function\n\n(sequential? (list 1 2 3))\n;=>true\n(sequential? [15])\n;=>true\n(sequential? sequential?)\n;=>false\n(sequential? nil)\n;=>false\n(sequential? \"abc\")\n;=>false\n\n;; Testing apply function with core functions and arguments in vector\n(apply + 4 [5])\n;=>9\n(apply prn 1 2 [\"3\" 4])\n;/1 2 \"3\" 4\n;=>nil\n(apply list [])\n;=>()\n;; Testing apply function with user functions and arguments in vector\n(apply (fn* (a b) (+ a b)) [2 3])\n;=>5\n(apply (fn* (a b) (+ a b)) 4 [5])\n;=>9\n\n\n;; Testing map function with vectors\n(map (fn* (a) (* 2 a)) [1 2 3])\n;=>(2 4 6)\n\n(map (fn* [& args] (list? args)) [1 2])\n;=>(true true)\n\n;; Testing vector functions\n\n(vector? [10 11])\n;=>true\n(vector? '(12 13))\n;=>false\n(vector 3 4 5)\n;=>[3 4 5]\n(= [] (vector))\n;=>true\n\n(map? {})\n;=>true\n(map? '())\n;=>false\n(map? [])\n;=>false\n(map? 'abc)\n;=>false\n(map? :abc)\n;=>false\n\n\n;;\n;; Testing hash-maps\n(hash-map \"a\" 1)\n;=>{\"a\" 1}\n\n{\"a\" 1}\n;=>{\"a\" 1}\n\n(assoc {} \"a\" 1)\n;=>{\"a\" 1}\n\n(get (assoc (assoc {\"a\" 1 } \"b\" 2) \"c\" 3) \"a\")\n;=>1\n\n(def! hm1 (hash-map))\n;=>{}\n\n(map? hm1)\n;=>true\n(map? 1)\n;=>false\n(map? \"abc\")\n;=>false\n\n(get nil \"a\")\n;=>nil\n\n(get hm1 \"a\")\n;=>nil\n\n(contains? hm1 \"a\")\n;=>false\n\n(def! hm2 (assoc hm1 \"a\" 1))\n;=>{\"a\" 1}\n\n(get hm1 \"a\")\n;=>nil\n\n(contains? hm1 \"a\")\n;=>false\n\n(get hm2 \"a\")\n;=>1\n\n(contains? hm2 \"a\")\n;=>true\n\n\n;;; TODO: fix. Clojure returns nil but this breaks mal impl\n(keys hm1)\n;=>()\n(= () (keys hm1))\n;=>true\n\n(keys hm2)\n;=>(\"a\")\n\n(keys {\"1\" 1})\n;=>(\"1\")\n\n;;; TODO: fix. Clojure returns nil but this breaks mal impl\n(vals hm1)\n;=>()\n(= () (vals hm1))\n;=>true\n\n(vals hm2)\n;=>(1)\n\n(count (keys (assoc hm2 \"b\" 2 \"c\" 3)))\n;=>3\n\n;; Testing keywords as hash-map keys\n(get {:abc 123} :abc)\n;=>123\n(contains? {:abc 123} :abc)\n;=>true\n(contains? {:abcd 123} :abc)\n;=>false\n(assoc {} :bcd 234)\n;=>{:bcd 234}\n(keyword? (nth (keys {:abc 123 :def 456}) 0))\n;=>true\n(keyword? (nth (vals {\"a\" :abc \"b\" :def}) 0))\n;=>true\n\n;; Testing whether assoc updates properly\n(def! hm4 (assoc {:a 1 :b 2} :a 3 :c 1))\n(get hm4 :a)\n;=>3\n(get hm4 :b)\n;=>2\n(get hm4 :c)\n;=>1\n\n;; Testing nil as hash-map values\n(contains? {:abc nil} :abc)\n;=>true\n(assoc {} :bcd nil)\n;=>{:bcd nil}\n\n;;\n;; Additional str and pr-str tests\n\n(str \"A\" {:abc \"val\"} \"Z\")\n;=>\"A{:abc val}Z\"\n\n(str true \".\" false \".\" nil \".\" :keyw \".\" 'symb)\n;=>\"true.false.nil.:keyw.symb\"\n\n(pr-str \"A\" {:abc \"val\"} \"Z\")\n;=>\"\\\"A\\\" {:abc \\\"val\\\"} \\\"Z\\\"\"\n\n(pr-str true \".\" false \".\" nil \".\" :keyw \".\" 'symb)\n;=>\"true \\\".\\\" false \\\".\\\" nil \\\".\\\" :keyw \\\".\\\" symb\"\n\n(def! s (str {:abc \"val1\" :def \"val2\"}))\n(cond (= s \"{:abc val1 :def val2}\") true (= s \"{:def val2 :abc val1}\") true)\n;=>true\n\n(def! p (pr-str {:abc \"val1\" :def \"val2\"}))\n(cond (= p \"{:abc \\\"val1\\\" :def \\\"val2\\\"}\") true (= p \"{:def \\\"val2\\\" :abc \\\"val1\\\"}\") true)\n;=>true\n\n;;\n;; Test extra function arguments as Mal List (bypassing TCO with apply)\n(apply (fn* (& more) (list? more)) [1 2 3])\n;=>true\n(apply (fn* (& more) (list? more)) [])\n;=>true\n(apply (fn* (a & more) (list? more)) [1])\n;=>true\n\n;>>> soft=True\n;>>> optional=True\n;;\n;; ------- Optional Functionality --------------\n;; ------- (Not needed for self-hosting) -------\n\n\n;; Testing throwing a hash-map\n(throw {:msg \"err2\"})\n;/.*([Ee][Rr][Rr][Oo][Rr]|[Ee]xception).*msg.*err2.*\n\n;;;TODO: fix so long lines don't trigger ANSI escape codes ;;;(try*\n;;;(try* (throw [\"data\" \"foo\"]) (catch* exc (do (prn \"exc is:\" exc) 7))) ;;;;\n;;;; \"exc is:\" [\"data\" \"foo\"] ;;;;=>7\n;;;;=>7\n\n;;\n;; Testing try* without catch*\n(try* xyz)\n;/.*'?xyz'? not found.*\n\n;;\n;; Testing throwing non-strings\n(try* (throw (list 1 2 3)) (catch* exc (do (prn \"err:\" exc) 7)))\n;/\"err:\" \\(1 2 3\\)\n;=>7\n\n;;\n;; Testing dissoc\n(def! hm3 (assoc hm2 \"b\" 2))\n(count (keys hm3))\n;=>2\n(count (vals hm3))\n;=>2\n(dissoc hm3 \"a\")\n;=>{\"b\" 2}\n(dissoc hm3 \"a\" \"b\")\n;=>{}\n(dissoc hm3 \"a\" \"b\" \"c\")\n;=>{}\n(count (keys hm3))\n;=>2\n\n(dissoc {:cde 345 :fgh 456} :cde)\n;=>{:fgh 456}\n(dissoc {:cde nil :fgh 456} :cde)\n;=>{:fgh 456}\n\n;;\n;; Testing equality of hash-maps\n(= {} {})\n;=>true\n(= {} (hash-map))\n;=>true\n(= {:a 11 :b 22} (hash-map :b 22 :a 11))\n;=>true\n(= {:a 11 :b [22 33]} (hash-map :b [22 33] :a 11))\n;=>true\n(= {:a 11 :b {:c 33}} (hash-map :b {:c 33} :a 11))\n;=>true\n(= {:a 11 :b 22} (hash-map :b 23 :a 11))\n;=>false\n(= {:a 11 :b 22} (hash-map :a 11))\n;=>false\n(= {:a [11 22]} {:a (list 11 22)})\n;=>true\n(= {:a 11 :b 22} (list :a 11 :b 22))\n;=>false\n(= {} [])\n;=>false\n(= [] {})\n;=>false\n\n(keyword :abc)\n;=>:abc\n(keyword? (first (keys {\":abc\" 123 \":def\" 456})))\n;=>false\n\n;; Testing that hashmaps don't alter function ast\n(def! bar (fn* [a] {:foo (get a :foo)}))\n(bar {:foo (fn* [x] x)})\n(bar {:foo 3})\n;=>{:foo 3}\n;; shouldn't give an error\n\n;; Keywords and strings must be distinct map keys.\n(get {\"abc\" 1} :abc)\n;=>nil\n(get {:abc 1} \"abc\")\n;=>nil\n(contains? {\"abc\" 1} :abc)\n;=>false\n(contains? {:abc 1} \"abc\")\n;=>false\n(dissoc {\"abc\" 1 :abc 1} :abc)\n;=>{\"abc\" 1}\n(dissoc {\"abc\" 1 :abc 1} \"abc\")\n;=>{:abc 1}\n\n;; Map updates must not create duplicate keys.\n{:a 1 :a 2}\n;=>{:a 2}\n(keys {:a 1 :a 2})\n;=>(:a)\n(hash-map :a 1 :a 2)\n;=>{:a 2}\n(keys (hash-map :a 1 :a 2))\n;=>(:a)\n(assoc {:a 1} :a 2)\n;=>{:a 2}\n(keys (assoc {:a 1} :a 2))\n;=>(:a)\n\n;; Assoc must not mutate the original map.\n(def! hm7 {:a 1})\n;=>{:a 1}\n(assoc hm7 :a 2)\n;=>{:a 2}\n(get hm7 :a)\n;=>1\n"
  },
  {
    "path": "impls/tests/stepA_mal.mal",
    "content": ";;;\n;;; See IMPL/tests/stepA_mal.mal for implementation specific\n;;; interop tests.\n;;;\n\n\n;;\n;; Testing readline\n(readline \"mal-user> \")\n\"hello\"\n;=>\"\\\"hello\\\"\"\n\n;;\n;; Testing *host-language*\n;;; each impl is different, but this should return false\n;;; rather than throwing an exception\n(= \"something bogus\" *host-language*)\n;=>false\n\n\n;>>> deferrable=True\n;;\n;; ------- Deferrable Functionality ----------\n;; ------- (Needed for self-hosting) -------\n\n;;\n;;\n;; Testing hash-map evaluation and atoms (i.e. an env)\n(def! e (atom {\"+\" +}))\n(swap! e assoc \"-\" -)\n( (get @e \"+\") 7 8)\n;=>15\n( (get @e \"-\") 11 8)\n;=>3\n(swap! e assoc \"foo\" (list))\n(get @e \"foo\")\n;=>()\n(swap! e assoc \"bar\" '(1 2 3))\n(get @e \"bar\")\n;=>(1 2 3)\n\n;; Testing for presence of optional functions\n(do (list time-ms string? number? seq conj meta with-meta fn?) nil)\n;=>nil\n\n(map symbol? '(nil false true))\n;=>(false false false)\n\n(def! add1 (fn* (x) (+ x 1)))\n\n;; Testing fn? function\n(fn? +)\n;=>true\n(fn? list?)\n;=>true\n(fn? add1)\n;=>true\n(fn? cond)\n;=>false\n(fn? \"+\")\n;=>false\n(fn? :+)\n;=>false\n\n;; Testing macro? function\n(macro? cond)\n;=>true\n(macro? +)\n;=>false\n(macro? add1)\n;=>false\n(macro? \"+\")\n;=>false\n(macro? :+)\n;=>false\n(macro? {})\n;=>false\n\n;; ------------------------------------------------------------------\n\n;>>> soft=True\n;>>> optional=True\n;;\n;; ------- Optional Functionality --------------\n;; ------- (Not needed for self-hosting) -------\n\n;; Testing metadata on functions\n\n;;\n;; Testing metadata on mal functions\n\n(meta (fn* (a) a))\n;=>nil\n\n(meta (with-meta (fn* (a) a) {\"b\" 1}))\n;=>{\"b\" 1}\n\n(meta (with-meta (fn* (a) a) \"abc\"))\n;=>\"abc\"\n\n(def! l-wm (with-meta (fn* (a) a) {\"b\" 2}))\n(meta l-wm)\n;=>{\"b\" 2}\n\n(meta (with-meta l-wm {\"new_meta\" 123}))\n;=>{\"new_meta\" 123}\n(meta l-wm)\n;=>{\"b\" 2}\n\n(def! f-wm (with-meta (fn* [a] (+ 1 a)) {\"abc\" 1}))\n(meta f-wm)\n;=>{\"abc\" 1}\n\n(meta (with-meta f-wm {\"new_meta\" 123}))\n;=>{\"new_meta\" 123}\n(meta f-wm)\n;=>{\"abc\" 1}\n\n(def! f-wm2 ^{\"abc\" 1} (fn* [a] (+ 1 a)))\n(meta f-wm2)\n;=>{\"abc\" 1}\n\n;; Meta of native functions should return nil (not fail)\n(meta +)\n;=>nil\n\n;;\n;; Make sure closures and metadata co-exist\n(def! gen-plusX (fn* (x) (with-meta (fn* (b) (+ x b)) {\"meta\" 1})))\n(def! plus7 (gen-plusX 7))\n(def! plus8 (gen-plusX 8))\n(plus7 8)\n;=>15\n(meta plus7)\n;=>{\"meta\" 1}\n(meta plus8)\n;=>{\"meta\" 1}\n(meta (with-meta plus7 {\"meta\" 2}))\n;=>{\"meta\" 2}\n(meta plus8)\n;=>{\"meta\" 1}\n\n;;\n;; Testing string? function\n(string? \"\")\n;=>true\n(string? 'abc)\n;=>false\n(string? \"abc\")\n;=>true\n(string? :abc)\n;=>false\n(string? (keyword \"abc\"))\n;=>false\n(string? 234)\n;=>false\n(string? nil)\n;=>false\n\n;; Testing number? function\n(number? 123)\n;=>true\n(number? -1)\n;=>true\n(number? nil)\n;=>false\n(number? false)\n;=>false\n(number? \"123\")\n;=>false\n\n\n;;\n;; Testing conj function\n(conj (list) 1)\n;=>(1)\n(conj (list 1) 2)\n;=>(2 1)\n(conj (list 2 3) 4)\n;=>(4 2 3)\n(conj (list 2 3) 4 5 6)\n;=>(6 5 4 2 3)\n(conj (list 1) (list 2 3))\n;=>((2 3) 1)\n\n(conj [] 1)\n;=>[1]\n(conj [1] 2)\n;=>[1 2]\n(conj [2 3] 4)\n;=>[2 3 4]\n(conj [2 3] 4 5 6)\n;=>[2 3 4 5 6]\n(conj [1] [2 3])\n;=>[1 [2 3]]\n\n;;\n;; Testing seq function\n(seq \"abc\")\n;=>(\"a\" \"b\" \"c\")\n(apply str (seq \"this is a test\"))\n;=>\"this is a test\"\n(seq '(2 3 4))\n;=>(2 3 4)\n(seq [2 3 4])\n;=>(2 3 4)\n\n(seq \"\")\n;=>nil\n(seq '())\n;=>nil\n(seq [])\n;=>nil\n(seq nil)\n;=>nil\n\n;;\n;; Testing metadata on collections\n\n(meta [1 2 3])\n;=>nil\n\n(with-meta [1 2 3] {\"a\" 1})\n;=>[1 2 3]\n\n(meta (with-meta [1 2 3] {\"a\" 1}))\n;=>{\"a\" 1}\n\n(vector? (with-meta [1 2 3] {\"a\" 1}))\n;=>true\n\n(meta (with-meta [1 2 3] \"abc\"))\n;=>\"abc\"\n\n(with-meta [] \"abc\")\n;=>[]\n\n(meta (with-meta (list 1 2 3) {\"a\" 1}))\n;=>{\"a\" 1}\n\n(list? (with-meta (list 1 2 3) {\"a\" 1}))\n;=>true\n\n(with-meta (list) {\"a\" 1})\n;=>()\n\n(empty? (with-meta (list) {\"a\" 1}))\n;=>true\n\n(meta (with-meta {\"abc\" 123} {\"a\" 1}))\n;=>{\"a\" 1}\n\n(map? (with-meta {\"abc\" 123} {\"a\" 1}))\n;=>true\n\n(with-meta {} {\"a\" 1})\n;=>{}\n\n(def! l-wm (with-meta [4 5 6] {\"b\" 2}))\n;=>[4 5 6]\n(meta l-wm)\n;=>{\"b\" 2}\n\n(meta (with-meta l-wm {\"new_meta\" 123}))\n;=>{\"new_meta\" 123}\n(meta l-wm)\n;=>{\"b\" 2}\n\n;;\n;; Testing metadata on mal and builtin functions\n(fn? ^{\"ismacro\" true} (fn* () 0))\n;=>true\n(meta +)\n;=>nil\n(def! f-wm3 ^{\"def\" 2} +)\n(meta f-wm3)\n;=>{\"def\" 2}\n(meta +)\n;=>nil\n\n;; Metadata should not break equality.\n(= [1] ^2 [1])\n;=>true\n\n(= '(1) ^2 '(1))\n;=>true\n\n(= {\"a\" 1} ^2 {\"a\" 1})\n;=>true\n\n(= '(1) ^2 [1])\n;=>true\n\n;; Loading sumdown from computations.mal\n(load-file \"../tests/computations.mal\")\n;=>nil\n\n;;\n;; Testing time-ms function\n(def! start-time (time-ms))\n(= start-time 0)\n;=>false\n(sumdown 10) ; Waste some time\n;=>55\n(> (time-ms) start-time)\n;=>true\n\n;;\n;; Test that defining a macro does not mutate an existing function.\n(def! f (fn* [x] (number? x)))\n(defmacro! m f)\n(f (+ 1 1))\n;=>true\n(m (+ 1 1))\n;=>false\n"
  },
  {
    "path": "impls/tests/test.txt",
    "content": "A line of text\n"
  },
  {
    "path": "impls/tests/travis_trigger.sh",
    "content": "#!/usr/bin/env bash\n\n# Reference: https://docs.travis-ci.com/user/triggering-builds/\n\nset -e\n\ndie() { echo \"${*}\"; exit 1; }\nusage() {\n  [ \"${*}\" ] && echo >&2 -e \"${*}\\n\"\n  echo \"Usage: $0 REPO BRANCH [VAR=VAL]...\n\n  Authorization:\n\n    If you have the travis program installed then it will be called\n    to get an API token (you need to have done 'travis login --org'\n    in the past). Alternately you can explicity pass a token using\n    the TRAVIS_TOKEN environment variable. You can see your API\n    token at https://travis-ci.org/account/preferences.\n\n  Travis .org vs .com:\n\n    By default 'api.travis-ci.org' is used for API calls. This can\n    be overridden by setting TRAVIS_HOST=\"api.travis-ci.com\"\n\n  Examples:\n\n    Trigger build/test in self-hosted mode:\n      $0 REPO BRANCH DO_SELF_HOST=1\n\n    Trigger build/test with stop on soft failures:\n      $0 REPO BRANCH DO_HARD=1\n\n    Trigger build/test using regress mode on stepA:\n      $0 REPO BRANCH REGRESS=1 STEP=stepA\n\n    Trigger build/test using regress mode on all steps:\n      $0 REPO BRANCH REGRESS=1\n  \" | sed 's/^  //' >&2\n\n  exit 2\n}\n\nTRAVIS_TOKEN=\"${TRAVIS_TOKEN:-}\" # default to travis program\nTRAVIS_HOST=\"${TRAVIS_HOST:-api.travis-ci.org}\"\n\nREPO=\"${1}\"; shift || usage \"REPO required\"\nBRANCH=\"${1}\"; shift || usage \"BRANCH required\"\nVARS=\"${*}\"\n\nrepo=\"${REPO/\\//%2F}\"\nvars=\"\"\n[ \"${VARS}\" ] && vars=\"\\\"${VARS// /\\\", \\\"}\\\"\"\n\nbody=\"{\n  \\\"request\\\": {\n    \\\"message\\\": \\\"Manual build. Settings: ${VARS}\\\",\n    \\\"branch\\\":\\\"${BRANCH}\\\",\n    \\\"config\\\": {\n      \\\"env\\\": {\n        \\\"global\\\": [${vars}]\n      }\n    }\n  }\n}\"\n\nif [ -z \"${TRAVIS_TOKEN}\" ]; then\n    which travis >/dev/null \\\n        || die \"TRAVIS_TOKEN not set and travis command not found\"\n    TRAVIS_TOKEN=\"$(travis token --org --no-interactive)\"\nfi\n\ncurl -X POST \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json\" \\\n  -H \"Travis-API-Version: 3\" \\\n  -H \"Authorization: token ${TRAVIS_TOKEN}\" \\\n  -d \"$body\" \\\n  \"https://${TRAVIS_HOST}/repo/${repo}/requests\"\n"
  },
  {
    "path": "impls/ts/.gitignore",
    "content": "node_modules/\n\nnpm-debug.log\n\n*.js\n"
  },
  {
    "path": "impls/ts/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++ libreadline-dev nodejs npm\n\nENV NPM_CONFIG_CACHE /mal/.npm\n"
  },
  {
    "path": "impls/ts/Makefile",
    "content": "STEPS = step0_repl step1_read_print step2_eval step3_env \\\n\tstep4_if_fn_do step5_tco step6_file step7_quote \\\n\tstep8_macros step9_try stepA_mal\n\nall: ts\n\nnode_modules:\n\tnpm install\n\nstep%.js: node_modules types.ts reader.ts printer.ts env.ts core.ts step%.ts\n\t./node_modules/.bin/tsc -p ./\n\n\n.PHONY: ts clean\n\nts: $(foreach s,$(STEPS),$(s).js)\n\nclean:\n\trm -f *.js\n"
  },
  {
    "path": "impls/ts/core.ts",
    "content": "import * as fs from \"fs\";\n\nimport { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalSymbol, MalFunction, MalNil, MalList, MalVector, MalBoolean, MalNumber, MalString, MalKeyword, MalHashMap, MalAtom, equals, isSeq } from \"./types\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\nexport const ns: Map<string, MalFunction> = (() => {\n    const ns: { [symbol: string]: typeof MalFunction.prototype.func; } = {\n        \"=\"(a: MalType, b: MalType): MalBoolean {\n            return new MalBoolean(equals(a, b));\n        },\n        throw(v: MalType): MalType {\n            throw v;\n        },\n\n        \"nil?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Nil);\n        },\n        \"true?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Boolean && v.v);\n        },\n        \"false?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Boolean && !v.v);\n        },\n        \"string?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.String);\n        },\n        symbol(v: MalType) {\n            if (v.type !== Node.String) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: string`);\n            }\n            return MalSymbol.get(v.v);\n        },\n        \"symbol?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Symbol);\n        },\n        keyword(v: MalType) {\n            if (v.type === Node.Keyword) {\n                return v;\n            }\n            if (v.type !== Node.String) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: string`);\n            }\n            return MalKeyword.get(v.v);\n        },\n        \"keyword?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Keyword);\n        },\n        \"number?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Number);\n        },\n        \"fn?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Function && !v.isMacro);\n        },\n        \"macro?\"(v: MalType) {\n            return new MalBoolean(v.type === Node.Function && v.isMacro);\n        },\n\n        \"pr-str\"(...args: MalType[]): MalString {\n            return new MalString(args.map(v => prStr(v, true)).join(\" \"));\n        },\n        \"str\"(...args: MalType[]): MalString {\n            return new MalString(args.map(v => prStr(v, false)).join(\"\"));\n        },\n        prn(...args: MalType[]): MalNil {\n            const str = args.map(v => prStr(v, true)).join(\" \");\n            console.log(str);\n            return MalNil.instance;\n        },\n        println(...args: MalType[]): MalNil {\n            const str = args.map(v => prStr(v, false)).join(\" \");\n            console.log(str);\n            return MalNil.instance;\n        },\n        \"read-string\"(v: MalType) {\n            if (v.type !== Node.String) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: string`);\n            }\n            return readStr(v.v);\n        },\n        readline(v: MalType) {\n            if (v.type !== Node.String) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: string`);\n            }\n\n            const ret = readline(v.v);\n            if (ret == null) {\n                return MalNil.instance;\n            }\n\n            return new MalString(ret);\n        },\n        slurp(v: MalType) {\n            if (v.type !== Node.String) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: string`);\n            }\n            const content = fs.readFileSync(v.v, \"utf-8\");\n            return new MalString(content);\n        },\n\n        \"<\"(a: MalType, b: MalType): MalBoolean {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalBoolean(a.v < b.v);\n        },\n        \"<=\"(a: MalType, b: MalType): MalBoolean {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalBoolean(a.v <= b.v);\n        },\n        \">\"(a: MalType, b: MalType): MalBoolean {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalBoolean(a.v > b.v);\n        },\n        \">=\"(a: MalType, b: MalType): MalBoolean {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalBoolean(a.v >= b.v);\n        },\n        \"+\"(a: MalType, b: MalType): MalNumber {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalNumber(a.v + b.v);\n        },\n        \"-\"(a: MalType, b: MalType): MalNumber {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalNumber(a.v - b.v);\n        },\n        \"*\"(a: MalType, b: MalType): MalNumber {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalNumber(a.v * b.v);\n        },\n        \"/\"(a: MalType, b: MalType): MalNumber {\n            if (a.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${a.type}, expected: number`);\n            }\n            if (b.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: number`);\n            }\n\n            return new MalNumber(a.v / b.v);\n        },\n        \"time-ms\"() {\n            return new MalNumber(Date.now());\n        },\n\n        list(...args: MalType[]): MalList {\n            return new MalList(args);\n        },\n        \"list?\"(v: MalType): MalBoolean {\n            return new MalBoolean(v.type === Node.List);\n        },\n        vector(...args: MalType[]): MalVector {\n            return new MalVector(args);\n        },\n        \"vector?\"(v: MalType): MalBoolean {\n            return new MalBoolean(v.type === Node.Vector);\n        },\n        \"hash-map\"(...args: MalType[]) {\n            return new MalHashMap(args);\n        },\n        \"map?\"(v: MalType): MalBoolean {\n            return new MalBoolean(v.type === Node.HashMap);\n        },\n        assoc(v: MalType, ...args: MalType[]) {\n            if (v.type !== Node.HashMap) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);\n            }\n            return v.assoc(args);\n        },\n        dissoc(v: MalType, ...args: MalType[]) {\n            if (v.type !== Node.HashMap) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);\n            }\n            return v.dissoc(args);\n        },\n        get(v: MalType, key: MalType) {\n            if (v.type === Node.Nil) {\n                return MalNil.instance;\n            }\n            if (v.type !== Node.HashMap) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);\n            }\n            if (key.type !== Node.String && key.type !== Node.Keyword) {\n                throw new Error(`unexpected symbol: ${key.type}, expected: string or keyword`);\n            }\n\n            return v.get(key) || MalNil.instance;\n        },\n        \"contains?\"(v: MalType, key: MalType) {\n            if (v.type === Node.Nil) {\n                return MalNil.instance;\n            }\n            if (v.type !== Node.HashMap) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);\n            }\n            if (key.type !== Node.String && key.type !== Node.Keyword) {\n                throw new Error(`unexpected symbol: ${key.type}, expected: string or keyword`);\n            }\n\n            return new MalBoolean(v.has(key));\n        },\n        keys(v: MalType) {\n            if (v.type !== Node.HashMap) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);\n            }\n\n            return new MalList([...v.keys()]);\n        },\n        vals(v: MalType) {\n            if (v.type !== Node.HashMap) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);\n            }\n\n            return new MalList([...v.vals()]);\n        },\n\n        \"sequential?\"(v: MalType) {\n            return new MalBoolean(isSeq(v));\n        },\n        cons(a: MalType, b: MalType) {\n            if (!isSeq(b)) {\n                throw new Error(`unexpected symbol: ${b.type}, expected: list or vector`);\n            }\n\n            return new MalList([a].concat(b.list));\n        },\n        concat(...args: MalType[]) {\n            const list = args\n                .map(arg => {\n                    if (!isSeq(arg)) {\n                        throw new Error(`unexpected symbol: ${arg.type}, expected: list or vector`);\n                    }\n                    return arg;\n                })\n                .reduce((p, c) => p.concat(c.list), [] as MalType[]);\n\n            return new MalList(list);\n        },\n        vec(a: MalType) {\n            switch (a.type) {\n                case Node.List:\n                    return new MalVector(a.list);\n                case Node.Vector:\n                    return a;\n            }\n            throw new Error(`unexpected symbol: ${a.type}, expected: list or vector`);\n        },\n\n        nth(list: MalType, idx: MalType) {\n            if (!isSeq(list)) {\n                throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);\n            }\n            if (idx.type !== Node.Number) {\n                throw new Error(`unexpected symbol: ${idx.type}, expected: number`);\n            }\n\n            const v = list.list[idx.v];\n            if (!v) {\n                throw new Error(\"nth: index out of range\");\n            }\n\n            return v;\n        },\n        first(v: MalType) {\n            if (v.type === Node.Nil) {\n                return MalNil.instance;\n            }\n            if (!isSeq(v)) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: list or vector`);\n            }\n\n            return v.list[0] || MalNil.instance;\n        },\n        rest(v: MalType) {\n            if (v.type === Node.Nil) {\n                return new MalList([]);\n            }\n            if (!isSeq(v)) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: list or vector`);\n            }\n\n            return new MalList(v.list.slice(1));\n        },\n        \"empty?\"(v: MalType): MalBoolean {\n            if (!isSeq(v)) {\n                return new MalBoolean(false);\n            }\n            return new MalBoolean(v.list.length === 0);\n        },\n        count(v: MalType): MalNumber {\n            if (isSeq(v)) {\n                return new MalNumber(v.list.length);\n            }\n            if (v.type === Node.Nil) {\n                return new MalNumber(0);\n            }\n            throw new Error(`unexpected symbol: ${v.type}`);\n        },\n        apply(f: MalType, ...list: MalType[]) {\n            if (f.type !== Node.Function) {\n                throw new Error(`unexpected symbol: ${f.type}, expected: function`);\n            }\n\n            const tail = list[list.length - 1];\n            if (!isSeq(tail)) {\n                throw new Error(`unexpected symbol: ${tail.type}, expected: list or vector`);\n            }\n            const args = list.slice(0, -1).concat(tail.list);\n            return f.func(...args);\n        },\n        map(f: MalType, list: MalType) {\n            if (f.type !== Node.Function) {\n                throw new Error(`unexpected symbol: ${f.type}, expected: function`);\n            }\n            if (!isSeq(list)) {\n                throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);\n            }\n\n            return new MalList(list.list.map(v => f.func(v)));\n        },\n\n        conj(list: MalType, ...args: MalType[]) {\n            switch (list.type) {\n                case Node.List:\n                    const newList = new MalList(list.list);\n                    args.forEach(arg => newList.list.unshift(arg));\n                    return newList;\n                case Node.Vector:\n                    return new MalVector([...list.list, ...args]);\n            }\n\n            throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);\n        },\n        seq(v: MalType) {\n            if (v.type === Node.List) {\n                if (v.list.length === 0) {\n                    return MalNil.instance;\n                }\n                return v;\n            }\n            if (v.type === Node.Vector) {\n                if (v.list.length === 0) {\n                    return MalNil.instance;\n                }\n                return new MalList(v.list);\n            }\n            if (v.type === Node.String) {\n                if (v.v.length === 0) {\n                    return MalNil.instance;\n                }\n                return new MalList(v.v.split(\"\").map(s => new MalString(s)));\n            }\n            if (v.type === Node.Nil) {\n                return MalNil.instance;\n            }\n\n            throw new Error(`unexpected symbol: ${v.type}, expected: list or vector or string`);\n        },\n\n        meta(v: MalType) {\n            return v.meta || MalNil.instance;\n        },\n        \"with-meta\"(v: MalType, m: MalType) {\n            return v.withMeta(m);\n        },\n        atom(v: MalType): MalAtom {\n            return new MalAtom(v);\n        },\n        \"atom?\"(v: MalType): MalBoolean {\n            return new MalBoolean(v.type === Node.Atom);\n        },\n        deref(v: MalType): MalType {\n            if (v.type !== Node.Atom) {\n                throw new Error(`unexpected symbol: ${v.type}, expected: atom`);\n            }\n            return v.v;\n        },\n        \"reset!\"(atom: MalType, v: MalType): MalType {\n            if (atom.type !== Node.Atom) {\n                throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);\n            }\n            atom.v = v;\n            return v;\n        },\n        \"swap!\"(atom: MalType, f: MalType, ...args: MalType[]): MalType {\n            if (atom.type !== Node.Atom) {\n                throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);\n            }\n            if (f.type !== Node.Function) {\n                throw new Error(`unexpected symbol: ${f.type}, expected: function`);\n            }\n            atom.v = f.func(...[atom.v].concat(args));\n            return atom.v;\n        },\n    };\n\n    const map : Map<string, MalFunction> = new Map<string, MalFunction>();\n    Object.keys(ns).forEach(key => map.set(key, MalFunction.fromBootstrap(ns[key])));\n    return map;\n})();\n"
  },
  {
    "path": "impls/ts/env.ts",
    "content": "import { MalType, MalSymbol, MalList } from \"./types\";\n\nexport class Env {\n    data: Map<string, MalType>;\n\n    constructor(public outer?: Env, binds: MalSymbol[] = [], exprts: MalType[] = []) {\n        this.data = new Map();\n\n        for (let i = 0; i < binds.length; i++) {\n            const bind : string = binds[i].v;\n            if (bind === \"&\") {\n                this.set(binds[i + 1].v, new MalList(exprts.slice(i)));\n                break;\n            }\n            this.set(bind, exprts[i]);\n        }\n    }\n\n    set(key: string, value: MalType): MalType {\n        this.data.set(key, value);\n        return value;\n    }\n\n    get(key: string): MalType | null {\n        const result : MalType | undefined = this.data.get(key);\n        if (result) {\n            return result;\n        } else if (this.outer) {\n            return this.outer.get(key);\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/node_readline.ts",
    "content": "import * as path from \"path\";\nimport * as koffi from \"koffi\";\nimport * as fs from \"fs\";\n\n// IMPORTANT: choose one\nconst RL_LIB = \"libreadline.so.8\";  // NOTE: libreadline is GPL\n// const RL_LIB = \"libedit.so.2\";\n\nconst HISTORY_FILE = path.join(process.env.HOME || \".\", \".mal-history\");\n\nlet rllib: any;\ntry {\n    rllib = koffi.load(RL_LIB);\n} catch (e) {\n    console.error('ERROR loading RL_LIB:', RL_LIB, e);\n    throw e;\n}\n\nconst readlineFunc = rllib.func('char *readline(char *)');\nconst addHistoryFunc = rllib.func('int add_history(char *)');\n\nlet rlHistoryLoaded = false;\n\nexport function readline(prompt?: string): string | null {\n    prompt = prompt || \"user> \";\n\n    if (!rlHistoryLoaded) {\n        rlHistoryLoaded = true;\n        let lines: string[] = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (let i = 0; i < lines.length; i++) {\n            if (lines[i]) { addHistoryFunc(lines[i]); }\n        }\n    }\n\n    const line = readlineFunc(prompt);\n    if (line) {\n        addHistoryFunc(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\n"
  },
  {
    "path": "impls/ts/package.json",
    "content": "{\n  \"name\": \"mal\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"description\": \"Make a Lisp (mal) language implemented in TypeScript\",\n  \"scripts\": {\n    \"build\": \"tsfmt -r && tsc -p ./\",\n    \"test\": \"npm run build && npm run test:step0 && npm run test:step1 && npm run test:step2 && npm run test:step3 && npm run test:step4 && npm run test:step5 && npm run test:step6 && npm run test:step7 && npm run test:step8 && npm run test:step9 && npm run test:stepA\",\n    \"test:step0\": \"cd .. && make 'test^ts^step0'\",\n    \"test:step1\": \"cd .. && make 'test^ts^step1'\",\n    \"test:step2\": \"cd .. && make 'test^ts^step2'\",\n    \"test:step3\": \"cd .. && make 'test^ts^step3'\",\n    \"test:step4\": \"cd .. && make 'test^ts^step4'\",\n    \"test:step5\": \"cd .. && make 'test^ts^step5'\",\n    \"test:step6\": \"cd .. && make 'test^ts^step6'\",\n    \"test:step7\": \"cd .. && make 'test^ts^step7'\",\n    \"test:step8\": \"cd .. && make 'test^ts^step8'\",\n    \"test:step9\": \"cd .. && make 'test^ts^step9'\",\n    \"test:stepA\": \"cd .. && make 'test^ts^stepA'\"\n  },\n  \"dependencies\": {\n    \"koffi\": \"^2.12.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^14.14.3\",\n    \"typescript\": \"^4.3.5\",\n    \"typescript-formatter\": \"^7.2.2\"\n  }\n}\n"
  },
  {
    "path": "impls/ts/printer.ts",
    "content": "import { Node, MalType } from \"./types\";\n\nexport function prStr(v: MalType, printReadably = true): string {\n    switch (v.type) {\n        case Node.List:\n            return `(${v.list.map(v => prStr(v, printReadably)).join(\" \")})`;\n        case Node.Vector:\n            return `[${v.list.map(v => prStr(v, printReadably)).join(\" \")}]`;\n        case Node.HashMap:\n            let result = \"{\";\n            for (const [key, value] of v.entries()) {\n                if (result !== \"{\") {\n                    result += \" \";\n                }\n                result += `${prStr(key, printReadably)} ${prStr(value, printReadably)}`;\n            }\n            result += \"}\";\n            return result;\n        case Node.Number:\n        case Node.Symbol:\n        case Node.Boolean:\n            return `${v.v}`;\n        case Node.String:\n            if (printReadably) {\n                const str = v.v\n                    .replace(/\\\\/g, \"\\\\\\\\\")\n                    .replace(/\"/g, '\\\\\"')\n                    .replace(/\\n/g, \"\\\\n\");\n                return `\"${str}\"`;\n            } else {\n                return v.v;\n            }\n        case Node.Nil:\n            return \"nil\";\n        case Node.Keyword:\n            return `:${v.v}`;\n        case Node.Function:\n            return \"#<function>\";\n        case Node.Atom:\n            return `(atom ${prStr(v.v, printReadably)})`;\n    }\n}\n"
  },
  {
    "path": "impls/ts/reader.ts",
    "content": "import { MalType, MalList, MalString, MalNumber, MalBoolean, MalNil, MalKeyword, MalSymbol, MalVector, MalHashMap } from \"./types\";\n\nclass Reader {\n    position = 0;\n\n    constructor(private tokens: string[]) { }\n\n    next(): string {\n        const ret = this.peek();\n        this.position += 1;\n        return ret;\n    }\n\n    peek(): string {\n        return this.tokens[this.position];\n    }\n}\n\nexport function readStr(input: string): MalType {\n    const tokens = tokenizer(input);\n    const reader = new Reader(tokens);\n    return readForm(reader);\n}\n\nfunction tokenizer(input: string): string[] {\n    const regexp = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)/g;\n    const tokens: string[] = [];\n    while (true) {\n        const matches = regexp.exec(input);\n        if (!matches) {\n            break;\n        }\n        const match = matches[1];\n        if (match === \"\") {\n            break;\n        }\n        if (match[0] !== \";\") {\n            tokens.push(match);\n        }\n    }\n\n    return tokens;\n}\n\nfunction readForm(reader: Reader): MalType {\n    const token = reader.peek();\n    switch (token) {\n        case \"(\":\n            return readList(reader);\n        case \"[\":\n            return readVector(reader);\n        case \"{\":\n            return readHashMap(reader);\n        case \"'\":\n            return readSymbol(\"quote\");\n        case \"`\":\n            return readSymbol(\"quasiquote\");\n        case \"~\":\n            return readSymbol(\"unquote\");\n        case \"~@\":\n            return readSymbol(\"splice-unquote\");\n        case \"@\":\n            return readSymbol(\"deref\");\n        case \"^\":\n            {\n                reader.next();\n                const sym = MalSymbol.get(\"with-meta\");\n                const target = readForm(reader);\n                return new MalList([sym, readForm(reader), target]);\n            }\n        default:\n            return readAtom(reader);\n    }\n\n    function readSymbol(name: string) {\n        reader.next();\n        const sym = MalSymbol.get(name);\n        const target = readForm(reader);\n        return new MalList([sym, target]);\n    }\n}\n\nfunction readList(reader: Reader): MalType {\n    return readParen(reader, MalList, \"(\", \")\");\n}\n\nfunction readVector(reader: Reader): MalType {\n    return readParen(reader, MalVector, \"[\", \"]\");\n}\n\nfunction readHashMap(reader: Reader): MalType {\n    return readParen(reader, MalHashMap, \"{\", \"}\");\n}\n\nfunction readParen(reader: Reader, ctor: { new (list: MalType[]): MalType; }, open: string, close: string): MalType {\n    const token = reader.next(); // drop open paren\n    if (token !== open) {\n        throw new Error(`unexpected token ${token}, expected ${open}`);\n    }\n    const list: MalType[] = [];\n    while (true) {\n        const next = reader.peek();\n        if (next === close) {\n            break;\n        } else if (!next) {\n            throw new Error(\"unexpected EOF\");\n        }\n        list.push(readForm(reader));\n    }\n    reader.next(); // drop close paren\n\n    return new ctor(list);\n}\n\nfunction readAtom(reader: Reader): MalType {\n    const token = reader.next();\n    if (token.match(/^-?[0-9]+$/)) {\n        const v = parseInt(token, 10);\n        return new MalNumber(v);\n    }\n    if (token.match(/^-?[0-9]\\.[0-9]+$/)) {\n        const v = parseFloat(token);\n        return new MalNumber(v);\n    }\n    if (token.match(/^\"(?:\\\\.|[^\\\\\"])*\"$/)) {\n        const v = token.slice(1, token.length - 1)\n            .replace(/\\\\(.)/g, (_, c: string) => c == 'n' ? '\\n' : c)\n        return new MalString(v);\n    }\n    if (token[0] === '\"') {\n        throw new Error(\"expected '\\\"', got EOF\");\n    }\n    if (token[0] === \":\") {\n        return MalKeyword.get(token.substr(1));\n    }\n    switch (token) {\n        case \"nil\":\n            return MalNil.instance;\n        case \"true\":\n            return new MalBoolean(true);\n        case \"false\":\n            return new MalBoolean(false);\n    }\n\n    return MalSymbol.get(token);\n}\n"
  },
  {
    "path": "impls/ts/run",
    "content": "#!/usr/bin/env bash\nexec node $(dirname $0)/${STEP:-stepA_mal}.js \"${@}\"\n"
  },
  {
    "path": "impls/ts/step0_repl.ts",
    "content": "import { readline } from \"./node_readline\";\n\n// READ\nfunction read(str: string): any {\n    // TODO\n    return str;\n}\n\n// EVAL\nfunction evalMal(ast: any, _env?: any): any {\n    // TODO\n    return ast;\n}\n\n// PRINT\nfunction print(exp: any): string {\n    // TODO\n    return exp;\n}\n\nfunction rep(str: string): string {\n    // TODO\n    return print(evalMal(read(str)));\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    console.log(rep(line));\n}\n"
  },
  {
    "path": "impls/ts/step1_read_print.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { MalType } from \"./types\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\n// EVAL\nfunction evalMal(ast: any, _env?: any): any {\n    // TODO\n    return ast;\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nfunction rep(str: string): string {\n    return print(evalMal(read(str)));\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        const err: Error = e;\n        console.error(err.message);\n    }\n}\n"
  },
  {
    "path": "impls/ts/step2_eval.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalNumber, MalVector, MalHashMap, MalFunction } from \"./types\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\ninterface MalEnvironment {\n    [key: string]: MalFunction;\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: MalEnvironment): MalType {\n    // console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f = env[ast.v];\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n    }\n    if (ast.list.length === 0) {\n        return ast;\n    }\n    const f : MalType = evalMal(ast.list[0], env);\n    if (f.type !== Node.Function) {\n        throw new Error(`unexpected token: ${f.type}, expected: function`);\n    }\n    const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n    return f.func(...args);\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv: MalEnvironment = {\n    \"+\": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v + b!.v)),\n    \"-\": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v - b!.v)),\n    \"*\": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v * b!.v)),\n    \"/\": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v / b!.v)),\n};\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        const err: Error = e;\n        console.error(err.message);\n    }\n}\n"
  },
  {
    "path": "impls/ts/step3_env.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalNumber, MalVector, MalHashMap, MalFunction, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n    }\n    if (ast.list.length === 0) {\n        return ast;\n    }\n    const first = ast.list[0];\n    switch (first.type) {\n        case Node.Symbol:\n            switch (first.v) {\n                case \"def!\": {\n                    const [, key, value] = ast.list;\n                    if (key.type !== Node.Symbol) {\n                        throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                    }\n                    if (!value) {\n                        throw new Error(`unexpected syntax`);\n                    }\n                    return env.set(key.v, evalMal(value, env));\n                }\n                case \"let*\": {\n                    let letEnv = new Env(env);\n                    const pairs = ast.list[1];\n                    if (!isSeq(pairs)) {\n                        throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                    }\n                    const list = pairs.list;\n                    for (let i = 0; i < list.length; i += 2) {\n                        const key = list[i];\n                        const value = list[i + 1];\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!key || !value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n\n                        letEnv.set(key.v, evalMal(value, letEnv));\n                    }\n                    return evalMal(ast.list[2], letEnv);\n                }\n            }\n    }\n    const f : MalType = evalMal(first, env);\n    if (f.type !== Node.Function) {\n        throw new Error(`unexpected token: ${f.type}, expected: function`);\n    }\n    const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n    return f.func(...args);\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\nreplEnv.set(\"+\", MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v + b!.v)));\nreplEnv.set(\"-\", MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v - b!.v)));\nreplEnv.set(\"*\", MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v * b!.v)));\nreplEnv.set(\"/\", MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v / b!.v)));\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        const err: Error = e;\n        console.error(err.message);\n    }\n}\n"
  },
  {
    "path": "impls/ts/step4_if_fn_do.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalNil, MalVector, MalHashMap, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n    }\n    if (ast.list.length === 0) {\n        return ast;\n    }\n    const first = ast.list[0];\n    switch (first.type) {\n        case Node.Symbol:\n            switch (first.v) {\n                case \"def!\": {\n                    const [, key, value] = ast.list;\n                    if (key.type !== Node.Symbol) {\n                        throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                    }\n                    if (!value) {\n                        throw new Error(`unexpected syntax`);\n                    }\n                    return env.set(key.v, evalMal(value, env));\n                }\n                case \"let*\": {\n                    let letEnv = new Env(env);\n                    const pairs = ast.list[1];\n                    if (!isSeq(pairs)) {\n                        throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                    }\n                    for (let i = 0; i < pairs.list.length; i += 2) {\n                        const key = pairs.list[i];\n                        const value = pairs.list[i + 1];\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!key || !value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n\n                        letEnv.set(key.v, evalMal(value, letEnv));\n                    }\n                    return evalMal(ast.list[2], letEnv);\n                }\n                case \"do\": {\n                    for (let i = 1; i < ast.list.length - 1; i++)\n                        evalMal(ast.list[i], env);\n                    return evalMal(ast.list[ast.list.length - 1], env);\n                }\n                case \"if\": {\n                    const [, cond, thenExpr, elseExrp] = ast.list;\n                    const ret = evalMal(cond, env);\n                    let b = true;\n                    if (ret.type === Node.Boolean && !ret.v) {\n                        b = false;\n                    } else if (ret.type === Node.Nil) {\n                        b = false;\n                    }\n                    if (b) {\n                        return evalMal(thenExpr, env);\n                    } else if (elseExrp) {\n                        return evalMal(elseExrp, env);\n                    } else {\n                        return MalNil.instance;\n                    }\n                }\n                case \"fn*\": {\n                    const [, args, binds] = ast.list;\n                    if (!isSeq(args)) {\n                        throw new Error(`unexpected return type: ${args.type}, expected: list or vector`);\n                    }\n                    const symbols = args.list.map(param => {\n                        if (param.type !== Node.Symbol) {\n                            throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                        }\n                        return param;\n                    });\n                    return MalFunction.fromBootstrap((...fnArgs: MalType[]) => {\n                        return evalMal(binds, new Env(env, symbols, fnArgs));\n                    });\n                }\n            }\n    }\n    const f : MalType = evalMal(first, env);\n    if (f.type !== Node.Function) {\n        throw new Error(`unexpected token: ${f.type}, expected: function`);\n    }\n    const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n    return f.func(...args);\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/step5_tco.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalNil, MalVector, MalHashMap, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    loop: while (true) {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n        }\n        if (ast.list.length === 0) {\n            return ast;\n        }\n        const first = ast.list[0];\n        switch (first.type) {\n            case Node.Symbol:\n                switch (first.v) {\n                    case \"def!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        return env.set(key.v, evalMal(value, env));\n                    }\n                    case \"let*\": {\n                        env = new Env(env);\n                        const pairs = ast.list[1];\n                        if (!isSeq(pairs)) {\n                            throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                        }\n                        for (let i = 0; i < pairs.list.length; i += 2) {\n                            const key = pairs.list[i];\n                            const value = pairs.list[i + 1];\n                            if (key.type !== Node.Symbol) {\n                                throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                            }\n                            if (!key || !value) {\n                                throw new Error(`unexpected syntax`);\n                            }\n\n                            env.set(key.v, evalMal(value, env));\n                        }\n                        ast = ast.list[2];\n                        continue loop;\n                    }\n                    case \"do\": {\n                        for (let i = 1; i < ast.list.length - 1; i++)\n                            evalMal(ast.list[i], env);\n                        ast = ast.list[ast.list.length - 1];\n                        continue loop;\n                    }\n                    case \"if\": {\n                        const [, cond, thenExpr, elseExrp] = ast.list;\n                        const ret = evalMal(cond, env);\n                        let b = true;\n                        if (ret.type === Node.Boolean && !ret.v) {\n                            b = false;\n                        } else if (ret.type === Node.Nil) {\n                            b = false;\n                        }\n                        if (b) {\n                            ast = thenExpr;\n                        } else if (elseExrp) {\n                            ast = elseExrp;\n                        } else {\n                            ast = MalNil.instance;\n                        }\n                        continue loop;\n                    }\n                    case \"fn*\": {\n                        const [, params, bodyAst] = ast.list;\n                        if (!isSeq(params)) {\n                            throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);\n                        }\n                        const symbols = params.list.map(param => {\n                            if (param.type !== Node.Symbol) {\n                                throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                            }\n                            return param;\n                        });\n                        return MalFunction.fromLisp(evalMal, env, symbols, bodyAst);\n                    }\n                }\n        }\n        const f : MalType = evalMal(first, env);\n        if (f.type !== Node.Function) {\n            throw new Error(`unexpected token: ${f.type}, expected: function`);\n        }\n        const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n        if (f.ast) {\n            ast = f.ast;\n            env = f.newEnv(args);\n            continue loop;\n        }\n\n        return f.func(...args);\n    }\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/step6_file.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalString, MalNil, MalList, MalVector, MalHashMap, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    loop: while (true) {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n        }\n        if (ast.list.length === 0) {\n            return ast;\n        }\n        const first = ast.list[0];\n        switch (first.type) {\n            case Node.Symbol:\n                switch (first.v) {\n                    case \"def!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        return env.set(key.v, evalMal(value, env));\n                    }\n                    case \"let*\": {\n                        env = new Env(env);\n                        const pairs = ast.list[1];\n                        if (!isSeq(pairs)) {\n                            throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                        }\n                        for (let i = 0; i < pairs.list.length; i += 2) {\n                            const key = pairs.list[i];\n                            const value = pairs.list[i + 1];\n                            if (key.type !== Node.Symbol) {\n                                throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                            }\n                            if (!key || !value) {\n                                throw new Error(`unexpected syntax`);\n                            }\n\n                            env.set(key.v, evalMal(value, env));\n                        }\n                        ast = ast.list[2];\n                        continue loop;\n                    }\n                    case \"do\": {\n                        for (let i = 1; i < ast.list.length - 1; i++)\n                            evalMal(ast.list[i], env);\n                        ast = ast.list[ast.list.length - 1];\n                        continue loop;\n                    }\n                    case \"if\": {\n                        const [, cond, thenExpr, elseExrp] = ast.list;\n                        const ret = evalMal(cond, env);\n                        let b = true;\n                        if (ret.type === Node.Boolean && !ret.v) {\n                            b = false;\n                        } else if (ret.type === Node.Nil) {\n                            b = false;\n                        }\n                        if (b) {\n                            ast = thenExpr;\n                        } else if (elseExrp) {\n                            ast = elseExrp;\n                        } else {\n                            ast = MalNil.instance;\n                        }\n                        continue loop;\n                    }\n                    case \"fn*\": {\n                        const [, params, bodyAst] = ast.list;\n                        if (!isSeq(params)) {\n                            throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);\n                        }\n                        const symbols = params.list.map(param => {\n                            if (param.type !== Node.Symbol) {\n                                throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                            }\n                            return param;\n                        });\n                        return MalFunction.fromLisp(evalMal, env, symbols, bodyAst);\n                    }\n                }\n        }\n        const f : MalType = evalMal(first, env);\n        if (f.type !== Node.Function) {\n            throw new Error(`unexpected token: ${f.type}, expected: function`);\n        }\n        const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n        if (f.ast) {\n            ast = f.ast;\n            env = f.newEnv(args);\n            continue loop;\n        }\n\n        return f.func(...args);\n    }\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\nreplEnv.set(\"eval\", MalFunction.fromBootstrap(ast => {\n    if (!ast) {\n        throw new Error(`undefined argument`);\n    }\n    return evalMal(ast, replEnv);\n}));\nreplEnv.set(\"*ARGV*\", new MalList([]));\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(`(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))`);\n\nif (typeof process !== \"undefined\" && 2 < process.argv.length) {\n    replEnv.set(\"*ARGV*\", new MalList(process.argv.slice(3).map(s => new MalString(s))));\n    rep(`(load-file \"${process.argv[2]}\")`);\n    process.exit(0);\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/step7_quote.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalString, MalNil, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\nfunction starts_with(lst: MalType[], sym: string): boolean {\n    if (lst.length == 2) {\n        let a0 = lst[0]\n        switch (a0.type) {\n            case Node.Symbol:\n                return a0.v === sym;\n        }\n    }\n    return false;\n}\n\nfunction qq_loop(elt: MalType, acc: MalList): MalList {\n    if (elt.type == Node.List && starts_with(elt.list, \"splice-unquote\")) {\n        return new MalList([MalSymbol.get(\"concat\"), elt.list[1], acc]);\n    } else {\n        return new MalList([MalSymbol.get(\"cons\"), quasiquote(elt), acc]);\n    }\n}\n\nfunction qq_foldr(xs : MalType[]): MalList {\n    let acc = new MalList([])\n    for (let i=xs.length-1; 0<=i; i-=1) {\n         acc = qq_loop(xs[i], acc)\n    }\n    return acc;\n}\n\nfunction quasiquote(ast: MalType): MalType {\n    switch (ast.type) {\n        case Node.Symbol:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.HashMap:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.List:\n            if (starts_with(ast.list, \"unquote\")) {\n                return ast.list[1];\n            } else {\n                return qq_foldr(ast.list);\n            }\n        case Node.Vector:\n            return new MalList([MalSymbol.get(\"vec\"), qq_foldr(ast.list)]);\n        default:\n            return ast;\n    }\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    loop: while (true) {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n        }\n        if (ast.list.length === 0) {\n            return ast;\n        }\n        const first = ast.list[0];\n        switch (first.type) {\n            case Node.Symbol:\n                switch (first.v) {\n                    case \"def!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        return env.set(key.v, evalMal(value, env));\n                    }\n                    case \"let*\": {\n                        env = new Env(env);\n                        const pairs = ast.list[1];\n                        if (!isSeq(pairs)) {\n                            throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                        }\n                        for (let i = 0; i < pairs.list.length; i += 2) {\n                            const key = pairs.list[i];\n                            const value = pairs.list[i + 1];\n                            if (key.type !== Node.Symbol) {\n                                throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                            }\n                            if (!key || !value) {\n                                throw new Error(`unexpected syntax`);\n                            }\n\n                            env.set(key.v, evalMal(value, env));\n                        }\n                        ast = ast.list[2];\n                        continue loop;\n                    }\n                    case \"quote\": {\n                        return ast.list[1];\n                    }\n                    case \"quasiquote\": {\n                        ast = quasiquote(ast.list[1]);\n                        continue loop;\n                    }\n                    case \"do\": {\n                        for (let i = 1; i < ast.list.length - 1; i++)\n                            evalMal(ast.list[i], env);\n                        ast = ast.list[ast.list.length - 1];\n                        continue loop;\n                    }\n                    case \"if\": {\n                        const [, cond, thenExpr, elseExrp] = ast.list;\n                        const ret = evalMal(cond, env);\n                        let b = true;\n                        if (ret.type === Node.Boolean && !ret.v) {\n                            b = false;\n                        } else if (ret.type === Node.Nil) {\n                            b = false;\n                        }\n                        if (b) {\n                            ast = thenExpr;\n                        } else if (elseExrp) {\n                            ast = elseExrp;\n                        } else {\n                            ast = MalNil.instance;\n                        }\n                        continue loop;\n                    }\n                    case \"fn*\": {\n                        const [, params, bodyAst] = ast.list;\n                        if (!isSeq(params)) {\n                            throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);\n                        }\n                        const symbols = params.list.map(param => {\n                            if (param.type !== Node.Symbol) {\n                                throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                            }\n                            return param;\n                        });\n                        return MalFunction.fromLisp(evalMal, env, symbols, bodyAst);\n                    }\n                }\n        }\n        const f : MalType = evalMal(first, env);\n        if (f.type !== Node.Function) {\n            throw new Error(`unexpected token: ${f.type}, expected: function`);\n        }\n        const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n        if (f.ast) {\n            ast = f.ast;\n            env = f.newEnv(args);\n            continue loop;\n        }\n\n        return f.func(...args);\n    }\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\nreplEnv.set(\"eval\", MalFunction.fromBootstrap(ast => {\n    if (!ast) {\n        throw new Error(`undefined argument`);\n    }\n    return evalMal(ast, replEnv);\n}));\nreplEnv.set(\"*ARGV*\", new MalList([]));\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(`(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))`);\n\nif (typeof process !== \"undefined\" && 2 < process.argv.length) {\n    replEnv.set(\"*ARGV*\", new MalList(process.argv.slice(3).map(s => new MalString(s))));\n    rep(`(load-file \"${process.argv[2]}\")`);\n    process.exit(0);\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/step8_macros.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalString, MalNil, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\nfunction starts_with(lst: MalType[], sym: string): boolean {\n    if (lst.length == 2) {\n        let a0 = lst[0]\n        switch (a0.type) {\n            case Node.Symbol:\n                return a0.v === sym;\n        }\n    }\n    return false;\n}\n\nfunction qq_loop(elt: MalType, acc: MalList): MalList {\n    if (elt.type == Node.List && starts_with(elt.list, \"splice-unquote\")) {\n        return new MalList([MalSymbol.get(\"concat\"), elt.list[1], acc]);\n    } else {\n        return new MalList([MalSymbol.get(\"cons\"), quasiquote(elt), acc]);\n    }\n}\n\nfunction qq_foldr(xs : MalType[]): MalList {\n    let acc = new MalList([])\n    for (let i=xs.length-1; 0<=i; i-=1) {\n         acc = qq_loop(xs[i], acc)\n    }\n    return acc;\n}\n\nfunction quasiquote(ast: MalType): MalType {\n    switch (ast.type) {\n        case Node.Symbol:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.HashMap:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.List:\n            if (starts_with(ast.list, \"unquote\")) {\n                return ast.list[1];\n            } else {\n                return qq_foldr(ast.list);\n            }\n        case Node.Vector:\n            return new MalList([MalSymbol.get(\"vec\"), qq_foldr(ast.list)]);\n        default:\n            return ast;\n    }\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    loop: while (true) {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n        }\n        if (ast.list.length === 0) {\n            return ast;\n        }\n        const first = ast.list[0];\n        switch (first.type) {\n            case Node.Symbol:\n                switch (first.v) {\n                    case \"def!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        return env.set(key.v, evalMal(value, env));\n                    }\n                    case \"let*\": {\n                        env = new Env(env);\n                        const pairs = ast.list[1];\n                        if (!isSeq(pairs)) {\n                            throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                        }\n                        for (let i = 0; i < pairs.list.length; i += 2) {\n                            const key = pairs.list[i];\n                            const value = pairs.list[i + 1];\n                            if (key.type !== Node.Symbol) {\n                                throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                            }\n                            if (!key || !value) {\n                                throw new Error(`unexpected syntax`);\n                            }\n\n                            env.set(key.v, evalMal(value, env));\n                        }\n                        ast = ast.list[2];\n                        continue loop;\n                    }\n                    case \"quote\": {\n                        return ast.list[1];\n                    }\n                    case \"quasiquote\": {\n                        ast = quasiquote(ast.list[1]);\n                        continue loop;\n                    }\n                    case \"defmacro!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        const f = evalMal(value, env);\n                        if (f.type !== Node.Function) {\n                            throw new Error(`unexpected token type: ${f.type}, expected: function`);\n                        }\n                        return env.set(key.v, f.toMacro());\n                    }\n                    case \"do\": {\n                        for (let i = 1; i < ast.list.length - 1; i++)\n                            evalMal(ast.list[i], env);\n                        ast = ast.list[ast.list.length - 1];\n                        continue loop;\n                    }\n                    case \"if\": {\n                        const [, cond, thenExpr, elseExrp] = ast.list;\n                        const ret = evalMal(cond, env);\n                        let b = true;\n                        if (ret.type === Node.Boolean && !ret.v) {\n                            b = false;\n                        } else if (ret.type === Node.Nil) {\n                            b = false;\n                        }\n                        if (b) {\n                            ast = thenExpr;\n                        } else if (elseExrp) {\n                            ast = elseExrp;\n                        } else {\n                            ast = MalNil.instance;\n                        }\n                        continue loop;\n                    }\n                    case \"fn*\": {\n                        const [, params, bodyAst] = ast.list;\n                        if (!isSeq(params)) {\n                            throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);\n                        }\n                        const symbols = params.list.map(param => {\n                            if (param.type !== Node.Symbol) {\n                                throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                            }\n                            return param;\n                        });\n                        return MalFunction.fromLisp(evalMal, env, symbols, bodyAst);\n                    }\n                }\n        }\n        const f : MalType = evalMal(first, env);\n        if (f.type !== Node.Function) {\n            throw new Error(`unexpected token: ${f.type}, expected: function`);\n        }\n        if (f.isMacro) {\n            ast = f.func(...ast.list.slice(1));\n            continue loop;\n        }\n        const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n        if (f.ast) {\n            ast = f.ast;\n            env = f.newEnv(args);\n            continue loop;\n        }\n\n        return f.func(...args);\n    }\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\nreplEnv.set(\"eval\", MalFunction.fromBootstrap(ast => {\n    if (!ast) {\n        throw new Error(`undefined argument`);\n    }\n    return evalMal(ast, replEnv);\n}));\nreplEnv.set(\"*ARGV*\", new MalList([]));\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(`(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))`);\nrep(`(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))`);\n\nif (typeof process !== \"undefined\" && 2 < process.argv.length) {\n    replEnv.set(\"*ARGV*\", new MalList(process.argv.slice(3).map(s => new MalString(s))));\n    rep(`(load-file \"${process.argv[2]}\")`);\n    process.exit(0);\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/step9_try.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalString, MalNil, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\nfunction starts_with(lst: MalType[], sym: string): boolean {\n    if (lst.length == 2) {\n        let a0 = lst[0]\n        switch (a0.type) {\n            case Node.Symbol:\n                return a0.v === sym;\n        }\n    }\n    return false;\n}\n\nfunction qq_loop(elt: MalType, acc: MalList): MalList {\n    if (elt.type == Node.List && starts_with(elt.list, \"splice-unquote\")) {\n        return new MalList([MalSymbol.get(\"concat\"), elt.list[1], acc]);\n    } else {\n        return new MalList([MalSymbol.get(\"cons\"), quasiquote(elt), acc]);\n    }\n}\n\nfunction qq_foldr(xs : MalType[]): MalList {\n    let acc = new MalList([])\n    for (let i=xs.length-1; 0<=i; i-=1) {\n         acc = qq_loop(xs[i], acc)\n    }\n    return acc;\n}\n\nfunction quasiquote(ast: MalType): MalType {\n    switch (ast.type) {\n        case Node.Symbol:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.HashMap:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.List:\n            if (starts_with(ast.list, \"unquote\")) {\n                return ast.list[1];\n            } else {\n                return qq_foldr(ast.list);\n            }\n        case Node.Vector:\n            return new MalList([MalSymbol.get(\"vec\"), qq_foldr(ast.list)]);\n        default:\n            return ast;\n    }\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    loop: while (true) {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n        }\n        if (ast.list.length === 0) {\n            return ast;\n        }\n        const first = ast.list[0];\n        switch (first.type) {\n            case Node.Symbol:\n                switch (first.v) {\n                    case \"def!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        return env.set(key.v, evalMal(value, env));\n                    }\n                    case \"let*\": {\n                        env = new Env(env);\n                        const pairs = ast.list[1];\n                        if (!isSeq(pairs)) {\n                            throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                        }\n                        for (let i = 0; i < pairs.list.length; i += 2) {\n                            const key = pairs.list[i];\n                            const value = pairs.list[i + 1];\n                            if (key.type !== Node.Symbol) {\n                                throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                            }\n                            if (!key || !value) {\n                                throw new Error(`unexpected syntax`);\n                            }\n\n                            env.set(key.v, evalMal(value, env));\n                        }\n                        ast = ast.list[2];\n                        continue loop;\n                    }\n                    case \"quote\": {\n                        return ast.list[1];\n                    }\n                    case \"quasiquote\": {\n                        ast = quasiquote(ast.list[1]);\n                        continue loop;\n                    }\n                    case \"defmacro!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        const f = evalMal(value, env);\n                        if (f.type !== Node.Function) {\n                            throw new Error(`unexpected token type: ${f.type}, expected: function`);\n                        }\n                        return env.set(key.v, f.toMacro());\n                    }\n                    case \"try*\": {\n                        try {\n                            return evalMal(ast.list[1], env);\n                        } catch (e) {\n                            if (ast.list.length < 3) {\n                                throw e;\n                            }\n                            const catchBody = ast.list[2];\n                            if (!isSeq(catchBody)) {\n                                throw new Error(`unexpected return type: ${catchBody.type}, expected: list or vector`);\n                            }\n                            const catchSymbol = catchBody.list[0];\n                            if (catchSymbol.type === Node.Symbol && catchSymbol.v === \"catch*\") {\n                                const errorSymbol = catchBody.list[1];\n                                if (errorSymbol.type !== Node.Symbol) {\n                                    throw new Error(`unexpected return type: ${errorSymbol.type}, expected: symbol`);\n                                }\n                                if (!isAST(e)) {\n                                    e = new MalString((e as Error).message);\n                                }\n                                return evalMal(catchBody.list[2], new Env(env, [errorSymbol], [e]));\n                            }\n                            throw e;\n                        }\n                    }\n                    case \"do\": {\n                        for (let i = 1; i < ast.list.length - 1; i++)\n                            evalMal(ast.list[i], env);\n                        ast = ast.list[ast.list.length - 1];\n                        continue loop;\n                    }\n                    case \"if\": {\n                        const [, cond, thenExpr, elseExrp] = ast.list;\n                        const ret = evalMal(cond, env);\n                        let b = true;\n                        if (ret.type === Node.Boolean && !ret.v) {\n                            b = false;\n                        } else if (ret.type === Node.Nil) {\n                            b = false;\n                        }\n                        if (b) {\n                            ast = thenExpr;\n                        } else if (elseExrp) {\n                            ast = elseExrp;\n                        } else {\n                            ast = MalNil.instance;\n                        }\n                        continue loop;\n                    }\n                    case \"fn*\": {\n                        const [, params, bodyAst] = ast.list;\n                        if (!isSeq(params)) {\n                            throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);\n                        }\n                        const symbols = params.list.map(param => {\n                            if (param.type !== Node.Symbol) {\n                                throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                            }\n                            return param;\n                        });\n                        return MalFunction.fromLisp(evalMal, env, symbols, bodyAst);\n                    }\n                }\n        }\n        const f : MalType = evalMal(first, env);\n        if (f.type !== Node.Function) {\n            throw new Error(`unexpected token: ${f.type}, expected: function`);\n        }\n        if (f.isMacro) {\n            ast = f.func(...ast.list.slice(1));\n            continue loop;\n        }\n        const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n        if (f.ast) {\n            ast = f.ast;\n            env = f.newEnv(args);\n            continue loop;\n        }\n\n        return f.func(...args);\n    }\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\nreplEnv.set(\"eval\", MalFunction.fromBootstrap(ast => {\n    if (!ast) {\n        throw new Error(`undefined argument`);\n    }\n    return evalMal(ast, replEnv);\n}));\nreplEnv.set(\"*ARGV*\", new MalList([]));\n\n// core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(`(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))`);\nrep(`(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))`);\n\nif (typeof process !== \"undefined\" && 2 < process.argv.length) {\n    replEnv.set(\"*ARGV*\", new MalList(process.argv.slice(3).map(s => new MalString(s))));\n    rep(`(load-file \"${process.argv[2]}\")`);\n    process.exit(0);\n}\n\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/stepA_mal.ts",
    "content": "import { readline } from \"./node_readline\";\n\nimport { Node, MalType, MalString, MalNil, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from \"./types\";\nimport { Env } from \"./env\";\nimport * as core from \"./core\";\nimport { readStr } from \"./reader\";\nimport { prStr } from \"./printer\";\n\n// READ\nfunction read(str: string): MalType {\n    return readStr(str);\n}\n\nfunction starts_with(lst: MalType[], sym: string): boolean {\n    if (lst.length == 2) {\n        let a0 = lst[0]\n        switch (a0.type) {\n            case Node.Symbol:\n                return a0.v === sym;\n        }\n    }\n    return false;\n}\n\nfunction qq_loop(elt: MalType, acc: MalList): MalList {\n    if (elt.type == Node.List && starts_with(elt.list, \"splice-unquote\")) {\n        return new MalList([MalSymbol.get(\"concat\"), elt.list[1], acc]);\n    } else {\n        return new MalList([MalSymbol.get(\"cons\"), quasiquote(elt), acc]);\n    }\n}\n\nfunction qq_foldr(xs : MalType[]): MalList {\n    let acc = new MalList([])\n    for (let i=xs.length-1; 0<=i; i-=1) {\n         acc = qq_loop(xs[i], acc)\n    }\n    return acc;\n}\n\nfunction quasiquote(ast: MalType): MalType {\n    switch (ast.type) {\n        case Node.Symbol:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.HashMap:\n            return new MalList([MalSymbol.get(\"quote\"), ast]);\n        case Node.List:\n            if (starts_with(ast.list, \"unquote\")) {\n                return ast.list[1];\n            } else {\n                return qq_foldr(ast.list);\n            }\n        case Node.Vector:\n            return new MalList([MalSymbol.get(\"vec\"), qq_foldr(ast.list)]);\n        default:\n            return ast;\n    }\n}\n\n// EVAL\nfunction evalMal(ast: MalType, env: Env): MalType {\n    loop: while (true) {\n    // Output a debug line if the option is enabled.\n    const dbgeval : MalType | null = env.get(\"DEBUG-EVAL\");\n    if (dbgeval !== null\n        && dbgeval.type !== Node.Nil\n        && (dbgeval.type !== Node.Boolean || dbgeval.v))\n      console.log(\"EVAL:\", prStr(ast));\n    // Deal with non-list types.\n    switch (ast.type) {\n        case Node.Symbol:\n            const f : MalType | null = env.get(ast.v);\n            if (!f) {\n                throw new Error(`'${ast.v}' not found`);\n            }\n            return f;\n        case Node.List:\n            break;\n        case Node.Vector:\n            return new MalVector(ast.list.map(ast => evalMal(ast, env)));\n        case Node.HashMap:\n            const list: MalType[] = [];\n            for (const [key, value] of ast.entries()) {\n                list.push(key);\n                list.push(evalMal(value, env));\n            }\n            return new MalHashMap(list);\n        default:\n            return ast;\n        }\n        if (ast.list.length === 0) {\n            return ast;\n        }\n        const first = ast.list[0];\n        switch (first.type) {\n            case Node.Symbol:\n                switch (first.v) {\n                    case \"def!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        return env.set(key.v, evalMal(value, env));\n                    }\n                    case \"let*\": {\n                        env = new Env(env);\n                        const pairs = ast.list[1];\n                        if (!isSeq(pairs)) {\n                            throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`);\n                        }\n                        for (let i = 0; i < pairs.list.length; i += 2) {\n                            const key = pairs.list[i];\n                            const value = pairs.list[i + 1];\n                            if (key.type !== Node.Symbol) {\n                                throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                            }\n                            if (!key || !value) {\n                                throw new Error(`unexpected syntax`);\n                            }\n\n                            env.set(key.v, evalMal(value, env));\n                        }\n                        ast = ast.list[2];\n                        continue loop;\n                    }\n                    case \"quote\": {\n                        return ast.list[1];\n                    }\n                    case \"quasiquote\": {\n                        ast = quasiquote(ast.list[1]);\n                        continue loop;\n                    }\n                    case \"defmacro!\": {\n                        const [, key, value] = ast.list;\n                        if (key.type !== Node.Symbol) {\n                            throw new Error(`unexpected token type: ${key.type}, expected: symbol`);\n                        }\n                        if (!value) {\n                            throw new Error(`unexpected syntax`);\n                        }\n                        const f = evalMal(value, env);\n                        if (f.type !== Node.Function) {\n                            throw new Error(`unexpected token type: ${f.type}, expected: function`);\n                        }\n                        return env.set(key.v, f.toMacro());\n                    }\n                    case \"try*\": {\n                        try {\n                            return evalMal(ast.list[1], env);\n                        } catch (e) {\n                            if (ast.list.length < 3) {\n                                throw e;\n                            }\n                            const catchBody = ast.list[2];\n                            if (!isSeq(catchBody)) {\n                                throw new Error(`unexpected return type: ${catchBody.type}, expected: list or vector`);\n                            }\n                            const catchSymbol = catchBody.list[0];\n                            if (catchSymbol.type === Node.Symbol && catchSymbol.v === \"catch*\") {\n                                const errorSymbol = catchBody.list[1];\n                                if (errorSymbol.type !== Node.Symbol) {\n                                    throw new Error(`unexpected return type: ${errorSymbol.type}, expected: symbol`);\n                                }\n                                if (!isAST(e)) {\n                                    e = new MalString((e as Error).message);\n                                }\n                                return evalMal(catchBody.list[2], new Env(env, [errorSymbol], [e]));\n                            }\n                            throw e;\n                        }\n                    }\n                    case \"do\": {\n                        for (let i = 1; i < ast.list.length - 1; i++)\n                            evalMal(ast.list[i], env);\n                        ast = ast.list[ast.list.length - 1];\n                        continue loop;\n                    }\n                    case \"if\": {\n                        const [, cond, thenExpr, elseExrp] = ast.list;\n                        const ret = evalMal(cond, env);\n                        let b = true;\n                        if (ret.type === Node.Boolean && !ret.v) {\n                            b = false;\n                        } else if (ret.type === Node.Nil) {\n                            b = false;\n                        }\n                        if (b) {\n                            ast = thenExpr;\n                        } else if (elseExrp) {\n                            ast = elseExrp;\n                        } else {\n                            ast = MalNil.instance;\n                        }\n                        continue loop;\n                    }\n                    case \"fn*\": {\n                        const [, params, bodyAst] = ast.list;\n                        if (!isSeq(params)) {\n                            throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);\n                        }\n                        const symbols = params.list.map(param => {\n                            if (param.type !== Node.Symbol) {\n                                throw new Error(`unexpected return type: ${param.type}, expected: symbol`);\n                            }\n                            return param;\n                        });\n                        return MalFunction.fromLisp(evalMal, env, symbols, bodyAst);\n                    }\n                }\n        }\n        const f : MalType = evalMal(first, env);\n        if (f.type !== Node.Function) {\n            throw new Error(`unexpected token: ${f.type}, expected: function`);\n        }\n        if (f.isMacro) {\n            ast = f.func(...ast.list.slice(1));\n            continue loop;\n        }\n        const args : Array<MalType> = ast.list.slice(1).map(x => evalMal(x, env));\n        if (f.ast) {\n            ast = f.ast;\n            env = f.newEnv(args);\n            continue loop;\n        }\n\n        return f.func(...args);\n    }\n}\n\n// PRINT\nfunction print(exp: MalType): string {\n    return prStr(exp);\n}\n\nconst replEnv = new Env();\nfunction rep(str: string): string {\n    return print(evalMal(read(str), replEnv));\n}\n\n// core.EXT: defined using Racket\ncore.ns.forEach((value, key) => {\n    replEnv.set(key, value);\n});\nreplEnv.set(\"eval\", MalFunction.fromBootstrap(ast => {\n    if (!ast) {\n        throw new Error(`undefined argument`);\n    }\n    return evalMal(ast, replEnv);\n}));\nreplEnv.set(\"*ARGV*\", new MalList([]));\n\n// core.mal: defined using the language itself\nrep(`(def! *host-language* \"TypeScript\")`);\nrep(\"(def! not (fn* (a) (if a false true)))\");\nrep(`(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))`);\nrep(`(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))`);\n\nif (typeof process !== \"undefined\" && 2 < process.argv.length) {\n    replEnv.set(\"*ARGV*\", new MalList(process.argv.slice(3).map(s => new MalString(s))));\n    rep(`(load-file \"${process.argv[2]}\")`);\n    process.exit(0);\n}\n\nrep(`(println (str \"Mal [\" *host-language* \"]\"))`);\nwhile (true) {\n    const line = readline(\"user> \");\n    if (line == null) {\n        break;\n    }\n    if (line === \"\") {\n        continue;\n    }\n    try {\n        console.log(rep(line));\n    } catch (e) {\n        if (isAST(e)) {\n            console.error(\"Error:\", prStr(e));\n        } else {\n            const err: Error = e;\n            console.error(\"Error:\", err.message);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/ts/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"target\": \"es5\",\n        \"lib\": [\n            \"es2015\"\n        ],\n        \"noImplicitAny\": true,\n        \"noEmitOnError\": true,\n        \"noImplicitReturns\": true,\n        \"noImplicitThis\": true,\n        \"noUnusedLocals\": true,\n        \"noUnusedParameters\": true,\n        \"newLine\": \"LF\",\n        \"strictNullChecks\": true,\n        \"sourceMap\": false\n    },\n    \"exclude\": [\n        \"node_modules\"\n    ]\n}\n"
  },
  {
    "path": "impls/ts/types.ts",
    "content": "import { Env } from \"./env\";\n\nexport type MalType = MalList | MalNumber | MalString | MalNil | MalBoolean | MalSymbol | MalKeyword | MalVector | MalHashMap | MalFunction | MalAtom;\n\nexport const enum Node {\n    List = 1,\n    Number,\n    String,\n    Nil,\n    Boolean,\n    Symbol,\n    Keyword,\n    Vector,\n    HashMap,\n    Function,\n    Atom,\n}\n\nexport function equals(a: MalType, b: MalType, strict?: boolean): boolean {\n    if (strict && a.type !== b.type) {\n        return false;\n    }\n\n    if (a.type === Node.Nil && b.type === Node.Nil) {\n        return true;\n    }\n    if (isSeq(a) && isSeq(b)) {\n        return listEquals(a.list, b.list);\n    }\n    if (a.type === Node.HashMap && b.type === Node.HashMap) {\n        if (a.keywordMap.size !== b.keywordMap.size) {\n            return false;\n        }\n        if (Object.keys(a.stringMap).length !== Object.keys(b.stringMap).length) {\n            return false;\n        }\n        for (const [aK, aV] of a.entries()) {\n            if (aK.type !== Node.String && aK.type !== Node.Keyword) {\n                throw new Error(`unexpected symbol: ${aK.type}, expected: string or keyword`);\n            }\n            const bV = b.get(aK);\n            if (aV.type === Node.Nil && bV.type === Node.Nil) {\n                continue;\n            }\n            if (!equals(aV, bV)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n    if (\n        (a.type === Node.Number && b.type === Node.Number)\n        || (a.type === Node.String && b.type === Node.String)\n        || (a.type === Node.Boolean && b.type === Node.Boolean)\n        || (a.type === Node.Symbol && b.type === Node.Symbol)\n        || (a.type === Node.Keyword && b.type === Node.Keyword)\n    ) {\n        return a.v === b.v;\n    }\n\n    return false;\n\n    function listEquals(a: MalType[], b: MalType[]): boolean {\n        if (a.length !== b.length) {\n            return false;\n        }\n        for (let i = 0; i < a.length; i++) {\n            if (!equals(a[i], b[i], strict)) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n\nexport function isSeq(ast: MalType): ast is MalList | MalVector {\n    return ast.type === Node.List || ast.type === Node.Vector;\n}\n\nexport function isAST(v: MalType): v is MalType {\n    return !!v.type;\n}\n\nexport class MalList {\n    type: Node.List = Node.List;\n    meta?: MalType;\n\n    constructor(public list: MalType[]) {\n    }\n\n    withMeta(meta: MalType) {\n        const v = new MalList(this.list);\n        v.meta = meta;\n        return v;\n    }\n}\n\nexport class MalNumber {\n    type: Node.Number = Node.Number;\n    meta?: MalType;\n\n    constructor(public v: number) {\n    }\n\n    withMeta(meta: MalType) {\n        const v = new MalNumber(this.v);\n        v.meta = meta;\n        return v;\n    }\n}\n\nexport class MalString {\n    type: Node.String = Node.String;\n    meta?: MalType;\n\n    constructor(public v: string) {\n    }\n\n    withMeta(meta: MalType) {\n        const v = new MalString(this.v);\n        v.meta = meta;\n        return v;\n    }\n}\n\nexport class MalNil {\n\n    private static _instance?: MalNil;\n\n    static get instance(): MalNil {\n        if (this._instance) {\n            return this._instance;\n        }\n        this._instance = new MalNil();\n        return this._instance;\n    }\n\n    type: Node.Nil = Node.Nil;\n    meta?: MalType;\n\n    private constructor() { }\n\n    withMeta(_meta: MalType): MalNil {\n        throw new Error(`not supported`);\n    }\n}\n\nexport class MalBoolean {\n    type: Node.Boolean = Node.Boolean;\n    meta?: MalType;\n\n    constructor(public v: boolean) {\n    }\n\n    withMeta(meta: MalType) {\n        const v = new MalBoolean(this.v);\n        v.meta = meta;\n        return v;\n    }\n}\n\nexport class MalSymbol {\n    static map = new Map<symbol, MalSymbol>();\n\n    static get(name: string): MalSymbol {\n        const sym = Symbol.for(name);\n        let token = this.map.get(sym);\n        if (token) {\n            return token;\n        }\n        token = new MalSymbol(name);\n        this.map.set(sym, token);\n        return token;\n    }\n\n    type: Node.Symbol = Node.Symbol;\n    meta?: MalType;\n\n    private constructor(public v: string) {\n    }\n\n    withMeta(_meta: MalType): MalSymbol {\n        throw new Error(`not supported`);\n    }\n}\n\nexport class MalKeyword {\n    static map = new Map<symbol, MalKeyword>();\n\n    static get(name: string): MalKeyword {\n        const sym = Symbol.for(name);\n        let token = this.map.get(sym);\n        if (token) {\n            return token;\n        }\n        token = new MalKeyword(name);\n        this.map.set(sym, token);\n        return token;\n    }\n\n    type: Node.Keyword = Node.Keyword;\n    meta?: MalType;\n\n    private constructor(public v: string) {\n    }\n\n    withMeta(_meta: MalType): MalKeyword {\n        throw new Error(`not supported`);\n    }\n}\n\nexport class MalVector {\n    type: Node.Vector = Node.Vector;\n    meta?: MalType;\n\n    constructor(public list: MalType[]) {\n    }\n\n    withMeta(meta: MalType) {\n        const v = new MalVector(this.list);\n        v.meta = meta;\n        return v;\n    }\n}\n\nexport class MalHashMap {\n    type: Node.HashMap = Node.HashMap;\n    stringMap: { [key: string]: MalType } = {};\n    keywordMap = new Map<MalType, MalType>();\n    meta?: MalType;\n\n    constructor(list: MalType[]) {\n        while (list.length !== 0) {\n            const key = list.shift()!;\n            const value = list.shift();\n            if (value == null) {\n                throw new Error(\"unexpected hash length\");\n            }\n            if (key.type === Node.Keyword) {\n                this.keywordMap.set(key, value);\n            } else if (key.type === Node.String) {\n                this.stringMap[key.v] = value;\n            } else {\n                throw new Error(`unexpected key symbol: ${key.type}, expected: keyword or string`);\n            }\n        }\n    }\n\n    withMeta(meta: MalType) {\n        const v = this.assoc([]);\n        v.meta = meta;\n        return v;\n    }\n\n    has(key: MalKeyword | MalString) {\n        if (key.type === Node.Keyword) {\n            return !!this.keywordMap.get(key);\n        }\n        return !!this.stringMap[key.v];\n    }\n\n    get(key: MalKeyword | MalString) {\n        if (key.type === Node.Keyword) {\n            return this.keywordMap.get(key) || MalNil.instance;\n        }\n        return this.stringMap[key.v] || MalNil.instance;\n    }\n\n    entries(): [MalType, MalType][] {\n        const list: [MalType, MalType][] = [];\n\n        this.keywordMap.forEach((v, k) => {\n            list.push([k, v]);\n        });\n        Object.keys(this.stringMap).forEach(v => list.push([new MalString(v), this.stringMap[v]]));\n\n        return list;\n    }\n\n    keys(): MalType[] {\n        const list: MalType[] = [];\n        this.keywordMap.forEach((_v, k) => {\n            list.push(k);\n        });\n        Object.keys(this.stringMap).forEach(v => list.push(new MalString(v)));\n        return list;\n    }\n\n    vals(): MalType[] {\n        const list: MalType[] = [];\n        this.keywordMap.forEach(v => {\n            list.push(v);\n        });\n        Object.keys(this.stringMap).forEach(v => list.push(this.stringMap[v]));\n        return list;\n    }\n\n    assoc(args: MalType[]): MalHashMap {\n        const list: MalType[] = [];\n        this.keywordMap.forEach((value, key) => {\n            list.push(key);\n            list.push(value);\n        });\n        Object.keys(this.stringMap).forEach(keyStr => {\n            list.push(new MalString(keyStr));\n            list.push(this.stringMap[keyStr]);\n        });\n\n        return new MalHashMap(list.concat(args));\n    }\n\n    dissoc(args: MalType[]): MalHashMap {\n        const newHashMap = this.assoc([]);\n\n        args.forEach(arg => {\n            if (arg.type === Node.String) {\n                delete newHashMap.stringMap[arg.v];\n            } else if (arg.type === Node.Keyword) {\n                newHashMap.keywordMap.delete(arg);\n            } else {\n                throw new Error(`unexpected symbol: ${arg.type}, expected: keyword or string`);\n            }\n        });\n        return newHashMap;\n    }\n}\n\ntype MalF = (...args: (MalType | undefined)[]) => MalType;\n\nexport class MalFunction {\n    static fromLisp(evalMal: (ast: MalType, env: Env) => MalType, env: Env, params: MalSymbol[], bodyAst: MalType): MalFunction {\n        const f = new MalFunction();\n        f.func = (...args) => evalMal(bodyAst, new Env(env, params, checkUndefined(args)));\n        f.env = env;\n        f.params = params;\n        f.ast = bodyAst;\n        f.isMacro = false;\n\n        return f;\n\n        function checkUndefined(args: (MalType | undefined)[]): MalType[] {\n            return args.map(arg => {\n                if (!arg) {\n                    throw new Error(`undefined argument`);\n                }\n                return arg;\n            });\n        }\n    }\n\n    static fromBootstrap(func: MalF): MalFunction {\n        const f = new MalFunction();\n        f.func = func;\n        f.isMacro = false;\n\n        return f;\n    }\n\n    type: Node.Function = Node.Function;\n    func: MalF;\n    ast: MalType;\n    env: Env;\n    params: MalSymbol[];\n    isMacro: boolean;\n    meta?: MalType;\n\n    private constructor() { }\n\n    toMacro() {\n        const f = new MalFunction();\n        f.func = this.func;\n        f.ast = this.ast;\n        f.env = this.env;\n        f.params = this.params;\n        f.isMacro = true;\n        f.meta = this.meta;\n\n        return f;\n    }\n\n    withMeta(meta: MalType) {\n        const f = new MalFunction();\n        f.func = this.func;\n        f.ast = this.ast;\n        f.env = this.env;\n        f.params = this.params;\n        f.isMacro = this.isMacro;\n        f.meta = meta;\n\n        return f;\n    }\n\n    newEnv(args: MalType[]) {\n        return new Env(this.env, this.params, args);\n    }\n}\n\nexport class MalAtom {\n    type: Node.Atom = Node.Atom;\n    meta?: MalType;\n\n    constructor(public v: MalType) {\n    }\n\n    withMeta(meta: MalType) {\n        const v = new MalAtom(this.v);\n        v.meta = meta;\n        return v;\n    }\n}"
  },
  {
    "path": "impls/vala/.gitignore",
    "content": "*.c\n*.h\n*.o\n"
  },
  {
    "path": "impls/vala/Dockerfile",
    "content": "FROM ubuntu:18.04\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Nothing additional needed for vala\nRUN apt-get -y install valac\n"
  },
  {
    "path": "impls/vala/Makefile",
    "content": "PROGRAMS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do \\\n           step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\nAUX1 = gc.vala types.vala reader.vala printer.vala\nAUX3 = $(AUX1) env.vala\nAUX4 = $(AUX3) core.vala\n\n# Inhibit default make rules, in case they try to build from leftover .c files\n.SUFFIXES:\n\nall: $(PROGRAMS)\n\n# You can define VFLAGS on the command line to add flags to the vala compiler.\n# Some useful ones:\n#\n#  -g        annotate the output C with #line directives so that backtraces\n#            from gdb, sanitisers and valgrind will list Vala source locations\n#\n#  -X -g -X -O0    compile the output C for sensible debugging\n#\n#  -X -fsanitize=address    link the output program against Address Sanitizer\n#\n#  --save-temps    don't automatically delete the C files after compiling\n#\n#  -D GC_STATS     print statistics every time the garbage collector runs\n#\n#  -D GC_DEBUG     print full diagnostics from the garbage collector\n#\n#  -D GC_ALWAYS    make the garbage collector run at every opportunity\n#                  (good for making occasional GC errors show up sooner)\n\n$(PROGRAMS): %: %.vala\n\tvalac $(VFLAGS) -o $@ $^ $(DEFINES) --pkg readline -X -lreadline\n\nstep1_read_print step2_eval: override DEFINES += -D NO_ENV\n\nstep0_repl:\nstep1_read_print: $(AUX1)\nstep2_eval: $(AUX1)\nstep3_env: $(AUX3)\nstep4_if_fn_do: $(AUX4)\nstep5_tco: $(AUX4)\nstep6_file: $(AUX4)\nstep7_quote: $(AUX4)\nstep8_macros: $(AUX4)\nstep9_try: $(AUX4)\nstepA_mal: $(AUX4)\n\nclean: clean-c\n\trm -f $(PROGRAMS)\n\nclean-c:\n\trm -f *.c *.h\n"
  },
  {
    "path": "impls/vala/README.md",
    "content": "# Vala implementation\n\nNotes on building:\n\n* With the Debian or Ubuntu packages `valac` and `libreadline-dev`\n  installed, and GNU make, you should be able to build using the\n  provided Makefile.\n\n* The build will not be warning-clean, because the shared modules like\n  `types.vala` and `core.vala` are shared between all the `stepN` main\n  programs, and not all the steps use all the functions in the shared\n  modules, and the Vala compiler has no way to turn off the warning\n  about unused pieces of source code.\n\n* The Vala compiler works by translating the program to C and then\n  compiling that. The C compilation stage can sometimes encounter an\n  error, in which case the compiler will leave `.c` source files in\n  the working directory. If that happens, you can run `make clean-c`\n  to get rid of them.\n\nDesign notes on the implementation:\n\n* Vala has exceptions (which it calls 'error domains'), but they don't\n  let you store an arbitrary data type: every exception subclass you\n  make stores the same data, namely a string. So mal exceptions are\n  implemented by storing a mal value in a static variable, and then\n  throwing a particular Vala error whose semantics are 'check that\n  variable when you catch me'.\n\n* Vala's bare function pointers are hard to use, especially if you\n  want one to survive the scope it was created in. So all the core\n  functions are implemented as classes with a `call` method, which\n  leads to a lot of boilerplate.\n\n* To make `types.vala` work in step 2, when the `Env` type doesn't\n  exist yet, I had to use `#if` to condition out the parts of the code\n  that depend on that type.\n\n* Mutability of objects at the Vala level is a bit informal. A lot of\n  core functions construct a list by making an empty `Mal.List` and\n  then mutating the `GLib.List` contained in it. But once they've\n  finished and returned the `Mal.List` to their caller, that list is\n  never mutated again, which means it's safe for the copying operation\n  in `with-meta` to make a second `Mal.List` sharing the reference to\n  the same `GLib.List`.\n\n* Vala has a reference counting system built in to the language, but\n  that's not enough to implement mal sensibly, because the common\n  construction `(def! FUNC (fn* [ARGS] BODY))` causes a length-2 cycle\n  of references: the environment captured in `FUNC`'s function object\n  is the same one where `def!` inserts the definition of `FUNC`, so\n  the function and environment both link to each other. And either\n  element of the cycle could end up being the last one referred to\n  from elsewhere, so you can't break the link by just making the right\n  one of those references weak. So instead there's a small garbage\n  collector in `gc.vala`, which works by being the only part of the\n  program that keeps a non-weak reference to any `Mal.Val` or\n  `Mal.Env`: it links all GCable objects together into a list, and\n  when the collector runs, it unlinks dead objects from that list and\n  allows Vala's normal reference counting to free them.\n"
  },
  {
    "path": "impls/vala/core.vala",
    "content": "abstract class Mal.BuiltinFunctionDyadicArithmetic : Mal.BuiltinFunction {\n    public abstract int64 result(int64 a, int64 b);\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        Mal.Num a = args.vs.data as Mal.Num;\n        Mal.Num b = args.vs.next.data as Mal.Num;\n        if (a == null || b == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        return new Mal.Num(result(a.v, b.v));\n    }\n}\n\nclass Mal.BuiltinFunctionAdd : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionAdd();\n    }\n    public override string name() { return \"+\"; }\n    public override int64 result(int64 a, int64 b) { return a+b; }\n}\n\nclass Mal.BuiltinFunctionSub : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSub();\n    }\n    public override string name() { return \"-\"; }\n    public override int64 result(int64 a, int64 b) { return a-b; }\n}\n\nclass Mal.BuiltinFunctionMul : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMul();\n    }\n    public override string name() { return \"*\"; }\n    public override int64 result(int64 a, int64 b) { return a*b; }\n}\n\nclass Mal.BuiltinFunctionDiv : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionDiv();\n    }\n    public override string name() { return \"/\"; }\n    public override int64 result(int64 a, int64 b) { return a/b; }\n}\n\nclass Mal.BuiltinFunctionPrStr : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionPrStr();\n    }\n    public override string name() { return \"pr-str\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        string result = \"\";\n        string sep = \"\";\n        foreach (var value in args.vs) {\n            result += sep + pr_str(value, true);\n            sep = \" \";\n        }\n        return new Mal.String(result);\n    }\n}\n\nclass Mal.BuiltinFunctionStr : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionStr();\n    }\n    public override string name() { return \"str\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        string result = \"\";\n        foreach (var value in args.vs) {\n            result += pr_str(value, false);\n        }\n        return new Mal.String(result);\n    }\n}\n\nclass Mal.BuiltinFunctionPrn : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionPrn();\n    }\n    public override string name() { return \"prn\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        string sep = \"\";\n        foreach (var value in args.vs) {\n            stdout.printf(\"%s%s\", sep, pr_str(value, true));\n            sep = \" \";\n        }\n        stdout.printf(\"\\n\");\n        return new Mal.Nil();\n    }\n}\n\nclass Mal.BuiltinFunctionPrintln : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionPrintln();\n    }\n    public override string name() { return \"println\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        string sep = \"\";\n        foreach (var value in args.vs) {\n            stdout.printf(\"%s%s\", sep, pr_str(value, false));\n            sep = \" \";\n        }\n        stdout.printf(\"\\n\");\n        return new Mal.Nil();\n    }\n}\n\nclass Mal.BuiltinFunctionReadString : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionReadString();\n    }\n    public override string name() { return \"read-string\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1 || !(args.vs.data is Mal.String))\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one string\", name());\n        return Reader.read_str((args.vs.data as Mal.String).v);\n    }\n}\n\nclass Mal.BuiltinFunctionSlurp : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSlurp();\n    }\n    public override string name() { return \"slurp\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1 || !(args.vs.data is Mal.String))\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one string\", name());\n        string filename = (args.vs.data as Mal.String).v;\n        string contents;\n        try {\n            FileUtils.get_contents(filename, out contents);\n        } catch (FileError e) {\n            throw new Mal.Error.BAD_PARAMS(\"%s: unable to read '%s': %s\",\n                                           name(), filename, e.message);\n        }\n        return new Mal.String(contents);\n    }\n}\n\nclass Mal.BuiltinFunctionList : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionList();\n    }\n    public override string name() { return \"list\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        return args;\n    }\n}\n\nclass Mal.BuiltinFunctionListP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionListP();\n    }\n    public override string name() { return \"list?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.List);\n    }\n}\n\nclass Mal.BuiltinFunctionSequentialP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSequentialP();\n    }\n    public override string name() { return \"sequential?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.List ||\n                            args.vs.data is Mal.Vector);\n    }\n}\n\nclass Mal.BuiltinFunctionNilP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionNilP();\n    }\n    public override string name() { return \"nil?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Nil);\n    }\n}\n\nclass Mal.BuiltinFunctionTrueP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionTrueP();\n    }\n    public override string name() { return \"true?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Bool &&\n                            (args.vs.data as Mal.Bool).v);\n    }\n}\n\nclass Mal.BuiltinFunctionFalseP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionFalseP();\n    }\n    public override string name() { return \"false?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Bool &&\n                            !(args.vs.data as Mal.Bool).v);\n    }\n}\n\nclass Mal.BuiltinFunctionNumberP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionNumberP();\n    }\n    public override string name() { return \"number?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Num);\n    }\n}\n\nclass Mal.BuiltinFunctionStringP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionStringP();\n    }\n    public override string name() { return \"string?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.String);\n    }\n}\n\nclass Mal.BuiltinFunctionSymbolP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSymbolP();\n    }\n    public override string name() { return \"symbol?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Sym);\n    }\n}\n\nclass Mal.BuiltinFunctionKeywordP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionKeywordP();\n    }\n    public override string name() { return \"keyword?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Keyword);\n    }\n}\n\nclass Mal.BuiltinFunctionVector : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionVector();\n    }\n    public override string name() { return \"vector\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        return new Mal.Vector.from_list(args.vs);\n    }\n}\n\nclass Mal.BuiltinFunctionVectorP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionVectorP();\n    }\n    public override string name() { return \"vector?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Vector);\n    }\n}\n\nclass Mal.BuiltinFunctionHashMap : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionHashMap();\n    }\n    public override string name() { return \"hash-map\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        var map = new Mal.Hashmap();\n        for (var iter = args.iter(); iter.nonempty(); iter.step()) {\n            var key = iter.deref();\n            var value = iter.step().deref();\n            if (value == null)\n                throw new Mal.Error.BAD_PARAMS(\n                    \"%s: expected an even number of arguments\", name());\n            map.insert(key, value);\n        }\n        return map;\n    }\n}\n\nclass Mal.BuiltinFunctionMapP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMapP();\n    }\n    public override string name() { return \"map?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Hashmap);\n    }\n}\n\nclass Mal.BuiltinFunctionEmptyP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEmptyP();\n    }\n    public override string name() { return \"empty?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        var list = args.vs.data as Mal.Listlike;\n        if (list == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a list-like argument\", name());\n        return new Mal.Bool(list.iter().deref() == null);\n    }\n}\n\nclass Mal.BuiltinFunctionFnP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionFnP();\n    }\n    public override string name() { return \"fn?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        if (args.vs.data is Mal.BuiltinFunction)\n            return new Mal.Bool(true);\n        var fn = args.vs.data as Mal.Function;\n        return new Mal.Bool(fn != null && !fn.is_macro);\n    }\n}\n\nclass Mal.BuiltinFunctionMacroP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMacroP();\n    }\n    public override string name() { return \"macro?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        var fn = args.vs.data as Mal.Function;\n        return new Mal.Bool(fn != null && fn.is_macro);\n    }\n}\n\nclass Mal.BuiltinFunctionCount : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionCount();\n    }\n    public override string name() { return \"count\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        if (args.vs.data is Mal.Nil)\n            return new Mal.Num(0);     // nil is treated like ()\n        if (args.vs.data is Mal.List)\n            return new Mal.Num((args.vs.data as Mal.List).vs.length());\n        if (args.vs.data is Mal.Vector)\n            return new Mal.Num((args.vs.data as Mal.Vector).length);\n        throw new Mal.Error.BAD_PARAMS(\n            \"%s: expected a list argument\", name());\n    }\n}\n\nclass Mal.BuiltinFunctionEQ : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEQ();\n    }\n    public override string name() { return \"=\"; }\n    private static bool eq(Mal.Val a, Mal.Val b) {\n        if (a is Mal.Nil && b is Mal.Nil)\n            return true;\n        if (a is Mal.Bool && b is Mal.Bool)\n            return (a as Mal.Bool).v == (b as Mal.Bool).v;\n        if (a is Mal.Sym && b is Mal.Sym)\n            return (a as Mal.Sym).v == (b as Mal.Sym).v;\n        if (a is Mal.Keyword && b is Mal.Keyword)\n            return (a as Mal.Keyword).v == (b as Mal.Keyword).v;\n        if (a is Mal.Num && b is Mal.Num)\n            return (a as Mal.Num).v == (b as Mal.Num).v;\n        if (a is Mal.String && b is Mal.String)\n            return (a as Mal.String).v == (b as Mal.String).v;\n        if (a is Mal.Listlike && b is Mal.Listlike) {\n            if (a is Mal.Nil || b is Mal.Nil)\n                return false;\n            var aiter = (a as Mal.Listlike).iter();\n            var biter = (b as Mal.Listlike).iter();\n            while (aiter.nonempty() || biter.nonempty()) {\n                if (aiter.empty() || biter.empty())\n                    return false;\n                if (!eq(aiter.deref(), biter.deref()))\n                    return false;\n                aiter.step();\n                biter.step();\n            }\n            return true;\n        }\n        if (a is Mal.Vector && b is Mal.Vector) {\n            var av = a as Mal.Vector;\n            var bv = b as Mal.Vector;\n            if (av.length != bv.length)\n                return false;\n            for (var i = 0; i < av.length; i++)\n                if (!eq(av[i], bv[i]))\n                    return false;\n            return true;\n        }\n        if (a is Mal.Hashmap && b is Mal.Hashmap) {\n            var ah = (a as Mal.Hashmap).vs;\n            var bh = (b as Mal.Hashmap).vs;\n            if (ah.length != bh.length)\n                return false;\n            foreach (var k in ah.get_keys()) {\n                var av = ah[k];\n                var bv = bh[k];\n                if (bv == null || !eq(av, bv))\n                    return false;\n            }\n            return true;\n        }\n        if (a is Mal.BuiltinFunction && b is Mal.BuiltinFunction) {\n            return ((a as Mal.BuiltinFunction).name() ==\n                    (b as Mal.BuiltinFunction).name());\n        }\n        if (a is Mal.Function && b is Mal.Function) {\n            var af = a as Mal.Function;\n            var bf = b as Mal.Function;\n            return (eq(af.parameters, bf.parameters) &&\n                    eq(af.body, bf.body));\n        }\n        return false;\n    }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        return new Mal.Bool(eq(args.vs.data, args.vs.next.data));\n    }\n}\n\nabstract class Mal.BuiltinFunctionNumberCmp : Mal.BuiltinFunction {\n    public abstract bool result(int64 a, int64 b);\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        Mal.Num a = args.vs.data as Mal.Num;\n        Mal.Num b = args.vs.next.data as Mal.Num;\n        if (a == null || b == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        return new Mal.Bool(result(a.v, b.v));\n    }\n}\n\nclass Mal.BuiltinFunctionLT : Mal.BuiltinFunctionNumberCmp {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionLT();\n    }\n    public override string name() { return \"<\"; }\n    public override bool result(int64 a, int64 b) { return a<b; }\n}\n\nclass Mal.BuiltinFunctionLE : Mal.BuiltinFunctionNumberCmp {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionLE();\n    }\n    public override string name() { return \"<=\"; }\n    public override bool result(int64 a, int64 b) { return a<=b; }\n}\n\nclass Mal.BuiltinFunctionGT : Mal.BuiltinFunctionNumberCmp {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionGT();\n    }\n    public override string name() { return \">\"; }\n    public override bool result(int64 a, int64 b) { return a>b; }\n}\n\nclass Mal.BuiltinFunctionGE : Mal.BuiltinFunctionNumberCmp {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionGE();\n    }\n    public override string name() { return \">=\"; }\n    public override bool result(int64 a, int64 b) { return a>=b; }\n}\n\nclass Mal.BuiltinFunctionAtom : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionAtom();\n    }\n    public override string name() { return \"atom\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Atom(args.vs.data);\n    }\n}\n\nclass Mal.BuiltinFunctionAtomP : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionAtomP();\n    }\n    public override string name() { return \"atom?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return new Mal.Bool(args.vs.data is Mal.Atom);\n    }\n}\n\nclass Mal.BuiltinFunctionDeref : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionDeref();\n    }\n    public override string name() { return \"deref\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        var atom = args.vs.data as Mal.Atom;\n        if (atom == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected an atom\", name());\n        return atom.v;\n    }\n}\n\nclass Mal.BuiltinFunctionReset : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionReset();\n    }\n    public override string name() { return \"reset!\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        var atom = args.vs.data as Mal.Atom;\n        if (atom == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected an atom\", name());\n        atom.v = args.vs.next.data;\n        return atom.v;\n    }\n}\n\nMal.Val call_function(Mal.Val function, GLib.List<Mal.Val> args, string caller)\nthrows Mal.Error {\n    var fnargs = new Mal.List(args);\n    if (function is Mal.BuiltinFunction) {\n        return (function as Mal.BuiltinFunction).call(fnargs);\n    } else if (function is Mal.Function) {\n        var fn = function as Mal.Function;\n        var env = new Mal.Env.funcall(fn.env, fn.parameters, fnargs);\n        return Mal.Main.EVAL(fn.body, env);\n    } else {\n        throw new Mal.Error.CANNOT_APPLY(\"%s: expected a function\", caller);\n    }\n}\n\nclass Mal.BuiltinFunctionSwap : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSwap();\n    }\n    public override string name() { return \"swap!\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() < 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected at least two arguments\", name());\n        var atom = args.vs.data as Mal.Atom;\n        var function = args.vs.next.data;\n        var fnargs = args.vs.next.next.copy();\n        fnargs.prepend(atom.v);\n        atom.v = call_function(function, fnargs, name());\n        return atom.v;\n    }\n}\n\nclass Mal.BuiltinFunctionCons : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionCons();\n    }\n    public override string name() { return \"cons\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        var first = args.vs.data;\n        var rest = args.vs.next.data as Mal.Listlike;\n        if (rest == null) {\n            if (args.vs.next.data is Mal.Nil)\n                rest = new Mal.List.empty();\n            else\n                throw new Mal.Error.BAD_PARAMS(\"%s: expected a list\", name());\n        }\n        var newlist = new Mal.List.empty();\n        newlist.vs.append(first);\n        for (var iter = rest.iter(); iter.nonempty(); iter.step())\n            newlist.vs.append(iter.deref());\n        return newlist;\n    }\n}\n\nclass Mal.BuiltinFunctionConcat : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionConcat();\n    }\n    public override string name() { return \"concat\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        var newlist = new GLib.List<Mal.Val>();\n        foreach (var listval in args.vs) {\n            if (listval is Mal.Nil)\n                continue;\n            var list = listval as Mal.Listlike;\n            if (list == null)\n                throw new Mal.Error.BAD_PARAMS(\"%s: expected a list\", name());\n            for (var iter = list.iter(); iter.nonempty(); iter.step())\n                newlist.append(iter.deref());\n        }\n        return new Mal.List(newlist);\n    }\n}\n\nclass Mal.BuiltinFunctionVec : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionVec();\n    }\n    public override string name() { return \"vec\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        var a0 = args.vs.data;\n        if (a0 is Mal.List)\n            return new Mal.Vector.from_list((a0 as Mal.List).vs);\n        if (a0 is Mal.Vector)\n            return a0;\n        throw new Mal.Error.BAD_PARAMS(\n            \"%s: expected a list or a vector\", name());\n    }\n}\n\nclass Mal.BuiltinFunctionNth : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionNth();\n    }\n    public override string name() { return \"nth\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        var list = args.vs.data as Mal.Listlike;\n        var index = args.vs.next.data as Mal.Num;\n        if (list == null || index == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a list and a number\", name());\n        if (index.v < 0)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: negative list index\", name());\n        Mal.Val? result = null;\n        if (list is Mal.Vector) {\n            var vec = list as Mal.Vector;\n            if (index.v < vec.length)\n                result = vec[(uint)index.v];\n        } else {\n            var iter = list.iter();\n            var i = index.v;\n            while (!iter.empty()) {\n                if (i == 0) {\n                    result = iter.deref();\n                    break;\n                }\n                iter.step();\n                i--;\n            }\n        }\n        if (result == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: list index out of range\", name());\n        return result;\n    }\n}\n\nclass Mal.BuiltinFunctionFirst : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionFirst();\n    }\n    public override string name() { return \"first\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        var list = args.vs.data as Mal.Listlike;\n        if (list == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a list number\", name());\n        Mal.Val? result = list.iter().deref();\n        if (result == null)\n            result = new Mal.Nil();\n        return result;\n    }\n}\n\nclass Mal.BuiltinFunctionRest : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionRest();\n    }\n    public override string name() { return \"rest\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        var list = args.vs.data as Mal.Listlike;\n        if (list == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a list\", name());\n        var result = new Mal.List.empty();\n        for (var iter = list.iter().step(); iter.nonempty(); iter.step())\n            result.vs.append(iter.deref());\n        return result;\n    }\n}\n\nclass Mal.BuiltinFunctionThrow : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionThrow();\n    }\n    private static Mal.Val? curr_exception;\n    static construct {\n        curr_exception = null;\n    }\n    public static void clear() {\n        curr_exception = null;\n    }\n    public static Mal.Val thrown_value(Mal.Error err) {\n        if (err is Mal.Error.EXCEPTION_THROWN) {\n            assert(curr_exception != null);\n            Mal.Val toret = curr_exception;\n            curr_exception = null;\n            return toret;\n        } else {\n            return new Mal.String(err.message);\n        }\n    }\n\n    public override string name() { return \"throw\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        assert(curr_exception == null);\n        curr_exception = args.vs.data;\n        throw new Mal.Error.EXCEPTION_THROWN(\"core function throw called\");\n    }\n}\n\nclass Mal.BuiltinFunctionApply : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionApply();\n    }\n    public override string name() { return \"apply\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() < 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected at least two arguments\", name());\n        var function = args.vs.data;\n        unowned GLib.List<Mal.Val> lastlink = args.vs.last();\n        var list = lastlink.data as Mal.Listlike;\n        if (list == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected final argument to be a list\", name());\n        var fnargs = new GLib.List<Mal.Val>();\n        for (var iter = list.iter(); iter.nonempty(); iter.step())\n            fnargs.append(iter.deref());\n        for (unowned GLib.List<Mal.Val> link = lastlink.prev;\n             link != args.vs; link = link.prev)\n            fnargs.prepend(link.data);\n        return call_function(function, fnargs, name());\n    }\n}\n\nclass Mal.BuiltinFunctionMap : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMap();\n    }\n    public override string name() { return \"map\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        var function = args.vs.data;\n        var list = args.vs.next.data as Mal.Listlike;\n        if (list == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected a list\", name());\n        var result = new Mal.List.empty();\n        var root = new GC.Root(result); (void)root;\n        for (var iter = list.iter(); iter.nonempty(); iter.step()) {\n            var fnargs = new GLib.List<Mal.Val>();\n            fnargs.append(iter.deref());\n            result.vs.append(call_function(function, fnargs, name()));\n        }\n        return result;\n    }\n}\n\nclass Mal.BuiltinFunctionSymbol : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSymbol();\n    }\n    public override string name() { return \"symbol\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1 || !(args.vs.data is Mal.String))\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one string\", name());\n        return new Mal.Sym((args.vs.data as Mal.String).v);\n    }\n}\n\nclass Mal.BuiltinFunctionKeyword : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionKeyword();\n    }\n    public override string name() { return \"keyword\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one string\", name());\n        else if (args.vs.data is Mal.Keyword)\n            return args.vs.data;\n        else if (!(args.vs.data is Mal.String))\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one string\", name());\n        return new Mal.Keyword((args.vs.data as Mal.String).v);\n    }\n}\n\nclass Mal.BuiltinFunctionAssoc : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionAssoc();\n    }\n    public override string name() { return \"assoc\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        var iter = args.iter();\n        var oldmap = iter.deref() as Mal.Hashmap;\n        if (iter.deref() is Mal.Nil)\n            oldmap = new Mal.Hashmap();\n        if (oldmap == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a hash-map to modify\", name());\n\n        var map = new Mal.Hashmap();\n        foreach (var key in oldmap.vs.get_keys())\n            map.insert(key, oldmap.vs[key]);\n\n        for (iter.step(); iter.nonempty(); iter.step()) {\n            var key = iter.deref();\n            var value = iter.step().deref();\n            if (value == null)\n                throw new Mal.Error.BAD_PARAMS(\n                    \"%s: expected an even number of arguments\", name());\n            map.insert(key, value);\n        }\n        return map;\n    }\n}\n\nclass Mal.BuiltinFunctionDissoc : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionDissoc();\n    }\n    public override string name() { return \"dissoc\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        var iter = args.iter();\n        var oldmap = iter.deref() as Mal.Hashmap;\n        if (iter.deref() is Mal.Nil)\n            oldmap = new Mal.Hashmap();\n        if (oldmap == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a hash-map to modify\", name());\n\n        var map = new Mal.Hashmap();\n        foreach (var key in oldmap.vs.get_keys())\n            map.insert(key, oldmap.vs[key]);\n\n        for (iter.step(); iter.nonempty(); iter.step()) {\n            var key = iter.deref();\n            map.remove(key);\n        }\n        return map;\n    }\n}\n\n// Can't call it BuiltinFunctionGet, or else valac defines\n// BUILTIN_FUNCTION_GET_CLASS at the C level for this class, but that\n// was already defined as the 'get class' macro for BuiltinFunction\n// itself!\nclass Mal.BuiltinFunctionGetFn : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionGetFn();\n    }\n    public override string name() { return \"get\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        if (args.vs.data is Mal.Nil)\n            return new Mal.Nil();\n        var map = args.vs.data as Mal.Hashmap;\n        if (map == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a hash-map to query\", name());\n        var key = args.vs.next.data as Mal.Hashable;\n        if (key == null)\n            throw new Mal.Error.HASH_KEY_TYPE_ERROR(\n                \"%s: bad type as hash key\", name());\n        var value = map.vs[key];\n        return value != null ? value : new Mal.Nil();\n    }\n}\n\nclass Mal.BuiltinFunctionContains : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionContains();\n    }\n    public override string name() { return \"contains?\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected two arguments\", name());\n        if (args.vs.data is Mal.Nil)\n            return new Mal.Bool(false);\n        var map = args.vs.data as Mal.Hashmap;\n        if (map == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a hash-map to query\", name());\n        var key = args.vs.next.data as Mal.Hashable;\n        if (key == null)\n            throw new Mal.Error.HASH_KEY_TYPE_ERROR(\n                \"%s: bad type as hash key\", name());\n        var value = map.vs[key];\n        return new Mal.Bool(value != null);\n    }\n}\n\nclass Mal.BuiltinFunctionKeys : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionKeys();\n    }\n    public override string name() { return \"keys\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected one argument\", name());\n        var keys = new Mal.List.empty();\n        if (args.vs.data is Mal.Nil)\n            return keys;\n        var map = args.vs.data as Mal.Hashmap;\n        if (map == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a hash-map to query\", name());\n        foreach (var key in map.vs.get_keys())\n            keys.vs.append(key);\n        return keys;\n    }\n}\n\nclass Mal.BuiltinFunctionVals : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionVals();\n    }\n    public override string name() { return \"vals\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected one argument\", name());\n        var vals = new Mal.List.empty();\n        if (args.vs.data is Mal.Nil)\n            return vals;\n        var map = args.vs.data as Mal.Hashmap;\n        if (map == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a hash-map to query\", name());\n        foreach (var key in map.vs.get_keys())\n            vals.vs.append(map.vs[key]);\n        return vals;\n    }\n}\n\nclass Mal.BuiltinFunctionReadline : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionReadline();\n    }\n    public override string name() { return \"readline\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected one argument\", name());\n        string prompt = \"\";\n        if (args.vs.data is Mal.String)\n            prompt = (args.vs.data as Mal.String).v;\n        else if (!(args.vs.data is Mal.Nil))\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a string prompt\", name());\n        string? line = Readline.readline(prompt);\n        if (line == null)\n            return new Mal.Nil();\n        return new Mal.String(line);\n    }\n}\n\nclass Mal.BuiltinFunctionMeta : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMeta();\n    }\n    public override string name() { return \"meta\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected one argument\", name());\n        var vwm = args.vs.data as Mal.ValWithMetadata;\n        if (vwm == null || vwm.metadata == null)\n            return new Mal.Nil();\n        return vwm.metadata;\n    }\n}\n\nclass Mal.BuiltinFunctionWithMeta : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionWithMeta();\n    }\n    public override string name() { return \"with-meta\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected one argument\", name());\n        var vwm = args.vs.data as Mal.ValWithMetadata;\n        if (vwm == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: bad type for with-meta\", name());\n        var copied = vwm.copy();\n        copied.metadata = args.vs.next.data;\n        return copied;\n    }\n}\n\nclass Mal.BuiltinFunctionTimeMs : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionTimeMs();\n    }\n    public override string name() { return \"time-ms\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 0)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected no arguments\", name());\n        var time = GLib.TimeVal();\n        time.get_current_time();\n        return new Mal.Num(time.tv_sec * 1000 + time.tv_usec / 1000);\n    }\n}\n\nclass Mal.BuiltinFunctionConj : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionConj();\n    }\n    public override string name() { return \"conj\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        var iter = args.iter();\n        var collection = iter.deref() as Mal.Listlike;\n        if (collection == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected a collection to modify\", name());\n\n        if (collection is Mal.Vector) {\n            var oldvec = collection as Mal.Vector;\n            var n = args.vs.length() - 1;\n            var newvec = new Mal.Vector.with_size(oldvec.length + n);\n            int i;\n            for (i = 0; i < oldvec.length; i++)\n                newvec[i] = oldvec[i];\n            for (iter.step(); iter.nonempty(); iter.step(), i++)\n                newvec[i] = iter.deref();\n            return newvec;\n        } else {\n            var newlist = new Mal.List.empty();\n            for (var citer = collection.iter(); citer.nonempty(); citer.step())\n                newlist.vs.append(citer.deref());\n            for (iter.step(); iter.nonempty(); iter.step())\n                newlist.vs.prepend(iter.deref());\n            return newlist;\n        }\n    }\n}\n\nclass Mal.BuiltinFunctionSeq : Mal.BuiltinFunction {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSeq();\n    }\n    public override string name() { return \"seq\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\n                \"%s: expected one argument\", name());\n        Mal.List toret;\n        if (args.vs.data is Mal.List) {\n            toret = args.vs.data as Mal.List;\n        } else {\n            toret = new Mal.List.empty();\n            if (args.vs.data is Mal.String) {\n                var str = (args.vs.data as Mal.String).v;\n                if (str.length != 0) {\n                    unowned string tail = str;\n                    while (tail != \"\") {\n                        unowned string new_tail = tail.next_char();\n                        var ch = str.substring(str.length - tail.length,\n                                               tail.length - new_tail.length);\n                        toret.vs.append(new Mal.String(ch));\n                        tail = new_tail;\n                    }\n                }\n            } else if (args.vs.data is Mal.Listlike) {\n                var collection = args.vs.data as Mal.Listlike;\n                for (var iter = collection.iter(); iter.nonempty(); iter.step())\n                    toret.vs.append(iter.deref());\n            } else {\n                throw new Mal.Error.BAD_PARAMS(\"%s: bad input type\", name());\n            }\n        }\n        if (toret.vs.length() == 0)\n            return new Mal.Nil();\n        return toret;\n    }\n}\n\nclass Mal.Core {\n    public static GLib.HashTable<string, Mal.Val> ns;\n\n    private static void add_builtin(Mal.BuiltinFunction f) {\n        ns[f.name()] = f;\n    }\n\n    public static void make_ns() {\n        ns = new GLib.HashTable<string, Val>(str_hash, str_equal);\n        add_builtin(new BuiltinFunctionAdd());\n        add_builtin(new BuiltinFunctionSub());\n        add_builtin(new BuiltinFunctionMul());\n        add_builtin(new BuiltinFunctionDiv());\n        add_builtin(new BuiltinFunctionPrStr());\n        add_builtin(new BuiltinFunctionStr());\n        add_builtin(new BuiltinFunctionPrn());\n        add_builtin(new BuiltinFunctionPrintln());\n        add_builtin(new BuiltinFunctionReadString());\n        add_builtin(new BuiltinFunctionSlurp());\n        add_builtin(new BuiltinFunctionList());\n        add_builtin(new BuiltinFunctionListP());\n        add_builtin(new BuiltinFunctionNilP());\n        add_builtin(new BuiltinFunctionTrueP());\n        add_builtin(new BuiltinFunctionFalseP());\n        add_builtin(new BuiltinFunctionNumberP());\n        add_builtin(new BuiltinFunctionStringP());\n        add_builtin(new BuiltinFunctionSymbol());\n        add_builtin(new BuiltinFunctionSymbolP());\n        add_builtin(new BuiltinFunctionKeyword());\n        add_builtin(new BuiltinFunctionKeywordP());\n        add_builtin(new BuiltinFunctionVector());\n        add_builtin(new BuiltinFunctionVectorP());\n        add_builtin(new BuiltinFunctionSequentialP());\n        add_builtin(new BuiltinFunctionHashMap());\n        add_builtin(new BuiltinFunctionMapP());\n        add_builtin(new BuiltinFunctionEmptyP());\n        add_builtin(new BuiltinFunctionFnP());\n        add_builtin(new BuiltinFunctionMacroP());\n        add_builtin(new BuiltinFunctionCount());\n        add_builtin(new BuiltinFunctionEQ());\n        add_builtin(new BuiltinFunctionLT());\n        add_builtin(new BuiltinFunctionLE());\n        add_builtin(new BuiltinFunctionGT());\n        add_builtin(new BuiltinFunctionGE());\n        add_builtin(new BuiltinFunctionAtom());\n        add_builtin(new BuiltinFunctionAtomP());\n        add_builtin(new BuiltinFunctionDeref());\n        add_builtin(new BuiltinFunctionReset());\n        add_builtin(new BuiltinFunctionSwap());\n        add_builtin(new BuiltinFunctionCons());\n        add_builtin(new BuiltinFunctionConcat());\n        add_builtin(new BuiltinFunctionVec());\n        add_builtin(new BuiltinFunctionNth());\n        add_builtin(new BuiltinFunctionFirst());\n        add_builtin(new BuiltinFunctionRest());\n        add_builtin(new BuiltinFunctionThrow());\n        add_builtin(new BuiltinFunctionApply());\n        add_builtin(new BuiltinFunctionMap());\n        add_builtin(new BuiltinFunctionAssoc());\n        add_builtin(new BuiltinFunctionDissoc());\n        add_builtin(new BuiltinFunctionGetFn());\n        add_builtin(new BuiltinFunctionContains());\n        add_builtin(new BuiltinFunctionKeys());\n        add_builtin(new BuiltinFunctionVals());\n        add_builtin(new BuiltinFunctionReadline());\n        add_builtin(new BuiltinFunctionMeta());\n        add_builtin(new BuiltinFunctionWithMeta());\n        add_builtin(new BuiltinFunctionTimeMs());\n        add_builtin(new BuiltinFunctionConj());\n        add_builtin(new BuiltinFunctionSeq());\n    }\n}\n"
  },
  {
    "path": "impls/vala/env.vala",
    "content": "class Mal.Env : GC.Object {\n    private GLib.HashTable<weak Mal.Sym, weak Mal.Val> data;\n    weak Mal.Env? outer;\n\n    construct {\n        data = new GLib.HashTable<weak Mal.Sym, weak Mal.Val>(\n            Mal.Hashable.hash, Mal.Hashable.equal);\n    }\n\n    public Env.within(Mal.Env outer_) {\n        outer = outer_;\n    }\n\n    public Env() {\n        outer = null;\n    }\n\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {\n        visit(outer);\n        foreach (var key in data.get_keys()) {\n            visit(key);\n            visit(data[key]);\n        }\n    }\n\n    public Env.funcall(Mal.Env outer_, Mal.Listlike binds, Mal.List exprs)\n    throws Mal.Error {\n        outer = outer_;\n        var binditer = binds.iter();\n        unowned GLib.List<Mal.Val> exprlist = exprs.vs;\n\n        while (binditer.nonempty()) {\n            var paramsym = binditer.deref() as Mal.Sym;\n            if (paramsym.v == \"&\") {\n                binditer.step();\n                var rest = binditer.deref();\n                binditer.step();\n                if (rest == null || binditer.nonempty())\n                    throw new Mal.Error.BAD_PARAMS(\n                        \"expected exactly one parameter name after &\");\n                set(rest as Mal.Sym, new Mal.List(exprlist.copy()));\n                return;\n            } else {\n                if (exprlist == null)\n                    throw new Mal.Error.BAD_PARAMS(\n                        \"too few arguments for function\");\n                set(paramsym, exprlist.data);\n                binditer.step();\n                exprlist = exprlist.next;\n            }\n        }\n        if (exprlist != null)\n            throw new Mal.Error.BAD_PARAMS(\"too many arguments for function\");\n    }\n\n    // Use the 'new' keyword to silence warnings about 'set' and 'get'\n    // already having meanings that we're overwriting\n    public new void set(Mal.Sym key, Mal.Val f) {\n        data[key] = f;\n    }\n\n    public new Mal.Val? get(Mal.Sym key) {\n        if (key in data)\n            return data[key];\n        if (outer == null)\n            return null;\n        return outer.get(key);\n    }\n}\n"
  },
  {
    "path": "impls/vala/gc.vala",
    "content": "abstract class GC.Object : GLib.Object {\n    public GC.Object? next;\n    public unowned GC.Object? prev;\n    public bool visited;\n\n    public delegate void VisitorFunc(GC.Object? obj);\n\n    construct {\n        next = null;\n        prev = null;\n        GC.Core.register_object(this);\n    }\n    public abstract void gc_traverse(VisitorFunc visitor);\n}\n\nclass GC.Root : GLib.Object {\n    public weak GC.Root? next;\n    public weak GC.Root? prev;\n\n    public GC.Object? obj;\n\n    construct { GC.Core.register_root(this); }\n    ~Root() { GC.Core.unregister_root(this); }\n\n    public Root.empty() { obj = null; }\n    public Root(GC.Object? obj_) { obj = obj_; }\n}\n\nclass GC.Core : GLib.Object {\n    private struct ObjectQueue {\n        GC.Object? head;\n        GC.Object? tail;\n\n        public void unlink(GC.Object obj_) {\n            GC.Object obj = obj_;\n\n            if (obj.prev == null) {\n                assert(obj == head);\n                head = obj.next;\n            }\n            else\n                obj.prev.next = obj.next;\n\n            if (obj.next == null)\n                tail = obj.prev;\n            else\n                obj.next.prev = obj.prev;\n        }\n\n        public void link(GC.Object obj) {\n            if (tail != null) {\n                tail.next = obj;\n                obj.prev = tail;\n            } else {\n                head = obj;\n                obj.prev = null;\n            }\n\n            tail = obj;\n            obj.next = null;\n        }\n    }\n\n    private static ObjectQueue objects;\n    private static weak GC.Root? roots_head;\n    private static uint until_next_collection;\n\n    static construct {\n        objects.head = objects.tail = null;\n        roots_head = null;\n    }\n\n    public static void register_object(GC.Object obj) {\n#if GC_DEBUG\n        stderr.printf(\"GC: registered %p [%s]\\n\",\n                      obj, Type.from_instance(obj).name());\n#endif\n        objects.link(obj);\n        if (until_next_collection > 0)\n            until_next_collection--;\n    }\n    public static void register_root(GC.Root root) {\n#if GC_DEBUG\n        stderr.printf(\"GC: registered root %p\\n\", root);\n#endif\n        root.next = roots_head;\n        root.prev = null;\n        if (roots_head != null)\n            roots_head.prev = root;\n        roots_head = root;\n    }\n    public static void unregister_root(GC.Root root) {\n#if GC_DEBUG\n        stderr.printf(\"GC: unregistered root %p\\n\", root);\n#endif\n        if (root.prev == null)\n            roots_head = root.next;\n        else\n            root.prev.next = root.next;\n        if (root.next != null)\n            root.next.prev = root.prev;\n    }\n\n    private static void statistics(uint before, uint after, uint roots) {\n#if GC_STATS\n        stderr.printf(\"GC: %u roots, %u -> %u objects\\n\",\n                      roots, before, after);\n#endif\n    }\n\n    public static void collect() {\n        uint orig = 0;\n        uint roots = 0;\n\n#if GC_DEBUG\n        stderr.printf(\"GC: started\\n\");\n#endif\n        for (unowned GC.Object obj = objects.head; obj != null; obj = obj.next)\n        {\n            obj.visited = false;\n#if GC_DEBUG\n            stderr.printf(\"GC: considering %p [%s]\\n\",\n                          obj, Type.from_instance(obj).name());\n#endif\n            orig++;\n        }\n\n        ObjectQueue after = { null, null };\n        until_next_collection = 0;\n\n        for (unowned GC.Root root = roots_head; root != null; root = root.next)\n        {\n            roots++;\n            if (root.obj != null && !root.obj.visited) {\n                GC.Object obj = root.obj;\n#if GC_DEBUG\n                stderr.printf(\"GC: root %p -> %p [%s]\\n\",\n                              root, obj, Type.from_instance(obj).name());\n#endif\n                objects.unlink(obj);\n                after.link(obj);\n                obj.visited = true;\n                until_next_collection++;\n            }\n        }\n\n        for (GC.Object? obj = after.head; obj != null; obj = obj.next) {\n#if GC_DEBUG\n            stderr.printf(\"GC: traversing %p [%s]\\n\",\n                          obj, Type.from_instance(obj).name());\n#endif\n            obj.gc_traverse((obj2_) => {\n                GC.Object obj2 = obj2_;\n                if (obj2 == null)\n                    return;\n                if (!obj2.visited) {\n#if GC_DEBUG\n                    stderr.printf(\"GC: %p -> %p [%s]\\n\",\n                                  obj, obj2, Type.from_instance(obj2).name());\n#endif\n                    objects.unlink(obj2);\n                    after.link(obj2);\n                    obj2.visited = true;\n                    until_next_collection++;\n                }\n            });\n        }\n\n        // Manually free everything, to avoid stack overflow while\n        // recursing down the list unreffing them all\n        objects.tail = null;\n        while (objects.head != null) {\n#if GC_DEBUG\n            stderr.printf(\"GC: collecting %p [%s]\\n\", objects.head,\n                          Type.from_instance(objects.head).name());\n#endif\n            objects.head = objects.head.next;\n        }\n\n        objects = after;\n\n#if GC_DEBUG\n        stderr.printf(\"GC: finished\\n\");\n#endif\n\n        statistics(orig, until_next_collection, roots);\n    }\n\n    public static void maybe_collect() {\n#if !GC_ALWAYS\n        if (until_next_collection > 0)\n            return;\n#endif\n        collect();\n    }\n}\n"
  },
  {
    "path": "impls/vala/printer.vala",
    "content": "namespace Mal {\n    string pr_str(Mal.Val val, bool print_readably = true) {\n        if (val is Mal.Nil)\n            return \"nil\";\n        if (val is Mal.Bool)\n            return (val as Mal.Bool).v ? \"true\" : \"false\";\n        if (val is Mal.Sym)\n            return (val as Mal.Sym).v;\n        if (val is Mal.Keyword)\n            return \":\" + (val as Mal.Keyword).v;\n        if (val is Mal.Num)\n            return (\"%\"+int64.FORMAT_MODIFIER+\"d\")\n                .printf((val as Mal.Num).v);\n        if (val is Mal.String) {\n            string s = (val as Mal.String).v;\n            if (print_readably)\n                s = \"\\\"%s\\\"\".printf(s.replace(\"\\\\\", \"\\\\\\\\\")\n                                    .replace(\"\\n\", \"\\\\n\").\n                                    replace(\"\\\"\", \"\\\\\\\"\"));\n            return s;\n        }\n        if (val is Mal.Listlike) {\n            bool vec = val is Mal.Vector;\n            string toret = vec ? \"[\" : \"(\";\n            string sep = \"\";\n            for (var iter = (val as Mal.Listlike).iter();\n                 iter.nonempty(); iter.step()) {\n                toret += sep + pr_str(iter.deref(), print_readably);\n                sep = \" \";\n            }\n            toret += vec ? \"]\" : \")\";\n            return toret;\n        }\n        if (val is Mal.Hashmap) {\n            string toret = \"{\";\n            string sep = \"\";\n            var map = (val as Mal.Hashmap).vs;\n            foreach (var key in map.get_keys()) {\n                toret += (sep + pr_str(key, print_readably) + \" \" +\n                          pr_str(map[key], print_readably));\n                sep = \" \";\n            }\n            toret += \"}\";\n            return toret;\n        }\n        if (val is Mal.BuiltinFunction) {\n            return \"#<builtin:%s>\".printf((val as Mal.BuiltinFunction).name());\n        }\n        if (val is Mal.Function) {\n            return \"#<function>\";\n        }\n        if (val is Mal.Atom) {\n            return \"(atom %s)\".printf(\n                pr_str((val as Mal.Atom).v, print_readably));\n        }\n        return \"??\";\n    }\n}\n"
  },
  {
    "path": "impls/vala/reader.vala",
    "content": "class Mal.Reader : GLib.Object {\n    static Regex tok_re;\n    static Regex tok_num;\n\n    int origlen;\n    string data;\n    int pos;\n\n    string next_token;\n\n    static construct {\n        tok_re = /[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;[^\\n]*|[^\\s\\[\\]{}('\"`,;)]*)/; // comment to unconfuse emacs vala-mode \"]);\n        tok_num = /^-?[0-9]/;\n    }\n\n    private string poserr(string fmt, ...) {\n        return \"char %d: %s\".printf(origlen - data.length,\n                                    fmt.vprintf(va_list()));\n    }\n\n    private void advance() throws Error {\n        do {\n            MatchInfo info;\n            if (!tok_re.match(data, 0, out info))\n                throw new Error.BAD_TOKEN(poserr(\"bad token\"));\n\n            next_token = info.fetch(1);\n            int tokenend;\n            info.fetch_pos(1, null, out tokenend);\n            data = data[tokenend:data.length];\n        } while (next_token.has_prefix(\";\"));\n    }\n\n    public Reader(string str) throws Error {\n        data = str;\n        origlen = data.length;\n        pos = 0;\n        advance();\n    }\n\n    public string peek() throws Error {\n        return next_token;\n    }\n\n    public string next() throws Error {\n        advance();\n        return peek();\n    }\n\n    public static Mal.Val? read_str(string str) throws Error {\n        var rdr = new Reader(str);\n        if (rdr.peek() == \"\")\n            return null;\n        var toret = rdr.read_form();\n        if (rdr.peek() != \"\")\n            throw new Mal.Error.PARSE_ERROR(\n                rdr.poserr(\"trailing junk after expression\"));\n        return toret;\n    }\n\n    public Mal.Val read_form() throws Error {\n        string token = peek();\n        if (token == \"(\") {\n            next(); // eat (\n            return new Mal.List(read_list(\")\"));\n        } else {\n            return read_atom();\n        }\n    }\n\n    public GLib.List<Mal.Val> read_list(string endtok) throws Error {\n        var list = new GLib.List<Mal.Val>();\n        string token;\n        while (true) {\n            token = peek();\n            if (token == \"\")\n                throw new Mal.Error.PARSE_ERROR(poserr(\"unbalanced parens\"));\n            if (token == endtok) {\n                next(); // eat end token\n                return list;\n            }\n\n            list.append(read_form());\n        }\n    }\n\n    public Mal.Hashmap read_hashmap() throws Error {\n        var map = new Mal.Hashmap();\n        string token;\n        while (true) {\n            Mal.Val vals[2];\n            for (int i = 0; i < 2; i++) {\n                token = peek();\n                if (token == \"\")\n                    throw new Mal.Error.PARSE_ERROR(\n                        poserr(\"unbalanced braces\"));\n                if (token == \"}\") {\n                    if (i != 0)\n                        throw new Mal.Error.PARSE_ERROR(\n                            poserr(\"odd number of elements in hashmap\"));\n\n                    next(); // eat end token\n                    return map;\n                }\n\n                vals[i] = read_form();\n            }\n            map.insert(vals[0], vals[1]);\n        }\n    }\n\n    public Mal.Val read_atom() throws Error {\n        string token = peek();\n        next();\n        if (tok_num.match(token))\n            return new Mal.Num(int64.parse(token));\n        if (token.has_prefix(\":\"))\n            return new Mal.Keyword(token[1:token.length]);\n        if (token.has_prefix(\"\\\"\")) {\n            if (token.length < 2 || !token.has_suffix(\"\\\"\"))\n                throw new Mal.Error.BAD_TOKEN(\n                    poserr(\"end of input in mid-string\"));\n\n            token = token[1:token.length-1];\n\n            int end = 0;\n            int pos = 0;\n            string strval = \"\";\n\n            while ((pos = token.index_of (\"\\\\\", end)) != -1) {\n                strval += token[end:pos];\n                if (token.length - pos < 2)\n                    throw new Mal.Error.BAD_TOKEN(\n                        poserr(\"end of input in mid-string\"));\n                switch (token[pos:pos+2]) {\n                case \"\\\\\\\\\":\n                    strval += \"\\\\\"; break;\n                case \"\\\\\\\"\":\n                    strval += \"\\\"\"; break;\n                case \"\\\\n\":\n                    strval += \"\\n\"; break;\n                }\n                end = pos+2;\n            }\n            strval += token[end:token.length];\n            return new Mal.String(strval);\n        }\n        switch (token) {\n        case \"nil\":\n            return new Mal.Nil();\n        case \"true\":\n            return new Mal.Bool(true);\n        case \"false\":\n            return new Mal.Bool(false);\n        case \"[\":\n            return new Mal.Vector.from_list(read_list(\"]\"));\n        case \"{\":\n            return read_hashmap();\n        case \"'\":\n        case \"`\":\n        case \"~\":\n        case \"~@\":\n        case \"@\":\n            var list = new GLib.List<Mal.Val>();\n            list.append(new Mal.Sym(\n                            token == \"'\" ? \"quote\" :\n                            token == \"`\" ? \"quasiquote\" :\n                            token == \"~\" ? \"unquote\" :\n                            token == \"~@\" ? \"splice-unquote\" : \"deref\"));\n            list.append(read_form());\n            return new Mal.List(list);\n        case \"^\":\n            var list = new GLib.List<Mal.Val>();\n            list.append(new Mal.Sym(\"with-meta\"));\n            var metadata = read_form();\n            list.append(read_form());\n            list.append(metadata);\n            return new Mal.List(list);\n        default:\n            return new Mal.Sym(token);\n        }\n    }\n}\n"
  },
  {
    "path": "impls/vala/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/vala/step0_repl.vala",
    "content": "class Mal.Main : GLib.Object {\n    public static string? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n        } else {\n            stdout.printf(\"\\n\");\n        }\n        return line;\n    }\n\n    public static string EVAL(string expr) {\n        return expr;\n    }\n\n    public static void PRINT(string value) {\n        stdout.printf(\"%s\\n\", value);\n    }\n\n    public static bool rep() {\n        string? line = READ();\n        if (line == null)\n            return false;\n        if (line.length > 0) {\n            string value = EVAL(line);\n            PRINT(value);\n        }\n        return true;\n    }\n\n    public static int main(string[] args) {\n        while (rep());\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step1_read_print.vala",
    "content": "class Mal.Main : GLib.Object {\n    static bool eof;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    public static Mal.Val EVAL(Mal.Val expr) {\n        return expr;\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep() {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val);\n            PRINT(val);\n            GC.Core.maybe_collect();\n        }\n    }\n\n    public static int main(string[] args) {\n        while (!eof)\n            rep();\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step2_eval.vala",
    "content": "abstract class Mal.BuiltinFunctionDyadicArithmetic : Mal.BuiltinFunction {\n    public abstract int64 result(int64 a, int64 b);\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        unowned Mal.Num a = args.vs.nth_data(0) as Mal.Num;\n        unowned Mal.Num b = args.vs.nth_data(1) as Mal.Num;\n        if (a == null || b == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        return new Mal.Num(result(a.v, b.v));\n    }\n}\n\nclass Mal.BuiltinFunctionAdd : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionAdd();\n    }\n    public override string name() { return \"+\"; }\n    public override int64 result(int64 a, int64 b) { return a+b; }\n}\n\nclass Mal.BuiltinFunctionSub : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSub();\n    }\n    public override string name() { return \"-\"; }\n    public override int64 result(int64 a, int64 b) { return a-b; }\n}\n\nclass Mal.BuiltinFunctionMul : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMul();\n    }\n    public override string name() { return \"*\"; }\n    public override int64 result(int64 a, int64 b) { return a*b; }\n}\n\nclass Mal.BuiltinFunctionDiv : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionDiv();\n    }\n    public override string name() { return \"/\"; }\n    public override int64 result(int64 a, int64 b) { return a/b; }\n}\n\nclass Mal.Env : GLib.Object {\n    public GLib.HashTable<Mal.Sym, Mal.Val> data;\n    construct {\n        data = new GLib.HashTable<Mal.Sym, Mal.Val>(\n            Mal.Hashable.hash, Mal.Hashable.equal);\n    }\n    // Use the 'new' keyword to silence warnings about 'set' and 'get'\n    // already having meanings that we're overwriting\n    public new void set(Mal.Sym key, Mal.Val f) {\n        data[key] = f;\n    }\n    public new Mal.Val get(Mal.Sym key) throws Mal.Error {\n        var toret = data[key];\n        if (toret == null)\n            throw new Error.ENV_LOOKUP_FAILED(\"no such variable '%s'\", key.v);\n        return toret;\n    }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast, Mal.Env env)\n    throws Mal.Error {\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        GC.Core.maybe_collect();\n\n        //  stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n        if (ast is Mal.Sym)\n            return env.get(ast as Mal.Sym);\n        if (ast is Mal.Vector) {\n            var vec = ast as Mal.Vector;\n            var result = new Mal.Vector.with_size(vec.length);\n            var root = new GC.Root(result); (void)root;\n            for (var i = 0; i < vec.length; i++)\n                result[i] = EVAL(vec[i], env);\n            return result;\n        }\n        if (ast is Mal.Hashmap) {\n            var result = new Mal.Hashmap();\n            var root = new GC.Root(result); (void)root;\n            var map = (ast as Mal.Hashmap).vs;\n            foreach (var key in map.get_keys())\n                result.insert(key, EVAL(map[key], env));\n            return result;\n        }\n        if (ast is Mal.List) {\n            unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n            if (list.first() == null)\n                return ast;\n\n            Mal.Val firstdata = EVAL(list.first().data, env);\n            var newlist = new Mal.List.empty();\n            var root = new GC.Root(newlist); (void)root;\n            for (var iter = (ast as Mal.Listlike).iter().step(); iter.nonempty(); iter.step())\n                newlist.vs.append(EVAL(iter.deref(), env));\n\n            return (firstdata as Mal.BuiltinFunction).call(newlist);\n        } else {\n            return ast;\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n\n        env.set(new Mal.Sym(\"+\"), new BuiltinFunctionAdd());\n        env.set(new Mal.Sym(\"-\"), new BuiltinFunctionSub());\n        env.set(new Mal.Sym(\"*\"), new BuiltinFunctionMul());\n        env.set(new Mal.Sym(\"/\"), new BuiltinFunctionDiv());\n\n        while (!eof) {\n            try {\n                rep(env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step3_env.vala",
    "content": "abstract class Mal.BuiltinFunctionDyadicArithmetic : Mal.BuiltinFunction {\n    public abstract int64 result(int64 a, int64 b);\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 2)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        unowned Mal.Num a = args.vs.nth_data(0) as Mal.Num;\n        unowned Mal.Num b = args.vs.nth_data(1) as Mal.Num;\n        if (a == null || b == null)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected two numbers\", name());\n        return new Mal.Num(result(a.v, b.v));\n    }\n}\n\nclass Mal.BuiltinFunctionAdd : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionAdd();\n    }\n    public override string name() { return \"+\"; }\n    public override int64 result(int64 a, int64 b) { return a+b; }\n}\n\nclass Mal.BuiltinFunctionSub : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionSub();\n    }\n    public override string name() { return \"-\"; }\n    public override int64 result(int64 a, int64 b) { return a-b; }\n}\n\nclass Mal.BuiltinFunctionMul : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionMul();\n    }\n    public override string name() { return \"*\"; }\n    public override int64 result(int64 a, int64 b) { return a*b; }\n}\n\nclass Mal.BuiltinFunctionDiv : Mal.BuiltinFunctionDyadicArithmetic {\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionDiv();\n    }\n    public override string name() { return \"/\"; }\n    public override int64 result(int64 a, int64 b) { return a/b; }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast, Mal.Env env)\n    throws Mal.Error {\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        GC.Core.maybe_collect();\n\n        if (dbgevalsym == null)\n            dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n        var dbgeval = env.get(dbgevalsym);\n        if (dbgeval != null && dbgeval.truth_value())\n            stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n        if (ast is Mal.Sym) {\n            var key = ast as Mal.Sym;\n            var val = env.get(key);\n            if (val == null)\n                throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n            return val;\n        }\n        if (ast is Mal.Vector) {\n            var vec = ast as Mal.Vector;\n            var result = new Mal.Vector.with_size(vec.length);\n            var root = new GC.Root(result); (void)root;\n            for (var i = 0; i < vec.length; i++)\n                result[i] = EVAL(vec[i], env);\n            return result;\n        }\n        if (ast is Mal.Hashmap) {\n            var result = new Mal.Hashmap();\n            var root = new GC.Root(result); (void)root;\n            var map = (ast as Mal.Hashmap).vs;\n            foreach (var key in map.get_keys())\n                result.insert(key, EVAL(map[key], env));\n            return result;\n        }\n        if (ast is Mal.List) {\n            unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n            if (list.first() == null)\n                return ast;\n\n            var first = list.first().data;\n            if (first is Mal.Sym) {\n                var sym = first as Mal.Sym;\n                switch (sym.v) {\n                case \"def!\":\n                    if (list.length() != 3)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"def!: expected two values\");\n                    return define_eval(list.next.data, list.next.next.data,\n                                       env);\n                case \"let*\":\n                    if (list.length() != 3)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"let*: expected two values\");\n                    var defns = list.nth(1).data;\n                    var newenv = new Mal.Env.within(env);\n\n                    if (defns is Mal.List) {\n                        for (unowned GLib.List<Mal.Val> iter =\n                                 (defns as Mal.List).vs;\n                             iter != null; iter = iter.next.next) {\n                            if (iter.next == null)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length list\" +\n                                    \" of definitions\");\n                            define_eval(iter.data, iter.next.data, newenv);\n                        }\n                    } else if (defns is Mal.Vector) {\n                        var vec = defns as Mal.Vector;\n                        if (vec.length % 2 != 0)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected an even-length vector\" +\n                                \" of definitions\");\n                        for (var i = 0; i < vec.length; i += 2)\n                            define_eval(vec[i], vec[i+1], newenv);\n                    } else {\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"let*: expected a list or vector of definitions\");\n                    }\n                    return EVAL(list.nth(2).data, newenv);\n                }\n            }\n\n            Mal.Val firstdata = EVAL(list.first().data, env);\n            var newlist = new Mal.List.empty();\n            var root = new GC.Root(newlist); (void)root;\n            for (var iter = (ast as Mal.Listlike).iter().step(); iter.nonempty(); iter.step())\n                newlist.vs.append(EVAL(iter.deref(), env));\n\n            if (firstdata is Mal.BuiltinFunction) {\n                return (firstdata as Mal.BuiltinFunction).call(newlist);\n            } else {\n                throw new Mal.Error.CANNOT_APPLY(\n                    \"bad value at start of list\");\n            }\n        } else {\n            return ast;\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        env.set(new Mal.Sym(\"+\"), new BuiltinFunctionAdd());\n        env.set(new Mal.Sym(\"-\"), new BuiltinFunctionSub());\n        env.set(new Mal.Sym(\"*\"), new BuiltinFunctionMul());\n        env.set(new Mal.Sym(\"/\"), new BuiltinFunctionDiv());\n\n        while (!eof) {\n            try {\n                rep(env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step4_if_fn_do.vala",
    "content": "class Mal.Main: GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast, Mal.Env env)\n    throws Mal.Error {\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        GC.Core.maybe_collect();\n\n        if (dbgevalsym == null)\n            dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n        var dbgeval = env.get(dbgevalsym);\n        if (dbgeval != null && dbgeval.truth_value())\n            stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n        if (ast is Mal.Sym) {\n            var key = ast as Mal.Sym;\n            var val = env.get(key);\n            if (val == null)\n                throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n            return val;\n        }\n        if (ast is Mal.Vector) {\n            var vec = ast as Mal.Vector;\n            var result = new Mal.Vector.with_size(vec.length);\n            var root = new GC.Root(result); (void)root;\n            for (var i = 0; i < vec.length; i++)\n                result[i] = EVAL(vec[i], env);\n            return result;\n        }\n        if (ast is Mal.Hashmap) {\n            var result = new Mal.Hashmap();\n            var root = new GC.Root(result); (void)root;\n            var map = (ast as Mal.Hashmap).vs;\n            foreach (var key in map.get_keys())\n                result.insert(key, EVAL(map[key], env));\n            return result;\n        }\n        if (ast is Mal.List) {\n            unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n            if (list.first() == null)\n                return ast;\n\n            var first = list.first().data;\n            if (first is Mal.Sym) {\n                var sym = first as Mal.Sym;\n                switch (sym.v) {\n                case \"def!\":\n                    if (list.length() != 3)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"def!: expected two values\");\n                    return define_eval(list.next.data, list.next.next.data,\n                                       env);\n                case \"let*\":\n                    if (list.length() != 3)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"let*: expected two values\");\n                    var defns = list.nth(1).data;\n                    var newenv = new Mal.Env.within(env);\n\n                    if (defns is Mal.List) {\n                        for (unowned GLib.List<Mal.Val> iter =\n                                 (defns as Mal.List).vs;\n                             iter != null; iter = iter.next.next) {\n                            if (iter.next == null)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length list\" +\n                                    \" of definitions\");\n                            define_eval(iter.data, iter.next.data, newenv);\n                        }\n                    } else if (defns is Mal.Vector) {\n                        var vec = defns as Mal.Vector;\n                        if (vec.length % 2 != 0)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected an even-length vector\" +\n                                \" of definitions\");\n                        for (var i = 0; i < vec.length; i += 2)\n                            define_eval(vec[i], vec[i+1], newenv);\n                    } else {\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"let*: expected a list or vector of definitions\");\n                    }\n                    return EVAL(list.nth(2).data, newenv);\n                case \"do\":\n                    Mal.Val result = null;\n                    for (list = list.next; list != null; list = list.next)\n                        result = EVAL(list.data, env);\n                    if (result == null)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"do: expected at least one argument\");\n                    return result;\n                case \"if\":\n                    if (list.length() != 3 && list.length() != 4)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"if: expected two or three arguments\");\n                    list = list.next;\n                    var cond = EVAL(list.data, env);\n                    list = list.next;\n                    if (!cond.truth_value()) {\n                        // Skip to the else clause, which defaults to nil.\n                        list = list.next;\n                        if (list == null)\n                            return new Mal.Nil();\n                    }\n                    return EVAL(list.data, env);\n                case \"fn*\":\n                    if (list.length() != 3)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"fn*: expected two arguments\");\n                    var binds = list.next.data as Mal.Listlike;\n                    var body = list.next.next.data;\n                    if (binds == null)\n                        throw new Mal.Error.BAD_PARAMS(\n                            \"fn*: expected a list of parameter names\");\n                    for (var iter = binds.iter(); iter.nonempty(); iter.step())\n                        if (!(iter.deref() is Mal.Sym))\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected parameter name to be \"+\n                                \"symbol\");\n                    return new Mal.Function(binds, body, env);\n                }\n            }\n\n            Mal.Val firstdata = EVAL(list.first().data, env);\n            var newlist = new Mal.List.empty();\n            var root = new GC.Root(newlist); (void)root;\n            for (var iter = (ast as Mal.Listlike).iter().step(); iter.nonempty(); iter.step())\n                newlist.vs.append(EVAL(iter.deref(), env));\n\n            if (firstdata is Mal.BuiltinFunction) {\n                return (firstdata as Mal.BuiltinFunction).call(newlist);\n            } else if (firstdata is Mal.Function) {\n                var fn = firstdata as Mal.Function;\n                var newenv = new Mal.Env.funcall(\n                    fn.env, fn.parameters, newlist);\n                return EVAL(fn.body, newenv);\n            } else {\n                throw new Mal.Error.CANNOT_APPLY(\n                    \"bad value at start of list\");\n            }\n        } else {\n            return ast;\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n\n        try {\n            EVAL(Mal.Reader.read_str(\"(def! not (fn* (a) (if a false true)))\"),\n                 env);\n        } catch (Mal.Error err) {\n            assert(false); // shouldn't happen\n        }\n\n        while (!eof) {\n            try {\n                rep(env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step5_tco.vala",
    "content": "class Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast_, Mal.Env env_)\n    throws Mal.Error {\n        // Copy the implicitly 'unowned' function arguments into\n        // ordinary owned variables which increment the objects'\n        // reference counts. This is so that when we overwrite these\n        // variables within the loop (for TCO) the objects we assign\n        // into them don't immediately get garbage-collected.\n        Mal.Val ast = ast_;\n        Mal.Env env = env_;\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        while (true) {\n            ast_root.obj = ast;\n            env_root.obj = env;\n            GC.Core.maybe_collect();\n\n            if (dbgevalsym == null)\n                dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n            var dbgeval = env.get(dbgevalsym);\n            if (dbgeval != null && dbgeval.truth_value())\n                stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n            if (ast is Mal.Sym) {\n                var key = ast as Mal.Sym;\n                var val = env.get(key);\n                if (val == null)\n                    throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n                return val;\n            }\n            if (ast is Mal.Vector) {\n                var vec = ast as Mal.Vector;\n                var result = new Mal.Vector.with_size(vec.length);\n                var root = new GC.Root(result); (void)root;\n                for (var i = 0; i < vec.length; i++)\n                    result[i] = EVAL(vec[i], env);\n                return result;\n            }\n            if (ast is Mal.Hashmap) {\n                var result = new Mal.Hashmap();\n                var root = new GC.Root(result); (void)root;\n                var map = (ast as Mal.Hashmap).vs;\n                foreach (var key in map.get_keys())\n                    result.insert(key, EVAL(map[key], env));\n                return result;\n            }\n            if (ast is Mal.List) {\n                unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n                if (list.first() == null)\n                    return ast;\n\n                var first = list.first().data;\n                if (first is Mal.Sym) {\n                    var sym = first as Mal.Sym;\n                    switch (sym.v) {\n                    case \"def!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"def!: expected two values\");\n                        return define_eval(list.next.data, list.next.next.data,\n                                           env);\n                    case \"let*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected two values\");\n                        var defns = list.nth(1).data;\n                        env = new Mal.Env.within(env);\n\n                        if (defns is Mal.List) {\n                            for (unowned GLib.List<Mal.Val> iter =\n                                     (defns as Mal.List).vs;\n                                 iter != null; iter = iter.next.next) {\n                                if (iter.next == null)\n                                    throw new Mal.Error.BAD_PARAMS(\n                                        \"let*: expected an even-length list\" +\n                                        \" of definitions\");\n                                define_eval(iter.data, iter.next.data, env);\n                            }\n                        } else if (defns is Mal.Vector) {\n                            var vec = defns as Mal.Vector;\n                            if (vec.length % 2 != 0)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length vector\" +\n                                    \" of definitions\");\n                            for (var i = 0; i < vec.length; i += 2)\n                                define_eval(vec[i], vec[i+1], env);\n                        } else {\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected a list or vector of definitions\");\n                        }\n                        ast = list.nth(2).data;\n                        continue;      // tail-call optimisation\n                    case \"do\":\n                        Mal.Val result = null;\n                        for (list = list.next; list != null; list = list.next)\n                            result = EVAL(list.data, env);\n                        if (result == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"do: expected at least one argument\");\n                        return result;\n                    case \"if\":\n                        if (list.length() != 3 && list.length() != 4)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"if: expected two or three arguments\");\n                        list = list.next;\n                        var cond = EVAL(list.data, env);\n                        list = list.next;\n                        if (!cond.truth_value()) {\n                            // Skip to the else clause, which defaults to nil.\n                            list = list.next;\n                            if (list == null)\n                                return new Mal.Nil();\n                        }\n                        ast = list.data;\n                        continue;      // tail-call optimisation\n                    case \"fn*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected two arguments\");\n                        var binds = list.next.data as Mal.Listlike;\n                        var body = list.next.next.data;\n                        if (binds == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected a list of parameter names\");\n                        for (var iter = binds.iter(); iter.nonempty(); iter.step())\n                            if (!(iter.deref() is Mal.Sym))\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"fn*: expected parameter name to be \"+\n                                    \"symbol\");\n                        return new Mal.Function(binds, body, env);\n                    }\n                }\n\n                Mal.Val firstdata = EVAL(list.first().data, env);\n                var newlist = new Mal.List.empty();\n                var root = new GC.Root(newlist); (void)root;\n                for (var iter = (ast as Mal.Listlike).iter().step();\n                     iter.nonempty(); iter.step())\n                    newlist.vs.append(EVAL(iter.deref(), env));\n\n                if (firstdata is Mal.BuiltinFunction) {\n                    return (firstdata as Mal.BuiltinFunction).call(newlist);\n                } else if (firstdata is Mal.Function) {\n                    var fn = firstdata as Mal.Function;\n                    env = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                    ast = fn.body;\n                    continue;      // tail-call optimisation\n                } else {\n                    throw new Mal.Error.CANNOT_APPLY(\n                        \"bad value at start of list\");\n                }\n            } else {\n                return ast;\n            }\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n\n        try {\n            EVAL(Mal.Reader.read_str(\"(def! not (fn* (a) (if a false true)))\"),\n                 env);\n        } catch (Mal.Error err) {\n            assert(false); // shouldn't happen\n        }\n\n        while (!eof) {\n            try {\n                rep(env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step6_file.vala",
    "content": "class Mal.BuiltinFunctionEval : Mal.BuiltinFunction {\n    public Mal.Env env;\n    public BuiltinFunctionEval(Mal.Env env_) { env = env_; }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEval(env);\n    }\n    public override string name() { return \"eval\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return Mal.Main.EVAL(args.vs.data, env);\n    }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast_, Mal.Env env_)\n    throws Mal.Error {\n        // Copy the implicitly 'unowned' function arguments into\n        // ordinary owned variables which increment the objects'\n        // reference counts. This is so that when we overwrite these\n        // variables within the loop (for TCO) the objects we assign\n        // into them don't immediately get garbage-collected.\n        Mal.Val ast = ast_;\n        Mal.Env env = env_;\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        while (true) {\n            ast_root.obj = ast;\n            env_root.obj = env;\n            GC.Core.maybe_collect();\n\n            if (dbgevalsym == null)\n                dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n            var dbgeval = env.get(dbgevalsym);\n            if (dbgeval != null && dbgeval.truth_value())\n                stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n            if (ast is Mal.Sym) {\n                var key = ast as Mal.Sym;\n                var val = env.get(key);\n                if (val == null)\n                    throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n                return val;\n            }\n            if (ast is Mal.Vector) {\n                var vec = ast as Mal.Vector;\n                var result = new Mal.Vector.with_size(vec.length);\n                var root = new GC.Root(result); (void)root;\n                for (var i = 0; i < vec.length; i++)\n                    result[i] = EVAL(vec[i], env);\n                return result;\n            }\n            if (ast is Mal.Hashmap) {\n                var result = new Mal.Hashmap();\n                var root = new GC.Root(result); (void)root;\n                var map = (ast as Mal.Hashmap).vs;\n                foreach (var key in map.get_keys())\n                    result.insert(key, EVAL(map[key], env));\n                return result;\n            }\n            if (ast is Mal.List) {\n                unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n                if (list.first() == null)\n                    return ast;\n\n                var first = list.first().data;\n                if (first is Mal.Sym) {\n                    var sym = first as Mal.Sym;\n                    switch (sym.v) {\n                    case \"def!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"def!: expected two values\");\n                        return define_eval(list.next.data, list.next.next.data,\n                                           env);\n                    case \"let*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected two values\");\n                        var defns = list.nth(1).data;\n                        env = new Mal.Env.within(env);\n\n                        if (defns is Mal.List) {\n                            for (unowned GLib.List<Mal.Val> iter =\n                                     (defns as Mal.List).vs;\n                                 iter != null; iter = iter.next.next) {\n                                if (iter.next == null)\n                                    throw new Mal.Error.BAD_PARAMS(\n                                        \"let*: expected an even-length list\" +\n                                        \" of definitions\");\n                                define_eval(iter.data, iter.next.data, env);\n                            }\n                        } else if (defns is Mal.Vector) {\n                            var vec = defns as Mal.Vector;\n                            if (vec.length % 2 != 0)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length vector\" +\n                                    \" of definitions\");\n                            for (var i = 0; i < vec.length; i += 2)\n                                define_eval(vec[i], vec[i+1], env);\n                        } else {\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected a list or vector of definitions\");\n                        }\n                        ast = list.nth(2).data;\n                        continue;      // tail-call optimisation\n                    case \"do\":\n                        Mal.Val result = null;\n                        for (list = list.next; list != null; list = list.next)\n                            result = EVAL(list.data, env);\n                        if (result == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"do: expected at least one argument\");\n                        return result;\n                    case \"if\":\n                        if (list.length() != 3 && list.length() != 4)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"if: expected two or three arguments\");\n                        list = list.next;\n                        var cond = EVAL(list.data, env);\n                        list = list.next;\n                        if (!cond.truth_value()) {\n                            // Skip to the else clause, which defaults to nil.\n                            list = list.next;\n                            if (list == null)\n                                return new Mal.Nil();\n                        }\n                        ast = list.data;\n                        continue;      // tail-call optimisation\n                    case \"fn*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected two arguments\");\n                        var binds = list.next.data as Mal.Listlike;\n                        var body = list.next.next.data;\n                        if (binds == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected a list of parameter names\");\n                        for (var iter = binds.iter(); iter.nonempty(); iter.step())\n                            if (!(iter.deref() is Mal.Sym))\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"fn*: expected parameter name to be \"+\n                                    \"symbol\");\n                        return new Mal.Function(binds, body, env);\n                    }\n                }\n\n                Mal.Val firstdata = EVAL(list.first().data, env);\n                var newlist = new Mal.List.empty();\n                var root = new GC.Root(newlist); (void)root;\n                for (var iter = (ast as Mal.Listlike).iter().step();\n                     iter.nonempty(); iter.step())\n                    newlist.vs.append(EVAL(iter.deref(), env));\n\n                if (firstdata is Mal.BuiltinFunction) {\n                    return (firstdata as Mal.BuiltinFunction).call(newlist);\n                } else if (firstdata is Mal.Function) {\n                    var fn = firstdata as Mal.Function;\n                    env = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                    ast = fn.body;\n                    continue;      // tail-call optimisation\n                } else {\n                    throw new Mal.Error.CANNOT_APPLY(\n                        \"bad value at start of list\");\n                }\n            } else {\n                return ast;\n            }\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static void setup(string line, Mal.Env env) {\n        try {\n            EVAL(Reader.read_str(line), env);\n        } catch (Mal.Error err) {\n            assert(false); // shouldn't happen\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n        env.set(new Mal.Sym(\"eval\"), new Mal.BuiltinFunctionEval(env));\n\n        setup(\"(def! not (fn* (a) (if a false true)))\", env);\n        setup(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env);\n\n        var ARGV = new GLib.List<Mal.Val>();\n        if (args.length > 1) {\n            for (int i = args.length - 1; i >= 2; i--)\n                ARGV.prepend(new Mal.String(args[i]));\n        }\n        env.set(new Mal.Sym(\"*ARGV*\"), new Mal.List(ARGV));\n\n        if (args.length > 1) {\n            var contents = new GLib.List<Mal.Val>();\n            contents.prepend(new Mal.String(args[1]));\n            contents.prepend(new Mal.Sym(\"load-file\"));\n            try {\n                EVAL(new Mal.List(contents), env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return 1;\n            }\n        } else {\n            while (!eof) {\n                try {\n                    rep(env);\n                } catch (Mal.Error err) {\n                    GLib.stderr.printf(\"%s\\n\", err.message);\n                }\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step7_quote.vala",
    "content": "class Mal.BuiltinFunctionEval : Mal.BuiltinFunction {\n    public Mal.Env env;\n    public BuiltinFunctionEval(Mal.Env env_) { env = env_; }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEval(env);\n    }\n    public override string name() { return \"eval\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return Mal.Main.EVAL(args.vs.data, env);\n    }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    //  If ast is (sym x), return x, else return null.\n    public static Mal.Val? unquoted (Mal.Val ast,\n                                     string sym)\n    throws Mal.Error {\n        var list = ast as Mal.List;\n        if (list == null || list.vs == null) return null;\n        var a0 = list.vs.data as Mal.Sym;\n        if (a0 == null || a0.v != sym) return null;\n        if (list.vs.next == null || list.vs.next.next != null)\n            throw new Mal.Error.BAD_PARAMS(sym + \": wrong arg count\");\n        return list.vs.next.data;\n    }\n\n    public static Mal.Val qq_loop(Mal.Val elt,\n                                  Mal.Val acc)\n    throws Mal.Error {\n        var list = new Mal.List.empty();\n        var unq = unquoted(elt, \"splice-unquote\");\n        if (unq != null) {\n            list.vs.append(new Mal.Sym(\"concat\"));\n            list.vs.append(unq);\n        } else {\n            list.vs.append(new Mal.Sym(\"cons\"));\n            list.vs.append(quasiquote (elt));\n        }\n        list.vs.append(acc);\n        return list;\n    }\n\n    public static Mal.Val qq_foldr(Mal.Iterator xs)\n    throws Mal.Error {\n        if (xs.empty()) {\n            return new Mal.List.empty();\n        } else {\n            var elt = xs.deref();\n            xs.step();\n            return qq_loop(elt, qq_foldr(xs));\n        }\n    }\n\n    public static Mal.Val quasiquote(Mal.Val ast)\n    throws Mal.Error {\n        if (ast is Mal.List) {\n            var unq = unquoted(ast, \"unquote\");\n            if (unq != null) {\n                return unq;\n            } else {\n                return qq_foldr((ast as Mal.List).iter());\n            }\n        } else if (ast is Mal.Vector) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"vec\"));\n            list.vs.append(qq_foldr((ast as Mal.Vector).iter()));\n            return list;\n        } else if (ast is Mal.Sym || ast is Mal.Hashmap) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"quote\"));\n            list.vs.append(ast);\n            return list;\n        } else {\n            return ast;\n        }\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast_, Mal.Env env_)\n    throws Mal.Error {\n        // Copy the implicitly 'unowned' function arguments into\n        // ordinary owned variables which increment the objects'\n        // reference counts. This is so that when we overwrite these\n        // variables within the loop (for TCO) the objects we assign\n        // into them don't immediately get garbage-collected.\n        Mal.Val ast = ast_;\n        Mal.Env env = env_;\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        while (true) {\n            ast_root.obj = ast;\n            env_root.obj = env;\n            GC.Core.maybe_collect();\n\n            if (dbgevalsym == null)\n                dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n            var dbgeval = env.get(dbgevalsym);\n            if (dbgeval != null && dbgeval.truth_value())\n                stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n            if (ast is Mal.Sym) {\n                var key = ast as Mal.Sym;\n                var val = env.get(key);\n                if (val == null)\n                    throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n                return val;\n            }\n            if (ast is Mal.Vector) {\n                var vec = ast as Mal.Vector;\n                var result = new Mal.Vector.with_size(vec.length);\n                var root = new GC.Root(result); (void)root;\n                for (var i = 0; i < vec.length; i++)\n                    result[i] = EVAL(vec[i], env);\n                return result;\n            }\n            if (ast is Mal.Hashmap) {\n                var result = new Mal.Hashmap();\n                var root = new GC.Root(result); (void)root;\n                var map = (ast as Mal.Hashmap).vs;\n                foreach (var key in map.get_keys())\n                    result.insert(key, EVAL(map[key], env));\n                return result;\n            }\n            if (ast is Mal.List) {\n                unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n                if (list.first() == null)\n                    return ast;\n\n                var first = list.first().data;\n                if (first is Mal.Sym) {\n                    var sym = first as Mal.Sym;\n                    switch (sym.v) {\n                    case \"def!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"def!: expected two values\");\n                        return define_eval(list.next.data, list.next.next.data,\n                                           env);\n                    case \"let*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected two values\");\n                        var defns = list.nth(1).data;\n                        env = new Mal.Env.within(env);\n\n                        if (defns is Mal.List) {\n                            for (unowned GLib.List<Mal.Val> iter =\n                                     (defns as Mal.List).vs;\n                                 iter != null; iter = iter.next.next) {\n                                if (iter.next == null)\n                                    throw new Mal.Error.BAD_PARAMS(\n                                        \"let*: expected an even-length list\" +\n                                        \" of definitions\");\n                                define_eval(iter.data, iter.next.data, env);\n                            }\n                        } else if (defns is Mal.Vector) {\n                            var vec = defns as Mal.Vector;\n                            if (vec.length % 2 != 0)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length vector\" +\n                                    \" of definitions\");\n                            for (var i = 0; i < vec.length; i += 2)\n                                define_eval(vec[i], vec[i+1], env);\n                        } else {\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected a list or vector of definitions\");\n                        }\n                        ast = list.nth(2).data;\n                        continue;      // tail-call optimisation\n                    case \"do\":\n                        Mal.Val result = null;\n                        for (list = list.next; list != null; list = list.next)\n                            result = EVAL(list.data, env);\n                        if (result == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"do: expected at least one argument\");\n                        return result;\n                    case \"if\":\n                        if (list.length() != 3 && list.length() != 4)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"if: expected two or three arguments\");\n                        list = list.next;\n                        var cond = EVAL(list.data, env);\n                        list = list.next;\n                        if (!cond.truth_value()) {\n                            // Skip to the else clause, which defaults to nil.\n                            list = list.next;\n                            if (list == null)\n                                return new Mal.Nil();\n                        }\n                        ast = list.data;\n                        continue;      // tail-call optimisation\n                    case \"fn*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected two arguments\");\n                        var binds = list.next.data as Mal.Listlike;\n                        var body = list.next.next.data;\n                        if (binds == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected a list of parameter names\");\n                        for (var iter = binds.iter(); iter.nonempty();\n                             iter.step())\n                            if (!(iter.deref() is Mal.Sym))\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"fn*: expected parameter name to be \"+\n                                    \"symbol\");\n                        return new Mal.Function(binds, body, env);\n                    case \"quote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quote: expected one argument\");\n                        return list.next.data;\n                    case \"quasiquote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quasiquote: expected one argument\");\n                        ast = quasiquote(list.next.data);\n                        continue;      // tail-call optimisation\n                    }\n                }\n\n                Mal.Val firstdata = EVAL(list.first().data, env);\n                var newlist = new Mal.List.empty();\n                var root = new GC.Root(newlist); (void)root;\n                for (var iter = (ast as Mal.Listlike).iter().step();\n                     iter.nonempty(); iter.step())\n                    newlist.vs.append(EVAL(iter.deref(), env));\n\n                if (firstdata is Mal.BuiltinFunction) {\n                    return (firstdata as Mal.BuiltinFunction).call(newlist);\n                } else if (firstdata is Mal.Function) {\n                    var fn = firstdata as Mal.Function;\n                    env = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                    ast = fn.body;\n                    continue;      // tail-call optimisation\n                } else {\n                    throw new Mal.Error.CANNOT_APPLY(\n                        \"bad value at start of list\");\n                }\n            } else {\n                return ast;\n            }\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static void setup(string line, Mal.Env env) {\n        try {\n            EVAL(Reader.read_str(line), env);\n        } catch (Mal.Error err) {\n            assert(false); // shouldn't happen\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n        env.set(new Mal.Sym(\"eval\"), new Mal.BuiltinFunctionEval(env));\n\n        setup(\"(def! not (fn* (a) (if a false true)))\", env);\n        setup(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env);\n\n        var ARGV = new GLib.List<Mal.Val>();\n        if (args.length > 1) {\n            for (int i = args.length - 1; i >= 2; i--)\n                ARGV.prepend(new Mal.String(args[i]));\n        }\n        env.set(new Mal.Sym(\"*ARGV*\"), new Mal.List(ARGV));\n\n        if (args.length > 1) {\n            var contents = new GLib.List<Mal.Val>();\n            contents.prepend(new Mal.String(args[1]));\n            contents.prepend(new Mal.Sym(\"load-file\"));\n            try {\n                EVAL(new Mal.List(contents), env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return 1;\n            }\n        } else {\n            while (!eof) {\n                try {\n                    rep(env);\n                } catch (Mal.Error err) {\n                    GLib.stderr.printf(\"%s\\n\", err.message);\n                }\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step8_macros.vala",
    "content": "class Mal.BuiltinFunctionEval : Mal.BuiltinFunction {\n    public Mal.Env env;\n    public BuiltinFunctionEval(Mal.Env env_) { env = env_; }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEval(env);\n    }\n    public override string name() { return \"eval\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return Mal.Main.EVAL(args.vs.data, env);\n    }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    //  If ast is (sym x), return x, else return null.\n    public static Mal.Val? unquoted (Mal.Val ast,\n                                     string sym)\n    throws Mal.Error {\n        var list = ast as Mal.List;\n        if (list == null || list.vs == null) return null;\n        var a0 = list.vs.data as Mal.Sym;\n        if (a0 == null || a0.v != sym) return null;\n        if (list.vs.next == null || list.vs.next.next != null)\n            throw new Mal.Error.BAD_PARAMS(sym + \": wrong arg count\");\n        return list.vs.next.data;\n    }\n\n    public static Mal.Val qq_loop(Mal.Val elt,\n                                  Mal.Val acc)\n    throws Mal.Error {\n        var list = new Mal.List.empty();\n        var unq = unquoted(elt, \"splice-unquote\");\n        if (unq != null) {\n            list.vs.append(new Mal.Sym(\"concat\"));\n            list.vs.append(unq);\n        } else {\n            list.vs.append(new Mal.Sym(\"cons\"));\n            list.vs.append(quasiquote (elt));\n        }\n        list.vs.append(acc);\n        return list;\n    }\n\n    public static Mal.Val qq_foldr(Mal.Iterator xs)\n    throws Mal.Error {\n        if (xs.empty()) {\n            return new Mal.List.empty();\n        } else {\n            var elt = xs.deref();\n            xs.step();\n            return qq_loop(elt, qq_foldr(xs));\n        }\n    }\n\n    public static Mal.Val quasiquote(Mal.Val ast)\n    throws Mal.Error {\n        if (ast is Mal.List) {\n            var unq = unquoted(ast, \"unquote\");\n            if (unq != null) {\n                return unq;\n            } else {\n                return qq_foldr((ast as Mal.List).iter());\n            }\n        } else if (ast is Mal.Vector) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"vec\"));\n            list.vs.append(qq_foldr((ast as Mal.Vector).iter()));\n            return list;\n        } else if (ast is Mal.Sym || ast is Mal.Hashmap) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"quote\"));\n            list.vs.append(ast);\n            return list;\n        } else {\n            return ast;\n        }\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast_, Mal.Env env_)\n    throws Mal.Error {\n        // Copy the implicitly 'unowned' function arguments into\n        // ordinary owned variables which increment the objects'\n        // reference counts. This is so that when we overwrite these\n        // variables within the loop (for TCO) the objects we assign\n        // into them don't immediately get garbage-collected.\n        Mal.Val ast = ast_;\n        Mal.Env env = env_;\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        while (true) {\n            ast_root.obj = ast;\n            env_root.obj = env;\n            GC.Core.maybe_collect();\n\n            if (dbgevalsym == null)\n                dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n            var dbgeval = env.get(dbgevalsym);\n            if (dbgeval != null && dbgeval.truth_value())\n                stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n            if (ast is Mal.Sym) {\n                var key = ast as Mal.Sym;\n                var val = env.get(key);\n                if (val == null)\n                    throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n                return val;\n            }\n            if (ast is Mal.Vector) {\n                var vec = ast as Mal.Vector;\n                var result = new Mal.Vector.with_size(vec.length);\n                var root = new GC.Root(result); (void)root;\n                for (var i = 0; i < vec.length; i++)\n                    result[i] = EVAL(vec[i], env);\n                return result;\n            }\n            if (ast is Mal.Hashmap) {\n                var result = new Mal.Hashmap();\n                var root = new GC.Root(result); (void)root;\n                var map = (ast as Mal.Hashmap).vs;\n                foreach (var key in map.get_keys())\n                    result.insert(key, EVAL(map[key], env));\n                return result;\n            }\n            if (ast is Mal.List) {\n                unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n                if (list.first() == null)\n                    return ast;\n\n                var first = list.first().data;\n                if (first is Mal.Sym) {\n                    var sym = first as Mal.Sym;\n                    switch (sym.v) {\n                    case \"def!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"def!: expected two values\");\n                        return define_eval(list.next.data, list.next.next.data,\n                                           env);\n                    case \"defmacro!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expected two values\");\n                        var symkey = list.next.data as Mal.Sym;\n                        if (symkey == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expects a symbol\");\n                        var val = EVAL(list.next.next.data, env) as Mal.Function;\n                        if (val == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expected a function\");\n                        val = val.copy() as Mal.Function;\n                        val.is_macro = true;\n                        env.set(symkey, val);\n                        return val;\n                    case \"let*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected two values\");\n                        var defns = list.nth(1).data;\n                        env = new Mal.Env.within(env);\n\n                        if (defns is Mal.List) {\n                            for (unowned GLib.List<Mal.Val> iter =\n                                     (defns as Mal.List).vs;\n                                 iter != null; iter = iter.next.next) {\n                                if (iter.next == null)\n                                    throw new Mal.Error.BAD_PARAMS(\n                                        \"let*: expected an even-length list\" +\n                                        \" of definitions\");\n                                define_eval(iter.data, iter.next.data, env);\n                            }\n                        } else if (defns is Mal.Vector) {\n                            var vec = defns as Mal.Vector;\n                            if (vec.length % 2 != 0)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length vector\" +\n                                    \" of definitions\");\n                            for (var i = 0; i < vec.length; i += 2)\n                                define_eval(vec[i], vec[i+1], env);\n                        } else {\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected a list or vector of definitions\");\n                        }\n                        ast = list.nth(2).data;\n                        continue;      // tail-call optimisation\n                    case \"do\":\n                        Mal.Val result = null;\n                        for (list = list.next; list != null; list = list.next)\n                            result = EVAL(list.data, env);\n                        if (result == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"do: expected at least one argument\");\n                        return result;\n                    case \"if\":\n                        if (list.length() != 3 && list.length() != 4)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"if: expected two or three arguments\");\n                        list = list.next;\n                        var cond = EVAL(list.data, env);\n                        list = list.next;\n                        if (!cond.truth_value()) {\n                            // Skip to the else clause, which defaults to nil.\n                            list = list.next;\n                            if (list == null)\n                                return new Mal.Nil();\n                        }\n                        ast = list.data;\n                        continue;      // tail-call optimisation\n                    case \"fn*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected two arguments\");\n                        var binds = list.next.data as Mal.Listlike;\n                        var body = list.next.next.data;\n                        if (binds == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected a list of parameter names\");\n                        for (var iter = binds.iter(); iter.nonempty();\n                             iter.step())\n                            if (!(iter.deref() is Mal.Sym))\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"fn*: expected parameter name to be \"+\n                                    \"symbol\");\n                        return new Mal.Function(binds, body, env);\n                    case \"quote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quote: expected one argument\");\n                        return list.next.data;\n                    case \"quasiquote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quasiquote: expected one argument\");\n                        ast = quasiquote(list.next.data);\n                        continue;      // tail-call optimisation\n                    }\n                }\n\n                Mal.Val firstdata = EVAL(list.first().data, env);\n                var newlist = new Mal.List.empty();\n                var root = new GC.Root(newlist); (void)root;\n                var iter = (ast as Mal.Listlike).iter().step();\n\n                if (firstdata is Mal.BuiltinFunction) {\n                    for (; iter.nonempty(); iter.step())\n                        newlist.vs.append(EVAL(iter.deref(), env));\n                    return (firstdata as Mal.BuiltinFunction).call(newlist);\n                } else if (firstdata is Mal.Function) {\n                    var fn = firstdata as Mal.Function;\n                    if (fn.is_macro) {\n                        for (; iter.nonempty(); iter.step())\n                            newlist.vs.append(iter.deref());\n                        var fenv = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                        ast = EVAL(fn.body, fenv);\n                        continue;\n                    }\n                    for (; iter.nonempty(); iter.step())\n                        newlist.vs.append(EVAL(iter.deref(), env));\n                    env = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                    ast = fn.body;\n                    continue;      // tail-call optimisation\n                } else {\n                    throw new Mal.Error.CANNOT_APPLY(\n                        \"bad value at start of list\");\n                }\n            } else {\n                return ast;\n            }\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static void setup(string line, Mal.Env env) {\n        try {\n            EVAL(Reader.read_str(line), env);\n        } catch (Mal.Error err) {\n            assert(false); // shouldn't happen\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n        env.set(new Mal.Sym(\"eval\"), new Mal.BuiltinFunctionEval(env));\n\n        setup(\"(def! not (fn* (a) (if a false true)))\", env);\n        setup(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env);\n        setup(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", env);\n\n        var ARGV = new GLib.List<Mal.Val>();\n        if (args.length > 1) {\n            for (int i = args.length - 1; i >= 2; i--)\n                ARGV.prepend(new Mal.String(args[i]));\n        }\n        env.set(new Mal.Sym(\"*ARGV*\"), new Mal.List(ARGV));\n\n        if (args.length > 1) {\n            var contents = new GLib.List<Mal.Val>();\n            contents.prepend(new Mal.String(args[1]));\n            contents.prepend(new Mal.Sym(\"load-file\"));\n            try {\n                EVAL(new Mal.List(contents), env);\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return 1;\n            }\n        } else {\n            while (!eof) {\n                try {\n                    rep(env);\n                } catch (Mal.Error err) {\n                    GLib.stderr.printf(\"%s\\n\", err.message);\n                }\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/step9_try.vala",
    "content": "class Mal.BuiltinFunctionEval : Mal.BuiltinFunction {\n    public Mal.Env env;\n    public BuiltinFunctionEval(Mal.Env env_) { env = env_; }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEval(env);\n    }\n    public override string name() { return \"eval\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return Mal.Main.EVAL(args.vs.data, env);\n    }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                Mal.BuiltinFunctionThrow.clear();\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    //  If ast is (sym x), return x, else return null.\n    public static Mal.Val? unquoted (Mal.Val ast,\n                                     string sym)\n    throws Mal.Error {\n        var list = ast as Mal.List;\n        if (list == null || list.vs == null) return null;\n        var a0 = list.vs.data as Mal.Sym;\n        if (a0 == null || a0.v != sym) return null;\n        if (list.vs.next == null || list.vs.next.next != null)\n            throw new Mal.Error.BAD_PARAMS(sym + \": wrong arg count\");\n        return list.vs.next.data;\n    }\n\n    public static Mal.Val qq_loop(Mal.Val elt,\n                                  Mal.Val acc)\n    throws Mal.Error {\n        var list = new Mal.List.empty();\n        var unq = unquoted(elt, \"splice-unquote\");\n        if (unq != null) {\n            list.vs.append(new Mal.Sym(\"concat\"));\n            list.vs.append(unq);\n        } else {\n            list.vs.append(new Mal.Sym(\"cons\"));\n            list.vs.append(quasiquote (elt));\n        }\n        list.vs.append(acc);\n        return list;\n    }\n\n    public static Mal.Val qq_foldr(Mal.Iterator xs)\n    throws Mal.Error {\n        if (xs.empty()) {\n            return new Mal.List.empty();\n        } else {\n            var elt = xs.deref();\n            xs.step();\n            return qq_loop(elt, qq_foldr(xs));\n        }\n    }\n\n    public static Mal.Val quasiquote(Mal.Val ast)\n    throws Mal.Error {\n        if (ast is Mal.List) {\n            var unq = unquoted(ast, \"unquote\");\n            if (unq != null) {\n                return unq;\n            } else {\n                return qq_foldr((ast as Mal.List).iter());\n            }\n        } else if (ast is Mal.Vector) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"vec\"));\n            list.vs.append(qq_foldr((ast as Mal.Vector).iter()));\n            return list;\n        } else if (ast is Mal.Sym || ast is Mal.Hashmap) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"quote\"));\n            list.vs.append(ast);\n            return list;\n        } else {\n            return ast;\n        }\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast_, Mal.Env env_)\n    throws Mal.Error {\n        // Copy the implicitly 'unowned' function arguments into\n        // ordinary owned variables which increment the objects'\n        // reference counts. This is so that when we overwrite these\n        // variables within the loop (for TCO) the objects we assign\n        // into them don't immediately get garbage-collected.\n        Mal.Val ast = ast_;\n        Mal.Env env = env_;\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        while (true) {\n            ast_root.obj = ast;\n            env_root.obj = env;\n            GC.Core.maybe_collect();\n\n            if (dbgevalsym == null)\n                dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n            var dbgeval = env.get(dbgevalsym);\n            if (dbgeval != null && dbgeval.truth_value())\n                stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n            if (ast is Mal.Sym) {\n                var key = ast as Mal.Sym;\n                var val = env.get(key);\n                if (val == null)\n                    throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n                return val;\n            }\n            if (ast is Mal.Vector) {\n                var vec = ast as Mal.Vector;\n                var result = new Mal.Vector.with_size(vec.length);\n                var root = new GC.Root(result); (void)root;\n                for (var i = 0; i < vec.length; i++)\n                    result[i] = EVAL(vec[i], env);\n                return result;\n            }\n            if (ast is Mal.Hashmap) {\n                var result = new Mal.Hashmap();\n                var root = new GC.Root(result); (void)root;\n                var map = (ast as Mal.Hashmap).vs;\n                foreach (var key in map.get_keys())\n                    result.insert(key, EVAL(map[key], env));\n                return result;\n            }\n            if (ast is Mal.List) {\n                unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n                if (list.first() == null)\n                    return ast;\n\n                var first = list.first().data;\n                if (first is Mal.Sym) {\n                    var sym = first as Mal.Sym;\n                    switch (sym.v) {\n                    case \"def!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"def!: expected two values\");\n                        return define_eval(list.next.data, list.next.next.data,\n                                           env);\n                    case \"defmacro!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expected two values\");\n                        var symkey = list.next.data as Mal.Sym;\n                        if (symkey == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expects a symbol\");\n                        var val = EVAL(list.next.next.data, env) as Mal.Function;\n                        if (val == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expected a function\");\n                        val = val.copy() as Mal.Function;\n                        val.is_macro = true;\n                        env.set(symkey, val);\n                        return val;\n                    case \"let*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected two values\");\n                        var defns = list.nth(1).data;\n                        env = new Mal.Env.within(env);\n\n                        if (defns is Mal.List) {\n                            for (unowned GLib.List<Mal.Val> iter =\n                                     (defns as Mal.List).vs;\n                                 iter != null; iter = iter.next.next) {\n                                if (iter.next == null)\n                                    throw new Mal.Error.BAD_PARAMS(\n                                        \"let*: expected an even-length list\" +\n                                        \" of definitions\");\n                                define_eval(iter.data, iter.next.data, env);\n                            }\n                        } else if (defns is Mal.Vector) {\n                            var vec = defns as Mal.Vector;\n                            if (vec.length % 2 != 0)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length vector\" +\n                                    \" of definitions\");\n                            for (var i = 0; i < vec.length; i += 2)\n                                define_eval(vec[i], vec[i+1], env);\n                        } else {\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected a list or vector of definitions\");\n                        }\n                        ast = list.nth(2).data;\n                        continue;      // tail-call optimisation\n                    case \"do\":\n                        Mal.Val result = null;\n                        for (list = list.next; list != null; list = list.next)\n                            result = EVAL(list.data, env);\n                        if (result == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"do: expected at least one argument\");\n                        return result;\n                    case \"if\":\n                        if (list.length() != 3 && list.length() != 4)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"if: expected two or three arguments\");\n                        list = list.next;\n                        var cond = EVAL(list.data, env);\n                        list = list.next;\n                        if (!cond.truth_value()) {\n                            // Skip to the else clause, which defaults to nil.\n                            list = list.next;\n                            if (list == null)\n                                return new Mal.Nil();\n                        }\n                        ast = list.data;\n                        continue;      // tail-call optimisation\n                    case \"fn*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected two arguments\");\n                        var binds = list.next.data as Mal.Listlike;\n                        var body = list.next.next.data;\n                        if (binds == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected a list of parameter names\");\n                        for (var iter = binds.iter(); iter.nonempty();\n                             iter.step())\n                            if (!(iter.deref() is Mal.Sym))\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"fn*: expected parameter name to be \"+\n                                    \"symbol\");\n                        return new Mal.Function(binds, body, env);\n                    case \"quote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quote: expected one argument\");\n                        return list.next.data;\n                    case \"quasiquote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quasiquote: expected one argument\");\n                        ast = quasiquote(list.next.data);\n                        continue;      // tail-call optimisation\n                    case \"try*\":\n                        if (list.length() != 2 && list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"try*: expected one or two arguments\");\n                        var trybody = list.next.data;\n                        if (list.length() == 2) {\n                            // Trivial catchless form of try\n                            ast = trybody;\n                            continue;  // tail-call optimisation\n                        }\n                        var catchclause = list.next.next.data as Mal.List;\n                        if (!(catchclause.vs.data is Mal.Sym) ||\n                            (catchclause.vs.data as Mal.Sym).v != \"catch*\")\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"try*: expected catch*\");\n                        if (catchclause.vs.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"catch*: expected two arguments\");\n                        var catchparam = catchclause.vs.next.data as Mal.Sym;\n                        if (catchparam == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"catch*: expected a parameter name\");\n                        var catchbody = catchclause.vs.next.next.data;\n                        try {\n                            return EVAL(trybody, env);\n                        } catch (Mal.Error exc) {\n                            var catchenv = new Mal.Env.within(env);\n                            catchenv.set(catchparam, Mal.BuiltinFunctionThrow.\n                                         thrown_value(exc));\n                            ast = catchbody;\n                            env = catchenv;\n                            continue;  // tail-call optimisation\n                        }\n                    }\n                }\n\n                Mal.Val firstdata = EVAL(list.first().data, env);\n                var newlist = new Mal.List.empty();\n                var root = new GC.Root(newlist); (void)root;\n                var iter = (ast as Mal.Listlike).iter().step();\n\n                if (firstdata is Mal.BuiltinFunction) {\n                    for (; iter.nonempty(); iter.step())\n                        newlist.vs.append(EVAL(iter.deref(), env));\n                    return (firstdata as Mal.BuiltinFunction).call(newlist);\n                } else if (firstdata is Mal.Function) {\n                    var fn = firstdata as Mal.Function;\n                    if (fn.is_macro) {\n                        for (; iter.nonempty(); iter.step())\n                            newlist.vs.append(iter.deref());\n                        var fenv = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                        ast = EVAL(fn.body, fenv);\n                        continue;\n                    }\n                    for (; iter.nonempty(); iter.step())\n                        newlist.vs.append(EVAL(iter.deref(), env));\n                    env = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                    ast = fn.body;\n                    continue;      // tail-call optimisation\n                } else {\n                    throw new Mal.Error.CANNOT_APPLY(\n                        \"bad value at start of list\");\n                }\n            } else {\n                return ast;\n            }\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static void setup(string line, Mal.Env env) {\n        try {\n            EVAL(Reader.read_str(line), env);\n        } catch (Mal.Error err) {\n            stderr.printf(\"Error during setup:\\n%s\\n-> %s\\n\",\n                          line, err.message);\n            GLib.Process.exit(1);\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n        env.set(new Mal.Sym(\"eval\"), new Mal.BuiltinFunctionEval(env));\n\n        setup(\"(def! not (fn* (a) (if a false true)))\", env);\n        setup(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env);\n        setup(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", env);\n\n        var ARGV = new GLib.List<Mal.Val>();\n        if (args.length > 1) {\n            for (int i = args.length - 1; i >= 2; i--)\n                ARGV.prepend(new Mal.String(args[i]));\n        }\n        env.set(new Mal.Sym(\"*ARGV*\"), new Mal.List(ARGV));\n\n        if (args.length > 1) {\n            var contents = new GLib.List<Mal.Val>();\n            contents.prepend(new Mal.String(args[1]));\n            contents.prepend(new Mal.Sym(\"load-file\"));\n            try {\n                EVAL(new Mal.List(contents), env);\n            } catch (Mal.Error.EXCEPTION_THROWN exc) {\n                GLib.stderr.printf(\n                    \"uncaught exception: %s\\n\",\n                    pr_str(Mal.BuiltinFunctionThrow.thrown_value(exc)));\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return 1;\n            }\n        } else {\n            while (!eof) {\n                try {\n                    rep(env);\n                } catch (Mal.Error.EXCEPTION_THROWN exc) {\n                    GLib.stderr.printf(\n                        \"uncaught exception: %s\\n\",\n                        pr_str(Mal.BuiltinFunctionThrow.thrown_value(exc)));\n                } catch (Mal.Error err) {\n                    GLib.stderr.printf(\"%s\\n\", err.message);\n                }\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/stepA_mal.vala",
    "content": "class Mal.BuiltinFunctionEval : Mal.BuiltinFunction {\n    public Mal.Env env;\n    public BuiltinFunctionEval(Mal.Env env_) { env = env_; }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.BuiltinFunctionEval(env);\n    }\n    public override string name() { return \"eval\"; }\n    public override Mal.Val call(Mal.List args) throws Mal.Error {\n        if (args.vs.length() != 1)\n            throw new Mal.Error.BAD_PARAMS(\"%s: expected one argument\", name());\n        return Mal.Main.EVAL(args.vs.data, env);\n    }\n}\n\nclass Mal.Main : GLib.Object {\n    static bool eof;\n    static Mal.Sym dbgevalsym;\n\n    static construct {\n        eof = false;\n    }\n\n    public static Mal.Val? READ() {\n        string? line = Readline.readline(\"user> \");\n        if (line != null) {\n            if (line.length > 0)\n                Readline.History.add(line);\n\n            try {\n                return Reader.read_str(line);\n            } catch (Mal.Error err) {\n                Mal.BuiltinFunctionThrow.clear();\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return null;\n            }\n        } else {\n            stdout.printf(\"\\n\");\n            eof = true;\n            return null;\n        }\n    }\n\n    private static Mal.Val define_eval(Mal.Val key, Mal.Val value,\n                                       Mal.Env env)\n    throws Mal.Error {\n        var rootk = new GC.Root(key); (void)rootk;\n        var roote = new GC.Root(env); (void)roote;\n        var symkey = key as Mal.Sym;\n        if (symkey == null)\n            throw new Mal.Error.BAD_PARAMS(\n                \"let*: expected a symbol to define\");\n        var val = EVAL(value, env);\n        env.set(symkey, val);\n        return val;\n    }\n\n    //  If ast is (sym x), return x, else return null.\n    public static Mal.Val? unquoted (Mal.Val ast,\n                                     string sym)\n    throws Mal.Error {\n        var list = ast as Mal.List;\n        if (list == null || list.vs == null) return null;\n        var a0 = list.vs.data as Mal.Sym;\n        if (a0 == null || a0.v != sym) return null;\n        if (list.vs.next == null || list.vs.next.next != null)\n            throw new Mal.Error.BAD_PARAMS(sym + \": wrong arg count\");\n        return list.vs.next.data;\n    }\n\n    public static Mal.Val qq_loop(Mal.Val elt,\n                                  Mal.Val acc)\n    throws Mal.Error {\n        var list = new Mal.List.empty();\n        var unq = unquoted(elt, \"splice-unquote\");\n        if (unq != null) {\n            list.vs.append(new Mal.Sym(\"concat\"));\n            list.vs.append(unq);\n        } else {\n            list.vs.append(new Mal.Sym(\"cons\"));\n            list.vs.append(quasiquote (elt));\n        }\n        list.vs.append(acc);\n        return list;\n    }\n\n    public static Mal.Val qq_foldr(Mal.Iterator xs)\n    throws Mal.Error {\n        if (xs.empty()) {\n            return new Mal.List.empty();\n        } else {\n            var elt = xs.deref();\n            xs.step();\n            return qq_loop(elt, qq_foldr(xs));\n        }\n    }\n\n    public static Mal.Val quasiquote(Mal.Val ast)\n    throws Mal.Error {\n        if (ast is Mal.List) {\n            var unq = unquoted(ast, \"unquote\");\n            if (unq != null) {\n                return unq;\n            } else {\n                return qq_foldr((ast as Mal.List).iter());\n            }\n        } else if (ast is Mal.Vector) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"vec\"));\n            list.vs.append(qq_foldr((ast as Mal.Vector).iter()));\n            return list;\n        } else if (ast is Mal.Sym || ast is Mal.Hashmap) {\n            var list = new Mal.List.empty();\n            list.vs.append(new Mal.Sym(\"quote\"));\n            list.vs.append(ast);\n            return list;\n        } else {\n            return ast;\n        }\n    }\n\n    public static Mal.Val EVAL(Mal.Val ast_, Mal.Env env_)\n    throws Mal.Error {\n        // Copy the implicitly 'unowned' function arguments into\n        // ordinary owned variables which increment the objects'\n        // reference counts. This is so that when we overwrite these\n        // variables within the loop (for TCO) the objects we assign\n        // into them don't immediately get garbage-collected.\n        Mal.Val ast = ast_;\n        Mal.Env env = env_;\n        var ast_root = new GC.Root(ast); (void)ast_root;\n        var env_root = new GC.Root(env); (void)env_root;\n        while (true) {\n            ast_root.obj = ast;\n            env_root.obj = env;\n            GC.Core.maybe_collect();\n\n            if (dbgevalsym == null)\n                dbgevalsym = new Mal.Sym(\"DEBUG-EVAL\");\n            var dbgeval = env.get(dbgevalsym);\n            if (dbgeval != null && dbgeval.truth_value())\n                stdout.printf(\"EVAL: %s\\n\", pr_str(ast));\n\n            if (ast is Mal.Sym) {\n                var key = ast as Mal.Sym;\n                var val = env.get(key);\n                if (val == null)\n                    throw new Error.ENV_LOOKUP_FAILED(\"'%s' not found\", key.v);\n                return val;\n            }\n            if (ast is Mal.Vector) {\n                var vec = ast as Mal.Vector;\n                var result = new Mal.Vector.with_size(vec.length);\n                var root = new GC.Root(result); (void)root;\n                for (var i = 0; i < vec.length; i++)\n                    result[i] = EVAL(vec[i], env);\n                return result;\n            }\n            if (ast is Mal.Hashmap) {\n                var result = new Mal.Hashmap();\n                var root = new GC.Root(result); (void)root;\n                var map = (ast as Mal.Hashmap).vs;\n                foreach (var key in map.get_keys())\n                    result.insert(key, EVAL(map[key], env));\n                return result;\n            }\n            if (ast is Mal.List) {\n                unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;\n                if (list.first() == null)\n                    return ast;\n\n                var first = list.first().data;\n                if (first is Mal.Sym) {\n                    var sym = first as Mal.Sym;\n                    switch (sym.v) {\n                    case \"def!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"def!: expected two values\");\n                        return define_eval(list.next.data, list.next.next.data,\n                                           env);\n                    case \"defmacro!\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expected two values\");\n                        var symkey = list.next.data as Mal.Sym;\n                        if (symkey == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expects a symbol\");\n                        var val = EVAL(list.next.next.data, env) as Mal.Function;\n                        if (val == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"defmacro!: expected a function\");\n                        val = val.copy() as Mal.Function;\n                        val.is_macro = true;\n                        env.set(symkey, val);\n                        return val;\n                    case \"let*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected two values\");\n                        var defns = list.nth(1).data;\n                        env = new Mal.Env.within(env);\n\n                        if (defns is Mal.List) {\n                            for (unowned GLib.List<Mal.Val> iter =\n                                     (defns as Mal.List).vs;\n                                 iter != null; iter = iter.next.next) {\n                                if (iter.next == null)\n                                    throw new Mal.Error.BAD_PARAMS(\n                                        \"let*: expected an even-length list\" +\n                                        \" of definitions\");\n                                define_eval(iter.data, iter.next.data, env);\n                            }\n                        } else if (defns is Mal.Vector) {\n                            var vec = defns as Mal.Vector;\n                            if (vec.length % 2 != 0)\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"let*: expected an even-length vector\" +\n                                    \" of definitions\");\n                            for (var i = 0; i < vec.length; i += 2)\n                                define_eval(vec[i], vec[i+1], env);\n                        } else {\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"let*: expected a list or vector of definitions\");\n                        }\n                        ast = list.nth(2).data;\n                        continue;      // tail-call optimisation\n                    case \"do\":\n                        Mal.Val result = null;\n                        for (list = list.next; list != null; list = list.next)\n                            result = EVAL(list.data, env);\n                        if (result == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"do: expected at least one argument\");\n                        return result;\n                    case \"if\":\n                        if (list.length() != 3 && list.length() != 4)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"if: expected two or three arguments\");\n                        list = list.next;\n                        var cond = EVAL(list.data, env);\n                        list = list.next;\n                        if (!cond.truth_value()) {\n                            // Skip to the else clause, which defaults to nil.\n                            list = list.next;\n                            if (list == null)\n                                return new Mal.Nil();\n                        }\n                        ast = list.data;\n                        continue;      // tail-call optimisation\n                    case \"fn*\":\n                        if (list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected two arguments\");\n                        var binds = list.next.data as Mal.Listlike;\n                        var body = list.next.next.data;\n                        if (binds == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"fn*: expected a list of parameter names\");\n                        for (var iter = binds.iter(); iter.nonempty();\n                             iter.step())\n                            if (!(iter.deref() is Mal.Sym))\n                                throw new Mal.Error.BAD_PARAMS(\n                                    \"fn*: expected parameter name to be \"+\n                                    \"symbol\");\n                        return new Mal.Function(binds, body, env);\n                    case \"quote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quote: expected one argument\");\n                        return list.next.data;\n                    case \"quasiquote\":\n                        if (list.length() != 2)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"quasiquote: expected one argument\");\n                        ast = quasiquote(list.next.data);\n                        continue;      // tail-call optimisation\n                    case \"try*\":\n                        if (list.length() != 2 && list.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"try*: expected one or two arguments\");\n                        var trybody = list.next.data;\n                        if (list.length() == 2) {\n                            // Trivial catchless form of try\n                            ast = trybody;\n                            continue;  // tail-call optimisation\n                        }\n                        var catchclause = list.next.next.data as Mal.List;\n                        if (!(catchclause.vs.data is Mal.Sym) ||\n                            (catchclause.vs.data as Mal.Sym).v != \"catch*\")\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"try*: expected catch*\");\n                        if (catchclause.vs.length() != 3)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"catch*: expected two arguments\");\n                        var catchparam = catchclause.vs.next.data as Mal.Sym;\n                        if (catchparam == null)\n                            throw new Mal.Error.BAD_PARAMS(\n                                \"catch*: expected a parameter name\");\n                        var catchbody = catchclause.vs.next.next.data;\n                        try {\n                            return EVAL(trybody, env);\n                        } catch (Mal.Error exc) {\n                            var catchenv = new Mal.Env.within(env);\n                            catchenv.set(catchparam, Mal.BuiltinFunctionThrow.\n                                         thrown_value(exc));\n                            ast = catchbody;\n                            env = catchenv;\n                            continue;  // tail-call optimisation\n                        }\n                    }\n                }\n\n                Mal.Val firstdata = EVAL(list.first().data, env);\n                var newlist = new Mal.List.empty();\n                var root = new GC.Root(newlist); (void)root;\n                var iter = (ast as Mal.Listlike).iter().step();\n\n                if (firstdata is Mal.BuiltinFunction) {\n                    for (; iter.nonempty(); iter.step())\n                        newlist.vs.append(EVAL(iter.deref(), env));\n                    return (firstdata as Mal.BuiltinFunction).call(newlist);\n                } else if (firstdata is Mal.Function) {\n                    var fn = firstdata as Mal.Function;\n                    if (fn.is_macro) {\n                        for (; iter.nonempty(); iter.step())\n                            newlist.vs.append(iter.deref());\n                        var fenv = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                        ast = EVAL(fn.body, fenv);\n                        continue;\n                    }\n                    for (; iter.nonempty(); iter.step())\n                        newlist.vs.append(EVAL(iter.deref(), env));\n                    env = new Mal.Env.funcall(fn.env, fn.parameters, newlist);\n                    ast = fn.body;\n                    continue;      // tail-call optimisation\n                } else {\n                    throw new Mal.Error.CANNOT_APPLY(\n                        \"bad value at start of list\");\n                }\n            } else {\n                return ast;\n            }\n        }\n    }\n\n    public static void PRINT(Mal.Val value) {\n        stdout.printf(\"%s\\n\", pr_str(value));\n    }\n\n    public static void rep(Mal.Env env) throws Mal.Error {\n        Mal.Val? val = READ();\n        if (val != null) {\n            val = EVAL(val, env);\n            PRINT(val);\n        }\n    }\n\n    public static void setup(string line, Mal.Env env) {\n        try {\n            EVAL(Reader.read_str(line), env);\n        } catch (Mal.Error err) {\n            stderr.printf(\"Error during setup:\\n%s\\n-> %s\\n\",\n                          line, err.message);\n            GLib.Process.exit(1);\n        }\n    }\n\n    public static int main(string[] args) {\n        var env = new Mal.Env();\n        var root = new GC.Root(env); (void)root;\n\n        Mal.Core.make_ns();\n        foreach (var key in Mal.Core.ns.get_keys())\n            env.set(new Mal.Sym(key), Mal.Core.ns[key]);\n        env.set(new Mal.Sym(\"eval\"), new Mal.BuiltinFunctionEval(env));\n        env.set(new Mal.Sym(\"*host-language*\"), new Mal.String(\"vala\"));\n\n        setup(\"(def! not (fn* (a) (if a false true)))\", env);\n        setup(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", env);\n        setup(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", env);\n\n        var ARGV = new GLib.List<Mal.Val>();\n        if (args.length > 1) {\n            for (int i = args.length - 1; i >= 2; i--)\n                ARGV.prepend(new Mal.String(args[i]));\n        }\n        env.set(new Mal.Sym(\"*ARGV*\"), new Mal.List(ARGV));\n\n        if (args.length > 1) {\n            var contents = new GLib.List<Mal.Val>();\n            contents.prepend(new Mal.String(args[1]));\n            contents.prepend(new Mal.Sym(\"load-file\"));\n            try {\n                EVAL(new Mal.List(contents), env);\n            } catch (Mal.Error.EXCEPTION_THROWN exc) {\n                GLib.stderr.printf(\n                    \"uncaught exception: %s\\n\",\n                    pr_str(Mal.BuiltinFunctionThrow.thrown_value(exc)));\n            } catch (Mal.Error err) {\n                GLib.stderr.printf(\"%s\\n\", err.message);\n                return 1;\n            }\n        } else {\n            setup(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", env);\n            while (!eof) {\n                try {\n                    rep(env);\n                } catch (Mal.Error.EXCEPTION_THROWN exc) {\n                    GLib.stderr.printf(\n                        \"uncaught exception: %s\\n\",\n                        pr_str(Mal.BuiltinFunctionThrow.thrown_value(exc)));\n                } catch (Mal.Error err) {\n                    GLib.stderr.printf(\"%s\\n\", err.message);\n                }\n            }\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "impls/vala/types.vala",
    "content": "public errordomain Mal.Error {\n    BAD_TOKEN,\n    PARSE_ERROR,\n    HASH_KEY_TYPE_ERROR,\n    ENV_LOOKUP_FAILED,\n    BAD_PARAMS,\n    CANNOT_APPLY,\n    EXCEPTION_THROWN,\n    NOT_IMPLEMENTED_IN_THIS_STEP,\n}\n\nabstract class Mal.Val : GC.Object {\n    public abstract bool truth_value();\n}\n\nabstract class Mal.Hashable : Mal.Val {\n    public string hashkey;\n    public static uint hash(Hashable h) { return str_hash(h.hashkey); }\n    public static bool equal(Hashable hl, Hashable hr) {\n        return hl.hashkey == hr.hashkey;\n    }\n}\n\nclass Mal.Bool : Mal.Hashable {\n    public bool v;\n    public Bool(bool value) {\n        v = value;\n        hashkey = value ? \"bt\" : \"bf\";\n    }\n    public override bool truth_value() { return v; }\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {}\n}\n\n// Mal.Listlike is a subclass of Mal.Val which includes both lists and\n// vectors, and provides a common iterator API so that core functions\n// and special forms can treat them the same.\n//\n// Most core functions that take a list argument also accept nil. To\n// make that easy, Mal.Nil also derives from Mal.Listlike.\nabstract class Mal.Listlike : Mal.ValWithMetadata {\n    public abstract Mal.Iterator iter();\n}\n\nabstract class Mal.Iterator : GLib.Object {\n    public abstract Mal.Val? deref();\n    public abstract Mal.Iterator step();\n    public bool empty() { return deref() == null; }\n    public bool nonempty() { return deref() != null; }\n}\n\n// ValWithMetadata is a subclass of Mal.Val which includes every value\n// type you can put metadata on. Value types implementing this class\n// must provide a copy() method, because with-meta has to make a copy\n// of the value with new metadata.\nabstract class Mal.ValWithMetadata : Mal.Val {\n    public Mal.Val? metadata;\n    construct {\n        metadata = null;\n    }\n    public abstract Mal.ValWithMetadata copy();\n    public abstract void gc_traverse_m(GC.Object.VisitorFunc visit);\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {\n        visit(metadata);\n        gc_traverse_m(visit);\n    }\n}\n\nclass Mal.Nil : Mal.Listlike {\n    public override bool truth_value() { return false; }\n    public override Mal.Iterator iter() { return new Mal.NilIterator(); }\n    public override Mal.ValWithMetadata copy() { return new Mal.Nil(); }\n    public override void gc_traverse_m(GC.Object.VisitorFunc visit) {}\n}\n\nclass Mal.NilIterator : Mal.Iterator {\n    public override Mal.Val? deref() { return null; }\n    public override Mal.Iterator step() { return this; }\n}\n\nclass Mal.List : Mal.Listlike {\n    public GLib.List<weak Val> vs;\n    public List(GLib.List<Val> values) {\n        foreach (var value in values) {\n            vs.append(value);\n        }\n    }\n    public List.empty() {\n    }\n    public override bool truth_value() { return true; }\n    public override Mal.Iterator iter() {\n        var toret = new Mal.ListIterator();\n        toret.node = vs;\n        return toret;\n    }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.List(vs);\n    }        \n    public override void gc_traverse_m(GC.Object.VisitorFunc visit) {\n        foreach (var v in vs)\n            visit(v);\n    }\n}\n\nclass Mal.ListIterator : Mal.Iterator {\n    public unowned GLib.List<Mal.Val>? node;\n    public override Mal.Val? deref() {\n        return node == null ? null : node.data;\n    }\n    public override Mal.Iterator step() {\n        if (node != null)\n            node = node.next;\n        return this;\n    }\n}\n\nclass Mal.Vector : Mal.Listlike {\n    struct Ref { weak Mal.Val v; }\n    private Ref[] rs;\n    public Vector.from_list(GLib.List<Val> values) {\n        rs = new Ref[values.length()];\n        int i = 0;\n        foreach (var value in values) {\n            rs[i++] = { value };\n        }\n    }\n    public Vector.with_size(uint size) {\n        rs = new Ref[size];\n    }\n    private Vector.copy_of(Vector v) {\n        rs = v.rs;\n    }\n    public override bool truth_value() { return true; }\n    public override Mal.Iterator iter() {\n        var toret = new Mal.VectorIterator();\n        toret.vec = this;\n        toret.pos = 0;\n        return toret;\n    }\n    public override Mal.ValWithMetadata copy() {\n        return new Mal.Vector.copy_of(this);\n    }\n    public uint length { get { return rs.length; } }\n    public new Mal.Val @get(uint pos) {\n        assert(pos < rs.length);\n        return rs[pos].v;\n    }\n    public new void @set(uint pos, Mal.Val v) {\n        assert(pos < rs.length);\n        rs[pos].v = v;\n    }\n    public override void gc_traverse_m(GC.Object.VisitorFunc visit) {\n        foreach (var r in rs)\n            visit(r.v);\n    }\n}\n\nclass Mal.VectorIterator : Mal.Iterator {\n    public Mal.Vector vec;\n    public int pos;\n    public override Mal.Val? deref() {\n        return pos >= vec.length ? null : vec[pos];\n    }\n    public override Mal.Iterator step() {\n        if (pos < vec.length) pos++;\n        return this;\n    }\n}\n\nclass Mal.Num : Mal.Hashable {\n    public int64 v;\n    public Num(int64 value) {\n        v = value;\n        hashkey = \"N\" + v.to_string();\n    }\n    public override bool truth_value() { return true; }\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {}\n}\n\nabstract class Mal.SymBase : Mal.Hashable {\n    public string v;\n    public override bool truth_value() { return true; }\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {}\n}\n\nclass Mal.Sym : Mal.SymBase {\n    public Sym(string value) {\n        v = value;\n        hashkey = \"'\" + v;\n    }\n}\n\nclass Mal.Keyword : Mal.SymBase {\n    public Keyword(string value) {\n        v = value;\n        hashkey = \":\" + v;\n    }\n}\n\nclass Mal.String : Mal.Hashable {\n    public string v;\n    public String(string value) {\n        v = value;\n        hashkey = \"\\\"\" + v;\n    }\n    public override bool truth_value() { return true; }\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {}\n}\n\nclass Mal.Hashmap : Mal.ValWithMetadata {\n    public GLib.HashTable<weak Mal.Hashable, weak Mal.Val> vs;\n    construct {\n        vs = new GLib.HashTable<weak Mal.Hashable, weak Mal.Val>(\n            Mal.Hashable.hash, Mal.Hashable.equal);\n    }\n    public void insert(Mal.Val key, Mal.Val value) throws Mal.Error {\n        var hkey = key as Mal.Hashable;\n        if (hkey == null)\n            throw new Error.HASH_KEY_TYPE_ERROR(\"bad type as hash key\");\n        vs[hkey] = value;\n    }\n    public void remove(Mal.Val key) throws Mal.Error {\n        var hkey = key as Mal.Hashable;\n        if (hkey == null)\n            throw new Error.HASH_KEY_TYPE_ERROR(\"bad type as hash key\");\n        vs.remove(hkey);\n    }\n    public override bool truth_value() { return true; }\n    public override Mal.ValWithMetadata copy() {\n        var toret = new Mal.Hashmap();\n        toret.vs = vs;\n        return toret;\n    }        \n    public override void gc_traverse_m(GC.Object.VisitorFunc visit) {\n        foreach (var key in vs.get_keys()) {\n            visit(key);\n            visit(vs[key]);\n        }\n    }\n}\n\nabstract class Mal.BuiltinFunction : Mal.ValWithMetadata {\n    public abstract string name();\n    public abstract Mal.Val call(Mal.List args) throws Mal.Error;\n    public override bool truth_value() { return true; }\n    public override void gc_traverse_m(GC.Object.VisitorFunc visit) {}\n}\n\nclass Mal.Function : Mal.ValWithMetadata {\n    public bool is_macro;\n#if !NO_ENV\n    public weak Mal.Listlike parameters;\n    public weak Mal.Val body;\n    public weak Mal.Env env;\n    public Function(Mal.Listlike parameters_, Mal.Val body_, Mal.Env env_) {\n        parameters = parameters_;\n        body = body_;\n        env = env_;\n        is_macro = false;\n    }\n#endif\n    public override Mal.ValWithMetadata copy() {\n#if !NO_ENV\n        var copied = new Mal.Function(parameters, body, env);\n        copied.is_macro = is_macro;\n        return copied;\n#else\n        throw new Mal.Error.NOT_IMPLEMENTED_IN_THIS_STEP(\n            \"can't copy a Mal.Function without Mal.Env existing\");\n#endif\n    }\n    public override bool truth_value() { return true; }\n    public override void gc_traverse_m(GC.Object.VisitorFunc visit) {\n#if !NO_ENV\n        visit(parameters);\n        visit(body);\n        visit(env);\n#endif\n    }\n}\n\nclass Mal.Atom : Mal.Val {\n    public weak Mal.Val v;\n    public Atom(Mal.Val v_) { v = v_; }\n    public override bool truth_value() { return true; }\n    public override void gc_traverse(GC.Object.VisitorFunc visit) {\n        visit(v);\n    }\n}\n"
  },
  {
    "path": "impls/vb/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# Deps for Mono-based languages (C#, VB.Net)\nRUN apt-get -y install tzdata mono-runtime mono-mcs mono-vbnc mono-devel\n"
  },
  {
    "path": "impls/vb/Makefile",
    "content": "#####################\n\nDEBUG =\n\nSOURCES_BASE = readline.vb types.vb reader.vb printer.vb\nSOURCES_LISP = env.vb core.vb stepA_mal.vb\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\n#####################\n\nSRCS = step0_repl.vb step1_read_print.vb step2_eval.vb \\\n       step3_env.vb step4_if_fn_do.vb step5_tco.vb step6_file.vb \\\n       step7_quote.vb step8_macros.vb step9_try.vb stepA_mal.vb\n\nLIB_CS_SRCS = getline.cs\nLIB_VB_SRCS = $(filter-out step%,$(filter %.vb,$(SOURCES)))\n\nFLAGS = $(if $(strip $(DEBUG)),-debug:full,)\n\n#####################\n\nall: $(patsubst %.vb,%.exe,$(SRCS))\n\ndist: mal.exe\n\nmal.exe: $(patsubst %.vb,%.exe,$(word $(words $(SOURCES)),$(SOURCES)))\n\tcp $< $@\n\nmal_cs.dll: $(LIB_CS_SRCS)\n\tmcs $(FLAGS) -target:library $+ -out:$@\n\nmal_vb.dll: mal_cs.dll $(LIB_VB_SRCS)\n\tvbnc $(FLAGS) -target:library -r:mal_cs.dll $(LIB_VB_SRCS) -out:$@\n\n%.exe: %.vb mal_vb.dll\n\tvbnc $(FLAGS) -r:mal_vb.dll -r:mal_cs.dll $<\n\nclean:\n\trm -f *.dll *.exe *.mdb\n"
  },
  {
    "path": "impls/vb/core.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports MalVal = Mal.types.MalVal\nImports MalConstant = Mal.types.MalConstant\nImports MalInt = Mal.types.MalInt\nImports MalSymbol = Mal.types.MalSymbol\nImports MalString = Mal.types.MalString\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalAtom = Mal.types.MalAtom\nImports MalFunc = Mal.types.MalFunc\n\nNamespace Mal\n    Public Class core\n        Shared Nil As MalConstant = Mal.types.Nil\n        Shared MalTrue As MalConstant = Mal.types.MalTrue\n        Shared MalFalse As MalConstant = Mal.types.MalFalse\n\n        ' Errors/Exceptions\n        Shared Function mal_throw(a As MalList) As MalVal\n            throw New Mal.types.MalException(a(0))\n        End Function\n\n        ' General functions\n        Shared Function equal_Q(a As MalList) As MalVal\n            If Mal.types._equal_Q(a(0), a(1)) Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        ' Scalar functions\n        Shared Function nil_Q(a As MalList) As MalVal\n            If a(0) Is Nil Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function true_Q(a As MalList) As MalVal\n            If a(0) Is MalTrue Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function false_Q(a As MalList) As MalVal\n            If a(0) Is MalFalse Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function symbol(a As MalList) As MalVal\n            return new MalSymbol(DirectCast(a(0),MalString))\n        End Function\n\n        Shared Function symbol_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalSymbol Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function string_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalString Then\n                Dim s As String = DirectCast(a(0),MalString).getValue()\n                If s.Length = 0 Then\n                    return MalTrue\n                Elseif s.Substring(0,1) = Strings.ChrW(&H029e) Then\n                    return MalFalse\n                Else\n                    return MalTrue\n                End If\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function keyword(a As MalList) As MalVal\n            Dim s As String = DirectCast(a(0),MalString).getValue()\n            If s.Substring(0,1) = Strings.ChrW(&H029e) Then\n                Return a(0)\n            End If\n            return new MalString(ChrW(&H029e) & s)\n        End Function\n\n        Shared Function keyword_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalString Then\n                Dim s As String = DirectCast(a(0),MalString).getValue()\n                If s.Length = 0 Then\n                    return MalFalse\n                Elseif s.Substring(0,1) = Strings.ChrW(&H029e) Then\n                    return MalTrue\n                Else\n                    return MalFalse\n                End If\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function number_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalInt Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function fn_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalFunc AndAlso Not DirectCast(a(0),MalFunc).isMacro() Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function macro_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalFunc AndAlso DirectCast(a(0),MalFunc).isMacro() Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n\n        ' Number functions\n        Shared Function lt(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) <  DirectCast(a(1),MalInt)\n        End Function\n        Shared Function lte(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) <= DirectCast(a(1),MalInt)\n        End Function\n        Shared Function gt(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) >  DirectCast(a(1),MalInt)\n        End Function\n        Shared Function gte(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) >= DirectCast(a(1),MalInt)\n        End Function\n        Shared Function plus(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) +  DirectCast(a(1),MalInt)\n        End Function\n        Shared Function minus(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) -  DirectCast(a(1),MalInt)\n        End Function\n        Shared Function mult(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) *  DirectCast(a(1),MalInt)\n        End Function\n        Shared Function div(a As MalList) As MalVal\n            return DirectCast(a(0),MalInt) /  DirectCast(a(1),MalInt)\n        End Function\n\n        Shared Function time_ms(a As MalList) As MalVal\n            return New MalInt(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond)\n        End Function\n\n        ' String functions\n        Shared Function pr_str(a As MalList) As MalVal\n            return New MalString(printer._pr_str_args(a, \" \", true))\n        End Function\n\n        Shared Function str(a As MalList) As MalVal\n            return new MalString(printer._pr_str_args(a, \"\", false))\n        End Function\n\n        Shared Function prn(a As MalList) As MalVal\n            Console.WriteLine(printer._pr_str_args(a, \" \", true))\n            return Nil\n        End Function\n\n        Shared Function println(a As MalList) As MalVal\n            Console.WriteLine(printer._pr_str_args(a, \" \", false))\n            return Nil\n        End Function\n\n        Shared Function mal_readline(a As MalList) As MalVal\n            Dim line As String\n            line = readline.Readline(DirectCast(a(0),MalString).getValue())\n            If line Is Nothing Then\n                return types.Nil\n            Else\n                return New MalString(line)\n            End If\n        End Function\n\n        Shared Function read_string(a As MalList) As MalVal\n            return reader.read_str(DirectCast(a(0),MalString).getValue())\n        End Function\n\n        Shared Function slurp(a As MalList) As MalVal\n            return New MalString(File.ReadAllText(DirectCast(a(0),MalString).getValue()))\n        End Function\n\n\n        ' List/Vector functions\n\n        Shared Function list(a As MalList) As MalVal\n            return New MalList(a.getValue())\n        End Function\n\n        Shared Function list_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalList And Not TypeOf a(0) Is MalVector Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function vector(a As MalList) As MalVal\n            return New MalVector(a.getValue())\n        End Function\n\n        Shared Function vector_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalVector Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        ' HashMap functions\n        Shared Function hash_map(a As MalList) As MalVal\n            return New MalHashMap(a)\n        End Function\n\n        Shared Function hash_map_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalHashMap Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function contains_Q(a As MalList) As MalVal\n            Dim key As String = DirectCast(a(1),MalString).getValue()\n            Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()\n            If dict.ContainsKey(key) Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function assoc(a As MalList) As MalVal\n            Dim new_hm As MalHashMap = DirectCast(a(0),MalHashMap).copy()\n            return new_hm.assoc_BANG(DirectCast(a.slice(1),MalList))\n        End Function\n\n        Shared Function dissoc(a As MalList) As MalVal\n            Dim new_hm As MalHashMap = DirectCast(a(0),MalHashMap).copy()\n            return new_hm.dissoc_BANG(DirectCast(a.slice(1),MalList))\n        End Function\n\n        Shared Function do_get(a As MalList) As MalVal\n            Dim k As String = DirectCast(a(1),MalString).getValue()\n            If a(0) Is Nil Then\n                return Nil\n            Else\n                Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()\n                If dict.ContainsKey(k) Then\n                    return dict(k)\n                Else\n                    return Nil\n                End If\n            End If\n        End Function\n\n        Shared Function keys(a As MalList) As MalVal\n            Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()\n            Dim key_lst As MalList = New MalList()\n            For Each key As String in dict.Keys\n                key_lst.conj_BANG(new MalString(key))\n            Next\n            return key_lst\n        End Function\n\n        Shared Function vals(a As MalList) As MalVal\n            Dim dict As Dictionary(Of String,MalVal) = DirectCast(a(0),MalHashMap).getValue()\n            Dim val_lst As MalList = New MalList()\n            For Each val As MalVal In dict.Values\n                val_lst.conj_BANG(val)\n            Next\n            return val_lst\n        End Function\n\n        ' Sequence functions\n        Shared Function sequential_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalList Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function cons(a As MalList) As MalVal\n            Dim lst As New List(Of MalVal)\n            lst.Add(a(0))\n            lst.AddRange(DirectCast(a(1),MalList).getValue())\n            return DirectCast(New MalList(lst),MalVal)\n        End Function\n\n        Shared Function concat(a As MalList) As MalVal\n            If a.size() = 0 Then\n                return new MalList()\n            End If\n            Dim lst As New List(Of MalVal)\n            lst.AddRange(DirectCast(a(0),MalList).getValue())\n            for i As Integer = 1 To a.size()-1\n                lst.AddRange(DirectCast(a(i),MalList).getValue())\n            Next\n            return DirectCast(new MalList(lst),MalVal)\n        End Function\n\n        Shared Function vec(a As MalList) As MalVal\n            return New MalVector(DirectCast(a(0),MalList).getValue())\n        End Function\n\n        Shared Function nth(a As MalList) As MalVal\n            Dim idx As Integer = DirectCast(a(1),MalInt).getValue()\n            If (idx < DirectCast(a(0),MalList).size()) Then\n                return DirectCast(a(0),MalList)( idx )\n            Else\n                throw new Mal.types.MalException(\n                    \"nth: index out of range\")\n            End If\n        End Function\n\n        Shared Function first(a As MalList) As MalVal\n            If a(0) Is Nil Then\n                return Nil\n            Else\n                return DirectCast(a(0),MalList)(0)\n            End If\n        End Function\n\n        Shared Function rest(a As MalList) As MalVal\n            If a(0) Is Nil Then\n                return new MalList()\n            Else\n                return DirectCast(a(0),MalList).rest()\n            End If\n        End Function\n\n        Shared Function empty_Q(a As MalList) As MalVal\n            If DirectCast(a(0),MalList).size() = 0 Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function count(a As MalList) As MalVal\n            If a(0) Is Nil Then\n                return new MalInt(0)\n            Else\n                return new MalInt(DirectCast(a(0),MalList).size())\n            End If\n        End Function\n\n        Shared Function conj(a As MalList) As MalVal\n            Dim src_lst As List(Of MalVal) = DirectCast(a(0),MalList).getValue()\n            Dim new_lst As New List(Of MalVal)\n            new_lst.AddRange(src_lst)\n            If TypeOf a(0) Is MalVector Then\n                For i As Integer = 1 To a.size()-1\n                    new_lst.Add(a(i))\n                Next\n                return new MalVector(new_lst)\n            Else\n                For i As Integer = 1 To a.size()-1\n                    new_lst.Insert(0, a(i))\n                Next\n                return new MalList(new_lst)\n            End If\n        End Function\n\n        Shared Function seq(a As MalList) As MalVal\n            If a(0) Is Nil Then\n                return Nil\n            Elseif TypeOf a(0) is MalVector Then\n                If DirectCast(a(0),MalVector).size() = 0 Then\n                    return Nil\n                End If\n                return new MalList(DirectCast(a(0),MalVector).getValue())\n            Elseif TypeOf a(0) is MalList Then\n                If DirectCast(a(0),MalList).size() = 0 Then\n                    return Nil\n                End If\n                return a(0)\n            Elseif TypeOf a(0) is MalString Then\n                Dim s As String = DirectCast(a(0),MalString).getValue()\n                If s.Length = 0 Then\n                    return Nil\n                End If\n                Dim chars_list As New List(Of MalVal)\n                For Each c As Char In s\n                    chars_list.Add(new MalString(c.ToString()))\n                Next\n                return new MalList(chars_list)\n            Else\n                return Nil\n            End If\n        End Function\n\n        ' General list related functions\n        Shared Function apply(a As MalList) As MalVal\n            Dim f As MalFunc = DirectCast(a(0),MalFunc)\n            Dim lst As New List(Of MalVal)\n            lst.AddRange(a.slice(1,a.size()-1).getValue())\n            lst.AddRange(DirectCast(a(a.size()-1),MalList).getValue())\n            return f.apply(New MalList(lst))\n        End Function\n\n        Shared Function map(a As MalList) As MalVal\n            Dim f As MalFunc = DirectCast(a(0),MalFunc)\n            Dim src_lst As List(Of MalVal) = DirectCast(a(1),MalList).getValue()\n            Dim new_lst As New List(Of MalVal)\n            for i As Integer = 0 To  src_lst.Count-1\n                new_lst.Add(f.apply(New MalList(src_lst(i))))\n            Next\n            return new MalList(new_lst)\n        End Function\n\n\n        ' Metadata functions\n        Shared Function atom(a As MalList) As MalVal\n            return new MalAtom(a(0))\n        End Function\n\n        Shared Function meta(a As MalList) As MalVal\n            return a(0).getMeta()\n        End Function\n\n        Shared Function with_meta(a As MalList) As MalVal\n            return DirectCast(a(0),MalVal).copy().setMeta(a(1))\n        End Function\n\n\n        ' Atom functions\n        Shared Function atom_Q(a As MalList) As MalVal\n            If TypeOf a(0) Is MalAtom Then\n                return MalTrue\n            Else\n                return MalFalse\n            End If\n        End Function\n\n        Shared Function deref(a As MalList) As MalVal\n            return DirectCast(a(0),MalAtom).getValue()\n        End Function\n\n        Shared Function reset_BANG(a As MalList) As MalVal\n            return DirectCast(a(0),MalAtom).setValue(a(1))\n        End Function\n\n        Shared Function swap_BANG(a As MalList) As MalVal\n            Dim atm As MalAtom = DirectCast(a(0),MalAtom)\n            Dim f As MalFunc = DirectCast(a(1),MalFunc)\n            Dim new_lst As New List(Of MalVal)\n            new_lst.Add(atm.getValue())\n            new_lst.AddRange(DirectCast(a.slice(2),MalList).getValue())\n            return atm.setValue(f.apply(New MalList(new_lst)))\n        End Function\n\n\n\n        Shared Function ns As Dictionary(Of String, MalVal)\n            Dim ns As New Dictionary(Of String, MalVal)\n\n            ns.Add(\"=\", New MalFunc(AddressOf equal_Q))\n            ns.Add(\"throw\", New MalFunc(AddressOf mal_throw))\n            ns.Add(\"nil?\", New MalFunc(AddressOf nil_Q))\n            ns.Add(\"true?\", New MalFunc(AddressOf true_Q))\n            ns.Add(\"false?\", New MalFunc(AddressOf false_Q))\n            ns.Add(\"symbol\", new MalFunc(AddressOf symbol))\n            ns.Add(\"symbol?\", New MalFunc(AddressOf symbol_Q))\n            ns.Add(\"string?\", New MalFunc(AddressOf string_Q))\n            ns.Add(\"keyword\", new MalFunc(AddressOf keyword))\n            ns.Add(\"keyword?\", New MalFunc(AddressOf keyword_Q))\n            ns.Add(\"number?\", New MalFunc(AddressOf number_Q))\n            ns.Add(\"fn?\", New MalFunc(AddressOf fn_Q))\n            ns.Add(\"macro?\", New MalFunc(AddressOf macro_Q))\n\n            ns.Add(\"pr-str\",New MalFunc(AddressOf pr_str))\n            ns.Add(\"str\", New MalFunc(AddressOf str))\n            ns.Add(\"prn\", New MalFunc(AddressOf prn))\n            ns.Add(\"println\", New MalFunc(AddressOf println))\n            ns.Add(\"readline\", New MalFunc(AddressOf mal_readline))\n            ns.Add(\"read-string\", New MalFunc(AddressOf read_string))\n            ns.Add(\"slurp\", New MalFunc(AddressOf slurp))\n            ns.Add(\"<\",  New MalFunc(AddressOf lt))\n            ns.Add(\"<=\", New MalFunc(AddressOf lte))\n            ns.Add(\">\",  New MalFunc(AddressOf gt))\n            ns.Add(\">=\", New MalFunc(AddressOf gte))\n            ns.Add(\"+\",  New MalFunc(AddressOf plus))\n            ns.Add(\"-\",  New MalFunc(AddressOf minus))\n            ns.Add(\"*\",  New MalFunc(AddressOf mult))\n            ns.Add(\"/\",  New MalFunc(AddressOf div))\n            ns.Add(\"time-ms\", New MalFunc(AddressOf time_ms))\n\n            ns.Add(\"list\",  New MalFunc(AddressOf list))\n            ns.Add(\"list?\", New MalFunc(AddressOf list_Q))\n            ns.Add(\"vector\", new MalFunc(AddressOf vector))\n            ns.Add(\"vector?\", New MalFunc(AddressOf vector_Q))\n            ns.Add(\"hash-map\", new MalFunc(AddressOf hash_map))\n            ns.Add(\"map?\", New MalFunc(AddressOf hash_map_Q))\n            ns.Add(\"contains?\", New MalFunc(AddressOf contains_Q))\n            ns.Add(\"assoc\", New MalFunc(AddressOf assoc))\n            ns.Add(\"dissoc\", New MalFunc(AddressOf dissoc))\n            ns.Add(\"get\", New MalFunc(AddressOf do_get))\n            ns.Add(\"keys\", New MalFunc(AddressOf keys))\n            ns.Add(\"vals\", New MalFunc(AddressOf vals))\n\n            ns.Add(\"sequential?\", New MalFunc(AddressOf sequential_Q))\n            ns.Add(\"cons\", New MalFunc(AddressOf cons))\n            ns.Add(\"concat\", New MalFunc(AddressOf concat))\n            ns.Add(\"vec\", New MalFunc(AddressOf vec))\n            ns.Add(\"nth\", New MalFunc(AddressOf nth))\n            ns.Add(\"first\", New MalFunc(AddressOf first))\n            ns.Add(\"rest\",  New MalFunc(AddressOf rest))\n            ns.Add(\"empty?\", New MalFunc(AddressOf empty_Q))\n            ns.Add(\"count\",New MalFunc(AddressOf  count))\n            ns.Add(\"conj\", New MalFunc(AddressOf conj))\n            ns.Add(\"seq\", New MalFunc(AddressOf seq))\n            ns.Add(\"apply\", New MalFunc(AddressOf apply))\n            ns.Add(\"map\", New MalFunc(AddressOf map))\n\n            ns.Add(\"with-meta\", New MalFunc(AddressOf with_meta))\n            ns.Add(\"meta\", New MalFunc(AddressOf meta))\n            ns.Add(\"atom\", new MalFunc(AddressOf atom))\n            ns.Add(\"atom?\", New MalFunc(AddressOf atom_Q))\n            ns.Add(\"deref\", New MalFunc(AddressOf deref))\n            ns.Add(\"reset!\", New MalFunc(AddressOf reset_BANG))\n            ns.Add(\"swap!\", New MalFunc(AddressOf swap_BANG))\n            return ns\n        End Function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/env.vb",
    "content": "Imports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\n\nNamespace Mal\n    Public Class env\n        Public Class Env\n            Dim outer As Env = Nothing\n            Dim data As Dictionary(Of String, MalVal) = New Dictionary(Of String, MalVal)\n\n            Public Sub New(new_outer As Env)\n                outer = new_outer\n            End Sub\n            Public Sub New(new_outer As Env, binds As MalList, exprs As MalList)\n                outer = new_outer\n                For i As Integer = 0 To binds.size()-1\n                    Dim sym As String = DirectCast(binds.nth(i),MalSymbol).getName()\n                    If sym = \"&\" Then\n                        data(DirectCast(binds.nth(i+1),MalSymbol).getName()) = exprs.slice(i)\n                        Exit For\n                    Else\n                        data(sym) = exprs.nth(i)\n                    End If\n                Next\n            End Sub\n            \n            Public Function do_get(key As String) As MalVal\n                If data.ContainsKey(key) Then\n                    return data(key)\n                Else If outer IsNot Nothing Then\n                    return outer.do_get(key)\n                Else\n                    return Nothing\n                End If\n            End Function\n\n            Public Function do_set(key As MalSymbol, value As MalVal) As Env\n                data(key.getName()) = value\n                return Me\n            End Function\n        End Class\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/getline.cs",
    "content": "//\n// getline.cs: A command line editor\n//\n// Authors:\n//   Miguel de Icaza (miguel@novell.com)\n//\n// Copyright 2008 Novell, Inc.\n//\n// Dual-licensed under the terms of the MIT X11 license or the\n// Apache License 2.0\n//\n// USE -define:DEMO to build this as a standalone file and test it\n//\n// TODO:\n//    Enter an error (a = 1);  Notice how the prompt is in the wrong line\n//\t\tThis is caused by Stderr not being tracked by System.Console.\n//    Completion support\n//    Why is Thread.Interrupt not working?   Currently I resort to Abort which is too much.\n//\n// Limitations in System.Console:\n//    Console needs SIGWINCH support of some sort\n//    Console needs a way of updating its position after things have been written\n//    behind its back (P/Invoke puts for example).\n//    System.Console needs to get the DELETE character, and report accordingly.\n//\n\nusing System;\nusing System.Text;\nusing System.IO;\nusing System.Threading;\nusing System.Reflection;\n\nnamespace Mono.Terminal {\n\n\tpublic class LineEditor {\n\n\t\tpublic class Completion {\n\t\t\tpublic string [] Result;\n\t\t\tpublic string Prefix;\n\n\t\t\tpublic Completion (string prefix, string [] result)\n\t\t\t{\n\t\t\t\tPrefix = prefix;\n\t\t\t\tResult = result;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic delegate Completion AutoCompleteHandler (string text, int pos);\n\t\t\n\t\t//static StreamWriter log;\n\t\t\n\t\t// The text being edited.\n\t\tStringBuilder text;\n\n\t\t// The text as it is rendered (replaces (char)1 with ^A on display for example).\n\t\tStringBuilder rendered_text;\n\n\t\t// The prompt specified, and the prompt shown to the user.\n\t\tstring prompt;\n\t\tstring shown_prompt;\n\t\t\n\t\t// The current cursor position, indexes into \"text\", for an index\n\t\t// into rendered_text, use TextToRenderPos\n\t\tint cursor;\n\n\t\t// The row where we started displaying data.\n\t\tint home_row;\n\n\t\t// The maximum length that has been displayed on the screen\n\t\tint max_rendered;\n\n\t\t// If we are done editing, this breaks the interactive loop\n\t\tbool done = false;\n\n\t\t// The thread where the Editing started taking place\n\t\tThread edit_thread;\n\n\t\t// Our object that tracks history\n\t\tHistory history;\n\n\t\t// The contents of the kill buffer (cut/paste in Emacs parlance)\n\t\tstring kill_buffer = \"\";\n\n\t\t// The string being searched for\n\t\tstring search;\n\t\tstring last_search;\n\n\t\t// whether we are searching (-1= reverse; 0 = no; 1 = forward)\n\t\tint searching;\n\n\t\t// The position where we found the match.\n\t\tint match_at;\n\t\t\n\t\t// Used to implement the Kill semantics (multiple Alt-Ds accumulate)\n\t\tKeyHandler last_handler;\n\t\t\n\t\tdelegate void KeyHandler ();\n\t\t\n\t\tstruct Handler {\n\t\t\tpublic ConsoleKeyInfo CKI;\n\t\t\tpublic KeyHandler KeyHandler;\n\n\t\t\tpublic Handler (ConsoleKey key, KeyHandler h)\n\t\t\t{\n\t\t\t\tCKI = new ConsoleKeyInfo ((char) 0, key, false, false, false);\n\t\t\t\tKeyHandler = h;\n\t\t\t}\n\n\t\t\tpublic Handler (char c, KeyHandler h)\n\t\t\t{\n\t\t\t\tKeyHandler = h;\n\t\t\t\t// Use the \"Zoom\" as a flag that we only have a character.\n\t\t\t\tCKI = new ConsoleKeyInfo (c, ConsoleKey.Zoom, false, false, false);\n\t\t\t}\n\n\t\t\tpublic Handler (ConsoleKeyInfo cki, KeyHandler h)\n\t\t\t{\n\t\t\t\tCKI = cki;\n\t\t\t\tKeyHandler = h;\n\t\t\t}\n\t\t\t\n\t\t\tpublic static Handler Control (char c, KeyHandler h)\n\t\t\t{\n\t\t\t\treturn new Handler ((char) (c - 'A' + 1), h);\n\t\t\t}\n\n\t\t\tpublic static Handler Alt (char c, ConsoleKey k, KeyHandler h)\n\t\t\t{\n\t\t\t\tConsoleKeyInfo cki = new ConsoleKeyInfo ((char) c, k, false, true, false);\n\t\t\t\treturn new Handler (cki, h);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t///   Invoked when the user requests auto-completion using the tab character\n\t\t/// </summary>\n\t\t/// <remarks>\n\t\t///    The result is null for no values found, an array with a single\n\t\t///    string, in that case the string should be the text to be inserted\n\t\t///    for example if the word at pos is \"T\", the result for a completion\n\t\t///    of \"ToString\" should be \"oString\", not \"ToString\".\n\t\t///\n\t\t///    When there are multiple results, the result should be the full\n\t\t///    text\n\t\t/// </remarks>\n\t\tpublic AutoCompleteHandler AutoCompleteEvent;\n\t\t\n\t\tstatic Handler [] handlers;\n\n\t\tpublic LineEditor (string name) : this (name, 10) { }\n\t\t\n\t\tpublic LineEditor (string name, int histsize)\n\t\t{\n\t\t\thandlers = new Handler [] {\n\t\t\t\tnew Handler (ConsoleKey.Home,       CmdHome),\n\t\t\t\tnew Handler (ConsoleKey.End,        CmdEnd),\n\t\t\t\tnew Handler (ConsoleKey.LeftArrow,  CmdLeft),\n\t\t\t\tnew Handler (ConsoleKey.RightArrow, CmdRight),\n\t\t\t\tnew Handler (ConsoleKey.UpArrow,    CmdHistoryPrev),\n\t\t\t\tnew Handler (ConsoleKey.DownArrow,  CmdHistoryNext),\n\t\t\t\tnew Handler (ConsoleKey.Enter,      CmdDone),\n\t\t\t\tnew Handler (ConsoleKey.Backspace,  CmdBackspace),\n\t\t\t\tnew Handler (ConsoleKey.Delete,     CmdDeleteChar),\n\t\t\t\tnew Handler (ConsoleKey.Tab,        CmdTabOrComplete),\n\t\t\t\t\n\t\t\t\t// Emacs keys\n\t\t\t\tHandler.Control ('A', CmdHome),\n\t\t\t\tHandler.Control ('E', CmdEnd),\n\t\t\t\tHandler.Control ('B', CmdLeft),\n\t\t\t\tHandler.Control ('F', CmdRight),\n\t\t\t\tHandler.Control ('P', CmdHistoryPrev),\n\t\t\t\tHandler.Control ('N', CmdHistoryNext),\n\t\t\t\tHandler.Control ('K', CmdKillToEOF),\n\t\t\t\tHandler.Control ('Y', CmdYank),\n\t\t\t\tHandler.Control ('D', CmdDeleteChar),\n\t\t\t\tHandler.Control ('L', CmdRefresh),\n\t\t\t\tHandler.Control ('R', CmdReverseSearch),\n\t\t\t\tHandler.Control ('G', delegate {} ),\n\t\t\t\tHandler.Alt ('B', ConsoleKey.B, CmdBackwardWord),\n\t\t\t\tHandler.Alt ('F', ConsoleKey.F, CmdForwardWord),\n\t\t\t\t\n\t\t\t\tHandler.Alt ('D', ConsoleKey.D, CmdDeleteWord),\n\t\t\t\tHandler.Alt ((char) 8, ConsoleKey.Backspace, CmdDeleteBackword),\n\t\t\t\t\n\t\t\t\t// DEBUG\n\t\t\t\t//Handler.Control ('T', CmdDebug),\n\n\t\t\t\t// quote\n\t\t\t\tHandler.Control ('Q', delegate { HandleChar (Console.ReadKey (true).KeyChar); })\n\t\t\t};\n\n\t\t\trendered_text = new StringBuilder ();\n\t\t\ttext = new StringBuilder ();\n\n\t\t\thistory = new History (name, histsize);\n\t\t\t\n\t\t\t//if (File.Exists (\"log\"))File.Delete (\"log\");\n\t\t\t//log = File.CreateText (\"log\"); \n\t\t}\n\n\t\tvoid CmdDebug ()\n\t\t{\n\t\t\thistory.Dump ();\n\t\t\tConsole.WriteLine ();\n\t\t\tRender ();\n\t\t}\n\n\t\tvoid Render ()\n\t\t{\n\t\t\tConsole.Write (shown_prompt);\n\t\t\tConsole.Write (rendered_text);\n\n\t\t\tint max = System.Math.Max (rendered_text.Length + shown_prompt.Length, max_rendered);\n\t\t\t\n\t\t\tfor (int i = rendered_text.Length + shown_prompt.Length; i < max_rendered; i++)\n\t\t\t\tConsole.Write (' ');\n\t\t\tmax_rendered = shown_prompt.Length + rendered_text.Length;\n\n\t\t\t// Write one more to ensure that we always wrap around properly if we are at the\n\t\t\t// end of a line.\n\t\t\tConsole.Write (' ');\n\n\t\t\tUpdateHomeRow (max);\n\t\t}\n\n\t\tvoid UpdateHomeRow (int screenpos)\n\t\t{\n\t\t\tint lines = 1 + (screenpos / Console.WindowWidth);\n\n\t\t\thome_row = Console.CursorTop - (lines - 1);\n\t\t\tif (home_row < 0)\n\t\t\t\thome_row = 0;\n\t\t}\n\t\t\n\n\t\tvoid RenderFrom (int pos)\n\t\t{\n\t\t\tint rpos = TextToRenderPos (pos);\n\t\t\tint i;\n\t\t\t\n\t\t\tfor (i = rpos; i < rendered_text.Length; i++)\n\t\t\t\tConsole.Write (rendered_text [i]);\n\n\t\t\tif ((shown_prompt.Length + rendered_text.Length) > max_rendered)\n\t\t\t\tmax_rendered = shown_prompt.Length + rendered_text.Length;\n\t\t\telse {\n\t\t\t\tint max_extra = max_rendered - shown_prompt.Length;\n\t\t\t\tfor (; i < max_extra; i++)\n\t\t\t\t\tConsole.Write (' ');\n\t\t\t}\n\t\t}\n\n\t\tvoid ComputeRendered ()\n\t\t{\n\t\t\trendered_text.Length = 0;\n\n\t\t\tfor (int i = 0; i < text.Length; i++){\n\t\t\t\tint c = (int) text [i];\n\t\t\t\tif (c < 26){\n\t\t\t\t\tif (c == '\\t')\n\t\t\t\t\t\trendered_text.Append (\"    \");\n\t\t\t\t\telse {\n\t\t\t\t\t\trendered_text.Append ('^');\n\t\t\t\t\t\trendered_text.Append ((char) (c + (int) 'A' - 1));\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\trendered_text.Append ((char)c);\n\t\t\t}\n\t\t}\n\n\t\tint TextToRenderPos (int pos)\n\t\t{\n\t\t\tint p = 0;\n\n\t\t\tfor (int i = 0; i < pos; i++){\n\t\t\t\tint c;\n\n\t\t\t\tc = (int) text [i];\n\t\t\t\t\n\t\t\t\tif (c < 26){\n\t\t\t\t\tif (c == 9)\n\t\t\t\t\t\tp += 4;\n\t\t\t\t\telse\n\t\t\t\t\t\tp += 2;\n\t\t\t\t} else\n\t\t\t\t\tp++;\n\t\t\t}\n\n\t\t\treturn p;\n\t\t}\n\n\t\tint TextToScreenPos (int pos)\n\t\t{\n\t\t\treturn shown_prompt.Length + TextToRenderPos (pos);\n\t\t}\n\t\t\n\t\tstring Prompt {\n\t\t\tget { return prompt; }\n\t\t\tset { prompt = value; }\n\t\t}\n\n\t\tint LineCount {\n\t\t\tget {\n\t\t\t\treturn (shown_prompt.Length + rendered_text.Length)/Console.WindowWidth;\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid ForceCursor (int newpos)\n\t\t{\n\t\t\tcursor = newpos;\n\n\t\t\tint actual_pos = shown_prompt.Length + TextToRenderPos (cursor);\n\t\t\tint row = home_row + (actual_pos/Console.WindowWidth);\n\t\t\tint col = actual_pos % Console.WindowWidth;\n\n\t\t\tif (row >= Console.BufferHeight)\n\t\t\t\trow = Console.BufferHeight-1;\n\t\t\tConsole.SetCursorPosition (col, row);\n\t\t\t\n\t\t\t//log.WriteLine (\"Going to cursor={0} row={1} col={2} actual={3} prompt={4} ttr={5} old={6}\", newpos, row, col, actual_pos, prompt.Length, TextToRenderPos (cursor), cursor);\n\t\t\t//log.Flush ();\n\t\t}\n\n\t\tvoid UpdateCursor (int newpos)\n\t\t{\n\t\t\tif (cursor == newpos)\n\t\t\t\treturn;\n\n\t\t\tForceCursor (newpos);\n\t\t}\n\n\t\tvoid InsertChar (char c)\n\t\t{\n\t\t\tint prev_lines = LineCount;\n\t\t\ttext = text.Insert (cursor, c);\n\t\t\tComputeRendered ();\n\t\t\tif (prev_lines != LineCount){\n\n\t\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\t\tRender ();\n\t\t\t\tForceCursor (++cursor);\n\t\t\t} else {\n\t\t\t\tRenderFrom (cursor);\n\t\t\t\tForceCursor (++cursor);\n\t\t\t\tUpdateHomeRow (TextToScreenPos (cursor));\n\t\t\t}\n\t\t}\n\n\t\t//\n\t\t// Commands\n\t\t//\n\t\tvoid CmdDone ()\n\t\t{\n\t\t\tdone = true;\n\t\t}\n\n\t\tvoid CmdTabOrComplete ()\n\t\t{\n\t\t\tbool complete = false;\n\n\t\t\tif (AutoCompleteEvent != null){\n\t\t\t\tif (TabAtStartCompletes)\n\t\t\t\t\tcomplete = true;\n\t\t\t\telse {\n\t\t\t\t\tfor (int i = 0; i < cursor; i++){\n\t\t\t\t\t\tif (!Char.IsWhiteSpace (text [i])){\n\t\t\t\t\t\t\tcomplete = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (complete){\n\t\t\t\t\tCompletion completion = AutoCompleteEvent (text.ToString (), cursor);\n\t\t\t\t\tstring [] completions = completion.Result;\n\t\t\t\t\tif (completions == null)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tint ncompletions = completions.Length;\n\t\t\t\t\tif (ncompletions == 0)\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t\tif (completions.Length == 1){\n\t\t\t\t\t\tInsertTextAtCursor (completions [0]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint last = -1;\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (int p = 0; p < completions [0].Length; p++){\n\t\t\t\t\t\t\tchar c = completions [0][p];\n\n\n\t\t\t\t\t\t\tfor (int i = 1; i < ncompletions; i++){\n\t\t\t\t\t\t\t\tif (completions [i].Length < p)\n\t\t\t\t\t\t\t\t\tgoto mismatch;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (completions [i][p] != c){\n\t\t\t\t\t\t\t\t\tgoto mismatch;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlast = p;\n\t\t\t\t\t\t}\n\t\t\t\t\tmismatch:\n\t\t\t\t\t\tif (last != -1){\n\t\t\t\t\t\t\tInsertTextAtCursor (completions [0].Substring (0, last+1));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\t\tforeach (string s in completions){\n\t\t\t\t\t\t\tConsole.Write (completion.Prefix);\n\t\t\t\t\t\t\tConsole.Write (s);\n\t\t\t\t\t\t\tConsole.Write (' ');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\t\tRender ();\n\t\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\tHandleChar ('\\t');\n\t\t\t} else\n\t\t\t\tHandleChar ('t');\n\t\t}\n\t\t\n\t\tvoid CmdHome ()\n\t\t{\n\t\t\tUpdateCursor (0);\n\t\t}\n\n\t\tvoid CmdEnd ()\n\t\t{\n\t\t\tUpdateCursor (text.Length);\n\t\t}\n\t\t\n\t\tvoid CmdLeft ()\n\t\t{\n\t\t\tif (cursor == 0)\n\t\t\t\treturn;\n\n\t\t\tUpdateCursor (cursor-1);\n\t\t}\n\n\t\tvoid CmdBackwardWord ()\n\t\t{\n\t\t\tint p = WordBackward (cursor);\n\t\t\tif (p == -1)\n\t\t\t\treturn;\n\t\t\tUpdateCursor (p);\n\t\t}\n\n\t\tvoid CmdForwardWord ()\n\t\t{\n\t\t\tint p = WordForward (cursor);\n\t\t\tif (p == -1)\n\t\t\t\treturn;\n\t\t\tUpdateCursor (p);\n\t\t}\n\n\t\tvoid CmdRight ()\n\t\t{\n\t\t\tif (cursor == text.Length)\n\t\t\t\treturn;\n\n\t\t\tUpdateCursor (cursor+1);\n\t\t}\n\n\t\tvoid RenderAfter (int p)\n\t\t{\n\t\t\tForceCursor (p);\n\t\t\tRenderFrom (p);\n\t\t\tForceCursor (cursor);\n\t\t}\n\t\t\n\t\tvoid CmdBackspace ()\n\t\t{\n\t\t\tif (cursor == 0)\n\t\t\t\treturn;\n\n\t\t\ttext.Remove (--cursor, 1);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tvoid CmdDeleteChar ()\n\t\t{\n\t\t\t// If there is no input, this behaves like EOF\n\t\t\tif (text.Length == 0){\n\t\t\t\tdone = true;\n\t\t\t\ttext = null;\n\t\t\t\tConsole.WriteLine ();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (cursor == text.Length)\n\t\t\t\treturn;\n\t\t\ttext.Remove (cursor, 1);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tint WordForward (int p)\n\t\t{\n\t\t\tif (p >= text.Length)\n\t\t\t\treturn -1;\n\n\t\t\tint i = p;\n\t\t\tif (Char.IsPunctuation (text [p]) || Char.IsSymbol (text [p]) || Char.IsWhiteSpace (text[p])){\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (; i < text.Length; i++){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t    break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i != p)\n\t\t\t\treturn i;\n\t\t\treturn -1;\n\t\t}\n\n\t\tint WordBackward (int p)\n\t\t{\n\t\t\tif (p == 0)\n\t\t\t\treturn -1;\n\n\t\t\tint i = p-1;\n\t\t\tif (i == 0)\n\t\t\t\treturn 0;\n\t\t\t\n\t\t\tif (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text[i])){\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text[i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (; i >= 0; i--){\n\t\t\t\t\tif (!Char.IsLetterOrDigit (text [i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ti++;\n\t\t\t\n\t\t\tif (i != p)\n\t\t\t\treturn i;\n\n\t\t\treturn -1;\n\t\t}\n\t\t\n\t\tvoid CmdDeleteWord ()\n\t\t{\n\t\t\tint pos = WordForward (cursor);\n\n\t\t\tif (pos == -1)\n\t\t\t\treturn;\n\n\t\t\tstring k = text.ToString (cursor, pos-cursor);\n\t\t\t\n\t\t\tif (last_handler == CmdDeleteWord)\n\t\t\t\tkill_buffer = kill_buffer + k;\n\t\t\telse\n\t\t\t\tkill_buffer = k;\n\t\t\t\n\t\t\ttext.Remove (cursor, pos-cursor);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\t\t\n\t\tvoid CmdDeleteBackword ()\n\t\t{\n\t\t\tint pos = WordBackward (cursor);\n\t\t\tif (pos == -1)\n\t\t\t\treturn;\n\n\t\t\tstring k = text.ToString (pos, cursor-pos);\n\t\t\t\n\t\t\tif (last_handler == CmdDeleteBackword)\n\t\t\t\tkill_buffer = k + kill_buffer;\n\t\t\telse\n\t\t\t\tkill_buffer = k;\n\t\t\t\n\t\t\ttext.Remove (pos, cursor-pos);\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (pos);\n\t\t}\n\t\t\n\t\t//\n\t\t// Adds the current line to the history if needed\n\t\t//\n\t\tvoid HistoryUpdateLine ()\n\t\t{\n\t\t\thistory.Update (text.ToString ());\n\t\t}\n\t\t\n\t\tvoid CmdHistoryPrev ()\n\t\t{\n\t\t\tif (!history.PreviousAvailable ())\n\t\t\t\treturn;\n\n\t\t\tHistoryUpdateLine ();\n\t\t\t\n\t\t\tSetText (history.Previous ());\n\t\t}\n\n\t\tvoid CmdHistoryNext ()\n\t\t{\n\t\t\tif (!history.NextAvailable())\n\t\t\t\treturn;\n\n\t\t\thistory.Update (text.ToString ());\n\t\t\tSetText (history.Next ());\n\t\t\t\n\t\t}\n\n\t\tvoid CmdKillToEOF ()\n\t\t{\n\t\t\tkill_buffer = text.ToString (cursor, text.Length-cursor);\n\t\t\ttext.Length = cursor;\n\t\t\tComputeRendered ();\n\t\t\tRenderAfter (cursor);\n\t\t}\n\n\t\tvoid CmdYank ()\n\t\t{\n\t\t\tInsertTextAtCursor (kill_buffer);\n\t\t}\n\n\t\tvoid InsertTextAtCursor (string str)\n\t\t{\n\t\t\tint prev_lines = LineCount;\n\t\t\ttext.Insert (cursor, str);\n\t\t\tComputeRendered ();\n\t\t\tif (prev_lines != LineCount){\n\t\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\t\tRender ();\n\t\t\t\tcursor += str.Length;\n\t\t\t\tForceCursor (cursor);\n\t\t\t} else {\n\t\t\t\tRenderFrom (cursor);\n\t\t\t\tcursor += str.Length;\n\t\t\t\tForceCursor (cursor);\n\t\t\t\tUpdateHomeRow (TextToScreenPos (cursor));\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid SetSearchPrompt (string s)\n\t\t{\n\t\t\tSetPrompt (\"(reverse-i-search)`\" + s + \"': \");\n\t\t}\n\n\t\tvoid ReverseSearch ()\n\t\t{\n\t\t\tint p;\n\n\t\t\tif (cursor == text.Length){\n\t\t\t\t// The cursor is at the end of the string\n\t\t\t\t\n\t\t\t\tp = text.ToString ().LastIndexOf (search);\n\t\t\t\tif (p != -1){\n\t\t\t\t\tmatch_at = p;\n\t\t\t\t\tcursor = p;\n\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// The cursor is somewhere in the middle of the string\n\t\t\t\tint start = (cursor == match_at) ? cursor - 1 : cursor;\n\t\t\t\tif (start != -1){\n\t\t\t\t\tp = text.ToString ().LastIndexOf (search, start);\n\t\t\t\t\tif (p != -1){\n\t\t\t\t\t\tmatch_at = p;\n\t\t\t\t\t\tcursor = p;\n\t\t\t\t\t\tForceCursor (cursor);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Need to search backwards in history\n\t\t\tHistoryUpdateLine ();\n\t\t\tstring s = history.SearchBackward (search);\n\t\t\tif (s != null){\n\t\t\t\tmatch_at = -1;\n\t\t\t\tSetText (s);\n\t\t\t\tReverseSearch ();\n\t\t\t}\n\t\t}\n\t\t\n\t\tvoid CmdReverseSearch ()\n\t\t{\n\t\t\tif (searching == 0){\n\t\t\t\tmatch_at = -1;\n\t\t\t\tlast_search = search;\n\t\t\t\tsearching = -1;\n\t\t\t\tsearch = \"\";\n\t\t\t\tSetSearchPrompt (\"\");\n\t\t\t} else {\n\t\t\t\tif (search == \"\"){\n\t\t\t\t\tif (last_search != \"\" && last_search != null){\n\t\t\t\t\t\tsearch = last_search;\n\t\t\t\t\t\tSetSearchPrompt (search);\n\n\t\t\t\t\t\tReverseSearch ();\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tReverseSearch ();\n\t\t\t} \n\t\t}\n\n\t\tvoid SearchAppend (char c)\n\t\t{\n\t\t\tsearch = search + c;\n\t\t\tSetSearchPrompt (search);\n\n\t\t\t//\n\t\t\t// If the new typed data still matches the current text, stay here\n\t\t\t//\n\t\t\tif (cursor < text.Length){\n\t\t\t\tstring r = text.ToString (cursor, text.Length - cursor);\n\t\t\t\tif (r.StartsWith (search))\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tReverseSearch ();\n\t\t}\n\t\t\n\t\tvoid CmdRefresh ()\n\t\t{\n\t\t\tConsole.Clear ();\n\t\t\tmax_rendered = 0;\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\n\t\tvoid InterruptEdit (object sender, ConsoleCancelEventArgs a)\n\t\t{\n\t\t\t// Do not abort our program:\n\t\t\ta.Cancel = true;\n\n\t\t\t// Interrupt the editor\n\t\t\tedit_thread.Abort();\n\t\t}\n\n\t\tvoid HandleChar (char c)\n\t\t{\n\t\t\tif (searching != 0)\n\t\t\t\tSearchAppend (c);\n\t\t\telse\n\t\t\t\tInsertChar (c);\n\t\t}\n\n\t\tvoid EditLoop ()\n\t\t{\n\t\t\tConsoleKeyInfo cki;\n\n\t\t\twhile (!done){\n\t\t\t\tConsoleModifiers mod;\n\t\t\t\t\n\t\t\t\tcki = Console.ReadKey (true);\n\t\t\t\tif (cki.Key == ConsoleKey.Escape){\n\t\t\t\t\tcki = Console.ReadKey (true);\n\n\t\t\t\t\tmod = ConsoleModifiers.Alt;\n\t\t\t\t} else\n\t\t\t\t\tmod = cki.Modifiers;\n\t\t\t\t\n\t\t\t\tbool handled = false;\n\n\t\t\t\tforeach (Handler handler in handlers){\n\t\t\t\t\tConsoleKeyInfo t = handler.CKI;\n\n\t\t\t\t\tif (t.Key == cki.Key && t.Modifiers == mod){\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\thandler.KeyHandler ();\n\t\t\t\t\t\tlast_handler = handler.KeyHandler;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else if (t.KeyChar == cki.KeyChar && t.Key == ConsoleKey.Zoom){\n\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\thandler.KeyHandler ();\n\t\t\t\t\t\tlast_handler = handler.KeyHandler;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (handled){\n\t\t\t\t\tif (searching != 0){\n\t\t\t\t\t\tif (last_handler != CmdReverseSearch){\n\t\t\t\t\t\t\tsearching = 0;\n\t\t\t\t\t\t\tSetPrompt (prompt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (cki.KeyChar != (char) 0)\n\t\t\t\t\tHandleChar (cki.KeyChar);\n\t\t\t} \n\t\t}\n\n\t\tvoid InitText (string initial)\n\t\t{\n\t\t\ttext = new StringBuilder (initial);\n\t\t\tComputeRendered ();\n\t\t\tcursor = text.Length;\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\n\t\tvoid SetText (string newtext)\n\t\t{\n\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\tInitText (newtext);\n\t\t}\n\n\t\tvoid SetPrompt (string newprompt)\n\t\t{\n\t\t\tshown_prompt = newprompt;\n\t\t\tConsole.SetCursorPosition (0, home_row);\n\t\t\tRender ();\n\t\t\tForceCursor (cursor);\n\t\t}\n\t\t\n\t\tpublic string Edit (string prompt, string initial)\n\t\t{\n\t\t\tedit_thread = Thread.CurrentThread;\n\t\t\tsearching = 0;\n\t\t\tConsole.CancelKeyPress += InterruptEdit;\n\t\t\t\n\t\t\tdone = false;\n\t\t\thistory.CursorToEnd ();\n\t\t\tmax_rendered = 0;\n\t\t\t\n\t\t\tPrompt = prompt;\n\t\t\tshown_prompt = prompt;\n\t\t\tInitText (initial);\n\t\t\thistory.Append (initial);\n\n\t\t\tdo {\n\t\t\t\ttry {\n\t\t\t\t\tEditLoop ();\n\t\t\t\t} catch (ThreadAbortException){\n\t\t\t\t\tsearching = 0;\n\t\t\t\t\tThread.ResetAbort ();\n\t\t\t\t\tConsole.WriteLine ();\n\t\t\t\t\tSetPrompt (prompt);\n\t\t\t\t\tSetText (\"\");\n\t\t\t\t}\n\t\t\t} while (!done);\n\t\t\tConsole.WriteLine ();\n\t\t\t\n\t\t\tConsole.CancelKeyPress -= InterruptEdit;\n\n\t\t\tif (text == null){\n\t\t\t\thistory.Close ();\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tstring result = text.ToString ();\n\t\t\tif (result != \"\")\n\t\t\t\thistory.Accept (result);\n\t\t\telse\n\t\t\t\thistory.RemoveLast ();\n\n\t\t\treturn result;\n\t\t}\n\t\t\n\t\tpublic void SaveHistory ()\n\t\t{\n\t\t\tif (history != null) {\n\t\t\t\thistory.Close ();\n\t\t\t}\n\t\t}\n\n\t\tpublic bool TabAtStartCompletes { get; set; }\n\t\t\t\n\t\t//\n\t\t// Emulates the bash-like behavior, where edits done to the\n\t\t// history are recorded\n\t\t//\n\t\tclass History {\n\t\t\tstring [] history;\n\t\t\tint head, tail;\n\t\t\tint cursor, count;\n\t\t\tstring histfile;\n\t\t\t\n\t\t\tpublic History (string app, int size)\n\t\t\t{\n\t\t\t\tif (size < 1)\n\t\t\t\t\tthrow new ArgumentException (\"size\");\n\n\t\t\t\tif (app != null){\n\t\t\t\t\tstring dir = Environment.GetFolderPath (Environment.SpecialFolder.Personal);\n\t\t\t\t\t//Console.WriteLine (dir);\n\t\t\t\t\t/*\n\t\t\t\t\tif (!Directory.Exists (dir)){\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tDirectory.CreateDirectory (dir);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tapp = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (app != null)\n\t\t\t\t\t\thistfile = Path.Combine (dir, app) + \".history\";\n\t\t\t\t\t*/\n\t\t\t\t\thistfile = Path.Combine (dir, \".mal-history\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\thistory = new string [size];\n\t\t\t\thead = tail = cursor = 0;\n\n\t\t\t\tif (File.Exists (histfile)){\n\t\t\t\t\tusing (StreamReader sr = File.OpenText (histfile)){\n\t\t\t\t\t\tstring line;\n\t\t\t\t\t\t\n\t\t\t\t\t\twhile ((line = sr.ReadLine ()) != null){\n\t\t\t\t\t\t\tif (line != \"\")\n\t\t\t\t\t\t\t\tAppend (line);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void Close ()\n\t\t\t{\n\t\t\t\tif (histfile == null)\n\t\t\t\t\treturn;\n\n\t\t\t\ttry {\n\t\t\t\t\tusing (StreamWriter sw = File.CreateText (histfile)){\n\t\t\t\t\t\tint start = (count == history.Length) ? head : tail;\n\t\t\t\t\t\tfor (int i = start; i < start+count; i++){\n\t\t\t\t\t\t\tint p = i % history.Length;\n\t\t\t\t\t\t\tsw.WriteLine (history [p]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t//\n\t\t\t// Appends a value to the history\n\t\t\t//\n\t\t\tpublic void Append (string s)\n\t\t\t{\n\t\t\t\t//Console.WriteLine (\"APPENDING {0} head={1} tail={2}\", s, head, tail);\n\t\t\t\thistory [head] = s;\n\t\t\t\thead = (head+1) % history.Length;\n\t\t\t\tif (head == tail)\n\t\t\t\t\ttail = (tail+1 % history.Length);\n\t\t\t\tif (count != history.Length)\n\t\t\t\t\tcount++;\n\t\t\t\t//Console.WriteLine (\"DONE: head={1} tail={2}\", s, head, tail);\n\t\t\t}\n\n\t\t\t//\n\t\t\t// Updates the current cursor location with the string,\n\t\t\t// to support editing of history items.   For the current\n\t\t\t// line to participate, an Append must be done before.\n\t\t\t//\n\t\t\tpublic void Update (string s)\n\t\t\t{\n\t\t\t\thistory [cursor] = s;\n\t\t\t}\n\n\t\t\tpublic void RemoveLast ()\n\t\t\t{\n\t\t\t\thead = head-1;\n\t\t\t\tif (head < 0)\n\t\t\t\t\thead = history.Length-1;\n\t\t\t}\n\t\t\t\n\t\t\tpublic void Accept (string s)\n\t\t\t{\n\t\t\t\tint t = head-1;\n\t\t\t\tif (t < 0)\n\t\t\t\t\tt = history.Length-1;\n\t\t\t\t\n\t\t\t\thistory [t] = s;\n\t\t\t}\n\t\t\t\n\t\t\tpublic bool PreviousAvailable ()\n\t\t\t{\n\t\t\t\t//Console.WriteLine (\"h={0} t={1} cursor={2}\", head, tail, cursor);\n\t\t\t\tif (count == 0)\n\t\t\t\t\treturn false;\n\t\t\t\tint next = cursor-1;\n\t\t\t\tif (next < 0)\n\t\t\t\t\tnext = count-1;\n\n\t\t\t\tif (next == head)\n\t\t\t\t\treturn false;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tpublic bool NextAvailable ()\n\t\t\t{\n\t\t\t\tif (count == 0)\n\t\t\t\t\treturn false;\n\t\t\t\tint next = (cursor + 1) % history.Length;\n\t\t\t\tif (next == head)\n\t\t\t\t\treturn false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\t\n\t\t\t//\n\t\t\t// Returns: a string with the previous line contents, or\n\t\t\t// nul if there is no data in the history to move to.\n\t\t\t//\n\t\t\tpublic string Previous ()\n\t\t\t{\n\t\t\t\tif (!PreviousAvailable ())\n\t\t\t\t\treturn null;\n\n\t\t\t\tcursor--;\n\t\t\t\tif (cursor < 0)\n\t\t\t\t\tcursor = history.Length - 1;\n\n\t\t\t\treturn history [cursor];\n\t\t\t}\n\n\t\t\tpublic string Next ()\n\t\t\t{\n\t\t\t\tif (!NextAvailable ())\n\t\t\t\t\treturn null;\n\n\t\t\t\tcursor = (cursor + 1) % history.Length;\n\t\t\t\treturn history [cursor];\n\t\t\t}\n\n\t\t\tpublic void CursorToEnd ()\n\t\t\t{\n\t\t\t\tif (head == tail)\n\t\t\t\t\treturn;\n\n\t\t\t\tcursor = head;\n\t\t\t}\n\n\t\t\tpublic void Dump ()\n\t\t\t{\n\t\t\t\tConsole.WriteLine (\"Head={0} Tail={1} Cursor={2} count={3}\", head, tail, cursor, count);\n\t\t\t\tfor (int i = 0; i < history.Length;i++){\n\t\t\t\t\tConsole.WriteLine (\" {0} {1}: {2}\", i == cursor ? \"==>\" : \"   \", i, history[i]);\n\t\t\t\t}\n\t\t\t\t//log.Flush ();\n\t\t\t}\n\n\t\t\tpublic string SearchBackward (string term)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < count; i++){\n\t\t\t\t\tint slot = cursor-i-1;\n\t\t\t\t\tif (slot < 0)\n\t\t\t\t\t\tslot = history.Length+slot;\n\t\t\t\t\tif (slot >= history.Length)\n\t\t\t\t\t\tslot = 0;\n\t\t\t\t\tif (history [slot] != null && history [slot].IndexOf (term) != -1){\n\t\t\t\t\t\tcursor = slot;\n\t\t\t\t\t\treturn history [slot];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\n#if DEMO\n\tclass Demo {\n\t\tstatic void Main ()\n\t\t{\n\t\t\tLineEditor le = new LineEditor (\"foo\");\n\t\t\tstring s;\n\t\t\t\n\t\t\twhile ((s = le.Edit (\"shell> \", \"\")) != null){\n\t\t\t\tConsole.WriteLine (\"----> [{0}]\", s);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "impls/vb/printer.vb",
    "content": "Imports System\nImports System.Collections.Generic\nImports System.Text.RegularExpressions\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalList = Mal.types.MalList\n\nNamespace Mal\n    Public Class printer\n        Shared Function join(value As List(Of MalVal),\n                             delim As String,\n                             print_readably As Boolean) As String\n            Dim strs As New List(Of String)\n            For Each mv As MalVal In value\n                strs.Add(mv.ToString(print_readably))\n            Next\n            return String.Join(delim, strs.ToArray())\n        End Function\n\n        Shared Function join(value As Dictionary(Of String, MalVal),\n                             delim As String,\n                             print_readably As Boolean) As String\n            Dim strs As New List(Of String)\n            For Each entry As KeyValuePair(Of String, MalVal) In value\n                If entry.Key.Length > 0 and entry.Key(0) = ChrW(&H029e) Then\n                    strs.Add(\":\" & entry.Key.Substring(1))\n                Else If print_readably Then\n                    strs.Add(\"\"\"\" & entry.Key.ToString() & \"\"\"\")\n                Else\n                    strs.Add(entry.Key.ToString())\n                End If\n                strs.Add(entry.Value.ToString(print_readably))\n            Next\n            return String.Join(delim, strs.ToArray())\n        End Function\n\n        Shared Function _pr_str(mv As MalVal,\n                               print_readably As Boolean) As String\n            return mv.ToString(print_readably)\n        End Function\n\n        Shared Function _pr_str_args(args As MalList,\n                                     sep As String,\n                                     print_readably As Boolean) As String\n            return join(args.getValue(), sep, print_readably)\n        End Function\n\n        Shared Function escapeString(str As String) As String\n            return Regex.Escape(str)\n        End Function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/reader.vb",
    "content": "Imports System\nImports System.Collections\nImports System.Collections.Generic\nImports System.Text.RegularExpressions\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalThrowable = Mal.types.MalThrowable\nImports MalContinue = Mal.types.MalContinue\n\nNamespace Mal\n    Public Class reader\n        Public Class ParseError\n            Inherits MalThrowable\n            Public Sub New(msg As String)\n                MyBase.New(msg)\n            End Sub\n        End Class\n\n        Public Class Reader\n            Private tokens As New List(Of String)\n            Private position As Int32 = 0\n            Sub New(t As List(Of String))\n                tokens = t\n                position = 0\n            End Sub\n\n            Public Function peek() As String\n                If position >= tokens.Count Then\n                    return Nothing\n                Else\n                    return tokens(position)\n                End If\n            End Function\n\n            Public Function get_next() As String\n                If position >= tokens.Count Then\n                    return Nothing\n                Else\n                    position += 1\n                    return tokens(position-1)\n                End If\n            End Function\n        End Class\n\n        Shared Function tokenize(str As String) As List(Of String)\n            Dim tokens As New List(Of String)\n            Dim pattern As String = \"[\\s ,]*(~@|[\\[\\]{}()'`~@]|\"\"(?:[\\\\].|[^\\\\\"\"])*\"\"?|;.*|[^\\s \\[\\]{}()'\"\"`~@,;]*)\"\n            Dim regex As New Regex(pattern)\n            For Each match As Match In regex.Matches(str)\n                Dim token As String = match.Groups(1).Value\n                If Not token Is Nothing _\n                    AndAlso Not token = \"\" _\n                    AndAlso Not token(0) = \";\" Then\n                    'Console.WriteLine(\"match: ^\" & match.Groups[1] & \"$\")\n                    tokens.Add(token)\n                End If\n            Next\n            return tokens\n        End Function\n\n        Shared Function read_atom(rdr As Reader) As MalVal\n            Dim token As String = rdr.get_next()\n            Dim pattern As String = \"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|(^\"\"(?:[\\\\].|[^\\\\\"\"])*\"\"$)|^(\"\".*)|^:(.*)|(^[^\"\"]*$)\"\n            Dim regex As Regex = New Regex(pattern)\n            Dim match As Match = regex.Match(token)\n            'Console.WriteLine(\"token: ^\" + token + \"$\")\n            If not match.Success Then\n                throw New ParseError(\"unrecognized token '\" & token & \"'\")\n            End If\n            If match.Groups(1).Value <> String.Empty Then\n                return New Mal.types.MalInt(Integer.Parse(match.Groups(1).Value))\n            Else If match.Groups(3).Value <> String.Empty Then\n                return Mal.types.Nil\n            Else If match.Groups(4).Value <> String.Empty Then\n                return Mal.types.MalTrue\n            Else If match.Groups(5).Value <> String.Empty Then\n                return Mal.types.MalFalse\n            Else If match.Groups(6).Value <> String.Empty Then\n                Dim str As String = match.Groups(6).Value\n                return New Mal.types.MalString(\n                        str.Substring(1, str.Length-2) _\n                        .Replace(\"\\\\\",         ChrW(&H029e)) _\n                        .Replace(\"\\\"\"\",        \"\"\"\") _\n                        .Replace(\"\\n\",         Environment.NewLine) _\n                        .Replace(ChrW(&H029e), \"\\\"))\n            Else If match.Groups(7).Value <> String.Empty Then\n                throw New ParseError(\"expected '\"\"', got EOF\")\n            Else If match.Groups(8).Value <> String.Empty Then\n                return New Mal.types.MalString(ChrW(&H029e) & match.Groups(8).Value)\n            Else If match.Groups(9).Value <> String.Empty Then\n                return New Mal.types.MalSymbol(match.Groups(9).Value)\n            Else\n                throw New ParseError(\"unrecognized '\" & match.Groups(0).Value & \"'\")\n            End If\n        End Function\n\n        Shared Function read_list(rdr As Reader, lst As MalList,\n                                  start As String, last As String) As MalVal\n            Dim token As String = rdr.get_next()\n            If token(0) <> start Then\n                throw New ParseError(\"expected '\" & start & \"'\")\n            End If\n            \n            token = rdr.peek()\n            While token IsNot Nothing AndAlso token(0) <> last\n                lst.conj_BANG(read_form(rdr))\n                token = rdr.peek()\n            End While\n\n            If token Is Nothing Then\n                throw New ParseError(\"expected '\" & last & \"', got EOF\")\n            End If\n            rdr.get_next()\n\n            return lst\n        End Function\n\n        Shared Function read_hash_map(rdr As Reader) As MalVal\n            Dim lst As MalList = DirectCast(read_list(rdr, new MalList(),\n                                                      \"{\", \"}\"),MalList)\n            return New MalHashMap(lst)\n        End Function\n\n\n        Shared Function read_form(rdr As Reader) As MalVal\n            Dim token As String = rdr.peek()\n            If token Is Nothing Then\n                throw New MalContinue()\n            End If\n            Dim form As MalVal = Nothing\n\n            Select token\n            Case \"'\"\n                rdr.get_next()\n                return New MalList(New MalSymbol(\"quote\"),\n                                   read_form(rdr))\n            Case \"`\"\n                rdr.get_next()\n                return New MalList(New MalSymbol(\"quasiquote\"),\n                                   read_form(rdr))\n            Case \"~\"\n                rdr.get_next()\n                return New MalList(New MalSymbol(\"unquote\"),\n                                   read_form(rdr))\n            Case \"~@\"\n                rdr.get_next()\n                return new MalList(New MalSymbol(\"splice-unquote\"),\n                                   read_form(rdr))\n            Case \"^\"\n                rdr.get_next()\n                Dim meta As MalVal = read_form(rdr)\n                return new MalList(New MalSymbol(\"with-meta\"),\n                                   read_form(rdr),\n                                   meta)\n            Case \"@\"\n                rdr.get_next()\n                return new MalList(New MalSymbol(\"deref\"),\n                                   read_form(rdr))\n\n            Case \"(\"\n                form = read_list(rdr, New MalList(), \"(\" , \")\")\n            Case \")\"\n                throw New ParseError(\"unexpected ')'\")\n            Case \"[\"\n                form = read_list(rdr, New MalVector(), \"[\" , \"]\")\n            Case \"]\"\n                throw New ParseError(\"unexpected ']'\")\n            Case \"{\"\n                form = read_hash_map(rdr)\n            Case \"}\"\n                throw New ParseError(\"unexpected '}'\")\n            Case Else\n                form = read_atom(rdr)\n            End Select\n            return form\n        End Function\n\n\n        Shared Function read_str(str As string) As MalVal\n            return read_form(New Reader(tokenize(str)))\n        End Function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/readline.vb",
    "content": "Imports System\nImports Mono.Terminal ' LineEditor (getline.cs)\n\nNamespace Mal\n    Public Class readline\n        Enum Modes\n            Terminal\n            Raw\n        End Enum\n\n        Public Shared mode As Modes = Modes.Terminal\n\n        Shared lineedit As LineEditor = Nothing\n\n        Public Shared Sub SetMode(new_mode As Modes)\n            mode = new_mode\n        End Sub\n\n        Public Shared Function Readline(prompt As String) As String\n            If mode = Modes.Terminal Then\n                If lineedit Is Nothing Then\n                    lineedit = New LineEditor(\"Mal\")\n                End If\n                return lineedit.Edit(prompt, \"\")\n            Else\n                Console.Write(prompt)\n                Console.Out.Flush()\n                return Console.ReadLine()\n            End If\n        End Function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/run",
    "content": "#!/usr/bin/env bash\nexec mono $(dirname $0)/${STEP:-stepA_mal}.exe ${RAW:+--raw} \"${@}\"\n"
  },
  {
    "path": "impls/vb/step0_repl.vb",
    "content": "Imports System\nImports Mal\n\nNamespace Mal\n    Class step0_repl\n        ' read\n        Shared Function READ(str As String) As String\n            Return str\n        End Function\n\n        ' eval\n        Shared Function EVAL(ast As String, env As String) As String\n            Return ast\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As String) As String\n            Return exp\n        End Function\n\n        ' repl\n        Shared Function REP(str As String, env As String) As String\n            Return PRINT(EVAL(READ(str), env))\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                line = Mal.readline.Readline(\"user> \")\n                If line is Nothing Then\n                    Exit Do\n                End If\n                If line = \"\" Then\n                    Continue Do\n                End If\n                Console.WriteLine(REP(line, \"\"))\n            Loop While True\n            Return 0\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step1_read_print.vb",
    "content": "Imports System\nImports System.IO\nImports Mal\nImports MalVal = Mal.types.MalVal\n\nNamespace Mal\n    Class step1_read_print\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function EVAL(ast As MalVal, env As String) As MalVal\n            Return ast\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), \"\"))\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step2_eval.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\n\nNamespace Mal\n    Class step2_eval\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function EVAL(orig_ast As MalVal, env As Dictionary(Of String, MalVal)) As MalVal\n\n            'Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim sym As MalSymbol = DirectCast(orig_ast, MalSymbol)\n                return env.Item(sym.getName())\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim el As MalList = New MalList\n            Dim mv As MalVal\n            For Each mv In ast.getValue()\n                el.conj_BANG(EVAL(mv, env))\n            Next\n            Dim f As MalFunc = DirectCast(el(0), MalFunc)\n            Return f.apply(el.rest())\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As Dictionary(Of String, MalVal)\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function add(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) + DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function minus(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) - DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function mult(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) * DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function div(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) / DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New Dictionary(Of String, MalVal)\n            repl_env.Add(\"+\", New MalFunc(AddressOf add))\n            repl_env.Add(\"-\", New MalFunc(AddressOf minus))\n            repl_env.Add(\"*\", New MalFunc(AddressOf mult))\n            repl_env.Add(\"/\", New MalFunc(AddressOf div))\n\n\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step3_env.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step3_env\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Select DirectCast(a0,MalSymbol).getName()\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                return EVAL(a2, let_env)\n            Case Else\n                Dim el As MalList = New MalList\n                Dim mv As MalVal\n                For Each mv In ast.getValue()\n                    el.conj_BANG(EVAL(mv, env))\n                Next\n                Dim f As MalFunc = DirectCast(el(0), MalFunc)\n                Return f.apply(el.rest())\n            End Select\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function add(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) + DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function minus(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) - DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function mult(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) * DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function div(a As MalList) As MalVal\n            Return DirectCast(a.Item(0),MalInt) / DirectCast(a.Item(1),MalInt)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n            repl_env.do_set(new MalSymbol(\"+\"), New MalFunc(AddressOf add))\n            repl_env.do_set(new MalSymbol(\"-\"), New MalFunc(AddressOf minus))\n            repl_env.do_set(new MalSymbol(\"*\"), New MalFunc(AddressOf mult))\n            repl_env.do_set(new MalSymbol(\"/\"), New MalFunc(AddressOf div))\n\n\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step4_if_fn_do.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step4_if_fn_do\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                return EVAL(a2, let_env)\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                return EVAL(ast(ast.size()-1), env)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        Dim a3 As MalVal = ast(3)\n                        return EVAL(a3, env)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    Dim a2 As MalVal = ast(2)\n                    return EVAL(a2, env)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim el As MalList = New MalList\n                Dim mv As MalVal\n                For Each mv In ast.getValue()\n                    el.conj_BANG(EVAL(mv, env))\n                Next\n                Dim f As MalFunc = DirectCast(el(0), MalFunc)\n                Return f.apply(el.rest())\n            End Select\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step5_tco.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step5_tco\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Do\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                orig_ast = a2\n                env = let_env\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                orig_ast = ast(ast.size()-1)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        orig_ast = ast(3)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    orig_ast = ast(2)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(ast(2), env,\n                                      DirectCast(ast(1),MalList), f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim el As MalList = New MalList\n                Dim mv As MalVal\n                For Each mv In ast.getValue()\n                    el.conj_BANG(EVAL(mv, env))\n                Next\n                Dim f As MalFunc = DirectCast(el(0), MalFunc)\n                Dim fnast As MalVal = f.getAst()\n                If not fnast Is Nothing\n                    orig_ast = fnast\n                    env = f.genEnv(el.rest())\n                Else\n                    Return f.apply(el.rest())\n                End If\n            End Select\n\n            Loop While True\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step6_file.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalString = Mal.types.MalString\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step6_file\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Do\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                orig_ast = a2\n                env = let_env\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                orig_ast = ast(ast.size()-1)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        orig_ast = ast(3)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    orig_ast = ast(2)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(ast(2), env,\n                                      DirectCast(ast(1),MalList), f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim el As MalList = New MalList\n                Dim mv As MalVal\n                For Each mv In ast.getValue()\n                    el.conj_BANG(EVAL(mv, env))\n                Next\n                Dim f As MalFunc = DirectCast(el(0), MalFunc)\n                Dim fnast As MalVal = f.getAst()\n                If not fnast Is Nothing\n                    orig_ast = fnast\n                    env = f.genEnv(el.rest())\n                Else\n                    Return f.apply(el.rest())\n                End If\n            End Select\n\n            Loop While True\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function do_eval(args As MalList) As MalVal\n            Return EVAL(args(0), repl_env)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n            repl_env.do_set(new MalSymbol(\"eval\"), new MalFunc(AddressOf do_eval))\n            Dim fileIdx As Integer = 1\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n                fileIdx = 2\n            End If\n            Dim argv As New MalList()\n            For i As Integer = fileIdx+1 To args.Length-1\n                argv.conj_BANG(new MalString(args(i)))\n            Next\n            repl_env.do_set(new MalSymbol(\"*ARGV*\"), argv)\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n            REP(\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\")\n\n            If args.Length > fileIdx Then\n                REP(\"(load-file \"\"\" & args(fileIdx) & \"\"\")\")\n                return 0\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step7_quote.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalString = Mal.types.MalString\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step7_quote\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function starts_with(ast As Malval, sym As String) As MalVal\n            If ast.list_Q() Then\n                Const lst As MalList = DirectCast(ast, MalList)\n                If 0 < lst.size() Then\n                    Const fst As MalSymbol = TryCast(lst(0), MalSymbol)\n                    If fst IsNot Nothing AndAlso fst.getName() = sym Then\n                        return lst(1)\n                    End If\n                End If\n            End If\n            return Nothing\n        End Function\n\n        Shared Function quasiquote(ast As MalVal) As MalVal\n            If TypeOf ast Is Mal.types.MalSymbol or Typeof ast Is Mal.types.MalHashMap Then\n                return New MalList(New MalSymbol(\"quote\"), ast)\n            End If\n            Const source As MalList = TryCast(ast, MalList)\n            If source Is Nothing Then\n                return ast\n            End If\n            Const unquoted As MalVal = starts_with(ast, \"unquote\")\n            If unquoted IsNot Nothing Then\n                return unquoted\n            End If\n            Dim result As MalList = New MalList()\n            For i As Integer = source.size()-1 To 0 Step -1\n                Const elt As MalVal = source(i)\n                Const splice_unquoted As MalVal = starts_with(elt, \"splice-unquote\")\n                If splice_unquoted IsNot Nothing Then\n                    result = New MalList(New MalSymbol(\"concat\"), splice_unquoted, result)\n                Else\n                    result = New MalList(New MalSymbol(\"cons\"), quasiquote(elt), result)\n                End If\n            Next\n            If TypeOf ast Is MalVector Then\n                result = New MalList(New MalSymbol(\"vec\"), result)\n            End If\n            return result\n        End Function\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Do\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                orig_ast = a2\n                env = let_env\n            Case \"quote\"\n                return ast(1)\n            Case \"quasiquote\"\n                orig_ast = quasiquote(ast(1))\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                orig_ast = ast(ast.size()-1)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        orig_ast = ast(3)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    orig_ast = ast(2)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(ast(2), env,\n                                      DirectCast(ast(1),MalList), f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim el As MalList = New MalList\n                Dim mv As MalVal\n                For Each mv In ast.getValue()\n                    el.conj_BANG(EVAL(mv, env))\n                Next\n                Dim f As MalFunc = DirectCast(el(0), MalFunc)\n                Dim fnast As MalVal = f.getAst()\n                If not fnast Is Nothing\n                    orig_ast = fnast\n                    env = f.genEnv(el.rest())\n                Else\n                    Return f.apply(el.rest())\n                End If\n            End Select\n\n            Loop While True\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function do_eval(args As MalList) As MalVal\n            Return EVAL(args(0), repl_env)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n            repl_env.do_set(new MalSymbol(\"eval\"), new MalFunc(AddressOf do_eval))\n            Dim fileIdx As Integer = 1\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n                fileIdx = 2\n            End If\n            Dim argv As New MalList()\n            For i As Integer = fileIdx+1 To args.Length-1\n                argv.conj_BANG(new MalString(args(i)))\n            Next\n            repl_env.do_set(new MalSymbol(\"*ARGV*\"), argv)\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n            REP(\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\")\n\n            If args.Length > fileIdx Then\n                REP(\"(load-file \"\"\" & args(fileIdx) & \"\"\")\")\n                return 0\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step8_macros.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalString = Mal.types.MalString\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step8_macros\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function starts_with(ast As Malval, sym As String) As MalVal\n            If ast.list_Q() Then\n                Const lst As MalList = DirectCast(ast, MalList)\n                If 0 < lst.size() Then\n                    Const fst As MalSymbol = TryCast(lst(0), MalSymbol)\n                    If fst IsNot Nothing AndAlso fst.getName() = sym Then\n                        return lst(1)\n                    End If\n                End If\n            End If\n            return Nothing\n        End Function\n\n        Shared Function quasiquote(ast As MalVal) As MalVal\n            If TypeOf ast Is Mal.types.MalSymbol or Typeof ast Is Mal.types.MalHashMap Then\n                return New MalList(New MalSymbol(\"quote\"), ast)\n            End If\n            Const source As MalList = TryCast(ast, MalList)\n            If source Is Nothing Then\n                return ast\n            End If\n            Const unquoted As MalVal = starts_with(ast, \"unquote\")\n            If unquoted IsNot Nothing Then\n                return unquoted\n            End If\n            Dim result As MalList = New MalList()\n            For i As Integer = source.size()-1 To 0 Step -1\n                Const elt As MalVal = source(i)\n                Const splice_unquoted As MalVal = starts_with(elt, \"splice-unquote\")\n                If splice_unquoted IsNot Nothing Then\n                    result = New MalList(New MalSymbol(\"concat\"), splice_unquoted, result)\n                Else\n                    result = New MalList(New MalSymbol(\"cons\"), quasiquote(elt), result)\n                End If\n            Next\n            If TypeOf ast Is MalVector Then\n                result = New MalList(New MalSymbol(\"vec\"), result)\n            End If\n            return result\n        End Function\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Do\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                orig_ast = a2\n                env = let_env\n            Case \"quote\"\n                return ast(1)\n            Case \"quasiquote\"\n                orig_ast = quasiquote(ast(1))\n            Case \"defmacro!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = DirectCast(EVAL(a2, env), MalFunc).asMacro()\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                orig_ast = ast(ast.size()-1)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        orig_ast = ast(3)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    orig_ast = ast(2)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(ast(2), env,\n                                      DirectCast(ast(1),MalList), f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim f As MalFunc = DirectCast(EVAL(a0, env), MalFunc)\n                If f.isMacro() Then\n                    orig_ast = f.apply(ast.rest())\n                    Continue Do\n                End If\n                Dim args As MalList = New MalList\n                For i As Integer = 1 To ast.size()-1\n                    args.conj_BANG(EVAL(ast(i), env))\n                Next\n                Dim fnast As MalVal = f.getAst()\n                If not fnast Is Nothing\n                    orig_ast = fnast\n                    env = f.genEnv(args)\n                Else\n                    Return f.apply(args)\n                End If\n            End Select\n\n            Loop While True\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function do_eval(args As MalList) As MalVal\n            Return EVAL(args(0), repl_env)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n            repl_env.do_set(new MalSymbol(\"eval\"), new MalFunc(AddressOf do_eval))\n            Dim fileIdx As Integer = 1\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n                fileIdx = 2\n            End If\n            Dim argv As New MalList()\n            For i As Integer = fileIdx+1 To args.Length-1\n                argv.conj_BANG(new MalString(args(i)))\n            Next\n            repl_env.do_set(new MalSymbol(\"*ARGV*\"), argv)\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n            REP(\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\")\n            REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons 'cond (rest (rest xs)))))))\")\n\n            If args.Length > fileIdx Then\n                REP(\"(load-file \"\"\" & args(fileIdx) & \"\"\")\")\n                return 0\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e as Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/step9_try.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalString = Mal.types.MalString\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class step9_try\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function starts_with(ast As Malval, sym As String) As MalVal\n            If ast.list_Q() Then\n                Const lst As MalList = DirectCast(ast, MalList)\n                If 0 < lst.size() Then\n                    Const fst As MalSymbol = TryCast(lst(0), MalSymbol)\n                    If fst IsNot Nothing AndAlso fst.getName() = sym Then\n                        return lst(1)\n                    End If\n                End If\n            End If\n            return Nothing\n        End Function\n\n        Shared Function quasiquote(ast As MalVal) As MalVal\n            If TypeOf ast Is Mal.types.MalSymbol or Typeof ast Is Mal.types.MalHashMap Then\n                return New MalList(New MalSymbol(\"quote\"), ast)\n            End If\n            Const source As MalList = TryCast(ast, MalList)\n            If source Is Nothing Then\n                return ast\n            End If\n            Const unquoted As MalVal = starts_with(ast, \"unquote\")\n            If unquoted IsNot Nothing Then\n                return unquoted\n            End If\n            Dim result As MalList = New MalList()\n            For i As Integer = source.size()-1 To 0 Step -1\n                Const elt As MalVal = source(i)\n                Const splice_unquoted As MalVal = starts_with(elt, \"splice-unquote\")\n                If splice_unquoted IsNot Nothing Then\n                    result = New MalList(New MalSymbol(\"concat\"), splice_unquoted, result)\n                Else\n                    result = New MalList(New MalSymbol(\"cons\"), quasiquote(elt), result)\n                End If\n            Next\n            If TypeOf ast Is MalVector Then\n                result = New MalList(New MalSymbol(\"vec\"), result)\n            End If\n            return result\n        End Function\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Do\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                orig_ast = a2\n                env = let_env\n            Case \"quote\"\n                return ast(1)\n            Case \"quasiquote\"\n                orig_ast = quasiquote(ast(1))\n            Case \"defmacro!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = DirectCast(EVAL(a2, env), MalFunc).asMacro()\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"try*\"\n                Try\n                    return EVAL(ast(1), env)\n                Catch e As Exception\n                    If ast.size() > 2 Then\n                        Dim exc As MalVal\n                        Dim a2 As MalVal = ast(2)\n                        Dim a20 As MalVal = DirectCast(a2,MalList)(0)\n                        If DirectCast(a20,MalSymbol).getName() = \"catch*\" Then\n                            If TypeOf e Is Mal.types.MalException Then\n                                exc = DirectCast(e,Mal.types.MalException).getValue()\n                            Else\n                                exc = New MalString(e.Message)\n                            End If\n                            return EVAL(\n                                DirectCast(a2,MalList)(2),\n                                New MalEnv(env,\n                                           DirectCast(a2,MalList).slice(1,2),\n                                           New MalList(exc)))\n                        End If\n                    End If\n                    Throw e\n                End Try\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                orig_ast = ast(ast.size()-1)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        orig_ast = ast(3)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    orig_ast = ast(2)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(ast(2), env,\n                                      DirectCast(ast(1),MalList), f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim f As MalFunc = DirectCast(EVAL(a0, env), MalFunc)\n                If f.isMacro() Then\n                    orig_ast = f.apply(ast.rest())\n                    Continue Do\n                End If\n                Dim args As MalList = New MalList\n                For i As Integer = 1 To ast.size()-1\n                    args.conj_BANG(EVAL(ast(i), env))\n                Next\n                Dim fnast As MalVal = f.getAst()\n                If not fnast Is Nothing\n                    orig_ast = fnast\n                    env = f.genEnv(args)\n                Else\n                    Return f.apply(args)\n                End If\n            End Select\n\n            Loop While True\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function do_eval(args As MalList) As MalVal\n            Return EVAL(args(0), repl_env)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n            repl_env.do_set(new MalSymbol(\"eval\"), new MalFunc(AddressOf do_eval))\n            Dim fileIdx As Integer = 1\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n                fileIdx = 2\n            End If\n            Dim argv As New MalList()\n            For i As Integer = fileIdx+1 To args.Length-1\n                argv.conj_BANG(new MalString(args(i)))\n            Next\n            repl_env.do_set(new MalSymbol(\"*ARGV*\"), argv)\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n            REP(\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\")\n            REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons 'cond (rest (rest xs)))))))\")\n\n            If args.Length > fileIdx Then\n                REP(\"(load-file \"\"\" & args(fileIdx) & \"\"\")\")\n                return 0\n            End If\n\n            ' repl loop\n            Dim line As String\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e As Mal.types.MalException\n                    Console.WriteLine(\"Error: \" & _\n                        printer._pr_str(e.getValue(), False))\n                    Continue Do\n                Catch e As Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/stepA_mal.vb",
    "content": "Imports System\nImports System.IO\nImports System.Collections.Generic\nImports Mal\nImports MalVal = Mal.types.MalVal\nImports MalInt = Mal.types.MalInt\nImports MalString = Mal.types.MalString\nImports MalSymbol = Mal.types.MalSymbol\nImports MalList = Mal.types.MalList\nImports MalVector = Mal.types.MalVector\nImports MalHashMap = Mal.types.MalHashMap\nImports MalFunc = Mal.types.MalFunc\nImports MalEnv = Mal.env.Env\n\nNamespace Mal\n    Class stepA_mal\n        ' read\n        Shared Function READ(str As String) As MalVal\n            Return reader.read_str(str)\n        End Function\n\n        ' eval\n        Shared Function starts_with(ast As Malval, sym As String) As MalVal\n            If ast.list_Q() Then\n                Const lst As MalList = DirectCast(ast, MalList)\n                If 0 < lst.size() Then\n                    Const fst As MalSymbol = TryCast(lst(0), MalSymbol)\n                    If fst IsNot Nothing AndAlso fst.getName() = sym Then\n                        return lst(1)\n                    End If\n                End If\n            End If\n            return Nothing\n        End Function\n\n        Shared Function quasiquote(ast As MalVal) As MalVal\n            If TypeOf ast Is Mal.types.MalSymbol or Typeof ast Is Mal.types.MalHashMap Then\n                return New MalList(New MalSymbol(\"quote\"), ast)\n            End If\n            Const source As MalList = TryCast(ast, MalList)\n            If source Is Nothing Then\n                return ast\n            End If\n            Const unquoted As MalVal = starts_with(ast, \"unquote\")\n            If unquoted IsNot Nothing Then\n                return unquoted\n            End If\n            Dim result As MalList = New MalList()\n            For i As Integer = source.size()-1 To 0 Step -1\n                Const elt As MalVal = source(i)\n                Const splice_unquoted As MalVal = starts_with(elt, \"splice-unquote\")\n                If splice_unquoted IsNot Nothing Then\n                    result = New MalList(New MalSymbol(\"concat\"), splice_unquoted, result)\n                Else\n                    result = New MalList(New MalSymbol(\"cons\"), quasiquote(elt), result)\n                End If\n            Next\n            If TypeOf ast Is MalVector Then\n                result = New MalList(New MalSymbol(\"vec\"), result)\n            End If\n            return result\n        End Function\n\n        ' TODO: move to types.vb when it is ported\n        Class FClosure\n            Public ast As MalVal\n            Public params As MalList\n            Public env As MalEnv\n            Function fn(args as MalList) As MalVal\n                return EVAL(ast, new MalEnv(env, params, args))\n            End Function\n        End Class\n\n        Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal\n            Do\n\n            Dim dbgeval As MalVal = env.do_get(\"DEBUG-EVAL\")\n            If dbgeval IsNot Nothing and dbgeval IsNot Mal.types.Nil and dbgeval IsNot Mal.types.MalFalse Then\n                Console.WriteLine(\"EVAL: {0}\", printer._pr_str(orig_ast, true))\n            End If\n\n            If TypeOf orig_ast Is MalSymbol Then\n                Dim key As String = DirectCast(orig_ast, MalSymbol).getName()\n                Dim result As MalVal = env.do_get(key)\n                If result Is Nothing Then\n                    throw New Mal.types.MalException(\"'\" & key & \"' not found\")\n                End If\n                return result\n            Else If TypeOf orig_ast Is MalVector Then\n                Dim old_lst As MalList = DirectCast(orig_ast, MalList)\n                Dim new_lst As MalList\n                    new_lst = DirectCast(New MalVector, MalList)\n                Dim mv As MalVal\n                For Each mv in old_lst.getValue()\n                    new_lst.conj_BANG(EVAL(mv, env))\n                Next\n                return new_lst\n            Else If TypeOf orig_ast Is MalHashMap Then\n                Dim new_dict As New Dictionary(Of String, MalVal)\n                Dim entry As KeyValuePair(Of String, MalVal)\n                For Each entry in DirectCast(orig_ast,MalHashMap).getValue()\n                    new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))\n                Next\n                return New MalHashMap(new_dict)\n            Else If not orig_ast.list_Q() Then\n                return orig_ast\n            End If\n\n            ' apply list\n            Dim ast As MalList = DirectCast(orig_ast, MalList)\n\n            If ast.size() = 0  Then\n                return ast\n            End If\n            Dim a0 As MalVal = ast(0)\n            Dim a0sym As String\n            If TypeOf a0 is MalSymbol Then\n                a0sym = DirectCast(a0,MalSymbol).getName()\n            Else\n                a0sym = \"__<*fn*>__\"\n            End If\n\n            Select a0sym\n            Case \"def!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = EVAL(a2, env)\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"let*\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim key As MalSymbol\n                Dim val as MalVal\n                Dim let_env As new MalEnv(env)\n                For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2\n                    key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)\n                    val = DirectCast(a1,MalList)(i+1)\n                    let_env.do_set(key, EVAL(val, let_env))\n                Next\n                orig_ast = a2\n                env = let_env\n            Case \"quote\"\n                return ast(1)\n            Case \"quasiquote\"\n                orig_ast = quasiquote(ast(1))\n            Case \"defmacro!\"\n                Dim a1 As MalVal = ast(1)\n                Dim a2 As MalVal = ast(2)\n                Dim res As MalVal = DirectCast(EVAL(a2, env), MalFunc).asMacro()\n                env.do_set(DirectCast(a1,MalSymbol), res)\n                return res\n            Case \"try*\"\n                Try\n                    return EVAL(ast(1), env)\n                Catch e As Exception\n                    If ast.size() > 2 Then\n                        Dim exc As MalVal\n                        Dim a2 As MalVal = ast(2)\n                        Dim a20 As MalVal = DirectCast(a2,MalList)(0)\n                        If DirectCast(a20,MalSymbol).getName() = \"catch*\" Then\n                            If TypeOf e Is Mal.types.MalException Then\n                                exc = DirectCast(e,Mal.types.MalException).getValue()\n                            Else\n                                exc = New MalString(e.Message)\n                            End If\n                            return EVAL(\n                                DirectCast(a2,MalList)(2),\n                                New MalEnv(env,\n                                           DirectCast(a2,MalList).slice(1,2),\n                                           New MalList(exc)))\n                        End If\n                    End If\n                    Throw e\n                End Try\n            Case \"do\"\n                For i As Integer = 1 To ast.size()-2\n                    EVAL(ast(i), env)\n                Next\n                orig_ast = ast(ast.size()-1)\n            Case \"if\"\n                Dim a1 As MalVal = ast(1)\n                Dim cond As MalVal = EVAL(a1, env)\n                If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then\n                    ' eval false slot form\n                    If ast.size() > 3 Then\n                        orig_ast = ast(3)\n                    Else\n                        return Mal.types.Nil\n                    End If\n                Else\n                    ' eval true slot form\n                    orig_ast = ast(2)\n\n                End If\n            Case \"fn*\"\n                Dim fc As New FClosure()\n                fc.ast = ast(2)\n                fc.params = DirectCast(ast(1),MalLIst)\n                fc.env = env\n                Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn\n                Dim mf As new MalFunc(ast(2), env,\n                                      DirectCast(ast(1),MalList), f)\n                return DirectCast(mf,MalVal)\n            Case Else\n                Dim f As MalFunc = DirectCast(EVAL(a0, env), MalFunc)\n                If f.isMacro() Then\n                    orig_ast = f.apply(ast.rest())\n                    Continue Do\n                End If\n                Dim args As MalList = New MalList\n                For i As Integer = 1 To ast.size()-1\n                    args.conj_BANG(EVAL(ast(i), env))\n                Next\n                Dim fnast As MalVal = f.getAst()\n                If not fnast Is Nothing\n                    orig_ast = fnast\n                    env = f.genEnv(args)\n                Else\n                    Return f.apply(args)\n                End If\n            End Select\n\n            Loop While True\n        End Function\n        \n        ' print\n        Shared Function PRINT(exp As MalVal) As String\n            return printer._pr_str(exp, TRUE)\n        End Function\n\n        ' repl\n        Shared repl_env As MalEnv\n\n        Shared Function REP(str As String) As String\n            Return PRINT(EVAL(READ(str), repl_env))\n        End Function\n\n        Shared Function do_eval(args As MalList) As MalVal\n            Return EVAL(args(0), repl_env)\n        End Function\n\n        Shared Function Main As Integer\n            Dim args As String() = Environment.GetCommandLineArgs()\n\n            repl_env = New MalEnv(Nothing)\n\n            ' core.vb: defined using VB.NET\n            For Each entry As KeyValuePair(Of String,MalVal) In core.ns()\n                repl_env.do_set(new MalSymbol(entry.Key), entry.Value)\n            Next\n            repl_env.do_set(new MalSymbol(\"eval\"), new MalFunc(AddressOf do_eval))\n            Dim fileIdx As Integer = 1\n            If args.Length > 1 AndAlso args(1) = \"--raw\" Then\n                Mal.readline.SetMode(Mal.readline.Modes.Raw)\n                fileIdx = 2\n            End If\n            Dim argv As New MalList()\n            For i As Integer = fileIdx+1 To args.Length-1\n                argv.conj_BANG(new MalString(args(i)))\n            Next\n            repl_env.do_set(new MalSymbol(\"*ARGV*\"), argv)\n\n            ' core.mal: defined using the language itself\n            REP(\"(def! *host-language* \"\"VB.NET\"\")\")\n            REP(\"(def! not (fn* (a) (if a false true)))\")\n            REP(\"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\")\n            REP(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons 'cond (rest (rest xs)))))))\")\n\n            If args.Length > fileIdx Then\n                REP(\"(load-file \"\"\" & args(fileIdx) & \"\"\")\")\n                return 0\n            End If\n\n            ' repl loop\n            Dim line As String\n            REP(\"(println (str \"\"Mal [\"\" *host-language* \"\"]\"\"))\")\n            Do\n                Try\n                    line = Mal.readline.Readline(\"user> \")\n                    If line is Nothing Then\n                        Exit Do\n                    End If\n                    If line = \"\" Then\n                        Continue Do\n                    End If\n                Catch e As IOException\n                    Console.WriteLine(\"IOException: \" & e.Message)\n                End Try\n                Try\n                    Console.WriteLine(REP(line))\n                Catch e As Mal.types.MalException\n                    Console.WriteLine(\"Error: \" & _\n                        printer._pr_str(e.getValue(), False))\n                    Continue Do\n                Catch e As Exception\n                    Console.WriteLine(\"Error: \" & e.Message)\n                    Console.WriteLine(e.StackTrace)\n                    Continue Do\n                End Try\n            Loop While True\n        End function\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vb/tests/step5_tco.mal",
    "content": ";; VB: skipping non-TCO recursion\n;; Reason: unrecoverable segfault at 10,000\n"
  },
  {
    "path": "impls/vb/types.vb",
    "content": "Imports System\nImports System.Collections.Generic\nImports System.Text.RegularExpressions\nImports Mal\n\nnamespace Mal\n    Public Class types\n        '\n        ' Exceptions/Errors\n        '\n        Public Class MalThrowable\n            Inherits Exception\n            Public Sub New()\n                MyBase.New()\n            End Sub\n            Public Sub New(msg As String)\n                MyBase.New(msg)\n            End Sub\n        End Class\n        Public Class MalError\n            Inherits MalThrowable\n            Public Sub New(msg As String)\n                MyBase.New(msg)\n            End Sub\n        End Class\n        Public Class MalContinue\n            Inherits MalThrowable\n        End Class\n\n        ' Thrown by throw function\n        Public Class MalException\n            Inherits MalThrowable\n            Private value As MalVal\n\n            'string Message\n            Public Sub New(new_value As MalVal)\n                value = new_value\n            End Sub\n            Public Sub New(new_value As String)\n                MyBase.New(new_value)\n                value = New MalString(new_value)\n            End Sub\n            Public Function getValue() As MalVal\n                return value\n            End Function\n        End Class\n\n        '\n        ' General functions\n        '\n        Public Shared Function _equal_Q(a As MalVal, b As MalVal) As Boolean\n            Dim ota As Type = a.GetType()\n            Dim otb As Type = b.GetType()\n            If not (ota = otb Or\n                (TypeOf a Is MalList and TypeOf b Is MalList)) Then\n                return False\n            Else\n                If TypeOf a Is MalInt Then\n                    return DirectCast(a,MalInt).getValue() =\n                           DirectCast(b,MalInt).getValue()\n                Else If TypeOf a Is MalSymbol Then\n                    return DirectCast(a,MalSymbol).getName() =\n                           DirectCast(b,MalSymbol).getName()\n                Else If TypeOf a Is MalString Then\n                    return DirectCast(a,MalString).getValue() =\n                           DirectCast(b,MalString).getValue()\n                Else If TypeOf a Is MalList Then\n                    If DirectCast(a,MalList).size() <>\n                       DirectCast(b,MalList).size()\n                        return False\n                    End If\n                    for i As Integer = 0 To DirectCast(a,MalList).size()-1\n                        If not _equal_Q(DirectCast(a,MalList)(i),\n                                        DirectCast(b,MalList)(i))\n                            return False\n                        End If\n                    Next\n                    return True\n                Else If TypeOf a Is MalHashMap Then\n                    Dim ahm As Dictionary(Of String,MalVal) = DirectCast(a,MalHashMap).getValue()\n                    Dim bhm As Dictionary(Of String,MalVal) = DirectCast(b,MalHashMap).getValue()\n                    For Each key As String in ahm.keys\n                        If not bhm.ContainsKey(key) Then\n                            return False\n                        End If\n                        If not _equal_Q(DirectCast(a,MalHashMap).getValue()(key),\n                                        DirectCast(b,MalHashMap).getValue()(key))\n                            return False\n                        End If\n                    Next\n                    return True\n                Else\n                    return a Is b\n                End If\n            End If\n        End Function\n\n\n        Public MustInherit Class MalVal\n            Private meta As MalVal = Nil\n            Public Overridable Function copy() As MalVal\n                return DirectCast(Me.MemberwiseClone(),MalVal)\n            End Function\n\n            ' Default is just to call regular toString()\n            Public Overridable Function ToString() As String\n                throw New MalException(\"ToString called on abstract MalVal\")\n            End Function\n            Public Overridable Function ToString(print_readably As Boolean) As String\n                return Me.ToString()\n            End Function\n            Public Function getMeta() As MalVal\n                return meta\n            End Function\n            Public Function setMeta(m As MalVal) As MalVal\n                meta = m\n                return Me\n            End Function\n            Public Overridable Function list_Q() As Boolean\n                return False\n            End Function\n        End Class\n\n        Public Class MalConstant\n            Inherits MalVal\n            Private value As String\n            Public Sub New(name As String)\n                value = name\n            End Sub\n            Public Shadows Function copy() As MalConstant\n                return Me\n            End Function\n\n            Public Overrides Function ToString() As String\n                return value\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                return value\n            End Function\n        End Class\n\n        Public Shared Nil As MalConstant = New MalConstant(\"nil\")\n        Public Shared MalTrue As MalConstant = New MalConstant(\"true\")\n        Public Shared MalFalse As MalConstant = New MalConstant(\"false\")\n\n        Public Class MalInt\n            Inherits MalVal\n            Private value As Int64\n            Public Sub New(v As Int64)\n                value = v\n            End Sub\n            Public Shadows Function copy() As MalInt \n                return Me\n            End Function\n\n            Public Function getValue() As Int64\n                return value\n            End Function\n            Public Overrides Function ToString() As String\n                return value.ToString()\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                return value.ToString()\n            End Function\n            Public Shared Operator <(a As MalInt, b As Malint) As MalConstant\n                If a.getValue() < b.getValue() Then\n                    return MalTrue\n                Else\n                    return MalFalse\n                End If\n            End Operator\n            Public Shared Operator <=(a As MalInt, b As Malint) As MalConstant\n                If a.getValue() <= b.getValue() Then\n                    return MalTrue\n                Else\n                    return MalFalse\n                End If\n            End Operator\n            Public Shared Operator >(a As MalInt, b As Malint) As MalConstant\n                If a.getValue() > b.getValue() Then\n                    return MalTrue\n                Else\n                    return MalFalse\n                End If\n            End Operator\n            Public Shared Operator >=(a As MalInt, b As Malint) As MalConstant\n                If a.getValue() >= b.getValue() Then\n                    return MalTrue\n                Else\n                    return MalFalse\n                End If\n            End Operator\n            Public Shared Operator +(a As MalInt, b As Malint) As MalInt\n                return new MalInt(a.getValue() + b.getValue())\n            End Operator\n            Public Shared Operator -(a As MalInt, b As Malint) As MalInt\n                return new MalInt(a.getValue() - b.getValue())\n            End Operator\n            Public Shared Operator *(a As MalInt, b As Malint) As MalInt\n                return new MalInt(a.getValue() * b.getValue())\n            End Operator\n            Public Shared Operator /(a As MalInt, b As Malint) As MalInt\n                return new MalInt(a.getValue() / b.getValue())\n            End Operator\n        End Class\n\n        Public Class MalSymbol\n            Inherits MalVal\n            Private value As String\n            Public Sub New(v As String)\n                value = v\n            End Sub\n            Public Sub New(v As MalString)\n                value = v.getValue()\n            End Sub\n            Public Shadows Function copy() As MalSymbol\n                return Me\n            End Function\n\n            Public Function getName() As String\n                return value\n            End Function\n            Public Overrides Function ToString() As String\n                return value\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                return value\n            End Function\n        End Class\n\n        Public Class MalString\n            Inherits MalVal\n            Private value As String\n            Public Sub New(v As String)\n                value = v\n            End Sub\n            Public Shadows Function copy() As MalString\n                return Me\n            End Function\n\n            Public Function getValue() As String\n                return value\n            End Function\n            Public Overrides Function ToString() As String\n                return \"\"\"\" & value & \"\"\"\"\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                If value.Length > 0 AndAlso value(0) = ChrW(&H029e) Then\n                    return \":\" & value.Substring(1)\n                Else If print_readably Then\n                    return \"\"\"\" & _\n                           value.Replace(\"\\\", \"\\\\\") _\n                                .Replace(\"\"\"\", \"\\\"\"\") _\n                                .Replace(Environment.NewLine, \"\\n\") & _\n                                \"\"\"\"\n                Else\n                    return value\n                End If\n            End Function\n        End Class\n\n\n        Public Class MalList\n            Inherits MalVal\n            Public start As String = \"(\"\n            Public last As String = \")\"\n            Private value As List(Of MalVal)\n            Public Sub New()\n                value = New List(Of MalVal)\n            End Sub\n            Public Sub New(val As List(Of MalVal))\n                value = val\n            End Sub\n            Public Sub New(ParamArray mvs() As MalVal)\n                value = New List(Of MalVal)\n                conj_BANG(mvs)\n            End Sub\n\n            Public Function getValue() As List(Of MalVal)\n                return value\n            End Function\n            Public Overrides Function list_Q() As Boolean\n                return True\n            End Function\n\n            Public Overrides Function ToString() As String\n                return start & printer.join(value, \" \", true) & last\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                return start & printer.join(value, \" \", print_readably) & last\n            End Function\n\n            Public Function conj_BANG(ParamArray mvs() As MalVal) As MalList \n                For i As Integer = 0 To mvs.Length-1\n                    value.Add(mvs(i))\n                Next\n                return Me\n            End Function\n\n            Public Function size() As Int64\n                return value.Count\n            End Function\n            Public Function nth(ByVal idx As Integer) As MalVal \n                If value.Count > idx Then\n                    return value(idx)\n                Else\n                    return Nil\n                End If\n            End Function\n            Default Public ReadOnly Property Item(idx As Integer) As MalVal\n                Get\n                    If value.Count > idx then\n                        return value(idx)\n                    Else\n                        return Nil\n                    End If\n                End Get\n            End Property\n            Public Function rest() As MalList\n                If size() > 0 Then\n                    return New MalList(value.GetRange(1, value.Count-1))\n                Else\n                    return New MalList()\n                End If\n            End Function\n            Public Overridable Function slice(start As Int64) As MalList \n                return New MalList(value.GetRange(start, value.Count-start))\n            End Function\n            Public Overridable Function slice(start As Int64, last As Int64) As MalList\n                return New MalList(value.GetRange(start, last-start))\n            End Function\n        End Class\n\n        Public Class MalVector\n            Inherits MalList\n'            ' Same implementation except for instantiation methods\n            Public Sub New()\n                MyBase.New()\n                start = \"[\"\n                last = \"]\"\n            End Sub\n            Public Sub New(val As List(Of MalVal))\n                MyBase.New(val)\n                start = \"[\"\n                last = \"]\"\n            End Sub\n\n            Public Overrides Function list_Q() As Boolean\n                return False\n            End Function\n\n            Public Overrides Function slice(start As Int64, last As Int64) As MalList\n                Dim val As List(Of MalVal) = Me.getValue()\n                return New MalVector(val.GetRange(start, val.Count-start))\n            End Function\n        End Class\n\n        Public Class MalHashMap\n            Inherits MalVal\n            Private value As Dictionary(Of string, MalVal)\n            Public Sub New(val As Dictionary(Of String, MalVal))\n                value = val\n            End Sub\n            Public Sub New(lst As MalList)\n                value = New Dictionary(Of String, MalVal)\n                assoc_BANG(lst)\n            End Sub\n            Public Shadows Function copy() As MalHashMap \n                Dim new_self As MalHashMap = DirectCast(Me.MemberwiseClone(),MalHashMap)\n                new_self.value = New Dictionary(Of String, MalVal)(value)\n                return new_self\n            End Function\n\n            Public Function getValue() As Dictionary(Of String, MalVal)\n                return value\n            End Function\n\n            Public Overrides Function ToString() As String\n                return \"{\" & printer.join(value, \" \", true) & \"}\"\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                return \"{\" & printer.join(value, \" \", print_readably) & \"}\"\n            End Function\n\n            Public Function assoc_BANG(lst As MalList) As MalHashMap\n                For i As Integer = 0 To lst.size()-1 Step 2\n                    value(DirectCast(lst(i),MalString).getValue()) = lst(i+1)\n                Next\n                return Me\n            End Function\n\n            Public Function dissoc_BANG(lst As MalList) As MalHashMap\n                for i As Integer = 0 To lst.size()-1\n                    value.Remove(DirectCast(lst.nth(i),MalString).getValue())\n                Next\n                return Me\n            End Function\n        End Class\n\n        Public Class MalAtom\n            Inherits MalVal\n            Private value As MalVal\n            Public Sub New(val As MalVal)\n                value = val\n            End Sub\n            'Public MalAtom copy() { return New MalAtom(value) }\n            Public Function getValue() As MalVal\n                return value\n            End Function\n            Public Function setValue(val As MalVal) As MalVal\n                value = val\n                return value\n            End Function\n            Public Overrides Function ToString() As String\n                return \"(atom \" & printer._pr_str(value, true) & \")\"\n            End Function\n            Public Overrides Function ToString(print_readably As Boolean) As String\n                return \"(atom \" & printer._pr_str(value, print_readably) & \")\"\n            End Function\n        End Class\n\n        Public Class MalFunc\n            Inherits MalVal\n            Private fn As Func(Of MalList, MalVal) = Nothing\n            Private ast As MalVal = Nothing\n            Private env As Mal.env.Env = Nothing\n            Private fparams As MalList\n            Private macro As Boolean = False\n            Public Sub New(new_fn As Func(Of MalList, MalVal))\n                fn = new_fn\n            End Sub\n            Public Sub New(new_ast As MalVal, new_env As Mal.env.Env,\n                           new_fparams As MalList, new_fn As Func(Of MalList, MalVal))\n                fn = new_fn\n                ast = new_ast\n                env = new_env\n                fparams = new_fparams\n            End Sub\n\n            Public Overrides Function ToString() As String\n                If Not ast Is Nothing Then\n                    return \"<fn* \" & Mal.printer._pr_str(fparams,true) &\n                           \" \" & Mal.printer._pr_str(ast, true) & \">\"\n                Else\n                    return \"<builtin_function \" & fn.ToString() & \">\"\n                End If\n            End Function\n\n            Public Function apply(args As MalList) As MalVal\n                return fn(args)\n            End Function\n\n            Public Function getAst() As MalVal\n                return ast\n            End Function\n            Public Function getEnv() As Mal.env.Env\n                return env\n            End Function\n            Public Function getFParams() As MalList\n                return fparams\n            End Function\n            Public Function genEnv(args As MalList) As Mal.env.Env\n                return New Mal.env.Env(env, fparams, args)\n            End Function\n            Public Function isMacro() As Boolean\n                return macro\n            End Function\n            Public Function asMacro() As MalVal\n                Dim res As new MalFunc (ast, env, fparams, fn)\n                res.macro = true\n                return res\n            End Function\n        End Class\n    End Class\nEnd Namespace\n"
  },
  {
    "path": "impls/vbs/Makefile",
    "content": "all:\n\ttrue\n\nclean:\n"
  },
  {
    "path": "impls/vbs/core.vbs",
    "content": "Option Explicit\r\n\r\nSub CheckArgNum(objArgs, lngArgNum)\r\n\tIf objArgs.Count - 1 <> lngArgNum Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckArgNum\", \"Wrong number of arguments.\"\r\n\tEnd IF\r\nEnd Sub\r\n\r\nSub CheckType(objMal, varType)\r\n\tIf objMal.Type <> varType Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckType\", \"Wrong argument type.\"\r\n\tEnd IF\r\nEnd Sub\r\n\r\nFunction IsListOrVec(objMal)\r\n\tIsListOrVec = _\r\n\t\tobjMal.Type = TYPES.LIST Or _\r\n\t\tobjMal.Type = TYPES.VECTOR\r\nEnd Function\r\n\r\nSub CheckListOrVec(objMal)\r\n\tIf Not IsListOrVec(objMal) Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckListOrVec\", _\r\n\t\t\t\"Wrong argument type, need a list or a vector.\"\r\n\tEnd If\r\nEnd Sub\r\n\r\nDim objNS\r\nSet objNS = NewEnv(Nothing)\r\n\r\nFunction MAdd(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MAdd = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value + objArgs.Item(2).Value)\r\nEnd Function\r\nobjNS.Add \"+\", NewVbsProc(\"MAdd\", False)\r\n\r\nFunction MSub(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MSub = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value - objArgs.Item(2).Value)\r\nEnd Function\r\nobjNS.Add \"-\", NewVbsProc(\"MSub\", False)\r\n\r\nFunction MMul(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MMul = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value * objArgs.Item(2).Value)\r\nEnd Function\r\nobjNS.Add \"*\", NewVbsProc(\"MMul\", False)\r\n\r\nFunction MDiv(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MDiv = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value \\ objArgs.Item(2).Value)\r\nEnd Function\r\nobjNS.Add \"/\", NewVbsProc(\"MDiv\", False)\r\n\r\nFunction MList(objArgs, objEnv)\r\n\tDim varRet\r\n\tSet varRet = NewMalList(Array())\r\n\tDim i\r\n\tFor i = 1 To objArgs.Count - 1\r\n\t\tvarRet.Add objArgs.Item(i)\r\n\tNext\r\n\tSet MList = varRet\r\nEnd Function\r\nobjNS.Add \"list\", NewVbsProc(\"MList\", False)\r\n\r\nFunction MIsList(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet MIsList = NewMalBool(objArgs.Item(1).Type = TYPES.LIST)\r\nEnd Function\r\nobjNS.Add \"list?\", NewVbsProc(\"MIsList\", False)\r\n\r\nFunction MIsEmpty(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tCheckListOrVec objArgs.Item(1)\r\n\r\n\tSet MIsEmpty = NewMalBool(objArgs.Item(1).Count = 0)\r\nEnd Function\r\nobjNS.Add \"empty?\", NewVbsProc(\"MIsEmpty\", False)\r\n\r\nFunction MCount(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tIf objArgs.Item(1).Type = TYPES.NIL Then\r\n\t\tSet MCount = NewMalNum(0)\r\n\tElse\r\n\t\tCheckListOrVec objArgs.Item(1)\r\n\t\tSet MCount = NewMalNum(objArgs.Item(1).Count)\r\n\tEnd If\r\nEnd Function\r\nobjNS.Add \"count\", NewVbsProc(\"MCount\", False)\r\n\r\nFunction MEqual(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim boolResult, i\r\n\tIf IsListOrVec(objArgs.Item(1)) And _\r\n\t\tIsListOrVec(objArgs.Item(2)) Then\r\n\t\tIf objArgs.Item(1).Count <> objArgs.Item(2).Count Then\r\n\t\t\tSet varRet = NewMalBool(False)\r\n\t\tElse\r\n\t\t\tboolResult = True\r\n\t\t\tFor i = 0 To objArgs.Item(1).Count - 1\r\n\t\t\t\tboolResult = boolResult And _\r\n\t\t\t\t\tMEqual(NewMalList(Array(Nothing, _\r\n\t\t\t\t\tobjArgs.Item(1).Item(i), _\r\n\t\t\t\t\tobjArgs.Item(2).Item(i))), objEnv).Value\r\n\t\t\tNext\r\n\t\t\tSet varRet = NewMalBool(boolResult)\t\r\n\t\tEnd If\r\n\tElse\r\n\t\tIf objArgs.Item(1).Type <> objArgs.Item(2).Type Then\r\n\t\t\tSet varRet = NewMalBool(False)\r\n\t\tElse\r\n\t\t\tSelect Case objArgs.Item(1).Type\r\n\t\t\t\tCase TYPES.HASHMAP\r\n\t\t\t\t\t'Err.Raise vbObjectError, _\r\n\t\t\t\t\t'\t\"MEqual\", \"Not implement yet~\"\r\n\t\t\t\t\tIf UBound(objArgs.Item(1).Keys) <> UBound(objArgs.Item(2).Keys) Then\r\n\t\t\t\t\t\tSet varRet = NewMalBool(False)\r\n\t\t\t\t\t\tSet MEqual = varRet\r\n\t\t\t\t\t\tExit Function\r\n\t\t\t\t\tEnd If\r\n\t\t\t\t\t\r\n\t\t\t\t\tboolResult = True\r\n\t\t\t\t\tFor Each i In objArgs.Item(1).Keys\r\n\t\t\t\t\t\tIf Not objArgs.Item(2).Exists(i) Then\r\n\t\t\t\t\t\t\tSet varRet = NewMalBool(False)\r\n\t\t\t\t\t\t\tSet MEqual = varRet\r\n\t\t\t\t\t\t\tExit Function\r\n\t\t\t\t\t\tEnd If\r\n\r\n\t\t\t\t\t\tboolResult = boolResult And _\r\n\t\t\t\t\t\t\tMEqual(NewMalList(Array(Nothing, objArgs.Item(1).Item(i), objArgs.Item(2).Item(i))), objEnv).Value\r\n\t\t\t\t\tNext\r\n\t\t\t\t\tSet varRet = NewMalBool(boolResult)\t\r\n\t\t\t\t\t\r\n\t\t\t\tCase Else\r\n\t\t\t\t\tSet varRet = NewMalBool( _\r\n\t\t\t\t\t\tobjArgs.Item(1).Value = objArgs.Item(2).Value)\r\n\t\t\tEnd Select\r\n\t\tEnd If\r\n\tEnd If\r\n\r\n\tSet MEqual = varRet\r\nEnd Function\r\nobjNS.Add \"=\", NewVbsProc(\"MEqual\", False)\r\n\r\nFunction MGreater(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet varRet = NewMalBool( _\r\n\t\tobjArgs.Item(1).Value > objArgs.Item(2).Value)\r\n\tSet MGreater = varRet\r\nEnd Function\r\nobjNS.Add \">\", NewVbsProc(\"MGreater\", False)\r\n\r\nFunction MPrStr(objArgs, objEnv)\r\n\tDim varRet\r\n\tDim strRet\r\n\tstrRet = \"\"\r\n\tDim i\r\n\tIf objArgs.Count - 1 >= 1 Then\r\n\t\tstrRet = PrintMalType(objArgs.Item(1), True)\r\n\tEnd If\r\n\tFor i = 2 To objArgs.Count - 1\r\n\t\tstrRet = strRet + \" \" + _\r\n\t\t\tPrintMalType(objArgs.Item(i), True)\r\n\tNext\r\n\tSet varRet = NewMalStr(strRet)\r\n\tSet MPrStr = varRet\r\nEnd Function\r\nobjNS.Add \"pr-str\", NewVbsProc(\"MPrStr\", False)\r\n\r\nFunction MStr(objArgs, objEnv)\r\n\tDim varRet\r\n\tDim strRet\r\n\tstrRet = \"\"\r\n\tDim i\r\n\tFor i = 1 To objArgs.Count - 1\r\n\t\tstrRet = strRet + _\r\n\t\t\tPrintMalType(objArgs.Item(i), False)\r\n\tNext\r\n\tSet varRet = NewMalStr(strRet)\r\n\tSet MStr = varRet\r\nEnd Function\r\nobjNS.Add \"str\", NewVbsProc(\"MStr\", False)\r\n\r\nFunction MPrn(objArgs, objEnv)\r\n\tDim varRet\r\n\tDim objStr\r\n\tSet objStr = MPrStr(objArgs, objEnv)\r\n\tIO.WriteLine objStr.Value\r\n\tSet varRet = NewMalNil()\r\n\tSet MPrn = varRet\r\nEnd Function\r\nobjNS.Add \"prn\", NewVbsProc(\"MPrn\", False)\r\n\r\nFunction MPrintln(objArgs, objEnv)\r\n\tDim varRet\r\n\tDim strRes\r\n\tstrRes = \"\"\r\n\tDim i\r\n\tIf objArgs.Count - 1 >= 1 Then\r\n\t\tstrRes = PrintMalType(objArgs.Item(1), False)\r\n\tEnd If\r\n\tFor i = 2 To objArgs.Count - 1\r\n\t\tstrRes = strRes + \" \" + _\r\n\t\t\tPrintMalType(objArgs.Item(i), False)\r\n\tNext\r\n\tIO.WriteLine strRes\r\n\tSet varRet = NewMalNil()\r\n\tSet MPrintln = varRet\r\nEnd Function\r\nobjNS.Add \"println\", NewVbsProc(\"MPrintln\", False)\r\n\r\nSub InitBuiltIn()\r\n\tREP \"(def! not (fn* [bool] (if bool false true)))\"\r\n\tREP \"(def! <= (fn* [a b] (not (> a b))))\"\r\n\tREP \"(def! < (fn* [a b] (> b a)))\"\r\n\tREP \"(def! >= (fn* [a b] (not (> b a))))\"\r\n\tREP \"(def! load-file (fn* (f) (eval (read-string (str \"\"(do \"\" (slurp f) \"\"\\nnil)\"\")))))\"\r\n\tREP \"(def! cons (fn* [a b] (concat (list a) b)))\"\r\n\tREP \"(def! nil? (fn* [x] (= x nil)))\"\r\n\tREP \"(def! true? (fn* [x] (= x true)))\"\r\n\tREP \"(def! false? (fn* [x] (= x false)))\"\r\n\tREP \"(def! vector (fn* [& args] (vec args)))\"\r\n\tREP \"(def! vals (fn* [hmap] (map (fn* [key] (get hmap key)) (keys hmap))))\"\r\n\tREP \"(def! *host-language* \"\"VBScript\"\")\"\r\nEnd Sub\r\n\r\nFunction MReadStr(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tCheckType objArgs.Item(1), TYPES.STRING\r\n\r\n\tSet varRes = ReadString(objArgs.Item(1).Value)\r\n\tIf TypeName(varRes) = \"Nothing\" Then\r\n\t\tSet varRes = NewMalNil()\r\n\tEnd If\r\n\tSet MReadStr = varRes\r\nEnd Function\r\nobjNS.Add \"read-string\", NewVbsProc(\"MReadStr\", False)\r\n\r\nFunction MSlurp(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tCheckType objArgs.Item(1), TYPES.STRING\r\n\r\n\tDim strRes\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tstrRes = .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & objArgs.Item(1).Value).ReadAll\r\n\tEnd With\r\n\r\n\tSet varRes = NewMalStr(strRes)\r\n\tSet MSlurp = varRes\r\nEnd Function\r\nobjNS.Add \"slurp\", NewVbsProc(\"MSlurp\", False)\r\n\r\nFunction MAtom(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = NewMalAtom(objArgs.Item(1))\r\n\tSet MAtom = varRes\r\nEnd Function\r\nobjNS.Add \"atom\", NewVbsProc(\"MAtom\", False)\r\n\r\nFunction MIsAtom(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = NewMalBool(objArgs.Item(1).Type = TYPES.ATOM)\r\n\tSet MIsAtom = varRes\r\nEnd Function\r\nobjNS.Add \"atom?\", NewVbsProc(\"MIsAtom\", False)\r\n\r\nFunction MDeref(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tCheckType objArgs.Item(1), TYPES.ATOM\r\n\r\n\tSet varRes = objArgs.Item(1).Value\r\n\tSet MDeref = varRes\r\nEnd Function\r\nobjNS.Add \"deref\", NewVbsProc(\"MDeref\", False)\r\n\r\nFunction MReset(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.ATOM\r\n\r\n\tobjArgs.Item(1).Reset objArgs.Item(2)\r\n\tSet varRes = objArgs.Item(2)\r\n\tSet MReset = varRes\r\nEnd Function\r\nobjNS.Add \"reset!\", NewVbsProc(\"MReset\", False)\r\n\r\nFunction MSwap(objArgs, objEnv)\r\n\tDim varRes\r\n\tIf objArgs.Count - 1 < 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MSwap\", \"Need more arguments.\"\r\n\tEnd If\r\n\r\n\tDim objAtom, objFn\r\n\tSet objAtom = objArgs.Item(1)\r\n\tCheckType objAtom, TYPES.ATOM\r\n\tSet objFn = objArgs.Item(2)\r\n\tCheckType objFn, TYPES.PROCEDURE\r\n\r\n\tDim objProg\r\n\tSet objProg = NewMalList(Array(objFn))\r\n\tobjProg.Add objAtom.Value\r\n\tDim i\r\n\tFor i = 3 To objArgs.Count - 1\r\n\t\tobjProg.Add objArgs.Item(i)\r\n\tNext\r\n\r\n\tobjAtom.Reset objFn.ApplyWithoutEval(objProg, objEnv)\r\n\tSet varRes = objAtom.Value\r\n\tSet MSwap = varRes\r\nEnd Function\r\nobjNS.Add \"swap!\", NewVbsProc(\"MSwap\", False)\r\n\r\nFunction MConcat(objArgs, objEnv)\r\n\tDim varRes\r\n\tDim i, j\r\n\tSet varRes = NewMalList(Array())\r\n\tFor i = 1 To objArgs.Count - 1\r\n\t\tIf Not IsListOrVec(objArgs.Item(i)) Then\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MConcat\", \"Invaild argument(s).\"\r\n\t\tEnd If\r\n\t\t\r\n\t\tFor j = 0 To objArgs.Item(i).Count - 1\r\n\t\t\tvarRes.Add objArgs.Item(i).Item(j)\r\n\t\tNext\r\n\tNext\r\n\tSet MConcat = varRes\r\nEnd Function\r\nobjNS.Add \"concat\", NewVbsProc(\"MConcat\", False)\r\n\r\nFunction MVec(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tCheckListOrVec objArgs.Item(1)\r\n\tSet varRes = NewMalVec(Array())\r\n\tDim i\r\n\tFor i = 0 To objArgs.Item(1).Count - 1\r\n\t\tvarRes.Add objArgs.Item(1).Item(i)\r\n\tNext\r\n\tSet MVec = varRes\r\nEnd Function\r\nobjNS.Add \"vec\", NewVbsProc(\"MVec\", False)\r\n\r\nFunction MNth(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 2\r\n\tCheckListOrVec objArgs.Item(1)\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\r\n\tIf objArgs.Item(2).Value < objArgs.Item(1).Count Then\r\n\t\tSet varRes = objArgs.Item(1).Item(objArgs.Item(2).Value)\r\n\tElse\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MNth\", \"Index out of bounds.\"\r\n\tEnd If\r\n\r\n\tSet MNth = varRes\r\nEnd Function\r\nobjNS.Add \"nth\", NewVbsProc(\"MNth\", False)\r\n\r\nFunction MFirst(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\t\r\n\tIf objArgs.Item(1).Type = TYPES.NIL Then\r\n\t\tSet varRes = NewMalNil()\r\n\t\tSet MFirst = varRes\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tCheckListOrVec objArgs.Item(1)\r\n\r\n\tIf objArgs.Item(1).Count < 1 Then\r\n\t\tSet varRes = NewMalNil()\r\n\tElse\r\n\t\tSet varRes = objArgs.Item(1).Item(0)\r\n\tEnd If\r\n\r\n\tSet MFirst = varRes\r\nEnd Function\r\nobjNS.Add \"first\", NewVbsProc(\"MFirst\", False)\r\n\r\nFunction MRest(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\t\r\n\tIf objArgs.Item(1).Type = TYPES.NIL Then\r\n\t\tSet varRes = NewMalList(Array())\r\n\t\tSet MRest = varRes\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tDim objList\r\n\tSet objList = objArgs.Item(1)\r\n\tCheckListOrVec objList\r\n\r\n\tSet varRes = NewMalList(Array())\r\n\tDim i\r\n\tFor i = 1 To objList.Count - 1\r\n\t\tvarRes.Add objList.Item(i)\r\n\tNext\r\n\t\r\n\tSet MRest = varRes\r\nEnd Function\r\nobjNS.Add \"rest\", NewVbsProc(\"MRest\", False)\r\n\r\nSub InitMacro()\r\n\tREP \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"\"odd number of forms to cond\"\")) (cons'cond (rest (rest xs)))))))\"\r\n\t'REP \"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))\"\r\n\tREP \"(def! *gensym-counter* (atom 0))\"\r\n\tREP \"(def! gensym (fn* [] (symbol (str \"\"G__\"\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))\"\r\n\tREP \"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))\"\r\nEnd Sub\r\n\r\nClass MalException\r\n\tPrivate objDict\r\n\tPrivate Sub Class_Initialize\r\n\t\tSet objDict = CreateObject(\"Scripting.Dictionary\")\r\n\tEnd Sub\r\n\r\n\tPublic Sub Add(varKey, varValue)\r\n\t\tobjDict.Add varKey, varValue\r\n\tEnd Sub\r\n\r\n\tPublic Function Item(varKey)\r\n\t\tSet Item = objDict.Item(varKey)\r\n\tEnd Function\r\n\r\n\tPublic Sub Remove(varKey)\r\n\t\tobjDict.Remove varKey\r\n\tEnd Sub\r\nEnd Class\r\n\r\nDim objExceptions\r\nSet objExceptions = New MalException\r\n\r\nFunction MThrow(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tDim strRnd\r\n\tstrRnd = CStr(Rnd())\r\n\tobjExceptions.Add strRnd, objArgs.Item(1)\r\n\tErr.Raise vbObjectError, _\r\n\t\t\"MThrow\", strRnd\r\nEnd Function\r\nobjNS.Add \"throw\", NewVbsProc(\"MThrow\", False)\r\n\r\nFunction MApply(objArgs, objEnv)\r\n\tDim varRes\r\n\tIf objArgs.Count - 1 < 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MApply\", \"Need more arguments.\"\r\n\tEnd If\r\n\t\r\n\tDim objFn\r\n\tSet objFn = objArgs.Item(1)\r\n\tCheckType objFn, TYPES.PROCEDURE\r\n\t' If objFn.IsSpecial Or objFn.IsMacro Then\r\n\t' \tErr.Raise vbObjectError, _\r\n\t' \t\t\"MApply\", \"Need a function.\"\r\n\t' End If\r\n\r\n\tDim objAST\r\n\tSet objAST = NewMalList(Array(objFn))\r\n\tDim i\r\n\tFor i = 2 To objArgs.Count - 2\r\n\t\tobjAST.Add objArgs.Item(i)\r\n\tNext\r\n\r\n\tDim objSeq\r\n\tSet objSeq = objArgs.Item(objArgs.Count - 1)\r\n\tCheckListOrVec objSeq\r\n\r\n\tFor i = 0 To objSeq.Count - 1\r\n\t\tobjAST.Add objSeq.Item(i)\r\n\tNext\r\n\t\r\n\tSet varRes = objFn.ApplyWithoutEval(objAST, objEnv)\r\n\tSet MApply = varRes\r\nEnd Function\r\nobjNS.Add \"apply\", NewVbsProc(\"MApply\", False)\r\n\r\nFunction MMap(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 2\r\n\tDim objFn, objSeq\r\n\tSet objFn = objArgs.Item(1)\r\n\tSet objSeq = objArgs.Item(2)\r\n\tCheckType objFn, TYPES.PROCEDURE\r\n\tCheckListOrVec objSeq\r\n\tIf objFn.IsSpecial Or objFn.IsMacro Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MApply\", \"Need a function.\"\r\n\tEnd If\r\n\r\n\tSet varRes = NewMalList(Array())\r\n\tDim i\r\n\tFor i = 0 To objSeq.Count - 1\r\n\t\tvarRes.Add objFn.ApplyWithoutEval(NewMalList(Array( _\r\n\t\t\tobjFn, objSeq.Item(i))), objEnv)\r\n\tNext\r\n\r\n\tSet MMap = varRes\r\nEnd Function\r\nobjNS.Add \"map\", NewVbsProc(\"MMap\", False)\r\n\r\nFunction MIsSymbol(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tSet varRes = NewMalBool(objArgs.Item(1).Type = TYPES.SYMBOL)\r\n\tSet MIsSymbol = varRes\r\nEnd Function\r\nobjNS.Add \"symbol?\", NewVbsProc(\"MIsSymbol\", False)\r\n\r\nFunction MSymbol(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tCheckType objArgs.Item(1), TYPES.STRING\r\n\tSet varRes = NewMalSym(objArgs.Item(1).Value)\r\n\tSet MSymbol = varRes\r\nEnd Function\r\nobjNS.Add \"symbol\", NewVbsProc(\"MSymbol\", False)\r\n\r\nFunction MKeyword(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tSelect Case objArgs.Item(1).Type\r\n\t\tCase TYPES.STRING\r\n\t\t\tSet varRes = NewMalKwd(\":\" + objArgs.Item(1).Value)\r\n\t\tCase TYPES.KEYWORD\r\n\t\t\tSet varRes = objArgs.Item(1)\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MKeyword\", \"Unexpect argument(s).\"\r\n\tEnd Select\r\n\tSet MKeyword = varRes\r\nEnd Function\r\nobjNS.Add \"keyword\", NewVbsProc(\"MKeyword\", False)\r\n\r\nFunction MIsKeyword(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tSet varRes = NewMalBool(objArgs.Item(1).Type = TYPES.KEYWORD)\r\n\tSet MIsKeyword = varRes\r\nEnd Function\r\nobjNS.Add \"keyword?\", NewVbsProc(\"MIsKeyword\", False)\r\n\r\nFunction MIsSeq(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tSet varRes = NewMalBool( _\r\n\t\tobjArgs.Item(1).Type = TYPES.LIST Or _\r\n\t\tobjArgs.Item(1).Type = TYPES.VECTOR)\r\n\tSet MIsSeq = varRes\r\nEnd Function\r\nobjNS.Add \"sequential?\", NewVbsProc(\"MIsSeq\", False)\r\n\r\nFunction MIsVec(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tSet varRes = NewMalBool(objArgs.Item(1).Type = TYPES.VECTOR)\r\n\tSet MIsVec = varRes\r\nEnd Function\r\nobjNS.Add \"vector?\", NewVbsProc(\"MIsVec\", False)\r\n\r\nFunction MIsMap(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tSet varRes = NewMalBool(objArgs.Item(1).Type = TYPES.HASHMAP)\r\n\tSet MIsMap = varRes\r\nEnd Function\r\nobjNS.Add \"map?\", NewVbsProc(\"MIsMap\", False)\r\n\r\nFunction MHashMap(objArgs, objEnv)\r\n\tDim varRes\r\n\tIf objArgs.Count Mod 2 <> 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MHashMap\", \"Unexpect argument(s).\"\r\n\tEnd If\r\n\tSet varRes = NewMalMap(Array(), Array())\r\n\tDim i\r\n\tFor i = 1 To objArgs.Count - 1 Step 2\r\n\t\tvarRes.Add objArgs.Item(i), objArgs.Item(i + 1)\r\n\tNext\r\n\tSet MHashMap = varRes\r\nEnd Function\r\nobjNS.Add \"hash-map\", NewVbsProc(\"MHashMap\", False)\r\n\r\nFunction MAssoc(objArgs, objEnv)\r\n\tDim varRes\r\n\tIf objArgs.Count - 1 < 3 Or objArgs.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MHashMap\", \"Unexpect argument(s).\"\r\n\tEnd If\r\n\t\r\n\tDim objMap\r\n\tSet objMap = objArgs.Item(1)\r\n\tCheckType objMap, TYPES.HASHMAP\r\n\r\n\tDim i\r\n\tSet varRes = NewMalMap(Array(), Array())\r\n\tFor Each i In objMap.Keys\r\n\t\tvarRes.Add i, objMap.Item(i)\r\n\tNext\r\n\tFor i = 2 To objArgs.Count - 1 Step 2\r\n\t\tvarRes.Add objArgs.Item(i), objArgs.Item(i + 1)\r\n\tNext\r\n\tSet MAssoc = varRes\r\nEnd Function\r\nobjNS.Add \"assoc\", NewVbsProc(\"MAssoc\", False)\r\n\r\nFunction MGet(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 2\r\n\t\r\n\tIf objArgs.Item(1).Type = TYPES.NIL Then\r\n\t\tSet varRes = NewMalNil()\r\n\tElse\r\n\t\tCheckType objArgs.Item(1), TYPES.HASHMAP\r\n\t\tIf objArgs.Item(1).Exists(objArgs.Item(2)) Then\r\n\t\t\tSet varRes = objArgs.Item(1).Item(objArgs.Item(2))\r\n\t\tElse\r\n\t\t\tSet varRes = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\t\r\n\tSet MGet = varRes\r\nEnd Function\r\nobjNS.Add \"get\", NewVbsProc(\"MGet\", False)\r\n\r\nFunction MDissoc(objArgs, objEnv)\r\n\tDim varRes\r\n\t'CheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.HASHMAP\r\n\t\r\n\tIf objArgs.Item(1).Exists(objArgs.Item(2)) Then\r\n\t\tSet varRes = NewMalMap(Array(), Array())\r\n\t\t\r\n\t\tDim i\r\n\t\tDim j, boolFlag\r\n\t\tFor Each i In objArgs.Item(1).Keys\r\n\t\t\tboolFlag = True\r\n\t\t\tFor j = 2 To objArgs.Count - 1\r\n\t\t\t\tIf i.Type = objArgs.Item(j).Type And _\r\n\t\t\t\t\ti.Value = objArgs.Item(j).Value Then\r\n\t\t\t\t\tboolFlag = False\r\n\t\t\t\tEnd If\r\n\t\t\tNext\r\n\t\t\tIf boolFlag Then\r\n\t\t\t\tvarRes.Add i, objArgs.Item(1).Item(i)\r\n\t\t\tEnd If\r\n\t\tNext\r\n\tElse\r\n\t\tSet varRes = objArgs.Item(1)\r\n\tEnd If\r\n\r\n\tSet MDissoc = varRes\r\nEnd Function\r\nobjNS.Add \"dissoc\", NewVbsProc(\"MDissoc\", False)\r\n\r\nFunction MKeys(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tCheckType objArgs.Item(1), TYPES.HASHMAP\r\n\tSet MKeys = NewMalList(objArgs.Item(1).Keys)\r\nEnd Function\r\nobjNS.Add \"keys\", NewVbsProc(\"MKeys\", False)\r\n\r\nFunction MIsContains(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.HASHMAP\r\n\r\n\tSet MIsContains = NewMalBool(objArgs.Item(1).Exists(objArgs.Item(2)))\r\nEnd Function\r\nobjNS.Add \"contains?\", NewVbsProc(\"MIsContains\", False)\r\n\r\nFunction MReadLine(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\tCheckType objArgs.Item(1), TYPES.STRING\r\n\r\n\tDim strInput\r\n\tIO.Write objArgs.Item(1).Value\r\n\tOn Error Resume Next\r\n\t\tstrInput = IO.ReadLine\r\n\t\tIf Err.Number <> 0 Then\r\n\t\t\tSet varRes = NewMalNil()\r\n\t\tElse\r\n\t\t\tSet varRes = NewMalStr(strInput)\r\n\t\tEnd If\r\n\tOn Error Goto 0\r\n\tSet MReadLine = varRes\r\nEnd Function\r\nobjNS.Add \"readline\", NewVbsProc(\"MReadLine\", False)\r\n\r\nFunction MTimeMs(objArgs, objEnv)\r\n\tSet MTimeMs = NewMalNum(CLng(Timer * 1000))\r\nEnd Function\r\nobjNS.Add \"time-ms\", NewVbsProc(\"MTimeMs\", False)\r\n\r\nFunction MIsStr(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tSet MIsStr = NewMalBool(objArgs.Item(1).Type = TYPES.STRING)\r\nEnd Function\r\nobjNS.Add \"string?\", NewVbsProc(\"MIsStr\", False)\r\n\r\nFunction MIsNum(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tSet MIsNum = NewMalBool(objArgs.Item(1).Type = TYPES.NUMBER)\r\nEnd Function\r\nobjNS.Add \"number?\", NewVbsProc(\"MIsNum\", False)\r\n\r\nFunction MIsFn(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tDim varRes\r\n\tvarRes = objArgs.Item(1).Type = TYPES.PROCEDURE\r\n\tIf varRes Then\r\n\t\tvarRes = (Not objArgs.Item(1).IsMacro) And _\r\n\t\t\t(Not objArgs.Item(1).IsSpecial)\r\n\tEnd If\r\n\t\r\n\tSet MIsFn = NewMalBool(varRes)\r\nEnd Function\r\nobjNS.Add \"fn?\", NewVbsProc(\"MIsFn\", False)\r\n\r\n\r\nFunction MIsMacro(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tDim varRes\r\n\tvarRes = objArgs.Item(1).Type = TYPES.PROCEDURE\r\n\tIf varRes Then\r\n\t\tvarRes = objArgs.Item(1).IsMacro And _\r\n\t\t\t(Not objArgs.Item(1).IsSpecial)\r\n\tEnd If\r\n\t\r\n\tSet MIsMacro = NewMalBool(varRes)\r\nEnd Function\r\nobjNS.Add \"macro?\", NewVbsProc(\"MIsMacro\", False)\r\n\r\n\r\nFunction MMeta(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\t'CheckType objArgs.Item(1), TYPES.PROCEDURE\r\n\r\n\tDim varRes\r\n\tSet varRes = GetMeta(objArgs.Item(1))\r\n\tSet MMeta = varRes\r\nEnd Function\r\nobjNS.Add \"meta\", NewVbsProc(\"MMeta\", False)\r\n\r\nFunction MWithMeta(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\t'CheckType objArgs.Item(1), TYPES.PROCEDURE\r\n\r\n\tDim varRes\r\n\tSet varRes = SetMeta(objArgs.Item(1), objArgs.Item(2))\r\n\tSet MWithMeta = varRes\r\nEnd Function\r\nobjNS.Add \"with-meta\", NewVbsProc(\"MWithMeta\", False)\r\n\r\nFunction MConj(objArgs, objEnv)\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MConj\", \"Need more arguments.\"\r\n\tEnd If\r\n\tDim varRes\r\n\tDim objSeq\r\n\tSet objSeq = objArgs.Item(1)\r\n\tDim i\r\n\tSelect Case objSeq.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\tFor i = objArgs.Count - 1 To 2 Step -1\r\n\t\t\t\tvarRes.Add objArgs.Item(i)\r\n\t\t\tNext\r\n\t\t\tFor i = 0 To objSeq.Count - 1\r\n\t\t\t\tvarRes.Add objSeq.Item(i)\r\n\t\t\tNext\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRes = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objSeq.Count - 1\r\n\t\t\t\tvarRes.Add objSeq.Item(i)\r\n\t\t\tNext\r\n\t\t\tFor i = 2 To objArgs.Count - 1\r\n\t\t\t\tvarRes.Add objArgs.Item(i)\r\n\t\t\tNext\r\n\t\tCase Else\t\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MConj\", \"Unexpect argument type.\"\r\n\tEnd Select\r\n\tSet MConj = varRes\r\nEnd Function\r\nobjNS.Add \"conj\", NewVbsProc(\"MConj\", False)\r\n\r\nFunction MSeq(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tDim objSeq\r\n\tSet objSeq = objArgs.Item(1)\r\n\tDim varRes\r\n\tDim i\r\n\tSelect Case objSeq.Type\r\n\t\tCase TYPES.STRING\r\n\t\t\tIf objSeq.Value = \"\" Then\r\n\t\t\t\tSet varRes = NewMalNil()\r\n\t\t\tElse\r\n\t\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\t\tFor i = 1 To Len(objSeq.Value)\r\n\t\t\t\t\tvarRes.Add NewMalStr(Mid(objSeq.Value, i, 1))\r\n\t\t\t\tNext\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tIf objSeq.Count = 0 Then\r\n\t\t\t\tSet varRes = NewMalNil()\r\n\t\t\tElse\r\n\t\t\t\tSet varRes = objSeq\r\n\t\t\tEnd If\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tIf objSeq.Count = 0 Then\r\n\t\t\t\tSet varRes = NewMalNil()\r\n\t\t\tElse\r\n\t\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\t\tFor i = 0 To objSeq.Count - 1\r\n\t\t\t\t\tvarRes.Add objSeq.Item(i)\r\n\t\t\t\tNext\r\n\t\t\tEnd If\r\n\t\tCase TYPES.NIL\r\n\t\t\tSet varRes = NewMalNil()\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MSeq\", \"Unexpect argument type.\"\r\n\tEnd Select\r\n\tSet MSeq = varRes\r\nEnd Function\r\nobjNS.Add \"seq\", NewVbsProc(\"MSeq\", False)\r\n\r\n"
  },
  {
    "path": "impls/vbs/env.vbs",
    "content": "Option Explicit\r\n\r\nFunction NewEnv(objOuter)\r\n\tSet NewEnv = New Environment\r\n\tSet NewEnv.Outer = objOuter\r\nEnd Function\r\n\r\nClass Environment\r\n\tPublic objOuter\r\n\tPublic objBinds\r\n\tPrivate Sub Class_Initialize()\r\n\t\tSet objBinds = CreateObject(\"Scripting.Dictionary\")\r\n\t\tSet objOuter = Nothing\r\n\tEnd Sub\r\n\t\r\n\tPublic Property Set Outer(objEnv)\r\n\t\tSet objOuter = objEnv\r\n\tEnd Property\r\n\r\n\tPublic Sub Add(varKey, varValue)\r\n\t\tSet objBinds.Item(varKey) = varValue\r\n\tEnd Sub\r\n\r\n\tPublic Function [Get](varKey)\r\n\t\tDim objEnv, varRet\r\n\t\tSet objEnv = Me\r\n\t\tDo\r\n\t\t\tIf objEnv.objBinds.Exists(varKey) Then\r\n\t\t\t\tSet varRet = objEnv.objBinds(varKey)\r\n\t\t\t\tExit Do\r\n\t\t\tEnd If\r\n\t\t\tSet objEnv = objEnv.objOuter\r\n\t\t\tIf TypeName(objEnv) = \"Nothing\" Then\r\n\t\t\t\tSet varRet = Nothing\r\n\t\t\t\tExit Do\r\n\t\t\tEnd If\r\n\t\tLoop\r\n\r\n\t\tSet [Get] = varRet\r\n\tEnd Function\r\nEnd Class"
  },
  {
    "path": "impls/vbs/install.vbs",
    "content": "On Error Resume Next\r\nCreateObject(\"System.Collections.ArrayList\")"
  },
  {
    "path": "impls/vbs/io.vbs",
    "content": "Option Explicit\r\n\r\nClass IOWrap\r\n\tPublic NoStdErr\r\n\tPublic EchoStdIn\r\n\r\n\tPrivate Sub Class_Initialize\r\n\t\tWith WScript.CreateObject(\"WScript.Shell\")\r\n\t\t\tNoStdErr = .ExpandEnvironmentStrings(\"%MAL_VBS_IMPL_NO_STDERR%\") <> \"%MAL_VBS_IMPL_NO_STDERR%\"\r\n\t\t\tEchoStdIn = .ExpandEnvironmentStrings(\"%MAL_VBS_IMPL_ECHO_STDIN%\") <> \"%MAL_VBS_IMPL_ECHO_STDIN%\"\r\n\t\tEnd With\r\n\tEnd Sub\r\n\r\n\tPublic Sub Write(sText)\r\n\t\tWScript.StdOut.Write sText\r\n\tEnd Sub\r\n\r\n\tPublic Sub WriteLine(sText)\r\n\t\tWScript.StdOut.WriteLine sText\r\n\tEnd Sub\r\n\r\n\tPublic Function ReadLine()\r\n\t\tReadLine = WScript.StdIn.ReadLine\r\n\t\tIf EchoStdIn Then\r\n\t\t\tWScript.StdOut.WriteLine ReadLine\r\n\t\tEnd If\r\n\tEnd Function\r\n\r\n\tPublic Sub WriteErr(sText)\r\n\t\tIf Not NoStdErr Then\r\n\t\t\tWScript.StdErr.Write sText\r\n\t\tElse ' Redirect to StdOut\r\n\t\t\tWScript.StdOut.Write sText\r\n\t\tEnd If\r\n\tEnd Sub\r\n\r\n\tPublic Sub WriteErrLine(sText)\r\n\t\tIf Not NoStdErr Then\r\n\t\t\tWScript.StdErr.WriteLine sText\r\n\t\tElse ' Redirect to StdOut\r\n\t\t\tWScript.StdOut.WriteLine sText\r\n\t\tEnd If\r\n\tEnd Sub\r\nEnd Class\r\n\r\nDim IO\r\nSet IO = New IOWrap"
  },
  {
    "path": "impls/vbs/printer.vbs",
    "content": "Option Explicit\r\n\r\nFunction PrintMalType(objMal, boolReadable)\r\n\tDim varResult\r\n\r\n\tvarResult = \"\"\r\n\r\n\tIf TypeName(objMal) = \"Nothing\" Then\r\n\t\tPrintMalType = \"\"\r\n\t\tExit Function\r\n\tEnd If\r\n\t\r\n\tDim i\r\n\tSelect Case objMal.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tWith objMal\r\n\t\t\t\tFor i = 0 To .Count - 2\r\n\t\t\t\t\tvarResult = varResult & _\r\n\t\t\t\t\t\tPrintMalType(.Item(i), boolReadable) & \" \"\r\n\t\t\t\tNext\r\n\t\t\t\tIf .Count > 0 Then\r\n\t\t\t\t\tvarResult = varResult & _\r\n\t\t\t\t\t\tPrintMalType(.Item(.Count - 1), boolReadable)\r\n\t\t\t\tEnd If\r\n\t\t\tEnd With\r\n\t\t\tvarResult = \"(\" & varResult & \")\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tWith objMal\r\n\t\t\t\tFor i = 0 To .Count - 2\r\n\t\t\t\t\tvarResult = varResult & _\r\n\t\t\t\t\t\tPrintMalType(.Item(i), boolReadable) & \" \"\r\n\t\t\t\tNext\r\n\t\t\t\tIf .Count > 0 Then\r\n\t\t\t\t\tvarResult = varResult & _\r\n\t\t\t\t\t\tPrintMalType(.Item(.Count - 1), boolReadable)\r\n\t\t\t\tEnd If\r\n\t\t\tEnd With\r\n\t\t\tvarResult = \"[\" & varResult & \"]\"\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tWith objMal\r\n\t\t\t\tDim arrKeys\r\n\t\t\t\tarrKeys = .Keys\r\n\t\t\t\tFor i = 0 To .Count - 2\r\n\t\t\t\t\tvarResult = varResult & _\r\n\t\t\t\t\t\tPrintMalType(arrKeys(i), boolReadable) & \" \" & _\r\n\t\t\t\t\t\tPrintMalType(.Item(arrKeys(i)), boolReadable) & \" \"\r\n\t\t\t\tNext\r\n\t\t\t\tIf .Count > 0 Then\r\n\t\t\t\t\tvarResult = varResult & _\r\n\t\t\t\t\t\tPrintMalType(arrKeys(.Count - 1), boolReadable) & \" \" & _\r\n\t\t\t\t\t\tPrintMalType(.Item(arrKeys(.Count - 1)), boolReadable)\r\n\t\t\t\tEnd If\r\n\t\t\tEnd With\r\n\t\t\tvarResult = \"{\" & varResult & \"}\"\r\n\t\tCase TYPES.STRING\r\n\t\t\tIf boolReadable Then\r\n\t\t\t\tvarResult = EscapeString(objMal.Value)\r\n\t\t\tElse\r\n\t\t\t\tvarResult = objMal.Value\r\n\t\t\tEnd If\r\n\t\tCase TYPES.BOOLEAN\r\n\t\t\tIf objMal.Value Then\r\n\t\t\t\tvarResult = \"true\"\r\n\t\t\tElse\r\n\t\t\t\tvarResult = \"false\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.NIL\r\n\t\t\tvarResult = \"nil\"\r\n\t\tCase TYPES.NUMBER\r\n\t\t\tvarResult = CStr(objMal.Value)\r\n\t\tCase TYPES.PROCEDURE\r\n\t\t\tvarResult = \"#<function>\"\r\n\t\tCase TYPES.KEYWORD\r\n\t\t\tvarResult = objMal.Value\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tvarResult = objMal.Value\r\n\t\tCase TYPES.ATOM\r\n\t\t\tvarResult = \"(atom \" + PrintMalType(objMal.Value, boolReadable) + \")\"\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"PrintMalType\", \"Unknown type.\"\r\n\tEnd Select\r\n\r\n\tPrintMalType = varResult\r\nEnd Function\r\n\r\nFunction EscapeString(strRaw)\r\n\tEscapeString = strRaw\r\n\tEscapeString = Replace(EscapeString, \"\\\", \"\\\\\")\r\n\tEscapeString = Replace(EscapeString, vbCrLf, vbLf)\r\n\tEscapeString = Replace(EscapeString, vbCr, vbLf)\r\n\tEscapeString = Replace(EscapeString, vbLf, \"\\n\")\r\n\tEscapeString = Replace(EscapeString, \"\"\"\", \"\\\"\"\")\r\n\tEscapeString = \"\"\"\" & EscapeString & \"\"\"\"\r\nEnd Function\r\n"
  },
  {
    "path": "impls/vbs/reader.vbs",
    "content": "Option Explicit\r\n\r\nFunction ReadString(strCode)\r\n\tDim objTokens\r\n\tSet objTokens = Tokenize(strCode)\r\n\tSet ReadString = ReadForm(objTokens)\r\n\tIf Not objTokens.AtEnd() Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadForm\", \"extra token '\" + objTokens.Current() + \"'.\"\r\n\tEnd If\r\nEnd Function\r\n\r\nClass Tokens\r\n\tPrivate objQueue\r\n\tPrivate objRE\r\n\r\n\tPrivate Sub Class_Initialize\r\n\t\tSet objRE = New RegExp\r\n\t\tWith objRE\r\n\t\t\t.Pattern = \"[\\s,]*\" + _\r\n\t\t\t\t\"(\" + _\r\n\t\t\t\t\t\"~@\" + \"|\" + _\r\n\t\t\t\t\t\"[\\[\\]{}()'`~^@]\" + \"|\" + _\r\n\t\t\t\t\t\"\"\"(?:\\\\.|[^\\\\\"\"])*\"\"?\" + \"|\" + _\r\n\t\t\t\t\t\";.*\" + \"|\" + _\r\n\t\t\t\t\t\"[^\\s\\[\\]{}('\"\"`,;)]*\" + _\r\n\t\t\t\t\")\"\r\n\t\t\t.IgnoreCase = True\r\n\t\t\t.Global = True\r\n\t\tEnd With\r\n\r\n\t\tSet objQueue = CreateObject(\"System.Collections.Queue\")\r\n\tEnd Sub\r\n\r\n\tPublic Function Init(strCode)\r\n\t\tDim objMatches, objMatch\r\n\t\tSet objMatches = objRE.Execute(strCode)\r\n\t\tDim strToken\r\n\t\tFor Each objMatch In objMatches\r\n\t\t\tstrToken = Trim(objMatch.SubMatches(0))\r\n\t\t\tIf Not (Left(strToken, 1) = \";\" Or strToken = \"\") Then\r\n\t\t\t\tobjQueue.Enqueue strToken\r\n\t\t\tEnd If\r\n\t\tNext\r\n\tEnd Function\r\n\r\n\tPublic Function Current()\r\n\t\tCurrent = objQueue.Peek()\r\n\tEnd Function\r\n\r\n\tPublic Function MoveToNext()\r\n\t\tMoveToNext = objQueue.Dequeue()\r\n\tEnd Function\r\n\r\n\tPublic Function AtEnd()\r\n\t\tAtEnd = (objQueue.Count = 0)\r\n\tEnd Function\r\n\r\n\tPublic Function Count()\r\n\t\tCount = objQueue.Count\r\n\tEnd Function\r\nEnd Class\r\n\r\nFunction Tokenize(strCode) ' Return objTokens\r\n\tDim varResult\r\n\tSet varResult = New Tokens\r\n\tvarResult.Init strCode\r\n\tSet Tokenize = varResult\r\nEnd Function\r\n\r\nFunction ReadForm(objTokens) ' Return Nothing / MalType\r\n\tIf objTokens.AtEnd() Then\r\n\t\tSet ReadForm = Nothing\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tDim strToken\r\n\tstrToken = objTokens.Current()\r\n\r\n\tDim varResult\r\n\tIf InStr(\"([{\", strToken) Then\r\n\t\tSelect Case strToken\r\n\t\t\tCase \"(\"\r\n\t\t\t\tSet varResult = ReadList(objTokens)\r\n\t\t\tCase \"[\"\r\n\t\t\t\tSet varResult = ReadVector(objTokens)\r\n\t\t\tCase \"{\"\r\n\t\t\t\tSet varResult = ReadHashmap(objTokens)\r\n\t\tEnd Select\r\n\tElseIf InStr(\"'`~@\", strToken) Then\r\n\t\tSet varResult = ReadSpecial(objTokens)\r\n\tElseIf InStr(\")]}\", strToken) Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadForm\", \"unbalanced parentheses.\"\r\n\tElseIf strToken = \"^\" Then\r\n\t\tSet varResult = ReadMetadata(objTokens)\r\n\tElse\r\n\t\tSet varResult = ReadAtom(objTokens)\r\n\tEnd If\r\n\r\n\tSet ReadForm = varResult\r\nEnd Function\r\n\r\nFunction ReadMetadata(objTokens)\r\n\tDim varResult\r\n\r\n\tCall objTokens.MoveToNext()\r\n\tDim objTemp\r\n\tSet objTemp = ReadForm(objTokens)\r\n\tSet varResult = NewMalList(Array( _\r\n\t\tNewMalSym(\"with-meta\"), _\r\n\t\tReadForm(objTokens), objTemp))\r\n\r\n\tSet ReadMetadata = varResult\r\nEnd Function\r\n\r\nFunction ReadSpecial(objTokens)\r\n\tDim varResult\r\n\r\n\tDim strToken, strAlias\r\n\tstrToken = objTokens.Current()\r\n\tSelect Case strToken\r\n\t\tCase \"'\"\r\n\t\t\tstrAlias = \"quote\"\r\n\t\tCase \"`\"\r\n\t\t\tstrAlias = \"quasiquote\"\r\n\t\tCase \"~\"\r\n\t\t\tstrAlias = \"unquote\"\r\n\t\tCase \"~@\"\r\n\t\t\tstrAlias = \"splice-unquote\"\r\n\t\tCase \"@\"\r\n\t\t\tstrAlias = \"deref\"\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"ReadSpecial\", \"unknown token '\" & strAlias & \"'.\"\r\n\tEnd Select\r\n\r\n\tCall objTokens.MoveToNext()\r\n\tSet varResult = NewMalList(Array( _\r\n\t\tNewMalSym(strAlias), _ \r\n\t\tReadForm(objTokens)))\r\n\r\n\tSet ReadSpecial = varResult\r\nEnd Function\r\n\r\nFunction ReadList(objTokens)\r\n\tDim varResult\r\n\tCall objTokens.MoveToNext()\r\n\r\n\tIf objTokens.AtEnd() Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadList\", \"unbalanced parentheses.\"\r\n\tEnd If\r\n\r\n\tSet varResult = NewMalList(Array())\r\n\tWith varResult\r\n\t\tWhile objTokens.Count() > 1 And objTokens.Current() <> \")\"\r\n\t\t\t.Add ReadForm(objTokens)\r\n\t\tWend\r\n\tEnd With\r\n\r\n\tIf objTokens.MoveToNext() <> \")\" Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadList\", \"unbalanced parentheses.\"\r\n\tEnd If\r\n\r\n\tSet ReadList = varResult\r\nEnd Function\r\n\r\nFunction ReadVector(objTokens)\r\n\tDim varResult\r\n\tCall objTokens.MoveToNext()\r\n\r\n\tIf objTokens.AtEnd() Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadVector\", \"unbalanced parentheses.\"\r\n\tEnd If\r\n\r\n\tSet varResult = NewMalVec(Array())\r\n\tWith varResult\r\n\t\tWhile objTokens.Count() > 1 And objTokens.Current() <> \"]\"\r\n\t\t\t.Add ReadForm(objTokens)\r\n\t\tWend\r\n\tEnd With\r\n\r\n\tIf objTokens.MoveToNext() <> \"]\" Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadVector\", \"unbalanced parentheses.\"\r\n\tEnd If\r\n\r\n\tSet ReadVector = varResult\r\nEnd Function\r\n\r\nFunction ReadHashmap(objTokens)\r\n\tDim varResult\r\n\tCall objTokens.MoveToNext()\r\n\r\n\tIf objTokens.Count = 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadHashmap\", \"unbalanced parentheses.\"\r\n\tEnd If\r\n\t\r\n\tSet varResult = NewMalMap(Array(), Array())\r\n\tDim objKey, objValue\r\n\tWith varResult\r\n\t\tWhile objTokens.Count > 2 And objTokens.Current() <> \"}\"\r\n\t\t\tSet objKey = ReadForm(objTokens)\r\n\t\t\tSet objValue = ReadForm(objTokens)\r\n\t\t\t.Add objKey, objValue\r\n\t\tWend\r\n\tEnd With\r\n\t\r\n\tIf objTokens.MoveToNext() <> \"}\" Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ReadHashmap\", \"unbalanced parentheses.\"\r\n\tEnd If\r\n\t\r\n\tSet ReadHashmap = varResult\r\nEnd Function\r\n\r\nFunction ReadAtom(objTokens)\r\n\tDim varResult\r\n\r\n\tDim strAtom\r\n\tstrAtom = objTokens.MoveToNext()\r\n\r\n\tSelect Case strAtom\r\n\t\tCase \"true\"\r\n\t\t\tSet varResult = NewMalBool(True)\r\n\t\tCase \"false\"\r\n\t\t\tSet varResult = NewMalBool(False)\r\n\t\tCase \"nil\"\r\n\t\t\tSet varResult = NewMalNil()\r\n\t\tCase Else\r\n\t\t\tSelect Case Left(strAtom, 1)\r\n\t\t\t\tCase \":\"\r\n\t\t\t\t\tSet varResult = NewMalKwd(strAtom)\r\n\t\t\t\tCase \"\"\"\"\r\n\t\t\t\t\tSet varResult = NewMalStr(ParseString(strAtom))\r\n\t\t\t\tCase Else\r\n\t\t\t\t\tIf IsNumeric(strAtom) Then\r\n\t\t\t\t\t\tSet varResult = NewMalNum(Eval(strAtom))\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tSet varResult = NewMalSym(strAtom)\r\n\t\t\t\t\tEnd If\r\n\t\t\tEnd Select\r\n\tEnd Select\r\n\r\n\tSet ReadAtom = varResult\r\nEnd Function\r\n\r\nFunction ParseString(strRaw)\r\n\tIf Right(strRaw, 1) <> \"\"\"\" Or Len(strRaw) < 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"ParseString\", \"unterminated string, got EOF.\"\r\n\tEnd If\r\n\r\n\tDim strTemp\r\n\tstrTemp = Mid(strRaw, 2, Len(strRaw) - 2)\r\n\tDim i\r\n\ti = 1\r\n\tParseString = \"\"\r\n\tWhile i <= Len(strTemp) - 1\r\n\t\tSelect Case Mid(strTemp, i, 2)\r\n\t\t\tCase \"\\\\\"\r\n\t\t\t\tParseString = ParseString & \"\\\"\r\n\t\t\tCase \"\\n\"\r\n\t\t\t\tParseString = ParseString & vbCrLf\r\n\t\t\tCase \"\\\"\"\"\r\n\t\t\t\tParseString = ParseString & \"\"\"\"\r\n\t\t\tCase Else\r\n\t\t\t\tParseString = ParseString & Mid(strTemp, i, 1)\r\n\t\t\t\ti = i - 1\r\n\t\tEnd Select\r\n\t\ti = i + 2\r\n\tWend\r\n\r\n\tIf i <= Len(strTemp) Then\r\n\t\t' Last char is not processed.\r\n\t\tIf Right(strTemp, 1) <> \"\\\" Then\r\n\t\t\tParseString = ParseString & Right(strTemp, 1)\r\n\t\tElse\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"ParseString\", \"unterminated string, got EOF.\"\r\n\t\tEnd If\r\n\tEnd If\r\nEnd Function\r\n"
  },
  {
    "path": "impls/vbs/run",
    "content": "#!/usr/bin/env bash\nMAL_VBS_IMPL_NO_STDERR=1 MAL_VBS_IMPL_ECHO_STDIN=1 \\\nWSLENV=MAL_VBS_IMPL_NO_STDERR/w:MAL_VBS_IMPL_ECHO_STDIN/w \\\ncscript.exe -nologo \"`wslpath -w \"$(dirname $0)/${STEP:-stepA_mal}.vbs\"`\" \"${@}\""
  },
  {
    "path": "impls/vbs/step0_repl.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\n\r\nFunction Read(strCode)\r\n\tRead = strCode\r\nEnd Function\r\n\r\nFunction Evaluate(strCode)\r\n\tEvaluate = strCode\r\nEnd Function\r\n\r\nFunction Print(strCode)\r\n\tPrint = strCode\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode)))\r\nEnd Function\r\n\r\nDim strCode\r\nWhile True 'REPL\r\n\tWScript.StdOut.Write \"user> \"\r\n\tOn Error Resume Next\r\n\t\tstrCode = IO.ReadLine\r\n\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\tOn Error Goto 0\r\n\t\r\n\tIO.WriteLine REP(strCode)\r\nWend\r\n\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub"
  },
  {
    "path": "impls/vbs/step1_read_print.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nFunction Evaluate(objCode)\r\n\tSet Evaluate = objCode\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode)))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub"
  },
  {
    "path": "impls/vbs/step2_eval.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\n\r\n\tFunction EnvGet(objDict, objSymbol)\r\n\t\tIf objDict.Exists(objSymbol) Then\r\n\t\t\tSet EnvGet = objDict.Item(objSymbol)\r\n\t\tElse\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"Enviroment\", \"Symbol '\" + objSymbol + \"' not found.\"\r\n\t\tEnd If\r\n\tEnd Function\r\n\r\nDim objEnv\r\nSet objEnv = CreateObject(\"Scripting.Dictionary\")\r\n\r\nFunction MAdd(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MAdd = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value + objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"+\", NewVbsProc(\"MAdd\", False)\r\n\r\nFunction MSub(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MSub = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value - objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"-\", NewVbsProc(\"MSub\", False)\r\n\r\nFunction MMul(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MMul = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value * objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"*\", NewVbsProc(\"MMul\", False)\r\n\r\nFunction MDiv(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MDiv = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value \\ objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"/\", NewVbsProc(\"MDiv\", False)\r\n\r\nSub CheckArgNum(objArgs, lngArgNum)\r\n\tIf objArgs.Count - 1 <> lngArgNum Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckArgNum\", \"Wrong number of arguments.\"\r\n\tEnd IF\r\nEnd Sub\r\n\r\nSub CheckType(objMal, varType)\r\n\tIf objMal.Type <> varType Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckType\", \"Wrong argument type.\"\r\n\tEnd IF\r\nEnd Sub\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nFunction Evaluate(objCode, objEnv)\r\n\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\tSet Evaluate = Nothing\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\t' DebugEval objCode, objEnv\r\n\r\n\tDim varRet, objFirst\r\n\tIf objCode.Type = TYPES.LIST Then\r\n\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\tSet Evaluate = objCode\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\tElse\r\n\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\tEnd If\r\n\r\n\tSet Evaluate = varRet\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = EnvGet(objEnv, objCode.Value)\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objEnv))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step3_env.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\n\r\nDim objEnv\r\nSet objEnv = NewEnv(Nothing)\r\n\r\nFunction MAdd(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MAdd = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value + objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"+\", NewVbsProc(\"MAdd\", False)\r\n\r\nFunction MSub(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MSub = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value - objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"-\", NewVbsProc(\"MSub\", False)\r\n\r\nFunction MMul(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MMul = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value * objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"*\", NewVbsProc(\"MMul\", False)\r\n\r\nFunction MDiv(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.NUMBER\r\n\tCheckType objArgs.Item(2), TYPES.NUMBER\r\n\tSet MDiv = NewMalNum( _\r\n\t\tobjArgs.Item(1).Value \\ objArgs.Item(2).Value)\r\nEnd Function\r\nobjEnv.Add \"/\", NewVbsProc(\"MDiv\", False)\r\n\r\nSub CheckArgNum(objArgs, lngArgNum)\r\n\tIf objArgs.Count - 1 <> lngArgNum Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckArgNum\", \"Wrong number of arguments.\"\r\n\tEnd IF\r\nEnd Sub\r\n\r\nSub CheckType(objMal, varType)\r\n\tIf objMal.Type <> varType Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"CheckType\", \"Wrong argument type.\"\r\n\tEnd IF\r\nEnd Sub\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjEnv.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tIf objBinds.Type <> TYPES.LIST And _\r\n\t\tobjBinds.Type <> TYPES.VECTOR Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument type.\"\r\n\tEnd If\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = Evaluate(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjEnv.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine()\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(objCode, objEnv)\r\n\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\tSet Evaluate = Nothing\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tDebugEval objCode, objEnv\r\n\r\n\tDim varRet, objFirst\r\n\tIf objCode.Type = TYPES.LIST Then\r\n\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\tSet Evaluate = objCode\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\tElse\r\n\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\tEnd If\r\n\r\n\tSet Evaluate = varRet\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objEnv))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step4_if_fn_do.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\t' A fake implement, for compatibility.\r\n\tDim varRes\r\n\tSet varRes = Evaluate(objMal, objEnv)\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = Evaluate(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 1\r\n\t\tSet varRet = Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = Evaluate(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nCall InitBuiltIn()\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(objCode, objEnv)\r\n\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\tSet Evaluate = Nothing\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tDebugEval objCode, objEnv\r\n\r\n\tDim varRet, objFirst\r\n\tIf objCode.Type = TYPES.LIST Then\r\n\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\tSet Evaluate = objCode\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\tElse\r\n\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\tEnd If\r\n\r\n\tSet Evaluate = varRet\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step5_tco.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nClass TailCall\r\n\tPublic objMalType\r\n\tPublic objEnv\r\nEnd Class\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\tDim varRes\r\n\tSet varRes = New TailCall\r\n\tSet varRes.objMalType = objMal\r\n\tSet varRes.objEnv = objEnv\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = EvalLater(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 2\r\n\t\tCall Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet varRet = EvalLater( _\r\n\t\tobjArgs.Item(objArgs.Count - 1), _\r\n\t\tobjEnv)\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = EvalLater(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = EvalLater(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nCall InitBuiltIn()\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(ByVal objCode, ByVal objEnv)\r\n\tWhile True\r\n\t\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\t\tSet Evaluate = Nothing\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\r\n\t\tDebugEval objCode, objEnv\r\n\r\n\t\tDim varRet, objFirst\r\n\t\tIf objCode.Type = TYPES.LIST Then\r\n\t\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\t\tSet Evaluate = objCode\r\n\t\t\t\tExit Function\r\n\t\t\tEnd If\r\n\r\n\t\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\t\tEnd If\r\n\t\t\r\n\t\tIf TypeName(varRet) = \"TailCall\" Then\r\n\t\t\t' NOTICE: If not specify 'ByVal', \r\n\t\t\t' Change of arguments will influence\r\n\t\t\t' the caller's variable!\r\n\t\t\tSet objCode = varRet.objMalType\r\n\t\t\tSet objEnv = varRet.objEnv\r\n\t\tElse\r\n\t\t\tSet Evaluate = varRet\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\tWend\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step6_file.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nClass TailCall\r\n\tPublic objMalType\r\n\tPublic objEnv\r\nEnd Class\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\tDim varRes\r\n\tSet varRes = New TailCall\r\n\tSet varRes.objMalType = objMal\r\n\tSet varRes.objEnv = objEnv\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = EvalLater(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 2\r\n\t\tCall Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet varRet = EvalLater( _\r\n\t\tobjArgs.Item(objArgs.Count - 1), _\r\n\t\tobjEnv)\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = EvalLater(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = EvalLater(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nFunction MEval(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = Evaluate(objArgs.Item(1), objEnv)\r\n\tSet varRes = EvalLater(varRes, objNS)\r\n\tSet MEval = varRes\r\nEnd Function\r\nobjNS.Add \"eval\", NewVbsProc(\"MEval\", True)\r\n\r\nCall InitBuiltIn()\r\n\r\nCall InitArgs()\r\nSub InitArgs()\r\n\tDim objArgs\r\n\tSet objArgs = NewMalList(Array())\r\n\r\n\tDim i\r\n\tFor i = 1 To WScript.Arguments.Count - 1\r\n\t\tobjArgs.Add NewMalStr(WScript.Arguments.Item(i))\r\n\tNext\r\n\t\r\n\tobjNS.Add \"*ARGV*\", objArgs\r\n\t\r\n\tIf WScript.Arguments.Count > 0 Then\r\n\t\tREP \"(load-file \"\"\" + WScript.Arguments.Item(0) + \"\"\")\"\r\n\t\tWScript.Quit 0\r\n\tEnd If\r\nEnd Sub\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(ByVal objCode, ByVal objEnv)\r\n\tWhile True\r\n\t\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\t\tSet Evaluate = Nothing\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\r\n\t\tDebugEval objCode, objEnv\r\n\r\n\t\tDim varRet, objFirst\r\n\t\tIf objCode.Type = TYPES.LIST Then\r\n\t\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\t\tSet Evaluate = objCode\r\n\t\t\t\tExit Function\r\n\t\t\tEnd If\r\n\r\n\t\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\t\tEnd If\r\n\t\t\r\n\t\tIf TypeName(varRet) = \"TailCall\" Then\r\n\t\t\t' NOTICE: If not specify 'ByVal', \r\n\t\t\t' Change of arguments will influence\r\n\t\t\t' the caller's variable!\r\n\t\t\tSet objCode = varRet.objMalType\r\n\t\t\tSet objEnv = varRet.objEnv\r\n\t\tElse\r\n\t\t\tSet Evaluate = varRet\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\tWend\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step7_quote.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nClass TailCall\r\n\tPublic objMalType\r\n\tPublic objEnv\r\nEnd Class\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\tDim varRes\r\n\tSet varRes = New TailCall\r\n\tSet varRes.objMalType = objMal\r\n\tSet varRes.objEnv = objEnv\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = EvalLater(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 2\r\n\t\tCall Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet varRet = EvalLater( _\r\n\t\tobjArgs.Item(objArgs.Count - 1), _\r\n\t\tobjEnv)\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = EvalLater(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = EvalLater(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nFunction MEval(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = Evaluate(objArgs.Item(1), objEnv)\r\n\tSet varRes = EvalLater(varRes, objNS)\r\n\tSet MEval = varRes\r\nEnd Function\r\nobjNS.Add \"eval\", NewVbsProc(\"MEval\", True)\r\n\r\nFunction MQuote(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tSet MQuote = objArgs.Item(1)\r\nEnd Function\r\nobjNS.Add \"quote\", NewVbsProc(\"MQuote\", True)\r\n\r\nFunction MQuasiQuote(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\t\r\n\tSet varRes = EvalLater( _\r\n\t\tMQuasiQuoteExpand(objArgs, objEnv), objEnv)\r\n\tSet MQuasiQuote = varRes\r\nEnd Function\r\nobjNS.Add \"quasiquote\", NewVbsProc(\"MQuasiQuote\", True)\r\n\r\nFunction MQuasiQuoteExpand(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = ExpandHelper(objArgs.Item(1))\r\n\tIf varRes.Splice Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MQuasiQuoteExpand\", \"Wrong return value type.\"\r\n\tEnd If\r\n\tSet varRes = varRes.Value\r\n\r\n\tSet MQuasiQuoteExpand = varRes\r\nEnd Function\r\n\r\nClass ExpandType\r\n\tPublic Splice\r\n\tPublic Value\r\nEnd Class\r\n\r\nFunction NewExpandType(objValue, boolSplice)\r\n\tDim varRes\r\n\tSet varRes = New ExpandType\r\n\tSet varRes.Value = objValue\r\n\tvarRes.Splice = boolSplice\r\n\tSet NewExpandType = varRes\r\nEnd Function\r\n\r\nFunction ExpandHelper(objArg)\r\n\tDim varRes, boolSplice\r\n\tDim varBuilder, varEType, i\r\n\tboolSplice = False\r\n\tSelect Case objArg.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tDim boolNormal\r\n\t\t\tboolNormal = False\r\n\r\n\t\t\t' Check for unquotes.\r\n\t\t\tSelect Case objArg.Count\r\n\t\t\t\tCase 2\r\n\t\t\t\t\t' Maybe have a bug here\r\n\t\t\t\t\t' like (unquote a b c) should be throw a error\r\n\t\t\t\t\tIf objArg.Item(0).Type = TYPES.SYMBOL Then\r\n\t\t\t\t\t\tSelect Case objArg.Item(0).Value\r\n\t\t\t\t\t\t\tCase \"unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\tCase \"splice-unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\t\tboolSplice = True\r\n\t\t\t\t\t\t\tCase Else\r\n\t\t\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\t\tEnd Select\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\tEnd If\r\n\t\t\t\tCase Else\r\n\t\t\t\t\tboolNormal = True\r\n\t\t\tEnd Select\r\n\t\t\t\r\n\t\t\tIf boolNormal Then\r\n\t\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varRes\r\n\r\n\t\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\t\tEnd If\r\n\t\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\t\tNext\r\n\t\t\tEnd If\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"vec\"), NewMalList(Array())))\r\n\t\t\t\r\n\t\t\tSet varBuilder = varRes.Item(1)\r\n\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\tElse\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\tEnd If\r\n\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' e.g. {\"key\" ~value}\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase Else\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' All unspecified type will return itself.\r\n\t\t\tSet varRes = objArg\r\n\tEnd Select\r\n\r\n\tSet ExpandHelper = NewExpandType(varRes, boolSplice)\r\nEnd Function\r\n\r\nCall InitBuiltIn()\r\n\r\nCall InitArgs()\r\nSub InitArgs()\r\n\tDim objArgs\r\n\tSet objArgs = NewMalList(Array())\r\n\r\n\tDim i\r\n\tFor i = 1 To WScript.Arguments.Count - 1\r\n\t\tobjArgs.Add NewMalStr(WScript.Arguments.Item(i))\r\n\tNext\r\n\t\r\n\tobjNS.Add \"*ARGV*\", objArgs\r\n\t\r\n\tIf WScript.Arguments.Count > 0 Then\r\n\t\tREP \"(load-file \"\"\" + WScript.Arguments.Item(0) + \"\"\")\"\r\n\t\tWScript.Quit 0\r\n\tEnd If\r\nEnd Sub\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(ByVal objCode, ByVal objEnv)\r\n\tWhile True\r\n\t\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\t\tSet Evaluate = Nothing\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\r\n\t\tDebugEval objCode, objEnv\r\n\r\n\t\tDim varRet, objFirst\r\n\t\tIf objCode.Type = TYPES.LIST Then\r\n\t\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\t\tSet Evaluate = objCode\r\n\t\t\t\tExit Function\r\n\t\t\tEnd If\r\n\r\n\t\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\t\tEnd If\r\n\t\t\r\n\t\tIf TypeName(varRet) = \"TailCall\" Then\r\n\t\t\t' NOTICE: If not specify 'ByVal', \r\n\t\t\t' Change of arguments will influence\r\n\t\t\t' the caller's variable!\r\n\t\t\tSet objCode = varRet.objMalType\r\n\t\t\tSet objEnv = varRet.objEnv\r\n\t\tElse\r\n\t\t\tSet Evaluate = varRet\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\tWend\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step8_macros.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nClass TailCall\r\n\tPublic objMalType\r\n\tPublic objEnv\r\nEnd Class\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\tDim varRes\r\n\tSet varRes = New TailCall\r\n\tSet varRes.objMalType = objMal\r\n\tSet varRes.objEnv = objEnv\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = EvalLater(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 2\r\n\t\tCall Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet varRet = EvalLater( _\r\n\t\tobjArgs.Item(objArgs.Count - 1), _\r\n\t\tobjEnv)\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = EvalLater(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = EvalLater(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nFunction MEval(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = Evaluate(objArgs.Item(1), objEnv)\r\n\tSet varRes = EvalLater(varRes, objNS)\r\n\tSet MEval = varRes\r\nEnd Function\r\nobjNS.Add \"eval\", NewVbsProc(\"MEval\", True)\r\n\r\nFunction MQuote(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tSet MQuote = objArgs.Item(1)\r\nEnd Function\r\nobjNS.Add \"quote\", NewVbsProc(\"MQuote\", True)\r\n\r\nFunction MQuasiQuote(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\t\r\n\tSet varRes = EvalLater( _\r\n\t\tMQuasiQuoteExpand(objArgs, objEnv), objEnv)\r\n\tSet MQuasiQuote = varRes\r\nEnd Function\r\nobjNS.Add \"quasiquote\", NewVbsProc(\"MQuasiQuote\", True)\r\n\r\nFunction MQuasiQuoteExpand(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = ExpandHelper(objArgs.Item(1))\r\n\tIf varRes.Splice Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MQuasiQuoteExpand\", \"Wrong return value type.\"\r\n\tEnd If\r\n\tSet varRes = varRes.Value\r\n\r\n\tSet MQuasiQuoteExpand = varRes\r\nEnd Function\r\n\r\nClass ExpandType\r\n\tPublic Splice\r\n\tPublic Value\r\nEnd Class\r\n\r\nFunction NewExpandType(objValue, boolSplice)\r\n\tDim varRes\r\n\tSet varRes = New ExpandType\r\n\tSet varRes.Value = objValue\r\n\tvarRes.Splice = boolSplice\r\n\tSet NewExpandType = varRes\r\nEnd Function\r\n\r\nFunction ExpandHelper(objArg)\r\n\tDim varRes, boolSplice\r\n\tDim varBuilder, varEType, i\r\n\tboolSplice = False\r\n\tSelect Case objArg.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tDim boolNormal\r\n\t\t\tboolNormal = False\r\n\r\n\t\t\t' Check for unquotes.\r\n\t\t\tSelect Case objArg.Count\r\n\t\t\t\tCase 2\r\n\t\t\t\t\t' Maybe have a bug here\r\n\t\t\t\t\t' like (unquote a b c) should be throw a error\r\n\t\t\t\t\tIf objArg.Item(0).Type = TYPES.SYMBOL Then\r\n\t\t\t\t\t\tSelect Case objArg.Item(0).Value\r\n\t\t\t\t\t\t\tCase \"unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\tCase \"splice-unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\t\tboolSplice = True\r\n\t\t\t\t\t\t\tCase Else\r\n\t\t\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\t\tEnd Select\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\tEnd If\r\n\t\t\t\tCase Else\r\n\t\t\t\t\tboolNormal = True\r\n\t\t\tEnd Select\r\n\t\t\t\r\n\t\t\tIf boolNormal Then\r\n\t\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varRes\r\n\r\n\t\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\t\tEnd If\r\n\t\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\t\tNext\r\n\t\t\tEnd If\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"vec\"), NewMalList(Array())))\r\n\t\t\t\r\n\t\t\tSet varBuilder = varRes.Item(1)\r\n\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\tElse\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\tEnd If\r\n\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' e.g. {\"key\" ~value}\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase Else\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' All unspecified type will return itself.\r\n\t\t\tSet varRes = objArg\r\n\tEnd Select\r\n\r\n\tSet ExpandHelper = NewExpandType(varRes, boolSplice)\r\nEnd Function\r\n\r\nFunction MDefMacro(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv).Copy()\r\n\tCheckType varRet, TYPES.PROCEDURE\r\n\tvarRet.IsMacro = True\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDefMacro = varRet\r\nEnd Function\r\nobjNS.Add \"defmacro!\", NewVbsProc(\"MDefMacro\", True)\r\n\r\nCall InitBuiltIn()\r\nCall InitMacro()\r\n\r\nCall InitArgs()\r\nSub InitArgs()\r\n\tDim objArgs\r\n\tSet objArgs = NewMalList(Array())\r\n\r\n\tDim i\r\n\tFor i = 1 To WScript.Arguments.Count - 1\r\n\t\tobjArgs.Add NewMalStr(WScript.Arguments.Item(i))\r\n\tNext\r\n\t\r\n\tobjNS.Add \"*ARGV*\", objArgs\r\n\t\r\n\tIf WScript.Arguments.Count > 0 Then\r\n\t\tREP \"(load-file \"\"\" + WScript.Arguments.Item(0) + \"\"\")\"\r\n\t\tWScript.Quit 0\r\n\tEnd If\r\nEnd Sub\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(ByVal objCode, ByVal objEnv)\r\n\tWhile True\r\n\t\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\t\tSet Evaluate = Nothing\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\r\n\t\tDebugEval objCode, objEnv\r\n\r\n\t\tDim varRet, objFirst\r\n\t\tIf objCode.Type = TYPES.LIST Then\r\n\t\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\t\tSet Evaluate = objCode\r\n\t\t\t\tExit Function\r\n\t\t\tEnd If\r\n\r\n\t\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\t\tIf objFirst.IsMacro Then\r\n\t\t\t\tSet varRet = EvalLater(objFirst.MacroApply(objCode, objEnv), objEnv)\r\n\t\t\tElse\r\n\t\t\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\t\t\tEnd If\r\n\t\tElse\r\n\t\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\t\tEnd If\r\n\t\t\r\n\t\tIf TypeName(varRet) = \"TailCall\" Then\r\n\t\t\t' NOTICE: If not specify 'ByVal', \r\n\t\t\t' Change of arguments will influence\r\n\t\t\t' the caller's variable!\r\n\t\t\tSet objCode = varRet.objMalType\r\n\t\t\tSet objEnv = varRet.objEnv\r\n\t\tElse\r\n\t\t\tSet Evaluate = varRet\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\tWend\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/step9_try.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nClass TailCall\r\n\tPublic objMalType\r\n\tPublic objEnv\r\nEnd Class\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\tDim varRes\r\n\tSet varRes = New TailCall\r\n\tSet varRes.objMalType = objMal\r\n\tSet varRes.objEnv = objEnv\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = EvalLater(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 2\r\n\t\tCall Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet varRet = EvalLater( _\r\n\t\tobjArgs.Item(objArgs.Count - 1), _\r\n\t\tobjEnv)\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = EvalLater(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = EvalLater(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nFunction MEval(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = Evaluate(objArgs.Item(1), objEnv)\r\n\tSet varRes = EvalLater(varRes, objNS)\r\n\tSet MEval = varRes\r\nEnd Function\r\nobjNS.Add \"eval\", NewVbsProc(\"MEval\", True)\r\n\r\nFunction MQuote(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tSet MQuote = objArgs.Item(1)\r\nEnd Function\r\nobjNS.Add \"quote\", NewVbsProc(\"MQuote\", True)\r\n\r\nFunction MQuasiQuote(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\t\r\n\tSet varRes = EvalLater( _\r\n\t\tMQuasiQuoteExpand(objArgs, objEnv), objEnv)\r\n\tSet MQuasiQuote = varRes\r\nEnd Function\r\nobjNS.Add \"quasiquote\", NewVbsProc(\"MQuasiQuote\", True)\r\n\r\nFunction MQuasiQuoteExpand(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = ExpandHelper(objArgs.Item(1))\r\n\tIf varRes.Splice Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MQuasiQuoteExpand\", \"Wrong return value type.\"\r\n\tEnd If\r\n\tSet varRes = varRes.Value\r\n\r\n\tSet MQuasiQuoteExpand = varRes\r\nEnd Function\r\n\r\nClass ExpandType\r\n\tPublic Splice\r\n\tPublic Value\r\nEnd Class\r\n\r\nFunction NewExpandType(objValue, boolSplice)\r\n\tDim varRes\r\n\tSet varRes = New ExpandType\r\n\tSet varRes.Value = objValue\r\n\tvarRes.Splice = boolSplice\r\n\tSet NewExpandType = varRes\r\nEnd Function\r\n\r\nFunction ExpandHelper(objArg)\r\n\tDim varRes, boolSplice\r\n\tDim varBuilder, varEType, i\r\n\tboolSplice = False\r\n\tSelect Case objArg.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tDim boolNormal\r\n\t\t\tboolNormal = False\r\n\r\n\t\t\t' Check for unquotes.\r\n\t\t\tSelect Case objArg.Count\r\n\t\t\t\tCase 2\r\n\t\t\t\t\t' Maybe have a bug here\r\n\t\t\t\t\t' like (unquote a b c) should be throw a error\r\n\t\t\t\t\tIf objArg.Item(0).Type = TYPES.SYMBOL Then\r\n\t\t\t\t\t\tSelect Case objArg.Item(0).Value\r\n\t\t\t\t\t\t\tCase \"unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\tCase \"splice-unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\t\tboolSplice = True\r\n\t\t\t\t\t\t\tCase Else\r\n\t\t\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\t\tEnd Select\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\tEnd If\r\n\t\t\t\tCase Else\r\n\t\t\t\t\tboolNormal = True\r\n\t\t\tEnd Select\r\n\t\t\t\r\n\t\t\tIf boolNormal Then\r\n\t\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varRes\r\n\r\n\t\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\t\tEnd If\r\n\t\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\t\tNext\r\n\t\t\tEnd If\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"vec\"), NewMalList(Array())))\r\n\t\t\t\r\n\t\t\tSet varBuilder = varRes.Item(1)\r\n\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\tElse\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\tEnd If\r\n\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' e.g. {\"key\" ~value}\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase Else\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' All unspecified type will return itself.\r\n\t\t\tSet varRes = objArg\r\n\tEnd Select\r\n\r\n\tSet ExpandHelper = NewExpandType(varRes, boolSplice)\r\nEnd Function\r\n\r\nFunction MDefMacro(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv).Copy()\r\n\tCheckType varRet, TYPES.PROCEDURE\r\n\tvarRet.IsMacro = True\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDefMacro = varRet\r\nEnd Function\r\nobjNS.Add \"defmacro!\", NewVbsProc(\"MDefMacro\", True)\r\n\r\nFunction MTry(objArgs, objEnv)\r\n\tDim varRes\r\n\t\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MTry\", \"Need more arguments.\"\r\n\tEnd If\r\n\r\n\tIf objArgs.Count - 1 = 1 Then\r\n\t\tSet varRes = EvalLater(objArgs.Item(1), objEnv)\r\n\t\tSet MTry = varRes\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(2), TYPES.LIST\r\n\r\n\tDim objTry, objCatch\r\n\tSet objTry = objArgs.Item(1)\r\n\tSet objCatch = objArgs.Item(2)\r\n\t\r\n\tCheckArgNum objCatch, 2\r\n\tCheckType objCatch.Item(0), TYPES.SYMBOL\r\n\tCheckType objCatch.Item(1), TYPES.SYMBOL\r\n\tIf objCatch.Item(0).Value <> \"catch*\" Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MTry\", \"Unexpect argument(s).\"\r\n\tEnd If\r\n\t\r\n\tOn Error Resume Next\r\n\tSet varRes = Evaluate(objTry, objEnv)\r\n\tIf Err.Number <> 0 Then\r\n\t\tDim objException\r\n\r\n\t\tIf Err.Source <> \"MThrow\" Then\r\n\t\t\tSet objException = NewMalStr(Err.Description)\r\n\t\tElse\r\n\t\t\tSet objException = objExceptions.Item(Err.Description)\r\n\t\t\tobjExceptions.Remove Err.Description\r\n\t\tEnd If\r\n\t\t\r\n\t\tCall Err.Clear()\r\n\t\tOn Error Goto 0\r\n\r\n\t\t' The code below may cause error too.\r\n\t\t' So we should clear err info & throw out any errors.\r\n\t\t' Use 'quote' to avoid eval objExp again.\r\n\t\tSet varRes = Evaluate(NewMalList(Array( _\r\n\t\t\tNewMalSym(\"let*\"), NewMalList(Array( _\r\n\t\t\t\tobjCatch.Item(1), NewMalList(Array( _\r\n\t\t\t\t\t\tNewMalSym(\"quote\"), objException)))), _\r\n\t\t\tobjCatch.Item(2))), objEnv)\r\n\tElse\r\n\t\tOn Error Goto 0\r\n\tEnd If\r\n\r\n\tSet MTry = varRes\r\nEnd Function\r\nobjNS.Add \"try*\", NewVbsProc(\"MTry\", True)\r\n\r\nCall InitBuiltIn()\r\nCall InitMacro()\r\n\r\nCall InitArgs()\r\nSub InitArgs()\r\n\tDim objArgs\r\n\tSet objArgs = NewMalList(Array())\r\n\r\n\tDim i\r\n\tFor i = 1 To WScript.Arguments.Count - 1\r\n\t\tobjArgs.Add NewMalStr(WScript.Arguments.Item(i))\r\n\tNext\r\n\t\r\n\tobjNS.Add \"*ARGV*\", objArgs\r\n\t\r\n\tIf WScript.Arguments.Count > 0 Then\r\n\t\tREP \"(load-file \"\"\" + WScript.Arguments.Item(0) + \"\"\")\"\r\n\t\tWScript.Quit 0\r\n\tEnd If\r\nEnd Sub\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIf Err.Source = \"MThrow\" Then\r\n\t\t\t\t\tIO.WriteErrLine \"Exception: \" + _\r\n\t\t\t\t\t\tPrintMalType(objExceptions.Item(Err.Description), True)\r\n\t\t\t\t\tobjExceptions.Remove Err.Description\r\n\t\t\t\tElse\r\n\t\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\t\tEnd If\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(ByVal objCode, ByVal objEnv)\r\n\tWhile True\r\n\t\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\t\tSet Evaluate = Nothing\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\r\n\t\tDebugEval objCode, objEnv\r\n\r\n\t\tDim varRet, objFirst\r\n\t\tIf objCode.Type = TYPES.LIST Then\r\n\t\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\t\tSet Evaluate = objCode\r\n\t\t\t\tExit Function\r\n\t\t\tEnd If\r\n\r\n\t\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\t\tIf objFirst.IsMacro Then\r\n\t\t\t\tSet varRet = EvalLater(objFirst.MacroApply(objCode, objEnv), objEnv)\r\n\t\t\tElse\r\n\t\t\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\t\t\tEnd If\r\n\t\tElse\r\n\t\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\t\tEnd If\r\n\t\t\r\n\t\tIf TypeName(varRet) = \"TailCall\" Then\r\n\t\t\t' NOTICE: If not specify 'ByVal', \r\n\t\t\t' Change of arguments will influence\r\n\t\t\t' the caller's variable!\r\n\t\t\tSet objCode = varRet.objMalType\r\n\t\t\tSet objEnv = varRet.objEnv\r\n\t\tElse\r\n\t\t\tSet Evaluate = varRet\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\tWend\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/stepA_mal.vbs",
    "content": "Option Explicit\r\n\r\nInclude \"IO.vbs\"\r\nInclude \"Types.vbs\"\r\nInclude \"Reader.vbs\"\r\nInclude \"Printer.vbs\"\r\nInclude \"Env.vbs\"\r\nInclude \"Core.vbs\"\r\n\r\nClass TailCall\r\n\tPublic objMalType\r\n\tPublic objEnv\r\nEnd Class\r\n\r\nFunction EvalLater(objMal, objEnv)\r\n\tDim varRes\r\n\tSet varRes = New TailCall\r\n\tSet varRes.objMalType = objMal\r\n\tSet varRes.objEnv = objEnv\r\n\tSet EvalLater = varRes\r\nEnd Function\r\n\r\nFunction MDef(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv)\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDef = varRet\r\nEnd Function\r\nobjNS.Add \"def!\", NewVbsProc(\"MDef\", True)\r\n\r\nFunction MLet(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objBinds\r\n\tSet objBinds = objArgs.Item(1)\r\n\tCheckListOrVec objBinds\r\n\t\r\n\tIf objBinds.Count Mod 2 <> 0 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MLet\", \"Wrong argument count.\"\r\n\tEnd If\r\n\r\n\tDim objNewEnv\r\n\tSet objNewEnv = NewEnv(objEnv)\r\n\tDim i, objSym\r\n\tFor i = 0 To objBinds.Count - 1 Step 2\r\n\t\tSet objSym = objBinds.Item(i)\r\n\t\tCheckType objSym, TYPES.SYMBOL\r\n\t\tobjNewEnv.Add objSym.Value, Evaluate(objBinds.Item(i + 1), objNewEnv)\r\n\tNext\r\n\r\n\tSet varRet = EvalLater(objArgs.Item(2), objNewEnv)\r\n\tSet MLet = varRet\r\nEnd Function\r\nobjNS.Add \"let*\", NewVbsProc(\"MLet\", True)\r\n\r\nFunction MDo(objArgs, objEnv)\r\n\tDim varRet, i\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MDo\", \"Need more arguments.\"\r\n\tEnd If\r\n\tFor i = 1 To objArgs.Count - 2\r\n\t\tCall Evaluate(objArgs.Item(i), objEnv)\r\n\tNext\r\n\tSet varRet = EvalLater( _\r\n\t\tobjArgs.Item(objArgs.Count - 1), _\r\n\t\tobjEnv)\r\n\tSet MDo = varRet\r\nEnd Function\r\nobjNS.Add \"do\", NewVbsProc(\"MDo\", True)\r\n\r\nFunction MIf(objArgs, objEnv)\r\n\tDim varRet\r\n\tIf objArgs.Count - 1 <> 3 And _\r\n\t\tobjArgs.Count - 1 <> 2 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MIf\", \"Wrong number of arguments.\"\r\n\tEnd If\r\n\r\n\tDim objCond\r\n\tSet objCond = Evaluate(objArgs.Item(1), objEnv)\r\n\tDim boolCond\r\n\tIf objCond.Type = TYPES.BOOLEAN Then\r\n\t\tboolCond = objCond.Value\r\n\tElse\r\n\t\tboolCond = True\r\n\tEnd If\r\n\tboolCond = (boolCond And objCond.Type <> TYPES.NIL)\r\n\tIf boolCond Then\r\n\t\tSet varRet = EvalLater(objArgs.Item(2), objEnv)\r\n\tElse\r\n\t\tIf objArgs.Count - 1 = 3 Then\r\n\t\t\tSet varRet = EvalLater(objArgs.Item(3), objEnv)\r\n\t\tElse\r\n\t\t\tSet varRet = NewMalNil()\r\n\t\tEnd If\r\n\tEnd If\r\n\tSet MIf = varRet\r\nEnd Function\r\nobjNS.Add \"if\", NewVbsProc(\"MIf\", True)\r\n\r\nFunction MFn(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\r\n\tDim objParams, objCode\r\n\tSet objParams = objArgs.Item(1)\r\n\tCheckListOrVec objParams\r\n\tSet objCode = objArgs.Item(2)\r\n\t\r\n\tDim i\r\n\tFor i = 0 To objParams.Count - 1\r\n\t\tCheckType objParams.Item(i), TYPES.SYMBOL\r\n\tNext\r\n\tSet varRet = NewMalProc(objParams, objCode, objEnv)\r\n\tSet MFn = varRet\r\nEnd Function\r\nobjNS.Add \"fn*\", NewVbsProc(\"MFn\", True)\r\n\r\nFunction MEval(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = Evaluate(objArgs.Item(1), objEnv)\r\n\tSet varRes = EvalLater(varRes, objNS)\r\n\tSet MEval = varRes\r\nEnd Function\r\nobjNS.Add \"eval\", NewVbsProc(\"MEval\", True)\r\n\r\nFunction MQuote(objArgs, objEnv)\r\n\tCheckArgNum objArgs, 1\r\n\tSet MQuote = objArgs.Item(1)\r\nEnd Function\r\nobjNS.Add \"quote\", NewVbsProc(\"MQuote\", True)\r\n\r\nFunction MQuasiQuote(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\t\r\n\tSet varRes = EvalLater( _\r\n\t\tMQuasiQuoteExpand(objArgs, objEnv), objEnv)\r\n\tSet MQuasiQuote = varRes\r\nEnd Function\r\nobjNS.Add \"quasiquote\", NewVbsProc(\"MQuasiQuote\", True)\r\n\r\nFunction MQuasiQuoteExpand(objArgs, objEnv)\r\n\tDim varRes\r\n\tCheckArgNum objArgs, 1\r\n\r\n\tSet varRes = ExpandHelper(objArgs.Item(1))\r\n\tIf varRes.Splice Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MQuasiQuoteExpand\", \"Wrong return value type.\"\r\n\tEnd If\r\n\tSet varRes = varRes.Value\r\n\r\n\tSet MQuasiQuoteExpand = varRes\r\nEnd Function\r\n\r\nClass ExpandType\r\n\tPublic Splice\r\n\tPublic Value\r\nEnd Class\r\n\r\nFunction NewExpandType(objValue, boolSplice)\r\n\tDim varRes\r\n\tSet varRes = New ExpandType\r\n\tSet varRes.Value = objValue\r\n\tvarRes.Splice = boolSplice\r\n\tSet NewExpandType = varRes\r\nEnd Function\r\n\r\nFunction ExpandHelper(objArg)\r\n\tDim varRes, boolSplice\r\n\tDim varBuilder, varEType, i\r\n\tboolSplice = False\r\n\tSelect Case objArg.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tDim boolNormal\r\n\t\t\tboolNormal = False\r\n\r\n\t\t\t' Check for unquotes.\r\n\t\t\tSelect Case objArg.Count\r\n\t\t\t\tCase 2\r\n\t\t\t\t\t' Maybe have a bug here\r\n\t\t\t\t\t' like (unquote a b c) should be throw a error\r\n\t\t\t\t\tIf objArg.Item(0).Type = TYPES.SYMBOL Then\r\n\t\t\t\t\t\tSelect Case objArg.Item(0).Value\r\n\t\t\t\t\t\t\tCase \"unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\tCase \"splice-unquote\"\r\n\t\t\t\t\t\t\t\tSet varRes = objArg.Item(1)\r\n\t\t\t\t\t\t\t\tboolSplice = True\r\n\t\t\t\t\t\t\tCase Else\r\n\t\t\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\t\tEnd Select\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tboolNormal = True\r\n\t\t\t\t\tEnd If\r\n\t\t\t\tCase Else\r\n\t\t\t\t\tboolNormal = True\r\n\t\t\tEnd Select\r\n\t\t\t\r\n\t\t\tIf boolNormal Then\r\n\t\t\t\tSet varRes = NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varRes\r\n\r\n\t\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\t\tElse\r\n\t\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\t\tEnd If\r\n\t\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\t\tNext\r\n\t\t\tEnd If\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"vec\"), NewMalList(Array())))\r\n\t\t\t\r\n\t\t\tSet varBuilder = varRes.Item(1)\r\n\t\t\tFor i = 0 To objArg.Count - 1\r\n\t\t\t\tSet varEType = ExpandHelper(objArg.Item(i))\r\n\t\t\t\tIf varEType.Splice Then\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"concat\")\r\n\t\t\t\tElse\r\n\t\t\t\t\tvarBuilder.Add NewMalSym(\"cons\")\r\n\t\t\t\tEnd If\r\n\t\t\t\tvarBuilder.Add varEType.Value\r\n\t\t\t\tvarBuilder.Add NewMalList(Array())\r\n\t\t\t\tSet varBuilder = varBuilder.Item(2)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' e.g. {\"key\" ~value}\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRes = NewMalList(Array( _\r\n\t\t\t\tNewMalSym(\"quote\"), objArg))\r\n\t\tCase Else\r\n\t\t\t' Maybe have a bug here.\r\n\t\t\t' All unspecified type will return itself.\r\n\t\t\tSet varRes = objArg\r\n\tEnd Select\r\n\r\n\tSet ExpandHelper = NewExpandType(varRes, boolSplice)\r\nEnd Function\r\n\r\nFunction MDefMacro(objArgs, objEnv)\r\n\tDim varRet\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(1), TYPES.SYMBOL\r\n\tSet varRet = Evaluate(objArgs.Item(2), objEnv).Copy()\r\n\tCheckType varRet, TYPES.PROCEDURE\r\n\tvarRet.IsMacro = True\r\n\tobjEnv.Add objArgs.Item(1).Value, varRet\r\n\tSet MDefMacro = varRet\r\nEnd Function\r\nobjNS.Add \"defmacro!\", NewVbsProc(\"MDefMacro\", True)\r\n\r\nFunction MTry(objArgs, objEnv)\r\n\tDim varRes\r\n\t\r\n\tIf objArgs.Count - 1 < 1 Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MTry\", \"Need more arguments.\"\r\n\tEnd If\r\n\r\n\tIf objArgs.Count - 1 = 1 Then\r\n\t\tSet varRes = EvalLater(objArgs.Item(1), objEnv)\r\n\t\tSet MTry = varRes\r\n\t\tExit Function\r\n\tEnd If\r\n\r\n\tCheckArgNum objArgs, 2\r\n\tCheckType objArgs.Item(2), TYPES.LIST\r\n\r\n\tDim objTry, objCatch\r\n\tSet objTry = objArgs.Item(1)\r\n\tSet objCatch = objArgs.Item(2)\r\n\t\r\n\tCheckArgNum objCatch, 2\r\n\tCheckType objCatch.Item(0), TYPES.SYMBOL\r\n\tCheckType objCatch.Item(1), TYPES.SYMBOL\r\n\tIf objCatch.Item(0).Value <> \"catch*\" Then\r\n\t\tErr.Raise vbObjectError, _\r\n\t\t\t\"MTry\", \"Unexpect argument(s).\"\r\n\tEnd If\r\n\t\r\n\tOn Error Resume Next\r\n\tSet varRes = Evaluate(objTry, objEnv)\r\n\tIf Err.Number <> 0 Then\r\n\t\tDim objException\r\n\r\n\t\tIf Err.Source <> \"MThrow\" Then\r\n\t\t\tSet objException = NewMalStr(Err.Description)\r\n\t\tElse\r\n\t\t\tSet objException = objExceptions.Item(Err.Description)\r\n\t\t\tobjExceptions.Remove Err.Description\r\n\t\tEnd If\r\n\t\t\r\n\t\tCall Err.Clear()\r\n\t\tOn Error Goto 0\r\n\r\n\t\t' The code below may cause error too.\r\n\t\t' So we should clear err info & throw out any errors.\r\n\t\t' Use 'quote' to avoid eval objExp again.\r\n\t\tSet varRes = Evaluate(NewMalList(Array( _\r\n\t\t\tNewMalSym(\"let*\"), NewMalList(Array( _\r\n\t\t\t\tobjCatch.Item(1), NewMalList(Array( _\r\n\t\t\t\t\t\tNewMalSym(\"quote\"), objException)))), _\r\n\t\t\tobjCatch.Item(2))), objEnv)\r\n\tElse\r\n\t\tOn Error Goto 0\r\n\tEnd If\r\n\r\n\tSet MTry = varRes\r\nEnd Function\r\nobjNS.Add \"try*\", NewVbsProc(\"MTry\", True)\r\n\r\nCall InitBuiltIn()\r\nCall InitMacro()\r\n\r\nCall InitArgs()\r\nSub InitArgs()\r\n\tDim objArgs\r\n\tSet objArgs = NewMalList(Array())\r\n\r\n\tDim i\r\n\tFor i = 1 To WScript.Arguments.Count - 1\r\n\t\tobjArgs.Add NewMalStr(WScript.Arguments.Item(i))\r\n\tNext\r\n\t\r\n\tobjNS.Add \"*ARGV*\", objArgs\r\n\t\r\n\tIf WScript.Arguments.Count > 0 Then\r\n\t\tREP \"(load-file \"\"\" + WScript.Arguments.Item(0) + \"\"\")\"\r\n\t\tWScript.Quit 0\r\n\tEnd If\r\nEnd Sub\r\n\r\nCall REPL()\r\nSub REPL()\r\n\tDim strCode\r\n\tREP \"(println (str \"\"Mal [\"\"*host-language*\"\"]\"\"))\"\r\n\tWhile True\r\n\t\tIO.Write \"user> \"\r\n\r\n\t\tOn Error Resume Next\r\n\t\t\tstrCode = IO.ReadLine\r\n\t\t\tIf Err.Number <> 0 Then WScript.Quit 0\r\n\t\tOn Error Goto 0\r\n\r\n\t\tDim strRes\r\n\t\tOn Error Resume Next\r\n\t\t\tstrRes = REP(strCode)\r\n\t\t\tIf Err.Number <> 0 Then\r\n\t\t\t\tIf Err.Source = \"MThrow\" Then\r\n\t\t\t\t\tIO.WriteErrLine \"Exception: \" + _\r\n\t\t\t\t\t\tPrintMalType(objExceptions.Item(Err.Description), True)\r\n\t\t\t\t\tobjExceptions.Remove Err.Description\r\n\t\t\t\tElse\r\n\t\t\t\t\tIO.WriteErrLine \"Exception: \" + Err.Description\r\n\t\t\t\tEnd If\r\n\t\t\tElse\r\n\t\t\t\tIf strRes <> \"\" Then\r\n\t\t\t\t\tIO.WriteLine strRes\r\n\t\t\t\tEnd If\r\n\t\t\tEnd If\r\n\t\tOn Error Goto 0\r\n\tWend\r\nEnd Sub\r\n\r\nFunction Read(strCode)\r\n\tSet Read = ReadString(strCode)\r\nEnd Function\r\n\r\nSub DebugEval(objCode, objEnv)\r\n\tDim value\r\n\tSet value = objEnv.Get(\"DEBUG-EVAL\")\r\n\t' And and Or do not short-circuit.\r\n\tIf TypeName(value) = \"Nothing\" Then\r\n\t\tExit Sub\r\n\tElse\r\n\t\tSelect Case value.Type\r\n\t\t\tCase TYPES.NIL\r\n\t\t\t\tExit Sub\r\n\t\t\tCase TYPES.BOOLEAN\r\n\t\t\t\tIf Not value.Value Then\r\n\t\t\t\t\tExit Sub\r\n\t\t\t\tEnd If\r\n\t\tEnd Select\r\n\tEnd If\r\n\tIO.WriteLine \"EVAL: \" + Print(objCode)\r\nEnd Sub\r\n\r\nFunction Evaluate(ByVal objCode, ByVal objEnv)\r\n\tWhile True\r\n\t\tIf TypeName(objCode) = \"Nothing\" Then\r\n\t\t\tSet Evaluate = Nothing\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\r\n\t\tDebugEval objCode, objEnv\r\n\r\n\t\tDim varRet, objFirst\r\n\t\tIf objCode.Type = TYPES.LIST Then\r\n\t\t\tIf objCode.Count = 0 Then ' ()\r\n\t\t\t\tSet Evaluate = objCode\r\n\t\t\t\tExit Function\r\n\t\t\tEnd If\r\n\r\n\t\t\tSet objFirst = Evaluate(objCode.Item(0), objEnv)\r\n\t\t\tIf objFirst.IsMacro Then\r\n\t\t\t\tSet varRet = EvalLater(objFirst.MacroApply(objCode, objEnv), objEnv)\r\n\t\t\tElse\r\n\t\t\t\tSet varRet = objFirst.Apply(objCode, objEnv)\r\n\t\t\tEnd If\r\n\t\tElse\r\n\t\t\tSet varRet = EvaluateAST(objCode, objEnv)\r\n\t\tEnd If\r\n\t\t\r\n\t\tIf TypeName(varRet) = \"TailCall\" Then\r\n\t\t\t' NOTICE: If not specify 'ByVal', \r\n\t\t\t' Change of arguments will influence\r\n\t\t\t' the caller's variable!\r\n\t\t\tSet objCode = varRet.objMalType\r\n\t\t\tSet objEnv = varRet.objEnv\r\n\t\tElse\r\n\t\t\tSet Evaluate = varRet\r\n\t\t\tExit Function\r\n\t\tEnd If\r\n\tWend\r\nEnd Function\r\n\r\n\r\nFunction EvaluateAST(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.SYMBOL\r\n\t\t\tSet varRet = objEnv.Get(objCode.Value)\r\n\t\t\tIf TypeName(varRet) = \"Nothing\" Then\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"EvaluateAST\", \"'\" + objCode.Value + \"' not found\"\r\n\t\t\tEnd If\r\n\t\tCase TYPES.LIST\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateAST\", \"Unexpect type.\"\r\n\t\tCase TYPES.VECTOR\r\n\t\t\tSet varRet = NewMalVec(Array())\r\n\t\t\tFor i = 0 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase TYPES.HASHMAP\r\n\t\t\tSet varRet = NewMalMap(Array(), Array())\r\n\t\t\tFor Each i In objCode.Keys()\r\n\t\t\t\tvarRet.Add i, Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tSet varRet = objCode\r\n\tEnd Select\r\n\tSet EvaluateAST = varRet\r\nEnd Function\r\n\r\nFunction EvaluateRest(objCode, objEnv)\r\n\tDim varRet, i\r\n\tSelect Case objCode.Type\r\n\t\tCase TYPES.LIST\r\n\t\t\tSet varRet = NewMalList(Array(NewMalNil()))\r\n\t\t\tFor i = 1 To objCode.Count() - 1\r\n\t\t\t\tvarRet.Add Evaluate(objCode.Item(i), objEnv)\r\n\t\t\tNext\r\n\t\tCase Else\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"EvaluateRest\", \"Unexpected type.\"\r\n\tEnd Select\r\n\tSet EvaluateRest = varRet\r\nEnd Function\r\n\r\nFunction Print(objCode)\r\n\tPrint = PrintMalType(objCode, True)\r\nEnd Function\r\n\r\nFunction REP(strCode)\r\n\tREP = Print(Evaluate(Read(strCode), objNS))\r\nEnd Function\r\n\r\nSub Include(strFileName)\r\n\tWith CreateObject(\"Scripting.FileSystemObject\")\r\n\t\tExecuteGlobal .OpenTextFile( _\r\n\t\t\t.GetParentFolderName( _\r\n\t\t\t.GetFile(WScript.ScriptFullName)) & _\r\n\t\t\t\"\\\" & strFileName).ReadAll\r\n\tEnd With\r\nEnd Sub\r\n"
  },
  {
    "path": "impls/vbs/tests/step4_if_fn_do.mal",
    "content": "((fn* [x] [x]) (list 1 2 3))\n;=>[(1 2 3)]\n\n((fn* [x] [x]) [1 2 3])\n;=>[[1 2 3]]\n\n((fn* [x] (list x)) (list 1 2 3))\n;=>((1 2 3))\n\n((fn* [x] (list x)) [1 2 3])\n;=>([1 2 3])\n\n((fn* [x] x) (list 1 2 3))\n;=>(1 2 3)\n\n((fn* [x] x) [1 2 3])\n;=>[1 2 3]\n\n"
  },
  {
    "path": "impls/vbs/tests/step9_try.mal",
    "content": "(throw (list 1 2 3))\n;/.*([Ee][Rr][Rr][Oo][Rr]|[Ee]xception).*\\(1 2 3\\).*\n\n(try* (throw {}) (catch* e (do (throw e))))\n;/.*([Ee][Rr][Rr][Oo][Rr]|[Ee]xception).*{}.*\n\n(try* (throw (list 1 2 3)) (catch* exc (do 7)))\n;=>7\n\n(try* (map throw (list \"my err\")) (catch* exc exc))\n;=>\"my err\""
  },
  {
    "path": "impls/vbs/types.vbs",
    "content": "Option Explicit\r\n\r\nDim TYPES\r\nSet TYPES = New MalTypes\r\n\r\nClass MalTypes\r\n\tPublic LIST, VECTOR, HASHMAP, [BOOLEAN], NIL\r\n\tPublic KEYWORD, [STRING], NUMBER, SYMBOL\r\n\tPublic PROCEDURE, ATOM\r\n\r\n\tPublic [TypeName]\r\n\tPrivate Sub Class_Initialize\r\n\t\t[TypeName] = Array( _\r\n\t\t\t\t\"LIST\", \"VECTOR\", \"HASHMAP\", \"BOOLEAN\", _\r\n\t\t\t\t\"NIL\", \"KEYWORD\", \"STRING\", \"NUMBER\", _\r\n\t\t\t\t\"SYMBOL\", \"PROCEDURE\", \"ATOM\")\r\n\r\n\t\tDim i\r\n\t\tFor i = 0 To UBound([TypeName])\r\n\t\t\tExecute \"[\" + [TypeName](i) + \"] = \" + CStr(i)\r\n\t\tNext\r\n\tEnd Sub\r\nEnd Class\r\n\r\nClass MalType\r\n\tPublic [Type]\r\n\tPublic Value\r\n\r\n\tPrivate varMeta\r\n\tPublic Property Get MetaData()\r\n\t\tIf IsEmpty(varMeta) Then\r\n\t\t\tSet MetaData = NewMalNil()\r\n\t\tElse\r\n\t\t\tSet MetaData = varMeta\r\n\t\tEnd If\r\n\tEnd Property\r\n\t\r\n\tPublic Property Set MetaData(objMeta)\r\n\t\tSet varMeta = objMeta\r\n\tEnd Property\r\n\t\r\n\tPublic Function Copy()\r\n\t\tSet Copy = NewMalType([Type], Value)\r\n\tEnd Function\r\n\r\n\tPublic Function Init(lngType, varValue)\r\n\t\t[Type] = lngType\r\n\t\tValue = varValue\r\n\tEnd Function\r\nEnd Class\r\n\r\nFunction NewMalType(lngType, varValue)\r\n\tDim varResult\r\n\tSet varResult = New MalType\r\n\tvarResult.Init lngType, varValue\r\n\tSet NewMalType = varResult\r\nEnd Function\r\n\r\nFunction NewMalBool(varValue)\r\n\tSet NewMalBool = NewMalType(TYPES.BOOLEAN, varValue)\r\nEnd Function\r\n\r\nFunction NewMalNil()\r\n\tSet NewMalNil = NewMalType(TYPES.NIL, Empty)\r\nEnd Function\r\n\r\nFunction NewMalKwd(varValue)\r\n\tSet NewMalKwd = NewMalType(TYPES.KEYWORD, varValue)\r\nEnd Function\r\n\r\nFunction NewMalStr(varValue)\r\n\tSet NewMalStr = NewMalType(TYPES.STRING, varValue)\r\nEnd Function\r\n\r\nFunction NewMalNum(varValue)\r\n\tSet NewMalNum = NewMalType(TYPES.NUMBER, varValue)\r\nEnd Function\r\n\r\nFunction NewMalSym(varValue)\r\n\tSet NewMalSym = NewMalType(TYPES.SYMBOL, varValue)\r\nEnd Function\r\n\r\nClass MalAtom\r\n\tPublic [Type]\r\n\tPublic Value\r\n\t\r\n\tPrivate varMeta\r\n\tPublic Property Get MetaData()\r\n\t\tIf IsEmpty(varMeta) Then\r\n\t\t\tSet MetaData = NewMalNil()\r\n\t\tElse\r\n\t\t\tSet MetaData = varMeta\r\n\t\tEnd If\r\n\tEnd Property\r\n\t\r\n\tPublic Property Set MetaData(objMeta)\r\n\t\tSet varMeta = objMeta\r\n\tEnd Property\r\n\r\n\tPublic Function Copy()\r\n\t\tSet Copy = NewMalAtom(Value)\r\n\tEnd Function\r\n\r\n\tPublic Sub Reset(objMal)\r\n\t\tSet Value = objMal\r\n\tEnd Sub\r\n\r\n\tPrivate Sub Class_Initialize\r\n\t\t[Type] = TYPES.ATOM\r\n\tEnd Sub\r\nEnd Class\r\n\r\nFunction NewMalAtom(varValue)\r\n\tDim varRes\r\n\tSet varRes = New MalAtom\r\n\tvarRes.Reset varValue\r\n\tSet NewMalAtom = varRes\r\nEnd Function\r\n\r\nClass MalList ' Extends MalType\r\n\tPublic [Type]\r\n\tPublic Value\r\n\t\r\n\tPrivate varMeta\r\n\tPublic Property Get MetaData()\r\n\t\tIf IsEmpty(varMeta) Then\r\n\t\t\tSet MetaData = NewMalNil()\r\n\t\tElse\r\n\t\t\tSet MetaData = varMeta\r\n\t\tEnd If\r\n\tEnd Property\r\n\t\r\n\tPublic Property Set MetaData(objMeta)\r\n\t\tSet varMeta = objMeta\r\n\tEnd Property\r\n\r\n\tPublic Function Copy()\r\n\t\tSet Copy = New MalList\r\n\t\tSet Copy.Value = Value\r\n\tEnd Function\r\n\r\n\tPrivate Sub Class_Initialize\r\n\t\t[Type] = TYPES.LIST\r\n\t\tSet Value = CreateObject(\"System.Collections.ArrayList\")\r\n\tEnd Sub\r\n\r\n\tPublic Function Init(arrValues)\r\n\t\tDim i\r\n\t\tFor i = 0 To UBound(arrValues)\r\n\t\t\tAdd arrValues(i)\r\n\t\tNext\r\n\tEnd Function\r\n\r\n\tPublic Function Add(objMalType)\r\n\t\tValue.Add objMalType\r\n\tEnd Function\r\n\t\r\n\tPublic Property Get Item(i)\r\n\t\tSet Item = Value.Item(i)\r\n\tEnd Property\r\n\r\n\tPublic Property Let Item(i, varValue)\r\n\t\tValue.Item(i) = varValue\r\n\tEnd Property\r\n\r\n\tPublic Property Set Item(i, varValue)\r\n\t\tSet Value.Item(i) = varValue\r\n\tEnd Property\r\n\r\n\tPublic Function Count()\r\n\t\tCount = Value.Count\r\n\tEnd Function\r\nEnd Class\r\n\r\nFunction NewMalList(arrValues)\r\n\tDim varResult\r\n\tSet varResult = New MalList\r\n\tvarResult.Init arrValues\r\n\tSet NewMalList = varResult\r\nEnd Function\r\n\r\nClass MalVector ' Extends MalType\r\n\tPublic [Type]\r\n\tPublic Value\r\n\t\r\n\tPrivate varMeta\r\n\tPublic Property Get MetaData()\r\n\t\tIf IsEmpty(varMeta) Then\r\n\t\t\tSet MetaData = NewMalNil()\r\n\t\tElse\r\n\t\t\tSet MetaData = varMeta\r\n\t\tEnd If\r\n\tEnd Property\r\n\t\r\n\tPublic Property Set MetaData(objMeta)\r\n\t\tSet varMeta = objMeta\r\n\tEnd Property\r\n\r\n\tPublic Function Copy()\r\n\t\tSet Copy = New MalVector\r\n\t\tSet Copy.Value = Value\r\n\tEnd Function\r\n\r\n\tPrivate Sub Class_Initialize\r\n\t\t[Type] = TYPES.VECTOR\r\n\t\tSet Value = CreateObject(\"System.Collections.ArrayList\")\r\n\tEnd Sub\r\n\r\n\tPublic Function Init(arrValues)\r\n\t\tDim i\r\n\t\tFor i = 0 To UBound(arrValues)\r\n\t\t\tAdd arrValues(i)\r\n\t\tNext\r\n\tEnd Function\r\n\r\n\tPublic Function Add(objMalType)\r\n\t\tValue.Add objMalType\r\n\tEnd Function\r\n\t\r\n\tPublic Property Get Item(i)\r\n\t\tSet Item = Value.Item(i)\r\n\tEnd Property\r\n\r\n\tPublic Property Let Item(i, varValue)\r\n\t\tValue.Item(i) = varValue\r\n\tEnd Property\r\n\r\n\tPublic Property Set Item(i, varValue)\r\n\t\tSet Value.Item(i) = varValue\r\n\tEnd Property\r\n\r\n\tPublic Function Count()\r\n\t\tCount = Value.Count\r\n\tEnd Function\r\nEnd Class\r\n\r\nFunction NewMalVec(arrValues)\r\n\tDim varResult\r\n\tSet varResult = New MalVector\r\n\tvarResult.Init arrValues\r\n\tSet NewMalVec = varResult\r\nEnd Function\r\n\r\nClass MalHashmap 'Extends MalType\r\n\tPublic [Type]\r\n\tPublic Value\r\n\r\n\tPrivate varMeta\r\n\tPublic Property Get MetaData()\r\n\t\tIf IsEmpty(varMeta) Then\r\n\t\t\tSet MetaData = NewMalNil()\r\n\t\tElse\r\n\t\t\tSet MetaData = varMeta\r\n\t\tEnd If\r\n\tEnd Property\r\n\t\r\n\tPublic Property Set MetaData(objMeta)\r\n\t\tSet varMeta = objMeta\r\n\tEnd Property\r\n\r\n\tPublic Function Copy()\r\n\t\tSet Copy = New MalHashmap\r\n\t\tSet Copy.Value = Value\r\n\tEnd Function\r\n\r\n\r\n\tPrivate Sub Class_Initialize\r\n\t\t[Type] = TYPES.HASHMAP\r\n\t\tSet Value = CreateObject(\"Scripting.Dictionary\")\r\n\tEnd Sub\r\n\r\n\tPublic Function Init(arrKeys, arrValues)\r\n\t\tDim i\r\n\t\tFor i = 0 To UBound(arrKeys)\r\n\t\t\tAdd arrKeys(i), arrValues(i)\r\n\t\tNext\r\n\tEnd Function\r\n\r\n\tPrivate Function M2S(objKey)\r\n\t\tDim varRes\r\n\t\tSelect Case objKey.Type\r\n\t\t\tCase TYPES.STRING\r\n\t\t\t\tvarRes = \"S\" + objKey.Value\r\n\t\t\tCase TYPES.KEYWORD\r\n\t\t\t\tvarRes = \"K\" + objKey.Value\r\n\t\t\tCase Else\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"MalHashmap\", \"Unexpect key type.\"\r\n\t\tEnd Select\r\n\t\tM2S = varRes\r\n\tEnd Function\r\n\r\n\tPrivate Function S2M(strKey)\r\n\t\tDim varRes\r\n\t\tSelect Case Left(strKey, 1)\r\n\t\t\tCase \"S\"\r\n\t\t\t\tSet varRes = NewMalStr(Right(strKey, Len(strKey) - 1))\r\n\t\t\tCase \"K\"\r\n\t\t\t\tSet varRes = NewMalKwd(Right(strKey, Len(strKey) - 1))\r\n\t\t\tCase Else\r\n\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\"MalHashmap\", \"Unexpect key type.\"\r\n\t\tEnd Select\r\n\t\tSet S2M = varRes\r\n\tEnd Function\r\n\r\n\tPublic Function Add(varKey, varValue)\r\n\t\tIf varKey.Type <> TYPES.STRING And _\r\n\t\t\tvarKey.Type <> TYPES.KEYWORD Then\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MalHashmap\", \"Unexpect key type.\"\r\n\t\tEnd If\r\n\t\t\r\n\t\tSet Value.Item(M2S(varKey)) = varValue\r\n\t\t'Value.Add M2S(varKey), varValue\r\n\tEnd Function\r\n\t\r\n\tPublic Property Get Keys()\r\n\t\tDim aKeys\r\n\t\taKeys = Value.Keys\r\n\t\tDim aRes()\r\n\t\tReDim aRes(UBound(aKeys))\r\n\t\tDim i\r\n\t\tFor i = 0 To UBound(aRes)\r\n\t\t\tSet aRes(i) = S2M(aKeys(i))\r\n\t\tNext\r\n\r\n\t\tKeys = aRes\r\n\tEnd Property\r\n\r\n\tPublic Function Count()\r\n\t\tCount = Value.Count\r\n\tEnd Function\r\n\r\n\tPublic Property Get Item(i)\r\n\t\tSet Item = Value.Item(M2S(i))\r\n\tEnd Property\r\n\r\n\tPublic Function Exists(varKey)\r\n\t\tIf varKey.Type <> TYPES.STRING And _\r\n\t\t\tvarKey.Type <> TYPES.KEYWORD Then\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MalHashmap\", \"Unexpect key type.\"\r\n\t\tEnd If\r\n\t\tExists = Value.Exists(M2S(varKey))\r\n\tEnd Function\r\n\r\n\tPublic Property Let Item(i, varValue)\r\n\t\tValue.Item(M2S(i)) = varValue\r\n\tEnd Property\r\n\r\n\tPublic Property Set Item(i, varValue)\r\n\t\tSet Value.Item(M2S(i)) = varValue\r\n\tEnd Property\r\nEnd Class\r\n\r\nFunction NewMalMap(arrKeys, arrValues)\r\n\tDim varResult\r\n\tSet varResult = New MalHashmap\r\n\tvarResult.Init arrKeys, arrValues\r\n\tSet NewMalMap = varResult\r\nEnd Function\r\n\r\nClass VbsProcedure 'Extends MalType\r\n\tPublic [Type]\r\n\tPublic Value\r\n\t\r\n\tPublic IsMacro\r\n\tPublic boolSpec\r\n\tPublic MetaData\r\n\tPrivate Sub Class_Initialize\r\n\t\t[Type] = TYPES.PROCEDURE\r\n\t\tIsMacro = False\r\n\t\tSet MetaData = NewMalNil()\r\n\tEnd Sub\r\n\r\n\tPublic Property Get IsSpecial()\r\n\t\tIsSpecial = boolSpec\r\n\tEnd Property\r\n\r\n\tPublic Function Init(objFunction, boolIsSpec)\r\n\t\tSet Value = objFunction\r\n\t\tboolSpec = boolIsSpec\r\n\tEnd Function\r\n\r\n\tPublic Function Apply(objArgs, objEnv)\r\n\t\tDim varResult\r\n\t\tIf boolSpec Then\r\n\t\t\tSet varResult = Value(objArgs, objEnv)\r\n\t\tElse\r\n\t\t\tSet varResult = Value(EvaluateRest(objArgs, objEnv), objEnv)\r\n\t\tEnd If\r\n\t\tSet Apply = varResult\r\n\tEnd Function\r\n\r\n\tPublic Function ApplyWithoutEval(objArgs, objEnv)\r\n\t\tDim varResult\r\n\t\tSet varResult = Value(objArgs, objEnv)\r\n\t\t\r\n\t\tSet ApplyWithoutEval = varResult\r\n\tEnd Function\r\n\r\n\tPublic Function Copy()\r\n\t\tDim varRes\r\n\t\tSet varRes = New VbsProcedure\r\n\t\tvarRes.Type = [Type]\r\n\t\tSet varRes.Value = Value\r\n\t\tvarRes.IsMacro = IsMacro\r\n\t\tvarRes.boolSpec = boolSpec\r\n\t\tSet Copy = varRes\r\n\tEnd Function\r\nEnd Class\r\n\r\nFunction NewVbsProc(strFnName, boolSpec)\r\n\tDim varResult\r\n\tSet varResult = New VbsProcedure\r\n\tvarResult.Init GetRef(strFnName), boolSpec\r\n\tSet NewVbsProc = varResult\r\nEnd Function\r\n\r\nClass MalProcedure 'Extends MalType\r\n\tPublic [Type]\r\n\tPublic Value\r\n\t\r\n\tPublic IsMacro\r\n\r\n\tPublic Property Get IsSpecial()\r\n\t\tIsSpecial = False\r\n\tEnd Property\r\n\r\n\tPublic MetaData\r\n\tPrivate Sub Class_Initialize\r\n\t\t[Type] = TYPES.PROCEDURE\r\n\t\tIsMacro = False\r\n\t\tSet MetaData = NewMalNil()\r\n\tEnd Sub\r\n\r\n\tPublic objParams, objCode, objSavedEnv\r\n\tPublic Function Init(objP, objC, objE)\r\n\t\tSet objParams = objP\r\n\t\tSet objCode = objC\r\n\t\tSet objSavedEnv = objE\r\n\tEnd Function\r\n\r\n\tPublic Function Apply(objArgs, objEnv)\r\n\t\tIf IsMacro Then\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MalProcedureApply\", \"Not a procedure.\"\r\n\t\tEnd If\r\n\r\n\t\tDim varRet\r\n\t\tDim objNewEnv\r\n\t\tSet objNewEnv = NewEnv(objSavedEnv)\r\n\t\tDim i\r\n\t\ti = 0\r\n\t\tDim objList\r\n\t\tWhile i < objParams.Count\r\n\t\t\tIf objParams.Item(i).Value = \"&\" Then\r\n\t\t\t\tIf objParams.Count - 1 = i + 1 Then\r\n\t\t\t\t\tSet objList = NewMalList(Array())\r\n\t\t\t\t\tobjNewEnv.Add objParams.Item(i + 1).Value, objList\r\n\t\t\t\t\tWhile i + 1 < objArgs.Count\r\n\t\t\t\t\t\tobjList.Add Evaluate(objArgs.Item(i + 1), objEnv)\r\n\t\t\t\t\t\ti = i + 1\r\n\t\t\t\t\tWend\r\n\t\t\t\t\ti = objParams.Count ' Break While\r\n\t\t\t\tElse\r\n\t\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\t\"MalProcedureApply\", \"Invalid parameter(s).\"\r\n\t\t\t\tEnd If\r\n\t\t\tElse\r\n\t\t\t\tIf i + 1 >= objArgs.Count Then\r\n\t\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\t\"MalProcedureApply\", \"Need more arguments.\"\r\n\t\t\t\tEnd If\r\n\t\t\t\tobjNewEnv.Add objParams.Item(i).Value, _\r\n\t\t\t\t\tEvaluate(objArgs.Item(i + 1), objEnv)\r\n\t\t\t\ti = i + 1\r\n\t\t\tEnd If\r\n\t\tWend\r\n\t\t\r\n\t\tSet varRet = EvalLater(objCode, objNewEnv)\r\n\t\tSet Apply = varRet\r\n\tEnd Function\r\n\r\n\tPublic Function MacroApply(objArgs, objEnv)\r\n\t\tIf Not IsMacro Then\r\n\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\"MalMacroApply\", \"Not a macro.\"\r\n\t\tEnd If\r\n\r\n\t\tDim varRet\r\n\t\tDim objNewEnv\r\n\t\tSet objNewEnv = NewEnv(objSavedEnv)\r\n\t\tDim i\r\n\t\ti = 0\r\n\t\tDim objList\r\n\t\tWhile i < objParams.Count\r\n\t\t\tIf objParams.Item(i).Value = \"&\" Then\r\n\t\t\t\tIf objParams.Count - 1 = i + 1 Then\r\n\t\t\t\t\tSet objList = NewMalList(Array())\r\n\t\t\t\t\t\r\n\t\t\t\t\t' No evaluation\r\n\t\t\t\t\tobjNewEnv.Add objParams.Item(i + 1).Value, objList\r\n\t\t\t\t\tWhile i + 1 < objArgs.Count\r\n\t\t\t\t\t\tobjList.Add objArgs.Item(i + 1)\r\n\t\t\t\t\t\ti = i + 1\r\n\t\t\t\t\tWend\r\n\t\t\t\t\ti = objParams.Count ' Break While\r\n\t\t\t\tElse\r\n\t\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\t\"MalMacroApply\", \"Invalid parameter(s).\"\r\n\t\t\t\tEnd If\r\n\t\t\tElse\r\n\t\t\t\tIf i + 1 >= objArgs.Count Then\r\n\t\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\t\"MalMacroApply\", \"Need more arguments.\"\r\n\t\t\t\tEnd If\r\n\t\t\t\t\r\n\t\t\t\t' No evaluation\r\n\t\t\t\tobjNewEnv.Add objParams.Item(i).Value, _\r\n\t\t\t\t\tobjArgs.Item(i + 1)\r\n\t\t\t\ti = i + 1\r\n\t\t\tEnd If\r\n\t\tWend\r\n\t\t\r\n\t\t' EvalLater -> Evaluate\r\n\t\tSet varRet = Evaluate(objCode, objNewEnv)\r\n\t\tSet MacroApply = varRet\r\n\tEnd Function\r\n\r\n\r\n\tPublic Function ApplyWithoutEval(objArgs, objEnv)\r\n\t\tDim varRet\r\n\t\tDim objNewEnv\r\n\t\tSet objNewEnv = NewEnv(objSavedEnv)\r\n\t\tDim i\r\n\t\ti = 0\r\n\t\tDim objList\r\n\t\tWhile i < objParams.Count\r\n\t\t\tIf objParams.Item(i).Value = \"&\" Then\r\n\t\t\t\tIf objParams.Count - 1 = i + 1 Then\r\n\t\t\t\t\tSet objList = NewMalList(Array())\r\n\t\t\t\t\t\r\n\t\t\t\t\t' No evaluation\r\n\t\t\t\t\tobjNewEnv.Add objParams.Item(i + 1).Value, objList\r\n\t\t\t\t\tWhile i + 1 < objArgs.Count\r\n\t\t\t\t\t\tobjList.Add objArgs.Item(i + 1)\r\n\t\t\t\t\t\ti = i + 1\r\n\t\t\t\t\tWend\r\n\t\t\t\t\ti = objParams.Count ' Break While\r\n\t\t\t\tElse\r\n\t\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\t\"MalMacroApply\", \"Invalid parameter(s).\"\r\n\t\t\t\tEnd If\r\n\t\t\tElse\r\n\t\t\t\tIf i + 1 >= objArgs.Count Then\r\n\t\t\t\t\tErr.Raise vbObjectError, _\r\n\t\t\t\t\t\t\"MalMacroApply\", \"Need more arguments.\"\r\n\t\t\t\tEnd If\r\n\t\t\t\t\r\n\t\t\t\t' No evaluation\r\n\t\t\t\tobjNewEnv.Add objParams.Item(i).Value, _\r\n\t\t\t\t\tobjArgs.Item(i + 1)\r\n\t\t\t\ti = i + 1\r\n\t\t\tEnd If\r\n\t\tWend\r\n\t\t\r\n\t\t' EvalLater -> Evaluate\r\n\t\tSet varRet = Evaluate(objCode, objNewEnv)\r\n\t\tSet ApplyWithoutEval = varRet\r\n\tEnd Function\r\n\r\n\t\r\n\tPublic Function Copy()\r\n\t\tDim varRes\r\n\t\tSet varRes = New MalProcedure\r\n\t\tvarRes.Type = [Type]\r\n\t\tvarRes.Value = Value\r\n\t\tvarRes.IsMacro = IsMacro\r\n\t\tSet varRes.objParams = objParams\r\n\t\tSet varRes.objCode = objCode\r\n\t\tSet varRes.objSavedEnv = objSavedEnv\r\n\t\tSet Copy = varRes\r\n\tEnd Function\r\nEnd Class\r\n\r\nFunction NewMalProc(objParams, objCode, objEnv)\r\n\tDim varRet\r\n\tSet varRet = New MalProcedure\r\n\tvarRet.Init objParams, objCode, objEnv\r\n\tSet NewMalProc = varRet\r\nEnd Function\r\n\r\nFunction NewMalMacro(objParams, objCode, objEnv)\r\n\tDim varRet\r\n\tSet varRet = New MalProcedure\r\n\tvarRet.Init objParams, objCode, objEnv\r\n\tvarRet.IsMacro = True\r\n\tSet NewMalProc = varRet\r\nEnd Function\r\n\r\nFunction SetMeta(objMal, objMeta)\r\n\tDim varRes\r\n\tSet varRes = objMal.Copy\r\n\tSet varRes.MetaData = objMeta\r\n\tSet SetMeta = varRes\r\nEnd Function\r\n\r\nFunction GetMeta(objMal)\r\n\tSet GetMeta = objMal.MetaData\r\nEnd Function"
  },
  {
    "path": "impls/vhdl/.gitignore",
    "content": "work-obj93.cf\n"
  },
  {
    "path": "impls/vhdl/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install gcc ghdl ghdl-gcc\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/vhdl/Makefile",
    "content": "SRCS = step0_repl.vhdl step1_read_print.vhdl step2_eval.vhdl step3_env.vhdl \\\n       step4_if_fn_do.vhdl step5_tco.vhdl step6_file.vhdl step7_quote.vhdl \\\n       step8_macros.vhdl step9_try.vhdl stepA_mal.vhdl\nOBJS = $(SRCS:%.vhdl=%.o)\nBINS = $(OBJS:%.o=%)\nOTHER_SRCS = pkg_readline.vhdl types.vhdl printer.vhdl reader.vhdl env.vhdl core.vhdl\nOTHER_OBJS = $(OTHER_SRCS:%.vhdl=%.o)\n\n#####################\n\nall: $(BINS)\n\ndist: mal\n\nmal: $(word $(words $(BINS)),$(BINS))\n\tcp $< $@\n\nwork-obj93.cf: $(OTHER_SRCS)\n\trm -f work-obj93.cf\n\tghdl -i $+\n\n$(OTHER_OBJS):  %.o: %.vhdl work-obj93.cf\n\tghdl -a -g $(@:%.o=%.vhdl)\n\n$(OBJS): %.o: %.vhdl $(OTHER_OBJS)\n\tghdl -a -g $(@:%.o=%.vhdl)\n\n$(patsubst %.o,%,$(filter step%,$(OBJS))): $(OTHER_OBJS)\n$(BINS): %: %.o\n\tghdl -e -g $@\n\t# ghdl linker creates a lowercase executable; rename it to stepA_mal\n\tif [ \"$@\" = \"stepA_mal\" ]; then mv stepa_mal $@; fi\n\nclean:\n\trm -f $(OBJS) $(BINS) $(OTHER_OBJS) work-obj93.cf mal\n"
  },
  {
    "path": "impls/vhdl/core.vhdl",
    "content": "library STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.types.all;\nuse WORK.env.all;\nuse WORK.reader.all;\nuse WORK.printer.all;\nuse WORK.pkg_readline.all;\n\npackage core is\n  procedure eval_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n  procedure define_core_functions(e: inout env_ptr);\nend package core;\n\npackage body core is\n\n  procedure fn_equal(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable is_equal: boolean;\n  begin\n    equal_q(args.seq_val(0), args.seq_val(1), is_equal);\n    new_boolean(is_equal, result);\n  end procedure fn_equal;\n\n  procedure fn_throw(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    err := args.seq_val(0);\n  end procedure fn_throw;\n\n  procedure fn_nil_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_nil, result);\n  end procedure fn_nil_q;\n\n  procedure fn_true_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_true, result);\n  end procedure fn_true_q;\n\n  procedure fn_false_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_false, result);\n  end procedure fn_false_q;\n\n  procedure fn_string_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_string, result);\n  end procedure fn_string_q;\n\n  procedure fn_symbol(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_symbol(args.seq_val(0).string_val, result);\n  end procedure fn_symbol;\n\n  procedure fn_symbol_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_symbol, result);\n  end procedure fn_symbol_q;\n\n  procedure fn_keyword(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_keyword(args.seq_val(0).string_val, result);\n  end procedure fn_keyword;\n\n  procedure fn_keyword_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_keyword, result);\n  end procedure fn_keyword_q;\n\n  procedure fn_number_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_number, result);\n  end procedure fn_number_q;\n\n  procedure fn_function_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean((args.seq_val(0).val_type = mal_fn and not args.seq_val(0).func_val.f_is_macro) or args.seq_val(0).val_type = mal_nativefn, result);\n  end procedure fn_function_q;\n\n  procedure fn_macro_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_fn and args.seq_val(0).func_val.f_is_macro, result);\n  end procedure fn_macro_q;\n\n  procedure fn_pr_str(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable s: line;\n  begin\n    pr_seq(\"\", \"\", \" \", args.seq_val, true, s);\n    new_string(s, result);\n  end procedure fn_pr_str;\n\n  procedure fn_str(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable s: line;\n  begin\n    pr_seq(\"\", \"\", \"\", args.seq_val, false, s);\n    new_string(s, result);\n  end procedure fn_str;\n\n  procedure fn_prn(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable s: line;\n  begin\n    pr_seq(\"\", \"\", \" \", args.seq_val, true, s);\n    mal_printline(s.all);\n    new_nil(result);\n  end procedure fn_prn;\n\n  procedure fn_println(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable s: line;\n  begin\n    pr_seq(\"\", \"\", \" \", args.seq_val, false, s);\n    mal_printline(s.all);\n    new_nil(result);\n  end procedure fn_println;\n\n  procedure fn_read_string(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast: mal_val_ptr;\n  begin\n    read_str(args.seq_val(0).string_val.all, ast, err);\n    if ast = null then\n      new_nil(result);\n    else\n      result := ast;\n    end if;\n  end procedure fn_read_string;\n\n  procedure fn_readline(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable input_line: line;\n    variable is_eof: boolean;\n  begin\n    mal_readline(args.seq_val(0).string_val.all, is_eof, input_line);\n    if is_eof then\n      new_nil(result);\n    else\n      new_string(input_line, result);\n    end if;\n  end procedure fn_readline;\n\n  procedure fn_slurp(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    file f: text;\n    variable status: file_open_status;\n    variable save_content, content, one_line: line;\n  begin\n    file_open(status, f, external_name => args.seq_val(0).string_val.all, open_kind => read_mode);\n    if status = open_ok then\n      content := new string'(\"\");\n      while not endfile(f) loop\n        readline(f, one_line);\n        save_content := content;\n        content := new string'(save_content.all & one_line.all & LF);\n        deallocate(save_content);\n      end loop;\n      file_close(f);\n      new_string(content, result);\n    else\n      new_string(\"Error opening file\", err);\n    end if;\n  end procedure fn_slurp;\n\n  procedure fn_lt(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).number_val < args.seq_val(1).number_val, result);\n  end procedure fn_lt;\n\n  procedure fn_lte(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).number_val <= args.seq_val(1).number_val, result);\n  end procedure fn_lte;\n\n  procedure fn_gt(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).number_val > args.seq_val(1).number_val, result);\n  end procedure fn_gt;\n\n  procedure fn_gte(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).number_val >= args.seq_val(1).number_val, result);\n  end procedure fn_gte;\n\n  procedure fn_add(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_number(args.seq_val(0).number_val + args.seq_val(1).number_val, result);\n  end procedure fn_add;\n\n  procedure fn_sub(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_number(args.seq_val(0).number_val - args.seq_val(1).number_val, result);\n  end procedure fn_sub;\n\n  procedure fn_mul(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_number(args.seq_val(0).number_val * args.seq_val(1).number_val, result);\n  end procedure fn_mul;\n\n  procedure fn_div(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_number(args.seq_val(0).number_val / args.seq_val(1).number_val, result);\n  end procedure fn_div;\n\n  -- Define physical types (c_seconds64, c_microseconds64) because these are\n  -- represented as 64-bit words when passed to C functions\n  type c_seconds64 is range 0 to 1E16\n    units\n      c_sec;\n    end units c_seconds64;\n\n  type c_microseconds64 is range 0 to 1E6\n    units\n      c_usec;\n    end units c_microseconds64;\n\n  type c_timeval is record\n    tv_sec: c_seconds64;\n    tv_usec: c_microseconds64;\n  end record c_timeval;\n\n  -- Leave enough room for two 64-bit words\n  type c_timezone is record\n    dummy_1: c_seconds64;\n    dummy_2: c_seconds64;\n  end record c_timezone;\n\n  function gettimeofday(tv: c_timeval; tz: c_timezone) return integer;\n  attribute foreign of gettimeofday: function is \"VHPIDIRECT gettimeofday\";\n\n  function gettimeofday(tv: c_timeval; tz: c_timezone) return integer is\n  begin\n    assert false severity failure;\n  end function gettimeofday;\n\n  -- Returns the number of milliseconds since last midnight UTC because a\n  -- standard VHDL integer is 32-bit and therefore cannot hold the number of\n  -- milliseconds since 1970-01-01.\n  procedure fn_time_ms(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable tv: c_timeval;\n    variable dummy: c_timezone;\n    variable rc: integer;\n  begin\n    rc := gettimeofday(tv, dummy);\n    new_number(((tv.tv_sec / 1 c_sec) mod 86400) * 1000 + (tv.tv_usec / 1000 c_usec), result);\n  end procedure fn_time_ms;\n\n  procedure fn_list(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    result := args;\n  end procedure fn_list;\n\n  procedure fn_list_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_list, result);\n  end procedure fn_list_q;\n\n  procedure fn_vector(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    args.val_type := mal_vector;\n    result := args;\n  end procedure fn_vector;\n\n  procedure fn_vector_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_vector, result);\n  end procedure fn_vector_q;\n\n  procedure fn_hash_map(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable new_map: mal_val_ptr;\n  begin\n    new_empty_hashmap(new_map);\n    for i in 0 to args.seq_val'length / 2 - 1 loop\n      hashmap_put(new_map, args.seq_val(2*i), args.seq_val(2*i+1));\n    end loop;\n    result := new_map;\n  end procedure fn_hash_map;\n\n  procedure fn_map_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(args.seq_val(0).val_type = mal_hashmap, result);\n  end procedure fn_map_q;\n\n  procedure fn_assoc(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable new_hashmap: mal_val_ptr;\n    variable i: integer;\n  begin\n    hashmap_copy(args.seq_val(0), new_hashmap);\n    i := 1;\n    while i < args.seq_val'length loop\n      hashmap_put(new_hashmap, args.seq_val(i), args.seq_val(i + 1));\n      i := i + 2;\n    end loop;\n    result := new_hashmap;\n  end procedure fn_assoc;\n\n  procedure fn_dissoc(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable new_hashmap: mal_val_ptr;\n    variable i: integer;\n  begin\n    hashmap_copy(args.seq_val(0), new_hashmap);\n    for i in 1 to args.seq_val'high loop\n      hashmap_delete(new_hashmap, args.seq_val(i));\n    end loop;\n    result := new_hashmap;\n  end procedure fn_dissoc;\n\n  procedure fn_get(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable a1: mal_val_ptr := args.seq_val(1);\n    variable val: mal_val_ptr;\n  begin\n    if a0.val_type = mal_nil then\n      new_nil(result);\n    else\n      hashmap_get(a0, a1, val);\n      if val = null then\n        new_nil(result);\n      else\n        result := val;\n      end if;\n    end if;\n  end procedure fn_get;\n\n  procedure fn_contains_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable a1: mal_val_ptr := args.seq_val(1);\n    variable found: boolean;\n  begin\n    hashmap_contains(a0, a1, found);\n    new_boolean(found, result);\n  end procedure fn_contains_q;\n\n  procedure fn_keys(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable seq: mal_seq_ptr;\n  begin\n    seq := new mal_seq(0 to a0.seq_val'length / 2 - 1);\n    for i in seq'range loop\n      seq(i) := a0.seq_val(i * 2);\n    end loop;\n    new_seq_obj(mal_list, seq, result);\n  end procedure fn_keys;\n\n  procedure fn_vals(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable seq: mal_seq_ptr;\n  begin\n    seq := new mal_seq(0 to a0.seq_val'length / 2 - 1);\n    for i in seq'range loop\n      seq(i) := a0.seq_val(i * 2 + 1);\n    end loop;\n    new_seq_obj(mal_list, seq, result);\n  end procedure fn_vals;\n\n  procedure cons_helper(a0: inout mal_val_ptr; a1: inout mal_val_ptr; result: out mal_val_ptr) is\n    variable seq: mal_seq_ptr;\n  begin\n    seq := new mal_seq(0 to a1.seq_val'length);\n    seq(0) := a0;\n    seq(1 to seq'length - 1) := a1.seq_val(0 to a1.seq_val'length - 1);\n    new_seq_obj(mal_list, seq, result);\n  end procedure cons_helper;\n\n  procedure fn_cons(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable a1: mal_val_ptr := args.seq_val(1);\n    variable seq: mal_seq_ptr;\n  begin\n    cons_helper(a0, a1, result);\n  end procedure fn_cons;\n\n  procedure fn_sequential_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_boolean(is_sequential_type(args.seq_val(0).val_type), result);\n  end procedure fn_sequential_q;\n\n  procedure fn_concat(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    seq := new mal_seq(0 to -1);\n    for i in args.seq_val'range loop\n      seq := new mal_seq'(seq.all & args.seq_val(i).seq_val.all);\n    end loop;\n    new_seq_obj(mal_list, seq, result);\n  end procedure fn_concat;\n\n  procedure fn_vec(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    if args.seq_val(0).val_type = mal_vector then\n      result := args.seq_val(0);\n    else\n      new_seq_obj(mal_vector, args.seq_val(0).seq_val, result);\n    end if;\n  end procedure fn_vec;\n\n  procedure fn_nth(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable lst_seq: mal_seq_ptr := args.seq_val(0).seq_val;\n    variable index: integer := args.seq_val(1).number_val;\n  begin\n    if index >= lst_seq'length then\n      new_string(\"nth: index out of range\", err);\n    else\n      result := lst_seq(index);\n    end if;\n  end procedure fn_nth;\n\n  procedure fn_first(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n  begin\n    if a0.val_type = mal_nil or a0.seq_val'length = 0 then\n      new_nil(result);\n    else\n      result := a0.seq_val(0);\n    end if;\n  end procedure fn_first;\n\n  procedure fn_rest(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable seq: mal_seq_ptr;\n    variable new_list: mal_val_ptr;\n  begin\n    if a0.val_type = mal_nil or a0.seq_val'length = 0 then\n      seq := new mal_seq(0 to -1);\n      new_seq_obj(mal_list, seq, result);\n    else\n      seq_drop_prefix(a0, 1, new_list);\n      new_list.val_type := mal_list;\n      result := new_list;\n    end if;\n  end procedure fn_rest;\n\n  procedure fn_empty_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable is_empty: boolean;\n  begin\n    case args.seq_val(0).val_type is\n      when mal_nil => new_boolean(true, result);\n      when mal_list | mal_vector => new_boolean(args.seq_val(0).seq_val'length = 0, result);\n      when others => new_string(\"empty?: invalid argument type\", err);\n    end case;\n  end procedure fn_empty_q;\n\n  procedure fn_count(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable count: integer;\n  begin\n    case args.seq_val(0).val_type is\n      when mal_nil => new_number(0, result);\n      when mal_list | mal_vector => new_number(args.seq_val(0).seq_val'length, result);\n      when others => new_string(\"count: invalid argument type\", err);\n    end case;\n  end procedure fn_count;\n\n  procedure fn_conj(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable r: mal_val_ptr;\n    variable seq: mal_seq_ptr;\n  begin\n    case a0.val_type is\n      when mal_list =>\n        r := a0;\n        for i in 1 to args.seq_val'high loop\n          cons_helper(args.seq_val(i), r, r);\n        end loop;\n        result := r;\n      when mal_vector =>\n        seq := new mal_seq(0 to a0.seq_val'length + args.seq_val'length - 2);\n        seq(0 to a0.seq_val'high) := a0.seq_val(a0.seq_val'range);\n        seq(a0.seq_val'high + 1 to seq'high) := args.seq_val(1 to args.seq_val'high);\n        new_seq_obj(mal_vector, seq, result);\n      when others =>\n        new_string(\"conj requires list or vector\", err);\n    end case;\n  end procedure fn_conj;\n\n  procedure fn_seq(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable new_seq: mal_seq_ptr;\n  begin\n    case a0.val_type is\n      when mal_string =>\n        if a0.string_val'length = 0 then\n          new_nil(result);\n        else\n          new_seq := new mal_seq(0 to a0.string_val'length - 1);\n          for i in new_seq'range loop\n            new_string(\"\" & a0.string_val(i + 1), new_seq(i));\n          end loop;\n          new_seq_obj(mal_list, new_seq, result);\n        end if;\n      when mal_list =>\n        if a0.seq_val'length = 0 then\n          new_nil(result);\n        else\n          result := a0;\n        end if;\n      when mal_vector =>\n        if a0.seq_val'length = 0 then\n          new_nil(result);\n        else\n          new_seq_obj(mal_list, a0.seq_val, result);\n        end if;\n      when mal_nil =>\n        new_nil(result);\n      when others =>\n        new_string(\"seq requires string or list or vector or nil\", err);\n    end case;\n  end procedure fn_seq;\n\n  procedure fn_meta(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable meta_val: mal_val_ptr;\n  begin\n    meta_val := args.seq_val(0).meta_val;\n    if meta_val = null then\n      new_nil(result);\n    else\n      result := meta_val;\n    end if;\n  end procedure fn_meta;\n\n  procedure fn_with_meta(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n  begin\n    result := new mal_val'(val_type => a0.val_type, number_val => a0.number_val, string_val => a0.string_val, seq_val => a0.seq_val, func_val => a0.func_val, meta_val => args.seq_val(1));\n  end procedure fn_with_meta;\n\n  procedure fn_atom(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    new_atom(args.seq_val(0), result);\n  end procedure fn_atom;\n\n  procedure fn_atom_q(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n  begin\n    new_boolean(a0.val_type = mal_atom, result);\n  end procedure fn_atom_q;\n\n  procedure fn_deref(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n  begin\n    result := a0.seq_val(0);\n  end procedure fn_deref;\n\n  procedure fn_reset(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable a0: mal_val_ptr := args.seq_val(0);\n    variable a1: mal_val_ptr := args.seq_val(1);\n  begin\n    a0.seq_val(0) := a1;\n    result := a1;\n  end procedure fn_reset;\n\n  procedure eval_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable f: line;\n  begin\n    if func_sym.val_type /= mal_nativefn then\n      new_string(\"not a native function!\", err);\n      return;\n    end if;\n    f := func_sym.string_val;\n    if    f.all = \"=\"           then fn_equal(args, result, err);\n    elsif f.all = \"throw\"       then fn_throw(args, result, err);\n    elsif f.all = \"nil?\"        then fn_nil_q(args, result, err);\n    elsif f.all = \"true?\"       then fn_true_q(args, result, err);\n    elsif f.all = \"false?\"      then fn_false_q(args, result, err);\n    elsif f.all = \"string?\"     then fn_string_q(args, result, err);\n    elsif f.all = \"symbol\"      then fn_symbol(args, result, err);\n    elsif f.all = \"symbol?\"     then fn_symbol_q(args, result, err);\n    elsif f.all = \"keyword\"     then fn_keyword(args, result, err);\n    elsif f.all = \"keyword?\"    then fn_keyword_q(args, result, err);\n    elsif f.all = \"number?\"     then fn_number_q(args, result, err);\n    elsif f.all = \"fn?\"         then fn_function_q(args, result, err);\n    elsif f.all = \"macro?\"      then fn_macro_q(args, result, err);\n    elsif f.all = \"pr-str\"      then fn_pr_str(args, result, err);\n    elsif f.all = \"str\"         then fn_str(args, result, err);\n    elsif f.all = \"prn\"         then fn_prn(args, result, err);\n    elsif f.all = \"println\"     then fn_println(args, result, err);\n    elsif f.all = \"read-string\" then fn_read_string(args, result, err);\n    elsif f.all = \"readline\"    then fn_readline(args, result, err);\n    elsif f.all = \"slurp\"       then fn_slurp(args, result, err);\n    elsif f.all = \"<\"           then fn_lt(args, result, err);\n    elsif f.all = \"<=\"          then fn_lte(args, result, err);\n    elsif f.all = \">\"           then fn_gt(args, result, err);\n    elsif f.all = \">=\"          then fn_gte(args, result, err);\n    elsif f.all = \"+\"           then fn_add(args, result, err);\n    elsif f.all = \"-\"           then fn_sub(args, result, err);\n    elsif f.all = \"*\"           then fn_mul(args, result, err);\n    elsif f.all = \"/\"           then fn_div(args, result, err);\n    elsif f.all = \"time-ms\"     then fn_time_ms(args, result, err);\n    elsif f.all = \"list\"        then fn_list(args, result, err);\n    elsif f.all = \"list?\"       then fn_list_q(args, result, err);\n    elsif f.all = \"vector\"      then fn_vector(args, result, err);\n    elsif f.all = \"vector?\"     then fn_vector_q(args, result, err);\n    elsif f.all = \"hash-map\"    then fn_hash_map(args, result, err);\n    elsif f.all = \"map?\"        then fn_map_q(args, result, err);\n    elsif f.all = \"assoc\"       then fn_assoc(args, result, err);\n    elsif f.all = \"dissoc\"      then fn_dissoc(args, result, err);\n    elsif f.all = \"get\"         then fn_get(args, result, err);\n    elsif f.all = \"contains?\"   then fn_contains_q(args, result, err);\n    elsif f.all = \"keys\"        then fn_keys(args, result, err);\n    elsif f.all = \"vals\"        then fn_vals(args, result, err);\n    elsif f.all = \"sequential?\" then fn_sequential_q(args, result, err);\n    elsif f.all = \"cons\"        then fn_cons(args, result, err);\n    elsif f.all = \"concat\"      then fn_concat(args, result, err);\n    elsif f.all = \"vec\"         then fn_vec(args, result, err);\n    elsif f.all = \"nth\"         then fn_nth(args, result, err);\n    elsif f.all = \"first\"       then fn_first(args, result, err);\n    elsif f.all = \"rest\"        then fn_rest(args, result, err);\n    elsif f.all = \"empty?\"      then fn_empty_q(args, result, err);\n    elsif f.all = \"count\"       then fn_count(args, result, err);\n    elsif f.all = \"conj\"        then fn_conj(args, result, err);\n    elsif f.all = \"seq\"         then fn_seq(args, result, err);\n    elsif f.all = \"meta\"        then fn_meta(args, result, err);\n    elsif f.all = \"with-meta\"   then fn_with_meta(args, result, err);\n    elsif f.all = \"atom\"        then fn_atom(args, result, err);\n    elsif f.all = \"atom?\"       then fn_atom_q(args, result, err);\n    elsif f.all = \"deref\"       then fn_deref(args, result, err);\n    elsif f.all = \"reset!\"      then fn_reset(args, result, err);\n    else\n      result := null;\n    end if;\n  end procedure eval_native_func;\n\n  procedure define_core_function(e: inout env_ptr; func_name: in string) is\n    variable sym: mal_val_ptr;\n    variable fn: mal_val_ptr;\n  begin\n    new_symbol(func_name, sym);\n    new_nativefn(func_name, fn);\n    env_set(e, sym, fn);\n  end procedure define_core_function;\n\n  procedure define_core_functions(e: inout env_ptr) is\n  begin\n    define_core_function(e, \"=\");\n    define_core_function(e, \"throw\");\n    define_core_function(e, \"nil?\");\n    define_core_function(e, \"true?\");\n    define_core_function(e, \"false?\");\n    define_core_function(e, \"string?\");\n    define_core_function(e, \"symbol\");\n    define_core_function(e, \"symbol?\");\n    define_core_function(e, \"keyword\");\n    define_core_function(e, \"keyword?\");\n    define_core_function(e, \"number?\");\n    define_core_function(e, \"fn?\");\n    define_core_function(e, \"macro?\");\n    define_core_function(e, \"pr-str\");\n    define_core_function(e, \"str\");\n    define_core_function(e, \"prn\");\n    define_core_function(e, \"println\");\n    define_core_function(e, \"read-string\");\n    define_core_function(e, \"readline\");\n    define_core_function(e, \"slurp\");\n    define_core_function(e, \"<\");\n    define_core_function(e, \"<=\");\n    define_core_function(e, \">\");\n    define_core_function(e, \">=\");\n    define_core_function(e, \"+\");\n    define_core_function(e, \"-\");\n    define_core_function(e, \"*\");\n    define_core_function(e, \"/\");\n    define_core_function(e, \"time-ms\");\n    define_core_function(e, \"list\");\n    define_core_function(e, \"list?\");\n    define_core_function(e, \"vector\");\n    define_core_function(e, \"vector?\");\n    define_core_function(e, \"hash-map\");\n    define_core_function(e, \"map?\");\n    define_core_function(e, \"assoc\");\n    define_core_function(e, \"dissoc\");\n    define_core_function(e, \"get\");\n    define_core_function(e, \"contains?\");\n    define_core_function(e, \"keys\");\n    define_core_function(e, \"vals\");\n    define_core_function(e, \"sequential?\");\n    define_core_function(e, \"cons\");\n    define_core_function(e, \"concat\");\n    define_core_function(e, \"vec\");\n    define_core_function(e, \"nth\");\n    define_core_function(e, \"first\");\n    define_core_function(e, \"rest\");\n    define_core_function(e, \"empty?\");\n    define_core_function(e, \"count\");\n    define_core_function(e, \"apply\");   -- implemented in the stepN_XXX files\n    define_core_function(e, \"map\");     -- implemented in the stepN_XXX files\n    define_core_function(e, \"conj\");\n    define_core_function(e, \"seq\");\n    define_core_function(e, \"meta\");\n    define_core_function(e, \"with-meta\");\n    define_core_function(e, \"atom\");\n    define_core_function(e, \"atom?\");\n    define_core_function(e, \"deref\");\n    define_core_function(e, \"reset!\");\n    define_core_function(e, \"swap!\");   -- implemented in the stepN_XXX files\n  end procedure define_core_functions;\n\nend package body core;\n"
  },
  {
    "path": "impls/vhdl/env.vhdl",
    "content": "library STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.types.all;\n\npackage env is\n  procedure new_env(e: out env_ptr; an_outer: inout env_ptr);\n  procedure new_env(e: out env_ptr; an_outer: inout env_ptr; binds: inout mal_val_ptr; exprs: inout mal_val_ptr);\n  procedure env_set(e: inout env_ptr; key: inout mal_val_ptr; val: inout mal_val_ptr);\n  procedure env_get(e      : inout env_ptr;\n                    key    : inout mal_val_ptr;\n                    result :   out mal_val_ptr);\nend package env;\n\npackage body env is\n  procedure new_env(e: out env_ptr; an_outer: inout env_ptr) is\n    variable null_list: mal_val_ptr;\n  begin\n    null_list := null;\n    new_env(e, an_outer, null_list, null_list);\n  end procedure new_env;\n\n  procedure new_env(e: out env_ptr; an_outer: inout env_ptr; binds: inout mal_val_ptr; exprs: inout mal_val_ptr) is\n    variable the_data, more_exprs: mal_val_ptr;\n    variable i: integer;\n  begin\n    new_empty_hashmap(the_data);\n    if binds /= null then\n      for i in binds.seq_val'range loop\n        if binds.seq_val(i).string_val.all = \"&\" then\n          seq_drop_prefix(exprs, i, more_exprs);\n          hashmap_put(the_data, binds.seq_val(i + 1), more_exprs);\n          exit;\n        else\n          hashmap_put(the_data, binds.seq_val(i), exprs.seq_val(i));\n        end if;\n      end loop;\n    end if;\n    e := new env_record'(outer => an_outer, data => the_data);\n  end procedure new_env;\n\n  procedure env_set(e: inout env_ptr; key: inout mal_val_ptr; val: inout mal_val_ptr) is\n  begin\n    hashmap_put(e.data, key, val);\n  end procedure env_set;\n\n  procedure env_get(e      : inout env_ptr;\n                    key    : inout mal_val_ptr;\n                    result :   out mal_val_ptr)\n  is\n    variable environment : env_ptr     := e;\n    variable val         : mal_val_ptr;\n  begin\n    loop\n      hashmap_get(environment.data, key, val);\n      exit when val /= null;\n      environment := environment.outer;\n      exit when environment = null;\n    end loop;\n    result := val;\n    return;\n  end procedure env_get;\n\nend package body env;\n"
  },
  {
    "path": "impls/vhdl/pkg_readline.vhdl",
    "content": "library STD;\nuse STD.textio.all;\n\npackage pkg_readline is\n  procedure mal_printstr(l: string);\n  procedure mal_printline(l: string);\n  procedure mal_readline(prompt: string; eof_detected: out boolean; l: inout line);\nend package pkg_readline;\n\npackage body pkg_readline is\n  type charfile is file of character;\n  file stdout_char: charfile open write_mode is \"STD_OUTPUT\";\n\n  procedure mal_printstr(l: string) is\n  begin\n    for i in l'range loop\n      write(stdout_char, l(i));\n    end loop;\n  end procedure mal_printstr;\n\n  procedure mal_printline(l: string) is\n  begin\n    mal_printstr(l);\n    write(stdout_char, LF);\n  end procedure mal_printline;\n\n  procedure mal_readline(prompt: string; eof_detected: out boolean; l: inout line) is\n  begin\n    mal_printstr(prompt);\n    if endfile(input) then\n      eof_detected := true;\n    else\n      readline(input, l);\n      eof_detected := false;\n    end if;\n  end procedure mal_readline;\nend package body pkg_readline;\n"
  },
  {
    "path": "impls/vhdl/printer.vhdl",
    "content": "library STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.types.all;\n\npackage printer is\n  procedure pr_str(ast: inout mal_val_ptr; readable: in boolean; result: out line);\n  procedure pr_seq(start_ch: in string; end_ch: in string; delim: in string; a_seq: inout mal_seq_ptr; readable: in boolean; result: out line);\nend package printer;\n\npackage body printer is\n\n  procedure pr_string(val: inout line; readable: in boolean; result: out line) is\n    variable s: line;\n    variable src_i, dst_i: integer;\n  begin\n    if readable then\n      s := new string(1 to val'length * 2);\n      dst_i := 0;\n      for src_i in val'range loop\n        dst_i := dst_i + 1;\n        case val(src_i) is\n          when LF =>\n            s(dst_i) := '\\';\n            dst_i := dst_i + 1;\n            s(dst_i) := 'n';\n          when '\"' =>\n            s(dst_i) := '\\';\n            dst_i := dst_i + 1;\n            s(dst_i) := '\"';\n          when '\\' =>\n            s(dst_i) := '\\';\n            dst_i := dst_i + 1;\n            s(dst_i) := '\\';\n          when others =>\n            s(dst_i) := val(src_i);\n        end case;\n      end loop;\n      result := new string'(\"\" & '\"' & s(1 to dst_i) & '\"');\n      deallocate(s);\n    else\n      result := val;\n    end if;\n  end;\n\n  procedure pr_str(ast: inout mal_val_ptr; readable: in boolean; result: out line) is\n    variable l: line;\n  begin\n    case ast.val_type is\n      when mal_nil =>\n        result := new string'(\"nil\");\n      when mal_true =>\n        result := new string'(\"true\");\n      when mal_false =>\n        result := new string'(\"false\");\n      when mal_number =>\n        write(l, ast.number_val);\n        result := l;\n      when mal_symbol =>\n        result := ast.string_val;\n      when mal_string =>\n        pr_string(ast.string_val, readable, result);\n      when mal_keyword =>\n        result := new string'(\":\" & ast.string_val.all);\n      when mal_list =>\n        pr_seq(\"(\", \")\", \" \", ast.seq_val, readable, result);\n      when mal_vector =>\n        pr_seq(\"[\", \"]\", \" \", ast.seq_val, readable, result);\n      when mal_hashmap =>\n        pr_seq(\"{\", \"}\", \" \", ast.seq_val, readable, result);\n      when mal_atom =>\n        pr_str(ast.seq_val(0), true, l);\n        result := new string'(\"(atom \" & l.all & \")\");\n      when mal_nativefn =>\n        result := new string'(\"#<NativeFunction:\" & ast.string_val.all & \">\");\n      when mal_fn =>\n        result := new string'(\"#<MalFunction>\");\n    end case;\n  end procedure pr_str;\n\n  procedure pr_seq(start_ch: in string; end_ch: in string; delim: in string; a_seq: inout mal_seq_ptr; readable: in boolean; result: out line) is\n    variable s, element_s: line;\n  begin\n    s := new string'(start_ch);\n    for i in a_seq'range loop\n      pr_str(a_seq(i), readable, element_s);\n      if i = 0 then\n        s := new string'(s.all & element_s.all);\n      else\n        s := new string'(s.all & delim & element_s.all);\n      end if;\n    end loop;\n    s := new string'(s.all & end_ch);\n    result := s;\n  end procedure pr_seq;\n\nend package body printer;\n"
  },
  {
    "path": "impls/vhdl/reader.vhdl",
    "content": "library STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.types.all;\n\npackage reader is\n  procedure read_str(s: in string; result: out mal_val_ptr; err: out mal_val_ptr);\nend package reader;\n\npackage body reader is\n\n  type token_list is array(natural range <>) of line;\n  type token_list_ptr is access token_list;\n\n  function is_eol_char(c: in character) return boolean is\n  begin\n    case c is\n      when LF | CR => return true;\n      when others => return false;\n    end case;\n  end function is_eol_char;\n\n  function is_separator_char(c: in character) return boolean is\n  begin\n    case c is\n      when LF | CR | ' ' | '[' | ']' | '{' | '}' | '(' | ')' |\n           ''' | '\"' | '`' | ',' | ';' => return true;\n      when others => return false;\n    end case;\n  end function is_separator_char;\n\n  procedure next_token(str: in string; pos: in positive; token: inout line; next_start_pos: out positive; ok: out boolean) is\n    variable ch: character;\n    variable tmppos: positive;\n  begin\n    token := new string'(\"\");\n    if pos > str'length then\n      ok := false;\n      return;\n    end if;\n\n    ch := str(pos);\n\n    case ch is\n      when ' ' | ',' | LF | CR | HT =>\n        next_start_pos := pos + 1;\n        token := new string'(\"\");\n        ok := true;\n        return;\n\n      when '[' | ']' | '{' | '}' | '(' | ')' | ''' | '`' | '^' | '@' =>\n        next_start_pos := pos + 1;\n        token := new string'(\"\" & ch);\n        ok := true;\n        return;\n\n      when '~' =>\n        if str(pos + 1) = '@' then\n          next_start_pos := pos + 2;\n          token := new string'(\"~@\");\n        else\n          next_start_pos := pos + 1;\n          token := new string'(\"~\");\n        end if;\n        ok := true;\n        return;\n\n      when ';' =>\n        tmppos := pos + 1;\n        while tmppos <= str'length and not is_eol_char(str(tmppos)) loop\n          tmppos := tmppos + 1;\n        end loop;\n        next_start_pos := tmppos;\n        token := new string'(\"\");\n        ok := true;\n        return;\n\n      when '\"' =>\n        tmppos := pos + 1;\n        while tmppos < str'length and str(tmppos) /= '\"' loop\n          if str(tmppos) = '\\' then\n            tmppos := tmppos + 2;\n          else\n            tmppos := tmppos + 1;\n          end if;\n        end loop;\n        if tmppos > str'length then\n          tmppos := tmppos - 1; -- unterminated string, will be caught in unescape_string_token\n        end if;\n        token := new string(1 to (tmppos - pos + 1));\n        token(1 to (tmppos - pos + 1)) := str(pos to tmppos);\n        next_start_pos := tmppos + 1;\n        ok := true;\n        return;\n\n      when others =>\n        tmppos := pos;\n        while tmppos <= str'length and not is_separator_char(str(tmppos)) loop\n          tmppos := tmppos + 1;\n        end loop;\n        token := new string(1 to (tmppos - pos));\n        token(1 to (tmppos - pos)) := str(pos to tmppos - 1);\n        next_start_pos := tmppos;\n        ok := true;\n        return;\n\n    end case;\n\n    ok := false;\n  end procedure next_token;\n\n  function tokenize(str: in string) return token_list_ptr is\n    variable next_pos: positive := 1;\n    variable ok: boolean := true;\n    variable tokens: token_list_ptr;\n    variable t: line;\n  begin\n    while ok loop\n      next_token(str, next_pos, t, next_pos, ok);\n      if t'length > 0 then\n        if tokens = null then\n          tokens := new token_list(0 to 0);\n          tokens(0) := t;\n        else\n          tokens := new token_list'(tokens.all & t);\n        end if;\n      end if;\n    end loop;\n    return tokens;\n  end function tokenize;\n\n  type reader_class is record\n    tokens: token_list_ptr;\n    pos: natural;\n  end record reader_class;\n\n  procedure reader_new(r: inout reader_class; a_tokens: inout token_list_ptr) is\n  begin\n    r := (tokens => a_tokens, pos => 0);\n  end procedure reader_new;\n\n  procedure reader_peek(r: inout reader_class; token: out line) is\n  begin\n    if r.pos < r.tokens'length then\n      token := r.tokens(r.pos);\n    else\n      token := null;\n    end if;\n  end procedure reader_peek;\n\n  procedure reader_next(r: inout reader_class; token: out line) is\n  begin\n    reader_peek(r, token);\n    r.pos := r.pos + 1;\n  end procedure reader_next;\n\n  -- Forward declaration\n  procedure read_form(r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  function is_digit(c: in character) return boolean is\n  begin\n    case c is\n      when '0' to '9' => return true;\n      when others => return false;\n    end case;\n  end function is_digit;\n\n  function unescape_char(c: in character) return character is\n  begin\n    case c is\n      when 'n' => return LF;\n      when others => return c;\n    end case;\n  end function unescape_char;\n\n  procedure unescape_string_token(token: inout line; result: out line) is\n    variable s: line;\n    variable src_i, dst_i: integer;\n  begin\n    s := new string(1 to token'length);\n    dst_i := 0;\n    src_i := 2; -- skip the initial quote\n    while src_i <= token'length - 1 loop\n      dst_i := dst_i + 1;\n      if token(src_i) = '\\' then\n        s(dst_i) := unescape_char(token(src_i + 1));\n        src_i := src_i + 2;\n      else\n        s(dst_i) := token(src_i);\n        src_i := src_i + 1;\n      end if;\n    end loop;\n    if src_i <= token'length then\n      result := new string'(s(1 to dst_i));\n    else\n      result := null;\n    end if;\n    deallocate(s);\n  end procedure unescape_string_token;\n\n  procedure read_atom(r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable token, s: line;\n    variable num: integer;\n    variable ch: character;\n  begin\n    reader_next(r, token);\n    if token.all = \"nil\" then\n      new_nil(result);\n    elsif token.all = \"true\" then\n      new_true(result);\n    elsif token.all = \"false\" then\n      new_false(result);\n    else\n      ch := token(1);\n      case ch is\n        when '-' =>\n          if token'length > 1 and is_digit(token(2)) then\n            read(token, num);\n            new_number(num, result);\n          else\n            new_symbol(token, result);\n          end if;\n        when '0' to '9' =>\n          read(token, num);\n          new_number(num, result);\n        when ':' =>\n          s := new string(1 to token'length - 1);\n          s(1 to s'length) := token(2 to token'length);\n          new_keyword(s, result);\n        when '\"' =>\n          if token(token'length) /= '\"' then\n            new_string(\"expected '\"\"', got EOF\", err);\n            result := null;\n            return;\n          end if;\n          unescape_string_token(token, s);\n          if s = null then\n            new_string(\"expected '\"\"', got EOF\", err);\n            result := null;\n            return;\n          end if;\n          new_string(s, result);\n        when others =>\n          new_symbol(token, result);\n      end case;\n    end if;\n  end procedure read_atom;\n\n  procedure read_sequence(list_type: in mal_type_tag; end_ch: in string; r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable token: line;\n    variable element, sub_err: mal_val_ptr;\n    variable seq: mal_seq_ptr;\n  begin\n    reader_next(r, token); -- Consume the open paren\n    reader_peek(r, token);\n    seq := new mal_seq(0 to -1);\n    while token /= null and token.all /= end_ch loop\n      read_form(r, element, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        result := null;\n        return;\n      end if;\n      seq := new mal_seq'(seq.all & element);\n      reader_peek(r, token);\n    end loop;\n    if token = null then\n      new_string(\"expected '\" & end_ch & \"', got EOF\", err);\n      result := null;\n      return;\n    end if;\n    reader_next(r, token); -- Consume the close paren\n    new_seq_obj(list_type, seq, result);\n  end procedure read_sequence;\n\n  procedure read_map(r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable sub_seq, sub_err, new_map: mal_val_ptr;\n  begin\n    read_sequence(mal_hashmap, \"}\", r, sub_seq, sub_err);\n    if sub_err = null then\n      new_empty_hashmap(new_map);\n      for i in 0 to sub_seq.seq_val'length / 2 - 1 loop\n        hashmap_put(new_map, sub_seq.seq_val(2*i), sub_seq.seq_val(2*i + 1));\n      end loop;\n      result := new_map;\n    else\n      err := sub_err;\n      result := null;\n    end if;\n  end procedure read_map;\n\n  procedure reader_macro(r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr; sym_name: in string) is\n    variable token, sym_line: line;\n    variable seq: mal_seq_ptr;\n    variable rest, rest_err: mal_val_ptr;\n  begin\n    reader_next(r, token);\n    seq := new mal_seq(0 to 1);\n    sym_line := new string'(sym_name);\n    new_symbol(sym_line, seq(0));\n    read_form(r, rest, rest_err);\n    if rest_err /= null then\n      err := rest_err;\n      result := null;\n      return;\n    end if;\n    seq(1) := rest;\n    new_seq_obj(mal_list, seq, result);\n  end procedure reader_macro;\n\n  procedure with_meta_reader_macro(r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable token, sym_line: line;\n    variable seq: mal_seq_ptr;\n    variable meta, rest, rest_err: mal_val_ptr;\n  begin\n    reader_next(r, token);\n    seq := new mal_seq(0 to 2);\n    sym_line := new string'(\"with-meta\");\n    new_symbol(sym_line, seq(0));\n    read_form(r, meta, rest_err);\n    if rest_err /= null then\n      err := rest_err;\n      result := null;\n      return;\n    end if;\n    read_form(r, rest, rest_err);\n    if rest_err /= null then\n      err := rest_err;\n      result := null;\n      return;\n    end if;\n    seq(1) := rest;\n    seq(2) := meta;\n    new_seq_obj(mal_list, seq, result);\n  end procedure with_meta_reader_macro;\n\n  procedure read_form(r: inout reader_class; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable token: line;\n    variable ch: character;\n  begin\n    reader_peek(r, token);\n    ch := token(1);\n    case ch is\n      when ''' => reader_macro(r, result, err, \"quote\");\n      when '`' => reader_macro(r, result, err, \"quasiquote\");\n      when '~' =>\n        if token'length = 1 then\n          reader_macro(r, result, err, \"unquote\");\n        else\n          if token(2) = '@' then\n            reader_macro(r, result, err, \"splice-unquote\");\n          else\n            new_string(\"Unknown token\", err);\n          end if;\n        end if;\n      when '^' => with_meta_reader_macro(r, result, err);\n      when '@' => reader_macro(r, result, err, \"deref\");\n      when '(' => read_sequence(mal_list, \")\", r, result, err);\n      when ')' => new_string(\"unexcepted ')'\", err);\n      when '[' => read_sequence(mal_vector, \"]\", r, result, err);\n      when ']' => new_string(\"unexcepted ']'\", err);\n      when '{' => read_map(r, result, err);\n      when '}' => new_string(\"unexcepted '}'\", err);\n      when others => read_atom(r, result, err);\n    end case;\n  end procedure read_form;\n\n  procedure read_str(s: in string; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable tokens: token_list_ptr;\n    variable r: reader_class;\n  begin\n    tokens := tokenize(s);\n    if tokens = null or tokens'length = 0 then\n      result := null;\n      err := null;\n      return;\n    end if;\n    reader_new(r, tokens);\n    read_form(r, result, err);\n  end procedure read_str;\n\nend package body reader;\n"
  },
  {
    "path": "impls/vhdl/run",
    "content": "#!/usr/bin/env bash\nexec $(dirname $0)/run_vhdl.sh $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/vhdl/run_vhdl.sh",
    "content": "#!/usr/bin/env bash\n\n# ghdl doesn't allow passing command-line arguments to the VHDL program.  To\n# circumvent that, we write the command-line arguments as lines in\n# vhdl_argv.tmp, and read the content of that file at the beginning of the VHDL\n# program.\n\ncleanup() {\n    trap - TERM QUIT INT EXIT\n    rm -f vhdl_argv.tmp\n}\ntrap \"cleanup\" TERM QUIT INT EXIT\n\nbin=\"$1\"\nshift\n\nfor arg in \"$@\" ; do\n    echo \"$arg\"\ndone > vhdl_argv.tmp\n\n$bin\n"
  },
  {
    "path": "impls/vhdl/step0_repl.vhdl",
    "content": "entity step0_repl is\nend entity step0_repl;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\n\narchitecture test of step0_repl is\n  function mal_READ(str: in string) return string is\n  begin\n    return str;\n  end function mal_READ;\n\n  function EVAL(ast: in string; env: in string) return string is\n  begin\n    return ast;\n  end function EVAL;\n\n  function mal_PRINT(exp: in string) return string is\n  begin\n    return exp;\n  end function mal_PRINT;\n\n  function REP(str: in string) return string is\n  begin\n    return mal_PRINT(EVAL(mal_READ(str), \"\"));\n  end function REP;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable input_line: line;\n  begin\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      mal_printline(REP(input_line.all));\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step1_read_print.vhdl",
    "content": "entity step1_read_print is\nend entity step1_read_print;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\n\narchitecture test of step1_read_print is\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  procedure EVAL(ast: inout mal_val_ptr; env: in string; result: out mal_val_ptr) is\n  begin\n    result := ast;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure REP(str: in string; result: out line; err: out mal_val_ptr) is\n    variable ast, eval_res, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, \"\", eval_res);\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable input_line, result: line;\n    variable err: mal_val_ptr;\n  begin\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step2_eval.vhdl",
    "content": "entity step2_eval is\nend entity step2_eval;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\n\narchitecture test of step2_eval is\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  -- Forward declaration\n  procedure EVAL(ast: inout mal_val_ptr; env: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure eval_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr) is\n    variable num_result: integer;\n    variable a: mal_seq_ptr;\n  begin\n    a := args.seq_val;\n    if func_sym.string_val.all = \"+\" then\n      new_number(a(0).number_val + a(1).number_val, result);\n    elsif func_sym.string_val.all = \"-\" then\n      new_number(a(0).number_val - a(1).number_val, result);\n    elsif func_sym.string_val.all = \"*\" then\n      new_number(a(0).number_val * a(1).number_val, result);\n    elsif func_sym.string_val.all = \"/\" then\n      new_number(a(0).number_val / a(1).number_val, result);\n    else\n      result := null;\n    end if;\n  end procedure eval_native_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout mal_val_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(ast    : inout mal_val_ptr;\n                 env    : inout mal_val_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable key, val, eval_err, call_args, sub_err, fn: mal_val_ptr;\n    variable new_seq: mal_seq_ptr;\n    -- variable s: line;\n    variable i: integer;\n  begin\n    -- mal_printstr(\"EVAL: \");\n    -- pr_str(ast, true, s);\n    -- mal_printline(s.all);\n\n    case ast.val_type is\n      when mal_symbol =>\n        new_string(ast.string_val, key);\n        hashmap_get(env, key, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n    if ast.seq_val'length = 0 then\n      result := ast;\n      return;\n    end if;\n\n    EVAL(ast.seq_val(0), env, fn, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    -- Evaluate arguments\n    eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    new_seq_obj(mal_list, new_seq, call_args);\n    eval_native_func(fn, call_args, result);\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure REP(str: in string; env: inout mal_val_ptr; result: out line; err: out mal_val_ptr) is\n    variable ast, eval_res, read_err, eval_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable input_line, result: line;\n    variable repl_seq: mal_seq_ptr;\n    variable repl_env, err: mal_val_ptr;\n  begin\n    repl_seq := new mal_seq(0 to 7);\n    new_string(\"+\", repl_seq(0));\n    new_nativefn(\"+\", repl_seq(1));\n    new_string(\"-\", repl_seq(2));\n    new_nativefn(\"-\", repl_seq(3));\n    new_string(\"*\", repl_seq(4));\n    new_nativefn(\"*\", repl_seq(5));\n    new_string(\"/\", repl_seq(6));\n    new_nativefn(\"/\", repl_seq(7));\n    new_seq_obj(mal_hashmap, repl_seq, repl_env);\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step3_env.vhdl",
    "content": "entity step3_env is\nend entity step3_env;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\n\narchitecture test of step3_env is\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  -- Forward declaration\n  procedure EVAL(ast: inout mal_val_ptr; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure eval_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr) is\n    variable num_result: integer;\n    variable a: mal_seq_ptr;\n  begin\n    a := args.seq_val;\n    if func_sym.string_val.all = \"+\" then\n      new_number(a(0).number_val + a(1).number_val, result);\n    elsif func_sym.string_val.all = \"-\" then\n      new_number(a(0).number_val - a(1).number_val, result);\n    elsif func_sym.string_val.all = \"*\" then\n      new_number(a(0).number_val * a(1).number_val, result);\n    elsif func_sym.string_val.all = \"/\" then\n      new_number(a(0).number_val / a(1).number_val, result);\n    else\n      result := null;\n    end if;\n  end procedure eval_native_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(ast    : inout mal_val_ptr;\n                 env    : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable let_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n    if ast.seq_val'length = 0 then\n      result := ast;\n      return;\n    end if;\n\n    a0 := ast.seq_val(0);\n    if a0.string_val.all = \"def!\" then\n      EVAL(ast.seq_val(2), env, val, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      env_set(env, ast.seq_val(1), val);\n      result := val;\n    elsif a0.string_val.all = \"let*\" then\n      vars := ast.seq_val(1);\n      new_env(let_env, env);\n      i := 0;\n      while i < vars.seq_val'length loop\n        EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n        if sub_err /= null then\n          err := sub_err;\n          deallocate(let_env);\n          return;\n        end if;\n        env_set(let_env, vars.seq_val(i), val);\n        i := i + 2;\n      end loop;\n      EVAL(ast.seq_val(2), let_env, result, err);\n      deallocate(let_env);\n    else\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      eval_native_func(fn, call_args, result);\n    end if;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable ast, eval_res, read_err, eval_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable input_line, result: line;\n    variable sym, fn, err: mal_val_ptr;\n    variable outer, repl_env: env_ptr;\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n    new_symbol(\"+\", sym);\n    new_nativefn(\"+\", fn);\n    env_set(repl_env, sym, fn);\n    new_symbol(\"-\", sym);\n    new_nativefn(\"-\", fn);\n    env_set(repl_env, sym, fn);\n    new_symbol(\"*\", sym);\n    new_nativefn(\"*\", fn);\n    env_set(repl_env, sym, fn);\n    new_symbol(\"/\", sym);\n    new_nativefn(\"/\", fn);\n    env_set(repl_env, sym, fn);\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step4_if_fn_do.vhdl",
    "content": "entity step4_if_fn_do is\nend entity step4_if_fn_do;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of step4_if_fn_do is\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  -- Forward declaration\n  procedure EVAL(ast: inout mal_val_ptr; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(ast    : inout mal_val_ptr;\n                 env    : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable let_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n    if ast.seq_val'length = 0 then\n      result := ast;\n      return;\n    end if;\n\n    a0 := ast.seq_val(0);\n    if a0.val_type = mal_symbol then\n      if a0.string_val.all = \"def!\" then\n        EVAL(ast.seq_val(2), env, val, sub_err);\n        if sub_err /= null then\n          err := sub_err;\n          return;\n        end if;\n        env_set(env, ast.seq_val(1), val);\n        result := val;\n        return;\n\n      elsif a0.string_val.all = \"let*\" then\n        vars := ast.seq_val(1);\n        new_env(let_env, env);\n        i := 0;\n        while i < vars.seq_val'length loop\n          EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(let_env, vars.seq_val(i), val);\n          i := i + 2;\n        end loop;\n        EVAL(ast.seq_val(2), let_env, result, err);\n        return;\n\n      elsif a0.string_val.all = \"do\" then\n        for i in 1 to ast.seq_val'high loop\n          EVAL(ast.seq_val(i), env, result, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n        end loop;\n        return;\n\n      elsif a0.string_val.all = \"if\" then\n        EVAL(ast.seq_val(1), env, val, sub_err);\n        if sub_err /= null then\n          err := sub_err;\n          return;\n        end if;\n        if val.val_type = mal_nil or val.val_type = mal_false then\n          if ast.seq_val'length > 3 then\n            EVAL(ast.seq_val(3), env, result, err);\n          else\n            new_nil(result);\n          end if;\n        else\n          EVAL(ast.seq_val(2), env, result, err);\n        end if;\n        return;\n\n      elsif a0.string_val.all = \"fn*\" then\n        new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n        return;\n\n      end if;\n    end if;\n\n    EVAL (a0, env, fn, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    -- Evaluate arguments\n    eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    new_seq_obj(mal_list, new_seq, call_args);\n    case fn.val_type is\n      when mal_nativefn =>\n        eval_native_func(fn, call_args, result, err);\n      when mal_fn => \n        new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n        EVAL(fn.func_val.f_body, fn_env, result, err);\n      when others =>\n        new_string(\"not a function\", err);\n    end case;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable input_line, result: line;\n    variable dummy_val, err: mal_val_ptr;\n    variable outer, repl_env: env_ptr;\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step5_tco.vhdl",
    "content": "entity step5_tco is\nend entity step5_tco;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of step5_tco is\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  -- Forward declaration\n  procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(in_ast : inout mal_val_ptr;\n                 in_env : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable ast : mal_val_ptr := in_ast;\n    variable env : env_ptr     := in_env;\n    variable let_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    loop\n\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n      if ast.seq_val'length = 0 then\n        result := ast;\n        return;\n      end if;\n\n      a0 := ast.seq_val(0);\n      if a0.val_type = mal_symbol then\n        if a0.string_val.all = \"def!\" then\n          EVAL(ast.seq_val(2), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"let*\" then\n          vars := ast.seq_val(1);\n          new_env(let_env, env);\n          i := 0;\n          while i < vars.seq_val'length loop\n            EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n            env_set(let_env, vars.seq_val(i), val);\n            i := i + 2;\n          end loop;\n          env := let_env;\n          ast := ast.seq_val(2);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"do\" then\n          for i in 1 to ast.seq_val'high - 1 loop\n            EVAL(ast.seq_val(i), env, result, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n          end loop;\n          ast := ast.seq_val(ast.seq_val'high);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"if\" then\n          EVAL(ast.seq_val(1), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          if val.val_type = mal_nil or val.val_type = mal_false then\n            if ast.seq_val'length > 3 then\n              ast := ast.seq_val(3);\n            else\n              new_nil(result);\n              return;\n            end if;\n          else\n            ast := ast.seq_val(2);\n          end if;\n          next; -- TCO\n\n        elsif a0.string_val.all = \"fn*\" then\n          new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n          return;\n\n        end if;\n      end if;\n\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      case fn.val_type is\n        when mal_nativefn =>\n          eval_native_func(fn, call_args, result, err);\n          return;\n        when mal_fn => \n          new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n          env := fn_env;\n          ast := fn.func_val.f_body;\n          next; -- TCO\n        when others =>\n          new_string(\"not a function\", err);\n          return;\n      end case;\n    end loop;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable input_line, result: line;\n    variable dummy_val, err: mal_val_ptr;\n    variable outer, repl_env: env_ptr;\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step6_file.vhdl",
    "content": "entity step6_file is\nend entity step6_file;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of step6_file is\n\n  shared variable repl_env: env_ptr;\n\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  -- Forward declaration\n  procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    EVAL(args.seq_val(0), repl_env, result, err);\n  end procedure fn_eval;\n\n  procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable atom: mal_val_ptr := args.seq_val(0);\n    variable fn: mal_val_ptr := args.seq_val(1);\n    variable call_args_seq: mal_seq_ptr;\n    variable call_args, eval_res, sub_err: mal_val_ptr;\n  begin\n    call_args_seq := new mal_seq(0 to args.seq_val'length - 2);\n    call_args_seq(0) := atom.seq_val(0);\n    call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, eval_res, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    atom.seq_val(0) := eval_res;\n    result := eval_res;\n  end procedure fn_swap;\n\n  procedure apply_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    if func_sym.string_val.all = \"eval\" then\n      fn_eval(args, result, err);\n    elsif func_sym.string_val.all = \"swap!\" then\n      fn_swap(args, result, err);\n    else\n      eval_native_func(func_sym, args, result, err);\n    end if;\n  end procedure apply_native_func;\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn_env: env_ptr;\n  begin\n    case fn.val_type is\n      when mal_nativefn =>\n        apply_native_func(fn, args, result, err);\n      when mal_fn =>\n        new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);\n        EVAL(fn.func_val.f_body, fn_env, result, err);\n      when others =>\n        new_string(\"not a function\", err);\n        return;\n    end case;\n  end procedure apply_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(in_ast : inout mal_val_ptr;\n                 in_env : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable ast : mal_val_ptr := in_ast;\n    variable env : env_ptr     := in_env;\n    variable let_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    loop\n\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n      if ast.seq_val'length = 0 then\n        result := ast;\n        return;\n      end if;\n\n      a0 := ast.seq_val(0);\n      if a0.val_type = mal_symbol then\n        if a0.string_val.all = \"def!\" then\n          EVAL(ast.seq_val(2), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"let*\" then\n          vars := ast.seq_val(1);\n          new_env(let_env, env);\n          i := 0;\n          while i < vars.seq_val'length loop\n            EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n            env_set(let_env, vars.seq_val(i), val);\n            i := i + 2;\n          end loop;\n          env := let_env;\n          ast := ast.seq_val(2);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"do\" then\n          for i in 1 to ast.seq_val'high - 1 loop\n            EVAL(ast.seq_val(i), env, result, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n          end loop;\n          ast := ast.seq_val(ast.seq_val'high);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"if\" then\n          EVAL(ast.seq_val(1), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          if val.val_type = mal_nil or val.val_type = mal_false then\n            if ast.seq_val'length > 3 then\n              ast := ast.seq_val(3);\n            else\n              new_nil(result);\n              return;\n            end if;\n          else\n            ast := ast.seq_val(2);\n          end if;\n          next; -- TCO\n\n        elsif a0.string_val.all = \"fn*\" then\n          new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n          return;\n\n        end if;\n      end if;\n\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      -- Special-case functions for TCO\n      if fn.val_type = mal_fn then\n          new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n          env := fn_env;\n          ast := fn.func_val.f_body;\n          next; -- TCO\n      end if;\n      apply_func(fn, call_args, result, err);\n      return;\n    end loop;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure set_argv(e: inout env_ptr; program_file: inout line) is\n    variable argv_var_name: string(1 to 6) := \"*ARGV*\";\n    variable argv_sym, argv_list: mal_val_ptr;\n    file f: text;\n    variable status: file_open_status;\n    variable one_line: line;\n    variable seq: mal_seq_ptr;\n    variable element: mal_val_ptr;\n  begin\n    program_file := null;\n    seq := new mal_seq(0 to -1);\n    file_open(status, f, external_name => \"vhdl_argv.tmp\", open_kind => read_mode);\n    if status = open_ok then\n      if not endfile(f) then\n        readline(f, program_file);\n        while not endfile(f) loop\n          readline(f, one_line);\n          new_string(one_line.all, element);\n          seq := new mal_seq'(seq.all & element);\n        end loop;\n      end if;\n      file_close(f);\n    end if;\n    new_seq_obj(mal_list, seq, argv_list);\n    new_symbol(argv_var_name, argv_sym);\n    env_set(e, argv_sym, argv_list);\n  end procedure set_argv;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable program_file, input_line, result: line;\n    variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;\n    variable outer: env_ptr;\n    variable eval_func_name: string(1 to 4) := \"eval\";\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n    new_symbol(eval_func_name, eval_sym);\n    new_nativefn(eval_func_name, eval_fn);\n    env_set(repl_env, eval_sym, eval_fn);\n    set_argv(repl_env, program_file);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n    RE(\"(def! load-file (fn* (f) (eval (read-string (str \" & '\"' & \"(do \" & '\"' & \" (slurp f) \" & '\"' & \"\\nnil)\" & '\"' & \")))))\", repl_env, dummy_val, err);\n\n    if program_file /= null then\n      REP(\"(load-file \" & '\"' & program_file.all & '\"' & \")\", repl_env, result, err);\n      return;\n    end if;\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step7_quote.vhdl",
    "content": "entity step7_quote is\nend entity step7_quote;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of step7_quote is\n\n  shared variable repl_env: env_ptr;\n\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  procedure starts_with(lst : inout mal_val_ptr;\n                        sym : in    string;\n                        res :   out boolean) is\n  begin\n    res := lst.seq_val.all'length = 2\n       and lst.seq_val.all (lst.seq_val.all'low).val_type = mal_symbol\n       and lst.seq_val.all (lst.seq_val.all'low).string_val.all = sym;\n  end starts_with;\n\n  -- Forward declaration\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr);\n\n  procedure qq_loop(elt : inout mal_val_ptr;\n                    acc : inout mal_val_ptr) is\n    variable sw  : boolean     := elt.val_type = mal_list;\n    variable seq : mal_seq_ptr := new mal_seq(0 to 2);\n  begin\n    if sw then\n      starts_with(elt, \"splice-unquote\", sw);\n    end if;\n    if sw then\n      new_symbol(\"concat\", seq(0));\n      seq(1) := elt.seq_val(1);\n    else\n      new_symbol(\"cons\", seq(0));\n      quasiquote(elt, seq(1));\n    end if;\n    seq(2) := acc;\n    new_seq_obj(mal_list, seq, acc);\n  end qq_loop;\n\n  procedure qq_foldr (xs  : inout mal_seq_ptr;\n                      res :   out mal_val_ptr) is\n    variable seq : mal_seq_ptr := new mal_seq(0 to -1);\n    variable acc : mal_val_ptr;\n  begin\n    new_seq_obj(mal_list, seq, acc);\n    for i in xs'reverse_range loop\n      qq_loop (xs(i), acc);\n    end loop;\n    res := acc;\n  end procedure qq_foldr;\n\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr) is\n    variable sw  : boolean;\n    variable seq : mal_seq_ptr;\n  begin\n    case ast.val_type is\n      when mal_list =>\n        starts_with(ast, \"unquote\", sw);\n        if sw then\n          result := ast.seq_val(1);\n        else\n          qq_foldr(ast.seq_val, result);\n        end if;\n      when mal_vector =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"vec\", seq(0));\n        qq_foldr(ast.seq_val, seq(1));\n        new_seq_obj(mal_list, seq, result);\n      when mal_symbol | mal_hashmap =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"quote\", seq(0));\n        seq(1) := ast;\n        new_seq_obj(mal_list, seq, result);\n      when others =>\n        result := ast;\n    end case;\n  end procedure quasiquote;\n\n  -- Forward declaration\n  procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    EVAL(args.seq_val(0), repl_env, result, err);\n  end procedure fn_eval;\n\n  procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable atom: mal_val_ptr := args.seq_val(0);\n    variable fn: mal_val_ptr := args.seq_val(1);\n    variable call_args_seq: mal_seq_ptr;\n    variable call_args, eval_res, sub_err: mal_val_ptr;\n  begin\n    call_args_seq := new mal_seq(0 to args.seq_val'length - 2);\n    call_args_seq(0) := atom.seq_val(0);\n    call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, eval_res, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    atom.seq_val(0) := eval_res;\n    result := eval_res;\n  end procedure fn_swap;\n\n  procedure apply_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    if func_sym.string_val.all = \"eval\" then\n      fn_eval(args, result, err);\n    elsif func_sym.string_val.all = \"swap!\" then\n      fn_swap(args, result, err);\n    else\n      eval_native_func(func_sym, args, result, err);\n    end if;\n  end procedure apply_native_func;\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn_env: env_ptr;\n  begin\n    case fn.val_type is\n      when mal_nativefn =>\n        apply_native_func(fn, args, result, err);\n      when mal_fn =>\n        new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);\n        EVAL(fn.func_val.f_body, fn_env, result, err);\n      when others =>\n        new_string(\"not a function\", err);\n        return;\n    end case;\n  end procedure apply_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(in_ast : inout mal_val_ptr;\n                 in_env : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable ast : mal_val_ptr := in_ast;\n    variable env : env_ptr     := in_env;\n    variable let_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    loop\n\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n      if ast.seq_val'length = 0 then\n        result := ast;\n        return;\n      end if;\n\n      a0 := ast.seq_val(0);\n      if a0.val_type = mal_symbol then\n        if a0.string_val.all = \"def!\" then\n          EVAL(ast.seq_val(2), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"let*\" then\n          vars := ast.seq_val(1);\n          new_env(let_env, env);\n          i := 0;\n          while i < vars.seq_val'length loop\n            EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n            env_set(let_env, vars.seq_val(i), val);\n            i := i + 2;\n          end loop;\n          env := let_env;\n          ast := ast.seq_val(2);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"quote\" then\n          result := ast.seq_val(1);\n          return;\n\n        elsif a0.string_val.all = \"quasiquote\" then\n          quasiquote(ast.seq_val(1), ast);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"do\" then\n          for i in 1 to ast.seq_val'high - 1 loop\n            EVAL(ast.seq_val(i), env, result, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n          end loop;\n          ast := ast.seq_val(ast.seq_val'high);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"if\" then\n          EVAL(ast.seq_val(1), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          if val.val_type = mal_nil or val.val_type = mal_false then\n            if ast.seq_val'length > 3 then\n              ast := ast.seq_val(3);\n            else\n              new_nil(result);\n              return;\n            end if;\n          else\n            ast := ast.seq_val(2);\n          end if;\n          next; -- TCO\n\n        elsif a0.string_val.all = \"fn*\" then\n          new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n          return;\n\n        end if;\n      end if;\n\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      -- Special-case functions for TCO\n      if fn.val_type = mal_fn then\n          new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n          env := fn_env;\n          ast := fn.func_val.f_body;\n          next; -- TCO\n      end if;\n      apply_func(fn, call_args, result, err);\n      return;\n    end loop;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure set_argv(e: inout env_ptr; program_file: inout line) is\n    variable argv_var_name: string(1 to 6) := \"*ARGV*\";\n    variable argv_sym, argv_list: mal_val_ptr;\n    file f: text;\n    variable status: file_open_status;\n    variable one_line: line;\n    variable seq: mal_seq_ptr;\n    variable element: mal_val_ptr;\n  begin\n    program_file := null;\n    seq := new mal_seq(0 to -1);\n    file_open(status, f, external_name => \"vhdl_argv.tmp\", open_kind => read_mode);\n    if status = open_ok then\n      if not endfile(f) then\n        readline(f, program_file);\n        while not endfile(f) loop\n          readline(f, one_line);\n          new_string(one_line.all, element);\n          seq := new mal_seq'(seq.all & element);\n        end loop;\n      end if;\n      file_close(f);\n    end if;\n    new_seq_obj(mal_list, seq, argv_list);\n    new_symbol(argv_var_name, argv_sym);\n    env_set(e, argv_sym, argv_list);\n  end procedure set_argv;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable program_file, input_line, result: line;\n    variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;\n    variable outer: env_ptr;\n    variable eval_func_name: string(1 to 4) := \"eval\";\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n    new_symbol(eval_func_name, eval_sym);\n    new_nativefn(eval_func_name, eval_fn);\n    env_set(repl_env, eval_sym, eval_fn);\n    set_argv(repl_env, program_file);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n    RE(\"(def! load-file (fn* (f) (eval (read-string (str \" & '\"' & \"(do \" & '\"' & \" (slurp f) \" & '\"' & \"\\nnil)\" & '\"' & \")))))\", repl_env, dummy_val, err);\n\n    if program_file /= null then\n      REP(\"(load-file \" & '\"' & program_file.all & '\"' & \")\", repl_env, result, err);\n      return;\n    end if;\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step8_macros.vhdl",
    "content": "entity step8_macros is\nend entity step8_macros;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of step8_macros is\n\n  shared variable repl_env: env_ptr;\n\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  procedure starts_with(lst : inout mal_val_ptr;\n                        sym : in    string;\n                        res :   out boolean) is\n  begin\n    res := lst.seq_val.all'length = 2\n       and lst.seq_val.all (lst.seq_val.all'low).val_type = mal_symbol\n       and lst.seq_val.all (lst.seq_val.all'low).string_val.all = sym;\n  end starts_with;\n\n  -- Forward declaration\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr);\n\n  procedure qq_loop(elt : inout mal_val_ptr;\n                    acc : inout mal_val_ptr) is\n    variable sw  : boolean     := elt.val_type = mal_list;\n    variable seq : mal_seq_ptr := new mal_seq(0 to 2);\n  begin\n    if sw then\n      starts_with(elt, \"splice-unquote\", sw);\n    end if;\n    if sw then\n      new_symbol(\"concat\", seq(0));\n      seq(1) := elt.seq_val(1);\n    else\n      new_symbol(\"cons\", seq(0));\n      quasiquote(elt, seq(1));\n    end if;\n    seq(2) := acc;\n    new_seq_obj(mal_list, seq, acc);\n  end qq_loop;\n\n  procedure qq_foldr (xs  : inout mal_seq_ptr;\n                      res :   out mal_val_ptr) is\n    variable seq : mal_seq_ptr := new mal_seq(0 to -1);\n    variable acc : mal_val_ptr;\n  begin\n    new_seq_obj(mal_list, seq, acc);\n    for i in xs'reverse_range loop\n      qq_loop (xs(i), acc);\n    end loop;\n    res := acc;\n  end procedure qq_foldr;\n\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr) is\n    variable sw  : boolean;\n    variable seq : mal_seq_ptr;\n  begin\n    case ast.val_type is\n      when mal_list =>\n        starts_with(ast, \"unquote\", sw);\n        if sw then\n          result := ast.seq_val(1);\n        else\n          qq_foldr(ast.seq_val, result);\n        end if;\n      when mal_vector =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"vec\", seq(0));\n        qq_foldr(ast.seq_val, seq(1));\n        new_seq_obj(mal_list, seq, result);\n      when mal_symbol | mal_hashmap =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"quote\", seq(0));\n        seq(1) := ast;\n        new_seq_obj(mal_list, seq, result);\n      when others =>\n        result := ast;\n    end case;\n  end procedure quasiquote;\n\n  -- Forward declaration\n  procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    EVAL(args.seq_val(0), repl_env, result, err);\n  end procedure fn_eval;\n\n  procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable atom: mal_val_ptr := args.seq_val(0);\n    variable fn: mal_val_ptr := args.seq_val(1);\n    variable call_args_seq: mal_seq_ptr;\n    variable call_args, eval_res, sub_err: mal_val_ptr;\n  begin\n    call_args_seq := new mal_seq(0 to args.seq_val'length - 2);\n    call_args_seq(0) := atom.seq_val(0);\n    call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, eval_res, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    atom.seq_val(0) := eval_res;\n    result := eval_res;\n  end procedure fn_swap;\n\n  procedure apply_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    if func_sym.string_val.all = \"eval\" then\n      fn_eval(args, result, err);\n    elsif func_sym.string_val.all = \"swap!\" then\n      fn_swap(args, result, err);\n    else\n      eval_native_func(func_sym, args, result, err);\n    end if;\n  end procedure apply_native_func;\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn_env: env_ptr;\n  begin\n    case fn.val_type is\n      when mal_nativefn =>\n        apply_native_func(fn, args, result, err);\n      when mal_fn =>\n        new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);\n        EVAL(fn.func_val.f_body, fn_env, result, err);\n      when others =>\n        new_string(\"not a function\", err);\n        return;\n    end case;\n  end procedure apply_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(in_ast : inout mal_val_ptr;\n                 in_env : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable ast : mal_val_ptr := in_ast;\n    variable env : env_ptr     := in_env;\n    variable let_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    loop\n\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n      if ast.seq_val'length = 0 then\n        result := ast;\n        return;\n      end if;\n\n      a0 := ast.seq_val(0);\n      if a0.val_type = mal_symbol then\n        if a0.string_val.all = \"def!\" then\n          EVAL(ast.seq_val(2), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"let*\" then\n          vars := ast.seq_val(1);\n          new_env(let_env, env);\n          i := 0;\n          while i < vars.seq_val'length loop\n            EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n            env_set(let_env, vars.seq_val(i), val);\n            i := i + 2;\n          end loop;\n          env := let_env;\n          ast := ast.seq_val(2);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"quote\" then\n          result := ast.seq_val(1);\n          return;\n\n        elsif a0.string_val.all = \"quasiquote\" then\n          quasiquote(ast.seq_val(1), ast);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"defmacro!\" then\n          EVAL(ast.seq_val(2), env, fn, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          new_fn(fn.func_val.f_body, fn.func_val.f_args, fn.func_val.f_env, val);\n          val.func_val.f_is_macro := true;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"do\" then\n          for i in 1 to ast.seq_val'high - 1 loop\n            EVAL(ast.seq_val(i), env, result, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n          end loop;\n          ast := ast.seq_val(ast.seq_val'high);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"if\" then\n          EVAL(ast.seq_val(1), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          if val.val_type = mal_nil or val.val_type = mal_false then\n            if ast.seq_val'length > 3 then\n              ast := ast.seq_val(3);\n            else\n              new_nil(result);\n              return;\n            end if;\n          else\n            ast := ast.seq_val(2);\n          end if;\n          next; -- TCO\n\n        elsif a0.string_val.all = \"fn*\" then\n          new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n          return;\n\n        end if;\n      end if;\n\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Special-case macros\n      if fn.val_type = mal_fn and fn.func_val.f_is_macro then\n        seq_drop_prefix(ast, 1, call_args);\n        apply_func(fn, call_args, ast, sub_err);\n        if sub_err /= null then\n          err := sub_err;\n          return;\n        end if;\n        next; -- TCO\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      -- Special-case functions for TCO\n      if fn.val_type = mal_fn then\n          new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n          env := fn_env;\n          ast := fn.func_val.f_body;\n          next; -- TCO\n      end if;\n      apply_func(fn, call_args, result, err);\n      return;\n    end loop;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure set_argv(e: inout env_ptr; program_file: inout line) is\n    variable argv_var_name: string(1 to 6) := \"*ARGV*\";\n    variable argv_sym, argv_list: mal_val_ptr;\n    file f: text;\n    variable status: file_open_status;\n    variable one_line: line;\n    variable seq: mal_seq_ptr;\n    variable element: mal_val_ptr;\n  begin\n    program_file := null;\n    seq := new mal_seq(0 to -1);\n    file_open(status, f, external_name => \"vhdl_argv.tmp\", open_kind => read_mode);\n    if status = open_ok then\n      if not endfile(f) then\n        readline(f, program_file);\n        while not endfile(f) loop\n          readline(f, one_line);\n          new_string(one_line.all, element);\n          seq := new mal_seq'(seq.all & element);\n        end loop;\n      end if;\n      file_close(f);\n    end if;\n    new_seq_obj(mal_list, seq, argv_list);\n    new_symbol(argv_var_name, argv_sym);\n    env_set(e, argv_sym, argv_list);\n  end procedure set_argv;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable program_file, input_line, result: line;\n    variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;\n    variable outer: env_ptr;\n    variable eval_func_name: string(1 to 4) := \"eval\";\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n    new_symbol(eval_func_name, eval_sym);\n    new_nativefn(eval_func_name, eval_fn);\n    env_set(repl_env, eval_sym, eval_fn);\n    set_argv(repl_env, program_file);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n    RE(\"(def! load-file (fn* (f) (eval (read-string (str \" & '\"' & \"(do \" & '\"' & \" (slurp f) \" & '\"' & \"\\nnil)\" & '\"' & \")))))\", repl_env, dummy_val, err);\n    RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \" & '\"' & \"odd number of forms to cond\" & '\"' & \")) (cons 'cond (rest (rest xs)))))))\", repl_env, dummy_val, err);\n\n    if program_file /= null then\n      REP(\"(load-file \" & '\"' & program_file.all & '\"' & \")\", repl_env, result, err);\n      return;\n    end if;\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/step9_try.vhdl",
    "content": "entity step9_try is\nend entity step9_try;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of step9_try is\n\n  shared variable repl_env: env_ptr;\n\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  procedure starts_with(lst : inout mal_val_ptr;\n                        sym : in    string;\n                        res :   out boolean) is\n  begin\n    res := lst.seq_val.all'length = 2\n       and lst.seq_val.all (lst.seq_val.all'low).val_type = mal_symbol\n       and lst.seq_val.all (lst.seq_val.all'low).string_val.all = sym;\n  end starts_with;\n\n  -- Forward declaration\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr);\n\n  procedure qq_loop(elt : inout mal_val_ptr;\n                    acc : inout mal_val_ptr) is\n    variable sw  : boolean     := elt.val_type = mal_list;\n    variable seq : mal_seq_ptr := new mal_seq(0 to 2);\n  begin\n    if sw then\n      starts_with(elt, \"splice-unquote\", sw);\n    end if;\n    if sw then\n      new_symbol(\"concat\", seq(0));\n      seq(1) := elt.seq_val(1);\n    else\n      new_symbol(\"cons\", seq(0));\n      quasiquote(elt, seq(1));\n    end if;\n    seq(2) := acc;\n    new_seq_obj(mal_list, seq, acc);\n  end qq_loop;\n\n  procedure qq_foldr (xs  : inout mal_seq_ptr;\n                      res :   out mal_val_ptr) is\n    variable seq : mal_seq_ptr := new mal_seq(0 to -1);\n    variable acc : mal_val_ptr;\n  begin\n    new_seq_obj(mal_list, seq, acc);\n    for i in xs'reverse_range loop\n      qq_loop (xs(i), acc);\n    end loop;\n    res := acc;\n  end procedure qq_foldr;\n\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr) is\n    variable sw  : boolean;\n    variable seq : mal_seq_ptr;\n  begin\n    case ast.val_type is\n      when mal_list =>\n        starts_with(ast, \"unquote\", sw);\n        if sw then\n          result := ast.seq_val(1);\n        else\n          qq_foldr(ast.seq_val, result);\n        end if;\n      when mal_vector =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"vec\", seq(0));\n        qq_foldr(ast.seq_val, seq(1));\n        new_seq_obj(mal_list, seq, result);\n      when mal_symbol | mal_hashmap =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"quote\", seq(0));\n        seq(1) := ast;\n        new_seq_obj(mal_list, seq, result);\n      when others =>\n        result := ast;\n    end case;\n  end procedure quasiquote;\n\n  -- Forward declaration\n  procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    EVAL(args.seq_val(0), repl_env, result, err);\n  end procedure fn_eval;\n\n  procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable atom: mal_val_ptr := args.seq_val(0);\n    variable fn: mal_val_ptr := args.seq_val(1);\n    variable call_args_seq: mal_seq_ptr;\n    variable call_args, eval_res, sub_err: mal_val_ptr;\n  begin\n    call_args_seq := new mal_seq(0 to args.seq_val'length - 2);\n    call_args_seq(0) := atom.seq_val(0);\n    call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, eval_res, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    atom.seq_val(0) := eval_res;\n    result := eval_res;\n  end procedure fn_swap;\n\n  procedure fn_apply(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn: mal_val_ptr := args.seq_val(0);\n    variable rest: mal_val_ptr;\n    variable mid_args_count, rest_args_count: integer;\n    variable call_args: mal_val_ptr;\n    variable call_args_seq: mal_seq_ptr;\n  begin\n    rest := args.seq_val(args.seq_val'high);\n    mid_args_count := args.seq_val'length - 2;\n    rest_args_count := rest.seq_val'length;\n    call_args_seq := new mal_seq(0 to mid_args_count + rest_args_count - 1);\n    call_args_seq(0 to mid_args_count - 1) := args.seq_val(1 to args.seq_val'length - 2);\n    call_args_seq(mid_args_count to call_args_seq'high) := rest.seq_val(rest.seq_val'range);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, result, err);\n  end procedure fn_apply;\n\n  procedure fn_map(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn: mal_val_ptr := args.seq_val(0);\n    variable lst: mal_val_ptr := args.seq_val(1);\n    variable call_args, sub_err: mal_val_ptr;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    new_seq := new mal_seq(lst.seq_val'range);  -- (0 to lst.seq_val.length - 1);\n    for i in new_seq'range loop\n      new_one_element_list(lst.seq_val(i), call_args);\n      apply_func(fn, call_args, new_seq(i), sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n    end loop;\n    new_seq_obj(mal_list, new_seq, result);\n  end procedure fn_map;\n\n  procedure apply_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    if func_sym.string_val.all = \"eval\" then\n      fn_eval(args, result, err);\n    elsif func_sym.string_val.all = \"swap!\" then\n      fn_swap(args, result, err);\n    elsif func_sym.string_val.all = \"apply\" then\n      fn_apply(args, result, err);\n    elsif func_sym.string_val.all = \"map\" then\n      fn_map(args, result, err);\n    else\n      eval_native_func(func_sym, args, result, err);\n    end if;\n  end procedure apply_native_func;\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn_env: env_ptr;\n  begin\n    case fn.val_type is\n      when mal_nativefn =>\n        apply_native_func(fn, args, result, err);\n      when mal_fn =>\n        new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);\n        EVAL(fn.func_val.f_body, fn_env, result, err);\n      when others =>\n        new_string(\"not a function\", err);\n        return;\n    end case;\n  end procedure apply_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(in_ast : inout mal_val_ptr;\n                 in_env : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable ast : mal_val_ptr := in_ast;\n    variable env : env_ptr     := in_env;\n    variable let_env, catch_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    loop\n\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n      if ast.seq_val'length = 0 then\n        result := ast;\n        return;\n      end if;\n\n      a0 := ast.seq_val(0);\n      if a0.val_type = mal_symbol then\n        if a0.string_val.all = \"def!\" then\n          EVAL(ast.seq_val(2), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"let*\" then\n          vars := ast.seq_val(1);\n          new_env(let_env, env);\n          i := 0;\n          while i < vars.seq_val'length loop\n            EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n            env_set(let_env, vars.seq_val(i), val);\n            i := i + 2;\n          end loop;\n          env := let_env;\n          ast := ast.seq_val(2);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"quote\" then\n          result := ast.seq_val(1);\n          return;\n\n        elsif a0.string_val.all = \"quasiquote\" then\n          quasiquote(ast.seq_val(1), ast);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"defmacro!\" then\n          EVAL(ast.seq_val(2), env, fn, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          new_fn(fn.func_val.f_body, fn.func_val.f_args, fn.func_val.f_env, val);\n          val.func_val.f_is_macro := true;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"try*\" then\n          EVAL(ast.seq_val(1), env, result, sub_err);\n          if sub_err /= null then\n            if ast.seq_val'length > 2 and\n               ast.seq_val(2).val_type = mal_list and\n               ast.seq_val(2).seq_val(0).val_type = mal_symbol and\n               ast.seq_val(2).seq_val(0).string_val.all = \"catch*\" then\n              new_one_element_list(ast.seq_val(2).seq_val(1), vars);\n              new_one_element_list(sub_err, call_args);\n              new_env(catch_env, env, vars, call_args);\n              EVAL(ast.seq_val(2).seq_val(2), catch_env, result, err);\n            else\n              err := sub_err;\n              return;\n            end if;\n          end if;\n          return;\n\n        elsif a0.string_val.all = \"do\" then\n          for i in 1 to ast.seq_val'high - 1 loop\n            EVAL(ast.seq_val(i), env, result, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n          end loop;\n          ast := ast.seq_val(ast.seq_val'high);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"if\" then\n          EVAL(ast.seq_val(1), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          if val.val_type = mal_nil or val.val_type = mal_false then\n            if ast.seq_val'length > 3 then\n              ast := ast.seq_val(3);\n            else\n              new_nil(result);\n              return;\n            end if;\n          else\n            ast := ast.seq_val(2);\n          end if;\n          next; -- TCO\n\n        elsif a0.string_val.all = \"fn*\" then\n          new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n          return;\n\n        end if;\n      end if;\n\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Special-case macros\n      if fn.val_type = mal_fn and fn.func_val.f_is_macro then\n        seq_drop_prefix(ast, 1, call_args);\n        apply_func(fn, call_args, ast, sub_err);\n        if sub_err /= null then\n          err := sub_err;\n          return;\n        end if;\n        next; -- TCO\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      -- Special-case functions for TCO\n      if fn.val_type = mal_fn then\n          new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n          env := fn_env;\n          ast := fn.func_val.f_body;\n          next; -- TCO\n      end if;\n      apply_func(fn, call_args, result, err);\n      return;\n    end loop;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure set_argv(e: inout env_ptr; program_file: inout line) is\n    variable argv_var_name: string(1 to 6) := \"*ARGV*\";\n    variable argv_sym, argv_list: mal_val_ptr;\n    file f: text;\n    variable status: file_open_status;\n    variable one_line: line;\n    variable seq: mal_seq_ptr;\n    variable element: mal_val_ptr;\n  begin\n    program_file := null;\n    seq := new mal_seq(0 to -1);\n    file_open(status, f, external_name => \"vhdl_argv.tmp\", open_kind => read_mode);\n    if status = open_ok then\n      if not endfile(f) then\n        readline(f, program_file);\n        while not endfile(f) loop\n          readline(f, one_line);\n          new_string(one_line.all, element);\n          seq := new mal_seq'(seq.all & element);\n        end loop;\n      end if;\n      file_close(f);\n    end if;\n    new_seq_obj(mal_list, seq, argv_list);\n    new_symbol(argv_var_name, argv_sym);\n    env_set(e, argv_sym, argv_list);\n  end procedure set_argv;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable program_file, input_line, result: line;\n    variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;\n    variable outer: env_ptr;\n    variable eval_func_name: string(1 to 4) := \"eval\";\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n    new_symbol(eval_func_name, eval_sym);\n    new_nativefn(eval_func_name, eval_fn);\n    env_set(repl_env, eval_sym, eval_fn);\n    set_argv(repl_env, program_file);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n    RE(\"(def! load-file (fn* (f) (eval (read-string (str \" & '\"' & \"(do \" & '\"' & \" (slurp f) \" & '\"' & \"\\nnil)\" & '\"' & \")))))\", repl_env, dummy_val, err);\n    RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \" & '\"' & \"odd number of forms to cond\" & '\"' & \")) (cons 'cond (rest (rest xs)))))))\", repl_env, dummy_val, err);\n\n    if program_file /= null then\n      REP(\"(load-file \" & '\"' & program_file.all & '\"' & \")\", repl_env, result, err);\n      return;\n    end if;\n\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/stepA_mal.vhdl",
    "content": "entity stepA_mal is\nend entity stepA_mal;\n\nlibrary STD;\nuse STD.textio.all;\nlibrary WORK;\nuse WORK.pkg_readline.all;\nuse WORK.types.all;\nuse WORK.printer.all;\nuse WORK.reader.all;\nuse WORK.env.all;\nuse WORK.core.all;\n\narchitecture test of stepA_mal is\n\n  shared variable repl_env: env_ptr;\n\n  procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    read_str(str, ast, err);\n  end procedure mal_READ;\n\n  procedure starts_with(lst : inout mal_val_ptr;\n                        sym : in    string;\n                        res :   out boolean) is\n  begin\n    res := lst.seq_val.all'length = 2\n       and lst.seq_val.all (lst.seq_val.all'low).val_type = mal_symbol\n       and lst.seq_val.all (lst.seq_val.all'low).string_val.all = sym;\n  end starts_with;\n\n  -- Forward declaration\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr);\n\n  procedure qq_loop(elt : inout mal_val_ptr;\n                    acc : inout mal_val_ptr) is\n    variable sw  : boolean     := elt.val_type = mal_list;\n    variable seq : mal_seq_ptr := new mal_seq(0 to 2);\n  begin\n    if sw then\n      starts_with(elt, \"splice-unquote\", sw);\n    end if;\n    if sw then\n      new_symbol(\"concat\", seq(0));\n      seq(1) := elt.seq_val(1);\n    else\n      new_symbol(\"cons\", seq(0));\n      quasiquote(elt, seq(1));\n    end if;\n    seq(2) := acc;\n    new_seq_obj(mal_list, seq, acc);\n  end qq_loop;\n\n  procedure qq_foldr (xs  : inout mal_seq_ptr;\n                      res :   out mal_val_ptr) is\n    variable seq : mal_seq_ptr := new mal_seq(0 to -1);\n    variable acc : mal_val_ptr;\n  begin\n    new_seq_obj(mal_list, seq, acc);\n    for i in xs'reverse_range loop\n      qq_loop (xs(i), acc);\n    end loop;\n    res := acc;\n  end procedure qq_foldr;\n\n  procedure quasiquote(ast:    inout mal_val_ptr;\n                       result:   out mal_val_ptr) is\n    variable sw  : boolean;\n    variable seq : mal_seq_ptr;\n  begin\n    case ast.val_type is\n      when mal_list =>\n        starts_with(ast, \"unquote\", sw);\n        if sw then\n          result := ast.seq_val(1);\n        else\n          qq_foldr(ast.seq_val, result);\n        end if;\n      when mal_vector =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"vec\", seq(0));\n        qq_foldr(ast.seq_val, seq(1));\n        new_seq_obj(mal_list, seq, result);\n      when mal_symbol | mal_hashmap =>\n        seq := new mal_seq(0 to 1);\n        new_symbol(\"quote\", seq(0));\n        seq(1) := ast;\n        new_seq_obj(mal_list, seq, result);\n      when others =>\n        result := ast;\n    end case;\n  end procedure quasiquote;\n\n  -- Forward declaration\n  procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);\n\n  procedure fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    EVAL(args.seq_val(0), repl_env, result, err);\n  end procedure fn_eval;\n\n  procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable atom: mal_val_ptr := args.seq_val(0);\n    variable fn: mal_val_ptr := args.seq_val(1);\n    variable call_args_seq: mal_seq_ptr;\n    variable call_args, eval_res, sub_err: mal_val_ptr;\n  begin\n    call_args_seq := new mal_seq(0 to args.seq_val'length - 2);\n    call_args_seq(0) := atom.seq_val(0);\n    call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, eval_res, sub_err);\n    if sub_err /= null then\n      err := sub_err;\n      return;\n    end if;\n    atom.seq_val(0) := eval_res;\n    result := eval_res;\n  end procedure fn_swap;\n\n  procedure fn_apply(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn: mal_val_ptr := args.seq_val(0);\n    variable rest: mal_val_ptr;\n    variable mid_args_count, rest_args_count: integer;\n    variable call_args: mal_val_ptr;\n    variable call_args_seq: mal_seq_ptr;\n  begin\n    rest := args.seq_val(args.seq_val'high);\n    mid_args_count := args.seq_val'length - 2;\n    rest_args_count := rest.seq_val'length;\n    call_args_seq := new mal_seq(0 to mid_args_count + rest_args_count - 1);\n    call_args_seq(0 to mid_args_count - 1) := args.seq_val(1 to args.seq_val'length - 2);\n    call_args_seq(mid_args_count to call_args_seq'high) := rest.seq_val(rest.seq_val'range);\n    new_seq_obj(mal_list, call_args_seq, call_args);\n    apply_func(fn, call_args, result, err);\n  end procedure fn_apply;\n\n  procedure fn_map(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn: mal_val_ptr := args.seq_val(0);\n    variable lst: mal_val_ptr := args.seq_val(1);\n    variable call_args, sub_err: mal_val_ptr;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    new_seq := new mal_seq(lst.seq_val'range);  -- (0 to lst.seq_val.length - 1);\n    for i in new_seq'range loop\n      new_one_element_list(lst.seq_val(i), call_args);\n      apply_func(fn, call_args, new_seq(i), sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n    end loop;\n    new_seq_obj(mal_list, new_seq, result);\n  end procedure fn_map;\n\n  procedure apply_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n  begin\n    if func_sym.string_val.all = \"eval\" then\n      fn_eval(args, result, err);\n    elsif func_sym.string_val.all = \"swap!\" then\n      fn_swap(args, result, err);\n    elsif func_sym.string_val.all = \"apply\" then\n      fn_apply(args, result, err);\n    elsif func_sym.string_val.all = \"map\" then\n      fn_map(args, result, err);\n    else\n      eval_native_func(func_sym, args, result, err);\n    end if;\n  end procedure apply_native_func;\n\n  procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable fn_env: env_ptr;\n  begin\n    case fn.val_type is\n      when mal_nativefn =>\n        apply_native_func(fn, args, result, err);\n      when mal_fn =>\n        new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);\n        EVAL(fn.func_val.f_body, fn_env, result, err);\n      when others =>\n        new_string(\"not a function\", err);\n        return;\n    end case;\n  end procedure apply_func;\n\n  procedure eval_ast_seq(ast_seq : inout mal_seq_ptr;\n                         skip    : in    natural;\n                         env     : inout env_ptr;\n                         result  : inout mal_seq_ptr;\n                         err     :   out mal_val_ptr) is\n    variable eval_err: mal_val_ptr;\n  begin\n    result := new mal_seq(0 to ast_seq'length - 1 - skip);\n    for i in result'range loop\n      EVAL(ast_seq(skip + i), env, result(i), eval_err);\n      if eval_err /= null then\n        err := eval_err;\n        return;\n      end if;\n    end loop;\n  end procedure eval_ast_seq;\n\n  procedure EVAL(in_ast : inout mal_val_ptr;\n                 in_env : inout env_ptr;\n                 result :   out mal_val_ptr;\n                 err    :   out mal_val_ptr) is\n    variable val, eval_err, a0, call_args, vars, fn, sub_err: mal_val_ptr;\n    variable ast : mal_val_ptr := in_ast;\n    variable env : env_ptr     := in_env;\n    variable let_env, catch_env, fn_env : env_ptr;\n    variable s: line;\n    variable new_seq: mal_seq_ptr;\n    variable i: integer;\n  begin\n    loop\n\n    new_symbol(\"DEBUG-EVAL\", a0);\n    env_get(env, a0, val);\n    if val /= null and val.val_type /= mal_nil and val.val_type /= mal_false\n    then\n      mal_printstr(\"EVAL: \");\n      pr_str(ast, true, s);\n      mal_printline(s.all);\n    end if;\n\n    case ast.val_type is\n      when mal_symbol =>\n        env_get(env, ast, val);\n        if val = null then\n          new_string(\"'\" & ast.string_val.all & \"' not found\", err);\n          return;\n        end if;\n        result := val;\n        return;\n      when mal_list =>\n        null;\n      when mal_vector | mal_hashmap =>\n        eval_ast_seq(ast.seq_val, 0, env, new_seq, eval_err);\n        if eval_err /= null then\n          err := eval_err;\n          return;\n        end if;\n        new_seq_obj(ast.val_type, new_seq, result);\n        return;\n      when others =>\n        result := ast;\n        return;\n    end case;\n\n      if ast.seq_val'length = 0 then\n        result := ast;\n        return;\n      end if;\n\n      a0 := ast.seq_val(0);\n      if a0.val_type = mal_symbol then\n        if a0.string_val.all = \"def!\" then\n          EVAL(ast.seq_val(2), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"let*\" then\n          vars := ast.seq_val(1);\n          new_env(let_env, env);\n          i := 0;\n          while i < vars.seq_val'length loop\n            EVAL(vars.seq_val(i + 1), let_env, val, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n            env_set(let_env, vars.seq_val(i), val);\n            i := i + 2;\n          end loop;\n          env := let_env;\n          ast := ast.seq_val(2);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"quote\" then\n          result := ast.seq_val(1);\n          return;\n\n        elsif a0.string_val.all = \"quasiquote\" then\n          quasiquote(ast.seq_val(1), ast);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"defmacro!\" then\n          EVAL(ast.seq_val(2), env, fn, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          new_fn(fn.func_val.f_body, fn.func_val.f_args, fn.func_val.f_env, val);\n          val.func_val.f_is_macro := true;\n          env_set(env, ast.seq_val(1), val);\n          result := val;\n          return;\n\n        elsif a0.string_val.all = \"try*\" then\n          EVAL(ast.seq_val(1), env, result, sub_err);\n          if sub_err /= null then\n            if ast.seq_val'length > 2 and\n               ast.seq_val(2).val_type = mal_list and\n               ast.seq_val(2).seq_val(0).val_type = mal_symbol and\n               ast.seq_val(2).seq_val(0).string_val.all = \"catch*\" then\n              new_one_element_list(ast.seq_val(2).seq_val(1), vars);\n              new_one_element_list(sub_err, call_args);\n              new_env(catch_env, env, vars, call_args);\n              EVAL(ast.seq_val(2).seq_val(2), catch_env, result, err);\n            else\n              err := sub_err;\n              return;\n            end if;\n          end if;\n          return;\n\n        elsif a0.string_val.all = \"do\" then\n          for i in 1 to ast.seq_val'high - 1 loop\n            EVAL(ast.seq_val(i), env, result, sub_err);\n            if sub_err /= null then\n              err := sub_err;\n              return;\n            end if;\n          end loop;\n          ast := ast.seq_val(ast.seq_val'high);\n          next; -- TCO\n\n        elsif a0.string_val.all = \"if\" then\n          EVAL(ast.seq_val(1), env, val, sub_err);\n          if sub_err /= null then\n            err := sub_err;\n            return;\n          end if;\n          if val.val_type = mal_nil or val.val_type = mal_false then\n            if ast.seq_val'length > 3 then\n              ast := ast.seq_val(3);\n            else\n              new_nil(result);\n              return;\n            end if;\n          else\n            ast := ast.seq_val(2);\n          end if;\n          next; -- TCO\n\n        elsif a0.string_val.all = \"fn*\" then\n          new_fn(ast.seq_val(2), ast.seq_val(1), env, result);\n          return;\n\n        end if;\n      end if;\n\n      EVAL (a0, env, fn, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      -- Special-case macros\n      if fn.val_type = mal_fn and fn.func_val.f_is_macro then\n        seq_drop_prefix(ast, 1, call_args);\n        apply_func(fn, call_args, ast, sub_err);\n        if sub_err /= null then\n          err := sub_err;\n          return;\n        end if;\n        next; -- TCO\n      end if;\n      -- Evaluate arguments\n      eval_ast_seq(ast.seq_val, 1, env, new_seq, sub_err);\n      if sub_err /= null then\n        err := sub_err;\n        return;\n      end if;\n      new_seq_obj(mal_list, new_seq, call_args);\n      -- Special-case functions for TCO\n      if fn.val_type = mal_fn then\n          new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);\n          env := fn_env;\n          ast := fn.func_val.f_body;\n          next; -- TCO\n      end if;\n      apply_func(fn, call_args, result, err);\n      return;\n    end loop;\n  end procedure EVAL;\n\n  procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is\n  begin\n    pr_str(exp, true, result);\n  end procedure mal_PRINT;\n\n  procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is\n    variable ast, read_err: mal_val_ptr;\n  begin\n    mal_READ(str, ast, read_err);\n    if read_err /= null then\n      err := read_err;\n      result := null;\n      return;\n    end if;\n    if ast = null then\n      result := null;\n      return;\n    end if;\n    EVAL(ast, env, result, err);\n  end procedure RE;\n\n  procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is\n    variable eval_res, eval_err: mal_val_ptr;\n  begin\n    RE(str, env, eval_res, eval_err);\n    if eval_err /= null then\n      err := eval_err;\n      result := null;\n      return;\n    end if;\n    mal_PRINT(eval_res, result);\n  end procedure REP;\n\n  procedure set_argv(e: inout env_ptr; program_file: inout line) is\n    variable argv_var_name: string(1 to 6) := \"*ARGV*\";\n    variable argv_sym, argv_list: mal_val_ptr;\n    file f: text;\n    variable status: file_open_status;\n    variable one_line: line;\n    variable seq: mal_seq_ptr;\n    variable element: mal_val_ptr;\n  begin\n    program_file := null;\n    seq := new mal_seq(0 to -1);\n    file_open(status, f, external_name => \"vhdl_argv.tmp\", open_kind => read_mode);\n    if status = open_ok then\n      if not endfile(f) then\n        readline(f, program_file);\n        while not endfile(f) loop\n          readline(f, one_line);\n          new_string(one_line.all, element);\n          seq := new mal_seq'(seq.all & element);\n        end loop;\n      end if;\n      file_close(f);\n    end if;\n    new_seq_obj(mal_list, seq, argv_list);\n    new_symbol(argv_var_name, argv_sym);\n    env_set(e, argv_sym, argv_list);\n  end procedure set_argv;\n\n  procedure repl is\n    variable is_eof: boolean;\n    variable program_file, input_line, result: line;\n    variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;\n    variable outer: env_ptr;\n    variable eval_func_name: string(1 to 4) := \"eval\";\n  begin\n    outer := null;\n    new_env(repl_env, outer);\n\n    -- core.EXT: defined using VHDL (see core.vhdl)\n    define_core_functions(repl_env);\n    new_symbol(eval_func_name, eval_sym);\n    new_nativefn(eval_func_name, eval_fn);\n    env_set(repl_env, eval_sym, eval_fn);\n    set_argv(repl_env, program_file);\n\n    -- core.mal: defined using the language itself\n    RE(\"(def! *host-language* \" & '\"' & \"vhdl\" & '\"' & \")\", repl_env, dummy_val, err);\n    RE(\"(def! not (fn* (a) (if a false true)))\", repl_env, dummy_val, err);\n    RE(\"(def! load-file (fn* (f) (eval (read-string (str \" & '\"' & \"(do \" & '\"' & \" (slurp f) \" & '\"' & \"\\nnil)\" & '\"' & \")))))\", repl_env, dummy_val, err);\n    RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \" & '\"' & \"odd number of forms to cond\" & '\"' & \")) (cons 'cond (rest (rest xs)))))))\", repl_env, dummy_val, err);\n\n    if program_file /= null then\n      REP(\"(load-file \" & '\"' & program_file.all & '\"' & \")\", repl_env, result, err);\n      return;\n    end if;\n\n    RE(\"(println (str \" & '\"' & \"Mal [\" & '\"' & \" *host-language* \" & '\"' & \"]\" & '\"' & \"))\", repl_env, dummy_val, err);\n    loop\n      mal_readline(\"user> \", is_eof, input_line);\n      exit when is_eof;\n      next when input_line'length = 0;\n      REP(input_line.all, repl_env, result, err);\n      if err /= null then\n        pr_str(err, false, result);\n        result := new string'(\"Error: \" & result.all);\n      end if;\n      if result /= null then\n        mal_printline(result.all);\n      end if;\n      deallocate(result);\n      deallocate(err);\n    end loop;\n    mal_printline(\"\");\n  end procedure repl;\n\nbegin\n  repl;\nend architecture test;\n"
  },
  {
    "path": "impls/vhdl/types.vhdl",
    "content": "library STD;\nuse STD.textio.all;\n\npackage types is\n\n  procedure debugline(l: inout line);\n  procedure debug(str: in string);\n  procedure debug(ch: in character);\n  procedure debug(i: in integer);\n\n  type mal_type_tag is (mal_nil, mal_true, mal_false, mal_number,\n                        mal_symbol, mal_string, mal_keyword,\n                        mal_list, mal_vector, mal_hashmap,\n                        mal_atom, mal_nativefn, mal_fn);\n\n  -- Forward declarations\n  type mal_val;\n  type mal_seq;\n  type mal_func;\n  type env_record;\n\n  type mal_val_ptr is access mal_val;\n  type mal_seq_ptr is access mal_seq;\n  type mal_func_ptr is access mal_func;\n  type env_ptr is access env_record;\n\n  type mal_val is record\n    val_type: mal_type_tag;\n    number_val: integer;    -- For types: number\n    string_val: line;       -- For types: symbol, string, keyword, nativefn\n    seq_val: mal_seq_ptr;   -- For types: list, vector, hashmap, atom\n    func_val: mal_func_ptr; -- For fn\n    meta_val: mal_val_ptr;\n  end record mal_val;\n\n  type mal_seq is array (natural range <>) of mal_val_ptr;\n\n  type mal_func is record\n    f_body: mal_val_ptr;\n    f_args: mal_val_ptr;\n    f_env: env_ptr;\n    f_is_macro: boolean;\n  end record mal_func;\n\n  type env_record is record\n    outer: env_ptr;\n    data: mal_val_ptr;\n  end record env_record;\n\n  procedure new_nil(obj: out mal_val_ptr);\n  procedure new_true(obj: out mal_val_ptr);\n  procedure new_false(obj: out mal_val_ptr);\n  procedure new_boolean(b: in boolean; obj: out mal_val_ptr);\n  procedure new_number(v: in integer; obj: out mal_val_ptr);\n  procedure new_symbol(name: in string; obj: out mal_val_ptr);\n  procedure new_symbol(name: inout line; obj: out mal_val_ptr);\n  procedure new_string(name: in string; obj: out mal_val_ptr);\n  procedure new_string(name: inout line; obj: out mal_val_ptr);\n  procedure new_keyword(name: in string; obj: out mal_val_ptr);\n  procedure new_keyword(name: inout line; obj: out mal_val_ptr);\n  procedure new_nativefn(name: in string; obj: out mal_val_ptr);\n  procedure new_fn(body_ast: inout mal_val_ptr; args: inout mal_val_ptr; env: inout env_ptr; obj: out mal_val_ptr);\n  procedure new_seq_obj(seq_type: in mal_type_tag; seq: inout mal_seq_ptr; obj: out mal_val_ptr);\n  procedure new_one_element_list(val: inout mal_val_ptr; obj: out mal_val_ptr);\n  procedure new_empty_hashmap(obj: out mal_val_ptr);\n  procedure new_atom(val: inout mal_val_ptr; obj: out mal_val_ptr);\n\n  procedure hashmap_copy(hashmap: inout mal_val_ptr; obj: out mal_val_ptr);\n  procedure hashmap_get(hashmap: inout mal_val_ptr; key: inout mal_val_ptr; val: out mal_val_ptr);\n  procedure hashmap_contains(hashmap: inout mal_val_ptr; key: inout mal_val_ptr; ok: out boolean);\n  procedure hashmap_put(hashmap: inout mal_val_ptr; key: inout mal_val_ptr; val: inout mal_val_ptr);\n  procedure hashmap_delete(hashmap: inout mal_val_ptr; key: inout mal_val_ptr);\n  procedure seq_drop_prefix(src: inout mal_val_ptr; prefix_length: in integer; result: out mal_val_ptr);\n  function is_sequential_type(t: in mal_type_tag) return boolean;\n  procedure equal_q(a: inout mal_val_ptr; b: inout mal_val_ptr; result: out boolean);\nend package types;\n\npackage body types is\n\n  procedure debugline(l: inout line) is\n    variable l2: line;\n  begin\n    l2 := new string(1 to 7 + l'length);\n    l2(1 to l2'length) := \"DEBUG: \" & l.all;\n    writeline(output, l2);\n  end procedure debugline;\n\n  procedure debug(str: in string) is\n    variable d: line;\n  begin\n    write(d, str);\n    debugline(d);\n  end procedure debug;\n\n  procedure debug(ch: in character) is\n    variable d: line;\n  begin\n    write(d, ch);\n    debugline(d);\n  end procedure debug;\n\n  procedure debug(i: in integer) is\n    variable d: line;\n  begin\n    write(d, i);\n    debugline(d);\n  end procedure debug;\n\n  procedure new_nil(obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_nil, number_val => 0, string_val => null, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_nil;\n\n  procedure new_true(obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_true, number_val => 0, string_val => null, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_true;\n\n  procedure new_false(obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_false, number_val => 0, string_val => null, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_false;\n\n  procedure new_boolean(b: in boolean; obj: out mal_val_ptr) is\n  begin\n    if b then\n      new_true(obj);\n    else\n      new_false(obj);\n    end if;\n  end procedure new_boolean;\n\n  procedure new_number(v: in integer; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_number, number_val => v, string_val => null, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_number;\n\n  procedure new_symbol(name: in string; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_symbol, number_val => 0, string_val => new string'(name), seq_val => null, func_val => null, meta_val => null);\n  end procedure new_symbol;\n\n  procedure new_symbol(name: inout line; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_symbol, number_val => 0, string_val => name, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_symbol;\n\n  procedure new_string(name: in string; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_string, number_val => 0, string_val => new string'(name), seq_val => null, func_val => null, meta_val => null);\n  end procedure new_string;\n\n  procedure new_string(name: inout line; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_string, number_val => 0, string_val => name, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_string;\n\n  procedure new_keyword(name: in string; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_keyword, number_val => 0, string_val => new string'(name), seq_val => null, func_val => null, meta_val => null);\n  end procedure new_keyword;\n\n  procedure new_keyword(name: inout line; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_keyword, number_val => 0, string_val => name, seq_val => null, func_val => null, meta_val => null);\n  end procedure new_keyword;\n\n  procedure new_nativefn(name: in string; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => mal_nativefn, number_val => 0, string_val => new string'(name), seq_val => null, func_val => null, meta_val => null);\n  end procedure new_nativefn;\n\n  procedure new_fn(body_ast: inout mal_val_ptr; args: inout mal_val_ptr; env: inout env_ptr; obj: out mal_val_ptr) is\n    variable f: mal_func_ptr;\n  begin\n    f := new mal_func'(f_body => body_ast, f_args => args, f_env => env, f_is_macro => false);\n    obj := new mal_val'(val_type => mal_fn, number_val => 0, string_val => null, seq_val => null, func_val => f, meta_val => null);\n  end procedure new_fn;\n\n  procedure new_seq_obj(seq_type: in mal_type_tag; seq: inout mal_seq_ptr; obj: out mal_val_ptr) is\n  begin\n    obj := new mal_val'(val_type => seq_type, number_val => 0, string_val => null, seq_val => seq, func_val => null, meta_val => null);\n  end procedure new_seq_obj;\n\n  procedure new_one_element_list(val: inout mal_val_ptr; obj: out mal_val_ptr) is\n    variable seq: mal_seq_ptr;\n  begin\n    seq := new mal_seq(0 to 0);\n    seq(0) := val;\n    new_seq_obj(mal_list, seq, obj);\n  end procedure new_one_element_list;\n\n  procedure new_empty_hashmap(obj: out mal_val_ptr) is\n    variable seq: mal_seq_ptr;\n  begin\n    seq := new mal_seq(0 to -1);\n    new_seq_obj(mal_hashmap, seq, obj);\n  end procedure new_empty_hashmap;\n\n  procedure new_atom(val: inout mal_val_ptr; obj: out mal_val_ptr) is\n    variable atom_seq: mal_seq_ptr;\n  begin\n    atom_seq := new mal_seq(0 to 0);\n    atom_seq(0) := val;\n    new_seq_obj(mal_atom, atom_seq, obj);\n  end procedure new_atom;\n\n  procedure hashmap_copy(hashmap: inout mal_val_ptr; obj: out mal_val_ptr) is\n    variable new_seq: mal_seq_ptr;\n  begin\n    new_seq := new mal_seq(hashmap.seq_val'range);\n    new_seq(new_seq'range) := hashmap.seq_val(hashmap.seq_val'range);\n    new_seq_obj(mal_hashmap, new_seq, obj);\n  end procedure hashmap_copy;\n\n  procedure hashmap_get(hashmap: inout mal_val_ptr; key: inout mal_val_ptr; val: out mal_val_ptr) is\n    variable i: natural;\n    variable curr_key: mal_val_ptr;\n  begin\n    i := 0;\n    while i < hashmap.seq_val'length loop\n      curr_key := hashmap.seq_val(i);\n      if key.val_type = curr_key.val_type and key.string_val.all = curr_key.string_val.all then\n        val := hashmap.seq_val(i + 1);\n        return;\n      end if;\n      i := i + 2;\n    end loop;\n    val := null;\n  end procedure hashmap_get;\n\n  procedure hashmap_contains(hashmap: inout mal_val_ptr; key: inout mal_val_ptr; ok: out boolean) is\n    variable val: mal_val_ptr;\n  begin\n    hashmap_get(hashmap, key, val);\n    if val = null then\n      ok := false;\n    else\n      ok := true;\n    end if;\n  end procedure hashmap_contains;\n\n  procedure hashmap_put(hashmap: inout mal_val_ptr; key: inout mal_val_ptr; val: inout mal_val_ptr) is\n    variable i: natural;\n    variable curr_key: mal_val_ptr;\n    variable new_seq: mal_seq_ptr;\n  begin\n    i := 0;\n    while i < hashmap.seq_val'length loop\n      curr_key := hashmap.seq_val(i);\n      if key.val_type = curr_key.val_type and key.string_val.all = curr_key.string_val.all then\n        hashmap.seq_val(i + 1) := val;\n        return;\n      end if;\n      i := i + 2;\n    end loop;\n    -- Not found so far, need to extend the seq\n    new_seq := new mal_seq(0 to hashmap.seq_val'length + 1);\n    for i in hashmap.seq_val'range loop\n      new_seq(i) := hashmap.seq_val(i);\n    end loop;\n    new_seq(new_seq'length - 2) := key;\n    new_seq(new_seq'length - 1) := val;\n    deallocate(hashmap.seq_val);\n    hashmap.seq_val := new_seq;\n  end procedure hashmap_put;\n\n  procedure hashmap_delete(hashmap: inout mal_val_ptr; key: inout mal_val_ptr) is\n    variable i, dst_i: natural;\n    variable curr_key: mal_val_ptr;\n    variable new_seq: mal_seq_ptr;\n    variable found: boolean;\n  begin\n    hashmap_contains(hashmap, key, found);\n    if not found then\n      return;\n    end if;\n    i := 0;\n    dst_i := 0;\n    new_seq := new mal_seq(0 to hashmap.seq_val'high - 2);\n    while i < hashmap.seq_val'length loop\n      curr_key := hashmap.seq_val(i);\n      if key.val_type = curr_key.val_type and key.string_val.all = curr_key.string_val.all then\n        i := i + 2;\n      else\n        new_seq(dst_i to dst_i + 1) := hashmap.seq_val(i to i + 1);\n        dst_i := dst_i + 2;\n        i := i + 2;\n      end if;\n    end loop;\n    deallocate(hashmap.seq_val);\n    hashmap.seq_val := new_seq;\n  end procedure hashmap_delete;\n\n  procedure seq_drop_prefix(src: inout mal_val_ptr; prefix_length: in integer; result: out mal_val_ptr) is\n    variable seq: mal_seq_ptr;\n  begin\n    seq := new mal_seq(0 to src.seq_val'length - 1 - prefix_length);\n    for i in seq'range loop\n      seq(i) := src.seq_val(i + prefix_length);\n    end loop;\n    new_seq_obj(src.val_type, seq, result);\n  end procedure seq_drop_prefix;\n\n  function is_sequential_type(t: in mal_type_tag) return boolean is\n  begin\n    return t = mal_list or t = mal_vector;\n  end function is_sequential_type;\n\n  procedure equal_seq_q(a: inout mal_val_ptr; b: inout mal_val_ptr; result: out boolean) is\n    variable i: integer;\n    variable is_element_equal: boolean;\n  begin\n    if a.seq_val'length = b.seq_val'length then\n      for i in a.seq_val'range loop\n        equal_q(a.seq_val(i), b.seq_val(i), is_element_equal);\n        if not is_element_equal then\n          result := false;\n          return;\n        end if;\n      end loop;\n      result := true;\n    else\n      result := false;\n    end if;\n  end procedure equal_seq_q;\n\n  procedure equal_hashmap_q(a: inout mal_val_ptr; b: inout mal_val_ptr; result: out boolean) is\n    variable i: integer;\n    variable is_value_equal: boolean;\n    variable b_val: mal_val_ptr;\n  begin\n    if a.seq_val'length = b.seq_val'length then\n      i := 0;\n      while i < a.seq_val'length loop\n        hashmap_get(b, a.seq_val(i), b_val);\n        if b_val = null then\n          result := false;\n          return;\n        else\n          equal_q(a.seq_val(i + 1), b_val, is_value_equal);\n          if not is_value_equal then\n            result := false;\n            return;\n          end if;\n        end if;\n        i := i + 2;\n      end loop;\n      result := true;\n    else\n      result := false;\n    end if;\n  end procedure equal_hashmap_q;\n\n  procedure equal_q(a: inout mal_val_ptr; b: inout mal_val_ptr; result: out boolean) is\n  begin\n    if is_sequential_type(a.val_type) and is_sequential_type(b.val_type) then\n      equal_seq_q(a, b, result);\n    elsif a.val_type = b.val_type then\n      case a.val_type is\n        when mal_nil | mal_true | mal_false =>\n          result := true;\n        when mal_number =>\n          result := a.number_val = b.number_val;\n        when mal_symbol | mal_string | mal_keyword =>\n          result := a.string_val.all = b.string_val.all;\n        when mal_hashmap =>\n          equal_hashmap_q(a, b, result);\n        when mal_atom =>\n          equal_q(a.seq_val(0), b.seq_val(0), result);\n        when others =>\n          result := false;\n      end case;\n    else\n      result := false;\n    end if;\n  end procedure equal_q;\nend package body types;\n"
  },
  {
    "path": "impls/vimscript/.gitignore",
    "content": "/*.o\n/*.so\n"
  },
  {
    "path": "impls/vimscript/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n# To build the readline plugin\nRUN apt-get -y install gcc libreadline-dev\n\nRUN apt-get -y install vim\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/vimscript/Makefile",
    "content": "SOURCES_BASE = readline.vim types.vim reader.vim printer.vim\nSOURCES_LISP = env.vim core.vim stepA_mal.vim\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\nall: libvimextras.so\n\ndist: mal.vim mal\n\nmal.vim: $(SOURCES)\n\tcat $+ | grep -v \"^source \" > $@\n\nmal: mal.vim\n\techo \"#!/bin/sh\" > $@\n\techo \"\\\":\\\" ; rundir=\\`dirname \\$$0\\`\" >> $@\n\techo \"\\\":\\\" ; export LD_LIBRARY_PATH=\\`readlink -f \\$$rundir\\`\" >> $@\n\techo \"\\\":\\\" ; exec vim -i NONE -V1 -nNesS \\\"\\$$0\\\" -- \\\"\\$$@\\\" 2>/dev/null\" >> $@\n\tcat $< >> $@\n\tchmod +x $@\n\n\nlibvimextras.so: vimextras.o\n\t$(CC) -g -shared -o $@ $< -lreadline\n\nvimextras.o: vimextras.c\n\t$(CC) -g -fPIC -c $< -o $@\n\nclean:\n\trm -f vimextras.o libvimextras.so mal.vim mal\n\n.PHONY: clean\n"
  },
  {
    "path": "impls/vimscript/core.vim",
    "content": "\" core module\n\nfunction MalAssoc(args)\n  let hash = copy(a:args[0].val)\n  let new_elements = HashBuild(a:args[1:])\n  call extend(hash, new_elements.val)\n  return HashNew(hash)\nendfunction\n\nfunction MalDissoc(args)\n  let hash = copy(a:args[0].val)\n  for keyobj in a:args[1:]\n    let key = HashMakeKey(keyobj)\n    if has_key(hash, key)\n      call remove(hash, key)\n    endif\n  endfor\n  return HashNew(hash)\nendfunction\n\nfunction MalGet(args)\n  if !HashQ(a:args[0])\n    return g:MalNil\n  endif\n  let hash = a:args[0].val\n  let key = HashMakeKey(a:args[1])\n  return get(hash, key, g:MalNil)\nendfunction\n\nfunction MalContainsQ(args)\n  if !HashQ(a:args[0])\n    return FalseNew()\n  endif\n  let hash = a:args[0].val\n  let key = HashMakeKey(a:args[1])\n  return BoolNew(has_key(hash, key))\nendfunction\n\nfunction MalKeys(args)\n  let listobjs = []\n  for keyname in keys(a:args[0].val)\n    let keyobj = HashParseKey(keyname)\n    call add(listobjs, keyobj)\n  endfor\n  return ListNew(listobjs)\nendfunction\n\nfunction MalReadLine(args)\n  let [eof, line] = Readline(a:args[0].val)\n  return eof ? g:MalNil : StringNew(line)\nendfunction\n\nfunction MalCons(args)\n  let items = copy(a:args[1].val)\n  call insert(items, a:args[0])\n  return ListNew(items)\nendfunction\n\nfunction MalConcat(args)\n  let res = []\n  for list in a:args\n    let res = res + list.val\n  endfor\n  return ListNew(res)\nendfunction\n\nfunction MalApply(args)\n  let funcobj = a:args[0]\n  let rest = a:args[1:]\n  if len(rest) == 0\n    let funcargs = []\n  elseif len(rest) == 1\n    let funcargs = rest[-1].val\n  else\n    let funcargs = rest[:-2] + rest[-1].val\n  endif\n  if NativeFunctionQ(funcobj)\n    return NativeFuncInvoke(funcobj, ListNew(funcargs))\n  elseif FunctionQ(funcobj) || MacroQ(funcobj)\n    return FuncInvoke(funcobj, ListNew(funcargs))\n  else\n    throw \"Not a function\"\n  endif\nendfunction\n\nfunction MalMap(args)\n  let funcobj = a:args[0]\n  let res = []\n  for item in a:args[1].val\n    unlet! mappeditem\n    if NativeFunctionQ(funcobj)\n      let mappeditem = NativeFuncInvoke(funcobj, ListNew([item]))\n    elseif FunctionQ(funcobj)\n      let mappeditem = FuncInvoke(funcobj, ListNew([item]))\n    else\n      throw \"Not a function\"\n    endif\n    call add(res, mappeditem)\n  endfor\n  return ListNew(res)\nendfunction\n\nfunction MalThrow(args)\n  unlet! g:MalExceptionObj\n  let g:MalExceptionObj = a:args[0]\n  throw \"__MalException__\"\nendfunction\n\nfunction ConjList(list, elements)\n  let newlist = a:list\n  for e in a:elements\n    let newlist = MalCons([e, newlist])\n  endfor\n  return newlist\nendfunction\n\nfunction ConjVector(vector, elements)\n  let items = copy(a:vector.val)\n  for e in a:elements\n    call add(items, e)\n  endfor\n  return VectorNew(items)\nendfunction\n\nfunction MalConj(args)\n  if ListQ(a:args[0])\n    return ConjList(a:args[0], a:args[1:])\n  elseif VectorQ(a:args[0])\n    return ConjVector(a:args[0], a:args[1:])\n  endif\nendfunction\n\nfunction MalSeq(args)\n  let obj = a:args[0]\n  if EmptyQ(obj)\n    return g:MalNil\n  elseif ListQ(obj)\n    return obj\n  elseif VectorQ(obj)\n    return ListNew(obj.val)\n  elseif StringQ(obj)\n    return ListNew(map(split(obj.val, '\\zs'), {_, c -> StringNew(c)}))\n  endif\n  throw \"seq requires string or list or vector or nil\"\nendfunction\n\nfunction VimToMal(e)\n  if type(a:e) == type(0)\n    return IntegerNew(a:e)\n  elseif type(a:e) == type(0.0)\n    return FloatNew(a:e)\n  elseif type(a:e) == type(\"\")\n    return StringNew(a:e)\n  elseif type(a:e) == type([])\n    let res = []\n    for v in a:e\n      call add(res, VimToMal(v))\n    endfor\n    return ListNew(res)\n  elseif type(a:e) == type({})\n    let res = {}\n    for [k,v] in items(a:e)\n      let keystring = HashMakeKey(StringNew(k))\n      let res[keystring] = VimToMal(v)\n    endfor\n    return HashNew(res)\n  else\n    return g:MalNil\n  endif\nendfunction\n\nlet CoreNs = {\n  \\ \"=\":           NewNativeFnLambda({a -> BoolNew(EqualQ(a[0], a[1]))}),\n  \\ \"<\":           NewNativeFnLambda({a -> BoolNew(a[0].val < a[1].val)}),\n  \\ \"<=\":          NewNativeFnLambda({a -> BoolNew(a[0].val <= a[1].val)}),\n  \\ \">\":           NewNativeFnLambda({a -> BoolNew(a[0].val > a[1].val)}),\n  \\ \">=\":          NewNativeFnLambda({a -> BoolNew(a[0].val >= a[1].val)}),\n  \\ \"+\":           NewNativeFnLambda({a -> IntegerNew(a[0].val + a[1].val)}),\n  \\ \"-\":           NewNativeFnLambda({a -> IntegerNew(a[0].val - a[1].val)}),\n  \\ \"*\":           NewNativeFnLambda({a -> IntegerNew(a[0].val * a[1].val)}),\n  \\ \"/\":           NewNativeFnLambda({a -> IntegerNew(a[0].val / a[1].val)}),\n  \\ \"time-ms\":     NewNativeFnLambda({a -> IntegerNew(libcallnr(\"libvimextras.so\", \"vimtimems\", 0))}),\n  \\ \"nil?\":        NewNativeFnLambda({a -> BoolNew(NilQ(a[0]))}),\n  \\ \"true?\":       NewNativeFnLambda({a -> BoolNew(TrueQ(a[0]))}),\n  \\ \"false?\":      NewNativeFnLambda({a -> BoolNew(FalseQ(a[0]))}),\n  \\ \"symbol\":      NewNativeFnLambda({a -> SymbolNew(a[0].val)}),\n  \\ \"symbol?\":     NewNativeFnLambda({a -> BoolNew(SymbolQ(a[0]))}),\n  \\ \"string?\":     NewNativeFnLambda({a -> BoolNew(StringQ(a[0]))}),\n  \\ \"keyword\":     NewNativeFnLambda({a -> KeywordNew(a[0].val)}),\n  \\ \"keyword?\":    NewNativeFnLambda({a -> BoolNew(KeywordQ(a[0]))}),\n  \\ \"number?\":     NewNativeFnLambda({a -> BoolNew(IntegerQ(a[0]))}),\n  \\ \"fn?\":         NewNativeFnLambda({a -> BoolNew(NativeFunctionQ(a[0]) || FunctionQ(a[0]))}),\n  \\ \"macro?\":      NewNativeFnLambda({a -> BoolNew(MacroQ(a[0]))}),\n  \\ \"list\":        NewNativeFnLambda({a -> ListNew(a)}),\n  \\ \"list?\":       NewNativeFnLambda({a -> BoolNew(ListQ(a[0]))}),\n  \\ \"vector\":      NewNativeFnLambda({a -> VectorNew(a)}),\n  \\ \"vector?\":     NewNativeFnLambda({a -> BoolNew(VectorQ(a[0]))}),\n  \\ \"sequential?\": NewNativeFnLambda({a -> BoolNew(SequentialQ(a[0]))}),\n  \\ \"hash-map\":    NewNativeFnLambda({a -> HashBuild(a)}),\n  \\ \"map?\":        NewNativeFnLambda({a -> BoolNew(HashQ(a[0]))}),\n  \\ \"empty?\":      NewNativeFnLambda({a -> BoolNew(EmptyQ(a[0]))}),\n  \\ \"count\":       NewNativeFnLambda({a -> IntegerNew(ListCount(a[0]))}),\n  \\ \"assoc\":       NewNativeFn(\"MalAssoc\"),\n  \\ \"dissoc\":      NewNativeFn(\"MalDissoc\"),\n  \\ \"get\":         NewNativeFn(\"MalGet\"),\n  \\ \"contains?\":   NewNativeFn(\"MalContainsQ\"),\n  \\ \"keys\":        NewNativeFn(\"MalKeys\"),\n  \\ \"vals\":        NewNativeFnLambda({a -> ListNew(values(a[0].val))}),\n  \\ \"pr-str\":      NewNativeFnLambda({a -> StringNew(join(map(copy(a), {_, e -> PrStr(e, 1)}), \" \"))}),\n  \\ \"str\":         NewNativeFnLambda({a -> StringNew(join(map(copy(a), {_, e -> PrStr(e, 0)}), \"\"))}),\n  \\ \"prn\":         NewNativeFnLambda({a -> [PrintLn(join(map(copy(a), {_, e -> PrStr(e, 1)}), \" \")), g:MalNil][1]}),\n  \\ \"println\":     NewNativeFnLambda({a -> [PrintLn(join(map(copy(a), {_, e -> PrStr(e, 0)}), \" \")), g:MalNil][1]}),\n  \\ \"read-string\": NewNativeFnLambda({a -> ReadStr(a[0].val)}),\n  \\ \"readline\":    NewNativeFn(\"MalReadLine\"),\n  \\ \"slurp\":       NewNativeFnLambda({a -> StringNew(join(readfile(a[0].val, \"b\"), \"\\n\"))}),\n  \\ \"cons\":        NewNativeFn(\"MalCons\"),\n  \\ \"concat\":      NewNativeFn(\"MalConcat\"),\n  \\ \"vec\":         NewNativeFnLambda({a -> VectorNew(a[0].val)}),\n  \\ \"first\":       NewNativeFnLambda({a -> NilQ(a[0]) ? g:MalNil : ListFirst(a[0])}),\n  \\ \"nth\":         NewNativeFnLambda({a -> ListNth(a[0], a[1].val)}),\n  \\ \"rest\":        NewNativeFnLambda({a -> NilQ(a[0]) ? ListNew([]) : ListRest(a[0])}),\n  \\ \"apply\":       NewNativeFn(\"MalApply\"),\n  \\ \"map\":         NewNativeFn(\"MalMap\"),\n  \\ \"throw\":       NewNativeFn(\"MalThrow\"),\n  \\ \"conj\":        NewNativeFn(\"MalConj\"),\n  \\ \"seq\":         NewNativeFn(\"MalSeq\"),\n  \\ \"meta\":        NewNativeFnLambda({a -> ObjMeta(a[0])}),\n  \\ \"with-meta\":   NewNativeFnLambda({a -> ObjNewWithMeta(a[0].type, copy(a[0].val), a[1])}),\n  \\ \"atom\":        NewNativeFnLambda({a -> AtomNew(a[0])}),\n  \\ \"atom?\":       NewNativeFnLambda({a -> BoolNew(AtomQ(a[0]))}),\n  \\ \"deref\":       NewNativeFnLambda({a -> a[0].val}),\n  \\ \"reset!\":      NewNativeFnLambda({a -> ObjSetValue(a[0], a[1])}),\n  \\ \"swap!\":       NewNativeFnLambda({a -> ObjSetValue(a[0], MalApply([a[1], ListNew([a[0].val] + a[2:])]))}),\n  \\ \"vim*\":        NewNativeFnLambda({a -> VimToMal(eval(a[0].val))})\n  \\ }\n"
  },
  {
    "path": "impls/vimscript/env.vim",
    "content": "\" env module\n\nlet Env = {}\n\nfunction NewEnv(outer)\n  let e = copy(g:Env)\n  let e.data = {}\n  let e.outer = a:outer\n  return e\nendfunction\n\nfunction NewEnvWithBinds(outer, binds, exprs)\n  let env = NewEnv(a:outer)\n  let i = 0\n  while i < ListCount(a:binds)\n    let varname = ListNth(a:binds, i).val\n    if varname == \"&\"\n      let restvarname = ListNth(a:binds, i + 1).val\n      let restvarvalues = ListDrop(a:exprs, i)\n      call env.set(restvarname, restvarvalues)\n      break\n    else\n      unlet! varvalue\n      let varvalue = ListNth(a:exprs, i)\n      call env.set(varname, varvalue)\n    endif\n    let i = i + 1\n  endwhile\n  return env\nendfunction\n\nfunction Env.set(key, value) dict\n  let self.data[a:key] = a:value\n  return a:value\nendfunction\n\nfunction Env.get(key) dict\n  let curr = self\n  while !has_key(curr.data, a:key)\n    let curr = curr.outer\n    if empty(curr)\n      return \"\"\n    endif\n  endwhile\n  return curr.data[a:key]\nendfunction\n\nfunction Env.root() dict\n  let curr = self\n  while !empty(curr.outer)\n    let curr = curr.outer\n  endwhile\n  return curr\nendfunction\n"
  },
  {
    "path": "impls/vimscript/printer.vim",
    "content": "\" printer module\n\nfunction PrStr(ast, readable)\n  let obj = a:ast\n  let r = a:readable\n  if ListQ(obj)\n    let ret = []\n    for e in obj.val\n      call add(ret, PrStr(e, r))\n    endfor\n    return \"(\" . join(ret, \" \") . \")\"\n  elseif VectorQ(obj)\n    let ret = []\n    for e in obj.val\n      call add(ret, PrStr(e, r))\n    endfor\n    return \"[\" . join(ret, \" \") . \"]\"\n  elseif HashQ(obj)\n    let ret = []\n    for [k, v] in items(obj.val)\n      let keyobj = HashParseKey(k)\n      call add(ret, PrStr(keyobj, r))\n      call add(ret, PrStr(v, r))\n    endfor\n    return \"{\" . join(ret, \" \") . \"}\"\n  elseif MacroQ(obj)\n    let numargs = ListCount(obj.val.params)\n    return \"<Macro:\" . numargs . \"-arguments>\"\n  elseif FunctionQ(obj)\n    let numargs = ListCount(obj.val.params)\n    return \"<Function:\" . numargs . \"-arguments>\"\n  elseif NativeFunctionQ(obj)\n    let funcname = obj.val.name\n    return \"<NativeFunction:\" . funcname . \">\"\n  elseif AtomQ(obj)\n    return \"(atom \" . PrStr(obj.val, 1) . \")\"\n  elseif KeywordQ(obj)\n    return ':' . obj.val\n  elseif StringQ(obj)\n    if r\n      let str = obj.val\n      let str = substitute(str, '\\\\', '\\\\\\\\', \"g\")\n      let str = substitute(str, '\"', '\\\\\"', \"g\")\n      let str = substitute(str, \"\\n\", '\\\\n', \"g\")\n      return '\"' . str . '\"'\n    else\n      return obj.val\n    endif\n  elseif NilQ(obj)\n    return \"nil\"\n  elseif TrueQ(obj)\n    return \"true\"\n  elseif FalseQ(obj)\n    return \"false\"\n  elseif IntegerQ(obj) || FloatQ(obj)\n    return string(obj.val)\n  else\n    return obj.val\n  end\nendfunction\n"
  },
  {
    "path": "impls/vimscript/reader.vim",
    "content": "\" reader module\n\nlet Reader = {}\n\nfunction NewReader(tokens)\n  let r = copy(g:Reader)\n  let r.tokens = a:tokens\n  let r.pos = 0\n  return r\nendfunction\n\nfunction Reader.peek() dict\n  return self.tokens[self.pos]\nendfunction\n\nfunction Reader.nexttoken() dict\n  let self.pos = self.pos + 1\n  return self.tokens[self.pos - 1]\nendfunction\n\nfunction Tokenize(str)\n  let tokenize_pat = \"[[:blank:]\\\\n,]*\" .\n                   \\ \"\\\\(\" .\n                   \\   \"\\\\~@\\\\|\" .\n                   \\   \"[\\\\[\\\\]{}()'`~^@]\\\\|\" .\n                   \\   \"\\\"\\\\%(\\\\\\\\.\\\\|[^\\\\\\\\\\\"]\\\\)*\\\"\\\\|\" .\n                   \\   \"\\\"\\\\%(\\\\\\\\.\\\\|[^\\\\\\\\\\\"]\\\\)*\\\\|\" .\n                   \\   \";[^\\\\n]*\\\\|\" .\n                   \\   \"[^[:blank:]\\\\n\\\\[\\\\]{}('\\\"`,;)]*\" .\n                   \\ \"\\\\)\"\n  let tokens = []\n  let pos = 0\n  while 1\n    let mat = matchlist(a:str, tokenize_pat, pos)\n    if len(mat) == 0 || mat[0] == \"\"\n      break\n    endif\n    if mat[1] != \"\" && mat[1][0] != \";\"\n      call add(tokens, mat[1])\n    endif\n    let pos = matchend(a:str, tokenize_pat, pos)\n  endwhile\n  return tokens\nendfunction\n\nfunction UnescapeChar(seq)\n  if a:seq == '\\\"'\n    return '\"'\n  elseif a:seq == '\\n'\n    return \"\\n\"\n  elseif a:seq == '\\\\'\n    return '\\'\n  else\n    return a:seq\n  endif\nendfunction\n\nfunction ParseString(token)\n  return substitute(a:token[1:-2], '\\\\.', '\\=UnescapeChar(submatch(0))', \"g\")\nendfunction\n\nfunction ReadAtom(rdr)\n  let token = a:rdr.nexttoken()\n  if token =~ \"^-\\\\?[0-9]\\\\+$\"\n    return IntegerNew(str2nr(token))\n  elseif token =~ \"^-\\\\?[0-9][0-9.]*$\"\n    return FloatNew(str2float(token))\n  elseif token =~ \"^\\\"\\\\%(\\\\\\\\.\\\\|[^\\\\\\\\\\\"]\\\\)*\\\"$\"\n    return StringNew(ParseString(token))\n  elseif token =~ \"^\\\".*$\"\n    throw \"expected '\\\"', got EOF\"\n  elseif token =~ \"^:\"\n    return KeywordNew(token[1:-1])\n  elseif token == \"nil\"\n    return g:MalNil\n  elseif token == \"true\"\n    return TrueNew()\n  elseif token == \"false\"\n    return FalseNew()\n  else\n    return SymbolNew(token)\n  endif\nendfunction\n\nfunction ReadTokensList(rdr, start, last)\n  let elements = []\n  let token = a:rdr.nexttoken()\n  if token != a:start\n    throw \"expected '\" . a:start . \"'\"\n  endif\n  let token = a:rdr.peek()\n  while token != a:last\n    call add(elements, ReadForm(a:rdr))\n    try\n      let token = a:rdr.peek()\n    catch\n      throw \"expected '\" . a:last . \"', got EOF\"\n    endtry\n  endwhile\n  call a:rdr.nexttoken()\n  return elements\nendfunction\n\nfunction ReadList(rdr)\n  let elements = ReadTokensList(a:rdr, \"(\", \")\")\n  return ListNew(elements)\nendfunction\n\nfunction ReadVector(rdr)\n  let elements = ReadTokensList(a:rdr, \"[\", \"]\")\n  return VectorNew(elements)\nendfunction\n\nfunction ReadHash(rdr)\n  let elements = ReadTokensList(a:rdr, \"{\", \"}\")\n  return HashBuild(elements)\nendfunction\n\nfunction ReadForm(rdr)\n  let token = a:rdr.peek()\n  if token == \";\"\n    return \"\"\n  elseif token == \"'\"\n    call a:rdr.nexttoken()\n    return ListNew([SymbolNew(\"quote\"), ReadForm(a:rdr)])\n  elseif token == \"`\"\n    call a:rdr.nexttoken()\n    return ListNew([SymbolNew(\"quasiquote\"), ReadForm(a:rdr)])\n  elseif token == \"~\"\n    call a:rdr.nexttoken()\n    return ListNew([SymbolNew(\"unquote\"), ReadForm(a:rdr)])\n  elseif token == \"~@\"\n    call a:rdr.nexttoken()\n    return ListNew([SymbolNew(\"splice-unquote\"), ReadForm(a:rdr)])\n  elseif token == \"^\"\n    call a:rdr.nexttoken()\n    let meta = ReadForm(a:rdr)\n    return ListNew([SymbolNew(\"with-meta\"), ReadForm(a:rdr), meta])\n  elseif token == \"@\"\n    call a:rdr.nexttoken()\n    return ListNew([SymbolNew(\"deref\"), ReadForm(a:rdr)])\n  elseif token == \"(\"\n    return ReadList(a:rdr)\")\n  elseif token == \")\"\n    throw \"unexpected ')'\"\n  elseif token == \"[\"\n    return ReadVector(a:rdr)\n  elseif token == \"]\"\n    throw \"unexpected ']'\"\n  elseif token == \"{\"\n    return ReadHash(a:rdr)\n  elseif token == \"}\"\n    throw \"unexpected '}'\"\n  else\n    return ReadAtom(a:rdr)\n  endif\nendfunction\n\nfunction ReadStr(str)\n  let tokens = Tokenize(a:str)\n  if empty(tokens)\n    return \"\"\n  endif\n  return ReadForm(NewReader(tokens))\nendfunction\n"
  },
  {
    "path": "impls/vimscript/readline.vim",
    "content": "function PrintLn(str)\n  let lines = split(a:str, \"\\n\", 1)\n  call writefile(lines, \"/dev/stdout\", \"a\")\nendfunction\n\nfunction s:buildlibvimreadline()\n  if !filereadable(\"libvimextras.so\")\n    call system(\"make libvimextras.so\")\n  endif\nendfunction\n\n\" Returns [is_eof, line_string]\nfunction Readline(prompt)\n  \" Use the vimreadline() function defined in vimextras.c and compiled\n  \" into libvimextras.so\n  call s:buildlibvimreadline()\n  let res = libcall(\"libvimextras.so\", \"vimreadline\", a:prompt)\n  if res[0] == \"E\"\n    return [1, \"\"]\n  else\n    return [0, res[1:]]\n  endif\nendfunction\n"
  },
  {
    "path": "impls/vimscript/run",
    "content": "#!/usr/bin/env bash\ncd $(dirname $0)\nexec ./run_vimscript.sh ./${STEP:-stepA_mal}.vim \"${@}\"\n"
  },
  {
    "path": "impls/vimscript/run_vimscript.sh",
    "content": "#!/bin/sh\n\n# Run Vim in ex mode (-e) and run the given script ($1) on startup. Our scripts\n# end with 'qall!' which causes actual Vim UI to never start up.\n#\n# Set environment variable DEBUG=1 to allow more verbose error output from Vim.\n#\n# See: http://vim.wikia.com/wiki/Vim_as_a_system_interpreter_for_vimscript\n\nrundir=`dirname $0`\nexport LD_LIBRARY_PATH=`readlink -f $rundir`\nvimscriptfile=\"$1\"\nshift\nif [ x$DEBUG = x ] ; then\n  exec 2> /dev/null\nfi\nexec vim -i NONE -V1 -nNesS $vimscriptfile -- \"$@\" | cat\n"
  },
  {
    "path": "impls/vimscript/step0_repl.vim",
    "content": "source readline.vim\n\nfunction READ(str)\n  return a:str\nendfunction\n\nfunction EVAL(ast, env)\n  return a:ast\nendfunction\n\nfunction PRINT(exp)\n  return a:exp\nendfunction\n\nfunction REP(str)\n  return PRINT(EVAL(READ(a:str), {}))\nendfunction\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  call PrintLn(REP(line))\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step1_read_print.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction EVAL(ast, env)\n  return a:ast\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction REP(str)\n  return PRINT(EVAL(READ(a:str), {}))\nendfunction\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step2_eval.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction EVAL(ast, env)\n  \" call PrintLn(\"EVAL: \" . PrStr(a:ast, 1))\n\n  if SymbolQ(a:ast)\n    let varname = a:ast.val\n    if !has_key(a:env, varname)\n      throw \"'\" . varname . \"' not found\"\n    end\n    return a:env[varname]\n  elseif VectorQ(a:ast)\n    return VectorNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))\n  elseif HashQ(a:ast)\n    let ret = {}\n    for [k,v] in items(a:ast.val)\n      let newval = EVAL(v, a:env)\n      let ret[k] = newval\n    endfor\n    return HashNew(ret)\n  endif\n  if !ListQ(a:ast)\n    return a:ast\n  end\n  if EmptyQ(a:ast)\n    return a:ast\n  endif\n\n  \" apply list\n  let el = ListNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))\n\n  let Fn = el.val[0]\n  return Fn(el.val[1:-1])\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nlet repl_env = {}\nlet repl_env[\"+\"] = {a -> IntegerNew(a[0].val + a[1].val)}\nlet repl_env[\"-\"] = {a -> IntegerNew(a[0].val - a[1].val)}\nlet repl_env[\"*\"] = {a -> IntegerNew(a[0].val * a[1].val)}\nlet repl_env[\"/\"] = {a -> IntegerNew(a[0].val / a[1].val)}\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"ERROR: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step3_env.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction EVAL(ast, env)\n  let dbgeval = a:env.get(\"DEBUG-EVAL\")\n  if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n    call PrintLn(\"EVAL: \" . PrStr(a:ast, 1))\n  endif\n\n  if SymbolQ(a:ast)\n    let varname = a:ast.val\n    let Val = a:env.get(varname)\n    if empty(Val)\n      throw \"'\" . varname . \"' not found\"\n    endif\n    return Val\n  elseif VectorQ(a:ast)\n    return VectorNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))\n  elseif HashQ(a:ast)\n    let ret = {}\n    for [k,v] in items(a:ast.val)\n      let newval = EVAL(v, a:env)\n      let ret[k] = newval\n    endfor\n    return HashNew(ret)\n  endif\n  if !ListQ(a:ast)\n    return a:ast\n  end\n  if EmptyQ(a:ast)\n    return a:ast\n  endif\n\n  let first_symbol = a:ast.val[0].val\n  if first_symbol == \"def!\"\n    let a1 = a:ast.val[1]\n    let a2 = a:ast.val[2]\n    return a:env.set(a1.val, EVAL(a2, a:env))\n  elseif first_symbol == \"let*\"\n    let a1 = a:ast.val[1]\n    let a2 = a:ast.val[2]\n    let let_env = NewEnv(a:env)\n    let let_binds = a1.val\n    let i = 0\n    while i < len(let_binds)\n      call let_env.set(let_binds[i].val, EVAL(let_binds[i+1], let_env))\n      let i = i + 2\n    endwhile\n    return EVAL(a2, let_env)\n  else\n    \" apply list\n    let el = ListNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))\n    let Fn = el.val[0]\n    return Fn(el.val[1:-1])\n  endif\n\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nlet repl_env = NewEnv(\"\")\ncall repl_env.set(\"+\", {a -> IntegerNew(a[0].val + a[1].val)})\ncall repl_env.set(\"-\", {a -> IntegerNew(a[0].val - a[1].val)})\ncall repl_env.set(\"*\", {a -> IntegerNew(a[0].val * a[1].val)})\ncall repl_env.set(\"/\", {a -> IntegerNew(a[0].val / a[1].val)})\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step4_if_fn_do.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction EVAL(ast, env)\n  let dbgeval = a:env.get(\"DEBUG-EVAL\")\n  if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n    call PrintLn(\"EVAL: \" . PrStr(a:ast, 1))\n  endif\n\n  if SymbolQ(a:ast)\n    let varname = a:ast.val\n    let val = a:env.get(varname)\n    if empty(val)\n      throw \"'\" . varname . \"' not found\"\n    endif\n    return val\n  elseif VectorQ(a:ast)\n    return VectorNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))\n  elseif HashQ(a:ast)\n    let ret = {}\n    for [k,v] in items(a:ast.val)\n      let newval = EVAL(v, a:env)\n      let ret[k] = newval\n    endfor\n    return HashNew(ret)\n  endif\n  if !ListQ(a:ast)\n    return a:ast\n  end\n  if EmptyQ(a:ast)\n    return a:ast\n  endif\n\n  let first = ListFirst(a:ast)\n  let first_symbol = SymbolQ(first) ? first.val : \"\"\n  if first_symbol == \"def!\"\n    let a1 = a:ast.val[1]\n    let a2 = a:ast.val[2]\n    let ret = a:env.set(a1.val, EVAL(a2, a:env))\n    return ret\n  elseif first_symbol == \"let*\"\n    let a1 = a:ast.val[1]\n    let a2 = a:ast.val[2]\n    let let_env = NewEnv(a:env)\n    let let_binds = a1.val\n    let i = 0\n    while i < len(let_binds)\n      call let_env.set(let_binds[i].val, EVAL(let_binds[i+1], let_env))\n      let i = i + 2\n    endwhile\n    return EVAL(a2, let_env)\n  elseif first_symbol == \"if\"\n    let condvalue = EVAL(a:ast.val[1], a:env)\n    if FalseQ(condvalue) || NilQ(condvalue)\n      if len(a:ast.val) < 4\n        return g:MalNil\n      else\n        return EVAL(a:ast.val[3], a:env)\n      endif\n    else\n      return EVAL(a:ast.val[2], a:env)\n    endif\n  elseif first_symbol == \"do\"\n    let astlist = a:ast.val\n    for elt in astlist[1:-2]\n      let ignored = EVAL(elt, a:env)\n    endfor\n    return EVAL(astlist[-1], a:env)\n  elseif first_symbol == \"fn*\"\n    let fn = NewFn(ListNth(a:ast, 2), a:env, ListNth(a:ast, 1))\n    return fn\n  else\n    \" apply list\n    let el = ListNew(map(copy(a:ast.val), {_, e -> EVAL(e, a:env)}))\n    let funcobj = ListFirst(el)\n    let args = ListRest(el)\n    if NativeFunctionQ(funcobj)\n      return NativeFuncInvoke(funcobj, args)\n    elseif FunctionQ(funcobj)\n      return FuncInvoke(funcobj, args)\n    else\n      throw \"Not a function\"\n    endif\n  endif\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nlet repl_env = NewEnv(\"\")\n\nfor [k, Fn] in items(CoreNs)\n  call repl_env.set(k, Fn)\nendfor\n\ncall REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step5_tco.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction EVAL(ast, env)\n  let ast = a:ast\n  let env = a:env\n\n  while 1\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n      call PrintLn(\"EVAL: \" . PrStr(ast, 1))\n    endif\n\n    if SymbolQ(ast)\n      let varname = ast.val\n      let val = env.get(varname)\n      if empty(val)\n        throw \"'\" . varname . \"' not found\"\n      endif\n      return val\n    elseif VectorQ(ast)\n      return VectorNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n    elseif HashQ(ast)\n      let ret = {}\n      for [k,v] in items(ast.val)\n        let newval = EVAL(v, env)\n        let ret[k] = newval\n      endfor\n      return HashNew(ret)\n    endif\n    if !ListQ(ast)\n      return ast\n    end\n    if EmptyQ(ast)\n      return ast\n    endif\n\n    let first = ListFirst(ast)\n    let first_symbol = SymbolQ(first) ? first.val : \"\"\n    if first_symbol == \"def!\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let ret = env.set(a1.val, EVAL(a2, env))\n      return ret\n    elseif first_symbol == \"let*\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let env = NewEnv(env)\n      let let_binds = a1.val\n      let i = 0\n      while i < len(let_binds)\n        call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))\n        let i = i + 2\n      endwhile\n      let ast = a2\n      \" TCO\n    elseif first_symbol == \"if\"\n      let condvalue = EVAL(ast.val[1], env)\n      if FalseQ(condvalue) || NilQ(condvalue)\n        if len(ast.val) < 4\n          return g:MalNil\n        else\n          let ast = ast.val[3]\n        endif\n      else\n        let ast = ast.val[2]\n      endif\n      \" TCO\n    elseif first_symbol == \"do\"\n      let astlist = ast.val\n      for elt in astlist[1:-2]\n        let ignored = EVAL(elt, env)\n      endfor\n      let ast = astlist[-1]\n      \" TCO\n    elseif first_symbol == \"fn*\"\n      let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))\n      return fn\n    else\n      \" apply list\n      let el = ListNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n      let funcobj = ListFirst(el)\n      let args = ListRest(el)\n      if NativeFunctionQ(funcobj)\n        return NativeFuncInvoke(funcobj, args)\n      elseif FunctionQ(funcobj)\n        let fn = funcobj.val\n        let ast = fn.ast\n        let env = NewEnvWithBinds(fn.env, fn.params, args)\n        \" TCO\n      else\n        throw \"Not a function\"\n      endif\n    endif\n  endwhile\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nset maxfuncdepth=10000\nlet repl_env = NewEnv(\"\")\n\nfor [k, v] in items(CoreNs)\n  call repl_env.set(k, v)\nendfor\n\ncall REP(\"(def! not (fn* (a) (if a false true)))\", repl_env)\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step6_file.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction EVAL(ast, env)\n  let ast = a:ast\n  let env = a:env\n\n  while 1\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n      call PrintLn(\"EVAL: \" . PrStr(ast, 1))\n    endif\n\n    if SymbolQ(ast)\n      let varname = ast.val\n      let val = env.get(varname)\n      if empty(val)\n        throw \"'\" . varname . \"' not found\"\n      endif\n      return val\n    elseif VectorQ(ast)\n      return VectorNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n    elseif HashQ(ast)\n      let ret = {}\n      for [k,v] in items(ast.val)\n        let newval = EVAL(v, env)\n        let ret[k] = newval\n      endfor\n      return HashNew(ret)\n    endif\n    if !ListQ(ast)\n      return ast\n    end\n    if EmptyQ(ast)\n      return ast\n    endif\n\n    let first = ListFirst(ast)\n    let first_symbol = SymbolQ(first) ? first.val : \"\"\n    if first_symbol == \"def!\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let ret = env.set(a1.val, EVAL(a2, env))\n      return ret\n    elseif first_symbol == \"let*\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let env = NewEnv(env)\n      let let_binds = a1.val\n      let i = 0\n      while i < len(let_binds)\n        call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))\n        let i = i + 2\n      endwhile\n      let ast = a2\n      \" TCO\n    elseif first_symbol == \"if\"\n      let condvalue = EVAL(ast.val[1], env)\n      if FalseQ(condvalue) || NilQ(condvalue)\n        if len(ast.val) < 4\n          return g:MalNil\n        else\n          let ast = ast.val[3]\n        endif\n      else\n        let ast = ast.val[2]\n      endif\n      \" TCO\n    elseif first_symbol == \"do\"\n      let astlist = ast.val\n      for elt in astlist[1:-2]\n        let ignored = EVAL(elt, env)\n      endfor\n      let ast = astlist[-1]\n      \" TCO\n    elseif first_symbol == \"fn*\"\n      let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))\n      return fn\n    elseif first_symbol == \"eval\"\n      let ast = EVAL(ListNth(ast, 1), env)\n      let env = env.root()\n      \" TCO\n    else\n      \" apply list\n      let el = ListNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n      let funcobj = ListFirst(el)\n      let args = ListRest(el)\n      if NativeFunctionQ(funcobj)\n        return NativeFuncInvoke(funcobj, args)\n      elseif FunctionQ(funcobj)\n        let fn = funcobj.val\n        let ast = fn.ast\n        let env = NewEnvWithBinds(fn.env, fn.params, args)\n        \" TCO\n      else\n        throw \"Not a function\"\n      endif\n    endif\n  endwhile\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction RE(str, env)\n  return EVAL(READ(a:str), a:env)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nfunction GetArgvList()\n  return ListNew(map(copy(argv()[1:]), {_, arg -> StringNew(arg)}))\nendfunction\n\nset maxfuncdepth=10000\nlet repl_env = NewEnv(\"\")\n\nfor [k, v] in items(CoreNs)\n  call repl_env.set(k, v)\nendfor\n\ncall repl_env.set(\"*ARGV*\", GetArgvList())\n\ncall RE(\"(def! not (fn* (a) (if a false true)))\", repl_env)\ncall RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\nif !empty(argv())\n  call RE('(load-file \"' . argv(0) . '\")', repl_env)\n  qall!\nendif\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step7_quote.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction StartsWith(ast, sym)\n  if EmptyQ(a:ast)\n     return 0\n  endif\n  let fst = ListFirst(a:ast)\n  return SymbolQ(fst) && fst.val == a:sym\nendfunction\n\nfunction QuasiquoteLoop(xs)\n  let revlist = reverse(copy(a:xs))\n  let acc = ListNew([])\n  for elt in revlist\n     if ListQ(elt) && StartsWith(elt, \"splice-unquote\")\n       let acc = ListNew([SymbolNew(\"concat\"), ListNth(elt, 1), acc])\n     else\n       let acc = ListNew([SymbolNew(\"cons\"), Quasiquote(elt), acc])\n     endif\n   endfor\n   return acc\n endfunction\n\nfunction Quasiquote(ast)\n  if VectorQ(a:ast)\n    return ListNew([SymbolNew(\"vec\"), QuasiquoteLoop(a:ast.val)])\n  elseif SymbolQ(a:ast) || HashQ(a:ast)\n    return ListNew([SymbolNew(\"quote\"), a:ast])\n  elseif !ListQ(a:ast)\n    return a:ast\n  elseif StartsWith(a:ast, \"unquote\")\n    return ListNth(a:ast, 1)\n  else\n    return QuasiquoteLoop(a:ast.val)\n  endif\nendfunction\n\nfunction EVAL(ast, env)\n  let ast = a:ast\n  let env = a:env\n\n  while 1\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n      call PrintLn(\"EVAL: \" . PrStr(ast, 1))\n    endif\n\n    if SymbolQ(ast)\n      let varname = ast.val\n      let val = env.get(varname)\n      if empty(val)\n        throw \"'\" . varname . \"' not found\"\n      endif\n      return val\n    elseif VectorQ(ast)\n      return VectorNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n    elseif HashQ(ast)\n      let ret = {}\n      for [k,v] in items(ast.val)\n        let newval = EVAL(v, env)\n        let ret[k] = newval\n      endfor\n      return HashNew(ret)\n    endif\n    if !ListQ(ast)\n      return ast\n    end\n    if EmptyQ(ast)\n      return ast\n    endif\n\n    let first = ListFirst(ast)\n    let first_symbol = SymbolQ(first) ? first.val : \"\"\n    if first_symbol == \"def!\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let ret = env.set(a1.val, EVAL(a2, env))\n      return ret\n    elseif first_symbol == \"let*\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let env = NewEnv(env)\n      let let_binds = a1.val\n      let i = 0\n      while i < len(let_binds)\n        call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))\n        let i = i + 2\n      endwhile\n      let ast = a2\n      \" TCO\n    elseif first_symbol == \"quote\"\n      return ListNth(ast, 1)\n    elseif first_symbol == \"quasiquote\"\n      let ast = Quasiquote(ListNth(ast, 1))\n      \" TCO\n    elseif first_symbol == \"if\"\n      let condvalue = EVAL(ast.val[1], env)\n      if FalseQ(condvalue) || NilQ(condvalue)\n        if len(ast.val) < 4\n          return g:MalNil\n        else\n          let ast = ast.val[3]\n        endif\n      else\n        let ast = ast.val[2]\n      endif\n      \" TCO\n    elseif first_symbol == \"do\"\n      let astlist = ast.val\n      for elt in astlist[1:-2]\n        let ignored = EVAL(elt, env)\n      endfor\n      let ast = astlist[-1]\n      \" TCO\n    elseif first_symbol == \"fn*\"\n      let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))\n      return fn\n    elseif first_symbol == \"eval\"\n      let ast = EVAL(ListNth(ast, 1), env)\n      let env = env.root()\n      \" TCO\n    else\n      \" apply list\n      let el = ListNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n      let funcobj = ListFirst(el)\n      let args = ListRest(el)\n      if NativeFunctionQ(funcobj)\n        return NativeFuncInvoke(funcobj, args)\n      elseif FunctionQ(funcobj)\n        let fn = funcobj.val\n        let ast = fn.ast\n        let env = NewEnvWithBinds(fn.env, fn.params, args)\n        \" TCO\n      else\n        throw \"Not a function\"\n      endif\n    endif\n  endwhile\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction RE(str, env)\n  return EVAL(READ(a:str), a:env)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nfunction GetArgvList()\n  return ListNew(map(copy(argv()[1:]), {_, arg -> StringNew(arg)}))\nendfunction\n\nset maxfuncdepth=10000\nlet repl_env = NewEnv(\"\")\n\nfor [k, v] in items(CoreNs)\n  call repl_env.set(k, v)\nendfor\n\ncall repl_env.set(\"*ARGV*\", GetArgvList())\n\ncall RE(\"(def! not (fn* (a) (if a false true)))\", repl_env)\ncall RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\n\nif !empty(argv())\n  call RE('(load-file \"' . argv(0) . '\")', repl_env)\n  qall!\nendif\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step8_macros.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction StartsWith(ast, sym)\n  if EmptyQ(a:ast)\n     return 0\n  endif\n  let fst = ListFirst(a:ast)\n  return SymbolQ(fst) && fst.val == a:sym\nendfunction\n\nfunction QuasiquoteLoop(xs)\n  let revlist = reverse(copy(a:xs))\n  let acc = ListNew([])\n  for elt in revlist\n     if ListQ(elt) && StartsWith(elt, \"splice-unquote\")\n       let acc = ListNew([SymbolNew(\"concat\"), ListNth(elt, 1), acc])\n     else\n       let acc = ListNew([SymbolNew(\"cons\"), Quasiquote(elt), acc])\n     endif\n   endfor\n   return acc\n endfunction\n\nfunction Quasiquote(ast)\n  if VectorQ(a:ast)\n    return ListNew([SymbolNew(\"vec\"), QuasiquoteLoop(a:ast.val)])\n  elseif SymbolQ(a:ast) || HashQ(a:ast)\n    return ListNew([SymbolNew(\"quote\"), a:ast])\n  elseif !ListQ(a:ast)\n    return a:ast\n  elseif StartsWith(a:ast, \"unquote\")\n    return ListNth(a:ast, 1)\n  else\n    return QuasiquoteLoop(a:ast.val)\n  endif\nendfunction\n\nfunction EVAL(ast, env)\n  let ast = a:ast\n  let env = a:env\n\n  while 1\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n      call PrintLn(\"EVAL: \" . PrStr(ast, 1))\n    endif\n\n    if SymbolQ(ast)\n      let varname = ast.val\n      let val = env.get(varname)\n      if empty(val)\n        throw \"'\" . varname . \"' not found\"\n      endif\n      return val\n    elseif VectorQ(ast)\n      return VectorNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n    elseif HashQ(ast)\n      let ret = {}\n      for [k,v] in items(ast.val)\n        let newval = EVAL(v, env)\n        let ret[k] = newval\n      endfor\n      return HashNew(ret)\n    endif\n    if !ListQ(ast)\n      return ast\n    end\n    if EmptyQ(ast)\n      return ast\n    endif\n\n    let first = ListFirst(ast)\n    let first_symbol = SymbolQ(first) ? first.val : \"\"\n    if first_symbol == \"def!\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      return env.set(a1.val, EVAL(a2, env))\n    elseif first_symbol == \"let*\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let env = NewEnv(env)\n      let let_binds = a1.val\n      let i = 0\n      while i < len(let_binds)\n        call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))\n        let i = i + 2\n      endwhile\n      let ast = a2\n      \" TCO\n    elseif first_symbol == \"quote\"\n      return ListNth(ast, 1)\n    elseif first_symbol == \"quasiquote\"\n      let ast = Quasiquote(ListNth(ast, 1))\n      \" TCO\n    elseif first_symbol == \"defmacro!\"\n      let a1 = ListNth(ast, 1)\n      let a2 = ListNth(ast, 2)\n      let macro = MarkAsMacro(EVAL(a2, env))\n      return env.set(a1.val, macro)\n    elseif first_symbol == \"if\"\n      let condvalue = EVAL(ast.val[1], env)\n      if FalseQ(condvalue) || NilQ(condvalue)\n        if len(ast.val) < 4\n          return g:MalNil\n        else\n          let ast = ast.val[3]\n        endif\n      else\n        let ast = ast.val[2]\n      endif\n      \" TCO\n    elseif first_symbol == \"do\"\n      let astlist = ast.val\n      for elt in astlist[1:-2]\n        let ignored = EVAL(elt, env)\n      endfor\n      let ast = astlist[-1]\n      \" TCO\n    elseif first_symbol == \"fn*\"\n      let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))\n      return fn\n    elseif first_symbol == \"eval\"\n      let ast = EVAL(ListNth(ast, 1), env)\n      let env = env.root()\n      \" TCO\n    else\n      \" apply list\n      let funcobj = EVAL(first, env)\n      let args = ListRest(ast)\n      if MacroQ(funcobj)\n        let ast = FuncInvoke(funcobj, args)\n        continue\n        \" TCO\n      endif\n      let args = ListNew(map(copy(args.val), {_, e -> EVAL(e, env)}))\n      if NativeFunctionQ(funcobj)\n        return NativeFuncInvoke(funcobj, args)\n      elseif FunctionQ(funcobj)\n        let fn = funcobj.val\n        let ast = fn.ast\n        let env = NewEnvWithBinds(fn.env, fn.params, args)\n        \" TCO\n      else\n        throw \"Not a function\"\n      endif\n    endif\n  endwhile\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction RE(str, env)\n  return EVAL(READ(a:str), a:env)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nfunction GetArgvList()\n  return ListNew(map(copy(argv()[1:]), {_, arg -> StringNew(arg)}))\nendfunction\n\nset maxfuncdepth=10000\nlet repl_env = NewEnv(\"\")\n\nfor [k, v] in items(CoreNs)\n  call repl_env.set(k, v)\nendfor\n\ncall repl_env.set(\"*ARGV*\", GetArgvList())\n\ncall RE(\"(def! not (fn* (a) (if a false true)))\", repl_env)\ncall RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\ncall RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\nif !empty(argv())\n  call RE('(load-file \"' . argv(0) . '\")', repl_env)\n  qall!\nendif\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/step9_try.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nlet MalExceptionObj = \"\"\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction StartsWith(ast, sym)\n  if EmptyQ(a:ast)\n     return 0\n  endif\n  let fst = ListFirst(a:ast)\n  return SymbolQ(fst) && fst.val == a:sym\nendfunction\n\nfunction QuasiquoteLoop(xs)\n  let revlist = reverse(copy(a:xs))\n  let acc = ListNew([])\n  for elt in revlist\n     if ListQ(elt) && StartsWith(elt, \"splice-unquote\")\n       let acc = ListNew([SymbolNew(\"concat\"), ListNth(elt, 1), acc])\n     else\n       let acc = ListNew([SymbolNew(\"cons\"), Quasiquote(elt), acc])\n     endif\n   endfor\n   return acc\n endfunction\n\nfunction Quasiquote(ast)\n  if VectorQ(a:ast)\n    return ListNew([SymbolNew(\"vec\"), QuasiquoteLoop(a:ast.val)])\n  elseif SymbolQ(a:ast) || HashQ(a:ast)\n    return ListNew([SymbolNew(\"quote\"), a:ast])\n  elseif !ListQ(a:ast)\n    return a:ast\n  elseif StartsWith(a:ast, \"unquote\")\n    return ListNth(a:ast, 1)\n  else\n    return QuasiquoteLoop(a:ast.val)\n  endif\nendfunction\n\nfunction GetCatchClause(ast)\n  if ListCount(a:ast) < 3\n    return \"\"\n  end\n  let catch_clause = ListNth(a:ast, 2)\n  if ListFirst(catch_clause) == SymbolNew(\"catch*\")\n    return catch_clause\n  else\n    return \"\"\n  end\nendfunction\n\nfunction EVAL(ast, env)\n  let ast = a:ast\n  let env = a:env\n\n  while 1\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n      call PrintLn(\"EVAL: \" . PrStr(ast, 1))\n    endif\n\n    if SymbolQ(ast)\n      let varname = ast.val\n      let val = env.get(varname)\n      if empty(val)\n        throw \"'\" . varname . \"' not found\"\n      endif\n      return val\n    elseif VectorQ(ast)\n      return VectorNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n    elseif HashQ(ast)\n      let ret = {}\n      for [k,v] in items(ast.val)\n        let newval = EVAL(v, env)\n        let ret[k] = newval\n      endfor\n      return HashNew(ret)\n    endif\n    if !ListQ(ast)\n      return ast\n    end\n    if EmptyQ(ast)\n      return ast\n    endif\n\n    let first = ListFirst(ast)\n    let first_symbol = SymbolQ(first) ? first.val : \"\"\n    if first_symbol == \"def!\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      return env.set(a1.val, EVAL(a2, env))\n    elseif first_symbol == \"let*\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let env = NewEnv(env)\n      let let_binds = a1.val\n      let i = 0\n      while i < len(let_binds)\n        call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))\n        let i = i + 2\n      endwhile\n      let ast = a2\n      \" TCO\n    elseif first_symbol == \"quote\"\n      return ListNth(ast, 1)\n    elseif first_symbol == \"quasiquote\"\n      let ast = Quasiquote(ListNth(ast, 1))\n      \" TCO\n    elseif first_symbol == \"defmacro!\"\n      let a1 = ListNth(ast, 1)\n      let a2 = ListNth(ast, 2)\n      let macro = MarkAsMacro(EVAL(a2, env))\n      return env.set(a1.val, macro)\n    elseif first_symbol == \"if\"\n      let condvalue = EVAL(ast.val[1], env)\n      if FalseQ(condvalue) || NilQ(condvalue)\n        if len(ast.val) < 4\n          return g:MalNil\n        else\n          let ast = ast.val[3]\n        endif\n      else\n        let ast = ast.val[2]\n      endif\n      \" TCO\n    elseif first_symbol == \"try*\"\n      try\n        return EVAL(ListNth(ast, 1), env)\n      catch\n        let catch_clause = GetCatchClause(ast)\n        if empty(catch_clause)\n          throw v:exception\n        endif\n\n        let exc_var = ListNth(catch_clause, 1).val\n        if v:exception == \"__MalException__\"\n          let exc_value = g:MalExceptionObj\n        else\n          let exc_value = StringNew(v:exception)\n        endif\n        let catch_env = NewEnvWithBinds(env, ListNew([SymbolNew(exc_var)]), ListNew([exc_value]))\n        return EVAL(ListNth(catch_clause, 2), catch_env)\n      endtry\n    elseif first_symbol == \"do\"\n      let astlist = ast.val\n      for elt in astlist[1:-2]\n        let ignored = EVAL(elt, env)\n      endfor\n      let ast = astlist[-1]\n      \" TCO\n    elseif first_symbol == \"fn*\"\n      let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))\n      return fn\n    elseif first_symbol == \"eval\"\n      let ast = EVAL(ListNth(ast, 1), env)\n      let env = env.root()\n      \" TCO\n    else\n      \" apply list\n      let funcobj = EVAL(first, env)\n      let args = ListRest(ast)\n      if MacroQ(funcobj)\n        let ast = FuncInvoke(funcobj, args)\n        continue\n        \" TCO\n      endif\n      let args = ListNew(map(copy(args.val), {_, e -> EVAL(e, env)}))\n      if NativeFunctionQ(funcobj)\n        return NativeFuncInvoke(funcobj, args)\n      elseif FunctionQ(funcobj)\n        let fn = funcobj.val\n        let ast = fn.ast\n        let env = NewEnvWithBinds(fn.env, fn.params, args)\n        \" TCO\n      else\n        throw \"Not a function\"\n      endif\n    endif\n  endwhile\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction RE(str, env)\n  return EVAL(READ(a:str), a:env)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nfunction GetArgvList()\n  return ListNew(map(copy(argv()[1:]), {_, arg -> StringNew(arg)}))\nendfunction\n\nset maxfuncdepth=10000\nlet repl_env = NewEnv(\"\")\n\nfor [k, v] in items(CoreNs)\n  call repl_env.set(k, v)\nendfor\n\ncall repl_env.set(\"*ARGV*\", GetArgvList())\n\ncall RE(\"(def! not (fn* (a) (if a false true)))\", repl_env)\ncall RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\ncall RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\nif !empty(argv())\n  try\n    call RE('(load-file \"' . argv(0) . '\")', repl_env)\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\n  qall!\nendif\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    if v:exception == \"__MalException__\"\n      call PrintLn(\"Error: \" . PrStr(g:MalExceptionObj, 1))\n    else\n      call PrintLn(\"Error: \" . v:exception)\n    end\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/stepA_mal.vim",
    "content": "source readline.vim\nsource types.vim\nsource reader.vim\nsource printer.vim\nsource env.vim\nsource core.vim\n\nlet MalExceptionObj = \"\"\n\nfunction READ(str)\n  return ReadStr(a:str)\nendfunction\n\nfunction StartsWith(ast, sym)\n  if EmptyQ(a:ast)\n     return 0\n  endif\n  let fst = ListFirst(a:ast)\n  return SymbolQ(fst) && fst.val == a:sym\nendfunction\n\nfunction QuasiquoteLoop(xs)\n  let revlist = reverse(copy(a:xs))\n  let acc = ListNew([])\n  for elt in revlist\n     if ListQ(elt) && StartsWith(elt, \"splice-unquote\")\n       let acc = ListNew([SymbolNew(\"concat\"), ListNth(elt, 1), acc])\n     else\n       let acc = ListNew([SymbolNew(\"cons\"), Quasiquote(elt), acc])\n     endif\n   endfor\n   return acc\n endfunction\n\nfunction Quasiquote(ast)\n  if VectorQ(a:ast)\n    return ListNew([SymbolNew(\"vec\"), QuasiquoteLoop(a:ast.val)])\n  elseif SymbolQ(a:ast) || HashQ(a:ast)\n    return ListNew([SymbolNew(\"quote\"), a:ast])\n  elseif !ListQ(a:ast)\n    return a:ast\n  elseif StartsWith(a:ast, \"unquote\")\n    return ListNth(a:ast, 1)\n  else\n    return QuasiquoteLoop(a:ast.val)\n  endif\nendfunction\n\nfunction GetCatchClause(ast)\n  if ListCount(a:ast) < 3\n    return \"\"\n  end\n  let catch_clause = ListNth(a:ast, 2)\n  if ListFirst(catch_clause) == SymbolNew(\"catch*\")\n    return catch_clause\n  else\n    return \"\"\n  end\nendfunction\n\nfunction EVAL(ast, env)\n  let ast = a:ast\n  let env = a:env\n\n  while 1\n\n    let dbgeval = env.get(\"DEBUG-EVAL\")\n    if !(empty(dbgeval) || FalseQ(dbgeval) || NilQ(dbgeval))\n      call PrintLn(\"EVAL: \" . PrStr(ast, 1))\n    endif\n\n    if SymbolQ(ast)\n      let varname = ast.val\n      let val = env.get(varname)\n      if empty(val)\n        throw \"'\" . varname . \"' not found\"\n      endif\n      return val\n    elseif VectorQ(ast)\n      return VectorNew(map(copy(ast.val), {_, e -> EVAL(e, env)}))\n    elseif HashQ(ast)\n      let ret = {}\n      for [k,v] in items(ast.val)\n        let newval = EVAL(v, env)\n        let ret[k] = newval\n      endfor\n      return HashNew(ret)\n    endif\n    if !ListQ(ast)\n      return ast\n    end\n    if EmptyQ(ast)\n      return ast\n    endif\n\n    let first = ListFirst(ast)\n    let first_symbol = SymbolQ(first) ? first.val : \"\"\n    if first_symbol == \"def!\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      return env.set(a1.val, EVAL(a2, env))\n    elseif first_symbol == \"let*\"\n      let a1 = ast.val[1]\n      let a2 = ast.val[2]\n      let env = NewEnv(env)\n      let let_binds = a1.val\n      let i = 0\n      while i < len(let_binds)\n        call env.set(let_binds[i].val, EVAL(let_binds[i+1], env))\n        let i = i + 2\n      endwhile\n      let ast = a2\n      \" TCO\n    elseif first_symbol == \"quote\"\n      return ListNth(ast, 1)\n    elseif first_symbol == \"quasiquote\"\n      let ast = Quasiquote(ListNth(ast, 1))\n      \" TCO\n    elseif first_symbol == \"defmacro!\"\n      let a1 = ListNth(ast, 1)\n      let a2 = ListNth(ast, 2)\n      let macro = MarkAsMacro(EVAL(a2, env))\n      return env.set(a1.val, macro)\n    elseif first_symbol == \"if\"\n      let condvalue = EVAL(ast.val[1], env)\n      if FalseQ(condvalue) || NilQ(condvalue)\n        if len(ast.val) < 4\n          return g:MalNil\n        else\n          let ast = ast.val[3]\n        endif\n      else\n        let ast = ast.val[2]\n      endif\n      \" TCO\n    elseif first_symbol == \"try*\"\n      try\n        return EVAL(ListNth(ast, 1), env)\n      catch\n        let catch_clause = GetCatchClause(ast)\n        if empty(catch_clause)\n          throw v:exception\n        endif\n\n        let exc_var = ListNth(catch_clause, 1).val\n        if v:exception == \"__MalException__\"\n          let exc_value = g:MalExceptionObj\n        else\n          let exc_value = StringNew(v:exception)\n        endif\n        let catch_env = NewEnvWithBinds(env, ListNew([SymbolNew(exc_var)]), ListNew([exc_value]))\n        return EVAL(ListNth(catch_clause, 2), catch_env)\n      endtry\n    elseif first_symbol == \"do\"\n      let astlist = ast.val\n      for elt in astlist[1:-2]\n        let ignored = EVAL(elt, env)\n      endfor\n      let ast = astlist[-1]\n      \" TCO\n    elseif first_symbol == \"fn*\"\n      let fn = NewFn(ListNth(ast, 2), env, ListNth(ast, 1))\n      return fn\n    elseif first_symbol == \"eval\"\n      let ast = EVAL(ListNth(ast, 1), env)\n      let env = env.root()\n      \" TCO\n    else\n      \" apply list\n      let funcobj = EVAL(first, env)\n      let args = ListRest(ast)\n      if MacroQ(funcobj)\n        let ast = FuncInvoke(funcobj, args)\n        continue\n        \" TCO\n      endif\n      let args = ListNew(map(copy(args.val), {_, e -> EVAL(e, env)}))\n      if NativeFunctionQ(funcobj)\n        return NativeFuncInvoke(funcobj, args)\n      elseif FunctionQ(funcobj)\n        let fn = funcobj.val\n        let ast = fn.ast\n        let env = NewEnvWithBinds(fn.env, fn.params, args)\n        \" TCO\n      else\n        throw \"Not a function\"\n      endif\n    endif\n  endwhile\nendfunction\n\nfunction PRINT(exp)\n  return PrStr(a:exp, 1)\nendfunction\n\nfunction RE(str, env)\n  return EVAL(READ(a:str), a:env)\nendfunction\n\nfunction REP(str, env)\n  return PRINT(EVAL(READ(a:str), a:env))\nendfunction\n\nfunction GetArgvList()\n  return ListNew(map(copy(argv()[1:]), {_, arg -> StringNew(arg)}))\nendfunction\n\nset maxfuncdepth=10000\nlet repl_env = NewEnv(\"\")\n\nfor [k, v] in items(CoreNs)\n  call repl_env.set(k, v)\nendfor\n\ncall repl_env.set(\"*ARGV*\", GetArgvList())\n\ncall RE(\"(def! *host-language* \\\"vimscript\\\")\", repl_env)\ncall RE(\"(def! not (fn* (a) (if a false true)))\", repl_env)\ncall RE(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\", repl_env)\ncall RE(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env)\n\nif !empty(argv())\n  try\n    call RE('(load-file \"' . argv(0) . '\")', repl_env)\n  catch\n    call PrintLn(\"Error: \" . v:exception)\n  endtry\n  qall!\nendif\n\ncall REP(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env)\n\nwhile 1\n  let [eof, line] = Readline(\"user> \")\n  if eof\n    break\n  endif\n  if line == \"\"\n    continue\n  endif\n  try\n    call PrintLn(REP(line, repl_env))\n  catch\n    if v:exception == \"__MalException__\"\n      call PrintLn(\"Error: \" . PrStr(g:MalExceptionObj, 1))\n    else\n      call PrintLn(\"Error: \" . v:exception)\n    end\n  endtry\nendwhile\nqall!\n"
  },
  {
    "path": "impls/vimscript/tests/step5_tco.mal",
    "content": ";; Test recursive non-tail call function\n\n(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))\n\n(sum-to 10)\n;=>55\n\n;;; no try* yet, so test completion of side-effects\n(def! res1 nil)\n;=>nil\n;;; For implementations without their own TCO this should fail and\n;;; leave res1 unchanged\n(def! res1 (sum-to 10000))\nres1\n;=>nil\n"
  },
  {
    "path": "impls/vimscript/tests/stepA_mal.mal",
    "content": ";; Testing basic Vim interop with (vim* \"...\")\n;;\n\n(vim* \"7\")\n;=>7\n\n(vim* \"'7'\")\n;=>\"7\"\n\n(vim* \"[7,8,9]\")\n;=>(7 8 9)\n\n(vim* \"{\\\"abc\\\": 789}\")\n;=>{\"abc\" 789}\n\n;;\n;; Test Vim eval() expression support\n;;\n\n(vim* \"3 + 7 * 8\")\n;=>59\n\n(vim* \"join(['a','b','c'], '_')\")\n;=>\"a_b_c\"\n\n(vim* \"split('d@@@@e@f@@g', '@\\+')\")\n;=>(\"d\" \"e\" \"f\" \"g\")\n\n(vim* \"add([1,2,3], 4)\")\n;=>(1 2 3 4)\n\n;;\n;; Test access to Vim predefined variables\n;;\n\n;;; (vim* \"v:progname\")\n;;; ;=>\"vim\"\n\n;; v:version is 800 for Vim 8.0\n(>= (vim* \"v:version\") 800)\n;=>true\n"
  },
  {
    "path": "impls/vimscript/types.vim",
    "content": "\" types module\n\nfunction ObjNewWithMeta(obj_type, obj_val, obj_meta)\n  return {\"type\": a:obj_type, \"val\": a:obj_val, \"meta\": a:obj_meta}\nendfunction\n\nfunction ObjNew(obj_type, obj_val)\n  return {\"type\": a:obj_type, \"val\": a:obj_val}\nendfunction\n\nfunction ObjHasMeta(obj)\n  return has_key(a:obj, \"meta\")\nendfunction\n\nfunction ObjMeta(obj)\n  return ObjHasMeta(a:obj) ? a:obj[\"meta\"] : g:MalNil\nendfunction\n\nfunction ObjSetValue(obj, newval)\n  let a:obj[\"val\"] = a:newval\n  return a:newval\nendfunction\n\nfunction ObjSetMeta(obj, newmeta)\n  let a:obj[\"meta\"] = a:newmeta\n  return a:newmeta\nendfunction\n\nfunction SymbolQ(obj)\n  return a:obj.type == \"symbol\"\nendfunction\n\nfunction StringQ(obj)\n  return a:obj.type == \"string\"\nendfunction\n\nfunction KeywordQ(obj)\n  return a:obj.type == \"keyword\"\nendfunction\n\nfunction AtomQ(obj)\n  return a:obj.type == \"atom\"\nendfunction\n\nfunction NilQ(obj)\n  return a:obj.type == \"nil\"\nendfunction\n\nfunction TrueQ(obj)\n  return a:obj.type == \"true\"\nendfunction\n\nfunction FalseQ(obj)\n  return a:obj.type == \"false\"\nendfunction\n\nfunction IntegerQ(obj)\n  return a:obj.type == \"integer\"\nendfunction\n\nfunction FloatQ(obj)\n  return a:obj.type == \"float\"\nendfunction\n\nfunction ListQ(obj)\n  return a:obj.type == \"list\"\nendfunction\n\nfunction VectorQ(obj)\n  return a:obj.type == \"vector\"\nendfunction\n\nfunction SequentialQ(obj)\n  return ListQ(a:obj) || VectorQ(a:obj)\nendfunction\n\nfunction HashQ(obj)\n  return a:obj.type == \"hash\"\nendfunction\n\nfunction FunctionQ(obj)\n  return a:obj.type == \"function\" && !a:obj.val.is_macro\nendfunction\n\nfunction MacroQ(obj)\n  return a:obj.type == \"function\" && a:obj.val.is_macro\nendfunction\n\nfunction NativeFunctionQ(obj)\n  return a:obj.type == \"nativefunction\"\nendfunction\n\nfunction NilNew()\n  return ObjNew(\"nil\", \"\")\nendfunction\n\nfunction TrueNew()\n  return ObjNew(\"true\", \"\")\nendfunction\n\nfunction FalseNew()\n  return ObjNew(\"false\", \"\")\nendfunction\n\nfunction BoolNew(bool)\n  return a:bool ? g:MalTrue : g:MalFalse\nendfunction\n\nfunction KeywordNew(val)\n  return ObjNew(\"keyword\", a:val)\nendfunction\n\nfunction AtomNew(val)\n  return ObjNewWithMeta(\"atom\", a:val, g:MalNil)\nendfunction\n\nfunction SymbolNew(val)\n  return ObjNew(\"symbol\", a:val)\nendfunction\n\nfunction StringNew(val)\n  return ObjNew(\"string\", a:val)\nendfunction\n\nfunction IntegerNew(val)\n  return ObjNew(\"integer\", a:val)\nendfunction\n\nfunction FloatNew(val)\n  return ObjNew(\"float\", a:val)\nendfunction\n\nfunction ListNew(val)\n  return ObjNewWithMeta(\"list\", a:val, g:MalNil)\nendfunction\n\nfunction VectorNew(val)\n  return ObjNewWithMeta(\"vector\", a:val, g:MalNil)\nendfunction\n\nfunction HashNew(val)\n  return ObjNewWithMeta(\"hash\", a:val, g:MalNil)\nendfunction\n\nfunction HashMakeKey(obj)\n  if !StringQ(a:obj) && !KeywordQ(a:obj)\n    throw \"expected hash-map key string, got: \" . a:obj.type);\n  endif\n  return a:obj.type . \"#\" . a:obj.val\nendfunction\n\nfunction HashParseKey(str)\n  if a:str =~ \"^string#\"\n    return StringNew(a:str[7:])\n  elseif a:str =~ \"^keyword#\"\n    return KeywordNew(a:str[8:])\n  endif\nendfunction\n\nfunction HashBuild(elements)\n  if (len(a:elements) % 2) != 0\n    throw \"Odd number of hash-map arguments\"\n  endif\n  let i = 0\n  let hash = {}\n  while i < len(a:elements)\n    let key = a:elements[i]\n    let val = a:elements[i + 1]\n    let keystring = HashMakeKey(key)\n    let hash[keystring] = val\n    let i = i + 2\n  endwhile\n  return HashNew(hash)\nendfunction\n\nfunction HashEqualQ(x, y)\n  if len(a:x.val) != len(a:y.val)\n    return 0\n  endif\n  for k in keys(a:x.val)\n    let vx = a:x.val[k]\n    let vy = a:y.val[k]\n    if empty(vy) || !EqualQ(vx, vy)\n      return 0\n    endif\n  endfor\n  return 1\nendfunction\n\nfunction SequentialEqualQ(x, y)\n  if len(a:x.val) != len(a:y.val)\n    return 0\n  endif\n  let i = 0\n  while i < len(a:x.val)\n    let ex = a:x.val[i]\n    let ey = a:y.val[i]\n    if !EqualQ(ex, ey)\n      return 0\n    endif\n    let i = i +1\n  endwhile\n  return 1\nendfunction\n\nfunction EqualQ(x, y)\n  if SequentialQ(a:x) && SequentialQ(a:y)\n    return SequentialEqualQ(a:x, a:y)\n  elseif HashQ(a:x) && HashQ(a:y)\n    return HashEqualQ(a:x, a:y)\n  elseif a:x.type != a:y.type\n    return 0\n  else\n    return a:x.val == a:y.val\n  endif\nendfunction\n\nfunction EmptyQ(list)\n  return empty(a:list.val)\nendfunction\n\nfunction ListCount(list)\n  return len(a:list.val)\nendfunction\n\nfunction ListNth(list, index)\n  if a:index >= len(a:list.val)\n    throw \"nth: index out of range\"\n  endif\n  return a:list.val[a:index]\nendfunction\n\nfunction ListFirst(list)\n  return get(a:list.val, 0, g:MalNil)\nendfunction\n\nfunction ListDrop(list, drop_elements)\n  return ListNew(a:list.val[a:drop_elements :])\nendfunction\n\nfunction ListRest(list)\n  return ListDrop(a:list, 1)\nendfunction\n\nfunction FuncInvoke(funcobj, args)\n  let fn = a:funcobj.val\n  let funcenv = NewEnvWithBinds(fn.env, fn.params, a:args)\n  return EVAL(fn.ast, funcenv)\nendfunction\n\nfunction NativeFuncInvoke(funcobj, argslist)\n  let fn = a:funcobj.val\n  return fn.Func(a:argslist.val)\nendfunction\n\nfunction MarkAsMacro(funcobj)\n  let fn = a:funcobj.val\n  let mac = {\"ast\": fn.ast, \"env\": fn.env, \"params\": fn.params, \"is_macro\": 1}\n  return ObjNewWithMeta(\"function\", mac, g:MalNil)\nendfunction\n\nfunction NewFn(ast, env, params)\n  let fn = {\"ast\": a:ast, \"env\": a:env, \"params\": a:params, \"is_macro\": 0}\n  return ObjNewWithMeta(\"function\", fn, g:MalNil)\nendfunction\n\nfunction NewNativeFn(funcname)\n  let fn = {\"Func\": function(a:funcname), \"name\": a:funcname}\n  return ObjNewWithMeta(\"nativefunction\", fn, g:MalNil)\nendfunction\n\nfunction NewNativeFnLambda(lambdaexpr)\n  let fn = {\"Func\": a:lambdaexpr, \"name\": \"inline\"}\n  return ObjNewWithMeta(\"nativefunction\", fn, g:MalNil)\nendfunction\n\nlet g:MalNil = NilNew()\nlet g:MalTrue = TrueNew()\nlet g:MalFalse = FalseNew()\n"
  },
  {
    "path": "impls/vimscript/vimextras.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <readline/readline.h>\n#include <sys/time.h>\n\n/*\n * Vim interface for the readline(3) function.\n *\n * Prints 'prompt' and reads a line from the input. If EOF is encountered,\n * returns the string \"E\"; otherwise, returns the string \"S<line>\" where <line>\n * is the line read from input.\n *\n * This function is not thread-safe.\n */\nchar* vimreadline(char* prompt) {\n    static char buf[1024];\n    char* res = readline(prompt);\n    if (res) {\n        buf[0] = 'S';\n        strncpy(buf + 1, res, sizeof(buf) - 1);\n        free(res);\n    } else {\n        buf[0] = 'E';\n        buf[1] = '\\0';\n    }\n    return buf;\n}\n\n#define UNIXTIME_BASE 1451606400 /* = Unix time of 2016-01-01 */\n\n/*\n * Returns the number of milliseconds since 2016-01-01 00:00:00 UTC.\n *\n * This date is chosen (instead of the standard 1970 epoch) so the number of\n * milliseconds will not exceed a 32-bit integer, which is the limit for Vim\n * number variables.\n */\nint vimtimems(int dummy) {\n    struct timeval tv;\n    (void) dummy; /* unused */\n    gettimeofday(&tv, NULL);\n    return (tv.tv_sec - UNIXTIME_BASE) * 1000 + (tv.tv_usec / 1000);\n}\n"
  },
  {
    "path": "impls/wasm/Dockerfile",
    "content": "FROM ubuntu:20.04 as base\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\n#\n# node\n#\n\n# For building node modules\nRUN apt-get -y install g++\n\n# Add nodesource apt repo config for 16.x stable\nRUN apt-get -y install gnupg\nRUN curl -sL https://deb.nodesource.com/setup_16.x | bash -\n\n# Install nodejs\nRUN apt-get -y install nodejs\n\nENV NPM_CONFIG_CACHE /mal/.npm\n\n#\n# wace build and runtime libs\n#\nRUN dpkg --add-architecture i386 && \\\n    apt-get -y update && \\\n    DEBIAN_FRONTEND=noninteractive apt-get -y install \\\n        lib32gcc-9-dev lib32gcc-8-dev lib32gcc-7-dev \\\n        libsdl2-dev:i386 libsdl2-image-dev:i386 \\\n        libedit-dev:i386 freeglut3-dev:i386 \\\n        libreadline-dev:i386\n\n#\n# binaryen\n#\nRUN apt-get -y install git-core cmake\n\nRUN apt-get -y install binaryen\n\n###########################################################################\nFROM base as build_tools\n###########################################################################\n\n#\n# clang/LLVM and rust (for building wasmtime)\n#\n#RUN apt-get -y install llvm-3.9-dev libclang-3.9-dev clang-3.9\n#RUN apt-get -y install curl &&  \\\n#    curl https://sh.rustup.rs -sSf > /tmp/rustup.sh && \\\n#    sh /tmp/rustup.sh -y\n#ENV PATH $PATH:/root/.cargo/bin\n\n#\n# pypy / rpython (for building warpy)\n#\n\n## rpython deps\n#ENV DEBIAN_FRONTEND=noninteractive\n#RUN apt-get -y install libffi-dev pkg-config libz-dev \\\n#    libbz2-dev libsqlite3-dev libncurses-dev libexpat1-dev \\\n#    libssl-dev libgdbm-dev tcl-dev\n#\n## install pypy, build and install pypy/rpython, remove prior pypy\n#RUN apt-get -y install software-properties-common && \\\n#    add-apt-repository ppa:pypy && \\\n#    apt-get -y update && \\\n#    apt-get -y install pypy && \\\n#    mkdir -p /opt/pypy && \\\n#    curl -L https://github.com/pypy/pypy/archive/refs/tags/release-pypy2.7-v6.0.0.tar.gz \\\n#        | tar -xzf - -C /opt/pypy/ --strip-components=1 && \\\n#    cd /opt/pypy && make && \\\n#    chmod -R ugo+rw /opt/pypy/rpython/_cache && \\\n#    ln -sf /opt/pypy/rpython/bin/rpython /usr/local/bin/rpython && \\\n#    ln -sf /opt/pypy/pypy-c /usr/local/bin/pypy && \\\n#    rm -rf /tmp/usession* && \\\n#    ln -sf /opt/pypy/pypy/goal/pypy-c /usr/local/bin/pypy && \\\n#    apt-get -y autoremove pypy\n\n\n#\n# wasi-sdk (C/C++ -> wasm+wasi)\n#\nRUN curl -LO https://github.com/CraneStation/wasi-sdk/releases/download/wasi-sdk-5/wasi-sdk_5.0_amd64.deb && \\\n    dpkg -i wasi-sdk_5.0_amd64.deb && \\\n    rm wasi-sdk_5.0_amd64.deb\n\n##\n## Rust wasm support\n##\n#RUN rustup default nightly\n#RUN rustup target add wasm32-unknown-wasi --toolchain nightly\n##RUN cargo +nightly build --target wasm32-unknown-wasi\n#\n## TODO: Do this when we install rust instead\n#RUN mv /root/.cargo /opt/cargo && mv /root/.rustup /opt/rustup\n#RUN chmod -R a+r /opt/cargo && chmod -R a+rw /opt/rustup\n#ENV CARGO_HOME /opt/cargo\n#ENV RUSTUP_HOME /opt/rustup\n#ENV PATH $PATH:/opt/cargo/bin\n\n###########################################################################\nFROM build_tools as runtimes\n###########################################################################\n\n#\n# warpy\n#\n#RUN git clone https://github.com/kanaka/warpy/  && \\\n#    cd warpy && \\\n#    make warpy-nojit && \\\n#    cp warpy-nojit /usr/bin/warpy\n\n#\n# wac/wace\n#\n#RUN git clone https://github.com/kanaka/wac/ && \\\n#    cd wac  && \\\n#    make USE_SDL= wac wax wace && \\\n#    cp wac wax wace /usr/bin\n\n#\n# wasmer\n#\n\n#RUN curl https://get.wasmer.io -sSfL | sh\nRUN sh -c \"$(curl https://get.wasmer.io -sSfL)\" -- 2.0.0 && \\\n    cp /root/.wasmer/bin/wasmer /usr/bin/wasmer && \\\n    cp /root/.wasmer/bin/wapm /usr/bin/wapm\n\n#RUN git clone --recursive https://github.com/wasmerio/wasmer  && \\\n#    cd wasmer && \\\n#    cargo build --release && \\\n#    cp target/release/wasmer /usr/bin/\n\n#\n# wasmtime\n#\n\nRUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v3.0.0/wasmtime-v3.0.0-x86_64-linux.tar.xz | tar xvJf - && \\\n    cp wasmtime-v3.0.0-x86_64-linux/wasmtime /usr/bin/wasmtime\n\n#RUN git clone --recursive https://github.com/CraneStation/wasmtime &&  \\\n#    cd wasmtime && \\\n#    sed -i 's/c3994bf57b5d2f1f973b0e4e37bc385695aa4ed2/8ea7a983d8b1364e5f62d2adf0e74b3b8db1c9b3/' Cargo.toml && \\\n#    cargo build --release && \\\n#    cp target/release/wasmtime /usr/bin/ && \\\n#    cp target/release/wasm2obj /usr/bin/\n\n\n###########################################################################\nFROM base as wasm\n###########################################################################\n\n#COPY --from=runtimes /usr/bin/wac /usr/bin/wac\n#COPY --from=runtimes /usr/bin/wax /usr/bin/wax\n#COPY --from=runtimes /usr/bin/wace /usr/bin/wace\n#COPY --from=runtimes /usr/bin/warpy /usr/bin/warpy\nCOPY --from=runtimes /usr/bin/wasmtime /usr/bin/wasmtime\n\nCOPY --from=runtimes /usr/bin/wasmer /usr/bin/wasmer\nCOPY --from=runtimes /usr/bin/wapm /usr/bin/wapm\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/wasm/Makefile",
    "content": "MODE ?= $(strip \\\n          $(if $(filter wace_libc,$(wasm_MODE)),\\\n\t    libc,\\\n\t    $(if $(filter direct node js wace_fooboot warpy,$(wasm_MODE)),\\\n\t      direct,\\\n\t      wasi)))\n\nEXT = .wasm\n\nWASM_AS ?= wasm-as\nWAMP ?= node_modules/.bin/wamp\n\nSTEP0_DEPS = $(WAMP) platform_$(MODE).wam string.wam printf.wam\nSTEP1_DEPS = $(STEP0_DEPS) types.wam mem.wam debug.wam reader.wam printer.wam\nSTEP3_DEPS = $(STEP1_DEPS) env.wam\nSTEP4_DEPS = $(STEP3_DEPS) core.wam\n\nSTEPS = step0_repl step1_read_print step2_eval step3_env \\\n        step4_if_fn_do step5_tco step6_file step7_quote \\\n        step8_macros step9_try stepA_mal\n\nall: $(STEPS:=$(EXT))\n\n$(WAMP):\n\tnpm install\n\n%.wat: %.wam\n\t$(WAMP) $(filter %.wam,$^) > $*.wat\n\n%.wasm: %.wat\n\t$(WASM_AS) $< -o $@\n\nstep0_repl.wat: $(STEP0_DEPS)\nstep1_read_print.wat step2_eval.wat: $(STEP1_DEPS)\nstep3_env.wat: $(STEP3_DEPS)\nstep4_if_fn_do.wat step5_tco.wat step6_file.wat: $(STEP4_DEPS)\nstep7_quote.wat step8_macros.wat step9_try.wat stepA_mal.wat: $(STEP4_DEPS)\n\n.PHONY: clean\n\nclean:\n\trm -f *.wat *.wasm\n"
  },
  {
    "path": "impls/wasm/core.wam",
    "content": "(module $core\n\n\n  ;; it would be nice to have this in types.wam but it uses\n  ;; ENV_NEW_BINDS which is not available until step3 but types is\n  ;; used in step1\n\n  (func $APPLY (param $f i32) (param $args i32) (result i32)\n    (local $res i32 $env i32 $ftype i32 $a i32)\n    (local.set $f ($DEREF_META $f))\n    (local.set $ftype ($TYPE $f))\n    (if (i32.eq $ftype (global.get $FUNCTION_T))\n      (then\n        ;; Must be kept in sync with EVAL's FUNCTION_T evaluation\n        (if (i32.eq ($VAL0 $f) 0) ;; eval\n          (then\n            (local.set $res ($EVAL ($MEM_VAL1_ptr $args)\n                                   (global.get $repl_env))))\n          (else\n            (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f))))))\n    (else (if (OR (i32.eq $ftype (global.get $MALFUNC_T))\n                  (i32.eq $ftype (global.get $MACRO_T)))\n      (then\n        ;; create new environment using env and params stored in function\n        (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                        ($MEM_VAL1_ptr $f) $args))\n\n        ;; claim the AST before releasing the list containing it\n        (local.set $a ($MEM_VAL0_ptr $f))\n        (drop ($INC_REF $a))\n\n        (local.set $res ($EVAL $a $env))\n\n        ($RELEASE $env)\n        ($RELEASE $a))\n    (else\n      ($THROW_STR_1 \"APPLY of non-function type: %d\\n\" $ftype)\n      (local.set $res 0)))))\n    $res\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; core functions\n\n  (type $fnT (func (param i32) (result i32)))\n\n  (func $equal_Q (param $args i32) (result i32)\n    ($TRUE_FALSE ($EQUAL_Q ($MEM_VAL1_ptr $args)\n                           ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)))))\n\n  (func $throw (param $args i32) (result i32)\n    (global.set $error_type 2)\n    (global.set $error_val ($INC_REF ($MEM_VAL1_ptr $args)))\n    0\n  )\n\n  (func $nil_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($MEM_VAL1_ptr $args))\n                         (global.get $NIL_T))))\n  (func $true_Q (param $args i32) (result i32)\n    (LET $ast ($MEM_VAL1_ptr $args))\n    ($TRUE_FALSE (AND (i32.eq ($TYPE $ast) (global.get $BOOLEAN_T))\n                      (i32.eq ($VAL0 $ast) 1)))\n  )\n  (func $false_Q (param $args i32) (result i32)\n    (LET $ast ($MEM_VAL1_ptr $args))\n    ($TRUE_FALSE (AND (i32.eq ($TYPE $ast) (global.get $BOOLEAN_T))\n                      (i32.eq ($VAL0 $ast) 0)))\n  )\n  (func $number_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($MEM_VAL1_ptr $args))\n                         (global.get $INTEGER_T))))\n  (func $string_Q (param $args i32) (result i32)\n    (LET $mv ($MEM_VAL1_ptr $args))\n    ($TRUE_FALSE (AND (i32.eq ($TYPE $mv) (global.get $STRING_T))\n                      (i32.ne (i32.load8_u ($to_String $mv))\n                              (CHR \"\\x7f\"))))\n  )\n\n  (func $keyword (param $args i32) (result i32)\n    (LET $str ($to_String ($MEM_VAL1_ptr $args)))\n    (if (result i32) (i32.eq (i32.load8_u $str) (CHR \"\\x7f\"))\n      (then ($INC_REF ($MEM_VAL1_ptr $args)))\n      (else\n        (drop ($sprintf_1 (global.get $printf_buf) \"\\x7f%s\" $str))\n        ($STRING (global.get $STRING_T) (global.get $printf_buf))))\n  )\n\n  (func $keyword_Q (param $args i32) (result i32)\n    (LET $ast ($MEM_VAL1_ptr $args))\n    ($TRUE_FALSE (AND (i32.eq ($TYPE $ast) (global.get $STRING_T))\n                      (i32.eq (i32.load8_u ($to_String $ast))\n                              (CHR \"\\x7f\"))))\n  )\n  (func $fn_Q (param $args i32) (result i32)\n    (LET $type ($TYPE ($MEM_VAL1_ptr $args)))\n    ($TRUE_FALSE (OR (i32.eq $type (global.get $FUNCTION_T))\n                     (i32.eq $type (global.get $MALFUNC_T)))))\n  (func $macro_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($MEM_VAL1_ptr $args))\n                         (global.get $MACRO_T))))\n\n  (func $symbol (param $args i32) (result i32)\n    ($STRING (global.get $SYMBOL_T) ($to_String ($MEM_VAL1_ptr $args))))\n\n  (func $symbol_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($MEM_VAL1_ptr $args))\n                         (global.get $SYMBOL_T))))\n\n  (func $core_pr_str (param $args i32) (result i32)\n    ($pr_str_seq $args 1 \" \"))\n  (func $str (param $args i32) (result i32)\n    ($pr_str_seq $args 0 \"\"))\n  (func $prn (param $args i32) (result i32)\n    (LET $res ($pr_str_seq $args 1 \" \"))\n    ($printf_1 \"%s\\n\" ($to_String $res))\n    ($RELEASE $res)\n    ($INC_REF (global.get $NIL))\n  )\n  (func $println (param $args i32) (result i32)\n    (LET $res ($pr_str_seq $args 0 \" \"))\n    ($printf_1 \"%s\\n\" ($to_String $res))\n    ($RELEASE $res)\n    ($INC_REF (global.get $NIL))\n  )\n\n  (func $core_readline (param $args i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $mv 0)\n    (if (i32.eqz ($readline ($to_String ($MEM_VAL1_ptr $args)) $line))\n      (return ($INC_REF (global.get $NIL))))\n    (local.set $mv ($STRING (global.get $STRING_T) $line))\n    $mv\n  )\n\n  (func $read_string (param $args i32) (result i32)\n    ($read_str ($to_String ($MEM_VAL1_ptr $args))))\n\n  (func $slurp (param $args i32) (result i32)\n    (LET $mv   ($STRING_INIT (global.get $STRING_T))\n         $size ($read_file ($to_String ($MEM_VAL1_ptr $args))\n                           ($to_String $mv)))\n    (if (i32.eqz $size)\n      (then\n        ($THROW_STR_1 \"failed to read file '%s'\" ($to_String ($MEM_VAL1_ptr $args)))\n        (return ($INC_REF (global.get $NIL)))))\n    (local.set $mv ($STRING_FINALIZE $mv $size))\n    $mv\n  )\n\n  (func $lt (param $args i32) (result i32)\n    ($TRUE_FALSE\n      (i32.lt_s ($VAL0 ($MEM_VAL1_ptr $args))\n                ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $lte (param $args i32) (result i32)\n    ($TRUE_FALSE\n      (i32.le_s ($VAL0 ($MEM_VAL1_ptr $args))\n                ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $gt (param $args i32) (result i32)\n    ($TRUE_FALSE\n      (i32.gt_s ($VAL0 ($MEM_VAL1_ptr $args))\n                ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $gte (param $args i32) (result i32)\n    ($TRUE_FALSE\n      (i32.ge_s ($VAL0 ($MEM_VAL1_ptr $args))\n                ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $add (param $args i32) (result i32)\n    ($INTEGER\n      (i32.add ($VAL0 ($MEM_VAL1_ptr $args))\n               ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $subtract (param $args i32) (result i32)\n    ($INTEGER\n      (i32.sub ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $multiply (param $args i32) (result i32)\n    ($INTEGER\n      (i32.mul ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $divide (param $args i32) (result i32)\n    ($INTEGER\n      (i32.div_s ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n\n  (func $time_ms (param $args i32) (result i32)\n    ($INTEGER ($get_time_ms)))\n\n  ;;;\n\n  (func $list (param $args i32) (result i32)\n    ($INC_REF $args))\n\n  (func $list_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($DEREF_META ($MEM_VAL1_ptr $args)))\n                         (global.get $LIST_T))))\n\n  (func $vector (param $args i32) (result i32)\n    ($FORCE_SEQ_TYPE (global.get $VECTOR_T) $args))\n\n  (func $vector_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($DEREF_META ($MEM_VAL1_ptr $args)))\n                         (global.get $VECTOR_T))))\n\n  (func $hash_map (param $args i32) (result i32)\n    (LET $type    (global.get $HASHMAP_T)\n         $res     ($MAP_LOOP_START $type)\n         $val2    0\n         $val3    0\n         $c       0\n         ;; push MAP_LOOP stack\n         $ret     $res\n         $current $res\n         $empty   $res)\n\n    ;; READ_SEQ_LOOP\n    (block $done\n      (loop $loop\n        (br_if $done (i32.eqz ($VAL0 $args)))\n\n        (local.set $val2 ($INC_REF ($MEM_VAL1_ptr $args)))\n        (local.set $val3 ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))\n\n        ;; skip two\n        (local.set $args ($MEM_VAL0_ptr ($MEM_VAL0_ptr $args)))\n\n        ;; update the return sequence structure\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (br $loop)\n      )\n    )\n\n    ;; MAP_LOOP_DONE\n    $ret\n  )\n\n\n  (func $hash_map_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($DEREF_META ($MEM_VAL1_ptr $args)))\n                         (global.get $HASHMAP_T))))\n\n  (func $assoc (param $args i32) (result i32)\n    (LET $hm ($MEM_VAL1_ptr $args)\n         $key 0)\n    (local.set $args ($MEM_VAL0_ptr $args))\n\n    (drop ($INC_REF $hm))\n    (block $done\n      (loop $loop\n        (br_if $done (OR (i32.eqz ($VAL0 $args))\n                         (i32.eqz ($VAL0 ($MEM_VAL0_ptr $args)))))\n        (local.set $hm ($ASSOC1 $hm ($MEM_VAL1_ptr $args)\n                                ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))\n        (local.set $args ($MEM_VAL0_ptr ($MEM_VAL0_ptr $args)))\n\n        (br $loop)\n      )\n    )\n    $hm\n  )\n\n  (func $get (param $args i32) (result i32)\n    (LET $hm  ($MEM_VAL1_ptr $args)\n         $key ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)))\n    (if (result i32) (i32.eq $hm (global.get $NIL))\n      (then ($INC_REF (global.get $NIL)))\n      (else ($INC_REF (i32.wrap_i64 ($HASHMAP_GET $hm $key)))))\n  )\n\n  (func $contains_Q (param $args i32) (result i32)\n    (LET $hm  ($MEM_VAL1_ptr $args)\n         $key ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)))\n    ($TRUE_FALSE\n      (if (result i32) (i32.eq $hm (global.get $NIL))\n        (then 0)\n        (else (i32.wrap_i64\n                (i64.shr_u ($HASHMAP_GET $hm $key) (i64.const 32))))))\n  )\n\n  (func $keys_or_vals (param $hm i32 $keys i32) (result i32)\n    (LET $res     ($MAP_LOOP_START (global.get $LIST_T))\n         $val2    0\n         ;; MAP_LOOP stack\n         $ret     $res\n         $current $res\n         $empty   $res)\n\n    (block $done\n      (loop $loop\n        (br_if $done (i32.eqz ($VAL0 $hm)))\n\n        (if $keys\n          (then (local.set $val2 ($INC_REF ($MEM_VAL1_ptr $hm))))\n          (else (local.set $val2 ($INC_REF ($MEM_VAL2_ptr $hm)))))\n\n        ;; next element\n        (local.set $hm ($MEM_VAL0_ptr $hm))\n\n        ;; update the return sequence structure\n        ;; do not release val2 since we are pulling it from the\n        ;; arguments and not creating it here\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE (global.get $LIST_T)\n                                          $empty $current $val2 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n\t(br $loop)\n      )\n    )\n\n    ;; MAP_LOOP_DONE\n    $ret\n  )\n\n  (func $keys (param $args i32) (result i32)\n    ($keys_or_vals ($MEM_VAL1_ptr $args) 1))\n\n  (func $vals (param $args i32) (result i32)\n    ($keys_or_vals ($MEM_VAL1_ptr $args) 0))\n\n  (func $sequential_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (OR (i32.eq ($TYPE ($MEM_VAL1_ptr $args))\n                             (global.get $LIST_T))\n                     (i32.eq ($TYPE ($MEM_VAL1_ptr $args))\n                             (global.get $VECTOR_T)))))\n\n  (func $seq (param $args i32) (result i32)\n    (LET $mv ($MEM_VAL1_ptr $args)\n        $type ($TYPE $mv)\n        $res 0\n        $ret 0\n        $empty 0\n        $current 0\n        $i 0\n        $char 0)\n\n    (if (i32.eq $type (global.get $NIL_T))\n      (then (return (global.get $NIL))))\n\n    (if (AND (i32.ne $type (global.get $LIST_T))\n            (i32.ne $type (global.get $VECTOR_T))\n            (i32.ne $type (global.get $STRING_T)))\n      (then (return (global.get $NIL))))\n\n    (if (i32.eqz ($VAL0 $mv)) (then (return (global.get $NIL))))\n\n    (if (i32.eq $type (global.get $LIST_T))\n      (then (return ($FORCE_SEQ_TYPE (global.get $LIST_T) $mv))))\n\n    (if (i32.eq $type (global.get $VECTOR_T))\n      (then (return ($FORCE_SEQ_TYPE (global.get $LIST_T) $mv))))\n\n    (if (i32.eq $type (global.get $STRING_T))\n      (then\n        (local.set $mv ($to_String $mv))\n        (if (i32.eqz ($strlen $mv)) (then (return (global.get $NIL))))\n        (local.set $res ($MAP_LOOP_START (global.get $LIST_T)))\n        (local.set $empty $res)\n        (local.set $current $res)\n        (local.set $ret $res)\n        (local.set $i 0)\n        (block $done\n          (loop $loop\n            (local.set $char (i32.load8_u (i32.add $mv $i)))\n            (br_if $done (i32.eq $char 0))\n\n            (i32.store8 (global.get $token_buf) $char)\n            (i32.store8 (i32.add (global.get $token_buf) 1) 0)\n\n            (local.set $res\n                      ($MAP_LOOP_UPDATE (global.get $LIST_T)\n                                      $empty $current ($STRING (global.get $STRING_T) (global.get $token_buf)) 0))\n            (if (i32.le_u $current (global.get $EMPTY_LIST))\n              ;; if first element, set return to new element\n              (local.set $ret $res))\n\n            (local.set $i (i32.add $i 1))\n            (local.set $current $res)\n            (br $loop)\n          )\n        )\n        (return $ret)\n      )\n    )\n    (global.get $NIL)\n  )\n\n  (func $cons (param $args i32) (result i32)\n    ($LIST ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)) ($MEM_VAL1_ptr $args)))\n\n  (func $concat (param $args i32) (result i32)\n    (local $last_sl i64)\n    (LET $res     ($INC_REF (global.get $EMPTY_LIST))\n         $current $res\n         $sl      0\n         $last    0\n         $arg     0)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.le_u $args (global.get $EMPTY_HASHMAP)))\n        (local.set $arg ($MEM_VAL1_ptr $args))\n        ;; skip empty elements\n        (if (i32.le_s $arg (global.get $EMPTY_HASHMAP))\n          (then\n            (local.set $args ($MEM_VAL0_ptr $args))\n            (br $loop)))\n        (local.set $last_sl ($SLICE $arg 0 -1))\n        (local.set $sl (i32.wrap_i64 $last_sl))\n        (local.set $last (i32.wrap_i64 (i64.shr_u $last_sl (i64.const 32))))\n        (if (i32.eq $res (global.get $EMPTY_LIST))\n          (then\n            ;; if this is the first element, set the return to the slice\n            (local.set $res $sl))\n          (else\n            ;; otherwise attach current to sliced\n            (i32.store ($VAL0_ptr $current) ($IDX $sl))))\n        ;; update current to end of sliced list\n        (local.set $current $last)\n        ;; release empty since no longer part of the slice\n        ($RELEASE (global.get $EMPTY_LIST))\n\n        (local.set $args ($MEM_VAL0_ptr $args))\n        (br $loop)\n      )\n    )\n    $res\n  )\n\n  (func $vec (param $args i32) (result i32)\n    ($FORCE_SEQ_TYPE (global.get $VECTOR_T) ($MEM_VAL1_ptr $args)))\n\n  (func $nth (param $args i32) (result i32)\n    (LET $a   ($MEM_VAL1_ptr $args)\n         $idx ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)))\n         $i   0)\n\n    (block $done\n      (loop $loop\n        (br_if $done (OR (i32.ge_s $i $idx) (i32.eqz ($VAL0 $a))))\n        (local.set $i (i32.add $i 1))\n        (local.set $a ($MEM_VAL0_ptr $a))\n        (br $loop)\n      )\n    )\n    (if (i32.eq ($VAL0 $a) 0)\n      (then\n        ($THROW_STR_0 \"nth: index out of range\")\n        (return 0)))\n\n    ($INC_REF ($MEM_VAL1_ptr $a))\n  )\n\n  (func $first (param $args i32) (result i32)\n    (LET $res (global.get $NIL)\n         $a   ($MEM_VAL1_ptr $args))\n    (if (AND (i32.ne $a (global.get $NIL))\n             (i32.ne ($VAL0 $a) 0))\n      (local.set $res ($MEM_VAL1_ptr $a)))\n    ($INC_REF $res)\n  )\n\n  (func $rest (param $args i32) (result i32)\n    (LET $a ($MEM_VAL1_ptr $args))\n    (if (i32.eq $a (global.get $NIL))\n      (return ($INC_REF (global.get $EMPTY_LIST))))\n    (if (i32.ne ($VAL0 $a) 0)\n      (local.set $a ($MEM_VAL0_ptr $a)))\n    ($FORCE_SEQ_TYPE (global.get $LIST_T) $a)\n  )\n\n  ;;;\n\n  (func $empty_Q (param $args i32) (result i32)\n    ($TRUE_FALSE ($EMPTY_Q ($MEM_VAL1_ptr $args))))\n\n  (func $count (param $args i32) (result i32)\n    ($INTEGER ($COUNT ($MEM_VAL1_ptr $args))))\n\n  (func $apply (param $args i32) (result i32)\n    (local $last_sl i64)\n    (LET $f          ($MEM_VAL1_ptr $args)\n         $f_args     0\n         $rest_args  ($MEM_VAL0_ptr $args)\n         $rest_count ($COUNT $rest_args)\n         $last       0\n         $res        0)\n\n    (if (i32.le_s $rest_count 1)\n      (then\n        ;; no intermediate args\n        (if (i32.ne ($TYPE ($MEM_VAL1_ptr $rest_args)) (global.get $LIST_T))\n          (then\n            ;; not a list, so convert it first\n            (local.set $f_args ($FORCE_SEQ_TYPE (global.get $LIST_T)\n                                                ($MEM_VAL1_ptr $rest_args))))\n          (else\n            ;; inc ref since we will release after APPLY\n            (local.set $f_args ($INC_REF ($MEM_VAL1_ptr $rest_args))))))\n      (else\n        ;; 1 or more intermediate args\n        (local.set $last_sl ($SLICE $rest_args 0 (i32.sub $rest_count 1)))\n        (local.set $f_args (i32.wrap_i64 $last_sl))\n        (local.set $last (i32.wrap_i64 (i64.shr_u $last_sl (i64.const 32))))\n        ;; release the terminator of the new list (we skip over it)\n        ;; we already checked for an empty list above, so $last is\n        ;; a real non-empty list\n        ($RELEASE ($MEM_VAL0_ptr $last))\n        ;; attach end of slice to final args element\n        (i32.store ($VAL0_ptr $last) ($IDX ($LAST $rest_args)))\n        ))\n\n    (local.set $res ($APPLY $f $f_args))\n\n    ;; release new args\n    ($RELEASE $f_args)\n    $res\n  )\n\n  (func $map (param $args i32) (result i32)\n    (LET $f         ($MEM_VAL1_ptr $args)\n         $rest_args ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))\n         $f_args    0\n         $res       ($MAP_LOOP_START (global.get $LIST_T))\n         ;; push MAP_LOOP stack\n         $ret       $res\n         $current   $res\n         $empty     $res)\n\n    (block $done\n      (loop $loop\n        (br_if $done (i32.eqz ($VAL0 $rest_args)))\n\n        ;; create argument list for apply\n        (local.set $f_args ($ALLOC (global.get $LIST_T)\n                                   (global.get $EMPTY_LIST)\n                                   ($MEM_VAL1_ptr $rest_args)\n                                   0))\n\n        (local.set $res ($APPLY $f $f_args))\n        ($RELEASE $f_args)\n\n        ;; go to the next element\n        (local.set $rest_args ($MEM_VAL0_ptr $rest_args))\n\n        (if (global.get $error_type)\n          (then\n            ;; if error, release the unattached element\n            ($RELEASE $res)\n            (br $done)))\n\n        ;; update the return sequence structure\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE (global.get $LIST_T)\n                                          $empty $current $res 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n\t(br $loop)\n      )\n    )\n\n    ;; MAP_LOOP_DONE\n    $ret\n  )\n\n  ;;;\n\n  (func $with_meta (param $args i32) (result i32)\n    (LET $mv   ($MEM_VAL1_ptr $args)\n         $meta ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)))\n    ;; remove existing metadata first\n    ($ALLOC (global.get $METADATA_T) ($DEREF_META $mv) $meta 0)\n  )\n\n  (func $meta (param $args i32) (result i32)\n    (if (result i32) (i32.eq ($TYPE ($MEM_VAL1_ptr $args)) (global.get $METADATA_T))\n      (then ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL1_ptr $args))))\n      (else ($INC_REF (global.get $NIL)))))\n\n  (func $atom (param $args i32) (result i32)\n    ($ALLOC_SCALAR (global.get $ATOM_T) ($VAL1 $args)))\n\n  (func $atom_Q (param $args i32) (result i32)\n    ($TRUE_FALSE (i32.eq ($TYPE ($MEM_VAL1_ptr $args)) (global.get $ATOM_T))))\n\n  (func $deref (param $args i32) (result i32)\n    ($INC_REF ($MEM_VAL0_ptr ($MEM_VAL1_ptr $args))))\n\n  (func $_reset_BANG (param $atom i32 $val i32) (result i32)\n    ;; release current value since we are about to overwrite it\n    ($RELEASE ($MEM_VAL0_ptr $atom))\n    ;; inc ref by 2 for atom ownership and since we are returning it\n    (drop ($INC_REF ($INC_REF $val)))\n    ;; update the value\n    (i32.store ($VAL0_ptr $atom) ($IDX $val))\n    $val\n  )\n\n  (func $reset_BANG (param $args i32) (result i32)\n    (LET $atom ($MEM_VAL1_ptr $args)\n         $val  ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args)))\n    ($_reset_BANG $atom $val)\n  )\n\n  (func $swap_BANG (param $args i32) (result i32)\n    (LET $atom      ($MEM_VAL1_ptr $args)\n         $f_args    ($MEM_VAL0_ptr $args)\n         $rest_args ($MEM_VAL0_ptr $f_args)\n         ;; add atom value to front of the args list\n         $s_args    ($LIST $rest_args ($MEM_VAL0_ptr $atom)) ;; cons\n         $f         ($MEM_VAL1_ptr $f_args)\n         $res       ($APPLY $f $s_args))\n    ;; release args\n    ($RELEASE $s_args)\n    ;; use reset to update the value\n    (drop ($_reset_BANG $atom $res))\n    ;; but decrease the ref cnt of return by 1 (not sure why)\n    ($RELEASE $res)\n    $res\n  )\n\n  ;;;\n\n  (func $pr_memory_summary (param $args i32) (result i32)\n    ($PR_MEMORY_SUMMARY_SMALL)\n    ($INC_REF (global.get $NIL))\n  )\n\n  (func $nop (param $args i32) (result i32)\n    ($INC_REF (global.get $NIL)))\n\n  (table\n    funcref\n    (elem $nop ;; placeholder for eval which will use 0\n          $equal_Q\n          $throw\n          $nil_Q\n          $true_Q\n          $false_Q\n          $number_Q\n          $string_Q\n          $symbol\n          $symbol_Q\n          $keyword\n          $keyword_Q\n          $fn_Q\n          $macro_Q\n\n          ;; 14\n          $core_pr_str\n          $str\n          $prn\n          $println\n          $core_readline\n          $read_string\n          $slurp\n          $lt\n          $lte\n          $gt\n          $gte\n          $add\n          $subtract\n          $multiply\n          $divide\n          $time_ms\n\n          ;; 30\n          $list\n          $list_Q\n          $vector\n          $vector_Q\n          $hash_map\n          $hash_map_Q\n          $assoc\n          $nop ;; $dissoc\n          $get\n          $contains_Q\n          $keys\n          $vals\n\n          ;; 42\n          $sequential_Q\n          $cons\n          $concat\n          $nth\n          $first\n          $rest\n          $empty_Q\n          $count\n          $apply\n          $map\n          $nop ;; $conj\n          $seq\n\n          ;; 54\n          $with_meta\n          $meta\n          $atom\n          $atom_Q\n          $deref\n          $reset_BANG\n          $swap_BANG\n\n          $pr_memory_summary\n          $vec\n    )\n  )\n\n  (func $add_core_ns (param $env i32)\n    ;;(drop ($ENV_SET_S $env \"eval\"        ($FUNCTION 0)))\n    (drop ($ENV_SET_S $env \"=\"           ($FUNCTION 1)))\n    (drop ($ENV_SET_S $env \"throw\"       ($FUNCTION 2)))\n    (drop ($ENV_SET_S $env \"nil?\"        ($FUNCTION 3)))\n    (drop ($ENV_SET_S $env \"true?\"       ($FUNCTION 4)))\n    (drop ($ENV_SET_S $env \"false?\"      ($FUNCTION 5)))\n    (drop ($ENV_SET_S $env \"number?\"     ($FUNCTION 6)))\n    (drop ($ENV_SET_S $env \"string?\"     ($FUNCTION 7)))\n    (drop ($ENV_SET_S $env \"symbol\"      ($FUNCTION 8)))\n    (drop ($ENV_SET_S $env \"symbol?\"     ($FUNCTION 9)))\n    (drop ($ENV_SET_S $env \"keyword\"     ($FUNCTION 10)))\n    (drop ($ENV_SET_S $env \"keyword?\"    ($FUNCTION 11)))\n    (drop ($ENV_SET_S $env \"fn?\"         ($FUNCTION 12)))\n    (drop ($ENV_SET_S $env \"macro?\"      ($FUNCTION 13)))\n\n    (drop ($ENV_SET_S $env \"pr-str\"      ($FUNCTION 14)))\n    (drop ($ENV_SET_S $env \"str\"         ($FUNCTION 15)))\n    (drop ($ENV_SET_S $env \"prn\"         ($FUNCTION 16)))\n    (drop ($ENV_SET_S $env \"println\"     ($FUNCTION 17)))\n    (drop ($ENV_SET_S $env \"readline\"    ($FUNCTION 18)))\n    (drop ($ENV_SET_S $env \"read-string\" ($FUNCTION 19)))\n    (drop ($ENV_SET_S $env \"slurp\"       ($FUNCTION 20)))\n    (drop ($ENV_SET_S $env \"<\"           ($FUNCTION 21)))\n    (drop ($ENV_SET_S $env \"<=\"          ($FUNCTION 22)))\n    (drop ($ENV_SET_S $env \">\"           ($FUNCTION 23)))\n    (drop ($ENV_SET_S $env \">=\"          ($FUNCTION 24)))\n    (drop ($ENV_SET_S $env \"+\"           ($FUNCTION 25)))\n    (drop ($ENV_SET_S $env \"-\"           ($FUNCTION 26)))\n    (drop ($ENV_SET_S $env \"*\"           ($FUNCTION 27)))\n    (drop ($ENV_SET_S $env \"/\"           ($FUNCTION 28)))\n    (drop ($ENV_SET_S $env \"time-ms\"     ($FUNCTION 29)))\n\n    (drop ($ENV_SET_S $env \"list\"        ($FUNCTION 30)))\n    (drop ($ENV_SET_S $env \"list?\"       ($FUNCTION 31)))\n    (drop ($ENV_SET_S $env \"vector\"      ($FUNCTION 32)))\n    (drop ($ENV_SET_S $env \"vector?\"     ($FUNCTION 33)))\n    (drop ($ENV_SET_S $env \"hash-map\"    ($FUNCTION 34)))\n    (drop ($ENV_SET_S $env \"map?\"        ($FUNCTION 35)))\n    (drop ($ENV_SET_S $env \"assoc\"       ($FUNCTION 36)))\n    (drop ($ENV_SET_S $env \"dissoc\"      ($FUNCTION 37)))\n    (drop ($ENV_SET_S $env \"get\"         ($FUNCTION 38)))\n    (drop ($ENV_SET_S $env \"contains?\"   ($FUNCTION 39)))\n    (drop ($ENV_SET_S $env \"keys\"        ($FUNCTION 40)))\n    (drop ($ENV_SET_S $env \"vals\"        ($FUNCTION 41)))\n\n    (drop ($ENV_SET_S $env \"sequential?\" ($FUNCTION 42)))\n    (drop ($ENV_SET_S $env \"cons\"        ($FUNCTION 43)))\n    (drop ($ENV_SET_S $env \"concat\"      ($FUNCTION 44)))\n    (drop ($ENV_SET_S $env \"nth\"         ($FUNCTION 45)))\n    (drop ($ENV_SET_S $env \"first\"       ($FUNCTION 46)))\n    (drop ($ENV_SET_S $env \"rest\"        ($FUNCTION 47)))\n    (drop ($ENV_SET_S $env \"empty?\"      ($FUNCTION 48)))\n    (drop ($ENV_SET_S $env \"count\"       ($FUNCTION 49)))\n    (drop ($ENV_SET_S $env \"apply\"       ($FUNCTION 50)))\n    (drop ($ENV_SET_S $env \"map\"         ($FUNCTION 51)))\n\n    (drop ($ENV_SET_S $env \"conj\"        ($FUNCTION 52)))\n    (drop ($ENV_SET_S $env \"seq\"         ($FUNCTION 53)))\n\n    (drop ($ENV_SET_S $env \"with-meta\"   ($FUNCTION 54)))\n    (drop ($ENV_SET_S $env \"meta\"        ($FUNCTION 55)))\n    (drop ($ENV_SET_S $env \"atom\"        ($FUNCTION 56)))\n    (drop ($ENV_SET_S $env \"atom?\"       ($FUNCTION 57)))\n    (drop ($ENV_SET_S $env \"deref\"       ($FUNCTION 58)))\n    (drop ($ENV_SET_S $env \"reset!\"      ($FUNCTION 59)))\n    (drop ($ENV_SET_S $env \"swap!\"       ($FUNCTION 60)))\n\n    (drop ($ENV_SET_S $env \"pr-memory-summary\"       ($FUNCTION 61)))\n    (drop ($ENV_SET_S $env \"vec\"         ($FUNCTION 62)))\n  )\n)\n"
  },
  {
    "path": "impls/wasm/debug.wam",
    "content": "(module $debug\n\n  (func $checkpoint_user_memory\n    (global.set $mem_user_start (global.get $mem_unused_start))\n    (global.set $string_mem_user_start (global.get $string_mem_next))\n  )\n\n  (func $CHECK_FREE_LIST (result i32)\n    (LET $first (i32.add\n                  (global.get $mem)\n                  (i32.mul (global.get $mem_free_list) 4))\n         $count 0)\n\n    (block $done\n      (loop $loop\n        (br_if $done\n               (i32.ge_s $first\n                         (i32.add (global.get $mem)\n                                  (i32.mul (global.get $mem_unused_start)\n                                             4))))\n        (local.set $count (i32.add $count ($MalVal_size $first)))\n        (local.set $first (i32.add (global.get $mem) (i32.mul 4 ($VAL0 $first))))\n        (br $loop)\n      )\n    )\n    $count\n  )\n\n  (func $PR_MEMORY_SUMMARY_SMALL\n    (LET $free (i32.sub (global.get $MEM_SIZE)\n                          (i32.mul (global.get $mem_unused_start) 4))\n         $free_list_count ($CHECK_FREE_LIST)\n         $mv              (global.get $NIL)\n         $mem_ref_count   0)\n\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_s $mv (i32.add\n                                     (global.get $mem)\n                                     (i32.mul (global.get $mem_unused_start)\n                                                4))))\n        (if (i32.ne ($TYPE $mv) (global.get $FREE_T))\n          (local.set $mem_ref_count (i32.add $mem_ref_count\n                                             (i32.shr_u\n                                               (i32.load $mv)\n                                               5))))\n        (local.set $mv (i32.add $mv (i32.mul 4 ($MalVal_size $mv))))\n        (br $loop)\n      )\n    )\n\n    ($printf_3 \"Free: %d, Values: %d (refs: %d), Emptys: \"\n               $free\n               (i32.sub\n                 (i32.sub (global.get $mem_unused_start) 1)\n                 $free_list_count)\n               $mem_ref_count)\n    (local.set $mv (global.get $NIL))\n    (block $done\n      (loop $loop\n        (br_if $done (i32.gt_s $mv (global.get $TRUE)))\n        ($printf_1 \"%d,\" (i32.div_s (i32.load $mv) 32))\n        (local.set $mv (i32.add $mv 8))\n        (br $loop)\n      )\n    )\n    (local.set $mv (global.get $EMPTY_LIST))\n    (block $done\n      (loop $loop\n        (br_if $done (i32.gt_s $mv (global.get $EMPTY_HASHMAP)))\n        ($printf_1 \"%d,\" (i32.div_s (i32.load $mv) 32))\n        (local.set $mv (i32.add $mv 12))\n        (br $loop)\n      )\n    )\n    ($print \"\\n\")\n  )\n\n  (func $PR_VALUE (param $fmt i32 $mv i32)\n    (LET $temp ($pr_str $mv 1))\n    ($printf_1 $fmt ($to_String $temp))\n    ($RELEASE $temp)\n  )\n\n  (func $PR_MEMORY_VALUE (param $idx i32) (result i32)\n    ;;; mv = mem + idx\n    (LET $mv ($MalVal_ptr $idx)\n         $type ($TYPE $mv)\n         $size ($MalVal_size $mv)\n         $val0 ($MalVal_val $idx 0))\n\n    ($printf_2 \"%4d: type %2d\" $idx $type)\n\n    (if (i32.eq $type 15)\n      (then ($printf_1 \", size %2d\" $size))\n      (else ($printf_1 \", refs %2d\" ($REFS $mv))))\n\n    (if (OR (i32.eq $type (global.get $STRING_T))\n            (i32.eq $type (global.get $SYMBOL_T)))\n      ;; for strings/symbolx pointers, print hex values\n      (then ($printf_2 \" [%4d|%3ds\" ($MalVal_refcnt_type $idx) $val0))\n      (else ($printf_2 \" [%4d|%4d\" ($MalVal_refcnt_type $idx) $val0)))\n\n    (if (i32.eq $size 2)\n      (then\n        ($print \"|----|----]\"))\n      (else\n        ($printf_1 \"|%4d\" ($MalVal_val $idx 1))\n        (if (i32.eq $size 3)\n          (then ($print \"|----]\"))\n          (else ($printf_1 \"|%4d]\" ($MalVal_val $idx 2))))))\n\n    ;;; printf(\" >> \")\n    ($print \" >> \")\n\n    (block $done (block $unknown\n      (block (block (block (block (block (block (block (block\n      (block (block (block (block (block (block (block (block\n        (br_table 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n                  $unknown $type))\n      ;; 0: nil\n      ($print \"nil\")\n      (br $done))\n      ;; 1: boolean\n      (if (i32.eq $val0 0)\n        ;; true\n        ($print \"false\")\n        ;; false\n        ($print \"true\"))\n      (br $done))\n      ;; 2: integer\n      ($printf_1 \"%d\" $val0)\n      (br $done))\n      ;; 3: float/ERROR\n      ($print \" *** GOT FLOAT *** \")\n      (br $done))\n      ;; 4: string/kw\n      ($printf_1 \"'%s'\" ($to_String $mv))\n      (br $done))\n      ;; 5: symbol\n      ($print ($to_String $mv))\n      (br $done))\n      ;; 6: list\n      (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))\n        (then\n          ($print \"()\"))\n        (else\n          ;;; printf(\"(... %d ...), next: %d\\n\", mv->val[1], mv->val[0])\n          ($printf_2 \"(... %d ...), next: %d\"\n                     ($MalVal_val $idx 1)\n                     ($MalVal_val $idx 0))))\n      (br $done))\n      ;; 7: vector\n      (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))\n        (then\n          ($print \"[]\"))\n        (else\n          ;;; printf(\"[... %d ...], next: %d\\n\", mv->val[1], mv->val[0])val\n          ($printf_2 \"[... %d ...], next: %d\"\n                     ($MalVal_val $idx 1)\n                     ($MalVal_val $idx 0))))\n      (br $done))\n      ;; 8: hashmap\n      (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))\n        (then\n          ($print \"{}\"))\n        (else\n          ;;; printf(\"{... '%s'(%d) : %d ...}\\n\",\n          ;;         to_String(mem + mv->val[1]), mv->val[1], mv->val[2])\n          ($printf_3 \"{... '%s'(%d) : %d ...}\"\n                     ($to_String ($MalVal_ptr ($MalVal_val $idx 1)))\n                     ($MalVal_val $idx 1)\n                     ($MalVal_val $idx 2))))\n      (br $done))\n      ;; 9: function\n      ($print \"function\")\n      (br $done))\n      ;; 10: mal function\n      ($print \"mal function\")\n      (br $done))\n      ;; 11: macro fn\n      ($print \"macro fn\")\n      (br $done))\n      ;; 12: atom\n      ($print \"atom\")\n      (br $done))\n      ;; 13: environment\n      ($print \"environment\")\n      (br $done))\n      ;; 14: metadata\n      ($print \"metadata\")\n      (br $done))\n      ;; 15: FREE\n      ($printf_1 \"FREE next: 0x%x\" $val0)\n      (if (i32.eq $idx (global.get $mem_free_list))\n        ($print \" (free start)\"))\n      (if (i32.eq $val0 (global.get $mem_unused_start))\n        ($print \" (free end)\"))\n      (br $done))\n      ;; 16: unknown\n      ($print \"unknown\")\n    )\n\n    ($print \"\\n\")\n\n    (i32.add $size $idx)\n  )\n\n  (func $PR_STRINGS (param $start i32)\n    (LET $ms  0\n         $idx 0)\n    ($printf_2 \"String - showing %d -> %d:\\n\"\n               $start (i32.sub (global.get $string_mem_next)\n                                 (global.get $string_mem)))\n    (if (i32.le_s (i32.sub (global.get $string_mem_next)\n                             (global.get $string_mem))\n                  $start)\n      (then ($print \" ---\\n\"))\n      (else\n        (local.set $ms (global.get $string_mem))\n        (block $done\n          (loop $loop\n            (br_if $done (i32.ge_u $ms (global.get $string_mem_next)))\n            (local.set $idx (i32.sub $ms (global.get $string_mem)))\n            (if (i32.ge_s $idx $start)\n              ($printf_4 \"%4d: refs %2d, size %2d >> '%s'\\n\"\n                         $idx\n                         (i32.load16_u $ms)\n                         (i32.load16_u (i32.add $ms 2))\n                         (i32.add $ms 4)))\n\n            (local.set $ms (i32.add $ms (i32.load16_u (i32.add $ms 2))))\n            (br $loop)\n          )\n        )))\n  )\n\n  (func $PR_MEMORY (param $start i32 $end i32)\n    (LET $string_start 0\n         $idx          0)\n    (if (i32.lt_s $start 0)\n      (then\n        (local.set $start (global.get $mem_user_start))\n        (local.set $string_start (i32.sub (global.get $string_mem_user_start)\n                                            (global.get $string_mem)))))\n    (if (i32.lt_s $end 0)\n      (local.set $end (global.get $mem_unused_start)))\n    ;;; printf(\"Values - (mem) showing %d -> %d\", start, end)\n    ;;; printf(\" (unused start: %d, free list: %d):\\n\",\n    ;;;        mem_unused_start, mem_free_list)\n    ($printf_4 \"Values - (mem) showing 0x%x -> 0x%x (unused start: 0x%x, free list: 0x%x):\\n\"\n          $start\n          $end\n          (global.get $mem_unused_start)\n          (global.get $mem_free_list))\n\n    (if (i32.le_s $end $start)\n      (then\n        ($print \"  ---\\n\")\n        (local.set $end (global.get $mem_unused_start)))\n      (else\n        (local.set $idx $start)\n        ;;; while (idx < end)\n        (block $loopvals_exit\n          (loop $loopvals\n            (br_if $loopvals_exit (i32.ge_s $idx $end))\n            (local.set $idx ($PR_MEMORY_VALUE $idx))\n            (br $loopvals)\n          )\n        )))\n    ($PR_STRINGS $string_start)\n    ($PR_MEMORY_SUMMARY_SMALL)\n  )\n\n  (func $PR_MEMORY_RAW (param $start i32 $end i32)\n    (block $loop_exit\n      (loop $loop\n        (br_if $loop_exit (i32.ge_u $start $end))\n        ($printf_2 \"0x%x 0x%x\\n\" $start (i32.load $start))\n        (local.set $start (i32.add 4 $start))\n        (br $loop)\n      )\n    )\n  )\n)\n"
  },
  {
    "path": "impls/wasm/env.wam",
    "content": "(module $env\n\n  (func $ENV_NEW (param $outer i32) (result i32)\n    (LET $data ($HASHMAP) ;; allocate the data hashmap\n         $env  ($ALLOC (global.get $ENVIRONMENT_T) $data $outer 0))\n    ;; environment takes ownership\n    ($RELEASE $data)\n    $env\n  )\n\n  (func $ENV_NEW_BINDS (param $outer i32 $binds i32 $exprs i32) (result i32)\n    (LET $env ($ENV_NEW $outer)\n         $key 0)\n\n    ;; process bindings\n    (block $done\n      (loop $loop\n        (br_if $done (i32.eqz ($VAL0 $binds)))\n\n        ;; get/deref the key from binds\n        (local.set $key ($MEM_VAL1_ptr $binds))\n        (if (i32.eqz ($strcmp \"&\" ($to_String $key)))\n          (then\n            ;; ENV_NEW_BIND_VARGS\n            ;; get/deref the key from the next element of binds\n            (local.set $binds ($MEM_VAL0_ptr $binds))\n            (local.set $key ($MEM_VAL1_ptr $binds))\n            ;; the value is the remaining list in exprs\n            (local.set $exprs ($FORCE_SEQ_TYPE (global.get $LIST_T) $exprs))\n            ;; set the binding in the environment data\n            (drop ($ENV_SET $env $key $exprs))\n            ;; list is owned by the environment\n            ($RELEASE $exprs)\n            (br $done))\n          (else\n            ;; ENV_NEW_BIND_1x1\n            ;; set the binding in the environment data\n            (drop ($ENV_SET $env $key ($MEM_VAL1_ptr $exprs)))\n            ;; go to next element of binds and exprs\n            (local.set $binds ($MEM_VAL0_ptr $binds))\n            (local.set $exprs ($MEM_VAL0_ptr $exprs))))\n\n        (br $loop)\n      )\n    )\n    $env\n  )\n\n  (func $ENV_SET (param $env i32 $key i32 $value i32) (result i32)\n    (LET $data ($MEM_VAL0_ptr $env))\n    (i32.store ($VAL0_ptr $env) ($IDX ($ASSOC1 $data $key $value)))\n    $value\n  )\n\n  (func $ENV_SET_S (param $env i32 $key i32 $value i32) (result i32)\n    (LET $data ($MEM_VAL0_ptr $env))\n    (i32.store ($VAL0_ptr $env) ($IDX ($ASSOC1_S $data $key $value)))\n    $value\n  )\n\n  (func $ENV_GET (param $env i32 $key i32) (result i32)\n    ;; Return 0 when the key is not found, but do not set THROW_STR.\n\n    (local $found_res i64)\n    (LET $res 0\n         $data 0)\n\n      (loop $loop\n        (local.set $data ($MEM_VAL0_ptr $env))\n        (local.set $found_res ($HASHMAP_GET $data $key))\n        ;;; if (found)\n        (if (i32.wrap_i64 (i64.shr_u $found_res (i64.const 32)))\n          (then\n            (local.set $res (i32.wrap_i64 $found_res))\n            (return ($INC_REF $res))))\n        (local.set $env ($MEM_VAL1_ptr $env))\n        (if (i32.eq $env (global.get $NIL))\n          (then\n            (return 0)))\n        (br $loop)\n      )\n  )\n\n)\n"
  },
  {
    "path": "impls/wasm/mem.wam",
    "content": "(module $mem\n  (global $MEM_SIZE               i32 1048576)\n  (global $STRING_MEM_SIZE        i32 1048576)\n\n  (global $heap_start             (mut i32) 0)\n  (global $heap_end               (mut i32) 0)\n\n  (global $mem                    (mut i32) 0)\n  (global $mem_unused_start       (mut i32) 0)\n  (global $mem_free_list          (mut i32) 0)\n  (global $mem_user_start         (mut i32) 0)\n\n  (global $string_mem             (mut i32) 0)\n  (global $string_mem_next        (mut i32) 0)\n  (global $string_mem_user_start  (mut i32) 0)\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; General type storage/pointer functions\n\n  (func $VAL0_ptr (param $mv i32) (result i32)\n    (i32.add $mv 4))\n  (func $VAL1_ptr (param $mv i32) (result i32)\n    (i32.add $mv 8))\n\n  (func $VAL0 (param $mv i32) (result i32)\n    (i32.load (i32.add $mv 4)))\n  (func $VAL1 (param $mv i32) (result i32)\n    (i32.load (i32.add $mv 8)))\n\n\n  (func $MEM_VAL0_ptr (param $mv i32) (result i32)\n    (i32.add (global.get $mem)\n             (i32.mul (i32.load (i32.add $mv 4)) 4)))\n  (func $MEM_VAL1_ptr (param $mv i32) (result i32)\n    (i32.add (global.get $mem)\n             (i32.mul (i32.load (i32.add $mv 8)) 4)))\n  (func $MEM_VAL2_ptr (param $mv i32) (result i32)\n    (i32.add (global.get $mem)\n             (i32.mul (i32.load (i32.add $mv 12)) 4)))\n\n  ;; Returns the memory index mem of mv\n  ;; Will usually be used with a load or store by the caller\n  (func $IDX (param $mv i32) (result i32)\n    ;; MalVal memory 64 bit (2 * i32) aligned\n    (i32.div_u (i32.sub $mv (global.get $mem)) 4))\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  ;; Returns the address of 'mem[mv_idx]'\n  (func $MalVal_ptr (param $mv_idx i32) (result i32)\n    ;; MalVal memory 64 bit (2 * i32) aligned\n    ;;; mem[mv_idx].refcnt_type\n    (i32.add (global.get $mem) (i32.mul $mv_idx 4)))\n\n  ;; Returns the address of 'mem[mv_idx].refcnt_type'\n  (func $MalVal_refcnt_type (param $mv_idx i32) (result i32)\n    (i32.load ($MalVal_ptr $mv_idx)))\n\n  (func $TYPE (param $mv i32) (result i32)\n    ;;; type = mv->refcnt_type & 31\n    (i32.and (i32.load $mv) 0x1f)) ;; 0x1f == 31\n\n  (func $SET_TYPE (param $mv i32 $type i32)\n    ;;; type = mv->refcnt_type & 31\n    ;;; mv->refcnt_type += - (mv->refcnt_type & 31) + type\n    (i32.store $mv (i32.or\n                     (i32.and $type 0x1f) ;; 0x1f == 31\n                     (i32.and (i32.load $mv) 0xffffffe1)))\n  )\n\n\n  (func $REFS (param $mv i32) (result i32)\n    ;;; type = mv->refcnt_type & 31\n    (i32.shr_u (i32.load $mv) 5)) ;; / 32\n\n  ;; Returns the address of 'mem[mv_idx].val[val]'\n  ;; Will usually be used with a load or store by the caller\n  (func $MalVal_val_ptr (param $mv_idx i32 $val i32) (result i32)\n    (i32.add (i32.add ($MalVal_ptr $mv_idx) 4)\n             (i32.mul $val 4)))\n\n  ;; Returns the value of 'mem[mv_idx].val[val]'\n  (func $MalVal_val (param $mv_idx i32 $val i32) (result i32)\n    (i32.load ($MalVal_val_ptr $mv_idx $val)))\n\n  (func $MalType_size (param $type i32) (result i32)\n    ;;; if (type <= 5 || type == 9 || type == 12)\n    (if (result i32) (OR (i32.le_u $type 5)\n                (i32.eq $type 9)\n                (i32.eq $type 12))\n      (then 2)\n      (else\n        ;;; else if (type == 8 || type == 10 || type == 11)\n        (if (result i32) (OR (i32.eq $type 8)\n                    (i32.eq $type 10)\n                    (i32.eq $type 11))\n          (then 4)\n          (else 3)))))\n\n  (func $MalVal_size (param $mv i32) (result i32)\n    (LET $type ($TYPE $mv))\n    ;; if (type == FREE_T)\n    (if (result i32) (i32.eq $type (global.get $FREE_T))\n      (then\n        ;;; return (mv->refcnt_type & 0xffe0)>>5\n        (i32.shr_u (i32.and (i32.load $mv) 0xffe0) 5)) ;;; / 32\n      (else\n        ;;; return MalType_size(type)\n        ($MalType_size $type))))\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; init_memory\n\n  (func $init_memory\n    (LET $heap_size 0)\n\n;;    ($print \">>> init_memory\\n\")\n\n    ($init_printf_mem)\n\n    ;; error_str string buffer\n    (global.set $error_str (STATIC_ARRAY 100))\n    ;; reader token string buffer\n    (global.set $token_buf (STATIC_ARRAY 256))\n    ;; printer string buffer\n    (global.set $printer_buf (STATIC_ARRAY 4096))\n\n    (local.set $heap_size (i32.add (global.get $MEM_SIZE)\n                                   (global.get $STRING_MEM_SIZE)))\n    (global.set $heap_start (i32.add (global.get $memoryBase)\n                                     (global.get $S_STRING_END)))\n    (global.set $heap_end (i32.add (global.get $heap_start)\n                                   $heap_size))\n\n    (global.set $mem (global.get $heap_start))\n    (global.set $mem_unused_start 0)\n    (global.set $mem_free_list 0)\n\n    (global.set $string_mem (i32.add (global.get $heap_start)\n                                     (global.get $MEM_SIZE)))\n    (global.set $string_mem_next (global.get $string_mem))\n\n    (global.set $mem_user_start (global.get $mem_unused_start))\n    (global.set $string_mem_user_start (global.get $string_mem_next))\n\n    ;; Empty values\n    (global.set $NIL\n                ($ALLOC_SCALAR (global.get $NIL_T) 0))\n    (global.set $FALSE\n                ($ALLOC_SCALAR (global.get $BOOLEAN_T) 0))\n    (global.set $TRUE\n                ($ALLOC_SCALAR (global.get $BOOLEAN_T) 1))\n    (global.set $EMPTY_LIST\n                ($ALLOC (global.get $LIST_T)\n                      (global.get $NIL) (global.get $NIL) (global.get $NIL)))\n    (global.set $EMPTY_VECTOR\n                ($ALLOC (global.get $VECTOR_T)\n                      (global.get $NIL) (global.get $NIL) (global.get $NIL)))\n    (global.set $EMPTY_HASHMAP\n                ($ALLOC (global.get $HASHMAP_T)\n                      (global.get $NIL) (global.get $NIL) (global.get $NIL)))\n\n;;    ($print \"<<< init_memory\\n\")\n\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; memory management\n\n  (func $ALLOC_INTERNAL (param $type i32\n                               $val1 i32 $val2 i32 $val3 i32) (result i32)\n    (LET $prev (global.get $mem_free_list)\n         $res  (global.get $mem_free_list)\n         $size ($MalType_size $type))\n\n    (block $loop_done\n      (loop $loop\n        ;; res == mem_unused_start\n        (if (i32.eq $res (global.get $mem_unused_start))\n          (then\n            ;; ALLOC_UNUSED\n            ;;; if (res + size > MEM_SIZE)\n            (if (i32.gt_u (i32.add $res $size) (global.get $MEM_SIZE))\n              ;; Out of memory, exit\n              ($fatal 7 \"Out of mal memory!\\n\"))\n            ;;; if (mem_unused_start += size)\n            (global.set $mem_unused_start\n                        (i32.add (global.get $mem_unused_start) $size))\n            ;;; if (prev == res)\n            (if (i32.eq $prev $res)\n              (then\n                (global.set $mem_free_list (global.get $mem_unused_start)))\n              (else\n                ;;; mem[prev].val[0] = mem_unused_start\n                (i32.store\n                  ($MalVal_val_ptr $prev 0)\n                  (global.get $mem_unused_start))))\n            (br $loop_done)))\n        ;; if (MalVal_size(mem+res) == size)\n        (if (i32.eq ($MalVal_size ($MalVal_ptr $res))\n                    $size)\n          (then\n            ;; ALLOC_MIDDLE\n            ;;; if (res == mem_free_list)\n            (if (i32.eq $res (global.get $mem_free_list))\n              ;; set free pointer (mem_free_list) to next free\n              ;;; mem_free_list = mem[res].val[0];\n              (global.set $mem_free_list ($MalVal_val $res 0)))\n            ;;  if (res != mem_free_list)\n            (if (i32.ne $res (global.get $mem_free_list))\n              ;; set previous free to next free\n              ;;; mem[prev].val[0] = mem[res].val[0]\n              (i32.store ($MalVal_val_ptr $prev 0) ($MalVal_val $res 0)))\n            (br $loop_done)))\n        ;;; prev = res\n        (local.set $prev $res)\n        ;;; res = mem[res].val[0]\n        (local.set $res ($MalVal_val $res 0))\n        (br $loop)\n      )\n    )\n    ;; ALLOC_DONE\n    ;;; mem[res].refcnt_type = type + 32\n    (i32.store ($MalVal_ptr $res) (i32.add $type 32))\n    ;; set val to default val1\n    ;;; mem[res].val[0] = val1\n    (i32.store ($MalVal_val_ptr $res 0) $val1)\n    ;;; if (type > 5 && type != 9)\n    (if (AND (i32.gt_u $type 5)\n             (i32.ne $type 9))\n      (then\n        ;; inc refcnt of referenced value\n        ;;; mem[val1].refcnt_type += 32\n        (i32.store ($MalVal_ptr $val1)\n                   (i32.add ($MalVal_refcnt_type $val1) 32))))\n    ;;; if (size > 2)\n    (if (i32.gt_u $size 2)\n      (then\n        ;; inc refcnt of referenced value\n        ;;; mem[val2].refcnt_type += 32\n        (i32.store ($MalVal_ptr $val2)\n                   (i32.add ($MalVal_refcnt_type $val2) 32))\n        ;;; mem[res].val[1] = val2\n        (i32.store ($MalVal_val_ptr $res 1) $val2)))\n    ;;; if (size > 3)\n    (if (i32.gt_u $size 3)\n      (then\n        ;; inc refcnt of referenced value\n        ;;; mem[val3].refcnt_type += 32\n        (i32.store ($MalVal_ptr $val3)\n                   (i32.add ($MalVal_refcnt_type $val3) 32))\n        ;;; mem[res].val[2] = val3\n        (i32.store ($MalVal_val_ptr $res 2) $val3)))\n\n    ;;; return mem + res\n    ($MalVal_ptr $res)\n  )\n\n  (func $ALLOC_SCALAR (param $type i32 $val1 i32) (result i32)\n    ($ALLOC_INTERNAL $type $val1 0 0)\n  )\n\n  (func $ALLOC (param $type i32 $val1 i32 $val2 i32 $val3 i32) (result i32)\n    ($ALLOC_INTERNAL $type ($IDX $val1) ($IDX $val2) ($IDX $val3))\n  )\n\n  (func $RELEASE (param $mv i32)\n    (LET $idx 0 $type 0 $size 0)\n\n    ;; Ignore NULLs\n    ;;; if (mv == NULL) { return; }\n    (if (i32.eqz $mv) (return))\n    ;;; idx = mv - mem\n    (local.set $idx ($IDX $mv))\n    ;;; type = mv->refcnt_type & 31\n    (local.set $type (i32.and (i32.load $mv) 0x1f)) ;; 0x1f == 31\n    ;;; size = MalType_size(type)\n    (local.set $size ($MalType_size $type))\n\n    ;; DEBUG\n    ;;; printf(\">>> RELEASE idx: %d, type: %d, size: %d\\n\", idx, type, size)\n\n    (if (i32.eq 0 $mv)\n      ($fatal 7 \"RELEASE of NULL!\\n\"))\n\n    (if (i32.eq (global.get $FREE_T) $type)\n      (then\n        ($printf_2 \"RELEASE of already free mv: 0x%x, idx: 0x%x\\n\" $mv $idx)\n        ($fatal 1 \"\")))\n    (if (i32.lt_u ($MalVal_refcnt_type $idx) 15)\n      (then\n        ($printf_2 \"RELEASE of unowned mv: 0x%x, idx: 0x%x\\n\" $mv $idx)\n        ($fatal 1 \"\")))\n\n    ;; decrease reference count by one\n    (i32.store ($MalVal_ptr $idx)\n               (i32.sub ($MalVal_refcnt_type $idx) 32))\n\n    ;; nil, false, true, empty sequences\n    (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))\n      (then\n        (if (i32.lt_u ($MalVal_refcnt_type $idx) 32)\n          (then\n            ($printf_2 \"RELEASE of unowned mv: 0x%x, idx: 0x%x\\n\" $mv $idx)\n            ($fatal 1 \"\")))\n        (return)))\n\n    ;; our reference count is not 0, so don't release\n    (if (i32.ge_u ($MalVal_refcnt_type $idx) 32)\n      (return))\n\n    (block $done\n      (block (block (block (block (block (block (block (block (block\n      (br_table 0 0 0 0 1 1 2 2 3 0 4 4 5 6 7 8 8 $type))\n      ;; nil, boolean, integer, float\n      (br $done))\n      ;; string, kw, symbol\n      ;; release string, then FREE reference\n      ($RELEASE_STRING (i32.add (global.get $string_mem) ($VAL0 $mv)))\n      (br $done))\n      ;; list, vector\n      (if (i32.ne ($MalVal_val $idx 0) 0)\n        (then\n          ;; release next element and value\n          ($RELEASE ($MEM_VAL0_ptr $mv))\n          ($RELEASE ($MEM_VAL1_ptr $mv))))\n      (br $done))\n      ;; hashmap\n      (if (i32.ne ($MalVal_val $idx 0) 0)\n        (then\n          ;; release next element, value, and key\n          ($RELEASE ($MEM_VAL0_ptr $mv))\n          ($RELEASE ($MEM_VAL2_ptr $mv))\n          ($RELEASE ($MEM_VAL1_ptr $mv))))\n      (br $done))\n      ;; mal / macro function\n      ;; release ast, params, and environment\n      ($RELEASE ($MEM_VAL2_ptr $mv))\n      ($RELEASE ($MEM_VAL1_ptr $mv))\n      ($RELEASE ($MEM_VAL0_ptr $mv))\n      (br $done))\n      ;; atom\n      ;; release contained/referred value\n      ($RELEASE ($MEM_VAL0_ptr $mv))\n      (br $done))\n      ;; env\n      ;; if outer is set then release outer\n      (if (i32.ne ($MalVal_val $idx 1) 0)\n        ($RELEASE ($MEM_VAL1_ptr $mv)))\n      ;; release the env data (hashmap)\n      ($RELEASE ($MEM_VAL0_ptr $mv))\n      (br $done))\n      ;; metadata\n      ;; release object and metdata object\n      ($RELEASE ($MEM_VAL0_ptr $mv))\n      ($RELEASE ($MEM_VAL1_ptr $mv))\n      (br $done))\n      ;; default/unknown\n    )\n\n    ;; FREE, free the current element\n\n    ;; set type(FREE/15) and size\n    ;;; mv->refcnt_type = size*32 + FREE_T\n    (i32.store $mv (i32.add (i32.mul $size 32) (global.get $FREE_T)))\n    (i32.store ($MalVal_val_ptr $idx 0) (global.get $mem_free_list))\n    (global.set $mem_free_list $idx)\n    (if (i32.ge_u $size 3) (i32.store ($MalVal_val_ptr $idx 1) 0))\n    (if (i32.eq $size 4) (i32.store ($MalVal_val_ptr $idx 2) 0))\n  )\n\n  ;; find string in string memory or 0 if not found\n  (func $FIND_STRING (param $str i32) (result i32)\n    (LET $ms (global.get $string_mem))\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_s $ms (global.get $string_mem_next)))\n        (if (i32.eqz ($strcmp $str (i32.add $ms 4)))\n          (return $ms))\n\n        (local.set $ms (i32.add $ms (i32.load16_u (i32.add $ms 2))))\n        (br $loop)\n      )\n    )\n    0\n  )\n\n  ;; str is a NULL terminated string\n  ;; size is number of characters in the string not including the\n  ;; trailing NULL\n  (func $ALLOC_STRING (param $str i32 $size i32 $intern i32) (result i32)\n    (LET $ms 0)\n\n    ;; search for matching string in string_mem\n    (if $intern\n      (then\n        (local.set $ms ($FIND_STRING $str))\n        (if $ms\n          (then\n            ;;; ms->refcnt += 1\n            (i32.store16 $ms (i32.add (i32.load16_u $ms) 1))\n            (return $ms)))))\n\n    ;; no existing matching string so create a new one\n    (local.set $ms (global.get $string_mem_next))\n    (i32.store16 $ms 1)\n    ;;; ms->size = sizeof(MalString)+size+1\n    (i32.store16 offset=2 $ms (i32.add (i32.add 4 $size) 1))\n    ($memmove (i32.add $ms 4) $str (i32.add $size 1))\n    ;;; string_mem_next = (void *)ms + ms->size\n    (global.set $string_mem_next\n                ;;(i32.add $ms (i32.load16_u (i32.add $ms 2))))\n                (i32.add $ms (i32.load16_u offset=2 $ms)))\n\n;;($printf_2 \"ALLOC_STRING 6 ms 0x%x, refs: %d\\n\" $ms (i32.load16_u $ms))\n    $ms\n  )\n\n  (func $RELEASE_STRING (param $ms i32)\n    (LET $size 0 $next 0 $ms_idx 0 $idx 0 $type 0 $mv 0)\n\n    (if (i32.le_s (i32.load16_u $ms) 0)\n      (then\n        ($printf_2 \"Release of already free string: %d (0x%x)\\n\"\n                   (i32.sub $ms (global.get $string_mem)) $ms)\n        ($fatal 1 \"\")))\n\n    ;;; size = ms->size\n    (local.set $size (i32.load16_u (i32.add $ms 2)))\n    ;;; *next = (void *)ms + size\n    (local.set $next (i32.add $ms $size))\n\n    ;;; ms->refcnt -= 1\n    (i32.store16 $ms (i32.sub (i32.load16_u $ms) 1))\n\n    (if (i32.eqz (i32.load16_u $ms))\n      (then\n        (if (i32.gt_s (global.get $string_mem_next) $next)\n          (then\n            ;; If no more references to this string then free it up by\n            ;; shifting up every string afterwards to fill the gap\n            ;; (splice).\n            ($memmove $ms $next (i32.sub (global.get $string_mem_next)\n                                           $next))\n\n            ;; Scan the mem values for string types after the freed\n            ;; string and shift their indexes by size\n            (local.set $ms_idx (i32.sub $ms (global.get $string_mem)))\n            (local.set $idx ($IDX (global.get $EMPTY_HASHMAP)))\n            (loop $loop\n              (local.set $mv ($MalVal_ptr $idx))\n              (local.set $type ($TYPE $mv))\n              (if (AND (i32.gt_s ($VAL0 $mv) $ms_idx)\n                       (OR (i32.eq $type (global.get $STRING_T))\n                           (i32.eq $type (global.get $SYMBOL_T))))\n                (i32.store ($VAL0_ptr $mv) (i32.sub ($VAL0 $mv) $size)))\n              (local.set $idx (i32.add $idx ($MalVal_size $mv)))\n\n              (br_if $loop (i32.lt_s $idx (global.get $mem_unused_start)))\n            )))\n\n        (global.set $string_mem_next\n                    (i32.sub (global.get $string_mem_next) $size))))\n  )\n)\n"
  },
  {
    "path": "impls/wasm/node_readline.js",
    "content": "// IMPORTANT: choose one\nvar RL_LIB = \"libreadline\";  // NOTE: libreadline is GPL\n//var RL_LIB = \"libedit\";\n\nvar HISTORY_FILE = require('path').join(process.env.HOME, '.mal-history');\n\nvar rlwrap = {}; // namespace for this module in web context\n\nvar ffi = require('ffi-napi'),\n    fs = require('fs');\n\nvar rllib = ffi.Library(RL_LIB, {\n    'readline':    [ 'string', [ 'string' ] ],\n    'add_history': [ 'int',    [ 'string' ] ]});\n\nvar rl_history_loaded = false;\n\nexports.readline = rlwrap.readline = function(prompt) {\n    prompt = typeof prompt !== 'undefined' ? prompt : \"user> \";\n\n    if (!rl_history_loaded) {\n        rl_history_loaded = true;\n        var lines = [];\n        if (fs.existsSync(HISTORY_FILE)) {\n            lines = fs.readFileSync(HISTORY_FILE).toString().split(\"\\n\");\n        }\n        // Max of 2000 lines\n        lines = lines.slice(Math.max(lines.length - 2000, 0));\n        for (var i=0; i<lines.length; i++) {\n            if (lines[i]) { rllib.add_history(lines[i]); }\n        }\n    }\n\n    var line = rllib.readline(prompt);\n    if (line) {\n        rllib.add_history(line);\n        try {\n            fs.appendFileSync(HISTORY_FILE, line + \"\\n\");\n        } catch (exc) {\n            // ignored\n        }\n    }\n\n    return line;\n};\nvar readline = exports;\n"
  },
  {
    "path": "impls/wasm/package.json",
    "content": "{\n    \"name\": \"mal\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Make a Lisp (mal) language implemented in WebAssembly\",\n    \"dependencies\": {\n        \"@kanaka/wamp\": \"1.0.9\",\n        \"ffi-napi\": \"^4.0.3\",\n        \"text-encoding\": \"0.6.4\"\n    }\n}\n"
  },
  {
    "path": "impls/wasm/platform_direct.wam",
    "content": "(module $platform_direct\n\n  (import \"env\" \"memory\" (memory 256))\n  (import \"env\" \"memoryBase\" (global $memoryBase i32))\n\n  (import \"env\" \"exit\"        (func $lib_exit (param i32)))\n  (import \"env\" \"printline\"   (func $lib_printline (param i32) (result i32)))\n  (import \"env\" \"readline\"    (func $lib_readline (param i32 i32 i32) (result i32)))\n  (import \"env\" \"read_file\"   (func $lib_read_file (param i32 i32) (result i32)))\n  (import \"env\" \"get_time_ms\" (func $lib_get_time_ms (result i32)))\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $fatal (param $code i32 $msg i32)\n    ($print $msg)\n    ($lib_exit $code)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $print (param $addr i32)\n    (drop ($lib_printline $addr))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $readline (param $prompt i32 $buf i32) (result i32)\n    ;; TODO: don't hardcode count to 200\n    ($lib_readline $prompt $buf 200)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $read_file (param $path i32 $buf i32) (result i32)\n    (LET $size ($lib_read_file $path $buf))\n    ;; Add null to string\n    (i32.store8 (i32.add $buf $size) 0)\n    (i32.add $size 1)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $get_time_ms (result i32)\n    ($lib_get_time_ms)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $entry (param $argc i32 $argv i32)\n    ($init_memory)\n    ($lib_exit ($main $argc $argv))\n  )\n\n  (export \"_main\" (func $entry))\n\n)\n"
  },
  {
    "path": "impls/wasm/platform_libc.wam",
    "content": "(module $platform_libc\n\n  (import \"env\" \"memory\" (memory 256))\n  (import \"env\" \"memoryBase\" (global $memoryBase i32))\n\n  (import \"env\" \"exit\"        (func $lib_exit (param i32)))\n  (import \"env\" \"stdout\"      (global $lib_stdout i32))\n  (import \"env\" \"fputs\"       (func $lib_fputs (param i32 i32) (result i32)))\n  (import \"env\" \"readline\"    (func $lib_readline (param i32) (result i32)))\n  (import \"env\" \"free\"        (func $lib_free (param i32)))\n  (import \"env\" \"add_history\" (func $lib_add_history (param i32)))\n\n  ;; read_file defintions / FFI information\n  (global $STAT_SIZE i32 88)\n  (global $STAT_ST_SIZE_OFFSET i32 44)\n  (global $STAT_VER_LINUX i32 3)\n  (global $O_RDONLY i32 0)\n  (import \"env\" \"open\" (func $lib_open (param i32 i32 i32) (result i32)))\n  (import \"env\" \"read\" (func $lib_read (param i32 i32 i32) (result i32)))\n  (import \"env\" \"__fxstat\" (func $lib___fxstat (param i32 i32 i32) (result i32)))\n  (global $TIMEVAL_SIZE i32 8)\n  (global $TV_SEC_OFFSET i32 0)\n  (global $TV_USEC_OFFSET i32 4)\n  (import \"env\" \"gettimeofday\" (func $lib_gettimeofday (param i32 i32) (result i32)))\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $fatal (param $code i32 $msg i32)\n    ($print $msg)\n    ($lib_exit $code)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $print (param $addr i32)\n    (drop ($lib_fputs $addr (global.get $lib_stdout))))\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $readline (param $prompt i32 $buf i32) (result i32)\n    (LET $line ($lib_readline $prompt)\n         $len  0)\n\n    (if $line\n      (then\n        ($lib_add_history $line)\n        (local.set $len ($strlen $line))\n        ($memmove $buf $line $len)\n        ($lib_free $line)))\n    (i32.store8 (i32.add $buf $len) (CHR \"\\x00\"))\n    (return (if (result i32) $line 1 0))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  ;; Returns malloc'd string. Must be free by caller\n  (func $read_file (param $path i32 $buf i32) (result i32)\n    (LET $fst     (STATIC_ARRAY 100)  ;; at least STAT_SIZE\n         $fd      ($lib_open $path (global.get $O_RDONLY) 0)\n         $st_size 0\n         $sz      0)\n\n    (if (i32.lt_s $fd 0)\n      (then\n        ($printf_1 \"ERROR: slurp failed to open '%s'\\n\" $path)\n        (return 0)))\n    (if (i32.lt_s ($lib___fxstat (global.get $STAT_VER_LINUX) $fd $fst) 0)\n      (then\n        ($printf_1 \"ERROR: slurp failed to stat '%s'\\n\" $path)\n        (return 0)))\n    (local.set $st_size (i32.load\n                          (i32.add $fst (global.get $STAT_ST_SIZE_OFFSET))))\n    (local.set $sz ($lib_read $fd $buf $st_size))\n    (if (i32.ne $sz $st_size)\n      (then\n        ($printf_1 \"ERROR: slurp failed to stat '%s'\\n\" $path)\n        (return 0)))\n    ;; Add null to string\n    (i32.store8 (i32.add $buf $st_size) 0)\n    (i32.add 1 $st_size)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n\n  (func $get_time_ms (result i32)\n    (LET $tv    (STATIC_ARRAY 10) ;; at least TIMEVAL_SIZE\n         $secs  0\n         $usecs 0\n         $msecs 0)\n    (drop ($lib_gettimeofday $tv 0))\n    (local.set $secs (i32.load (i32.add $tv (global.get $TV_SEC_OFFSET))))\n    ;; subtract 30 years to make sure secs is positive and can be\n    ;; multiplied by 1000\n    (local.set $secs (i32.sub $secs 0x38640900))\n    (local.set $usecs (i32.load (i32.add $tv (global.get $TV_USEC_OFFSET))))\n    (local.set $msecs (i32.add (i32.mul $secs 1000)\n                               (i32.div_u $usecs 1000)))\n    $msecs\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $entry (param $argc i32 $argv i32)\n    ($init_memory)\n    ($lib_exit ($main $argc $argv))\n  )\n\n  (export \"_main\" (func $entry))\n\n)\n"
  },
  {
    "path": "impls/wasm/platform_wasi.wam",
    "content": "(module $platform_wasi\n\n  (memory 256)\n  (export \"memory\" (memory 0))\n\n  (global $WASI_RIGHT_FD_READ i64 (i64.const 2))\n  (global $WASI_ESUCCESS i32 0)\n  (global $WASI_EBADF    i32 8)\n  (global $WASI_PREOPENTYPE_DIR i32 0)\n\n  (import \"wasi_unstable\" \"args_get\"       (func $args_get (param i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"args_sizes_get\" (func $args_sizes_get (param i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"clock_time_get\" (func $clock_time_get (param i32 i64 i32) (result i32)))\n  (import \"wasi_unstable\" \"fd_prestat_get\"      (func $fd_prestat_get (param i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"fd_prestat_dir_name\" (func $fd_prestat_dir_name (param i32 i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"fd_read\"        (func $fd_read (param i32 i32 i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"fd_write\"       (func $fd_write (param i32 i32 i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"path_open\"      (func $path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))\n  (import \"wasi_unstable\" \"proc_exit\"      (func $proc_exit (param i32)))\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $fatal (param $code i32 $msg i32)\n    ($print $msg)\n    ($proc_exit $code)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $print (param $addr i32)\n    (LET $ret 0\n         $nwritten_ptr (STATIC_ARRAY 4 4)\n         $iovec (STATIC_ARRAY 8 8))\n    (i32.store $iovec $addr)\n    (i32.store offset=4 $iovec ($strlen $addr))\n    (local.set $ret ($fd_write 1 $iovec 1 $nwritten_ptr))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $readline (param $prompt i32 $buf i32) (result i32)\n    (LET $ret 0\n         $nread_ptr (STATIC_ARRAY 4 4)\n         $iovec (STATIC_ARRAY 8 8))\n    ($print $prompt)\n    (i32.store $iovec $buf)\n    (i32.store offset=4 $iovec 200) ;; TODO: not hardcoded length\n    (local.set $ret ($fd_read 0 $iovec 1 $nread_ptr))\n    (if (i32.le_s (i32.load $nread_ptr) 0)\n      (return 0))\n    ;; Replace ending newline with NULL\n    ;; NOTE: oddly, there isn't always a newline so check first\n    ;; Specifically, this input chops too much:\n    ;;   (abcd abcdefg (abc (n) (if (> n 0) (+ n (abcdefg  (- n 1))) 0)))\n    (if (i32.eq (CHR \"\\n\")\n                (i32.load8_u (i32.add $buf (i32.sub (i32.load $nread_ptr) 1)) 0))\n      (i32.store8 (i32.add $buf (i32.sub (i32.load $nread_ptr) 1)) 0))\n    1\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $read_file (param $path i32 $buf i32) (result i32)\n    (LET $orig_path $path\n         $ret 0\n         $prestat_ptr (STATIC_ARRAY 8 4)\n         $pr_type 0\n         $pr_name_len 0\n         $prepath (STATIC_ARRAY 1024)\n         $dirfd -1\n         $fd 3\n         $fd_ptr (STATIC_ARRAY 4 4)\n         $nread_ptr (STATIC_ARRAY 4 4)\n         $iovec (STATIC_ARRAY 8 8))\n\n    ;; Find the pre-opened dir fd with the same prefix as the our path\n    ;; following the algorithm at:\n    ;; https://github.com/CraneStation/wasi-sysroot/blob/1cc98f27f5ab8afdc033e16eac8799ee606eb769/libc-bottom-half/crt/crt1.c#L71\n    ;; The matching dir fd is then used to open and read the path.\n    (block $loop_done\n      (loop $loop\n        ;; prestat the fd from 3 onward until EBADF is returned\n        (local.set $ret ($fd_prestat_get $fd $prestat_ptr))\n        (if (i32.eq (global.get $WASI_EBADF) $ret)\n          (br $loop_done))\n        (if (i32.ne (global.get $WASI_ESUCCESS) $ret)\n          (then\n            (local.set $fd (i32.add 1 $fd))\n            (br $loop)))\n          ;;(br $loop_done))\n        (local.set $pr_type (i32.load $prestat_ptr))\n        (local.set $pr_name_len (i32.load offset=4 $prestat_ptr))\n        ;; Read the pre-opened path name\n        (local.set $ret ($fd_prestat_dir_name $fd $prepath $pr_name_len))\n        (if (i32.ne (global.get $WASI_ESUCCESS) $ret)\n          (br $loop_done))\n        ;; if pr_name_len includes a null, exclude it from the compare\n        ;;($printf_2 \"here1 pr_name_len: %d, char is %d\\n\" $pr_name_len (i32.load8_u (i32.add $prepath (i32.sub $pr_name_len 1))))\n        (if (i32.eqz (i32.load8_u (i32.add $prepath (i32.sub $pr_name_len 1))))\n          (then\n            (local.set $pr_name_len (i32.sub $pr_name_len 1))))\n        ;; if it is a dir and the path prefix matches, use it\n        ;;($printf_5 \"fd: %d, ret: %d, pr_type: %d, pr_name_len: %d, prepath: %s\\n\"\n        ;;           $fd $ret $pr_type $pr_name_len $prepath)\n        (if (AND (i32.eq $pr_type (global.get $WASI_PREOPENTYPE_DIR))\n                 (i32.eqz ($strncmp $prepath $path $pr_name_len)))\n          (then\n            (local.set $path (i32.add $pr_name_len $path))\n            (local.set $dirfd $fd)\n            (br $loop_done)))\n        (local.set $fd (i32.add 1 $fd))\n        (br $loop)\n      )\n    )\n\n    ;;($printf_3 \"final dirfd: %d, adjusted path: %s (%d)\\n\" $dirfd $path ($strlen $path))\n\n    (if (i32.eq $dirfd -1)\n      (then\n        ($printf_1 \"ERROR: could not find permission for '%s'\\n\" $orig_path)\n        (return 0)))\n\n    (local.set $ret ($path_open $dirfd\n                                1 ;; dirflags (symlink follow)\n                                $path\n                                ($strlen $path)\n                                0 ;; o_flags\n                                (global.get $WASI_RIGHT_FD_READ)\n                                (global.get $WASI_RIGHT_FD_READ)\n                                0 ;; fs_flags\n                                $fd_ptr))\n    (if (i32.ne (global.get $WASI_ESUCCESS) $ret)\n      (then\n        ($printf_2 \"ERROR: failed to open '%s', error %d\\n\" $orig_path $ret)\n        (return 0)))\n\n    (i32.store $iovec $buf)\n    ;; TODO: use stat result instead of not hardcoded length\n    (i32.store offset=4 $iovec 16384)\n    (local.set $ret ($fd_read (i32.load $fd_ptr) $iovec 1 $nread_ptr))\n    (if (i32.ne (global.get $WASI_ESUCCESS) $ret)\n      (then\n        ($printf_2 \"ERROR: failed to read '%s', error %d\\n\" $orig_path $ret)\n        (return 0)))\n\n    ;; Add null to string\n    (i32.store8 (i32.add $buf (i32.load $nread_ptr)) 0)\n    (i32.add 1 (i32.load $nread_ptr))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $get_time_ms (result i32)\n    (LET $tv (STATIC_ARRAY 8 8))\n    (drop (call $clock_time_get 0 (i64.const 0) $tv))\n    (i32.wrap_i64\n      ;; convert nanoseconds to milliseconds\n      (i64.div_u (i64.load $tv) (i64.const 1000000)))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  ;; Returns an i64 with argc in high 32 and argv in low 32.\n  ;; String memory is: argv + (argc * 4)\n  (func $get_argc_argv (result i64)\n    (LET $argc_ptr (STATIC_ARRAY 4 4)\n         $argv_size_ptr (STATIC_ARRAY 4 4)\n         $argc 0\n         $argv (STATIC_ARRAY 1024 4))\n    (drop ($args_sizes_get $argc_ptr $argv_size_ptr))\n    (local.set $argc (i32.load $argc_ptr))\n    (if (i32.gt_u (i32.add (i32.mul 4 $argc)\n                           (i32.load $argv_size_ptr))\n                  1024)\n      ($fatal 2 \"Command line arguments memory exceeds 1024 bytes\"))\n    (drop ($args_get $argv (i32.add $argv (i32.mul 4 $argc))))\n    (i64.or (i64.shl (i64.extend_i32_u $argc) (i64.const 32))\n            (i64.extend_i32_u $argv))\n  )\n\n  (func $entry\n    (local $argc_argv i64)\n    ($init_memory)\n    (local.set $argc_argv ($get_argc_argv))\n    ($proc_exit\n      ($main (i32.wrap_i64 (i64.shr_u $argc_argv (i64.const 32)))\n             (i32.wrap_i64 $argc_argv)))\n  )\n  ;;(start $entry)\n\n  (export \"_start\" (func $entry))\n\n)\n"
  },
  {
    "path": "impls/wasm/printer.wam",
    "content": "(module $printer\n\n  (global $printer_buf   (mut i32) 0)\n\n  (func $pr_str_val (param $res i32 $mv i32 $print_readably i32) (result i32)\n    (LET $type ($TYPE $mv)\n         $val0 ($VAL0 $mv)\n         $sval 0)\n\n    ;;; switch(type)\n    (block $done\n      (block $default\n      (block (block (block (block (block (block (block (block\n      (block (block (block (block (block (block (block (block\n      (br_table 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $type))\n      ;; 0: nil\n      ($memmove $res \"nil\" 4)\n      (local.set $res (i32.add 3 $res))\n      (br $done))\n      ;; 1: boolean\n      (if (i32.eq $val0 0)\n        (then\n          ;; false\n          ($memmove $res \"false\" 6)\n          (local.set $res (i32.add 5 $res)))\n        (else\n          ;; true\n          ($memmove $res \"true\" 5)\n          (local.set $res (i32.add 4 $res))))\n      (br $done))\n      ;; 2: integer\n      (local.set $res ($sprintf_1 $res \"%d\" $val0))\n      (br $done))\n      ;; 3: float/ERROR\n      (local.set $res ($sprintf_1 $res \"%d\" \" *** GOT FLOAT *** \"))\n      (br $done))\n      ;; 4: string/kw\n      (local.set $sval ($to_String $mv))\n      (if (i32.eq (i32.load8_u $sval) (CHR \"\\x7f\"))\n        (then\n          (local.set $res ($sprintf_1 $res \":%s\" (i32.add $sval 1))))\n      (else (if $print_readably\n        (then\n          ;; escape backslashes, quotes, and newlines\n          (local.set $res ($sprintf_1 $res \"\\\"\" 0))\n          (local.set $res (i32.add $res ($REPLACE3 $res ($to_String $mv)\n                                                   \"\\\\\" \"\\\\\\\\\"\n                                                   \"\\\"\" \"\\\\\\\"\"\n                                                   \"\\n\" \"\\\\n\")))\n          (local.set $res ($sprintf_1 $res \"\\\"\" 0)))\n      (else\n        (local.set $res ($sprintf_1 $res \"%s\" $sval))))))\n      (br $done))\n      ;; 5: symbol\n      (local.set $res ($sprintf_1 $res \"%s\" ($to_String $mv)))\n      (br $done))\n      ;; 6: list, fallthrouogh\n      )\n      ;; 7: vector, fallthrough\n      )\n      ;; 8: hashmap\n      (local.set\n        $res ($sprintf_1 $res \"%c\"\n                         (if (result i32) (i32.eq $type (global.get $LIST_T))\n                           (then (CHR \"(\"))\n                           (else (if (result i32) (i32.eq $type (global.get $VECTOR_T))\n                                   (then (CHR \"[\"))\n                                   (else (CHR \"{\")))))))\n      ;; PR_SEQ_LOOP\n      ;;; while (VAL0(mv) != 0)\n      (block $done_seq\n        (loop $seq_loop\n          (br_if $done_seq (i32.eq ($VAL0 $mv) 0))\n          ;;; res = pr_str_val(res, MEM_VAL1(mv), print_readably)\n          (local.set $res ($pr_str_val $res ($MEM_VAL1_ptr $mv) $print_readably))\n\n          ;; if this is a hash-map, print the next element\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              ;;; res += snprintf(res, 2, \" \")\n              (local.set $res ($sprintf_1 $res \" \" 0))\n              (local.set $res ($pr_str_val $res ($MEM_VAL2_ptr $mv)\n                                           $print_readably))))\n          ;;; mv = MEM_VAL0(mv)\n          (local.set $mv ($MEM_VAL0_ptr $mv))\n          ;;; if (VAL0(mv) != 0)\n          (if (i32.ne ($VAL0 $mv) 0)\n            ;;; res += snprintf(res, 2, \" \")\n            (local.set $res ($sprintf_1 $res \" \" 0)))\n          (br $seq_loop)\n        )\n      )\n\n      (local.set\n        $res ($sprintf_1 $res \"%c\"\n                         (if (result i32) (i32.eq $type (global.get $LIST_T))\n                           (then (CHR \")\"))\n                           (else (if (result i32) (i32.eq $type (global.get $VECTOR_T))\n                                   (then (CHR \"]\"))\n                                   (else (CHR \"}\")))))))\n      (br $done))\n      ;; 9: function\n      ($memmove $res \"#<fn ...>\" 10)\n      (local.set $res (i32.add 9 $res))\n      (br $done))\n      ;; 10: mal function\n      ($memmove $res \"(fn* \" 6)\n      (local.set $res (i32.add 5 $res))\n      (local.set $res ($pr_str_val $res ($MEM_VAL1_ptr $mv) $print_readably))\n      ($memmove $res \" \" 2)\n      (local.set $res (i32.add 1 $res))\n      (local.set $res ($pr_str_val $res ($MEM_VAL0_ptr $mv) $print_readably))\n      ($memmove $res \")\" 2)\n      (local.set $res (i32.add 1 $res))\n      (br $done))\n      ;; 11: macro fn\n      ($memmove $res \"#<macro ...>\" 13)\n      (local.set $res (i32.add 12 $res))\n      (br $done))\n      ;; 12: atom\n      ($memmove $res \"(atom \" 7)\n      (local.set $res (i32.add 6 $res))\n      (local.set $res ($pr_str_val $res ($MEM_VAL0_ptr $mv) $print_readably))\n      ($memmove $res \")\" 2)\n      (local.set $res (i32.add 1 $res))\n      (br $done))\n      ;; 13: environment\n      ($memmove $res \"#<mem ...>\" 11)\n      (local.set $res (i32.add 10 $res))\n      (br $done))\n      ;; 14: metadata\n      ;; recur on object itself\n      (local.set $res ($pr_str_val $res ($MEM_VAL0_ptr $mv) $print_readably))\n      (br $done))\n      ;; 15: FREE\n      ($memmove $res \"#<free ...>\" 12)\n      (local.set $res (i32.add 11 $res))\n      (br $done))\n      ;; 16: default\n      ($memmove $res \"#<unknown>\" 11)\n      (local.set $res (i32.add 10 $res))\n    )\n\n    $res\n  )\n\n  (func $pr_str_internal (param $seq i32) (param $mv i32)\n        (param $print_readably i32) (param $sep i32) (result i32)\n    (LET $res     ($STRING_INIT (global.get $STRING_T))\n         $res_str ($to_String $res))\n\n    (if $seq\n      (then\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $mv)))\n            (local.set $res_str ($pr_str_val $res_str ($MEM_VAL1_ptr $mv) $print_readably))\n            (local.set $mv ($MEM_VAL0_ptr $mv))\n            (if (i32.ne ($VAL0 $mv) 0)\n              (local.set $res_str ($sprintf_1 $res_str \"%s\" $sep)))\n            (br $loop)\n          )\n        ))\n      (else\n        (local.set $res_str ($pr_str_val $res_str $mv $print_readably))))\n\n    (local.set $res ($STRING_FINALIZE $res (i32.sub $res_str ($to_String $res))))\n\n    $res\n  )\n\n  (func $pr_str (param $mv i32 $print_readably i32) (result i32)\n    ($pr_str_internal 0 $mv $print_readably \"\")\n  )\n\n  (func $pr_str_seq (param $mv i32 $print_readably i32 $sep i32) (result i32)\n    ($pr_str_internal 1 $mv $print_readably $sep)\n  )\n\n  (export \"pr_str\" (func $pr_str))\n\n)\n"
  },
  {
    "path": "impls/wasm/printf.wam",
    "content": "(module $printf\n\n  (global $printf_buf (mut i32) 0)\n\n  (func $init_printf_mem\n    ;; sprintf static buffer\n    (global.set $printf_buf (STATIC_ARRAY 256))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $printf_1 (param $fmt i32) (param $v0 i32)\n    (drop ($sprintf_6 (global.get $printf_buf) $fmt $v0 0 0 0 0 0))\n    ($print (global.get $printf_buf))\n  )\n\n  (func $printf_2 (param $fmt i32 $v0 i32 $v1 i32)\n    (drop ($sprintf_6 (global.get $printf_buf) $fmt $v0 $v1 0 0 0 0))\n    ($print (global.get $printf_buf))\n  )\n\n  (func $printf_3 (param $fmt i32)\n        (param $v0 i32) (param $v1 i32) (param $v2 i32)\n    (drop ($sprintf_6 (global.get $printf_buf) $fmt $v0 $v1 $v2 0 0 0))\n    ($print (global.get $printf_buf))\n  )\n\n  (func $printf_4 (param $fmt i32)\n        (param $v0 i32) (param $v1 i32) (param $v2 i32)\n        (param $v3 i32)\n    (drop ($sprintf_6 (global.get $printf_buf) $fmt $v0 $v1 $v2 $v3 0 0))\n    ($print (global.get $printf_buf))\n  )\n\n  (func $printf_5 (param $fmt i32)\n        (param $v0 i32) (param $v1 i32) (param $v2 i32)\n        (param $v3 i32) (param $v4 i32)\n    (drop ($sprintf_6 (global.get $printf_buf) $fmt $v0 $v1 $v2 $v3 $v4 0))\n    ($print (global.get $printf_buf))\n  )\n\n  (func $printf_6 (param $fmt i32)\n        (param $v0 i32) (param $v1 i32) (param $v2 i32)\n        (param $v3 i32) (param $v4 i32) (param $v5 i32)\n    (drop ($sprintf_6 (global.get $printf_buf) $fmt $v0 $v1 $v2 $v3 $v4 $v5))\n    ($print (global.get $printf_buf))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  (func $_sprintdigit (param $str i32) (param $num i32) (param $base i32)\n    (LET $n  (i32.rem_u $num $base)\n         $ch (if (result i32) (i32.lt_u $n 10) 48 55))\n    (i32.store8 $str (i32.add $n $ch))\n  )\n\n  ;; TODO: add max buf length (i.e. snprintnum)\n  (func $_sprintnum (param $buf i32) (param $val i32) (param $radix i32)\n        (param $pad_cnt i32) (param $pad_char i32) (result i32)\n    (LET $pbuf  $buf\n         $neg 0 $i 0 $j 0 $k 0 $len 0 $digit 0)\n\n    (if (AND (i32.lt_s $val 0) (i32.eq $radix 10))\n      (then\n        (local.set $neg 1)\n        (local.set $val (i32.sub 0 $val))))\n\n    ;; Calculate smallest to most significant digit\n    (loop $loop\n      (local.set $digit (i32.rem_u $val $radix))\n      (i32.store8 $pbuf (if (result i32) (i32.lt_u $digit 10)\n                            (i32.add (CHR \"0\") $digit)\n                            (i32.sub (i32.add (CHR \"A\") $digit) 10)))\n      (local.set $pbuf (i32.add $pbuf 1))\n      (local.set $val (i32.div_u $val $radix))\n      (br_if $loop (i32.gt_u $val 0))\n    )\n\n    (local.set $i (i32.sub $pbuf $buf))\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i $pad_cnt))\n        (i32.store8 $pbuf $pad_char)\n        (local.set $pbuf (i32.add $pbuf 1))\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n\n    (if $neg\n      (then\n        (i32.store8 $pbuf (CHR \"-\"))\n        (local.set $pbuf (i32.add $pbuf 1))))\n\n    (i32.store8 $pbuf (CHR \"\\x00\"))\n\n    ;; now reverse it\n    (local.set $len (i32.sub $pbuf $buf))\n    (local.set $i 0)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i (i32.div_u $len 2)))\n\n        (local.set $j (i32.load8_u (i32.add $buf $i)))\n        (local.set $k (i32.add $buf (i32.sub (i32.sub $len $i) 1)))\n        (i32.store8 (i32.add $buf $i) (i32.load8_u $k))\n        (i32.store8 $k $j)\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n\n    (i32.add $buf $len)\n  )\n\n  ;; TODO: switch to snprint* (add buffer len)\n  (func $sprintf_1 (param $str i32) (param $fmt i32)\n        (param $v0 i32) (result i32)\n    ($sprintf_6 $str $fmt $v0 0 0 0 0 0)\n  )\n\n  (func $sprintf_6 (param $str i32) (param $fmt i32)\n        (param $v0 i32) (param $v1 i32) (param $v2 i32)\n        (param $v3 i32) (param $v4 i32) (param $v5 i32)\n        (result i32)\n    (LET $pstr $str\n         $vidx 0 $ch 0 $v 0 $len 0 $pad_cnt 0 $pad_char 0)\n\n    (block $done\n      (loop $loop\n        (block $after_v\n          ;; set $v to the current parameter\n          (block (block (block (block (block (block\n          (br_table 0 1 2 3 4 5 0 $vidx))\n          (; 0 ;) (local.set $v $v0) (br $after_v))\n          (; 1 ;) (local.set $v $v1) (br $after_v))\n          (; 2 ;) (local.set $v $v2) (br $after_v))\n          (; 3 ;) (local.set $v $v3) (br $after_v))\n          (; 4 ;) (local.set $v $v4) (br $after_v))\n          (; 5 ;) (local.set $v $v5) (br $after_v)\n        )\n\n        ;;; while ((ch=*(fmt++)))\n        (local.set $ch (i32.load8_u $fmt))\n        (local.set $fmt (i32.add 1 $fmt))\n        (br_if $done (i32.eqz $ch))\n        ;; TODO: check buffer length\n\n        (if (i32.ne $ch (CHR \"%\"))\n          (then\n            ;; TODO: check buffer length\n            (i32.store8 $pstr $ch)\n            (local.set $pstr (i32.add 1 $pstr))\n            (br $loop)))\n\n        ;;; ch=*(fmt++)\n        (local.set $ch (i32.load8_u $fmt))\n        (local.set $fmt (i32.add 1 $fmt))\n        (br_if $done (i32.eqz $ch))\n\n        (local.set $pad_cnt 0)\n        (local.set $pad_char (CHR \" \"))\n        (if (AND (i32.ge_s $ch (CHR \"0\")) (i32.le_s $ch (CHR \"9\")))\n          (then\n            ;; padding requested\n            (if (i32.eq $ch (CHR \"0\"))\n              (then\n                ;; zero padding requested\n                (local.set $pad_char (CHR \"0\"))\n                ;;; ch=*(fmt++)\n                (local.set $ch (i32.load8_u $fmt))\n                (local.set $fmt (i32.add 1 $fmt))\n                (br_if $done (i32.eqz $ch))))\n            (loop $loop\n              (local.set $pad_cnt (i32.mul $pad_cnt 10))\n              (local.set $pad_cnt (i32.add $pad_cnt\n                                           (i32.sub $ch (CHR \"0\"))))\n              (local.set $ch (i32.load8_u $fmt))\n              (local.set $fmt (i32.add 1 $fmt))\n              (br_if $loop (AND (i32.ge_s $ch (CHR \"0\"))\n                                (i32.le_s $ch (CHR \"9\"))))\n            )))\n\n        (if (i32.eq (CHR \"d\") $ch)\n          (then\n            (local.set $pstr ($_sprintnum $pstr $v 10 $pad_cnt $pad_char)))\n        (else (if (i32.eq (CHR \"x\") $ch)\n          (then\n            (local.set $pstr ($_sprintnum $pstr $v 16 $pad_cnt $pad_char)))\n        (else (if (i32.eq (CHR \"s\") $ch)\n          (then\n            (local.set $len ($strlen $v))\n            (block $done\n              (loop $loop\n                (br_if $done (i32.le_s $pad_cnt $len))\n                (i32.store8 $pstr (CHR \" \"))\n                (local.set $pstr (i32.add $pstr 1))\n                (local.set $pad_cnt (i32.sub $pad_cnt 1))\n                (br $loop)\n              )\n            )\n            ($memmove $pstr $v $len)\n            (local.set $pstr (i32.add $pstr $len)))\n        (else (if (i32.eq (CHR \"c\") $ch)\n          (then\n            (i32.store8 $pstr $v)\n            (local.set $pstr (i32.add $pstr 1)))\n        (else (if (i32.eq (CHR \"%\") $ch)\n          (then\n            (i32.store8 $pstr (CHR \"%\"))\n            (local.set $pstr (i32.add $pstr 1))\n            (br $loop)) ;; don't increase vidx\n        (else\n          ($printf_1 \"Illegal format character: '%c'\\n\" $ch)\n          ($fatal 3 \"\")))))))))))\n\n        (local.set $vidx (i32.add 1 $vidx))\n        (br $loop)\n      )\n    )\n\n    (i32.store8 $pstr (CHR \"\\x00\"))\n    $pstr\n  )\n\n)\n"
  },
  {
    "path": "impls/wasm/reader.wam",
    "content": "(module $reader\n\n  ;; TODO: global warning\n  (global $token_buf   (mut i32) 0)\n  (global $read_index  (mut i32) 0)\n\n  (func $skip_spaces (param $str i32) (result i32)\n    (LET $found 0\n         $c (i32.load8_u (i32.add $str (global.get $read_index))))\n    (block $done\n      (loop $loop\n        ;;; while (c == ' ' || c == ',' || c == '\\n')\n        (br_if $done (AND (i32.ne $c (CHR \" \"))\n                          (i32.ne $c (CHR \",\"))\n                          (i32.ne $c (CHR \"\\n\"))))\n        (local.set $found 1)\n        ;;; c=str[++(*index)]\n        (global.set $read_index (i32.add (global.get $read_index) 1))\n        (local.set $c (i32.load8_u (i32.add $str (global.get $read_index))))\n        (br $loop)\n      )\n    )\n;;    ($debug \">>> skip_spaces:\" $found)\n    $found\n  )\n\n  (func $skip_to_eol (param $str i32) (result i32)\n    (LET $found 0\n         $c (i32.load8_u (i32.add $str (global.get $read_index))))\n    (if (i32.eq $c (CHR \";\"))\n      (then\n        (local.set $found 1)\n        (block $done\n          (loop $loop\n            ;;; c=str[++(*index)]\n            (global.set $read_index (i32.add (global.get $read_index) 1))\n            (local.set $c (i32.load8_u (i32.add $str\n                                                (global.get $read_index))))\n            ;;; while (c != '\\0' && c != '\\n')\n            (br_if $loop (AND (i32.ne $c (CHR \"\\x00\"))\n                              (i32.ne $c (CHR \"\\n\"))))\n          )\n        )))\n;;    ($debug \">>> skip_to_eol:\" $found)\n    $found\n  )\n\n  (func $skip_spaces_comments (param $str i32)\n    (loop $loop\n      ;; skip spaces\n      (br_if $loop ($skip_spaces $str))\n      ;; skip comments\n      (br_if $loop ($skip_to_eol $str))\n    )\n  )\n\n  (func $read_token (param $str i32) (result i32)\n    (LET $token_index 0\n         $isstring    0\n         $instring    0\n         $escaped     0\n         $c           0)\n\n    ($skip_spaces_comments $str)\n\n    ;; read first character\n    ;;; c=str[++(*index)]\n    (local.set $c (i32.load8_u (i32.add $str (global.get $read_index))))\n    (global.set $read_index (i32.add (global.get $read_index) 1))\n    ;; read first character\n    ;;; token[token_index++] = c\n    (i32.store8 (i32.add (global.get $token_buf) $token_index) $c)\n    (local.set $token_index (i32.add $token_index 1))\n    ;; single/double character token\n    (if (OR (i32.eq $c (CHR \"(\"))\n            (i32.eq $c (CHR \")\"))\n            (i32.eq $c (CHR \"[\"))\n            (i32.eq $c (CHR \"]\"))\n            (i32.eq $c (CHR \"{\"))\n            (i32.eq $c (CHR \"}\"))\n            (i32.eq $c (CHR \"'\"))\n            (i32.eq $c (CHR \"`\"))\n            (i32.eq $c (CHR \"@\"))\n            (AND (i32.eq $c (CHR \"~\"))\n                 (i32.ne (i32.load8_u (i32.add $str (global.get $read_index)))\n                         (CHR \"@\"))))\n\n      (then\n        ;; continue\n        (nop))\n      (else\n        ;;; if (c == '\"') isstring = true\n        (local.set $isstring (i32.eq $c (CHR \"\\\"\")))\n        (local.set $instring $isstring)\n        (block $done\n          (loop $loop\n            ;; peek at next character\n            ;;; c = str[*index]\n            (local.set $c (i32.load8_u\n                            (i32.add $str (global.get $read_index))))\n            ;;; if (c == '\\0') break\n            (br_if $done (i32.eq $c 0))\n            ;;; if (!isstring)\n            (if (i32.eqz $isstring)\n              (then\n                ;; next character is token delimiter\n                (br_if $done (OR (i32.eq $c (CHR \"(\"))\n                                 (i32.eq $c (CHR \")\"))\n                                 (i32.eq $c (CHR \"[\"))\n                                 (i32.eq $c (CHR \"]\"))\n                                 (i32.eq $c (CHR \"{\"))\n                                 (i32.eq $c (CHR \"}\"))\n                                 (i32.eq $c (CHR \" \"))\n                                 (i32.eq $c (CHR \",\"))\n                                 (i32.eq $c (CHR \"\\n\"))))))\n            ;; read next character\n            ;;; token[token_index++] = str[(*index)++]\n            (i32.store8 (i32.add (global.get $token_buf) $token_index)\n                          (i32.load8_u\n                            (i32.add $str (global.get $read_index))))\n            (local.set $token_index (i32.add $token_index 1))\n            (global.set $read_index (i32.add (global.get $read_index) 1))\n            ;;; if (token[0] == '~' && token[1] == '@') break\n            (br_if $done (AND (i32.eq (i32.load8_u\n                                        (i32.add (global.get $token_buf) 0))\n                                      (CHR \"~\"))\n                              (i32.eq (i32.load8_u\n                                        (i32.add (global.get $token_buf) 1))\n                                      (CHR \"@\"))))\n\n            ;;; if ((!isstring) || escaped)\n            (if (OR (i32.eqz $isstring) $escaped)\n              (then\n                (local.set $escaped 0)\n                (br $loop)))\n            (if (i32.eq $c (CHR \"\\\\\"))\n              (local.set $escaped 1))\n            (if (i32.eq $c (CHR \"\\\"\"))\n              (then\n                (local.set $instring 0)\n                (br $done)))\n            (br $loop)\n          )\n        )\n\n        (if (AND $isstring $instring)\n          (then\n            ($THROW_STR_0 \"expected '\\\"', got EOF\")\n            (return 0)))))\n\n    ;;; token[token_index] = '\\0'\n    (i32.store8 (i32.add (global.get $token_buf) $token_index) 0)\n    (global.get $token_buf)\n  )\n\n  (func $read_seq (param $str i32 $type i32 $end i32) (result i32)\n    (LET $res ($MAP_LOOP_START $type)\n         $val2 0\n         $val3 0\n         $c 0\n         ;; MAP_LOOP stack\n         $ret $res\n         $empty $res\n         $current $res)\n\n    ;; READ_SEQ_LOOP\n    (block $done\n      (loop $loop\n        ($skip_spaces_comments $str)\n\n        ;; peek at next character\n        ;;; c = str[*index]\n        (local.set $c (i32.load8_u (i32.add $str (global.get $read_index))))\n        (if (i32.eq $c (CHR \"\\x00\"))\n          (then\n            ($THROW_STR_0 \"unexpected EOF\")\n            (br $done)))\n        (if (i32.eq $c $end)\n          (then\n            ;; read next character\n            ;;; c = str[(*index)++]\n            (local.set $c (i32.load8_u (i32.add $str (global.get $read_index))))\n            (global.set $read_index (i32.add (global.get $read_index) 1))\n            (br $done)))\n\n        ;; value (or key for hash-maps)\n        (local.set $val2 ($read_form $str))\n\n        ;; if error, release the unattached element\n        (if (global.get $error_type)\n          (then\n            ($RELEASE $val2)\n            (br $done)))\n\n        ;; if this is a hash-map, READ_FORM again\n        (if (i32.eq $type (global.get $HASHMAP_T))\n          (local.set $val3 ($read_form $str)))\n\n        ;; update the return sequence structure\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (br $loop)\n      )\n    )\n\n    ;; MAP_LOOP_DONE\n    $ret\n  )\n\n  (func $read_macro (param $str i32 $sym i32 $with_meta i32) (result i32)\n    (LET $first ($STRING (global.get $SYMBOL_T) $sym)\n         $second ($read_form $str)\n         $third 0\n         $res $second)\n    (if (global.get $error_type) (return $res))\n    (if (i32.eqz $with_meta)\n      (then\n        (local.set $res ($LIST2 $first $second)))\n      (else\n        (local.set $third ($read_form $str))\n        (local.set $res ($LIST3 $first $third $second))\n        ;; release values, list has ownership\n        ($RELEASE $third)))\n    ;; release values, list has ownership\n    ($RELEASE $second)\n    ($RELEASE $first)\n    $res\n  )\n\n  (func $read_form (param $str i32) (result i32)\n    (LET $tok 0 $c0 0 $c1 0 $res 0 $slen 0)\n\n    (if (global.get $error_type) (return 0))\n\n    (local.set $tok ($read_token $str))\n\n    (if (global.get $error_type) (return 0))\n    ;;($printf_1 \">>> read_form 1: %s\\n\" $tok)\n    ;;; c0 = token[0]\n    (local.set $c0 (i32.load8_u $tok))\n    (local.set $c1 (i32.load8_u (i32.add $tok 1)))\n\n    (if (i32.eq $c0 0)\n      (then\n        (return ($INC_REF (global.get $NIL))))\n      (else (if (OR (AND (i32.ge_u $c0 (CHR \"0\"))\n                         (i32.le_u $c0 (CHR \"9\")))\n                    (AND (i32.eq $c0 (CHR \"-\"))\n                         (i32.ge_u $c1 (CHR \"0\"))\n                         (i32.le_u $c1 (CHR \"9\"))))\n      (then\n        (return ($INTEGER ($atoi $tok))))\n    (else (if (i32.eq $c0 (CHR \":\"))\n      (then\n        (i32.store8 $tok (CHR \"\\x7f\"))\n        (return ($STRING (global.get $STRING_T) $tok)))\n    (else (if (i32.eq $c0 (CHR \"\\\"\"))\n      (then\n        (local.set $slen ($strlen (i32.add $tok 1)))\n        (if (i32.ne (i32.load8_u (i32.add $tok $slen)) (CHR \"\\\"\"))\n          (then\n            ($THROW_STR_0 \"expected '\\\"', got EOF\")\n            (return 0))\n          (else\n            ;; unescape backslashes, quotes, and newlines\n            ;; remove the trailing quote\n            (i32.store8 (i32.add $tok $slen) (CHR \"\\x00\"))\n            (local.set $tok (i32.add $tok 1))\n            (drop ($REPLACE3 0 $tok\n                             \"\\\\\\\"\" \"\\\"\"\n                             \"\\\\n\" \"\\n\"\n                             \"\\\\\\\\\" \"\\\\\"))\n            (return ($STRING (global.get $STRING_T) $tok)))))\n    (else (if (i32.eqz ($strcmp \"nil\" $tok))\n      (then (return ($INC_REF (global.get $NIL))))\n    (else (if (i32.eqz ($strcmp \"false\" $tok))\n      (then (return ($INC_REF (global.get $FALSE))))\n    (else (if (i32.eqz ($strcmp \"true\" $tok))\n      (then (return ($INC_REF (global.get $TRUE))))\n    (else (if (i32.eqz ($strcmp \"'\" $tok))\n      (then (return ($read_macro $str \"quote\" 0)))\n    (else (if (i32.eqz ($strcmp \"`\" $tok))\n      (then (return ($read_macro $str \"quasiquote\" 0)))\n    (else (if (i32.eqz ($strcmp \"~@\" $tok))\n      (then (return ($read_macro $str \"splice-unquote\" 0)))\n    (else (if (i32.eqz ($strcmp \"~\" $tok))\n      (then (return ($read_macro $str \"unquote\" 0)))\n    (else (if (i32.eqz ($strcmp \"^\" $tok))\n      (then (return ($read_macro $str \"with-meta\" 1)))\n    (else (if (i32.eqz ($strcmp \"@\" $tok))\n      (then (return ($read_macro $str \"deref\" 0)))\n    (else (if (i32.eq $c0 (CHR \"(\"))\n      (then (return ($read_seq $str (global.get $LIST_T) (CHR \")\"))))\n    (else (if (i32.eq $c0 (CHR \"[\"))\n      (then (return ($read_seq $str (global.get $VECTOR_T) (CHR \"]\"))))\n    (else (if (i32.eq $c0 (CHR \"{\"))\n      (then (return ($read_seq $str (global.get $HASHMAP_T) (CHR \"}\"))))\n    (else (if (OR (i32.eq $c0 (CHR \")\"))\n                  (i32.eq $c0 (CHR \"]\"))\n                  (i32.eq $c0 (CHR \"}\")))\n      (then\n        ($THROW_STR_1 \"unexpected '%c'\" $c0)\n        (return 0))\n    (else\n      (return ($STRING (global.get $SYMBOL_T) $tok))))\n    ))))))))))))))))))))))))))))))))\n    0 ;; not reachable\n  )\n\n  (func $read_str (param $str i32) (result i32)\n    (global.set $read_index 0)\n    ($read_form $str)\n  )\n\n  (export \"read_str\" (func $read_str))\n\n)\n"
  },
  {
    "path": "impls/wasm/run",
    "content": "#!/usr/bin/env bash\nSTEP=${STEP:-stepA_mal}\ncase \"${wasm_MODE}\" in\nwasmtime)\n    exec wasmtime --dir=./ --dir=../ --dir=/ $(dirname $0)/${STEP:-stepA_mal}.wasm \"${@}\" ;;\nwasmer)\n    exec wasmer run --dir=./ --dir=../ --dir=/ $(dirname $0)/${STEP:-stepA_mal}.wasm -- \"${@}\" ;;\nwarpy)\n    exec warpy --argv --memory-pages 256 $(dirname $0)/${STEP:-stepA_mal}.wasm \"${@}\" ;;\nwax)\n    exec wax $(dirname $0)/${STEP:-stepA_mal}.wasm \"${@}\" ;;\nwace_libc)\n    exec wace $(dirname $0)/${STEP:-stepA_mal}.wasm \"${@}\" ;;\nwace_fooboot)\n    echo >&2 \"wace_fooboot mode not yet supported\" ;;\nnode|js|*)\n    exec ./run.js $(dirname $0)/${STEP:-stepA_mal}.wasm \"${@}\" ;;\nesac\n"
  },
  {
    "path": "impls/wasm/run.js",
    "content": "#!/usr/bin/env node\n\n// Copyright Joel Martin\n// License MIT\n\nconst { promisify } = require('util')\nconst fs = require('fs')\nconst readFile = promisify(fs.readFile)\nconst assert = require('assert')\nconst { TextDecoder, TextEncoder } = require('text-encoding')\nconst node_readline = require('./node_readline.js')\n\nassert('WebAssembly' in global, 'WebAssembly not detected')\n\n//\n// Memory interaction utilities\n//\n\n// Convert node Buffer to Uint8Array\nfunction toUint8Array(buf) {\n  let u = new Uint8Array(buf.length)\n  for (let i = 0; i < buf.length; ++i) {\n    u[i] = buf[i]\n  }\n  return u\n}\n\n// Read null terminated string out of webassembly memory\nfunction get_string(memory, addr) {\n    //console.warn(\"get_string:\", addr)\n    let u8 = new Uint8Array(memory.buffer, addr)\n    let length = u8.findIndex(e => e == 0)\n    let bytes = new Uint8Array(memory.buffer, addr, length)\n    let str = new TextDecoder('utf8').decode(bytes)\n    return str\n}\n\n// Write null terminated string into webassembly memory\nfunction put_string(memory, addr, str, max_length) {\n    let buf8 = new Uint8Array(memory.buffer, addr)\n\n    let bytes = new TextEncoder('utf8').encode(str)\n    if (max_length && bytes.length > max_length) {\n        bytes = bytes.slice(0, max_length)\n    }\n\n    buf8.set(bytes, 0)\n    buf8[bytes.length] = 0 // null terminator\n    return bytes.length+1\n}\n\n// Put argv structure at beginning of memory\nfunction marshal_argv(memory, offset, args) {\n    let view = new DataView(memory.buffer, offset)\n    let buf8 = new Uint8Array(memory.buffer, offset)\n\n    let stringStart = (args.length + 1) * 4\n    for (let i = 0; i < args.length; i++) {\n        let len = put_string(memory, stringStart, args[i])\n        view.setUint32(i*4, stringStart, true)\n        stringStart = stringStart + len\n    }\n    view.setUint32(args.length*4, 0, true)\n    return offset + stringStart // start of free memory\n}\n\n// Based on:\n// https://gist.github.com/kripken/59c67556dc03bb6d57052fedef1e61ab\n\n// Loads a WebAssembly dynamic library, returns a promise.\nasync function loadWebAssembly(filename, args) {\n  // Fetch the file and compile it\n  const wasm_str = await readFile(filename)\n  const wasm_bin = toUint8Array(wasm_str)\n  const module = await WebAssembly.compile(wasm_bin)\n  let memory = new WebAssembly.Memory({ initial: 256 })\n  // Core imports\n  function printline(addr, stream) {\n      console.log(get_string(memory, addr).replace(/\\n$/, ''))\n  }\n\n  // Returns addr on success and -1 on failure\n  // Truncates to max_length\n  function readline(prompt, addr, max_length) {\n      let line = node_readline.readline(get_string(memory, prompt))\n      if (line === null) { return 0 }\n      put_string(memory, addr, line, max_length)\n      return 1\n  }\n\n  function read_file(path_addr, buf) {\n      let path = get_string(memory, path_addr)\n      let contents = fs.readFileSync(path, 'utf8')\n      return put_string(memory, buf, contents)\n  }\n\n  function get_time_ms() {\n      // subtract 30 years to make sure it fits into i32 without\n      // wrapping or becoming negative\n      return (new Date()).getTime() - 0x38640900\n  }\n\n  // Marshal arguments\n  const memoryStart = 0\n  let memoryBase = marshal_argv(memory, memoryStart, args)\n  memoryBase = memoryBase + (8 - (memoryBase % 8))\n\n  // Create the imports for the module, including the\n  // standard dynamic library imports\n  imports = {}\n  imports.env = {}\n  imports.env.exit = process.exit\n  imports.env.printline = printline\n  imports.env.readline = readline\n  imports.env.read_file = read_file\n  imports.env.get_time_ms = get_time_ms\n\n  imports.env.stdout = 0\n  imports.env.fputs = printline\n\n  imports.env.memory = memory\n  imports.env.memoryBase = memoryBase\n  imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' })\n  imports.env.tableBase = imports.env.tableBase || 0\n  // Create the instance.\n  return [new WebAssembly.Instance(module, imports), args.length, 0]\n}\n\nasync function main() {\n  assert(process.argv.length >= 3,\n         'Usage: ./run.js prog.wasm [ARGS...]')\n\n  const wasm = process.argv[2]\n  const args = process.argv.slice(2)\n  const [instance, argc, argv] = await loadWebAssembly(wasm, args)\n\n  let exports = instance.exports\n  assert(exports, 'no exports found')\n  assert('_main' in exports, '_main not found in wasm module exports')\n  if ('__post_instantiate' in exports) {\n      //console.warn('calling exports.__post_instantiate()')\n      exports['__post_instantiate']()\n  }\n  //console.warn(`calling exports._main(${argc}, ${argv})`)\n  let start = new Date()\n  let res = exports['_main'](argc, argv)\n  let end = new Date()\n  //console.warn('runtime: ' + (end-start) + 'ms')\n  process.exit(res)\n}\n\nif (module.parent) {\n    module.exports.loadWebAssembly = loadWebAssembly\n} else {\n    main()\n}\n"
  },
  {
    "path": "impls/wasm/step0_repl.wam",
    "content": "(module $step0_repl\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    $str\n  )\n\n  (func $EVAL (param $ast i32) (param $env i32) (result i32)\n    $ast\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    $ast\n  )\n\n  ;; REPL\n  (func $rep (param $line i32) (result i32)\n    ($PRINT ($EVAL ($READ $line) 0))\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    ;; Constant location/value definitions\n    (LET $line (STATIC_ARRAY 201))\n\n    ;; DEBUG\n    ;;($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        ($printf_1 \"%s\\n\" ($rep $line))\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    0\n  )\n\n  ;; init_memory is provided by mem.wam in later steps but we just\n  ;; printf in step0 so provide init_memory that just calls that\n  (func $init_memory\n    ($init_printf_mem)\n  )\n)\n\n"
  },
  {
    "path": "impls/wasm/step1_read_print.wam",
    "content": "(module $step1_read_print\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n  (func $EVAL (param $ast i32 $env i32) (result i32)\n    $ast\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $mv2 ($EVAL $mv1 $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res  0)\n\n    ;; DEBUG\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n;;    ($PR_MEMORY_RAW\n;;      (global.get $mem) (i32.add (global.get $mem)\n;;                                 (i32.mul (global.get $mem_unused_start) 4)))\n\n    (drop ($STRING (global.get $STRING_T) \"uvw\"))\n    (drop ($STRING (global.get $STRING_T) \"xyz\"))\n\n    ;;($PR_MEMORY -1 -1)\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line 0))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step2_eval.wam",
    "content": "(module $step2_eval\n\n  (global $repl_env (mut i32) (i32.const 0))\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n  (func $EVAL_AST (param $ast i32 $env i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (i32.eqz ($VAL0 $ast))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (type $fnT (func (param i32) (result i32)))\n\n  (table funcref\n    (elem\n      $add $subtract $multiply $divide))\n\n  (func $EVAL (param $ast i32 $env i32) (result i32)\n    (local $res2 i64)\n    (LET $res 0\n         $ftype 0 $f_args 0 $ast_type 0 $f 0 $args 0 $found 0)\n\n    (if (global.get $error_type) (return 0))\n\n    ;;($PR_VALUE \"EVAL: %s\\n\" $ast)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n       (local.set $res2 ($HASHMAP_GET $env $ast))\n       (local.set $res (i32.wrap_i64 $res2))\n       (local.set $found (i32.wrap_i64 (i64.shr_u $res2\n                                                  (i64.const 32))))\n       (if (i32.eqz $found)\n           ($THROW_STR_1 \"'%s' not found\"\n                 ($to_String $ast)))\n       (return ($INC_REF $res))))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (return ($EVAL_AST $ast $env))))\n\n    (if (OR (i32.ne $ast_type (global.get $LIST_T))\n            ($EMPTY_Q $ast))\n      (then\n        (return ($INC_REF $ast))))\n\n    ;; APPLY_LIST\n\n      ;; EVAL_INVOKE\n\n      (local.set $res ($EVAL_AST $ast $env))\n      (local.set $f_args $res)\n\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n        (return $f_args))\n\n      (local.set $args ($MEM_VAL0_ptr $f_args)) ;; rest\n      (local.set $f ($MEM_VAL1_ptr $f_args)) ;; value\n\n      (local.set $ftype ($TYPE $f))\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))\n          ($RELEASE $f_args)\n          (return $res))\n      )\n\n      ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n      ($RELEASE $f_args)\n      (return 0)\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $mv2 ($EVAL $mv1 $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from MAL_READ and EVAL\n    ($RELEASE $mv2)\n    ($RELEASE $mv1)\n    $ms\n  )\n\n  (func $add (param $args i32) (result i32)\n    ($INTEGER\n      (i32.add ($VAL0 ($MEM_VAL1_ptr $args))\n               ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $subtract (param $args i32) (result i32)\n    ($INTEGER\n      (i32.sub ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $multiply (param $args i32) (result i32)\n    ($INTEGER\n      (i32.mul ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $divide (param $args i32) (result i32)\n    ($INTEGER\n      (i32.div_s ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $repl_env ($HASHMAP))\n    (local.set $repl_env (global.get $repl_env))\n\n    (local.set $repl_env ($ASSOC1_S $repl_env \"+\" ($FUNCTION 0)))\n    (local.set $repl_env ($ASSOC1_S $repl_env \"-\" ($FUNCTION 1)))\n    (local.set $repl_env ($ASSOC1_S $repl_env \"*\" ($FUNCTION 2)))\n    (local.set $repl_env ($ASSOC1_S $repl_env \"/\" ($FUNCTION 3)))\n\n    ;;($PR_MEMORY -1 -1)\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step3_env.wam",
    "content": "(module $step3_env\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n  (func $EVAL_AST (param $ast i32 $env i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (i32.eqz ($VAL0 $ast))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (type $fnT (func (param i32) (result i32)))\n\n  (table funcref\n    (elem\n      $add $subtract $multiply $divide))\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $ast i32 $env i32) (result i32)\n    (LET $res 0\n         $ftype 0 $f_args 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0\n         $let_env 0)\n\n    (if (global.get $error_type) (return 0))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (return $res)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (return ($EVAL_AST $ast $env))))\n\n    (if (OR (i32.ne $ast_type (global.get $LIST_T))\n            ($EMPTY_Q $ast))\n      (then\n        (return ($INC_REF $ast))))\n\n    ;; APPLY_LIST\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (if (global.get $error_type) (return $res))\n\n        ;; set a1 in env to a2\n        (return ($ENV_SET $env $a1 $res)))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $let_env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1))\n                                   $let_env))\n\n            (if (global.get $error_type)\n              (then\n                (return 0)))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $let_env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n        (local.set $res ($EVAL $a2 $let_env))\n        ;; EVAL_RETURN\n        ($RELEASE $let_env)\n        (return $res))\n    )\n      ;; EVAL_INVOKE\n\n      (local.set $res ($EVAL_AST $ast $env))\n      (local.set $f_args $res)\n\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n        (return $f_args))\n\n      (local.set $args ($MEM_VAL0_ptr $f_args)) ;; rest\n      (local.set $f ($MEM_VAL1_ptr $f_args)) ;; value\n\n      (local.set $ftype ($TYPE $f))\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))\n          ($RELEASE $f_args)\n          (return $res))\n      )\n\n      ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n      ($RELEASE $f_args)\n      (return 0)\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $mv2 ($EVAL $mv1 $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from MAL_READ and EVAL\n    ($RELEASE $mv2)\n    ($RELEASE $mv1)\n    $ms\n  )\n\n  (func $add (param $args i32) (result i32)\n    ($INTEGER\n      (i32.add ($VAL0 ($MEM_VAL1_ptr $args))\n               ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $subtract (param $args i32) (result i32)\n    ($INTEGER\n      (i32.sub ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $multiply (param $args i32) (result i32)\n    ($INTEGER\n      (i32.mul ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $divide (param $args i32) (result i32)\n    ($INTEGER\n      (i32.div_s ($VAL0 ($MEM_VAL1_ptr $args))\n                 ($VAL0 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $args))))))\n  (func $pr_memory (param $args i32) (result i32)\n    ($PR_MEMORY -1 -1)\n    ($INC_REF (global.get $NIL)))\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    (drop ($ENV_SET_S $repl_env \"+\" ($FUNCTION 0)))\n    (drop ($ENV_SET_S $repl_env \"-\" ($FUNCTION 1)))\n    (drop ($ENV_SET_S $repl_env \"*\" ($FUNCTION 2)))\n    (drop ($ENV_SET_S $repl_env \"/\" ($FUNCTION 3)))\n\n    ;;($PR_MEMORY -1 -1)\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step4_if_fn_do.wam",
    "content": "(module $step4_if_fn_do\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n  (func $EVAL_AST (param $ast i32 $env i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (i32.eqz ($VAL0 $ast))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $ast i32 $env i32) (result i32)\n    (LET $res 0 $el 0\n         $ftype 0 $f_args 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0 $a3 0\n         $let_env 0 $fn_env 0 $a 0)\n\n    (if (global.get $error_type) (return 0))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (return $res)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (return ($EVAL_AST $ast $env))))\n\n    (if (OR (i32.ne $ast_type (global.get $LIST_T))\n            ($EMPTY_Q $ast))\n      (then\n        (return ($INC_REF $ast))))\n\n    ;; APPLY_LIST\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (if (global.get $error_type) (return $res))\n\n        ;; set a1 in env to a2\n        (return ($ENV_SET $env $a1 $res)))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $let_env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1))\n                                   $let_env))\n\n            (if (global.get $error_type)\n              (then\n                (return 0)))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $let_env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n        (local.set $res ($EVAL $a2 $let_env))\n        ;; EVAL_RETURN\n        ($RELEASE $let_env)\n        (return $res))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env))\n        (local.set $res ($LAST $el))\n        ($RELEASE $el)\n        (return $res))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (return ($INC_REF (global.get $NIL))))\n              (else\n                (local.set $a3 ($MAL_GET_A3 $ast))\n                (return ($EVAL $a3 $env)))))\n        (else\n          ($RELEASE $res)\n          (local.set $a2 ($MAL_GET_A2 $ast))\n          (return ($EVAL $a2 $env)))))))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (return ($ALLOC (global.get $MALFUNC_T) $a2 $a1 $env)))\n    )\n      ;; EVAL_INVOKE\n\n      (local.set $res ($EVAL_AST $ast $env))\n      (local.set $f_args $res)\n\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n        (return $f_args))\n\n      (local.set $args ($MEM_VAL0_ptr $f_args)) ;; rest\n      (local.set $f ($MEM_VAL1_ptr $f_args)) ;; value\n\n      (local.set $ftype ($TYPE $f))\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))\n          ($RELEASE $f_args)\n          (return $res))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          (local.set $fn_env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                             ($MEM_VAL1_ptr $f) $args))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $a ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $a))\n\n          ;; release f/args\n          ($RELEASE $f_args)\n\n          (local.set $res ($EVAL $a $fn_env))\n          ;; EVAL_RETURN\n          ($RELEASE $fn_env)\n          ($RELEASE $a)\n          (return $res))\n      )\n        ;; create new environment using env and params stored in function\n\n     ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n     ($RELEASE $f_args)\n     (return 0)\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n\n    ;;($PR_MEMORY -1 -1)\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step5_tco.wam",
    "content": "(module $step5_tco\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n  (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (OR (i32.eqz ($VAL0 $ast))\n                  (AND $skiplast\n                       (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)))))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)\n    (LET $ast $orig_ast\n         $env $orig_env\n         $prev_ast 0 $prev_env 0 $res 0 $el 0\n         $ftype 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0)\n\n    (block $EVAL_return\n    (loop $TCO_loop\n\n    (if (global.get $error_type)\n      (then\n        (local.set $res 0)\n        (br $EVAL_return)))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (br $EVAL_return)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (local.set $res ($EVAL_AST $ast $env 0))\n        (br $EVAL_return)))\n\n    (if (i32.ne $ast_type (global.get $LIST_T))\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    ;; APPLY_LIST\n\n    (if ($EMPTY_Q $ast)\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (br_if $EVAL_return (global.get $error_type))\n\n        ;; set a1 in env to a2\n        (local.set $res ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $prev_env $env) ;; save env for later release\n        (local.set $env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))\n\n            (br_if $done (global.get $error_type))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        ;; EVAL the rest through second to last\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))\n        (local.set $ast ($LAST $ast))\n        ($RELEASE $ast) ;; we already own it via ast\n        ($RELEASE $el)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (local.set $res ($INC_REF (global.get $NIL)))\n                (br $EVAL_return))\n              (else\n                (local.set $ast ($MAL_GET_A3 $ast)))))\n        (else\n          ($RELEASE $res)\n          (local.set $ast ($MAL_GET_A2 $ast))))))\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($MALFUNC $a2 $a1 $env))\n        (br $EVAL_return))\n    )\n      ;; EVAL_INVOKE\n\n      ;; Evaluate the first element to find a function.\n      (local.set $f ($EVAL $a0 $env))\n      (if (global.get $error_type)\n        (then\n          (local.set $res 0)\n          (br $EVAL_return)))\n\n      (local.set $ftype ($TYPE $f))\n\n      ;; Evaluate the arguments.\n      (local.set $args ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 0))\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n       (then\n         (local.set $res $f)\n         ($RELEASE $args)\n         (br $EVAL_return)))\n\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n          (br $EVAL_return))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          ;; save the current environment for release\n          (local.set $prev_env $env)\n          ;; create new environment using env and params stored in function\n          (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                          ($MEM_VAL1_ptr $f) $args))\n\n          ;; release previous environment if not the current EVAL env\n          ;; because our new env refers to it and we no longer need to\n          ;; track it (since we are TCO recurring)\n          (if (i32.ne $prev_env $orig_env)\n            (then\n              ($RELEASE $prev_env)\n              (local.set $prev_env 0)))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $ast ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $ast))\n\n          ;; if we have already been here via TCO, release previous\n          ;; ast\n          ;; PEND_A_LV\n          (if $prev_ast ($RELEASE $prev_ast))\n          (local.set $prev_ast $ast)\n\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n\n          (br $TCO_loop))\n      )\n        ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n        (local.set $res 0)\n        ($RELEASE $f)\n        ($RELEASE $args)\n        (br $EVAL_return)\n\n    ) ;; end of TCO_loop\n    ) ;; end of EVAL_return\n\n    ;; EVAL_RETURN\n    (if (i32.ne $env $orig_env) ($RELEASE $env))\n    (if $prev_ast ($RELEASE $prev_ast))\n\n    $res\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n\n    ;;($PR_MEMORY -1 -1)\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step6_file.wam",
    "content": "(module $step6_file\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n  (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (OR (i32.eqz ($VAL0 $ast))\n                  (AND $skiplast\n                       (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)))))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)\n    (LET $ast $orig_ast\n         $env $orig_env\n         $prev_ast 0 $prev_env 0 $res 0 $el 0\n         $ftype 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0)\n\n    (block $EVAL_return\n    (loop $TCO_loop\n\n    (if (global.get $error_type)\n      (then\n        (local.set $res 0)\n        (br $EVAL_return)))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (br $EVAL_return)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (local.set $res ($EVAL_AST $ast $env 0))\n        (br $EVAL_return)))\n\n    (if (i32.ne $ast_type (global.get $LIST_T))\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    ;; APPLY_LIST\n\n    (if ($EMPTY_Q $ast)\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (br_if $EVAL_return (global.get $error_type))\n\n        ;; set a1 in env to a2\n        (local.set $res ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $prev_env $env) ;; save env for later release\n        (local.set $env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))\n\n            (br_if $done (global.get $error_type))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        ;; EVAL the rest through second to last\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))\n        (local.set $ast ($LAST $ast))\n        ($RELEASE $ast) ;; we already own it via ast\n        ($RELEASE $el)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (local.set $res ($INC_REF (global.get $NIL)))\n                (br $EVAL_return))\n              (else\n                (local.set $ast ($MAL_GET_A3 $ast)))))\n        (else\n          ($RELEASE $res)\n          (local.set $ast ($MAL_GET_A2 $ast))))))\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($MALFUNC $a2 $a1 $env))\n        (br $EVAL_return))\n    )\n      ;; EVAL_INVOKE\n\n      ;; Evaluate the first element to find a function.\n      (local.set $f ($EVAL $a0 $env))\n      (if (global.get $error_type)\n        (then\n          (local.set $res 0)\n          (br $EVAL_return)))\n\n      (local.set $ftype ($TYPE $f))\n\n      ;; Evaluate the arguments.\n      (local.set $args ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 0))\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n       (then\n         (local.set $res $f)\n         ($RELEASE $args)\n         (br $EVAL_return)))\n\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (if (i32.eq ($VAL0 $f) 0) ;; eval\n            (then\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $args)\n                                     (global.get $repl_env))))\n            (else\n              (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))))\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n          (br $EVAL_return))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          ;; save the current environment for release\n          (local.set $prev_env $env)\n          ;; create new environment using env and params stored in function\n          (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                          ($MEM_VAL1_ptr $f) $args))\n\n          ;; release previous environment if not the current EVAL env\n          ;; because our new env refers to it and we no longer need to\n          ;; track it (since we are TCO recurring)\n          (if (i32.ne $prev_env $orig_env)\n            (then\n              ($RELEASE $prev_env)\n              (local.set $prev_env 0)))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $ast ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $ast))\n\n          ;; if we have already been here via TCO, release previous\n          ;; ast\n          ;; PEND_A_LV\n          (if $prev_ast ($RELEASE $prev_ast))\n          (local.set $prev_ast $ast)\n\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n\n          (br $TCO_loop))\n      )\n        ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n        (local.set $res 0)\n        ($RELEASE $f)\n        ($RELEASE $args)\n        (br $EVAL_return)\n\n    ) ;; end of TCO_loop\n    ) ;; end of EVAL_return\n\n    ;; EVAL_RETURN\n    (if (i32.ne $env $orig_env) ($RELEASE $env))\n    (if $prev_ast ($RELEASE $prev_ast))\n\n    $res\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0\n         ;; argument processing\n         $i 0 $ret 0 $empty 0 $current 0 $val2 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"argc: 0x%x\\n\" $argc)\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n    (drop ($ENV_SET_S $repl_env \"eval\" ($FUNCTION 0)))\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n    ($RELEASE ($RE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" $repl_env))\n\n\n    ;; Command line arguments\n    (local.set $res ($MAP_LOOP_START (global.get $LIST_T)))\n    ;; push MAP_LOP stack\n    ;; empty = current = ret = res\n    (local.set $ret $res)\n    (local.set $current $res)\n    (local.set $empty $res)\n\n    (local.set $i 2)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i $argc))\n\n\t(local.set $val2 ($STRING (global.get $STRING_T)\n                                  (i32.load (i32.add $argv (i32.mul $i 4)))))\n\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE\n                          (global.get $LIST_T) $empty $current $val2 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    (drop ($ENV_SET_S $repl_env \"*ARGV*\" $ret))\n\n\n    ;;($PR_MEMORY -1 -1)\n\n    (if (i32.gt_u $argc 1)\n      (then\n        (drop ($ENV_SET_S $repl_env\n                          \"*FILE*\" ($STRING (global.get $STRING_T)\n                                            (i32.load (i32.add $argv 4)))))\n        ($RELEASE ($RE \"(load-file *FILE*)\" $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (return 1))\n          (else\n            (return 0)))))\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step7_quote.wam",
    "content": "(module $step7_quote\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n\n\n  (func $QUASIQUOTE (param $ast i32) (result i32)\n    (LET $type ($TYPE $ast) $res 0 $sym 0 $second 0)\n\n    ;; symbol or map -> ('quote ast)\n    (if (OR (i32.eq $type (global.get $SYMBOL_T))\n            (i32.eq $type (global.get $HASHMAP_T)))\n    (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"quote\"))\n      (local.set $res ($LIST2 $sym $ast))\n      ($RELEASE $sym)\n      (return $res)))\n\n    ;; [xs..] -> ('vec (processed like a list))\n    (if (i32.eq $type (global.get $VECTOR_T)) (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"vec\"))\n      (local.set $second ($qq_foldr $ast))\n      (local.set $res ($LIST2 $sym $second))\n      ($RELEASE $sym)\n      ($RELEASE $second)\n      (return $res)))\n\n    ;; If ast is not affected by eval, return it unchanged.\n    (if (i32.ne $type (global.get $LIST_T)) (then\n      (return ($INC_REF $ast))))\n\n    ;; (unquote x) -> x\n    (local.set $second ($qq_unquote $ast \"unquote\"))\n    (if $second (then\n      (return ($INC_REF $second))))\n\n    ;; ast is a normal list, iterate on its elements\n    (return ($qq_foldr $ast)))\n\n  ;; Helper for quasiquote.\n  ;; If the given list ast contains at least two elements and starts\n  ;; with the given symbol, return the second element. Else return 0.\n  (func $qq_unquote (param $ast i32) (param $sym i32) (result i32)\n    (LET $car 0 $cdr 0)\n    (if ($VAL0 $ast) (then\n      (local.set $car ($MEM_VAL1_ptr $ast))\n      (if (i32.eq ($TYPE $car) (global.get $SYMBOL_T)) (then\n        (if (i32.eqz ($strcmp ($to_String $car) $sym)) (then\n          (local.set $cdr ($MEM_VAL0_ptr $ast))\n          (if ($VAL0 $cdr) (then\n            (return ($MEM_VAL1_ptr $cdr))))))))))\n    (return 0))\n\n  ;; Iteration on sequences for quasiquote (right reduce/fold).\n  (func $qq_foldr (param $xs i32) (result i32)\n    (if ($VAL0 $xs) (then\n      (return ($qq_loop ($MEM_VAL1_ptr $xs) ($qq_foldr ($MEM_VAL0_ptr $xs)))))\n    (else\n      (return ($INC_REF (global.get $EMPTY_LIST))))))\n\n  ;; Transition function for quasiquote right fold/reduce.\n  (func $qq_loop (param $elt i32) (param $acc i32) (result i32)\n    (LET $sym 0 $second 0 $res 0)\n\n    ;; If elt is ('splice-unquote x) -> ('concat, x, acc)\n    (if (i32.eq ($TYPE $elt) (global.get $LIST_T)) (then\n      (local.set $second ($qq_unquote $elt \"splice-unquote\"))\n      (if $second (then\n        (local.set $sym    ($STRING (global.get $SYMBOL_T) \"concat\"))\n        (local.set $res    ($LIST3 $sym $second $acc))\n        ;; release inner quasiquoted since outer list takes ownership\n        ($RELEASE $sym)\n        (return $res)))))\n\n    ;; normal elt -> ('cons, (quasiquoted x), acc)\n    (local.set $sym    ($STRING (global.get $SYMBOL_T) \"cons\"))\n    (local.set $second ($QUASIQUOTE $elt))\n    (local.set $res    ($LIST3 $sym $second $acc))\n    ;; release inner quasiquoted since outer list takes ownership\n    ($RELEASE $second)\n    ($RELEASE $sym)\n    (return $res))\n\n\n  (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (OR (i32.eqz ($VAL0 $ast))\n                  (AND $skiplast\n                       (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)))))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)\n    (LET $ast $orig_ast\n         $env $orig_env\n         $prev_ast 0 $prev_env 0 $res 0 $el 0\n         $ftype 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0)\n\n    (block $EVAL_return\n    (loop $TCO_loop\n\n    (if (global.get $error_type)\n      (then\n        (local.set $res 0)\n        (br $EVAL_return)))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (br $EVAL_return)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (local.set $res ($EVAL_AST $ast $env 0))\n        (br $EVAL_return)))\n\n    (if (i32.ne $ast_type (global.get $LIST_T))\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    ;; APPLY_LIST\n\n    (if ($EMPTY_Q $ast)\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (br_if $EVAL_return (global.get $error_type))\n\n        ;; set a1 in env to a2\n        (local.set $res ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $prev_env $env) ;; save env for later release\n        (local.set $env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))\n\n            (br_if $done (global.get $error_type))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        ;; EVAL the rest through second to last\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))\n        (local.set $ast ($LAST $ast))\n        ($RELEASE $ast) ;; we already own it via ast\n        ($RELEASE $el)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"quote\" $a0sym))\n      (then\n        (local.set $res ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"quasiquote\" $a0sym))\n      (then\n        (local.set $ast ($QUASIQUOTE ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n\n        ;; if we have already been here via TCO, release previous ast\n        (if $prev_ast ($RELEASE $prev_ast))\n        (local.set $prev_ast $ast)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (local.set $res ($INC_REF (global.get $NIL)))\n                (br $EVAL_return))\n              (else\n                (local.set $ast ($MAL_GET_A3 $ast)))))\n        (else\n          ($RELEASE $res)\n          (local.set $ast ($MAL_GET_A2 $ast))))))\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($MALFUNC $a2 $a1 $env))\n        (br $EVAL_return))\n    )\n      ;; EVAL_INVOKE\n\n      ;; Evaluate the first element to find a function.\n      (local.set $f ($EVAL $a0 $env))\n      (if (global.get $error_type)\n        (then\n          (local.set $res 0)\n          (br $EVAL_return)))\n\n      (local.set $ftype ($TYPE $f))\n\n      ;; Evaluate the arguments.\n      (local.set $args ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 0))\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n       (then\n         (local.set $res $f)\n         ($RELEASE $args)\n         (br $EVAL_return)))\n\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (if (i32.eq ($VAL0 $f) 0) ;; eval\n            (then\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $args)\n                                     (global.get $repl_env))))\n            (else\n              (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))))\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n          (br $EVAL_return))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          ;; save the current environment for release\n          (local.set $prev_env $env)\n          ;; create new environment using env and params stored in function\n          (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                          ($MEM_VAL1_ptr $f) $args))\n\n          ;; release previous environment if not the current EVAL env\n          ;; because our new env refers to it and we no longer need to\n          ;; track it (since we are TCO recurring)\n          (if (i32.ne $prev_env $orig_env)\n            (then\n              ($RELEASE $prev_env)\n              (local.set $prev_env 0)))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $ast ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $ast))\n\n          ;; if we have already been here via TCO, release previous\n          ;; ast\n          ;; PEND_A_LV\n          (if $prev_ast ($RELEASE $prev_ast))\n          (local.set $prev_ast $ast)\n\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n\n          (br $TCO_loop))\n      )\n        ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n        (local.set $res 0)\n        ($RELEASE $f)\n        ($RELEASE $args)\n        (br $EVAL_return)\n\n    ) ;; end of TCO_loop\n    ) ;; end of EVAL_return\n\n    ;; EVAL_RETURN\n    (if (i32.ne $env $orig_env) ($RELEASE $env))\n    (if $prev_ast ($RELEASE $prev_ast))\n\n    $res\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0\n         ;; argument processing\n         $i 0 $ret 0 $empty 0 $current 0 $val2 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"argc: 0x%x\\n\" $argc)\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n    (drop ($ENV_SET_S $repl_env \"eval\" ($FUNCTION 0)))\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n    ($RELEASE ($RE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" $repl_env))\n\n\n    ;; Command line arguments\n    (local.set $res ($MAP_LOOP_START (global.get $LIST_T)))\n    ;; push MAP_LOP stack\n    ;; empty = current = ret = res\n    (local.set $ret $res)\n    (local.set $current $res)\n    (local.set $empty $res)\n\n    (local.set $i 2)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i $argc))\n\n\t(local.set $val2 ($STRING (global.get $STRING_T)\n                                  (i32.load (i32.add $argv (i32.mul $i 4)))))\n\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE\n                          (global.get $LIST_T) $empty $current $val2 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    (drop ($ENV_SET_S $repl_env \"*ARGV*\" $ret))\n\n\n    ;;($PR_MEMORY -1 -1)\n\n    (if (i32.gt_u $argc 1)\n      (then\n        (drop ($ENV_SET_S $repl_env\n                          \"*FILE*\" ($STRING (global.get $STRING_T)\n                                            (i32.load (i32.add $argv 4)))))\n        ($RELEASE ($RE \"(load-file *FILE*)\" $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (return 1))\n          (else\n            (return 0)))))\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step8_macros.wam",
    "content": "(module $step8_macros\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n\n\n  (func $QUASIQUOTE (param $ast i32) (result i32)\n    (LET $type ($TYPE $ast) $res 0 $sym 0 $second 0)\n\n    ;; symbol or map -> ('quote ast)\n    (if (OR (i32.eq $type (global.get $SYMBOL_T))\n            (i32.eq $type (global.get $HASHMAP_T)))\n    (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"quote\"))\n      (local.set $res ($LIST2 $sym $ast))\n      ($RELEASE $sym)\n      (return $res)))\n\n    ;; [xs..] -> ('vec (processed like a list))\n    (if (i32.eq $type (global.get $VECTOR_T)) (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"vec\"))\n      (local.set $second ($qq_foldr $ast))\n      (local.set $res ($LIST2 $sym $second))\n      ($RELEASE $sym)\n      ($RELEASE $second)\n      (return $res)))\n\n    ;; If ast is not affected by eval, return it unchanged.\n    (if (i32.ne $type (global.get $LIST_T)) (then\n      (return ($INC_REF $ast))))\n\n    ;; (unquote x) -> x\n    (local.set $second ($qq_unquote $ast \"unquote\"))\n    (if $second (then\n      (return ($INC_REF $second))))\n\n    ;; ast is a normal list, iterate on its elements\n    (return ($qq_foldr $ast)))\n\n  ;; Helper for quasiquote.\n  ;; If the given list ast contains at least two elements and starts\n  ;; with the given symbol, return the second element. Else return 0.\n  (func $qq_unquote (param $ast i32) (param $sym i32) (result i32)\n    (LET $car 0 $cdr 0)\n    (if ($VAL0 $ast) (then\n      (local.set $car ($MEM_VAL1_ptr $ast))\n      (if (i32.eq ($TYPE $car) (global.get $SYMBOL_T)) (then\n        (if (i32.eqz ($strcmp ($to_String $car) $sym)) (then\n          (local.set $cdr ($MEM_VAL0_ptr $ast))\n          (if ($VAL0 $cdr) (then\n            (return ($MEM_VAL1_ptr $cdr))))))))))\n    (return 0))\n\n  ;; Iteration on sequences for quasiquote (right reduce/fold).\n  (func $qq_foldr (param $xs i32) (result i32)\n    (if ($VAL0 $xs) (then\n      (return ($qq_loop ($MEM_VAL1_ptr $xs) ($qq_foldr ($MEM_VAL0_ptr $xs)))))\n    (else\n      (return ($INC_REF (global.get $EMPTY_LIST))))))\n\n  ;; Transition function for quasiquote right fold/reduce.\n  (func $qq_loop (param $elt i32) (param $acc i32) (result i32)\n    (LET $sym 0 $second 0 $res 0)\n\n    ;; If elt is ('splice-unquote x) -> ('concat, x, acc)\n    (if (i32.eq ($TYPE $elt) (global.get $LIST_T)) (then\n      (local.set $second ($qq_unquote $elt \"splice-unquote\"))\n      (if $second (then\n        (local.set $sym    ($STRING (global.get $SYMBOL_T) \"concat\"))\n        (local.set $res    ($LIST3 $sym $second $acc))\n        ;; release inner quasiquoted since outer list takes ownership\n        ($RELEASE $sym)\n        (return $res)))))\n\n    ;; normal elt -> ('cons, (quasiquoted x), acc)\n    (local.set $sym    ($STRING (global.get $SYMBOL_T) \"cons\"))\n    (local.set $second ($QUASIQUOTE $elt))\n    (local.set $res    ($LIST3 $sym $second $acc))\n    ;; release inner quasiquoted since outer list takes ownership\n    ($RELEASE $second)\n    ($RELEASE $sym)\n    (return $res))\n\n\n  (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (OR (i32.eqz ($VAL0 $ast))\n                  (AND $skiplast\n                       (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)))))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)\n    (LET $ast $orig_ast\n         $env $orig_env\n         $prev_ast 0 $prev_env 0 $res 0 $el 0\n         $ftype 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0\n         $err 0)\n\n    (block $EVAL_return\n    (loop $TCO_loop\n\n    (if (global.get $error_type)\n      (then\n        (local.set $res 0)\n        (br $EVAL_return)))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (br $EVAL_return)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (local.set $res ($EVAL_AST $ast $env 0))\n        (br $EVAL_return)))\n\n    (if (i32.ne $ast_type (global.get $LIST_T))\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    ;; APPLY_LIST\n\n    (if ($EMPTY_Q $ast)\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (br_if $EVAL_return (global.get $error_type))\n\n        ;; set a1 in env to a2\n        (local.set $res ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $prev_env $env) ;; save env for later release\n        (local.set $env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))\n\n            (br_if $done (global.get $error_type))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        ;; EVAL the rest through second to last\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))\n        (local.set $ast ($LAST $ast))\n        ($RELEASE $ast) ;; we already own it via ast\n        ($RELEASE $el)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"quote\" $a0sym))\n      (then\n        (local.set $res ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"quasiquote\" $a0sym))\n      (then\n        (local.set $ast ($QUASIQUOTE ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n\n        ;; if we have already been here via TCO, release previous ast\n        (if $prev_ast ($RELEASE $prev_ast))\n        (local.set $prev_ast $ast)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"defmacro!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $f ($EVAL $a2 $env))\n        (local.set $res ($MALFUNC ($MEM_VAL0_ptr $f)\n          ($MEM_VAL1_ptr $f) ($MEM_VAL2_ptr $f)))\n        ($SET_TYPE $res (global.get $MACRO_T))\n        (br_if $EVAL_return (global.get $error_type))\n        ($RELEASE $f)\n\n        ;; set a1 in env to a2\n        (drop ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (local.set $res ($INC_REF (global.get $NIL)))\n                (br $EVAL_return))\n              (else\n                (local.set $ast ($MAL_GET_A3 $ast)))))\n        (else\n          ($RELEASE $res)\n          (local.set $ast ($MAL_GET_A2 $ast))))))\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($MALFUNC $a2 $a1 $env))\n        (br $EVAL_return))\n    )\n      ;; EVAL_INVOKE\n\n      ;; Evaluate the first element to find a function or macro.\n      (local.set $f ($EVAL $a0 $env))\n      (if (global.get $error_type)\n        (then\n          (local.set $res 0)\n          (br $EVAL_return)))\n\n      (local.set $ftype ($TYPE $f))\n\n      (if (i32.eq $ftype (global.get $MACRO_T))\n        (then\n          (local.set $ast ($APPLY $f ($MEM_VAL0_ptr $ast)))\n          ($RELEASE $f)\n          (if (global.get $error_type)\n            (then\n              (local.set $res 0)\n              (br $EVAL_return)))\n          (br $TCO_loop)))\n\n      ;; Evaluate the arguments.\n      (local.set $args ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 0))\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n       (then\n         (local.set $res $f)\n         ($RELEASE $args)\n         (br $EVAL_return)))\n\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (if (i32.eq ($VAL0 $f) 0) ;; eval\n            (then\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $args)\n                                     (global.get $repl_env))))\n            (else\n              (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))))\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n          (br $EVAL_return))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          ;; save the current environment for release\n          (local.set $prev_env $env)\n          ;; create new environment using env and params stored in function\n          (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                          ($MEM_VAL1_ptr $f) $args))\n\n          ;; release previous environment if not the current EVAL env\n          ;; because our new env refers to it and we no longer need to\n          ;; track it (since we are TCO recurring)\n          (if (i32.ne $prev_env $orig_env)\n            (then\n              ($RELEASE $prev_env)\n              (local.set $prev_env 0)))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $ast ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $ast))\n\n          ;; if we have already been here via TCO, release previous\n          ;; ast\n          ;; PEND_A_LV\n          (if $prev_ast ($RELEASE $prev_ast))\n          (local.set $prev_ast $ast)\n\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n\n          (br $TCO_loop))\n      )\n        ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n        (local.set $res 0)\n        ($RELEASE $f)\n        ($RELEASE $args)\n        (br $EVAL_return)\n\n    ) ;; end of TCO_loop\n    ) ;; end of EVAL_return\n\n    ;; EVAL_RETURN\n    (if (i32.ne $env $orig_env) ($RELEASE $env))\n    (if $prev_ast ($RELEASE $prev_ast))\n\n    $res\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0\n         ;; argument processing\n         $i 0 $ret 0 $empty 0 $current 0 $val2 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"argc: 0x%x\\n\" $argc)\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n    (drop ($ENV_SET_S $repl_env \"eval\" ($FUNCTION 0)))\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n    ($RELEASE ($RE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" $repl_env))\n    ($RELEASE ($RE \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" $repl_env))\n\n    ;; Command line arguments\n    (local.set $res ($MAP_LOOP_START (global.get $LIST_T)))\n    ;; push MAP_LOP stack\n    ;; empty = current = ret = res\n    (local.set $ret $res)\n    (local.set $current $res)\n    (local.set $empty $res)\n\n    (local.set $i 2)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i $argc))\n\n\t(local.set $val2 ($STRING (global.get $STRING_T)\n                                  (i32.load (i32.add $argv (i32.mul $i 4)))))\n\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE\n                          (global.get $LIST_T) $empty $current $val2 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    (drop ($ENV_SET_S $repl_env \"*ARGV*\" $ret))\n\n\n    ;;($PR_MEMORY -1 -1)\n\n    (if (i32.gt_u $argc 1)\n      (then\n        (drop ($ENV_SET_S $repl_env\n                          \"*FILE*\" ($STRING (global.get $STRING_T)\n                                            (i32.load (i32.add $argv 4)))))\n        ($RELEASE ($RE \"(load-file *FILE*)\" $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (return 1))\n          (else\n            (return 0)))))\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/step9_try.wam",
    "content": "(module $step9_try\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n\n\n  (func $QUASIQUOTE (param $ast i32) (result i32)\n    (LET $type ($TYPE $ast) $res 0 $sym 0 $second 0)\n\n    ;; symbol or map -> ('quote ast)\n    (if (OR (i32.eq $type (global.get $SYMBOL_T))\n            (i32.eq $type (global.get $HASHMAP_T)))\n    (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"quote\"))\n      (local.set $res ($LIST2 $sym $ast))\n      ($RELEASE $sym)\n      (return $res)))\n\n    ;; [xs..] -> ('vec (processed like a list))\n    (if (i32.eq $type (global.get $VECTOR_T)) (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"vec\"))\n      (local.set $second ($qq_foldr $ast))\n      (local.set $res ($LIST2 $sym $second))\n      ($RELEASE $sym)\n      ($RELEASE $second)\n      (return $res)))\n\n    ;; If ast is not affected by eval, return it unchanged.\n    (if (i32.ne $type (global.get $LIST_T)) (then\n      (return ($INC_REF $ast))))\n\n    ;; (unquote x) -> x\n    (local.set $second ($qq_unquote $ast \"unquote\"))\n    (if $second (then\n      (return ($INC_REF $second))))\n\n    ;; ast is a normal list, iterate on its elements\n    (return ($qq_foldr $ast)))\n\n  ;; Helper for quasiquote.\n  ;; If the given list ast contains at least two elements and starts\n  ;; with the given symbol, return the second element. Else return 0.\n  (func $qq_unquote (param $ast i32) (param $sym i32) (result i32)\n    (LET $car 0 $cdr 0)\n    (if ($VAL0 $ast) (then\n      (local.set $car ($MEM_VAL1_ptr $ast))\n      (if (i32.eq ($TYPE $car) (global.get $SYMBOL_T)) (then\n        (if (i32.eqz ($strcmp ($to_String $car) $sym)) (then\n          (local.set $cdr ($MEM_VAL0_ptr $ast))\n          (if ($VAL0 $cdr) (then\n            (return ($MEM_VAL1_ptr $cdr))))))))))\n    (return 0))\n\n  ;; Iteration on sequences for quasiquote (right reduce/fold).\n  (func $qq_foldr (param $xs i32) (result i32)\n    (if ($VAL0 $xs) (then\n      (return ($qq_loop ($MEM_VAL1_ptr $xs) ($qq_foldr ($MEM_VAL0_ptr $xs)))))\n    (else\n      (return ($INC_REF (global.get $EMPTY_LIST))))))\n\n  ;; Transition function for quasiquote right fold/reduce.\n  (func $qq_loop (param $elt i32) (param $acc i32) (result i32)\n    (LET $sym 0 $second 0 $res 0)\n\n    ;; If elt is ('splice-unquote x) -> ('concat, x, acc)\n    (if (i32.eq ($TYPE $elt) (global.get $LIST_T)) (then\n      (local.set $second ($qq_unquote $elt \"splice-unquote\"))\n      (if $second (then\n        (local.set $sym    ($STRING (global.get $SYMBOL_T) \"concat\"))\n        (local.set $res    ($LIST3 $sym $second $acc))\n        ;; release inner quasiquoted since outer list takes ownership\n        ($RELEASE $sym)\n        (return $res)))))\n\n    ;; normal elt -> ('cons, (quasiquoted x), acc)\n    (local.set $sym    ($STRING (global.get $SYMBOL_T) \"cons\"))\n    (local.set $second ($QUASIQUOTE $elt))\n    (local.set $res    ($LIST3 $sym $second $acc))\n    ;; release inner quasiquoted since outer list takes ownership\n    ($RELEASE $second)\n    ($RELEASE $sym)\n    (return $res))\n\n\n  (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (OR (i32.eqz ($VAL0 $ast))\n                  (AND $skiplast\n                       (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)))))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)\n    (LET $ast $orig_ast\n         $env $orig_env\n         $prev_ast 0 $prev_env 0 $res 0 $el 0\n         $ftype 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0\n         $err 0)\n\n    (block $EVAL_return\n    (loop $TCO_loop\n\n    (if (global.get $error_type)\n      (then\n        (local.set $res 0)\n        (br $EVAL_return)))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (br $EVAL_return)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (local.set $res ($EVAL_AST $ast $env 0))\n        (br $EVAL_return)))\n\n    (if (i32.ne $ast_type (global.get $LIST_T))\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    ;; APPLY_LIST\n\n    (if ($EMPTY_Q $ast)\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (br_if $EVAL_return (global.get $error_type))\n\n        ;; set a1 in env to a2\n        (local.set $res ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $prev_env $env) ;; save env for later release\n        (local.set $env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))\n\n            (br_if $done (global.get $error_type))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        ;; EVAL the rest through second to last\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))\n        (local.set $ast ($LAST $ast))\n        ($RELEASE $ast) ;; we already own it via ast\n        ($RELEASE $el)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"quote\" $a0sym))\n      (then\n        (local.set $res ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"quasiquote\" $a0sym))\n      (then\n        (local.set $ast ($QUASIQUOTE ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n\n        ;; if we have already been here via TCO, release previous ast\n        (if $prev_ast ($RELEASE $prev_ast))\n        (local.set $prev_ast $ast)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"defmacro!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $f ($EVAL $a2 $env))\n        (local.set $res ($MALFUNC ($MEM_VAL0_ptr $f)\n          ($MEM_VAL1_ptr $f) ($MEM_VAL2_ptr $f)))\n        ($SET_TYPE $res (global.get $MACRO_T))\n        (br_if $EVAL_return (global.get $error_type))\n        ($RELEASE $f)\n\n        ;; set a1 in env to a2\n        (drop ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"try*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        ;; if there is no error, return\n        (br_if $EVAL_return (i32.eqz (global.get $error_type)))\n        ;; if there is an error and res is set, we need to free it\n        ($RELEASE $res)\n        ;; if there is no catch block then return\n        (br_if $EVAL_return\n               (i32.eqz ($VAL0 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n        ;; save the current environment for release\n        (local.set $prev_env $env)\n        ;; create environment for the catch block eval\n        (local.set $env ($ENV_NEW $env))\n\n        ;; set a1 and a2 from the catch block\n        (local.set $a1 ($MAL_GET_A1 ($MAL_GET_A2 $ast)))\n        (local.set $a2 ($MAL_GET_A2 ($MAL_GET_A2 $ast)))\n\n        ;; create object for string errors\n        (if (i32.eq (global.get $error_type) 1)\n          (then\n            (local.set $err ($STRING (global.get $STRING_T)\n                                     (global.get $error_str))))\n          (else\n            (local.set $err (global.get $error_val))))\n        ;; bind the catch symbol to the error object\n        (drop ($ENV_SET $env $a1 $err))\n        ;; release our use, env took ownership\n        ($RELEASE $err)\n\n        ;; unset error for catch eval\n        (global.set $error_type 0)\n        (i32.store (global.get $error_str) (CHR \"\\x00\"))\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (local.set $res ($INC_REF (global.get $NIL)))\n                (br $EVAL_return))\n              (else\n                (local.set $ast ($MAL_GET_A3 $ast)))))\n        (else\n          ($RELEASE $res)\n          (local.set $ast ($MAL_GET_A2 $ast))))))\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($MALFUNC $a2 $a1 $env))\n        (br $EVAL_return))\n    )\n      ;; EVAL_INVOKE\n\n      ;; Evaluate the first element to find a function or macro.\n      (local.set $f ($EVAL $a0 $env))\n      (if (global.get $error_type)\n        (then\n          (local.set $res 0)\n          (br $EVAL_return)))\n\n      (local.set $ftype ($TYPE $f))\n\n      (if (i32.eq $ftype (global.get $MACRO_T))\n        (then\n          (local.set $ast ($APPLY $f ($MEM_VAL0_ptr $ast)))\n          ($RELEASE $f)\n          (if (global.get $error_type)\n            (then\n              (local.set $res 0)\n              (br $EVAL_return)))\n          (br $TCO_loop)))\n\n      ;; Evaluate the arguments.\n      (local.set $args ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 0))\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n       (then\n         (local.set $res $f)\n         ($RELEASE $args)\n         (br $EVAL_return)))\n\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (if (i32.eq ($VAL0 $f) 0) ;; eval\n            (then\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $args)\n                                     (global.get $repl_env))))\n            (else\n              (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))))\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n          (br $EVAL_return))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          ;; save the current environment for release\n          (local.set $prev_env $env)\n          ;; create new environment using env and params stored in function\n          (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                          ($MEM_VAL1_ptr $f) $args))\n\n          ;; release previous environment if not the current EVAL env\n          ;; because our new env refers to it and we no longer need to\n          ;; track it (since we are TCO recurring)\n          (if (i32.ne $prev_env $orig_env)\n            (then\n              ($RELEASE $prev_env)\n              (local.set $prev_env 0)))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $ast ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $ast))\n\n          ;; if we have already been here via TCO, release previous\n          ;; ast\n          ;; PEND_A_LV\n          (if $prev_ast ($RELEASE $prev_ast))\n          (local.set $prev_ast $ast)\n\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n\n          (br $TCO_loop))\n      )\n        ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n        (local.set $res 0)\n        ($RELEASE $f)\n        ($RELEASE $args)\n        (br $EVAL_return)\n\n    ) ;; end of TCO_loop\n    ) ;; end of EVAL_return\n\n    ;; EVAL_RETURN\n    (if (i32.ne $env $orig_env) ($RELEASE $env))\n    (if $prev_ast ($RELEASE $prev_ast))\n\n    $res\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0\n         ;; argument processing\n         $i 0 $ret 0 $empty 0 $current 0 $val2 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"argc: 0x%x\\n\" $argc)\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n    (drop ($ENV_SET_S $repl_env \"eval\" ($FUNCTION 0)))\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n    ($RELEASE ($RE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" $repl_env))\n    ($RELEASE ($RE \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" $repl_env))\n\n    ;; Command line arguments\n    (local.set $res ($MAP_LOOP_START (global.get $LIST_T)))\n    ;; push MAP_LOP stack\n    ;; empty = current = ret = res\n    (local.set $ret $res)\n    (local.set $current $res)\n    (local.set $empty $res)\n\n    (local.set $i 2)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i $argc))\n\n\t(local.set $val2 ($STRING (global.get $STRING_T)\n                                  (i32.load (i32.add $argv (i32.mul $i 4)))))\n\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE\n                          (global.get $LIST_T) $empty $current $val2 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    (drop ($ENV_SET_S $repl_env \"*ARGV*\" $ret))\n\n\n    ;;($PR_MEMORY -1 -1)\n\n    (if (i32.gt_u $argc 1)\n      (then\n        (drop ($ENV_SET_S $repl_env\n                          \"*FILE*\" ($STRING (global.get $STRING_T)\n                                            (i32.load (i32.add $argv 4)))))\n        ($RELEASE ($RE \"(load-file *FILE*)\" $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (return 1))\n          (else\n            (return 0)))))\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/stepA_mal.wam",
    "content": "(module $stepA_mal\n\n  (global $repl_env (mut i32) (i32.const 0))\n  (global $DEBUG_EVAL_S (mut i32) (i32.const 0)) ;; never $RELEASED\n\n  ;; READ\n  (func $READ (param $str i32) (result i32)\n    ($read_str $str)\n  )\n\n  ;; EVAL\n\n\n  (func $QUASIQUOTE (param $ast i32) (result i32)\n    (LET $type ($TYPE $ast) $res 0 $sym 0 $second 0)\n\n    ;; symbol or map -> ('quote ast)\n    (if (OR (i32.eq $type (global.get $SYMBOL_T))\n            (i32.eq $type (global.get $HASHMAP_T)))\n    (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"quote\"))\n      (local.set $res ($LIST2 $sym $ast))\n      ($RELEASE $sym)\n      (return $res)))\n\n    ;; [xs..] -> ('vec (processed like a list))\n    (if (i32.eq $type (global.get $VECTOR_T)) (then\n      (local.set $sym ($STRING (global.get $SYMBOL_T) \"vec\"))\n      (local.set $second ($qq_foldr $ast))\n      (local.set $res ($LIST2 $sym $second))\n      ($RELEASE $sym)\n      ($RELEASE $second)\n      (return $res)))\n\n    ;; If ast is not affected by eval, return it unchanged.\n    (if (i32.ne $type (global.get $LIST_T)) (then\n      (return ($INC_REF $ast))))\n\n    ;; (unquote x) -> x\n    (local.set $second ($qq_unquote $ast \"unquote\"))\n    (if $second (then\n      (return ($INC_REF $second))))\n\n    ;; ast is a normal list, iterate on its elements\n    (return ($qq_foldr $ast)))\n\n  ;; Helper for quasiquote.\n  ;; If the given list ast contains at least two elements and starts\n  ;; with the given symbol, return the second element. Else return 0.\n  (func $qq_unquote (param $ast i32) (param $sym i32) (result i32)\n    (LET $car 0 $cdr 0)\n    (if ($VAL0 $ast) (then\n      (local.set $car ($MEM_VAL1_ptr $ast))\n      (if (i32.eq ($TYPE $car) (global.get $SYMBOL_T)) (then\n        (if (i32.eqz ($strcmp ($to_String $car) $sym)) (then\n          (local.set $cdr ($MEM_VAL0_ptr $ast))\n          (if ($VAL0 $cdr) (then\n            (return ($MEM_VAL1_ptr $cdr))))))))))\n    (return 0))\n\n  ;; Iteration on sequences for quasiquote (right reduce/fold).\n  (func $qq_foldr (param $xs i32) (result i32)\n    (if ($VAL0 $xs) (then\n      (return ($qq_loop ($MEM_VAL1_ptr $xs) ($qq_foldr ($MEM_VAL0_ptr $xs)))))\n    (else\n      (return ($INC_REF (global.get $EMPTY_LIST))))))\n\n  ;; Transition function for quasiquote right fold/reduce.\n  (func $qq_loop (param $elt i32) (param $acc i32) (result i32)\n    (LET $sym 0 $second 0 $res 0)\n\n    ;; If elt is ('splice-unquote x) -> ('concat, x, acc)\n    (if (i32.eq ($TYPE $elt) (global.get $LIST_T)) (then\n      (local.set $second ($qq_unquote $elt \"splice-unquote\"))\n      (if $second (then\n        (local.set $sym    ($STRING (global.get $SYMBOL_T) \"concat\"))\n        (local.set $res    ($LIST3 $sym $second $acc))\n        ;; release inner quasiquoted since outer list takes ownership\n        ($RELEASE $sym)\n        (return $res)))))\n\n    ;; normal elt -> ('cons, (quasiquoted x), acc)\n    (local.set $sym    ($STRING (global.get $SYMBOL_T) \"cons\"))\n    (local.set $second ($QUASIQUOTE $elt))\n    (local.set $res    ($LIST3 $sym $second $acc))\n    ;; release inner quasiquoted since outer list takes ownership\n    ($RELEASE $second)\n    ($RELEASE $sym)\n    (return $res))\n\n\n  (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)\n    ;; Return a list/vector/map with evaluated elements\n    ;; of a list, vector or hashmap $ast\n    (LET $res 0 $val2 0 $val3 0 $type 0\n         $ret 0 $empty 0 $current 0)\n\n    (if (global.get $error_type) (return 0))\n    (local.set $type ($TYPE $ast))\n\n    ;;($PR_VALUE \">>> EVAL_AST ast: '%s'\\n\" $ast)\n\n      ;; MAP_LOOP_START\n      (local.set $res ($MAP_LOOP_START $type))\n      ;; push MAP_LOOP stack\n      ;;; empty = current = ret = res\n      (local.set $ret $res)\n      (local.set $current $res)\n      (local.set $empty $res)\n\n        (loop $loop\n          ;; check if we are done evaluating the source sequence\n          (if (OR (i32.eqz ($VAL0 $ast))\n                  (AND $skiplast\n                       (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)))))\n            (then\n              (return $ret)))\n\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))\n            (else\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))\n          (local.set $val2 $res)\n\n          ;; if error, release the unattached element\n          (if (global.get $error_type)\n            (then\n              ($RELEASE $res)\n              (return 0)))\n\n          ;; for hash-maps, copy the key (inc ref since we are going\n          ;; to release it below)\n          (if (i32.eq $type (global.get $HASHMAP_T))\n            (then\n              (local.set $val3 $val2)\n              (local.set $val2 ($MEM_VAL1_ptr $ast))\n              (drop ($INC_REF $val2))))\n\n          ;; MAP_LOOP_UPDATE\n          (local.set $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))\n          (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n            ;; if first element, set return to new element\n            (local.set $ret $res))\n          ;; update current to point to new element\n          (local.set $current $res)\n\n          (local.set $ast ($MEM_VAL0_ptr $ast))\n\n          (br $loop)\n        )\n      ;; MAP_LOOP_DONE\n  )\n\n  (func $MAL_GET_A1 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))\n  (func $MAL_GET_A2 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))\n  (func $MAL_GET_A3 (param $ast i32) (result i32)\n    ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n  (func $ECHO_IF_DEBUG_EVAL (param $ast i32 $env i32)\n    (LET $value ($ENV_GET $env (global.get $DEBUG_EVAL_S)))\n    (if (AND $value\n             (i32.ne $value (global.get $NIL))\n             (i32.ne $value (global.get $FALSE)))\n      (then\n        ($PR_VALUE \"EVAL: %s\\n\" $ast))))\n\n  (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)\n    (LET $ast $orig_ast\n         $env $orig_env\n         $prev_ast 0 $prev_env 0 $res 0 $el 0\n         $ftype 0 $ast_type 0 $f 0 $args 0\n         $a0 0 $a0sym 0 $a1 0 $a2 0\n         $err 0)\n\n    (block $EVAL_return\n    (loop $TCO_loop\n\n    (if (global.get $error_type)\n      (then\n        (local.set $res 0)\n        (br $EVAL_return)))\n\n    ($ECHO_IF_DEBUG_EVAL $ast $env)\n\n    (local.set $ast_type ($TYPE $ast))\n\n    (if (i32.eq $ast_type (global.get $SYMBOL_T))\n      (then\n        (local.set $res ($ENV_GET $env $ast))\n        (if (i32.eqz $res)\n          ($THROW_STR_1 \"'%s' not found\" ($to_String $ast)))\n        (br $EVAL_return)))\n\n    (if (OR (i32.eq $ast_type (global.get $VECTOR_T))\n            (i32.eq $ast_type (global.get $HASHMAP_T)))\n      (then\n        (local.set $res ($EVAL_AST $ast $env 0))\n        (br $EVAL_return)))\n\n    (if (i32.ne $ast_type (global.get $LIST_T))\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    ;; APPLY_LIST\n\n    (if ($EMPTY_Q $ast)\n      (then\n        (local.set $res ($INC_REF $ast))\n        (br $EVAL_return)))\n\n    (local.set $a0 ($MEM_VAL1_ptr $ast))\n    (local.set $a0sym \"\")\n    (if (i32.eq ($TYPE $a0) (global.get $SYMBOL_T))\n      (local.set $a0sym ($to_String $a0)))\n\n    (if (i32.eqz ($strcmp \"def!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($EVAL $a2 $env))\n        (br_if $EVAL_return (global.get $error_type))\n\n        ;; set a1 in env to a2\n        (local.set $res ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"let*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n\n        ;; create new environment with outer as current environment\n        (local.set $prev_env $env) ;; save env for later release\n        (local.set $env ($ENV_NEW $env))\n\n        (block $done\n          (loop $loop\n            (br_if $done (i32.eqz ($VAL0 $a1)))\n            ;; eval current A1 odd element\n            (local.set $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))\n\n            (br_if $done (global.get $error_type))\n\n            ;; set key/value in the let environment\n            (local.set $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))\n            ;; release our use, ENV_SET took ownership\n            ($RELEASE $res)\n\n            ;; skip to the next pair of a1 elements\n            (local.set $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))\n            (br $loop)\n          )\n        )\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"do\" $a0sym))\n      (then\n        ;; EVAL the rest through second to last\n        (local.set $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))\n        (local.set $ast ($LAST $ast))\n        ($RELEASE $ast) ;; we already own it via ast\n        ($RELEASE $el)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"quote\" $a0sym))\n      (then\n        (local.set $res ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"quasiquote\" $a0sym))\n      (then\n        (local.set $ast ($QUASIQUOTE ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))\n\n        ;; if we have already been here via TCO, release previous ast\n        (if $prev_ast ($RELEASE $prev_ast))\n        (local.set $prev_ast $ast)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"defmacro!\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $f ($EVAL $a2 $env))\n        (local.set $res ($MALFUNC ($MEM_VAL0_ptr $f)\n          ($MEM_VAL1_ptr $f) ($MEM_VAL2_ptr $f)))\n        ($SET_TYPE $res (global.get $MACRO_T))\n        (br_if $EVAL_return (global.get $error_type))\n        ($RELEASE $f)\n\n        ;; set a1 in env to a2\n        (drop ($ENV_SET $env $a1 $res))\n        (br $EVAL_return))\n    )\n    (if (i32.eqz ($strcmp \"try*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        ;; if there is no error, return\n        (br_if $EVAL_return (i32.eqz (global.get $error_type)))\n        ;; if there is an error and res is set, we need to free it\n        ($RELEASE $res)\n        ;; if there is no catch block then return\n        (br_if $EVAL_return\n               (i32.eqz ($VAL0 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))\n\n        ;; save the current environment for release\n        (local.set $prev_env $env)\n        ;; create environment for the catch block eval\n        (local.set $env ($ENV_NEW $env))\n\n        ;; set a1 and a2 from the catch block\n        (local.set $a1 ($MAL_GET_A1 ($MAL_GET_A2 $ast)))\n        (local.set $a2 ($MAL_GET_A2 ($MAL_GET_A2 $ast)))\n\n        ;; create object for string errors\n        (if (i32.eq (global.get $error_type) 1)\n          (then\n            (local.set $err ($STRING (global.get $STRING_T)\n                                     (global.get $error_str))))\n          (else\n            (local.set $err (global.get $error_val))))\n        ;; bind the catch symbol to the error object\n        (drop ($ENV_SET $env $a1 $err))\n        ;; release our use, env took ownership\n        ($RELEASE $err)\n\n        ;; unset error for catch eval\n        (global.set $error_type 0)\n        (i32.store (global.get $error_str) (CHR \"\\x00\"))\n\n        ;; release previous environment if not the current EVAL env\n        (if (i32.ne $prev_env $orig_env)\n          (then\n            ($RELEASE $prev_env)\n            (local.set $prev_env 0)))\n\n        (local.set $ast $a2)\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"if\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $res ($EVAL $a1 $env))\n\n        (if (global.get $error_type)\n          (then (nop))\n        (else (if (OR (i32.eq $res (global.get $NIL))\n                      (i32.eq $res (global.get $FALSE)))\n          (then\n            ($RELEASE $res)\n            ;; if no false case (A3), return nil\n            (if (i32.lt_u ($COUNT $ast) 4)\n              (then\n                (local.set $res ($INC_REF (global.get $NIL)))\n                (br $EVAL_return))\n              (else\n                (local.set $ast ($MAL_GET_A3 $ast)))))\n        (else\n          ($RELEASE $res)\n          (local.set $ast ($MAL_GET_A2 $ast))))))\n        (br $TCO_loop))\n    )\n    (if (i32.eqz ($strcmp \"fn*\" $a0sym))\n      (then\n        (local.set $a1 ($MAL_GET_A1 $ast))\n        (local.set $a2 ($MAL_GET_A2 $ast))\n        (local.set $res ($MALFUNC $a2 $a1 $env))\n        (br $EVAL_return))\n    )\n      ;; EVAL_INVOKE\n\n      ;; Evaluate the first element to find a function or macro.\n      (local.set $f ($EVAL $a0 $env))\n      (if (global.get $error_type)\n        (then\n          (local.set $res 0)\n          (br $EVAL_return)))\n\n      (local.set $ftype ($TYPE $f))\n\n      (if (i32.eq $ftype (global.get $MACRO_T))\n        (then\n          (local.set $ast ($APPLY $f ($MEM_VAL0_ptr $ast)))\n          ($RELEASE $f)\n          (if (global.get $error_type)\n            (then\n              (local.set $res 0)\n              (br $EVAL_return)))\n          (br $TCO_loop)))\n\n      ;; Evaluate the arguments.\n      (local.set $args ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 0))\n      ;; if error, return f/args for release by caller\n      (if (global.get $error_type)\n       (then\n         (local.set $res $f)\n         ($RELEASE $args)\n         (br $EVAL_return)))\n\n      (if (i32.eq $ftype (global.get $FUNCTION_T))\n        (then\n          (if (i32.eq ($VAL0 $f) 0) ;; eval\n            (then\n              (local.set $res ($EVAL ($MEM_VAL1_ptr $args)\n                                     (global.get $repl_env))))\n            (else\n              (local.set $res (call_indirect (type $fnT) $args ($VAL0 $f)))))\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n          (br $EVAL_return))\n      )\n      (if (i32.eq $ftype (global.get $MALFUNC_T))\n        (then\n          ;; save the current environment for release\n          (local.set $prev_env $env)\n          ;; create new environment using env and params stored in function\n          (local.set $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)\n                                          ($MEM_VAL1_ptr $f) $args))\n\n          ;; release previous environment if not the current EVAL env\n          ;; because our new env refers to it and we no longer need to\n          ;; track it (since we are TCO recurring)\n          (if (i32.ne $prev_env $orig_env)\n            (then\n              ($RELEASE $prev_env)\n              (local.set $prev_env 0)))\n\n          ;; claim the AST before releasing the list containing it\n          (local.set $ast ($MEM_VAL0_ptr $f))\n          (drop ($INC_REF $ast))\n\n          ;; if we have already been here via TCO, release previous\n          ;; ast\n          ;; PEND_A_LV\n          (if $prev_ast ($RELEASE $prev_ast))\n          (local.set $prev_ast $ast)\n\n          ;; release f/args\n          ($RELEASE $f)\n          ($RELEASE $args)\n\n          (br $TCO_loop))\n      )\n        ($THROW_STR_1 \"apply of non-function type: %d\\n\" $ftype)\n        (local.set $res 0)\n        ($RELEASE $f)\n        ($RELEASE $args)\n        (br $EVAL_return)\n\n    ) ;; end of TCO_loop\n    ) ;; end of EVAL_return\n\n    ;; EVAL_RETURN\n    (if (i32.ne $env $orig_env) ($RELEASE $env))\n    (if $prev_ast ($RELEASE $prev_ast))\n\n    $res\n  )\n\n  ;; PRINT\n  (func $PRINT (param $ast i32) (result i32)\n    ($pr_str $ast 1)\n  )\n\n  ;; REPL\n  (func $RE (param $line i32 $env i32) (result i32)\n    (LET $mv1 0 $res 0)\n    (block $done\n      (local.set $mv1 ($READ $line))\n      (br_if $done (global.get $error_type))\n\n      (local.set $res ($EVAL $mv1 $env))\n    )\n\n    ;; release memory from MAL_READ\n    ($RELEASE $mv1)\n    $res\n  )\n\n  (func $REP (param $line i32 $env i32) (result i32)\n    (LET $mv2 0 $ms 0)\n    (block $done\n      (local.set $mv2 ($RE $line $env))\n      (br_if $done (global.get $error_type))\n\n;;      ($PR_MEMORY -1 -1)\n      (local.set $ms ($PRINT $mv2))\n    )\n\n    ;; release memory from RE\n    ($RELEASE $mv2)\n    $ms\n  )\n\n  (func $main (param $argc i32 $argv i32) (result i32)\n    (LET $line (STATIC_ARRAY 201)\n         $res 0 $repl_env 0 $ms 0\n         ;; argument processing\n         $i 0 $ret 0 $empty 0 $current 0 $val2 0)\n\n    ;; DEBUG\n;;    ($printf_1 \"argc: 0x%x\\n\" $argc)\n;;    ($printf_1 \"memoryBase: 0x%x\\n\" (global.get $memoryBase))\n;;    ($printf_1 \"heap_start: 0x%x\\n\" (global.get $heap_start))\n;;    ($printf_1 \"heap_end: 0x%x\\n\" (global.get $heap_end))\n;;    ($printf_1 \"mem: 0x%x\\n\" (global.get $mem))\n;;    ($printf_1 \"string_mem: %d\\n\" (global.get $string_mem))\n\n    (global.set $DEBUG_EVAL_S ($STRING (global.get $SYMBOL_T) \"DEBUG-EVAL\"))\n    (global.set $repl_env ($ENV_NEW (global.get $NIL)))\n    (local.set $repl_env (global.get $repl_env))\n\n    ;; core.EXT: defined in wasm\n    ($add_core_ns $repl_env)\n    (drop ($ENV_SET_S $repl_env \"eval\" ($FUNCTION 0)))\n\n    ($checkpoint_user_memory)\n\n    ;; core.mal: defined using the language itself\n    ($RELEASE ($RE \"(def! *host-language* \\\"WebAssembly\\\")\" $repl_env))\n    ($RELEASE ($RE \"(def! not (fn* (a) (if a false true)))\" $repl_env))\n    ($RELEASE ($RE \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\" $repl_env))\n    ($RELEASE ($RE \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\" $repl_env))\n\n    ;; Command line arguments\n    (local.set $res ($MAP_LOOP_START (global.get $LIST_T)))\n    ;; push MAP_LOP stack\n    ;; empty = current = ret = res\n    (local.set $ret $res)\n    (local.set $current $res)\n    (local.set $empty $res)\n\n    (local.set $i 2)\n    (block $done\n      (loop $loop\n        (br_if $done (i32.ge_u $i $argc))\n\n\t(local.set $val2 ($STRING (global.get $STRING_T)\n                                  (i32.load (i32.add $argv (i32.mul $i 4)))))\n\n        ;; MAP_LOOP_UPDATE\n        (local.set $res ($MAP_LOOP_UPDATE\n                          (global.get $LIST_T) $empty $current $val2 0))\n        (if (i32.le_u $current (global.get $EMPTY_HASHMAP))\n          ;; if first element, set return to new element\n          (local.set $ret $res))\n        ;; update current to point to new element\n        (local.set $current $res)\n\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    (drop ($ENV_SET_S $repl_env \"*ARGV*\" $ret))\n\n\n    ;;($PR_MEMORY -1 -1)\n\n    (if (i32.gt_u $argc 1)\n      (then\n        (drop ($ENV_SET_S $repl_env\n                          \"*FILE*\" ($STRING (global.get $STRING_T)\n                                            (i32.load (i32.add $argv 4)))))\n        ($RELEASE ($RE \"(load-file *FILE*)\" $repl_env))\n        (if (global.get $error_type)\n          (then\n            ($printf_1 \"Error: %s\\n\" (global.get $error_str))\n            (return 1))\n          (else\n            (return 0)))))\n\n    ($RELEASE ($RE \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\" $repl_env))\n\n    ;; Start REPL\n    (block $repl_done\n      (loop $repl_loop\n        (br_if $repl_done (i32.eqz ($readline \"user> \" $line)))\n        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))\n        (local.set $res ($REP $line $repl_env))\n        (if (global.get $error_type)\n          (then\n            (if (i32.eq 2 (global.get $error_type))\n              (then\n                (local.set $ms ($pr_str (global.get $error_val) 1))\n                ($printf_1 \"Error: %s\\n\" ($to_String $ms))\n                ($RELEASE $ms)\n                ($RELEASE (global.get $error_val)))\n              (else\n                ($printf_1 \"Error: %s\\n\" (global.get $error_str))))\n            (global.set $error_type 0))\n          (else\n            ($printf_1 \"%s\\n\" ($to_String $res))))\n        ($RELEASE $res)\n        ;;($PR_MEMORY_SUMMARY_SMALL)\n        (br $repl_loop)\n      )\n    )\n\n    ($print \"\\n\")\n    ;;($PR_MEMORY -1 -1)\n    0\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/string.wam",
    "content": "(module $string\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n  ;; Copy len bytes from src to dst\n  ;; Returns len\n  (func $memmove  (param $dst i32 $src i32 $len i32)\n    (LET $idx 0)\n    (loop $copy\n          (i32.store8 (i32.add $idx $dst)\n                        (i32.load8_u (i32.add $idx $src)))\n      (local.set $idx (i32.add 1 $idx))\n      (br_if $copy (i32.lt_u $idx $len))\n    )\n  )\n\n  (func $strlen (param $str i32) (result i32)\n    (LET $cur $str)\n    (loop $count\n      (if (i32.ne 0 (i32.load8_u $cur))\n        (then\n          (local.set $cur (i32.add $cur 1))\n          (br $count)))\n    )\n    (i32.sub $cur $str)\n  )\n\n  ;; Based on https://stackoverflow.com/a/25705264/471795\n  ;; This could be made much more efficient\n  (func $strstr (param $haystack i32 $needle i32) (result i32)\n    (LET $i          0\n         $needle_len ($strlen $needle)\n         $len        ($strlen $haystack))\n\n    (if (i32.eq $needle_len 0) (return $haystack))\n\n    (local.set $i 0)\n    (block $done\n      (loop $loop\n        (if (i32.gt_s $i (i32.sub $len $needle_len)) (br $done))\n\n        (if (AND (i32.eq (i32.load8_u $haystack)\n                         (i32.load8_u $needle))\n                 (i32.eqz ($strncmp $haystack $needle $needle_len)))\n          (return $haystack))\n        (local.set $haystack (i32.add $haystack 1))\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    0\n  )\n\n  (func $atoi (param $str i32) (result i32)\n    (LET $acc 0\n         $i   0\n         $neg 0\n         $ch  0)\n    (block $done\n      (loop $loop\n        (local.set $ch (i32.load8_u (i32.add $str $i)))\n        (if (AND (i32.ne $ch (CHR \"-\"))\n                 (OR (i32.lt_u $ch (CHR \"0\"))\n                     (i32.gt_u $ch (CHR \"9\"))))\n          (br $done))\n        (local.set $i (i32.add $i 1))\n        (if (i32.eq $ch (CHR \"-\"))\n          (then\n            (local.set $neg 1))\n          (else\n            (local.set $acc (i32.add (i32.mul $acc 10)\n                                     (i32.sub $ch (CHR \"0\"))))))\n        (br $loop)\n      )\n    )\n    (if (result i32) $neg\n      (then (i32.sub 0 $acc))\n      (else $acc))\n  )\n\n  (func $strcmp (param $s1 i32 $s2 i32) (result i32)\n    (block $done\n      (loop $loop\n        (if (OR (i32.eqz (i32.load8_u $s1)) (i32.eqz (i32.load8_u $s2)))\n          (br $done))\n        (if (i32.ne (i32.load8_u $s1) (i32.load8_u $s2))\n          (br $done))\n        (local.set $s1 (i32.add $s1 1))\n        (local.set $s2 (i32.add $s2 1))\n        (br $loop)\n      )\n    )\n    (if (result i32) (i32.eq (i32.load8_u $s1) (i32.load8_u $s2))\n      (then 0)\n      (else\n        (if (result i32) (i32.lt_u (i32.load8_u $s1) (i32.load8_u $s2))\n          (then -1)\n          (else 1))))\n  )\n\n  (func $strncmp (param $s1 i32 $s2 i32 $len i32) (result i32)\n    (LET $i 0)\n    (if (i32.eq $len 0) (return 0))\n    (block $done\n      (loop $loop\n        (if (i32.ge_u $i $len) (br $done))\n        (if (i32.eqz (i32.load8_u (i32.add $i $s1))) (br $done))\n        (if (i32.ne (i32.load8_u (i32.add $i $s1))\n                    (i32.load8_u (i32.add $i $s2))) (br $done))\n        (local.set $i (i32.add $i 1))\n        (br $loop)\n      )\n    )\n    (if (OR (i32.eq $i $len)\n            (i32.eq (i32.load8_u (i32.add $i $s1))\n                    (i32.load8_u (i32.add $i $s2))))\n      (return 0))\n    (if (result i32) (i32.lt_u (i32.load8_u (i32.add $i $s1))\n                      (i32.load8_u (i32.add $i $s2)))\n      (then -1)\n      (else 1))\n  )\n\n  ;; Writes new string to grass with all needles in haystack replaced.\n  ;; If the length of replace is equal to of less than needle then\n  ;; grass can be NULL.\n  ;; Returns length of grass.\n  (func $REPLACE3 (param $grass i32 $haystack i32\n                         $needle0 i32 $replace0 i32\n                         $needle1 i32 $replace1 i32\n                         $needle2 i32 $replace2 i32) (result i32)\n    (LET $haystack_len ($strlen $haystack)\n         $src_str      $haystack\n         $dst_str      $grass\n         $s 0 $found_tmp 0 $found 0\n         $needle 0 $replace 0 $needle_len 0 $replace_len 0\n         $replace_s 0 $replace_len_s 0 $needle_len_s 0)\n\n    ;; in-place\n    (if (i32.eqz $grass)\n      (then\n        ;; check that we aren't expanding in place\n        (local.set $s 0)\n        (block $done\n          (loop $loop\n            (if (i32.ge_u $s 3) (br $done))\n            (local.set $needle (if (result i32) (i32.eq $s 0)   $needle0\n                                 (if (result i32) (i32.eq $s 1) $needle1\n                                                       $needle2)))\n            (local.set $replace (if (result i32) (i32.eq $s 0)   $replace0\n                                  (if (result i32) (i32.eq $s 1) $replace1\n                                                        $replace2)))\n            (local.set $needle_len ($strlen $needle))\n            (local.set $replace_len ($strlen $replace))\n            (if (i32.gt_u $replace_len $needle_len)\n              ($fatal 7 \"REPLACE: invalid expanding in-place call\\n\"))\n            (local.set $s (i32.add $s 1))\n            (br $loop)\n          )\n        )\n        (local.set $grass $haystack)\n        (local.set $dst_str $grass)))\n\n    (block $done1\n      (loop $loop1\n        (if (i32.ge_s (i32.sub $src_str $haystack) $haystack_len)\n          (br $done1))\n\n        ;; Find the earliest match\n        (local.set $found 0)\n        (local.set $s 0)\n        (block $done2\n          (loop $loop2\n            (if (i32.ge_u $s 3) (br $done2))\n            (local.set $needle (if (result i32) (i32.eq $s 0)   $needle0\n                                 (if (result i32) (i32.eq $s 1) $needle1\n                                                       $needle2)))\n            (local.set $replace (if (result i32) (i32.eq $s 0)   $replace0\n                                  (if (result i32) (i32.eq $s 1) $replace1\n                                                        $replace2)))\n            (local.set $s (i32.add $s 1))\n            (local.set $found_tmp ($strstr $src_str $needle))\n            (if (i32.eqz $found_tmp) (br $loop2))\n            (if (OR (i32.eqz $found) (i32.lt_s $found_tmp $found))\n              (then\n                (local.set $found $found_tmp)\n                (local.set $needle_len_s ($strlen $needle))\n                (local.set $replace_s $replace)\n                (local.set $replace_len_s ($strlen $replace))))\n            (br $loop2)\n          )\n        )\n        (if (i32.eqz $found) (br $done1))\n        ;; copy before the match\n        ($memmove $dst_str $src_str (i32.add (i32.sub $found $src_str) 1))\n        (local.set $dst_str (i32.add $dst_str (i32.sub $found $src_str)))\n        ;; add the replace string\n        ($memmove $dst_str $replace_s (i32.add $replace_len_s 1))\n        (local.set $dst_str (i32.add $dst_str $replace_len_s))\n        ;; Move to after the match\n        (local.set $src_str (i32.add $found $needle_len_s))\n        (br $loop1)\n      )\n    )\n\n    ;; Copy the left-over\n    ($memmove $dst_str $src_str ($strlen $src_str))\n    (local.set $dst_str (i32.add $dst_str ($strlen $src_str)))\n    (i32.store8 $dst_str (CHR \"\\x00\"))\n\n    (i32.sub $dst_str $grass)\n  )\n\n)\n\n"
  },
  {
    "path": "impls/wasm/types.wam",
    "content": ";; Mal value memory layout\n;;   type           words\n;;   ----------     ----------\n;;   nil            ref/ 0 |  0           |               |\n;;   false          ref/ 1 |  0           |               |\n;;   true           ref/ 1 |  1           |               |\n;;   integer        ref/ 2 | int          |               |\n;;   float          ref/ 3 | ???          |               |\n;;   string/kw      ref/ 4 | string ptr   |               |\n;;   symbol         ref/ 5 | string ptr   |               |\n;;   list           ref/ 6 | next mem idx | val mem idx   |\n;;   vector         ref/ 7 | next mem idx | val mem idx   |\n;;   hashmap        ref/ 8 | next mem idx | key mem idx   | val mem idx\n;;   function       ref/ 9 | fn idx       |               |\n;;   mal function   ref/10 | body mem idx | param mem idx | env mem idx\n;;   macro fn       ref/11 | body mem idx | param mem idx | env mem idx\n;;   atom           ref/12 | val mem idx  |               |\n;;   environment    ref/13 | hmap mem idx | outer mem idx |\n;;   metadata       ref/14 | obj mem idx  | meta mem idx  |\n;;   FREE            sz/15 | next mem idx |               |\n\n(module $types\n\n  (global $NIL_T                  i32 0)\n  (global $BOOLEAN_T              i32 1)\n  (global $INTEGER_T              i32 2)\n  (global $FLOAT_T                i32 3)\n  (global $STRING_T               i32 4)\n  (global $SYMBOL_T               i32 5)\n  (global $LIST_T                 i32 6)\n  (global $VECTOR_T               i32 7)\n  (global $HASHMAP_T              i32 8)\n  (global $FUNCTION_T             i32 9)\n  (global $MALFUNC_T              i32 10)\n  (global $MACRO_T                i32 11)\n  (global $ATOM_T                 i32 12)\n  (global $ENVIRONMENT_T          i32 13)\n  (global $METADATA_T             i32 14)\n  (global $FREE_T                 i32 15)\n\n  (global $error_type             (mut i32) 0)\n  (global $error_val              (mut i32) 0)\n  ;; Index into static string memory (static.wast)\n  (global $error_str              (mut i32) 0)\n\n  (global $NIL                    (mut i32) 0)\n  (global $FALSE                  (mut i32) 0)\n  (global $TRUE                   (mut i32) 0)\n  (global $EMPTY_LIST             (mut i32) 0)\n  (global $EMPTY_VECTOR           (mut i32) 0)\n  (global $EMPTY_HASHMAP          (mut i32) 0)\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; General functions\n\n  (func $INC_REF (param $mv i32) (result i32)\n    (i32.store $mv (i32.add (i32.load $mv) 32))\n    $mv\n  )\n\n  (func $TRUE_FALSE (param $val i32) (result i32)\n    ($INC_REF (if (result i32) $val (global.get $TRUE) (global.get $FALSE)))\n  )\n\n  (func $THROW_STR_0 (param $fmt i32)\n    (drop ($sprintf_1 (global.get $error_str) $fmt \"\"))\n    (global.set $error_type 1)\n  )\n\n  (func $THROW_STR_1 (param $fmt i32) (param $v0 i32)\n    (drop ($sprintf_1 (global.get $error_str) $fmt $v0))\n    (global.set $error_type 1)\n  )\n\n  (func $EQUAL_Q (param $a i32 $b i32) (result i32)\n    (LET $ta ($TYPE $a)\n         $tb ($TYPE $b))\n\n    (if (AND (OR (i32.eq $ta (global.get $LIST_T))\n                 (i32.eq $ta (global.get $VECTOR_T)))\n             (OR (i32.eq $tb (global.get $LIST_T))\n                 (i32.eq $tb (global.get $VECTOR_T))))\n      (then\n        ;; EQUAL_Q_SEQ\n        (block $done\n          (loop $loop\n            (if (OR (i32.eq ($VAL0 $a) 0) (i32.eq ($VAL0 $b) 0))\n              (br $done))\n            (if ($EQUAL_Q ($MEM_VAL1_ptr $a) ($MEM_VAL1_ptr $b))\n              (then\n                (local.set $a ($MEM_VAL0_ptr $a))\n                (local.set $b ($MEM_VAL0_ptr $b)))\n              (else\n                (return 0)))\n            (br $loop)\n          )\n        )\n        (return (AND (i32.eq ($VAL0 $a) 0) (i32.eq ($VAL0 $b) 0))))\n    (else (if (AND (i32.eq $ta (global.get $HASHMAP_T))\n                   (i32.eq $tb (global.get $HASHMAP_T)))\n      ;; EQUAL_Q_HM\n      (then (return 1))\n    ;; TODO: remove this once strings are interned\n    (else (if (OR (AND (i32.eq $ta (global.get $STRING_T))\n                       (i32.eq $tb (global.get $STRING_T)))\n                  (AND (i32.eq $ta (global.get $SYMBOL_T))\n                       (i32.eq $tb (global.get $SYMBOL_T))))\n      (then (return (i32.eqz ($strcmp ($to_String $a) ($to_String $b)))))\n    (else\n      (return (AND (i32.eq $ta $tb)\n                   (i32.eq ($VAL0 $a) ($VAL0 $b))))))))))\n    0 ;; not reachable\n  )\n\n  (func $DEREF_META (param $mv i32) (result i32)\n    (loop $loop\n      (if (i32.eq ($TYPE $mv) (global.get $METADATA_T))\n        (then\n          (local.set $mv ($MEM_VAL0_ptr $mv))\n          (br $loop)))\n    )\n    $mv\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; string functions\n\n  (func $to_MalString (param $mv i32) (result i32)\n    ;; TODO: assert mv is a string/keyword/symbol\n    (i32.add (global.get $string_mem) ($VAL0 $mv))\n  )\n\n  (func $to_String (param $mv i32) (result i32)\n    ;; skip string refcnt and size\n    (i32.add 4 ($to_MalString $mv))\n  )\n\n  ;; Duplicate regular character array string into a Mal string and\n  ;; return the MalVal pointer\n  (func $STRING (param $type i32 $str i32) (result i32)\n    (LET $ms ($ALLOC_STRING $str ($strlen $str) 1))\n    ($ALLOC_SCALAR $type (i32.sub $ms (global.get $string_mem)))\n  )\n\n  ;; Find first duplicate (internet) of mv. If one is found, free up\n  ;; mv and return the interned version. If no duplicate is found,\n  ;; return NULL.\n  (func $INTERN_STRING (param $mv i32) (result i32)\n    (LET $res         0\n         $ms          ($to_MalString $mv)\n         $existing_ms ($FIND_STRING (i32.add $ms 4))\n         $tmp         0)\n    (if (AND $existing_ms (i32.lt_s $existing_ms $ms))\n      (then\n        (local.set $tmp $mv)\n        (local.set $res ($ALLOC_SCALAR (global.get $STRING_T)\n                                       (i32.sub $existing_ms\n                                                  (global.get $string_mem))))\n        (i32.store16 $existing_ms (i32.add (i32.load16_u $existing_ms) 1))\n        ($RELEASE $tmp)))\n    $res\n  )\n\n  (func $STRING_INIT (param $type i32) (result i32)\n    (LET $ms ($ALLOC_STRING \"\" 0 0))\n    ($ALLOC_SCALAR $type (i32.sub $ms (global.get $string_mem)))\n  )\n\n  (func $STRING_FINALIZE (param $mv i32 $size i32) (result i32)\n    ;; Check if the new string can be interned.\n    (LET $tmp ($INTERN_STRING $mv)\n         $ms  ($to_MalString $mv))\n    (if $tmp\n      (then\n        (local.set $mv $tmp))\n      (else\n        ;;; ms->size = sizeof(MalString) + size + 1\n        (i32.store16 (i32.add $ms 2)\n                     (i32.add (i32.add 4 $size) 1))\n        ;;; string_mem_next = (void *)ms  + ms->size\n        (global.set $string_mem_next\n                    (i32.add $ms (i32.load16_u (i32.add $ms 2))))))\n    $mv\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; numeric functions\n\n  (func $INTEGER (param $val i32) (result i32)\n    ($ALLOC_SCALAR (global.get $INTEGER_T) $val)\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; sequence functions\n\n  (func $MAP_LOOP_START (param $type i32) (result i32)\n    (LET $res (if (result i32) (i32.eq $type (global.get $LIST_T))\n                (then (global.get $EMPTY_LIST))\n              (else (if (result i32) (i32.eq $type (global.get $VECTOR_T))\n                (then (global.get $EMPTY_VECTOR))\n              (else (if (result i32) (i32.eq $type (global.get $HASHMAP_T))\n                (then (global.get $EMPTY_HASHMAP))\n              (else\n                ($THROW_STR_1 \"read_seq invalid type %d\" $type)\n                0)))))))\n\n    ($INC_REF $res)\n  )\n\n  (func $MAP_LOOP_UPDATE (param $type i32) (param $empty i32)\n        (param $current i32) (param $val2 i32) (param $val3 i32)\n        (result i32)\n    (LET $res ($ALLOC $type $empty $val2 $val3))\n\n    ;; sequence took ownership\n    ($RELEASE $empty)\n    ($RELEASE $val2)\n    (if (i32.eq $type (global.get $HASHMAP_T))\n      ($RELEASE $val3))\n    (if (i32.gt_u $current (global.get $EMPTY_HASHMAP))\n      ;; if not first element, set current next to point to new element\n      (i32.store ($VAL0_ptr $current) ($IDX $res)))\n\n    $res\n  )\n\n  (func $FORCE_SEQ_TYPE (param $type i32) (param $mv i32) (result i32)\n    (LET $res 0)\n    ;; if it's already the right type, inc ref cnt and return it\n    (if (i32.eq $type ($TYPE $mv)) (return ($INC_REF $mv)))\n    ;; if it's empty, return the sequence match\n    (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))\n      (return ($MAP_LOOP_START $type)))\n    ;; otherwise, copy first element to turn it into correct type\n    ($ALLOC $type ($MEM_VAL0_ptr $mv) ($MEM_VAL1_ptr $mv) 0)\n  )\n\n  (func $LIST (param $seq i32 $first i32) (result i32)\n    ($ALLOC (global.get $LIST_T) $seq $first 0)\n  )\n\n  (func $LIST2 (param $first i32 $second i32) (result i32)\n    ;; last element is empty list\n    (LET $tmp ($LIST (global.get $EMPTY_LIST) $second)\n         $res ($LIST $tmp $first))\n    ($RELEASE $tmp) ;; new list takes ownership of previous\n    $res\n  )\n\n  (func $LIST3 (param $first i32 $second i32 $third i32) (result i32)\n    (LET $tmp ($LIST2 $second $third)\n         $res ($LIST $tmp $first))\n    ($RELEASE $tmp) ;; new list takes ownership of previous\n    $res\n  )\n\n  (func $LIST_Q (param $mv i32) (result i32)\n    (i32.eq ($TYPE $mv) (global.get $LIST_T))\n  )\n\n  (func $EMPTY_Q (param $mv i32) (result i32)\n    (i32.eq ($VAL0 $mv) 0)\n  )\n\n  (func $COUNT (param $mv i32) (result i32)\n    (LET $cnt 0)\n    (block $done\n      (loop $loop\n        (if (i32.eq ($VAL0 $mv) 0) (br $done))\n        (local.set $cnt (i32.add $cnt 1))\n        (local.set $mv ($MEM_VAL0_ptr $mv))\n        (br $loop)\n      )\n    )\n    $cnt\n  )\n\n  (func $LAST (param $mv i32) (result i32)\n    (LET $cur 0)\n    ;; TODO: check that actually a list/vector\n    (if (i32.eq ($VAL0 $mv) 0)\n      ;; empty seq, return nil\n      (return ($INC_REF (global.get $NIL))))\n    (block $done\n      (loop $loop\n        ;; end, return previous value\n        (if (i32.eq ($VAL0 $mv) 0) (br $done))\n        ;; current becomes previous entry\n        (local.set $cur $mv)\n        ;; next entry\n        (local.set $mv ($MEM_VAL0_ptr $mv))\n        (br $loop)\n      )\n    )\n    ($INC_REF ($MEM_VAL1_ptr $cur))\n  )\n\n  ;; make a copy of sequence seq from index start to end\n  ;; set last to last element of slice before the empty\n  ;; set after to element following slice (or original)\n  (func $SLICE (param $seq i32) (param $start i32) (param $end i32)\n        (result i64)\n    (LET $idx  0\n         $res  ($INC_REF (global.get $EMPTY_LIST))\n         $last 0\n         $tmp  $res)\n    ;; advance seq to start\n    (block $done\n      (loop $loop\n        (if (OR (i32.ge_s $idx $start)\n                (i32.eqz ($VAL0 $seq)))\n          (br $done))\n        (local.set $seq ($MEM_VAL0_ptr $seq))\n        (local.set $idx (i32.add $idx 1))\n        (br $loop)\n      )\n    )\n    (block $done\n      (loop $loop\n\t;; if current position is at end, then return or if we reached\n        ;; end seq, then return\n        (if (OR (AND (i32.ne $end -1)\n                     (i32.ge_s $idx $end))\n                (i32.eqz ($VAL0 $seq)))\n          (then\n            (local.set $res $tmp)\n            (br $done)))\n        ;; allocate new list element with copied value\n        (local.set $res ($LIST (global.get $EMPTY_LIST)\n                               ($MEM_VAL1_ptr $seq)))\n        ;; sequence took ownership\n        ($RELEASE (global.get $EMPTY_LIST))\n        (if (i32.eqz $last)\n          (then\n            ;; if first element, set return value to new element\n            (local.set $tmp $res))\n          (else\n            ;; if not the first element, set return value to new element\n            (i32.store ($VAL0_ptr $last) ($IDX $res))))\n        (local.set $last $res) ;; update last list element\n        ;; advance to next element of seq\n        (local.set $seq ($MEM_VAL0_ptr $seq))\n        (local.set $idx (i32.add $idx 1))\n        (br $loop)\n      )\n    )\n\n    ;; combine last/res as hi 32/low 32 of i64\n    (i64.or\n      (i64.shl (i64.extend_i32_u $last) (i64.const 32))\n      (i64.extend_i32_u $res))\n  )\n\n  (func $HASHMAP (result i32)\n    ;; just point to static empty hash-map\n    ($INC_REF (global.get $EMPTY_HASHMAP))\n  )\n\n  (func $ASSOC1 (param $hm i32 $k i32 $v i32) (result i32)\n    (LET $res ($ALLOC (global.get $HASHMAP_T) $hm $k $v))\n    ;; we took ownership of previous release\n    ($RELEASE $hm)\n    $res\n  )\n\n  (func $ASSOC1_S (param $hm i32 $k i32 $v i32) (result i32)\n    (LET $kmv ($STRING (global.get $STRING_T) $k)\n         $res ($ASSOC1 $hm $kmv $v))\n    ;; map took ownership of key\n    ($RELEASE $kmv)\n    $res\n  )\n\n  (func $HASHMAP_GET (param $hm i32) (param $key_mv i32) (result i64)\n    (LET $key         ($to_String $key_mv)\n         $found       0\n         $res         0\n         $test_key_mv 0)\n\n    (block $done\n      (loop $loop\n        ;;; if (VAL0(hm) == 0)\n        (if (i32.eq ($VAL0 $hm) 0)\n          (then\n            (local.set $res (global.get $NIL))\n            (br $done)))\n        ;;; test_key_mv = MEM_VAL1(hm)\n        (local.set $test_key_mv ($MEM_VAL1_ptr $hm))\n        ;;; if (strcmp(key, to_String(test_key_mv)) == 0)\n        (if (i32.eq ($strcmp $key ($to_String $test_key_mv)) 0)\n          (then\n            (local.set $found 1)\n            (local.set $res ($MEM_VAL2_ptr $hm))\n            (br $done)))\n        (local.set $hm ($MEM_VAL0_ptr $hm))\n\n        (br $loop)\n      )\n    )\n\n    ;; combine found/res as hi 32/low 32 of i64\n    (i64.or (i64.shl (i64.extend_i32_u $found) (i64.const 32))\n            (i64.extend_i32_u $res))\n  )\n\n  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n  ;; function functions\n\n  (func $FUNCTION (param $index i32) (result i32)\n    ($ALLOC_SCALAR (global.get $FUNCTION_T) $index)\n  )\n\n  (func $MALFUNC (param $ast i32 $params i32 $env i32) (result i32)\n    ($ALLOC (global.get $MALFUNC_T) $ast $params $env)\n  )\n\n)\n"
  },
  {
    "path": "impls/wren/Dockerfile",
    "content": "FROM ubuntu:18.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install g++\nRUN apt-get -y install git\n\nCOPY wren-add-gettimeofday.patch /tmp/\nRUN cd /tmp && git clone --depth=1 https://github.com/wren-lang/wren.git \\\n    && cd wren \\\n    && patch -p1 < /tmp/wren-add-gettimeofday.patch \\\n    && make \\\n    && cp ./wren /usr/local/bin/ \\\n    && cd /tmp && rm -rf wren\n"
  },
  {
    "path": "impls/wren/Makefile",
    "content": "SOURCES = types.wren env.wren printer.wren reader.wren readline.wren interop.wren core.wren stepA_mal.wren\n\nall:\n\ttrue\n\ndist: mal\n\nmal.wren: $(SOURCES)\n\tcat $+ | grep -v '^import \"./' > $@\n\nmal: mal.wren\n\techo \"#!/usr/bin/env wren\" > $@\n\tcat $< >> $@\n\tchmod +x $@\n\n.PHONY: clean\n\nclean:\n\trm -f mal.wren mal\n"
  },
  {
    "path": "impls/wren/README.md",
    "content": "# Wren implementation\n\n### Adding a time function\n\nSince Wren doesn't have a time function, we add a `System.gettimeofday`\nfunction which returns a float with the number of seconds since epoch (with\nfractions of seconds).\n\nThis is done by applying the patch in `wren-add-gettimeofday.path` to Wren's\nsource code before compiling it (see `Dockerfile`).\n\n### Wren interop\n\nSee examples in `tests/stepA_mal.mal` for usage of `wren-eval` to evaluate Wren\nexpressions inside a Mal program.\n"
  },
  {
    "path": "impls/wren/core.wren",
    "content": "import \"io\" for File\nimport \"./reader\" for MalReader\nimport \"./readline\" for Readline\nimport \"./printer\" for Printer\nimport \"./types\" for MalVal, MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalAtom, MalException\nimport \"./interop\" for Interop\n\nclass Core {\n  static fn(func) { MalNativeFn.new(func) }\n  static ns {\n    return {\n      \"=\":     fn { |a| a[0] == a[1] },\n      \"throw\": fn { |a|\n                 MalException.set(a[0])\n                 Fiber.abort(\"___MalException___\")\n               },\n\n      \"nil?\":     fn { |a| a[0] == null },\n      \"true?\":    fn { |a| a[0] == true },\n      \"false?\":   fn { |a| a[0] == false },\n      \"string?\":  fn { |a| a[0] is String && !MalVal.isKeyword(a[0]) },\n      \"symbol\":   fn { |a| a[0] is MalSymbol ? a[0] : MalSymbol.new(a[0]) },\n      \"symbol?\":  fn { |a| a[0] is MalSymbol },\n      \"keyword\":  fn { |a| MalVal.isKeyword(a[0]) ? a[0] : MalVal.newKeyword(a[0]) },\n      \"keyword?\": fn { |a| MalVal.isKeyword(a[0]) },\n      \"number?\":  fn { |a| a[0] is Num },\n      \"fn?\":      fn { |a| a[0] is MalNativeFn || (a[0] is MalFn && !a[0].isMacro) },\n      \"macro?\":   fn { |a| a[0] is MalFn && a[0].isMacro },\n\n      \"pr-str\":      fn { |a| a.map { |e| Printer.pr_str(e, true) }.join(\" \") },\n      \"str\":         fn { |a| a.map { |e| Printer.pr_str(e, false) }.join() },\n      \"prn\":         fn { |a|\n                       System.print(a.map { |e| Printer.pr_str(e, true) }.join(\" \"))\n                       return null\n                     },\n      \"println\":     fn { |a|\n                       System.print(a.map { |e| Printer.pr_str(e, false) }.join(\" \"))\n                       return null\n                     },\n      \"read-string\": fn { |a| MalReader.read_str(a[0]) },\n      \"readline\":    fn { |a| Readline.readLine(a[0]) },\n      \"slurp\":       fn { |a| File.read(a[0]) },\n\n      \"<\":       fn { |a| a[0] < a[1] },\n      \"<=\":      fn { |a| a[0] <= a[1] },\n      \">\":       fn { |a| a[0] > a[1] },\n      \">=\":      fn { |a| a[0] >= a[1] },\n      \"+\":       fn { |a| a[0] + a[1] },\n      \"-\":       fn { |a| a[0] - a[1] },\n      \"*\":       fn { |a| a[0] * a[1] },\n      \"/\":       fn { |a| a[0] / a[1] },\n      \"time-ms\": fn { |a| (System.gettimeofday * 1000).floor },\n\n      \"list\":      fn { |a| MalList.new(a) },\n      \"list?\":     fn { |a| a[0] is MalList },\n      \"vector\":    fn { |a| MalVector.new(a) },\n      \"vector?\":   fn { |a| a[0] is MalVector },\n      \"hash-map\":  fn { |a| MalMap.fromList(a) },\n      \"map?\":      fn { |a| a[0] is MalMap },\n      \"assoc\":     fn { |a| a[0].assoc(a[1...a.count]) },\n      \"dissoc\":    fn { |a| a[0].dissoc(a[1...a.count]) },\n      \"get\":       fn { |a| a[0] == null ? null : a[0].data[a[1]] },\n      \"contains?\": fn { |a| a[0].data.containsKey(a[1]) },\n      \"keys\":      fn { |a| MalList.new(a[0].data.keys.toList) },\n      \"vals\":      fn { |a| MalList.new(a[0].data.values.toList) },\n\n      \"sequential?\": fn { |a| a[0] is MalSequential },\n      \"cons\":        fn { |a| MalList.new([a[0]] + a[1].elements) },\n      \"concat\":      fn { |a| MalList.new(a.reduce([]) { |acc,e| acc + e.elements }) },\n      \"vec\":         fn { |a| MalVector.new(a[0].elements) },\n      \"nth\":         fn { |a| a[1] < a[0].count ? a[0][a[1]] : Fiber.abort(\"nth: index out of range\") },\n      \"first\":       fn { |a| a[0] == null ? null : a[0].first },\n      \"rest\":        fn { |a| a[0] == null ? MalList.new([]) : a[0].rest },\n      \"empty?\":      fn { |a| a[0].isEmpty },\n      \"count\":       fn { |a| a[0] == null ? 0 : a[0].count },\n      \"apply\":       fn { |a| a[0].call(a[1...(a.count - 1)] + a[-1].elements) },\n      \"map\":         fn { |a| MalList.new(a[1].elements.map { |e| a[0].call([e]) }.toList) },\n\n      \"conj\": fn { |a|\n                if (a[0] is MalList) return MalList.new(a[-1..1] + a[0].elements)\n                if (a[0] is MalVector) return MalVector.new(a[0].elements + a[1..-1])\n              },\n      \"seq\":  fn { |a|\n                if (a[0] == null) return null\n                if (a[0].count == 0) return null\n                if (a[0] is String) return MalList.new(a[0].toList)\n                if (a[0] is MalVector) return MalList.new(a[0].elements)\n                return a[0]\n              },\n\n      \"meta\":      fn { |a| a[0].meta },\n      \"with-meta\": fn { |a|\n                     var x = a[0].clone()\n                     x.meta = a[1]\n                     return x\n                   },\n      \"atom\":      fn { |a| MalAtom.new(a[0]) },\n      \"atom?\":     fn { |a| a[0] is MalAtom },\n      \"deref\":     fn { |a| a[0].value },\n      \"reset!\":    fn { |a| a[0].value = a[1] },\n      \"swap!\":     fn { |a| a[0].value = a[1].call([a[0].value] + a[2..-1]) },\n\n      \"wren-eval\": fn { |a| Interop.wren_eval(a[0]) }\n    }\n  }\n}\n"
  },
  {
    "path": "impls/wren/env.wren",
    "content": "import \"./types\" for MalList\n\nclass Env {\n  construct new() {\n    _outer = null\n    _data = {}\n  }\n  construct new(outer) {\n    _outer = outer\n    _data = {}\n  }\n  construct new(outer, binds, exprs) {\n    _outer = outer\n    _data = {}\n    for (i in 0...binds.count) {\n      if (binds[i].value == \"&\") {\n        _data[binds[i + 1].value] = MalList.new(exprs[i..-1])\n        break\n      } else {\n        _data[binds[i].value] = exprs[i]\n      }\n    }\n  }\n\n  set(k, v) { _data[k] = v }\n\n  find(k) {\n    if (_data.containsKey(k)) return this\n    if (_outer) return _outer.find(k)\n    return null\n  }\n\n  get(k) {\n    var foundEnv = find(k)\n    if (!foundEnv) Fiber.abort(\"'%(k)' not found\")\n    return foundEnv.getValue(k)\n  }\n\n  getValue(k) { _data[k] }\n}\n"
  },
  {
    "path": "impls/wren/interop.wren",
    "content": "import \"meta\" for Meta\nimport \"./types\" for MalList, MalMap\n\nclass Interop {\n  static wren_eval(str) {\n    var f = Meta.compileExpression(str)\n    return f == null ? null : wren2mal(f.call())\n  }\n\n  static wren2mal(v) {\n    if (v == null || v == true || v == false) return v\n    if (v is Num || v is String) return v\n    if (v is Map) {\n      var m = {}\n      for (e in v) {\n        m[wren2mal(e.key)] = wren2mal(e.value)\n      }\n      return MalMap.new(m)\n    }\n    if (v is Sequence) return MalList.new(v.map { |e| wren2mal(e) }.toList)\n    return null\n  }\n}\n"
  },
  {
    "path": "impls/wren/printer.wren",
    "content": "import \"./types\" for MalVal, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalAtom\n\nclass Printer {\n  static joinElements(elements, print_readably) {\n    return elements.map { |e| pr_str(e, print_readably) }.join(\" \")\n  }\n\n  static joinMapElements(data, print_readably) {\n    return data.map { |e| pr_str(e.key, print_readably) + \" \" + pr_str(e.value, print_readably) }.join(\" \")\n  }\n\n  static escape(s) {\n    return \"\\\"\" + s.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\").replace(\"\\n\", \"\\\\n\") + \"\\\"\"\n  }\n\n  static pr_str(obj) { pr_str(obj, true) }\n\n  static pr_str(obj, print_readably) {\n    if (obj == null) return \"nil\"\n    if (obj is MalList) return \"(%(joinElements(obj.elements, print_readably)))\"\n    if (obj is MalVector) return \"[%(joinElements(obj.elements, print_readably))]\"\n    if (obj is MalMap) return \"{%(joinMapElements(obj.data, print_readably))}\"\n    if (obj is MalNativeFn) return \"#<MalNativeFn>\"\n    if (obj is MalFn) return \"#<MalFn>\"\n    if (obj is MalAtom) return \"(atom %(pr_str(obj.value, print_readably)))\"\n    if (MalVal.isKeyword(obj)) return \":%(obj[1..-1])\"\n    if (obj is String) return print_readably ? escape(obj) : obj\n    return obj.toString\n  }\n}\n"
  },
  {
    "path": "impls/wren/reader.wren",
    "content": "import \"./types\" for MalVal, MalSymbol, MalList, MalVector, MalMap\n\nclass Tokenizer {\n  construct new(s) {\n    _s = s\n  }\n\n  tokenize() {\n    _pos = 0\n    var tokens = []\n    while (true) {\n      var token = nextToken()\n      if (token == null) break\n      if (token.count > 0) tokens.add(token)\n    }\n    return tokens\n  }\n\n  static eolChars { \"\\r\\n\" }\n  static whitespace { \" ,\\r\\n\\t\" }\n  static delimiters { \"[]{}()'`^@\" }\n  static separators { Tokenizer.whitespace + \"[]{}()'\\\"`,;\" }\n\n  nextToken() {\n    if (isEOF()) return null\n    var ch = curr\n    if (Tokenizer.whitespace.contains(ch)) {\n      advance()\n      return \"\"\n    }\n    if (Tokenizer.delimiters.contains(ch)) {\n      advance()\n      return ch\n    }\n    if (ch == \"~\") {\n      advance()\n      if (!isEOF() && curr == \"@\") {\n        advance()\n        return \"~@\"\n      } else {\n        return \"~\"\n      }\n    }\n    if (ch == \";\") {\n      advance()\n      while (!isEOF() && !Tokenizer.eolChars.contains(curr)) advance()\n      return \"\"\n    }\n    if (ch == \"\\\"\") {\n      var s = ch\n      advance()\n      while (!isEOF() && curr != \"\\\"\") {\n        if (curr == \"\\\\\") {\n          s = s + curr\n          advance()\n          if (isEOF()) Fiber.abort(\"expected '\\\"', got EOF 111\")\n        }\n        s = s + curr\n        advance()\n      }\n      if (isEOF()) Fiber.abort(\"expected '\\\"', got EOF 222\")\n      s = s + curr\n      advance()\n      return s\n    }\n    var token = ch\n    advance()\n    while (!isEOF() && !Tokenizer.separators.contains(curr)) {\n      token = token + curr\n      advance()\n    }\n    return token\n  }\n\n  curr { _s[_pos] }\n  isEOF() { _pos >= _s.count }\n  advance() { _pos = _pos + 1 }\n}\n\nclass Reader {\n  construct new(tokens) {\n    _tokens = tokens\n    _pos = 0\n  }\n\n  next() {\n    if (_pos >= _tokens.count) return null\n    var token = _tokens[_pos]\n    _pos = _pos + 1\n    return token\n  }\n\n  peek() {\n    if (_pos >= _tokens.count) return null\n    return _tokens[_pos]\n  }\n}\n\nclass MalReader {\n  static parse_str(token) {\n    if (token.count <= 2) return \"\"\n    return token[1..-2].replace(\"\\\\\\\\\", \"\\u029e\").replace(\"\\\\\\\"\", \"\\\"\").replace(\"\\\\n\", \"\\n\").replace(\"\\u029e\", \"\\\\\")\n  }\n\n  static is_all_digits(s) {\n    if (s.count == 0) return false\n    return s.all { |c| c.bytes[0] >= 0x30 && c.bytes[0] <= 0x39 }\n  }\n\n  static is_number(token) {\n    return token.startsWith(\"-\") ? is_all_digits(token[1..-1]) : is_all_digits(token)\n  }\n\n  static read_atom(rdr) {\n    var token = rdr.next()\n    if (is_number(token)) return Num.fromString(token)\n    if (token.startsWith(\"\\\"\")) return parse_str(token)\n    if (token.startsWith(\":\")) return MalVal.newKeyword(token[1..-1])\n    if (token == \"nil\") return null\n    if (token == \"true\") return true\n    if (token == \"false\") return false\n    return MalSymbol.new(token)\n  }\n\n  static read_seq(rdr, start, end) {\n    var token = rdr.next()\n    if (token != start) Fiber.abort(\"expected '%(start)'\")\n    var elements = []\n    token = rdr.peek()\n    while (token != end) {\n      if (!token) Fiber.abort(\"expected '%(end)', got EOF\")\n      elements.add(read_form(rdr))\n      token = rdr.peek()\n    }\n    rdr.next()\n    return elements\n  }\n\n  static reader_macro(rdr, sym) {\n    rdr.next()\n    return MalList.new([MalSymbol.new(sym), read_form(rdr)])\n  }\n\n  static read_form(rdr) {\n    var token = rdr.peek()\n    if (token == \"'\") return reader_macro(rdr, \"quote\")\n    if (token == \"`\") return reader_macro(rdr, \"quasiquote\")\n    if (token == \"~\") return reader_macro(rdr, \"unquote\")\n    if (token == \"~@\") return reader_macro(rdr, \"splice-unquote\")\n    if (token == \"^\") {\n      rdr.next()\n      var meta = read_form(rdr)\n      return MalList.new([MalSymbol.new(\"with-meta\"), read_form(rdr), meta])\n    }\n    if (token == \"@\") return reader_macro(rdr, \"deref\")\n    if (token == \"(\") return MalList.new(read_seq(rdr, \"(\", \")\"))\n    if (token == \")\") Fiber.abort(\"unexpected ')'\")\n    if (token == \"[\") return MalVector.new(read_seq(rdr, \"[\", \"]\"))\n    if (token == \"]\") Fiber.abort(\"unexpected ']'\")\n    if (token == \"{\") return MalMap.fromList(read_seq(rdr, \"{\", \"}\"))\n    if (token == \"}\") Fiber.abort(\"unexpected '}'\")\n    return read_atom(rdr)\n  }\n\n  static read_str(s) {\n    var tokens = Tokenizer.new(s).tokenize()\n    if (tokens.count == 0) return null\n    return read_form(Reader.new(tokens))\n  }\n}\n"
  },
  {
    "path": "impls/wren/readline.wren",
    "content": "import \"io\" for Stdin, Stdout\n\nclass Readline {\n  static readLine(prompt) {\n    var line = null\n    var fiber = Fiber.new {\n      System.write(prompt)\n      Stdout.flush()\n      line = Stdin.readLine()\n    }\n    var error = fiber.try()\n    return error ? null : line\n  }\n}\n"
  },
  {
    "path": "impls/wren/run",
    "content": "#!/usr/bin/env bash\nexec wren $(dirname $0)/${STEP:-stepA_mal}.wren \"${@}\"\n"
  },
  {
    "path": "impls/wren/step0_repl.wren",
    "content": "import \"./readline\" for Readline\n\nclass Mal {\n  static read(str) {\n    return str\n  }\n\n  static eval(ast, env) {\n    return ast\n  }\n\n  static print(ast) {\n    return ast\n  }\n\n  static rep(str) {\n    return print(eval(read(str), null))\n  }\n\n  static main() {\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") System.print(rep(line))\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step1_read_print.wren",
    "content": "import \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static eval(ast, env) {\n    return ast\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), null))\n  }\n\n  static main() {\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step2_eval.wren",
    "content": "import \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalList, MalVector, MalMap\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static eval(ast, env) {\n    // System.print(\"EVAL: %(print(ast))\")\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      if (!env.containsKey(ast.value)) Fiber.abort(\"'%(ast.value)' not found\")\n      return env[ast.value]\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n    if (ast.isEmpty) return ast\n    var evaled_ast = ast.elements.map { |e| eval(e, env) }.toList\n    var f = evaled_ast[0]\n    return f.call(evaled_ast[1..-1])\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = {\n      \"+\": Fn.new { |a| a[0] + a[1] },\n      \"-\": Fn.new { |a| a[0] - a[1] },\n      \"*\": Fn.new { |a| a[0] * a[1] },\n      \"/\": Fn.new { |a| a[0] / a[1] }\n    }\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step3_env.wren",
    "content": "import \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalList, MalVector, MalMap\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static eval(ast, env) {\n    var dbgenv = env.find(\"DEBUG-EVAL\")\n    if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n      System.print(\"EVAL: %(print(ast))\")\n    }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n    if (ast.isEmpty) return ast\n    if (ast[0] is MalSymbol) {\n      if (ast[0].value == \"def!\") {\n        return env.set(ast[1].value, eval(ast[2], env))\n      } else if (ast[0].value == \"let*\") {\n        var letEnv = Env.new(env)\n        var i = 0\n        while (i < ast[1].count) {\n          letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n          i = i + 2\n        }\n        return eval(ast[2], letEnv)\n      }\n    }\n    var evaled_ast = ast.elements.map { |e| eval(e, env) }.toList\n    var f = evaled_ast[0]\n    return f.call(evaled_ast[1..-1])\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    __repl_env.set(\"+\", Fn.new { |a| a[0] + a[1] })\n    __repl_env.set(\"-\", Fn.new { |a| a[0] - a[1] })\n    __repl_env.set(\"*\", Fn.new { |a| a[0] * a[1] })\n    __repl_env.set(\"/\", Fn.new { |a| a[0] / a[1] })\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step4_if_fn_do.wren",
    "content": "import \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalList, MalVector, MalMap\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static eval(ast, env) {\n    var dbgenv = env.find(\"DEBUG-EVAL\")\n    if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n      System.print(\"EVAL: %(print(ast))\")\n    }\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n    if (ast.isEmpty) return ast\n    if (ast[0] is MalSymbol) {\n      if (ast[0].value == \"def!\") {\n        return env.set(ast[1].value, eval(ast[2], env))\n      } else if (ast[0].value == \"let*\") {\n        var letEnv = Env.new(env)\n        var i = 0\n        while (i < ast[1].count) {\n          letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n          i = i + 2\n        }\n        return eval(ast[2], letEnv)\n      } else if (ast[0].value == \"do\") {\n        for (i in 1...(ast.count - 1)) {\n          eval(ast[i], env)\n        }\n        return eval(ast[-1], env)\n      } else if (ast[0].value == \"if\") {\n        var condval = eval(ast[1], env)\n        if (condval) {\n          return eval(ast[2], env)\n        } else {\n          return ast.count > 3 ? eval(ast[3], env) : null\n        }\n      } else if (ast[0].value == \"fn*\") {\n        return Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) }\n      }\n    }\n    var evaled_ast = ast.elements.map { |e| eval(e, env) }.toList\n    var f = evaled_ast[0]\n    return f.call(evaled_ast[1..-1])\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    // core.mal: defined using the language itself\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step5_tco.wren",
    "content": "import \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalList, MalVector, MalMap, MalNativeFn, MalFn\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static eval(ast, env) {\n\n    while (true) {\n      var tco = false\n\n      var dbgenv = env.find(\"DEBUG-EVAL\")\n      if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n        System.print(\"EVAL: %(print(ast))\")\n      }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n\n      if (ast.isEmpty) return ast\n      if (ast[0] is MalSymbol) {\n        if (ast[0].value == \"def!\") {\n          return env.set(ast[1].value, eval(ast[2], env))\n        } else if (ast[0].value == \"let*\") {\n          var letEnv = Env.new(env)\n          var i = 0\n          while (i < ast[1].count) {\n            letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n            i = i + 2\n          }\n          ast = ast[2]\n          env = letEnv\n          tco = true\n        } else if (ast[0].value == \"do\") {\n          for (i in 1...(ast.count - 1)) {\n            eval(ast[i], env)\n          }\n          ast = ast[-1]\n          tco = true\n        } else if (ast[0].value == \"if\") {\n          var condval = eval(ast[1], env)\n          if (condval) {\n            ast = ast[2]\n          } else {\n            if (ast.count <= 3) return null\n            ast = ast[3]\n          }\n          tco = true\n        } else if (ast[0].value == \"fn*\") {\n          return MalFn.new(ast[2], ast[1].elements, env,\n                           Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })\n        }\n      }\n      if (!tco) {\n        var evaled_ast = ast.elements.map { |e| eval(e, env) }.toList\n        var f = evaled_ast[0]\n        if (f is MalNativeFn) {\n          return f.call(evaled_ast[1..-1])\n        } else if (f is MalFn) {\n          ast = f.ast\n          env = Env.new(f.env, f.params, evaled_ast[1..-1])\n          tco = true\n        } else {\n          Fiber.abort(\"unknown function type\")\n        }\n      }\n    }\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    // core.mal: defined using the language itself\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step6_file.wren",
    "content": "import \"os\" for Process\nimport \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalList, MalVector, MalMap, MalNativeFn, MalFn\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static eval(ast, env) {\n\n    while (true) {\n      var tco = false\n\n      var dbgenv = env.find(\"DEBUG-EVAL\")\n      if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n        System.print(\"EVAL: %(print(ast))\")\n      }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n\n      if (ast.isEmpty) return ast\n      if (ast[0] is MalSymbol) {\n        if (ast[0].value == \"def!\") {\n          return env.set(ast[1].value, eval(ast[2], env))\n        } else if (ast[0].value == \"let*\") {\n          var letEnv = Env.new(env)\n          var i = 0\n          while (i < ast[1].count) {\n            letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n            i = i + 2\n          }\n          ast = ast[2]\n          env = letEnv\n          tco = true\n        } else if (ast[0].value == \"do\") {\n          for (i in 1...(ast.count - 1)) {\n            eval(ast[i], env)\n          }\n          ast = ast[-1]\n          tco = true\n        } else if (ast[0].value == \"if\") {\n          var condval = eval(ast[1], env)\n          if (condval) {\n            ast = ast[2]\n          } else {\n            if (ast.count <= 3) return null\n            ast = ast[3]\n          }\n          tco = true\n        } else if (ast[0].value == \"fn*\") {\n          return MalFn.new(ast[2], ast[1].elements, env,\n                           Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })\n        }\n      }\n      if (!tco) {\n        var evaled_ast = ast.elements.map { |e| eval(e, env) }.toList\n        var f = evaled_ast[0]\n        if (f is MalNativeFn) {\n          return f.call(evaled_ast[1..-1])\n        } else if (f is MalFn) {\n          ast = f.ast\n          env = Env.new(f.env, f.params, evaled_ast[1..-1])\n          tco = true\n        } else {\n          Fiber.abort(\"unknown function type\")\n        }\n      }\n    }\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    __repl_env.set(\"eval\", MalNativeFn.new { |a| eval(a[0], __repl_env) })\n    __repl_env.set(\"*ARGV*\", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))\n    // core.mal: defined using the language itself\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n    if (Process.arguments.count > 0) {\n      rep(\"(load-file \\\"%(Process.arguments[0])\\\")\")\n      return\n    }\n\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step7_quote.wren",
    "content": "import \"os\" for Process\nimport \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static qq_loop(elt, acc) {\n    if (elt is MalList && elt.count == 2 && elt[0] is MalSymbol && elt[0].value == \"splice-unquote\") {\n      return MalList.new([MalSymbol.new(\"concat\"), elt[1], acc])\n    } else {\n      return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n    }\n  }\n\n  static qq_foldr(ast) {\n    var acc = MalList.new([])\n    var i = ast.count - 1\n    while (0 <= i) {\n      acc = qq_loop(ast[i], acc)\n      i = i - 1\n    }\n    return acc\n  }\n\n  static quasiquote(ast) {\n    if (ast is MalList) {\n      if (ast.count == 2 && ast[0] is MalSymbol && ast[0].value == \"unquote\") {\n        return ast[1]\n      } else {\n        return qq_foldr(ast)\n      }\n    } else if (ast is MalVector) {\n      return MalList.new([MalSymbol.new(\"vec\"), qq_foldr(ast)])\n    } else if (ast is MalSymbol || ast is MalMap) {\n      return MalList.new([MalSymbol.new(\"quote\"), ast])\n    } else {\n      return ast\n    }\n  }\n\n  static eval(ast, env) {\n\n    while (true) {\n      var tco = false\n\n      var dbgenv = env.find(\"DEBUG-EVAL\")\n      if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n        System.print(\"EVAL: %(print(ast))\")\n      }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n\n      if (ast.isEmpty) return ast\n      if (ast[0] is MalSymbol) {\n        if (ast[0].value == \"def!\") {\n          return env.set(ast[1].value, eval(ast[2], env))\n        } else if (ast[0].value == \"let*\") {\n          var letEnv = Env.new(env)\n          var i = 0\n          while (i < ast[1].count) {\n            letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n            i = i + 2\n          }\n          ast = ast[2]\n          env = letEnv\n          tco = true\n        } else if (ast[0].value == \"quote\") {\n          return ast[1]\n        } else if (ast[0].value == \"quasiquote\") {\n          ast = quasiquote(ast[1])\n          tco = true\n        } else if (ast[0].value == \"do\") {\n          for (i in 1...(ast.count - 1)) {\n            eval(ast[i], env)\n          }\n          ast = ast[-1]\n          tco = true\n        } else if (ast[0].value == \"if\") {\n          var condval = eval(ast[1], env)\n          if (condval) {\n            ast = ast[2]\n          } else {\n            if (ast.count <= 3) return null\n            ast = ast[3]\n          }\n          tco = true\n        } else if (ast[0].value == \"fn*\") {\n          return MalFn.new(ast[2], ast[1].elements, env,\n                           Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })\n        }\n      }\n      if (!tco) {\n        var evaled_ast = ast.elements.map { |e| eval(e, env) }.toList\n        var f = evaled_ast[0]\n        if (f is MalNativeFn) {\n          return f.call(evaled_ast[1..-1])\n        } else if (f is MalFn) {\n          ast = f.ast\n          env = Env.new(f.env, f.params, evaled_ast[1..-1])\n          tco = true\n        } else {\n          Fiber.abort(\"unknown function type\")\n        }\n      }\n    }\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    __repl_env.set(\"eval\", MalNativeFn.new { |a| eval(a[0], __repl_env) })\n    __repl_env.set(\"*ARGV*\", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))\n    // core.mal: defined using the language itself\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\n    if (Process.arguments.count > 0) {\n      rep(\"(load-file \\\"%(Process.arguments[0])\\\")\")\n      return\n    }\n\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step8_macros.wren",
    "content": "import \"os\" for Process\nimport \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static qq_loop(elt, acc) {\n    if (elt is MalList && elt.count == 2 && elt[0] is MalSymbol && elt[0].value == \"splice-unquote\") {\n      return MalList.new([MalSymbol.new(\"concat\"), elt[1], acc])\n    } else {\n      return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n    }\n  }\n\n  static qq_foldr(ast) {\n    var acc = MalList.new([])\n    var i = ast.count - 1\n    while (0 <= i) {\n      acc = qq_loop(ast[i], acc)\n      i = i - 1\n    }\n    return acc\n  }\n\n  static quasiquote(ast) {\n    if (ast is MalList) {\n      if (ast.count == 2 && ast[0] is MalSymbol && ast[0].value == \"unquote\") {\n        return ast[1]\n      } else {\n        return qq_foldr(ast)\n      }\n    } else if (ast is MalVector) {\n      return MalList.new([MalSymbol.new(\"vec\"), qq_foldr(ast)])\n    } else if (ast is MalSymbol || ast is MalMap) {\n      return MalList.new([MalSymbol.new(\"quote\"), ast])\n    } else {\n      return ast\n    }\n  }\n\n  static eval(ast, env) {\n\n    while (true) {\n      var tco = false\n\n      var dbgenv = env.find(\"DEBUG-EVAL\")\n      if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n        System.print(\"EVAL: %(print(ast))\")\n      }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n\n      if (ast.isEmpty) return ast\n      if (ast[0] is MalSymbol) {\n        if (ast[0].value == \"def!\") {\n          return env.set(ast[1].value, eval(ast[2], env))\n        } else if (ast[0].value == \"let*\") {\n          var letEnv = Env.new(env)\n          var i = 0\n          while (i < ast[1].count) {\n            letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n            i = i + 2\n          }\n          ast = ast[2]\n          env = letEnv\n          tco = true\n        } else if (ast[0].value == \"quote\") {\n          return ast[1]\n        } else if (ast[0].value == \"quasiquote\") {\n          ast = quasiquote(ast[1])\n          tco = true\n        } else if (ast[0].value == \"defmacro!\") {\n          return env.set(ast[1].value, eval(ast[2], env).makeMacro())\n        } else if (ast[0].value == \"do\") {\n          for (i in 1...(ast.count - 1)) {\n            eval(ast[i], env)\n          }\n          ast = ast[-1]\n          tco = true\n        } else if (ast[0].value == \"if\") {\n          var condval = eval(ast[1], env)\n          if (condval) {\n            ast = ast[2]\n          } else {\n            if (ast.count <= 3) return null\n            ast = ast[3]\n          }\n          tco = true\n        } else if (ast[0].value == \"fn*\") {\n          return MalFn.new(ast[2], ast[1].elements, env,\n                           Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })\n        }\n      }\n      if (!tco) {\n        var f = eval(ast[0], env)\n        if (f is MalNativeFn) {\n          var args = ast.elements[1..-1].map { |e| eval(e, env) }.toList\n          return f.call(args)\n        } else if (f is MalFn) {\n         if (f.isMacro) {\n          ast = f.call(ast.elements[1..-1])\n         } else {\n          var args = ast.elements[1..-1].map { |e| eval(e, env) }.toList\n          ast = f.ast\n          env = Env.new(f.env, f.params, args)\n         }\n        } else {\n          Fiber.abort(\"unknown function type\")\n        }\n      }\n    }\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    __repl_env.set(\"eval\", MalNativeFn.new { |a| eval(a[0], __repl_env) })\n    __repl_env.set(\"*ARGV*\", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))\n    // core.mal: defined using the language itself\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n    if (Process.arguments.count > 0) {\n      rep(\"(load-file \\\"%(Process.arguments[0])\\\")\")\n      return\n    }\n\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        if (fiber.error) System.print(\"Error: %(fiber.error)\")\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/step9_try.wren",
    "content": "import \"os\" for Process\nimport \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalException\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static qq_loop(elt, acc) {\n    if (elt is MalList && elt.count == 2 && elt[0] is MalSymbol && elt[0].value == \"splice-unquote\") {\n      return MalList.new([MalSymbol.new(\"concat\"), elt[1], acc])\n    } else {\n      return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n    }\n  }\n\n  static qq_foldr(ast) {\n    var acc = MalList.new([])\n    var i = ast.count - 1\n    while (0 <= i) {\n      acc = qq_loop(ast[i], acc)\n      i = i - 1\n    }\n    return acc\n  }\n\n  static quasiquote(ast) {\n    if (ast is MalList) {\n      if (ast.count == 2 && ast[0] is MalSymbol && ast[0].value == \"unquote\") {\n        return ast[1]\n      } else {\n        return qq_foldr(ast)\n      }\n    } else if (ast is MalVector) {\n      return MalList.new([MalSymbol.new(\"vec\"), qq_foldr(ast)])\n    } else if (ast is MalSymbol || ast is MalMap) {\n      return MalList.new([MalSymbol.new(\"quote\"), ast])\n    } else {\n      return ast\n    }\n  }\n\n  static eval(ast, env) {\n\n    while (true) {\n      var tco = false\n\n      var dbgenv = env.find(\"DEBUG-EVAL\")\n      if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n        System.print(\"EVAL: %(print(ast))\")\n      }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n\n      if (ast.isEmpty) return ast\n      if (ast[0] is MalSymbol) {\n        if (ast[0].value == \"def!\") {\n          return env.set(ast[1].value, eval(ast[2], env))\n        } else if (ast[0].value == \"let*\") {\n          var letEnv = Env.new(env)\n          var i = 0\n          while (i < ast[1].count) {\n            letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n            i = i + 2\n          }\n          ast = ast[2]\n          env = letEnv\n          tco = true\n        } else if (ast[0].value == \"quote\") {\n          return ast[1]\n        } else if (ast[0].value == \"quasiquote\") {\n          ast = quasiquote(ast[1])\n          tco = true\n        } else if (ast[0].value == \"defmacro!\") {\n          return env.set(ast[1].value, eval(ast[2], env).makeMacro())\n        } else if (ast[0].value == \"try*\") {\n          if (ast.count > 2 && ast[2][0] is MalSymbol && ast[2][0].value == \"catch*\") {\n            var fiber = Fiber.new { eval(ast[1], env) }\n            var result = fiber.try()\n            var error = fiber.error\n            if (!error) return result\n            if (error == \"___MalException___\") {\n              error = MalException.value\n              MalException.set(null)\n            }\n            return eval(ast[2][2], Env.new(env, [ast[2][1]], [error]))\n          } else {\n            return eval(ast[1], env)\n          }\n        } else if (ast[0].value == \"do\") {\n          for (i in 1...(ast.count - 1)) {\n            eval(ast[i], env)\n          }\n          ast = ast[-1]\n          tco = true\n        } else if (ast[0].value == \"if\") {\n          var condval = eval(ast[1], env)\n          if (condval) {\n            ast = ast[2]\n          } else {\n            if (ast.count <= 3) return null\n            ast = ast[3]\n          }\n          tco = true\n        } else if (ast[0].value == \"fn*\") {\n          return MalFn.new(ast[2], ast[1].elements, env,\n                           Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })\n        }\n      }\n      if (!tco) {\n        var f = eval(ast[0], env)\n        if (f is MalNativeFn) {\n          var args = ast.elements[1..-1].map { |e| eval(e, env) }.toList\n          return f.call(args)\n        } else if (f is MalFn) {\n         if (f.isMacro) {\n          ast = f.call(ast.elements[1..-1])\n         } else {\n          var args = ast.elements[1..-1].map { |e| eval(e, env) }.toList\n          ast = f.ast\n          env = Env.new(f.env, f.params, args)\n         }\n        } else {\n          Fiber.abort(\"unknown function type\")\n        }\n      }\n    }\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    __repl_env.set(\"eval\", MalNativeFn.new { |a| eval(a[0], __repl_env) })\n    __repl_env.set(\"*ARGV*\", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))\n    // core.mal: defined using the language itself\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n    if (Process.arguments.count > 0) {\n      rep(\"(load-file \\\"%(Process.arguments[0])\\\")\")\n      return\n    }\n\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        var error = fiber.error\n        if (error) {\n          if (error == \"___MalException___\") {\n            error = Printer.pr_str(MalException.value, false)\n            MalException.set(null)\n          }\n          System.print(\"Error: %(error)\")\n        }\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/stepA_mal.wren",
    "content": "import \"os\" for Process\nimport \"./env\" for Env\nimport \"./readline\" for Readline\nimport \"./reader\" for MalReader\nimport \"./printer\" for Printer\nimport \"./types\" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalException\nimport \"./core\" for Core\n\nclass Mal {\n  static read(str) {\n    return MalReader.read_str(str)\n  }\n\n  static qq_loop(elt, acc) {\n    if (elt is MalList && elt.count == 2 && elt[0] is MalSymbol && elt[0].value == \"splice-unquote\") {\n      return MalList.new([MalSymbol.new(\"concat\"), elt[1], acc])\n    } else {\n      return MalList.new([MalSymbol.new(\"cons\"), quasiquote(elt), acc])\n    }\n  }\n\n  static qq_foldr(ast) {\n    var acc = MalList.new([])\n    var i = ast.count - 1\n    while (0 <= i) {\n      acc = qq_loop(ast[i], acc)\n      i = i - 1\n    }\n    return acc\n  }\n\n  static quasiquote(ast) {\n    if (ast is MalList) {\n      if (ast.count == 2 && ast[0] is MalSymbol && ast[0].value == \"unquote\") {\n        return ast[1]\n      } else {\n        return qq_foldr(ast)\n      }\n    } else if (ast is MalVector) {\n      return MalList.new([MalSymbol.new(\"vec\"), qq_foldr(ast)])\n    } else if (ast is MalSymbol || ast is MalMap) {\n      return MalList.new([MalSymbol.new(\"quote\"), ast])\n    } else {\n      return ast\n    }\n  }\n\n  static eval(ast, env) {\n\n    while (true) {\n      var tco = false\n\n      var dbgenv = env.find(\"DEBUG-EVAL\")\n      if (dbgenv && env.get(\"DEBUG-EVAL\")) {\n        System.print(\"EVAL: %(print(ast))\")\n      }\n\n    // Process non-list types.\n    if (ast is MalSymbol) {\n      return env.get(ast.value)\n    } else if (ast is MalList) {\n      // The only case leading after this switch.\n    } else if (ast is MalVector) {\n      return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)\n    } else if (ast is MalMap) {\n      var m = {}\n      for (e in ast.data) {\n        m[e.key] = eval(e.value, env)\n      }\n      return MalMap.new(m)\n    } else {\n      return ast\n    }\n    // ast is a list, search for special forms\n\n      if (ast.isEmpty) return ast\n      if (ast[0] is MalSymbol) {\n        if (ast[0].value == \"def!\") {\n          return env.set(ast[1].value, eval(ast[2], env))\n        } else if (ast[0].value == \"let*\") {\n          var letEnv = Env.new(env)\n          var i = 0\n          while (i < ast[1].count) {\n            letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))\n            i = i + 2\n          }\n          ast = ast[2]\n          env = letEnv\n          tco = true\n        } else if (ast[0].value == \"quote\") {\n          return ast[1]\n        } else if (ast[0].value == \"quasiquote\") {\n          ast = quasiquote(ast[1])\n          tco = true\n        } else if (ast[0].value == \"defmacro!\") {\n          return env.set(ast[1].value, eval(ast[2], env).makeMacro())\n        } else if (ast[0].value == \"try*\") {\n          if (ast.count > 2 && ast[2][0] is MalSymbol && ast[2][0].value == \"catch*\") {\n            var fiber = Fiber.new { eval(ast[1], env) }\n            var result = fiber.try()\n            var error = fiber.error\n            if (!error) return result\n            if (error == \"___MalException___\") {\n              error = MalException.value\n              MalException.set(null)\n            }\n            return eval(ast[2][2], Env.new(env, [ast[2][1]], [error]))\n          } else {\n            return eval(ast[1], env)\n          }\n        } else if (ast[0].value == \"do\") {\n          for (i in 1...(ast.count - 1)) {\n            eval(ast[i], env)\n          }\n          ast = ast[-1]\n          tco = true\n        } else if (ast[0].value == \"if\") {\n          var condval = eval(ast[1], env)\n          if (condval) {\n            ast = ast[2]\n          } else {\n            if (ast.count <= 3) return null\n            ast = ast[3]\n          }\n          tco = true\n        } else if (ast[0].value == \"fn*\") {\n          return MalFn.new(ast[2], ast[1].elements, env,\n                           Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })\n        }\n      }\n      if (!tco) {\n        var f = eval(ast[0], env)\n        if (f is MalNativeFn) {\n          var args = ast.elements[1..-1].map { |e| eval(e, env) }.toList\n          return f.call(args)\n        } else if (f is MalFn) {\n         if (f.isMacro) {\n          ast = f.call(ast.elements[1..-1])\n         } else {\n          var args = ast.elements[1..-1].map { |e| eval(e, env) }.toList\n          ast = f.ast\n          env = Env.new(f.env, f.params, args)\n         }\n        } else {\n          Fiber.abort(\"unknown function type\")\n        }\n      }\n    }\n  }\n\n  static print(ast) {\n    return Printer.pr_str(ast)\n  }\n\n  static rep(str) {\n    return print(eval(read(str), __repl_env))\n  }\n\n  static main() {\n    __repl_env = Env.new()\n    // core.wren: defined in wren\n    for (e in Core.ns) { __repl_env.set(e.key, e.value) }\n    __repl_env.set(\"eval\", MalNativeFn.new { |a| eval(a[0], __repl_env) })\n    __repl_env.set(\"*ARGV*\", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))\n    // core.mal: defined using the language itself\n    rep(\"(def! *host-language* \\\"wren\\\")\")\n    rep(\"(def! not (fn* (a) (if a false true)))\")\n    rep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n    rep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\")\n\n    if (Process.arguments.count > 0) {\n      rep(\"(load-file \\\"%(Process.arguments[0])\\\")\")\n      return\n    }\n\n    rep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\n    while (true) {\n      var line = Readline.readLine(\"user> \")\n      if (line == null) break\n      if (line != \"\") {\n        var fiber = Fiber.new { System.print(rep(line)) }\n        fiber.try()\n        var error = fiber.error\n        if (error) {\n          if (error == \"___MalException___\") {\n            error = Printer.pr_str(MalException.value, false)\n            MalException.set(null)\n          }\n          System.print(\"Error: %(error)\")\n        }\n      }\n    }\n    System.print()\n  }\n}\n\nMal.main()\n"
  },
  {
    "path": "impls/wren/tests/step5_tco.mal",
    "content": ";; Wren: skipping non-TCO recursion\n;; Reason: completes up to 1,000,000 (with extended timeout)\n"
  },
  {
    "path": "impls/wren/tests/stepA_mal.mal",
    "content": ";; Testing basic Wren interop\n\n;;; wren-eval evaluates the given string as an expression.\n\n(wren-eval \"7\")\n;=>7\n\n(wren-eval \"0x41\")\n;=>65\n\n(wren-eval \"\\\"7\\\"\")\n;=>\"7\"\n\n(wren-eval \"[ 7,8,9 ]\")\n;=>(7 8 9)\n\n(wren-eval \"{ \\\"abc\\\": 789 }\")\n;=>{\"abc\" 789}\n\n(wren-eval \"System.print(\\\"hello\\\")\")\n;/hello\n;=>\"hello\"\n\n(wren-eval \"[\\\"a\\\", \\\"b\\\", \\\"c\\\"].map { |x| \\\"X%(x)Y\\\" }.join(\\\" \\\")\")\n;=>\"XaY XbY XcY\"\n\n(wren-eval \"[1,2,3].map { |x| 1 + x }\")\n;=>(2 3 4)\n\n(wren-eval \"[null, (1 == 1), (1 == 2)]\")\n;=>(nil true false)\n\n(wren-eval \"Fiber.abort(\\\"AAA\\\" + \\\"BBB\\\")\")\n;/Error: AAABBB\n"
  },
  {
    "path": "impls/wren/types.wren",
    "content": "class MalVal {\n  static newKeyword(value) { \"\\u029e%(value)\" }\n  static isKeyword(obj) { obj is String && obj.count > 0 && obj[0] == \"\\u029e\" }\n  meta { _meta }\n  meta=(value) { _meta = value }\n}\n\nclass MalSymbol is MalVal {\n  construct new(value) { _value = value }\n  value { _value }\n  toString { _value }\n  ==(other) { other is MalSymbol && other.value == _value }\n  !=(other) { !(this == other) }\n}\n\nclass MalSequential is MalVal {\n  construct new(elements) { _elements = elements }\n  elements { _elements }\n  [index] { _elements[index] }\n  isEmpty { _elements.count == 0 }\n  count { _elements.count }\n  first { isEmpty ? null : _elements[0] }\n  rest { MalList.new(isEmpty ? [] : elements[1..-1]) }\n  ==(other) {\n    if (!(other is MalSequential)) return false\n    if (other.count != count) return false\n    for (i in 0...count) {\n      if (other[i] != this[i]) return false\n    }\n    return true\n  }\n  !=(other) { !(this == other) }\n}\n\nclass MalList is MalSequential {\n  construct new(elements) { super(elements) }\n  clone() { MalList.new(elements) }\n}\n\nclass MalVector is MalSequential {\n  construct new(elements) { super(elements) }\n  clone() { MalVector.new(elements) }\n}\n\nclass MalMap is MalVal {\n  construct new(data) { _data = data }\n  construct fromList(elements) {\n    _data = {}\n    var i = 0\n    while (i < elements.count) {\n      _data[elements[i]] = elements[i + 1]\n      i = i + 2\n    }\n  }\n  clone() { MalMap.new(_data) }\n  data { _data }\n  assoc(pairsList) {\n    var newData = {}\n    for (e in _data) {\n      newData[e.key] = e.value\n    }\n    var i = 0\n    while (i < pairsList.count) {\n      newData[pairsList[i]] = pairsList[i + 1]\n      i = i + 2\n    }\n    return MalMap.new(newData)\n  }\n  dissoc(keysList) {\n    var newData = {}\n    for (e in _data) {\n      newData[e.key] = e.value\n    }\n    for (k in keysList) {\n      newData.remove(k)\n    }\n    return MalMap.new(newData)\n  }\n  ==(other) {\n    if (!(other is MalMap)) return false\n    if (other.data.count != data.count) return false\n    for (e in _data) {\n      if (other.data[e.key] != e.value) return false\n    }\n    return true\n  }\n  !=(other) { !(this == other) }\n}\n\nclass MalNativeFn is MalVal {\n  construct new(fn) { _fn = fn }\n  call(args) { _fn.call(args) }\n  clone() { MalNativeFn.new(_fn) }\n}\n\nclass MalFn is MalVal {\n  construct new(ast, params, env, fn) {\n    _ast = ast\n    _params = params\n    _env = env\n    _fn = fn\n    _isMacro = false\n  }\n  construct new(ast, params, env, fn, isMacro) {\n    _ast = ast\n    _params = params\n    _env = env\n    _fn = fn\n    _isMacro = isMacro\n  }\n  ast { _ast }\n  params { _params }\n  env { _env }\n  isMacro { _isMacro }\n  clone() { MalFn.new(_ast, _params, _env, _fn, _isMacro) }\n  makeMacro() { MalFn.new(_ast, _params, _env, _fn, true) }\n  call(args) { _fn.call(args) }\n}\n\nclass MalAtom is MalVal {\n  construct new(value) { _value = value }\n  value { _value }\n  value=(other) { _value = other }\n  clone() { MalAtom.new(value) }\n}\n\nclass MalException {\n  static value { __exception }\n  static set(exception) { __exception = exception }\n}\n"
  },
  {
    "path": "impls/wren/wren-add-gettimeofday.patch",
    "content": "diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c\nindex 34a13c8b..3c4e6ab8 100644\n--- a/src/vm/wren_core.c\n+++ b/src/vm/wren_core.c\n@@ -4,6 +4,7 @@\n #include <math.h>\n #include <string.h>\n #include <time.h>\n+#include <sys/time.h>\n \n #include \"wren_common.h\"\n #include \"wren_core.h\"\n@@ -1121,6 +1122,13 @@ DEF_PRIMITIVE(string_toString)\n   RETURN_VAL(args[0]);\n }\n \n+DEF_PRIMITIVE(system_gettimeofday)\n+{\n+  struct timeval tv;\n+  gettimeofday(&tv, NULL);\n+  RETURN_NUM((double)tv.tv_sec + (double)tv.tv_usec/1000000.0);\n+}\n+\n DEF_PRIMITIVE(system_clock)\n {\n   RETURN_NUM((double)clock() / CLOCKS_PER_SEC);\n@@ -1374,6 +1382,7 @@ void wrenInitializeCore(WrenVM* vm)\n   PRIMITIVE(vm->rangeClass, \"toString\", range_toString);\n \n   ObjClass* systemClass = AS_CLASS(wrenFindVariable(vm, coreModule, \"System\"));\n+  PRIMITIVE(systemClass->obj.classObj, \"gettimeofday\", system_gettimeofday);\n   PRIMITIVE(systemClass->obj.classObj, \"clock\", system_clock);\n   PRIMITIVE(systemClass->obj.classObj, \"gc()\", system_gc);\n   PRIMITIVE(systemClass->obj.classObj, \"writeString_(_)\", system_writeString);\n"
  },
  {
    "path": "impls/xslt/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN DEBIAN_FRONTEND=noninteractive apt-get -y install \\\n  default-jre-headless libsaxonhe-java\n"
  },
  {
    "path": "impls/xslt/Makefile",
    "content": ".DEFAULT:\n\techo\n\n.PHONY: clean\n\nall:\n\techo \"hello there general kenobi\"\n"
  },
  {
    "path": "impls/xslt/core.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:core=\"CORE\" version=\"3.0\" exclude-result-prefixes=\"core fn xsl xs\">\n  <xsl:function name=\"core:ns\">\n    <xsl:sequence>\n      <malval kind=\"function\" name=\"+\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"-\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"*\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"/\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"prn\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"pr-str\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"str\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"println\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"list\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"list?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"empty?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"count\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"=\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"&lt;\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"&lt;=\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"&gt;\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"&gt;=\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"read-string\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"slurp\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"eval\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"atom\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"atom?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"deref\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"swap!\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"reset!\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"cons\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"concat\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"vec\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"nth\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"first\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"rest\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"throw\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"apply\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"map\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in the step files -->\n      <malval kind=\"function\" name=\"nil?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"true?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"false?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"symbol?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"symbol\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"keyword\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"keyword?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"vector\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"vector?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"sequential?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"hash-map\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"map?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"assoc\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"dissoc\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"get\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"contains?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"keys\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"vals\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"readline\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- defined in step file -->\n      <malval kind=\"function\" name=\"meta\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"with-meta\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"time-ms\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"conj\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"string?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"number?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"fn?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"macro?\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"seq\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"xpath-eval\">\n        <is_macro>false</is_macro>\n      </malval>\n      <malval kind=\"function\" name=\"xslt-halt\">\n        <is_macro>false</is_macro>\n      </malval>\n      <!-- evaluate xpath, no context node | requires Saxon PE/EE [paywalls piss me off] -->\n    </xsl:sequence>\n  </xsl:function>\n  <xsl:template name=\"core-apply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:variable name=\"atoms\" select=\"atoms\"/>\n    <xsl:choose>\n      <xsl:when test=\"$func/malval/@kind = 'function'\">\n        <xsl:choose>\n          <xsl:when test=\"$func/malval/@name = '+'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) + number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"core:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '-'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) - number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"core:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '*'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) * number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"core:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '/'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) div number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"core:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'prn'\">\n            <xsl:variable name=\"args\" select=\"$args/value/malval/lvalue/malval\"/>\n            <xsl:variable name=\"sargs\">\n              <xsl:for-each select=\"$args\">\n                <xsl:variable name=\"arg\">\n                  <value>\n                    <xsl:sequence select=\".\"/>\n                  </value>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <str>\n                  <xsl:for-each select=\"$arg\">\n                    <xsl:call-template name=\"malprinter-pr_str\">\n                      <xsl:with-param name=\"readably\" select=\"true()\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </str>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:message>\n              <request kind=\"display\" value=\"{string-join($sargs/str, ' ')}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"core:makeMALType((), 'nil')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'pr-str'\">\n            <xsl:variable name=\"args\" select=\"$args/value/malval/lvalue/malval\"/>\n            <xsl:variable name=\"sargs\">\n              <xsl:for-each select=\"$args\">\n                <xsl:variable name=\"arg\">\n                  <value>\n                    <xsl:sequence select=\".\"/>\n                  </value>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <str>\n                  <xsl:for-each select=\"$arg\">\n                    <xsl:call-template name=\"malprinter-pr_str\">\n                      <xsl:with-param name=\"readably\" select=\"true()\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </str>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:sequence select=\"core:makeMALType(string-join($sargs/str, ' '), 'string')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'str'\">\n            <xsl:variable name=\"args\" select=\"$args/value/malval/lvalue/malval\"/>\n            <xsl:variable name=\"sargs\">\n              <xsl:for-each select=\"$args\">\n                <xsl:variable name=\"arg\">\n                  <value>\n                    <xsl:sequence select=\".\"/>\n                  </value>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <str>\n                  <xsl:for-each select=\"$arg\">\n                    <xsl:call-template name=\"malprinter-pr_str\">\n                      <xsl:with-param name=\"readably\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </str>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:sequence select=\"core:makeMALType(string-join($sargs/str, ''), 'string')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'println'\">\n            <xsl:variable name=\"args\" select=\"$args/value/malval/lvalue/malval\"/>\n            <xsl:variable name=\"sargs\">\n              <xsl:for-each select=\"$args\">\n                <xsl:variable name=\"arg\">\n                  <value>\n                    <xsl:sequence select=\".\"/>\n                  </value>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <str>\n                  <xsl:for-each select=\"$arg\">\n                    <xsl:call-template name=\"malprinter-pr_str\">\n                      <xsl:with-param name=\"readably\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </str>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:message>\n              <request kind=\"display\" value=\"{string-join($sargs/str, ' ')}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"core:makeMALType((), 'nil')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'list'\">\n            <xsl:sequence select=\"$args\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'list?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'list') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'empty?'\">\n            <xsl:sequence select=\"core:makeMALType((), if (count($args/value/malval/lvalue/malval[1]/lvalue/malval) = 0) then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'count'\">\n            <xsl:choose>\n              <xsl:when test=\"$args/value/malval/lvalue/malval[1]/@kind = 'hash'\">\n                <xsl:sequence select=\"core:makeMALType(count($args/value/malval/lvalue/malval[1]/lvalue/malval) div 2, 'number')\"/>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"core:makeMALType(count($args/value/malval/lvalue/malval[1]/lvalue/malval), 'number')\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '='\">\n            <xsl:sequence select=\"core:makeMALType((), if (core:equal($args/value/malval/lvalue/malval[1], $args/value/malval/lvalue/malval[2])) then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '&lt;'\">\n            <xsl:sequence select=\"core:makeMALType((), if (number($args/value/malval/lvalue/malval[1]/@value) lt number($args/value/malval/lvalue/malval[2]/@value)) then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '&lt;='\">\n            <xsl:sequence select=\"core:makeMALType((), if (number($args/value/malval/lvalue/malval[1]/@value) le number($args/value/malval/lvalue/malval[2]/@value)) then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '&gt;'\">\n            <xsl:sequence select=\"core:makeMALType((), if (number($args/value/malval/lvalue/malval[1]/@value) gt number($args/value/malval/lvalue/malval[2]/@value)) then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '&gt;='\">\n            <xsl:sequence select=\"core:makeMALType((), if (number($args/value/malval/lvalue/malval[1]/@value) ge number($args/value/malval/lvalue/malval[2]/@value)) then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'read-string'\">\n            <xsl:variable name=\"read-string-context\">\n              <str>\n                <xsl:value-of select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              </str>\n            </xsl:variable>\n            <xsl:variable name=\"form\">\n              <xsl:for-each select=\"$read-string-context\">\n                <xsl:call-template name=\"malreader-read_str\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$form\">\n              <xsl:if test=\"error\">\n                <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error), core:makeMALValue(string(error), 'string'))\"/>\n              </xsl:if>\n              <xsl:copy-of select=\".\"/>\n            </xsl:for-each>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'slurp'\">\n            <xsl:sequence select=\"core:makeMALType(unparsed-text($args/value/malval/lvalue/malval[1]/@value), 'string')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'atom?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'atom') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'cons'\">\n            <xsl:variable name=\"result\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                    <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]/lvalue/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n            </xsl:variable>\n            <xsl:sequence select=\"$result\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'concat'\">\n            <xsl:variable name=\"result\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <xsl:sequence select=\"$args/value/malval/lvalue/malval/lvalue/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n            </xsl:variable>\n            <xsl:sequence select=\"$result\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'vec'\">\n            <value>\n              <malval kind=\"vector\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]/lvalue\"/>\n              </malval>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'nth'\">\n            <value>\n              <xsl:variable name=\"res\" select=\"$args/value/malval/lvalue/malval[1]/lvalue/malval[position() = (number($args/value/malval/lvalue/malval[2]/@value) + 1)]\"/>\n              <xsl:if test=\"empty($res)\">\n                <xsl:value-of select=\"error(QName('MAL', 'Error'), 'Index out of bounds', core:makeMALValue('Index out of bounds', 'string'))\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$res\"/>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'first'\">\n            <value>\n              <xsl:variable name=\"res\" select=\"$args/value/malval/lvalue/malval[1]/lvalue/malval[1]\"/>\n              <xsl:choose>\n                <xsl:when test=\"empty($res)\">\n                  <malval kind=\"nil\"/>\n                </xsl:when>\n                <xsl:otherwise>\n                  <xsl:sequence select=\"$res\"/>\n                </xsl:otherwise>\n              </xsl:choose>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'rest'\">\n            <value>\n              <malval kind=\"list\">\n                <lvalue>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]/lvalue/malval[position() &gt; 1]\"/>\n                </lvalue>\n              </malval>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'throw'\">\n            <xsl:variable name=\"err\" select=\"$args/value/malval/lvalue/malval[1]\"/>\n            <xsl:value-of select=\"error(QName('MAL', 'Error'), core:pr-str($err), $err)\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'nil?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'nil') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'true?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'true') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'false?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'false') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'symbol?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'symbol') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'symbol'\">\n            <xsl:sequence select=\"core:makeMALType($args/value/malval/lvalue/malval[1]/@value, 'symbol')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'keyword'\">\n            <xsl:sequence select=\"core:makeMALType($args/value/malval/lvalue/malval[1]/@value, 'keyword')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'keyword?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'keyword') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'vector'\">\n            <value>\n              <malval kind=\"vector\">\n                <xsl:sequence select=\"$args/value/malval/lvalue\"/>\n              </malval>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'vector?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'vector') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'sequential?'\">\n            <xsl:sequence select=\"core:makeMALType((), if (let $kind := $args/value/malval/lvalue/malval[1]/@kind return $kind = 'vector' or $kind = 'list') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'hash-map'\">\n            <xsl:if test=\"count($args/value/malval/lvalue/malval) mod 2 = 1\">\n              <xsl:value-of select=\"error(QName('MAL', 'Error'), 'Odd number of args to hash-map', core:makeMALValue('Odd number of args to hash-map', 'string'))\"/>\n            </xsl:if>\n            <value>\n              <malval kind=\"hash\">\n                <xsl:sequence select=\"$args/value/malval/lvalue\"/>\n              </malval>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'map?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'hash') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'assoc'\">\n            <xsl:if test=\"count($args/value/malval/lvalue/malval) mod 2 = 0\">\n              <xsl:value-of select=\"error(QName('MAL', 'Error'), 'Odd number of args to assoc', core:makeMALValue('Odd number of args to assoc', 'string'))\"/>\n            </xsl:if>\n            <xsl:variable name=\"names\" select=\"$args/value/malval/lvalue/malval[(position() gt 1) and (position() mod 2 = 0)]\"/>\n            <xsl:variable name=\"values\" select=\"$args/value/malval/lvalue/malval[(position() gt 2) and (position() mod 2 = 1)]\"/>\n            <xsl:sequence select=\"let $hash := $args/value/malval/lvalue/malval[1] return core:map-dissoc($hash/lvalue/malval, $names) =&gt; core:map-assoc($names, $values) =&gt; core:makeMALList('hash')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'dissoc'\">\n            <xsl:variable name=\"names\" select=\"$args/value/malval/lvalue/malval[position() gt 1]\"/>\n            <xsl:sequence select=\"let $hash := $args/value/malval/lvalue/malval[1] return core:map-dissoc($hash/lvalue/malval, $names) =&gt; core:makeMALList('hash')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'get'\">\n            <xsl:variable name=\"name\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n            <value>\n              <xsl:sequence select=\"let $hash := $args/value/malval/lvalue/malval[1] return (core:map-get($hash/lvalue/malval, $name), core:makeMALValue((), 'nil'))[1]\"/>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'contains?'\">\n            <xsl:variable name=\"name\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n            <xsl:sequence select=\"let $hash := $args/value/malval/lvalue/malval[1] return core:makeMALType((), if (empty(core:map-get($hash/lvalue/malval, $name))) then 'false' else 'true')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'keys'\">\n            <xsl:sequence select=\"let $hash := $args/value/malval/lvalue/malval[1] return ($hash/lvalue/malval[position() mod 2 = 1]) =&gt; core:makeMALList('list')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'vals'\">\n            <xsl:sequence select=\"let $hash := $args/value/malval/lvalue/malval[1] return ($hash/lvalue/malval[position() mod 2 = 0]) =&gt; core:makeMALList('list')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'meta'\">\n            <value>\n              <xsl:sequence select=\"($args/value/malval/lvalue/malval[1]/meta/malval, core:makeMALValue((), 'nil'))[1]\"/>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'with-meta'\">\n            <value>\n              <malval>\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]/(*[name() != 'meta']|@*)\"/>\n                <meta>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]\"/>\n                </meta>\n              </malval>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'time-ms'\">\n            <!-- current-dateTime() does not change while transforming :( -->\n            <xsl:sequence select=\"core:makeMALType(core:mstime(), 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'conj'\">\n            <xsl:variable name=\"xargs\" select=\"$args/value/malval/lvalue/malval[position() &gt; 1]\"/>\n            <xsl:variable name=\"coll\">\n              <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n            </xsl:variable>\n            <value>\n              <xsl:choose>\n                <xsl:when test=\"$coll/malval/@kind = 'list'\">\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:sequence select=\"reverse($xargs)\"/>\n                      <xsl:sequence select=\"$coll/malval/lvalue/malval\"/>\n                    </lvalue>\n                  </malval>\n                </xsl:when>\n                <xsl:otherwise>\n                  <malval kind=\"vector\">\n                    <lvalue>\n                      <xsl:sequence select=\"$coll/malval/lvalue/malval\"/>\n                      <xsl:sequence select=\"$xargs\"/>\n                    </lvalue>\n                  </malval>\n                </xsl:otherwise>\n              </xsl:choose>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'string?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'string') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'number?'\">\n            <xsl:sequence select=\"core:makeMALType((), if ($args/value/malval/lvalue/malval[1]/@kind = 'number') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'fn?'\">\n            <xsl:sequence select=\"core:makeMALType((), if (let $f := $args/value/malval/lvalue/malval[1] return ($f/@kind = 'userfunction' or $f/@kind = 'function') and $f/is_macro/text() != 'true') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'macro?'\">\n            <xsl:sequence select=\"core:makeMALType((), if (let $f := $args/value/malval/lvalue/malval[1] return ($f/@kind = 'userfunction' or $f/@kind = 'function') and $f/is_macro/text() = 'true') then 'true' else 'false')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'seq'\">\n            <xsl:variable name=\"arg\" select=\"$args/value/malval/lvalue/malval[1]\"/>\n            <xsl:choose>\n              <xsl:when test=\"$arg/@kind = 'string'\">\n                <xsl:choose>\n                  <xsl:when test=\"string-length($arg/@value) = 0\">\n                    <xsl:sequence select=\"core:makeMALType((), 'nil')\"/>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <value>\n                      <malval kind=\"list\">\n                        <lvalue>\n                          <xsl:for-each select=\"string-to-codepoints($arg/@value)\">\n                            <xsl:sequence select=\"core:makeMALValue(codepoints-to-string(.), 'string')\"/>\n                          </xsl:for-each>\n                        </lvalue>\n                      </malval>\n                    </value>\n                  </xsl:otherwise>\n                </xsl:choose>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:choose>\n                  <xsl:when test=\"count($arg/lvalue/malval) = 0\">\n                    <xsl:sequence select=\"core:makeMALType((), 'nil')\"/>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <value>\n                      <malval kind=\"list\">\n                        <xsl:sequence select=\"$arg/lvalue\"/>\n                      </malval>\n                    </value>\n                  </xsl:otherwise>\n                </xsl:choose>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'xpath-eval'\">\n            <xsl:message>\n              <request kind=\"xpath-eval\" value=\"{$args/value/malval/lvalue/malval[1]/@value}\" context=\"{$args/value/malval/lvalue/malval[2] =&gt; serialize()}\"/>\n            </xsl:message>\n            <value>\n              <xsl:sequence select=\"document('xsl_input-string')\"/>\n            </value>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = 'xslt-halt'\">\n            <xsl:message>\n              <request kind=\"halt\"/>\n            </xsl:message>\n            <value>\n              <malval kind=\"list\" />\n            </value>\n          </xsl:when>\n          <xsl:otherwise>\n            <xsl:value-of select=\"error(QName('MAL', 'Error'), concat('Invalid function ', $func/malval/@name), core:makeMALValue(concat('Invalid function ', $func/malval/@name), 'string'))\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:when>\n      <xsl:otherwise/>\n    </xsl:choose>\n  </xsl:template>\n  <xsl:function name=\"core:makeMALList\">\n    <xsl:param name=\"values\"/>\n    <xsl:param name=\"kind\"/>\n    <value>\n      <malval kind=\"{$kind}\">\n        <lvalue>\n          <xsl:sequence select=\"$values\"/>\n        </lvalue>\n      </malval>\n    </value>\n  </xsl:function>\n  <xsl:function name=\"core:makeMALType\">\n    <xsl:param name=\"value\"/>\n    <xsl:param name=\"kind\"/>\n    <value>\n      <malval kind=\"{$kind}\" value=\"{$value}\"/>\n    </value>\n  </xsl:function>\n  <xsl:function name=\"core:makeMALValue\">\n    <xsl:param name=\"value\"/>\n    <xsl:param name=\"kind\"/>\n    <malval kind=\"{$kind}\" value=\"{$value}\"/>\n  </xsl:function>\n  <xsl:function name=\"core:pr-str\">\n    <xsl:param name=\"value\"/>\n    <xsl:variable name=\"ctx\">\n      <value>\n        <xsl:sequence select=\"$value\"/>\n      </value>\n    </xsl:variable>\n    <xsl:variable name=\"res\">\n      <xsl:for-each select=\"$ctx\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$res\"/>\n  </xsl:function>\n  <xsl:function name=\"core:all-equal\">\n    <xsl:param name=\"seq\"/>\n    <xsl:param name=\"left\"/>\n    <xsl:param name=\"right\"/>\n    <xsl:choose>\n      <xsl:when test=\"empty($seq)\">\n        <xsl:sequence select=\"true()\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:choose>\n          <xsl:when test=\"core:list-equal($left, $right, head($seq))\">\n            <xsl:sequence select=\"core:all-equal(tail($seq), $right, $left)\"/>\n          </xsl:when>\n          <xsl:otherwise>\n            <xsl:sequence select=\"false()\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"core:any-equal\">\n    <xsl:param name=\"comps\"/>\n    <xsl:param name=\"value\"/>\n    <xsl:choose>\n      <xsl:when test=\"empty($comps)\">\n        <xsl:sequence select=\"false()\"/>\n      </xsl:when>\n      <xsl:when test=\"core:equal($value, head($comps))\">\n        <xsl:sequence select=\"true()\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"core:any-equal(tail($comps), $value)\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"core:map-values-equal\">\n    <xsl:param name=\"ma\"/>\n    <xsl:param name=\"mb\"/>\n    <xsl:choose>\n      <xsl:when test=\"empty($ma)\">\n        <xsl:sequence select=\"true()\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"core:equal($ma[2], core:map-get($mb, $ma[1])) and core:map-values-equal($ma[position() gt 2], $mb)\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"core:equal\">\n    <xsl:param name=\"left\"/>\n    <xsl:param name=\"right\"/>\n    <xsl:choose>\n      <!-- equal kinds -->\n      <xsl:when test=\"$left/@kind = $right/@kind\">\n        <xsl:choose>\n          <xsl:when test=\"$left/@kind = 'hash' and $right/@kind = 'hash'\">\n            <!-- counts are equal, check if all keys share the same value -->\n            <xsl:sequence select=\"core:map-values-equal($left/lvalue/malval, $right/lvalue/malval)\"/>\n          </xsl:when>\n          <!-- sequence? -->\n          <xsl:when test=\"$left/@kind = 'list' or $left/@kind = 'vector' or $left/@kind = 'hash'\">\n            <xsl:choose>\n              <!-- same counts -->\n              <xsl:when test=\"count($left/lvalue/malval) = count($right/lvalue/malval)\">\n                <xsl:sequence select=\"core:all-equal(1 to count($left/lvalue/malval), $left, $right)\"/>\n              </xsl:when>\n              <!-- different counts -->\n              <xsl:otherwise>\n                <xsl:sequence select=\"false()\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:when>\n          <!-- simple 'value' type -->\n          <xsl:otherwise>\n            <xsl:sequence select=\"string($left/@value) = string($right/@value)\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:when>\n      <!-- different types -->\n      <xsl:otherwise>\n        <xsl:choose>\n          <xsl:when test=\"($left/@kind = 'list' and $right/@kind = 'vector') or ($left/@kind = 'vector' and $right/@kind = 'list')\">\n            <xsl:choose>\n              <!-- same counts -->\n              <xsl:when test=\"count($left/lvalue/malval) = count($right/lvalue/malval)\">\n                <xsl:sequence select=\"core:all-equal(1 to count($left/lvalue/malval), $left, $right)\"/>\n              </xsl:when>\n              <!-- different counts -->\n              <xsl:otherwise>\n                <xsl:sequence select=\"false()\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:when>\n          <xsl:otherwise>\n            <xsl:sequence select=\"false()\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"core:list-equal\">\n    <xsl:param name=\"l\"/>\n    <xsl:param name=\"r\"/>\n    <xsl:param name=\"v\"/>\n    <xsl:sequence select=\"core:equal($l/lvalue/malval[$v], $r/lvalue/malval[$v])\"/>\n  </xsl:function>\n  <xsl:function name=\"core:map-assoc\">\n    <xsl:param name=\"map\"/>\n    <xsl:param name=\"names\"/>\n    <xsl:param name=\"values\"/>\n    <xsl:sequence select=\"$map\"/>\n    <xsl:for-each select=\"1 to count($names)\">\n      <xsl:variable name=\"idx\" select=\"position()\"/>\n      <xsl:sequence select=\"$names[position() = $idx]\"/>\n      <xsl:sequence select=\"$values[position() = $idx]\"/>\n    </xsl:for-each>\n  </xsl:function>\n  <xsl:function name=\"core:map-dissoc\">\n    <xsl:param name=\"map\"/>\n    <xsl:param name=\"names\"/>\n    <xsl:sequence select=\"$map[let $pos := position() return not(($pos mod 2 = 1 and core:any-equal($names, .)) or ($pos mod 2 = 0 and core:any-equal($names, ../malval[$pos - 1])))]\"/>\n  </xsl:function>\n  <xsl:function name=\"core:map-get\">\n    <xsl:param name=\"map\"/>\n    <xsl:param name=\"name\"/>\n    <xsl:sequence select=\"$map[let $pos := position() return $pos mod 2 = 0 and core:equal($name, ../malval[$pos - 1])]\"/>\n  </xsl:function>\n  <xsl:function name=\"core:mstime\">\n    <xsl:message>\n      <request kind=\"time\" value=\"now\"/>\n    </xsl:message>\n    <xsl:sequence select=\"unparsed-text('xsl_input-string')\"/>\n  </xsl:function>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/env.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\" xmlns:env=\"ENV\" xmlns:core=\"CORE\" version=\"3.0\">\n  <!-- since I can not, for the life of me, figure out how to (de-)serialise maps from/to xml, we're gonna be storing the env as a json string -->\n  <xsl:function name=\"env:noReplEnv\">\n    <xsl:param name=\"env\"/>\n    <xsl:sequence select=\"map {'outer': $env('outer'), 'isReplEnv': false(), 'data': $env('data') }\"/>\n  </xsl:function>\n  <xsl:function name=\"env:set\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"name\"/>\n    <xsl:param name=\"value\"/>\n    <xsl:sequence select=\"if ($env('isReplEnv')) then\n                            map { 'outer': $env('outer'), 'replEnv': env:set($env('replEnv'), $name, $value), 'isReplEnv': true(), 'data': $env('data') }\n                          else\n                            map { 'outer': $env('outer'), 'replEnv': $env('replEnv'), 'isReplEnv': false(), 'data': map:put($env('data'), $name, $value =&gt; serialize(map{})) }\"/>\n  </xsl:function>\n  <xsl:function name=\"env:find\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"name\"/>\n    <xsl:sequence select=\"if (empty($env)) then\n                            ()\n                          else if (map:contains($env('data'), $name)) then\n                            $env\n                          else\n                            (env:find($env('outer'), $name), env:find($env('replEnv'), $name))[1]\"/>\n  </xsl:function>\n  <xsl:function name=\"env:get\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"name\"/>\n    <xsl:variable name=\"value\" select=\"let $venv := env:find($env, $name)\n                                        return if (empty($venv)) then\n                                                ()\n                                               else\n                                                $venv('data')($name)\"/>\n    <xsl:choose>\n      <xsl:when test=\"empty($value)\">\n        <xsl:variable name=\"apos\" select=\"&quot;'&quot;\"/>\n        <xsl:value-of select=\"error(\n                                QName('MAL', 'Error'),\n                                concat($apos, $name, $apos, ' not found'),\n                                fn:makeMALValue(concat($apos, $name, $apos, ' not found'), 'string'))\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:try>\n          <xsl:sequence select=\"parse-xml($value)\"/>\n          <xsl:catch errors=\"*\">\n            <xsl:sequence select=\"$value\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"env:get-noerror\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"name\"/>\n    <xsl:variable name=\"value\" select=\"let $venv := env:find($env, $name)\n                                        return if (empty($venv)) then \n                                                ()\n                                               else\n                                                $venv('data')($name)\"/>\n    <xsl:choose>\n      <xsl:when test=\"empty($value)\"/>\n      <xsl:otherwise>\n        <xsl:sequence select=\"parse-xml($value)\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"env:base\" as=\"xs:string\">\n    <xsl:variable name=\"plus\">\n      <malval kind=\"function\" name=\"+\"/>\n    </xsl:variable>\n    <xsl:variable name=\"minus\">\n      <malval kind=\"function\" name=\"-\"/>\n    </xsl:variable>\n    <xsl:variable name=\"mult\">\n      <malval kind=\"function\" name=\"*\"/>\n    </xsl:variable>\n    <xsl:variable name=\"div\">\n      <malval kind=\"function\" name=\"/\"/>\n    </xsl:variable>\n    <xsl:sequence select=\"env:serialise(env:set(env:set(env:set(env:set(map{'outer':(), 'data':map{}, 'isReplEnv': false()}, '+', $plus), '-', $minus), '*', $mult), '/', $div))\"/>\n  </xsl:function>\n  <xsl:function name=\"env:swap-replEnv\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"toRepl\"/>\n    <xsl:sequence select=\"if (not(empty($toRepl))) then\n                            map:put($env, 'replEnv', $toRepl)\n                          else\n                            $env\"/>\n  </xsl:function>\n  <xsl:function name=\"env:replEnv\">\n    <xsl:param name=\"env\"/>\n    <xsl:sequence select=\"$env('replEnv')\"/>\n  </xsl:function>\n  <xsl:function name=\"env:toReplEnv\">\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"renv\" select=\"map{'outer':(), 'data': map{}, 'replEnv': map{'data':env:dump($env), 'outer':(), 'isReplEnv': false()}, 'isReplEnv': true()}\"/>\n    <xsl:sequence select=\"$renv\"/>\n  </xsl:function>\n  <xsl:function name=\"env:wrapReplEnv\">\n    <xsl:param name=\"replEnv\"/>\n    <xsl:sequence select=\"map{'outer':(), 'data':map{}, 'replEnv':$replEnv, 'isReplEnv':false()}\"/>\n  </xsl:function>\n  <xsl:function name=\"env:collapseReplEnv\">\n    <xsl:param name=\"env\"/>\n    <xsl:iterate select=\"map:keys($env('data'))\">\n      <xsl:param name=\"new-env\" select=\"$env('replEnv')\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$new-env\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"name\" select=\".\"/>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"new-env\" select=\"env:set($new-env, $name, $env =&gt; env:get($name))\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:function>\n  <xsl:function name=\"env:empty\">\n    <xsl:sequence select=\"map{'outer':(), 'data':map{}, 'isReplEnv': false()}\"/>\n  </xsl:function>\n  <xsl:function name=\"env:serialise\">\n    <xsl:param name=\"env\"/>\n    <xsl:sequence select=\"serialize($env, map {'method': 'json'})\"/>\n  </xsl:function>\n  <xsl:function name=\"env:close\">\n    <xsl:param name=\"env\"/>\n    <xsl:sequence select=\"map{'outer': env:noReplEnv($env), 'data': map{}, 'isReplEnv': false(), 'replEnv': $env('replEnv')}\"/>\n  </xsl:function>\n  <xsl:function name=\"env:close-with-binds\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"binds\"/>\n    <xsl:param name=\"exprs\"/>\n    <xsl:variable name=\"new-env\" select=\"map {'outer': env:noReplEnv($env), 'replEnv': $env('replEnv'), 'data': map{}, 'isReplEnv': false()}\"/>\n    <xsl:sequence select=\"$new-env =&gt; env:bind-all($binds, $exprs)\"/>\n  </xsl:function>\n  <xsl:function name=\"env:dump\">\n    <xsl:param name=\"env\"/>\n    <xsl:sequence select=\"if (not(empty($env))) then\n                            map:merge(($env('data'), env:dump($env('outer'))))\n                          else\n                            map{}\"/>\n  </xsl:function>\n  <xsl:function name=\"env:merge\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"second\"/>\n    <xsl:variable name=\"env-items\" select=\"env:dump($env)\"/>\n    <xsl:variable name=\"second-items\" select=\"env:dump($second)\"/>\n    <xsl:variable name=\"new-env\" select=\"if (empty($env)) then\n                                            $second\n                                        else if (empty($second)) then\n                                            $env\n                                        else\n                                            map {'outer': $env('outer'), 'data': map:merge(($env-items, $second-items)), 'isReplEnv': false(), 'replEnv': $env('replEnv')}\"/>\n    <xsl:sequence select=\"$new-env\"/>\n  </xsl:function>\n  <xsl:function name=\"env:hier\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"over\"/>\n    <xsl:variable name=\"newEnv\" select=\"env:merge($over, $env)\"/>\n    <xsl:sequence select=\"map{'outer': env:noReplEnv($newEnv), 'data': map{}, 'isReplEnv': false(), 'replEnv': $env('replEnv')}\"/>\n  </xsl:function>\n  <xsl:function name=\"env:bind-all\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"binds\"/>\n    <xsl:param name=\"exprs\"/>\n    <xsl:choose>\n      <xsl:when test=\"exists($binds) and exists($exprs)\">\n        <xsl:choose>\n          <xsl:when test=\"string(head($binds)) = '&amp;'\">\n            <xsl:variable name=\"listExprs\">\n              <malval kind=\"list\">\n                <lvalue>\n                  <xsl:sequence select=\"$exprs\"/>\n                </lvalue>\n              </malval>\n            </xsl:variable>\n            <xsl:sequence select=\"$env =&gt; env:set(string($binds[2]), $listExprs)\"/>\n          </xsl:when>\n          <xsl:otherwise>\n            <xsl:variable name=\"new-env\" select=\"$env =&gt; env:set(string(head($binds)), head($exprs))\"/>\n            <xsl:sequence select=\"$new-env =&gt; env:bind-all(tail($binds), tail($exprs))\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:when>\n      <xsl:when test=\"exists($binds)\">\n        <xsl:choose>\n          <xsl:when test=\"string(head($binds)) = '&amp;'\">\n            <xsl:variable name=\"listExprs\">\n              <malval kind=\"list\">\n                <lvalue>\n                                </lvalue>\n              </malval>\n            </xsl:variable>\n            <xsl:sequence select=\"$env =&gt; env:set(string($binds[2]), $listExprs)\"/>\n          </xsl:when>\n          <xsl:otherwise>\n            <xsl:variable name=\"Exprs\">\n              <malval kind=\"nil\">\n                            </malval>\n            </xsl:variable>\n            <xsl:variable name=\"new-env\" select=\"$env =&gt; env:set(string(head($binds)), $Exprs)\"/>\n            <xsl:sequence select=\"$new-env =&gt; env:bind-all(tail($binds), $exprs)\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"$env\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"env:deserialise\">\n    <xsl:param name=\"env\"/>\n    <xsl:sequence select=\"parse-json($env)\"/>\n  </xsl:function>\n  <xsl:function name=\"fn:makeMALValue\">\n    <xsl:param name=\"value\"/>\n    <xsl:param name=\"kind\"/>\n    <malval kind=\"{$kind}\" value=\"{$value}\"/>\n  </xsl:function>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/printer.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\" version=\"3.0\" exclude-result-prefixes=\"xsl xs fn\">\n  <!-- expects input as a single <value><malval .../></value> -->\n  <!-- output of form <value>literal string</value> -->\n  <xsl:template name=\"malprinter-pr_str\">\n    <xsl:param name=\"readably\" as=\"xs:boolean\"/>\n    <xsl:variable name=\"value\">\n      <xsl:sequence select=\"value/malval\"/>\n      <xsl:sequence select=\"atoms\"/>\n    </xsl:variable>\n    <xsl:for-each select=\"$value\">\n      <xsl:choose>\n        <xsl:when test=\"malval/@kind = 'true'\">\n          <value>true</value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'false'\">\n          <value>false</value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'nil'\">\n          <value>nil</value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'string'\">\n          <value>\n            <xsl:value-of select=\"concat(\n                                        if ($readably) then\n                                            '&quot;'\n                                        else\n                                            '',\n\n                                        fn:desc_string(malval/@value, $readably),\n\n                                        if ($readably) then\n                                            '&quot;'\n                                        else\n                                            '')\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'keyword'\">\n          <value>\n            <xsl:value-of select=\"concat(':', malval/@value)\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'symbol'\">\n          <value>\n            <xsl:value-of select=\"malval/@value\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'number'\">\n          <value>\n            <xsl:value-of select=\"malval/@value\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'list'\">\n          <xsl:variable name=\"val\">\n            <xsl:for-each select=\"malval/lvalue/malval\">\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:copy-of select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malprinter-pr_str\">\n                  <xsl:with-param name=\"readably\" select=\"$readably\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$val\">\n            <value>\n              <xsl:value-of select=\"concat('(', string-join(/value, ' '), ')')\"/>\n            </value>\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'vector'\">\n          <xsl:variable name=\"val\">\n            <xsl:for-each select=\"malval/lvalue/malval\">\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:copy-of select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malprinter-pr_str\">\n                  <xsl:with-param name=\"readably\" select=\"$readably\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$val\">\n            <value>\n              <xsl:value-of select=\"concat('[', string-join(/value, ' '), ']')\"/>\n            </value>\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'hash'\">\n          <xsl:variable name=\"val\">\n            <xsl:for-each select=\"malval/lvalue/malval\">\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:copy-of select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malprinter-pr_str\">\n                  <xsl:with-param name=\"readably\" select=\"$readably\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$val\">\n            <value>\n              <xsl:value-of select=\"concat('{', string-join(/value, ' '), '}')\"/>\n            </value>\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'function'\">\n          <value>\n            <xsl:variable name=\"gt\">?</xsl:variable>\n            <xsl:variable name=\"lt\">?</xsl:variable>\n            <xsl:value-of select=\"concat('#', $lt, 'fn ', malval/@name, $gt)\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'userfunction'\">\n          <value>\n            <xsl:variable name=\"gt\">?</xsl:variable>\n            <xsl:variable name=\"lt\">?</xsl:variable>\n            <xsl:value-of select=\"concat('#', $lt, 'function', $gt)\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"malval/@kind = 'atom'\">\n          <xsl:variable name=\"val\" select=\"malval\"/>\n          <xsl:variable name=\"inner\">\n            <xsl:variable name=\"ctx\">\n              <value>\n                <xsl:sequence select=\"atoms/atom[@identity = $val/@value]/malval\"/>\n              </value>\n            </xsl:variable>\n            <xsl:for-each select=\"$ctx\">\n              <xsl:call-template name=\"malprinter-pr_str\">\n                <xsl:with-param name=\"readably\" select=\"$readably\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <value>\n            <xsl:value-of select=\"concat(\n                                      '(atom ',\n                                      (let $v := $inner/value\n                                            return if ($v = 'Unknown') then\n                                                     concat('id=', string($val/@value), ' existing=', string-join(atoms/atom/@identity, ','))\n                                                   else\n                                                      $v),\n                                      ')')\"/>\n          </value>\n        </xsl:when>\n        <xsl:otherwise>\n          <value>Unknown</value>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:function name=\"fn:desc_string\" as=\"xs:string\">\n    <xsl:param name=\"str\" as=\"xs:string\"/>\n    <xsl:param name=\"readable\" as=\"xs:boolean\"/>\n    <xsl:choose>\n      <xsl:when test=\"($readable)\">\n        <xsl:variable name=\"sx\">\n          <xsl:analyze-string select=\"$str\" regex=\"(\\\\|&quot;|&#10;)\">\n            <xsl:matching-substring>\n              <x>\n                <xsl:choose>\n                  <xsl:when test=\"regex-group(1) = '\\'\">\n                    <xsl:value-of select=\"'\\\\'\"/>\n                  </xsl:when>\n                  <xsl:when test=\"regex-group(1) = '&quot;'\">\n                    <xsl:value-of select=\"'\\&quot;'\"/>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <xsl:value-of select=\"'\\n'\"/>\n                  </xsl:otherwise>\n                </xsl:choose>\n              </x>\n            </xsl:matching-substring>\n            <xsl:non-matching-substring>\n              <x>\n                <xsl:value-of select=\".\"/>\n              </x>\n            </xsl:non-matching-substring>\n          </xsl:analyze-string>\n        </xsl:variable>\n        <xsl:value-of select=\"string-join($sx/x, '')\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:value-of select=\"$str\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/reader.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\" version=\"3.0\" exclude-result-prefixes=\"xsl xs fn\">\n  <!-- Expects a \"tokens\" in current scope -->\n  <xsl:template name=\"malreader-peek\">\n    <!-- <xsl:message>PEEK <xsl:sequence select=\".\">\n              \n            </xsl:sequence>\n        \n        ;</xsl:message> -->\n    <xsl:variable name=\"context\">\n      <tokens>\n        <xsl:sequence select=\"tokens/*\"/>\n      </tokens>\n      <value>\n        <xsl:for-each select=\"tokens/token[1]\">\n          <xsl:sequence select=\".\"/>\n        </xsl:for-each>\n      </value>\n      <xsl:sequence select=\"lvalue\"/>\n      <xsl:sequence select=\"error\"/>\n    </xsl:variable>\n    <xsl:for-each select=\"$context\">\n      <xsl:sequence select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:template name=\"malreader-next\">\n    <!-- <xsl:message>NEXT <xsl:sequence select=\".\">\n              \n            </xsl:sequence>\n        \n        ;\n        </xsl:message> -->\n    <xsl:variable name=\"context\">\n      <tokens>\n        <xsl:for-each select=\"tokens/token[position() != 1]\">\n          <xsl:sequence select=\".\"/>\n        </xsl:for-each>\n      </tokens>\n      <value>\n        <xsl:for-each select=\"tokens/token[1]\">\n          <xsl:sequence select=\".\"/>\n        </xsl:for-each>\n      </value>\n      <xsl:sequence select=\"lvalue\"/>\n      <xsl:sequence select=\"error\"/>\n    </xsl:variable>\n    <xsl:for-each select=\"$context\">\n      <xsl:sequence select=\"./*\"/>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:template name=\"malreader-read_str\">\n    <xsl:variable name=\"context\">\n      <input value=\"{str}\"/>\n      <tokens>\n        <xsl:call-template name=\"malreader-tokenize\"/>\n      </tokens>\n    </xsl:variable>\n    <xsl:for-each select=\"$context\">\n      <xsl:call-template name=\"malreader-read_form\"/>\n    </xsl:for-each>\n    <!-- <xsl:sequence select=\"$context\" /> -->\n  </xsl:template>\n  <xsl:template name=\"malreader-tokenize\">\n    <xsl:analyze-string select=\"str\" regex=\"[\\s,]*(~@|[\\[\\]{{}}()'`~^@]|&quot;(?:\\\\.|[^\\\\&quot;])*&quot;?|;.*|[^\\s\\[\\]{{}}('&quot;`,;)]+)\" flags=\";j\">\n      <xsl:matching-substring>\n        <xsl:variable name=\"match\">\n          <xsl:sequence select=\"regex-group(1)\"/>\n        </xsl:variable>\n        <xsl:if test=\"string-length($match) &gt; 0\">\n          <xsl:choose>\n            <xsl:when test=\"starts-with($match, '&quot;')\">\n              <xsl:choose>\n                <xsl:when test=\"fn:check_string($match)\">\n                  <token type=\"error\" text=\"EOF while reading string or invalid escape in string\"/>\n                </xsl:when>\n                <xsl:when test=\"ends-with($match, '&quot;')\">\n                  <token type=\"string\" text=\"{fn:process-string(replace($match, '&quot;((.|\\s)*)&quot;', '$1'))}\"> </token>\n                </xsl:when>\n                <xsl:otherwise>\n                  <token type=\"error\" text=\"EOF while reading string\"/>\n                </xsl:otherwise>\n              </xsl:choose>\n            </xsl:when>\n            <xsl:when test=\"starts-with($match, ':')\">\n              <token type=\"keyword\" text=\"{replace($match, ':(.*)', '$1')}\"/>\n            </xsl:when>\n            <xsl:when test=\"starts-with($match, ';')\">\n              <!-- ignore comments -->\n            </xsl:when>\n            <xsl:when test=\"starts-with($match, '~@')\">\n              <token type=\"special\" text=\"~@\"/>\n            </xsl:when>\n            <xsl:when test=\"matches($match, '[\\[\\]\\{\\}()''`~^@]')\">\n              <token type=\"special\" text=\"{$match}\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:choose>\n                <xsl:when test=\"$match = 'false'\">\n                  <token type=\"false\"/>\n                </xsl:when>\n                <xsl:when test=\"$match = 'true'\">\n                  <token type=\"true\"/>\n                </xsl:when>\n                <xsl:when test=\"$match = 'nil'\">\n                  <token type=\"nil\"/>\n                </xsl:when>\n                <xsl:when test=\"matches($match, '^-?\\d+$')\">\n                  <token type=\"number\" text=\"{$match}\"/>\n                </xsl:when>\n                <xsl:otherwise>\n                  <token type=\"symbol\" text=\"{$match}\"/>\n                </xsl:otherwise>\n              </xsl:choose>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:if>\n      </xsl:matching-substring>\n    </xsl:analyze-string>\n  </xsl:template>\n  <xsl:template name=\"malreader-read_form\">\n    <xsl:variable name=\"peek\">\n      <xsl:call-template name=\"malreader-peek\"/>\n    </xsl:variable>\n    <xsl:variable name=\"next\">\n      <xsl:call-template name=\"malreader-next\"/>\n    </xsl:variable>\n    <xsl:for-each select=\"$peek\">\n      <xsl:choose>\n        <xsl:when test=\"value/token/@type = 'error'\">\n          <error>\n            <malval kind=\"error\">\n              <xsl:value-of select=\"value/token/@text\"/>\n            </malval>\n          </error>\n        </xsl:when>\n        <xsl:when test=\"contains('([{', value/token/@text) and value/token/@type = 'special'\">\n          <xsl:variable name=\"next\">\n            <xsl:call-template name=\"malreader-next\"/>\n          </xsl:variable>\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"listkind\">\n              <xsl:value-of select=\"value/token/@text\"/>\n              <!-- listkind [/(/{ -->\n            </xsl:variable>\n            <xsl:call-template name=\"malreader-read_list\">\n              <xsl:with-param name=\"listkind\" select=\"$listkind\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"value/token/@text = &quot;'&quot; and value/token/@type = 'special'\">\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"inner\">\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"tokens\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$inner\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <malval kind=\"symbol\" value=\"quote\"/>\n                    <xsl:sequence select=\"/value/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n              <xsl:sequence select=\"tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:for-each>\n            <xsl:sequence select=\"lvalue\"/>\n            <!-- preserve previous list (if any) -->\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"value/token/@text = '`' and value/token/@type = 'special'\">\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"inner\">\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"tokens\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$inner\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <malval kind=\"symbol\" value=\"quasiquote\"/>\n                    <xsl:sequence select=\"/value/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n              <xsl:sequence select=\"tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:for-each>\n            <xsl:sequence select=\"lvalue\"/>\n            <!-- preserve previous list (if any) -->\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"value/token/@text = '~' and value/token/@type = 'special'\">\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"inner\">\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"tokens\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$inner\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <malval kind=\"symbol\" value=\"unquote\"/>\n                    <xsl:sequence select=\"/value/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n              <xsl:sequence select=\"tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:for-each>\n            <xsl:sequence select=\"lvalue\"/>\n            <!-- preserve previous list (if any) -->\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"value/token/@text = '~@' and value/token/@type = 'special'\">\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"inner\">\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"tokens\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$inner\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <malval kind=\"symbol\" value=\"splice-unquote\"/>\n                    <xsl:sequence select=\"/value/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n              <xsl:sequence select=\"tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:for-each>\n            <xsl:sequence select=\"lvalue\"/>\n            <!-- preserve previous list (if any) -->\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"value/token/@text = '@' and value/token/@type = 'special'\">\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"inner\">\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"tokens\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$inner\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <malval kind=\"symbol\" value=\"deref\"/>\n                    <xsl:sequence select=\"/value/malval\"/>\n                  </lvalue>\n                </malval>\n              </value>\n              <xsl:sequence select=\"tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:for-each>\n            <xsl:sequence select=\"lvalue\"/>\n            <!-- preserve previous list (if any) -->\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:when test=\"value/token/@text = '^' and value/token/@type = 'special'\">\n          <xsl:for-each select=\"$next\">\n            <xsl:variable name=\"meta\">\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"tokens\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:variable name=\"form\">\n              <xsl:for-each select=\"$meta\">\n                <xsl:variable name=\"ctx\">\n                  <xsl:sequence select=\"tokens\"/>\n                </xsl:variable>\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"malreader-read_form\"/>\n                </xsl:for-each>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:for-each select=\"$form\">\n              <value>\n                <malval kind=\"list\">\n                  <lvalue>\n                    <malval kind=\"symbol\" value=\"with-meta\"/>\n                    <xsl:sequence select=\"/value/malval\"/>\n                    <xsl:for-each select=\"$meta\">\n                      <xsl:sequence select=\"/value/malval\"/>\n                    </xsl:for-each>\n                  </lvalue>\n                </malval>\n              </value>\n              <xsl:sequence select=\"tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:for-each>\n            <xsl:sequence select=\"lvalue\"/>\n            <!-- preserve previous list (if any) -->\n          </xsl:for-each>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:call-template name=\"malreader-read_atom\"/>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:template name=\"malreader-read_list\">\n    <xsl:param name=\"listkind\" as=\"xs:string\"/>\n    <xsl:variable name=\"prev_lvalue\">\n      <xsl:copy-of select=\"lvalue\"/>\n    </xsl:variable>\n    <xsl:variable name=\"value\">\n      <xsl:variable name=\"ctx\">\n        <xsl:sequence select=\"tokens\"/>\n      </xsl:variable>\n      <xsl:for-each select=\"$ctx\">\n        <xsl:call-template name=\"malreader-read_list_helper\">\n          <xsl:with-param name=\"listkind\" select=\"$listkind\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$value\">\n      <xsl:sequence select=\"tokens\"/>\n      <xsl:variable name=\"value\">\n        <value>\n          <malval kind=\"{fn:list-kind($listkind)}\">\n            <xsl:sequence select=\"lvalue[1]\"/>\n          </malval>\n        </value>\n      </xsl:variable>\n      <xsl:choose>\n        <xsl:when test=\"error\">\n          <xsl:copy-of select=\".\"/>\n        </xsl:when>\n        <xsl:when test=\"$listkind = '{'\">\n          <xsl:choose>\n            <xsl:when test=\"count($value/value/malval/lvalue/malval) mod 2 = 1\">\n              <error>\n                <malval kind=\"error\">Odd number of values to hash</malval>\n              </error>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:sequence select=\"$value\"/>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:sequence select=\"$value\"/>\n          <xsl:sequence select=\"error\"/>\n        </xsl:otherwise>\n      </xsl:choose>\n      <xsl:sequence select=\"$prev_lvalue\"/>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:template name=\"malreader-read_list_helper\">\n    <xsl:param name=\"listkind\" as=\"xs:string\"/>\n    <xsl:choose>\n      <xsl:when test=\"count(tokens/*) &gt; 0\">\n        <xsl:variable name=\"peek\">\n          <xsl:call-template name=\"malreader-peek\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$peek\">\n          <xsl:choose>\n            <xsl:when test=\"value/token/@text = fn:list-ender($listkind) and value/token/@type = 'special'\">\n              <!-- ok -->\n              <xsl:variable name=\"next\">\n                <xsl:call-template name=\"malreader-next\"/>\n              </xsl:variable>\n              <xsl:sequence select=\"lvalue\"/>\n              <xsl:sequence select=\"$next/tokens\"/>\n              <xsl:sequence select=\"error\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:variable name=\"form\">\n                <xsl:call-template name=\"malreader-read_form\"/>\n              </xsl:variable>\n              <xsl:variable name=\"context\">\n                <xsl:for-each select=\"$form\">\n                  <!-- <xsl:message>READ_FORM <xsl:sequence select=\".\">\n                                  \n                                </xsl:sequence>\n                            \n                            ;</xsl:message> -->\n                  <xsl:sequence select=\"tokens\"/>\n                  <lvalue>\n                    <xsl:for-each select=\"lvalue/malval\">\n                      <xsl:sequence select=\".\"/>\n                    </xsl:for-each>\n                    <xsl:for-each select=\"value/malval\">\n                      <xsl:sequence select=\".\"/>\n                    </xsl:for-each>\n                  </lvalue>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"malreader-read_list_helper\">\n                  <xsl:with-param name=\"listkind\" select=\"$listkind\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:otherwise>\n          </xsl:choose>\n          <xsl:sequence select=\"lvalue\"/>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <error>\n          <malval kind=\"error\">EOF while reading sequence</malval>\n        </error>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n  <xsl:template name=\"malreader-read_atom\">\n    <xsl:variable name=\"next\">\n      <xsl:call-template name=\"malreader-next\"/>\n    </xsl:variable>\n    <xsl:for-each select=\"$next\">\n      <xsl:sequence select=\"tokens\"/>\n      <xsl:sequence select=\"lvalue\"/>\n      <xsl:sequence select=\"error\"/>\n      <xsl:choose>\n        <xsl:when test=\"value/token/@type = 'number'\">\n          <value>\n            <malval kind=\"number\" value=\"{value/token/@text}\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"value/token/@type = 'symbol'\">\n          <value>\n            <malval kind=\"symbol\" value=\"{value/token/@text}\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"value/token/@type = 'string'\">\n          <value>\n            <malval kind=\"string\" value=\"{value/token/@text}\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"value/token/@type = 'keyword'\">\n          <value>\n            <malval kind=\"keyword\" value=\"{value/token/@text}\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"value/token/@type = 'true'\">\n          <value>\n            <malval kind=\"true\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"value/token/@type = 'false'\">\n          <value>\n            <malval kind=\"false\"/>\n          </value>\n        </xsl:when>\n        <xsl:when test=\"value/token/@type = 'nil'\">\n          <value>\n            <malval kind=\"nil\"/>\n          </value>\n        </xsl:when>\n        <xsl:otherwise>\n          <error>\n            <malval kind=\"error\">\n              <xsl:sequence select=\"value\"/>\n            </malval>\n          </error>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:function name=\"fn:check_string\" as=\"xs:boolean\">\n    <xsl:param name=\"str\" as=\"xs:string\"/>\n    <xsl:sequence select=\"$str = '&quot;' or matches($str, '[^\\\\](\\\\[^n\\\\&quot;]|\\\\(\\\\\\\\)*&quot;$)')\"/>\n  </xsl:function>\n  <xsl:function name=\"fn:process-string\" as=\"xs:string\">\n    <xsl:param name=\"str\" as=\"xs:string\"/>\n    <xsl:sequence select=\"replace(replace($str, '([^\\\\]|^)\\\\n', '$1&#10;'), '\\\\([\\\\&quot;])', '$1')\"/>\n  </xsl:function>\n  <xsl:function name=\"fn:list-ender\" as=\"xs:string\">\n    <xsl:param name=\"str\" as=\"xs:string\"/>\n    <xsl:choose>\n      <xsl:when test=\"$str = '('\">\n        <xsl:sequence select=\"')'\"/>\n      </xsl:when>\n      <xsl:when test=\"$str = '['\">\n        <xsl:sequence select=\"']'\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"'}'\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n  <xsl:function name=\"fn:list-kind\" as=\"xs:string\">\n    <xsl:param name=\"str\" as=\"xs:string\"/>\n    <xsl:choose>\n      <xsl:when test=\"$str = '('\">\n        <xsl:sequence select=\"'list'\"/>\n      </xsl:when>\n      <xsl:when test=\"$str = '['\">\n        <xsl:sequence select=\"'vector'\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"'hash'\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/readline.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Readline: interface for talking to the harness about readline -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\">\n  <xsl:function name=\"readline:readline\">\n    <xsl:param name=\"prompt\"/>\n    <xsl:message>\n      <request kind=\"readline\" value=\"{$prompt}\"/>\n    </xsl:message>\n    <xsl:variable name=\"value\" select=\"unparsed-text('xsl_input-string')\"/>\n    <xsl:sequence select=\"$value\"/>\n  </xsl:function>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/run",
    "content": "#!/usr/bin/python3\n\nimport time\nimport os.path\nimport readline\nimport sys\nimport xml.etree.ElementTree as ET\nfrom threading import Thread\nfrom threading import Lock\nfrom collections import deque\n\nsaxon_jar = '/usr/share/java/Saxon-HE-*.jar'\nsaxon = f'java -Xmx2G -cp {saxon_jar} net.sf.saxon.Transform'\n\nstep_dir = os.path.dirname(sys.argv[0])\nstep_base = os.getenv(key='STEP', default='stepA_mal')\nfname = os.path.join(step_dir, step_base + '.xslt')\n\nargs = sys.argv[1:]\ntree = ET.Element('mal')\n\nif len(args) > 0:\n    args0 = args[0]\n    ET.SubElement(tree, 'argv')\n    for a in tree.iter('mal'):\n        for a in a.iter('argv'):\n            for arg in args[1:]:\n                ET.SubElement(a, 'arg').text = arg\n    ET.SubElement(tree, 'no_repl')\n\ntree = ET.ElementTree(tree)\nstdout = sys.stdout\n\ntry:\n    readline.read_history_file('.xslt_mal_history')\nexcept:\n    pass\n\nHALT = False\nTHE_PID = None\ninit_t = time.time() * 1000\nreadline_queue = deque()\nos.system('rm -rf xsl_error.xml')\nos.system('mkfifo xsl_error.xml')\n\ndef setup_request_file():\n    os.system('rm -rf xsl_input-string')\n    os.system('mkfifo xsl_input-string')\n\n\ndef get_one(fd):\n    s = b\"\"\n    while True:\n        x = os.read(fd, 1)\n        if x == b'\\n':\n            break\n        if x == b'':\n            break\n        s += x\n    if s == \"\":\n        return None\n    return s.decode('utf-8')\n\n\ndef serve_one_request(res):\n    global HALT\n    if len(res) == 0:\n        return\n    try:\n        xtree = ET.fromstring(\"<data>\" + res.strip('\\x00') + \"</data>\")\n        # stdout.write(xtree.attrib['kind'])\n        for req in xtree:\n            if req.attrib['kind'] == 'readline':\n                x = None\n                if len(readline_queue) > 0:\n                    x = readline_queue.popleft()\n                else:\n                    x = input(req.attrib['value'])\n                with open('xsl_input-string', 'w') as fx:\n                    fx.write(x)\n                # stdout.write(' = ' + x)\n            elif req.attrib['kind'] == 'halt':\n                HALT = True\n            elif req.attrib['kind'] == 'display':\n                stdout.write(req.attrib['value'] + '\\n')\n            elif req.attrib['kind'] == 'time':\n                x = time.time() * 1000 - init_t\n                # stdout.write(' = ' + str(int(x)))\n                with open('xsl_input-string', 'w') as fx:\n                    fx.write(str(int(x)))\n            # stdout.write('\\n')\n            elif req.attrib['kind'] == 'xpath-eval':\n                xpath = req.attrib['value']\n                with open('xsl-eval.xslt', 'w') as f:\n                    f.write(f'<?xml version=\"1.0\" encoding=\"UTF-8\"?><xsl:stylesheet version=\"3.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"  xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"  xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\" xmlns:env=\"ENV\" xmlns:core=\"CORE\" exclude-result-prefixes=\"env core xs xsl map fn\"><xsl:output omit-xml-declaration=\"yes\"/><xsl:template match=\"/\"><xsl:sequence select=\"{xpath}\" /></xsl:template></xsl:stylesheet>')\n                with open('xsl-null.xml', 'w') as f:\n                    f.write(req.attrib['context'])\n\n                if os.system(f'{saxon} -xsl:xsl-eval.xslt -s:xsl-null.xml > xsl-eval_output.xml'):\n                    x = ''\n                else:\n                    with open('xsl-eval_output.xml', 'r') as f:\n                        x = f.read()\n                with open('xsl_input-string', 'w') as fx:\n                    fx.write(x)\n            else:\n                stdout.write(\"UNKNOWN REQUEST \" + req.attrib['kind'])\n            # stdout.write('\\n')\n    except Exception as e:\n        # if str(e) != 'no element found: line 1, column 0':\n        #     f.seek(0)\n        #     print(e, list(x for x in f.read()))\n        return\n    # with open('xsl_error.xml', 'w') as f:\n    #     f.write('')\n\ndef transform(do_print=True):\n    global tree, HALT, THE_PID\n\n    tree.write('xslt_input.xml')\n    setup_request_file()\n    pid = os.fork()\n    if pid == 0:\n        os.system(f'{saxon} -xsl:\"{fname}\" -s:xslt_input.xml -TP:perf.html > xslt_output.xml 2> xsl_error.xml')\n        HALT = True\n    else:\n        THE_PID = pid\n        fd = os.open('xsl_error.xml', os.O_RDONLY | os.O_CLOEXEC)\n        while True:\n            try:\n                if HALT:\n                    os.kill(THE_PID, 9)\n                    raise KeyboardInterrupt()\n                cmd = get_one(fd)\n                if cmd:\n                    serve_one_request(cmd)\n            except KeyboardInterrupt:\n                exit()\n            except Exception as e:\n                print(\"Harness error:\", e)\n        tree = ET.parse('xslt_output.xml')\n        if do_print:\n            stdout = ''\n            for a in tree.iter('mal'):\n                for a in a.iter('stdout'):\n                    stdout = a\n            print(stdout.text)\n            stdout.clear()\n            del stdout\n\n\nif len(args) > 0:\n    readline_queue.append(f'(do (load-file \"{args0}\") (xslt-halt))')\n    transform(do_print=False)\nelse:\n    if fname == 'stepA_mal.xslt':\n        readline_queue.append('(println (str \"Mal [\" *host-language* \"]\"))')\n        transform(do_print=False)\n    else:\n        transform()\n    readline.write_history_file('.xslt_mal_history')\n"
  },
  {
    "path": "impls/xslt/step0_repl.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 0: REPL -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> ignored, preserved </state>\n</mal>\n-->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <mal>\n      <stdin/>\n      <!-- clear stdin -->\n      <xsl:copy-of select=\"state\"/>\n      <!-- preserve state -->\n      <xsl:variable name=\"_read\">\n        <xsl:call-template name=\"READ\"/>\n      </xsl:variable>\n      <xsl:variable name=\"_eval\">\n        <xsl:for-each select=\"$_read\">\n          <xsl:call-template name=\"EVAL\"/>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:variable name=\"_print\">\n        <xsl:for-each select=\"$_eval\">\n          <xsl:call-template name=\"PRINT\"/>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{$_print}\"/>\n      </xsl:message>\n    </mal>\n  </xsl:template>\n  <xsl:template name=\"PRINT\">\n    <xsl:sequence select=\".\"/>\n  </xsl:template>\n  <xsl:template name=\"EVAL\">\n    <xsl:sequence select=\".\"/>\n  </xsl:template>\n  <xsl:template name=\"READ\">\n    <xsl:copy-of select=\"stdin/text()\"/>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step0_repl.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step0_repl.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step1_read_print.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 1: Read-Print -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> ignored, preserved </state>\n</mal>\n-->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <mal>\n      <stdin/>\n      <!-- clear stdin -->\n      <xsl:copy-of select=\"state\"/>\n      <!-- preserve state -->\n      <xsl:variable name=\"_read\">\n        <xsl:call-template name=\"READ\"/>\n      </xsl:variable>\n      <xsl:variable name=\"_eval\">\n        <xsl:for-each select=\"$_read\">\n          <xsl:call-template name=\"EVAL\"/>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:variable name=\"_print\">\n        <xsl:for-each select=\"$_eval\">\n          <xsl:call-template name=\"PRINT\"/>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{$_print}\"/>\n      </xsl:message>\n    </mal>\n  </xsl:template>\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"context\">\n      <xsl:variable name=\"str\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:value-of select=\"$str\"/>\n    </xsl:variable>\n    <xsl:for-each select=\"$context\">\n      <xsl:copy-of select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n  <xsl:template name=\"EVAL\">\n    <xsl:copy-of select=\".\"/>\n  </xsl:template>\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:copy-of select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step1_read_print.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step1_read_print.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step2_eval.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 2: Eval -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> ignored, preserved </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <mal>\n      <xsl:variable name=\"env\" as=\"map(*)\">\n        <xsl:if test=\"not(state)\">\n          <!-- we never update this -->\n          <xsl:map>\n            <xsl:map-entry key=\"'+'\" select=\"'+'\"/>\n            <xsl:map-entry key=\"'-'\" select=\"'-'\"/>\n            <xsl:map-entry key=\"'*'\" select=\"'*'\"/>\n            <xsl:map-entry key=\"'/'\" select=\"'/'\"/>\n          </xsl:map>\n        </xsl:if>\n      </xsl:variable>\n      <xsl:sequence select=\"stdin\"/>\n      <xsl:variable name=\"_read\">\n        <xsl:call-template name=\"READ\"/>\n      </xsl:variable>\n      <xsl:variable name=\"_eval\">\n        <xsl:for-each select=\"$_read\">\n          <xsl:call-template name=\"EVAL\">\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:variable name=\"_print\">\n        <xsl:for-each select=\"$_eval\">\n          <xsl:call-template name=\"PRINT\">\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{$_print}\"/>\n      </xsl:message>\n    </mal>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <!-- vapply[fn, args] :: fn/value/text() -->\n  <xsl:template name=\"vapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:choose>\n      <xsl:when test=\"$func = '+'\">\n        <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) + number($args/value/malval/lvalue/malval[2]/@value)\"/>\n        <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n      </xsl:when>\n      <xsl:when test=\"$func = '-'\">\n        <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) - number($args/value/malval/lvalue/malval[2]/@value)\"/>\n        <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n      </xsl:when>\n      <xsl:when test=\"$func = '*'\">\n        <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) * number($args/value/malval/lvalue/malval[2]/@value)\"/>\n        <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n      </xsl:when>\n      <xsl:when test=\"$func = '/'\">\n        <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) div number($args/value/malval/lvalue/malval[2]/@value)\"/>\n        <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <error>Invalid function <xsl:sequence select=\"$func\"/> </error>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"fn:env_lookup($env, value/malval/@value)\"/>\n          </value>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\">\n          <value>\n            <malval kind=\"vector\">\n              <lvalue>\n                <xsl:for-each select=\"value/malval/lvalue/malval\">\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctx\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:call-template name=\"EVAL\">\n                        <xsl:with-param name=\"env\" select=\"$env\"/>\n                      </xsl:call-template>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$xctx/value/malval\"/>\n                </xsl:for-each>\n              </lvalue>\n            </malval>\n          </value>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'hash'\">\n          <value>\n            <malval kind=\"hash\">\n              <lvalue>\n                <xsl:for-each select=\"value/malval/lvalue/malval\">\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctx\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:call-template name=\"EVAL\">\n                        <xsl:with-param name=\"env\" select=\"$env\"/>\n                      </xsl:call-template>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$xctx/value/malval\"/>\n                </xsl:for-each>\n              </lvalue>\n            </malval>\n          </value>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n          <xsl:variable name=\"new_list\">\n            <xsl:for-each select=\"value/malval/lvalue/malval\">\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"xctx\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$xctx/value/malval\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:sequence select=\"$new_list/malval[1]/text()\"/>\n          </xsl:variable>\n          <xsl:variable name=\"args\">\n            <value>\n              <malval kind=\"list\">\n                <lvalue>\n                  <xsl:sequence select=\"$new_list/node()[position() != 1]\"/>\n                </lvalue>\n              </malval>\n            </value>\n          </xsl:variable>\n          <xsl:call-template name=\"vapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n          </xsl:call-template>\n        </xsl:otherwise>\n      </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:copy-of select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:env_lookup\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"name\"/>\n    <xsl:variable name=\"value\">\n      <xsl:sequence select=\"$env(string($name))\"/>\n    </xsl:variable>\n    <xsl:choose>\n      <xsl:when test=\"$value != ''\">\n        <malval kind=\"nfunction\" name=\"{$name}\">\n          <xsl:sequence select=\"$value\"/>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <malval kind=\"nil\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:function>\n\n  <xsl:function name=\"fn:makeMALType\">\n    <xsl:param name=\"value\"/>\n    <xsl:param name=\"kind\"/>\n    <value>\n      <malval kind=\"{$kind}\" value=\"{$value}\"/>\n    </value>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step2_eval.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step2_eval.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step3_env.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 3: Environment -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <mal>\n      <xsl:variable name=\"env\" as=\"map(*)\">\n        <xsl:sequence select=\"env:deserialise((state/env/@data, env:base())[1])\"/>\n      </xsl:variable>\n      <xsl:sequence select=\"stdin\"/>\n      <xsl:variable name=\"_read\">\n        <xsl:call-template name=\"READ\"/>\n      </xsl:variable>\n      <xsl:variable name=\"_eval\">\n        <xsl:for-each select=\"$_read\">\n          <xsl:call-template name=\"EVAL\">\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:variable>\n      <xsl:for-each select=\"$_eval\">\n        <xsl:variable name=\"_print\">\n          <xsl:for-each select=\"data\">\n            <xsl:call-template name=\"PRINT\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:message>\n          <request kind=\"display\" value=\"{$_print}\"/>\n        </xsl:message>\n        <state>\n          <env data=\"{env/@data}\"/>\n        </state>\n      </xsl:for-each>\n    </mal>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <!-- vapply[fn, args] :: fn/value/text() -->\n  <xsl:template name=\"vapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:choose>\n      <xsl:when test=\"$func/malval/@kind = 'function'\">\n        <xsl:choose>\n          <xsl:when test=\"$func/malval/@name = '+'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) + number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '-'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) - number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '*'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) * number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:when test=\"$func/malval/@name = '/'\">\n            <xsl:variable name=\"result\" select=\"number($args/value/malval/lvalue/malval[1]/@value) div number($args/value/malval/lvalue/malval[2]/@value)\"/>\n            <xsl:sequence select=\"fn:makeMALType($result, 'number')\"/>\n          </xsl:when>\n          <xsl:otherwise>\n            <xsl:value-of select=\"error(QName('MAL', 'Error'), concat('Invalid function ', $func))\"/>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:when>\n      <xsl:otherwise/>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\">\n          <value>\n            <malval kind=\"vector\">\n              <lvalue>\n                <xsl:for-each select=\"value/malval/lvalue/malval\">\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctx\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$xctx/value/malval\"/>\n                </xsl:for-each>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'hash'\">\n          <value>\n            <malval kind=\"hash\">\n              <lvalue>\n                <xsl:for-each select=\"value/malval/lvalue/malval\">\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctx\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$xctx/value/malval\"/>\n                </xsl:for-each>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n        </xsl:when>\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n          </xsl:variable>\n          <xsl:variable name=\"new_env\" select=\"env:close($env)\"/>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($env)}\"/>\n              </xsl:if>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n          <xsl:variable name=\"new_list\">\n            <xsl:for-each select=\"value/malval/lvalue/malval\">\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"xctx\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$xctx/value/malval\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:sequence select=\"$new_list/malval[1]\"/>\n          </xsl:variable>\n          <xsl:variable name=\"args\">\n            <value>\n              <malval kind=\"list\">\n                <lvalue>\n                  <xsl:sequence select=\"$new_list/node()[position() != 1]\"/>\n                </lvalue>\n              </malval>\n            </value>\n          </xsl:variable>\n          <xsl:call-template name=\"vapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n          </xsl:call-template>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:copy-of select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:makeMALType\">\n    <xsl:param name=\"value\"/>\n    <xsl:param name=\"kind\"/>\n    <value>\n      <malval kind=\"{$kind}\" value=\"{$value}\"/>\n    </value>\n  </xsl:function>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step3_env.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step3_env.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step4_if_fn_do.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 4: If Fn Do -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:core=\"CORE\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env core\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:import href=\"core.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <xsl:choose>\n      <xsl:when test=\"string(state/env/@data) = ''\">\n        <xsl:variable name=\"vstate\">\n          <mal>\n            <state>\n              <env data=\"{env:serialise(env:empty() =&gt; env:bind-all(core:ns()/@name, core:ns()))}\"/>\n            </state>\n            <stdin>(def! not (fn* (a) (if a false true)))</stdin>\n          </mal>\n        </xsl:variable>\n        <xsl:variable name=\"new-state\">\n          <xsl:for-each select=\"$vstate/mal\">\n            <xsl:call-template name=\"rep\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:variable name=\"state-v\">\n          <xsl:sequence select=\"$new-state/mal/state\"/>\n          <xsl:sequence select=\"stdin\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$state-v\">\n          <xsl:call-template name=\"rep\">\n            <xsl:with-param name=\"display\" select=\"$display\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <mal>\n          <xsl:variable name=\"env\" as=\"map(*)\">\n            <xsl:sequence select=\"env:deserialise(state/env/@data)\"/>\n          </xsl:variable>\n          <xsl:sequence select=\"stdin\"/>\n          <xsl:variable name=\"_read\">\n            <xsl:call-template name=\"READ\"/>\n          </xsl:variable>\n          <xsl:variable name=\"_eval\">\n            <xsl:for-each select=\"$_read\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$_eval\">\n            <xsl:variable name=\"_print\">\n              <xsl:for-each select=\"data\">\n                <xsl:call-template name=\"PRINT\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:if test=\"$display\">\n              <xsl:message>\n                <request kind=\"display\" value=\"{$_print}\"/>\n              </xsl:message>\n            </xsl:if>\n            <state>\n              <env data=\"{env/@data}\"/>\n            </state>\n          </xsl:for-each>\n        </mal>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <!-- uapply[env, fn, args] -->\n  <xsl:template name=\"uapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"nenv\" select=\"$env =&gt; env:hier(env:deserialise($func/malval/env/@data)) =&gt; env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)\"/>\n    <xsl:variable name=\"body\">\n      <value>\n        <xsl:sequence select=\"$func/malval/body/malval\"/>\n      </value>\n    </xsl:variable>\n    <xsl:variable name=\"result\">\n      <xsl:for-each select=\"$body\">\n        <xsl:call-template name=\"EVAL\">\n          <xsl:with-param name=\"env\" select=\"$nenv\"/>\n          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$result/data/value\"/>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\">\n          <value>\n            <malval kind=\"vector\">\n              <lvalue>\n                <xsl:for-each select=\"value/malval/lvalue/malval\">\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctx\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$xctx/value/malval\"/>\n                </xsl:for-each>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'hash'\">\n          <value>\n            <malval kind=\"hash\">\n              <lvalue>\n                <xsl:for-each select=\"value/malval/lvalue/malval\">\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctx\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$xctx/value/malval\"/>\n                </xsl:for-each>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n        </xsl:when>\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n          </xsl:variable>\n          <xsl:variable name=\"new_env\" select=\"env:close($env)\"/>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($env)}\"/>\n              </xsl:if>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'do'\">\n          <xsl:iterate select=\"value/malval/lvalue/malval[position() &gt; 1]\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:param name=\"previous_res\" select=\"()\"/>\n            <xsl:on-completion>\n              <xsl:sequence select=\"$previous_res\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($new_env)}\"/>\n              </xsl:if>\n            </xsl:on-completion>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"true()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:deserialise($value/env/@data)\"/>\n              <xsl:with-param name=\"previous_res\" select=\"$value/data/value\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'if'\">\n          <xsl:variable name=\"cond\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[2]\">\n              <xsl:variable name=\"context\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"ptrue\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[3]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"pfalse\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[4]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"xfalse\">\n            <xsl:choose>\n              <xsl:when test=\"empty($pfalse/value)\">\n                <value>\n                  <malval kind=\"nil\"/>\n                </value>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"$pfalse/value\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:choose>\n              <xsl:when test=\"let $kind := $cond/data/value/malval/@kind\n                              return $kind = 'nil' or $kind = 'false'\">\n                <xsl:for-each select=\"$xfalse\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:for-each select=\"$ptrue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'fn*'\">\n          <value>\n            <malval kind=\"userfunction\">\n              <binds>\n                <xsl:sequence select=\"value/malval/lvalue/malval[2]/lvalue/malval\"/>\n              </binds>\n              <body>\n                <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n              </body>\n              <env data=\"{env:serialise($env)}\"/>\n              <!-- capture current env -->\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n          <xsl:variable name=\"new_list\">\n            <xsl:for-each select=\"value/malval/lvalue/malval\">\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"xctx\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$xctx/value/malval\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:sequence select=\"$new_list/malval[1]\"/>\n          </xsl:variable>\n          <xsl:variable name=\"args\">\n            <value>\n              <malval kind=\"list\">\n                <lvalue>\n                  <xsl:sequence select=\"$new_list/node()[position() != 1]\"/>\n                </lvalue>\n              </malval>\n            </value>\n          </xsl:variable>\n          <xsl:choose>\n            <xsl:when test=\"$func/malval/@kind = 'userfunction'\">\n              <xsl:call-template name=\"uapply\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"func\" select=\"$func\"/>\n                <xsl:with-param name=\"args\" select=\"$args\"/>\n              </xsl:call-template>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:call-template name=\"core-apply\">\n                <xsl:with-param name=\"func\" select=\"$func\"/>\n                <xsl:with-param name=\"args\" select=\"$args\"/>\n              </xsl:call-template>\n            </xsl:otherwise>\n          </xsl:choose>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:copy-of select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step4_if_fn_do.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step4_if_fn_do.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step6_file.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 6: File -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env and atoms </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:core=\"CORE\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env core\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:import href=\"core.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <xsl:choose>\n      <xsl:when test=\"string(state/env/@data) = ''\">\n        <xsl:variable name=\"argv\">\n          <malval kind=\"list\">\n            <lvalue>\n              <xsl:for-each select=\"argv/arg/text()\">\n                <malval kind=\"string\" value=\"{.}\"/>\n              </xsl:for-each>\n            </lvalue>\n          </malval>\n        </xsl:variable>\n        <xsl:variable name=\"vstate\">\n          <mal>\n            <state>\n              <env data=\"{env:serialise(env:empty() =&gt; env:bind-all(core:ns()/@name, core:ns()) =&gt; env:set('*ARGV*', $argv) =&gt; env:toReplEnv())}\"/>\n              <atoms/>\n            </state>\n            <stdin>(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))))</stdin>\n          </mal>\n        </xsl:variable>\n        <xsl:variable name=\"new-state\">\n          <xsl:for-each select=\"$vstate/mal\">\n            <xsl:call-template name=\"rep\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:variable name=\"state-v\">\n          <xsl:sequence select=\"$new-state/mal/state\"/>\n          <xsl:sequence select=\"stdin\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$state-v\">\n          <xsl:call-template name=\"rep\">\n            <xsl:with-param name=\"display\" select=\"$display\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <mal>\n          <xsl:variable name=\"env\" as=\"map(*)\">\n            <xsl:sequence select=\"env:deserialise(state/env/@data)\"/>\n          </xsl:variable>\n          <xsl:sequence select=\"stdin\"/>\n          <xsl:variable name=\"_read\">\n            <xsl:call-template name=\"READ\"/>\n          </xsl:variable>\n          <xsl:variable name=\"_eval\">\n            <xsl:for-each select=\"$_read\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$_eval\">\n            <xsl:variable name=\"_print\">\n              <xsl:variable name=\"data\">\n                <xsl:sequence select=\"data/value\"/>\n                <xsl:sequence select=\"atoms[1]\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$data\">\n                <xsl:call-template name=\"PRINT\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:if test=\"$display\">\n              <xsl:message>\n                <request kind=\"display\" value=\"{$_print}\"/>\n              </xsl:message>\n            </xsl:if>\n            <state>\n              <env data=\"{env/@data}\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n            </state>\n          </xsl:for-each>\n        </mal>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <xsl:template name=\"call-function\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"atoms\" select=\"atoms\"/>\n    <xsl:variable name=\"res\">\n      <xsl:choose>\n        <xsl:when test=\"$func/malval/@kind = 'userfunction'\">\n          <xsl:call-template name=\"uapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:choose>\n            <xsl:when test=\"$func/malval/@name = 'env??'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"nev\" select=\"env:dump($env)\"/>\n              <xsl:variable name=\"value\">\n                <malval kind=\"string\" value=\"{$env =&gt; serialize(map{'method':'json'})}\"/>\n              </xsl:variable>\n              <value>\n                <xsl:sequence select=\"$value\"/>\n              </value>\n              <env data=\"{env:serialise($env)}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'eval'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"venv\" select=\"env:replEnv($env) =&gt; env:wrapReplEnv()\"/>\n              <xsl:variable name=\"form\">\n                <value>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$form\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$venv\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:collapseReplEnv()))}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'atom'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"count($atoms/atom)\"/>\n              <value>\n                <malval kind=\"atom\" value=\"{$atom-ident}\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'deref'\">\n              <!-- needs access to atoms -->\n              <value>\n                <xsl:sequence select=\"$atoms/atom[@identity = $args/value/malval/lvalue/malval[1]/@value]/malval\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'reset!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"newv\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n              <value>\n                <xsl:sequence select=\"$newv\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'swap!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"atom-value\" select=\"$atoms/atom[@identity = $atom-ident]/malval\"/>\n              <xsl:variable name=\"fn\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"cargs\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:sequence select=\"$atom-value\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() &gt; 2]\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"newv\">\n                <xsl:call-template name=\"call-function\">\n                  <xsl:with-param name=\"func\" select=\"$fn\"/>\n                  <xsl:with-param name=\"args\" select=\"$cargs\"/>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:sequence select=\"$newv/value\"/>\n              <atoms>\n                <xsl:for-each select=\"$newv/atoms[1]/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv/value/malval\"/>\n                </atom>\n              </atoms>\n              <xsl:sequence select=\"$newv/env\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'apply'\">\n              <!-- needs access to env -->\n              <!-- (apply F A... T) -> (F A... T...) -->\n              <xsl:variable name=\"fun\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"args\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:variable name=\"argc\" select=\"count($args/value/malval/lvalue/malval)\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() gt 1 and position() lt $argc]\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[$argc]/lvalue/malval\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"./*\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"func\" select=\"$fun\"/>\n                    <xsl:with-param name=\"args\" select=\"$args\"/>\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/value\"/>\n              <xsl:sequence select=\"$res/env\"/>\n              <xsl:sequence select=\"$res/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'map'\">\n              <!-- needs access to env -->\n              <!-- (map F T) -> (list (F Tx)...) -->\n              <xsl:variable name=\"func\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:iterate select=\"$args/value/malval/lvalue/malval[2]/lvalue/malval\">\n                <xsl:param name=\"xenv\" select=\"$env\"/>\n                <xsl:param name=\"atoms\" select=\"$atoms\"/>\n                <xsl:param name=\"value\" select=\"()\"/>\n                <xsl:on-completion>\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\"$value\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                  <env data=\"{$env =&gt; env:serialise()}\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:on-completion>\n                <xsl:variable name=\"args\">\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\".\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                </xsl:variable>\n                <xsl:variable name=\"ctx\">\n                  <xsl:sequence select=\"./*\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <xsl:variable name=\"res\">\n                  <xsl:for-each select=\"$ctx\">\n                    <xsl:call-template name=\"call-function\">\n                      <xsl:with-param name=\"func\" select=\"$func\"/>\n                      <xsl:with-param name=\"args\" select=\"$args\"/>\n                      <xsl:with-param name=\"env\" select=\"$xenv\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:next-iteration>\n                  <xsl:with-param name=\"xenv\" select=\"env:deserialise($res/env/@data)\"/>\n                  <xsl:with-param name=\"atoms\" select=\"$res/atoms[1]\"/>\n                  <xsl:with-param name=\"value\" select=\"$value, $res/value/malval\"/>\n                </xsl:next-iteration>\n              </xsl:iterate>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'readline'\">\n              <!-- requires interaction with harness -->\n              <xsl:message>\n                <request kind=\"readline\" value=\"{$args/value/malval/lvalue/malval[1]/@value}\"/>\n              </xsl:message>\n              <xsl:variable name=\"str\" select=\"unparsed-text('xsl_input-string')\"/>\n              <xsl:sequence select=\"core:makeMALType($str, if (string-length($str) = 0) then 'nil' else 'string')\"/>\n              <xsl:sequence select=\"env\"/>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"core-apply\">\n                  <xsl:with-param name=\"func\" select=\"$func\"/>\n                  <xsl:with-param name=\"args\" select=\"$args\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$res/value\"/>\n    <xsl:choose>\n      <xsl:when test=\"$res/atoms\">\n        <xsl:sequence select=\"$res/atoms[1]\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"atoms\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n    <xsl:choose>\n      <xsl:when test=\"$res/env\">\n        <xsl:sequence select=\"$res/env\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <env data=\"{$env =&gt; env:serialise()}\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- uapply[env, fn, args] -->\n  <xsl:template name=\"uapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"nenv\" select=\"$env =&gt; env:hier(env:deserialise($func/malval/env/@data)) =&gt; env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)\"/>\n    <xsl:variable name=\"body\">\n      <value>\n        <xsl:sequence select=\"$func/malval/body/malval\"/>\n      </value>\n      <xsl:sequence select=\"atoms[1]\"/>\n    </xsl:variable>\n    <xsl:variable name=\"result\">\n      <xsl:for-each select=\"$body\">\n        <xsl:call-template name=\"EVAL\">\n          <xsl:with-param name=\"env\" select=\"$nenv\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$result/data/value\"/>\n    <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($result/env/@data) =&gt; env:replEnv()))}\"/>\n    <xsl:sequence select=\"$result/atoms[1]\"/>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n    <!-- <xsl:message>\n\nEVALUATE <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> IN (<xsl:sequence select=\"empty(atoms)\"/>) ATOMS <xsl:sequence select=\"fn:pretty(atoms)\"/>\n\n</xsl:message> -->\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"atoms\" select=\"atoms[1]\"/>\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\n                        or value/malval/@kind = 'hash'\">\n          <xsl:variable name=\"myctx\">\n            <xsl:iterate select=\"value/malval/lvalue/malval\">\n              <xsl:param name=\"atoms\" select=\"$atoms\"/>\n              <xsl:param name=\"xctx\" select=\"()\"/>\n              <xsl:on-completion>\n                <xsl:sequence select=\"$xctx/value/malval\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:on-completion>\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"xctxy\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                  <xsl:sequence select=\"$val/atoms\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:next-iteration>\n                <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n              </xsl:next-iteration>\n            </xsl:iterate>\n          </xsl:variable>\n          <value>\n            <malval kind=\"{value/malval/@kind}\">\n              <lvalue>\n                <xsl:sequence select=\"$myctx/malval\"/>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:sequence select=\"$myctx/atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:sequence select=\"$atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"env:close($env)\"/>\n            <xsl:param name=\"new_atoms\" select=\"$atoms\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"xvalue\">\n                <xsl:sequence select=\"$xvalue/value\"/>\n                <xsl:sequence select=\"$new_atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:replEnv()))}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n              <xsl:sequence select=\"$new_atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n              <xsl:with-param name=\"new_atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'do'\">\n          <xsl:iterate select=\"value/malval/lvalue/malval[position() &gt; 1]\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:param name=\"atoms\" select=\"$atoms\"/>\n            <xsl:param name=\"previous_res\" select=\"()\"/>\n            <xsl:on-completion>\n              <xsl:sequence select=\"$previous_res\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($new_env)}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:deserialise($value/env/@data)\"/>\n              <xsl:with-param name=\"previous_res\" select=\"$value/data/value\"/>\n              <xsl:with-param name=\"atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'if'\">\n          <xsl:variable name=\"cond\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[2]\">\n              <xsl:variable name=\"context\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"ptrue\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[3]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"pfalse\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[4]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"xfalse\">\n            <xsl:choose>\n              <xsl:when test=\"empty($pfalse/value)\">\n                <value>\n                  <malval kind=\"nil\"/>\n                </value>\n                <xsl:sequence select=\"$cond/atoms[1]\"/>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"$pfalse/value\"/>\n                <xsl:sequence select=\"$pfalse/atoms[1]\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:choose>\n              <xsl:when test=\"let $kind := $cond/data/value/malval/@kind\n                              return $kind = 'nil' or $kind = 'false'\">\n                <xsl:for-each select=\"$xfalse\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:for-each select=\"$ptrue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'fn*'\">\n          <value>\n            <malval kind=\"userfunction\">\n              <binds>\n                <xsl:sequence select=\"value/malval/lvalue/malval[2]/lvalue/malval\"/>\n              </binds>\n              <body>\n                <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n              </body>\n              <env data=\"{env:serialise(env:noReplEnv($env))}\"/>\n              <!-- capture current env -->\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n\n          <!-- evaluate the first element -->\n          <xsl:variable name=\"ctx\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[1]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:for-each select=\"$ctx\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n\n              <xsl:variable name=\"new_list\">\n                <xsl:iterate select=\"value/malval/lvalue/malval[position() != 1]\">\n                  <xsl:param name=\"atoms\" select=\"$func/atoms\"/>\n                  <xsl:param name=\"xctx\" select=\"()\"/>\n                  <xsl:on-completion>\n                    <xsl:sequence select=\"$xctx/value/malval\"/>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:on-completion>\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctxy\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                      <xsl:sequence select=\"$val/atoms\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:next-iteration>\n                    <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                    <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n                  </xsl:next-iteration>\n                </xsl:iterate>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$new_list/atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"resultv\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                    <xsl:with-param name=\"args\">\n                      <value>\n                        <malval kind=\"list\">\n                          <lvalue>\n                            <xsl:sequence select=\"$new_list/malval\"/>\n                          </lvalue>\n                        </malval>\n                      </value>\n                    </xsl:with-param>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:for-each select=\"$resultv\">\n                <xsl:choose>\n                  <xsl:when test=\"empty(env)\">\n                    <xsl:if test=\"$encode-env\">\n                      <env data=\"{env:serialise($env)}\"/>\n                    </xsl:if>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <xsl:if test=\"$encode-env\">\n                      <xsl:sequence select=\"env\"/>\n                    </xsl:if>\n                  </xsl:otherwise>\n                </xsl:choose>\n                <xsl:sequence select=\"atoms[1]\"/>\n                <xsl:sequence select=\"value\"/>\n              </xsl:for-each>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:variable name=\"new-atoms\" select=\"($data/atoms[1], $atoms)[1]\"/>\n    <!-- <xsl:message>\nEVALUATED (<xsl:sequence select=\"empty($data/atoms)\"/>) <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> TO <xsl:sequence select=\"core:pr-str($data/value/malval)/value/text()\"/> WITH ATOMS <xsl:sequence select=\"fn:pretty($new-atoms)\"/>\n      </xsl:message> -->\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n    <xsl:sequence select=\"$new-atoms\"/>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:sequence select=\"state/atoms[1]\"/>\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:sequence select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n  <xsl:function name=\"fn:pretty\">\n    <xsl:param name=\"atoms\"/>\n    <xsl:sequence select=\"$atoms/atom/concat('(', @identity, ': ', malval/@kind, ' ', core:pr-str(malval), ')')\"/>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step6_file.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step6_file.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step7_quote.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 7: Quoting -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env and atoms </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:core=\"CORE\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env core\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:import href=\"core.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <xsl:choose>\n      <xsl:when test=\"string(state/env/@data) = ''\">\n        <xsl:variable name=\"argv\">\n          <malval kind=\"list\">\n            <lvalue>\n              <xsl:for-each select=\"argv/arg/text()\">\n                <malval kind=\"string\" value=\"{.}\"/>\n              </xsl:for-each>\n            </lvalue>\n          </malval>\n        </xsl:variable>\n        <xsl:variable name=\"vstate\">\n          <mal>\n            <state>\n              <env data=\"{env:serialise(env:empty() =&gt; env:bind-all(core:ns()/@name, core:ns()) =&gt; env:set('*ARGV*', $argv) =&gt; env:toReplEnv())}\"/>\n              <atoms/>\n            </state>\n            <stdin>(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))))</stdin>\n          </mal>\n        </xsl:variable>\n        <xsl:variable name=\"new-state\">\n          <xsl:for-each select=\"$vstate/mal\">\n            <xsl:call-template name=\"rep\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:variable name=\"state-v\">\n          <xsl:sequence select=\"$new-state/mal/state\"/>\n          <xsl:sequence select=\"stdin\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$state-v\">\n          <xsl:call-template name=\"rep\">\n            <xsl:with-param name=\"display\" select=\"$display\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <mal>\n          <xsl:variable name=\"env\" as=\"map(*)\">\n            <xsl:sequence select=\"env:deserialise(state/env/@data)\"/>\n          </xsl:variable>\n          <xsl:sequence select=\"stdin\"/>\n          <xsl:variable name=\"_read\">\n            <xsl:call-template name=\"READ\"/>\n          </xsl:variable>\n          <xsl:variable name=\"_eval\">\n            <xsl:for-each select=\"$_read\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$_eval\">\n            <xsl:variable name=\"_print\">\n              <xsl:variable name=\"data\">\n                <xsl:sequence select=\"data/value\"/>\n                <xsl:sequence select=\"atoms[1]\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$data\">\n                <xsl:call-template name=\"PRINT\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:if test=\"$display\">\n              <xsl:message>\n                <request kind=\"display\" value=\"{$_print}\"/>\n              </xsl:message>\n            </xsl:if>\n            <state>\n              <env data=\"{env/@data}\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n            </state>\n          </xsl:for-each>\n        </mal>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <xsl:template name=\"call-function\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"atoms\" select=\"atoms\"/>\n    <xsl:variable name=\"res\">\n      <xsl:choose>\n        <xsl:when test=\"$func/malval/@kind = 'userfunction'\">\n          <xsl:call-template name=\"uapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:choose>\n            <xsl:when test=\"$func/malval/@name = 'env??'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"nev\" select=\"env:dump($env)\"/>\n              <xsl:variable name=\"value\">\n                <malval kind=\"string\" value=\"{$env =&gt; serialize(map{'method':'json'})}\"/>\n              </xsl:variable>\n              <value>\n                <xsl:sequence select=\"$value\"/>\n              </value>\n              <env data=\"{env:serialise($env)}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'eval'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"venv\" select=\"env:replEnv($env) =&gt; env:wrapReplEnv()\"/>\n              <xsl:variable name=\"form\">\n                <value>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$form\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$venv\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:collapseReplEnv()))}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'atom'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"count($atoms/atom)\"/>\n              <value>\n                <malval kind=\"atom\" value=\"{$atom-ident}\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'deref'\">\n              <!-- needs access to atoms -->\n              <value>\n                <xsl:sequence select=\"$atoms/atom[@identity = $args/value/malval/lvalue/malval[1]/@value]/malval\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'reset!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"newv\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n              <value>\n                <xsl:sequence select=\"$newv\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'swap!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"atom-value\" select=\"$atoms/atom[@identity = $atom-ident]/malval\"/>\n              <xsl:variable name=\"fn\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"cargs\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:sequence select=\"$atom-value\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() &gt; 2]\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"newv\">\n                <xsl:call-template name=\"call-function\">\n                  <xsl:with-param name=\"func\" select=\"$fn\"/>\n                  <xsl:with-param name=\"args\" select=\"$cargs\"/>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:sequence select=\"$newv/value\"/>\n              <atoms>\n                <xsl:for-each select=\"$newv/atoms[1]/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv/value/malval\"/>\n                </atom>\n              </atoms>\n              <xsl:sequence select=\"$newv/env\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'apply'\">\n              <!-- needs access to env -->\n              <!-- (apply F A... T) -> (F A... T...) -->\n              <xsl:variable name=\"fun\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"args\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:variable name=\"argc\" select=\"count($args/value/malval/lvalue/malval)\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() gt 1 and position() lt $argc]\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[$argc]/lvalue/malval\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"./*\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"func\" select=\"$fun\"/>\n                    <xsl:with-param name=\"args\" select=\"$args\"/>\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/value\"/>\n              <xsl:sequence select=\"$res/env\"/>\n              <xsl:sequence select=\"$res/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'map'\">\n              <!-- needs access to env -->\n              <!-- (map F T) -> (list (F Tx)...) -->\n              <xsl:variable name=\"func\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:iterate select=\"$args/value/malval/lvalue/malval[2]/lvalue/malval\">\n                <xsl:param name=\"xenv\" select=\"$env\"/>\n                <xsl:param name=\"atoms\" select=\"$atoms\"/>\n                <xsl:param name=\"value\" select=\"()\"/>\n                <xsl:on-completion>\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\"$value\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                  <env data=\"{$env =&gt; env:serialise()}\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:on-completion>\n                <xsl:variable name=\"args\">\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\".\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                </xsl:variable>\n                <xsl:variable name=\"ctx\">\n                  <xsl:sequence select=\"./*\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <xsl:variable name=\"res\">\n                  <xsl:for-each select=\"$ctx\">\n                    <xsl:call-template name=\"call-function\">\n                      <xsl:with-param name=\"func\" select=\"$func\"/>\n                      <xsl:with-param name=\"args\" select=\"$args\"/>\n                      <xsl:with-param name=\"env\" select=\"$xenv\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:next-iteration>\n                  <xsl:with-param name=\"xenv\" select=\"env:deserialise($res/env/@data)\"/>\n                  <xsl:with-param name=\"atoms\" select=\"$res/atoms[1]\"/>\n                  <xsl:with-param name=\"value\" select=\"$value, $res/value/malval\"/>\n                </xsl:next-iteration>\n              </xsl:iterate>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'readline'\">\n              <!-- requires interaction with harness -->\n              <xsl:message>\n                <request kind=\"readline\" value=\"{$args/value/malval/lvalue/malval[1]/@value}\"/>\n              </xsl:message>\n              <xsl:variable name=\"str\" select=\"unparsed-text('xsl_input-string')\"/>\n              <xsl:sequence select=\"core:makeMALType($str, if (string-length($str) = 0) then 'nil' else 'string')\"/>\n              <xsl:sequence select=\"env\"/>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"core-apply\">\n                  <xsl:with-param name=\"func\" select=\"$func\"/>\n                  <xsl:with-param name=\"args\" select=\"$args\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$res/value\"/>\n    <xsl:choose>\n      <xsl:when test=\"$res/atoms\">\n        <xsl:sequence select=\"$res/atoms[1]\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"atoms\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n    <xsl:choose>\n      <xsl:when test=\"$res/env\">\n        <xsl:sequence select=\"$res/env\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <env data=\"{$env =&gt; env:serialise()}\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- uapply[env, fn, args] -->\n  <xsl:template name=\"uapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"nenv\" select=\"$env =&gt; env:hier(env:deserialise($func/malval/env/@data)) =&gt; env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)\"/>\n    <xsl:variable name=\"body\">\n      <value>\n        <xsl:sequence select=\"$func/malval/body/malval\"/>\n      </value>\n      <xsl:sequence select=\"atoms[1]\"/>\n    </xsl:variable>\n    <xsl:variable name=\"result\">\n      <xsl:for-each select=\"$body\">\n        <xsl:call-template name=\"EVAL\">\n          <xsl:with-param name=\"env\" select=\"$nenv\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$result/data/value\"/>\n    <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($result/env/@data) =&gt; env:replEnv()))}\"/>\n    <xsl:sequence select=\"$result/atoms[1]\"/>\n  </xsl:template>\n\n  <!-- Quasiquote: reduce/fold function, computing the new accumulator\n       value from the current element and the previous accumulator -->\n  <xsl:template name=\"qq_loop\">\n    <xsl:param name=\"elt\"/>\n    <xsl:param name=\"acc\"/>\n    <xsl:choose>\n      <xsl:when test=\"$elt/@kind                   = 'list'\n            and count($elt/lvalue/malval)          = 2\n            and       $elt/lvalue/malval[1]/@kind  = 'symbol'\n            and       $elt/lvalue/malval[1]/@value = 'splice-unquote'\">\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"concat\"/>\n            <xsl:sequence select=\"$elt/lvalue/malval[2]\"/>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"cons\"/>\n            <xsl:call-template name=\"quasiquote\">\n              <xsl:with-param name=\"ast\" select=\"$elt\"/>\n            </xsl:call-template>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- Quasiquote: right reduce/fold for an XML sequence -->\n  <xsl:template name=\"qq_foldr\">\n    <xsl:param name=\"xs\"/>\n    <xsl:choose>\n      <xsl:when test=\"count($xs) = 0\">\n        <malval kind=\"list\">\n          <lvalue/>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:call-template name=\"qq_loop\">\n          <xsl:with-param name=\"elt\" select=\"$xs[1]\"/>\n          <xsl:with-param name=\"acc\">\n            <xsl:call-template name=\"qq_foldr\">\n              <xsl:with-param name=\"xs\" select=\"$xs[position() != 1]\"/>\n            </xsl:call-template>\n          </xsl:with-param>\n        </xsl:call-template>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"quasiquote\">\n    <xsl:param name=\"ast\"/>\n    <xsl:variable name=\"result\">\n      <xsl:choose>\n        <xsl:when test=\"$ast/@kind = 'list'\">\n          <xsl:choose>\n            <xsl:when test=\"count($ast/lvalue/malval)          = 2\n                              and $ast/lvalue/malval[1]/@kind  = 'symbol'\n                              and $ast/lvalue/malval[1]/@value = 'unquote'\">\n              <xsl:sequence select=\"$ast/lvalue/malval[2]\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'vector'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"vec\"/>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'symbol' or $ast/@kind = 'hash'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"quote\"/>\n              <xsl:sequence select=\"$ast\"/>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:sequence select=\"$ast\"/>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$result\"/>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n    <!-- <xsl:message>\n\nEVALUATE <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> IN (<xsl:sequence select=\"empty(atoms)\"/>) ATOMS <xsl:sequence select=\"fn:pretty(atoms)\"/>\n\n</xsl:message> -->\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"atoms\" select=\"atoms[1]\"/>\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\n                        or value/malval/@kind = 'hash'\">\n          <xsl:variable name=\"myctx\">\n            <xsl:iterate select=\"value/malval/lvalue/malval\">\n              <xsl:param name=\"atoms\" select=\"$atoms\"/>\n              <xsl:param name=\"xctx\" select=\"()\"/>\n              <xsl:on-completion>\n                <xsl:sequence select=\"$xctx/value/malval\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:on-completion>\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"xctxy\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                  <xsl:sequence select=\"$val/atoms\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:next-iteration>\n                <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n              </xsl:next-iteration>\n            </xsl:iterate>\n          </xsl:variable>\n          <value>\n            <malval kind=\"{value/malval/@kind}\">\n              <lvalue>\n                <xsl:sequence select=\"$myctx/malval\"/>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:sequence select=\"$myctx/atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:sequence select=\"$atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"env:close($env)\"/>\n            <xsl:param name=\"new_atoms\" select=\"$atoms\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"xvalue\">\n                <xsl:sequence select=\"$xvalue/value\"/>\n                <xsl:sequence select=\"$new_atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:replEnv()))}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n              <xsl:sequence select=\"$new_atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n              <xsl:with-param name=\"new_atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'do'\">\n          <xsl:iterate select=\"value/malval/lvalue/malval[position() &gt; 1]\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:param name=\"atoms\" select=\"$atoms\"/>\n            <xsl:param name=\"previous_res\" select=\"()\"/>\n            <xsl:on-completion>\n              <xsl:sequence select=\"$previous_res\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($new_env)}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:deserialise($value/env/@data)\"/>\n              <xsl:with-param name=\"previous_res\" select=\"$value/data/value\"/>\n              <xsl:with-param name=\"atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'if'\">\n          <xsl:variable name=\"cond\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[2]\">\n              <xsl:variable name=\"context\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"ptrue\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[3]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"pfalse\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[4]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"xfalse\">\n            <xsl:choose>\n              <xsl:when test=\"empty($pfalse/value)\">\n                <value>\n                  <malval kind=\"nil\"/>\n                </value>\n                <xsl:sequence select=\"$cond/atoms[1]\"/>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"$pfalse/value\"/>\n                <xsl:sequence select=\"$pfalse/atoms[1]\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:choose>\n              <xsl:when test=\"let $kind := $cond/data/value/malval/@kind\n                              return $kind = 'nil' or $kind = 'false'\">\n                <xsl:for-each select=\"$xfalse\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:for-each select=\"$ptrue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'fn*'\">\n          <value>\n            <malval kind=\"userfunction\">\n              <binds>\n                <xsl:sequence select=\"value/malval/lvalue/malval[2]/lvalue/malval\"/>\n              </binds>\n              <body>\n                <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n              </body>\n              <env data=\"{env:serialise(env:noReplEnv($env))}\"/>\n              <!-- capture current env -->\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quote'\">\n          <value>\n            <xsl:sequence select=\"value/malval/lvalue/malval[2]\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quasiquote'\">\n          <xsl:variable name=\"exp\">\n            <value>\n              <xsl:call-template name=\"quasiquote\">\n                <xsl:with-param name=\"ast\" select=\"value/malval/lvalue/malval[2]\"/>\n              </xsl:call-template>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:for-each select=\"$exp\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n\n          <!-- evaluate the first element -->\n          <xsl:variable name=\"ctx\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[1]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:for-each select=\"$ctx\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n\n              <xsl:variable name=\"new_list\">\n                <xsl:iterate select=\"value/malval/lvalue/malval[position() != 1]\">\n                  <xsl:param name=\"atoms\" select=\"$func/atoms\"/>\n                  <xsl:param name=\"xctx\" select=\"()\"/>\n                  <xsl:on-completion>\n                    <xsl:sequence select=\"$xctx/value/malval\"/>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:on-completion>\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctxy\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                      <xsl:sequence select=\"$val/atoms\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:next-iteration>\n                    <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                    <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n                  </xsl:next-iteration>\n                </xsl:iterate>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$new_list/atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"resultv\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                    <xsl:with-param name=\"args\">\n                      <value>\n                        <malval kind=\"list\">\n                          <lvalue>\n                            <xsl:sequence select=\"$new_list/malval\"/>\n                          </lvalue>\n                        </malval>\n                      </value>\n                    </xsl:with-param>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:for-each select=\"$resultv\">\n                <xsl:choose>\n                  <xsl:when test=\"empty(env)\">\n                    <xsl:if test=\"$encode-env\">\n                      <env data=\"{env:serialise($env)}\"/>\n                    </xsl:if>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <xsl:if test=\"$encode-env\">\n                      <xsl:sequence select=\"env\"/>\n                    </xsl:if>\n                  </xsl:otherwise>\n                </xsl:choose>\n                <xsl:sequence select=\"atoms[1]\"/>\n                <xsl:sequence select=\"value\"/>\n              </xsl:for-each>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:variable name=\"new-atoms\" select=\"($data/atoms[1], $atoms)[1]\"/>\n    <!-- <xsl:message>\nEVALUATED (<xsl:sequence select=\"empty($data/atoms)\"/>) <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> TO <xsl:sequence select=\"core:pr-str($data/value/malval)/value/text()\"/> WITH ATOMS <xsl:sequence select=\"fn:pretty($new-atoms)\"/>\n      </xsl:message> -->\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n    <xsl:sequence select=\"$new-atoms\"/>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:sequence select=\"state/atoms[1]\"/>\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:sequence select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n  <xsl:function name=\"fn:pretty\">\n    <xsl:param name=\"atoms\"/>\n    <xsl:sequence select=\"$atoms/atom/concat('(', @identity, ': ', malval/@kind, ' ', core:pr-str(malval), ')')\"/>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step7_quote.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step7_quote.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step8_macros.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 8: Macros -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env and atoms </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:core=\"CORE\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env core\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:import href=\"core.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <xsl:choose>\n      <xsl:when test=\"string(state/env/@data) = ''\">\n        <xsl:variable name=\"argv\">\n          <malval kind=\"list\">\n            <lvalue>\n              <xsl:for-each select=\"argv/arg/text()\">\n                <malval kind=\"string\" value=\"{.}\"/>\n              </xsl:for-each>\n            </lvalue>\n          </malval>\n        </xsl:variable>\n        <xsl:variable name=\"vstate\">\n          <mal>\n            <state>\n              <env data=\"{env:serialise(env:empty() =&gt; env:bind-all(core:ns()/@name, core:ns()) =&gt; env:set('*ARGV*', $argv) =&gt; env:toReplEnv())}\"/>\n              <atoms/>\n            </state>\n            <stdin>(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) (defmacro! cond (fn* (&amp; xs) (if (&gt; (count xs) 0) (list 'if (first xs) (if (&gt; (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs))))))))</stdin>\n          </mal>\n        </xsl:variable>\n        <xsl:variable name=\"new-state\">\n          <xsl:for-each select=\"$vstate/mal\">\n            <xsl:call-template name=\"rep\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:variable name=\"state-v\">\n          <xsl:sequence select=\"$new-state/mal/state\"/>\n          <xsl:sequence select=\"stdin\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$state-v\">\n          <xsl:call-template name=\"rep\">\n            <xsl:with-param name=\"display\" select=\"$display\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <mal>\n          <xsl:variable name=\"env\" as=\"map(*)\">\n            <xsl:sequence select=\"env:deserialise(state/env/@data)\"/>\n          </xsl:variable>\n          <xsl:sequence select=\"stdin\"/>\n          <xsl:variable name=\"_read\">\n            <xsl:call-template name=\"READ\"/>\n          </xsl:variable>\n          <xsl:variable name=\"_eval\">\n            <xsl:for-each select=\"$_read\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$_eval\">\n            <xsl:variable name=\"_print\">\n              <xsl:variable name=\"data\">\n                <xsl:sequence select=\"data/value\"/>\n                <xsl:sequence select=\"atoms[1]\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$data\">\n                <xsl:call-template name=\"PRINT\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:if test=\"$display\">\n              <xsl:message>\n                <request kind=\"display\" value=\"{$_print}\"/>\n              </xsl:message>\n            </xsl:if>\n            <state>\n              <env data=\"{env/@data}\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n            </state>\n          </xsl:for-each>\n        </mal>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <xsl:template name=\"call-function\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"atoms\" select=\"atoms\"/>\n    <xsl:variable name=\"res\">\n      <xsl:choose>\n        <xsl:when test=\"$func/malval/@kind = 'userfunction'\">\n          <xsl:call-template name=\"uapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:choose>\n            <xsl:when test=\"$func/malval/@name = 'env??'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"nev\" select=\"env:dump($env)\"/>\n              <xsl:variable name=\"value\">\n                <malval kind=\"string\" value=\"{$env =&gt; serialize(map{'method':'json'})}\"/>\n              </xsl:variable>\n              <value>\n                <xsl:sequence select=\"$value\"/>\n              </value>\n              <env data=\"{env:serialise($env)}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'eval'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"venv\" select=\"env:replEnv($env) =&gt; env:wrapReplEnv()\"/>\n              <xsl:variable name=\"form\">\n                <value>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$form\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$venv\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:collapseReplEnv()))}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'atom'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"count($atoms/atom)\"/>\n              <value>\n                <malval kind=\"atom\" value=\"{$atom-ident}\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'deref'\">\n              <!-- needs access to atoms -->\n              <value>\n                <xsl:sequence select=\"$atoms/atom[@identity = $args/value/malval/lvalue/malval[1]/@value]/malval\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'reset!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"newv\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n              <value>\n                <xsl:sequence select=\"$newv\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'swap!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"atom-value\" select=\"$atoms/atom[@identity = $atom-ident]/malval\"/>\n              <xsl:variable name=\"fn\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"cargs\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:sequence select=\"$atom-value\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() &gt; 2]\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"newv\">\n                <xsl:call-template name=\"call-function\">\n                  <xsl:with-param name=\"func\" select=\"$fn\"/>\n                  <xsl:with-param name=\"args\" select=\"$cargs\"/>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:sequence select=\"$newv/value\"/>\n              <atoms>\n                <xsl:for-each select=\"$newv/atoms[1]/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv/value/malval\"/>\n                </atom>\n              </atoms>\n              <xsl:sequence select=\"$newv/env\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'apply'\">\n              <!-- needs access to env -->\n              <!-- (apply F A... T) -> (F A... T...) -->\n              <xsl:variable name=\"fun\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"args\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:variable name=\"argc\" select=\"count($args/value/malval/lvalue/malval)\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() gt 1 and position() lt $argc]\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[$argc]/lvalue/malval\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"./*\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"func\" select=\"$fun\"/>\n                    <xsl:with-param name=\"args\" select=\"$args\"/>\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/value\"/>\n              <xsl:sequence select=\"$res/env\"/>\n              <xsl:sequence select=\"$res/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'map'\">\n              <!-- needs access to env -->\n              <!-- (map F T) -> (list (F Tx)...) -->\n              <xsl:variable name=\"func\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:iterate select=\"$args/value/malval/lvalue/malval[2]/lvalue/malval\">\n                <xsl:param name=\"xenv\" select=\"$env\"/>\n                <xsl:param name=\"atoms\" select=\"$atoms\"/>\n                <xsl:param name=\"value\" select=\"()\"/>\n                <xsl:on-completion>\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\"$value\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                  <env data=\"{$env =&gt; env:serialise()}\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:on-completion>\n                <xsl:variable name=\"args\">\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\".\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                </xsl:variable>\n                <xsl:variable name=\"ctx\">\n                  <xsl:sequence select=\"./*\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <xsl:variable name=\"res\">\n                  <xsl:for-each select=\"$ctx\">\n                    <xsl:call-template name=\"call-function\">\n                      <xsl:with-param name=\"func\" select=\"$func\"/>\n                      <xsl:with-param name=\"args\" select=\"$args\"/>\n                      <xsl:with-param name=\"env\" select=\"$xenv\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:next-iteration>\n                  <xsl:with-param name=\"xenv\" select=\"env:deserialise($res/env/@data)\"/>\n                  <xsl:with-param name=\"atoms\" select=\"$res/atoms[1]\"/>\n                  <xsl:with-param name=\"value\" select=\"$value, $res/value/malval\"/>\n                </xsl:next-iteration>\n              </xsl:iterate>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'readline'\">\n              <!-- requires interaction with harness -->\n              <xsl:message>\n                <request kind=\"readline\" value=\"{$args/value/malval/lvalue/malval[1]/@value}\"/>\n              </xsl:message>\n              <xsl:variable name=\"str\" select=\"unparsed-text('xsl_input-string')\"/>\n              <xsl:sequence select=\"core:makeMALType($str, if (string-length($str) = 0) then 'nil' else 'string')\"/>\n              <xsl:sequence select=\"env\"/>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"core-apply\">\n                  <xsl:with-param name=\"func\" select=\"$func\"/>\n                  <xsl:with-param name=\"args\" select=\"$args\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$res/value\"/>\n    <xsl:choose>\n      <xsl:when test=\"$res/atoms\">\n        <xsl:sequence select=\"$res/atoms[1]\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"atoms\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n    <xsl:choose>\n      <xsl:when test=\"$res/env\">\n        <xsl:sequence select=\"$res/env\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <env data=\"{$env =&gt; env:serialise()}\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- uapply[env, fn, args] -->\n  <xsl:template name=\"uapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"nenv\" select=\"$env =&gt; env:hier(env:deserialise($func/malval/env/@data)) =&gt; env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)\"/>\n    <xsl:variable name=\"body\">\n      <value>\n        <xsl:sequence select=\"$func/malval/body/malval\"/>\n      </value>\n      <xsl:sequence select=\"atoms[1]\"/>\n    </xsl:variable>\n    <xsl:variable name=\"result\">\n      <xsl:for-each select=\"$body\">\n        <xsl:call-template name=\"EVAL\">\n          <xsl:with-param name=\"env\" select=\"$nenv\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$result/data/value\"/>\n    <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($result/env/@data) =&gt; env:replEnv()))}\"/>\n    <xsl:sequence select=\"$result/atoms[1]\"/>\n  </xsl:template>\n\n  <!-- Quasiquote: reduce/fold function, computing the new accumulator\n       value from the current element and the previous accumulator -->\n  <xsl:template name=\"qq_loop\">\n    <xsl:param name=\"elt\"/>\n    <xsl:param name=\"acc\"/>\n    <xsl:choose>\n      <xsl:when test=\"$elt/@kind                   = 'list'\n            and count($elt/lvalue/malval)          = 2\n            and       $elt/lvalue/malval[1]/@kind  = 'symbol'\n            and       $elt/lvalue/malval[1]/@value = 'splice-unquote'\">\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"concat\"/>\n            <xsl:sequence select=\"$elt/lvalue/malval[2]\"/>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"cons\"/>\n            <xsl:call-template name=\"quasiquote\">\n              <xsl:with-param name=\"ast\" select=\"$elt\"/>\n            </xsl:call-template>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- Quasiquote: right reduce/fold for an XML sequence -->\n  <xsl:template name=\"qq_foldr\">\n    <xsl:param name=\"xs\"/>\n    <xsl:choose>\n      <xsl:when test=\"count($xs) = 0\">\n        <malval kind=\"list\">\n          <lvalue/>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:call-template name=\"qq_loop\">\n          <xsl:with-param name=\"elt\" select=\"$xs[1]\"/>\n          <xsl:with-param name=\"acc\">\n            <xsl:call-template name=\"qq_foldr\">\n              <xsl:with-param name=\"xs\" select=\"$xs[position() != 1]\"/>\n            </xsl:call-template>\n          </xsl:with-param>\n        </xsl:call-template>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"quasiquote\">\n    <xsl:param name=\"ast\"/>\n    <xsl:variable name=\"result\">\n      <xsl:choose>\n        <xsl:when test=\"$ast/@kind = 'list'\">\n          <xsl:choose>\n            <xsl:when test=\"count($ast/lvalue/malval)          = 2\n                              and $ast/lvalue/malval[1]/@kind  = 'symbol'\n                              and $ast/lvalue/malval[1]/@value = 'unquote'\">\n              <xsl:sequence select=\"$ast/lvalue/malval[2]\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'vector'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"vec\"/>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'symbol' or $ast/@kind = 'hash'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"quote\"/>\n              <xsl:sequence select=\"$ast\"/>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:sequence select=\"$ast\"/>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$result\"/>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n    <!-- <xsl:message>\n\nEVALUATE <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> IN (<xsl:sequence select=\"empty(atoms)\"/>) ATOMS <xsl:sequence select=\"fn:pretty(atoms)\"/>\n\n</xsl:message> -->\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"atoms\" select=\"atoms[1]\"/>\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\n                        or value/malval/@kind = 'hash'\">\n          <xsl:variable name=\"myctx\">\n            <xsl:iterate select=\"value/malval/lvalue/malval\">\n              <xsl:param name=\"atoms\" select=\"$atoms\"/>\n              <xsl:param name=\"xctx\" select=\"()\"/>\n              <xsl:on-completion>\n                <xsl:sequence select=\"$xctx/value/malval\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:on-completion>\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"xctxy\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                  <xsl:sequence select=\"$val/atoms\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:next-iteration>\n                <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n              </xsl:next-iteration>\n            </xsl:iterate>\n          </xsl:variable>\n          <value>\n            <malval kind=\"{value/malval/@kind}\">\n              <lvalue>\n                <xsl:sequence select=\"$myctx/malval\"/>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:sequence select=\"$myctx/atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:sequence select=\"$atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'defmacro!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"resv\">\n            <value>\n              <malval kind=\"{$value/data/value/malval/@kind}\" value=\"{$value/data/value/malval/@value}\">\n                <xsl:sequence select=\"$value/data/value/malval/*[name() != 'is_macro']\"/>\n                <is_macro>true</is_macro>\n              </malval>\n            </value>\n          </xsl:variable>\n          <xsl:sequence select=\"$resv\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $resv/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"env:close($env)\"/>\n            <xsl:param name=\"new_atoms\" select=\"$atoms\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"xvalue\">\n                <xsl:sequence select=\"$xvalue/value\"/>\n                <xsl:sequence select=\"$new_atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:replEnv()))}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n              <xsl:sequence select=\"$new_atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n              <xsl:with-param name=\"new_atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'do'\">\n          <xsl:iterate select=\"value/malval/lvalue/malval[position() &gt; 1]\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:param name=\"atoms\" select=\"$atoms\"/>\n            <xsl:param name=\"previous_res\" select=\"()\"/>\n            <xsl:on-completion>\n              <xsl:sequence select=\"$previous_res\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($new_env)}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:deserialise($value/env/@data)\"/>\n              <xsl:with-param name=\"previous_res\" select=\"$value/data/value\"/>\n              <xsl:with-param name=\"atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'if'\">\n          <xsl:variable name=\"cond\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[2]\">\n              <xsl:variable name=\"context\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"ptrue\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[3]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"pfalse\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[4]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"xfalse\">\n            <xsl:choose>\n              <xsl:when test=\"empty($pfalse/value)\">\n                <value>\n                  <malval kind=\"nil\"/>\n                </value>\n                <xsl:sequence select=\"$cond/atoms[1]\"/>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"$pfalse/value\"/>\n                <xsl:sequence select=\"$pfalse/atoms[1]\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:choose>\n              <xsl:when test=\"let $kind := $cond/data/value/malval/@kind\n                              return $kind = 'nil' or $kind = 'false'\">\n                <xsl:for-each select=\"$xfalse\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:for-each select=\"$ptrue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'fn*'\">\n          <value>\n            <malval kind=\"userfunction\">\n              <is_macro>false</is_macro>\n              <binds>\n                <xsl:sequence select=\"value/malval/lvalue/malval[2]/lvalue/malval\"/>\n              </binds>\n              <body>\n                <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n              </body>\n              <env data=\"{env:serialise(env:noReplEnv($env))}\"/>\n              <!-- capture current env -->\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quote'\">\n          <value>\n            <xsl:sequence select=\"value/malval/lvalue/malval[2]\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quasiquote'\">\n          <xsl:variable name=\"exp\">\n            <value>\n              <xsl:call-template name=\"quasiquote\">\n                <xsl:with-param name=\"ast\" select=\"value/malval/lvalue/malval[2]\"/>\n              </xsl:call-template>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:for-each select=\"$exp\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n\n          <!-- evaluate the first element -->\n          <xsl:variable name=\"ctx\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[1]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:for-each select=\"$ctx\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:choose>\n\n            <!-- Macro -->\n            <xsl:when test=\"$func/data/value/malval/is_macro/text() = 'true'\">\n              <xsl:variable name=\"new\">\n                <xsl:call-template name=\"uapply\">\n                  <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                  <xsl:with-param name=\"args\">\n                    <value>\n                      <malval kind=\"list\">\n                        <lvalue>\n                          <xsl:sequence select=\"value/malval/lvalue/malval[position() != 1]\"/>\n                        </lvalue>\n                      </malval>\n                    </value>\n                  </xsl:with-param>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$new\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/data/value\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{$env =&gt; env:serialise()}\"/>\n              </xsl:if>\n            </xsl:when>\n\n            <!-- Function -->\n            <xsl:otherwise>\n              <xsl:variable name=\"new_list\">\n                <xsl:iterate select=\"value/malval/lvalue/malval[position() != 1]\">\n                  <xsl:param name=\"atoms\" select=\"$func/atoms\"/>\n                  <xsl:param name=\"xctx\" select=\"()\"/>\n                  <xsl:on-completion>\n                    <xsl:sequence select=\"$xctx/value/malval\"/>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:on-completion>\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctxy\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                      <xsl:sequence select=\"$val/atoms\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:next-iteration>\n                    <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                    <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n                  </xsl:next-iteration>\n                </xsl:iterate>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$new_list/atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"resultv\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                    <xsl:with-param name=\"args\">\n                      <value>\n                        <malval kind=\"list\">\n                          <lvalue>\n                            <xsl:sequence select=\"$new_list/malval\"/>\n                          </lvalue>\n                        </malval>\n                      </value>\n                    </xsl:with-param>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:for-each select=\"$resultv\">\n                <xsl:choose>\n                  <xsl:when test=\"empty(env)\">\n                    <xsl:if test=\"$encode-env\">\n                      <env data=\"{env:serialise($env)}\"/>\n                    </xsl:if>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <xsl:if test=\"$encode-env\">\n                      <xsl:sequence select=\"env\"/>\n                    </xsl:if>\n                  </xsl:otherwise>\n                </xsl:choose>\n                <xsl:sequence select=\"atoms[1]\"/>\n                <xsl:sequence select=\"value\"/>\n              </xsl:for-each>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:variable name=\"new-atoms\" select=\"($data/atoms[1], $atoms)[1]\"/>\n    <!-- <xsl:message>\nEVALUATED (<xsl:sequence select=\"empty($data/atoms)\"/>) <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> TO <xsl:sequence select=\"core:pr-str($data/value/malval)/value/text()\"/> WITH ATOMS <xsl:sequence select=\"fn:pretty($new-atoms)\"/>\n      </xsl:message> -->\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n    <xsl:sequence select=\"$new-atoms\"/>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:sequence select=\"state/atoms[1]\"/>\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:sequence select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n  <xsl:function name=\"fn:pretty\">\n    <xsl:param name=\"atoms\"/>\n    <xsl:sequence select=\"$atoms/atom/concat('(', @identity, ': ', malval/@kind, ' ', core:pr-str(malval), ')')\"/>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step8_macros.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step8_macros.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step9_try.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step 9: Try -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env and atoms </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:core=\"CORE\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    xmlns:err=\"http://www.w3.org/2005/xqt-errors\"\n    xmlns:MAL=\"MAL\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env core err MAL\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:import href=\"core.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <xsl:choose>\n      <xsl:when test=\"string(state/env/@data) = ''\">\n        <xsl:variable name=\"argv\">\n          <malval kind=\"list\">\n            <lvalue>\n              <xsl:for-each select=\"argv/arg/text()\">\n                <malval kind=\"string\" value=\"{.}\"/>\n              </xsl:for-each>\n            </lvalue>\n          </malval>\n        </xsl:variable>\n        <xsl:variable name=\"vstate\">\n          <mal>\n            <state>\n              <env data=\"{env:serialise(env:empty() =&gt; env:bind-all(core:ns()/@name, core:ns()) =&gt; env:set('*ARGV*', $argv) =&gt; env:toReplEnv())}\"/>\n              <atoms/>\n            </state>\n            <stdin>(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) (defmacro! cond (fn* (&amp; xs) (if (&gt; (count xs) 0) (list 'if (first xs) (if (&gt; (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs))))))))</stdin>\n          </mal>\n        </xsl:variable>\n        <xsl:variable name=\"new-state\">\n          <xsl:for-each select=\"$vstate/mal\">\n            <xsl:call-template name=\"rep\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:variable name=\"state-v\">\n          <xsl:sequence select=\"$new-state/mal/state\"/>\n          <xsl:sequence select=\"stdin\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$state-v\">\n          <xsl:call-template name=\"rep\">\n            <xsl:with-param name=\"display\" select=\"$display\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <mal>\n          <xsl:variable name=\"env\" as=\"map(*)\">\n            <xsl:sequence select=\"env:deserialise(state/env/@data)\"/>\n          </xsl:variable>\n          <xsl:sequence select=\"stdin\"/>\n          <xsl:variable name=\"_read\">\n            <xsl:call-template name=\"READ\"/>\n          </xsl:variable>\n          <xsl:variable name=\"_eval\">\n            <xsl:for-each select=\"$_read\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$_eval\">\n            <xsl:variable name=\"_print\">\n              <xsl:variable name=\"data\">\n                <xsl:sequence select=\"data/value\"/>\n                <xsl:sequence select=\"atoms[1]\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$data\">\n                <xsl:call-template name=\"PRINT\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:if test=\"$display\">\n              <xsl:message>\n                <request kind=\"display\" value=\"{$_print}\"/>\n              </xsl:message>\n            </xsl:if>\n            <state>\n              <env data=\"{env/@data}\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n            </state>\n          </xsl:for-each>\n        </mal>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <xsl:template name=\"call-function\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"atoms\" select=\"atoms\"/>\n    <xsl:variable name=\"res\">\n      <xsl:choose>\n        <xsl:when test=\"$func/malval/@kind = 'userfunction'\">\n          <xsl:call-template name=\"uapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:choose>\n            <xsl:when test=\"$func/malval/@name = 'env??'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"nev\" select=\"env:dump($env)\"/>\n              <xsl:variable name=\"value\">\n                <malval kind=\"string\" value=\"{$env =&gt; serialize(map{'method':'json'})}\"/>\n              </xsl:variable>\n              <value>\n                <xsl:sequence select=\"$value\"/>\n              </value>\n              <env data=\"{env:serialise($env)}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'eval'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"venv\" select=\"env:replEnv($env) =&gt; env:wrapReplEnv()\"/>\n              <xsl:variable name=\"form\">\n                <value>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$form\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$venv\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:collapseReplEnv()))}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'atom'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"count($atoms/atom)\"/>\n              <value>\n                <malval kind=\"atom\" value=\"{$atom-ident}\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'deref'\">\n              <!-- needs access to atoms -->\n              <value>\n                <xsl:sequence select=\"$atoms/atom[@identity = $args/value/malval/lvalue/malval[1]/@value]/malval\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'reset!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"newv\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n              <value>\n                <xsl:sequence select=\"$newv\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'swap!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"atom-value\" select=\"$atoms/atom[@identity = $atom-ident]/malval\"/>\n              <xsl:variable name=\"fn\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"cargs\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:sequence select=\"$atom-value\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() &gt; 2]\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"newv\">\n                <xsl:call-template name=\"call-function\">\n                  <xsl:with-param name=\"func\" select=\"$fn\"/>\n                  <xsl:with-param name=\"args\" select=\"$cargs\"/>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:sequence select=\"$newv/value\"/>\n              <atoms>\n                <xsl:for-each select=\"$newv/atoms[1]/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv/value/malval\"/>\n                </atom>\n              </atoms>\n              <xsl:sequence select=\"$newv/env\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'apply'\">\n              <!-- needs access to env -->\n              <!-- (apply F A... T) -> (F A... T...) -->\n              <xsl:variable name=\"fun\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"args\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:variable name=\"argc\" select=\"count($args/value/malval/lvalue/malval)\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() gt 1 and position() lt $argc]\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[$argc]/lvalue/malval\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"./*\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"func\" select=\"$fun\"/>\n                    <xsl:with-param name=\"args\" select=\"$args\"/>\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/value\"/>\n              <xsl:sequence select=\"$res/env\"/>\n              <xsl:sequence select=\"$res/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'map'\">\n              <!-- needs access to env -->\n              <!-- (map F T) -> (list (F Tx)...) -->\n              <xsl:variable name=\"func\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:iterate select=\"$args/value/malval/lvalue/malval[2]/lvalue/malval\">\n                <xsl:param name=\"xenv\" select=\"$env\"/>\n                <xsl:param name=\"atoms\" select=\"$atoms\"/>\n                <xsl:param name=\"value\" select=\"()\"/>\n                <xsl:on-completion>\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\"$value\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                  <env data=\"{$env =&gt; env:serialise()}\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:on-completion>\n                <xsl:variable name=\"args\">\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\".\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                </xsl:variable>\n                <xsl:variable name=\"ctx\">\n                  <xsl:sequence select=\"./*\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <xsl:variable name=\"res\">\n                  <xsl:for-each select=\"$ctx\">\n                    <xsl:call-template name=\"call-function\">\n                      <xsl:with-param name=\"func\" select=\"$func\"/>\n                      <xsl:with-param name=\"args\" select=\"$args\"/>\n                      <xsl:with-param name=\"env\" select=\"$xenv\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:next-iteration>\n                  <xsl:with-param name=\"xenv\" select=\"env:deserialise($res/env/@data)\"/>\n                  <xsl:with-param name=\"atoms\" select=\"$res/atoms[1]\"/>\n                  <xsl:with-param name=\"value\" select=\"$value, $res/value/malval\"/>\n                </xsl:next-iteration>\n              </xsl:iterate>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'readline'\">\n              <!-- requires interaction with harness -->\n              <xsl:message>\n                <request kind=\"readline\" value=\"{$args/value/malval/lvalue/malval[1]/@value}\"/>\n              </xsl:message>\n              <xsl:variable name=\"str\" select=\"unparsed-text('xsl_input-string')\"/>\n              <xsl:sequence select=\"core:makeMALType($str, if (string-length($str) = 0) then 'nil' else 'string')\"/>\n              <xsl:sequence select=\"env\"/>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"core-apply\">\n                  <xsl:with-param name=\"func\" select=\"$func\"/>\n                  <xsl:with-param name=\"args\" select=\"$args\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$res/value\"/>\n    <xsl:choose>\n      <xsl:when test=\"$res/atoms\">\n        <xsl:sequence select=\"$res/atoms[1]\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"atoms\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n    <xsl:choose>\n      <xsl:when test=\"$res/env\">\n        <xsl:sequence select=\"$res/env\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <env data=\"{$env =&gt; env:serialise()}\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- uapply[env, fn, args] -->\n  <xsl:template name=\"uapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"nenv\" select=\"$env =&gt; env:hier(env:deserialise($func/malval/env/@data)) =&gt; env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)\"/>\n    <xsl:variable name=\"body\">\n      <value>\n        <xsl:sequence select=\"$func/malval/body/malval\"/>\n      </value>\n      <xsl:sequence select=\"atoms[1]\"/>\n    </xsl:variable>\n    <xsl:variable name=\"result\">\n      <xsl:for-each select=\"$body\">\n        <xsl:call-template name=\"EVAL\">\n          <xsl:with-param name=\"env\" select=\"$nenv\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$result/data/value\"/>\n    <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($result/env/@data) =&gt; env:replEnv()))}\"/>\n    <xsl:sequence select=\"$result/atoms[1]\"/>\n  </xsl:template>\n\n  <!-- Quasiquote: reduce/fold function, computing the new accumulator\n       value from the current element and the previous accumulator -->\n  <xsl:template name=\"qq_loop\">\n    <xsl:param name=\"elt\"/>\n    <xsl:param name=\"acc\"/>\n    <xsl:choose>\n      <xsl:when test=\"$elt/@kind                   = 'list'\n            and count($elt/lvalue/malval)          = 2\n            and       $elt/lvalue/malval[1]/@kind  = 'symbol'\n            and       $elt/lvalue/malval[1]/@value = 'splice-unquote'\">\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"concat\"/>\n            <xsl:sequence select=\"$elt/lvalue/malval[2]\"/>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"cons\"/>\n            <xsl:call-template name=\"quasiquote\">\n              <xsl:with-param name=\"ast\" select=\"$elt\"/>\n            </xsl:call-template>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- Quasiquote: right reduce/fold for an XML sequence -->\n  <xsl:template name=\"qq_foldr\">\n    <xsl:param name=\"xs\"/>\n    <xsl:choose>\n      <xsl:when test=\"count($xs) = 0\">\n        <malval kind=\"list\">\n          <lvalue/>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:call-template name=\"qq_loop\">\n          <xsl:with-param name=\"elt\" select=\"$xs[1]\"/>\n          <xsl:with-param name=\"acc\">\n            <xsl:call-template name=\"qq_foldr\">\n              <xsl:with-param name=\"xs\" select=\"$xs[position() != 1]\"/>\n            </xsl:call-template>\n          </xsl:with-param>\n        </xsl:call-template>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"quasiquote\">\n    <xsl:param name=\"ast\"/>\n    <xsl:variable name=\"result\">\n      <xsl:choose>\n        <xsl:when test=\"$ast/@kind = 'list'\">\n          <xsl:choose>\n            <xsl:when test=\"count($ast/lvalue/malval)          = 2\n                              and $ast/lvalue/malval[1]/@kind  = 'symbol'\n                              and $ast/lvalue/malval[1]/@value = 'unquote'\">\n              <xsl:sequence select=\"$ast/lvalue/malval[2]\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'vector'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"vec\"/>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'symbol' or $ast/@kind = 'hash'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"quote\"/>\n              <xsl:sequence select=\"$ast\"/>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:sequence select=\"$ast\"/>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$result\"/>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n    <!-- <xsl:message>\n\nEVALUATE <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> IN (<xsl:sequence select=\"empty(atoms)\"/>) ATOMS <xsl:sequence select=\"fn:pretty(atoms)\"/>\n\n</xsl:message> -->\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"atoms\" select=\"atoms[1]\"/>\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\n                        or value/malval/@kind = 'hash'\">\n          <xsl:variable name=\"myctx\">\n            <xsl:iterate select=\"value/malval/lvalue/malval\">\n              <xsl:param name=\"atoms\" select=\"$atoms\"/>\n              <xsl:param name=\"xctx\" select=\"()\"/>\n              <xsl:on-completion>\n                <xsl:sequence select=\"$xctx/value/malval\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:on-completion>\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"xctxy\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                  <xsl:sequence select=\"$val/atoms\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:next-iteration>\n                <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n              </xsl:next-iteration>\n            </xsl:iterate>\n          </xsl:variable>\n          <value>\n            <malval kind=\"{value/malval/@kind}\">\n              <lvalue>\n                <xsl:sequence select=\"$myctx/malval\"/>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:sequence select=\"$myctx/atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:sequence select=\"$atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'defmacro!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"resv\">\n            <value>\n              <malval kind=\"{$value/data/value/malval/@kind}\" value=\"{$value/data/value/malval/@value}\">\n                <xsl:sequence select=\"$value/data/value/malval/*[name() != 'is_macro']\"/>\n                <is_macro>true</is_macro>\n              </malval>\n            </value>\n          </xsl:variable>\n          <xsl:sequence select=\"$resv\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $resv/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"env:close($env)\"/>\n            <xsl:param name=\"new_atoms\" select=\"$atoms\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"xvalue\">\n                <xsl:sequence select=\"$xvalue/value\"/>\n                <xsl:sequence select=\"$new_atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:replEnv()))}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n              <xsl:sequence select=\"$new_atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n              <xsl:with-param name=\"new_atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'do'\">\n          <xsl:iterate select=\"value/malval/lvalue/malval[position() &gt; 1]\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:param name=\"atoms\" select=\"$atoms\"/>\n            <xsl:param name=\"previous_res\" select=\"()\"/>\n            <xsl:on-completion>\n              <xsl:sequence select=\"$previous_res\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($new_env)}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:deserialise($value/env/@data)\"/>\n              <xsl:with-param name=\"previous_res\" select=\"$value/data/value\"/>\n              <xsl:with-param name=\"atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'if'\">\n          <xsl:variable name=\"cond\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[2]\">\n              <xsl:variable name=\"context\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"ptrue\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[3]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"pfalse\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[4]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"xfalse\">\n            <xsl:choose>\n              <xsl:when test=\"empty($pfalse/value)\">\n                <value>\n                  <malval kind=\"nil\"/>\n                </value>\n                <xsl:sequence select=\"$cond/atoms[1]\"/>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"$pfalse/value\"/>\n                <xsl:sequence select=\"$pfalse/atoms[1]\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:choose>\n              <xsl:when test=\"let $kind := $cond/data/value/malval/@kind\n                              return $kind = 'nil' or $kind = 'false'\">\n                <xsl:for-each select=\"$xfalse\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:for-each select=\"$ptrue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'fn*'\">\n          <value>\n            <malval kind=\"userfunction\">\n              <is_macro>false</is_macro>\n              <binds>\n                <xsl:sequence select=\"value/malval/lvalue/malval[2]/lvalue/malval\"/>\n              </binds>\n              <body>\n                <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n              </body>\n              <env data=\"{env:serialise(env:noReplEnv($env))}\"/>\n              <!-- capture current env -->\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quote'\">\n          <value>\n            <xsl:sequence select=\"value/malval/lvalue/malval[2]\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quasiquote'\">\n          <xsl:variable name=\"exp\">\n            <value>\n              <xsl:call-template name=\"quasiquote\">\n                <xsl:with-param name=\"ast\" select=\"value/malval/lvalue/malval[2]\"/>\n              </xsl:call-template>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:for-each select=\"$exp\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'try*'\">\n          <xsl:variable name=\"tryv\">\n            <xsl:try>\n              <xsl:variable name=\"xvalue\">\n                <value>\n                  <xsl:sequence select=\"value/malval/lvalue/malval[2]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <xsl:sequence select=\"$value/env\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n              <xsl:catch errors=\"*\">\n                <xsl:variable name=\"catchv-name\" select=\"value/malval/lvalue/malval[3]/lvalue/malval[2]/@value\"/>\n                <xsl:variable name=\"catchv\">\n                  <value>\n                    <xsl:sequence select=\"value/malval/lvalue/malval[3]/lvalue/malval[3]\"/>\n                  </value>\n                </xsl:variable>\n                <xsl:if test=\"empty($catchv/value/malval)\">\n                  <xsl:value-of select=\"error($err:code, $err:description, $err:value)\"/>\n                </xsl:if>\n                <xsl:variable name=\"newenv\" select=\"env:close($env) =&gt; env:set($catchv-name, $err:value)\"/>\n                <xsl:variable name=\"value\">\n                  <xsl:for-each select=\"$catchv\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$newenv\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:sequence select=\"$value/data/value\"/>\n                <xsl:if test=\"$encode-env\">\n                  <xsl:sequence select=\"$value/env\"/>\n                  <!-- leaks the bound name, ouch -->\n                </xsl:if>\n                <xsl:sequence select=\"$value/atoms[1]\"/>\n              </xsl:catch>\n            </xsl:try>\n          </xsl:variable>\n          <xsl:sequence select=\"$tryv\"/>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n\n          <!-- evaluate the first element -->\n          <xsl:variable name=\"ctx\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[1]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:for-each select=\"$ctx\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:choose>\n\n            <!-- Macro -->\n            <xsl:when test=\"$func/data/value/malval/is_macro/text() = 'true'\">\n              <xsl:variable name=\"new\">\n                <xsl:call-template name=\"uapply\">\n                  <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                  <xsl:with-param name=\"args\">\n                    <value>\n                      <malval kind=\"list\">\n                        <lvalue>\n                          <xsl:sequence select=\"value/malval/lvalue/malval[position() != 1]\"/>\n                        </lvalue>\n                      </malval>\n                    </value>\n                  </xsl:with-param>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$new\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/data/value\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{$env =&gt; env:serialise()}\"/>\n              </xsl:if>\n            </xsl:when>\n\n            <!-- Function -->\n            <xsl:otherwise>\n              <xsl:variable name=\"new_list\">\n                <xsl:iterate select=\"value/malval/lvalue/malval[position() != 1]\">\n                  <xsl:param name=\"atoms\" select=\"$func/atoms\"/>\n                  <xsl:param name=\"xctx\" select=\"()\"/>\n                  <xsl:on-completion>\n                    <xsl:sequence select=\"$xctx/value/malval\"/>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:on-completion>\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctxy\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                      <xsl:sequence select=\"$val/atoms\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:next-iteration>\n                    <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                    <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n                  </xsl:next-iteration>\n                </xsl:iterate>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$new_list/atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"resultv\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                    <xsl:with-param name=\"args\">\n                      <value>\n                        <malval kind=\"list\">\n                          <lvalue>\n                            <xsl:sequence select=\"$new_list/malval\"/>\n                          </lvalue>\n                        </malval>\n                      </value>\n                    </xsl:with-param>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:for-each select=\"$resultv\">\n                <xsl:choose>\n                  <xsl:when test=\"empty(env)\">\n                    <xsl:if test=\"$encode-env\">\n                      <env data=\"{env:serialise($env)}\"/>\n                    </xsl:if>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <xsl:if test=\"$encode-env\">\n                      <xsl:sequence select=\"env\"/>\n                    </xsl:if>\n                  </xsl:otherwise>\n                </xsl:choose>\n                <xsl:sequence select=\"atoms[1]\"/>\n                <xsl:sequence select=\"value\"/>\n              </xsl:for-each>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:variable name=\"new-atoms\" select=\"($data/atoms[1], $atoms)[1]\"/>\n    <!-- <xsl:message>\nEVALUATED (<xsl:sequence select=\"empty($data/atoms)\"/>) <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> TO <xsl:sequence select=\"core:pr-str($data/value/malval)/value/text()\"/> WITH ATOMS <xsl:sequence select=\"fn:pretty($new-atoms)\"/>\n      </xsl:message> -->\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n    <xsl:sequence select=\"$new-atoms\"/>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:sequence select=\"state/atoms[1]\"/>\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:sequence select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n  <xsl:function name=\"fn:pretty\">\n    <xsl:param name=\"atoms\"/>\n    <xsl:sequence select=\"$atoms/atom/concat('(', @identity, ': ', malval/@kind, ' ', core:pr-str(malval), ')')\"/>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/step9_try.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"step9_try.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/stepA_mal.inc.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Step A: MAL -->\n<!-- input document must be in the following format -->\n<!--\n<mal>\n    <stdin>...stdin text...</stdin>\n    <stdout> ... ignored, omitted ... </stdout>\n    <state> contains env and atoms </state>\n</mal>\n-->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    xmlns:fn=\"http://www.w3.org/2005/02/xpath-functions\"\n    xmlns:env=\"ENV\"\n    xmlns:core=\"CORE\"\n    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n    xmlns:map=\"http://www.w3.org/2005/xpath-functions/map\"\n    xmlns:err=\"http://www.w3.org/2005/xqt-errors\"\n    xmlns:MAL=\"MAL\"\n    version=\"3.0\"\n    exclude-result-prefixes=\"fn xs map env core err MAL\">\n  <xsl:import href=\"reader.xslt\"/>\n  <xsl:import href=\"printer.xslt\"/>\n  <xsl:import href=\"env.xslt\"/>\n  <xsl:import href=\"core.xslt\"/>\n  <xsl:output method=\"xml\" encoding=\"utf-8\" indent=\"yes\"/>\n\n  <xsl:template match=\"mal\" name=\"rep\">\n    <xsl:param name=\"display\" select=\"false()\" />\n    <xsl:choose>\n      <xsl:when test=\"string(state/env/@data) = ''\">\n        <xsl:variable name=\"argv\">\n          <malval kind=\"list\">\n            <lvalue>\n              <xsl:for-each select=\"argv/arg/text()\">\n                <malval kind=\"string\" value=\"{.}\"/>\n              </xsl:for-each>\n            </lvalue>\n          </malval>\n        </xsl:variable>\n        <xsl:variable name=\"vstate\">\n          <mal>\n            <state>\n              <env data=\"{env:serialise(env:empty() =&gt; env:bind-all(core:ns()/@name, core:ns()) =&gt; env:set('*ARGV*', $argv) =&gt; env:toReplEnv())}\"/>\n              <atoms/>\n            </state>\n            <stdin>(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\"))))) (defmacro! cond (fn* (&amp; xs) (if (&gt; (count xs) 0) (list 'if (first xs) (if (&gt; (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs))))))) (def! *host-language* \"XSLT\"))</stdin>\n          </mal>\n        </xsl:variable>\n        <xsl:variable name=\"new-state\">\n          <xsl:for-each select=\"$vstate/mal\">\n            <xsl:call-template name=\"rep\"/>\n          </xsl:for-each>\n        </xsl:variable>\n        <xsl:variable name=\"state-v\">\n          <xsl:sequence select=\"$new-state/mal/state\"/>\n          <xsl:sequence select=\"stdin\"/>\n        </xsl:variable>\n        <xsl:for-each select=\"$state-v\">\n          <xsl:call-template name=\"rep\">\n            <xsl:with-param name=\"display\" select=\"$display\"/>\n          </xsl:call-template>\n        </xsl:for-each>\n      </xsl:when>\n      <xsl:otherwise>\n        <mal>\n          <xsl:variable name=\"env\" as=\"map(*)\">\n            <xsl:sequence select=\"env:deserialise(state/env/@data)\"/>\n          </xsl:variable>\n          <xsl:sequence select=\"stdin\"/>\n          <xsl:variable name=\"_read\">\n            <xsl:call-template name=\"READ\"/>\n          </xsl:variable>\n          <xsl:variable name=\"_eval\">\n            <xsl:for-each select=\"$_read\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:for-each select=\"$_eval\">\n            <xsl:variable name=\"_print\">\n              <xsl:variable name=\"data\">\n                <xsl:sequence select=\"data/value\"/>\n                <xsl:sequence select=\"atoms[1]\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$data\">\n                <xsl:call-template name=\"PRINT\"/>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:if test=\"$display\">\n              <xsl:message>\n                <request kind=\"display\" value=\"{$_print}\"/>\n              </xsl:message>\n            </xsl:if>\n            <state>\n              <env data=\"{env/@data}\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n            </state>\n          </xsl:for-each>\n        </mal>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"PRINT\">\n    <xsl:variable name=\"str\">\n      <xsl:call-template name=\"malprinter-pr_str\">\n        <xsl:with-param name=\"readably\" select=\"true()\"/>\n      </xsl:call-template>\n    </xsl:variable>\n    <xsl:value-of select=\"$str\"/>\n  </xsl:template>\n\n  <xsl:template name=\"call-function\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"atoms\" select=\"atoms\"/>\n    <xsl:variable name=\"res\">\n      <xsl:choose>\n        <xsl:when test=\"$func/malval/@kind = 'userfunction'\">\n          <xsl:call-template name=\"uapply\">\n            <xsl:with-param name=\"func\" select=\"$func\"/>\n            <xsl:with-param name=\"args\" select=\"$args\"/>\n            <xsl:with-param name=\"env\" select=\"$env\"/>\n          </xsl:call-template>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:choose>\n            <xsl:when test=\"$func/malval/@name = 'env??'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"nev\" select=\"env:dump($env)\"/>\n              <xsl:variable name=\"value\">\n                <malval kind=\"string\" value=\"{$env =&gt; serialize(map{'method':'json'})}\"/>\n              </xsl:variable>\n              <value>\n                <xsl:sequence select=\"$value\"/>\n              </value>\n              <env data=\"{env:serialise($env)}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'eval'\">\n              <!-- needs access to env -->\n              <xsl:variable name=\"venv\" select=\"env:replEnv($env) =&gt; env:wrapReplEnv()\"/>\n              <xsl:variable name=\"form\">\n                <value>\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$form\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$venv\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:collapseReplEnv()))}\"/>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'atom'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"count($atoms/atom)\"/>\n              <value>\n                <malval kind=\"atom\" value=\"{$atom-ident}\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'deref'\">\n              <!-- needs access to atoms -->\n              <value>\n                <xsl:sequence select=\"$atoms/atom[@identity = $args/value/malval/lvalue/malval[1]/@value]/malval\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'reset!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"newv\" select=\"$args/value/malval/lvalue/malval[2]\"/>\n              <value>\n                <xsl:sequence select=\"$newv\"/>\n              </value>\n              <atoms>\n                <xsl:for-each select=\"$atoms/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv\"/>\n                </atom>\n              </atoms>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'swap!'\">\n              <!-- needs access to atoms -->\n              <xsl:variable name=\"atom-ident\" select=\"$args/value/malval/lvalue/malval[1]/@value\"/>\n              <xsl:variable name=\"atom-value\" select=\"$atoms/atom[@identity = $atom-ident]/malval\"/>\n              <xsl:variable name=\"fn\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[2]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"cargs\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:sequence select=\"$atom-value\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() &gt; 2]\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"newv\">\n                <xsl:call-template name=\"call-function\">\n                  <xsl:with-param name=\"func\" select=\"$fn\"/>\n                  <xsl:with-param name=\"args\" select=\"$cargs\"/>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:sequence select=\"$newv/value\"/>\n              <atoms>\n                <xsl:for-each select=\"$newv/atoms[1]/atom[@identity != $atom-ident]\">\n                  <xsl:sequence select=\".\"/>\n                </xsl:for-each>\n                <atom identity=\"{$atom-ident}\">\n                  <xsl:sequence select=\"$newv/value/malval\"/>\n                </atom>\n              </atoms>\n              <xsl:sequence select=\"$newv/env\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'apply'\">\n              <!-- needs access to env -->\n              <!-- (apply F A... T) -> (F A... T...) -->\n              <xsl:variable name=\"fun\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:variable name=\"args\">\n                <value>\n                  <malval kind=\"list\">\n                    <lvalue>\n                      <xsl:variable name=\"argc\" select=\"count($args/value/malval/lvalue/malval)\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[position() gt 1 and position() lt $argc]\"/>\n                      <xsl:sequence select=\"$args/value/malval/lvalue/malval[$argc]/lvalue/malval\"/>\n                    </lvalue>\n                  </malval>\n                </value>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"./*\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"func\" select=\"$fun\"/>\n                    <xsl:with-param name=\"args\" select=\"$args\"/>\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/value\"/>\n              <xsl:sequence select=\"$res/env\"/>\n              <xsl:sequence select=\"$res/atoms[1]\"/>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'map'\">\n              <!-- needs access to env -->\n              <!-- (map F T) -> (list (F Tx)...) -->\n              <xsl:variable name=\"func\">\n                <xsl:sequence select=\"$args/value/malval/lvalue/malval[1]\"/>\n              </xsl:variable>\n              <xsl:iterate select=\"$args/value/malval/lvalue/malval[2]/lvalue/malval\">\n                <xsl:param name=\"xenv\" select=\"$env\"/>\n                <xsl:param name=\"atoms\" select=\"$atoms\"/>\n                <xsl:param name=\"value\" select=\"()\"/>\n                <xsl:on-completion>\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\"$value\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                  <env data=\"{$env =&gt; env:serialise()}\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:on-completion>\n                <xsl:variable name=\"args\">\n                  <value>\n                    <malval kind=\"list\">\n                      <lvalue>\n                        <xsl:sequence select=\".\"/>\n                      </lvalue>\n                    </malval>\n                  </value>\n                </xsl:variable>\n                <xsl:variable name=\"ctx\">\n                  <xsl:sequence select=\"./*\"/>\n                  <xsl:sequence select=\"$atoms\"/>\n                </xsl:variable>\n                <xsl:variable name=\"res\">\n                  <xsl:for-each select=\"$ctx\">\n                    <xsl:call-template name=\"call-function\">\n                      <xsl:with-param name=\"func\" select=\"$func\"/>\n                      <xsl:with-param name=\"args\" select=\"$args\"/>\n                      <xsl:with-param name=\"env\" select=\"$xenv\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:next-iteration>\n                  <xsl:with-param name=\"xenv\" select=\"env:deserialise($res/env/@data)\"/>\n                  <xsl:with-param name=\"atoms\" select=\"$res/atoms[1]\"/>\n                  <xsl:with-param name=\"value\" select=\"$value, $res/value/malval\"/>\n                </xsl:next-iteration>\n              </xsl:iterate>\n            </xsl:when>\n            <xsl:when test=\"$func/malval/@name = 'readline'\">\n              <!-- requires interaction with harness -->\n              <xsl:message>\n                <request kind=\"readline\" value=\"{$args/value/malval/lvalue/malval[1]/@value}\"/>\n              </xsl:message>\n              <xsl:variable name=\"str\" select=\"unparsed-text('xsl_input-string')\"/>\n              <xsl:sequence select=\"core:makeMALType($str, if (string-length($str) = 0) then 'nil' else 'string')\"/>\n              <xsl:sequence select=\"env\"/>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$ctx\">\n                <xsl:call-template name=\"core-apply\">\n                  <xsl:with-param name=\"func\" select=\"$func\"/>\n                  <xsl:with-param name=\"args\" select=\"$args\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$res/value\"/>\n    <xsl:choose>\n      <xsl:when test=\"$res/atoms\">\n        <xsl:sequence select=\"$res/atoms[1]\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:sequence select=\"atoms\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n    <xsl:choose>\n      <xsl:when test=\"$res/env\">\n        <xsl:sequence select=\"$res/env\"/>\n      </xsl:when>\n      <xsl:otherwise>\n        <env data=\"{$env =&gt; env:serialise()}\"/>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- uapply[env, fn, args] -->\n  <xsl:template name=\"uapply\">\n    <xsl:param name=\"func\"/>\n    <xsl:param name=\"args\"/>\n    <xsl:param name=\"env\"/>\n    <xsl:variable name=\"nenv\" select=\"$env =&gt; env:hier(env:deserialise($func/malval/env/@data)) =&gt; env:close-with-binds($func/malval/binds/malval/@value, $args/value/malval/lvalue/malval)\"/>\n    <xsl:variable name=\"body\">\n      <value>\n        <xsl:sequence select=\"$func/malval/body/malval\"/>\n      </value>\n      <xsl:sequence select=\"atoms[1]\"/>\n    </xsl:variable>\n    <xsl:variable name=\"result\">\n      <xsl:for-each select=\"$body\">\n        <xsl:call-template name=\"EVAL\">\n          <xsl:with-param name=\"env\" select=\"$nenv\"/>\n        </xsl:call-template>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:sequence select=\"$result/data/value\"/>\n    <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($result/env/@data) =&gt; env:replEnv()))}\"/>\n    <xsl:sequence select=\"$result/atoms[1]\"/>\n  </xsl:template>\n\n  <!-- Quasiquote: reduce/fold function, computing the new accumulator\n       value from the current element and the previous accumulator -->\n  <xsl:template name=\"qq_loop\">\n    <xsl:param name=\"elt\"/>\n    <xsl:param name=\"acc\"/>\n    <xsl:choose>\n      <xsl:when test=\"$elt/@kind                   = 'list'\n            and count($elt/lvalue/malval)          = 2\n            and       $elt/lvalue/malval[1]/@kind  = 'symbol'\n            and       $elt/lvalue/malval[1]/@value = 'splice-unquote'\">\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"concat\"/>\n            <xsl:sequence select=\"$elt/lvalue/malval[2]\"/>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <malval kind=\"list\">\n          <lvalue>\n            <malval kind=\"symbol\" value=\"cons\"/>\n            <xsl:call-template name=\"quasiquote\">\n              <xsl:with-param name=\"ast\" select=\"$elt\"/>\n            </xsl:call-template>\n            <xsl:sequence select=\"$acc\"/>\n          </lvalue>\n        </malval>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <!-- Quasiquote: right reduce/fold for an XML sequence -->\n  <xsl:template name=\"qq_foldr\">\n    <xsl:param name=\"xs\"/>\n    <xsl:choose>\n      <xsl:when test=\"count($xs) = 0\">\n        <malval kind=\"list\">\n          <lvalue/>\n        </malval>\n      </xsl:when>\n      <xsl:otherwise>\n        <xsl:call-template name=\"qq_loop\">\n          <xsl:with-param name=\"elt\" select=\"$xs[1]\"/>\n          <xsl:with-param name=\"acc\">\n            <xsl:call-template name=\"qq_foldr\">\n              <xsl:with-param name=\"xs\" select=\"$xs[position() != 1]\"/>\n            </xsl:call-template>\n          </xsl:with-param>\n        </xsl:call-template>\n      </xsl:otherwise>\n    </xsl:choose>\n  </xsl:template>\n\n  <xsl:template name=\"quasiquote\">\n    <xsl:param name=\"ast\"/>\n    <xsl:variable name=\"result\">\n      <xsl:choose>\n        <xsl:when test=\"$ast/@kind = 'list'\">\n          <xsl:choose>\n            <xsl:when test=\"count($ast/lvalue/malval)          = 2\n                              and $ast/lvalue/malval[1]/@kind  = 'symbol'\n                              and $ast/lvalue/malval[1]/@value = 'unquote'\">\n              <xsl:sequence select=\"$ast/lvalue/malval[2]\"/>\n            </xsl:when>\n            <xsl:otherwise>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'vector'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"vec\"/>\n              <xsl:call-template name=\"qq_foldr\">\n                <xsl:with-param name=\"xs\" select=\"$ast/lvalue/malval\"/>\n              </xsl:call-template>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:when test=\"$ast/@kind = 'symbol' or $ast/@kind = 'hash'\">\n          <malval kind=\"list\">\n            <lvalue>\n              <malval kind=\"symbol\" value=\"quote\"/>\n              <xsl:sequence select=\"$ast\"/>\n            </lvalue>\n          </malval>\n        </xsl:when>\n        <xsl:otherwise>\n          <xsl:sequence select=\"$ast\"/>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:sequence select=\"$result\"/>\n  </xsl:template>\n\n  <xsl:template name=\"EVAL\">\n    <xsl:param name=\"env\"/>\n    <xsl:param name=\"encode-env\" select=\"true()\"/>\n    <!-- <xsl:message>\n\nEVALUATE <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> IN (<xsl:sequence select=\"empty(atoms)\"/>) ATOMS <xsl:sequence select=\"fn:pretty(atoms)\"/>\n\n</xsl:message> -->\n\n    <xsl:if test=\"let $dbgeval := env:get-noerror($env, 'DEBUG-EVAL')\n                  return not(empty($dbgeval)) and\n                    (let $kind := $dbgeval['kind']\n                     return $kind != 'nil' and $kind != 'false')\">\n      <xsl:variable name=\"msg\">\n        <xsl:call-template name=\"malprinter-pr_str\">\n          <xsl:with-param name=\"readably\" select=\"true()\"/>\n        </xsl:call-template>\n      </xsl:variable>\n      <xsl:message>\n        <request kind=\"display\" value=\"{concat('EVAL: ', $msg)}\"/>\n      </xsl:message>\n    </xsl:if>\n\n    <xsl:variable name=\"atoms\" select=\"atoms[1]\"/>\n    <xsl:variable name=\"data\">\n      <xsl:choose>\n\n        <xsl:when test=\"value/malval/@kind = 'symbol'\">\n          <value>\n            <xsl:sequence select=\"env:get($env, value/malval/@value)\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind = 'vector'\n                        or value/malval/@kind = 'hash'\">\n          <xsl:variable name=\"myctx\">\n            <xsl:iterate select=\"value/malval/lvalue/malval\">\n              <xsl:param name=\"atoms\" select=\"$atoms\"/>\n              <xsl:param name=\"xctx\" select=\"()\"/>\n              <xsl:on-completion>\n                <xsl:sequence select=\"$xctx/value/malval\"/>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:on-completion>\n              <xsl:variable name=\"ctx\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"xctxy\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:variable name=\"val\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$env\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                    </xsl:call-template>\n                  </xsl:variable>\n                  <xsl:sequence select=\"$val/data/value\"/>\n                  <xsl:sequence select=\"$val/atoms\"/>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:next-iteration>\n                <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n              </xsl:next-iteration>\n            </xsl:iterate>\n          </xsl:variable>\n          <value>\n            <malval kind=\"{value/malval/@kind}\">\n              <lvalue>\n                <xsl:sequence select=\"$myctx/malval\"/>\n              </lvalue>\n            </malval>\n          </value>\n          <xsl:sequence select=\"$myctx/atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"value/malval/@kind != 'list'\n                        or count(value/malval/lvalue/malval) = 0\">\n          <xsl:sequence select=\".\"/>\n          <xsl:sequence select=\"$atoms\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'def!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$value/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $value/data/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'defmacro!'\">\n          <xsl:variable name=\"name\">\n            <xsl:value-of select=\"value/malval/lvalue/malval[2]/@value\"/>\n          </xsl:variable>\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"value\">\n            <xsl:for-each select=\"$xvalue\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"resv\">\n            <value>\n              <malval kind=\"{$value/data/value/malval/@kind}\" value=\"{$value/data/value/malval/@value}\">\n                <xsl:sequence select=\"$value/data/value/malval/*[name() != 'is_macro']\"/>\n                <is_macro>true</is_macro>\n              </malval>\n            </value>\n          </xsl:variable>\n          <xsl:sequence select=\"$resv\"/>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise(env:set($env, $name, $resv/value/malval))}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$value/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'let*'\">\n          <xsl:variable name=\"xvalue\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:iterate select=\"fn:group_consec(value/malval/lvalue/malval[2]/lvalue/malval)\">\n            <xsl:param name=\"new_env\" select=\"env:close($env)\"/>\n            <xsl:param name=\"new_atoms\" select=\"$atoms\"/>\n            <xsl:on-completion>\n              <xsl:variable name=\"xvalue\">\n                <xsl:sequence select=\"$xvalue/value\"/>\n                <xsl:sequence select=\"$new_atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise(env:swap-replEnv($env, env:deserialise($value/env/@data) =&gt; env:replEnv()))}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"name\">\n              <xsl:value-of select=\"node()[name() = 'first']/malval/@value\"/>\n            </xsl:variable>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\"node()[name() = 'second']/malval\"/>\n              </value>\n              <xsl:sequence select=\"$new_atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:set($new_env, $name, $value/data/value/malval)\"/>\n              <xsl:with-param name=\"new_atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'do'\">\n          <xsl:iterate select=\"value/malval/lvalue/malval[position() &gt; 1]\">\n            <xsl:param name=\"new_env\" select=\"$env\"/>\n            <xsl:param name=\"atoms\" select=\"$atoms\"/>\n            <xsl:param name=\"previous_res\" select=\"()\"/>\n            <xsl:on-completion>\n              <xsl:sequence select=\"$previous_res\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{env:serialise($new_env)}\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:on-completion>\n            <xsl:variable name=\"xvalue\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$atoms\"/>\n            </xsl:variable>\n            <xsl:variable name=\"value\">\n              <xsl:for-each select=\"$xvalue\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$new_env\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:variable>\n            <xsl:next-iteration>\n              <xsl:with-param name=\"new_env\" select=\"env:deserialise($value/env/@data)\"/>\n              <xsl:with-param name=\"previous_res\" select=\"$value/data/value\"/>\n              <xsl:with-param name=\"atoms\" select=\"$value/atoms[1]\"/>\n            </xsl:next-iteration>\n          </xsl:iterate>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'if'\">\n          <xsl:variable name=\"cond\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[2]\">\n              <xsl:variable name=\"context\">\n                <value>\n                  <xsl:sequence select=\".\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:for-each select=\"$context\">\n                <xsl:call-template name=\"EVAL\">\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                  <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                </xsl:call-template>\n              </xsl:for-each>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"ptrue\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[3]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"pfalse\">\n            <xsl:for-each select=\"value/malval/lvalue/malval[4]\">\n              <value>\n                <xsl:sequence select=\".\"/>\n              </value>\n              <xsl:sequence select=\"$cond/atoms[1]\"/>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:variable name=\"xfalse\">\n            <xsl:choose>\n              <xsl:when test=\"empty($pfalse/value)\">\n                <value>\n                  <malval kind=\"nil\"/>\n                </value>\n                <xsl:sequence select=\"$cond/atoms[1]\"/>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:sequence select=\"$pfalse/value\"/>\n                <xsl:sequence select=\"$pfalse/atoms[1]\"/>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:choose>\n              <xsl:when test=\"let $kind := $cond/data/value/malval/@kind\n                              return $kind = 'nil' or $kind = 'false'\">\n                <xsl:for-each select=\"$xfalse\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:when>\n              <xsl:otherwise>\n                <xsl:for-each select=\"$ptrue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:otherwise>\n            </xsl:choose>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'fn*'\">\n          <value>\n            <malval kind=\"userfunction\">\n              <is_macro>false</is_macro>\n              <binds>\n                <xsl:sequence select=\"value/malval/lvalue/malval[2]/lvalue/malval\"/>\n              </binds>\n              <body>\n                <xsl:sequence select=\"value/malval/lvalue/malval[3]\"/>\n              </body>\n              <env data=\"{env:serialise(env:noReplEnv($env))}\"/>\n              <!-- capture current env -->\n            </malval>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quote'\">\n          <value>\n            <xsl:sequence select=\"value/malval/lvalue/malval[2]\"/>\n          </value>\n          <xsl:if test=\"$encode-env\">\n            <env data=\"{env:serialise($env)}\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$atoms\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'quasiquote'\">\n          <xsl:variable name=\"exp\">\n            <value>\n              <xsl:call-template name=\"quasiquote\">\n                <xsl:with-param name=\"ast\" select=\"value/malval/lvalue/malval[2]\"/>\n              </xsl:call-template>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"res\">\n            <xsl:for-each select=\"$exp\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:sequence select=\"$res/data/value\"/>\n          <xsl:if test=\"$encode-env\">\n            <xsl:sequence select=\"$res/env\"/>\n          </xsl:if>\n          <xsl:sequence select=\"$res/atoms[1]\"/>\n        </xsl:when>\n\n        <xsl:when test=\"let $fn := value/malval/lvalue/malval[1]\n                        return $fn/@kind = 'symbol' and $fn/@value = 'try*'\">\n          <xsl:variable name=\"tryv\">\n            <xsl:try>\n              <xsl:variable name=\"xvalue\">\n                <value>\n                  <xsl:sequence select=\"value/malval/lvalue/malval[2]\"/>\n                </value>\n                <xsl:sequence select=\"$atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"value\">\n                <xsl:for-each select=\"$xvalue\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$value/data/value\"/>\n              <xsl:if test=\"$encode-env\">\n                <xsl:sequence select=\"$value/env\"/>\n              </xsl:if>\n              <xsl:sequence select=\"$value/atoms[1]\"/>\n              <xsl:catch errors=\"*\">\n                <xsl:variable name=\"catchv-name\" select=\"value/malval/lvalue/malval[3]/lvalue/malval[2]/@value\"/>\n                <xsl:variable name=\"catchv\">\n                  <value>\n                    <xsl:sequence select=\"value/malval/lvalue/malval[3]/lvalue/malval[3]\"/>\n                  </value>\n                </xsl:variable>\n                <xsl:if test=\"empty($catchv/value/malval)\">\n                  <xsl:value-of select=\"error($err:code, $err:description, $err:value)\"/>\n                </xsl:if>\n                <xsl:variable name=\"newenv\" select=\"env:close($env) =&gt; env:set($catchv-name, $err:value)\"/>\n                <xsl:variable name=\"value\">\n                  <xsl:for-each select=\"$catchv\">\n                    <xsl:call-template name=\"EVAL\">\n                      <xsl:with-param name=\"env\" select=\"$newenv\"/>\n                      <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                    </xsl:call-template>\n                  </xsl:for-each>\n                </xsl:variable>\n                <xsl:sequence select=\"$value/data/value\"/>\n                <xsl:if test=\"$encode-env\">\n                  <xsl:sequence select=\"$value/env\"/>\n                  <!-- leaks the bound name, ouch -->\n                </xsl:if>\n                <xsl:sequence select=\"$value/atoms[1]\"/>\n              </xsl:catch>\n            </xsl:try>\n          </xsl:variable>\n          <xsl:sequence select=\"$tryv\"/>\n        </xsl:when>\n\n        <!-- apply phase -->\n        <xsl:otherwise>\n\n          <!-- evaluate the first element -->\n          <xsl:variable name=\"ctx\">\n            <value>\n              <xsl:sequence select=\"value/malval/lvalue/malval[1]\"/>\n            </value>\n            <xsl:sequence select=\"$atoms\"/>\n          </xsl:variable>\n          <xsl:variable name=\"func\">\n            <xsl:for-each select=\"$ctx\">\n              <xsl:call-template name=\"EVAL\">\n                <xsl:with-param name=\"env\" select=\"$env\"/>\n                <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n              </xsl:call-template>\n            </xsl:for-each>\n          </xsl:variable>\n          <xsl:choose>\n\n            <!-- Macro -->\n            <xsl:when test=\"$func/data/value/malval/is_macro/text() = 'true'\">\n              <xsl:variable name=\"new\">\n                <xsl:call-template name=\"uapply\">\n                  <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                  <xsl:with-param name=\"args\">\n                    <value>\n                      <malval kind=\"list\">\n                        <lvalue>\n                          <xsl:sequence select=\"value/malval/lvalue/malval[position() != 1]\"/>\n                        </lvalue>\n                      </malval>\n                    </value>\n                  </xsl:with-param>\n                  <xsl:with-param name=\"env\" select=\"$env\"/>\n                </xsl:call-template>\n              </xsl:variable>\n              <xsl:variable name=\"res\">\n                <xsl:for-each select=\"$new\">\n                  <xsl:call-template name=\"EVAL\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"encode-env\" select=\"$encode-env\"/>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:sequence select=\"$res/data/value\"/>\n              <xsl:sequence select=\"atoms[1]\"/>\n              <xsl:if test=\"$encode-env\">\n                <env data=\"{$env =&gt; env:serialise()}\"/>\n              </xsl:if>\n            </xsl:when>\n\n            <!-- Function -->\n            <xsl:otherwise>\n              <xsl:variable name=\"new_list\">\n                <xsl:iterate select=\"value/malval/lvalue/malval[position() != 1]\">\n                  <xsl:param name=\"atoms\" select=\"$func/atoms\"/>\n                  <xsl:param name=\"xctx\" select=\"()\"/>\n                  <xsl:on-completion>\n                    <xsl:sequence select=\"$xctx/value/malval\"/>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:on-completion>\n                  <xsl:variable name=\"ctx\">\n                    <value>\n                      <xsl:sequence select=\".\"/>\n                    </value>\n                    <xsl:sequence select=\"$func/atoms\"/>\n                  </xsl:variable>\n                  <xsl:variable name=\"xctxy\">\n                    <xsl:for-each select=\"$ctx\">\n                      <xsl:variable name=\"val\">\n                        <xsl:call-template name=\"EVAL\">\n                          <xsl:with-param name=\"env\" select=\"$env\"/>\n                          <xsl:with-param name=\"encode-env\" select=\"false()\"/>\n                        </xsl:call-template>\n                      </xsl:variable>\n                      <xsl:sequence select=\"$val/data/value\"/>\n                      <xsl:sequence select=\"$val/atoms\"/>\n                    </xsl:for-each>\n                  </xsl:variable>\n                  <xsl:next-iteration>\n                    <xsl:with-param name=\"atoms\" select=\"$xctxy/atoms\"/>\n                    <xsl:with-param name=\"xctx\" select=\"$xctx, $xctxy\"/>\n                  </xsl:next-iteration>\n                </xsl:iterate>\n              </xsl:variable>\n              <xsl:variable name=\"ctx\">\n                <xsl:sequence select=\"$new_list/atoms\"/>\n              </xsl:variable>\n              <xsl:variable name=\"resultv\">\n                <xsl:for-each select=\"$ctx\">\n                  <xsl:call-template name=\"call-function\">\n                    <xsl:with-param name=\"env\" select=\"$env\"/>\n                    <xsl:with-param name=\"func\" select=\"$func/data/value\"/>\n                    <xsl:with-param name=\"args\">\n                      <value>\n                        <malval kind=\"list\">\n                          <lvalue>\n                            <xsl:sequence select=\"$new_list/malval\"/>\n                          </lvalue>\n                        </malval>\n                      </value>\n                    </xsl:with-param>\n                  </xsl:call-template>\n                </xsl:for-each>\n              </xsl:variable>\n              <xsl:for-each select=\"$resultv\">\n                <xsl:choose>\n                  <xsl:when test=\"empty(env)\">\n                    <xsl:if test=\"$encode-env\">\n                      <env data=\"{env:serialise($env)}\"/>\n                    </xsl:if>\n                  </xsl:when>\n                  <xsl:otherwise>\n                    <xsl:if test=\"$encode-env\">\n                      <xsl:sequence select=\"env\"/>\n                    </xsl:if>\n                  </xsl:otherwise>\n                </xsl:choose>\n                <xsl:sequence select=\"atoms[1]\"/>\n                <xsl:sequence select=\"value\"/>\n              </xsl:for-each>\n            </xsl:otherwise>\n          </xsl:choose>\n        </xsl:otherwise>\n      </xsl:choose>\n    </xsl:variable>\n    <xsl:variable name=\"new-atoms\" select=\"($data/atoms[1], $atoms)[1]\"/>\n    <!-- <xsl:message>\nEVALUATED (<xsl:sequence select=\"empty($data/atoms)\"/>) <xsl:sequence select=\"core:pr-str(value/malval)/value/text()\"/> TO <xsl:sequence select=\"core:pr-str($data/value/malval)/value/text()\"/> WITH ATOMS <xsl:sequence select=\"fn:pretty($new-atoms)\"/>\n      </xsl:message> -->\n    <data>\n      <xsl:sequence select=\"$data/value\"/>\n    </data>\n    <xsl:if test=\"$encode-env\">\n      <env data=\"{$data/env/@data}\"/>\n    </xsl:if>\n    <xsl:sequence select=\"$new-atoms\"/>\n  </xsl:template>\n\n  <xsl:template name=\"READ\">\n    <xsl:variable name=\"context\">\n      <str>\n        <xsl:copy-of select=\"stdin/text()\"/>\n      </str>\n    </xsl:variable>\n    <xsl:variable name=\"form\">\n      <xsl:sequence select=\"state/atoms[1]\"/>\n      <xsl:for-each select=\"$context\">\n        <xsl:call-template name=\"malreader-read_str\"/>\n      </xsl:for-each>\n    </xsl:variable>\n    <xsl:for-each select=\"$form\">\n      <xsl:if test=\"error\">\n        <xsl:value-of select=\"error(QName('MAL', 'Error'), string(error))\"/>\n      </xsl:if>\n      <xsl:sequence select=\".\"/>\n    </xsl:for-each>\n  </xsl:template>\n\n  <xsl:function name=\"fn:group_consec\">\n    <xsl:param name=\"nodes\"/>\n    <xsl:variable name=\"groups\">\n      <xsl:for-each-group select=\"$nodes\" group-by=\"position() mod 2\">\n        <xsl:choose>\n          <xsl:when test=\"position() = 1\">\n            <first>\n              <xsl:sequence select=\"current-group()\"/>\n            </first>\n          </xsl:when>\n          <xsl:otherwise>\n            <second>\n              <xsl:sequence select=\"current-group()\"/>\n            </second>\n          </xsl:otherwise>\n        </xsl:choose>\n      </xsl:for-each-group>\n    </xsl:variable>\n    <xsl:iterate select=\"1 to count($groups/first/*)\">\n      <element>\n        <xsl:variable name=\"idx\" select=\"number(.)\"/>\n        <first>\n          <xsl:sequence select=\"$groups/first/node()[position() = $idx]\"/>\n        </first>\n        <second>\n          <xsl:sequence select=\"$groups/second/node()[position() = $idx]\"/>\n        </second>\n      </element>\n    </xsl:iterate>\n  </xsl:function>\n\n  <xsl:function name=\"fn:pretty\">\n    <xsl:param name=\"atoms\"/>\n    <xsl:sequence select=\"$atoms/atom/concat('(', @identity, ': ', malval/@kind, ' ', core:pr-str(malval), ')')\"/>\n  </xsl:function>\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/stepA_mal.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- A way to keep a process waiting until we signal it -->\n<!-- In order to have a process pool that executes our queries faster -->\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:readline=\"READLINE\" version=\"3.0\" xmlns:err=\"http://www.w3.org/2005/xqt-errors\" exclude-result-prefixes=\"readline err\">\n  <xsl:import href=\"stepA_mal.inc.xslt\"/>\n  <xsl:import href=\"readline.xslt\"/>\n  <xsl:template match=\"/\">\n    <xsl:variable name=\"ctx\" select=\".\"/>\n    <xsl:variable name=\"in_repl\" select=\"empty($ctx/mal/no_repl)\"></xsl:variable>\n    <xsl:iterate select=\"1 to (if ($in_repl) then 100000000 else 1)\">\n      <xsl:param name=\"val\" select=\"$ctx\"/>\n      <xsl:on-completion>\n        <xsl:sequence select=\"$val/*\"/>\n      </xsl:on-completion>\n      <xsl:variable name=\"line\" select=\"readline:readline('user&gt; ')\"/>\n      <xsl:variable name=\"ctx\">\n        <mal>\n          <stdin>\n            <xsl:value-of select=\"$line\"/>\n          </stdin>\n          <xsl:sequence select=\"$val/mal/argv\"/>\n          <xsl:sequence select=\"$val/mal/state\"/>\n        </mal>\n      </xsl:variable>\n      <xsl:variable name=\"val\">\n        <xsl:try>\n          <xsl:for-each select=\"$ctx/mal\">\n            <xsl:call-template name=\"rep\">\n              <xsl:with-param name=\"display\" select=\"$in_repl\"/>\n            </xsl:call-template>\n          </xsl:for-each>\n          <xsl:catch errors=\"*\">\n            <xsl:message>\n              <request kind=\"display\" value=\"Error: {$err:description}\"/>\n            </xsl:message>\n            <xsl:sequence select=\"$ctx\"/>\n          </xsl:catch>\n        </xsl:try>\n      </xsl:variable>\n      <xsl:next-iteration>\n        <xsl:with-param name=\"val\" select=\"$val\"/>\n      </xsl:next-iteration>\n    </xsl:iterate>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/xslt/test.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n  <xsl:template match=\"/\">\n    <xsl:value-of select=\"current-dateTime()\"/>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "impls/yorick/Dockerfile",
    "content": "FROM ubuntu:xenial\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python\n\n# Some typical implementation and test requirements\nRUN apt-get -y install curl libreadline-dev libedit-dev\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\n\nRUN apt-get -y install yorick yorick-yeti yorick-yeti-regex\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/yorick/Makefile",
    "content": "SOURCES_BASE = hash.i types.i reader.i printer.i\nSOURCES_LISP = env.i core.i stepA_mal.i\nSOURCES = $(SOURCES_BASE) $(SOURCES_LISP)\n\n.PHONY: all dist clean\n\nall: dist\n\ndist: mal\n\nmal: $(SOURCES)\n\techo \"#!/usr/bin/yorick -batch\" > $@\n\tcat $+ | grep -v \"^require,\" >> $@\n\tchmod +x $@\n\nclean:\n\trm -f mal\n"
  },
  {
    "path": "impls/yorick/core.i",
    "content": "require, \"types.i\"\n\nfunc mal_equal(a) { return new_boolean(equal(*a(1), *a(2))); }\nfunc mal_throw(a) { return MalError(obj=a(1)); }\n\nfunc mal_nil_q(a) { return new_boolean(structof(*a(1)) == MalNil); }\nfunc mal_true_q(a) { return new_boolean(structof(*a(1)) == MalTrue); }\nfunc mal_false_q(a) { return new_boolean(structof(*a(1)) == MalFalse); }\nfunc mal_string_q(a) { return new_boolean(structof(*a(1)) == MalString); }\nfunc mal_symbol(a) { return MalSymbol(val=a(1)->val); }\nfunc mal_symbol_q(a) { return new_boolean(structof(*a(1)) == MalSymbol); }\nfunc mal_keyword(a) { return MalKeyword(val=a(1)->val); }\nfunc mal_keyword_q(a) { return new_boolean(structof(*a(1)) == MalKeyword); }\nfunc mal_number_q(a) { return new_boolean(structof(*a(1)) == MalNumber); }\nfunc mal_fn_q(a)\n{\n  if (structof(*a(1)) == MalNativeFunction) return MAL_TRUE;\n  return new_boolean(structof(*a(1)) == MalFunction && !a(1)->macro);\n}\nfunc mal_macro_q(a) { return new_boolean(structof(*a(1)) == MalFunction && a(1)->macro); }\n\nfunc string_helper(a, delimiter, readable)\n{\n  res = \"\"\n  for (i = 1; i <= numberof(a); ++i) {\n    if (i > 1) res += delimiter\n    res += pr_str(*a(i), readable)\n  }\n  return res\n}\n\nfunc mal_pr_str(a) { return MalString(val=string_helper(a, \" \", 1)); }\nfunc mal_str(a) { return MalString(val=string_helper(a, \"\", 0)); }\nfunc mal_prn(a) { write, format=\"%s\\n\", string_helper(a, \" \", 1); return MAL_NIL; }\nfunc mal_println(a) { write, format=\"%s\\n\", string_helper(a, \" \", 0); return MAL_NIL; }\nfunc mal_read_string(a) { return read_str(a(1)->val); }\n\nfunc mal_readline(a)\n{\n  extern stdin_file\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  write, format=\"%s\", a(1)->val\n  line = rdline(stdin_file, prompt=\"\")\n  return line ? MalString(val=line) : MAL_NIL\n}\n\nfunc mal_slurp(a)\n{\n  f = open(a(1)->val)\n  lines = rdfile(f)\n  close, f\n  s = \"\"\n  for (i = 1; i <= numberof(lines); ++i) {\n    s += (lines(i) + \"\\n\")\n  }\n  return MalString(val=s)\n}\n\nfunc mal_lt(a) { return new_boolean(a(1)->val < a(2)->val); }\nfunc mal_lte(a) { return new_boolean(a(1)->val <= a(2)->val); }\nfunc mal_gt(a) { return new_boolean(a(1)->val > a(2)->val); }\nfunc mal_gte(a) { return new_boolean(a(1)->val >= a(2)->val); }\n\nfunc mal_add(a) { return MalNumber(val=(a(1)->val + a(2)->val)); }\nfunc mal_sub(a) { return MalNumber(val=(a(1)->val - a(2)->val)); }\nfunc mal_mul(a) { return MalNumber(val=(a(1)->val * a(2)->val)); }\nfunc mal_div(a) { return MalNumber(val=(a(1)->val / a(2)->val)); }\n\nfunc mal_time_ms(a)\n{\n  elapsed = array(double, 3)\n  timer, elapsed\n  return MalNumber(val=floor(elapsed(3) * 1000))\n}\n\nfunc mal_list(a) { return MalList(val=&a); }\nfunc mal_list_q(a) { return new_boolean(structof(*a(1)) == MalList); }\nfunc mal_vector(a) { return MalVector(val=&a); }\nfunc mal_vector_q(a) { return new_boolean(structof(*a(1)) == MalVector); }\nfunc mal_hash_map(a) { return array_to_hashmap(a); }\nfunc mal_map_q(a) { return new_boolean(structof(*a(1)) == MalHashmap); }\n\nfunc mal_assoc(a) {\n  h = *(a(1)->val)\n  k1 = *h.keys\n  v1 = *h.vals\n  new_h = Hash(keys=&k1, vals=&v1)\n  for (i = 2; i <= numberof(a); i += 2) {\n    hash_set, new_h, hashmap_obj_to_key(*a(i)), *a(i + 1)\n  }\n  return MalHashmap(val=&new_h);\n}\n\nfunc mal_dissoc(a) {\n  h = *(a(1)->val)\n  k1 = *h.keys\n  v1 = *h.vals\n  new_h = Hash(keys=&k1, vals=&v1)\n  for (i = 2; i <= numberof(a); ++i) {\n    hash_delete, new_h, hashmap_obj_to_key(*a(i))\n  }\n  return MalHashmap(val=&new_h);\n}\n\nfunc mal_get(a) {\n  if (structof(*a(1)) == MalNil) return MAL_NIL\n  h = *(a(1)->val)\n  key_obj = *a(2)\n  val = hash_get(h, hashmap_obj_to_key(key_obj))\n  return is_void(val) ? MAL_NIL : val\n}\n\nfunc mal_contains_q(a) {\n  if (structof(*a(1)) == MalNil) return MAL_FALSE\n  h = *(a(1)->val)\n  key_obj = *a(2)\n  return hash_has_key(h, hashmap_obj_to_key(key_obj)) ? MAL_TRUE : MAL_FALSE\n}\n\nfunc mal_keys(a) {\n  keys_strs = *(a(1)->val->keys)\n  if (numberof(keys_strs) == 0) return MalList(val=&[])\n  res = array(pointer, numberof(keys_strs))\n  for (i = 1; i <= numberof(keys_strs); ++i) {\n    res(i) = &hashmap_key_to_obj(keys_strs(i))\n  }\n  return MalList(val=&res);\n}\n\nfunc mal_vals(a) { return MalList(val=a(1)->val->vals); }\n\nfunc mal_sequential_q(a) { return new_boolean(structof(*a(1)) == MalList || structof(*a(1)) == MalVector); }\n\nfunc mal_cons(a)\n{\n  a2_len = count(*a(2))\n  seq = array(pointer, a2_len + 1)\n  seq(1) = a(1)\n  if (a2_len > 0) {\n    seq(2:) = *(a(2)->val)\n  }\n  return MalList(val=&seq)\n}\n\nfunc mal_concat(a)\n{\n  seq = []\n  for (i = 1; i <= numberof(a); ++i) {\n    grow, seq, *(a(i)->val)\n  }\n  return MalList(val=&seq)\n}\n\nfunc mal_vec(a)\n{\n  if (numberof(a) == 1) {\n    type = structof(*a(1))\n    if (type == MalVector) return *(a(1))\n    if (type == MalList)   return MalVector(val=a(1)->val)\n  }\n  return MalError(message=\"vec: requires a sequence\")\n}\n\nfunc mal_nth(a)\n{\n  index = a(2)->val\n  if (index >= count(*a(1))) return MalError(message=\"nth: index out of range\")\n  return *((*(a(1)->val))(index + 1))\n}\n\nfunc mal_first(a)\n{\n  if (structof(*a(1)) == MalNil || count(*a(1)) == 0) return MAL_NIL\n  return *((*(a(1)->val))(1))\n}\n\nfunc mal_rest(a)\n{\n  if (structof(*a(1)) == MalNil) return MalList(val=&[])\n  return rest(*a(1))\n}\n\nfunc mal_empty_q(a) { return new_boolean((structof(*a(1)) == MalNil ? 1 : count(*a(1)) == 0)); }\nfunc mal_count(a) { return MalNumber(val=(structof(*a(1)) == MalNil ? 0 : count(*a(1)))); }\n\nfunc call_func(fn, args)\n{\n  if (structof(fn) == MalNativeFunction) {\n    return call_core_fn(fn.val, args)\n  } else if (structof(fn) == MalFunction) {\n    fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n    return EVAL(*fn.ast, fn_env)\n  } else {\n    return MalError(message=\"Unknown function type\")\n  }\n}\n\nfunc mal_apply(a) {\n  mid_args = numberof(a) > 2 ? a(2:-1) : []\n  return call_func(*a(1), grow(mid_args, *(a(0)->val)))\n}\n\nfunc mal_map(a) {\n  fn = *a(1)\n  seq = *(a(2)->val)\n  if (numberof(seq) == 0) return MalList(val=&[])\n  new_seq = array(pointer, numberof(seq))\n  for (i = 1; i <= numberof(seq); ++i) {\n    new_val = call_func(fn, [seq(i)])\n    if (structof(new_val) == MalError) return new_val\n    new_seq(i) = &new_val\n  }\n  return MalList(val=&new_seq)\n}\n\nfunc mal_conj(a)\n{\n  obj = *a(1)\n  type = structof(obj)\n  if (type == MalList) {\n    res = obj\n    for (i = 2; i <= numberof(a); ++i) {\n      res = mal_cons([a(i), &res])\n    }\n    return res\n  } else if (type == MalVector) {\n    seq = *obj.val\n    grow, seq, a(2:)\n    return MalVector(val=&seq)\n  } else {\n    return MalError(message=\"conj requires list or vector\")\n  }\n}\n\nfunc mal_seq(a)\n{\n  obj = *a(1)\n  type = structof(obj)\n  if (type == MalString) {\n    len = strlen(obj.val)\n    if (len == 0) return MAL_NIL\n    seq = array(pointer, len)\n    for (i = 1; i <= len; ++i) {\n      seq(i) = &MalString(val=strpart(obj.val, i:i))\n    }\n    return MalList(val=&seq)\n  } else if (type == MalList) {\n    return count(obj) == 0 ? MAL_NIL : obj\n  } else if (type == MalVector) {\n    return count(obj) == 0 ? MAL_NIL : MalList(val=obj.val)\n  } else if (type == MalNil) {\n    return MAL_NIL\n  } else {\n    return MalError(message=\"seq requires string or list or vector or nil\")\n  }\n}\n\nfunc mal_meta(a)\n{\n  meta_obj = *(a(1)->meta)\n  return is_void(meta_obj) ? MAL_NIL : meta_obj\n}\n\nfunc mal_with_meta(a)\n{\n  new_obj = *a(1)\n  new_obj.meta = a(2)\n  return new_obj\n}\n\nfunc mal_atom(a) { return MalAtom(val=&MalAtomVal(val=a(1))); }\nfunc mal_atom_q(a) { return new_boolean(structof(*a(1)) == MalAtom); }\nfunc mal_deref(a) { return *(a(1)->val->val); }\nfunc mal_reset_bang(a) { a(1)->val->val = a(2); return *(a(1)->val->val); }\nfunc mal_swap_bang(a)\n{\n  old_val = mal_deref([a(1)])\n  args = array(pointer, numberof(a) - 1)\n  args(1) = &old_val\n  if (numberof(a) > 2) args(2:) = a(3:)\n  new_val = call_func(*a(2), args)\n  if (structof(new_val) == MalError) return new_val\n  return mal_reset_bang([a(1), &new_val])\n}\n\nfunc mal_eval(a) { return EVAL(*a(1), repl_env); }\n\nfunc yorick_to_mal(e)\n{\n  if (is_void(e)) return MAL_NIL\n  if (is_scalar(e)) {\n    if (is_numerical(e)) return MalNumber(val=e)\n    else if (is_string(e)) return MalString(val=e)\n    else return MalString(val=totxt(e))\n  } else {\n    seq = array(pointer, numberof(e))\n    for (i = 1; i <= numberof(e); ++i) {\n      seq(i) = &yorick_to_mal(e(i))\n    }\n    return MalList(val=&seq)\n  }\n}\n\nfunc mal_yorick_eval(a) { return yorick_to_mal(exec(a(1)->val)); }\n\ncore_ns = h_new()\n\nh_set, core_ns, \"=\",           mal_equal\nh_set, core_ns, \"throw\",       mal_throw\n\nh_set, core_ns, \"nil?\",        mal_nil_q\nh_set, core_ns, \"true?\",       mal_true_q\nh_set, core_ns, \"false?\",      mal_false_q\nh_set, core_ns, \"string?\",     mal_string_q\nh_set, core_ns, \"symbol\",      mal_symbol\nh_set, core_ns, \"symbol?\",     mal_symbol_q\nh_set, core_ns, \"keyword\",     mal_keyword\nh_set, core_ns, \"keyword?\",    mal_keyword_q\nh_set, core_ns, \"number?\",     mal_number_q\nh_set, core_ns, \"fn?\",         mal_fn_q\nh_set, core_ns, \"macro?\",      mal_macro_q\n\nh_set, core_ns, \"pr-str\",      mal_pr_str\nh_set, core_ns, \"str\",         mal_str\nh_set, core_ns, \"prn\",         mal_prn\nh_set, core_ns, \"println\",     mal_println\nh_set, core_ns, \"read-string\", mal_read_string\nh_set, core_ns, \"readline\",    mal_readline\nh_set, core_ns, \"slurp\",       mal_slurp\n\nh_set, core_ns, \"<\",           mal_lt\nh_set, core_ns, \"<=\",          mal_lte\nh_set, core_ns, \">\",           mal_gt\nh_set, core_ns, \">=\",          mal_gte\nh_set, core_ns, \"+\",           mal_add\nh_set, core_ns, \"-\",           mal_sub\nh_set, core_ns, \"*\",           mal_mul\nh_set, core_ns, \"/\",           mal_div\nh_set, core_ns, \"time-ms\",     mal_time_ms\n\nh_set, core_ns, \"list\",        mal_list\nh_set, core_ns, \"list?\",       mal_list_q\nh_set, core_ns, \"vector\",      mal_vector\nh_set, core_ns, \"vector?\",     mal_vector_q\nh_set, core_ns, \"hash-map\",    mal_hash_map\nh_set, core_ns, \"map?\",        mal_map_q\nh_set, core_ns, \"assoc\",       mal_assoc\nh_set, core_ns, \"dissoc\",      mal_dissoc\nh_set, core_ns, \"get\",         mal_get\nh_set, core_ns, \"contains?\",   mal_contains_q\nh_set, core_ns, \"keys\",        mal_keys\nh_set, core_ns, \"vals\",        mal_vals\n\nh_set, core_ns, \"sequential?\", mal_sequential_q\nh_set, core_ns, \"cons\",        mal_cons\nh_set, core_ns, \"concat\",      mal_concat\nh_set, core_ns, \"vec\",         mal_vec\nh_set, core_ns, \"nth\",         mal_nth\nh_set, core_ns, \"first\",       mal_first\nh_set, core_ns, \"rest\",        mal_rest\nh_set, core_ns, \"empty?\",      mal_empty_q\nh_set, core_ns, \"count\",       mal_count\nh_set, core_ns, \"apply\",       mal_apply\nh_set, core_ns, \"map\",         mal_map\n\nh_set, core_ns, \"conj\",        mal_conj\nh_set, core_ns, \"seq\",         mal_seq\n\nh_set, core_ns, \"meta\",        mal_meta\nh_set, core_ns, \"with-meta\",   mal_with_meta\nh_set, core_ns, \"atom\",        mal_atom\nh_set, core_ns, \"atom?\",       mal_atom_q\nh_set, core_ns, \"deref\",       mal_deref\nh_set, core_ns, \"reset!\",      mal_reset_bang\nh_set, core_ns, \"swap!\",       mal_swap_bang\n\nh_set, core_ns, \"eval\",        mal_eval\nh_set, core_ns, \"yorick-eval\", mal_yorick_eval\n\nfunc call_core_fn(name, args_list)\n{\n    f = h_get(core_ns, name)\n    return f(args_list)\n}\n"
  },
  {
    "path": "impls/yorick/env.i",
    "content": "require, \"hash.i\"\nrequire, \"types.i\"\n\nstruct Env {\n  pointer outer\n  Hash data\n}\n\nfunc env_new(outer_ptr, binds=, exprs=)\n{\n  env = Env(outer=outer_ptr, data=hash_new())\n  for (i = 1; i <= numberof(binds); ++i) {\n    if (binds(i)->val == \"&\") {\n      rest_args = numberof(exprs) >= i ? exprs(i:) : []\n      env_set, env, binds(i + 1)->val, MalList(val=&rest_args)\n      break\n    } else {\n      env_set, env, binds(i)->val, *exprs(i)\n    }\n  }\n  return env\n}\n\nfunc env_find(env, key)\n{\n  if (hash_has_key(env.data, key)) return env\n  if (is_void(*env.outer)) return nil\n  return env_find(*env.outer, key)\n}\n\nfunc env_get(env, key)\n{\n  found_env = env_find(env, key)\n  if (is_void(found_env)) return MalError(message=(\"'\" + key + \"' not found\"))\n  return hash_get(found_env.data, key)\n}\n\nfunc env_set(&env, key, val)\n{\n  d = env.data\n  hash_set, d, key, val\n  env.data = d\n  return val\n}\n"
  },
  {
    "path": "impls/yorick/hash.i",
    "content": "// Implement our old naive O(n) map because Yeti's hash table (h_new()) cannot\n// be used inside arrays and structs (we can't get a pointer to hash table).\n// This prevents saving pointer to environment in MalFunction for example.\n\nstruct Hash {\n  pointer keys\n  pointer vals\n}\n\nfunc hash_new(void)\n{\n  return Hash(keys=&[], vals=&[])\n}\n\nfunc hash_get(h, key)\n{\n  for (i = 1; i <= numberof(*h.keys); ++i) {\n    if ((*h.keys)(i) == key) return *((*h.vals)(i))\n  }\n  return nil\n}\n\nfunc hash_has_key(h, key)\n{\n  for (i = 1; i <= numberof(*h.keys); ++i) {\n    if ((*h.keys)(i) == key) return 1\n  }\n  return 0\n}\n\nfunc hash_set(&h, key, val)\n{\n  if (is_void(*h.keys)) {\n    h.keys = &[key]\n    h.vals = &[&val]\n    return\n  }\n  for (i = 1; i <= numberof(*h.keys); ++i) {\n    if ((*h.keys)(i) == key) {\n      (*h.vals)(i) = &val\n      return\n    }\n  }\n  tmp = *h.keys\n  grow, tmp, [key]\n  h.keys = &tmp\n  tmp = *h.vals\n  grow, tmp, [&val]\n  h.vals = &tmp\n}\n\nfunc hash_delete(&h, key)\n{\n  if (is_void(*h.keys) || numberof(*h.keys) == 0) return\n  k = *h.keys\n  v = *h.vals\n  if (numberof(k) == 1) {\n    if (k(1) == key) {\n      h.keys = &[]\n      h.vals = &[]\n      return\n    }\n  }\n  for (i = 1; i <= numberof(k); ++i) {\n    if (k(i) == key) {\n      if (i == 1) {\n        h.keys = &(k(i+1:))\n        h.vals = &(v(i+1:))\n      } else if (i == numberof(k)) {\n        h.keys = &(k(1:i-1))\n        h.vals = &(v(1:i-1))\n      } else {\n        h.keys = &grow(k(1:i-1), k(i+1:))\n        h.vals = &grow(v(1:i-1), v(i+1:))\n      }\n      return\n    }\n  }\n}\n"
  },
  {
    "path": "impls/yorick/printer.i",
    "content": "require, \"types.i\"\n\nfunc format_seq(val, start_char, end_char, readable)\n{\n  seq = *val\n  res = \"\"\n  for (i = 1; i <= numberof(seq); ++i) {\n    if (i > 1) res += \" \"\n    res += pr_str(*seq(i), readable)\n  }\n  return start_char + res + end_char\n}\n\nfunc format_hashmap(h, readable)\n{\n  res = \"\"\n  for (i = 1; i <= numberof(*h.keys); ++i) {\n    if (i > 1) res += \" \"\n    key = hashmap_key_to_obj((*h.keys)(i))\n    res += pr_str(key, readable) + \" \" + pr_str(*((*h.vals)(i)), readable)\n  }\n  return \"{\" + res + \"}\"\n}\n\nfunc escape(s)\n{\n  s1 = streplaceall(s, \"\\\\\", \"\\\\\\\\\")\n  s2 = streplaceall(s1, \"\\\"\", \"\\\\\\\"\")\n  s3 = streplaceall(s2, \"\\n\", \"\\\\n\")\n  return \"\\\"\" + s3 + \"\\\"\"\n}\n\nfunc pr_str(ast, readable)\n{\n  type = structof(ast)\n  if (type == MalNil) return \"nil\"\n  else if (type == MalTrue) return \"true\"\n  else if (type == MalFalse) return \"false\"\n  else if (type == MalNumber) return totxt(ast.val)\n  else if (type == MalSymbol) return ast.val\n  else if (type == MalString) return readable ? escape(ast.val) : ast.val\n  else if (type == MalKeyword) return \":\" + ast.val\n  else if (type == MalList) return format_seq(ast.val, \"(\", \")\", readable)\n  else if (type == MalVector) return format_seq(ast.val, \"[\", \"]\", readable)\n  else if (type == MalHashmap) return format_hashmap(*ast.val, readable)\n  else if (type == MalAtom) return \"(atom \" + pr_str(*(ast.val->val), readable) + \")\"\n  else if (type == MalNativeFunction) return \"#<nativefunction:\" + ast.val + \">\"\n  else if (type == MalFunction) return \"#<function:\" + totxt(numberof(*ast.binds)) + \" args>\"\n  else MalError(message=(\"Unknown type \" + totxt(type)))\n}\n"
  },
  {
    "path": "impls/yorick/reader.i",
    "content": "#include \"yeti_regex.i\"\nrequire, \"types.i\"\n\nTOKENIZER_REGEXP = regcomp(\"[[:space:],]*(~@|[][{}()'`~@]|\\\"([\\\\].|[^\\\\\\\"])*\\\"?|;[^\\n]*|[^][[:space:]{}()'\\\"`~@,;]*)\")\n\nfunc tokenize(str)\n{\n  match0 = \"\"\n  match1 = \"\"\n  pos = 1\n  tokens = []\n  while (1) {\n    m = regmatch(TOKENIZER_REGEXP, str, match0, match1, start=pos, indices=1)\n    if (m == 0) break\n    b = match1(1)\n    e = match1(2) - 1\n    if (e < b) {\n      pos = match1(2) + 1\n      continue\n    }\n    token = strpart(str, b:e)\n    pos = match1(2)\n    if (strpart(token, 1:1) == \";\") continue\n    grow, tokens, [token]\n  }\n  return tokens\n}\n\nstruct Reader {\n  pointer tokens\n  int pos\n}\n\nfunc reader_peek(rdr)\n{\n  if (rdr.pos > numberof(*rdr.tokens)) return string(0)\n  return (*rdr.tokens)(rdr.pos)\n}\n\nfunc reader_next(rdr)\n{\n  token = reader_peek(rdr)\n  rdr.pos += 1\n  return token\n}\n\nNUMBER_REGEXP = regcomp(\"^-?[0-9]+$\")\nSTR_REGEXP = regcomp(\"^\\\"([\\\\].|[^\\\\\\\"])*\\\"$\")\nSTR_BAD_REGEXP = regcomp(\"^\\\".*$\")\n\nfunc unescape(s)\n{\n  s = strpart(s, 2:-1) // remove surrounding quotes\n  s = streplaceall(s, \"\\\\\\\\\", \"\\x01\")\n  s = streplaceall(s, \"\\\\n\", \"\\n\")\n  s = streplaceall(s, \"\\\\\\\"\", \"\\\"\")\n  return streplaceall(s, \"\\x01\", \"\\\\\")\n}\n\nfunc read_atom(rdr)\n{\n  token = reader_next(rdr)\n  if (token == \"nil\") return MAL_NIL\n  else if (token == \"true\") return MAL_TRUE\n  else if (token == \"false\") return MAL_FALSE\n  else if (regmatch(NUMBER_REGEXP, token)) return MalNumber(val=tonum(token))\n  else if (regmatch(STR_REGEXP, token)) return MalString(val=unescape(token))\n  else if (regmatch(STR_BAD_REGEXP, token)) return MalError(message=(\"expected '\\\"', got EOF\"))\n  else if (strpart(token, 1:1) == \":\") return MalKeyword(val=strpart(token, 2:))\n  else return MalSymbol(val=token)\n}\n\nfunc read_seq(rdr, start_char, end_char)\n{\n  token = reader_next(rdr)\n  if (token != start_char) {\n    return MalError(message=(\"expected '\" + start_char + \"', got EOF\"))\n  }\n\n  elements = []\n  token = reader_peek(rdr)\n  while (token != end_char) {\n    if (token == string(0)) {\n      return MalError(message=(\"expected '\" + end_char + \"', got EOF\"))\n    }\n    e = read_form(rdr)\n    if (structof(e) == MalError) return e\n    grow, elements, [&e]\n    token = reader_peek(rdr)\n  }\n  token = reader_next(rdr)\n  return elements\n}\n\nfunc read_list(rdr)\n{\n  seq = read_seq(rdr, \"(\", \")\")\n  if (structof(seq) == MalError) return seq\n  return MalList(val=&seq)\n}\n\nfunc read_vector(rdr)\n{\n  seq = read_seq(rdr, \"[\", \"]\")\n  if (structof(seq) == MalError) return seq\n  return MalVector(val=&seq)\n}\n\nfunc read_hashmap(rdr)\n{\n  seq = read_seq(rdr, \"{\", \"}\")\n  if (structof(seq) == MalError) return seq\n  return array_to_hashmap(seq)\n}\n\nfunc reader_macro(rdr, symbol_name)\n{\n  shortcut = reader_next(rdr)\n  form = read_form(rdr)\n  if (structof(form) == MalError) return form\n  seq = [&MalSymbol(val=symbol_name), &form]\n  return MalList(val=&seq)\n}\n\nfunc reader_with_meta_macro(rdr)\n{\n  shortcut = reader_next(rdr)\n  meta = read_form(rdr)\n  if (structof(meta) == MalError) return meta\n  form = read_form(rdr)\n  if (structof(form) == MalError) return form\n  seq = [&MalSymbol(val=\"with-meta\"), &form, &meta]\n  return MalList(val=&seq)\n}\n\nfunc read_form(rdr)\n{\n  token = reader_peek(rdr)\n  if (token == \"'\") return reader_macro(rdr, \"quote\")\n  else if (token == \"`\") return reader_macro(rdr, \"quasiquote\")\n  else if (token == \"~\") return reader_macro(rdr, \"unquote\")\n  else if (token == \"~@\") return reader_macro(rdr, \"splice-unquote\")\n  else if (token == \"@\") return reader_macro(rdr, \"deref\")\n  else if (token == \"^\") return reader_with_meta_macro(rdr)\n  else if (token == \"(\") return read_list(rdr)\n  else if (token == \")\") return MalError(message=\"unexpected ')'\")\n  else if (token == \"[\") return read_vector(rdr)\n  else if (token == \"]\") return MalError(message=\"unexpected ']'\")\n  else if (token == \"{\") return read_hashmap(rdr)\n  else if (token == \"}\") return MalError(message=\"unexpected '}'\")\n  else return read_atom(rdr)\n}\n\nfunc read_str(str)\n{\n  tokens = tokenize(str)\n  rdr = Reader(tokens=&tokens, pos=1)\n  return read_form(rdr)\n}\n"
  },
  {
    "path": "impls/yorick/run",
    "content": "#!/usr/bin/env bash\nexport YORICK_MAL_PATH=\"$(dirname $0)\"\nexec yorick -batch \"$YORICK_MAL_PATH/${STEP:-stepA_mal}.i\" \"${@}\"\n"
  },
  {
    "path": "impls/yorick/step0_repl.i",
    "content": "func READ(str)\n{\n  return str\n}\n\nfunc EVAL(exp, env)\n{\n  return exp\n}\n\nfunc PRINT(exp)\n{\n  return exp\n}\n\nfunc REP(str)\n{\n  return PRINT(EVAL(READ(str), \"\"))\n}\n\nfunc main(void)\n{\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) write, format=\"%s\\n\", REP(line)\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step1_read_print.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc EVAL(exp, env)\n{\n  if (structof(exp) == MalError) return exp\n  return exp\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc REP(str)\n{\n  return PRINT(EVAL(READ(str), \"\"))\n}\n\nfunc main(void)\n{\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step2_eval.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc eval_ast(ast, env)\n{\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalList(val=&res)\n}\n\nfunc EVAL(ast, env)\n{\n  // write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n  // Process non-list types.\n  type = structof(ast)\n  if (type == MalSymbol) {\n    val = h_get(env, ast.val)\n    if (is_void(val)) return MalError(message=(\"'\" + ast.val + \"' not found\"))\n    return val\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n  // The else branch includes MalError. Now ast is a list.\n  if (numberof(*ast.val) == 0) return ast\n  el = eval_ast(ast, env)\n  if (structof(el) == MalError) return el\n  seq = *el.val\n  args = (numberof(seq) > 1) ? seq(2:) : []\n  return call_core_fn(seq(1)->val, args)\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc main(void)\n{\n  repl_env = h_new()\n  h_set, repl_env, \"+\", MalNativeFunction(val=\"+\")\n  h_set, repl_env, \"-\", MalNativeFunction(val=\"-\")\n  h_set, repl_env, \"*\", MalNativeFunction(val=\"*\")\n  h_set, repl_env, \"/\", MalNativeFunction(val=\"/\")\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step3_env.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc eval_ast(ast, env)\n{\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalList(val=&res)\n}\n\nfunc EVAL(ast, env)\n{\n  dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n  if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n     write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n  }\n  // Process non-list types.\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n  // The else branch includes MalError. Now ast is a list.\n  lst = *ast.val\n  if (numberof(lst) == 0) return ast\n  a1 = lst(1)->val\n  if (a1 == \"def!\") {\n    new_value = EVAL(*lst(3), env)\n    if (structof(new_value) == MalError) return new_value\n    return env_set(env, lst(2)->val, new_value)\n  } else if (a1 == \"let*\") {\n    let_env = env_new(&env)\n    args_lst = *(lst(2)->val)\n    for (i = 1; i <= numberof(args_lst); i += 2) {\n      var_name = args_lst(i)->val\n      var_value = EVAL(*args_lst(i + 1), let_env)\n      if (structof(var_value) == MalError) return var_value\n      env_set, let_env, var_name, var_value\n    }\n    return EVAL(*lst(3), let_env)\n  } else {\n    el = eval_ast(ast, env)\n    if (structof(el) == MalError) return el\n    seq = *el.val\n    args = (numberof(seq) > 1) ? seq(2:) : []\n    return call_core_fn(seq(1)->val, args)\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc main(void)\n{\n  repl_env = env_new(pointer(0))\n  env_set, repl_env, \"+\", MalNativeFunction(val=\"+\")\n  env_set, repl_env, \"-\", MalNativeFunction(val=\"-\")\n  env_set, repl_env, \"*\", MalNativeFunction(val=\"*\")\n  env_set, repl_env, \"/\", MalNativeFunction(val=\"/\")\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step4_if_fn_do.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc eval_ast(ast, env)\n{\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalList(val=&res)\n}\n\nfunc EVAL(ast, env)\n{\n  dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n  if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n     write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n  }\n  // Process non-list types.\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n  // The else branch includes MalError. Now ast is a list.\n  lst = *ast.val\n  if (numberof(lst) == 0) return ast\n  a1 = lst(1)->val\n  if (a1 == \"def!\") {\n    new_value = EVAL(*lst(3), env)\n    if (structof(new_value) == MalError) return new_value\n    return env_set(env, lst(2)->val, new_value)\n  } else if (a1 == \"let*\") {\n    let_env = env_new(&env)\n    args_lst = *(lst(2)->val)\n    for (i = 1; i <= numberof(args_lst); i += 2) {\n      var_name = args_lst(i)->val\n      var_value = EVAL(*args_lst(i + 1), let_env)\n      if (structof(var_value) == MalError) return var_value\n      env_set, let_env, var_name, var_value\n    }\n    return EVAL(*lst(3), let_env)\n  } else if (a1 == \"do\") {\n    ret = nil\n    for (i = 2; i <= numberof(lst); ++i) {\n      ret = EVAL(*lst(i), env)\n      if (structof(ret) == MalError) return ret\n    }\n    return ret\n  } else if (a1 == \"if\") {\n    cond_val = EVAL(*lst(2), env)\n    if (structof(cond_val) == MalError) return cond_val\n    if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n      if (numberof(lst) > 3) {\n        return EVAL(*lst(4), env)\n      } else {\n        return MAL_NIL\n      }\n    } else {\n      return EVAL(*lst(3), env)\n    }\n  } else if (a1 == \"fn*\") {\n    return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3))\n  } else {\n    el = eval_ast(ast, env)\n    if (structof(el) == MalError) return el\n    seq = *el.val\n    if (structof(*seq(1)) == MalNativeFunction) {\n      args = (numberof(seq) > 1) ? seq(2:) : []\n      return call_core_fn(seq(1)->val, args)\n    } else if (structof(*seq(1)) == MalFunction) {\n      fn = *seq(1)\n      exprs = numberof(seq) > 1 ? seq(2:) : []\n      fn_env = env_new(fn.env, binds=*fn.binds, exprs=exprs)\n      return EVAL(*fn.ast, fn_env)\n    } else {\n      return MalError(message=\"Unknown function type\")\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc main(void)\n{\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n\n  // core.mal: defined using the language itself\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step5_tco.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc eval_ast(ast, env)\n{\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalList(val=&res)\n}\n\nfunc EVAL(ast, env)\n{\n  while (1) {\n    dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n    if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n       write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n    }\n    // Process non-list types (todo: indent right)\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n    // The else branch includes MalError. Now ast is a list.\n    lst = *ast.val\n    if (numberof(lst) == 0) return ast\n    a1 = lst(1)->val\n    if (a1 == \"def!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"let*\") {\n      let_env = env_new(&env)\n      args_lst = *(lst(2)->val)\n      for (i = 1; i <= numberof(args_lst); i += 2) {\n        var_name = args_lst(i)->val\n        var_value = EVAL(*args_lst(i + 1), let_env)\n        if (structof(var_value) == MalError) return var_value\n        env_set, let_env, var_name, var_value\n      }\n      ast = *lst(3)\n      env = let_env\n      // TCO\n    } else if (a1 == \"do\") {\n      for (i = 2; i < numberof(lst); ++i) {\n        ret = EVAL(*lst(i), env)\n        if (structof(ret) == MalError) return ret\n      }\n      ast = *lst(numberof(lst))\n      // TCO\n    } else if (a1 == \"if\") {\n      cond_val = EVAL(*lst(2), env)\n      if (structof(cond_val) == MalError) return cond_val\n      if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n        if (numberof(lst) > 3) {\n          ast = *lst(4)\n        } else {\n          return MAL_NIL\n        }\n      } else {\n        ast = *lst(3)\n      }\n      // TCO\n    } else if (a1 == \"fn*\") {\n      return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3))\n    } else {\n      el = eval_ast(ast, env)\n      if (structof(el) == MalError) return el\n      seq = *el.val\n      if (structof(*seq(1)) == MalNativeFunction) {\n        args = (numberof(seq) > 1) ? seq(2:) : []\n        return call_core_fn(seq(1)->val, args)\n      } else if (structof(*seq(1)) == MalFunction) {\n        fn = *seq(1)\n        exprs = numberof(seq) > 1 ? seq(2:) : []\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=exprs)\n        ast = *fn.ast\n        env = fn_env\n        // TCO\n      } else {\n        return MalError(message=\"Unknown function type\")\n      }\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc main(void)\n{\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n\n  // core.mal: defined using the language itself\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step6_file.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc eval_ast(ast, env)\n{\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalList(val=&res)\n}\n\nfunc EVAL(ast, env)\n{\n  while (1) {\n    dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n    if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n       write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n    }\n    // Process non-list types (todo: indent right)\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n    // The else branch includes MalError. Now ast is a list.\n    lst = *ast.val\n    if (numberof(lst) == 0) return ast\n    a1 = lst(1)->val\n    if (a1 == \"def!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"let*\") {\n      let_env = env_new(&env)\n      args_lst = *(lst(2)->val)\n      for (i = 1; i <= numberof(args_lst); i += 2) {\n        var_name = args_lst(i)->val\n        var_value = EVAL(*args_lst(i + 1), let_env)\n        if (structof(var_value) == MalError) return var_value\n        env_set, let_env, var_name, var_value\n      }\n      ast = *lst(3)\n      env = let_env\n      // TCO\n    } else if (a1 == \"do\") {\n      for (i = 2; i < numberof(lst); ++i) {\n        ret = EVAL(*lst(i), env)\n        if (structof(ret) == MalError) return ret\n      }\n      ast = *lst(numberof(lst))\n      // TCO\n    } else if (a1 == \"if\") {\n      cond_val = EVAL(*lst(2), env)\n      if (structof(cond_val) == MalError) return cond_val\n      if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n        if (numberof(lst) > 3) {\n          ast = *lst(4)\n        } else {\n          return MAL_NIL\n        }\n      } else {\n        ast = *lst(3)\n      }\n      // TCO\n    } else if (a1 == \"fn*\") {\n      return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3))\n    } else {\n      el = eval_ast(ast, env)\n      if (structof(el) == MalError) return el\n      seq = *el.val\n      if (structof(*seq(1)) == MalNativeFunction) {\n        args = (numberof(seq) > 1) ? seq(2:) : []\n        return call_core_fn(seq(1)->val, args)\n      } else if (structof(*seq(1)) == MalFunction) {\n        fn = *seq(1)\n        exprs = numberof(seq) > 1 ? seq(2:) : []\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=exprs)\n        ast = *fn.ast\n        env = fn_env\n        // TCO\n      } else {\n        return MalError(message=\"Unknown function type\")\n      }\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc get_command_line(void)\n// Force quiet mode (-q) to prevent Yorick from printing its banner\n{\n  argv = get_argv()\n  return numberof(argv) > 1 ? grow([argv(1), \"-q\"], argv(2:)) : [argv(1), \"-q\"]\n}\n\nfunc prepare_argv_list(args)\n{\n  if (numberof(args) <= 1) return MalList(val=&[])\n  str_lst = array(pointer, numberof(args) - 1)\n  for (i = 2; i <= numberof(args); ++i) {\n    str_lst(i - 1) = &MalString(val=args(i))\n  }\n  return MalList(val=&str_lst)\n}\n\nrepl_env = nil\n\nfunc main(void)\n{\n  extern repl_env\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n  command_line_args = process_argv()\n  env_set, repl_env, \"*ARGV*\", prepare_argv_list(command_line_args)\n\n  // core.mal: defined using the language itself\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n  RE, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\", repl_env\n\n  if (numberof(command_line_args) > 0) {\n    RE, \"(load-file \\\"\" + command_line_args(1) + \"\\\")\", repl_env\n    return 0\n  }\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step7_quote.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc starts_with(seq, sym)\n{\n  return numberof(seq) == 2 && structof(*seq(1)) == MalSymbol && seq(1)->val == sym\n}\n\nfunc quasiquote_loop(seq)\n{\n  acc = MalList(val=&[])\n  for (i=numberof(seq); 0<i; --i) {\n    elt = *seq(i)\n    if (structof(elt) == MalList && starts_with(*elt.val, \"splice-unquote\")) {\n      acc = MalList(val=&[&MalSymbol(val=\"concat\"), (*elt.val)(2), &acc])\n    } else {\n      acc = MalList(val=&[&MalSymbol(val=\"cons\"), &quasiquote(elt), &acc])\n    }\n  }\n  return acc\n}\n\nfunc quasiquote(ast)\n{\n  type = structof(ast)\n  if (type == MalList) {\n    seq = *ast.val\n    if (starts_with(seq, \"unquote\")) {\n      return *seq(2)\n    } else {\n      return quasiquote_loop(seq)\n    }\n  } else if (type == MalVector) {\n    return MalList(val=&[&MalSymbol(val=\"vec\"), &quasiquote_loop(*ast.val)])\n  } else if (type == MalHashmap || type == MalSymbol) {\n    return MalList(val=&[&MalSymbol(val=\"quote\"), &ast])\n  } else {\n    return ast\n  }\n}\n\nfunc eval_ast(ast, env)\n{\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalList(val=&res)\n}\n\nfunc EVAL(ast, env)\n{\n  while (1) {\n    dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n    if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n       write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n    }\n    // Process non-list types (todo: indent right)\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n    // The else branch includes MalError. Now ast is a list.\n    lst = *ast.val\n    if (numberof(lst) == 0) return ast\n    a1 = lst(1)->val\n    if (a1 == \"def!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"let*\") {\n      let_env = env_new(&env)\n      args_lst = *(lst(2)->val)\n      for (i = 1; i <= numberof(args_lst); i += 2) {\n        var_name = args_lst(i)->val\n        var_value = EVAL(*args_lst(i + 1), let_env)\n        if (structof(var_value) == MalError) return var_value\n        env_set, let_env, var_name, var_value\n      }\n      ast = *lst(3)\n      env = let_env\n      // TCO\n    } else if (a1 == \"quote\") {\n      return *lst(2)\n    } else if (a1 == \"quasiquote\") {\n      ast = quasiquote(*lst(2)) // TCO\n    } else if (a1 == \"do\") {\n      for (i = 2; i < numberof(lst); ++i) {\n        ret = EVAL(*lst(i), env)\n        if (structof(ret) == MalError) return ret\n      }\n      ast = *lst(numberof(lst))\n      // TCO\n    } else if (a1 == \"if\") {\n      cond_val = EVAL(*lst(2), env)\n      if (structof(cond_val) == MalError) return cond_val\n      if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n        if (numberof(lst) > 3) {\n          ast = *lst(4)\n        } else {\n          return MAL_NIL\n        }\n      } else {\n        ast = *lst(3)\n      }\n      // TCO\n    } else if (a1 == \"fn*\") {\n      return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3))\n    } else {\n      el = eval_ast(ast, env)\n      if (structof(el) == MalError) return el\n      seq = *el.val\n      if (structof(*seq(1)) == MalNativeFunction) {\n        args = (numberof(seq) > 1) ? seq(2:) : []\n        return call_core_fn(seq(1)->val, args)\n      } else if (structof(*seq(1)) == MalFunction) {\n        fn = *seq(1)\n        exprs = numberof(seq) > 1 ? seq(2:) : []\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=exprs)\n        ast = *fn.ast\n        env = fn_env\n        // TCO\n      } else {\n        return MalError(message=\"Unknown function type\")\n      }\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc get_command_line(void)\n// Force quiet mode (-q) to prevent Yorick from printing its banner\n{\n  argv = get_argv()\n  return numberof(argv) > 1 ? grow([argv(1), \"-q\"], argv(2:)) : [argv(1), \"-q\"]\n}\n\nfunc prepare_argv_list(args)\n{\n  if (numberof(args) <= 1) return MalList(val=&[])\n  str_lst = array(pointer, numberof(args) - 1)\n  for (i = 2; i <= numberof(args); ++i) {\n    str_lst(i - 1) = &MalString(val=args(i))\n  }\n  return MalList(val=&str_lst)\n}\n\nrepl_env = nil\n\nfunc main(void)\n{\n  extern repl_env\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n  command_line_args = process_argv()\n  env_set, repl_env, \"*ARGV*\", prepare_argv_list(command_line_args)\n\n  // core.mal: defined using the language itself\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n  RE, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\", repl_env\n\n  if (numberof(command_line_args) > 0) {\n    RE, \"(load-file \\\"\" + command_line_args(1) + \"\\\")\", repl_env\n    return 0\n  }\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step8_macros.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc starts_with(seq, sym)\n{\n  return numberof(seq) == 2 && structof(*seq(1)) == MalSymbol && seq(1)->val == sym\n}\n\nfunc quasiquote_loop(seq)\n{\n  acc = MalList(val=&[])\n  for (i=numberof(seq); 0<i; --i) {\n    elt = *seq(i)\n    if (structof(elt) == MalList && starts_with(*elt.val, \"splice-unquote\")) {\n      acc = MalList(val=&[&MalSymbol(val=\"concat\"), (*elt.val)(2), &acc])\n    } else {\n      acc = MalList(val=&[&MalSymbol(val=\"cons\"), &quasiquote(elt), &acc])\n    }\n  }\n  return acc\n}\n\nfunc quasiquote(ast)\n{\n  type = structof(ast)\n  if (type == MalList) {\n    seq = *ast.val\n    if (starts_with(seq, \"unquote\")) {\n      return *seq(2)\n    } else {\n      return quasiquote_loop(seq)\n    }\n  } else if (type == MalVector) {\n    return MalList(val=&[&MalSymbol(val=\"vec\"), &quasiquote_loop(*ast.val)])\n  } else if (type == MalHashmap || type == MalSymbol) {\n    return MalList(val=&[&MalSymbol(val=\"quote\"), &ast])\n  } else {\n    return ast\n  }\n}\n\nfunc EVAL(ast, env)\n{\n  while (1) {\n    dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n    if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n       write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n    }\n    // Process non-list types (todo: indent right)\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n    // The else branch includes MalError. Now ast is a list.\n    lst = *ast.val\n    if (numberof(lst) == 0) return ast\n    a1 = lst(1)->val\n    if (a1 == \"def!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"let*\") {\n      let_env = env_new(&env)\n      args_lst = *(lst(2)->val)\n      for (i = 1; i <= numberof(args_lst); i += 2) {\n        var_name = args_lst(i)->val\n        var_value = EVAL(*args_lst(i + 1), let_env)\n        if (structof(var_value) == MalError) return var_value\n        env_set, let_env, var_name, var_value\n      }\n      ast = *lst(3)\n      env = let_env\n      // TCO\n    } else if (a1 == \"quote\") {\n      return *lst(2)\n    } else if (a1 == \"quasiquote\") {\n      ast = quasiquote(*lst(2)) // TCO\n    } else if (a1 == \"defmacro!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      new_value.macro = 1\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"do\") {\n      for (i = 2; i < numberof(lst); ++i) {\n        ret = EVAL(*lst(i), env)\n        if (structof(ret) == MalError) return ret\n      }\n      ast = *lst(numberof(lst))\n      // TCO\n    } else if (a1 == \"if\") {\n      cond_val = EVAL(*lst(2), env)\n      if (structof(cond_val) == MalError) return cond_val\n      if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n        if (numberof(lst) > 3) {\n          ast = *lst(4)\n        } else {\n          return MAL_NIL\n        }\n      } else {\n        ast = *lst(3)\n      }\n      // TCO\n    } else if (a1 == \"fn*\") {\n      return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3), macro=0)\n    } else {\n      fn = EVAL(*lst(1), env)\n      if (structof(fn) == MalError) return fn\n      if (is_macro(fn)) {\n        if (numberof(lst) == 1) {\n          args = []\n        } else {\n          args = lst(2:)\n        }\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n        ast = EVAL(*fn.ast, fn_env)\n        continue // TCO\n      }\n      // Evaluate arguments\n      if (numberof(lst) == 1) {\n        args = []\n      } else {\n        args = array(pointer, numberof(lst) - 1)\n        for (i = 1; i <= numberof(args); ++i) {\n          e = EVAL(*lst(1+i), env)\n          if (structof(e) == MalError) return e\n          args(i) = &e\n        }\n      }\n      // Apply\n      if (structof(fn) == MalNativeFunction) {\n        return call_core_fn(fn.val, args)\n      } else if (structof(fn) == MalFunction) {\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n        ast = *fn.ast\n        env = fn_env\n        // TCO\n      } else {\n        return MalError(message=\"Unknown function type\")\n      }\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc get_command_line(void)\n// Force quiet mode (-q) to prevent Yorick from printing its banner\n{\n  argv = get_argv()\n  return numberof(argv) > 1 ? grow([argv(1), \"-q\"], argv(2:)) : [argv(1), \"-q\"]\n}\n\nfunc prepare_argv_list(args)\n{\n  if (numberof(args) <= 1) return MalList(val=&[])\n  str_lst = array(pointer, numberof(args) - 1)\n  for (i = 2; i <= numberof(args); ++i) {\n    str_lst(i - 1) = &MalString(val=args(i))\n  }\n  return MalList(val=&str_lst)\n}\n\nrepl_env = nil\n\nfunc main(void)\n{\n  extern repl_env\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n  command_line_args = process_argv()\n  env_set, repl_env, \"*ARGV*\", prepare_argv_list(command_line_args)\n\n  // core.mal: defined using the language itself\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n  RE, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\", repl_env\n  RE, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env\n\n  if (numberof(command_line_args) > 0) {\n    RE, \"(load-file \\\"\" + command_line_args(1) + \"\\\")\", repl_env\n    return 0\n  }\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) write, format=\"Error: %s\\n\", result.message\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/step9_try.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc starts_with(seq, sym)\n{\n  return numberof(seq) == 2 && structof(*seq(1)) == MalSymbol && seq(1)->val == sym\n}\n\nfunc quasiquote_loop(seq)\n{\n  acc = MalList(val=&[])\n  for (i=numberof(seq); 0<i; --i) {\n    elt = *seq(i)\n    if (structof(elt) == MalList && starts_with(*elt.val, \"splice-unquote\")) {\n      acc = MalList(val=&[&MalSymbol(val=\"concat\"), (*elt.val)(2), &acc])\n    } else {\n      acc = MalList(val=&[&MalSymbol(val=\"cons\"), &quasiquote(elt), &acc])\n    }\n  }\n  return acc\n}\n\nfunc quasiquote(ast)\n{\n  type = structof(ast)\n  if (type == MalList) {\n    seq = *ast.val\n    if (starts_with(seq, \"unquote\")) {\n      return *seq(2)\n    } else {\n      return quasiquote_loop(seq)\n    }\n  } else if (type == MalVector) {\n    return MalList(val=&[&MalSymbol(val=\"vec\"), &quasiquote_loop(*ast.val)])\n  } else if (type == MalHashmap || type == MalSymbol) {\n    return MalList(val=&[&MalSymbol(val=\"quote\"), &ast])\n  } else {\n    return ast\n  }\n}\n\nfunc EVAL(ast, env)\n{\n  while (1) {\n    dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n    if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n       write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n    }\n    // Process non-list types (todo: indent right)\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n    // The else branch includes MalError. Now ast is a list.\n    lst = *ast.val\n    if (numberof(lst) == 0) return ast\n    a1 = lst(1)->val\n    if (a1 == \"def!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"let*\") {\n      let_env = env_new(&env)\n      args_lst = *(lst(2)->val)\n      for (i = 1; i <= numberof(args_lst); i += 2) {\n        var_name = args_lst(i)->val\n        var_value = EVAL(*args_lst(i + 1), let_env)\n        if (structof(var_value) == MalError) return var_value\n        env_set, let_env, var_name, var_value\n      }\n      ast = *lst(3)\n      env = let_env\n      // TCO\n    } else if (a1 == \"quote\") {\n      return *lst(2)\n    } else if (a1 == \"quasiquote\") {\n      ast = quasiquote(*lst(2)) // TCO\n    } else if (a1 == \"defmacro!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      new_value.macro = 1\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"try*\") {\n      ret = EVAL(*lst(2), env)\n      if (structof(ret) == MalError && numberof(lst) > 2) {\n        exc = *ret.obj\n        if (is_void(exc)) {\n          exc = MalString(val=ret.message)\n        }\n        catch_lst = *(lst(3)->val)\n        catch_env = env_new(&env)\n        env_set, catch_env, catch_lst(2)->val, exc\n        return EVAL(*catch_lst(3), catch_env)\n      } else {\n        return ret\n      }\n    } else if (a1 == \"do\") {\n      for (i = 2; i < numberof(lst); ++i) {\n        ret = EVAL(*lst(i), env)\n        if (structof(ret) == MalError) return ret\n      }\n      ast = *lst(numberof(lst))\n      // TCO\n    } else if (a1 == \"if\") {\n      cond_val = EVAL(*lst(2), env)\n      if (structof(cond_val) == MalError) return cond_val\n      if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n        if (numberof(lst) > 3) {\n          ast = *lst(4)\n        } else {\n          return MAL_NIL\n        }\n      } else {\n        ast = *lst(3)\n      }\n      // TCO\n    } else if (a1 == \"fn*\") {\n      return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3), macro=0)\n    } else {\n      fn = EVAL(*lst(1), env)\n      if (structof(fn) == MalError) return fn\n      if (is_macro(fn)) {\n        if (numberof(lst) == 1) {\n          args = []\n        } else {\n          args = lst(2:)\n        }\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n        ast = EVAL(*fn.ast, fn_env)\n        continue // TCO\n      }\n      // Evaluate arguments\n      if (numberof(lst) == 1) {\n        args = []\n      } else {\n        args = array(pointer, numberof(lst) - 1)\n        for (i = 1; i <= numberof(args); ++i) {\n          e = EVAL(*lst(1+i), env)\n          if (structof(e) == MalError) return e\n          args(i) = &e\n        }\n      }\n      // Apply\n      if (structof(fn) == MalNativeFunction) {\n        return call_core_fn(fn.val, args)\n      } else if (structof(fn) == MalFunction) {\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n        ast = *fn.ast\n        env = fn_env\n        // TCO\n      } else {\n        return MalError(message=\"Unknown function type\")\n      }\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc get_command_line(void)\n// Force quiet mode (-q) to prevent Yorick from printing its banner\n{\n  argv = get_argv()\n  return numberof(argv) > 1 ? grow([argv(1), \"-q\"], argv(2:)) : [argv(1), \"-q\"]\n}\n\nfunc prepare_argv_list(args)\n{\n  if (numberof(args) <= 1) return MalList(val=&[])\n  str_lst = array(pointer, numberof(args) - 1)\n  for (i = 2; i <= numberof(args); ++i) {\n    str_lst(i - 1) = &MalString(val=args(i))\n  }\n  return MalList(val=&str_lst)\n}\n\nrepl_env = nil\n\nfunc main(void)\n{\n  extern repl_env\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n  command_line_args = process_argv()\n  env_set, repl_env, \"*ARGV*\", prepare_argv_list(command_line_args)\n\n  // core.mal: defined using the language itself\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n  RE, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\", repl_env\n  RE, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env\n\n  if (numberof(command_line_args) > 0) {\n    RE, \"(load-file \\\"\" + command_line_args(1) + \"\\\")\", repl_env\n    return 0\n  }\n\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) {\n        exc = *result.obj\n        if (is_void(exc)) {\n          write, format=\"Error: %s\\n\", result.message\n        } else {\n          write, format=\"Error: %s\\n\", pr_str(exc, 1)\n        }\n      }\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/stepA_mal.i",
    "content": "set_path, get_env(\"YORICK_MAL_PATH\") + \":\" + get_path()\nrequire, \"reader.i\"\nrequire, \"printer.i\"\nrequire, \"core.i\"\nrequire, \"env.i\"\n\nfunc READ(str)\n{\n  return read_str(str)\n}\n\nfunc starts_with(seq, sym)\n{\n  return numberof(seq) == 2 && structof(*seq(1)) == MalSymbol && seq(1)->val == sym\n}\n\nfunc quasiquote_loop(seq)\n{\n  acc = MalList(val=&[])\n  for (i=numberof(seq); 0<i; --i) {\n    elt = *seq(i)\n    if (structof(elt) == MalList && starts_with(*elt.val, \"splice-unquote\")) {\n      acc = MalList(val=&[&MalSymbol(val=\"concat\"), (*elt.val)(2), &acc])\n    } else {\n      acc = MalList(val=&[&MalSymbol(val=\"cons\"), &quasiquote(elt), &acc])\n    }\n  }\n  return acc\n}\n\nfunc quasiquote(ast)\n{\n  type = structof(ast)\n  if (type == MalList) {\n    seq = *ast.val\n    if (starts_with(seq, \"unquote\")) {\n      return *seq(2)\n    } else {\n      return quasiquote_loop(seq)\n    }\n  } else if (type == MalVector) {\n    return MalList(val=&[&MalSymbol(val=\"vec\"), &quasiquote_loop(*ast.val)])\n  } else if (type == MalHashmap || type == MalSymbol) {\n    return MalList(val=&[&MalSymbol(val=\"quote\"), &ast])\n  } else {\n    return ast\n  }\n}\n\nfunc EVAL(ast, env)\n{\n  while (1) {\n    dbgeval = structof(env_get(env, \"DEBUG-EVAL\"))\n    if ((dbgeval != MalError) && (dbgeval != MalNil) && (dbgeval != MalFalse)) {\n       write, format=\"EVAL: %s\\n\", pr_str(ast, 1)\n    }\n    // Process non-list types (todo: indent right)\n  type = structof(ast)\n  if (type == MalSymbol) {\n    return env_get(env, ast.val)\n  } else if (type == MalList) {\n    // Proceed after this switch.\n  } else if (type == MalVector) {\n    seq = *(ast.val)\n    if (numberof(seq) == 0) return ast\n    res = array(pointer, numberof(seq))\n    for (i = 1; i <= numberof(seq); ++i) {\n      e = EVAL(*seq(i), env)\n      if (structof(e) == MalError) return e\n      res(i) = &e\n    }\n    return MalVector(val=&res)\n  } else if (type == MalHashmap) {\n    h = *(ast.val)\n    if (numberof(*h.keys) == 0) return ast\n    res = hash_new()\n    for (i = 1; i <= numberof(*h.keys); ++i) {\n      new_val = EVAL(*((*h.vals)(i)), env)\n      if (structof(new_val) == MalError) return new_val\n      hash_set, res, (*h.keys)(i), new_val\n    }\n    return MalHashmap(val=&res)\n  } else return ast\n    // The else branch includes MalError. Now ast is a list.\n    lst = *ast.val\n    if (numberof(lst) == 0) return ast\n    a1 = lst(1)->val\n    if (a1 == \"def!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"let*\") {\n      let_env = env_new(&env)\n      args_lst = *(lst(2)->val)\n      for (i = 1; i <= numberof(args_lst); i += 2) {\n        var_name = args_lst(i)->val\n        var_value = EVAL(*args_lst(i + 1), let_env)\n        if (structof(var_value) == MalError) return var_value\n        env_set, let_env, var_name, var_value\n      }\n      ast = *lst(3)\n      env = let_env\n      // TCO\n    } else if (a1 == \"quote\") {\n      return *lst(2)\n    } else if (a1 == \"quasiquote\") {\n      ast = quasiquote(*lst(2)) // TCO\n    } else if (a1 == \"defmacro!\") {\n      new_value = EVAL(*lst(3), env)\n      if (structof(new_value) == MalError) return new_value\n      new_value.macro = 1\n      return env_set(env, lst(2)->val, new_value)\n    } else if (a1 == \"try*\") {\n      ret = EVAL(*lst(2), env)\n      if (structof(ret) == MalError && numberof(lst) > 2) {\n        exc = *ret.obj\n        if (is_void(exc)) {\n          exc = MalString(val=ret.message)\n        }\n        catch_lst = *(lst(3)->val)\n        catch_env = env_new(&env)\n        env_set, catch_env, catch_lst(2)->val, exc\n        return EVAL(*catch_lst(3), catch_env)\n      } else {\n        return ret\n      }\n    } else if (a1 == \"do\") {\n      for (i = 2; i < numberof(lst); ++i) {\n        ret = EVAL(*lst(i), env)\n        if (structof(ret) == MalError) return ret\n      }\n      ast = *lst(numberof(lst))\n      // TCO\n    } else if (a1 == \"if\") {\n      cond_val = EVAL(*lst(2), env)\n      if (structof(cond_val) == MalError) return cond_val\n      if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {\n        if (numberof(lst) > 3) {\n          ast = *lst(4)\n        } else {\n          return MAL_NIL\n        }\n      } else {\n        ast = *lst(3)\n      }\n      // TCO\n    } else if (a1 == \"fn*\") {\n      return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3), macro=0)\n    } else {\n      fn = EVAL(*lst(1), env)\n      if (structof(fn) == MalError) return fn\n      if (is_macro(fn)) {\n        if (numberof(lst) == 1) {\n          args = []\n        } else {\n          args = lst(2:)\n        }\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n        ast = EVAL(*fn.ast, fn_env)\n        continue // TCO\n      }\n      // Evaluate arguments\n      if (numberof(lst) == 1) {\n        args = []\n      } else {\n        args = array(pointer, numberof(lst) - 1)\n        for (i = 1; i <= numberof(args); ++i) {\n          e = EVAL(*lst(1+i), env)\n          if (structof(e) == MalError) return e\n          args(i) = &e\n        }\n      }\n      // Apply\n      if (structof(fn) == MalNativeFunction) {\n        return call_core_fn(fn.val, args)\n      } else if (structof(fn) == MalFunction) {\n        fn_env = env_new(fn.env, binds=*fn.binds, exprs=args)\n        ast = *fn.ast\n        env = fn_env\n        // TCO\n      } else {\n        return MalError(message=\"Unknown function type\")\n      }\n    }\n  }\n}\n\nfunc PRINT(exp)\n{\n  if (structof(exp) == MalError) return exp\n  return pr_str(exp, 1)\n}\n\nfunc RE(str, env)\n{\n  return EVAL(READ(str), env)\n}\n\nfunc REP(str, env)\n{\n  return PRINT(EVAL(READ(str), env))\n}\n\nfunc get_command_line(void)\n// Force quiet mode (-q) to prevent Yorick from printing its banner\n{\n  argv = get_argv()\n  return numberof(argv) > 1 ? grow([argv(1), \"-q\"], argv(2:)) : [argv(1), \"-q\"]\n}\n\nfunc prepare_argv_list(args)\n{\n  if (numberof(args) <= 1) return MalList(val=&[])\n  str_lst = array(pointer, numberof(args) - 1)\n  for (i = 2; i <= numberof(args); ++i) {\n    str_lst(i - 1) = &MalString(val=args(i))\n  }\n  return MalList(val=&str_lst)\n}\n\nrepl_env = nil\n\nfunc main(void)\n{\n  extern repl_env\n  repl_env = env_new(pointer(0))\n\n  // core.i: defined using Yorick\n  core_symbols = h_keys(core_ns)\n  for (i = 1; i <= numberof(core_symbols); ++i) {\n    env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))\n  }\n  command_line_args = process_argv()\n  env_set, repl_env, \"*ARGV*\", prepare_argv_list(command_line_args)\n\n  // core.mal: defined using the language itself\n  RE, \"(def! *host-language* \\\"yorick\\\")\", repl_env\n  RE, \"(def! not (fn* (a) (if a false true)))\", repl_env\n  RE, \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\\\nnil)\\\")))))\", repl_env\n  RE, \"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\", repl_env\n\n  if (numberof(command_line_args) > 0) {\n    RE, \"(load-file \\\"\" + command_line_args(1) + \"\\\")\", repl_env\n    return 0\n  }\n\n  RE, \"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\", repl_env\n  stdin_file = open(\"/dev/stdin\", \"r\")\n  while (1) {\n    write, format=\"%s\", \"user> \"\n    line = rdline(stdin_file, prompt=\"\")\n    if (!line) break\n    if (strlen(line) > 0) {\n      result = REP(line, repl_env)\n      if (structof(result) == MalError) {\n        exc = *result.obj\n        if (is_void(exc)) {\n          write, format=\"Error: %s\\n\", result.message\n        } else {\n          write, format=\"Error: %s\\n\", pr_str(exc, 1)\n        }\n      }\n      else write, format=\"%s\\n\", result\n    }\n  }\n  write, \"\"\n}\n\nmain;\n"
  },
  {
    "path": "impls/yorick/tests/stepA_mal.mal",
    "content": ";; Testing basic Yorick interop\n\n(yorick-eval \"7\")\n;=>7\n\n(yorick-eval \"\\\"7\\\" + \\\"89\\\"\")\n;=>\"789\"\n\n(yorick-eval \"123 == 123\")\n;=>1\n\n(yorick-eval \"123 == 456\")\n;=>0\n\n(yorick-eval \"[7, 8, 9]\")\n;=>(7 8 9)\n\n(yorick-eval \"write, format=\\\"%s-%d\\\\x0A\\\", \\\"hello\\\", 1234; return nil;\")\n;/hello-1234\n;=>nil\n\n(yorick-eval \"extern my_global_var; my_global_var = 8; return nil;\")\n(yorick-eval \"my_global_var\")\n;=>8\n\n(yorick-eval \"a = [7, 8, 9]; return a + 10;\")\n;=>(17 18 19)\n\n(yorick-eval \"[\\\"ab\\\", \\\"cd\\\", \\\"ef\\\"] + [\\\"X\\\", \\\"Y\\\", \\\"Z\\\"]\")\n;=>(\"abX\" \"cdY\" \"efZ\")\n\n(yorick-eval \"strpart(\\\"ABCDEFGHIJ\\\", 4:7)\")\n;=>\"DEFG\"\n"
  },
  {
    "path": "impls/yorick/types.i",
    "content": "require, \"hash.i\"\n\nstruct MalError {\n  string message\n  pointer obj\n}\n\nstruct MalNil {\n  int val\n}\n\nMAL_NIL = MalNil()\n\nstruct MalTrue {\n  int val\n}\n\nMAL_TRUE = MalTrue()\n\nstruct MalFalse {\n  int val\n}\n\nMAL_FALSE = MalFalse()\n\nstruct MalNumber {\n  int val\n}\n\nfunc new_number(s)\n{\n  return MalNumber(val=atoi(s))\n}\n\nstruct MalSymbol {\n  string val\n  pointer meta\n}\n\nstruct MalString {\n  string val\n  pointer meta\n}\n\nstruct MalKeyword {\n  string val\n  pointer meta\n}\n\nstruct MalList {\n  pointer val\n  pointer meta\n}\n\nstruct MalVector {\n  pointer val\n  pointer meta\n}\n\nfunc count(obj) { return numberof(*obj.val); }\n\nfunc rest(obj) {\n  seq = count(obj) <= 1 ? [] : ((*obj.val)(2:))\n  return MalList(val=&seq)\n}\n\nstruct MalHashmap {\n  pointer val\n  pointer meta\n}\n\nfunc hashmap_obj_to_key(obj) {\n  if (structof(obj) == MalString) return \"str:\" + obj.val\n  else if (structof(obj) == MalSymbol) return \"sym:\" + obj.val\n  else if (structof(obj) == MalKeyword) return \"key:\" + obj.val\n  else error, \"Unsupported obj type for hash key\"\n}\n\nfunc hashmap_key_to_obj(key) {\n  type_str = strpart(key, 1:4)\n  val = strpart(key, 5:)\n  if (type_str == \"str:\") return MalString(val=val)\n  else if (type_str == \"sym:\") return MalSymbol(val=val)\n  else if (type_str == \"key:\") return MalKeyword(val=val)\n  else error, \"Unsupported key type\"\n}\n\nfunc array_to_hashmap(seq)\n{\n  if (numberof(seq) % 2 != 0) return MalError(message=\"Odd number of elements in hashmap\")\n  h = hash_new()\n  for (i = 1; i <= numberof(seq); i += 2) {\n    hash_set, h, hashmap_obj_to_key(*seq(i)), *seq(i + 1)\n  }\n  return MalHashmap(val=&h)\n}\n\nstruct MalNativeFunction {\n  string val\n  pointer meta\n}\n\nstruct MalFunction {\n  pointer env\n  pointer binds\n  pointer ast\n  int macro\n  pointer meta\n}\n\nstruct MalAtom {\n  pointer val\n  pointer meta\n}\n\nfunc is_macro(obj) { return (structof(obj) == MalFunction && obj.macro); }\n\nstruct MalAtomVal {\n  pointer val\n}\n\nfunc new_boolean(b) {\n  if (b) return MAL_TRUE\n  return MAL_FALSE\n}\n\nfunc equal_seq(seq_a, seq_b) {\n  if (numberof(seq_a) != numberof(seq_b)) return 0\n  for (i = 1; i <= numberof(seq_a); ++i) {\n    if (!equal(*seq_a(i), *seq_b(i))) return 0\n  }\n  return 1\n}\n\nfunc equal_hash(hm_a, hm_b) {\n  if (numberof(*hm_a.keys) != numberof(*hm_b.keys)) return 0\n  for (i = 1; i <= numberof(*hm_a.keys); ++i) {\n    key_a = (*hm_a.keys)(i)\n    val_a = *((*hm_a.vals)(i))\n    val_b = hash_get(hm_b, key_a)\n    if (is_void(val_b) || !equal(val_a, val_b)) return 0\n  }\n  return 1\n}\n\nfunc equal(a, b) {\n  ta = structof(a)\n  tb = structof(b)\n  if (ta == MalNil) return tb == MalNil\n  else if (ta == MalTrue) return tb == MalTrue\n  else if (ta == MalFalse) return tb == MalFalse\n  else if (ta == MalNumber) return tb == MalNumber && a.val == b.val\n  else if (ta == MalSymbol) return tb == MalSymbol && a.val == b.val\n  else if (ta == MalString) return tb == MalString && a.val == b.val\n  else if (ta == MalKeyword) return tb == MalKeyword && a.val == b.val\n  else if (ta == MalList || ta == MalVector) {\n    return (tb == MalList || tb == MalVector) && equal_seq(*(a.val), *(b.val))\n  }\n  else if (ta == MalHashmap) return tb == MalHashmap && equal_hash(*a.val, *b.val)\n  else return 0\n}\n\nfunc streplaceall(s, pattern, subst)\n{\n  return streplace(s, strfind(pattern, s, n=999), subst)\n}\n"
  },
  {
    "path": "impls/zig/Dockerfile",
    "content": "FROM ubuntu:24.04\nMAINTAINER Joel Martin <github@martintribe.org>\n\n##########################################################\n# General requirements for testing or common across many\n# implementations\n##########################################################\n\nRUN apt-get -y update\n\n# Required for running tests\nRUN apt-get -y install make python3\nRUN ln -fs /usr/bin/python3 /usr/local/bin/python\n\nRUN mkdir -p /mal\nWORKDIR /mal\n\n##########################################################\n# Specific implementation requirements\n##########################################################\nRUN apt-get -y install ca-certificates curl gcc libc6-dev libpcre3-dev libreadline-dev xz-utils\n\nRUN curl https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz | tar -xJC/opt\nRUN ln -fst/usr/local/bin /opt/zig-linux-x86_64-0.13.0/zig\n\nENV HOME /mal\n"
  },
  {
    "path": "impls/zig/Makefile",
    "content": "\nSTEPS = step0_repl step1_read_print step2_eval step3_env step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal\n\nall: $(STEPS)\n\nzig_opts += --release=safe\nzig_opts += -Doptimize=Debug\n$(STEPS):\n\tzig build $(zig_opts) -Dname=$@ -Droot_source_file=$@.zig\n\n.PHONY: all $(STEPS) clean\n\nclean:\n\trm -fr .zig-cache/ zig-out/\n\trm -f *~\n"
  },
  {
    "path": "impls/zig/README",
    "content": "debug_alloc in types.zig may help with reference counting.\n\n\nTODO Simplify the printer with the new reader functions in the zig\nlibrary.\n\n\nNOTE Before implementing any optimization or optional fix that would\nincrease the complexity, please take into account that someone has to\nmaintain the code, and the zig language evolves quickly.\n\nSome memory leaks are probably already present, especially when an\nerror interrupts the normal execution flow.\n\nExamples of things that are deliberately not implemented...\n  * TCO for try*\n  * preallocate integers between 0 and 100 at startup\n  * use ArrayList.ensureTotalCapacityPrecise/HashMap.ensureTotalCapacity\n    after most calls to new_list/vector/map.\n  * store symbols in a global hash map,\n  * implement lists/vectors as slices/cons cells/whatever\n  * deallocate cyclic structures not detected by reference counting like\n    (let* (f (fn* () nil)))\n    (def! a (atom 2)) (def! v [a]) (reset! a v)\n"
  },
  {
    "path": "impls/zig/build.zig",
    "content": "const Builder = @import(\"std\").Build;\n\npub fn build(b: *Builder) void {\n\n    //  Two options select the built step.\n\n    const name = b.option([]const u8, \"name\", \"step name (without .zig)\")\n                 orelse \"stepA_mal\";\n\n    const root_source_file = b.path(\n        b.option([]const u8, \"root_source_file\", \"step name (with .zig)\")\n        orelse \"stepA_mal.zig\");\n\n    const exe = b.addExecutable(.{\n        .name = name,\n        .root_source_file = root_source_file,\n        .target = b.standardTargetOptions(.{}),\n        .optimize = b.standardOptimizeOption(.{}),\n    });\n\n    exe.linkSystemLibrary(\"c\");\n    exe.linkSystemLibrary(\"pcre\");\n    exe.linkSystemLibrary(\"readline\");\n    b.default_step.dependOn(&exe.step);\n    b.installArtifact(exe);\n}\n"
  },
  {
    "path": "impls/zig/core.zig",
    "content": "const std = @import(\"std\");\n\nconst Allocator = std.heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst printer = @import(\"printer.zig\");\nconst reader = @import(\"reader.zig\");\nconst getline_prompt = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\n\nconst MalError = @import(\"error.zig\").MalError;\n\nconst hmap = @import(\"hmap.zig\");\n\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst MalHashMap = @import(\"hmap.zig\").MalHashMap;\n\n//  Set by the step file at startup.\npub var apply_function: *const fn(f: MalType, args: []*MalType) MalError!*MalType = undefined;\n\nconst safeAdd = @import(\"std\").math.add;\nconst safeSub = @import(\"std\").math.sub;\nconst safeMul = @import(\"std\").math.mul;\nconst safeDivFloor = @import(\"std\").math.divFloor;\n\nconst stdout_file = std.io.getStdOut();\nconst throw = @import(\"error.zig\").throw;\n\nfn int_plus(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeAdd(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_minus(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeSub(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_mult(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeMul(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_div(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeDivFloor(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_lt(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    return MalType.new_bool((try a1.as_int()) < (try a2.as_int()));\n}\n\nfn int_leq(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    return MalType.new_bool((try a1.as_int()) <= (try a2.as_int()));\n}\n\nfn int_gt(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    return MalType.new_bool((try a1.as_int()) > (try a2.as_int()));\n}\n\nfn int_geq(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    return MalType.new_bool((try a1.as_int()) >= (try a2.as_int()));\n}\n\nfn _linked_list_equality(l1: []const *MalType, l2:[]const  *MalType) bool {\n    if(l1.len != l2.len) return false;\n    for(l1, l2) |m1, m2| {\n        if(! _equality(m1.*, m2.*)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nfn _hashmap_equality(h1: MalHashMap, h2: MalHashMap) bool {\n    if(h1.count() != h2.count()) {\n        return false;\n    }\n\n    var iterator = h1.iterator();\n    while(iterator.next()) |pair| {\n        const optional_val = h2.get(pair.key_ptr.*);\n        if(optional_val) |val| {\n            const el_cmp = _equality(pair.value_ptr.*.*, val.*);\n            if(! el_cmp) {\n                return false;\n            }\n        }\n        else {\n            return false;\n        }\n    }\n    return true;\n}\n\nfn equality(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    return MalType.new_bool(_equality(a1.*, a2.*));\n}\n\nfn _equality(a1: MalType, a2: MalType) bool {\n    switch(a1) {\n        .Nil => {\n            switch(a2) {\n                .Nil => return true,\n                else => return false,\n            }\n        },\n        .False => {\n            switch(a2) {\n                .False => return true,\n                else => return false,\n            }\n        },\n        .True => {\n            switch(a2) {\n                .True => return true,\n                else => return false,\n            }\n        },\n        .Int => |l1| {\n            switch(a2) {\n                .Int => |l2| return l1.data == l2.data,\n                else => return false,\n            }\n        },\n        .String => |s1| {\n            switch(a2) {\n                .String => |s2| return string_eql(s1.data, s2.data),\n                else => return false,\n            }\n        },\n        .Symbol => |s1| {\n            switch(a2) {\n                .Symbol => |s2| return string_eql(s1.data, s2.data),\n                else => return false,\n            }\n        },\n        .Keyword => |s1| {\n            switch(a2) {\n                .Keyword => |s2| return string_eql(s1.data, s2.data),\n                else => return false,\n            }\n        },\n        .List, .Vector => |l1| {\n            switch(a2) {\n                .List, .Vector => |l2| return _linked_list_equality(\n                    l1.data.items, l2.data.items),\n                else => return false,\n            }\n        },\n        .HashMap => |h1| {\n            switch(a2) {\n                .HashMap => |h2| return _hashmap_equality(h1.data, h2.data),\n                else => return false,\n            }\n        },\n        else => {\n            return false;\n        },\n    }\n}\n\nfn list(args: []*MalType) !*MalType {\n    const new_mal = try MalType.new_list();\n    errdefer new_mal.decref();\n    for(args) |x| {\n        try new_mal.List.data.append(Allocator, x);\n        x.incref();\n    }\n    return new_mal;\n}\n\nfn vector(args: []*MalType) !*MalType {\n    const new_mal = try MalType.new_vector();\n    errdefer new_mal.decref();\n    for(args) |x| {\n        try new_mal.Vector.data.append(Allocator, x);\n        x.incref();\n    }\n    return new_mal;\n}\n\nfn map(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const func_mal = args[0];\n    const args_mal = args[1];\n    var to_map_ll = try args_mal.as_slice();\n    const new_list = try MalType.new_list();\n    errdefer new_list.decref();\n    for(0..to_map_ll.len) |i| {\n        const new_mal = try apply_function(func_mal.*, to_map_ll[i..i+1]);\n        try new_list.List.data.append(Allocator, new_mal);\n    }\n    return new_list;\n}\n\nfn is_list(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .List => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_vector(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .Vector => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_string(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .String => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_number(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .Int => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_fn(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const is_function = switch(a1.*) {\n        .FnCore => true,\n        .Func => |func_data| ! func_data.is_macro,\n        else => false,\n    };\n    return MalType.new_bool(is_function);\n}\n\nfn is_macro(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const is_func_and_macro = switch(a1.*) {\n        .Func => |data| data.is_macro,\n        else => false,\n    };\n    return MalType.new_bool(is_func_and_macro);\n}\n\nfn empty(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const slice = try a1.as_slice();\n    return MalType.new_bool(slice.len == 0);\n}\n\nfn prn(args: []*MalType) MalError!*MalType {\n    try printer.n_stdout(args, true, true);\n    try stdout_file.writeAll(\"\\n\");\n    const mal = &MalType.NIL;\n    return mal;\n}\n\nfn println(args: []*MalType) !*MalType {\n    try printer.n_stdout(args, false, true);\n    try stdout_file.writeAll(\"\\n\");\n    const mal = &MalType.NIL;\n    return mal;\n}\n\nfn str(args: []*MalType) !*MalType {\n    const items = try printer.print_mal_to_string(args, false, false);\n    errdefer Allocator.free(items);\n    return MalType.new_string(items, false);\n}\n\nfn pr_str(args: []*MalType) !*MalType {\n    const s = try printer.print_mal_to_string(args, true, true);\n    errdefer Allocator.free(s);\n    return MalType.new_string(s, false);\n}\n\nfn slurp(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const path = try a1.as_string();\n    const dir = std.fs.cwd();\n    const items = try dir.readFileAlloc(Allocator, path, 10000);\n    return MalType.new_string(items, false);\n}\n\nfn atom(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const result = try MalType.new_atom(a1);\n    a1.incref();\n    return result;\n}\n\nfn is_atom(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    return switch(args[0].*) {\n        .Atom => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn deref(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    switch(a1.*) {\n        .Atom => |atom_val| {\n            atom_val.data.incref();\n            return atom_val.data;\n        },\n        else => return MalError.TypeError,\n    }\n}\n\nfn atom_reset(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    switch(a1.*) {\n        .Atom => |*atom_val| {\n            atom_val.data.decref();\n            atom_val.data = a2;\n            //  incref for the atom and for the result\n            a2.incref();\n            a2.incref();\n            return a2;\n        },\n        else => return MalError.TypeError,\n    }\n}\n\nfn atom_swap(args: []*MalType) !*MalType {\n    const n = args.len;\n    if(n < 2) return MalError.ArgError;\n\n    const atom_val = switch(args[0].*) {\n        .Atom => |*a| a,\n        else => return MalError.TypeError,\n    };\n\n    var new_args = try Allocator.alloc(*MalType, args.len - 1);\n    defer Allocator.free(new_args);\n    var i:usize = 0;\n    new_args[i] = atom_val.data; i+=1;\n    for(args[2..args.len]) |x| {\n        new_args[i] = x;\n        i += 1;\n    }\n    std.debug.assert(i == new_args.len);\n\n    const new_mal = try apply_function(args[1].*, new_args);\n    atom_val.data.decref(); // after the computation\n    atom_val.data = new_mal;\n    new_mal.incref();\n    return new_mal;\n}\n\nfn vec(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    switch(a1.*) {\n        .List => |l| {\n            const result = try MalType.new_vector();\n            errdefer result.decref();\n            for(l.data.items) |x| {\n                try result.Vector.data.append(Allocator, x);\n                x.incref();\n            }\n            return result;\n        },\n        .Vector => {\n            a1.incref();\n            return a1;\n        },\n        else => return MalError.TypeError,\n    }\n}\n\nfn cons(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const old_ll = try a2.as_slice();\n    const new_list = try MalType.new_list();\n    errdefer new_list.decref();\n    try new_list.List.data.append(Allocator, a1);\n    a1.incref();\n    for(old_ll) |x| {\n        try new_list.List.data.append(Allocator, x);\n        x.incref();\n    }\n    return new_list;\n}\n\npub fn concat(args: []*MalType) !*MalType {\n    const new_mal = try MalType.new_list();\n    errdefer new_mal.decref();\n    for(args) |x| {\n        for(try x.as_slice()) |y| {\n            try new_mal.List.data.append(Allocator, y);\n            y.incref();\n        }\n    }\n    return new_mal;\n}\n\nfn rest(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const new_mal = try MalType.new_list();\n    errdefer new_mal.decref();\n    switch(a1.*) {\n        .List, .Vector => |l| {\n            const old_list = l.data.items;\n            if(old_list.len != 0) {\n                for(l.data.items[1..]) |x| {\n                    try new_mal.List.data.append(Allocator, x);\n                    x.incref();\n                }\n            }\n        },\n        .Nil => { },\n        else => return MalError.TypeError,\n    }\n    return new_mal;\n}\n\nfn nth(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const l = try a1.as_slice();\n    const i = try a2.as_int();\n    const pos: usize = @intCast(i);\n    if(pos < 0 or l.len <= pos)  {\n        return MalError.OutOfBounds;\n    }\n    const result = l[pos];\n    result.incref();\n    return result;\n}\n\nfn first(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    switch(a1.*) {\n        .List, .Vector => |l| {\n            if(l.data.items.len == 0) return &MalType.NIL;\n            const result = l.data.items[0];\n            result.incref();\n            return result;\n        },\n        .Nil => return &MalType.NIL,\n        else => return MalError.TypeError,\n    }\n}\n\nfn is_nil(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .Nil => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_true(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .True => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_false(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .False => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_symbol(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .Symbol => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_keyword(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .Keyword => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_map(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .HashMap => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn is_sequential(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return switch(a1.*) {\n        .List, .Vector => &MalType.TRUE,\n        else => &MalType.FALSE,\n    };\n}\n\nfn symbol(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const string = try a1.as_string();\n    return MalType.new_symbol(string, true);\n}\n\npub fn hash_map(args: []*MalType) !*MalType {\n    const new_mal = try MalType.new_hashmap();\n    errdefer new_mal.decref();\n    try hmap.map_insert_from_kvs(&new_mal.HashMap.data, args);\n    return new_mal;\n}\n\npub fn hash_map_assoc(args: []*MalType) !*MalType {\n    if(args.len < 1) return MalError.ArgError;\n    const a1 = args[0];\n    const new_mal = try MalType.new_hashmap();\n    errdefer new_mal.decref();\n    const base_hmap = try a1.as_map();\n    try hmap.map_insert_from_map(&new_mal.HashMap.data, base_hmap);\n    try hmap.map_insert_from_kvs(&new_mal.HashMap.data, args[1..]);\n    return new_mal;\n}\n\npub fn hash_map_dissoc(args: []*MalType) !*MalType {\n    if(args.len < 1) return MalError.ArgError;\n    const a1 = args[0];\n    const new_mal = try MalType.new_hashmap();\n    errdefer new_mal.decref();\n    const base_hmap = try a1.as_map();\n    try hmap.map_insert_from_map(&new_mal.HashMap.data, base_hmap);\n    for(args[1..]) |k| {\n        switch(k.*) {\n            .Keyword, .String => {\n                if(new_mal.HashMap.data.fetchRemove(k)) |old| {\n                    old.key.decref();\n                    old.value.decref();\n                }\n            },\n            else => return MalError.TypeError,\n        }\n    }\n    return new_mal;\n}\n\nfn hash_map_get(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const hm = switch(a1.*) {\n        .HashMap => |m| m.data,\n        .Nil => return &MalType.NIL,\n        else => return MalError.TypeError,\n    };\n    switch(a2.*) {\n        .Keyword, .String => {},\n        else => return MalError.TypeError,\n    }\n    if(hm.get(a2)) |value| {\n        value.incref();\n        return value;\n    }\n    return &MalType.NIL;\n}\n\nfn hash_map_contains(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    switch(a2.*) {\n        .Keyword, .String => {\n            const hm = try a1.as_map();\n            return MalType.new_bool(hm.contains(a2));\n        },\n        else => return MalError.TypeError,\n    }\n}\n\nfn hash_map_keys(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const hm = try a1.as_map();\n    const new_mal = try MalType.new_list();\n    errdefer new_mal.decref();\n    var iterator = hm.keyIterator();\n    while(iterator.next()) |key_mal| {\n        try new_mal.List.data.append(Allocator, key_mal.*);\n        key_mal.*.incref();\n    }\n    return new_mal;\n}\n\nfn hash_map_vals(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const hm = try a1.as_map();\n    const new_mal = try MalType.new_list();\n    errdefer new_mal.decref();\n    var iterator = hm.valueIterator();\n    while(iterator.next()) |val| {\n        try new_mal.List.data.append(Allocator, val.*);\n        val.*.incref();\n    }\n    return new_mal;\n}\n\nfn sequence_length(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const len = switch(a1.*) {\n        .List, .Vector => |l| l.data.items.len,\n        .String => |s| s.data.len,\n        .Nil => 0,\n        else => return MalError.TypeError,\n    };\n    return MalType.new_int(@intCast(len));\n}\n\nfn keyword(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    switch(a1.*) {\n        .String => |s| {\n            return MalType.new_keyword(s.data, true);\n        },\n        .Keyword => {\n            a1.incref();\n            return a1;\n        },\n        else => return MalError.TypeError,\n    }\n}\n\nfn core_readline(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const prompt = try a1.as_string();\n    const optional_read_line = try getline_prompt(prompt);\n    if(optional_read_line) |read_line| {\n        return MalType.new_string(read_line, false);\n    }\n    return &MalType.NIL;\n}\n\nfn time_ms(args: []*MalType) !*MalType {\n    if(args.len != 0) return MalError.ArgError;\n    const itime = std.time.milliTimestamp();\n    return try MalType.new_int(@intCast(itime));\n}\n\nfn meta(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const result = switch(a1.*) {\n        .List, .Vector => |l| l.metadata,\n        .FnCore        => |l| l.metadata,\n        .Func          => |l| l.metadata,\n        .HashMap       => |l| l.metadata,\n        else           => return MalError.TypeError,\n    };\n    result.incref();\n    return result;\n}\n\nfn with_meta(args: []*MalType) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    switch(a1.*) {\n        .List => |l| {\n            const new_mal = try MalType.new_list();\n            errdefer new_mal.decref();\n            for(l.data.items) |x| {\n                try new_mal.List.data.append(Allocator, x);\n                x.incref();\n            }\n            new_mal.List.metadata = a2;\n            a2.incref();\n            return new_mal;\n        },\n        .Vector => |l| {\n            const new_mal = try MalType.new_vector();\n            errdefer new_mal.decref();\n            for(l.data.items) |x| {\n                try new_mal.Vector.data.append(Allocator, x);\n                x.incref();\n            }\n            new_mal.Vector.metadata = a2;\n            a2.incref();\n            return new_mal;\n        },\n        .FnCore => |l| {\n            const new_mal = try MalType.newFnCore(l.data);\n            new_mal.FnCore.metadata = a2;\n            a2.incref();\n            return new_mal;\n        },\n        .Func => |l| {\n            const new_mal = try MalType.newFunc(l.arg_list, l.body,\n                                                l.environment);\n            l.arg_list.incref();\n            l.body.incref();\n            l.environment.incref();\n            new_mal.Func.metadata = a2;\n            a2.incref();\n            return new_mal;\n        },\n        .HashMap => |l| {\n            const new_mal = try MalType.new_hashmap();\n            errdefer new_mal.decref();\n            try hmap.map_insert_from_map(&new_mal.HashMap.data, l.data);\n            new_mal.HashMap.metadata = a2;\n            a2.incref();\n            return new_mal;\n        },\n        else => return MalError.TypeError,\n    }\n}\n\nfn seq(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    switch(a1.*) {\n        .List => |l| {\n            if(l.data.items.len == 0) return &MalType.NIL;\n            a1.incref();\n            return a1;\n        },\n        .Vector => |l| {\n            if(l.data.items.len == 0) return &MalType.NIL;\n            const mal_copy = try MalType.new_list();\n            errdefer mal_copy.decref();\n            for(l.data.items) |x| {\n                try mal_copy.List.data.append(Allocator, x);\n                x.incref();\n            }\n            return mal_copy;\n        },\n        .String => |s| {\n            if(s.data.len == 0) return &MalType.NIL;\n            const new_list = try MalType.new_list();\n            errdefer new_list.decref();\n            for(s.data) |x| {\n                const one_char = try Allocator.alloc(u8, 1);\n                one_char[0] = x;\n                const new_char = try MalType.new_string(one_char, false);\n                errdefer new_char.decref();\n                try new_list.List.data.append(Allocator, new_char);\n            }\n            return new_list;\n        },\n        .Nil => {\n            return &MalType.NIL;\n        },\n        else => {\n            return MalError.TypeError;\n        }\n    }\n}\n\npub fn conj(args: []*MalType) !*MalType {\n    if(args.len == 0) return MalError.ArgError;\n    const container = args[0];\n    switch(container.*) {\n        .List => |l| {\n            const return_mal = try MalType.new_list();\n            errdefer return_mal.decref();\n            for(1..args.len) |j| {\n                const new_item = args[args.len-j];\n                try return_mal.List.data.append(Allocator, new_item);\n                new_item.incref();\n            }\n            for(l.data.items) |x| {\n                try return_mal.List.data.append(Allocator, x);\n                x.incref();\n            }\n            return return_mal;\n        },\n        .Vector => |l|{\n            const return_mal = try MalType.new_vector();\n            errdefer return_mal.decref();\n            for(l.data.items) |x| {\n                try return_mal.Vector.data.append(Allocator, x);\n                x.incref();\n            }\n            for(args[1..]) |x| {\n                try return_mal.Vector.data.append(Allocator, x);\n                x.incref();\n            }\n            return return_mal;\n        },\n        else => return MalError.ArgError,\n    }\n}\n\nfn read_string(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    const str_to_eval = try a1.as_string();\n    var read = try reader.read_str(str_to_eval);\n    return reader.read_form(&read);\n}\n\npub fn do_apply(args: []*MalType) !*MalType {\n    if(args.len < 2) return MalError.ArgError;\n    const a1 = args[0];\n    const last = args[args.len - 1];\n    const more_args = try last.as_slice();\n    var fargs = try Allocator.alloc(*MalType, args.len + more_args.len - 2);\n    defer Allocator.free(fargs);\n    var i:usize = 0;\n    for(args[1..args.len-1]) |x| { fargs[i] = x; i+=1; }\n    for(more_args)           |x| { fargs[i] = x; i+=1; }\n    std.debug.assert(i == fargs.len);\n    return apply_function(a1.*, fargs);\n}\n\npub fn core_throw(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return throw(a1);\n}\n\npub const CorePair = struct {\n    name: []const u8,\n    func: *const fn(args: []*MalType) MalError!*MalType,\n};\n\npub const core_namespace = [_]CorePair {\n    .{ .name = \"+\", .func = &int_plus },\n    .{ .name = \"-\", .func = &int_minus },\n    .{ .name = \"*\", .func = &int_mult },\n    .{ .name = \"/\", .func = &int_div },\n    .{ .name = \"<\",  .func = &int_lt },\n    .{ .name = \"<=\", .func = &int_leq },\n    .{ .name = \">\",  .func = &int_gt },\n    .{ .name = \">=\", .func = &int_geq },\n    .{ .name = \"=\",  .func = &equality },\n    .{ .name = \"list?\", .func = &is_list },\n    .{ .name = \"vector?\", .func = &is_vector },\n    .{ .name = \"count\", .func = &sequence_length },\n    .{ .name = \"list\",  .func = &list, },\n    .{ .name = \"vector\",  .func = &vector, },\n    .{ .name = \"map\",  .func = &map },\n    .{ .name = \"empty?\", .func = &empty },\n    .{ .name = \"prn\", .func = &prn },\n    .{ .name = \"println\", .func = &println },\n    .{ .name = \"pr-str\", .func = &pr_str },\n    .{ .name = \"str\", .func = &str },\n    .{ .name = \"slurp\", .func = &slurp },\n    .{ .name = \"atom\", .func = &atom },\n    .{ .name = \"atom?\", .func = &is_atom },\n    .{ .name = \"deref\", .func = &deref },\n    .{ .name = \"reset!\", .func = &atom_reset },\n    .{ .name = \"swap!\", .func = &atom_swap },\n    .{ .name = \"vec\", .func = &vec },\n    .{ .name = \"cons\", .func = &cons },\n    .{ .name = \"concat\", .func = &concat },\n    .{ .name = \"rest\", .func = &rest },\n    .{ .name = \"nth\", .func = &nth },\n    .{ .name = \"first\", .func = &first },\n    .{ .name = \"nil?\", .func = &is_nil },\n    .{ .name = \"true?\", .func = &is_true },\n    .{ .name = \"false?\", .func = &is_false },\n    .{ .name = \"symbol\", .func = &symbol },\n    .{ .name = \"symbol?\", .func = &is_symbol },\n    .{ .name = \"keyword?\", .func = &is_keyword },\n    .{ .name = \"map?\", .func = &is_map },\n    .{ .name = \"sequential?\", .func = &is_sequential },\n    .{ .name = \"apply\", .func = &do_apply },\n    .{ .name = \"hash-map\", .func = &hash_map },\n    .{ .name = \"assoc\", .func = &hash_map_assoc },\n    .{ .name = \"dissoc\", .func = &hash_map_dissoc },\n    .{ .name = \"get\", .func = &hash_map_get },\n    .{ .name = \"contains?\", .func = &hash_map_contains },\n    .{ .name = \"keys\", .func = &hash_map_keys },\n    .{ .name = \"vals\", .func = &hash_map_vals },\n    .{ .name = \"keyword\", .func = &keyword },\n    .{ .name = \"read-string\", .func = &read_string },\n    .{ .name = \"readline\", .func = &core_readline },\n    .{ .name = \"time-ms\", .func = &time_ms },\n    .{ .name = \"meta\", .func = &meta },\n    .{ .name = \"with-meta\", .func = &with_meta },\n    .{ .name = \"fn?\", .func = &is_fn },\n    .{ .name = \"string?\", .func = &is_string },\n    .{ .name = \"number?\", .func = &is_number },\n    .{ .name = \"macro?\", .func = &is_macro },\n    .{ .name = \"seq\", .func = &seq },\n    .{ .name = \"conj\", .func = &conj },\n    .{ .name = \"throw\", .func = &core_throw },\n};\n"
  },
  {
    "path": "impls/zig/env.zig",
    "content": "const std = @import(\"std\");\nconst warn = std.log.warn;\nconst allocator = std.heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalHashMap = @import(\"hmap.zig\").MalHashMap;\nconst MalError = @import(\"error.zig\").MalError;\nconst hash_map = @import(\"hmap.zig\");\nconst debug_alloc = @import(\"types.zig\").debug_alloc;\n\npub const Env = struct {\n    outer: ?*Env,\n    data: MalHashMap,\n    refcount: i32 = 1,\n\n    pub fn new_root() Env {\n        return .{.outer = null, .data = .{}};\n    }\n\n    pub fn new(outer: *Env) !*Env {\n        //  The caller is in charge of incremeting the reference count\n        //  for outer if necessary.\n        const env = try allocator.create(Env);\n        env.* = .{ .outer = outer, .data = .{} };\n        if(debug_alloc) warn(\"Env: new {any}\", .{env});\n        return env;\n    }\n\n    pub fn incref(env: *Env) void {\n        if(debug_alloc) {\n            warn(\"Env: incref {any}\", .{env});\n        }\n        env.refcount += 1;\n        // std.debug.assert(env.refcount < 100);\n    }\n\n    pub fn decref(env: *Env) void {\n        var e = env;\n        while (true) {\n            if(debug_alloc) {\n                warn(\"Env: decref {any}\", .{e});\n                e.print_keys();\n            }\n            std.debug.assert (0 < e.refcount);\n            e.refcount -= 1;\n            if(0 < e.refcount) {\n                break;\n            }\n            if(debug_alloc) {\n                warn(\"Env: FREE {any}\", .{e});\n            }\n            const old = e;\n            if(e.outer) |outer| {\n                e = outer;\n            } else {\n                warn(\"INTERNAL ERROR: repl-env should never reach a 0 refcount.\", .{});\n                break;\n            }\n            hash_map.map_destroy(&old.data);\n            allocator.destroy(old);\n        }\n    }\n\n    //  Incref both the key and value.\n    pub fn set(env: *Env, key: *MalType, value: *MalType) !void {\n        //  The caller is in charge of incremeting the reference count\n        //  for the value if necessary.\n        switch (key.*) {\n            .Symbol => {\n                if(debug_alloc) {\n                    warn(\"Env: set {s} {any}\", .{key.Symbol.data, key});\n                }\n                try hash_map.map_insert_incref_key(&env.data, key, value);\n            },\n            else => return MalError.ArgError,\n        }\n    }\n\n    pub fn get(env: Env, key: *MalType) !?*MalType {\n        // The result is not increfed().\n        switch (key.*) {\n            .Symbol => {\n                if(debug_alloc) {\n                    warn(\"Env: get {s} {any}\", .{key.Symbol.data, key});\n                }\n                var e: * const Env = &env;\n                while(true) {\n                    if(e.data.get(key)) |value| {\n                        return value;\n                    }\n                    e = e.outer orelse return null;\n                }\n            },\n            else => return MalError.KeyError,\n        }\n    }\n    \n    pub fn print_keys(env: Env) void {\n        var it = env.data.keyIterator();\n        var count: i32 = 5;\n        while (it.next()) |key| {\n            warn(\"  key={s},\", .{key.*.Symbol.data});\n            count -= 1;\n            if(count <= 0) {\n                warn(\"  ...\", .{});\n                break;\n            }\n        }\n    }\n};\n"
  },
  {
    "path": "impls/zig/error.zig",
    "content": "const assert = @import(\"std\").debug.assert;\nconst MalType = @import(\"types.zig\").MalType;\n\npub const MalError = error {\n    SystemError,\n    ApplyError,\n    KeyError,\n    ThrownError,\n    TypeError,\n    ArgError,\n    Overflow,\n    DivisionByZero,\n    OutOfBounds,\n\n    OutOfMemory,\n\n    InvalidCharacter,\n\n    DiskQuota,\n    FileTooBig,\n    InputOutput,\n    NoSpaceLeft,\n    DeviceBusy,\n    InvalidArgument,\n    AccessDenied,\n    BrokenPipe,\n    SystemResources,\n    OperationAborted,\n    NotOpenForWriting,\n    LockViolation,\n    WouldBlock,\n    ConnectionResetByPeer,\n    Unexpected,\n\n    InvalidUtf8,\n    SharingViolation,\n    PathAlreadyExists,\n    FileNotFound,\n    PipeBusy,\n    NameTooLong,\n    InvalidWtf8,\n    BadPathName,\n    NetworkNotFound,\n    AntivirusInterference,\n    SymLinkLoop,\n    ProcessFdQuotaExceeded,\n    SystemFdQuotaExceeded,\n    NoDevice,\n    IsDir,\n    NotDir,\n    FileLocksNotSupported,\n    FileBusy,\n    Unseekable,\n    ConnectionTimedOut,\n    NotOpenForReading,\n    SocketNotConnected,\n};\n\nvar error_data: ?*MalType = null;\n\npub fn throw(mal: *MalType) MalError {\n    assert(error_data == null);\n    error_data = mal;\n    mal.incref();\n    return MalError.ThrownError;\n}\n\npub fn get_error_data() ?*MalType {\n    defer error_data = null;\n    return error_data;\n}\n"
  },
  {
    "path": "impls/zig/hmap.zig",
    "content": "const warn = @import(\"std\").log.warn;\nconst allocator = @import(\"std\").heap.c_allocator;\n\nconst hash_map = @import(\"std\").hash_map;\nconst MalType = @import(\"types.zig\").MalType;\nconst string_eql = @import(\"std\").hash_map.eqlString;\nconst MalError = @import(\"error.zig\").MalError;\nconst debug_alloc = @import(\"types.zig\").debug_alloc;\n\nconst Context = struct {\n\n    pub fn hash(_: @This(), key: *MalType) u64 {\n        return switch(key.*) {\n            .Symbol, .String, .Keyword => |s| hash_map.hashString(s.data),\n            else                       => unreachable,\n        };\n    }\n\n    pub fn eql(_: @This(), ma: *MalType, mb: *MalType) bool {\n        return switch(ma.*) {\n            .Keyword => |a| switch(mb.*) {\n                .Keyword => |b| string_eql(a.data, b.data),\n                else     =>    false,\n            },\n            .String => |a| switch(mb.*) {\n                .String => |b| string_eql(a.data, b.data),\n                else    =>     false,\n            },\n            .Symbol => |a| switch(mb.*) {\n                .Symbol => |b| string_eql(a.data, b.data),\n                else    =>     false,\n            },\n            else        =>     unreachable,\n        };\n    }\n};\n\n\npub const MalHashMap = hash_map.HashMapUnmanaged(*MalType, *MalType,\n                                                 Context, 80);\n\npub fn map_destroy(hashmap: *MalHashMap) void {\n    if (debug_alloc) {\n        warn(\"destroy_map_elements\", .{});\n    }\n    var iterator = hashmap.iterator();\n    while(iterator.next()) |pair| {\n        pair.key_ptr.*.decref();\n        pair.value_ptr.*.decref();\n    }\n    hashmap.deinit(allocator);\n}\n\n// If the key was present in the map, the implementation reuses it,\n// instead of the new one.  So we need to increment the reference\n// counting for the key here.\n// The ref count of the value is not incremented here.\npub fn map_insert_incref_key(hashmap: *MalHashMap, key: *MalType, value: *MalType) !void {\n    switch(key.*) {\n        .String, .Keyword, .Symbol => {\n            if (try hashmap.fetchPut(allocator, key, value)) |old| {\n                // No change in the key reference count.\n                old.value.decref();\n            } else {\n                key.incref();\n            }\n        },\n        else => return MalError.TypeError,\n    }\n}\n\npub fn map_insert_from_map(hashmap: *MalHashMap, from: MalHashMap) !void {\n    var iterator = from.iterator();\n    while(iterator.next()) |pair| {\n        const key = pair.key_ptr.*;\n        const value = pair.value_ptr.*;\n        try map_insert_incref_key(hashmap, key, value);\n        value.incref();\n    }\n}\n\npub fn map_insert_from_kvs(hashmap: *MalHashMap, kvs: []const *MalType) !void {\n    if (kvs.len % 2 == 1) {\n        return MalError.TypeError;\n    }\n    for (0..kvs.len/2) |i| {\n        const key = kvs[2*i];\n        const value = kvs[2*i+1];\n        try map_insert_incref_key(hashmap, key, value);\n        value.incref();\n    }\n}\n"
  },
  {
    "path": "impls/zig/linked_list.zig",
    "content": "const allocator = @import(\"std\").heap.c_allocator;\nconst ArrayListUnmanaged = @import(\"std\").ArrayListUnmanaged;\nconst MalType = @import(\"types.zig\").MalType;\n\n//  The name is poorly choosen but historical.\n\npub const MalLinkedList = ArrayListUnmanaged(*MalType);\n\npub fn list_destroy(ll: *MalLinkedList) void {\n    for(ll.items) |x|\n        x.decref();\n    ll.deinit(allocator);\n}\n"
  },
  {
    "path": "impls/zig/printer.zig",
    "content": "const std = @import(\"std\");\nconst stdout_writer = std.io.getStdOut().writer();\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\n\n// TODO fix emacs highlighting, remove this\nconst backslash =\n    \\\\\\\n;\n\npub fn one_stdout(mal: MalType) !void {\n    try print_to_buffer(mal, stdout_writer, true);\n}\n\npub fn n_stdout(args: []const *MalType, readably: bool, sep: bool) !void {\n    try n_writer(stdout_writer, args, readably, sep);\n}\n\nfn n_writer(rb: anytype, args: []const *MalType, readable: bool, sep: bool) !void {\n    for (args, 0..) |node, idx| {\n        if(0 < idx and sep) {\n            try rb.writeAll(\" \");\n        }\n        try print_to_buffer(node.*, rb, readable);\n    }\n}\n\npub fn print_mal_to_string(args: []const *MalType, readable: bool, sep: bool) ![]u8 {\n    var rb = std.ArrayListUnmanaged(u8) { };\n    errdefer rb.deinit(Allocator);\n    const writer = rb.writer(Allocator);\n    try n_writer(writer, args, readable, sep);\n    return rb.toOwnedSlice(Allocator);\n}\n\nfn print_to_buffer(mal: MalType, rb: anytype, readable: bool) MalError!void {\n    switch(mal) {\n        .String => |string| {\n            if(readable) {\n                try rb.writeAll(\"\\\"\");\n                // TODO: optimize this\n                for(string.data, 0..) |this_char, i| {\n                  if(this_char == '\"' or this_char==92) {\n                    try rb.writeAll(backslash);\n                  }\n                  if(this_char == '\\n') {\n                    try rb.writeAll(\"\\\\n\");\n                  }\n                  else {\n                    try rb.writeAll(string.data[i..i+1]);\n                  }\n                }\n                try rb.writeAll(\"\\\"\");\n            }\n            else {\n                try rb.writeAll(string.data);\n            }\n        },\n        .Keyword => |kwd| {\n            try rb.writeAll(\":\");\n            try rb.writeAll(kwd.data);\n        },\n        .Int => |val| {\n            try rb.print(\"{0}\", .{val.data});\n        },\n        .Nil => {\n            try rb.writeAll(\"nil\");\n        },\n        .True => {\n            try rb.writeAll(\"true\");\n        },\n        .False => {\n            try rb.writeAll(\"false\");\n        },\n        .List => |l| {\n            try rb.writeAll(\"(\");\n            try n_writer(rb, l.data.items, readable, true);\n            try rb.writeAll(\")\");\n        },\n        .Vector => |v| {\n            try rb.writeAll(\"[\");\n            try n_writer(rb, v.data.items, readable, true);\n            try rb.writeAll(\"]\");\n        },\n        .Atom => |atom_value| {\n            try rb.writeAll(\"(atom \");\n            try print_to_buffer(atom_value.data.*, rb, readable);\n            try rb.writeAll(\")\");\n        },\n        .Func, .FnCore => {\n            try rb.writeAll(\"#<function>\");\n        },\n        .Symbol => |value| {\n            try rb.writeAll(value.data);\n        },\n        .HashMap => |h| {\n            try rb.writeAll(\"{\");\n            var iterator = h.data.iterator();\n            var first = true;\n            while(iterator.next()) |pair| {\n                if(!first) {\n                    try rb.writeAll(\" \");\n                }\n                try print_to_buffer(pair.key_ptr.*.*, rb, true);\n                try rb.writeAll(\" \");\n                try print_to_buffer(pair.value_ptr.*.*, rb, readable);\n                first = false;\n            }\n            try rb.writeAll(\"}\");\n        },\n    }\n}\n"
  },
  {
    "path": "impls/zig/reader.zig",
    "content": "const fmt = @import(\"std\").fmt;\n\nconst pcre = @cImport({\n    @cInclude(\"pcre.h\");\n});\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\n\nconst Allocator = @import(\"std\").heap.c_allocator;\nconst string_eql = @import(\"std\").hash_map.eqlString;\nconst linked_list = @import(\"linked_list.zig\");\nconst assert = @import(\"std\").debug.assert;\nconst throw = @import(\"error.zig\").throw;\nconst MalHashMap = @import(\"hmap.zig\").MalHashMap;\nconst map_insert_incref_key = @import(\"hmap.zig\").map_insert_incref_key;\n\nconst match: [*]const u8 =\n    \\\\[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)\n;\nvar error_msg: [*c]const u8 = undefined;\nvar erroroffset: c_int = 0;\nvar re: ?*pcre.pcre = null;\n\nconst Reader = struct {\n    position: u32,\n    string: [] const u8,\n    tokens: [] usize,\n\n    pub fn init(string: [] const u8, tokens: [] usize) Reader {\n        return Reader {\n            .position = 0,\n            .string = string,\n            .tokens = tokens,\n        };\n    }\n\n    pub fn next(self: *Reader) void {\n        self.position += 1;\n    }\n\n    pub fn peek(self: *Reader) ?[]const u8 {\n        while(!self.eol()) {\n            const start = self.tokens[2*self.position];\n            const end = self.tokens[2*self.position+1];\n            if(self.string[start] == ';') {\n                self.position += 1;\n                continue;\n            }\n            return self.string[start..end];\n        }\n        return null;\n    }\n\n    pub fn eol(self: *Reader) bool {\n        return (2 * self.position >= self.tokens.len);\n    }\n};\n\nconst AliasPair = struct {\n    name: []const u8,\n    value: []const u8,\n    count: u8,\n};\n\nconst alias_pairs = [_] AliasPair {\n    AliasPair {.name=\"@\", .value=\"deref\", .count=1},\n    AliasPair {.name=\"\\'\", .value=\"quote\", .count=1},\n    AliasPair {.name=\"`\", .value=\"quasiquote\", .count=1},\n    AliasPair {.name=\"~\", .value=\"unquote\", .count=1},\n    AliasPair {.name=\"~@\", .value=\"splice-unquote\", .count=1},\n    AliasPair {.name=\"^\", .value=\"with-meta\", .count=2},\n};\n\npub fn read_form(reader: *Reader) MalError!*MalType {\n    const token = reader.peek() orelse return MalError.ArgError;\n    reader.next();\n    if(token[0] == '(') {\n        return try read_list(reader);\n    }\n    else if(token[0] == '[') {\n        return try read_vector(reader);\n    }\n    else if(token[0] == ':') {\n        return MalType.new_keyword(token[1..], true);\n    }\n    else if(token[0] == '{') {\n        return try read_hashmap(reader);\n    }\n    for(alias_pairs) |pair| {\n        const name = pair.name;\n        const value = pair.value;\n        const count = pair.count;\n        if(string_eql(token, name)) {\n            assert (count == 1 or count == 2);\n            const result = try MalType.new_list();\n            errdefer result.decref();\n            const first = try MalType.new_symbol(value, true);\n            try result.List.data.append(Allocator, first);\n            for(0..count) |_| {\n                const second = try read_form(reader);\n                errdefer second.decref();\n                try result.List.data.insert(Allocator, 1, second);\n            }\n            return result;\n        }\n    }\n    if(token_is_int(token)) {\n        const value = try fmt.parseInt(i32, token, 10);\n        return try MalType.new_int(value);\n    }\n    else if(string_eql(token, \"nil\")) {\n        return &MalType.NIL;\n    }\n    else if(string_eql(token, \"true\")) {\n        return &MalType.TRUE;\n    }\n    else if(string_eql(token, \"false\")) {\n        return &MalType.FALSE;\n    }\n    else if(token[0] == '\"') {\n        return try read_atom_string(token);\n    }\n    else {\n        return try MalType.new_symbol(token, true);\n    }\n}\n\nfn read_list(reader: *Reader) !*MalType {\n    const result = try MalType.new_list();\n    errdefer result.decref();\n    while(try read_list_element(reader, ')', \"unbalanced '('\")) |mal| {\n        try result.List.data.append(Allocator, mal);\n    }\n    return result;\n}\n\nfn read_vector(reader: *Reader) !*MalType {\n    const result = try MalType.new_vector();\n    errdefer result.decref();\n    while(try read_list_element(reader, ']', \"unbalanced '['\")) |mal| {\n        try result.Vector.data.append(Allocator, mal);\n    }\n    return result;\n}\n\nfn read_hashmap(reader: *Reader) !*MalType {\n    const result = try MalType.new_hashmap();\n    errdefer result.decref();\n    while(try read_list_element(reader, '}', \"unbalanced '{'\")) |key| {\n        const value = try read_form(reader);\n        errdefer value.decref();\n        try map_insert_incref_key(&result.HashMap.data, key, value);\n        key.decref();\n    }\n    return result;\n}\n\nfn read_list_element(reader: *Reader,\n                     comptime closer: u8,\n                     comptime unbalanced: []const u8,\n                    ) !?*MalType {\n    if(reader.peek()) |next_token| {\n        if(next_token[0] == closer) {\n            reader.next();\n            return null;\n        }\n        return try read_form(reader);\n    }\n    return throw(try MalType.new_string(unbalanced, true));\n}\n\nfn char_is_int(c: u8) bool {\n    return (c >= '0' and c <= '9');\n}\n\nfn token_is_int(token: []const u8) bool {\n    if(char_is_int(token[0]))\n        return true;\n    if(token.len >= 2 and token[0] == '-' and char_is_int(token[1]))\n        return true;\n    return false;\n}\n\nfn read_atom_string(token: []const u8) MalError!*MalType {\n    const n = token.len;\n    if(token[0] != '\"' or token[n-1] != '\"' or n <= 1) {\n        return throw(try MalType.new_string(\"unbalanced '\\\"'\", true));\n    }\n\n    var tmp_buffer = try Allocator.alloc(u8, n-2);\n    errdefer Allocator.free(tmp_buffer);\n    var i: usize = 1;\n    var j: usize = 0;\n    const escape_char: u8 = '\\\\'; //TODO: remove this comment required by bad emacs config '\n    while(i < n-1) {\n        if(token[i] != escape_char) {\n            tmp_buffer[j] = token[i];\n            j += 1;\n            i += 1;\n        }\n        else {\n            if(i==n-2) {\n                return throw(try MalType.new_string(\"unbalanced '\\\"'\", true));\n            }\n            if(token[i+1] == 'n') {\n                tmp_buffer[j] = '\\n';\n            } else {\n                tmp_buffer[j] = token[i+1];\n            }\n            j += 1;\n            i += 2;\n        }\n    }\n\n    return try MalType.new_string(tmp_buffer[0..j], false);\n}\n\npub fn read_str(string: [] const u8) MalError!Reader {\n    if(re == null) {\n        re = pcre.pcre_compile(&match[0], 0, &error_msg, &erroroffset, 0);\n    }    \n    const tokens = try tokenize(re, string);\n    return Reader.init(string, tokens);\n}\n\n// Allocates an array of matches.  Caller is becomes owner of memory.\nfn tokenize(regex: ?*pcre.pcre, string: [] const u8) MalError![] usize {\n    // TODO: pass in allocator\n    const buffer_size: usize = 3 * string.len + 10;\n    var indices: [] c_int = try Allocator.alloc(c_int, buffer_size);\n    defer Allocator.free(indices);\n    var match_buffer: [] usize = try Allocator.alloc(usize, buffer_size);\n    defer Allocator.free(match_buffer);\n    var current_match: usize = 0;\n    var start_pos: c_int = 0;\n    \n    var rc: c_int = 0;\n    var start_match: usize = 0;\n    var end_match: usize = 0;\n    const subject_size: c_int = @intCast(string.len);\n\n    while(start_pos < subject_size) {\n        rc = pcre.pcre_exec(regex, 0, &string[0], subject_size, start_pos, 0,\n                            &indices[0], @intCast(buffer_size));\n        if(rc <= 0)\n            break;\n        start_pos = indices[1];\n        start_match = @intCast(indices[2]);\n        end_match = @intCast(indices[3]);\n        match_buffer[current_match] = start_match;\n        match_buffer[current_match+1] = end_match;\n        current_match += 2;\n    }\n\n    var matches: [] usize = try Allocator.alloc(usize, current_match);\n    for(0..current_match) |i| {\n        matches[i] = match_buffer[i];\n    }\n\n    return matches;\n}\n"
  },
  {
    "path": "impls/zig/readline.zig",
    "content": "const allocator = @import(\"std\").heap.c_allocator;\nconst readline = @cImport(\n    @cInclude(\"readline/readline.h\"));\nconst rl_hist = @cImport(\n    @cInclude(\"readline/history.h\"));\nconst free = @import(\"std\").c.free;\n\nfn addNullByte(prompt: []const u8) ![]u8 {\n    const result = try allocator.alloc(u8, prompt.len + 1);\n    for (0.., prompt) |i, source|\n        result[i] = source;\n    result[prompt.len] = 0;\n    return result;\n}\n\nfn slice_from_cstr(str: [*]const u8) ![]const u8 {\n    var length: usize = 0;\n    while(str[length] != 0) {\n        length += 1;\n    }\n    // TODO: check for 0-length\n    const slice = try allocator.alloc(u8, length);\n    for (str, 0..length) |source, i| {\n        slice[i] = source;\n    }\n    return slice;\n}\n\npub fn getline(prompt: []const u8) !?[]const u8 {\n    const null_terminated_prompt = try addNullByte(prompt);\n    defer allocator.free(null_terminated_prompt);\n    const input = readline.readline(&null_terminated_prompt[0]);\n    if(input) |actual| {\n        defer free(actual);\n        const aslice = try slice_from_cstr(actual);\n        rl_hist.add_history(actual);\n        return aslice;\n    }\n    return null;\n}\n"
  },
  {
    "path": "impls/zig/run",
    "content": "#!/bin/sh\nexec $(dirname $0)/zig-out/bin/${STEP:-stepA_mal} \"${@}\"\n"
  },
  {
    "path": "impls/zig/step0_repl.zig",
    "content": "const getline = @import(\"readline.zig\").getline;\n\nconst Allocator = @import(\"std\").heap.c_allocator;\nconst stdout_file = @import(\"std\").io.getStdOut();\n\nfn READ(a: []const u8) []const u8 {\n    return a;\n}\n\nfn EVAL(a: []const u8) []const u8 {\n    return a;\n}\n\nfn PRINT(a: []const u8) !void {\n    try stdout_file.writeAll(a);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(input: []const u8) !void {\n    const read_input = READ(input);\n    const eval_input = EVAL(read_input);\n    try PRINT(eval_input);\n}\n\npub fn main() !void {\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        try rep(line);\n    }\n}\n"
  },
  {
    "path": "impls/zig/step1_read_print.zig",
    "content": "const reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst stdout_file = @import(\"std\").io.getStdOut();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\nfn EVAL(a: *MalType) *MalType {\n    a.incref();\n    return a;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = EVAL(read_input);\n    defer eval_input.decref();\n    try PRINT(eval_input.*);\n}\n\npub fn main() !void {\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step2_eval.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst hash_map = @import(\"hmap.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = hash_map.MalHashMap { };\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\nfn EVAL(mal: *MalType, env: hash_map.MalHashMap) MalError!*MalType {\n\n    //  try stdout_file.writeAll(\"EVAL: \");\n    //  try PRINT(mal.*);\n\n    switch(mal.*) {\n        .List => |ll| {\n            const items = ll.data.items;\n            if(items.len == 0) {\n                mal.incref();\n                return mal;\n            }\n            else {\n                const first_mal = items[0];\n                const evaluated_first = try EVAL(first_mal, env);\n                defer evaluated_first.decref();\n                // A slice would be sufficient, but a List is convenient\n                // for partial deallocation in case of error.\n                const args = try MalType.new_list();\n                defer args.decref();\n                for(items[1..]) |x| {\n                    const new_item = try EVAL(x, env);\n                    try args.List.data.append(Allocator, new_item);\n                }\n                return apply_function(evaluated_first.*, args.List.data.items);\n            }\n        },\n        .Symbol => {\n            return EVAL_symbol(mal, env);\n        },\n        .Vector => |ll| {\n            return EVAL_vector(ll.data.items, env);\n        },\n        .HashMap => |hmap| {\n            return EVAL_map(hmap.data, env);\n        },\n        else => {\n            mal.incref();\n            return mal;\n        },\n    }\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, repl_environment);\n    defer eval_input.decref();\n    try PRINT(eval_input.*);\n}\n\nfn EVAL_symbol(mal: *MalType, env: hash_map.MalHashMap) !*MalType {\n    if(env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: hash_map.MalHashMap) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: hash_map.MalHashMap) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env);\n                // key *is* new in this map.\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nconst safeAdd = @import(\"std\").math.add;\nconst safeSub = @import(\"std\").math.sub;\nconst safeMul = @import(\"std\").math.mul;\nconst safeDivFloor = @import(\"std\").math.divFloor;\n\nfn int_plus(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeAdd(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_minus(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeSub(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_mult(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeMul(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_div(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeDivFloor(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn make_environment() !void {\n\n    const plus_sym = try MalType.new_symbol(\"+\", true);\n    const plus_mal = try MalType.newFnCore(&int_plus);\n    try repl_environment.put(Allocator, plus_sym, plus_mal);\n    const minus_sym = try MalType.new_symbol(\"-\", true);\n    const minus_mal = try MalType.newFnCore(&int_minus);\n    try repl_environment.put(Allocator, minus_sym, minus_mal);\n    const mult_sym = try MalType.new_symbol(\"*\", true);\n    const mult_mal = try MalType.newFnCore(&int_mult);\n    try repl_environment.put(Allocator, mult_sym, mult_mal);\n    const div_sym = try MalType.new_symbol(\"/\", true);\n    const div_mal = try MalType.newFnCore(&int_div);\n    try repl_environment.put(Allocator, div_sym, div_mal);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n    try make_environment();\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step3_env.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal: *MalType, env: *Env) MalError!*MalType {\n\n    if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n        switch (dbgeval.*) {\n            .Nil, .False => {},\n            else => {\n                try stdout_file.writeAll(\"EVAL: \");\n                try PRINT(mal.*);\n            }\n        }\n    }\n\n    switch(mal.*) {\n        .List => |ll| {\n            const items = ll.data.items;\n            if(items.len == 0) {\n                mal.incref();\n                return mal;\n            }\n            const first_mal = items[0];\n            const symbol = switch(first_mal.*) {\n                .Symbol => |symbol| symbol.data,\n                else => \"\",\n            };\n            if(string_eql(symbol, \"def!\")) {\n                return EVAL_def(items[1..], env);\n            }\n            else if(string_eql(symbol, \"let*\")) {\n                return EVAL_let(items[1..], env);\n            }\n            else {\n                const evaluated_first = try EVAL(first_mal, env);\n                defer evaluated_first.decref();\n                // A slice would be sufficient, but a List is convenient\n                // for partial deallocation in case of error.\n                const args = try MalType.new_list();\n                defer args.decref();\n                for(items[1..]) |x| {\n                    const new_item = try EVAL(x, env);\n                    try args.List.data.append(Allocator, new_item);\n                }\n                return apply_function(evaluated_first.*, args.List.data.items);\n            }\n        },\n        .Symbol => {\n            return EVAL_symbol(mal, env);\n        },\n        .Vector => |ll| {\n            return EVAL_vector(ll.data.items, env);\n        },\n        .HashMap => |hmap| {\n            return EVAL_map(hmap.data, env);\n        },\n        else => {\n            mal.incref();\n            return mal;\n        },\n    }\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_let(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    env.incref();\n    defer new_env.decref();\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    return EVAL(eval_arg, new_env);\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment);\n    defer eval_input.decref();\n    try PRINT(eval_input.*);\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env);\n                // key *is* new in this map.\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nconst safeAdd = @import(\"std\").math.add;\nconst safeSub = @import(\"std\").math.sub;\nconst safeMul = @import(\"std\").math.mul;\nconst safeDivFloor = @import(\"std\").math.divFloor;\n\nfn int_plus(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeAdd(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_minus(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeSub(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_mult(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeMul(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn int_div(args: []*MalType) MalError!*MalType {\n    if (args.len != 2) return MalError.ArgError;\n    const a1 = args[0];\n    const a2 = args[1];\n    const x = try a1.as_int();\n    const y = try a2.as_int();\n    const res = try safeDivFloor(i64, x, y);\n    return MalType.new_int(res);\n}\n\nfn make_environment() !void {\n\n    const plus_sym = try MalType.new_symbol(\"+\", true);\n    const plus_mal = try MalType.newFnCore(&int_plus);\n    try repl_environment.set(plus_sym, plus_mal);\n    const minus_sym = try MalType.new_symbol(\"-\", true);\n    const minus_mal = try MalType.newFnCore(&int_minus);\n    try repl_environment.set(minus_sym, minus_mal);\n    const mult_sym = try MalType.new_symbol(\"*\", true);\n    const mult_mal = try MalType.newFnCore(&int_mult);\n    try repl_environment.set(mult_sym, mult_mal);\n    const div_sym = try MalType.new_symbol(\"/\", true);\n    const div_mal = try MalType.newFnCore(&int_div);\n    try repl_environment.set(div_sym, div_mal);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n    try make_environment();\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step4_if_fn_do.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal: *MalType, env: *Env) MalError!*MalType {\n\n    if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n        switch (dbgeval.*) {\n            .Nil, .False => {},\n            else => {\n                try stdout_file.writeAll(\"EVAL: \");\n                try PRINT(mal.*);\n            }\n        }\n    }\n\n    switch(mal.*) {\n        .List => |ll| {\n            const items = ll.data.items;\n            if(items.len == 0) {\n                mal.incref();\n                return mal;\n            }\n            const first_mal = items[0];\n            const symbol = switch(first_mal.*) {\n                .Symbol => |symbol| symbol.data,\n                else => \"\",\n            };\n            if(string_eql(symbol, \"def!\")) {\n                return EVAL_def(items[1..], env);\n            }\n            else if(string_eql(symbol, \"let*\")) {\n                return EVAL_let(items[1..], env);\n            }\n            else if(string_eql(symbol, \"do\")) {\n                return EVAL_do(items[1..], env);\n            }\n            else if(string_eql(symbol, \"if\")) {\n                return EVAL_if(items[1..], env);\n            }\n            else if(string_eql(symbol, \"fn*\")) {\n                return EVAL_fn(items[1..], env);\n            }\n            else {\n                const evaluated_first = try EVAL(first_mal, env);\n                defer evaluated_first.decref();\n                // A slice would be sufficient, but a List is convenient\n                // for partial deallocation in case of error.\n                const args = try MalType.new_list();\n                defer args.decref();\n                for(items[1..]) |x| {\n                    const new_item = try EVAL(x, env);\n                    try args.List.data.append(Allocator, new_item);\n                }\n                return apply_function(evaluated_first.*, args.List.data.items);\n            }\n        },\n        .Symbol => {\n            return EVAL_symbol(mal, env);\n        },\n        .Vector => |ll| {\n            return EVAL_vector(ll.data.items, env);\n        },\n        .HashMap => |hmap| {\n            return EVAL_map(hmap.data, env);\n        },\n        else => {\n            mal.incref();\n            return mal;\n        },\n    }\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_let(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    env.incref();\n    defer new_env.decref();\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    return EVAL(eval_arg, new_env);\n}\n\nfn EVAL_do(args: []*MalType, env: *Env) !*MalType {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env);\n        item.decref();\n    }\n    return EVAL(last_mal, env);\n}\n\nfn EVAL_if(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        return EVAL(second_arg, env);\n    }\n    if(args.len == 2) {\n        return &MalType.NIL;\n    }\n    const third_arg = args[2];\n    return EVAL(third_arg, env);\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n    try make_environment();\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step5_tco.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {\n    var mal = mal_arg;\n    var env = env_arg;\n    var fde = finally_destroy_env;\n    defer if(fde) env.decref();\n    while(true) {\n\n        if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n            switch (dbgeval.*) {\n                .Nil, .False => {},\n                else => {\n                    try stdout_file.writeAll(\"EVAL: \");\n                    try PRINT(mal.*);\n                }\n            }\n        }\n\n        switch(mal.*) {\n            .List => |ll| {\n                const items = ll.data.items;\n                if(items.len == 0) {\n                    mal.incref();\n                    return mal;\n                }\n                const first_mal = items[0];\n                const symbol = switch(first_mal.*) {\n                    .Symbol => |symbol| symbol.data,\n                    else => \"\",\n                };\n                if(string_eql(symbol, \"def!\")) {\n                    return EVAL_def(items[1..], env);\n                }\n                else if(string_eql(symbol, \"let*\")) {\n                    try EVAL_let(items[1..], &mal, &env, &fde);\n                    continue;\n                }\n                else if(string_eql(symbol, \"do\")) {\n                    try EVAL_do(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"if\")) {\n                    try EVAL_if(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"fn*\")) {\n                    return EVAL_fn(items[1..], env);\n                }\n                else {\n                    const evaluated_first = try EVAL(first_mal, env, false);\n                    defer evaluated_first.decref();\n                    // A slice would be sufficient, but a List is convenient\n                    // for partial deallocation in case of error.\n                    const args = try MalType.new_list();\n                    defer args.decref();\n                    for(items[1..]) |x| {\n                        const new_item = try EVAL(x, env, false);\n                        try args.List.data.append(Allocator, new_item);\n                    }\n                   switch(evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(fde) {\n                                env.decref();\n                            }\n                            else {\n                                fde = true;\n                            }\n                            env = try func_data.gen_env(args.List.data.items);\n                            mal = func_data.body;\n                            continue;\n                        },\n                        else => {},\n                    }\n                    return apply_function(evaluated_first.*, args.List.data.items);\n                }\n            },\n            .Symbol => {\n                return EVAL_symbol(mal, env);\n            },\n            .Vector => |ll| {\n                return EVAL_vector(ll.data.items, env);\n            },\n            .HashMap => |hmap| {\n                return EVAL_map(hmap.data, env);\n            },\n            else => {\n                mal.incref();\n                return mal;\n            },\n        }\n    }\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {\n    if(args.len != 2) return MalError.ArgError;\n    const env = env_ptr.*;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    //  Change env and fde in case an error occurs later in this procedure\n    //  and fde triggers an env.decref() at the exit of EVAL.\n    if(!fde.*) {\n        env.incref();\n        fde.* = true;\n    }\n    env_ptr.* = new_env;\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env, false);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    mal_ptr.* = eval_arg;\n}\n\nfn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env, false);\n        item.decref();\n    }\n    mal_ptr.* = last_mal;\n}\n\nfn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env, false);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        mal_ptr.* = second_arg;\n        return;\n    }\n    if(args.len == 2) {\n        mal_ptr.* = &MalType.NIL;\n        return;\n    }\n    const third_arg = args[2];\n    mal_ptr.* = third_arg;\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment, false);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env, false);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env, false);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env, false);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n    try make_environment();\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step6_file.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {\n    var mal = mal_arg;\n    var env = env_arg;\n    var fde = finally_destroy_env;\n    defer if(fde) env.decref();\n    while(true) {\n\n        if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n            switch (dbgeval.*) {\n                .Nil, .False => {},\n                else => {\n                    try stdout_file.writeAll(\"EVAL: \");\n                    try PRINT(mal.*);\n                }\n            }\n        }\n\n        switch(mal.*) {\n            .List => |ll| {\n                const items = ll.data.items;\n                if(items.len == 0) {\n                    mal.incref();\n                    return mal;\n                }\n                const first_mal = items[0];\n                const symbol = switch(first_mal.*) {\n                    .Symbol => |symbol| symbol.data,\n                    else => \"\",\n                };\n                if(string_eql(symbol, \"def!\")) {\n                    return EVAL_def(items[1..], env);\n                }\n                else if(string_eql(symbol, \"let*\")) {\n                    try EVAL_let(items[1..], &mal, &env, &fde);\n                    continue;\n                }\n                else if(string_eql(symbol, \"do\")) {\n                    try EVAL_do(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"if\")) {\n                    try EVAL_if(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"fn*\")) {\n                    return EVAL_fn(items[1..], env);\n                }\n                else {\n                    const evaluated_first = try EVAL(first_mal, env, false);\n                    defer evaluated_first.decref();\n                    // A slice would be sufficient, but a List is convenient\n                    // for partial deallocation in case of error.\n                    const args = try MalType.new_list();\n                    defer args.decref();\n                    for(items[1..]) |x| {\n                        const new_item = try EVAL(x, env, false);\n                        try args.List.data.append(Allocator, new_item);\n                    }\n                   switch(evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(fde) {\n                                env.decref();\n                            }\n                            else {\n                                fde = true;\n                            }\n                            env = try func_data.gen_env(args.List.data.items);\n                            mal = func_data.body;\n                            continue;\n                        },\n                        else => {},\n                    }\n                    return apply_function(evaluated_first.*, args.List.data.items);\n                }\n            },\n            .Symbol => {\n                return EVAL_symbol(mal, env);\n            },\n            .Vector => |ll| {\n                return EVAL_vector(ll.data.items, env);\n            },\n            .HashMap => |hmap| {\n                return EVAL_map(hmap.data, env);\n            },\n            else => {\n                mal.incref();\n                return mal;\n            },\n        }\n    }\n}\n\nfn eval(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return EVAL(a1, &repl_environment, false);\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {\n    if(args.len != 2) return MalError.ArgError;\n    const env = env_ptr.*;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    //  Change env and fde in case an error occurs later in this procedure\n    //  and fde triggers an env.decref() at the exit of EVAL.\n    if(!fde.*) {\n        env.incref();\n        fde.* = true;\n    }\n    env_ptr.* = new_env;\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env, false);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    mal_ptr.* = eval_arg;\n}\n\nfn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env, false);\n        item.decref();\n    }\n    mal_ptr.* = last_mal;\n}\n\nfn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env, false);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        mal_ptr.* = second_arg;\n        return;\n    }\n    if(args.len == 2) {\n        mal_ptr.* = &MalType.NIL;\n        return;\n    }\n    const third_arg = args[2];\n    mal_ptr.* = third_arg;\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment, false);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env, false);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env, false);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const eval_sym = try MalType.new_symbol(\"eval\", true);\n    const eval_mal = try MalType.newFnCore(eval);\n    try repl_environment.set(eval_sym, eval_mal);\n    eval_sym.decref();\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n\n    const load_file_string: [] const u8 =\n        \\\\(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n    ;\n    try rep(false, load_file_string);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env, false);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n\n    //  Break a circular dependency between modules.\n    core.apply_function = &apply_function;\n\n    try make_environment();\n\n    const args = try std.process.argsAlloc(Allocator);\n    const arg_list = try MalType.new_list();\n    if(1 < args.len) {\n        for (args[2..]) |arg| {\n            const new_mal = try MalType.new_string(arg, false);\n            try arg_list.List.data.append(Allocator, new_mal);\n        }\n    }\n    const argv_sym = try MalType.new_symbol(\"*ARGV*\", true);\n    try repl_environment.set(argv_sym, arg_list);\n    argv_sym.decref();\n\n    if(args.len > 1) {\n        const run_cmd = try std.fmt.allocPrint(Allocator, \"(load-file \\\"{s}\\\")\", .{args[1]});\n        try rep(false, run_cmd);\n        return;\n    }\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step7_quote.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {\n    var mal = mal_arg;\n    var env = env_arg;\n    var fde = finally_destroy_env;\n    defer if(fde) env.decref();\n    while(true) {\n\n        if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n            switch (dbgeval.*) {\n                .Nil, .False => {},\n                else => {\n                    try stdout_file.writeAll(\"EVAL: \");\n                    try PRINT(mal.*);\n                }\n            }\n        }\n\n        switch(mal.*) {\n            .List => |ll| {\n                const items = ll.data.items;\n                if(items.len == 0) {\n                    mal.incref();\n                    return mal;\n                }\n                const first_mal = items[0];\n                const symbol = switch(first_mal.*) {\n                    .Symbol => |symbol| symbol.data,\n                    else => \"\",\n                };\n                if(string_eql(symbol, \"def!\")) {\n                    return EVAL_def(items[1..], env);\n                }\n                else if(string_eql(symbol, \"let*\")) {\n                    try EVAL_let(items[1..], &mal, &env, &fde);\n                    continue;\n                }\n                else if(string_eql(symbol, \"do\")) {\n                    try EVAL_do(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"if\")) {\n                    try EVAL_if(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"fn*\")) {\n                    return EVAL_fn(items[1..], env);\n                }\n                else if(string_eql(symbol, \"quote\")) {\n                    return EVAL_quote(items[1..]);\n                }\n                else if(string_eql(symbol, \"quasiquote\")) {\n                    if(items.len != 2) return MalError.ArgError;\n                    const second = items[1];\n                    mal = try quasiquote(second);\n                    continue;\n                }\n                else {\n                    const evaluated_first = try EVAL(first_mal, env, false);\n                    defer evaluated_first.decref();\n                    // A slice would be sufficient, but a List is convenient\n                    // for partial deallocation in case of error.\n                    const args = try MalType.new_list();\n                    defer args.decref();\n                    for(items[1..]) |x| {\n                        const new_item = try EVAL(x, env, false);\n                        try args.List.data.append(Allocator, new_item);\n                    }\n                   switch(evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(fde) {\n                                env.decref();\n                            }\n                            else {\n                                fde = true;\n                            }\n                            env = try func_data.gen_env(args.List.data.items);\n                            mal = func_data.body;\n                            continue;\n                        },\n                        else => {},\n                    }\n                    return apply_function(evaluated_first.*, args.List.data.items);\n                }\n            },\n            .Symbol => {\n                return EVAL_symbol(mal, env);\n            },\n            .Vector => |ll| {\n                return EVAL_vector(ll.data.items, env);\n            },\n            .HashMap => |hmap| {\n                return EVAL_map(hmap.data, env);\n            },\n            else => {\n                mal.incref();\n                return mal;\n            },\n        }\n    }\n}\n\nfn eval(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return EVAL(a1, &repl_environment, false);\n}\n\nfn starts_with(mal: MalType, sym: []const u8) ?*MalType {\n    const ll = switch(mal) {\n        .List => |l| l,\n        else => return null,\n    };\n    const items = ll.data.items;\n    if(items.len != 2) {\n        return null;\n    }\n    const ss = switch(items[0].*) {\n        .Symbol => |s| s,\n        else => return null,\n    };\n    if(string_eql(ss.data, sym)) {\n        return items[1];\n    }\n    return null;\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {\n    if(args.len != 2) return MalError.ArgError;\n    const env = env_ptr.*;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    //  Change env and fde in case an error occurs later in this procedure\n    //  and fde triggers an env.decref() at the exit of EVAL.\n    if(!fde.*) {\n        env.incref();\n        fde.* = true;\n    }\n    env_ptr.* = new_env;\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env, false);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    mal_ptr.* = eval_arg;\n}\n\nfn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env, false);\n        item.decref();\n    }\n    mal_ptr.* = last_mal;\n}\n\nfn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env, false);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        mal_ptr.* = second_arg;\n        return;\n    }\n    if(args.len == 2) {\n        mal_ptr.* = &MalType.NIL;\n        return;\n    }\n    const third_arg = args[2];\n    mal_ptr.* = third_arg;\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn EVAL_quote(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const quoted = args[0];\n    quoted.incref();\n    return quoted;\n}\n\nfn quasiquote(ast: *MalType) MalError!*MalType {\n  switch (ast.*) {\n    .Symbol, .HashMap => {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"quote\", true));\n        try new_list.List.data.append(Allocator, ast);\n        ast.incref();\n        return new_list;\n    },\n    .List => |l| {\n        if(starts_with(ast.*, \"unquote\")) |unquoted| {\n            unquoted.incref();\n            return unquoted;\n        }\n        return try qq_loop(l.data.items);\n    },\n    .Vector => |l| {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"vec\", true));\n        try new_list.List.data.append(Allocator, try qq_loop(l.data.items));\n        return new_list;\n    },\n    else => {\n        ast.incref();\n        return ast;\n    },\n  }\n}\n\nfn qq_loop(items: []*MalType) !*MalType {\n    var result = try MalType.new_list();\n    errdefer result.decref();\n    for (0..items.len) |i| {\n        const elt = items[items.len - 1 - i];\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        if(starts_with(elt.*, \"splice-unquote\")) |unquoted| {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"concat\", true));\n            try new_list.List.data.append(Allocator, unquoted);\n            unquoted.incref();\n        }\n        else {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"cons\", true));\n            try new_list.List.data.append(Allocator, try quasiquote(elt));\n        }\n        try new_list.List.data.append(Allocator, result);\n        result = new_list;\n    }\n    return result;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment, false);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env, false);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env, false);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const eval_sym = try MalType.new_symbol(\"eval\", true);\n    const eval_mal = try MalType.newFnCore(eval);\n    try repl_environment.set(eval_sym, eval_mal);\n    eval_sym.decref();\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n\n    const load_file_string: [] const u8 =\n        \\\\(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n    ;\n    try rep(false, load_file_string);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env, false);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n\n    //  Break a circular dependency between modules.\n    core.apply_function = &apply_function;\n\n    try make_environment();\n\n    const args = try std.process.argsAlloc(Allocator);\n    const arg_list = try MalType.new_list();\n    if(1 < args.len) {\n        for (args[2..]) |arg| {\n            const new_mal = try MalType.new_string(arg, false);\n            try arg_list.List.data.append(Allocator, new_mal);\n        }\n    }\n    const argv_sym = try MalType.new_symbol(\"*ARGV*\", true);\n    try repl_environment.set(argv_sym, arg_list);\n    argv_sym.decref();\n\n    if(args.len > 1) {\n        const run_cmd = try std.fmt.allocPrint(Allocator, \"(load-file \\\"{s}\\\")\", .{args[1]});\n        try rep(false, run_cmd);\n        return;\n    }\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step8_macros.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {\n    var mal = mal_arg;\n    var env = env_arg;\n    var fde = finally_destroy_env;\n    defer if(fde) env.decref();\n    while(true) {\n\n        if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n            switch (dbgeval.*) {\n                .Nil, .False => {},\n                else => {\n                    try stdout_file.writeAll(\"EVAL: \");\n                    try PRINT(mal.*);\n                }\n            }\n        }\n\n        switch(mal.*) {\n            .List => |ll| {\n                const items = ll.data.items;\n                if(items.len == 0) {\n                    mal.incref();\n                    return mal;\n                }\n                const first_mal = items[0];\n                const symbol = switch(first_mal.*) {\n                    .Symbol => |symbol| symbol.data,\n                    else => \"\",\n                };\n                if(string_eql(symbol, \"def!\")) {\n                    return EVAL_def(items[1..], env);\n                }\n                else if(string_eql(symbol, \"defmacro!\")) {\n                    return EVAL_defmacro(items[1..], env);\n                }\n                else if(string_eql(symbol, \"let*\")) {\n                    try EVAL_let(items[1..], &mal, &env, &fde);\n                    continue;\n                }\n                else if(string_eql(symbol, \"do\")) {\n                    try EVAL_do(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"if\")) {\n                    try EVAL_if(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"fn*\")) {\n                    return EVAL_fn(items[1..], env);\n                }\n                else if(string_eql(symbol, \"quote\")) {\n                    return EVAL_quote(items[1..]);\n                }\n                else if(string_eql(symbol, \"quasiquote\")) {\n                    if(items.len != 2) return MalError.ArgError;\n                    const second = items[1];\n                    mal = try quasiquote(second);\n                    continue;\n                }\n                else {\n                    const evaluated_first = try EVAL(first_mal, env, false);\n                    defer evaluated_first.decref();\n                    switch (evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(func_data.is_macro) {\n                                mal = try apply_function(evaluated_first.*, items[1..]);\n                                continue;\n                            }\n                        },\n                        else => {}\n                    }\n                    // A slice would be sufficient, but a List is convenient\n                    // for partial deallocation in case of error.\n                    const args = try MalType.new_list();\n                    defer args.decref();\n                    for(items[1..]) |x| {\n                        const new_item = try EVAL(x, env, false);\n                        try args.List.data.append(Allocator, new_item);\n                    }\n                   switch(evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(fde) {\n                                env.decref();\n                            }\n                            else {\n                                fde = true;\n                            }\n                            env = try func_data.gen_env(args.List.data.items);\n                            mal = func_data.body;\n                            continue;\n                        },\n                        else => {},\n                    }\n                    return apply_function(evaluated_first.*, args.List.data.items);\n                }\n            },\n            .Symbol => {\n                return EVAL_symbol(mal, env);\n            },\n            .Vector => |ll| {\n                return EVAL_vector(ll.data.items, env);\n            },\n            .HashMap => |hmap| {\n                return EVAL_map(hmap.data, env);\n            },\n            else => {\n                mal.incref();\n                return mal;\n            },\n        }\n    }\n}\n\nfn eval(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return EVAL(a1, &repl_environment, false);\n}\n\nfn starts_with(mal: MalType, sym: []const u8) ?*MalType {\n    const ll = switch(mal) {\n        .List => |l| l,\n        else => return null,\n    };\n    const items = ll.data.items;\n    if(items.len != 2) {\n        return null;\n    }\n    const ss = switch(items[0].*) {\n        .Symbol => |s| s,\n        else => return null,\n    };\n    if(string_eql(ss.data, sym)) {\n        return items[1];\n    }\n    return null;\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_defmacro(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    errdefer new_value.decref();\n    const f = switch (new_value.*) {\n        .Func => |func_data| func_data,\n        else => return MalError.TypeError,\n    };\n    const macro = try MalType.newFunc(f.arg_list, f.body, f.environment);\n    f.arg_list.incref();\n    f.body.incref();\n    f.environment.incref();\n    macro.Func.is_macro = true;\n    try env.set(symbol_name, macro);\n    macro.incref();\n    return macro;\n}\n\nfn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {\n    if(args.len != 2) return MalError.ArgError;\n    const env = env_ptr.*;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    //  Change env and fde in case an error occurs later in this procedure\n    //  and fde triggers an env.decref() at the exit of EVAL.\n    if(!fde.*) {\n        env.incref();\n        fde.* = true;\n    }\n    env_ptr.* = new_env;\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env, false);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    mal_ptr.* = eval_arg;\n}\n\nfn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env, false);\n        item.decref();\n    }\n    mal_ptr.* = last_mal;\n}\n\nfn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env, false);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        mal_ptr.* = second_arg;\n        return;\n    }\n    if(args.len == 2) {\n        mal_ptr.* = &MalType.NIL;\n        return;\n    }\n    const third_arg = args[2];\n    mal_ptr.* = third_arg;\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn EVAL_quote(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const quoted = args[0];\n    quoted.incref();\n    return quoted;\n}\n\nfn quasiquote(ast: *MalType) MalError!*MalType {\n  switch (ast.*) {\n    .Symbol, .HashMap => {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"quote\", true));\n        try new_list.List.data.append(Allocator, ast);\n        ast.incref();\n        return new_list;\n    },\n    .List => |l| {\n        if(starts_with(ast.*, \"unquote\")) |unquoted| {\n            unquoted.incref();\n            return unquoted;\n        }\n        return try qq_loop(l.data.items);\n    },\n    .Vector => |l| {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"vec\", true));\n        try new_list.List.data.append(Allocator, try qq_loop(l.data.items));\n        return new_list;\n    },\n    else => {\n        ast.incref();\n        return ast;\n    },\n  }\n}\n\nfn qq_loop(items: []*MalType) !*MalType {\n    var result = try MalType.new_list();\n    errdefer result.decref();\n    for (0..items.len) |i| {\n        const elt = items[items.len - 1 - i];\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        if(starts_with(elt.*, \"splice-unquote\")) |unquoted| {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"concat\", true));\n            try new_list.List.data.append(Allocator, unquoted);\n            unquoted.incref();\n        }\n        else {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"cons\", true));\n            try new_list.List.data.append(Allocator, try quasiquote(elt));\n        }\n        try new_list.List.data.append(Allocator, result);\n        result = new_list;\n    }\n    return result;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment, false);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env, false);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env, false);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const eval_sym = try MalType.new_symbol(\"eval\", true);\n    const eval_mal = try MalType.newFnCore(eval);\n    try repl_environment.set(eval_sym, eval_mal);\n    eval_sym.decref();\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n\n    const load_file_string: [] const u8 =\n        \\\\(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n    ;\n    try rep(false, load_file_string);\n\n    const def_cond_macro_string: [] const u8 =\n        \\\\(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\n    ;\n    try rep(false, def_cond_macro_string);\n\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env, false);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n\n    //  Break a circular dependency between modules.\n    core.apply_function = &apply_function;\n\n    try make_environment();\n\n    const args = try std.process.argsAlloc(Allocator);\n    const arg_list = try MalType.new_list();\n    if(1 < args.len) {\n        for (args[2..]) |arg| {\n            const new_mal = try MalType.new_string(arg, false);\n            try arg_list.List.data.append(Allocator, new_mal);\n        }\n    }\n    const argv_sym = try MalType.new_symbol(\"*ARGV*\", true);\n    try repl_environment.set(argv_sym, arg_list);\n    argv_sym.decref();\n\n    if(args.len > 1) {\n        const run_cmd = try std.fmt.allocPrint(Allocator, \"(load-file \\\"{s}\\\")\", .{args[1]});\n        try rep(false, run_cmd);\n        return;\n    }\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/step9_try.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {\n    var mal = mal_arg;\n    var env = env_arg;\n    var fde = finally_destroy_env;\n    defer if(fde) env.decref();\n    while(true) {\n\n        if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n            switch (dbgeval.*) {\n                .Nil, .False => {},\n                else => {\n                    try stdout_file.writeAll(\"EVAL: \");\n                    try PRINT(mal.*);\n                }\n            }\n        }\n\n        switch(mal.*) {\n            .List => |ll| {\n                const items = ll.data.items;\n                if(items.len == 0) {\n                    mal.incref();\n                    return mal;\n                }\n                const first_mal = items[0];\n                const symbol = switch(first_mal.*) {\n                    .Symbol => |symbol| symbol.data,\n                    else => \"\",\n                };\n                if(string_eql(symbol, \"def!\")) {\n                    return EVAL_def(items[1..], env);\n                }\n                else if(string_eql(symbol, \"defmacro!\")) {\n                    return EVAL_defmacro(items[1..], env);\n                }\n                else if(string_eql(symbol, \"let*\")) {\n                    try EVAL_let(items[1..], &mal, &env, &fde);\n                    continue;\n                }\n                else if(string_eql(symbol, \"do\")) {\n                    try EVAL_do(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"if\")) {\n                    try EVAL_if(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"fn*\")) {\n                    return EVAL_fn(items[1..], env);\n                }\n                else if(string_eql(symbol, \"quote\")) {\n                    return EVAL_quote(items[1..]);\n                }\n                else if(string_eql(symbol, \"quasiquote\")) {\n                    if(items.len != 2) return MalError.ArgError;\n                    const second = items[1];\n                    mal = try quasiquote(second);\n                    continue;\n                }\n                else if(string_eql(symbol, \"try*\")) {\n                    return EVAL_try(items[1..], env);\n                }\n                else {\n                    const evaluated_first = try EVAL(first_mal, env, false);\n                    defer evaluated_first.decref();\n                    switch (evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(func_data.is_macro) {\n                                mal = try apply_function(evaluated_first.*, items[1..]);\n                                continue;\n                            }\n                        },\n                        else => {}\n                    }\n                    // A slice would be sufficient, but a List is convenient\n                    // for partial deallocation in case of error.\n                    const args = try MalType.new_list();\n                    defer args.decref();\n                    for(items[1..]) |x| {\n                        const new_item = try EVAL(x, env, false);\n                        try args.List.data.append(Allocator, new_item);\n                    }\n                   switch(evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(fde) {\n                                env.decref();\n                            }\n                            else {\n                                fde = true;\n                            }\n                            env = try func_data.gen_env(args.List.data.items);\n                            mal = func_data.body;\n                            continue;\n                        },\n                        else => {},\n                    }\n                    return apply_function(evaluated_first.*, args.List.data.items);\n                }\n            },\n            .Symbol => {\n                return EVAL_symbol(mal, env);\n            },\n            .Vector => |ll| {\n                return EVAL_vector(ll.data.items, env);\n            },\n            .HashMap => |hmap| {\n                return EVAL_map(hmap.data, env);\n            },\n            else => {\n                mal.incref();\n                return mal;\n            },\n        }\n    }\n}\n\nfn eval(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return EVAL(a1, &repl_environment, false);\n}\n\nfn starts_with(mal: MalType, sym: []const u8) ?*MalType {\n    const ll = switch(mal) {\n        .List => |l| l,\n        else => return null,\n    };\n    const items = ll.data.items;\n    if(items.len != 2) {\n        return null;\n    }\n    const ss = switch(items[0].*) {\n        .Symbol => |s| s,\n        else => return null,\n    };\n    if(string_eql(ss.data, sym)) {\n        return items[1];\n    }\n    return null;\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_defmacro(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    errdefer new_value.decref();\n    const f = switch (new_value.*) {\n        .Func => |func_data| func_data,\n        else => return MalError.TypeError,\n    };\n    const macro = try MalType.newFunc(f.arg_list, f.body, f.environment);\n    f.arg_list.incref();\n    f.body.incref();\n    f.environment.incref();\n    macro.Func.is_macro = true;\n    try env.set(symbol_name, macro);\n    macro.incref();\n    return macro;\n}\n\nfn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {\n    if(args.len != 2) return MalError.ArgError;\n    const env = env_ptr.*;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    //  Change env and fde in case an error occurs later in this procedure\n    //  and fde triggers an env.decref() at the exit of EVAL.\n    if(!fde.*) {\n        env.incref();\n        fde.* = true;\n    }\n    env_ptr.* = new_env;\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env, false);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    mal_ptr.* = eval_arg;\n}\n\nfn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env, false);\n        item.decref();\n    }\n    mal_ptr.* = last_mal;\n}\n\nfn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env, false);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        mal_ptr.* = second_arg;\n        return;\n    }\n    if(args.len == 2) {\n        mal_ptr.* = &MalType.NIL;\n        return;\n    }\n    const third_arg = args[2];\n    mal_ptr.* = third_arg;\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn EVAL_quote(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const quoted = args[0];\n    quoted.incref();\n    return quoted;\n}\n\nfn EVAL_try(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 1 and args.len != 2) return MalError.ArgError;\n    const mal_to_try = args[0];\n    if(args.len == 1) {\n        return EVAL(mal_to_try, env, false);\n    }\n    const catch_mal = args[1];\n    const catch_list = switch (catch_mal.*) {\n        .List => |l| l.data.items,\n        else => return MalError.TypeError,\n    };\n    if(catch_list.len != 3) return MalError.ArgError;\n    switch (catch_list[0].*) {\n        .Symbol => |s| {\n            if(!string_eql(s.data, \"catch*\")) return MalError.ArgError;\n        },\n        else => return MalError.ArgError,\n    }\n\n    const evaled_mal = EVAL(mal_to_try, env, false) catch |err| {\n        const err_symbol = catch_list[1];\n        const err_body = catch_list[2];\n        const err_val = get_error_data()\n            orelse try MalType.new_string(@errorName(err), true);\n        const new_env = try Env.new(env);\n        env.incref();\n        defer new_env.decref();\n        try new_env.set(err_symbol, err_val); // no incref for err_val.\n        const result = EVAL(err_body, new_env, false);\n        return result;\n    };\n    return evaled_mal;\n}\n\nfn quasiquote(ast: *MalType) MalError!*MalType {\n  switch (ast.*) {\n    .Symbol, .HashMap => {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"quote\", true));\n        try new_list.List.data.append(Allocator, ast);\n        ast.incref();\n        return new_list;\n    },\n    .List => |l| {\n        if(starts_with(ast.*, \"unquote\")) |unquoted| {\n            unquoted.incref();\n            return unquoted;\n        }\n        return try qq_loop(l.data.items);\n    },\n    .Vector => |l| {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"vec\", true));\n        try new_list.List.data.append(Allocator, try qq_loop(l.data.items));\n        return new_list;\n    },\n    else => {\n        ast.incref();\n        return ast;\n    },\n  }\n}\n\nfn qq_loop(items: []*MalType) !*MalType {\n    var result = try MalType.new_list();\n    errdefer result.decref();\n    for (0..items.len) |i| {\n        const elt = items[items.len - 1 - i];\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        if(starts_with(elt.*, \"splice-unquote\")) |unquoted| {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"concat\", true));\n            try new_list.List.data.append(Allocator, unquoted);\n            unquoted.incref();\n        }\n        else {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"cons\", true));\n            try new_list.List.data.append(Allocator, try quasiquote(elt));\n        }\n        try new_list.List.data.append(Allocator, result);\n        result = new_list;\n    }\n    return result;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment, false);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env, false);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env, false);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const eval_sym = try MalType.new_symbol(\"eval\", true);\n    const eval_mal = try MalType.newFnCore(eval);\n    try repl_environment.set(eval_sym, eval_mal);\n    eval_sym.decref();\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n\n    const load_file_string: [] const u8 =\n        \\\\(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n    ;\n    try rep(false, load_file_string);\n\n    const def_cond_macro_string: [] const u8 =\n        \\\\(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\n    ;\n    try rep(false, def_cond_macro_string);\n\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env, false);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n\n    //  Break a circular dependency between modules.\n    core.apply_function = &apply_function;\n\n    try make_environment();\n\n    const args = try std.process.argsAlloc(Allocator);\n    const arg_list = try MalType.new_list();\n    if(1 < args.len) {\n        for (args[2..]) |arg| {\n            const new_mal = try MalType.new_string(arg, false);\n            try arg_list.List.data.append(Allocator, new_mal);\n        }\n    }\n    const argv_sym = try MalType.new_symbol(\"*ARGV*\", true);\n    try repl_environment.set(argv_sym, arg_list);\n    argv_sym.decref();\n\n    if(args.len > 1) {\n        const run_cmd = try std.fmt.allocPrint(Allocator, \"(load-file \\\"{s}\\\")\", .{args[1]});\n        try rep(false, run_cmd);\n        return;\n    }\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/stepA_mal.zig",
    "content": "const std = @import(\"std\");\n\nconst reader = @import(\"reader.zig\");\nconst printer = @import(\"printer.zig\");\nconst getline = @import(\"readline.zig\").getline;\nconst string_eql = std.hash_map.eqlString;\nconst hash_map = @import(\"hmap.zig\");\nconst core = @import(\"core.zig\");\n\nconst Allocator = @import(\"std\").heap.c_allocator;\n\nconst MalType = @import(\"types.zig\").MalType;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\nconst Env = @import(\"env.zig\").Env;\nconst get_error_data = @import(\"error.zig\").get_error_data;\nconst throw = @import(\"error.zig\").throw;\nconst stdout_file = std.io.getStdOut();\n\nvar repl_environment = Env.new_root();\n\nfn READ(a: []const u8) !*MalType {\n    var read = try reader.read_str(a);\n    return reader.read_form(&read);\n}\n\n// Do not allocate this one on each EVAL run.\n// The string is static, but will never be deallocated.\nvar DEBUG_EVAL = MalType { .Symbol = .{ .data = \"DEBUG-EVAL\" } };\n\nfn EVAL(mal_arg: *MalType, env_arg: *Env, finally_destroy_env: bool) MalError!*MalType {\n    var mal = mal_arg;\n    var env = env_arg;\n    var fde = finally_destroy_env;\n    defer if(fde) env.decref();\n    while(true) {\n\n        if(try env.get(&DEBUG_EVAL)) |dbgeval| {\n            switch (dbgeval.*) {\n                .Nil, .False => {},\n                else => {\n                    try stdout_file.writeAll(\"EVAL: \");\n                    try PRINT(mal.*);\n                }\n            }\n        }\n\n        switch(mal.*) {\n            .List => |ll| {\n                const items = ll.data.items;\n                if(items.len == 0) {\n                    mal.incref();\n                    return mal;\n                }\n                const first_mal = items[0];\n                const symbol = switch(first_mal.*) {\n                    .Symbol => |symbol| symbol.data,\n                    else => \"\",\n                };\n                if(string_eql(symbol, \"def!\")) {\n                    return EVAL_def(items[1..], env);\n                }\n                else if(string_eql(symbol, \"defmacro!\")) {\n                    return EVAL_defmacro(items[1..], env);\n                }\n                else if(string_eql(symbol, \"let*\")) {\n                    try EVAL_let(items[1..], &mal, &env, &fde);\n                    continue;\n                }\n                else if(string_eql(symbol, \"do\")) {\n                    try EVAL_do(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"if\")) {\n                    try EVAL_if(items[1..], &mal, env);\n                    continue;\n                }\n                else if(string_eql(symbol, \"fn*\")) {\n                    return EVAL_fn(items[1..], env);\n                }\n                else if(string_eql(symbol, \"quote\")) {\n                    return EVAL_quote(items[1..]);\n                }\n                else if(string_eql(symbol, \"quasiquote\")) {\n                    if(items.len != 2) return MalError.ArgError;\n                    const second = items[1];\n                    mal = try quasiquote(second);\n                    continue;\n                }\n                else if(string_eql(symbol, \"try*\")) {\n                    return EVAL_try(items[1..], env);\n                }\n                else {\n                    const evaluated_first = try EVAL(first_mal, env, false);\n                    defer evaluated_first.decref();\n                    switch (evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(func_data.is_macro) {\n                                mal = try apply_function(evaluated_first.*, items[1..]);\n                                continue;\n                            }\n                        },\n                        else => {}\n                    }\n                    // A slice would be sufficient, but a List is convenient\n                    // for partial deallocation in case of error.\n                    const args = try MalType.new_list();\n                    defer args.decref();\n                    for(items[1..]) |x| {\n                        const new_item = try EVAL(x, env, false);\n                        try args.List.data.append(Allocator, new_item);\n                    }\n                   switch(evaluated_first.*) {\n                        .Func => |func_data| {\n                            if(fde) {\n                                env.decref();\n                            }\n                            else {\n                                fde = true;\n                            }\n                            env = try func_data.gen_env(args.List.data.items);\n                            mal = func_data.body;\n                            continue;\n                        },\n                        else => {},\n                    }\n                    return apply_function(evaluated_first.*, args.List.data.items);\n                }\n            },\n            .Symbol => {\n                return EVAL_symbol(mal, env);\n            },\n            .Vector => |ll| {\n                return EVAL_vector(ll.data.items, env);\n            },\n            .HashMap => |hmap| {\n                return EVAL_map(hmap.data, env);\n            },\n            else => {\n                mal.incref();\n                return mal;\n            },\n        }\n    }\n}\n\nfn eval(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const a1 = args[0];\n    return EVAL(a1, &repl_environment, false);\n}\n\nfn starts_with(mal: MalType, sym: []const u8) ?*MalType {\n    const ll = switch(mal) {\n        .List => |l| l,\n        else => return null,\n    };\n    const items = ll.data.items;\n    if(items.len != 2) {\n        return null;\n    }\n    const ss = switch(items[0].*) {\n        .Symbol => |s| s,\n        else => return null,\n    };\n    if(string_eql(ss.data, sym)) {\n        return items[1];\n    }\n    return null;\n}\n\nfn EVAL_def(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    try env.set(symbol_name, new_value);\n    new_value.incref();\n    return new_value;\n}\n\nfn EVAL_defmacro(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const symbol_name = args[0];\n    const second_arg = args[1];\n    const new_value = try EVAL(second_arg, env, false);\n    errdefer new_value.decref();\n    const f = switch (new_value.*) {\n        .Func => |func_data| func_data,\n        else => return MalError.TypeError,\n    };\n    const macro = try MalType.newFunc(f.arg_list, f.body, f.environment);\n    f.arg_list.incref();\n    f.body.incref();\n    f.environment.incref();\n    macro.Func.is_macro = true;\n    try env.set(symbol_name, macro);\n    macro.incref();\n    return macro;\n}\n\nfn EVAL_let(args: []*MalType, mal_ptr: **MalType, env_ptr: **Env, fde: *bool) !void {\n    if(args.len != 2) return MalError.ArgError;\n    const env = env_ptr.*;\n    const binding_arg = args[0];\n    const eval_arg = args[1];\n    const binds = try binding_arg.as_slice();\n    if(binds.len % 2 != 0) return MalError.ArgError;\n    const new_env = try Env.new(env);\n    //  Change env and fde in case an error occurs later in this procedure\n    //  and fde triggers an env.decref() at the exit of EVAL.\n    if(!fde.*) {\n        env.incref();\n        fde.* = true;\n    }\n    env_ptr.* = new_env;\n    for(0..binds.len / 2) |i| {\n        const key = binds[2*i];\n        const val_mal = binds[2*i + 1];\n        const evaled_mal = try EVAL(val_mal, new_env, false);\n        errdefer evaled_mal.decref();\n        try new_env.set(key, evaled_mal);\n        //  Do not increment the refcount for the value.\n    }\n    mal_ptr.* = eval_arg;\n}\n\nfn EVAL_do(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len == 0) return MalError.ArgError;\n    const last_mal = args[args.len - 1];\n    for (args[0..args.len - 1]) |form| {\n        const item = try EVAL(form, env, false);\n        item.decref();\n    }\n    mal_ptr.* = last_mal;\n}\n\nfn EVAL_if(args: []*MalType, mal_ptr: **MalType, env: *Env) !void {\n    if(args.len != 2 and args.len != 3) return MalError.ArgError;\n    const first_arg = args[0];\n    const evaled = try EVAL(first_arg, env, false);\n    const is_true = switch(evaled.*) {\n        .False => false,\n        .Nil => false,\n        else => true,\n    };\n    evaled.decref();\n    if(is_true) {\n        const second_arg = args[1];\n        mal_ptr.* = second_arg;\n        return;\n    }\n    if(args.len == 2) {\n        mal_ptr.* = &MalType.NIL;\n        return;\n    }\n    const third_arg = args[2];\n    mal_ptr.* = third_arg;\n}\n\nfn EVAL_fn(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 2) return MalError.ArgError;\n    const arg_mal = args[0];\n    const body_mal = args[1];\n    for (try arg_mal.as_slice()) |x| {\n        switch (x.*) {\n            .Symbol => {},\n            else => return MalError.TypeError,\n        }\n    }\n    const new_func = try MalType.newFunc(arg_mal, body_mal, env);\n    arg_mal.incref();\n    body_mal.incref();\n    env.incref();\n    return new_func;\n}\n\nfn EVAL_quote(args: []*MalType) !*MalType {\n    if(args.len != 1) return MalError.ArgError;\n    const quoted = args[0];\n    quoted.incref();\n    return quoted;\n}\n\nfn EVAL_try(args: []*MalType, env: *Env) !*MalType {\n    if(args.len != 1 and args.len != 2) return MalError.ArgError;\n    const mal_to_try = args[0];\n    if(args.len == 1) {\n        return EVAL(mal_to_try, env, false);\n    }\n    const catch_mal = args[1];\n    const catch_list = switch (catch_mal.*) {\n        .List => |l| l.data.items,\n        else => return MalError.TypeError,\n    };\n    if(catch_list.len != 3) return MalError.ArgError;\n    switch (catch_list[0].*) {\n        .Symbol => |s| {\n            if(!string_eql(s.data, \"catch*\")) return MalError.ArgError;\n        },\n        else => return MalError.ArgError,\n    }\n\n    const evaled_mal = EVAL(mal_to_try, env, false) catch |err| {\n        const err_symbol = catch_list[1];\n        const err_body = catch_list[2];\n        const err_val = get_error_data()\n            orelse try MalType.new_string(@errorName(err), true);\n        const new_env = try Env.new(env);\n        env.incref();\n        defer new_env.decref();\n        try new_env.set(err_symbol, err_val); // no incref for err_val.\n        const result = EVAL(err_body, new_env, false);\n        return result;\n    };\n    return evaled_mal;\n}\n\nfn quasiquote(ast: *MalType) MalError!*MalType {\n  switch (ast.*) {\n    .Symbol, .HashMap => {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"quote\", true));\n        try new_list.List.data.append(Allocator, ast);\n        ast.incref();\n        return new_list;\n    },\n    .List => |l| {\n        if(starts_with(ast.*, \"unquote\")) |unquoted| {\n            unquoted.incref();\n            return unquoted;\n        }\n        return try qq_loop(l.data.items);\n    },\n    .Vector => |l| {\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        try new_list.List.data.append(Allocator, try MalType.new_symbol(\"vec\", true));\n        try new_list.List.data.append(Allocator, try qq_loop(l.data.items));\n        return new_list;\n    },\n    else => {\n        ast.incref();\n        return ast;\n    },\n  }\n}\n\nfn qq_loop(items: []*MalType) !*MalType {\n    var result = try MalType.new_list();\n    errdefer result.decref();\n    for (0..items.len) |i| {\n        const elt = items[items.len - 1 - i];\n        const new_list = try MalType.new_list();\n        errdefer new_list.decref();\n        if(starts_with(elt.*, \"splice-unquote\")) |unquoted| {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"concat\", true));\n            try new_list.List.data.append(Allocator, unquoted);\n            unquoted.incref();\n        }\n        else {\n            try new_list.List.data.append(Allocator, try MalType.new_symbol(\"cons\", true));\n            try new_list.List.data.append(Allocator, try quasiquote(elt));\n        }\n        try new_list.List.data.append(Allocator, result);\n        result = new_list;\n    }\n    return result;\n}\n\nfn PRINT(mal: MalType) !void {\n    try printer.one_stdout(mal);\n    try stdout_file.writeAll(\"\\n\");\n}\n\nfn rep(print: bool, input: []const u8) !void {\n    const read_input = try READ(input);\n    defer read_input.decref();\n    const eval_input = try EVAL(read_input, &repl_environment, false);\n    defer eval_input.decref();\n    if(print) {\n        try PRINT(eval_input.*);\n    }\n}\n\nfn EVAL_symbol(mal: *MalType, env: *Env) !*MalType {\n    if(try env.get(mal)) |value| {\n        value.incref();\n        return value;\n    }\n    const err = try std.fmt.allocPrint(Allocator, \"'{s}' not found\",\n        .{mal.Symbol.data});\n    return throw(try MalType.new_string(err, false));\n}\n\nfn EVAL_vector(ll: []*MalType, env: *Env) !*MalType {\n            const ret_mal = try MalType.new_vector();\n            errdefer ret_mal.decref();\n            for(ll) |x| {\n                const new_mal = try EVAL(x, env, false);\n                try ret_mal.Vector.data.append(Allocator, new_mal);\n            }\n            return ret_mal;\n}\n\nfn EVAL_map(hmap: hash_map.MalHashMap, env: *Env) !*MalType {\n            const new_hashmap = try MalType.new_hashmap();\n            errdefer new_hashmap.decref();\n            var iterator = hmap.iterator();\n            while(iterator.next()) |pair| {\n                const key = pair.key_ptr.*;\n                const value = pair.value_ptr.*;\n                const evaled_value = try EVAL(value, env, false);\n                try hash_map.map_insert_incref_key(&new_hashmap.HashMap.data, key, evaled_value);\n            }\n            return new_hashmap;\n}\n\nfn make_environment() !void {\n\n   for(core.core_namespace) |pair| {\n        const name = try MalType.new_symbol(pair.name, true);\n        const func_mal = try MalType.newFnCore(pair.func);\n        try repl_environment.set(name, func_mal);\n        name.decref();\n    }\n\n    const eval_sym = try MalType.new_symbol(\"eval\", true);\n    const eval_mal = try MalType.newFnCore(eval);\n    try repl_environment.set(eval_sym, eval_mal);\n    eval_sym.decref();\n\n    const def_not_string: [] const u8 =\n        \\\\(def! not (fn* (a) (if a false true)))\n    ;\n    try rep(false, def_not_string);\n\n    const load_file_string: [] const u8 =\n        \\\\(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))\n    ;\n    try rep(false, load_file_string);\n\n    const def_cond_macro_string: [] const u8 =\n        \\\\(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))\n    ;\n    try rep(false, def_cond_macro_string);\n\n    const host_language_sym = try MalType.new_symbol(\"*host-language*\", true);\n    const host_language_mal = try MalType.new_string(\"Zig\", true);\n    try repl_environment.set(host_language_sym, host_language_mal);\n}\n\nfn do_print_header() !void {\n    const welcome_msg_cmd: [] const u8 =\n        \\\\(println (str \"Mal [\" *host-language* \"]\"))\n    ;\n    try rep(false, welcome_msg_cmd);\n}\n\npub fn apply_function(f: MalType, args: []*MalType) MalError!*MalType {\n\n    switch(f) {\n        .FnCore => |fncoredata| {\n            return fncoredata.data(args);\n        },\n        .Func => |funcdata| {\n            const apply_env = try funcdata.gen_env(args);\n            defer apply_env.decref();\n            return EVAL(funcdata.body, apply_env, false);\n        },\n        else => {\n            return MalError.ApplyError;\n        },\n    }\n}\n\npub fn main() !void {\n\n    //  Break a circular dependency between modules.\n    core.apply_function = &apply_function;\n\n    try make_environment();\n\n    const args = try std.process.argsAlloc(Allocator);\n    const arg_list = try MalType.new_list();\n    if(1 < args.len) {\n        for (args[2..]) |arg| {\n            const new_mal = try MalType.new_string(arg, false);\n            try arg_list.List.data.append(Allocator, new_mal);\n        }\n    }\n    const argv_sym = try MalType.new_symbol(\"*ARGV*\", true);\n    try repl_environment.set(argv_sym, arg_list);\n    argv_sym.decref();\n\n    if(args.len > 1) {\n        const run_cmd = try std.fmt.allocPrint(Allocator, \"(load-file \\\"{s}\\\")\", .{args[1]});\n        try rep(false, run_cmd);\n        return;\n    }\n\n    try do_print_header();\n\n    while(try getline(\"user> \")) |line| {\n        defer Allocator.free(line);\n        rep(true, line) catch |err| {\n            try stdout_file.writeAll(\"Error: \");\n            try stdout_file.writeAll(@errorName(err));\n            try stdout_file.writeAll(\"\\n\");\n            if(get_error_data()) |mal| {\n                defer mal.decref();\n                try stdout_file.writeAll(\"MAL error object is: \");\n                try PRINT(mal.*);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impls/zig/types.zig",
    "content": "const std = @import(\"std\");\n\nconst allocator = std.heap.c_allocator;\nconst warn = std.log.warn;\nconst Env = @import(\"env.zig\").Env;\nconst MalError = @import(\"error.zig\").MalError;\nconst MalHashMap = @import(\"hmap.zig\").MalHashMap;\nconst MalLinkedList = @import(\"linked_list.zig\").MalLinkedList;\n\nconst linked_list = @import(\"linked_list.zig\");\nconst hash_map = @import(\"hmap.zig\");\nconst map_destroy = @import(\"hmap.zig\").map_destroy;\n\npub const debug_alloc = false;\n\npub const ListData = struct {\n    data: MalLinkedList,\n    reference_count: i32 = 1,\n    metadata: *MalType = &MalType.NIL,\n};\n\npub const FnCoreData = struct {\n    data: *const fn (args: []*MalType) MalError!*MalType,\n    reference_count: i32 = 1,  //  May reach 0 when metadata.\n    metadata: *MalType = &MalType.NIL,\n};\n\npub const MalFuncData = struct {\n    arg_list: *MalType,\n    body: *MalType,\n    environment: *Env,\n    is_macro: bool = false,\n    reference_count: i32 = 1,\n    metadata: *MalType = &MalType.NIL,\n\n    pub fn gen_env(self: MalFuncData, args: []*MalType) !*Env {\n        const binds = try self.arg_list.as_slice();\n        var res = try Env.new(self.environment);\n        self.environment.incref();\n        errdefer res.decref();\n        if (2 <= binds.len\n            and std.hash_map.eqlString(binds[binds.len - 2].Symbol.data, \"&\"))\n        {\n            if (args.len < binds.len - 2)\n                return MalError.TypeError;\n            for (binds[0..binds.len-2], args[0..binds.len-2]) |k, v| {\n                try res.set(k, v);\n                v.incref();\n            }\n            const more = try MalType.new_list();\n            errdefer more.decref();\n            for (args[binds.len-2..args.len]) |x| {\n                try more.List.data.append(allocator, x);\n                x.incref();\n            }\n            try res.set(binds[binds.len - 1], more);\n            //  Do not increment the reference count for this value.\n        }\n        else {\n            if (args.len != binds.len) {\n                return MalError.TypeError;\n            }\n            for(binds, args) |k, v| {\n                try res.set(k, v);\n                v.incref();\n            }\n        }\n        return res;\n    }\n};\n\npub const StringData = struct {\n    data: [] const u8,\n    reference_count: i32 = 1,\n};\n\npub const HashMapData = struct {\n    data: MalHashMap,\n    reference_count: i32 = 1,\n    metadata: *MalType = &MalType.NIL,\n};\n\npub const MalType = union(enum) {\n    List: ListData,\n    Vector: ListData,\n    Int: struct {\n        data: i64,\n        reference_count: i32 = 1,\n    },\n    Symbol: StringData,\n    String: StringData,\n    Keyword: StringData,\n    Nil: void,\n    True: void,\n    False: void,\n    FnCore: FnCoreData,\n    Func: MalFuncData,\n    Atom: struct {\n        data: *MalType,\n        reference_count: i32 = 1,\n    },\n    HashMap: HashMapData,\n\n    //  Define some frequent values in advance.  They are not allocated\n    //  on the heap, but should never be deallocated anyway.\n    pub var NIL   = MalType { .Nil   = undefined };\n    pub var FALSE = MalType { .False = undefined };\n    pub var TRUE  = MalType { .True  = undefined };\n\n    pub fn new_symbol(value: []const u8, copy: bool) !*MalType {\n        const mal = try allocator.create(MalType);\n        errdefer allocator.destroy(mal);\n        const data = if (copy) try allocator.dupe(u8, value) else value;\n        mal.* = .{.Symbol=.{.data = data}};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_string(value: []const u8, copy: bool) !*MalType {\n        const mal = try allocator.create(MalType);\n        errdefer allocator.destroy(mal);\n        const data = if (copy) try allocator.dupe(u8, value) else value;\n        mal.* = .{.String=.{.data = data}};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_keyword(value: []const u8, copy: bool) !*MalType {\n        const mal = try allocator.create(MalType);\n        errdefer allocator.destroy(mal);\n        const data = if (copy) try allocator.dupe(u8, value) else value;\n        mal.* = .{.Keyword=.{.data = data}};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_int(value: i64) !*MalType {\n        const mal = try allocator.create(MalType);\n        mal.* = .{.Int=.{.data = value}};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_bool(b: bool) *MalType {\n        if(b) {\n            return &TRUE;\n        }\n        else {\n            return &FALSE;\n        }\n    }\n\n    pub fn newFnCore(f: *const fn (args: []*MalType) MalError!*MalType) !*MalType {\n        const mal = try allocator.create(MalType);\n        mal.* = .{.FnCore=.{.data = f}};\n        if (debug_alloc) warn(\"Init core function\", .{});\n        return mal;\n    }\n\n    pub fn newFunc(arg_list: *MalType,\n                   body: *MalType,\n                   environment: *Env,\n                  ) !*MalType\n    {\n        const mal = try allocator.create(MalType);\n        mal.* = .{.Func=.{\n            .arg_list = arg_list,\n            .body = body,\n            .environment = environment,\n        }};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_list() !*MalType {\n        const mal = try allocator.create(MalType);\n        mal.* = .{.List=.{.data = MalLinkedList { }}};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_vector() !*MalType {\n        const mal = try allocator.create(MalType);\n        errdefer allocator.destroy(mal);\n        mal.* = .{.Vector=.{.data = MalLinkedList { }}};\n        if (debug_alloc) warn(\"Init {any}\", .{mal});\n        return mal;\n    }\n\n    pub fn new_atom(mal: *MalType) !*MalType {\n        const new_mal = try allocator.create(MalType);\n        errdefer allocator.destroy(new_mal);\n        new_mal.* = .{.Atom=.{.data = mal}};\n        if (debug_alloc) warn(\"Init {any}\", .{new_mal});\n        return new_mal;\n    }\n\n    pub fn new_hashmap() !*MalType {\n        const new_mal = try allocator.create(MalType);\n        errdefer allocator.destroy(new_mal);\n        new_mal.* = .{.HashMap=.{.data = .{}}};\n        if (debug_alloc) warn(\"Init {any}\", .{new_mal});\n        return new_mal;\n    }\n\n    //  Trivial but convenient checkers/getters.\n\n    pub fn as_slice(self: MalType) ![]*MalType {\n        return switch (self) {\n            .List, .Vector => |x| x.data.items,\n            else => MalError.TypeError,\n        };\n    }\n\n    pub fn as_int(mal: MalType) !i64 {\n        return switch (mal) {\n            .Int => |val| val.data,\n            else => MalError.TypeError,\n        };\n    }\n\n    pub fn as_string(self: MalType) ![]const u8 {\n        return switch (self) {\n            .String => |s| s.data,\n            else => MalError.TypeError,\n        };\n    }\n\n    pub fn as_map(self: MalType) !MalHashMap {\n        switch (self) {\n            .HashMap => |x| return x.data,\n            else => return MalError.TypeError,\n        }\n    }\n\n    pub fn decref(mal: *MalType) void {\n        switch(mal.*) {\n            .List, .Vector => |*l| {\n                std.debug.assert (0 < l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {any}\", .{mal});\n                    linked_list.list_destroy(&l.data);\n                    l.metadata.decref();\n                    allocator.destroy(mal);\n                }\n            },\n            .Keyword, .String, .Symbol => |*l| {\n                std.debug.assert (0 < l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {s} {any}\", .{l.data, mal});\n                    allocator.free(l.data);\n                    allocator.destroy(mal);\n                }\n            },\n            .Atom => |*l| {\n                std.debug.assert (0 < l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {any}\", .{mal});\n                    l.data.decref();\n                    allocator.destroy(mal);\n                }\n            },\n            .HashMap => |*l| {\n                std.debug.assert (0 <= l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {any}\", .{mal});\n                    map_destroy(&l.data);\n                    l.metadata.decref();\n                    allocator.destroy(mal);\n                }\n            },\n            .Func => |*l| {\n                std.debug.assert (0 < l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {any}\", .{mal});\n                    l.arg_list.decref();\n                    l.body.decref();\n                    l.environment.decref();\n                    l.metadata.decref();\n                    allocator.destroy(mal);\n                }\n            },\n            .Int => |*l| {\n                std.debug.assert (0 < l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {any}\", .{mal});\n                    allocator.destroy(mal);\n                }\n            },\n            .FnCore => |*l| {\n                std.debug.assert (0 < l.reference_count);\n                l.reference_count -= 1;\n                if (l.reference_count == 0) {\n                    if (debug_alloc) warn(\"Free {any}\", .{mal});\n                    l.metadata.decref();\n                    allocator.destroy(mal);\n                }\n            },\n            .Nil, .False, .True => {},\n        }\n    }\n\n    pub fn incref(mal: *MalType) void {\n        // A procedure instead of a function returning its argument\n        // because it must most of the time be applied *after* a\n        // successful assignment.\n        switch(mal.*) {\n            .List, .Vector             => |*l| l.reference_count += 1,\n            .Int                       => |*l| l.reference_count += 1,\n            .Keyword, .String, .Symbol => |*l| l.reference_count += 1,\n            .FnCore                    => |*l| l.reference_count += 1,\n            .Func                      => |*l| l.reference_count += 1,\n            .Atom                      => |*l| l.reference_count += 1,\n            .HashMap                   => |*l| l.reference_count += 1,\n            .Nil, .False, .True        => {},\n        }\n    }\n\n};\n"
  },
  {
    "path": "process/guide.md",
    "content": "# The Make-A-Lisp Process\n\nSo you want to write a Lisp interpreter? Welcome!\n\nThe goal of the Make-A-Lisp project is to make it easy to write your\nown Lisp interpreter without sacrificing those many \"Aha!\" moments\nthat come from ascending the McCarthy mountain. When you reach the peak\nof this particular mountain, you will have an interpreter for the mal\nLisp language that is powerful enough to be self-hosting, meaning it\nwill be able to run a mal interpreter written in mal itself.\n\nSo jump right in (er ... start the climb)!\n\n- [Pick a language](#pick-a-language)\n- [Getting started](#getting-started)\n- [General hints](#general-hints)\n- [The Make-A-Lisp Process](#the-make-a-lisp-process-1)\n  - [Step 0: The REPL](#step-0-the-repl)\n  - [Step 1: Read and Print](#step-1-read-and-print)\n  - [Step 2: Eval](#step-2-eval)\n  - [Step 3: Environments](#step-3-environments)\n  - [Step 4: If Fn Do](#step-4-if-fn-do)\n  - [Step 5: Tail call optimization](#step-5-tail-call-optimization)\n  - [Step 6: Files, Mutation, and Evil](#step-6-files-mutation-and-evil)\n  - [Step 7: Quoting](#step-7-quoting)\n  - [Step 8: Macros](#step-8-macros)\n  - [Step 9: Try](#step-9-try)\n  - [Step A: Metadata, Self-hosting and Interop](#step-a-metadata-self-hosting-and-interop)\n\n\n## Pick a language\n\nYou might already have a language in mind that you want to use.\nTechnically speaking, mal can be implemented in any sufficiently\ncomplete programming language (i.e. Turing complete), however, there\nare a few language features that can make the task MUCH easier. Here\nare some of them in rough order of importance:\n\n* A sequential compound data structure (e.g. arrays, lists,\n  vectors, etc)\n* An associative compound data structure (e.g. a dictionary,\n  hash-map, associative array, etc)\n* Function references (first class functions, function pointers,\n  etc)\n* Real exception handling (try/catch, raise, throw, etc)\n* Variable argument functions (variadic, var args, splats, apply, etc)\n* Function closures\n* PCRE regular expressions\n\nIn addition, the following will make your task especially easy:\n\n* Dynamic typing / boxed types (specifically, the ability to store\n  different data types in the sequential and associative structures\n  and the language keeps track of the type for you)\n* Compound data types support arbitrary runtime \"hidden\" data\n  (metadata, metatables, dynamic fields attributes)\n\nHere are some examples of languages that have all of the above\nfeatures: JavaScript, Ruby, Python, Lua, R, Clojure.\n\nMichael Fogus has some great blog posts on interesting but less well\nknown languages and many of the languages on his lists do not yet have\nany mal implementations:\n* http://blog.fogus.me/2011/08/14/perlis-languages/\n* http://blog.fogus.me/2011/10/18/programming-language-development-the-past-5-years/\n\nMany of the most popular languages already have Mal implementations.\nHowever, this should not discourage you from creating your own\nimplementation in a language that already has one. However, if you go\nthis route, I suggest you avoid referring to the existing\nimplementations (i.e. \"cheating\") to maximize your learning experience\ninstead of just borrowing mine. On the other hand, if your goal is to\nadd new implementations to mal as efficiently as possible, then you\nSHOULD find the most similar target language implementation and refer\nto it frequently.\n\nIf you want a list of programming languages with an\napproximate measure of popularity try the [RedMonk Programming\nLanguage\nRankings](https://redmonk.com/sogrady/2019/03/20/language-rankings-1-19/)\nor the [GitHut 2.0 Project](https://madnight.github.io/githut).\n\n\n## Getting started\n\n* Install your chosen language interpreter/compiler, language package\n  manager and build tools (if applicable)\n\n* Fork the mal repository on github and then clone your forked\n  repository:\n```\ngit clone git@github.com:YOUR_NAME/mal.git\ncd mal\n```\n\n* Make a new directory for your implementation. For example, if your\nlanguage is called \"quux\":\n```\nmkdir impls/quux\n```\n\n* Modify the top level Makefile.impls to allow the tests to be run against\n  your implementation. For example, if your language is named \"quux\"\n  and uses \"qx\" as the file extension, then make the following\n  3 modifications to Makefile.impls:\n```\nIMPLS = ... quux ...\n...\nquux_STEP_TO_PROG = impls/quux/$($(1)).qx\n```\n\n* Add a \"run\" script to your implementation directory that listens to\n  the \"STEP\" environment variable for the implementation step to run\n  and defaults to \"stepA_mal\". Make sure the run script has the\n  executable file permission set (or else the test runner might fail with a\n  permission denied error message). The following are examples of \"run\"\n  scripts for a compiled language and an interpreted language (where\n  the interpreter is named \"quux\"):\n\n```\n#!/usr/bin/env bash\nexec $(dirname $0)/${STEP:-stepA_mal} \"${@}\"\n```\n\n```\n#!/usr/bin/env bash\nexec quux $(dirname $0)/${STEP:-stepA_mal}.qx \"${@}\"\n```\n\nThis allows you to run tests against your implementation like this:\n```\nmake \"test^quux^stepX\"\n```\n\nIf your implementation language is a compiled language, then you\nshould also add a Makefile at the top level of your implementation\ndirectory. This Makefile will define how to build the files pointed to\nby the quux_STEP_TO_PROG macro. The top-level Makefile will attempt to\nbuild those targets before running tests. If it is a scripting\nlanguage/uncompiled, then no Makefile is necessary because\nquux_STEP_TO_PROG will point to a source file that already exists and\ndoes not need to be compiled/built.\n\n\n## General hints\n\nStackoverflow and Google are your best friends. Modern polyglot\ndevelopers do not memorize dozens of programming languages. Instead,\nthey learn the peculiar terminology used with each language and then\nuse this to search for their answers.\n\nHere are some other resources where multiple languages are\ncompared/described:\n* http://learnxinyminutes.com/\n* http://hyperpolyglot.org/\n* http://rosettacode.org/\n* http://rigaux.org/language-study/syntax-across-languages/\n\nDo not let yourself be bogged down by specific problems. While the\nmake-a-lisp process is structured as a series of steps, the reality is\nthat building a lisp interpreter is more like a branching tree. If you\nget stuck on tail call optimization, or hash-maps, move on to other\nthings. You will often have a stroke of inspiration for a problem as\nyou work through other functionality. I have tried to structure this\nguide and the tests to make clear which things can be deferred until\nlater.\n\nAn aside on deferrable/optional bits: when you run the tests for\na given step, the last tests are often marked with an \"optional\"\nheader. This indicates that these are tests for functionality that is\nnot critical to finish a basic mal implementation. Many of the steps\nin this process guide have a \"Deferrable\" section, however, it is not\nquite the same meaning. Those sections include the functionality that\nis marked as optional in the tests, but they also include\nfunctionality that becomes mandatory at a later step. In other words,\nthis is a \"make your own Lisp adventure\".\n\nUse test driven development. Each step of the make-a-lisp process has\na bunch of tests associated with it and there is an easy script to run\nall the tests for a specific step in the process. Pick a failing test,\nfix it, repeat until all the tests for that step pass.\n\n## Reference Code\n\nThe `process` directory contains abbreviated pseudocode and\narchitecture diagrams for each step of the make-a-lisp process. Use\na textual diff/comparison tool to compare the previous pseudocode step\nwith the one you are working on. The architecture diagram images have\nchanges from the previous step highlighted in red. There is also\na concise\n[cheatsheet](http://kanaka.github.io/mal/cheatsheet.html) that\nsummarizes the key changes at each step.\n\nIf you get completely stuck and are feeling like giving up, then you\nshould \"cheat\" by referring to the same step or functionality in\nan existing implementation language. You are here to learn, not to take\na test, so do not feel bad about it. Okay, you should feel a little\nbit bad about it.\n\n\n## The Make-A-Lisp Process\n\nFeel free to follow the guide as literally or as loosely as you\nlike. You are here to learn; wandering off the beaten path may be the\nway you learn best. However, each step builds on the previous steps,\nso if you are new to Lisp or new to your implementation language then\nyou may want to stick more closely to the guide your first time\nthrough to avoid frustration at later steps.\n\nIn the steps that follow the name of the target language is \"quux\" and\nthe file extension for that language is \"qx\".\n\n\n<a name=\"step0\"></a>\n\n### Step 0: The REPL\n\n![step0_repl architecture](step0_repl.png)\n\nThis step is basically just creating a skeleton of your interpreter.\n\n* Create a `step0_repl.qx` file in `impls/quux/`.\n\n* Add the 4 trivial functions `READ`, `EVAL`, `PRINT`, and `rep`\n  (read-eval-print). `READ`, `EVAL`, and `PRINT` are basically just\n  stubs that return their first parameter (a string if your target\n  language is a statically typed) and `rep` calls them in order\n  passing the return to the input of the next.\n\n* Add a main loop that repeatedly prints a prompt (needs to be\n  \"user> \" for later tests to pass), gets a line of input from the\n  user, calls `rep` with that line of input, and then prints out the\n  result from `rep`. It should also exit when you send it an EOF\n  (often Ctrl-D).\n\n* If you are using a compiled (ahead-of-time rather than just-in-time)\n  language, then create a Makefile (or appropriate project definition\n  file) in your directory.\n\nIt is time to run your first tests. This will check that your program\ndoes input and output in a way that can be captured by the test\nharness. Go to the top level and run the following:\n```\nmake \"test^quux^step0\"\n```\n\nAdd and then commit your new `step0_repl.qx` and `Makefile` to git.\n\nCongratulations! You have just completed the first step of the\nmake-a-lisp process.\n\n\n#### Optional:\n\n* Add full line editing and command history support to your\n  interpreter REPL. Many languages have a library/module that provide\n  line editing support. Another option if your language supports it is\n  to use an FFI (foreign function interface) to load and call directly\n  into GNU readline, editline, or linenoise library. Add line\n  editing interface code to `readline.qx`\n\n\n<a name=\"step1\"></a>\n\n### Step 1: Read and Print\n\n![step1_read_print architecture](step1_read_print.png)\n\nIn this step, your interpreter will \"read\" the string from the user\nand parse it into an internal tree data structure (an abstract syntax\ntree) and then take that data structure and \"print\" it back to\na string.\n\nIn non-lisp languages, this step (called \"lexing and parsing\") can be\none of the most complicated parts of the compiler/interpreter. In\nLisp, the data structure that you want in memory is basically\nrepresented directly in the code that the programmer writes\n(homoiconicity).\n\nFor example, if the string is \"(+ 2 (* 3 4))\" then the read function\nwill process this into a tree structure that looks like this:\n```\n          List\n         / |  \\\n        /  |   \\\n       /   |    \\\n  Sym:+  Int:2  List\n               / |  \\\n              /  |   \\\n             /   |    \\\n         Sym:*  Int:3  Int:4\n```\n\nEach left paren and its matching right paren (lisp \"sexpr\") becomes\na node in the tree and everything else becomes a leaf in the tree.\n\nIf you can find code for an implementation of a JSON encoder/decoder\nin your target language then you can probably just borrow and modify\nthat and be 75% of the way done with this step.\n\nThe rest of this section is going to assume that you are not starting\nfrom an existing JSON encoder/decoder, but that you do have access to\na Perl compatible regular expressions (PCRE) module/library. You can\ncertainly implement the reader using simple string operations, but it\nis more involved. The `make`, `ps` (postscript) and Haskell\nimplementations have examples of a reader/parser without using regular\nexpression support.\n\n* Copy `step0_repl.qx` to `step1_read_print.qx`.\n\n* Add a `reader.qx` file to hold functions related to the reader.\n\n* If the target language has object types (OOP), then the next step\n  is to create a simple stateful Reader object in `reader.qx`. This\n  object will store the tokens and a position. The Reader object will\n  have two methods: `next` and `peek`. `next` returns the token at\n  the current position and increments the position. `peek` just\n  returns the token at the current position.\n\n* Add a function `read_str` in `reader.qx`. This function\n  will call `tokenize` and then create a new Reader object instance\n  with the tokens. Then it will call `read_form` with the Reader\n  instance.\n\n* Add a function `tokenize` in `reader.qx`. This function will take\n  a single string and return an array/list\n  of all the tokens (strings) in it. The following regular expression\n  (PCRE) will match all mal tokens.\n```\n[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)\n```\n* For each match captured within the parenthesis starting at char 6 of the\n  regular expression a new token will be created.\n\n  * `[\\s,]*`: Matches any number of whitespaces or commas. This is not captured\n    so it will be ignored and not tokenized.\n\n  * `~@`: Captures the special two-characters `~@` (tokenized).\n\n  * ```[\\[\\]{}()'`~^@]```: Captures any special single character, one of\n    ```[]{}()'`~^@``` (tokenized).\n\n  * `\"(?:\\\\.|[^\\\\\"])*\"?`: Starts capturing at a double-quote and stops at the\n    next double-quote unless it was preceded by a backslash in which case it\n    includes it until the next double-quote (tokenized). It will also\n    match unbalanced strings (no ending double-quote) which should be\n    reported as an error.\n\n  * `;.*`: Captures any sequence of characters starting with `;` (tokenized).\n\n  * ```[^\\s\\[\\]{}('\"`,;)]*```: Captures a sequence of zero or more non special\n    characters (e.g. symbols, numbers, \"true\", \"false\", and \"nil\") and is sort\n    of the inverse of the one above that captures special characters (tokenized).\n\n* Add the function `read_form` to `reader.qx`. This function\n  will peek at the first token in the Reader object and switch on the\n  first character of that token. If the character is a left paren then\n  `read_list` is called with the Reader object. Otherwise, `read_atom`\n  is called with the Reader Object. The return value from `read_form`\n  is a mal data type. If your target language is statically typed then\n  you will need some way for `read_form` to return a variant or\n  subclass type. For example, if your language is object oriented,\n  then you can define a top level MalType (in `types.qx`) that all\n  your mal data types inherit from. The MalList type (which also\n  inherits from MalType) will contain a list/array of other MalTypes.\n  If your language is dynamically typed then you can likely just\n  return a plain list/array of other mal types.\n\n* Add the function `read_list` to `reader.qx`. This function will\n  repeatedly call `read_form` with the Reader object until it\n  encounters a ')' token (if it reaches EOF before reading a ')' then\n  that is an error). It accumulates the results into a List type.  If\n  your language does not have a sequential data type that can hold mal\n  type values you may need to implement one (in `types.qx`).  Note\n  that `read_list` repeatedly calls `read_form` rather than\n  `read_atom`. This mutually recursive definition between `read_list`\n  and `read_form` is what allows lists to contain lists.\n\n* Add the function `read_atom` to `reader.qx`. This function will\n  look at the contents of the token and return the appropriate scalar\n  (simple/single) data type value. Initially, you can just implement\n  numbers (integers) and symbols. This will allow you to proceed\n  through the next couple of steps before you will need to implement\n  the other fundamental mal types: nil, true, false, and string. The\n  remaining scalar mal type, keyword does not\n  need to be implemented until step A (but can be implemented at any\n  point between this step and that). BTW, symbol types are just an\n  object that contains a single string name value (some languages have\n  symbol types already).\n\n* Add a file `printer.qx`. This file will contain a single function\n  `pr_str` which does the opposite of `read_str`: take a mal data\n  structure and return a string representation of it. But `pr_str` is\n  much simpler and is basically just a switch statement on the type of\n  the input object:\n\n  * symbol: return the string name of the symbol\n  * number: return the number as a string\n  * list: iterate through each element of the list calling `pr_str` on\n    it, then join the results with a space separator, and surround the\n    final result with parens\n\n* Change the `READ` function in `step1_read_print.qx` to call\n  `reader.read_str` and the `PRINT` function to call `printer.pr_str`.\n  `EVAL` continues to simply return its input but the type is now\n  a mal data type.\n\nYou now have enough hooked up to begin testing your code. You can\nmanually try some simple inputs:\n  * `123` -> `123`\n  * `   123  ` -> `123`\n  * `abc` -> `abc`\n  * `   abc   ` -> `abc`\n  * `(123 456)` -> `(123 456)`\n  * `(  123   456 789   )   ` -> `(123 456 789)`\n  * `(  + 2   (*  3  4)  )  ` -> `(+ 2 (* 3 4))`\n\nTo verify that your code is doing more than just eliminating extra\nspaces (and not failing), you can instrument your `reader.qx` functions.\n\nOnce you have gotten past those simple manual tests, it is time to run\nthe full suite of step 1 tests. Go to the top level and run the\nfollowing:\n```\nmake \"test^quux^step1\"\n```\n\nFix any test failures related to symbols, numbers and lists.\n\nDepending on the functionality of your target language, it is likely\nthat you have now just completed one of the most difficult steps. It\nis down hill from here. The remaining steps will probably be easier\nand each step will give progressively more bang for the buck.\n\n#### Deferrable:\n\n\n* Add support for the other basic data type to your reader and printer\n  functions: string, nil, true, and false. Nil, true, and false\n  become mandatory at step 4, strings at step 6. When a string is read,\n  the following transformations are\n  applied: a backslash followed by a doublequote is translated into\n  a plain doublequote character, a backslash followed by \"n\" is\n  translated into a newline, and a backslash followed by another\n  backslash is translated into a single backslash. To properly print\n  a string (for step 4 string functions), the `pr_str` function needs\n  another parameter called `print_readably`.  When `print_readably` is\n  true, doublequotes, newlines, and backslashes are translated into\n  their printed representations (the reverse of the reader). The\n  `PRINT` function in the main program should call `pr_str` with\n  print_readably set to true.\n\n* Add error checking to your reader functions to make sure parens\n  are properly matched. Catch and print these errors in your main\n  loop. If your language does not have try/catch style bubble up\n  exception handling, then you will need to add explicit error\n  handling to your code to catch and pass on errors without crashing.\n\n* Add support for reader macros which are forms that are\n  transformed into other forms during the read phase. Refer to\n  `tests/step1_read_print.mal` for the form that these macros should\n  take (they are just simple transformations of the token stream).\n\n* Add support for the other mal types: keyword, vector, hash-map.\n  * keyword: a keyword is a token that begins with a colon. A keyword\n    can just be stored as a string with special unicode prefix like\n    0x29E (or char 0xff/127 if the target language does not have good\n    unicode support) and the printer translates strings with that\n    prefix back to the keyword representation. This makes it easy to\n    use keywords as hash map keys in most languages. You can also\n    store keywords as a unique data type, but you will need to make\n    sure they can be used as hash map keys (which may involve doing\n    a similar prefixed translation anyways).\n  * vector: a vector can be implemented with same underlying\n    type as a list as long as there is some mechanism to keep track of\n    the difference.\n    Vector literals are similar to lists, but use bracket as\n    delimiters instead of parenthesis.\n    For example, `[]` constructs an empty vector and `[1 \"a\"]` a\n    vector with two elements.\n    You can use the same reader function for both\n    lists and vectors by adding parameters for the starting and ending\n    tokens.\n  * hash-map: a hash-map is an associative data structure that maps\n    strings to other mal values. If you implement keywords as prefixed\n    strings, then you only need a native associative data structure\n    which supports string keys. Clojure allows any value to be a hash\n    map key, but the base functionality in mal is to support strings\n    and keyword keys.\n    Hash-map literals are constructed with braces delimiters.\n    For example,\n    `{}` constructs an empty map,\n    `{\"a\" 1 :b \"whatever\"}` associates the `a` key to an integer value\n    and the `:b` key to a string value.\n    Because of the representation of hash-maps as\n    an alternating sequence of keys and values, you can probably use\n    the same reader function for hash-maps as lists and vectors with\n    parameters to indicate the starting and ending tokens. The odd\n    tokens are then used for keys with the corresponding even tokens\n    as the values.\n\n* Add comment support to your reader. The tokenizer should ignore\n  tokens that start with \";\". Your `read_str` function will need to\n  properly handle when the tokenizer returns no values. The simplest\n  way to do this is to return `nil` mal value. A cleaner option (that\n  does not print `nil` at the prompt is to throw a special exception\n  that causes the main loop to simply continue at the beginning of the\n  loop without calling `rep`.\n\n\n<a name=\"step2\"></a>\n\n### Step 2: Eval\n\n![step2_eval architecture](step2_eval.png)\n\nIn step 1 your mal interpreter was basically just a way to validate\ninput and eliminate extraneous white space. In this step you will turn\nyour interpreter into a simple number calculator by adding\nfunctionality to the evaluator (`EVAL`).\n\nCompare the pseudocode for step 1 and step 2 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step1_read_print.txt ../../process/step2_eval.txt\n```\n\n* Copy `step1_read_print.qx` to `step2_eval.qx`.\n\n* Define a simple initial REPL environment. This environment is an\n  associative structure that maps symbols (or symbol names) to\n  numeric functions. For example, in python this would look something\n  like this:\n```\nrepl_env = {'+': lambda a,b: a+b,\n            '-': lambda a,b: a-b,\n            '*': lambda a,b: a*b,\n            '/': lambda a,b: int(a/b)}\n```\n\n* Modify the `rep` function to pass the REPL environment as the second\n  parameter for the `EVAL` call.\n\n* In `EVAL`, switch on the type of the first parameter `ast` as follows:\n\n  * symbol: lookup the symbol in the environment structure and return\n    the value.\n    If the key is missing, throw/raise a \"not found\" error.\n\n  * `ast` is a non-empty list:\n    call `EVAL` on each of the members of the list.\n    Take the first evaluated item and call it as function using\n    the rest of the evaluated items as its arguments.\n  * otherwise just return the original `ast` value\n\nIf your target language does not have full variable length argument\nsupport (e.g. variadic, vararg, splats, apply) then you will need to\npass the full list of arguments as a single parameter and split apart\nthe individual values inside of every mal function. This is annoying,\nbut workable.\n\nThe process of taking a list and invoking or executing it to return\nsomething new is known in Lisp as the \"apply\" phase.\n\nTry some simple expressions:\n\n  * `(+ 2 3)` -> `5`\n  * `(+ 2 (* 3 4))` -> `14`\n\nThe most likely challenge you will encounter is how to properly call\na function reference using an arguments list.\n\nNow go to the top level, run the step 2 tests and fix the errors.\n```\nmake \"test^quux^step2\"\n```\n\nYou now have a simple prefix notation calculator!\n\n#### Deferrable:\n\n* Add a print statement at the top of the main `eval` function, for\n  debugging issues or simply figuring how evaluation works.\n  The statement should be active when `env` contains the `DEBUG-EVAL`\n  key and the associated value is neither `nil` nor `false`.\n  For consistency, it should print \"EVAL: \" followed by the current\n  value of `ast` formatted with `pr_str` with the readably flag set.\n  Feel free to add any information you see fit, for example the\n  contents of `env`.\n\n* `EVAL` should evaluate elements of vectors and hash-maps.  Add the\n  following cases in `EVAL`:\n  * If `ast` is a vector: return a new vector that is the result of calling\n    `EVAL` on each of the members of the vector.\n  * If `ast` is a hash-map: return a new hash-map which consists of key-value\n    pairs where the key is a key from the hash-map and the value is the result\n    of calling `EVAL` on the corresponding value.\n    Depending on the implementation of maps, it may be convenient to\n    also call `EVAL` on keys.  The result is the same because keys are\n    not affected by evaluation.\n\n\n<a name=\"step3\"></a>\n\n### Step 3: Environments\n\n![step3_env architecture](step3_env.png)\n\nIn step 2 you were already introduced to REPL environment (`repl_env`)\nwhere the basic numeric functions were stored and looked up. In this\nstep you will add the ability to create new environments (`let*`) and\nmodify existing environments (`def!`).\n\nA Lisp environment is an associative data structure that maps symbols (the\nkeys) to values. But Lisp environments have an additional important\nfunction: they can refer to another environment (the outer\nenvironment). During environment lookups, if the current environment\ndoes not have the symbol, the lookup continues in the outer\nenvironment, and continues this way until the symbol is either found,\nor the outer environment is `nil` (the outermost environment in the\nchain).\n\nCompare the pseudocode for step 2 and step 3 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step2_eval.txt ../../process/step3_env.txt\n```\n\n* Copy `step2_eval.qx` to `step3_env.qx`.\n\n* Create `env.qx` to hold the environment definition.\n\n* Define an `Env` object that is instantiated with a single `outer`\n  parameter and starts with an empty associative data structure\n  property `data`.\n\n* Define three methods for the Env object:\n  * set: takes a symbol key and a mal value and adds to the `data`\n    structure\n  * get: takes a symbol key and if the current environment contains\n    that key then return the matching value. If no key is found and outer\n    is not `nil` then call get (recurse) on the outer environment.\n    Depending on the host language, a loop structure may be more\n    simple or efficient than a recursion.\n    If no key is found up the outer chain, then report that the key is\n    missing with the most idiomatic mechanism.\n\n* Update `step3_env.qx` to use the new `Env` type to create the\n  repl_env (with a `nil` outer value) and use the `set` method to add\n  the numeric functions.\n\n* Modify `EVAL` to call the `get` method on the `env` parameter.\n\n* Modify the apply section of `EVAL` to switch on the first element of\n  the list:\n  * symbol \"def!\": call the set method of the current environment\n    (second parameter of `EVAL` called `env`) using the unevaluated\n    first parameter (second list element) as the symbol key and the\n    evaluated second parameter as the value.\n  * symbol \"let\\*\": create a new environment using the current\n    environment as the outer value and then use the first parameter as\n    a list of new bindings in the \"let\\*\" environment. Take the second\n    element of the binding list, call `EVAL` using the new \"let\\*\"\n    environment as the evaluation environment, then call `set` on the\n    \"let\\*\" environment using the first binding list element as the key\n    and the evaluated second element as the value. This is repeated\n    for each odd/even pair in the binding list. Note in particular,\n    the bindings earlier in the list can be referred to by later\n    bindings. Finally, the second parameter (third element) of the\n    original `let*` form is evaluated using the new \"let\\*\" environment\n    and the result is returned as the result of the `let*` (the new\n    let environment is discarded upon completion).\n  * otherwise: proceed as before.\n\n`def!` and `let*` are Lisp \"specials\" (or \"special atoms\") which means\nthat they are language level features and more specifically that the\nrest of the list elements (arguments) may be evaluated differently (or\nnot at all) unlike the default apply case where all elements of the\nlist are evaluated before the first element is invoked. Lists which\ncontain a \"special\" as the first element are known as \"special forms\".\nThey are special because they follow special evaluation rules.\n\nTry some simple environment tests:\n\n  * `(def! a 6)` -> `6`\n  * `a` -> `6`\n  * `(def! b (+ a 2))` -> `8`\n  * `(+ a b)` -> `14`\n  * `(let* (c 2) c)` -> `2`\n\nNow go to the top level, run the step 3 tests and fix the errors.\n```\nmake \"test^quux^step3\"\n```\n\nYour mal implementation is still basically just a numeric calculator\nwith save/restore capability. But you have set the foundation for step\n4 where it will begin to feel like a real programming language.\n\n\nAn aside on mutation and typing:\n\nThe \"!\" suffix on symbols is used to indicate that this symbol refers\nto a function that mutates something else. In this case, the `def!`\nsymbol indicates a special form that will mutate the current\nenvironment. Many (maybe even most) of runtime problems that are\nencountered in software engineering are a result of mutation. By\nclearly marking code where mutation may occur, you can more easily\ntrack down the likely cause of runtime problems when they do occur.\n\nAnother cause of runtime errors is type errors, where a value of one\ntype is unexpectedly treated by the program as a different and\nincompatible type. Statically typed languages try to make the\nprogrammer solve all type problems before the program is allowed to\nrun. Most Lisp variants tend to be dynamically typed (types of values\nare checked when they are actually used at runtime).\n\nAs an aside-aside: The great debate between static and dynamic typing\ncan be understood by following the money. Advocates of strict static\ntyping use words like \"correctness\" and \"safety\" and thus get\ngovernment and academic funding. Advocates of dynamic typing use words\nlike \"agile\" and \"time-to-market\" and thus get venture capital and\ncommercial funding.\n\n\n<a name=\"step4\"></a>\n\n### Step 4: If Fn Do\n\n![step4_if_fn_do architecture](step4_if_fn_do.png)\n\nIn step 3 you added environments and the special forms for\nmanipulating environments. In this step you will add 3 new special\nforms (`if`, `fn*` and `do`) and add several more core functions to\nthe default REPL environment. Our new architecture will look like\nthis:\n\nThe `fn*` special form is how new user-defined functions are created.\nIn some Lisps, this special form is named \"lambda\".\n\nCompare the pseudocode for step 3 and step 4 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step3_env.txt ../../process/step4_if_fn_do.txt\n```\n\n* Copy `step3_env.qx` to `step4_if_fn_do.qx`.\n\n* If you have not implemented reader and printer support (and data\n  types) for `nil`, `true` and `false`, you will need to do so for\n  this step.\n\n* Update the constructor/initializer for environments to take two new\n  parameters: `binds` and `exprs`. Bind (`set`) each element (symbol)\n  of the `binds` list to the respective element of the `exprs` list.\n\n* Add support to `printer.qx` to print function values. A string\n  literal like \"#\\<function>\" is sufficient.\n\n* Add the following special forms to `EVAL`:\n\n  * `do`: Evaluate all the elements of the list\n    and return the final evaluated element.\n  * `if`: Evaluate the first parameter (second element). If the result\n    (condition) is anything other than `nil` or `false`, then evaluate\n    the second parameter (third element of the list) and return the\n    result.  Otherwise, evaluate the third parameter (fourth element)\n    and return the result. If condition is false and there is no third\n    parameter, then just return `nil`.\n  * `fn*`: Return a new function closure. The body of that closure\n    does the following:\n    * Create a new environment using `env` (closed over from outer\n      scope) as the `outer` parameter, the first parameter (second\n      list element of `ast` from the outer scope) as the `binds`\n      parameter, and the parameters to the closure as the `exprs`\n      parameter.\n    * Call `EVAL` on the second parameter (third list element of `ast`\n      from outer scope), using the new environment. Use the result as\n      the return value of the closure.\n\nIf your target language does not support closures, then you will need\nto implement `fn*` using some sort of structure or object that stores\nthe values being closed over: the first and second elements of the\n`ast` list (function parameter list and function body) and the current\nenvironment `env`. In this case, your native functions will need to be\nwrapped in the same way. You will probably also need a method/function\nthat invokes your function object/structure for the default case of\nthe apply section of `EVAL`.\n\nTry out the basic functionality you have implemented:\n\n  * `(fn* (a) a)` -> `#<function>`\n  * `( (fn* (a) a) 7)` -> `7`\n  * `( (fn* (a) (+ a 1)) 10)` -> `11`\n  * `( (fn* (a b) (+ a b)) 2 3)` -> `5`\n\n* Add a new file `core.qx` and define an associative data structure\n  `ns` (namespace) that maps symbols to functions. Move the numeric\n  function definitions into this structure.\n\n* Modify `step4_if_fn_do.qx` to iterate through the `core.ns`\n  structure and add (`set`) each symbol/function mapping to the\n  REPL environment (`repl_env`).\n\n* Add the following functions to `core.ns`:\n  * `prn`: call `pr_str` on the first parameter with `print_readably`\n    set to true, print the result to the screen and then return\n    `nil`. Note that the full version of `prn` is a deferrable below.\n  * `list`: take the parameters and return them as a list.\n  * `list?`: return true if the first parameter is a list, false\n    otherwise.\n  * `empty?`: treat the first parameter as a list and return true if\n    the list is empty and false if it contains any elements.\n  * `count`: treat the first parameter as a list and return the number\n    of elements that it contains.\n  * `=`: compare the first two parameters and return true if they are\n    the same type and contain the same value. In the case of equal\n    length lists, each element of the list should be compared for\n    equality and if they are the same return true, otherwise false.\n  * `<`, `<=`, `>`, and `>=`: treat the first two parameters as\n    numbers and do the corresponding numeric comparison, returning\n    either true or false.\n\nNow go to the top level, run the step 4 tests. There are a lot of\ntests in step 4 but all of the non-optional tests that do not involve\nstrings should be able to pass now.\n\n```\nmake \"test^quux^step4\"\n```\n\nYour mal implementation is already beginning to look like a real\nlanguage. You have flow control, conditionals, user-defined functions\nwith lexical scope, side-effects (if you implement the string\nfunctions), etc. However, our little interpreter has not quite reached\nLisp-ness yet. The next several steps will take your implementation\nfrom a neat toy to a full featured language.\n\n#### Deferrable:\n\n* Implement Clojure-style variadic function parameters. Modify the\n  constructor/initializer for environments, so that if a \"&\" symbol is\n  encountered in the `binds` list, the next symbol in the `binds` list\n  after the \"&\" is bound to the rest of the `exprs` list that has not\n  been bound yet.\n\n* Define a `not` function using mal itself. In `step4_if_fn_do.qx`\n  call the `rep` function with this string:\n  \"(def! not (fn* (a) (if a false true)))\".\n\n* Implement the string functions in `core.qx`. To implement these\n  functions, you will need to implement the string support in the\n  reader and printer (deferrable section of step 1). Each of the string\n  functions takes multiple mal values, prints them (`pr_str`) and\n  joins them together into a new string.\n  * `pr-str`: calls `pr_str` on each argument with `print_readably`\n    set to true, joins the results with \" \" and returns the new\n    string.\n  * `str`: calls `pr_str` on each argument with `print_readably` set\n    to false, concatenates the results together (\"\" separator), and\n    returns the new string.\n  * `prn`:  calls `pr_str` on each argument with `print_readably` set\n    to true, joins the results with \" \", prints the string to the\n    screen and then returns `nil`.\n  * `println`:  calls `pr_str` on each argument with `print_readably` set\n    to false, joins the results with \" \", prints the string to the\n    screen and then returns `nil`.\n\n\n<a name=\"step5\"></a>\n\n### Step 5: Tail call optimization\n\n![step5_tco architecture](step5_tco.png)\n\nIn step 4 you added special forms `do`, `if` and `fn*` and you defined\nsome core functions. In this step you will add a Lisp feature called\ntail call optimization (TCO). Also called \"tail recursion\" or\nsometimes just \"tail calls\".\n\nSeveral of the special forms that you have defined in `EVAL` end up\ncalling back into `EVAL`. For those forms that call `EVAL` as the last\nthing that they do before returning (tail call) you will just loop back\nto the beginning of eval rather than calling it again. The advantage\nof this approach is that it avoids adding more frames to the call\nstack. This is especially important in Lisp languages because they tend\nto prefer using recursion instead of iteration for control structures.\n(Though some Lisps, such as Common Lisp, have iteration.) However, with\ntail call optimization, recursion can be made as stack efficient as\niteration.\n\nCompare the pseudocode for step 4 and step 5 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step4_if_fn_do.txt ../../process/step5_tco.txt\n```\n\n* Copy `step4_if_fn_do.qx` to `step5_tco.qx`.\n\n* Add a loop (e.g. while true) around all code in `EVAL`.\n\n* Modify each of the following form cases to add tail call recursion\n  support:\n  * `let*`: remove the final `EVAL` call on the second `ast` argument\n    (third list element). Set `env` (i.e. the local variable passed in\n    as second parameter of `EVAL`) to the new let environment. Set\n    `ast` (i.e. the local variable passed in as first parameter of\n    `EVAL`) to be the second `ast` argument. Continue at the beginning\n    of the loop (no return).\n  * `do`: change the implementation to evaluate all the parameters\n    except for the last (2nd list element up to but not including\n    last). Set `ast` to the last element of `ast`. Continue\n    at the beginning of the loop (`env` stays unchanged).\n  * `if`: the condition continues to be evaluated, however, rather\n    than evaluating the true or false branch, `ast` is set to the\n    unevaluated value of the chosen branch. Continue at the beginning\n    of the loop (`env` is unchanged).\n\n* The return value from the `fn*` special form will now become an\n  object/structure with attributes that allow the default invoke case\n  of `EVAL` to do TCO on mal functions. Those attributes are:\n  * `ast`: the second `ast` argument (third list element) representing\n    the body of the function.\n  * `params`: the first `ast` argument (second list element)\n    representing the parameter names of the function.\n  * `env`: the current value of the `env` parameter of `EVAL`.\n  * `fn`: the original function value (i.e. what was return by `fn*`\n    in step 4). Note that this is deferrable until step 9 when it is\n    required for the `map` and `apply` core functions). You will also\n    need it in step 6 if you choose to not to defer atoms/`swap!` from\n    that step.\n\n* The default \"apply\"/invoke case of `EVAL` must now be changed to\n  account for the new object/structure returned by the `fn*` form.\n  Once each element of `ast` is evaluated, the first element of the\n  result of `eval_ast` is `f` and the remaining elements are in `args`.\n  Switch on the type of `f`:\n  * regular function (not one defined by `fn*`): apply/invoke it as\n    before (in step 4).\n  * a `fn*` value: set `ast` to the `ast` attribute of `f`. Generate\n    a new environment using the `env` and `params` attributes of `f`\n    as the `outer` and `binds` arguments and `args` as the `exprs`\n    argument. Set `env` to the new environment. Continue at the\n    beginning of the loop.\n\nRun some manual tests from previous steps to make sure you have not\nbroken anything by adding TCO.\n\nNow go to the top level, run the step 5 tests.\n\n```\nmake \"test^quux^step5\"\n```\n\nLook at the step 5 test file `tests/step5_tco.mal`. The `sum-to`\nfunction cannot be tail call optimized because it does something after\nthe recursive call (`sum-to` calls itself and then does the addition).\nLispers say that the `sum-to` is not in tail position. The `sum2`\nfunction however, calls itself from tail position. In other words, the\nrecursive call to `sum2` is the last action that `sum2` does. Calling\n`sum-to` with a large value will cause a stack overflow exception in\nmost target languages (some have super-special tricks they use to\navoid stack overflows).\n\nCongratulations, your mal implementation already has a feature (TCO)\nthat most mainstream languages lack.\n\n\n<a name=\"step6\"></a>\n\n### Step 6: Files, Mutation, and Evil\n\n![step6_file architecture](step6_file.png)\n\nIn step 5 you added tail call optimization. In this step you will add\nsome string and file operations and give your implementation a touch\nof evil ... er, eval. And as long as your language supports function\nclosures, this step will be quite simple. However, to complete this\nstep, you must implement string type support, so if you have been\nholding off on that you will need to go back and do so.\n\nCompare the pseudocode for step 5 and step 6 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step5_tco.txt ../../process/step6_file.txt\n```\n\n* Copy `step5_tco.qx` to `step6_file.qx`.\n\n* Add two new string functions to the core namespaces:\n  * `read-string`: this function just exposes the `read_str` function\n    from the reader. If your mal string type is not the same as your\n    target language (e.g. statically typed language) then your\n    `read-string` function will need to unbox (extract) the raw string\n    from the mal string type in order to call `read_str`.\n  * `slurp`: this function takes a file name (string) and returns the\n    contents of the file as a string. Once again, if your mal string\n    type wraps a raw target language string, then you will need to\n    unmarshall (extract) the string parameter to get the raw file name\n    string and marshall (wrap) the result back to a mal string type.\n\n* In your main program, add a new symbol \"eval\" to your REPL\n  environment. The value of this new entry is a function that takes\n  a single argument `ast`. The closure calls your `EVAL` function\n  using the `ast` as the first argument and the REPL environment\n  (closed over from outside) as the second argument. The result of\n  the `EVAL` call is returned. This simple but powerful addition\n  allows your program to treat mal data as a mal program. For example,\n  you can now do this:\n```\n(def! mal-prog (list + 1 2))\n(eval mal-prog)\n```\n\n* Define a `load-file` function using mal itself. In your main\n  program call the `rep` function with this string:\n  \"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\".\n\nTry out `load-file`:\n  * `(load-file \"../tests/incA.mal\")` -> `9`\n  * `(inc4 3)` -> `7`\n\nThe `load-file` function does the following:\n  * Call `slurp` to read in a file by name. Surround the contents with\n    \"(do ...)\" so that the whole file will be treated as a single\n    program AST (abstract syntax tree). Add a new line in case the files\n    ends with a comment. The `nil` ensures a short and predictable result,\n    instead of what happens to be the last function defined in the loaded file.\n  * Call `read-string` on the string returned from `slurp`. This uses\n    the reader to read/convert the file contents into mal data/AST.\n  * Call `eval` (the one in the REPL environment) on the AST returned\n    from `read-string` to \"run\" it.\n\nBesides adding file and eval support, we'll add support for the atom data type\nin this step.  An atom is the Mal way to represent *state*; it is\nheavily inspired by [Clojure's atoms](http://clojure.org/state).  An atom holds\na reference to a single Mal value of any type; it supports reading that Mal value\nand *modifying* the reference to point to another Mal value.  Note that this is\nthe only Mal data type that is mutable (but the Mal values it refers to are\nstill immutable; immutability is explained in greater detail in step 7).\nYou'll need to add 5 functions to the core namespace to support atoms:\n\n  * `atom`: Takes a Mal value and returns a new atom which points to that Mal value.\n  * `atom?`: Takes an argument and returns `true` if the argument is an atom.\n  * `deref`: Takes an atom argument and returns the Mal value referenced by this atom.\n  * `reset!`: Takes an atom and a Mal value; the atom is modified to refer to\n    the given Mal value. The Mal value is returned.\n  * `swap!`: Takes an atom, a function, and zero or more function arguments. The\n    atom's value is modified to the result of applying the function with the atom's\n    value as the first argument and the optionally given function arguments as\n    the rest of the arguments. The new atom's value is returned. (Side note: Mal is\n    single-threaded, but in concurrent languages like Clojure, `swap!` promises\n    atomic update: `(swap! myatom (fn* [x] (+ 1 x)))` will always increase the\n    `myatom` counter by one and will not suffer from missing updates when the\n    atom is updated from multiple threads.)\n\nOptionally, you can add a reader macro `@` which will serve as a short form for\n`deref`, so that `@a` is equivalent to `(deref a)`.  In order to do that, modify\nthe conditional in reader function `read_form` and add a case which deals with\nthe `@` token: if the token is `@` (at sign) then return a new list that\ncontains the symbol `deref` and the result of reading the next form\n(`read_form`).\n\nNow go to the top level, run the step 6 tests. The optional tests will\nneed support from the reader for comments, vectors, hash-maps and the `@`\nreader macro:\n```\nmake \"test^quux^step6\"\n```\n\nCongratulations, you now have a full-fledged scripting language that\ncan run other mal programs. The `slurp` function loads a file as\na string, the `read-string` function calls the mal reader to turn that\nstring into data, and the `eval` function takes data and evaluates it\nas a normal mal program. However, it is important to note that the\n`eval` function is not just for running external programs. Because mal\nprograms are regular mal data structures, you can dynamically generate\nor manipulate those data structures before calling `eval` on them.\nThis isomorphism (same shape) between data and programs is known as\n\"homoiconicity\". Lisp languages are homoiconic and this property\ndistinguishes them from most other programming languages.\n\nYour mal implementation is quite powerful already but the set of\nfunctions that are available (from `core.qx`) is fairly limited. The\nbulk of the functions you will add are described in step 9 and step A,\nbut you will begin to flesh them out over the next few steps to\nsupport quoting (step 7) and macros (step 8).\n\n\n#### Deferrable:\n\n* Add the ability to run another mal program from the command line.\n  Prior to the REPL loop, check if your mal implementation is called\n  with command line arguments. If so, treat the first argument as\n  a filename and use `rep` to call `load-file` on that filename, and\n  finally exit/terminate execution.\n\n* Add the rest of the command line arguments to your REPL environment\n  so that programs that are run with `load-file` have access to their\n  calling environment. Add a new \"\\*ARGV\\*\" (symbol) entry to your REPL\n  environment. The value of this entry should be the rest of the\n  command line arguments as a mal list value.\n\n\n<a name=\"step7\"></a>\n\n### Step 7: Quoting\n\n![step7_quote architecture](step7_quote.png)\n\nIn step 7 you will add the special forms `quote` and `quasiquote` and\nadd supporting core functions `cons` and `concat`. The two quote forms\nadd a powerful abstraction for manipulating mal code itself\n(meta-programming).\n\nThe `quote` special form indicates to the evaluator (`EVAL`) that the\nparameter should not be evaluated (yet). At first glance, this might\nnot seem particularly useful but an example of what this enables is the\nability for a mal program to refer to a symbol itself rather than the\nvalue that it evaluates to. Likewise with lists. For example, consider\nthe following:\n\n* `(prn abc)`: this will lookup the symbol `abc` in the current\n  evaluation environment and print it. This will result in an error if\n  `abc` is not defined.\n* `(prn (quote abc))`: this will print \"abc\" (prints the symbol\n  itself). This will work regardless of whether `abc` is defined in\n  the current environment.\n* `(prn (1 2 3))`: this will result in an error because `1` is not\n  a function and cannot be applied to the arguments `(2 3)`.\n* `(prn (quote (1 2 3)))`: this will print \"(1 2 3)\".\n* `(def! l (quote (1 2 3)))`: list quoting allows us to define lists\n  directly in the code (list literal). Another way of doing this is\n  with the list function: `(def! l (list 1 2 3))`.\n\nThe second special quoting form is `quasiquote`. This allows a quoted\nlist to have internal elements of the list that are temporarily\nunquoted (normal evaluation). There are two special forms that only\nmean something within a quasiquoted list: `unquote` and\n`splice-unquote`. These are perhaps best explained with some examples:\n\n* `(def! lst (quote (b c)))` -> `(b c)`\n* `(quasiquote (a lst d))` -> `(a lst d)`\n* `(quasiquote (a (unquote lst) d))` -> `(a (b c) d)`\n* `(quasiquote (a (splice-unquote lst) d))` -> `(a b c d)`\n\nThe `unquote` form turns evaluation back on for its argument and the\nresult of evaluation is put in place into the quasiquoted list. The\n`splice-unquote` also turns evaluation back on for its argument, but\nthe evaluated value must be a list which is then \"spliced\" into the\nquasiquoted list. The true power of the quasiquote form will be\nmanifest when it is used together with macros (in the next step).\n\nCompare the pseudocode for step 6 and step 7 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step6_file.txt ../../process/step7_quote.txt\n```\n\n* Copy `step6_file.qx` to `step7_quote.qx`.\n\n* Before implementing the quoting forms, you will need to implement\n  some supporting functions in the core namespace:\n  * `cons`: this function takes a list as its second\n    parameter and returns a new list that has the first argument\n    prepended to it.\n  * `concat`: this functions takes 0 or more lists as\n    parameters and returns a new list that is a concatenation of all\n    the list parameters.\n\nAn aside on immutability: note that neither cons or concat mutate\ntheir original list arguments. Any references to them (i.e. other\nlists that they may be \"contained\" in) will still refer to the\noriginal unchanged value. Mal, like Clojure, is a language which uses\nimmutable data structures. I encourage you to read about the power and\nimportance of immutability as implemented in Clojure (from which\nMal borrows most of its syntax and feature-set).\n\n* Add the `quote` special form. This form just returns its argument\n  (the second list element of `ast`).\n\n* Add the `quasiquote` function.\n  The `quasiquote` function takes a parameter `ast` and has the\n  following conditional.\n  - If `ast` is a list starting with the \"unquote\" symbol, return its\n    second element.\n  - If `ast` is a list failing the previous test, the result will be a\n    list populated by the following process.\n\n    The result is initially an empty list.\n    Iterate over each element `elt` of `ast` in reverse order:\n    - If `elt` is a list starting with the \"splice-unquote\" symbol,\n      replace the current result with a list containing:\n      the \"concat\" symbol,\n      the second element of `elt`,\n      then the previous result.\n    - Else replace the current result with a list containing:\n      the \"cons\" symbol,\n      the result of calling `quasiquote` with `elt` as argument,\n      then the previous result.\n\n    This process can also be described recursively:\n    - If `ast` is empty return it unchanged. else let `elt` be its\n      first element.\n    - If `elt` is a list starting with the \"splice-unquote\" symbol,\n      return a list containing:\n      the \"concat\" symbol,\n      the second element of `elt`,\n      then the result of processing the rest of `ast`.\n    - Else return a list containing:\n      the \"cons\" symbol,\n      the result of calling `quasiquote` with `elt` as argument,\n      then the result of processing the rest of `ast`.\n  - If `ast` is a map or a symbol, return a list containing:\n    the \"quote\" symbol,\n    then `ast`.\n  - Else return `ast` unchanged.\n    Such forms are not affected by evaluation, so you may quote them\n    as in the previous case if implementation is easier.\n\n* Add the `quasiquote` special form.\n  This form calls the `quasiquote` function using the first `ast`\n  argument (second list element),\n  then evaluates the result in the current environment,\n  either by recursively calling `EVAL` with the result and `env`,\n  or by assigning `ast` with the result and continuing execution at\n  the top of the loop (TCO).\n\nNow go to the top level, run the step 7 tests:\n```\nmake \"test^quux^step7\"\n```\n\nIf some tests do not pass, it may be convenient to enable the debug\nprint statement at the top of your main `eval` function (inside the\nTCO loop).  The quasiquoted but yet unevaluated AST will often reveal\nthe source of the issue.\n\nQuoting is one of the more mundane functions available in mal, but do\nnot let that discourage you. Your mal implementation is almost\ncomplete, and quoting sets the stage for the next very exciting step:\nmacros.\n\n\n#### Deferrable\n\n* The full names for the quoting forms are fairly verbose. Most Lisp\n  languages have a short-hand syntax and Mal is no exception. These\n  short-hand syntaxes are known as reader macros because they allow us\n  to manipulate mal code during the reader phase. Macros that run\n  during the eval phase are just called \"macros\" and are described in\n  the next section. Expand the conditional in reader function\n  `read_form` to add the following four cases:\n  * token is \"'\" (single quote): return a new list that contains the\n    symbol \"quote\" and the result of reading the next form\n    (`read_form`).\n  * token is \"\\`\" (back-tick): return a new list that contains the\n    symbol \"quasiquote\" and the result of reading the next form\n    (`read_form`).\n  * token is \"~\" (tilde): return a new list that contains the\n    symbol \"unquote\" and the result of reading the next form\n    (`read_form`).\n  * token is \"~@\" (tilde + at sign): return a new list that contains\n    the symbol \"splice-unquote\" and the result of reading the next\n    form (`read_form`).\n\n* Add support for quoting of vectors. `cons`\n  should also accept a vector as the second argument. The return value\n  is a list regardless.  `concat` should support concatenation of\n  lists, vectors, or a mix of both.  The result is always a list.\n\n  Implement a core function `vec` turning a list into a vector with\n  the same elements.  If provided a vector, `vec` should return it\n  unchanged.\n\n  In the `quasiquote` function, when `ast` is a vector,\n  return a list containing:\n  the \"vec\" symbol,\n  then the result of processing `ast` as if it were a list not\n  starting with `unquote`.\n\n<a name=\"step8\"></a>\n\n### Step 8: Macros\n\n![step8_macros architecture](step8_macros.png)\n\nYour mal implementation is now ready for one of the most lispy and\nexciting of all programming concepts: macros. In the previous step,\nquoting enabled some simple manipulation data structures and therefore\nmanipulation of mal code (because the `eval` function from step\n6 turns mal data into code). In this step you will be able to mark mal\nfunctions as macros which can manipulate mal code before it is\nevaluated. In other words, macros are user-defined special forms. Or\nto look at it another way, macros allow mal programs to redefine\nthe mal language itself.\n\nCompare the pseudocode for step 7 and step 8 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step7_quote.txt ../../process/step8_macros.txt\n```\n\n* Copy `step7_quote.qx` to `step8_macros.qx`.\n\n\nYou might think that the infinite power of macros would require some\nsort of complex mechanism, but the implementation is actually fairly\nsimple.\n\n* Add a new attribute `is_macro` to mal function types. This should\n  default to false.\n\n* Add a new special form `defmacro!`. This is very similar to the\n  `def!` form, but before the evaluated value (mal function) is set in\n  the environment, the `is_macro` attribute should be set to true.\n\n* In `EVAL`,\n  when `ast` is a non-empty list without leading special form,\n  the normal apply phase evaluates all elements of `ast`.\n\n  Start by evaluating the first element separately.\n  The result must be a function.\n  If this function does have the `is_macro` attribute set,\n\n  * apply the function to the (unevaluated) remaining elements of\n    `ast`, producing a new form.\n\n  * evaluate the new form in the `env` environment.\n    Of course, instead of recursively calling `EVAL`, replace `ast`\n    with the new form and restart the TCO loop.\n\n  For functions without the attribute, proceed as before: evaluate the\n  remaining elements of `ast`, then apply the function to them.\n\n\nIf you check existing implementations, be warned that former versions\nof this guide were describing a slightly different macro expansion\nmechanism.\n\nNow go to the top level, run the step 8 tests:\n```\nmake \"test^quux^step8\"\n```\n\nThere is a reasonably good chance that the macro tests will not pass\nthe first time. Although the implementation of macros is fairly\nsimple, debugging runtime bugs with macros can be fairly tricky. If\nyou do run into subtle problems that are difficult to solve, let me\n\nrecommend an approach:\n\n* Enable the debug print statement at the top of your main `eval`\n  function (inside the TCO loop).\n  The expanded but yet unevaluated AST will often reveal the source of\n  the issue.\n\n* Pull up the step8\n  implementation from another language and uncomment its `eval`\n  function (yes, I give you permission to violate the rule this once).\n  Run the two side-by-side. The first difference is likely to point to\n  the bug.\n\nCongratulations! You now have a Lisp interpreter with a super power\nthat most non-Lisp languages can only dream of (I have it on good\nauthority that languages dream when you are not using them). If you\nare not already familiar with Lisp macros, I suggest the following\nexercise: write a recursive macro that handles postfixed mal code\n(with the function as the last parameter instead of the first). Or\nnot. I have not actually done so myself, but I have heard it is an\ninteresting exercise.\n\nIn the next step you will add try/catch style exception handling to\nyour implementation in addition to some new core functions. After\nstep9 you will be very close to having a fully self-hosting mal\nimplementation. Let us continue!\n\n\n#### Deferrable\n\n* Add the following new core functions which are frequently used in\n  macro functions:\n  * `nth`: this function takes a list (or vector) and a number (index)\n    as arguments, returns the element of the list at the given index.\n    If the index is out of range, this function raises an exception.\n  * `first`: this function takes a list (or vector) as its argument\n    and returns the first element. If the list (or vector) is empty or\n    is `nil` then `nil` is returned.\n  * `rest`: this function takes a list (or vector) as its argument and\n    returns a new list containing all the elements except the first. If\n    the list (or vector) is empty or is `nil` then `()` (empty list)\n    is returned.\n\n* In the main program, call the `rep` function with the following\n  string argument  to define a new control structure.\n```\n\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\"\n```\n  * Note that `cond` calls the `throw` function when `cond` is\n    called with an odd number of args. The `throw` function is\n    implemented in the next step, but it will still serve it's\n    purpose here by causing an undefined symbol error.\n\n\n<a name=\"step9\"></a>\n\n### Step 9: Try\n\n![step9_try architecture](step9_try.png)\n\nIn this step you will implement the final mal special form for\nerror/exception handling: `try*/catch*`. You will also add several core\nfunctions to your implementation. In particular, you will enhance the\nfunctional programming pedigree of your implementation by adding the\n`apply` and `map` core functions.\n\nCompare the pseudocode for step 8 and step 9 to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step8_macros.txt ../../process/step9_try.txt\n```\n\n* Copy `step8_macros.qx` to `step9_try.qx`.\n\n* Add the `try*/catch*` special form to the EVAL function. The\n  try catch form looks like this: `(try* A (catch* B C))`. The form\n  `A` is evaluated, if it throws an exception, then form `C` is\n  evaluated with a new environment that binds the symbol `B` to the\n  value of the exception that was thrown.\n  * If your target language has built-in try/catch style exception\n    handling then you are already 90% of the way done. Add a\n    (native language) try/catch block that evaluates `A` within\n    the try block and catches all exceptions. If an exception is\n    caught, then translate it to a mal type/value. For native\n    exceptions this is either the message string or a mal hash-map\n    that contains the message string and other attributes of the\n    exception. When a regular mal type/value is used as an\n    exception, you will probably need to store it within a native\n    exception type in order to be able to convey/transport it using\n    the native try/catch mechanism. Then you will extract the mal\n    type/value from the native exception. Create a new mal environment\n    that binds `B` to the value of the exception. Finally, evaluate `C`\n    using that new environment.\n  * If your target language does not have built-in try/catch style\n    exception handling then you have some extra work to do. One of the\n    most straightforward approaches is to create a a global error\n    variable that stores the thrown mal type/value. The complication\n    is that there are a bunch of places where you must check to see if\n    the global error state is set and return without proceeding. The\n    rule of thumb is that this check should happen at the top of your\n    EVAL function and also right after any call to EVAL (and after any\n    function call that might happen to call EVAL further down the\n    chain). Yes, it is ugly, but you were warned in the section on\n    picking a language.\n\n* Add the `throw` core function.\n  * If your language supports try/catch style exception handling, then\n    this function takes a mal type/value and throws/raises it as an\n    exception. In order to do this, you may need to create a custom\n    exception object that wraps a mal value/type.\n  * If your language does not support try/catch style exception\n    handling, then set the global error state to the mal type/value.\n\n* Add the `apply` and `map` core functions. In step 5, if you did not\n  add the original function (`fn`) to the structure returned from\n  `fn*`, then you will need to do so now.\n  * `apply`: takes at least two arguments. The first argument is\n    a function and the last argument is a list (or vector). The\n    function may be either a built-in core function,\n    an user function constructed with the `fn*` special form,\n    or a macro, not distinguished from the underlying user function). The\n    arguments between the function and the last argument (if there are\n    any) are concatenated with the final argument to create the\n    arguments that are used to call the function. The apply\n    function allows a function to be called with arguments that are\n    contained in a list (or vector). In other words, `(apply F A B [C\n    D])` is equivalent to `(F A B C D)`.\n  * `map`: takes a function and a list (or vector) and evaluates the\n    function against every element of the list (or vector) one at\n    a time and returns the results as a list.\n\n* Add some type predicate core functions. In Lisp, predicates are\n  functions that return true/false (or true value/nil) and typically\n  end in \"?\" or \"p\".\n  * `nil?`: takes a single argument and returns true (mal true value)\n    if the argument is nil (mal nil value).\n  * `true?`: takes a single argument and returns true (mal true value)\n    if the argument is a true value (mal true value).\n  * `false?`: takes a single argument and returns true (mal true\n    value) if the argument is a false value (mal false value).\n  * `symbol?`: takes a single argument and returns true (mal true\n    value) if the argument is a symbol (mal symbol value).\n\nNow go to the top level, run the step 9 tests:\n```\nmake \"test^quux^step9\"\n```\n\nYour mal implementation is now essentially a fully featured Lisp\ninterpreter. But if you stop now you will miss one of the most\nsatisfying and enlightening aspects of creating a mal implementation:\nself-hosting.\n\n#### Deferrable\n\n* Add the following new core functions:\n  * `symbol`: takes a string and returns a new symbol with the string\n    as its name.\n  * `keyword`: takes a string and returns a keyword with the same name\n    (usually just be prepending the special keyword\n    unicode symbol). This function should also detect if the argument\n    is already a keyword and just return it.\n  * `keyword?`: takes a single argument and returns true (mal true\n    value) if the argument is a keyword, otherwise returns false (mal\n    false value).\n  * `vector`: takes a variable number of arguments and returns\n    a vector containing those arguments.\n  * `vector?`: takes a single argument and returns true (mal true\n    value) if the argument is a vector, otherwise returns false (mal\n    false value).\n  * `sequential?`: takes a single argument and returns true (mal true\n    value) if it is a list or a vector, otherwise returns false (mal\n    false value).\n  * `hash-map`: takes a variable but even number of arguments and\n    returns a new mal hash-map value with keys from the odd arguments\n    and values from the even arguments respectively. This is basically\n    the functional form of the `{}` reader literal syntax.\n  * `map?`: takes a single argument and returns true (mal true\n    value) if the argument is a hash-map, otherwise returns false (mal\n    false value).\n  * `assoc`: takes a hash-map as the first argument and the remaining\n    arguments are odd/even key/value pairs to \"associate\" (merge) into\n    the hash-map. Note that the original hash-map is unchanged\n    (remember, mal values are immutable), and a new hash-map\n    containing the old hash-maps key/values plus the merged key/value\n    arguments is returned.\n  * `dissoc`: takes a hash-map and a list of keys to remove from the\n    hash-map. Again, note that the original hash-map is unchanged and\n    a new hash-map with the keys removed is returned. Key arguments\n    that do not exist in the hash-map are ignored.\n  * `get`: takes a hash-map and a key and returns the value of looking\n    up that key in the hash-map. If the key is not found in the\n    hash-map then nil is returned.\n  * `contains?`: takes a hash-map and a key and returns true (mal true\n    value) if the key exists in the hash-map and false (mal false\n    value) otherwise.\n  * `keys`: takes a hash-map and returns a list (mal list value) of\n    all the keys in the hash-map.\n  * `vals`: takes a hash-map and returns a list (mal list value) of\n    all the values in the hash-map.\n\n\n<a name=\"stepA\"></a>\n\n### Step A: Metadata, Self-hosting and Interop\n\n![stepA_mal architecture](stepA_mal.png)\n\nYou have reached the final step of your mal implementation. This step\nis kind of a catchall for things that did not fit into other steps.\nBut most importantly, the changes you make in this step will unlock\nthe magical power known as \"self-hosting\". You might have noticed\nthat one of the languages that mal is implemented in is \"mal\". Any mal\nimplementation that is complete enough can run the mal implementation\nof mal. You might need to pull out your hammock and ponder this for\na while if you have never built a compiler or interpreter before. Look\nat the step source files for the mal implementation of mal (it is not\ncheating now that you have reached step A).\n\nIf you deferred the implementation of keywords, vectors and hash-maps,\nnow is the time to go back and implement them if you want your\nimplementation to self-host.\n\nCompare the pseudocode for step 9 and step A to get a basic idea of\nthe changes that will be made during this step:\n```\ndiff -u ../../process/step9_try.txt ../../process/stepA_mal.txt\n```\n\n* Copy `step9_try.qx` to `stepA_mal.qx`.\n\n* Add the `readline` core function. This functions takes a\n  string that is used to prompt the user for input. The line of text\n  entered by the user is returned as a string. If the user sends an\n  end-of-file (usually Ctrl-D), then nil is returned.\n\n* Add a new \"\\*host-language\\*\" (symbol) entry to your REPL\n  environment. The value of this entry should be a mal string\n  containing the name of the current implementation.\n\n* When the REPL starts up (as opposed to when it is called with\n  a script and/or arguments), call the `rep` function with this string\n  to print a startup header:\n  \"(println (str \\\"Mal [\\\" \\*host-language\\* \\\"]\\\"))\".\n\n* Ensure that the REPL environment contains definitions for `time-ms`,\n  `meta`, `with-meta`, `fn?`\n  `string?`, `number?`, `seq`, and `conj`.  It doesn't really matter\n  what they do at this stage: they just need to be defined.  Making\n  them functions that raise a \"not implemented\" exception would be\n  fine.\n\nNow go to the top level, run the step A tests:\n```\nmake \"test^quux^stepA\"\n```\n\nOnce you have passed all the non-optional step A tests, it is time to\ntry self-hosting. Run your step A implementation as normal, but use\nthe file argument mode you added in step 6 to run each step\nfrom the mal implementation:\n```\n./stepA_mal.qx ../mal/step1_read_print.mal\n./stepA_mal.qx ../mal/step2_eval.mal\n...\n./stepA_mal.qx ../mal/step9_try.mal\n./stepA_mal.qx ../mal/stepA_mal.mal\n```\n\nThere is a very good chance that you will encounter an error at some\npoint while trying to run the mal in mal implementation steps above.\nDebugging failures that happen while self-hosting is MUCH more\ndifficult and mind bending. One of the best approaches I have\npersonally found is to add prn statements to the mal implementation\nstep (not your own implementation of mal) that is causing problems.\n\nAnother approach I have frequently used is to pull out the code from\nthe mal implementation that is causing the problem and simplify it\nstep by step until you have a simple piece of mal code that still\nreproduces the problem. Once the reproducer is simple enough you will\nprobably know where in your own implementation that problem is likely\nto be. Please add your simple reproducer as a test case so that future\nimplementers will fix similar issues in their code before they get to\nself-hosting when it is much more difficult to track down and fix.\n\nOnce you can manually run all the self-hosted steps, it is time to run\nall the tests in self-hosted mode:\n```\nmake MAL_IMPL=quux \"test^mal\"\n```\n\nWhen you run into problems (which you almost certainly will), use the\nsame process described above to debug them.\n\nCongratulations!!! When all the tests pass, you should pause for\na moment and consider what you have accomplished. You have implemented\na Lisp interpreter that is powerful and complete enough to run a large\nmal program which is itself an implementation of the mal language. You\nmight even be asking if you can continue the \"inception\" by using your\nimplementation to run a mal implementation which itself runs the mal\nimplementation.\n\n\n#### Optional additions\n\n* Add meta-data support to composite data types (lists, vectors\n  and hash-maps), and to functions (native or not), by adding a new\n  metadata attribute that refers to another mal value/type\n  (nil by default). Add the following metadata related core functions\n  (and remove any stub versions):\n  * `meta`: this takes a single mal function/list/vector/hash-map argument\n    and returns the value of the metadata attribute.\n  * `with-meta`: this function takes two arguments. The first argument\n    is a mal value and the second argument is another mal value/type\n    to set as metadata. A copy of the mal value is returned that has\n    its `meta` attribute set to the second argument.  Note that when\n    copying a mal function, it is important that the environment and\n    macro attribute are retained.\n  * Add a reader-macro that expands the token \"^\" to\n    return a new list that contains the symbol \"with-meta\" and the\n    result of reading the next next form (2nd argument) (`read_form`) and the\n    next form (1st argument) in that order\n    (metadata comes first with the ^ macro and the function second).\n  * If you implemented `defmacro!` as mutating an existing function\n    without copying it, you can now use the function copying mechanism\n    used for metadata to make functions immutable even in the\n    defmacro! case...\n\n* Add the following new core functions (and remove any stub versions):\n  * `time-ms`: takes no arguments and returns the number of\n    milliseconds since epoch (00:00:00 UTC January 1, 1970), or, if\n    not possible, since another point in time (`time-ms` is usually\n    used relatively to measure time durations).  After `time-ms` is\n    implemented, you can run the performance micro-benchmarks by\n    running `make perf^quux`.\n  * `conj`: takes a collection and one or more elements as arguments\n    and returns a new collection which includes the original\n    collection and the new elements.  If the collection is a list, a\n    new list is returned with the elements inserted at the start of\n    the given list in opposite order; if the collection is a vector, a\n    new vector is returned with the elements added to the end of the\n    given vector.\n  * `string?`: returns true if the parameter is a string.\n  * `number?`: returns true if the parameter is a number.\n  * `fn?`: returns true if the parameter is a function (internal or\n    user-defined).\n  * `macro?`: returns true if the parameter is a macro.\n  * `seq`: takes a list, vector, string, or nil. If an empty list,\n    empty vector, or empty string (\"\") is passed in then nil is\n    returned. Otherwise, a list is returned unchanged, a vector is\n    converted into a list, and a string is converted to a list\n    containing the original string split into single character\n    strings.\n* For interop with the target language, add this core function:\n  * `quux-eval`: takes a string, evaluates it in the target language,\n    and returns the result converted to the relevant Mal type. You may\n    also add other interop functions as you see fit; Clojure, for\n    example, has a function called `.` which allows calling Java\n    methods. If the target language is a static language, consider\n    using FFI or some language-specific reflection mechanism, if\n    available. The tests for `quux-eval` and any other interop\n    function should be added in `impls/quux/tests/stepA_mal.mal` (see\n    the [tests for `lua-eval`](../impls/lua/tests/stepA_mal.mal) as an\n    example).\n\n### Next Steps\n\n* Join our [Discord](https://discord.gg/CKgnNbJBpF) channel.\n* If you have created an implementation for a new target language (or\n  a unique and interesting variant of an existing implementation),\n  consider sending a pull request to add it into the main mal\n  repository. The [FAQ](../docs/FAQ.md#will-you-add-my-new-implementation)\n  describes general requirements for getting an implementation merged\n  into the main repository.\n* Take your interpreter implementation and have it emit source code in\n  the target language rather than immediately evaluating it. In other\n  words, create a compiler.\n* Pick a new target language and implement mal in it. Pick a language\n  that is very different from any that you already know.\n* Use your mal implementation to implement a real world project. Many\n  of these will force you to address interop. Some ideas:\n  * Web server (with mal as CGI language for extra points)\n  * An IRC/Slack chat bot\n  * An editor (GUI or curses) with mal as a scripting/extension\n    language.\n  * An AI player for a game like Chess or Go.\n* Implement a feature in your mal implementation that is not covered\n  by this guide. Some ideas:\n  * Namespaces\n  * Multi-threading support\n  * Errors with line numbers and/or stack traces.\n  * Lazy sequences\n  * Clojure-style protocols\n  * Full call/cc (call-with-current-continuation) support\n  * Explicit TCO (i.e. `recur`) with tail-position error checking\n"
  },
  {
    "path": "process/step0_repl.txt",
    "content": "--- step0_repl ----------------------------------\nREAD(str): return str\n\nEVAL(ast,env): return ast\n\nPRINT(exp): return exp\n\nrep(str): return PRINT(EVAL(READ(str),\"\"))\n\nmain loop: println(rep(readline(\"user> \")))\n\n"
  },
  {
    "path": "process/step1_read_print.txt",
    "content": "--- step1_read_print ----------------------------\nimport reader, printer\n\nREAD(str): return reader.read_str(str)\n\nEVAL(ast,env): return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrep(str): return PRINT(EVAL(READ(str),\"\"))\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n"
  },
  {
    "path": "process/step2_eval.txt",
    "content": "--- step2_eval ----------------------------------\nimport types, reader, printer\n\nREAD(str): return reader.read_str(str)\n\nEVAL(ast, env):\n  // prn('EVAL ast)\n  match ast:\n    'key:                     return env[key] or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              args = [EVAL(arg1, env) ..]\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = {'+: add_fn, ...}\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n"
  },
  {
    "path": "process/step3_env.txt",
    "content": "--- step3_env -----------------------------------\nimport types, reader, printer, env\n\nREAD(str): return reader.read_str(str)\n\nEVAL(ast, env):\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              return EVAL(form, env)\n    ('let* [k1 v1 ..] form):  // idem\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              args = [EVAL(arg1, env) ..]\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\nrepl_env.set('+, add_fn)\n    ...\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null)\n    data = hash_map()\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n"
  },
  {
    "path": "process/step4_if_fn_do.txt",
    "content": "--- step4_if_fn_do ------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nEVAL(ast, env):\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              return EVAL(form, env)\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              return EVAL(last, env)\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then return EVAL(yes, env)\n                                else return EVAL(no, env)\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                return EVAL(f.impl,\n                                            new Env(f.env, f.parm, args))\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\n\n;; core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\")\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n\n      'list:     list,\n      'list?:    list?,\n\n      'empty?:   empty?,\n      'count:    count}\n"
  },
  {
    "path": "process/step5_tco.txt",
    "content": "--- step5_tco -----------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nEVAL(ast, env):\n loop:\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              ast = form; continue\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              ast = last; continue\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then ast = yes; continue\n                                else ast = no;  continue\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                env = new Env(f.env, f.parm, args)\n                                ast = f.impl; continue\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\n\n;; core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\")\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n\n      'list:     list,\n      'list?:    list?,\n\n      'empty?:   empty?,\n      'count:    count}\n"
  },
  {
    "path": "process/step6_file.txt",
    "content": "--- step6_file ----------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nEVAL(ast, env):\n loop:\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              ast = form; continue\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              ast = last; continue\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then ast = yes; continue\n                                else ast = no;  continue\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                env = new Env(f.env, f.parm, args)\n                                ast = f.impl; continue\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\nrepl_env.set('eval, (ast) -> EVAL(ast, repl-env))\nrepl_env.set('*ARGV*, cmdline_args[1..])\n\n;; core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif cmdline_args: rep(\"(load-file \\\"\" + args[0] + \"\\\")\"); exit 0\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n      'read-string: read_str,\n      'slurp     read-file,\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n\n      'list:     list,\n      'list?:    list?,\n\n      'empty?:   empty?,\n      'count:    count,\n\n      'atom:     (a) -> new Atom(a[0]),\n      'atom?:    (a) -> type(a[0]) == \"atom\",\n      'deref:    (a) -> a[0].val,\n      'reset!:   (a) -> a[0].val = a[1],\n      'swap!:    swap!}\n"
  },
  {
    "path": "process/step7_quote.txt",
    "content": "--- step7_quote ---------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nquasiquote(ast): return ... // quasiquote\n\nEVAL(ast, env):\n loop:\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              ast = form; continue\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              ast = last; continue\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then ast = yes; continue\n                                else ast = no;  continue\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    ('quote form):            return form\n    ('quasiquote form):       ast = quasiquote(form); continue\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                env = new Env(f.env, f.parm, args)\n                                ast = f.impl; continue\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\nrepl_env.set('eval, (ast) -> EVAL(ast, repl-env))\nrepl_env.set('*ARGV*, cmdline_args[1..])\n\n;; core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\n\nif cmdline_args: rep(\"(load-file \\\"\" + args[0] + \"\\\")\"); exit 0\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n      'read-string: read_str,\n      'slurp     read-file,\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n\n      'list:     list,\n      'list?:    list?,\n\n      'cons:     (a) -> concat([a[0]], a[1]),\n      'concat:   (a) -> reduce(concat, [], a),\n      'vec:      (l) -> l converted to vector,\n      'empty?:   empty?,\n      'count:    count,\n\n      'atom:     (a) -> new Atom(a[0]),\n      'atom?:    (a) -> type(a[0]) == \"atom\",\n      'deref:    (a) -> a[0].val,\n      'reset!:   (a) -> a[0].val = a[1],\n      'swap!:    swap!}\n"
  },
  {
    "path": "process/step8_macros.txt",
    "content": "--- step8_macros --------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nquasiquote(ast): return ... // quasiquote\n\nEVAL(ast, env):\n loop:\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              ast = form; continue\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              ast = last; continue\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then ast = yes; continue\n                                else ast = no;  continue\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    ('quote form):            return form\n    ('quasiquote form):       ast = quasiquote(form); continue\n    ('defmacro! 'key value):  return env.set(key, as_macro(EVAL(value, env)))\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              if macro?(f) then:\n                                ast = f(arg1, ..); continue\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                env = new Env(f.env, f.parm, args)\n                                ast = f.impl; continue\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\nrepl_env.set('eval, (ast) -> EVAL(ast, repl-env))\nrepl_env.set('*ARGV*, cmdline_args[1..])\n\n;; core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif cmdline_args: rep(\"(load-file \\\"\" + args[0] + \"\\\")\"); exit 0\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n      'read-string: read_str,\n      'slurp     read-file,\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n\n      'list:     list,\n      'list?:    list?,\n\n      'cons:     (a) -> concat([a[0]], a[1]),\n      'concat:   (a) -> reduce(concat, [], a),\n      'vec:      (l) -> l converted to vector,\n      'nth:      (a) -> a[0][a[1]] OR raise \"nth: index out of range\",\n      'first:    (a) -> a[0][0] OR nil,\n      'rest:     (a) -> a[0][1..] OR list(),\n      'empty?:   empty?,\n      'count:    count,\n\n      'atom:     (a) -> new Atom(a[0]),\n      'atom?:    (a) -> type(a[0]) == \"atom\",\n      'deref:    (a) -> a[0].val,\n      'reset!:   (a) -> a[0].val = a[1],\n      'swap!:    swap!}\n"
  },
  {
    "path": "process/step9_try.txt",
    "content": "--- step9_try -----------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nquasiquote(ast): return ... // quasiquote\n\nEVAL(ast, env):\n loop:\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              ast = form; continue\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              ast = last; continue\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then ast = yes; continue\n                                else ast = no;  continue\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    ('quote form):            return form\n    ('quasiquote form):       ast = quasiquote(form); continue\n    ('defmacro! 'key value):  return env.set(key, as_macro(EVAL(value, env)))\n    ('try* f ('catch* 'k h)): try returning EVAL(f, env)\n                              if native or malval exception then:\n                                env = new Env(env)\n                                env.set(k, exception)\n                                ast = h; continue\n    ('try* form):             ast = form; continue\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              if macro?(f) then:\n                                ast = f(arg1, ..); continue\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                env = new Env(f.env, f.parm, args)\n                                ast = f.impl; continue\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\nrepl_env.set('eval, (ast) -> EVAL(ast, repl-env))\nrepl_env.set('*ARGV*, cmdline_args[1..])\n\n;; core.mal: defined using the language itself\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif cmdline_args: rep(\"(load-file \\\"\" + args[0] + \"\\\")\"); exit 0\n\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n      'throw:    throw,\n\n      'nil?:     nil?,\n      'true?:    true?,\n      'false?:   false?,\n      'symbol:   symbol,\n      'symbol?:  symbol?,\n      'keyword:  keyword,\n      'keyword?: keyword?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n      'read-string: read_str,\n      'slurp     read-file,\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n\n      'list:     list,\n      'list?:    list?,\n      'vector:   vector,\n      'vector?:  vector?,\n      'hash-map: hash_map,\n      'map?:     hash_map?,\n      'assoc:    assoc,\n      'dissoc:   dissoc,\n      'get:      get,\n      'contains?: contains?,\n      'keys:     keys,\n      'vals:     vals,\n\n      'sequential? sequential?,\n      'cons:     (a) -> concat([a[0]], a[1]),\n      'concat:   (a) -> reduce(concat, [], a),\n      'vec:      (l) -> l converted to vector,\n      'nth:      (a) -> a[0][a[1]] OR raise \"nth: index out of range\",\n      'first:    (a) -> a[0][0] OR nil,\n      'rest:     (a) -> a[0][1..] OR list(),\n      'empty?:   empty?,\n      'count:    count,\n      'apply:    apply,\n      'map:      map,\n\n      'atom:     (a) -> new Atom(a[0]),\n      'atom?:    (a) -> type(a[0]) == \"atom\",\n      'deref:    (a) -> a[0].val,\n      'reset!:   (a) -> a[0].val = a[1],\n      'swap!:    swap!}\n"
  },
  {
    "path": "process/stepA_mal.txt",
    "content": "--- stepA_mal -------------------------------\nimport types, reader, printer, env, core\n\nREAD(str): return reader.read_str(str)\n\nquasiquote(ast): return ... // quasiquote\n\nEVAL(ast, env):\n loop:\n  if env.get('DEBUG-EVAL) exists and not in nil, false then prn('EVAL ast)\n  match ast:\n    'key:                     return env.get(key) or raise \"'{key}' not found\"\n    [form1 ..]:               return [EVAL(form1, env) ..]\n    {key1 value1 ..}:         return {key1 EVAL(value1, env) ..}\n    ('def! 'key value):       return env.set(key, EVAL(value, env))\n    ('let* (k1 v1 ..) form):  env = new Env(env)\n                              env.set(k1, EVAL(v1, env))\n                              ..\n                              ast = form; continue\n    ('let* [k1 v1 ..] form):  // idem\n    ('do form1 .. last):      EVAL(form1, env)\n                              ..\n                              ast = last; continue\n    ('if cond yes no):        if EVAL(cond, env) in nil, false\n                                then ast = yes; continue\n                                else ast = no;  continue\n    ('if cond yes):           // idem with return nil in the else branch\n    ('fn* ('key1 ..) impl):   return new MalFn(env, impl, parm=[key1 ..])\n    ('fn* ['key1 ..] impl):   // idem\n    ('quote form):            return form\n    ('quasiquote form):       ast = quasiquote(form); continue\n    ('defmacro! 'key value):  return env.set(key, as_macro(EVAL(value, env)))\n    ('try* f ('catch* 'k h)): try returning EVAL(f, env)\n                              if native or malval exception then:\n                                env = new Env(env)\n                                env.set(k, exception)\n                                ast = h; continue\n    ('try* form):             ast = form; continue\n    (callable arg1 ..):       f = EVAL(callable, env)\n                              if macro?(f) then:\n                                ast = f(arg1, ..); continue\n                              args = [EVAL(arg1, env) ..]\n                              if malfn?(f) then:\n                                env = new Env(f.env, f.parm, args)\n                                ast = f.impl; continue\n                              return f(args)\n    otherwise:                return ast\n\nPRINT(exp): return printer.pr_str(exp)\n\nrepl_env = new Env()\nrep(str): return PRINT(EVAL(READ(str),repl_env))\n\n;; core.EXT: defined using the host language.\ncore.ns.map((k,v) -> (repl_env.set(k, v)))\nrepl_env.set('eval, (ast) -> EVAL(ast, repl-env))\nrepl_env.set('*ARGV*, cmdline_args[1..])\n\n;; core.mal: defined using the language itself\nrep(\"(def! *host-language* \\\"...\\\")\")\nrep(\"(def! not (fn* (a) (if a false true)))\")\nrep(\"(def! load-file (fn* (f) (eval (read-string (str \\\"(do \\\" (slurp f) \\\"\\nnil)\\\")))))\")\nrep(\"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \\\"odd number of forms to cond\\\")) (cons 'cond (rest (rest xs)))))))\");\n\nif cmdline_args: rep(\"(load-file \\\"\" + args[0] + \"\\\")\"); exit 0\n\nrep(\"(println (str \\\"Mal [\\\" *host-language* \\\"]\\\"))\")\nmain loop:\n  try:      println(rep(readline(\"user> \")))\n  catch e:  println(\"Error: \", e)\n\n--- env module ----------------------------------\nclass Env (outer=null,binds=[],exprs=[])\n    data = hash_map()\n    foreach b, i in binds:\n      if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break\n      else: data[binds[i]] = exprs[i]\n  set(k,v): return data.set(k,v)\n  get(k): return data.has(k) ? data.get(k) : (outer ? outer.get(k) : null)\n\n--- core module ---------------------------------\nns = {'=:        equal?,\n      'throw:    throw,\n\n      'nil?:     nil?,\n      'true?:    true?,\n      'false?:   false?,\n      'string?:  string?,\n      'symbol:   symbol,\n      'symbol?:  symbol?,\n      'keyword:  keyword,\n      'keyword?: keyword?,\n      'number?:  number?,\n      'fn?:      fn?,\n      'macro?:   macro?,\n\n      'pr-str:   (a) -> a.map(|s| pr_str(e,true)).join(\" \")),\n      'str:      (a) -> a.map(|s| pr_str(e,false)).join(\"\")),\n      'prn:      (a) -> println(a.map(|s| pr_str(e,true)).join(\" \")),\n      'println:  (a) -> println(a.map(|s| pr_str(e,false)).join(\" \")),\n      'read-string: read_str,\n      'readline: readline,\n      'slurp     read-file,\n\n      '<:        lt,\n      '<=:       lte,\n      '>:        gt,\n      '>=:       gte,\n      '+:        add,\n      '-:        sub,\n      '*:        mult,\n      '/:        div,\n      'time-ms   cur-epoch-millis,\n\n      'list:     list,\n      'list?:    list?,\n      'vector:   vector,\n      'vector?:  vector?,\n      'hash-map: hash_map,\n      'map?:     hash_map?,\n      'assoc:    assoc,\n      'dissoc:   dissoc,\n      'get:      get,\n      'contains?: contains?,\n      'keys:     keys,\n      'vals:     vals,\n\n      'sequential? sequential?,\n      'cons:     (a) -> concat([a[0]], a[1]),\n      'concat:   (a) -> reduce(concat, [], a),\n      'vec:      (l) -> l converted to vector,\n      'nth:      (a) -> a[0][a[1]] OR raise \"nth: index out of range\",\n      'first:    (a) -> a[0][0] OR nil,\n      'rest:     (a) -> a[0][1..] OR list(),\n      'empty?:   empty?,\n      'count:    count,\n      'apply:    apply,\n      'map:      map,\n\n      'conj:     conj,\n      'seq:      seq,\n\n      'meta:     (a) -> a[0].meta,\n      'with-meta: (a) -> a[0].with_meta(a[1]),\n      'atom:     (a) -> new Atom(a[0]),\n      'atom?:    (a) -> type(a[0]) == \"atom\",\n      'deref:    (a) -> a[0].val,\n      'reset!:   (a) -> a[0].val = a[1],\n      'swap!:    swap!}\n"
  },
  {
    "path": "process/steps.drawio",
    "content": "<mxfile host=\"Electron\" modified=\"2024-08-13T14:27:24.937Z\" agent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36\" etag=\"r__5xrBhZDiwibQytHO1\" version=\"24.2.5\" type=\"device\">\n  <diagram id=\"X9f1pY7bRxJgmkKPEZmz\" name=\"Page-1\">\n    <mxGraphModel dx=\"1242\" dy=\"878\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"1100\" pageHeight=\"850\" math=\"0\" shadow=\"0\">\n      <root>\n        <mxCell id=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-71\" value=\"Background Box\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-73\" value=\"&lt;div style=&#39;width: 385.8px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;ENV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=14.1;spacingRight=6;spacingTop=1.0;spacingBottom=8;whiteSpace=wrap;gliffyId=56;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"321.5\" y=\"87.5\" width=\"405\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-74\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"51.5\" y=\"252.5\" width=\"110\" height=\"360\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-75\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=2;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"226.5\" y=\"252.5\" width=\"500\" height=\"360\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-76\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=9;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-85\" target=\"_SNN-nFgLk4Tr6xxPPOo-82\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"141.5\" y=\"463.03289794921875\" />\n              <mxPoint x=\"201.5\" y=\"463.03289794921875\" />\n              <mxPoint x=\"201.5\" y=\"417.5\" />\n              <mxPoint x=\"261.5\" y=\"417.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-77\" value=\"&lt;div style=&#39;width: 35.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;AST&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=98;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-76\" vertex=\"1\">\n          <mxGeometry x=\"-0.02272986685128997\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-78\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=13;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"781.5\" y=\"252.5\" width=\"110\" height=\"360\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-79\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=16.0 16.0;gliffyId=15;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-78\" target=\"_SNN-nFgLk4Tr6xxPPOo-74\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"836.5\" y=\"612.5\" />\n              <mxPoint x=\"836.5\" y=\"708.5\" />\n              <mxPoint x=\"106.5\" y=\"708.5\" />\n              <mxPoint x=\"106.5\" y=\"612.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-80\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=17;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-83\" target=\"_SNN-nFgLk4Tr6xxPPOo-93\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"701.5\" y=\"417.5\" />\n              <mxPoint x=\"751.5\" y=\"417.5\" />\n              <mxPoint x=\"751.5\" y=\"455\" />\n              <mxPoint x=\"801.5\" y=\"455\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-81\" value=\"&lt;div style=&#39;width: 35.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;AST&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=99;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-80\" vertex=\"1\">\n          <mxGeometry x=\"-0.07526378938759026\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-82\" value=\"&lt;div style=&#39;width: 113.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;* symbol&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;* list&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;* vector&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;* hash-map&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=18;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"261.5\" y=\"302.5\" width=\"120\" height=\"230\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-83\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=20;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"302.5\" width=\"280\" height=\"230\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-84\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;readline&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=22;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"71.5\" y=\"302.5\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-85\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;read_str&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=7.4;spacingRight=6;spacingTop=1.0;spacingBottom=8;whiteSpace=wrap;gliffyId=24;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"71.5\" y=\"392.5\" width=\"70\" height=\"177.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-86\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=26;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-84\" target=\"_SNN-nFgLk4Tr6xxPPOo-85\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"106.5\" y=\"342.5\" />\n              <mxPoint x=\"106.5\" y=\"359.1666564941406\" />\n              <mxPoint x=\"106.5\" y=\"375.8333435058594\" />\n              <mxPoint x=\"106.5\" y=\"392.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-87\" value=\"&lt;div style=&#39;width: 44.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;string&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=184;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-86\" vertex=\"1\">\n          <mxGeometry x=\"-0.11999999999999988\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-88\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=27;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" target=\"_SNN-nFgLk4Tr6xxPPOo-84\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"17\" y=\"321.5\" as=\"sourcePoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"40.17021179199219\" y=\"321.5\" />\n              <mxPoint x=\"55.840423583984375\" y=\"321.5\" />\n              <mxPoint x=\"71.51063537597656\" y=\"321.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-89\" value=\"&lt;div style=&#39;width: 22.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;in&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=4;spacingRight=0;gliffyId=100;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-88\" vertex=\"1\">\n          <mxGeometry x=\"-0.5320208072073311\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-0.5\" y=\"0.5\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-90\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=29;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-92\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"940\" y=\"322.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"871.5\" y=\"322.5\" />\n              <mxPoint x=\"890.8333129882812\" y=\"322.5\" />\n              <mxPoint x=\"910.1666870117188\" y=\"322.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-91\" value=\"&lt;div style=&#39;width: 30.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;out&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=109;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-90\" vertex=\"1\">\n          <mxGeometry x=\"0.1379310344827589\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-92\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;printline&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=30;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"801.5\" y=\"302.5\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-93\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;pr_str&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=32;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"801.5\" y=\"435\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-94\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=34;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-93\" target=\"_SNN-nFgLk4Tr6xxPPOo-92\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"836.5\" y=\"435\" />\n              <mxPoint x=\"836.5\" y=\"404.1666564941406\" />\n              <mxPoint x=\"836.5\" y=\"373.3333435058594\" />\n              <mxPoint x=\"836.5\" y=\"342.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-95\" value=\"&lt;div style=&#39;width: 44.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;string&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=185;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-94\" vertex=\"1\">\n          <mxGeometry x=\"-0.1567567567567567\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-96\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=36;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-83\" target=\"_SNN-nFgLk4Tr6xxPPOo-75\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"496.5\" y=\"302.5\" />\n              <mxPoint x=\"496.5\" y=\"212.5\" />\n              <mxPoint x=\"372.94659423828125\" y=\"212.5\" />\n              <mxPoint x=\"372.94659423828125\" y=\"252.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-97\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=42;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-82\" target=\"_SNN-nFgLk4Tr6xxPPOo-83\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"321.5\" y=\"532.5\" />\n              <mxPoint x=\"321.5\" y=\"572.5\" />\n              <mxPoint x=\"488.5\" y=\"572.5\" />\n              <mxPoint x=\"488.5\" y=\"532.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-98\" value=\"&lt;div style=&#39;width: 89.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;macroexpand&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=187;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-97\" vertex=\"1\">\n          <mxGeometry x=\"-0.03117165447637804\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-99\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=44;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"395\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-100\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=46;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-99\" target=\"_SNN-nFgLk4Tr6xxPPOo-106\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"331.5\" y=\"122.5\" />\n              <mxPoint x=\"331.5\" y=\"182.50369262695312\" />\n              <mxPoint x=\"331.5\" y=\"242.50741577148438\" />\n              <mxPoint x=\"331.5\" y=\"302.5111083984375\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-101\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;symbol&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;lookup&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=112;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-100\" vertex=\"1\">\n          <mxGeometry x=\"-0.21130582637728546\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-102\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=53;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"522.5\" y=\"340.5049743652344\" as=\"sourcePoint\" />\n            <mxPoint x=\"522.5\" y=\"155.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"522.5\" y=\"248.0024871826172\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-103\" style=\"shape=filledEdge;strokeWidth=1;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=1.0 1.0;html=1;nl2Br=0;gliffyId=58;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-73\" target=\"_SNN-nFgLk4Tr6xxPPOo-99\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"714.8092651367188\" y=\"87.5\" />\n              <mxPoint x=\"714.8092651367188\" y=\"24\" />\n              <mxPoint x=\"669.9000244140625\" y=\"24\" />\n              <mxPoint x=\"669.9000244140625\" y=\"52.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-104\" value=\"&lt;div style=&#39;width: 36.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:10px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;outer&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=168;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-103\" vertex=\"1\">\n          <mxGeometry x=\"0.2546657463953934\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-105\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;apply&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=87;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"302.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-106\" value=\"&lt;div style=&#39;width: 81.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;eval_ast&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=9.0;spacingRight=6;whiteSpace=wrap;gliffyId=90;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"261.5\" y=\"302.5\" width=\"90\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-107\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;READ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=92;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"51.5\" y=\"252.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-108\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;EVAL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=94;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"226.5\" y=\"252.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-109\" value=\"&lt;div style=&#39;width: 62.333336px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;PRINT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.333333;spacingRight=6;whiteSpace=wrap;gliffyId=96;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"781.5\" y=\"252.5\" width=\"70\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-110\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;Env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=110;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-111\" value=\"&lt;div style=&#39;width: 102.6px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;try*/catch*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.2;spacingRight=6;whiteSpace=wrap;gliffyId=122;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"481.5\" y=\"402.5\" width=\"110\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-112\" value=\"&lt;div style=&#39;width: 35.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.8;spacingRight=6;whiteSpace=wrap;gliffyId=124;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"481.5\" y=\"432.5\" width=\"40\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-113\" value=\"&lt;div style=&#39;width: 35.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;if&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.8;spacingRight=6;whiteSpace=wrap;gliffyId=126;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"481.5\" y=\"462.5\" width=\"40\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-114\" value=\"&lt;div style=&#39;width: 30.599998px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;fn*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.7;spacingRight=6;whiteSpace=wrap;gliffyId=128;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"656.5\" y=\"432.5\" width=\"35\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-115\" value=\"&lt;div style=&#39;width: 45.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;let*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.0;spacingRight=6;whiteSpace=wrap;gliffyId=130;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"481.5\" y=\"342.5\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-116\" value=\"&lt;div style=&#39;width: 40.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;def!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.9;spacingRight=6;whiteSpace=wrap;gliffyId=134;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"646.5\" y=\"342.5\" width=\"45\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-117\" value=\"&lt;div style=&#39;width: 83.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;defmacro!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8;spacingRight=6;whiteSpace=wrap;gliffyId=136;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"601.5\" y=\"372.5\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-118\" value=\"&lt;div style=&#39;width: 57.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;quote&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.25;spacingRight=6;whiteSpace=wrap;gliffyId=138;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"629\" y=\"462.5\" width=\"62.5\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-119\" value=\"&lt;div style=&#39;width: 83.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;quasiquote&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8;spacingRight=6;whiteSpace=wrap;gliffyId=140;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"481.5\" y=\"492.5\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-120\" value=\"&lt;div style=&#39;width: 93.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;macroexpand&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=142;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"591.5\" y=\"492.5\" width=\"100\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-121\" value=\"&lt;div style=&#39;width: 73.8px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;&amp;#34;apply&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"481.5\" y=\"372.5\" width=\"80\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-122\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-121\" target=\"_SNN-nFgLk4Tr6xxPPOo-75\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"481.5\" y=\"382.5\" />\n              <mxPoint x=\"402.82330322265625\" y=\"382.5\" />\n              <mxPoint x=\"402.82330322265625\" y=\"212.50001525878906\" />\n              <mxPoint x=\"372.94659423828125\" y=\"212.50001525878906\" />\n              <mxPoint x=\"372.94659423828125\" y=\"252.50001525878906\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-123\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=159;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-112\" target=\"_SNN-nFgLk4Tr6xxPPOo-75\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"481.5\" y=\"442.5\" />\n              <mxPoint x=\"403.0233154296875\" y=\"442.5\" />\n              <mxPoint x=\"403.0233154296875\" y=\"212.49998474121094\" />\n              <mxPoint x=\"372.9466247558594\" y=\"212.49998474121094\" />\n              <mxPoint x=\"372.9466247558594\" y=\"252.49998474121094\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-124\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=160;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-113\" target=\"_SNN-nFgLk4Tr6xxPPOo-75\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"481.5\" y=\"472.5\" />\n              <mxPoint x=\"403.22332763671875\" y=\"472.5\" />\n              <mxPoint x=\"403.22332763671875\" y=\"212.5\" />\n              <mxPoint x=\"372.9466247558594\" y=\"212.5\" />\n              <mxPoint x=\"372.9466247558594\" y=\"252.50001525878906\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-125\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=161;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-119\" target=\"_SNN-nFgLk4Tr6xxPPOo-75\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"481.5\" y=\"502.4999694824219\" />\n              <mxPoint x=\"403.2232971191406\" y=\"502.4999694824219\" />\n              <mxPoint x=\"403.2232971191406\" y=\"212.49996948242188\" />\n              <mxPoint x=\"372.94659423828125\" y=\"212.49996948242188\" />\n              <mxPoint x=\"372.94659423828125\" y=\"252.49996948242188\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-126\" value=\"&lt;div style=&#39;width: 62.333336px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;LOOP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.333333;spacingRight=6;whiteSpace=wrap;gliffyId=176;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"696.5\" width=\"70\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-127\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=154;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"669.5\" y=\"342.5105285644531\" as=\"sourcePoint\" />\n            <mxPoint x=\"669.5\" y=\"122.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"669.5\" y=\"232.50526428222656\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-128\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=51;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-117\" target=\"_SNN-nFgLk4Tr6xxPPOo-99\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"631.5\" y=\"372.5264892578125\" />\n              <mxPoint x=\"631.5\" y=\"289.184326171875\" />\n              <mxPoint x=\"631.5\" y=\"205.8421630859375\" />\n              <mxPoint x=\"631.5\" y=\"122.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-129\" value=\"&lt;div style=&#39;width: 74.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;right&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;update env&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=189;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-128\" vertex=\"1\">\n          <mxGeometry x=\"0.3359693404145152\" relative=\"1\" as=\"geometry\">\n            <mxPoint y=\"20\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-130\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=152;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"576.1666259765625\" y=\"402.50091552734375\" as=\"sourcePoint\" />\n            <mxPoint x=\"576.1666259765625\" y=\"157.50001525878906\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"576.1666259765625\" y=\"280.0004577636719\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-131\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=150;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-121\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"549.5\" y=\"156.83334350585938\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"549.5\" y=\"372.5\" />\n              <mxPoint x=\"549.5\" y=\"264.66668701171875\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-132\" value=\"&lt;div style=&#39;width: 70.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;create env&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=151;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-131\" vertex=\"1\">\n          <mxGeometry x=\"0.5857805255023187\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-133\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=157;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-115\" target=\"_SNN-nFgLk4Tr6xxPPOo-75\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"481.5\" y=\"352.5\" />\n              <mxPoint x=\"403.2232971191406\" y=\"352.5\" />\n              <mxPoint x=\"403.2232971191406\" y=\"212.5\" />\n              <mxPoint x=\"372.94659423828125\" y=\"212.5\" />\n              <mxPoint x=\"372.94659423828125\" y=\"252.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-134\" value=\"&lt;div style=&#39;width: 36.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;TCO&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=180;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-133\" vertex=\"1\">\n          <mxGeometry x=\"0.08205532939406224\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-135\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=197;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"31.5\" y=\"27.5\" width=\"230\" height=\"200\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-136\" value=\"&lt;div style=&#39;width: 48.333332px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;Core&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8333335;spacingRight=6;whiteSpace=wrap;gliffyId=195;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"31.5\" y=\"27.5\" width=\"55\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-137\" value=\"&lt;div style=&quot;width: 53px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;eval&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;*ARGV*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=201;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"393.5\" y=\"63.5\" width=\"56\" height=\"33\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-138\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;=&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;throw&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;nil?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;true?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;false? &lt;span style=&quot;color: #cc0000 ; line-height: normal&quot;&gt;string?&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;symbol symbol? keyword keyword?&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;pr-str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;prn&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;println&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: 0&quot;&gt;&lt;span style=&quot;color: #cc0000 ; line-height: normal&quot;&gt;readline&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;read-string&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;slurp&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&amp;lt; &amp;lt;= &amp;gt; &amp;gt;=&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;+ - * /&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; color: #cc0000 ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;time-ms&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;list list? vector vector?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;hash-map map? assoc dissoc&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;get contains? keys vals&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;sequential? cons concat nth&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;first rest empty? count apply&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;map &lt;span style=&quot;color: #cc0000 ; line-height: normal&quot;&gt;conj seq&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: 0&quot;&gt;&lt;span style=&quot;color: #cc0000 ; line-height: normal&quot;&gt;with-meta meta&lt;/span&gt; atom atom?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;deref reset! swap!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"31.5\" y=\"63.5\" width=\"225\" height=\"154\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-139\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=208;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" source=\"_SNN-nFgLk4Tr6xxPPOo-135\" target=\"_SNN-nFgLk4Tr6xxPPOo-99\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"261.5\" y=\"86.07864379882812\" />\n              <mxPoint x=\"276.5074768066406\" y=\"86.07864379882812\" />\n              <mxPoint x=\"291.51495361328125\" y=\"86.07864379882812\" />\n              <mxPoint x=\"306.5224304199219\" y=\"86.07864379882812\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-140\" value=\"&lt;div style=&quot;width: 97px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; color: rgb(204 , 0 , 0) ; line-height: normal&quot;&gt;*host-language*&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: normal&quot;&gt;not&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: normal&quot;&gt;load-file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=211;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"451.5\" y=\"63.5\" width=\"100\" height=\"33\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-141\" value=\"&lt;div style=&quot;width: 74.5px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: normal&quot;&gt;cond&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; color: #cc0000 ; line-height: normal&quot;&gt;gensym&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; color: #cc0000 ; line-height: normal&quot;&gt;or&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=212;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry x=\"561.5\" y=\"63.5\" width=\"77.5\" height=\"33\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-70\" value=\"\" style=\"rounded=0;whiteSpace=wrap;html=1;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-71\" vertex=\"1\">\n          <mxGeometry width=\"960\" height=\"750\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-142\" value=\"step0\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-6\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=13;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"781.5\" y=\"252.5\" width=\"110\" height=\"367.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-3\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=2;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"226.5\" y=\"252.5\" width=\"500\" height=\"367.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-2\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"51.5\" y=\"252.5\" width=\"110\" height=\"367.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-16\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=27;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" target=\"_SNN-nFgLk4Tr6xxPPOo-12\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"14\" y=\"321.5\" as=\"sourcePoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"40.17021179199219\" y=\"321.5\" />\n              <mxPoint x=\"55.840423583984375\" y=\"321.5\" />\n              <mxPoint x=\"71.51063537597656\" y=\"321.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-17\" value=\"&lt;div style=&#39;width: 22.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;in&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6;spacingRight=0;gliffyId=100;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-16\" vertex=\"1\">\n          <mxGeometry x=\"-0.5320208072073311\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-0.5\" y=\"0.5\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-35\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;READ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=92;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"51.5\" y=\"252.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-12\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;readline&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=22;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"71.5\" y=\"302.5\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-36\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;EVAL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=94;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"226.5\" y=\"252.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-37\" value=\"&lt;div style=&#39;width: 62.333336px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;PRINT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.333333;spacingRight=6;whiteSpace=wrap;gliffyId=96;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"781.5\" y=\"252.5\" width=\"70\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-20\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;printline&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=30;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"801.5\" y=\"302.5\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-18\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=29;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" source=\"_SNN-nFgLk4Tr6xxPPOo-20\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"944\" y=\"322.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"871.5\" y=\"322.5\" />\n              <mxPoint x=\"890.8333129882812\" y=\"322.5\" />\n              <mxPoint x=\"910.1666870117188\" y=\"322.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-19\" value=\"&lt;div style=&#39;width: 30.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;out&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=109;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-18\" vertex=\"1\">\n          <mxGeometry x=\"0.1379310344827589\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-7\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=16.0 16.0;gliffyId=15;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" source=\"_SNN-nFgLk4Tr6xxPPOo-54\" target=\"_SNN-nFgLk4Tr6xxPPOo-2\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"107\" y=\"684\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-40\" value=\"\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=16.0 16.0;gliffyId=15;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;\" edge=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" source=\"_SNN-nFgLk4Tr6xxPPOo-6\" target=\"_SNN-nFgLk4Tr6xxPPOo-54\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"837\" y=\"684\" />\n            </Array>\n            <mxPoint x=\"837\" y=\"640\" as=\"sourcePoint\" />\n            <mxPoint x=\"107\" y=\"640\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-54\" value=\"&lt;div style=&#39;width: 62.333336px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;LOOP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#c9daf8;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.333333;spacingRight=6;whiteSpace=wrap;gliffyId=176;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-142\" vertex=\"1\">\n          <mxGeometry x=\"440\" y=\"670\" width=\"70\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-143\" value=\"step0 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-145\" value=\"\" style=\"endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.001;entryY=0.338;entryDx=0;entryDy=0;entryPerimeter=0;edgeStyle=orthogonalEdgeStyle;strokeWidth=2;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-143\" source=\"_SNN-nFgLk4Tr6xxPPOo-12\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\" edge=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"10\" y=\"820\" as=\"sourcePoint\" />\n            <mxPoint x=\"60\" y=\"770\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"190\" y=\"323\" />\n              <mxPoint x=\"190\" y=\"377\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-147\" value=\"&lt;b&gt;string&lt;/b&gt;\" style=\"text;html=1;resizable=0;points=[];align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-145\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"0.2206\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-1\" y=\"-8\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-148\" value=\"\" style=\"endArrow=classic;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;edgeStyle=orthogonalEdgeStyle;strokeWidth=2;exitX=1.001;exitY=0.333;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-143\" source=\"_SNN-nFgLk4Tr6xxPPOo-3\" target=\"_SNN-nFgLk4Tr6xxPPOo-20\" edge=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"152\" y=\"333\" as=\"sourcePoint\" />\n            <mxPoint x=\"237\" y=\"384\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"750\" y=\"375\" />\n              <mxPoint x=\"750\" y=\"323\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-149\" value=\"&lt;b&gt;string&lt;/b&gt;\" style=\"text;html=1;resizable=0;points=[];align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-148\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"0.2206\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-2\" y=\"29\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-102\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;pass through&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-143\">\n          <mxGeometry x=\"430\" y=\"405\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-152\" value=\"step1\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-13\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;read_str&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=7.4;spacingRight=6;spacingTop=1.0;spacingBottom=8;whiteSpace=wrap;gliffyId=24;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-152\" vertex=\"1\">\n          <mxGeometry x=\"71.5\" y=\"392.5\" width=\"70\" height=\"167.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-4\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=9;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-152\" source=\"_SNN-nFgLk4Tr6xxPPOo-13\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"141.5\" y=\"463.03289794921875\" />\n              <mxPoint x=\"201.5\" y=\"463.03289794921875\" />\n              <mxPoint x=\"201.5\" y=\"417.5\" />\n              <mxPoint x=\"261.5\" y=\"417.5\" />\n            </Array>\n            <mxPoint x=\"262\" y=\"418\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-5\" value=\"&lt;div style=&#39;width: 35.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;AST&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=98;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-4\" vertex=\"1\">\n          <mxGeometry x=\"-0.02272986685128997\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-8\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=17;edgeStyle=orthogonalEdgeStyle;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-152\" target=\"_SNN-nFgLk4Tr6xxPPOo-21\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"752\" y=\"418\" />\n              <mxPoint x=\"752\" y=\"470\" />\n            </Array>\n            <mxPoint x=\"701\" y=\"418\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-9\" value=\"&lt;div style=&#39;width: 35.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;AST&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=99;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-8\" vertex=\"1\">\n          <mxGeometry x=\"-0.07526378938759026\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-21\" value=\"&lt;div style=&#39;width: 64.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;pr_str&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=32;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-152\" vertex=\"1\">\n          <mxGeometry x=\"801.5\" y=\"450\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-22\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=34;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-152\" source=\"_SNN-nFgLk4Tr6xxPPOo-21\" target=\"_SNN-nFgLk4Tr6xxPPOo-20\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"836.5\" y=\"435\" />\n              <mxPoint x=\"836.5\" y=\"404.1666564941406\" />\n              <mxPoint x=\"836.5\" y=\"373.3333435058594\" />\n              <mxPoint x=\"836.5\" y=\"342.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-23\" value=\"&lt;div style=&quot;width: 44.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;string&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=185;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-22\" vertex=\"1\">\n          <mxGeometry x=\"-0.1567567567567567\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-14\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=26;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-152\" source=\"_SNN-nFgLk4Tr6xxPPOo-12\" target=\"_SNN-nFgLk4Tr6xxPPOo-13\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"106.5\" y=\"342.5\" />\n              <mxPoint x=\"106.5\" y=\"359.1666564941406\" />\n              <mxPoint x=\"106.5\" y=\"375.8333435058594\" />\n              <mxPoint x=\"106.5\" y=\"392.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-15\" value=\"&lt;div style=&quot;width: 44.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;string&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=184;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-14\" vertex=\"1\">\n          <mxGeometry x=\"-0.11999999999999988\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-162\" value=\"step1 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-163\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=26;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" target=\"_SNN-nFgLk4Tr6xxPPOo-165\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"106.5\" y=\"342.5\" />\n              <mxPoint x=\"106.5\" y=\"359.1666564941406\" />\n              <mxPoint x=\"106.5\" y=\"375.8333435058594\" />\n              <mxPoint x=\"106.5\" y=\"392.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-164\" value=\"&lt;div style=&#39;width: 44.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;string&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=184;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-163\" vertex=\"1\">\n          <mxGeometry x=\"-0.11999999999999988\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-165\" value=\"&lt;div style=&quot;width: 64.2px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;read_str&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=7.4;spacingRight=6;spacingTop=1.0;spacingBottom=8;whiteSpace=wrap;gliffyId=24;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" vertex=\"1\">\n          <mxGeometry x=\"71.5\" y=\"392.5\" width=\"70\" height=\"167.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-166\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=9;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" source=\"_SNN-nFgLk4Tr6xxPPOo-165\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"141.5\" y=\"463.03289794921875\" />\n              <mxPoint x=\"201.5\" y=\"463.03289794921875\" />\n              <mxPoint x=\"201.5\" y=\"417.5\" />\n              <mxPoint x=\"261.5\" y=\"417.5\" />\n            </Array>\n            <mxPoint x=\"262\" y=\"418\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-167\" value=\"&lt;div style=&quot;width: 35.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;AST&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=98;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-166\" vertex=\"1\">\n          <mxGeometry x=\"-0.02272986685128997\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-168\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=17;edgeStyle=orthogonalEdgeStyle;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" target=\"_SNN-nFgLk4Tr6xxPPOo-170\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"752\" y=\"418\" />\n              <mxPoint x=\"752\" y=\"470\" />\n            </Array>\n            <mxPoint x=\"701\" y=\"418\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-169\" value=\"&lt;div style=&quot;width: 35.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;AST&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=99;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-168\" vertex=\"1\">\n          <mxGeometry x=\"-0.07526378938759026\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-170\" value=\"&lt;div style=&quot;width: 64.2px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;pr_str&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.4;spacingRight=6;whiteSpace=wrap;gliffyId=32;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" vertex=\"1\">\n          <mxGeometry x=\"801.5\" y=\"450\" width=\"70\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-171\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=34;edgeStyle=orthogonalEdgeStyle;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" source=\"_SNN-nFgLk4Tr6xxPPOo-170\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"836.5\" y=\"435\" />\n              <mxPoint x=\"836.5\" y=\"404.1666564941406\" />\n              <mxPoint x=\"836.5\" y=\"373.3333435058594\" />\n              <mxPoint x=\"836.5\" y=\"342.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-172\" value=\"&lt;div style=&#39;width: 44.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;string&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=185;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-171\" vertex=\"1\">\n          <mxGeometry x=\"-0.1567567567567567\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-177\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=26;edgeStyle=orthogonalEdgeStyle;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" source=\"_SNN-nFgLk4Tr6xxPPOo-12\" target=\"_SNN-nFgLk4Tr6xxPPOo-165\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"107\" y=\"370\" />\n              <mxPoint x=\"107\" y=\"370\" />\n            </Array>\n            <mxPoint x=\"117\" y=\"352.5\" as=\"sourcePoint\" />\n            <mxPoint x=\"117\" y=\"402.5\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-178\" value=\"&lt;div style=&quot;width: 44.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=184;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-177\" vertex=\"1\">\n          <mxGeometry x=\"-0.11999999999999988\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-180\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=34;edgeStyle=orthogonalEdgeStyle;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\" source=\"_SNN-nFgLk4Tr6xxPPOo-170\" target=\"_SNN-nFgLk4Tr6xxPPOo-20\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"837\" y=\"390\" />\n              <mxPoint x=\"837\" y=\"390\" />\n            </Array>\n            <mxPoint x=\"847\" y=\"445\" as=\"sourcePoint\" />\n            <mxPoint x=\"847\" y=\"352.5\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-181\" value=\"&lt;div style=&quot;width: 44.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=185;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-180\" vertex=\"1\">\n          <mxGeometry x=\"-0.1567567567567567\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-105\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;pass through&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-162\">\n          <mxGeometry x=\"430\" y=\"405\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-182\" value=\"step2\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-10\" value=\"\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=18;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" vertex=\"1\">\n          <mxGeometry x=\"260\" y=\"302.5\" width=\"120\" height=\"257.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-34\" value=\"&lt;div style=&quot;width: 81.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;evaluate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=9.0;spacingRight=6;whiteSpace=wrap;gliffyId=90;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" vertex=\"1\">\n          <mxGeometry x=\"260\" y=\"302.5\" width=\"80\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-11\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=20;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"302.5\" width=\"280\" height=\"257.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-33\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;apply&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=87;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"302.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-27\" value=\"&lt;p style=&quot;line-height: 100%&quot;&gt;&lt;br&gt;&lt;/p&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=44;html=1;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"395\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-4\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;fontColor=#DD0000;strokeWidth=2;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" source=\"8e3ejTn5SqRV94l3z-M2-81\" target=\"_SNN-nFgLk4Tr6xxPPOo-11\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"331.5\" y=\"542.5\" as=\"sourcePoint\" />\n            <mxPoint x=\"501.5\" y=\"542.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"315\" y=\"590\" />\n              <mxPoint x=\"492\" y=\"590\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-78\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;hash-map&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#000000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#000000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\">\n          <mxGeometry x=\"281.5\" y=\"460\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-79\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;symbol&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#000000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#000000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\">\n          <mxGeometry x=\"280\" y=\"360\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-80\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;vector&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#000000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#000000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\">\n          <mxGeometry x=\"280\" y=\"410\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-81\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;list&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#000000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#000000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\">\n          <mxGeometry x=\"280\" y=\"510\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-49\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;apply&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"370\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-24\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=36;edgeStyle=orthogonalEdgeStyle;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontColor=#000000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-182\" source=\"_SNN-nFgLk4Tr6xxPPOo-11\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"497\" y=\"220\" />\n              <mxPoint x=\"477\" y=\"220\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-183\" value=\"step2 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-1\" value=\"&lt;p style=&quot;line-height: 100%&quot;&gt;&lt;b&gt;+&lt;br&gt;-&lt;br&gt;*&lt;br&gt;/&lt;/b&gt;&lt;br&gt;&lt;/p&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;gliffyId=44;html=1;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"395\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-184\" value=\"&lt;div style=&quot;width: 53.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div&gt;&lt;span style=&quot;font-size: 14px ; font-weight: bold ; font-family: &amp;quot;arial&amp;quot; ; text-decoration: none ; line-height: normal&quot;&gt;REPL Env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=8.0;spacingRight=6;gliffyId=110;direction=east;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"93\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-5\" value=\"&lt;div style=&quot;width: 113.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=left;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=18;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"260\" y=\"302.5\" width=\"120\" height=\"257.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-6\" value=\"&lt;div style=&quot;width: 81.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;evaluate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=9.0;spacingRight=6;whiteSpace=wrap;gliffyId=90;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"260\" y=\"302.5\" width=\"80\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-7\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;gliffyId=20;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"302.5\" width=\"280\" height=\"257.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-8\" value=\"&lt;div style=&quot;width: 53.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;apply&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=87;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"421.5\" y=\"302.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-11\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;fontColor=#DD0000;strokeWidth=2;strokeColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" source=\"8e3ejTn5SqRV94l3z-M2-85\" target=\"ane8kT5oCuLirpTJA7_6-7\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"331.5\" y=\"542.5\" as=\"sourcePoint\" />\n            <mxPoint x=\"501.5\" y=\"542.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"315\" y=\"590\" />\n              <mxPoint x=\"492\" y=\"590\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-82\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;hash-map&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#DD0000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\">\n          <mxGeometry x=\"281.5\" y=\"460\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-83\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;symbol&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#DD0000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\">\n          <mxGeometry x=\"280\" y=\"360\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-84\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;vector&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#DD0000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\">\n          <mxGeometry x=\"280\" y=\"410\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-85\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;list&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontStyle=2;rounded=1;perimeterSpacing=0;fontColor=#DD0000;\" vertex=\"1\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\">\n          <mxGeometry x=\"280\" y=\"510\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-9\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=46;edgeStyle=orthogonalEdgeStyle;fontColor=#DD0000;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0.16;exitY=1.017;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" target=\"8e3ejTn5SqRV94l3z-M2-83\" edge=\"1\" source=\"ane8kT5oCuLirpTJA7_6-1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"380\" y=\"140\" as=\"sourcePoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"370\" y=\"160\" />\n              <mxPoint x=\"370\" y=\"370\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-10\" value=\"&lt;div style=&quot;width: 53.0px ; height: auto ; word-break: break-word&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;symbol&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;lookup&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=112;spacingTop=2;\" parent=\"ane8kT5oCuLirpTJA7_6-9\" vertex=\"1\">\n          <mxGeometry x=\"-0.21130582637728546\" relative=\"1\" as=\"geometry\">\n            <mxPoint y=\"-28\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-82\" value=\"&lt;div style=&quot;width: 73.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;apply&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.6;spacingRight=6;whiteSpace=wrap;gliffyId=146;fontColor=#DD0000;rounded=1;fontStyle=2\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"370\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-98\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=36;edgeStyle=orthogonalEdgeStyle;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontColor=#DD0000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-183\" edge=\"1\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"497\" y=\"220\" />\n              <mxPoint x=\"477\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"497\" y=\"302\" as=\"sourcePoint\" />\n            <mxPoint x=\"374\" y=\"251\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-12\" value=\"step3\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-1\" value=\"&lt;div style=&#39;width: 385.8px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;ENV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=14.1;spacingRight=6;spacingTop=1.0;spacingBottom=8;whiteSpace=wrap;gliffyId=56;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" vertex=\"1\">\n          <mxGeometry x=\"321.5\" y=\"87.5\" width=\"405\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-13\" value=\"&lt;p style=&quot;line-height: 100%&quot;&gt;&lt;br&gt;&lt;/p&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=44;html=1;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"395\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-43\" value=\"&lt;div style=&#39;width: 45.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;let*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.0;spacingRight=6;whiteSpace=wrap;gliffyId=130;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"340\" width=\"60\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-30\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=53;edgeStyle=orthogonalEdgeStyle;entryX=0.496;entryY=0.999;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" target=\"_SNN-nFgLk4Tr6xxPPOo-1\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"522.5\" y=\"340.5049743652344\" as=\"sourcePoint\" />\n            <mxPoint x=\"522.5\" y=\"155.5\" as=\"targetPoint\" />\n            <Array as=\"points\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-59\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=150;edgeStyle=orthogonalEdgeStyle;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" source=\"_SNN-nFgLk4Tr6xxPPOo-49\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"549.5\" y=\"156.83334350585938\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"549.5\" y=\"372.5\" />\n              <mxPoint x=\"549.5\" y=\"264.66668701171875\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-38\" value=\"&lt;div style=&#39;width: 53.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;Env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=110;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-44\" value=\"&lt;div style=&#39;width: 40.2px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;def!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.9;spacingRight=6;whiteSpace=wrap;gliffyId=134;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" vertex=\"1\">\n          <mxGeometry x=\"640\" y=\"340\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-55\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=154;edgeStyle=orthogonalEdgeStyle;exitX=0.585;exitY=-0.043;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" edge=\"1\" source=\"_SNN-nFgLk4Tr6xxPPOo-44\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"669.5\" y=\"342.5105285644531\" as=\"sourcePoint\" />\n            <mxPoint x=\"669.5\" y=\"122.5\" as=\"targetPoint\" />\n            <Array as=\"points\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-14\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=46;edgeStyle=orthogonalEdgeStyle;exitX=0.118;exitY=1.003;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" edge=\"1\" target=\"8e3ejTn5SqRV94l3z-M2-79\" source=\"_SNN-nFgLk4Tr6xxPPOo-1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"369\" y=\"180\" />\n              <mxPoint x=\"370\" y=\"180\" />\n              <mxPoint x=\"370\" y=\"370\" />\n            </Array>\n            <mxPoint x=\"330\" y=\"122.5\" as=\"sourcePoint\" />\n            <mxPoint x=\"330\" y=\"302.5\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-15\" value=\"&lt;div style=&quot;width: 53.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;symbol&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;lookup&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=112;fontStyle=1\" parent=\"ane8kT5oCuLirpTJA7_6-14\" vertex=\"1\">\n          <mxGeometry x=\"-0.21130582637728546\" relative=\"1\" as=\"geometry\">\n            <mxPoint y=\"-50\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-31\" style=\"shape=filledEdge;strokeWidth=1;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=1.0 1.0;html=1;nl2Br=0;gliffyId=58;edgeStyle=orthogonalEdgeStyle;fontColor=#000000;\" parent=\"ane8kT5oCuLirpTJA7_6-12\" source=\"_SNN-nFgLk4Tr6xxPPOo-1\" target=\"_SNN-nFgLk4Tr6xxPPOo-27\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"714.8092651367188\" y=\"87.5\" />\n              <mxPoint x=\"714.8092651367188\" y=\"24\" />\n              <mxPoint x=\"669.9000244140625\" y=\"24\" />\n              <mxPoint x=\"669.9000244140625\" y=\"52.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-32\" value=\"&lt;div style=&quot;width: 36.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:10px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;outer&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=168;fontColor=#000000;\" parent=\"_SNN-nFgLk4Tr6xxPPOo-31\" vertex=\"1\">\n          <mxGeometry x=\"0.2546657463953934\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-16\" value=\"step3 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-76\" value=\"&lt;div style=&quot;width: 385.8px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;ENV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=14.1;spacingRight=6;spacingTop=1.0;spacingBottom=8;whiteSpace=wrap;gliffyId=56;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" vertex=\"1\">\n          <mxGeometry x=\"321.5\" y=\"87.5\" width=\"405\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-77\" value=\"&lt;p style=&quot;line-height: 100%&quot;&gt;&lt;b&gt;+&lt;br&gt;-&lt;br&gt;*&lt;br&gt;/&lt;/b&gt;&lt;br&gt;&lt;/p&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;gliffyId=44;html=1;fontColor=#000000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"395\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-78\" value=\"&lt;div style=&quot;width: 45.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;let*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.0;spacingRight=6;whiteSpace=wrap;gliffyId=130;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"340\" width=\"60\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-79\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=53;edgeStyle=orthogonalEdgeStyle;entryX=0.496;entryY=0.999;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" target=\"ane8kT5oCuLirpTJA7_6-76\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"522.5\" y=\"340.5049743652344\" as=\"sourcePoint\" />\n            <mxPoint x=\"522.5\" y=\"155.5\" as=\"targetPoint\" />\n            <Array as=\"points\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-80\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=150;edgeStyle=orthogonalEdgeStyle;fontColor=#DD0000;exitX=0.878;exitY=-0.05;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" source=\"_SNN-nFgLk4Tr6xxPPOo-49\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"549.5\" y=\"156.83334350585938\" as=\"targetPoint\" />\n            <Array as=\"points\" />\n            <mxPoint x=\"1010\" y=\"440\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-81\" value=\"&lt;div style=&quot;width: 70.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;create env&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=151;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-80\" vertex=\"1\">\n          <mxGeometry x=\"0.5857805255023187\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-83\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=36;edgeStyle=orthogonalEdgeStyle;entryX=0.295;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"497\" y=\"213\" />\n              <mxPoint x=\"374\" y=\"213\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-84\" value=\"&lt;div style=&quot;width: 53.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;Env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=110;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" vertex=\"1\">\n          <mxGeometry x=\"306.5\" y=\"52.5\" width=\"60\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-85\" style=\"shape=filledEdge;strokeWidth=1;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=1.0 1.0;html=1;nl2Br=0;gliffyId=58;edgeStyle=orthogonalEdgeStyle;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" source=\"ane8kT5oCuLirpTJA7_6-76\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"714.8092651367188\" y=\"87.5\" />\n              <mxPoint x=\"714.8092651367188\" y=\"24\" />\n              <mxPoint x=\"669.9000244140625\" y=\"24\" />\n              <mxPoint x=\"669.9000244140625\" y=\"52.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-86\" value=\"&lt;div style=&#39;width: 36.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:10px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;outer&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=168;\" parent=\"ane8kT5oCuLirpTJA7_6-85\" vertex=\"1\">\n          <mxGeometry x=\"0.2546657463953934\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-87\" value=\"&lt;div style=&quot;width: 40.2px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;def!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.9;spacingRight=6;whiteSpace=wrap;gliffyId=134;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" vertex=\"1\">\n          <mxGeometry x=\"640\" y=\"340\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-88\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=154;edgeStyle=orthogonalEdgeStyle;fontColor=#DD0000;exitX=0.586;exitY=-0.009;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" edge=\"1\" source=\"ane8kT5oCuLirpTJA7_6-87\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"669.5\" y=\"342.5105285644531\" as=\"sourcePoint\" />\n            <mxPoint x=\"669.5\" y=\"122.5\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"669\" y=\"233\" />\n              <mxPoint x=\"670\" y=\"233\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-2\" value=\"update env\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontColor=#DD0000;fontStyle=1;fontSize=12;\" vertex=\"1\" connectable=\"0\" parent=\"ane8kT5oCuLirpTJA7_6-88\">\n          <mxGeometry x=\"0.2864\" y=\"5\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-14\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-96\" style=\"shape=filledEdge;strokeWidth=1;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;dashed=1;fixDash=1;dashPattern=1.0 1.0;html=1;nl2Br=0;gliffyId=58;edgeStyle=orthogonalEdgeStyle;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-16\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"714.9192651367188\" y=\"87.5\" />\n              <mxPoint x=\"714.9192651367188\" y=\"24\" />\n              <mxPoint x=\"670.0100244140625\" y=\"24\" />\n              <mxPoint x=\"670.0100244140625\" y=\"52.5\" />\n            </Array>\n            <mxPoint x=\"714.91\" y=\"87.5\" as=\"sourcePoint\" />\n            <mxPoint x=\"669.9957142857144\" y=\"52.5\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-97\" value=\"&lt;div style=&quot;width: 36.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:10px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;outer&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=168;fontColor=#DD0000;\" parent=\"ane8kT5oCuLirpTJA7_6-96\" vertex=\"1\">\n          <mxGeometry x=\"0.2546657463953934\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-99\" value=\"step4\" parent=\"0\" />\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-105\" value=\"\" style=\"group\" parent=\"ane8kT5oCuLirpTJA7_6-99\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"31.5\" y=\"27.5\" width=\"660\" height=\"455\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-63\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;gliffyId=197;\" parent=\"ane8kT5oCuLirpTJA7_6-105\" vertex=\"1\">\n          <mxGeometry width=\"230\" height=\"200\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-64\" value=\"&lt;div style=&#39;width: 48.333332px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;Core&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8333335;spacingRight=6;whiteSpace=wrap;gliffyId=195;\" parent=\"ane8kT5oCuLirpTJA7_6-105\" vertex=\"1\">\n          <mxGeometry width=\"55\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-67\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=208;edgeStyle=orthogonalEdgeStyle;\" parent=\"ane8kT5oCuLirpTJA7_6-105\" source=\"_SNN-nFgLk4Tr6xxPPOo-63\" target=\"_SNN-nFgLk4Tr6xxPPOo-27\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"230\" y=\"58.578643798828125\" />\n              <mxPoint x=\"245.00747680664062\" y=\"58.578643798828125\" />\n              <mxPoint x=\"260.01495361328125\" y=\"58.578643798828125\" />\n              <mxPoint x=\"275.0224304199219\" y=\"58.578643798828125\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-51\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=208;edgeStyle=orthogonalEdgeStyle;entryX=-0.002;entryY=0.474;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.996;exitY=0.293;exitDx=0;exitDy=0;exitPerimeter=0;\" edge=\"1\" parent=\"ane8kT5oCuLirpTJA7_6-105\" source=\"ane8kT5oCuLirpTJA7_6-110\" target=\"ane8kT5oCuLirpTJA7_6-13\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\" />\n            <mxPoint x=\"238.5\" y=\"52.5\" as=\"sourcePoint\" />\n            <mxPoint x=\"285\" y=\"69\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-7\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;quot;arial&amp;quot; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-family: arial; font-size: 10px; font-weight: bold;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold; font-family: arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&amp;lt; &amp;lt;= &amp;gt; &amp;gt;=&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;+ - * /&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;quot;arial&amp;quot; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;pr-str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;prn&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;println&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold;&quot;&gt;list list?&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold;&quot;&gt;empty? count&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"ane8kT5oCuLirpTJA7_6-105\">\n          <mxGeometry x=\"8.5\" y=\"32.5\" width=\"210\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-40\" value=\"&lt;div style=&#39;width: 35.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;do&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.8;spacingRight=6;whiteSpace=wrap;gliffyId=124;\" parent=\"ane8kT5oCuLirpTJA7_6-99\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"460\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-41\" value=\"&lt;div style=&#39;width: 35.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;if&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.8;spacingRight=6;whiteSpace=wrap;gliffyId=126;\" parent=\"ane8kT5oCuLirpTJA7_6-99\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"490\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-42\" value=\"&lt;div style=&#39;width: 30.599998px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;fn*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.7;spacingRight=6;whiteSpace=wrap;gliffyId=128;\" parent=\"ane8kT5oCuLirpTJA7_6-99\" vertex=\"1\">\n          <mxGeometry x=\"640\" y=\"490\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-24\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b&gt;not&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" vertex=\"1\" parent=\"ane8kT5oCuLirpTJA7_6-99\">\n          <mxGeometry x=\"380\" y=\"60\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-100\" value=\"step4 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-103\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;pr-str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;prn&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;println&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;#34;arial&amp;#34; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&amp;lt; &amp;lt;= &amp;gt; &amp;gt;=&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;+ - * /&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;list list?&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;empty?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;with-meta&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;spacing=0;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"36\" y=\"60\" width=\"225\" height=\"168\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-104\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b&gt;&lt;font color=&quot;#dd0000&quot;&gt;not&lt;/font&gt;&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"380\" y=\"60\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-107\" value=\"&lt;div style=&quot;width: 35.4px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;do&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.8;spacingRight=6;whiteSpace=wrap;gliffyId=124;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"460\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-108\" value=\"&lt;div style=&quot;width: 35.4px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;if&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.8;spacingRight=6;whiteSpace=wrap;gliffyId=126;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"490\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-109\" value=\"&lt;div style=&quot;width: 30.599998px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;fn*&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=6.7;spacingRight=6;whiteSpace=wrap;gliffyId=128;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"640\" y=\"490\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-110\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;gliffyId=197;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"31.5\" y=\"27.5\" width=\"230\" height=\"200\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-111\" value=\"&lt;div style=&quot;width: 48.333332px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:14px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;Core&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8333335;spacingRight=6;whiteSpace=wrap;gliffyId=195;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" vertex=\"1\">\n          <mxGeometry x=\"31.5\" y=\"27.5\" width=\"55\" height=\"28\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"ane8kT5oCuLirpTJA7_6-112\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=208;edgeStyle=orthogonalEdgeStyle;\" parent=\"ane8kT5oCuLirpTJA7_6-100\" source=\"ane8kT5oCuLirpTJA7_6-110\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"261.5\" y=\"86.07864379882812\" />\n              <mxPoint x=\"276.5074768066406\" y=\"86.07864379882812\" />\n              <mxPoint x=\"291.51495361328125\" y=\"86.07864379882812\" />\n              <mxPoint x=\"306.5224304199219\" y=\"86.07864379882812\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-87\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;quot;arial&amp;quot; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-family: arial; font-size: 10px; font-weight: bold;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold; font-family: arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&amp;lt; &amp;lt;= &amp;gt; &amp;gt;=&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;+ - * /&lt;/span&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; font-family: &amp;quot;arial&amp;quot; ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;pr-str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;str&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;prn&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; line-height: 0&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;println&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold;&quot;&gt;list list?&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold;&quot;&gt;empty? count&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"ane8kT5oCuLirpTJA7_6-100\">\n          <mxGeometry x=\"40\" y=\"60\" width=\"210\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-5\" value=\"step5\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-51\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=159;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.425;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"8e3ejTn5SqRV94l3z-M2-5\" source=\"_SNN-nFgLk4Tr6xxPPOo-40\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"470\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-52\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=160;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;\" parent=\"8e3ejTn5SqRV94l3z-M2-5\" source=\"_SNN-nFgLk4Tr6xxPPOo-41\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"500\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"440\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"440\" y=\"250\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-50\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.425;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"8e3ejTn5SqRV94l3z-M2-5\" source=\"_SNN-nFgLk4Tr6xxPPOo-49\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"380\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"460\" y=\"200\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-61\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=157;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.425;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"8e3ejTn5SqRV94l3z-M2-5\" source=\"_SNN-nFgLk4Tr6xxPPOo-43\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"350\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-9\" value=\"step5 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-17\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=160;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;spacingLeft=0;fontColor=#DD0000;entryX=0.423;entryY=-0.007;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-9\" source=\"_SNN-nFgLk4Tr6xxPPOo-41\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"500\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"438\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"479\" y=\"473\" as=\"sourcePoint\" />\n            <mxPoint x=\"450\" y=\"190\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-18\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=159;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;spacingLeft=0;fontColor=#DD0000;entryX=0.425;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-9\" source=\"_SNN-nFgLk4Tr6xxPPOo-40\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"470\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"479\" y=\"443\" as=\"sourcePoint\" />\n            <mxPoint x=\"430\" y=\"200\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-19\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;spacingLeft=0;fontColor=#DD0000;entryX=0.425;entryY=-0.007;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-9\" source=\"_SNN-nFgLk4Tr6xxPPOo-49\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"380\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"479\" y=\"383\" as=\"sourcePoint\" />\n            <mxPoint x=\"460\" y=\"210\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-20\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=157;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;spacingLeft=0;fontColor=#DD0000;entryX=0.425;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-9\" source=\"_SNN-nFgLk4Tr6xxPPOo-43\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"350\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"479\" y=\"353\" as=\"sourcePoint\" />\n            <mxPoint x=\"440\" y=\"200\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-21\" value=\"&lt;div style=&quot;width: 36.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;TCO&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;labelBackgroundColor=#FFFFFF;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=0.0;spacingRight=0;gliffyId=180;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-20\">\n          <mxGeometry x=\"0.08205532939406224\" relative=\"1\" as=\"geometry\">\n            <mxPoint y=\"6\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-10\" value=\"step6\" parent=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-88\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;eval load-file *ARGV*&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-10\">\n          <mxGeometry x=\"380\" y=\"80\" width=\"130\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-89\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold;&quot;&gt;read-string slurp&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;atom atom? deref reset! swap!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-10\">\n          <mxGeometry x=\"40\" y=\"90\" width=\"215\" height=\"37.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-22\" value=\"step6 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-8\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;eval load-file *ARGV*&lt;/font&gt;&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-22\">\n          <mxGeometry x=\"380\" y=\"80\" width=\"130\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-23\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; color: rgb(221, 0, 0); font-size: 10px; font-weight: bold;&quot;&gt;read-string slurp&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px; font-weight: bold; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;atom atom? deref reset! swap!&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px ; font-weight: bold ; text-decoration: none ; line-height: 0&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-22\">\n          <mxGeometry x=\"40\" y=\"90\" width=\"215\" height=\"37.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-25\" value=\"step7\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-47\" value=\"&lt;div style=&#39;width: 83.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;quasiquote&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8;spacingRight=6;whiteSpace=wrap;gliffyId=140;\" parent=\"8e3ejTn5SqRV94l3z-M2-25\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"520\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-46\" value=\"&lt;div style=&#39;width: 57.0px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;quote&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.25;spacingRight=6;whiteSpace=wrap;gliffyId=138;\" parent=\"8e3ejTn5SqRV94l3z-M2-25\" vertex=\"1\">\n          <mxGeometry x=\"630\" y=\"520\" width=\"60\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-53\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=161;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.425;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"8e3ejTn5SqRV94l3z-M2-25\" source=\"_SNN-nFgLk4Tr6xxPPOo-47\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"530\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"430\" y=\"200\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-27\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;font face=&quot;arial&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;cons concat&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-25\">\n          <mxGeometry x=\"40\" y=\"120\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-32\" value=\"step7 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-33\" value=\"&lt;div style=&quot;width: 83.4px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;quasiquote&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8;spacingRight=6;whiteSpace=wrap;gliffyId=140;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-32\">\n          <mxGeometry x=\"470\" y=\"520\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-34\" value=\"&lt;div style=&quot;width: 57.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;quote&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.25;spacingRight=6;whiteSpace=wrap;gliffyId=138;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-32\">\n          <mxGeometry x=\"630\" y=\"520\" width=\"60\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-74\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=161;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.427;entryY=-0.004;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-32\" source=\"8e3ejTn5SqRV94l3z-M2-33\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"530\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"440\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"480\" y=\"540\" as=\"sourcePoint\" />\n            <mxPoint x=\"440\" y=\"200\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-90\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;font face=&quot;arial&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;cons concat&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;fontColor=#DD0000;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-32\">\n          <mxGeometry x=\"40\" y=\"120\" width=\"211.5\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-28\" value=\"step8\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-45\" value=\"&lt;div style=&#39;width: 83.4px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot; line-height: normal;&quot;&gt;defmacro!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8;spacingRight=6;whiteSpace=wrap;gliffyId=136;\" parent=\"8e3ejTn5SqRV94l3z-M2-28\" vertex=\"1\">\n          <mxGeometry x=\"600\" y=\"370\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-56\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=51;edgeStyle=orthogonalEdgeStyle;\" parent=\"8e3ejTn5SqRV94l3z-M2-28\" source=\"_SNN-nFgLk4Tr6xxPPOo-45\" target=\"_SNN-nFgLk4Tr6xxPPOo-27\" edge=\"1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"631.5\" y=\"372.5264892578125\" />\n              <mxPoint x=\"631.5\" y=\"289.184326171875\" />\n              <mxPoint x=\"631.5\" y=\"205.8421630859375\" />\n              <mxPoint x=\"631.5\" y=\"122.5\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-48\" value=\"&lt;div style=&quot;width: 93.0px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size: 12px; font-family: Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;apply macro&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=142;fontStyle=2;rounded=1;\" parent=\"8e3ejTn5SqRV94l3z-M2-28\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"400\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-52\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.423;entryY=-0.001;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-28\" source=\"_SNN-nFgLk4Tr6xxPPOo-48\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"410\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"438\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"460\" y=\"410\" as=\"sourcePoint\" />\n            <mxPoint x=\"430\" y=\"200\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-92\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;cond&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-28\">\n          <mxGeometry x=\"520\" y=\"60\" width=\"60\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-36\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;font face=&quot;arial&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;nth first rest&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-28\">\n          <mxGeometry x=\"110\" y=\"120\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-37\" value=\"step8 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-42\" value=\"&lt;div style=&quot;width: 83.4px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial; line-height: 0;&quot;&gt;&lt;span style=&quot;line-height: normal;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;defmacro!&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=7.8;spacingRight=6;whiteSpace=wrap;gliffyId=136;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-37\">\n          <mxGeometry x=\"600\" y=\"370\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-43\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;html=1;nl2Br=0;gliffyId=51;edgeStyle=orthogonalEdgeStyle;entryX=0.823;entryY=1.002;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.35;exitY=-0.031;exitDx=0;exitDy=0;exitPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-37\" source=\"8e3ejTn5SqRV94l3z-M2-42\" target=\"ane8kT5oCuLirpTJA7_6-13\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\" />\n            <mxPoint x=\"630.5\" y=\"128\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-45\" value=\"&lt;font color=&quot;#dd0000&quot; face=&quot;Arial&quot;&gt;&lt;span&gt;apply macro&lt;/span&gt;&lt;/font&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.0;spacingRight=6;whiteSpace=wrap;gliffyId=142;fontStyle=2;rounded=1;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-37\">\n          <mxGeometry x=\"470\" y=\"400\" width=\"90\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-69\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.423;entryY=-0.001;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-37\" source=\"8e3ejTn5SqRV94l3z-M2-45\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"410\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"438\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"490\" y=\"420\" as=\"sourcePoint\" />\n            <mxPoint x=\"420\" y=\"190\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-91\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;font face=&quot;arial&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;nth first rest&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;fontColor=#DD0000;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-37\">\n          <mxGeometry x=\"110\" y=\"120\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-93\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;font color=&quot;#dd0000&quot;&gt;cond&lt;/font&gt;&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-37\">\n          <mxGeometry x=\"520\" y=\"60\" width=\"60\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-29\" value=\"step9\" parent=\"0\" />\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-39\" value=\"&lt;div style=&#39;width: 102.6px;height:auto;word-break: break-word;&#39;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;try*/catch*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#333333;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.2;spacingRight=6;whiteSpace=wrap;gliffyId=122;\" parent=\"8e3ejTn5SqRV94l3z-M2-29\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"430\" width=\"110\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"_SNN-nFgLk4Tr6xxPPOo-58\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=152;edgeStyle=orthogonalEdgeStyle;exitX=0.921;exitY=-0.02;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.619;entryY=0.993;entryDx=0;entryDy=0;entryPerimeter=0;\" parent=\"8e3ejTn5SqRV94l3z-M2-29\" edge=\"1\" target=\"_SNN-nFgLk4Tr6xxPPOo-1\" source=\"_SNN-nFgLk4Tr6xxPPOo-39\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"576.1666259765625\" y=\"402.50091552734375\" as=\"sourcePoint\" />\n            <mxPoint x=\"583\" y=\"157\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"572\" y=\"170\" />\n              <mxPoint x=\"572\" y=\"170\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-72\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#000000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.423;entryY=-0.001;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-29\" source=\"_SNN-nFgLk4Tr6xxPPOo-39\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"440\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"438\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"490\" y=\"420\" as=\"sourcePoint\" />\n            <mxPoint x=\"440\" y=\"170\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-94\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;throw nil? true? false? symbol symbol?&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;keyword keyword? vector vector?&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;hash-map map? assoc dissoc get contains?&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;keys vals sequential? apply map&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-29\">\n          <mxGeometry x=\"40\" y=\"140\" width=\"215\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-38\" value=\"step9 ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-70\" value=\"&lt;div style=&quot;width: 102.6px;height:auto;word-break: break-word;&quot;&gt;&lt;div align=&quot;center&quot;&gt;&lt;span style=&quot;font-size:12px;font-weight:bold;font-family:Arial;text-decoration:none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration:none; line-height: normal;&quot;&gt;try*/catch*&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"shape=rect;shadow=0;strokeWidth=2;fillColor=#FFFFFF;strokeColor=#DD0000;opacity=100.0;html=1;nl2Br=0;verticalAlign=middle;align=center;spacingLeft=8.2;spacingRight=6;whiteSpace=wrap;gliffyId=122;fontColor=#DD0000;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-38\">\n          <mxGeometry x=\"470\" y=\"430\" width=\"110\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-71\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=152;edgeStyle=orthogonalEdgeStyle;exitX=0.921;exitY=-0.02;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.619;entryY=0.997;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=#DD0000;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-38\" source=\"8e3ejTn5SqRV94l3z-M2-70\" target=\"_SNN-nFgLk4Tr6xxPPOo-1\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"595.6666259765625\" y=\"412.50091552734375\" as=\"sourcePoint\" />\n            <mxPoint x=\"591.5\" y=\"167\" as=\"targetPoint\" />\n            <Array as=\"points\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-73\" style=\"shape=filledEdge;strokeWidth=2;strokeColor=#DD0000;fillColor=none;startArrow=none;startFill=0;startSize=6;endArrow=block;endFill=1;endSize=6;rounded=1;gliffyId=158;edgeStyle=orthogonalEdgeStyle;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.425;entryY=-0.007;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-38\" source=\"8e3ejTn5SqRV94l3z-M2-70\" target=\"_SNN-nFgLk4Tr6xxPPOo-3\">\n          <mxGeometry width=\"100\" height=\"100\" relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"400\" y=\"440\" />\n              <mxPoint x=\"400\" y=\"220\" />\n              <mxPoint x=\"439\" y=\"220\" />\n            </Array>\n            <mxPoint x=\"490\" y=\"450\" as=\"sourcePoint\" />\n            <mxPoint x=\"470\" y=\"180\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-95\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;throw nil? true? false? symbol symbol?&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;keyword keyword? vector vector?&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;hash-map map? assoc dissoc get contains?&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px;&quot;&gt;&lt;b&gt;keys vals sequential? apply map&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;fontColor=#DD0000;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-38\">\n          <mxGeometry x=\"40\" y=\"140\" width=\"215\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"1\" value=\"stepA\" parent=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-96\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px;&quot;&gt;readline fn? string? number? conj seq&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px;&quot;&gt;with-meta meta time-ms&lt;/span&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px; text-decoration: none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;fontStyle=1\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"40\" y=\"190\" width=\"215\" height=\"37.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-99\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;*host-language*&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"520\" y=\"80\" width=\"130\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-39\" value=\"stepA ONLY\" parent=\"0\" visible=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-97\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px;&quot;&gt;readline fn? string? number? conj seq&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;background-color: initial; font-size: 10px;&quot;&gt;with-meta meta time-ms&lt;/span&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;span style=&quot;font-size: 10px; text-decoration: none; line-height: 0;&quot;&gt;&lt;span style=&quot;text-decoration: none ; line-height: normal&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;br&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;horizontal=1;fontColor=#DD0000;fontStyle=1\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-39\">\n          <mxGeometry x=\"40\" y=\"190\" width=\"215\" height=\"37.5\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-100\" value=\"&lt;div style=&quot;width: 222px ; height: auto ; word-break: break-word ; line-height: 90%&quot;&gt;&lt;div align=&quot;left&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;*host-language*&lt;/b&gt;&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;&lt;/div&gt;&lt;/div&gt;\" style=\"text;html=1;nl2Br=0;html=1;nl2Br=0;verticalAlign=top;align=left;spacingLeft=0.0;spacingRight=0;whiteSpace=wrap;gliffyId=207;fontColor=#DD0000;\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-39\">\n          <mxGeometry x=\"520\" y=\"80\" width=\"130\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-53\" value=\"step3 LABELS\" parent=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-58\" value=\"create env\" style=\"rounded=0;whiteSpace=wrap;html=1;strokeColor=none;fontStyle=1\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-53\">\n          <mxGeometry x=\"515\" y=\"190\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-61\" value=\"update env\" style=\"rounded=0;whiteSpace=wrap;html=1;strokeColor=none;fontStyle=1\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-53\">\n          <mxGeometry x=\"620\" y=\"190\" width=\"70\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-62\" value=\"step5 LABEL\" parent=\"0\" />\n        <mxCell id=\"8e3ejTn5SqRV94l3z-M2-63\" value=\"TCO\" style=\"rounded=0;whiteSpace=wrap;html=1;strokeColor=none;fontStyle=1\" vertex=\"1\" parent=\"8e3ejTn5SqRV94l3z-M2-62\">\n          <mxGeometry x=\"375\" y=\"270\" width=\"50\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n      </root>\n    </mxGraphModel>\n  </diagram>\n</mxfile>\n"
  },
  {
    "path": "runtest.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport os, sys, re\nimport argparse, time\nimport signal, atexit\n\nfrom subprocess import Popen, STDOUT, PIPE\nfrom select import select\n\n# Pseudo-TTY and terminal manipulation\nimport pty, array, fcntl, termios\n\nIS_PY_3 = sys.version_info[0] == 3\n\nverbose = 0\ndebug_file = None\nlog_file = None\n\ndef debug(data):\n    if debug_file:\n        debug_file.write(data)\n        debug_file.flush()\n\ndef log(data, verbosity=0, end='\\n'):\n    if log_file:\n        log_file.write(data + end)\n        log_file.flush()\n    if verbose >= verbosity:\n        print(data, end=end)\n        sys.stdout.flush()\n\ndef vlog(data, end='\\n'): log(data, verbosity=1, end=end)\ndef vvlog(data, end='\\n'): log(data, verbosity=2, end=end)\n\nsep = \"\\n\"\nrundir = None\n\nparser = argparse.ArgumentParser(\n        description=\"Run a test file against a Mal implementation\")\nparser.add_argument('-v', '--verbose', action='count', default=0,\n        help=\"verbose output; repeat to increase verbosity\")\nparser.add_argument('--rundir',\n        help=\"change to the directory before running tests\")\nparser.add_argument('--start-timeout', default=10, type=int,\n        help=\"default timeout for initial prompt\")\nparser.add_argument('--test-timeout', default=20, type=int,\n        help=\"default timeout for each individual test action\")\nparser.add_argument('--pre-eval', default=None, type=str,\n        help=\"Mal code to evaluate prior to running the test\")\nparser.add_argument('--no-pty', action='store_true',\n        help=\"Use direct pipes instead of pseudo-tty\")\nparser.add_argument('--log-file', type=str,\n        help=\"Write messages to the named file in addition the screen\")\nparser.add_argument('--debug-file', type=str,\n        help=\"Write all test interactions to the named file\")\nparser.add_argument('--hard', action='store_true',\n        help=\"Turn soft tests (soft, deferrable, optional) into hard failures\")\nparser.add_argument('--continue-after-fail', action='store_true',\n        help=\"Run all tests in a test file even if there are failures\")\n\n# Control whether deferrable and optional tests are executed\nparser.add_argument('--deferrable', dest='deferrable', action='store_true',\n        help=\"Enable deferrable tests that follow a ';>>> deferrable=True'\")\nparser.add_argument('--no-deferrable', dest='deferrable', action='store_false',\n        help=\"Disable deferrable tests that follow a ';>>> deferrable=True'\")\nparser.set_defaults(deferrable=True)\nparser.add_argument('--optional', dest='optional', action='store_true',\n        help=\"Enable optional tests that follow a ';>>> optional=True'\")\nparser.add_argument('--no-optional', dest='optional', action='store_false',\n        help=\"Disable optional tests that follow a ';>>> optional=True'\")\nparser.set_defaults(optional=True)\n\nparser.add_argument('test_file', type=str,\n        help=\"a test file formatted as with mal test data\")\nparser.add_argument('mal_cmd', nargs=\"*\",\n        help=\"Mal implementation command line. Use '--' to \"\n             \"specify a Mal command line with dashed options.\")\nparser.add_argument('--crlf', dest='crlf', action='store_true',\n        help=\"Write \\\\r\\\\n instead of \\\\n to the input\")\n\nclass Runner():\n    def __init__(self, args, no_pty=False, line_break=\"\\n\"):\n        #print \"args: %s\" % repr(args)\n        self.no_pty = no_pty\n\n        # Cleanup child process on exit\n        atexit.register(self.cleanup)\n\n        self.p = None\n        env = os.environ\n        env['TERM'] = 'dumb'\n        env['INPUTRC'] = '/dev/null'\n        env['PERL_RL'] = 'false'\n        if no_pty:\n            self.p = Popen(args, bufsize=0,\n                           stdin=PIPE, stdout=PIPE, stderr=STDOUT,\n                           preexec_fn=os.setsid,\n                           env=env)\n            self.stdin = self.p.stdin\n            self.stdout = self.p.stdout\n        else:\n            # provide tty to get 'interactive' readline to work\n            master, slave = pty.openpty()\n\n            # Set terminal size large so that readline will not send\n            # ANSI/VT escape codes when the lines are long.\n            buf = array.array('h', [100, 200, 0, 0])\n            fcntl.ioctl(master, termios.TIOCSWINSZ, buf, True)\n\n            self.p = Popen(args, bufsize=0,\n                           stdin=slave, stdout=slave, stderr=STDOUT,\n                           preexec_fn=os.setsid,\n                           env=env)\n            # Now close slave so that we will get an exception from\n            # read when the child exits early\n            # http://stackoverflow.com/questions/11165521\n            os.close(slave)\n            self.stdin = os.fdopen(master, 'r+b', 0)\n            self.stdout = self.stdin\n\n        #print \"started\"\n        self.buf = \"\"\n        self.last_prompt = \"\"\n\n        self.line_break = line_break\n\n    def read_to_prompt(self, prompts, timeout):\n        end_time = time.time() + timeout\n        while time.time() < end_time:\n            [outs,_,_] = select([self.stdout], [], [], 1)\n            if self.stdout in outs:\n                new_data = self.stdout.read(1)\n                new_data = new_data.decode(\"latin1\") if IS_PY_3 else new_data\n                #print(\"new_data: %s\" % repr(new_data))\n                debug(new_data)\n                # Perform newline cleanup\n                self.buf += new_data.replace(\"\\r\", \"\")\n                if self.buf.endswith('\\x1b[6n'):\n                    vvlog(\"Handling ASCII cursor query\")\n                    self.stdin.write(b\"\\x1b[1;1R\")\n                    self.buf = \"\"\n                    continue\n                for prompt in prompts:\n                    regexp = re.compile(prompt)\n                    match = regexp.search(self.buf)\n                    if match:\n                        end = match.end()\n                        buf = self.buf[0:match.start()]\n                        self.buf = self.buf[end:]\n                        self.last_prompt = prompt\n                        return buf\n        return None\n\n    def writeline(self, str):\n        def _to_bytes(s):\n            return bytes(s, \"latin1\") if IS_PY_3 else s\n\n        data = _to_bytes(str.replace('\\r', '\\x16\\r') + self.line_break)\n        #print(\"write: %s\" % repr(data))\n        self.stdin.write(data)\n\n    def cleanup(self):\n        #print \"cleaning up\"\n        if self.p:\n            try:\n                os.killpg(self.p.pid, signal.SIGTERM)\n            except OSError:\n                pass\n            self.p = None\n\nclass TestReader:\n    def __init__(self, test_file):\n        self.line_num = 0\n        f = open(test_file, newline='') if IS_PY_3 else open(test_file)\n        self.data = f.read().split('\\n')\n        self.soft = False\n        self.deferrable = False\n        self.optional = False\n\n    def next(self):\n        self.msg = None\n        self.form = None\n        self.out = \"\"\n        self.ret = None\n\n        while self.data:\n            self.line_num += 1\n            line = self.data.pop(0)\n            if re.match(r\"^\\s*$\", line):   # blank line\n                continue\n            elif line[0:3] == \";;;\":       # ignore comment\n                continue\n            elif line[0:2] == \";;\":        # output comment\n                self.msg = line[3:]\n                return True\n            elif line[0:5] == \";>>> \":     # settings/commands\n                settings = {}\n                exec(line[5:], {}, settings)\n                if 'soft' in settings:\n                    self.soft = settings['soft']\n                if 'deferrable' in settings and settings['deferrable']:\n                    self.deferrable = \"\\nSkipping deferrable and optional tests\"\n                    return True\n                if 'optional' in settings and settings['optional']:\n                    self.optional = \"\\nSkipping optional tests\"\n                    return True\n                continue\n            elif line[0:1] == \";\":         # unexpected comment\n                raise Exception(\"Test data error at line %d:\\n%s\" % (self.line_num, line))\n            self.form = line   # the line is a form to send\n\n            # Now find the output and return value\n            while self.data:\n                line = self.data[0]\n                if line[0:3] == \";=>\":\n                    self.ret = line[3:]\n                    self.line_num += 1\n                    self.data.pop(0)\n                    break\n                elif line[0:2] == \";/\":\n                    self.out = self.out + line[2:] + sep\n                    self.line_num += 1\n                    self.data.pop(0)\n                else:\n                    self.ret = \"\"\n                    break\n            if self.ret != None: break\n\n        if self.out[-1:] == sep and not self.ret:\n            # If there is no return value, output should not end in\n            # separator\n            self.out = self.out[0:-1]\n        return self.form\n\nargs = parser.parse_args(sys.argv[1:])\nverbose = args.verbose\n# Workaround argparse issue with two '--' on command line\nif sys.argv.count('--') > 0:\n    args.mal_cmd = sys.argv[sys.argv.index('--')+1:]\n\nif args.rundir: os.chdir(args.rundir)\n\nif args.log_file:   log_file   = open(args.log_file, \"a\")\nif args.debug_file: debug_file = open(args.debug_file, \"a\")\n\nr = Runner(args.mal_cmd, no_pty=args.no_pty, line_break=\"\\r\\n\" if args.crlf else \"\\n\")\nt = TestReader(args.test_file)\n\n\ndef assert_prompt(runner, prompts, timeout):\n    # Wait for the initial prompt\n    header = runner.read_to_prompt(prompts, timeout=timeout)\n    if not header == None:\n        if header:\n            vvlog(\"Started with:\\n%s\" % header)\n    else:\n        log(\"Did not receive one of following prompt(s): %s\" % repr(prompts))\n        log(\"    Got      : %s\" % repr(r.buf))\n        sys.exit(1)\n\ndef elide(s, max = 79):\n    \"\"\"Replace middle of a long string with '...' so length is <= max.\"\"\"\n    return s if len(s) <= max else s[:(max-3)//2] + \"...\" + s[-((max-2)//2):]\n\n# Wait for the initial prompt\ntry:\n    assert_prompt(r, ['[^\\\\s()<>]+> '], args.start_timeout)\nexcept:\n    _, exc, _ = sys.exc_info()\n    log(\"\\nException: %s\" % repr(exc))\n    log(\"Output before exception:\\n%s\" % r.buf)\n    sys.exit(1)\n\n# Send the pre-eval code if any\nif args.pre_eval:\n    sys.stdout.write(\"RUNNING pre-eval: %s\" % args.pre_eval)\n    r.writeline(args.pre_eval)\n    assert_prompt(r, ['[^\\\\s()<>]+> '], args.test_timeout)\n\ntotal_test_cnt = 0\ntest_cnt = 0\npass_cnt = 0\nfail_cnt = 0\nsoft_fail_cnt = 0\nfailures = []\nfail_type = \"\"\n\nclass TestTimeout(Exception):\n    pass\n\nwhile t.next():\n    if args.deferrable == False and t.deferrable:\n        log(t.deferrable)\n        break\n\n    if args.optional == False and t.optional:\n        log(t.optional)\n        break\n\n    if t.msg != None:\n        # omit blank test lines unless verbose\n        if verbose or t.msg:\n            log(t.msg)\n        continue\n\n    if t.form == None: continue\n\n    total_test_cnt += 1\n    if fail_type == \"TIMED OUT\":\n        continue  # repl is stuck\n    if not args.continue_after_fail:\n        if fail_cnt > 0:\n            continue\n\n    # The repeated form is to get around an occasional OS X issue\n    # where the form is repeated.\n    # https://github.com/kanaka/mal/issues/30\n    expects = [\".*%s%s%s\" % (sep, t.out, re.escape(t.ret)),\n               \".*%s.*%s%s%s\" % (sep, sep, t.out, re.escape(t.ret))]\n\n    test_msg = \"TEST (line %d): %s -> %s\" % (\n            t.line_num, repr(t.form), repr(expects[0]))\n    vlog(test_msg, end='')\n\n    r.writeline(t.form)\n    try:\n        test_cnt += 1\n        res = r.read_to_prompt(['\\r\\n[^\\\\s()<>]+> ', '\\n[^\\\\s()<>]+> '],\n                                timeout=args.test_timeout)\n        #print \"%s,%s,%s\" % (idx, repr(p.before), repr(p.after))\n        if (t.ret == \"\" and t.out == \"\"):\n            vlog(\" -> SUCCESS (result ignored)\")\n            pass_cnt += 1\n        elif res and (re.search(expects[0], res, re.S) or\n                re.search(expects[1], res, re.S)):\n            vlog(\" -> SUCCESS\")\n            pass_cnt += 1\n        else:\n            if (res == None):\n                if verbose == 0: log(test_msg, end='')\n                log(\" -> TIMED OUT\")\n                fail_cnt += 1\n                fail_type = \"TIMED OUT\"\n            elif t.soft and not args.hard:\n                vlog(\" -> SOFT FAIL:\")\n                soft_fail_cnt += 1\n                fail_type = \"SOFT FAILED\"\n            else:\n                vlog(\" -> FAIL:\")\n                fail_cnt += 1\n                fail_type = \"FAILED\"\n            expected = \"    Expected : %s\" % repr(expects[0])\n            got = \"    Got      : %s\" % repr(res or \"\")\n            vvlog(expected)\n            vlog(got if verbose >= 2 else elide(got))\n            failed_test = \"%s %s:\\n%s\\n%s\" % (\n                    fail_type, test_msg, expected, got)\n            failures.append(failed_test)\n    except:\n        _, exc, _ = sys.exc_info()\n        log(\"\\nException: %s\" % repr(exc))\n        log(\"Output before exception:\\n%s\" % r.buf)\n        break\n\nif len(failures) > 0:\n    log(\"\\nFAILURES:\")\n    for f in failures:\n        log(f)\n\nresults = \"\"\"\nTEST RESULTS (for %s):\n  %3d: soft failing tests\n  %3d: failing tests\n  %3d: passing tests\n  %3d: executed tests\n  %3d: total tests in the file (%d skipped)\n\"\"\" % (args.test_file, soft_fail_cnt, fail_cnt, pass_cnt, test_cnt,\n        total_test_cnt, total_test_cnt - test_cnt)\nlog(results)\n\ndebug(\"\\n\") # add some separate to debug log\n\nif fail_cnt > 0:\n    sys.exit(1)\nsys.exit(0)\n"
  },
  {
    "path": "voom-like-version.sh",
    "content": "#!/usr/bin/env sh\n\necho $(TZ=UTC git log -1 --pretty=%ad-g%h --date=format-local:\"%Y%m%d_%H%M%S\" -- \"$@\")$(test -z \"$(git status --short -- \"$@\")\" || echo _DIRTY)\n"
  }
]